@maxiaochao/pi-toolkit 0.11.17 → 0.11.19
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 +8 -0
- package/global/AGENTS.md +0 -2
- package/package.json +1 -1
- package/packages/herdr-subagents/ARCHITECTURE.md +2 -2
- package/packages/herdr-subagents/CHANGELOG.md +4 -0
- package/packages/herdr-subagents/README.md +2 -0
- package/packages/herdr-subagents/TESTING.md +1 -0
- package/packages/herdr-subagents/extensions/herdr-subagents.ts +2 -17
- package/packages/herdr-subagents/package.json +1 -1
- package/packages/herdr-subagents/tests/core-state-machine.mjs +32 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## 2026-10-14 - v0.11.19
|
|
2
|
+
|
|
3
|
+
- herdr-subagents v0.5.2: stop Pi startup from scanning historical runs or closing recorded Herdr tabs
|
|
4
|
+
|
|
5
|
+
## 2026-10-14 - v0.11.18
|
|
6
|
+
|
|
7
|
+
- simplify global worktree isolation guidance
|
|
8
|
+
|
|
1
9
|
## 2026-10-14 - v0.11.17
|
|
2
10
|
|
|
3
11
|
- openai-account-pool v0.1.7: fix the session account marker after successful requests and failover
|
package/global/AGENTS.md
CHANGED
|
@@ -62,5 +62,3 @@ For frontend feature development, load the `web-browser` skill and verify the re
|
|
|
62
62
|
## 9. Worktree Isolation
|
|
63
63
|
|
|
64
64
|
For a Git-repository task that coordinates substantial changes across multiple files or modules, or implements a feature intended for release, ask before editing whether to isolate it in a worktree. Skip the question when already in a linked worktree.
|
|
65
|
-
|
|
66
|
-
If the user agrees, read the `pi-worktree` skill and establish the worktree before changing code. When no human is reachable, create a worktree only when the task explicitly requests one.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maxiaochao/pi-toolkit",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.19",
|
|
4
4
|
"description": "Personal pi coding agent toolkit: scheduler, /btw, /cache_export, apply_patch, browser automation, and bundled theme/skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
|
@@ -107,6 +107,6 @@ There is no fixed drain delay and no transcript parsing. Pi retries are internal
|
|
|
107
107
|
|
|
108
108
|
Writing the same report again is idempotent. A conflicting replacement is rejected.
|
|
109
109
|
|
|
110
|
-
## Cancellation and
|
|
110
|
+
## Cancellation and shutdown
|
|
111
111
|
|
|
112
|
-
Caller or parent-session cancellation aborts active waits, marks nonterminal tasks cancelled, and closes the owned
|
|
112
|
+
Caller or parent-session cancellation aborts active waits, marks nonterminal tasks cancelled, and closes the Herdr tab owned by that session. A new Pi session never scans, mutates, resumes, or closes historical runs during startup. If Pi is terminated before its shutdown handlers run, its visible Subagent tabs remain for explicit user inspection or cleanup.
|
|
@@ -36,6 +36,8 @@ The reproducible source recording is retained as [`docs/demo.webm`](docs/demo.we
|
|
|
36
36
|
|
|
37
37
|
Each task starts a single-task interactive Pi process in its own Herdr pane. Herdr first confirms the interactive Pi is ready, then the prompt is delivered exactly once. Prompt acknowledgement never decides task completion. Panes remain ordinary Herdr panes: they can be focused and inspected while work is running or after it settles. An early-finishing child stays at the Pi UI while sibling tasks continue. After the whole run settles, cleanup closes an unfocused run tab immediately or waits for the user to leave a focused run tab.
|
|
38
38
|
|
|
39
|
+
Cleanup belongs to the Pi session that created the run. Starting a new Pi session never scans historical runs or closes their recorded Herdr tabs. If Pi terminates before shutdown cleanup runs, any surviving Subagent tab remains visible for explicit inspection or cleanup.
|
|
40
|
+
|
|
39
41
|
## Result protocol
|
|
40
42
|
|
|
41
43
|
The child must call `subagent_report` once with a terminal status:
|
|
@@ -16,6 +16,7 @@ It verifies:
|
|
|
16
16
|
- waiting through a delayed authoritative report after a prompt transport error while the Pi process is alive;
|
|
17
17
|
- explicit failure when the process exits without a report;
|
|
18
18
|
- parent cancellation of running and queued tasks;
|
|
19
|
+
- startup isolation: a new Pi session neither mutates historical records nor closes their recorded Herdr tabs;
|
|
19
20
|
- immutable terminal task records;
|
|
20
21
|
- idempotent duplicate reports and rejection of conflicting replacements;
|
|
21
22
|
- keeping a settled child alive for focus-aware cleanup instead of force-exiting its TUI.
|
|
@@ -6,8 +6,7 @@ import { loadSubagentConfiguration } from "../src/config.ts";
|
|
|
6
6
|
import { CliHerdrAutomation, type HerdrAutomation } from "../src/herdr.ts";
|
|
7
7
|
import { activeRunText } from "../src/monitor.ts";
|
|
8
8
|
import { historyText, listSubagentHistory } from "../src/history.ts";
|
|
9
|
-
import {
|
|
10
|
-
import { cancelActiveSubagentRuns, reconcileSubagentRuns } from "../src/reconcile.ts";
|
|
9
|
+
import { cancelActiveSubagentRuns } from "../src/reconcile.ts";
|
|
11
10
|
import { executeHerdrSubagents, executeHerdrSubagentsControl, type HerdrSubagentsBatchDetails, type HerdrSubagentsControlToolParams, type HerdrSubagentsRunParams } from "../src/tool.ts";
|
|
12
11
|
import { HerdrSubagentsControlParams, HerdrSubagentsParams } from "../src/parameters.ts";
|
|
13
12
|
import { HERDR_ACTIONS } from "../src/validation.ts";
|
|
@@ -108,19 +107,6 @@ export interface HerdrSubagentsExtensionDependencies {
|
|
|
108
107
|
fleetWidgetRuntime?: FleetWidgetRuntime;
|
|
109
108
|
}
|
|
110
109
|
|
|
111
|
-
async function recoverParentSession(cwd: string, herdr: HerdrAutomation, owner: { sessionId?: string; processId: number }, notify: (message: string) => void): Promise<void> {
|
|
112
|
-
try {
|
|
113
|
-
const queued = await loadQueuedRuns(cwd, notify);
|
|
114
|
-
await cancelQueuedRuns(cwd, queued.map((run) => run.runId), { reason: "Owning parent Pi session is no longer active.", owner, onlyOrphaned: true, onError: notify });
|
|
115
|
-
}
|
|
116
|
-
catch (error) { notify(`Subagent queue recovery failed: ${error instanceof Error ? error.message : String(error)}`); }
|
|
117
|
-
try {
|
|
118
|
-
const reconciliation = await reconcileSubagentRuns(cwd, herdr, owner);
|
|
119
|
-
for (const message of reconciliation.cleanupErrors) notify(message);
|
|
120
|
-
}
|
|
121
|
-
catch (error) { notify(`Subagent recovery could not inspect active runs: ${error instanceof Error ? error.message : String(error)}`); }
|
|
122
|
-
}
|
|
123
|
-
|
|
124
110
|
async function shutdownParentSession(options: { cwd: string; herdr: HerdrAutomation; owner: { sessionId?: string; processId: number }; fleet: FleetController; sessionController: AbortController; runControllers: Map<string, AbortController>; inFlight: Set<Promise<unknown>>; notify: (message: string) => void }): Promise<void> {
|
|
125
111
|
options.sessionController.abort(new Error("Parent Pi session closed."));
|
|
126
112
|
for (const controller of options.runControllers.values()) controller.abort(new Error("Parent Pi session closed."));
|
|
@@ -146,12 +132,11 @@ export function registerHerdrSubagents(pi: ExtensionAPI, dependencies: HerdrSuba
|
|
|
146
132
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
147
133
|
await shutdownParentSession({ cwd: ctx.cwd, herdr, owner: { sessionId: ownerSessionId, processId: process.pid }, fleet, sessionController, runControllers, inFlight, notify: (message) => ctx.ui.notify(message, "error") });
|
|
148
134
|
});
|
|
149
|
-
pi.on("session_start",
|
|
135
|
+
pi.on("session_start", (_event, ctx) => {
|
|
150
136
|
sessionController.abort(new Error("Parent Pi session changed."));
|
|
151
137
|
sessionController = new AbortController();
|
|
152
138
|
ownerSessionId = `${ctx.sessionManager.getSessionId()}:${randomUUID()}`;
|
|
153
139
|
fleet.bind(ctx.ui, ctx.mode === "tui");
|
|
154
|
-
await recoverParentSession(ctx.cwd, herdr, { sessionId: ownerSessionId, processId: process.pid }, (message) => ctx.ui.notify(message, "error"));
|
|
155
140
|
});
|
|
156
141
|
|
|
157
142
|
pi.registerTool({
|
|
@@ -4,6 +4,7 @@ import { tmpdir } from "node:os";
|
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { runHerdrSubagentsBatch } from "../src/batch-runner.ts";
|
|
6
6
|
import registerSubagentReport from "../extensions/subagent-report.ts";
|
|
7
|
+
import { registerHerdrSubagents } from "../extensions/herdr-subagents.ts";
|
|
7
8
|
import { applyTaskStatus } from "../src/records.ts";
|
|
8
9
|
|
|
9
10
|
process.env.HERDR_ENV = "1";
|
|
@@ -150,6 +151,37 @@ await assert.rejects(() => run([task("writer", "edit", "write"), task("reader",
|
|
|
150
151
|
const terminal = { id: "task", runId: "run", order: 1, status: "completed" };
|
|
151
152
|
assert.throws(() => applyTaskStatus(terminal, "running"), /terminal tasks are immutable/);
|
|
152
153
|
|
|
154
|
+
{
|
|
155
|
+
const lifecycleRoot = join(root, "startup-history-isolation");
|
|
156
|
+
const runDirectory = join(lifecycleRoot, ".pi", "herdr-subagents", "runs", "historical-run");
|
|
157
|
+
const taskDirectory = join(runDirectory, "tasks", "01-task");
|
|
158
|
+
mkdirSync(taskDirectory, { recursive: true });
|
|
159
|
+
const historicalRun = { id: "historical-run", label: "historical", status: "completed", ownerSessionId: "old-session", ownerProcessId: 99999999, tabId: "w1:t-reused", taskIds: ["historical-task"], completedAt: "2026-10-01T00:00:00.000Z" };
|
|
160
|
+
writeFileSync(join(runDirectory, "run.json"), JSON.stringify(historicalRun));
|
|
161
|
+
writeFileSync(join(taskDirectory, "task.json"), JSON.stringify({ id: "historical-task", runId: "historical-run", order: 1, status: "completed", completedAt: historicalRun.completedAt }));
|
|
162
|
+
const events = new Map();
|
|
163
|
+
const herdrCalls = [];
|
|
164
|
+
registerHerdrSubagents({
|
|
165
|
+
on(name, handler) { events.set(name, handler); },
|
|
166
|
+
registerTool() {},
|
|
167
|
+
registerCommand() {},
|
|
168
|
+
}, {
|
|
169
|
+
herdr: {
|
|
170
|
+
async isTabFocused(tabId) { herdrCalls.push(["isTabFocused", tabId]); return false; },
|
|
171
|
+
async closeTab(tabId) { herdrCalls.push(["closeTab", tabId]); },
|
|
172
|
+
},
|
|
173
|
+
isEditor: () => false,
|
|
174
|
+
});
|
|
175
|
+
await events.get("session_start")?.({}, {
|
|
176
|
+
cwd: lifecycleRoot,
|
|
177
|
+
mode: "rpc",
|
|
178
|
+
sessionManager: { getSessionId: () => "new-session" },
|
|
179
|
+
ui: { notify() {} },
|
|
180
|
+
});
|
|
181
|
+
assert.deepEqual(herdrCalls, [], "Pi startup must not inspect or close historical Subagent tabs");
|
|
182
|
+
assert.deepEqual(JSON.parse(readFileSync(join(runDirectory, "run.json"), "utf8")), historicalRun, "Pi startup must not mutate historical Subagent records");
|
|
183
|
+
}
|
|
184
|
+
|
|
153
185
|
{
|
|
154
186
|
const directory = join(root, "report-idempotency");
|
|
155
187
|
mkdirSync(directory, { recursive: true });
|