Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
b11c31e
tui3: the approvals chip — what a conversation runs without asking is…
ZeroPoint95 Sep 16, 2026
32d004d
docs: the approvals chip's change entry names its pull request
ZeroPoint95 Sep 16, 2026
6372adb
tui3: after #1063 the status page says the conversation's own posture…
ZeroPoint95 Sep 16, 2026
28743fd
cmd/codeaf: a banked rule rebuilds through the engine only where the …
ZeroPoint95 Sep 16, 2026
3a6326a
tui3: one seam over every box — the draft on every place carries the …
ZeroPoint95 Sep 17, 2026
c0edf51
tui3: a task's room gets the box seam too — the node's model, rung an…
ZeroPoint95 Sep 17, 2026
9d62c93
tui3: only home starts things — the box, the draft's rule and alt+ent…
ZeroPoint95 Sep 17, 2026
b1a2014
tui3: the tasks place stops counting itself on the rule
ZeroPoint95 Sep 17, 2026
10850fd
tui3: the greeting draws no YOLO badge — the cell arrives with the seam
ZeroPoint95 Sep 17, 2026
47f7f6c
tui3: the lowest line is for keys — the numbers climb onto the seam, …
ZeroPoint95 Sep 17, 2026
5a01d3e
tui3: home's box says the promise, the seam drops the title and seats…
ZeroPoint95 Sep 17, 2026
89a41cb
tui3: the machine is written into the model's cell — `deepseek-v4.1-f…
ZeroPoint95 Sep 17, 2026
6691139
remote: the engine host tells a window that attaches which machine an…
ZeroPoint95 Sep 17, 2026
a35e9a0
session: a sighting told before the window's reader registers is held…
ZeroPoint95 Sep 17, 2026
8ea9290
home: threads and tasks are the panel words, spend's lines are read a…
ZeroPoint95 Sep 17, 2026
5e4156d
home: a heading that opens a place underlines its word under the pointer
ZeroPoint95 Sep 17, 2026
b01ff45
tui3: simplify home hints and highlight the current model
ZeroPoint95 Sep 17, 2026
9318b04
home: tasks is the last day's work flattened, its rows open the task,…
ZeroPoint95 Sep 17, 2026
bd29f8d
tui3: lead the seam with model effort and keep project path roots
ZeroPoint95 Sep 17, 2026
5b8420c
home: bare tasks and threads headings, needs-you descriptions only un…
ZeroPoint95 Sep 17, 2026
4069d8a
Align conversation message-box controls with home
ZeroPoint95 Sep 17, 2026
902b36c
home: a needs-you description opens with its thread as a title line
ZeroPoint95 Sep 17, 2026
82c8ff8
Simplify home hints and move task panel hide control
ZeroPoint95 Sep 17, 2026
ef01009
home: the arrows never leave the field, ↑ stays off the tab bar, and …
ZeroPoint95 Sep 18, 2026
67639fb
Merge workflow UX changes into the message-box wave
ZeroPoint95 Sep 18, 2026
6c0a92b
Fix home project cycling and retain the selected project
ZeroPoint95 Sep 18, 2026
c02f403
Record home and conversation message-box parity rule
ZeroPoint95 Sep 18, 2026
fc91657
Keep project beside approvals on home and conversation seams
ZeroPoint95 Sep 18, 2026
71b3399
Align project seams right, add hover cues and swap control shortcuts
ZeroPoint95 Sep 18, 2026
2383108
Support Shift+Enter newlines across message boxes
ZeroPoint95 Sep 18, 2026
9f80b9f
Restore home effort controls when the default is auto
ZeroPoint95 Sep 18, 2026
8780501
Simplify decision boxes and wrap their option navigation
ZeroPoint95 Sep 18, 2026
49503ac
Align shared decision labels and regression expectations
ZeroPoint95 Sep 18, 2026
d5182bc
Keep home's multiline draft cursor and placeholder in sync
ZeroPoint95 Sep 18, 2026
2345529
Make decision dialogs support replacement and concurrent clarification
ZeroPoint95 Sep 18, 2026
ef4313e
Drain stopped dialogue events before showing the updated request
ZeroPoint95 Sep 18, 2026
9264259
Prioritize clarification input over expanded decisions
ZeroPoint95 Sep 18, 2026
a1eeab7
Preserve completed-action corrections and cover clarification events
ZeroPoint95 Sep 18, 2026
8552bcd
Unify mouse and keyboard list selection
ZeroPoint95 Sep 18, 2026
b119774
Show home item options beneath the middle-column description
ZeroPoint95 Sep 18, 2026
e518ef3
Add persistent per-task put-away and shared project actions
ZeroPoint95 Sep 18, 2026
0b75d42
Use x close for thread and task row actions
ZeroPoint95 Sep 18, 2026
0c90721
Keep full model identifiers on home and conversation seams
ZeroPoint95 Sep 18, 2026
7ba6197
Use project wording and n/p shortcuts in row menus
ZeroPoint95 Sep 18, 2026
14fa7af
Keep pasted folder paths in the message box after attachment refusal
ZeroPoint95 Sep 18, 2026
81d4fbd
Make home folder paste a one-use project-start offer
ZeroPoint95 Sep 18, 2026
c402c08
Replace home submission rows with an explicit ask command
ZeroPoint95 Sep 18, 2026
9866ef3
Unify alphabetical command browsing above both composers
ZeroPoint95 Sep 19, 2026
cbbcb3d
Remove approvals slash commands and their yolo alias
ZeroPoint95 Sep 19, 2026
b548c76
Make Escape back navigation ending at Home
ZeroPoint95 Sep 20, 2026
c8d403e
Merge dev into UX changes branch
ZeroPoint95 Sep 20, 2026
b771782
Move effort cycling to Option-E and Alt-E
ZeroPoint95 Sep 20, 2026
1186cce
Keep Home conversations in sync with open tabs
ZeroPoint95 Sep 20, 2026
1953980
Pin the conversation sidebar header above tasks in creation order
ZeroPoint95 Sep 20, 2026
739b84e
Align remaining task navigation checks with creation order
ZeroPoint95 Sep 20, 2026
f322c0f
Use one conversation title and name pending tabs from prompts
ZeroPoint95 Sep 20, 2026
afaff58
Replace the sidebar task heading with a blank spacer
ZeroPoint95 Sep 20, 2026
1cbcbf2
Remove the retired sidebar working-count capability
ZeroPoint95 Sep 20, 2026
ab225c9
Animate ask replies and show conversation activity on Home
ZeroPoint95 Sep 20, 2026
4e0e04b
Retry incomplete tasks in place from their task screen
ZeroPoint95 Sep 20, 2026
27c7126
Index task retry instructions in the manual
ZeroPoint95 Sep 20, 2026
dabb441
Merge branch 'codex/task-interface-cleanup' into claude/codeaf-autono…
ZeroPoint95 Sep 20, 2026
2cf269f
Put Home questions on their rows and clarify Escape navigation
ZeroPoint95 Sep 20, 2026
b4fe621
Default new conversations to YOLO approvals
ZeroPoint95 Sep 20, 2026
2b5a998
Remove duplicate daily spend from Home panel heading
ZeroPoint95 Sep 20, 2026
e48b552
Organize Tasks into expanded conversation activity trees
ZeroPoint95 Sep 20, 2026
95ec9bd
Expand task retries and refine the Tasks tree table
ZeroPoint95 Sep 20, 2026
f5d476a
Place Tasks fold arrows immediately after titles
ZeroPoint95 Sep 21, 2026
bf4faa6
Replace Home task list with recent sessions and rename the tab
ZeroPoint95 Sep 21, 2026
63b1194
Consolidate Home conversations into one Sessions list
ZeroPoint95 Sep 21, 2026
4d5cdcd
Advertise Home row options before draft controls
ZeroPoint95 Sep 21, 2026
c34a758
Repair UX regressions and bring PR checks in line with the new interface
ZeroPoint95 Sep 21, 2026
a0e6753
Compare Home input gestures with the same project destination
ZeroPoint95 Sep 21, 2026
a70d963
Merge dev into UX changes and reconcile task trees and input controls
ZeroPoint95 Sep 21, 2026
e13d9b6
Keep run views out of Home and chat conversation lists
ZeroPoint95 Sep 21, 2026
9f8a03e
Stabilize merged regression fixtures across publication and path boun…
ZeroPoint95 Sep 21, 2026
2b1725f
Address UX review: restore saved choices and require explicit headles…
ZeroPoint95 Sep 21, 2026
c62a2bd
Merge remote-tracking branch 'origin/dev' into claude/codeaf-autonomy…
ZeroPoint95 Sep 21, 2026
d7a0bf8
Make the settings ownership answer directly retrievable
ZeroPoint95 Sep 21, 2026
78ddda4
Merge dev model-picker table into UX changes
ZeroPoint95 Sep 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
14 changes: 14 additions & 0 deletions PERF.md
Original file line number Diff line number Diff line change
Expand Up @@ -2532,3 +2532,17 @@ plus at most two 400-byte neighbours on each side. They do not read whole
transcripts or invoke an embedding model. Search tokenization accepts at most
32 Unicode word/number tokens, quoted as FTS data. History access inherited by
tasks grants reads only and introduces no background memory calls or writes.

## Home conversation list budget

Home's conversation list is bounded by `tui3.tabsCap` (32 open tabs) plus
`tui3.homeClosedLimit` (3 closed conversations). It replaces the ten-row saved-history
budget. A short frame folds what cannot fit; metadata comes from Home's cached world
reading. Tab membership changes rebuild the list without rereading the filesystem.

### One conversation name (2026-09-20)

Conversation naming requests one 5–8 word phrase. The former two-word/32-byte
short-label cap is removed; `titleLimit` remains 80 bytes for the only stored title.
`titleClip`, the 20-second ask window and the two-minute naming window are unchanged.
Home and tab widths truncate that same title with three dots at render time.
103 changes: 89 additions & 14 deletions cmd/codeaf/chatv3.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,14 +397,6 @@ func openChatV3(name string, args []string, pickSession bool) error {
// only model it can be about: --reasoning names a strength, not a model, and
// the level is kept per model from here on (internal/session's agent.go).
agent.SetReasoning(level)
// AND THE RUNG THIS CONVERSATION WAS LEFT ON. It is the meta.json half of
// the same law the model row keeps (internal/session's Meta): a person who
// dialled a conversation deeper, worked in it and came back found it at the
// install's default as though they had chosen nothing. Absence sets nothing
// and stamps nothing, so a conversation nobody has dialled is unchanged.
if saved := strings.TrimSpace(v3SavedEffort(cfg.Place)); saved != "" {
agent.SetConversationEffort(saved)
}
proc.track(agent)
// EVERY CONVERSATION THIS PROCESS OPENED, CLOSED HOWEVER THE SURFACE RETURNS.
// Close is the surface's to call — /quit and ctrl+c both go through it — but
Expand Down Expand Up @@ -1065,6 +1057,14 @@ func openV3Launch(proc *v3Process, opts v3Options) (*v3Launch, error) {
// never a second policy.
cfg.Unattended = opts.Yolo
cfg.Interactive = opts.Interactive
if !opts.Interactive {
gate := v3ApprovalGate{workspace: cfg.Workspace, profileDir: settings.ProfileDir, headless: true}
cfg.ApprovalGate = gate
cfg.ApprovalPolicy, cfg.Guardian, err = gate.Build(cfg.ApprovalPosture)
if err != nil {
return nil, err
}
}
cfg.Budget = opts.Budget
// AND THE ACCOUNTS MANAGER IS THE PROCESS'S, not this launch's. Governance
// leaves the field empty for exactly this reason: an account connected on
Expand Down Expand Up @@ -1153,6 +1153,21 @@ func v3SavedEffort(place session.Place) string {
return meta.Effort
}

// v3SavedApproval is the posture this conversation last set on its own gate,
// read back off its folder on [v3SavedEffort]'s terms: "" for a fresh
// conversation, a build before the field existed, or a file that does not read.
func v3SavedApproval(place session.Place) string {
dir := strings.TrimSpace(place.Dir)
if dir == "" {
return ""
}
meta, err := session.LoadMeta(dir)
if err != nil {
return ""
}
return meta.Approval
}

// v3TalkModel is which model this conversation opens on, and the order is the
// whole content: what the person named on the command line, then what they
// last chose and it was written down (internal/config's chatmodel.go), then
Expand Down Expand Up @@ -1238,8 +1253,46 @@ func v3Connections(manager *connect.Manager) tui3.Connections {
// cannot be shown a fuel gate (engine.go) — and a door deciding that for itself
// would be this file guessing who is watching.
func openV3Agent(cfg session.Config, workspace string, open func(session.Config) (*session.Agent, error)) (*session.Agent, session.Config, string, error) {
agent, err := open(cfg)
// Restore the gate before construction, so restored work cannot start behind
// the profile default. Return the launch config unchanged: a subsequent new
// conversation must not inherit this one's saved override.
restored := cfg
savedEffort := strings.TrimSpace(v3SavedEffort(cfg.Place))
restoredPosture := ""
if saved := strings.TrimSpace(v3SavedApproval(cfg.Place)); saved != "" && cfg.ApprovalPosture == "" && cfg.ApprovalGate != nil {
valid := false
for _, posture := range session.ApprovalPostures {
if saved == posture {
valid = true
}
}
if !valid {
saved = session.PostureAsk
}
build := saved
if saved == session.PostureAuto {
build = ""
}
policy, guardian, err := cfg.ApprovalGate.Build(build)
if err != nil {
return nil, cfg, "", fmt.Errorf("restore conversation approvals: %w", err)
}
if policy == nil {
return nil, cfg, "", errors.New("restore conversation approvals: rules unavailable")
}
restored.ApprovalPolicy, restored.Guardian, restoredPosture = policy, guardian, saved
}
agent, err := open(restored)
if err == nil {
if restoredPosture != "" {
if err := agent.SetApprovalPosture(restoredPosture); err != nil {
_ = agent.Close()
return nil, cfg, "", fmt.Errorf("restore conversation approvals: %w", err)
}
}
if savedEffort != "" {
agent.SetConversationEffort(savedEffort)
}
return agent, cfg, "", nil
}
if !errors.Is(err, session.ErrSessionLocked) {
Expand Down Expand Up @@ -1439,6 +1492,15 @@ func applyV3Governance(cfg session.Config, profileDir string, yolo, oneModel boo
// a repository may state are still the rules — it can say what to ask about;
// it cannot say who answers.
cfg.Guardian = config.GuardianEnabledAt(profileDir)
// AND THE DOOR THE CONVERSATION MOVES ITS OWN GATE THROUGH, over the same
// rows (chatv3_approval.go's [v3ApprovalGate]). --yolo is handed down as the
// posture an untouched conversation starts at rather than only as the
// policy it starts on, so the seam can say what it is and the wheel can
// walk away from it (internal/session's approvalposture.go).
cfg.ApprovalGate = v3ApprovalGate{workspace: workspace, profileDir: profileDir}
if yolo {
cfg.ApprovalPosture = session.PostureAllow
}
cfg.TaskAutoApproveSeconds = config.TaskAutoApproveAt(profileDir)
cfg.BashBackgroundAfterSeconds = config.BashBackgroundAfterAt(profileDir)
// Which model the work that leaves this conversation runs on, PROFILE-ONLY
Expand Down Expand Up @@ -1658,12 +1720,25 @@ func v3SurfacePosture(yolo bool) string {
// `bash:prompt` still gets asked about bash: the flag is "stop asking me about
// the ordinary things", not "forget what I wrote down".
func v3Policy(workspace, profileDir string, yolo bool) (*approval.Policy, error) {
mode, err := config.ProjectStringAt(workspace, profileDir, config.KeyToolApprovalMode)
if err != nil {
return nil, err
}
if yolo {
mode = string(approval.ActionAllow)
return v3PolicyMode(workspace, profileDir, string(approval.ActionAllow))
}
return v3PolicyMode(workspace, profileDir, "")
}

// v3PolicyMode is [v3Policy] with the blanket answer named outright: mode
// replaces the `tools.approvalMode` row for this build and "" reads the row.
// It is the one function every posture goes through — the flag's forced allow,
// a conversation's own wheel (chatv3_approval.go's [v3ApprovalGate]) and the
// ordinary launch — so the exceptions, the shell rules and the floor cannot
// differ between them.
func v3PolicyMode(workspace, profileDir, mode string) (*approval.Policy, error) {
if mode == "" {
row, err := config.ProjectStringAt(workspace, profileDir, config.KeyToolApprovalMode)
if err != nil {
return nil, err
}
mode = row
}
raw := map[string]any{"default": mode}
text, err := config.ProjectStringAt(workspace, profileDir, config.KeyToolApprovals)
Expand Down
96 changes: 96 additions & 0 deletions cmd/codeaf/chatv3_approval.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,96 @@ type v3Gate interface {
SetApprovalPolicy(policy *approval.Policy)
}

// v3Rebuilder is the newer door on the same agent: a session that knows its
// own posture rebuilds the gate FOR that posture (internal/session's
// approvalposture.go), so a rule banked inside a conversation walked to allow
// lands on an allow gate. [refreshV3Policy] and [applyV3Approvals] prefer it
// and keep the plain push for a gate that does not have it.
type v3Rebuilder interface {
ApprovalDial() bool
RebuildApprovalGate() error
}

// v3Rebuilt asks the agent to rebuild its own gate where it can, and reports
// whether it could. AN AGENT WITH THE METHODS AND NO DOOR IS NOT REBUILT HERE:
// a session assembled without [session.Config.ApprovalGate] — a test's, a
// worker's — refuses the rebuild, and a refusal that silenced the plain push
// left a banked rule reaching nobody's gate (the person was asked twice for
// one command). So the door is asked first, and only a session that has one
// takes this road.
func v3Rebuilt(agent v3Gate) (bool, error) {
rebuilder, ok := agent.(v3Rebuilder)
if !ok || !rebuilder.ApprovalDial() {
return false, nil
}
return true, rebuilder.RebuildApprovalGate()
}

// v3ApprovalGate is the door a conversation moves its own gate through
// ([session.ApprovalGate]): the same three rows, the same project layer, the
// same floors as the launch, asked for by posture instead of by flag.
//
// THE POSTURE IS A WORD FOR A PAIR. The ladder folds the guardian row into the
// mode (internal/session's approvalposture.go says why), so `ask` is prompt
// with the guardian stood down, `guardian` is prompt with it up, and `allow`
// and `deny` are the row's own words with the guardian irrelevant. An empty
// posture is the rows exactly as they stand — which is what an untouched
// conversation runs at and what `auto` hands back to.
type v3ApprovalGate struct {
workspace, profileDir string
headless bool
}

func (g v3ApprovalGate) Build(posture string) (*approval.Policy, bool, error) {
mode := ""
guardian := config.GuardianEnabledAt(g.profileDir)
switch posture {
case session.PostureAsk:
mode, guardian = string(approval.ActionPrompt), false
case session.PostureGuardian:
mode, guardian = string(approval.ActionPrompt), true
case session.PostureAllow:
mode, guardian = string(approval.ActionAllow), false
case session.PostureDeny:
mode, guardian = string(approval.ActionDeny), false
}
if mode == "" && g.headless {
var err error
mode, err = config.HeadlessToolApprovalModeAt(g.workspace, g.profileDir)
if err != nil {
return nil, false, err
}
}
policy, err := v3PolicyMode(g.workspace, g.profileDir, mode)
if err != nil {
return nil, false, err
}
return policy, guardian, nil
}

// Standing is the rows as they stand, said in the ladder's words. An unreadable
// mode row reads as the strictest answer it could be, which is internal/config's
// own law for that row and the one direction a garbled setting may be wrong in.
func (g v3ApprovalGate) Standing() string {
mode, err := config.ProjectStringAt(g.workspace, g.profileDir, config.KeyToolApprovalMode)
if g.headless {
mode, err = config.HeadlessToolApprovalModeAt(g.workspace, g.profileDir)
}
if err != nil {
mode = string(approval.ActionPrompt)
}
switch mode {
case string(approval.ActionAllow):
return session.PostureAllow
case string(approval.ActionDeny):
return session.PostureDeny
}
if config.GuardianEnabledAt(g.profileDir) {
return session.PostureGuardian
}
return session.PostureAsk
}

// The consent card's door back to disk: where "always" is written down.
//
// It is a PAIR — one seam for a tool, one for a shell command — handed to the
Expand Down Expand Up @@ -114,6 +204,9 @@ func bankBashApproval(agent v3Gate, workspace, profileDir string, yolo bool) fun
// names the next session rather than claiming the line is already gone.
func applyV3Approvals(agent v3Gate, workspace, profileDir string, yolo bool) func() error {
return func() error {
if rebuilt, err := v3Rebuilt(agent); rebuilt {
return err
}
policy, err := v3Policy(workspace, profileDir, yolo)
if err != nil {
return err
Expand Down Expand Up @@ -157,6 +250,9 @@ func refreshV3Policy(agent v3Gate, workspace, profileDir string, yolo bool) {
if agent == nil {
return
}
if rebuilt, _ := v3Rebuilt(agent); rebuilt {
return
}
policy, err := v3Policy(workspace, profileDir, yolo)
if err != nil {
return
Expand Down
2 changes: 1 addition & 1 deletion cmd/codeaf/chatv3_approvalfloor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func TestTheSecondConversationKeepsTheLaunchsPosture(t *testing.T) {
// A rebuild that cannot read the rows leaves the launch's gate in place. The
// answer to "I could not read the rules" is never a session with no rules.
func TestAnUnreadableRowLeavesTheSecondConversationOnTheLaunchsGate(t *testing.T) {
launch, err := applyV3Governance(session.Config{Model: "m"}, t.TempDir(), false, false)
launch, err := applyV3Governance(session.Config{Model: "m"}, v3Profile(t, map[string]any{"tools.approvalMode": "prompt"}), false, false)
if err != nil {
t.Fatalf("the launch did not load: %v", err)
}
Expand Down
1 change: 1 addition & 0 deletions cmd/codeaf/chatv3_at.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ func openChatV3At(launch atLaunch) error {
defer func() { closeLink() }()

client, err := remote.Dial(tunnel, name, remote.Hello{
Headless: launch.once != "",
Workspace: workspace,
Session: launch.session,
Model: launch.model,
Expand Down
4 changes: 3 additions & 1 deletion cmd/codeaf/chatv3_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,9 @@ func openChatV3Host(launch hostLaunch) error {
if launch.pick && launch.once != "" {
return fmt.Errorf(`codeaf resume opens the session picker; for one headless message use: codeaf chat --host %s --once "text"`, dest)
}
link, err := dialEngine(dest, workspace, launchHello(launch.session, launch.model, launch.level))
hello := launchHello(launch.session, launch.model, launch.level)
hello.Headless = launch.once != ""
link, err := dialEngine(dest, workspace, hello)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/codeaf/chatv3_lanes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func TestTheLanesOverAConnectionAreTheOnesTheWireSaysItCarries(t *testing.T) {
// is built INTO the session rather than switched on after it opened.
func TestTheEngineBuildsTheSessionWithTheShapeTheHelloCarried(t *testing.T) {
plain := engineLaunchOptions(remote.Hello{Workspace: "/srv/app"}, "/srv/app", "")
if plain.Yolo || plain.NoCompact || plain.OneModel || plain.Budget.Set() || plain.Interactive {
if plain.Yolo || plain.NoCompact || plain.OneModel || plain.Budget.Set() || !plain.Interactive {
t.Fatalf("a hello with no shape built %+v", plain)
}
shaped := engineLaunchOptions(remote.Hello{
Expand Down
1 change: 1 addition & 0 deletions cmd/codeaf/chatv3_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ func openChatV3Local(launch localLaunch) error {
// front of a path, and `another window` where a connection would name a
// host.
hello := remote.Hello{
Headless: launch.once != "",
Workspace: launch.workspace,
Session: launch.session,
Model: launch.model,
Expand Down
2 changes: 1 addition & 1 deletion cmd/codeaf/chatv3_local_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ func TestAConnectedModelServiceIsLiveInTheRunningEngineConversation(t *testing.T
// the answer releases the call. The next matching command therefore runs
// without asking the person a second time.
func TestABankedBashRuleReachesTheRunningEngineBeforeTheAnswer(t *testing.T) {
profileDir := t.TempDir()
profileDir := v3Profile(t, map[string]any{"tools.approvalMode": "prompt"})
workspace := t.TempDir()
command := "printf banked-rule-probe"
server := consentSource(t, command)
Expand Down
Loading
Loading