@maxiaochao/pi-toolkit 0.11.13 → 0.11.15

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,14 @@
1
+ ## 2026-10-14 - v0.11.15
2
+
3
+ - openai-account-pool v0.1.5: make the current-account marker session-local and hide it until the session uses Codex
4
+ - make the OpenAI Account Pool `●当前` marker session-local and hide it before the session uses Codex
5
+
6
+ ## 2026-10-14 - v0.11.14
7
+
8
+ - herdr-subagents v0.5.1: fix focus-safe run cleanup and compact report output
9
+ - fix Herdr Subagents so completed child Pi panes remain inspectable while focused and close cleanly after focus leaves, without raw ANSI fragments from forced TUI exits
10
+ - keep large Subagent results on disk while showing compact report calls and one result path per task to the parent model
11
+
1
12
  ## 2026-10-14 - v0.11.13
2
13
 
3
14
  - openai-account-pool v0.1.4: hide the redundant next-session marker when it matches the current account
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maxiaochao/pi-toolkit",
3
- "version": "0.11.13",
3
+ "version": "0.11.15",
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": [
@@ -80,7 +80,7 @@ There is no global dispatcher, persisted background queue, blocked response lane
80
80
 
81
81
  ## Child boundary
82
82
 
83
- Each task owns one Herdr pane and one single-task Pi process. Herdr first starts and detects the interactive Pi, then submits the prompt exactly once. The child extension exits the process at Pi’s final `agent_settled` boundary, giving each task an unambiguous process lifetime without parent-side prompt retries.
83
+ Each task owns one Herdr pane and one single-task Pi process. Herdr first starts and detects the interactive Pi, then submits the prompt exactly once. At Pi’s final `agent_settled` boundary, the child extension records settled state but leaves the interactive process alive. The atomic report remains the authoritative task result. Early-finishing children remain visible while sibling tasks continue. Once the whole run settles, the orchestrator closes an unfocused owned tab immediately and defers a focused tab until the user leaves it, so Pi can restore the terminal normally.
84
84
 
85
85
  The child writes:
86
86
 
@@ -1,3 +1,9 @@
1
+ ## 2026-10-14 - v0.5.1
2
+
3
+ - fix focus-safe run cleanup and compact report output
4
+ - keep settled child Pi TUIs alive until focus-aware tab cleanup, preventing abrupt exits and leaked ANSI control sequences in focused panes
5
+ - render report submission compactly and return only each task's complete-result path to the parent model
6
+
1
7
  ## 2026-10-13 - v0.5.0
2
8
 
3
9
  - simplify lifecycle to one report-first state machine with strict serial and bounded read-only parallel execution
@@ -34,7 +34,7 @@ The reproducible source recording is retained as [`docs/demo.webm`](docs/demo.we
34
34
  }
35
35
  ```
36
36
 
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.
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
39
  ## Result protocol
40
40
 
@@ -45,6 +45,8 @@ The child must call `subagent_report` once with a terminal status:
45
45
 
46
46
  The complete result is stored on disk. The parent receives only the compact summary and document paths.
47
47
 
48
+ The child UI renders only the report status and saved size instead of echoing the complete result argument. The parent-facing tool result includes one complete-result path per task; additional consulted-document metadata remains in the durable report.
49
+
48
50
  An atomically renamed attempt report is the authoritative completion signal. Herdr states such as `idle`, `blocked`, and `done` are wake-up hints only. A prompt transport error or Pi retry cannot fail a task while its child process remains alive. If the process exits without a valid report, the task fails explicitly.
49
51
 
50
52
  Duplicate identical reports are idempotent. A conflicting report for the same task attempt is rejected. Terminal task records are immutable.
@@ -17,7 +17,8 @@ It verifies:
17
17
  - explicit failure when the process exits without a report;
18
18
  - parent cancellation of running and queued tasks;
19
19
  - immutable terminal task records;
20
- - idempotent duplicate reports and rejection of conflicting replacements.
20
+ - idempotent duplicate reports and rejection of conflicting replacements;
21
+ - keeping a settled child alive for focus-aware cleanup instead of force-exiting its TUI.
21
22
 
22
23
  The previous broad lifecycle suite remains available temporarily as `npm run test:legacy --workspace @maxiaochao/pi-herdr-subagents` for migration auditing. Scenarios for background queues, blocked/respond, and historical resume intentionally no longer describe the public contract.
23
24
 
@@ -33,4 +34,4 @@ Run the real Herdr smoke test from a Herdr session:
33
34
  npm run test:real-herdr --workspace @maxiaochao/pi-herdr-subagents
34
35
  ```
35
36
 
36
- For real smoke testing, verify that each child appears in a visible pane, can be focused and inspected, exits after one terminal report, and that the parent returns only after all requested tasks settle. Also verify cancellation closes the run tab and does not overwrite a report already atomically published.
37
+ For real smoke testing, verify that each child appears in a visible pane and can be focused and inspected. An early-finishing child must remain visible while sibling tasks continue. After the whole run settles, a focused child must remain at its restored Pi UI until focus leaves the run tab; the tab must then close automatically. Verify that the parent returns only after all requested tasks settle. Also verify cancellation closes the run tab and does not overwrite a report already atomically published.
@@ -3,6 +3,7 @@ import net from "node:net";
3
3
  import { dirname, resolve } from "node:path";
4
4
  import { StringEnum } from "@earendil-works/pi-ai";
5
5
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
+ import { Text } from "@earendil-works/pi-tui";
6
7
  import { Type } from "typebox";
7
8
  import { resultPathForAttemptId } from "../src/result-path.ts";
8
9
  import { PARENT_PANE_ENV } from "../src/live-agent.ts";
@@ -75,12 +76,11 @@ export default function (pi: ExtensionAPI) {
75
76
  const taskDirectory = process.env.PI_HERDR_SUBAGENTS_TASK_DIR?.trim();
76
77
  if (taskDirectory && activeAttemptId) {
77
78
  writeAttemptState(taskDirectory, activeAttemptId, "settled");
78
- const reported = existsSync(attemptReportPath(taskDirectory, activeAttemptId));
79
- // agent_settled is Pi's final automatic-work boundary. Ending the child here
80
- // gives the orchestrator an authoritative no-report process-exit signal while
81
- // still allowing all provider auto-retries to finish first.
82
- setTimeout(() => process.exit(reported ? 0 : 2), 25);
83
79
  }
80
+ // Keep the interactive child alive at Pi's final automatic-work boundary.
81
+ // The orchestrator settles each task from its atomic report. Once every task in
82
+ // the run settles, it closes the owned tab immediately when unfocused or waits
83
+ // for the user to leave it. Force-exiting here can interrupt TUI restoration.
84
84
  if (ctx?.isIdle?.() === true) reportHerdrState("idle");
85
85
  });
86
86
  pi.events.on("herdr:blocked", (data: { active?: boolean; label?: string }) => {
@@ -103,6 +103,19 @@ export default function (pi: ExtensionAPI) {
103
103
  label: "Subagent Report",
104
104
  description: "Finish this delegated task by saving its complete result and returning only a compact structured report to the parent.",
105
105
  parameters: ReportParams,
106
+ renderCall(params, theme) {
107
+ const resultBytes = Buffer.byteLength(params.result, "utf8");
108
+ const size = resultBytes < 1024 ? `${resultBytes} B` : `${(resultBytes / 1024).toFixed(1)} KiB`;
109
+ return new Text(`${theme.fg("toolTitle", theme.bold("subagent_report "))}${theme.fg(params.status === "completed" ? "success" : "error", params.status)}${theme.fg("dim", ` · ${size} saved to disk`)}`, 0, 0);
110
+ },
111
+ renderResult(result, { isPartial }, theme) {
112
+ if (isPartial) return new Text(theme.fg("warning", "Saving report..."), 0, 0);
113
+ const report = result.details as { status?: string; summary?: string; documents?: Array<{ path?: string }> } | undefined;
114
+ const resultPath = report?.documents?.[0]?.path;
115
+ const status = report?.status ?? "saved";
116
+ const summary = report?.summary?.replaceAll("\n", " ") ?? "Report saved.";
117
+ return new Text(`${theme.fg(status === "completed" ? "success" : status === "failed" ? "error" : "accent", status)}${theme.fg("dim", ` · ${summary}${resultPath ? `\n${resultPath}` : ""}`)}`, 0, 0);
118
+ },
106
119
  async execute(_toolCallId, params) {
107
120
  const taskDirectory = process.env.PI_HERDR_SUBAGENTS_TASK_DIR?.trim();
108
121
  if (!taskDirectory) throw new Error("PI_HERDR_SUBAGENTS_TASK_DIR is not set");
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maxiaochao/pi-herdr-subagents",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Run inspectable interactive Pi subagent batches through Herdr.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -159,8 +159,12 @@ async function executeBatch({ params, signal, onUpdate, ctx, dependencies, disco
159
159
  onCleanupError: (text) => onUpdate?.({ content: [{ type: "text", text }], details: { action: "batch", runId: dependencies.runId, cleanupError: text } }),
160
160
  onUpdate: (partial) => onUpdate?.({ content: [{ type: "text", text: partial.summary }], details: { ...partial, prompts: tasks.map((task) => task.prompt) } }),
161
161
  });
162
- const lines = [result.summary, ...result.tasks.map((task) => `- ${task.name}: ${task.status} — ${task.summary}`)];
163
- if (result.documents.length) lines.push("", "Documents:", ...result.documents.map((document) => `- ${document.description}: ${document.path}`));
162
+ const lines = [result.summary];
163
+ for (const task of result.tasks) {
164
+ lines.push(`- ${task.name}: ${task.status} — ${task.summary}`);
165
+ const resultDocument = task.documents[0];
166
+ if (resultDocument) lines.push(` Result: ${resultDocument.path}`);
167
+ }
164
168
  const isError = result.status === "failed" || result.status === "partial" || result.status === "cancelled";
165
169
  return { content: [{ type: "text", text: lines.join("\n") }], details: { ...result, prompts: tasks.map((task) => task.prompt) }, ...(isError ? { isError: true } : {}) };
166
170
  } catch (error) {
@@ -170,6 +170,24 @@ assert.throws(() => applyTaskStatus(terminal, "running"), /terminal tasks are im
170
170
  await reportTool.execute("first", report);
171
171
  await reportTool.execute("duplicate", report);
172
172
  await assert.rejects(() => reportTool.execute("conflict", { ...report, summary: "different" }), /Conflicting subagent_report/);
173
+ const originalExit = process.exit;
174
+ let exitCode;
175
+ process.exit = (code) => { exitCode = code; };
176
+ try {
177
+ await handlers.get("agent_settled")?.({}, { isIdle: () => true });
178
+ await new Promise((resolve) => setTimeout(resolve, 40));
179
+ } finally {
180
+ process.exit = originalExit;
181
+ }
182
+ assert.equal(exitCode, undefined, "settled child must remain alive for focus-aware tab cleanup");
183
+ const attempt = JSON.parse(readFileSync(join(directory, "attempts", `${attemptId}.json`), "utf8"));
184
+ assert.equal(attempt.state, "settled");
185
+ const theme = { fg: (_color, text) => text, bold: (text) => text };
186
+ const renderedCall = reportTool.renderCall({ ...report, result: "FULL RESULT MUST NOT RENDER" }, theme).render(120).join("\n");
187
+ assert.match(renderedCall, /completed.*saved to disk/);
188
+ assert.doesNotMatch(renderedCall, /FULL RESULT MUST NOT RENDER/);
189
+ const renderedResult = reportTool.renderResult(await reportTool.execute("render", report), { isPartial: false }, theme).render(120).join("\n");
190
+ assert.match(renderedResult, /completed.*done/);
173
191
  const persisted = JSON.parse(readFileSync(join(directory, "reports", `${attemptId}.json`), "utf8"));
174
192
  assert.equal(persisted.summary, "done");
175
193
  delete process.env.PI_HERDR_SUBAGENTS_TASK_DIR;