pi-subagents 0.64.0 → 0.65.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.
- package/CHANGELOG.md +33 -0
- package/README.md +2 -2
- package/agents/reviewer.md +1 -1
- package/agents/scout.md +1 -1
- package/docs/agents.md +18 -16
- package/docs/configuration.md +7 -15
- package/docs/extension-api.md +15 -6
- package/docs/missions.md +2 -0
- package/docs/observability.md +10 -12
- package/docs/tool-reference.md +7 -6
- package/docs/watchdog.md +1 -1
- package/docs/workflows.md +5 -3
- package/package.json +3 -4
- package/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/skills/pi-subagents/references/execution-controls.md +2 -2
- package/src/agents/agent-management.ts +47 -15
- package/src/api/capability-ceiling.ts +0 -1
- package/src/api/{pi-args.ts → child-tool-plan.ts} +1 -1
- package/src/api/preflight.ts +2 -3
- package/src/extension/doctor.ts +2 -10
- package/src/extension/fanout-child.ts +9 -11
- package/src/extension/index.ts +27 -5
- package/src/extension/public-execution.ts +14 -0
- package/src/extension/rpc.ts +3 -2
- package/src/extension/schemas.ts +2 -1
- package/src/extension/tool-description.ts +9 -8
- package/src/intercom/native-supervisor-channel.ts +138 -60
- package/src/intercom/supervisor-ui.ts +243 -0
- package/src/runs/background/async-execution.ts +43 -24
- package/src/runs/background/async-job-tracker.ts +11 -0
- package/src/runs/background/async-resume.ts +11 -2
- package/src/runs/background/control-channel.ts +2 -204
- package/src/runs/background/process-terminal.ts +1 -1
- package/src/runs/background/run-child-session.ts +613 -0
- package/src/runs/background/run-status.ts +0 -1
- package/src/runs/background/runner-aliases.ts +125 -0
- package/src/runs/background/runner-child-sessions.ts +31 -0
- package/src/runs/background/scheduled-runs.ts +18 -4
- package/src/runs/background/subagent-runner.ts +184 -901
- package/src/runs/foreground/async-steering-action.ts +1 -17
- package/src/runs/foreground/execution.ts +189 -377
- package/src/runs/foreground/foreground-control.ts +4 -0
- package/src/runs/foreground/subagent-executor.ts +100 -57
- package/src/runs/foreground/workflow-foreground-steering.ts +24 -98
- package/src/runs/shared/abort-recovery.ts +3 -3
- package/src/runs/shared/capability-ceiling.ts +1 -2
- package/src/runs/shared/child-hooks.ts +25 -0
- package/src/runs/shared/child-identity.ts +13 -2
- package/src/runs/shared/child-launch.ts +314 -0
- package/src/runs/shared/child-lifecycle.ts +25 -0
- package/src/runs/shared/child-runtime-config.ts +126 -0
- package/src/runs/shared/child-session.ts +342 -0
- package/src/runs/shared/child-tool-plan.ts +530 -0
- package/src/runs/shared/claude-code-adapter.ts +5 -1
- package/src/runs/shared/completion-guard.ts +1 -1
- package/src/runs/shared/external-cli-preflight.ts +16 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
- package/src/runs/shared/model-exclusions.ts +82 -14
- package/src/runs/shared/model-fallback.ts +47 -4
- package/src/runs/shared/nested-events.ts +29 -45
- package/src/runs/shared/nested-path.ts +0 -14
- package/src/runs/shared/orca-progress-tabs.ts +12 -7
- package/src/runs/shared/parallel-utils.ts +0 -2
- package/src/runs/shared/permissions.ts +0 -13
- package/src/runs/shared/process-signal.ts +4 -1
- package/src/runs/shared/run-fanout-budget.ts +0 -13
- package/src/runs/shared/runtime-acknowledged-extensions.ts +0 -27
- package/src/runs/shared/structured-output.ts +17 -4
- package/src/runs/shared/subagent-control.ts +2 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +87 -384
- package/src/runs/shared/tool-availability.ts +18 -62
- package/src/runs/shared/tool-budget.ts +0 -14
- package/src/runs/shared/worktree-cleanup-plan.ts +25 -6
- package/src/runs/shared/worktree.ts +117 -30
- package/src/shared/child-session-name.ts +1 -1
- package/src/shared/jsonl-writer.ts +11 -0
- package/src/shared/thinking-ceiling.ts +0 -6
- package/src/shared/types.ts +55 -28
- package/src/shared/utils.ts +3 -4
- package/src/slash/slash-commands.ts +0 -6
- package/src/tui/fleet.ts +0 -1
- package/src/tui/render.ts +233 -31
- package/src/watchdog/child-status.ts +0 -1
- package/src/watchdog/register-child.ts +12 -12
- package/src/workflows/scripted-workflow.ts +48 -1
- package/src/runs/shared/child-protocol.ts +0 -415
- package/src/runs/shared/pi-args.ts +0 -1059
- package/src/runs/shared/subagent-startup-retry.ts +0 -116
- package/src/shared/post-exit-stdio-guard.ts +0 -85
|
@@ -1,1059 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import * as fs from "node:fs";
|
|
3
|
-
import * as os from "node:os";
|
|
4
|
-
import * as path from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
import {
|
|
7
|
-
encodeNestedPathEnv,
|
|
8
|
-
parseNestedPathEnv,
|
|
9
|
-
type NestedPathEntry,
|
|
10
|
-
} from "./nested-path.ts";
|
|
11
|
-
import {
|
|
12
|
-
formatUnresolvedMcpDirectToolSelectors,
|
|
13
|
-
resolveMcpDirectToolResolution,
|
|
14
|
-
type McpConfig,
|
|
15
|
-
type McpRuntimeSnapshotHost,
|
|
16
|
-
type ResolvedMcpDirectToolSelection,
|
|
17
|
-
} from "./mcp-direct-tool-allowlist.ts";
|
|
18
|
-
import { resolvePiPackageRoot } from "./pi-spawn.ts";
|
|
19
|
-
import { encodeExtensionBindings, PI_SUBAGENT_EXTENSION_BINDINGS_ENV, type ExtensionBindings } from "./extension-bindings.ts";
|
|
20
|
-
import { RUNTIME_EXTENSION_ACK_PATH_ENV } from "./runtime-acknowledged-extensions.ts";
|
|
21
|
-
import {
|
|
22
|
-
STRUCTURED_OUTPUT_ACCEPTANCE_CAPTURE_ENV,
|
|
23
|
-
STRUCTURED_OUTPUT_ACCEPTANCE_REQUIRED_ENV,
|
|
24
|
-
STRUCTURED_OUTPUT_CAPTURE_ENV,
|
|
25
|
-
STRUCTURED_OUTPUT_SCHEMA_ENV,
|
|
26
|
-
type StructuredOutputRuntime,
|
|
27
|
-
} from "./structured-output.ts";
|
|
28
|
-
import {
|
|
29
|
-
TEMP_ROOT_DIR,
|
|
30
|
-
type JsonSchemaObject,
|
|
31
|
-
type LaunchResolvedChildExtensionsV1,
|
|
32
|
-
type ResolvedToolBudget,
|
|
33
|
-
type RunFanoutBudgetDescriptor,
|
|
34
|
-
} from "../../shared/types.ts";
|
|
35
|
-
import { THINKING_LEVELS } from "../../shared/model-info.ts";
|
|
36
|
-
import { decodeThinkingCeiling, intersectThinkingCeilings, SUBAGENT_THINKING_CEILING_ENV } from "../../shared/thinking-ceiling.ts";
|
|
37
|
-
import { encodeRunFanoutBudgetDescriptor, RUN_FANOUT_BUDGET_ENV } from "./run-fanout-budget.ts";
|
|
38
|
-
import {
|
|
39
|
-
TOOL_BUDGET_ENV,
|
|
40
|
-
TOOL_BUDGET_ZERO_AUTH_ENV,
|
|
41
|
-
encodeToolBudgetEnv,
|
|
42
|
-
} from "./tool-budget.ts";
|
|
43
|
-
import {
|
|
44
|
-
CHILD_TOOL_DIAGNOSTIC_PATH_ENV,
|
|
45
|
-
MCP_DIRECT_CHILD_TOOLS_ENV,
|
|
46
|
-
REQUIRED_CHILD_TOOLS_ENV,
|
|
47
|
-
} from "./tool-availability.ts";
|
|
48
|
-
import {
|
|
49
|
-
CHILD_WATCHDOG_CONFIG_ENV,
|
|
50
|
-
type ChildWatchdogConfig,
|
|
51
|
-
} from "../../watchdog/child-status.ts";
|
|
52
|
-
import { WAIT_TOOL_DEFAULT_TIMEOUT_MS_ENV, WAIT_TOOL_ENABLED_ENV } from "../background/wait-config.ts";
|
|
53
|
-
import {
|
|
54
|
-
PI_CODING_AGENT_PACKAGE_ROOT_ENV,
|
|
55
|
-
getAgentDir,
|
|
56
|
-
} from "../../shared/utils.ts";
|
|
57
|
-
import {
|
|
58
|
-
encodePermissionRules,
|
|
59
|
-
PERMISSION_AUDIT_PATH_ENV,
|
|
60
|
-
PERMISSION_POLICY_ENV,
|
|
61
|
-
type PermissionRules,
|
|
62
|
-
} from "./permissions.ts";
|
|
63
|
-
import {
|
|
64
|
-
SUBAGENT_CAPABILITY_CEILING_ENV,
|
|
65
|
-
capabilityCeilingAgentRestrictionSources,
|
|
66
|
-
decodeSubagentCapabilityCeiling,
|
|
67
|
-
encodeSubagentCapabilityCeiling,
|
|
68
|
-
intersectSubagentCapabilityCeilings,
|
|
69
|
-
isAgentAllowedByCapabilityCeiling,
|
|
70
|
-
type ResolvedSubagentCapabilityCeiling,
|
|
71
|
-
type SubagentCapabilityAudit,
|
|
72
|
-
} from "./capability-ceiling.ts";
|
|
73
|
-
|
|
74
|
-
const TASK_ARG_LIMIT = 8000;
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Env override for how the task text reaches the child process. Endpoint
|
|
78
|
-
* protection (EDR) pre-execution command-line scanning may deny exec of
|
|
79
|
-
* children whose argv embeds a long natural-language task, which surfaces
|
|
80
|
-
* as an immediate zero-activity SIGKILL. File delivery keeps the task out
|
|
81
|
-
* of argv entirely.
|
|
82
|
-
*/
|
|
83
|
-
export const SUBAGENT_TASK_DELIVERY_ENV = "PI_SUBAGENT_TASK_DELIVERY";
|
|
84
|
-
|
|
85
|
-
export type SubagentTaskDelivery = "auto" | "file";
|
|
86
|
-
|
|
87
|
-
export function resolveSubagentTaskDelivery(
|
|
88
|
-
env: NodeJS.ProcessEnv = process.env,
|
|
89
|
-
): SubagentTaskDelivery {
|
|
90
|
-
return env[SUBAGENT_TASK_DELIVERY_ENV]?.trim().toLowerCase() === "file"
|
|
91
|
-
? "file"
|
|
92
|
-
: "auto";
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function shouldDeliverTaskViaFile(
|
|
96
|
-
task: string,
|
|
97
|
-
delivery: SubagentTaskDelivery,
|
|
98
|
-
platform: NodeJS.Platform = process.platform,
|
|
99
|
-
): boolean {
|
|
100
|
-
return delivery === "file" || platform === "darwin" || task.length > TASK_ARG_LIMIT;
|
|
101
|
-
}
|
|
102
|
-
const MAX_LAUNCH_RESOLVED_EXTENSION_IDS = 32;
|
|
103
|
-
const PROMPT_RUNTIME_EXTENSION_PATH = path.join(
|
|
104
|
-
path.dirname(fileURLToPath(import.meta.url)),
|
|
105
|
-
"subagent-prompt-runtime.ts",
|
|
106
|
-
);
|
|
107
|
-
const FANOUT_CHILD_EXTENSION_PATH = path.join(
|
|
108
|
-
path.dirname(fileURLToPath(import.meta.url)),
|
|
109
|
-
"..",
|
|
110
|
-
"..",
|
|
111
|
-
"extension",
|
|
112
|
-
"fanout-child.ts",
|
|
113
|
-
);
|
|
114
|
-
const FAST_MODE_EXTENSION_PATH = path.join(
|
|
115
|
-
path.dirname(fileURLToPath(import.meta.url)),
|
|
116
|
-
"fast-mode-extension.ts",
|
|
117
|
-
);
|
|
118
|
-
const FAST_MODE_ALLOWED_MODELS = new Set([
|
|
119
|
-
"openai-codex/gpt-5.6-luna",
|
|
120
|
-
"openai-codex/gpt-5.6-sol",
|
|
121
|
-
]);
|
|
122
|
-
const OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH = 64;
|
|
123
|
-
export const SUBAGENT_CHILD_ENV = "PI_SUBAGENT_CHILD";
|
|
124
|
-
export const SUBAGENT_ORCHESTRATOR_TARGET_ENV =
|
|
125
|
-
"PI_SUBAGENT_ORCHESTRATOR_TARGET";
|
|
126
|
-
export const SUBAGENT_ORCHESTRATOR_SESSION_ID_ENV =
|
|
127
|
-
"PI_SUBAGENT_ORCHESTRATOR_SESSION_ID";
|
|
128
|
-
export const SUBAGENT_SUPERVISOR_CHANNEL_DIR_ENV =
|
|
129
|
-
"PI_SUBAGENT_SUPERVISOR_CHANNEL_DIR";
|
|
130
|
-
export const SUBAGENT_RUN_ID_ENV = "PI_SUBAGENT_RUN_ID";
|
|
131
|
-
export const SUBAGENT_CHILD_AGENT_ENV = "PI_SUBAGENT_CHILD_AGENT";
|
|
132
|
-
export const SUBAGENT_CHILD_INDEX_ENV = "PI_SUBAGENT_CHILD_INDEX";
|
|
133
|
-
export const SUBAGENT_FANOUT_CHILD_ENV = "PI_SUBAGENT_FANOUT_CHILD";
|
|
134
|
-
export const SUBAGENT_PARENT_EVENT_SINK_ENV = "PI_SUBAGENT_PARENT_EVENT_SINK";
|
|
135
|
-
export const SUBAGENT_PARENT_CONTROL_INBOX_ENV =
|
|
136
|
-
"PI_SUBAGENT_PARENT_CONTROL_INBOX";
|
|
137
|
-
export const SUBAGENT_PARENT_ROOT_RUN_ID_ENV = "PI_SUBAGENT_PARENT_ROOT_RUN_ID";
|
|
138
|
-
export const SUBAGENT_PARENT_RUN_ID_ENV = "PI_SUBAGENT_PARENT_RUN_ID";
|
|
139
|
-
export const SUBAGENT_PARENT_CHILD_INDEX_ENV = "PI_SUBAGENT_PARENT_CHILD_INDEX";
|
|
140
|
-
export const SUBAGENT_PARENT_DEPTH_ENV = "PI_SUBAGENT_PARENT_DEPTH";
|
|
141
|
-
export const SUBAGENT_PARENT_PATH_ENV = "PI_SUBAGENT_PARENT_PATH";
|
|
142
|
-
export const SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV =
|
|
143
|
-
"PI_SUBAGENT_PARENT_CAPABILITY_TOKEN";
|
|
144
|
-
export const SUBAGENT_PARENT_SESSION_ENV = "PI_SUBAGENT_PARENT_SESSION";
|
|
145
|
-
export const SUBAGENT_FORK_CACHE_KEY_ENV = "PI_SUBAGENT_FORK_CACHE_KEY";
|
|
146
|
-
export const SUBAGENT_STEER_INBOX_ENV = "PI_SUBAGENT_STEER_INBOX";
|
|
147
|
-
export const SUBAGENT_STEER_CAPABILITY_ENV = "PI_SUBAGENT_STEER_CAPABILITY";
|
|
148
|
-
export const SUBAGENT_STEER_ACK_DIR_ENV = "PI_SUBAGENT_STEER_ACK_DIR";
|
|
149
|
-
export const PI_INTERCOM_STABLE_ID_ENV = "PI_INTERCOM_STABLE_ID";
|
|
150
|
-
export const PI_INTERCOM_SESSION_ID_ENV = "PI_INTERCOM_SESSION_ID";
|
|
151
|
-
export const SUBAGENT_INHERIT_GLOBAL_CONTEXT_ENV = "PI_SUBAGENT_INHERIT_GLOBAL_CONTEXT";
|
|
152
|
-
|
|
153
|
-
export function deriveForkPromptCacheKey(parentSessionId: string | undefined): string | undefined {
|
|
154
|
-
const parent = parentSessionId?.trim();
|
|
155
|
-
if (!parent) return undefined;
|
|
156
|
-
const digest = createHash("sha256").update(parent).digest("hex").slice(0, OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH - "pi-fork:".length);
|
|
157
|
-
return `pi-fork:${digest}`;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export interface BuildPiArgsInput {
|
|
161
|
-
parentSessionId?: string;
|
|
162
|
-
forkCacheKey?: string;
|
|
163
|
-
baseArgs: string[];
|
|
164
|
-
task: string;
|
|
165
|
-
sessionEnabled: boolean;
|
|
166
|
-
sessionDir?: string;
|
|
167
|
-
sessionFile?: string;
|
|
168
|
-
model?: string;
|
|
169
|
-
thinking?: string | false;
|
|
170
|
-
systemPromptMode?: "append" | "replace";
|
|
171
|
-
inheritProjectContext: boolean;
|
|
172
|
-
inheritGlobalContext: boolean;
|
|
173
|
-
inheritSkills: boolean;
|
|
174
|
-
requireReadTool?: boolean;
|
|
175
|
-
tools?: string[];
|
|
176
|
-
excludeTools?: string[];
|
|
177
|
-
extensions?: string[];
|
|
178
|
-
subagentOnlyExtensions?: string[];
|
|
179
|
-
systemPrompt?: string | null;
|
|
180
|
-
mcpDirectTools?: string[];
|
|
181
|
-
mcpConfig?: McpConfig;
|
|
182
|
-
runtimeServerNames?: string[];
|
|
183
|
-
cwd?: string;
|
|
184
|
-
promptFileStem?: string;
|
|
185
|
-
intercomSessionName?: string;
|
|
186
|
-
/** Human-readable display name for the child session (agent + task excerpt,
|
|
187
|
-
* derived by the caller). Passed to the child as PI_SUBAGENT_SESSION_NAME;
|
|
188
|
-
* the prompt runtime applies it via pi.setSessionName unless an intercom
|
|
189
|
-
* target takes precedence. */
|
|
190
|
-
sessionName?: string;
|
|
191
|
-
orchestratorIntercomTarget?: string;
|
|
192
|
-
runId?: string;
|
|
193
|
-
childAgentName?: string;
|
|
194
|
-
childIndex?: number;
|
|
195
|
-
parentEventSink?: string;
|
|
196
|
-
parentControlInbox?: string;
|
|
197
|
-
parentRootRunId?: string;
|
|
198
|
-
parentRunId?: string;
|
|
199
|
-
parentChildIndex?: number;
|
|
200
|
-
parentDepth?: number;
|
|
201
|
-
parentPath?: NestedPathEntry[];
|
|
202
|
-
parentCapabilityToken?: string;
|
|
203
|
-
runFanoutBudget?: RunFanoutBudgetDescriptor;
|
|
204
|
-
steerInboxDir?: string;
|
|
205
|
-
steerCapabilityPath?: string;
|
|
206
|
-
steerAckDir?: string;
|
|
207
|
-
structuredOutput?: StructuredOutputRuntime;
|
|
208
|
-
fast?: boolean;
|
|
209
|
-
modelCandidates?: readonly string[];
|
|
210
|
-
toolBudget?: ResolvedToolBudget;
|
|
211
|
-
allowZeroToolBudget?: boolean;
|
|
212
|
-
permissionRules?: PermissionRules;
|
|
213
|
-
permissionAuditPath?: string;
|
|
214
|
-
childWatchdog?: ChildWatchdogConfig;
|
|
215
|
-
/**
|
|
216
|
-
* Per-launch override of the task delivery mode. Startup-retry paths set
|
|
217
|
-
* this to "file" after an unexplained zero-activity SIGKILL so the retry
|
|
218
|
-
* keeps the task text out of the child's argv.
|
|
219
|
-
*/
|
|
220
|
-
taskDelivery?: SubagentTaskDelivery;
|
|
221
|
-
waitToolEnabled?: boolean;
|
|
222
|
-
waitToolDefaultTimeoutMs?: number;
|
|
223
|
-
allowNestedSubagents?: boolean;
|
|
224
|
-
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
225
|
-
thinkingCeiling?: import("../../shared/model-info.ts").ThinkingLevel;
|
|
226
|
-
extensionBindings?: ExtensionBindings;
|
|
227
|
-
runtimeSnapshotHost?: McpRuntimeSnapshotHost;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
export interface BuildPiArgsResult {
|
|
231
|
-
args: string[];
|
|
232
|
-
env: Record<string, string | undefined>;
|
|
233
|
-
warnings: string[];
|
|
234
|
-
tempDir?: string;
|
|
235
|
-
toolDiagnosticPath?: string;
|
|
236
|
-
runtimeAcknowledgedExtensionsPath?: string;
|
|
237
|
-
capabilityAudit?: SubagentCapabilityAudit;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function sanitizeSupervisorChannelSegment(value: string): string {
|
|
241
|
-
return (
|
|
242
|
-
value
|
|
243
|
-
.trim()
|
|
244
|
-
.replace(/[^A-Za-z0-9._-]+/g, "-")
|
|
245
|
-
.replace(/^-+|-+$/g, "") || "unknown"
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function supervisorChannelDir(
|
|
250
|
-
runId: string,
|
|
251
|
-
agent: string,
|
|
252
|
-
childIndex: number,
|
|
253
|
-
): string {
|
|
254
|
-
return path.join(
|
|
255
|
-
TEMP_ROOT_DIR,
|
|
256
|
-
"supervisor-channels",
|
|
257
|
-
`${sanitizeSupervisorChannelSegment(runId)}-${sanitizeSupervisorChannelSegment(agent)}-${childIndex}`,
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
export function applyThinkingSuffix(
|
|
262
|
-
model: string | undefined,
|
|
263
|
-
thinking: string | false | undefined,
|
|
264
|
-
replaceExisting = false,
|
|
265
|
-
): string | undefined {
|
|
266
|
-
if (!model || !thinking) return model;
|
|
267
|
-
const colonIdx = model.lastIndexOf(":");
|
|
268
|
-
if (
|
|
269
|
-
colonIdx !== -1 &&
|
|
270
|
-
THINKING_LEVELS.some((level) => level === model.substring(colonIdx + 1))
|
|
271
|
-
) {
|
|
272
|
-
return replaceExisting ? `${model.slice(0, colonIdx)}:${thinking}` : model;
|
|
273
|
-
}
|
|
274
|
-
return `${model}:${thinking}`;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function stripThinkingSuffix(model: string): string {
|
|
278
|
-
const colonIdx = model.lastIndexOf(":");
|
|
279
|
-
if (colonIdx === -1) return model;
|
|
280
|
-
return THINKING_LEVELS.some((level) => level === model.substring(colonIdx + 1))
|
|
281
|
-
? model.slice(0, colonIdx)
|
|
282
|
-
: model;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
function resolveFastModeExtension(input: Pick<ResolvePiLaunchToolPlanInput, "fast" | "model" | "modelCandidates" | "agentName">): string[] {
|
|
286
|
-
if (!input.fast) return [];
|
|
287
|
-
const candidates = (input.modelCandidates?.length ? input.modelCandidates : input.model ? [input.model] : [])
|
|
288
|
-
.map(stripThinkingSuffix);
|
|
289
|
-
if (candidates.length === 0) {
|
|
290
|
-
throw new Error(`fast mode requires an explicit supported native OpenAI-Codex model${input.agentName ? ` for agent '${input.agentName}'` : ""}.`);
|
|
291
|
-
}
|
|
292
|
-
const unsupported = candidates.filter((model) => !FAST_MODE_ALLOWED_MODELS.has(model));
|
|
293
|
-
if (unsupported.length > 0) {
|
|
294
|
-
throw new Error(`fast mode supports only ${[...FAST_MODE_ALLOWED_MODELS].join(", ")}; unsupported model${unsupported.length === 1 ? "" : "s"}: ${unsupported.join(", ")}.`);
|
|
295
|
-
}
|
|
296
|
-
return [FAST_MODE_EXTENSION_PATH];
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
export interface ResolvePiLaunchToolPlanInput {
|
|
300
|
-
tools?: string[];
|
|
301
|
-
excludeTools?: string[];
|
|
302
|
-
allowNestedSubagents?: boolean;
|
|
303
|
-
extensions?: string[];
|
|
304
|
-
subagentOnlyExtensions?: string[];
|
|
305
|
-
mcpDirectTools?: string[];
|
|
306
|
-
mcpConfig?: McpConfig;
|
|
307
|
-
runtimeServerNames?: string[];
|
|
308
|
-
cwd?: string;
|
|
309
|
-
requireReadTool?: boolean;
|
|
310
|
-
structuredOutput?:
|
|
311
|
-
| boolean
|
|
312
|
-
| {
|
|
313
|
-
schema: JsonSchemaObject;
|
|
314
|
-
schemaPath: string;
|
|
315
|
-
outputPath: string;
|
|
316
|
-
};
|
|
317
|
-
fast?: boolean;
|
|
318
|
-
model?: string;
|
|
319
|
-
modelCandidates?: readonly string[];
|
|
320
|
-
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
321
|
-
inheritedCapabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
322
|
-
agentName?: string;
|
|
323
|
-
permissionRules?: PermissionRules;
|
|
324
|
-
runtimeSnapshotHost?: McpRuntimeSnapshotHost;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
export interface PiLaunchToolPlan {
|
|
328
|
-
capabilityCeiling?: ResolvedSubagentCapabilityCeiling;
|
|
329
|
-
requestedBuiltinTools: string[];
|
|
330
|
-
declaredBuiltinTools: string[];
|
|
331
|
-
excludeTools: string[];
|
|
332
|
-
toolExtensionPaths: string[];
|
|
333
|
-
resolvedMcpSelections: ResolvedMcpDirectToolSelection[];
|
|
334
|
-
effectiveMcpSelections: ResolvedMcpDirectToolSelection[];
|
|
335
|
-
effectiveMcpTools: string[];
|
|
336
|
-
explicitToolAllowlist: boolean;
|
|
337
|
-
internalTools: string[];
|
|
338
|
-
effectiveToolAllowlist: string[];
|
|
339
|
-
requiredChildTools: string[];
|
|
340
|
-
fanoutAuthorized: boolean;
|
|
341
|
-
runtimeExtensions: string[];
|
|
342
|
-
configuredExtensions: string[];
|
|
343
|
-
extensionArgs: string[];
|
|
344
|
-
disableAmbientExtensions: boolean;
|
|
345
|
-
capabilityAudit?: SubagentCapabilityAudit;
|
|
346
|
-
/** Non-fatal launch warnings; they do not change behavior. */
|
|
347
|
-
warnings: string[];
|
|
348
|
-
mcpConfig?: McpConfig;
|
|
349
|
-
runtimeServerNames?: string[];
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
function extensionIdentifier(value: string): string {
|
|
353
|
-
return `sha256:${createHash("sha256").update(path.normalize(value.trim())).digest("hex").slice(0, 16)}`;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
function boundedExtensionIdentifiers(values: string[]): {
|
|
357
|
-
ids: string[];
|
|
358
|
-
omitted: number;
|
|
359
|
-
} {
|
|
360
|
-
const ids = [...new Set(values.map(extensionIdentifier))];
|
|
361
|
-
return {
|
|
362
|
-
ids: ids.slice(0, MAX_LAUNCH_RESOLVED_EXTENSION_IDS),
|
|
363
|
-
omitted: Math.max(0, ids.length - MAX_LAUNCH_RESOLVED_EXTENSION_IDS),
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
function hasPermissionRules(rules: PermissionRules | undefined): boolean {
|
|
368
|
-
return rules !== undefined && Object.keys(rules).length > 0;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
function filterRuntimeMcpConfig(
|
|
372
|
-
config: McpConfig,
|
|
373
|
-
runtimeServerNames: readonly string[],
|
|
374
|
-
effectiveRuntimeServerNames: readonly string[],
|
|
375
|
-
): McpConfig {
|
|
376
|
-
const runtimeNames = new Set(runtimeServerNames);
|
|
377
|
-
const effectiveNames = new Set(effectiveRuntimeServerNames);
|
|
378
|
-
return {
|
|
379
|
-
...config,
|
|
380
|
-
mcpServers: Object.fromEntries(
|
|
381
|
-
Object.entries(config.mcpServers).filter(([name]) => !runtimeNames.has(name) || effectiveNames.has(name)),
|
|
382
|
-
),
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
export function projectLaunchResolvedChildExtensions(
|
|
387
|
-
toolPlan: Pick<
|
|
388
|
-
PiLaunchToolPlan,
|
|
389
|
-
| "runtimeExtensions"
|
|
390
|
-
| "configuredExtensions"
|
|
391
|
-
| "extensionArgs"
|
|
392
|
-
| "disableAmbientExtensions"
|
|
393
|
-
>,
|
|
394
|
-
): LaunchResolvedChildExtensionsV1 {
|
|
395
|
-
const runtime = boundedExtensionIdentifiers(toolPlan.runtimeExtensions);
|
|
396
|
-
const configured = boundedExtensionIdentifiers(toolPlan.configuredExtensions);
|
|
397
|
-
const effective = boundedExtensionIdentifiers(toolPlan.extensionArgs);
|
|
398
|
-
return {
|
|
399
|
-
version: 1,
|
|
400
|
-
source: "launch-resolved",
|
|
401
|
-
disableAmbientExtensions: toolPlan.disableAmbientExtensions,
|
|
402
|
-
runtime: runtime.ids,
|
|
403
|
-
configured: configured.ids,
|
|
404
|
-
effective: effective.ids,
|
|
405
|
-
omitted: {
|
|
406
|
-
runtime: runtime.omitted,
|
|
407
|
-
configured: configured.omitted,
|
|
408
|
-
effective: effective.omitted,
|
|
409
|
-
},
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
/**
|
|
414
|
-
* Resolve the permission-system extension entry point when installed.
|
|
415
|
-
* Returns the absolute path to the extension's main module, or undefined
|
|
416
|
-
* when the package is not installed. Callers can check `autoInject` config
|
|
417
|
-
* to decide whether to include it in child processes.
|
|
418
|
-
*/
|
|
419
|
-
export function resolvePermissionSystemExtension(): string | undefined {
|
|
420
|
-
const agentDir = getAgentDir();
|
|
421
|
-
const candidates = [
|
|
422
|
-
// npm-scoped package (most common)
|
|
423
|
-
path.join(
|
|
424
|
-
agentDir,
|
|
425
|
-
"npm",
|
|
426
|
-
"node_modules",
|
|
427
|
-
"@gotgenes",
|
|
428
|
-
"pi-permission-system",
|
|
429
|
-
),
|
|
430
|
-
// direct extension directory (some layouts)
|
|
431
|
-
path.join(agentDir, "extensions", "pi-permission-system"),
|
|
432
|
-
];
|
|
433
|
-
const errors: Error[] = [];
|
|
434
|
-
for (const extDir of candidates) {
|
|
435
|
-
if (!fs.existsSync(extDir)) continue;
|
|
436
|
-
const pkgPath = path.join(extDir, "package.json");
|
|
437
|
-
if (!fs.existsSync(pkgPath)) {
|
|
438
|
-
errors.push(new Error(`Permission-system package manifest is missing at ${pkgPath}.`));
|
|
439
|
-
continue;
|
|
440
|
-
}
|
|
441
|
-
try {
|
|
442
|
-
let pkg: { pi?: { extensions?: string[] } };
|
|
443
|
-
const parsed: unknown = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
444
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
445
|
-
throw new Error("manifest root must be an object");
|
|
446
|
-
}
|
|
447
|
-
pkg = parsed as typeof pkg;
|
|
448
|
-
const extensions = pkg.pi?.extensions;
|
|
449
|
-
const entry = Array.isArray(extensions) ? extensions[0] : undefined;
|
|
450
|
-
if (typeof entry !== "string" || !entry.trim()) {
|
|
451
|
-
throw new Error(
|
|
452
|
-
`Permission-system package manifest at ${pkgPath} must declare pi.extensions[0] as a non-empty string.`,
|
|
453
|
-
);
|
|
454
|
-
}
|
|
455
|
-
const resolved = path.resolve(extDir, entry);
|
|
456
|
-
if (fs.existsSync(resolved)) return resolved;
|
|
457
|
-
throw new Error(
|
|
458
|
-
`Permission-system extension entry ${JSON.stringify(entry)} in ${pkgPath} does not exist at ${resolved}.`,
|
|
459
|
-
);
|
|
460
|
-
} catch (error) {
|
|
461
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
462
|
-
errors.push(message.startsWith("Permission-system") ? new Error(message) : new Error(`Cannot read permission-system package manifest at ${pkgPath}: ${message}`));
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
if (errors.length > 0) throw errors[0]!;
|
|
466
|
-
return undefined;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
export function resolvePiLaunchToolPlan(
|
|
470
|
-
input: ResolvePiLaunchToolPlanInput,
|
|
471
|
-
): PiLaunchToolPlan {
|
|
472
|
-
const capabilityCeiling = intersectSubagentCapabilityCeilings(
|
|
473
|
-
input.capabilityCeiling,
|
|
474
|
-
input.inheritedCapabilityCeiling,
|
|
475
|
-
);
|
|
476
|
-
const allowedToolSet =
|
|
477
|
-
capabilityCeiling?.allowedTools === undefined
|
|
478
|
-
? undefined
|
|
479
|
-
: new Set(capabilityCeiling.allowedTools);
|
|
480
|
-
const requestedBuiltinTools =
|
|
481
|
-
input.tools?.filter(
|
|
482
|
-
(tool) =>
|
|
483
|
-
!(tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js")),
|
|
484
|
-
) ?? [];
|
|
485
|
-
if (input.requireReadTool && allowedToolSet && !allowedToolSet.has("read")) {
|
|
486
|
-
throw new Error(
|
|
487
|
-
`Capability ceiling from ${capabilityCeiling?.sources.join(", ") || "unknown source"} excludes required tool 'read' for lazy skill loading.`,
|
|
488
|
-
);
|
|
489
|
-
}
|
|
490
|
-
const declaredBuiltinTools =
|
|
491
|
-
input.tools === undefined
|
|
492
|
-
? allowedToolSet
|
|
493
|
-
? [...allowedToolSet]
|
|
494
|
-
: []
|
|
495
|
-
: (input.requireReadTool &&
|
|
496
|
-
requestedBuiltinTools.length > 0 &&
|
|
497
|
-
!requestedBuiltinTools.includes("read") &&
|
|
498
|
-
!allowedToolSet
|
|
499
|
-
? ["read", ...requestedBuiltinTools]
|
|
500
|
-
: requestedBuiltinTools
|
|
501
|
-
).filter((tool) => !allowedToolSet || allowedToolSet.has(tool));
|
|
502
|
-
const excludeTools = [...new Set((input.excludeTools ?? []).map((tool) => tool.trim()).filter(Boolean))];
|
|
503
|
-
const excludedToolSet = new Set(excludeTools);
|
|
504
|
-
const effectiveDeclaredBuiltinTools = declaredBuiltinTools.filter((tool) => !excludedToolSet.has(tool));
|
|
505
|
-
const fanoutAuthorized = effectiveDeclaredBuiltinTools.includes("subagent") || (
|
|
506
|
-
input.allowNestedSubagents === true &&
|
|
507
|
-
!excludedToolSet.has("subagent") &&
|
|
508
|
-
(!allowedToolSet || allowedToolSet.has("subagent"))
|
|
509
|
-
);
|
|
510
|
-
const toolExtensionPaths: string[] = capabilityCeiling?.denyExtensions
|
|
511
|
-
? []
|
|
512
|
-
: (input.tools ?? []).filter(
|
|
513
|
-
(tool) =>
|
|
514
|
-
!requestedBuiltinTools.includes(tool) &&
|
|
515
|
-
(tool.includes("/") || tool.endsWith(".ts") || tool.endsWith(".js")),
|
|
516
|
-
);
|
|
517
|
-
const mcpResolution = capabilityCeiling?.denyExtensions
|
|
518
|
-
? { selections: [], unresolvedSelectors: [] }
|
|
519
|
-
: resolveMcpDirectToolResolution(input.mcpDirectTools, input.cwd, input.runtimeSnapshotHost, input.mcpConfig);
|
|
520
|
-
if (mcpResolution.unresolvedSelectors.length > 0) {
|
|
521
|
-
throw new Error(formatUnresolvedMcpDirectToolSelectors(mcpResolution.unresolvedSelectors));
|
|
522
|
-
}
|
|
523
|
-
const resolvedMcpSelections = mcpResolution.selections;
|
|
524
|
-
const resolvedMcpNames = new Set(resolvedMcpSelections.map((selection) => selection.name));
|
|
525
|
-
const legacyMcpNameCounts = countLegacyUnderscoreMcpToolNames(resolvedMcpSelections);
|
|
526
|
-
const effectiveMcpSelections = resolvedMcpSelections.filter(
|
|
527
|
-
(selection) =>
|
|
528
|
-
!allowedToolSet ||
|
|
529
|
-
allowedToolSet.has(selection.name) ||
|
|
530
|
-
isLegacyUnderscoreMcpToolAllowed(selection, allowedToolSet, resolvedMcpNames, legacyMcpNameCounts),
|
|
531
|
-
).filter((selection) => !excludedToolSet.has(selection.name));
|
|
532
|
-
const effectiveMcpTools = effectiveMcpSelections.map(
|
|
533
|
-
(selection) => selection.name,
|
|
534
|
-
);
|
|
535
|
-
const runtimeServerNames = mcpResolution.runtimeServerNames ?? input.runtimeServerNames ?? [];
|
|
536
|
-
const effectiveRuntimeServerNames = runtimeServerNames.filter((serverName) =>
|
|
537
|
-
effectiveMcpSelections.some((selection) => selection.selector.startsWith(`${serverName}/`)),
|
|
538
|
-
);
|
|
539
|
-
const effectiveMcpConfig = mcpResolution.mcpConfig ?? input.mcpConfig;
|
|
540
|
-
const filteredMcpConfig = effectiveMcpConfig
|
|
541
|
-
? filterRuntimeMcpConfig(effectiveMcpConfig, runtimeServerNames, effectiveRuntimeServerNames)
|
|
542
|
-
: undefined;
|
|
543
|
-
const explicitToolAllowlist =
|
|
544
|
-
input.tools !== undefined ||
|
|
545
|
-
(input.mcpDirectTools?.length ?? 0) > 0 ||
|
|
546
|
-
allowedToolSet !== undefined;
|
|
547
|
-
const internalTools = (input.structuredOutput ? ["structured_output"] : []).filter((tool) => !excludedToolSet.has(tool));
|
|
548
|
-
const effectiveToolAllowlist = [
|
|
549
|
-
...new Set([
|
|
550
|
-
...effectiveDeclaredBuiltinTools,
|
|
551
|
-
...effectiveMcpTools,
|
|
552
|
-
...internalTools,
|
|
553
|
-
]),
|
|
554
|
-
];
|
|
555
|
-
// Supervisor-coordination names stay in the --tools allowlist but are never
|
|
556
|
-
// strict requirements: children register contact_supervisor at runtime through
|
|
557
|
-
// the native supervisor channel (or pi-intercom). The pre-0.50 bridge always
|
|
558
|
-
// appended intercom alongside contact_supervisor, so that exact pairing is
|
|
559
|
-
// legacy plumbing, not a user demand for an external intercom provider;
|
|
560
|
-
// a lone intercom entry stays strictly required (#1207).
|
|
561
|
-
const legacySupervisorPairing = effectiveDeclaredBuiltinTools.includes("contact_supervisor");
|
|
562
|
-
const requiredChildTools = explicitToolAllowlist
|
|
563
|
-
? [
|
|
564
|
-
...new Set([
|
|
565
|
-
...(input.tools !== undefined ? effectiveDeclaredBuiltinTools : []),
|
|
566
|
-
...(input.mcpDirectTools?.length ? effectiveMcpTools : []),
|
|
567
|
-
...internalTools,
|
|
568
|
-
].filter((tool) => tool !== "contact_supervisor" && (!legacySupervisorPairing || tool !== "intercom"))),
|
|
569
|
-
]
|
|
570
|
-
: [];
|
|
571
|
-
const permSystemExt = capabilityCeiling?.denyExtensions
|
|
572
|
-
? undefined
|
|
573
|
-
: hasPermissionRules(input.permissionRules)
|
|
574
|
-
? resolvePermissionSystemExtension()
|
|
575
|
-
: undefined;
|
|
576
|
-
if (input.fast && capabilityCeiling?.denyExtensions) throw new Error("fast mode requires a child runtime extension, but this launch denies extensions.");
|
|
577
|
-
const fastModeExtensions = resolveFastModeExtension({ fast: input.fast, model: input.model, modelCandidates: input.modelCandidates, agentName: input.agentName });
|
|
578
|
-
const runtimeExtensions = [
|
|
579
|
-
PROMPT_RUNTIME_EXTENSION_PATH,
|
|
580
|
-
...fastModeExtensions,
|
|
581
|
-
...(fanoutAuthorized ? [FANOUT_CHILD_EXTENSION_PATH] : []),
|
|
582
|
-
...(permSystemExt ? [permSystemExt] : []),
|
|
583
|
-
];
|
|
584
|
-
const disableAmbientExtensions =
|
|
585
|
-
capabilityCeiling?.denyExtensions === true ||
|
|
586
|
-
input.extensions !== undefined;
|
|
587
|
-
const warnings: string[] = [];
|
|
588
|
-
// An explicit empty list disables ambient extensions, including model providers.
|
|
589
|
-
if (capabilityCeiling?.denyExtensions !== true && Array.isArray(input.extensions) && input.extensions.length === 0) {
|
|
590
|
-
const agentLabel = input.agentName ? ` for agent '${input.agentName}'` : "";
|
|
591
|
-
warnings.push(
|
|
592
|
-
`extensions: [] override${agentLabel} disables ALL ambient extensions for this child (not just "adds nothing"), `
|
|
593
|
-
+ "including any model-provider extension needed to resolve a provider-qualified model. "
|
|
594
|
-
+ "List the extensions this child actually needs instead of an empty array.",
|
|
595
|
-
);
|
|
596
|
-
}
|
|
597
|
-
const configuredExtensions = capabilityCeiling?.denyExtensions
|
|
598
|
-
? []
|
|
599
|
-
: [
|
|
600
|
-
...toolExtensionPaths,
|
|
601
|
-
...(input.extensions ?? []),
|
|
602
|
-
...(input.subagentOnlyExtensions ?? []),
|
|
603
|
-
];
|
|
604
|
-
const extensionArgs = disableAmbientExtensions
|
|
605
|
-
? [...new Set([...runtimeExtensions, ...configuredExtensions])]
|
|
606
|
-
: [
|
|
607
|
-
...new Set([
|
|
608
|
-
...runtimeExtensions,
|
|
609
|
-
...toolExtensionPaths,
|
|
610
|
-
...(input.subagentOnlyExtensions ?? []),
|
|
611
|
-
]),
|
|
612
|
-
];
|
|
613
|
-
const requestedToolNames =
|
|
614
|
-
input.tools !== undefined
|
|
615
|
-
? [
|
|
616
|
-
...new Set([
|
|
617
|
-
...requestedBuiltinTools,
|
|
618
|
-
...resolvedMcpSelections.map((selection) => selection.name),
|
|
619
|
-
]),
|
|
620
|
-
]
|
|
621
|
-
: undefined;
|
|
622
|
-
const capabilityAudit = capabilityCeiling
|
|
623
|
-
? ({
|
|
624
|
-
ceiling: capabilityCeiling,
|
|
625
|
-
...(requestedToolNames ? { requestedTools: requestedToolNames } : {}),
|
|
626
|
-
effectiveTools: effectiveToolAllowlist,
|
|
627
|
-
...(excludeTools.length > 0 ? { excludeTools } : {}),
|
|
628
|
-
removedTools:
|
|
629
|
-
requestedToolNames?.filter(
|
|
630
|
-
(tool) => !effectiveToolAllowlist.includes(tool),
|
|
631
|
-
) ?? [],
|
|
632
|
-
internalTools,
|
|
633
|
-
extensionsDenied: capabilityCeiling.denyExtensions,
|
|
634
|
-
removedExtensionCount: capabilityCeiling.denyExtensions
|
|
635
|
-
? (input.extensions?.length ?? 0) +
|
|
636
|
-
(input.subagentOnlyExtensions?.length ?? 0) +
|
|
637
|
-
(input.tools ?? []).filter(
|
|
638
|
-
(tool) =>
|
|
639
|
-
tool.includes("/") ||
|
|
640
|
-
tool.endsWith(".ts") ||
|
|
641
|
-
tool.endsWith(".js"),
|
|
642
|
-
).length
|
|
643
|
-
: 0,
|
|
644
|
-
requestedMcpToolCount: input.mcpDirectTools?.length ?? 0,
|
|
645
|
-
effectiveMcpTools,
|
|
646
|
-
agentAllowed:
|
|
647
|
-
input.agentName === undefined
|
|
648
|
-
? true
|
|
649
|
-
: isAgentAllowedByCapabilityCeiling(
|
|
650
|
-
input.agentName,
|
|
651
|
-
capabilityCeiling,
|
|
652
|
-
),
|
|
653
|
-
...(capabilityCeilingAgentRestrictionSources(capabilityCeiling)
|
|
654
|
-
? {
|
|
655
|
-
agentRestrictionSources:
|
|
656
|
-
capabilityCeilingAgentRestrictionSources(capabilityCeiling),
|
|
657
|
-
}
|
|
658
|
-
: {}),
|
|
659
|
-
} satisfies SubagentCapabilityAudit)
|
|
660
|
-
: undefined;
|
|
661
|
-
return {
|
|
662
|
-
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
663
|
-
requestedBuiltinTools,
|
|
664
|
-
declaredBuiltinTools,
|
|
665
|
-
excludeTools,
|
|
666
|
-
toolExtensionPaths,
|
|
667
|
-
resolvedMcpSelections,
|
|
668
|
-
effectiveMcpSelections,
|
|
669
|
-
effectiveMcpTools,
|
|
670
|
-
explicitToolAllowlist,
|
|
671
|
-
internalTools,
|
|
672
|
-
effectiveToolAllowlist,
|
|
673
|
-
requiredChildTools,
|
|
674
|
-
fanoutAuthorized,
|
|
675
|
-
runtimeExtensions,
|
|
676
|
-
configuredExtensions,
|
|
677
|
-
extensionArgs,
|
|
678
|
-
disableAmbientExtensions,
|
|
679
|
-
warnings,
|
|
680
|
-
...(effectiveRuntimeServerNames.length > 0 && filteredMcpConfig
|
|
681
|
-
? { mcpConfig: filteredMcpConfig, runtimeServerNames: effectiveRuntimeServerNames }
|
|
682
|
-
: {}),
|
|
683
|
-
...(capabilityAudit ? { capabilityAudit } : {}),
|
|
684
|
-
};
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
// Capability ceilings persisted before #1685 may still name hyphenated MCP server prefixes with underscores.
|
|
688
|
-
function countLegacyUnderscoreMcpToolNames(selections: readonly ResolvedMcpDirectToolSelection[]): Map<string, number> {
|
|
689
|
-
const counts = new Map<string, number>();
|
|
690
|
-
for (const selection of selections) {
|
|
691
|
-
const legacyName = legacyUnderscoreMcpToolName(selection);
|
|
692
|
-
if (legacyName !== selection.name) counts.set(legacyName, (counts.get(legacyName) ?? 0) + 1);
|
|
693
|
-
}
|
|
694
|
-
return counts;
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
function isLegacyUnderscoreMcpToolAllowed(
|
|
698
|
-
selection: ResolvedMcpDirectToolSelection,
|
|
699
|
-
allowedToolSet: ReadonlySet<string>,
|
|
700
|
-
resolvedMcpNames: ReadonlySet<string>,
|
|
701
|
-
legacyMcpNameCounts: ReadonlyMap<string, number>,
|
|
702
|
-
): boolean {
|
|
703
|
-
const legacyName = legacyUnderscoreMcpToolName(selection);
|
|
704
|
-
return legacyMcpNameCounts.get(legacyName) === 1 && !resolvedMcpNames.has(legacyName) && allowedToolSet.has(legacyName);
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
function legacyUnderscoreMcpToolName(selection: ResolvedMcpDirectToolSelection): string {
|
|
708
|
-
const slash = selection.selector.indexOf("/");
|
|
709
|
-
if (slash < 1) return selection.name;
|
|
710
|
-
const toolName = selection.selector.slice(slash + 1);
|
|
711
|
-
const suffix = `_${toolName}`;
|
|
712
|
-
if (!selection.name.endsWith(suffix)) return selection.name;
|
|
713
|
-
const prefix = selection.name.slice(0, -suffix.length);
|
|
714
|
-
return `${prefix.replace(/-/g, "_")}${suffix}`;
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
/** Escape XML-significant characters in a string for safe attribute interpolation. */
|
|
718
|
-
function escapeXmlAttr(value: string): string {
|
|
719
|
-
return value
|
|
720
|
-
.replace(/&/g, "&")
|
|
721
|
-
.replace(/"/g, """)
|
|
722
|
-
.replace(/</g, "<")
|
|
723
|
-
.replace(/>/g, ">");
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
727
|
-
const args = [...input.baseArgs];
|
|
728
|
-
|
|
729
|
-
if (input.sessionFile) {
|
|
730
|
-
fs.mkdirSync(path.dirname(input.sessionFile), { recursive: true });
|
|
731
|
-
args.push("--session", input.sessionFile);
|
|
732
|
-
} else {
|
|
733
|
-
if (!input.sessionEnabled) {
|
|
734
|
-
args.push("--no-session");
|
|
735
|
-
}
|
|
736
|
-
if (input.sessionDir) {
|
|
737
|
-
fs.mkdirSync(input.sessionDir, { recursive: true });
|
|
738
|
-
args.push("--session-dir", input.sessionDir);
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
const modelArg = applyThinkingSuffix(input.model, input.thinking);
|
|
743
|
-
if (modelArg) {
|
|
744
|
-
args.push("--model", modelArg);
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
const toolPlan = resolvePiLaunchToolPlan({
|
|
748
|
-
tools: input.tools,
|
|
749
|
-
excludeTools: input.excludeTools,
|
|
750
|
-
allowNestedSubagents: input.allowNestedSubagents,
|
|
751
|
-
extensions: input.extensions,
|
|
752
|
-
subagentOnlyExtensions: input.subagentOnlyExtensions,
|
|
753
|
-
mcpDirectTools: input.mcpDirectTools,
|
|
754
|
-
mcpConfig: input.mcpConfig,
|
|
755
|
-
runtimeServerNames: input.runtimeServerNames,
|
|
756
|
-
cwd: input.cwd,
|
|
757
|
-
requireReadTool: input.requireReadTool,
|
|
758
|
-
structuredOutput: input.structuredOutput,
|
|
759
|
-
fast: input.fast,
|
|
760
|
-
model: modelArg,
|
|
761
|
-
modelCandidates: input.modelCandidates,
|
|
762
|
-
capabilityCeiling: input.capabilityCeiling,
|
|
763
|
-
inheritedCapabilityCeiling: decodeSubagentCapabilityCeiling(
|
|
764
|
-
process.env[SUBAGENT_CAPABILITY_CEILING_ENV],
|
|
765
|
-
),
|
|
766
|
-
agentName: input.childAgentName,
|
|
767
|
-
permissionRules: input.permissionRules,
|
|
768
|
-
runtimeSnapshotHost: input.runtimeSnapshotHost,
|
|
769
|
-
});
|
|
770
|
-
if (toolPlan.explicitToolAllowlist) {
|
|
771
|
-
args.push(
|
|
772
|
-
toolPlan.effectiveToolAllowlist.length > 0 ? "--tools" : "--no-tools",
|
|
773
|
-
);
|
|
774
|
-
if (toolPlan.effectiveToolAllowlist.length > 0)
|
|
775
|
-
args.push(toolPlan.effectiveToolAllowlist.join(","));
|
|
776
|
-
} else if (toolPlan.excludeTools.length > 0) {
|
|
777
|
-
args.push("--exclude-tools", toolPlan.excludeTools.join(","));
|
|
778
|
-
}
|
|
779
|
-
if (toolPlan.disableAmbientExtensions) {
|
|
780
|
-
args.push("--no-extensions");
|
|
781
|
-
}
|
|
782
|
-
for (const extPath of toolPlan.extensionArgs)
|
|
783
|
-
args.push("--extension", extPath);
|
|
784
|
-
let tempDir: string | undefined;
|
|
785
|
-
if (toolPlan.mcpConfig && toolPlan.runtimeServerNames?.length) {
|
|
786
|
-
if (!tempDir)
|
|
787
|
-
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
|
|
788
|
-
const mcpConfigPath = path.join(tempDir, "mcp-config.json");
|
|
789
|
-
fs.writeFileSync(mcpConfigPath, JSON.stringify(toolPlan.mcpConfig, null, 2), { mode: 0o600 });
|
|
790
|
-
args.push("--mcp-config", mcpConfigPath);
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
if (!input.inheritProjectContext) {
|
|
794
|
-
args.push("--no-context-files");
|
|
795
|
-
}
|
|
796
|
-
if (!input.inheritSkills) {
|
|
797
|
-
args.push("--no-skills");
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
if (input.systemPrompt !== undefined && input.systemPrompt !== null) {
|
|
801
|
-
if (!tempDir)
|
|
802
|
-
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
|
|
803
|
-
const stem = (input.promptFileStem ?? "prompt").replace(/[^\w.-]/g, "_");
|
|
804
|
-
const promptPath = path.join(tempDir, `${stem}.md`);
|
|
805
|
-
// Inject <active_agent> tag so @gotgenes/pi-permission-system can
|
|
806
|
-
// resolve per-agent policy inside the child session.
|
|
807
|
-
const taggedPrompt = input.childAgentName
|
|
808
|
-
? `<active_agent name="${escapeXmlAttr(input.childAgentName)}"/>\n\n${input.systemPrompt}`
|
|
809
|
-
: input.systemPrompt;
|
|
810
|
-
fs.writeFileSync(promptPath, taggedPrompt, { mode: 0o600 });
|
|
811
|
-
args.push(
|
|
812
|
-
input.systemPromptMode === "replace"
|
|
813
|
-
? "--system-prompt"
|
|
814
|
-
: "--append-system-prompt",
|
|
815
|
-
promptPath,
|
|
816
|
-
);
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
if (
|
|
820
|
-
shouldDeliverTaskViaFile(
|
|
821
|
-
input.task,
|
|
822
|
-
input.taskDelivery ?? resolveSubagentTaskDelivery(),
|
|
823
|
-
)
|
|
824
|
-
) {
|
|
825
|
-
if (!tempDir) {
|
|
826
|
-
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
|
|
827
|
-
}
|
|
828
|
-
const taskFilePath = path.join(tempDir, "task.md");
|
|
829
|
-
fs.writeFileSync(taskFilePath, `Task: ${input.task}`, { mode: 0o600 });
|
|
830
|
-
args.push(`@${taskFilePath}`);
|
|
831
|
-
} else {
|
|
832
|
-
args.push(`Task: ${input.task}`);
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
const env: Record<string, string | undefined> = {};
|
|
836
|
-
env[PI_SUBAGENT_EXTENSION_BINDINGS_ENV] = encodeExtensionBindings(input.extensionBindings);
|
|
837
|
-
const piPackageRoot =
|
|
838
|
-
process.env[PI_CODING_AGENT_PACKAGE_ROOT_ENV] ?? resolvePiPackageRoot();
|
|
839
|
-
if (piPackageRoot) env[PI_CODING_AGENT_PACKAGE_ROOT_ENV] = piPackageRoot;
|
|
840
|
-
if (!tempDir)
|
|
841
|
-
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
|
|
842
|
-
const runtimeAcknowledgedExtensionsPath = path.join(
|
|
843
|
-
tempDir,
|
|
844
|
-
"runtime-acknowledged-extensions.json",
|
|
845
|
-
);
|
|
846
|
-
env[RUNTIME_EXTENSION_ACK_PATH_ENV] = runtimeAcknowledgedExtensionsPath;
|
|
847
|
-
let toolDiagnosticPath: string | undefined;
|
|
848
|
-
// Child launch environments are merged over process.env. Explicitly clear
|
|
849
|
-
// parent-scoped diagnostics so a nested zero-tool child cannot validate
|
|
850
|
-
// against, or overwrite, its parent's required-tool report.
|
|
851
|
-
env[REQUIRED_CHILD_TOOLS_ENV] = undefined;
|
|
852
|
-
env[CHILD_TOOL_DIAGNOSTIC_PATH_ENV] = undefined;
|
|
853
|
-
if (toolPlan.requiredChildTools.length > 0) {
|
|
854
|
-
if (!tempDir)
|
|
855
|
-
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-"));
|
|
856
|
-
toolDiagnosticPath = path.join(tempDir, "tool-diagnostic.json");
|
|
857
|
-
env[REQUIRED_CHILD_TOOLS_ENV] = JSON.stringify(toolPlan.requiredChildTools);
|
|
858
|
-
env[CHILD_TOOL_DIAGNOSTIC_PATH_ENV] = toolDiagnosticPath;
|
|
859
|
-
}
|
|
860
|
-
env[MCP_DIRECT_CHILD_TOOLS_ENV] =
|
|
861
|
-
toolPlan.effectiveMcpTools.length > 0
|
|
862
|
-
? JSON.stringify(toolPlan.effectiveMcpTools)
|
|
863
|
-
: undefined;
|
|
864
|
-
env[SUBAGENT_CHILD_ENV] = "1";
|
|
865
|
-
env[SUBAGENT_FANOUT_CHILD_ENV] = toolPlan.fanoutAuthorized ? "1" : "0";
|
|
866
|
-
if (input.waitToolEnabled !== undefined) {
|
|
867
|
-
env[WAIT_TOOL_ENABLED_ENV] = input.waitToolEnabled ? "true" : "false";
|
|
868
|
-
}
|
|
869
|
-
if (input.waitToolDefaultTimeoutMs !== undefined) {
|
|
870
|
-
env[WAIT_TOOL_DEFAULT_TIMEOUT_MS_ENV] = String(input.waitToolDefaultTimeoutMs);
|
|
871
|
-
}
|
|
872
|
-
const inheritedNestedRoute = Boolean(
|
|
873
|
-
process.env[SUBAGENT_PARENT_EVENT_SINK_ENV] &&
|
|
874
|
-
process.env[SUBAGENT_PARENT_ROOT_RUN_ID_ENV] &&
|
|
875
|
-
process.env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV],
|
|
876
|
-
);
|
|
877
|
-
const parentRunId =
|
|
878
|
-
input.parentRunId ??
|
|
879
|
-
input.runId ??
|
|
880
|
-
(inheritedNestedRoute ? process.env[SUBAGENT_RUN_ID_ENV] : undefined) ??
|
|
881
|
-
process.env[SUBAGENT_PARENT_RUN_ID_ENV] ??
|
|
882
|
-
"";
|
|
883
|
-
const parentChildIndex =
|
|
884
|
-
input.parentChildIndex !== undefined
|
|
885
|
-
? String(input.parentChildIndex)
|
|
886
|
-
: input.childIndex !== undefined
|
|
887
|
-
? String(input.childIndex)
|
|
888
|
-
: (process.env[SUBAGENT_PARENT_CHILD_INDEX_ENV] ?? "");
|
|
889
|
-
const inheritedDepth = Number(process.env[SUBAGENT_PARENT_DEPTH_ENV]);
|
|
890
|
-
const parentDepth =
|
|
891
|
-
input.parentDepth ??
|
|
892
|
-
(inheritedNestedRoute && Number.isFinite(inheritedDepth)
|
|
893
|
-
? inheritedDepth + 1
|
|
894
|
-
: 1);
|
|
895
|
-
const parentPath = input.parentPath ?? [
|
|
896
|
-
...parseNestedPathEnv(process.env[SUBAGENT_PARENT_PATH_ENV]),
|
|
897
|
-
...(parentRunId
|
|
898
|
-
? [
|
|
899
|
-
{
|
|
900
|
-
runId: parentRunId,
|
|
901
|
-
...(parentChildIndex && /^\d+$/.test(parentChildIndex)
|
|
902
|
-
? { stepIndex: Number(parentChildIndex) }
|
|
903
|
-
: {}),
|
|
904
|
-
...(input.childAgentName ? { agent: input.childAgentName } : {}),
|
|
905
|
-
},
|
|
906
|
-
]
|
|
907
|
-
: []),
|
|
908
|
-
];
|
|
909
|
-
env[SUBAGENT_PARENT_EVENT_SINK_ENV] = toolPlan.fanoutAuthorized
|
|
910
|
-
? (input.parentEventSink ??
|
|
911
|
-
process.env[SUBAGENT_PARENT_EVENT_SINK_ENV] ??
|
|
912
|
-
"")
|
|
913
|
-
: "";
|
|
914
|
-
env[SUBAGENT_PARENT_CONTROL_INBOX_ENV] = toolPlan.fanoutAuthorized
|
|
915
|
-
? (input.parentControlInbox ??
|
|
916
|
-
process.env[SUBAGENT_PARENT_CONTROL_INBOX_ENV] ??
|
|
917
|
-
"")
|
|
918
|
-
: "";
|
|
919
|
-
env[SUBAGENT_PARENT_ROOT_RUN_ID_ENV] = toolPlan.fanoutAuthorized
|
|
920
|
-
? (input.parentRootRunId ??
|
|
921
|
-
process.env[SUBAGENT_PARENT_ROOT_RUN_ID_ENV] ??
|
|
922
|
-
input.runId ??
|
|
923
|
-
"")
|
|
924
|
-
: "";
|
|
925
|
-
env[SUBAGENT_PARENT_RUN_ID_ENV] = toolPlan.fanoutAuthorized
|
|
926
|
-
? parentRunId
|
|
927
|
-
: "";
|
|
928
|
-
env[SUBAGENT_PARENT_CHILD_INDEX_ENV] = toolPlan.fanoutAuthorized
|
|
929
|
-
? parentChildIndex
|
|
930
|
-
: "";
|
|
931
|
-
env[SUBAGENT_PARENT_DEPTH_ENV] = toolPlan.fanoutAuthorized
|
|
932
|
-
? String(parentDepth)
|
|
933
|
-
: "";
|
|
934
|
-
env[SUBAGENT_PARENT_PATH_ENV] = toolPlan.fanoutAuthorized
|
|
935
|
-
? encodeNestedPathEnv(parentPath)
|
|
936
|
-
: "";
|
|
937
|
-
env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV] = toolPlan.fanoutAuthorized
|
|
938
|
-
? (input.parentCapabilityToken ??
|
|
939
|
-
process.env[SUBAGENT_PARENT_CAPABILITY_TOKEN_ENV] ??
|
|
940
|
-
"")
|
|
941
|
-
: "";
|
|
942
|
-
env[RUN_FANOUT_BUDGET_ENV] = toolPlan.fanoutAuthorized
|
|
943
|
-
? (input.runFanoutBudget ? encodeRunFanoutBudgetDescriptor(input.runFanoutBudget) : process.env[RUN_FANOUT_BUDGET_ENV])
|
|
944
|
-
: undefined;
|
|
945
|
-
env.PI_SUBAGENT_INHERIT_PROJECT_CONTEXT = input.inheritProjectContext
|
|
946
|
-
? "1"
|
|
947
|
-
: "0";
|
|
948
|
-
env[SUBAGENT_INHERIT_GLOBAL_CONTEXT_ENV] = input.inheritGlobalContext ? "1" : "0";
|
|
949
|
-
env.PI_SUBAGENT_INHERIT_SKILLS = input.inheritSkills ? "1" : "0";
|
|
950
|
-
env[PI_INTERCOM_STABLE_ID_ENV] = input.intercomSessionName || undefined;
|
|
951
|
-
env[PI_INTERCOM_SESSION_ID_ENV] = undefined;
|
|
952
|
-
if (input.intercomSessionName) {
|
|
953
|
-
env.PI_SUBAGENT_INTERCOM_SESSION_NAME = input.intercomSessionName;
|
|
954
|
-
}
|
|
955
|
-
if (input.sessionName?.trim()) {
|
|
956
|
-
env.PI_SUBAGENT_SESSION_NAME = input.sessionName.trim();
|
|
957
|
-
}
|
|
958
|
-
if (input.orchestratorIntercomTarget) {
|
|
959
|
-
env[SUBAGENT_ORCHESTRATOR_TARGET_ENV] = input.orchestratorIntercomTarget;
|
|
960
|
-
}
|
|
961
|
-
if (input.parentSessionId) {
|
|
962
|
-
env[SUBAGENT_ORCHESTRATOR_SESSION_ID_ENV] = input.parentSessionId;
|
|
963
|
-
}
|
|
964
|
-
const encodedPermissionRules = encodePermissionRules(input.permissionRules);
|
|
965
|
-
if (
|
|
966
|
-
input.orchestratorIntercomTarget &&
|
|
967
|
-
input.parentSessionId &&
|
|
968
|
-
input.runId &&
|
|
969
|
-
input.childAgentName
|
|
970
|
-
) {
|
|
971
|
-
const childIndex = input.childIndex ?? 0;
|
|
972
|
-
const channelDir = supervisorChannelDir(
|
|
973
|
-
input.runId,
|
|
974
|
-
input.childAgentName,
|
|
975
|
-
childIndex,
|
|
976
|
-
);
|
|
977
|
-
fs.mkdirSync(path.join(channelDir, "requests"), { recursive: true });
|
|
978
|
-
fs.mkdirSync(path.join(channelDir, "replies"), { recursive: true });
|
|
979
|
-
env[SUBAGENT_SUPERVISOR_CHANNEL_DIR_ENV] = channelDir;
|
|
980
|
-
}
|
|
981
|
-
if (encodedPermissionRules)
|
|
982
|
-
env[PERMISSION_AUDIT_PATH_ENV] =
|
|
983
|
-
input.permissionAuditPath ?? path.join(tempDir, "permission-audit.jsonl");
|
|
984
|
-
if (input.runId) {
|
|
985
|
-
env[SUBAGENT_RUN_ID_ENV] = input.runId;
|
|
986
|
-
}
|
|
987
|
-
if (input.childAgentName) {
|
|
988
|
-
env[SUBAGENT_CHILD_AGENT_ENV] = input.childAgentName;
|
|
989
|
-
}
|
|
990
|
-
if (input.childIndex !== undefined) {
|
|
991
|
-
env[SUBAGENT_CHILD_INDEX_ENV] = String(input.childIndex);
|
|
992
|
-
}
|
|
993
|
-
if (!toolPlan.capabilityCeiling && input.mcpDirectTools?.length)
|
|
994
|
-
env.MCP_DIRECT_TOOLS = input.mcpDirectTools.join(",");
|
|
995
|
-
else if (
|
|
996
|
-
toolPlan.capabilityCeiling &&
|
|
997
|
-
toolPlan.effectiveMcpSelections.length &&
|
|
998
|
-
!toolPlan.capabilityCeiling.denyExtensions
|
|
999
|
-
)
|
|
1000
|
-
env.MCP_DIRECT_TOOLS = toolPlan.effectiveMcpSelections
|
|
1001
|
-
.map((selection) => selection.selector)
|
|
1002
|
-
.join(",");
|
|
1003
|
-
else env.MCP_DIRECT_TOOLS = "__none__";
|
|
1004
|
-
const encodedCapabilityCeiling = encodeSubagentCapabilityCeiling(
|
|
1005
|
-
toolPlan.capabilityCeiling,
|
|
1006
|
-
);
|
|
1007
|
-
const thinkingCeiling = intersectThinkingCeilings(
|
|
1008
|
-
input.thinkingCeiling,
|
|
1009
|
-
decodeThinkingCeiling(process.env[SUBAGENT_THINKING_CEILING_ENV]),
|
|
1010
|
-
);
|
|
1011
|
-
if (thinkingCeiling) env[SUBAGENT_THINKING_CEILING_ENV] = thinkingCeiling;
|
|
1012
|
-
if (encodedCapabilityCeiling)
|
|
1013
|
-
env[SUBAGENT_CAPABILITY_CEILING_ENV] = encodedCapabilityCeiling;
|
|
1014
|
-
if (encodedPermissionRules)
|
|
1015
|
-
env[PERMISSION_POLICY_ENV] = encodedPermissionRules;
|
|
1016
|
-
env[STRUCTURED_OUTPUT_ACCEPTANCE_CAPTURE_ENV] = undefined;
|
|
1017
|
-
env[STRUCTURED_OUTPUT_ACCEPTANCE_REQUIRED_ENV] = undefined;
|
|
1018
|
-
if (input.structuredOutput) {
|
|
1019
|
-
env[STRUCTURED_OUTPUT_CAPTURE_ENV] = input.structuredOutput.outputPath;
|
|
1020
|
-
env[STRUCTURED_OUTPUT_SCHEMA_ENV] = input.structuredOutput.schemaPath;
|
|
1021
|
-
if (input.structuredOutput.acceptanceReportPath) env[STRUCTURED_OUTPUT_ACCEPTANCE_CAPTURE_ENV] = input.structuredOutput.acceptanceReportPath;
|
|
1022
|
-
if (input.structuredOutput.acceptanceReportRequired) env[STRUCTURED_OUTPUT_ACCEPTANCE_REQUIRED_ENV] = "1";
|
|
1023
|
-
}
|
|
1024
|
-
if (input.steerInboxDir) {
|
|
1025
|
-
env[SUBAGENT_STEER_INBOX_ENV] = input.steerInboxDir;
|
|
1026
|
-
}
|
|
1027
|
-
if (input.steerCapabilityPath)
|
|
1028
|
-
env[SUBAGENT_STEER_CAPABILITY_ENV] = input.steerCapabilityPath;
|
|
1029
|
-
if (input.steerAckDir) env[SUBAGENT_STEER_ACK_DIR_ENV] = input.steerAckDir;
|
|
1030
|
-
const encodedToolBudget = encodeToolBudgetEnv(input.toolBudget);
|
|
1031
|
-
if (encodedToolBudget) env[TOOL_BUDGET_ENV] = encodedToolBudget;
|
|
1032
|
-
env[TOOL_BUDGET_ZERO_AUTH_ENV] = input.allowZeroToolBudget ? "1" : undefined;
|
|
1033
|
-
if (input.childWatchdog) env[CHILD_WATCHDOG_CONFIG_ENV] = JSON.stringify(input.childWatchdog);
|
|
1034
|
-
|
|
1035
|
-
env[SUBAGENT_PARENT_SESSION_ENV] =
|
|
1036
|
-
input.parentSessionId ?? process.env[SUBAGENT_PARENT_SESSION_ENV] ?? "";
|
|
1037
|
-
env[SUBAGENT_FORK_CACHE_KEY_ENV] = input.forkCacheKey?.trim() || undefined;
|
|
1038
|
-
|
|
1039
|
-
return {
|
|
1040
|
-
args,
|
|
1041
|
-
env,
|
|
1042
|
-
warnings: toolPlan.warnings,
|
|
1043
|
-
tempDir,
|
|
1044
|
-
toolDiagnosticPath,
|
|
1045
|
-
runtimeAcknowledgedExtensionsPath,
|
|
1046
|
-
capabilityAudit: toolPlan.capabilityAudit,
|
|
1047
|
-
};
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
export const parseParentPathEnv = parseNestedPathEnv;
|
|
1051
|
-
|
|
1052
|
-
export function cleanupTempDir(tempDir: string | null | undefined): void {
|
|
1053
|
-
if (!tempDir) return;
|
|
1054
|
-
try {
|
|
1055
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
1056
|
-
} catch {
|
|
1057
|
-
// Temp cleanup is best effort.
|
|
1058
|
-
}
|
|
1059
|
-
}
|