@maxiaochao/pi-toolkit 0.10.5 → 0.10.6
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 +7 -0
- package/README.md +26 -4
- package/package.json +3 -5
- package/packages/herdr-subagents/CHANGELOG.md +6 -0
- package/packages/herdr-subagents/README.md +12 -1
- package/packages/herdr-subagents/TESTING.md +4 -3
- package/packages/herdr-subagents/agents/analyst.md +8 -0
- package/packages/herdr-subagents/extensions/herdr-subagents.ts +1 -1
- package/packages/herdr-subagents/package.json +1 -1
- package/packages/herdr-subagents/src/parameters.ts +2 -2
- package/packages/herdr-subagents/src/status.ts +95 -0
- package/packages/herdr-subagents/src/tool.ts +13 -2
- package/packages/herdr-subagents/src/validation.ts +2 -1
- package/packages/herdr-subagents/tests/extension-contract.ts +4 -0
- package/packages/herdr-subagents/tests/run-tests.mjs +40 -2
- package/skills/agent-team/SKILL.md +33 -57
- package/skills/agent-team/models.json +19 -12
- package/packages/todo/CHANGELOG.md +0 -9
- package/packages/todo/README.md +0 -73
- package/packages/todo/TESTING.md +0 -22
- package/packages/todo/docs/demo.gif +0 -0
- package/packages/todo/docs/screenshot.png +0 -0
- package/packages/todo/extensions/todo.ts +0 -279
- package/packages/todo/package.json +0 -25
- package/packages/todo/src/todo-state.ts +0 -159
- package/packages/todo/tests/todo-extension.mjs +0 -134
- package/packages/todo/tests/todo-state.mjs +0 -108
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## 2026-10-09 - v0.10.6
|
|
2
|
+
|
|
3
|
+
- bundle herdr-subagents v0.4.3: add per-run status, neutral analyst persona, and tool-agnostic Agent Team protocol
|
|
4
|
+
- Add per-run Herdr Subagents status queries so background review gates can inspect one run without polling the full history archive.
|
|
5
|
+
- Make Agent Team a tool-agnostic concurrent review protocol, decouple models from rotating methodological roles, and add a neutral read-only analyst persona.
|
|
6
|
+
- Remove Todo from the default toolkit bundle and loadout; users who want its progress UI can install `@maxiaochao/pi-todo` explicitly.
|
|
7
|
+
|
|
1
8
|
## 2026-10-08 - v0.10.5
|
|
2
9
|
|
|
3
10
|
- bundle herdr-subagents v0.4.2: add Alt+P and /parent controls to return from child Agents
|
package/README.md
CHANGED
|
@@ -16,13 +16,21 @@ Update it:
|
|
|
16
16
|
pi update npm:@maxiaochao/pi-toolkit
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
The root package embeds
|
|
19
|
+
The root package embeds its enabled child-package source snapshots. Todo is intentionally excluded from the default toolkit and remains available as an opt-in standalone package.
|
|
20
|
+
|
|
21
|
+
Existing toolkit users can pick up the change with:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pi update npm:@maxiaochao/pi-toolkit
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Restart Pi after updating. The `todo` tool and widget will no longer load. `pi update --extensions` has the same effect when updating all installed packages. Explicitly versioned sources remain pinned and must be changed or reinstalled without the old version suffix.
|
|
20
28
|
|
|
21
29
|
## Child packages
|
|
22
30
|
|
|
23
31
|
| Package | Capability | Install | Update |
|
|
24
32
|
|---|---|---|---|
|
|
25
|
-
| [pi-todo](packages/todo/README.md) |
|
|
33
|
+
| [pi-todo](packages/todo/README.md) | Optional branch-local execution plan UI; not bundled with pi-toolkit | `pi install npm:@maxiaochao/pi-todo` | `pi update npm:@maxiaochao/pi-todo` |
|
|
26
34
|
| [pi-cache-export](packages/cache-export/README.md) | Tau-aligned interactive cache dashboard for Pi sessions | `pi install npm:@maxiaochao/pi-cache-export` | `pi update npm:@maxiaochao/pi-cache-export` |
|
|
27
35
|
| [pi-codex-edit](packages/codex-edit/README.md) | Codex-harness `apply_patch` routing for selected GPT models | `pi install npm:@maxiaochao/pi-codex-edit` | `pi update npm:@maxiaochao/pi-codex-edit` |
|
|
28
36
|
| [pi-herdr-subagents](packages/herdr-subagents/README.md) | Interactive Herdr Pi Agents with bounded concurrency, direct navigation, and recovery | `pi install npm:@maxiaochao/pi-herdr-subagents` | `pi update npm:@maxiaochao/pi-herdr-subagents` |
|
|
@@ -30,7 +38,21 @@ The root package embeds the child-package source snapshots listed below. Install
|
|
|
30
38
|
|
|
31
39
|
Each child README owns its feature description, motivation or upstream alignment, examples, screenshots/demos, configuration, and test documentation.
|
|
32
40
|
|
|
33
|
-
Multiple standalone children may be installed together. Do not install
|
|
41
|
+
Multiple standalone children may be installed together. `pi-todo` may also be installed alongside the complete toolkit because Todo is no longer bundled. Do not install another bundled child alongside the complete toolkit because that exposes the same resource twice.
|
|
42
|
+
|
|
43
|
+
To keep Todo after updating the toolkit:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pi install npm:@maxiaochao/pi-todo
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
If Todo was already installed separately and should be removed completely:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pi remove npm:@maxiaochao/pi-todo
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Use `--local` with install or remove when the package declaration belongs to a project's `.pi/settings.json` rather than personal settings.
|
|
34
56
|
|
|
35
57
|
Root and child versions remain independent, but repository release tooling publishes a modified child and a new toolkit snapshot from the same commit. Run `pi update --extensions` to update all installed packages. Explicit version sources such as `npm:@maxiaochao/pi-toolkit@0.9.3` remain pinned.
|
|
36
58
|
|
|
@@ -64,7 +86,7 @@ bash scripts/install.sh
|
|
|
64
86
|
| `html-artifact` | Build self-contained HTML explainers and diagrams |
|
|
65
87
|
| `pdlog` | Decompress and inspect Pudu `.pdlog` files |
|
|
66
88
|
| `herdr` | Control Herdr workspaces, tabs, panes, and agents |
|
|
67
|
-
| `agent-team` | Run cross-vendor adversarial review
|
|
89
|
+
| `agent-team` | Run tool-agnostic, cross-vendor adversarial review with independent concurrent agents |
|
|
68
90
|
| `tapd` | Use the TAPD CLI for requirements, defects, tasks, and wikis |
|
|
69
91
|
|
|
70
92
|
Package-specific skills, such as `pi-worktree`, are documented by their child package.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maxiaochao/pi-toolkit",
|
|
3
|
-
"version": "0.10.
|
|
4
|
-
"description": "Personal pi coding agent toolkit:
|
|
3
|
+
"version": "0.10.6",
|
|
4
|
+
"description": "Personal pi coding agent toolkit: /btw, /cache_export and apply_patch extensions, browser automation, and bundled theme/skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
|
7
7
|
"packages/*"
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
],
|
|
13
13
|
"files": [
|
|
14
14
|
"extensions",
|
|
15
|
-
"packages/todo",
|
|
16
15
|
"packages/cache-export",
|
|
17
16
|
"packages/codex-edit",
|
|
18
17
|
"packages/herdr-subagents",
|
|
@@ -30,7 +29,6 @@
|
|
|
30
29
|
},
|
|
31
30
|
"pi": {
|
|
32
31
|
"extensions": [
|
|
33
|
-
"./packages/todo/extensions/todo.ts",
|
|
34
32
|
"./packages/cache-export/extensions/cache-export.ts",
|
|
35
33
|
"./extensions/btw/index.ts",
|
|
36
34
|
"./packages/codex-edit/extensions/codex-edit.ts",
|
|
@@ -58,7 +56,7 @@
|
|
|
58
56
|
},
|
|
59
57
|
"scripts": {
|
|
60
58
|
"test": "node tests/package-layout.mjs && npm --prefix packages/todo test && npm --prefix packages/cache-export test && npm --prefix packages/codex-edit test && npm --prefix packages/herdr-subagents test && npm --prefix packages/worktree test && node --experimental-strip-types extensions/btw/tests/history.mjs",
|
|
61
|
-
"load-test": "pi -ne -e ./packages/
|
|
59
|
+
"load-test": "pi -ne -e ./packages/cache-export/extensions/cache-export.ts && pi -ne -e ./extensions/btw/index.ts && pi -ne -e ./packages/codex-edit/extensions/codex-edit.ts && pi -ne -e ./packages/herdr-subagents/extensions/herdr-subagents.ts",
|
|
62
60
|
"eval:agents": "node evaluations/agents-md/run.mjs",
|
|
63
61
|
"eval:agents:test": "node evaluations/agents-md/tests/run-tests.mjs",
|
|
64
62
|
"postinstall": "node scripts/install-agents.mjs"
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## 2026-10-09 - v0.4.3
|
|
2
|
+
|
|
3
|
+
- add per-run status, neutral analyst persona, and tool-agnostic Agent Team protocol
|
|
4
|
+
- Add `herdr_subagents_control({ action: "status", runId })` for a compact snapshot of one queued, active, blocked, or settled run without loading the full history archive.
|
|
5
|
+
- Add a neutral read-only `analyst` persona for tasks that assign models and methodological roles dynamically.
|
|
6
|
+
|
|
1
7
|
## 2026-10-08 - v0.4.2
|
|
2
8
|
|
|
3
9
|
- add Alt+P and /parent controls to return from child Agents
|
|
@@ -66,6 +66,14 @@ herdr_subagents({
|
|
|
66
66
|
|
|
67
67
|
`tasks` is always a non-empty array: one item is a single task and multiple items form a batch. Set `background: true` for either form to return a stable run ID immediately while the queue continues. One run is dispatched at a time by an extension instance; later runs remain queued and start FIFO. Each background run sends one compact parent notification only when the whole run completes, partially fails, fails, or blocks. Individual successful tasks do not wake the parent model.
|
|
68
68
|
|
|
69
|
+
Background work normally relies on that terminal notification. When the user asks for progress or the parent must confirm a review gate before continuing, query only that run's compact snapshot:
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
herdr_subagents_control({ action: "status", runId: "<run-id>" })
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The snapshot reports the aggregate run state and every task's state, including a blocked question when present. It checks durable run state before queued state, never returns unrelated runs, and does not wait or trigger a parent turn. Do not poll full `history` for progress.
|
|
76
|
+
|
|
69
77
|
A child missing required information reports `needs-input` with an exact question. The run becomes blocked, keeps its Herdr tab, pane, partial report, and Pi session, and returns control to the parent. Answer through the control tool to continue the original session:
|
|
70
78
|
|
|
71
79
|
```text
|
|
@@ -80,7 +88,7 @@ With an empty editor, press `↓` to enter the list, `↑`/`↓` to choose a tas
|
|
|
80
88
|
|
|
81
89
|
## History
|
|
82
90
|
|
|
83
|
-
Run `/herdr-subagents history` or `herdr_subagents_control({ action: "history" })` to list compact project-local run and task
|
|
91
|
+
Run `/herdr-subagents history` or `herdr_subagents_control({ action: "history" })` to list the full compact project-local run and task archive after Herdr tabs close. History does not accept a run ID and is not a status interface. Full results, described documents, artifacts, and persistent Pi sessions remain separate on disk and are not loaded into parent context.
|
|
84
92
|
|
|
85
93
|
Continue a saved conversation in a new Herdr tab with `herdr_subagents_control({ action: "resume", runId: "<run-id>", task: 1, prompt: "Follow up..." })`. Remove only a selected archive with `herdr_subagents_control({ action: "cleanup", runId: "<run-id>" })`. History has no automatic count-based eviction. The extension adds the runtime path to local Git excludes when available (and otherwise uses `.pi/.gitignore`) without overwriting unrelated rules.
|
|
86
94
|
|
|
@@ -192,6 +200,9 @@ The package includes these personas:
|
|
|
192
200
|
| `worker` | write | `read`, `bash`, `edit`, `write`, `grep`, `find`, `ls` | Focused implementation work |
|
|
193
201
|
| `explorer` | read | `read`, `bash`, `grep`, `find`, `ls` | Fast codebase investigation |
|
|
194
202
|
| `reviewer` | read | `read`, `bash`, `grep`, `find`, `ls` | Correctness and maintainability review |
|
|
203
|
+
| `analyst` | read | `read`, `bash`, `grep`, `find`, `ls` | Neutral analysis with the method and role supplied by each task |
|
|
204
|
+
|
|
205
|
+
`analyst` deliberately does not bind a model or review method. Use the task-level `model` field when independent tasks need different models, and put roles such as auditor, replicator, or adversary in each task prompt.
|
|
195
206
|
|
|
196
207
|
Persona definitions use Markdown frontmatter followed by the child system prompt. For example, save this as `.pi/herdr-subagents/agents/security-reviewer.md`:
|
|
197
208
|
|
|
@@ -10,18 +10,19 @@
|
|
|
10
10
|
|
|
11
11
|
## Cases
|
|
12
12
|
|
|
13
|
-
- Resolve package, global, and project personas and validate frontmatter/configuration.
|
|
13
|
+
- Resolve package, global, and project personas and validate frontmatter/configuration, including the neutral read-only `analyst` persona.
|
|
14
14
|
- Run foreground tasks and FIFO bounded-concurrency batches through an injectable Herdr boundary.
|
|
15
15
|
- Verify all paths use interactive Agent start/prompt without print mode, shell injection, markers, or pane reuse.
|
|
16
|
-
- Force write-capable batches to serial execution while allowing bounded read-only parallelism.
|
|
16
|
+
- Force write-capable batches to serial execution while allowing bounded read-only parallelism, including three concurrent `analyst` tasks with independent task-level models.
|
|
17
17
|
- Preserve compact reports, full results, Pi sessions, blocked questions, and historical follow-ups.
|
|
18
18
|
- Recover queued/running work after interruption without retrying post-prompt side effects, including truncated reports and recovery errors.
|
|
19
19
|
- Verify the split public contracts (`herdr_subagents` with required `tasks`, plus `herdr_subagents_control` with required `action`) and injectable extension wiring, including conditional registration only inside Herdr, Fleet rendering, editor focus ownership, terminal-input passthrough, exact-pane navigation, child `/parent` and `Alt+P` return controls, and stable display while another background run queues.
|
|
20
20
|
- Verify Fleet divider/row formatting, warning/yellow running indicators, success/green completed indicators, persona and task projections, cumulative active duration across blocking/resume, exact Pi usage aggregation from assistant messages, tool results, standalone usage, compaction, and branch summaries, malformed/truncated JSONL tolerance, queued metric omission, and visible-column bounds at narrow widths.
|
|
21
21
|
- Verify live token updates are file-event driven and polled on each running one-second tick, elapsed repainting exists only while work runs, and widget disposal closes watchers and timers.
|
|
22
22
|
- Verify selected active/queued run cancellation, blocked-response live Fleet updates, authoritative stale-field replacement, parent-shutdown cancellation, idempotent missing-tab cleanup, cleanup notifications, atomic state replacement, non-retried uncertain startup operations, and stalled warnings.
|
|
23
|
+
- Verify `status(runId)` isolates one run, projects queue-only state, prefers durable state over a stale queue record, exposes blocked questions, marks terminal states settled, rejects unknown IDs, and leaves full `history` semantics unchanged.
|
|
23
24
|
- Verify resumed batches retain newly allocated pane IDs and historical follow-ups exclude simultaneous session writers.
|
|
24
25
|
|
|
25
26
|
## Latest baseline
|
|
26
27
|
|
|
27
|
-
All
|
|
28
|
+
All 179 deterministic scheduler, recovery, process-boundary, schema-validation, navigation, metrics, responsive-formatting, and persona cases must pass. Release validation also requires extension loading and a real Herdr read-only batch smoke with distinct task panes and final tab cleanup. The latest smoke completed two concurrent tasks, retained its focused run tab through settlement, and closed it automatically after focus moved away.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: analyst
|
|
3
|
+
description: Neutral read-only analyst for dynamically assigned roles
|
|
4
|
+
access: read
|
|
5
|
+
tools: read, bash, grep, find, ls
|
|
6
|
+
---
|
|
7
|
+
|
|
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.
|
|
@@ -259,7 +259,7 @@ export function registerHerdrSubagents(pi: ExtensionAPI, dependencies: HerdrSuba
|
|
|
259
259
|
pi.registerTool({
|
|
260
260
|
name: "herdr_subagents_control",
|
|
261
261
|
label: "Herdr Subagents Control",
|
|
262
|
-
description: "Inspect Herdr personas
|
|
262
|
+
description: "Inspect Herdr personas, query one run by ID, list run history, cancel active or queued work, answer a blocked task, resume a saved task, or remove a selected archived run. Use status for one run and history only for the full archive. Use herdr_subagents to start new work.",
|
|
263
263
|
promptSnippet: "Inspect and control Herdr subagent runs",
|
|
264
264
|
parameters: HerdrSubagentsControlParams,
|
|
265
265
|
|
|
@@ -16,8 +16,8 @@ export const HerdrSubagentsParams = Type.Object({
|
|
|
16
16
|
}, { additionalProperties: false });
|
|
17
17
|
|
|
18
18
|
export const HerdrSubagentsControlParams = Type.Object({
|
|
19
|
-
action: StringEnum(["list", "respond", "history", "resume", "cancel", "cleanup"] as const, { description: "Control operation to perform." }),
|
|
20
|
-
runId: Type.Optional(Type.String({ minLength: 1, description: "Durable run ID used by respond, resume, cancel, or cleanup." })),
|
|
19
|
+
action: StringEnum(["list", "status", "respond", "history", "resume", "cancel", "cleanup"] as const, { description: "Control operation to perform." }),
|
|
20
|
+
runId: Type.Optional(Type.String({ minLength: 1, description: "Durable run ID used by status, respond, resume, cancel, or cleanup." })),
|
|
21
21
|
answer: Type.Optional(Type.String({ minLength: 1, description: "Answer sent to a blocked task when action is respond." })),
|
|
22
22
|
task: Type.Optional(Type.Integer({ minimum: 1, description: "One-based task number selected for respond or resume." })),
|
|
23
23
|
prompt: Type.Optional(Type.String({ minLength: 1, description: "Follow-up prompt used by resume." })),
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { loadQueuedRuns } from "./queued-runs.ts";
|
|
4
|
+
import { isSettledRunStatus, readReport, readRunRecord, readTaskRecord, type RunStatus, type TaskStatus } from "./records.ts";
|
|
5
|
+
import { findRunDirectory } from "./run-locator.ts";
|
|
6
|
+
|
|
7
|
+
export interface RunStatusTask {
|
|
8
|
+
order: number;
|
|
9
|
+
name: string;
|
|
10
|
+
agent: string;
|
|
11
|
+
status: TaskStatus;
|
|
12
|
+
summary?: string;
|
|
13
|
+
question?: string;
|
|
14
|
+
error?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface RunStatusSnapshot {
|
|
18
|
+
runId: string;
|
|
19
|
+
label: string;
|
|
20
|
+
status: RunStatus;
|
|
21
|
+
settled: boolean;
|
|
22
|
+
source: "run" | "queue";
|
|
23
|
+
queuedAt?: string;
|
|
24
|
+
startedAt?: string;
|
|
25
|
+
updatedAt?: string;
|
|
26
|
+
completedAt?: string;
|
|
27
|
+
question?: string;
|
|
28
|
+
error?: string;
|
|
29
|
+
tasks: RunStatusTask[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function loadDurableStatus(cwd: string, runId: string): Promise<RunStatusSnapshot | undefined> {
|
|
33
|
+
const runsDirectory = join(cwd, ".pi", "herdr-subagents", "runs");
|
|
34
|
+
const runDirectory = await findRunDirectory(runsDirectory, runId);
|
|
35
|
+
if (!runDirectory) return undefined;
|
|
36
|
+
const run = await readRunRecord(join(runDirectory, "run.json"));
|
|
37
|
+
const tasksDirectory = join(runDirectory, "tasks");
|
|
38
|
+
const tasks: RunStatusTask[] = [];
|
|
39
|
+
if (existsSync(tasksDirectory)) for (const entry of readdirSync(tasksDirectory).sort()) {
|
|
40
|
+
const taskDirectory = join(tasksDirectory, entry);
|
|
41
|
+
const task = await readTaskRecord(join(taskDirectory, "task.json"));
|
|
42
|
+
const reportFile = join(taskDirectory, "report.json");
|
|
43
|
+
const reportIsCurrent = task.status === "blocked" || isSettledRunStatus(task.status);
|
|
44
|
+
const report = reportIsCurrent && existsSync(reportFile) ? await readReport(reportFile) : undefined;
|
|
45
|
+
const question = report?.question ?? task.question;
|
|
46
|
+
const error = report?.error ?? task.error;
|
|
47
|
+
tasks.push({
|
|
48
|
+
order: task.order,
|
|
49
|
+
name: task.name ?? task.agent ?? `Task ${task.order}`,
|
|
50
|
+
agent: task.agent ?? "worker",
|
|
51
|
+
status: task.status,
|
|
52
|
+
...(report?.summary ? { summary: report.summary } : {}),
|
|
53
|
+
...(question ? { question } : {}),
|
|
54
|
+
...(error ? { error } : {}),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
runId: run.id,
|
|
59
|
+
label: run.label,
|
|
60
|
+
status: run.status,
|
|
61
|
+
settled: isSettledRunStatus(run.status),
|
|
62
|
+
source: "run",
|
|
63
|
+
...(run.startedAt ? { startedAt: run.startedAt } : {}),
|
|
64
|
+
...(run.updatedAt ? { updatedAt: run.updatedAt } : {}),
|
|
65
|
+
...(run.completedAt ? { completedAt: run.completedAt } : {}),
|
|
66
|
+
...(run.question ? { question: run.question } : {}),
|
|
67
|
+
...(run.error ? { error: run.error } : {}),
|
|
68
|
+
tasks: tasks.sort((left, right) => left.order - right.order),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function getSubagentRunStatus(cwd: string, runId: string): Promise<RunStatusSnapshot | undefined> {
|
|
73
|
+
const durable = await loadDurableStatus(cwd, runId);
|
|
74
|
+
if (durable) return durable;
|
|
75
|
+
const queued = (await loadQueuedRuns(cwd)).find((candidate) => candidate.runId === runId);
|
|
76
|
+
if (!queued) return undefined;
|
|
77
|
+
return {
|
|
78
|
+
runId: queued.runId,
|
|
79
|
+
label: queued.params.label ?? "batch",
|
|
80
|
+
status: "queued",
|
|
81
|
+
settled: false,
|
|
82
|
+
source: "queue",
|
|
83
|
+
queuedAt: queued.queuedAt,
|
|
84
|
+
tasks: queued.params.tasks.map((task, index) => ({ order: index + 1, name: task.name, agent: task.agent ?? "worker", status: "queued" })),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function runStatusText(status: RunStatusSnapshot): string {
|
|
89
|
+
const lines = [`${status.runId} · ${status.label} · ${status.status} · ${status.tasks.length} tasks`];
|
|
90
|
+
for (const task of status.tasks) {
|
|
91
|
+
const detail = task.question ? ` — ${task.question}` : task.summary ? ` — ${task.summary}` : task.error ? ` — ${task.error}` : "";
|
|
92
|
+
lines.push(` ${task.order}. ${task.name}: ${task.status}${detail}`);
|
|
93
|
+
}
|
|
94
|
+
return lines.join("\n");
|
|
95
|
+
}
|
|
@@ -8,6 +8,7 @@ import { HERDR_ACTIONS, validateControlParams, validateRunParams } from "./valid
|
|
|
8
8
|
import type { OwnerIdentity } from "./ownership.ts";
|
|
9
9
|
import { cancelActiveSubagentRuns } from "./reconcile.ts";
|
|
10
10
|
import { cancelQueuedRuns, loadQueuedRuns } from "./queued-runs.ts";
|
|
11
|
+
import { getSubagentRunStatus, runStatusText } from "./status.ts";
|
|
11
12
|
|
|
12
13
|
export interface HerdrSubagentsTaskParams { name: string; prompt: string; agent?: string; model?: string; }
|
|
13
14
|
|
|
@@ -19,7 +20,7 @@ export interface HerdrSubagentsRunParams {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export interface HerdrSubagentsControlToolParams {
|
|
22
|
-
action: "list" | "respond" | "history" | "resume" | "cancel" | "cleanup";
|
|
23
|
+
action: "list" | "status" | "respond" | "history" | "resume" | "cancel" | "cleanup";
|
|
23
24
|
runId?: string;
|
|
24
25
|
answer?: string;
|
|
25
26
|
task?: number;
|
|
@@ -28,7 +29,7 @@ export interface HerdrSubagentsControlToolParams {
|
|
|
28
29
|
|
|
29
30
|
/** Internal superset used by shared execution helpers. */
|
|
30
31
|
export interface HerdrSubagentsToolParams {
|
|
31
|
-
action?: "list" | "respond" | "history" | "resume" | "cancel" | "cleanup";
|
|
32
|
+
action?: "list" | "status" | "respond" | "history" | "resume" | "cancel" | "cleanup";
|
|
32
33
|
prompt?: string;
|
|
33
34
|
agent?: string;
|
|
34
35
|
label?: string;
|
|
@@ -114,6 +115,16 @@ const errorResponse = (text: string, details: Record<string, unknown> = {}): Too
|
|
|
114
115
|
});
|
|
115
116
|
|
|
116
117
|
const actionHandlers = {
|
|
118
|
+
async status({ params, ctx }: ToolExecutionContext) {
|
|
119
|
+
try {
|
|
120
|
+
const runId = (params.runId as string).trim();
|
|
121
|
+
const status = await getSubagentRunStatus(ctx.cwd, runId);
|
|
122
|
+
if (!status) return errorResponse(`Unknown Subagent run '${runId}'.`, { action: "status", runId });
|
|
123
|
+
return { content: [{ type: "text", text: runStatusText(status) }], details: { action: "status", ...status } };
|
|
124
|
+
} catch (error) {
|
|
125
|
+
return errorResponse(error instanceof Error ? error.message : String(error), { action: "status", runId: params.runId });
|
|
126
|
+
}
|
|
127
|
+
},
|
|
117
128
|
async history({ ctx }: ToolExecutionContext) {
|
|
118
129
|
const history = await listSubagentHistory(ctx.cwd);
|
|
119
130
|
return { content: [{ type: "text", text: historyText(history) }], details: { action: "history", runs: history } };
|
|
@@ -3,6 +3,7 @@ import type { HerdrSubagentsControlToolParams, HerdrSubagentsRunParams } from ".
|
|
|
3
3
|
export const HERDR_ACTIONS = {
|
|
4
4
|
list: { allowed: ["action"], includeRunId: false },
|
|
5
5
|
history: { allowed: ["action"], includeRunId: false },
|
|
6
|
+
status: { allowed: ["action", "runId"], includeRunId: true },
|
|
6
7
|
cancel: { allowed: ["action", "runId"], includeRunId: true },
|
|
7
8
|
cleanup: { allowed: ["action", "runId"], includeRunId: true },
|
|
8
9
|
respond: { allowed: ["action", "runId", "answer", "task"], includeRunId: true },
|
|
@@ -19,7 +20,7 @@ export function validateControlParams(params: HerdrSubagentsControlToolParams):
|
|
|
19
20
|
if (!allowed) return "Provide a valid control action.";
|
|
20
21
|
const extra = Object.keys(params).find((key) => !allowed.includes(key as keyof HerdrSubagentsControlToolParams));
|
|
21
22
|
if (extra) return `Field '${extra}' is not valid with action '${params.action}'.`;
|
|
22
|
-
if ((params.action === "cancel" || params.action === "cleanup") && !params.runId?.trim()) return `runId is required for action '${params.action}'.`;
|
|
23
|
+
if ((params.action === "status" || params.action === "cancel" || params.action === "cleanup") && !params.runId?.trim()) return `runId is required for action '${params.action}'.`;
|
|
23
24
|
if (params.action === "respond" && (!params.runId?.trim() || !params.answer?.trim())) return "runId and answer are required for action 'respond'.";
|
|
24
25
|
if (params.action === "resume" && (!params.runId?.trim() || !params.task || !params.prompt?.trim())) return "runId, task, and prompt are required for action 'resume'.";
|
|
25
26
|
return undefined;
|
|
@@ -251,6 +251,10 @@ export default function (_pi: ExtensionAPI) {
|
|
|
251
251
|
if (!controlSchema.properties?.[name]?.description) throw new Error(`control schema property '${name}' is missing or undocumented`);
|
|
252
252
|
}
|
|
253
253
|
if (!controlSchema.required?.includes("action")) throw new Error("herdr_subagents_control action must be required");
|
|
254
|
+
const controlActions = controlSchema.properties.action.enum ?? controlSchema.properties.action.anyOf?.map((entry: Record<string, unknown>) => entry.const);
|
|
255
|
+
if (!controlActions?.includes("status")) throw new Error("herdr_subagents_control action enum must include status");
|
|
256
|
+
if (!/status/.test(controlSchema.properties.runId.description)) throw new Error("control runId description must document status");
|
|
257
|
+
if (!/Use status for one run/.test(controlTool.description)) throw new Error("control tool description must distinguish status from history");
|
|
254
258
|
if (!/Foreground calls block/.test(tool.description) || !/background calls return a run ID immediately/.test(tool.description)) {
|
|
255
259
|
throw new Error("tool description does not distinguish foreground and background execution");
|
|
256
260
|
}
|
|
@@ -141,14 +141,16 @@ function latestRunDirectory() {
|
|
|
141
141
|
try {
|
|
142
142
|
check("run validation accepts one or more tasks", validateRunParams({ tasks: [{ name: "one", prompt: "do it" }], concurrency: 2, background: true }) === undefined);
|
|
143
143
|
check("control validation accepts supported calls", [
|
|
144
|
-
{ action: "list" }, { action: "history" }, { action: "cancel", runId: "run" }, { action: "cleanup", runId: "run" },
|
|
144
|
+
{ action: "list" }, { action: "history" }, { action: "status", runId: "run" }, { action: "cancel", runId: "run" }, { action: "cleanup", runId: "run" },
|
|
145
145
|
{ action: "respond", runId: "run", answer: "yes", task: 1 },
|
|
146
146
|
{ action: "resume", runId: "run", task: 1, prompt: "continue" },
|
|
147
147
|
].every((params) => validateControlParams(params) === undefined));
|
|
148
|
-
check("all supported actions have shared validation and rendering metadata", ["list", "history", "cancel", "cleanup", "respond", "resume"].every((action) => action in HERDR_ACTIONS));
|
|
148
|
+
check("all supported actions have shared validation and rendering metadata", ["list", "history", "status", "cancel", "cleanup", "respond", "resume"].every((action) => action in HERDR_ACTIONS));
|
|
149
149
|
check("split validation rejects missing tasks and invalid control fields", [
|
|
150
150
|
validateRunParams({ tasks: [] }),
|
|
151
151
|
validateControlParams({ action: "list", prompt: "not allowed" }),
|
|
152
|
+
validateControlParams({ action: "status" }),
|
|
153
|
+
validateControlParams({ action: "history", runId: "not-allowed" }),
|
|
152
154
|
validateControlParams({ action: "respond", runId: "run" }),
|
|
153
155
|
validateControlParams({ action: "resume", runId: "run", task: 1 }),
|
|
154
156
|
].every((message) => typeof message === "string" && message.length > 0));
|
|
@@ -404,6 +406,9 @@ try {
|
|
|
404
406
|
writeFileSync(join(agentsDir, "broken.md"), "not frontmatter");
|
|
405
407
|
const discovery = discoverPackageAgents(agentsDir);
|
|
406
408
|
check("persona discovery and diagnostics", discovery.agents.length === 1 && discovery.agents[0].name === "worker" && discovery.diagnostics.length === 1);
|
|
409
|
+
const packageDiscovery = discoverPackageAgents(join(process.cwd(), "agents"));
|
|
410
|
+
const analystPersona = packageDiscovery.agents.find((persona) => persona.name === "analyst");
|
|
411
|
+
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));
|
|
407
412
|
|
|
408
413
|
const configRoot = join(root, "configuration");
|
|
409
414
|
const packageAgents = join(configRoot, "package-agents");
|
|
@@ -443,6 +448,18 @@ try {
|
|
|
443
448
|
const configuredStart = configuredHerdr.calls.find(([name]) => name === "startAgent")[1];
|
|
444
449
|
check("configured persona uses displayed effective values", !configuredRun.isError && configuredStart.args.includes("task/model") && configuredStart.args.includes("high") && configuredStart.args.includes("--skill") && configuredStart.args.includes("project-skill"));
|
|
445
450
|
|
|
451
|
+
const analystHerdr = fakeHerdr();
|
|
452
|
+
const analystBatch = await executeHerdrSubagents({ label: "independent-analysis", concurrency: 3, tasks: [
|
|
453
|
+
{ name: "sol-audit", prompt: "slow audit", agent: "analyst", model: "vendor/sol" },
|
|
454
|
+
{ name: "grok-replicate", prompt: "slow replicate", agent: "analyst", model: "vendor/grok" },
|
|
455
|
+
{ name: "opus-challenge", prompt: "slow challenge", agent: "analyst", model: "vendor/opus" },
|
|
456
|
+
] }, undefined, undefined, { cwd: root, model: { provider: "parent", id: "model" } }, { agentsDirectory: join(process.cwd(), "agents"), herdr: analystHerdr });
|
|
457
|
+
const analystStarts = analystHerdr.calls.filter(([name]) => name === "startAgent").map(([, request]) => request);
|
|
458
|
+
const analystModels = analystStarts.map((request) => request.args[request.args.indexOf("--model") + 1]);
|
|
459
|
+
check("three read-only analyst tasks run concurrently", !analystBatch.isError && analystBatch.details.effectiveConcurrency === 3 && analystHerdr.maxActive === 3);
|
|
460
|
+
check("analyst tasks preserve independent model overrides", analystModels.join(",") === "vendor/sol,vendor/grok,vendor/opus");
|
|
461
|
+
check("completed analyst batch closes its run tab", analystHerdr.calls.at(-1)?.[0] === "closeTab");
|
|
462
|
+
|
|
446
463
|
const batchHerdr = fakeHerdr();
|
|
447
464
|
const batchUpdates = [];
|
|
448
465
|
const batch = await executeHerdrSubagents({ label: "reviews", concurrency: 2, tasks: [
|
|
@@ -603,10 +620,31 @@ try {
|
|
|
603
620
|
const stableHerdr = fakeHerdr();
|
|
604
621
|
const stable = await executeHerdrSubagents({ tasks: [{ name: "stable", prompt: "stable", agent: "explorer" }] }, undefined, undefined, { cwd: root, model: { provider: "fake", id: "model" } }, { agentsDirectory: join(process.cwd(), "agents"), herdr: stableHerdr, runId: backgroundId });
|
|
605
622
|
check("preallocated background run id stays stable", stable.details.runId === backgroundId && readFileSync(join(stable.details.recordDirectory, "run.json"), "utf8").includes(backgroundId));
|
|
623
|
+
const stableStatus = await executeHerdrSubagents({ action: "status", runId: backgroundId }, undefined, undefined, { cwd: root }, { agentsDirectory: join(process.cwd(), "agents") });
|
|
624
|
+
check("status returns only one durable run with task details", !stableStatus.isError && stableStatus.details.runId === backgroundId && stableStatus.details.status === "completed" && stableStatus.details.settled === true && stableStatus.details.source === "run" && stableStatus.details.tasks[0].name === "stable" && !stableStatus.content[0].text.includes("run-one"));
|
|
625
|
+
await persistQueuedRun(root, backgroundId, { label: "stale-queue", tasks: [{ name: "wrong-source", prompt: "queued" }], background: true });
|
|
626
|
+
const durableFirstStatus = await executeHerdrSubagents({ action: "status", runId: backgroundId }, undefined, undefined, { cwd: root }, { agentsDirectory: join(process.cwd(), "agents") });
|
|
627
|
+
check("status prefers durable state over a stale queue record", durableFirstStatus.details.source === "run" && durableFirstStatus.details.label !== "stale-queue" && durableFirstStatus.details.tasks[0].name === "stable");
|
|
628
|
+
const queuedStatusId = "queued-status-id";
|
|
629
|
+
await persistQueuedRun(root, queuedStatusId, { label: "queued-status", tasks: [{ name: "waiting", prompt: "wait", agent: "reviewer" }], background: true });
|
|
630
|
+
const queuedStatus = await executeHerdrSubagents({ action: "status", runId: queuedStatusId }, undefined, undefined, { cwd: root }, { agentsDirectory: join(process.cwd(), "agents") });
|
|
631
|
+
check("status projects a queued run before durable records exist", !queuedStatus.isError && queuedStatus.details.source === "queue" && queuedStatus.details.status === "queued" && queuedStatus.details.settled === false && queuedStatus.details.tasks[0].status === "queued" && queuedStatus.details.tasks[0].agent === "reviewer");
|
|
632
|
+
const unknownStatus = await executeHerdrSubagents({ action: "status", runId: "unknown-status-id" }, undefined, undefined, { cwd: root }, { agentsDirectory: join(process.cwd(), "agents") });
|
|
633
|
+
check("status rejects unknown run IDs without falling back to history", unknownStatus.isError && unknownStatus.content[0].text === "Unknown Subagent run 'unknown-status-id'.");
|
|
634
|
+
const staleStatusRoot = join(root, "stale-status-report");
|
|
635
|
+
const staleStatusTaskDirectory = join(staleStatusRoot, ".pi", "herdr-subagents", "runs", "stale", "tasks", "01-task");
|
|
636
|
+
mkdirSync(staleStatusTaskDirectory, { recursive: true });
|
|
637
|
+
writeFileSync(join(staleStatusTaskDirectory, "..", "..", "run.json"), JSON.stringify({ id: "stale-status", label: "stale-status", status: "running", startedAt: "2026-10-08T00:00:00.000Z" }));
|
|
638
|
+
writeFileSync(join(staleStatusTaskDirectory, "task.json"), JSON.stringify({ id: "stale-task", runId: "stale-status", order: 1, name: "stale-task", agent: "reviewer", status: "running" }));
|
|
639
|
+
writeFileSync(join(staleStatusTaskDirectory, "report.json"), JSON.stringify({ status: "needs-input", summary: "Old blocked report.", question: "Old question?", documents: [] }));
|
|
640
|
+
const runningStatus = await executeHerdrSubagents({ action: "status", runId: "stale-status" }, undefined, undefined, { cwd: staleStatusRoot }, { agentsDirectory: join(process.cwd(), "agents") });
|
|
641
|
+
check("running status does not expose a stale report from an earlier blocked turn", runningStatus.details.tasks[0].status === "running" && !runningStatus.details.tasks[0].question && !runningStatus.details.tasks[0].summary && !runningStatus.content[0].text.includes("Old question"));
|
|
606
642
|
|
|
607
643
|
const blockedHerdr = fakeHerdr("blocked");
|
|
608
644
|
const blocked = await executeHerdrSubagents({ concurrency: 1, tasks: [{ name: "question", prompt: "inspect branch", agent: "explorer" }, { name: "after-answer", prompt: "continue queued", agent: "reviewer" }] }, undefined, undefined, { cwd: root, model: { provider: "fake", id: "model" } }, { agentsDirectory: join(process.cwd(), "agents"), herdr: blockedHerdr });
|
|
609
645
|
check("needs-input returns foreground control and preserves tab", blocked.details.status === "blocked" && blocked.details.tasks[0].question === "Which branch should I inspect?" && !blockedHerdr.calls.some(([name]) => name === "closeTab"));
|
|
646
|
+
const blockedStatus = await executeHerdrSubagents({ action: "status", runId: blocked.details.runId }, undefined, undefined, { cwd: root }, { agentsDirectory: join(process.cwd(), "agents") });
|
|
647
|
+
check("status exposes blocked task questions for respond", blockedStatus.details.status === "blocked" && blockedStatus.details.settled === false && blockedStatus.details.tasks[0].question === "Which branch should I inspect?" && blockedStatus.content[0].text.includes("Which branch should I inspect?"));
|
|
610
648
|
const activeCleanup = await executeHerdrSubagents({ action: "cleanup", runId: blocked.details.runId }, undefined, undefined, { cwd: root }, { agentsDirectory: join(process.cwd(), "agents"), herdr: blockedHerdr });
|
|
611
649
|
check("cleanup refuses to delete an active blocked run", activeCleanup.isError && activeCleanup.content[0].text.includes("still active") && existsSync(blocked.details.recordDirectory));
|
|
612
650
|
const resumeUpdates = [];
|
|
@@ -4,86 +4,62 @@ description: 以对抗评审的方式让不同厂商的模型交叉验证同一
|
|
|
4
4
|
disable-model-invocation: true
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
当前会话是**管理者**,不是转述人:自己也承担一份实证工作,并对每一轮的裁决负责。每轮并发启动三个相互独立的子代理,统一收集结果后由管理者裁决。子代理不需要常驻;跨轮连续性来自持久调查档案和逻辑成员身份,而不是聊天记忆。
|
|
8
8
|
|
|
9
9
|
## 何时值得开
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
只在用户要求组队时开。值得开的条件:结论会驱动一个代价高的动作(改内核、下线设备、向客户定责),或者证据链已经长到单模型容易自我确认。查一个事实、读一份文件用不上。
|
|
12
12
|
|
|
13
|
-
## 1.
|
|
13
|
+
## 1. 建立调查档案
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
为当前问题建立 `.pi/agent-team/<case>/`:
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
```text
|
|
18
|
+
.pi/agent-team/<case>/
|
|
19
|
+
├── BRIEF.md # 问题、范围、术语、输入和排除项
|
|
20
|
+
├── FINDINGS-stageN.md # 当前唯一事实源;新阶段整体取代旧阶段
|
|
21
|
+
├── LEDGER.md # 只增不改的 N/X 记录
|
|
22
|
+
└── rounds/
|
|
23
|
+
└── N.md # 本轮角色映射和可证伪攻击点
|
|
23
24
|
```
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
`BRIEF.md` 只记录稳定背景。`FINDINGS-stageN.md` 必含机制链、编号发现、已推翻清单和未闭合点。`LEDGER.md` 中发现按 `N1, N2…` 追加,被推翻的结论按 `X1, X2…` 追加并写明推翻者。只有管理者能更新这三个权威文件;子代理只提交候选证据。
|
|
26
27
|
|
|
27
|
-
## 2.
|
|
28
|
+
## 2. 配置成员与角色
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
名单在同目录的 `models.json`。三个逻辑成员必须来自不同模型厂商,并显式使用各自的 `model`;不能依赖父会话或环境默认模型。启动后核对并在本轮记录中写下每个任务实际生效的模型;无法确认、与名单不符或三个成员落到同一厂商时,本轮无效。
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
三个成员使用相同的中性只读执行约束,方法角色由每轮任务动态赋予:
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
- `auditor`:审源码、规范和提交,只接受可定位的直接证据。
|
|
35
|
+
- `replicator`:绕开管理者的脚本和口径,从原始数据独立复算。
|
|
36
|
+
- `adversary`:攻击前提和方法学,查样本敏感性、一对多计数和阴性对照。
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
管理者(本会话)
|
|
37
|
-
唯一写 FINDINGS 的人
|
|
38
|
-
│ │ │
|
|
39
|
-
下行:消息 │ │ │ herdr agent prompt
|
|
40
|
-
(短指针) ▼ ▼ ▼
|
|
41
|
-
┌────┐ ┌──────┐ ┌──────┐
|
|
42
|
-
│ sol│ │ grok │ │ opus │ 彼此不相连
|
|
43
|
-
└──┬──┘ └──┬───┘ └──┬───┘
|
|
44
|
-
│ │ │
|
|
45
|
-
上行:文件 └───────┴───────┘
|
|
46
|
-
▼
|
|
47
|
-
/tmp/<轮次>-<name>.md ← 管理者从磁盘读
|
|
48
|
-
```
|
|
38
|
+
模型与角色不得永久绑定。首轮可以按 `models.json` 顺序分配三个角色;后续根据争议轮换或交叉复核,使模型差异和角色差异能够被区分。每轮在 `rounds/N.md` 写明成员、模型、角色、输入文件和攻击点。
|
|
49
39
|
|
|
50
|
-
|
|
40
|
+
## 3. 交底与通信
|
|
51
41
|
|
|
52
|
-
|
|
42
|
+
拓扑是星型,管理者是唯一消息路由。成员在同一轮中独立作答,在提交前不得看到其他成员本轮答案。需要逻辑成员 A 反驳逻辑成员 B 的上一轮结论时,由管理者在新任务中引用 B 的原话和证据位置;不能假定新实例记得上一轮聊天。
|
|
53
43
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
MSG='【N17】读 /tmp/FINDINGS-stage4.md 全文。本轮你的攻击点:……。结论写 /tmp/N17-<你的名字>.md,终端只回一行路径。'
|
|
58
|
-
for a in sol grok opus; do herdr agent prompt "$a" "$MSG"; done
|
|
59
|
-
for a in sol grok opus; do herdr agent wait "$a" --until idle --timeout 900000; done
|
|
60
|
-
```
|
|
44
|
+
下行只给短任务和文件指针:每个成员阅读 `BRIEF.md`、最新 `FINDINGS-stageN.md`、`LEDGER.md` 和本轮文件。不得要求它重新梳理已经确认的全部背景。上行结果必须能归属到轮次和成员,并包含证据位置;长结果写入持久文件,不能依赖截断的终端文本。
|
|
61
45
|
|
|
62
46
|
## 4. 回合
|
|
63
47
|
|
|
64
|
-
一轮 =
|
|
48
|
+
一轮 = 记录受保护文件和工作树初始状态 → 写本轮任务 → 三个成员并发独立调查 → 管理者收齐结果 → 检查子代理未修改项目或权威文件 → 核验证据并裁决 → 更新 FINDINGS 和 LEDGER → 决定下一轮。
|
|
65
49
|
|
|
66
|
-
|
|
50
|
+
每个成员必须收到具体到能证伪的攻击点,例如“查 `adbconnection.cc`,确认该错误日志与被销毁的 fd 是否 1:1”,而不是“看看有没有问题”。子代理结束后即可释放;下一轮通过调查档案恢复事实状态,并重新创建独立任务。
|
|
67
51
|
|
|
68
52
|
## 5. 纪律
|
|
69
53
|
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
- **
|
|
73
|
-
- **
|
|
74
|
-
- **
|
|
75
|
-
-
|
|
54
|
+
- **独立性**:同轮答案不得互相可见;成员之间不直接通信。
|
|
55
|
+
- **只读完整性**:只读是协议约束,不假定执行环境构成沙箱。每轮结束后比较初始状态;发现子代理修改项目或权威文件时,该成员结果无效并显式报告污染范围。
|
|
56
|
+
- **retraction**:管理者被驳倒时,在下一轮开头明确写“我错了,更正为……”,并追加对应 X。
|
|
57
|
+
- **replication**:任何承载结论的数字至少由两个不同模型独立计算;对不上先查口径再查代码。
|
|
58
|
+
- **control**:任何“异常”指标必须配阴性样本才进入 FINDINGS。
|
|
59
|
+
- **grading**:每条结论标注 `源码读取 / 实测 / 推断` 和置信度;推断不能与实测并列陈述。
|
|
60
|
+
- **裁决**:成员冲突时管理者必须判定谁对并说明证据,不能多数投票或折中平均。
|
|
61
|
+
- **缺席视角**:成员失败或额度耗尽时,明确记录缺失的模型和方法角色,不能把不完整轮次当成完整评审。
|
|
76
62
|
|
|
77
63
|
## 6. 收尾
|
|
78
64
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
最终文档必须单列**未闭合点**,以及**每一个需要人或设备批准才能执行的动作**。
|
|
82
|
-
|
|
83
|
-
用完 `herdr pane close <pane_id>` 关掉,pane 不会自己退出。
|
|
84
|
-
|
|
85
|
-
## 故障
|
|
86
|
-
|
|
87
|
-
- pane 额度耗尽:降级为剩余 pane 继续,并在文档里记下缺失的是哪个视角。
|
|
88
|
-
- agent 卡在 blocked:`herdr agent read <name> --source visible` 看它在等什么,多半是在等一个确认。
|
|
89
|
-
- 广播后无人响应:`herdr agent list` 核对 pane 是否还活着,pane id 会随布局变化。
|
|
65
|
+
收尾前必须进行一轮**开放攻击**:三个方法角色各自选择其认为最强的攻击点,不能只回答管理者预设的窄问题。只有完整开放攻击轮没有产生任何被管理者接受并记入 X 的新反证时才算收敛。最终文档单列未闭合点、缺失视角,以及每一个需要人或设备批准才能执行的动作。
|