@esso0428/pi-subagents 0.17.0 → 0.17.2

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 CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.17.2] - 2026-09-15
11
+
12
+ ### Fixed
13
+ - **Agents navigator selection now survives returning to the text input**, restoring the selected agent and bounded viewport on re-entry; if a record disappears, the nearest valid roster position is used.
14
+ - **Running agents and Agent history menus retain their independent selected rows**, and closing a conversation viewer returns to the originating submenu instead of the top-level Agents menu.
15
+ - **Durable transcript results are reloaded on demand after in-memory cleanup**, and full results are no longer duplicated into the parent session branch when a durable transcript exists, reducing long-session heap growth.
16
+
17
+ ## [0.17.1] - 2026-09-15
18
+
19
+ ### Fixed
20
+ - **The Agents panel now initializes during TUI session startup after restoring persisted agent records**, so openable completed-agent history is visible immediately when a session starts or resumes instead of waiting for the next tool execution.
21
+
10
22
  ## [0.17.0] - 2026-09-15
11
23
 
12
24
  ### Changed
package/README.md CHANGED
@@ -19,6 +19,7 @@ https://github.com/user-attachments/assets/8685261b-9338-4fea-8dfe-1c590d5df543
19
19
  - **Custom agent types** — define agents in `.pi/agents/<name>.md` or `.agents/agents/<name>.md` (project) or globally, with YAML frontmatter: custom system prompts, model selection, thinking levels, tool restrictions
20
20
  - **Mid-run steering** — inject messages into running agents to redirect their work without restarting
21
21
  - **Session resume** — pick up where an agent left off, preserving full conversation context
22
+ - **Durable interruption recovery** — catchable shutdowns preserve running/queued metadata and partial transcripts so interrupted agents remain indexed after reload; abrupt `SIGKILL` termination cannot be checkpointed
22
23
  - **Graceful turn limits** — agents get a "wrap up" warning before hard abort, producing clean partial results instead of cut-off output
23
24
  - **Case-insensitive agent types** — `"explore"`, `"Explore"`, `"EXPLORE"` all work. Unknown types fall back to general-purpose with a note
24
25
  - **Fuzzy model selection** — specify models by name (`"haiku"`, `"sonnet"`) instead of full IDs, with automatic filtering to only available/configured models
@@ -94,7 +95,7 @@ Restrictions:
94
95
 
95
96
  ## UI
96
97
 
97
- The extension renders a persistent Agents widget above the editor. By default it shows all agents (`widgetMode: all`), including foreground and background runs. Switch to `background` or `off` via `/agents → Settings → Widget`:
98
+ The extension renders a persistent Agents widget above the editor. It initializes during TUI `session_start`, after the session branch restores completed-agent history, so openable agents are visible immediately when a session starts or resumes. By default it shows all agents (`widgetMode: all`), including foreground and background runs. Switch to `background` or `off` via `/agents → Settings → Widget`:
98
99
 
99
100
  ```
100
101
  ● Agents
@@ -123,7 +124,7 @@ While agents are running or have openable history, the single Agents panel rende
123
124
  └─ +1 more (↓ 1 more; 1 finished)
124
125
  ```
125
126
 
126
- At an **empty prompt**, press `↓` to activate the panel. The selected row is marked `●`; `↑`/`↓` move through the full bounded roster, `Enter` opens a running agent live or a terminal agent's read-only history. A queued agent remains selectable, but has no live viewer until it starts; `Esc` returns to the prompt. `↑` while the first row is selected also returns to the prompt. Pressing `↑` at an inactive prompt, typing in a non-empty prompt, or using `j`/`k`/`←` behaves normally; the panel only captures the focus-gated arrow sequence described above. Disable it entirely via `/agents → Settings → Widget → off`.
127
+ At an **empty prompt**, press `↓` to activate the panel. The selected row is marked `●`; `↑`/`↓` move through the full bounded roster, `Enter` opens a running agent live or a terminal agent's read-only history. A queued or interrupted agent remains selectable, and an interrupted agent's partial transcript can be opened read-only after reload. The widget remembers the selected agent and bounded viewport when you return from the prompt or a viewer; if that record disappears, it falls back to the nearest valid row. `Esc` returns to the prompt. `↑` while the first row is selected also returns to the prompt. Pressing `↑` at an inactive prompt, typing in a non-empty prompt, or using `j`/`k`/`←` behaves normally; the panel only captures the focus-gated arrow sequence described above. Disable it entirely via `/agents → Settings → Widget → off`.
127
128
 
128
129
  Individual agent results render Claude Code-style in the conversation:
129
130
 
@@ -22,4 +22,10 @@ export declare function hasAgentHistory(cwd: string, locator: string | undefined
22
22
  * damaged entry cannot hide the rest of a history.
23
23
  */
24
24
  export declare function readAgentHistory(cwd: string, locator: string): AgentSession["messages"] | undefined;
25
+ /**
26
+ * Read the last assistant text from a durable transcript. Runtime records may
27
+ * release their in-memory result after the retention TTL; the transcript then
28
+ * becomes the source of truth for get_subagent_result.
29
+ */
30
+ export declare function readAgentHistoryResult(cwd: string, locator: string): string | undefined;
25
31
  //# sourceMappingURL=agent-history.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-history.d.ts","sourceRoot":"","sources":["../src/agent-history.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAI/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAMpE;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAe5D;AAED,uDAAuD;AACvD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAM3E;AAED,4EAA4E;AAC5E,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAE5E;AAED,+EAA+E;AAC/E,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAWxF;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAIjF;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,SAAS,CA2BnG"}
1
+ {"version":3,"file":"agent-history.d.ts","sourceRoot":"","sources":["../src/agent-history.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAI/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAMpE;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAe5D;AAED,uDAAuD;AACvD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAM3E;AAED,4EAA4E;AAC5E,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAE5E;AAED,+EAA+E;AAC/E,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAWxF;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAIjF;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,SAAS,CA2BnG;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CA+BvF"}
@@ -101,4 +101,42 @@ export function readAgentHistory(cwd, locator) {
101
101
  }
102
102
  return messages.length > 0 ? messages : undefined;
103
103
  }
104
+ /**
105
+ * Read the last assistant text from a durable transcript. Runtime records may
106
+ * release their in-memory result after the retention TTL; the transcript then
107
+ * becomes the source of truth for get_subagent_result.
108
+ */
109
+ export function readAgentHistoryResult(cwd, locator) {
110
+ const messages = readAgentHistory(cwd, locator);
111
+ if (!messages)
112
+ return undefined;
113
+ for (let i = messages.length - 1; i >= 0; i--) {
114
+ const message = messages[i];
115
+ if (message.role !== "assistant")
116
+ continue;
117
+ // AgentSession types currently model content as an array, but the
118
+ // persisted JSONL format may also contain a plain string. Cross the
119
+ // runtime/parser boundary through unknown so both shapes remain safe.
120
+ const content = message.content;
121
+ if (typeof content === "string") {
122
+ const text = content.trim();
123
+ if (text)
124
+ return text;
125
+ continue;
126
+ }
127
+ if (!Array.isArray(content))
128
+ continue;
129
+ const text = content
130
+ .filter((part) => typeof part === "object"
131
+ && part !== null
132
+ && part.type === "text"
133
+ && typeof part.text === "string")
134
+ .map((part) => part.text)
135
+ .join("\n")
136
+ .trim();
137
+ if (text)
138
+ return text;
139
+ }
140
+ return undefined;
141
+ }
104
142
  //# sourceMappingURL=agent-history.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-history.js","sourceRoot":"","sources":["../src/agent-history.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-history.js","sourceRoot":"","sources":["../src/agent-history.ts"],"names":[],"mappings":"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"}
@@ -81,6 +81,8 @@ interface SpawnOptions {
81
81
  }) => void;
82
82
  /** Called when the session successfully compacts. */
83
83
  onCompaction?: (info: CompactionInfo) => void;
84
+ /** Called synchronously after the record exists, before it is queued or started. */
85
+ onSpawned?: (id: string) => void;
84
86
  }
85
87
  export declare class AgentManager {
86
88
  private agents;
@@ -92,14 +94,35 @@ export declare class AgentManager {
92
94
  /** Base repos worktrees were created from — so dispose() can prune them all,
93
95
  * not just the parent repo (caller-supplied cwd can target other repos). */
94
96
  private worktreeRepos;
97
+ /** Project cwd for each record's durable checkpoint. */
98
+ private recoveryCwds;
95
99
  /** Queue of background agents waiting to start. */
96
100
  private queue;
97
101
  /** Number of currently running background agents. */
98
102
  private runningBackground;
103
+ /** Prevent late promise settlement from decrementing a replacement run. */
104
+ private runningBackgroundIds;
99
105
  constructor(onComplete?: OnAgentComplete, maxConcurrent?: number, onStart?: OnAgentStart, onCompact?: OnAgentCompact);
100
106
  /** Update the max concurrent background agents limit. */
101
107
  setMaxConcurrent(n: number): void;
102
108
  getMaxConcurrent(): number;
109
+ private finishBackground;
110
+ private checkpointStatus;
111
+ private makeCheckpoint;
112
+ private checkpoint;
113
+ private flushOutput;
114
+ /** Set the durable transcript locator and checkpoint the current state. */
115
+ setTranscript(id: string, historyFile: string, transcriptPath: string, cwd?: string): void;
116
+ /** Checkpoint one record explicitly (used after transcript setup). */
117
+ checkpointRecord(id: string): void;
118
+ /**
119
+ * Reload durable records from this project's checkpoint directory. A
120
+ * running/queued checkpoint means the process was killed before it could
121
+ * write its stopped state; treat it as stopped and retain its transcript.
122
+ * SIGKILL cannot run a final flush/checkpoint, so this active snapshot is
123
+ * necessarily the last recoverable state.
124
+ */
125
+ restoreRecovered(cwd: string): void;
103
126
  /**
104
127
  * Spawn an agent and return its ID immediately (for background use).
105
128
  * If the concurrency limit is reached, the agent is queued.
@@ -109,12 +132,6 @@ export declare class AgentManager {
109
132
  private startAgent;
110
133
  /** Start queued agents up to the concurrency limit. */
111
134
  private drainQueue;
112
- /**
113
- * Called synchronously right after spawn, before onSessionCreated fires.
114
- * Lets the caller set up the output file path on the record.
115
- * The record is guaranteed to be in this.agents at this point.
116
- */
117
- private onSpawned?;
118
135
  /**
119
136
  * Spawn an agent and wait for completion (foreground use).
120
137
  * Foreground agents bypass the concurrency queue.
@@ -1 +1 @@
1
- {"version":3,"file":"agent-manager.d.ts","sourceRoot":"","sources":["../src/agent-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-manager.d.ts","sourceRoot":"","sources":["../src/agent-manager.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,8 @@
8
8
  import { randomUUID } from "node:crypto";
9
9
  import { statSync } from "node:fs";
10
10
  import { isAbsolute } from "node:path";
11
+ import { readAgentHistory } from "./agent-history.js";
12
+ import { readAgentRecoveryCheckpoints, removeAgentRecoveryCheckpoint, writeAgentRecoveryCheckpoint, } from "./agent-recovery.js";
11
13
  import { resumeAgent, runAgent } from "./agent-runner.js";
12
14
  import { addUsage } from "./usage.js";
13
15
  import { cleanupWorktree, createWorktree, pruneWorktrees, } from "./worktree.js";
@@ -134,10 +136,14 @@ export class AgentManager {
134
136
  /** Base repos worktrees were created from — so dispose() can prune them all,
135
137
  * not just the parent repo (caller-supplied cwd can target other repos). */
136
138
  worktreeRepos = new Set();
139
+ /** Project cwd for each record's durable checkpoint. */
140
+ recoveryCwds = new Map();
137
141
  /** Queue of background agents waiting to start. */
138
142
  queue = [];
139
143
  /** Number of currently running background agents. */
140
144
  runningBackground = 0;
145
+ /** Prevent late promise settlement from decrementing a replacement run. */
146
+ runningBackgroundIds = new Set();
141
147
  constructor(onComplete, maxConcurrent = DEFAULT_MAX_CONCURRENT, onStart, onCompact) {
142
148
  this.onComplete = onComplete;
143
149
  this.onStart = onStart;
@@ -156,6 +162,101 @@ export class AgentManager {
156
162
  getMaxConcurrent() {
157
163
  return this.maxConcurrent;
158
164
  }
165
+ finishBackground(id) {
166
+ if (!this.runningBackgroundIds.delete(id))
167
+ return;
168
+ this.runningBackground = Math.max(0, this.runningBackground - 1);
169
+ }
170
+ checkpointStatus(record) {
171
+ return record.status;
172
+ }
173
+ makeCheckpoint(record) {
174
+ const checkpoint = {
175
+ version: 1,
176
+ id: record.id,
177
+ type: record.type,
178
+ description: record.description,
179
+ status: this.checkpointStatus(record),
180
+ startedAt: record.startedAt,
181
+ toolUses: record.toolUses,
182
+ lifetimeUsage: { ...record.lifetimeUsage },
183
+ compactionCount: record.compactionCount,
184
+ ...(record.completedAt !== undefined && { completedAt: record.completedAt }),
185
+ // A durable transcript is the source of truth for partial/full output.
186
+ // Avoid duplicating potentially sensitive or very large result text.
187
+ ...(!record.transcriptPath && record.result !== undefined && { result: record.result }),
188
+ ...(record.error !== undefined && { error: record.error }),
189
+ ...(record.transcriptPath !== undefined && { transcriptPath: record.transcriptPath }),
190
+ ...(record.invocation !== undefined && { invocation: cloneInvocation(record.invocation) }),
191
+ };
192
+ return checkpoint;
193
+ }
194
+ checkpoint(record) {
195
+ const cwd = this.recoveryCwds.get(record.id);
196
+ if (!cwd)
197
+ return;
198
+ writeAgentRecoveryCheckpoint(cwd, this.makeCheckpoint(record));
199
+ }
200
+ flushOutput(record) {
201
+ if (!record.outputCleanup)
202
+ return;
203
+ try {
204
+ record.outputCleanup();
205
+ }
206
+ catch { /* recovery must remain best effort */ }
207
+ record.outputCleanup = undefined;
208
+ }
209
+ /** Set the durable transcript locator and checkpoint the current state. */
210
+ setTranscript(id, historyFile, transcriptPath, cwd) {
211
+ const record = this.agents.get(id);
212
+ if (!record)
213
+ return;
214
+ record.historyFile = historyFile;
215
+ record.transcriptPath = transcriptPath;
216
+ if (cwd)
217
+ this.recoveryCwds.set(id, cwd);
218
+ this.checkpoint(record);
219
+ }
220
+ /** Checkpoint one record explicitly (used after transcript setup). */
221
+ checkpointRecord(id) {
222
+ const record = this.agents.get(id);
223
+ if (record)
224
+ this.checkpoint(record);
225
+ }
226
+ /**
227
+ * Reload durable records from this project's checkpoint directory. A
228
+ * running/queued checkpoint means the process was killed before it could
229
+ * write its stopped state; treat it as stopped and retain its transcript.
230
+ * SIGKILL cannot run a final flush/checkpoint, so this active snapshot is
231
+ * necessarily the last recoverable state.
232
+ */
233
+ restoreRecovered(cwd) {
234
+ for (const checkpoint of readAgentRecoveryCheckpoints(cwd)) {
235
+ if (!checkpoint.transcriptPath || !readAgentHistory(cwd, checkpoint.transcriptPath))
236
+ continue;
237
+ const status = checkpoint.status === "running" || checkpoint.status === "queued"
238
+ ? "stopped"
239
+ : checkpoint.status;
240
+ const completedAt = checkpoint.completedAt ?? Date.now();
241
+ const existing = this.agents.get(checkpoint.id);
242
+ if (existing) {
243
+ // Parent-branch records can still carry an unread in-memory result.
244
+ // Never replace that richer record with the checkpoint's transcript
245
+ // stub during the same session. Merge only durable locator metadata.
246
+ if (!existing.transcriptPath && checkpoint.transcriptPath) {
247
+ existing.transcriptPath = checkpoint.transcriptPath;
248
+ }
249
+ this.recoveryCwds.set(checkpoint.id, cwd);
250
+ continue;
251
+ }
252
+ this.agents.set(checkpoint.id, this.createRestoredRecord({
253
+ ...checkpoint,
254
+ status,
255
+ completedAt,
256
+ }));
257
+ this.recoveryCwds.set(checkpoint.id, cwd);
258
+ }
259
+ }
159
260
  /**
160
261
  * Spawn an agent and return its ID immediately (for background use).
161
262
  * If the concurrency limit is reached, the agent is queued.
@@ -186,6 +287,20 @@ export class AgentManager {
186
287
  invocation: options.invocation,
187
288
  };
188
289
  this.agents.set(id, record);
290
+ this.recoveryCwds.set(id, ctx.cwd);
291
+ // Give callers a chance to create the durable transcript before the first
292
+ // checkpoint. This closes the small spawn→attach window in which a queued
293
+ // or running agent could be left recoverable only as metadata.
294
+ try {
295
+ options.onSpawned?.(id);
296
+ this.checkpoint(record);
297
+ }
298
+ catch (err) {
299
+ this.agents.delete(id);
300
+ this.recoveryCwds.delete(id);
301
+ removeAgentRecoveryCheckpoint(ctx.cwd, id);
302
+ throw err;
303
+ }
189
304
  const args = { pi, ctx, type, prompt, options };
190
305
  if (options.isBackground && !options.bypassQueue && this.runningBackground >= this.maxConcurrent) {
191
306
  // Queue it — will be started when a running agent completes
@@ -199,6 +314,8 @@ export class AgentManager {
199
314
  }
200
315
  catch (err) {
201
316
  this.agents.delete(id);
317
+ this.recoveryCwds.delete(id);
318
+ removeAgentRecoveryCheckpoint(ctx.cwd, id);
202
319
  throw err;
203
320
  }
204
321
  return id;
@@ -235,8 +352,11 @@ export class AgentManager {
235
352
  }
236
353
  record.status = "running";
237
354
  record.startedAt = Date.now();
238
- if (options.isBackground)
355
+ this.checkpoint(record);
356
+ if (options.isBackground) {
239
357
  this.runningBackground++;
358
+ this.runningBackgroundIds.add(id);
359
+ }
240
360
  this.onStart?.(record);
241
361
  // Wire parent abort signal to stop the subagent when the parent is interrupted
242
362
  let detachParentSignal;
@@ -294,9 +414,14 @@ export class AgentManager {
294
414
  record.pendingSteers = undefined;
295
415
  }
296
416
  options.onSessionCreated?.(session);
417
+ this.checkpoint(record);
297
418
  },
298
419
  })
299
420
  .then(({ responseText, session, aborted, steered, failure }) => {
421
+ // A disposed manager no longer owns this run. Avoid late callbacks
422
+ // mutating a dead session or emitting completion side effects.
423
+ if (this.agents.get(id) !== record)
424
+ return responseText;
300
425
  // Don't overwrite status if externally stopped via abort()
301
426
  if (record.status !== "stopped") {
302
427
  // Precedence: a hard abort keeps "aborted"; then a failed final turn
@@ -337,6 +462,7 @@ export class AgentManager {
337
462
  `\n\n---\nChanges saved to branch \`${wtResult.branch}\`${repoNote}. Merge with: \`git merge ${wtResult.branch}\`${customCwd !== undefined ? ` (run in \`${baseCwd}\`)` : ""}`;
338
463
  }
339
464
  }
465
+ this.checkpoint(record);
340
466
  // Fire onComplete for foreground agents too — lifecycle symmetry.
341
467
  // Mark resultConsumed so the callback skips notifications (result returned inline).
342
468
  if (!options.isBackground) {
@@ -347,7 +473,7 @@ export class AgentManager {
347
473
  catch { /* ignore completion side-effect errors */ }
348
474
  }
349
475
  else {
350
- this.runningBackground--;
476
+ this.finishBackground(id);
351
477
  try {
352
478
  this.onComplete?.(record);
353
479
  }
@@ -357,6 +483,10 @@ export class AgentManager {
357
483
  return responseText;
358
484
  })
359
485
  .catch((err) => {
486
+ // A disposed manager no longer owns this run. Avoid late callbacks
487
+ // mutating a dead session or emitting completion side effects.
488
+ if (this.agents.get(id) !== record)
489
+ return "";
360
490
  // Don't overwrite status if externally stopped via abort()
361
491
  if (record.status !== "stopped") {
362
492
  record.status = "error";
@@ -380,6 +510,7 @@ export class AgentManager {
380
510
  }
381
511
  catch { /* ignore cleanup errors */ }
382
512
  }
513
+ this.checkpoint(record);
383
514
  // Fire onComplete for foreground agents too — lifecycle symmetry.
384
515
  // Mark resultConsumed so the callback skips notifications (result returned inline).
385
516
  if (!options.isBackground) {
@@ -387,17 +518,13 @@ export class AgentManager {
387
518
  this.onComplete?.(record);
388
519
  }
389
520
  else {
390
- this.runningBackground--;
521
+ this.finishBackground(id);
391
522
  this.onComplete?.(record);
392
523
  this.drainQueue();
393
524
  }
394
525
  return "";
395
526
  });
396
527
  record.promise = promise;
397
- // Notify caller that spawn is complete (record is in the map, promise is set).
398
- // Called synchronously — onSessionCreated fires asynchronously inside runAgent.
399
- // Used by spawnAndWait to let the caller set up output files before streaming starts.
400
- this.onSpawned?.(id);
401
528
  }
402
529
  /** Start queued agents up to the concurrency limit. */
403
530
  drainQueue() {
@@ -415,16 +542,11 @@ export class AgentManager {
415
542
  record.status = "error";
416
543
  record.error = err instanceof Error ? err.message : String(err);
417
544
  record.completedAt = Date.now();
545
+ this.checkpoint(record);
418
546
  this.onComplete?.(record);
419
547
  }
420
548
  }
421
549
  }
422
- /**
423
- * Called synchronously right after spawn, before onSessionCreated fires.
424
- * Lets the caller set up the output file path on the record.
425
- * The record is guaranteed to be in this.agents at this point.
426
- */
427
- onSpawned;
428
550
  /**
429
551
  * Spawn an agent and wait for completion (foreground use).
430
552
  * Foreground agents bypass the concurrency queue.
@@ -434,18 +556,14 @@ export class AgentManager {
434
556
  * Use this to set record.outputFile so streamToOutputFile can pick it up.
435
557
  */
436
558
  async spawnAndWait(pi, ctx, type, prompt, options, onSpawned) {
437
- // Temporarily register the onSpawned hook so startAgent can call it.
438
- const prevOnSpawned = this.onSpawned;
439
- this.onSpawned = onSpawned;
440
- try {
441
- const id = this.spawn(pi, ctx, type, prompt, { ...options, isBackground: false });
442
- const record = this.agents.get(id);
443
- await record.promise;
444
- return { id, record };
445
- }
446
- finally {
447
- this.onSpawned = prevOnSpawned;
448
- }
559
+ const id = this.spawn(pi, ctx, type, prompt, {
560
+ ...options,
561
+ isBackground: false,
562
+ onSpawned,
563
+ });
564
+ const record = this.agents.get(id);
565
+ await record.promise;
566
+ return { id, record };
449
567
  }
450
568
  /**
451
569
  * Resume an existing agent session with a new prompt.
@@ -465,6 +583,7 @@ export class AgentManager {
465
583
  ...(resumedModel && { effectiveModelName: resumedModel.name ?? resumedModel.id }),
466
584
  effectiveThinking: record.session.thinkingLevel,
467
585
  };
586
+ this.checkpoint(record);
468
587
  try {
469
588
  const { text, failure } = await resumeAgent(record.session, prompt, {
470
589
  onToolActivity: (activity) => {
@@ -487,11 +606,13 @@ export class AgentManager {
487
606
  record.error = failure;
488
607
  record.result = text;
489
608
  record.completedAt = Date.now();
609
+ this.checkpoint(record);
490
610
  }
491
611
  catch (err) {
492
612
  record.status = "error";
493
613
  record.error = err instanceof Error ? err.message : String(err);
494
614
  record.completedAt = Date.now();
615
+ this.checkpoint(record);
495
616
  }
496
617
  return record;
497
618
  }
@@ -556,7 +677,7 @@ export class AgentManager {
556
677
  lifetimeUsage: record.lifetimeUsage
557
678
  ? { ...record.lifetimeUsage }
558
679
  : { input: 0, output: 0, cacheWrite: 0 },
559
- compactionCount: 0,
680
+ compactionCount: record.compactionCount ?? 0,
560
681
  };
561
682
  }
562
683
  abort(id) {
@@ -568,6 +689,7 @@ export class AgentManager {
568
689
  this.queue = this.queue.filter(q => q.id !== id);
569
690
  record.status = "stopped";
570
691
  record.completedAt = Date.now();
692
+ this.checkpoint(record);
571
693
  return true;
572
694
  }
573
695
  if (record.status !== "running")
@@ -575,6 +697,10 @@ export class AgentManager {
575
697
  record.abortController?.abort();
576
698
  record.status = "stopped";
577
699
  record.completedAt = Date.now();
700
+ this.flushOutput(record);
701
+ this.checkpoint(record);
702
+ this.finishBackground(id);
703
+ this.drainQueue();
578
704
  return true;
579
705
  }
580
706
  /** Dispose a record's session and remove it from the map. */
@@ -607,6 +733,10 @@ export class AgentManager {
607
733
  record.outputCleanup = undefined;
608
734
  record.outputFile = undefined;
609
735
  record.historyFile = undefined;
736
+ // The durable transcript is the source of truth after the TTL. Keep
737
+ // only the small identity/status record in memory; get_subagent_result
738
+ // reloads the final answer from transcriptPath on demand.
739
+ record.result = undefined;
610
740
  continue;
611
741
  }
612
742
  this.removeRecord(id, record);
@@ -640,16 +770,21 @@ export class AgentManager {
640
770
  if (record) {
641
771
  record.status = "stopped";
642
772
  record.completedAt = Date.now();
773
+ this.checkpoint(record);
643
774
  count++;
644
775
  }
645
776
  }
646
777
  this.queue = [];
647
- // Abort running agents
778
+ // Abort running agents. Flush before checkpointing so a catchable
779
+ // shutdown/session switch leaves the latest assistant message available.
648
780
  for (const record of this.agents.values()) {
649
781
  if (record.status === "running") {
650
782
  record.abortController?.abort();
651
783
  record.status = "stopped";
652
784
  record.completedAt = Date.now();
785
+ this.flushOutput(record);
786
+ this.finishBackground(record.id);
787
+ this.checkpoint(record);
653
788
  count++;
654
789
  }
655
790
  }
@@ -678,6 +813,9 @@ export class AgentManager {
678
813
  record.session?.dispose();
679
814
  }
680
815
  this.agents.clear();
816
+ this.recoveryCwds.clear();
817
+ this.runningBackgroundIds.clear();
818
+ this.runningBackground = 0;
681
819
  // Prune any orphaned git worktrees (crash recovery)
682
820
  try {
683
821
  pruneWorktrees(process.cwd());