@aragon-agent/cli 0.6.2 → 0.6.3
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 +216 -0
- package/README.md +202 -0
- package/dist/agent/controller.d.ts +74 -1
- package/dist/agent/controller.js +200 -1
- package/dist/agent/headless.d.ts +8 -0
- package/dist/agent/headless.js +47 -0
- package/dist/agent/reducer.d.ts +120 -0
- package/dist/agent/reducer.js +201 -2
- package/dist/cli.js +224 -5
- package/dist/commands/builtins.js +73 -23
- package/dist/commands/registry.d.ts +27 -0
- package/dist/compaction/archive.d.ts +175 -0
- package/dist/compaction/archive.js +267 -0
- package/dist/compaction/child.d.ts +97 -0
- package/dist/compaction/child.js +113 -0
- package/dist/compaction/command.d.ts +41 -0
- package/dist/compaction/command.js +342 -0
- package/dist/compaction/compactor.d.ts +268 -0
- package/dist/compaction/compactor.js +689 -0
- package/dist/compaction/digest.d.ts +62 -0
- package/dist/compaction/digest.js +164 -0
- package/dist/compaction/limits.d.ts +165 -0
- package/dist/compaction/limits.js +172 -0
- package/dist/compaction/pressure.d.ts +135 -0
- package/dist/compaction/pressure.js +167 -0
- package/dist/compaction/summarize-call.d.ts +70 -0
- package/dist/compaction/summarize-call.js +86 -0
- package/dist/compaction/summary-prompt.d.ts +85 -0
- package/dist/compaction/summary-prompt.js +197 -0
- package/dist/compaction/tail-budget.d.ts +52 -0
- package/dist/compaction/tail-budget.js +67 -0
- package/dist/compaction/types.d.ts +179 -0
- package/dist/compaction/types.js +22 -0
- package/dist/compaction/wiring.d.ts +255 -0
- package/dist/compaction/wiring.js +556 -0
- package/dist/config/app-paths.d.ts +14 -0
- package/dist/config/app-paths.js +16 -0
- package/dist/config/cli-commands.d.ts +41 -0
- package/dist/config/cli-commands.js +77 -1
- package/dist/config/env.d.ts +4 -1
- package/dist/config/env.js +92 -2
- package/dist/config/load.d.ts +31 -0
- package/dist/config/load.js +63 -1
- package/dist/config/schema.d.ts +159 -0
- package/dist/config/schema.js +137 -0
- package/dist/config/store.js +17 -1
- package/dist/config/ui-state.d.ts +28 -1
- package/dist/config/ui-state.js +9 -0
- package/dist/exec/events.d.ts +50 -1
- package/dist/exec/runner.js +42 -0
- package/dist/input/mouse-events.d.ts +31 -5
- package/dist/input/mouse-events.js +86 -27
- package/dist/input/stdin-mouse-filter.d.ts +8 -3
- package/dist/logging/install.d.ts +51 -1
- package/dist/logging/install.js +28 -0
- package/dist/logging/logger.d.ts +1 -1
- package/dist/session/persist.js +19 -0
- package/dist/team/report.js +7 -1
- package/dist/team/runtime.d.ts +10 -0
- package/dist/team/runtime.js +7 -0
- package/dist/team/subagent.d.ts +31 -1
- package/dist/team/subagent.js +42 -15
- package/dist/team/types.d.ts +8 -0
- package/dist/ui/ActivityLine.d.ts +19 -1
- package/dist/ui/ActivityLine.js +10 -2
- package/dist/ui/App.d.ts +48 -1
- package/dist/ui/App.js +294 -13
- package/dist/ui/Composer.d.ts +13 -1
- package/dist/ui/Composer.js +2 -2
- package/dist/ui/PromptInput.d.ts +34 -1
- package/dist/ui/PromptInput.js +54 -3
- package/dist/ui/StatusBar.d.ts +40 -0
- package/dist/ui/StatusBar.js +13 -4
- package/dist/ui/TeamPanel.js +7 -1
- package/dist/ui/Transcript.d.ts +21 -1
- package/dist/ui/Transcript.js +32 -2
- package/dist/ui/clipboard.d.ts +52 -0
- package/dist/ui/clipboard.js +91 -0
- package/dist/ui/entries/CompactionCard.d.ts +75 -0
- package/dist/ui/entries/CompactionCard.js +158 -0
- package/dist/ui/frame-differ.d.ts +46 -0
- package/dist/ui/frame-differ.js +94 -10
- package/dist/ui/gauge.d.ts +16 -1
- package/dist/ui/gauge.js +25 -6
- package/dist/ui/glyphs.d.ts +11 -0
- package/dist/ui/glyphs.js +2 -0
- package/dist/ui/layout/ScrollViewport.d.ts +43 -6
- package/dist/ui/layout/ScrollViewport.js +128 -21
- package/dist/ui/layout/budget.d.ts +10 -3
- package/dist/ui/layout/budget.js +10 -3
- package/dist/ui/layout/follow-state.d.ts +137 -0
- package/dist/ui/layout/follow-state.js +150 -0
- package/dist/ui/layout/virtual-window.d.ts +11 -0
- package/dist/ui/layout/virtual-window.js +34 -0
- package/dist/ui/overlays/HelpOverlay.js +6 -1
- package/dist/ui/overlays/SettingsScreen.d.ts +47 -1
- package/dist/ui/overlays/SettingsScreen.js +60 -1
- package/dist/ui/palettes.d.ts +16 -0
- package/dist/ui/palettes.js +6 -0
- package/dist/ui/screen.d.ts +22 -0
- package/dist/ui/screen.js +70 -8
- package/dist/ui/selection/highlight.d.ts +39 -0
- package/dist/ui/selection/highlight.js +64 -0
- package/dist/ui/selection/screen-mirror.d.ts +24 -0
- package/dist/ui/selection/screen-mirror.js +35 -0
- package/dist/ui/selection/selection-controller.d.ts +88 -0
- package/dist/ui/selection/selection-controller.js +282 -0
- package/dist/ui/selection/selection.d.ts +90 -0
- package/dist/ui/selection/selection.js +223 -0
- package/dist/ui/stdout-frame-writer.d.ts +33 -0
- package/dist/ui/stdout-frame-writer.js +15 -0
- package/dist/ui/theme.d.ts +11 -0
- package/dist/ui/theme.js +2 -0
- package/dist/ui/transcript-text.js +23 -0
- package/dist/ui/use-startup-notices.d.ts +27 -1
- package/dist/ui/use-startup-notices.js +43 -11
- package/dist/ui/use-wheel-routing.js +8 -0
- package/package.json +4 -2
|
@@ -129,7 +129,17 @@ export interface AgentEventSource {
|
|
|
129
129
|
thinkingLevel: string;
|
|
130
130
|
};
|
|
131
131
|
}
|
|
132
|
-
/**
|
|
132
|
+
/**
|
|
133
|
+
* The ELEVEN members of the core `AgentEvent` union, as this module reads them.
|
|
134
|
+
*
|
|
135
|
+
* THIS UNION MUST STAY A SUPERSET OF CORE'S, and the failure when it does not is
|
|
136
|
+
* a COMPILE error rather than a silent one (implementation finding IF-3): the
|
|
137
|
+
* `AgentEventSource` interface above takes a listener of THIS type, so a core
|
|
138
|
+
* union member missing here makes `AgentController` structurally incompatible
|
|
139
|
+
* with `attachAgentEvents` and every call site fails to type-check. That is the
|
|
140
|
+
* right failure mode, but it means adding an event to core is also a change
|
|
141
|
+
* here — the two `compaction_*` members below arrived that way.
|
|
142
|
+
*/
|
|
133
143
|
export type AgentLogEvent = {
|
|
134
144
|
type: 'agent_start';
|
|
135
145
|
} | {
|
|
@@ -183,6 +193,46 @@ export type AgentLogEvent = {
|
|
|
183
193
|
duration?: number;
|
|
184
194
|
error?: string;
|
|
185
195
|
output?: string;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Context compaction (context-auto-compaction §5.1).
|
|
199
|
+
*
|
|
200
|
+
* RECORDED UNDER THE `agent` SCOPE HERE, not `compaction`. This module logs the
|
|
201
|
+
* LEAD'S LIFECYCLE, and "the run paused to compact" is part of that story — a
|
|
202
|
+
* support reader following a run's `agent` records must not find an unexplained
|
|
203
|
+
* gap where a 40-second summarization was. The `compaction` scope carries the
|
|
204
|
+
* subsystem's own detail (`compaction/compactor.ts`), which is a different
|
|
205
|
+
* question.
|
|
206
|
+
*
|
|
207
|
+
* NO `summary` FIELD IS READ, ever: it can be tens of thousands of characters,
|
|
208
|
+
* and this file's `message_update` case already records why a log larger than
|
|
209
|
+
* the conversation is not a log.
|
|
210
|
+
*/
|
|
211
|
+
| {
|
|
212
|
+
type: 'compaction_start';
|
|
213
|
+
trigger?: string;
|
|
214
|
+
messageCount?: number;
|
|
215
|
+
} | {
|
|
216
|
+
type: 'compaction_end';
|
|
217
|
+
applied?: boolean;
|
|
218
|
+
mode?: string;
|
|
219
|
+
reason?: string;
|
|
220
|
+
messagesBefore?: number;
|
|
221
|
+
messagesAfter?: number;
|
|
222
|
+
droppedMessages?: number;
|
|
223
|
+
/**
|
|
224
|
+
* The tail-relief report (context-auto-compaction-hardening W2).
|
|
225
|
+
*
|
|
226
|
+
* THIS UNION MUST STAY A SUPERSET OF CORE'S (IF-3). A field added to
|
|
227
|
+
* `CompactionEndEvent` and not mirrored here does not fail the build — the
|
|
228
|
+
* structural type simply ignores it — so the only trace of a degradation
|
|
229
|
+
* the user is entitled to know about would be missing from the log.
|
|
230
|
+
*/
|
|
231
|
+
tailRelief?: {
|
|
232
|
+
messages: number;
|
|
233
|
+
charsRemoved: number;
|
|
234
|
+
};
|
|
235
|
+
durationMs?: number;
|
|
186
236
|
};
|
|
187
237
|
/**
|
|
188
238
|
* Subscribe to the agent's events and record them.
|
package/dist/logging/install.js
CHANGED
|
@@ -273,6 +273,34 @@ export function attachAgentEvents(logger, controller) {
|
|
|
273
273
|
ms: turnStartedAt > 0 ? Date.now() - turnStartedAt : 0,
|
|
274
274
|
});
|
|
275
275
|
break;
|
|
276
|
+
// Context compaction (context-auto-compaction §5.1). At `info`, like
|
|
277
|
+
// `turn_end`, so a run's `agent` records have no unexplained 40-second gap
|
|
278
|
+
// where a summarization was.
|
|
279
|
+
case 'compaction_start':
|
|
280
|
+
logger.info('agent', 'compaction_start', {
|
|
281
|
+
trigger: event.trigger,
|
|
282
|
+
messages: event.messageCount,
|
|
283
|
+
});
|
|
284
|
+
break;
|
|
285
|
+
case 'compaction_end':
|
|
286
|
+
// `applied: false` is a WARN, not an info: it means the run is still at
|
|
287
|
+
// the occupancy that triggered this and is about to send anyway.
|
|
288
|
+
logger[event.applied ? 'info' : 'warn']('agent', 'compaction_end', {
|
|
289
|
+
applied: event.applied,
|
|
290
|
+
mode: event.mode,
|
|
291
|
+
reason: event.reason,
|
|
292
|
+
before: event.messagesBefore,
|
|
293
|
+
after: event.messagesAfter,
|
|
294
|
+
dropped: event.droppedMessages,
|
|
295
|
+
...(event.tailRelief
|
|
296
|
+
? {
|
|
297
|
+
clipped: event.tailRelief.messages,
|
|
298
|
+
clippedChars: event.tailRelief.charsRemoved,
|
|
299
|
+
}
|
|
300
|
+
: {}),
|
|
301
|
+
ms: event.durationMs,
|
|
302
|
+
});
|
|
303
|
+
break;
|
|
276
304
|
case 'message_update': {
|
|
277
305
|
// API retry (llm-api-retry-backoff §6.8), ABOVE the `trace` line so a
|
|
278
306
|
// three-minute pause is in the log at the DEFAULT level. Redaction is
|
package/dist/logging/logger.d.ts
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
import type { LogConfig } from '../config/schema.js';
|
|
24
24
|
import { type LogLevelName } from './levels.js';
|
|
25
25
|
/** Closed set, not free text: a typo'd scope is a record nobody can filter on. */
|
|
26
|
-
export type LogScope = 'cli' | 'config' | 'agent' | 'tool' | 'skills' | 'llm' | 'migrate' | 'log' | 'fast' | 'history' | 'update';
|
|
26
|
+
export type LogScope = 'cli' | 'config' | 'agent' | 'tool' | 'skills' | 'llm' | 'migrate' | 'log' | 'fast' | 'history' | 'update' | 'compaction';
|
|
27
27
|
export interface LogRecord {
|
|
28
28
|
ts: string;
|
|
29
29
|
lv: LogLevelName;
|
package/dist/session/persist.js
CHANGED
|
@@ -139,6 +139,25 @@ export function normalizeLoadedEntries(entries) {
|
|
|
139
139
|
detail: 'interrupted (session resumed)',
|
|
140
140
|
};
|
|
141
141
|
}
|
|
142
|
+
// THE FIFTH CLAUSE (context-auto-compaction §5.5 / C-8 / AC-19). A compaction
|
|
143
|
+
// card saved while its summarization was open resumes claiming to be running
|
|
144
|
+
// while nothing is, and — the part that actually costs — it never settles, so
|
|
145
|
+
// `Transcript`'s MONOTONIC boundary never advances past it and every frame for
|
|
146
|
+
// the rest of the session re-renders the whole tail.
|
|
147
|
+
//
|
|
148
|
+
// `applied: false` is also simply TRUE: whatever the call was going to
|
|
149
|
+
// return died with the process, and the history on disk is the PRE-compaction
|
|
150
|
+
// one. A card that claimed otherwise would tell the user their session had
|
|
151
|
+
// been compacted when it had not.
|
|
152
|
+
if (entry.kind === 'compaction' && entry.live) {
|
|
153
|
+
return {
|
|
154
|
+
...entry,
|
|
155
|
+
live: false,
|
|
156
|
+
applied: false,
|
|
157
|
+
mode: 'none',
|
|
158
|
+
reason: 'interrupted (session resumed)',
|
|
159
|
+
};
|
|
160
|
+
}
|
|
142
161
|
return entry;
|
|
143
162
|
});
|
|
144
163
|
}
|
package/dist/team/report.js
CHANGED
|
@@ -143,9 +143,15 @@ function buildSection(run) {
|
|
|
143
143
|
// bytes, and it is the only place the lead learns which of its children were
|
|
144
144
|
// cheap — the fact the whole second cost line is about.
|
|
145
145
|
const tier = run.tier === 'fast' ? ' [fast]' : '';
|
|
146
|
+
// `compacted N` in the same position `retried Nx` uses, and for the same
|
|
147
|
+
// reason (context-auto-compaction-hardening §3.4.4 / W3): this is the ONLY
|
|
148
|
+
// place the lead learns that a child's own history was summarized mid-task, so
|
|
149
|
+
// a summary that reads thinner than the turn count suggests has a stated cause
|
|
150
|
+
// rather than an unexplained one. Absent when it never happened.
|
|
151
|
+
const compacted = run.compactions ? `, compacted ${run.compactions}` : '';
|
|
146
152
|
const head = [
|
|
147
153
|
`### ${run.label}${tier} "${run.description}" ${statusBadge(run)} ${elapsed(run)}, ` +
|
|
148
|
-
`${run.turns} turns, ${run.toolCalls} tools${retried}`,
|
|
154
|
+
`${run.turns} turns, ${run.toolCalls} tools${retried}${compacted}`,
|
|
149
155
|
];
|
|
150
156
|
if (run.filesTouched.length > 0)
|
|
151
157
|
head.push(`files: ${run.filesTouched.join(', ')}`);
|
package/dist/team/runtime.d.ts
CHANGED
|
@@ -60,6 +60,16 @@ export interface TeamRuntimeDeps {
|
|
|
60
60
|
* is the pre-feature behaviour.
|
|
61
61
|
*/
|
|
62
62
|
resolveTier?: SubagentDeps['resolveTier'];
|
|
63
|
+
/**
|
|
64
|
+
* Per-child context compaction (context-auto-compaction-hardening §3.4 / W3).
|
|
65
|
+
*
|
|
66
|
+
* Passed straight through to `SubagentDeps`; absent means children get none,
|
|
67
|
+
* which is the pre-hardening behaviour exactly. `AgentController` supplies it
|
|
68
|
+
* from `CompactionWiring.childFactory()`, and the closure returns `undefined`
|
|
69
|
+
* when compaction is unregistered or `compaction.subagents` is false - which is
|
|
70
|
+
* what lets `subagent.ts` spread nothing at all.
|
|
71
|
+
*/
|
|
72
|
+
contextManagerFor?: SubagentDeps['contextManagerFor'];
|
|
63
73
|
}
|
|
64
74
|
export declare class TeamRuntime {
|
|
65
75
|
private readonly deps;
|
package/dist/team/runtime.js
CHANGED
|
@@ -132,6 +132,13 @@ export class TeamRuntime {
|
|
|
132
132
|
...(this.deps.humanQueue ? { humanQueue: this.deps.humanQueue } : {}),
|
|
133
133
|
agentFactory: this.deps.agentFactory ?? defaultSubagentFactory,
|
|
134
134
|
...(this.deps.resolveTier ? { resolveTier: this.deps.resolveTier } : {}),
|
|
135
|
+
// Site two of two, and the omission is silent in the same way the policy
|
|
136
|
+
// one above is: declaring it on `TeamRuntimeDeps` and forgetting this line
|
|
137
|
+
// type-checks and forwards nothing, so every child would quietly run with
|
|
138
|
+
// no compaction while the config key says it has one.
|
|
139
|
+
...(this.deps.contextManagerFor
|
|
140
|
+
? { contextManagerFor: this.deps.contextManagerFor }
|
|
141
|
+
: {}),
|
|
135
142
|
};
|
|
136
143
|
// Hoisted into a factory so a retry can build a fresh child with IDENTICAL
|
|
137
144
|
// wiring (F-4). Replacing the child rather than re-prompting it is the only
|
package/dist/team/subagent.d.ts
CHANGED
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
* them (§3.11 / R-6). What a child does NOT get is spelled out below, and each
|
|
11
11
|
* omission has a reason that is a correctness boundary rather than caution.
|
|
12
12
|
*/
|
|
13
|
-
import { type AgentConfig, type AgentEvent, type AgentTool, type ModelRef, type ProviderRegistry, type SkillRegistry, type ThinkingLevel, type TokenUsage, type ToolPolicyDecision, type ToolPolicyVerdict } from '@aragon-agent/core';
|
|
13
|
+
import { type AgentConfig, type AgentEvent, type AgentTool, type Message, type ModelRef, type ProviderRegistry, type SkillRegistry, type ThinkingLevel, type TokenUsage, type ToolPolicyDecision, type ToolPolicyVerdict } from '@aragon-agent/core';
|
|
14
14
|
import type { AgentMode } from '../agent/agent-mode.js';
|
|
15
|
+
import type { ChildContextManagerProvider } from '../compaction/child.js';
|
|
15
16
|
import type { CliConfig } from '../config/schema.js';
|
|
16
17
|
import type { ToolPermission } from '../exec/permission.js';
|
|
17
18
|
import type { FastTierName } from '../fast/types.js';
|
|
@@ -29,6 +30,24 @@ export interface SubagentAgentLike extends WatchdogPausable {
|
|
|
29
30
|
prompt(text: string): Promise<void>;
|
|
30
31
|
abort(): void;
|
|
31
32
|
subscribe(listener: (event: AgentEvent) => void): () => void;
|
|
33
|
+
/**
|
|
34
|
+
* The child's live history, for its own context manager
|
|
35
|
+
* (context-auto-compaction-hardening §3.4.3 / W3).
|
|
36
|
+
*
|
|
37
|
+
* NARROWED ON PURPOSE. `Agent` satisfies this with its full `AgentState`
|
|
38
|
+
* getter and a stub satisfies it with `{ messages: [] }`. Widening it to
|
|
39
|
+
* `AgentState` would make every test stub fabricate a systemPrompt, a model and
|
|
40
|
+
* a phase to compile, for fields none of them use.
|
|
41
|
+
*
|
|
42
|
+
* A PROPERTY READ, NOT A METHOD CALL, AND THAT IS NOT A STYLE CHOICE: core's
|
|
43
|
+
* `Agent` has NO `getMessages()`. It exposes `get state(): AgentState`;
|
|
44
|
+
* `AgentController.getMessages()` is the CONTROLLER's own one-line forwarder
|
|
45
|
+
* over `this.agent.state.messages`. Adding a method to the engine to serve a
|
|
46
|
+
* host-side convenience would move the core export surface for nothing.
|
|
47
|
+
*/
|
|
48
|
+
readonly state: {
|
|
49
|
+
readonly messages: readonly Message[];
|
|
50
|
+
};
|
|
32
51
|
}
|
|
33
52
|
export type SubagentAgentFactory = (config: AgentConfig) => SubagentAgentLike;
|
|
34
53
|
/** The default factory: a real `Agent` from `@aragon-agent/core`. */
|
|
@@ -91,6 +110,17 @@ export interface SubagentDeps {
|
|
|
91
110
|
ref: ModelRef;
|
|
92
111
|
thinkingLevel: ThinkingLevel;
|
|
93
112
|
};
|
|
113
|
+
/**
|
|
114
|
+
* Build this child its own context manager
|
|
115
|
+
* (context-auto-compaction-hardening §3.4.3 / W3).
|
|
116
|
+
*
|
|
117
|
+
* ABSENT means children get none, and `subagent.ts` then spreads NO
|
|
118
|
+
* `contextManager` key at all - so the child's loop gate tests a genuinely
|
|
119
|
+
* undefined field and a `compaction.subagents: false` session is byte-identical
|
|
120
|
+
* to round 1 (AC-H10). `AgentController` supplies it from
|
|
121
|
+
* `CompactionWiring.childFactory()`, which returns `null` when the key is off.
|
|
122
|
+
*/
|
|
123
|
+
contextManagerFor?: ChildContextManagerProvider;
|
|
94
124
|
}
|
|
95
125
|
export interface SubagentHooks {
|
|
96
126
|
/** Coalesced at the source (§5.2); phase transitions always pass. */
|
package/dist/team/subagent.js
CHANGED
|
@@ -183,22 +183,40 @@ export function createSubagent(spec, deps, hooks) {
|
|
|
183
183
|
thinkingLevel: deps.config.thinkingLevel,
|
|
184
184
|
};
|
|
185
185
|
const model = resolved.ref;
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
186
|
+
// HOISTED TO A LOCAL, AND THAT IS WHAT MAKES W3 POSSIBLE AT ALL. This was built
|
|
187
|
+
// INLINE inside the `agentFactory({...})` argument, so there was nothing for the
|
|
188
|
+
// child manager's `getSystemPrompt` accessor to close over - and the accessor
|
|
189
|
+
// cannot read it back off the agent, because `SubagentAgentLike` is narrowed to
|
|
190
|
+
// `state.messages` on purpose.
|
|
191
|
+
const systemPrompt = buildSystemPrompt({
|
|
192
|
+
cwd: deps.getCwd(),
|
|
193
|
+
tools,
|
|
194
|
+
skillsBlock: deps.alwaysBlock ?? '',
|
|
195
|
+
agentMode: spec.readOnly ? 'plan' : deps.getMode(),
|
|
196
|
+
// A child has no overlay, so the plan block must describe the headless
|
|
197
|
+
// shape: write the plan as the final message rather than calling tools it
|
|
198
|
+
// does not have.
|
|
199
|
+
planInteractive: false,
|
|
200
|
+
subagentBlock: buildSubagentBlock({
|
|
201
|
+
label: spec.label,
|
|
202
|
+
description: spec.description,
|
|
203
|
+
peers: deps.bus.peersOf(spec.label),
|
|
201
204
|
}),
|
|
205
|
+
});
|
|
206
|
+
// LAZY ACCESSORS OVER `agentRef`, WHICH IS ASSIGNED AFTER THIS RETURNS. A
|
|
207
|
+
// non-lazy read here is `undefined` at construction and the child's estimate
|
|
208
|
+
// branch would silently measure an empty history - CR-1 one module over.
|
|
209
|
+
const contextManager = deps.contextManagerFor?.({
|
|
210
|
+
label: spec.label,
|
|
211
|
+
model,
|
|
212
|
+
getMessages: () => agentRef?.state.messages ?? [],
|
|
213
|
+
getSystemPrompt: () => systemPrompt,
|
|
214
|
+
onCompacted: () => {
|
|
215
|
+
run.compactions = (run.compactions ?? 0) + 1;
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
const agent = deps.agentFactory({
|
|
219
|
+
systemPrompt,
|
|
202
220
|
model,
|
|
203
221
|
tools,
|
|
204
222
|
thinkingLevel: resolved.thinkingLevel,
|
|
@@ -217,6 +235,11 @@ export function createSubagent(spec, deps, hooks) {
|
|
|
217
235
|
// explicitly.
|
|
218
236
|
idleTimeout: deps.config.idleTimeoutMs,
|
|
219
237
|
},
|
|
238
|
+
// SPREAD CONDITIONALLY, never `contextManager: deps.contextManagerFor?.(...)`.
|
|
239
|
+
// With the feature off the options bag has no such key, so the loop's gate
|
|
240
|
+
// tests a field that is genuinely `undefined` rather than a property holding
|
|
241
|
+
// one - the identical argument `AgentController` makes for the lead (AC-H10).
|
|
242
|
+
...(contextManager ? { contextManager } : {}),
|
|
220
243
|
});
|
|
221
244
|
agentRef = agent;
|
|
222
245
|
let lastEmit = 0;
|
|
@@ -289,6 +312,10 @@ export function createSubagent(spec, deps, hooks) {
|
|
|
289
312
|
outputTokens: run.usage.outputTokens + event.usage.outputTokens,
|
|
290
313
|
};
|
|
291
314
|
hooks.onUsage(spec.label, event.usage);
|
|
315
|
+
// W1's delta needs the history LENGTH the measurement covered, and the
|
|
316
|
+
// port carries no turn hook. `turn_end` is emitted BEFORE the assistant
|
|
317
|
+
// push, so this is exactly the history the reported `usage` describes.
|
|
318
|
+
contextManager?.onTurnEnd(event.usage, agentRef?.state.messages ?? [], systemPrompt);
|
|
292
319
|
const text = assistantText(event.message);
|
|
293
320
|
// The LAST non-empty assistant text wins, which is exactly the child's
|
|
294
321
|
// final message once the loop stops calling tools. Capturing it here
|
package/dist/team/types.d.ts
CHANGED
|
@@ -78,6 +78,14 @@ export interface SubagentRun {
|
|
|
78
78
|
error?: string;
|
|
79
79
|
/** Hit `team.maxTurnsPerSubagent` and was stopped mid-work. */
|
|
80
80
|
truncated?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Compactions this child performed on its OWN history
|
|
83
|
+
* (context-auto-compaction-hardening §3.4.4 / W3).
|
|
84
|
+
*
|
|
85
|
+
* OPTIONAL, so every existing construction site and fixture compiles
|
|
86
|
+
* unchanged. Absent and `0` mean the same thing to every reader.
|
|
87
|
+
*/
|
|
88
|
+
compactions?: number;
|
|
81
89
|
/**
|
|
82
90
|
* Prose the child is writing right now, sanitized and clamped to
|
|
83
91
|
* `TEAM_LIMITS.activityChars`. Set only between tool calls; cleared on
|
|
@@ -74,7 +74,25 @@ export interface ActivityLineProps {
|
|
|
74
74
|
* not calm, it is wrong.
|
|
75
75
|
*/
|
|
76
76
|
runningTool?: string;
|
|
77
|
+
/**
|
|
78
|
+
* A context compaction is in flight (context-auto-compaction §6.1).
|
|
79
|
+
*
|
|
80
|
+
* HIGHEST PRECEDENCE IN THE LABEL LADDER, above `runningTool` and above the
|
|
81
|
+
* rotating phrase. Compaction happens BEFORE `turn_start`, i.e. at a moment
|
|
82
|
+
* when `status` is `'running'` but no tool is in flight — precisely the window
|
|
83
|
+
* in which the phrase rotation would say something false, and it can last
|
|
84
|
+
* tens of seconds. `runningTool` already established the principle one step
|
|
85
|
+
* back: a UI that says "Pondering" while `npm test` runs is not calm, it is
|
|
86
|
+
* wrong, and one that says it while a 30 k-token summarization runs is the same
|
|
87
|
+
* error.
|
|
88
|
+
*
|
|
89
|
+
* STILL NO SECOND SPINNER. `single-spinner-while-running`'s D-1 stands: this
|
|
90
|
+
* row owns the only animation while it is mounted, and the compaction card
|
|
91
|
+
* follows `FastCard` in taking a `reducedMotion` that `App` has already
|
|
92
|
+
* widened.
|
|
93
|
+
*/
|
|
94
|
+
compacting?: boolean;
|
|
77
95
|
theme: Theme;
|
|
78
96
|
caps: TermCapabilities;
|
|
79
97
|
}
|
|
80
|
-
export declare function ActivityLine({ startedAt, elapsedMs, reducedMotion, runningTool, theme, caps, }: ActivityLineProps): React.ReactElement;
|
|
98
|
+
export declare function ActivityLine({ startedAt, elapsedMs, reducedMotion, runningTool, compacting, theme, caps, }: ActivityLineProps): React.ReactElement;
|
package/dist/ui/ActivityLine.js
CHANGED
|
@@ -21,7 +21,7 @@ import { pickActivityPhrase } from './activity-phrases.js';
|
|
|
21
21
|
export function liveSpinner(reducedMotion, caps) {
|
|
22
22
|
return caps.unicode && !reducedMotion ? _jsx(Spinner, { type: "dots" }) : null;
|
|
23
23
|
}
|
|
24
|
-
export function ActivityLine({ startedAt, elapsedMs, reducedMotion, runningTool, theme, caps, }) {
|
|
24
|
+
export function ActivityLine({ startedAt, elapsedMs, reducedMotion, runningTool, compacting, theme, caps, }) {
|
|
25
25
|
const glyphs = pickGlyphs(caps);
|
|
26
26
|
// PURE AND DERIVED FROM `(startedAt, now)`, so the sequence does not need to
|
|
27
27
|
// keep rotating underneath the tool label: when the tool settles the phrase
|
|
@@ -31,7 +31,15 @@ export function ActivityLine({ startedAt, elapsedMs, reducedMotion, runningTool,
|
|
|
31
31
|
// SECOND registry of tool names, and `toolGlyph` is already the one place that
|
|
32
32
|
// knows them. Everything round 1 legislated for this row survives — one row,
|
|
33
33
|
// no digits, no `esc abort`, no width ladder, ASCII only.
|
|
34
|
-
|
|
34
|
+
// THREE RUNGS, MOST SPECIFIC FIRST. `Compacting context` outranks a tool name
|
|
35
|
+
// because compaction can be the only thing happening, and it outranks the
|
|
36
|
+
// phrase for the reason `runningTool` does. ASCII only, and the ellipsis comes
|
|
37
|
+
// from `pickGlyphs` like every other one on this row.
|
|
38
|
+
const label = compacting
|
|
39
|
+
? `Compacting context${glyphs.ellipsis}`
|
|
40
|
+
: runningTool
|
|
41
|
+
? `Running ${runningTool}`
|
|
42
|
+
: `${phrase}${glyphs.ellipsis}`;
|
|
35
43
|
// The IDENTICAL branch `Transcript.tsx` uses for the assistant role marker, and
|
|
36
44
|
// it must stay identical: braille dots are both an animation AND a Unicode-only
|
|
37
45
|
// glyph, so reduced motion and an ASCII terminal fall back to the same marker.
|
package/dist/ui/App.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { type Overlay, type ToastLevel } from '../agent/reducer.js';
|
|
|
11
11
|
import type { UpdateBridge } from '../update/types.js';
|
|
12
12
|
import { type RenderMode } from './layout/frame.js';
|
|
13
13
|
import type { MouseSource } from '../input/stdin-mouse-filter.js';
|
|
14
|
+
import type { SelectionBridge } from './selection/selection-controller.js';
|
|
14
15
|
import type { HumanInputBridge } from '../tools/human-input.js';
|
|
15
16
|
export interface ConfirmBridge {
|
|
16
17
|
handler: ((req: ConfirmRequest) => Promise<boolean>) | null;
|
|
@@ -72,5 +73,51 @@ export interface AppProps {
|
|
|
72
73
|
* 'off'`, on a non-TTY, in CI, and in every test that does not exercise it.
|
|
73
74
|
*/
|
|
74
75
|
updateBridge?: UpdateBridge;
|
|
76
|
+
/**
|
|
77
|
+
* The terminal-facing services `cli.tsx` owns and this component cannot build
|
|
78
|
+
* (tui-selection-and-scroll-follow §4.4).
|
|
79
|
+
*
|
|
80
|
+
* ONE PROP RATHER THAN FOUR, and every FIELD of it is independently optional,
|
|
81
|
+
* so `--no-mouse`, `--no-diff-render` and inline mode each drop exactly the
|
|
82
|
+
* services they do not have. Absent altogether on a non-TTY and in every test
|
|
83
|
+
* that does not exercise selection, which is what keeps those trees
|
|
84
|
+
* byte-identical.
|
|
85
|
+
*/
|
|
86
|
+
terminal?: TerminalBridge;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Terminal-level services, almost all of which exist only in a full-screen TTY
|
|
90
|
+
* session — `writeForeign` is the exception, because `/copy` is available in
|
|
91
|
+
* inline mode too and OSC 52 is what makes it work over SSH (§4.4.5).
|
|
92
|
+
*
|
|
93
|
+
* `selection` is a LATE-BOUND BRIDGE for the reason `updateBridge` above is one:
|
|
94
|
+
* the controller is constructed before `render()` and must not hold React state,
|
|
95
|
+
* while the toast sink and the redraw nonce only exist once this component has
|
|
96
|
+
* mounted. The effect below assigns both and clears them on unmount.
|
|
97
|
+
*/
|
|
98
|
+
export interface TerminalBridge {
|
|
99
|
+
/** Whether drag-select is on for this session (`mouseSelect` + a filter). */
|
|
100
|
+
mouseSelect: boolean;
|
|
101
|
+
/** Release the mouse to the terminal, or take it back (`/mouse`, G3). */
|
|
102
|
+
setMouseCapture?: (on: boolean) => void;
|
|
103
|
+
/** Whether the mouse is captured right now — read at command time. */
|
|
104
|
+
isMouseCaptured?: () => boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Write bytes that are ours but are not a frame (OSC 52).
|
|
107
|
+
*
|
|
108
|
+
* IT MUST NOT BE `stdout.write` WITH THE DIFFER IN FRONT OF STDOUT (P1-6):
|
|
109
|
+
* that goes through the frame-writer proxy, which does not recognise an OSC 52
|
|
110
|
+
* chunk, counts it as a foreign write and prints `FRAME_FALLBACK_NOTICE` at
|
|
111
|
+
* the user the first time they copy anything. Without a differ — inline mode,
|
|
112
|
+
* `--no-diff-render` — stdout IS the real stream and `cli.tsx` supplies a
|
|
113
|
+
* direct write instead.
|
|
114
|
+
*
|
|
115
|
+
* Absent only when there is no terminal to accept it at all. It is never a
|
|
116
|
+
* function that writes nowhere: `copyText` reports `'osc52'` whenever its door
|
|
117
|
+
* took the text, so a no-op door would make the toast name a mechanism that
|
|
118
|
+
* was never attempted.
|
|
119
|
+
*/
|
|
120
|
+
writeForeign?: (text: string) => void;
|
|
121
|
+
selection?: SelectionBridge;
|
|
75
122
|
}
|
|
76
|
-
export declare function App({ controller, version, mode, initialOverlay, initialPrompt, confirmBridge, humanInputBridge, mouseSource, vtInputWarning, updateBridge, }: AppProps): React.ReactElement;
|
|
123
|
+
export declare function App({ controller, version, mode, initialOverlay, initialPrompt, confirmBridge, humanInputBridge, mouseSource, vtInputWarning, updateBridge, terminal, }: AppProps): React.ReactElement;
|