pi-subagents 0.64.0 → 0.65.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/README.md +2 -2
- package/agents/reviewer.md +1 -1
- package/agents/scout.md +1 -1
- package/docs/agents.md +19 -17
- package/docs/configuration.md +23 -15
- package/docs/extension-api.md +18 -6
- package/docs/missions.md +2 -0
- package/docs/observability.md +10 -12
- package/docs/tool-reference.md +15 -8
- package/docs/watchdog.md +1 -1
- package/docs/workflows.md +13 -3
- package/package.json +5 -4
- package/runner-server-preload.mjs +13 -0
- package/skills/pi-subagents/SKILL.md +2 -1
- package/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/skills/pi-subagents/references/execution-controls.md +7 -3
- package/skills/pi-subagents/references/multi-lane-orchestration.md +2 -0
- 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 +7 -4
- package/src/extension/config.ts +4 -2
- package/src/extension/doctor.ts +2 -10
- package/src/extension/fanout-child.ts +9 -11
- package/src/extension/index.ts +58 -7
- package/src/extension/public-execution.ts +14 -0
- package/src/extension/rpc.ts +3 -2
- package/src/extension/schemas.ts +3 -2
- package/src/extension/tool-description.ts +13 -8
- package/src/integrations/pi-web-session-liveness.ts +73 -0
- package/src/intercom/native-supervisor-channel.ts +159 -95
- package/src/intercom/supervisor-ui.ts +244 -0
- package/src/missions/workflow-state.ts +37 -16
- package/src/runs/background/async-execution.ts +51 -25
- package/src/runs/background/async-job-tracker.ts +11 -0
- package/src/runs/background/async-resume.ts +14 -3
- package/src/runs/background/async-retention.ts +9 -0
- package/src/runs/background/control-channel.ts +2 -204
- package/src/runs/background/notify.ts +2 -0
- package/src/runs/background/process-terminal.ts +1 -1
- package/src/runs/background/retained-nested-route-tracker.ts +96 -0
- package/src/runs/background/run-child-session.ts +614 -0
- package/src/runs/background/run-status.ts +0 -1
- package/src/runs/background/runner-aliases.ts +152 -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 +203 -901
- package/src/runs/foreground/async-steering-action.ts +1 -17
- package/src/runs/foreground/execution.ts +204 -378
- package/src/runs/foreground/foreground-control.ts +4 -0
- package/src/runs/foreground/foreground-history.ts +3 -1
- package/src/runs/foreground/prompt-audit.ts +9 -5
- package/src/runs/foreground/subagent-executor.ts +161 -91
- package/src/runs/foreground/workflow-foreground-steering.ts +24 -98
- package/src/runs/shared/abort-recovery.ts +3 -3
- package/src/runs/shared/acceptance.ts +14 -1
- 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 +337 -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/llm-intent-arbiter.ts +20 -11
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
- package/src/runs/shared/model-exclusions.ts +84 -15
- package/src/runs/shared/model-fallback.ts +78 -6
- package/src/runs/shared/nested-events.ts +32 -48
- 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 +1 -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 +31 -9
- package/src/runs/shared/worktree.ts +192 -40
- package/src/shared/child-session-name.ts +1 -1
- package/src/shared/jsonl-writer.ts +11 -0
- package/src/shared/model-response-aliases.ts +13 -0
- package/src/shared/thinking-ceiling.ts +0 -6
- package/src/shared/types.ts +63 -28
- package/src/shared/utils.ts +6 -4
- package/src/shared/watch-strategy.ts +2 -0
- package/src/slash/slash-commands.ts +0 -6
- package/src/tui/fleet-status.ts +1 -1
- package/src/tui/fleet.ts +0 -1
- package/src/tui/render.ts +254 -41
- package/src/watchdog/child-status.ts +0 -1
- package/src/watchdog/register-child.ts +12 -12
- package/src/workflows/scripted-workflow.ts +80 -7
- package/src/workflows/workflow-checklist.ts +4 -3
- 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
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import type { EntryRenderOptions, MessageRenderOptions, Theme } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component } from "@earendil-works/pi-tui";
|
|
3
|
+
import { safeTerminalText } from "../shared/display-text.ts";
|
|
4
|
+
|
|
5
|
+
export const SUPERVISOR_REQUEST_MESSAGE_TYPE = "subagent_supervisor_request";
|
|
6
|
+
export const SUPERVISOR_REPLY_ENTRY_TYPE = "subagent_supervisor_reply";
|
|
7
|
+
|
|
8
|
+
export type SupervisorReason = "need_decision" | "interview_request" | "progress_update";
|
|
9
|
+
|
|
10
|
+
export interface SupervisorRequestMessageDetails {
|
|
11
|
+
id?: string;
|
|
12
|
+
requestId?: string;
|
|
13
|
+
reason?: SupervisorReason;
|
|
14
|
+
expectsReply?: boolean;
|
|
15
|
+
runId?: string;
|
|
16
|
+
agent?: string;
|
|
17
|
+
childIndex?: number;
|
|
18
|
+
childTarget?: string;
|
|
19
|
+
interview?: unknown;
|
|
20
|
+
requestBody?: string;
|
|
21
|
+
replyHint?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SupervisorReplyEntryData {
|
|
25
|
+
requestId: string;
|
|
26
|
+
reason?: SupervisorReason;
|
|
27
|
+
runId: string;
|
|
28
|
+
agent: string;
|
|
29
|
+
childIndex: number;
|
|
30
|
+
childTarget?: string;
|
|
31
|
+
message: string;
|
|
32
|
+
createdAt: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface SupervisorMessageLike {
|
|
36
|
+
content: unknown;
|
|
37
|
+
details?: unknown;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface SupervisorEntryLike {
|
|
41
|
+
data?: unknown;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const MAX_FIELD_CHARS = 512;
|
|
45
|
+
const MAX_BODY_CHARS = 8_000;
|
|
46
|
+
const MAX_INTERVIEW_CHARS = 4_000;
|
|
47
|
+
const MAX_RENDER_LINES = 36;
|
|
48
|
+
const TRUNCATION_MARKER = "[truncated]";
|
|
49
|
+
|
|
50
|
+
export function supervisorReplyHint(requestId: string): string {
|
|
51
|
+
return `subagent_supervisor({ action: "reply", replyTo: "${requestId}", message: "..." })`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function boundedText(value: string, maxChars: number): string {
|
|
55
|
+
const safe = safeTerminalText(value);
|
|
56
|
+
if (safe.length <= maxChars) return safe;
|
|
57
|
+
const prefixLength = Math.max(0, maxChars - TRUNCATION_MARKER.length - 1);
|
|
58
|
+
let prefix = "";
|
|
59
|
+
for (const character of safe) {
|
|
60
|
+
if (prefix.length + character.length > prefixLength) break;
|
|
61
|
+
prefix += character;
|
|
62
|
+
}
|
|
63
|
+
return `${prefix} ${TRUNCATION_MARKER}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function displayText(value: string, maxChars: number, expanded: boolean): string {
|
|
67
|
+
return expanded ? safeTerminalText(value) : boundedText(value, maxChars);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function boundedField(value: unknown, fallback = "unknown"): string {
|
|
71
|
+
return boundedText(typeof value === "string" ? value : value === undefined ? fallback : String(value), MAX_FIELD_CHARS);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function contentText(content: unknown): string {
|
|
75
|
+
if (typeof content === "string") return content;
|
|
76
|
+
if (!Array.isArray(content)) return "";
|
|
77
|
+
return content
|
|
78
|
+
.map((part) => {
|
|
79
|
+
if (!part || typeof part !== "object") return "";
|
|
80
|
+
const text = (part as { text?: unknown }).text;
|
|
81
|
+
return typeof text === "string" ? text : "";
|
|
82
|
+
})
|
|
83
|
+
.filter(Boolean)
|
|
84
|
+
.join("\n");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function interviewText(interview: unknown, expanded: boolean): string {
|
|
88
|
+
let serialized: string;
|
|
89
|
+
try {
|
|
90
|
+
serialized = JSON.stringify(interview, null, 2) ?? String(interview);
|
|
91
|
+
} catch {
|
|
92
|
+
serialized = "[unavailable]";
|
|
93
|
+
}
|
|
94
|
+
return displayText(serialized, MAX_INTERVIEW_CHARS, expanded);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
98
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function isSupervisorReason(value: unknown): value is SupervisorReason {
|
|
102
|
+
return value === "need_decision" || value === "interview_request" || value === "progress_update";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function optionalString(value: unknown): boolean {
|
|
106
|
+
return value === undefined || typeof value === "string";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function requestDetails(value: unknown): SupervisorRequestMessageDetails | undefined {
|
|
110
|
+
if (!isRecord(value)) return undefined;
|
|
111
|
+
if (!optionalString(value.id) || !optionalString(value.requestId) || !optionalString(value.replyHint) || !optionalString(value.requestBody) || !optionalString(value.runId) || !optionalString(value.agent) || !optionalString(value.childTarget)) return undefined;
|
|
112
|
+
if (value.reason !== undefined && !isSupervisorReason(value.reason)) return undefined;
|
|
113
|
+
if (value.expectsReply !== undefined && typeof value.expectsReply !== "boolean") return undefined;
|
|
114
|
+
if (value.childIndex !== undefined && (typeof value.childIndex !== "number" || !Number.isFinite(value.childIndex))) return undefined;
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function replyData(value: unknown): SupervisorReplyEntryData | undefined {
|
|
119
|
+
if (!isRecord(value)) return undefined;
|
|
120
|
+
if (typeof value.requestId !== "string" || typeof value.runId !== "string" || typeof value.agent !== "string" || typeof value.message !== "string") return undefined;
|
|
121
|
+
if (value.reason !== undefined && !isSupervisorReason(value.reason)) return undefined;
|
|
122
|
+
if (value.childTarget !== undefined && typeof value.childTarget !== "string") return undefined;
|
|
123
|
+
if (typeof value.childIndex !== "number" || !Number.isFinite(value.childIndex) || typeof value.createdAt !== "number" || !Number.isFinite(value.createdAt)) return undefined;
|
|
124
|
+
return {
|
|
125
|
+
requestId: value.requestId,
|
|
126
|
+
...(value.reason === undefined ? {} : { reason: value.reason }),
|
|
127
|
+
runId: value.runId,
|
|
128
|
+
agent: value.agent,
|
|
129
|
+
childIndex: value.childIndex,
|
|
130
|
+
...(value.childTarget === undefined ? {} : { childTarget: value.childTarget }),
|
|
131
|
+
message: value.message,
|
|
132
|
+
createdAt: value.createdAt,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function withRequestId(details: SupervisorRequestMessageDetails): string {
|
|
137
|
+
return boundedField(details.requestId ?? details.id);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function requestHeading(reason: SupervisorReason | undefined): string {
|
|
141
|
+
if (reason === "interview_request") return "⚠ Supervisor interview request";
|
|
142
|
+
if (reason === "progress_update") return "ℹ Supervisor progress update";
|
|
143
|
+
return "⚠ Supervisor decision request";
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function requestLines(message: SupervisorMessageLike, details: SupervisorRequestMessageDetails, expanded: boolean): string[] {
|
|
147
|
+
const requestId = withRequestId(details);
|
|
148
|
+
const lines = [
|
|
149
|
+
`Reason: ${boundedField(details.reason)}`,
|
|
150
|
+
`Run: ${boundedField(details.runId)}`,
|
|
151
|
+
`Agent: ${boundedField(details.agent)}`,
|
|
152
|
+
`Child index: ${boundedField(details.childIndex)}`,
|
|
153
|
+
];
|
|
154
|
+
if (details.childTarget) lines.push(`Child target: ${boundedField(details.childTarget)}`);
|
|
155
|
+
lines.push(`Request ID: ${requestId}`);
|
|
156
|
+
if (details.expectsReply) lines.push(`Reply with: ${displayText(details.replyHint ?? supervisorReplyHint(requestId), MAX_BODY_CHARS, expanded)}`);
|
|
157
|
+
lines.push("", "Request:", displayText((details.requestBody ?? contentText(message.content)) || "(no request body)", MAX_BODY_CHARS, expanded));
|
|
158
|
+
if (details.interview !== undefined) lines.push("", "Interview shape:", interviewText(details.interview, expanded));
|
|
159
|
+
return lines;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function replyLines(data: SupervisorReplyEntryData, expanded: boolean): string[] {
|
|
163
|
+
const requestId = boundedField(data.requestId);
|
|
164
|
+
const lines = [
|
|
165
|
+
...(data.reason ? [`Reason: ${boundedField(data.reason)}`] : []),
|
|
166
|
+
`Run: ${boundedField(data.runId)}`,
|
|
167
|
+
`Agent: ${boundedField(data.agent)}`,
|
|
168
|
+
`Child index: ${boundedField(data.childIndex)}`,
|
|
169
|
+
];
|
|
170
|
+
if (data.childTarget) lines.push(`Child target: ${boundedField(data.childTarget)}`);
|
|
171
|
+
lines.push(`Reply to: ${requestId}`, "", "Reply:", displayText(data.message || "(empty reply)", MAX_BODY_CHARS, expanded));
|
|
172
|
+
return lines;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function renderCard(lines: string[], heading: string, theme: Theme, width: number, expanded: boolean): string[] {
|
|
176
|
+
const safeWidth = Math.max(0, Math.floor(width));
|
|
177
|
+
if (safeWidth < 3) return [truncateToWidth(heading, safeWidth, "")];
|
|
178
|
+
const bodyWidth = safeWidth - 2;
|
|
179
|
+
const headerText = truncateToWidth(` ${heading} `, bodyWidth, "");
|
|
180
|
+
const headerPadding = Math.max(0, bodyWidth - visibleWidth(headerText));
|
|
181
|
+
const border = (text: string) => theme.fg("accent", text);
|
|
182
|
+
const rendered = [border(`╭${headerText}${"─".repeat(headerPadding)}╮`)];
|
|
183
|
+
let hidden = false;
|
|
184
|
+
let interiorLines = 0;
|
|
185
|
+
for (const line of lines) {
|
|
186
|
+
const wrapped = wrapTextWithAnsi(line, Math.max(1, bodyWidth));
|
|
187
|
+
for (const wrappedLine of wrapped.length > 0 ? wrapped : [""]) {
|
|
188
|
+
if (!expanded && interiorLines >= MAX_RENDER_LINES) {
|
|
189
|
+
hidden = true;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
const text = truncateToWidth(wrappedLine, bodyWidth, "");
|
|
193
|
+
rendered.push(border(`│${text}${" ".repeat(Math.max(0, bodyWidth - visibleWidth(text)))}│`));
|
|
194
|
+
interiorLines++;
|
|
195
|
+
}
|
|
196
|
+
if (hidden) break;
|
|
197
|
+
}
|
|
198
|
+
if (hidden) {
|
|
199
|
+
const text = truncateToWidth(TRUNCATION_MARKER, bodyWidth, "");
|
|
200
|
+
rendered.push(border(`│${text}${" ".repeat(Math.max(0, bodyWidth - visibleWidth(text)))}│`));
|
|
201
|
+
}
|
|
202
|
+
rendered.push(border(`╰${"─".repeat(bodyWidth)}╯`));
|
|
203
|
+
return rendered;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
class SupervisorCardComponent implements Component {
|
|
207
|
+
private readonly expanded: boolean;
|
|
208
|
+
private readonly heading: string;
|
|
209
|
+
private readonly lines: string[];
|
|
210
|
+
private readonly theme: Theme;
|
|
211
|
+
|
|
212
|
+
constructor(heading: string, lines: string[], theme: Theme, expanded: boolean) {
|
|
213
|
+
this.expanded = expanded;
|
|
214
|
+
this.heading = heading;
|
|
215
|
+
this.lines = lines;
|
|
216
|
+
this.theme = theme;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
invalidate(): void {}
|
|
220
|
+
|
|
221
|
+
render(width: number): string[] {
|
|
222
|
+
return renderCard(this.lines, this.heading, this.theme, width, this.expanded);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function renderSupervisorRequest(
|
|
227
|
+
message: SupervisorMessageLike,
|
|
228
|
+
options: MessageRenderOptions,
|
|
229
|
+
theme: Theme,
|
|
230
|
+
): Component | undefined {
|
|
231
|
+
const details = requestDetails(message.details);
|
|
232
|
+
if (!details) return undefined;
|
|
233
|
+
return new SupervisorCardComponent(requestHeading(details.reason), requestLines(message, details, options.expanded), theme, options.expanded);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function renderSupervisorReply(
|
|
237
|
+
entry: SupervisorEntryLike,
|
|
238
|
+
options: EntryRenderOptions,
|
|
239
|
+
theme: Theme,
|
|
240
|
+
): Component | undefined {
|
|
241
|
+
const data = replyData(entry.data);
|
|
242
|
+
if (!data) return undefined;
|
|
243
|
+
return new SupervisorCardComponent("↩ Supervisor reply to child", replyLines(data, options.expanded), theme, options.expanded);
|
|
244
|
+
}
|
|
@@ -18,6 +18,12 @@ export interface MissionWorkflowState {
|
|
|
18
18
|
set(key: string, value: unknown): void;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
export interface MissionWorkflowStateOptions {
|
|
22
|
+
isProcessAlive?: (pid: number) => boolean;
|
|
23
|
+
getProcessStartKey?: (pid: number) => string | undefined;
|
|
24
|
+
retryDelaysMs?: readonly number[];
|
|
25
|
+
}
|
|
26
|
+
|
|
21
27
|
export function missionStatePath(location: MissionStoreLocation, missionId: string): string {
|
|
22
28
|
return path.join(location.missionDir, validateMissionId(missionId), "state.json");
|
|
23
29
|
}
|
|
@@ -66,13 +72,22 @@ function windowsProcessStartKey(pid: number): string | undefined {
|
|
|
66
72
|
}
|
|
67
73
|
}
|
|
68
74
|
|
|
75
|
+
let currentProcessKey: string | undefined | null = null;
|
|
76
|
+
|
|
69
77
|
function processStartKey(pid: number): string | undefined {
|
|
78
|
+
// Foreign PIDs can be reused while this process lives, so resolve them afresh.
|
|
79
|
+
if (pid !== process.pid) return computeProcessStartKey(pid);
|
|
80
|
+
if (currentProcessKey === null) currentProcessKey = computeProcessStartKey(pid);
|
|
81
|
+
return currentProcessKey;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function computeProcessStartKey(pid: number): string | undefined {
|
|
70
85
|
if (process.platform === "linux") return linuxProcessStartKey(pid) ?? psProcessStartKey(pid);
|
|
71
86
|
if (process.platform === "win32") return windowsProcessStartKey(pid);
|
|
72
87
|
return undefined;
|
|
73
88
|
}
|
|
74
89
|
|
|
75
|
-
|
|
90
|
+
type StateLockOptions = Required<MissionWorkflowStateOptions>;
|
|
76
91
|
|
|
77
92
|
function readStateLockOwner(lockPath: string): StateLockOwner | undefined {
|
|
78
93
|
try {
|
|
@@ -91,13 +106,13 @@ function readStateLockOwner(lockPath: string): StateLockOwner | undefined {
|
|
|
91
106
|
return undefined;
|
|
92
107
|
}
|
|
93
108
|
|
|
94
|
-
function stateLockIsStale(lockPath: string, now = Date.now()): boolean {
|
|
109
|
+
function stateLockIsStale(lockPath: string, options: StateLockOptions, now = Date.now()): boolean {
|
|
95
110
|
const owner = readStateLockOwner(lockPath);
|
|
96
111
|
if (owner) {
|
|
97
|
-
if (!isProcessAlive(owner.pid)) return true;
|
|
112
|
+
if (!options.isProcessAlive(owner.pid)) return true;
|
|
98
113
|
if (owner.processKey) {
|
|
99
|
-
const
|
|
100
|
-
if (
|
|
114
|
+
const observedProcessKey = options.getProcessStartKey(owner.pid);
|
|
115
|
+
if (observedProcessKey) return owner.processKey !== observedProcessKey;
|
|
101
116
|
if (owner.pid === process.pid) return true;
|
|
102
117
|
}
|
|
103
118
|
return false;
|
|
@@ -140,11 +155,11 @@ function waitForStateLock(delayMs: number | undefined, lockPath: string): void {
|
|
|
140
155
|
waitForFileSystemRetry(delayMs);
|
|
141
156
|
}
|
|
142
157
|
|
|
143
|
-
function reclaimStaleStateLock(lockPath: string, reclaimPath: string): boolean {
|
|
144
|
-
if (!stateLockIsStale(lockPath)) return false;
|
|
158
|
+
function reclaimStaleStateLock(lockPath: string, reclaimPath: string, options: StateLockOptions): boolean {
|
|
159
|
+
if (!stateLockIsStale(lockPath, options)) return false;
|
|
145
160
|
if (!tryMakeDirectory(reclaimPath, 0o700)) return false;
|
|
146
161
|
try {
|
|
147
|
-
if (!stateLockIsStale(lockPath)) return false;
|
|
162
|
+
if (!stateLockIsStale(lockPath, options)) return false;
|
|
148
163
|
fs.rmSync(lockPath, { recursive: true, force: true });
|
|
149
164
|
return true;
|
|
150
165
|
} finally {
|
|
@@ -152,7 +167,7 @@ function reclaimStaleStateLock(lockPath: string, reclaimPath: string): boolean {
|
|
|
152
167
|
}
|
|
153
168
|
}
|
|
154
169
|
|
|
155
|
-
function withStateFileLock<T>(filePath: string, operation: () => T): T {
|
|
170
|
+
function withStateFileLock<T>(filePath: string, operation: () => T, options: StateLockOptions): T {
|
|
156
171
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
157
172
|
const lockPath = `${filePath}.lock`;
|
|
158
173
|
const reclaimPath = `${lockPath}.reclaim`;
|
|
@@ -163,7 +178,7 @@ function withStateFileLock<T>(filePath: string, operation: () => T): T {
|
|
|
163
178
|
fs.rmSync(reclaimPath, { recursive: true, force: true });
|
|
164
179
|
continue;
|
|
165
180
|
}
|
|
166
|
-
waitForStateLock(
|
|
181
|
+
waitForStateLock(options.retryDelaysMs[attempt], lockPath);
|
|
167
182
|
continue;
|
|
168
183
|
}
|
|
169
184
|
let acquired = false;
|
|
@@ -171,17 +186,18 @@ function withStateFileLock<T>(filePath: string, operation: () => T): T {
|
|
|
171
186
|
acquired = tryMakeDirectory(lockPath, 0o700);
|
|
172
187
|
} catch (error) {
|
|
173
188
|
if (isRetryableFileSystemError(error)) {
|
|
174
|
-
waitForStateLock(
|
|
189
|
+
waitForStateLock(options.retryDelaysMs[attempt], lockPath);
|
|
175
190
|
continue;
|
|
176
191
|
}
|
|
177
192
|
throw new Error(`Failed to acquire mission state lock '${lockPath}': ${error instanceof Error ? error.message : String(error)}`);
|
|
178
193
|
}
|
|
179
194
|
if (!acquired) {
|
|
180
|
-
if (reclaimStaleStateLock(lockPath, reclaimPath)) continue;
|
|
181
|
-
waitForStateLock(
|
|
195
|
+
if (reclaimStaleStateLock(lockPath, reclaimPath, options)) continue;
|
|
196
|
+
waitForStateLock(options.retryDelaysMs[attempt], lockPath);
|
|
182
197
|
continue;
|
|
183
198
|
}
|
|
184
|
-
|
|
199
|
+
const key = options.getProcessStartKey(process.pid);
|
|
200
|
+
owner = { pid: process.pid, token: randomUUID(), createdAt: Date.now(), ...(key ? { processKey: key } : {}) };
|
|
185
201
|
try {
|
|
186
202
|
fs.writeFileSync(path.join(lockPath, "owner.json"), JSON.stringify(owner), { encoding: "utf-8", mode: 0o600 });
|
|
187
203
|
} catch (error) {
|
|
@@ -205,8 +221,13 @@ function validateStateKey(value: unknown): string {
|
|
|
205
221
|
return value;
|
|
206
222
|
}
|
|
207
223
|
|
|
208
|
-
export function createMissionWorkflowState(location: MissionStoreLocation, missionId: string): MissionWorkflowState {
|
|
224
|
+
export function createMissionWorkflowState(location: MissionStoreLocation, missionId: string, options: MissionWorkflowStateOptions = {}): MissionWorkflowState {
|
|
209
225
|
const filePath = missionStatePath(location, missionId);
|
|
226
|
+
const lockOptions: StateLockOptions = {
|
|
227
|
+
isProcessAlive: options.isProcessAlive ?? isProcessAlive,
|
|
228
|
+
getProcessStartKey: options.getProcessStartKey ?? processStartKey,
|
|
229
|
+
retryDelaysMs: options.retryDelaysMs ?? DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS,
|
|
230
|
+
};
|
|
210
231
|
let loaded = false;
|
|
211
232
|
let values: Record<string, unknown> = Object.create(null) as Record<string, unknown>;
|
|
212
233
|
|
|
@@ -254,7 +275,7 @@ export function createMissionWorkflowState(location: MissionStoreLocation, missi
|
|
|
254
275
|
writePrivateAtomicJson(filePath, next);
|
|
255
276
|
values = next;
|
|
256
277
|
loaded = true;
|
|
257
|
-
});
|
|
278
|
+
}, lockOptions);
|
|
258
279
|
},
|
|
259
280
|
};
|
|
260
281
|
}
|
|
@@ -14,13 +14,14 @@ import { appendAgentRefinementOverlay } from "../../agents/agent-refinements.ts"
|
|
|
14
14
|
import { createAtomicJsonWriter, writePrivateAtomicJson } from "../../shared/atomic-json.ts";
|
|
15
15
|
import { currentCompletionOwnerId } from "../../shared/completion-owner.ts";
|
|
16
16
|
import { planChildLaunch, resolveStepBehavior, suppressProgressForReadOnlyTask, type ResolvedStepBehavior } from "../shared/child-launch-plan.ts";
|
|
17
|
-
import { applyThinkingSuffix, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan } from "../shared/
|
|
17
|
+
import { applyThinkingSuffix, projectLaunchResolvedChildExtensions, resolvePiLaunchToolPlan } from "../shared/child-tool-plan.ts";
|
|
18
18
|
import { injectOutputPathSystemPrompt, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
|
|
19
19
|
import { applyWatchdogLaunchRules, sendRuleViolationWarning } from "../../watchdog/rules.ts";
|
|
20
20
|
import { buildChainInstructions, isDynamicParallelStep, isParallelStep, resolveExistingReadInstructionPaths, resolveExistingReadPaths, writeInitialProgressFile, type ChainStep, type SequentialStep, type StepOverrides } from "../../shared/settings.ts";
|
|
21
21
|
import type { RunnerStep } from "../shared/parallel-utils.ts";
|
|
22
22
|
import type { ContextMode } from "../shared/context-mode.ts";
|
|
23
|
-
import { resolveInstalledPiPackageRoot, resolvePiPackageRoot } from "../shared/pi-spawn.ts";
|
|
23
|
+
import { PI_CODING_AGENT_PACKAGE, resolveInstalledPiPackageRoot, resolvePiPackageRoot } from "../shared/pi-spawn.ts";
|
|
24
|
+
import { JITI_ALIAS_ENV, resolveHostPeerAliases } from "./runner-aliases.ts";
|
|
24
25
|
import { preflightLaunchCwd } from "../shared/launch-cwd.ts";
|
|
25
26
|
import { resolveNodeExecutable } from "../../shared/node-executable.ts";
|
|
26
27
|
import { backgroundProcessOptions } from "../shared/background-process-options.ts";
|
|
@@ -31,7 +32,7 @@ import { buildModelCandidates, resolveEffectiveSubagentModel, resolveModelOrigin
|
|
|
31
32
|
import { resolveToolTimeoutMs, toolTimeoutFromEnv } from "../shared/tool-timeout.ts";
|
|
32
33
|
import { resolveModelScopesForAgent, type ModelScopeConfig } from "../shared/model-scope.ts";
|
|
33
34
|
import { findModelInfo, resolveEffectiveThinking } from "../../shared/model-info.ts";
|
|
34
|
-
import { assertThinkingWithinCeiling,
|
|
35
|
+
import { assertThinkingWithinCeiling, intersectThinkingCeilings, type ThinkingLevel } from "../../shared/thinking-ceiling.ts";
|
|
35
36
|
import { resolveExpectedWorktreeAgentCwd, resolveWorktreeProvider, shouldDeferWorktreeCwd, WORKTREE_AGENT_CWD_PLACEHOLDER } from "../shared/worktree.ts";
|
|
36
37
|
import { buildWorkflowGraphSnapshot } from "../shared/workflow-graph.ts";
|
|
37
38
|
import { ChainOutputValidationError, validateChainOutputBindings } from "../shared/chain-outputs.ts";
|
|
@@ -65,7 +66,10 @@ import {
|
|
|
65
66
|
getAsyncConfigPath,
|
|
66
67
|
resolveChildMaxSubagentDepth,
|
|
67
68
|
} from "../../shared/types.ts";
|
|
68
|
-
import {
|
|
69
|
+
import { inheritedNestedParentAddressOf, inheritedNestedRouteOf, nestedResultsPath, nestedSummaryFromAsyncStatus, writeNestedEvent } from "../shared/nested-events.ts";
|
|
70
|
+
import type { ChildRuntimeConfig } from "../shared/child-runtime-config.ts";
|
|
71
|
+
import { childSessionFactoryModule } from "../shared/child-session.ts";
|
|
72
|
+
import { inheritedChildRuntime } from "../shared/child-launch.ts";
|
|
69
73
|
import { resultFilePath } from "./result-files.ts";
|
|
70
74
|
import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
|
|
71
75
|
import { usageBudgetState } from "../shared/usage-budget.ts";
|
|
@@ -75,7 +79,7 @@ import { finalizeProcessTerminal, initializeProcessTerminal, readProcessTerminal
|
|
|
75
79
|
import type { ActiveAsyncCapacityHandle } from "./active-async-capacity.ts";
|
|
76
80
|
import { statusStepDescription } from "./chain-append.ts";
|
|
77
81
|
import { SUBAGENT_PROCESS_TERMINAL_EVENT } from "../../shared/types.ts";
|
|
78
|
-
import { assertAgentAllowedByCapabilityCeiling,
|
|
82
|
+
import { assertAgentAllowedByCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
|
|
79
83
|
import { agentDefinitionDigest, launchBindingDigest } from "../../shared/launch-contract.ts";
|
|
80
84
|
import { resolvePermissionRules, type PermissionConfig } from "../shared/permissions.ts";
|
|
81
85
|
import { normalizeExtensionBindings, omitExtensionBindingsEnv, type ExtensionBindings } from "../shared/extension-bindings.ts";
|
|
@@ -142,8 +146,11 @@ interface AsyncExecutionContext {
|
|
|
142
146
|
currentModel?: ParentModel;
|
|
143
147
|
/** Optional model-scope enforcement resolved from subagent settings. */
|
|
144
148
|
modelScope?: ModelScopeConfig;
|
|
149
|
+
modelResponseAliases?: Record<string, string[]>;
|
|
145
150
|
/** Whether the parent session has an interactive UI. */
|
|
146
151
|
interactive?: boolean;
|
|
152
|
+
/** The executor's own child runtime when the launch comes from an in-process child. */
|
|
153
|
+
childRuntime?: ChildRuntimeConfig;
|
|
147
154
|
}
|
|
148
155
|
|
|
149
156
|
export const DEFAULT_ASYNC_TIMEOUT_MS = 30 * 60 * 1000;
|
|
@@ -179,6 +186,7 @@ interface AsyncChainParams {
|
|
|
179
186
|
worktreeSetupHook?: string;
|
|
180
187
|
worktreeSetupHookTimeoutMs?: number;
|
|
181
188
|
worktreeBaseDir?: string;
|
|
189
|
+
baseRef?: string;
|
|
182
190
|
worktreeProvider?: import("../../shared/types.ts").WorktreeProvider;
|
|
183
191
|
worktreeBranchPrefix?: string;
|
|
184
192
|
controlConfig?: ResolvedControlConfig;
|
|
@@ -248,6 +256,7 @@ interface AsyncSingleParams {
|
|
|
248
256
|
worktreeSetupHook?: string;
|
|
249
257
|
worktreeSetupHookTimeoutMs?: number;
|
|
250
258
|
worktreeBaseDir?: string;
|
|
259
|
+
baseRef?: string;
|
|
251
260
|
worktreeProvider?: import("../../shared/types.ts").WorktreeProvider;
|
|
252
261
|
worktreeBranchPrefix?: string;
|
|
253
262
|
worktree?: boolean;
|
|
@@ -536,6 +545,13 @@ function spawnRunner(cfg: object, suffix: string, cwd: string, initialStatus: Om
|
|
|
536
545
|
if (!jitiCliPath) {
|
|
537
546
|
return { error: "upstream jiti for TypeScript execution could not be found; ensure package dependencies are installed" };
|
|
538
547
|
}
|
|
548
|
+
if (!piPackageRoot) {
|
|
549
|
+
return { error: `Background children require pi installed as the npm package (${PI_CODING_AGENT_PACKAGE}); a standalone pi binary has no package directory, so the async runner cannot create child sessions. Run this child in the foreground (async: false) or install pi from npm.` };
|
|
550
|
+
}
|
|
551
|
+
const hostPeerAliases = resolveHostPeerAliases(piPackageRoot);
|
|
552
|
+
if (hostPeerAliases.missing.length > 0) {
|
|
553
|
+
return { error: `Background children require pi installed as the npm package (${PI_CODING_AGENT_PACKAGE}) with its dependencies; ${piPackageRoot} does not provide ${hostPeerAliases.missing.join(", ")}, so the async runner cannot create child sessions. A standalone pi binary cannot run background children.` };
|
|
554
|
+
}
|
|
539
555
|
|
|
540
556
|
fs.mkdirSync(TEMP_ROOT_DIR, { recursive: true });
|
|
541
557
|
const cfgPath = getAsyncConfigPath(suffix);
|
|
@@ -571,13 +587,17 @@ function spawnRunner(cfg: object, suffix: string, cwd: string, initialStatus: Om
|
|
|
571
587
|
stdoutFd = fs.openSync(logPaths.stdoutPath, "a");
|
|
572
588
|
stderrFd = fs.openSync(logPaths.stderrPath, "a");
|
|
573
589
|
}
|
|
574
|
-
const
|
|
590
|
+
const preload = hostPeerAliases.supplemental.length > 0
|
|
591
|
+
? ["--import", new URL("../../../runner-server-preload.mjs", import.meta.url).href]
|
|
592
|
+
: [];
|
|
593
|
+
const proc = spawn(nodeCommand, [...preload, jitiCliPath, runner, cfgPath], {
|
|
575
594
|
cwd,
|
|
576
595
|
...backgroundProcessOptions(),
|
|
577
596
|
stdio: ["ignore", stdoutFd ?? "ignore", stderrFd ?? "ignore"],
|
|
578
597
|
env: {
|
|
579
598
|
...omitExtensionBindingsEnv(process.env),
|
|
580
|
-
|
|
599
|
+
[PI_CODING_AGENT_PACKAGE_ROOT_ENV]: piPackageRoot,
|
|
600
|
+
[JITI_ALIAS_ENV]: JSON.stringify(hostPeerAliases.aliases),
|
|
581
601
|
},
|
|
582
602
|
});
|
|
583
603
|
closeFd(stdoutFd);
|
|
@@ -833,7 +853,7 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
833
853
|
if (unsupported.length > 0) throw new AsyncStartValidationError(`Agent '${a.name}' uses runner.type='${externalRunnerType}' and does not support: ${unsupported.join(", ")}.`);
|
|
834
854
|
}
|
|
835
855
|
try {
|
|
836
|
-
assertAgentAllowedByCapabilityCeiling(a.name, intersectSubagentCapabilityCeilings(params.capabilityCeiling,
|
|
856
|
+
assertAgentAllowedByCapabilityCeiling(a.name, intersectSubagentCapabilityCeilings(params.capabilityCeiling, ctx.childRuntime?.capabilityCeiling));
|
|
837
857
|
} catch (error) {
|
|
838
858
|
throw new AsyncStartValidationError(error instanceof Error ? error.message : String(error));
|
|
839
859
|
}
|
|
@@ -913,7 +933,7 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
913
933
|
const thinkingCeiling = externalRunner ? undefined : intersectThinkingCeilings(
|
|
914
934
|
params.thinkingCeiling,
|
|
915
935
|
a.maxThinking,
|
|
916
|
-
|
|
936
|
+
ctx.childRuntime?.thinkingCeiling,
|
|
917
937
|
);
|
|
918
938
|
if (!externalRunner) {
|
|
919
939
|
try {
|
|
@@ -957,7 +977,7 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
957
977
|
model,
|
|
958
978
|
modelCandidates,
|
|
959
979
|
capabilityCeiling: params.capabilityCeiling,
|
|
960
|
-
inheritedCapabilityCeiling:
|
|
980
|
+
inheritedCapabilityCeiling: ctx.childRuntime?.capabilityCeiling,
|
|
961
981
|
agentName: a.name,
|
|
962
982
|
permissionRules,
|
|
963
983
|
runtimeSnapshotHost: ctx.pi,
|
|
@@ -1002,11 +1022,10 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
|
|
|
1002
1022
|
thinking: resolveEffectiveThinking(model, effectiveThinking),
|
|
1003
1023
|
...(thinkingCeiling ? { thinkingCeiling } : {}),
|
|
1004
1024
|
launchResolvedExtensions,
|
|
1005
|
-
...(toolPlan.mcpConfig ? { mcpConfig: toolPlan.mcpConfig } : {}),
|
|
1006
|
-
...(toolPlan.runtimeServerNames ? { runtimeServerNames: toolPlan.runtimeServerNames } : {}),
|
|
1007
1025
|
modelCandidates: externalRunner ? undefined : modelCandidates,
|
|
1008
1026
|
...(primaryModelFromParent ? { skipPrimaryModelVerification: true } : {}),
|
|
1009
1027
|
...(availableModels && availableModels.length > 0 ? { modelVerificationRegistry: availableModels } : {}),
|
|
1028
|
+
...(ctx.modelResponseAliases ? { modelResponseAliases: ctx.modelResponseAliases } : {}),
|
|
1010
1029
|
tools: a.tools,
|
|
1011
1030
|
excludeTools: a.excludeTools,
|
|
1012
1031
|
allowNestedSubagents: a.allowNestedSubagents,
|
|
@@ -1207,6 +1226,7 @@ export function executeAsyncChain(
|
|
|
1207
1226
|
worktreeSetupHook,
|
|
1208
1227
|
worktreeSetupHookTimeoutMs,
|
|
1209
1228
|
worktreeBaseDir,
|
|
1229
|
+
baseRef,
|
|
1210
1230
|
worktreeProvider,
|
|
1211
1231
|
worktreeBranchPrefix,
|
|
1212
1232
|
controlConfig,
|
|
@@ -1224,8 +1244,8 @@ export function executeAsyncChain(
|
|
|
1224
1244
|
});
|
|
1225
1245
|
if (acceptanceErrors.length > 0) return formatAsyncStartError(resultMode, acceptanceErrors.join(" "));
|
|
1226
1246
|
const capabilityCeiling = params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId);
|
|
1227
|
-
const inheritedNestedRoute =
|
|
1228
|
-
const nestedAddress = inheritedNestedRoute ?
|
|
1247
|
+
const inheritedNestedRoute = inheritedNestedRouteOf(ctx.childRuntime);
|
|
1248
|
+
const nestedAddress = inheritedNestedRoute ? inheritedNestedParentAddressOf(ctx.childRuntime) : undefined;
|
|
1229
1249
|
const asyncDir = inheritedNestedRoute
|
|
1230
1250
|
? path.join(TEMP_ROOT_DIR, "nested-subagent-runs", inheritedNestedRoute.rootRunId, id)
|
|
1231
1251
|
: path.join(DIRS.async, id);
|
|
@@ -1338,10 +1358,12 @@ export function executeAsyncChain(
|
|
|
1338
1358
|
completionOwnerId: ctx.completionOwnerId ?? currentCompletionOwnerId(),
|
|
1339
1359
|
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
1340
1360
|
piPackageRoot,
|
|
1341
|
-
|
|
1361
|
+
childSessionFactoryModule: childSessionFactoryModule(),
|
|
1362
|
+
inheritedChildRuntime: inheritedChildRuntime(ctx.childRuntime),
|
|
1342
1363
|
worktreeSetupHook,
|
|
1343
1364
|
worktreeSetupHookTimeoutMs,
|
|
1344
1365
|
worktreeBaseDir,
|
|
1366
|
+
baseRef,
|
|
1345
1367
|
worktreeProvider,
|
|
1346
1368
|
worktreeBranchPrefix,
|
|
1347
1369
|
controlConfig,
|
|
@@ -1454,7 +1476,7 @@ export function executeAsyncChain(
|
|
|
1454
1476
|
path: nestedAddress.path,
|
|
1455
1477
|
asyncDir,
|
|
1456
1478
|
pid: spawnResult.pid,
|
|
1457
|
-
ownerIntercomTarget:
|
|
1479
|
+
ownerIntercomTarget: ctx.childRuntime?.intercomSessionName,
|
|
1458
1480
|
leafIntercomTarget: childIntercomTargets?.[0],
|
|
1459
1481
|
intercomTarget: childIntercomTargets?.[0],
|
|
1460
1482
|
ownerState: "live",
|
|
@@ -1541,6 +1563,7 @@ export function executeAsyncSingle(
|
|
|
1541
1563
|
worktreeSetupHook,
|
|
1542
1564
|
worktreeSetupHookTimeoutMs,
|
|
1543
1565
|
worktreeBaseDir,
|
|
1566
|
+
baseRef,
|
|
1544
1567
|
worktreeProvider,
|
|
1545
1568
|
worktreeBranchPrefix,
|
|
1546
1569
|
controlConfig,
|
|
@@ -1581,7 +1604,7 @@ export function executeAsyncSingle(
|
|
|
1581
1604
|
if (extensionBindings !== undefined) unsupported.push("extension bindings");
|
|
1582
1605
|
if (unsupported.length > 0) return formatAsyncStartError("single", `Agent '${agentConfig.name}' uses runner.type='${externalRunnerType}' and does not support: ${unsupported.join(", ")}.`);
|
|
1583
1606
|
}
|
|
1584
|
-
const capabilityCeiling = intersectSubagentCapabilityCeilings(params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId),
|
|
1607
|
+
const capabilityCeiling = intersectSubagentCapabilityCeilings(params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId), ctx.childRuntime?.capabilityCeiling);
|
|
1585
1608
|
try {
|
|
1586
1609
|
assertAgentAllowedByCapabilityCeiling(agentConfig.name, capabilityCeiling);
|
|
1587
1610
|
} catch (error) {
|
|
@@ -1624,8 +1647,8 @@ export function executeAsyncSingle(
|
|
|
1624
1647
|
}
|
|
1625
1648
|
systemPrompt = appendAgentRefinementOverlay(systemPrompt, { cwd: runnerCwd, agentName: agentConfig.name });
|
|
1626
1649
|
|
|
1627
|
-
const inheritedNestedRoute =
|
|
1628
|
-
const nestedAddress = inheritedNestedRoute ?
|
|
1650
|
+
const inheritedNestedRoute = inheritedNestedRouteOf(ctx.childRuntime);
|
|
1651
|
+
const nestedAddress = inheritedNestedRoute ? inheritedNestedParentAddressOf(ctx.childRuntime) : undefined;
|
|
1629
1652
|
const asyncDir = inheritedNestedRoute
|
|
1630
1653
|
? path.join(TEMP_ROOT_DIR, "nested-subagent-runs", inheritedNestedRoute.rootRunId, id)
|
|
1631
1654
|
: path.join(DIRS.async, id);
|
|
@@ -1690,7 +1713,7 @@ export function executeAsyncSingle(
|
|
|
1690
1713
|
const thinkingCeiling = externalRunner ? undefined : intersectThinkingCeilings(
|
|
1691
1714
|
params.thinkingCeiling,
|
|
1692
1715
|
agentConfig.maxThinking,
|
|
1693
|
-
|
|
1716
|
+
ctx.childRuntime?.thinkingCeiling,
|
|
1694
1717
|
);
|
|
1695
1718
|
if (!externalRunner) {
|
|
1696
1719
|
try {
|
|
@@ -1750,7 +1773,7 @@ export function executeAsyncSingle(
|
|
|
1750
1773
|
model,
|
|
1751
1774
|
modelCandidates,
|
|
1752
1775
|
capabilityCeiling,
|
|
1753
|
-
inheritedCapabilityCeiling:
|
|
1776
|
+
inheritedCapabilityCeiling: ctx.childRuntime?.capabilityCeiling,
|
|
1754
1777
|
agentName: agentConfig.name,
|
|
1755
1778
|
permissionRules: resolvePermissionRules(ctx.permissions, agentConfig.permissions),
|
|
1756
1779
|
runtimeSnapshotHost: ctx.pi,
|
|
@@ -1804,6 +1827,7 @@ export function executeAsyncSingle(
|
|
|
1804
1827
|
});
|
|
1805
1828
|
const recoveryAgentConfig = params.recoveryAgentConfig ?? agentConfig;
|
|
1806
1829
|
const recoveryDescriptor: SteeringRecoveryDescriptor = {
|
|
1830
|
+
...(ctx.modelResponseAliases ? { modelResponseAliases: ctx.modelResponseAliases } : {}),
|
|
1807
1831
|
version: 1,
|
|
1808
1832
|
...(lane ? { lane } : {}),
|
|
1809
1833
|
launchContractDigest,
|
|
@@ -1847,6 +1871,7 @@ export function executeAsyncSingle(
|
|
|
1847
1871
|
...(controlConfig ? { controlConfig } : {}),
|
|
1848
1872
|
...(params.context ? { context: params.context } : {}),
|
|
1849
1873
|
...(params.intercomBridge !== undefined ? { intercomBridge: params.intercomBridge } : {}),
|
|
1874
|
+
...(params.baseRef !== undefined ? { baseRef: params.baseRef } : {}),
|
|
1850
1875
|
...(deadlineAt !== undefined ? { absoluteDeadlineAt: deadlineAt } : {}),
|
|
1851
1876
|
...(resolvedToolBudget.budget ? { initialToolBudget: resolvedToolBudget.budget } : {}),
|
|
1852
1877
|
maxSubagentDepth: resolveChildMaxSubagentDepth(maxSubagentDepth, recoveryAgentConfig.maxSubagentDepth),
|
|
@@ -1891,14 +1916,13 @@ export function executeAsyncSingle(
|
|
|
1891
1916
|
modelCandidates,
|
|
1892
1917
|
...(modelOrigin === "inherited" ? { skipPrimaryModelVerification: true } : {}),
|
|
1893
1918
|
...(availableModels && availableModels.length > 0 ? { modelVerificationRegistry: availableModels } : {}),
|
|
1919
|
+
...(ctx.modelResponseAliases ? { modelResponseAliases: ctx.modelResponseAliases } : {}),
|
|
1894
1920
|
tools: agentConfig.tools,
|
|
1895
1921
|
excludeTools: agentConfig.excludeTools,
|
|
1896
1922
|
allowNestedSubagents: agentConfig.allowNestedSubagents,
|
|
1897
1923
|
extensions: agentConfig.extensions,
|
|
1898
1924
|
subagentOnlyExtensions: agentConfig.subagentOnlyExtensions,
|
|
1899
1925
|
mcpDirectTools: agentConfig.mcpDirectTools,
|
|
1900
|
-
...(toolPlan.mcpConfig ? { mcpConfig: toolPlan.mcpConfig } : {}),
|
|
1901
|
-
...(toolPlan.runtimeServerNames ? { runtimeServerNames: toolPlan.runtimeServerNames } : {}),
|
|
1902
1926
|
mutationTools: agentConfig.mutationTools,
|
|
1903
1927
|
completionGuard: agentConfig.completionGuard,
|
|
1904
1928
|
systemPrompt,
|
|
@@ -1944,10 +1968,12 @@ export function executeAsyncSingle(
|
|
|
1944
1968
|
completionOwnerId: ctx.completionOwnerId ?? currentCompletionOwnerId(),
|
|
1945
1969
|
...(capabilityCeiling ? { capabilityCeiling } : {}),
|
|
1946
1970
|
piPackageRoot,
|
|
1947
|
-
|
|
1971
|
+
childSessionFactoryModule: childSessionFactoryModule(),
|
|
1972
|
+
inheritedChildRuntime: inheritedChildRuntime(ctx.childRuntime),
|
|
1948
1973
|
worktreeSetupHook,
|
|
1949
1974
|
worktreeSetupHookTimeoutMs,
|
|
1950
1975
|
worktreeBaseDir,
|
|
1976
|
+
baseRef,
|
|
1951
1977
|
worktreeProvider,
|
|
1952
1978
|
worktreeBranchPrefix,
|
|
1953
1979
|
controlConfig,
|
|
@@ -2030,7 +2056,7 @@ export function executeAsyncSingle(
|
|
|
2030
2056
|
path: nestedAddress.path,
|
|
2031
2057
|
asyncDir,
|
|
2032
2058
|
pid: spawnResult.pid,
|
|
2033
|
-
ownerIntercomTarget:
|
|
2059
|
+
ownerIntercomTarget: ctx.childRuntime?.intercomSessionName,
|
|
2034
2060
|
leafIntercomTarget: childIntercomTarget?.(agent, 0),
|
|
2035
2061
|
intercomTarget: childIntercomTarget?.(agent, 0),
|
|
2036
2062
|
ownerState: "live",
|