@cr1ms0n/pi-subagent 0.8.9 → 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.
@@ -12,9 +12,10 @@
12
12
  after a second window so retry can take over. Group kills verify process start-time
13
13
  identity (Linux `/proc`, macOS/BSD `ps lstart`) before signalling a possibly-recycled
14
14
  PID; transcript joins happen only on message boundaries, not per-chunk ticks.
15
- - Retry with model fallback lives in `orchestrator.ts` (`isTransientFailure`): queue
16
- timeouts, stalls, spawn errors, and provider errors re-run the same spec on the next
17
- fallback model with accumulated usage; task-quality failures never retry.
15
+ - Retry lives in `orchestrator.ts` (`isTransientFailure`): queue timeouts, stalls, spawn
16
+ errors, and provider errors re-run the same already-selected spec (model and tool set)
17
+ with accumulated usage; there is no fallback-model escalation, and task-quality failures
18
+ never retry.
18
19
  - `context: "fork"` spawns the child with `--fork <parent session file>` so it starts
19
20
  from a real branched copy of the parent conversation. Fail-fast when the parent
20
21
  session is not persisted; single-task only.
@@ -26,10 +27,20 @@
26
27
  identity records for orphan reconcile.
27
28
  - `launch.ts`: resolve the child `pi` invocation via `PI_SUBAGENT_BIN` or
28
29
  `process.execPath` + CLI entry (bare PATH name only as last-resort fallback).
29
- - `persistence.ts`: versioned active-branch event folding and bounded child transcript metadata.
30
+ - `persistence.ts`: versioned active-branch event folding, the bounded routing-event decoder
31
+ (`subagent-routing-v1`), and bounded child transcript metadata.
30
32
  - `maintenance.ts`: filesystem GC (session files) and abort-race helpers; kept out of persistence.
31
- - `usage.ts`: provider-reported root/subagent/combined accounting.
33
+ - `usage.ts`: provider-reported root/subagent/combined accounting, plus a separate
34
+ once-per-request routing-token category whose currency is reported as unreported.
32
35
  - `policy.ts` / `schema.ts`: discriminated request validation and safe capability profiles. `schema.ts` retains the canonical TypeBox validators and derives provider-safe tool-schema projections; `extension.ts` registers those projections while validating calls with the originals. Pi context-management control-plane tools remain available to child allowlists without granting project-file write access.
36
+ - `routing-types.ts` / `routing-policy.ts` / `jev-router.ts` / `dispatch-routing.ts`:
37
+ the mandatory Jev route. `routing-types.ts` owns the selector DTOs, decision/receipt
38
+ shapes and local resource limits; `routing-policy.ts` owns the strict `jevRouting`
39
+ parser, the candidate intersection with locally available models, and the injected
40
+ model-facing guidance; `jev-router.ts` owns the injectable TypeSafe transport,
41
+ response validation, deadlines and per-request receipts; `dispatch-routing.ts`
42
+ resolves every worker before any launch and refuses a partially selected fanout. The
43
+ router has no engine imports and makes no parent UI calls.
33
44
  - `config.ts`: defaults ← `~/.pi/subagent.json` ← `PI_SUBAGENT_*` env overrides.
34
45
  - `structured.ts`: structured-output contract (dependency-free JSON-Schema subset
35
46
  validation, fenced json:result extraction, contract/repair prompts) and
@@ -65,7 +76,9 @@ Invariants:
65
76
  9. On startup, orphan process groups recorded under `lockDir` are reaped (SIGTERM then SIGKILL)
66
77
  before any matching child session is eligible for resume. `$state: "lost"` is a labeling
67
78
  that keeps `resumeBlocked` until reconciliation proves death.
68
- 10. Billed usage is folded once per root message and once per full child run UUID.
79
+ 10. Billed execution usage is folded once per root message and once per full child run UUID.
80
+ Selector usage is a separate category folded once per selector request ID, with currency
81
+ reported as unreported rather than inferred.
69
82
  11. Checkpoint persistence events are lightweight (state, usage, process identity, pointers).
70
83
  Full transcripts and final output are persisted exactly once, in the terminal event.
71
84
  12. High-frequency registry "changed" events coalesce (trailing window); state transitions,
@@ -96,10 +109,10 @@ Invariants:
96
109
  turns before SIGTERM; a child that concludes within grace ends `partial` with
97
110
  `wrappedUp: true`. `graceTurns: 0` restores immediate stops.
98
111
  21. Transient failures (queued timeout, stall, spawn error, provider error, protocol
99
- truncation) retry up to `maxRetries` extra attempts, escalating through
100
- `fallback_models`; usage accumulates across attempts and `attemptedModels` is
101
- recorded. Task-quality failures (nonzero exit with complete protocol, cancellation,
102
- budget stop, running timeout) never retry.
112
+ truncation) retry up to `maxRetries` extra attempts on the already selected model and
113
+ tool set; there is no fallback-model escalation and no reselection. Usage accumulates
114
+ across attempts. Task-quality failures (nonzero exit with complete protocol,
115
+ cancellation, budget stop, running timeout) never retry.
103
116
  22. The stall watchdog treats protocol silence as suspect, not fatal: after
104
117
  `stallAfterMs` the task is flagged and probed via `get_state` (a live child's
105
118
  answer clears the flag); only continued silence for `stallKillAfterMs` more kills
@@ -120,6 +133,36 @@ Invariants:
120
133
  `activeAtOrBelowDepth(depth) < maxGlobalActive - reservedFor(depth)`, holding slots
121
134
  back for deeper tiers so a full-width spawn tree cannot deadlock on its own children.
122
135
  Slot records without a `depth` field count as depth 0.
123
- 28. `action: "plan"` is a truth oracle: it runs the exact validation and preflights of a
124
- real spawn and returns the resolved plan without spawning never a softer check, and
125
- never a registry entry.
136
+ 28. `action: "plan"` is a truth oracle: it runs the exact validation, Jev selection and
137
+ local preflights of a real spawn and returns the resolved plan and its selector usage
138
+ without spawning. It creates no registry entry, and its fee-bearing selection is not
139
+ cached for a later dispatch.
140
+ 29. Every new extension-managed launch (`task`/`tasks[]`, `action:"plan"`, `/btw`,
141
+ resume, fork, nested dispatch and the optional synthesis child) crosses one selector
142
+ interface before any child starts. The dedicated candidate list intersected with
143
+ locally available models is the only source of execution models; the full locally
144
+ permitted tool catalog is the only candidate source. Legacy `model`/`fallback_models`
145
+ fields are rejected on new work, and an empty selected tool set never becomes
146
+ inheritance or "all tools".
147
+ 30. The finalized tool subset is passed to the child as Pi's `--tools` allowlist
148
+ (`--no-tools` when empty). Pi 0.86.0 is the verified baseline for built-in, extension
149
+ and late-registered tool enforcement; a host that cannot honor the allowlist is
150
+ refused rather than silently weakened, and no older release is advertised as
151
+ equivalent.
152
+ Startup verification is the enforcement companion: the Pi adapter supplies a
153
+ package-local preflight extension plus a bounded non-secret expectation, verifies that
154
+ the nonce-specific bootstrap command exists from the expected package source, then
155
+ requires the child to acknowledge the exact selected model and finalized tool names
156
+ (including nested-tool source) before the real task prompt is sent. Missing or
157
+ mismatched acknowledgement is a capability/startup diagnostic, never compensated by
158
+ broadening tools or choosing another model.
159
+ 31. An absolute task deadline is created before preflight/selection, and routing, setup,
160
+ queue and retries all count against it. Pending selector work is tracked per session
161
+ runtime, aborted on cancellation, shutdown or session switch, and every post-await
162
+ transition re-checks captured runtime/session ownership so a late response cannot
163
+ launch into a replaced session.
164
+ 32. Before any paid selection, plan and dispatch share a side-effect-free direct-resume
165
+ availability check (in-memory owner, `resumeBlocked`, durable lock ownership and
166
+ staleness) that acquires, renews or reaps nothing. Dispatch still takes the
167
+ authoritative lock atomically at the existing launch point, and forked resumes skip
168
+ the exclusive direct-resume check.
@@ -1,66 +1,116 @@
1
- # Cost accounting
2
-
3
- `pi-subagent` reports three independent ledgers:
4
-
5
- - **root** — provider-reported usage from assistant messages on the active parent-session branch.
6
- - **subagents** — provider-reported cumulative usage from child-run checkpoints and terminal events on that same branch.
7
- - **combined** root + subagents.
8
-
9
- These totals appear in `subagent { action: "status" }`, per-run status, the `/subagent-cost` command, and the `/subagents` overlay header. The footer stays terse (running/ready counts only) because Pi's native footer already shows session cost — including subagent spend, on Pi builds with native tool-result usage accounting (see below).
10
-
11
- ## Source of truth
12
-
13
- The extension does not estimate prices. It uses Pi's normalized provider response:
14
-
15
- ```ts
16
- message.usage.cost.total
17
- ```
18
-
19
- It also retains provider-reported input/output/cache category costs, token counts, reasoning tokens, context size, and completed turn count when supplied.
20
-
21
- ## Native Pi usage accounting
22
-
23
- Pi builds after v0.80.10 persist an optional `usage` field on tool-result messages ([pi#6671](https://github.com/earendil-works/pi/pull/6671)) and fold it into the native footer total, `/session` statistics (as `Tools/summaries`), and RPC `get_state` totals.
24
-
25
- The extension participates in both directions:
26
-
27
- - **Upward** — the tool result that *delivers* a run (foreground completion, or the first `wait`) carries the run's total provider usage as native `usage`. Attachment is gated on the same delivered-flag transition as output delivery, so it happens exactly once per run UUID. Status, replayed waits, steer, diff/apply/discard, and plan responses never attach usage. Older Pi hosts copy only `content`/`details` from tool results and silently ignore the field — safe on every version this package supports.
28
- - **Downward** a child's event stream may contain tool-result messages that themselves carry nested usage (for example, a grandchild subagent on a new-Pi child). The parent folds that into the run's cumulative usage, so `max_cost` budgets and both ledgers see true subtree spend. Pre-#6671 children simply never emit the field.
29
-
30
- Known undercounts in the **native** total (the extension ledger still counts these from persisted entries):
31
-
32
- - A background run dismissed in the overlay (or via status) without a delivering `wait` never produces a tool result, so its spend reaches only the extension ledger.
33
- - A failed or lost run raises an error instead of returning a tool result; any pre-failure usage likewise reaches only the extension ledger.
34
-
35
- Because the native footer counts parent assistant messages plus delivered tool-result usage, and the extension's **combined** counts the same runs by UUID, the two agree whenever every terminal run was delivered.
36
-
37
- ## Deduplication rules
38
-
39
- 1. Root assistant messages are counted once by session-entry ID.
40
- 2. Each subagent run is counted once by full run UUID; the newest live/checkpoint/terminal cumulative value replaces older values.
41
- 3. Delivery, dismissal, status, and checkpoint events never add cost.
42
- 4. If an old run is evicted from in-memory UI history, its latest persisted usage still contributes to the session ledger.
43
- 5. Active and immediately completed runs supplement or replace stale persisted checkpoints until newer session entries become visible; the full run UUID prevents double counting afterward.
44
- 6. Resumed and forked invocations are distinct billed runs. Their new provider usage is counted once, even though they reuse prior context.
45
- 7. Retry attempts (transient-failure retries and model fallbacks) accumulate into their
46
- run's single usage record every attempt's billed usage counts once, under one run
47
- UUID, with `attemptedModels` recording the escalation path.
48
- 8. The optional parallel `synthesis` child bills into the same run as an extra result.
49
- 9. Native `usage` on the delivering tool result mirrors rule 2's run totals and is attached at most once per run (delivered-flag gated), so Pi-side totals cannot double count a run either.
50
-
51
- ## Branch semantics
52
-
53
- Only `sessionManager.getBranch()` is used. Costs from abandoned sibling branches are excluded. When a parent session is forked, its inherited active-branch terminal entries remain part of that fork's historical total; new runs are added to the fork independently.
54
-
55
- ## Failure and cancellation
56
-
57
- Any usage reported before a failure, timeout, budget stop, cancellation, or parent crash is retained in a cumulative checkpoint/terminal record. A run with no provider response contributes zero rather than an estimate.
58
-
59
- ## Provider limitations
60
-
61
- Accounting is only as precise as the provider data normalized by Pi:
62
-
63
- - Some providers may report zero or incomplete costs.
64
- - `reasoning` is a subset of output tokens and is not added to output again.
65
- - `contextTokens` is the latest turn's context size, not an additive billed-token field.
66
- - The extension deliberately does not infer missing prices from a local model table.
1
+ # Cost accounting
2
+
3
+ `pi-subagent` reports four independent ledgers:
4
+
5
+ - **root** — provider-reported usage from assistant messages on the active parent-session branch.
6
+ - **subagents** — provider-reported cumulative usage from child-run checkpoints and terminal events on that same branch.
7
+ - **routing**: usage reported by the Jev/TypeSafe selector, one record per selector HTTP request.
8
+ - **combined**: root + subagents + routing.
9
+
10
+ These totals appear in `subagent { action: "status" }`, per-run status, the
11
+ `/subagent-cost` command, and the `/subagents` overlay header. The footer stays
12
+ terse (running/ready counts only) because Pi's native footer already shows
13
+ session cost, including subagent spend, on Pi builds with native tool-result
14
+ usage accounting (see below).
15
+
16
+ ## Source of truth
17
+
18
+ The extension does not estimate prices. It uses Pi's normalized provider response:
19
+
20
+ ```ts
21
+ message.usage.cost.total
22
+ ```
23
+
24
+ It also retains provider-reported input/output/cache category costs, token counts, reasoning tokens, context size, and completed turn count when supplied.
25
+
26
+ ## Routing (selector) accounting
27
+
28
+ Jev selection is billed separately from child execution:
29
+
30
+ - TypeSafe reports input and output **tokens**, not billed currency. The routing
31
+ ledger therefore reports tokens and marks its currency as **unreported**. Do not
32
+ read the numeric `0` currency placeholder in the native usage schema as the
33
+ selector being free: it is the API-required number for an unreported value.
34
+ - Numeric dollar totals for root/subagent/combined exclude unreported routing
35
+ spend. The status line says so explicitly rather than silently omitting it.
36
+ - `max_cost` remains the provider-reported execution ceiling for the child and
37
+ its subtree. It cannot cap TypeSafe charges, so a run can stay under `max_cost`
38
+ while still incurring selector fees.
39
+ - No selector price is inferred from a public price page or a local model table.
40
+
41
+ A routing record exists per selector HTTP request, not per logical selection: a
42
+ model question plus one or more packed tool-question requests each produce their
43
+ own record, and all of them count once by full request ID. Plan selections and
44
+ pre-spawn failures are included, because no child run exists to carry them.
45
+
46
+ Successful route metadata (decision ID, selector model and reported version(s),
47
+ selected execution model/tools, locally added control-plane tools, confidence,
48
+ success outcome, latency and receipt IDs) travels with the run and both registry
49
+ projections. Per-request failure outcomes and safe error codes stay in selector receipts. It carries no descriptions, raw request bodies, headers,
50
+ credentials or invented rationale.
51
+
52
+ Receipts pending append visibility remain in a bounded session-local overlay until the
53
+ active branch exposes the matching record. Persistence retries reuse the receipt ID,
54
+ not the paid selector request. If persistence cannot be confirmed, new child launch is
55
+ blocked and a branch change is cancelled; a forced shutdown reports that durable usage
56
+ may be incomplete. This cannot repair an unavailable storage adapter after process exit.
57
+
58
+ ## Native Pi usage accounting
59
+
60
+ Pi builds after v0.80.10 persist an optional `usage` field on tool-result messages ([pi#6671](https://github.com/earendil-works/pi/pull/6671)) and fold it into the native footer total, `/session` statistics (as `Tools/summaries`), and RPC `get_state` totals.
61
+
62
+ The extension participates in both directions:
63
+
64
+ - **Upward**: the tool result that *delivers* a run (foreground completion, or the first `wait`) carries the run's total provider usage as native `usage`. Attachment is gated on the same delivered-flag transition as output delivery, so it happens exactly once per run UUID. Status, replayed waits, steer, diff/apply/discard, and plan responses never attach run execution usage; plan may report its own selector usage through its result instead (rule 10). Older Pi hosts copy only `content`/`details` from tool results and silently ignore the field; that is safe on every version this package supports.
65
+ - **Downward**: a child's event stream may contain tool-result messages that themselves carry nested usage (for example, a grandchild subagent on a new-Pi child). The parent folds that into the run's cumulative usage, so `max_cost` budgets and the execution/routing ledgers see true subtree spend. Pre-#6671 children simply never emit the field.
66
+
67
+ Known undercounts in the **native** total (the extension ledger still counts these from persisted entries):
68
+
69
+ - A background run dismissed in the overlay (or via status) without a delivering `wait` never produces a tool result, so its spend reaches only the extension ledger.
70
+ - A failed or lost run raises an error instead of returning a tool result; any pre-failure usage likewise reaches only the extension ledger. The same native limitation applies to selector usage on a thrown error: the extension ledger retains it, but a thrown tool call cannot attach a native usage object. This is documented rather than converted into a success.
71
+
72
+ Because the native footer counts parent assistant messages plus delivered tool-result usage, and the extension's **combined** counts the same runs by UUID, the reported token totals agree when every run and selector receipt was successfully delivered as native usage. Thrown errors and undelivered plan/dispatch failures can leave selector usage only in the extension ledger; currency totals never include unreported selector fees.
73
+
74
+ ## Deduplication rules
75
+
76
+ 1. Root assistant messages are counted once by session-entry ID.
77
+ 2. Each subagent run is counted once by full run UUID; the newest live/checkpoint/terminal cumulative value replaces older values.
78
+ 3. Delivery, dismissal, status, and checkpoint events never add cost.
79
+ 4. If an old run is evicted from in-memory UI history, its latest persisted usage still contributes to the session ledger.
80
+ 5. Active and immediately completed runs supplement or replace stale persisted checkpoints until newer session entries become visible; the full run UUID prevents double counting afterward.
81
+ 6. Resumed and forked invocations are distinct billed runs. Their new provider usage is counted once, even though they reuse prior context.
82
+ 7. Retry attempts (transient-failure retries on the already selected model and tool set)
83
+ accumulate into their run's single usage record: every attempt's billed usage counts once
84
+ under one run UUID. There is no fallback-model escalation, so `attemptedModels` repeats the
85
+ selected model rather than recording a route change.
86
+ 8. The optional parallel `synthesis` child bills into the same run as an extra result.
87
+ 9. Selector requests are counted once by full selector request ID, including plan
88
+ requests and pre-spawn rejected decisions. Route references inside task results
89
+ never add selector usage again, and each receipt is folded once across
90
+ replay/status/repeated wait.
91
+ 10. Native `usage` on the delivering tool result mirrors rule 2's run totals and is attached at most once per run (delivered-flag gated), so Pi-side totals cannot double count a run either. Worker pre-spawn routing tokens attach once at async start; the first delivery/wait attaches child execution usage plus any deferred-synthesis routing tokens not yet delivered; foreground completion attaches all invocation routing tokens plus execution tokens. Plan attaches only its own routing usage. Missing usage on an interrupted or invalid response stays **unknown**, never an invented zero. A partial or malformed token report retains individually validated counts while marking completeness unknown; for example, valid input with invalid output is not a complete report.
92
+
93
+ Native routing attachment commits atomically: foreground/wait use the run's single
94
+ `delivered` event for linked receipts; plan and async-start use one `native-delivery`
95
+ request-ID batch on the routing event stream. A throwing append does not consume a
96
+ prefix or set the in-memory run delivered flag. Transient persistence failures retry
97
+ without another selector call. A batch holds at most 1024 receipt IDs; larger plan or
98
+ background requests must be split. Background requests check this bound before any
99
+ child/run registration. Already incurred selector usage stays in the ledger.
100
+
101
+ ## Branch semantics
102
+
103
+ Only `sessionManager.getBranch()` is used. Costs from abandoned sibling branches are excluded. When a parent session is forked, its inherited active-branch terminal entries remain part of that fork's historical total; new runs are added to the fork independently.
104
+
105
+ ## Failure and cancellation
106
+
107
+ Any usage reported before a failure, timeout, budget stop, cancellation, or parent crash is retained in a cumulative checkpoint/terminal record. A run with no provider response contributes zero rather than an estimate.
108
+
109
+ ## Provider limitations
110
+
111
+ Accounting is only as precise as the provider data normalized by Pi:
112
+
113
+ - Some providers may report zero or incomplete costs.
114
+ - `reasoning` is a subset of output tokens and is not added to output again.
115
+ - `contextTokens` is the latest turn's context size, not an additive billed-token field.
116
+ - The extension deliberately does not infer missing prices from a local model table, and TypeSafe's token-only reports are never converted into a dollar estimate.
package/docs/RELEASING.md CHANGED
@@ -1,32 +1,32 @@
1
- # Releasing @cr1ms0n/pi-subagent
2
-
3
- This is an independent community fork of `@parke.dev/pi-subagent` by Luke Parke. Preserve the MIT license and copyright, and do not publish this fork under the upstream scope. The upstream monorepo's tag automation does not publish this package.
4
-
5
- ## Prepare
6
-
7
- 1. Update `package.json` and `CHANGELOG.md`; use an unpublished version.
8
- 2. Run strict type checking against the installed Pi version and offline fake-runner/extension tests. Do not run real provider calls without permission.
9
- 3. Run `npm pack --dry-run --json` and review the file list. Do not include authentication, user settings, machine-specific paths, local backups, session transcripts or generated test bundles.
10
- 4. Run `npm pack --ignore-scripts`, install the resulting tarball into an isolated prefix with `--ignore-scripts --legacy-peer-deps`, then repeat the offline checks against the packed source.
11
-
12
- ## Publish
13
-
14
- Use an existing npm login or a securely supplied environment-based credential. Never put tokens in command text, source files, or a committed `.npmrc`. Enable TLS verification.
15
-
16
- ```bash
17
- npm whoami --strict-ssl=true --registry=https://registry.npmjs.org/
18
- npm publish ./cr1ms0n-pi-subagent-<version>.tgz --access public --ignore-scripts --strict-ssl=true --registry=https://registry.npmjs.org/
19
- npm view @cr1ms0n/pi-subagent@<version> name version dist.integrity --strict-ssl=true --registry=https://registry.npmjs.org/
20
- ```
21
-
22
- A successful public release makes every file in the tarball public. Verify the package metadata and registry integrity before replacing an existing installation.
23
-
24
- ## Install and replace
25
-
26
- ```bash
27
- pi install npm:@cr1ms0n/pi-subagent@<version>
28
- ```
29
-
30
- Do not enable this package and `@parke.dev/pi-subagent` at the same time: both register the same tool names. Back up the old package selection, activate only the new package after verification, and keep the old source for rollback. Reload or restart Pi after changing packages. Configure `~/.pi/subagent.json` modelPolicy before spawning tasks.
31
-
32
- This fork uses the same config and persisted state paths as upstream. Switching packages is not a data migration. Upgrading must not overwrite user model choices or credentials.
1
+ # Releasing @cr1ms0n/pi-subagent
2
+
3
+ This is an independent community fork of `@parke.dev/pi-subagent` by Luke Parke. Preserve the MIT license and copyright, and do not publish this fork under the upstream scope. The upstream monorepo's tag automation does not publish this package.
4
+
5
+ ## Prepare
6
+
7
+ 1. Update `package.json` and `CHANGELOG.md`; use an unpublished version.
8
+ 2. Run strict type checking against the installed Pi version and offline fake-runner/extension tests. Do not run real provider calls without permission.
9
+ 3. Run `npm pack --dry-run --json` and review the file list. Do not include authentication, user settings, machine-specific paths, local backups, session transcripts or generated test bundles.
10
+ 4. Run `npm pack --ignore-scripts`, install the resulting tarball into an isolated prefix with `--ignore-scripts --legacy-peer-deps`, then repeat the offline checks against the packed source.
11
+
12
+ ## Publish
13
+
14
+ Use an existing npm login or a securely supplied environment-based credential. Never put tokens in command text, source files, or a committed `.npmrc`. Enable TLS verification.
15
+
16
+ ```bash
17
+ npm whoami --strict-ssl=true --registry=https://registry.npmjs.org/
18
+ npm publish ./cr1ms0n-pi-subagent-<version>.tgz --access public --ignore-scripts --strict-ssl=true --registry=https://registry.npmjs.org/
19
+ npm view @cr1ms0n/pi-subagent@<version> name version dist.integrity --strict-ssl=true --registry=https://registry.npmjs.org/
20
+ ```
21
+
22
+ A successful public release makes every file in the tarball public. Verify the package metadata and registry integrity before replacing an existing installation.
23
+
24
+ ## Install and replace
25
+
26
+ ```bash
27
+ pi install npm:@cr1ms0n/pi-subagent@<version>
28
+ ```
29
+
30
+ Do not enable this package and `@parke.dev/pi-subagent` at the same time: both register the same tool names. Back up the old package selection, activate only the new package after verification, and keep the old source for rollback. Reload or restart Pi after changing packages. Configure `~/.pi/subagent.json` `jevRouting` (candidate models plus the credential environment variable) before spawning tasks.
31
+
32
+ This fork uses the same config and persisted state paths as upstream. Switching packages is not a data migration. Upgrading must not overwrite user model choices or credentials.
package/docs/SECURITY.md CHANGED
@@ -10,7 +10,7 @@ and can use tools according to their capability profile.
10
10
  |---------|---------------|---------|
11
11
  | `explore` | `read`, `grep`, `find`, `ls` (+ safe extras and Pi context tools) | No project-file writes |
12
12
  | `review` | same as explore | No project-file writes |
13
- | `general` | inherited active tools (+ Pi context tools) | Yes if `bash`/`edit`/`write` are active |
13
+ | `general` | Jev-chosen subset of the full available locally permitted catalog (+ Pi context tools) | Yes if the selected tools include `bash`/`edit`/`write` |
14
14
 
15
15
  Parallel mode defaults to `explore` to avoid concurrent shared writes.
16
16
 
@@ -20,7 +20,16 @@ Parallel mode defaults to `explore` to avoid concurrent shared writes.
20
20
  an explore/review profile. Pi context-management tools (`new_context`,
21
21
  `get_context_remaining`, `history`, `notes`) are an explicit control-plane
22
22
  exception: they may update continuity notes/window state but cannot access
23
- the project write tools.
23
+ the project write tools. The finalized tools reach the child as Pi's `--tools`
24
+ allowlist (`--no-tools` when empty), and the selector's answer is re-validated
25
+ locally: unknown, unavailable or unsafe choices cannot launch broader
26
+ capability, and an empty selection never becomes "all tools". Pi 0.86.0 is the
27
+ verified baseline for built-in, extension and late-registered tool enforcement;
28
+ a host that cannot honor the allowlist is refused rather than silently weakened.
29
+ Before the real task prompt, a package-local startup check verifies the routing
30
+ bootstrap command source and has the child acknowledge the exact selected model
31
+ and tool names; a mismatch aborts as a capability diagnostic and is never fixed
32
+ by widening tools, switching models or approving project trust.
24
33
  2. **Parallel writers** require `isolation: "worktree"`, distinct `cwd` values,
25
34
  or an explicit `allow_shared_writes: true` opt-in.
26
35
  3. **Depth is capped** (`maxDepth`, default 2). Nested children at the ceiling do
@@ -55,8 +64,34 @@ Parallel mode defaults to `explore` to avoid concurrent shared writes.
55
64
  Git worktrees only isolate the checkout. For untrusted tasks, use an outer
56
65
  container/cgroup/network policy.
57
66
  10. **`max_cost` is accounting, not a hard provider gate.** Usage arrives after a
58
- turn; orphans may spend money the ledger never sees. Combine with provider
59
- account budgets for hard spend limits.
67
+ turn; orphans may spend money the ledger never sees. It caps provider-reported
68
+ execution cost only: TypeSafe reports routing tokens, not currency, so selector
69
+ cost is unreported and outside `max_cost`. Combine with provider account
70
+ budgets for hard spend limits.
71
+
72
+ ## Routing disclosure and credentials
73
+
74
+ Jev routing sends a minimal projection to TypeSafe: the current delegated task
75
+ text, the configured candidate model IDs and your per-model descriptions,
76
+ eligible candidate tool names and descriptions, and necessary constraints
77
+ (profile, requested thinking, whether structured output is needed). It does not
78
+ upload repository files, conversation history, full system prompts, persona text
79
+ or tool parameter schemas, and does not read them in the background. Resume, fork
80
+ and synthesis select from the new task instruction rather than the assembled
81
+ transcript. Task text and model descriptions are user content and can themselves
82
+ contain secrets; there is no guaranteed redaction.
83
+
84
+ The TypeSafe credential is read from the environment variable named by
85
+ `jevRouting.apiKeyEnv` (default `TYPESAFE_API_KEY`) and sent only as an
86
+ `Authorization` header to the fixed official HTTPS endpoint, with redirects
87
+ disabled. It is never stored in the config file, prompts, argv, logs or results.
88
+ Rotate any credential that has been pasted into a transcript or shared in
89
+ conversation.
90
+
91
+ New extension-managed dispatch is Pi-only. A `backend: "codex"` or
92
+ `backend: "claude"` new task is rejected before any selector or provider work,
93
+ including a backend inherited from agent frontmatter, rather than silently
94
+ switched to Pi. Existing native-backend runs stay manageable.
60
95
 
61
96
  ## Trust and project cwd
62
97
 
@@ -72,7 +107,9 @@ duplicate output paths across parallel workers.
72
107
  ## Named agent files
73
108
 
74
109
  Agent files (`.pi/agents/`, `.agents/agents/`, global agent dir) inject their
75
- body into the child's system prompt and set its model/tools/budgets. A
110
+ body into the child's system prompt and set persona, thinking and budget
111
+ defaults. Model and tool selection come from Jev routing; a legacy
112
+ `model`/`fallback_models` in frontmatter is ignored. A
76
113
  project-level agent file shapes subagent behavior the same way project
77
114
  extensions and skills do — review them like code when working in untrusted
78
115
  repositories. Mitigations: capability profiles still fail closed (an agent