pi-subagents 0.82.1 → 0.83.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 +49 -0
- package/bundle/agents.js +5 -5
- package/bundle/background-work.js +1 -1
- package/bundle/capability-ceiling.js +1 -1
- package/bundle/child-tool-plan.js +4 -4
- package/bundle/{chunk-RMQFMMAT.js → chunk-2D52JFLF.js} +231 -10
- package/bundle/{chunk-52UBALJL.js → chunk-2DHZTCOL.js} +2 -2
- package/bundle/{chunk-43SCHIMD.js → chunk-2S4K53MV.js} +1 -1
- package/bundle/{chunk-IVAE5Z6Q.js → chunk-5YIITCPD.js} +31 -27
- package/bundle/{chunk-JYUKW2SY.js → chunk-AHIFYGR3.js} +13 -13
- package/bundle/{chunk-XSWOTIA3.js → chunk-C5P5CHYK.js} +1 -1
- package/bundle/{chunk-PDZWJCHD.js → chunk-C7JMPU5F.js} +10 -7
- package/bundle/{chunk-5TYMSZX3.js → chunk-D3JAFGMO.js} +2 -2
- package/bundle/{chunk-OYF6AFDY.js → chunk-F4ASL6OV.js} +2 -2
- package/bundle/{chunk-ZIV4J4HG.js → chunk-F5DBXJ2G.js} +51 -34
- package/bundle/{chunk-YKEWIB4M.js → chunk-G2Z6ZH26.js} +1 -1
- package/bundle/{chunk-G4GZD3OJ.js → chunk-IPGZMQXV.js} +11 -11
- package/bundle/{chunk-5LFLVSTM.js → chunk-JEZOSEWG.js} +2 -0
- package/bundle/{chunk-PLCCBLPY.js → chunk-LFDBD4PW.js} +2 -2
- package/bundle/{chunk-CIZQXJ5Q.js → chunk-LNRJCP2Y.js} +56 -20
- package/bundle/{chunk-UTT254JE.js → chunk-MABL3FUM.js} +8 -4
- package/bundle/{chunk-3JCDWF4V.js → chunk-NPIYWVOD.js} +12 -18
- package/bundle/{chunk-UCPP4GNU.js → chunk-QP5ZWNIS.js} +12 -5
- package/bundle/{chunk-Z7KVHQHL.js → chunk-QXASAHIA.js} +2 -2
- package/bundle/{chunk-46XKCPJR.js → chunk-RNIZLSEA.js} +2221 -3260
- package/bundle/{chunk-GJECHSAR.js → chunk-SSG7IEP5.js} +2 -2
- package/bundle/{chunk-VSUSLDNW.js → chunk-UFL7IYCW.js} +10 -8
- package/bundle/{chunk-QE4AGIMG.js → chunk-UUP372DR.js} +482 -705
- package/bundle/{chunk-6XJB7PIX.js → chunk-UYK5MZWE.js} +16 -12
- package/bundle/{chunk-4SDU2BZX.js → chunk-V2BZYLEP.js} +3 -3
- package/bundle/{chunk-FXUXCX2K.js → chunk-VLFZIN6D.js} +6 -6
- package/bundle/{chunk-N2BN7BF5.js → chunk-XA3DY6UG.js} +7 -5
- package/bundle/{chunk-PBK2I7WY.js → chunk-YXFJVCOL.js} +6 -6
- package/bundle/{chunk-4Y2ROR7L.js → chunk-ZVOAMTUQ.js} +5 -5
- package/bundle/control-channel.js +4 -4
- package/bundle/index.js +1 -1
- package/bundle/intercom-bridge.js +4 -4
- package/bundle/preflight.js +10 -10
- package/bundle/project-panes.js +5 -5
- package/bundle/shared-types.js +1 -1
- package/docs/configuration.md +13 -2
- package/docs/extension-api.md +21 -4
- package/docs/missions.md +2 -0
- package/docs/observability.md +1 -0
- package/docs/tool-reference.md +2 -2
- package/package.json +1 -1
- package/skills/pi-subagents/SKILL.md +6 -6
- package/src/agents/agent-management.d.ts +1 -1
- package/src/agents/agent-management.js +10 -16
- package/src/agents/agent-serializer.js +1 -2
- package/src/agents/agents.js +20 -5
- package/src/agents/frontmatter.js +22 -18
- package/src/api/background-work.d.ts +9 -6
- package/src/api/background-work.js +14 -7
- package/src/extension/index.js +6 -3
- package/src/extension/public-execution.d.ts +2 -2
- package/src/extension/public-execution.js +3 -3
- package/src/extension/reply-workflow-script.js +4 -5
- package/src/extension/rpc.js +3 -1
- package/src/extension/subagent-options.js +17 -4
- package/src/missions/actions.js +7 -2
- package/src/missions/lifecycle.d.ts +11 -0
- package/src/missions/lifecycle.js +37 -6
- package/src/missions/store.js +1 -0
- package/src/missions/types.d.ts +2 -0
- package/src/runs/background/async-execution.d.ts +1 -1
- package/src/runs/background/async-execution.js +7 -4
- package/src/runs/background/async-job-tracker.js +2 -1
- package/src/runs/background/async-resume.js +3 -2
- package/src/runs/background/auto-drain.js +2 -3
- package/src/runs/background/fleet-view.js +1 -1
- package/src/runs/background/retained-children.js +1 -1
- package/src/runs/background/run-child-session.js +22 -6
- package/src/runs/background/subagent-wait.d.ts +2 -1
- package/src/runs/background/subagent-wait.js +5 -2
- package/src/runs/foreground/execution.js +22 -6
- package/src/runs/foreground/subagent-executor.js +26 -56
- package/src/runs/shared/capability-ceiling.d.ts +2 -1
- package/src/runs/shared/capability-ceiling.js +3 -2
- package/src/runs/shared/child-launch.js +5 -2
- package/src/runs/shared/child-session.d.ts +6 -0
- package/src/runs/shared/child-session.js +20 -4
- package/src/runs/shared/herdr-machine.d.ts +2 -0
- package/src/runs/shared/herdr-machine.js +2 -0
- package/src/runs/shared/herdr-placed-run.js +11 -4
- package/src/runs/shared/parallel-handoff.d.ts +1 -1
- package/src/runs/shared/parallel-handoff.js +10 -6
- package/src/runs/shared/subagent-prompt-runtime.js +11 -17
- package/src/shared/disabled-features.d.ts +3 -1
- package/src/shared/disabled-features.js +11 -3
- package/src/shared/types.d.ts +4 -2
- package/src/shared/types.js +2 -0
- package/src/workflows/scripted-workflow.js +13 -1071
- package/workflow-script-worker.cjs +1068 -0
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
projectRuntimeAcknowledgedExtensions,
|
|
4
4
|
registerSubagentPromptBoundary,
|
|
5
5
|
registerSubagentPromptRuntime
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-NPIYWVOD.js";
|
|
7
7
|
import {
|
|
8
8
|
INSPECTOR_ACTIONS,
|
|
9
9
|
canQueueRetainedAsyncFollowUp,
|
|
@@ -25,12 +25,13 @@ import {
|
|
|
25
25
|
updateSteeringTarget,
|
|
26
26
|
waitForSteeringAction,
|
|
27
27
|
workflowAsyncChildSteeringGuidance
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-YXFJVCOL.js";
|
|
29
29
|
import {
|
|
30
|
-
|
|
30
|
+
PLACED_RESTORE_UNSUPPORTED,
|
|
31
|
+
formatHerdrMachineRunnerUnsupported,
|
|
31
32
|
isRexLocalMachine,
|
|
32
|
-
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
resolveMachinePlacement
|
|
34
|
+
} from "./chunk-2D52JFLF.js";
|
|
34
35
|
import {
|
|
35
36
|
COMPLETION_ACTION,
|
|
36
37
|
DEFAULT_CONTROL_CONFIG,
|
|
@@ -119,7 +120,7 @@ import {
|
|
|
119
120
|
workflowStopCause,
|
|
120
121
|
writeWorkflowLaunchRecord,
|
|
121
122
|
writeWorkflowReceipt
|
|
122
|
-
} from "./chunk-
|
|
123
|
+
} from "./chunk-F5DBXJ2G.js";
|
|
123
124
|
import {
|
|
124
125
|
NATIVE_SUPERVISOR_TOOL_NAME,
|
|
125
126
|
SUBAGENT_PARENT_SESSION_ENV,
|
|
@@ -135,7 +136,7 @@ import {
|
|
|
135
136
|
resolveWatchdogConfig,
|
|
136
137
|
sendRuleViolationWarning,
|
|
137
138
|
writeWatchdogModelSettings
|
|
138
|
-
} from "./chunk-
|
|
139
|
+
} from "./chunk-UFL7IYCW.js";
|
|
139
140
|
import {
|
|
140
141
|
MAX_REMEMBERED_FOREGROUND_RUNS,
|
|
141
142
|
discardRunningForegroundRun,
|
|
@@ -148,7 +149,7 @@ import {
|
|
|
148
149
|
resolveSubagentRunId,
|
|
149
150
|
resolveWorkflowChatProgress,
|
|
150
151
|
stopAsyncRun
|
|
151
|
-
} from "./chunk-
|
|
152
|
+
} from "./chunk-VLFZIN6D.js";
|
|
152
153
|
import {
|
|
153
154
|
MODEL_ONLY_TOOL,
|
|
154
155
|
RunFanoutLimitError,
|
|
@@ -210,7 +211,7 @@ import {
|
|
|
210
211
|
writeParallelHandoffGroup,
|
|
211
212
|
writeRunFanoutBudgetDescriptor,
|
|
212
213
|
writeWorktreeSetupHandoff
|
|
213
|
-
} from "./chunk-
|
|
214
|
+
} from "./chunk-UYK5MZWE.js";
|
|
214
215
|
import {
|
|
215
216
|
authorizeWorkflowResourceHost,
|
|
216
217
|
claimWorkflowChildPermit,
|
|
@@ -227,14 +228,15 @@ import {
|
|
|
227
228
|
canPreferFork,
|
|
228
229
|
createForkContextResolver,
|
|
229
230
|
resolveSubagentLaunchContext
|
|
230
|
-
} from "./chunk-
|
|
231
|
+
} from "./chunk-SSG7IEP5.js";
|
|
231
232
|
import {
|
|
232
233
|
handleRefinementAction
|
|
233
|
-
} from "./chunk-
|
|
234
|
+
} from "./chunk-LFDBD4PW.js";
|
|
234
235
|
import {
|
|
235
236
|
ActiveAsyncCapacityError,
|
|
236
237
|
acquireActiveAsyncCapacity,
|
|
237
238
|
disabledFeatureNotice,
|
|
239
|
+
disabledFeatureSectionNotice,
|
|
238
240
|
disabledFeatureUseError,
|
|
239
241
|
getActiveAsyncCapacitySnapshot,
|
|
240
242
|
inspectActiveAsyncCapacityOwner,
|
|
@@ -244,7 +246,7 @@ import {
|
|
|
244
246
|
resolveDisabledFeatureSurface,
|
|
245
247
|
resolveMaxActiveAsyncRunsPerSession,
|
|
246
248
|
transferActiveAsyncCapacity
|
|
247
|
-
} from "./chunk-
|
|
249
|
+
} from "./chunk-QP5ZWNIS.js";
|
|
248
250
|
import {
|
|
249
251
|
DEFAULT_STALE_TERMINAL_ACTIVE_MARKER_MS,
|
|
250
252
|
MACHINE_DIFF_OPTIONS,
|
|
@@ -278,6 +280,7 @@ import {
|
|
|
278
280
|
inheritedNestedParentAddressOf,
|
|
279
281
|
inheritedNestedRouteOf,
|
|
280
282
|
initializeProcessTerminal,
|
|
283
|
+
missionAcceptanceStatus,
|
|
281
284
|
nestedResultsPath,
|
|
282
285
|
nestedRunNotActiveMessage,
|
|
283
286
|
nestedRunScope,
|
|
@@ -323,7 +326,7 @@ import {
|
|
|
323
326
|
writeNestedControlRequest,
|
|
324
327
|
writeNestedControlResult,
|
|
325
328
|
writeNestedEvent
|
|
326
|
-
} from "./chunk-
|
|
329
|
+
} from "./chunk-RNIZLSEA.js";
|
|
327
330
|
import {
|
|
328
331
|
closeSteerInbox,
|
|
329
332
|
consumeSteerRequests,
|
|
@@ -331,7 +334,7 @@ import {
|
|
|
331
334
|
readRevivalBriefs,
|
|
332
335
|
requestAsyncSteer,
|
|
333
336
|
watchAsyncControlInbox
|
|
334
|
-
} from "./chunk-
|
|
337
|
+
} from "./chunk-F4ASL6OV.js";
|
|
335
338
|
import "./chunk-NIXWDZZV.js";
|
|
336
339
|
import {
|
|
337
340
|
INTERCOM_BRIDGE_MARKER,
|
|
@@ -340,7 +343,7 @@ import {
|
|
|
340
343
|
resolveIntercomBridge,
|
|
341
344
|
resolveIntercomSessionTarget,
|
|
342
345
|
resolveSubagentIntercomTarget
|
|
343
|
-
} from "./chunk-
|
|
346
|
+
} from "./chunk-C5P5CHYK.js";
|
|
344
347
|
import {
|
|
345
348
|
resolveLaunchBinding,
|
|
346
349
|
stableJsonDigest
|
|
@@ -354,7 +357,7 @@ import {
|
|
|
354
357
|
resolvePiLaunchToolPlan,
|
|
355
358
|
resolveRuntimeModulePath,
|
|
356
359
|
supervisorChannelDir
|
|
357
|
-
} from "./chunk-
|
|
360
|
+
} from "./chunk-V2BZYLEP.js";
|
|
358
361
|
import {
|
|
359
362
|
assertRequiredChildExtensionsAdmitted,
|
|
360
363
|
hasMandatoryRequiredChildExtensions,
|
|
@@ -365,7 +368,7 @@ import {
|
|
|
365
368
|
import {
|
|
366
369
|
HERDR_PROJECT_PANE_ACTIONS,
|
|
367
370
|
handleHerdrProjectPaneAction
|
|
368
|
-
} from "./chunk-
|
|
371
|
+
} from "./chunk-2DHZTCOL.js";
|
|
369
372
|
import "./chunk-7J4ZXMY7.js";
|
|
370
373
|
import {
|
|
371
374
|
PROJECT_SUBAGENTS_RELATIVE_DIR,
|
|
@@ -378,14 +381,13 @@ import {
|
|
|
378
381
|
getProjectSubagentsDir,
|
|
379
382
|
writeArtifact,
|
|
380
383
|
writeMetadata
|
|
381
|
-
} from "./chunk-
|
|
382
|
-
import "./chunk-
|
|
384
|
+
} from "./chunk-D3JAFGMO.js";
|
|
385
|
+
import "./chunk-XA3DY6UG.js";
|
|
383
386
|
import {
|
|
384
387
|
getExternalJobProvider
|
|
385
388
|
} from "./chunk-YYREO77V.js";
|
|
386
|
-
import "./chunk-
|
|
389
|
+
import "./chunk-G2Z6ZH26.js";
|
|
387
390
|
import {
|
|
388
|
-
CODE_OWNED_EXTERNAL_CLI_ADAPTER_IDS,
|
|
389
391
|
ChainOutputValidationError,
|
|
390
392
|
MAX_STREAMED_RECENT_TOOLS,
|
|
391
393
|
MISSING_STRUCTURED_ACCEPTANCE_REPORT_ERROR,
|
|
@@ -449,7 +451,6 @@ import {
|
|
|
449
451
|
formatUnknownAgentError,
|
|
450
452
|
getAgentDir,
|
|
451
453
|
getFinalOutput,
|
|
452
|
-
getProjectConfigDir,
|
|
453
454
|
getSingleResultOutput,
|
|
454
455
|
getStepAgents,
|
|
455
456
|
hasEmptyTerminalAssistantResponse,
|
|
@@ -531,7 +532,7 @@ import {
|
|
|
531
532
|
writeAtomicJson,
|
|
532
533
|
writeInitialProgressFile,
|
|
533
534
|
writePrivateAtomicJson
|
|
534
|
-
} from "./chunk-
|
|
535
|
+
} from "./chunk-LNRJCP2Y.js";
|
|
535
536
|
import {
|
|
536
537
|
CHAIN_RUNS_DIR,
|
|
537
538
|
DEFAULT_ARTIFACT_CONFIG,
|
|
@@ -560,7 +561,7 @@ import {
|
|
|
560
561
|
resolveMaxSubagentSpawnsPerSession,
|
|
561
562
|
truncateOutput,
|
|
562
563
|
wrapForkTask
|
|
563
|
-
} from "./chunk-
|
|
564
|
+
} from "./chunk-JEZOSEWG.js";
|
|
564
565
|
import {
|
|
565
566
|
safeTerminalText
|
|
566
567
|
} from "./chunk-YFCK7H2S.js";
|
|
@@ -569,12 +570,12 @@ import {
|
|
|
569
570
|
assertAgentAllowedByCapabilityCeiling,
|
|
570
571
|
intersectSubagentCapabilityCeilings,
|
|
571
572
|
resolveCurrentSubagentCapabilityCeiling
|
|
572
|
-
} from "./chunk-
|
|
573
|
+
} from "./chunk-QXASAHIA.js";
|
|
573
574
|
|
|
574
575
|
// src/runs/foreground/subagent-executor.ts
|
|
575
576
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
576
|
-
import * as
|
|
577
|
-
import * as
|
|
577
|
+
import * as fs20 from "node:fs";
|
|
578
|
+
import * as path18 from "node:path";
|
|
578
579
|
|
|
579
580
|
// src/shared/capacity-resilient-json.ts
|
|
580
581
|
var defaultTimerApi = {
|
|
@@ -775,235 +776,24 @@ function finishForegroundChild(control, index) {
|
|
|
775
776
|
}
|
|
776
777
|
|
|
777
778
|
// src/extension/doctor.ts
|
|
778
|
-
import * as
|
|
779
|
-
import * as
|
|
779
|
+
import * as fs7 from "node:fs";
|
|
780
|
+
import * as path6 from "node:path";
|
|
780
781
|
|
|
781
782
|
// src/runs/background/async-execution.ts
|
|
782
783
|
import { spawn } from "node:child_process";
|
|
783
784
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
784
|
-
import * as
|
|
785
|
-
import * as
|
|
785
|
+
import * as fs6 from "node:fs";
|
|
786
|
+
import * as path5 from "node:path";
|
|
786
787
|
import { fileURLToPath } from "node:url";
|
|
787
788
|
import * as nodeModule from "node:module";
|
|
788
789
|
|
|
789
|
-
// src/runs/shared/herdr-machine.ts
|
|
790
|
-
import { spawnSync } from "node:child_process";
|
|
791
|
-
import * as fs from "node:fs";
|
|
792
|
-
import * as path from "node:path";
|
|
793
|
-
var MAX_MACHINE_NAME_LENGTH = 128;
|
|
794
|
-
var HERDR_MACHINE_LIST_TIMEOUT_MS = 7500;
|
|
795
|
-
var MAX_HERDR_MACHINE_LIST_BYTES = 1024 * 1024;
|
|
796
|
-
var CONTROL_CHARS = /[\u0000-\u001f\u007f]/u;
|
|
797
|
-
var SUPPORTED_MACHINE_ADAPTERS = new Set(CODE_OWNED_EXTERNAL_CLI_ADAPTER_IDS);
|
|
798
|
-
function validateMachineName(value) {
|
|
799
|
-
const trimmed = value.trim();
|
|
800
|
-
if (!trimmed) throw new Error("Herdr machine id or label is required.");
|
|
801
|
-
if (trimmed.length > MAX_MACHINE_NAME_LENGTH) throw new Error(`Herdr machine '${trimmed.slice(0, 24)}\u2026' exceeds ${MAX_MACHINE_NAME_LENGTH} characters.`);
|
|
802
|
-
if (CONTROL_CHARS.test(trimmed)) throw new Error("Herdr machine id or label contains control characters.");
|
|
803
|
-
return trimmed;
|
|
804
|
-
}
|
|
805
|
-
function validateTarget(value, requested) {
|
|
806
|
-
const target = value.trim();
|
|
807
|
-
if (!target || target.startsWith("-") || /[\s\u0000-\u001f\u007f]/u.test(target)) {
|
|
808
|
-
throw new Error(`Herdr machine '${requested}' has an ssh target that cannot be passed safely: ${JSON.stringify(target)}.`);
|
|
809
|
-
}
|
|
810
|
-
return target;
|
|
811
|
-
}
|
|
812
|
-
function isRemoteAbsolute(value) {
|
|
813
|
-
return value.startsWith("/") || value === "~" || value.startsWith("~/");
|
|
814
|
-
}
|
|
815
|
-
function validateRemoteCwd(value, requested) {
|
|
816
|
-
const cwd = value.trim();
|
|
817
|
-
if (!isRemoteAbsolute(cwd)) throw new Error(`Herdr machine '${requested}' cwd must be an absolute POSIX path or start with '~': ${JSON.stringify(cwd)}.`);
|
|
818
|
-
if (CONTROL_CHARS.test(cwd)) throw new Error(`Herdr machine '${requested}' cwd contains control characters.`);
|
|
819
|
-
return cwd.length > 1 ? cwd.replace(/\/+$/u, "") : cwd;
|
|
820
|
-
}
|
|
821
|
-
function parseMachineCatalog(json) {
|
|
822
|
-
let parsed;
|
|
823
|
-
try {
|
|
824
|
-
parsed = JSON.parse(json);
|
|
825
|
-
} catch (error) {
|
|
826
|
-
throw new Error(`Failed to parse herdr machine list --json: ${error instanceof Error ? error.message : String(error)}`);
|
|
827
|
-
}
|
|
828
|
-
const entries = Array.isArray(parsed) ? parsed : parsed && typeof parsed === "object" && Array.isArray(parsed.machines) ? parsed.machines : void 0;
|
|
829
|
-
if (!entries) throw new Error("herdr machine list --json returned no machine list.");
|
|
830
|
-
return entries.flatMap((entry) => {
|
|
831
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return [];
|
|
832
|
-
const record2 = entry;
|
|
833
|
-
if (typeof record2.id !== "string" || !record2.id.trim() || typeof record2.target !== "string") return [];
|
|
834
|
-
return [{
|
|
835
|
-
id: record2.id.trim(),
|
|
836
|
-
...typeof record2.label === "string" && record2.label.trim() ? { label: record2.label.trim() } : {},
|
|
837
|
-
target: record2.target,
|
|
838
|
-
...typeof record2.session === "string" && record2.session.trim() ? { session: record2.session.trim() } : {},
|
|
839
|
-
enabled: record2.enabled !== false
|
|
840
|
-
}];
|
|
841
|
-
});
|
|
842
|
-
}
|
|
843
|
-
function readHerdrMachineCatalog(env, herdrBin) {
|
|
844
|
-
const result2 = spawnSync(herdrBin, ["machine", "list", "--json"], {
|
|
845
|
-
env,
|
|
846
|
-
encoding: "utf-8",
|
|
847
|
-
maxBuffer: MAX_HERDR_MACHINE_LIST_BYTES,
|
|
848
|
-
timeout: HERDR_MACHINE_LIST_TIMEOUT_MS,
|
|
849
|
-
windowsHide: true
|
|
850
|
-
});
|
|
851
|
-
if (result2.error) {
|
|
852
|
-
const code = result2.error.code;
|
|
853
|
-
if (code === "ENOENT") throw new Error(`Herdr CLI '${herdrBin}' was not found on PATH. Saved-machine placement needs Herdr installed locally.`);
|
|
854
|
-
throw new Error(`Failed to run herdr machine list --json: ${result2.error.message}`);
|
|
855
|
-
}
|
|
856
|
-
if (result2.status !== 0) throw new Error(`herdr machine list --json exited with code ${result2.status}: ${(result2.stderr || result2.stdout).trim()}`);
|
|
857
|
-
return parseMachineCatalog(result2.stdout);
|
|
858
|
-
}
|
|
859
|
-
function selectMachine(catalog, requested) {
|
|
860
|
-
const byId = catalog.find((entry) => entry.id === requested);
|
|
861
|
-
const matches = byId ? [byId] : catalog.filter((entry) => entry.label === requested);
|
|
862
|
-
if (matches.length > 1) throw new Error(`Machine label '${requested}' is ambiguous; use its profile ID.`);
|
|
863
|
-
const machine = matches[0];
|
|
864
|
-
if (!machine) {
|
|
865
|
-
const saved = catalog.filter((entry) => entry.enabled).map((entry) => entry.label ?? entry.id);
|
|
866
|
-
throw new Error(`Herdr machine '${requested}' was not found. Saved machines: ${saved.length ? saved.join(", ") : "none"}. Add one with herdr machine add <target> --label <name>.`);
|
|
867
|
-
}
|
|
868
|
-
if (!machine.enabled) throw new Error(`Machine '${requested}' is disabled. Run herdr machine enable ${machine.id}.`);
|
|
869
|
-
return machine;
|
|
870
|
-
}
|
|
871
|
-
function readJsonObject(filePath) {
|
|
872
|
-
if (!fs.existsSync(filePath)) return {};
|
|
873
|
-
let parsed;
|
|
874
|
-
try {
|
|
875
|
-
parsed = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
876
|
-
} catch (error) {
|
|
877
|
-
throw new Error(`Failed to read settings file '${filePath}': ${error instanceof Error ? error.message : String(error)}`);
|
|
878
|
-
}
|
|
879
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
880
|
-
}
|
|
881
|
-
function machineSettingsFrom(settings, keys, filePath) {
|
|
882
|
-
const subagents = settings.subagents;
|
|
883
|
-
if (!subagents || typeof subagents !== "object" || Array.isArray(subagents)) return void 0;
|
|
884
|
-
const machines = subagents.machines;
|
|
885
|
-
if (machines === void 0) return void 0;
|
|
886
|
-
if (!machines || typeof machines !== "object" || Array.isArray(machines)) throw new Error(`Subagent settings in '${filePath}' have invalid 'machines'; expected an object keyed by machine label or id.`);
|
|
887
|
-
const key = keys.find((candidate) => candidate in machines);
|
|
888
|
-
if (key === void 0) return void 0;
|
|
889
|
-
const value = machines[key];
|
|
890
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`Subagent settings in '${filePath}' have invalid 'machines.${key}'; expected an object with 'cwd' and optional 'env'.`);
|
|
891
|
-
const record2 = value;
|
|
892
|
-
const entry = {};
|
|
893
|
-
if (record2.cwd !== void 0) {
|
|
894
|
-
if (typeof record2.cwd !== "string" || !record2.cwd.trim()) throw new Error(`Subagent settings in '${filePath}' have invalid 'machines.${key}.cwd'; expected a non-empty string.`);
|
|
895
|
-
entry.cwd = validateRemoteCwd(record2.cwd, key);
|
|
896
|
-
}
|
|
897
|
-
if (record2.env !== void 0) {
|
|
898
|
-
if (!record2.env || typeof record2.env !== "object" || Array.isArray(record2.env) || Object.entries(record2.env).some(([name, item]) => !/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name) || typeof item !== "string")) {
|
|
899
|
-
throw new Error(`Subagent settings in '${filePath}' have invalid 'machines.${key}.env'; expected an object of string values keyed by variable name.`);
|
|
900
|
-
}
|
|
901
|
-
entry.env = { ...record2.env };
|
|
902
|
-
if (Object.keys(entry.env).length > 0) {
|
|
903
|
-
throw new Error(`Subagent settings in '${filePath}' set 'machines.${key}.env'. Saved-machine runs use the remote Herdr/Pi environment; configure credentials and environment on '${key}' and remove the local env map.`);
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
if (record2.rex !== void 0) {
|
|
907
|
-
const rex = record2.rex, ssh = rex && typeof rex === "object" && !Array.isArray(rex) && Object.keys(rex).every((field) => field === "ssh") ? rex.ssh : void 0;
|
|
908
|
-
if (typeof ssh !== "string") throw new Error(`Subagent settings in '${filePath}' have invalid 'machines.${key}.rex'; expected { "ssh": "<ssh target>" }.`);
|
|
909
|
-
entry.rex = { ssh: validateTarget(ssh, key) };
|
|
910
|
-
}
|
|
911
|
-
return entry;
|
|
912
|
-
}
|
|
913
|
-
function readMachineSettings(cwd, keys) {
|
|
914
|
-
const userPath = path.join(getAgentDir(), "settings.json");
|
|
915
|
-
const projectPath = path.join(getProjectConfigDir(findProjectRootForSettings(cwd)), "settings.json");
|
|
916
|
-
const user = machineSettingsFrom(readJsonObject(userPath), keys, userPath);
|
|
917
|
-
const project = projectPath === userPath ? void 0 : machineSettingsFrom(readJsonObject(projectPath), keys, projectPath);
|
|
918
|
-
if (project?.rex) throw new Error(`Project settings '${projectPath}' set a Rex SSH host in 'machines'. Move the rex entry to your user settings '${userPath}'; only user settings choose which host a task runs on.`);
|
|
919
|
-
if (!user && !project) return void 0;
|
|
920
|
-
return { ...user, ...project };
|
|
921
|
-
}
|
|
922
|
-
function userRexMachine(machine) {
|
|
923
|
-
const userPath = path.join(getAgentDir(), "settings.json");
|
|
924
|
-
return machineSettingsFrom(readJsonObject(userPath), [machine], userPath)?.rex !== void 0;
|
|
925
|
-
}
|
|
926
|
-
function findProjectRootForSettings(cwd) {
|
|
927
|
-
let current = path.resolve(cwd);
|
|
928
|
-
while (true) {
|
|
929
|
-
if (fs.existsSync(path.join(getProjectConfigDir(current), "settings.json"))) return current;
|
|
930
|
-
const parent = path.dirname(current);
|
|
931
|
-
if (parent === current) return path.resolve(cwd);
|
|
932
|
-
current = parent;
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
function remoteMachineCwd(stepCwd, root, name) {
|
|
936
|
-
const step = stepCwd?.trim();
|
|
937
|
-
if (step && isRemoteAbsolute(step)) return step;
|
|
938
|
-
if (!root) throw new Error(`No root for ${name} in this repo. Set subagents.machines.${name}.cwd in .pi/settings.json or pass an absolute cwd on that machine.`);
|
|
939
|
-
return step ? path.posix.join(root, step) : root;
|
|
940
|
-
}
|
|
941
|
-
function resolveHerdrMachinePlacement(input) {
|
|
942
|
-
const requested = validateMachineName(input.machine);
|
|
943
|
-
const catalog = input.catalogJson !== void 0 ? parseMachineCatalog(input.catalogJson) : readHerdrMachineCatalog(input.env ?? process.env, input.herdrBin ?? process.env.HERDR_BIN ?? "herdr");
|
|
944
|
-
const selected = selectMachine(catalog, requested);
|
|
945
|
-
const name = selected.label ?? selected.id;
|
|
946
|
-
const keys = [.../* @__PURE__ */ new Set([requested, ...selected.label ? [selected.label] : [], selected.id])];
|
|
947
|
-
const settings = input.settings ?? readMachineSettings(input.cwd, keys);
|
|
948
|
-
if (settings?.env && Object.keys(settings.env).length > 0) {
|
|
949
|
-
throw new Error(`Saved-machine environment for '${name}' must be configured remotely. Remove machines.${name}.env and configure the remote Herdr/Pi session instead.`);
|
|
950
|
-
}
|
|
951
|
-
const cwd = remoteMachineCwd(input.stepCwd, settings?.cwd, name);
|
|
952
|
-
return {
|
|
953
|
-
machine: {
|
|
954
|
-
provider: "herdr",
|
|
955
|
-
id: selected.id,
|
|
956
|
-
...selected.label ? { label: selected.label } : {},
|
|
957
|
-
target: validateTarget(selected.target, requested),
|
|
958
|
-
...selected.session && selected.session !== "default" ? { session: selected.session } : {},
|
|
959
|
-
cwd: validateRemoteCwd(cwd, requested)
|
|
960
|
-
},
|
|
961
|
-
...settings?.env ? { env: settings.env } : {}
|
|
962
|
-
};
|
|
963
|
-
}
|
|
964
|
-
function resolveMachinePlacement(input) {
|
|
965
|
-
const requested = validateMachineName(input.machine);
|
|
966
|
-
const settings = input.settings ?? readMachineSettings(input.cwd, [requested]);
|
|
967
|
-
if (requested === REX_LOCAL_MACHINE) {
|
|
968
|
-
if (settings?.rex) throw new Error("Machine 'local' is reserved for a Rex pane on this machine; give the Rex SSH host in subagents.machines.local.rex another name.");
|
|
969
|
-
return { machine: resolveRexLocalPlacement({ cwd: input.cwd, stepCwd: input.stepCwd, env: input.env }) };
|
|
970
|
-
}
|
|
971
|
-
if (settings?.rex) return { machine: { provider: "rex", id: requested, target: settings.rex.ssh, cwd: validateRemoteCwd(remoteMachineCwd(input.stepCwd, settings.cwd, requested), requested) } };
|
|
972
|
-
return resolveHerdrMachinePlacement(input);
|
|
973
|
-
}
|
|
974
|
-
function formatHerdrMachineRunnerUnsupported(input) {
|
|
975
|
-
if (input.machine === void 0) return void 0;
|
|
976
|
-
const name = input.machine.trim();
|
|
977
|
-
let rex;
|
|
978
|
-
try {
|
|
979
|
-
rex = name === REX_LOCAL_MACHINE || userRexMachine(name);
|
|
980
|
-
} catch (error) {
|
|
981
|
-
return error instanceof Error ? error.message : String(error);
|
|
982
|
-
}
|
|
983
|
-
if (rex) {
|
|
984
|
-
if (input.runnerType !== void 0 && input.runnerType !== "pi") return `Agent '${input.agentName}' requested machine '${name}', but Rex supports native Pi children only. Remove the machine or use a native Pi agent.`;
|
|
985
|
-
if (input.worktree === true) return `Agent '${input.agentName}' requested machine '${name}', but managed worktrees cannot be combined with machine placement.`;
|
|
986
|
-
if (process.platform === "win32") return `Machine '${name}' runs the child in a Rex pane, which is not supported on Windows.`;
|
|
987
|
-
return void 0;
|
|
988
|
-
}
|
|
989
|
-
if (input.runnerType !== void 0 && input.runnerType !== "pi" && input.runnerType !== "external-cli") {
|
|
990
|
-
return `Agent '${input.agentName}' requested machine '${input.machine}', but this runner cannot use pane-native Herdr placement. Use native Pi or a built-in Claude, Codex, or Cursor profile.`;
|
|
991
|
-
}
|
|
992
|
-
if (input.runnerType === "external-cli" && (input.adapter === void 0 || !SUPPORTED_MACHINE_ADAPTERS.has(input.adapter))) {
|
|
993
|
-
return `Agent '${input.agentName}' requested machine '${input.machine}', but generic external-cli commands cannot be remote-wrapped safely. Use claude-code, claude-code-writer, codex-exec, codex-exec-writer, cursor-agent, or cursor-agent-writer.`;
|
|
994
|
-
}
|
|
995
|
-
if (input.worktree === true) return `Agent '${input.agentName}' requested machine '${input.machine}', but managed worktrees are local git operations and cannot be combined with a Herdr saved machine.`;
|
|
996
|
-
if (process.platform === "win32") return "Herdr saved-machine pane transport requires hardened OpenSSH StreamLocal forwarding, which is not supported from a Windows host yet.";
|
|
997
|
-
return void 0;
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
790
|
// src/runs/shared/launch-cwd.ts
|
|
1001
|
-
import * as
|
|
791
|
+
import * as fs from "node:fs";
|
|
1002
792
|
function preflightLaunchCwd(requestedCwd, effectiveCwd) {
|
|
1003
793
|
const resolution = requestedCwd === effectiveCwd ? "" : `
|
|
1004
794
|
(resolved from ${JSON.stringify(requestedCwd)})`;
|
|
1005
795
|
try {
|
|
1006
|
-
if (!
|
|
796
|
+
if (!fs.statSync(effectiveCwd).isDirectory()) {
|
|
1007
797
|
return `Subagent launch aborted: cwd is not a directory: ${effectiveCwd}${resolution}`;
|
|
1008
798
|
}
|
|
1009
799
|
} catch (error) {
|
|
@@ -1025,23 +815,23 @@ function backgroundProcessOptions(platform = process.platform) {
|
|
|
1025
815
|
}
|
|
1026
816
|
|
|
1027
817
|
// src/runs/shared/child-launch.ts
|
|
1028
|
-
import * as
|
|
1029
|
-
import * as
|
|
818
|
+
import * as fs3 from "node:fs";
|
|
819
|
+
import * as path2 from "node:path";
|
|
1030
820
|
|
|
1031
821
|
// src/extension/fanout-child.ts
|
|
1032
|
-
import * as
|
|
822
|
+
import * as fs2 from "node:fs";
|
|
1033
823
|
import * as os from "node:os";
|
|
1034
|
-
import * as
|
|
824
|
+
import * as path from "node:path";
|
|
1035
825
|
function getSubagentSessionRoot(parentSessionFile) {
|
|
1036
826
|
if (parentSessionFile) {
|
|
1037
|
-
const baseName =
|
|
1038
|
-
const sessionsDir =
|
|
1039
|
-
return
|
|
827
|
+
const baseName = path.basename(parentSessionFile, ".jsonl");
|
|
828
|
+
const sessionsDir = path.dirname(parentSessionFile);
|
|
829
|
+
return path.join(sessionsDir, baseName);
|
|
1040
830
|
}
|
|
1041
|
-
return
|
|
831
|
+
return fs2.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-session-"));
|
|
1042
832
|
}
|
|
1043
833
|
function expandTilde(p) {
|
|
1044
|
-
return p.startsWith("~/") ?
|
|
834
|
+
return p.startsWith("~/") ? path.join(os.homedir(), p.slice(2)) : p;
|
|
1045
835
|
}
|
|
1046
836
|
function createChildSafeState() {
|
|
1047
837
|
return {
|
|
@@ -1127,7 +917,7 @@ ${request.message.trim()}`,
|
|
|
1127
917
|
inboxState.pendingResults.delete(request.requestId);
|
|
1128
918
|
inboxState.seen.add(request.requestId);
|
|
1129
919
|
try {
|
|
1130
|
-
|
|
920
|
+
fs2.unlinkSync(request.filePath);
|
|
1131
921
|
} catch {
|
|
1132
922
|
}
|
|
1133
923
|
} finally {
|
|
@@ -1413,12 +1203,12 @@ function childProcessEnv(input, toolPlan) {
|
|
|
1413
1203
|
}
|
|
1414
1204
|
function childStorage(input) {
|
|
1415
1205
|
if (input.sessionFile) {
|
|
1416
|
-
|
|
1206
|
+
fs3.mkdirSync(path2.dirname(input.sessionFile), { recursive: true });
|
|
1417
1207
|
return { kind: "file", sessionFile: input.sessionFile };
|
|
1418
1208
|
}
|
|
1419
1209
|
if (!input.sessionEnabled) return { kind: "memory" };
|
|
1420
1210
|
if (input.sessionDir) {
|
|
1421
|
-
|
|
1211
|
+
fs3.mkdirSync(input.sessionDir, { recursive: true });
|
|
1422
1212
|
return { kind: "dir", sessionDir: input.sessionDir };
|
|
1423
1213
|
}
|
|
1424
1214
|
return { kind: "default" };
|
|
@@ -1472,8 +1262,8 @@ function buildInProcessChildLaunch(input) {
|
|
|
1472
1262
|
let supervisorDir;
|
|
1473
1263
|
if (input.orchestratorIntercomTarget && input.parentSessionId && input.runId) {
|
|
1474
1264
|
supervisorDir = supervisorChannelDir(input.runId, input.childAgentName, input.childIndex);
|
|
1475
|
-
|
|
1476
|
-
|
|
1265
|
+
fs3.mkdirSync(path2.join(supervisorDir, "requests"), { recursive: true });
|
|
1266
|
+
fs3.mkdirSync(path2.join(supervisorDir, "replies"), { recursive: true });
|
|
1477
1267
|
}
|
|
1478
1268
|
const thinkingCeiling = intersectThinkingCeilings(input.thinkingCeiling, inherited?.thinkingCeiling);
|
|
1479
1269
|
let structuredValue;
|
|
@@ -1558,7 +1348,8 @@ ${input.systemPrompt}` : void 0;
|
|
|
1558
1348
|
requiredExtensions: toolPlan.requiredExtensions,
|
|
1559
1349
|
ambientExtensions,
|
|
1560
1350
|
hooks: capturedHooks.hooks,
|
|
1561
|
-
|
|
1351
|
+
// A foreground child gets only its own bindings, so it never sees those of the session that launched it.
|
|
1352
|
+
...input.host === "runner" ? { processEnv: childProcessEnv(input, toolPlan) } : { processEnv: { [PI_SUBAGENT_EXTENSION_BINDINGS_ENV]: encodeExtensionBindings(input.extensionBindings) }, restoreProcessEnv: true },
|
|
1562
1353
|
runtime: config,
|
|
1563
1354
|
noSkills: !input.inheritSkills,
|
|
1564
1355
|
noContextFiles: !input.inheritProjectContext,
|
|
@@ -1642,14 +1433,14 @@ function usageBudgetExceededMessage(state) {
|
|
|
1642
1433
|
}
|
|
1643
1434
|
|
|
1644
1435
|
// src/runs/background/runner-startup-failure.ts
|
|
1645
|
-
import * as
|
|
1646
|
-
import * as
|
|
1436
|
+
import * as fs4 from "node:fs";
|
|
1437
|
+
import * as path3 from "node:path";
|
|
1647
1438
|
function persistRunnerStartupFailure(input) {
|
|
1648
1439
|
const now = Date.now();
|
|
1649
|
-
const statusPath =
|
|
1440
|
+
const statusPath = path3.join(input.asyncDir, "status.json");
|
|
1650
1441
|
let status = {};
|
|
1651
1442
|
try {
|
|
1652
|
-
status = JSON.parse(
|
|
1443
|
+
status = JSON.parse(fs4.readFileSync(statusPath, "utf-8"));
|
|
1653
1444
|
} catch (error) {
|
|
1654
1445
|
if (error.code !== "ENOENT") throw error;
|
|
1655
1446
|
}
|
|
@@ -1671,7 +1462,7 @@ function persistRunnerStartupFailure(input) {
|
|
|
1671
1462
|
});
|
|
1672
1463
|
const existingCandidate = readProcessTerminalCandidate(input.asyncDir);
|
|
1673
1464
|
const stepCount = Math.max(1, status.steps?.length ?? 0);
|
|
1674
|
-
writePrivateAtomicJson(
|
|
1465
|
+
writePrivateAtomicJson(path3.join(input.asyncDir, "process-terminal-candidate.json"), {
|
|
1675
1466
|
...existingCandidate,
|
|
1676
1467
|
version: 1,
|
|
1677
1468
|
runId: input.runId,
|
|
@@ -1685,19 +1476,19 @@ function persistRunnerStartupFailure(input) {
|
|
|
1685
1476
|
|
|
1686
1477
|
// src/runs/background/chain-append.ts
|
|
1687
1478
|
import { randomUUID } from "node:crypto";
|
|
1688
|
-
import * as
|
|
1689
|
-
import * as
|
|
1479
|
+
import * as fs5 from "node:fs";
|
|
1480
|
+
import * as path4 from "node:path";
|
|
1690
1481
|
var APPEND_REQUESTS_DIR = "append-requests";
|
|
1691
1482
|
function appendDir(asyncDir) {
|
|
1692
|
-
return
|
|
1483
|
+
return path4.join(asyncDir, APPEND_REQUESTS_DIR);
|
|
1693
1484
|
}
|
|
1694
1485
|
function appendRequestPath(asyncDir, request) {
|
|
1695
|
-
return
|
|
1486
|
+
return path4.join(appendDir(asyncDir), `${request.createdAt}-${request.id}.json`);
|
|
1696
1487
|
}
|
|
1697
1488
|
function listAppendRequestFiles(asyncDir) {
|
|
1698
1489
|
const dir = appendDir(asyncDir);
|
|
1699
1490
|
try {
|
|
1700
|
-
return
|
|
1491
|
+
return fs5.readdirSync(dir).filter((entry) => entry.endsWith(".json")).map((entry) => path4.join(dir, entry)).sort();
|
|
1701
1492
|
} catch (error) {
|
|
1702
1493
|
if (error.code === "ENOENT") return [];
|
|
1703
1494
|
throw error;
|
|
@@ -1733,7 +1524,7 @@ function enqueueChainAppendRequest(input) {
|
|
|
1733
1524
|
createdAt: input.now ?? Date.now(),
|
|
1734
1525
|
steps: input.steps
|
|
1735
1526
|
};
|
|
1736
|
-
|
|
1527
|
+
fs5.mkdirSync(appendDir(input.asyncDir), { recursive: true });
|
|
1737
1528
|
const persist = () => writeAtomicJson(appendRequestPath(input.asyncDir, request), request);
|
|
1738
1529
|
if (input.admit) input.admit(persist);
|
|
1739
1530
|
else persist();
|
|
@@ -1744,7 +1535,7 @@ function enqueueChainAppendRequest(input) {
|
|
|
1744
1535
|
} catch (error) {
|
|
1745
1536
|
bookkeepingErrors.push(`pending append count failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1746
1537
|
}
|
|
1747
|
-
const statusPath =
|
|
1538
|
+
const statusPath = path4.join(input.asyncDir, "status.json");
|
|
1748
1539
|
const updatedStatus = { ...status, pendingAppends: pendingCount, lastUpdate: request.createdAt };
|
|
1749
1540
|
try {
|
|
1750
1541
|
writeAtomicJson(statusPath, updatedStatus);
|
|
@@ -1752,7 +1543,7 @@ function enqueueChainAppendRequest(input) {
|
|
|
1752
1543
|
bookkeepingErrors.push(`status update failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1753
1544
|
}
|
|
1754
1545
|
try {
|
|
1755
|
-
appendJsonl(
|
|
1546
|
+
appendJsonl(path4.join(input.asyncDir, "events.jsonl"), JSON.stringify({
|
|
1756
1547
|
type: "subagent.chain.append.requested",
|
|
1757
1548
|
ts: request.createdAt,
|
|
1758
1549
|
runId: input.runId,
|
|
@@ -1766,7 +1557,7 @@ function enqueueChainAppendRequest(input) {
|
|
|
1766
1557
|
return { request, pendingCount, ...bookkeepingErrors.length > 0 ? { bookkeepingError: bookkeepingErrors.join("; ") } : {} };
|
|
1767
1558
|
}
|
|
1768
1559
|
function readAppendRequest(filePath) {
|
|
1769
|
-
const raw = JSON.parse(
|
|
1560
|
+
const raw = JSON.parse(fs5.readFileSync(filePath, "utf-8"));
|
|
1770
1561
|
if (!raw.id || typeof raw.id !== "string") return void 0;
|
|
1771
1562
|
const createdAt = raw.createdAt;
|
|
1772
1563
|
if (typeof createdAt !== "number" || !Number.isFinite(createdAt)) return void 0;
|
|
@@ -1813,28 +1604,28 @@ var piPackageSelection = (() => {
|
|
|
1813
1604
|
})();
|
|
1814
1605
|
var piPackageRoot = piPackageSelection.root;
|
|
1815
1606
|
function resolveJitiCliFromPackageJson(packageJsonPath) {
|
|
1816
|
-
if (!
|
|
1817
|
-
const packageRoot =
|
|
1818
|
-
const pkg = JSON.parse(
|
|
1607
|
+
if (!fs6.existsSync(packageJsonPath)) return void 0;
|
|
1608
|
+
const packageRoot = path5.dirname(packageJsonPath);
|
|
1609
|
+
const pkg = JSON.parse(fs6.readFileSync(packageJsonPath, "utf-8"));
|
|
1819
1610
|
const binField = pkg.bin;
|
|
1820
1611
|
const binPath = typeof binField === "string" ? binField : binField?.jiti ?? Object.values(binField ?? {})[0];
|
|
1821
1612
|
const candidates = [binPath, "lib/jiti-cli.mjs"].filter((candidate) => Boolean(candidate));
|
|
1822
1613
|
for (const candidate of candidates) {
|
|
1823
|
-
const cliPath =
|
|
1824
|
-
if (
|
|
1614
|
+
const cliPath = path5.resolve(packageRoot, candidate);
|
|
1615
|
+
if (fs6.existsSync(cliPath)) return cliPath;
|
|
1825
1616
|
}
|
|
1826
1617
|
return void 0;
|
|
1827
1618
|
}
|
|
1828
1619
|
function resolveJitiCliPath() {
|
|
1829
1620
|
const candidates = [
|
|
1830
1621
|
() => require2.resolve("jiti/package.json"),
|
|
1831
|
-
() => piPackageRoot ? nodeModule.createRequire(
|
|
1622
|
+
() => piPackageRoot ? nodeModule.createRequire(path5.join(piPackageRoot, "package.json")).resolve("jiti/package.json") : void 0,
|
|
1832
1623
|
() => {
|
|
1833
1624
|
if (!process.argv[1]) return void 0;
|
|
1834
|
-
const piEntry =
|
|
1625
|
+
const piEntry = fs6.realpathSync(process.argv[1]);
|
|
1835
1626
|
return nodeModule.createRequire(piEntry).resolve("jiti/package.json");
|
|
1836
1627
|
},
|
|
1837
|
-
() => piPackageRoot ?
|
|
1628
|
+
() => piPackageRoot ? path5.join(piPackageRoot, "node_modules", "jiti", "package.json") : void 0
|
|
1838
1629
|
];
|
|
1839
1630
|
for (const candidate of candidates) {
|
|
1840
1631
|
try {
|
|
@@ -1848,12 +1639,12 @@ function resolveJitiCliPath() {
|
|
|
1848
1639
|
return void 0;
|
|
1849
1640
|
}
|
|
1850
1641
|
var jitiCliPath = resolveJitiCliPath();
|
|
1851
|
-
var asyncRunnerModuleDir =
|
|
1642
|
+
var asyncRunnerModuleDir = path5.dirname(fileURLToPath(new URL("../src/runs/background/async-execution.js", import.meta.url).href));
|
|
1852
1643
|
function asyncRunnerSourcePath() {
|
|
1853
1644
|
return resolveRuntimeModulePath(asyncRunnerModuleDir, "subagent-runner-bootstrap");
|
|
1854
1645
|
}
|
|
1855
1646
|
function sourceUnderNodeModules() {
|
|
1856
|
-
return asyncRunnerSourcePath().split(
|
|
1647
|
+
return asyncRunnerSourcePath().split(path5.sep).some((segment) => segment.toLowerCase() === "node_modules");
|
|
1857
1648
|
}
|
|
1858
1649
|
function supportsNativeRunner(nodeExecutable) {
|
|
1859
1650
|
return Boolean(process.features.typescript) && typeof nodeModule.registerHooks === "function" && nodeExecutable === process.execPath && !sourceUnderNodeModules();
|
|
@@ -1875,25 +1666,25 @@ function resolveAsyncRunnerLogPaths(cfg) {
|
|
|
1875
1666
|
const asyncDir = typeof cfg.asyncDir === "string" ? cfg.asyncDir : void 0;
|
|
1876
1667
|
if (!asyncDir) return void 0;
|
|
1877
1668
|
return {
|
|
1878
|
-
stdoutPath:
|
|
1879
|
-
stderrPath:
|
|
1669
|
+
stdoutPath: path5.join(asyncDir, "runner.stdout.log"),
|
|
1670
|
+
stderrPath: path5.join(asyncDir, "runner.stderr.log")
|
|
1880
1671
|
};
|
|
1881
1672
|
}
|
|
1882
1673
|
function closeFd(fd) {
|
|
1883
1674
|
if (fd === void 0) return;
|
|
1884
1675
|
try {
|
|
1885
|
-
|
|
1676
|
+
fs6.closeSync(fd);
|
|
1886
1677
|
} catch {
|
|
1887
1678
|
}
|
|
1888
1679
|
}
|
|
1889
1680
|
var RUNNER_STARTUP_TIMEOUT_MS = 1e4;
|
|
1890
1681
|
function waitForStartupInterval(delayMs = 20) {
|
|
1891
|
-
return new Promise((
|
|
1682
|
+
return new Promise((resolve9) => setTimeout(resolve9, delayMs));
|
|
1892
1683
|
}
|
|
1893
1684
|
function readRunnerStartup(startupPath, expectedState, expectedToken) {
|
|
1894
|
-
if (!
|
|
1685
|
+
if (!fs6.existsSync(startupPath)) return void 0;
|
|
1895
1686
|
try {
|
|
1896
|
-
const payload = JSON.parse(
|
|
1687
|
+
const payload = JSON.parse(fs6.readFileSync(startupPath, "utf-8"));
|
|
1897
1688
|
if (payload.state === "error" && typeof payload.error === "string") return { ok: false, error: payload.error, startupDidNotProceed: true };
|
|
1898
1689
|
if (payload.state !== expectedState) return void 0;
|
|
1899
1690
|
if (typeof payload.token !== "string" || expectedToken !== void 0 && payload.token !== expectedToken) {
|
|
@@ -1907,7 +1698,7 @@ function readRunnerStartup(startupPath, expectedState, expectedToken) {
|
|
|
1907
1698
|
async function waitForRunnerStartup(startupPath, expectedState, timeoutMs, expectedToken, processClosed) {
|
|
1908
1699
|
const confirmOpen = async (result2) => {
|
|
1909
1700
|
if (!processClosed || result2.ok === false) return result2;
|
|
1910
|
-
const outcome = await Promise.race([processClosed.then((exit) => ({ exit })), new Promise((
|
|
1701
|
+
const outcome = await Promise.race([processClosed.then((exit) => ({ exit })), new Promise((resolve9) => setImmediate(() => resolve9(void 0)))]);
|
|
1911
1702
|
if (!outcome) return result2;
|
|
1912
1703
|
const finalResult2 = readRunnerStartup(startupPath, expectedState, expectedToken);
|
|
1913
1704
|
if (finalResult2?.ok === false) return finalResult2;
|
|
@@ -2022,7 +1813,7 @@ async function completeRunnerStartupHandshake(startupPath, startupAckPath, start
|
|
|
2022
1813
|
}
|
|
2023
1814
|
const closedAtCommit = await Promise.race([
|
|
2024
1815
|
processClosed.then((exit) => exit),
|
|
2025
|
-
new Promise((
|
|
1816
|
+
new Promise((resolve9) => setImmediate(() => resolve9(void 0)))
|
|
2026
1817
|
]);
|
|
2027
1818
|
const observedExit = closedAtCommit ?? getObservedProcessExit();
|
|
2028
1819
|
if (observedExit) {
|
|
@@ -2040,12 +1831,12 @@ async function completeRunnerStartupHandshake(startupPath, startupAckPath, start
|
|
|
2040
1831
|
return { pid: proc.pid, runnerProcessInstanceId, error: message, terminationObserved, startupDidNotProceed: true };
|
|
2041
1832
|
}
|
|
2042
1833
|
try {
|
|
2043
|
-
|
|
1834
|
+
fs6.rmSync(startupPath, { force: true });
|
|
2044
1835
|
} catch {
|
|
2045
1836
|
}
|
|
2046
1837
|
return { pid: proc.pid, runnerProcessInstanceId };
|
|
2047
1838
|
} finally {
|
|
2048
|
-
await new Promise((
|
|
1839
|
+
await new Promise((resolve9) => setImmediate(resolve9));
|
|
2049
1840
|
}
|
|
2050
1841
|
}
|
|
2051
1842
|
function persistPreProceedStartupFailure(asyncDir, runId, runnerProcessInstanceId, sessionId, completionOwnerId, message) {
|
|
@@ -2080,9 +1871,9 @@ function spawnRunner(cfg, suffix, cwd, initialStatus, initialStatusPath, launchP
|
|
|
2080
1871
|
const nodeExecutable = resolveNodeExecutable();
|
|
2081
1872
|
const nativeRunnerSupported = supportsNativeRunner(nodeExecutable);
|
|
2082
1873
|
const runner = asyncRunnerSourcePath();
|
|
2083
|
-
const runnerIsJavaScript =
|
|
2084
|
-
const bootstrap = resolveRuntimeModulePath(
|
|
2085
|
-
if (binaryHost && !
|
|
1874
|
+
const runnerIsJavaScript = path5.extname(runner) === ".js";
|
|
1875
|
+
const bootstrap = resolveRuntimeModulePath(path5.dirname(runner), "binary-bootstrap");
|
|
1876
|
+
if (binaryHost && !fs6.existsSync(bootstrap)) return { error: `Background runner bootstrap not found: ${bootstrap}` };
|
|
2086
1877
|
if (!binaryHost && !runnerIsJavaScript && !nativeRunnerSupported && !jitiCliPath) {
|
|
2087
1878
|
return { error: sourceUnderNodeModules() ? "Background runner source is installed under node_modules, so upstream jiti is required for TypeScript execution." : "Background runner requires enabled native TypeScript with synchronous module hooks, or installed upstream jiti for TypeScript execution." };
|
|
2088
1879
|
}
|
|
@@ -2093,7 +1884,7 @@ function spawnRunner(cfg, suffix, cwd, initialStatus, initialStatusPath, launchP
|
|
|
2093
1884
|
if (hostPeerAliases.missing.length > 0) {
|
|
2094
1885
|
return { error: `Background children require the host npm package (${PI_CODING_AGENT_PACKAGE}) with its dependencies; ${piPackageRoot} does not provide ${hostPeerAliases.missing.join(", ")}.` };
|
|
2095
1886
|
}
|
|
2096
|
-
|
|
1887
|
+
fs6.mkdirSync(TEMP_ROOT_DIR, { recursive: true });
|
|
2097
1888
|
const cfgPath = getAsyncConfigPath(suffix);
|
|
2098
1889
|
const runnerProcessInstanceId = randomUUID2();
|
|
2099
1890
|
const hasRevivalLease = typeof cfg.revivalLease === "object";
|
|
@@ -2108,23 +1899,23 @@ function spawnRunner(cfg, suffix, cwd, initialStatus, initialStatusPath, launchP
|
|
|
2108
1899
|
const launchRunId = typeof launchForStartup.id === "string" ? launchForStartup.id : suffix;
|
|
2109
1900
|
const launchSessionId = typeof launchForStartup.sessionId === "string" ? launchForStartup.sessionId : void 0;
|
|
2110
1901
|
const launchCompletionOwnerId = typeof launchForStartup.completionOwnerId === "string" ? launchForStartup.completionOwnerId : void 0;
|
|
2111
|
-
const startupPath = typeof launchForStartup.revivalLease === "object" && launchAsyncDir ?
|
|
2112
|
-
const startupAckPath = startupPath ?
|
|
2113
|
-
const startupConfirmPath = startupPath ?
|
|
2114
|
-
const startupProceedPath = launchAsyncDir && (startupPath || launchBarrierToken) ?
|
|
2115
|
-
if (startupPath)
|
|
2116
|
-
if (startupAckPath)
|
|
2117
|
-
if (startupConfirmPath)
|
|
2118
|
-
if (startupProceedPath)
|
|
2119
|
-
const identityPath = launcher && launchBarrierToken && launchAsyncDir ?
|
|
2120
|
-
if (identityPath)
|
|
1902
|
+
const startupPath = typeof launchForStartup.revivalLease === "object" && launchAsyncDir ? path5.join(launchAsyncDir, "runner-startup.json") : void 0;
|
|
1903
|
+
const startupAckPath = startupPath ? path5.join(path5.dirname(startupPath), "runner-startup-ack.json") : void 0;
|
|
1904
|
+
const startupConfirmPath = startupPath ? path5.join(path5.dirname(startupPath), "runner-startup-confirm.json") : void 0;
|
|
1905
|
+
const startupProceedPath = launchAsyncDir && (startupPath || launchBarrierToken) ? path5.join(launchAsyncDir, "runner-startup-proceed.json") : void 0;
|
|
1906
|
+
if (startupPath) fs6.rmSync(startupPath, { force: true });
|
|
1907
|
+
if (startupAckPath) fs6.rmSync(startupAckPath, { force: true });
|
|
1908
|
+
if (startupConfirmPath) fs6.rmSync(startupConfirmPath, { force: true });
|
|
1909
|
+
if (startupProceedPath) fs6.rmSync(startupProceedPath, { force: true });
|
|
1910
|
+
const identityPath = launcher && launchBarrierToken && launchAsyncDir ? path5.join(launchAsyncDir, "runner-startup.json") : void 0;
|
|
1911
|
+
if (identityPath) fs6.rmSync(identityPath, { force: true });
|
|
2121
1912
|
let runnerIdentity;
|
|
2122
1913
|
const terminateBeforeProceed = (pid) => !launcher ? terminateRunnerBeforeProceed(pid) : runnerIdentity !== void 0 && runnerIdentity.pidNamespaceScope === currentPidNamespaceScope() && terminateRunnerBeforeProceed(runnerIdentity.pid);
|
|
2123
1914
|
const adoptRunnerIdentity = (pid, pidNamespaceScope) => {
|
|
2124
1915
|
if (typeof pid !== "number" || !Number.isSafeInteger(pid) || pid < 1) return `Async runner reported an invalid process identity in '${launchAsyncDir}'.`;
|
|
2125
1916
|
runnerIdentity = { pid, ...typeof pidNamespaceScope === "string" ? { pidNamespaceScope } : {} };
|
|
2126
1917
|
try {
|
|
2127
|
-
const status = JSON.parse(
|
|
1918
|
+
const status = JSON.parse(fs6.readFileSync(initialStatusPath, "utf-8"));
|
|
2128
1919
|
writePrivateAtomicJson(initialStatusPath, { ...status, pid, ...runnerIdentity.pidNamespaceScope !== void 0 ? { pidNamespaceScope: runnerIdentity.pidNamespaceScope } : {} });
|
|
2129
1920
|
return void 0;
|
|
2130
1921
|
} catch (error) {
|
|
@@ -2136,9 +1927,9 @@ function spawnRunner(cfg, suffix, cwd, initialStatus, initialStatusPath, launchP
|
|
|
2136
1927
|
let stderrFd;
|
|
2137
1928
|
try {
|
|
2138
1929
|
if (logPaths) {
|
|
2139
|
-
|
|
2140
|
-
stdoutFd =
|
|
2141
|
-
stderrFd =
|
|
1930
|
+
fs6.mkdirSync(path5.dirname(logPaths.stdoutPath), { recursive: true });
|
|
1931
|
+
stdoutFd = fs6.openSync(logPaths.stdoutPath, "a");
|
|
1932
|
+
stderrFd = fs6.openSync(logPaths.stderrPath, "a");
|
|
2142
1933
|
}
|
|
2143
1934
|
const preload = Object.keys(hostPeerAliases.aliases).length > 0 ? ["--import", new URL("../../../runner-peer-preload.mjs", new URL("../src/runs/background/async-execution.js", import.meta.url).href).href] : [];
|
|
2144
1935
|
const args = binaryHost ? ["--no-extensions", "--no-skills", "--no-prompt-templates", "--no-session", "--mode", "rpc", "--extension", bootstrap] : runnerIsJavaScript ? [...preload, runner, cfgPath] : nativeRunnerSupported ? [...preload, "--experimental-strip-types", runner, cfgPath] : [...preload, jitiCliPath, runner, cfgPath];
|
|
@@ -2165,10 +1956,10 @@ function spawnRunner(cfg, suffix, cwd, initialStatus, initialStatusPath, launchP
|
|
|
2165
1956
|
proc.once("exit", (exitCode, signal) => {
|
|
2166
1957
|
observedProcessExit = { exitCode, signal };
|
|
2167
1958
|
});
|
|
2168
|
-
const processClosed = new Promise((
|
|
1959
|
+
const processClosed = new Promise((resolve9) => {
|
|
2169
1960
|
proc.once("close", (exitCode, signal) => {
|
|
2170
1961
|
observedProcessExit ??= { exitCode, signal };
|
|
2171
|
-
|
|
1962
|
+
resolve9({ exitCode, signal });
|
|
2172
1963
|
});
|
|
2173
1964
|
});
|
|
2174
1965
|
closeFd(stdoutFd);
|
|
@@ -2184,7 +1975,7 @@ function spawnRunner(cfg, suffix, cwd, initialStatus, initialStatusPath, launchP
|
|
|
2184
1975
|
if (typeof asyncDir !== "string" || typeof runId !== "string") return;
|
|
2185
1976
|
if (launcher) {
|
|
2186
1977
|
try {
|
|
2187
|
-
writePrivateAtomicJson(
|
|
1978
|
+
writePrivateAtomicJson(path5.join(asyncDir, "launcher-close.json"), { version: 1, runId, runnerProcessInstanceId, closeObservedAt: Date.now(), exitCode, signal, ...runnerIdentity ? { runnerPid: runnerIdentity.pid } : {} });
|
|
2188
1979
|
} catch {
|
|
2189
1980
|
}
|
|
2190
1981
|
if (!runnerIdentity || runnerIdentity.pidNamespaceScope !== currentPidNamespaceScope() || checkRecordedPidLiveness(runnerIdentity) !== "dead") return;
|
|
@@ -2201,7 +1992,7 @@ function spawnRunner(cfg, suffix, cwd, initialStatus, initialStatusPath, launchP
|
|
|
2201
1992
|
try {
|
|
2202
1993
|
let status;
|
|
2203
1994
|
try {
|
|
2204
|
-
status = JSON.parse(
|
|
1995
|
+
status = JSON.parse(fs6.readFileSync(path5.join(asyncDir, "status.json"), "utf-8"));
|
|
2205
1996
|
status.processTerminal = persisted;
|
|
2206
1997
|
} catch {
|
|
2207
1998
|
status = {
|
|
@@ -2246,7 +2037,7 @@ function spawnRunner(cfg, suffix, cwd, initialStatus, initialStatusPath, launchP
|
|
|
2246
2037
|
...launcher ? {} : { pid: proc.pid, pidNamespaceScope: currentPidNamespaceScope(), ...bootId ? { bootId } : {} },
|
|
2247
2038
|
processTerminal: { version: 1, state: "pending", runId: initialStatus.runId, runnerProcessInstanceId }
|
|
2248
2039
|
});
|
|
2249
|
-
updateActiveRunIndex(
|
|
2040
|
+
updateActiveRunIndex(path5.dirname(initialStatusPath), initialStatus.state, initialStatus.toolCallId);
|
|
2250
2041
|
} catch (error) {
|
|
2251
2042
|
const message = `Failed to persist initial async status: ${error instanceof Error ? error.message : String(error)}`;
|
|
2252
2043
|
if (launchAsyncDir) persistPreProceedStartupFailure(launchAsyncDir, launchRunId, runnerProcessInstanceId, launchSessionId, launchCompletionOwnerId, message);
|
|
@@ -2387,7 +2178,7 @@ function buildAsyncRunnerSteps(id, params) {
|
|
|
2387
2178
|
if (error instanceof ChainOutputValidationError) return { error: error.message };
|
|
2388
2179
|
throw error;
|
|
2389
2180
|
}
|
|
2390
|
-
const diagnosticContext = params.unknownAgentDiagnosticContext ?? unknownAgentDiagnosticContext(discoverAgents(
|
|
2181
|
+
const diagnosticContext = params.unknownAgentDiagnosticContext ?? unknownAgentDiagnosticContext(discoverAgents(path5.resolve(runnerCwd), "both"));
|
|
2391
2182
|
for (const s of chain) {
|
|
2392
2183
|
const stepAgents = isParallelStep(s) ? s.parallel.map((t) => t.agent) : isDynamicParallelStep(s) ? [s.parallel.agent] : [s.agent];
|
|
2393
2184
|
for (const agentName of stepAgents) {
|
|
@@ -2484,7 +2275,7 @@ function buildAsyncRunnerSteps(id, params) {
|
|
|
2484
2275
|
stepCwd,
|
|
2485
2276
|
ctx.cwd,
|
|
2486
2277
|
a.skillPath,
|
|
2487
|
-
a.filePath ?
|
|
2278
|
+
a.filePath ? path5.dirname(a.filePath) : stepCwd
|
|
2488
2279
|
);
|
|
2489
2280
|
if (missingSkills.includes("pi-subagents")) throw new UnavailableSubagentSkillError(UNAVAILABLE_SUBAGENT_SKILL_ERROR);
|
|
2490
2281
|
const systemPrompt = buildEffectiveSystemPrompt({ agent: a, resolvedSkills, cwd: stepCwd });
|
|
@@ -2657,7 +2448,7 @@ function buildAsyncRunnerSteps(id, params) {
|
|
|
2657
2448
|
acceptanceRole: a.acceptanceRole,
|
|
2658
2449
|
...s.gateOn ? { gateOn: s.gateOn } : {},
|
|
2659
2450
|
...behavior.outputSchema ? { structuredOutputSchema: behavior.outputSchema } : {},
|
|
2660
|
-
...behavior.outputSchema ? { structuredOutput: createStructuredOutputRuntime(behavior.outputSchema,
|
|
2451
|
+
...behavior.outputSchema ? { structuredOutput: createStructuredOutputRuntime(behavior.outputSchema, path5.join(asyncDir, "structured-output"), { acceptanceReport: resolveAcceptanceReportMode(s.acceptance) }) } : {},
|
|
2661
2452
|
...resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {},
|
|
2662
2453
|
...s.worktree ? { worktree: true } : {}
|
|
2663
2454
|
};
|
|
@@ -2831,14 +2622,15 @@ function executeAsyncChain(id, params) {
|
|
|
2831
2622
|
if (acceptanceErrors.length > 0) return formatAsyncStartError(resultMode, acceptanceErrors.join(" "));
|
|
2832
2623
|
const undefinedLauncher = chain.flatMap(getStepAgents).map((name) => agents.find((agent) => agent.name === name)?.launcher).find((name) => name !== void 0 && !lookupRunnerLauncher(ctx.runnerLaunchers, name));
|
|
2833
2624
|
if (undefinedLauncher !== void 0) return formatAsyncStartError(resultMode, `Launcher '${undefinedLauncher}' used by this ${resultMode} is not defined in runnerLaunchers in the user subagent config.`);
|
|
2834
|
-
const
|
|
2625
|
+
const launchParentSessionId = ctx.parentSessionId ?? ctx.currentSessionId;
|
|
2626
|
+
const capabilityCeiling = params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(launchParentSessionId);
|
|
2835
2627
|
const inheritedNestedRoute2 = inheritedNestedRouteOf(ctx.childRuntime);
|
|
2836
2628
|
const nestedAddress = inheritedNestedRoute2 ? inheritedNestedParentAddressOf(ctx.childRuntime) : void 0;
|
|
2837
|
-
const asyncDir = inheritedNestedRoute2 ?
|
|
2629
|
+
const asyncDir = inheritedNestedRoute2 ? path5.join(TEMP_ROOT_DIR, "nested-subagent-runs", inheritedNestedRoute2.rootRunId, id) : path5.join(DIRS.async, id);
|
|
2838
2630
|
let runFanoutBudget;
|
|
2839
2631
|
try {
|
|
2840
2632
|
runFanoutBudget = params.runFanoutBudget ?? createRunFanoutBudget(id, 64);
|
|
2841
|
-
|
|
2633
|
+
fs6.mkdirSync(asyncDir, { recursive: true });
|
|
2842
2634
|
writeRunFanoutBudgetDescriptor(asyncDir, runFanoutBudget);
|
|
2843
2635
|
} catch (error) {
|
|
2844
2636
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -2864,9 +2656,9 @@ function executeAsyncChain(id, params) {
|
|
|
2864
2656
|
sessionFilesByFlatIndex,
|
|
2865
2657
|
thinkingOverridesByFlatIndex,
|
|
2866
2658
|
contextForAgent: params.contextForAgent,
|
|
2867
|
-
progressDir: params.progressDir ?? (artifactsDir ?
|
|
2659
|
+
progressDir: params.progressDir ?? (artifactsDir ? path5.join(artifactsDir, "progress", id) : resultMode === "parallel" ? path5.join(asyncDir, "progress") : void 0),
|
|
2868
2660
|
agentContract: params.agentContract,
|
|
2869
|
-
outputBaseDir: artifactsDir ?
|
|
2661
|
+
outputBaseDir: artifactsDir ? path5.join(artifactsDir, "outputs", id) : void 0,
|
|
2870
2662
|
dynamicFanoutMaxItems: params.dynamicFanoutMaxItems,
|
|
2871
2663
|
maxSubagentDepth,
|
|
2872
2664
|
waitToolEnabled: params.waitToolEnabled,
|
|
@@ -2887,7 +2679,7 @@ function executeAsyncChain(id, params) {
|
|
|
2887
2679
|
});
|
|
2888
2680
|
if ("error" in built) {
|
|
2889
2681
|
try {
|
|
2890
|
-
|
|
2682
|
+
fs6.rmSync(asyncDir, { recursive: true, force: true });
|
|
2891
2683
|
} catch {
|
|
2892
2684
|
}
|
|
2893
2685
|
return formatAsyncStartError(resultMode, built.error);
|
|
@@ -2923,11 +2715,10 @@ function executeAsyncChain(id, params) {
|
|
|
2923
2715
|
const initialStatusAt = Date.now();
|
|
2924
2716
|
const initialCompletionOwnerId = ctx.completionOwnerId ?? currentCompletionOwnerId();
|
|
2925
2717
|
const completionOwnerProcess = currentRecordedPid();
|
|
2926
|
-
const launchParentSessionId = ctx.parentSessionId ?? ctx.currentSessionId;
|
|
2927
2718
|
try {
|
|
2928
2719
|
writeRetainedRequiredChildExtensions(asyncDir, params.requiredExtensions ?? ctx.childRuntime?.requiredExtensions ?? resolveRequiredChildExtensions(launchParentSessionId ?? void 0));
|
|
2929
2720
|
} catch (error) {
|
|
2930
|
-
|
|
2721
|
+
fs6.rmSync(asyncDir, { recursive: true, force: true });
|
|
2931
2722
|
return formatAsyncStartError(resultMode, `Failed to record required child extensions: ${error instanceof Error ? error.message : String(error)}`);
|
|
2932
2723
|
}
|
|
2933
2724
|
let spawnResultOrPromise = {};
|
|
@@ -2943,7 +2734,7 @@ function executeAsyncChain(id, params) {
|
|
|
2943
2734
|
artifactsDir: artifactConfig.enabled ? artifactsDir : void 0,
|
|
2944
2735
|
artifactConfig,
|
|
2945
2736
|
share: shareEnabled,
|
|
2946
|
-
sessionDir: sessionRoot ?
|
|
2737
|
+
sessionDir: sessionRoot ? path5.join(sessionRoot, `async-${id}`) : void 0,
|
|
2947
2738
|
asyncDir,
|
|
2948
2739
|
sessionId: ctx.currentSessionId,
|
|
2949
2740
|
completionOwnerId: ctx.completionOwnerId ?? currentCompletionOwnerId(),
|
|
@@ -3000,7 +2791,7 @@ function executeAsyncChain(id, params) {
|
|
|
3000
2791
|
...launcher ? { launcher } : {},
|
|
3001
2792
|
steps: initialStatusSteps
|
|
3002
2793
|
},
|
|
3003
|
-
|
|
2794
|
+
path5.join(asyncDir, "status.json"),
|
|
3004
2795
|
launchParentSessionId,
|
|
3005
2796
|
(proof) => emitProcessTerminalEvent(ctx, proof),
|
|
3006
2797
|
(runnerProcessInstanceId) => params.activeAsyncCapacity?.markStarted(runnerProcessInstanceId)
|
|
@@ -3123,7 +2914,7 @@ function executeAsyncChain(id, params) {
|
|
|
3123
2914
|
return spawnResultOrPromise instanceof Promise ? spawnResultOrPromise.then(finishSpawnResult) : finishSpawnResult(spawnResultOrPromise);
|
|
3124
2915
|
}
|
|
3125
2916
|
function workflowAwaitedAsyncResultPath(asyncDir) {
|
|
3126
|
-
return
|
|
2917
|
+
return path5.join(asyncDir, "workflow-result.json");
|
|
3127
2918
|
}
|
|
3128
2919
|
function executeAsyncSingle(id, params) {
|
|
3129
2920
|
const {
|
|
@@ -3183,7 +2974,7 @@ function executeAsyncSingle(id, params) {
|
|
|
3183
2974
|
if (extensionBindings !== void 0) unsupported.push("extension bindings");
|
|
3184
2975
|
if (unsupported.length > 0) return formatAsyncStartError("single", `Agent '${agentConfig.name}' uses runner.type='${externalRunnerType}' and does not support: ${unsupported.join(", ")}.`);
|
|
3185
2976
|
}
|
|
3186
|
-
const capabilityCeiling = intersectSubagentCapabilityCeilings(params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId), ctx.childRuntime?.capabilityCeiling);
|
|
2977
|
+
const capabilityCeiling = intersectSubagentCapabilityCeilings(params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.parentSessionId ?? ctx.currentSessionId), ctx.childRuntime?.capabilityCeiling);
|
|
3187
2978
|
try {
|
|
3188
2979
|
assertAgentAllowedByCapabilityCeiling(agentConfig.name, capabilityCeiling);
|
|
3189
2980
|
} catch (error) {
|
|
@@ -3193,6 +2984,7 @@ function executeAsyncSingle(id, params) {
|
|
|
3193
2984
|
const requestedMachine = params.machine ?? agentConfig.machine;
|
|
3194
2985
|
const machineUnsupported = formatHerdrMachineRunnerUnsupported({ machine: requestedMachine, agentName: agentConfig.name, runnerType: agentConfig.runner?.type, adapter: agentConfig.runner?.type === "external-cli" ? agentConfig.runner.adapter : void 0, worktree: params.worktree });
|
|
3195
2986
|
if (machineUnsupported) return formatAsyncStartError("single", machineUnsupported);
|
|
2987
|
+
if (requestedMachine && params.revivalLease) return formatAsyncStartError("single", PLACED_RESTORE_UNSUPPORTED);
|
|
3196
2988
|
const resolvedLauncher = resolveAgentRunnerLauncher(agentConfig, ctx.runnerLaunchers, requestedMachine);
|
|
3197
2989
|
if (resolvedLauncher.error) return formatAsyncStartError("single", resolvedLauncher.error);
|
|
3198
2990
|
const launcher = resolvedLauncher.launcher;
|
|
@@ -3231,16 +3023,16 @@ function executeAsyncSingle(id, params) {
|
|
|
3231
3023
|
runnerCwd,
|
|
3232
3024
|
ctx.cwd,
|
|
3233
3025
|
agentConfig.skillPath,
|
|
3234
|
-
agentConfig.filePath ?
|
|
3026
|
+
agentConfig.filePath ? path5.dirname(agentConfig.filePath) : runnerCwd
|
|
3235
3027
|
);
|
|
3236
3028
|
if (missingSkills.includes("pi-subagents")) return formatAsyncStartError("single", UNAVAILABLE_SUBAGENT_SKILL_ERROR);
|
|
3237
3029
|
const inheritedNestedRoute2 = inheritedNestedRouteOf(ctx.childRuntime);
|
|
3238
3030
|
const nestedAddress = inheritedNestedRoute2 ? inheritedNestedParentAddressOf(ctx.childRuntime) : void 0;
|
|
3239
|
-
const asyncDir = inheritedNestedRoute2 ?
|
|
3031
|
+
const asyncDir = inheritedNestedRoute2 ? path5.join(TEMP_ROOT_DIR, "nested-subagent-runs", inheritedNestedRoute2.rootRunId, id) : path5.join(DIRS.async, id);
|
|
3240
3032
|
let runFanoutBudget;
|
|
3241
3033
|
try {
|
|
3242
3034
|
runFanoutBudget = params.runFanoutBudget ?? createRunFanoutBudget(id, 64);
|
|
3243
|
-
|
|
3035
|
+
fs6.mkdirSync(asyncDir, { recursive: true });
|
|
3244
3036
|
writeRunFanoutBudgetDescriptor(asyncDir, runFanoutBudget);
|
|
3245
3037
|
} catch (error) {
|
|
3246
3038
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -3251,14 +3043,14 @@ function executeAsyncSingle(id, params) {
|
|
|
3251
3043
|
};
|
|
3252
3044
|
}
|
|
3253
3045
|
const effectiveOutput = normalizeSingleOutputOverride(params.output, agentConfig.output);
|
|
3254
|
-
const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, instructionCwd, params.outputBaseDir ?? (artifactsDir ?
|
|
3046
|
+
const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, instructionCwd, params.outputBaseDir ?? (artifactsDir ? path5.join(artifactsDir, "outputs", id) : void 0));
|
|
3255
3047
|
const systemPrompt = buildEffectiveSystemPrompt({ agent: agentConfig, resolvedSkills, cwd: runnerCwd });
|
|
3256
3048
|
const outputMode = params.outputMode ?? agentConfig.outputMode ?? "inline";
|
|
3257
3049
|
const validationError = validateFileOnlyOutputMode(outputMode, outputPath, `Async single run (${agent})`);
|
|
3258
3050
|
if (validationError) return formatAsyncStartError("single", validationError);
|
|
3259
3051
|
const taskWithOutputInstruction = injectSingleOutputInstruction(task, outputPath, agentConfig);
|
|
3260
3052
|
const reads = params.reads !== void 0 ? params.reads : agentConfig.defaultReads ?? false;
|
|
3261
|
-
const readPaths = Array.isArray(reads) ? machine ? externalRunner ? reads.map((read) => read === "~" || read.startsWith("~/") ||
|
|
3053
|
+
const readPaths = Array.isArray(reads) ? machine ? externalRunner ? reads.map((read) => read === "~" || read.startsWith("~/") || path5.posix.isAbsolute(read) ? read : path5.posix.resolve(instructionCwd, read)) : [] : managedWorktreeProvider === "worktrunk" ? resolveExistingReadInstructionPaths(reads, instructionCwd, readExistenceCwd) : resolveExistingReadPaths(reads, readExistenceCwd) : [];
|
|
3262
3054
|
const readsInstruction = readPaths.length > 0 ? `[Read from: ${readPaths.join(", ")}]
|
|
3263
3055
|
|
|
3264
3056
|
` : "";
|
|
@@ -3330,8 +3122,8 @@ function executeAsyncSingle(id, params) {
|
|
|
3330
3122
|
if (resolvedToolTimeout.error) return formatAsyncStartError("single", resolvedToolTimeout.error);
|
|
3331
3123
|
const toolTimeoutMs = resolvedToolTimeout.toolTimeoutMs;
|
|
3332
3124
|
const initialUsageBudget = usageBudgetState(params.usageBudget, void 0);
|
|
3333
|
-
const resolvedSessionDir = params.sessionDir ?? (sessionRoot ?
|
|
3334
|
-
const structuredOutput = params.structuredOutputSchema ? createStructuredOutputRuntime(params.structuredOutputSchema,
|
|
3125
|
+
const resolvedSessionDir = params.sessionDir ?? (sessionRoot ? path5.join(sessionRoot, `async-${id}`) : void 0);
|
|
3126
|
+
const structuredOutput = params.structuredOutputSchema ? createStructuredOutputRuntime(params.structuredOutputSchema, path5.join(asyncDir, "structured-output"), { acceptanceReport: resolveAcceptanceReportMode(params.acceptance) }) : void 0;
|
|
3335
3127
|
let selectedModel = model;
|
|
3336
3128
|
let requestedModel;
|
|
3337
3129
|
if (!externalRunner) {
|
|
@@ -3454,7 +3246,7 @@ function executeAsyncSingle(id, params) {
|
|
|
3454
3246
|
};
|
|
3455
3247
|
if (!externalRunner) {
|
|
3456
3248
|
try {
|
|
3457
|
-
writePrivateAtomicJson(
|
|
3249
|
+
writePrivateAtomicJson(path5.join(asyncDir, "recovery-descriptor.json"), recoveryDescriptor);
|
|
3458
3250
|
} catch (error) {
|
|
3459
3251
|
return formatAsyncStartError("single", `Failed to persist async recovery descriptor for '${id}': ${error instanceof Error ? error.message : String(error)}`);
|
|
3460
3252
|
}
|
|
@@ -3601,7 +3393,7 @@ function executeAsyncSingle(id, params) {
|
|
|
3601
3393
|
...launcher ? { launcher } : {},
|
|
3602
3394
|
steps: [{ agent, status: "pending", ...lane ? { lane } : {}, ...model ? { model } : {}, ...contextLimit !== void 0 ? { contextLimit } : {} }]
|
|
3603
3395
|
},
|
|
3604
|
-
|
|
3396
|
+
path5.join(asyncDir, "status.json"),
|
|
3605
3397
|
launchParentSessionId,
|
|
3606
3398
|
(proof) => emitProcessTerminalEvent(ctx, proof),
|
|
3607
3399
|
(runnerProcessInstanceId) => params.activeAsyncCapacity?.markStarted(runnerProcessInstanceId),
|
|
@@ -3809,10 +3601,10 @@ function lineFromCheck(label, check) {
|
|
|
3809
3601
|
}
|
|
3810
3602
|
function formatExistingDirectory(label, dirPath) {
|
|
3811
3603
|
try {
|
|
3812
|
-
if (!
|
|
3813
|
-
const stats =
|
|
3604
|
+
if (!fs7.existsSync(dirPath)) return `- ${label}: missing (${dirPath})`;
|
|
3605
|
+
const stats = fs7.statSync(dirPath);
|
|
3814
3606
|
if (!stats.isDirectory()) throw new Error(`not a directory: ${dirPath}`);
|
|
3815
|
-
|
|
3607
|
+
fs7.accessSync(dirPath, fs7.constants.R_OK | fs7.constants.W_OK);
|
|
3816
3608
|
return `- ${label}: ok (${dirPath})`;
|
|
3817
3609
|
} catch (error) {
|
|
3818
3610
|
return `- ${label}: failed (${dirPath}) \u2014 ${errorText(error)}`;
|
|
@@ -3840,10 +3632,10 @@ function formatSkillSourceCounts(skills) {
|
|
|
3840
3632
|
}
|
|
3841
3633
|
function formatConfiguredSessionDir(input) {
|
|
3842
3634
|
if (input.requestedSessionDir) {
|
|
3843
|
-
return
|
|
3635
|
+
return path6.resolve(input.expandTilde?.(input.requestedSessionDir) ?? input.requestedSessionDir);
|
|
3844
3636
|
}
|
|
3845
3637
|
if (input.config.defaultSessionDir) {
|
|
3846
|
-
return
|
|
3638
|
+
return path6.resolve(input.expandTilde?.(input.config.defaultSessionDir) ?? input.config.defaultSessionDir);
|
|
3847
3639
|
}
|
|
3848
3640
|
return "not configured";
|
|
3849
3641
|
}
|
|
@@ -3852,7 +3644,7 @@ function formatSessionLines(input) {
|
|
|
3852
3644
|
const lines = [
|
|
3853
3645
|
lineFromCheck("configured session dir", () => `- configured session dir: ${formatConfiguredSessionDir(input)}`),
|
|
3854
3646
|
`- current session file: ${sessionFile ?? "not available"}`,
|
|
3855
|
-
`- current session dir: ${sessionFile ?
|
|
3647
|
+
`- current session dir: ${sessionFile ? path6.dirname(sessionFile) : "not available"}`,
|
|
3856
3648
|
`- current session id: ${input.currentSessionId ?? input.state.currentSessionId ?? "not available"}`
|
|
3857
3649
|
];
|
|
3858
3650
|
if (input.sessionError) lines.push(`- session manager: failed \u2014 ${input.sessionError}`);
|
|
@@ -3987,8 +3779,7 @@ var WORKFLOW_FENCE = /^```(?:js|javascript) workflow[ \t]*$/;
|
|
|
3987
3779
|
var PLAIN_JS_FENCE = /^```(?:js|javascript)[ \t]*$/;
|
|
3988
3780
|
var OPEN_FENCE = /^(`{3,}|~{3,})/;
|
|
3989
3781
|
var CLOSE_FENCE = /^(`{3,}|~{3,})[ \t]*$/;
|
|
3990
|
-
var SUMMARY_NOTE =
|
|
3991
|
-
var FILE_FALLBACK = `If the block is in your reply: ${SUMMARY_NOTE}`;
|
|
3782
|
+
var SUMMARY_NOTE = `On some hosts, text written after a tool call in the same turn reaches the session only as a summary without code blocks, so for the rest of this turn pass the script's source as a tool argument: options: { script: "..." } in place of workflow: true, with the call's other fields unchanged.`;
|
|
3992
3783
|
function readReplyWorkflowScript(sessionManager, toolCallId) {
|
|
3993
3784
|
const branch = sessionManager.getBranch();
|
|
3994
3785
|
for (let index = branch.length - 1; index >= 0; index--) {
|
|
@@ -4011,7 +3802,7 @@ function scriptFromReply(content) {
|
|
|
4011
3802
|
const blocks = tagged.length > 0 ? tagged : plain.length === 1 ? plain : [];
|
|
4012
3803
|
if (blocks.length !== 1) {
|
|
4013
3804
|
const untagged = tagged.length === 0 && plain.length > 1 ? ` This reply has ${plain.length} untagged js blocks; open the script's block with the line "\`\`\`js workflow".` : "";
|
|
4014
|
-
return { error: `workflow: true needs one fenced block in the same reply as the tool call, opened with the exact line "\`\`\`js workflow" and closed with "\`\`\`"; found ${tagged.length}.${untagged} ${
|
|
3805
|
+
return { error: `workflow: true needs one fenced block in the same reply as the tool call, opened with the exact line "\`\`\`js workflow" and closed with "\`\`\`"; found ${tagged.length}.${untagged} If the block is in your reply: ${SUMMARY_NOTE}` };
|
|
4015
3806
|
}
|
|
4016
3807
|
const script = blocks[0];
|
|
4017
3808
|
if (!script.trim()) return { error: "The script's fenced block in this reply is empty." };
|
|
@@ -4038,14 +3829,14 @@ function workflowBlocks(text) {
|
|
|
4038
3829
|
}
|
|
4039
3830
|
|
|
4040
3831
|
// src/runs/foreground/execution.ts
|
|
4041
|
-
import { existsSync as
|
|
4042
|
-
import * as
|
|
3832
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync6, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
3833
|
+
import * as path10 from "node:path";
|
|
4043
3834
|
|
|
4044
3835
|
// src/shared/fork-session-cwd.ts
|
|
4045
|
-
import * as
|
|
4046
|
-
import * as
|
|
3836
|
+
import * as fs8 from "node:fs";
|
|
3837
|
+
import * as path7 from "node:path";
|
|
4047
3838
|
function alignForkedSessionCwd(sessionFile, cwd) {
|
|
4048
|
-
const lines =
|
|
3839
|
+
const lines = fs8.readFileSync(sessionFile, "utf-8").split("\n").filter((line) => line.trim().length > 0);
|
|
4049
3840
|
const entries = lines.map((line, index) => {
|
|
4050
3841
|
try {
|
|
4051
3842
|
return JSON.parse(line);
|
|
@@ -4056,16 +3847,16 @@ function alignForkedSessionCwd(sessionFile, cwd) {
|
|
|
4056
3847
|
});
|
|
4057
3848
|
const header = entries[0];
|
|
4058
3849
|
if (header?.type !== "session") throw new Error(`Forked session ${sessionFile} does not start with a session header.`);
|
|
4059
|
-
const effectiveCwd =
|
|
3850
|
+
const effectiveCwd = fs8.realpathSync.native(path7.resolve(cwd));
|
|
4060
3851
|
if (header.cwd === effectiveCwd) return;
|
|
4061
3852
|
header.cwd = effectiveCwd;
|
|
4062
|
-
|
|
3853
|
+
fs8.writeFileSync(sessionFile, `${entries.map((entry) => JSON.stringify(entry)).join("\n")}
|
|
4063
3854
|
`, "utf-8");
|
|
4064
3855
|
}
|
|
4065
3856
|
|
|
4066
3857
|
// src/shared/child-transcript.ts
|
|
4067
|
-
import * as
|
|
4068
|
-
import * as
|
|
3858
|
+
import * as fs9 from "node:fs";
|
|
3859
|
+
import * as path8 from "node:path";
|
|
4069
3860
|
var MAX_TOOL_PAYLOAD_BYTES = 32 * 1024;
|
|
4070
3861
|
var TOOL_PAYLOAD_TRUNCATION_MARKER = "\n\n\u2026 payload truncated";
|
|
4071
3862
|
function boundedPayload(value, maxBytes = MAX_TOOL_PAYLOAD_BYTES) {
|
|
@@ -4142,7 +3933,7 @@ function createChildTranscriptWriter(input) {
|
|
|
4142
3933
|
const markerBytes = Buffer.byteLength(marker, "utf-8");
|
|
4143
3934
|
if (bytesWritten + markerBytes > maxBytes) return false;
|
|
4144
3935
|
try {
|
|
4145
|
-
|
|
3936
|
+
fs9.appendFileSync(input.transcriptPath, marker, "utf-8");
|
|
4146
3937
|
bytesWritten += markerBytes;
|
|
4147
3938
|
return true;
|
|
4148
3939
|
} catch (error) {
|
|
@@ -4170,15 +3961,15 @@ function createChildTranscriptWriter(input) {
|
|
|
4170
3961
|
return;
|
|
4171
3962
|
}
|
|
4172
3963
|
try {
|
|
4173
|
-
|
|
3964
|
+
fs9.appendFileSync(input.transcriptPath, line, "utf-8");
|
|
4174
3965
|
bytesWritten += bytes;
|
|
4175
3966
|
} catch (error) {
|
|
4176
3967
|
writeError = `Failed to write child transcript '${input.transcriptPath}': ${errorMessage(error)}`;
|
|
4177
3968
|
}
|
|
4178
3969
|
};
|
|
4179
3970
|
try {
|
|
4180
|
-
|
|
4181
|
-
|
|
3971
|
+
fs9.mkdirSync(path8.dirname(input.transcriptPath), { recursive: true });
|
|
3972
|
+
fs9.writeFileSync(input.transcriptPath, "", "utf-8");
|
|
4182
3973
|
} catch (error) {
|
|
4183
3974
|
writeError = `Failed to initialize child transcript '${input.transcriptPath}': ${errorMessage(error)}`;
|
|
4184
3975
|
}
|
|
@@ -4300,7 +4091,7 @@ ${text}`;
|
|
|
4300
4091
|
}
|
|
4301
4092
|
|
|
4302
4093
|
// src/shared/jsonl-writer.ts
|
|
4303
|
-
import * as
|
|
4094
|
+
import * as fs10 from "node:fs";
|
|
4304
4095
|
var DEFAULT_MAX_JSONL_BYTES = 50 * 1024 * 1024;
|
|
4305
4096
|
function createJsonlWriter(filePath, source, deps = {}) {
|
|
4306
4097
|
if (!filePath) {
|
|
@@ -4311,7 +4102,7 @@ function createJsonlWriter(filePath, source, deps = {}) {
|
|
|
4311
4102
|
}
|
|
4312
4103
|
};
|
|
4313
4104
|
}
|
|
4314
|
-
const createWriteStream3 = deps.createWriteStream ?? ((targetPath) =>
|
|
4105
|
+
const createWriteStream3 = deps.createWriteStream ?? ((targetPath) => fs10.createWriteStream(targetPath, { flags: "a" }));
|
|
4315
4106
|
let stream;
|
|
4316
4107
|
try {
|
|
4317
4108
|
stream = createWriteStream3(filePath);
|
|
@@ -4361,15 +4152,15 @@ function createJsonlWriter(filePath, source, deps = {}) {
|
|
|
4361
4152
|
closed = true;
|
|
4362
4153
|
const current = stream;
|
|
4363
4154
|
stream = void 0;
|
|
4364
|
-
await new Promise((
|
|
4155
|
+
await new Promise((resolve9) => current.end(() => resolve9()));
|
|
4365
4156
|
}
|
|
4366
4157
|
};
|
|
4367
4158
|
}
|
|
4368
4159
|
|
|
4369
4160
|
// src/runs/shared/orca-progress-tabs.ts
|
|
4370
4161
|
import { spawn as spawn2 } from "node:child_process";
|
|
4371
|
-
import * as
|
|
4372
|
-
import * as
|
|
4162
|
+
import * as fs11 from "node:fs";
|
|
4163
|
+
import * as path9 from "node:path";
|
|
4373
4164
|
import { createHash, randomUUID as randomUUID3 } from "node:crypto";
|
|
4374
4165
|
var ORCA_CREATE_TIMEOUT_MS = 2e4;
|
|
4375
4166
|
var ORCA_KILL_GRACE_MS = 2e3;
|
|
@@ -4414,10 +4205,10 @@ var ORCA_CLEANUP_WATCHDOG_SCRIPT = [
|
|
|
4414
4205
|
].join("");
|
|
4415
4206
|
function executableFile(candidate) {
|
|
4416
4207
|
try {
|
|
4417
|
-
const stats =
|
|
4208
|
+
const stats = fs11.statSync(candidate);
|
|
4418
4209
|
if (!stats.isFile()) return false;
|
|
4419
4210
|
if (process.platform === "win32") return true;
|
|
4420
|
-
|
|
4211
|
+
fs11.accessSync(candidate, fs11.constants.X_OK);
|
|
4421
4212
|
return true;
|
|
4422
4213
|
} catch {
|
|
4423
4214
|
return false;
|
|
@@ -4431,9 +4222,9 @@ function executableNames(command, env) {
|
|
|
4431
4222
|
function resolveOrcaCommand(env = process.env) {
|
|
4432
4223
|
const override = env.PI_SUBAGENT_ORCA_BINARY?.trim();
|
|
4433
4224
|
if (override) return executableFile(override) ? override : void 0;
|
|
4434
|
-
for (const directory of (env.PATH ?? "").split(
|
|
4225
|
+
for (const directory of (env.PATH ?? "").split(path9.delimiter).filter(Boolean)) {
|
|
4435
4226
|
for (const name of executableNames("orca", env)) {
|
|
4436
|
-
const candidate =
|
|
4227
|
+
const candidate = path9.join(directory, name);
|
|
4437
4228
|
if (executableFile(candidate)) return candidate;
|
|
4438
4229
|
}
|
|
4439
4230
|
}
|
|
@@ -4470,15 +4261,15 @@ function viewerCommand(nodeExecutable, logPath, donePath) {
|
|
|
4470
4261
|
return invocation;
|
|
4471
4262
|
}
|
|
4472
4263
|
function progressRoot() {
|
|
4473
|
-
return
|
|
4264
|
+
return path9.join(TEMP_ROOT_DIR, "orca-progress");
|
|
4474
4265
|
}
|
|
4475
4266
|
function pruneStaleProgressFiles(root, now = Date.now()) {
|
|
4476
4267
|
try {
|
|
4477
|
-
for (const entry of
|
|
4268
|
+
for (const entry of fs11.readdirSync(root, { withFileTypes: true })) {
|
|
4478
4269
|
if (!entry.isFile() || !entry.name.endsWith(".log") && !entry.name.endsWith(".done") && !entry.name.endsWith(".ready") && !entry.name.endsWith(".pending")) continue;
|
|
4479
|
-
const file =
|
|
4270
|
+
const file = path9.join(root, entry.name);
|
|
4480
4271
|
try {
|
|
4481
|
-
if (now -
|
|
4272
|
+
if (now - fs11.statSync(file).mtimeMs > STALE_PROGRESS_MAX_AGE_MS) fs11.rmSync(file, { force: true });
|
|
4482
4273
|
} catch {
|
|
4483
4274
|
}
|
|
4484
4275
|
}
|
|
@@ -4491,9 +4282,9 @@ function safeSegment(value, fallback = "subagent") {
|
|
|
4491
4282
|
}
|
|
4492
4283
|
function observerManifestPath(cwd, stem) {
|
|
4493
4284
|
try {
|
|
4494
|
-
const dir =
|
|
4495
|
-
|
|
4496
|
-
return
|
|
4285
|
+
const dir = path9.join(getProjectSubagentsDir(resolveSequenceScope(cwd)), "views", "orca");
|
|
4286
|
+
fs11.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
4287
|
+
return path9.join(dir, `${stem}.json`);
|
|
4497
4288
|
} catch {
|
|
4498
4289
|
return void 0;
|
|
4499
4290
|
}
|
|
@@ -4501,7 +4292,7 @@ function observerManifestPath(cwd, stem) {
|
|
|
4501
4292
|
function writeObserverManifest(manifestPath, manifest) {
|
|
4502
4293
|
if (!manifestPath) return;
|
|
4503
4294
|
try {
|
|
4504
|
-
|
|
4295
|
+
fs11.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
4505
4296
|
`, { encoding: "utf-8", mode: 384 });
|
|
4506
4297
|
} catch {
|
|
4507
4298
|
}
|
|
@@ -4511,18 +4302,18 @@ function sleepSync(ms) {
|
|
|
4511
4302
|
Atomics.wait(signal, 0, 0, ms);
|
|
4512
4303
|
}
|
|
4513
4304
|
function resolveSequenceScope(cwd) {
|
|
4514
|
-
let current =
|
|
4305
|
+
let current = path9.resolve(cwd);
|
|
4515
4306
|
try {
|
|
4516
|
-
current =
|
|
4307
|
+
current = fs11.realpathSync(current);
|
|
4517
4308
|
} catch {
|
|
4518
4309
|
}
|
|
4519
4310
|
while (true) {
|
|
4520
4311
|
try {
|
|
4521
|
-
if (
|
|
4312
|
+
if (fs11.existsSync(path9.join(current, ".git"))) return current;
|
|
4522
4313
|
} catch {
|
|
4523
4314
|
}
|
|
4524
|
-
const parent =
|
|
4525
|
-
if (parent === current) return
|
|
4315
|
+
const parent = path9.dirname(current);
|
|
4316
|
+
if (parent === current) return path9.resolve(cwd);
|
|
4526
4317
|
current = parent;
|
|
4527
4318
|
}
|
|
4528
4319
|
}
|
|
@@ -4530,18 +4321,18 @@ function sequenceKey(cwd) {
|
|
|
4530
4321
|
return createHash("sha256").update(resolveSequenceScope(cwd)).digest("hex").slice(0, 20);
|
|
4531
4322
|
}
|
|
4532
4323
|
function withSequenceLock(root, key, fn) {
|
|
4533
|
-
const lockPath =
|
|
4324
|
+
const lockPath = path9.join(root, `counter-${key}.lock`);
|
|
4534
4325
|
let locked = false;
|
|
4535
4326
|
for (let attempt = 0; attempt < COUNTER_LOCK_RETRIES; attempt++) {
|
|
4536
4327
|
try {
|
|
4537
|
-
|
|
4328
|
+
fs11.mkdirSync(lockPath, { mode: 448 });
|
|
4538
4329
|
locked = true;
|
|
4539
4330
|
break;
|
|
4540
4331
|
} catch (error) {
|
|
4541
4332
|
if (error.code !== "EEXIST") break;
|
|
4542
4333
|
try {
|
|
4543
|
-
if (Date.now() -
|
|
4544
|
-
|
|
4334
|
+
if (Date.now() - fs11.statSync(lockPath).mtimeMs > COUNTER_LOCK_STALE_MS) {
|
|
4335
|
+
fs11.rmSync(lockPath, { recursive: true, force: true });
|
|
4545
4336
|
continue;
|
|
4546
4337
|
}
|
|
4547
4338
|
} catch {
|
|
@@ -4554,7 +4345,7 @@ function withSequenceLock(root, key, fn) {
|
|
|
4554
4345
|
return fn();
|
|
4555
4346
|
} finally {
|
|
4556
4347
|
try {
|
|
4557
|
-
|
|
4348
|
+
fs11.rmSync(lockPath, { recursive: true, force: true });
|
|
4558
4349
|
} catch {
|
|
4559
4350
|
}
|
|
4560
4351
|
}
|
|
@@ -4563,18 +4354,18 @@ function predecessorMarker(pathValue) {
|
|
|
4563
4354
|
if (!pathValue) return void 0;
|
|
4564
4355
|
const ready = pathValue.endsWith(".pending") ? pathValue.replace(/\.pending$/, ".ready") : pathValue;
|
|
4565
4356
|
const pending = ready.replace(/\.ready$/, ".pending");
|
|
4566
|
-
if (
|
|
4357
|
+
if (fs11.existsSync(ready) || fs11.existsSync(pending)) return pending;
|
|
4567
4358
|
return void 0;
|
|
4568
4359
|
}
|
|
4569
4360
|
function reserveTabSequence(root, cwd) {
|
|
4570
4361
|
const key = sequenceKey(cwd);
|
|
4571
|
-
const counterPath =
|
|
4572
|
-
const createDone =
|
|
4362
|
+
const counterPath = path9.join(root, `counter-${key}`);
|
|
4363
|
+
const createDone = path9.join(root, `create-${key}-${randomUUID3()}.pending`);
|
|
4573
4364
|
return withSequenceLock(root, key, () => {
|
|
4574
4365
|
let current = 0;
|
|
4575
4366
|
let previousCreateDone;
|
|
4576
4367
|
try {
|
|
4577
|
-
const raw =
|
|
4368
|
+
const raw = fs11.readFileSync(counterPath, "utf-8");
|
|
4578
4369
|
const [countLine, previousLine] = raw.split("\n");
|
|
4579
4370
|
const parsed = Number.parseInt(countLine ?? "", 10);
|
|
4580
4371
|
if (Number.isSafeInteger(parsed) && parsed >= 0) current = parsed;
|
|
@@ -4583,14 +4374,14 @@ function reserveTabSequence(root, cwd) {
|
|
|
4583
4374
|
}
|
|
4584
4375
|
const next = current + 1;
|
|
4585
4376
|
try {
|
|
4586
|
-
|
|
4587
|
-
|
|
4377
|
+
fs11.writeFileSync(createDone, "", { encoding: "utf-8", mode: 384 });
|
|
4378
|
+
fs11.writeFileSync(counterPath, `${next}
|
|
4588
4379
|
${createDone}
|
|
4589
4380
|
`, { encoding: "utf-8", mode: 384 });
|
|
4590
4381
|
return { sequence: next, previousCreateDone, createDone };
|
|
4591
4382
|
} catch {
|
|
4592
4383
|
try {
|
|
4593
|
-
|
|
4384
|
+
fs11.rmSync(createDone, { force: true });
|
|
4594
4385
|
} catch {
|
|
4595
4386
|
}
|
|
4596
4387
|
return void 0;
|
|
@@ -4612,8 +4403,8 @@ function scheduleCleanup(nodeExecutable, paths) {
|
|
|
4612
4403
|
}
|
|
4613
4404
|
function loadOrcaProgressTabsConfig() {
|
|
4614
4405
|
try {
|
|
4615
|
-
const configPath =
|
|
4616
|
-
const parsed = JSON.parse(
|
|
4406
|
+
const configPath = path9.join(getAgentDir(), "extensions", "subagent", "config.json");
|
|
4407
|
+
const parsed = JSON.parse(fs11.readFileSync(configPath, "utf-8"));
|
|
4617
4408
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
|
|
4618
4409
|
const value = parsed.orcaProgressTabs;
|
|
4619
4410
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
@@ -4632,7 +4423,7 @@ function createOrcaProgressTab(input) {
|
|
|
4632
4423
|
const nodeExecutable = resolveNodeExecutable();
|
|
4633
4424
|
const root = progressRoot();
|
|
4634
4425
|
try {
|
|
4635
|
-
|
|
4426
|
+
fs11.mkdirSync(root, { recursive: true, mode: 448 });
|
|
4636
4427
|
} catch {
|
|
4637
4428
|
return void 0;
|
|
4638
4429
|
}
|
|
@@ -4646,12 +4437,12 @@ function createOrcaProgressTab(input) {
|
|
|
4646
4437
|
const tabSequence = reservation.sequence;
|
|
4647
4438
|
const stem = `${runId}-${index}-${randomUUID3()}`;
|
|
4648
4439
|
const stepCount = typeof input.stepCount === "number" && Number.isInteger(input.stepCount) && input.stepCount > 0 ? input.stepCount : 1;
|
|
4649
|
-
const logPath =
|
|
4650
|
-
const donePath =
|
|
4440
|
+
const logPath = path9.join(root, `${stem}.log`);
|
|
4441
|
+
const donePath = path9.join(root, `${stem}.done`);
|
|
4651
4442
|
const title = `subagents \xB7 ${agent} \xB7 ${tabSequence}`;
|
|
4652
4443
|
const markCreateReady = () => {
|
|
4653
4444
|
try {
|
|
4654
|
-
|
|
4445
|
+
fs11.writeFileSync(reservation.createDone.replace(/\.pending$/, ".ready"), "");
|
|
4655
4446
|
} catch {
|
|
4656
4447
|
}
|
|
4657
4448
|
};
|
|
@@ -4663,36 +4454,36 @@ function createOrcaProgressTab(input) {
|
|
|
4663
4454
|
role: "run",
|
|
4664
4455
|
runId,
|
|
4665
4456
|
title,
|
|
4666
|
-
worktree:
|
|
4457
|
+
worktree: path9.resolve(cwd),
|
|
4667
4458
|
state: "opening",
|
|
4668
4459
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4669
4460
|
logPath
|
|
4670
4461
|
});
|
|
4671
4462
|
try {
|
|
4672
|
-
|
|
4463
|
+
fs11.writeFileSync(logPath, `pi-subagents / ${agent}
|
|
4673
4464
|
run ${runId} \xB7 ${stepCount === 1 ? "1 child" : `${stepCount} children`}
|
|
4674
4465
|
${"\u2500".repeat(48)}
|
|
4675
4466
|
`, { encoding: "utf-8", mode: 384 });
|
|
4676
|
-
|
|
4467
|
+
fs11.rmSync(donePath, { force: true });
|
|
4677
4468
|
} catch {
|
|
4678
4469
|
markCreateReady();
|
|
4679
4470
|
return void 0;
|
|
4680
4471
|
}
|
|
4681
4472
|
let available = true;
|
|
4682
4473
|
let truncated = false;
|
|
4683
|
-
let scheduledBytes =
|
|
4474
|
+
let scheduledBytes = fs11.statSync(logPath).size;
|
|
4684
4475
|
const progressByteLimit = MAX_MIRROR_BYTES - MIRROR_FOOTER_RESERVE_BYTES;
|
|
4685
|
-
const logStream =
|
|
4476
|
+
const logStream = fs11.createWriteStream(logPath, { flags: "a", mode: 384 });
|
|
4686
4477
|
const failObserver = () => {
|
|
4687
4478
|
if (!available) return;
|
|
4688
4479
|
available = false;
|
|
4689
4480
|
logStream.destroy();
|
|
4690
4481
|
try {
|
|
4691
|
-
|
|
4482
|
+
fs11.rmSync(logPath, { force: true });
|
|
4692
4483
|
} catch {
|
|
4693
4484
|
}
|
|
4694
4485
|
try {
|
|
4695
|
-
|
|
4486
|
+
fs11.rmSync(donePath, { force: true });
|
|
4696
4487
|
} catch {
|
|
4697
4488
|
}
|
|
4698
4489
|
};
|
|
@@ -4713,8 +4504,8 @@ ${"\u2500".repeat(48)}
|
|
|
4713
4504
|
};
|
|
4714
4505
|
let createSettled = false;
|
|
4715
4506
|
let resolveCreationSettled;
|
|
4716
|
-
const creationSettled = new Promise((
|
|
4717
|
-
resolveCreationSettled =
|
|
4507
|
+
const creationSettled = new Promise((resolve9) => {
|
|
4508
|
+
resolveCreationSettled = resolve9;
|
|
4718
4509
|
});
|
|
4719
4510
|
let cleanupPaths;
|
|
4720
4511
|
const scheduleDeferredCleanup = () => {
|
|
@@ -4736,7 +4527,7 @@ ${"\u2500".repeat(48)}
|
|
|
4736
4527
|
"terminal",
|
|
4737
4528
|
"create",
|
|
4738
4529
|
"--worktree",
|
|
4739
|
-
`path:${
|
|
4530
|
+
`path:${path9.resolve(cwd)}`,
|
|
4740
4531
|
"--title",
|
|
4741
4532
|
title,
|
|
4742
4533
|
"--command",
|
|
@@ -4804,7 +4595,7 @@ ${"\u2500".repeat(16)} child ${index2 + 1}/${count} \xB7 ${label} ${"\u2500".rep
|
|
|
4804
4595
|
let verifiedSessionFile;
|
|
4805
4596
|
if (sessionId && sessionFile) {
|
|
4806
4597
|
try {
|
|
4807
|
-
verifiedSessionFile =
|
|
4598
|
+
verifiedSessionFile = fs11.realpathSync(sessionFile);
|
|
4808
4599
|
} catch {
|
|
4809
4600
|
}
|
|
4810
4601
|
}
|
|
@@ -4819,18 +4610,18 @@ ${terminalMessage}
|
|
|
4819
4610
|
if (scheduledBytes + Buffer.byteLength(footer) > MAX_MIRROR_BYTES) footer = `
|
|
4820
4611
|
${status}
|
|
4821
4612
|
`;
|
|
4822
|
-
return new Promise((
|
|
4613
|
+
return new Promise((resolve9) => {
|
|
4823
4614
|
logStream.end(footer, () => {
|
|
4824
4615
|
if (available) {
|
|
4825
4616
|
try {
|
|
4826
|
-
|
|
4617
|
+
fs11.writeFileSync(donePath, `${status}
|
|
4827
4618
|
`, { encoding: "utf-8", mode: 384 });
|
|
4828
4619
|
} catch {
|
|
4829
4620
|
}
|
|
4830
4621
|
cleanupPaths = [logPath, donePath];
|
|
4831
4622
|
scheduleDeferredCleanup();
|
|
4832
4623
|
}
|
|
4833
|
-
|
|
4624
|
+
resolve9();
|
|
4834
4625
|
});
|
|
4835
4626
|
});
|
|
4836
4627
|
}
|
|
@@ -5456,7 +5247,7 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
5456
5247
|
childIndex: options.index ?? 0
|
|
5457
5248
|
}) : void 0;
|
|
5458
5249
|
const permissionRules = resolvePermissionRules(options.permissions, agent.permissions);
|
|
5459
|
-
const permissionAuditPath = permissionRules && options.artifactsDir ?
|
|
5250
|
+
const permissionAuditPath = permissionRules && options.artifactsDir ? path10.join(options.artifactsDir, "permission-audit", `${options.runId}-${options.index ?? 0}.jsonl`) : void 0;
|
|
5460
5251
|
let onWatchdogStatus;
|
|
5461
5252
|
const launch = buildInProcessChildLaunch({
|
|
5462
5253
|
machine: options.machine,
|
|
@@ -5647,7 +5438,7 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5647
5438
|
const childSessions = options.childSessionFactory ?? childSessionFactory();
|
|
5648
5439
|
const partialOutput = createPartialOutputTracker();
|
|
5649
5440
|
let childPromptFailed = false;
|
|
5650
|
-
const exitCode = await new Promise((
|
|
5441
|
+
const exitCode = await new Promise((resolve9) => {
|
|
5651
5442
|
const jsonlWriter = createJsonlWriter(shared.jsonlPath, { pause() {
|
|
5652
5443
|
}, resume() {
|
|
5653
5444
|
} });
|
|
@@ -5720,6 +5511,8 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5720
5511
|
let agentSettledReceived = false;
|
|
5721
5512
|
let queuedDrainHold = false;
|
|
5722
5513
|
let compactionStartedReceived = false;
|
|
5514
|
+
let terminalAgentEndReceived = false;
|
|
5515
|
+
let compactionAfterFinalOpen = false;
|
|
5723
5516
|
let finalDrainTimer;
|
|
5724
5517
|
let finalHardFinishTimer;
|
|
5725
5518
|
let watchdogTailTimer;
|
|
@@ -5762,7 +5555,7 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5762
5555
|
if (sessionSettled || finalDrainTimer || lifecycleFinished) return;
|
|
5763
5556
|
finalDrainTimer = setTimeout(() => {
|
|
5764
5557
|
if (lifecycleFinished || sessionSettled) return;
|
|
5765
|
-
if (capture.finalDrainHeld() || observeQueuedDrainHold()) {
|
|
5558
|
+
if (capture.finalDrainHeld() || observeQueuedDrainHold() || compactionAfterFinalOpen) {
|
|
5766
5559
|
finalDrainTimer = void 0;
|
|
5767
5560
|
armFinalDrainTimer();
|
|
5768
5561
|
return;
|
|
@@ -5776,9 +5569,7 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5776
5569
|
if (lifecycleFinished || sessionSettled) return;
|
|
5777
5570
|
settle(void 0, true);
|
|
5778
5571
|
}, HARD_FINISH_MS);
|
|
5779
|
-
finalHardFinishTimer.unref?.();
|
|
5780
5572
|
}, FINAL_STOP_GRACE_MS);
|
|
5781
|
-
finalDrainTimer.unref?.();
|
|
5782
5573
|
};
|
|
5783
5574
|
function armWatchdogTail() {
|
|
5784
5575
|
if (!cleanTerminalAssistantStopReceived && !agentSettledReceived || watchdogTailTimer || lifecycleFinished || sessionSettled) return;
|
|
@@ -5796,12 +5587,18 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5796
5587
|
}, childWatchdog?.watchdogTailTimeoutMs ?? 12e4);
|
|
5797
5588
|
watchdogTailTimer.unref?.();
|
|
5798
5589
|
}
|
|
5799
|
-
const applyChildLifecycle = (action) => {
|
|
5590
|
+
const applyChildLifecycle = (action, event) => {
|
|
5800
5591
|
if (action === "cancel-drain") {
|
|
5801
|
-
|
|
5592
|
+
terminalAgentEndReceived = event?.type === "agent_end" && event.willRetry !== true && cleanTerminalAssistantStopReceived;
|
|
5802
5593
|
agentSettledReceived = false;
|
|
5803
5594
|
clearFinalDrainTimers();
|
|
5804
5595
|
clearWatchdogTailTimer();
|
|
5596
|
+
if (terminalAgentEndReceived) {
|
|
5597
|
+
startFinalDrain();
|
|
5598
|
+
return;
|
|
5599
|
+
}
|
|
5600
|
+
cleanTerminalAssistantStopReceived = false;
|
|
5601
|
+
compactionAfterFinalOpen = false;
|
|
5805
5602
|
return;
|
|
5806
5603
|
}
|
|
5807
5604
|
if (action === "start-drain") startFinalDrain();
|
|
@@ -5851,7 +5648,7 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5851
5648
|
}
|
|
5852
5649
|
await session?.dispose();
|
|
5853
5650
|
}).catch(() => void 0).then(() => {
|
|
5854
|
-
|
|
5651
|
+
resolve9(code);
|
|
5855
5652
|
});
|
|
5856
5653
|
};
|
|
5857
5654
|
const drainPendingControlEvents = () => {
|
|
@@ -5883,13 +5680,13 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5883
5680
|
};
|
|
5884
5681
|
const recordActiveToolCall = (event, args, now) => {
|
|
5885
5682
|
const key = toolTimeoutCallKey(event, ++activeToolSequence);
|
|
5886
|
-
const
|
|
5683
|
+
const path19 = resolveCurrentPath(event.toolName, args);
|
|
5887
5684
|
const active = {
|
|
5888
5685
|
key,
|
|
5889
5686
|
tool: event.toolName,
|
|
5890
5687
|
args: extractToolArgsPreview(args),
|
|
5891
5688
|
startedAt: now,
|
|
5892
|
-
...
|
|
5689
|
+
...path19 !== void 0 ? { path: path19 } : {}
|
|
5893
5690
|
};
|
|
5894
5691
|
activeToolCalls.set(key, active);
|
|
5895
5692
|
const keys = activeToolKeysByName.get(active.tool) ?? [];
|
|
@@ -6042,11 +5839,17 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
6042
5839
|
if (evt.type === "compaction_start") {
|
|
6043
5840
|
compactionStartedReceived = true;
|
|
6044
5841
|
if (agentSettledReceived) afterCompactionSettlement = true;
|
|
5842
|
+
if (terminalAgentEndReceived) compactionAfterFinalOpen = true;
|
|
6045
5843
|
}
|
|
6046
5844
|
if (evt.type === "compaction_end" && evt.willRetry === true) {
|
|
6047
5845
|
compactionStartedReceived = false;
|
|
6048
5846
|
afterCompactionSettlement = false;
|
|
6049
5847
|
}
|
|
5848
|
+
if (evt.type === "compaction_end" && evt.willRetry !== true && compactionAfterFinalOpen) {
|
|
5849
|
+
compactionAfterFinalOpen = false;
|
|
5850
|
+
clearFinalDrainTimers();
|
|
5851
|
+
startFinalDrain();
|
|
5852
|
+
}
|
|
6050
5853
|
if (evt.type === "turn_start" || evt.type === "agent_start" || evt.type === "auto_retry_start") {
|
|
6051
5854
|
queuedDrainHold = false;
|
|
6052
5855
|
}
|
|
@@ -6073,7 +5876,7 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
6073
5876
|
agentSettledReceived = true;
|
|
6074
5877
|
afterCompactionSettlement = compactionStartedReceived;
|
|
6075
5878
|
}
|
|
6076
|
-
applyChildLifecycle(lifecycleAction);
|
|
5879
|
+
applyChildLifecycle(lifecycleAction, evt);
|
|
6077
5880
|
if (isChildWatchdogStatusEvent(evt)) {
|
|
6078
5881
|
if (!childWatchdog) return;
|
|
6079
5882
|
const next = acceptChildWatchdogEvent({
|
|
@@ -6625,7 +6428,7 @@ ${resolvedOutput.saveError}` : resolvedOutput.saveError;
|
|
|
6625
6428
|
}
|
|
6626
6429
|
function writeStructuredOutputArtifacts(runtime, value, acceptanceReport) {
|
|
6627
6430
|
try {
|
|
6628
|
-
mkdirSync6(
|
|
6431
|
+
mkdirSync6(path10.dirname(runtime.outputPath), { recursive: true });
|
|
6629
6432
|
writeFileSync4(runtime.outputPath, JSON.stringify(value), { mode: 384 });
|
|
6630
6433
|
if (runtime.acceptanceReportPath) {
|
|
6631
6434
|
if (acceptanceReport !== void 0) writeFileSync4(runtime.acceptanceReportPath, JSON.stringify(acceptanceReport), { mode: 384 });
|
|
@@ -6655,7 +6458,7 @@ async function runSyncCompletionInner(runtimeCwd, agents, agentName, task, optio
|
|
|
6655
6458
|
const childSessionName = deriveChildSessionName({ agent: agentName, task });
|
|
6656
6459
|
const agent = agents.find((a) => a.name === agentName);
|
|
6657
6460
|
if (!agent) {
|
|
6658
|
-
const diagnosticContext = options.unknownAgentDiagnosticContext ?? unknownAgentDiagnosticContext(discoverAgents(
|
|
6461
|
+
const diagnosticContext = options.unknownAgentDiagnosticContext ?? unknownAgentDiagnosticContext(discoverAgents(path10.resolve(options.cwd ?? runtimeCwd), "both"));
|
|
6659
6462
|
return redactResultPrompt(withRunContext({
|
|
6660
6463
|
index: options.index ?? 0,
|
|
6661
6464
|
agent: agentName,
|
|
@@ -6748,7 +6551,7 @@ async function runSyncCompletionInner(runtimeCwd, agents, agentName, task, optio
|
|
|
6748
6551
|
${acceptancePrompt}` : task;
|
|
6749
6552
|
options.onEffectivePrompt?.(taskWithAcceptance);
|
|
6750
6553
|
const sessionEnabled = Boolean(options.sessionFile || options.sessionDir) || shareEnabled;
|
|
6751
|
-
if (options.context === "fork" && options.sessionFile &&
|
|
6554
|
+
if (options.context === "fork" && options.sessionFile && existsSync4(options.sessionFile)) {
|
|
6752
6555
|
alignForkedSessionCwd(options.sessionFile, options.cwd ?? runtimeCwd);
|
|
6753
6556
|
}
|
|
6754
6557
|
const skillNames = options.skills ?? agent.skills ?? [];
|
|
@@ -6758,7 +6561,7 @@ ${acceptancePrompt}` : task;
|
|
|
6758
6561
|
skillCwd,
|
|
6759
6562
|
runtimeCwd,
|
|
6760
6563
|
agent.skillPath,
|
|
6761
|
-
agent.filePath ?
|
|
6564
|
+
agent.filePath ? path10.dirname(agent.filePath) : skillCwd
|
|
6762
6565
|
);
|
|
6763
6566
|
if (skillNames.some((skill) => skill.trim() === "pi-subagents") && missingSkills.includes("pi-subagents")) {
|
|
6764
6567
|
return redactResultPrompt(withRunContext({
|
|
@@ -6913,7 +6716,7 @@ ${PROMPT_REDACTED}; live Prompt Audit only.
|
|
|
6913
6716
|
messages: attemptResult.messages ?? [],
|
|
6914
6717
|
error: attemptResult.error,
|
|
6915
6718
|
processSignal: attemptResult.processSignal,
|
|
6916
|
-
sessionAvailable: Boolean(options.sessionFile &&
|
|
6719
|
+
sessionAvailable: Boolean(options.sessionFile && existsSync4(options.sessionFile)),
|
|
6917
6720
|
alreadyResumed: attemptIndex > 0,
|
|
6918
6721
|
stopped: attemptResult.stopped || attemptResult.detached || Boolean(detachedReason) || Boolean(options.workflowChildPermitLaunch) || options.signal?.aborted,
|
|
6919
6722
|
interrupted: attemptResult.interrupted || options.interruptSignal?.aborted,
|
|
@@ -6984,7 +6787,7 @@ ${recovery.diagnostic}` : recovery.diagnostic;
|
|
|
6984
6787
|
result2.outputSaveError = result2.outputSaveError ? `${result2.outputSaveError}
|
|
6985
6788
|
${message}` : message;
|
|
6986
6789
|
}
|
|
6987
|
-
if (options.sessionFile && (
|
|
6790
|
+
if (options.sessionFile && (existsSync4(options.sessionFile) || result2.messages?.length)) {
|
|
6988
6791
|
result2.sessionFile = options.sessionFile;
|
|
6989
6792
|
} else if (shareEnabled && options.sessionDir) {
|
|
6990
6793
|
const sessionFile = findLatestSessionFile(options.sessionDir);
|
|
@@ -7089,8 +6892,8 @@ async function runSync(runtimeCwd, agents, agentName, task, options) {
|
|
|
7089
6892
|
let activeDetachAttempt;
|
|
7090
6893
|
let detachReadyPublished = false;
|
|
7091
6894
|
let resolveReceipt;
|
|
7092
|
-
const receipt = new Promise((
|
|
7093
|
-
resolveReceipt =
|
|
6895
|
+
const receipt = new Promise((resolve9) => {
|
|
6896
|
+
resolveReceipt = resolve9;
|
|
7094
6897
|
});
|
|
7095
6898
|
const originSignal = options.signal;
|
|
7096
6899
|
const originController = new AbortController();
|
|
@@ -7344,8 +7147,8 @@ var WATCHDOG_THINKING_VALUES = ["inherit", ...THINKING_LEVELS];
|
|
|
7344
7147
|
// src/shared/pruned-fork.ts
|
|
7345
7148
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
7346
7149
|
import { createHash as createHash2 } from "node:crypto";
|
|
7347
|
-
import * as
|
|
7348
|
-
import * as
|
|
7150
|
+
import * as fs12 from "node:fs";
|
|
7151
|
+
import * as path11 from "node:path";
|
|
7349
7152
|
var MAX_INHERITED_SESSION_BYTES = 64 * 1024;
|
|
7350
7153
|
var MIN_USEFUL_SPILL_BYTES = 320;
|
|
7351
7154
|
var MAX_ITEM_SUMMARY_CHARS = 160;
|
|
@@ -7374,7 +7177,7 @@ function stableJson(value) {
|
|
|
7374
7177
|
throw new Error(`Pruned fork cannot serialize ${typeof value} transcript data.`);
|
|
7375
7178
|
}
|
|
7376
7179
|
function readEntries(sessionFile) {
|
|
7377
|
-
return
|
|
7180
|
+
return fs12.readFileSync(sessionFile, "utf-8").split("\n").filter((line) => line.trim()).map((line, index) => {
|
|
7378
7181
|
try {
|
|
7379
7182
|
const parsed = JSON.parse(line);
|
|
7380
7183
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || typeof parsed.type !== "string") throw new Error("entry must be an object with a type");
|
|
@@ -7615,17 +7418,17 @@ function prunedForkRecoveryPath(sessionFile) {
|
|
|
7615
7418
|
return `${sessionFile}.pruned-recovery.json`;
|
|
7616
7419
|
}
|
|
7617
7420
|
function writeAtomicPrivate(filePath, content, mode) {
|
|
7618
|
-
const tempFile =
|
|
7421
|
+
const tempFile = path11.join(path11.dirname(filePath), `.${path11.basename(filePath)}.${process.pid}.tmp`);
|
|
7619
7422
|
try {
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7423
|
+
fs12.writeFileSync(tempFile, content, { encoding: "utf-8", mode });
|
|
7424
|
+
fs12.chmodSync(tempFile, mode);
|
|
7425
|
+
fs12.renameSync(tempFile, filePath);
|
|
7623
7426
|
} finally {
|
|
7624
|
-
|
|
7427
|
+
fs12.rmSync(tempFile, { force: true });
|
|
7625
7428
|
}
|
|
7626
7429
|
}
|
|
7627
7430
|
function writeEntriesAtomic(sessionFile, entries) {
|
|
7628
|
-
writeAtomicPrivate(sessionFile, serializedEntries(entries),
|
|
7431
|
+
writeAtomicPrivate(sessionFile, serializedEntries(entries), fs12.statSync(sessionFile).mode & 511);
|
|
7629
7432
|
}
|
|
7630
7433
|
async function pruneForkSessionFile(sessionFile, summarize, options = {}) {
|
|
7631
7434
|
const entries = readEntries(sessionFile);
|
|
@@ -7666,11 +7469,11 @@ async function pruneForkSessionFile(sessionFile, summarize, options = {}) {
|
|
|
7666
7469
|
try {
|
|
7667
7470
|
writeAtomicPrivate(recoveryPath, `${JSON.stringify(payload, null, " ")}
|
|
7668
7471
|
`, 384);
|
|
7669
|
-
const stored = JSON.parse(
|
|
7472
|
+
const stored = JSON.parse(fs12.readFileSync(recoveryPath, "utf-8"));
|
|
7670
7473
|
if (!validateRecovery(stored) || stableJson(stored) !== stableJson(payload)) throw new Error("Pruned fork stored recovery payload failed validation.");
|
|
7671
7474
|
writeEntriesAtomic(sessionFile, entries);
|
|
7672
7475
|
} catch (error) {
|
|
7673
|
-
|
|
7476
|
+
fs12.rmSync(recoveryPath, { force: true });
|
|
7674
7477
|
throw error;
|
|
7675
7478
|
}
|
|
7676
7479
|
return true;
|
|
@@ -7719,11 +7522,11 @@ async function createPrunedForkSessionWriter(ctx, config, signal) {
|
|
|
7719
7522
|
}
|
|
7720
7523
|
|
|
7721
7524
|
// src/runs/foreground/async-dismiss-action.ts
|
|
7722
|
-
import * as
|
|
7723
|
-
import * as
|
|
7525
|
+
import * as fs13 from "node:fs";
|
|
7526
|
+
import * as path12 from "node:path";
|
|
7724
7527
|
function dismissRecoveredWorkflow(state, location) {
|
|
7725
7528
|
const asyncDir = location.asyncDir;
|
|
7726
|
-
const runId = location.resolvedId ?? (asyncDir ?
|
|
7529
|
+
const runId = location.resolvedId ?? (asyncDir ? path12.basename(asyncDir) : "requested");
|
|
7727
7530
|
if (!asyncDir) {
|
|
7728
7531
|
return {
|
|
7729
7532
|
content: [{ type: "text", text: `Recovered workflow '${runId}' has no disk status to dismiss.` }],
|
|
@@ -7762,7 +7565,7 @@ function dismissRecoveredWorkflow(state, location) {
|
|
|
7762
7565
|
}
|
|
7763
7566
|
let latestStatus = status;
|
|
7764
7567
|
const resultPath = resultFilePath(DIRS.results, status.runId);
|
|
7765
|
-
if (
|
|
7568
|
+
if (fs13.existsSync(resultPath)) {
|
|
7766
7569
|
const reconciled = reconcileAsyncRun(asyncDir).status;
|
|
7767
7570
|
if (reconciled && reconciled.state !== "running") {
|
|
7768
7571
|
return {
|
|
@@ -7774,7 +7577,7 @@ function dismissRecoveredWorkflow(state, location) {
|
|
|
7774
7577
|
if (reconciled) latestStatus = reconciled;
|
|
7775
7578
|
}
|
|
7776
7579
|
const dismissed = { ...latestStatus, displayDismissedAt: Date.now() };
|
|
7777
|
-
writeAtomicJson(
|
|
7580
|
+
writeAtomicJson(path12.join(asyncDir, "status.json"), dismissed);
|
|
7778
7581
|
const repaired = reconcileAsyncRun(asyncDir).status;
|
|
7779
7582
|
if (repaired && repaired.state !== "running") {
|
|
7780
7583
|
return {
|
|
@@ -7795,8 +7598,8 @@ function dismissRecoveredWorkflow(state, location) {
|
|
|
7795
7598
|
}
|
|
7796
7599
|
|
|
7797
7600
|
// src/runs/foreground/workflow-detach-reconcile.ts
|
|
7798
|
-
import * as
|
|
7799
|
-
import * as
|
|
7601
|
+
import * as fs14 from "node:fs";
|
|
7602
|
+
import * as path13 from "node:path";
|
|
7800
7603
|
|
|
7801
7604
|
// src/workflows/workflow-settlement.ts
|
|
7802
7605
|
var UNSUPPORTED_DETACHED_WORKFLOW_CONTINUATION = "unsupported-continuation: detached workflow child settled, but JavaScript workflow continuation was not persisted. Resume the workflow explicitly instead of treating the completed child as top-level workflow completion.";
|
|
@@ -8028,9 +7831,9 @@ function workflowResultChildren(status, childRunId, result2, existingResults, re
|
|
|
8028
7831
|
}));
|
|
8029
7832
|
}
|
|
8030
7833
|
function reconcileWorkflowReceipt(status, childRunId, result2, asyncDir, resolution) {
|
|
8031
|
-
const receiptRoot =
|
|
7834
|
+
const receiptRoot = path13.dirname(asyncDir);
|
|
8032
7835
|
const receiptPath = workflowReceiptPath(receiptRoot, status.runId);
|
|
8033
|
-
if (!
|
|
7836
|
+
if (!fs14.existsSync(receiptPath)) return void 0;
|
|
8034
7837
|
const receipt = readWorkflowReceipt(receiptRoot, status.runId);
|
|
8035
7838
|
const step = status.steps?.find((candidate) => candidate.runId === childRunId);
|
|
8036
7839
|
const key = step?.workflowKey;
|
|
@@ -8039,13 +7842,13 @@ function reconcileWorkflowReceipt(status, childRunId, result2, asyncDir, resolut
|
|
|
8039
7842
|
if (!entry) throw new Error(`Workflow receipt '${status.runId}' has no detached child key '${key}'.`);
|
|
8040
7843
|
let resumability;
|
|
8041
7844
|
try {
|
|
8042
|
-
const target = resolveAsyncResumeTarget({ id: childRunId, dir:
|
|
7845
|
+
const target = resolveAsyncResumeTarget({ id: childRunId, dir: path13.join(DIRS.async, childRunId) }, {}, { requireSessionFile: true, sessionId: status.sessionId });
|
|
8043
7846
|
resumability = target.kind === "revive" ? { state: "resumable" } : { state: "not-resumable", reason: "child is still running" };
|
|
8044
7847
|
} catch (error) {
|
|
8045
7848
|
resumability = { state: "not-resumable", reason: error instanceof Error ? error.message : String(error) };
|
|
8046
7849
|
}
|
|
8047
7850
|
const outputReference = result2.savedOutputPath ?? result2.outputReference?.path ?? entry.outputReference;
|
|
8048
|
-
const childStatus = readStatus(
|
|
7851
|
+
const childStatus = readStatus(path13.join(DIRS.async, childRunId));
|
|
8049
7852
|
const externalStep = childStatus?.steps?.length === 1 && childStatus.steps[0]?.runner?.type === "external-cli" ? childStatus.steps[0] : void 0;
|
|
8050
7853
|
const externalRunner = normalizeExternalCliRunnerStatus(result2.runner?.type === "external-cli" ? result2.runner : externalStep?.runner);
|
|
8051
7854
|
const externalProcess = result2.externalProcess ?? externalStep?.externalProcess;
|
|
@@ -8089,9 +7892,9 @@ function reconcileWorkflowReceipt(status, childRunId, result2, asyncDir, resolut
|
|
|
8089
7892
|
return { receipt: next, path: writeWorkflowReceipt(asyncDir, next) };
|
|
8090
7893
|
}
|
|
8091
7894
|
function appendDetachedWorkflowEvent(asyncDir, event) {
|
|
8092
|
-
const eventsPath =
|
|
7895
|
+
const eventsPath = path13.join(asyncDir, "events.jsonl");
|
|
8093
7896
|
try {
|
|
8094
|
-
|
|
7897
|
+
fs14.appendFileSync(eventsPath, `${JSON.stringify(event)}
|
|
8095
7898
|
`, "utf-8");
|
|
8096
7899
|
} catch (error) {
|
|
8097
7900
|
console.error(`Failed to append detached workflow event '${eventsPath}':`, error);
|
|
@@ -8099,7 +7902,7 @@ function appendDetachedWorkflowEvent(asyncDir, event) {
|
|
|
8099
7902
|
}
|
|
8100
7903
|
function reconcileDetachedWorkflowChildCompletion(input) {
|
|
8101
7904
|
const job = input.state.asyncJobs.get(input.workflowRunId);
|
|
8102
|
-
const asyncDir = job?.asyncDir ??
|
|
7905
|
+
const asyncDir = job?.asyncDir ?? path13.join(DIRS.async, input.workflowRunId);
|
|
8103
7906
|
const status = readStatus(asyncDir);
|
|
8104
7907
|
if (!status) return false;
|
|
8105
7908
|
const matchingControls = input.workflowKey === void 0 ? [] : [...input.state.foregroundControls?.values() ?? []].filter(
|
|
@@ -8124,7 +7927,7 @@ function reconcileDetachedWorkflowChildCompletion(input) {
|
|
|
8124
7927
|
const resultPath = resultFilePath(DIRS.results, input.workflowRunId);
|
|
8125
7928
|
let existing;
|
|
8126
7929
|
try {
|
|
8127
|
-
const parsed = JSON.parse(
|
|
7930
|
+
const parsed = JSON.parse(fs14.readFileSync(resultPath, "utf-8"));
|
|
8128
7931
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) existing = parsed;
|
|
8129
7932
|
} catch (error) {
|
|
8130
7933
|
if (error.code !== "ENOENT") throw error;
|
|
@@ -8170,7 +7973,7 @@ function reconcileDetachedWorkflowChildCompletion(input) {
|
|
|
8170
7973
|
terminalOutcome: receipt?.terminalOutcome,
|
|
8171
7974
|
eventMetadata: { reconciledFromDetachedChild: input.childRunId }
|
|
8172
7975
|
});
|
|
8173
|
-
writeAtomicJson(
|
|
7976
|
+
writeAtomicJson(path13.join(asyncDir, "status.json"), plan.status);
|
|
8174
7977
|
updateActiveRunIndex(asyncDir, plan.status.state, plan.status.toolCallId);
|
|
8175
7978
|
if (job) {
|
|
8176
7979
|
job.status = plan.status.state;
|
|
@@ -8222,14 +8025,14 @@ function reconcileDetachedWorkflowChildCompletion(input) {
|
|
|
8222
8025
|
}
|
|
8223
8026
|
|
|
8224
8027
|
// src/runs/background/chain-root-attachment.ts
|
|
8225
|
-
import * as
|
|
8226
|
-
import * as
|
|
8028
|
+
import * as fs15 from "node:fs";
|
|
8029
|
+
import * as path14 from "node:path";
|
|
8227
8030
|
var TERMINAL_STATES = /* @__PURE__ */ new Set(["complete", "failed", "partial", "paused", "stopped"]);
|
|
8228
8031
|
var TERMINAL_STEP_STATUSES = /* @__PURE__ */ new Set(["complete", "completed", "failed", "partial", "paused", "stopped"]);
|
|
8229
8032
|
function readResultFile(resultPath) {
|
|
8230
8033
|
let raw;
|
|
8231
8034
|
try {
|
|
8232
|
-
raw =
|
|
8035
|
+
raw = fs15.readFileSync(resultPath, "utf-8");
|
|
8233
8036
|
} catch (error) {
|
|
8234
8037
|
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
8235
8038
|
return void 0;
|
|
@@ -8241,7 +8044,7 @@ function readResultFile(resultPath) {
|
|
|
8241
8044
|
function readImportedResultFile(root, status) {
|
|
8242
8045
|
const direct = readResultFile(root.resultPath);
|
|
8243
8046
|
if (direct || !status?.sessionId) return direct;
|
|
8244
|
-
const indexedPath = resultPayloadPathForSessionRun(
|
|
8047
|
+
const indexedPath = resultPayloadPathForSessionRun(path14.dirname(root.resultPath), status.sessionId, root.runId);
|
|
8245
8048
|
return indexedPath ? readResultFile(indexedPath) : void 0;
|
|
8246
8049
|
}
|
|
8247
8050
|
function selectedStatusStep(status, index) {
|
|
@@ -8376,10 +8179,10 @@ async function waitForImportedAsyncRoot(root, options = {}) {
|
|
|
8376
8179
|
} else {
|
|
8377
8180
|
terminalSince = void 0;
|
|
8378
8181
|
}
|
|
8379
|
-
if (!status && !
|
|
8182
|
+
if (!status && !fs15.existsSync(root.asyncDir)) {
|
|
8380
8183
|
throw new Error(`Attached async root '${root.runId}' directory does not exist: ${root.asyncDir}`);
|
|
8381
8184
|
}
|
|
8382
|
-
await new Promise((
|
|
8185
|
+
await new Promise((resolve9) => setTimeout(resolve9, pollIntervalMs));
|
|
8383
8186
|
}
|
|
8384
8187
|
}
|
|
8385
8188
|
function resolveAsyncRootResultPath(resultsDir, runId) {
|
|
@@ -8387,7 +8190,7 @@ function resolveAsyncRootResultPath(resultsDir, runId) {
|
|
|
8387
8190
|
}
|
|
8388
8191
|
|
|
8389
8192
|
// src/runs/background/await-async-run.ts
|
|
8390
|
-
import * as
|
|
8193
|
+
import * as fs16 from "node:fs";
|
|
8391
8194
|
function errorMessage2(error) {
|
|
8392
8195
|
return error instanceof Error ? error.message : String(error);
|
|
8393
8196
|
}
|
|
@@ -8395,7 +8198,7 @@ function runnerExitedWithoutResult(asyncDir, runId, resultPath) {
|
|
|
8395
8198
|
const status = readStatus(asyncDir);
|
|
8396
8199
|
if (!status || status.state !== "running" && status.state !== "queued" || typeof status.pid !== "number") return false;
|
|
8397
8200
|
if (checkRecordedPidLiveness({ pid: status.pid, pidNamespaceScope: status.pidNamespaceScope, bootId: status.bootId }) !== "dead") return false;
|
|
8398
|
-
return !
|
|
8201
|
+
return !fs16.existsSync(resultPath) && !(status.sessionId && resultPayloadPathForSessionRun(asyncDir, status.sessionId, runId));
|
|
8399
8202
|
}
|
|
8400
8203
|
async function awaitExistingAsyncRun(asyncDir, runId, signal) {
|
|
8401
8204
|
signal.throwIfAborted();
|
|
@@ -8430,7 +8233,7 @@ function claimWorkflowAwaitedResult(asyncDir, claimant, read) {
|
|
|
8430
8233
|
try {
|
|
8431
8234
|
return withResultRunLease(asyncDir, read.runId, () => {
|
|
8432
8235
|
if (resultSnapshotReplaced(resultPath, read)) return void 0;
|
|
8433
|
-
|
|
8236
|
+
fs16.renameSync(resultPath, claimedPath);
|
|
8434
8237
|
return claimedPath;
|
|
8435
8238
|
});
|
|
8436
8239
|
} catch {
|
|
@@ -8439,8 +8242,8 @@ function claimWorkflowAwaitedResult(asyncDir, claimant, read) {
|
|
|
8439
8242
|
}
|
|
8440
8243
|
|
|
8441
8244
|
// src/runs/background/run-status.ts
|
|
8442
|
-
import * as
|
|
8443
|
-
import * as
|
|
8245
|
+
import * as fs17 from "node:fs";
|
|
8246
|
+
import * as path15 from "node:path";
|
|
8444
8247
|
var STRUCTURED_PREVIEW_WITH_PATH_CHARS = 500;
|
|
8445
8248
|
function formatProcessTerminal(value) {
|
|
8446
8249
|
if (!value) return "missing";
|
|
@@ -8450,7 +8253,7 @@ function debugProcessTerminal(asyncDir, status) {
|
|
|
8450
8253
|
const expected = { runId: status.runId, runnerProcessInstanceId: status.processTerminal?.runnerProcessInstanceId };
|
|
8451
8254
|
return {
|
|
8452
8255
|
sidecar: readProcessTerminal(asyncDir, expected),
|
|
8453
|
-
overlay: sanitizeProcessTerminal(status.processTerminal, expected,
|
|
8256
|
+
overlay: sanitizeProcessTerminal(status.processTerminal, expected, path15.join(asyncDir, "status.json"))
|
|
8454
8257
|
};
|
|
8455
8258
|
}
|
|
8456
8259
|
function formatCapacityOwner(inspect) {
|
|
@@ -8484,9 +8287,9 @@ function formatRunLifecycleDebug(input) {
|
|
|
8484
8287
|
"Run lifecycle debug",
|
|
8485
8288
|
`Run: ${status.runId}`,
|
|
8486
8289
|
`Dir: ${asyncDir}`,
|
|
8487
|
-
`Status file: ${
|
|
8290
|
+
`Status file: ${path15.join(asyncDir, "status.json")}`,
|
|
8488
8291
|
status.workflowReceiptPath ? `Workflow receipt: ${status.workflowReceiptPath}` : void 0,
|
|
8489
|
-
`Process terminal file: ${
|
|
8292
|
+
`Process terminal file: ${path15.join(asyncDir, "process-terminal.json")}`,
|
|
8490
8293
|
`Session: ${status.sessionId ?? "unknown"}`,
|
|
8491
8294
|
`State: ${status.state}`,
|
|
8492
8295
|
`Mode: ${status.mode}`,
|
|
@@ -8501,7 +8304,7 @@ function formatRunLifecycleDebug(input) {
|
|
|
8501
8304
|
return lines.join("\n");
|
|
8502
8305
|
}
|
|
8503
8306
|
function hasExistingSessionFile(value) {
|
|
8504
|
-
return typeof value === "string" &&
|
|
8307
|
+
return typeof value === "string" && fs17.existsSync(value);
|
|
8505
8308
|
}
|
|
8506
8309
|
function formatResumeGuidance(runId, children, fallbackSessionFile, options = {}) {
|
|
8507
8310
|
if (options.stopped) return "Resume: unavailable; stopped runs are not resumable. Start a new run instead.";
|
|
@@ -8564,9 +8367,9 @@ function externalJobFollowUpSupported(provider) {
|
|
|
8564
8367
|
}
|
|
8565
8368
|
function rememberedForegroundChildOutput(child) {
|
|
8566
8369
|
const outputPath = child.artifactPaths?.outputPath ?? child.savedOutputPath;
|
|
8567
|
-
if (outputPath &&
|
|
8370
|
+
if (outputPath && fs17.existsSync(outputPath)) {
|
|
8568
8371
|
try {
|
|
8569
|
-
const artifactOutput =
|
|
8372
|
+
const artifactOutput = fs17.readFileSync(outputPath, "utf-8").trim();
|
|
8570
8373
|
if (artifactOutput) return artifactOutput;
|
|
8571
8374
|
} catch {
|
|
8572
8375
|
}
|
|
@@ -8865,8 +8668,8 @@ No running work was terminated.` }],
|
|
|
8865
8668
|
};
|
|
8866
8669
|
}
|
|
8867
8670
|
const effectiveRunId = status?.runId ?? resolvedId ?? "unknown";
|
|
8868
|
-
const logPath =
|
|
8869
|
-
const eventsPath =
|
|
8671
|
+
const logPath = path15.join(asyncDir, `subagent-log-${effectiveRunId}.md`);
|
|
8672
|
+
const eventsPath = path15.join(asyncDir, "events.jsonl");
|
|
8870
8673
|
if (status) {
|
|
8871
8674
|
if (params.action === "debug.run") {
|
|
8872
8675
|
const { sidecar, overlay } = debugProcessTerminal(asyncDir, status);
|
|
@@ -8915,7 +8718,7 @@ No running work was terminated.` }],
|
|
|
8915
8718
|
const updated = status.lastUpdate ? new Date(status.lastUpdate).toISOString() : "n/a";
|
|
8916
8719
|
const statusActivityText = status.state === "running" ? formatActivityLabel(status.lastActivityAt, status.activityState) : void 0;
|
|
8917
8720
|
const steeringText = formatSteeringSummary(status);
|
|
8918
|
-
const processTerminal = readProcessTerminal(asyncDir, { runId: status.runId, runnerProcessInstanceId: status.processTerminal?.runnerProcessInstanceId }) ?? sanitizeProcessTerminal(status.processTerminal, { runId: status.runId, runnerProcessInstanceId: status.processTerminal?.runnerProcessInstanceId },
|
|
8721
|
+
const processTerminal = readProcessTerminal(asyncDir, { runId: status.runId, runnerProcessInstanceId: status.processTerminal?.runnerProcessInstanceId }) ?? sanitizeProcessTerminal(status.processTerminal, { runId: status.runId, runnerProcessInstanceId: status.processTerminal?.runnerProcessInstanceId }, path15.join(asyncDir, "status.json"));
|
|
8919
8722
|
const runFanoutBudgetDescriptor = readRunFanoutBudgetDescriptor(asyncDir);
|
|
8920
8723
|
const runFanoutBudget = runFanoutBudgetDescriptor ? getRunFanoutBudgetSnapshot(runFanoutBudgetDescriptor) : status.runFanoutBudget;
|
|
8921
8724
|
let missionId;
|
|
@@ -8951,7 +8754,7 @@ Extensions denied: ${status.capabilityCeiling.denyExtensions ? "yes" : "no"} (so
|
|
|
8951
8754
|
status.parentWorkflowRunId ? `Workflow parent: ${status.parentWorkflowRunId}${status.workflowKey ? ` (${status.workflowKey})` : ""}` : void 0,
|
|
8952
8755
|
status.mode === "workflow" && workflowReturnPreview !== void 0 ? `Return: ${workflowReturnPreview}` : void 0,
|
|
8953
8756
|
status.mode === "workflow" && workflowEmitPreview !== void 0 ? `Latest emit: ${workflowEmitPreview}` : void 0,
|
|
8954
|
-
status.mode === "workflow" && (workflowReturnPreview?.endsWith("\u2026") || workflowEmitPreview?.endsWith("\u2026")) ? `Full return value and emits: ${
|
|
8757
|
+
status.mode === "workflow" && (workflowReturnPreview?.endsWith("\u2026") || workflowEmitPreview?.endsWith("\u2026")) ? `Full return value and emits: ${path15.join(asyncDir, "status.json")} (workflow.value, workflow.emits)` : void 0,
|
|
8955
8758
|
`Progress: ${progressLabel}`,
|
|
8956
8759
|
...status.mode === "workflow" ? formatWorkflowChecklistText(projectWorkflowChecklist({
|
|
8957
8760
|
graph: status.workflowGraph,
|
|
@@ -8969,7 +8772,7 @@ Extensions denied: ${status.capabilityCeiling.denyExtensions ? "yes" : "no"} (so
|
|
|
8969
8772
|
outputPath ? `Output: ${outputPath}` : void 0,
|
|
8970
8773
|
status.parallelHandoff ? `Parallel handoff: ${status.parallelHandoff.path}` : void 0,
|
|
8971
8774
|
reconciliation.message ? `Diagnosis: ${reconciliation.message}` : void 0,
|
|
8972
|
-
reconciliation.resultPath &&
|
|
8775
|
+
reconciliation.resultPath && fs17.existsSync(reconciliation.resultPath) ? `Result: ${reconciliation.resultPath}` : void 0
|
|
8973
8776
|
].filter((line) => Boolean(line));
|
|
8974
8777
|
const liveWorkflowControls = status.mode === "workflow" && deps.state?.currentSessionId === status.sessionId && deps.state?.workflowControllers?.has(status.runId) ? [...deps.state?.foregroundControls.values() ?? []].filter((control) => control.parentWorkflowRunId === status.runId && control.sessionId === status.sessionId && (control.activeChildren?.size ?? 0) > 0) : [];
|
|
8975
8778
|
let hasExternalJobFollowUpHint = false;
|
|
@@ -9031,8 +8834,8 @@ Extensions denied: ${status.capabilityCeiling.denyExtensions ? "yes" : "no"} (so
|
|
|
9031
8834
|
}
|
|
9032
8835
|
}
|
|
9033
8836
|
lines.push(...formatNestedRunStatusLines(step.children, { indent: " ", commandHints: true, maxLines: 20 }));
|
|
9034
|
-
const stepOutputPath =
|
|
9035
|
-
if (stepOutputPath !== outputPath &&
|
|
8837
|
+
const stepOutputPath = path15.join(asyncDir, `output-${index}.log`);
|
|
8838
|
+
if (stepOutputPath !== outputPath && fs17.existsSync(stepOutputPath)) lines.push(` Output: ${stepOutputPath}`);
|
|
9036
8839
|
if (step.status === "running" && step.runner?.type !== "external-cli" && step.runner?.type !== "external-job" && status.mode !== "workflow") {
|
|
9037
8840
|
lines.push(` Intercom target: ${resolveSubagentIntercomTarget(status.runId, step.agent, index)} (if registered)`);
|
|
9038
8841
|
lines.push(` Steer: subagent({ action: "steer", id: "${status.runId}", message: "...", options: { index: ${index} } })`);
|
|
@@ -9065,8 +8868,8 @@ Extensions denied: ${status.capabilityCeiling.denyExtensions ? "yes" : "no"} (so
|
|
|
9065
8868
|
if (status.state !== "running") {
|
|
9066
8869
|
lines.push(allExternal ? hasExternalJobFollowUpHint ? "Resume: use the external-job follow-up hint above." : "Resume: unavailable; external runners do not persist Pi sessions." : formatResumeGuidance(status.runId, status.steps ?? [], status.sessionFile, { stopped: status.state === "stopped" || status.stopped === true, otherSession: foreign(status.sessionId) }));
|
|
9067
8870
|
}
|
|
9068
|
-
if (
|
|
9069
|
-
if (
|
|
8871
|
+
if (fs17.existsSync(logPath)) lines.push(`Log: ${logPath}`);
|
|
8872
|
+
if (fs17.existsSync(eventsPath)) lines.push(`Events: ${eventsPath}`);
|
|
9070
8873
|
return { content: [{ type: "text", text: lines.join("\n") }], details: { mode: "single", runId: status.runId, ...status.toolCallId ? { toolCallId: status.toolCallId } : {}, results: [], ...status.workflowReceiptPath ? { workflowReceiptPath: status.workflowReceiptPath } : {}, ...status.preflight ? { preflight: status.preflight } : {}, ...status.workflow?.preflightWarnings?.length ? { preflightWarnings: status.workflow.preflightWarnings } : {}, ...workflowChildren ? { workflowChildren } : {}, ...workflowTerminalProof ? { workflowTerminalProof } : {}, ...runFanoutBudget ? { runFanoutBudget } : {}, ...processTerminal ? { lifecycleStatus: { processTerminal } } : {} } };
|
|
9071
8874
|
}
|
|
9072
8875
|
}
|
|
@@ -9079,7 +8882,7 @@ Extensions denied: ${status.capabilityCeiling.denyExtensions ? "yes" : "no"} (so
|
|
|
9079
8882
|
};
|
|
9080
8883
|
}
|
|
9081
8884
|
try {
|
|
9082
|
-
const raw =
|
|
8885
|
+
const raw = fs17.readFileSync(resultPath, "utf-8");
|
|
9083
8886
|
const data = JSON.parse(raw);
|
|
9084
8887
|
if (params.view === "transcript") {
|
|
9085
8888
|
try {
|
|
@@ -9172,16 +8975,16 @@ function applyForceTopLevelAsyncOverride(params, depth, forceTopLevelAsync) {
|
|
|
9172
8975
|
|
|
9173
8976
|
// src/runs/shared/worktree-cleanup-apply.ts
|
|
9174
8977
|
import { createHash as createHash5 } from "node:crypto";
|
|
9175
|
-
import { spawnSync as
|
|
9176
|
-
import * as
|
|
9177
|
-
import * as
|
|
8978
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
8979
|
+
import * as fs19 from "node:fs";
|
|
8980
|
+
import * as path17 from "node:path";
|
|
9178
8981
|
|
|
9179
8982
|
// src/runs/shared/worktree-cleanup-plan.ts
|
|
9180
8983
|
import { createHash as createHash4, randomUUID as randomUUID4 } from "node:crypto";
|
|
9181
|
-
import { spawnSync
|
|
9182
|
-
import * as
|
|
8984
|
+
import { spawnSync } from "node:child_process";
|
|
8985
|
+
import * as fs18 from "node:fs";
|
|
9183
8986
|
import * as os2 from "node:os";
|
|
9184
|
-
import * as
|
|
8987
|
+
import * as path16 from "node:path";
|
|
9185
8988
|
var WORKTREE_CLEANUP_PLAN_VERSION = 1;
|
|
9186
8989
|
var WORKTREE_CLEANUP_PLAN_TTL_MS = 30 * 60 * 1e3;
|
|
9187
8990
|
var MAX_DISCOVERED_HANDOFFS = 256;
|
|
@@ -9189,7 +8992,7 @@ var MAX_PLAN_ENTRIES = 512;
|
|
|
9189
8992
|
var MAX_METADATA_FILE_BYTES = 2 * 1024 * 1024;
|
|
9190
8993
|
function runGit(cwd, args) {
|
|
9191
8994
|
try {
|
|
9192
|
-
const result2 =
|
|
8995
|
+
const result2 = spawnSync("git", ["-C", cwd, ...args], { encoding: "utf-8", windowsHide: true });
|
|
9193
8996
|
return {
|
|
9194
8997
|
stdout: result2.stdout ?? "",
|
|
9195
8998
|
stderr: result2.stderr ?? "",
|
|
@@ -9213,7 +9016,7 @@ function runGitChecked(cwd, args) {
|
|
|
9213
9016
|
if (result2.status !== 0) throw new Error(gitFailure(result2, `git -C ${cwd} ${args.join(" ")}`));
|
|
9214
9017
|
return result2.stdout.trim();
|
|
9215
9018
|
}
|
|
9216
|
-
function realpathExisting(candidate, nativeRealpath =
|
|
9019
|
+
function realpathExisting(candidate, nativeRealpath = fs18.realpathSync.native, fallbackRealpath = fs18.realpathSync) {
|
|
9217
9020
|
try {
|
|
9218
9021
|
return nativeRealpath(candidate);
|
|
9219
9022
|
} catch {
|
|
@@ -9221,29 +9024,29 @@ function realpathExisting(candidate, nativeRealpath = fs19.realpathSync.native,
|
|
|
9221
9024
|
}
|
|
9222
9025
|
}
|
|
9223
9026
|
function comparablePath(candidate) {
|
|
9224
|
-
let current =
|
|
9027
|
+
let current = path16.resolve(candidate);
|
|
9225
9028
|
const missingSegments = [];
|
|
9226
9029
|
let canonical = current;
|
|
9227
9030
|
while (true) {
|
|
9228
9031
|
try {
|
|
9229
|
-
canonical =
|
|
9032
|
+
canonical = path16.join(realpathExisting(current), ...missingSegments.reverse());
|
|
9230
9033
|
break;
|
|
9231
9034
|
} catch {
|
|
9232
|
-
const parent =
|
|
9035
|
+
const parent = path16.dirname(current);
|
|
9233
9036
|
if (parent === current) break;
|
|
9234
|
-
missingSegments.push(
|
|
9037
|
+
missingSegments.push(path16.basename(current));
|
|
9235
9038
|
current = parent;
|
|
9236
9039
|
}
|
|
9237
9040
|
}
|
|
9238
|
-
const normalized =
|
|
9041
|
+
const normalized = path16.normalize(canonical);
|
|
9239
9042
|
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
9240
9043
|
}
|
|
9241
9044
|
function samePath(left, right) {
|
|
9242
9045
|
if (comparablePath(left) === comparablePath(right)) return true;
|
|
9243
9046
|
if (process.platform !== "win32") return false;
|
|
9244
9047
|
try {
|
|
9245
|
-
const leftStat =
|
|
9246
|
-
const rightStat =
|
|
9048
|
+
const leftStat = fs18.statSync(left, { bigint: true });
|
|
9049
|
+
const rightStat = fs18.statSync(right, { bigint: true });
|
|
9247
9050
|
return leftStat.dev !== 0n && leftStat.ino !== 0n && leftStat.dev === rightStat.dev && leftStat.ino === rightStat.ino;
|
|
9248
9051
|
} catch {
|
|
9249
9052
|
return false;
|
|
@@ -9253,15 +9056,15 @@ function resolveExistingPath(candidate) {
|
|
|
9253
9056
|
try {
|
|
9254
9057
|
return realpathExisting(candidate);
|
|
9255
9058
|
} catch {
|
|
9256
|
-
return
|
|
9059
|
+
return path16.resolve(candidate);
|
|
9257
9060
|
}
|
|
9258
9061
|
}
|
|
9259
9062
|
function pathInside(root, candidate, strict = false) {
|
|
9260
9063
|
const normalizedRoot = comparablePath(root);
|
|
9261
9064
|
const normalizedCandidate = comparablePath(candidate);
|
|
9262
9065
|
if (!strict && normalizedRoot === normalizedCandidate) return true;
|
|
9263
|
-
const relative3 =
|
|
9264
|
-
return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(`..${
|
|
9066
|
+
const relative3 = path16.relative(normalizedRoot, normalizedCandidate);
|
|
9067
|
+
return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(`..${path16.sep}`) && !path16.isAbsolute(relative3);
|
|
9265
9068
|
}
|
|
9266
9069
|
function sha2562(value) {
|
|
9267
9070
|
return createHash4("sha256").update(value, "utf-8").digest("hex");
|
|
@@ -9273,7 +9076,7 @@ function validatePlanId(planId) {
|
|
|
9273
9076
|
return planId;
|
|
9274
9077
|
}
|
|
9275
9078
|
function resolveCleanupRepoRoot(repo) {
|
|
9276
|
-
const requested =
|
|
9079
|
+
const requested = path16.resolve(repo);
|
|
9277
9080
|
const toplevel = runGitChecked(requested, ["rev-parse", "--show-toplevel"]);
|
|
9278
9081
|
return realpathExisting(toplevel);
|
|
9279
9082
|
}
|
|
@@ -9281,17 +9084,17 @@ function resolveCleanupBaseDir(repoRoot, configuredBaseDir) {
|
|
|
9281
9084
|
const raw = configuredBaseDir ?? process.env.PI_SUBAGENTS_WORKTREE_DIR;
|
|
9282
9085
|
let dedicatedRoot;
|
|
9283
9086
|
if (raw === void 0 || configuredBaseDir === void 0 && !raw.trim()) {
|
|
9284
|
-
dedicatedRoot =
|
|
9087
|
+
dedicatedRoot = path16.join(path16.dirname(repoRoot), "worktrees");
|
|
9285
9088
|
} else {
|
|
9286
9089
|
const trimmed = raw.trim();
|
|
9287
9090
|
if (!trimmed) throw new Error("worktree base directory cannot be empty");
|
|
9288
|
-
const expanded = trimmed.startsWith("~/") ?
|
|
9289
|
-
dedicatedRoot =
|
|
9091
|
+
const expanded = trimmed.startsWith("~/") ? path16.join(os2.homedir(), trimmed.slice(2)) : trimmed;
|
|
9092
|
+
dedicatedRoot = path16.isAbsolute(expanded) ? expanded : path16.resolve(repoRoot, expanded);
|
|
9290
9093
|
}
|
|
9291
|
-
return
|
|
9094
|
+
return path16.resolve(path16.join(dedicatedRoot, path16.basename(repoRoot)));
|
|
9292
9095
|
}
|
|
9293
9096
|
function cleanupContainmentInvalid(repoRoot, projectDir) {
|
|
9294
|
-
const extensionsDir =
|
|
9097
|
+
const extensionsDir = path16.join(getAgentDir(), "extensions");
|
|
9295
9098
|
return samePath(projectDir, repoRoot) || pathInside(repoRoot, projectDir, true) || pathInside(extensionsDir, projectDir);
|
|
9296
9099
|
}
|
|
9297
9100
|
function parseGitWorktreeList(raw) {
|
|
@@ -9330,14 +9133,14 @@ function isRegisteredCleanupWorktree(repoRoot, candidate) {
|
|
|
9330
9133
|
return listGitWorktrees(repoRoot).some((tree) => samePath(tree.path, candidate));
|
|
9331
9134
|
}
|
|
9332
9135
|
function readJsonFile(filePath) {
|
|
9333
|
-
return JSON.parse(
|
|
9136
|
+
return JSON.parse(fs18.readFileSync(filePath, "utf-8"));
|
|
9334
9137
|
}
|
|
9335
9138
|
function isRecord(value) {
|
|
9336
9139
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
9337
9140
|
}
|
|
9338
9141
|
function readManifest(manifestPath) {
|
|
9339
9142
|
try {
|
|
9340
|
-
const stat =
|
|
9143
|
+
const stat = fs18.lstatSync(manifestPath);
|
|
9341
9144
|
if (stat.isSymbolicLink() || !stat.isFile()) return { error: `parallel handoff manifest is not a regular file: ${manifestPath}` };
|
|
9342
9145
|
if (stat.size > MAX_METADATA_FILE_BYTES) return { error: `parallel handoff manifest exceeds the ${MAX_METADATA_FILE_BYTES}-byte limit: ${manifestPath}` };
|
|
9343
9146
|
const parsed = readParallelHandoffManifest(manifestPath);
|
|
@@ -9355,22 +9158,22 @@ function discoverHandoffPaths(repoRoot, input) {
|
|
|
9355
9158
|
const paths = [];
|
|
9356
9159
|
const warnings = [];
|
|
9357
9160
|
const add = (candidate) => {
|
|
9358
|
-
const resolved =
|
|
9161
|
+
const resolved = path16.isAbsolute(candidate) ? path16.resolve(candidate) : path16.resolve(repoRoot, candidate);
|
|
9359
9162
|
if (!paths.some((existing) => comparablePath(existing) === comparablePath(resolved))) paths.push(resolved);
|
|
9360
9163
|
};
|
|
9361
9164
|
if (input.handoffPath) add(input.handoffPath);
|
|
9362
9165
|
for (const candidate of input.handoffPaths ?? []) add(candidate);
|
|
9363
|
-
const artifactsDir =
|
|
9364
|
-
const projectHandoffPath =
|
|
9365
|
-
if (
|
|
9366
|
-
const handoffsDir =
|
|
9166
|
+
const artifactsDir = path16.join(repoRoot, PROJECT_SUBAGENTS_RELATIVE_DIR, "artifacts");
|
|
9167
|
+
const projectHandoffPath = path16.join(artifactsDir, "handoff.json");
|
|
9168
|
+
if (fs18.existsSync(projectHandoffPath)) add(projectHandoffPath);
|
|
9169
|
+
const handoffsDir = path16.join(artifactsDir, "handoffs");
|
|
9367
9170
|
try {
|
|
9368
|
-
const stat =
|
|
9171
|
+
const stat = fs18.lstatSync(handoffsDir);
|
|
9369
9172
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
9370
9173
|
warnings.push(`ignored non-directory handoff metadata root: ${handoffsDir}`);
|
|
9371
9174
|
} else {
|
|
9372
|
-
const entries =
|
|
9373
|
-
for (const entry of entries) add(
|
|
9175
|
+
const entries = fs18.readdirSync(handoffsDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name).sort().slice(0, MAX_DISCOVERED_HANDOFFS);
|
|
9176
|
+
for (const entry of entries) add(path16.join(handoffsDir, entry));
|
|
9374
9177
|
if (entries.length === MAX_DISCOVERED_HANDOFFS) warnings.push(`handoff metadata discovery capped at ${MAX_DISCOVERED_HANDOFFS} files`);
|
|
9375
9178
|
}
|
|
9376
9179
|
} catch (error) {
|
|
@@ -9380,9 +9183,9 @@ function discoverHandoffPaths(repoRoot, input) {
|
|
|
9380
9183
|
return { paths: paths.sort((left, right) => comparablePath(left).localeCompare(comparablePath(right))), warnings: [...new Set(warnings)].sort() };
|
|
9381
9184
|
}
|
|
9382
9185
|
function readStatusBesideManifest(manifestPath) {
|
|
9383
|
-
const statusPath =
|
|
9186
|
+
const statusPath = path16.join(path16.dirname(manifestPath), "status.json");
|
|
9384
9187
|
try {
|
|
9385
|
-
const stat =
|
|
9188
|
+
const stat = fs18.lstatSync(statusPath);
|
|
9386
9189
|
if (stat.isSymbolicLink() || !stat.isFile()) return { statusPath, error: `async status is not a regular file: ${statusPath}` };
|
|
9387
9190
|
if (stat.size > MAX_METADATA_FILE_BYTES) return { statusPath, error: `async status exceeds the ${MAX_METADATA_FILE_BYTES}-byte limit: ${statusPath}` };
|
|
9388
9191
|
const parsed = readJsonFile(statusPath);
|
|
@@ -9443,7 +9246,7 @@ function loadMetadata(input, repoRoot) {
|
|
|
9443
9246
|
continue;
|
|
9444
9247
|
}
|
|
9445
9248
|
const manifestRoot = resolveExistingPath(manifestPath);
|
|
9446
|
-
if (manifestRoot !== manifestPath &&
|
|
9249
|
+
if (manifestRoot !== manifestPath && fs18.lstatSync(manifestPath).isSymbolicLink()) {
|
|
9447
9250
|
warnings.push(`ignored symlinked handoff manifest: ${manifestPath}`);
|
|
9448
9251
|
continue;
|
|
9449
9252
|
}
|
|
@@ -9452,9 +9255,9 @@ function loadMetadata(input, repoRoot) {
|
|
|
9452
9255
|
return { records, paths: discovered.paths, warnings: [...new Set(warnings)].sort() };
|
|
9453
9256
|
}
|
|
9454
9257
|
function inspectPath(candidate) {
|
|
9455
|
-
const resolved =
|
|
9258
|
+
const resolved = path16.resolve(candidate);
|
|
9456
9259
|
try {
|
|
9457
|
-
const stat =
|
|
9260
|
+
const stat = fs18.lstatSync(resolved);
|
|
9458
9261
|
if (stat.isSymbolicLink()) return { resolved, missing: false, symlink: true, directory: false };
|
|
9459
9262
|
return { resolved, realpath: realpathExisting(resolved), missing: false, symlink: false, directory: stat.isDirectory() };
|
|
9460
9263
|
} catch (error) {
|
|
@@ -9464,7 +9267,7 @@ function inspectPath(candidate) {
|
|
|
9464
9267
|
}
|
|
9465
9268
|
}
|
|
9466
9269
|
function metadataPath(manifestPath, candidate) {
|
|
9467
|
-
return
|
|
9270
|
+
return path16.isAbsolute(candidate) ? path16.resolve(candidate) : path16.resolve(path16.dirname(manifestPath), candidate);
|
|
9468
9271
|
}
|
|
9469
9272
|
function metadataRecordPath(record2) {
|
|
9470
9273
|
return metadataPath(record2.manifestPath, record2.task.path);
|
|
@@ -9505,7 +9308,7 @@ function inspectRunState(record2, now, foregroundRunOwnership) {
|
|
|
9505
9308
|
}
|
|
9506
9309
|
}
|
|
9507
9310
|
try {
|
|
9508
|
-
const markerAge = activeRunMarkerAgeMs(
|
|
9311
|
+
const markerAge = activeRunMarkerAgeMs(path16.dirname(record2.statusPath), now);
|
|
9509
9312
|
if (markerAge !== void 0) {
|
|
9510
9313
|
if (markerAge <= DEFAULT_STALE_TERMINAL_ACTIVE_MARKER_MS) return { kind: "active", reason: "owning run still has a recent active marker" };
|
|
9511
9314
|
return { kind: "stale", reason: `terminal active marker is stale (${Math.floor(markerAge / 1e3)}s old)` };
|
|
@@ -9566,7 +9369,7 @@ function isPatchCaptured(record2, worktreePath, baseCommit) {
|
|
|
9566
9369
|
if (inspection.missing || !inspection.realpath) return { error: "captured handoff patch is missing" };
|
|
9567
9370
|
let patchStat;
|
|
9568
9371
|
try {
|
|
9569
|
-
patchStat =
|
|
9372
|
+
patchStat = fs18.statSync(patchPath);
|
|
9570
9373
|
} catch (error) {
|
|
9571
9374
|
return { error: `captured patch cannot be inspected: ${error instanceof Error ? error.message : String(error)}` };
|
|
9572
9375
|
}
|
|
@@ -9579,7 +9382,7 @@ function isPatchCaptured(record2, worktreePath, baseCommit) {
|
|
|
9579
9382
|
}
|
|
9580
9383
|
function buildManagedEntry(input) {
|
|
9581
9384
|
const { repoRoot, baseDir, targetHead, rootPath, git, record: record2, foregroundRunOwnership } = input;
|
|
9582
|
-
const gitWorktreePath =
|
|
9385
|
+
const gitWorktreePath = path16.resolve(git.path);
|
|
9583
9386
|
const metadataWorktreePath = metadataRecordPath(record2);
|
|
9584
9387
|
const worktreePath = resolveExistingPath(metadataWorktreePath);
|
|
9585
9388
|
const entry = buildEntryBase({ path: worktreePath, branch: git.branch ?? "", source: "both", git, metadata: record2, targetHead });
|
|
@@ -9596,13 +9399,13 @@ function buildManagedEntry(input) {
|
|
|
9596
9399
|
if (pathInspection.missing) return blockedEntry(entry, "stale", "unknown", "worktree path is missing from disk");
|
|
9597
9400
|
if (pathInspection.symlink) return blockedEntry(entry, "unknown", "unknown", "worktree path is a symlink; cleanup requires a real directory");
|
|
9598
9401
|
if (!pathInspection.directory || !pathInspection.realpath) return blockedEntry(entry, "unknown", "unknown", "worktree path is not a directory");
|
|
9599
|
-
const extensionsDir =
|
|
9402
|
+
const extensionsDir = path16.join(getAgentDir(), "extensions");
|
|
9600
9403
|
if (pathInside(extensionsDir, pathInspection.realpath)) return blockedEntry(entry, "ineligible", "keep", `worktree real path is inside Pi extensions directory '${extensionsDir}'`);
|
|
9601
9404
|
if (input.containmentInvalid || !pathInside(baseDir, pathInspection.realpath, true)) return blockedEntry(entry, "ineligible", "keep", `worktree real path is outside configured base directory '${baseDir}'`);
|
|
9602
9405
|
const recordedBaseDir = record2.task.recordedBaseDir;
|
|
9603
|
-
if (typeof recordedBaseDir !== "string" || !
|
|
9604
|
-
const creationRelative =
|
|
9605
|
-
if (!creationRelative || creationRelative === ".." || creationRelative.startsWith(`..${
|
|
9406
|
+
if (typeof recordedBaseDir !== "string" || !path16.isAbsolute(recordedBaseDir)) return blockedEntry(entry, "unknown", "keep", "handoff metadata has no creation-time base directory proof");
|
|
9407
|
+
const creationRelative = path16.relative(recordedBaseDir, pathInspection.realpath);
|
|
9408
|
+
if (!creationRelative || creationRelative === ".." || creationRelative.startsWith(`..${path16.sep}`) || path16.isAbsolute(creationRelative)) return blockedEntry(entry, "ineligible", "keep", `worktree real path is outside its creation-time recorded base directory '${recordedBaseDir}'`);
|
|
9606
9409
|
if (rootPath === comparablePath(pathInspection.realpath)) return blockedEntry(entry, "ineligible", "keep", "worktree is the repository root");
|
|
9607
9410
|
if (git.locked) return blockedEntry(entry, "ineligible", "keep", "Git worktree is locked");
|
|
9608
9411
|
if (!git.branch) return blockedEntry(entry, "unknown", "unknown", "detached worktrees have no metadata-recorded branch");
|
|
@@ -9781,7 +9584,7 @@ function buildWorktreeCleanupPlan(input) {
|
|
|
9781
9584
|
};
|
|
9782
9585
|
}
|
|
9783
9586
|
function worktreeCleanupPlanPath(repoRoot, planId) {
|
|
9784
|
-
return
|
|
9587
|
+
return path16.join(repoRoot, PROJECT_SUBAGENTS_RELATIVE_DIR, "cleanup-plans", `${validatePlanId(planId)}.json`);
|
|
9785
9588
|
}
|
|
9786
9589
|
function createWorktreeCleanupPlan(input) {
|
|
9787
9590
|
const plan = buildWorktreeCleanupPlan(input);
|
|
@@ -9829,13 +9632,13 @@ function formatWorktreeCleanupPlan(created) {
|
|
|
9829
9632
|
|
|
9830
9633
|
// src/runs/shared/worktree-cleanup-apply.ts
|
|
9831
9634
|
function assertContainedRealPath(repoRoot, candidate, create = false) {
|
|
9832
|
-
const relative3 =
|
|
9833
|
-
if (!relative3 || relative3 === ".." || relative3.startsWith(`..${
|
|
9635
|
+
const relative3 = path17.relative(repoRoot, candidate);
|
|
9636
|
+
if (!relative3 || relative3 === ".." || relative3.startsWith(`..${path17.sep}`) || path17.isAbsolute(relative3)) throw new Error("Cleanup evidence must be inside the selected repository.");
|
|
9834
9637
|
let current = repoRoot;
|
|
9835
|
-
for (const segment of relative3.split(
|
|
9836
|
-
current =
|
|
9837
|
-
if (!
|
|
9838
|
-
const stat =
|
|
9638
|
+
for (const segment of relative3.split(path17.sep)) {
|
|
9639
|
+
current = path17.join(current, segment);
|
|
9640
|
+
if (!fs19.existsSync(current) && create) fs19.mkdirSync(current, { mode: 448 });
|
|
9641
|
+
const stat = fs19.lstatSync(current);
|
|
9839
9642
|
if (stat.isSymbolicLink()) throw new Error(`Cleanup evidence cannot use a symlink: ${current}`);
|
|
9840
9643
|
}
|
|
9841
9644
|
}
|
|
@@ -9843,14 +9646,14 @@ function loadReviewedCleanupPlan(repo, planId, now = Date.now()) {
|
|
|
9843
9646
|
const repoRoot = resolveCleanupRepoRoot(repo);
|
|
9844
9647
|
const file = worktreeCleanupPlanPath(repoRoot, planId);
|
|
9845
9648
|
assertContainedRealPath(repoRoot, file);
|
|
9846
|
-
const stat =
|
|
9649
|
+
const stat = fs19.statSync(file);
|
|
9847
9650
|
if (!stat.isFile() || stat.size > 2 * 1024 * 1024) throw new Error("Cleanup plan is not a bounded regular file.");
|
|
9848
|
-
const plan = JSON.parse(
|
|
9849
|
-
if (plan.version !== 1 || plan.planId !== planId || plan.repoRoot !== repoRoot || !Number.isFinite(plan.createdAt) || !Number.isFinite(plan.expiresAt) || plan.createdAt > now || plan.expiresAt <= now && !
|
|
9850
|
-
if (!Array.isArray(plan.entries) || plan.entries.length > 512 || !Array.isArray(plan.baseDirs) || plan.baseDirs.length !== 1 || !plan.baseDirs.every((item) => typeof item === "string" &&
|
|
9651
|
+
const plan = JSON.parse(fs19.readFileSync(file, "utf-8"));
|
|
9652
|
+
if (plan.version !== 1 || plan.planId !== planId || plan.repoRoot !== repoRoot || !Number.isFinite(plan.createdAt) || !Number.isFinite(plan.expiresAt) || plan.createdAt > now || plan.expiresAt <= now && !fs19.existsSync(file.replace(/\.json$/, ".claim")) || plan.expiresAt - plan.createdAt !== WORKTREE_CLEANUP_PLAN_TTL_MS) throw new Error("Cleanup plan is invalid, expired, or belongs to another repository.");
|
|
9653
|
+
if (!Array.isArray(plan.entries) || plan.entries.length > 512 || !Array.isArray(plan.baseDirs) || plan.baseDirs.length !== 1 || !plan.baseDirs.every((item) => typeof item === "string" && path17.isAbsolute(item)) || !Array.isArray(plan.metadataPaths) || !plan.metadataPaths.every((item) => typeof item === "string" && path17.isAbsolute(item)) || !Array.isArray(plan.pruneCandidates)) throw new Error("Cleanup plan has invalid inventory fields.");
|
|
9851
9654
|
const paths = /* @__PURE__ */ new Set();
|
|
9852
9655
|
for (const entry of plan.entries) {
|
|
9853
|
-
if (!entry || typeof entry.path !== "string" || !
|
|
9656
|
+
if (!entry || typeof entry.path !== "string" || !path17.isAbsolute(entry.path) || typeof entry.branch !== "string" || !["remove", "keep", "unknown"].includes(entry.decision) || !entry.preconditions || entry.preconditions.path !== entry.path || entry.preconditions.branch !== entry.branch || !Array.isArray(entry.reasons) || !entry.reasons.every((reason) => typeof reason === "string") || paths.has(entry.path)) throw new Error("Cleanup plan has invalid or duplicate entries.");
|
|
9854
9657
|
paths.add(entry.path);
|
|
9855
9658
|
}
|
|
9856
9659
|
const hash = createHash5("sha256").update(JSON.stringify(worktreeCleanupContentPayload(plan))).digest("hex");
|
|
@@ -9860,7 +9663,7 @@ function loadReviewedCleanupPlan(repo, planId, now = Date.now()) {
|
|
|
9860
9663
|
function recordRemoval(entry) {
|
|
9861
9664
|
const manifest = readParallelHandoffManifest(entry.handoffPath);
|
|
9862
9665
|
if (!manifest || manifest.runId !== entry.runId) throw new Error("Handoff ownership changed before recording removal.");
|
|
9863
|
-
const tasks = manifest.groups.flatMap((group) => group.cleanup.tasks).filter((task) => task.index === entry.taskIndex && samePath(
|
|
9666
|
+
const tasks = manifest.groups.flatMap((group) => group.cleanup.tasks).filter((task) => task.index === entry.taskIndex && samePath(path17.resolve(path17.dirname(entry.handoffPath), task.path), entry.path) && task.branch === entry.branch);
|
|
9864
9667
|
if (tasks.length !== 1) throw new Error("Handoff task no longer identifies exactly one removed worktree.");
|
|
9865
9668
|
if (tasks[0].worktreeRemoved) return;
|
|
9866
9669
|
tasks[0].worktreeRemoved = true;
|
|
@@ -9875,20 +9678,20 @@ async function applyReviewedCleanupPlan(input) {
|
|
|
9875
9678
|
return withWorktreeTransaction(() => withRepositoryWorktreeLock(input.repo, async () => {
|
|
9876
9679
|
const plan = loadReviewedCleanupPlan(input.repo, input.planId);
|
|
9877
9680
|
const claimPath = worktreeCleanupPlanPath(plan.repoRoot, plan.planId).replace(/\.json$/, ".claim");
|
|
9878
|
-
const receiptPath =
|
|
9879
|
-
assertContainedRealPath(plan.repoRoot,
|
|
9681
|
+
const receiptPath = path17.join(claimPath, "receipt.json");
|
|
9682
|
+
assertContainedRealPath(plan.repoRoot, path17.dirname(claimPath));
|
|
9880
9683
|
try {
|
|
9881
|
-
|
|
9684
|
+
fs19.mkdirSync(claimPath, { mode: 448 });
|
|
9882
9685
|
} catch (error) {
|
|
9883
9686
|
if (error.code !== "EEXIST") throw error;
|
|
9884
9687
|
assertContainedRealPath(plan.repoRoot, receiptPath);
|
|
9885
|
-
const receipt2 = JSON.parse(
|
|
9688
|
+
const receipt2 = JSON.parse(fs19.readFileSync(receiptPath, "utf-8"));
|
|
9886
9689
|
if (receipt2.version !== 1 || receipt2.planId !== plan.planId || receipt2.repoRoot !== plan.repoRoot || receipt2.contentHash !== plan.contentHash || !Array.isArray(receipt2.entries) || !["applying", "complete", "partial"].includes(receipt2.state)) throw new Error("Claimed cleanup plan has an invalid receipt; inspect it before creating a fresh plan.");
|
|
9887
9690
|
for (const entry of plan.entries.filter((entry2) => receipt2.state !== "complete" && entry2.decision === "remove" && receipt2.entries.some((item) => (item?.state === "removing" || item?.state === "removed") && item.path === entry2.path && item.branch === entry2.branch))) {
|
|
9888
|
-
await new Promise((
|
|
9691
|
+
await new Promise((resolve9) => setImmediate(resolve9));
|
|
9889
9692
|
input.signal?.throwIfAborted();
|
|
9890
9693
|
try {
|
|
9891
|
-
|
|
9694
|
+
fs19.lstatSync(entry.path);
|
|
9892
9695
|
continue;
|
|
9893
9696
|
} catch (error2) {
|
|
9894
9697
|
if (error2.code !== "ENOENT") throw error2;
|
|
@@ -9910,7 +9713,7 @@ async function applyReviewedCleanupPlan(input) {
|
|
|
9910
9713
|
try {
|
|
9911
9714
|
const selected = input.select?.(plan);
|
|
9912
9715
|
for (const entry of plan.entries.filter((item) => item.decision === "remove")) {
|
|
9913
|
-
await new Promise((
|
|
9716
|
+
await new Promise((resolve9) => setImmediate(resolve9));
|
|
9914
9717
|
input.signal?.throwIfAborted();
|
|
9915
9718
|
if (selected && !selected.has(entry.path)) {
|
|
9916
9719
|
receipt.entries.push({ path: entry.path, branch: entry.branch, state: "kept", reason: "outside the current retention excess" });
|
|
@@ -9920,7 +9723,7 @@ async function applyReviewedCleanupPlan(input) {
|
|
|
9920
9723
|
withHandoffWriteLock(entry.handoffPath, () => {
|
|
9921
9724
|
const manifest = readParallelHandoffManifest(entry.handoffPath);
|
|
9922
9725
|
if (!manifest || manifest.runId !== entry.runId) throw new Error("Handoff ownership changed before cleanup validation.");
|
|
9923
|
-
const fresh = buildWorktreeCleanupPlan({ repo: plan.repoRoot, candidatePaths: [entry.path], handoffPaths: plan.metadataPaths, worktreeBaseDir:
|
|
9726
|
+
const fresh = buildWorktreeCleanupPlan({ repo: plan.repoRoot, candidatePaths: [entry.path], handoffPaths: plan.metadataPaths, worktreeBaseDir: path17.dirname(plan.baseDirs[0]), foregroundRunOwnership: input.foregroundRunOwnership });
|
|
9924
9727
|
const candidate = fresh.entries.find((item) => item.path === entry.path);
|
|
9925
9728
|
if (fresh.warnings?.some((warning) => /capped|failed to inspect/.test(warning)) || !candidate || candidate.decision !== "remove" || candidate.handoffPath !== entry.handoffPath || candidate.runId !== entry.runId || candidate.taskIndex !== entry.taskIndex || candidate.patchPath !== entry.patchPath || JSON.stringify(candidate.preconditions) !== JSON.stringify(entry.preconditions)) {
|
|
9926
9729
|
receipt.entries.push({ path: entry.path, branch: entry.branch, state: "kept", reason: candidate?.reasons.join("; ") || "reviewed ownership or Git facts changed" });
|
|
@@ -9930,7 +9733,7 @@ async function applyReviewedCleanupPlan(input) {
|
|
|
9930
9733
|
const journalEntry = { path: entry.path, branch: entry.branch, state: "removing", reason: "removal prepared; outcome not yet recorded" };
|
|
9931
9734
|
receipt.entries.push(journalEntry);
|
|
9932
9735
|
writePrivateAtomicJson(receiptPath, receipt);
|
|
9933
|
-
const removed =
|
|
9736
|
+
const removed = spawnSync2("git", ["-C", plan.repoRoot, "worktree", "remove", "--", entry.path], { encoding: "utf-8", windowsHide: true });
|
|
9934
9737
|
if (removed.status !== 0) throw new Error(removed.error?.message || removed.stderr.trim() || "git worktree remove failed");
|
|
9935
9738
|
journalEntry.state = "removed";
|
|
9936
9739
|
journalEntry.reason = "worktree removed; local branch retained";
|
|
@@ -10018,13 +9821,13 @@ function inheritedRunFanoutBudget(deps) {
|
|
|
10018
9821
|
return deps.childRuntime?.runFanoutBudget;
|
|
10019
9822
|
}
|
|
10020
9823
|
function resolveRequestedCwd(runtimeCwd, requestedCwd) {
|
|
10021
|
-
return requestedCwd ?
|
|
9824
|
+
return requestedCwd ? path18.resolve(runtimeCwd, requestedCwd) : runtimeCwd;
|
|
10022
9825
|
}
|
|
10023
9826
|
function readWorkflowScriptFile(requestedPath, requestedCwd, runtimeCwd) {
|
|
10024
|
-
const scriptPath =
|
|
9827
|
+
const scriptPath = path18.resolve(resolveRequestedCwd(runtimeCwd, requestedCwd), requestedPath);
|
|
10025
9828
|
let script;
|
|
10026
9829
|
try {
|
|
10027
|
-
script =
|
|
9830
|
+
script = fs20.readFileSync(scriptPath, "utf8");
|
|
10028
9831
|
} catch (error) {
|
|
10029
9832
|
return { error: `Failed to read workflow script '${scriptPath}': ${error instanceof Error ? error.message : String(error)}` };
|
|
10030
9833
|
}
|
|
@@ -10098,7 +9901,7 @@ function nestedResolutionScopeForExecutor(deps) {
|
|
|
10098
9901
|
};
|
|
10099
9902
|
}
|
|
10100
9903
|
function trustedSessionRoots(parentSessionFile, deps) {
|
|
10101
|
-
const roots = deps.config.defaultSessionDir ? [
|
|
9904
|
+
const roots = deps.config.defaultSessionDir ? [path18.resolve(deps.expandTilde(deps.config.defaultSessionDir))] : [];
|
|
10102
9905
|
if (parentSessionFile) for (const sessionFile of [parentSessionFile, ...inheritedSessionIds(parentSessionFile)]) roots.push(deps.getSubagentSessionRoot(sessionFile));
|
|
10103
9906
|
return [...new Set(roots)];
|
|
10104
9907
|
}
|
|
@@ -10428,9 +10231,9 @@ function resolveForegroundResumeTarget(params, state, options = {}) {
|
|
|
10428
10231
|
if (index < 0 || index >= run.children.length) throw new Error(`Foreground run '${run.runId}' has ${run.children.length} children. Index ${index} is out of range.`);
|
|
10429
10232
|
const child = run.children[index];
|
|
10430
10233
|
if (!child.sessionFile) throw new Error(`Foreground run '${run.runId}' child ${index} does not have a persisted session file to resume from.`);
|
|
10431
|
-
if (
|
|
10432
|
-
const sessionFile =
|
|
10433
|
-
if (!
|
|
10234
|
+
if (path18.extname(child.sessionFile) !== ".jsonl") throw new Error(`Foreground run '${run.runId}' child ${index} session file must be a .jsonl file: ${child.sessionFile}`);
|
|
10235
|
+
const sessionFile = path18.resolve(child.sessionFile);
|
|
10236
|
+
if (!fs20.existsSync(sessionFile)) throw new Error(`Foreground run '${run.runId}' child ${index} session file does not exist: ${child.sessionFile}`);
|
|
10434
10237
|
return {
|
|
10435
10238
|
runId: run.runId,
|
|
10436
10239
|
mode: run.mode,
|
|
@@ -10449,9 +10252,6 @@ function resolveForegroundResumeTarget(params, state, options = {}) {
|
|
|
10449
10252
|
...child.capabilityCeiling ? { capabilityCeiling: child.capabilityCeiling } : {}
|
|
10450
10253
|
};
|
|
10451
10254
|
}
|
|
10452
|
-
function isAsyncRunNotFound(error) {
|
|
10453
|
-
return error instanceof Error && error.message.startsWith("Async run not found.");
|
|
10454
|
-
}
|
|
10455
10255
|
function isMissingExactAsyncStatusFile(error, requested) {
|
|
10456
10256
|
return error instanceof Error && error.message === `Status file not found for async run '${requested}'.`;
|
|
10457
10257
|
}
|
|
@@ -10481,7 +10281,7 @@ function resolveResumeTarget(params, state, options = {}) {
|
|
|
10481
10281
|
foregroundError = error;
|
|
10482
10282
|
}
|
|
10483
10283
|
try {
|
|
10484
|
-
const asyncParams = options.exactOnly && requested && !params.dir ? { ...params, dir:
|
|
10284
|
+
const asyncParams = options.exactOnly && requested && !params.dir ? { ...params, dir: path18.join(DIRS.async, requested) } : params;
|
|
10485
10285
|
asyncTarget = {
|
|
10486
10286
|
source: "async",
|
|
10487
10287
|
...resolveAsyncResumeTarget(asyncParams, {}, compactOptional({
|
|
@@ -10509,32 +10309,10 @@ function resolveResumeTarget(params, state, options = {}) {
|
|
|
10509
10309
|
if (isResumeAmbiguity(foregroundError) && !resumeTargetExact(asyncTarget, requested)) throw foregroundError;
|
|
10510
10310
|
return asyncTarget;
|
|
10511
10311
|
}
|
|
10512
|
-
if (foregroundError && !isAsyncRunNotFound(asyncError)) throw foregroundError;
|
|
10513
10312
|
if (foregroundError) throw foregroundError;
|
|
10514
10313
|
if (asyncError) throw asyncError;
|
|
10515
10314
|
throw new Error("Run not found. Provide id or runId.");
|
|
10516
10315
|
}
|
|
10517
|
-
function getAsyncInterruptTarget(state, runId, location, options = {}) {
|
|
10518
|
-
if (location?.asyncDir) {
|
|
10519
|
-
return {
|
|
10520
|
-
asyncId: location.resolvedId ?? runId ?? path19.basename(location.asyncDir),
|
|
10521
|
-
asyncDir: location.asyncDir
|
|
10522
|
-
};
|
|
10523
|
-
}
|
|
10524
|
-
if (runId) {
|
|
10525
|
-
const direct = state.asyncJobs.get(runId);
|
|
10526
|
-
if (direct) return { asyncId: direct.asyncId, asyncDir: direct.asyncDir };
|
|
10527
|
-
if (options.fallbackToNewest === false) return void 0;
|
|
10528
|
-
}
|
|
10529
|
-
let newest;
|
|
10530
|
-
for (const job of state.asyncJobs.values()) {
|
|
10531
|
-
if (job.status !== "running") continue;
|
|
10532
|
-
if (!newest || (job.updatedAt ?? 0) > newest.updatedAt) {
|
|
10533
|
-
newest = { asyncId: job.asyncId, asyncDir: job.asyncDir, updatedAt: job.updatedAt ?? 0 };
|
|
10534
|
-
}
|
|
10535
|
-
}
|
|
10536
|
-
return newest ? { asyncId: newest.asyncId, asyncDir: newest.asyncDir } : void 0;
|
|
10537
|
-
}
|
|
10538
10316
|
function isStaleExtensionContextError2(error) {
|
|
10539
10317
|
if (!(error instanceof Error)) return false;
|
|
10540
10318
|
return /extension ctx is stale|stale after session replacement or reload/i.test(error.message);
|
|
@@ -10566,7 +10344,7 @@ function persistAsyncWorkflowControlEvent(input) {
|
|
|
10566
10344
|
} : {}
|
|
10567
10345
|
};
|
|
10568
10346
|
try {
|
|
10569
|
-
|
|
10347
|
+
fs20.appendFileSync(path18.join(input.job.asyncDir, "events.jsonl"), `${JSON.stringify(record2)}
|
|
10570
10348
|
`, "utf-8");
|
|
10571
10349
|
} catch (error) {
|
|
10572
10350
|
if (!isStorageCapacityError(error)) throw error;
|
|
@@ -10594,12 +10372,12 @@ function emitControlNotification(input) {
|
|
|
10594
10372
|
}
|
|
10595
10373
|
}
|
|
10596
10374
|
function interruptAsyncRun(state, runId, kill, location) {
|
|
10597
|
-
const target =
|
|
10375
|
+
const target = location?.asyncDir ? { asyncId: location.resolvedId ?? runId, asyncDir: location.asyncDir } : state.asyncJobs.get(runId);
|
|
10598
10376
|
if (!target) return null;
|
|
10599
10377
|
const status = reconcileAsyncRun(target.asyncDir, omitUndefinedProperties({ kill })).status;
|
|
10600
10378
|
if (!status || status.state !== "running" || typeof status.pid !== "number") {
|
|
10601
10379
|
return {
|
|
10602
|
-
content: [{ type: "text", text: `No running async run with an interrupt-capable pid was found for '${runId
|
|
10380
|
+
content: [{ type: "text", text: `No running async run with an interrupt-capable pid was found for '${runId}'.` }],
|
|
10603
10381
|
isError: true,
|
|
10604
10382
|
details: { mode: "management", results: [] }
|
|
10605
10383
|
};
|
|
@@ -10816,7 +10594,7 @@ function appendStepToAsyncChain(input) {
|
|
|
10816
10594
|
worktreeBranchPrefix: input.deps.config.worktreeBranchPrefix,
|
|
10817
10595
|
asyncDir: resolved.location.asyncDir,
|
|
10818
10596
|
validateOutputBindings: false,
|
|
10819
|
-
capabilityCeiling: intersectSubagentCapabilityCeilings(status.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(asyncCtx.
|
|
10597
|
+
capabilityCeiling: intersectSubagentCapabilityCeilings(status.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(asyncCtx.parentSessionId)),
|
|
10820
10598
|
requiredExtensions: retainedRequiredExtensions
|
|
10821
10599
|
}));
|
|
10822
10600
|
if ("error" in built) {
|
|
@@ -10886,25 +10664,25 @@ function nestedRunAgent(run) {
|
|
|
10886
10664
|
return run.agent ?? run.agents?.[0] ?? (run.steps?.length === 1 ? run.steps[0]?.agent : void 0);
|
|
10887
10665
|
}
|
|
10888
10666
|
function pathWithin(base, candidate) {
|
|
10889
|
-
const resolvedBase =
|
|
10890
|
-
const resolvedCandidate =
|
|
10891
|
-
return resolvedCandidate === resolvedBase || resolvedCandidate.startsWith(`${resolvedBase}${
|
|
10667
|
+
const resolvedBase = path18.resolve(base);
|
|
10668
|
+
const resolvedCandidate = path18.resolve(candidate);
|
|
10669
|
+
return resolvedCandidate === resolvedBase || resolvedCandidate.startsWith(`${resolvedBase}${path18.sep}`);
|
|
10892
10670
|
}
|
|
10893
10671
|
function validateNestedSessionFile(run, trustedSessionRoots2, resolveSource, visited = []) {
|
|
10894
10672
|
const sessionFile = nestedRunSessionFile(run);
|
|
10895
10673
|
if (!sessionFile) throw new Error(`Nested run '${run.id}' does not have a persisted session file to resume from.`);
|
|
10896
|
-
if (
|
|
10897
|
-
const resolved =
|
|
10898
|
-
if (!
|
|
10899
|
-
if (!
|
|
10900
|
-
const stat =
|
|
10674
|
+
if (path18.extname(sessionFile) !== ".jsonl") throw new Error(`Nested run '${run.id}' session file must be a .jsonl file: ${sessionFile}`);
|
|
10675
|
+
const resolved = path18.resolve(sessionFile);
|
|
10676
|
+
if (!path18.isAbsolute(sessionFile)) throw new Error(`Nested run '${run.id}' session file must be absolute: ${sessionFile}`);
|
|
10677
|
+
if (!fs20.existsSync(resolved)) throw new Error(`Nested run '${run.id}' session file does not exist: ${sessionFile}`);
|
|
10678
|
+
const stat = fs20.lstatSync(resolved);
|
|
10901
10679
|
if (!stat.isFile() || stat.isSymbolicLink()) throw new Error(`Nested run '${run.id}' session file is not a regular file: ${sessionFile}`);
|
|
10902
|
-
const realSessionFile =
|
|
10903
|
-
const trustedRoots = trustedSessionRoots2.filter((root) =>
|
|
10680
|
+
const realSessionFile = fs20.realpathSync(resolved);
|
|
10681
|
+
const trustedRoots = trustedSessionRoots2.filter((root) => fs20.existsSync(root)).map((root) => fs20.realpathSync(root));
|
|
10904
10682
|
if (!trustedRoots.some((root) => pathWithin(root, realSessionFile))) {
|
|
10905
10683
|
throw new Error(`Nested run '${run.id}' session file is outside trusted nested session roots: ${sessionFile}`);
|
|
10906
10684
|
}
|
|
10907
|
-
if (!realSessionFile.split(
|
|
10685
|
+
if (!realSessionFile.split(path18.sep).includes(run.id)) {
|
|
10908
10686
|
const sourceId = run.revivedFrom;
|
|
10909
10687
|
const chain = [...visited, run.id];
|
|
10910
10688
|
const source = sourceId && chain.length <= 8 && !chain.includes(sourceId) ? resolveSource(sourceId) : void 0;
|
|
@@ -10945,7 +10723,7 @@ function resolveNestedResumeTarget(match, trustedSessionRoots2, resolveSource) {
|
|
|
10945
10723
|
state,
|
|
10946
10724
|
agent,
|
|
10947
10725
|
index: 0,
|
|
10948
|
-
cwd: asyncDir ?
|
|
10726
|
+
cwd: asyncDir ? path18.dirname(asyncDir) : void 0,
|
|
10949
10727
|
sessionFile: validateNestedSessionFile(run, trustedSessionRoots2, resolveSource),
|
|
10950
10728
|
...run.capabilityCeiling ? { capabilityCeiling: run.capabilityCeiling } : {},
|
|
10951
10729
|
...recoveryDescriptor ? { recoveryDescriptor } : {}
|
|
@@ -10992,7 +10770,7 @@ async function waitForNestedControlResult(target, requestId, ignoredFiles, timeo
|
|
|
10992
10770
|
while (Date.now() < deadline) {
|
|
10993
10771
|
const result2 = findNestedControlResult(target.match.route, requestId, target.match.run.id, ignoredFiles);
|
|
10994
10772
|
if (result2) return result2;
|
|
10995
|
-
await new Promise((
|
|
10773
|
+
await new Promise((resolve9) => setTimeout(resolve9, 50));
|
|
10996
10774
|
}
|
|
10997
10775
|
return void 0;
|
|
10998
10776
|
}
|
|
@@ -11013,7 +10791,7 @@ function directNestedAsyncInterrupt(target) {
|
|
|
11013
10791
|
const run = target.match.run;
|
|
11014
10792
|
const asyncDir = resolveNestedAsyncDir(target.match.rootRunId, run);
|
|
11015
10793
|
if (!asyncDir) return void 0;
|
|
11016
|
-
const status = reconcileAsyncRun(asyncDir, { resultsDir:
|
|
10794
|
+
const status = reconcileAsyncRun(asyncDir, { resultsDir: path18.join(DIRS.results, "nested", target.match.rootRunId) }).status;
|
|
11017
10795
|
const pid = typeof status?.pid === "number" && status.pid > 0 ? status.pid : run.pid;
|
|
11018
10796
|
if (!status || status.state !== "running" || typeof pid !== "number" || pid <= 0) return void 0;
|
|
11019
10797
|
try {
|
|
@@ -11028,7 +10806,7 @@ async function directNestedAsyncSteer(input) {
|
|
|
11028
10806
|
const run = input.target.match.run;
|
|
11029
10807
|
const asyncDir = resolveNestedAsyncDir(input.target.match.rootRunId, run);
|
|
11030
10808
|
if (!asyncDir) return void 0;
|
|
11031
|
-
const status = reconcileAsyncRun(asyncDir, { resultsDir:
|
|
10809
|
+
const status = reconcileAsyncRun(asyncDir, { resultsDir: path18.join(DIRS.results, "nested", input.target.match.rootRunId) }).status;
|
|
11032
10810
|
if (!status || status.state !== "running" && status.state !== "queued") return void 0;
|
|
11033
10811
|
const steps = status.steps ?? [];
|
|
11034
10812
|
if (input.index !== void 0) {
|
|
@@ -11137,10 +10915,10 @@ async function resumeExternalJobFollowUp(input) {
|
|
|
11137
10915
|
const runId = externalJobFollowUpRunId(requestDigest);
|
|
11138
10916
|
const nestedRoute = inheritedNestedRoute(input.deps);
|
|
11139
10917
|
const runRoots = nestedRoute ? nestedRunScope(nestedRoute.rootRunId) : { asyncDirRoot: DIRS.async, resultsDir: DIRS.results };
|
|
11140
|
-
const asyncDir =
|
|
10918
|
+
const asyncDir = path18.join(runRoots.asyncDirRoot, runId);
|
|
11141
10919
|
const currentSessionId = input.deps.state.currentSessionId;
|
|
11142
10920
|
if (!currentSessionId) return { content: [{ type: "text", text: "External-job follow-up requires an active parent session." }], isError: true, details: { mode: "management", results: [] } };
|
|
11143
|
-
if (
|
|
10921
|
+
if (fs20.existsSync(asyncDir) || fs20.existsSync(resultFilePath(runRoots.resultsDir, runId))) {
|
|
11144
10922
|
return externalJobFollowUpStarted({ sourceRunId: input.target.runId, runId, asyncDir, duplicate: true, interactive: input.ctx.hasUI });
|
|
11145
10923
|
}
|
|
11146
10924
|
const depthState = checkSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime);
|
|
@@ -11209,7 +10987,7 @@ async function resumeExternalJobFollowUp(input) {
|
|
|
11209
10987
|
availableModels: input.ctx.modelRegistry.getAvailable().map(toModelInfo),
|
|
11210
10988
|
outputBaseDir: resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId),
|
|
11211
10989
|
...input.absoluteDeadlineAt !== void 0 ? { absoluteDeadlineAt: input.absoluteDeadlineAt } : {},
|
|
11212
|
-
capabilityCeiling: resolveCurrentSubagentCapabilityCeiling(
|
|
10990
|
+
capabilityCeiling: resolveCurrentSubagentCapabilityCeiling(input.ctx.sessionManager.getSessionId() ?? void 0),
|
|
11213
10991
|
runFanoutBudget: createRunFanoutBudget(runId, resolveMaxSubagentSpawnsPerRun(input.deps.config.maxSubagentSpawnsPerRun)),
|
|
11214
10992
|
activeAsyncCapacity,
|
|
11215
10993
|
externalJobFollowUp: { sourceRunId: input.target.runId, sourceStepIndex: input.target.index, parentProviderJobId: externalJob.providerJobId, requestId, requestDigest }
|
|
@@ -11341,7 +11119,7 @@ async function resumeAsyncRun(input) {
|
|
|
11341
11119
|
inheritGlobalContext: recoveryDescriptor.inheritGlobalContext,
|
|
11342
11120
|
inheritSkills: recoveryDescriptor.inheritSkills,
|
|
11343
11121
|
source: "project",
|
|
11344
|
-
filePath: recoveryDescriptor.agentFilePath ??
|
|
11122
|
+
filePath: recoveryDescriptor.agentFilePath ?? path18.join(getProjectSubagentsDir(recoveryDescriptor.cwd), "recovery-agent"),
|
|
11345
11123
|
...discovered.maxThinking ? { maxThinking: discovered.maxThinking } : {}
|
|
11346
11124
|
} : void 0);
|
|
11347
11125
|
if (!baseAgentConfig) {
|
|
@@ -11362,7 +11140,7 @@ async function resumeAsyncRun(input) {
|
|
|
11362
11140
|
if (target.source === "async" && target.managedWorktree && target.asyncDir) {
|
|
11363
11141
|
try {
|
|
11364
11142
|
const protectedCwd = await protectRetainedWorktreeForResume(parallelHandoffPath(target.asyncDir), target.runId, target.index, input.signal);
|
|
11365
|
-
if (
|
|
11143
|
+
if (path18.resolve(protectedCwd) !== path18.resolve(effectiveCwd)) throw new Error("Retained worktree cwd changed during resume admission.");
|
|
11366
11144
|
} catch (error) {
|
|
11367
11145
|
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
|
|
11368
11146
|
}
|
|
@@ -11415,7 +11193,7 @@ async function resumeAsyncRun(input) {
|
|
|
11415
11193
|
limit: resolveMaxActiveAsyncRunsPerSession(input.deps.config.maxActiveAsyncRunsPerSession),
|
|
11416
11194
|
runId: runId2,
|
|
11417
11195
|
kind: "runner",
|
|
11418
|
-
asyncDir:
|
|
11196
|
+
asyncDir: path18.join(DIRS.async, runId2)
|
|
11419
11197
|
}, { liveWorkflowRunIds: new Set(input.deps.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(input.deps.config.capacity?.abandonedSlotReleaseAfterMs) }) : void 0;
|
|
11420
11198
|
} catch (error) {
|
|
11421
11199
|
if (error instanceof ActiveAsyncCapacityError) return { content: [{ type: "text", text: error.message }], isError: true, details: { mode: "chain", results: [], activeAsyncCapacity: error.snapshot } };
|
|
@@ -11435,7 +11213,7 @@ async function resumeAsyncRun(input) {
|
|
|
11435
11213
|
goal,
|
|
11436
11214
|
attachRoot: {
|
|
11437
11215
|
runId: target.runId,
|
|
11438
|
-
asyncDir: target.asyncDir ??
|
|
11216
|
+
asyncDir: target.asyncDir ?? path18.join(DIRS.async, target.runId),
|
|
11439
11217
|
resultPath: resolveAsyncRootResultPath(DIRS.results, target.runId),
|
|
11440
11218
|
index: target.index,
|
|
11441
11219
|
agent: target.agent,
|
|
@@ -11486,7 +11264,7 @@ async function resumeAsyncRun(input) {
|
|
|
11486
11264
|
childIntercomTarget: intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(runId2, agent, index) : void 0,
|
|
11487
11265
|
globalConcurrencyLimit: input.deps.config.globalConcurrencyLimit,
|
|
11488
11266
|
runFanoutBudget: createRunFanoutBudget(runId2, resolveMaxSubagentSpawnsPerRun(input.deps.config.maxSubagentSpawnsPerRun)),
|
|
11489
|
-
capabilityCeiling: intersectSubagentCapabilityCeilings("capabilityCeiling" in target ? target.capabilityCeiling : void 0, resolveCurrentSubagentCapabilityCeiling(input.
|
|
11267
|
+
capabilityCeiling: intersectSubagentCapabilityCeilings("capabilityCeiling" in target ? target.capabilityCeiling : void 0, resolveCurrentSubagentCapabilityCeiling(input.ctx.sessionManager.getSessionId() ?? void 0)),
|
|
11490
11268
|
thinkingCeiling: target.thinkingCeiling,
|
|
11491
11269
|
activeAsyncCapacity: activeAsyncCapacity2
|
|
11492
11270
|
}));
|
|
@@ -11539,13 +11317,13 @@ async function resumeAsyncRun(input) {
|
|
|
11539
11317
|
limit: resolveMaxActiveAsyncRunsPerSession(input.deps.config.maxActiveAsyncRunsPerSession),
|
|
11540
11318
|
sourceRunId: target.runId,
|
|
11541
11319
|
runId,
|
|
11542
|
-
asyncDir:
|
|
11320
|
+
asyncDir: path18.join(DIRS.async, runId)
|
|
11543
11321
|
}, { abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(input.deps.config.capacity?.abandonedSlotReleaseAfterMs) }) : acquireActiveAsyncCapacity({
|
|
11544
11322
|
sessionId: input.deps.state.currentSessionId,
|
|
11545
11323
|
limit: resolveMaxActiveAsyncRunsPerSession(input.deps.config.maxActiveAsyncRunsPerSession),
|
|
11546
11324
|
runId,
|
|
11547
11325
|
kind: "runner",
|
|
11548
|
-
asyncDir:
|
|
11326
|
+
asyncDir: path18.join(DIRS.async, runId)
|
|
11549
11327
|
}, { abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(input.deps.config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
11550
11328
|
} catch (error) {
|
|
11551
11329
|
if (error instanceof ActiveAsyncCapacityError) return { content: [{ type: "text", text: error.message }], isError: true, details: { mode: "single", results: [], activeAsyncCapacity: error.snapshot } };
|
|
@@ -11556,7 +11334,7 @@ async function resumeAsyncRun(input) {
|
|
|
11556
11334
|
const artifactsDir = recoveryDescriptor?.artifactsDir ?? getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
|
|
11557
11335
|
const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
11558
11336
|
const parentModel = input.parentModel;
|
|
11559
|
-
const revivalAsyncDir =
|
|
11337
|
+
const revivalAsyncDir = path18.join(DIRS.async, runId);
|
|
11560
11338
|
const revivalOutputBaseDir = resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId);
|
|
11561
11339
|
const inheritedOutput = foregroundContract?.output ?? recoveryDescriptor?.outputPath;
|
|
11562
11340
|
const inheritedOutputPath = input.params.output === void 0 && input.params.workflowParentRunId === void 0 ? (target.source === "foreground" ? target.savedOutputPath : void 0) ?? resolveSingleOutputPath(inheritedOutput, input.requestCwd, effectiveCwd, revivalOutputBaseDir) : void 0;
|
|
@@ -11638,7 +11416,7 @@ async function resumeAsyncRun(input) {
|
|
|
11638
11416
|
...input.params.toolBudget !== void 0 ? { toolBudget: input.params.toolBudget } : {},
|
|
11639
11417
|
// Recovery descriptors, remembered foreground runs, and current workflow roots
|
|
11640
11418
|
// preserve parent authority before the selected agent's descendant ceiling.
|
|
11641
|
-
capabilityCeiling: intersectSubagentCapabilityCeilings(recoveryDescriptor?.capabilityCeiling ?? (target.source === "foreground" || target.source === "async" && target.mode === "workflow" ? target.capabilityCeiling : void 0), resolveCurrentSubagentCapabilityCeiling(input.
|
|
11419
|
+
capabilityCeiling: intersectSubagentCapabilityCeilings(recoveryDescriptor?.capabilityCeiling ?? (target.source === "foreground" || target.source === "async" && target.mode === "workflow" ? target.capabilityCeiling : void 0), resolveCurrentSubagentCapabilityCeiling(input.ctx.sessionManager.getSessionId() ?? void 0)),
|
|
11642
11420
|
runFanoutBudget: input.params.runFanoutBudget ?? recoveryDescriptor?.runFanoutBudget ?? createRunFanoutBudget(runId, resolveMaxSubagentSpawnsPerRun(input.deps.config.maxSubagentSpawnsPerRun)),
|
|
11643
11421
|
parentWorkflowRunId: input.params.workflowParentRunId,
|
|
11644
11422
|
workflowKey: input.params.workflowKey,
|
|
@@ -11664,12 +11442,12 @@ async function resumeAsyncRun(input) {
|
|
|
11664
11442
|
return result2;
|
|
11665
11443
|
}
|
|
11666
11444
|
notifyLaunch();
|
|
11667
|
-
for (const brief of queuedBriefs)
|
|
11445
|
+
for (const brief of queuedBriefs) fs20.rmSync(brief.path, { force: true });
|
|
11668
11446
|
if (queuedBriefs.length > 0 && sourceAsyncDir) {
|
|
11669
11447
|
const sourceStatus = readStatus(sourceAsyncDir);
|
|
11670
11448
|
if (sourceStatus?.steering) {
|
|
11671
11449
|
for (const brief of queuedBriefs) updateSteeringTarget(sourceStatus.steering, brief.request.id, target.index, "delivered", Date.now());
|
|
11672
|
-
createCapacityResilientJsonWriter({ keepAlive: true }).write(
|
|
11450
|
+
createCapacityResilientJsonWriter({ keepAlive: true }).write(path18.join(sourceAsyncDir, "status.json"), sourceStatus);
|
|
11673
11451
|
}
|
|
11674
11452
|
}
|
|
11675
11453
|
const revivedId = result2.details.asyncId ?? runId;
|
|
@@ -11731,7 +11509,7 @@ function formatFailedSingleRunOutput(result2, displayOutput) {
|
|
|
11731
11509
|
cappedOutput = capInlineOutput(output, { artifactPath: result2.artifactPaths?.outputPath, savedPath: result2.savedOutputPath });
|
|
11732
11510
|
lines.push("", "Output:", cappedOutput);
|
|
11733
11511
|
}
|
|
11734
|
-
if (cappedOutput === output && result2.artifactPaths?.outputPath &&
|
|
11512
|
+
if (cappedOutput === output && result2.artifactPaths?.outputPath && fs20.existsSync(result2.artifactPaths.outputPath)) {
|
|
11735
11513
|
lines.push("", `Output artifact: ${result2.artifactPaths.outputPath}`);
|
|
11736
11514
|
}
|
|
11737
11515
|
return lines.join("\n");
|
|
@@ -11824,7 +11602,7 @@ async function maybeBuildForegroundIntercomReceipt(input) {
|
|
|
11824
11602
|
}
|
|
11825
11603
|
function diagnosticContextFromDiscovery(discovered, cwd, scope) {
|
|
11826
11604
|
if (discovered.cwd && discovered.scope && discovered.directories) return unknownAgentDiagnosticContext({ ...discovered, cwd: discovered.cwd, scope: discovered.scope, directories: discovered.directories });
|
|
11827
|
-
return unknownAgentDiagnosticContext(discoverAgents(
|
|
11605
|
+
return unknownAgentDiagnosticContext(discoverAgents(path18.resolve(cwd), scope));
|
|
11828
11606
|
}
|
|
11829
11607
|
function canonicalizeAgentName(name, agents, diagnostics, context) {
|
|
11830
11608
|
const resolved = resolveAgentName(name, agents);
|
|
@@ -11857,12 +11635,12 @@ function workflowValidationOptions(deps, params, runtimeCwd, parentModelProvider
|
|
|
11857
11635
|
};
|
|
11858
11636
|
}
|
|
11859
11637
|
function canonicalizeExecutionParams(params, agents, diagnostics, context) {
|
|
11860
|
-
const
|
|
11638
|
+
const resolve9 = (name, location) => {
|
|
11861
11639
|
const result2 = canonicalizeAgentName(name, agents, diagnostics, context);
|
|
11862
11640
|
return result2.error && location ? { error: `${result2.error} (${location})` } : result2;
|
|
11863
11641
|
};
|
|
11864
11642
|
if (params.agent) {
|
|
11865
|
-
const result2 =
|
|
11643
|
+
const result2 = resolve9(params.agent);
|
|
11866
11644
|
if (result2.error) return { error: result2.error };
|
|
11867
11645
|
params = omitUndefinedProperties({ ...params, agent: result2.name });
|
|
11868
11646
|
const agent = agents.find((candidate) => candidate.name === result2.name);
|
|
@@ -11881,7 +11659,7 @@ function canonicalizeExecutionParams(params, agents, diagnostics, context) {
|
|
|
11881
11659
|
const tasks = [];
|
|
11882
11660
|
for (let index = 0; index < params.tasks.length; index++) {
|
|
11883
11661
|
const task = params.tasks[index];
|
|
11884
|
-
const result2 =
|
|
11662
|
+
const result2 = resolve9(task.agent, `task ${index + 1}`);
|
|
11885
11663
|
if (result2.error) return { error: result2.error };
|
|
11886
11664
|
tasks.push({ ...task, agent: result2.name });
|
|
11887
11665
|
}
|
|
@@ -11895,7 +11673,7 @@ function canonicalizeExecutionParams(params, agents, diagnostics, context) {
|
|
|
11895
11673
|
const parallel = [];
|
|
11896
11674
|
for (let taskIndex = 0; taskIndex < step.parallel.length; taskIndex++) {
|
|
11897
11675
|
const task = step.parallel[taskIndex];
|
|
11898
|
-
const result2 =
|
|
11676
|
+
const result2 = resolve9(task.agent, `step ${index + 1}, task ${taskIndex + 1}`);
|
|
11899
11677
|
if (result2.error) return { error: result2.error };
|
|
11900
11678
|
parallel.push({ ...task, agent: result2.name });
|
|
11901
11679
|
}
|
|
@@ -11903,13 +11681,13 @@ function canonicalizeExecutionParams(params, agents, diagnostics, context) {
|
|
|
11903
11681
|
continue;
|
|
11904
11682
|
}
|
|
11905
11683
|
if (isDynamicParallelStep(step)) {
|
|
11906
|
-
const result2 =
|
|
11684
|
+
const result2 = resolve9(step.parallel.agent, `step ${index + 1}`);
|
|
11907
11685
|
if (result2.error) return { error: result2.error };
|
|
11908
11686
|
chain.push({ ...step, parallel: { ...step.parallel, agent: result2.name } });
|
|
11909
11687
|
continue;
|
|
11910
11688
|
}
|
|
11911
11689
|
if ("agent" in step && typeof step.agent === "string") {
|
|
11912
|
-
const result2 =
|
|
11690
|
+
const result2 = resolve9(step.agent, `step ${index + 1}`);
|
|
11913
11691
|
if (result2.error) return { error: result2.error };
|
|
11914
11692
|
chain.push({ ...step, agent: result2.name });
|
|
11915
11693
|
continue;
|
|
@@ -12776,7 +12554,7 @@ async function createSingleWorktreeSetup(cwd, runId, agent, setupHook, setupHook
|
|
|
12776
12554
|
}
|
|
12777
12555
|
}
|
|
12778
12556
|
function resolveConfiguredSingleRunOutputBaseDir(deps) {
|
|
12779
|
-
return deps.config.singleRunOutputBaseDir ?
|
|
12557
|
+
return deps.config.singleRunOutputBaseDir ? path18.resolve(deps.expandTilde(deps.config.singleRunOutputBaseDir)) : void 0;
|
|
12780
12558
|
}
|
|
12781
12559
|
function sanitizeRunPathSegment(value, maxBytes = 120) {
|
|
12782
12560
|
const sanitized = value.trim().replace(/[^A-Za-z0-9._-]+/g, "_").replace(/^_+|_+$/g, "");
|
|
@@ -12785,22 +12563,22 @@ function sanitizeRunPathSegment(value, maxBytes = 120) {
|
|
|
12785
12563
|
return sanitized.slice(0, maxBytes).replace(/_+$/, "") || "unknown";
|
|
12786
12564
|
}
|
|
12787
12565
|
function resolveSingleRunOutputBaseDir(deps, artifactsDir, runId) {
|
|
12788
|
-
return resolveConfiguredSingleRunOutputBaseDir(deps) ??
|
|
12566
|
+
return resolveConfiguredSingleRunOutputBaseDir(deps) ?? path18.join(artifactsDir, "outputs", sanitizeRunPathSegment(runId));
|
|
12789
12567
|
}
|
|
12790
12568
|
function suffixedOutputPath(outputPath, suffix) {
|
|
12791
|
-
const parsed =
|
|
12792
|
-
return
|
|
12569
|
+
const parsed = path18.parse(outputPath);
|
|
12570
|
+
return path18.join(parsed.dir, `${parsed.name}.${suffix}${parsed.ext || ".md"}`);
|
|
12793
12571
|
}
|
|
12794
12572
|
function workflowChildDefaultOutput(aggregateOutputPath, artifactsDir, workflowRunId, workflowKey) {
|
|
12795
12573
|
if (aggregateOutputPath) return suffixedOutputPath(aggregateOutputPath, workflowKey);
|
|
12796
|
-
return
|
|
12574
|
+
return path18.join(artifactsDir, "outputs", sanitizeRunPathSegment(workflowRunId), `${workflowKey}.md`);
|
|
12797
12575
|
}
|
|
12798
12576
|
function workflowHostCommandRunner(input) {
|
|
12799
12577
|
return async (key, params, signal) => {
|
|
12800
12578
|
const authorizationError = input.authorize?.(key, params);
|
|
12801
12579
|
if (authorizationError) throw new Error(authorizationError);
|
|
12802
|
-
const defaultOutputPath =
|
|
12803
|
-
const outputPath = params.output ?
|
|
12580
|
+
const defaultOutputPath = path18.join(input.artifactsDir, "outputs", sanitizeRunPathSegment(input.workflowRunId), "host", `${sanitizeRunPathSegment(key)}.log`);
|
|
12581
|
+
const outputPath = params.output ? path18.resolve(input.workflowCwd, params.output) : defaultOutputPath;
|
|
12804
12582
|
const claimPath = resolveWorkflowHostOutputClaimPath(outputPath);
|
|
12805
12583
|
const previous = input.claimedOutputPaths.get(claimPath);
|
|
12806
12584
|
if (previous) throw new Error(`runs.host('${key}') output path is already claimed by '${previous}': ${outputPath}.`);
|
|
@@ -12814,8 +12592,8 @@ function writeWorkflowAggregateOutput(outputPath, text, producedChildOutputPaths
|
|
|
12814
12592
|
if (!outputPath) return void 0;
|
|
12815
12593
|
try {
|
|
12816
12594
|
if (producedChildOutputPaths.has(resolveWorkflowHostOutputClaimPath(outputPath))) return void 0;
|
|
12817
|
-
|
|
12818
|
-
|
|
12595
|
+
fs20.mkdirSync(path18.dirname(outputPath), { recursive: true });
|
|
12596
|
+
fs20.writeFileSync(outputPath, text, "utf-8");
|
|
12819
12597
|
return void 0;
|
|
12820
12598
|
} catch (error) {
|
|
12821
12599
|
return `Output file error: ${outputPath}
|
|
@@ -12886,7 +12664,7 @@ function resolveWorkflowChildOutputPath(input) {
|
|
|
12886
12664
|
}
|
|
12887
12665
|
const output = rawOutput === true || rawOutput === "true" ? agentOutput : hasExplicitOutput ? rawOutput : input.aggregateOutputPath ? workflowChildDefaultOutput(input.aggregateOutputPath, input.artifactsDir, input.workflowRunId, input.key) : agentOutput;
|
|
12888
12666
|
return {
|
|
12889
|
-
path: resolveSingleOutputPath(output, input.ctxCwd, childCwd, input.configuredOutputBaseDir ??
|
|
12667
|
+
path: resolveSingleOutputPath(output, input.ctxCwd, childCwd, input.configuredOutputBaseDir ?? path18.join(input.artifactsDir, "outputs", sanitizeRunPathSegment(input.workflowRunId))),
|
|
12890
12668
|
inherited: !hasExplicitOutput && !input.aggregateOutputPath && agentOutput !== void 0
|
|
12891
12669
|
};
|
|
12892
12670
|
}
|
|
@@ -12951,7 +12729,7 @@ async function finalizeSingleWorktreeHandoff(input) {
|
|
|
12951
12729
|
try {
|
|
12952
12730
|
return await withWorktreeTransaction(() => {
|
|
12953
12731
|
admitted = true;
|
|
12954
|
-
const diffsDir =
|
|
12732
|
+
const diffsDir = path18.join(input.artifactsDir, "worktree-diffs", input.runId);
|
|
12955
12733
|
const diffs = diffWorktrees(input.worktreeSetup, [input.agent], diffsDir);
|
|
12956
12734
|
const diffSummary = formatWorktreeDiffSummary(diffs);
|
|
12957
12735
|
const manifestPath = parallelHandoffPath(input.artifactsDir, input.runId);
|
|
@@ -13197,7 +12975,7 @@ async function runSinglePath(data, deps) {
|
|
|
13197
12975
|
await cleanupSingleWorktree();
|
|
13198
12976
|
return { content: [{ type: "text", text: validationError }], isError: true, details: { mode: "single", results: [], parallelHandoff: pendingHandoff } };
|
|
13199
12977
|
}
|
|
13200
|
-
const structuredRuntime = params.outputSchema ? createStructuredOutputRuntime(params.outputSchema, artifactConfig.enabled ?
|
|
12978
|
+
const structuredRuntime = params.outputSchema ? createStructuredOutputRuntime(params.outputSchema, artifactConfig.enabled ? path18.join(artifactsDir, "structured-output", runId) : void 0, { acceptanceReport: resolveAcceptanceReportMode(params.acceptance) }) : void 0;
|
|
13201
12979
|
const reads = readsOverride !== void 0 ? readsOverride : agentConfig.defaultReads ?? false;
|
|
13202
12980
|
const readPaths = !foregroundMachine && Array.isArray(reads) ? resolveExistingReadPaths(reads, singleCwd) : [];
|
|
13203
12981
|
const readsInstruction = readPaths.length > 0 ? `[Read from: ${readPaths.join(", ")}]
|
|
@@ -13281,8 +13059,8 @@ async function runSinglePath(data, deps) {
|
|
|
13281
13059
|
};
|
|
13282
13060
|
let r;
|
|
13283
13061
|
let resolveDetachedWorkflowChild;
|
|
13284
|
-
const detachedWorkflowChild = params.workflowAwaitDetached === true ? new Promise((
|
|
13285
|
-
resolveDetachedWorkflowChild =
|
|
13062
|
+
const detachedWorkflowChild = params.workflowAwaitDetached === true ? new Promise((resolve9) => {
|
|
13063
|
+
resolveDetachedWorkflowChild = resolve9;
|
|
13286
13064
|
}) : void 0;
|
|
13287
13065
|
try {
|
|
13288
13066
|
const childSessionFile = sessionFileForTask(params.agent, 0, modelOverride, modelOverrideFromParent, modelOrigin);
|
|
@@ -13427,7 +13205,7 @@ async function runSinglePath(data, deps) {
|
|
|
13427
13205
|
}));
|
|
13428
13206
|
if (launched.detached && foregroundControl) {
|
|
13429
13207
|
foregroundControl.stopForRuntimeReplacement = () => {
|
|
13430
|
-
const sessionFile = childSessionFile &&
|
|
13208
|
+
const sessionFile = childSessionFile && fs20.existsSync(childSessionFile) ? childSessionFile : void 0;
|
|
13431
13209
|
const stopped = { ...launched, detached: void 0, stopped: true, exitCode: 1, error: RUNTIME_REPLACED_STOP_ERROR, finalOutput: RUNTIME_REPLACED_STOP_ERROR, sessionFile };
|
|
13432
13210
|
rememberForegroundRun(deps.state, { modelResponseAliases, runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, results: [stopped], params, effectiveOutput, effectiveOutputMode, extensionBindings: params.extensionBindings, requiredExtensions });
|
|
13433
13211
|
if (childSessionControls) childSessionControls.stop(RUNTIME_REPLACED_STOP_ERROR);
|
|
@@ -13595,8 +13373,8 @@ function bindMissionWorkflowChildAsyncLaunch(params, binding, asyncByDefault, as
|
|
|
13595
13373
|
const requestedAsync = params.async ?? asyncByDefault;
|
|
13596
13374
|
if (!binding || !requestedAsync || params.clarify === true) return params;
|
|
13597
13375
|
const id = asyncId.trim();
|
|
13598
|
-
if (!id ||
|
|
13599
|
-
writeMissionAsyncBinding(
|
|
13376
|
+
if (!id || path18.basename(id) !== id) throw new Error("workflow child async id must be a single path segment");
|
|
13377
|
+
writeMissionAsyncBinding(path18.join(nestedRootRunId ? nestedRunScope(nestedRootRunId).asyncDirRoot : DIRS.async, id), binding);
|
|
13600
13378
|
return { ...params, workflowChildAsyncId: id };
|
|
13601
13379
|
}
|
|
13602
13380
|
function workflowChildResult(key, result2, childParams = {}, resumeState, forcedTerminalOutcome) {
|
|
@@ -13737,7 +13515,7 @@ var WORKFLOW_STEP_STATES = /* @__PURE__ */ new Set(["pending", "running", "compl
|
|
|
13737
13515
|
function isSafeWorkflowChildRunId(value) {
|
|
13738
13516
|
if (typeof value !== "string") return false;
|
|
13739
13517
|
const runId = value.trim();
|
|
13740
|
-
return value === runId && Boolean(runId) && Buffer.byteLength(runId, "utf8") <= MAX_WORKFLOW_CHILD_RUN_ID_BYTES &&
|
|
13518
|
+
return value === runId && Boolean(runId) && Buffer.byteLength(runId, "utf8") <= MAX_WORKFLOW_CHILD_RUN_ID_BYTES && path18.basename(runId) === runId && path18.win32.basename(runId) === runId && !/[\\/]/.test(runId) && !runId.includes("..") && !/[\u0000-\u001f\u007f]/.test(runId);
|
|
13741
13519
|
}
|
|
13742
13520
|
function isMissingWorkflowReceiptDiagnostic(error, workflowRunId) {
|
|
13743
13521
|
return error instanceof Error && error.message.startsWith(`Workflow receipt '${workflowRunId}' is not available because the workflow may still be active or terminal receipt writing failed.`);
|
|
@@ -13746,7 +13524,7 @@ function missingWorkflowReceiptResumeHint(reference, state) {
|
|
|
13746
13524
|
try {
|
|
13747
13525
|
if (!state.currentSessionId) return void 0;
|
|
13748
13526
|
const workflowRunId = reference.workflowRunId.trim();
|
|
13749
|
-
const workflowStatus = readStatus(
|
|
13527
|
+
const workflowStatus = readStatus(path18.join(DIRS.async, workflowRunId));
|
|
13750
13528
|
if (!workflowStatus || workflowStatus.runId !== workflowRunId || workflowStatus.mode !== "workflow" || workflowStatus.sessionId !== state.currentSessionId || typeof workflowStatus.startedAt !== "number" || !Number.isFinite(workflowStatus.startedAt) || !WORKFLOW_RESUME_HINT_PARENT_STATES.has(workflowStatus.state) || !Array.isArray(workflowStatus.steps) || workflowStatus.steps.some((step) => {
|
|
13751
13529
|
if (!step || typeof step !== "object" || Array.isArray(step)) return true;
|
|
13752
13530
|
const record2 = step;
|
|
@@ -13781,7 +13559,7 @@ function resolveWorkflowResume(reference, deps, parentSessionFile, index) {
|
|
|
13781
13559
|
if (status?.runId === runId && status.parentWorkflowRunId && status.workflowKey) {
|
|
13782
13560
|
const receiptPath = workflowReceiptPath(DIRS.async, status.parentWorkflowRunId);
|
|
13783
13561
|
try {
|
|
13784
|
-
|
|
13562
|
+
fs20.statSync(receiptPath);
|
|
13785
13563
|
} catch (error) {
|
|
13786
13564
|
if (error.code === "ENOENT") return { runId, runIds: [runId] };
|
|
13787
13565
|
throw error;
|
|
@@ -13840,11 +13618,11 @@ async function steerWorkflowChildByKey(input) {
|
|
|
13840
13618
|
});
|
|
13841
13619
|
if (!result2.details.steering?.targets.some((target) => target.reason === CHILD_SESSION_NOT_RUNNING_YET) || Date.now() >= deadline) return workflowSteerReceipt(input.key, result2);
|
|
13842
13620
|
}
|
|
13843
|
-
const workflowStatus = readStatus(
|
|
13621
|
+
const workflowStatus = readStatus(path18.join(asyncDirRoot, input.workflowRunId));
|
|
13844
13622
|
const step = workflowStatus?.steps?.find((candidate) => candidate.workflowKey === input.key);
|
|
13845
13623
|
const childRunId = step?.runId ?? input.resolveRunId?.();
|
|
13846
13624
|
if (childRunId) {
|
|
13847
|
-
const asyncDir =
|
|
13625
|
+
const asyncDir = path18.join(asyncDirRoot, childRunId);
|
|
13848
13626
|
const childStatus = reconcileAsyncRun(asyncDir).status;
|
|
13849
13627
|
if (childStatus && childStatus.state !== "running" && childStatus.state !== "queued" && (input.options.mode !== "follow_up" || !canQueueRetainedAsyncFollowUp(childStatus, input.options.index))) {
|
|
13850
13628
|
return { key: input.key, state: "missed", error: `Workflow child '${input.key}' is ${childStatus.state}.` };
|
|
@@ -13874,7 +13652,7 @@ async function steerWorkflowChildByKey(input) {
|
|
|
13874
13652
|
if (input.signal?.aborted || Date.now() >= deadline) {
|
|
13875
13653
|
return { key: input.key, state: "missed", error: `Workflow child '${input.key}' had no live steering route.` };
|
|
13876
13654
|
}
|
|
13877
|
-
await new Promise((
|
|
13655
|
+
await new Promise((resolve9) => setTimeout(resolve9, Math.min(10, Math.max(1, deadline - Date.now()))));
|
|
13878
13656
|
}
|
|
13879
13657
|
}
|
|
13880
13658
|
async function preflightWorkflowWorktrees(input) {
|
|
@@ -14320,7 +14098,7 @@ function createSubagentExecutor(deps) {
|
|
|
14320
14098
|
limit: resolveMaxActiveAsyncRunsPerSession(deps2.config.maxActiveAsyncRunsPerSession),
|
|
14321
14099
|
runId: workflowRunId,
|
|
14322
14100
|
kind: "workflow",
|
|
14323
|
-
asyncDir:
|
|
14101
|
+
asyncDir: path18.join(DIRS.async, workflowRunId)
|
|
14324
14102
|
}, { liveWorkflowRunIds: new Set(deps2.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps2.config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
14325
14103
|
} catch (error) {
|
|
14326
14104
|
if (error instanceof ActiveAsyncCapacityError) {
|
|
@@ -14366,14 +14144,14 @@ function createSubagentExecutor(deps) {
|
|
|
14366
14144
|
};
|
|
14367
14145
|
if (workflowRunId) {
|
|
14368
14146
|
const toolCallId = _id;
|
|
14369
|
-
const workflowSessionRoot = requestParams.sessionDir ?
|
|
14370
|
-
const asyncDir =
|
|
14147
|
+
const workflowSessionRoot = requestParams.sessionDir ? path18.resolve(deps2.expandTilde(requestParams.sessionDir)) : trustedSessionRoots(ctx.sessionManager.getSessionFile() ?? null, deps2)[0];
|
|
14148
|
+
const asyncDir = path18.join(DIRS.async, workflowRunId);
|
|
14371
14149
|
const resultPath = resultFilePath(DIRS.results, workflowRunId);
|
|
14372
|
-
const statusPath =
|
|
14373
|
-
const eventsPath =
|
|
14150
|
+
const statusPath = path18.join(asyncDir, "status.json");
|
|
14151
|
+
const eventsPath = path18.join(asyncDir, "events.jsonl");
|
|
14374
14152
|
const startedAt = Date.now();
|
|
14375
14153
|
const currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
14376
|
-
const workflowCapabilityCeiling2 = intersectSubagentCapabilityCeilings(requestParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(
|
|
14154
|
+
const workflowCapabilityCeiling2 = intersectSubagentCapabilityCeilings(requestParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(ctx.sessionManager.getSessionId() ?? void 0));
|
|
14377
14155
|
const completionOwnerId = deps2.state.completionOwnerId ?? currentCompletionOwnerId();
|
|
14378
14156
|
deps2.state.completionOwnerId = completionOwnerId;
|
|
14379
14157
|
const workflowScriptSourceDigest = workflowScriptDigest(requestParams.workflowScript);
|
|
@@ -14386,7 +14164,7 @@ function createSubagentExecutor(deps) {
|
|
|
14386
14164
|
return { content: [{ type: "text", text: `Failed to create async workflow storage: ${error instanceof Error ? error.message : String(error)}` }], isError: true, details: { mode: "workflow", results: [] } };
|
|
14387
14165
|
};
|
|
14388
14166
|
if (workflowReuseSource) {
|
|
14389
|
-
const reuseSourceDir =
|
|
14167
|
+
const reuseSourceDir = path18.join(DIRS.async, workflowReuseSource.runId);
|
|
14390
14168
|
let claim;
|
|
14391
14169
|
try {
|
|
14392
14170
|
claim = claimWorkflowResume(reuseSourceDir, { newRunId: workflowRunId, trigger: "reuse" });
|
|
@@ -14398,14 +14176,14 @@ function createSubagentExecutor(deps) {
|
|
|
14398
14176
|
return { content: [{ type: "text", text: claimedWorkflowRunRefusal(workflowReuseSource.runId, claim, requestParams.reuseFrom !== void 0) }], isError: true, details: { mode: "workflow", results: [] } };
|
|
14399
14177
|
}
|
|
14400
14178
|
pendingReuseClaimReleases.set(params, () => {
|
|
14401
|
-
if (!
|
|
14179
|
+
if (!fs20.existsSync(statusPath)) releaseWorkflowResumeClaim(reuseSourceDir, workflowRunId);
|
|
14402
14180
|
});
|
|
14403
14181
|
}
|
|
14404
14182
|
try {
|
|
14405
|
-
|
|
14183
|
+
fs20.mkdirSync(asyncDir, { recursive: true });
|
|
14406
14184
|
writeRunFanoutBudgetDescriptor(asyncDir, workflowFanoutBudget);
|
|
14407
14185
|
if (topLevelAsyncWorkflow && publicWorkflowLaunch) writeWorkflowLaunchRecord(asyncDir, workflowLaunchRecord(publicWorkflowLaunch, workflowCwd, workflowScriptSourceDigest));
|
|
14408
|
-
|
|
14186
|
+
fs20.mkdirSync(DIRS.results, { recursive: true });
|
|
14409
14187
|
} catch (error) {
|
|
14410
14188
|
return storageFailure(error);
|
|
14411
14189
|
}
|
|
@@ -14438,7 +14216,7 @@ function createSubagentExecutor(deps) {
|
|
|
14438
14216
|
};
|
|
14439
14217
|
const appendWorkflowEvent = (event) => {
|
|
14440
14218
|
try {
|
|
14441
|
-
|
|
14219
|
+
fs20.appendFileSync(eventsPath, `${JSON.stringify({ ts: Date.now(), runId: workflowRunId, ...event })}
|
|
14442
14220
|
`, "utf-8");
|
|
14443
14221
|
} catch (error) {
|
|
14444
14222
|
console.error(`Failed to append async workflow event '${eventsPath}':`, error);
|
|
@@ -14467,7 +14245,7 @@ function createSubagentExecutor(deps) {
|
|
|
14467
14245
|
const message = `Failed to write async workflow result ${resultPath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
14468
14246
|
console.error(message, error);
|
|
14469
14247
|
try {
|
|
14470
|
-
if (currentSessionId && fallbackResultPayloadPathForSessionRun(
|
|
14248
|
+
if (currentSessionId && fallbackResultPayloadPathForSessionRun(path18.dirname(resultPath), currentSessionId, workflowRunId)) return true;
|
|
14471
14249
|
} catch (pendingError) {
|
|
14472
14250
|
console.error(`Failed to verify pending async workflow result '${workflowRunId}':`, pendingError);
|
|
14473
14251
|
}
|
|
@@ -14575,8 +14353,8 @@ Parent action: ${STATUS_ACTION}`,
|
|
|
14575
14353
|
};
|
|
14576
14354
|
pendingResultPublication = void 0;
|
|
14577
14355
|
runPersistence.write(resultPath, payload);
|
|
14578
|
-
if (!resultPublished) pendingResultPublication = new Promise((
|
|
14579
|
-
settleResultPublication =
|
|
14356
|
+
if (!resultPublished) pendingResultPublication = new Promise((resolve9) => {
|
|
14357
|
+
settleResultPublication = resolve9;
|
|
14580
14358
|
});
|
|
14581
14359
|
return true;
|
|
14582
14360
|
} catch (error) {
|
|
@@ -14913,7 +14691,7 @@ Parent action: ${STATUS_ACTION}`,
|
|
|
14913
14691
|
const step = status.steps?.find((candidate) => candidate.workflowKey === key);
|
|
14914
14692
|
if (!step) return;
|
|
14915
14693
|
if (runId2) step.runId = runId2;
|
|
14916
|
-
step.async = runId2 !== void 0 &&
|
|
14694
|
+
step.async = runId2 !== void 0 && path18.basename(runId2) === runId2 && fs20.existsSync(path18.join(path18.dirname(asyncDir), runId2));
|
|
14917
14695
|
persist({ tolerateStatusWriteFailure: true });
|
|
14918
14696
|
};
|
|
14919
14697
|
try {
|
|
@@ -15014,7 +14792,7 @@ Parent action: ${COMPLETION_ACTION}` : content,
|
|
|
15014
14792
|
}
|
|
15015
14793
|
if (priorChild?.kind === "started") {
|
|
15016
14794
|
const priorRunId = priorChild.runId;
|
|
15017
|
-
const priorAsyncDir =
|
|
14795
|
+
const priorAsyncDir = path18.join(DIRS.async, priorRunId);
|
|
15018
14796
|
appendWorkflowChildJournal(asyncDir, { type: "start", key, fingerprint: journalFingerprint, runId: priorRunId });
|
|
15019
14797
|
recordReusedWorkflowChild(key, priorRunId);
|
|
15020
14798
|
const stopListener = stopAwaitedAsyncChildOnAbort(workflowSignal, deps2.state, priorRunId, priorAsyncDir, deps2.kill, true);
|
|
@@ -15117,6 +14895,7 @@ Parent action: ${COMPLETION_ACTION}` : content,
|
|
|
15117
14895
|
...result2.details.results[0]?.sessionFile ? { sessionPath: result2.details.results[0].sessionFile } : {},
|
|
15118
14896
|
artifactPaths: child.artifactPaths,
|
|
15119
14897
|
...["completed", "failed"].includes(childStatus) ? { completedAt: (/* @__PURE__ */ new Date()).toISOString() } : {},
|
|
14898
|
+
acceptanceStatus: missionAcceptanceStatus(result2.details.results),
|
|
15120
14899
|
heartbeat: { status: childStatus, ...childPhase ? { phase: childPhase } : {} }
|
|
15121
14900
|
});
|
|
15122
14901
|
if (result2.details.asyncId) {
|
|
@@ -15236,7 +15015,7 @@ Parent action: ${COMPLETION_ACTION}` : content,
|
|
|
15236
15015
|
const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : void 0;
|
|
15237
15016
|
const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps2);
|
|
15238
15017
|
const workflowAggregateOutputPath = resolveSingleOutputPath(workflowOutput, ctx.cwd, workflowCwd, resolveSingleRunOutputBaseDir(deps2, workflowArtifactsDir, foregroundWorkflowRunId));
|
|
15239
|
-
const workflowFullResultPath =
|
|
15018
|
+
const workflowFullResultPath = path18.join(workflowArtifactsDir, `${sanitizeRunPathSegment(foregroundWorkflowRunId)}_workflow-result.md`);
|
|
15240
15019
|
const claimedOutputPaths = /* @__PURE__ */ new Map();
|
|
15241
15020
|
const childOutputOverrides = /* @__PURE__ */ new Map();
|
|
15242
15021
|
const childOutputClaimPaths = /* @__PURE__ */ new Map();
|
|
@@ -15261,7 +15040,7 @@ Parent action: ${COMPLETION_ACTION}` : content,
|
|
|
15261
15040
|
let lastProgressAt = 0;
|
|
15262
15041
|
let workflowProgressClosed = false;
|
|
15263
15042
|
const workflowDeadlineAt = timeout === void 0 ? void 0 : Date.now() + timeout;
|
|
15264
|
-
const workflowCapabilityCeiling = intersectSubagentCapabilityCeilings(requestParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(
|
|
15043
|
+
const workflowCapabilityCeiling = intersectSubagentCapabilityCeilings(requestParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(ctx.sessionManager.getSessionId() ?? void 0));
|
|
15265
15044
|
const sendWorkflowProgress = () => {
|
|
15266
15045
|
if (workflowProgressClosed) return;
|
|
15267
15046
|
if (activityTimer) clearTimeout(activityTimer);
|
|
@@ -15394,6 +15173,7 @@ Parent action: ${COMPLETION_ACTION}` : content,
|
|
|
15394
15173
|
...result2.details.results[0]?.sessionFile ? { sessionPath: result2.details.results[0].sessionFile } : {},
|
|
15395
15174
|
artifactPaths: child.artifactPaths,
|
|
15396
15175
|
...["completed", "failed"].includes(childStatus) ? { completedAt: (/* @__PURE__ */ new Date()).toISOString() } : {},
|
|
15176
|
+
acceptanceStatus: missionAcceptanceStatus(result2.details.results),
|
|
15397
15177
|
heartbeat: { status: childStatus, ...childPhase ? { phase: childPhase } : {} }
|
|
15398
15178
|
});
|
|
15399
15179
|
return child;
|
|
@@ -15507,7 +15287,7 @@ ${partial.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`]
|
|
|
15507
15287
|
if (paramsWithResolvedCwd.mode === "apply") {
|
|
15508
15288
|
try {
|
|
15509
15289
|
if (!paramsWithResolvedCwd.repo?.trim() || !paramsWithResolvedCwd.planId?.trim() || paramsWithResolvedCwd.handoffPath !== void 0) throw new Error("worktree.cleanup apply requires explicit repo and planId, without handoffPath.");
|
|
15510
|
-
const repo =
|
|
15290
|
+
const repo = path18.resolve(requestCwd, paramsWithResolvedCwd.repo);
|
|
15511
15291
|
const plan = loadReviewedCleanupPlan(repo, paramsWithResolvedCwd.planId);
|
|
15512
15292
|
const decision = resolveAuthorityDecision({ action: "discardWorktree", ...deps2.config.authorityPolicy === void 0 ? {} : { policy: deps2.config.authorityPolicy } });
|
|
15513
15293
|
if (decision === "forbid") throw new Error("Authority policy forbids worktree cleanup.");
|
|
@@ -15537,8 +15317,8 @@ Remove up to ${plan.entries.filter((entry) => entry.decision === "remove").lengt
|
|
|
15537
15317
|
}
|
|
15538
15318
|
try {
|
|
15539
15319
|
const created = createWorktreeCleanupPlan({
|
|
15540
|
-
repo: paramsWithResolvedCwd.repo?.trim() ?
|
|
15541
|
-
...paramsWithResolvedCwd.handoffPath ? { handoffPath:
|
|
15320
|
+
repo: paramsWithResolvedCwd.repo?.trim() ? path18.isAbsolute(paramsWithResolvedCwd.repo) ? paramsWithResolvedCwd.repo : path18.resolve(requestCwd, paramsWithResolvedCwd.repo) : requestCwd,
|
|
15321
|
+
...paramsWithResolvedCwd.handoffPath ? { handoffPath: path18.isAbsolute(paramsWithResolvedCwd.handoffPath) ? paramsWithResolvedCwd.handoffPath : path18.resolve(requestCwd, paramsWithResolvedCwd.handoffPath) } : {},
|
|
15542
15322
|
...deps2.config.worktreeBaseDir ? { worktreeBaseDir: deps2.config.worktreeBaseDir } : {},
|
|
15543
15323
|
foregroundRunOwnership: (runId2) => {
|
|
15544
15324
|
if (deps2.state.foregroundControls.has(runId2)) return "active";
|
|
@@ -15571,7 +15351,7 @@ ${paramsWithResolvedCwd.handoffPath}`);
|
|
|
15571
15351
|
}
|
|
15572
15352
|
if (!confirmed) return { content: [{ type: "text", text: "Worktree discard canceled; preserved worktrees were not changed." }], details: { mode: "management", results: [] } };
|
|
15573
15353
|
try {
|
|
15574
|
-
const manifestPath =
|
|
15354
|
+
const manifestPath = path18.isAbsolute(paramsWithResolvedCwd.handoffPath) ? paramsWithResolvedCwd.handoffPath : path18.resolve(requestCwd, paramsWithResolvedCwd.handoffPath);
|
|
15575
15355
|
const discarded = await withWorktreeTransaction(() => discardPreservedWorktrees(
|
|
15576
15356
|
manifestPath,
|
|
15577
15357
|
{ kind: decision === "confirm" ? "confirmed" : "policy", ...deps2.config.authorityPolicy ? { policy: deps2.config.authorityPolicy } : {} }
|
|
@@ -15589,7 +15369,7 @@ ${paramsWithResolvedCwd.handoffPath}`);
|
|
|
15589
15369
|
if (!laneId) return { content: [{ type: "text", text: `${action} requires laneId.` }], isError: true, details: { mode: "management", results: [] } };
|
|
15590
15370
|
const handoffPath = paramsWithResolvedCwd.handoffPath?.trim();
|
|
15591
15371
|
if (!handoffPath) return { content: [{ type: "text", text: `${action} requires handoffPath for the existing parallel handoff manifest.` }], isError: true, details: { mode: "management", results: [] } };
|
|
15592
|
-
const manifestPath =
|
|
15372
|
+
const manifestPath = path18.isAbsolute(handoffPath) ? handoffPath : path18.resolve(requestCwd, handoffPath);
|
|
15593
15373
|
try {
|
|
15594
15374
|
if (action === "lane.status") {
|
|
15595
15375
|
let manifest;
|
|
@@ -15765,9 +15545,7 @@ Usage is not reset. Compaction keeps the same budget; a new parent session start
|
|
|
15765
15545
|
if (action === "guide") {
|
|
15766
15546
|
try {
|
|
15767
15547
|
const guide = readSubagentGuide(paramsWithResolvedCwd.topic);
|
|
15768
|
-
const
|
|
15769
|
-
const fullNotice = topic === "tool-reference" ? disabledFeatureNotice(disabledFeatures) : void 0;
|
|
15770
|
-
const notice = fullNotice && section !== void 0 ? "Some options and actions documented here are disabled by config in this session; guide topic tool-reference lists them." : fullNotice;
|
|
15548
|
+
const notice = paramsWithResolvedCwd.topic?.includes("/") ? disabledFeatureSectionNotice(disabledFeatures) : disabledFeatureNotice(disabledFeatures);
|
|
15771
15549
|
return {
|
|
15772
15550
|
content: [{ type: "text", text: notice ? `${notice}
|
|
15773
15551
|
|
|
@@ -15918,7 +15696,7 @@ ${item.text}` } : item)
|
|
|
15918
15696
|
if (paramsWithResolvedCwd.dir) {
|
|
15919
15697
|
try {
|
|
15920
15698
|
const location = resolveAsyncRunLocation(paramsWithResolvedCwd, DIRS.async, DIRS.results);
|
|
15921
|
-
const runId2 = location.resolvedId ?? targetRunId ??
|
|
15699
|
+
const runId2 = location.resolvedId ?? targetRunId ?? path18.basename(location.asyncDir ?? paramsWithResolvedCwd.dir);
|
|
15922
15700
|
const directoryStatus = location.asyncDir ? readStatus(location.asyncDir) : null;
|
|
15923
15701
|
if (directoryStatus?.mode === "workflow") {
|
|
15924
15702
|
return steerWorkflowRun({ state: deps2.state, runId: runId2, asyncDir: location.asyncDir, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
|
|
@@ -16051,7 +15829,7 @@ ${item.text}` } : item)
|
|
|
16051
15829
|
if (!stopChild) return { content: [{ type: "text", text: `Workflow ${workflowRunId} child stop is unavailable in this extension runtime.` }], isError: true, details: { mode: "management", results: [] } };
|
|
16052
15830
|
if (!stopChild(resolution.child.id, `Workflow child '${resolution.child.id}' stopped.`)) return { content: [{ type: "text", text: `Child '${paramsWithResolvedCwd.childId}' in workflow ${workflowRunId} is not available to stop.` }], isError: true, details: { mode: "management", results: [] } };
|
|
16053
15831
|
try {
|
|
16054
|
-
|
|
15832
|
+
fs20.appendFileSync(path18.join(asyncJob.asyncDir, "events.jsonl"), `${JSON.stringify({
|
|
16055
15833
|
type: "subagent.child-status",
|
|
16056
15834
|
version: 1,
|
|
16057
15835
|
runId: workflowRunId,
|
|
@@ -16077,7 +15855,7 @@ ${item.text}` } : item)
|
|
|
16077
15855
|
if (paramsWithResolvedCwd.dir) {
|
|
16078
15856
|
try {
|
|
16079
15857
|
const location = resolveAsyncRunLocation(paramsWithResolvedCwd, DIRS.async, DIRS.results);
|
|
16080
|
-
const stopResult2 = stopAsyncRun(deps2.state, location.resolvedId ?? targetRunId ??
|
|
15858
|
+
const stopResult2 = stopAsyncRun(deps2.state, location.resolvedId ?? targetRunId ?? path18.basename(location.asyncDir ?? paramsWithResolvedCwd.dir), deps2.kill, location, paramsWithResolvedCwd.childId);
|
|
16081
15859
|
return stopResult2 ?? { content: [{ type: "text", text: `No running or queued async run was found for '${targetRunId ?? paramsWithResolvedCwd.dir}'.` }], isError: true, details: { mode: "management", results: [] } };
|
|
16082
15860
|
} catch (error) {
|
|
16083
15861
|
const text = error instanceof Error ? error.message : String(error);
|
|
@@ -16102,21 +15880,20 @@ ${item.text}` } : item)
|
|
|
16102
15880
|
);
|
|
16103
15881
|
if (stopResult) return stopResult;
|
|
16104
15882
|
return {
|
|
16105
|
-
content: [{ type: "text", text:
|
|
15883
|
+
content: [{ type: "text", text: `No stoppable async run found for '${targetRunId}'.` }],
|
|
16106
15884
|
isError: true,
|
|
16107
15885
|
details: { mode: "management", results: [] }
|
|
16108
15886
|
};
|
|
16109
15887
|
}
|
|
16110
15888
|
if (action === "interrupt") {
|
|
16111
15889
|
const targetRunId = paramsWithResolvedCwd.runId ?? paramsWithResolvedCwd.id;
|
|
15890
|
+
if (!targetRunId) return { content: [{ type: "text", text: "action='interrupt' requires id." }], isError: true, details: { mode: "management", results: [] } };
|
|
16112
15891
|
let resolved;
|
|
16113
|
-
|
|
16114
|
-
|
|
16115
|
-
|
|
16116
|
-
|
|
16117
|
-
|
|
16118
|
-
return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
|
|
16119
|
-
}
|
|
15892
|
+
try {
|
|
15893
|
+
resolved = resolveSubagentRunId(targetRunId, omitUndefinedProperties({ state: deps2.state, nested: nestedResolutionScopeForExecutor(deps2) }));
|
|
15894
|
+
} catch (error) {
|
|
15895
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15896
|
+
return { content: [{ type: "text", text: message }], isError: true, details: { mode: "management", results: [] } };
|
|
16120
15897
|
}
|
|
16121
15898
|
if (resolved?.kind === "nested") return interruptNestedRun(resolved);
|
|
16122
15899
|
const foreground = getForegroundControl(deps2.state, resolved?.kind === "foreground" ? resolved.id : targetRunId);
|
|
@@ -16144,7 +15921,7 @@ ${item.text}` } : item)
|
|
|
16144
15921
|
);
|
|
16145
15922
|
if (asyncInterruptResult) return asyncInterruptResult;
|
|
16146
15923
|
return {
|
|
16147
|
-
content: [{ type: "text", text:
|
|
15924
|
+
content: [{ type: "text", text: `No interrupt-capable run found for '${targetRunId}'.` }],
|
|
16148
15925
|
isError: true,
|
|
16149
15926
|
details: { mode: "management", results: [] }
|
|
16150
15927
|
};
|
|
@@ -16160,7 +15937,7 @@ ${item.text}` } : item)
|
|
|
16160
15937
|
...ctx,
|
|
16161
15938
|
cwd: requestCwd,
|
|
16162
15939
|
config: deps2.config,
|
|
16163
|
-
|
|
15940
|
+
piSessionId: ctx.sessionManager.getSessionId() ?? void 0,
|
|
16164
15941
|
discoverAgentsAll: deps2.discoverAgentsAll,
|
|
16165
15942
|
runtimeAgentOwner: deps2.pi,
|
|
16166
15943
|
onAgentsChanged: deps2.onAgentsChanged
|
|
@@ -16262,7 +16039,7 @@ ${item.text}` } : item)
|
|
|
16262
16039
|
let prepareForkSessionForIndex = async () => {
|
|
16263
16040
|
};
|
|
16264
16041
|
const launchCapabilityCeiling = () => intersectSubagentCapabilityCeilings(
|
|
16265
|
-
intersectSubagentCapabilityCeilings(effectiveParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(
|
|
16042
|
+
intersectSubagentCapabilityCeilings(effectiveParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(requestPiSessionId)),
|
|
16266
16043
|
deps2.childRuntime?.capabilityCeiling
|
|
16267
16044
|
);
|
|
16268
16045
|
const assertLaunchableAgentsAllowedBeforeFork = () => {
|
|
@@ -16325,7 +16102,7 @@ ${item.text}` } : item)
|
|
|
16325
16102
|
if (foregroundTimeout.error) return buildRequestedModeError(effectiveParams, foregroundTimeout.error);
|
|
16326
16103
|
const controlConfig = resolveControlConfig(deps2.config.control, effectiveParams.control);
|
|
16327
16104
|
const requestedWorkflowChildAsyncId = typeof effectiveParams.workflowChildAsyncId === "string" ? effectiveParams.workflowChildAsyncId.trim() : "";
|
|
16328
|
-
const asyncRunId = requestedWorkflowChildAsyncId &&
|
|
16105
|
+
const asyncRunId = requestedWorkflowChildAsyncId && path18.basename(requestedWorkflowChildAsyncId) === requestedWorkflowChildAsyncId ? requestedWorkflowChildAsyncId : randomUUID5();
|
|
16329
16106
|
const topLevelAsyncCapacityEligible = depth === 0 && !inheritedNestedRouteValue && !effectiveParams.workflowParentRunId;
|
|
16330
16107
|
const topLevelAsync = effectiveAsync && topLevelAsyncCapacityEligible;
|
|
16331
16108
|
let activeAsyncCapacity;
|
|
@@ -16337,7 +16114,7 @@ ${item.text}` } : item)
|
|
|
16337
16114
|
limit: activeLimit,
|
|
16338
16115
|
runId: asyncRunId,
|
|
16339
16116
|
kind: "runner",
|
|
16340
|
-
asyncDir:
|
|
16117
|
+
asyncDir: path18.join(DIRS.async, asyncRunId)
|
|
16341
16118
|
}, { liveWorkflowRunIds: new Set(deps2.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps2.config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
16342
16119
|
deps2.state.activeAsyncCapacity = getActiveAsyncCapacitySnapshot(requestSessionId, activeLimit, { liveWorkflowRunIds: new Set(deps2.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps2.config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
16343
16120
|
} catch (error) {
|
|
@@ -16373,13 +16150,13 @@ ${item.text}` } : item)
|
|
|
16373
16150
|
let sessionRoot;
|
|
16374
16151
|
sessionRunId = effectiveAsync ? asyncRunId : runId;
|
|
16375
16152
|
if (effectiveParams.sessionDir) {
|
|
16376
|
-
sessionRoot =
|
|
16153
|
+
sessionRoot = path18.join(path18.resolve(deps2.expandTilde(effectiveParams.sessionDir)), sessionRunId);
|
|
16377
16154
|
} else {
|
|
16378
|
-
const baseSessionRoot = deps2.config.defaultSessionDir ?
|
|
16379
|
-
sessionRoot =
|
|
16155
|
+
const baseSessionRoot = deps2.config.defaultSessionDir ? path18.resolve(deps2.expandTilde(deps2.config.defaultSessionDir)) : deps2.getSubagentSessionRoot(parentSessionFile);
|
|
16156
|
+
sessionRoot = path18.join(baseSessionRoot, sessionRunId);
|
|
16380
16157
|
}
|
|
16381
16158
|
try {
|
|
16382
|
-
|
|
16159
|
+
fs20.mkdirSync(sessionRoot, { recursive: true });
|
|
16383
16160
|
} catch (error) {
|
|
16384
16161
|
activeAsyncCapacity?.rollback();
|
|
16385
16162
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -16389,7 +16166,7 @@ ${item.text}` } : item)
|
|
|
16389
16166
|
contextPolicy.contextSummary
|
|
16390
16167
|
);
|
|
16391
16168
|
}
|
|
16392
|
-
const sessionDirForIndex = (idx) =>
|
|
16169
|
+
const sessionDirForIndex = (idx) => path18.join(sessionRoot, `run-${idx ?? 0}`);
|
|
16393
16170
|
const forkSessionFileForTask = (agentName, idx = 0) => {
|
|
16394
16171
|
if (!shouldForkAgent(contextPolicy, agentName)) return void 0;
|
|
16395
16172
|
return forkSessionFileForIndex(idx);
|
|
@@ -16399,8 +16176,8 @@ ${item.text}` } : item)
|
|
|
16399
16176
|
await prepareForkSessionForIndex(idx);
|
|
16400
16177
|
};
|
|
16401
16178
|
const thinkingOverrideForTask = () => delegatedThinkingOverride;
|
|
16402
|
-
const childSessionFileForTask = (agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) => forkSessionFileForTask(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) ??
|
|
16403
|
-
const childSessionFileForIndex = (idx) =>
|
|
16179
|
+
const childSessionFileForTask = (agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) => forkSessionFileForTask(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) ?? path18.join(sessionDirForIndex(idx), "session.jsonl");
|
|
16180
|
+
const childSessionFileForIndex = (idx) => path18.join(sessionDirForIndex(idx), "session.jsonl");
|
|
16404
16181
|
try {
|
|
16405
16182
|
assertLaunchableAgentsAllowedBeforeFork();
|
|
16406
16183
|
if (!(effectiveParams.clarify === true && ctx.hasUI) || deps2.config.forkContext?.mode === "pruned") {
|
|
@@ -16508,7 +16285,7 @@ ${item.text}` } : item)
|
|
|
16508
16285
|
scopedModelIds: scopedModelIdsFromContext(ctx),
|
|
16509
16286
|
parentSessionId: requestSessionId,
|
|
16510
16287
|
parentPiSessionId: requestPiSessionId,
|
|
16511
|
-
capabilityCeiling: intersectSubagentCapabilityCeilings(effectiveParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(
|
|
16288
|
+
capabilityCeiling: intersectSubagentCapabilityCeilings(effectiveParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(requestPiSessionId)),
|
|
16512
16289
|
runFanoutBudget,
|
|
16513
16290
|
topLevelAsyncCapacityEligible,
|
|
16514
16291
|
activeAsyncCapacity,
|