pi-subagents 0.80.0 → 0.81.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 +28 -0
- package/bundle/agents.js +18 -0
- package/bundle/background-work.js +16 -0
- package/bundle/capability-ceiling.js +22 -0
- package/bundle/child-tool-plan.js +11 -0
- package/bundle/chunk-26C3WLYQ.js +8388 -0
- package/bundle/chunk-2XYWN7QJ.js +128 -0
- package/bundle/chunk-2ZSNH26H.js +4164 -0
- package/bundle/chunk-3DC5KBVP.js +235 -0
- package/bundle/chunk-3IF2PGW7.js +380 -0
- package/bundle/chunk-4QFZZV3O.js +57 -0
- package/bundle/chunk-563BHAZ5.js +1365 -0
- package/bundle/chunk-5LFLVSTM.js +260 -0
- package/bundle/chunk-62OCY2S4.js +14 -0
- package/bundle/chunk-74RT5YQE.js +326 -0
- package/bundle/chunk-7F6RKT6W.js +132 -0
- package/bundle/chunk-7J4ZXMY7.js +18 -0
- package/bundle/chunk-ARRKBB2B.js +179 -0
- package/bundle/chunk-BJN5LUQY.js +458 -0
- package/bundle/chunk-BPS2AGBZ.js +5421 -0
- package/bundle/chunk-CEKS3TFO.js +1337 -0
- package/bundle/chunk-CTPAE36Y.js +94 -0
- package/bundle/chunk-D4EHT5SJ.js +14 -0
- package/bundle/chunk-EYNJ6VL6.js +526 -0
- package/bundle/chunk-G5DKFLLI.js +1185 -0
- package/bundle/chunk-KSWQVGDW.js +1027 -0
- package/bundle/chunk-L5ZHE4L4.js +946 -0
- package/bundle/chunk-LSRUTHT7.js +57 -0
- package/bundle/chunk-N2BN7BF5.js +159 -0
- package/bundle/chunk-NF7IEKBH.js +490 -0
- package/bundle/chunk-NIXWDZZV.js +9 -0
- package/bundle/chunk-NNLASTQE.js +4082 -0
- package/bundle/chunk-TUWLXTAM.js +935 -0
- package/bundle/chunk-UKG4V45O.js +228 -0
- package/bundle/chunk-V2JWDB5U.js +753 -0
- package/bundle/chunk-V3BIUPLU.js +9163 -0
- package/bundle/chunk-V6MR5TOQ.js +270 -0
- package/bundle/chunk-VJHOELQP.js +8245 -0
- package/bundle/chunk-WSBNENFS.js +732 -0
- package/bundle/chunk-XMFLCKMQ.js +16664 -0
- package/bundle/chunk-YFCK7H2S.js +129 -0
- package/bundle/chunk-YYREO77V.js +137 -0
- package/bundle/chunk-Z7KVHQHL.js +173 -0
- package/bundle/chunk-ZBC44JJ3.js +8606 -0
- package/bundle/chunk-ZP3HODFH.js +175 -0
- package/bundle/control-channel.js +11 -0
- package/bundle/delegation.js +14 -0
- package/bundle/external-job-provider.js +20 -0
- package/bundle/external-runs.js +21 -0
- package/bundle/index.js +30 -0
- package/bundle/inspectors.js +8 -0
- package/bundle/intercom-bridge.js +11 -0
- package/bundle/preflight.js +425 -0
- package/bundle/project-panes.js +28 -0
- package/bundle/required-child-extensions.js +6 -0
- package/bundle/shared-types.js +8 -0
- package/bundle/workflow-resources.js +7 -0
- package/docs/extension-api.md +27 -0
- package/docs/observability.md +6 -2
- package/package.json +18 -18
- package/src/agents/frontmatter.d.ts +2 -0
- package/src/agents/frontmatter.js +9 -2
- package/src/api/delegation.d.ts +20 -0
- package/src/extension/index.js +27 -7
- package/src/runs/background/run-child-session.js +18 -0
- package/src/runs/background/stale-run-reconciler.js +2 -0
- package/src/runs/foreground/execution.js +39 -2
- package/src/runs/shared/structured-output.d.ts +1 -0
- package/src/runs/shared/structured-output.js +1 -0
- package/src/shared/accessible-dir.js +4 -1
- package/src/shared/provider-diagnostics.d.ts +9 -0
- package/src/shared/provider-diagnostics.js +75 -0
- package/src/shared/types.d.ts +3 -0
- package/src/slash/delegation-adapters.d.ts +3 -1
- package/src/slash/delegation-adapters.js +7 -0
- package/src/slash/prompt-template-bridge.js +3 -1
- package/src/slash/subagents-admin.d.ts +1 -0
- package/src/slash/subagents-admin.js +1 -1
- package/src/tui/fleet-layout.d.ts +20 -0
- package/src/tui/fleet-layout.js +17 -0
- package/src/tui/fleet-viewport.d.ts +12 -0
- package/src/tui/fleet-viewport.js +35 -0
- package/src/tui/fleet.d.ts +5 -6
- package/src/tui/fleet.js +69 -51
- package/src/workflows/structured-workflow-scripts.d.ts +1 -0
- package/src/workflows/structured-workflow-scripts.js +5 -4
- package/src/workflows/workflow-resources.js +2 -2
|
@@ -0,0 +1,946 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_WATCHDOG_CONFIG,
|
|
3
|
+
MainWatchdogRuntime,
|
|
4
|
+
WATCHDOG_DIFF_TOOL_NAME,
|
|
5
|
+
captureWatchdogDiffBaseline,
|
|
6
|
+
childSupervisorMetadata,
|
|
7
|
+
createMainWatchdogReview,
|
|
8
|
+
createWatchdogDiffTool,
|
|
9
|
+
createWatchdogWarningMessage,
|
|
10
|
+
drainOutstandingWork,
|
|
11
|
+
evaluateChildToolDiagnostic,
|
|
12
|
+
formatWatchdogCwdSection,
|
|
13
|
+
registerNativeSupervisorClient,
|
|
14
|
+
registerWaitTool,
|
|
15
|
+
resolveCurrentSessionId,
|
|
16
|
+
resolveWatchdogReviewModel,
|
|
17
|
+
startWatchdogDiffBaselineCapture
|
|
18
|
+
} from "./chunk-BPS2AGBZ.js";
|
|
19
|
+
import {
|
|
20
|
+
MODEL_ONLY_TOOL,
|
|
21
|
+
agentStreamOptions,
|
|
22
|
+
opencodeSessionHeaders
|
|
23
|
+
} from "./chunk-NNLASTQE.js";
|
|
24
|
+
import {
|
|
25
|
+
inheritedNestedRouteOf
|
|
26
|
+
} from "./chunk-VJHOELQP.js";
|
|
27
|
+
import {
|
|
28
|
+
CHILD_WATCHDOG_STATUS_EVENT,
|
|
29
|
+
MISSING_STRUCTURED_ACCEPTANCE_REPORT_ERROR,
|
|
30
|
+
SUBAGENT_WATCHDOG_WARNING_TYPE,
|
|
31
|
+
appendPermissionAudit,
|
|
32
|
+
createStructuredOutputToolParameters,
|
|
33
|
+
decodeChildWatchdogConfig,
|
|
34
|
+
getAgentDir,
|
|
35
|
+
permissionArgsPreview,
|
|
36
|
+
permissionDecision,
|
|
37
|
+
shouldBlockToolForBudget,
|
|
38
|
+
toolBudgetBlockedMessage,
|
|
39
|
+
toolBudgetSoftNudge,
|
|
40
|
+
validateAcceptanceReport,
|
|
41
|
+
validateStructuredOutputValue
|
|
42
|
+
} from "./chunk-ZBC44JJ3.js";
|
|
43
|
+
import {
|
|
44
|
+
INTERCOM_SESSION_IDENTITY_EVENT
|
|
45
|
+
} from "./chunk-5LFLVSTM.js";
|
|
46
|
+
|
|
47
|
+
// src/runs/shared/subagent-prompt-runtime.ts
|
|
48
|
+
import { createHash } from "node:crypto";
|
|
49
|
+
import * as fs from "node:fs";
|
|
50
|
+
import * as path from "node:path";
|
|
51
|
+
import { declarationsEqual, getCurrentSystemMessage, getCurrentTools } from "@earendil-works/pi-ai";
|
|
52
|
+
|
|
53
|
+
// src/runs/shared/runtime-acknowledged-extensions.ts
|
|
54
|
+
var RUNTIME_EXTENSION_ACK_EVENT = "subagent:acknowledge-extension";
|
|
55
|
+
var MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_IDS = 32;
|
|
56
|
+
var MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_ID_LENGTH = 128;
|
|
57
|
+
function isRuntimeAcknowledgedExtensionId(value) {
|
|
58
|
+
return typeof value === "string" && value.length > 0 && value.length <= MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_ID_LENGTH && /^[A-Za-z0-9._:@+-]+$/.test(value) && !value.includes("..") && !value.includes("/") && !value.includes("\\");
|
|
59
|
+
}
|
|
60
|
+
function projectRuntimeAcknowledgedExtensions(ids) {
|
|
61
|
+
const unique = [];
|
|
62
|
+
const seen = /* @__PURE__ */ new Set();
|
|
63
|
+
for (const id of ids) {
|
|
64
|
+
if (!isRuntimeAcknowledgedExtensionId(id) || seen.has(id)) continue;
|
|
65
|
+
seen.add(id);
|
|
66
|
+
unique.push(id);
|
|
67
|
+
}
|
|
68
|
+
if (unique.length === 0) return void 0;
|
|
69
|
+
return {
|
|
70
|
+
version: 1,
|
|
71
|
+
source: "child-runtime",
|
|
72
|
+
ids: unique.slice(0, MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_IDS),
|
|
73
|
+
omitted: Math.max(0, unique.length - MAX_RUNTIME_ACKNOWLEDGED_EXTENSION_IDS)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// src/runs/shared/tool-availability.ts
|
|
78
|
+
function formatChildToolDiagnostic(diagnostic, options = {}) {
|
|
79
|
+
const subject = diagnostic.agent ? `Agent '${diagnostic.agent}'` : "Subagent";
|
|
80
|
+
if (options.host === "parent") {
|
|
81
|
+
return [
|
|
82
|
+
`${subject} ran as a foreground child, which never loads the parent's ambient extensions, and these child tools were unavailable: ${diagnostic.missing.join(", ")}.`,
|
|
83
|
+
"The `tools` field is a strict allowlist; it does not load extension code.",
|
|
84
|
+
...diagnostic.missingMcpDirectTools?.length ? [`MCP direct tools missing from the child registry: ${diagnostic.missingMcpDirectTools.join(", ")}.`] : [],
|
|
85
|
+
"MCP tools require background children (`async: true`) because only they load ambient extensions.",
|
|
86
|
+
"For extension tools a foreground child can load, add the provider path to `subagentOnlyExtensions` (child-only), `extensions`, or as a path-like entry in `tools`, while keeping each registered tool name in `tools`."
|
|
87
|
+
].join("\n");
|
|
88
|
+
}
|
|
89
|
+
return [
|
|
90
|
+
`${subject} requested unavailable child tools: ${diagnostic.missing.join(", ")}.`,
|
|
91
|
+
"The `tools` field is a strict allowlist; it does not load extension code.",
|
|
92
|
+
...diagnostic.missingMcpDirectTools?.length ? [`Resolved MCP direct tools missing from the child registry: ${diagnostic.missingMcpDirectTools.join(", ")}. Resolved names must match what the host or pi-mcp-adapter registers; check the MCP direct-tool registration before treating this as a tool-call failure.`] : [],
|
|
93
|
+
"For extension tools, add the provider path to `subagentOnlyExtensions` (child-only), `extensions`, or as a path-like entry in `tools`, while keeping each registered tool name in `tools`.",
|
|
94
|
+
"For MCP tools, verify the MCP adapter configuration and selected tool names. For builtin tools, verify the name against the installed Pi version."
|
|
95
|
+
].join("\n");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/watchdog/register-child.ts
|
|
99
|
+
function childResolvedConfig(config) {
|
|
100
|
+
return {
|
|
101
|
+
...DEFAULT_WATCHDOG_CONFIG,
|
|
102
|
+
enabled: true,
|
|
103
|
+
agentEndTimeoutMs: config.agentEndTimeoutMs,
|
|
104
|
+
maxWarnings: config.maxWarnings,
|
|
105
|
+
main: {
|
|
106
|
+
enabled: true,
|
|
107
|
+
...config.model ? { model: config.model } : {},
|
|
108
|
+
...config.thinking !== void 0 ? { thinking: config.thinking } : {}
|
|
109
|
+
},
|
|
110
|
+
stalemateRepeats: config.stalemateRepeats,
|
|
111
|
+
cadence: { ...config.cadence },
|
|
112
|
+
children: {
|
|
113
|
+
...DEFAULT_WATCHDOG_CONFIG.children,
|
|
114
|
+
watchdogTailTimeoutMs: config.watchdogTailTimeoutMs
|
|
115
|
+
},
|
|
116
|
+
lsp: { ...config.lsp }
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function childWarningDetails(details, config) {
|
|
120
|
+
return {
|
|
121
|
+
...details,
|
|
122
|
+
source: details.source === "lsp" ? "lsp" : "child",
|
|
123
|
+
...config.agent ? { agent: config.agent } : {},
|
|
124
|
+
...config.runId ? { runId: config.runId } : {}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function registerChildWatchdog(pi, childConfig, writeStatus, structuredTerminal) {
|
|
128
|
+
if (!childConfig) return void 0;
|
|
129
|
+
if (!writeStatus) throw new Error("Child watchdog status sink is missing; the host must pass ChildRuntimeConfig.watchdogStatus.");
|
|
130
|
+
let currentContext;
|
|
131
|
+
let diffBaseline;
|
|
132
|
+
let agentSettled = false;
|
|
133
|
+
let seq = 0;
|
|
134
|
+
const emitStatus = (phase, reason, warning) => {
|
|
135
|
+
writeStatus({
|
|
136
|
+
type: CHILD_WATCHDOG_STATUS_EVENT,
|
|
137
|
+
...childConfig.runId ? { runId: childConfig.runId } : {},
|
|
138
|
+
...childConfig.agent ? { agent: childConfig.agent } : {},
|
|
139
|
+
...childConfig.childIndex !== void 0 ? { childIndex: childConfig.childIndex, stepIndex: childConfig.childIndex } : {},
|
|
140
|
+
seq: ++seq,
|
|
141
|
+
phase,
|
|
142
|
+
ts: Date.now(),
|
|
143
|
+
...reason ? { reason } : {},
|
|
144
|
+
...warning ? { warning } : {}
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
const resolved = childResolvedConfig(childConfig);
|
|
148
|
+
const runtime = new MainWatchdogRuntime({
|
|
149
|
+
resolveConfig: () => ({ ok: true, config: resolved, errors: [], sources: [{ scope: "session", exists: true }] }),
|
|
150
|
+
review: createMainWatchdogReview(() => currentContext, { getThinkingLevel: () => pi.getThinkingLevel(), diffBaseline: () => diffBaseline }),
|
|
151
|
+
reviewDescription: "child model review",
|
|
152
|
+
reviewChangesOnly: true,
|
|
153
|
+
displayWarning: (details, options) => {
|
|
154
|
+
const childDetails = childWarningDetails(details, childConfig);
|
|
155
|
+
emitStatus("reviewing", void 0, { severity: childDetails.severity, importance: childDetails.importance, category: childDetails.category, summary: childDetails.summary, evidence: childDetails.evidence, recommendedAction: childDetails.recommendedAction, ...childDetails.displayedAt ? { displayedAt: childDetails.displayedAt } : {}, addressed: false, stalemate: childDetails.state === "stalemate" });
|
|
156
|
+
const message = createWatchdogWarningMessage(childDetails, { display: true, details: childDetails });
|
|
157
|
+
if (agentSettled) pi.appendEntry(SUBAGENT_WATCHDOG_WARNING_TYPE, childDetails);
|
|
158
|
+
else pi.sendMessage(message, options);
|
|
159
|
+
},
|
|
160
|
+
displayUserWarning: (details) => {
|
|
161
|
+
const childDetails = childWarningDetails(details, childConfig);
|
|
162
|
+
emitStatus("reviewing", void 0, { severity: childDetails.severity, importance: childDetails.importance, category: childDetails.category, summary: childDetails.summary, evidence: childDetails.evidence, recommendedAction: childDetails.recommendedAction, ...childDetails.displayedAt ? { displayedAt: childDetails.displayedAt } : {}, addressed: false, stalemate: childDetails.state === "stalemate" });
|
|
163
|
+
pi.appendEntry(SUBAGENT_WATCHDOG_WARNING_TYPE, childDetails);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
const rememberContext = (ctx) => {
|
|
167
|
+
currentContext = ctx;
|
|
168
|
+
};
|
|
169
|
+
const onRuntimeEvent = pi.on;
|
|
170
|
+
onRuntimeEvent("session_start", (_event, ctx) => {
|
|
171
|
+
rememberContext(ctx);
|
|
172
|
+
diffBaseline = startWatchdogDiffBaselineCapture(ctx.cwd);
|
|
173
|
+
runtime.bindSession(ctx);
|
|
174
|
+
emitStatus("idle");
|
|
175
|
+
});
|
|
176
|
+
onRuntimeEvent("before_agent_start", (event, ctx) => {
|
|
177
|
+
agentSettled = false;
|
|
178
|
+
rememberContext(ctx);
|
|
179
|
+
runtime.handleBeforeAgentStart(event, ctx);
|
|
180
|
+
});
|
|
181
|
+
onRuntimeEvent("turn_end", (event, ctx) => {
|
|
182
|
+
rememberContext(ctx);
|
|
183
|
+
runtime.handleTurnEnd(event, ctx, structuredTerminal?.captured === true);
|
|
184
|
+
});
|
|
185
|
+
onRuntimeEvent("tool_result", (_event, ctx) => {
|
|
186
|
+
rememberContext(ctx);
|
|
187
|
+
runtime.handleToolResult(ctx);
|
|
188
|
+
});
|
|
189
|
+
onRuntimeEvent("agent_end", async (event, ctx) => {
|
|
190
|
+
rememberContext(ctx);
|
|
191
|
+
emitStatus("reviewing");
|
|
192
|
+
await runtime.handleAgentEnd(event, ctx);
|
|
193
|
+
const snapshot = runtime.getSnapshot(ctx.cwd);
|
|
194
|
+
if (snapshot.status === "failed") emitStatus("failed", snapshot.lastError);
|
|
195
|
+
else if (snapshot.status === "stale") emitStatus("stale", "review stale");
|
|
196
|
+
else emitStatus("idle");
|
|
197
|
+
});
|
|
198
|
+
onRuntimeEvent("agent_settled", () => {
|
|
199
|
+
agentSettled = true;
|
|
200
|
+
});
|
|
201
|
+
onRuntimeEvent("session_shutdown", () => {
|
|
202
|
+
currentContext = void 0;
|
|
203
|
+
runtime.dispose();
|
|
204
|
+
emitStatus("idle");
|
|
205
|
+
});
|
|
206
|
+
return runtime;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/watchdog/permission-arbiter.ts
|
|
210
|
+
import { Agent } from "@earendil-works/pi-agent-core";
|
|
211
|
+
import { convertToLlm } from "@earendil-works/pi-coding-agent";
|
|
212
|
+
import { Type } from "typebox";
|
|
213
|
+
var PermissionDecisionParams = Type.Object({
|
|
214
|
+
decision: Type.String({ enum: ["approve", "deny"] }),
|
|
215
|
+
reason: Type.String({ description: "One concise reason for this exact decision." })
|
|
216
|
+
}, { additionalProperties: false });
|
|
217
|
+
function conciseReason(value) {
|
|
218
|
+
const trimmed = value.trim();
|
|
219
|
+
return trimmed ? trimmed.slice(0, 500) : "Watchdog returned an empty reason.";
|
|
220
|
+
}
|
|
221
|
+
function createWatchdogPermissionArbiter(options = {}) {
|
|
222
|
+
return async (request) => {
|
|
223
|
+
const preview = permissionArgsPreview(request.args);
|
|
224
|
+
const createdAt = Date.now();
|
|
225
|
+
const auditBase = { type: "permission.request", createdAt, toolName: request.toolName, preview, matchedRule: "ask", decisionSource: "watchdog" };
|
|
226
|
+
appendPermissionAudit(request.auditPath, auditBase);
|
|
227
|
+
let completed = false;
|
|
228
|
+
const finish = (approved, reason, decision2) => {
|
|
229
|
+
const concise = conciseReason(reason);
|
|
230
|
+
if (completed) return { approved, reason: concise, source: "watchdog" };
|
|
231
|
+
completed = true;
|
|
232
|
+
appendPermissionAudit(request.auditPath, {
|
|
233
|
+
type: "permission.decision",
|
|
234
|
+
createdAt: Date.now(),
|
|
235
|
+
requestCreatedAt: createdAt,
|
|
236
|
+
toolName: request.toolName,
|
|
237
|
+
decision: decision2,
|
|
238
|
+
approved,
|
|
239
|
+
decisionSource: "watchdog",
|
|
240
|
+
reason: concise
|
|
241
|
+
});
|
|
242
|
+
return { approved, reason: concise, source: "watchdog" };
|
|
243
|
+
};
|
|
244
|
+
let childConfig;
|
|
245
|
+
try {
|
|
246
|
+
childConfig = decodeChildWatchdogConfig(request.rawWatchdogConfig);
|
|
247
|
+
} catch (error) {
|
|
248
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
249
|
+
return finish(false, `Watchdog permission arbiter configuration is invalid: ${reason}`, "unavailable");
|
|
250
|
+
}
|
|
251
|
+
if (!childConfig) return finish(false, "Watchdog permission arbiter is unavailable because the child watchdog is disabled.", "unavailable");
|
|
252
|
+
if (request.signal?.aborted || request.ctx.signal?.aborted) return finish(false, "Watchdog permission decision was cancelled.", "cancelled");
|
|
253
|
+
let decision;
|
|
254
|
+
const tool = {
|
|
255
|
+
name: "watchdog_permission_decision",
|
|
256
|
+
label: "Watchdog permission decision",
|
|
257
|
+
description: "Approve or deny this exact child tool call. Call exactly once.",
|
|
258
|
+
parameters: PermissionDecisionParams,
|
|
259
|
+
executionMode: "sequential",
|
|
260
|
+
async execute(_toolCallId, params) {
|
|
261
|
+
if (!decision) decision = params;
|
|
262
|
+
return { content: [{ type: "text", text: "Permission decision recorded." }], details: { recorded: true } };
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
let timeout;
|
|
266
|
+
let agent;
|
|
267
|
+
let abort;
|
|
268
|
+
try {
|
|
269
|
+
const run = async () => {
|
|
270
|
+
const config = childResolvedConfig(childConfig);
|
|
271
|
+
const selection = await resolveWatchdogReviewModel(request.ctx, config);
|
|
272
|
+
const auth = selection.auth;
|
|
273
|
+
const sessionId = request.ctx.sessionManager.getSessionId();
|
|
274
|
+
const baseStreamFn = options.streamFn ?? ((model, context, streamOptions) => request.ctx.modelRegistry.streamSimple(model, context, streamOptions));
|
|
275
|
+
const streamFn = (model, context, streamOptions) => baseStreamFn(model, context, {
|
|
276
|
+
...streamOptions,
|
|
277
|
+
...auth.apiKey ? { apiKey: auth.apiKey } : {},
|
|
278
|
+
env: auth.env || streamOptions?.env ? { ...auth.env ?? {}, ...streamOptions?.env ?? {} } : void 0,
|
|
279
|
+
headers: { ...opencodeSessionHeaders(model, sessionId), ...streamOptions?.headers ?? {}, ...auth.headers ?? {} }
|
|
280
|
+
});
|
|
281
|
+
const systemPrompt = [
|
|
282
|
+
"You are the pi-subagents watchdog permission arbiter.",
|
|
283
|
+
"Decide only whether this exact non-bash child tool call should proceed.",
|
|
284
|
+
"Call watchdog_permission_decision exactly once with approve or deny and a concise reason.",
|
|
285
|
+
"Deny when uncertain. Do not produce freeform advice or ask the parent orchestrator.",
|
|
286
|
+
"",
|
|
287
|
+
formatWatchdogCwdSection(request.ctx.cwd)
|
|
288
|
+
].join("\n");
|
|
289
|
+
const tools = [tool];
|
|
290
|
+
agent = new Agent({
|
|
291
|
+
initialState: {
|
|
292
|
+
systemPrompt,
|
|
293
|
+
model: selection.model,
|
|
294
|
+
thinkingLevel: selection.thinkingLevel,
|
|
295
|
+
tools
|
|
296
|
+
},
|
|
297
|
+
convertToLlm,
|
|
298
|
+
...agentStreamOptions(streamFn),
|
|
299
|
+
getApiKey: (providerName) => providerName === selection.model.provider ? auth.apiKey : void 0,
|
|
300
|
+
beforeToolCall: async ({ toolCall }) => toolCall.name === tool.name ? void 0 : { block: true, reason: `Permission arbiter tool '${toolCall.name}' is not allowed.` },
|
|
301
|
+
toolExecution: "sequential"
|
|
302
|
+
});
|
|
303
|
+
await agent.prompt(`Tool: ${request.toolName}
|
|
304
|
+
Redacted arguments: ${preview}`);
|
|
305
|
+
if (!decision) return finish(false, "Watchdog permission arbiter returned no decision.", "malformed");
|
|
306
|
+
const approved = decision.decision === "approve";
|
|
307
|
+
return finish(approved, decision.reason, decision.decision);
|
|
308
|
+
};
|
|
309
|
+
return await Promise.race([
|
|
310
|
+
run(),
|
|
311
|
+
new Promise((resolve2) => {
|
|
312
|
+
timeout = setTimeout(() => {
|
|
313
|
+
agent?.abort();
|
|
314
|
+
resolve2(finish(false, "Watchdog permission decision timed out.", "timeout"));
|
|
315
|
+
}, childConfig.agentEndTimeoutMs);
|
|
316
|
+
}),
|
|
317
|
+
new Promise((resolve2) => {
|
|
318
|
+
abort = () => {
|
|
319
|
+
agent?.abort();
|
|
320
|
+
resolve2(finish(false, "Watchdog permission decision was cancelled.", "cancelled"));
|
|
321
|
+
};
|
|
322
|
+
request.signal?.addEventListener("abort", abort, { once: true });
|
|
323
|
+
request.ctx.signal?.addEventListener("abort", abort, { once: true });
|
|
324
|
+
})
|
|
325
|
+
]);
|
|
326
|
+
} catch (error) {
|
|
327
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
328
|
+
return finish(false, `Watchdog permission arbiter failed closed: ${reason}`, reason.includes("timed out") ? "timeout" : "error");
|
|
329
|
+
} finally {
|
|
330
|
+
if (timeout) clearTimeout(timeout);
|
|
331
|
+
if (abort) {
|
|
332
|
+
request.signal?.removeEventListener("abort", abort);
|
|
333
|
+
request.ctx.signal?.removeEventListener("abort", abort);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
var requestWatchdogPermission = createWatchdogPermissionArbiter();
|
|
339
|
+
|
|
340
|
+
// src/runs/shared/subagent-prompt-runtime.ts
|
|
341
|
+
var STRUCTURED_OUTPUT_INSTRUCTIONS = [
|
|
342
|
+
"This subagent step has a strict structured output contract.",
|
|
343
|
+
"Your final action must be to call the `structured_output` tool with JSON matching the provided schema.",
|
|
344
|
+
"Do not rely on prose-only completion; if you do not call `structured_output`, the parent will fail this step."
|
|
345
|
+
].join("\n");
|
|
346
|
+
var CHILD_SUBAGENT_BOUNDARY_INSTRUCTIONS = [
|
|
347
|
+
"You are a child subagent, not the parent orchestrator.",
|
|
348
|
+
"The parent session owns delegation, orchestration, review fanout, and follow-up worker launches.",
|
|
349
|
+
"Ignore prior parent-only orchestration instructions in inherited conversation history.",
|
|
350
|
+
"Do not propose or run subagents. Complete only your assigned role-specific task with the tools available to you.",
|
|
351
|
+
"If you need to edit files, use the available editing tools. Do not print tool-call syntax, patches, or pseudo-tool calls as text."
|
|
352
|
+
].join("\n");
|
|
353
|
+
var CHILD_FANOUT_BOUNDARY_INSTRUCTIONS = [
|
|
354
|
+
"You are a child subagent with explicit fanout responsibility for this assigned task.",
|
|
355
|
+
"The parent session owns final orchestration, acceptance, and follow-up implementation launches.",
|
|
356
|
+
"You may use the `subagent` tool only for the fanout work explicitly requested in this task.",
|
|
357
|
+
"Do not broaden yourself into general parent orchestration. Do not launch follow-up workers unless the task explicitly asks for that.",
|
|
358
|
+
"The maxSubagentDepth cap still applies and may block further fanout.",
|
|
359
|
+
"If you need to edit files, use the available editing tools. Do not print tool-call syntax, patches, or pseudo-tool calls as text."
|
|
360
|
+
].join("\n");
|
|
361
|
+
var PARENT_ONLY_CUSTOM_MESSAGE_TYPES = /* @__PURE__ */ new Set([
|
|
362
|
+
"subagent-orchestration-instructions",
|
|
363
|
+
"subagent-slash-result",
|
|
364
|
+
"subagent-slash-text-result",
|
|
365
|
+
"subagent-notify",
|
|
366
|
+
"subagent_control_notice",
|
|
367
|
+
"subagent-control",
|
|
368
|
+
"subagent-control-notice"
|
|
369
|
+
]);
|
|
370
|
+
var SUBAGENT_ORCHESTRATION_SKILL_NAME_PATTERN = /<name>\s*pi-subagents\s*<\/name>/;
|
|
371
|
+
var PROJECT_CONTEXT_XML_HEADER = "\n\n<project_context>\n\n";
|
|
372
|
+
var PROJECT_CONTEXT_LEGACY_HEADER = "\n\n# Project Context\n\nProject-specific instructions and guidelines:\n\n";
|
|
373
|
+
var SKILLS_HEADER = "\n\nThe following skills provide specialized instructions for specific tasks.";
|
|
374
|
+
var DATE_HEADER = "\nCurrent date:";
|
|
375
|
+
function registerRuntimeExtensionAcknowledgements(pi, sink) {
|
|
376
|
+
if (!sink) return;
|
|
377
|
+
const ids = [];
|
|
378
|
+
let finalized = false;
|
|
379
|
+
const acknowledge = (payload) => {
|
|
380
|
+
if (finalized || !payload || typeof payload !== "object") return void 0;
|
|
381
|
+
const id = payload.id;
|
|
382
|
+
if (isRuntimeAcknowledgedExtensionId(id)) ids.push(id);
|
|
383
|
+
return void 0;
|
|
384
|
+
};
|
|
385
|
+
const finalize = () => {
|
|
386
|
+
if (finalized) return void 0;
|
|
387
|
+
finalized = true;
|
|
388
|
+
sink(ids);
|
|
389
|
+
return void 0;
|
|
390
|
+
};
|
|
391
|
+
try {
|
|
392
|
+
const events = pi.events;
|
|
393
|
+
events?.on?.(RUNTIME_EXTENSION_ACK_EVENT, acknowledge);
|
|
394
|
+
const onRuntimeEvent = pi.on;
|
|
395
|
+
onRuntimeEvent("agent_end", finalize);
|
|
396
|
+
onRuntimeEvent("session_shutdown", finalize);
|
|
397
|
+
} catch {
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
function findSectionEnd(prompt, startIndex, nextHeaders) {
|
|
401
|
+
let endIndex = prompt.length;
|
|
402
|
+
for (const header of nextHeaders) {
|
|
403
|
+
const index = prompt.indexOf(header, startIndex);
|
|
404
|
+
if (index !== -1 && index < endIndex) {
|
|
405
|
+
endIndex = index;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return endIndex;
|
|
409
|
+
}
|
|
410
|
+
function stripProjectContext(prompt) {
|
|
411
|
+
const xmlStartIndex = prompt.indexOf(PROJECT_CONTEXT_XML_HEADER);
|
|
412
|
+
if (xmlStartIndex !== -1) {
|
|
413
|
+
const closingTag = "</project_context>";
|
|
414
|
+
const closingIndex = prompt.indexOf(closingTag, xmlStartIndex + PROJECT_CONTEXT_XML_HEADER.length);
|
|
415
|
+
if (closingIndex !== -1) {
|
|
416
|
+
return `${prompt.slice(0, xmlStartIndex)}${prompt.slice(closingIndex + closingTag.length)}`;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
const legacyStartIndex = prompt.indexOf(PROJECT_CONTEXT_LEGACY_HEADER);
|
|
420
|
+
if (legacyStartIndex === -1) return prompt;
|
|
421
|
+
const endIndex = findSectionEnd(prompt, legacyStartIndex + PROJECT_CONTEXT_LEGACY_HEADER.length, [SKILLS_HEADER, DATE_HEADER]);
|
|
422
|
+
return `${prompt.slice(0, legacyStartIndex)}${prompt.slice(endIndex)}`;
|
|
423
|
+
}
|
|
424
|
+
var GLOBAL_CONTEXT_FILE_NAMES = /* @__PURE__ */ new Set(["agents.md", "agents.override.md", "claude.md"]);
|
|
425
|
+
function canonicalDirectory(dir) {
|
|
426
|
+
try {
|
|
427
|
+
return fs.realpathSync(dir);
|
|
428
|
+
} catch {
|
|
429
|
+
return path.resolve(dir);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
function expandContextPath(filePath) {
|
|
433
|
+
const home = process.env.HOME ?? process.env.USERPROFILE;
|
|
434
|
+
return filePath === "~" ? home ?? filePath : /^~[\\/]/.test(filePath) ? path.join(home ?? "~", filePath.slice(2)) : filePath;
|
|
435
|
+
}
|
|
436
|
+
function isContextFilePath(filePath) {
|
|
437
|
+
return GLOBAL_CONTEXT_FILE_NAMES.has(path.basename(expandContextPath(filePath)).toLowerCase());
|
|
438
|
+
}
|
|
439
|
+
function isGlobalContextFile(filePath) {
|
|
440
|
+
const expanded = expandContextPath(filePath);
|
|
441
|
+
if (!isContextFilePath(filePath)) return false;
|
|
442
|
+
return canonicalDirectory(path.dirname(expanded)) === canonicalDirectory(getAgentDir());
|
|
443
|
+
}
|
|
444
|
+
function stripGlobalInstructionsFromXmlContext(context) {
|
|
445
|
+
const block = /<project_instructions\s+path=(["'])(.*?)\1\s*>[\s\S]*?<\/project_instructions>\s*/gi;
|
|
446
|
+
return context.replace(block, (match, _quote, filePath) => isGlobalContextFile(filePath) ? "" : match);
|
|
447
|
+
}
|
|
448
|
+
function stripGlobalInstructionsFromLegacyContext(context) {
|
|
449
|
+
const sections = [...context.matchAll(/^## ([^\r\n]+)(?:\r?\n|$)/gm)].filter((match) => isContextFilePath(match[1].trim()));
|
|
450
|
+
let rewritten = "";
|
|
451
|
+
let cursor = 0;
|
|
452
|
+
for (let index = 0; index < sections.length; index++) {
|
|
453
|
+
const section = sections[index];
|
|
454
|
+
const start = section.index;
|
|
455
|
+
const end = sections[index + 1]?.index ?? context.length;
|
|
456
|
+
rewritten += context.slice(cursor, start);
|
|
457
|
+
if (!isGlobalContextFile(section[1].trim())) rewritten += context.slice(start, end);
|
|
458
|
+
cursor = end;
|
|
459
|
+
}
|
|
460
|
+
return `${rewritten}${context.slice(cursor)}`;
|
|
461
|
+
}
|
|
462
|
+
function stripGlobalContext(prompt) {
|
|
463
|
+
const rewrittenXml = prompt.replace(/<project_context>[\s\S]*?<\/project_context>/gi, (context) => {
|
|
464
|
+
const rewritten = stripGlobalInstructionsFromXmlContext(context);
|
|
465
|
+
return /<project_instructions\b/i.test(rewritten) ? rewritten : "";
|
|
466
|
+
});
|
|
467
|
+
const legacyStartIndex = rewrittenXml.indexOf(PROJECT_CONTEXT_LEGACY_HEADER);
|
|
468
|
+
if (legacyStartIndex === -1) return rewrittenXml;
|
|
469
|
+
const legacyEndIndex = findSectionEnd(rewrittenXml, legacyStartIndex + PROJECT_CONTEXT_LEGACY_HEADER.length, [SKILLS_HEADER, DATE_HEADER]);
|
|
470
|
+
const legacyContext = rewrittenXml.slice(legacyStartIndex, legacyEndIndex);
|
|
471
|
+
return `${rewrittenXml.slice(0, legacyStartIndex)}${stripGlobalInstructionsFromLegacyContext(legacyContext)}${rewrittenXml.slice(legacyEndIndex)}`;
|
|
472
|
+
}
|
|
473
|
+
function stripInheritedSkills(prompt) {
|
|
474
|
+
const startIndex = prompt.indexOf(SKILLS_HEADER);
|
|
475
|
+
if (startIndex === -1) return prompt;
|
|
476
|
+
const endIndex = findSectionEnd(prompt, startIndex + SKILLS_HEADER.length, [DATE_HEADER]);
|
|
477
|
+
return `${prompt.slice(0, startIndex)}${prompt.slice(endIndex)}`;
|
|
478
|
+
}
|
|
479
|
+
function stripSubagentOrchestrationSkill(prompt) {
|
|
480
|
+
return prompt.replace(/\n{0,2}<skill\s+name=["']pi-subagents["'][^>]*>[\s\S]*?<\/skill>\n{0,2}/g, "\n\n").replace(/[ \t]*<skill>\s*[\s\S]*?<\/skill>\s*/g, (block) => SUBAGENT_ORCHESTRATION_SKILL_NAME_PATTERN.test(block) ? "" : block);
|
|
481
|
+
}
|
|
482
|
+
function stripChildBoundaryInstructions(prompt) {
|
|
483
|
+
let rewritten = prompt;
|
|
484
|
+
for (const instructions of [CHILD_SUBAGENT_BOUNDARY_INSTRUCTIONS, CHILD_FANOUT_BOUNDARY_INSTRUCTIONS, STRUCTURED_OUTPUT_INSTRUCTIONS]) {
|
|
485
|
+
rewritten = rewritten.split(`
|
|
486
|
+
|
|
487
|
+
${instructions}`).join("").split(instructions).join("");
|
|
488
|
+
}
|
|
489
|
+
return rewritten.replace(/^(?:[ \t]*\r?\n)+/, "");
|
|
490
|
+
}
|
|
491
|
+
function stripInheritedPromptText(prompt, options) {
|
|
492
|
+
let rewritten = prompt;
|
|
493
|
+
if (!options.inheritProjectContext) {
|
|
494
|
+
rewritten = stripProjectContext(rewritten);
|
|
495
|
+
}
|
|
496
|
+
if (!options.inheritGlobalContext) {
|
|
497
|
+
rewritten = stripGlobalContext(rewritten);
|
|
498
|
+
}
|
|
499
|
+
if (!options.inheritSkills) {
|
|
500
|
+
rewritten = stripInheritedSkills(rewritten);
|
|
501
|
+
}
|
|
502
|
+
return stripChildBoundaryInstructions(stripSubagentOrchestrationSkill(rewritten));
|
|
503
|
+
}
|
|
504
|
+
function childBoundary(options) {
|
|
505
|
+
const boundary = options.fanoutChild ? CHILD_FANOUT_BOUNDARY_INSTRUCTIONS : CHILD_SUBAGENT_BOUNDARY_INSTRUCTIONS;
|
|
506
|
+
return options.structuredOutput ? `${boundary}
|
|
507
|
+
|
|
508
|
+
${STRUCTURED_OUTPUT_INSTRUCTIONS}` : boundary;
|
|
509
|
+
}
|
|
510
|
+
function appendChildBoundary(prompt, options) {
|
|
511
|
+
return `${stripChildBoundaryInstructions(prompt)}
|
|
512
|
+
|
|
513
|
+
${childBoundary(options)}`;
|
|
514
|
+
}
|
|
515
|
+
function rewriteSubagentPrompt(prompt, options) {
|
|
516
|
+
return appendChildBoundary(stripInheritedPromptText(prompt, options), options);
|
|
517
|
+
}
|
|
518
|
+
function filterChildPromptOptions(options, inherited) {
|
|
519
|
+
if (!inherited.inheritProjectContext) options.contextFiles = [];
|
|
520
|
+
else if (!inherited.inheritGlobalContext) options.contextFiles = options.contextFiles.filter((file) => !isGlobalContextFile(file.path));
|
|
521
|
+
options.skills = inherited.inheritSkills ? options.skills.filter((skill) => skill.name !== "pi-subagents") : [];
|
|
522
|
+
if (options.customPrompt) options.customPrompt = stripInheritedPromptText(options.customPrompt, inherited);
|
|
523
|
+
if (options.appendSystemPrompt) options.appendSystemPrompt = stripInheritedPromptText(options.appendSystemPrompt, inherited);
|
|
524
|
+
}
|
|
525
|
+
function rewritesChildPrompt(config) {
|
|
526
|
+
return config.inheritProjectContext !== void 0 || config.inheritGlobalContext !== void 0 || config.inheritSkills !== void 0 || Boolean(config.fanoutChild);
|
|
527
|
+
}
|
|
528
|
+
function registerSubagentPromptBoundary(pi, config) {
|
|
529
|
+
if (!rewritesChildPrompt(config)) return;
|
|
530
|
+
if (config.forkCacheKey) return;
|
|
531
|
+
pi.on("before_agent_start", (event) => {
|
|
532
|
+
const finalized = appendChildBoundary(event.systemPrompt, { fanoutChild: config.fanoutChild, structuredOutput: Boolean(config.structuredOutput) });
|
|
533
|
+
return finalized === event.systemPrompt ? void 0 : { systemPrompt: finalized };
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
function isParentOnlySubagentMessage(message) {
|
|
537
|
+
const m = message;
|
|
538
|
+
if (m?.role !== "custom" || typeof m.customType !== "string") return false;
|
|
539
|
+
if (m.customType === SUBAGENT_WATCHDOG_WARNING_TYPE) return true;
|
|
540
|
+
return PARENT_ONLY_CUSTOM_MESSAGE_TYPES.has(m.customType);
|
|
541
|
+
}
|
|
542
|
+
function isSubagentToolResultMessage(message) {
|
|
543
|
+
const m = message;
|
|
544
|
+
return m?.role === "toolResult" && m.toolName === "subagent";
|
|
545
|
+
}
|
|
546
|
+
function isSubagentToolCallBlock(block) {
|
|
547
|
+
const b = block;
|
|
548
|
+
return b?.type === "toolCall" && b.name === "subagent";
|
|
549
|
+
}
|
|
550
|
+
var PORTABLE_TOOL_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
551
|
+
var MAX_PORTABLE_TOOL_ID_LENGTH = 64;
|
|
552
|
+
var COMPOSITE_TOOL_ID_APIS = /* @__PURE__ */ new Set([
|
|
553
|
+
"azure-openai-responses",
|
|
554
|
+
"cursor-native",
|
|
555
|
+
"openai-completions",
|
|
556
|
+
"openai-responses"
|
|
557
|
+
]);
|
|
558
|
+
var PROMPT_CACHE_KEY_APIS = /* @__PURE__ */ new Set([
|
|
559
|
+
"azure-openai-responses",
|
|
560
|
+
"openai-codex-responses",
|
|
561
|
+
"openai-completions",
|
|
562
|
+
"openai-responses"
|
|
563
|
+
]);
|
|
564
|
+
var isSystemMessage = (message) => message.role === "system";
|
|
565
|
+
var writtenBefore = (message, forkedAt) => typeof message.timestamp === "number" && message.timestamp < forkedAt;
|
|
566
|
+
var promptText = (message) => typeof message?.content === "string" ? message.content : (message?.content ?? []).map((block) => block.text).join("");
|
|
567
|
+
function systemMessagesBefore(messages, forkedAt) {
|
|
568
|
+
return messages.filter((message) => isSystemMessage(message) && writtenBefore(message, forkedAt));
|
|
569
|
+
}
|
|
570
|
+
function modelBefore(messages, forkedAt) {
|
|
571
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
572
|
+
const message = messages[index];
|
|
573
|
+
if (message.role !== "assistant" || !writtenBefore(message, forkedAt)) continue;
|
|
574
|
+
if (typeof message.provider !== "string" || typeof message.api !== "string" || typeof message.model !== "string") return void 0;
|
|
575
|
+
return { provider: message.provider, api: message.api, model: message.model };
|
|
576
|
+
}
|
|
577
|
+
return void 0;
|
|
578
|
+
}
|
|
579
|
+
var LOADOUT_SECTIONS = /* @__PURE__ */ new Set(["tools", "rules", "docs", "skills", "advertised_subagents", "mcp_servers", "project_context"]);
|
|
580
|
+
function forkInstructions(input) {
|
|
581
|
+
const parent = getCurrentSystemMessage(input.parent);
|
|
582
|
+
const child = getCurrentSystemMessage(input.child);
|
|
583
|
+
const parts = [
|
|
584
|
+
"The system prompt and tool list of this conversation are the parent session's, sent unchanged so this request reuses the parent's prompt cache. The instructions below are yours and take precedence over the parent's role.",
|
|
585
|
+
`Tools you can call: ${getCurrentTools(input.child).map((tool) => tool.name).join(", ") || "none"}. Any other declared tool belongs to the parent session and fails if called.`
|
|
586
|
+
];
|
|
587
|
+
const ownPrompt = promptText(getCurrentSystemMessage(input.own));
|
|
588
|
+
if (ownPrompt.length > 0) parts.push(ownPrompt);
|
|
589
|
+
for (const [name, value] of Object.entries(child?.sections ?? {})) {
|
|
590
|
+
if (!LOADOUT_SECTIONS.has(name) && value && parent?.sections?.[name] !== value) parts.push(value);
|
|
591
|
+
}
|
|
592
|
+
if (input.boundary) parts.push(input.boundary);
|
|
593
|
+
return `<subagent_instructions>
|
|
594
|
+
${parts.join("\n\n")}
|
|
595
|
+
</subagent_instructions>`;
|
|
596
|
+
}
|
|
597
|
+
function prependText(message, text) {
|
|
598
|
+
const content = message.content;
|
|
599
|
+
if (typeof content === "string") return { ...message, content: `${text}
|
|
600
|
+
|
|
601
|
+
${content}` };
|
|
602
|
+
if (Array.isArray(content)) return { ...message, content: [{ type: "text", text }, ...content] };
|
|
603
|
+
return message;
|
|
604
|
+
}
|
|
605
|
+
function projectForkRequest(messages, inherited, forkedAt, options = {}) {
|
|
606
|
+
const own = (message) => !writtenBefore(message, forkedAt);
|
|
607
|
+
if (inherited.length === 0 || !messages.some((message) => !isSystemMessage(message) && !own(message))) {
|
|
608
|
+
if (!options.boundary) return messages;
|
|
609
|
+
const at2 = messages.findIndex((message) => !isSystemMessage(message));
|
|
610
|
+
const section = { role: "system", content: "", sections: { subagent_boundary: `<subagent_boundary>
|
|
611
|
+
${options.boundary}
|
|
612
|
+
</subagent_boundary>` }, timestamp: forkedAt };
|
|
613
|
+
return at2 === -1 ? [...messages, section] : [...messages.slice(0, at2), section, ...messages.slice(at2)];
|
|
614
|
+
}
|
|
615
|
+
const instructions = forkInstructions({ parent: inherited, child: messages, own: messages.filter((message) => isSystemMessage(message) && own(message)), boundary: options.boundary });
|
|
616
|
+
const parentTools = new Map(getCurrentTools(inherited).map((tool) => [tool.name, tool]));
|
|
617
|
+
const childTools = getCurrentTools(messages).filter((tool) => {
|
|
618
|
+
const parentTool = parentTools.get(tool.name);
|
|
619
|
+
return !parentTool || !declarationsEqual(parentTool, tool);
|
|
620
|
+
});
|
|
621
|
+
const redefined = childTools.filter((tool) => parentTools.has(tool.name)).map((tool) => ({ name: tool.name }));
|
|
622
|
+
const sentInherited = messages.filter((message) => isSystemMessage(message) && !own(message));
|
|
623
|
+
const uncollapsed = sentInherited.length === inherited.length && sentInherited.every((message, index) => JSON.stringify(message) === JSON.stringify(inherited[index]));
|
|
624
|
+
const conversation = messages.filter((message) => !isSystemMessage(message));
|
|
625
|
+
const head = getCurrentSystemMessage(inherited);
|
|
626
|
+
const next = uncollapsed ? messages.filter((message) => !isSystemMessage(message) || !own(message)) : [...head ? [head] : [], ...conversation];
|
|
627
|
+
const firstOwn = next.findIndex((message) => !isSystemMessage(message) && own(message));
|
|
628
|
+
const task = next.findIndex((message) => message.role === "user" && own(message));
|
|
629
|
+
const additions = [];
|
|
630
|
+
if (childTools.length > 0) additions.push({ role: "system", content: "", ...redefined.length > 0 ? { toolsRemoved: redefined } : {}, toolsAdded: childTools, timestamp: forkedAt });
|
|
631
|
+
if (task === -1) additions.push({ role: "system", content: "", sections: { subagent_instructions: instructions }, timestamp: forkedAt });
|
|
632
|
+
else next[task] = prependText(next[task], instructions);
|
|
633
|
+
const at = firstOwn === -1 ? next.length : firstOwn;
|
|
634
|
+
return [...next.slice(0, at), ...additions, ...next.slice(at)];
|
|
635
|
+
}
|
|
636
|
+
function applyForkSessionAffinity(headers, model, parentSessionId, parentModel) {
|
|
637
|
+
const parent = parentSessionId?.trim();
|
|
638
|
+
if (!headers || !parent || model?.api !== "anthropic-messages") return;
|
|
639
|
+
if (!parentModel || parentModel.provider !== model.provider || parentModel.api !== model.api || parentModel.model !== model.id) return;
|
|
640
|
+
const compat = model.compat;
|
|
641
|
+
if (!compat?.sendSessionAffinityHeaders) return;
|
|
642
|
+
headers[compat.sessionAffinityFormat === "openrouter" ? "x-session-id" : "x-session-affinity"] = parent;
|
|
643
|
+
}
|
|
644
|
+
function rewriteForkCacheProviderRequest(event, ctx, forkCacheKey) {
|
|
645
|
+
const key = forkCacheKey?.trim();
|
|
646
|
+
if (!key || !PROMPT_CACHE_KEY_APIS.has(ctx?.model?.api ?? "")) return void 0;
|
|
647
|
+
if (!event || typeof event !== "object") return void 0;
|
|
648
|
+
const payload = event.payload;
|
|
649
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return void 0;
|
|
650
|
+
if (typeof payload.prompt_cache_key !== "string") return void 0;
|
|
651
|
+
return { ...payload, prompt_cache_key: key };
|
|
652
|
+
}
|
|
653
|
+
function portableToolId(id, preserveBoundedCompositeToolIds = false) {
|
|
654
|
+
if (PORTABLE_TOOL_ID_PATTERN.test(id) && id.length <= MAX_PORTABLE_TOOL_ID_LENGTH) return id;
|
|
655
|
+
if (preserveBoundedCompositeToolIds) {
|
|
656
|
+
const parts = id.split("|");
|
|
657
|
+
if (parts.length === 2 && parts.every((part) => PORTABLE_TOOL_ID_PATTERN.test(part) && part.length <= MAX_PORTABLE_TOOL_ID_LENGTH)) return id;
|
|
658
|
+
}
|
|
659
|
+
const encoded = `tool_${Buffer.from(id).toString("base64url") || "empty"}`;
|
|
660
|
+
if (encoded.length <= MAX_PORTABLE_TOOL_ID_LENGTH) return encoded;
|
|
661
|
+
return `tool_${createHash("sha256").update(id).digest("base64url")}`;
|
|
662
|
+
}
|
|
663
|
+
function sanitizeToolHistoryMessage(message, preserveBoundedCompositeToolIds = false) {
|
|
664
|
+
const m = message;
|
|
665
|
+
if (m?.role === "toolResult" && typeof m.toolCallId === "string") {
|
|
666
|
+
const toolCallId = portableToolId(m.toolCallId, preserveBoundedCompositeToolIds);
|
|
667
|
+
return toolCallId === m.toolCallId ? message : { ...m, toolCallId };
|
|
668
|
+
}
|
|
669
|
+
if (m?.role !== "assistant" || !Array.isArray(m.content)) return message;
|
|
670
|
+
let changed = false;
|
|
671
|
+
const content = m.content.map((block) => {
|
|
672
|
+
const b = block;
|
|
673
|
+
if (b?.type !== "toolCall" || typeof b.id !== "string") return block;
|
|
674
|
+
const id = portableToolId(b.id, preserveBoundedCompositeToolIds);
|
|
675
|
+
if (id === b.id) return block;
|
|
676
|
+
changed = true;
|
|
677
|
+
return { ...b, id };
|
|
678
|
+
});
|
|
679
|
+
return changed ? { ...m, content } : message;
|
|
680
|
+
}
|
|
681
|
+
function stripAssistantSubagentToolCallBlocks(message) {
|
|
682
|
+
const m = message;
|
|
683
|
+
if (m?.role !== "assistant" || !Array.isArray(m.content)) return message;
|
|
684
|
+
const filteredContent = m.content.filter((block) => !isSubagentToolCallBlock(block));
|
|
685
|
+
if (filteredContent.length === m.content.length) return message;
|
|
686
|
+
if (filteredContent.length === 0) return void 0;
|
|
687
|
+
return { ...m, content: filteredContent };
|
|
688
|
+
}
|
|
689
|
+
function stripParentOnlySubagentMessages(messages, options = {}) {
|
|
690
|
+
const preserveCurrentFanoutToolHistory = options.preserveFanoutToolHistory === true;
|
|
691
|
+
const sanitizeToolIds = options.sanitizeToolIds ?? true;
|
|
692
|
+
const inheritedBefore = options.inheritedBefore;
|
|
693
|
+
const inherited = (message) => inheritedBefore !== void 0 && typeof message?.timestamp === "number" && message.timestamp < inheritedBefore;
|
|
694
|
+
let changed = false;
|
|
695
|
+
const filtered = [];
|
|
696
|
+
for (const message of messages) {
|
|
697
|
+
const keep = inherited(message);
|
|
698
|
+
if (!keep && (isParentOnlySubagentMessage(message) || !preserveCurrentFanoutToolHistory && isSubagentToolResultMessage(message))) {
|
|
699
|
+
changed = true;
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
const stripped = keep || preserveCurrentFanoutToolHistory ? message : stripAssistantSubagentToolCallBlocks(message);
|
|
703
|
+
if (stripped === void 0) {
|
|
704
|
+
changed = true;
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
const sanitized = sanitizeToolIds ? sanitizeToolHistoryMessage(stripped, options.preserveBoundedCompositeToolIds === true) : stripped;
|
|
708
|
+
if (stripped !== message || sanitized !== stripped) changed = true;
|
|
709
|
+
filtered.push(sanitized);
|
|
710
|
+
}
|
|
711
|
+
return changed ? filtered : messages;
|
|
712
|
+
}
|
|
713
|
+
function registerPermissionGate(pi, permissions, childWatchdog, requestPermission = requestWatchdogPermission) {
|
|
714
|
+
const rules = permissions?.rules;
|
|
715
|
+
if (!rules || Object.keys(rules).length === 0) return;
|
|
716
|
+
const rawWatchdogConfig = childWatchdog ? JSON.stringify(childWatchdog) : void 0;
|
|
717
|
+
const timeoutMs = childWatchdog?.agentEndTimeoutMs ?? 3e4;
|
|
718
|
+
const onRuntimeEvent = pi.on;
|
|
719
|
+
onRuntimeEvent("tool_call", async (event, ctx) => {
|
|
720
|
+
const toolName = typeof event.toolName === "string" ? event.toolName : "tool";
|
|
721
|
+
const decision = permissionDecision(rules, toolName);
|
|
722
|
+
if (decision === "allow") return void 0;
|
|
723
|
+
if (decision === "deny") return { block: true, reason: `Blocked by pi-subagents permission rule: '${toolName}' is denied.` };
|
|
724
|
+
if (ctx.signal?.aborted) return { block: true, reason: "Blocked by pi-subagents permission rule: Watchdog permission decision was cancelled." };
|
|
725
|
+
let timeout;
|
|
726
|
+
let abort;
|
|
727
|
+
let result;
|
|
728
|
+
try {
|
|
729
|
+
result = await Promise.race([
|
|
730
|
+
requestPermission({
|
|
731
|
+
ctx,
|
|
732
|
+
toolName,
|
|
733
|
+
args: event.input ?? {},
|
|
734
|
+
rawWatchdogConfig,
|
|
735
|
+
auditPath: permissions.auditPath,
|
|
736
|
+
...ctx.signal ? { signal: ctx.signal } : {}
|
|
737
|
+
}),
|
|
738
|
+
new Promise((resolve2) => {
|
|
739
|
+
if (!ctx.signal) return;
|
|
740
|
+
abort = () => resolve2({ approved: false, reason: "Watchdog permission decision was cancelled.", source: "watchdog" });
|
|
741
|
+
ctx.signal.addEventListener("abort", abort, { once: true });
|
|
742
|
+
}),
|
|
743
|
+
new Promise((_, reject) => {
|
|
744
|
+
timeout = setTimeout(() => reject(new Error(`Watchdog permission decision timed out after ${timeoutMs}ms.`)), timeoutMs);
|
|
745
|
+
})
|
|
746
|
+
]);
|
|
747
|
+
} catch (error) {
|
|
748
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
749
|
+
return { block: true, reason: `Blocked by pi-subagents permission rule: Watchdog permission arbiter failed closed: ${reason}` };
|
|
750
|
+
} finally {
|
|
751
|
+
if (timeout) clearTimeout(timeout);
|
|
752
|
+
if (abort) ctx.signal?.removeEventListener("abort", abort);
|
|
753
|
+
}
|
|
754
|
+
if (result.approved) return void 0;
|
|
755
|
+
return { block: true, reason: `Blocked by pi-subagents permission rule: ${result.reason}` };
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
function registerToolBudget(pi, budget) {
|
|
759
|
+
if (!budget) return;
|
|
760
|
+
let toolCount = 0;
|
|
761
|
+
let softNudged = false;
|
|
762
|
+
const sendUserMessage = pi.sendUserMessage;
|
|
763
|
+
const onRuntimeEvent = pi.on;
|
|
764
|
+
onRuntimeEvent("tool_call", (event) => {
|
|
765
|
+
const toolName = typeof event.toolName === "string" ? event.toolName : "tool";
|
|
766
|
+
toolCount++;
|
|
767
|
+
if (budget.soft !== void 0 && toolCount >= budget.soft && !softNudged) {
|
|
768
|
+
softNudged = true;
|
|
769
|
+
try {
|
|
770
|
+
sendUserMessage?.(toolBudgetSoftNudge(budget, toolCount), { deliverAs: "steer" });
|
|
771
|
+
} catch {
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
if (!shouldBlockToolForBudget(budget, toolName, toolCount)) return void 0;
|
|
775
|
+
return { block: true, reason: toolBudgetBlockedMessage(budget, toolName, toolCount) };
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
function registerStructuredOutputTool(pi, structured) {
|
|
779
|
+
const terminalState = structured.terminalState ??= { captured: false };
|
|
780
|
+
const required = structured.acceptanceReport === "required";
|
|
781
|
+
const parameters = createStructuredOutputToolParameters(structured.schema, { acceptanceReport: structured.acceptanceReport });
|
|
782
|
+
const registerTool = pi.registerTool;
|
|
783
|
+
registerTool({
|
|
784
|
+
name: "structured_output",
|
|
785
|
+
...MODEL_ONLY_TOOL,
|
|
786
|
+
label: "Structured Output",
|
|
787
|
+
description: "Submit the required final structured output for this subagent step. This terminates the step.",
|
|
788
|
+
parameters,
|
|
789
|
+
async execute(_id, params) {
|
|
790
|
+
const validation = await validateStructuredOutputValue(structured.schema, params.value);
|
|
791
|
+
if (validation.status === "invalid") {
|
|
792
|
+
throw new Error(`Structured output validation failed: ${validation.message}`);
|
|
793
|
+
}
|
|
794
|
+
if (required && params.acceptanceReport === void 0) {
|
|
795
|
+
throw new Error(MISSING_STRUCTURED_ACCEPTANCE_REPORT_ERROR);
|
|
796
|
+
}
|
|
797
|
+
if (required && params.acceptanceReport !== void 0) {
|
|
798
|
+
const acceptanceValidation = validateAcceptanceReport(params.acceptanceReport, "acceptanceReport");
|
|
799
|
+
if (!acceptanceValidation.report) {
|
|
800
|
+
throw new Error(`Invalid structured output acceptance report: ${acceptanceValidation.errors.join("; ")}`);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
structured.capture(params.value, structured.acceptanceReport ? params.acceptanceReport : void 0);
|
|
804
|
+
terminalState.captured = true;
|
|
805
|
+
return {
|
|
806
|
+
content: [{ type: "text", text: "Structured output captured." }],
|
|
807
|
+
details: {},
|
|
808
|
+
terminate: true
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
function registerSubagentPromptRuntime(pi, config, drainObservation) {
|
|
814
|
+
if (!config) return;
|
|
815
|
+
registerRuntimeExtensionAcknowledgements(pi, config.runtimeAcknowledgements);
|
|
816
|
+
registerPermissionGate(pi, config.permissions, config.childWatchdog);
|
|
817
|
+
registerToolBudget(pi, config.toolBudget);
|
|
818
|
+
if (config.structuredOutput && !config.structuredOutput.terminalState) config.structuredOutput.terminalState = { captured: false };
|
|
819
|
+
registerChildWatchdog(pi, config.childWatchdog, config.watchdogStatus, config.structuredOutput?.terminalState);
|
|
820
|
+
const requestedWatchdogDiff = config.requiredTools?.includes(WATCHDOG_DIFF_TOOL_NAME);
|
|
821
|
+
const reviewerLaunchBaseline = requestedWatchdogDiff && config.cwd ? captureWatchdogDiffBaseline(config.cwd) : void 0;
|
|
822
|
+
if (requestedWatchdogDiff && typeof pi.registerTool === "function") {
|
|
823
|
+
pi.registerTool(createWatchdogDiffTool(reviewerLaunchBaseline, { workingTreeAtLaunch: true }));
|
|
824
|
+
}
|
|
825
|
+
const waitState = config.runtimeState ?? {
|
|
826
|
+
baseCwd: "",
|
|
827
|
+
currentSessionId: null,
|
|
828
|
+
asyncJobs: /* @__PURE__ */ new Map(),
|
|
829
|
+
foregroundRuns: /* @__PURE__ */ new Map(),
|
|
830
|
+
foregroundControls: /* @__PURE__ */ new Map(),
|
|
831
|
+
lastForegroundControlId: null,
|
|
832
|
+
cleanupTimers: /* @__PURE__ */ new Map(),
|
|
833
|
+
lastUiContext: null,
|
|
834
|
+
poller: null,
|
|
835
|
+
completionSeen: /* @__PURE__ */ new Map(),
|
|
836
|
+
watcher: null,
|
|
837
|
+
watcherRestartTimer: null,
|
|
838
|
+
resultFileCoalescer: { schedule: () => false, clear: () => {
|
|
839
|
+
} }
|
|
840
|
+
};
|
|
841
|
+
const nestedRootRunId = inheritedNestedRouteOf(config)?.rootRunId;
|
|
842
|
+
if (typeof pi.registerTool === "function") registerWaitTool(pi, waitState, config.waitTool.enabled, void 0, config.waitTool.defaultTimeoutMs, { nestedRootRunId }, () => config.hasPendingSupervisorRequest?.() === true);
|
|
843
|
+
const supervisorMetadata = childSupervisorMetadata(config);
|
|
844
|
+
let nativeSupervisorClientRegistered = false;
|
|
845
|
+
const registerNativeSupervisorClientOnce = () => {
|
|
846
|
+
if (nativeSupervisorClientRegistered) return;
|
|
847
|
+
nativeSupervisorClientRegistered = true;
|
|
848
|
+
registerNativeSupervisorClient(pi, supervisorMetadata);
|
|
849
|
+
};
|
|
850
|
+
const onRuntimeEvent = pi.on;
|
|
851
|
+
onRuntimeEvent("session_start", (_event, ctx) => {
|
|
852
|
+
const sessionManager = ctx?.sessionManager;
|
|
853
|
+
waitState.currentSessionId = sessionManager ? resolveCurrentSessionId(sessionManager) : null;
|
|
854
|
+
registerNativeSupervisorClientOnce();
|
|
855
|
+
});
|
|
856
|
+
onRuntimeEvent("agent_start", () => {
|
|
857
|
+
if (!config.requiredTools) return;
|
|
858
|
+
const diagnostic = evaluateChildToolDiagnostic(config, pi.getAllTools().map((tool) => tool.name));
|
|
859
|
+
config.toolDiagnostic?.(diagnostic);
|
|
860
|
+
if (diagnostic) throw new Error(formatChildToolDiagnostic(diagnostic));
|
|
861
|
+
});
|
|
862
|
+
onRuntimeEvent("agent_end", async (_event, ctx) => {
|
|
863
|
+
if (ctx?.hasUI === true) drainObservation?.deny();
|
|
864
|
+
if (drainObservation) {
|
|
865
|
+
try {
|
|
866
|
+
if (ctx?.sessionManager?.getSessionFile() !== waitState.currentSessionId) drainObservation.deny();
|
|
867
|
+
} catch {
|
|
868
|
+
drainObservation.deny();
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
config.holdFinalDrain?.(true);
|
|
872
|
+
try {
|
|
873
|
+
await drainOutstandingWork({ state: waitState, events: pi.events, nestedRootRunId, hasPendingSupervisorRequest: config.hasPendingSupervisorRequest }, drainObservation);
|
|
874
|
+
} finally {
|
|
875
|
+
config.holdFinalDrain?.(false);
|
|
876
|
+
}
|
|
877
|
+
});
|
|
878
|
+
if (config.structuredOutput) registerStructuredOutputTool(pi, config.structuredOutput);
|
|
879
|
+
onRuntimeEvent("before_provider_request", (event, ctx) => rewriteForkCacheProviderRequest(event, ctx, config.forkCacheKey));
|
|
880
|
+
let inherited;
|
|
881
|
+
const readInherited = (ctx) => {
|
|
882
|
+
const header = ctx?.sessionManager?.getHeader();
|
|
883
|
+
if (!header) return void 0;
|
|
884
|
+
if (inherited?.session !== header.id) {
|
|
885
|
+
const forkedAt = Date.parse(header.timestamp);
|
|
886
|
+
const messages = Number.isFinite(forkedAt) ? ctx.sessionManager.buildSessionProjection().messages : [];
|
|
887
|
+
inherited = { session: header.id, forkedAt, system: systemMessagesBefore(messages, forkedAt), model: modelBefore(messages, forkedAt) };
|
|
888
|
+
}
|
|
889
|
+
return Number.isFinite(inherited.forkedAt) ? inherited : void 0;
|
|
890
|
+
};
|
|
891
|
+
const boundary = rewritesChildPrompt(config) ? childBoundary({ fanoutChild: config.fanoutChild, structuredOutput: Boolean(config.structuredOutput) }) : void 0;
|
|
892
|
+
if (config.forkCacheKey) onRuntimeEvent("before_provider_headers", (event, ctx) => {
|
|
893
|
+
applyForkSessionAffinity(event?.headers, ctx?.model, config.parentSessionId, readInherited(ctx)?.model);
|
|
894
|
+
});
|
|
895
|
+
if (config.forkCacheKey) onRuntimeEvent("context_with_system", (event, ctx) => {
|
|
896
|
+
if (!event || typeof event !== "object" || !("messages" in event) || !Array.isArray(event.messages)) return void 0;
|
|
897
|
+
const fork = readInherited(ctx);
|
|
898
|
+
if (!fork) return void 0;
|
|
899
|
+
const messages = projectForkRequest(event.messages, fork.system, fork.forkedAt, { boundary });
|
|
900
|
+
return messages === event.messages ? void 0 : { messages };
|
|
901
|
+
});
|
|
902
|
+
onRuntimeEvent("context", (event, ctx) => {
|
|
903
|
+
if (!event || typeof event !== "object" || !("messages" in event) || !Array.isArray(event.messages)) return void 0;
|
|
904
|
+
const forkedAt = config.forkCacheKey ? Date.parse(ctx?.sessionManager?.getHeader()?.timestamp ?? "") : Number.NaN;
|
|
905
|
+
const messages = stripParentOnlySubagentMessages(event.messages, {
|
|
906
|
+
sanitizeToolIds: !COMPOSITE_TOOL_ID_APIS.has(ctx?.model?.api ?? ""),
|
|
907
|
+
// Providers that reuse the Codex Responses wire under their own api id (e.g. cliproxyapi-codex-responses) split call_id|item_id the same way.
|
|
908
|
+
preserveBoundedCompositeToolIds: ctx?.model?.api?.endsWith("-codex-responses") === true,
|
|
909
|
+
preserveFanoutToolHistory: config.fanoutChild,
|
|
910
|
+
...Number.isFinite(forkedAt) ? { inheritedBefore: forkedAt } : {}
|
|
911
|
+
});
|
|
912
|
+
if (messages === event.messages) return void 0;
|
|
913
|
+
return { messages };
|
|
914
|
+
});
|
|
915
|
+
const routingName = config.intercomSessionName;
|
|
916
|
+
let intercomIdClaimed = false;
|
|
917
|
+
if (routingName) {
|
|
918
|
+
pi.events.on(INTERCOM_SESSION_IDENTITY_EVENT, (request) => {
|
|
919
|
+
if (!request || typeof request !== "object" || !("version" in request) || request.version !== 1 || !("claim" in request) || typeof request.claim !== "function") return;
|
|
920
|
+
request.claim(routingName);
|
|
921
|
+
intercomIdClaimed = true;
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
onRuntimeEvent("before_agent_start", async (event) => {
|
|
925
|
+
if (!event || typeof event !== "object" || !("systemPrompt" in event) || typeof event.systemPrompt !== "string") return void 0;
|
|
926
|
+
registerNativeSupervisorClientOnce();
|
|
927
|
+
const childSessionName = intercomIdClaimed ? config.sessionName || routingName : routingName || config.sessionName;
|
|
928
|
+
if (childSessionName && typeof pi.setSessionName === "function") {
|
|
929
|
+
pi.setSessionName(childSessionName);
|
|
930
|
+
}
|
|
931
|
+
if (!rewritesChildPrompt(config)) return;
|
|
932
|
+
filterChildPromptOptions(event.systemPromptOptions, {
|
|
933
|
+
inheritProjectContext: config.inheritProjectContext ?? true,
|
|
934
|
+
inheritGlobalContext: config.inheritGlobalContext ?? true,
|
|
935
|
+
inheritSkills: config.inheritSkills ?? true
|
|
936
|
+
});
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
export {
|
|
941
|
+
projectRuntimeAcknowledgedExtensions,
|
|
942
|
+
formatChildToolDiagnostic,
|
|
943
|
+
rewriteSubagentPrompt,
|
|
944
|
+
registerSubagentPromptBoundary,
|
|
945
|
+
registerSubagentPromptRuntime
|
|
946
|
+
};
|