pi-subagents 0.46.0 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/docs/agents.md +1 -1
- package/docs/configuration.md +14 -6
- package/docs/extension-api.md +1 -1
- package/docs/missions.md +5 -3
- package/docs/models.md +3 -1
- package/docs/observability.md +3 -3
- package/docs/tool-reference.md +2 -2
- package/docs/watchdog.md +1 -1
- package/package.json +1 -1
- package/skills/pi-subagents/references/execution-controls.md +4 -4
- package/skills/pi-subagents/references/management-authoring-rpc.md +1 -1
- package/src/extension/config.ts +3 -0
- package/src/extension/fanout-child.ts +5 -4
- package/src/extension/index.ts +30 -3
- package/src/extension/rpc.ts +3 -6
- package/src/extension/schemas.ts +25 -5
- package/src/extension/tool-description.ts +26 -8
- package/src/inspectors/herdr/project-panes.ts +2 -1
- package/src/missions/store.ts +2 -1
- package/src/missions/workflow-state.ts +19 -13
- package/src/runs/background/async-execution.ts +10 -5
- package/src/runs/background/async-job-tracker.ts +15 -0
- package/src/runs/background/async-resume.ts +19 -3
- package/src/runs/background/async-status.ts +6 -1
- package/src/runs/background/control-channel.ts +36 -0
- package/src/runs/background/result-watcher.ts +16 -2
- package/src/runs/background/scheduled-runs.ts +2 -1
- package/src/runs/background/stale-run-reconciler.ts +2 -21
- package/src/runs/background/subagent-runner.ts +47 -6
- package/src/runs/foreground/async-steering-action.ts +1 -1
- package/src/runs/foreground/chain-execution.ts +3 -0
- package/src/runs/foreground/execution.ts +3 -0
- package/src/runs/foreground/subagent-executor.ts +95 -12
- package/src/runs/foreground/workflow-foreground-steering.ts +187 -0
- package/src/runs/shared/dynamic-fanout.ts +1 -1
- package/src/runs/shared/model-fallback.ts +8 -4
- package/src/runs/shared/model-scope.ts +12 -2
- package/src/runs/shared/parallel-utils.ts +1 -0
- package/src/runs/shared/worktree.ts +3 -2
- package/src/shared/artifacts.ts +14 -14
- package/src/shared/display-text.ts +100 -0
- package/src/shared/formatters.ts +4 -6
- package/src/shared/settings.ts +15 -2
- package/src/shared/types.ts +11 -1
- package/src/shared/utils.ts +43 -33
- package/src/slash/slash-commands.ts +3 -1
- package/src/tui/fleet-status.ts +14 -10
- package/src/tui/render.ts +30 -26
- package/src/watchdog/change-signature.ts +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.47.0] - 2026-08-11
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- Add optional strict model-scope enforcement that rejects inherited and fallback models outside the configured allowlist. Thanks to @antonioc-cl for #995.
|
|
9
|
+
- Trim legacy chain-control schema fields and guidance by default, saving 1,319 `o200k_base` tokens from the serialized default tool schema plus description versus `legacyChainControls: true`. Thanks to @tajquitgenius for #977.
|
|
10
|
+
- Move project-scoped pi-subagents storage from `.pi-subagents/` to `.pi/subagents/` for cleaner project roots. Thanks to @yceachan for #971.
|
|
11
|
+
- Clarify the accepted mission launch object contract for tool callers.
|
|
12
|
+
- Reduce repeated async status parsing, workflow trace projection, and constrained widget rendering work.
|
|
13
|
+
- Keep parsed async statuses cached beyond 50 runs while preserving per-read freshness checks. Thanks to @bcanvural for #982.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- Omit missing configured read files from child task instructions.
|
|
17
|
+
- Stabilize steering recovery budget coverage around the confirmed paused handoff.
|
|
18
|
+
- Keep timeout-sensitive async acceptance verification coverage off Windows CI where signal delivery is intermittent.
|
|
19
|
+
- Retry transient Windows mission state lock creation failures and stabilize no-session steering recovery coverage.
|
|
20
|
+
- Keep async widget running glyphs moving while children are quiet but active. Thanks to @bcanvural for #983.
|
|
21
|
+
- Let active-session workflows live-steer their workflow-owned foreground children by child or unique workflow identity. Thanks to @youlikemodernart for #988.
|
|
22
|
+
- Accept persisted async recovery descriptors that include internal turn-budget state fields (#985).
|
|
23
|
+
- Add a 30-minute default wall-clock timeout to async children while keeping async composite parents unbounded by default. Thanks to @forrestbthomas for #978 and #979.
|
|
24
|
+
- Keep tool argument previews on one physical terminal line so live widget updates do not leave stacked terminal frames. Thanks to @xz-dev for #972.
|
|
25
|
+
- Recover durable async completions when a healthy native result watcher misses a filesystem event. Thanks to @xz-dev for #973.
|
|
26
|
+
|
|
5
27
|
## [0.46.0] - 2026-08-11
|
|
6
28
|
|
|
7
29
|
### Added
|
|
@@ -12,6 +34,7 @@
|
|
|
12
34
|
- Persist workflow child attempts, status heartbeats, session paths, and artifacts in their enclosing mission, and add explicit mission decision resolution.
|
|
13
35
|
|
|
14
36
|
### Fixed
|
|
37
|
+
- Suspend subagent status widgets during automatic compaction to prevent duplicate terminal frames.
|
|
15
38
|
- Make live foreground workflow children visible as workflow-owned Fleet rows, route Herdr inspection to their workflow parent, and report their active and needs-attention state to Herdr. Thanks to @lukechen526 for #965.
|
|
16
39
|
- Wait for retained-child resumes inside `workflowScript` to finish and return completed output before the script continues (#961).
|
|
17
40
|
- Keep fork-context workflow children inside their managed worktree by aligning the persisted child session cwd before launch. Thanks to @flopsi for #953.
|
package/docs/agents.md
CHANGED
|
@@ -228,7 +228,7 @@ How it works:
|
|
|
228
228
|
|
|
229
229
|
- `refine` collects bounded evidence from that agent's recent runs in the project (statuses, errors, review findings, residual risks, output tails), then launches a fresh read-only proposal child to draft small guidance edits from that evidence.
|
|
230
230
|
- Proposed guidance is validated before it is written. Edits that try to override safety, policy, tool, output, acceptance, developer, or system instructions are rejected, as are edits that target all agents or base agent files.
|
|
231
|
-
- The accepted overlay is stored at `.pi
|
|
231
|
+
- The accepted overlay is stored at `.pi/subagents/refinements/<agent>.md` with revision metadata and snapshots. Each `refine` or `refine.rollback` adds a snapshot, and `refine.rollback` restores the previous revision.
|
|
232
232
|
- At launch, the current overlay is injected into that agent's child system prompt as a `<pi-subagents-refinement>` block scoped to this project. The base agent definition is never modified.
|
|
233
233
|
|
|
234
234
|
`refine.show` prints the current overlay and revision history. Delete the overlay file to remove the refinement entirely.
|
package/docs/configuration.md
CHANGED
|
@@ -28,6 +28,14 @@ Controls the parent-facing `subagent` tool description registered at startup. `f
|
|
|
28
28
|
|
|
29
29
|
`custom` reads `subagent-tool-description.md` from the project config directory, then from `~/.pi/agent/subagent-tool-description.md`. Missing, empty, unreadable, or oversized custom files fall back to the full description. Custom templates may use `{{fullDescription}}`, `{{compactDescription}}`, `{{safetyGuidance}}`, `{{agentDir}}`, and `{{projectConfigDir}}`; the safety guidance is always present so custom prose cannot remove the runtime guardrails. Restart Pi after changing the mode or custom file.
|
|
30
30
|
|
|
31
|
+
## `legacyChainControls`
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{ "legacyChainControls": true }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Defaults to `false`. The default registered model-facing tool schema and description omit the legacy `append-step` `step` schema and legacy checkpoint controls. This does not change runtime support for existing durable legacy chains. Set this to `true` before directly managing a legacy chain with `append-step`, `approve-checkpoint`, or `reject-checkpoint`.
|
|
38
|
+
|
|
31
39
|
## `inlineToolDisplay`
|
|
32
40
|
|
|
33
41
|
```json
|
|
@@ -133,7 +141,7 @@ A user may explicitly call `subagent({ action: "grant-spawn-budget", additional:
|
|
|
133
141
|
{ "scheduledRuns": { "enabled": false, "maxPending": 20 } }
|
|
134
142
|
```
|
|
135
143
|
|
|
136
|
-
Durable schedules are enabled by default and stored per project under `.pi
|
|
144
|
+
Durable schedules are enabled by default and stored per project under `.pi/subagents/schedules/<id>/`. See [missions.md](missions.md#schedules) for usage.
|
|
137
145
|
|
|
138
146
|
Set `storeRoot` to keep durable schedules outside project repositories. It must be an absolute path or a `~/` path, which expands from the user home directory. Each project is stored under a hash of its resolved working directory, so projects do not share schedules.
|
|
139
147
|
|
|
@@ -141,7 +149,7 @@ Set `storeRoot` to keep durable schedules outside project repositories. It must
|
|
|
141
149
|
{ "scheduledRuns": { "storeRoot": "~/.local/share/pi-subagents/schedules" } }
|
|
142
150
|
```
|
|
143
151
|
|
|
144
|
-
When `storeRoot` is omitted, schedules remain at `<cwd>/.pi
|
|
152
|
+
When `storeRoot` is omitted, schedules remain at `<cwd>/.pi/subagents/schedules`.
|
|
145
153
|
|
|
146
154
|
## `parallel`
|
|
147
155
|
|
|
@@ -245,7 +253,7 @@ stdin is a JSON object with `repoRoot`, `worktreePath`, `agentCwd`, `branch`, `i
|
|
|
245
253
|
{
|
|
246
254
|
"missions": {
|
|
247
255
|
"enabled": true,
|
|
248
|
-
"directory": ".pi
|
|
256
|
+
"directory": ".pi/subagents/missions",
|
|
249
257
|
"globalIndex": true,
|
|
250
258
|
"retainTerminal": 200
|
|
251
259
|
}
|
|
@@ -283,15 +291,15 @@ Each fixed action resolves to `"auto"`, `"confirm"`, or `"forbid"`. This is inte
|
|
|
283
291
|
|
|
284
292
|
Controls where subagent artifact files (inputs, outputs, transcripts, metadata) are stored:
|
|
285
293
|
|
|
286
|
-
- `"project"` (default): writes to `<cwd>/.pi
|
|
294
|
+
- `"project"` (default): writes to `<cwd>/.pi/subagents/artifacts/`.
|
|
287
295
|
- `"session"`: stores artifacts under pi's session directory (`~/.pi/agent/sessions/<session>/subagent-artifacts/`), keeping the working directory clean.
|
|
288
296
|
- `"temp"`: uses the OS temp directory.
|
|
289
297
|
|
|
290
|
-
This preference also controls the default chain scratch directory. `"project"` uses `<cwd>/.pi
|
|
298
|
+
This preference also controls the default chain scratch directory. `"project"` uses `<cwd>/.pi/subagents/chain-runs/`, while `"session"` and `"temp"` use the user-scoped temp chain directory.
|
|
291
299
|
|
|
292
300
|
The `"session"` option uses the same directory that `cleanupAllArtifactDirs` already scans for age-based cleanup, so artifacts are still cleaned up automatically. Temporary chain directories are cleaned up separately after 24 hours.
|
|
293
301
|
|
|
294
|
-
When a project-scoped launch runs from an npm package directory, pi-subagents warns if package settings can include `.pi
|
|
302
|
+
When a project-scoped launch runs from an npm package directory, pi-subagents warns if package settings can include `.pi/subagents/` in the published package. Add `.pi/subagents/` to `.npmignore` (or `.gitignore` when no `.npmignore` exists), use a `files` allowlist that does not include `.pi/subagents/`, or select `"session"` or `"temp"`.
|
|
295
303
|
|
|
296
304
|
## `completionBatch`
|
|
297
305
|
|
package/docs/extension-api.md
CHANGED
|
@@ -268,7 +268,7 @@ subagent({ action: "project.status", cwd: "/path/to/repo" })
|
|
|
268
268
|
subagent({ action: "project.close", cwd: "/path/to/repo" })
|
|
269
269
|
```
|
|
270
270
|
|
|
271
|
-
A project pane runs its own Pi session in the target directory, so subagents launched from that pane use that project's config, agents, skills, files, git state, and missions. The parent session keeps coordination authority; existing headless runs are not moved into the pane. Pane bindings live under `<projectRoot>/.pi
|
|
271
|
+
A project pane runs its own Pi session in the target directory, so subagents launched from that pane use that project's config, agents, skills, files, git state, and missions. The parent session keeps coordination authority; existing headless runs are not moved into the pane. Pane bindings live under `<projectRoot>/.pi/subagents/project-panes/herdr.json` and are only a local pointer to the Herdr pane.
|
|
272
272
|
|
|
273
273
|
Other Pi extensions should use the versioned public TypeScript surface instead of invoking the model-facing tool or importing inspector internals:
|
|
274
274
|
|
package/docs/missions.md
CHANGED
|
@@ -11,7 +11,7 @@ Missions are durable wrappers around runs. The noun map:
|
|
|
11
11
|
- **Run** — one actual subagent execution.
|
|
12
12
|
- **Receipt** — proof or a link for an external outcome, such as a PR, CI check, deployment, or release.
|
|
13
13
|
|
|
14
|
-
Ordinary workflow launches create one enclosing mission by default, with detailed JSON records under `<cwd>/.pi
|
|
14
|
+
Ordinary workflow launches create one enclosing mission by default, with detailed JSON records under `<cwd>/.pi/subagents/missions/` linking objectives, run ids, lifecycle status, decisions, artifact paths, and delivery receipts. Workflow children do not create separate missions. Each workflow child attempt is stored in the enclosing mission with its stable workflow key, run id when known, agent, task metadata, timestamps, session and artifact paths, and latest status heartbeat.
|
|
15
15
|
|
|
16
16
|
Behavior:
|
|
17
17
|
|
|
@@ -19,7 +19,9 @@ Behavior:
|
|
|
19
19
|
- Human receipts end with `Mission: <id> (<status>)`, while JSON/structured output text stays unchanged and `details.missionId` is authoritative.
|
|
20
20
|
- Pass `mission: false` for an intentionally ephemeral workflow. It creates no mission for the workflow or its children and has no `state` global.
|
|
21
21
|
- Set `missions.enabled: false` to disable automatic mission creation; explicit mission fields and actions still work.
|
|
22
|
-
- A workflow with a mission can use `await state.get(key)` and `await state.set(key, value)` for durable JSON state. Missing keys return `undefined`. Keys use the same format as `runs.run` keys. Each set takes the state-file lock, reads the latest file, merges the key, and atomically writes `<cwd>/.pi
|
|
22
|
+
- A workflow with a mission can use `await state.get(key)` and `await state.set(key, value)` for durable JSON state. Missing keys return `undefined`. Keys use the same format as `runs.run` keys. Each set takes the state-file lock, reads the latest file, merges the key, and atomically writes `<cwd>/.pi/subagents/missions/<mission-id>/state.json`. The complete file cannot exceed 256 KiB. Each workflow caches the file on its first `get`. A `mission:false` workflow has no `state` global.
|
|
23
|
+
|
|
24
|
+
An explicit `mission` object must have exactly one non-empty `title` or `summary`. `objective` and `labels` are optional. When supplied, `goal` must be `true` and requires `budget: { tokens: <positive integer> }`.
|
|
23
25
|
|
|
24
26
|
```ts
|
|
25
27
|
const created = subagent({
|
|
@@ -81,7 +83,7 @@ Mission storage configuration (`missions.directory`, `retainTerminal`, `globalIn
|
|
|
81
83
|
|
|
82
84
|
## Schedules
|
|
83
85
|
|
|
84
|
-
Durable schedules are enabled by default and stored per project under `.pi
|
|
86
|
+
Durable schedules are enabled by default and stored per project under `.pi/subagents/schedules/<id>/`.
|
|
85
87
|
|
|
86
88
|
Create a one-shot schedule:
|
|
87
89
|
|
package/docs/models.md
CHANGED
|
@@ -150,6 +150,7 @@ To keep subagents inside a budget or compliance profile, enforce a model scope.
|
|
|
150
150
|
"subagents": {
|
|
151
151
|
"modelScope": {
|
|
152
152
|
"enforce": true,
|
|
153
|
+
"strict": true,
|
|
153
154
|
"allow": ["anthropic/*", "openai/gpt-5-*"]
|
|
154
155
|
}
|
|
155
156
|
}
|
|
@@ -158,7 +159,8 @@ To keep subagents inside a budget or compliance profile, enforce a model scope.
|
|
|
158
159
|
|
|
159
160
|
- `allow` is a list of glob patterns matched against the resolved `provider/id` (only `*` is special, case-insensitive). A resolved model that matches none of the patterns is rejected.
|
|
160
161
|
- Models you pass explicitly — the tool-call `model`, `--model`, or a clarify pick — error and abort the run.
|
|
161
|
-
-
|
|
162
|
+
- By default, models from agent frontmatter, `subagents.defaultModel`, the inherited parent session model, or fallback chains only warn and remain available, so existing configurations keep working while you tighten the scope.
|
|
163
|
+
- Set `strict: true` with `enforce: true` to reject every resolved out-of-scope model. This includes inherited models and fallback candidates. An invalid fallback fails the run instead of being removed from the candidate chain.
|
|
162
164
|
- `enforce: true` requires a non-empty `allow` list; otherwise the config is rejected at load time.
|
|
163
165
|
|
|
164
166
|
## Profiles and provider model catalogs
|
package/docs/observability.md
CHANGED
|
@@ -126,7 +126,7 @@ Foreground and async runners share bounded child-protocol handling:
|
|
|
126
126
|
|
|
127
127
|
## Chain and debug artifacts
|
|
128
128
|
|
|
129
|
-
Each chain run creates a scratch directory under its resolved chain root. With the default `artifactDir: "project"`, that root is `<cwd>/.pi
|
|
129
|
+
Each chain run creates a scratch directory under its resolved chain root. With the default `artifactDir: "project"`, that root is `<cwd>/.pi/subagents/chain-runs/`. With `artifactDir: "session"` or `"temp"`, it is user-scoped temp storage:
|
|
130
130
|
|
|
131
131
|
```text
|
|
132
132
|
<tmpdir>/pi-subagents-<scope>/chain-runs/{runId}/
|
|
@@ -134,7 +134,7 @@ Each chain run creates a scratch directory under its resolved chain root. With t
|
|
|
134
134
|
|
|
135
135
|
A run directory may contain files such as `context.md`, `plan.md`, `progress.md`, and `parallel-{stepIndex}/.../output.md`. User-scoped temp chain directories older than 24 hours are cleaned up on extension startup; project-local and explicit persistent roots are not age-scanned.
|
|
136
136
|
|
|
137
|
-
Debug artifacts live under `{sessionDir}/subagent-artifacts/`, `.pi
|
|
137
|
+
Debug artifacts live under `{sessionDir}/subagent-artifacts/`, `.pi/subagents/artifacts/` for project-scoped runs, or a user-scoped temp artifact directory. Single-run relative `output` files are saved under `{artifactsDir}/outputs/{runId}/` unless `singleRunOutputBaseDir` is configured. Per task you may see:
|
|
138
138
|
|
|
139
139
|
- `{runId}_{agent}_input.md`
|
|
140
140
|
- `{runId}_{agent}_output.md`
|
|
@@ -143,7 +143,7 @@ Debug artifacts live under `{sessionDir}/subagent-artifacts/`, `.pi-subagents/ar
|
|
|
143
143
|
|
|
144
144
|
Metadata records timing, usage, exit code, final model, attempted models, fallback attempt outcomes, and the resolved acceptance ledger with its parsed child report.
|
|
145
145
|
|
|
146
|
-
For npm package projects, project-scoped artifacts need a `.npmignore` rule (or `.gitignore` when no `.npmignore` exists) or a `files` allowlist that does not include `.pi
|
|
146
|
+
For npm package projects, project-scoped artifacts need a `.npmignore` rule (or `.gitignore` when no `.npmignore` exists) or a `files` allowlist that does not include `.pi/subagents/`. pi-subagents warns at launch when these package settings can include the artifacts. Use `artifactDir: "session"` or `"temp"` to keep them outside the package worktree.
|
|
147
147
|
|
|
148
148
|
## Sessions
|
|
149
149
|
|
package/docs/tool-reference.md
CHANGED
|
@@ -35,7 +35,7 @@ Parameters and actions for the `subagent` tool. These are what the LLM passes wh
|
|
|
35
35
|
| `config` | object/string | - | Agent or existing durable chain config for management create/update. |
|
|
36
36
|
| `context` | `fresh \| fork` | per-agent default or `fresh` | Explicit `fresh` or `fork` overrides every workflow child. When omitted, each child agent uses its own `defaultContext`; `fork` creates real branched sessions from the parent leaf. Packaged `worker`, `oracle`, and `advisor` default to `fork`. |
|
|
37
37
|
| `missionId` | string | - | Attach a workflow to an existing project mission instead of creating its default enclosing mission. |
|
|
38
|
-
| `mission` | object/false | auto-create | Override the default enclosing mission with `{ title, objective?, goal?, budget?, labels? }`; `goal
|
|
38
|
+
| `mission` | object/false | auto-create | Override the default enclosing mission with `{ title \| summary, objective?, goal?, budget?, labels? }`. Set exactly one non-empty `title` or `summary`; `objective` and `labels` are optional. `goal` may only be `true`, requires `budget.tokens`, and enables continuation notices. Pass `false` for an intentionally ephemeral workflow with no mission for it or its children and no `state` global. Explicit mission persistence failures are strict. |
|
|
39
39
|
| `handoffPath` | string | - | Aggregate handoff manifest required by `action: "worktree.discard"`. |
|
|
40
40
|
| `focus` | boolean | true | Focus the newly split pane for `action: "inspector.open"` or `action: "project.open"`; not a standalone action. |
|
|
41
41
|
| `view` | `fleet \| transcript` | - | Optional `status` view for the active fleet surface or transcript tail inspection. |
|
|
@@ -246,7 +246,7 @@ The persisted `steering` ledger retains 20 requests and replaces the old `steerC
|
|
|
246
246
|
|
|
247
247
|
### append-step
|
|
248
248
|
|
|
249
|
-
`append-step` accepts exactly one `step` object for an existing durable chain for a top-level async chain whose status is still `running`. The step is persisted in the run directory and becomes eligible only after the chain's already-queued steps finish. Completed, failed, rejected, paused, foreground, single, and non-chain runs reject appends.
|
|
249
|
+
`append-step` requires `legacyChainControls: true`. The default registered model-facing schema omits this legacy control surface. When enabled, it accepts exactly one `step` object for an existing durable chain for a top-level async chain whose status is still `running`. The step is persisted in the run directory and becomes eligible only after the chain's already-queued steps finish. Completed, failed, rejected, paused, foreground, single, and non-chain runs reject appends.
|
|
250
250
|
|
|
251
251
|
## Acceptance gates
|
|
252
252
|
|
package/docs/watchdog.md
CHANGED
|
@@ -11,7 +11,7 @@ It reviews repo edits, not ordinary conversation:
|
|
|
11
11
|
- It runs at the safe `agent_end` boundary, only when the current agent or child writer changed the final repo state since the start of that turn.
|
|
12
12
|
- Multiple edits in one turn are coalesced into one review of the final changed state.
|
|
13
13
|
- Unchanged/reverted diffs are skipped.
|
|
14
|
-
- Generated `.pi
|
|
14
|
+
- Generated `.pi/subagents/` or `tmp/` artifacts do not trigger review.
|
|
15
15
|
- In orchestrated runs, each writing child can review its own edited worktree, and the parent can still review the aggregate repo diff after child changes are applied.
|
|
16
16
|
|
|
17
17
|
## Choosing a model
|
package/package.json
CHANGED
|
@@ -158,7 +158,7 @@ A cooperating terminal runtime can register read-only external records through `
|
|
|
158
158
|
|
|
159
159
|
### Scheduled subagent runs
|
|
160
160
|
|
|
161
|
-
Schedules are durable project records under `.pi
|
|
161
|
+
Schedules are durable project records under `.pi/subagents/schedules/`. They are enabled by default; set `{ "scheduledRuns": { "enabled": false } }` in `~/.pi/agent/extensions/subagent/config.json` to disable them. Only schedule explicit work the user asked for.
|
|
162
162
|
|
|
163
163
|
```typescript
|
|
164
164
|
// One-shot reviewer
|
|
@@ -235,7 +235,7 @@ The subagent watchdog is an **opt-in** adversarial change reviewer. It is not th
|
|
|
235
235
|
|
|
236
236
|
When enabled, it reviews actual repo edits at safe `agent_end` boundaries only if
|
|
237
237
|
the final worktree state changed during that turn. Unchanged or reverted diffs and
|
|
238
|
-
generated `.pi
|
|
238
|
+
generated `.pi/subagents/` / temp artifacts do not trigger review. Writing children
|
|
239
239
|
can review their own worktree; the parent can still review the aggregate diff after
|
|
240
240
|
child changes land. Enabled watchdogs also run changed-file TypeScript/JavaScript
|
|
241
241
|
LSP diagnostics before the model pass when `typescript-language-server` is available.
|
|
@@ -298,7 +298,7 @@ Routing rule:
|
|
|
298
298
|
- Several projects with independent work: one async `workflowScript` whose child keys include repo slugs and whose child calls set explicit `cwd`; keep publication and merge decisions serial per repo.
|
|
299
299
|
- Different project, substantial or long-running work: open a project-owned Herdr pane rooted there when a separate visible project session is useful, then give that project Pi session a narrow mission/result contract. Do not model it as ordinary child nesting, and do not expect existing headless runs to move into the pane.
|
|
300
300
|
|
|
301
|
-
Project panes run a separate Pi session from the target directory. Subagents launched inside that pane use that project's config, agents, skills, files, git state, and mission records. The pane binding lives under `<projectRoot>/.pi
|
|
301
|
+
Project panes run a separate Pi session from the target directory. Subagents launched inside that pane use that project's config, agents, skills, files, git state, and mission records. The pane binding lives under `<projectRoot>/.pi/subagents/project-panes/herdr.json`. For ordinary headless delegation to another repo, prefer explicit `cwd` first; reserve project panes for visible or persistent project ownership.
|
|
302
302
|
|
|
303
303
|
```typescript
|
|
304
304
|
subagent({ action: "mission.create", mission: { title: "Ship auth refresh", objective: "Implement and validate refresh handling" } })
|
|
@@ -340,7 +340,7 @@ single-writer pattern instead.
|
|
|
340
340
|
|
|
341
341
|
Git worktrees start from tracked files, so ignored or untracked build state
|
|
342
342
|
such as `node_modules` may be absent. The clean-check ignores pi-subagents'
|
|
343
|
-
own `.pi
|
|
343
|
+
own `.pi/subagents/` runtime state, including default mission records, but still
|
|
344
344
|
rejects ordinary source/config changes. `pi-subagents` attempts to symlink the
|
|
345
345
|
root checkout's `node_modules` into each managed worktree when it exists, but
|
|
346
346
|
agents should still treat dependency setup as an explicit bootstrap step before
|
|
@@ -28,7 +28,7 @@ subagent({ action: "refine.show", agent: "reviewer" })
|
|
|
28
28
|
subagent({ action: "refine.rollback", agent: "reviewer" })
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
`refine` builds a bounded project-local guidance overlay for one agent from recent run evidence, using a fresh read-only proposal child; validated guidance is stored under `.pi
|
|
31
|
+
`refine` builds a bounded project-local guidance overlay for one agent from recent run evidence, using a fresh read-only proposal child; validated guidance is stored under `.pi/subagents/refinements/<agent>.md` with revision snapshots and is injected into that agent's child system prompt for this project. `refine.show` prints the current overlay and history; `refine.rollback` restores the previous revision. Guidance that tries to override safety, policy, tool, output, acceptance, developer, or system instructions is rejected. `/subagents-refine <agent>` is the slash equivalent.
|
|
32
32
|
|
|
33
33
|
### Create an agent
|
|
34
34
|
|
package/src/extension/config.ts
CHANGED
|
@@ -41,6 +41,9 @@ function validateConfig(config: Record<string, unknown>): void {
|
|
|
41
41
|
if (config.artifactDir !== undefined && !ARTIFACT_DIR_PREFERENCES.has(config.artifactDir as ArtifactDirPreference)) {
|
|
42
42
|
throw new Error(`config.artifactDir must be "project", "session", or "temp"`);
|
|
43
43
|
}
|
|
44
|
+
if (config.legacyChainControls !== undefined && typeof config.legacyChainControls !== "boolean") {
|
|
45
|
+
throw new Error("config.legacyChainControls must be a boolean");
|
|
46
|
+
}
|
|
44
47
|
validateMissionStoreConfig(config.missions);
|
|
45
48
|
validateAuthorityPolicy(config.authorityPolicy);
|
|
46
49
|
validatePermissionConfig(config.permissions);
|
|
@@ -10,7 +10,7 @@ import { SUBAGENT_CHILD_ENV, SUBAGENT_FANOUT_CHILD_ENV } from "../runs/shared/pi
|
|
|
10
10
|
import { readNestedControlRequests, resolveNestedRouteFromEnv, type NestedRoute, writeNestedControlResult } from "../runs/shared/nested-events.ts";
|
|
11
11
|
import { deliverSubagentIntercomMessageEvent } from "../intercom/result-intercom.ts";
|
|
12
12
|
import { resolveSubagentIntercomTarget } from "../intercom/intercom-bridge.ts";
|
|
13
|
-
import {
|
|
13
|
+
import { createSubagentParamsSchema } from "./schemas.ts";
|
|
14
14
|
import { loadConfig, resolveAsyncByDefault } from "./config.ts";
|
|
15
15
|
import { type Details, type SubagentState } from "../shared/types.ts";
|
|
16
16
|
|
|
@@ -171,15 +171,16 @@ export default function registerFanoutChildSubagentExtension(pi: ExtensionAPI):
|
|
|
171
171
|
allowMutatingManagementActions: false,
|
|
172
172
|
});
|
|
173
173
|
|
|
174
|
-
const
|
|
174
|
+
const params = createSubagentParamsSchema(config);
|
|
175
|
+
const tool: ToolDefinition<typeof params, Details> = {
|
|
175
176
|
name: "subagent",
|
|
176
177
|
label: "Subagent",
|
|
177
178
|
description: [
|
|
178
179
|
"Delegate to subagents from child-safe fanout mode.",
|
|
179
|
-
|
|
180
|
+
`Allowed management/control actions: list, get, status, interrupt, resume, steer${config.legacyChainControls === true ? ", append-step" : ""}, doctor.`,
|
|
180
181
|
"Mutating management actions (create, update, delete, eject, disable, enable, reset, grant-spawn-budget) are blocked in this mode.",
|
|
181
182
|
].join("\n"),
|
|
182
|
-
parameters:
|
|
183
|
+
parameters: params,
|
|
183
184
|
execute(id, params, signal, onUpdate, ctx) {
|
|
184
185
|
return executor.executePublic(id, params as SubagentParamsLike, signal ?? new AbortController().signal, onUpdate, ctx);
|
|
185
186
|
},
|
package/src/extension/index.ts
CHANGED
|
@@ -27,7 +27,7 @@ import { cleanupOldChainDirs } from "../shared/settings.ts";
|
|
|
27
27
|
import { clearLegacyResultAnimationTimer, renderSubagentResult, renderSubagentSummary } from "../tui/render.ts";
|
|
28
28
|
import { openSubagentFleet } from "../tui/fleet.ts";
|
|
29
29
|
import { SubagentFleetStatus, resolveFleetViewPlacement } from "../tui/fleet-status.ts";
|
|
30
|
-
import {
|
|
30
|
+
import { createSubagentParamsSchema } from "./schemas.ts";
|
|
31
31
|
import { createSubagentExecutor, type SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
|
|
32
32
|
import { createAsyncJobTracker } from "../runs/background/async-job-tracker.ts";
|
|
33
33
|
import { createResultWatcher } from "../runs/background/result-watcher.ts";
|
|
@@ -392,6 +392,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
392
392
|
lastUiContext: null,
|
|
393
393
|
poller: null,
|
|
394
394
|
completionSeen: new Map(),
|
|
395
|
+
widgetsSuspended: false,
|
|
395
396
|
watcher: null,
|
|
396
397
|
watcherRestartTimer: null,
|
|
397
398
|
resultFileCoalescer: {
|
|
@@ -566,11 +567,12 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
566
567
|
});
|
|
567
568
|
|
|
568
569
|
|
|
569
|
-
const
|
|
570
|
+
const parameters = createSubagentParamsSchema(config);
|
|
571
|
+
const tool: ToolDefinition<typeof parameters, Details> = {
|
|
570
572
|
name: "subagent",
|
|
571
573
|
label: "Subagent",
|
|
572
574
|
description: buildSubagentToolDescription(config),
|
|
573
|
-
parameters
|
|
575
|
+
parameters,
|
|
574
576
|
|
|
575
577
|
execute(id, params, signal, onUpdate, ctx) {
|
|
576
578
|
return executeSubagentCollapsed(id, params as SubagentParamsLike, signal ?? new AbortController().signal, onUpdate, ctx);
|
|
@@ -713,7 +715,22 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
713
715
|
}
|
|
714
716
|
};
|
|
715
717
|
|
|
718
|
+
const suspendWidgetsForCompaction = () => {
|
|
719
|
+
if (state.widgetsSuspended) return;
|
|
720
|
+
state.widgetsSuspended = true;
|
|
721
|
+
if (state.lastUiContext?.hasUI) state.lastUiContext.ui.setWidget(WIDGET_KEY, undefined);
|
|
722
|
+
fleetStatus?.refresh();
|
|
723
|
+
};
|
|
724
|
+
const resumeWidgetsAfterCompaction = () => {
|
|
725
|
+
if (!state.widgetsSuspended) return;
|
|
726
|
+
state.widgetsSuspended = false;
|
|
727
|
+
const ctx = state.lastUiContext;
|
|
728
|
+
if (ctx?.hasUI) refreshWidget(ctx);
|
|
729
|
+
fleetStatus?.refresh();
|
|
730
|
+
};
|
|
731
|
+
|
|
716
732
|
const resetSessionState = (ctx: ExtensionContext, recovering: boolean) => {
|
|
733
|
+
state.widgetsSuspended = false;
|
|
717
734
|
state.baseCwd = ctx.cwd;
|
|
718
735
|
goalTurnId = 0;
|
|
719
736
|
state.currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
@@ -752,9 +769,18 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
752
769
|
};
|
|
753
770
|
|
|
754
771
|
pi.on("agent_start", () => {
|
|
772
|
+
resumeWidgetsAfterCompaction();
|
|
755
773
|
herdrStatusBridge.agentStarted();
|
|
756
774
|
});
|
|
757
775
|
|
|
776
|
+
pi.on("agent_settled", () => {
|
|
777
|
+
resumeWidgetsAfterCompaction();
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
pi.on("session_before_compact", (event) => {
|
|
781
|
+
if (event.reason !== "manual") suspendWidgetsForCompaction();
|
|
782
|
+
});
|
|
783
|
+
|
|
758
784
|
pi.on("session_compact", () => {
|
|
759
785
|
const hasActiveAsyncWork = [...state.asyncJobs.values()].some((job) => job.status === "queued" || job.status === "running");
|
|
760
786
|
if (!hasActiveAsyncWork || state.lastUiContext?.hasUI !== true) return;
|
|
@@ -780,6 +806,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
780
806
|
});
|
|
781
807
|
|
|
782
808
|
pi.on("session_shutdown", async () => {
|
|
809
|
+
state.widgetsSuspended = false;
|
|
783
810
|
stopResultWatcher();
|
|
784
811
|
state.currentSessionId = null;
|
|
785
812
|
state.parentSessionFile = null;
|
package/src/extension/rpc.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
SUBAGENT_PROCESS_TERMINAL_EVENT,
|
|
17
17
|
SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
|
|
18
18
|
} from "../shared/types.ts";
|
|
19
|
+
import { sanitizeDisplayText, truncateDisplayText } from "../shared/display-text.ts";
|
|
19
20
|
import { readStatus } from "../shared/utils.ts";
|
|
20
21
|
import { SubagentParams } from "./schemas.ts";
|
|
21
22
|
import { formatWorkflowJsonPreview } from "../workflows/scripted-workflow.ts";
|
|
@@ -101,12 +102,8 @@ const MAX_METADATA_LENGTH = 128;
|
|
|
101
102
|
|
|
102
103
|
function displayText(value: unknown, maxLength: number): string | undefined {
|
|
103
104
|
if (typeof value !== "string") return undefined;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const normalized = value.slice(0, 4_096)
|
|
107
|
-
.replace(/\x1b\[[0-?]*[ -/]*[@-~]|\x9b[0-?]*[ -/]*[@-~]|\x1b][\s\S]*?(?:\x07|\x1b\\)|\x1b[PX^_][\s\S]*?\x1b\\|[\u0000-\u001f\u007f-\u009f]/g, " ")
|
|
108
|
-
.replace(/\s+/g, " ").trim();
|
|
109
|
-
return normalized ? normalized.slice(0, maxLength) : undefined;
|
|
105
|
+
const normalized = sanitizeDisplayText(value.slice(0, 4_096));
|
|
106
|
+
return normalized ? truncateDisplayText(normalized, maxLength) : undefined;
|
|
110
107
|
}
|
|
111
108
|
|
|
112
109
|
function publicTokens(value: unknown): { input: number; output: number; total: number } {
|
package/src/extension/schemas.ts
CHANGED
|
@@ -254,7 +254,7 @@ const ControlOverrides = Type.Object({
|
|
|
254
254
|
})),
|
|
255
255
|
});
|
|
256
256
|
|
|
257
|
-
const
|
|
257
|
+
const SubagentParamProperties = {
|
|
258
258
|
agent: Type.Optional(Type.String({ description: "Agent target for management actions such as get, update, delete, and models." })),
|
|
259
259
|
resume: Type.Optional(Type.String({ description: "Retained child run id for a workflowScript runs.run/runs.all item. Mutually exclusive with agent; task supplies the follow-up." })),
|
|
260
260
|
// Management action (when present, tool operates in management mode)
|
|
@@ -296,7 +296,7 @@ const SubagentParamsSchema = Type.Object({
|
|
|
296
296
|
overlap: Type.Optional(Type.String({ enum: ["skip"], description: "Overlap policy. This slice supports skip only." })),
|
|
297
297
|
catchUp: Type.Optional(Type.String({ enum: ["none", "latest"], description: "Missed occurrence policy for recurring schedules. Defaults to latest." })),
|
|
298
298
|
missionId: Type.Optional(Type.String({ description: "Mission id." })),
|
|
299
|
-
mission: Type.Optional(Type.Unsafe({ ...MissionLaunchOverride, description: "Mission object, or false for no mission.
|
|
299
|
+
mission: Type.Optional(Type.Unsafe({ ...MissionLaunchOverride, description: "Mission object, or false for no mission. Set exactly one non-empty title or summary; objective and labels are optional. goal may only be true and then requires budget.tokens." })),
|
|
300
300
|
missionUpdate: Type.Optional(Type.Unsafe({ ...MissionUpdateOverride, description: "Mission update: objective, goal false or {paused:boolean}, budget, summary, labels, decisions, artifacts, or delivery receipts." })),
|
|
301
301
|
missionStatus: Type.Optional(Type.String({ description: "Mission status." })),
|
|
302
302
|
missionScope: Type.Optional(Type.String({ description: "Mission list scope: project (default) or global pointer index." })),
|
|
@@ -324,8 +324,8 @@ const SubagentParamsSchema = Type.Object({
|
|
|
324
324
|
description: "'fresh' or 'fork' to branch from parent session. Explicit context overrides every child in the invocation. If omitted, each requested agent uses its own defaultContext; agents without defaultContext: 'fork' run fresh.",
|
|
325
325
|
})),
|
|
326
326
|
async: Type.Optional(Type.Boolean({ description: "Run in background (default: false, or per config)" })),
|
|
327
|
-
timeoutMs: Type.Optional(Type.Integer({ minimum: 1, description: "
|
|
328
|
-
maxRuntimeMs: Type.Optional(Type.Integer({ minimum: 1, description: "Alias timeoutMs
|
|
327
|
+
timeoutMs: Type.Optional(Type.Integer({ minimum: 1, description: "Run timeout. Foreground runs and async children default to 30m; async composites have no default parent deadline. Alias maxRuntimeMs." })),
|
|
328
|
+
maxRuntimeMs: Type.Optional(Type.Integer({ minimum: 1, description: "Alias timeoutMs. Foreground runs and async children default to 30m; async composites have no default parent deadline." })),
|
|
329
329
|
turnBudget: Type.Optional(TurnBudgetOverride),
|
|
330
330
|
toolBudget: Type.Optional(ToolBudgetOverride),
|
|
331
331
|
usageBudget: Type.Optional(UsageBudgetOverride),
|
|
@@ -353,9 +353,29 @@ const SubagentParamsSchema = Type.Object({
|
|
|
353
353
|
agentContract: Type.Optional(AgentContractOverride),
|
|
354
354
|
acceptance: Type.Optional(AcceptanceOverride),
|
|
355
355
|
gate: Type.Optional(Type.String({ minLength: 1, description: "Host gate command. Cannot be combined with acceptance." })),
|
|
356
|
-
}
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const { step: _legacyChainStep, ...subagentParamPropertiesWithoutStep } = SubagentParamProperties;
|
|
359
|
+
const trimmedSubagentParamProperties = {
|
|
360
|
+
...subagentParamPropertiesWithoutStep,
|
|
361
|
+
id: Type.Optional(Type.String({
|
|
362
|
+
description: "Run id or prefix for status, interrupt, stop, resume, steer, mission.attach-run, or the decision id for mission.resolve-decision."
|
|
363
|
+
})),
|
|
364
|
+
runId: Type.Optional(Type.String({
|
|
365
|
+
description: "Target run ID for interrupt, stop, resume, steer, or mission.attach-run. Prefer id for new calls."
|
|
366
|
+
})),
|
|
367
|
+
};
|
|
368
|
+
const SubagentParamsSchema = Type.Object(SubagentParamProperties);
|
|
369
|
+
const TrimmedSubagentParamsSchema = Type.Object(trimmedSubagentParamProperties);
|
|
357
370
|
|
|
358
371
|
export const SubagentParams = keepTopLevelParameterDescriptions(SubagentParamsSchema);
|
|
372
|
+
export const SubagentParamsWithoutLegacyChainControls = keepTopLevelParameterDescriptions(TrimmedSubagentParamsSchema);
|
|
373
|
+
|
|
374
|
+
export function createSubagentParamsSchema(options: { legacyChainControls?: boolean } = {}): typeof SubagentParams | typeof SubagentParamsWithoutLegacyChainControls {
|
|
375
|
+
return options.legacyChainControls === true
|
|
376
|
+
? SubagentParams
|
|
377
|
+
: SubagentParamsWithoutLegacyChainControls;
|
|
378
|
+
}
|
|
359
379
|
|
|
360
380
|
const SubagentWaitParamsSchema = Type.Object({
|
|
361
381
|
id: Type.Optional(Type.String({
|
|
@@ -22,7 +22,7 @@ EXECUTION:
|
|
|
22
22
|
• Sequential example: { workflowScript: "const a = await runs.run('analyze', {agent:'agent-a', task:'Analyze the request'}); return (await runs.run('plan', {agent:'agent-b', task:'Plan from: '+a.output})).output" }
|
|
23
23
|
• Parallel example: { workflowScript: "const [a,b] = await runs.all([{key:'correctness',agent:'agent-a',task:'Review correctness'},{key:'tests',agent:'agent-b',task:'Review tests'}]); return {correctness:a.output,tests:b.output}" }
|
|
24
24
|
• Optional context is "fresh" or "fork". timeoutMs/maxRuntimeMs apply to foreground and async workflows; foreground workflows default to 30 minutes and async workflows have no default timeout. Omit acceptance for reviewer/read-only calls; evidence levels end at verified, and acceptance.review.required requests independent writer review.
|
|
25
|
-
• Durable mission attachment is automatic by default. Use missionId to attach an existing mission, mission:{...} to override auto-create, or mission:false for ephemeral work.
|
|
25
|
+
• Durable mission attachment is automatic by default. Use missionId to attach an existing mission, mission:{...} to override auto-create, or mission:false for ephemeral work. A mission object needs exactly one non-empty title or summary; objective and labels are optional. goal may only be true and requires budget:{tokens}.
|
|
26
26
|
|
|
27
27
|
MANAGEMENT / CONTROL (use action; omit execution fields):
|
|
28
28
|
• list, get, models, guide, children.list, create, update, delete, eject, disable, enable, reset, doctor, grant-spawn-budget, worktree.discard, refine/refine.show/refine.rollback, mission.create/list/show/update/resolve-decision/attach-run/close, inspector.open/status/close, project.open/status/close, and watchdog actions remain available. Use {action:"guide", topic:"overview"} for packaged current-version help; topics are overview, workflows, agents, missions, observability, tool-reference, configuration, models, watchdog, and extension-api.
|
|
@@ -44,6 +44,7 @@ EXECUTE:
|
|
|
44
44
|
MANAGE / CONTROL:
|
|
45
45
|
• Use action without execution fields for list/get/models/guide/authoring, refine/refine.show/refine.rollback, mission, watchdog, status, interrupt, stop, resume, steer, script-only scheduling, diagnostics, and other management actions. guide reads shipped current-version docs by topic.
|
|
46
46
|
• append-step uses step:{...} only for an already-running durable legacy chain; step is not an execution mode.
|
|
47
|
+
• A mission object needs exactly one non-empty title or summary; objective and labels are optional. goal may only be true and requires budget:{tokens}.
|
|
47
48
|
|
|
48
49
|
ASYNC / SAFETY:
|
|
49
50
|
• Omitted async detaches background work. Do not sleep or poll merely to wait; use subagent_wait only when this turn must receive results.
|
|
@@ -152,13 +153,30 @@ function withMandatorySafetyGuidance(description: string): string {
|
|
|
152
153
|
: SUBAGENT_SAFETY_GUIDANCE;
|
|
153
154
|
}
|
|
154
155
|
|
|
155
|
-
|
|
156
|
+
const LEGACY_CHAIN_CONTROL_GUIDANCE_LINES = new Set([
|
|
157
|
+
'• { action: "append-step", id: "...", step: {agent:"agent-c", task:"Use {previous}"} } appends one step to an already-running durable legacy chain. step is control-only, not an execution mode.',
|
|
158
|
+
"• approve-checkpoint and reject-checkpoint decide a paused durable legacy chain checkpoint.",
|
|
159
|
+
"• append-step uses step:{...} only for an already-running durable legacy chain; step is not an execution mode.",
|
|
160
|
+
]);
|
|
161
|
+
|
|
162
|
+
function withoutLegacyChainControlGuidance(description: string): string {
|
|
163
|
+
return description
|
|
164
|
+
.split("\n")
|
|
165
|
+
.filter((line) => !LEGACY_CHAIN_CONTROL_GUIDANCE_LINES.has(line.trim()))
|
|
166
|
+
.join("\n");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function buildSubagentToolDescription(config: Pick<ExtensionConfig, "toolDescriptionMode" | "legacyChainControls"> = {}, options?: ToolDescriptionOptions): string {
|
|
156
170
|
const mode = resolveToolDescriptionMode(config, options);
|
|
157
|
-
|
|
158
|
-
if (mode === "
|
|
171
|
+
let description: string;
|
|
172
|
+
if (mode === "compact") description = COMPACT_SUBAGENT_TOOL_DESCRIPTION;
|
|
173
|
+
else if (mode === "custom") {
|
|
159
174
|
const custom = loadCustomToolDescription(options);
|
|
160
|
-
if (custom)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
175
|
+
if (custom) description = withMandatorySafetyGuidance(custom);
|
|
176
|
+
else {
|
|
177
|
+
warn(options, `${CUSTOM_TOOL_DESCRIPTION_FILE} was not found or valid for toolDescriptionMode "custom"; using full description.`);
|
|
178
|
+
description = FULL_SUBAGENT_TOOL_DESCRIPTION;
|
|
179
|
+
}
|
|
180
|
+
} else description = FULL_SUBAGENT_TOOL_DESCRIPTION;
|
|
181
|
+
return config.legacyChainControls === true ? description : withoutLegacyChainControlGuidance(description);
|
|
164
182
|
}
|
|
@@ -2,6 +2,7 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
4
4
|
import { getPiSpawnCommand } from "../../runs/shared/pi-spawn.ts";
|
|
5
|
+
import { getProjectSubagentsDir } from "../../shared/artifacts.ts";
|
|
5
6
|
import { writeAtomicJson } from "../../shared/atomic-json.ts";
|
|
6
7
|
import type { Details } from "../../shared/types.ts";
|
|
7
8
|
import { createHerdrClient, detectHerdr, type HerdrClient, type HerdrErrorCode } from "./client.ts";
|
|
@@ -153,7 +154,7 @@ function formatProjectPaneError(input: { code: ProjectPaneErrorCode; message: st
|
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
function projectPaneDir(projectRoot: string): string {
|
|
156
|
-
return path.join(projectRoot, "
|
|
157
|
+
return path.join(getProjectSubagentsDir(projectRoot), "project-panes");
|
|
157
158
|
}
|
|
158
159
|
|
|
159
160
|
export function projectPaneBindingPath(projectRoot: string): string {
|
package/src/missions/store.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as os from "node:os";
|
|
4
4
|
import * as path from "node:path";
|
|
5
|
+
import { getProjectSubagentsDir } from "../shared/artifacts.ts";
|
|
5
6
|
import { writePrivateAtomicJson } from "../shared/atomic-json.ts";
|
|
6
7
|
import { getAgentDir } from "../shared/utils.ts";
|
|
7
8
|
import {
|
|
@@ -290,7 +291,7 @@ export function resolveMissionStoreLocation(input: {
|
|
|
290
291
|
const projectRoot = path.resolve(input.projectRoot);
|
|
291
292
|
const missionDir = input.config?.directory
|
|
292
293
|
? expandConfiguredPath(input.config.directory, projectRoot)
|
|
293
|
-
: path.join(projectRoot, "
|
|
294
|
+
: path.join(getProjectSubagentsDir(projectRoot), "missions");
|
|
294
295
|
const globalIndexDir = input.config?.globalIndexDir
|
|
295
296
|
? expandConfiguredPath(input.config.globalIndexDir, projectRoot)
|
|
296
297
|
: path.join(input.agentDir ?? getAgentDir(), "missions", "index");
|