@signalridge/pi-subagents 1.10.3 → 1.11.1
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 +53 -0
- package/README.md +34 -11
- package/examples/agent-tool-description.md +1 -1
- package/package.json +9 -8
- package/src/agent-manager.ts +1405 -486
- package/src/agent-runner.ts +538 -268
- package/src/agent-types.ts +6 -0
- package/src/ask-tools.ts +31 -21
- package/src/context-boundary.ts +54 -0
- package/src/context.ts +121 -36
- package/src/custom-agents.ts +19 -4
- package/src/enabled-models.ts +84 -83
- package/src/gate.ts +0 -0
- package/src/index.ts +1118 -861
- package/src/mention-clone.ts +129 -83
- package/src/model-runtime-bridge.ts +68 -0
- package/src/model-scope.ts +8 -3
- package/src/nested-tools.ts +13 -5
- package/src/project-trust.ts +146 -0
- package/src/settings.ts +7 -4
- package/src/skill-loader.ts +22 -10
- package/src/types.ts +6 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,58 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.11.1
|
|
4
|
+
### Patch Changes
|
|
5
|
+
|
|
6
|
+
- 3364cc0: Gate project agent definitions, own settings, and custom tool descriptions on the host's fail-closed project trust decision. Reject denied definition provenance before child package resolution, including fallbacks and global aliases into denied roots; refresh configuration authority on session replacement and reload.
|
|
7
|
+
- 3364cc0: Use patched Vitest 4.1.11 for development and regression testing without moving to a new major version. Refresh compatible transitive dependencies to include fixed source-map and brace-expansion releases.
|
|
8
|
+
- 3364cc0: Inherit the parent's public Pi project-trust decision before child resource resolution instead of accepting the SDK's trusted default. Unknown trust and different configuration roots deny project settings, extension factories, skills (including named preloading), and MCP configuration without prompting or granting trust. Explicit project resource paths cannot bypass denial. Preserve global resources, child policy restrictions, and trusted parent configuration during worktree execution and nested delegation; bind built-in MCP configuration discovery to that same trusted configuration root.
|
|
9
|
+
- 3364cc0: Preserve denied parent resource provenance when a child changes its configuration root. Reject explicit parent extensions and filter configured/discovered extensions and skills using both denied roots, including canonical aliases, before extension execution or skill injection. Named skill preloading also rejects denied sources behind global-root ancestor aliases. Genuine global resources and trusted parent configuration during worktree execution remain available; this resource trust boundary is not an OS sandbox or containment of arbitrary global extension behavior.
|
|
10
|
+
- 3364cc0: Fingerprint tracked gate inputs without textconv or external diff helpers, and retain full binary-safe changes so transformed or abbreviated diffs cannot reuse a stale passing verdict.
|
|
11
|
+
- 3364cc0: Declare `@sinclair/typebox` as a host-provided peer dependency with a `*` range instead of a runtime dependency, avoiding duplicate runtime modules and Pi extension-loader warnings.
|
|
12
|
+
- 3364cc0: Reject native global settings and MCP configuration aliases into retained denied project roots before SDK parsing or package resolution. Walk raw path components and symlink targets without collapsing dot segments, so a traversed denied directory cannot be hidden by a later `..` or an outward relay. Preserve genuine global configuration and aliases into approved parent roots without duplicating host configuration schemas.
|
|
13
|
+
- 3364cc0: Capture configuration trust before nested agent discovery and invocation defaults, and preserve denied ancestor roots across nested delegation. Reject global aliases into denied parent or child configuration roots before definition admission, model setup, or resource resolution while retaining authorized global definitions and standalone discovery defaults.
|
|
14
|
+
- 3364cc0: Adopt Pi 1.0.4's host-provided dependency contract: declare imported Pi and TypeBox modules as `*` peers rather than installing duplicate runtime modules, and align Pi development pins with the repository's tested 1.0.4 baseline. The wildcard identifies host module ownership; it does not promise compatibility with every Pi version.
|
|
15
|
+
- 3364cc0: Close configuration-admission gaps: apply model scope only from admitted settings at the captured configuration root, retain ancestor denials, and reject resource aliases that traverse denied project paths before escaping outside. Refresh provider-facing Agent descriptions and configuration-derived parameter descriptions through Pi's public tool-registration API when preparing prompts and continuations, preserving inactive tools. Disable gate-result caching while untracked files exist and prevent external diff commands during fingerprint inspection.
|
|
16
|
+
- 3364cc0: Keep Goal terminal tools directly declared but unavailable to nested execution, where termination is not propagated. Verify direct termination and settlement-gated queue advancement against the installed Pi 1.0.4 host and codemode.
|
|
17
|
+
|
|
18
|
+
Translate Pi exec results into the existing gate executor contract, failing killed commands even when their code is zero and enabling successful git fingerprint inspection. Exercise real-SDK exposure, nested approval, policy, and navigation regressions on Pi 1.0.4 instead of skipping them.
|
|
19
|
+
- Updated dependencies [3364cc0]
|
|
20
|
+
- Updated dependencies [3364cc0]
|
|
21
|
+
- Updated dependencies [3364cc0]
|
|
22
|
+
- Updated dependencies [3364cc0]
|
|
23
|
+
- @signalridge/pi-subagents-protocol@1.5.1
|
|
24
|
+
- @signalridge/pi-ui@1.3.3
|
|
25
|
+
|
|
26
|
+
## 1.11.0
|
|
27
|
+
### Minor Changes
|
|
28
|
+
|
|
29
|
+
- 8190516: Refuse automatic `inherit_context: true` before child creation on current Pi hosts: the parent projection precedes request-local redaction hooks and child provider routing cannot be pinned to the parent's physical endpoint. Supply a deliberately sanitized summary in the Agent task instead; previously persisted children containing inherited parent history must start a new session rather than resume. Carry the parent's checked ModelRuntime into child and mention sessions, failing before provider dispatch on hosts where an unavailable or incompatible runtime would otherwise silently lose custom providers and virtual routes.
|
|
30
|
+
- 8190516: Fail closed when raw `inherit_context: true` would copy pre-hook parent history into a child request: top-level, nested, and managed spawns now require an explicitly sanitized task summary instead, and child sessions persisted with inherited-history prompts cannot resume. Preserve the parent's configured providers and virtual model routes through a checked runtime bridge; refuse child creation on capable Pi hosts when that bridge is missing or incompatible rather than silently starting with a fresh model runtime. Older supported hosts retain their legacy model-registry option.
|
|
31
|
+
|
|
32
|
+
### Patch Changes
|
|
33
|
+
|
|
34
|
+
- 8190516: Keep a bounded, compaction-aware parent-history renderer for tests and a future host-safe inheritance API, prioritizing recent turns and summaries and retaining visible shell output tails. Current `inherit_context: true` no longer dispatches this pre-hook history to a child provider; see the separate fail-closed privacy change.
|
|
35
|
+
- 8190516: Respect Pi's `exposure` and `defaultActive` settings when exposing child extension tools, without reactivating tools deliberately disabled during a later turn. Bound selected extension `tool_call` handlers, including late registrations and nested tool calls, under a shared per-call deadline while preserving `pi.on` unsubscription; interactive `ask_tools` confirmation remains unbounded until the human responds. A timed-out hook returns a blocked result but cannot cancel side effects that continue after the Promise race.
|
|
36
|
+
- 8190516: Clarify that approving an `ask_tools` prompt allows subsequent calls to that tool for the whole child session, including resumed turns. The confirmation now names that scope rather than implying approval is limited to the displayed call, and parallel first calls share one approval decision instead of presenting contradictory dialogs.
|
|
37
|
+
- 8190516: Clarify that tool approvals survive resumed turns of the current in-memory child but are requested again after reopening its session from disk or restarting Pi.
|
|
38
|
+
- 8190516: Keep mention routing live when another extension cancels session or tree navigation; fence in-flight clones when the transition commits, and avoid a stale clone provider call if the origin changes during resource loading.
|
|
39
|
+
- 8190516: Discard pending off-screen agent mentions when their originating session or branch is replaced, including direct fallback after a failed clone; abort a hidden provider stream that remains active after replacement. Record the background spawn before UI and event updates so a post-spawn error cannot start a duplicate agent, while pre-spawn failures still fall back directly.
|
|
40
|
+
- 8190516: Preselect child extensions through Pi's public package resolver before loading restrictive include/exclude policies on hosts with replaceable built-ins. An excluded discovered extension can no longer register `/mcp` first and displace a selected built-in; explicit extension paths retain priority, host-disabled built-ins remain disabled, and missing sources or changed settings fail clearly instead of silently dropping selected capabilities.
|
|
41
|
+
- 8190516: Keep `skills: false` and named-skill subagents from inheriting skills added by loaded extensions via `resources_discover`. Apply the child skill policy after every Pi resource update, while leaving `skills: true` discovery unchanged.
|
|
42
|
+
- 8190516: Enforce child `ext:` tool narrowing and `ask_tools:` consent through Pi's public `tool_call` event, including nested codemode/MCP `ctx.executeTool()` calls that bypassed the model-only hook. The policy runs before other extensions can mutate the call event and checks the effective registered tool source, so same-name tools from unselected extensions cannot borrow a selected tool's permission. Pi 0.99 children now load the SDK's public codemode, tool-search, and MCP built-in factories subject to `extensions:`, `exclude_extensions:`, and `ext:` selectors; older hosts remain supported.
|
|
43
|
+
- 8190516: Resolve full provider-qualified partial model patterns before treating a colon as a thinking suffix, matching Pi's enabled-model scope selection for model IDs containing colons.
|
|
44
|
+
- 8190516: Extend the tested Pi host compatibility range through 0.87.x while retaining supported older hosts. Validate the published extensions against Pi 0.87.1 and adapt changed provider, session-context, and lifecycle contracts where necessary.
|
|
45
|
+
- 8190516: Add Pi 0.99.1 to the supported host peer ranges while retaining Pi 0.84–0.87 compatibility. Align Pi development dependency pins with 0.99.1 for validation against the new host.
|
|
46
|
+
- 8190516: Resolve restrictive subagent extension policies before Pi loads extension factories, so an excluded discovered extension cannot replace a selected Pi 0.99 built-in. Preserve host-disabled built-ins and explicit source priority; report missing sources or changed settings instead of silently dropping a requested extension.
|
|
47
|
+
- 8190516: Maintain a projection-aware parent-context formatter that respects persisted context-edit omissions, replacements, and compaction boundaries; older hosts use resolved session context instead of raw pre-compaction branch entries. The formatter is not dispatched by the current child runtime because Pi does not expose request-hook-redacted context or an atomically pinned child destination; automatic inheritance now fails closed.
|
|
48
|
+
- 8190516: Resolve enabled model scope against the current registry on every check instead of reusing an allowed set across projects or availability changes. Honor Pi's bare names, glob patterns, and thinking-suffixed model references in addition to exact IDs, including its provider-qualified partial matching and thinking-suffixed glob precedence. Malformed entries cannot erase a configured project scope, and a list with no available matches now refuses caller-supplied model choices instead of disabling the opt-in scope guard; pinned and inherited models retain their warning policy.
|
|
49
|
+
- 8190516: Retain Pi's tree-navigation attempt signal for branch ownership and deduplication. Active child or workflow work now causes a non-destructive preflight veto rather than pre-veto quiescence; accepted tree changes fence late old-branch callbacks. A second attempt rechecks live work instead of reusing state from a canceled attempt. Pi still lacks an atomic post-veto, pre-leaf commit hook: work started during asynchronous summarization may need to be quarantined on commit.
|
|
50
|
+
- 8190516: Keep model-assisted agent mentions on Pi 0.84–0.99 without silently exposing the parent's raw, pre-hook conversation to the hidden provider turn. The throwaway session now sees only the current mention text, a static narrow system prompt, and its single Agent tool; it receives no parent history, system prompt, other tools, or summaries. Pi's public extension context cannot replay programmatic parent privacy hooks, so partial hook copying is not a safe fix. Pin the Agent call to the mentioned type and report success only after background spawn acknowledgement. Fence asynchronous dispatch to its originating session; a policy refusal cannot be retried through direct spawning, and a direct infrastructure fallback revalidates that the selected agent type remains enabled. Explicit child `inherit_context: true` is refused under the current host APIs rather than forwarding unredacted parent history; pass a reviewed, sanitized task summary instead.
|
|
51
|
+
- 8190516: Block session switches and tree navigation while subagents, workflows, or provider cleanup remain unsettled, without stopping work on a cancellable attempt. Confirmed shutdown coordinates workflow-owned cleanup before retiring the subagent RPC responder; confirmed tree changes fence stale callbacks and keep old terminal facts off the new branch. Pi's hook ordering still leaves a narrow, non-atomic interval between preflight and commit, so new work during summarization is quarantined on commit rather than guaranteed to finish.
|
|
52
|
+
- Updated dependencies [8190516]
|
|
53
|
+
- Updated dependencies [8190516]
|
|
54
|
+
- @signalridge/pi-ui@1.3.2
|
|
55
|
+
|
|
3
56
|
## 1.10.3
|
|
4
57
|
### Patch Changes
|
|
5
58
|
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ A [pi](https://pi.dev) extension that brings **Claude Code-style autonomous sub-
|
|
|
19
19
|
- **Graceful turn limits** — agents get a "wrap up" warning before hard abort, producing clean partial results instead of cut-off output
|
|
20
20
|
- **Case-insensitive agent types** — `"explore"`, `"Explore"`, `"EXPLORE"` all work. Unknown types fall back to general-purpose with a note
|
|
21
21
|
- **Fuzzy model selection** — specify models by name (`"haiku"`, `"sonnet"`) instead of full IDs, with automatic filtering to only available/configured models
|
|
22
|
-
- **Context
|
|
22
|
+
- **Context boundary** — raw `inherit_context: true` is refused because Pi does not expose the parent's post-hook request or atomically pin the child's physical endpoint; pass a reviewed, sanitized summary in the task instead
|
|
23
23
|
- **Persistent agent memory** — three scopes (project, local, user) with automatic read-only fallback for agents without write tools
|
|
24
24
|
- **Git worktree isolation** — run agents in isolated repo copies; changes auto-committed to branches on completion, with an explicit prompt guard keeping the base checkout off-limits
|
|
25
25
|
- **Skill preloading** — inject named skills into agent system prompts, discovered from `.pi/skills/`, `.agents/skills/`, and global locations (Pi-standard `<name>/SKILL.md` directory layout supported)
|
|
@@ -46,6 +46,8 @@ From the repository root:
|
|
|
46
46
|
pi -e ./packages/pi-subagents
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
**Verified host baseline:** Pi 1.0.4. Wildcard peer dependency ranges and retained capability fallbacks do not establish support for older host versions.
|
|
50
|
+
|
|
49
51
|
## Lint policy
|
|
50
52
|
|
|
51
53
|
Formatter checks remain disabled for this package, while legacy `noExplicitAny`, control-character regex, and empty-interface rules remain off. Signalridge additions follow the package AGENTS rules and the strict `--error-on-warnings` lint gate.
|
|
@@ -65,6 +67,10 @@ Agent({
|
|
|
65
67
|
|
|
66
68
|
Foreground agents block until complete and return results inline. Background agents return an ID immediately and notify you on completion.
|
|
67
69
|
|
|
70
|
+
At a TUI prompt, `@explore find it` starts one background Explore agent. In model mention mode, a hidden, throwaway turn uses **only the current mention text** to draft the task prompt; it does not receive the parent's conversation, system prompt, other tools, compaction summaries, or context hooks. This prevents raw parent history from reaching a separate provider before request-local privacy hooks can redact it. The resulting Agent spawn is still attributed to the parent; raw `inherit_context: true` is refused before the child starts (see below). If the hidden turn cannot start the agent, the extension starts it directly unless the parent runtime itself is unavailable. A pending mention is discarded if you switch sessions or branches before it starts; the off-screen provider turn is cancelled if the switch happens during streaming. An already-started agent is not started twice if a later UI or event update fails.
|
|
71
|
+
|
|
72
|
+
**Pi runtime requirement:** On hosts where child sessions require the parent's `ModelRuntime`, Pi currently exposes it to extensions only through the registry's private `.runtime` property. If that bridge is missing or incompatible, Agent, mentions, and managed spawns refuse before allocating an ID or making a provider request; they do not silently construct a fresh runtime that would lose custom providers, credentials, or virtual routes. Existing managed idempotency keys remain readable. An explicit child model is validated against the parent runtime *after* final model selection, so a stale `ctx.model` alone does not block a valid physical child model. The error advises updating to a host that exposes the parent runtime, or disabling dispatch until one does.
|
|
73
|
+
|
|
68
74
|
### Scheduling
|
|
69
75
|
|
|
70
76
|
Add a `schedule` field to register the agent to fire later instead of running now:
|
|
@@ -227,9 +233,10 @@ All fields are optional — sensible defaults for everything.
|
|
|
227
233
|
| `tools` | all 7 | Which tools the agent can call. Built-in names (`read, grep, …`), `*` / `all` (all built-ins), `none`, and `ext:<extension>` / `ext:<extension>/<tool>` selectors for extension tools. See [Tool & extension scoping](#tool--extension-scoping) below |
|
|
228
234
|
| `extensions` | `true` | Which extensions to load for the agent. `true` (all defaults), `false` (none), or an explicit list: `[mcp, "/abs/path.ts", "*"]`. See [Tool & extension scoping](#tool--extension-scoping) below |
|
|
229
235
|
| `exclude_extensions` | — | Extension denylist applied after `extensions:` — exclude wins. Plain names only (case-insensitive), no paths or `*`. Useful with `extensions: true` to drop one extension (e.g. `pi-notify`) |
|
|
230
|
-
| `skills` | `true` |
|
|
236
|
+
| `skills` | `true` | `true` uses Pi's discovered skills, including skills supplied by loaded extensions; `false` exposes none; a comma-separated list preloads only those named skills into the system prompt (see [Skill Preloading](#skill-preloading) for discovery locations) |
|
|
231
237
|
| `memory` | — | Persistent agent memory scope: `project`, `local`, or `user`. Auto-detects read-only agents |
|
|
232
238
|
| `disallowed_tools` | — | Comma-separated tools to deny even if extensions provide them |
|
|
239
|
+
| `ask_tools` | — | Comma-separated tools whose first call requires human approval. Approval permits later calls to that tool across resumed turns of the current in-memory child. It is not persisted: reopening an evicted child from disk or restarting Pi asks again. Without an interactive approver, calls are blocked |
|
|
233
240
|
| `isolation` | — | Set to `worktree` to run in an isolated git worktree |
|
|
234
241
|
| `tier` | none | This agent's default model tier, by name, from `agentTiers.profiles`. A tier passed at the call site overrides it. When set, it wins over `model`/`thinking` below — see [Model tiers](#model-tiers) |
|
|
235
242
|
| ~~`model`~~ | — | **Removed.** An agent no longer chooses its own model; use `tier`. A file that still has it loads normally, with a warning naming it — the line simply has no effect |
|
|
@@ -240,14 +247,16 @@ All fields are optional — sensible defaults for everything.
|
|
|
240
247
|
| `session_dir` | pi default | Optional session directory when `persist_session: true`; omitted uses pi's normal session location, and relative paths resolve from the agent cwd |
|
|
241
248
|
| `allowed_subagents` | none | Opt in to scoped nested `Agent`, `get_subagent_result`, and `steer_subagent` tools. Omitted / empty / `none` / `false` = no nesting; `all` (or `"*"` / `true`) = any enabled agent; comma-separated list = only those agent types |
|
|
242
249
|
| `prompt_mode` | `replace` | `replace`: body is the full system prompt (no AGENTS.md / CLAUDE.md inheritance). `append`: body appended to parent's prompt (agent acts as a "parent twin" — inherits parent's AGENTS.md / CLAUDE.md) |
|
|
243
|
-
| `inherit_context` | `false` |
|
|
250
|
+
| `inherit_context` | `false` | `true` is refused: the host cannot safely transfer post-hook parent context or pin the child's physical provider endpoint |
|
|
244
251
|
| `run_in_background` | `false` | Run in background by default |
|
|
245
252
|
| `isolated` | `false` | Hermetic specialist mode: forces `extensions: false` + `skills: false` + drops `ext:` selectors. Only built-in tools. Distinct from `isolation: worktree` (filesystem) |
|
|
246
253
|
| `enabled` | `true` | Set to `false` to disable an agent (useful for hiding a default agent per-project) |
|
|
247
254
|
|
|
248
255
|
Frontmatter is authoritative. If an agent file sets `model`, `thinking`, `max_turns`, `inherit_context`, `run_in_background`, `isolated`, or `isolation`, those values are locked for that agent. `Agent` tool parameters only fill fields the agent config leaves unspecified.
|
|
249
256
|
|
|
250
|
-
|
|
257
|
+
`inherit_context: true` is rejected before child creation, for top-level, nested, mention-started, and managed agents. A parent's session projection is **pre-hook**: Pi does not expose the post-`context` / `context_with_system` result to this extension, and a child cannot atomically pin the same physical endpoint across virtual/provider routes. A `raw_for` confirmation would not fix that request-time auth/routing race. Pass a deliberately sanitized summary in the Agent task prompt instead; if context must be edited in the parent session, persist a `context_edit` first and still pass only the summary you intend to disclose. Normal calls with `inherit_context: false` are unchanged. Older persisted child sessions containing the inherited-history prompt cannot resume or switch models safely; start a new child session with a sanitized task rather than reopening them.
|
|
258
|
+
|
|
259
|
+
**Forgiving `model:` resolution.** A `model:` pin is matched against pi's model registry tolerantly, so cosmetic id variations don't silently drop the agent back to the parent's model: `.` and `-` are treated as equivalent in version numbers (`claude-haiku-4.5` ≡ `claude-haiku-4-5`), a trailing `-YYYYMMDD` date stamp is optional (`anthropic/claude-haiku-4-5-20251001` matches an undated registry id and vice-versa), and a `provider/modelId` whose named provider doesn't carry that model retries the bare id against every provider. Precedence is **exact → fuzzy under the named provider → same model under any provider → unavailable**, so an exact match always wins and dated snapshots aren't conflated. If nothing resolves, the pin can't run and the agent inherits the parent model — `/agents → Agent types` flags this case as `(unavailable, fallback: inherit)` and shows the resolved target `(→ provider/id)` when resolution lands on a different provider or version than configured. (This is distinct from [Model Scope](#model-scope) enforcement, which resolves pi's configured `enabledModels` patterns.)
|
|
251
260
|
|
|
252
261
|
### Nested subagents
|
|
253
262
|
|
|
@@ -271,6 +280,16 @@ Nested children don't occupy `maxConcurrent` slots — their parent already hold
|
|
|
271
280
|
|
|
272
281
|
Because a subagent session never activates this extension (that is what keeps a child from building a second agent manager, and it is why nested tools are injected directly instead), a subagent also gets none of the extension's other surfaces: no `/agents` command, no cross-extension RPC handlers, no `subagents:ready` event.
|
|
273
282
|
|
|
283
|
+
### Child project trust
|
|
284
|
+
|
|
285
|
+
Root startup and reload also gate this package's project `subagents.json`, `.pi/agents`, `.agents/agents`, and custom tool description on the public project-trust decision. Unknown or throwing trust checks skip project configuration before parsing; global definitions/settings and embedded defaults remain available. Global aliases into denied roots are not treated as global authority. The runner rejects a denied project-owned definition (including the actual fallback definition) before package resolution, even when it requests an extension outside the project or in a global package cache.
|
|
286
|
+
|
|
287
|
+
On Pi 1.0.4, children capture the parent's public `isProjectTrusted()` decision and configuration root before resolving child resources. Missing or failed trust checks mean denial; children never prompt for or grant project trust. The child passes its derived trust explicitly to `SettingsManager`, governing project settings merging, resource/package discovery, and project MCP configuration. Denied project settings and MCP configuration are not merged. Before the SDK reads native global `settings.json` or `mcp.json`, the child checks their lexical and canonical sources against every retained denied root. A global file or agent-directory alias into a denied root fails child admission, even with extensions disabled; genuine global files and aliases into an approved parent remain usable.
|
|
288
|
+
|
|
289
|
+
Trust belongs to the **configuration root**, not the tool execution directory. Execution in a worktree with `configCwd` pinned to the trusted parent retains that parent's resources (including MCP configuration), also for nested children. A different configuration root, including a newly copied worktree, is not implicitly trusted. Resource checks retain **all denied ancestor roots**, including the captured parent root whose trust was denied and the child's untrusted `configCwd`. Lexical and canonical path checks apply to explicit, configured, and discovered extension paths before their factories execute, and to discovered/preloaded skills before instruction injection. Changing `cwd`/`configCwd`, naming an explicit parent extension, or using a symlink/package alias cannot re-admit the denied source. Named skill preloading also checks global-root ancestor aliases against all retained denied roots.
|
|
290
|
+
|
|
291
|
+
Genuine user/global resources outside denied roots remain available subject to the child's normal policy. This is a resource-source and instruction-loading boundary, **not an OS sandbox**: arbitrary allowed global extension code can perform its own I/O or register MCP servers; project trust does not contain that code's behavior. `isolated`, `extensions: false`, named extension selection, and skill restrictions still narrow access independently. Project context files and appended prompts remain suppressed in child resource loading; append-mode agents receive only the parent's already-built system prompt.
|
|
292
|
+
|
|
274
293
|
### Tool & extension scoping
|
|
275
294
|
|
|
276
295
|
`extensions:` decides **which extensions load**, `tools:` decides **which tools surface to the LLM**. They compose:
|
|
@@ -301,11 +320,13 @@ A few rules the examples don't make obvious:
|
|
|
301
320
|
- `extensions:` is the sole loading authority. `ext:foo` in `tools:` narrows what surfaces; it can't load `foo` on its own. Mismatches fire `extension-error:…` warnings.
|
|
302
321
|
- Any `ext:` entry flips extension tools to an explicit allowlist — unnamed extensions still load (handlers fire) but expose no tools. So `tools: "*, ext:mcp/search"` exposes only `search` from `mcp`, nothing from any other extension.
|
|
303
322
|
- Extension names match case-insensitively (`[Mcp]` = `[mcp]`); tool names in `ext:foo/bar` stay case-sensitive.
|
|
304
|
-
- Extensions that register tools **lazily** work too. MCP-backed extensions typically can't enumerate their tools until their servers connect, so they register from `session_start` or `before_agent_start` rather than at load. Subagent scoping is re-derived as tools appear,
|
|
323
|
+
- Extensions that register tools **lazily** work too. MCP-backed extensions typically can't enumerate their tools until their servers connect, so they register from `session_start` or `before_agent_start` rather than at load. Subagent scoping is re-derived as tools appear, including under `ext:` selectors. Scoping does not override Pi's own exposure: `deferred`/`codemode` tools and `defaultActive: false` tools are not activated for the model, and tools deliberately deactivated during a session stay inactive on later turns. Pi may still allow nested `ctx.executeTool()` calls to deferred/codemode tools, but the child's selector and `ask_tools:` approval gate both model-issued and nested calls.
|
|
324
|
+
- A configured `defaultToolTimeoutMs` bounds the public `tool_call` policy and each selected extension handler using one deadline per call, including nested calls and handlers registered later with `pi.on`. On expiry the call is blocked; a Promise race returns control but **cannot cancel side effects** of a misbehaving hook that keeps running. Interactive `ask_tools:` confirmation is human decision time, not tool execution time: the deadline starts after approval. The separate execution timeout can still abort a tool whose `execute()` hangs.
|
|
325
|
+
- When the SDK exports public codemode, tool-search, and MCP factories (as on the verified Pi 1.0.4 baseline), they are included as built-in extensions in children. `extensions: false` omits them; `extensions: [mcp]` selects MCP; `exclude_extensions: mcp` removes it; and `tools: ext:codemode`, `ext:tool-search`, or `ext:mcp/<tool>` narrows their tools like any other extension. Capability checks omit factories that the host does not export; this fallback is not an older-host support guarantee. For restrictive `extensions:` or `exclude_extensions:` policies, the child uses Pi's public package resolver to select discovered paths **before** loading factories. Thus an excluded extension cannot register a conflicting `/mcp` command and displace a selected replaceable built-in before filtering. Resolution can perform configured package installs, as Pi's normal loader does, and a selected extension that fails to load is reported rather than silently dropped.
|
|
305
326
|
- An installed **package** extension matches by its package short name (`@scope/pi-subagents` → `[pi-subagents]`), in addition to its path-derived name (a package whose entry is `src/index.ts` also answers to `[src]`). Prefer the package name — the path-derived one is incidental.
|
|
306
327
|
- Plain `tools:` typos fail loudly: `tools: reed, grep` fires `tools-error:…` instead of silently producing an under-tooled agent.
|
|
307
328
|
- `exclude_extensions:` wins over `extensions:` and over `ext:` selectors — an excluded extension never loads and a `tools: ext:` entry can't pull it back. Plain names only (no paths, no `*`); a name matching nothing fires an `extension-error:…` warning.
|
|
308
|
-
- `exclude_extensions:` is **not a sandbox**:
|
|
329
|
+
- `exclude_extensions:` is **not a sandbox**: when the built-in factory capability enables preselection, the restrictive prefilter prevents excluded discovered factories from loading into the child, but resolving configured packages can still install code. The retained fallback without that capability filters post-load and can run an excluded factory before suppressing its tools and bound lifecycle hooks; a factory that subscribes directly to the shared `pi.events` bus may remain live. Don't rely on extension selection to contain untrusted packages.
|
|
309
330
|
- Array and string forms are equivalent: `[a, b]` == `"a, b"`.
|
|
310
331
|
|
|
311
332
|
## Tools
|
|
@@ -325,7 +346,7 @@ Launch a sub-agent.
|
|
|
325
346
|
| `resume` | string | no | Agent ID to resume a previous session |
|
|
326
347
|
| `isolated` | boolean | no | No extension/MCP tools |
|
|
327
348
|
| `isolation` | `"worktree"` | no | Run in an isolated git worktree |
|
|
328
|
-
| `inherit_context` | boolean | no |
|
|
349
|
+
| `inherit_context` | boolean | no | Defaults to `false`; `true` is refused before child dispatch. Put a sanitized summary in `prompt` instead |
|
|
329
350
|
|
|
330
351
|
### `get_subagent_result`
|
|
331
352
|
|
|
@@ -658,7 +679,7 @@ When on, each subagent spawn's effective model is validated against pi's own `en
|
|
|
658
679
|
|
|
659
680
|
**Nested spawns** ([nested subagents](#nested-subagents)) apply the same table against the parent's config root. The hard-error case is identical; the warning cases proceed silently, since a subagent session has no UI to toast to.
|
|
660
681
|
|
|
661
|
-
**Pattern format:**
|
|
682
|
+
**Pattern format:** pi's `enabledModels` entries may use exact `provider/modelId` references, bare IDs or partial names, case-insensitive globs such as `anthropic/*` and `*sonnet*`, and optional `:thinking` suffixes. Non-glob partial names match model IDs or display names, not provider-qualified `provider/partial` references; a glob's thinking suffix is removed before matching, so `custom/*:high` also includes colon-suffixed model IDs. This guard checks only model membership, not the configured thinking level. Malformed non-string entries are ignored without erasing valid entries or disabling a configured project scope. If a configured list resolves to no available models, caller-supplied model choices are refused rather than letting the scope check silently pass; pinned and inherited models retain the warning-and-proceed behavior above.
|
|
662
683
|
|
|
663
684
|
**No-op safety:** if `enabledModels` is missing or empty in pi's settings, scope check skips entirely — no false positives, no spurious errors.
|
|
664
685
|
|
|
@@ -795,7 +816,7 @@ Workflow-owned orchestration uses the `subagents:rpc:spawn-managed` channel. Its
|
|
|
795
816
|
}
|
|
796
817
|
```
|
|
797
818
|
|
|
798
|
-
The manager validates and resolves the tier, agent configuration, queue, tool, session, and worktree policy against its own Agent-tier catalogue and model scope. The resolved tier and its snapshot are retained on the managed invocation/tombstone. `spawnKey` is idempotent within a root manager; the same normalized request returns the existing agent id and a conflicting request is rejected. A named managed `thread` re-enters one sequential session only while its effective model, thinking, toolset, denylist, isolation, and agent policy fingerprint remain unchanged — including the model and thinking its tier currently resolves to, so switching the session model interrupts a thread whose tier inherits it; a policy change or concurrent call is rejected rather than silently reusing the old session. Managed agents use the normal Agent execution path, queue, FleetView, activity, transcript, compaction, and lifecycle events. Only the automatic main-session completion nudge is suppressed for an owner-scoped record. Managed requests must carry an attempt-scoped owner, and `stop-owned`/`quiesce-owned` fail closed when exact node/generation metadata is missing. During branch replacement, timed-out records are detached and late callbacks are suppressed.
|
|
819
|
+
The manager validates and resolves the tier, agent configuration, queue, tool, session, and worktree policy against its own Agent-tier catalogue and model scope. The resolved tier and its snapshot are retained on the managed invocation/tombstone. `spawnKey` is idempotent within a root manager; the same normalized request returns the existing agent id and a conflicting request is rejected. A named managed `thread` re-enters one sequential session only while its effective model, thinking, toolset, denylist, isolation, and agent policy fingerprint remain unchanged — including the model and thinking its tier currently resolves to, so switching the session model interrupts a thread whose tier inherits it; a policy change or concurrent call is rejected rather than silently reusing the old session. Managed agents use the normal Agent execution path, queue, FleetView, activity, transcript, compaction, and lifecycle events. Only the automatic main-session completion nudge is suppressed for an owner-scoped record. Managed requests must carry an attempt-scoped owner, and `stop-owned`/`quiesce-owned` fail closed when exact node/generation metadata is missing. During branch replacement, timed-out records are detached and late callbacks are suppressed. Pre-tree bus notifications carry the host navigation attempt's signal so workflow consumers can distinguish retries after a veto; the event payload retains a fallback for an absent signal, without establishing support for older hosts. Active or unsettled child work vetoes a tree-navigation attempt rather than being aborted by that attempt. Pi has no post-veto, pre-leaf commit hook, so work started after preflight during asynchronous summarization can be quarantined at commit but cannot be guaranteed to finish.
|
|
799
820
|
|
|
800
821
|
### Spawn
|
|
801
822
|
|
|
@@ -891,7 +912,7 @@ skills: api-conventions, error-handling
|
|
|
891
912
|
---
|
|
892
913
|
```
|
|
893
914
|
|
|
894
|
-
**Discovery roots** (checked in this order, first match wins):
|
|
915
|
+
**Discovery roots** (checked in this order, first match wins). Both project roots are skipped when the child's configuration project is untrusted or trust is unknown; global named skills still preload unless their lexical or canonical source belongs to either the captured denied parent root or the untrusted child configuration root. Skill names are not explicit paths and cannot bypass that decision:
|
|
895
916
|
|
|
896
917
|
| Scope | Path | Source |
|
|
897
918
|
|---|---|---|
|
|
@@ -943,7 +964,9 @@ src/
|
|
|
943
964
|
output-file.ts # Streaming output file transcripts for agent sessions
|
|
944
965
|
worktree.ts # Git worktree isolation (create, cleanup, prune)
|
|
945
966
|
prompts.ts # Config-driven system prompt builder
|
|
946
|
-
context.ts #
|
|
967
|
+
context.ts # Pure parent-context renderer (not used for child dispatch)
|
|
968
|
+
context-boundary.ts # Raw inheritance and persisted-session guards
|
|
969
|
+
model-runtime-bridge.ts # Checked parent runtime compatibility bridge
|
|
947
970
|
env.ts # Environment detection (git, platform)
|
|
948
971
|
ui/
|
|
949
972
|
agent-display.ts # Shared agent formatting: spinner frames, activity, stats, names
|
|
@@ -24,7 +24,7 @@ If the target is already known, use a direct tool — `read` for a known path, `
|
|
|
24
24
|
- Clearly tell the agent whether you expect it to write code or just to do research (search, file reads, etc.), since it is not aware of the user's intent.
|
|
25
25
|
- If an agent's description says it should be used proactively, try to use it without the user having to ask for it first.
|
|
26
26
|
- Use tier to pick the model profile for this spawn, by name. A tier overrides the agent's own default tier. Model and thinking are not callable parameters — they are what a tier resolves to.
|
|
27
|
-
-
|
|
27
|
+
- Raw inherit_context is unavailable: put only an explicitly sanitized summary in the task prompt, or persist a context_edit before starting a new agent.
|
|
28
28
|
- Use isolation: "worktree" to run the agent in an isolated git worktree (safe parallel file modifications). The worktree is automatically cleaned up if the agent makes no changes; otherwise the path and branch are returned in the result.{{scheduleGuideline}}
|
|
29
29
|
|
|
30
30
|
## Writing the prompt
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signalridge/pi-subagents",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.1",
|
|
4
4
|
"description": "Signalridge's managed subagent runtime with workflow-owned orchestration RPC.",
|
|
5
5
|
"author": "tintinweb and signalridge contributors",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,15 +25,16 @@
|
|
|
25
25
|
"autonomous"
|
|
26
26
|
],
|
|
27
27
|
"peerDependencies": {
|
|
28
|
-
"@earendil-works/pi-ai": "
|
|
29
|
-
"@earendil-works/pi-coding-agent": "
|
|
30
|
-
"@earendil-works/pi-tui": "
|
|
28
|
+
"@earendil-works/pi-ai": "*",
|
|
29
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
30
|
+
"@earendil-works/pi-tui": "*",
|
|
31
|
+
"@sinclair/typebox": "*"
|
|
31
32
|
},
|
|
32
33
|
"dependencies": {
|
|
33
|
-
"@sinclair/typebox": "^0.34.50",
|
|
34
34
|
"croner": "^10.0.1",
|
|
35
|
-
"@signalridge/pi-subagents-protocol": "^1.5.
|
|
36
|
-
"@signalridge/pi-ui": "^1.3.
|
|
35
|
+
"@signalridge/pi-subagents-protocol": "^1.5.1",
|
|
36
|
+
"@signalridge/pi-ui": "^1.3.3",
|
|
37
|
+
"minimatch": "^10.2.6",
|
|
37
38
|
"nanoid": "^5.1.16"
|
|
38
39
|
},
|
|
39
40
|
"scripts": {
|
|
@@ -52,7 +53,7 @@
|
|
|
52
53
|
"@biomejs/biome": "^2.4.14",
|
|
53
54
|
"@types/node": "^25.5.0",
|
|
54
55
|
"typescript": "^6.0.0",
|
|
55
|
-
"vitest": "^4.
|
|
56
|
+
"vitest": "^4.1.11"
|
|
56
57
|
},
|
|
57
58
|
"pi": {
|
|
58
59
|
"extensions": [
|