@cr1ms0n/pi-subagent 0.8.8 → 0.9.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 +14 -6
- package/README.md +218 -128
- package/docs/ARCHITECTURE.md +168 -132
- package/docs/COST-ACCOUNTING.md +116 -66
- package/docs/RELEASING.md +32 -32
- package/docs/SECURITY.md +125 -97
- package/docs/UX.md +158 -141
- package/package.json +2 -2
- package/skills/subagent/SKILL.md +142 -121
- package/src/agents.ts +282 -288
- package/src/backends/pi.ts +164 -94
- package/src/child-preflight.ts +166 -0
- package/src/config.ts +254 -252
- package/src/dispatch-preflight.ts +87 -0
- package/src/dispatch-routing.ts +56 -0
- package/src/extension.ts +368 -187
- package/src/format.ts +436 -365
- package/src/jev-router.ts +1036 -0
- package/src/orchestrator.ts +303 -312
- package/src/persistence.ts +643 -335
- package/src/policy.ts +562 -561
- package/src/process-lock.ts +730 -687
- package/src/protocol.ts +320 -290
- package/src/registry.ts +730 -632
- package/src/routing-policy.ts +268 -0
- package/src/routing-types.ts +217 -0
- package/src/runner.ts +1299 -850
- package/src/schema.ts +189 -189
- package/src/startup-check.ts +481 -0
- package/src/types.ts +208 -198
- package/src/usage.ts +316 -274
- package/src/context-policy.ts +0 -169
- package/src/model-policy.ts +0 -169
package/CHANGELOG.md
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## Unreleased
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.9.0 — 2026-09-21
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
### Jev model and tool routing
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
- Replace fixed `modelPolicy` routes for extension-managed work with required Jev selection from user-described, locally available model candidates and individual permitted tools. New work is Pi-only; management and explicit low-level SDK specs remain available.
|
|
10
|
+
- Route normal/parallel/background dispatch, paid plan, `/btw`, resume/fork and deferred synthesis through the shared selector. Reject legacy explicit model/fallback fields; accept valid low-confidence decisions without substituting an emergency model.
|
|
11
|
+
- Verify the child model and exact selected tool set before sending task text. Carry cancellation/deadlines through routing, preflight, startup and same-model retries.
|
|
12
|
+
- Persist bounded per-request selector receipts separately from execution usage, with deduplicated native delivery and unreported selector currency. Preserve resume-lock safety, fanout usage and retained worktree pointers across failures.
|
|
13
|
+
- Document manual configuration migration, minimum disclosure and rollback. No automatic settings rewrite, installation or live provider evaluation is included.
|
|
8
14
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
## 0.8.9 — 2026-09-16
|
|
16
|
+
|
|
17
|
+
### Restore the stable 0.8.7 behavior
|
|
18
|
+
|
|
19
|
+
- Restore the `0.8.7` source behavior after the withdrawn `0.8.8` release. Remote Context ownership and model gating remain in the separate `pi-openai-toolkit` project.
|
|
12
20
|
|
|
13
21
|
## 0.8.7 — 2026-09-14
|
|
14
22
|
|
package/README.md
CHANGED
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
This is an independent community fork of **@parke.dev/pi-subagent 0.8.0**, originally by Luke Parke. It is not an official upstream release. The original MIT license and copyright are preserved. Upstream source: [LukasParke/pi-extensions](https://github.com/LukasParke/pi-extensions/tree/main/packages/pi-subagent).
|
|
4
4
|
|
|
5
|
-
This fork
|
|
5
|
+
This fork replaces the upstream fixed model route with mandatory Jev routing. New subagent dispatches require a `jevRouting` block in `~/.pi/subagent.json` (a dedicated candidate-model list plus the name of the environment variable holding your TypeSafe credential), and the TUI shows the selected execution model and tools.
|
|
6
6
|
|
|
7
7
|
Production-grade isolated subagents for [Pi](https://github.com/badlogic/pi-mono).
|
|
8
8
|
|
|
9
9
|
Delegate research, parallel exploration, and clean-context review to child Pi
|
|
10
|
-
processes. Named agent personas,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
context forking, worktree isolation with a
|
|
14
|
-
profiles, a root/subagent/combined cost
|
|
10
|
+
processes. Named agent personas, Jev-selected execution models and individual
|
|
11
|
+
tools, cancellable background runs with completion notifications and a live
|
|
12
|
+
widget, mid-run steering, graceful budget wrap-ups, bounded same-model retry, a
|
|
13
|
+
stall watchdog, session resume and context forking, worktree isolation with a
|
|
14
|
+
diff/apply/discard loop, capability profiles, a root/subagent/combined cost
|
|
15
|
+
ledger, and a TUI inspector.
|
|
15
16
|
|
|
16
17
|
## Install
|
|
17
18
|
|
|
@@ -45,17 +46,18 @@ This fork is published manually after pack-content and offline checks — see
|
|
|
45
46
|
## Quick usage
|
|
46
47
|
|
|
47
48
|
```ts
|
|
48
|
-
// Single foreground task
|
|
49
|
-
|
|
49
|
+
// Single foreground task (default profile: general). Omit model and
|
|
50
|
+
// fallback_models: Jev selects the execution model from your configured list.
|
|
51
|
+
{ task: "Find all call sites of parseConfig and summarize patterns.", description: "Map parseConfig usage" }
|
|
50
52
|
|
|
51
|
-
// Named agent
|
|
52
|
-
{ task: "Review this diff for security issues", agent: "reviewer"
|
|
53
|
+
// Named agent: persona prompt from .pi/agents/reviewer.md. Jev still picks the model.
|
|
54
|
+
{ task: "Review this diff for security issues", agent: "reviewer" }
|
|
53
55
|
|
|
54
|
-
// Parallel read-only explorers (default profile: explore)
|
|
56
|
+
// Parallel read-only explorers (default profile for tasks[]: explore).
|
|
55
57
|
{
|
|
56
58
|
tasks: [
|
|
57
|
-
{ task: "Map auth middleware flow", description: "Auth flow map"
|
|
58
|
-
{ task: "List all env vars used in server/", description: "Env var inventory"
|
|
59
|
+
{ task: "Map auth middleware flow", description: "Auth flow map" },
|
|
60
|
+
{ task: "List all env vars used in server/", description: "Env var inventory" }
|
|
59
61
|
]
|
|
60
62
|
}
|
|
61
63
|
|
|
@@ -63,15 +65,14 @@ This fork is published manually after pack-content and offline checks — see
|
|
|
63
65
|
// outputs into a single brief, delivered first.
|
|
64
66
|
{
|
|
65
67
|
tasks: [
|
|
66
|
-
{ task: "Audit backend error handling", description: "Backend audit"
|
|
67
|
-
{ task: "Audit frontend error handling", description: "Frontend audit"
|
|
68
|
+
{ task: "Audit backend error handling", description: "Backend audit" },
|
|
69
|
+
{ task: "Audit frontend error handling", description: "Frontend audit" }
|
|
68
70
|
],
|
|
69
71
|
synthesis: "Merge both audits into one prioritized findings list"
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
// Background run
|
|
73
|
-
|
|
74
|
-
{ task: "Audit dependency licenses", model: "<provider/model-id>", async: true }
|
|
74
|
+
// Background run: the widget shows the selected model; expanded results include tools.
|
|
75
|
+
{ task: "Audit dependency licenses", async: true }
|
|
75
76
|
// later
|
|
76
77
|
{ action: "status", id: "abc123" }
|
|
77
78
|
{ action: "wait", id: "abc123" } // interruptible; does not cancel
|
|
@@ -84,17 +85,17 @@ This fork is published manually after pack-content and offline checks — see
|
|
|
84
85
|
subagent_wait { id: "abc123" }
|
|
85
86
|
subagent_wait { id: "abc123", timeout_ms: 30000 }
|
|
86
87
|
|
|
87
|
-
// Dry-run a spawn request: full validation
|
|
88
|
-
// session, output paths)
|
|
89
|
-
// budgets, isolation) without spawning
|
|
90
|
-
|
|
88
|
+
// Dry-run a spawn request: full validation, Jev selection and local preflights
|
|
89
|
+
// (git repo, fork session, output paths). Returns the resolved per-task plan
|
|
90
|
+
// (model, tools, budgets, isolation) and its selector usage without spawning.
|
|
91
|
+
// Selector fees apply, and a later dispatch selects again.
|
|
92
|
+
{ action: "plan", tasks: [{ task: "Implement feature A", profile: "general", isolation: "worktree" }] }
|
|
91
93
|
|
|
92
94
|
// Structured output: the child must end with a fenced json:result block
|
|
93
95
|
// matching the schema. Invalid output gets one automatic repair round;
|
|
94
96
|
// delivery is the clean JSON and details carry the parsed object.
|
|
95
97
|
{
|
|
96
98
|
task: "Audit the auth module",
|
|
97
|
-
model: "<exact model from current modelPolicy route>",
|
|
98
99
|
output_schema: {
|
|
99
100
|
type: "object",
|
|
100
101
|
required: ["findings", "risk"],
|
|
@@ -107,15 +108,15 @@ subagent_wait { id: "abc123", timeout_ms: 30000 }
|
|
|
107
108
|
|
|
108
109
|
// Fork the parent conversation into the child (needs a persisted session).
|
|
109
110
|
// The child starts from a branched copy of everything discussed so far.
|
|
110
|
-
{ task: "Implement the plan we agreed on",
|
|
111
|
+
{ task: "Implement the plan we agreed on", context: "fork", profile: "general" }
|
|
111
112
|
|
|
112
113
|
// Budgets with graceful wrap-up: at the limit the child is steered to produce
|
|
113
114
|
// a final answer and given grace turns before any hard stop.
|
|
114
|
-
{ task: "Audit deps",
|
|
115
|
+
{ task: "Audit deps", max_turns: 15, grace_turns: 2 }
|
|
115
116
|
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
{ task: "Research X",
|
|
117
|
+
// Transient child failures retry the already selected model and tool set, up to
|
|
118
|
+
// max_retries. There are no emergency or fallback models.
|
|
119
|
+
{ task: "Research X", max_retries: 1 }
|
|
119
120
|
|
|
120
121
|
// Steer a running child mid-run instead of cancel + retry. The message is
|
|
121
122
|
// delivered after the current assistant turn, before the next LLM call.
|
|
@@ -123,14 +124,14 @@ subagent_wait { id: "abc123", timeout_ms: 30000 }
|
|
|
123
124
|
// Parallel runs: pass index to pick one live task.
|
|
124
125
|
{ action: "steer", id: "abc123", index: 1, message: "Wrap up now" }
|
|
125
126
|
|
|
126
|
-
// Resume a child session
|
|
127
|
-
{ task: "Continue from your findings and propose a fix plan",
|
|
127
|
+
// Resume a child session. The new invocation selects again.
|
|
128
|
+
{ task: "Continue from your findings and propose a fix plan", resume: "<session-id>" }
|
|
128
129
|
|
|
129
130
|
// Isolated writers
|
|
130
131
|
{
|
|
131
132
|
tasks: [
|
|
132
|
-
{ task: "Implement feature A",
|
|
133
|
-
{ task: "Implement feature B",
|
|
133
|
+
{ task: "Implement feature A", profile: "general", isolation: "worktree" },
|
|
134
|
+
{ task: "Implement feature B", profile: "general", isolation: "worktree" }
|
|
134
135
|
]
|
|
135
136
|
}
|
|
136
137
|
|
|
@@ -159,19 +160,27 @@ conversation or polluting the context window.
|
|
|
159
160
|
|
|
160
161
|
## Backends
|
|
161
162
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
Jev routing manages Pi-backed new dispatch only. A `backend: "codex"` or
|
|
164
|
+
`backend: "claude"` new task is refused before any selector or provider work,
|
|
165
|
+
including a backend inherited from agent frontmatter; the extension never
|
|
166
|
+
silently switches it to Pi. Existing Codex/Claude runs stay manageable:
|
|
167
|
+
`status`, `wait`, `cancel`, `steer`, `diff`, `apply` and `discard` all still
|
|
168
|
+
work. Provider diversity is not lost, because another provider's execution
|
|
169
|
+
model stays eligible through Pi once it is in your configured candidate list.
|
|
165
170
|
|
|
166
171
|
```ts
|
|
167
|
-
|
|
168
|
-
{ task: "
|
|
172
|
+
// Refused on new work. Use the Pi-backed path so Jev can route it.
|
|
173
|
+
{ task: "Summarize this module", backend: "codex", profile: "explore" }
|
|
169
174
|
```
|
|
170
175
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
176
|
+
The low-level SDK is a different contract: `runTasks`/`runSubagent` execute the
|
|
177
|
+
explicit `TaskSpec` you hand them, so embedding code can still select a backend
|
|
178
|
+
directly. Everything else (worktrees, process locks, depth limits, budgets,
|
|
179
|
+
orphan reclaim) is backend-agnostic and applies unchanged.
|
|
180
|
+
|
|
181
|
+
Capabilities differ, and **unsupported combinations are refused with an
|
|
182
|
+
explanation rather than silently ignored**: a dropped `max_cost` or unenforced
|
|
183
|
+
read-only profile would be a safety regression, not a minor degradation.
|
|
175
184
|
|
|
176
185
|
| | `pi` (default) | `codex` | `claude` |
|
|
177
186
|
| ------------------------------- | -------------- | ------------------------------------- | ---------------------- |
|
|
@@ -187,36 +196,43 @@ A budget breach on a backend without steering hard-stops instead of asking the
|
|
|
187
196
|
child to wrap up. Codex's read-only sandbox is enforced by the OS, which is
|
|
188
197
|
stronger than a tool allowlist.
|
|
189
198
|
|
|
190
|
-
|
|
199
|
+
Agent frontmatter `backend:` remains a default. New extension-managed work rejects
|
|
200
|
+
any effective backend other than Pi, including a native backend inherited from an agent.
|
|
201
|
+
Direct SDK specs retain the backend capabilities listed above.
|
|
191
202
|
|
|
192
203
|
## Profiles
|
|
193
204
|
|
|
194
205
|
| Profile | Tools | Writes |
|
|
195
206
|
| ---------------------------- | ------------------------------------------------------- | ------------------------------------------- |
|
|
196
|
-
| `explore` (parallel default) |
|
|
207
|
+
| `explore` (parallel default) | locally permitted read-only tools + Pi context tools | no project-file writes |
|
|
197
208
|
| `review` | same as explore | no project-file writes |
|
|
198
|
-
| `general` |
|
|
209
|
+
| `general` | Jev chooses from the full available locally permitted catalog + Pi context tools | yes for selected write-capable tools; unknown custom tools count as writable |
|
|
210
|
+
|
|
211
|
+
Jev chooses individual tool names, not a capability bundle. Candidates come
|
|
212
|
+
from the full available locally permitted catalog, not from the agent file's
|
|
213
|
+
`tools` defaults and not from the parent's currently active tools. An explicit
|
|
214
|
+
task `tools` list is a ceiling, and explore/review keep their read-only rule
|
|
215
|
+
regardless of what the selector returns. An empty selection never means "all
|
|
216
|
+
tools".
|
|
199
217
|
|
|
200
218
|
For the Pi backend, the context-management tools `new_context`,
|
|
201
|
-
`get_context_remaining`, `history`, and `notes` are
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
invalid, or not in Remote Context mode, the effective allowlist is empty (fail
|
|
219
|
-
closed). Non-Pi backends are unchanged.
|
|
219
|
+
`get_context_remaining`, `history`, and `notes` are added locally when the
|
|
220
|
+
parent exposes them, so they are never a selector question. They are
|
|
221
|
+
control-plane tools: they may update continuity notes or the remote context
|
|
222
|
+
window, but cannot modify the child checkout or run a shell command. This
|
|
223
|
+
exception also applies when a task supplies a narrower tool list, so Pi's
|
|
224
|
+
`contextManagement` remains usable for configured gateway models. Locally added
|
|
225
|
+
controls are reported in the route metadata.
|
|
226
|
+
|
|
227
|
+
The finalized tool set is passed to the child as Pi's `--tools` allowlist
|
|
228
|
+
(`--no-tools` for a true empty set). Pi 0.86.0 is the verified baseline for
|
|
229
|
+
built-in, extension and late-registered tool enforcement; a host that cannot
|
|
230
|
+
honor that allowlist is refused rather than silently weakened, and the extension
|
|
231
|
+
does not claim identical behavior on untested older releases. Before the real
|
|
232
|
+
task prompt is sent, the child is also asked to confirm the selected model and
|
|
233
|
+
the finalized tool names through a verified private startup command; if the host
|
|
234
|
+
cannot verify that command or the child cannot confirm both, the launch aborts
|
|
235
|
+
with a startup diagnostic instead of running with a broader tool set.
|
|
220
236
|
|
|
221
237
|
Parallel write-capable tasks sharing one checkout are rejected unless each uses
|
|
222
238
|
`isolation: "worktree"`, distinct `cwd`, or explicit `allow_shared_writes: true`.
|
|
@@ -242,6 +258,7 @@ Defaults can be overridden in `~/.pi/subagent.json` and per-field via env vars
|
|
|
242
258
|
| `sessionRetentionDays` | `PI_SUBAGENT_SESSION_RETENTION_DAYS` | unused (lifecycle GC) |
|
|
243
259
|
| `lockRetentionDays` | `PI_SUBAGENT_LOCK_RETENTION_DAYS` | 7 |
|
|
244
260
|
| `taskDefaults` | — | none |
|
|
261
|
+
| `jevRouting` | - | required for new dispatch (see below) |
|
|
245
262
|
| `graceTurns` | `PI_SUBAGENT_GRACE_TURNS` | 2 |
|
|
246
263
|
| `stallAfterMs` | `PI_SUBAGENT_STALL_AFTER_MS` | 90000 |
|
|
247
264
|
| `stallKillAfterMs` | `PI_SUBAGENT_STALL_KILL_AFTER_MS` | 90000 |
|
|
@@ -250,52 +267,93 @@ Defaults can be overridden in `~/.pi/subagent.json` and per-field via env vars
|
|
|
250
267
|
| `notifications` | `PI_SUBAGENT_NOTIFICATIONS` | `batched` (`off` disables) |
|
|
251
268
|
| (bin) | `PI_SUBAGENT_BIN` | auto (`process.execPath` + CLI entry) |
|
|
252
269
|
|
|
253
|
-
###
|
|
270
|
+
### Jev routing
|
|
254
271
|
|
|
255
|
-
New
|
|
256
|
-
|
|
257
|
-
|
|
272
|
+
New subagent dispatches are selected by Jev, TypeSafe's structured-decision API,
|
|
273
|
+
against a dedicated candidate list you maintain in `~/.pi/subagent.json`. Fixed
|
|
274
|
+
routing is gone: a `modelPolicy` block produces a migration error, and an
|
|
275
|
+
explicit `model` or `fallback_models` on new work is rejected rather than
|
|
276
|
+
bypassing selection. Management actions (`status`, `wait`, `cancel`, `steer`,
|
|
277
|
+
`diff`, `apply`, `discard`) never call the selector and need no credential.
|
|
258
278
|
|
|
259
279
|
```json
|
|
260
280
|
{
|
|
261
|
-
"
|
|
262
|
-
"
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
"agents": {
|
|
268
|
-
"<agent-name>": {
|
|
281
|
+
"jevRouting": {
|
|
282
|
+
"selectorModel": "jev-latest",
|
|
283
|
+
"apiKeyEnv": "TYPESAFE_API_KEY",
|
|
284
|
+
"timeoutMs": 15000,
|
|
285
|
+
"models": [
|
|
286
|
+
{
|
|
269
287
|
"model": "<provider/model-id>",
|
|
270
|
-
"
|
|
271
|
-
"thinking": "
|
|
288
|
+
"description": "<your characteristics notes, Chinese allowed>",
|
|
289
|
+
"thinking": "<optional opaque Pi thinking default>"
|
|
272
290
|
}
|
|
273
|
-
|
|
291
|
+
]
|
|
274
292
|
}
|
|
275
293
|
}
|
|
276
294
|
```
|
|
277
295
|
|
|
296
|
+
- `selectorModel` defaults to the stable alias `jev-latest`. Pin an exact
|
|
297
|
+
version to control which selector version is requested. This does not guarantee
|
|
298
|
+
deterministic choices; the extension records the version that actually answered.
|
|
299
|
+
- `apiKeyEnv` defaults to `TYPESAFE_API_KEY`. Only the variable *name* lives in
|
|
300
|
+
the file. Set the credential in your local environment; the extension reads it
|
|
301
|
+
at request time and never writes it to config, prompts, argv, logs or results.
|
|
302
|
+
- `timeoutMs` defaults to 15000 and must be an integer between 100 and 600000.
|
|
303
|
+
It bounds one logical task selection, including all its HTTP requests and queue
|
|
304
|
+
waits. Parallel workers each have a selection allowance, still capped by their
|
|
305
|
+
absolute task `timeout_ms` deadline. Deferred synthesis has a separate allowance.
|
|
306
|
+
- `models` holds 1 to 255 entries, each with an exact `provider/model-id` and a
|
|
307
|
+
non-blank description. Those descriptions are what Jev matches against your
|
|
308
|
+
task, so write them the way you would explain the model to a colleague.
|
|
309
|
+
`thinking` is optional.
|
|
310
|
+
|
|
311
|
+
Plan and background-start native usage attachments are limited to 1024 selector
|
|
312
|
+
HTTP receipts per invocation. Larger requests fail with a request-splitting error;
|
|
313
|
+
background work has not started at that point. Previously incurred selector tokens
|
|
314
|
+
remain in the ledger. This bounds an atomic delivery record, not the number of tools
|
|
315
|
+
Jev may consider within each task.
|
|
316
|
+
|
|
317
|
+
The candidate list is intersected with the models the local Pi registry reports
|
|
318
|
+
as available. A configured model Pi cannot resolve is not eligible, and an empty
|
|
319
|
+
eligible pool fails before any request. Adding a model anywhere else in Pi does
|
|
320
|
+
not authorize it, and legacy `modelPolicy` entries are never imported
|
|
321
|
+
automatically. An unknown `jevRouting` field is an error, not a silent default.
|
|
322
|
+
|
|
323
|
+
Jev receives only the current delegated task text, your model IDs and
|
|
324
|
+
descriptions, candidate tool names and descriptions, and the permission/output
|
|
325
|
+
requirements it needs to choose. It does not receive repository files,
|
|
326
|
+
conversation history, full system prompts, persona text or tool parameter
|
|
327
|
+
schemas. Task text and descriptions are user content and may contain sensitive
|
|
328
|
+
material, so treat what you delegate as disclosure to TypeSafe.
|
|
329
|
+
|
|
330
|
+
Every new extension-managed dispatch routes through Jev: `task`/`tasks[]`,
|
|
331
|
+
`action:"plan"`, `/btw`, resume, fork, locally permitted nested dispatch and the
|
|
332
|
+
optional `synthesis` child. `action:"plan"` calls Jev and runs the same local
|
|
333
|
+
preflights, returns the resolved model/tool plan and the selector usage, and
|
|
334
|
+
creates no child or run entry. A later dispatch selects again; there is no cached
|
|
335
|
+
decision to reuse. If optional synthesis selection fails, the worker plan and its
|
|
336
|
+
usage stay valid and synthesis is reported as blocked with its diagnostic.
|
|
337
|
+
|
|
278
338
|
`thinking` is optional and is an opaque Pi thinking-level string. Common values
|
|
279
339
|
include `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`, but the
|
|
280
340
|
package does not remap or restrict model-specific values. Pi receives the value
|
|
281
|
-
unchanged and decides whether the active model supports it.
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
`modelPolicy`; library callers must parse and validate their own policy before
|
|
298
|
-
passing `TaskSpec` values. Do not treat direct SDK calls as policy enforcement.
|
|
341
|
+
unchanged and decides whether the active model supports it. Resolution order is:
|
|
342
|
+
explicit task `thinking` > agent frontmatter `thinking` > profile
|
|
343
|
+
`taskDefaults.<profile>.thinking` > the selected candidate's optional `thinking`
|
|
344
|
+
> the parent session's thinking level. Jev never chooses a thinking level.
|
|
345
|
+
|
|
346
|
+
The extension re-reads `jevRouting` on each dispatch and injects the current
|
|
347
|
+
routing guidance into the parent prompt, so configuration edits reach the next
|
|
348
|
+
decision without a code change. Missing or invalid `jevRouting`, or a missing
|
|
349
|
+
credential environment variable, rejects new dispatch and plan with a remedy;
|
|
350
|
+
management stays available.
|
|
351
|
+
|
|
352
|
+
The mandatory routing above is the Extension dispatch contract. The stable SDK
|
|
353
|
+
exports (`runTasks` and `runSubagent`) are trusted low-level library APIs: they
|
|
354
|
+
execute the explicit `TaskSpec` you pass and perform no implicit routing, config
|
|
355
|
+
discovery or network call. Library callers own model and tool choice, and must not
|
|
356
|
+
read these SDK calls as Jev enforcement.
|
|
299
357
|
|
|
300
358
|
### Named agent files
|
|
301
359
|
|
|
@@ -314,7 +372,7 @@ supplies defaults using the same snake_case names as the tool parameters:
|
|
|
314
372
|
```md
|
|
315
373
|
---
|
|
316
374
|
description: Security-focused code reviewer
|
|
317
|
-
# Legacy model/fallback fields are ignored;
|
|
375
|
+
# Legacy model/fallback fields are ignored; Jev routing owns model/tool choice.
|
|
318
376
|
thinking: high
|
|
319
377
|
profile: review
|
|
320
378
|
max_turns: 20
|
|
@@ -343,12 +401,13 @@ file one level deep (relative to the agent file, same 64KB/symlink guards
|
|
|
343
401
|
as `@contract.json`). Missing or rejected includes leave the line verbatim;
|
|
344
402
|
includes do not recurse.
|
|
345
403
|
|
|
346
|
-
Invoke with `{ task: "…", agent: "reviewer"
|
|
347
|
-
|
|
348
|
-
`model`/`fallback_models`
|
|
349
|
-
appends after the persona body.
|
|
350
|
-
Profiles still enforce capability:
|
|
351
|
-
|
|
404
|
+
Invoke with `{ task: "…", agent: "reviewer" }`. The agent file supplies
|
|
405
|
+
persona, capability, thinking and budget defaults only: model and tool selection
|
|
406
|
+
stay with Jev routing, and a legacy `model`/`fallback_models` in frontmatter is
|
|
407
|
+
ignored. An explicit `system_prompt` appends after the persona body.
|
|
408
|
+
Profiles still enforce capability: `profile: review` filters candidates to read-only
|
|
409
|
+
tools. Legacy agent `tools` defaults are ignored; an explicit task `tools` list
|
|
410
|
+
requesting write tools under review fails closed. The agent catalog is advertised in the tool's
|
|
352
411
|
system-prompt guidelines (session start) and in bare `status` output (live),
|
|
353
412
|
and file changes are picked up within seconds — no restart needed.
|
|
354
413
|
|
|
@@ -356,15 +415,17 @@ and file changes are picked up within seconds — no restart needed.
|
|
|
356
415
|
|
|
357
416
|
`taskDefaults` in `~/.pi/subagent.json` remains available for non-model
|
|
358
417
|
fields such as thinking, budgets, and retry counts. Its legacy `model` and
|
|
359
|
-
`fallbackModels` fields are ignored; model routing
|
|
360
|
-
`
|
|
361
|
-
Invalid fields are dropped field-by-field.
|
|
418
|
+
`fallbackModels` fields are ignored; model and tool routing belong only to
|
|
419
|
+
`jevRouting`. A profile `thinking` value overrides the selected candidate's
|
|
420
|
+
optional `thinking` default. Invalid fields are dropped field-by-field.
|
|
362
421
|
|
|
363
422
|
Notes on behavior:
|
|
364
423
|
|
|
365
|
-
- `timeout_ms`
|
|
366
|
-
|
|
367
|
-
|
|
424
|
+
- `timeout_ms` is the absolute task deadline: local preflight, Jev selection,
|
|
425
|
+
setup, queue time and runtime all count against it, and selection cannot reset
|
|
426
|
+
it. Timed-out tasks report `state: "timeout"` with
|
|
427
|
+
`timeoutPhase: "queued"|"starting"|"running"` so agents can retry capacity
|
|
428
|
+
issues without confusing them for task failures.
|
|
368
429
|
- Budget stops (`max_turns`, `max_cost`) trigger a **graceful wrap-up**: the
|
|
369
430
|
child is steered to produce its final answer NOW and allowed `graceTurns`
|
|
370
431
|
more turns before SIGTERM. Results end as `partial` with `wrappedUp: true`
|
|
@@ -374,11 +435,11 @@ Notes on behavior:
|
|
|
374
435
|
then kills after `stallKillAfterMs` more silence — feeding automatic retry
|
|
375
436
|
instead of burning the whole timeout.
|
|
376
437
|
- **Transient failures retry automatically** (queue timeouts, stalls, spawn
|
|
377
|
-
errors, provider errors) up to `maxRetries` extra attempts
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
(nonzero exit with complete
|
|
381
|
-
timeouts) never retry.
|
|
438
|
+
errors, provider errors) up to `maxRetries` extra attempts on the already
|
|
439
|
+
selected model and tool set. There are no emergency or fallback models, and a
|
|
440
|
+
quality or budget failure never reselects. Usage accumulates across attempts;
|
|
441
|
+
results record `attempts`. Task-quality failures (nonzero exit with complete
|
|
442
|
+
protocol, cancellations, budget stops, running timeouts) never retry.
|
|
382
443
|
- `context: "fork"` starts a single child from a real branched copy of the
|
|
383
444
|
parent conversation (`--fork` on the parent's session file). It requires a
|
|
384
445
|
persisted parent session, cannot combine with `resume`, and is rejected for
|
|
@@ -456,22 +517,23 @@ import {
|
|
|
456
517
|
type TaskResult,
|
|
457
518
|
type RunState,
|
|
458
519
|
type UsageStats,
|
|
459
|
-
} from "@
|
|
520
|
+
} from "@cr1ms0n/pi-subagent/sdk";
|
|
460
521
|
```
|
|
461
522
|
|
|
462
523
|
The package root is an alias for the same SDK:
|
|
463
524
|
`import { runTasks } from "@cr1ms0n/pi-subagent"`.
|
|
464
525
|
|
|
465
|
-
The Extension dispatch path
|
|
466
|
-
|
|
467
|
-
|
|
526
|
+
The Extension dispatch path routes every new task through Jev, so callers never
|
|
527
|
+
pass a model to it. The low-level SDK is the opposite contract: it is
|
|
528
|
+
explicit-spec and performs no implicit routing, config discovery or network
|
|
529
|
+
call, so embedding code supplies the model (and tool list) it resolved itself.
|
|
530
|
+
This placeholder is illustrative only and configures nothing:
|
|
468
531
|
|
|
469
532
|
```ts
|
|
470
|
-
const routeModel = "<exact model from current modelPolicy route>";
|
|
471
533
|
const task: TaskSpec = {
|
|
472
534
|
task: "Audit src/ for unsafe parsing",
|
|
473
535
|
profile: "explore",
|
|
474
|
-
model:
|
|
536
|
+
model: "<provider/model-id resolved by your embedding code>",
|
|
475
537
|
timeoutMs: 10 * 60_000,
|
|
476
538
|
};
|
|
477
539
|
```
|
|
@@ -501,25 +563,34 @@ The Pi extension entry is unchanged: package `pi.extensions` still points at
|
|
|
501
563
|
7. On parent session shutdown, live children are aborted and awaited briefly.
|
|
502
564
|
8. On parent (re)start, orphan process groups recorded under `lockDir` are reaped
|
|
503
565
|
before any resume is allowed for the matching child session.
|
|
504
|
-
9. Provider-reported usage is counted once per root message and terminal child run
|
|
566
|
+
9. Provider-reported usage is counted once per root message and terminal child run;
|
|
567
|
+
selector usage is a separate category counted once per selector request ID.
|
|
505
568
|
10. Protocol completion prefers `agent_settled` (falls back to non-retrying `agent_end`).
|
|
569
|
+
11. Jev selection precedes every new extension-managed launch, and the selected
|
|
570
|
+
tool subset is enforced by Pi's CLI allowlist; empty selection never means all tools.
|
|
506
571
|
|
|
507
572
|
## Layout
|
|
508
573
|
|
|
509
574
|
```
|
|
510
575
|
src/
|
|
511
|
-
index.ts # stable public SDK entry (@
|
|
576
|
+
index.ts # stable public SDK entry (@cr1ms0n/pi-subagent)
|
|
512
577
|
extension.ts # Pi wiring only
|
|
513
578
|
schema.ts # request schemas (subagent + subagent_wait)
|
|
514
579
|
btw.ts # /btw side questions (model-hidden entries)
|
|
515
580
|
backend.ts # backend adapter seam + capability gate
|
|
516
581
|
backends/ # pi | codex | claude adapters (invocation + parser)
|
|
517
582
|
policy.ts # profiles, normalization, write guards, agent resolution
|
|
583
|
+
routing-types.ts # selector DTOs, decisions, receipts, local resource limits
|
|
584
|
+
routing-policy.ts# strict jevRouting parser, candidate list, routing guidance
|
|
585
|
+
jev-router.ts # injectable Jev transport, response validation, receipts
|
|
586
|
+
dispatch-preflight.ts # bounded read-only checks before selector work
|
|
587
|
+
startup-check.ts / child-preflight.ts # private child model/tool verification
|
|
588
|
+
dispatch-routing.ts # prepare -> select -> finalize for one or many tasks
|
|
518
589
|
agents.ts # named agent files (.pi/agents/, .agents/agents/, global)
|
|
519
590
|
launch.ts # resolve child pi via execPath / PI_SUBAGENT_BIN
|
|
520
591
|
process-lock.ts # durable session locks, global slots, orphan records
|
|
521
592
|
worktree.ts # git worktree isolation + diff/apply/discard
|
|
522
|
-
orchestrator.ts # multi-task execution,
|
|
593
|
+
orchestrator.ts # multi-task execution, worktree prep, bounded same-model retry
|
|
523
594
|
runner.ts # child process lifecycle, RPC channel, steering,
|
|
524
595
|
# graceful budget wrap-up, stall watchdog
|
|
525
596
|
protocol.ts # Pi RPC/JSON event parser (agent_settled-aware)
|
|
@@ -534,14 +605,24 @@ src/
|
|
|
534
605
|
|
|
535
606
|
## Develop
|
|
536
607
|
|
|
608
|
+
This checkout has no `scripts`, no `devDependencies`, no `tsconfig.json` and no
|
|
609
|
+
test runner, so `npm install`, `npm run typecheck`, `npm test` and
|
|
610
|
+
`npm run pack:check` are not defined here. The checks that do run locally are
|
|
611
|
+
offline:
|
|
612
|
+
|
|
537
613
|
```bash
|
|
538
|
-
npm
|
|
539
|
-
npm run typecheck
|
|
540
|
-
npm test
|
|
541
|
-
npm run pack:check
|
|
614
|
+
npm pack --dry-run --json
|
|
542
615
|
```
|
|
543
616
|
|
|
544
|
-
|
|
617
|
+
That verifies the publishable file list matches `package.json` `files` and leaks
|
|
618
|
+
no backup, transcript, session data, research output or generated bundle. Source
|
|
619
|
+
changes are parse/type-strip checked by running the global Pi install's bundled
|
|
620
|
+
esbuild over every `src/` and `extensions/` file; that catches malformed
|
|
621
|
+
TypeScript only, not type errors or peer API mismatches. Provider-free fixtures
|
|
622
|
+
and an injected fake selector transport, run with the already installed Pi runtime
|
|
623
|
+
rather than a repository test framework, exercise routing behavior without any
|
|
624
|
+
model call. See [.trellis/spec/backend/quality-guidelines.md](.trellis/spec/backend/quality-guidelines.md)
|
|
625
|
+
for the exact commands and what each check does and does not prove.
|
|
545
626
|
|
|
546
627
|
## Cost accounting
|
|
547
628
|
|
|
@@ -557,6 +638,15 @@ The extension footer stays terse (running/ready counts only). Delivery and
|
|
|
557
638
|
replay do not double count runs. See
|
|
558
639
|
[docs/COST-ACCOUNTING.md](./docs/COST-ACCOUNTING.md).
|
|
559
640
|
|
|
641
|
+
Jev selection is billed separately from execution. TypeSafe reports tokens, not
|
|
642
|
+
currency, so the ledger shows routing tokens as their own category, counts each
|
|
643
|
+
selector request once by its request ID (including plan and pre-spawn failures),
|
|
644
|
+
and marks routing cost as **unreported** rather than free. Numeric dollar totals
|
|
645
|
+
exclude unreported routing spend, and `max_cost` caps provider-reported execution
|
|
646
|
+
cost only; it does not cap TypeSafe charges. Route metadata (selected model,
|
|
647
|
+
selected tools, locally added controls, selector version, confidence, outcome,
|
|
648
|
+
latency) travels with the run alongside usage.
|
|
649
|
+
|
|
560
650
|
## Roadmap
|
|
561
651
|
|
|
562
652
|
Planned work — agent spawn policies, dry-run validation, engine hardening,
|