@cr1ms0n/pi-subagent 0.8.8 → 0.8.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,13 +2,11 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
- ## 0.8.8 — 2026-09-16
5
+ ## 0.8.9 — 2026-09-16
6
6
 
7
- ### Remote Context tools are target-model gated
7
+ ### Restore the stable 0.8.7 behavior
8
8
 
9
- - Restrict `new_context`, `get_context_remaining`, `history`, and `notes` to Pi children whose target model is an exact `compaction.gatewayContextModels` entry of the operator-owned `pi-openai-toolkit` config while `compaction.contextManagement` is `remote`. The previous 0.8.6 behavior appended these tools to every Pi child whenever the parent exposed them, so a non-allowlisted child could receive context tools simply because the parent session had them.
10
- - Matching is exact and case-sensitive on the full `provider/model` string; prefixes, bare model ids, and native `openai-codex` identity do not match. Explicit requests for a context tool on a non-eligible target are rejected as unavailable, inherited/default lists are filtered, and retry fallback attempts are re-filtered per attempt so a disallowed fallback cannot inherit them. The internally constructed synthesis child uses the same gate.
11
- - Missing, unreadable, invalid, or non-`remote` toolkit configuration now fails closed to no context tools. Non-Pi backends, read-only/write classification, and model-policy routing are unchanged. The package reads the existing operator-owned allowlist and does not duplicate it in `~/.pi/subagent.json`.
9
+ - 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
10
 
13
11
  ## 0.8.7 — 2026-09-14
14
12
 
package/README.md CHANGED
@@ -193,30 +193,17 @@ Set a persona's backend in agent frontmatter with `backend: codex`.
193
193
 
194
194
  | Profile | Tools | Writes |
195
195
  | ---------------------------- | ------------------------------------------------------- | ------------------------------------------- |
196
- | `explore` (parallel default) | read/grep/find/ls + safe extras + allowlisted context tools | no project-file writes |
196
+ | `explore` (parallel default) | read/grep/find/ls + safe extras + Pi context tools | no project-file writes |
197
197
  | `review` | same as explore | no project-file writes |
198
- | `general` | inherited active tools + allowlisted context tools | yes if tools include bash/edit/write |
198
+ | `general` | inherited active tools + Pi context tools | yes if tools include bash/edit/write |
199
199
 
200
200
  For the Pi backend, the context-management tools `new_context`,
201
201
  `get_context_remaining`, `history`, and `notes` are retained in child tool
202
- allowlists only when all three hold: the toolkit config at
203
- `$PI_CODING_AGENT_DIR/extensions/pi-openai-toolkit/config.json` (default:
204
- `~/.pi/agent/extensions/pi-openai-toolkit/config.json`) has
205
- `compaction.contextManagement: "remote"`, the child's target model exactly
206
- equals one `compaction.gatewayContextModels` entry, and the parent exposes the
207
- tool. Eligibility is exact and case-sensitive on the full `provider/model`
208
- string — prefixes, bare model ids, and native `openai-codex` identity do not
209
- match. The package reads that operator-owned list and never duplicates it.
210
-
211
- Context tools are control-plane tools: they may update continuity notes or the
212
- remote context window, but cannot modify the child checkout or run a shell
213
- command. For an eligible target they are appended even when the task supplies a
214
- narrower tool list; for every other Pi target they are removed from inherited
215
- tools, never passed to `--tools`, and an explicit request for one is rejected as
216
- unavailable. Retry fallback attempts are filtered per attempt, so a disallowed
217
- fallback cannot inherit them. If the toolkit file is missing, unreadable,
218
- invalid, or not in Remote Context mode, the effective allowlist is empty (fail
219
- closed). Non-Pi backends are unchanged.
202
+ allowlists when the parent exposes them. They are control-plane tools: they may
203
+ update continuity notes or the remote context window, but cannot modify the
204
+ child checkout or run a shell command. This exception also applies when a task
205
+ supplies a narrower tool list, so Pi's `contextManagement` remains usable for
206
+ configured gateway models.
220
207
 
221
208
  Parallel write-capable tasks sharing one checkout are rejected unless each uses
222
209
  `isolation: "worktree"`, distinct `cwd`, or explicit `allow_shared_writes: true`.
@@ -1,132 +1,125 @@
1
- # Architecture contract
2
-
3
- `pi-subagent` is split by ownership boundary:
4
-
5
- - `runner.ts`: one child process, Pi RPC protocol (JSONL commands on stdin, events on
6
- stdout — a superset of `--mode json`), cancellation, process trees, budgets, and a live
7
- stdin command channel used for mid-run steering. Extension UI dialogs from headless
8
- children are auto-cancelled so they can never hang a run; stdin is closed after
9
- `agent_settled` so RPC children shut down cleanly. Budget breaches steer a wrap-up
10
- message and allow grace turns before SIGTERM (`wrappedUp` marks a clean conclusion).
11
- A stall watchdog flags protocol silence, probes liveness via `get_state`, and kills
12
- after a second window so retry can take over. Group kills verify process start-time
13
- identity (Linux `/proc`, macOS/BSD `ps lstart`) before signalling a possibly-recycled
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. Each attempt
18
- re-derives its Pi context-tool membership from the attempt's target model via
19
- `context-policy.ts`, so an ineligible fallback cannot inherit the primary's tools.
20
- - `context-policy.ts`: sole reader/parser of the operator-owned `pi-openai-toolkit`
21
- Remote Context allowlist (`<Pi agent dir>/extensions/pi-openai-toolkit/config.json`).
22
- Exposes the exact `gatewayContextModels` match plus the four context-tool names, and
23
- fails closed to an empty allowlist on any missing/unreadable/invalid/non-remote
24
- configuration. The toolkit's native `openai-codex` rule is intentionally not mirrored.
25
- - `context: "fork"` spawns the child with `--fork <parent session file>` so it starts
26
- from a real branched copy of the parent conversation. Fail-fast when the parent
27
- session is not persisted; single-task only.
28
- - `registry.ts`: one parent-session runtime, run state, snapshots, resume locks, and the
29
- single LiveRun→snapshot/persisted-result projections used by every consumer.
30
- - `semaphore.ts`: per-parent-runtime child-process limit.
31
- - `process-lock.ts`: machine-wide durable coordination under `~/.pi/subagent-locks/`
32
- exclusive per-child-session resume locks, global concurrency slots, and run process
33
- identity records for orphan reconcile.
34
- - `launch.ts`: resolve the child `pi` invocation via `PI_SUBAGENT_BIN` or
35
- `process.execPath` + CLI entry (bare PATH name only as last-resort fallback).
36
- - `persistence.ts`: versioned active-branch event folding and bounded child transcript metadata.
37
- - `maintenance.ts`: filesystem GC (session files) and abort-race helpers; kept out of persistence.
38
- - `usage.ts`: provider-reported root/subagent/combined accounting.
39
- - `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 are granted per target model: `context-policy.ts` reads the operator-owned `pi-openai-toolkit` Remote Context allowlist (`compaction.gatewayContextModels` with `contextManagement: "remote"`), `policy.ts` removes the four context names from every non-eligible Pi child, `orchestrator.ts` re-filters them per fallback attempt, and both fail closed to no context tools when the config is missing or invalid.
40
- - `config.ts`: defaults `~/.pi/subagent.json` `PI_SUBAGENT_*` env overrides.
41
- - `structured.ts`: structured-output contract (dependency-free JSON-Schema subset
42
- validation, fenced json:result extraction, contract/repair prompts) and
43
- conservative double-encoded-arg repair. The runner gates the child's settle on
44
- validation and runs one steer-based repair round before accepting failure.
45
- - `agents.ts`: named agent files (`.pi/agents/`, `.agents/agents/`, global agent dir).
46
- Flat-YAML frontmatter + markdown persona body; resolved in policy with explicit
47
- params > agent file > profile taskDefaults > parent inheritance. Catalog refreshes
48
- lazily (5s TTL) so new files work mid-session; symlinks and oversized files skipped.
49
- - `notifications.ts`: background-run completion batching. Successes group within a
50
- debounce window (hard cap on hold time); failures bypass batching and flush
51
- immediately; delivered-state is re-checked at flush time so a consuming `wait`
52
- suppresses the redundant notification.
53
- - `ui.ts`: renderers, footer status and `/subagents` inspector. The ambient widget
54
- (extension-side) shows BACKGROUND runs only foreground runs render inline as the
55
- tool result, so widget display would double-render them.
56
- - `extension.ts`: wiring only; no business logic. Nested children at the depth ceiling do
57
- not re-register the tool; only top-level parents run maintenance.
58
-
59
- Invariants:
60
-
61
- 1. A run belongs to exactly one parent session key and cannot update another session.
62
- 2. No more than `maxActiveProcesses` children run per extension runtime, and no more than
63
- `maxGlobalActive` across every Pi parent process on the machine.
64
- 3. Cancellation prevents queued tasks from spawning.
65
- 4. A child session may have only one direct resume writer at a time, enforced by an
66
- in-memory lock *and* a durable file lock under `lockDir` that survives crashes and
67
- coordinates across independent parent processes.
68
- 5. Parallel write-capable tasks need isolated worktrees/distinct cwd or explicit unsafe opt-in.
69
- 6. Every tool response is globally capped to 50 KB / 2,000 lines; full data lives in artifacts/transcripts.
70
- 7. Status is compact; wait is the one-shot deliverable.
71
- 8. On shutdown or tree navigation, child runs are cancelled and awaited for a bounded grace period.
72
- 9. On startup, orphan process groups recorded under `lockDir` are reaped (SIGTERM then SIGKILL)
73
- before any matching child session is eligible for resume. `$state: "lost"` is a labeling
74
- that keeps `resumeBlocked` until reconciliation proves death.
75
- 10. Billed usage is folded once per root message and once per full child run UUID.
76
- 11. Checkpoint persistence events are lightweight (state, usage, process identity, pointers).
77
- Full transcripts and final output are persisted exactly once, in the terminal event.
78
- 12. High-frequency registry "changed" events coalesce (trailing window); state transitions,
79
- new child sessions, billed-usage advances, and terminal events flush immediately.
80
- 13. `wait` is interruptible: aborting a wait returns promptly and does NOT cancel the
81
- background run. Only `cancel` (or parent shutdown) aborts a run.
82
- 14. Budget stops (`max_turns`/`max_cost`) with at least one completed turn end as `partial`
83
- and deliver their output normally. Streams truncated after useful assistant output also
84
- end as `partial`. Timeouts report `state: "timeout"` with `timeoutPhase`.
85
- 15. `timeout_ms` covers the whole task, including semaphore queue time, but the phase
86
- (queued / starting / running) is recorded so agents can apply the right retry policy.
87
- 16. Worktrees live under a durable root (`~/.pi/subagent-worktrees`), never a purgeable OS
88
- tmpdir. Startup maintenance (top-level parents only) prunes stale git registrations,
89
- removes unchanged leftovers, and sweeps changed-but-expired worktrees. Live-run
90
- worktrees are always shielded. `include_wip` worktrees carry the parent's WIP patch in
91
- the handle: `diff`/`apply` subtract it when subtraction is clean and otherwise report
92
- the combined delta with an explicit `[includes parent WIP]` warning never silently
93
- wrong; a worktree containing only the untouched WIP patch counts as unchanged.
94
- 17. Process-tree reaping after a clean exit can be disabled per task with `keep_background`
95
- (for legitimately backgrounded work such as dev servers); forced stops always reap.
96
- 18. Protocol completion prefers Pi's `agent_settled` event. Legacy `agent_end` without
97
- `willRetry` is accepted for older Pi builds; `agent_end` with `willRetry: true` is
98
- treated as non-terminal.
99
- 19. Depth and spawn-policy parsing fail closed on malformed values: env scrubbing cannot
100
- silently reset the depth counter to top-level, and a malformed `PI_SUBAGENT_SPAWNS`
101
- disables spawning rather than unrestricting it.
102
- 20. Budget breaches (`max_turns`/`max_cost`) steer a wrap-up message and allow grace
103
- turns before SIGTERM; a child that concludes within grace ends `partial` with
104
- `wrappedUp: true`. `graceTurns: 0` restores immediate stops.
105
- 21. Transient failures (queued timeout, stall, spawn error, provider error, protocol
106
- truncation) retry up to `maxRetries` extra attempts, escalating through
107
- `fallback_models`; usage accumulates across attempts and `attemptedModels` is
108
- recorded. Task-quality failures (nonzero exit with complete protocol, cancellation,
109
- budget stop, running timeout) never retry.
110
- 22. The stall watchdog treats protocol silence as suspect, not fatal: after
111
- `stallAfterMs` the task is flagged and probed via `get_state` (a live child's
112
- answer clears the flag); only continued silence for `stallKillAfterMs` more kills
113
- the child which is then a transient failure eligible for retry.
114
- 23. Only `async: true` runs notify on completion and appear in the ambient widget.
115
- Notification delivery respects delivered-once: a `wait` that consumed the run
116
- suppresses the notification.
117
- 24. Named agent files supply per-field defaults only; explicit request params always
118
- win, and capability profiles fail closed regardless of what an agent file declares.
119
- 25. Structured-output validation never discards paid work: schema failure after the
120
- repair round downgrades completed → partial with `structuredError`, and the raw
121
- text still delivers. Validation is enforced on the parent side of the process
122
- boundary the child cannot self-attest.
123
- 26. Arg repair only decodes free-text fields with high-signal escape patterns
124
- (literal \n or \") and no real newlines; identifier fields, tool lists, and
125
- Windows-path-like strings are never modified.
126
- 27. Global slots are depth-tiered: `tryAcquireGlobalSlot(runId, depth)` admits only while
127
- `activeAtOrBelowDepth(depth) < maxGlobalActive - reservedFor(depth)`, holding slots
128
- back for deeper tiers so a full-width spawn tree cannot deadlock on its own children.
129
- Slot records without a `depth` field count as depth 0.
130
- 28. `action: "plan"` is a truth oracle: it runs the exact validation and preflights of a
131
- real spawn and returns the resolved plan without spawning — never a softer check, and
132
- never a registry entry.
1
+ # Architecture contract
2
+
3
+ `pi-subagent` is split by ownership boundary:
4
+
5
+ - `runner.ts`: one child process, Pi RPC protocol (JSONL commands on stdin, events on
6
+ stdout — a superset of `--mode json`), cancellation, process trees, budgets, and a live
7
+ stdin command channel used for mid-run steering. Extension UI dialogs from headless
8
+ children are auto-cancelled so they can never hang a run; stdin is closed after
9
+ `agent_settled` so RPC children shut down cleanly. Budget breaches steer a wrap-up
10
+ message and allow grace turns before SIGTERM (`wrappedUp` marks a clean conclusion).
11
+ A stall watchdog flags protocol silence, probes liveness via `get_state`, and kills
12
+ after a second window so retry can take over. Group kills verify process start-time
13
+ identity (Linux `/proc`, macOS/BSD `ps lstart`) before signalling a possibly-recycled
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.
18
+ - `context: "fork"` spawns the child with `--fork <parent session file>` so it starts
19
+ from a real branched copy of the parent conversation. Fail-fast when the parent
20
+ session is not persisted; single-task only.
21
+ - `registry.ts`: one parent-session runtime, run state, snapshots, resume locks, and the
22
+ single LiveRun→snapshot/persisted-result projections used by every consumer.
23
+ - `semaphore.ts`: per-parent-runtime child-process limit.
24
+ - `process-lock.ts`: machine-wide durable coordination under `~/.pi/subagent-locks/` —
25
+ exclusive per-child-session resume locks, global concurrency slots, and run process
26
+ identity records for orphan reconcile.
27
+ - `launch.ts`: resolve the child `pi` invocation via `PI_SUBAGENT_BIN` or
28
+ `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
+ - `maintenance.ts`: filesystem GC (session files) and abort-race helpers; kept out of persistence.
31
+ - `usage.ts`: provider-reported root/subagent/combined accounting.
32
+ - `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.
33
+ - `config.ts`: defaults `~/.pi/subagent.json` ← `PI_SUBAGENT_*` env overrides.
34
+ - `structured.ts`: structured-output contract (dependency-free JSON-Schema subset
35
+ validation, fenced json:result extraction, contract/repair prompts) and
36
+ conservative double-encoded-arg repair. The runner gates the child's settle on
37
+ validation and runs one steer-based repair round before accepting failure.
38
+ - `agents.ts`: named agent files (`.pi/agents/`, `.agents/agents/`, global agent dir).
39
+ Flat-YAML frontmatter + markdown persona body; resolved in policy with explicit
40
+ params > agent file > profile taskDefaults > parent inheritance. Catalog refreshes
41
+ lazily (5s TTL) so new files work mid-session; symlinks and oversized files skipped.
42
+ - `notifications.ts`: background-run completion batching. Successes group within a
43
+ debounce window (hard cap on hold time); failures bypass batching and flush
44
+ immediately; delivered-state is re-checked at flush time so a consuming `wait`
45
+ suppresses the redundant notification.
46
+ - `ui.ts`: renderers, footer status and `/subagents` inspector. The ambient widget
47
+ (extension-side) shows BACKGROUND runs only foreground runs render inline as the
48
+ tool result, so widget display would double-render them.
49
+ - `extension.ts`: wiring only; no business logic. Nested children at the depth ceiling do
50
+ not re-register the tool; only top-level parents run maintenance.
51
+
52
+ Invariants:
53
+
54
+ 1. A run belongs to exactly one parent session key and cannot update another session.
55
+ 2. No more than `maxActiveProcesses` children run per extension runtime, and no more than
56
+ `maxGlobalActive` across every Pi parent process on the machine.
57
+ 3. Cancellation prevents queued tasks from spawning.
58
+ 4. A child session may have only one direct resume writer at a time, enforced by an
59
+ in-memory lock *and* a durable file lock under `lockDir` that survives crashes and
60
+ coordinates across independent parent processes.
61
+ 5. Parallel write-capable tasks need isolated worktrees/distinct cwd or explicit unsafe opt-in.
62
+ 6. Every tool response is globally capped to 50 KB / 2,000 lines; full data lives in artifacts/transcripts.
63
+ 7. Status is compact; wait is the one-shot deliverable.
64
+ 8. On shutdown or tree navigation, child runs are cancelled and awaited for a bounded grace period.
65
+ 9. On startup, orphan process groups recorded under `lockDir` are reaped (SIGTERM then SIGKILL)
66
+ before any matching child session is eligible for resume. `$state: "lost"` is a labeling
67
+ that keeps `resumeBlocked` until reconciliation proves death.
68
+ 10. Billed usage is folded once per root message and once per full child run UUID.
69
+ 11. Checkpoint persistence events are lightweight (state, usage, process identity, pointers).
70
+ Full transcripts and final output are persisted exactly once, in the terminal event.
71
+ 12. High-frequency registry "changed" events coalesce (trailing window); state transitions,
72
+ new child sessions, billed-usage advances, and terminal events flush immediately.
73
+ 13. `wait` is interruptible: aborting a wait returns promptly and does NOT cancel the
74
+ background run. Only `cancel` (or parent shutdown) aborts a run.
75
+ 14. Budget stops (`max_turns`/`max_cost`) with at least one completed turn end as `partial`
76
+ and deliver their output normally. Streams truncated after useful assistant output also
77
+ end as `partial`. Timeouts report `state: "timeout"` with `timeoutPhase`.
78
+ 15. `timeout_ms` covers the whole task, including semaphore queue time, but the phase
79
+ (queued / starting / running) is recorded so agents can apply the right retry policy.
80
+ 16. Worktrees live under a durable root (`~/.pi/subagent-worktrees`), never a purgeable OS
81
+ tmpdir. Startup maintenance (top-level parents only) prunes stale git registrations,
82
+ removes unchanged leftovers, and sweeps changed-but-expired worktrees. Live-run
83
+ worktrees are always shielded. `include_wip` worktrees carry the parent's WIP patch in
84
+ the handle: `diff`/`apply` subtract it when subtraction is clean and otherwise report
85
+ the combined delta with an explicit `[includes parent WIP]` warning never silently
86
+ wrong; a worktree containing only the untouched WIP patch counts as unchanged.
87
+ 17. Process-tree reaping after a clean exit can be disabled per task with `keep_background`
88
+ (for legitimately backgrounded work such as dev servers); forced stops always reap.
89
+ 18. Protocol completion prefers Pi's `agent_settled` event. Legacy `agent_end` without
90
+ `willRetry` is accepted for older Pi builds; `agent_end` with `willRetry: true` is
91
+ treated as non-terminal.
92
+ 19. Depth and spawn-policy parsing fail closed on malformed values: env scrubbing cannot
93
+ silently reset the depth counter to top-level, and a malformed `PI_SUBAGENT_SPAWNS`
94
+ disables spawning rather than unrestricting it.
95
+ 20. Budget breaches (`max_turns`/`max_cost`) steer a wrap-up message and allow grace
96
+ turns before SIGTERM; a child that concludes within grace ends `partial` with
97
+ `wrappedUp: true`. `graceTurns: 0` restores immediate stops.
98
+ 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.
103
+ 22. The stall watchdog treats protocol silence as suspect, not fatal: after
104
+ `stallAfterMs` the task is flagged and probed via `get_state` (a live child's
105
+ answer clears the flag); only continued silence for `stallKillAfterMs` more kills
106
+ the child which is then a transient failure eligible for retry.
107
+ 23. Only `async: true` runs notify on completion and appear in the ambient widget.
108
+ Notification delivery respects delivered-once: a `wait` that consumed the run
109
+ suppresses the notification.
110
+ 24. Named agent files supply per-field defaults only; explicit request params always
111
+ win, and capability profiles fail closed regardless of what an agent file declares.
112
+ 25. Structured-output validation never discards paid work: schema failure after the
113
+ repair round downgrades completed partial with `structuredError`, and the raw
114
+ text still delivers. Validation is enforced on the parent side of the process
115
+ boundary the child cannot self-attest.
116
+ 26. Arg repair only decodes free-text fields with high-signal escape patterns
117
+ (literal \n or \") and no real newlines; identifier fields, tool lists, and
118
+ Windows-path-like strings are never modified.
119
+ 27. Global slots are depth-tiered: `tryAcquireGlobalSlot(runId, depth)` admits only while
120
+ `activeAtOrBelowDepth(depth) < maxGlobalActive - reservedFor(depth)`, holding slots
121
+ back for deeper tiers so a full-width spawn tree cannot deadlock on its own children.
122
+ 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.
package/docs/SECURITY.md CHANGED
@@ -1,97 +1,88 @@
1
- # Security model
2
-
3
- Pi packages run with full system access. This extension spawns child `pi`
4
- processes that inherit the parent environment (including provider credentials)
5
- and can use tools according to their capability profile.
6
-
7
- ## What subagents can do
8
-
9
- | Profile | Default tools | Writes? |
10
- |---------|---------------|---------|
11
- | `explore` | `read`, `grep`, `find`, `ls` (+ safe extras and allowlisted Pi context tools) | No project-file writes |
12
- | `review` | same as explore | No project-file writes |
13
- | `general` | inherited active tools (+ allowlisted Pi context tools) | Yes if `bash`/`edit`/`write` are active |
14
-
15
- Pi context tools are allowlisted by **target model**, not by parent session: a
16
- child receives `new_context`, `get_context_remaining`, `history`, or `notes`
17
- only when the toolkit config has `compaction.contextManagement: "remote"`, the
18
- child's target model exactly equals a `compaction.gatewayContextModels` entry,
19
- and the parent exposes the tool. Missing/unreadable/invalid/disabled toolkit
20
- configuration yields no context tools. Retry fallback attempts are re-filtered
21
- per attempt, so a disallowed fallback cannot inherit them.
22
-
23
- Parallel mode defaults to `explore` to avoid concurrent shared writes.
24
-
25
- ## Hard rules
26
-
27
- 1. **Read-only means no project-file mutation.** `bash` can rewrite the disk and is never part of
28
- an explore/review profile. Pi context-management tools (`new_context`,
29
- `get_context_remaining`, `history`, `notes`) are an explicit control-plane
30
- exception, but only for the exact target models allowlisted above: they may
31
- update continuity notes/window state but cannot access the project write
32
- tools, and they are rejected as unavailable for every other target.
33
- 2. **Parallel writers** require `isolation: "worktree"`, distinct `cwd` values,
34
- or an explicit `allow_shared_writes: true` opt-in.
35
- 3. **Depth is capped** (`maxDepth`, default 2). Nested children at the ceiling do
36
- not re-register the subagent tool. Depth is scheduling metadata — `bash` or an
37
- env-scrubbing wrapper can still invoke `pi` directly, so treat it as an
38
- accidental-recursion guard, not a security boundary.
39
- **Spawn allowlists** (`spawns:` in agent frontmatter, env `PI_SUBAGENT_SPAWNS`)
40
- refine that same guard: a child may be limited to named personas, or to none
41
- (tool not registered). Like depth, this is not a sandbox children can still
42
- shell out to `pi`.
43
- 4. **Process caps** limit concurrency both per parent session (`maxActiveProcesses`)
44
- and machine-wide (`maxGlobalActive`, default 16).
45
- 5. **Transcripts** under `~/.pi/subagent-sessions` may contain task content, tool
46
- output, and secrets that appeared in context. Protect that directory. Task text
47
- is delivered via stdin (not argv) so it stays out of `ps` listings, but it is
48
- still written into the child session log.
49
- 6. **Background permission prompts** are limited because children run headless
50
- (RPC mode). Extension UI dialogs raised inside a child are auto-cancelled so
51
- they can never hang a run — which also means a child can never obtain
52
- interactive consent. Prefer restricted tools for async/background runs.
53
- 11. **Steering messages** (`action: "steer"` and the overlay `s` key) inject text
54
- into a running child's conversation with user-level authority. Anything that
55
- can call the subagent tool can steer any live run in the same session.
56
- 7. **Process cleanup.** On POSIX, children run in their own process group so tree
57
- kills work for ordinary descendants. Parent (re)start reaps orphans recorded
58
- under `~/.pi/subagent-locks/runs/` so resume cannot race a still-alive writer.
59
- Grandchildren that call `setsid()` can still escape a simple process-group kill.
60
- 8. **Resume exclusivity.** Direct resume takes a durable per-session file lock;
61
- concurrent parents cannot append to the same child session.
62
- 9. **Profiles are tool-selection policy, not a sandbox.** Children inherit
63
- `$HOME`, SSH/cloud credentials, network access, and the parent filesystem.
64
- Git worktrees only isolate the checkout. For untrusted tasks, use an outer
65
- container/cgroup/network policy.
66
- 10. **`max_cost` is accounting, not a hard provider gate.** Usage arrives after a
67
- turn; orphans may spend money the ledger never sees. Combine with provider
68
- account budgets for hard spend limits.
69
-
70
- ## Trust and project cwd
71
-
72
- If `cwd` points outside the parent project, the child inherits whatever local
73
- project config/trust applies to that path. Treat external `cwd` as elevated risk
74
- and prefer read-only profiles when exploring third-party trees.
75
-
76
- ## Output artifacts
77
-
78
- `output` files are written by the child. Resolve paths carefully and reject
79
- duplicate output paths across parallel workers.
80
-
81
- ## Named agent files
82
-
83
- Agent files (`.pi/agents/`, `.agents/agents/`, global agent dir) inject their
84
- body into the child's system prompt and set its model/tools/budgets. A
85
- project-level agent file shapes subagent behavior the same way project
86
- extensions and skills do review them like code when working in untrusted
87
- repositories. Mitigations: capability profiles still fail closed (an agent
88
- cannot grant write tools under `explore`/`review`), symlinked agent files are
89
- skipped, names are validated against traversal characters, and files over
90
- 64KB are ignored.
91
-
92
- ## Machine-wide state
93
-
94
- `~/.pi/subagent-locks/` holds session locks, global concurrency slots, and run
95
- process identity records. It is per-user (under `$HOME`) and must not be shared
96
- across untrusted users/containers without care — a compromised client could
97
- interfere with lock reclaim on the same account.
1
+ # Security model
2
+
3
+ Pi packages run with full system access. This extension spawns child `pi`
4
+ processes that inherit the parent environment (including provider credentials)
5
+ and can use tools according to their capability profile.
6
+
7
+ ## What subagents can do
8
+
9
+ | Profile | Default tools | Writes? |
10
+ |---------|---------------|---------|
11
+ | `explore` | `read`, `grep`, `find`, `ls` (+ safe extras and Pi context tools) | No project-file writes |
12
+ | `review` | same as explore | No project-file writes |
13
+ | `general` | inherited active tools (+ Pi context tools) | Yes if `bash`/`edit`/`write` are active |
14
+
15
+ Parallel mode defaults to `explore` to avoid concurrent shared writes.
16
+
17
+ ## Hard rules
18
+
19
+ 1. **Read-only means no project-file mutation.** `bash` can rewrite the disk and is never part of
20
+ an explore/review profile. Pi context-management tools (`new_context`,
21
+ `get_context_remaining`, `history`, `notes`) are an explicit control-plane
22
+ exception: they may update continuity notes/window state but cannot access
23
+ the project write tools.
24
+ 2. **Parallel writers** require `isolation: "worktree"`, distinct `cwd` values,
25
+ or an explicit `allow_shared_writes: true` opt-in.
26
+ 3. **Depth is capped** (`maxDepth`, default 2). Nested children at the ceiling do
27
+ not re-register the subagent tool. Depth is scheduling metadata `bash` or an
28
+ env-scrubbing wrapper can still invoke `pi` directly, so treat it as an
29
+ accidental-recursion guard, not a security boundary.
30
+ **Spawn allowlists** (`spawns:` in agent frontmatter, env `PI_SUBAGENT_SPAWNS`)
31
+ refine that same guard: a child may be limited to named personas, or to none
32
+ (tool not registered). Like depth, this is not a sandbox children can still
33
+ shell out to `pi`.
34
+ 4. **Process caps** limit concurrency both per parent session (`maxActiveProcesses`)
35
+ and machine-wide (`maxGlobalActive`, default 16).
36
+ 5. **Transcripts** under `~/.pi/subagent-sessions` may contain task content, tool
37
+ output, and secrets that appeared in context. Protect that directory. Task text
38
+ is delivered via stdin (not argv) so it stays out of `ps` listings, but it is
39
+ still written into the child session log.
40
+ 6. **Background permission prompts** are limited because children run headless
41
+ (RPC mode). Extension UI dialogs raised inside a child are auto-cancelled so
42
+ they can never hang a run — which also means a child can never obtain
43
+ interactive consent. Prefer restricted tools for async/background runs.
44
+ 11. **Steering messages** (`action: "steer"` and the overlay `s` key) inject text
45
+ into a running child's conversation with user-level authority. Anything that
46
+ can call the subagent tool can steer any live run in the same session.
47
+ 7. **Process cleanup.** On POSIX, children run in their own process group so tree
48
+ kills work for ordinary descendants. Parent (re)start reaps orphans recorded
49
+ under `~/.pi/subagent-locks/runs/` so resume cannot race a still-alive writer.
50
+ Grandchildren that call `setsid()` can still escape a simple process-group kill.
51
+ 8. **Resume exclusivity.** Direct resume takes a durable per-session file lock;
52
+ concurrent parents cannot append to the same child session.
53
+ 9. **Profiles are tool-selection policy, not a sandbox.** Children inherit
54
+ `$HOME`, SSH/cloud credentials, network access, and the parent filesystem.
55
+ Git worktrees only isolate the checkout. For untrusted tasks, use an outer
56
+ container/cgroup/network policy.
57
+ 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.
60
+
61
+ ## Trust and project cwd
62
+
63
+ If `cwd` points outside the parent project, the child inherits whatever local
64
+ project config/trust applies to that path. Treat external `cwd` as elevated risk
65
+ and prefer read-only profiles when exploring third-party trees.
66
+
67
+ ## Output artifacts
68
+
69
+ `output` files are written by the child. Resolve paths carefully and reject
70
+ duplicate output paths across parallel workers.
71
+
72
+ ## Named agent files
73
+
74
+ 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
76
+ project-level agent file shapes subagent behavior the same way project
77
+ extensions and skills do — review them like code when working in untrusted
78
+ repositories. Mitigations: capability profiles still fail closed (an agent
79
+ cannot grant write tools under `explore`/`review`), symlinked agent files are
80
+ skipped, names are validated against traversal characters, and files over
81
+ 64KB are ignored.
82
+
83
+ ## Machine-wide state
84
+
85
+ `~/.pi/subagent-locks/` holds session locks, global concurrency slots, and run
86
+ process identity records. It is per-user (under `$HOME`) and must not be shared
87
+ across untrusted users/containers without care a compromised client could
88
+ interfere with lock reclaim on the same account.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cr1ms0n/pi-subagent",
3
- "version": "0.8.8",
3
+ "version": "0.8.9",
4
4
  "description": "Community fork of Luke Parke's pi-subagent with explicit model policy and model visibility for Pi",
5
5
  "type": "module",
6
6
  "license": "MIT",