@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
package/dist/exec/events.d.ts
CHANGED
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
* the same reason: a shape, erased at compile time.
|
|
14
14
|
*/
|
|
15
15
|
import type { ServiceStatus } from '../proc/types.js';
|
|
16
|
+
import type { AgentScope, DecisionInstallPayload, DecisionKind, DecisionToolPayload } from './bridge/protocol.js';
|
|
17
|
+
import type { ExecThreadEndReason } from './threads/limits.js';
|
|
18
|
+
import type { HookPoint } from './hooks/limits.js';
|
|
19
|
+
import type { NormalizedPlan, NormalizedQuestion } from '../tools/human-input.js';
|
|
16
20
|
/**
|
|
17
21
|
* `EXEC_SCHEMA_VERSION` appears on `system` and on `result` and NOWHERE ELSE.
|
|
18
22
|
* ADDING A FIELD OR AN EVENT TYPE NEVER BUMPS IT; renaming or removing one does.
|
|
@@ -23,9 +27,38 @@ import type { ServiceStatus } from '../proc/types.js';
|
|
|
23
27
|
*/
|
|
24
28
|
export declare const EXEC_SCHEMA_VERSION = 1;
|
|
25
29
|
/** The three baselines of section 4.2. `plan` is a MODE, never a filter. */
|
|
26
|
-
export type ExecPermissionMode = 'auto' | 'plan' | 'strict';
|
|
30
|
+
export type ExecPermissionMode = 'auto' | 'plan' | 'strict' | 'ask';
|
|
27
31
|
export type ExecOutputFormat = 'text' | 'json' | 'stream-json';
|
|
28
|
-
|
|
32
|
+
/**
|
|
33
|
+
* `bridge` is `stream-json` plus a request/response channel on stdin
|
|
34
|
+
* (headless-host-bridge). It REQUIRES `--output-format stream-json`, for the same
|
|
35
|
+
* structural reason `stream-json` does: a per-event write is where `seq` is
|
|
36
|
+
* stamped, and `json` writes one object at the very end.
|
|
37
|
+
*/
|
|
38
|
+
export type ExecInputFormat = 'text' | 'stream-json' | 'bridge';
|
|
39
|
+
/**
|
|
40
|
+
* Present on every event a bridge caller can correlate. All five are OPTIONAL and
|
|
41
|
+
* absent in `stream-json` mode, which is what keeps that stream byte-identical
|
|
42
|
+
* (AC-2): a field that is not set is not serialized.
|
|
43
|
+
*
|
|
44
|
+
* `seq` is stamped by `BridgeWriter`, never by an emitter, so "gap-free" is a
|
|
45
|
+
* property of one loop rather than of every construction site.
|
|
46
|
+
*/
|
|
47
|
+
export interface ExecCorrelation {
|
|
48
|
+
seq?: number;
|
|
49
|
+
requestId?: string;
|
|
50
|
+
/**
|
|
51
|
+
* WHICH CONVERSATION THIS EVENT BELONGS TO (headless-threads-and-lifecycle-hooks
|
|
52
|
+
* section 2.3.10, I-T10).
|
|
53
|
+
*
|
|
54
|
+
* ABSENT OUTSIDE THREAD MODE, which is what keeps `stream-json` byte-identical
|
|
55
|
+
* (I-C1): a field that is not set is not serialized. Inside thread mode it is stamped
|
|
56
|
+
* on EVERY line that carries a `sessionId` - see `ExecRunner.emit`, which is the one
|
|
57
|
+
* place that can promise it, plus the six explicit `bridge.write` sites that bypass
|
|
58
|
+
* `emit()`.
|
|
59
|
+
*/
|
|
60
|
+
threadId?: string;
|
|
61
|
+
}
|
|
29
62
|
/**
|
|
30
63
|
* Why the run ended.
|
|
31
64
|
*
|
|
@@ -58,7 +91,7 @@ export interface ExecErrorInfo {
|
|
|
58
91
|
code: string;
|
|
59
92
|
message: string;
|
|
60
93
|
}
|
|
61
|
-
export interface ExecSystemInitEvent {
|
|
94
|
+
export interface ExecSystemInitEvent extends ExecCorrelation {
|
|
62
95
|
type: 'system';
|
|
63
96
|
subtype: 'init';
|
|
64
97
|
schemaVersion: number;
|
|
@@ -87,6 +120,16 @@ export interface ExecSystemInitEvent {
|
|
|
87
120
|
* wrong costs the user their conversation, so this build states it.
|
|
88
121
|
*/
|
|
89
122
|
capabilities?: string[];
|
|
123
|
+
/**
|
|
124
|
+
* `2` when this run speaks the bridge protocol, absent otherwise.
|
|
125
|
+
*
|
|
126
|
+
* A NEW OPTIONAL FIELD, so `EXEC_SCHEMA_VERSION` does not move (file header).
|
|
127
|
+
* It exists because `capabilities` answers "which frames may I send" and this
|
|
128
|
+
* answers "what shape are they" - and only the second can ever be bumped
|
|
129
|
+
* incompatibly. A caller that checks it before `includes('bridge')` is doing the
|
|
130
|
+
* two-step in the right order.
|
|
131
|
+
*/
|
|
132
|
+
protocolVersion?: number;
|
|
90
133
|
/**
|
|
91
134
|
* Absolute path of the session file THIS run reads and writes; `null` under
|
|
92
135
|
* `--no-save-session` (web-use-durable-control-and-tier-accountability W2).
|
|
@@ -111,8 +154,27 @@ export interface ExecSystemInitEvent {
|
|
|
111
154
|
* latter.
|
|
112
155
|
*/
|
|
113
156
|
sessionFile?: string | null;
|
|
157
|
+
/**
|
|
158
|
+
* Thread mode only: the pool this run opened with (section 4.3 / AC-8).
|
|
159
|
+
*
|
|
160
|
+
* DECLARED HERE BECAUSE THE LINE CARRIES IT: an object literal spread into a return value
|
|
161
|
+
* escapes excess-property checking, so a misspelled key or a dropped field compiles and
|
|
162
|
+
* then silently disappears from the wire. `default` names the thread a caller is already
|
|
163
|
+
* talking to when it sends no `threadId`.
|
|
164
|
+
*/
|
|
165
|
+
threads?: {
|
|
166
|
+
maxOpen: number;
|
|
167
|
+
concurrency: number;
|
|
168
|
+
default: string;
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* `true` when this run accepts `hook_register` / `hook_answer`, i.e. whenever it speaks the
|
|
172
|
+
* bridge protocol (section 4.6). A CAPABILITY, NOT A COUNT: zero hooks registered is still
|
|
173
|
+
* a hook-capable run.
|
|
174
|
+
*/
|
|
175
|
+
hooks?: boolean;
|
|
114
176
|
}
|
|
115
|
-
export interface ExecUserEvent {
|
|
177
|
+
export interface ExecUserEvent extends ExecCorrelation {
|
|
116
178
|
type: 'user';
|
|
117
179
|
sessionId: string;
|
|
118
180
|
turn: number;
|
|
@@ -121,11 +183,18 @@ export interface ExecUserEvent {
|
|
|
121
183
|
* `todo_continue` marks a message the CLI generated on the caller's behalf
|
|
122
184
|
* through the follow-through loop. A wrapper replaying a transcript has to be
|
|
123
185
|
* able to tell those apart from what it actually sent.
|
|
186
|
+
*
|
|
187
|
+
* `result_repair` is the second such message (headless-request-scope-and-typed-
|
|
188
|
+
* result section 2.3.5): the CLI telling the model that its `submit_result` was
|
|
189
|
+
* rejected, or never happened at all. A NEW MEMBER OF AN EXISTING UNION, which is
|
|
190
|
+
* the additive shape this file's header allows - a consumer that only knows the
|
|
191
|
+
* first two members already has to handle a value it does not recognise, and a
|
|
192
|
+
* value it does not recognise must not change what it does with the other two.
|
|
124
193
|
*/
|
|
125
|
-
source: 'caller' | 'todo_continue';
|
|
194
|
+
source: 'caller' | 'todo_continue' | 'result_repair';
|
|
126
195
|
}
|
|
127
196
|
/** One caller input and its complete automatic follow-through chain. */
|
|
128
|
-
export interface ExecTurnStateEvent {
|
|
197
|
+
export interface ExecTurnStateEvent extends ExecCorrelation {
|
|
129
198
|
type: 'turn_state';
|
|
130
199
|
sessionId: string;
|
|
131
200
|
requestSeq: number;
|
|
@@ -152,13 +221,13 @@ export interface ExecThinkingEvent {
|
|
|
152
221
|
turn: number;
|
|
153
222
|
text: string;
|
|
154
223
|
}
|
|
155
|
-
export interface ExecAssistantEvent {
|
|
224
|
+
export interface ExecAssistantEvent extends ExecCorrelation {
|
|
156
225
|
type: 'assistant';
|
|
157
226
|
sessionId: string;
|
|
158
227
|
turn: number;
|
|
159
228
|
text: string;
|
|
160
229
|
}
|
|
161
|
-
export interface ExecToolCallEvent {
|
|
230
|
+
export interface ExecToolCallEvent extends ExecCorrelation {
|
|
162
231
|
type: 'tool_call';
|
|
163
232
|
sessionId: string;
|
|
164
233
|
turn: number;
|
|
@@ -166,7 +235,7 @@ export interface ExecToolCallEvent {
|
|
|
166
235
|
name: string;
|
|
167
236
|
input: Record<string, unknown>;
|
|
168
237
|
}
|
|
169
|
-
export interface ExecToolResultEvent {
|
|
238
|
+
export interface ExecToolResultEvent extends ExecCorrelation {
|
|
170
239
|
type: 'tool_result';
|
|
171
240
|
sessionId: string;
|
|
172
241
|
turn: number;
|
|
@@ -352,6 +421,135 @@ export interface ExecRetryEvent {
|
|
|
352
421
|
errorType: string;
|
|
353
422
|
delayMs: number;
|
|
354
423
|
}
|
|
424
|
+
/**
|
|
425
|
+
* The bridge control channel (headless-host-bridge §4.2.2).
|
|
426
|
+
*
|
|
427
|
+
* SEVEN ADDITIVE EVENT TYPES, AND `EXEC_SCHEMA_VERSION` DOES NOT MOVE - the rule
|
|
428
|
+
* this file's header states: adding a field or an event type never bumps it.
|
|
429
|
+
*
|
|
430
|
+
* They exist because a callback API has to be a wire API for an embedder, and a
|
|
431
|
+
* wire API needs an id, an answer and an outcome that can be logged. `ack` proves
|
|
432
|
+
* a request was received; `decision_request` / `decision_resolved` are one
|
|
433
|
+
* decision's two ends, and keeping BOTH is what makes "the caller denied it" and
|
|
434
|
+
* "the caller never answered" distinguishable after the fact - a run that only
|
|
435
|
+
* recorded the refusal would look identical to a timeout in every transcript.
|
|
436
|
+
*/
|
|
437
|
+
export interface ExecAckEvent extends ExecCorrelation {
|
|
438
|
+
type: 'ack';
|
|
439
|
+
/** The caller's own id, echoed. Absent when the frame carried none. */
|
|
440
|
+
id?: string;
|
|
441
|
+
accepted: boolean;
|
|
442
|
+
/** Why it was refused, when it was: `not_running`, `plan_mode_read_only`, ... */
|
|
443
|
+
code?: string;
|
|
444
|
+
message?: string;
|
|
445
|
+
/** `false` means this frame started driving a turn right away. */
|
|
446
|
+
queued?: boolean;
|
|
447
|
+
/** The runner's own turn counter, known only when the turn begins. */
|
|
448
|
+
requestSeq?: number;
|
|
449
|
+
/** `steer` only: the receipt id Core will confirm in a `steering` event. */
|
|
450
|
+
steeringId?: string;
|
|
451
|
+
/**
|
|
452
|
+
* `interrupt{requestId}` only: the message had not started, so it was deleted
|
|
453
|
+
* from the queue rather than aborting a turn. A caller that asked to withdraw
|
|
454
|
+
* its own queued message needs this to know nothing else was disturbed.
|
|
455
|
+
*/
|
|
456
|
+
cancelled?: 'queued';
|
|
457
|
+
/** `tool_register` only: whose tool array was replaced. Always `'lead'`. */
|
|
458
|
+
scope?: 'lead';
|
|
459
|
+
/** `tool_register` only: the names that are now registered. */
|
|
460
|
+
tools?: string[];
|
|
461
|
+
/** `tool_register` only: the first turn the new set can be used in. */
|
|
462
|
+
appliedAtTurn?: number;
|
|
463
|
+
/**
|
|
464
|
+
* `thread_open` only: the session id this thread writes (section 2.3.2's ack table).
|
|
465
|
+
*
|
|
466
|
+
* Stated rather than left to be derived: `explicitSessionId` wins and the default thread
|
|
467
|
+
* reuses the process id, so a caller that guessed `${base}-${threadId}` would be wrong in
|
|
468
|
+
* two of the three cases.
|
|
469
|
+
*/
|
|
470
|
+
sessionId?: string;
|
|
471
|
+
/** `thread_open` only: the thread's session file, `null` under `--no-save-session`. */
|
|
472
|
+
sessionFile?: string | null;
|
|
473
|
+
/** `thread_open` only. Always `false`: a thread never resumes (section 1.4). */
|
|
474
|
+
resumed?: boolean;
|
|
475
|
+
/** `hook_register` only: the ids that are now live, in registration order. */
|
|
476
|
+
hooks?: string[];
|
|
477
|
+
/** `thread_list` only: one summary per thread, in open order. */
|
|
478
|
+
threads?: Array<{
|
|
479
|
+
threadId: string;
|
|
480
|
+
sessionId: string;
|
|
481
|
+
state: 'opening' | 'open' | 'closing' | 'closed';
|
|
482
|
+
requests: number;
|
|
483
|
+
turns: number;
|
|
484
|
+
inFlight: boolean;
|
|
485
|
+
}>;
|
|
486
|
+
/** Non-fatal notes about an ACCEPTED frame (a clipped description, say). */
|
|
487
|
+
warnings?: string[];
|
|
488
|
+
}
|
|
489
|
+
export interface ExecDecisionRequestEvent {
|
|
490
|
+
type: 'decision_request';
|
|
491
|
+
requestId: string;
|
|
492
|
+
sessionId: string;
|
|
493
|
+
kind: DecisionKind;
|
|
494
|
+
createdAt: number;
|
|
495
|
+
deadlineAt: number;
|
|
496
|
+
timeoutMs: number;
|
|
497
|
+
tool?: DecisionToolPayload;
|
|
498
|
+
questions?: NormalizedQuestion[];
|
|
499
|
+
plan?: NormalizedPlan;
|
|
500
|
+
install?: DecisionInstallPayload;
|
|
501
|
+
/** Always present on the wire; the type keeps it optional for stub callers. */
|
|
502
|
+
agent?: AgentScope;
|
|
503
|
+
}
|
|
504
|
+
export interface ExecDecisionResolvedEvent {
|
|
505
|
+
type: 'decision_resolved';
|
|
506
|
+
requestId: string;
|
|
507
|
+
sessionId: string;
|
|
508
|
+
kind: DecisionKind;
|
|
509
|
+
/**
|
|
510
|
+
* `error` is the fail-closed answer to a frame that could not be trusted; it is
|
|
511
|
+
* distinct from `aborted` because one is the caller's fault and the other is
|
|
512
|
+
* the turn's.
|
|
513
|
+
*/
|
|
514
|
+
outcome: 'allow' | 'deny' | 'timeout' | 'aborted' | 'error';
|
|
515
|
+
scope?: 'once' | 'session';
|
|
516
|
+
reason?: string;
|
|
517
|
+
}
|
|
518
|
+
export interface ExecToolInvokeEvent {
|
|
519
|
+
type: 'tool_invoke';
|
|
520
|
+
requestId: string;
|
|
521
|
+
sessionId: string;
|
|
522
|
+
/** Core's tool call id, so this pairs with the `tool_call` event beside it. */
|
|
523
|
+
callId: string;
|
|
524
|
+
turn: number;
|
|
525
|
+
tool: string;
|
|
526
|
+
input: Record<string, unknown>;
|
|
527
|
+
timeoutMs: number;
|
|
528
|
+
deadlineAt: number;
|
|
529
|
+
agent: AgentScope;
|
|
530
|
+
}
|
|
531
|
+
export interface ExecToolCancelEvent {
|
|
532
|
+
type: 'tool_cancel';
|
|
533
|
+
requestId: string;
|
|
534
|
+
sessionId: string;
|
|
535
|
+
reason: 'timeout' | 'turn_aborted' | 'host_shutdown';
|
|
536
|
+
}
|
|
537
|
+
export interface ExecPongEvent extends ExecCorrelation {
|
|
538
|
+
type: 'pong';
|
|
539
|
+
id?: string;
|
|
540
|
+
now: number;
|
|
541
|
+
}
|
|
542
|
+
export interface ExecSteeringEvent extends ExecCorrelation {
|
|
543
|
+
type: 'steering';
|
|
544
|
+
sessionId: string;
|
|
545
|
+
/**
|
|
546
|
+
* ARRAY, NOT A SINGLE ID (review P1-7). Core's `steering_accepted` carries
|
|
547
|
+
* `ids: string[]` because one drain can accept a whole batch; reporting only the
|
|
548
|
+
* first would leave the rest permanently unconfirmed on the caller's side, which
|
|
549
|
+
* is precisely what the receipt exists to prevent.
|
|
550
|
+
*/
|
|
551
|
+
steeringIds: string[];
|
|
552
|
+
}
|
|
355
553
|
export interface ExecErrorEvent {
|
|
356
554
|
type: 'error';
|
|
357
555
|
sessionId: string;
|
|
@@ -396,6 +594,57 @@ export interface ExecResultEvent {
|
|
|
396
594
|
passed: number;
|
|
397
595
|
failed: number;
|
|
398
596
|
} | null;
|
|
597
|
+
/**
|
|
598
|
+
* The accepted structured result (headless-request-scope-and-typed-result
|
|
599
|
+
* section 2.3.7), or `null` when `--result-schema` was given and nothing was
|
|
600
|
+
* ever accepted.
|
|
601
|
+
*
|
|
602
|
+
* ABSENT, NOT `null`, WITHOUT THE FLAG (I-C1). "The field is missing" and "this
|
|
603
|
+
* build has no such capability" look identical in JSON, which is exactly why the
|
|
604
|
+
* flag-ON failure path must instead be an EXPLICIT `null`: a caller that passed
|
|
605
|
+
* `--result-schema` can then read one field and know whether it got what it asked
|
|
606
|
+
* for. Two different questions, two different encodings.
|
|
607
|
+
*/
|
|
608
|
+
structured?: {
|
|
609
|
+
value: unknown;
|
|
610
|
+
schemaName: string;
|
|
611
|
+
schemaBytes: number;
|
|
612
|
+
attempts: number;
|
|
613
|
+
acceptedAt: number;
|
|
614
|
+
} | null;
|
|
615
|
+
/**
|
|
616
|
+
* The model's last prose, present only when `result.result` was REPLACED by the
|
|
617
|
+
* canonical JSON text - that is, when a structured result was accepted.
|
|
618
|
+
*
|
|
619
|
+
* The README's `aragon exec --output-format json ... | jq -r .result` idiom has to
|
|
620
|
+
* keep working, and under a schema the answer IS the object, so `result` carries
|
|
621
|
+
* the object and the prose moves here instead of being lost.
|
|
622
|
+
*/
|
|
623
|
+
text?: string;
|
|
624
|
+
/**
|
|
625
|
+
* The thread-by-thread account, thread mode only (section 2.3.2 / AC-37).
|
|
626
|
+
*
|
|
627
|
+
* ABSENT IN EVERY NON-THREAD RUN. `items` is ordered by OPEN ORDER so a wrapper can
|
|
628
|
+
* zip it with the `thread_state` events it received; `usage`/`cost` on the `result`
|
|
629
|
+
* itself are the sums of these, which is why the same numbers appear twice - once as
|
|
630
|
+
* the total a single-call embedder wants, once per tenant.
|
|
631
|
+
*/
|
|
632
|
+
threads?: {
|
|
633
|
+
opened: number;
|
|
634
|
+
closed: number;
|
|
635
|
+
/** Threads whose account reported `isError`. */
|
|
636
|
+
failed: number;
|
|
637
|
+
items: Array<{
|
|
638
|
+
threadId: string;
|
|
639
|
+
sessionId: string;
|
|
640
|
+
requests: number;
|
|
641
|
+
turns: number;
|
|
642
|
+
usage: ExecUsage;
|
|
643
|
+
cost: ExecCost;
|
|
644
|
+
isError: boolean;
|
|
645
|
+
reason: ExecThreadEndReason;
|
|
646
|
+
}>;
|
|
647
|
+
};
|
|
399
648
|
error: ExecErrorInfo | null;
|
|
400
649
|
}
|
|
401
650
|
/**
|
|
@@ -421,7 +670,135 @@ export interface ExecJobEvent {
|
|
|
421
670
|
progress?: string;
|
|
422
671
|
exitCode?: number | null;
|
|
423
672
|
}
|
|
424
|
-
|
|
673
|
+
/**
|
|
674
|
+
* Why ONE request ended (headless-request-scope-and-typed-result section 2.4.6).
|
|
675
|
+
*
|
|
676
|
+
* A SEPARATE UNION FROM `ExecStopReason`, ON PURPOSE. Adding members to the process
|
|
677
|
+
* union would break every downstream exhaustive `switch` over it, and the two answer
|
|
678
|
+
* genuinely different questions: "why did this process end" and "why did this
|
|
679
|
+
* request end". A request can stop while the process keeps serving the next one -
|
|
680
|
+
* that is the entire point of the feature - so the two must not share a vocabulary
|
|
681
|
+
* that implies they move together.
|
|
682
|
+
*/
|
|
683
|
+
export type ExecRequestStopReason = 'completed' | 'max_turns' | 'timeout' | 'cancelled' | 'error';
|
|
684
|
+
/** A request-level failure. `code` is stable; `message` is prose for a human. */
|
|
685
|
+
export interface ExecRequestError {
|
|
686
|
+
code: string;
|
|
687
|
+
message: string;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* One request's account: what it cost, how it ended, and whether the caller got
|
|
691
|
+
* what it asked for (section 2.4.6).
|
|
692
|
+
*
|
|
693
|
+
* BRIDGE MODE ONLY (I-C3). The v1 input format (`--input-format stream-json`) is
|
|
694
|
+
* already a multi-prompt loop, but emitting this event there would change its byte
|
|
695
|
+
* output, which is the one thing the compatibility strategy cannot allow.
|
|
696
|
+
*/
|
|
697
|
+
export interface ExecRequestEndEvent extends ExecCorrelation {
|
|
698
|
+
type: 'request_end';
|
|
699
|
+
sessionId: string;
|
|
700
|
+
/** The caller's id, or the CLI's `prompt_<n>` when the frame carried none. */
|
|
701
|
+
requestId: string;
|
|
702
|
+
/**
|
|
703
|
+
* The same number space as `turn_state.requestSeq` and the `ack` for this
|
|
704
|
+
* request.
|
|
705
|
+
*
|
|
706
|
+
* OPTIONAL, AND THAT IS NOT SLOPPINESS (review P0-2). The number is minted at the
|
|
707
|
+
* moment a prompt is SERVED. A prompt cancelled while still in the queue was
|
|
708
|
+
* never served, so it never got one - and inventing one would collide with a
|
|
709
|
+
* later real request. Absent is the honest answer.
|
|
710
|
+
*/
|
|
711
|
+
requestSeq?: number;
|
|
712
|
+
stopReason: ExecRequestStopReason;
|
|
713
|
+
isError: boolean;
|
|
714
|
+
/** Turns this request took, including repair and follow-through continuations. */
|
|
715
|
+
turns: number;
|
|
716
|
+
durationMs: number;
|
|
717
|
+
usage: ExecUsage;
|
|
718
|
+
cost: ExecCost;
|
|
719
|
+
/** This request's last assistant text, clipped to `REQUEST_LIMITS.maxResultChars`. */
|
|
720
|
+
result: string;
|
|
721
|
+
/** The accepted structured result, or `null`; absent when no schema was given. */
|
|
722
|
+
structured?: {
|
|
723
|
+
value: unknown;
|
|
724
|
+
schemaName: string;
|
|
725
|
+
attempts: number;
|
|
726
|
+
} | null;
|
|
727
|
+
error: ExecRequestError | null;
|
|
728
|
+
}
|
|
729
|
+
/** Everything `request_end` needs; `type` is stamped by the builder below. */
|
|
730
|
+
export type ExecRequestEndParams = Omit<ExecRequestEndEvent, 'type'>;
|
|
731
|
+
export declare function buildRequestEndEvent(params: ExecRequestEndParams): ExecRequestEndEvent;
|
|
732
|
+
/**
|
|
733
|
+
* One thread's lifecycle (headless-threads-and-lifecycle-hooks section 2.3.2).
|
|
734
|
+
*
|
|
735
|
+
* `phase` HAS THREE MEMBERS AND NOT FIVE, deliberately: `busy` / `idle` are already
|
|
736
|
+
* carried by `turn_state`, and two state machines describing one fact will eventually
|
|
737
|
+
* disagree. This file's own history has one such lesson (`ui/team-status.ts`), which is
|
|
738
|
+
* why the smaller set is the one that survived.
|
|
739
|
+
*/
|
|
740
|
+
export interface ExecThreadStateEvent extends ExecCorrelation {
|
|
741
|
+
type: 'thread';
|
|
742
|
+
sessionId: string;
|
|
743
|
+
threadId: string;
|
|
744
|
+
phase: 'opened' | 'closing' | 'closed';
|
|
745
|
+
/** Only on `'closed'`. */
|
|
746
|
+
reason?: ExecThreadEndReason;
|
|
747
|
+
/** Only on `'closed'`: this thread's account. */
|
|
748
|
+
requests?: number;
|
|
749
|
+
turns?: number;
|
|
750
|
+
durationMs?: number;
|
|
751
|
+
usage?: ExecUsage;
|
|
752
|
+
cost?: ExecCost;
|
|
753
|
+
isError?: boolean;
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* One hook round trip, outward (headless-threads-and-lifecycle-hooks section 2.4.2).
|
|
757
|
+
*
|
|
758
|
+
* `requestId` IS REQUIRED, like `decision_request`'s, and for the same reason: a hook
|
|
759
|
+
* request that cannot name the request it belongs to cannot be timed out, cancelled or
|
|
760
|
+
* audited. `prompt_pre` therefore fires after `scope.begin()` and before the `ack`, so
|
|
761
|
+
* the id always exists.
|
|
762
|
+
*/
|
|
763
|
+
export interface ExecHookRequestEvent extends ExecCorrelation {
|
|
764
|
+
type: 'hook_request';
|
|
765
|
+
sessionId: string;
|
|
766
|
+
requestId: string;
|
|
767
|
+
/**
|
|
768
|
+
* THIS ROUND TRIP'S OWN ID: what `hook_answer.respondsTo` must name.
|
|
769
|
+
*
|
|
770
|
+
* IT IS NOT `requestId`, AND THAT IS A GAP FOUND DURING IMPLEMENTATION. One hook fires many
|
|
771
|
+
* times inside one request, so the semantic id cannot identify a round trip - while
|
|
772
|
+
* `decision_request` does not have the problem, because one decision belongs to one request.
|
|
773
|
+
* Recorded in the spec's implementation-notes section.
|
|
774
|
+
*/
|
|
775
|
+
hookRequestId: string;
|
|
776
|
+
hookId: string;
|
|
777
|
+
point: HookPoint;
|
|
778
|
+
/** The point-specific payload; see the tool/prompt/turn modules below. */
|
|
779
|
+
payload: Record<string, unknown>;
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* The other end of exactly one `hook_request` (I-H5).
|
|
783
|
+
*
|
|
784
|
+
* `outcome` is the operational fact and `code` the stable reason; keeping BOTH is what
|
|
785
|
+
* makes "the hook timed out" and "the hook answered nonsense" distinguishable after the
|
|
786
|
+
* fact, which a single boolean never is.
|
|
787
|
+
*/
|
|
788
|
+
export interface ExecHookResolvedEvent extends ExecCorrelation {
|
|
789
|
+
type: 'hook_resolved';
|
|
790
|
+
sessionId: string;
|
|
791
|
+
requestId: string;
|
|
792
|
+
/** The same `hookRequestId` its request carried, so the pair is unambiguous. */
|
|
793
|
+
hookRequestId?: string;
|
|
794
|
+
hookId: string;
|
|
795
|
+
point: HookPoint;
|
|
796
|
+
outcome: 'answered' | 'timeout' | 'aborted' | 'invalid' | 'saturated' | 'unregistered';
|
|
797
|
+
action?: string;
|
|
798
|
+
/** Present when `outcome !== 'answered'`, and for a rejected answer. */
|
|
799
|
+
code?: string;
|
|
800
|
+
}
|
|
801
|
+
export type ExecEvent = ExecSystemInitEvent | ExecUserEvent | ExecTurnStateEvent | ExecProgressEvent | ExecTextDeltaEvent | ExecThinkingEvent | ExecAssistantEvent | ExecToolCallEvent | ExecToolResultEvent | ExecTodoEvent | ExecTeamEvent | ExecFastReviewEvent | ExecFastTierEvent | ExecCompactionEvent | ExecRetryEvent | ExecJobEvent | ExecAckEvent | ExecDecisionRequestEvent | ExecDecisionResolvedEvent | ExecToolInvokeEvent | ExecToolCancelEvent | ExecPongEvent | ExecSteeringEvent | ExecThreadStateEvent | ExecHookRequestEvent | ExecHookResolvedEvent | ExecErrorEvent | ExecRequestEndEvent | ExecResultEvent;
|
|
425
802
|
/** Everything `system/init` needs that the runner cannot derive for itself. */
|
|
426
803
|
export interface ExecInitParams {
|
|
427
804
|
sessionId: string;
|
|
@@ -436,11 +813,54 @@ export interface ExecInitParams {
|
|
|
436
813
|
permissionMode: ExecPermissionMode;
|
|
437
814
|
tools: string[];
|
|
438
815
|
resumed: boolean;
|
|
816
|
+
/**
|
|
817
|
+
* Thread mode only: the pool this run opened with, so a wrapper can decide whether
|
|
818
|
+
* opening a fifth thread is worth the round trip (section 2.3.2 / AC-8).
|
|
819
|
+
*
|
|
820
|
+
* ABSENT ON EVERY NON-THREAD RUN (I-C1). `default` is stated rather than implied: a caller
|
|
821
|
+
* that never sends `threadId` needs to know which conversation it is already talking to.
|
|
822
|
+
*/
|
|
823
|
+
threads?: {
|
|
824
|
+
maxOpen: number;
|
|
825
|
+
concurrency: number;
|
|
826
|
+
default: string;
|
|
827
|
+
};
|
|
828
|
+
/**
|
|
829
|
+
* `true` when this run accepts `hook_register` / `hook_answer`, i.e. in bridge mode.
|
|
830
|
+
*
|
|
831
|
+
* A CAPABILITY, NOT A COUNT: zero hooks registered is still a hook-capable run, and a
|
|
832
|
+
* caller that cannot tell the two apart would conclude "hooks are unsupported" from
|
|
833
|
+
* "I have not registered one".
|
|
834
|
+
*
|
|
835
|
+
* IT IS NOT GATED ON `--threads`, so a bridge run's init line gains this key and the
|
|
836
|
+
* `hooks` capability string. That is an ADDITIVE change to a v1 bridge line - a caller
|
|
837
|
+
* that compares capability SETS is unaffected, one that compares literals is not - and it
|
|
838
|
+
* is the price of "declare it where it is true" (section 4.6). `threads` is the key that
|
|
839
|
+
* stays absent, which is what keeps a non-thread run byte-identical.
|
|
840
|
+
*/
|
|
841
|
+
hooks?: boolean;
|
|
439
842
|
/**
|
|
440
843
|
* Absolute path of the session file this run reads and writes; `null` under
|
|
441
844
|
* `--no-save-session`. See `ExecSystemInitEvent.sessionFile`.
|
|
442
845
|
*/
|
|
443
846
|
sessionFile?: string | null;
|
|
847
|
+
/**
|
|
848
|
+
* `true` under `--input-format bridge`. Stamped by `runExec`, never by a caller,
|
|
849
|
+
* and it is what adds `protocolVersion` plus the bridge capability strings -
|
|
850
|
+
* declaring `bridge` in a `stream-json` run would tell a wrapper to send frames
|
|
851
|
+
* nothing is reading.
|
|
852
|
+
*/
|
|
853
|
+
bridge?: boolean;
|
|
854
|
+
/**
|
|
855
|
+
* `true` when this run carries `--result-schema`, so `submit_result` is
|
|
856
|
+
* registered and the `<structured_result>` block is in the system prompt.
|
|
857
|
+
*
|
|
858
|
+
* A SEPARATE FLAG FROM THE OPTIONS, not derived from them here: this module is
|
|
859
|
+
* pure types and builders and must not learn what a flag is. `runExec` stamps it,
|
|
860
|
+
* which also keeps the capability list a fact about the run rather than about the
|
|
861
|
+
* argv it was started with.
|
|
862
|
+
*/
|
|
863
|
+
structuredResult?: boolean;
|
|
444
864
|
}
|
|
445
865
|
/**
|
|
446
866
|
* What this build announces in `system/init`.
|
|
@@ -470,6 +890,41 @@ export interface ExecInitParams {
|
|
|
470
890
|
* is still resumable instead of guessing the path.
|
|
471
891
|
*/
|
|
472
892
|
export declare const EXEC_CAPABILITIES: readonly string[];
|
|
893
|
+
/**
|
|
894
|
+
* What a run must be able to observe about ITSELF to answer "which capability
|
|
895
|
+
* strings do I announce".
|
|
896
|
+
*
|
|
897
|
+
* AN OBJECT, NOT A BOOLEAN (review P0-4). Until this round the list was assembled
|
|
898
|
+
* from one flag (`bridge`), and `structured_result` cannot join it: that capability
|
|
899
|
+
* is TRUE ONLY FOR A RUN THAT ACTUALLY CARRIES `--result-schema`. Appending it to
|
|
900
|
+
* the `EXEC_CAPABILITIES` constant would make a run without the flag announce a
|
|
901
|
+
* capability it does not have, which is precisely the failure the field exists to
|
|
902
|
+
* prevent - a caller would send a schema-shaped expectation and get prose back.
|
|
903
|
+
*
|
|
904
|
+
* Named for what it describes: the capabilities of THIS RUN, not of this build.
|
|
905
|
+
*/
|
|
906
|
+
export interface ExecCapabilities {
|
|
907
|
+
bridge: boolean;
|
|
908
|
+
/** True ONLY when this run carries `--result-schema`. */
|
|
909
|
+
structuredResult: boolean;
|
|
910
|
+
/** True only for a run that opened `--threads` (review P1-5). */
|
|
911
|
+
threads?: boolean;
|
|
912
|
+
/** True only for a bridge run, which is the only run that accepts hook frames. */
|
|
913
|
+
hooks?: boolean;
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* The capability strings one run announces: `EXEC_CAPABILITIES` plus whatever its
|
|
917
|
+
* own switches add, APPEND ONLY.
|
|
918
|
+
*
|
|
919
|
+
* A SEPARATE LIST RATHER THAN A LONGER `EXEC_CAPABILITIES`, because declaring
|
|
920
|
+
* `bridge` in a `stream-json` run would tell a wrapper to send frames nothing is
|
|
921
|
+
* reading - and it would send them into a stdin parser that ignores them, so the
|
|
922
|
+
* caller's first real symptom would be "no decision_request ever arrives".
|
|
923
|
+
*
|
|
924
|
+
* `'structured_result'` IS PUSHED HERE AND NOWHERE ELSE. There is exactly one
|
|
925
|
+
* derivation point, so `system/init` and `aragon info` cannot disagree.
|
|
926
|
+
*/
|
|
927
|
+
export declare function execCapabilities(caps: ExecCapabilities): string[];
|
|
473
928
|
export declare function buildInitEvent(params: ExecInitParams): ExecSystemInitEvent;
|
|
474
929
|
/** Everything `result` needs. `schemaVersion` is stamped here, never by a caller. */
|
|
475
930
|
export type ExecResultParams = Omit<ExecResultEvent, 'type' | 'schemaVersion'>;
|
package/dist/exec/events.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// BY VALUE, AND THE DIRECTION IS ONE-WAY: `bridge/protocol.ts` never imports this
|
|
2
|
+
// module, so a wrapper reading `system/init` and a caller writing frames share one
|
|
3
|
+
// copy of the two bridge constants instead of two that can disagree.
|
|
4
|
+
import { BRIDGE_CAPABILITIES, BRIDGE_PROTOCOL_VERSION } from './bridge/protocol.js';
|
|
1
5
|
/**
|
|
2
6
|
* `EXEC_SCHEMA_VERSION` appears on `system` and on `result` and NOWHERE ELSE.
|
|
3
7
|
* ADDING A FIELD OR AN EVENT TYPE NEVER BUMPS IT; renaming or removing one does.
|
|
@@ -7,6 +11,9 @@
|
|
|
7
11
|
* additive change and every wrapper would pin an exact number forever.
|
|
8
12
|
*/
|
|
9
13
|
export const EXEC_SCHEMA_VERSION = 1;
|
|
14
|
+
export function buildRequestEndEvent(params) {
|
|
15
|
+
return { type: 'request_end', ...params };
|
|
16
|
+
}
|
|
10
17
|
/**
|
|
11
18
|
* What this build announces in `system/init`.
|
|
12
19
|
*
|
|
@@ -49,7 +56,40 @@ export const EXEC_CAPABILITIES = [
|
|
|
49
56
|
// asking for this string, and an older wrapper never asks.
|
|
50
57
|
'todo-acceptance',
|
|
51
58
|
];
|
|
59
|
+
/**
|
|
60
|
+
* The capability strings one run announces: `EXEC_CAPABILITIES` plus whatever its
|
|
61
|
+
* own switches add, APPEND ONLY.
|
|
62
|
+
*
|
|
63
|
+
* A SEPARATE LIST RATHER THAN A LONGER `EXEC_CAPABILITIES`, because declaring
|
|
64
|
+
* `bridge` in a `stream-json` run would tell a wrapper to send frames nothing is
|
|
65
|
+
* reading - and it would send them into a stdin parser that ignores them, so the
|
|
66
|
+
* caller's first real symptom would be "no decision_request ever arrives".
|
|
67
|
+
*
|
|
68
|
+
* `'structured_result'` IS PUSHED HERE AND NOWHERE ELSE. There is exactly one
|
|
69
|
+
* derivation point, so `system/init` and `aragon info` cannot disagree.
|
|
70
|
+
*/
|
|
71
|
+
export function execCapabilities(caps) {
|
|
72
|
+
const list = [...EXEC_CAPABILITIES];
|
|
73
|
+
if (caps.structuredResult)
|
|
74
|
+
list.push('structured_result');
|
|
75
|
+
// THE TWO NEW STRINGS FOLLOW THE SAME RULE as `structured_result` (review P1-5):
|
|
76
|
+
// conditions on THIS RUN, pushed from the ONE derivation point. `hooks` keys off
|
|
77
|
+
// `bridge` rather than off `threads` because hooks need only the frame channel - tying
|
|
78
|
+
// them to threads would force the commonest embedding shape ("one conversation, one
|
|
79
|
+
// redactor") to sign a multi-tenant contract it does not use (section 9.9).
|
|
80
|
+
if (caps.threads)
|
|
81
|
+
list.push('threads');
|
|
82
|
+
if (caps.hooks)
|
|
83
|
+
list.push('hooks');
|
|
84
|
+
if (caps.bridge)
|
|
85
|
+
list.push(...BRIDGE_CAPABILITIES);
|
|
86
|
+
return list;
|
|
87
|
+
}
|
|
52
88
|
export function buildInitEvent(params) {
|
|
89
|
+
const bridge = params.bridge === true;
|
|
90
|
+
const structuredResult = params.structuredResult === true;
|
|
91
|
+
const threads = params.threads !== undefined;
|
|
92
|
+
const hooks = params.hooks === true;
|
|
53
93
|
return {
|
|
54
94
|
type: 'system',
|
|
55
95
|
subtype: 'init',
|
|
@@ -62,7 +102,13 @@ export function buildInitEvent(params) {
|
|
|
62
102
|
permissionMode: params.permissionMode,
|
|
63
103
|
tools: params.tools,
|
|
64
104
|
resumed: params.resumed,
|
|
65
|
-
capabilities:
|
|
105
|
+
capabilities: execCapabilities({ bridge, structuredResult, threads, hooks }),
|
|
106
|
+
...(bridge ? { protocolVersion: BRIDGE_PROTOCOL_VERSION } : {}),
|
|
107
|
+
// CONDITIONAL SPREADS, so a non-thread run's init line keeps the exact key set it
|
|
108
|
+
// had before this round (I-C1). The counters live in the type, the absence is the
|
|
109
|
+
// proof.
|
|
110
|
+
...(params.threads !== undefined ? { threads: params.threads } : {}),
|
|
111
|
+
...(hooks ? { hooks: true } : {}),
|
|
66
112
|
// `?? null` rather than a conditional spread: the field's contract is
|
|
67
113
|
// three-valued for a CONSUMER (`undefined` = an old build that cannot say,
|
|
68
114
|
// `null` = this run saves nothing, a string = here it is), and omitting it
|