pi-cohort 7.1.2 → 7.2.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 +6 -0
- package/README.md +6 -0
- package/package.json +3 -3
- package/src/execution-backend/child-host-controller.ts +10 -8
- package/src/extension/doctor.ts +6 -2
- package/src/extension/fanout-child.ts +2 -2
- package/src/extension/index.ts +8 -6
- package/src/runs/background/async-execution.ts +154 -87
- package/src/runs/background/async-job-tracker.ts +4 -1
- package/src/runs/background/async-resume.ts +93 -17
- package/src/runs/background/async-status.ts +12 -9
- package/src/runs/background/external-attempt.ts +6 -2
- package/src/runs/background/run-id-resolver.ts +15 -18
- package/src/runs/background/run-status.ts +22 -11
- package/src/runs/background/stale-run-reconciler.ts +9 -1
- package/src/runs/background/subagent-runner.ts +54 -79
- package/src/runs/foreground/chain-execution.ts +7 -0
- package/src/runs/foreground/execution.ts +30 -13
- package/src/runs/foreground/external-single-attempt.ts +3 -2
- package/src/runs/foreground/subagent-executor.ts +142 -63
- package/src/runs/shared/external-execution.ts +2 -0
- package/src/runs/shared/jiti-cli.ts +17 -48
- package/src/runs/shared/nested-events.ts +103 -30
- package/src/runs/shared/pi-spawn.ts +75 -67
- package/src/shared/agent-dir.ts +12 -0
- package/src/shared/atomic-json.ts +12 -1
- package/src/shared/fork-context.ts +15 -3
- package/src/shared/types.ts +11 -1
- package/src/shared/utils.ts +1 -6
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
1
|
+
import { randomUUID, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { capturePiRuntime, parsePiRuntime, type PiRuntimeIdentity } from "../shared/pi-spawn.ts";
|
|
2
3
|
import * as fs from "node:fs";
|
|
3
4
|
import * as path from "node:path";
|
|
4
5
|
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
@@ -41,7 +42,7 @@ import { finalizeSingleOutput, injectSingleOutputInstruction, normalizeTopLevelO
|
|
|
41
42
|
import { compactForegroundDetails, getSingleResultOutput, mapConcurrent, readStatus, resolveChildCwd } from "../../shared/utils.ts";
|
|
42
43
|
import { resolveSubagentResultStatus } from "../shared/result-children.ts";
|
|
43
44
|
import { buildRevivedAsyncTask, resolveAsyncResumeTarget } from "../background/async-resume.ts";
|
|
44
|
-
import { createNestedRoute, readNestedControlResults, resolveInheritedNestedRouteFromEnv, resolveNestedAsyncDir, resolveNestedParentAddressFromEnv, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedRunResolutionScope } from "../shared/nested-events.ts";
|
|
45
|
+
import { projectNestedEvents, nestedResultsPath, createNestedRoute, readNestedControlResults, resolveInheritedNestedRouteFromEnv, resolveNestedAsyncDir, resolveNestedParentAddressFromEnv, updateForegroundNestedProjection, writeNestedControlRequest, writeNestedEvent, type NestedRunResolutionScope } from "../shared/nested-events.ts";
|
|
45
46
|
import { resolveSubagentRunId, type ResolvedSubagentRunId } from "../background/run-id-resolver.ts";
|
|
46
47
|
import { formatNestedRunStatusLines } from "../shared/nested-render.ts";
|
|
47
48
|
import { inspectSubagentStatus } from "../background/run-status.ts";
|
|
@@ -132,6 +133,7 @@ export interface SubagentParamsLike {
|
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
interface ExecutorDeps {
|
|
136
|
+
captureRuntime?: () => PiRuntimeIdentity;
|
|
135
137
|
pi: ExtensionAPI;
|
|
136
138
|
state: SubagentState;
|
|
137
139
|
config: ExtensionConfig;
|
|
@@ -145,6 +147,7 @@ interface ExecutorDeps {
|
|
|
145
147
|
}
|
|
146
148
|
|
|
147
149
|
interface ExecutionContextData {
|
|
150
|
+
runtime: PiRuntimeIdentity;
|
|
148
151
|
params: SubagentParamsLike;
|
|
149
152
|
effectiveCwd: string;
|
|
150
153
|
ctx: ExtensionContext;
|
|
@@ -156,6 +159,7 @@ interface ExecutionContextData {
|
|
|
156
159
|
sessionRoot: string;
|
|
157
160
|
sessionDirForIndex: (idx?: number) => string;
|
|
158
161
|
sessionFileForIndex: (idx?: number) => string | undefined;
|
|
162
|
+
sessionFileHintForIndex: (idx?: number) => string;
|
|
159
163
|
artifactConfig: ArtifactConfig;
|
|
160
164
|
artifactsDir: string;
|
|
161
165
|
backgroundRequestedWhileClarifying: boolean;
|
|
@@ -210,8 +214,9 @@ function nestedResolutionScopeForExecutor(deps: ExecutorDeps): NestedRunResoluti
|
|
|
210
214
|
};
|
|
211
215
|
}
|
|
212
216
|
|
|
213
|
-
function buildAsyncContext(ctx: ExtensionContext, deps: ExecutorDeps, cwd: string) {
|
|
217
|
+
function buildAsyncContext(ctx: ExtensionContext, deps: ExecutorDeps, cwd: string, runtime: PiRuntimeIdentity) {
|
|
214
218
|
return {
|
|
219
|
+
runtime,
|
|
215
220
|
pi: deps.pi,
|
|
216
221
|
cwd,
|
|
217
222
|
currentSessionId: deps.state.currentSessionId!,
|
|
@@ -225,7 +230,7 @@ function buildAsyncContext(ctx: ExtensionContext, deps: ExecutorDeps, cwd: strin
|
|
|
225
230
|
function foregroundStatusResult(control: SubagentState["foregroundControls"] extends Map<string, infer T> ? T : never): AgentToolResult<Details> {
|
|
226
231
|
let nestedWarning: string | undefined;
|
|
227
232
|
try {
|
|
228
|
-
|
|
233
|
+
if (control.nestedRoute) control.nestedChildren = projectNestedEvents(control.nestedRoute, { readOnlyLegacy: true }).children;
|
|
229
234
|
} catch (error) {
|
|
230
235
|
nestedWarning = `Nested status unavailable: ${error instanceof Error ? error.message : String(error)}`;
|
|
231
236
|
}
|
|
@@ -242,9 +247,11 @@ function foregroundStatusResult(control: SubagentState["foregroundControls"] ext
|
|
|
242
247
|
return { content: [{ type: "text", text: lines.join("\n") }], details: { mode: "management", results: [] } };
|
|
243
248
|
}
|
|
244
249
|
|
|
245
|
-
function rememberForegroundRun(state: SubagentState, input: { runId: string; mode: "single" | "parallel" | "chain"; cwd: string; results: SingleResult[] }): void {
|
|
250
|
+
function rememberForegroundRun(state: SubagentState, input: { runtime: PiRuntimeIdentity; sessionRoot: string; runId: string; mode: "single" | "parallel" | "chain"; cwd: string; results: SingleResult[] }): void {
|
|
246
251
|
state.foregroundRuns ??= new Map();
|
|
247
|
-
|
|
252
|
+
const rememberedRun = {
|
|
253
|
+
sessionRoot: input.sessionRoot,
|
|
254
|
+
runtime: input.runtime,
|
|
248
255
|
runId: input.runId,
|
|
249
256
|
mode: input.mode,
|
|
250
257
|
cwd: input.cwd,
|
|
@@ -255,7 +262,8 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
|
|
|
255
262
|
status: resolveSubagentResultStatus({ exitCode: result.exitCode, interrupted: result.interrupted }),
|
|
256
263
|
...(result.sessionFile ? { sessionFile: result.sessionFile } : {}),
|
|
257
264
|
})),
|
|
258
|
-
}
|
|
265
|
+
};
|
|
266
|
+
state.foregroundRuns.set(input.runId, rememberedRun);
|
|
259
267
|
while (state.foregroundRuns.size > 50) {
|
|
260
268
|
const oldest = [...state.foregroundRuns.values()].sort((left, right) => left.updatedAt - right.updatedAt)[0];
|
|
261
269
|
if (!oldest) break;
|
|
@@ -263,7 +271,7 @@ function rememberForegroundRun(state: SubagentState, input: { runId: string; mod
|
|
|
263
271
|
}
|
|
264
272
|
}
|
|
265
273
|
|
|
266
|
-
function resolveForegroundResumeTarget(params: SubagentParamsLike, state: SubagentState): { runId: string; mode: "single" | "parallel" | "chain"; state: "complete"; agent: string; index: number; cwd: string; sessionFile: string } | undefined {
|
|
274
|
+
function resolveForegroundResumeTarget(params: SubagentParamsLike, state: SubagentState): { runId: string; mode: "single" | "parallel" | "chain"; state: "complete"; agent: string; index: number; cwd: string; sessionFile: string; runtime: PiRuntimeIdentity; sessionRoot: string } | undefined {
|
|
267
275
|
const requested = (params.id ?? params.runId)?.trim();
|
|
268
276
|
if (!requested || !state.foregroundRuns?.size) return undefined;
|
|
269
277
|
const direct = state.foregroundRuns.get(requested);
|
|
@@ -280,7 +288,10 @@ function resolveForegroundResumeTarget(params: SubagentParamsLike, state: Subage
|
|
|
280
288
|
if (path.extname(child.sessionFile) !== ".jsonl") throw new Error(`Foreground run '${run.runId}' child ${index} session file must be a .jsonl file: ${child.sessionFile}`);
|
|
281
289
|
const sessionFile = path.resolve(child.sessionFile);
|
|
282
290
|
if (!fs.existsSync(sessionFile)) throw new Error(`Foreground run '${run.runId}' child ${index} session file does not exist: ${child.sessionFile}`);
|
|
283
|
-
|
|
291
|
+
const runtime = parsePiRuntime(run.runtime, `Foreground run '${run.runId}'`);
|
|
292
|
+
const stat = fs.lstatSync(sessionFile);
|
|
293
|
+
if (!stat.isFile() || stat.isSymbolicLink()) throw new Error(`Foreground run '${run.runId}' session file is not a regular file: ${sessionFile}`);
|
|
294
|
+
return { runId: run.runId, mode: run.mode, state: "complete", agent: child.agent, index, cwd: run.cwd, sessionFile: fs.realpathSync.native(sessionFile), runtime, sessionRoot: validateResumeSessionRoot((run as typeof run & { sessionRoot?: string }).sessionRoot ?? path.dirname(fs.realpathSync.native(sessionFile)), sessionFile, `Foreground run '${run.runId}'`) };
|
|
284
295
|
}
|
|
285
296
|
|
|
286
297
|
type AsyncResumeSourceTarget = ReturnType<typeof resolveAsyncResumeTarget> & { source: "async" };
|
|
@@ -288,6 +299,8 @@ type ForegroundResumeSourceTarget = NonNullable<ReturnType<typeof resolveForegro
|
|
|
288
299
|
type NestedResumeSourceTarget = {
|
|
289
300
|
kind: "revive";
|
|
290
301
|
source: "nested";
|
|
302
|
+
runtime: PiRuntimeIdentity;
|
|
303
|
+
sessionRoot: string;
|
|
291
304
|
runId: string;
|
|
292
305
|
state: "complete" | "failed" | "paused";
|
|
293
306
|
agent: string;
|
|
@@ -437,7 +450,7 @@ function pathWithin(base: string, candidate: string): boolean {
|
|
|
437
450
|
return resolvedCandidate === resolvedBase || resolvedCandidate.startsWith(`${resolvedBase}${path.sep}`);
|
|
438
451
|
}
|
|
439
452
|
|
|
440
|
-
function validateNestedSessionFile(run: NestedRunSummary, trustedSessionRoots: string[]): string {
|
|
453
|
+
function validateNestedSessionFile(run: NestedRunSummary, trustedSessionRoots: string[], route: NestedRouteInfo): string {
|
|
441
454
|
const sessionFile = nestedRunSessionFile(run);
|
|
442
455
|
if (!sessionFile) throw new Error(`Nested run '${run.id}' does not have a persisted session file to resume from.`);
|
|
443
456
|
if (path.extname(sessionFile) !== ".jsonl") throw new Error(`Nested run '${run.id}' session file must be a .jsonl file: ${sessionFile}`);
|
|
@@ -447,6 +460,15 @@ function validateNestedSessionFile(run: NestedRunSummary, trustedSessionRoots: s
|
|
|
447
460
|
const stat = fs.lstatSync(resolved);
|
|
448
461
|
if (!stat.isFile() || stat.isSymbolicLink()) throw new Error(`Nested run '${run.id}' session file is not a regular file: ${sessionFile}`);
|
|
449
462
|
const realSessionFile = fs.realpathSync(resolved);
|
|
463
|
+
if (run.runtime !== undefined || run.sessionRoot !== undefined) {
|
|
464
|
+
if (!run.sessionRoot || !path.isAbsolute(run.sessionRoot)) throw new Error(`Nested run '${run.id}' has no absolute original session root.`);
|
|
465
|
+
const root = fs.realpathSync.native(run.sessionRoot);
|
|
466
|
+
if (root !== run.sessionRoot || !fs.statSync(root).isDirectory() || !pathWithin(root, realSessionFile)) throw new Error(`Nested run '${run.id}' session file is outside its stamped session root.`);
|
|
467
|
+
const recorded = Buffer.from(run.capabilityToken ?? "");
|
|
468
|
+
const expected = Buffer.from(route.capabilityToken);
|
|
469
|
+
if (!recorded.length || recorded.length !== expected.length || !timingSafeEqual(recorded, expected)) throw new Error(`Nested run '${run.id}' capability does not match its route.`);
|
|
470
|
+
return realSessionFile;
|
|
471
|
+
}
|
|
450
472
|
const trustedRoots = trustedSessionRoots
|
|
451
473
|
.filter((root) => fs.existsSync(root))
|
|
452
474
|
.map((root) => fs.realpathSync(root));
|
|
@@ -459,22 +481,33 @@ function validateNestedSessionFile(run: NestedRunSummary, trustedSessionRoots: s
|
|
|
459
481
|
return realSessionFile;
|
|
460
482
|
}
|
|
461
483
|
|
|
462
|
-
function resolveNestedResumeTarget(match: ResolvedSubagentRunId & { kind: "nested" }, trustedSessionRoots: string[]): NestedResumeSourceTarget {
|
|
484
|
+
function resolveNestedResumeTarget(match: ResolvedSubagentRunId & { kind: "nested" }, trustedSessionRoots: string[], index: SubagentParamsLike["index"]): NestedResumeSourceTarget {
|
|
463
485
|
const run = match.match.run;
|
|
464
|
-
if (run.state === "running" || run.state === "queued") throw new Error(`Nested run '${run.id}' is live; route the follow-up to the owner process instead.`);
|
|
465
486
|
const agent = nestedRunAgent(run);
|
|
466
487
|
if (!agent) throw new Error(`Could not determine child agent for nested run '${run.id}'.`);
|
|
467
488
|
const state = run.state === "complete" || run.state === "failed" || run.state === "paused" ? run.state : "failed";
|
|
468
489
|
const asyncDir = resolveNestedAsyncDir(match.match.rootRunId, run);
|
|
490
|
+
const authoritative = asyncDir ? resolveAsyncResumeTarget({ dir: asyncDir, id: run.id, index }, {
|
|
491
|
+
asyncDirRoot: path.dirname(asyncDir),
|
|
492
|
+
resultsDir: path.dirname(nestedResultsPath(match.match.rootRunId, run.id)),
|
|
493
|
+
}) : undefined;
|
|
494
|
+
if (!authoritative && (run.state === "running" || run.state === "queued")) throw new Error(`Nested run '${run.id}' is live; route the follow-up to the owner process instead.`);
|
|
495
|
+
const sessionFile = validateNestedSessionFile(
|
|
496
|
+
authoritative ? { ...run, sessionFile: authoritative.sessionFile } : run,
|
|
497
|
+
trustedSessionRoots,
|
|
498
|
+
match.match.route,
|
|
499
|
+
);
|
|
469
500
|
return {
|
|
470
501
|
kind: "revive",
|
|
471
502
|
source: "nested",
|
|
472
503
|
runId: run.id,
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
504
|
+
runtime: authoritative?.runtime ?? parsePiRuntime(run.runtime, `Nested run '${run.id}'`),
|
|
505
|
+
sessionRoot: run.sessionRoot!,
|
|
506
|
+
state: authoritative?.state ?? state,
|
|
507
|
+
agent: authoritative?.agent ?? agent,
|
|
508
|
+
index: authoritative?.index ?? 0,
|
|
509
|
+
cwd: authoritative?.cwd ?? (asyncDir ? path.dirname(asyncDir) : undefined),
|
|
510
|
+
sessionFile,
|
|
478
511
|
};
|
|
479
512
|
}
|
|
480
513
|
|
|
@@ -535,6 +568,14 @@ async function resumeLiveNestedRun(input: { target: ResolvedSubagentRunId & { ki
|
|
|
535
568
|
return { content: [{ type: "text", text: `Nested run ${run.id} appears live but its owner route is not reachable. Wait for completion, then retry action='resume'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
536
569
|
}
|
|
537
570
|
|
|
571
|
+
function validateResumeSessionRoot(value: unknown, sessionFile: string | undefined, source: string): string {
|
|
572
|
+
if (typeof value !== "string" || !path.isAbsolute(value)) throw new Error(`${source}: original session root was not recorded; a new run is required`);
|
|
573
|
+
const root = fs.realpathSync.native(value);
|
|
574
|
+
if (root !== value || !fs.statSync(root).isDirectory()) throw new Error(`${source}: original session root must be a canonical directory`);
|
|
575
|
+
if (sessionFile && !pathWithin(root, fs.realpathSync.native(sessionFile))) throw new Error(`${source}: session file is outside its original session root`);
|
|
576
|
+
return root;
|
|
577
|
+
}
|
|
578
|
+
|
|
538
579
|
async function resumeAsyncRun(input: {
|
|
539
580
|
params: SubagentParamsLike;
|
|
540
581
|
requestCwd: string;
|
|
@@ -556,14 +597,14 @@ async function resumeAsyncRun(input: {
|
|
|
556
597
|
const requestedId = input.params.id ?? input.params.runId;
|
|
557
598
|
const resolved = requestedId ? resolveSubagentRunId(requestedId, { state: input.deps.state, nested: nestedResolutionScopeForExecutor(input.deps) }) : undefined;
|
|
558
599
|
if (resolved?.kind === "nested") {
|
|
559
|
-
if (resolved.match.run.state === "running" || resolved.match.run.state === "queued") {
|
|
600
|
+
if (!resolveNestedAsyncDir(resolved.match.rootRunId, resolved.match.run) && (resolved.match.run.state === "running" || resolved.match.run.state === "queued")) {
|
|
560
601
|
return resumeLiveNestedRun({ target: resolved, message: followUp });
|
|
561
602
|
}
|
|
562
603
|
const trustedSessionRoots = [
|
|
563
604
|
...(input.deps.config.defaultSessionDir ? [path.resolve(input.deps.expandTilde(input.deps.config.defaultSessionDir))] : []),
|
|
564
605
|
...(parentSessionFile ? [input.deps.getSubagentSessionRoot(parentSessionFile)] : []),
|
|
565
606
|
];
|
|
566
|
-
target = resolveNestedResumeTarget(resolved, trustedSessionRoots);
|
|
607
|
+
target = resolveNestedResumeTarget(resolved, trustedSessionRoots, input.params.index);
|
|
567
608
|
} else {
|
|
568
609
|
target = resolveResumeTarget(input.params, input.deps.state);
|
|
569
610
|
}
|
|
@@ -596,20 +637,29 @@ async function resumeAsyncRun(input: {
|
|
|
596
637
|
};
|
|
597
638
|
}
|
|
598
639
|
|
|
599
|
-
const runId = randomUUID()
|
|
640
|
+
const runId = randomUUID();
|
|
641
|
+
const recordedRoot = target.sessionRoot ?? (target.source === "async" && target.sessionFile
|
|
642
|
+
? path.dirname(fs.realpathSync.native(target.sessionFile))
|
|
643
|
+
: undefined);
|
|
644
|
+
const originalRoot = validateResumeSessionRoot(recordedRoot, target.sessionFile, `Run '${target.runId}'`);
|
|
600
645
|
const artifactConfig: ArtifactConfig = { ...DEFAULT_ARTIFACT_CONFIG, enabled: input.params.artifacts !== false };
|
|
601
646
|
const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
602
647
|
const result = executeAsyncSingle(runId, {
|
|
603
648
|
agent: target.agent,
|
|
604
649
|
task: buildRevivedAsyncTask(target, followUp),
|
|
605
650
|
agentConfig,
|
|
606
|
-
ctx: buildAsyncContext(input.ctx, input.deps, input.requestCwd),
|
|
651
|
+
ctx: buildAsyncContext(input.ctx, input.deps, input.requestCwd, target.runtime),
|
|
607
652
|
cwd: effectiveCwd,
|
|
608
653
|
maxOutput: input.params.maxOutput,
|
|
609
654
|
artifactsDir: input.deps.tempArtifactsDir,
|
|
610
655
|
artifactConfig,
|
|
611
656
|
shareEnabled: input.params.share === true,
|
|
612
|
-
sessionRoot: input.
|
|
657
|
+
sessionRoot: input.params.sessionDir
|
|
658
|
+
? path.resolve(input.deps.expandTilde(input.params.sessionDir))
|
|
659
|
+
: input.deps.config.defaultSessionDir
|
|
660
|
+
? path.resolve(input.deps.expandTilde(input.deps.config.defaultSessionDir))
|
|
661
|
+
: input.deps.getSubagentSessionRoot(parentSessionFile),
|
|
662
|
+
originSessionRoot: originalRoot,
|
|
613
663
|
sessionFile: target.sessionFile,
|
|
614
664
|
maxSubagentDepth: resolveCurrentMaxSubagentDepth(input.deps.config.maxSubagentDepth),
|
|
615
665
|
worktreeSetupHook: input.deps.config.worktreeSetupHook,
|
|
@@ -891,10 +941,7 @@ function collectChainSessionFiles(
|
|
|
891
941
|
}
|
|
892
942
|
continue;
|
|
893
943
|
}
|
|
894
|
-
if (isDynamicParallelStep(step))
|
|
895
|
-
sessionFiles.push(undefined);
|
|
896
|
-
continue;
|
|
897
|
-
}
|
|
944
|
+
if (isDynamicParallelStep(step)) continue;
|
|
898
945
|
sessionFiles.push(sessionFileForIndex(flatIndex));
|
|
899
946
|
flatIndex++;
|
|
900
947
|
}
|
|
@@ -972,15 +1019,15 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
972
1019
|
}
|
|
973
1020
|
}
|
|
974
1021
|
|
|
975
|
-
if (!isAsyncAvailable()) {
|
|
1022
|
+
if (!isAsyncAvailable(data.runtime)) {
|
|
976
1023
|
return {
|
|
977
|
-
content: [{ type: "text", text:
|
|
1024
|
+
content: [{ type: "text", text: `Cannot resolve jiti from selected Pi installation ${data.runtime.piPackageRoot}` }],
|
|
978
1025
|
isError: true,
|
|
979
1026
|
details: { mode: "single" as const, results: [] },
|
|
980
1027
|
};
|
|
981
1028
|
}
|
|
982
1029
|
const id = randomUUID();
|
|
983
|
-
const asyncCtx = buildAsyncContext(ctx, deps, ctx.cwd);
|
|
1030
|
+
const asyncCtx = buildAsyncContext(ctx, deps, ctx.cwd, data.runtime);
|
|
984
1031
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
985
1032
|
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
986
1033
|
const currentProvider = ctx.model?.provider;
|
|
@@ -1020,6 +1067,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
1020
1067
|
shareEnabled,
|
|
1021
1068
|
sessionRoot,
|
|
1022
1069
|
chainSkills: [],
|
|
1070
|
+
originSessionRoot: params.context === "fork" ? sessionRoot : undefined,
|
|
1023
1071
|
sessionFilesByFlatIndex: params.tasks.map((_, index) => sessionFileForIndex(index)),
|
|
1024
1072
|
maxSubagentDepth: currentMaxSubagentDepth,
|
|
1025
1073
|
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
@@ -1047,6 +1095,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
1047
1095
|
shareEnabled,
|
|
1048
1096
|
sessionRoot,
|
|
1049
1097
|
chainSkills,
|
|
1098
|
+
originSessionRoot: params.context === "fork" ? sessionRoot : undefined,
|
|
1050
1099
|
sessionFilesByFlatIndex: collectChainSessionFiles(chain, sessionFileForIndex),
|
|
1051
1100
|
dynamicFanoutMaxItems: deps.config.chain?.dynamicFanout?.maxItems,
|
|
1052
1101
|
maxSubagentDepth: currentMaxSubagentDepth,
|
|
@@ -1085,6 +1134,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
1085
1134
|
artifactConfig,
|
|
1086
1135
|
shareEnabled,
|
|
1087
1136
|
sessionRoot,
|
|
1137
|
+
originSessionRoot: params.context === "fork" ? sessionRoot : undefined,
|
|
1088
1138
|
sessionFile: sessionFileForIndex(0),
|
|
1089
1139
|
skills,
|
|
1090
1140
|
output: effectiveOutput,
|
|
@@ -1127,6 +1177,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
1127
1177
|
const chain = wrapChainTasksForFork(params.chain as ChainStep[], params.context);
|
|
1128
1178
|
const currentMaxSubagentDepth = resolveCurrentMaxSubagentDepth(deps.config.maxSubagentDepth);
|
|
1129
1179
|
const chainResult = await executeChain({
|
|
1180
|
+
runtime: data.runtime,
|
|
1130
1181
|
chain,
|
|
1131
1182
|
task: params.task,
|
|
1132
1183
|
agents,
|
|
@@ -1136,7 +1187,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
1136
1187
|
cwd: effectiveCwd,
|
|
1137
1188
|
shareEnabled,
|
|
1138
1189
|
sessionDirForIndex,
|
|
1139
|
-
sessionFileForIndex,
|
|
1190
|
+
sessionFileForIndex: data.sessionFileHintForIndex,
|
|
1140
1191
|
artifactsDir,
|
|
1141
1192
|
artifactConfig,
|
|
1142
1193
|
includeProgress: params.includeProgress,
|
|
@@ -1156,15 +1207,15 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
1156
1207
|
});
|
|
1157
1208
|
|
|
1158
1209
|
if (chainResult.requestedAsync) {
|
|
1159
|
-
if (!isAsyncAvailable()) {
|
|
1210
|
+
if (!isAsyncAvailable(data.runtime)) {
|
|
1160
1211
|
return {
|
|
1161
|
-
content: [{ type: "text", text:
|
|
1212
|
+
content: [{ type: "text", text: `Cannot resolve jiti from selected Pi installation ${data.runtime.piPackageRoot}` }],
|
|
1162
1213
|
isError: true,
|
|
1163
1214
|
details: { mode: "chain" as const, results: [] },
|
|
1164
1215
|
};
|
|
1165
1216
|
}
|
|
1166
1217
|
const id = randomUUID();
|
|
1167
|
-
const asyncCtx = buildAsyncContext(ctx, deps, ctx.cwd);
|
|
1218
|
+
const asyncCtx = buildAsyncContext(ctx, deps, ctx.cwd, data.runtime);
|
|
1168
1219
|
const asyncChain = wrapChainTasksForFork(chainResult.requestedAsync.chain, params.context);
|
|
1169
1220
|
return executeAsyncChain(id, {
|
|
1170
1221
|
chain: asyncChain,
|
|
@@ -1179,6 +1230,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
1179
1230
|
shareEnabled,
|
|
1180
1231
|
sessionRoot,
|
|
1181
1232
|
chainSkills: chainResult.requestedAsync.chainSkills,
|
|
1233
|
+
originSessionRoot: params.context === "fork" ? sessionRoot : undefined,
|
|
1182
1234
|
sessionFilesByFlatIndex: collectChainSessionFiles(asyncChain, sessionFileForIndex),
|
|
1183
1235
|
dynamicFanoutMaxItems: deps.config.chain?.dynamicFanout?.maxItems,
|
|
1184
1236
|
maxSubagentDepth: currentMaxSubagentDepth,
|
|
@@ -1192,7 +1244,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
1192
1244
|
|
|
1193
1245
|
const chainDetails = chainResult.details ? compactForegroundDetails({ ...chainResult.details, runId }) : undefined;
|
|
1194
1246
|
if (foregroundControl) updateForegroundNestedProjection(foregroundControl);
|
|
1195
|
-
if (chainDetails) rememberForegroundRun(deps.state, { runId, mode: "chain", cwd: effectiveCwd, results: chainDetails.results });
|
|
1247
|
+
if (chainDetails) rememberForegroundRun(deps.state, { runtime: data.runtime, sessionRoot: data.sessionRoot, runId, mode: "chain", cwd: effectiveCwd, results: chainDetails.results });
|
|
1196
1248
|
recordSyncCost(deps.state.grandTotal, runId, (chainDetails?.results.reduce((sum, r) => sum + r.usage.cost, 0) ?? 0) + sumNestedCost(foregroundControl?.nestedChildren));
|
|
1197
1249
|
renderGrandTotal(deps.state);
|
|
1198
1250
|
|
|
@@ -1200,6 +1252,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
1200
1252
|
}
|
|
1201
1253
|
|
|
1202
1254
|
interface ForegroundParallelRunInput {
|
|
1255
|
+
runtime: PiRuntimeIdentity;
|
|
1203
1256
|
tasks: TaskParam[];
|
|
1204
1257
|
taskTexts: string[];
|
|
1205
1258
|
agents: AgentConfig[];
|
|
@@ -1405,6 +1458,7 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
1405
1458
|
}
|
|
1406
1459
|
const agentConfig = input.agents.find((agent) => agent.name === task.agent);
|
|
1407
1460
|
return runSync(input.ctx.cwd, input.agents, task.agent, taskText, {
|
|
1461
|
+
runtime: input.runtime,
|
|
1408
1462
|
cwd: taskCwd,
|
|
1409
1463
|
signal: input.signal,
|
|
1410
1464
|
interruptSignal: interruptController.signal,
|
|
@@ -1593,15 +1647,15 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
1593
1647
|
}
|
|
1594
1648
|
|
|
1595
1649
|
if (result.runInBackground) {
|
|
1596
|
-
if (!isAsyncAvailable()) {
|
|
1650
|
+
if (!isAsyncAvailable(data.runtime)) {
|
|
1597
1651
|
return {
|
|
1598
|
-
content: [{ type: "text", text:
|
|
1652
|
+
content: [{ type: "text", text: `Cannot resolve jiti from selected Pi installation ${data.runtime.piPackageRoot}` }],
|
|
1599
1653
|
isError: true,
|
|
1600
1654
|
details: { mode: "parallel" as const, results: [] },
|
|
1601
1655
|
};
|
|
1602
1656
|
}
|
|
1603
1657
|
const id = randomUUID();
|
|
1604
|
-
const asyncCtx = buildAsyncContext(ctx, deps, ctx.cwd);
|
|
1658
|
+
const asyncCtx = buildAsyncContext(ctx, deps, ctx.cwd, data.runtime);
|
|
1605
1659
|
const parallelTasks = tasks.map((t, i) => {
|
|
1606
1660
|
const taskText = params.context === "fork" ? wrapForkTask(taskTexts[i]!) : taskTexts[i]!;
|
|
1607
1661
|
const progress = taskDisallowsFileUpdates(taskText) ? false : behaviorOverrides[i]?.progress;
|
|
@@ -1631,6 +1685,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
1631
1685
|
shareEnabled,
|
|
1632
1686
|
sessionRoot,
|
|
1633
1687
|
chainSkills: [],
|
|
1688
|
+
originSessionRoot: params.context === "fork" ? sessionRoot : undefined,
|
|
1634
1689
|
sessionFilesByFlatIndex: tasks.map((_, index) => sessionFileForIndex(index)),
|
|
1635
1690
|
maxSubagentDepth: currentMaxSubagentDepth,
|
|
1636
1691
|
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
@@ -1702,6 +1757,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
1702
1757
|
: undefined;
|
|
1703
1758
|
|
|
1704
1759
|
const results = await runForegroundParallelTasks({
|
|
1760
|
+
runtime: data.runtime,
|
|
1705
1761
|
tasks,
|
|
1706
1762
|
taskTexts,
|
|
1707
1763
|
agents,
|
|
@@ -1709,7 +1765,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
1709
1765
|
signal,
|
|
1710
1766
|
runId,
|
|
1711
1767
|
sessionDirForIndex,
|
|
1712
|
-
sessionFileForIndex,
|
|
1768
|
+
sessionFileForIndex: data.sessionFileHintForIndex,
|
|
1713
1769
|
shareEnabled,
|
|
1714
1770
|
artifactConfig,
|
|
1715
1771
|
artifactsDir,
|
|
@@ -1755,7 +1811,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
1755
1811
|
progress: params.includeProgress ? allProgress : undefined,
|
|
1756
1812
|
artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
|
|
1757
1813
|
});
|
|
1758
|
-
rememberForegroundRun(deps.state, { runId, mode: "parallel", cwd: effectiveCwd, results: details.results });
|
|
1814
|
+
rememberForegroundRun(deps.state, { runtime: data.runtime, sessionRoot: data.sessionRoot, runId, mode: "parallel", cwd: effectiveCwd, results: details.results });
|
|
1759
1815
|
if (interrupted) {
|
|
1760
1816
|
return {
|
|
1761
1817
|
content: [{ type: "text", text: withWorktreeSuffix(`Parallel run paused after interrupt (${interrupted.agent}). Waiting for explicit next action.`) }],
|
|
@@ -1865,15 +1921,15 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
1865
1921
|
if (override?.skills !== undefined) skillOverride = override.skills;
|
|
1866
1922
|
|
|
1867
1923
|
if (result.runInBackground) {
|
|
1868
|
-
if (!isAsyncAvailable()) {
|
|
1924
|
+
if (!isAsyncAvailable(data.runtime)) {
|
|
1869
1925
|
return {
|
|
1870
|
-
content: [{ type: "text", text:
|
|
1926
|
+
content: [{ type: "text", text: `Cannot resolve jiti from selected Pi installation ${data.runtime.piPackageRoot}` }],
|
|
1871
1927
|
isError: true,
|
|
1872
1928
|
details: { mode: "single" as const, results: [] },
|
|
1873
1929
|
};
|
|
1874
1930
|
}
|
|
1875
1931
|
const id = randomUUID();
|
|
1876
|
-
const asyncCtx = buildAsyncContext(ctx, deps, ctx.cwd);
|
|
1932
|
+
const asyncCtx = buildAsyncContext(ctx, deps, ctx.cwd, data.runtime);
|
|
1877
1933
|
return executeAsyncSingle(id, {
|
|
1878
1934
|
agent: params.agent!,
|
|
1879
1935
|
task: params.context === "fork" ? wrapForkTask(task) : task,
|
|
@@ -1886,6 +1942,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
1886
1942
|
artifactConfig,
|
|
1887
1943
|
shareEnabled,
|
|
1888
1944
|
sessionRoot,
|
|
1945
|
+
originSessionRoot: params.context === "fork" ? sessionRoot : undefined,
|
|
1889
1946
|
sessionFile: sessionFileForIndex(0),
|
|
1890
1947
|
skills: skillOverride === false ? [] : skillOverride,
|
|
1891
1948
|
output: effectiveOutput,
|
|
@@ -1956,12 +2013,13 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
1956
2013
|
: undefined;
|
|
1957
2014
|
|
|
1958
2015
|
const r = await runSync(ctx.cwd, agents, params.agent!, task, {
|
|
2016
|
+
runtime: data.runtime,
|
|
1959
2017
|
cwd: effectiveCwd,
|
|
1960
2018
|
signal,
|
|
1961
2019
|
interruptSignal: interruptController.signal,
|
|
1962
2020
|
runId,
|
|
1963
2021
|
sessionDir: sessionDirForIndex(0),
|
|
1964
|
-
sessionFile:
|
|
2022
|
+
sessionFile: data.sessionFileHintForIndex(0),
|
|
1965
2023
|
share: shareEnabled,
|
|
1966
2024
|
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
1967
2025
|
artifactConfig,
|
|
@@ -2024,7 +2082,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
2024
2082
|
artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
|
|
2025
2083
|
truncation: r.truncation,
|
|
2026
2084
|
});
|
|
2027
|
-
rememberForegroundRun(deps.state, { runId, mode: "single", cwd: effectiveCwd, results: details.results });
|
|
2085
|
+
rememberForegroundRun(deps.state, { runtime: data.runtime, sessionRoot: data.sessionRoot, runId, mode: "single", cwd: effectiveCwd, results: details.results });
|
|
2028
2086
|
|
|
2029
2087
|
if (!r.interrupted) {
|
|
2030
2088
|
if (foregroundControl) updateForegroundNestedProjection(foregroundControl);
|
|
@@ -2059,6 +2117,13 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2059
2117
|
ctx: ExtensionContext,
|
|
2060
2118
|
) => Promise<AgentToolResult<Details>>;
|
|
2061
2119
|
} {
|
|
2120
|
+
let runtime: PiRuntimeIdentity | undefined;
|
|
2121
|
+
let runtimeError: unknown;
|
|
2122
|
+
try {
|
|
2123
|
+
runtime = (deps.captureRuntime ?? capturePiRuntime)();
|
|
2124
|
+
} catch (error) {
|
|
2125
|
+
runtimeError = error;
|
|
2126
|
+
}
|
|
2062
2127
|
const execute = async (
|
|
2063
2128
|
_id: string,
|
|
2064
2129
|
params: SubagentParamsLike,
|
|
@@ -2091,6 +2156,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2091
2156
|
config: deps.config,
|
|
2092
2157
|
state: deps.state,
|
|
2093
2158
|
requestedSessionDir: paramsWithResolvedCwd.sessionDir,
|
|
2159
|
+
runtime,
|
|
2160
|
+
runtimeError: runtimeError instanceof Error ? runtimeError.message : runtimeError === undefined ? undefined : String(runtimeError),
|
|
2094
2161
|
currentSessionFile,
|
|
2095
2162
|
currentSessionId,
|
|
2096
2163
|
sessionError,
|
|
@@ -2105,7 +2172,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2105
2172
|
if (targetRunId) {
|
|
2106
2173
|
try {
|
|
2107
2174
|
const nestedScope = nestedResolutionScopeForExecutor(deps);
|
|
2108
|
-
const resolved = resolveSubagentRunId(targetRunId, { state: deps.state, nested: nestedScope });
|
|
2175
|
+
const resolved = resolveSubagentRunId(targetRunId, { state: deps.state, nested: nestedScope, readOnlyLegacy: true });
|
|
2109
2176
|
if (resolved?.kind === "foreground") {
|
|
2110
2177
|
const foreground = getForegroundControl(deps.state, resolved.id);
|
|
2111
2178
|
if (foreground) return foregroundStatusResult(foreground);
|
|
@@ -2121,7 +2188,11 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2121
2188
|
return inspectSubagentStatus(paramsWithResolvedCwd, { state: deps.state, nested: nestedResolutionScopeForExecutor(deps) });
|
|
2122
2189
|
}
|
|
2123
2190
|
if (params.action === "resume") {
|
|
2124
|
-
|
|
2191
|
+
try {
|
|
2192
|
+
return await resumeAsyncRun({ params: paramsWithResolvedCwd, requestCwd, ctx, deps });
|
|
2193
|
+
} catch (error) {
|
|
2194
|
+
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
|
|
2195
|
+
}
|
|
2125
2196
|
}
|
|
2126
2197
|
if (params.action === "interrupt") {
|
|
2127
2198
|
const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
|
|
@@ -2177,6 +2248,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2177
2248
|
return handleManagementAction(params.action, paramsWithResolvedCwd, { ...ctx, cwd: requestCwd });
|
|
2178
2249
|
}
|
|
2179
2250
|
|
|
2251
|
+
if (!runtime) return toExecutionErrorResult(paramsWithResolvedCwd, runtimeError ?? new Error("Parent runtime capture failed."));
|
|
2252
|
+
|
|
2180
2253
|
const { blocked, depth, maxDepth } = checkSubagentDepth(deps.config.maxSubagentDepth);
|
|
2181
2254
|
if (blocked) {
|
|
2182
2255
|
return {
|
|
@@ -2207,7 +2280,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2207
2280
|
const scope: AgentScope = resolveExecutionAgentScope(effectiveParams.agentScope);
|
|
2208
2281
|
const effectiveCwd = effectiveParams.cwd ?? ctx.cwd;
|
|
2209
2282
|
const parentSessionFile = ctx.sessionManager.getSessionFile() ?? null;
|
|
2210
|
-
const runId = randomUUID()
|
|
2283
|
+
const runId = randomUUID();
|
|
2211
2284
|
const inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();
|
|
2212
2285
|
const nestedParentAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
|
|
2213
2286
|
const nestedRoute = inheritedNestedRoute ?? createNestedRoute(runId);
|
|
@@ -2228,12 +2301,6 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2228
2301
|
);
|
|
2229
2302
|
if (validationError) return validationError;
|
|
2230
2303
|
|
|
2231
|
-
let sessionFileForIndex: (idx?: number) => string | undefined = () => undefined;
|
|
2232
|
-
try {
|
|
2233
|
-
sessionFileForIndex = createForkContextResolver(ctx.sessionManager, effectiveParams.context).sessionFileForIndex;
|
|
2234
|
-
} catch (error) {
|
|
2235
|
-
return toExecutionErrorResult(effectiveParams, error);
|
|
2236
|
-
}
|
|
2237
2304
|
const requestedAsync = effectiveParams.async ?? deps.asyncByDefault;
|
|
2238
2305
|
const backgroundRequestedWhileClarifying = (hasChain || hasTasks) && requestedAsync && effectiveParams.clarify === true;
|
|
2239
2306
|
const effectiveAsync = requestedAsync && effectiveParams.clarify !== true;
|
|
@@ -2246,17 +2313,16 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2246
2313
|
};
|
|
2247
2314
|
const artifactsDir = effectiveAsync ? deps.tempArtifactsDir : getArtifactsDir(parentSessionFile);
|
|
2248
2315
|
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
} else {
|
|
2253
|
-
const baseSessionRoot = deps.config.defaultSessionDir
|
|
2316
|
+
const baseSessionRoot = effectiveParams.sessionDir
|
|
2317
|
+
? path.resolve(deps.expandTilde(effectiveParams.sessionDir))
|
|
2318
|
+
: deps.config.defaultSessionDir
|
|
2254
2319
|
? path.resolve(deps.expandTilde(deps.config.defaultSessionDir))
|
|
2255
2320
|
: deps.getSubagentSessionRoot(parentSessionFile);
|
|
2256
|
-
|
|
2257
|
-
}
|
|
2321
|
+
let sessionRoot = path.join(baseSessionRoot, runId);
|
|
2258
2322
|
try {
|
|
2259
|
-
fs.mkdirSync(
|
|
2323
|
+
fs.mkdirSync(baseSessionRoot, { recursive: true });
|
|
2324
|
+
fs.mkdirSync(sessionRoot, { mode: 0o700 });
|
|
2325
|
+
sessionRoot = fs.realpathSync.native(sessionRoot);
|
|
2260
2326
|
} catch (error) {
|
|
2261
2327
|
const message = error instanceof Error ? error.message : String(error);
|
|
2262
2328
|
return toExecutionErrorResult(
|
|
@@ -2266,6 +2332,12 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2266
2332
|
}
|
|
2267
2333
|
const sessionDirForIndex = (idx?: number) =>
|
|
2268
2334
|
path.join(sessionRoot, `run-${idx ?? 0}`);
|
|
2335
|
+
let sessionFileForIndex: (idx?: number) => string | undefined;
|
|
2336
|
+
try {
|
|
2337
|
+
sessionFileForIndex = createForkContextResolver(ctx.sessionManager, effectiveParams.context, { sessionDirForIndex }).sessionFileForIndex;
|
|
2338
|
+
} catch (error) {
|
|
2339
|
+
return toExecutionErrorResult(effectiveParams, error);
|
|
2340
|
+
}
|
|
2269
2341
|
const childSessionFileForIndex = (idx?: number) =>
|
|
2270
2342
|
sessionFileForIndex(idx) ?? path.join(sessionDirForIndex(idx), "session.jsonl");
|
|
2271
2343
|
|
|
@@ -2274,6 +2346,7 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2274
2346
|
: undefined;
|
|
2275
2347
|
|
|
2276
2348
|
const execData: ExecutionContextData = {
|
|
2349
|
+
runtime,
|
|
2277
2350
|
params: effectiveParams,
|
|
2278
2351
|
effectiveCwd,
|
|
2279
2352
|
ctx,
|
|
@@ -2284,7 +2357,8 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2284
2357
|
shareEnabled,
|
|
2285
2358
|
sessionRoot,
|
|
2286
2359
|
sessionDirForIndex,
|
|
2287
|
-
sessionFileForIndex
|
|
2360
|
+
sessionFileForIndex,
|
|
2361
|
+
sessionFileHintForIndex: childSessionFileForIndex,
|
|
2288
2362
|
artifactConfig,
|
|
2289
2363
|
artifactsDir,
|
|
2290
2364
|
backgroundRequestedWhileClarifying,
|
|
@@ -2332,12 +2406,15 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2332
2406
|
? effectiveParams.chain.flatMap((step) => isParallelStep(step) ? step.parallel.map((task) => task.agent) : [(step as SequentialStep).agent])
|
|
2333
2407
|
: effectiveParams.agent ? [effectiveParams.agent] : [];
|
|
2334
2408
|
try {
|
|
2335
|
-
writeNestedEvent(inheritedNestedRoute, {
|
|
2409
|
+
const written = writeNestedEvent(inheritedNestedRoute, {
|
|
2336
2410
|
type,
|
|
2337
2411
|
ts: now,
|
|
2338
2412
|
parentRunId: nestedParentAddress.parentRunId,
|
|
2339
2413
|
parentStepIndex: nestedParentAddress.parentStepIndex,
|
|
2340
2414
|
child: {
|
|
2415
|
+
runtime,
|
|
2416
|
+
sessionRoot,
|
|
2417
|
+
capabilityToken: inheritedNestedRoute.capabilityToken,
|
|
2341
2418
|
id: runId,
|
|
2342
2419
|
parentRunId: nestedParentAddress.parentRunId,
|
|
2343
2420
|
parentStepIndex: nestedParentAddress.parentStepIndex,
|
|
@@ -2360,7 +2437,9 @@ export function createSubagentExecutor(deps: ExecutorDeps): {
|
|
|
2360
2437
|
})) } : {}),
|
|
2361
2438
|
},
|
|
2362
2439
|
});
|
|
2440
|
+
if (type === "subagent.nested.started" && written === false) throw new Error("Nested origin stamp was refused.");
|
|
2363
2441
|
} catch (error) {
|
|
2442
|
+
if (type === "subagent.nested.started") throw error;
|
|
2364
2443
|
console.error("Failed to emit nested foreground status event:", error);
|
|
2365
2444
|
}
|
|
2366
2445
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
+
import type { PiRuntimeIdentity } from "./pi-spawn.ts";
|
|
2
3
|
import type { Message } from "@earendil-works/pi-ai";
|
|
3
4
|
import {
|
|
4
5
|
createChildHostController,
|
|
@@ -16,6 +17,7 @@ import { createSessionWatcher, type OnNewSessionMessage, type SessionWatcher } f
|
|
|
16
17
|
import type { ExecutionBackend, ExecutionSurfaceHandle } from "../../execution-backend/types.ts";
|
|
17
18
|
|
|
18
19
|
export interface ExternalExecutionOptions {
|
|
20
|
+
readonly runtime: PiRuntimeIdentity;
|
|
19
21
|
readonly backend: ExecutionBackend;
|
|
20
22
|
readonly runId: string;
|
|
21
23
|
readonly childId: string;
|