pi-subagents 0.40.0 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +54 -1879
  3. package/agents/oracle.md +1 -0
  4. package/inspector-runner.mjs +11 -0
  5. package/package.json +13 -5
  6. package/prompts/parallel-context-build.md +1 -1
  7. package/prompts/parallel-handoff-plan.md +1 -1
  8. package/prompts/review-loop.md +1 -1
  9. package/skills/pi-subagents/SKILL.md +9 -7
  10. package/skills/pi-subagents/references/constraints-and-recipes.md +24 -32
  11. package/skills/pi-subagents/references/execution-controls.md +99 -97
  12. package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
  13. package/skills/pi-subagents/references/prompting-and-roles.md +23 -30
  14. package/src/agents/agent-management.ts +155 -65
  15. package/src/agents/agent-serializer.ts +19 -0
  16. package/src/agents/agents.ts +154 -71
  17. package/src/agents/chain-serializer.ts +10 -7
  18. package/src/agents/frontmatter.ts +5 -3
  19. package/src/agents/identity.ts +1 -1
  20. package/src/agents/proactive-skills.ts +13 -10
  21. package/src/agents/skills.ts +23 -6
  22. package/src/api/control-channel.ts +4 -0
  23. package/src/api/delegation.ts +26 -194
  24. package/src/api/external-runs.ts +129 -0
  25. package/src/api/intercom-bridge.ts +3 -0
  26. package/src/api/pi-args.ts +5 -0
  27. package/src/api/preflight.ts +3 -3
  28. package/src/api/shared-types.ts +19 -0
  29. package/src/extension/config.ts +10 -0
  30. package/src/extension/control-notices.ts +5 -39
  31. package/src/extension/doctor.ts +10 -9
  32. package/src/extension/fanout-child.ts +7 -4
  33. package/src/extension/index.ts +232 -68
  34. package/src/extension/rpc.ts +18 -12
  35. package/src/extension/schemas.ts +48 -37
  36. package/src/extension/tool-description.ts +36 -86
  37. package/src/inspectors/herdr/actions.ts +229 -0
  38. package/src/inspectors/herdr/client.ts +130 -0
  39. package/src/inspectors/herdr/inspector-runner.ts +141 -0
  40. package/src/inspectors/herdr/project-panes.ts +154 -0
  41. package/src/integrations/herdr-status.ts +330 -0
  42. package/src/intercom/intercom-bridge.ts +3 -2
  43. package/src/intercom/result-intercom.ts +5 -1
  44. package/src/missions/actions.ts +374 -0
  45. package/src/missions/lifecycle.ts +314 -0
  46. package/src/missions/store.ts +442 -0
  47. package/src/missions/types.ts +135 -0
  48. package/src/policy/authority.ts +46 -0
  49. package/src/profiles/profiles.ts +29 -3
  50. package/src/runs/background/async-execution.ts +110 -50
  51. package/src/runs/background/async-job-tracker.ts +27 -4
  52. package/src/runs/background/async-resume.ts +6 -6
  53. package/src/runs/background/async-status.ts +29 -1
  54. package/src/runs/background/auto-drain.ts +3 -3
  55. package/src/runs/background/chain-append.ts +3 -2
  56. package/src/runs/background/control-channel.ts +9 -7
  57. package/src/runs/background/fleet-view.ts +3 -4
  58. package/src/runs/background/notify.ts +2 -1
  59. package/src/runs/background/process-terminal.ts +5 -5
  60. package/src/runs/background/result-watcher.ts +13 -5
  61. package/src/runs/background/run-id-resolver.ts +3 -3
  62. package/src/runs/background/run-status.ts +35 -8
  63. package/src/runs/background/scheduled-runs.ts +602 -375
  64. package/src/runs/background/stale-run-reconciler.ts +3 -3
  65. package/src/runs/background/subagent-runner.ts +608 -445
  66. package/src/runs/background/subagent-wait.ts +50 -9
  67. package/src/runs/background/wait-subscriptions.ts +253 -0
  68. package/src/runs/background/wait-tool.ts +12 -4
  69. package/src/runs/foreground/async-steering-action.ts +3 -3
  70. package/src/runs/foreground/chain-clarify.ts +8 -4
  71. package/src/runs/foreground/chain-execution.ts +56 -30
  72. package/src/runs/foreground/execution.ts +34 -9
  73. package/src/runs/foreground/subagent-executor.ts +1036 -274
  74. package/src/runs/shared/acceptance.ts +28 -6
  75. package/src/runs/shared/child-protocol.ts +302 -22
  76. package/src/runs/shared/dynamic-fanout.ts +1 -1
  77. package/src/runs/shared/external-cli-runner.ts +130 -0
  78. package/src/runs/shared/long-running-guard.ts +42 -1
  79. package/src/runs/shared/mcp-direct-tool-allowlist.ts +2 -0
  80. package/src/runs/shared/nested-events.ts +59 -5
  81. package/src/runs/shared/nested-render.ts +9 -4
  82. package/src/runs/shared/parallel-handoff.ts +86 -2
  83. package/src/runs/shared/parallel-utils.ts +11 -2
  84. package/src/runs/shared/permissions.ts +95 -0
  85. package/src/runs/shared/pi-args.ts +468 -109
  86. package/src/runs/shared/pi-spawn.ts +11 -1
  87. package/src/runs/shared/run-history.ts +1 -1
  88. package/src/runs/shared/subagent-prompt-runtime.ts +36 -5
  89. package/src/runs/shared/subagent-startup-retry.ts +5 -2
  90. package/src/runs/shared/turn-budget.ts +6 -6
  91. package/src/runs/shared/worktree.ts +122 -12
  92. package/src/shared/accessible-dir.ts +29 -7
  93. package/src/shared/artifacts.ts +143 -1
  94. package/src/shared/fork-context.ts +3 -2
  95. package/src/shared/launch-contract.ts +1 -0
  96. package/src/shared/settings.ts +10 -0
  97. package/src/shared/types.ts +156 -14
  98. package/src/shared/utils.ts +8 -6
  99. package/src/slash/delegation-adapters.ts +32 -194
  100. package/src/slash/delegation-request.ts +43 -126
  101. package/src/slash/prompt-template-bridge.ts +158 -205
  102. package/src/slash/prompt-workflows.ts +21 -57
  103. package/src/slash/slash-bridge.ts +14 -0
  104. package/src/slash/slash-commands.ts +31 -632
  105. package/src/slash/subagents-admin.ts +18 -14
  106. package/src/tui/fleet-status.ts +156 -21
  107. package/src/tui/fleet-transcript.ts +110 -5
  108. package/src/tui/fleet.ts +57 -25
  109. package/src/tui/render.ts +368 -152
  110. package/src/types/pi-runtime-compat.d.ts +8 -0
  111. package/src/watchdog/lsp-diagnostics.ts +12 -7
  112. package/src/watchdog/model-selection.ts +2 -2
  113. package/src/watchdog/permission-arbiter.ts +145 -0
  114. package/src/watchdog/register-child.ts +1 -1
  115. package/src/watchdog/register-main.ts +1 -1
  116. package/src/watchdog/review.ts +4 -1
  117. package/src/watchdog/runtime.ts +3 -2
  118. package/src/workflows/chat-progress.ts +140 -0
  119. package/src/workflows/scripted-workflow.ts +415 -0
  120. package/agents/advisor.md +0 -73
  121. package/src/extension/chain-validation.ts +0 -181
@@ -3,40 +3,99 @@ import * as fs from "node:fs";
3
3
  import * as os from "node:os";
4
4
  import * as path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
- import { encodeNestedPathEnv, parseNestedPathEnv, type NestedPathEntry } from "./nested-path.ts";
7
- import { resolveMcpDirectToolSelections, type ResolvedMcpDirectToolSelection } from "./mcp-direct-tool-allowlist.ts";
6
+ import {
7
+ encodeNestedPathEnv,
8
+ parseNestedPathEnv,
9
+ type NestedPathEntry,
10
+ } from "./nested-path.ts";
11
+ import {
12
+ resolveMcpDirectToolSelections,
13
+ type ResolvedMcpDirectToolSelection,
14
+ } from "./mcp-direct-tool-allowlist.ts";
8
15
  import { resolvePiPackageRoot } from "./pi-spawn.ts";
9
16
  import { RUNTIME_EXTENSION_ACK_PATH_ENV } from "./runtime-acknowledged-extensions.ts";
10
- import { STRUCTURED_OUTPUT_CAPTURE_ENV, STRUCTURED_OUTPUT_SCHEMA_ENV } from "./structured-output.ts";
11
- import { TEMP_ROOT_DIR, type JsonSchemaObject, type LaunchResolvedChildExtensionsV1, type ResolvedToolBudget } from "../../shared/types.ts";
17
+ import {
18
+ STRUCTURED_OUTPUT_CAPTURE_ENV,
19
+ STRUCTURED_OUTPUT_SCHEMA_ENV,
20
+ } from "./structured-output.ts";
21
+ import {
22
+ TEMP_ROOT_DIR,
23
+ type JsonSchemaObject,
24
+ type LaunchResolvedChildExtensionsV1,
25
+ type ResolvedToolBudget,
26
+ } from "../../shared/types.ts";
12
27
  import { THINKING_LEVELS } from "../../shared/model-info.ts";
13
- import { TOOL_BUDGET_ENV, TOOL_BUDGET_ZERO_AUTH_ENV, encodeToolBudgetEnv } from "./tool-budget.ts";
14
- import { CHILD_TOOL_DIAGNOSTIC_PATH_ENV, MCP_DIRECT_CHILD_TOOLS_ENV, REQUIRED_CHILD_TOOLS_ENV } from "./tool-availability.ts";
15
- import { CHILD_WATCHDOG_CONFIG_ENV, encodeChildWatchdogConfig, type ChildWatchdogConfig } from "../../watchdog/child-status.ts";
28
+ import {
29
+ TOOL_BUDGET_ENV,
30
+ TOOL_BUDGET_ZERO_AUTH_ENV,
31
+ encodeToolBudgetEnv,
32
+ } from "./tool-budget.ts";
33
+ import {
34
+ CHILD_TOOL_DIAGNOSTIC_PATH_ENV,
35
+ MCP_DIRECT_CHILD_TOOLS_ENV,
36
+ REQUIRED_CHILD_TOOLS_ENV,
37
+ } from "./tool-availability.ts";
38
+ import {
39
+ CHILD_WATCHDOG_CONFIG_ENV,
40
+ encodeChildWatchdogConfig,
41
+ type ChildWatchdogConfig,
42
+ } from "../../watchdog/child-status.ts";
16
43
  import { WAIT_TOOL_ENABLED_ENV } from "../background/wait-config.ts";
17
- import { PI_CODING_AGENT_PACKAGE_ROOT_ENV } from "../../shared/utils.ts";
18
- import { SUBAGENT_CAPABILITY_CEILING_ENV, capabilityCeilingAgentRestrictionSources, decodeSubagentCapabilityCeiling, encodeSubagentCapabilityCeiling, intersectSubagentCapabilityCeilings, isAgentAllowedByCapabilityCeiling, type ResolvedSubagentCapabilityCeiling, type SubagentCapabilityAudit } from "./capability-ceiling.ts";
44
+ import {
45
+ PI_CODING_AGENT_PACKAGE_ROOT_ENV,
46
+ getAgentDir,
47
+ } from "../../shared/utils.ts";
48
+ import {
49
+ encodePermissionRules,
50
+ PERMISSION_AUDIT_PATH_ENV,
51
+ PERMISSION_POLICY_ENV,
52
+ type PermissionRules,
53
+ } from "./permissions.ts";
54
+ import {
55
+ SUBAGENT_CAPABILITY_CEILING_ENV,
56
+ capabilityCeilingAgentRestrictionSources,
57
+ decodeSubagentCapabilityCeiling,
58
+ encodeSubagentCapabilityCeiling,
59
+ intersectSubagentCapabilityCeilings,
60
+ isAgentAllowedByCapabilityCeiling,
61
+ type ResolvedSubagentCapabilityCeiling,
62
+ type SubagentCapabilityAudit,
63
+ } from "./capability-ceiling.ts";
19
64
 
20
65
  const TASK_ARG_LIMIT = 8000;
21
66
  const MAX_LAUNCH_RESOLVED_EXTENSION_IDS = 32;
22
- const PROMPT_RUNTIME_EXTENSION_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), "subagent-prompt-runtime.ts");
23
- const FANOUT_CHILD_EXTENSION_PATH = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "extension", "fanout-child.ts");
67
+ const PROMPT_RUNTIME_EXTENSION_PATH = path.join(
68
+ path.dirname(fileURLToPath(import.meta.url)),
69
+ "subagent-prompt-runtime.ts",
70
+ );
71
+ const FANOUT_CHILD_EXTENSION_PATH = path.join(
72
+ path.dirname(fileURLToPath(import.meta.url)),
73
+ "..",
74
+ "..",
75
+ "extension",
76
+ "fanout-child.ts",
77
+ );
24
78
  export const SUBAGENT_CHILD_ENV = "PI_SUBAGENT_CHILD";
25
- export const SUBAGENT_ORCHESTRATOR_TARGET_ENV = "PI_SUBAGENT_ORCHESTRATOR_TARGET";
26
- export const SUBAGENT_ORCHESTRATOR_SESSION_ID_ENV = "PI_SUBAGENT_ORCHESTRATOR_SESSION_ID";
27
- export const SUBAGENT_SUPERVISOR_CHANNEL_DIR_ENV = "PI_SUBAGENT_SUPERVISOR_CHANNEL_DIR";
79
+ export const SUBAGENT_ORCHESTRATOR_TARGET_ENV =
80
+ "PI_SUBAGENT_ORCHESTRATOR_TARGET";
81
+ export const SUBAGENT_ORCHESTRATOR_SESSION_ID_ENV =
82
+ "PI_SUBAGENT_ORCHESTRATOR_SESSION_ID";
83
+ export const SUBAGENT_SUPERVISOR_CHANNEL_DIR_ENV =
84
+ "PI_SUBAGENT_SUPERVISOR_CHANNEL_DIR";
28
85
  export const SUBAGENT_RUN_ID_ENV = "PI_SUBAGENT_RUN_ID";
29
86
  export const SUBAGENT_CHILD_AGENT_ENV = "PI_SUBAGENT_CHILD_AGENT";
30
87
  export const SUBAGENT_CHILD_INDEX_ENV = "PI_SUBAGENT_CHILD_INDEX";
31
88
  export const SUBAGENT_FANOUT_CHILD_ENV = "PI_SUBAGENT_FANOUT_CHILD";
32
89
  export const SUBAGENT_PARENT_EVENT_SINK_ENV = "PI_SUBAGENT_PARENT_EVENT_SINK";
33
- export const SUBAGENT_PARENT_CONTROL_INBOX_ENV = "PI_SUBAGENT_PARENT_CONTROL_INBOX";
90
+ export const SUBAGENT_PARENT_CONTROL_INBOX_ENV =
91
+ "PI_SUBAGENT_PARENT_CONTROL_INBOX";
34
92
  export const SUBAGENT_PARENT_ROOT_RUN_ID_ENV = "PI_SUBAGENT_PARENT_ROOT_RUN_ID";
35
93
  export const SUBAGENT_PARENT_RUN_ID_ENV = "PI_SUBAGENT_PARENT_RUN_ID";
36
94
  export const SUBAGENT_PARENT_CHILD_INDEX_ENV = "PI_SUBAGENT_PARENT_CHILD_INDEX";
37
95
  export const SUBAGENT_PARENT_DEPTH_ENV = "PI_SUBAGENT_PARENT_DEPTH";
38
96
  export const SUBAGENT_PARENT_PATH_ENV = "PI_SUBAGENT_PARENT_PATH";
39
- export const SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV = "PI_SUBAGENT_PARENT_CAPABILITY_TOKEN";
97
+ export const SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV =
98
+ "PI_SUBAGENT_PARENT_CAPABILITY_TOKEN";
40
99
  export const SUBAGENT_PARENT_SESSION_ENV = "PI_SUBAGENT_PARENT_SESSION";
41
100
  export const SUBAGENT_STEER_INBOX_ENV = "PI_SUBAGENT_STEER_INBOX";
42
101
  export const SUBAGENT_STEER_CAPABILITY_ENV = "PI_SUBAGENT_STEER_CAPABILITY";
@@ -87,6 +146,8 @@ export interface BuildPiArgsInput {
87
146
  };
88
147
  toolBudget?: ResolvedToolBudget;
89
148
  allowZeroToolBudget?: boolean;
149
+ permissionRules?: PermissionRules;
150
+ permissionAuditPath?: string;
90
151
  childWatchdog?: ChildWatchdogConfig;
91
152
  waitToolEnabled?: boolean;
92
153
  capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
@@ -102,17 +163,37 @@ export interface BuildPiArgsResult {
102
163
  }
103
164
 
104
165
  function sanitizeSupervisorChannelSegment(value: string): string {
105
- return value.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
166
+ return (
167
+ value
168
+ .trim()
169
+ .replace(/[^A-Za-z0-9._-]+/g, "-")
170
+ .replace(/^-+|-+$/g, "") || "unknown"
171
+ );
106
172
  }
107
173
 
108
- function supervisorChannelDir(runId: string, agent: string, childIndex: number): string {
109
- return path.join(TEMP_ROOT_DIR, "supervisor-channels", `${sanitizeSupervisorChannelSegment(runId)}-${sanitizeSupervisorChannelSegment(agent)}-${childIndex}`);
174
+ function supervisorChannelDir(
175
+ runId: string,
176
+ agent: string,
177
+ childIndex: number,
178
+ ): string {
179
+ return path.join(
180
+ TEMP_ROOT_DIR,
181
+ "supervisor-channels",
182
+ `${sanitizeSupervisorChannelSegment(runId)}-${sanitizeSupervisorChannelSegment(agent)}-${childIndex}`,
183
+ );
110
184
  }
111
185
 
112
- export function applyThinkingSuffix(model: string | undefined, thinking: string | false | undefined, replaceExisting = false): string | undefined {
186
+ export function applyThinkingSuffix(
187
+ model: string | undefined,
188
+ thinking: string | false | undefined,
189
+ replaceExisting = false,
190
+ ): string | undefined {
113
191
  if (!model || !thinking) return model;
114
192
  const colonIdx = model.lastIndexOf(":");
115
- if (colonIdx !== -1 && THINKING_LEVELS.some((level) => level === model.substring(colonIdx + 1))) {
193
+ if (
194
+ colonIdx !== -1 &&
195
+ THINKING_LEVELS.some((level) => level === model.substring(colonIdx + 1))
196
+ ) {
116
197
  return replaceExisting ? `${model.slice(0, colonIdx)}:${thinking}` : model;
117
198
  }
118
199
  return `${model}:${thinking}`;
@@ -125,7 +206,13 @@ export interface ResolvePiLaunchToolPlanInput {
125
206
  mcpDirectTools?: string[];
126
207
  cwd?: string;
127
208
  requireReadTool?: boolean;
128
- structuredOutput?: boolean;
209
+ structuredOutput?:
210
+ | boolean
211
+ | {
212
+ schema: JsonSchemaObject;
213
+ schemaPath: string;
214
+ outputPath: string;
215
+ };
129
216
  capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
130
217
  inheritedCapabilityCeiling?: ResolvedSubagentCapabilityCeiling;
131
218
  agentName?: string;
@@ -155,7 +242,10 @@ function extensionIdentifier(value: string): string {
155
242
  return `sha256:${createHash("sha256").update(path.normalize(value.trim())).digest("hex").slice(0, 16)}`;
156
243
  }
157
244
 
158
- function boundedExtensionIdentifiers(values: string[]): { ids: string[]; omitted: number } {
245
+ function boundedExtensionIdentifiers(values: string[]): {
246
+ ids: string[];
247
+ omitted: number;
248
+ } {
159
249
  const ids = [...new Set(values.map(extensionIdentifier))];
160
250
  return {
161
251
  ids: ids.slice(0, MAX_LAUNCH_RESOLVED_EXTENSION_IDS),
@@ -163,7 +253,15 @@ function boundedExtensionIdentifiers(values: string[]): { ids: string[]; omitted
163
253
  };
164
254
  }
165
255
 
166
- export function projectLaunchResolvedChildExtensions(toolPlan: Pick<PiLaunchToolPlan, "runtimeExtensions" | "configuredExtensions" | "extensionArgs" | "disableAmbientExtensions">): LaunchResolvedChildExtensionsV1 {
256
+ export function projectLaunchResolvedChildExtensions(
257
+ toolPlan: Pick<
258
+ PiLaunchToolPlan,
259
+ | "runtimeExtensions"
260
+ | "configuredExtensions"
261
+ | "extensionArgs"
262
+ | "disableAmbientExtensions"
263
+ >,
264
+ ): LaunchResolvedChildExtensionsV1 {
167
265
  const runtime = boundedExtensionIdentifiers(toolPlan.runtimeExtensions);
168
266
  const configured = boundedExtensionIdentifiers(toolPlan.configuredExtensions);
169
267
  const effective = boundedExtensionIdentifiers(toolPlan.extensionArgs);
@@ -182,55 +280,207 @@ export function projectLaunchResolvedChildExtensions(toolPlan: Pick<PiLaunchTool
182
280
  };
183
281
  }
184
282
 
185
- export function resolvePiLaunchToolPlan(input: ResolvePiLaunchToolPlanInput): PiLaunchToolPlan {
186
- const capabilityCeiling = intersectSubagentCapabilityCeilings(input.capabilityCeiling, input.inheritedCapabilityCeiling);
187
- const allowedToolSet = capabilityCeiling?.allowedTools === undefined ? undefined : new Set(capabilityCeiling.allowedTools);
188
- const requestedBuiltinTools = input.tools?.filter((tool) => !(tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js"))) ?? [];
283
+ /**
284
+ * Resolve the permission-system extension entry point when installed.
285
+ * Returns the absolute path to the extension's main module, or undefined
286
+ * when the package is not installed. Callers can check `autoInject` config
287
+ * to decide whether to include it in child processes.
288
+ */
289
+ export function resolvePermissionSystemExtension(): string | undefined {
290
+ const agentDir = getAgentDir();
291
+ const candidates = [
292
+ // npm-scoped package (most common)
293
+ path.join(
294
+ agentDir,
295
+ "npm",
296
+ "node_modules",
297
+ "@gotgenes",
298
+ "pi-permission-system",
299
+ ),
300
+ // direct extension directory (some layouts)
301
+ path.join(agentDir, "extensions", "pi-permission-system"),
302
+ ];
303
+ const errors: Error[] = [];
304
+ for (const extDir of candidates) {
305
+ if (!fs.existsSync(extDir)) continue;
306
+ const pkgPath = path.join(extDir, "package.json");
307
+ if (!fs.existsSync(pkgPath)) {
308
+ errors.push(new Error(`Permission-system package manifest is missing at ${pkgPath}.`));
309
+ continue;
310
+ }
311
+ try {
312
+ let pkg: { pi?: { extensions?: string[] } };
313
+ const parsed: unknown = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
314
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
315
+ throw new Error("manifest root must be an object");
316
+ }
317
+ pkg = parsed as typeof pkg;
318
+ const extensions = pkg.pi?.extensions;
319
+ const entry = Array.isArray(extensions) ? extensions[0] : undefined;
320
+ if (typeof entry !== "string" || !entry.trim()) {
321
+ throw new Error(
322
+ `Permission-system package manifest at ${pkgPath} must declare pi.extensions[0] as a non-empty string.`,
323
+ );
324
+ }
325
+ const resolved = path.resolve(extDir, entry);
326
+ if (fs.existsSync(resolved)) return resolved;
327
+ throw new Error(
328
+ `Permission-system extension entry ${JSON.stringify(entry)} in ${pkgPath} does not exist at ${resolved}.`,
329
+ );
330
+ } catch (error) {
331
+ const message = error instanceof Error ? error.message : String(error);
332
+ errors.push(message.startsWith("Permission-system") ? new Error(message) : new Error(`Cannot read permission-system package manifest at ${pkgPath}: ${message}`));
333
+ }
334
+ }
335
+ if (errors.length > 0) throw errors[0]!;
336
+ return undefined;
337
+ }
338
+
339
+ export function resolvePiLaunchToolPlan(
340
+ input: ResolvePiLaunchToolPlanInput,
341
+ ): PiLaunchToolPlan {
342
+ const capabilityCeiling = intersectSubagentCapabilityCeilings(
343
+ input.capabilityCeiling,
344
+ input.inheritedCapabilityCeiling,
345
+ );
346
+ const allowedToolSet =
347
+ capabilityCeiling?.allowedTools === undefined
348
+ ? undefined
349
+ : new Set(capabilityCeiling.allowedTools);
350
+ const requestedBuiltinTools =
351
+ input.tools?.filter(
352
+ (tool) =>
353
+ !(tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js")),
354
+ ) ?? [];
189
355
  if (input.requireReadTool && allowedToolSet && !allowedToolSet.has("read")) {
190
- throw new Error(`Capability ceiling from ${capabilityCeiling?.sources.join(", ") || "unknown source"} excludes required tool 'read' for lazy skill loading.`);
356
+ throw new Error(
357
+ `Capability ceiling from ${capabilityCeiling?.sources.join(", ") || "unknown source"} excludes required tool 'read' for lazy skill loading.`,
358
+ );
191
359
  }
192
- const declaredBuiltinTools = input.tools === undefined
193
- ? (allowedToolSet ? [...allowedToolSet] : [])
194
- : (input.requireReadTool && requestedBuiltinTools.length > 0 && !requestedBuiltinTools.includes("read") && !allowedToolSet
195
- ? ["read", ...requestedBuiltinTools]
196
- : requestedBuiltinTools).filter((tool) => !allowedToolSet || allowedToolSet.has(tool));
360
+ const declaredBuiltinTools =
361
+ input.tools === undefined
362
+ ? allowedToolSet
363
+ ? [...allowedToolSet]
364
+ : []
365
+ : (input.requireReadTool &&
366
+ requestedBuiltinTools.length > 0 &&
367
+ !requestedBuiltinTools.includes("read") &&
368
+ !allowedToolSet
369
+ ? ["read", ...requestedBuiltinTools]
370
+ : requestedBuiltinTools
371
+ ).filter((tool) => !allowedToolSet || allowedToolSet.has(tool));
197
372
  const fanoutAuthorized = declaredBuiltinTools.includes("subagent");
198
- const toolExtensionPaths: string[] = capabilityCeiling?.denyExtensions ? [] : (input.tools ?? []).filter((tool) => !requestedBuiltinTools.includes(tool) && (tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js")));
199
- const resolvedMcpSelections = capabilityCeiling?.denyExtensions ? [] : resolveMcpDirectToolSelections(input.mcpDirectTools, input.cwd);
200
- const effectiveMcpSelections = resolvedMcpSelections.filter((selection) => !allowedToolSet || allowedToolSet.has(selection.name));
201
- const effectiveMcpTools = effectiveMcpSelections.map((selection) => selection.name);
202
- const explicitToolAllowlist = input.tools !== undefined || (input.mcpDirectTools?.length ?? 0) > 0 || allowedToolSet !== undefined;
373
+ const toolExtensionPaths: string[] = capabilityCeiling?.denyExtensions
374
+ ? []
375
+ : (input.tools ?? []).filter(
376
+ (tool) =>
377
+ !requestedBuiltinTools.includes(tool) &&
378
+ (tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js")),
379
+ );
380
+ const resolvedMcpSelections = capabilityCeiling?.denyExtensions
381
+ ? []
382
+ : resolveMcpDirectToolSelections(input.mcpDirectTools, input.cwd);
383
+ const effectiveMcpSelections = resolvedMcpSelections.filter(
384
+ (selection) => !allowedToolSet || allowedToolSet.has(selection.name),
385
+ );
386
+ const effectiveMcpTools = effectiveMcpSelections.map(
387
+ (selection) => selection.name,
388
+ );
389
+ const explicitToolAllowlist =
390
+ input.tools !== undefined ||
391
+ (input.mcpDirectTools?.length ?? 0) > 0 ||
392
+ allowedToolSet !== undefined;
203
393
  const internalTools = input.structuredOutput ? ["structured_output"] : [];
204
- const effectiveToolAllowlist = [...new Set([...declaredBuiltinTools, ...effectiveMcpTools, ...internalTools])];
205
- const requiredChildTools = explicitToolAllowlist ? [...new Set([
206
- ...(input.tools !== undefined ? declaredBuiltinTools : []),
207
- ...(input.mcpDirectTools?.length ? effectiveMcpTools : []),
208
- ...internalTools,
209
- ])] : [];
210
- const runtimeExtensions = fanoutAuthorized
211
- ? [PROMPT_RUNTIME_EXTENSION_PATH, FANOUT_CHILD_EXTENSION_PATH]
212
- : [PROMPT_RUNTIME_EXTENSION_PATH];
213
- const disableAmbientExtensions = capabilityCeiling?.denyExtensions === true || input.extensions !== undefined;
214
- const configuredExtensions = capabilityCeiling?.denyExtensions ? [] : [...toolExtensionPaths, ...(input.extensions ?? []), ...(input.subagentOnlyExtensions ?? [])];
394
+ const effectiveToolAllowlist = [
395
+ ...new Set([
396
+ ...declaredBuiltinTools,
397
+ ...effectiveMcpTools,
398
+ ...internalTools,
399
+ ]),
400
+ ];
401
+ const requiredChildTools = explicitToolAllowlist
402
+ ? [
403
+ ...new Set([
404
+ ...(input.tools !== undefined ? declaredBuiltinTools : []),
405
+ ...(input.mcpDirectTools?.length ? effectiveMcpTools : []),
406
+ ...internalTools,
407
+ ]),
408
+ ]
409
+ : [];
410
+ const permSystemExt = capabilityCeiling?.denyExtensions
411
+ ? undefined
412
+ : resolvePermissionSystemExtension();
413
+ const runtimeExtensions = [
414
+ PROMPT_RUNTIME_EXTENSION_PATH,
415
+ ...(fanoutAuthorized ? [FANOUT_CHILD_EXTENSION_PATH] : []),
416
+ ...(permSystemExt ? [permSystemExt] : []),
417
+ ];
418
+ const disableAmbientExtensions =
419
+ capabilityCeiling?.denyExtensions === true ||
420
+ input.extensions !== undefined;
421
+ const configuredExtensions = capabilityCeiling?.denyExtensions
422
+ ? []
423
+ : [
424
+ ...toolExtensionPaths,
425
+ ...(input.extensions ?? []),
426
+ ...(input.subagentOnlyExtensions ?? []),
427
+ ];
215
428
  const extensionArgs = disableAmbientExtensions
216
429
  ? [...new Set([...runtimeExtensions, ...configuredExtensions])]
217
- : [...new Set([...runtimeExtensions, ...toolExtensionPaths, ...(input.subagentOnlyExtensions ?? [])])];
218
- const requestedToolNames = input.tools !== undefined
219
- ? [...new Set([...requestedBuiltinTools, ...resolvedMcpSelections.map((selection) => selection.name)])]
430
+ : [
431
+ ...new Set([
432
+ ...runtimeExtensions,
433
+ ...toolExtensionPaths,
434
+ ...(input.subagentOnlyExtensions ?? []),
435
+ ]),
436
+ ];
437
+ const requestedToolNames =
438
+ input.tools !== undefined
439
+ ? [
440
+ ...new Set([
441
+ ...requestedBuiltinTools,
442
+ ...resolvedMcpSelections.map((selection) => selection.name),
443
+ ]),
444
+ ]
445
+ : undefined;
446
+ const capabilityAudit = capabilityCeiling
447
+ ? ({
448
+ ceiling: capabilityCeiling,
449
+ ...(requestedToolNames ? { requestedTools: requestedToolNames } : {}),
450
+ effectiveTools: effectiveToolAllowlist,
451
+ removedTools:
452
+ requestedToolNames?.filter(
453
+ (tool) => !effectiveToolAllowlist.includes(tool),
454
+ ) ?? [],
455
+ internalTools,
456
+ extensionsDenied: capabilityCeiling.denyExtensions,
457
+ removedExtensionCount: capabilityCeiling.denyExtensions
458
+ ? (input.extensions?.length ?? 0) +
459
+ (input.subagentOnlyExtensions?.length ?? 0) +
460
+ (input.tools ?? []).filter(
461
+ (tool) =>
462
+ tool.includes("/") ||
463
+ tool.endsWith(".ts") ||
464
+ tool.endsWith(".js"),
465
+ ).length
466
+ : 0,
467
+ requestedMcpToolCount: input.mcpDirectTools?.length ?? 0,
468
+ effectiveMcpTools,
469
+ agentAllowed:
470
+ input.agentName === undefined
471
+ ? true
472
+ : isAgentAllowedByCapabilityCeiling(
473
+ input.agentName,
474
+ capabilityCeiling,
475
+ ),
476
+ ...(capabilityCeilingAgentRestrictionSources(capabilityCeiling)
477
+ ? {
478
+ agentRestrictionSources:
479
+ capabilityCeilingAgentRestrictionSources(capabilityCeiling),
480
+ }
481
+ : {}),
482
+ } satisfies SubagentCapabilityAudit)
220
483
  : undefined;
221
- const capabilityAudit = capabilityCeiling ? {
222
- ceiling: capabilityCeiling,
223
- ...(requestedToolNames ? { requestedTools: requestedToolNames } : {}),
224
- effectiveTools: effectiveToolAllowlist,
225
- removedTools: requestedToolNames?.filter((tool) => !effectiveToolAllowlist.includes(tool)) ?? [],
226
- internalTools,
227
- extensionsDenied: capabilityCeiling.denyExtensions,
228
- removedExtensionCount: capabilityCeiling.denyExtensions ? (input.extensions?.length ?? 0) + (input.subagentOnlyExtensions?.length ?? 0) + ((input.tools ?? []).filter((tool) => tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js")).length) : 0,
229
- requestedMcpToolCount: input.mcpDirectTools?.length ?? 0,
230
- effectiveMcpTools,
231
- agentAllowed: input.agentName === undefined ? true : isAgentAllowedByCapabilityCeiling(input.agentName, capabilityCeiling),
232
- ...(capabilityCeilingAgentRestrictionSources(capabilityCeiling) ? { agentRestrictionSources: capabilityCeilingAgentRestrictionSources(capabilityCeiling) } : {}),
233
- } satisfies SubagentCapabilityAudit : undefined;
234
484
  return {
235
485
  ...(capabilityCeiling ? { capabilityCeiling } : {}),
236
486
  requestedBuiltinTools,
@@ -252,6 +502,15 @@ export function resolvePiLaunchToolPlan(input: ResolvePiLaunchToolPlanInput): Pi
252
502
  };
253
503
  }
254
504
 
505
+ /** Escape XML-significant characters in a string for safe attribute interpolation. */
506
+ function escapeXmlAttr(value: string): string {
507
+ return value
508
+ .replace(/&/g, "&amp;")
509
+ .replace(/"/g, "&quot;")
510
+ .replace(/</g, "&lt;")
511
+ .replace(/>/g, "&gt;");
512
+ }
513
+
255
514
  export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
256
515
  const args = [...input.baseArgs];
257
516
 
@@ -282,17 +541,23 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
282
541
  requireReadTool: input.requireReadTool,
283
542
  structuredOutput: input.structuredOutput,
284
543
  capabilityCeiling: input.capabilityCeiling,
285
- inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
544
+ inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(
545
+ process.env[SUBAGENT_CAPABILITY_CEILING_ENV],
546
+ ),
286
547
  agentName: input.childAgentName,
287
548
  });
288
549
  if (toolPlan.explicitToolAllowlist) {
289
- args.push(toolPlan.effectiveToolAllowlist.length > 0 ? "--tools" : "--no-tools");
290
- if (toolPlan.effectiveToolAllowlist.length > 0) args.push(toolPlan.effectiveToolAllowlist.join(","));
550
+ args.push(
551
+ toolPlan.effectiveToolAllowlist.length > 0 ? "--tools" : "--no-tools",
552
+ );
553
+ if (toolPlan.effectiveToolAllowlist.length > 0)
554
+ args.push(toolPlan.effectiveToolAllowlist.join(","));
291
555
  }
292
556
  if (toolPlan.disableAmbientExtensions) {
293
557
  args.push("--no-extensions");
294
558
  }
295
- for (const extPath of toolPlan.extensionArgs) args.push("--extension", extPath);
559
+ for (const extPath of toolPlan.extensionArgs)
560
+ args.push("--extension", extPath);
296
561
 
297
562
  if (!input.inheritProjectContext) {
298
563
  args.push("--no-context-files");
@@ -306,8 +571,18 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
306
571
  tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
307
572
  const stem = (input.promptFileStem ?? "prompt").replace(/[^\w.-]/g, "_");
308
573
  const promptPath = path.join(tempDir, `${stem}.md`);
309
- fs.writeFileSync(promptPath, input.systemPrompt, { mode: 0o600 });
310
- args.push(input.systemPromptMode === "replace" ? "--system-prompt" : "--append-system-prompt", promptPath);
574
+ // Inject <active_agent> tag so @gotgenes/pi-permission-system can
575
+ // resolve per-agent policy inside the child session.
576
+ const taggedPrompt = input.childAgentName
577
+ ? `<active_agent name="${escapeXmlAttr(input.childAgentName)}"/>\n\n${input.systemPrompt}`
578
+ : input.systemPrompt;
579
+ fs.writeFileSync(promptPath, taggedPrompt, { mode: 0o600 });
580
+ args.push(
581
+ input.systemPromptMode === "replace"
582
+ ? "--system-prompt"
583
+ : "--append-system-prompt",
584
+ promptPath,
585
+ );
311
586
  }
312
587
 
313
588
  if (input.task.length > TASK_ARG_LIMIT) {
@@ -322,58 +597,106 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
322
597
  }
323
598
 
324
599
  const env: Record<string, string | undefined> = {};
325
- const piPackageRoot = process.env[PI_CODING_AGENT_PACKAGE_ROOT_ENV] ?? resolvePiPackageRoot();
600
+ const piPackageRoot =
601
+ process.env[PI_CODING_AGENT_PACKAGE_ROOT_ENV] ?? resolvePiPackageRoot();
326
602
  if (piPackageRoot) env[PI_CODING_AGENT_PACKAGE_ROOT_ENV] = piPackageRoot;
327
- if (!tempDir) tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
328
- const runtimeAcknowledgedExtensionsPath = path.join(tempDir, "runtime-acknowledged-extensions.json");
603
+ if (!tempDir)
604
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
605
+ const runtimeAcknowledgedExtensionsPath = path.join(
606
+ tempDir,
607
+ "runtime-acknowledged-extensions.json",
608
+ );
329
609
  env[RUNTIME_EXTENSION_ACK_PATH_ENV] = runtimeAcknowledgedExtensionsPath;
330
610
  let toolDiagnosticPath: string | undefined;
331
611
  if (toolPlan.requiredChildTools.length > 0) {
332
- if (!tempDir) tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
612
+ if (!tempDir)
613
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
333
614
  toolDiagnosticPath = path.join(tempDir, "tool-diagnostic.json");
334
615
  env[REQUIRED_CHILD_TOOLS_ENV] = JSON.stringify(toolPlan.requiredChildTools);
335
616
  env[CHILD_TOOL_DIAGNOSTIC_PATH_ENV] = toolDiagnosticPath;
336
617
  }
337
- env[MCP_DIRECT_CHILD_TOOLS_ENV] = toolPlan.effectiveMcpTools.length > 0 ? JSON.stringify(toolPlan.effectiveMcpTools) : undefined;
618
+ env[MCP_DIRECT_CHILD_TOOLS_ENV] =
619
+ toolPlan.effectiveMcpTools.length > 0
620
+ ? JSON.stringify(toolPlan.effectiveMcpTools)
621
+ : undefined;
338
622
  env[SUBAGENT_CHILD_ENV] = "1";
339
623
  env[SUBAGENT_FANOUT_CHILD_ENV] = toolPlan.fanoutAuthorized ? "1" : "0";
340
624
  if (input.waitToolEnabled !== undefined) {
341
625
  env[WAIT_TOOL_ENABLED_ENV] = input.waitToolEnabled ? "true" : "false";
342
626
  }
343
- const inheritedNestedRoute = Boolean(process.env[SUBAGENT_PARENT_EVENT_SINK_ENV] && process.env[SUBAGENT_PARENT_ROOT_RUN_ID_ENV] && process.env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV]);
344
- const parentRunId = input.parentRunId ?? input.runId ?? (inheritedNestedRoute ? process.env[SUBAGENT_RUN_ID_ENV] : undefined) ?? process.env[SUBAGENT_PARENT_RUN_ID_ENV] ?? "";
345
- const parentChildIndex = input.parentChildIndex !== undefined
346
- ? String(input.parentChildIndex)
347
- : input.childIndex !== undefined
348
- ? String(input.childIndex)
349
- : process.env[SUBAGENT_PARENT_CHILD_INDEX_ENV] ?? "";
627
+ const inheritedNestedRoute = Boolean(
628
+ process.env[SUBAGENT_PARENT_EVENT_SINK_ENV] &&
629
+ process.env[SUBAGENT_PARENT_ROOT_RUN_ID_ENV] &&
630
+ process.env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV],
631
+ );
632
+ const parentRunId =
633
+ input.parentRunId ??
634
+ input.runId ??
635
+ (inheritedNestedRoute ? process.env[SUBAGENT_RUN_ID_ENV] : undefined) ??
636
+ process.env[SUBAGENT_PARENT_RUN_ID_ENV] ??
637
+ "";
638
+ const parentChildIndex =
639
+ input.parentChildIndex !== undefined
640
+ ? String(input.parentChildIndex)
641
+ : input.childIndex !== undefined
642
+ ? String(input.childIndex)
643
+ : (process.env[SUBAGENT_PARENT_CHILD_INDEX_ENV] ?? "");
350
644
  const inheritedDepth = Number(process.env[SUBAGENT_PARENT_DEPTH_ENV]);
351
- const parentDepth = input.parentDepth ?? (inheritedNestedRoute && Number.isFinite(inheritedDepth) ? inheritedDepth + 1 : 1);
645
+ const parentDepth =
646
+ input.parentDepth ??
647
+ (inheritedNestedRoute && Number.isFinite(inheritedDepth)
648
+ ? inheritedDepth + 1
649
+ : 1);
352
650
  const parentPath = input.parentPath ?? [
353
651
  ...parseNestedPathEnv(process.env[SUBAGENT_PARENT_PATH_ENV]),
354
- ...(parentRunId ? [{
355
- runId: parentRunId,
356
- ...(parentChildIndex && /^\d+$/.test(parentChildIndex) ? { stepIndex: Number(parentChildIndex) } : {}),
357
- ...(input.childAgentName ? { agent: input.childAgentName } : {}),
358
- }] : []),
652
+ ...(parentRunId
653
+ ? [
654
+ {
655
+ runId: parentRunId,
656
+ ...(parentChildIndex && /^\d+$/.test(parentChildIndex)
657
+ ? { stepIndex: Number(parentChildIndex) }
658
+ : {}),
659
+ ...(input.childAgentName ? { agent: input.childAgentName } : {}),
660
+ },
661
+ ]
662
+ : []),
359
663
  ];
360
664
  env[SUBAGENT_PARENT_EVENT_SINK_ENV] = toolPlan.fanoutAuthorized
361
- ? input.parentEventSink ?? process.env[SUBAGENT_PARENT_EVENT_SINK_ENV] ?? ""
665
+ ? (input.parentEventSink ??
666
+ process.env[SUBAGENT_PARENT_EVENT_SINK_ENV] ??
667
+ "")
362
668
  : "";
363
669
  env[SUBAGENT_PARENT_CONTROL_INBOX_ENV] = toolPlan.fanoutAuthorized
364
- ? input.parentControlInbox ?? process.env[SUBAGENT_PARENT_CONTROL_INBOX_ENV] ?? ""
670
+ ? (input.parentControlInbox ??
671
+ process.env[SUBAGENT_PARENT_CONTROL_INBOX_ENV] ??
672
+ "")
365
673
  : "";
366
674
  env[SUBAGENT_PARENT_ROOT_RUN_ID_ENV] = toolPlan.fanoutAuthorized
367
- ? input.parentRootRunId ?? process.env[SUBAGENT_PARENT_ROOT_RUN_ID_ENV] ?? input.runId ?? ""
675
+ ? (input.parentRootRunId ??
676
+ process.env[SUBAGENT_PARENT_ROOT_RUN_ID_ENV] ??
677
+ input.runId ??
678
+ "")
679
+ : "";
680
+ env[SUBAGENT_PARENT_RUN_ID_ENV] = toolPlan.fanoutAuthorized
681
+ ? parentRunId
682
+ : "";
683
+ env[SUBAGENT_PARENT_CHILD_INDEX_ENV] = toolPlan.fanoutAuthorized
684
+ ? parentChildIndex
685
+ : "";
686
+ env[SUBAGENT_PARENT_DEPTH_ENV] = toolPlan.fanoutAuthorized
687
+ ? String(parentDepth)
688
+ : "";
689
+ env[SUBAGENT_PARENT_PATH_ENV] = toolPlan.fanoutAuthorized
690
+ ? encodeNestedPathEnv(parentPath)
368
691
  : "";
369
- env[SUBAGENT_PARENT_RUN_ID_ENV] = toolPlan.fanoutAuthorized ? parentRunId : "";
370
- env[SUBAGENT_PARENT_CHILD_INDEX_ENV] = toolPlan.fanoutAuthorized ? parentChildIndex : "";
371
- env[SUBAGENT_PARENT_DEPTH_ENV] = toolPlan.fanoutAuthorized ? String(parentDepth) : "";
372
- env[SUBAGENT_PARENT_PATH_ENV] = toolPlan.fanoutAuthorized ? encodeNestedPathEnv(parentPath) : "";
373
692
  env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV] = toolPlan.fanoutAuthorized
374
- ? input.parentCapabilityToken ?? process.env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV] ?? ""
693
+ ? (input.parentCapabilityToken ??
694
+ process.env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV] ??
695
+ "")
375
696
  : "";
376
- env.PI_SUBAGENT_INHERIT_PROJECT_CONTEXT = input.inheritProjectContext ? "1" : "0";
697
+ env.PI_SUBAGENT_INHERIT_PROJECT_CONTEXT = input.inheritProjectContext
698
+ ? "1"
699
+ : "0";
377
700
  env.PI_SUBAGENT_INHERIT_SKILLS = input.inheritSkills ? "1" : "0";
378
701
  env[PI_INTERCOM_STABLE_ID_ENV] = input.intercomSessionName || undefined;
379
702
  env[PI_INTERCOM_SESSION_ID_ENV] = undefined;
@@ -386,13 +709,26 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
386
709
  if (input.parentSessionId) {
387
710
  env[SUBAGENT_ORCHESTRATOR_SESSION_ID_ENV] = input.parentSessionId;
388
711
  }
389
- if (input.orchestratorIntercomTarget && input.parentSessionId && input.runId && input.childAgentName) {
712
+ const encodedPermissionRules = encodePermissionRules(input.permissionRules);
713
+ if (
714
+ input.orchestratorIntercomTarget &&
715
+ input.parentSessionId &&
716
+ input.runId &&
717
+ input.childAgentName
718
+ ) {
390
719
  const childIndex = input.childIndex ?? 0;
391
- const channelDir = supervisorChannelDir(input.runId, input.childAgentName, childIndex);
720
+ const channelDir = supervisorChannelDir(
721
+ input.runId,
722
+ input.childAgentName,
723
+ childIndex,
724
+ );
392
725
  fs.mkdirSync(path.join(channelDir, "requests"), { recursive: true });
393
726
  fs.mkdirSync(path.join(channelDir, "replies"), { recursive: true });
394
727
  env[SUBAGENT_SUPERVISOR_CHANNEL_DIR_ENV] = channelDir;
395
728
  }
729
+ if (encodedPermissionRules)
730
+ env[PERMISSION_AUDIT_PATH_ENV] =
731
+ input.permissionAuditPath ?? path.join(tempDir, "permission-audit.jsonl");
396
732
  if (input.runId) {
397
733
  env[SUBAGENT_RUN_ID_ENV] = input.runId;
398
734
  }
@@ -402,11 +738,24 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
402
738
  if (input.childIndex !== undefined) {
403
739
  env[SUBAGENT_CHILD_INDEX_ENV] = String(input.childIndex);
404
740
  }
405
- if (!toolPlan.capabilityCeiling && input.mcpDirectTools?.length) env.MCP_DIRECT_TOOLS = input.mcpDirectTools.join(",");
406
- else if (toolPlan.capabilityCeiling && toolPlan.effectiveMcpSelections.length && !toolPlan.capabilityCeiling.denyExtensions) env.MCP_DIRECT_TOOLS = toolPlan.effectiveMcpSelections.map((selection) => selection.selector).join(",");
741
+ if (!toolPlan.capabilityCeiling && input.mcpDirectTools?.length)
742
+ env.MCP_DIRECT_TOOLS = input.mcpDirectTools.join(",");
743
+ else if (
744
+ toolPlan.capabilityCeiling &&
745
+ toolPlan.effectiveMcpSelections.length &&
746
+ !toolPlan.capabilityCeiling.denyExtensions
747
+ )
748
+ env.MCP_DIRECT_TOOLS = toolPlan.effectiveMcpSelections
749
+ .map((selection) => selection.selector)
750
+ .join(",");
407
751
  else env.MCP_DIRECT_TOOLS = "__none__";
408
- const encodedCapabilityCeiling = encodeSubagentCapabilityCeiling(toolPlan.capabilityCeiling);
409
- if (encodedCapabilityCeiling) env[SUBAGENT_CAPABILITY_CEILING_ENV] = encodedCapabilityCeiling;
752
+ const encodedCapabilityCeiling = encodeSubagentCapabilityCeiling(
753
+ toolPlan.capabilityCeiling,
754
+ );
755
+ if (encodedCapabilityCeiling)
756
+ env[SUBAGENT_CAPABILITY_CEILING_ENV] = encodedCapabilityCeiling;
757
+ if (encodedPermissionRules)
758
+ env[PERMISSION_POLICY_ENV] = encodedPermissionRules;
410
759
  if (input.structuredOutput) {
411
760
  env[STRUCTURED_OUTPUT_CAPTURE_ENV] = input.structuredOutput.outputPath;
412
761
  env[STRUCTURED_OUTPUT_SCHEMA_ENV] = input.structuredOutput.schemaPath;
@@ -414,17 +763,27 @@ export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
414
763
  if (input.steerInboxDir) {
415
764
  env[SUBAGENT_STEER_INBOX_ENV] = input.steerInboxDir;
416
765
  }
417
- if (input.steerCapabilityPath) env[SUBAGENT_STEER_CAPABILITY_ENV] = input.steerCapabilityPath;
766
+ if (input.steerCapabilityPath)
767
+ env[SUBAGENT_STEER_CAPABILITY_ENV] = input.steerCapabilityPath;
418
768
  if (input.steerAckDir) env[SUBAGENT_STEER_ACK_DIR_ENV] = input.steerAckDir;
419
769
  const encodedToolBudget = encodeToolBudgetEnv(input.toolBudget);
420
770
  if (encodedToolBudget) env[TOOL_BUDGET_ENV] = encodedToolBudget;
421
771
  env[TOOL_BUDGET_ZERO_AUTH_ENV] = input.allowZeroToolBudget ? "1" : undefined;
422
772
  const encodedChildWatchdog = encodeChildWatchdogConfig(input.childWatchdog);
423
- if (encodedChildWatchdog) env[CHILD_WATCHDOG_CONFIG_ENV] = encodedChildWatchdog;
773
+ if (encodedChildWatchdog)
774
+ env[CHILD_WATCHDOG_CONFIG_ENV] = encodedChildWatchdog;
424
775
 
425
- env[SUBAGENT_PARENT_SESSION_ENV] = input.parentSessionId ?? process.env[SUBAGENT_PARENT_SESSION_ENV] ?? "";
776
+ env[SUBAGENT_PARENT_SESSION_ENV] =
777
+ input.parentSessionId ?? process.env[SUBAGENT_PARENT_SESSION_ENV] ?? "";
426
778
 
427
- return { args, env, tempDir, toolDiagnosticPath, runtimeAcknowledgedExtensionsPath, capabilityAudit: toolPlan.capabilityAudit };
779
+ return {
780
+ args,
781
+ env,
782
+ tempDir,
783
+ toolDiagnosticPath,
784
+ runtimeAcknowledgedExtensionsPath,
785
+ capabilityAudit: toolPlan.capabilityAudit,
786
+ };
428
787
  }
429
788
 
430
789
  export const parseParentPathEnv = parseNestedPathEnv;