@narumitw/pi-subagents 0.43.0 → 0.43.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/README.md CHANGED
@@ -18,7 +18,8 @@ Use it to split independent research, planning, implementation, and review work
18
18
  - Loads custom user agents from `~/.pi/agent/agents/*.md`.
19
19
  - Optionally loads project agents from `.pi/agents/*.md` with confirmation.
20
20
  - Provides a current-session-first `/subagents` manager, direct `settings|status|help` routes, and compatibility aliases for agent tools and retained agents.
21
- - Supports per-task `cwd`, hard subprocess `timeoutMs`, task-selected `thinkingLevel`, abort propagation, and streaming progress.
21
+ - Supports trust-aware per-task `cwd` policies, hard subprocess `timeoutMs`, task-selected `thinkingLevel`, abort propagation, and streaming progress.
22
+ - Renders all seven tools with Pi-native compact/expanded transcript rows; long-running blocking and consultation calls show bounded live activity.
22
23
  - Bounds JSON lines, captured messages, stderr, final output, chain substitution, and fan-in context.
23
24
  - Enforces a recursion-depth guard and deterministic process-group termination.
24
25
  - Provides addressable stateful agents with follow-up, consolidated mailbox/management actions, context selection, and persistence.
@@ -63,6 +64,18 @@ The available tools are:
63
64
  - `subagent_inspect` — inspect agent/model/run/runtime metadata without launching work or changing state.
64
65
  - `subagent_consult` — run one ephemeral read-only consultation and wait for its answer.
65
66
 
67
+ ### Interactive tool rows
68
+
69
+ In Pi's interactive TUI, every registered tool uses Pi's native tool shell and theme. Call rows identify the action, agent or retained id, scope, and a bounded task/message preview. Result rows use explicit `Starting`, `Running`, `Completed`, `Failed`, `Cancelled`, `Interrupted`, or `Closed` text in addition to icons and color.
70
+
71
+ Collapsed rows stay scan-friendly: consultation and blocking calls show recent activity while running, completed answers show up to three lines, and list actions show up to five items. Use Pi's configured `app.tools.expand` keybinding (Ctrl+O by default) for the additional bounded task, policy, activity, answer, usage, inspection, or mailbox details available to that tool. The hint follows the user's keybinding rather than assuming Ctrl+O.
72
+
73
+ `subagent_consult` emits an initial starting update before launching its child and then reports the actual provider/model, thinking request, usage, and a safe projection of recent `read`, `grep`, `find`, and `ls` activity. Progress never includes full child messages, prompts, credentials, headers, or environment values. Tool-row previews remove terminal controls and redact private text.
74
+
75
+ `subagent_spawn` remains deliberately detached and non-polling: its tool row ends after returning the new `agentId` and initial retained state. It does not pretend to stream the background child after the tool call has completed; the existing completion message and configured delivery policy report eventual completion.
76
+
77
+ Custom transcript rendering is TUI presentation only. Tool names, parameter schemas, model-facing final content/details, errors, completion delivery, and print/JSON/RPC final output remain unchanged; JSON/RPC observers may see additive bounded consultation partial-progress details.
78
+
66
79
  After each session starts, the descriptions of the registered `subagent`, `subagent_spawn`, and
67
80
  `subagent_consult` tools include the same bounded parent-facing catalog of the agents available in
68
81
  that session. Entries show the source (`built-in`, `user`, or `project`) and the `agentScope` needed to
@@ -92,12 +105,29 @@ Execution modes:
92
105
 
93
106
  Common controls:
94
107
 
95
- - `cwd` — run a job from a different working directory.
108
+ - `cwd` — choose a launch directory subject to the user-owned trust-aware target policy described below.
96
109
  - `timeoutMs` — set a hard subprocess timeout.
97
110
  - `thinkingLevel` — request `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max` thinking for the spawned Pi process or in-process child.
98
111
 
99
112
  For `subagent_spawn`, the root agent selects the lowest thinking level sufficient for the delegated task. This is a tool-argument decision made from the task already in context; `pi-subagents` does not run a string heuristic or an extra classifier model call.
100
113
 
114
+ ## 🔐 Working-directory trust policy
115
+
116
+ Pi records saved project trust in `~/.pi/agent/trust.json`. The closest saved decision for the canonical target or one of its parents wins, so trusting a worktree parent covers worktrees below it while a nearer `false` overrides a trusted parent. `pi-subagents` reads this through Pi's public `ProjectTrustStore`; it never parses, writes, or migrates the file. Open Pi in a folder and use `/trust` to manage trust, then restart Pi before expecting retained-runtime behavior to change.
117
+
118
+ The default target policies are:
119
+
120
+ | Setting | Values | Default behavior |
121
+ | --- | --- | --- |
122
+ | `cwdPolicy.consultation` | `"anywhere"`, `"current-workspace"` | `"anywhere"`: consultation may start in any existing directory, but a target without effective trust is forced to `resources: "none"` |
123
+ | `cwdPolicy.delegation` | `"trusted-targets"`, `"current-workspace"`, `"anywhere"` | `"trusted-targets"`: blocking and detached delegation may target the current workspace or an external folder covered by a saved `true` decision |
124
+
125
+ All paths are resolved relative to the current session workspace and canonicalized before containment and trust checks. Missing paths, non-directories, sibling paths, and symlink escapes cannot bypass the policy. Blocking parallel, chain, and fan-in calls preflight every target before any child starts. A generated `workspaceMode: "worktree"` inherits the resolved trust of its approved base cwd.
126
+
127
+ `"anywhere"` for general delegation restores the previous external-target flexibility. An external target without effective trust starts with `projectTrusted: false`, so Pi-protected project settings, packages, extensions, skills, prompts, and system resources stay disabled. General agents still have their configured tools and ordinary Pi/OS permissions, and Pi may still load `AGENTS.md` or `CLAUDE.md` because those context files are not protected by project trust. Resource-free consultation is stricter: it also passes `--no-context-files`, `--no-skills`, `--no-prompt-templates`, `--no-approve`, and `--no-extensions`.
128
+
129
+ These controls govern child starting directories and automatically loaded resources. They do **not** restrict absolute paths, shell commands, custom tools, network access, extension code, or filesystem access available to the Pi process. For real isolation, run Pi in a container, VM, micro-VM, or OS sandbox with only the required paths and credentials mounted.
130
+
101
131
  ## 🧭 Proactive use
102
132
 
103
133
  When registered, the blocking `subagent` tool advertises only blocking guidance. When stateful lifecycle tools
@@ -190,7 +220,7 @@ A blocking fan-out is reserved for output that must be synthesized before the ro
190
220
  | `list_runs` | Optional `includeClosed` and `limit` (default 50, maximum 100) | Metadata-only retained-run summaries and unread counts |
191
221
  | `get_run` | Required `agentId` | Safe `cwd`, current-task/error summaries, thinking level, policy, history count, and unread count |
192
222
  | `list_models` | Optional `limit` (default 50, maximum 100) | Session-scoped models, or the already-loaded available snapshot |
193
- | `status` | No additional fields | Effective workflow, runtime counts/transport, completion delivery, and consultation-resource setting |
223
+ | `status` | No additional fields | Effective workflow, runtime counts/transport, completion delivery, consultation resources, and configured/runtime cwd policies with per-field sources |
194
224
  | `diagnose` | No additional fields | Structured `pass`, `warning`, and `fail` checks; failed checks are report data rather than a tool error |
195
225
 
196
226
  The schema rejects fields that do not belong to the selected action. Explicit `project` or `both` scope fails before project-agent discovery unless Pi already trusts the project. Run inspection never returns history output, stored context, or mailbox content; unread counts come from a metadata-only snapshot and do not acknowledge messages. Paths beneath the Pi agent directory use `~`, project paths are workspace-relative, model objects are projected through an allow-list, and model-facing text is bounded to 50 KiB or 2,000 lines.
@@ -227,9 +257,11 @@ without launching or charging a child.
227
257
  | `"none"` | Use only the package consultation base, selected agent prompt, and enforced read-only instruction |
228
258
  | `"all"` | Keep ordinarily discoverable trusted context/system/append-system files, skills, and prompt templates |
229
259
 
230
- Extensions remain disabled for all three values. For an untrusted current project, project resources are omitted; because Pi's context-file switch cannot separate user and project `AGENTS.md` files, `project-context` and `all` fail closed by disabling context files while still permitting the user `SYSTEM.md` selected by the extension. The setting is user-owned in `~/.pi/agent/pi-subagents.json`; projects cannot override it. An external or symlink-escaped `cwd` is accepted only with `consult.resources: "none"`, but this is not a path sandbox: read-only tools can still read an explicitly requested accessible absolute path.
260
+ Extensions remain disabled for all three values. A current target uses the session's effective project trust, including session-only or CLI overrides. An external target uses the nearest saved trust decision. For an untrusted, explicitly denied, unsaved, or trust-error target, consultation remains available when `cwdPolicy.consultation` permits it but automatically downgrades to `resources: "none"`. This also disables context files because Pi does not protect `AGENTS.md` and `CLAUDE.md` with project trust alone. A saved-trusted external target uses the configured resource policy and discovers `SYSTEM.md`, `APPEND_SYSTEM.md`, and ordinary child context from that target rather than the parent workspace.
231
261
 
232
- Result details report requested/effective tools and resources, agent/model/thinking/timeout metadata, and the facts that extensions, session persistence, and retained-agent state are disabled. Nested model usage is returned through Pi's usage field, so footer, `/session`, and RPC totals include consultation cost. Validation, trust, unsafe-cwd, and launch failures throw. Failures after model launch preserve bounded partial evidence and usage while the finalized Pi tool result is marked as an error. Abort, timeout, session replacement, and shutdown use the existing process-tree termination and temporary-file cleanup path.
262
+ Both settings are user-owned in `~/.pi/agent/pi-subagents.json`; projects cannot override them. `cwdPolicy.consultation: "current-workspace"` rejects every canonical external target before agent discovery or launch even when that target is saved-trusted. This is not a path sandbox: read-only tools can still read an explicitly requested accessible absolute path.
263
+
264
+ Result details report the canonical safe cwd, current/external boundary, bounded target-trust decision/source/warning, requested and effective tools/resources, downgrade reason, agent/model/thinking/timeout metadata, and the facts that extensions, session persistence, and retained-agent state are disabled. They never dump prompt contents or the full trust store. Nested model usage is returned through Pi's usage field, so footer, `/session`, and RPC totals include consultation cost. Validation, disallowed targets, and launch failures throw. Failures after model launch preserve bounded partial evidence and usage while the finalized Pi tool result is marked as an error. Abort, timeout, session replacement, and shutdown use the existing process-tree termination and temporary-file cleanup path.
233
265
 
234
266
  ## 🚀 Blocking batch examples
235
267
 
@@ -320,12 +352,12 @@ Auto-resume is best-effort because Pi's custom-message API is fire-and-forget. S
320
352
  The default `subprocess` transport preserves compatibility: each turn starts a fresh isolated `pi --mode json -p --no-session` child and receives sanitized, bounded history. Set `transport` to `in-process` to retain one public Pi SDK `AgentSession` per stateful `agentId`, avoiding repeated process startup while preserving native child history in memory.
321
353
 
322
354
  Run `/subagents` in TUI mode to open the standard primary manager. It leads with the current
323
- delegation workflow, human-readable async completion behavior, consultation-resource policy, and active/retained counts. **Change delegation**, **Current agents**, and **Settings** cover the common workflows; agent permissions, transport/runtime details, source, and settings path remain under **Advanced settings**.
355
+ delegation workflow, human-readable async completion behavior, consultation/delegation target policies, consultation-resource policy, and active/retained counts. **Change delegation**, **Current agents**, and **Settings** cover the common workflows; agent permissions, transport/runtime details, source, and settings path remain under **Advanced settings**.
324
356
  Escape returns from a nested screen to a newly refreshed manager; Ctrl+C closes the full flow.
325
357
  Exact workflow/reload and project-agent safety confirmations remain extension-owned because they
326
358
  guard live agent and trust-boundary policy rather than ordinary navigation.
327
359
 
328
- The direct routes remain predictable: `/subagents settings` changes completion delivery and consultation resources and applies them immediately, including refreshing model-facing spawn guidance; `/subagents status` reports current-session runtime values separately from configured values, sources, and path; `/subagents help` summarizes the single-command interface. In RPC mode, bare `/subagents` emits the same bounded status through Pi's notification protocol instead of opening a custom TUI. JSON and print modes do not emit ad hoc command output. Manual edits use `~/.pi/agent/pi-subagents.json` and take effect after reloading Pi:
360
+ The direct routes remain predictable: `/subagents settings` changes both target policies, consultation resources, and completion delivery and applies them immediately, including refreshing model-facing tool guidance; `/subagents status` reports current-session runtime values separately from configured values, per-field sources, and path; `/subagents help` summarizes the single-command interface and the non-sandbox limitation. In RPC mode, bare `/subagents` emits the same bounded status through Pi's notification protocol instead of opening a custom TUI. JSON and print modes do not emit ad hoc command output. Manual edits use `~/.pi/agent/pi-subagents.json` and take effect after reloading Pi:
329
361
 
330
362
  ```json
331
363
  {
@@ -346,13 +378,17 @@ The direct routes remain predictable: `/subagents settings` changes completion d
346
378
  "retentionDays": 30,
347
379
  "maxStoredAgents": 50
348
380
  },
381
+ "cwdPolicy": {
382
+ "consultation": "anywhere",
383
+ "delegation": "trusted-targets"
384
+ },
349
385
  "consult": {
350
386
  "resources": "project-context"
351
387
  }
352
388
  }
353
389
  ```
354
390
 
355
- The settings UI patches the raw JSON atomically and preserves unknown fields; it refuses to overwrite malformed or invalid settings. Supported Pi writers serialize the latest-document read and same-directory temporary-file rename through `pi-subagents.json.mutation-lock`. Editors and older extension versions do not participate in that lock, so avoid manual edits while a settings save is in progress. `blocking.enabled` defaults to `true`; set it to `false` for async-only delegation. `stateful.enabled` also defaults to `true`; its existing `false` value remains the blocking-only workflow. `consult.resources` defaults to `"project-context"`; the Settings UI applies a saved change to subsequent consultations immediately, while manual edits take effect on session start or `/reload`. When stateful tools are enabled, their membership stays fixed across spawn, completion, interrupt, close, and mailbox transitions. This avoids lifecycle-driven tool-schema churn and preserves a stable provider prompt prefix for KV caching.
391
+ The settings UI patches the raw JSON atomically and preserves unknown fields; it refuses to overwrite malformed or invalid settings. Supported Pi writers serialize the latest-document read and same-directory temporary-file rename through `pi-subagents.json.mutation-lock`. Editors and older extension versions do not participate in that lock, so avoid manual edits while a settings save is in progress. `blocking.enabled` defaults to `true`; set it to `false` for async-only delegation. `stateful.enabled` also defaults to `true`; its existing `false` value remains the blocking-only workflow. `cwdPolicy.consultation` defaults to `"anywhere"`, `cwdPolicy.delegation` defaults to `"trusted-targets"`, and `consult.resources` defaults to `"project-context"`. The Settings UI applies a saved change immediately to subsequent launches and refreshes the affected tool descriptions; manual edits take effect on session start or `/reload`. The UI explicitly states that target/trust settings are not filesystem sandboxing and directs trust changes to Pi `/trust`. When stateful tools are enabled, their membership stays fixed across spawn, completion, interrupt, close, and mailbox transitions. This avoids lifecycle-driven tool-schema churn and preserves a stable provider prompt prefix for KV caching.
356
392
 
357
393
  | Tool | Purpose |
358
394
  | --- | --- |
@@ -422,7 +458,7 @@ Stateful execution uses a transport boundary:
422
458
 
423
459
  - `subprocess` is the default compatibility and rollback path.
424
460
  - `in-process` uses only public Pi SDK APIs: `createAgentSession()`, `SessionManager.inMemory()`, `DefaultResourceLoader`, and normal session lifecycle methods. It isolates conversation/tool selection, not memory or crashes; child failures share the parent Node.js process.
425
- - Child resource loading sets `noExtensions: true`, preventing recursive `pi-subagents` loading and duplicate extension side effects while retaining normal context/skill resources and the selected agent prompt.
461
+ - Child resource loading sets `noExtensions: true`, preventing recursive `pi-subagents` loading and duplicate extension side effects while retaining trust-eligible context/skill resources and the selected agent prompt. Both transports receive the same resolved target-trust boolean: subprocess children get explicit `--approve`/`--no-approve`, and in-process children set the same `SettingsManager.projectTrusted` value.
426
462
  - Agent model, thinking level, and built-in tool allow-list overrides are applied when the child is created. Parent model/thinking changes are snapshotted for subsequently created children; an existing child keeps its own session configuration.
427
463
  - Extension/custom tool names are rejected in-process with an actionable recommendation to use `subprocess`; permissions are never silently widened.
428
464
  - Timeout, parent abort, close, expiry, and session shutdown abort/dispose owned child sessions. A child that does not settle after abort grace is discarded rather than reused.
@@ -432,11 +468,11 @@ No private Pi imports, runtime casts, or `ExtensionAPI` monkey-patching are used
432
468
 
433
469
  Write-capable agents share the workspace by default. Concurrent write-capable starts in the same cwd are rejected unless `allowConcurrentWrites` is explicitly set. Classification is intentionally conservative: an agent with `bash`, `write`, or `edit` is write-capable even when its task prompt says “read only,” because prompt wording is not a filesystem sandbox. Prefer one detached agent when asynchronous work can be combined. If concurrent work is genuinely required, use the blocking batch only when synchronous outputs justify making the root unavailable, explicitly accept safe detached overlap with `allowConcurrentWrites`, or use isolated worktrees when repository isolation is needed.
434
470
 
435
- Set `workspaceMode: "worktree"` to opt into a disposable detached Git worktree; this requires a clean repository and the worktree is removed on close or session shutdown. Isolated worktree agents are intentionally not restored after shutdown.
471
+ Set `workspaceMode: "worktree"` to opt into a disposable detached Git worktree; this requires a clean repository and the worktree is removed on close or session shutdown. The generated path inherits the approved base cwd's trust snapshot. Retained records mark disposable worktrees explicitly, so they are never restored even if cleanup could not remove the generated directory. Shared-workspace retained records store an additive bounded target-trust snapshot for transport and inspection parity; session restore canonicalizes the retained cwd and re-resolves current/saved trust rather than blindly trusting the persisted value. Older records without either field remain readable.
436
472
 
437
473
  ## 📜 Compatibility and failure contract
438
474
 
439
- Existing `subagent` requests remain unchanged:
475
+ Existing `subagent` input schemas remain unchanged. The intentional compatibility change is that an external target without saved trust is rejected by the new default `cwdPolicy.delegation: "trusted-targets"`; set the user-owned policy to `"anywhere"` to restore the preceding target flexibility.
440
476
 
441
477
  | Mode | Ordering | Failure behavior |
442
478
  | --- | --- | --- |
@@ -448,7 +484,7 @@ Existing `subagent` requests remain unchanged:
448
484
  An aggregator whose `agent` or `task` is empty or whitespace-only is treated as absent, so successful
449
485
  parallel outputs remain available instead of being replaced by a malformed fan-in failure.
450
486
 
451
- Timeout precedence remains: task/step/aggregator → call → agent setting → `PI_SUBAGENT_TIMEOUT_MS` → 600000 ms. Blocking thinking precedence remains: task/step/aggregator → call → agent setting → child default. Stateful spawn thinking precedence is: `subagent_spawn.thinkingLevel` → agent setting → transport fallback. Project-agent resolution and confirmation behavior is unchanged.
487
+ Timeout precedence remains: task/step/aggregator → call → agent setting → `PI_SUBAGENT_TIMEOUT_MS` → 600000 ms. Blocking thinking precedence remains: task/step/aggregator → call → agent setting → child default. Stateful spawn thinking precedence is: `subagent_spawn.thinkingLevel` → agent setting → transport fallback. Project-agent resolution and confirmation behavior is unchanged after target preflight. Blocking and retained result/inspection details add bounded target boundary and effective trust metadata.
452
488
 
453
489
  ## 🤖 Built-in agents
454
490
 
@@ -616,8 +652,12 @@ extensions/pi-subagents/
616
652
  │ ├── inspect.ts # Side-effect-free metadata inspection tool
617
653
  │ ├── consult.ts # Synchronous read-only consultation tool
618
654
  │ ├── consult-policy.ts # Enforced read-only tool intersection
655
+ │ ├── cwd-policy.ts # Canonical target and saved-trust resolution
619
656
  │ ├── safe-text.ts # Shared byte/line/path sanitization
620
657
  │ ├── stateful.ts # Detached lifecycle registration and dispatch
658
+ │ ├── stateful-guidance.ts # Detached model-facing workflow guidance
659
+ │ ├── stateful-lifecycle.ts # Runtime disposal and spawn ownership guards
660
+ │ ├── stateful-safety.ts # Project-agent and shared-write safety checks
621
661
  │ ├── stateful-tool-params.ts # Consolidated action schemas and validation
622
662
  │ └── *.ts # Package-local discovery, execution, rendering, and settings modules
623
663
  ├── README.md
@@ -626,7 +666,7 @@ extensions/pi-subagents/
626
666
  └── package.json
627
667
  ```
628
668
 
629
- `index.ts` is the Pi entrypoint and forwards to `subagents.ts`; the other source modules are internal. Workflow settings remain backward compatible: older files without `blocking.enabled` receive the seven-tool default, existing `stateful.enabled: false` files expose blocking delegation plus inspection/consultation, and older package releases ignore and preserve the optional `consult` object. The package exposes its Pi extension through `package.json`:
669
+ `index.ts` is the Pi entrypoint and forwards to `subagents.ts`; the other source modules are internal. Workflow settings remain backward compatible: older files without `blocking.enabled` receive the seven-tool default, existing `stateful.enabled: false` files expose blocking delegation plus inspection/consultation, and older package releases ignore and preserve the optional `consult` and `cwdPolicy` objects. The package exposes its Pi extension through `package.json`:
630
670
 
631
671
  ```json
632
672
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narumitw/pi-subagents",
3
- "version": "0.43.0",
3
+ "version": "0.43.1",
4
4
  "description": "Pi extension for delegating work to specialized isolated subagents.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/agents.ts CHANGED
@@ -49,6 +49,21 @@ export interface SubagentConsultSettings {
49
49
  resources?: ConsultResourcePolicy;
50
50
  }
51
51
 
52
+ export const CONSULTATION_CWD_POLICIES = ["anywhere", "current-workspace"] as const;
53
+ export type ConsultationCwdPolicy = (typeof CONSULTATION_CWD_POLICIES)[number];
54
+
55
+ export const DELEGATION_CWD_POLICIES = [
56
+ "trusted-targets",
57
+ "current-workspace",
58
+ "anywhere",
59
+ ] as const;
60
+ export type DelegationCwdPolicy = (typeof DELEGATION_CWD_POLICIES)[number];
61
+
62
+ export interface SubagentCwdPolicySettings {
63
+ consultation?: ConsultationCwdPolicy;
64
+ delegation?: DelegationCwdPolicy;
65
+ }
66
+
52
67
  export interface SubagentBlockingSettings {
53
68
  enabled?: boolean;
54
69
  }
@@ -73,6 +88,7 @@ export interface SubagentSettings {
73
88
  blocking?: SubagentBlockingSettings;
74
89
  stateful?: SubagentRuntimeSettings;
75
90
  consult?: SubagentConsultSettings;
91
+ cwdPolicy?: SubagentCwdPolicySettings;
76
92
  }
77
93
 
78
94
  const BUILT_IN_AGENTS: AgentConfig[] = [
package/src/config-ui.ts CHANGED
@@ -1,12 +1,19 @@
1
1
  import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
2
2
  import { defineMenu, runMenu } from "@narumitw/pi-tui-kit";
3
- import { type CompletionDelivery, type ConsultResourcePolicy, discoverAgents } from "./agents.js";
3
+ import {
4
+ type CompletionDelivery,
5
+ type ConsultationCwdPolicy,
6
+ type ConsultResourcePolicy,
7
+ type DelegationCwdPolicy,
8
+ discoverAgents,
9
+ } from "./agents.js";
4
10
  import type { ManagedAgent } from "./registry.js";
5
11
  import {
6
12
  type DelegationWorkflow,
7
13
  hasOwn,
8
14
  inspectCompletionDeliverySettings,
9
15
  inspectConsultResourceSettings,
16
+ inspectCwdPolicySettings,
10
17
  inspectDelegationWorkflowSettings,
11
18
  readSubagentSettings,
12
19
  sameToolSet,
@@ -14,6 +21,7 @@ import {
14
21
  updateAgentToolsSetting,
15
22
  updateCompletionDeliverySetting,
16
23
  updateConsultResourceSetting,
24
+ updateCwdPolicySetting,
17
25
  updateDelegationWorkflowSetting,
18
26
  } from "./settings.js";
19
27
  import { formatStatefulAgentLine, type StatefulSubagentRuntimeStatus } from "./stateful.js";
@@ -29,8 +37,12 @@ export interface SubagentSettingsRuntime {
29
37
  getBlockingEnabled(): boolean;
30
38
  getCompletionDelivery(): CompletionDelivery;
31
39
  getConsultResourcePolicy(): ConsultResourcePolicy;
40
+ getConsultationCwdPolicy(): ConsultationCwdPolicy;
41
+ getDelegationCwdPolicy(): DelegationCwdPolicy;
32
42
  setCompletionDelivery(value: CompletionDelivery): void;
33
43
  setConsultResourcePolicy(value: ConsultResourcePolicy): void;
44
+ setConsultationCwdPolicy(value: ConsultationCwdPolicy): void;
45
+ setDelegationCwdPolicy(value: DelegationCwdPolicy): void;
34
46
  getRuntimeStatus(): StatefulSubagentRuntimeStatus;
35
47
  listAgents(includeClosed?: boolean): ManagedAgent[];
36
48
  clearAgents(): Promise<number>;
@@ -90,7 +102,7 @@ function registerSubagentPrimaryCommand(
90
102
  showSubagentStatus(ctx, runtime);
91
103
  return;
92
104
  case "help":
93
- showSubagentHelp(ctx);
105
+ showSubagentHelp(ctx, runtime);
94
106
  return;
95
107
  default:
96
108
  if (ctx.mode === "tui" || ctx.hasUI) {
@@ -129,6 +141,8 @@ async function showSubagentManager(
129
141
  | "clear-agents"
130
142
  | "set-completion"
131
143
  | "set-consult-resources"
144
+ | "set-consultation-cwd"
145
+ | "set-delegation-cwd"
132
146
  | "load-agent-picker"
133
147
  | "pick-agent"
134
148
  | "toggle-tool"
@@ -161,7 +175,7 @@ async function showSubagentManager(
161
175
  {
162
176
  id: "settings",
163
177
  label: "Settings",
164
- description: "Configure async completion and read-only consultation resources",
178
+ description: "Configure targets, trusted resources, and async completion",
165
179
  to: "settings",
166
180
  },
167
181
  {
@@ -271,7 +285,7 @@ async function showSubagentManager(
271
285
  help: () => ({
272
286
  kind: "detail",
273
287
  title: "Subagents help",
274
- lines: helpLines(),
288
+ lines: helpLines(runtime),
275
289
  hint: "back",
276
290
  }),
277
291
  "agent-picker": () => {
@@ -395,6 +409,8 @@ async function showSubagentManager(
395
409
  "set-completion": async ({ value }) => applyCompletionSetting(value, ctx, runtime),
396
410
  "set-consult-resources": async ({ value }) =>
397
411
  applyConsultResourceSetting(value, ctx, runtime),
412
+ "set-consultation-cwd": async ({ value }) => applyConsultationCwdSetting(value, ctx, runtime),
413
+ "set-delegation-cwd": async ({ value }) => applyDelegationCwdSetting(value, ctx, runtime),
398
414
  "load-agent-picker": async () => {
399
415
  availableAgents = discoverAgents(ctx.cwd, "user", readSubagentSettings() ?? {}).agents;
400
416
  if (availableAgents.length === 0) {
@@ -486,7 +502,11 @@ async function showSubagentSettings(
486
502
  return;
487
503
  }
488
504
  const generation = owner.generation;
489
- type SettingsAction = "set-completion" | "set-consult-resources";
505
+ type SettingsAction =
506
+ | "set-completion"
507
+ | "set-consult-resources"
508
+ | "set-consultation-cwd"
509
+ | "set-delegation-cwd";
490
510
  const menu = defineMenu<undefined, "settings", SettingsAction, ExtensionCommandContext>({
491
511
  start: "settings",
492
512
  screens: { settings: () => subagentSettingsScreen(runtime) },
@@ -494,6 +514,8 @@ async function showSubagentSettings(
494
514
  "set-completion": async ({ value }) => applyCompletionSetting(value, ctx, runtime),
495
515
  "set-consult-resources": async ({ value }) =>
496
516
  applyConsultResourceSetting(value, ctx, runtime),
517
+ "set-consultation-cwd": async ({ value }) => applyConsultationCwdSetting(value, ctx, runtime),
518
+ "set-delegation-cwd": async ({ value }) => applyDelegationCwdSetting(value, ctx, runtime),
497
519
  },
498
520
  });
499
521
  await runMenu(ctx, menu, {
@@ -506,12 +528,15 @@ async function showSubagentSettings(
506
528
  function subagentSettingsScreen(runtime: SubagentSettingsRuntime) {
507
529
  const completion = inspectCompletionDeliverySettings();
508
530
  const consult = inspectConsultResourceSettings();
509
- const error = completion.error ?? consult.error;
531
+ const cwdPolicy = inspectCwdPolicySettings();
532
+ const error = completion.error ?? consult.error ?? cwdPolicy.error;
510
533
  return {
511
534
  kind: "settings" as const,
512
535
  title: error ? "Subagent User Settings · Read only" : "Subagent User Settings",
513
536
  lines: [
514
537
  "Applies now and to future sessions",
538
+ "Target and trust settings control startup resources, not filesystem access or sandboxing.",
539
+ "Manage folder trust with Pi /trust; restart Pi after changing it.",
515
540
  safeTerminalText(consult.path),
516
541
  ...(error ? [`Settings cannot be edited: ${safeTerminalText(error)}`] : []),
517
542
  ],
@@ -519,23 +544,45 @@ function subagentSettingsScreen(runtime: SubagentSettingsRuntime) {
519
544
  ? []
520
545
  : [
521
546
  {
522
- id: "completionDelivery",
523
- label: "When async work finishes",
547
+ id: "consultationCwd",
548
+ label: "Read-only consultation target",
524
549
  description:
525
- "Wait for your next turn, or request one synthesis turn after the root settles.",
526
- currentValue: completionLabel(runtime.getCompletionDelivery()),
527
- values: ["Wait until my next turn", "Resume automatically when finished"],
528
- action: "set-completion" as const,
550
+ "Untrusted external targets inherit no target/project resources; agent and package read-only prompts remain.",
551
+ currentValue: consultationCwdLabel(runtime.getConsultationCwdPolicy()),
552
+ values: ["Anywhere · untrusted targets inherit nothing", "Current workspace only"],
553
+ action: "set-consultation-cwd" as const,
554
+ },
555
+ {
556
+ id: "delegationCwd",
557
+ label: "General delegation target",
558
+ description:
559
+ "Controls starting directories, not absolute paths, shell commands, or OS permissions.",
560
+ currentValue: delegationCwdLabel(runtime.getDelegationCwdPolicy()),
561
+ values: [
562
+ "Current or saved-trusted folders",
563
+ "Current workspace only",
564
+ "Anywhere · normal Pi permissions",
565
+ ],
566
+ action: "set-delegation-cwd" as const,
529
567
  },
530
568
  {
531
569
  id: "consultResources",
532
- label: "Read-only consultation resources",
570
+ label: "Consultation resources for trusted targets",
533
571
  description:
534
572
  "Choose which trusted context, system, skill, and prompt resources a consultation inherits.",
535
573
  currentValue: consultResourceLabel(runtime.getConsultResourcePolicy()),
536
574
  values: ["Project context only", "No inherited resources", "All trusted resources"],
537
575
  action: "set-consult-resources" as const,
538
576
  },
577
+ {
578
+ id: "completionDelivery",
579
+ label: "When async work finishes",
580
+ description:
581
+ "Wait for your next turn, or request one synthesis turn after the root settles.",
582
+ currentValue: completionLabel(runtime.getCompletionDelivery()),
583
+ values: ["Wait until my next turn", "Resume automatically when finished"],
584
+ action: "set-completion" as const,
585
+ },
539
586
  ],
540
587
  };
541
588
  }
@@ -584,6 +631,50 @@ function applyConsultResourceSetting(
584
631
  }
585
632
  }
586
633
 
634
+ function applyConsultationCwdSetting(
635
+ value: string | undefined,
636
+ ctx: ExtensionCommandContext,
637
+ runtime: SubagentSettingsRuntime,
638
+ ) {
639
+ const previous = runtime.getConsultationCwdPolicy();
640
+ const next: ConsultationCwdPolicy =
641
+ value === "Current workspace only" ? "current-workspace" : "anywhere";
642
+ if (next === previous) return { kind: "stay" as const };
643
+ try {
644
+ updateCwdPolicySetting("consultation", next);
645
+ runtime.setConsultationCwdPolicy(next);
646
+ ctx.ui.notify(`Saved and applied: ${consultationCwdLabel(next)}.`, "info");
647
+ return { kind: "stay" as const };
648
+ } catch (error) {
649
+ ctx.ui.notify(`Subagent settings were not saved: ${formatError(error)}`, "error");
650
+ return { kind: "rejected" as const };
651
+ }
652
+ }
653
+
654
+ function applyDelegationCwdSetting(
655
+ value: string | undefined,
656
+ ctx: ExtensionCommandContext,
657
+ runtime: SubagentSettingsRuntime,
658
+ ) {
659
+ const previous = runtime.getDelegationCwdPolicy();
660
+ const next: DelegationCwdPolicy =
661
+ value === "Current workspace only"
662
+ ? "current-workspace"
663
+ : value === "Anywhere · normal Pi permissions"
664
+ ? "anywhere"
665
+ : "trusted-targets";
666
+ if (next === previous) return { kind: "stay" as const };
667
+ try {
668
+ updateCwdPolicySetting("delegation", next);
669
+ runtime.setDelegationCwdPolicy(next);
670
+ ctx.ui.notify(`Saved and applied: ${delegationCwdLabel(next)}.`, "info");
671
+ return { kind: "stay" as const };
672
+ } catch (error) {
673
+ ctx.ui.notify(`Subagent settings were not saved: ${formatError(error)}`, "error");
674
+ return { kind: "rejected" as const };
675
+ }
676
+ }
677
+
587
678
  function blockReloadWithRetainedAgents(
588
679
  ctx: ExtensionCommandContext,
589
680
  runtime: SubagentSettingsRuntime,
@@ -628,9 +719,9 @@ function showSubagentStatus(ctx: ExtensionCommandContext, runtime: SubagentSetti
628
719
  );
629
720
  }
630
721
 
631
- function showSubagentHelp(ctx: ExtensionCommandContext) {
722
+ function showSubagentHelp(ctx: ExtensionCommandContext, runtime: SubagentSettingsRuntime) {
632
723
  if (ctx.mode !== "tui" && !ctx.hasUI) return;
633
- ctx.ui.notify(helpLines().join("\n"), "info");
724
+ ctx.ui.notify(helpLines(runtime).join("\n"), "info");
634
725
  }
635
726
 
636
727
  function statusLines(runtime: SubagentSettingsRuntime): string[] {
@@ -638,13 +729,20 @@ function statusLines(runtime: SubagentSettingsRuntime): string[] {
638
729
  return formatStatus(runtime.getRuntimeStatus(), snapshot, runtime).split("\n");
639
730
  }
640
731
 
641
- function helpLines(): string[] {
732
+ function helpLines(runtime: SubagentSettingsRuntime): string[] {
642
733
  const snapshot = inspectCompletionDeliverySettings();
734
+ const cwdPolicy = inspectCwdPolicySettings();
643
735
  return [
644
736
  "/subagents — choose delegation workflow, manage current agents, and configure agent tools",
645
- "/subagents settings — configure async completion and read-only consultation resources",
737
+ "/subagents settings — configure target locations, trusted resources, and async completion",
646
738
  "/subagents status — show current-session and user-setting values",
647
739
  "/subagents help — show this help",
740
+ "Target policies control startup directories and resources, not filesystem access or sandboxing.",
741
+ "Manage saved folder trust with Pi /trust and restart Pi after changing it.",
742
+ `Runtime consultation target: ${consultationCwdLabel(runtime.getConsultationCwdPolicy())}`,
743
+ `Configured consultation target: ${consultationCwdLabel(cwdPolicy.consultation.value)} (${cwdPolicy.consultation.source})`,
744
+ `Runtime delegation target: ${delegationCwdLabel(runtime.getDelegationCwdPolicy())}`,
745
+ `Configured delegation target: ${delegationCwdLabel(cwdPolicy.delegation.value)} (${cwdPolicy.delegation.source})`,
648
746
  `User settings: ${safeTerminalText(snapshot.path)}`,
649
747
  ];
650
748
  }
@@ -655,10 +753,18 @@ function formatManagerSummary(
655
753
  configured: ReturnType<typeof inspectDelegationWorkflowSettings>,
656
754
  ): string {
657
755
  const current = currentWorkflow(runtime, status);
756
+ const cwdPolicy = inspectCwdPolicySettings();
757
+ const consult = inspectConsultResourceSettings();
658
758
  return [
659
759
  `Delegation: ${workflowLabel(current)}`,
660
760
  `Completion: ${completionLabel(status.completionDelivery)}`,
761
+ `Consult target: ${consultationCwdLabel(runtime.getConsultationCwdPolicy())}`,
762
+ `Delegation target: ${delegationCwdLabel(runtime.getDelegationCwdPolicy())}`,
661
763
  `Consult resources: ${consultResourceLabel(runtime.getConsultResourcePolicy())}`,
764
+ `Configured consult target: ${consultationCwdLabel(cwdPolicy.consultation.value)} · ${cwdPolicy.consultation.source}`,
765
+ `Configured delegation target: ${delegationCwdLabel(cwdPolicy.delegation.value)} · ${cwdPolicy.delegation.source}`,
766
+ `Configured consult resources: ${consultResourceLabel(consult.value)} · ${consult.source}`,
767
+ `Settings: ${safeTerminalText(cwdPolicy.path)}`,
662
768
  `Agents: ${status.activeAgents} active · ${status.retainedAgents} retained`,
663
769
  ...(configured.value !== current
664
770
  ? [`Configured after reload: ${workflowLabel(configured.value)}`]
@@ -674,6 +780,7 @@ function formatStatus(
674
780
  ): string {
675
781
  const configuredWorkflow = inspectDelegationWorkflowSettings();
676
782
  const consult = inspectConsultResourceSettings();
783
+ const cwdPolicy = inspectCwdPolicySettings();
677
784
  const current = runtime ? currentWorkflow(runtime, status) : configuredWorkflow.value;
678
785
  return [
679
786
  "Current session",
@@ -681,17 +788,24 @@ function formatStatus(
681
788
  ` Async runtime: ${status.initialized ? "initialized" : status.enabled ? "not initialized" : "disabled"}`,
682
789
  ` Transport: ${status.transport}`,
683
790
  ` Completion: ${completionLabel(status.completionDelivery)}`,
791
+ ` Consultation target: ${consultationCwdLabel(runtime?.getConsultationCwdPolicy() ?? cwdPolicy.consultation.value)}`,
792
+ ` Delegation target: ${delegationCwdLabel(runtime?.getDelegationCwdPolicy() ?? cwdPolicy.delegation.value)}`,
793
+ ` Consultation resources: ${consultResourceLabel(runtime?.getConsultResourcePolicy() ?? consult.value)}`,
684
794
  ` Agents: ${status.activeAgents} active, ${status.retainedAgents} retained`,
685
795
  "User settings",
686
796
  ` Delegation source: ${configuredWorkflow.source}`,
687
797
  ` Configured delegation: ${workflowLabel(configuredWorkflow.value)}`,
688
798
  ` Completion source: ${snapshot.source}`,
689
799
  ` Configured completion: ${completionLabel(snapshot.value)}`,
690
- ` Consultation resources: ${consultResourceLabel(runtime?.getConsultResourcePolicy() ?? consult.value)}`,
800
+ ` Configured consultation target: ${consultationCwdLabel(cwdPolicy.consultation.value)}`,
801
+ ` Consultation target source: ${cwdPolicy.consultation.source}`,
802
+ ` Configured delegation target: ${delegationCwdLabel(cwdPolicy.delegation.value)}`,
803
+ ` Delegation target source: ${cwdPolicy.delegation.source}`,
804
+ ` Configured consultation resources: ${consultResourceLabel(consult.value)}`,
691
805
  ` Consultation resource source: ${consult.source}`,
692
806
  ` Path: ${safeTerminalText(snapshot.path)}`,
693
- configuredWorkflow.error || snapshot.error
694
- ? ` Warning: ${safeTerminalText(configuredWorkflow.error ?? snapshot.error ?? "invalid settings")}`
807
+ configuredWorkflow.error || snapshot.error || cwdPolicy.error
808
+ ? ` Warning: ${safeTerminalText(configuredWorkflow.error ?? snapshot.error ?? cwdPolicy.error ?? "invalid settings")}`
695
809
  : " Warning: none",
696
810
  configuredWorkflow.value !== current
697
811
  ? "Configured delegation differs from this session. Run /reload to apply it."
@@ -737,6 +851,23 @@ function completionLabel(value: CompletionDelivery): string {
737
851
  return value === "auto-resume" ? "Resume automatically when finished" : "Wait until my next turn";
738
852
  }
739
853
 
854
+ function consultationCwdLabel(value: ConsultationCwdPolicy): string {
855
+ return value === "current-workspace"
856
+ ? "Current workspace only"
857
+ : "Anywhere · untrusted targets inherit nothing";
858
+ }
859
+
860
+ function delegationCwdLabel(value: DelegationCwdPolicy): string {
861
+ switch (value) {
862
+ case "trusted-targets":
863
+ return "Current or saved-trusted folders";
864
+ case "current-workspace":
865
+ return "Current workspace only";
866
+ case "anywhere":
867
+ return "Anywhere · normal Pi permissions";
868
+ }
869
+ }
870
+
740
871
  function consultResourceLabel(value: ConsultResourcePolicy): string {
741
872
  switch (value) {
742
873
  case "project-context":