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.
Files changed (106) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +2 -2
  3. package/agents/reviewer.md +1 -1
  4. package/agents/scout.md +1 -1
  5. package/docs/agents.md +19 -17
  6. package/docs/configuration.md +23 -15
  7. package/docs/extension-api.md +18 -6
  8. package/docs/missions.md +2 -0
  9. package/docs/observability.md +10 -12
  10. package/docs/tool-reference.md +15 -8
  11. package/docs/watchdog.md +1 -1
  12. package/docs/workflows.md +13 -3
  13. package/package.json +5 -4
  14. package/runner-server-preload.mjs +13 -0
  15. package/skills/pi-subagents/SKILL.md +2 -1
  16. package/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
  17. package/skills/pi-subagents/references/execution-controls.md +7 -3
  18. package/skills/pi-subagents/references/multi-lane-orchestration.md +2 -0
  19. package/src/agents/agent-management.ts +47 -15
  20. package/src/api/capability-ceiling.ts +0 -1
  21. package/src/api/{pi-args.ts → child-tool-plan.ts} +1 -1
  22. package/src/api/preflight.ts +7 -4
  23. package/src/extension/config.ts +4 -2
  24. package/src/extension/doctor.ts +2 -10
  25. package/src/extension/fanout-child.ts +9 -11
  26. package/src/extension/index.ts +58 -7
  27. package/src/extension/public-execution.ts +14 -0
  28. package/src/extension/rpc.ts +3 -2
  29. package/src/extension/schemas.ts +3 -2
  30. package/src/extension/tool-description.ts +13 -8
  31. package/src/integrations/pi-web-session-liveness.ts +73 -0
  32. package/src/intercom/native-supervisor-channel.ts +159 -95
  33. package/src/intercom/supervisor-ui.ts +244 -0
  34. package/src/missions/workflow-state.ts +37 -16
  35. package/src/runs/background/async-execution.ts +51 -25
  36. package/src/runs/background/async-job-tracker.ts +11 -0
  37. package/src/runs/background/async-resume.ts +14 -3
  38. package/src/runs/background/async-retention.ts +9 -0
  39. package/src/runs/background/control-channel.ts +2 -204
  40. package/src/runs/background/notify.ts +2 -0
  41. package/src/runs/background/process-terminal.ts +1 -1
  42. package/src/runs/background/retained-nested-route-tracker.ts +96 -0
  43. package/src/runs/background/run-child-session.ts +614 -0
  44. package/src/runs/background/run-status.ts +0 -1
  45. package/src/runs/background/runner-aliases.ts +152 -0
  46. package/src/runs/background/runner-child-sessions.ts +31 -0
  47. package/src/runs/background/scheduled-runs.ts +18 -4
  48. package/src/runs/background/subagent-runner.ts +203 -901
  49. package/src/runs/foreground/async-steering-action.ts +1 -17
  50. package/src/runs/foreground/execution.ts +204 -378
  51. package/src/runs/foreground/foreground-control.ts +4 -0
  52. package/src/runs/foreground/foreground-history.ts +3 -1
  53. package/src/runs/foreground/prompt-audit.ts +9 -5
  54. package/src/runs/foreground/subagent-executor.ts +161 -91
  55. package/src/runs/foreground/workflow-foreground-steering.ts +24 -98
  56. package/src/runs/shared/abort-recovery.ts +3 -3
  57. package/src/runs/shared/acceptance.ts +14 -1
  58. package/src/runs/shared/capability-ceiling.ts +1 -2
  59. package/src/runs/shared/child-hooks.ts +25 -0
  60. package/src/runs/shared/child-identity.ts +13 -2
  61. package/src/runs/shared/child-launch.ts +314 -0
  62. package/src/runs/shared/child-lifecycle.ts +25 -0
  63. package/src/runs/shared/child-runtime-config.ts +126 -0
  64. package/src/runs/shared/child-session.ts +337 -0
  65. package/src/runs/shared/child-tool-plan.ts +530 -0
  66. package/src/runs/shared/claude-code-adapter.ts +5 -1
  67. package/src/runs/shared/completion-guard.ts +1 -1
  68. package/src/runs/shared/external-cli-preflight.ts +16 -0
  69. package/src/runs/shared/llm-intent-arbiter.ts +20 -11
  70. package/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
  71. package/src/runs/shared/model-exclusions.ts +84 -15
  72. package/src/runs/shared/model-fallback.ts +78 -6
  73. package/src/runs/shared/nested-events.ts +32 -48
  74. package/src/runs/shared/nested-path.ts +0 -14
  75. package/src/runs/shared/orca-progress-tabs.ts +12 -7
  76. package/src/runs/shared/parallel-utils.ts +1 -2
  77. package/src/runs/shared/permissions.ts +0 -13
  78. package/src/runs/shared/process-signal.ts +4 -1
  79. package/src/runs/shared/run-fanout-budget.ts +0 -13
  80. package/src/runs/shared/runtime-acknowledged-extensions.ts +0 -27
  81. package/src/runs/shared/structured-output.ts +17 -4
  82. package/src/runs/shared/subagent-control.ts +2 -0
  83. package/src/runs/shared/subagent-prompt-runtime.ts +87 -384
  84. package/src/runs/shared/tool-availability.ts +18 -62
  85. package/src/runs/shared/tool-budget.ts +0 -14
  86. package/src/runs/shared/worktree-cleanup-plan.ts +31 -9
  87. package/src/runs/shared/worktree.ts +192 -40
  88. package/src/shared/child-session-name.ts +1 -1
  89. package/src/shared/jsonl-writer.ts +11 -0
  90. package/src/shared/model-response-aliases.ts +13 -0
  91. package/src/shared/thinking-ceiling.ts +0 -6
  92. package/src/shared/types.ts +63 -28
  93. package/src/shared/utils.ts +6 -4
  94. package/src/shared/watch-strategy.ts +2 -0
  95. package/src/slash/slash-commands.ts +0 -6
  96. package/src/tui/fleet-status.ts +1 -1
  97. package/src/tui/fleet.ts +0 -1
  98. package/src/tui/render.ts +254 -41
  99. package/src/watchdog/child-status.ts +0 -1
  100. package/src/watchdog/register-child.ts +12 -12
  101. package/src/workflows/scripted-workflow.ts +80 -7
  102. package/src/workflows/workflow-checklist.ts +4 -3
  103. package/src/runs/shared/child-protocol.ts +0 -415
  104. package/src/runs/shared/pi-args.ts +0 -1059
  105. package/src/runs/shared/subagent-startup-retry.ts +0 -116
  106. 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
- const CURRENT_PROCESS_KEY = processStartKey(process.pid);
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 currentProcessKey = owner.pid === process.pid ? CURRENT_PROCESS_KEY : processStartKey(owner.pid);
100
- if (currentProcessKey) return owner.processKey !== currentProcessKey;
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(DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS[attempt], lockPath);
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(DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS[attempt], lockPath);
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(DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS[attempt], lockPath);
195
+ if (reclaimStaleStateLock(lockPath, reclaimPath, options)) continue;
196
+ waitForStateLock(options.retryDelaysMs[attempt], lockPath);
182
197
  continue;
183
198
  }
184
- owner = { pid: process.pid, token: randomUUID(), createdAt: Date.now(), ...(CURRENT_PROCESS_KEY ? { processKey: CURRENT_PROCESS_KEY } : {}) };
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/pi-args.ts";
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, decodeThinkingCeiling, intersectThinkingCeilings, SUBAGENT_THINKING_CEILING_ENV, type ThinkingLevel } from "../../shared/thinking-ceiling.ts";
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 { nestedResultsPath, nestedSummaryFromAsyncStatus, resolveInheritedNestedRouteFromEnv, resolveNestedParentAddressFromEnv, writeNestedEvent } from "../shared/nested-events.ts";
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, decodeSubagentCapabilityCeiling, intersectSubagentCapabilityCeilings, resolveCurrentSubagentCapabilityCeiling, SUBAGENT_CAPABILITY_CEILING_ENV, type ResolvedSubagentCapabilityCeiling } from "../shared/capability-ceiling.ts";
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 proc = spawn(nodeCommand, [jitiCliPath, runner, cfgPath], {
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
- ...(piPackageRoot ? { [PI_CODING_AGENT_PACKAGE_ROOT_ENV]: piPackageRoot } : {}),
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, decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV])));
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
- decodeThinkingCeiling(process.env[SUBAGENT_THINKING_CEILING_ENV]),
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: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
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 = resolveInheritedNestedRouteFromEnv();
1228
- const nestedAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
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
- piArgv1: process.argv[1],
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: process.env.PI_SUBAGENT_INTERCOM_SESSION_NAME,
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), decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]));
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 = resolveInheritedNestedRouteFromEnv();
1628
- const nestedAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
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
- decodeThinkingCeiling(process.env[SUBAGENT_THINKING_CEILING_ENV]),
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: decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]),
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
- piArgv1: process.argv[1],
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: process.env.PI_SUBAGENT_INTERCOM_SESSION_NAME,
2059
+ ownerIntercomTarget: ctx.childRuntime?.intercomSessionName,
2034
2060
  leafIntercomTarget: childIntercomTarget?.(agent, 0),
2035
2061
  intercomTarget: childIntercomTarget?.(agent, 0),
2036
2062
  ownerState: "live",