@aragon-agent/cli 0.6.37 → 0.6.39
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 +2523 -2326
- package/README.md +3614 -3173
- package/dist/agent/controller.d.ts +142 -1
- package/dist/agent/controller.js +171 -3
- package/dist/agent/reducer.d.ts +41 -1
- package/dist/agent/reducer.js +25 -0
- package/dist/agent/system-prompt.d.ts +14 -0
- package/dist/agent/system-prompt.js +5 -0
- package/dist/cli.js +85 -27
- package/dist/commands/builtins.js +13 -1
- package/dist/commands/keys-command.d.ts +23 -0
- package/dist/commands/keys-command.js +38 -0
- package/dist/commands/perf.d.ts +7 -1
- package/dist/commands/perf.js +2 -2
- package/dist/commands/registry.d.ts +39 -0
- package/dist/commands/task-command.d.ts +39 -0
- package/dist/commands/task-command.js +102 -0
- package/dist/config/cli-commands.d.ts +23 -0
- package/dist/config/cli-commands.js +36 -0
- package/dist/config/env.js +17 -4
- package/dist/config/load.js +6 -1
- package/dist/config/schema.d.ts +43 -4
- package/dist/config/schema.js +31 -5
- package/dist/config/store.js +17 -2
- package/dist/diagnostics/doctor.d.ts +9 -0
- package/dist/diagnostics/doctor.js +34 -0
- package/dist/diagnostics/info.d.ts +71 -0
- package/dist/diagnostics/info.js +91 -5
- package/dist/exec/bridge/caller-tools.d.ts +96 -0
- package/dist/exec/bridge/caller-tools.js +213 -0
- package/dist/exec/bridge/decisions.d.ts +78 -0
- package/dist/exec/bridge/decisions.js +116 -0
- package/dist/exec/bridge/human-answers.d.ts +38 -0
- package/dist/exec/bridge/human-answers.js +164 -0
- package/dist/exec/bridge/limits.d.ts +68 -0
- package/dist/exec/bridge/limits.js +68 -0
- package/dist/exec/bridge/ndjson.d.ts +17 -0
- package/dist/exec/bridge/ndjson.js +83 -0
- package/dist/exec/bridge/pending.d.ts +70 -0
- package/dist/exec/bridge/pending.js +132 -0
- package/dist/exec/bridge/protocol.d.ts +346 -0
- package/dist/exec/bridge/protocol.js +256 -0
- package/dist/exec/bridge/server.d.ts +142 -0
- package/dist/exec/bridge/server.js +502 -0
- package/dist/exec/bridge/source.d.ts +58 -0
- package/dist/exec/bridge/source.js +145 -0
- package/dist/exec/bridge/writer.d.ts +26 -0
- package/dist/exec/bridge/writer.js +156 -0
- package/dist/exec/cli-commands.js +30 -2
- package/dist/exec/emitter.d.ts +16 -2
- package/dist/exec/emitter.js +26 -6
- package/dist/exec/events.d.ts +465 -10
- package/dist/exec/events.js +47 -1
- package/dist/exec/hooks/dispatch.d.ts +76 -0
- package/dist/exec/hooks/dispatch.js +169 -0
- package/dist/exec/hooks/index.d.ts +83 -0
- package/dist/exec/hooks/index.js +205 -0
- package/dist/exec/hooks/limits.d.ts +56 -0
- package/dist/exec/hooks/limits.js +49 -0
- package/dist/exec/hooks/registry.d.ts +49 -0
- package/dist/exec/hooks/registry.js +91 -0
- package/dist/exec/hooks/tool-wrapper.d.ts +94 -0
- package/dist/exec/hooks/tool-wrapper.js +154 -0
- package/dist/exec/hooks/turn.d.ts +61 -0
- package/dist/exec/hooks/turn.js +59 -0
- package/dist/exec/hooks/validate.d.ts +85 -0
- package/dist/exec/hooks/validate.js +310 -0
- package/dist/exec/index.d.ts +130 -4
- package/dist/exec/index.js +501 -10
- package/dist/exec/options.d.ts +124 -2
- package/dist/exec/options.js +413 -6
- package/dist/exec/permission.d.ts +9 -1
- package/dist/exec/permission.js +7 -1
- package/dist/exec/request/limits.d.ts +51 -0
- package/dist/exec/request/limits.js +49 -0
- package/dist/exec/request/scope.d.ts +106 -0
- package/dist/exec/request/scope.js +149 -0
- package/dist/exec/result/limits.d.ts +74 -0
- package/dist/exec/result/limits.js +72 -0
- package/dist/exec/result/prompt.d.ts +28 -0
- package/dist/exec/result/prompt.js +74 -0
- package/dist/exec/result/schema.d.ts +99 -0
- package/dist/exec/result/schema.js +464 -0
- package/dist/exec/result/sink.d.ts +56 -0
- package/dist/exec/result/sink.js +50 -0
- package/dist/exec/result/tool.d.ts +37 -0
- package/dist/exec/result/tool.js +118 -0
- package/dist/exec/runner.d.ts +395 -1
- package/dist/exec/runner.js +860 -8
- package/dist/exec/threads/aggregate.d.ts +34 -0
- package/dist/exec/threads/aggregate.js +30 -0
- package/dist/exec/threads/capacity.d.ts +23 -0
- package/dist/exec/threads/capacity.js +38 -0
- package/dist/exec/threads/dispatch-source.d.ts +60 -0
- package/dist/exec/threads/dispatch-source.js +106 -0
- package/dist/exec/threads/execute-threads.d.ts +53 -0
- package/dist/exec/threads/execute-threads.js +234 -0
- package/dist/exec/threads/gate.d.ts +42 -0
- package/dist/exec/threads/gate.js +81 -0
- package/dist/exec/threads/identity.d.ts +36 -0
- package/dist/exec/threads/identity.js +67 -0
- package/dist/exec/threads/interrupt.d.ts +29 -0
- package/dist/exec/threads/interrupt.js +79 -0
- package/dist/exec/threads/limits.d.ts +58 -0
- package/dist/exec/threads/limits.js +35 -0
- package/dist/exec/threads/outcome.d.ts +47 -0
- package/dist/exec/threads/outcome.js +26 -0
- package/dist/exec/threads/registry.d.ts +81 -0
- package/dist/exec/threads/registry.js +427 -0
- package/dist/exec/threads/runtime.d.ts +107 -0
- package/dist/exec/threads/runtime.js +523 -0
- package/dist/exec/threads/thread-source.d.ts +20 -0
- package/dist/exec/threads/thread-source.js +134 -0
- package/dist/fast/limits.d.ts +54 -0
- package/dist/fast/limits.js +54 -0
- package/dist/fast/summary-call.d.ts +72 -0
- package/dist/fast/summary-call.js +133 -0
- package/dist/fast/summary-prompt.d.ts +106 -0
- package/dist/fast/summary-prompt.js +320 -0
- package/dist/fast/task-summary-types.d.ts +66 -0
- package/dist/fast/task-summary-types.js +17 -0
- package/dist/logging/logger.d.ts +1 -1
- package/dist/proc/limits.d.ts +7 -0
- package/dist/proc/limits.js +7 -0
- package/dist/proc/output-streams.d.ts +50 -0
- package/dist/proc/output-streams.js +54 -0
- package/dist/team/runtime.d.ts +9 -0
- package/dist/team/runtime.js +8 -0
- package/dist/team/subagent.d.ts +15 -0
- package/dist/team/subagent.js +45 -1
- package/dist/tools/bash-tool.d.ts +10 -0
- package/dist/tools/bash-tool.js +71 -6
- package/dist/tools/index.d.ts +93 -1
- package/dist/tools/index.js +112 -3
- package/dist/tools/tool-gate.d.ts +100 -0
- package/dist/tools/tool-gate.js +87 -0
- package/dist/ui/App.js +224 -11
- package/dist/ui/Header.d.ts +28 -16
- package/dist/ui/Header.js +21 -19
- package/dist/ui/interaction-hints.js +6 -0
- package/dist/ui/layout/OverlayFrame.d.ts +18 -0
- package/dist/ui/layout/OverlayFrame.js +19 -4
- package/dist/ui/layout/header-hints.d.ts +55 -62
- package/dist/ui/layout/header-hints.js +59 -166
- package/dist/ui/layout/header-left.d.ts +115 -0
- package/dist/ui/layout/header-left.js +206 -0
- package/dist/ui/layout/shortcut-panel-layout.d.ts +191 -0
- package/dist/ui/layout/shortcut-panel-layout.js +321 -0
- package/dist/ui/layout/status-detail-layout.js +5 -1
- package/dist/ui/layout/task-summary-cell.d.ts +61 -0
- package/dist/ui/layout/task-summary-cell.js +65 -0
- package/dist/ui/overlays/HelpOverlay.d.ts +12 -0
- package/dist/ui/overlays/HelpOverlay.js +29 -2
- package/dist/ui/overlays/ShortcutPanel.d.ts +39 -0
- package/dist/ui/overlays/ShortcutPanel.js +106 -0
- package/dist/ui/scrollback/canvas.d.ts +3 -2
- package/dist/ui/scrollback/canvas.js +3 -2
- package/dist/ui/scrollback/scrollback-writer.d.ts +13 -1
- package/dist/ui/scrollback/scrollback-writer.js +109 -10
- package/dist/ui/shortcuts.d.ts +81 -0
- package/dist/ui/shortcuts.js +221 -0
- package/package.json +2 -2
|
@@ -19,6 +19,8 @@ import type { CliConfig, CompactionConfig, FastConfig, RetryConfig, TeamConfig,
|
|
|
19
19
|
import type { ModelRole } from '../config/model-profiles.js';
|
|
20
20
|
import { ModelWindows } from '../config/model-windows.js';
|
|
21
21
|
import { type ConfirmRequest } from '../tools/index.js';
|
|
22
|
+
import type { ToolGate } from '../tools/tool-gate.js';
|
|
23
|
+
import type { ToolHookRunner } from '../exec/hooks/tool-wrapper.js';
|
|
22
24
|
import type { ToolPermission } from '../exec/permission.js';
|
|
23
25
|
import { type ToolOutputListener } from '../tools/tool-output-store.js';
|
|
24
26
|
import type { FilePatch } from '../tools/patch.js';
|
|
@@ -28,6 +30,7 @@ import { ProcSupervisor } from '../proc/supervisor.js';
|
|
|
28
30
|
import type { ProcEventListener, ServiceSnapshot } from '../proc/types.js';
|
|
29
31
|
import { type FastStatus } from '../fast/wiring.js';
|
|
30
32
|
import type { FastEventListener, FastSnapshot } from '../fast/types.js';
|
|
33
|
+
import type { TaskSummaryOutcome } from '../fast/task-summary-types.js';
|
|
31
34
|
import { ContextMeter, type ContextUsageListener } from '../compaction/meter.js';
|
|
32
35
|
import type { CompactionEventListener, CompactionSnapshot, ContextUsageSnapshot } from '../compaction/types.js';
|
|
33
36
|
import type { AcceptanceItem, TodoEventListener, TodoSnapshot } from '../todo/types.js';
|
|
@@ -100,6 +103,36 @@ export interface ControllerDeps {
|
|
|
100
103
|
* `tools/index.ts`.
|
|
101
104
|
*/
|
|
102
105
|
permission?: ToolPermission;
|
|
106
|
+
/**
|
|
107
|
+
* The caller's per-call approval gate (headless-host-bridge §2.3.3).
|
|
108
|
+
*
|
|
109
|
+
* FORWARDED INTO `createBuiltinTools` AND NOWHERE ELSE. Unlike `permission` it
|
|
110
|
+
* does NOT reach subagents in this round: every child builds its own tool array
|
|
111
|
+
* through `team/runtime.ts`, and a fan-out that asked "may I run bash?" once per
|
|
112
|
+
* child for one decision is an approval waterfall, not a control. Absent on
|
|
113
|
+
* every existing path, which is what keeps the tool array identical there.
|
|
114
|
+
*/
|
|
115
|
+
toolGate?: ToolGate;
|
|
116
|
+
/**
|
|
117
|
+
* How long a decision may wait, added to the tool's own budget.
|
|
118
|
+
*
|
|
119
|
+
* SEE THE TWO-PHASE FORMULA AT `toolTimeoutOverrides`. The executor arms its
|
|
120
|
+
* timer BEFORE the approval wrapper runs, so a ceiling of `toolTimeoutMs` alone
|
|
121
|
+
* would give the tool less than its configured time by exactly however long the
|
|
122
|
+
* caller took to answer.
|
|
123
|
+
*/
|
|
124
|
+
decisionBudgetMs?: number;
|
|
125
|
+
/**
|
|
126
|
+
* The gate each SUBAGENT gets, keyed by its label (headless-host-bridge
|
|
127
|
+
* section 2.3.7).
|
|
128
|
+
*
|
|
129
|
+
* A SEPARATE FIELD BECAUSE THE SCOPE DIFFERS, NOT BECAUSE THE GATE DOES: every
|
|
130
|
+
* child reports `{id, label, depth: 1}` so the caller can render "subagent A
|
|
131
|
+
* wants to run bash", while the lead reports `depth: 0`. One shared instance
|
|
132
|
+
* would attribute every child's request to the lead. Absent outside bridge mode,
|
|
133
|
+
* which keeps a child's tool array identical today.
|
|
134
|
+
*/
|
|
135
|
+
toolGateForChild?: (label: string) => ToolGate;
|
|
103
136
|
/**
|
|
104
137
|
* `--append-system-prompt` text, appended verbatim to the composed prompt
|
|
105
138
|
* (§4.1). Threaded through `composeSystemPrompt` so it survives every rebuild
|
|
@@ -107,6 +140,41 @@ export interface ControllerDeps {
|
|
|
107
140
|
* once at construction and lost at the first `rebuildSystemPrompt()`.
|
|
108
141
|
*/
|
|
109
142
|
appendSystemPrompt?: string;
|
|
143
|
+
/**
|
|
144
|
+
* `submit_result`, registered with the lead's toolset when the caller supplied
|
|
145
|
+
* `--result-schema` (headless-request-scope-and-typed-result section 2.3.9).
|
|
146
|
+
*
|
|
147
|
+
* FORWARDED INTO `createBuiltinTools` AND NOWHERE ELSE. It must NOT reach a
|
|
148
|
+
* subagent: a child's product is a report that goes back through `task`, and a
|
|
149
|
+
* second submitter inside one request would compete with the lead's schema for the
|
|
150
|
+
* one accepted value. The child's `createBuiltinTools` call site is
|
|
151
|
+
* `team/subagent.ts`, and it deliberately passes nothing here.
|
|
152
|
+
*
|
|
153
|
+
* Absent on every existing path (the TUI, `-p`, every subcommand), which is what
|
|
154
|
+
* keeps their tool arrays byte-identical (I-S2).
|
|
155
|
+
*/
|
|
156
|
+
resultTool?: AgentTool;
|
|
157
|
+
/**
|
|
158
|
+
* The rendered `<structured_result>` block, or absent.
|
|
159
|
+
*
|
|
160
|
+
* SEE THE FIELD'S OWN COMMENT for why this is threaded through the controller
|
|
161
|
+
* rather than spliced once: the prompt is rebuilt, and a block that vanished on a
|
|
162
|
+
* rebuild would leave the model unaware of the contract it is being graded against.
|
|
163
|
+
*/
|
|
164
|
+
structuredResultBlock?: string;
|
|
165
|
+
/**
|
|
166
|
+
* The caller's lifecycle hooks (headless-threads-and-lifecycle-hooks section 2.4.9).
|
|
167
|
+
*
|
|
168
|
+
* FORWARDED INTO `createBuiltinTools` AND NOWHERE ELSE, in the same shape `resultTool`
|
|
169
|
+
* is: absent on every existing path (the TUI, `-p`, every subcommand and every
|
|
170
|
+
* subagent), which is what keeps their tool arrays byte-identical.
|
|
171
|
+
*
|
|
172
|
+
* THE CHILD'S CALL SITE IS DELIBERATELY SILENT ABOUT IT. Hooks see the LEAD's tool calls;
|
|
173
|
+
* a subagent builds its own array in `team/subagent.ts` and a hook installed for tenant
|
|
174
|
+
* isolation on the lead must not silently become a hook on every child's identical
|
|
175
|
+
* `read_file` - that is a different security decision with its own design (section 9.5).
|
|
176
|
+
*/
|
|
177
|
+
hookRunner?: ToolHookRunner;
|
|
110
178
|
/**
|
|
111
179
|
* The per-model window table from ~/.aragon-agent/model-windows.json.
|
|
112
180
|
* Injectable so tests can hand the controller a table without writing into
|
|
@@ -131,6 +199,17 @@ export interface PreflightResult {
|
|
|
131
199
|
/** 'config' for a missing/invalid key or unusable provider. */
|
|
132
200
|
kind?: 'config';
|
|
133
201
|
}
|
|
202
|
+
/** Inputs for one task-label attempt (`AgentController.summarizeTask`). */
|
|
203
|
+
export interface SummarizeTaskOptions {
|
|
204
|
+
/**
|
|
205
|
+
* Cancels the call. The CALLER also owns the epoch guard
|
|
206
|
+
* (tui-shortcut-drawer-and-task-summary D-10): aborting frees the connection,
|
|
207
|
+
* the epoch decides correctness, and neither substitutes for the other.
|
|
208
|
+
*/
|
|
209
|
+
signal?: AbortSignal;
|
|
210
|
+
/** The glyph a cut label ends with. ASCII by default so a headless caller is safe. */
|
|
211
|
+
ellipsis?: string;
|
|
212
|
+
}
|
|
134
213
|
export declare class AgentController {
|
|
135
214
|
private config;
|
|
136
215
|
private readonly agent;
|
|
@@ -143,6 +222,16 @@ export declare class AgentController {
|
|
|
143
222
|
private modelMetadataAbort;
|
|
144
223
|
private disposed;
|
|
145
224
|
private readonly tools;
|
|
225
|
+
/**
|
|
226
|
+
* Tools the CALLER serves, replaced on every `tool_register` (I-13).
|
|
227
|
+
*
|
|
228
|
+
* A SECOND ARRAY, NOT ONE GROWN ONE. `this.tools` is read by `listTools()`,
|
|
229
|
+
* `toolNames()`, `composeSystemPrompt()` and three "fixed at construction" flags
|
|
230
|
+
* (bash_output/bash_kill, task, todo_write). Growing it would silently change
|
|
231
|
+
* what those flags mean; keeping the caller's tools beside it gives one honest
|
|
232
|
+
* rule - the REGISTRY is the union, `this.tools` stays the host's own set.
|
|
233
|
+
*/
|
|
234
|
+
private callerTools;
|
|
146
235
|
private readonly skills;
|
|
147
236
|
private readonly skillsEnabled;
|
|
148
237
|
private onSkillsChanged?;
|
|
@@ -157,6 +246,17 @@ export declare class AgentController {
|
|
|
157
246
|
* Empty on every path but `aragon exec`.
|
|
158
247
|
*/
|
|
159
248
|
private readonly appendSystemPrompt;
|
|
249
|
+
/**
|
|
250
|
+
* The `<structured_result>` block (headless-request-scope-and-typed-result
|
|
251
|
+
* section 2.3.8), or `''`.
|
|
252
|
+
*
|
|
253
|
+
* A FIELD FOR THE SAME REASON `appendSystemPrompt` IS ONE: `composeSystemPrompt()`
|
|
254
|
+
* runs again on every rebuild, so a value spliced once at construction would vanish
|
|
255
|
+
* at the first `/cwd` change, skills rescan or mode flip - and the model would then
|
|
256
|
+
* be asked to submit a result it had never been told about. Empty on every path but
|
|
257
|
+
* `aragon exec --result-schema`.
|
|
258
|
+
*/
|
|
259
|
+
private readonly structuredResultBlock;
|
|
160
260
|
private effectiveMode;
|
|
161
261
|
private pendingMode;
|
|
162
262
|
private askRounds;
|
|
@@ -870,6 +970,24 @@ export declare class AgentController {
|
|
|
870
970
|
* consumed by `App` too, and this round promised to be purely additive.
|
|
871
971
|
*/
|
|
872
972
|
fastSnapshot(): FastSnapshot | null;
|
|
973
|
+
/**
|
|
974
|
+
* Summarise one user prompt into a header label, using the FAST tier only.
|
|
975
|
+
*
|
|
976
|
+
* NEVER REJECTS - the same contract `prompt()` has, and the reason `App` needs
|
|
977
|
+
* no `.catch`. Every failure is folded into a discriminated
|
|
978
|
+
* `TaskSummaryOutcome`, because a header label must not be able to produce an
|
|
979
|
+
* unhandled rejection in a TUI that has nowhere to show one.
|
|
980
|
+
*
|
|
981
|
+
* THE CONTROLLER OWNS THE `AbortController` AND ABORTS IT UNCONDITIONALLY on the
|
|
982
|
+
* way out: `runTaskSummaryCall` may return a `timeout` outcome while the request
|
|
983
|
+
* is still open (it is forbidden to abort its own call - see that file's
|
|
984
|
+
* header), so this `finally` is what actually releases the connection.
|
|
985
|
+
*
|
|
986
|
+
* EVERY `this.fast` READ IS BEHIND THE CLOSURE, exactly as the reviewer and the
|
|
987
|
+
* overseer wiring do it: `complete` is only reached from inside the call, which
|
|
988
|
+
* runs long after construction, so there is no use-before-assign.
|
|
989
|
+
*/
|
|
990
|
+
summarizeTask(anchor: string, opts?: SummarizeTaskOptions): Promise<TaskSummaryOutcome>;
|
|
873
991
|
/**
|
|
874
992
|
* Subscribe to occupancy publications. Returns an unsubscribe function.
|
|
875
993
|
*
|
|
@@ -970,6 +1088,29 @@ export declare class AgentController {
|
|
|
970
1088
|
* `subscribeTodos` and `subscribeFast` already set.
|
|
971
1089
|
*/
|
|
972
1090
|
subscribeCompaction(listener: CompactionEventListener): () => void;
|
|
973
|
-
/**
|
|
1091
|
+
/**
|
|
1092
|
+
* List usable tools (name + description) for the /tools command.
|
|
1093
|
+
*
|
|
1094
|
+
* THE UNION, SO THE VIEW AND THE REGISTRY CANNOT DISAGREE (I-13). `system/init`
|
|
1095
|
+
* builds `init.tools` from here, as do `/tools` and `/skills`; reporting only the
|
|
1096
|
+
* host's own set while the model is calling `db_query` is the exact failure that
|
|
1097
|
+
* invariant names - a caller reading "the model has no such tool" about a tool it
|
|
1098
|
+
* is watching run.
|
|
1099
|
+
*/
|
|
974
1100
|
listTools(): AgentTool[];
|
|
1101
|
+
/**
|
|
1102
|
+
* Replace the caller-served tool set. Called ONLY at a turn boundary.
|
|
1103
|
+
*
|
|
1104
|
+
* `Agent.setTools()` unregisters and re-registers entries in the LIVE registry.
|
|
1105
|
+
* This class already learned the lesson once: `submit_plan` mutates the mode from
|
|
1106
|
+
* INSIDE a tool execution, so rebuilding the array at that moment would mutate
|
|
1107
|
+
* the registry mid-iteration (see the comment on the plan tools). Hence the
|
|
1108
|
+
* refusal rather than a silently deferred write: a caller that registers
|
|
1109
|
+
* mid-turn gets an error it can act on, and the bridge defers the frame instead.
|
|
1110
|
+
*
|
|
1111
|
+
* `[]` is meaningful and not an omission: it means "restore the factory set".
|
|
1112
|
+
*/
|
|
1113
|
+
applyCallerTools(tools: AgentTool[]): void;
|
|
1114
|
+
/** The names the caller currently serves, for `ack.tools`. */
|
|
1115
|
+
callerToolNames(): string[];
|
|
975
1116
|
}
|
package/dist/agent/controller.js
CHANGED
|
@@ -11,7 +11,7 @@ import { resolveModelProfileState } from '../config/model-profile-resolution.js'
|
|
|
11
11
|
import { makeGetApiKey } from '../config/load.js';
|
|
12
12
|
import { updatePersistedConfig } from '../config/store.js';
|
|
13
13
|
import { ModelWindows } from '../config/model-windows.js';
|
|
14
|
-
import { createBuiltinTools } from '../tools/index.js';
|
|
14
|
+
import { createBuiltinTools, gatedToolTimeouts, } from '../tools/index.js';
|
|
15
15
|
import { createFileChangeStore } from '../tools/file-change-store.js';
|
|
16
16
|
import { createToolOutputStore, } from '../tools/tool-output-store.js';
|
|
17
17
|
import { TeamRuntime } from '../team/runtime.js';
|
|
@@ -34,6 +34,11 @@ import { getHomeRoot } from '../config/app-paths.js';
|
|
|
34
34
|
import { addSignalHook } from '../logging/install.js';
|
|
35
35
|
import { describeFastTierProblem, fastProviderOf, resolveFastTier } from '../fast/resolve.js';
|
|
36
36
|
import { FastWiring, offFastStatus } from '../fast/wiring.js';
|
|
37
|
+
// ONE-SHOT FAST CALLS (tui-shortcut-drawer-and-task-summary §6.1). The TYPES come
|
|
38
|
+
// from `fast/task-summary-types.ts` and only `fast/` produces them, so this module
|
|
39
|
+
// never learns how a label is made - it owns the controller and the epoch, which
|
|
40
|
+
// is the pair `runTaskSummaryCall` is forbidden to own (see its header).
|
|
41
|
+
import { createTaskSummaryDeps, runTaskSummaryCall } from '../fast/summary-call.js';
|
|
37
42
|
import { CompactionWiring, offCompactionSnapshot } from '../compaction/wiring.js';
|
|
38
43
|
import { ContextMeter } from '../compaction/meter.js';
|
|
39
44
|
import { TodoStore } from '../todo/store.js';
|
|
@@ -74,6 +79,16 @@ export class AgentController {
|
|
|
74
79
|
modelMetadataAbort = new AbortController();
|
|
75
80
|
disposed = false;
|
|
76
81
|
tools;
|
|
82
|
+
/**
|
|
83
|
+
* Tools the CALLER serves, replaced on every `tool_register` (I-13).
|
|
84
|
+
*
|
|
85
|
+
* A SECOND ARRAY, NOT ONE GROWN ONE. `this.tools` is read by `listTools()`,
|
|
86
|
+
* `toolNames()`, `composeSystemPrompt()` and three "fixed at construction" flags
|
|
87
|
+
* (bash_output/bash_kill, task, todo_write). Growing it would silently change
|
|
88
|
+
* what those flags mean; keeping the caller's tools beside it gives one honest
|
|
89
|
+
* rule - the REGISTRY is the union, `this.tools` stays the host's own set.
|
|
90
|
+
*/
|
|
91
|
+
callerTools = [];
|
|
77
92
|
skills;
|
|
78
93
|
skillsEnabled;
|
|
79
94
|
onSkillsChanged;
|
|
@@ -88,6 +103,17 @@ export class AgentController {
|
|
|
88
103
|
* Empty on every path but `aragon exec`.
|
|
89
104
|
*/
|
|
90
105
|
appendSystemPrompt;
|
|
106
|
+
/**
|
|
107
|
+
* The `<structured_result>` block (headless-request-scope-and-typed-result
|
|
108
|
+
* section 2.3.8), or `''`.
|
|
109
|
+
*
|
|
110
|
+
* A FIELD FOR THE SAME REASON `appendSystemPrompt` IS ONE: `composeSystemPrompt()`
|
|
111
|
+
* runs again on every rebuild, so a value spliced once at construction would vanish
|
|
112
|
+
* at the first `/cwd` change, skills rescan or mode flip - and the model would then
|
|
113
|
+
* be asked to submit a result it had never been told about. Empty on every path but
|
|
114
|
+
* `aragon exec --result-schema`.
|
|
115
|
+
*/
|
|
116
|
+
structuredResultBlock;
|
|
91
117
|
// -----------------------------------------------------------------------
|
|
92
118
|
// Plan mode (§3.1) — THE CONTROLLER OWNS THE MODE.
|
|
93
119
|
//
|
|
@@ -308,6 +334,10 @@ export class AgentController {
|
|
|
308
334
|
this.config = config;
|
|
309
335
|
this.cwd = config.cwd;
|
|
310
336
|
this.appendSystemPrompt = deps.appendSystemPrompt ?? '';
|
|
337
|
+
// ABOVE `createBuiltinTools` FOR THE REASON THE PROMPT FIELD IS: the tool has to
|
|
338
|
+
// exist before the array is built, and the block has to exist before the prompt is
|
|
339
|
+
// composed - and both are read again on every rebuild.
|
|
340
|
+
this.structuredResultBlock = deps.structuredResultBlock ?? '';
|
|
311
341
|
const humanInput = deps.humanInput ?? DENY_ALL_HUMAN_INPUT;
|
|
312
342
|
this.planInteractive = humanInput.neverPrompts !== true;
|
|
313
343
|
this.effectiveMode = config.startInPlanMode ? 'plan' : 'build';
|
|
@@ -431,6 +461,14 @@ export class AgentController {
|
|
|
431
461
|
// mid-dispatch and abort it at `idleTimeoutMs`. The queue pauses the
|
|
432
462
|
// CHILD's watchdog instead, which is the one that needs it (P0-4).
|
|
433
463
|
...(confirm ? { humanQueue: new TeamHumanQueue(confirm) } : {}),
|
|
464
|
+
// SITE ONE OF TWO FOR THE CHILD GATE (headless-host-bridge 2.3.7). The
|
|
465
|
+
// second is `runtime.ts::subDeps`; declaring it here and forgetting that
|
|
466
|
+
// one type-checks and forwards nothing, and every child would then run
|
|
467
|
+
// unapproved while the caller believes it is approving.
|
|
468
|
+
...(deps.toolGateForChild ? { toolGateFor: deps.toolGateForChild } : {}),
|
|
469
|
+
...(deps.decisionBudgetMs !== undefined
|
|
470
|
+
? { decisionBudgetMs: deps.decisionBudgetMs }
|
|
471
|
+
: {}),
|
|
434
472
|
// CHILDREN INHERIT THE POLICY (§4.2 invariant 3 / R-4 / AC-12).
|
|
435
473
|
// Without this line `--deny-tool bash` is bypassed by one `task` call
|
|
436
474
|
// and nothing anywhere reports it. Spread conditionally so a session
|
|
@@ -599,6 +637,15 @@ export class AgentController {
|
|
|
599
637
|
}),
|
|
600
638
|
]
|
|
601
639
|
: [],
|
|
640
|
+
// SPREAD, NEVER `resultTool: deps.resultTool` (the D-12 lesson, and the shape
|
|
641
|
+
// every optional dep in this bag follows). With no schema the options bag has no
|
|
642
|
+
// such key, so `createBuiltinTools` adds nothing and the tool array is the one it
|
|
643
|
+
// builds today - which is what makes I-S2 provable by object identity.
|
|
644
|
+
...(deps.resultTool ? { resultTool: deps.resultTool } : {}),
|
|
645
|
+
// THE HOOK PAIR, SPREAD FOR THE SAME REASON AND AT THE SAME SITE (D-12): a dep that
|
|
646
|
+
// is declared but not forwarded is dropped in silence, and the only symptom is that
|
|
647
|
+
// the caller's hooks never fire.
|
|
648
|
+
...(deps.hookRunner ? { hookRunner: deps.hookRunner } : {}),
|
|
602
649
|
// The PULL port. Absent when jobs are off, which leaves every tool object
|
|
603
650
|
// untouched and provable by identity.
|
|
604
651
|
...(this.jobsRegistered
|
|
@@ -663,6 +710,21 @@ export class AgentController {
|
|
|
663
710
|
// today — which is what makes "the TUI and `-p` are unchanged" provable by
|
|
664
711
|
// identity rather than by inspection.
|
|
665
712
|
...(deps.permission ? { permission: deps.permission } : {}),
|
|
713
|
+
// THE CALLER'S APPROVAL GATE, spread the same way and for the same reason
|
|
714
|
+
// (headless-host-bridge section 2.3.3): with no gate the options bag has no
|
|
715
|
+
// such key, so `createBuiltinTools` never enters its mapping branch and the
|
|
716
|
+
// tool OBJECTS are the ones it builds today.
|
|
717
|
+
//
|
|
718
|
+
// THE PAUSE IS `withHumanWait`, NOT A SECOND WRAPPER. `withPausedWatchdog` is
|
|
719
|
+
// documented as not reentrant, and this is the same function the plan tools
|
|
720
|
+
// already receive - so an approval wait and a question wait can never nest
|
|
721
|
+
// two pauses and resume the watchdog one level early.
|
|
722
|
+
...(deps.toolGate
|
|
723
|
+
? {
|
|
724
|
+
toolGate: deps.toolGate,
|
|
725
|
+
withToolWait: (fn) => this.withHumanWait(fn),
|
|
726
|
+
}
|
|
727
|
+
: {}),
|
|
666
728
|
// Omitted entirely when skills are off, which is what leaves the seven
|
|
667
729
|
// built-ins unwrapped and AC-G17 provable by object identity.
|
|
668
730
|
...(this.skillsEnabled
|
|
@@ -747,6 +809,22 @@ export class AgentController {
|
|
|
747
809
|
toolTimeoutOverrides: {
|
|
748
810
|
ask_user: config.planModeHumanTimeoutMs,
|
|
749
811
|
submit_plan: config.planModeHumanTimeoutMs,
|
|
812
|
+
// THE TWO PHASES, ADDED (headless-host-bridge §2.3.4). The executor
|
|
813
|
+
// arms its timer BEFORE the approval wrapper runs, so a ceiling of
|
|
814
|
+
// `toolTimeoutMs` alone would leave the tool less than its configured
|
|
815
|
+
// budget by exactly the time a human took to answer. ADMITTED
|
|
816
|
+
// OVER-GRANT: a caller that answers instantly still gets the sum. The
|
|
817
|
+
// opposite trade - charging the wait to the tool - would make "the
|
|
818
|
+
// human thought for two minutes" read as "this command is too slow",
|
|
819
|
+
// and a tool timeout is supposed to mean the latter.
|
|
820
|
+
//
|
|
821
|
+
// THE KEY SET IS `CALLER_GATED_TOOLS`, the same constant
|
|
822
|
+
// `ToolGate.applies` is built from (I-6): two lists here would drift,
|
|
823
|
+
// and the drift would be a gated tool with no wait budget - a
|
|
824
|
+
// five-minute approval that kills its own tool at 180 s.
|
|
825
|
+
...(deps.decisionBudgetMs !== undefined
|
|
826
|
+
? gatedToolTimeouts(config.toolTimeoutMs, deps.decisionBudgetMs)
|
|
827
|
+
: {}),
|
|
750
828
|
// A fan-out is not a hung tool either. Like the two above, this
|
|
751
829
|
// number only bites BECAUSE `task` races `ctx.signal` — the
|
|
752
830
|
// executor's timeout is cooperative, so an override is inert for any
|
|
@@ -946,6 +1024,11 @@ export class AgentController {
|
|
|
946
1024
|
// tools that make its promises achievable are registered. With the flag off
|
|
947
1025
|
// the prompt is byte-identical to the pre-feature text (I-3).
|
|
948
1026
|
jobsBlock: this.jobsRegistered ? buildBackgroundJobsBlock() : '',
|
|
1027
|
+
// FROM THE FIELD, so the block survives every rebuild for the reason the field's
|
|
1028
|
+
// own comment records. Empty everywhere but `aragon exec --result-schema`, and
|
|
1029
|
+
// `buildSystemPrompt` splices it conditionally - so every existing prompt is
|
|
1030
|
+
// byte-identical (I-S1).
|
|
1031
|
+
structuredResultBlock: this.structuredResultBlock,
|
|
949
1032
|
// Read from the FIELD on every rebuild, for the reason the field's own
|
|
950
1033
|
// comment gives. Empty everywhere but `aragon exec`, and `buildSystemPrompt`
|
|
951
1034
|
// splices it conditionally, so every existing prompt is byte-identical.
|
|
@@ -2273,6 +2356,60 @@ export class AgentController {
|
|
|
2273
2356
|
fastSnapshot() {
|
|
2274
2357
|
return this.fast?.snapshot() ?? null;
|
|
2275
2358
|
}
|
|
2359
|
+
/**
|
|
2360
|
+
* Summarise one user prompt into a header label, using the FAST tier only.
|
|
2361
|
+
*
|
|
2362
|
+
* NEVER REJECTS - the same contract `prompt()` has, and the reason `App` needs
|
|
2363
|
+
* no `.catch`. Every failure is folded into a discriminated
|
|
2364
|
+
* `TaskSummaryOutcome`, because a header label must not be able to produce an
|
|
2365
|
+
* unhandled rejection in a TUI that has nowhere to show one.
|
|
2366
|
+
*
|
|
2367
|
+
* THE CONTROLLER OWNS THE `AbortController` AND ABORTS IT UNCONDITIONALLY on the
|
|
2368
|
+
* way out: `runTaskSummaryCall` may return a `timeout` outcome while the request
|
|
2369
|
+
* is still open (it is forbidden to abort its own call - see that file's
|
|
2370
|
+
* header), so this `finally` is what actually releases the connection.
|
|
2371
|
+
*
|
|
2372
|
+
* EVERY `this.fast` READ IS BEHIND THE CLOSURE, exactly as the reviewer and the
|
|
2373
|
+
* overseer wiring do it: `complete` is only reached from inside the call, which
|
|
2374
|
+
* runs long after construction, so there is no use-before-assign.
|
|
2375
|
+
*/
|
|
2376
|
+
async summarizeTask(anchor, opts = {}) {
|
|
2377
|
+
const deps = createTaskSummaryDeps({
|
|
2378
|
+
getConfig: () => this.config,
|
|
2379
|
+
hasKey: (id, role) => this.hasApiKey(id, role),
|
|
2380
|
+
getApiKey: (id, role) => this.resolveKey(id, role),
|
|
2381
|
+
complete: (providerId, request) => {
|
|
2382
|
+
if (this.fast === null) {
|
|
2383
|
+
// A session whose tier never registered has no fast transport at all;
|
|
2384
|
+
// rejecting here is caught by `runTaskSummaryCall` and reported as a
|
|
2385
|
+
// tier reason rather than as a crash.
|
|
2386
|
+
return Promise.reject(new Error('Fast tier is not registered for this session.'));
|
|
2387
|
+
}
|
|
2388
|
+
return this.fast.completeViaFastRegistry(providerId, request);
|
|
2389
|
+
},
|
|
2390
|
+
...(opts.ellipsis ? { ellipsis: () => opts.ellipsis } : {}),
|
|
2391
|
+
});
|
|
2392
|
+
const controller = new AbortController();
|
|
2393
|
+
const upstream = opts.signal;
|
|
2394
|
+
const onAbort = () => controller.abort();
|
|
2395
|
+
if (upstream) {
|
|
2396
|
+
if (upstream.aborted)
|
|
2397
|
+
controller.abort();
|
|
2398
|
+
else
|
|
2399
|
+
upstream.addEventListener('abort', onAbort);
|
|
2400
|
+
}
|
|
2401
|
+
try {
|
|
2402
|
+
return await runTaskSummaryCall(deps, {
|
|
2403
|
+
anchor,
|
|
2404
|
+
signal: controller.signal,
|
|
2405
|
+
now: Date.now(),
|
|
2406
|
+
});
|
|
2407
|
+
}
|
|
2408
|
+
finally {
|
|
2409
|
+
upstream?.removeEventListener('abort', onAbort);
|
|
2410
|
+
controller.abort();
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2276
2413
|
// -----------------------------------------------------------------------
|
|
2277
2414
|
// Context occupancy (context-usage-gauge-accuracy §4.2) — THREE FORWARDERS
|
|
2278
2415
|
//
|
|
@@ -2485,8 +2622,39 @@ export class AgentController {
|
|
|
2485
2622
|
return () => { };
|
|
2486
2623
|
return this.compaction.subscribe(listener);
|
|
2487
2624
|
}
|
|
2488
|
-
/**
|
|
2625
|
+
/**
|
|
2626
|
+
* List usable tools (name + description) for the /tools command.
|
|
2627
|
+
*
|
|
2628
|
+
* THE UNION, SO THE VIEW AND THE REGISTRY CANNOT DISAGREE (I-13). `system/init`
|
|
2629
|
+
* builds `init.tools` from here, as do `/tools` and `/skills`; reporting only the
|
|
2630
|
+
* host's own set while the model is calling `db_query` is the exact failure that
|
|
2631
|
+
* invariant names - a caller reading "the model has no such tool" about a tool it
|
|
2632
|
+
* is watching run.
|
|
2633
|
+
*/
|
|
2489
2634
|
listTools() {
|
|
2490
|
-
return this.tools;
|
|
2635
|
+
return this.callerTools.length === 0 ? this.tools : [...this.tools, ...this.callerTools];
|
|
2636
|
+
}
|
|
2637
|
+
/**
|
|
2638
|
+
* Replace the caller-served tool set. Called ONLY at a turn boundary.
|
|
2639
|
+
*
|
|
2640
|
+
* `Agent.setTools()` unregisters and re-registers entries in the LIVE registry.
|
|
2641
|
+
* This class already learned the lesson once: `submit_plan` mutates the mode from
|
|
2642
|
+
* INSIDE a tool execution, so rebuilding the array at that moment would mutate
|
|
2643
|
+
* the registry mid-iteration (see the comment on the plan tools). Hence the
|
|
2644
|
+
* refusal rather than a silently deferred write: a caller that registers
|
|
2645
|
+
* mid-turn gets an error it can act on, and the bridge defers the frame instead.
|
|
2646
|
+
*
|
|
2647
|
+
* `[]` is meaningful and not an omission: it means "restore the factory set".
|
|
2648
|
+
*/
|
|
2649
|
+
applyCallerTools(tools) {
|
|
2650
|
+
if (this.agent.state.isRunning) {
|
|
2651
|
+
throw new Error('applyCallerTools() called mid-turn; defer to the turn boundary.');
|
|
2652
|
+
}
|
|
2653
|
+
this.callerTools = [...tools];
|
|
2654
|
+
this.agent.setTools([...this.tools, ...this.callerTools]);
|
|
2655
|
+
}
|
|
2656
|
+
/** The names the caller currently serves, for `ack.tools`. */
|
|
2657
|
+
callerToolNames() {
|
|
2658
|
+
return this.callerTools.map((tool) => tool.name);
|
|
2491
2659
|
}
|
|
2492
2660
|
}
|
package/dist/agent/reducer.d.ts
CHANGED
|
@@ -20,9 +20,17 @@ import type { AcceptanceSnapshot, TodoItem, TodoSnapshot } from '../todo/types.j
|
|
|
20
20
|
import type { JobKind, ServiceSnapshot, ServiceStatus } from '../proc/types.js';
|
|
21
21
|
import type { RetryPhase } from './retry-view.js';
|
|
22
22
|
import type { FilePatch } from '../tools/patch.js';
|
|
23
|
+
import type { TaskSummaryView } from '../fast/task-summary-types.js';
|
|
23
24
|
export type NoticeLevel = 'info' | 'warn' | 'error';
|
|
24
25
|
export type ToastLevel = NoticeLevel | 'success';
|
|
25
|
-
export type Overlay = null | 'settings' | 'model' | 'help' | 'queue' | 'confirm' | 'question' | 'plan' | 'kit' | 'prompts' | 'prompt-pick' | 'agents'
|
|
26
|
+
export type Overlay = null | 'settings' | 'model' | 'help' | 'queue' | 'confirm' | 'question' | 'plan' | 'kit' | 'prompts' | 'prompt-pick' | 'agents'
|
|
27
|
+
/**
|
|
28
|
+
* tui-shortcut-drawer-and-task-summary §4.5: the keyboard drawer (`Ctrl+G` /
|
|
29
|
+
* `/keys`). A single field, so the drawer and `/help` cannot both hold the
|
|
30
|
+
* keyboard - which is also why `Ctrl+G` TAKES OVER from another overlay instead
|
|
31
|
+
* of being swallowed while one is open (P2-3).
|
|
32
|
+
*/
|
|
33
|
+
| 'shortcuts';
|
|
26
34
|
export type ToolStatus = 'pending' | 'running' | 'done' | 'error';
|
|
27
35
|
/** Ephemeral, auto-dismissed acknowledgement (spec §3.9). */
|
|
28
36
|
export interface Toast {
|
|
@@ -391,6 +399,23 @@ export interface ViewState {
|
|
|
391
399
|
* totals. `null` is what leaves an ordinary session's status bar unchanged.
|
|
392
400
|
*/
|
|
393
401
|
compaction: CompactionSnapshot | null;
|
|
402
|
+
/**
|
|
403
|
+
* The session's task label, or `null` while there is none
|
|
404
|
+
* (tui-shortcut-drawer-and-task-summary D-13).
|
|
405
|
+
*
|
|
406
|
+
* PURE VIEW STATE: never persisted, never in the conversation history, never in
|
|
407
|
+
* `config.json`. It lives here because the header and the drawer must read the
|
|
408
|
+
* SAME field - two copies would be two labels for one task.
|
|
409
|
+
*
|
|
410
|
+
* THE TWO WRITERS ARE `taskSummaryPending` / `taskSummaryReady`, and the two
|
|
411
|
+
* CLEARERS are `resetConversation` and `restoreEntries`: clearing is the
|
|
412
|
+
* reducer's job (it is the only place that knows a task ended), while
|
|
413
|
+
* REBUILDING after `/resume` is the App wrapper's job, because only it can see
|
|
414
|
+
* the restored entries before they are reduced.
|
|
415
|
+
*/
|
|
416
|
+
taskSummary: TaskSummaryView | null;
|
|
417
|
+
/** A summary call is in flight. Drives the drawer's `summarizing` line and `/task`. */
|
|
418
|
+
taskSummaryPending: boolean;
|
|
394
419
|
seq: number;
|
|
395
420
|
/** The `kind: 'team'` entry the live dispatch is writing into. */
|
|
396
421
|
teamEntryId?: string;
|
|
@@ -562,6 +587,21 @@ export type ViewAction = {
|
|
|
562
587
|
} | {
|
|
563
588
|
type: 'restoreEntries';
|
|
564
589
|
entries: Entry[];
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* The task label lifecycle (tui-shortcut-drawer-and-task-summary §7.1).
|
|
593
|
+
*
|
|
594
|
+
* TWO ACTIONS, because "a call started" and "a call finished" are different
|
|
595
|
+
* facts and the drawer reports them differently (`summarizing` vs the label).
|
|
596
|
+
* `taskSummaryReady` carries `null` in exactly one case: the call failed AND
|
|
597
|
+
* `taskSummary.fallback` is off, which is a deliberate "show nothing" rather
|
|
598
|
+
* than an empty string that would read as a broken feature.
|
|
599
|
+
*/
|
|
600
|
+
| {
|
|
601
|
+
type: 'taskSummaryPending';
|
|
602
|
+
} | {
|
|
603
|
+
type: 'taskSummaryReady';
|
|
604
|
+
view: TaskSummaryView | null;
|
|
565
605
|
} | {
|
|
566
606
|
type: 'setAgentMode';
|
|
567
607
|
mode: AgentMode;
|
package/dist/agent/reducer.js
CHANGED
|
@@ -67,6 +67,8 @@ export function initialViewState(seed = {}) {
|
|
|
67
67
|
retry: null,
|
|
68
68
|
fast: null,
|
|
69
69
|
compaction: null,
|
|
70
|
+
taskSummary: null,
|
|
71
|
+
taskSummaryPending: false,
|
|
70
72
|
seq: 0,
|
|
71
73
|
teamEntryId: undefined,
|
|
72
74
|
todoEntryId: undefined,
|
|
@@ -906,9 +908,25 @@ export function viewReducer(state, action) {
|
|
|
906
908
|
turnProduced: false,
|
|
907
909
|
errorNoticed: false,
|
|
908
910
|
aborted: false,
|
|
911
|
+
// THE TASK LABEL IS PART OF THE TASK (D-13). `/clear`, `/reset` and a
|
|
912
|
+
// session switch all land here, so the header loses its label without the
|
|
913
|
+
// App needing a second cleanup path. `/clear screen` does NOT come here -
|
|
914
|
+
// it keeps the conversation, so it keeps the label.
|
|
915
|
+
taskSummary: null,
|
|
916
|
+
taskSummaryPending: false,
|
|
909
917
|
};
|
|
910
918
|
case 'setOverlay':
|
|
911
919
|
return { ...state, overlay: action.overlay };
|
|
920
|
+
// --- Task summary (tui-shortcut-drawer-and-task-summary §7.1) ----------
|
|
921
|
+
//
|
|
922
|
+
// TWO FIELDS, ONE DIRECTION: `pending` is a latch the App raises before the
|
|
923
|
+
// call and releases on the result, and `taskSummary` is only ever replaced
|
|
924
|
+
// wholesale. Neither is persisted and neither is derived from `entries`, so a
|
|
925
|
+
// resumed conversation shows nothing until the App rebuilds the label.
|
|
926
|
+
case 'taskSummaryPending':
|
|
927
|
+
return { ...state, taskSummaryPending: true };
|
|
928
|
+
case 'taskSummaryReady':
|
|
929
|
+
return { ...state, taskSummary: action.view, taskSummaryPending: false };
|
|
912
930
|
// Pure mirror write. The App only ever dispatches what
|
|
913
931
|
// `controller.setAgentMode()` reported it ADOPTED, so this reducer has no
|
|
914
932
|
// deferral logic of its own to get out of step with (§3.1 / R-P7).
|
|
@@ -1423,6 +1441,13 @@ export function viewReducer(state, action) {
|
|
|
1423
1441
|
// NOT cleared — it is the session's own tier projection, which a
|
|
1424
1442
|
// `/resume` does not change.
|
|
1425
1443
|
fastEntryId: undefined,
|
|
1444
|
+
// `/resume` ADOPTS ANOTHER CONVERSATION, so the previous conversation's
|
|
1445
|
+
// label describes something that is no longer on screen. The reducer only
|
|
1446
|
+
// CLEARS it; the App's `makeCtx().dispatch` wrapper rebuilds one from
|
|
1447
|
+
// `action.entries` (D-13), because it is the only layer that sees the
|
|
1448
|
+
// restored entries BEFORE this branch runs.
|
|
1449
|
+
taskSummary: null,
|
|
1450
|
+
taskSummaryPending: false,
|
|
1426
1451
|
};
|
|
1427
1452
|
}
|
|
1428
1453
|
// --- Background services (§3.10 / D-11) -----------------------------
|
|
@@ -114,6 +114,20 @@ export interface SystemPromptParams {
|
|
|
114
114
|
* the child does not have.
|
|
115
115
|
*/
|
|
116
116
|
jobsBlock?: string;
|
|
117
|
+
/**
|
|
118
|
+
* The rendered `<structured_result>` block, or `''`
|
|
119
|
+
* (headless-request-scope-and-typed-result section 2.3.8).
|
|
120
|
+
*
|
|
121
|
+
* Spliced conditionally exactly as every block above is, which is what keeps I-S1
|
|
122
|
+
* true: a run without `--result-schema` produces the pre-feature prompt BYTE FOR
|
|
123
|
+
* BYTE for a fixed tool array.
|
|
124
|
+
*
|
|
125
|
+
* POSITION IS THE SEMANTICS. It goes after every subsystem block and before
|
|
126
|
+
* `## Additional instructions`: it describes the SHAPE OF THE ANSWER, which is
|
|
127
|
+
* closer to the end of the work than any "how to behave" block, while the caller's
|
|
128
|
+
* own appended instructions still come last and still win.
|
|
129
|
+
*/
|
|
130
|
+
structuredResultBlock?: string;
|
|
117
131
|
}
|
|
118
132
|
/** Describe the active shell so the model does not emit incompatible syntax. */
|
|
119
133
|
export declare function describeShell(): {
|
|
@@ -44,6 +44,7 @@ export function buildSystemPrompt(params) {
|
|
|
44
44
|
const fastBlock = params.fastBlock ?? '';
|
|
45
45
|
const backgroundBlock = params.backgroundBlock ?? '';
|
|
46
46
|
const jobsBlock = params.jobsBlock ?? '';
|
|
47
|
+
const structuredResultBlock = params.structuredResultBlock ?? '';
|
|
47
48
|
const appended = (params.appendSystemPrompt ?? '').trim();
|
|
48
49
|
return [
|
|
49
50
|
'You are AragonAgent, an autonomous coding assistant operating inside a terminal (TUI).',
|
|
@@ -67,6 +68,10 @@ export function buildSystemPrompt(params) {
|
|
|
67
68
|
...(fastBlock ? ['', fastBlock] : []),
|
|
68
69
|
...(backgroundBlock ? ['', backgroundBlock] : []),
|
|
69
70
|
...(jobsBlock ? ['', jobsBlock] : []),
|
|
71
|
+
// AFTER every subsystem block, BEFORE the caller's own instructions: it describes
|
|
72
|
+
// the shape of the answer, which is the last thing to know before answering, while
|
|
73
|
+
// `## Additional instructions` still has the final word (section 2.3.8).
|
|
74
|
+
...(structuredResultBlock ? ['', structuredResultBlock] : []),
|
|
70
75
|
'',
|
|
71
76
|
'Operating guidance:',
|
|
72
77
|
'- Prefer reading files and inspecting the workspace before making changes.',
|