pi-daddy 0.15.0 → 0.16.0
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 +91 -0
- package/README.md +27 -12
- package/dist/cli.js +0 -0
- package/dist/executor.d.ts +38 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +93 -0
- package/dist/executor.js.map +1 -0
- package/dist/herdr-cli.d.ts +78 -0
- package/dist/herdr-cli.d.ts.map +1 -0
- package/dist/herdr-cli.js +113 -0
- package/dist/herdr-cli.js.map +1 -0
- package/dist/herdr-name.d.ts +37 -0
- package/dist/herdr-name.d.ts.map +1 -0
- package/dist/herdr-name.js +59 -0
- package/dist/herdr-name.js.map +1 -0
- package/dist/herdr-poll.d.ts +104 -0
- package/dist/herdr-poll.d.ts.map +1 -0
- package/dist/herdr-poll.js +150 -0
- package/dist/herdr-poll.js.map +1 -0
- package/dist/herdr-stage.d.ts +40 -0
- package/dist/herdr-stage.d.ts.map +1 -0
- package/dist/herdr-stage.js +54 -0
- package/dist/herdr-stage.js.map +1 -0
- package/dist/ledger-report.d.ts +18 -0
- package/dist/ledger-report.d.ts.map +1 -1
- package/dist/ledger-report.js +10 -0
- package/dist/ledger-report.js.map +1 -1
- package/dist/ledger.d.ts +17 -0
- package/dist/ledger.d.ts.map +1 -1
- package/dist/ledger.js +1 -0
- package/dist/ledger.js.map +1 -1
- package/dist/pane-reaper.d.ts +66 -4
- package/dist/pane-reaper.d.ts.map +1 -1
- package/dist/pane-reaper.js +131 -9
- package/dist/pane-reaper.js.map +1 -1
- package/dist/progress.d.ts +96 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +167 -0
- package/dist/progress.js.map +1 -0
- package/dist/run-child.d.ts +27 -0
- package/dist/run-child.d.ts.map +1 -1
- package/dist/run-child.js +84 -7
- package/dist/run-child.js.map +1 -1
- package/dist/run-herdr.d.ts +41 -28
- package/dist/run-herdr.d.ts.map +1 -1
- package/dist/run-herdr.js +150 -167
- package/dist/run-herdr.js.map +1 -1
- package/extensions/delegation.ts +94 -2
- package/extensions/grants-command.ts +26 -1
- package/extensions/grants.ts +85 -163
- package/extensions/run-delegation.ts +70 -8
- package/extensions/session-report.ts +231 -0
- package/extensions/session.ts +63 -11
- package/extensions/tripwire.ts +44 -0
- package/package.json +17 -1
- package/src/executor.ts +122 -0
- package/src/herdr-cli.ts +125 -0
- package/src/herdr-name.ts +61 -0
- package/src/herdr-poll.ts +185 -0
- package/src/herdr-stage.ts +55 -0
- package/src/ledger-report.ts +21 -0
- package/src/ledger.ts +18 -0
- package/src/pane-reaper.ts +147 -9
- package/src/progress.ts +206 -0
- package/src/run-child.ts +96 -7
- package/src/run-herdr.ts +170 -174
package/dist/run-herdr.js
CHANGED
|
@@ -20,67 +20,33 @@
|
|
|
20
20
|
* `bash` escape is unchanged, and a pane is *attachable by design*, so a human can type into a governed
|
|
21
21
|
* child. Humans are not this project's threat model, but nothing here should be read as containing one.
|
|
22
22
|
*/
|
|
23
|
-
import {
|
|
24
|
-
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
25
|
-
import { tmpdir } from "node:os";
|
|
26
|
-
import { join } from "node:path";
|
|
23
|
+
import { rm } from "node:fs/promises";
|
|
27
24
|
import { DEFAULT_MAX_OUTPUT_BYTES, DEFAULT_TIMEOUT_MS } from "./run-child.js";
|
|
28
|
-
import { trackPane, untrackPane } from "./pane-reaper.js";
|
|
25
|
+
import { MAX_OPEN_PANES, markPaneSettled, trackPane, trimOpenPanes, untrackPane } from "./pane-reaper.js";
|
|
26
|
+
import { defaultExec, parseReply } from "./herdr-cli.js";
|
|
27
|
+
import { readPane, waitForSettled } from "./herdr-poll.js";
|
|
28
|
+
import { stageSystemPrompt } from "./herdr-stage.js";
|
|
29
|
+
import { uniqueAgentName } from "./herdr-name.js";
|
|
29
30
|
/**
|
|
30
|
-
*
|
|
31
|
+
* Re-exported so importers of the executor still reach the protocol at the name they always used.
|
|
31
32
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* shell`. A definition's `SKILL.md` body is always multi-line, so every `delegate({agent})` spawn would
|
|
35
|
-
* fail on this path.
|
|
36
|
-
*
|
|
37
|
-
* pi accepts a **file path** there as readily as literal text (`resolvePromptInput` + `existsSync` in
|
|
38
|
-
* `dist/core/resource-loader.js`), so the fix is to write the body to a temp file and pass its path — one
|
|
39
|
-
* short, shell-safe argument.
|
|
40
|
-
*
|
|
41
|
-
* The split lives here rather than in `planSpawn` because the constraint is **herdr's**, not pi's: the
|
|
42
|
-
* direct executor passes the same text inline with no trouble, and a plan builder that pre-emptively wrote
|
|
43
|
-
* temp files for everybody would be paying one executor's tax on both paths.
|
|
33
|
+
* Deliberate rather than lazy: `test/run-herdr.test.ts` imports `HerdrExec` from here and must pass
|
|
34
|
+
* **unmodified** across this extraction — that is the only available proof the move changed no behaviour.
|
|
44
35
|
*/
|
|
45
|
-
export
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
/** Statuses herdr reports for a settled agent. `blocked` counts: it is waiting for a human, not working. */
|
|
52
|
-
const TERMINAL = new Set(["idle", "done", "blocked"]);
|
|
53
|
-
/** How often to poll `agent get` while waiting for the child to settle. */
|
|
54
|
-
export const POLL_INTERVAL_MS = 750;
|
|
55
|
-
/** How often to retry `agent start` while a freshly created pane is still reaching its shell prompt. */
|
|
56
|
-
export const PANE_READY_POLL_MS = 300;
|
|
57
|
-
const defaultExec = (args) => new Promise((settle) => {
|
|
58
|
-
execFile("herdr", args, { maxBuffer: 32 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
59
|
-
const code = error && typeof error.code === "number" ? error.code : error ? 1 : 0;
|
|
60
|
-
settle({ code, stdout: String(stdout), stderr: String(stderr) });
|
|
61
|
-
});
|
|
62
|
-
});
|
|
36
|
+
export { parseReply } from "./herdr-cli.js";
|
|
37
|
+
/** Re-exported: `splitSystemPrompt` moved to `./herdr-stage.ts` under the ceiling, its tests import it here. */
|
|
38
|
+
export { splitSystemPrompt, stageSystemPrompt } from "./herdr-stage.js";
|
|
39
|
+
/** Re-exported: the name rules moved to `./herdr-name.ts` under the ceiling; tests import them here. */
|
|
40
|
+
export { uniqueAgentName } from "./herdr-name.js";
|
|
63
41
|
/**
|
|
64
|
-
*
|
|
42
|
+
* Re-exported so `test/run-herdr.test.ts` and any importer keep reaching these where they always were.
|
|
65
43
|
*
|
|
66
|
-
* `
|
|
67
|
-
*
|
|
68
|
-
* hides the substrate's own error message costs more time than it saves.
|
|
44
|
+
* `POLL_INTERVAL_MS` and `newSuffix` moved to `./herdr-poll.ts` under the 400-line ceiling; the names are part
|
|
45
|
+
* of this module's surface and moving a file should not move an export.
|
|
69
46
|
*/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (parsed.error)
|
|
74
|
-
return { error: parsed.error.message ?? parsed.error.code ?? "herdr reported an error" };
|
|
75
|
-
return { result: parsed.result };
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
// A non-JSON reply is a herdr-version or PATH problem, not a governance decision. Surfaced as a spawn
|
|
79
|
-
// error so the caller reports "could not start" rather than "the child produced nothing".
|
|
80
|
-
const detail = [reply.stdout.trim(), reply.stderr.trim()].filter((t) => t.length > 0).join(" | ");
|
|
81
|
-
return { error: `unparseable herdr reply: ${detail.slice(0, 300) || "(no output)"}` };
|
|
82
|
-
}
|
|
83
|
-
}
|
|
47
|
+
export { DEFAULT_SNAPSHOT_LINES, POLL_INTERVAL_MS, readPane, tailLines, waitForSettled } from "./herdr-poll.js";
|
|
48
|
+
/** How often to retry `agent start` while a freshly created pane is still reaching its shell prompt. */
|
|
49
|
+
export const PANE_READY_POLL_MS = 300;
|
|
84
50
|
/**
|
|
85
51
|
* Run one governed child in a pane and return its output.
|
|
86
52
|
*
|
|
@@ -109,30 +75,31 @@ export async function runHerdrPane(request) {
|
|
|
109
75
|
"and never be detected. Build the plan with `print: false` for this executor.",
|
|
110
76
|
};
|
|
111
77
|
}
|
|
112
|
-
// A multi-line system prompt becomes a file; see `
|
|
113
|
-
//
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
promptDir = await mkdtemp(join(tmpdir(), "grants-herdr-"));
|
|
120
|
-
const file = join(promptDir, "system-prompt.md");
|
|
121
|
-
await writeFile(file, split.systemPrompt, "utf8");
|
|
122
|
-
effectiveArgs = [...split.args, "--append-system-prompt", file];
|
|
123
|
-
}
|
|
124
|
-
catch (error) {
|
|
125
|
-
return { ...empty, spawnError: `could not stage the system prompt for herdr: ${String(error)}` };
|
|
126
|
-
}
|
|
127
|
-
}
|
|
78
|
+
// A multi-line system prompt becomes a file; see `stageSystemPrompt`. Removed on every path that does not
|
|
79
|
+
// end up tracking a pane, because only a tracked pane's dir is reachable by a sweep.
|
|
80
|
+
const staged = await stageSystemPrompt(request.args);
|
|
81
|
+
if (staged.error)
|
|
82
|
+
return { ...empty, spawnError: staged.error };
|
|
83
|
+
const promptDir = staged.promptDir;
|
|
84
|
+
const effectiveArgs = staged.args;
|
|
128
85
|
const create = ["tab", "create", "--label", request.name, "--cwd", request.cwd];
|
|
129
86
|
if (request.workspace)
|
|
130
87
|
create.push("--workspace", request.workspace);
|
|
131
88
|
for (const [key, value] of Object.entries(request.env))
|
|
132
89
|
create.push("--env", `${key}=${value}`);
|
|
90
|
+
// The name herdr will actually know this agent by. Every later command in this function uses `agentName`,
|
|
91
|
+
// never `request.name`, which is only a base.
|
|
92
|
+
const agentName = uniqueAgentName(request.name);
|
|
133
93
|
const created = parseReply(await exec(create));
|
|
134
|
-
if (created.error)
|
|
94
|
+
if (created.error) {
|
|
95
|
+
// **The staged prompt is removed here too.** This path returned without touching it, and nothing tracked it,
|
|
96
|
+
// so neither sweep could ever reach it — a permanent `/tmp/grants-herdr-*` per failed `tab create`. Measured
|
|
97
|
+
// by a reviewer, who also found leftovers from earlier runs already sitting in `/tmp`. It is the most likely
|
|
98
|
+
// failure on a machine where herdr is not running, i.e. the common case.
|
|
99
|
+
if (promptDir)
|
|
100
|
+
await rm(promptDir, { recursive: true, force: true }).catch(() => undefined);
|
|
135
101
|
return { ...empty, spawnError: `herdr tab create failed: ${created.error}` };
|
|
102
|
+
}
|
|
136
103
|
const rootPane = (created.result?.root_pane ?? {});
|
|
137
104
|
const paneId = rootPane.pane_id;
|
|
138
105
|
const tabId = rootPane.tab_id;
|
|
@@ -140,8 +107,34 @@ export async function runHerdrPane(request) {
|
|
|
140
107
|
// so registering later leaves a window — one herdr round-trip wide — in which a killed process orphans a
|
|
141
108
|
// tab nothing would reap. The normal path had no such window and the error path did, which is backwards:
|
|
142
109
|
// the error path is the one more likely to be taken while something is already going wrong.
|
|
143
|
-
if (tabId &&
|
|
144
|
-
|
|
110
|
+
if (tabId && request.keepPane && promptDir) {
|
|
111
|
+
// `keepPane` keeps the PANE, and the staged prompt with it — a human inspecting the pane may want to see
|
|
112
|
+
// what the child was told. Registered `settled` so the trim never touches it, but registered nonetheless so
|
|
113
|
+
// the `exit` sweep can remove the temp dir rather than leaving one per kept pane forever. Measured: this
|
|
114
|
+
// path leaked a `/tmp/grants-herdr-*` that neither sweep could reach.
|
|
115
|
+
trackPane({ tab: tabId, name: agentName, promptDir, settled: true, keepTab: true });
|
|
116
|
+
}
|
|
117
|
+
if (tabId && !request.keepPane) {
|
|
118
|
+
trackPane({ tab: tabId, name: agentName, promptDir });
|
|
119
|
+
// ADR-0032: panes now live until `agent_settled`, so they accumulate across one agent run — and a plain
|
|
120
|
+
// blocking `delegate` spends nothing from the fan-out budget, so the count is otherwise unbounded. Trimmed
|
|
121
|
+
// here rather than at settle time so the bound holds DURING a long run, not only after it.
|
|
122
|
+
//
|
|
123
|
+
// Whatever is closed is reported through `onOutput` rather than dropped: an operator whose pane vanished
|
|
124
|
+
// while they were reading it deserves to know why (R-48's rule, applied to a display).
|
|
125
|
+
const dropped = await trimOpenPanes(exec);
|
|
126
|
+
if (dropped.length > 0 && request.onSnapshot) {
|
|
127
|
+
try {
|
|
128
|
+
request.onSnapshot([
|
|
129
|
+
`[grants] closed ${dropped.length} older pane(s) to stay within the ${MAX_OPEN_PANES}-pane limit: ` +
|
|
130
|
+
dropped.join(", "),
|
|
131
|
+
]);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
/* display only */
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
145
138
|
if (!paneId) {
|
|
146
139
|
// The tab may exist even though the reply carried no pane id, and this return used to be BEFORE
|
|
147
140
|
// `cleanup` was defined — so the one path where herdr half-succeeded was the one that leaked a tab.
|
|
@@ -152,65 +145,117 @@ export async function runHerdrPane(request) {
|
|
|
152
145
|
if (reply !== undefined && !parseReply(reply).error)
|
|
153
146
|
untrackPane(tabId);
|
|
154
147
|
}
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
// that away on the one path where there is no agent in the pane to ask instead.
|
|
148
|
+
// There is no agent in this pane to ask, so the staged prompt has no reader — remove it. Kept only under
|
|
149
|
+
// `keepPane`, where the operator asked to inspect whatever exists.
|
|
158
150
|
if (promptDir && !request.keepPane)
|
|
159
151
|
await rm(promptDir, { recursive: true, force: true }).catch(() => undefined);
|
|
160
152
|
return { ...empty, spawnError: "herdr tab create returned no pane id" };
|
|
161
153
|
}
|
|
162
|
-
/**
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
154
|
+
/**
|
|
155
|
+
* End this call. **A child that did not settle has its tab closed, because that is the only way to stop it.**
|
|
156
|
+
*
|
|
157
|
+
* ADR-0032 keeps a pane alive past its tool call so a human can read it. The original version of that also
|
|
158
|
+
* called `herdr agent stop` and claimed *"the agent is still stopped here… what survives is a terminal, not a
|
|
159
|
+
* running descendant."* **`herdr agent stop` does not exist.** Measured against herdr 0.7.5: the `agent`
|
|
160
|
+
* subcommands are `list get read send-keys prompt rename focus wait attach start explain`, and `agent stop`
|
|
161
|
+
* prints the usage banner and exits 0 — which `defaultExec` reports as a success, so nothing ever noticed.
|
|
162
|
+
* `docs/probes/g16-herdr/README.md` asserted it worked, in a *How to rerun* block that was never run.
|
|
163
|
+
*
|
|
164
|
+
* So there is exactly one kill available: closing the tab. That forces the distinction below.
|
|
165
|
+
*
|
|
166
|
+
* - **Settled cleanly** (`idle`/`done`/`blocked` with an advanced counter): the child has answered and is
|
|
167
|
+
* sitting idle. Its pane stays, which is the whole feature, and the `agent_settled` sweep reaps it.
|
|
168
|
+
* - **Anything else** — a timeout, an abort, a failed start, a failed prompt, an unreadable pane: the child
|
|
169
|
+
* may still be **working, with its grant**, after its tool call has already returned a result. Leaving
|
|
170
|
+
* that running is a governance failure, not an untidy workspace, so the tab is closed here and now.
|
|
171
|
+
*
|
|
172
|
+
* `keepPane` is the operator overriding the whole question, and it is honoured either way: nothing is tracked
|
|
173
|
+
* and nothing is closed, which is what "keep this pane for me to inspect" has to mean.
|
|
174
|
+
*/
|
|
175
|
+
const cleanup = async (settledCleanly) => {
|
|
176
|
+
if (request.keepPane)
|
|
177
|
+
return;
|
|
178
|
+
if (settledCleanly)
|
|
179
|
+
return;
|
|
180
|
+
if (!tabId)
|
|
181
|
+
return;
|
|
182
|
+
const reply = await exec(["tab", "close", tabId]).catch(() => undefined);
|
|
183
|
+
// Untracked only if provably closed — R-62's rule. A close herdr refused stays the reaper's problem rather
|
|
184
|
+
// than being dropped on the assumption that it worked.
|
|
185
|
+
if (reply !== undefined && !parseReply(reply).error) {
|
|
181
186
|
untrackPane(tabId);
|
|
187
|
+
if (promptDir)
|
|
188
|
+
await rm(promptDir, { recursive: true, force: true }).catch(() => undefined);
|
|
189
|
+
}
|
|
182
190
|
};
|
|
191
|
+
// ADR-0032: the pane exists and has a name, so tell the caller NOW. A pane id that arrives with the result is
|
|
192
|
+
// useless — the point is switching to a child while it is still working. Guarded because a renderer must not
|
|
193
|
+
// be able to strand a pane we have already created.
|
|
194
|
+
if (request.onPane) {
|
|
195
|
+
try {
|
|
196
|
+
request.onPane(paneId, agentName);
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
/* display only */
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Set only on the one path where the child answered and stopped working. Everything else — including an
|
|
203
|
+
// early `return` from a failed start or prompt — leaves it false, so `cleanup` closes the tab.
|
|
204
|
+
let settledCleanly = false;
|
|
183
205
|
try {
|
|
184
|
-
const started = await startAgent(exec,
|
|
206
|
+
const started = await startAgent(exec, agentName, paneId, effectiveArgs, deadline);
|
|
185
207
|
if (started.error)
|
|
186
208
|
return { ...empty, spawnError: `herdr agent start failed: ${started.error}` };
|
|
187
209
|
// The state counter BEFORE prompting is what makes the wait correct — see the R-33 note below.
|
|
188
210
|
const before = seqOf(started.result);
|
|
189
|
-
const prompted = parseReply(await exec(["agent", "prompt",
|
|
211
|
+
const prompted = parseReply(await exec(["agent", "prompt", agentName, request.prompt]));
|
|
190
212
|
if (prompted.error)
|
|
191
213
|
return { ...empty, spawnError: `herdr agent prompt failed: ${prompted.error}` };
|
|
192
|
-
const settled = await waitForSettled(exec, request, before, deadline);
|
|
214
|
+
const settled = await waitForSettled(exec, { ...request, name: agentName }, before, deadline, maxOutputBytes);
|
|
193
215
|
if (settled.aborted || settled.timedOut) {
|
|
194
216
|
// Still read: a timed-out child usually produced something, and a partial answer labelled partial is
|
|
195
217
|
// more useful than none. R-03's rule — a missing result must never look like an empty one.
|
|
196
|
-
const partial = await readPane(exec,
|
|
197
|
-
return { ...empty, ...settled, text: partial.text, truncated: partial.truncated };
|
|
218
|
+
const partial = await readPane(exec, agentName, maxOutputBytes);
|
|
219
|
+
return { ...empty, ...settled, text: partial.readFailed ? "" : partial.text, truncated: partial.truncated };
|
|
198
220
|
}
|
|
199
221
|
if (settled.spawnError)
|
|
200
222
|
return { ...empty, spawnError: settled.spawnError };
|
|
201
|
-
const out = await readPane(exec,
|
|
223
|
+
const out = await readPane(exec, agentName, maxOutputBytes);
|
|
224
|
+
// **A failed read is a failed spawn, not an empty answer.** This branch used to return `readPane`'s own
|
|
225
|
+
// diagnostic as `text` with `code: 0`, so the orchestrator received
|
|
226
|
+
// `[grants] could not read the agent pane: …` **as the sub-agent's report** — R-03 exactly, and measured.
|
|
227
|
+
// The child may well have done its work; what we cannot do is claim to know what it said.
|
|
228
|
+
if (out.readFailed) {
|
|
229
|
+
return {
|
|
230
|
+
...empty,
|
|
231
|
+
spawnError: `the child settled but its pane could not be read (${out.readFailed}), so its answer is unknown. ` +
|
|
232
|
+
`The work may have been done — check pane ${paneId} if it is still open.`,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
settledCleanly = true;
|
|
236
|
+
// The trim may now reclaim this pane if the cap is exceeded. Until this point it must not: closing the tab
|
|
237
|
+
// kills the child, and the child was still working.
|
|
238
|
+
if (tabId)
|
|
239
|
+
markPaneSettled(tabId);
|
|
202
240
|
return {
|
|
203
241
|
code: settled.status === "blocked" ? 1 : 0,
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
242
|
+
// `truncated` is returned in the result too, but it was never SAID on this path, so a model received a
|
|
243
|
+
// tail with no indication that a head existed. The direct executor has always flagged it; a delegation
|
|
244
|
+
// that silently drops the first megabyte of an answer is the same defect wearing the other executor.
|
|
245
|
+
text: [
|
|
246
|
+
out.truncated ? `[grants] this pane exceeded the output cap; only its most recent output is below.\n` : "",
|
|
247
|
+
out.text,
|
|
248
|
+
settled.status === "blocked"
|
|
249
|
+
? `\n\n[grants] this agent is BLOCKED waiting for a human in pane ${paneId}.`
|
|
250
|
+
: "",
|
|
251
|
+
].join(""),
|
|
207
252
|
truncated: out.truncated,
|
|
208
253
|
timedOut: false,
|
|
209
254
|
aborted: false,
|
|
210
255
|
};
|
|
211
256
|
}
|
|
212
257
|
finally {
|
|
213
|
-
await cleanup();
|
|
258
|
+
await cleanup(settledCleanly);
|
|
214
259
|
}
|
|
215
260
|
}
|
|
216
261
|
/**
|
|
@@ -241,66 +286,4 @@ function seqOf(result) {
|
|
|
241
286
|
const agent = (result?.agent ?? {});
|
|
242
287
|
return typeof agent.state_change_seq === "number" ? agent.state_change_seq : -1;
|
|
243
288
|
}
|
|
244
|
-
/**
|
|
245
|
-
* Wait for the child to settle, without accepting the state it was already in.
|
|
246
|
-
*
|
|
247
|
-
* **R-33, measured.** `herdr agent wait --until idle` called right after `agent prompt` returned
|
|
248
|
-
* *immediately*, matching the agent's **pre-existing** idle state with `state_change_seq` unchanged — a
|
|
249
|
-
* reply indistinguishable from a completed run. For fan-out that is not an inconvenience but a
|
|
250
|
-
* correctness bug: an orchestrator would "collect" N children that never ran and merge N empty results
|
|
251
|
-
* into a confident summary (R-03 with a new cause).
|
|
252
|
-
*
|
|
253
|
-
* So this polls `agent get` and requires **both** that the status is terminal **and** that
|
|
254
|
-
* `state_change_seq` has advanced past the value observed before prompting. `agent wait` is deliberately
|
|
255
|
-
* not used at all: its contract cannot express "settled *after* this point".
|
|
256
|
-
*/
|
|
257
|
-
async function waitForSettled(exec, request, before, deadline) {
|
|
258
|
-
for (;;) {
|
|
259
|
-
if (request.signal?.aborted)
|
|
260
|
-
return { aborted: true };
|
|
261
|
-
if (Date.now() >= deadline)
|
|
262
|
-
return { timedOut: true };
|
|
263
|
-
const reply = parseReply(await exec(["agent", "get", request.name]));
|
|
264
|
-
if (reply.error)
|
|
265
|
-
return { spawnError: `herdr agent get failed: ${reply.error}` };
|
|
266
|
-
const agent = (reply.result?.agent ?? reply.result ?? {});
|
|
267
|
-
const status = agent.agent_status;
|
|
268
|
-
const seq = typeof agent.state_change_seq === "number" ? agent.state_change_seq : -1;
|
|
269
|
-
if (status && TERMINAL.has(status) && seq > before)
|
|
270
|
-
return { status };
|
|
271
|
-
await new Promise((r) => setTimeout(r, Math.min(POLL_INTERVAL_MS, Math.max(0, deadline - Date.now()))));
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Read the pane's contents.
|
|
276
|
-
*
|
|
277
|
-
* `agent read` is the ONE command that does not return herdr's JSON envelope — it writes the terminal's
|
|
278
|
-
* text straight to stdout. Running it through `parseReply` turned every successful read into
|
|
279
|
-
* "unparseable herdr reply", i.e. reported the child's actual answer as a failure to read it. Found by the
|
|
280
|
-
* end-to-end run; the unit fake had been written to the envelope shape and so agreed with the bug.
|
|
281
|
-
*
|
|
282
|
-
* A JSON envelope is still accepted first, because an `error` reply here IS JSON and must not be mistaken
|
|
283
|
-
* for terminal output.
|
|
284
|
-
*/
|
|
285
|
-
async function readPane(exec, name, maxOutputBytes) {
|
|
286
|
-
const reply = await exec(["agent", "read", name]);
|
|
287
|
-
let text;
|
|
288
|
-
try {
|
|
289
|
-
const parsed = JSON.parse(reply.stdout);
|
|
290
|
-
if (parsed.error) {
|
|
291
|
-
return { text: `[grants] could not read the agent pane: ${parsed.error.message ?? "unknown error"}`, truncated: false };
|
|
292
|
-
}
|
|
293
|
-
const raw = parsed.result?.output ?? parsed.result?.text ?? parsed.result?.content ?? "";
|
|
294
|
-
text = typeof raw === "string" ? raw : JSON.stringify(raw);
|
|
295
|
-
}
|
|
296
|
-
catch {
|
|
297
|
-
text = reply.stdout;
|
|
298
|
-
}
|
|
299
|
-
if (Buffer.byteLength(text) <= maxOutputBytes)
|
|
300
|
-
return { text, truncated: false };
|
|
301
|
-
// Keep the TAIL, not the head: a terminal's useful content is its most recent output, and the head is
|
|
302
|
-
// the startup banner. `runChild` keeps the head because it streams and must stop a runaway producer;
|
|
303
|
-
// here the output is already complete, so the choice is free and the tail is the answer.
|
|
304
|
-
return { text: text.slice(-maxOutputBytes), truncated: true };
|
|
305
|
-
}
|
|
306
289
|
//# sourceMappingURL=run-herdr.js.map
|
package/dist/run-herdr.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-herdr.js","sourceRoot":"","sources":["../src/run-herdr.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"run-herdr.js","sourceRoot":"","sources":["../src/run-herdr.ts"],"names":[],"mappings":"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"}
|
package/extensions/delegation.ts
CHANGED
|
@@ -13,9 +13,90 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
13
13
|
import { Type } from "typebox";
|
|
14
14
|
import { maySpawnDefinition } from "../src/delegate.ts";
|
|
15
15
|
import { MAX_CHILDREN_PER_CALL, childSpawnId, splitBudget } from "../src/fanout.ts";
|
|
16
|
+
import {
|
|
17
|
+
PAINT_INTERVAL_MS,
|
|
18
|
+
appendTail,
|
|
19
|
+
emptyTail,
|
|
20
|
+
renderProgress,
|
|
21
|
+
replaceTail,
|
|
22
|
+
throttle,
|
|
23
|
+
type ChildProgress,
|
|
24
|
+
} from "../src/progress.ts";
|
|
16
25
|
import { runOneDelegation } from "./run-delegation.ts";
|
|
17
26
|
import { type GrantsSession } from "./session.ts";
|
|
18
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Wire a set of children to pi's partial-result channel — ADR-0032.
|
|
30
|
+
*
|
|
31
|
+
* **One block for the whole call**, not one per child: `onUpdate` replaces the tool's rendered result, so N
|
|
32
|
+
* independent painters would each overwrite the others and the operator would see one child flickering.
|
|
33
|
+
* `delegate` is simply the one-child case of the same thing, which is why both tools come through here.
|
|
34
|
+
*
|
|
35
|
+
* Painting is throttled, and the trailing frame matters more than the throttling: without it the final paint —
|
|
36
|
+
* the one showing every child settled — is the one most likely to be dropped, so the block would freeze
|
|
37
|
+
* mid-run. `flush()` at the end is what guarantees the last state is the one left on screen.
|
|
38
|
+
*
|
|
39
|
+
* **Nothing here is the result.** The tool's content still comes from each child's returned text.
|
|
40
|
+
*/
|
|
41
|
+
function progressReporter(
|
|
42
|
+
session: GrantsSession,
|
|
43
|
+
labels: string[],
|
|
44
|
+
onUpdate: ((partial: { content: Array<{ type: "text"; text: string }> }) => void) | undefined,
|
|
45
|
+
) {
|
|
46
|
+
const started = Date.now();
|
|
47
|
+
const children: ChildProgress[] = labels.map((label) => ({
|
|
48
|
+
label,
|
|
49
|
+
state: "starting",
|
|
50
|
+
startedAt: started,
|
|
51
|
+
tail: emptyTail,
|
|
52
|
+
}));
|
|
53
|
+
|
|
54
|
+
const paint = throttle(() => {
|
|
55
|
+
onUpdate?.({
|
|
56
|
+
content: [{ type: "text", text: renderProgress(children, session.executor.kind, Date.now()) }],
|
|
57
|
+
});
|
|
58
|
+
}, PAINT_INTERVAL_MS);
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
/** One sink per child index, shaped for `runOneDelegation`'s `onProgress`. */
|
|
62
|
+
sink: (index: number) => (update: {
|
|
63
|
+
chunk?: string;
|
|
64
|
+
snapshot?: string[];
|
|
65
|
+
paneId?: string;
|
|
66
|
+
agentName?: string;
|
|
67
|
+
state?: ChildProgress["state"];
|
|
68
|
+
}) => {
|
|
69
|
+
const child = children[index];
|
|
70
|
+
if (!child) return;
|
|
71
|
+
if (update.paneId) child.paneId = update.paneId;
|
|
72
|
+
// **Reported, not derived.** It used to be rebuilt here from the label and the child id, which was true
|
|
73
|
+
// until `runHerdrPane` began uniquifying the name to stop herdr's `agent_name_taken` — after which the
|
|
74
|
+
// block named an agent that did not exist, and an operator following it would find nothing. The name is
|
|
75
|
+
// known only where it is minted, so it travels from there.
|
|
76
|
+
if (update.agentName) child.agentName = update.agentName;
|
|
77
|
+
// Two shapes, deliberately distinct, because the two executors are: a subprocess APPENDS bytes, a pane
|
|
78
|
+
// reports a SNAPSHOT that replaces. Collapsing them into one "output" field is what caused the herdr
|
|
79
|
+
// path's amplification, so the difference is spelled out in the type rather than remembered.
|
|
80
|
+
if (update.chunk) child.tail = appendTail(child.tail, update.chunk);
|
|
81
|
+
if (update.snapshot) child.tail = replaceTail(update.snapshot);
|
|
82
|
+
if (update.state) child.state = update.state;
|
|
83
|
+
else if (child.state === "starting") child.state = "running";
|
|
84
|
+
paint.call();
|
|
85
|
+
},
|
|
86
|
+
/** Record how each child ended and leave the final frame on screen. */
|
|
87
|
+
settle: (outcomes: Array<{ ok: boolean }>) => {
|
|
88
|
+
const at = Date.now();
|
|
89
|
+
outcomes.forEach((outcome, index) => {
|
|
90
|
+
const child = children[index];
|
|
91
|
+
if (!child) return;
|
|
92
|
+
child.state = outcome.ok ? "completed" : "failed";
|
|
93
|
+
child.settledAt = at;
|
|
94
|
+
});
|
|
95
|
+
paint.flush();
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
19
100
|
export interface DelegationRegistration {
|
|
20
101
|
/**
|
|
21
102
|
* Re-derive "which definitions may this session spawn?" into the registered tool schemas.
|
|
@@ -113,7 +194,10 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
|
|
|
113
194
|
"written by the operator. Use 'tools' only when no definition fits. Grant 'delegate' if the " +
|
|
114
195
|
"sub-agent must itself delegate further; withhold it to make the sub-agent a leaf.",
|
|
115
196
|
parameters: delegateParams,
|
|
116
|
-
async execute(_toolCallId, params, signal,
|
|
197
|
+
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
198
|
+
// ADR-0032: one child, same block. `_onUpdate` was discarded here, so a delegation showed the bare word
|
|
199
|
+
// `delegate` for up to DEFAULT_TIMEOUT_MS — ten minutes of nothing.
|
|
200
|
+
const progress = progressReporter(session, [params.agent ?? "delegate"], onUpdate as never);
|
|
117
201
|
const outcome = await runOneDelegation(
|
|
118
202
|
session,
|
|
119
203
|
{ task: params.task, agent: params.agent, tools: params.tools, model: params.model },
|
|
@@ -124,7 +208,9 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
|
|
|
124
208
|
session.fanoutBudget,
|
|
125
209
|
ctx,
|
|
126
210
|
signal,
|
|
211
|
+
progress.sink(0),
|
|
127
212
|
);
|
|
213
|
+
progress.settle([outcome]);
|
|
128
214
|
|
|
129
215
|
if (!outcome.ok) {
|
|
130
216
|
// THROW, do not return. `AgentToolResult` has no `isError` field: pi sets it only when `execute`
|
|
@@ -168,7 +254,7 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
|
|
|
168
254
|
"when independent tasks can proceed in parallel — several reviewers over one diff, say — and read " +
|
|
169
255
|
"every child's outcome, because one can be refused while the others succeed.",
|
|
170
256
|
parameters: delegateAllParams,
|
|
171
|
-
async execute(_toolCallId, params, signal,
|
|
257
|
+
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
172
258
|
const children = params.children ?? [];
|
|
173
259
|
const split = splitBudget(session.fanoutBudget, children.length);
|
|
174
260
|
if (!split.ok) {
|
|
@@ -177,6 +263,10 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
|
|
|
177
263
|
throw new Error(`fan-out refused: ${split.reason}`);
|
|
178
264
|
}
|
|
179
265
|
|
|
266
|
+
// ADR-0032: ONE status block covering every child. `onUpdate` replaces the tool's rendered result, so a
|
|
267
|
+
// painter per child would have each overwriting the others.
|
|
268
|
+
const progress = progressReporter(session, children.map((c) => c.agent ?? "delegate"), onUpdate as never);
|
|
269
|
+
|
|
180
270
|
// Concurrent by construction. Each child gets its own budget share and its own ledger id, so the
|
|
181
271
|
// records form a tree and two siblings can never be confused for one another.
|
|
182
272
|
const outcomes = await Promise.all(
|
|
@@ -188,9 +278,11 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
|
|
|
188
278
|
split.perChild,
|
|
189
279
|
ctx,
|
|
190
280
|
signal,
|
|
281
|
+
progress.sink(index),
|
|
191
282
|
),
|
|
192
283
|
),
|
|
193
284
|
);
|
|
285
|
+
progress.settle(outcomes);
|
|
194
286
|
|
|
195
287
|
const failed = outcomes.filter((o) => !o.ok);
|
|
196
288
|
// Every child is reported, including the ones that failed. R-03's rule: a missing result must never
|