Skip to content

Commit b6d4977

Browse files
byapparovclaude
andcommitted
feat(run): remove the AICTRL_ENABLE_TODO_TOOL opt-in (#155)
Headless `aictrl run` simply does not offer todowrite; there is no switch back to the old behaviour. Callers who want plan tracking can instruct the agent to keep a plan file. ACP and other sessions still get todowrite. The registry test now asserts the old variable has no effect. The prefix budget is unchanged (35,188 chars, budget 35,500). Co-Authored-By: Claude Opus 5.5 <[email protected]>
1 parent b196f70 commit b6d4977

7 files changed

Lines changed: 16 additions & 53 deletions

File tree

‎README.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,9 @@ and [JSON event reference](EVENTS.md).
7070

7171
In headless mode, Aictrl automatically rejects all interactive permission requests (like `question` or `plan_enter`), ensuring your pipelines never hang.
7272

73-
`aictrl run` leaves out the `todowrite` tool by default: its definition adds about 2.4k tokens
74-
to every request, and a one-shot run has nobody following a todo list. Set
75-
`AICTRL_ENABLE_TODO_TOOL=1` to bring it back.
73+
`aictrl run` does not offer the `todowrite` tool: its definition adds about 2.4k tokens to
74+
every request, and a one-shot run has nobody following a todo list. To track a plan, instruct
75+
the agent to keep one in a file.
7676

7777
### CI/CD Integration
7878

‎packages/cli/README.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ Provider failures and stream timeouts retain **1**; SIGINT/SIGTERM retain
9797

9898
In headless mode, Aictrl automatically rejects permissions that would otherwise prompt a user.
9999

100-
`aictrl run` leaves out the `todowrite` tool by default: its definition adds about 2.4k tokens
101-
to every request, and a one-shot run has nobody following a todo list. Set
102-
`AICTRL_ENABLE_TODO_TOOL=1` to bring it back.
100+
`aictrl run` does not offer the `todowrite` tool: its definition adds about 2.4k tokens to
101+
every request, and a one-shot run has nobody following a todo list. To track a plan, instruct
102+
the agent to keep one in a file.
103103

104104
## GitHub Agent
105105

‎packages/cli/src/flag/flag.ts‎

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ export namespace Flag {
2222
export const AICTRL_FAKE_VCS = process.env["AICTRL_FAKE_VCS"]
2323
export declare const AICTRL_CLIENT: string
2424
export const AICTRL_ENABLE_QUESTION_TOOL = truthy("AICTRL_ENABLE_QUESTION_TOOL")
25-
export declare const AICTRL_ENABLE_TODO_TOOL: boolean
2625
export declare const AICTRL_MODEL_STREAM_IDLE_TIMEOUT_MS: number
2726

2827
// Experimental
@@ -94,14 +93,3 @@ Object.defineProperty(Flag, "AICTRL_CLIENT", {
9493
enumerable: true,
9594
configurable: false,
9695
})
97-
98-
// Dynamic getter for AICTRL_ENABLE_TODO_TOOL
99-
// Evaluated at access time, not module load time, so tests and embedders
100-
// can toggle the opt-in after the registry has been imported
101-
Object.defineProperty(Flag, "AICTRL_ENABLE_TODO_TOOL", {
102-
get() {
103-
return truthy("AICTRL_ENABLE_TODO_TOOL")
104-
},
105-
enumerable: true,
106-
configurable: false,
107-
})

‎packages/cli/src/tool/registry.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,9 @@ export namespace ToolRegistry {
115115
/**
116116
* todowrite keeps a progress list for someone watching the session. A headless
117117
* `aictrl run` has no such reader, and the tool definition costs ~2.4k tokens on
118-
* every step, so a headless run only gets it with AICTRL_ENABLE_TODO_TOOL (#155).
118+
* every step, so a headless run does not offer it (#155).
119119
*/
120120
function todoEnabled() {
121-
if (Flag.AICTRL_ENABLE_TODO_TOOL) return true
122121
return !HeadlessRun.enabled()
123122
}
124123

‎packages/cli/test/cli/run-default-tools.test.ts‎

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const entries = {
88
}
99

1010
/** Runs `aictrl run` against a fixture Gemini server and returns the tool names of the first request. */
11-
async function requestedTools(entry: string, env: Record<string, string> = {}) {
11+
async function requestedTools(entry: string) {
1212
await using tmp = await tmpdir()
1313
const requests: any[] = []
1414
const server = Bun.serve({
@@ -38,15 +38,12 @@ async function requestedTools(entry: string, env: Record<string, string> = {}) {
3838
agent: { title: { disable: true } },
3939
}),
4040
)
41-
const environment: Record<string, string | undefined> = {
41+
const environment = {
4242
...process.env,
4343
AICTRL_DISABLE_DEFAULT_PLUGINS: "true",
4444
AICTRL_DISABLE_MODELS_FETCH: "true",
4545
AICTRL_DISABLE_AUTOCOMPACT: "true",
46-
...env,
4746
}
48-
// Only the opt-in passed by the test counts, not one inherited from the test process.
49-
if (!("AICTRL_ENABLE_TODO_TOOL" in env)) delete environment.AICTRL_ENABLE_TODO_TOOL
5047
const proc = Bun.spawn(
5148
["bun", "run", "--conditions=browser", entry, "run", "--model", "fixture/gemini-fixture", "Check this input."],
5249
{ cwd: tmp.path, env: environment, stdout: "pipe", stderr: "pipe" },
@@ -83,12 +80,4 @@ describe("headless run default tools (#155)", () => {
8380
},
8481
20000,
8582
)
86-
87-
test(
88-
"AICTRL_ENABLE_TODO_TOOL=1 brings todowrite back",
89-
async () => {
90-
expect(await requestedTools(entries.full, { AICTRL_ENABLE_TODO_TOOL: "1" })).toContain("todowrite")
91-
},
92-
20000,
93-
)
9483
})

‎packages/cli/test/session/headless-prompt-budget.test.ts‎

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,8 @@ import { HeadlessRun } from "../../src/flag/headless-run"
1919
*/
2020
const HEADLESS_PREFIX_BUDGET = 35_500
2121

22-
// The lite build's prompt contract, with any inherited todo opt-in cleared.
23-
const HEADLESS_ENV: Record<string, string | undefined> = {
24-
AICTRL_HEADLESS: "true",
25-
AICTRL_ENABLE_TODO_TOOL: undefined,
26-
}
22+
// The lite build's prompt contract.
23+
const HEADLESS_ENV = { AICTRL_HEADLESS: "true" }
2724

2825
type Piece = { name: string; chars: number }
2926

@@ -66,8 +63,7 @@ describe("headless request prefix budget (#160)", () => {
6663
HeadlessRun.enable()
6764
for (const [key, value] of Object.entries(HEADLESS_ENV)) {
6865
previous[key] = process.env[key]
69-
if (value === undefined) delete process.env[key]
70-
else process.env[key] = value
66+
process.env[key] = value
7167
}
7268
})
7369

‎packages/cli/test/tool/registry.test.ts‎

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { afterEach, beforeEach, describe, expect, test } from "bun:test"
1+
import { afterEach, describe, expect, test } from "bun:test"
22
import path from "path"
33
import fs from "fs/promises"
44
import os from "os"
@@ -183,17 +183,10 @@ describe("tool.registry", () => {
183183
})
184184

185185
describe("tool.registry todo tools (#155)", () => {
186-
const previous = process.env.AICTRL_ENABLE_TODO_TOOL
187-
188-
beforeEach(() => {
189-
delete process.env.AICTRL_ENABLE_TODO_TOOL
190-
})
191-
192186
afterEach(() => {
193187
HeadlessRun.disable()
194188
delete process.env.AICTRL_HEADLESS_RUN
195-
if (previous === undefined) delete process.env.AICTRL_ENABLE_TODO_TOOL
196-
else process.env.AICTRL_ENABLE_TODO_TOOL = previous
189+
delete process.env.AICTRL_ENABLE_TODO_TOOL
197190
})
198191

199192
async function toolIDs() {
@@ -216,12 +209,10 @@ describe("tool.registry todo tools (#155)", () => {
216209
expect(ids).not.toContain("todoread")
217210
})
218211

219-
test("AICTRL_ENABLE_TODO_TOOL brings todowrite back to a headless run", async () => {
212+
test("a headless run has no environment switch back to todowrite", async () => {
220213
HeadlessRun.enable()
221214
process.env.AICTRL_ENABLE_TODO_TOOL = "1"
222-
const ids = await toolIDs()
223-
expect(ids).toContain("todowrite")
224-
expect(ids).not.toContain("todoread")
215+
expect(await toolIDs()).not.toContain("todowrite")
225216
})
226217

227218
test("sessions outside a headless run keep todowrite", async () => {

0 commit comments

Comments
 (0)