@maxiaochao/pi-toolkit 0.11.19 → 0.11.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 2026-10-15 - v0.11.21
2
+
3
+ - simplify Agent Team to config-driven orchestration with persistent investigation records and optional extra review
4
+
5
+ ## 2026-10-14 - v0.11.20
6
+
7
+ - herdr-subagents v0.5.3: allow optional Pi-native tool exposure while inheriting Pi defaults when unset
8
+
1
9
  ## 2026-10-14 - v0.11.19
2
10
 
3
11
  - herdr-subagents v0.5.2: stop Pi startup from scanning historical runs or closing recorded Herdr tabs
@@ -36,6 +44,8 @@
36
44
 
37
45
  ## Unreleased
38
46
 
47
+ - Agent Team: make the main agent responsible for config-driven dispatch, result collection, and investigation records; use configured role charters as the single source of truth and make role rotation, extra replication, and open attack rounds optional.
48
+ - Keep release-routing test fixtures independent of changes since the latest root release tag.
39
49
  - OpenAI Account Pool: restore `●本会话` after successful requests and failover across fresh command contexts; status and preference commands no longer reinstall the session provider.
40
50
  - Scope child and root release checks to their owned code, and run Herdr tests or the real Agent smoke only when Herdr is the target or changed since the previous root release.
41
51
  - OpenAI Account Pool: keep deterministic account priority and show it as `★首选`, separately from the session-local `●本会话` account; automatic mode uses account order rather than random selection.
package/README.md CHANGED
@@ -76,7 +76,7 @@ bash scripts/install.sh
76
76
  | `html-artifact` | Build self-contained HTML explainers and diagrams |
77
77
  | `pdlog` | Decompress and inspect Pudu `.pdlog` files |
78
78
  | `herdr` | Control Herdr workspaces, tabs, panes, and agents |
79
- | `agent-team` | Run tool-agnostic, cross-vendor adversarial review with independent concurrent agents |
79
+ | `agent-team` | Let the main agent dispatch concurrent analysis using configured models and roles, collect results, and maintain investigation records; run extra review rounds as needed |
80
80
  | `tapd` | Investigate and operate on TAPD items with workspace, evidence, and safe-download guardrails |
81
81
 
82
82
  Package-specific skills, such as `pi-worktree`, are documented by their child package.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maxiaochao/pi-toolkit",
3
- "version": "0.11.19",
3
+ "version": "0.11.21",
4
4
  "description": "Personal pi coding agent toolkit: scheduler, /btw, /cache_export, apply_patch, browser automation, and bundled theme/skills.",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -110,3 +110,7 @@ Writing the same report again is idempotent. A conflicting replacement is reject
110
110
  ## Cancellation and shutdown
111
111
 
112
112
  Caller or parent-session cancellation aborts active waits, marks nonterminal tasks cancelled, and closes the Herdr tab owned by that session. A new Pi session never scans, mutates, resumes, or closes historical runs during startup. If Pi is terminated before its shutdown handlers run, its visible Subagent tabs remain for explicit user inspection or cleanup.
113
+
114
+ ## Tool exposure
115
+
116
+ The extension reads optional `exposure` once before registering its two tools. When omitted, the extension also omits the registration field and delegates to Pi's default. Global or project configuration can select Pi's `model-only` or `direct` exposure explicitly. Changing this registration-time setting requires `/reload` or a Pi restart; the extension does not re-register tools during a session.
@@ -1,3 +1,7 @@
1
+ ## 2026-10-14 - v0.5.3
2
+
3
+ - allow optional Pi-native tool exposure while inheriting Pi defaults when unset
4
+
1
5
  ## 2026-10-14 - v0.5.2
2
6
 
3
7
  - stop Pi startup from scanning historical runs or closing recorded Herdr tabs
@@ -12,6 +12,18 @@ https://github.com/user-attachments/assets/8ddf3ae6-a11a-492b-9cae-d9496ba65b67
12
12
 
13
13
  The reproducible source recording is retained as [`docs/demo.webm`](docs/demo.webm).
14
14
 
15
+ ## Requirements and install
16
+
17
+ Run Pi inside a Herdr workspace, then install the standalone package:
18
+
19
+ ```bash
20
+ pi install npm:@maxiaochao/pi-herdr-subagents
21
+ ```
22
+
23
+ Update it with `pi update npm:@maxiaochao/pi-herdr-subagents`. The full `@maxiaochao/pi-toolkit` package already bundles this extension, so install the toolkit or this standalone package, not both.
24
+
25
+ The tools and command are registered only when Pi is running inside Herdr with a workspace ID. If they are unavailable after entering Herdr, use `/reload` or restart Pi there.
26
+
15
27
  ## Execution contract
16
28
 
17
29
  `herdr_subagents` accepts an ordered, non-empty `tasks` array.
@@ -85,14 +97,228 @@ Records are stored under:
85
97
 
86
98
  The orchestrator is the only writer of `run.json`, `task.json`, and promoted `report.json`. The child only writes its attempt-scoped result and report.
87
99
 
88
- ## Control and UI
100
+ ## Tools and UI
101
+
102
+ ### `herdr_subagents`
103
+
104
+ Starts one ordered batch and waits until every task reaches a terminal state. Its parameters are:
105
+
106
+ | Field | Required | Default | Purpose |
107
+ | --- | --- | --- | --- |
108
+ | `tasks` | Yes | — | Non-empty ordered task array. |
109
+ | `label` | No | `"batch"` | Short Herdr run-tab label, at most 48 characters. |
110
+ | `concurrency` | No | `defaultConcurrency` | Maximum number of simultaneously active read-only tasks. It is capped by `maxConcurrency`. |
111
+ | `tasks[].name` | Yes | — | Short pane/task name, at most 48 characters. |
112
+ | `tasks[].prompt` | Yes | — | Self-contained task instructions. |
113
+ | `tasks[].agent` | No | `"worker"` | Effective Persona name. |
114
+ | `tasks[].model` | No | Persona/default model | Model override for this task. |
115
+
116
+ A batch containing a write-capable Persona must explicitly or effectively use `concurrency: 1`; an unsafe parallel request is rejected.
117
+
118
+ ### `herdr_subagents_control`
119
+
120
+ | Action | `runId` | Purpose |
121
+ | --- | --- | --- |
122
+ | `list` | Not accepted | List effective Personas, resolved options, and diagnostics. |
123
+ | `status` | Required | Show one run and its task states. |
124
+ | `history` | Not accepted | List durable project-local run history. |
125
+ | `cancel` | Required | Cancel an active run. |
126
+ | `cleanup` | Required | Remove one archived run. |
127
+
128
+ Settled tasks are immutable. Follow-up work starts a new task or run; there is no resume/respond lifecycle.
129
+
130
+ ### Slash command
131
+
132
+ `/herdr-subagents` supports:
133
+
134
+ | Command | Purpose |
135
+ | --- | --- |
136
+ | `active` | Show the current run. This is also the default with no argument. |
137
+ | `history` | Show project-local history. |
138
+ | `focus <task-number>` | Focus a running task pane. |
139
+ | `agents` | Show effective Persona source, access, model, thinking, tools, skills, and extensions. |
140
+ | `models` | List authenticated Pi models. |
141
+ | `settings` | Show effective settings, their sources, and configuration diagnostics. |
142
+
143
+ ## Configuration and Personas
144
+
145
+ Settings and Persona definitions are separate:
146
+
147
+ | Scope | Settings file | Persona directory |
148
+ | --- | --- | --- |
149
+ | Project | `<project>/.pi/herdr-subagents.json` | `<project>/.pi/herdr-subagents/agents/*.md` |
150
+ | Global | `~/.pi/agent/herdr-subagents.json` | `~/.pi/agent/herdr-subagents/agents/*.md` |
151
+ | Package | Built-in defaults | Installed package `agents/*.md` |
152
+
153
+ For JSON settings, project fields override global fields, which override built-in defaults. `personas` entries merge by Persona name and then by field; array values replace lower-precedence arrays rather than appending to them.
154
+
155
+ Persona Markdown uses whole-definition replacement instead of field merging. A same-name definition is selected in this order:
156
+
157
+ 1. project Persona
158
+ 2. global Persona
159
+ 3. package Persona
160
+
161
+ ### Complete settings reference
162
+
163
+ These are all supported top-level fields in `herdr-subagents.json`:
164
+
165
+ | Option | Accepted value | Built-in default | Purpose |
166
+ | --- | --- | --- | --- |
167
+ | `exposure` | `"model-only"` or `"direct"` | Unset (inherit Pi; currently `direct`) | Controls whether the two tools may be called only by the top-level model or also by nested tools such as Codemode. |
168
+ | `defaultConcurrency` | Positive integer | `1` | Concurrency used when a call omits `concurrency`; capped to `maxConcurrency`. |
169
+ | `maxConcurrency` | Positive integer | `4` | Hard ceiling for requested/default read-only concurrency. It does not make write-capable batches safe. |
170
+ | `stalledWarningSeconds` | Positive integer | `300` | Emit a warning after a child runs this long. This is not a timeout and does not stop it. |
171
+ | `defaultModel` | Non-empty Pi model ID, normally `provider/model` | Unset | Fallback child model. If unset, the parent model is inherited. |
172
+ | `defaultThinking` | Non-empty Pi thinking value | Unset | Fallback thinking level below Persona-specific settings. Typical Pi values are `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`; model support varies. |
173
+ | `personas` | Object keyed by an existing Persona name | `{}` | Per-Persona overrides. It cannot create a Persona without a matching Markdown definition. |
174
+
175
+ Each `personas.<name>` object supports:
176
+
177
+ | Option | Accepted value | Purpose |
178
+ | --- | --- | --- |
179
+ | `model` | Non-empty Pi model ID | Override the model from Persona Markdown and `defaultModel`. |
180
+ | `thinking` | Non-empty Pi thinking value | Override Persona Markdown and `defaultThinking`. |
181
+ | `skills` | Array of non-empty strings | Replace the lower-precedence explicit skill list. Use `[]` to clear it. |
182
+ | `extensions` | Array of non-empty Pi source strings | Replace the lower-precedence extension-source list. Use `[]` to clear it. |
183
+
184
+ JSON overrides cannot change a Persona's `name`, `description`, `access`, `tools`, or system prompt; those belong in its Markdown definition.
185
+
186
+ Invalid known values are ignored and reported by `/herdr-subagents settings` or the control tool's `list` action. If `defaultConcurrency` exceeds `maxConcurrency`, the effective default is reduced to the maximum. Model IDs, thinking compatibility, skills, and extension sources are ultimately resolved by child Pi.
187
+
188
+ ### Exposure
189
+
190
+ Use the restrictive mode when only direct model calls should be allowed:
191
+
192
+ ```json
193
+ {
194
+ "exposure": "model-only"
195
+ }
196
+ ```
197
+
198
+ - `model-only`: the top-level model may call the tools, but Codemode scripts and other tools cannot invoke them as nested tools.
199
+ - `direct`: allow both top-level and nested calls explicitly.
200
+ - omitted: do not set an exposure during registration; inherit Pi's default behavior.
201
+
202
+ `exposure` is read once when the extension registers. After changing it, use `/reload` or restart Pi. Other settings and Persona files are re-read at the start of each new tool call; they do not modify an already running task.
203
+
204
+ ### Complete global example
205
+
206
+ Save this as `~/.pi/agent/herdr-subagents.json`. Model IDs, skills, and extension sources are illustrative and must exist in your Pi environment.
207
+
208
+ ```json
209
+ {
210
+ "exposure": "model-only",
211
+ "defaultConcurrency": 2,
212
+ "maxConcurrency": 4,
213
+ "stalledWarningSeconds": 300,
214
+ "defaultModel": "anthropic/claude-sonnet-4-5",
215
+ "defaultThinking": "medium",
216
+ "personas": {
217
+ "explorer": {
218
+ "model": "anthropic/claude-haiku-4-5",
219
+ "thinking": "low",
220
+ "skills": ["code-search"],
221
+ "extensions": ["~/.pi/agent/extensions/provider-routing.ts"]
222
+ },
223
+ "reviewer": {
224
+ "thinking": "high",
225
+ "skills": ["code-review"],
226
+ "extensions": []
227
+ }
228
+ }
229
+ }
230
+ ```
231
+
232
+ A project can override only what differs in `.pi/herdr-subagents.json`:
233
+
234
+ ```json
235
+ {
236
+ "defaultConcurrency": 3,
237
+ "stalledWarningSeconds": 600,
238
+ "personas": {
239
+ "explorer": {
240
+ "model": "openai/gpt-5.2",
241
+ "skills": []
242
+ },
243
+ "reviewer": {
244
+ "extensions": ["npm:@example/pi-review-tools@1.0.0"]
245
+ }
246
+ }
247
+ }
248
+ ```
249
+
250
+ Here the project keeps global `maxConcurrency`, `defaultModel`, and `defaultThinking`; replaces the explorer model and skill list; and replaces the reviewer extension list. It does not append arrays.
251
+
252
+ ### Built-in Personas
253
+
254
+ None of the bundled Personas pins a model, thinking level, skill, or optional extension. Those values come from configuration or parent-session inheritance.
255
+
256
+ | Persona | Access | Built-in tools | Intended use |
257
+ | --- | --- | --- | --- |
258
+ | `worker` | `write` | `read`, `bash`, `edit`, `write`, `grep`, `find`, `ls`, `codemode` | One focused implementation or other file-changing task. |
259
+ | `explorer` | `read` | `read`, `bash`, `grep`, `find`, `ls`, `codemode` | Fast codebase investigation without file changes. |
260
+ | `analyst` | `read` | `read`, `bash`, `grep`, `find`, `ls`, `codemode` | Neutral analysis where the task prompt supplies the role and evaluation method. |
261
+ | `reviewer` | `read` | `read`, `bash`, `grep`, `find`, `ls`, `codemode` | Correctness and maintainability review with actionable evidence. |
262
+
263
+ `access` is a scheduling declaration: any `write` Persona requires serial execution. It is not by itself a sandbox. For a custom read-only Persona, also omit write-capable tools and load only trusted extensions.
264
+
265
+ ### Custom Persona example
266
+
267
+ Save the following as `.pi/herdr-subagents/agents/security-reviewer.md` (or in the global Persona directory):
268
+
269
+ ```md
270
+ ---
271
+ name: security-reviewer
272
+ description: Read-only reviewer for security risks
273
+ access: read
274
+ model: anthropic/claude-sonnet-4-5
275
+ thinking: high
276
+ tools: read, bash, grep, find, ls, codemode
277
+ skills: security-review, code-review
278
+ extensions: ~/.pi/agent/extensions/provider-routing.ts
279
+ ---
280
+
281
+ Review the assigned scope without modifying files. Separate confirmed vulnerabilities from hypotheses and cite concrete evidence.
282
+ ```
283
+
284
+ The Markdown body is the child system prompt. Frontmatter is intentionally a small single-line format, not full YAML:
285
+
286
+ | Field | Accepted value | Required/default | Purpose |
287
+ | --- | --- | --- | --- |
288
+ | `name` | Non-empty string | Required | Identifier used by `tasks[].agent`; unique within one directory. |
289
+ | `description` | Non-empty string | Required | Description shown by Persona listings. |
290
+ | `access` | `read` or `write` | `write` | Scheduling safety declaration. |
291
+ | `model` | Non-empty Pi model ID | Unset | Persona model default. |
292
+ | `thinking` | Non-empty Pi thinking value | Unset | Persona thinking default. |
293
+ | `tools` | Non-empty comma-separated list, optionally `[bracketed]` | Pi child defaults | Built-in-tool allowlist. Supported names: `read`, `bash`, `edit`, `write`, `grep`, `find`, `ls`, `powershell`, `codemode`. `subagent_report` is always added. |
294
+ | `skills` | Comma-separated list, optionally `[bracketed]` | None | Explicit child skills; normal automatic skill discovery is disabled. |
295
+ | `extensions` | One source or a comma-separated list; `[]` clears | None | Additional Pi extension/package sources loaded into the child. |
296
+
297
+ Relative extension paths resolve from the project root. `~` resolves to the home directory. Pi-native local, `npm:`, `git:`, and `builtin:` sources are accepted. Children always load the required report extension and Codemode; configured sources are additional. Pi packages may provide resources beyond extensions, so load only trusted code and pin versions/refs when reproducibility matters.
298
+
299
+ ### Effective option precedence
300
+
301
+ The child model is selected in this order:
302
+
303
+ 1. `tasks[].model`
304
+ 2. project `personas.<name>.model`
305
+ 3. global `personas.<name>.model`
306
+ 4. effective Persona Markdown `model`
307
+ 5. effective `defaultModel` (project, then global)
308
+ 6. parent Pi model
309
+
310
+ Thinking is selected in this order:
89
311
 
90
- `herdr_subagents_control` supports `list`, `status`, `history`, `cancel`, and `cleanup`. Follow-up work is a new task or run; settled tasks are never revived.
312
+ 1. project `personas.<name>.thinking`
313
+ 2. global `personas.<name>.thinking`
314
+ 3. effective Persona Markdown `thinking`
315
+ 4. effective `defaultThinking` (project, then global)
316
+ 5. parent thinking when the child also inherits the parent model
91
317
 
92
- Use `/herdr-subagents focus <task-number>` to focus a running pane. `/herdr-subagents active`, `history`, `agents`, `models`, and `settings` provide compact inspection.
318
+ There is no task-level thinking override. `skills` and `extensions` use project Persona JSON, then global Persona JSON, then effective Persona Markdown. Each winning list replaces the whole lower-precedence list.
93
319
 
94
- ## Personas and settings
320
+ Use `/herdr-subagents agents`, `/herdr-subagents models`, and `/herdr-subagents settings` to inspect the result before starting work.
95
321
 
96
- Bundled personas are `worker`, `explorer`, `analyst`, and `reviewer`. Project personas can be added under `.pi/agents/` using the same frontmatter format as the bundled files. Access is declared as `read` or `write` and drives concurrency safety.
322
+ ## Development and tests
97
323
 
98
- Settings are read from the existing package configuration. `defaultConcurrency`, `maxConcurrency`, and `stalledWarningSeconds` remain supported.
324
+ See [TESTING.md](TESTING.md) for deterministic tests, extension loading, packaging, and real Herdr smoke requirements.
@@ -17,6 +17,7 @@ It verifies:
17
17
  - explicit failure when the process exits without a report;
18
18
  - parent cancellation of running and queued tasks;
19
19
  - startup isolation: a new Pi session neither mutates historical records nor closes their recorded Herdr tabs;
20
+ - omitted exposure delegates both tools to Pi's default, while project configuration can consistently select model-only exposure;
20
21
  - immutable terminal task records;
21
22
  - idempotent duplicate reports and rejection of conflicting replacements;
22
23
  - keeping a settled child alive for focus-aware cleanup instead of force-exiting its TUI.
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import type { AgentToolResult, ExtensionAPI, ExtensionCommandContext, Theme, ToolRenderResultOptions } from "@earendil-works/pi-coding-agent";
4
4
  import { Container, Editor, Text } from "@earendil-works/pi-tui";
5
- import { loadSubagentConfiguration } from "../src/config.ts";
5
+ import { loadSubagentConfiguration, type ConfigurationPaths } from "../src/config.ts";
6
6
  import { CliHerdrAutomation, type HerdrAutomation } from "../src/herdr.ts";
7
7
  import { activeRunText } from "../src/monitor.ts";
8
8
  import { historyText, listSubagentHistory } from "../src/history.ts";
@@ -62,7 +62,7 @@ function renderToolResult(result: AgentToolResult<unknown>, _options: ToolRender
62
62
  return new Text(content?.type === "text" ? content.text : "(no output)", 0, 0);
63
63
  }
64
64
 
65
- async function handleHerdrCommand(args: string, ctx: ExtensionCommandContext, fleet: FleetController): Promise<void> {
65
+ async function handleHerdrCommand(args: string, ctx: ExtensionCommandContext, fleet: FleetController, registeredExposure: { value?: "direct" | "model-only"; source: string }): Promise<void> {
66
66
  const section = args.trim() || "active";
67
67
  if (section === "active") {
68
68
  ctx.ui.notify(fleet.active ? activeRunText(fleet.active) : "No active Subagent run.", "info");
@@ -90,7 +90,7 @@ async function handleHerdrCommand(args: string, ctx: ExtensionCommandContext, fl
90
90
  if (section === "settings") {
91
91
  const settings = configuration.settings;
92
92
  const values: Record<string, string | number | undefined> = { defaultConcurrency: settings.defaultConcurrency, maxConcurrency: settings.maxConcurrency, stalledWarningSeconds: settings.stalledWarningSeconds, defaultModel: settings.defaultModel, defaultThinking: settings.defaultThinking };
93
- const lines = Object.entries(values).map(([key, value]) => `${key}=${String(value ?? "unset")} (${configuration.settingSources[key] ?? "built-in"})`);
93
+ const lines = [`exposure=${registeredExposure.value ?? "Pi default"} (${registeredExposure.source}; loaded at extension registration)`, ...Object.entries(values).map(([key, value]) => `${key}=${String(value ?? "unset")} (${configuration.settingSources[key] ?? "built-in"})`)];
94
94
  if (configuration.diagnostics.length) lines.push("Diagnostics:", ...configuration.diagnostics);
95
95
  ctx.ui.notify(lines.join("\n"), "info");
96
96
  return;
@@ -100,6 +100,8 @@ async function handleHerdrCommand(args: string, ctx: ExtensionCommandContext, fl
100
100
 
101
101
  export interface HerdrSubagentsExtensionDependencies {
102
102
  herdr?: HerdrAutomation;
103
+ configurationCwd?: string;
104
+ configurationPaths?: ConfigurationPaths;
103
105
  execute?: typeof executeHerdrSubagents;
104
106
  executeControl?: typeof executeHerdrSubagentsControl;
105
107
  isEditor?: (value: unknown) => boolean;
@@ -121,6 +123,9 @@ export function registerHerdrSubagents(pi: ExtensionAPI, dependencies: HerdrSuba
121
123
  const herdr = dependencies.herdr ?? new CliHerdrAutomation();
122
124
  const execute = dependencies.execute ?? executeHerdrSubagents;
123
125
  const executeControl = dependencies.executeControl ?? executeHerdrSubagentsControl;
126
+ const registrationConfiguration = loadSubagentConfiguration(dependencies.configurationCwd ?? process.cwd(), packageAgentsDir, undefined, dependencies.configurationPaths);
127
+ const exposure = registrationConfiguration.settings.exposure;
128
+ const registeredExposure = { value: exposure, source: registrationConfiguration.settingSources.exposure ?? "Pi" };
124
129
  const isEditorComponent = dependencies.isEditor ?? ((value: unknown) => value instanceof Editor);
125
130
  const fleet = new FleetController(herdr, isEditorComponent, dependencies.createFleetWidget, dependencies.fleetWidgetRuntime);
126
131
  const runControllers = new Map<string, AbortController>();
@@ -141,6 +146,7 @@ export function registerHerdrSubagents(pi: ExtensionAPI, dependencies: HerdrSuba
141
146
 
142
147
  pi.registerTool({
143
148
  name: "herdr_subagents",
149
+ ...(exposure ? { exposure } : {}),
144
150
  label: "Herdr Subagents",
145
151
  description: "Run one or more ordered tasks in visible, focusable Herdr Pi panes. concurrency 1 is strict serial execution; higher values form a bounded pool for read-only tasks. Write-capable tasks require concurrency 1. The call returns only after every task has one terminal completed, failed, or cancelled result. Complete results and Pi sessions stay in project-local records.",
146
152
  promptSnippet: "Run focused work in visible Herdr subagents",
@@ -181,6 +187,7 @@ export function registerHerdrSubagents(pi: ExtensionAPI, dependencies: HerdrSuba
181
187
 
182
188
  pi.registerTool({
183
189
  name: "herdr_subagents_control",
190
+ ...(exposure ? { exposure } : {}),
184
191
  label: "Herdr Subagents Control",
185
192
  description: "Inspect Herdr personas, query one run by ID, list run history, cancel active work, or remove an archived run. Use herdr_subagents to start new work.",
186
193
  parameters: HerdrSubagentsControlParams,
@@ -231,7 +238,7 @@ export function registerHerdrSubagents(pi: ExtensionAPI, dependencies: HerdrSuba
231
238
  pi.registerCommand("herdr-subagents", {
232
239
  description: "Inspect effective subagent agents, models, and settings",
233
240
  getArgumentCompletions: (prefix) => ["active", "history", "agents", "models", "settings", "focus "].filter((value) => value.startsWith(prefix)).map((value) => ({ value, label: value })),
234
- handler: (args, ctx) => handleHerdrCommand(args, ctx, fleet),
241
+ handler: (args, ctx) => handleHerdrCommand(args, ctx, fleet, registeredExposure),
235
242
  });
236
243
  }
237
244
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maxiaochao/pi-herdr-subagents",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "Run inspectable interactive Pi subagent batches through Herdr.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -4,8 +4,10 @@ import { dirname, join } from "node:path";
4
4
  import { discoverPackageAgents, type AgentDefinition } from "./personas.ts";
5
5
 
6
6
  export type PersonaAccess = "read" | "write";
7
+ export type SubagentToolExposure = "direct" | "model-only";
7
8
  export interface PersonaConfig { model?: string; thinking?: string; skills?: string[]; extensions?: string[]; }
8
9
  export interface SubagentConfig {
10
+ exposure?: SubagentToolExposure;
9
11
  defaultConcurrency: number;
10
12
  maxConcurrency: number;
11
13
  stalledWarningSeconds: number;
@@ -32,6 +34,7 @@ export interface SubagentConfiguration {
32
34
  diagnostics: string[];
33
35
  }
34
36
  interface ConfigFile {
37
+ exposure?: string;
35
38
  defaultConcurrency?: number;
36
39
  maxConcurrency?: number;
37
40
  stalledWarningSeconds?: number;
@@ -61,6 +64,10 @@ function readConfig(path: string, source: string, diagnostics: string[]): Config
61
64
 
62
65
  function mergeConfig(base: SubagentConfig, input: ConfigFile, source: string, sources: Record<string, string>, diagnostics: string[]): SubagentConfig {
63
66
  const result: SubagentConfig = { ...base, personas: { ...base.personas } };
67
+ if (input.exposure !== undefined) {
68
+ if (input.exposure === "direct" || input.exposure === "model-only") { result.exposure = input.exposure; sources.exposure = source; }
69
+ else diagnostics.push(`${source} exposure must be 'direct' or 'model-only'`);
70
+ }
64
71
  for (const key of ["defaultConcurrency", "maxConcurrency", "stalledWarningSeconds"] as const) {
65
72
  const value = input[key];
66
73
  if (value === undefined) continue;
@@ -160,10 +160,11 @@ assert.throws(() => applyTaskStatus(terminal, "running"), /terminal tasks are im
160
160
  writeFileSync(join(runDirectory, "run.json"), JSON.stringify(historicalRun));
161
161
  writeFileSync(join(taskDirectory, "task.json"), JSON.stringify({ id: "historical-task", runId: "historical-run", order: 1, status: "completed", completedAt: historicalRun.completedAt }));
162
162
  const events = new Map();
163
+ const registeredTools = new Map();
163
164
  const herdrCalls = [];
164
165
  registerHerdrSubagents({
165
166
  on(name, handler) { events.set(name, handler); },
166
- registerTool() {},
167
+ registerTool(tool) { registeredTools.set(tool.name, tool); },
167
168
  registerCommand() {},
168
169
  }, {
169
170
  herdr: {
@@ -180,6 +181,18 @@ assert.throws(() => applyTaskStatus(terminal, "running"), /terminal tasks are im
180
181
  });
181
182
  assert.deepEqual(herdrCalls, [], "Pi startup must not inspect or close historical Subagent tabs");
182
183
  assert.deepEqual(JSON.parse(readFileSync(join(runDirectory, "run.json"), "utf8")), historicalRun, "Pi startup must not mutate historical Subagent records");
184
+ assert.equal(registeredTools.get("herdr_subagents")?.exposure, undefined, "Unconfigured Subagent execution must inherit Pi's exposure default");
185
+ assert.equal(registeredTools.get("herdr_subagents_control")?.exposure, undefined, "Unconfigured Subagent control must inherit Pi's exposure default");
186
+ mkdirSync(join(lifecycleRoot, ".pi"), { recursive: true });
187
+ writeFileSync(join(lifecycleRoot, ".pi", "herdr-subagents.json"), JSON.stringify({ exposure: "model-only" }));
188
+ const configuredTools = new Map();
189
+ registerHerdrSubagents({
190
+ on() {},
191
+ registerTool(tool) { configuredTools.set(tool.name, tool); },
192
+ registerCommand() {},
193
+ }, { herdr: {}, isEditor: () => false, configurationCwd: lifecycleRoot });
194
+ assert.equal(configuredTools.get("herdr_subagents")?.exposure, "model-only", "Project configuration can exclude Subagent execution from Codemode");
195
+ assert.equal(configuredTools.get("herdr_subagents_control")?.exposure, "model-only", "Project configuration applies consistently to both Subagent tools");
183
196
  }
184
197
 
185
198
  {
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: agent-team
3
- description: 以对抗评审的方式让不同厂商的模型交叉验证同一个结论,当前会话担任管理者。Use when the user asks for 对抗评审 / 交叉验证, or when a costly decision or long evidence chain warrants independent red-teaming.
3
+ description: 多模型协作分析:主 Agent 按配置的模型和角色调度独立子代理,汇总结果并维护调查档案。用于用户要求多模型分析或对抗评审,或高代价决策需要独立复核时。
4
4
  ---
5
5
 
6
- 当前会话是**管理者**,不是转述人:自己也承担一份实证工作,并对每一轮的裁决负责。每轮并发启动三个相互独立的子代理,统一收集结果后由管理者裁决。子代理不需要常驻;跨轮连续性来自持久调查档案和逻辑成员身份,而不是聊天记忆。
6
+ 当前会话是**主 Agent**,负责核心调度:按配置中的模型和角色启动独立子代理,分派用户问题,收集结果并维护调查档案。主 Agent 根据结果决定直接回答、补充核验或继续下一轮。子代理不需要常驻;跨轮连续性来自持久调查档案和逻辑成员身份,而不是聊天记忆。
7
7
 
8
8
  ## 何时值得开
9
9
 
10
- 用户明确要求组队时开启;模型也可在结论会驱动高代价动作(改内核、下线设备、向客户定责),或证据链已经长到单模型容易自我确认时主动开启。查一个事实、读一份文件用不上。
10
+ 用户明确要求多模型分析或对抗评审时开启;主 Agent 也可在结论会驱动高代价动作(改内核、下线设备、向客户定责)时主动开启。查一个事实、读一份文件用不上。
11
11
 
12
12
  ## 1. 建立调查档案
13
13
 
@@ -19,46 +19,42 @@ description: 以对抗评审的方式让不同厂商的模型交叉验证同一
19
19
  ├── FINDINGS-stageN.md # 当前唯一事实源;新阶段整体取代旧阶段
20
20
  ├── LEDGER.md # 只增不改的 N/X 记录
21
21
  └── rounds/
22
- └── N.md # 本轮角色映射和可证伪攻击点
22
+ └── N.md # 本轮成员、模型、角色和分派问题
23
23
  ```
24
24
 
25
- `BRIEF.md` 只记录稳定背景。`FINDINGS-stageN.md` 必含机制链、编号发现、已推翻清单和未闭合点。`LEDGER.md` 中发现按 `N1, N2…` 追加,被推翻的结论按 `X1, X2…` 追加并写明推翻者。只有管理者能更新这三个权威文件;子代理只提交候选证据。
25
+ `BRIEF.md` 只记录稳定背景。`FINDINGS-stageN.md` 记录当前问题、编号发现及证据、已推翻结论和待处理项。`LEDGER.md` 中发现按 `N1, N2…` 追加,被推翻的结论按 `X1, X2…` 追加并写明推翻者。只有主 Agent 能更新这三个权威文件;子代理只提交结果和候选证据。
26
26
 
27
27
  ## 2. 配置成员与角色
28
28
 
29
- 名单在同目录的 `models.json`。三个逻辑成员必须来自不同模型厂商,并显式使用各自的 `model`;不能依赖父会话或环境默认模型。启动后核对并在本轮记录中写下每个任务实际生效的模型;无法确认、与名单不符或三个成员落到同一厂商时,本轮无效。
29
+ 读取本 skill 同目录的 [models.json](models.json):`members` 是成员与模型的唯一配置来源,`roles` 是角色职责的唯一配置来源。默认将两个数组按顺序一一对应;配置为空或数量不匹配时,报告配置问题后再启动。
30
30
 
31
- 三个成员使用相同的中性只读执行约束,方法角色由每轮任务动态赋予:
31
+ 按配置成员数并发启动子代理,显式传入各自的 `model`,并将对应角色的 `charter` 放入任务。成员使用相同的中性只读执行约束。启动后核对并在本轮记录中写下每个任务实际生效的模型;无法确认或与配置不符时,该成员结果无效并报告原因。
32
32
 
33
- - `auditor`:审源码、规范和提交,只接受可定位的直接证据。
34
- - `replicator`:绕开管理者的脚本和口径,从原始数据独立复算。
35
- - `adversary`:攻击前提和方法学,查样本敏感性、一对多计数和阴性对照。
36
-
37
- 模型与角色不得永久绑定。首轮可以按 `models.json` 顺序分配三个角色;后续根据争议轮换或交叉复核,使模型差异和角色差异能够被区分。每轮在 `rounds/N.md` 写明成员、模型、角色、输入文件和攻击点。
33
+ 每轮在 `rounds/N.md` 写明成员、模型、角色、输入文件和分派问题。默认沿用角色映射;需要交叉复核时可轮换,并记录新的映射。
38
34
 
39
35
  ## 3. 交底与通信
40
36
 
41
- 拓扑是星型,管理者是唯一消息路由。成员在同一轮中独立作答,在提交前不得看到其他成员本轮答案。需要逻辑成员 A 反驳逻辑成员 B 的上一轮结论时,由管理者在新任务中引用 B 的原话和证据位置;不能假定新实例记得上一轮聊天。
37
+ 拓扑是星型,主 Agent 是唯一消息路由。成员在同一轮中独立作答,在提交前不得看到其他成员本轮答案。需要逻辑成员 A 复核逻辑成员 B 的上一轮结论时,由主 Agent 在新任务中引用 B 的原话和证据位置;不能假定新实例记得上一轮聊天。
42
38
 
43
- 下行只给短任务和文件指针:每个成员阅读 `BRIEF.md`、最新 `FINDINGS-stageN.md`、`LEDGER.md` 和本轮文件。不得要求它重新梳理已经确认的全部背景。上行结果必须能归属到轮次和成员,并包含证据位置;长结果写入持久文件,不能依赖截断的终端文本。
39
+ 下行给用户问题、角色职责、具体任务和文件指针:每个成员阅读 `BRIEF.md`、最新 `FINDINGS-stageN.md`、`LEDGER.md` 和本轮文件,使用档案恢复背景。上行结果必须能归属到轮次和成员,并包含结论、证据位置和未解决问题;长结果写入持久文件,不能依赖截断的终端文本。
44
40
 
45
41
  ## 4. 回合
46
42
 
47
- 一轮 = 记录受保护文件和工作树初始状态 → 写本轮任务 → 三个成员并发独立调查 → 管理者收齐结果 → 检查子代理未修改项目或权威文件 → 核验证据并裁决 → 更新 FINDINGS 和 LEDGER → 决定下一轮。
43
+ 一轮 = 记录受保护文件和工作树初始状态 → 写本轮任务 → 配置成员并发独立分析 → 主 Agent 收齐结果或失败报告 → 检查子代理未修改项目或权威文件 → 汇总并核验关键证据 → 更新 FINDINGS 和 LEDGER → 决定收尾或继续。
48
44
 
49
- 每个成员必须收到具体到能证伪的攻击点,例如“查 `adbconnection.cc`,确认该错误日志与被销毁的 fd 是否 1:1”,而不是“看看有没有问题”。子代理结束后即可释放;下一轮通过调查档案恢复事实状态,并重新创建独立任务。
45
+ 每个成员收到范围明确、可检查结果的任务。例如“查 `adbconnection.cc`,确认该错误日志与被销毁的 fd 是否 1:1”。只有存在需要补证、复核或继续处理的问题时才开下一轮,并写明目标;子代理结束后即可释放。
50
46
 
51
47
  ## 5. 纪律
52
48
 
53
49
  - **独立性**:同轮答案不得互相可见;成员之间不直接通信。
54
50
  - **只读完整性**:只读是协议约束,不假定执行环境构成沙箱。每轮结束后比较初始状态;发现子代理修改项目或权威文件时,该成员结果无效并显式报告污染范围。
55
- - **retraction**:管理者被驳倒时,在下一轮开头明确写“我错了,更正为……”,并追加对应 X。
56
- - **replication**:任何承载结论的数字至少由两个不同模型独立计算;对不上先查口径再查代码。
57
- - **control**:任何“异常”指标必须配阴性样本才进入 FINDINGS。
58
- - **grading**:每条结论标注 `源码读取 / 实测 / 推断` 和置信度;推断不能与实测并列陈述。
59
- - **裁决**:成员冲突时管理者必须判定谁对并说明证据,不能多数投票或折中平均。
51
+ - **更正**:结论被推翻时,明确记录更正及依据,并追加对应 X。
52
+ - **证据**:区分直接证据、实测、推断和未知;结论附证据位置。
53
+ - **分歧**:根据证据说明支持、推翻或未决;未决项写明缺失证据,不以多数投票或折中平均替代核验。
60
54
  - **缺席视角**:成员失败或额度耗尽时,明确记录缺失的模型和方法角色,不能把不完整轮次当成完整评审。
61
55
 
62
- ## 6. 收尾
56
+ ## 6. 按需核验与收尾
57
+
58
+ 主 Agent 根据问题需要选择角色轮换、跨模型独立复算、阴性对照或开放攻击。开放攻击时,由各成员自行选择其认为最强的反证方向。这些是补充核验手段,不是每次分析的必经步骤;任务仍须遵循所分配角色的 `charter`。
63
59
 
64
- 收尾前必须进行一轮**开放攻击**:三个方法角色各自选择其认为最强的攻击点,不能只回答管理者预设的窄问题。只有完整开放攻击轮没有产生任何被管理者接受并记入 X 的新反证时才算收敛。最终文档单列未闭合点、缺失视角,以及每一个需要人或设备批准才能执行的动作。
60
+ 当用户问题已有足够证据支持的回答,或剩余问题受缺失输入、工具或权限阻塞时,可以收尾。最终回答汇总结论及依据,单列未解决问题、失败或缺席成员,以及需要人或设备批准的动作;受阻停止时明确说明限制,不宣称已经收敛。