@aragon-agent/cli 0.5.3 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +670 -540
- package/README.md +1105 -932
- package/dist/agent/controller.d.ts +71 -1
- package/dist/agent/controller.js +135 -1
- package/dist/agent/headless.d.ts +29 -0
- package/dist/agent/headless.js +86 -1
- package/dist/agent/reducer.d.ts +45 -0
- package/dist/agent/reducer.js +80 -1
- package/dist/agent/system-prompt.d.ts +8 -0
- package/dist/agent/system-prompt.js +2 -0
- package/dist/cli.js +72 -6
- package/dist/commands/builtins.d.ts +2 -2
- package/dist/commands/builtins.js +217 -3
- package/dist/commands/registry.d.ts +27 -1
- package/dist/config/cli-commands.js +20 -2
- package/dist/config/env.d.ts +2 -1
- package/dist/config/env.js +60 -5
- package/dist/config/load.d.ts +27 -0
- package/dist/config/load.js +67 -2
- package/dist/config/schema.d.ts +111 -2
- package/dist/config/schema.js +111 -2
- package/dist/config/store.js +37 -1
- package/dist/session/persist.d.ts +19 -1
- package/dist/session/persist.js +21 -2
- package/dist/team/subagent.js +11 -0
- package/dist/todo/follow-through.d.ts +117 -0
- package/dist/todo/follow-through.js +186 -0
- package/dist/todo/limits.d.ts +114 -0
- package/dist/todo/limits.js +114 -0
- package/dist/todo/normalize.d.ts +33 -0
- package/dist/todo/normalize.js +117 -0
- package/dist/todo/panel-rows.d.ts +53 -0
- package/dist/todo/panel-rows.js +103 -0
- package/dist/todo/prompt.d.ts +54 -0
- package/dist/todo/prompt.js +76 -0
- package/dist/todo/store.d.ts +86 -0
- package/dist/todo/store.js +183 -0
- package/dist/todo/todo-tool.d.ts +38 -0
- package/dist/todo/todo-tool.js +134 -0
- package/dist/todo/types.d.ts +62 -0
- package/dist/todo/types.js +12 -0
- package/dist/tools/index.d.ts +16 -2
- package/dist/tools/index.js +13 -0
- package/dist/ui/App.d.ts +22 -1
- package/dist/ui/App.js +292 -13
- package/dist/ui/StatusBar.d.ts +18 -0
- package/dist/ui/StatusBar.js +5 -2
- package/dist/ui/TodoPanel.d.ts +48 -0
- package/dist/ui/TodoPanel.js +74 -0
- package/dist/ui/TodoStrip.d.ts +36 -0
- package/dist/ui/TodoStrip.js +32 -0
- package/dist/ui/Transcript.js +11 -1
- package/dist/ui/entries/TodoCard.d.ts +31 -0
- package/dist/ui/entries/TodoCard.js +35 -0
- package/dist/ui/glyphs.d.ts +12 -0
- package/dist/ui/glyphs.js +8 -0
- package/dist/ui/layout/AppShell.d.ts +34 -1
- package/dist/ui/layout/AppShell.js +3 -3
- package/dist/ui/layout/ScrollViewport.d.ts +13 -1
- package/dist/ui/layout/ScrollViewport.js +2 -2
- package/dist/ui/layout/rail.d.ts +56 -0
- package/dist/ui/layout/rail.js +68 -0
- package/dist/ui/overlays/HelpOverlay.js +5 -0
- package/dist/ui/overlays/SettingsScreen.d.ts +12 -1
- package/dist/ui/overlays/SettingsScreen.js +38 -1
- package/package.json +3 -2
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `todo_write` — the ONLY writer of `TodoStore` (todo-plan-execution §3.3 /
|
|
3
|
+
* §4.1).
|
|
4
|
+
*
|
|
5
|
+
* ASCII ONLY: `src/todo/**` is inside the glyph scanner's scope.
|
|
6
|
+
*
|
|
7
|
+
* FULL-LIST REPLACEMENT, NO INCREMENTAL OPERATIONS (D-1). A delta protocol
|
|
8
|
+
* (`todo_add` / `todo_complete`) lets the model's belief and the rendered panel
|
|
9
|
+
* drift apart with nothing anywhere able to notice, and needs an id the model
|
|
10
|
+
* must keep stable across calls - a field it will eventually get wrong. A full
|
|
11
|
+
* replacement is idempotent, order-free and self-healing.
|
|
12
|
+
*
|
|
13
|
+
* THE SCHEMA DECLARES SHAPE, NOT POLICY (C-8): no `enum`, no `maxItems`, no
|
|
14
|
+
* `minItems`, and `activeForm` is not `required`. `ajv` is an OPTIONAL
|
|
15
|
+
* dependency of core, so any of those would reject a payload where it happens to
|
|
16
|
+
* be installed and repair the identical payload where it is not.
|
|
17
|
+
*
|
|
18
|
+
* PURE STATE: no filesystem, no network, no child process, no human wait. It
|
|
19
|
+
* therefore must NOT be routed through `withPausedWatchdog` (C-9) -
|
|
20
|
+
* `IdleWatchdog.pause()` is a boolean rather than a counter, so a nested
|
|
21
|
+
* pause/resume around an instant call would re-arm the watchdog inside an outer
|
|
22
|
+
* human wait.
|
|
23
|
+
*/
|
|
24
|
+
import { type AgentTool } from '@aragon-agent/core';
|
|
25
|
+
import type { TodoStore } from './store.js';
|
|
26
|
+
export interface TodoToolDeps {
|
|
27
|
+
store: TodoStore;
|
|
28
|
+
/** Read LIVE, never cached: `/todo off` flips this; it cannot unregister (C-1). */
|
|
29
|
+
isEnabled: () => boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Turned off mid-session. A NON-ERROR result on purpose: the user turned a
|
|
33
|
+
* display off, the model did nothing wrong, and an error result invites a retry.
|
|
34
|
+
*/
|
|
35
|
+
export declare const TODO_OFF_REFUSAL = "Todo planning is off for this session. Just do the work and say what you did.";
|
|
36
|
+
/** The structural half of R-c: a one-step list is not a plan. */
|
|
37
|
+
export declare const TOO_SMALL_REFUSAL = "A one-step list is not a plan. Just do the work and summarize when you are done.";
|
|
38
|
+
export declare function createTodoTool(deps: TodoToolDeps): AgentTool;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `todo_write` — the ONLY writer of `TodoStore` (todo-plan-execution §3.3 /
|
|
3
|
+
* §4.1).
|
|
4
|
+
*
|
|
5
|
+
* ASCII ONLY: `src/todo/**` is inside the glyph scanner's scope.
|
|
6
|
+
*
|
|
7
|
+
* FULL-LIST REPLACEMENT, NO INCREMENTAL OPERATIONS (D-1). A delta protocol
|
|
8
|
+
* (`todo_add` / `todo_complete`) lets the model's belief and the rendered panel
|
|
9
|
+
* drift apart with nothing anywhere able to notice, and needs an id the model
|
|
10
|
+
* must keep stable across calls - a field it will eventually get wrong. A full
|
|
11
|
+
* replacement is idempotent, order-free and self-healing.
|
|
12
|
+
*
|
|
13
|
+
* THE SCHEMA DECLARES SHAPE, NOT POLICY (C-8): no `enum`, no `maxItems`, no
|
|
14
|
+
* `minItems`, and `activeForm` is not `required`. `ajv` is an OPTIONAL
|
|
15
|
+
* dependency of core, so any of those would reject a payload where it happens to
|
|
16
|
+
* be installed and repair the identical payload where it is not.
|
|
17
|
+
*
|
|
18
|
+
* PURE STATE: no filesystem, no network, no child process, no human wait. It
|
|
19
|
+
* therefore must NOT be routed through `withPausedWatchdog` (C-9) -
|
|
20
|
+
* `IdleWatchdog.pause()` is a boolean rather than a counter, so a nested
|
|
21
|
+
* pause/resume around an instant call would re-arm the watchdog inside an outer
|
|
22
|
+
* human wait.
|
|
23
|
+
*/
|
|
24
|
+
import { errorResult, textResult } from '@aragon-agent/core';
|
|
25
|
+
import { TODO_LIMITS } from './limits.js';
|
|
26
|
+
/**
|
|
27
|
+
* Turned off mid-session. A NON-ERROR result on purpose: the user turned a
|
|
28
|
+
* display off, the model did nothing wrong, and an error result invites a retry.
|
|
29
|
+
*/
|
|
30
|
+
export const TODO_OFF_REFUSAL = 'Todo planning is off for this session. Just do the work and say what you did.';
|
|
31
|
+
/** The structural half of R-c: a one-step list is not a plan. */
|
|
32
|
+
export const TOO_SMALL_REFUSAL = 'A one-step list is not a plan. Just do the work and summarize when you are done.';
|
|
33
|
+
/**
|
|
34
|
+
* The model-facing description. Deliberately close to Claude Code's `TodoWrite`
|
|
35
|
+
* (R-f): what a model already knows about that tool transfers directly, and a
|
|
36
|
+
* model's priors are worth more than our naming taste - the same argument
|
|
37
|
+
* `team/task-tool.ts` records for mirroring `Task`.
|
|
38
|
+
*/
|
|
39
|
+
const TODO_WRITE_DESCRIPTION = 'Record and update the plan for a multi-step task. Send the complete list every time - ' +
|
|
40
|
+
'this tool replaces the whole list. Use it for work with three or more distinct steps, ' +
|
|
41
|
+
'and skip it entirely for anything you can finish in one or two. Exactly one item may be ' +
|
|
42
|
+
'`in_progress`; mark an item `completed` and the next one `in_progress` in the same call, ' +
|
|
43
|
+
`the moment the step is done. At most ${TODO_LIMITS.maxItems} items; extras are dropped, so ` +
|
|
44
|
+
'keep within it. This list is how the user follows a long task, so keep it current.';
|
|
45
|
+
export function createTodoTool(deps) {
|
|
46
|
+
return {
|
|
47
|
+
name: 'todo_write',
|
|
48
|
+
label: 'Update todos',
|
|
49
|
+
description: TODO_WRITE_DESCRIPTION,
|
|
50
|
+
parameters: {
|
|
51
|
+
type: 'object',
|
|
52
|
+
properties: {
|
|
53
|
+
todos: {
|
|
54
|
+
type: 'array',
|
|
55
|
+
description: 'The COMPLETE list, every time. This replaces the previous list; there is ' +
|
|
56
|
+
'no add or update operation.',
|
|
57
|
+
items: {
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
content: {
|
|
61
|
+
type: 'string',
|
|
62
|
+
description: `Imperative, <= ${TODO_LIMITS.contentChars} chars. What the step is. ` +
|
|
63
|
+
'e.g. "Add the rail to AppShell".',
|
|
64
|
+
},
|
|
65
|
+
activeForm: {
|
|
66
|
+
type: 'string',
|
|
67
|
+
description: `Present continuous, <= ${TODO_LIMITS.activeFormChars} chars, shown while ` +
|
|
68
|
+
'this step is in progress. e.g. "Adding the rail to AppShell".',
|
|
69
|
+
},
|
|
70
|
+
status: {
|
|
71
|
+
type: 'string',
|
|
72
|
+
description: 'pending | in_progress | completed. Exactly one item may be in_progress.',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
required: ['content', 'status'],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
required: ['todos'],
|
|
80
|
+
},
|
|
81
|
+
async execute(_id, params) {
|
|
82
|
+
// 1. Turned off mid-session. The tool array is immutable for the life of
|
|
83
|
+
// the session (C-1), so `/todo off` cannot unregister - it flips this
|
|
84
|
+
// closure instead.
|
|
85
|
+
if (!deps.isEnabled()) {
|
|
86
|
+
deps.store.reject(TODO_OFF_REFUSAL);
|
|
87
|
+
return textResult(TODO_OFF_REFUSAL);
|
|
88
|
+
}
|
|
89
|
+
const todos = params.todos;
|
|
90
|
+
// 2. GATED ON AN EMPTY STORE, and that gate is the whole subtlety.
|
|
91
|
+
// Shrinking an EXISTING list to one item is legitimate - the model
|
|
92
|
+
// merged two steps - so the guard must fire only on a fresh plan.
|
|
93
|
+
if (deps.store.isEmpty() &&
|
|
94
|
+
Array.isArray(todos) &&
|
|
95
|
+
todos.length < TODO_LIMITS.minFreshItems) {
|
|
96
|
+
deps.store.reject(TOO_SMALL_REFUSAL);
|
|
97
|
+
return textResult(TOO_SMALL_REFUSAL);
|
|
98
|
+
}
|
|
99
|
+
// 3. Repair, never reject. Zero survivors is the single hard failure, and
|
|
100
|
+
// it leaves the previous list untouched (AC-9).
|
|
101
|
+
const result = deps.store.write(todos);
|
|
102
|
+
if (result.items.length === 0) {
|
|
103
|
+
return errorResult('No usable todo items: each needs a non-empty content string.');
|
|
104
|
+
}
|
|
105
|
+
return textResult(buildResultText(result.items, result.repairs));
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The tool result — THE SINGLE STRONGEST LEVER ON R-b ("one at a time"), because
|
|
111
|
+
* unlike the system prompt it is re-read on every single call. There is
|
|
112
|
+
* deliberately NO RATE LIMIT to compete with it: a cap would fire exactly when
|
|
113
|
+
* the model is being most diligent and produce a refusal it cannot act on, and
|
|
114
|
+
* the costs a cap would have contained are already gone (§3.8 collapses N calls
|
|
115
|
+
* into ONE transcript entry, and the store is memory-only).
|
|
116
|
+
*/
|
|
117
|
+
function buildResultText(items, repairs) {
|
|
118
|
+
const done = items.filter((item) => item.status === 'completed').length;
|
|
119
|
+
const activeIndex = items.findIndex((item) => item.status === 'in_progress');
|
|
120
|
+
const lines = [`Todos updated. ${done}/${items.length} done.`];
|
|
121
|
+
if (activeIndex >= 0) {
|
|
122
|
+
lines.push(`In progress: ${items[activeIndex].activeForm}`);
|
|
123
|
+
const next = items.slice(activeIndex + 1).find((item) => item.status === 'pending');
|
|
124
|
+
if (next)
|
|
125
|
+
lines.push(`Next: ${next.content}`);
|
|
126
|
+
lines.push('Call todo_write again the moment that item is finished - exactly one item in', 'progress at a time, and do not batch several completions into one call.');
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
lines.push('Every item is complete. Tell the user what you did.');
|
|
130
|
+
}
|
|
131
|
+
for (const repair of repairs)
|
|
132
|
+
lines.push(`Note: ${repair}`);
|
|
133
|
+
return lines.join('\n');
|
|
134
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime shapes for the todo subsystem (todo-plan-execution §5.1).
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY: `src/todo/**` is inside the glyph scanner's scope.
|
|
5
|
+
*
|
|
6
|
+
* `TodoEvent` is CLI-LOCAL and is deliberately NOT a member of core's
|
|
7
|
+
* `AgentEvent` union (D-11), exactly as `TeamEvent` is not: core freezes its
|
|
8
|
+
* runtime export list (`public-api.test.ts`) and forbids host coupling
|
|
9
|
+
* (`no-host-coupling.test.ts`), so a new event member would break the first and
|
|
10
|
+
* teach core about a host shape it has no business defining.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Re-exported so `config/schema.ts` can name the mode without importing the
|
|
14
|
+
* decision module (todo-plan-followthrough §3.1). TYPE-ONLY, so the apparent
|
|
15
|
+
* cycle `types -> follow-through -> types` is erased at compile time and there
|
|
16
|
+
* is no runtime import at all.
|
|
17
|
+
*/
|
|
18
|
+
export type { FollowThroughMode } from './follow-through.js';
|
|
19
|
+
export type TodoStatus = 'pending' | 'in_progress' | 'completed';
|
|
20
|
+
export interface TodoItem {
|
|
21
|
+
/** Imperative, sanitized, <= TODO_LIMITS.contentChars. */
|
|
22
|
+
content: string;
|
|
23
|
+
/** Present continuous, shown while in progress. Falls back to `content`. */
|
|
24
|
+
activeForm: string;
|
|
25
|
+
status: TodoStatus;
|
|
26
|
+
}
|
|
27
|
+
/** The immutable projection the UI renders. Rebuilt on every write. */
|
|
28
|
+
export interface TodoSnapshot {
|
|
29
|
+
items: TodoItem[];
|
|
30
|
+
total: number;
|
|
31
|
+
doneCount: number;
|
|
32
|
+
/** Index of the single in_progress item, or -1 when everything is completed. */
|
|
33
|
+
activeIndex: number;
|
|
34
|
+
/**
|
|
35
|
+
* Stamped by an INJECTED clock (`TodoStore`'s constructor takes
|
|
36
|
+
* `now?: () => number`, defaulting to `Date.now`), for the reason `TeamPanel`
|
|
37
|
+
* takes a `now?` prop: a snapshot test that reads the wall clock is a test
|
|
38
|
+
* that fails on a slow machine and nowhere else (P2-4).
|
|
39
|
+
*/
|
|
40
|
+
updatedAt: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Why a list went away.
|
|
44
|
+
*
|
|
45
|
+
* `user` - `/todo clear`.
|
|
46
|
+
* `reset` - `/reset`, or a `/resume` whose file carried no list (§3.13).
|
|
47
|
+
* `turn` - every item was completed when the next user turn started.
|
|
48
|
+
* `stale` - unfinished, and unwritten for more than `TODO_LIMITS.staleTurns`
|
|
49
|
+
* consecutive turns (§3.2 / P1-7).
|
|
50
|
+
*/
|
|
51
|
+
export type TodoClearReason = 'user' | 'reset' | 'turn' | 'stale';
|
|
52
|
+
export type TodoEvent = {
|
|
53
|
+
type: 'updated';
|
|
54
|
+
snapshot: TodoSnapshot;
|
|
55
|
+
} | {
|
|
56
|
+
type: 'rejected';
|
|
57
|
+
reason: string;
|
|
58
|
+
} | {
|
|
59
|
+
type: 'cleared';
|
|
60
|
+
reason: TodoClearReason;
|
|
61
|
+
};
|
|
62
|
+
export type TodoEventListener = (event: TodoEvent) => void;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime shapes for the todo subsystem (todo-plan-execution §5.1).
|
|
3
|
+
*
|
|
4
|
+
* ASCII ONLY: `src/todo/**` is inside the glyph scanner's scope.
|
|
5
|
+
*
|
|
6
|
+
* `TodoEvent` is CLI-LOCAL and is deliberately NOT a member of core's
|
|
7
|
+
* `AgentEvent` union (D-11), exactly as `TeamEvent` is not: core freezes its
|
|
8
|
+
* runtime export list (`public-api.test.ts`) and forbids host coupling
|
|
9
|
+
* (`no-host-coupling.test.ts`), so a new event member would break the first and
|
|
10
|
+
* teach core about a host shape it has no business defining.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -85,6 +85,20 @@ export interface BuiltinToolsOptions {
|
|
|
85
85
|
* burns a turn.
|
|
86
86
|
*/
|
|
87
87
|
planTools?: AgentTool[];
|
|
88
|
+
/**
|
|
89
|
+
* `todo_write`, appended verbatim (todo-plan-execution §3.6).
|
|
90
|
+
*
|
|
91
|
+
* THROUGH THE FACTORY FOR THE REASON `teamTools` RECORDS BELOW (C-2):
|
|
92
|
+
* `tools.test.ts::C7` asserts `HOST_TOOL_NAMES` equals what this factory
|
|
93
|
+
* PRODUCES, so appending `todo_write` in `AgentController` after the factory
|
|
94
|
+
* returned would turn C7 red with a message about two lists of names that
|
|
95
|
+
* says nothing about todos.
|
|
96
|
+
*
|
|
97
|
+
* An empty array (the `--no-todo` path) leaves the tool list byte-identical to
|
|
98
|
+
* the pre-todo build — AC-30, provable by object identity exactly as
|
|
99
|
+
* `--no-skills` and `--no-team` are.
|
|
100
|
+
*/
|
|
101
|
+
todoTools?: AgentTool[];
|
|
88
102
|
/**
|
|
89
103
|
* Team-mode tools, appended verbatim. The lead gets `[task]`; a subagent gets
|
|
90
104
|
* `[team_send, team_wait]`.
|
|
@@ -134,7 +148,7 @@ export interface BuiltinToolsOptions {
|
|
|
134
148
|
* is the whole of what this mechanism is worth, and saying so plainly beats
|
|
135
149
|
* implying it covers more.
|
|
136
150
|
*/
|
|
137
|
-
export declare const SKILL_TOOL_FLOOR: readonly ["read_file", "list_dir", "glob", "grep", "skill", "skill_find", "ask_user", "submit_plan", "task"];
|
|
151
|
+
export declare const SKILL_TOOL_FLOOR: readonly ["read_file", "list_dir", "glob", "grep", "skill", "skill_find", "ask_user", "submit_plan", "task", "todo_write"];
|
|
138
152
|
/**
|
|
139
153
|
* The five tools plan mode refuses: exactly the complement of `SKILL_TOOL_FLOOR`
|
|
140
154
|
* within `HOST_TOOL_NAMES` — "the five that change something", the set this file
|
|
@@ -167,5 +181,5 @@ export declare const PLAN_MODE_BLOCKED_TOOLS: ReadonlySet<string>;
|
|
|
167
181
|
* declaration MEANS, and that reading must not change with how the CLI was
|
|
168
182
|
* started.
|
|
169
183
|
*/
|
|
170
|
-
export declare const HOST_TOOL_NAMES: readonly ["read_file", "write_file", "edit_file", "list_dir", "glob", "grep", "bash", "skill", "skill_find", "skill_install", "skill_create", "ask_user", "submit_plan", "task"];
|
|
184
|
+
export declare const HOST_TOOL_NAMES: readonly ["read_file", "write_file", "edit_file", "list_dir", "glob", "grep", "bash", "skill", "skill_find", "skill_install", "skill_create", "ask_user", "submit_plan", "todo_write", "task"];
|
|
171
185
|
export declare function createBuiltinTools(options: BuiltinToolsOptions): AgentTool[];
|
package/dist/tools/index.js
CHANGED
|
@@ -52,6 +52,13 @@ export const SKILL_TOOL_FLOOR = [
|
|
|
52
52
|
// level down, with the child inheriting `agentMode` through the same closure.
|
|
53
53
|
// Blocking it would forfeit parallel research, which is plan mode's best use.
|
|
54
54
|
'task',
|
|
55
|
+
// `todo_write` joins the floor rather than the blocked set (D-12), and the
|
|
56
|
+
// partition test below is what forces that choice to be conscious. A todo list
|
|
57
|
+
// is a DISPLAY: it writes no file and runs no shell. More concretely, a skill
|
|
58
|
+
// whose `allowed-tools` omitted it would make the planning UI unreachable
|
|
59
|
+
// inside that skill's frame — the identical dead end that put `ask_user` /
|
|
60
|
+
// `submit_plan` / `task` here.
|
|
61
|
+
'todo_write',
|
|
55
62
|
];
|
|
56
63
|
/**
|
|
57
64
|
* The five tools plan mode refuses: exactly the complement of `SKILL_TOOL_FLOOR`
|
|
@@ -105,6 +112,7 @@ export const HOST_TOOL_NAMES = [
|
|
|
105
112
|
'skill_create',
|
|
106
113
|
'ask_user',
|
|
107
114
|
'submit_plan',
|
|
115
|
+
'todo_write',
|
|
108
116
|
// `team_send` / `team_wait` are DELIBERATELY ABSENT (P2-5). This constant
|
|
109
117
|
// answers "can a skill's `allowed-tools` declaration ever take effect?", and
|
|
110
118
|
// those two are never registered on a lead — so a skill naming them genuinely
|
|
@@ -227,6 +235,11 @@ export function createBuiltinTools(options) {
|
|
|
227
235
|
if (options.planTools && options.planTools.length > 0) {
|
|
228
236
|
tools = [...tools, ...options.planTools];
|
|
229
237
|
}
|
|
238
|
+
// AFTER `planTools`, BEFORE `teamTools`, so a lead's `task` stays last and
|
|
239
|
+
// `HOST_TOOL_NAMES` changes by one insertion rather than a reshuffle.
|
|
240
|
+
if (options.todoTools && options.todoTools.length > 0) {
|
|
241
|
+
tools = [...tools, ...options.todoTools];
|
|
242
|
+
}
|
|
230
243
|
// BEFORE both wrappers, so the lead's `task` is covered by the plan gate and
|
|
231
244
|
// the ceiling like everything else, and a child's comm tools are covered by
|
|
232
245
|
// everything except the ceiling (see `policyExempt`).
|
package/dist/ui/App.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import React from 'react';
|
|
8
8
|
import type { AgentController } from '../agent/controller.js';
|
|
9
9
|
import type { ConfirmRequest } from '../tools/index.js';
|
|
10
|
-
import { type Overlay } from '../agent/reducer.js';
|
|
10
|
+
import { type Overlay, type ToastLevel } from '../agent/reducer.js';
|
|
11
11
|
import { type RenderMode } from './layout/frame.js';
|
|
12
12
|
import { type FrameGeometry } from './layout/regions.js';
|
|
13
13
|
import type { MouseSource } from '../input/stdin-mouse-filter.js';
|
|
@@ -15,6 +15,27 @@ import type { HumanInputBridge } from '../tools/human-input.js';
|
|
|
15
15
|
export interface ConfirmBridge {
|
|
16
16
|
handler: ((req: ConfirmRequest) => Promise<boolean>) | null;
|
|
17
17
|
}
|
|
18
|
+
/** What the settings screen's `Max tokens` field resolved to. */
|
|
19
|
+
export interface SettingsMaxTokens {
|
|
20
|
+
/** Pushed into the live controller. `undefined` is AUTO. */
|
|
21
|
+
applied: number | undefined;
|
|
22
|
+
/** Written to the config file, or `'skip'` to leave the stored value alone. */
|
|
23
|
+
persist: number | null | 'skip';
|
|
24
|
+
/** Replaces the generic "Settings saved." acknowledgement when present. */
|
|
25
|
+
toast?: [ToastLevel, string];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Decide what the `Max tokens` field means, WITHOUT letting a bad value in it
|
|
29
|
+
* discard the rest of the form.
|
|
30
|
+
*
|
|
31
|
+
* The three outcomes are genuinely different and must stay distinguishable:
|
|
32
|
+
* `auto` is a setting, a clamped number is a setting the user should be told
|
|
33
|
+
* about, and nonsense is not a setting at all — it keeps the previous value and
|
|
34
|
+
* says so, rather than silently resolving to a default the user never chose.
|
|
35
|
+
*
|
|
36
|
+
* Exported for the tests; there is no other caller.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveSettingsMaxTokens(raw: string, previous: number | undefined): SettingsMaxTokens;
|
|
18
39
|
export interface AppProps {
|
|
19
40
|
controller: AgentController;
|
|
20
41
|
version: string;
|