@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 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.17",
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 recovery
110
+ ## Cancellation and shutdown
111
111
 
112
- Caller or parent-session cancellation aborts active waits, marks nonterminal tasks cancelled, and closes the owned Herdr tab. Startup recovery is scoped by recorded parent ownership and retains readers for prior record formats. Recovery never resumes or mutates an already terminal task.
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.
@@ -1,3 +1,7 @@
1
+ ## 2026-10-14 - v0.5.2
2
+
3
+ - stop Pi startup from scanning historical runs or closing recorded Herdr tabs
4
+
1
5
  ## 2026-10-14 - v0.5.1
2
6
 
3
7
  - fix focus-safe run cleanup and compact report output
@@ -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 { cancelQueuedRuns, loadQueuedRuns } from "../src/queued-runs.ts";
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", async (_event, ctx) => {
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({
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maxiaochao/pi-herdr-subagents",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Run inspectable interactive Pi subagent batches through Herdr.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -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 });