@maxiaochao/pi-toolkit 0.10.21 → 0.10.22

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.
Files changed (31) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/package.json +1 -1
  3. package/packages/herdr-subagents/CHANGELOG.md +19 -0
  4. package/packages/herdr-subagents/README.md +16 -8
  5. package/packages/herdr-subagents/RESUME-LIFECYCLE-DESIGN.md +84 -0
  6. package/packages/herdr-subagents/TESTING.md +13 -3
  7. package/packages/herdr-subagents/extensions/herdr-subagents.ts +4 -17
  8. package/packages/herdr-subagents/extensions/subagent-report.ts +2 -2
  9. package/packages/herdr-subagents/package.json +1 -1
  10. package/packages/herdr-subagents/src/batch-runner.ts +56 -15
  11. package/packages/herdr-subagents/src/blocking.ts +115 -72
  12. package/packages/herdr-subagents/src/continue-run.ts +22 -7
  13. package/packages/herdr-subagents/src/fleet.ts +2 -1
  14. package/packages/herdr-subagents/src/herdr.ts +10 -1
  15. package/packages/herdr-subagents/src/historical-resume.ts +184 -0
  16. package/packages/herdr-subagents/src/history.ts +5 -131
  17. package/packages/herdr-subagents/src/live-agent.ts +99 -35
  18. package/packages/herdr-subagents/src/parameters.ts +2 -2
  19. package/packages/herdr-subagents/src/protocol.ts +3 -1
  20. package/packages/herdr-subagents/src/reconcile.ts +156 -135
  21. package/packages/herdr-subagents/src/records.ts +50 -32
  22. package/packages/herdr-subagents/src/resume-state.ts +46 -0
  23. package/packages/herdr-subagents/src/run-pane-allocator.ts +39 -37
  24. package/packages/herdr-subagents/src/run-state.ts +120 -0
  25. package/packages/herdr-subagents/src/status.ts +0 -6
  26. package/packages/herdr-subagents/src/tab-cleanup.ts +16 -16
  27. package/packages/herdr-subagents/src/tool.ts +1 -54
  28. package/packages/herdr-subagents/src/validation.ts +1 -1
  29. package/packages/herdr-subagents/tests/extension-contract.ts +4 -4
  30. package/packages/herdr-subagents/tests/run-tests.mjs +556 -60
  31. package/packages/herdr-subagents/src/runner.ts +0 -234
@@ -1,234 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { mkdir, writeFile } from "node:fs/promises";
3
- import { join } from "node:path";
4
- import { CliHerdrAutomation, type HerdrAutomation } from "./herdr.ts";
5
- import { ensureRuntimeIgnored } from "./history.ts";
6
- import { childEnvironment, liveAgentName, promptLiveAgent } from "./live-agent.ts";
7
- import { writeJsonAtomic } from "./state.ts";
8
- import { cleanupRunTab } from "./tab-cleanup.ts";
9
- import type { OwnerIdentity } from "./ownership.ts";
10
- import { ownerRecord } from "./ownership.ts";
11
- import { applyReport, applyTaskStatus, settleAttemptFailure, type PersistedDocument, type PersistedReport, type RunRecord, type TaskRecord } from "./records.ts";
12
- import { reportProtocolPrompt } from "./protocol.ts";
13
- import { applyRunStatus } from "./run-status.ts";
14
- import { isAbortError } from "./errors.ts";
15
-
16
- export type HerdrSubagentsDocument = PersistedDocument;
17
- export type HerdrSubagentsReport = PersistedReport;
18
-
19
- export interface HerdrSubagentsOptions {
20
- agent?: string;
21
- label?: string;
22
- model?: string;
23
- thinking?: string;
24
- tools?: string[];
25
- skills?: string[];
26
- access?: "read" | "write";
27
- systemPrompt?: string;
28
- cwd?: string;
29
- signal?: AbortSignal;
30
- herdr?: HerdrAutomation;
31
- owner?: OwnerIdentity;
32
- onUpdate?: (result: HerdrSubagentsResult) => void;
33
- onCleanupError?: (message: string) => void;
34
- stalledWarningSeconds?: number;
35
- onStalled?: (message: string) => void;
36
- }
37
-
38
- export interface HerdrSubagentsResult {
39
- ok: boolean;
40
- status: "starting" | "running" | "blocked" | "completed" | "failed" | "cancelled";
41
- summary: string;
42
- documents: HerdrSubagentsDocument[];
43
- output: string;
44
- errorMessage?: string;
45
- runId?: string;
46
- taskId?: string;
47
- tabId?: string;
48
- paneId?: string;
49
- sessionFile?: string;
50
- recordDirectory?: string;
51
- model?: string;
52
- cwd: string;
53
- durationMs: number;
54
- }
55
-
56
- function compactText(report: HerdrSubagentsReport): string {
57
- const lines = [report.summary];
58
- if (report.documents.length > 0) {
59
- lines.push("", "Documents:", ...report.documents.map((document) => `- ${document.description}: ${document.path}`));
60
- }
61
- if (report.error) lines.push("", `Error: ${report.error}`);
62
- return lines.join("\n");
63
- }
64
-
65
- function failure(cwd: string, startedAt: number, errorMessage: string, fields: Partial<HerdrSubagentsResult> = {}): HerdrSubagentsResult {
66
- return {
67
- ok: false,
68
- status: "failed",
69
- summary: "Herdr Subagents failed.",
70
- documents: [],
71
- output: `Herdr Subagents failed.\nError: ${errorMessage}`,
72
- errorMessage,
73
- cwd,
74
- durationMs: Date.now() - startedAt,
75
- ...fields,
76
- };
77
- }
78
-
79
- async function writeJson(path: string, value: unknown): Promise<void> {
80
- await writeJsonAtomic(path, value);
81
- }
82
-
83
- export async function runHerdrSubagents(prompt: string, options: HerdrSubagentsOptions = {}): Promise<HerdrSubagentsResult> {
84
- const startedAt = Date.now();
85
- const cwd = options.cwd ?? process.cwd();
86
- if (process.env.HERDR_ENV !== "1" || !process.env.HERDR_WORKSPACE_ID) {
87
- return failure(cwd, startedAt, "Herdr Subagents must run from a Pi session inside Herdr.");
88
- }
89
-
90
- const runId = randomUUID();
91
- const taskId = randomUUID();
92
- const agent = options.agent?.trim() || "worker";
93
- const label = options.label?.trim() || agent;
94
- const runDirectory = join(cwd, ".pi", "herdr-subagents", "runs", `${new Date().toISOString().replace(/[-:.]/g, "").replace("Z", "Z-")}${runId.slice(0, 8)}`);
95
- const taskDirectory = join(runDirectory, "tasks", `01-${taskId.slice(0, 8)}`);
96
- const sessionFile = join(taskDirectory, "session.jsonl");
97
- const systemPromptFile = join(taskDirectory, "system-prompt.md");
98
- const reportFile = join(taskDirectory, "report.json");
99
- const runFile = join(runDirectory, "run.json");
100
- const taskFile = join(taskDirectory, "task.json");
101
- const startedAtIso = new Date(startedAt).toISOString();
102
- const systemPrompt = reportProtocolPrompt(options.systemPrompt ?? "You are a focused worker subagent.");
103
- const runRecord: RunRecord = {
104
- id: runId,
105
- label,
106
- status: "starting",
107
- cwd,
108
- ...ownerRecord(options.owner),
109
- stalledWarningSeconds: options.stalledWarningSeconds,
110
- taskIds: [taskId],
111
- startedAt: startedAtIso,
112
- };
113
- const taskRecord: TaskRecord = {
114
- id: taskId,
115
- runId,
116
- order: 1,
117
- name: label,
118
- status: "starting",
119
- prompt,
120
- agent,
121
- ...(options.model ? { model: options.model } : {}),
122
- ...(options.thinking ? { thinking: options.thinking } : {}),
123
- ...(options.tools ? { tools: options.tools } : {}),
124
- ...(options.skills ? { skills: options.skills } : {}),
125
- ...(options.access ? { access: options.access } : {}),
126
- systemPrompt,
127
- sessionFile,
128
- startedAt: startedAtIso,
129
- };
130
- await ensureRuntimeIgnored(cwd);
131
- await mkdir(taskDirectory, { recursive: true, mode: 0o700 });
132
- await writeFile(systemPromptFile, systemPrompt, { encoding: "utf8", mode: 0o600 });
133
- await Promise.all([writeJson(runFile, runRecord), writeJson(taskFile, taskRecord)]);
134
-
135
- const herdr = options.herdr ?? new CliHerdrAutomation();
136
- let tabId: string | undefined;
137
- let paneId: string | undefined;
138
- let result: HerdrSubagentsResult | undefined;
139
- const common = (): Partial<HerdrSubagentsResult> => ({
140
- runId,
141
- taskId,
142
- ...(tabId ? { tabId } : {}),
143
- ...(paneId ? { paneId } : {}),
144
- sessionFile,
145
- recordDirectory: taskDirectory,
146
- ...(options.model ? { model: options.model } : {}),
147
- });
148
- const update = (status: HerdrSubagentsResult["status"], summary: string) => {
149
- options.onUpdate?.({
150
- ok: false,
151
- status,
152
- summary,
153
- documents: [],
154
- output: summary,
155
- cwd,
156
- durationMs: Date.now() - startedAt,
157
- ...common(),
158
- });
159
- };
160
- update("starting", "Creating Herdr tab...");
161
-
162
- let attempt: { status: "blocked" | "completed" | "failed" | "cancelled"; report: PersistedReport; error?: unknown };
163
- try {
164
- const tab = await herdr.createTab({
165
- workspaceId: process.env.HERDR_WORKSPACE_ID,
166
- cwd,
167
- label: `SA · ${label}`,
168
- env: childEnvironment(taskDirectory),
169
- focus: false,
170
- signal: options.signal,
171
- });
172
- tabId = tab.tabId;
173
- paneId = tab.paneId;
174
- const paneLabel = `01 · ${label}`;
175
- await herdr.renamePane(paneId, paneLabel, options.signal);
176
- applyRunStatus(runRecord, "running");
177
- Object.assign(runRecord, { tabId, paneId });
178
- applyTaskStatus(taskRecord, "running");
179
- Object.assign(taskRecord, { tabId, paneId, paneLabel });
180
- await Promise.all([writeJson(runFile, runRecord), writeJson(taskFile, taskRecord)]);
181
-
182
- const name = liveAgentName(taskId);
183
- taskRecord.agentName = name;
184
- await writeJson(taskFile, taskRecord);
185
- update("starting", "Starting child Pi in Herdr...");
186
- update("running", "Child Pi is working...");
187
- const prompted = await promptLiveAgent({ herdr, task: { ...taskRecord, id: taskId, sessionFile }, paneId, prompt, systemPromptFile, agentName: name, signal: options.signal, stalledWarningMs: options.stalledWarningSeconds ? options.stalledWarningSeconds * 1000 : undefined, onStalled: () => options.onStalled?.(`${label} appears stalled; it is still running.`) });
188
- taskRecord.currentAttemptId = prompted.attemptId;
189
- const report = prompted.report;
190
- attempt = { status: applyReport(taskRecord, report), report };
191
- } catch (error) {
192
- const settled = await settleAttemptFailure(taskRecord, reportFile, {
193
- cancelled: isAbortError(error, options.signal),
194
- error: error instanceof Error ? error.message : String(error),
195
- name: "Herdr Subagents",
196
- summary: isAbortError(error, options.signal) ? "Herdr Subagents was cancelled." : "Herdr Subagents failed.",
197
- });
198
- attempt = { ...settled, error };
199
- }
200
- try {
201
- if (!attempt.error) {
202
- const { report, status: settledStatus } = attempt;
203
- const completedAt = new Date().toISOString();
204
- applyRunStatus(runRecord, settledStatus, { at: completedAt, error: report.error, question: report.question });
205
- await Promise.all([writeJson(runFile, runRecord), writeJson(taskFile, taskRecord)]);
206
- result = {
207
- ok: report.status === "completed",
208
- status: settledStatus,
209
- summary: report.summary,
210
- documents: report.documents,
211
- output: compactText(report),
212
- ...(report.error || report.question ? { errorMessage: report.error ?? report.question } : {}),
213
- cwd,
214
- durationMs: Date.now() - startedAt,
215
- ...common(),
216
- };
217
- } else {
218
- const errorMessage = attempt.error instanceof Error ? attempt.error.message : String(attempt.error);
219
- const cancelled = attempt.status === "cancelled";
220
- const completedAt = new Date().toISOString();
221
- applyRunStatus(runRecord, cancelled ? "cancelled" : "failed", { at: completedAt, error: errorMessage });
222
- await Promise.all([writeJson(runFile, runRecord), writeJson(taskFile, taskRecord)]);
223
- result = cancelled
224
- ? { ...failure(cwd, startedAt, errorMessage, common()), status: "cancelled", summary: "Herdr Subagents cancelled.", output: `Herdr Subagents cancelled.\nError: ${errorMessage}` }
225
- : failure(cwd, startedAt, errorMessage, common());
226
- }
227
- } finally {
228
- if (tabId && result?.status !== "blocked") {
229
- await cleanupRunTab({ herdr, tabId, runFile, runRecord, signal: options.signal, onError: options.onCleanupError });
230
- }
231
- }
232
-
233
- return result ?? failure(cwd, startedAt, "Herdr Subagents ended without a result.", common());
234
- }