@maxiaochao/pi-toolkit 0.11.1 → 0.11.4
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 +19 -0
- package/package.json +1 -1
- package/packages/herdr-subagents/CHANGELOG.md +12 -0
- package/packages/herdr-subagents/README.md +2 -0
- package/packages/herdr-subagents/TESTING.md +5 -4
- package/packages/herdr-subagents/agents/analyst.md +1 -1
- package/packages/herdr-subagents/agents/explorer.md +1 -1
- package/packages/herdr-subagents/agents/reviewer.md +1 -1
- package/packages/herdr-subagents/agents/worker.md +1 -1
- package/packages/herdr-subagents/package.json +2 -1
- package/packages/herdr-subagents/src/live-agent.ts +4 -4
- package/packages/herdr-subagents/src/personas.ts +1 -0
- package/packages/herdr-subagents/tests/real-herdr-smoke.mjs +77 -0
- package/packages/herdr-subagents/tests/run-tests.mjs +19 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
## 2026-10-12 - v0.11.4
|
|
2
|
+
|
|
3
|
+
- bundle herdr-subagents v0.4.13: fix stale-idle child startup race and require real Herdr E2E release smoke
|
|
4
|
+
|
|
5
|
+
## 2026-10-12 - v0.11.3
|
|
6
|
+
|
|
7
|
+
- harden coordinated publishing and make local release checks authoritative
|
|
8
|
+
|
|
9
|
+
## Unreleased
|
|
10
|
+
|
|
11
|
+
- Fix Herdr Subagents startup failures caused by stale idle observations and require real child-Agent smoke tests before releasing that package.
|
|
12
|
+
|
|
13
|
+
- Replace split child/root publishing with one idempotent root-tag workflow, make final-version local checks authoritative, keep CI publication-only, and remove timing-sensitive Herdr lifecycle assertions.
|
|
14
|
+
|
|
15
|
+
## 2026-10-12 - v0.11.2
|
|
16
|
+
|
|
17
|
+
- bundle herdr-subagents v0.4.12: enable codemode for child agents while preserving persona tool boundaries
|
|
18
|
+
- Enable `codemode` for bundled Herdr Subagents children while preserving persona tool boundaries.
|
|
19
|
+
|
|
1
20
|
## 2026-10-12 - v0.11.1
|
|
2
21
|
|
|
3
22
|
- bundle herdr-subagents v0.4.11: add architecture documentation and clear-output guidance evaluation
|
package/package.json
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## 2026-10-12 - v0.4.13
|
|
2
|
+
|
|
3
|
+
- fix stale-idle child startup race and require real Herdr E2E release smoke
|
|
4
|
+
- Make lifecycle tests accept both valid report-first and observation-first execution orderings.
|
|
5
|
+
- Fix child startup being mistaken for completion when `before_agent_start` records the attempt as working before Herdr leaves its stale idle state.
|
|
6
|
+
- Add mandatory real-Herdr single-Agent and concurrent batch smoke tests to the Herdr Subagents release gate.
|
|
7
|
+
|
|
8
|
+
## 2026-10-12 - v0.4.12
|
|
9
|
+
|
|
10
|
+
- enable codemode for child agents while preserving persona tool boundaries
|
|
11
|
+
- Enable `codemode` for every child Pi while preserving each persona's existing tool allowlist and read/write boundary.
|
|
12
|
+
|
|
1
13
|
## 2026-10-12 - v0.4.11
|
|
2
14
|
|
|
3
15
|
- add architecture documentation and clear-output guidance evaluation
|
|
@@ -172,6 +172,8 @@ Each `personas.<name>` object supports exactly these overrides:
|
|
|
172
172
|
|
|
173
173
|
JSON persona overrides cannot change `name`, `description`, `access`, `tools`, or the system prompt; define those in persona Markdown.
|
|
174
174
|
|
|
175
|
+
Every child Pi explicitly loads and enables `codemode`. Built-in personas include it in their tool allowlists. Custom personas may omit `tools` and inherit Pi's defaults plus `codemode`, or list `codemode` explicitly alongside their allowed tools. A read-only persona remains read-only because codemode can call only the tools enabled for that child session.
|
|
176
|
+
|
|
175
177
|
A global configuration can establish shared defaults:
|
|
176
178
|
|
|
177
179
|
```json
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
1. `npm --prefix packages/herdr-subagents test` runs deterministic scheduler, recovery, and persona tests.
|
|
6
6
|
2. `pi -ne -ns --no-session -e ./packages/herdr-subagents/extensions/herdr-subagents.ts` verifies extension loading.
|
|
7
|
-
3. From a
|
|
8
|
-
4. `
|
|
9
|
-
5.
|
|
7
|
+
3. From a Pi session inside Herdr, run `npm --prefix packages/herdr-subagents run test:real-herdr`. This is a mandatory child-package and root-bundle release gate. It starts one real child Pi and then two concurrent read-only child Pi Agents, requires assistant events and matching attempt-scoped reports, and verifies final tab cleanup. `HERDR_SUBAGENTS_E2E_MODEL=provider/model` optionally pins the smoke model. Failed cleanup preserves the temporary durable records and fails the gate.
|
|
8
|
+
4. From a dedicated visible X11 Herdr client/window with the GStreamer X11 and VP8 plugins installed, run `HERDR_MEDIA_WINDOW_ID=<window-id> node scripts/capture-package-media.mjs`. Capture intentionally cycles through the live child panes, so avoid using that client/window for other work while recording; the script restores the previously focused pane afterward when it still exists. The Herdr capture must show a parent Pi conversation, a three-task run tab, each of the three real interactive Agent panes receiving focus in turn, the active parent Fleet footer with yellow running indicators and real metrics, and the final compact parent response. The published screenshot must be the parent Fleet view, while the full WebM preserves the parent → three focused children → Fleet → parent sequence.
|
|
9
|
+
5. `npm pack ./packages/herdr-subagents --dry-run` verifies package contents.
|
|
10
|
+
6. Verify the README renders the GitHub attachment video directly and live Herdr UI media `docs/screenshot.png` and `docs/demo.webm` are present in the tarball; `docs/demo.gif` must be absent. Inspect the WebM metadata and play it through once to confirm the parent → three focused children → Fleet → parent sequence.
|
|
10
11
|
|
|
11
12
|
## Cases
|
|
12
13
|
|
|
@@ -45,4 +46,4 @@
|
|
|
45
46
|
|
|
46
47
|
## Latest baseline
|
|
47
48
|
|
|
48
|
-
All 246 deterministic scheduler, recovery, process-boundary, schema-validation, navigation, metrics, responsive-formatting, lifecycle, attempt-isolation, and persona cases plus the extension-contract lifecycle simulations must pass.
|
|
49
|
+
All 246 deterministic scheduler, recovery, process-boundary, schema-validation, navigation, metrics, responsive-formatting, lifecycle, attempt-isolation, and persona cases plus the extension-contract lifecycle simulations must pass. The startup regression test reproduces the real ordering where `before_agent_start` records the attempt as working while Herdr can still report stale idle. Every release also runs the real single-Agent and concurrent read-only Herdr smoke; a skipped smoke is not a passing release check.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: analyst
|
|
3
3
|
description: Neutral read-only analyst for dynamically assigned roles
|
|
4
4
|
access: read
|
|
5
|
-
tools: read, bash, grep, find, ls
|
|
5
|
+
tools: read, bash, grep, find, ls, codemode
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
Act as an independent analyst and follow the role and evaluation method in the task prompt. Do not modify project files. Separate direct evidence, inference, and unknowns, and report concrete evidence with useful paths.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: explorer
|
|
3
3
|
description: Read-only explorer for fast codebase investigation
|
|
4
4
|
access: read
|
|
5
|
-
tools: read, bash, grep, find, ls
|
|
5
|
+
tools: read, bash, grep, find, ls, codemode
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
Investigate the assigned question without modifying files. Report concrete evidence and useful paths.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: reviewer
|
|
3
3
|
description: Read-only reviewer for correctness and maintainability risks
|
|
4
4
|
access: read
|
|
5
|
-
tools: read, bash, grep, find, ls
|
|
5
|
+
tools: read, bash, grep, find, ls, codemode
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
Review the assigned work without modifying files. Report actionable findings with concrete evidence.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maxiaochao/pi-herdr-subagents",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.13",
|
|
4
4
|
"description": "Run inspectable interactive Pi subagent batches through Herdr.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"test": "node --experimental-strip-types tests/run-tests.mjs",
|
|
41
|
+
"test:real-herdr": "node --experimental-strip-types tests/real-herdr-smoke.mjs",
|
|
41
42
|
"load-test": "env -u PI_HERDR_SUBAGENTS_CHILD pi -ne -ns --no-session -e ./tests/extension-contract.ts && env -u PI_HERDR_SUBAGENTS_CHILD pi -ne -ns --no-session -e ./extensions/herdr-subagents.ts"
|
|
42
43
|
},
|
|
43
44
|
"publishConfig": {
|
|
@@ -36,13 +36,15 @@ export function childPiArgs(task: LiveTaskRecord, systemPromptFile: string): str
|
|
|
36
36
|
"--session", task.sessionFile,
|
|
37
37
|
"--name", `Subagent: ${task.name ?? task.agent ?? "worker"}`,
|
|
38
38
|
"--no-extensions",
|
|
39
|
+
"--extension", "builtin:codemode",
|
|
39
40
|
"--extension", fileURLToPath(new URL("../extensions/subagent-report.ts", import.meta.url)),
|
|
40
41
|
"--no-skills",
|
|
41
42
|
"--append-system-prompt", systemPromptFile,
|
|
42
43
|
];
|
|
43
44
|
if (task.model) args.push("--model", task.model);
|
|
44
45
|
if (task.thinking) args.push("--thinking", task.thinking);
|
|
45
|
-
if (task.tools?.length) args.push("--tools", [...new Set([...task.tools, "subagent_report"])].join(","));
|
|
46
|
+
if (task.tools?.length) args.push("--tools", [...new Set([...task.tools, "codemode", "subagent_report"])].join(","));
|
|
47
|
+
else args.push("--tools", "+codemode");
|
|
46
48
|
for (const skill of task.skills ?? []) args.push("--skill", skill);
|
|
47
49
|
return args;
|
|
48
50
|
}
|
|
@@ -132,7 +134,6 @@ export async function promptLiveAgent(options: {
|
|
|
132
134
|
});
|
|
133
135
|
const stalledTimer = options.stalledWarningMs ? setTimeout(() => options.onStalled?.(), options.stalledWarningMs) : undefined;
|
|
134
136
|
let terminalObservedAt: number | undefined;
|
|
135
|
-
let turnObserved = false;
|
|
136
137
|
try {
|
|
137
138
|
await persistRuntime("working");
|
|
138
139
|
while (!existsSync(attemptReportFile)) {
|
|
@@ -142,7 +143,6 @@ export async function promptLiveAgent(options: {
|
|
|
142
143
|
try { agentState = await options.herdr.getAgentState(agentName, options.signal); }
|
|
143
144
|
catch (error) { observationDiagnostic = `Could not observe Herdr agent state: ${error instanceof Error ? error.message : String(error)}`; }
|
|
144
145
|
const attemptState = await readAttemptState();
|
|
145
|
-
if (agentState === "working" || agentState === "blocked" || attemptState === "working") turnObserved = true;
|
|
146
146
|
const promptDetail = promptError instanceof Error ? promptError.message : promptError === undefined ? "" : String(promptError);
|
|
147
147
|
if (agentState === "blocked" || /agent_blocked/i.test(promptDetail)) {
|
|
148
148
|
await persistRuntime("waiting_interaction", observationDiagnostic ?? "Herdr is waiting for an approval or interaction; no business input request has been reported.");
|
|
@@ -158,7 +158,7 @@ export async function promptLiveAgent(options: {
|
|
|
158
158
|
} else if (promptError && promptSettled) {
|
|
159
159
|
terminalObservedAt ??= Date.now();
|
|
160
160
|
if (Date.now() - terminalObservedAt >= 50) throw promptError;
|
|
161
|
-
} else if ((promptSettled ||
|
|
161
|
+
} else if ((promptSettled || attemptState === "settled") && (agentState === "idle" || agentState === "done" || attemptState === "settled")) {
|
|
162
162
|
if (attemptState === "working") {
|
|
163
163
|
let live = false;
|
|
164
164
|
try { live = await options.herdr.isTaskRunning(options.paneId, options.signal); } catch { live = true; }
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { executeHerdrSubagents } from "../src/tool.ts";
|
|
7
|
+
import { CliHerdrAutomation } from "../src/herdr.ts";
|
|
8
|
+
|
|
9
|
+
if (process.env.HERDR_ENV !== "1" || !process.env.HERDR_WORKSPACE_ID) {
|
|
10
|
+
throw new Error("The real Herdr smoke test must run from a Pi session inside Herdr.");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const packageDirectory = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
14
|
+
const root = mkdtempSync(join(tmpdir(), "pi-herdr-subagents-real-"));
|
|
15
|
+
const herdr = new CliHerdrAutomation();
|
|
16
|
+
const tabIds = new Set();
|
|
17
|
+
const model = process.env.HERDR_SUBAGENTS_E2E_MODEL?.trim();
|
|
18
|
+
const configurationPaths = {
|
|
19
|
+
globalConfig: join(root, "global-config.json"),
|
|
20
|
+
projectConfig: join(root, "project-config.json"),
|
|
21
|
+
globalAgents: join(root, "global-agents"),
|
|
22
|
+
projectAgents: join(root, "project-agents"),
|
|
23
|
+
};
|
|
24
|
+
const timeout = AbortSignal.timeout(120_000);
|
|
25
|
+
|
|
26
|
+
function hasAssistantMessage(path) {
|
|
27
|
+
return readFileSync(path, "utf8").split("\n").filter(Boolean).some((line) => {
|
|
28
|
+
try { return JSON.parse(line)?.message?.role === "assistant"; }
|
|
29
|
+
catch { return false; }
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function runCase(label, names, concurrency) {
|
|
34
|
+
const response = await executeHerdrSubagents({
|
|
35
|
+
label,
|
|
36
|
+
concurrency,
|
|
37
|
+
tasks: names.map((name) => ({
|
|
38
|
+
name,
|
|
39
|
+
agent: "analyst",
|
|
40
|
+
...(model ? { model } : {}),
|
|
41
|
+
prompt: `Do not inspect files. Call subagent_report with status completed, summary '${name} completed', and result 'hello'.`,
|
|
42
|
+
})),
|
|
43
|
+
}, timeout, (update) => {
|
|
44
|
+
if (update.details?.tabId) tabIds.add(update.details.tabId);
|
|
45
|
+
}, { cwd: root }, {
|
|
46
|
+
agentsDirectory: join(packageDirectory, "agents"),
|
|
47
|
+
configurationPaths,
|
|
48
|
+
herdr,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const result = response.details;
|
|
52
|
+
if (result?.tabId) tabIds.add(result.tabId);
|
|
53
|
+
assert.equal(response.isError, undefined, response.content?.[0]?.text);
|
|
54
|
+
assert.equal(result.status, "completed");
|
|
55
|
+
assert.equal(result.effectiveConcurrency, concurrency);
|
|
56
|
+
assert.equal(result.tasks.length, names.length);
|
|
57
|
+
assert.equal(new Set(result.tasks.map((task) => task.paneId)).size, names.length);
|
|
58
|
+
|
|
59
|
+
for (const taskResult of result.tasks) {
|
|
60
|
+
const task = JSON.parse(readFileSync(join(taskResult.recordDirectory, "task.json"), "utf8"));
|
|
61
|
+
const report = JSON.parse(readFileSync(join(taskResult.recordDirectory, "report.json"), "utf8"));
|
|
62
|
+
assert.equal(task.status, "completed");
|
|
63
|
+
assert.equal(report.status, "completed");
|
|
64
|
+
assert.equal(report.attemptId, task.currentAttemptId);
|
|
65
|
+
assert.equal(hasAssistantMessage(task.sessionFile), true, `${taskResult.name} produced no assistant event`);
|
|
66
|
+
assert.equal(await herdr.paneExists(task.paneId), false, `${taskResult.name} pane survived tab cleanup`);
|
|
67
|
+
}
|
|
68
|
+
console.log(`PASS ${label}: ${names.length} real child Agent${names.length === 1 ? "" : "s"} completed and cleaned up`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
await runCase("real-single", ["hello-single"], 1);
|
|
73
|
+
await runCase("real-concurrent", ["hello-one", "hello-two"], 2);
|
|
74
|
+
} finally {
|
|
75
|
+
for (const tabId of tabIds) await herdr.closeTab(tabId).catch(() => undefined);
|
|
76
|
+
rmSync(root, { recursive: true, force: true });
|
|
77
|
+
}
|
|
@@ -24,6 +24,7 @@ import { RunPaneAllocator } from "../src/run-pane-allocator.ts";
|
|
|
24
24
|
import { aggregateTaskStatus } from "../src/run-status.ts";
|
|
25
25
|
import { isAbortError } from "../src/errors.ts";
|
|
26
26
|
import { acquireDirectoryLock, withRunStateLock } from "../src/run-state.ts";
|
|
27
|
+
import { childPiArgs } from "../src/live-agent.ts";
|
|
27
28
|
import registerSubagentReport from "../extensions/subagent-report.ts";
|
|
28
29
|
|
|
29
30
|
/** Drives one task through the batch runner and projects its single result like a task-scoped run. */
|
|
@@ -457,13 +458,13 @@ try {
|
|
|
457
458
|
herdr,
|
|
458
459
|
});
|
|
459
460
|
check("compact successful report", success.ok && success.status === "completed" && success.summary === "Authentication review completed." && success.documents.length === 1 && !success.output.includes("FULL RESULT"));
|
|
460
|
-
check("Herdr lifecycle", herdr.calls.map(([name]) => name).join(",") === "createTab,renamePane,startAgent,promptAgent,
|
|
461
|
+
check("Herdr lifecycle", herdr.calls.map(([name]) => name).filter((name) => name !== "getAgentState").join(",") === "createTab,renamePane,startAgent,promptAgent,isTabFocused,closeTab");
|
|
461
462
|
const createRequest = herdr.calls.find(([name]) => name === "createTab")[1];
|
|
462
463
|
const startRequest = herdr.calls.find(([name]) => name === "startAgent")[1];
|
|
463
464
|
check("named unfocused tab in parent workspace", createRequest.workspaceId === "w1" && createRequest.cwd === root && createRequest.label === "SA · auth-review" && createRequest.focus === false);
|
|
464
465
|
check("ordinary execution never steals pane focus", !herdr.calls.some(([name]) => name === "focusPane"));
|
|
465
466
|
check("child environment points at durable task and parent pane", createRequest.env.PI_HERDR_SUBAGENTS_CHILD === "1" && createRequest.env.PI_HERDR_SUBAGENTS_TASK_DIR.includes(join(".pi", "herdr-subagents", "runs")) && createRequest.env.PI_HERDR_SUBAGENTS_PARENT_PANE_ID === "w1:p-parent");
|
|
466
|
-
check("persistent isolated child Pi", startRequest.kind === "pi" && startRequest.paneId === "w1:p2" && startRequest.args.includes("--session") && startRequest.args.includes("--no-extensions") && startRequest.args.includes("--extension") && startRequest.args.includes("--no-skills") && startRequest.args.includes("--model") && startRequest.args.includes("fake/model") && startRequest.args.includes("--tools") && startRequest.args.includes("read,grep,subagent_report"));
|
|
467
|
+
check("persistent isolated child Pi", startRequest.kind === "pi" && startRequest.paneId === "w1:p2" && startRequest.args.includes("--session") && startRequest.args.includes("--no-extensions") && startRequest.args.includes("builtin:codemode") && startRequest.args.includes("--extension") && startRequest.args.includes("--no-skills") && startRequest.args.includes("--model") && startRequest.args.includes("fake/model") && startRequest.args.includes("--tools") && startRequest.args.includes("read,grep,codemode,subagent_report"));
|
|
467
468
|
const promptRequest = herdr.calls.find(([name]) => name === "promptAgent")[1];
|
|
468
469
|
check("prompt submitted through Herdr", promptRequest.prompt === "Review authentication" && promptRequest.target === startRequest.name);
|
|
469
470
|
|
|
@@ -480,7 +481,7 @@ try {
|
|
|
480
481
|
model: { provider: "fake", id: "model" },
|
|
481
482
|
thinkingLevel: "medium",
|
|
482
483
|
}, { agentsDirectory: join(process.cwd(), "agents"), herdr: extensionHerdr });
|
|
483
|
-
check("public extension behavior uses injectable Herdr", !publicResult.isError && publicResult.content[0].text.includes("Authentication review completed.") && !publicResult.content[0].text.includes("FULL RESULT") && extensionHerdr.calls.map(([name]) => name).join(",") === "createTab,renamePane,startAgent,promptAgent,
|
|
484
|
+
check("public extension behavior uses injectable Herdr", !publicResult.isError && publicResult.content[0].text.includes("Authentication review completed.") && !publicResult.content[0].text.includes("FULL RESULT") && extensionHerdr.calls.map(([name]) => name).filter((name) => name !== "getAgentState").join(",") === "createTab,renamePane,startAgent,promptAgent,isTabFocused,closeTab", JSON.stringify({ publicResult, calls: extensionHerdr.calls.map(([name]) => name) }));
|
|
484
485
|
let singleStalledWarning = "";
|
|
485
486
|
await runSingleTask("single slow task", { cwd: root, herdr: fakeHerdr(), stalledWarningSeconds: 0.001, onStalled: (message) => { singleStalledWarning = message; } });
|
|
486
487
|
check("single-task execution emits configured stalled warning", singleStalledWarning.includes("appears stalled"));
|
|
@@ -494,8 +495,18 @@ try {
|
|
|
494
495
|
check("missing report is visible failure", !silent.ok && silent.errorMessage?.includes("did not submit") && silentHerdr.calls.at(-1)[0] === "closeTab");
|
|
495
496
|
const initialIdleHerdr = fakeHerdr();
|
|
496
497
|
const initialIdlePrompt = initialIdleHerdr.promptAgent.bind(initialIdleHerdr);
|
|
498
|
+
const initialIdleStart = initialIdleHerdr.startAgent.bind(initialIdleHerdr);
|
|
499
|
+
let initialIdleDirectory;
|
|
500
|
+
initialIdleHerdr.startAgent = async (request) => {
|
|
501
|
+
const sessionIndex = request.args.indexOf("--session");
|
|
502
|
+
initialIdleDirectory = join(request.args[sessionIndex + 1], "..");
|
|
503
|
+
return initialIdleStart(request);
|
|
504
|
+
};
|
|
497
505
|
let initialIdleAccepted = false;
|
|
498
506
|
initialIdleHerdr.promptAgent = async (request) => {
|
|
507
|
+
const task = JSON.parse(readFileSync(join(initialIdleDirectory, "task.json"), "utf8"));
|
|
508
|
+
mkdirSync(join(initialIdleDirectory, "attempts"), { recursive: true });
|
|
509
|
+
writeFileSync(join(initialIdleDirectory, "attempts", `${task.currentAttemptId}.json`), JSON.stringify({ attemptId: task.currentAttemptId, state: "working" }));
|
|
499
510
|
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
500
511
|
initialIdleAccepted = true;
|
|
501
512
|
return initialIdlePrompt(request);
|
|
@@ -570,6 +581,10 @@ try {
|
|
|
570
581
|
|
|
571
582
|
const definition = parseAgentMarkdown(`---\nname: reviewer\ndescription: Review code\nmodel: fake/model\ntools: read, grep\n---\nReview carefully.`, "reviewer.md");
|
|
572
583
|
check("persona frontmatter parsing", definition.name === "reviewer" && definition.model === "fake/model" && definition.tools?.join(",") === "read,grep" && definition.systemPrompt === "Review carefully.");
|
|
584
|
+
const inheritedToolArgs = childPiArgs({ id: "inherited", sessionFile: "/tmp/inherited.jsonl" }, "/tmp/system.md");
|
|
585
|
+
check("child Pi enables codemode when a persona inherits default tools", inheritedToolArgs.includes("builtin:codemode") && inheritedToolArgs[inheritedToolArgs.indexOf("--tools") + 1] === "+codemode");
|
|
586
|
+
const explicitToolArgs = childPiArgs({ id: "explicit", sessionFile: "/tmp/explicit.jsonl", tools: ["read"] }, "/tmp/system.md");
|
|
587
|
+
check("child Pi enables codemode with an explicit tool allowlist", explicitToolArgs.includes("builtin:codemode") && explicitToolArgs.includes("read,codemode,subagent_report"));
|
|
573
588
|
const agentsDir = join(root, "agents");
|
|
574
589
|
await import("node:fs/promises").then(({ mkdir }) => mkdir(agentsDir));
|
|
575
590
|
writeFileSync(join(agentsDir, "worker.md"), `---\nname: worker\ndescription: Worker\n---\nDo work.`);
|
|
@@ -578,7 +593,7 @@ try {
|
|
|
578
593
|
check("persona discovery and diagnostics", discovery.agents.length === 1 && discovery.agents[0].name === "worker" && discovery.diagnostics.length === 1);
|
|
579
594
|
const packageDiscovery = discoverPackageAgents(join(process.cwd(), "agents"));
|
|
580
595
|
const analystPersona = packageDiscovery.agents.find((persona) => persona.name === "analyst");
|
|
581
|
-
check("built-in analyst is neutral and read-only", packageDiscovery.diagnostics.length === 0 && analystPersona?.access === "read" && analystPersona.model === undefined && analystPersona.tools?.join(",") === "read,bash,grep,find,ls" && /independent analyst/.test(analystPersona.systemPrompt) && /direct evidence, inference, and unknowns/.test(analystPersona.systemPrompt) && !/auditor|replicator|adversary/.test(analystPersona.systemPrompt));
|
|
596
|
+
check("built-in analyst is neutral and read-only", packageDiscovery.diagnostics.length === 0 && analystPersona?.access === "read" && analystPersona.model === undefined && analystPersona.tools?.join(",") === "read,bash,grep,find,ls,codemode" && /independent analyst/.test(analystPersona.systemPrompt) && /direct evidence, inference, and unknowns/.test(analystPersona.systemPrompt) && !/auditor|replicator|adversary/.test(analystPersona.systemPrompt));
|
|
582
597
|
|
|
583
598
|
const configRoot = join(root, "configuration");
|
|
584
599
|
const packageAgents = join(configRoot, "package-agents");
|