@gotgenes/pi-subagents 10.2.1 → 11.0.1
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 +30 -0
- package/docs/architecture/architecture.md +6 -5
- package/docs/plans/0229-agent-born-complete.md +564 -0
- package/docs/retro/0229-agent-born-complete.md +89 -0
- package/docs/retro/0231-push-exec-registry-to-runner.md +31 -0
- package/package.json +1 -1
- package/src/lifecycle/agent-manager.ts +49 -103
- package/src/lifecycle/agent-runner.ts +1 -2
- package/src/lifecycle/agent.ts +166 -39
- package/src/observation/record-observer.ts +1 -2
- package/src/tools/agent-tool.ts +2 -2
- package/src/tools/background-spawner.ts +7 -5
- package/src/tools/foreground-runner.ts +11 -9
- package/src/types.ts +13 -0
package/src/lifecycle/agent.ts
CHANGED
|
@@ -15,16 +15,32 @@
|
|
|
15
15
|
* after construction as lifecycle information becomes available.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
import type { Model } from "@earendil-works/pi-ai";
|
|
18
19
|
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
19
20
|
import { debugLog } from "#src/debug";
|
|
20
|
-
import type { RunResult } from "#src/lifecycle/agent-runner";
|
|
21
|
+
import type { AgentRunner, RunResult } from "#src/lifecycle/agent-runner";
|
|
21
22
|
import type { ExecutionState } from "#src/lifecycle/execution-state";
|
|
23
|
+
import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
|
|
22
24
|
import type { LifetimeUsage } from "#src/lifecycle/usage";
|
|
23
25
|
import { addUsage } from "#src/lifecycle/usage";
|
|
24
26
|
import type { WorktreeManager } from "#src/lifecycle/worktree";
|
|
25
27
|
import { WorktreeState } from "#src/lifecycle/worktree-state";
|
|
26
|
-
import
|
|
27
|
-
import
|
|
28
|
+
import { NotificationState } from "#src/observation/notification-state";
|
|
29
|
+
import { subscribeAgentObserver } from "#src/observation/record-observer";
|
|
30
|
+
import type { RunConfig } from "#src/runtime";
|
|
31
|
+
import type { AgentInvocation, CompactionInfo, IsolationMode, ParentSessionInfo, SubagentType, ThinkingLevel } from "#src/types";
|
|
32
|
+
|
|
33
|
+
/** Per-agent lifecycle observer — created by AgentManager for each spawn. */
|
|
34
|
+
export interface AgentLifecycleObserver {
|
|
35
|
+
/** Fires when the agent transitions to running (inside run(), after markRunning). */
|
|
36
|
+
onStarted?(agent: Agent): void;
|
|
37
|
+
/** Fires when the runner creates the session — delivers the session to external consumers. */
|
|
38
|
+
onSessionCreated?(agent: Agent, session: AgentSession): void;
|
|
39
|
+
/** Fires once when the run completes or fails (for concurrency drain). */
|
|
40
|
+
onRunFinished?(agent: Agent): void;
|
|
41
|
+
/** Fires on compaction events during the run. */
|
|
42
|
+
onCompacted?(agent: Agent, info: CompactionInfo): void;
|
|
43
|
+
}
|
|
28
44
|
|
|
29
45
|
export type AgentStatus =
|
|
30
46
|
| "queued"
|
|
@@ -36,17 +52,36 @@ export type AgentStatus =
|
|
|
36
52
|
| "error";
|
|
37
53
|
|
|
38
54
|
export interface AgentInit {
|
|
55
|
+
// Identity
|
|
39
56
|
id: string;
|
|
40
57
|
type: SubagentType;
|
|
41
58
|
description: string;
|
|
59
|
+
invocation?: AgentInvocation;
|
|
60
|
+
|
|
61
|
+
// Status (for tests and restore scenarios)
|
|
42
62
|
status?: AgentStatus;
|
|
43
63
|
startedAt?: number;
|
|
44
64
|
completedAt?: number;
|
|
45
65
|
result?: string;
|
|
46
66
|
error?: string;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
67
|
+
|
|
68
|
+
// Shared deps (required for run(), optional for tests)
|
|
69
|
+
runner?: AgentRunner;
|
|
70
|
+
worktrees?: WorktreeManager;
|
|
71
|
+
observer?: AgentLifecycleObserver;
|
|
72
|
+
getRunConfig?: () => RunConfig;
|
|
73
|
+
|
|
74
|
+
// Run config (required for run(), optional for tests)
|
|
75
|
+
snapshot?: ParentSnapshot;
|
|
76
|
+
prompt?: string;
|
|
77
|
+
model?: Model<any>;
|
|
78
|
+
maxTurns?: number;
|
|
79
|
+
isolated?: boolean;
|
|
80
|
+
thinkingLevel?: ThinkingLevel;
|
|
81
|
+
isolation?: IsolationMode;
|
|
82
|
+
parentSession?: ParentSessionInfo;
|
|
83
|
+
isBackground?: boolean;
|
|
84
|
+
signal?: AbortSignal;
|
|
50
85
|
}
|
|
51
86
|
|
|
52
87
|
export class Agent {
|
|
@@ -82,11 +117,28 @@ export class Agent {
|
|
|
82
117
|
private _compactionCount: number;
|
|
83
118
|
get compactionCount(): number { return this._compactionCount; }
|
|
84
119
|
|
|
85
|
-
/** AbortController for cancelling this agent.
|
|
86
|
-
readonly abortController
|
|
87
|
-
/** Promise for the full agent run (including post-processing). Set
|
|
120
|
+
/** AbortController for cancelling this agent. Created at construction. */
|
|
121
|
+
readonly abortController: AbortController;
|
|
122
|
+
/** Promise for the full agent run (including post-processing). Set by run(). */
|
|
88
123
|
promise?: Promise<void>;
|
|
89
124
|
|
|
125
|
+
// Shared deps — optional (required for run())
|
|
126
|
+
private readonly _runner?: AgentRunner;
|
|
127
|
+
private readonly _worktrees?: WorktreeManager;
|
|
128
|
+
readonly observer?: AgentLifecycleObserver;
|
|
129
|
+
private readonly _getRunConfig?: () => RunConfig;
|
|
130
|
+
|
|
131
|
+
// Run config — optional (required for run())
|
|
132
|
+
private readonly _snapshot?: ParentSnapshot;
|
|
133
|
+
private readonly _prompt?: string;
|
|
134
|
+
private readonly _model?: Model<any>;
|
|
135
|
+
private readonly _maxTurns?: number;
|
|
136
|
+
private readonly _isolated?: boolean;
|
|
137
|
+
private readonly _thinkingLevel?: ThinkingLevel;
|
|
138
|
+
private readonly _isolation?: IsolationMode;
|
|
139
|
+
private readonly _parentSession?: ParentSessionInfo;
|
|
140
|
+
private readonly _signal?: AbortSignal;
|
|
141
|
+
|
|
90
142
|
// Phase-specific collaborators — each born complete when their info becomes available
|
|
91
143
|
execution?: ExecutionState;
|
|
92
144
|
worktreeState?: WorktreeState;
|
|
@@ -96,10 +148,14 @@ export class Agent {
|
|
|
96
148
|
* Create a git worktree for isolated execution, set worktreeState, and return the worktree path.
|
|
97
149
|
* Returns undefined if isolation is not "worktree".
|
|
98
150
|
* Throws if worktree creation fails (strict isolation).
|
|
151
|
+
* Uses this._worktrees and this._isolation (set at construction).
|
|
99
152
|
*/
|
|
100
|
-
setupWorktree(
|
|
101
|
-
if (
|
|
102
|
-
|
|
153
|
+
setupWorktree(): string | undefined {
|
|
154
|
+
if (this._isolation !== "worktree") return undefined;
|
|
155
|
+
if (!this._worktrees) {
|
|
156
|
+
throw new Error("Agent not configured for worktree isolation — missing worktrees dependency");
|
|
157
|
+
}
|
|
158
|
+
const wt = this._worktrees.create(this.id);
|
|
103
159
|
if (!wt) {
|
|
104
160
|
throw new Error(
|
|
105
161
|
'Cannot run with isolation: "worktree" — not a git repo, no commits yet, or `git worktree add` failed. ' +
|
|
@@ -126,22 +182,107 @@ export class Agent {
|
|
|
126
182
|
}
|
|
127
183
|
|
|
128
184
|
constructor(init: AgentInit) {
|
|
185
|
+
// Identity
|
|
129
186
|
this.id = init.id;
|
|
130
187
|
this.type = init.type;
|
|
131
188
|
this.description = init.description;
|
|
132
189
|
this.invocation = init.invocation;
|
|
133
190
|
|
|
191
|
+
// Status
|
|
134
192
|
this._status = init.status ?? "queued";
|
|
135
193
|
this._result = init.result;
|
|
136
194
|
this._error = init.error;
|
|
137
195
|
this._startedAt = init.startedAt ?? Date.now();
|
|
138
196
|
this._completedAt = init.completedAt;
|
|
139
197
|
|
|
198
|
+
// Stats
|
|
140
199
|
this._toolUses = 0;
|
|
141
200
|
this._lifetimeUsage = { input: 0, output: 0, cacheWrite: 0 };
|
|
142
201
|
this._compactionCount = 0;
|
|
143
|
-
|
|
144
|
-
|
|
202
|
+
|
|
203
|
+
// Abort controller — always created, never injected
|
|
204
|
+
this.abortController = new AbortController();
|
|
205
|
+
|
|
206
|
+
// Shared deps
|
|
207
|
+
this._runner = init.runner;
|
|
208
|
+
this._worktrees = init.worktrees;
|
|
209
|
+
this.observer = init.observer;
|
|
210
|
+
this._getRunConfig = init.getRunConfig;
|
|
211
|
+
|
|
212
|
+
// Run config
|
|
213
|
+
this._snapshot = init.snapshot;
|
|
214
|
+
this._prompt = init.prompt;
|
|
215
|
+
this._model = init.model;
|
|
216
|
+
this._maxTurns = init.maxTurns;
|
|
217
|
+
this._isolated = init.isolated;
|
|
218
|
+
this._thinkingLevel = init.thinkingLevel;
|
|
219
|
+
this._isolation = init.isolation;
|
|
220
|
+
this._parentSession = init.parentSession;
|
|
221
|
+
this._signal = init.signal;
|
|
222
|
+
|
|
223
|
+
// Notification state — created from parentSession.toolCallId if present
|
|
224
|
+
if (init.parentSession?.toolCallId) {
|
|
225
|
+
this.notification = new NotificationState(init.parentSession.toolCallId);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Execute the full agent lifecycle: worktree setup, runner invocation,
|
|
231
|
+
* session-creation handling, observer wiring, worktree cleanup, and
|
|
232
|
+
* status transitions.
|
|
233
|
+
*
|
|
234
|
+
* Requires runner and snapshot to be set at construction.
|
|
235
|
+
* The returned promise always resolves (errors are captured internally).
|
|
236
|
+
*/
|
|
237
|
+
async run(): Promise<void> {
|
|
238
|
+
if (!this._runner) {
|
|
239
|
+
throw new Error("Agent not configured for execution — missing runner");
|
|
240
|
+
}
|
|
241
|
+
if (!this._snapshot || !this._prompt) {
|
|
242
|
+
throw new Error("Agent not configured for execution — missing snapshot or prompt");
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
this.markRunning(Date.now());
|
|
246
|
+
this.observer?.onStarted?.(this);
|
|
247
|
+
this.wireSignal(this._signal, () => this.abort());
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
this.setupWorktree();
|
|
251
|
+
} catch (err) {
|
|
252
|
+
this.markError(err);
|
|
253
|
+
this.observer?.onRunFinished?.(this);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const runConfig = this._getRunConfig?.();
|
|
258
|
+
try {
|
|
259
|
+
const result = await this._runner.run(this._snapshot, this.type, this._prompt, {
|
|
260
|
+
context: {
|
|
261
|
+
cwd: this.worktreeState?.path,
|
|
262
|
+
parentSession: this._parentSession,
|
|
263
|
+
},
|
|
264
|
+
model: this._model,
|
|
265
|
+
maxTurns: this._maxTurns,
|
|
266
|
+
defaultMaxTurns: runConfig?.defaultMaxTurns,
|
|
267
|
+
graceTurns: runConfig?.graceTurns,
|
|
268
|
+
isolated: this._isolated,
|
|
269
|
+
thinkingLevel: this._thinkingLevel,
|
|
270
|
+
signal: this.abortController.signal,
|
|
271
|
+
onSessionCreated: (session) => {
|
|
272
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- sessionManager is typed as always present but Pi SDK may not provide it
|
|
273
|
+
const outputFile = session.sessionManager?.getSessionFile?.() ?? undefined;
|
|
274
|
+
this.execution = { session, outputFile };
|
|
275
|
+
this.flushPendingSteers(session);
|
|
276
|
+
this.attachObserver(subscribeAgentObserver(session, this, {
|
|
277
|
+
onCompact: (r, info) => this.observer?.onCompacted?.(r, info),
|
|
278
|
+
}));
|
|
279
|
+
this.observer?.onSessionCreated?.(this, session);
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
this.completeRun(result);
|
|
283
|
+
} catch (err) {
|
|
284
|
+
this.failRun(err);
|
|
285
|
+
}
|
|
145
286
|
}
|
|
146
287
|
|
|
147
288
|
/** Increment tool use count. Called by record-observer on tool_execution_end. */
|
|
@@ -226,7 +367,7 @@ export class Agent {
|
|
|
226
367
|
*/
|
|
227
368
|
abort(): boolean {
|
|
228
369
|
if (this._status !== "running") return false;
|
|
229
|
-
this.abortController
|
|
370
|
+
this.abortController.abort();
|
|
230
371
|
this.markStopped();
|
|
231
372
|
return true;
|
|
232
373
|
}
|
|
@@ -258,13 +399,11 @@ export class Agent {
|
|
|
258
399
|
this._result = undefined;
|
|
259
400
|
this._error = undefined;
|
|
260
401
|
this.releaseListeners();
|
|
261
|
-
this._onRunFinished = undefined;
|
|
262
402
|
}
|
|
263
403
|
|
|
264
404
|
// --- Per-run listener state (released on completion or resume reset) ---
|
|
265
405
|
private _unsub?: () => void;
|
|
266
406
|
private _detachFn?: () => void;
|
|
267
|
-
private _onRunFinished?: () => void;
|
|
268
407
|
|
|
269
408
|
/** Wire a parent AbortSignal so it stops this agent when fired. */
|
|
270
409
|
wireSignal(signal: AbortSignal | undefined, onAbort: () => void): void {
|
|
@@ -287,25 +426,13 @@ export class Agent {
|
|
|
287
426
|
this._detachFn = undefined;
|
|
288
427
|
}
|
|
289
428
|
|
|
290
|
-
/**
|
|
291
|
-
|
|
292
|
-
this._onRunFinished = fn;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/** Fire the onRunFinished callback at most once. */
|
|
296
|
-
private fireOnRunFinished(): void {
|
|
297
|
-
const fn = this._onRunFinished;
|
|
298
|
-
this._onRunFinished = undefined;
|
|
299
|
-
fn?.();
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/** Complete a run: release listeners, worktree cleanup, status transition, execution update, fire onRunFinished. */
|
|
303
|
-
completeRun(result: RunResult, worktrees: WorktreeManager): void {
|
|
429
|
+
/** Complete a run: release listeners, worktree cleanup, status transition, execution update, notify observer. */
|
|
430
|
+
completeRun(result: RunResult): void {
|
|
304
431
|
this.releaseListeners();
|
|
305
432
|
|
|
306
433
|
let finalResult = result.responseText;
|
|
307
|
-
if (this.worktreeState) {
|
|
308
|
-
const wtResult = this.worktreeState.performCleanup(
|
|
434
|
+
if (this.worktreeState && this._worktrees) {
|
|
435
|
+
const wtResult = this.worktreeState.performCleanup(this._worktrees, this.description);
|
|
309
436
|
if (wtResult.hasChanges && wtResult.branch) {
|
|
310
437
|
finalResult += `\n\n---\nChanges saved to branch \`${wtResult.branch}\`. Merge with: \`git merge ${wtResult.branch}\``;
|
|
311
438
|
}
|
|
@@ -320,20 +447,20 @@ export class Agent {
|
|
|
320
447
|
outputFile: result.sessionFile ?? this.execution?.outputFile,
|
|
321
448
|
};
|
|
322
449
|
|
|
323
|
-
this.
|
|
450
|
+
this.observer?.onRunFinished?.(this);
|
|
324
451
|
}
|
|
325
452
|
|
|
326
|
-
/** Fail a run: mark error, release listeners, best-effort worktree cleanup,
|
|
327
|
-
failRun(err: unknown
|
|
453
|
+
/** Fail a run: mark error, release listeners, best-effort worktree cleanup, notify observer. */
|
|
454
|
+
failRun(err: unknown): void {
|
|
328
455
|
this.markError(err);
|
|
329
456
|
this.releaseListeners();
|
|
330
457
|
|
|
331
|
-
if (this.worktreeState) {
|
|
458
|
+
if (this.worktreeState && this._worktrees) {
|
|
332
459
|
try {
|
|
333
|
-
this.worktreeState.performCleanup(
|
|
460
|
+
this.worktreeState.performCleanup(this._worktrees, this.description);
|
|
334
461
|
} catch (cleanupErr) { debugLog("cleanupWorktree on agent error", cleanupErr); }
|
|
335
462
|
}
|
|
336
463
|
|
|
337
|
-
this.
|
|
464
|
+
this.observer?.onRunFinished?.(this);
|
|
338
465
|
}
|
|
339
466
|
}
|
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { Agent } from "#src/lifecycle/agent";
|
|
9
|
-
import type { CompactionInfo } from "#src/
|
|
10
|
-
import type { SubscribableSession } from "#src/types";
|
|
9
|
+
import type { CompactionInfo, SubscribableSession } from "#src/types";
|
|
11
10
|
|
|
12
11
|
export interface AgentObserverOptions {
|
|
13
12
|
onCompact?: (record: Agent, info: CompactionInfo) => void;
|
package/src/tools/agent-tool.ts
CHANGED
|
@@ -4,14 +4,14 @@ import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
|
4
4
|
import { Text } from "@earendil-works/pi-tui";
|
|
5
5
|
import { Type } from "@sinclair/typebox";
|
|
6
6
|
import { AgentTypeRegistry } from "#src/config/agent-types";
|
|
7
|
-
import type { AgentSpawnConfig
|
|
7
|
+
import type { AgentSpawnConfig } from "#src/lifecycle/agent-manager";
|
|
8
8
|
import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
|
|
9
9
|
import { spawnBackground } from "#src/tools/background-spawner";
|
|
10
10
|
import { runForeground } from "#src/tools/foreground-runner";
|
|
11
11
|
import { buildDetails, buildTypeListText, textResult } from "#src/tools/helpers";
|
|
12
12
|
import { renderAgentResult } from "#src/tools/result-renderer";
|
|
13
13
|
import { type ModelInfo, resolveSpawnConfig } from "#src/tools/spawn-config";
|
|
14
|
-
import type { Agent } from "#src/types";
|
|
14
|
+
import type { Agent, ParentSessionInfo } from "#src/types";
|
|
15
15
|
import { AgentActivityTracker } from "#src/ui/agent-activity-tracker";
|
|
16
16
|
import { type UICtx } from "#src/ui/agent-widget";
|
|
17
17
|
import { type AgentDetails, getDisplayName } from "#src/ui/display";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { AgentSpawnConfig
|
|
1
|
+
import type { AgentSpawnConfig } from "#src/lifecycle/agent-manager";
|
|
2
2
|
import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
|
|
3
3
|
import type { AgentActivityAccess } from "#src/tools/agent-tool";
|
|
4
4
|
import { textResult } from "#src/tools/helpers";
|
|
5
5
|
import type { ResolvedSpawnConfig } from "#src/tools/spawn-config";
|
|
6
|
-
import type { Agent } from "#src/types";
|
|
6
|
+
import type { Agent, ParentSessionInfo } from "#src/types";
|
|
7
7
|
import { AgentActivityTracker } from "#src/ui/agent-activity-tracker";
|
|
8
8
|
import { subscribeUIObserver } from "#src/ui/ui-observer";
|
|
9
9
|
|
|
@@ -54,9 +54,11 @@ export function spawnBackground(
|
|
|
54
54
|
isBackground: true,
|
|
55
55
|
isolation: execution.isolation,
|
|
56
56
|
invocation: execution.agentInvocation,
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
observer: {
|
|
58
|
+
onSessionCreated: (_agent, session) => {
|
|
59
|
+
bgState.setSession(session);
|
|
60
|
+
subscribeUIObserver(session, bgState);
|
|
61
|
+
},
|
|
60
62
|
},
|
|
61
63
|
});
|
|
62
64
|
} catch (err) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentToolResult } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import type { AgentSpawnConfig
|
|
2
|
+
import type { AgentSpawnConfig } from "#src/lifecycle/agent-manager";
|
|
3
3
|
import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
|
|
4
4
|
import type { AgentActivityAccess } from "#src/tools/agent-tool";
|
|
5
5
|
import {
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
textResult,
|
|
10
10
|
} from "#src/tools/helpers";
|
|
11
11
|
import type { ResolvedSpawnConfig } from "#src/tools/spawn-config";
|
|
12
|
-
import type { Agent } from "#src/types";
|
|
12
|
+
import type { Agent, ParentSessionInfo } from "#src/types";
|
|
13
13
|
import { AgentActivityTracker } from "#src/ui/agent-activity-tracker";
|
|
14
14
|
import {
|
|
15
15
|
type AgentDetails,
|
|
@@ -109,13 +109,15 @@ export async function runForeground(
|
|
|
109
109
|
invocation: execution.agentInvocation,
|
|
110
110
|
signal,
|
|
111
111
|
parentSession: params.parentSession,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
112
|
+
observer: {
|
|
113
|
+
onSessionCreated: (agent, session) => {
|
|
114
|
+
fgState.setSession(session);
|
|
115
|
+
recordRef = agent;
|
|
116
|
+
unsubUI = subscribeUIObserver(session, fgState, streamUpdate);
|
|
117
|
+
fgId = agent.id;
|
|
118
|
+
agentActivity.set(agent.id, fgState);
|
|
119
|
+
widget.ensureTimer();
|
|
120
|
+
},
|
|
119
121
|
},
|
|
120
122
|
},
|
|
121
123
|
);
|
package/src/types.ts
CHANGED
|
@@ -105,3 +105,16 @@ export type ShellExec = (
|
|
|
105
105
|
args: string[],
|
|
106
106
|
options?: { cwd?: string; timeout?: number },
|
|
107
107
|
) => Promise<{ stdout: string; stderr: string; code: number }>;
|
|
108
|
+
|
|
109
|
+
/** Parent session identity — grouped fields that travel together from the tool boundary. */
|
|
110
|
+
export interface ParentSessionInfo {
|
|
111
|
+
/** Path to the parent session's JSONL file (for deriving the subagent session directory). */
|
|
112
|
+
parentSessionFile?: string;
|
|
113
|
+
/** Session ID of the parent agent (stored in the child session's parentSession header). */
|
|
114
|
+
parentSessionId?: string;
|
|
115
|
+
/** Tool call ID for background notification wiring. When set, spawn attaches NotificationState. */
|
|
116
|
+
toolCallId?: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Compaction event info passed through lifecycle observers. */
|
|
120
|
+
export type CompactionInfo = { reason: "manual" | "threshold" | "overflow"; tokensBefore: number };
|