@esso0428/pi-subagents 0.15.5 → 0.15.7
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 +10 -19
- package/README.md +5 -5
- package/dist/agent-history-list.d.ts +19 -0
- package/dist/agent-history-list.d.ts.map +1 -0
- package/dist/agent-history-list.js +69 -0
- package/dist/agent-history-list.js.map +1 -0
- package/dist/agent-history.d.ts +2 -1
- package/dist/agent-history.d.ts.map +1 -0
- package/dist/agent-history.js +32 -34
- package/dist/agent-history.js.map +1 -0
- package/dist/agent-manager.d.ts +24 -8
- package/dist/agent-manager.d.ts.map +1 -0
- package/dist/agent-manager.js +171 -93
- package/dist/agent-manager.js.map +1 -0
- package/dist/agent-runner.d.ts +1 -0
- package/dist/agent-runner.d.ts.map +1 -0
- package/dist/agent-runner.js +75 -86
- package/dist/agent-runner.js.map +1 -0
- package/dist/agent-types.d.ts +1 -0
- package/dist/agent-types.d.ts.map +1 -0
- package/dist/agent-types.js +28 -45
- package/dist/agent-types.js.map +1 -0
- package/dist/context.d.ts +1 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +3 -6
- package/dist/context.js.map +1 -0
- package/dist/cross-extension-rpc.d.ts +1 -0
- package/dist/cross-extension-rpc.d.ts.map +1 -0
- package/dist/cross-extension-rpc.js +6 -9
- package/dist/cross-extension-rpc.js.map +1 -0
- package/dist/custom-agents.d.ts +1 -0
- package/dist/custom-agents.d.ts.map +1 -0
- package/dist/custom-agents.js +16 -18
- package/dist/custom-agents.js.map +1 -0
- package/dist/default-agents.d.ts +1 -0
- package/dist/default-agents.d.ts.map +1 -0
- package/dist/default-agents.js +2 -4
- package/dist/default-agents.js.map +1 -0
- package/dist/enabled-models.d.ts +1 -0
- package/dist/enabled-models.d.ts.map +1 -0
- package/dist/enabled-models.js +12 -16
- package/dist/enabled-models.js.map +1 -0
- package/dist/env.d.ts +1 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +2 -4
- package/dist/env.js.map +1 -0
- package/dist/group-join.d.ts +1 -0
- package/dist/group-join.d.ts.map +1 -0
- package/dist/group-join.js +2 -5
- package/dist/group-join.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +306 -292
- package/dist/index.js.map +1 -0
- package/dist/invocation-config.d.ts +1 -0
- package/dist/invocation-config.d.ts.map +1 -0
- package/dist/invocation-config.js +3 -6
- package/dist/invocation-config.js.map +1 -0
- package/dist/memory.d.ts +1 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +25 -34
- package/dist/memory.js.map +1 -0
- package/dist/model-resolver.d.ts +1 -0
- package/dist/model-resolver.d.ts.map +1 -0
- package/dist/model-resolver.js +2 -4
- package/dist/model-resolver.js.map +1 -0
- package/dist/nico-overrides.d.ts +1 -0
- package/dist/nico-overrides.d.ts.map +1 -0
- package/dist/nico-overrides.js +15 -20
- package/dist/nico-overrides.js.map +1 -0
- package/dist/output-file.d.ts +1 -0
- package/dist/output-file.d.ts.map +1 -0
- package/dist/output-file.js +17 -22
- package/dist/output-file.js.map +1 -0
- package/dist/prompts.d.ts +1 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +2 -4
- package/dist/prompts.js.map +1 -0
- package/dist/schedule-store.d.ts +1 -0
- package/dist/schedule-store.d.ts.map +1 -0
- package/dist/schedule-store.js +17 -21
- package/dist/schedule-store.js.map +1 -0
- package/dist/schedule.d.ts +1 -0
- package/dist/schedule.d.ts.map +1 -0
- package/dist/schedule.js +9 -12
- package/dist/schedule.js.map +1 -0
- package/dist/settings.d.ts +1 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +16 -23
- package/dist/settings.js.map +1 -0
- package/dist/skill-loader.d.ts +1 -0
- package/dist/skill-loader.d.ts.map +1 -0
- package/dist/skill-loader.js +21 -23
- package/dist/skill-loader.js.map +1 -0
- package/dist/status-note.d.ts +1 -0
- package/dist/status-note.d.ts.map +1 -0
- package/dist/status-note.js +2 -4
- package/dist/status-note.js.map +1 -0
- package/dist/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -4
- package/dist/types.js.map +1 -0
- package/dist/ui/agent-widget.d.ts +2 -5
- package/dist/ui/agent-widget.d.ts.map +1 -0
- package/dist/ui/agent-widget.js +29 -51
- package/dist/ui/agent-widget.js.map +1 -0
- package/dist/ui/ccstyle/diff/ansi-utils.d.ts +11 -0
- package/dist/ui/ccstyle/diff/ansi-utils.d.ts.map +1 -0
- package/dist/ui/ccstyle/diff/ansi-utils.js +145 -0
- package/dist/ui/ccstyle/diff/ansi-utils.js.map +1 -0
- package/dist/ui/ccstyle/diff/diff-presentation.d.ts +12 -0
- package/dist/ui/ccstyle/diff/diff-presentation.d.ts.map +1 -0
- package/dist/ui/ccstyle/diff/diff-presentation.js +49 -0
- package/dist/ui/ccstyle/diff/diff-presentation.js.map +1 -0
- package/dist/ui/ccstyle/diff/diff-renderer.d.ts +46 -0
- package/dist/ui/ccstyle/diff/diff-renderer.d.ts.map +1 -0
- package/dist/ui/ccstyle/diff/diff-renderer.js +2044 -0
- package/dist/ui/ccstyle/diff/diff-renderer.js.map +1 -0
- package/dist/ui/ccstyle/diff/line-width-safety.d.ts +12 -0
- package/dist/ui/ccstyle/diff/line-width-safety.d.ts.map +1 -0
- package/dist/ui/ccstyle/diff/line-width-safety.js +59 -0
- package/dist/ui/ccstyle/diff/line-width-safety.js.map +1 -0
- package/dist/ui/ccstyle/diff/render-utils.d.ts +6 -0
- package/dist/ui/ccstyle/diff/render-utils.d.ts.map +1 -0
- package/dist/ui/ccstyle/diff/render-utils.js +25 -0
- package/dist/ui/ccstyle/diff/render-utils.js.map +1 -0
- package/dist/ui/ccstyle/diff/types.d.ts +22 -0
- package/dist/ui/ccstyle/diff/types.d.ts.map +1 -0
- package/dist/ui/ccstyle/diff/types.js +10 -0
- package/dist/ui/ccstyle/diff/types.js.map +1 -0
- package/dist/ui/ccstyle/diff/write-display-utils.d.ts +2 -0
- package/dist/ui/ccstyle/diff/write-display-utils.d.ts.map +1 -0
- package/dist/ui/ccstyle/diff/write-display-utils.js +12 -0
- package/dist/ui/ccstyle/diff/write-display-utils.js.map +1 -0
- package/dist/ui/ccstyle/tool-renderer.d.ts +17 -0
- package/dist/ui/ccstyle/tool-renderer.d.ts.map +1 -0
- package/dist/ui/ccstyle/tool-renderer.js +58 -0
- package/dist/ui/ccstyle/tool-renderer.js.map +1 -0
- package/dist/ui/ccstyle/tool-result.d.ts +24 -0
- package/dist/ui/ccstyle/tool-result.d.ts.map +1 -0
- package/dist/ui/ccstyle/tool-result.js +193 -0
- package/dist/ui/ccstyle/tool-result.js.map +1 -0
- package/dist/ui/conversation-blocks.d.ts +46 -0
- package/dist/ui/conversation-blocks.d.ts.map +1 -0
- package/dist/ui/conversation-blocks.js +306 -0
- package/dist/ui/conversation-blocks.js.map +1 -0
- package/dist/ui/conversation-nvim.d.ts +8 -0
- package/dist/ui/conversation-nvim.d.ts.map +1 -0
- package/dist/ui/conversation-nvim.js +117 -0
- package/dist/ui/conversation-nvim.js.map +1 -0
- package/dist/ui/conversation-role.d.ts +13 -0
- package/dist/ui/conversation-role.d.ts.map +1 -0
- package/dist/ui/conversation-role.js +39 -0
- package/dist/ui/conversation-role.js.map +1 -0
- package/dist/ui/conversation-search.d.ts +39 -0
- package/dist/ui/conversation-search.d.ts.map +1 -0
- package/dist/ui/conversation-search.js +124 -0
- package/dist/ui/conversation-search.js.map +1 -0
- package/dist/ui/conversation-timeline.d.ts +62 -0
- package/dist/ui/conversation-timeline.d.ts.map +1 -0
- package/dist/ui/conversation-timeline.js +263 -0
- package/dist/ui/conversation-timeline.js.map +1 -0
- package/dist/ui/conversation-viewer.d.ts +62 -20
- package/dist/ui/conversation-viewer.d.ts.map +1 -0
- package/dist/ui/conversation-viewer.js +449 -224
- package/dist/ui/conversation-viewer.js.map +1 -0
- package/dist/ui/fleet-list.d.ts +24 -11
- package/dist/ui/fleet-list.d.ts.map +1 -0
- package/dist/ui/fleet-list.js +86 -78
- package/dist/ui/fleet-list.js.map +1 -0
- package/dist/ui/schedule-menu.d.ts +1 -0
- package/dist/ui/schedule-menu.d.ts.map +1 -0
- package/dist/ui/schedule-menu.js +2 -4
- package/dist/ui/schedule-menu.js.map +1 -0
- package/dist/ui/viewer-keys.d.ts +1 -0
- package/dist/ui/viewer-keys.d.ts.map +1 -0
- package/dist/ui/viewer-keys.js +8 -10
- package/dist/ui/viewer-keys.js.map +1 -0
- package/dist/usage.d.ts +1 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +5 -10
- package/dist/usage.js.map +1 -0
- package/dist/worktree.d.ts +1 -0
- package/dist/worktree.d.ts.map +1 -0
- package/dist/worktree.js +27 -31
- package/dist/worktree.js.map +1 -0
- package/dist/write-execution.d.ts +42 -0
- package/dist/write-execution.d.ts.map +1 -0
- package/dist/write-execution.js +138 -0
- package/dist/write-execution.js.map +1 -0
- package/package.json +1 -1
- package/src/agent-history-list.ts +91 -0
- package/src/agent-history.ts +12 -17
- package/src/agent-manager.ts +169 -81
- package/src/agent-runner.ts +12 -13
- package/src/index.ts +117 -98
- package/src/output-file.ts +1 -1
- package/src/types.ts +4 -1
- package/src/ui/agent-widget.ts +3 -9
- package/src/ui/ccstyle/ATTRIBUTION.md +37 -0
- package/src/ui/ccstyle/diff/ansi-utils.ts +168 -0
- package/src/ui/ccstyle/diff/diff-presentation.ts +70 -0
- package/src/ui/ccstyle/diff/diff-renderer.ts +3148 -0
- package/src/ui/ccstyle/diff/line-width-safety.ts +92 -0
- package/src/ui/ccstyle/diff/render-utils.ts +32 -0
- package/src/ui/ccstyle/diff/types.ts +33 -0
- package/src/ui/ccstyle/diff/write-display-utils.ts +12 -0
- package/src/ui/ccstyle/tool-renderer.ts +71 -0
- package/src/ui/ccstyle/tool-result.ts +238 -0
- package/src/ui/conversation-blocks.ts +404 -0
- package/src/ui/conversation-nvim.ts +127 -0
- package/src/ui/conversation-role.ts +45 -0
- package/src/ui/conversation-search.ts +161 -0
- package/src/ui/conversation-timeline.ts +315 -0
- package/src/ui/conversation-viewer.ts +418 -210
- package/src/ui/fleet-list.ts +73 -52
- package/src/write-execution.ts +180 -0
- package/test/agent-history-list.test.ts +121 -0
- package/test/agent-manager-history.test.ts +59 -58
- package/test/agent-menu-history-navigation.test.ts +86 -0
- package/test/output-file-history.test.ts +50 -0
- package/test/ui/conversation-blocks.test.ts +103 -0
- package/test/ui/conversation-diff.test.ts +91 -0
- package/test/ui/conversation-search.test.ts +55 -0
- package/test/ui/conversation-viewer.test.ts +182 -0
- package/test/ui/fleet-list.test.ts +131 -0
- package/test/write-execution.test.ts +83 -0
- package/tsconfig.json +16 -0
- package/vitest.temp.config.mts +6 -0
- package/dist/ui/markdown-result.d.ts +0 -3
- package/dist/ui/markdown-result.js +0 -53
- package/src/ui/markdown-result.ts +0 -56
- package/test/agent-history.test.ts +0 -63
- package/test/ui/markdown-result.test.ts +0 -45
package/src/agent-manager.ts
CHANGED
|
@@ -23,13 +23,101 @@ export type CompactionInfo = { reason: "manual" | "threshold" | "overflow"; toke
|
|
|
23
23
|
|
|
24
24
|
/** Default max concurrent background agents. */
|
|
25
25
|
const DEFAULT_MAX_CONCURRENT = 4;
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
const RESTORABLE_STATUSES = new Set(["completed", "steered", "stopped", "aborted", "error"] as const);
|
|
27
|
+
const THINKING_LEVELS = new Set(["minimal", "low", "medium", "high", "xhigh", "max", "off"]);
|
|
28
|
+
|
|
29
|
+
type RestorableAgentStatus = "completed" | "steered" | "stopped" | "aborted" | "error";
|
|
30
|
+
|
|
31
|
+
/** Narrow persisted strings before putting them into runtime/UI state. */
|
|
32
|
+
function isSafePersistedString(value: unknown, maxLength: number): value is string {
|
|
33
|
+
return typeof value === "string" && value.length > 0 && value.length <= maxLength && !/[\0\r\n]/.test(value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isFiniteTimestamp(value: unknown): value is number {
|
|
37
|
+
return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value >= 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isValidUsage(value: unknown): value is { input: number; output: number; cacheWrite: number } {
|
|
41
|
+
if (!value || typeof value !== "object") return false;
|
|
42
|
+
const usage = value as Record<string, unknown>;
|
|
43
|
+
return ["input", "output", "cacheWrite"].every((key) => {
|
|
44
|
+
const n = usage[key];
|
|
45
|
+
return typeof n === "number" && Number.isFinite(n) && n >= 0;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isSafeTranscriptLocator(value: unknown): value is string {
|
|
50
|
+
return typeof value === "string"
|
|
51
|
+
&& /^\.pi-subagents\/agent-transcripts\/[^/]+\.jsonl$/.test(value)
|
|
52
|
+
&& !value.includes("..")
|
|
53
|
+
&& !value.includes("\\")
|
|
54
|
+
&& !value.includes("\0");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isSafeInvocation(value: unknown): value is AgentInvocation {
|
|
58
|
+
if (!value || typeof value !== "object") return false;
|
|
59
|
+
const invocation = value as Record<string, unknown>;
|
|
60
|
+
for (const key of ["modelName", "effectiveModelName"]) {
|
|
61
|
+
if (invocation[key] !== undefined && !isSafePersistedString(invocation[key], 512)) return false;
|
|
62
|
+
}
|
|
63
|
+
for (const key of ["thinking", "effectiveThinking"]) {
|
|
64
|
+
if (invocation[key] !== undefined && (typeof invocation[key] !== "string" || !THINKING_LEVELS.has(invocation[key]))) return false;
|
|
65
|
+
}
|
|
66
|
+
if (invocation.maxTurns !== undefined && (!Number.isInteger(invocation.maxTurns) || (invocation.maxTurns as number) < 0)) return false;
|
|
67
|
+
for (const key of ["isolated", "inheritContext", "runInBackground"]) {
|
|
68
|
+
if (invocation[key] !== undefined && typeof invocation[key] !== "boolean") return false;
|
|
69
|
+
}
|
|
70
|
+
if (invocation.isolation !== undefined && invocation.isolation !== "worktree") return false;
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function cloneInvocation(value: AgentInvocation | undefined): AgentInvocation | undefined {
|
|
75
|
+
return value ? {
|
|
76
|
+
modelName: value.modelName,
|
|
77
|
+
effectiveModelName: value.effectiveModelName,
|
|
78
|
+
thinking: value.thinking,
|
|
79
|
+
effectiveThinking: value.effectiveThinking,
|
|
80
|
+
maxTurns: value.maxTurns,
|
|
81
|
+
isolated: value.isolated,
|
|
82
|
+
inheritContext: value.inheritContext,
|
|
83
|
+
runInBackground: value.runInBackground,
|
|
84
|
+
isolation: value.isolation,
|
|
85
|
+
} : undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Validate one persisted terminal record without constructing runtime handles. */
|
|
89
|
+
export function isRestorableAgentRecord(value: unknown): value is {
|
|
90
|
+
id: string;
|
|
91
|
+
type: string;
|
|
92
|
+
description: string;
|
|
93
|
+
status: RestorableAgentStatus;
|
|
94
|
+
startedAt: number;
|
|
95
|
+
completedAt: number;
|
|
96
|
+
result?: string;
|
|
97
|
+
error?: string;
|
|
98
|
+
toolUses?: number;
|
|
99
|
+
lifetimeUsage?: { input: number; output: number; cacheWrite: number };
|
|
100
|
+
transcriptPath?: string;
|
|
101
|
+
invocation?: AgentInvocation;
|
|
102
|
+
} {
|
|
103
|
+
if (!value || typeof value !== "object") return false;
|
|
104
|
+
const record = value as Record<string, unknown>;
|
|
105
|
+
if (!isSafePersistedString(record.id, 256)
|
|
106
|
+
|| !isSafePersistedString(record.type, 256)
|
|
107
|
+
|| !isSafePersistedString(record.description, 4096)
|
|
108
|
+
|| typeof record.status !== "string"
|
|
109
|
+
|| !RESTORABLE_STATUSES.has(record.status as RestorableAgentStatus)
|
|
110
|
+
|| !isFiniteTimestamp(record.startedAt)
|
|
111
|
+
|| !isFiniteTimestamp(record.completedAt)
|
|
112
|
+
|| record.completedAt < record.startedAt) return false;
|
|
113
|
+
if (record.result !== undefined && !isSafePersistedString(record.result, 2_000_000)) return false;
|
|
114
|
+
if (record.error !== undefined && !isSafePersistedString(record.error, 64_000)) return false;
|
|
115
|
+
if (record.toolUses !== undefined && (!Number.isInteger(record.toolUses) || (record.toolUses as number) < 0)) return false;
|
|
116
|
+
if (record.lifetimeUsage !== undefined && !isValidUsage(record.lifetimeUsage)) return false;
|
|
117
|
+
if (record.transcriptPath !== undefined && !isSafeTranscriptLocator(record.transcriptPath)) return false;
|
|
118
|
+
if (record.invocation !== undefined && !isSafeInvocation(record.invocation)) return false;
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
33
121
|
|
|
34
122
|
/**
|
|
35
123
|
* Validate a caller-supplied SpawnOptions.cwd. `undefined`/`null` mean "unset"
|
|
@@ -104,45 +192,6 @@ interface SpawnOptions {
|
|
|
104
192
|
onCompaction?: (info: CompactionInfo) => void;
|
|
105
193
|
}
|
|
106
194
|
|
|
107
|
-
const RESTORABLE_STATUSES = new Set<AgentRecord["status"]>([
|
|
108
|
-
"completed",
|
|
109
|
-
"steered",
|
|
110
|
-
"aborted",
|
|
111
|
-
"stopped",
|
|
112
|
-
"error",
|
|
113
|
-
]);
|
|
114
|
-
|
|
115
|
-
type PersistedAgentRecord = Pick<
|
|
116
|
-
AgentRecord,
|
|
117
|
-
"id" | "type" | "description" | "status" | "result" | "error" | "startedAt" | "completedAt" | "transcriptPath"
|
|
118
|
-
>;
|
|
119
|
-
|
|
120
|
-
function isRestorableRecord(value: unknown): value is PersistedAgentRecord {
|
|
121
|
-
if (!value || typeof value !== "object") return false;
|
|
122
|
-
const record = value as Record<string, unknown>;
|
|
123
|
-
const transcriptPath = record.transcriptPath;
|
|
124
|
-
const validTranscriptPath =
|
|
125
|
-
transcriptPath === undefined ||
|
|
126
|
-
(typeof transcriptPath === "string" &&
|
|
127
|
-
transcriptPath.startsWith(".pi-subagents/agent-transcripts/") &&
|
|
128
|
-
!transcriptPath.includes("..") &&
|
|
129
|
-
!transcriptPath.includes("\\"));
|
|
130
|
-
return (
|
|
131
|
-
typeof record.id === "string" &&
|
|
132
|
-
record.id.length > 0 &&
|
|
133
|
-
typeof record.type === "string" &&
|
|
134
|
-
typeof record.description === "string" &&
|
|
135
|
-
RESTORABLE_STATUSES.has(record.status as AgentRecord["status"]) &&
|
|
136
|
-
typeof record.startedAt === "number" &&
|
|
137
|
-
Number.isFinite(record.startedAt) &&
|
|
138
|
-
typeof record.completedAt === "number" &&
|
|
139
|
-
Number.isFinite(record.completedAt) &&
|
|
140
|
-
(record.result === undefined || typeof record.result === "string") &&
|
|
141
|
-
(record.error === undefined || typeof record.error === "string") &&
|
|
142
|
-
validTranscriptPath
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
195
|
export class AgentManager {
|
|
147
196
|
private agents = new Map<string, AgentRecord>();
|
|
148
197
|
private cleanupInterval: ReturnType<typeof setInterval>;
|
|
@@ -323,6 +372,12 @@ export class AgentManager {
|
|
|
323
372
|
},
|
|
324
373
|
onSessionCreated: (session) => {
|
|
325
374
|
record.session = session;
|
|
375
|
+
const model = session.model;
|
|
376
|
+
record.invocation = {
|
|
377
|
+
...(record.invocation ?? {}),
|
|
378
|
+
...(model && { effectiveModelName: model.name ?? model.id }),
|
|
379
|
+
effectiveThinking: session.thinkingLevel,
|
|
380
|
+
};
|
|
326
381
|
// Flush any steers that arrived before the session was ready
|
|
327
382
|
if (record.pendingSteers?.length) {
|
|
328
383
|
for (const msg of record.pendingSteers) {
|
|
@@ -501,6 +556,12 @@ export class AgentManager {
|
|
|
501
556
|
record.completedAt = undefined;
|
|
502
557
|
record.result = undefined;
|
|
503
558
|
record.error = undefined;
|
|
559
|
+
const resumedModel = record.session.model;
|
|
560
|
+
record.invocation = {
|
|
561
|
+
...(record.invocation ?? {}),
|
|
562
|
+
...(resumedModel && { effectiveModelName: resumedModel.name ?? resumedModel.id }),
|
|
563
|
+
effectiveThinking: record.session.thinkingLevel,
|
|
564
|
+
};
|
|
504
565
|
|
|
505
566
|
try {
|
|
506
567
|
const { text, failure } = await resumeAgent(record.session, prompt, {
|
|
@@ -556,47 +617,61 @@ export class AgentManager {
|
|
|
556
617
|
return this.agents.get(id);
|
|
557
618
|
}
|
|
558
619
|
|
|
559
|
-
/**
|
|
560
|
-
* Restore terminal records persisted in a parent session.
|
|
561
|
-
*
|
|
562
|
-
* Restored records deliberately have no live session, promise, or abort
|
|
563
|
-
* controller. Invalid data is ignored because session entries are persisted
|
|
564
|
-
* extension data and may have been written by an older version.
|
|
565
|
-
*/
|
|
566
|
-
restoreCompleted(records: readonly unknown[]): void {
|
|
567
|
-
const restoredIds = new Set<string>();
|
|
568
|
-
// getBranch() is chronological; newest persisted state wins on duplicate IDs.
|
|
569
|
-
for (const value of [...records].reverse()) {
|
|
570
|
-
if (!isRestorableRecord(value)) continue;
|
|
571
|
-
if (restoredIds.has(value.id) || this.agents.has(value.id)) continue;
|
|
572
|
-
restoredIds.add(value.id);
|
|
573
|
-
|
|
574
|
-
this.agents.set(value.id, {
|
|
575
|
-
id: value.id,
|
|
576
|
-
type: value.type,
|
|
577
|
-
description: value.description,
|
|
578
|
-
status: value.status,
|
|
579
|
-
result: value.result,
|
|
580
|
-
error: value.error,
|
|
581
|
-
transcriptPath: value.transcriptPath,
|
|
582
|
-
toolUses: 0,
|
|
583
|
-
startedAt: value.startedAt,
|
|
584
|
-
completedAt: value.completedAt,
|
|
585
|
-
lifetimeUsage: { input: 0, output: 0, cacheWrite: 0 },
|
|
586
|
-
compactionCount: 0,
|
|
587
|
-
// Historical records have no inline tool surface and should remain
|
|
588
|
-
// visible in the background widget.
|
|
589
|
-
isBackground: true,
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
620
|
listAgents(): AgentRecord[] {
|
|
595
621
|
return [...this.agents.values()].sort(
|
|
596
622
|
(a, b) => b.startedAt - a.startedAt,
|
|
597
623
|
);
|
|
598
624
|
}
|
|
599
625
|
|
|
626
|
+
/** Restore terminal records persisted by a parent branch without runtime handles. */
|
|
627
|
+
restoreCompleted(records: readonly unknown[]): void {
|
|
628
|
+
const latest = new Map<string, ReturnType<typeof this.createRestoredRecord>>();
|
|
629
|
+
for (const value of records) {
|
|
630
|
+
if (isRestorableAgentRecord(value)) {
|
|
631
|
+
latest.set(value.id, this.createRestoredRecord(value));
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
for (const [id, restored] of latest) {
|
|
636
|
+
const existing = this.agents.get(id);
|
|
637
|
+
if (existing?.status === "running" || existing?.status === "queued") continue;
|
|
638
|
+
this.agents.set(id, restored);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
private createRestoredRecord(record: {
|
|
643
|
+
id: string;
|
|
644
|
+
type: string;
|
|
645
|
+
description: string;
|
|
646
|
+
status: RestorableAgentStatus;
|
|
647
|
+
startedAt: number;
|
|
648
|
+
completedAt: number;
|
|
649
|
+
result?: string;
|
|
650
|
+
error?: string;
|
|
651
|
+
toolUses?: number;
|
|
652
|
+
lifetimeUsage?: { input: number; output: number; cacheWrite: number };
|
|
653
|
+
transcriptPath?: string;
|
|
654
|
+
invocation?: AgentInvocation;
|
|
655
|
+
}): AgentRecord {
|
|
656
|
+
return {
|
|
657
|
+
id: record.id,
|
|
658
|
+
type: record.type,
|
|
659
|
+
description: record.description,
|
|
660
|
+
status: record.status,
|
|
661
|
+
result: record.result,
|
|
662
|
+
error: record.error,
|
|
663
|
+
toolUses: record.toolUses ?? 0,
|
|
664
|
+
startedAt: record.startedAt,
|
|
665
|
+
completedAt: record.completedAt,
|
|
666
|
+
transcriptPath: record.transcriptPath,
|
|
667
|
+
invocation: cloneInvocation(record.invocation),
|
|
668
|
+
lifetimeUsage: record.lifetimeUsage
|
|
669
|
+
? { ...record.lifetimeUsage }
|
|
670
|
+
: { input: 0, output: 0, cacheWrite: 0 },
|
|
671
|
+
compactionCount: 0,
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
|
|
600
675
|
abort(id: string): boolean {
|
|
601
676
|
const record = this.agents.get(id);
|
|
602
677
|
if (!record) return false;
|
|
@@ -628,6 +703,19 @@ export class AgentManager {
|
|
|
628
703
|
for (const [id, record] of this.agents) {
|
|
629
704
|
if (record.status === "running" || record.status === "queued") continue;
|
|
630
705
|
if ((record.completedAt ?? 0) >= cutoff) continue;
|
|
706
|
+
// A durable transcript is the source of truth for history. Release the
|
|
707
|
+
// live session after the TTL, but retain a lightweight record so opening
|
|
708
|
+
// history again in this session does not silently lose its identity or
|
|
709
|
+
// locator. Records without durable storage remain eligible for eviction.
|
|
710
|
+
if (record.transcriptPath) {
|
|
711
|
+
try { record.session?.dispose?.(); } catch { /* ignore cleanup failures */ }
|
|
712
|
+
record.session = undefined;
|
|
713
|
+
try { record.outputCleanup?.(); } catch { /* ignore cleanup failures */ }
|
|
714
|
+
record.outputCleanup = undefined;
|
|
715
|
+
record.outputFile = undefined;
|
|
716
|
+
record.historyFile = undefined;
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
631
719
|
this.removeRecord(id, record);
|
|
632
720
|
}
|
|
633
721
|
}
|
package/src/agent-runner.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { readFileSync } from "node:fs";
|
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
8
8
|
import type { Model } from "@earendil-works/pi-ai";
|
|
9
|
-
import type { ExtensionContext, LoadExtensionsResult } from "@earendil-works/pi-coding-agent";
|
|
9
|
+
import type { ExtensionContext, LoadExtensionsResult, ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
10
10
|
import {
|
|
11
11
|
type AgentSession,
|
|
12
12
|
type AgentSessionEvent,
|
|
@@ -25,6 +25,7 @@ import { buildMemoryBlock, buildReadOnlyMemoryBlock } from "./memory.js";
|
|
|
25
25
|
import { buildAgentPrompt, type PromptExtras } from "./prompts.js";
|
|
26
26
|
import { preloadSkills } from "./skill-loader.js";
|
|
27
27
|
import type { SubagentType, ThinkingLevel } from "./types.js";
|
|
28
|
+
import { createTrackedWriteTool } from "./write-execution.js";
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* Tool names registered by THIS extension. Single source of truth so the
|
|
@@ -781,32 +782,30 @@ export async function runAgent(
|
|
|
781
782
|
const sessionManager = agentConfig?.persistSession
|
|
782
783
|
? SessionManager.create(effectiveCwd, configuredSessionDir ?? defaultSessionDir)
|
|
783
784
|
: SessionManager.inMemory(effectiveCwd);
|
|
785
|
+
// Keep write tracking session-local. The custom definition shadows only this
|
|
786
|
+
// child session's built-in write entry; the parent registry is untouched.
|
|
787
|
+
const trackedWriteTool = toolNames.includes("write")
|
|
788
|
+
? createTrackedWriteTool(effectiveCwd)
|
|
789
|
+
: undefined;
|
|
784
790
|
|
|
785
791
|
// Pi 0.80.8 replaced createAgentSession's modelRegistry option with
|
|
786
792
|
// modelRuntime, but ExtensionContext still exposes only the registry facade.
|
|
787
793
|
// Pass both so the full supported Pi range retains the parent's providers.
|
|
788
|
-
const parentModelRuntime = (ctx.modelRegistry as unknown as { runtime?:
|
|
789
|
-
|
|
790
|
-
// `modelRuntime` was added in Pi 0.80.8. Infer it when available, while
|
|
791
|
-
// falling back to unknown for older Pi declarations.
|
|
792
|
-
type CompatibleModelRuntime = CreateAgentSessionOptions extends {
|
|
793
|
-
modelRuntime?: infer Runtime;
|
|
794
|
-
} ? Runtime : unknown;
|
|
795
|
-
const sessionOpts: CreateAgentSessionOptions & {
|
|
794
|
+
const parentModelRuntime = (ctx.modelRegistry as unknown as { runtime?: ModelRuntime | null }).runtime ?? undefined;
|
|
795
|
+
const sessionOpts: Parameters<typeof createAgentSession>[0] & {
|
|
796
796
|
modelRegistry: ExtensionContext["modelRegistry"];
|
|
797
|
-
modelRuntime?:
|
|
797
|
+
modelRuntime?: ModelRuntime;
|
|
798
798
|
} = {
|
|
799
799
|
cwd: effectiveCwd,
|
|
800
800
|
agentDir,
|
|
801
801
|
sessionManager,
|
|
802
802
|
settingsManager,
|
|
803
803
|
modelRegistry: ctx.modelRegistry,
|
|
804
|
-
...(parentModelRuntime
|
|
805
|
-
modelRuntime: parentModelRuntime as CompatibleModelRuntime,
|
|
806
|
-
}),
|
|
804
|
+
...(parentModelRuntime !== undefined && { modelRuntime: parentModelRuntime }),
|
|
807
805
|
model,
|
|
808
806
|
tools: sessionTools,
|
|
809
807
|
resourceLoader: loader,
|
|
808
|
+
...(trackedWriteTool && { customTools: [trackedWriteTool as any] }),
|
|
810
809
|
};
|
|
811
810
|
if (sessionExcludeTools) {
|
|
812
811
|
sessionOpts.excludeTools = sessionExcludeTools;
|