sidekick-shared 0.29.1 → 0.30.0
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 +26 -0
- package/README.md +171 -60
- package/dist/aggregation/EventAggregator.d.ts +79 -4
- package/dist/aggregation/EventAggregator.js +391 -37
- package/dist/aggregation/types.d.ts +27 -3
- package/dist/browser.d.ts +7 -3
- package/dist/browser.js +23 -3
- package/dist/context/sessionContext.js +5 -1
- package/dist/esm/aggregation/EventAggregator.js +392 -38
- package/dist/esm/browser.js +3 -1
- package/dist/esm/context/sessionContext.js +6 -2
- package/dist/esm/fileContinuity.js +149 -0
- package/dist/esm/index.js +5 -2
- package/dist/esm/node.js +2 -0
- package/dist/esm/parsers/codexParser.js +192 -3
- package/dist/esm/parsers/subagentEndState.js +61 -0
- package/dist/esm/parsers/subagentScanner.js +4 -0
- package/dist/esm/parsers/subagentTraceParser.js +6 -0
- package/dist/esm/promptHistory.js +22 -11
- package/dist/esm/providers/claudeCode.js +50 -27
- package/dist/esm/providers/codex.js +153 -163
- package/dist/esm/providers/codexCompressedReader.js +184 -0
- package/dist/esm/providers/codexDatabase.js +47 -2
- package/dist/esm/providers/codexRollouts.js +20 -2
- package/dist/esm/providers/codexSubagents.js +456 -0
- package/dist/esm/providers/compressedRollouts.js +330 -0
- package/dist/esm/providers/factory.js +4 -1
- package/dist/esm/providers/rolloutWalker.js +32 -4
- package/dist/esm/schemas/sessionEvent.js +32 -4
- package/dist/esm/sessionMonitor.js +7 -1
- package/dist/esm/sessionPreviews.js +9 -0
- package/dist/esm/sessionPromptHistory.js +5 -3
- package/dist/esm/subagentActivity.js +125 -0
- package/dist/esm/subagentDisplayState.js +81 -0
- package/dist/esm/subagentSpawn.js +206 -6
- package/dist/esm/testing/codexSubagentFixtures.js +336 -0
- package/dist/esm/toolCallOrigins.js +17 -10
- package/dist/esm/toolCategory.js +6 -2
- package/dist/esm/transcript.js +6 -2
- package/dist/esm/turns/assistantTurn.js +6 -3
- package/dist/esm/usage/usageEvents.js +4 -1
- package/dist/esm/watchers/eventBridge.js +31 -1
- package/dist/esm/watchers/jsonlTail.js +11 -42
- package/dist/esm/watchers/jsonlWatcher.js +19 -9
- package/dist/esm/watchers/providerReaderWatcher.js +5 -4
- package/dist/fileContinuity.d.ts +95 -0
- package/dist/fileContinuity.js +187 -0
- package/dist/index.d.ts +14 -7
- package/dist/index.js +39 -10
- package/dist/node.d.ts +4 -0
- package/dist/node.js +16 -2
- package/dist/parsers/codexParser.d.ts +44 -0
- package/dist/parsers/codexParser.js +192 -3
- package/dist/parsers/subagentEndState.d.ts +20 -0
- package/dist/parsers/subagentEndState.js +65 -0
- package/dist/parsers/subagentScanner.js +4 -0
- package/dist/parsers/subagentTraceParser.d.ts +8 -0
- package/dist/parsers/subagentTraceParser.js +6 -0
- package/dist/promptHistory.d.ts +13 -3
- package/dist/promptHistory.js +22 -11
- package/dist/providers/claudeCode.js +50 -27
- package/dist/providers/codex.d.ts +35 -5
- package/dist/providers/codex.js +150 -160
- package/dist/providers/codexCompressedReader.d.ts +58 -0
- package/dist/providers/codexCompressedReader.js +221 -0
- package/dist/providers/codexDatabase.d.ts +25 -0
- package/dist/providers/codexDatabase.js +48 -2
- package/dist/providers/codexRollouts.d.ts +16 -2
- package/dist/providers/codexRollouts.js +20 -2
- package/dist/providers/codexSubagents.d.ts +66 -0
- package/dist/providers/codexSubagents.js +495 -0
- package/dist/providers/compressedRollouts.d.ts +114 -0
- package/dist/providers/compressedRollouts.js +380 -0
- package/dist/providers/factory.d.ts +5 -0
- package/dist/providers/factory.js +4 -1
- package/dist/providers/rolloutWalker.d.ts +15 -2
- package/dist/providers/rolloutWalker.js +33 -4
- package/dist/providers/types.d.ts +22 -1
- package/dist/schemas/sessionEvent.d.ts +26 -2
- package/dist/schemas/sessionEvent.js +31 -3
- package/dist/sessionMonitor.d.ts +6 -0
- package/dist/sessionMonitor.js +7 -1
- package/dist/sessionPreviews.js +9 -0
- package/dist/sessionPromptHistory.d.ts +5 -0
- package/dist/sessionPromptHistory.js +5 -36
- package/dist/subagentActivity.d.ts +91 -0
- package/dist/subagentActivity.js +132 -0
- package/dist/subagentDisplayState.d.ts +81 -0
- package/dist/subagentDisplayState.js +86 -0
- package/dist/subagentSpawn.d.ts +100 -5
- package/dist/subagentSpawn.js +218 -7
- package/dist/testing/codexSubagentFixtures.d.ts +48 -0
- package/dist/testing/codexSubagentFixtures.js +344 -0
- package/dist/toolCallOrigins.d.ts +5 -0
- package/dist/toolCallOrigins.js +17 -10
- package/dist/toolCategory.js +6 -2
- package/dist/transcript.js +5 -1
- package/dist/turns/assistantTurn.d.ts +4 -1
- package/dist/turns/assistantTurn.js +5 -2
- package/dist/types/codex.d.ts +71 -2
- package/dist/types/sessionEvent.d.ts +38 -3
- package/dist/usage/usageEvents.d.ts +4 -1
- package/dist/usage/usageEvents.js +4 -1
- package/dist/watchers/eventBridge.js +30 -0
- package/dist/watchers/jsonlTail.js +11 -42
- package/dist/watchers/jsonlWatcher.d.ts +5 -1
- package/dist/watchers/jsonlWatcher.js +18 -8
- package/dist/watchers/providerReaderWatcher.js +5 -4
- package/package.json +1 -1
|
@@ -22,6 +22,7 @@ const HeatmapTracker_1 = require("./HeatmapTracker");
|
|
|
22
22
|
const PatternExtractor_1 = require("./PatternExtractor");
|
|
23
23
|
const errorTaxonomy_1 = require("../extractors/errorTaxonomy");
|
|
24
24
|
const tokenEstimation_1 = require("../tokenEstimation");
|
|
25
|
+
const subagentActivity_1 = require("../subagentActivity");
|
|
25
26
|
const subagentSpawn_1 = require("../subagentSpawn");
|
|
26
27
|
const usageNormalization_1 = require("../usageNormalization");
|
|
27
28
|
// ── Defaults ──
|
|
@@ -31,8 +32,26 @@ const DEFAULT_CONTEXT_TIMELINE_CAP = 2_000;
|
|
|
31
32
|
const DEFAULT_BURN_WINDOW_MS = 5 * 60_000;
|
|
32
33
|
const DEFAULT_BURN_SAMPLE_MS = 10_000;
|
|
33
34
|
const COMPACTION_DROP_THRESHOLD = 0.8; // >20% drop
|
|
34
|
-
/**
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Schema version for serialized snapshots. v6: token totals count each Claude
|
|
37
|
+
* response and Codex call once. v7: subagent lifecycles track Codex spawns and
|
|
38
|
+
* record what settled a background agent (`settledBy`).
|
|
39
|
+
*/
|
|
40
|
+
exports.SNAPSHOT_SCHEMA_VERSION = 7;
|
|
41
|
+
// Checked without the schema module so this file stays free of Zod.
|
|
42
|
+
const PERMISSION_MODES = new Set([
|
|
43
|
+
'default',
|
|
44
|
+
'acceptEdits',
|
|
45
|
+
'bypassPermissions',
|
|
46
|
+
'plan',
|
|
47
|
+
'auto',
|
|
48
|
+
'dontAsk',
|
|
49
|
+
]);
|
|
50
|
+
function asPermissionMode(value) {
|
|
51
|
+
return typeof value === 'string' && PERMISSION_MODES.has(value)
|
|
52
|
+
? value
|
|
53
|
+
: null;
|
|
54
|
+
}
|
|
36
55
|
// ── Goal gate detection regex ──
|
|
37
56
|
const GOAL_GATE_REGEX = /\b(CRITICAL|MUST|blocker|required|must.?complete|goal.?gate|essential|do.?not.?skip|blocking)\b/i;
|
|
38
57
|
// ── Task notifications delivered as attachments ──
|
|
@@ -51,6 +70,62 @@ function taskNotificationFromAttachmentFollowEvent(event) {
|
|
|
51
70
|
const asEvent = raw;
|
|
52
71
|
return (0, subagentSpawn_1.isTaskNotificationSystemEvent)(asEvent) ? (0, subagentSpawn_1.taskNotificationFromEvent)(asEvent) : null;
|
|
53
72
|
}
|
|
73
|
+
function subagentRecordFromFollowEvent(event) {
|
|
74
|
+
const raw = event.raw;
|
|
75
|
+
if (!raw || typeof raw !== 'object')
|
|
76
|
+
return null;
|
|
77
|
+
const notification = taskNotificationFromAttachmentFollowEvent(event);
|
|
78
|
+
if (notification)
|
|
79
|
+
return { kind: 'notification', notification };
|
|
80
|
+
const asEvent = raw;
|
|
81
|
+
const prompt = (0, subagentSpawn_1.taskNotificationPromptFromAttachment)(raw);
|
|
82
|
+
const notice = prompt !== null ? (0, subagentSpawn_1.parseAgentsStoppedNotice)(prompt) : (0, subagentSpawn_1.agentsStoppedNoticeFromEvent)(asEvent);
|
|
83
|
+
if (notice)
|
|
84
|
+
return { kind: 'stopped', notice };
|
|
85
|
+
if (prompt !== null || (0, subagentSpawn_1.isTaskNotificationSystemEvent)(asEvent)) {
|
|
86
|
+
// A notification this release cannot read: still bookkeeping.
|
|
87
|
+
return { kind: 'unreadable' };
|
|
88
|
+
}
|
|
89
|
+
const status = (0, subagentSpawn_1.taskStatusFromAttachment)(raw) ?? (0, subagentSpawn_1.taskStatusFromEvent)(asEvent);
|
|
90
|
+
if (status)
|
|
91
|
+
return { kind: 'task-status', status };
|
|
92
|
+
if ((0, subagentActivity_1.isSubagentActivitySystemEvent)(asEvent)) {
|
|
93
|
+
const activity = (0, subagentActivity_1.subagentActivityFromEvent)(asEvent);
|
|
94
|
+
return activity ? { kind: 'activity', activity } : { kind: 'unreadable' };
|
|
95
|
+
}
|
|
96
|
+
if ((0, subagentSpawn_1.isAgentsKilledSystemEvent)(raw))
|
|
97
|
+
return { kind: 'agents-killed' };
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
// ── Subagent spawn input ──
|
|
101
|
+
/**
|
|
102
|
+
* Description and type of a spawn: Claude Code's `description` /
|
|
103
|
+
* `subagent_type`, or Codex `spawn_agent`'s `task_name` / `agent_type`.
|
|
104
|
+
*/
|
|
105
|
+
function spawnFields(input) {
|
|
106
|
+
const text = (value) => typeof value === 'string' && value ? value : undefined;
|
|
107
|
+
const description = text(input.description) ?? text(input.task_name);
|
|
108
|
+
const subagentType = text(input.subagent_type) ?? text(input.subagentType) ?? text(input.agent_type);
|
|
109
|
+
return {
|
|
110
|
+
...(description ? { description } : {}),
|
|
111
|
+
...(subagentType ? { subagentType } : {}),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Whether a spawn's tool result launched the agent in the background or
|
|
116
|
+
* ended it. A Codex `spawn_agent` result is always a launch unless it failed
|
|
117
|
+
* ("collab spawn failed: …"); its `{"task_name": …}` output is a launch even
|
|
118
|
+
* when the output's text looked like an error to the parser (an agent path
|
|
119
|
+
* such as `/root/fix_failed_tests`).
|
|
120
|
+
*/
|
|
121
|
+
function spawnResultKind(codex, content, isError) {
|
|
122
|
+
// Read first: the `codex` hint does not survive a snapshot boundary.
|
|
123
|
+
if ((0, subagentSpawn_1.parseCodexSpawnLaunch)(content))
|
|
124
|
+
return 'launch';
|
|
125
|
+
if (codex)
|
|
126
|
+
return isError ? 'end' : 'launch';
|
|
127
|
+
return !isError && (0, subagentSpawn_1.parseAsyncAgentLaunch)(content) ? 'launch' : 'end';
|
|
128
|
+
}
|
|
54
129
|
// ── Todo dependency parsing ──
|
|
55
130
|
/**
|
|
56
131
|
* Parses dependency references from a todo item's content text.
|
|
@@ -136,6 +211,8 @@ class EventAggregator {
|
|
|
136
211
|
// Subagent lifecycle
|
|
137
212
|
subagents = [];
|
|
138
213
|
pendingSubagents = new Map(); // toolUseId -> index
|
|
214
|
+
/** Codex `spawn_agent` calls still waiting for their result (always a background launch). */
|
|
215
|
+
pendingCodexSpawns = new Set();
|
|
139
216
|
// Plan state
|
|
140
217
|
planExtractor;
|
|
141
218
|
planState = null;
|
|
@@ -198,8 +275,32 @@ class EventAggregator {
|
|
|
198
275
|
this.applyTaskNotification(notification, event.timestamp);
|
|
199
276
|
this.addTaskNotificationTimeline(event.timestamp, notification);
|
|
200
277
|
}
|
|
278
|
+
else {
|
|
279
|
+
// The plain-text "background agents were stopped by the user" notice.
|
|
280
|
+
const notice = (0, subagentSpawn_1.agentsStoppedNoticeFromEvent)(event);
|
|
281
|
+
if (notice)
|
|
282
|
+
this.applyAgentsStopped(notice, event.timestamp);
|
|
283
|
+
}
|
|
201
284
|
return;
|
|
202
285
|
}
|
|
286
|
+
// So are a background agent's final `task_status` (same opt-in) and the
|
|
287
|
+
// opt-in Codex subagent lifecycle records (`subagentActivityFromEvent`).
|
|
288
|
+
if ((0, subagentSpawn_1.isTaskStatusSystemEvent)(event)) {
|
|
289
|
+
const status = (0, subagentSpawn_1.taskStatusFromEvent)(event);
|
|
290
|
+
if (status)
|
|
291
|
+
this.applyTaskStatus(status, event.timestamp);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if ((0, subagentActivity_1.isSubagentActivitySystemEvent)(event)) {
|
|
295
|
+
const activity = (0, subagentActivity_1.subagentActivityFromEvent)(event);
|
|
296
|
+
if (activity)
|
|
297
|
+
this.applySubagentActivity(activity, event.timestamp);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
// The user killed every running background agent. The line itself is
|
|
301
|
+
// counted below as before.
|
|
302
|
+
if ((0, subagentSpawn_1.isAgentsKilledSystemEvent)(event))
|
|
303
|
+
this.applyAgentsKilled(event.timestamp);
|
|
203
304
|
// 1. Increment counters, capture timestamps
|
|
204
305
|
this.eventCount++;
|
|
205
306
|
if (!this.sessionStartTime) {
|
|
@@ -265,12 +366,13 @@ class EventAggregator {
|
|
|
265
366
|
// processFollowEvent — adapter for CLI FollowEvent format
|
|
266
367
|
// ═══════════════════════════════════════════════════════════════════════
|
|
267
368
|
processFollowEvent(event) {
|
|
268
|
-
//
|
|
369
|
+
// Subagent bookkeeping (a mid-turn task-notification or task-status
|
|
370
|
+
// attachment, a stop notice or `agents_killed` line, a Codex lifecycle
|
|
371
|
+
// record), like its SessionEvent form above: not counted as activity.
|
|
269
372
|
if (event.type === 'system') {
|
|
270
|
-
const
|
|
271
|
-
if (
|
|
272
|
-
this.
|
|
273
|
-
this.addTaskNotificationTimeline(event.timestamp, notification);
|
|
373
|
+
const record = subagentRecordFromFollowEvent(event);
|
|
374
|
+
if (record) {
|
|
375
|
+
this.applySubagentFollowRecord(record, event.timestamp);
|
|
274
376
|
return;
|
|
275
377
|
}
|
|
276
378
|
}
|
|
@@ -280,6 +382,8 @@ class EventAggregator {
|
|
|
280
382
|
this.sessionStartTime = event.timestamp;
|
|
281
383
|
}
|
|
282
384
|
this.lastEventTime = event.timestamp;
|
|
385
|
+
// Followed events carry the mode as a plain string.
|
|
386
|
+
this.trackPermissionMode(event);
|
|
283
387
|
// Provider
|
|
284
388
|
if (!this._providerId && event.providerId) {
|
|
285
389
|
this._providerId = event.providerId;
|
|
@@ -411,6 +515,10 @@ class EventAggregator {
|
|
|
411
515
|
getSubagents() {
|
|
412
516
|
return [...this.subagents];
|
|
413
517
|
}
|
|
518
|
+
/** Timestamp of the last counted event (`getMetrics().lastEventTime`), without building the metrics. */
|
|
519
|
+
getLastEventTime() {
|
|
520
|
+
return this.lastEventTime;
|
|
521
|
+
}
|
|
414
522
|
getPlan() {
|
|
415
523
|
return this.planState
|
|
416
524
|
? { ...this.planState, steps: this.planState.steps.map((step) => ({ ...step })) }
|
|
@@ -507,6 +615,7 @@ class EventAggregator {
|
|
|
507
615
|
this.activeTaskId = null;
|
|
508
616
|
this.subagents = [];
|
|
509
617
|
this.pendingSubagents.clear();
|
|
618
|
+
this.pendingCodexSpawns.clear();
|
|
510
619
|
this.planExtractor.reset();
|
|
511
620
|
this.planState = null;
|
|
512
621
|
this.planEnteredAt = undefined;
|
|
@@ -640,7 +749,7 @@ class EventAggregator {
|
|
|
640
749
|
this.tasks = new Map(state.tasks);
|
|
641
750
|
this.activeTaskId = state.activeTaskId;
|
|
642
751
|
this.subagents = [...state.subagents];
|
|
643
|
-
this.currentPermissionMode = state.permissionMode ?? null;
|
|
752
|
+
this.currentPermissionMode = asPermissionMode(state.permissionMode) ?? null;
|
|
644
753
|
this.permissionModeHistory = state.permissionModeHistory
|
|
645
754
|
? [...state.permissionModeHistory]
|
|
646
755
|
: [];
|
|
@@ -667,6 +776,7 @@ class EventAggregator {
|
|
|
667
776
|
this.pendingToolCalls.clear();
|
|
668
777
|
this.pendingTaskCreates.clear();
|
|
669
778
|
this.pendingSubagents.clear();
|
|
779
|
+
this.pendingCodexSpawns.clear();
|
|
670
780
|
// A spawn still waiting for its tool_result is resolved by that result,
|
|
671
781
|
// which can land after the snapshot boundary. Background agents need no
|
|
672
782
|
// entry: their notifications are matched against every subagent.
|
|
@@ -1153,7 +1263,7 @@ class EventAggregator {
|
|
|
1153
1263
|
else if (name === 'UpdatePlan') {
|
|
1154
1264
|
this.handleUpdatePlan(input, new Date(event.timestamp));
|
|
1155
1265
|
}
|
|
1156
|
-
else if ((0, subagentSpawn_1.isSubagentSpawnTool)(name) && toolUseId) {
|
|
1266
|
+
else if ((0, subagentSpawn_1.isSubagentSpawnTool)(name, input) && toolUseId) {
|
|
1157
1267
|
this.handleAgentSpawn(toolUseId, input, new Date(event.timestamp));
|
|
1158
1268
|
}
|
|
1159
1269
|
}
|
|
@@ -1197,7 +1307,7 @@ class EventAggregator {
|
|
|
1197
1307
|
else if (event.toolName === 'UpdatePlan') {
|
|
1198
1308
|
this.handleUpdatePlan(input, new Date(event.timestamp));
|
|
1199
1309
|
}
|
|
1200
|
-
else if ((0, subagentSpawn_1.isSubagentSpawnTool)(event.toolName) && toolUseId) {
|
|
1310
|
+
else if ((0, subagentSpawn_1.isSubagentSpawnTool)(event.toolName, input) && toolUseId) {
|
|
1201
1311
|
this.handleAgentSpawn(toolUseId, input, new Date(event.timestamp));
|
|
1202
1312
|
}
|
|
1203
1313
|
}
|
|
@@ -1496,8 +1606,9 @@ class EventAggregator {
|
|
|
1496
1606
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1497
1607
|
handleAgentSpawn(toolUseId, input, now) {
|
|
1498
1608
|
const agentTaskId = `agent-${toolUseId}`;
|
|
1499
|
-
const
|
|
1500
|
-
const
|
|
1609
|
+
const spawn = spawnFields(input);
|
|
1610
|
+
const description = spawn.description ?? 'Subagent';
|
|
1611
|
+
const subagentType = spawn.subagentType;
|
|
1501
1612
|
const task = {
|
|
1502
1613
|
taskId: agentTaskId,
|
|
1503
1614
|
subject: description,
|
|
@@ -1520,8 +1631,9 @@ class EventAggregator {
|
|
|
1520
1631
|
if (!agentTask)
|
|
1521
1632
|
return;
|
|
1522
1633
|
// A background launch returns at once; its task notification finishes it.
|
|
1523
|
-
if (
|
|
1634
|
+
if (spawnResultKind(this.pendingCodexSpawns.has(toolUseId), content, isError) === 'launch') {
|
|
1524
1635
|
return;
|
|
1636
|
+
}
|
|
1525
1637
|
agentTask.status = isError ? 'deleted' : 'completed';
|
|
1526
1638
|
agentTask.updatedAt = new Date(timestamp);
|
|
1527
1639
|
}
|
|
@@ -1548,12 +1660,12 @@ class EventAggregator {
|
|
|
1548
1660
|
if (!Array.isArray(content))
|
|
1549
1661
|
return;
|
|
1550
1662
|
for (const block of content) {
|
|
1551
|
-
if (block.type === 'tool_use' && (0, subagentSpawn_1.isSubagentSpawnTool)(block.name)) {
|
|
1663
|
+
if (block.type === 'tool_use' && (0, subagentSpawn_1.isSubagentSpawnTool)(block.name, block.input)) {
|
|
1552
1664
|
const input = block.input;
|
|
1553
1665
|
const toolUseId = block.id;
|
|
1554
1666
|
if (!input || !toolUseId)
|
|
1555
1667
|
continue;
|
|
1556
|
-
this.trackSubagentSpawn(toolUseId, input, event.timestamp);
|
|
1668
|
+
this.trackSubagentSpawn(toolUseId, input, event.timestamp, (0, subagentSpawn_1.isCodexSubagentSpawnTool)(block.name, input));
|
|
1557
1669
|
}
|
|
1558
1670
|
else if (block.type === 'tool_result') {
|
|
1559
1671
|
const toolUseId = block.tool_use_id;
|
|
@@ -1568,10 +1680,10 @@ class EventAggregator {
|
|
|
1568
1680
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1569
1681
|
extractSubagentFromFollowEvent(event) {
|
|
1570
1682
|
const raw = event.raw;
|
|
1571
|
-
if (event.type === 'tool_use' && (0, subagentSpawn_1.isSubagentSpawnTool)(event.toolName)) {
|
|
1683
|
+
if (event.type === 'tool_use' && (0, subagentSpawn_1.isSubagentSpawnTool)(event.toolName, raw?.input)) {
|
|
1572
1684
|
if (!raw?.input)
|
|
1573
1685
|
return;
|
|
1574
|
-
this.trackSubagentSpawn(raw.id || '', raw.input, event.timestamp);
|
|
1686
|
+
this.trackSubagentSpawn(raw.id || '', raw.input, event.timestamp, (0, subagentSpawn_1.isCodexSubagentSpawnTool)(event.toolName, raw.input));
|
|
1575
1687
|
}
|
|
1576
1688
|
else if (event.type === 'tool_result') {
|
|
1577
1689
|
if (!raw)
|
|
@@ -1588,11 +1700,12 @@ class EventAggregator {
|
|
|
1588
1700
|
this.applyTaskNotification(notification, event.timestamp);
|
|
1589
1701
|
}
|
|
1590
1702
|
}
|
|
1591
|
-
trackSubagentSpawn(toolUseId, input, timestamp) {
|
|
1703
|
+
trackSubagentSpawn(toolUseId, input, timestamp, codex = false) {
|
|
1704
|
+
const spawn = spawnFields(input);
|
|
1592
1705
|
const info = {
|
|
1593
1706
|
id: toolUseId,
|
|
1594
|
-
description:
|
|
1595
|
-
subagentType:
|
|
1707
|
+
description: spawn.description ?? 'Unknown task',
|
|
1708
|
+
subagentType: spawn.subagentType ?? (codex ? 'default' : 'general'),
|
|
1596
1709
|
spawnTime: timestamp,
|
|
1597
1710
|
status: 'running',
|
|
1598
1711
|
};
|
|
@@ -1600,19 +1713,23 @@ class EventAggregator {
|
|
|
1600
1713
|
this.subagents.push(info);
|
|
1601
1714
|
if (toolUseId) {
|
|
1602
1715
|
this.pendingSubagents.set(toolUseId, idx);
|
|
1716
|
+
if (codex)
|
|
1717
|
+
this.pendingCodexSpawns.add(toolUseId);
|
|
1603
1718
|
}
|
|
1604
1719
|
}
|
|
1605
1720
|
/**
|
|
1606
1721
|
* The spawning tool call's result. A foreground agent ends here; a
|
|
1607
|
-
* background launch only reports the agent id
|
|
1608
|
-
* until
|
|
1722
|
+
* background launch only reports the agent id (a Codex spawn, its agent
|
|
1723
|
+
* path), and the agent keeps running until a notification or another
|
|
1724
|
+
* record says it ended.
|
|
1609
1725
|
*/
|
|
1610
1726
|
resolveSubagentResult(toolUseId, content, isError, timestamp) {
|
|
1611
1727
|
const idx = this.pendingSubagents.get(toolUseId);
|
|
1612
1728
|
if (idx === undefined)
|
|
1613
1729
|
return;
|
|
1614
|
-
const
|
|
1615
|
-
if (launch) {
|
|
1730
|
+
const codex = this.pendingCodexSpawns.delete(toolUseId);
|
|
1731
|
+
if (spawnResultKind(codex, content, isError) === 'launch') {
|
|
1732
|
+
const launch = (0, subagentSpawn_1.parseCodexSpawnLaunch)(content) ?? (0, subagentSpawn_1.parseAsyncAgentLaunch)(content) ?? {};
|
|
1616
1733
|
this.markBackgroundLaunch(idx, launch);
|
|
1617
1734
|
return;
|
|
1618
1735
|
}
|
|
@@ -1624,6 +1741,8 @@ class EventAggregator {
|
|
|
1624
1741
|
agent.background = true;
|
|
1625
1742
|
if (launch.agentId)
|
|
1626
1743
|
agent.agentId = launch.agentId;
|
|
1744
|
+
if (launch.agentPath && !agent.agentPath)
|
|
1745
|
+
agent.agentPath = launch.agentPath;
|
|
1627
1746
|
}
|
|
1628
1747
|
completeSubagent(toolUseId, timestamp, outcome = 'completed') {
|
|
1629
1748
|
const idx = this.pendingSubagents.get(toolUseId);
|
|
@@ -1631,16 +1750,58 @@ class EventAggregator {
|
|
|
1631
1750
|
return;
|
|
1632
1751
|
this.finishSubagent(this.subagents[idx], timestamp, outcome);
|
|
1633
1752
|
}
|
|
1634
|
-
finishSubagent(agent, timestamp, outcome) {
|
|
1753
|
+
finishSubagent(agent, timestamp, outcome, settledBy) {
|
|
1635
1754
|
this.pendingSubagents.delete(agent.id);
|
|
1755
|
+
this.pendingCodexSpawns.delete(agent.id);
|
|
1636
1756
|
agent.status = 'completed';
|
|
1637
1757
|
agent.completionTime = timestamp;
|
|
1638
1758
|
agent.outcome = outcome;
|
|
1759
|
+
if (settledBy)
|
|
1760
|
+
agent.settledBy = settledBy;
|
|
1761
|
+
else
|
|
1762
|
+
delete agent.settledBy;
|
|
1639
1763
|
const start = new Date(agent.spawnTime).getTime();
|
|
1640
1764
|
const end = new Date(timestamp).getTime();
|
|
1641
1765
|
if (!isNaN(start) && !isNaN(end) && end >= start) {
|
|
1642
1766
|
agent.durationMs = end - start;
|
|
1643
1767
|
}
|
|
1768
|
+
else {
|
|
1769
|
+
delete agent.durationMs;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
/** Puts a settled agent back to running (it resumed). */
|
|
1773
|
+
reopenSubagent(agent, timestamp) {
|
|
1774
|
+
this.pendingSubagents.delete(agent.id);
|
|
1775
|
+
agent.status = 'running';
|
|
1776
|
+
delete agent.completionTime;
|
|
1777
|
+
delete agent.durationMs;
|
|
1778
|
+
delete agent.outcome;
|
|
1779
|
+
delete agent.settledBy;
|
|
1780
|
+
this.settleAgentTask(agent.id, null, timestamp);
|
|
1781
|
+
}
|
|
1782
|
+
/**
|
|
1783
|
+
* Settles a lifecycle that is still running (and its task card). Agents
|
|
1784
|
+
* already settled are left alone: a stop notice or status can name agents
|
|
1785
|
+
* that finished earlier.
|
|
1786
|
+
*/
|
|
1787
|
+
settleRunningSubagent(agent, timestamp, outcome, settledBy) {
|
|
1788
|
+
if (agent.status !== 'running')
|
|
1789
|
+
return false;
|
|
1790
|
+
this.finishSubagent(agent, timestamp, outcome, settledBy);
|
|
1791
|
+
this.settleAgentTask(agent.id, outcome, timestamp);
|
|
1792
|
+
return true;
|
|
1793
|
+
}
|
|
1794
|
+
/**
|
|
1795
|
+
* Moves an agent's task card with its lifecycle: `null` back to in
|
|
1796
|
+
* progress, a failure to deleted, any other outcome to completed.
|
|
1797
|
+
*/
|
|
1798
|
+
settleAgentTask(toolUseId, outcome, timestamp) {
|
|
1799
|
+
const agentTask = toolUseId ? this.tasks.get(`agent-${toolUseId}`) : undefined;
|
|
1800
|
+
if (!agentTask?.isSubagent)
|
|
1801
|
+
return;
|
|
1802
|
+
agentTask.status =
|
|
1803
|
+
outcome === null ? 'in_progress' : outcome === 'failed' ? 'deleted' : 'completed';
|
|
1804
|
+
agentTask.updatedAt = new Date(timestamp);
|
|
1644
1805
|
}
|
|
1645
1806
|
/**
|
|
1646
1807
|
* Applies a background task's notification to the subagent it reports on,
|
|
@@ -1663,20 +1824,13 @@ class EventAggregator {
|
|
|
1663
1824
|
delete agent.completionTime;
|
|
1664
1825
|
delete agent.durationMs;
|
|
1665
1826
|
delete agent.outcome;
|
|
1827
|
+
delete agent.settledBy;
|
|
1666
1828
|
}
|
|
1667
1829
|
else {
|
|
1668
|
-
this.finishSubagent(agent, timestamp, outcome);
|
|
1830
|
+
this.finishSubagent(agent, timestamp, outcome, 'notification');
|
|
1669
1831
|
}
|
|
1670
1832
|
}
|
|
1671
|
-
|
|
1672
|
-
if (agentTask?.isSubagent) {
|
|
1673
|
-
agentTask.status = notification.interim
|
|
1674
|
-
? 'in_progress'
|
|
1675
|
-
: outcome === 'failed'
|
|
1676
|
-
? 'deleted'
|
|
1677
|
-
: 'completed';
|
|
1678
|
-
agentTask.updatedAt = new Date(timestamp);
|
|
1679
|
-
}
|
|
1833
|
+
this.settleAgentTask(toolUseId, notification.interim ? null : outcome, timestamp);
|
|
1680
1834
|
}
|
|
1681
1835
|
findNotifiedSubagent(notification) {
|
|
1682
1836
|
const { toolUseId, taskId } = notification;
|
|
@@ -1690,6 +1844,206 @@ class EventAggregator {
|
|
|
1690
1844
|
}
|
|
1691
1845
|
return undefined;
|
|
1692
1846
|
}
|
|
1847
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1848
|
+
// Subagent settling: evidence, stop markers, task statuses, Codex records
|
|
1849
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
1850
|
+
/**
|
|
1851
|
+
* Applies host evidence about a subagent (Claude Code's meta flags):
|
|
1852
|
+
* `stoppedByUser` settles a running agent as `stopped`, `finished` as
|
|
1853
|
+
* `completed` (`settledBy: 'meta'`). Agents already settled some other way
|
|
1854
|
+
* keep their outcome; `finished: false` reopens an agent that only meta
|
|
1855
|
+
* settled. Matches by `toolUseId`, then `agentId` (the newest such agent),
|
|
1856
|
+
* and records the agent id when matched by tool_use id.
|
|
1857
|
+
*
|
|
1858
|
+
* @param timestamp ISO time to record as the completion (the agent's last
|
|
1859
|
+
* activity, when the host knows it)
|
|
1860
|
+
* @returns Whether a lifecycle changed
|
|
1861
|
+
*/
|
|
1862
|
+
applySubagentEvidence(evidence, timestamp) {
|
|
1863
|
+
const agent = this.findSubagent(evidence.toolUseId, evidence.agentId);
|
|
1864
|
+
if (!agent)
|
|
1865
|
+
return false;
|
|
1866
|
+
let changed = false;
|
|
1867
|
+
if (evidence.agentId && !agent.agentId) {
|
|
1868
|
+
agent.agentId = evidence.agentId;
|
|
1869
|
+
changed = true;
|
|
1870
|
+
}
|
|
1871
|
+
if (agent.status === 'running') {
|
|
1872
|
+
const outcome = evidence.stoppedByUser
|
|
1873
|
+
? 'stopped'
|
|
1874
|
+
: evidence.finished
|
|
1875
|
+
? 'completed'
|
|
1876
|
+
: null;
|
|
1877
|
+
if (outcome)
|
|
1878
|
+
changed = this.settleRunningSubagent(agent, timestamp, outcome, 'meta') || changed;
|
|
1879
|
+
}
|
|
1880
|
+
else if (agent.settledBy === 'meta' &&
|
|
1881
|
+
evidence.finished === false &&
|
|
1882
|
+
evidence.stoppedByUser !== true) {
|
|
1883
|
+
this.reopenSubagent(agent, timestamp);
|
|
1884
|
+
changed = true;
|
|
1885
|
+
}
|
|
1886
|
+
return changed;
|
|
1887
|
+
}
|
|
1888
|
+
/** The newest lifecycle with this spawning tool_use id, else with this agent id. */
|
|
1889
|
+
findSubagent(toolUseId, agentId) {
|
|
1890
|
+
if (toolUseId) {
|
|
1891
|
+
for (let i = this.subagents.length - 1; i >= 0; i--) {
|
|
1892
|
+
if (this.subagents[i].id === toolUseId)
|
|
1893
|
+
return this.subagents[i];
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
if (agentId) {
|
|
1897
|
+
for (let i = this.subagents.length - 1; i >= 0; i--) {
|
|
1898
|
+
if (this.subagents[i].agentId === agentId)
|
|
1899
|
+
return this.subagents[i];
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
return undefined;
|
|
1903
|
+
}
|
|
1904
|
+
/** `agents_killed`: the user killed every running background agent. */
|
|
1905
|
+
applyAgentsKilled(timestamp) {
|
|
1906
|
+
let settled = 0;
|
|
1907
|
+
for (const agent of this.subagents) {
|
|
1908
|
+
if (!agent.background)
|
|
1909
|
+
continue;
|
|
1910
|
+
if (this.settleRunningSubagent(agent, timestamp, 'stopped', 'stop-marker'))
|
|
1911
|
+
settled++;
|
|
1912
|
+
}
|
|
1913
|
+
if (settled > 0)
|
|
1914
|
+
this.addSubagentTimeline(timestamp, (0, subagentSpawn_1.describeAgentsStopped)(null));
|
|
1915
|
+
}
|
|
1916
|
+
/**
|
|
1917
|
+
* "N background agents were stopped by the user: "A", "B"": settles the
|
|
1918
|
+
* running agents with those descriptions, the newest first for repeats.
|
|
1919
|
+
*/
|
|
1920
|
+
applyAgentsStopped(notice, timestamp) {
|
|
1921
|
+
const taken = new Set();
|
|
1922
|
+
for (const description of notice.descriptions) {
|
|
1923
|
+
for (let i = this.subagents.length - 1; i >= 0; i--) {
|
|
1924
|
+
const agent = this.subagents[i];
|
|
1925
|
+
if (taken.has(agent) || agent.status !== 'running')
|
|
1926
|
+
continue;
|
|
1927
|
+
if (agent.description.trim() !== description)
|
|
1928
|
+
continue;
|
|
1929
|
+
taken.add(agent);
|
|
1930
|
+
this.settleRunningSubagent(agent, timestamp, 'stopped', 'stop-marker');
|
|
1931
|
+
break;
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
this.addSubagentTimeline(timestamp, (0, subagentSpawn_1.describeAgentsStopped)(notice));
|
|
1935
|
+
}
|
|
1936
|
+
/** A background agent's final `task_status` attachment, matched by agent id. */
|
|
1937
|
+
applyTaskStatus(status, timestamp) {
|
|
1938
|
+
if (!(0, subagentSpawn_1.isFinalTaskStatus)(status.status))
|
|
1939
|
+
return;
|
|
1940
|
+
const agent = this.findSubagent(undefined, status.taskId);
|
|
1941
|
+
if (!agent)
|
|
1942
|
+
return;
|
|
1943
|
+
if (this.settleRunningSubagent(agent, timestamp, (0, subagentSpawn_1.taskNotificationOutcome)(status.status), 'task-status')) {
|
|
1944
|
+
this.addSubagentTimeline(timestamp, (0, subagentSpawn_1.describeTaskStatus)(status), agent.id);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
/**
|
|
1948
|
+
* A Codex subagent lifecycle record: `started` links the spawn to its child
|
|
1949
|
+
* thread; `completed` (a child turn finished) and `final-answer` (the
|
|
1950
|
+
* child's report, matched by its agent path) complete it; `interacted`
|
|
1951
|
+
* after `followup_task` puts it back to running (a `send_message` does
|
|
1952
|
+
* not: it does not start a turn); `interrupted` (`interrupt_agent`) settles
|
|
1953
|
+
* it as `stopped`, since the child keeps its thread and a follow-up can
|
|
1954
|
+
* run it again, like a user stop rather than a kill.
|
|
1955
|
+
*/
|
|
1956
|
+
applySubagentActivity(activity, timestamp) {
|
|
1957
|
+
const byThread = () => this.findSubagent(undefined, activity.agentThreadId) ??
|
|
1958
|
+
this.findSubagentByPath(activity.agentPath);
|
|
1959
|
+
let agent;
|
|
1960
|
+
switch (activity.kind) {
|
|
1961
|
+
case 'started': {
|
|
1962
|
+
agent = this.findSubagent(activity.callId, undefined);
|
|
1963
|
+
if (!agent)
|
|
1964
|
+
break;
|
|
1965
|
+
agent.background = true;
|
|
1966
|
+
if (activity.agentThreadId)
|
|
1967
|
+
agent.agentId = activity.agentThreadId;
|
|
1968
|
+
if (activity.agentPath)
|
|
1969
|
+
agent.agentPath = activity.agentPath;
|
|
1970
|
+
break;
|
|
1971
|
+
}
|
|
1972
|
+
case 'completed':
|
|
1973
|
+
agent = byThread();
|
|
1974
|
+
if (agent)
|
|
1975
|
+
this.settleRunningSubagent(agent, timestamp, 'completed', 'notification');
|
|
1976
|
+
break;
|
|
1977
|
+
case 'final-answer':
|
|
1978
|
+
agent = this.findSubagentByPath(activity.agentPath, true);
|
|
1979
|
+
if (agent)
|
|
1980
|
+
this.settleRunningSubagent(agent, timestamp, 'completed', 'notification');
|
|
1981
|
+
break;
|
|
1982
|
+
case 'interacted':
|
|
1983
|
+
agent = byThread();
|
|
1984
|
+
if (agent && activity.tool === 'followup_task' && agent.status === 'completed') {
|
|
1985
|
+
this.reopenSubagent(agent, timestamp);
|
|
1986
|
+
}
|
|
1987
|
+
break;
|
|
1988
|
+
case 'interrupted':
|
|
1989
|
+
agent = byThread();
|
|
1990
|
+
if (agent)
|
|
1991
|
+
this.settleRunningSubagent(agent, timestamp, 'stopped', 'stop-marker');
|
|
1992
|
+
break;
|
|
1993
|
+
}
|
|
1994
|
+
this.addSubagentTimeline(timestamp, (0, subagentActivity_1.describeSubagentActivity)(activity), agent?.id);
|
|
1995
|
+
}
|
|
1996
|
+
/** The newest lifecycle with this Codex agent path (with `runningFirst`, a running one first). */
|
|
1997
|
+
findSubagentByPath(agentPath, runningFirst = false) {
|
|
1998
|
+
if (!agentPath)
|
|
1999
|
+
return undefined;
|
|
2000
|
+
let fallback;
|
|
2001
|
+
for (let i = this.subagents.length - 1; i >= 0; i--) {
|
|
2002
|
+
const agent = this.subagents[i];
|
|
2003
|
+
if (agent.agentPath !== agentPath)
|
|
2004
|
+
continue;
|
|
2005
|
+
if (!runningFirst || agent.status === 'running')
|
|
2006
|
+
return agent;
|
|
2007
|
+
fallback ??= agent;
|
|
2008
|
+
}
|
|
2009
|
+
return fallback;
|
|
2010
|
+
}
|
|
2011
|
+
applySubagentFollowRecord(record, timestamp) {
|
|
2012
|
+
switch (record.kind) {
|
|
2013
|
+
case 'notification':
|
|
2014
|
+
this.applyTaskNotification(record.notification, timestamp);
|
|
2015
|
+
this.addTaskNotificationTimeline(timestamp, record.notification);
|
|
2016
|
+
break;
|
|
2017
|
+
case 'stopped':
|
|
2018
|
+
this.applyAgentsStopped(record.notice, timestamp);
|
|
2019
|
+
break;
|
|
2020
|
+
case 'task-status':
|
|
2021
|
+
this.applyTaskStatus(record.status, timestamp);
|
|
2022
|
+
break;
|
|
2023
|
+
case 'activity':
|
|
2024
|
+
this.applySubagentActivity(record.activity, timestamp);
|
|
2025
|
+
break;
|
|
2026
|
+
case 'agents-killed':
|
|
2027
|
+
this.applyAgentsKilled(timestamp);
|
|
2028
|
+
break;
|
|
2029
|
+
case 'unreadable':
|
|
2030
|
+
break;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
addSubagentTimeline(timestamp, description, toolUseId) {
|
|
2034
|
+
const text = description.length > 200 ? description.substring(0, 197) + '...' : description;
|
|
2035
|
+
this.timeline.push({
|
|
2036
|
+
type: 'tool_result',
|
|
2037
|
+
timestamp,
|
|
2038
|
+
description: text,
|
|
2039
|
+
noiseLevel: 'system',
|
|
2040
|
+
messageClassification: 'system',
|
|
2041
|
+
...(toolUseId ? { metadata: { toolUseId } } : {}),
|
|
2042
|
+
});
|
|
2043
|
+
if (this.timeline.length > this.timelineCap) {
|
|
2044
|
+
this.timeline.shift();
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
1693
2047
|
addTaskNotificationTimeline(timestamp, notification) {
|
|
1694
2048
|
let description = notification.summary ?? `Background task ${notification.status ?? 'finished'}`;
|
|
1695
2049
|
if (description.length > 200)
|
|
@@ -1940,9 +2294,9 @@ class EventAggregator {
|
|
|
1940
2294
|
// Private: Permission Mode
|
|
1941
2295
|
// ═══════════════════════════════════════════════════════════════════════
|
|
1942
2296
|
trackPermissionMode(event) {
|
|
1943
|
-
|
|
2297
|
+
const mode = asPermissionMode(event.permissionMode);
|
|
2298
|
+
if (!mode)
|
|
1944
2299
|
return;
|
|
1945
|
-
const mode = event.permissionMode;
|
|
1946
2300
|
if (mode !== this.currentPermissionMode) {
|
|
1947
2301
|
this.permissionModeHistory.push({
|
|
1948
2302
|
timestamp: event.timestamp,
|
|
@@ -119,9 +119,23 @@ export interface BurnRateInfo {
|
|
|
119
119
|
}
|
|
120
120
|
/** How a subagent ended: a failed spawn, or a background agent's notification status. */
|
|
121
121
|
export type SubagentOutcome = 'completed' | 'failed' | 'killed' | 'stopped';
|
|
122
|
+
/**
|
|
123
|
+
* What settled a background subagent, when something other than its spawn's
|
|
124
|
+
* own tool result did:
|
|
125
|
+
*
|
|
126
|
+
* - `notification`: its final task notification (Claude Code), or a Codex
|
|
127
|
+
* `SubAgentActivity` `completed` record or `FINAL_ANSWER` report.
|
|
128
|
+
* - `meta`: the host's evidence from Claude Code's `agent-<id>.meta.json`
|
|
129
|
+
* (`finished`, `stoppedByUser`), via `EventAggregator.applySubagentEvidence()`.
|
|
130
|
+
* - `stop-marker`: an in-stream stop: Claude Code's `agents_killed` system
|
|
131
|
+
* line or its "background agents were stopped by the user" notice, or a
|
|
132
|
+
* Codex `interrupt_agent` record.
|
|
133
|
+
* - `task-status`: a Claude Code `task_status` attachment with a final status.
|
|
134
|
+
*/
|
|
135
|
+
export type SubagentSettledBy = 'notification' | 'meta' | 'stop-marker' | 'task-status';
|
|
122
136
|
/** Subagent lifecycle info (for tracking spawn/completion from tool events). */
|
|
123
137
|
export interface SubagentLifecycle {
|
|
124
|
-
/** The spawning tool_use id (`Agent` / `Task`). */
|
|
138
|
+
/** The spawning tool_use id (`Agent` / `Task`, or Codex `spawn_agent`). */
|
|
125
139
|
id: string;
|
|
126
140
|
description: string;
|
|
127
141
|
subagentType: string;
|
|
@@ -133,12 +147,22 @@ export interface SubagentLifecycle {
|
|
|
133
147
|
*/
|
|
134
148
|
status: 'running' | 'completed';
|
|
135
149
|
durationMs?: number;
|
|
136
|
-
/**
|
|
150
|
+
/**
|
|
151
|
+
* The agent's id, once known: Claude Code's agent id (the `agent-<id>.jsonl`
|
|
152
|
+
* transcript name), or the Codex child thread id (its rollout's session id).
|
|
153
|
+
*/
|
|
137
154
|
agentId?: string;
|
|
155
|
+
/** Codex: the child's agent path (e.g. `/root/review_builder`), once known. */
|
|
156
|
+
agentPath?: string;
|
|
138
157
|
/** The agent was launched in the background (its spawn returned at once). */
|
|
139
158
|
background?: boolean;
|
|
140
159
|
/** How the agent ended, once `status` is `completed`. */
|
|
141
160
|
outcome?: SubagentOutcome;
|
|
161
|
+
/**
|
|
162
|
+
* What settled a background agent (see {@link SubagentSettledBy}); absent
|
|
163
|
+
* while it runs and when its spawn's own result settled it.
|
|
164
|
+
*/
|
|
165
|
+
settledBy?: SubagentSettledBy;
|
|
142
166
|
}
|
|
143
167
|
/** Frequency entry for tool/word frequency tracking. */
|
|
144
168
|
export interface FrequencyMetric {
|
|
@@ -178,7 +202,7 @@ export interface AggregatedMetrics {
|
|
|
178
202
|
taskState: TaskState;
|
|
179
203
|
subagents: SubagentLifecycle[];
|
|
180
204
|
plan: PlanState | null;
|
|
181
|
-
/** Current permission mode (default | acceptEdits | bypassPermissions | plan) */
|
|
205
|
+
/** Current permission mode (default | acceptEdits | bypassPermissions | plan | auto | dontAsk) */
|
|
182
206
|
permissionMode: PermissionMode | null;
|
|
183
207
|
/** History of permission mode changes */
|
|
184
208
|
permissionModeHistory: PermissionModeChange[];
|