@ferris1225/pi-subagents 1.0.0 → 1.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/README.md +18 -14
- package/agents/reviewer.md +2 -0
- package/package.json +1 -1
- package/src/agents.ts +2 -7
- package/src/announcements.ts +4 -1
- package/src/completion.ts +7 -36
- package/src/config.ts +327 -364
- package/src/dispatch.ts +47 -198
- package/src/fixloop.ts +0 -16
- package/src/format.ts +2 -7
- package/src/index.ts +5 -2
- package/src/models.ts +0 -10
- package/src/monitor.ts +46 -98
- package/src/rpc-run.ts +1 -15
- package/src/runtime.ts +3 -15
- package/src/session-fork.ts +0 -4
- package/src/spawn.ts +542 -557
- package/src/tools.ts +5 -29
- package/src/ui.ts +3 -7
- package/src/widget.ts +107 -0
- package/src/worktree.ts +1 -1
- package/src/trajectory.ts +0 -312
package/src/monitor.ts
CHANGED
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
import { stripVTControlCharacters } from "node:util";
|
|
12
12
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
13
13
|
import { visibleWidth } from "@earendil-works/pi-tui";
|
|
14
|
-
import type
|
|
15
|
-
import { redactSensitiveText } from "./trajectory.ts";
|
|
14
|
+
import { emptyUsage, type UsageStats } from "./rpc-run.ts";
|
|
16
15
|
import type { IsolationMode, WorktreeFinalizationStatus } from "./worktree.ts";
|
|
17
16
|
|
|
18
17
|
// ---------------------------------------------------------------------------
|
|
@@ -46,14 +45,6 @@ export interface RunView {
|
|
|
46
45
|
usage: UsageStats;
|
|
47
46
|
/** Concise current activity ("thinking", "read src/index.ts"); last writer wins. */
|
|
48
47
|
activity?: string;
|
|
49
|
-
/** Total tool calls started by the run so far (a progress signal). */
|
|
50
|
-
toolCount?: number;
|
|
51
|
-
/** Tool currently executing (set on tool_start, cleared on tool_end). When set,
|
|
52
|
-
* the run is NOT idle for needs-attention purposes. */
|
|
53
|
-
currentTool?: string;
|
|
54
|
-
/** Epoch ms of the last live activity (tool, usage, status). Used to derive
|
|
55
|
-
* the needs-attention state: no tool running AND now - lastActivityAt > threshold. */
|
|
56
|
-
lastActivityAt?: number;
|
|
57
48
|
/** Epoch ms when the run started executing (set on first "running" status). */
|
|
58
49
|
startedAt?: number;
|
|
59
50
|
/** Epoch ms when the run finished (set on "done"/"failed"). */
|
|
@@ -62,16 +53,6 @@ export interface RunView {
|
|
|
62
53
|
groupId?: string;
|
|
63
54
|
/** Human-readable role within a chain, e.g. "fix round 1" or "re-review round 1". */
|
|
64
55
|
relationLabel?: string;
|
|
65
|
-
/** Free-form orchestration note (e.g. "auto-fix chain running"). */
|
|
66
|
-
annotation?: string;
|
|
67
|
-
/** One-line outcome summary of a finished chain run: a reviewer reports its verdict
|
|
68
|
-
* plus key fragments of what it found ("fail · src/index.ts · render()"), a
|
|
69
|
-
* worker the fragments of what it changed. Unset for non-chain runs. */
|
|
70
|
-
summary?: string;
|
|
71
|
-
/** True when a finished run remains in monitor state (e.g. an auto-fix
|
|
72
|
-
* parent whose chain is still running). beginTurn preserves
|
|
73
|
-
* retained runs so they are not swept between turns. */
|
|
74
|
-
retained?: boolean;
|
|
75
56
|
}
|
|
76
57
|
|
|
77
58
|
/** Optional chain metadata for runs spawned by an auto-fix loop. */
|
|
@@ -280,18 +261,40 @@ export function formatDuration(ms: number): string {
|
|
|
280
261
|
/** Elapsed wall time of a run: live while running, final once finished. */
|
|
281
262
|
export function formatElapsed(run: RunView, now: number = Date.now()): string {
|
|
282
263
|
if (run.startedAt === undefined) return "";
|
|
283
|
-
|
|
284
|
-
// must keep ticking: its `endedAt` was stamped when the review itself
|
|
285
|
-
// finished, but the work is ongoing, so show live elapsed until the chain
|
|
286
|
-
// resolves and the row is removed. Without this, subagent_status would show a
|
|
287
|
-
// frozen elapsed for a run the UI otherwise presents as still active.
|
|
288
|
-
const end = run.retained ? now : (run.endedAt ?? now);
|
|
289
|
-
return formatDuration(end - run.startedAt);
|
|
264
|
+
return formatDuration((run.endedAt ?? now) - run.startedAt);
|
|
290
265
|
}
|
|
291
266
|
|
|
292
267
|
/** Max length of the argument target inside a formatted activity line. */
|
|
293
268
|
export const ACTIVITY_TARGET_MAX = 60;
|
|
294
269
|
|
|
270
|
+
const REDACTED = "<redacted>";
|
|
271
|
+
|
|
272
|
+
/** Remove credentials embedded in otherwise ordinary activity strings such as
|
|
273
|
+
* shell commands and HTTP headers. */
|
|
274
|
+
function redactSensitiveText(value: string): string {
|
|
275
|
+
let text = stripVTControlCharacters(value);
|
|
276
|
+
text = text.replace(
|
|
277
|
+
/(\bauthorization\s*:\s*(?:bearer|basic)\s+)([^\s'"`;,]+)/giu,
|
|
278
|
+
`$1${REDACTED}`,
|
|
279
|
+
);
|
|
280
|
+
text = text.replace(
|
|
281
|
+
/(\bbearer\s+)([A-Za-z0-9._~+/=-]{6,})/giu,
|
|
282
|
+
`$1${REDACTED}`,
|
|
283
|
+
);
|
|
284
|
+
text = text.replace(
|
|
285
|
+
/(\b(?:api[-_]?key|apikey|access[-_]?token|refresh[-_]?token|token|password|passwd|secret|credential|cookie)\b\s*(?:=|:)\s*)(?:"[^"]*"|'[^']*'|[^\s;&,]+)/giu,
|
|
286
|
+
`$1${REDACTED}`,
|
|
287
|
+
);
|
|
288
|
+
text = text.replace(
|
|
289
|
+
/((?:--?(?:api[-_]?key|access[-_]?token|token|password|secret|credential))\s+)(?:"[^"]*"|'[^']*'|\S+)/giu,
|
|
290
|
+
`$1${REDACTED}`,
|
|
291
|
+
);
|
|
292
|
+
return text.replace(
|
|
293
|
+
/\b(?:sk-[A-Za-z0-9_-]{8,}|gh[pousr]_[A-Za-z0-9]{8,}|AKIA[A-Z0-9]{16})\b/gu,
|
|
294
|
+
REDACTED,
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
295
298
|
/** Monitor activity is returned to the parent model and rendered in the terminal,
|
|
296
299
|
* so treat every live string as untrusted before it reaches store state. */
|
|
297
300
|
function sanitizeActivityText(value: string): string {
|
|
@@ -363,19 +366,22 @@ export class MonitorStore {
|
|
|
363
366
|
private subscribers = new Set<() => void>();
|
|
364
367
|
|
|
365
368
|
beginTurn(): void {
|
|
366
|
-
// Clear finished runs from a previous turn, but keep
|
|
367
|
-
//
|
|
368
|
-
// Retained runs (e.g. an auto-fix chain parent whose chain is still
|
|
369
|
-
// running) are also preserved — their status is "done" but they must
|
|
370
|
-
// stay visible until the chain resolves.
|
|
369
|
+
// Clear finished runs from a previous turn, but keep active and parked
|
|
370
|
+
// threads so concurrent work is not wiped between parent turns.
|
|
371
371
|
this.runs = this.runs.filter(
|
|
372
|
-
(r) => isRunActiveStatus(r.status) || r.status === "parked"
|
|
372
|
+
(r) => isRunActiveStatus(r.status) || r.status === "parked",
|
|
373
373
|
);
|
|
374
374
|
this.notify();
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
+
/** Reserve a stable id for a durable result that must remain independently
|
|
378
|
+
* addressable without appearing as a live monitor row. */
|
|
379
|
+
reserveRunId(): number {
|
|
380
|
+
return this.nextId++;
|
|
381
|
+
}
|
|
382
|
+
|
|
377
383
|
addRun(agent: string, task: string, model?: string, thinking?: string, meta?: RunChainMeta): number {
|
|
378
|
-
const id = this.
|
|
384
|
+
const id = this.reserveRunId();
|
|
379
385
|
this.runs.push({
|
|
380
386
|
id,
|
|
381
387
|
agent,
|
|
@@ -384,7 +390,7 @@ export class MonitorStore {
|
|
|
384
390
|
model,
|
|
385
391
|
thinking,
|
|
386
392
|
status: "queued",
|
|
387
|
-
usage:
|
|
393
|
+
usage: emptyUsage(),
|
|
388
394
|
...(meta?.groupId ? { groupId: meta.groupId } : {}),
|
|
389
395
|
...(meta?.relationLabel ? { relationLabel: meta.relationLabel } : {}),
|
|
390
396
|
...(meta?.isolation ? { isolation: meta.isolation, integrationStatus: meta.isolation === "worktree" ? "pending" : undefined } : {}),
|
|
@@ -403,7 +409,6 @@ export class MonitorStore {
|
|
|
403
409
|
// A model-fallback retry or resumed generation restarts the clock; a
|
|
404
410
|
// stale endedAt would freeze the elapsed display at the first attempt.
|
|
405
411
|
if (run.endedAt !== undefined) run.endedAt = undefined;
|
|
406
|
-
run.lastActivityAt = Date.now();
|
|
407
412
|
} else if ((status === "parked" || status === "done" || status === "failed") && run.endedAt === undefined) {
|
|
408
413
|
run.endedAt = Date.now();
|
|
409
414
|
}
|
|
@@ -414,7 +419,6 @@ export class MonitorStore {
|
|
|
414
419
|
if (!run) return;
|
|
415
420
|
run.usage = { ...usage };
|
|
416
421
|
if (model) run.model = model;
|
|
417
|
-
run.lastActivityAt = Date.now();
|
|
418
422
|
this.notify();
|
|
419
423
|
}
|
|
420
424
|
|
|
@@ -432,59 +436,27 @@ export class MonitorStore {
|
|
|
432
436
|
const run = this.find(id);
|
|
433
437
|
if (!run) return;
|
|
434
438
|
run.activity = sanitizeActivityText(text) || undefined;
|
|
435
|
-
run.lastActivityAt = Date.now();
|
|
436
439
|
this.notify();
|
|
437
440
|
}
|
|
438
441
|
|
|
439
|
-
/** Record a tool starting
|
|
440
|
-
* A running tool means the run is NOT idle, so needs-attention is suppressed
|
|
441
|
-
* while it stays current. */
|
|
442
|
+
/** Record a tool starting and update the run's visible activity. */
|
|
442
443
|
recordToolStart(id: number, toolName: string, activity: string): void {
|
|
443
444
|
const run = this.find(id);
|
|
444
445
|
if (!run) return;
|
|
445
446
|
const safeToolName = sanitizeActivityText(toolName) || "tool";
|
|
446
|
-
run.toolCount = (run.toolCount ?? 0) + 1;
|
|
447
|
-
run.currentTool = safeToolName;
|
|
448
447
|
run.activity = sanitizeActivityText(activity) || safeToolName;
|
|
449
|
-
run.lastActivityAt = Date.now();
|
|
450
448
|
this.notify();
|
|
451
449
|
}
|
|
452
450
|
|
|
453
|
-
/** Record a tool
|
|
454
|
-
*
|
|
451
|
+
/** Record a failed tool; successful completions keep their last activity
|
|
452
|
+
* until the next model event supplies a more useful description. */
|
|
455
453
|
recordToolEnd(id: number, toolName: string, isError: boolean): void {
|
|
456
454
|
const run = this.find(id);
|
|
457
455
|
if (!run) return;
|
|
458
|
-
run.currentTool = undefined;
|
|
459
|
-
run.lastActivityAt = Date.now();
|
|
460
456
|
if (isError) run.activity = `✗ ${sanitizeActivityText(toolName) || "tool"} failed`;
|
|
461
457
|
this.notify();
|
|
462
458
|
}
|
|
463
459
|
|
|
464
|
-
/** Set an orchestration note on the run (e.g. auto-fix chain running). */
|
|
465
|
-
setAnnotation(id: number, text: string): void {
|
|
466
|
-
const run = this.find(id);
|
|
467
|
-
if (!run) return;
|
|
468
|
-
run.annotation = text;
|
|
469
|
-
this.notify();
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/** Set the run's one-line outcome summary (what a finished chain round did). */
|
|
473
|
-
setSummary(id: number, text: string | undefined): void {
|
|
474
|
-
const run = this.find(id);
|
|
475
|
-
if (!run) return;
|
|
476
|
-
run.summary = text;
|
|
477
|
-
this.notify();
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
/** Keep a finished chain step in status state until its group settles. */
|
|
481
|
-
setRetained(id: number, retained: boolean): void {
|
|
482
|
-
const run = this.find(id);
|
|
483
|
-
if (!run) return;
|
|
484
|
-
run.retained = retained;
|
|
485
|
-
this.notify();
|
|
486
|
-
}
|
|
487
|
-
|
|
488
460
|
setIsolation(id: number, isolation: IsolationMode, integrationStatus?: "pending" | WorktreeFinalizationStatus): void {
|
|
489
461
|
const run = this.find(id);
|
|
490
462
|
if (!run) return;
|
|
@@ -526,7 +498,7 @@ export class MonitorStore {
|
|
|
526
498
|
thinking,
|
|
527
499
|
...(isolation ? { isolation, integrationStatus: isolation === "worktree" ? "pending" as const : undefined } : {}),
|
|
528
500
|
status: "queued",
|
|
529
|
-
usage:
|
|
501
|
+
usage: emptyUsage(),
|
|
530
502
|
});
|
|
531
503
|
this.notify();
|
|
532
504
|
return;
|
|
@@ -539,16 +511,10 @@ export class MonitorStore {
|
|
|
539
511
|
if (isolation) run.isolation = isolation;
|
|
540
512
|
run.integrationStatus = isolation === "worktree" ? "pending" : undefined;
|
|
541
513
|
run.status = "queued";
|
|
542
|
-
run.usage =
|
|
514
|
+
run.usage = emptyUsage();
|
|
543
515
|
run.activity = undefined;
|
|
544
|
-
run.toolCount = undefined;
|
|
545
|
-
run.currentTool = undefined;
|
|
546
|
-
run.lastActivityAt = undefined;
|
|
547
516
|
run.startedAt = undefined;
|
|
548
517
|
run.endedAt = undefined;
|
|
549
|
-
run.annotation = undefined;
|
|
550
|
-
run.summary = undefined;
|
|
551
|
-
run.retained = undefined;
|
|
552
518
|
this.notify();
|
|
553
519
|
}
|
|
554
520
|
|
|
@@ -589,7 +555,6 @@ export class MonitorStore {
|
|
|
589
555
|
const usage = formatUsageCompact(run.usage);
|
|
590
556
|
const parts = [run.agent];
|
|
591
557
|
if (run.relationLabel) parts.push(run.relationLabel);
|
|
592
|
-
if (run.summary) parts.push(run.summary);
|
|
593
558
|
if (run.model) parts.push(run.model);
|
|
594
559
|
if (run.thinking) parts.push(`thinking ${run.thinking}`);
|
|
595
560
|
if (run.isolation === "worktree") parts.push(`worktree ${run.integrationStatus ?? "active"}`);
|
|
@@ -658,20 +623,3 @@ export function statusLabel(status: RunStatus): string {
|
|
|
658
623
|
return "stopped";
|
|
659
624
|
}
|
|
660
625
|
}
|
|
661
|
-
|
|
662
|
-
/** Theme color matching the status label. */
|
|
663
|
-
export function statusColor(status: RunStatus): "accent" | "success" | "error" | "warning" | "dim" {
|
|
664
|
-
switch (status) {
|
|
665
|
-
case "running":
|
|
666
|
-
case "steering":
|
|
667
|
-
return "accent";
|
|
668
|
-
case "interrupting":
|
|
669
|
-
return "warning";
|
|
670
|
-
case "done":
|
|
671
|
-
return "success";
|
|
672
|
-
case "failed":
|
|
673
|
-
return "error";
|
|
674
|
-
default:
|
|
675
|
-
return "dim";
|
|
676
|
-
}
|
|
677
|
-
}
|
package/src/rpc-run.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { tmpdir } from "node:os";
|
|
|
14
14
|
import { basename, join } from "node:path";
|
|
15
15
|
import { StringDecoder } from "node:string_decoder";
|
|
16
16
|
import type { Message } from "@earendil-works/pi-ai";
|
|
17
|
-
import type { AgentConfig
|
|
17
|
+
import type { AgentConfig } from "./agents.ts";
|
|
18
18
|
import type { ThinkingLevel } from "./config.ts";
|
|
19
19
|
import type { IsolationMode, WorktreeFinalizationStatus } from "./worktree.ts";
|
|
20
20
|
|
|
@@ -43,7 +43,6 @@ export interface UsageStats {
|
|
|
43
43
|
|
|
44
44
|
export interface RpcSingleResult {
|
|
45
45
|
agent: string;
|
|
46
|
-
agentSource: AgentSource | "unknown";
|
|
47
46
|
task: string;
|
|
48
47
|
exitCode: number;
|
|
49
48
|
messages: Message[];
|
|
@@ -65,16 +64,12 @@ export interface RpcSingleResult {
|
|
|
65
64
|
failedTools?: Array<{ toolName: string; error: string }>;
|
|
66
65
|
sessionId?: string;
|
|
67
66
|
sessionDir?: string;
|
|
68
|
-
resumed?: boolean;
|
|
69
67
|
/** Internal disposition: dispatch suppresses completion delivery for parks. */
|
|
70
68
|
parked?: boolean;
|
|
71
69
|
/** Stable logical run id assigned by dispatch (also present on queued results). */
|
|
72
70
|
runId?: number;
|
|
73
71
|
/** Filesystem isolation selected for this logical thread. */
|
|
74
72
|
isolation?: IsolationMode;
|
|
75
|
-
/** Original parent cwd; isolated children execute at isolationCwd instead. */
|
|
76
|
-
originalCwd?: string;
|
|
77
|
-
isolationCwd?: string;
|
|
78
73
|
/** Final integration state for a worktree-isolated settlement. */
|
|
79
74
|
integrationStatus?: "pending" | WorktreeFinalizationStatus;
|
|
80
75
|
integrationApplied?: boolean;
|
|
@@ -168,10 +163,6 @@ export class RpcRunControl {
|
|
|
168
163
|
this.setPhase("parked");
|
|
169
164
|
}
|
|
170
165
|
|
|
171
|
-
markQueued(): void {
|
|
172
|
-
this.setPhase("queued");
|
|
173
|
-
}
|
|
174
|
-
|
|
175
166
|
markStarting(): void {
|
|
176
167
|
this.setPhase("starting");
|
|
177
168
|
}
|
|
@@ -431,12 +422,8 @@ export async function runRpcAgentAttempt(options: RunRpcAttemptOptions): Promise
|
|
|
431
422
|
args.push("--append-system-prompt", tmpPromptPath);
|
|
432
423
|
}
|
|
433
424
|
|
|
434
|
-
const resumed = Boolean(
|
|
435
|
-
options.sessionDir && options.sessionId && sessionExists(options.sessionDir, options.sessionId),
|
|
436
|
-
);
|
|
437
425
|
const result: RpcSingleResult = {
|
|
438
426
|
agent: agentName,
|
|
439
|
-
agentSource: agent.source,
|
|
440
427
|
task,
|
|
441
428
|
exitCode: 0,
|
|
442
429
|
messages: [],
|
|
@@ -446,7 +433,6 @@ export async function runRpcAgentAttempt(options: RunRpcAttemptOptions): Promise
|
|
|
446
433
|
thinking: thinkingLevel,
|
|
447
434
|
sessionId: options.sessionId,
|
|
448
435
|
sessionDir: options.sessionDir,
|
|
449
|
-
resumed,
|
|
450
436
|
};
|
|
451
437
|
|
|
452
438
|
const childDepth = currentSubagentDepth(options.env) + 1;
|
package/src/runtime.ts
CHANGED
|
@@ -27,7 +27,6 @@ import {
|
|
|
27
27
|
type RecoveryRecord,
|
|
28
28
|
} from "./recovery.ts";
|
|
29
29
|
import type { RpcRunControl } from "./rpc-run.ts";
|
|
30
|
-
import { trajectoryStore } from "./trajectory.ts";
|
|
31
30
|
import type { SingleResult } from "./spawn.ts";
|
|
32
31
|
import type { IsolationMode, WorktreeFinalization, WorktreeIsolation } from "./worktree.ts";
|
|
33
32
|
|
|
@@ -118,9 +117,6 @@ export interface SubagentRuntime {
|
|
|
118
117
|
sessionDirs: Set<string>;
|
|
119
118
|
retainSession: (result: Pick<SingleResult, "sessionDir">) => void;
|
|
120
119
|
retireThreadSession: (thread: SubagentThread) => void;
|
|
121
|
-
/** Worktree/patch paths intentionally retained after a failed integration. */
|
|
122
|
-
retainedArtifactPaths: Set<string>;
|
|
123
|
-
retainWorktreeArtifacts: (finalization: WorktreeFinalization) => void;
|
|
124
120
|
/** Flip sessionActive off and release all session-scoped resources. */
|
|
125
121
|
shutdown: () => Promise<void>;
|
|
126
122
|
}
|
|
@@ -141,9 +137,11 @@ export function createRuntime(pi: ExtensionAPI, configPath: string): SubagentRun
|
|
|
141
137
|
// Computing this at delivery (emit) time — not when the item was
|
|
142
138
|
// pushed — reflects the current monitor state, since finishing runs
|
|
143
139
|
// are removed from the monitor before their completion is pushed.
|
|
140
|
+
// Auto-fix parents are flipped back to "running" while their chain
|
|
141
|
+
// owns the logical run, so they are included without a special case.
|
|
144
142
|
const active = monitor
|
|
145
143
|
.getRuns()
|
|
146
|
-
.filter((run) => isRunActiveStatus(run.status)
|
|
144
|
+
.filter((run) => isRunActiveStatus(run.status))
|
|
147
145
|
.map((run) => ({ id: run.id, agent: run.agent, label: run.label }));
|
|
148
146
|
const message = {
|
|
149
147
|
customType: "subagent-result",
|
|
@@ -171,11 +169,6 @@ export function createRuntime(pi: ExtensionAPI, configPath: string): SubagentRun
|
|
|
171
169
|
threads: new Map<number, SubagentThread>(),
|
|
172
170
|
preflightOperations: new Set<Promise<void>>(),
|
|
173
171
|
sessionDirs: new Set<string>(),
|
|
174
|
-
retainedArtifactPaths: new Set<string>(),
|
|
175
|
-
retainWorktreeArtifacts: (finalization) => {
|
|
176
|
-
if (finalization.worktreePath) runtime.retainedArtifactPaths.add(finalization.worktreePath);
|
|
177
|
-
if (finalization.patchPath) runtime.retainedArtifactPaths.add(finalization.patchPath);
|
|
178
|
-
},
|
|
179
172
|
retainSession: (result) => {
|
|
180
173
|
if (result.sessionDir) runtime.sessionDirs.add(result.sessionDir);
|
|
181
174
|
},
|
|
@@ -241,7 +234,6 @@ export function createRuntime(pi: ExtensionAPI, configPath: string): SubagentRun
|
|
|
241
234
|
for (const thread of runtime.threads.values()) {
|
|
242
235
|
const finalization = await thread.finalizeIsolation(thread.generation).catch(() => undefined);
|
|
243
236
|
if (finalization?.status === "retained") {
|
|
244
|
-
runtime.retainWorktreeArtifacts(finalization);
|
|
245
237
|
recoveryRecords.push(recoveryRecordFromFinalization(thread.id, finalization));
|
|
246
238
|
if (!thread.isolationFailureNotified) {
|
|
247
239
|
thread.isolationFailureNotified = true;
|
|
@@ -268,12 +260,8 @@ export function createRuntime(pi: ExtensionAPI, configPath: string): SubagentRun
|
|
|
268
260
|
}
|
|
269
261
|
runtime.sessionDirs.clear();
|
|
270
262
|
runtime.preflightOperations.clear();
|
|
271
|
-
// Deliberately do not remove retainedArtifactPaths: they are the recovery
|
|
272
|
-
// path after a failed patch apply/cleanup.
|
|
273
263
|
runtime.threads.clear();
|
|
274
264
|
monitor.clear();
|
|
275
|
-
// Lifecycle trajectories are parent-session scoped.
|
|
276
|
-
trajectoryStore.clearAll();
|
|
277
265
|
},
|
|
278
266
|
};
|
|
279
267
|
|
package/src/session-fork.ts
CHANGED
|
@@ -7,7 +7,6 @@ import { tmpdir } from "node:os";
|
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
|
|
9
9
|
export interface ForkedSession {
|
|
10
|
-
sourceSessionFile: string;
|
|
11
10
|
sessionDir: string;
|
|
12
11
|
sessionId: string;
|
|
13
12
|
sessionFile: string;
|
|
@@ -15,7 +14,6 @@ export interface ForkedSession {
|
|
|
15
14
|
|
|
16
15
|
/** Locate one retained session by its authoritative header id. */
|
|
17
16
|
export async function findRetainedSessionFile(
|
|
18
|
-
cwd: string,
|
|
19
17
|
sessionDir: string,
|
|
20
18
|
sessionId: string,
|
|
21
19
|
): Promise<string> {
|
|
@@ -47,7 +45,6 @@ export async function forkRetainedSession(options: {
|
|
|
47
45
|
sessionId: string;
|
|
48
46
|
}): Promise<ForkedSession> {
|
|
49
47
|
const sourceSessionFile = await findRetainedSessionFile(
|
|
50
|
-
options.cwd,
|
|
51
48
|
options.sessionDir,
|
|
52
49
|
options.sessionId,
|
|
53
50
|
);
|
|
@@ -72,7 +69,6 @@ export async function forkRetainedSession(options: {
|
|
|
72
69
|
throw new Error(`Forked session branch has no persisted assistant checkpoint at ${sessionFile}.`);
|
|
73
70
|
}
|
|
74
71
|
return {
|
|
75
|
-
sourceSessionFile,
|
|
76
72
|
sessionDir,
|
|
77
73
|
sessionId: manager.getSessionId(),
|
|
78
74
|
sessionFile,
|