pi-subagents 0.82.1 → 0.83.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/bundle/agents.js +4 -4
- package/bundle/background-work.js +1 -1
- package/bundle/child-tool-plan.js +3 -3
- package/bundle/{chunk-FXUXCX2K.js → chunk-4BW55BTL.js} +6 -6
- package/bundle/{chunk-PBK2I7WY.js → chunk-527E3D2J.js} +6 -6
- package/bundle/{chunk-46XKCPJR.js → chunk-5ESXK62X.js} +7 -1073
- package/bundle/{chunk-UTT254JE.js → chunk-6B4ITO2W.js} +8 -4
- package/bundle/{chunk-CIZQXJ5Q.js → chunk-7F5SK2U3.js} +39 -6
- package/bundle/{chunk-OYF6AFDY.js → chunk-BKFWQ642.js} +2 -2
- package/bundle/{chunk-ZIV4J4HG.js → chunk-CDI3GMNV.js} +47 -30
- package/bundle/{chunk-3JCDWF4V.js → chunk-CGXWSNPA.js} +12 -18
- package/bundle/{chunk-JYUKW2SY.js → chunk-CNLDYEH4.js} +12 -12
- package/bundle/{chunk-4SDU2BZX.js → chunk-CSSZRTKY.js} +2 -2
- package/bundle/{chunk-VSUSLDNW.js → chunk-CZTMUBUI.js} +10 -8
- package/bundle/{chunk-IVAE5Z6Q.js → chunk-HJJAVHFB.js} +28 -24
- package/bundle/{chunk-GJECHSAR.js → chunk-HLE22FSH.js} +2 -2
- package/bundle/{chunk-4Y2ROR7L.js → chunk-HXVKGS33.js} +5 -5
- package/bundle/{chunk-5LFLVSTM.js → chunk-JEZOSEWG.js} +2 -0
- package/bundle/{chunk-YKEWIB4M.js → chunk-JHZ6DZ5M.js} +1 -1
- package/bundle/{chunk-QE4AGIMG.js → chunk-LDMXJIA6.js} +457 -657
- package/bundle/{chunk-43SCHIMD.js → chunk-MBDOYZ2V.js} +1 -1
- package/bundle/{chunk-XSWOTIA3.js → chunk-MHX22USZ.js} +1 -1
- package/bundle/{chunk-6XJB7PIX.js → chunk-OOFW75PV.js} +4 -4
- package/bundle/{chunk-RMQFMMAT.js → chunk-P2NCCTZP.js} +231 -10
- package/bundle/{chunk-UCPP4GNU.js → chunk-RZJN63GU.js} +12 -5
- package/bundle/{chunk-G4GZD3OJ.js → chunk-TJQJ3MEA.js} +10 -10
- package/bundle/{chunk-5TYMSZX3.js → chunk-VTMNBPCQ.js} +2 -2
- package/bundle/{chunk-52UBALJL.js → chunk-WG2OTTAK.js} +2 -2
- package/bundle/{chunk-N2BN7BF5.js → chunk-XA3DY6UG.js} +7 -5
- package/bundle/{chunk-PDZWJCHD.js → chunk-XXKG3P2W.js} +9 -6
- package/bundle/{chunk-PLCCBLPY.js → chunk-Y3I6H6DV.js} +2 -2
- package/bundle/control-channel.js +3 -3
- package/bundle/index.js +1 -1
- package/bundle/intercom-bridge.js +3 -3
- package/bundle/preflight.js +9 -9
- package/bundle/project-panes.js +4 -4
- package/bundle/shared-types.js +1 -1
- package/docs/configuration.md +1 -1
- package/docs/extension-api.md +19 -4
- package/docs/observability.md +1 -0
- package/docs/tool-reference.md +1 -1
- package/package.json +1 -1
- package/skills/pi-subagents/SKILL.md +6 -6
- package/src/agents/agent-management.js +8 -14
- package/src/agents/agent-serializer.js +1 -2
- package/src/agents/agents.js +19 -3
- package/src/api/background-work.d.ts +9 -6
- package/src/api/background-work.js +14 -7
- package/src/extension/index.js +4 -1
- 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/runs/background/async-execution.js +4 -1
- package/src/runs/background/async-job-tracker.js +2 -1
- package/src/runs/background/auto-drain.js +2 -3
- package/src/runs/background/fleet-view.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 +2 -5
- 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/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 +3 -1
- package/src/shared/types.js +2 -0
- package/src/workflows/scripted-workflow.js +9 -1070
- package/workflow-script-worker.cjs +1067 -0
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
projectRuntimeAcknowledgedExtensions,
|
|
4
4
|
registerSubagentPromptBoundary,
|
|
5
5
|
registerSubagentPromptRuntime
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-CGXWSNPA.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-527E3D2J.js";
|
|
29
29
|
import {
|
|
30
|
-
|
|
30
|
+
PLACED_RESTORE_UNSUPPORTED,
|
|
31
|
+
formatHerdrMachineRunnerUnsupported,
|
|
31
32
|
isRexLocalMachine,
|
|
32
|
-
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
resolveMachinePlacement
|
|
34
|
+
} from "./chunk-P2NCCTZP.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-CDI3GMNV.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-CZTMUBUI.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-4BW55BTL.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-OOFW75PV.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-HLE22FSH.js";
|
|
231
232
|
import {
|
|
232
233
|
handleRefinementAction
|
|
233
|
-
} from "./chunk-
|
|
234
|
+
} from "./chunk-Y3I6H6DV.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-RZJN63GU.js";
|
|
248
250
|
import {
|
|
249
251
|
DEFAULT_STALE_TERMINAL_ACTIVE_MARKER_MS,
|
|
250
252
|
MACHINE_DIFF_OPTIONS,
|
|
@@ -323,7 +325,7 @@ import {
|
|
|
323
325
|
writeNestedControlRequest,
|
|
324
326
|
writeNestedControlResult,
|
|
325
327
|
writeNestedEvent
|
|
326
|
-
} from "./chunk-
|
|
328
|
+
} from "./chunk-5ESXK62X.js";
|
|
327
329
|
import {
|
|
328
330
|
closeSteerInbox,
|
|
329
331
|
consumeSteerRequests,
|
|
@@ -331,7 +333,7 @@ import {
|
|
|
331
333
|
readRevivalBriefs,
|
|
332
334
|
requestAsyncSteer,
|
|
333
335
|
watchAsyncControlInbox
|
|
334
|
-
} from "./chunk-
|
|
336
|
+
} from "./chunk-BKFWQ642.js";
|
|
335
337
|
import "./chunk-NIXWDZZV.js";
|
|
336
338
|
import {
|
|
337
339
|
INTERCOM_BRIDGE_MARKER,
|
|
@@ -340,7 +342,7 @@ import {
|
|
|
340
342
|
resolveIntercomBridge,
|
|
341
343
|
resolveIntercomSessionTarget,
|
|
342
344
|
resolveSubagentIntercomTarget
|
|
343
|
-
} from "./chunk-
|
|
345
|
+
} from "./chunk-MHX22USZ.js";
|
|
344
346
|
import {
|
|
345
347
|
resolveLaunchBinding,
|
|
346
348
|
stableJsonDigest
|
|
@@ -354,7 +356,7 @@ import {
|
|
|
354
356
|
resolvePiLaunchToolPlan,
|
|
355
357
|
resolveRuntimeModulePath,
|
|
356
358
|
supervisorChannelDir
|
|
357
|
-
} from "./chunk-
|
|
359
|
+
} from "./chunk-CSSZRTKY.js";
|
|
358
360
|
import {
|
|
359
361
|
assertRequiredChildExtensionsAdmitted,
|
|
360
362
|
hasMandatoryRequiredChildExtensions,
|
|
@@ -365,7 +367,7 @@ import {
|
|
|
365
367
|
import {
|
|
366
368
|
HERDR_PROJECT_PANE_ACTIONS,
|
|
367
369
|
handleHerdrProjectPaneAction
|
|
368
|
-
} from "./chunk-
|
|
370
|
+
} from "./chunk-WG2OTTAK.js";
|
|
369
371
|
import "./chunk-7J4ZXMY7.js";
|
|
370
372
|
import {
|
|
371
373
|
PROJECT_SUBAGENTS_RELATIVE_DIR,
|
|
@@ -378,14 +380,13 @@ import {
|
|
|
378
380
|
getProjectSubagentsDir,
|
|
379
381
|
writeArtifact,
|
|
380
382
|
writeMetadata
|
|
381
|
-
} from "./chunk-
|
|
382
|
-
import "./chunk-
|
|
383
|
+
} from "./chunk-VTMNBPCQ.js";
|
|
384
|
+
import "./chunk-XA3DY6UG.js";
|
|
383
385
|
import {
|
|
384
386
|
getExternalJobProvider
|
|
385
387
|
} from "./chunk-YYREO77V.js";
|
|
386
|
-
import "./chunk-
|
|
388
|
+
import "./chunk-JHZ6DZ5M.js";
|
|
387
389
|
import {
|
|
388
|
-
CODE_OWNED_EXTERNAL_CLI_ADAPTER_IDS,
|
|
389
390
|
ChainOutputValidationError,
|
|
390
391
|
MAX_STREAMED_RECENT_TOOLS,
|
|
391
392
|
MISSING_STRUCTURED_ACCEPTANCE_REPORT_ERROR,
|
|
@@ -449,7 +450,6 @@ import {
|
|
|
449
450
|
formatUnknownAgentError,
|
|
450
451
|
getAgentDir,
|
|
451
452
|
getFinalOutput,
|
|
452
|
-
getProjectConfigDir,
|
|
453
453
|
getSingleResultOutput,
|
|
454
454
|
getStepAgents,
|
|
455
455
|
hasEmptyTerminalAssistantResponse,
|
|
@@ -531,7 +531,7 @@ import {
|
|
|
531
531
|
writeAtomicJson,
|
|
532
532
|
writeInitialProgressFile,
|
|
533
533
|
writePrivateAtomicJson
|
|
534
|
-
} from "./chunk-
|
|
534
|
+
} from "./chunk-7F5SK2U3.js";
|
|
535
535
|
import {
|
|
536
536
|
CHAIN_RUNS_DIR,
|
|
537
537
|
DEFAULT_ARTIFACT_CONFIG,
|
|
@@ -560,7 +560,7 @@ import {
|
|
|
560
560
|
resolveMaxSubagentSpawnsPerSession,
|
|
561
561
|
truncateOutput,
|
|
562
562
|
wrapForkTask
|
|
563
|
-
} from "./chunk-
|
|
563
|
+
} from "./chunk-JEZOSEWG.js";
|
|
564
564
|
import {
|
|
565
565
|
safeTerminalText
|
|
566
566
|
} from "./chunk-YFCK7H2S.js";
|
|
@@ -573,8 +573,8 @@ import {
|
|
|
573
573
|
|
|
574
574
|
// src/runs/foreground/subagent-executor.ts
|
|
575
575
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
576
|
-
import * as
|
|
577
|
-
import * as
|
|
576
|
+
import * as fs20 from "node:fs";
|
|
577
|
+
import * as path18 from "node:path";
|
|
578
578
|
|
|
579
579
|
// src/shared/capacity-resilient-json.ts
|
|
580
580
|
var defaultTimerApi = {
|
|
@@ -775,235 +775,24 @@ function finishForegroundChild(control, index) {
|
|
|
775
775
|
}
|
|
776
776
|
|
|
777
777
|
// src/extension/doctor.ts
|
|
778
|
-
import * as
|
|
779
|
-
import * as
|
|
778
|
+
import * as fs7 from "node:fs";
|
|
779
|
+
import * as path6 from "node:path";
|
|
780
780
|
|
|
781
781
|
// src/runs/background/async-execution.ts
|
|
782
782
|
import { spawn } from "node:child_process";
|
|
783
783
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
784
|
-
import * as
|
|
785
|
-
import * as
|
|
784
|
+
import * as fs6 from "node:fs";
|
|
785
|
+
import * as path5 from "node:path";
|
|
786
786
|
import { fileURLToPath } from "node:url";
|
|
787
787
|
import * as nodeModule from "node:module";
|
|
788
788
|
|
|
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
789
|
// src/runs/shared/launch-cwd.ts
|
|
1001
|
-
import * as
|
|
790
|
+
import * as fs from "node:fs";
|
|
1002
791
|
function preflightLaunchCwd(requestedCwd, effectiveCwd) {
|
|
1003
792
|
const resolution = requestedCwd === effectiveCwd ? "" : `
|
|
1004
793
|
(resolved from ${JSON.stringify(requestedCwd)})`;
|
|
1005
794
|
try {
|
|
1006
|
-
if (!
|
|
795
|
+
if (!fs.statSync(effectiveCwd).isDirectory()) {
|
|
1007
796
|
return `Subagent launch aborted: cwd is not a directory: ${effectiveCwd}${resolution}`;
|
|
1008
797
|
}
|
|
1009
798
|
} catch (error) {
|
|
@@ -1025,23 +814,23 @@ function backgroundProcessOptions(platform = process.platform) {
|
|
|
1025
814
|
}
|
|
1026
815
|
|
|
1027
816
|
// src/runs/shared/child-launch.ts
|
|
1028
|
-
import * as
|
|
1029
|
-
import * as
|
|
817
|
+
import * as fs3 from "node:fs";
|
|
818
|
+
import * as path2 from "node:path";
|
|
1030
819
|
|
|
1031
820
|
// src/extension/fanout-child.ts
|
|
1032
|
-
import * as
|
|
821
|
+
import * as fs2 from "node:fs";
|
|
1033
822
|
import * as os from "node:os";
|
|
1034
|
-
import * as
|
|
823
|
+
import * as path from "node:path";
|
|
1035
824
|
function getSubagentSessionRoot(parentSessionFile) {
|
|
1036
825
|
if (parentSessionFile) {
|
|
1037
|
-
const baseName =
|
|
1038
|
-
const sessionsDir =
|
|
1039
|
-
return
|
|
826
|
+
const baseName = path.basename(parentSessionFile, ".jsonl");
|
|
827
|
+
const sessionsDir = path.dirname(parentSessionFile);
|
|
828
|
+
return path.join(sessionsDir, baseName);
|
|
1040
829
|
}
|
|
1041
|
-
return
|
|
830
|
+
return fs2.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-session-"));
|
|
1042
831
|
}
|
|
1043
832
|
function expandTilde(p) {
|
|
1044
|
-
return p.startsWith("~/") ?
|
|
833
|
+
return p.startsWith("~/") ? path.join(os.homedir(), p.slice(2)) : p;
|
|
1045
834
|
}
|
|
1046
835
|
function createChildSafeState() {
|
|
1047
836
|
return {
|
|
@@ -1127,7 +916,7 @@ ${request.message.trim()}`,
|
|
|
1127
916
|
inboxState.pendingResults.delete(request.requestId);
|
|
1128
917
|
inboxState.seen.add(request.requestId);
|
|
1129
918
|
try {
|
|
1130
|
-
|
|
919
|
+
fs2.unlinkSync(request.filePath);
|
|
1131
920
|
} catch {
|
|
1132
921
|
}
|
|
1133
922
|
} finally {
|
|
@@ -1413,12 +1202,12 @@ function childProcessEnv(input, toolPlan) {
|
|
|
1413
1202
|
}
|
|
1414
1203
|
function childStorage(input) {
|
|
1415
1204
|
if (input.sessionFile) {
|
|
1416
|
-
|
|
1205
|
+
fs3.mkdirSync(path2.dirname(input.sessionFile), { recursive: true });
|
|
1417
1206
|
return { kind: "file", sessionFile: input.sessionFile };
|
|
1418
1207
|
}
|
|
1419
1208
|
if (!input.sessionEnabled) return { kind: "memory" };
|
|
1420
1209
|
if (input.sessionDir) {
|
|
1421
|
-
|
|
1210
|
+
fs3.mkdirSync(input.sessionDir, { recursive: true });
|
|
1422
1211
|
return { kind: "dir", sessionDir: input.sessionDir };
|
|
1423
1212
|
}
|
|
1424
1213
|
return { kind: "default" };
|
|
@@ -1472,8 +1261,8 @@ function buildInProcessChildLaunch(input) {
|
|
|
1472
1261
|
let supervisorDir;
|
|
1473
1262
|
if (input.orchestratorIntercomTarget && input.parentSessionId && input.runId) {
|
|
1474
1263
|
supervisorDir = supervisorChannelDir(input.runId, input.childAgentName, input.childIndex);
|
|
1475
|
-
|
|
1476
|
-
|
|
1264
|
+
fs3.mkdirSync(path2.join(supervisorDir, "requests"), { recursive: true });
|
|
1265
|
+
fs3.mkdirSync(path2.join(supervisorDir, "replies"), { recursive: true });
|
|
1477
1266
|
}
|
|
1478
1267
|
const thinkingCeiling = intersectThinkingCeilings(input.thinkingCeiling, inherited?.thinkingCeiling);
|
|
1479
1268
|
let structuredValue;
|
|
@@ -1558,7 +1347,8 @@ ${input.systemPrompt}` : void 0;
|
|
|
1558
1347
|
requiredExtensions: toolPlan.requiredExtensions,
|
|
1559
1348
|
ambientExtensions,
|
|
1560
1349
|
hooks: capturedHooks.hooks,
|
|
1561
|
-
|
|
1350
|
+
// A foreground child gets only its own bindings, so it never sees those of the session that launched it.
|
|
1351
|
+
...input.host === "runner" ? { processEnv: childProcessEnv(input, toolPlan) } : { processEnv: { [PI_SUBAGENT_EXTENSION_BINDINGS_ENV]: encodeExtensionBindings(input.extensionBindings) }, restoreProcessEnv: true },
|
|
1562
1352
|
runtime: config,
|
|
1563
1353
|
noSkills: !input.inheritSkills,
|
|
1564
1354
|
noContextFiles: !input.inheritProjectContext,
|
|
@@ -1642,14 +1432,14 @@ function usageBudgetExceededMessage(state) {
|
|
|
1642
1432
|
}
|
|
1643
1433
|
|
|
1644
1434
|
// src/runs/background/runner-startup-failure.ts
|
|
1645
|
-
import * as
|
|
1646
|
-
import * as
|
|
1435
|
+
import * as fs4 from "node:fs";
|
|
1436
|
+
import * as path3 from "node:path";
|
|
1647
1437
|
function persistRunnerStartupFailure(input) {
|
|
1648
1438
|
const now = Date.now();
|
|
1649
|
-
const statusPath =
|
|
1439
|
+
const statusPath = path3.join(input.asyncDir, "status.json");
|
|
1650
1440
|
let status = {};
|
|
1651
1441
|
try {
|
|
1652
|
-
status = JSON.parse(
|
|
1442
|
+
status = JSON.parse(fs4.readFileSync(statusPath, "utf-8"));
|
|
1653
1443
|
} catch (error) {
|
|
1654
1444
|
if (error.code !== "ENOENT") throw error;
|
|
1655
1445
|
}
|
|
@@ -1671,7 +1461,7 @@ function persistRunnerStartupFailure(input) {
|
|
|
1671
1461
|
});
|
|
1672
1462
|
const existingCandidate = readProcessTerminalCandidate(input.asyncDir);
|
|
1673
1463
|
const stepCount = Math.max(1, status.steps?.length ?? 0);
|
|
1674
|
-
writePrivateAtomicJson(
|
|
1464
|
+
writePrivateAtomicJson(path3.join(input.asyncDir, "process-terminal-candidate.json"), {
|
|
1675
1465
|
...existingCandidate,
|
|
1676
1466
|
version: 1,
|
|
1677
1467
|
runId: input.runId,
|
|
@@ -1685,19 +1475,19 @@ function persistRunnerStartupFailure(input) {
|
|
|
1685
1475
|
|
|
1686
1476
|
// src/runs/background/chain-append.ts
|
|
1687
1477
|
import { randomUUID } from "node:crypto";
|
|
1688
|
-
import * as
|
|
1689
|
-
import * as
|
|
1478
|
+
import * as fs5 from "node:fs";
|
|
1479
|
+
import * as path4 from "node:path";
|
|
1690
1480
|
var APPEND_REQUESTS_DIR = "append-requests";
|
|
1691
1481
|
function appendDir(asyncDir) {
|
|
1692
|
-
return
|
|
1482
|
+
return path4.join(asyncDir, APPEND_REQUESTS_DIR);
|
|
1693
1483
|
}
|
|
1694
1484
|
function appendRequestPath(asyncDir, request) {
|
|
1695
|
-
return
|
|
1485
|
+
return path4.join(appendDir(asyncDir), `${request.createdAt}-${request.id}.json`);
|
|
1696
1486
|
}
|
|
1697
1487
|
function listAppendRequestFiles(asyncDir) {
|
|
1698
1488
|
const dir = appendDir(asyncDir);
|
|
1699
1489
|
try {
|
|
1700
|
-
return
|
|
1490
|
+
return fs5.readdirSync(dir).filter((entry) => entry.endsWith(".json")).map((entry) => path4.join(dir, entry)).sort();
|
|
1701
1491
|
} catch (error) {
|
|
1702
1492
|
if (error.code === "ENOENT") return [];
|
|
1703
1493
|
throw error;
|
|
@@ -1733,7 +1523,7 @@ function enqueueChainAppendRequest(input) {
|
|
|
1733
1523
|
createdAt: input.now ?? Date.now(),
|
|
1734
1524
|
steps: input.steps
|
|
1735
1525
|
};
|
|
1736
|
-
|
|
1526
|
+
fs5.mkdirSync(appendDir(input.asyncDir), { recursive: true });
|
|
1737
1527
|
const persist = () => writeAtomicJson(appendRequestPath(input.asyncDir, request), request);
|
|
1738
1528
|
if (input.admit) input.admit(persist);
|
|
1739
1529
|
else persist();
|
|
@@ -1744,7 +1534,7 @@ function enqueueChainAppendRequest(input) {
|
|
|
1744
1534
|
} catch (error) {
|
|
1745
1535
|
bookkeepingErrors.push(`pending append count failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1746
1536
|
}
|
|
1747
|
-
const statusPath =
|
|
1537
|
+
const statusPath = path4.join(input.asyncDir, "status.json");
|
|
1748
1538
|
const updatedStatus = { ...status, pendingAppends: pendingCount, lastUpdate: request.createdAt };
|
|
1749
1539
|
try {
|
|
1750
1540
|
writeAtomicJson(statusPath, updatedStatus);
|
|
@@ -1752,7 +1542,7 @@ function enqueueChainAppendRequest(input) {
|
|
|
1752
1542
|
bookkeepingErrors.push(`status update failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1753
1543
|
}
|
|
1754
1544
|
try {
|
|
1755
|
-
appendJsonl(
|
|
1545
|
+
appendJsonl(path4.join(input.asyncDir, "events.jsonl"), JSON.stringify({
|
|
1756
1546
|
type: "subagent.chain.append.requested",
|
|
1757
1547
|
ts: request.createdAt,
|
|
1758
1548
|
runId: input.runId,
|
|
@@ -1766,7 +1556,7 @@ function enqueueChainAppendRequest(input) {
|
|
|
1766
1556
|
return { request, pendingCount, ...bookkeepingErrors.length > 0 ? { bookkeepingError: bookkeepingErrors.join("; ") } : {} };
|
|
1767
1557
|
}
|
|
1768
1558
|
function readAppendRequest(filePath) {
|
|
1769
|
-
const raw = JSON.parse(
|
|
1559
|
+
const raw = JSON.parse(fs5.readFileSync(filePath, "utf-8"));
|
|
1770
1560
|
if (!raw.id || typeof raw.id !== "string") return void 0;
|
|
1771
1561
|
const createdAt = raw.createdAt;
|
|
1772
1562
|
if (typeof createdAt !== "number" || !Number.isFinite(createdAt)) return void 0;
|
|
@@ -1813,28 +1603,28 @@ var piPackageSelection = (() => {
|
|
|
1813
1603
|
})();
|
|
1814
1604
|
var piPackageRoot = piPackageSelection.root;
|
|
1815
1605
|
function resolveJitiCliFromPackageJson(packageJsonPath) {
|
|
1816
|
-
if (!
|
|
1817
|
-
const packageRoot =
|
|
1818
|
-
const pkg = JSON.parse(
|
|
1606
|
+
if (!fs6.existsSync(packageJsonPath)) return void 0;
|
|
1607
|
+
const packageRoot = path5.dirname(packageJsonPath);
|
|
1608
|
+
const pkg = JSON.parse(fs6.readFileSync(packageJsonPath, "utf-8"));
|
|
1819
1609
|
const binField = pkg.bin;
|
|
1820
1610
|
const binPath = typeof binField === "string" ? binField : binField?.jiti ?? Object.values(binField ?? {})[0];
|
|
1821
1611
|
const candidates = [binPath, "lib/jiti-cli.mjs"].filter((candidate) => Boolean(candidate));
|
|
1822
1612
|
for (const candidate of candidates) {
|
|
1823
|
-
const cliPath =
|
|
1824
|
-
if (
|
|
1613
|
+
const cliPath = path5.resolve(packageRoot, candidate);
|
|
1614
|
+
if (fs6.existsSync(cliPath)) return cliPath;
|
|
1825
1615
|
}
|
|
1826
1616
|
return void 0;
|
|
1827
1617
|
}
|
|
1828
1618
|
function resolveJitiCliPath() {
|
|
1829
1619
|
const candidates = [
|
|
1830
1620
|
() => require2.resolve("jiti/package.json"),
|
|
1831
|
-
() => piPackageRoot ? nodeModule.createRequire(
|
|
1621
|
+
() => piPackageRoot ? nodeModule.createRequire(path5.join(piPackageRoot, "package.json")).resolve("jiti/package.json") : void 0,
|
|
1832
1622
|
() => {
|
|
1833
1623
|
if (!process.argv[1]) return void 0;
|
|
1834
|
-
const piEntry =
|
|
1624
|
+
const piEntry = fs6.realpathSync(process.argv[1]);
|
|
1835
1625
|
return nodeModule.createRequire(piEntry).resolve("jiti/package.json");
|
|
1836
1626
|
},
|
|
1837
|
-
() => piPackageRoot ?
|
|
1627
|
+
() => piPackageRoot ? path5.join(piPackageRoot, "node_modules", "jiti", "package.json") : void 0
|
|
1838
1628
|
];
|
|
1839
1629
|
for (const candidate of candidates) {
|
|
1840
1630
|
try {
|
|
@@ -1848,12 +1638,12 @@ function resolveJitiCliPath() {
|
|
|
1848
1638
|
return void 0;
|
|
1849
1639
|
}
|
|
1850
1640
|
var jitiCliPath = resolveJitiCliPath();
|
|
1851
|
-
var asyncRunnerModuleDir =
|
|
1641
|
+
var asyncRunnerModuleDir = path5.dirname(fileURLToPath(new URL("../src/runs/background/async-execution.js", import.meta.url).href));
|
|
1852
1642
|
function asyncRunnerSourcePath() {
|
|
1853
1643
|
return resolveRuntimeModulePath(asyncRunnerModuleDir, "subagent-runner-bootstrap");
|
|
1854
1644
|
}
|
|
1855
1645
|
function sourceUnderNodeModules() {
|
|
1856
|
-
return asyncRunnerSourcePath().split(
|
|
1646
|
+
return asyncRunnerSourcePath().split(path5.sep).some((segment) => segment.toLowerCase() === "node_modules");
|
|
1857
1647
|
}
|
|
1858
1648
|
function supportsNativeRunner(nodeExecutable) {
|
|
1859
1649
|
return Boolean(process.features.typescript) && typeof nodeModule.registerHooks === "function" && nodeExecutable === process.execPath && !sourceUnderNodeModules();
|
|
@@ -1875,25 +1665,25 @@ function resolveAsyncRunnerLogPaths(cfg) {
|
|
|
1875
1665
|
const asyncDir = typeof cfg.asyncDir === "string" ? cfg.asyncDir : void 0;
|
|
1876
1666
|
if (!asyncDir) return void 0;
|
|
1877
1667
|
return {
|
|
1878
|
-
stdoutPath:
|
|
1879
|
-
stderrPath:
|
|
1668
|
+
stdoutPath: path5.join(asyncDir, "runner.stdout.log"),
|
|
1669
|
+
stderrPath: path5.join(asyncDir, "runner.stderr.log")
|
|
1880
1670
|
};
|
|
1881
1671
|
}
|
|
1882
1672
|
function closeFd(fd) {
|
|
1883
1673
|
if (fd === void 0) return;
|
|
1884
1674
|
try {
|
|
1885
|
-
|
|
1675
|
+
fs6.closeSync(fd);
|
|
1886
1676
|
} catch {
|
|
1887
1677
|
}
|
|
1888
1678
|
}
|
|
1889
1679
|
var RUNNER_STARTUP_TIMEOUT_MS = 1e4;
|
|
1890
1680
|
function waitForStartupInterval(delayMs = 20) {
|
|
1891
|
-
return new Promise((
|
|
1681
|
+
return new Promise((resolve9) => setTimeout(resolve9, delayMs));
|
|
1892
1682
|
}
|
|
1893
1683
|
function readRunnerStartup(startupPath, expectedState, expectedToken) {
|
|
1894
|
-
if (!
|
|
1684
|
+
if (!fs6.existsSync(startupPath)) return void 0;
|
|
1895
1685
|
try {
|
|
1896
|
-
const payload = JSON.parse(
|
|
1686
|
+
const payload = JSON.parse(fs6.readFileSync(startupPath, "utf-8"));
|
|
1897
1687
|
if (payload.state === "error" && typeof payload.error === "string") return { ok: false, error: payload.error, startupDidNotProceed: true };
|
|
1898
1688
|
if (payload.state !== expectedState) return void 0;
|
|
1899
1689
|
if (typeof payload.token !== "string" || expectedToken !== void 0 && payload.token !== expectedToken) {
|
|
@@ -1907,7 +1697,7 @@ function readRunnerStartup(startupPath, expectedState, expectedToken) {
|
|
|
1907
1697
|
async function waitForRunnerStartup(startupPath, expectedState, timeoutMs, expectedToken, processClosed) {
|
|
1908
1698
|
const confirmOpen = async (result2) => {
|
|
1909
1699
|
if (!processClosed || result2.ok === false) return result2;
|
|
1910
|
-
const outcome = await Promise.race([processClosed.then((exit) => ({ exit })), new Promise((
|
|
1700
|
+
const outcome = await Promise.race([processClosed.then((exit) => ({ exit })), new Promise((resolve9) => setImmediate(() => resolve9(void 0)))]);
|
|
1911
1701
|
if (!outcome) return result2;
|
|
1912
1702
|
const finalResult2 = readRunnerStartup(startupPath, expectedState, expectedToken);
|
|
1913
1703
|
if (finalResult2?.ok === false) return finalResult2;
|
|
@@ -2022,7 +1812,7 @@ async function completeRunnerStartupHandshake(startupPath, startupAckPath, start
|
|
|
2022
1812
|
}
|
|
2023
1813
|
const closedAtCommit = await Promise.race([
|
|
2024
1814
|
processClosed.then((exit) => exit),
|
|
2025
|
-
new Promise((
|
|
1815
|
+
new Promise((resolve9) => setImmediate(() => resolve9(void 0)))
|
|
2026
1816
|
]);
|
|
2027
1817
|
const observedExit = closedAtCommit ?? getObservedProcessExit();
|
|
2028
1818
|
if (observedExit) {
|
|
@@ -2040,12 +1830,12 @@ async function completeRunnerStartupHandshake(startupPath, startupAckPath, start
|
|
|
2040
1830
|
return { pid: proc.pid, runnerProcessInstanceId, error: message, terminationObserved, startupDidNotProceed: true };
|
|
2041
1831
|
}
|
|
2042
1832
|
try {
|
|
2043
|
-
|
|
1833
|
+
fs6.rmSync(startupPath, { force: true });
|
|
2044
1834
|
} catch {
|
|
2045
1835
|
}
|
|
2046
1836
|
return { pid: proc.pid, runnerProcessInstanceId };
|
|
2047
1837
|
} finally {
|
|
2048
|
-
await new Promise((
|
|
1838
|
+
await new Promise((resolve9) => setImmediate(resolve9));
|
|
2049
1839
|
}
|
|
2050
1840
|
}
|
|
2051
1841
|
function persistPreProceedStartupFailure(asyncDir, runId, runnerProcessInstanceId, sessionId, completionOwnerId, message) {
|
|
@@ -2080,9 +1870,9 @@ function spawnRunner(cfg, suffix, cwd, initialStatus, initialStatusPath, launchP
|
|
|
2080
1870
|
const nodeExecutable = resolveNodeExecutable();
|
|
2081
1871
|
const nativeRunnerSupported = supportsNativeRunner(nodeExecutable);
|
|
2082
1872
|
const runner = asyncRunnerSourcePath();
|
|
2083
|
-
const runnerIsJavaScript =
|
|
2084
|
-
const bootstrap = resolveRuntimeModulePath(
|
|
2085
|
-
if (binaryHost && !
|
|
1873
|
+
const runnerIsJavaScript = path5.extname(runner) === ".js";
|
|
1874
|
+
const bootstrap = resolveRuntimeModulePath(path5.dirname(runner), "binary-bootstrap");
|
|
1875
|
+
if (binaryHost && !fs6.existsSync(bootstrap)) return { error: `Background runner bootstrap not found: ${bootstrap}` };
|
|
2086
1876
|
if (!binaryHost && !runnerIsJavaScript && !nativeRunnerSupported && !jitiCliPath) {
|
|
2087
1877
|
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
1878
|
}
|
|
@@ -2093,7 +1883,7 @@ function spawnRunner(cfg, suffix, cwd, initialStatus, initialStatusPath, launchP
|
|
|
2093
1883
|
if (hostPeerAliases.missing.length > 0) {
|
|
2094
1884
|
return { error: `Background children require the host npm package (${PI_CODING_AGENT_PACKAGE}) with its dependencies; ${piPackageRoot} does not provide ${hostPeerAliases.missing.join(", ")}.` };
|
|
2095
1885
|
}
|
|
2096
|
-
|
|
1886
|
+
fs6.mkdirSync(TEMP_ROOT_DIR, { recursive: true });
|
|
2097
1887
|
const cfgPath = getAsyncConfigPath(suffix);
|
|
2098
1888
|
const runnerProcessInstanceId = randomUUID2();
|
|
2099
1889
|
const hasRevivalLease = typeof cfg.revivalLease === "object";
|
|
@@ -2108,23 +1898,23 @@ function spawnRunner(cfg, suffix, cwd, initialStatus, initialStatusPath, launchP
|
|
|
2108
1898
|
const launchRunId = typeof launchForStartup.id === "string" ? launchForStartup.id : suffix;
|
|
2109
1899
|
const launchSessionId = typeof launchForStartup.sessionId === "string" ? launchForStartup.sessionId : void 0;
|
|
2110
1900
|
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)
|
|
1901
|
+
const startupPath = typeof launchForStartup.revivalLease === "object" && launchAsyncDir ? path5.join(launchAsyncDir, "runner-startup.json") : void 0;
|
|
1902
|
+
const startupAckPath = startupPath ? path5.join(path5.dirname(startupPath), "runner-startup-ack.json") : void 0;
|
|
1903
|
+
const startupConfirmPath = startupPath ? path5.join(path5.dirname(startupPath), "runner-startup-confirm.json") : void 0;
|
|
1904
|
+
const startupProceedPath = launchAsyncDir && (startupPath || launchBarrierToken) ? path5.join(launchAsyncDir, "runner-startup-proceed.json") : void 0;
|
|
1905
|
+
if (startupPath) fs6.rmSync(startupPath, { force: true });
|
|
1906
|
+
if (startupAckPath) fs6.rmSync(startupAckPath, { force: true });
|
|
1907
|
+
if (startupConfirmPath) fs6.rmSync(startupConfirmPath, { force: true });
|
|
1908
|
+
if (startupProceedPath) fs6.rmSync(startupProceedPath, { force: true });
|
|
1909
|
+
const identityPath = launcher && launchBarrierToken && launchAsyncDir ? path5.join(launchAsyncDir, "runner-startup.json") : void 0;
|
|
1910
|
+
if (identityPath) fs6.rmSync(identityPath, { force: true });
|
|
2121
1911
|
let runnerIdentity;
|
|
2122
1912
|
const terminateBeforeProceed = (pid) => !launcher ? terminateRunnerBeforeProceed(pid) : runnerIdentity !== void 0 && runnerIdentity.pidNamespaceScope === currentPidNamespaceScope() && terminateRunnerBeforeProceed(runnerIdentity.pid);
|
|
2123
1913
|
const adoptRunnerIdentity = (pid, pidNamespaceScope) => {
|
|
2124
1914
|
if (typeof pid !== "number" || !Number.isSafeInteger(pid) || pid < 1) return `Async runner reported an invalid process identity in '${launchAsyncDir}'.`;
|
|
2125
1915
|
runnerIdentity = { pid, ...typeof pidNamespaceScope === "string" ? { pidNamespaceScope } : {} };
|
|
2126
1916
|
try {
|
|
2127
|
-
const status = JSON.parse(
|
|
1917
|
+
const status = JSON.parse(fs6.readFileSync(initialStatusPath, "utf-8"));
|
|
2128
1918
|
writePrivateAtomicJson(initialStatusPath, { ...status, pid, ...runnerIdentity.pidNamespaceScope !== void 0 ? { pidNamespaceScope: runnerIdentity.pidNamespaceScope } : {} });
|
|
2129
1919
|
return void 0;
|
|
2130
1920
|
} catch (error) {
|
|
@@ -2136,9 +1926,9 @@ function spawnRunner(cfg, suffix, cwd, initialStatus, initialStatusPath, launchP
|
|
|
2136
1926
|
let stderrFd;
|
|
2137
1927
|
try {
|
|
2138
1928
|
if (logPaths) {
|
|
2139
|
-
|
|
2140
|
-
stdoutFd =
|
|
2141
|
-
stderrFd =
|
|
1929
|
+
fs6.mkdirSync(path5.dirname(logPaths.stdoutPath), { recursive: true });
|
|
1930
|
+
stdoutFd = fs6.openSync(logPaths.stdoutPath, "a");
|
|
1931
|
+
stderrFd = fs6.openSync(logPaths.stderrPath, "a");
|
|
2142
1932
|
}
|
|
2143
1933
|
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
1934
|
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 +1955,10 @@ function spawnRunner(cfg, suffix, cwd, initialStatus, initialStatusPath, launchP
|
|
|
2165
1955
|
proc.once("exit", (exitCode, signal) => {
|
|
2166
1956
|
observedProcessExit = { exitCode, signal };
|
|
2167
1957
|
});
|
|
2168
|
-
const processClosed = new Promise((
|
|
1958
|
+
const processClosed = new Promise((resolve9) => {
|
|
2169
1959
|
proc.once("close", (exitCode, signal) => {
|
|
2170
1960
|
observedProcessExit ??= { exitCode, signal };
|
|
2171
|
-
|
|
1961
|
+
resolve9({ exitCode, signal });
|
|
2172
1962
|
});
|
|
2173
1963
|
});
|
|
2174
1964
|
closeFd(stdoutFd);
|
|
@@ -2184,7 +1974,7 @@ function spawnRunner(cfg, suffix, cwd, initialStatus, initialStatusPath, launchP
|
|
|
2184
1974
|
if (typeof asyncDir !== "string" || typeof runId !== "string") return;
|
|
2185
1975
|
if (launcher) {
|
|
2186
1976
|
try {
|
|
2187
|
-
writePrivateAtomicJson(
|
|
1977
|
+
writePrivateAtomicJson(path5.join(asyncDir, "launcher-close.json"), { version: 1, runId, runnerProcessInstanceId, closeObservedAt: Date.now(), exitCode, signal, ...runnerIdentity ? { runnerPid: runnerIdentity.pid } : {} });
|
|
2188
1978
|
} catch {
|
|
2189
1979
|
}
|
|
2190
1980
|
if (!runnerIdentity || runnerIdentity.pidNamespaceScope !== currentPidNamespaceScope() || checkRecordedPidLiveness(runnerIdentity) !== "dead") return;
|
|
@@ -2201,7 +1991,7 @@ function spawnRunner(cfg, suffix, cwd, initialStatus, initialStatusPath, launchP
|
|
|
2201
1991
|
try {
|
|
2202
1992
|
let status;
|
|
2203
1993
|
try {
|
|
2204
|
-
status = JSON.parse(
|
|
1994
|
+
status = JSON.parse(fs6.readFileSync(path5.join(asyncDir, "status.json"), "utf-8"));
|
|
2205
1995
|
status.processTerminal = persisted;
|
|
2206
1996
|
} catch {
|
|
2207
1997
|
status = {
|
|
@@ -2246,7 +2036,7 @@ function spawnRunner(cfg, suffix, cwd, initialStatus, initialStatusPath, launchP
|
|
|
2246
2036
|
...launcher ? {} : { pid: proc.pid, pidNamespaceScope: currentPidNamespaceScope(), ...bootId ? { bootId } : {} },
|
|
2247
2037
|
processTerminal: { version: 1, state: "pending", runId: initialStatus.runId, runnerProcessInstanceId }
|
|
2248
2038
|
});
|
|
2249
|
-
updateActiveRunIndex(
|
|
2039
|
+
updateActiveRunIndex(path5.dirname(initialStatusPath), initialStatus.state, initialStatus.toolCallId);
|
|
2250
2040
|
} catch (error) {
|
|
2251
2041
|
const message = `Failed to persist initial async status: ${error instanceof Error ? error.message : String(error)}`;
|
|
2252
2042
|
if (launchAsyncDir) persistPreProceedStartupFailure(launchAsyncDir, launchRunId, runnerProcessInstanceId, launchSessionId, launchCompletionOwnerId, message);
|
|
@@ -2387,7 +2177,7 @@ function buildAsyncRunnerSteps(id, params) {
|
|
|
2387
2177
|
if (error instanceof ChainOutputValidationError) return { error: error.message };
|
|
2388
2178
|
throw error;
|
|
2389
2179
|
}
|
|
2390
|
-
const diagnosticContext = params.unknownAgentDiagnosticContext ?? unknownAgentDiagnosticContext(discoverAgents(
|
|
2180
|
+
const diagnosticContext = params.unknownAgentDiagnosticContext ?? unknownAgentDiagnosticContext(discoverAgents(path5.resolve(runnerCwd), "both"));
|
|
2391
2181
|
for (const s of chain) {
|
|
2392
2182
|
const stepAgents = isParallelStep(s) ? s.parallel.map((t) => t.agent) : isDynamicParallelStep(s) ? [s.parallel.agent] : [s.agent];
|
|
2393
2183
|
for (const agentName of stepAgents) {
|
|
@@ -2484,7 +2274,7 @@ function buildAsyncRunnerSteps(id, params) {
|
|
|
2484
2274
|
stepCwd,
|
|
2485
2275
|
ctx.cwd,
|
|
2486
2276
|
a.skillPath,
|
|
2487
|
-
a.filePath ?
|
|
2277
|
+
a.filePath ? path5.dirname(a.filePath) : stepCwd
|
|
2488
2278
|
);
|
|
2489
2279
|
if (missingSkills.includes("pi-subagents")) throw new UnavailableSubagentSkillError(UNAVAILABLE_SUBAGENT_SKILL_ERROR);
|
|
2490
2280
|
const systemPrompt = buildEffectiveSystemPrompt({ agent: a, resolvedSkills, cwd: stepCwd });
|
|
@@ -2657,7 +2447,7 @@ function buildAsyncRunnerSteps(id, params) {
|
|
|
2657
2447
|
acceptanceRole: a.acceptanceRole,
|
|
2658
2448
|
...s.gateOn ? { gateOn: s.gateOn } : {},
|
|
2659
2449
|
...behavior.outputSchema ? { structuredOutputSchema: behavior.outputSchema } : {},
|
|
2660
|
-
...behavior.outputSchema ? { structuredOutput: createStructuredOutputRuntime(behavior.outputSchema,
|
|
2450
|
+
...behavior.outputSchema ? { structuredOutput: createStructuredOutputRuntime(behavior.outputSchema, path5.join(asyncDir, "structured-output"), { acceptanceReport: resolveAcceptanceReportMode(s.acceptance) }) } : {},
|
|
2661
2451
|
...resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {},
|
|
2662
2452
|
...s.worktree ? { worktree: true } : {}
|
|
2663
2453
|
};
|
|
@@ -2834,11 +2624,11 @@ function executeAsyncChain(id, params) {
|
|
|
2834
2624
|
const capabilityCeiling = params.capabilityCeiling ?? resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId);
|
|
2835
2625
|
const inheritedNestedRoute2 = inheritedNestedRouteOf(ctx.childRuntime);
|
|
2836
2626
|
const nestedAddress = inheritedNestedRoute2 ? inheritedNestedParentAddressOf(ctx.childRuntime) : void 0;
|
|
2837
|
-
const asyncDir = inheritedNestedRoute2 ?
|
|
2627
|
+
const asyncDir = inheritedNestedRoute2 ? path5.join(TEMP_ROOT_DIR, "nested-subagent-runs", inheritedNestedRoute2.rootRunId, id) : path5.join(DIRS.async, id);
|
|
2838
2628
|
let runFanoutBudget;
|
|
2839
2629
|
try {
|
|
2840
2630
|
runFanoutBudget = params.runFanoutBudget ?? createRunFanoutBudget(id, 64);
|
|
2841
|
-
|
|
2631
|
+
fs6.mkdirSync(asyncDir, { recursive: true });
|
|
2842
2632
|
writeRunFanoutBudgetDescriptor(asyncDir, runFanoutBudget);
|
|
2843
2633
|
} catch (error) {
|
|
2844
2634
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -2864,9 +2654,9 @@ function executeAsyncChain(id, params) {
|
|
|
2864
2654
|
sessionFilesByFlatIndex,
|
|
2865
2655
|
thinkingOverridesByFlatIndex,
|
|
2866
2656
|
contextForAgent: params.contextForAgent,
|
|
2867
|
-
progressDir: params.progressDir ?? (artifactsDir ?
|
|
2657
|
+
progressDir: params.progressDir ?? (artifactsDir ? path5.join(artifactsDir, "progress", id) : resultMode === "parallel" ? path5.join(asyncDir, "progress") : void 0),
|
|
2868
2658
|
agentContract: params.agentContract,
|
|
2869
|
-
outputBaseDir: artifactsDir ?
|
|
2659
|
+
outputBaseDir: artifactsDir ? path5.join(artifactsDir, "outputs", id) : void 0,
|
|
2870
2660
|
dynamicFanoutMaxItems: params.dynamicFanoutMaxItems,
|
|
2871
2661
|
maxSubagentDepth,
|
|
2872
2662
|
waitToolEnabled: params.waitToolEnabled,
|
|
@@ -2887,7 +2677,7 @@ function executeAsyncChain(id, params) {
|
|
|
2887
2677
|
});
|
|
2888
2678
|
if ("error" in built) {
|
|
2889
2679
|
try {
|
|
2890
|
-
|
|
2680
|
+
fs6.rmSync(asyncDir, { recursive: true, force: true });
|
|
2891
2681
|
} catch {
|
|
2892
2682
|
}
|
|
2893
2683
|
return formatAsyncStartError(resultMode, built.error);
|
|
@@ -2927,7 +2717,7 @@ function executeAsyncChain(id, params) {
|
|
|
2927
2717
|
try {
|
|
2928
2718
|
writeRetainedRequiredChildExtensions(asyncDir, params.requiredExtensions ?? ctx.childRuntime?.requiredExtensions ?? resolveRequiredChildExtensions(launchParentSessionId ?? void 0));
|
|
2929
2719
|
} catch (error) {
|
|
2930
|
-
|
|
2720
|
+
fs6.rmSync(asyncDir, { recursive: true, force: true });
|
|
2931
2721
|
return formatAsyncStartError(resultMode, `Failed to record required child extensions: ${error instanceof Error ? error.message : String(error)}`);
|
|
2932
2722
|
}
|
|
2933
2723
|
let spawnResultOrPromise = {};
|
|
@@ -2943,7 +2733,7 @@ function executeAsyncChain(id, params) {
|
|
|
2943
2733
|
artifactsDir: artifactConfig.enabled ? artifactsDir : void 0,
|
|
2944
2734
|
artifactConfig,
|
|
2945
2735
|
share: shareEnabled,
|
|
2946
|
-
sessionDir: sessionRoot ?
|
|
2736
|
+
sessionDir: sessionRoot ? path5.join(sessionRoot, `async-${id}`) : void 0,
|
|
2947
2737
|
asyncDir,
|
|
2948
2738
|
sessionId: ctx.currentSessionId,
|
|
2949
2739
|
completionOwnerId: ctx.completionOwnerId ?? currentCompletionOwnerId(),
|
|
@@ -3000,7 +2790,7 @@ function executeAsyncChain(id, params) {
|
|
|
3000
2790
|
...launcher ? { launcher } : {},
|
|
3001
2791
|
steps: initialStatusSteps
|
|
3002
2792
|
},
|
|
3003
|
-
|
|
2793
|
+
path5.join(asyncDir, "status.json"),
|
|
3004
2794
|
launchParentSessionId,
|
|
3005
2795
|
(proof) => emitProcessTerminalEvent(ctx, proof),
|
|
3006
2796
|
(runnerProcessInstanceId) => params.activeAsyncCapacity?.markStarted(runnerProcessInstanceId)
|
|
@@ -3123,7 +2913,7 @@ function executeAsyncChain(id, params) {
|
|
|
3123
2913
|
return spawnResultOrPromise instanceof Promise ? spawnResultOrPromise.then(finishSpawnResult) : finishSpawnResult(spawnResultOrPromise);
|
|
3124
2914
|
}
|
|
3125
2915
|
function workflowAwaitedAsyncResultPath(asyncDir) {
|
|
3126
|
-
return
|
|
2916
|
+
return path5.join(asyncDir, "workflow-result.json");
|
|
3127
2917
|
}
|
|
3128
2918
|
function executeAsyncSingle(id, params) {
|
|
3129
2919
|
const {
|
|
@@ -3193,6 +2983,7 @@ function executeAsyncSingle(id, params) {
|
|
|
3193
2983
|
const requestedMachine = params.machine ?? agentConfig.machine;
|
|
3194
2984
|
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
2985
|
if (machineUnsupported) return formatAsyncStartError("single", machineUnsupported);
|
|
2986
|
+
if (requestedMachine && params.revivalLease) return formatAsyncStartError("single", PLACED_RESTORE_UNSUPPORTED);
|
|
3196
2987
|
const resolvedLauncher = resolveAgentRunnerLauncher(agentConfig, ctx.runnerLaunchers, requestedMachine);
|
|
3197
2988
|
if (resolvedLauncher.error) return formatAsyncStartError("single", resolvedLauncher.error);
|
|
3198
2989
|
const launcher = resolvedLauncher.launcher;
|
|
@@ -3231,16 +3022,16 @@ function executeAsyncSingle(id, params) {
|
|
|
3231
3022
|
runnerCwd,
|
|
3232
3023
|
ctx.cwd,
|
|
3233
3024
|
agentConfig.skillPath,
|
|
3234
|
-
agentConfig.filePath ?
|
|
3025
|
+
agentConfig.filePath ? path5.dirname(agentConfig.filePath) : runnerCwd
|
|
3235
3026
|
);
|
|
3236
3027
|
if (missingSkills.includes("pi-subagents")) return formatAsyncStartError("single", UNAVAILABLE_SUBAGENT_SKILL_ERROR);
|
|
3237
3028
|
const inheritedNestedRoute2 = inheritedNestedRouteOf(ctx.childRuntime);
|
|
3238
3029
|
const nestedAddress = inheritedNestedRoute2 ? inheritedNestedParentAddressOf(ctx.childRuntime) : void 0;
|
|
3239
|
-
const asyncDir = inheritedNestedRoute2 ?
|
|
3030
|
+
const asyncDir = inheritedNestedRoute2 ? path5.join(TEMP_ROOT_DIR, "nested-subagent-runs", inheritedNestedRoute2.rootRunId, id) : path5.join(DIRS.async, id);
|
|
3240
3031
|
let runFanoutBudget;
|
|
3241
3032
|
try {
|
|
3242
3033
|
runFanoutBudget = params.runFanoutBudget ?? createRunFanoutBudget(id, 64);
|
|
3243
|
-
|
|
3034
|
+
fs6.mkdirSync(asyncDir, { recursive: true });
|
|
3244
3035
|
writeRunFanoutBudgetDescriptor(asyncDir, runFanoutBudget);
|
|
3245
3036
|
} catch (error) {
|
|
3246
3037
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -3251,14 +3042,14 @@ function executeAsyncSingle(id, params) {
|
|
|
3251
3042
|
};
|
|
3252
3043
|
}
|
|
3253
3044
|
const effectiveOutput = normalizeSingleOutputOverride(params.output, agentConfig.output);
|
|
3254
|
-
const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, instructionCwd, params.outputBaseDir ?? (artifactsDir ?
|
|
3045
|
+
const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, instructionCwd, params.outputBaseDir ?? (artifactsDir ? path5.join(artifactsDir, "outputs", id) : void 0));
|
|
3255
3046
|
const systemPrompt = buildEffectiveSystemPrompt({ agent: agentConfig, resolvedSkills, cwd: runnerCwd });
|
|
3256
3047
|
const outputMode = params.outputMode ?? agentConfig.outputMode ?? "inline";
|
|
3257
3048
|
const validationError = validateFileOnlyOutputMode(outputMode, outputPath, `Async single run (${agent})`);
|
|
3258
3049
|
if (validationError) return formatAsyncStartError("single", validationError);
|
|
3259
3050
|
const taskWithOutputInstruction = injectSingleOutputInstruction(task, outputPath, agentConfig);
|
|
3260
3051
|
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("~/") ||
|
|
3052
|
+
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
3053
|
const readsInstruction = readPaths.length > 0 ? `[Read from: ${readPaths.join(", ")}]
|
|
3263
3054
|
|
|
3264
3055
|
` : "";
|
|
@@ -3330,8 +3121,8 @@ function executeAsyncSingle(id, params) {
|
|
|
3330
3121
|
if (resolvedToolTimeout.error) return formatAsyncStartError("single", resolvedToolTimeout.error);
|
|
3331
3122
|
const toolTimeoutMs = resolvedToolTimeout.toolTimeoutMs;
|
|
3332
3123
|
const initialUsageBudget = usageBudgetState(params.usageBudget, void 0);
|
|
3333
|
-
const resolvedSessionDir = params.sessionDir ?? (sessionRoot ?
|
|
3334
|
-
const structuredOutput = params.structuredOutputSchema ? createStructuredOutputRuntime(params.structuredOutputSchema,
|
|
3124
|
+
const resolvedSessionDir = params.sessionDir ?? (sessionRoot ? path5.join(sessionRoot, `async-${id}`) : void 0);
|
|
3125
|
+
const structuredOutput = params.structuredOutputSchema ? createStructuredOutputRuntime(params.structuredOutputSchema, path5.join(asyncDir, "structured-output"), { acceptanceReport: resolveAcceptanceReportMode(params.acceptance) }) : void 0;
|
|
3335
3126
|
let selectedModel = model;
|
|
3336
3127
|
let requestedModel;
|
|
3337
3128
|
if (!externalRunner) {
|
|
@@ -3454,7 +3245,7 @@ function executeAsyncSingle(id, params) {
|
|
|
3454
3245
|
};
|
|
3455
3246
|
if (!externalRunner) {
|
|
3456
3247
|
try {
|
|
3457
|
-
writePrivateAtomicJson(
|
|
3248
|
+
writePrivateAtomicJson(path5.join(asyncDir, "recovery-descriptor.json"), recoveryDescriptor);
|
|
3458
3249
|
} catch (error) {
|
|
3459
3250
|
return formatAsyncStartError("single", `Failed to persist async recovery descriptor for '${id}': ${error instanceof Error ? error.message : String(error)}`);
|
|
3460
3251
|
}
|
|
@@ -3601,7 +3392,7 @@ function executeAsyncSingle(id, params) {
|
|
|
3601
3392
|
...launcher ? { launcher } : {},
|
|
3602
3393
|
steps: [{ agent, status: "pending", ...lane ? { lane } : {}, ...model ? { model } : {}, ...contextLimit !== void 0 ? { contextLimit } : {} }]
|
|
3603
3394
|
},
|
|
3604
|
-
|
|
3395
|
+
path5.join(asyncDir, "status.json"),
|
|
3605
3396
|
launchParentSessionId,
|
|
3606
3397
|
(proof) => emitProcessTerminalEvent(ctx, proof),
|
|
3607
3398
|
(runnerProcessInstanceId) => params.activeAsyncCapacity?.markStarted(runnerProcessInstanceId),
|
|
@@ -3809,10 +3600,10 @@ function lineFromCheck(label, check) {
|
|
|
3809
3600
|
}
|
|
3810
3601
|
function formatExistingDirectory(label, dirPath) {
|
|
3811
3602
|
try {
|
|
3812
|
-
if (!
|
|
3813
|
-
const stats =
|
|
3603
|
+
if (!fs7.existsSync(dirPath)) return `- ${label}: missing (${dirPath})`;
|
|
3604
|
+
const stats = fs7.statSync(dirPath);
|
|
3814
3605
|
if (!stats.isDirectory()) throw new Error(`not a directory: ${dirPath}`);
|
|
3815
|
-
|
|
3606
|
+
fs7.accessSync(dirPath, fs7.constants.R_OK | fs7.constants.W_OK);
|
|
3816
3607
|
return `- ${label}: ok (${dirPath})`;
|
|
3817
3608
|
} catch (error) {
|
|
3818
3609
|
return `- ${label}: failed (${dirPath}) \u2014 ${errorText(error)}`;
|
|
@@ -3840,10 +3631,10 @@ function formatSkillSourceCounts(skills) {
|
|
|
3840
3631
|
}
|
|
3841
3632
|
function formatConfiguredSessionDir(input) {
|
|
3842
3633
|
if (input.requestedSessionDir) {
|
|
3843
|
-
return
|
|
3634
|
+
return path6.resolve(input.expandTilde?.(input.requestedSessionDir) ?? input.requestedSessionDir);
|
|
3844
3635
|
}
|
|
3845
3636
|
if (input.config.defaultSessionDir) {
|
|
3846
|
-
return
|
|
3637
|
+
return path6.resolve(input.expandTilde?.(input.config.defaultSessionDir) ?? input.config.defaultSessionDir);
|
|
3847
3638
|
}
|
|
3848
3639
|
return "not configured";
|
|
3849
3640
|
}
|
|
@@ -3852,7 +3643,7 @@ function formatSessionLines(input) {
|
|
|
3852
3643
|
const lines = [
|
|
3853
3644
|
lineFromCheck("configured session dir", () => `- configured session dir: ${formatConfiguredSessionDir(input)}`),
|
|
3854
3645
|
`- current session file: ${sessionFile ?? "not available"}`,
|
|
3855
|
-
`- current session dir: ${sessionFile ?
|
|
3646
|
+
`- current session dir: ${sessionFile ? path6.dirname(sessionFile) : "not available"}`,
|
|
3856
3647
|
`- current session id: ${input.currentSessionId ?? input.state.currentSessionId ?? "not available"}`
|
|
3857
3648
|
];
|
|
3858
3649
|
if (input.sessionError) lines.push(`- session manager: failed \u2014 ${input.sessionError}`);
|
|
@@ -3987,8 +3778,7 @@ var WORKFLOW_FENCE = /^```(?:js|javascript) workflow[ \t]*$/;
|
|
|
3987
3778
|
var PLAIN_JS_FENCE = /^```(?:js|javascript)[ \t]*$/;
|
|
3988
3779
|
var OPEN_FENCE = /^(`{3,}|~{3,})/;
|
|
3989
3780
|
var CLOSE_FENCE = /^(`{3,}|~{3,})[ \t]*$/;
|
|
3990
|
-
var SUMMARY_NOTE =
|
|
3991
|
-
var FILE_FALLBACK = `If the block is in your reply: ${SUMMARY_NOTE}`;
|
|
3781
|
+
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
3782
|
function readReplyWorkflowScript(sessionManager, toolCallId) {
|
|
3993
3783
|
const branch = sessionManager.getBranch();
|
|
3994
3784
|
for (let index = branch.length - 1; index >= 0; index--) {
|
|
@@ -4011,7 +3801,7 @@ function scriptFromReply(content) {
|
|
|
4011
3801
|
const blocks = tagged.length > 0 ? tagged : plain.length === 1 ? plain : [];
|
|
4012
3802
|
if (blocks.length !== 1) {
|
|
4013
3803
|
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} ${
|
|
3804
|
+
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
3805
|
}
|
|
4016
3806
|
const script = blocks[0];
|
|
4017
3807
|
if (!script.trim()) return { error: "The script's fenced block in this reply is empty." };
|
|
@@ -4038,14 +3828,14 @@ function workflowBlocks(text) {
|
|
|
4038
3828
|
}
|
|
4039
3829
|
|
|
4040
3830
|
// src/runs/foreground/execution.ts
|
|
4041
|
-
import { existsSync as
|
|
4042
|
-
import * as
|
|
3831
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync6, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
3832
|
+
import * as path10 from "node:path";
|
|
4043
3833
|
|
|
4044
3834
|
// src/shared/fork-session-cwd.ts
|
|
4045
|
-
import * as
|
|
4046
|
-
import * as
|
|
3835
|
+
import * as fs8 from "node:fs";
|
|
3836
|
+
import * as path7 from "node:path";
|
|
4047
3837
|
function alignForkedSessionCwd(sessionFile, cwd) {
|
|
4048
|
-
const lines =
|
|
3838
|
+
const lines = fs8.readFileSync(sessionFile, "utf-8").split("\n").filter((line) => line.trim().length > 0);
|
|
4049
3839
|
const entries = lines.map((line, index) => {
|
|
4050
3840
|
try {
|
|
4051
3841
|
return JSON.parse(line);
|
|
@@ -4056,16 +3846,16 @@ function alignForkedSessionCwd(sessionFile, cwd) {
|
|
|
4056
3846
|
});
|
|
4057
3847
|
const header = entries[0];
|
|
4058
3848
|
if (header?.type !== "session") throw new Error(`Forked session ${sessionFile} does not start with a session header.`);
|
|
4059
|
-
const effectiveCwd =
|
|
3849
|
+
const effectiveCwd = fs8.realpathSync.native(path7.resolve(cwd));
|
|
4060
3850
|
if (header.cwd === effectiveCwd) return;
|
|
4061
3851
|
header.cwd = effectiveCwd;
|
|
4062
|
-
|
|
3852
|
+
fs8.writeFileSync(sessionFile, `${entries.map((entry) => JSON.stringify(entry)).join("\n")}
|
|
4063
3853
|
`, "utf-8");
|
|
4064
3854
|
}
|
|
4065
3855
|
|
|
4066
3856
|
// src/shared/child-transcript.ts
|
|
4067
|
-
import * as
|
|
4068
|
-
import * as
|
|
3857
|
+
import * as fs9 from "node:fs";
|
|
3858
|
+
import * as path8 from "node:path";
|
|
4069
3859
|
var MAX_TOOL_PAYLOAD_BYTES = 32 * 1024;
|
|
4070
3860
|
var TOOL_PAYLOAD_TRUNCATION_MARKER = "\n\n\u2026 payload truncated";
|
|
4071
3861
|
function boundedPayload(value, maxBytes = MAX_TOOL_PAYLOAD_BYTES) {
|
|
@@ -4142,7 +3932,7 @@ function createChildTranscriptWriter(input) {
|
|
|
4142
3932
|
const markerBytes = Buffer.byteLength(marker, "utf-8");
|
|
4143
3933
|
if (bytesWritten + markerBytes > maxBytes) return false;
|
|
4144
3934
|
try {
|
|
4145
|
-
|
|
3935
|
+
fs9.appendFileSync(input.transcriptPath, marker, "utf-8");
|
|
4146
3936
|
bytesWritten += markerBytes;
|
|
4147
3937
|
return true;
|
|
4148
3938
|
} catch (error) {
|
|
@@ -4170,15 +3960,15 @@ function createChildTranscriptWriter(input) {
|
|
|
4170
3960
|
return;
|
|
4171
3961
|
}
|
|
4172
3962
|
try {
|
|
4173
|
-
|
|
3963
|
+
fs9.appendFileSync(input.transcriptPath, line, "utf-8");
|
|
4174
3964
|
bytesWritten += bytes;
|
|
4175
3965
|
} catch (error) {
|
|
4176
3966
|
writeError = `Failed to write child transcript '${input.transcriptPath}': ${errorMessage(error)}`;
|
|
4177
3967
|
}
|
|
4178
3968
|
};
|
|
4179
3969
|
try {
|
|
4180
|
-
|
|
4181
|
-
|
|
3970
|
+
fs9.mkdirSync(path8.dirname(input.transcriptPath), { recursive: true });
|
|
3971
|
+
fs9.writeFileSync(input.transcriptPath, "", "utf-8");
|
|
4182
3972
|
} catch (error) {
|
|
4183
3973
|
writeError = `Failed to initialize child transcript '${input.transcriptPath}': ${errorMessage(error)}`;
|
|
4184
3974
|
}
|
|
@@ -4300,7 +4090,7 @@ ${text}`;
|
|
|
4300
4090
|
}
|
|
4301
4091
|
|
|
4302
4092
|
// src/shared/jsonl-writer.ts
|
|
4303
|
-
import * as
|
|
4093
|
+
import * as fs10 from "node:fs";
|
|
4304
4094
|
var DEFAULT_MAX_JSONL_BYTES = 50 * 1024 * 1024;
|
|
4305
4095
|
function createJsonlWriter(filePath, source, deps = {}) {
|
|
4306
4096
|
if (!filePath) {
|
|
@@ -4311,7 +4101,7 @@ function createJsonlWriter(filePath, source, deps = {}) {
|
|
|
4311
4101
|
}
|
|
4312
4102
|
};
|
|
4313
4103
|
}
|
|
4314
|
-
const createWriteStream3 = deps.createWriteStream ?? ((targetPath) =>
|
|
4104
|
+
const createWriteStream3 = deps.createWriteStream ?? ((targetPath) => fs10.createWriteStream(targetPath, { flags: "a" }));
|
|
4315
4105
|
let stream;
|
|
4316
4106
|
try {
|
|
4317
4107
|
stream = createWriteStream3(filePath);
|
|
@@ -4361,15 +4151,15 @@ function createJsonlWriter(filePath, source, deps = {}) {
|
|
|
4361
4151
|
closed = true;
|
|
4362
4152
|
const current = stream;
|
|
4363
4153
|
stream = void 0;
|
|
4364
|
-
await new Promise((
|
|
4154
|
+
await new Promise((resolve9) => current.end(() => resolve9()));
|
|
4365
4155
|
}
|
|
4366
4156
|
};
|
|
4367
4157
|
}
|
|
4368
4158
|
|
|
4369
4159
|
// src/runs/shared/orca-progress-tabs.ts
|
|
4370
4160
|
import { spawn as spawn2 } from "node:child_process";
|
|
4371
|
-
import * as
|
|
4372
|
-
import * as
|
|
4161
|
+
import * as fs11 from "node:fs";
|
|
4162
|
+
import * as path9 from "node:path";
|
|
4373
4163
|
import { createHash, randomUUID as randomUUID3 } from "node:crypto";
|
|
4374
4164
|
var ORCA_CREATE_TIMEOUT_MS = 2e4;
|
|
4375
4165
|
var ORCA_KILL_GRACE_MS = 2e3;
|
|
@@ -4414,10 +4204,10 @@ var ORCA_CLEANUP_WATCHDOG_SCRIPT = [
|
|
|
4414
4204
|
].join("");
|
|
4415
4205
|
function executableFile(candidate) {
|
|
4416
4206
|
try {
|
|
4417
|
-
const stats =
|
|
4207
|
+
const stats = fs11.statSync(candidate);
|
|
4418
4208
|
if (!stats.isFile()) return false;
|
|
4419
4209
|
if (process.platform === "win32") return true;
|
|
4420
|
-
|
|
4210
|
+
fs11.accessSync(candidate, fs11.constants.X_OK);
|
|
4421
4211
|
return true;
|
|
4422
4212
|
} catch {
|
|
4423
4213
|
return false;
|
|
@@ -4431,9 +4221,9 @@ function executableNames(command, env) {
|
|
|
4431
4221
|
function resolveOrcaCommand(env = process.env) {
|
|
4432
4222
|
const override = env.PI_SUBAGENT_ORCA_BINARY?.trim();
|
|
4433
4223
|
if (override) return executableFile(override) ? override : void 0;
|
|
4434
|
-
for (const directory of (env.PATH ?? "").split(
|
|
4224
|
+
for (const directory of (env.PATH ?? "").split(path9.delimiter).filter(Boolean)) {
|
|
4435
4225
|
for (const name of executableNames("orca", env)) {
|
|
4436
|
-
const candidate =
|
|
4226
|
+
const candidate = path9.join(directory, name);
|
|
4437
4227
|
if (executableFile(candidate)) return candidate;
|
|
4438
4228
|
}
|
|
4439
4229
|
}
|
|
@@ -4470,15 +4260,15 @@ function viewerCommand(nodeExecutable, logPath, donePath) {
|
|
|
4470
4260
|
return invocation;
|
|
4471
4261
|
}
|
|
4472
4262
|
function progressRoot() {
|
|
4473
|
-
return
|
|
4263
|
+
return path9.join(TEMP_ROOT_DIR, "orca-progress");
|
|
4474
4264
|
}
|
|
4475
4265
|
function pruneStaleProgressFiles(root, now = Date.now()) {
|
|
4476
4266
|
try {
|
|
4477
|
-
for (const entry of
|
|
4267
|
+
for (const entry of fs11.readdirSync(root, { withFileTypes: true })) {
|
|
4478
4268
|
if (!entry.isFile() || !entry.name.endsWith(".log") && !entry.name.endsWith(".done") && !entry.name.endsWith(".ready") && !entry.name.endsWith(".pending")) continue;
|
|
4479
|
-
const file =
|
|
4269
|
+
const file = path9.join(root, entry.name);
|
|
4480
4270
|
try {
|
|
4481
|
-
if (now -
|
|
4271
|
+
if (now - fs11.statSync(file).mtimeMs > STALE_PROGRESS_MAX_AGE_MS) fs11.rmSync(file, { force: true });
|
|
4482
4272
|
} catch {
|
|
4483
4273
|
}
|
|
4484
4274
|
}
|
|
@@ -4491,9 +4281,9 @@ function safeSegment(value, fallback = "subagent") {
|
|
|
4491
4281
|
}
|
|
4492
4282
|
function observerManifestPath(cwd, stem) {
|
|
4493
4283
|
try {
|
|
4494
|
-
const dir =
|
|
4495
|
-
|
|
4496
|
-
return
|
|
4284
|
+
const dir = path9.join(getProjectSubagentsDir(resolveSequenceScope(cwd)), "views", "orca");
|
|
4285
|
+
fs11.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
4286
|
+
return path9.join(dir, `${stem}.json`);
|
|
4497
4287
|
} catch {
|
|
4498
4288
|
return void 0;
|
|
4499
4289
|
}
|
|
@@ -4501,7 +4291,7 @@ function observerManifestPath(cwd, stem) {
|
|
|
4501
4291
|
function writeObserverManifest(manifestPath, manifest) {
|
|
4502
4292
|
if (!manifestPath) return;
|
|
4503
4293
|
try {
|
|
4504
|
-
|
|
4294
|
+
fs11.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
4505
4295
|
`, { encoding: "utf-8", mode: 384 });
|
|
4506
4296
|
} catch {
|
|
4507
4297
|
}
|
|
@@ -4511,18 +4301,18 @@ function sleepSync(ms) {
|
|
|
4511
4301
|
Atomics.wait(signal, 0, 0, ms);
|
|
4512
4302
|
}
|
|
4513
4303
|
function resolveSequenceScope(cwd) {
|
|
4514
|
-
let current =
|
|
4304
|
+
let current = path9.resolve(cwd);
|
|
4515
4305
|
try {
|
|
4516
|
-
current =
|
|
4306
|
+
current = fs11.realpathSync(current);
|
|
4517
4307
|
} catch {
|
|
4518
4308
|
}
|
|
4519
4309
|
while (true) {
|
|
4520
4310
|
try {
|
|
4521
|
-
if (
|
|
4311
|
+
if (fs11.existsSync(path9.join(current, ".git"))) return current;
|
|
4522
4312
|
} catch {
|
|
4523
4313
|
}
|
|
4524
|
-
const parent =
|
|
4525
|
-
if (parent === current) return
|
|
4314
|
+
const parent = path9.dirname(current);
|
|
4315
|
+
if (parent === current) return path9.resolve(cwd);
|
|
4526
4316
|
current = parent;
|
|
4527
4317
|
}
|
|
4528
4318
|
}
|
|
@@ -4530,18 +4320,18 @@ function sequenceKey(cwd) {
|
|
|
4530
4320
|
return createHash("sha256").update(resolveSequenceScope(cwd)).digest("hex").slice(0, 20);
|
|
4531
4321
|
}
|
|
4532
4322
|
function withSequenceLock(root, key, fn) {
|
|
4533
|
-
const lockPath =
|
|
4323
|
+
const lockPath = path9.join(root, `counter-${key}.lock`);
|
|
4534
4324
|
let locked = false;
|
|
4535
4325
|
for (let attempt = 0; attempt < COUNTER_LOCK_RETRIES; attempt++) {
|
|
4536
4326
|
try {
|
|
4537
|
-
|
|
4327
|
+
fs11.mkdirSync(lockPath, { mode: 448 });
|
|
4538
4328
|
locked = true;
|
|
4539
4329
|
break;
|
|
4540
4330
|
} catch (error) {
|
|
4541
4331
|
if (error.code !== "EEXIST") break;
|
|
4542
4332
|
try {
|
|
4543
|
-
if (Date.now() -
|
|
4544
|
-
|
|
4333
|
+
if (Date.now() - fs11.statSync(lockPath).mtimeMs > COUNTER_LOCK_STALE_MS) {
|
|
4334
|
+
fs11.rmSync(lockPath, { recursive: true, force: true });
|
|
4545
4335
|
continue;
|
|
4546
4336
|
}
|
|
4547
4337
|
} catch {
|
|
@@ -4554,7 +4344,7 @@ function withSequenceLock(root, key, fn) {
|
|
|
4554
4344
|
return fn();
|
|
4555
4345
|
} finally {
|
|
4556
4346
|
try {
|
|
4557
|
-
|
|
4347
|
+
fs11.rmSync(lockPath, { recursive: true, force: true });
|
|
4558
4348
|
} catch {
|
|
4559
4349
|
}
|
|
4560
4350
|
}
|
|
@@ -4563,18 +4353,18 @@ function predecessorMarker(pathValue) {
|
|
|
4563
4353
|
if (!pathValue) return void 0;
|
|
4564
4354
|
const ready = pathValue.endsWith(".pending") ? pathValue.replace(/\.pending$/, ".ready") : pathValue;
|
|
4565
4355
|
const pending = ready.replace(/\.ready$/, ".pending");
|
|
4566
|
-
if (
|
|
4356
|
+
if (fs11.existsSync(ready) || fs11.existsSync(pending)) return pending;
|
|
4567
4357
|
return void 0;
|
|
4568
4358
|
}
|
|
4569
4359
|
function reserveTabSequence(root, cwd) {
|
|
4570
4360
|
const key = sequenceKey(cwd);
|
|
4571
|
-
const counterPath =
|
|
4572
|
-
const createDone =
|
|
4361
|
+
const counterPath = path9.join(root, `counter-${key}`);
|
|
4362
|
+
const createDone = path9.join(root, `create-${key}-${randomUUID3()}.pending`);
|
|
4573
4363
|
return withSequenceLock(root, key, () => {
|
|
4574
4364
|
let current = 0;
|
|
4575
4365
|
let previousCreateDone;
|
|
4576
4366
|
try {
|
|
4577
|
-
const raw =
|
|
4367
|
+
const raw = fs11.readFileSync(counterPath, "utf-8");
|
|
4578
4368
|
const [countLine, previousLine] = raw.split("\n");
|
|
4579
4369
|
const parsed = Number.parseInt(countLine ?? "", 10);
|
|
4580
4370
|
if (Number.isSafeInteger(parsed) && parsed >= 0) current = parsed;
|
|
@@ -4583,14 +4373,14 @@ function reserveTabSequence(root, cwd) {
|
|
|
4583
4373
|
}
|
|
4584
4374
|
const next = current + 1;
|
|
4585
4375
|
try {
|
|
4586
|
-
|
|
4587
|
-
|
|
4376
|
+
fs11.writeFileSync(createDone, "", { encoding: "utf-8", mode: 384 });
|
|
4377
|
+
fs11.writeFileSync(counterPath, `${next}
|
|
4588
4378
|
${createDone}
|
|
4589
4379
|
`, { encoding: "utf-8", mode: 384 });
|
|
4590
4380
|
return { sequence: next, previousCreateDone, createDone };
|
|
4591
4381
|
} catch {
|
|
4592
4382
|
try {
|
|
4593
|
-
|
|
4383
|
+
fs11.rmSync(createDone, { force: true });
|
|
4594
4384
|
} catch {
|
|
4595
4385
|
}
|
|
4596
4386
|
return void 0;
|
|
@@ -4612,8 +4402,8 @@ function scheduleCleanup(nodeExecutable, paths) {
|
|
|
4612
4402
|
}
|
|
4613
4403
|
function loadOrcaProgressTabsConfig() {
|
|
4614
4404
|
try {
|
|
4615
|
-
const configPath =
|
|
4616
|
-
const parsed = JSON.parse(
|
|
4405
|
+
const configPath = path9.join(getAgentDir(), "extensions", "subagent", "config.json");
|
|
4406
|
+
const parsed = JSON.parse(fs11.readFileSync(configPath, "utf-8"));
|
|
4617
4407
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
|
|
4618
4408
|
const value = parsed.orcaProgressTabs;
|
|
4619
4409
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
@@ -4632,7 +4422,7 @@ function createOrcaProgressTab(input) {
|
|
|
4632
4422
|
const nodeExecutable = resolveNodeExecutable();
|
|
4633
4423
|
const root = progressRoot();
|
|
4634
4424
|
try {
|
|
4635
|
-
|
|
4425
|
+
fs11.mkdirSync(root, { recursive: true, mode: 448 });
|
|
4636
4426
|
} catch {
|
|
4637
4427
|
return void 0;
|
|
4638
4428
|
}
|
|
@@ -4646,12 +4436,12 @@ function createOrcaProgressTab(input) {
|
|
|
4646
4436
|
const tabSequence = reservation.sequence;
|
|
4647
4437
|
const stem = `${runId}-${index}-${randomUUID3()}`;
|
|
4648
4438
|
const stepCount = typeof input.stepCount === "number" && Number.isInteger(input.stepCount) && input.stepCount > 0 ? input.stepCount : 1;
|
|
4649
|
-
const logPath =
|
|
4650
|
-
const donePath =
|
|
4439
|
+
const logPath = path9.join(root, `${stem}.log`);
|
|
4440
|
+
const donePath = path9.join(root, `${stem}.done`);
|
|
4651
4441
|
const title = `subagents \xB7 ${agent} \xB7 ${tabSequence}`;
|
|
4652
4442
|
const markCreateReady = () => {
|
|
4653
4443
|
try {
|
|
4654
|
-
|
|
4444
|
+
fs11.writeFileSync(reservation.createDone.replace(/\.pending$/, ".ready"), "");
|
|
4655
4445
|
} catch {
|
|
4656
4446
|
}
|
|
4657
4447
|
};
|
|
@@ -4663,36 +4453,36 @@ function createOrcaProgressTab(input) {
|
|
|
4663
4453
|
role: "run",
|
|
4664
4454
|
runId,
|
|
4665
4455
|
title,
|
|
4666
|
-
worktree:
|
|
4456
|
+
worktree: path9.resolve(cwd),
|
|
4667
4457
|
state: "opening",
|
|
4668
4458
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4669
4459
|
logPath
|
|
4670
4460
|
});
|
|
4671
4461
|
try {
|
|
4672
|
-
|
|
4462
|
+
fs11.writeFileSync(logPath, `pi-subagents / ${agent}
|
|
4673
4463
|
run ${runId} \xB7 ${stepCount === 1 ? "1 child" : `${stepCount} children`}
|
|
4674
4464
|
${"\u2500".repeat(48)}
|
|
4675
4465
|
`, { encoding: "utf-8", mode: 384 });
|
|
4676
|
-
|
|
4466
|
+
fs11.rmSync(donePath, { force: true });
|
|
4677
4467
|
} catch {
|
|
4678
4468
|
markCreateReady();
|
|
4679
4469
|
return void 0;
|
|
4680
4470
|
}
|
|
4681
4471
|
let available = true;
|
|
4682
4472
|
let truncated = false;
|
|
4683
|
-
let scheduledBytes =
|
|
4473
|
+
let scheduledBytes = fs11.statSync(logPath).size;
|
|
4684
4474
|
const progressByteLimit = MAX_MIRROR_BYTES - MIRROR_FOOTER_RESERVE_BYTES;
|
|
4685
|
-
const logStream =
|
|
4475
|
+
const logStream = fs11.createWriteStream(logPath, { flags: "a", mode: 384 });
|
|
4686
4476
|
const failObserver = () => {
|
|
4687
4477
|
if (!available) return;
|
|
4688
4478
|
available = false;
|
|
4689
4479
|
logStream.destroy();
|
|
4690
4480
|
try {
|
|
4691
|
-
|
|
4481
|
+
fs11.rmSync(logPath, { force: true });
|
|
4692
4482
|
} catch {
|
|
4693
4483
|
}
|
|
4694
4484
|
try {
|
|
4695
|
-
|
|
4485
|
+
fs11.rmSync(donePath, { force: true });
|
|
4696
4486
|
} catch {
|
|
4697
4487
|
}
|
|
4698
4488
|
};
|
|
@@ -4713,8 +4503,8 @@ ${"\u2500".repeat(48)}
|
|
|
4713
4503
|
};
|
|
4714
4504
|
let createSettled = false;
|
|
4715
4505
|
let resolveCreationSettled;
|
|
4716
|
-
const creationSettled = new Promise((
|
|
4717
|
-
resolveCreationSettled =
|
|
4506
|
+
const creationSettled = new Promise((resolve9) => {
|
|
4507
|
+
resolveCreationSettled = resolve9;
|
|
4718
4508
|
});
|
|
4719
4509
|
let cleanupPaths;
|
|
4720
4510
|
const scheduleDeferredCleanup = () => {
|
|
@@ -4736,7 +4526,7 @@ ${"\u2500".repeat(48)}
|
|
|
4736
4526
|
"terminal",
|
|
4737
4527
|
"create",
|
|
4738
4528
|
"--worktree",
|
|
4739
|
-
`path:${
|
|
4529
|
+
`path:${path9.resolve(cwd)}`,
|
|
4740
4530
|
"--title",
|
|
4741
4531
|
title,
|
|
4742
4532
|
"--command",
|
|
@@ -4804,7 +4594,7 @@ ${"\u2500".repeat(16)} child ${index2 + 1}/${count} \xB7 ${label} ${"\u2500".rep
|
|
|
4804
4594
|
let verifiedSessionFile;
|
|
4805
4595
|
if (sessionId && sessionFile) {
|
|
4806
4596
|
try {
|
|
4807
|
-
verifiedSessionFile =
|
|
4597
|
+
verifiedSessionFile = fs11.realpathSync(sessionFile);
|
|
4808
4598
|
} catch {
|
|
4809
4599
|
}
|
|
4810
4600
|
}
|
|
@@ -4819,18 +4609,18 @@ ${terminalMessage}
|
|
|
4819
4609
|
if (scheduledBytes + Buffer.byteLength(footer) > MAX_MIRROR_BYTES) footer = `
|
|
4820
4610
|
${status}
|
|
4821
4611
|
`;
|
|
4822
|
-
return new Promise((
|
|
4612
|
+
return new Promise((resolve9) => {
|
|
4823
4613
|
logStream.end(footer, () => {
|
|
4824
4614
|
if (available) {
|
|
4825
4615
|
try {
|
|
4826
|
-
|
|
4616
|
+
fs11.writeFileSync(donePath, `${status}
|
|
4827
4617
|
`, { encoding: "utf-8", mode: 384 });
|
|
4828
4618
|
} catch {
|
|
4829
4619
|
}
|
|
4830
4620
|
cleanupPaths = [logPath, donePath];
|
|
4831
4621
|
scheduleDeferredCleanup();
|
|
4832
4622
|
}
|
|
4833
|
-
|
|
4623
|
+
resolve9();
|
|
4834
4624
|
});
|
|
4835
4625
|
});
|
|
4836
4626
|
}
|
|
@@ -5456,7 +5246,7 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
5456
5246
|
childIndex: options.index ?? 0
|
|
5457
5247
|
}) : void 0;
|
|
5458
5248
|
const permissionRules = resolvePermissionRules(options.permissions, agent.permissions);
|
|
5459
|
-
const permissionAuditPath = permissionRules && options.artifactsDir ?
|
|
5249
|
+
const permissionAuditPath = permissionRules && options.artifactsDir ? path10.join(options.artifactsDir, "permission-audit", `${options.runId}-${options.index ?? 0}.jsonl`) : void 0;
|
|
5460
5250
|
let onWatchdogStatus;
|
|
5461
5251
|
const launch = buildInProcessChildLaunch({
|
|
5462
5252
|
machine: options.machine,
|
|
@@ -5647,7 +5437,7 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5647
5437
|
const childSessions = options.childSessionFactory ?? childSessionFactory();
|
|
5648
5438
|
const partialOutput = createPartialOutputTracker();
|
|
5649
5439
|
let childPromptFailed = false;
|
|
5650
|
-
const exitCode = await new Promise((
|
|
5440
|
+
const exitCode = await new Promise((resolve9) => {
|
|
5651
5441
|
const jsonlWriter = createJsonlWriter(shared.jsonlPath, { pause() {
|
|
5652
5442
|
}, resume() {
|
|
5653
5443
|
} });
|
|
@@ -5720,6 +5510,8 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5720
5510
|
let agentSettledReceived = false;
|
|
5721
5511
|
let queuedDrainHold = false;
|
|
5722
5512
|
let compactionStartedReceived = false;
|
|
5513
|
+
let terminalAgentEndReceived = false;
|
|
5514
|
+
let compactionAfterFinalOpen = false;
|
|
5723
5515
|
let finalDrainTimer;
|
|
5724
5516
|
let finalHardFinishTimer;
|
|
5725
5517
|
let watchdogTailTimer;
|
|
@@ -5762,7 +5554,7 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5762
5554
|
if (sessionSettled || finalDrainTimer || lifecycleFinished) return;
|
|
5763
5555
|
finalDrainTimer = setTimeout(() => {
|
|
5764
5556
|
if (lifecycleFinished || sessionSettled) return;
|
|
5765
|
-
if (capture.finalDrainHeld() || observeQueuedDrainHold()) {
|
|
5557
|
+
if (capture.finalDrainHeld() || observeQueuedDrainHold() || compactionAfterFinalOpen) {
|
|
5766
5558
|
finalDrainTimer = void 0;
|
|
5767
5559
|
armFinalDrainTimer();
|
|
5768
5560
|
return;
|
|
@@ -5776,9 +5568,7 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5776
5568
|
if (lifecycleFinished || sessionSettled) return;
|
|
5777
5569
|
settle(void 0, true);
|
|
5778
5570
|
}, HARD_FINISH_MS);
|
|
5779
|
-
finalHardFinishTimer.unref?.();
|
|
5780
5571
|
}, FINAL_STOP_GRACE_MS);
|
|
5781
|
-
finalDrainTimer.unref?.();
|
|
5782
5572
|
};
|
|
5783
5573
|
function armWatchdogTail() {
|
|
5784
5574
|
if (!cleanTerminalAssistantStopReceived && !agentSettledReceived || watchdogTailTimer || lifecycleFinished || sessionSettled) return;
|
|
@@ -5796,12 +5586,18 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5796
5586
|
}, childWatchdog?.watchdogTailTimeoutMs ?? 12e4);
|
|
5797
5587
|
watchdogTailTimer.unref?.();
|
|
5798
5588
|
}
|
|
5799
|
-
const applyChildLifecycle = (action) => {
|
|
5589
|
+
const applyChildLifecycle = (action, event) => {
|
|
5800
5590
|
if (action === "cancel-drain") {
|
|
5801
|
-
|
|
5591
|
+
terminalAgentEndReceived = event?.type === "agent_end" && event.willRetry !== true && cleanTerminalAssistantStopReceived;
|
|
5802
5592
|
agentSettledReceived = false;
|
|
5803
5593
|
clearFinalDrainTimers();
|
|
5804
5594
|
clearWatchdogTailTimer();
|
|
5595
|
+
if (terminalAgentEndReceived) {
|
|
5596
|
+
startFinalDrain();
|
|
5597
|
+
return;
|
|
5598
|
+
}
|
|
5599
|
+
cleanTerminalAssistantStopReceived = false;
|
|
5600
|
+
compactionAfterFinalOpen = false;
|
|
5805
5601
|
return;
|
|
5806
5602
|
}
|
|
5807
5603
|
if (action === "start-drain") startFinalDrain();
|
|
@@ -5851,7 +5647,7 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5851
5647
|
}
|
|
5852
5648
|
await session?.dispose();
|
|
5853
5649
|
}).catch(() => void 0).then(() => {
|
|
5854
|
-
|
|
5650
|
+
resolve9(code);
|
|
5855
5651
|
});
|
|
5856
5652
|
};
|
|
5857
5653
|
const drainPendingControlEvents = () => {
|
|
@@ -5883,13 +5679,13 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5883
5679
|
};
|
|
5884
5680
|
const recordActiveToolCall = (event, args, now) => {
|
|
5885
5681
|
const key = toolTimeoutCallKey(event, ++activeToolSequence);
|
|
5886
|
-
const
|
|
5682
|
+
const path19 = resolveCurrentPath(event.toolName, args);
|
|
5887
5683
|
const active = {
|
|
5888
5684
|
key,
|
|
5889
5685
|
tool: event.toolName,
|
|
5890
5686
|
args: extractToolArgsPreview(args),
|
|
5891
5687
|
startedAt: now,
|
|
5892
|
-
...
|
|
5688
|
+
...path19 !== void 0 ? { path: path19 } : {}
|
|
5893
5689
|
};
|
|
5894
5690
|
activeToolCalls.set(key, active);
|
|
5895
5691
|
const keys = activeToolKeysByName.get(active.tool) ?? [];
|
|
@@ -6042,11 +5838,17 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
6042
5838
|
if (evt.type === "compaction_start") {
|
|
6043
5839
|
compactionStartedReceived = true;
|
|
6044
5840
|
if (agentSettledReceived) afterCompactionSettlement = true;
|
|
5841
|
+
if (terminalAgentEndReceived) compactionAfterFinalOpen = true;
|
|
6045
5842
|
}
|
|
6046
5843
|
if (evt.type === "compaction_end" && evt.willRetry === true) {
|
|
6047
5844
|
compactionStartedReceived = false;
|
|
6048
5845
|
afterCompactionSettlement = false;
|
|
6049
5846
|
}
|
|
5847
|
+
if (evt.type === "compaction_end" && evt.willRetry !== true && compactionAfterFinalOpen) {
|
|
5848
|
+
compactionAfterFinalOpen = false;
|
|
5849
|
+
clearFinalDrainTimers();
|
|
5850
|
+
startFinalDrain();
|
|
5851
|
+
}
|
|
6050
5852
|
if (evt.type === "turn_start" || evt.type === "agent_start" || evt.type === "auto_retry_start") {
|
|
6051
5853
|
queuedDrainHold = false;
|
|
6052
5854
|
}
|
|
@@ -6073,7 +5875,7 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
6073
5875
|
agentSettledReceived = true;
|
|
6074
5876
|
afterCompactionSettlement = compactionStartedReceived;
|
|
6075
5877
|
}
|
|
6076
|
-
applyChildLifecycle(lifecycleAction);
|
|
5878
|
+
applyChildLifecycle(lifecycleAction, evt);
|
|
6077
5879
|
if (isChildWatchdogStatusEvent(evt)) {
|
|
6078
5880
|
if (!childWatchdog) return;
|
|
6079
5881
|
const next = acceptChildWatchdogEvent({
|
|
@@ -6625,7 +6427,7 @@ ${resolvedOutput.saveError}` : resolvedOutput.saveError;
|
|
|
6625
6427
|
}
|
|
6626
6428
|
function writeStructuredOutputArtifacts(runtime, value, acceptanceReport) {
|
|
6627
6429
|
try {
|
|
6628
|
-
mkdirSync6(
|
|
6430
|
+
mkdirSync6(path10.dirname(runtime.outputPath), { recursive: true });
|
|
6629
6431
|
writeFileSync4(runtime.outputPath, JSON.stringify(value), { mode: 384 });
|
|
6630
6432
|
if (runtime.acceptanceReportPath) {
|
|
6631
6433
|
if (acceptanceReport !== void 0) writeFileSync4(runtime.acceptanceReportPath, JSON.stringify(acceptanceReport), { mode: 384 });
|
|
@@ -6655,7 +6457,7 @@ async function runSyncCompletionInner(runtimeCwd, agents, agentName, task, optio
|
|
|
6655
6457
|
const childSessionName = deriveChildSessionName({ agent: agentName, task });
|
|
6656
6458
|
const agent = agents.find((a) => a.name === agentName);
|
|
6657
6459
|
if (!agent) {
|
|
6658
|
-
const diagnosticContext = options.unknownAgentDiagnosticContext ?? unknownAgentDiagnosticContext(discoverAgents(
|
|
6460
|
+
const diagnosticContext = options.unknownAgentDiagnosticContext ?? unknownAgentDiagnosticContext(discoverAgents(path10.resolve(options.cwd ?? runtimeCwd), "both"));
|
|
6659
6461
|
return redactResultPrompt(withRunContext({
|
|
6660
6462
|
index: options.index ?? 0,
|
|
6661
6463
|
agent: agentName,
|
|
@@ -6748,7 +6550,7 @@ async function runSyncCompletionInner(runtimeCwd, agents, agentName, task, optio
|
|
|
6748
6550
|
${acceptancePrompt}` : task;
|
|
6749
6551
|
options.onEffectivePrompt?.(taskWithAcceptance);
|
|
6750
6552
|
const sessionEnabled = Boolean(options.sessionFile || options.sessionDir) || shareEnabled;
|
|
6751
|
-
if (options.context === "fork" && options.sessionFile &&
|
|
6553
|
+
if (options.context === "fork" && options.sessionFile && existsSync4(options.sessionFile)) {
|
|
6752
6554
|
alignForkedSessionCwd(options.sessionFile, options.cwd ?? runtimeCwd);
|
|
6753
6555
|
}
|
|
6754
6556
|
const skillNames = options.skills ?? agent.skills ?? [];
|
|
@@ -6758,7 +6560,7 @@ ${acceptancePrompt}` : task;
|
|
|
6758
6560
|
skillCwd,
|
|
6759
6561
|
runtimeCwd,
|
|
6760
6562
|
agent.skillPath,
|
|
6761
|
-
agent.filePath ?
|
|
6563
|
+
agent.filePath ? path10.dirname(agent.filePath) : skillCwd
|
|
6762
6564
|
);
|
|
6763
6565
|
if (skillNames.some((skill) => skill.trim() === "pi-subagents") && missingSkills.includes("pi-subagents")) {
|
|
6764
6566
|
return redactResultPrompt(withRunContext({
|
|
@@ -6913,7 +6715,7 @@ ${PROMPT_REDACTED}; live Prompt Audit only.
|
|
|
6913
6715
|
messages: attemptResult.messages ?? [],
|
|
6914
6716
|
error: attemptResult.error,
|
|
6915
6717
|
processSignal: attemptResult.processSignal,
|
|
6916
|
-
sessionAvailable: Boolean(options.sessionFile &&
|
|
6718
|
+
sessionAvailable: Boolean(options.sessionFile && existsSync4(options.sessionFile)),
|
|
6917
6719
|
alreadyResumed: attemptIndex > 0,
|
|
6918
6720
|
stopped: attemptResult.stopped || attemptResult.detached || Boolean(detachedReason) || Boolean(options.workflowChildPermitLaunch) || options.signal?.aborted,
|
|
6919
6721
|
interrupted: attemptResult.interrupted || options.interruptSignal?.aborted,
|
|
@@ -6984,7 +6786,7 @@ ${recovery.diagnostic}` : recovery.diagnostic;
|
|
|
6984
6786
|
result2.outputSaveError = result2.outputSaveError ? `${result2.outputSaveError}
|
|
6985
6787
|
${message}` : message;
|
|
6986
6788
|
}
|
|
6987
|
-
if (options.sessionFile && (
|
|
6789
|
+
if (options.sessionFile && (existsSync4(options.sessionFile) || result2.messages?.length)) {
|
|
6988
6790
|
result2.sessionFile = options.sessionFile;
|
|
6989
6791
|
} else if (shareEnabled && options.sessionDir) {
|
|
6990
6792
|
const sessionFile = findLatestSessionFile(options.sessionDir);
|
|
@@ -7089,8 +6891,8 @@ async function runSync(runtimeCwd, agents, agentName, task, options) {
|
|
|
7089
6891
|
let activeDetachAttempt;
|
|
7090
6892
|
let detachReadyPublished = false;
|
|
7091
6893
|
let resolveReceipt;
|
|
7092
|
-
const receipt = new Promise((
|
|
7093
|
-
resolveReceipt =
|
|
6894
|
+
const receipt = new Promise((resolve9) => {
|
|
6895
|
+
resolveReceipt = resolve9;
|
|
7094
6896
|
});
|
|
7095
6897
|
const originSignal = options.signal;
|
|
7096
6898
|
const originController = new AbortController();
|
|
@@ -7344,8 +7146,8 @@ var WATCHDOG_THINKING_VALUES = ["inherit", ...THINKING_LEVELS];
|
|
|
7344
7146
|
// src/shared/pruned-fork.ts
|
|
7345
7147
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
7346
7148
|
import { createHash as createHash2 } from "node:crypto";
|
|
7347
|
-
import * as
|
|
7348
|
-
import * as
|
|
7149
|
+
import * as fs12 from "node:fs";
|
|
7150
|
+
import * as path11 from "node:path";
|
|
7349
7151
|
var MAX_INHERITED_SESSION_BYTES = 64 * 1024;
|
|
7350
7152
|
var MIN_USEFUL_SPILL_BYTES = 320;
|
|
7351
7153
|
var MAX_ITEM_SUMMARY_CHARS = 160;
|
|
@@ -7374,7 +7176,7 @@ function stableJson(value) {
|
|
|
7374
7176
|
throw new Error(`Pruned fork cannot serialize ${typeof value} transcript data.`);
|
|
7375
7177
|
}
|
|
7376
7178
|
function readEntries(sessionFile) {
|
|
7377
|
-
return
|
|
7179
|
+
return fs12.readFileSync(sessionFile, "utf-8").split("\n").filter((line) => line.trim()).map((line, index) => {
|
|
7378
7180
|
try {
|
|
7379
7181
|
const parsed = JSON.parse(line);
|
|
7380
7182
|
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 +7417,17 @@ function prunedForkRecoveryPath(sessionFile) {
|
|
|
7615
7417
|
return `${sessionFile}.pruned-recovery.json`;
|
|
7616
7418
|
}
|
|
7617
7419
|
function writeAtomicPrivate(filePath, content, mode) {
|
|
7618
|
-
const tempFile =
|
|
7420
|
+
const tempFile = path11.join(path11.dirname(filePath), `.${path11.basename(filePath)}.${process.pid}.tmp`);
|
|
7619
7421
|
try {
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7422
|
+
fs12.writeFileSync(tempFile, content, { encoding: "utf-8", mode });
|
|
7423
|
+
fs12.chmodSync(tempFile, mode);
|
|
7424
|
+
fs12.renameSync(tempFile, filePath);
|
|
7623
7425
|
} finally {
|
|
7624
|
-
|
|
7426
|
+
fs12.rmSync(tempFile, { force: true });
|
|
7625
7427
|
}
|
|
7626
7428
|
}
|
|
7627
7429
|
function writeEntriesAtomic(sessionFile, entries) {
|
|
7628
|
-
writeAtomicPrivate(sessionFile, serializedEntries(entries),
|
|
7430
|
+
writeAtomicPrivate(sessionFile, serializedEntries(entries), fs12.statSync(sessionFile).mode & 511);
|
|
7629
7431
|
}
|
|
7630
7432
|
async function pruneForkSessionFile(sessionFile, summarize, options = {}) {
|
|
7631
7433
|
const entries = readEntries(sessionFile);
|
|
@@ -7666,11 +7468,11 @@ async function pruneForkSessionFile(sessionFile, summarize, options = {}) {
|
|
|
7666
7468
|
try {
|
|
7667
7469
|
writeAtomicPrivate(recoveryPath, `${JSON.stringify(payload, null, " ")}
|
|
7668
7470
|
`, 384);
|
|
7669
|
-
const stored = JSON.parse(
|
|
7471
|
+
const stored = JSON.parse(fs12.readFileSync(recoveryPath, "utf-8"));
|
|
7670
7472
|
if (!validateRecovery(stored) || stableJson(stored) !== stableJson(payload)) throw new Error("Pruned fork stored recovery payload failed validation.");
|
|
7671
7473
|
writeEntriesAtomic(sessionFile, entries);
|
|
7672
7474
|
} catch (error) {
|
|
7673
|
-
|
|
7475
|
+
fs12.rmSync(recoveryPath, { force: true });
|
|
7674
7476
|
throw error;
|
|
7675
7477
|
}
|
|
7676
7478
|
return true;
|
|
@@ -7719,11 +7521,11 @@ async function createPrunedForkSessionWriter(ctx, config, signal) {
|
|
|
7719
7521
|
}
|
|
7720
7522
|
|
|
7721
7523
|
// src/runs/foreground/async-dismiss-action.ts
|
|
7722
|
-
import * as
|
|
7723
|
-
import * as
|
|
7524
|
+
import * as fs13 from "node:fs";
|
|
7525
|
+
import * as path12 from "node:path";
|
|
7724
7526
|
function dismissRecoveredWorkflow(state, location) {
|
|
7725
7527
|
const asyncDir = location.asyncDir;
|
|
7726
|
-
const runId = location.resolvedId ?? (asyncDir ?
|
|
7528
|
+
const runId = location.resolvedId ?? (asyncDir ? path12.basename(asyncDir) : "requested");
|
|
7727
7529
|
if (!asyncDir) {
|
|
7728
7530
|
return {
|
|
7729
7531
|
content: [{ type: "text", text: `Recovered workflow '${runId}' has no disk status to dismiss.` }],
|
|
@@ -7762,7 +7564,7 @@ function dismissRecoveredWorkflow(state, location) {
|
|
|
7762
7564
|
}
|
|
7763
7565
|
let latestStatus = status;
|
|
7764
7566
|
const resultPath = resultFilePath(DIRS.results, status.runId);
|
|
7765
|
-
if (
|
|
7567
|
+
if (fs13.existsSync(resultPath)) {
|
|
7766
7568
|
const reconciled = reconcileAsyncRun(asyncDir).status;
|
|
7767
7569
|
if (reconciled && reconciled.state !== "running") {
|
|
7768
7570
|
return {
|
|
@@ -7774,7 +7576,7 @@ function dismissRecoveredWorkflow(state, location) {
|
|
|
7774
7576
|
if (reconciled) latestStatus = reconciled;
|
|
7775
7577
|
}
|
|
7776
7578
|
const dismissed = { ...latestStatus, displayDismissedAt: Date.now() };
|
|
7777
|
-
writeAtomicJson(
|
|
7579
|
+
writeAtomicJson(path12.join(asyncDir, "status.json"), dismissed);
|
|
7778
7580
|
const repaired = reconcileAsyncRun(asyncDir).status;
|
|
7779
7581
|
if (repaired && repaired.state !== "running") {
|
|
7780
7582
|
return {
|
|
@@ -7795,8 +7597,8 @@ function dismissRecoveredWorkflow(state, location) {
|
|
|
7795
7597
|
}
|
|
7796
7598
|
|
|
7797
7599
|
// src/runs/foreground/workflow-detach-reconcile.ts
|
|
7798
|
-
import * as
|
|
7799
|
-
import * as
|
|
7600
|
+
import * as fs14 from "node:fs";
|
|
7601
|
+
import * as path13 from "node:path";
|
|
7800
7602
|
|
|
7801
7603
|
// src/workflows/workflow-settlement.ts
|
|
7802
7604
|
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 +7830,9 @@ function workflowResultChildren(status, childRunId, result2, existingResults, re
|
|
|
8028
7830
|
}));
|
|
8029
7831
|
}
|
|
8030
7832
|
function reconcileWorkflowReceipt(status, childRunId, result2, asyncDir, resolution) {
|
|
8031
|
-
const receiptRoot =
|
|
7833
|
+
const receiptRoot = path13.dirname(asyncDir);
|
|
8032
7834
|
const receiptPath = workflowReceiptPath(receiptRoot, status.runId);
|
|
8033
|
-
if (!
|
|
7835
|
+
if (!fs14.existsSync(receiptPath)) return void 0;
|
|
8034
7836
|
const receipt = readWorkflowReceipt(receiptRoot, status.runId);
|
|
8035
7837
|
const step = status.steps?.find((candidate) => candidate.runId === childRunId);
|
|
8036
7838
|
const key = step?.workflowKey;
|
|
@@ -8039,13 +7841,13 @@ function reconcileWorkflowReceipt(status, childRunId, result2, asyncDir, resolut
|
|
|
8039
7841
|
if (!entry) throw new Error(`Workflow receipt '${status.runId}' has no detached child key '${key}'.`);
|
|
8040
7842
|
let resumability;
|
|
8041
7843
|
try {
|
|
8042
|
-
const target = resolveAsyncResumeTarget({ id: childRunId, dir:
|
|
7844
|
+
const target = resolveAsyncResumeTarget({ id: childRunId, dir: path13.join(DIRS.async, childRunId) }, {}, { requireSessionFile: true, sessionId: status.sessionId });
|
|
8043
7845
|
resumability = target.kind === "revive" ? { state: "resumable" } : { state: "not-resumable", reason: "child is still running" };
|
|
8044
7846
|
} catch (error) {
|
|
8045
7847
|
resumability = { state: "not-resumable", reason: error instanceof Error ? error.message : String(error) };
|
|
8046
7848
|
}
|
|
8047
7849
|
const outputReference = result2.savedOutputPath ?? result2.outputReference?.path ?? entry.outputReference;
|
|
8048
|
-
const childStatus = readStatus(
|
|
7850
|
+
const childStatus = readStatus(path13.join(DIRS.async, childRunId));
|
|
8049
7851
|
const externalStep = childStatus?.steps?.length === 1 && childStatus.steps[0]?.runner?.type === "external-cli" ? childStatus.steps[0] : void 0;
|
|
8050
7852
|
const externalRunner = normalizeExternalCliRunnerStatus(result2.runner?.type === "external-cli" ? result2.runner : externalStep?.runner);
|
|
8051
7853
|
const externalProcess = result2.externalProcess ?? externalStep?.externalProcess;
|
|
@@ -8089,9 +7891,9 @@ function reconcileWorkflowReceipt(status, childRunId, result2, asyncDir, resolut
|
|
|
8089
7891
|
return { receipt: next, path: writeWorkflowReceipt(asyncDir, next) };
|
|
8090
7892
|
}
|
|
8091
7893
|
function appendDetachedWorkflowEvent(asyncDir, event) {
|
|
8092
|
-
const eventsPath =
|
|
7894
|
+
const eventsPath = path13.join(asyncDir, "events.jsonl");
|
|
8093
7895
|
try {
|
|
8094
|
-
|
|
7896
|
+
fs14.appendFileSync(eventsPath, `${JSON.stringify(event)}
|
|
8095
7897
|
`, "utf-8");
|
|
8096
7898
|
} catch (error) {
|
|
8097
7899
|
console.error(`Failed to append detached workflow event '${eventsPath}':`, error);
|
|
@@ -8099,7 +7901,7 @@ function appendDetachedWorkflowEvent(asyncDir, event) {
|
|
|
8099
7901
|
}
|
|
8100
7902
|
function reconcileDetachedWorkflowChildCompletion(input) {
|
|
8101
7903
|
const job = input.state.asyncJobs.get(input.workflowRunId);
|
|
8102
|
-
const asyncDir = job?.asyncDir ??
|
|
7904
|
+
const asyncDir = job?.asyncDir ?? path13.join(DIRS.async, input.workflowRunId);
|
|
8103
7905
|
const status = readStatus(asyncDir);
|
|
8104
7906
|
if (!status) return false;
|
|
8105
7907
|
const matchingControls = input.workflowKey === void 0 ? [] : [...input.state.foregroundControls?.values() ?? []].filter(
|
|
@@ -8124,7 +7926,7 @@ function reconcileDetachedWorkflowChildCompletion(input) {
|
|
|
8124
7926
|
const resultPath = resultFilePath(DIRS.results, input.workflowRunId);
|
|
8125
7927
|
let existing;
|
|
8126
7928
|
try {
|
|
8127
|
-
const parsed = JSON.parse(
|
|
7929
|
+
const parsed = JSON.parse(fs14.readFileSync(resultPath, "utf-8"));
|
|
8128
7930
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) existing = parsed;
|
|
8129
7931
|
} catch (error) {
|
|
8130
7932
|
if (error.code !== "ENOENT") throw error;
|
|
@@ -8170,7 +7972,7 @@ function reconcileDetachedWorkflowChildCompletion(input) {
|
|
|
8170
7972
|
terminalOutcome: receipt?.terminalOutcome,
|
|
8171
7973
|
eventMetadata: { reconciledFromDetachedChild: input.childRunId }
|
|
8172
7974
|
});
|
|
8173
|
-
writeAtomicJson(
|
|
7975
|
+
writeAtomicJson(path13.join(asyncDir, "status.json"), plan.status);
|
|
8174
7976
|
updateActiveRunIndex(asyncDir, plan.status.state, plan.status.toolCallId);
|
|
8175
7977
|
if (job) {
|
|
8176
7978
|
job.status = plan.status.state;
|
|
@@ -8222,14 +8024,14 @@ function reconcileDetachedWorkflowChildCompletion(input) {
|
|
|
8222
8024
|
}
|
|
8223
8025
|
|
|
8224
8026
|
// src/runs/background/chain-root-attachment.ts
|
|
8225
|
-
import * as
|
|
8226
|
-
import * as
|
|
8027
|
+
import * as fs15 from "node:fs";
|
|
8028
|
+
import * as path14 from "node:path";
|
|
8227
8029
|
var TERMINAL_STATES = /* @__PURE__ */ new Set(["complete", "failed", "partial", "paused", "stopped"]);
|
|
8228
8030
|
var TERMINAL_STEP_STATUSES = /* @__PURE__ */ new Set(["complete", "completed", "failed", "partial", "paused", "stopped"]);
|
|
8229
8031
|
function readResultFile(resultPath) {
|
|
8230
8032
|
let raw;
|
|
8231
8033
|
try {
|
|
8232
|
-
raw =
|
|
8034
|
+
raw = fs15.readFileSync(resultPath, "utf-8");
|
|
8233
8035
|
} catch (error) {
|
|
8234
8036
|
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
8235
8037
|
return void 0;
|
|
@@ -8241,7 +8043,7 @@ function readResultFile(resultPath) {
|
|
|
8241
8043
|
function readImportedResultFile(root, status) {
|
|
8242
8044
|
const direct = readResultFile(root.resultPath);
|
|
8243
8045
|
if (direct || !status?.sessionId) return direct;
|
|
8244
|
-
const indexedPath = resultPayloadPathForSessionRun(
|
|
8046
|
+
const indexedPath = resultPayloadPathForSessionRun(path14.dirname(root.resultPath), status.sessionId, root.runId);
|
|
8245
8047
|
return indexedPath ? readResultFile(indexedPath) : void 0;
|
|
8246
8048
|
}
|
|
8247
8049
|
function selectedStatusStep(status, index) {
|
|
@@ -8376,10 +8178,10 @@ async function waitForImportedAsyncRoot(root, options = {}) {
|
|
|
8376
8178
|
} else {
|
|
8377
8179
|
terminalSince = void 0;
|
|
8378
8180
|
}
|
|
8379
|
-
if (!status && !
|
|
8181
|
+
if (!status && !fs15.existsSync(root.asyncDir)) {
|
|
8380
8182
|
throw new Error(`Attached async root '${root.runId}' directory does not exist: ${root.asyncDir}`);
|
|
8381
8183
|
}
|
|
8382
|
-
await new Promise((
|
|
8184
|
+
await new Promise((resolve9) => setTimeout(resolve9, pollIntervalMs));
|
|
8383
8185
|
}
|
|
8384
8186
|
}
|
|
8385
8187
|
function resolveAsyncRootResultPath(resultsDir, runId) {
|
|
@@ -8387,7 +8189,7 @@ function resolveAsyncRootResultPath(resultsDir, runId) {
|
|
|
8387
8189
|
}
|
|
8388
8190
|
|
|
8389
8191
|
// src/runs/background/await-async-run.ts
|
|
8390
|
-
import * as
|
|
8192
|
+
import * as fs16 from "node:fs";
|
|
8391
8193
|
function errorMessage2(error) {
|
|
8392
8194
|
return error instanceof Error ? error.message : String(error);
|
|
8393
8195
|
}
|
|
@@ -8395,7 +8197,7 @@ function runnerExitedWithoutResult(asyncDir, runId, resultPath) {
|
|
|
8395
8197
|
const status = readStatus(asyncDir);
|
|
8396
8198
|
if (!status || status.state !== "running" && status.state !== "queued" || typeof status.pid !== "number") return false;
|
|
8397
8199
|
if (checkRecordedPidLiveness({ pid: status.pid, pidNamespaceScope: status.pidNamespaceScope, bootId: status.bootId }) !== "dead") return false;
|
|
8398
|
-
return !
|
|
8200
|
+
return !fs16.existsSync(resultPath) && !(status.sessionId && resultPayloadPathForSessionRun(asyncDir, status.sessionId, runId));
|
|
8399
8201
|
}
|
|
8400
8202
|
async function awaitExistingAsyncRun(asyncDir, runId, signal) {
|
|
8401
8203
|
signal.throwIfAborted();
|
|
@@ -8430,7 +8232,7 @@ function claimWorkflowAwaitedResult(asyncDir, claimant, read) {
|
|
|
8430
8232
|
try {
|
|
8431
8233
|
return withResultRunLease(asyncDir, read.runId, () => {
|
|
8432
8234
|
if (resultSnapshotReplaced(resultPath, read)) return void 0;
|
|
8433
|
-
|
|
8235
|
+
fs16.renameSync(resultPath, claimedPath);
|
|
8434
8236
|
return claimedPath;
|
|
8435
8237
|
});
|
|
8436
8238
|
} catch {
|
|
@@ -8439,8 +8241,8 @@ function claimWorkflowAwaitedResult(asyncDir, claimant, read) {
|
|
|
8439
8241
|
}
|
|
8440
8242
|
|
|
8441
8243
|
// src/runs/background/run-status.ts
|
|
8442
|
-
import * as
|
|
8443
|
-
import * as
|
|
8244
|
+
import * as fs17 from "node:fs";
|
|
8245
|
+
import * as path15 from "node:path";
|
|
8444
8246
|
var STRUCTURED_PREVIEW_WITH_PATH_CHARS = 500;
|
|
8445
8247
|
function formatProcessTerminal(value) {
|
|
8446
8248
|
if (!value) return "missing";
|
|
@@ -8450,7 +8252,7 @@ function debugProcessTerminal(asyncDir, status) {
|
|
|
8450
8252
|
const expected = { runId: status.runId, runnerProcessInstanceId: status.processTerminal?.runnerProcessInstanceId };
|
|
8451
8253
|
return {
|
|
8452
8254
|
sidecar: readProcessTerminal(asyncDir, expected),
|
|
8453
|
-
overlay: sanitizeProcessTerminal(status.processTerminal, expected,
|
|
8255
|
+
overlay: sanitizeProcessTerminal(status.processTerminal, expected, path15.join(asyncDir, "status.json"))
|
|
8454
8256
|
};
|
|
8455
8257
|
}
|
|
8456
8258
|
function formatCapacityOwner(inspect) {
|
|
@@ -8484,9 +8286,9 @@ function formatRunLifecycleDebug(input) {
|
|
|
8484
8286
|
"Run lifecycle debug",
|
|
8485
8287
|
`Run: ${status.runId}`,
|
|
8486
8288
|
`Dir: ${asyncDir}`,
|
|
8487
|
-
`Status file: ${
|
|
8289
|
+
`Status file: ${path15.join(asyncDir, "status.json")}`,
|
|
8488
8290
|
status.workflowReceiptPath ? `Workflow receipt: ${status.workflowReceiptPath}` : void 0,
|
|
8489
|
-
`Process terminal file: ${
|
|
8291
|
+
`Process terminal file: ${path15.join(asyncDir, "process-terminal.json")}`,
|
|
8490
8292
|
`Session: ${status.sessionId ?? "unknown"}`,
|
|
8491
8293
|
`State: ${status.state}`,
|
|
8492
8294
|
`Mode: ${status.mode}`,
|
|
@@ -8501,7 +8303,7 @@ function formatRunLifecycleDebug(input) {
|
|
|
8501
8303
|
return lines.join("\n");
|
|
8502
8304
|
}
|
|
8503
8305
|
function hasExistingSessionFile(value) {
|
|
8504
|
-
return typeof value === "string" &&
|
|
8306
|
+
return typeof value === "string" && fs17.existsSync(value);
|
|
8505
8307
|
}
|
|
8506
8308
|
function formatResumeGuidance(runId, children, fallbackSessionFile, options = {}) {
|
|
8507
8309
|
if (options.stopped) return "Resume: unavailable; stopped runs are not resumable. Start a new run instead.";
|
|
@@ -8564,9 +8366,9 @@ function externalJobFollowUpSupported(provider) {
|
|
|
8564
8366
|
}
|
|
8565
8367
|
function rememberedForegroundChildOutput(child) {
|
|
8566
8368
|
const outputPath = child.artifactPaths?.outputPath ?? child.savedOutputPath;
|
|
8567
|
-
if (outputPath &&
|
|
8369
|
+
if (outputPath && fs17.existsSync(outputPath)) {
|
|
8568
8370
|
try {
|
|
8569
|
-
const artifactOutput =
|
|
8371
|
+
const artifactOutput = fs17.readFileSync(outputPath, "utf-8").trim();
|
|
8570
8372
|
if (artifactOutput) return artifactOutput;
|
|
8571
8373
|
} catch {
|
|
8572
8374
|
}
|
|
@@ -8865,8 +8667,8 @@ No running work was terminated.` }],
|
|
|
8865
8667
|
};
|
|
8866
8668
|
}
|
|
8867
8669
|
const effectiveRunId = status?.runId ?? resolvedId ?? "unknown";
|
|
8868
|
-
const logPath =
|
|
8869
|
-
const eventsPath =
|
|
8670
|
+
const logPath = path15.join(asyncDir, `subagent-log-${effectiveRunId}.md`);
|
|
8671
|
+
const eventsPath = path15.join(asyncDir, "events.jsonl");
|
|
8870
8672
|
if (status) {
|
|
8871
8673
|
if (params.action === "debug.run") {
|
|
8872
8674
|
const { sidecar, overlay } = debugProcessTerminal(asyncDir, status);
|
|
@@ -8915,7 +8717,7 @@ No running work was terminated.` }],
|
|
|
8915
8717
|
const updated = status.lastUpdate ? new Date(status.lastUpdate).toISOString() : "n/a";
|
|
8916
8718
|
const statusActivityText = status.state === "running" ? formatActivityLabel(status.lastActivityAt, status.activityState) : void 0;
|
|
8917
8719
|
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 },
|
|
8720
|
+
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
8721
|
const runFanoutBudgetDescriptor = readRunFanoutBudgetDescriptor(asyncDir);
|
|
8920
8722
|
const runFanoutBudget = runFanoutBudgetDescriptor ? getRunFanoutBudgetSnapshot(runFanoutBudgetDescriptor) : status.runFanoutBudget;
|
|
8921
8723
|
let missionId;
|
|
@@ -8951,7 +8753,7 @@ Extensions denied: ${status.capabilityCeiling.denyExtensions ? "yes" : "no"} (so
|
|
|
8951
8753
|
status.parentWorkflowRunId ? `Workflow parent: ${status.parentWorkflowRunId}${status.workflowKey ? ` (${status.workflowKey})` : ""}` : void 0,
|
|
8952
8754
|
status.mode === "workflow" && workflowReturnPreview !== void 0 ? `Return: ${workflowReturnPreview}` : void 0,
|
|
8953
8755
|
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: ${
|
|
8756
|
+
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
8757
|
`Progress: ${progressLabel}`,
|
|
8956
8758
|
...status.mode === "workflow" ? formatWorkflowChecklistText(projectWorkflowChecklist({
|
|
8957
8759
|
graph: status.workflowGraph,
|
|
@@ -8969,7 +8771,7 @@ Extensions denied: ${status.capabilityCeiling.denyExtensions ? "yes" : "no"} (so
|
|
|
8969
8771
|
outputPath ? `Output: ${outputPath}` : void 0,
|
|
8970
8772
|
status.parallelHandoff ? `Parallel handoff: ${status.parallelHandoff.path}` : void 0,
|
|
8971
8773
|
reconciliation.message ? `Diagnosis: ${reconciliation.message}` : void 0,
|
|
8972
|
-
reconciliation.resultPath &&
|
|
8774
|
+
reconciliation.resultPath && fs17.existsSync(reconciliation.resultPath) ? `Result: ${reconciliation.resultPath}` : void 0
|
|
8973
8775
|
].filter((line) => Boolean(line));
|
|
8974
8776
|
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
8777
|
let hasExternalJobFollowUpHint = false;
|
|
@@ -9031,8 +8833,8 @@ Extensions denied: ${status.capabilityCeiling.denyExtensions ? "yes" : "no"} (so
|
|
|
9031
8833
|
}
|
|
9032
8834
|
}
|
|
9033
8835
|
lines.push(...formatNestedRunStatusLines(step.children, { indent: " ", commandHints: true, maxLines: 20 }));
|
|
9034
|
-
const stepOutputPath =
|
|
9035
|
-
if (stepOutputPath !== outputPath &&
|
|
8836
|
+
const stepOutputPath = path15.join(asyncDir, `output-${index}.log`);
|
|
8837
|
+
if (stepOutputPath !== outputPath && fs17.existsSync(stepOutputPath)) lines.push(` Output: ${stepOutputPath}`);
|
|
9036
8838
|
if (step.status === "running" && step.runner?.type !== "external-cli" && step.runner?.type !== "external-job" && status.mode !== "workflow") {
|
|
9037
8839
|
lines.push(` Intercom target: ${resolveSubagentIntercomTarget(status.runId, step.agent, index)} (if registered)`);
|
|
9038
8840
|
lines.push(` Steer: subagent({ action: "steer", id: "${status.runId}", message: "...", options: { index: ${index} } })`);
|
|
@@ -9065,8 +8867,8 @@ Extensions denied: ${status.capabilityCeiling.denyExtensions ? "yes" : "no"} (so
|
|
|
9065
8867
|
if (status.state !== "running") {
|
|
9066
8868
|
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
8869
|
}
|
|
9068
|
-
if (
|
|
9069
|
-
if (
|
|
8870
|
+
if (fs17.existsSync(logPath)) lines.push(`Log: ${logPath}`);
|
|
8871
|
+
if (fs17.existsSync(eventsPath)) lines.push(`Events: ${eventsPath}`);
|
|
9070
8872
|
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
8873
|
}
|
|
9072
8874
|
}
|
|
@@ -9079,7 +8881,7 @@ Extensions denied: ${status.capabilityCeiling.denyExtensions ? "yes" : "no"} (so
|
|
|
9079
8881
|
};
|
|
9080
8882
|
}
|
|
9081
8883
|
try {
|
|
9082
|
-
const raw =
|
|
8884
|
+
const raw = fs17.readFileSync(resultPath, "utf-8");
|
|
9083
8885
|
const data = JSON.parse(raw);
|
|
9084
8886
|
if (params.view === "transcript") {
|
|
9085
8887
|
try {
|
|
@@ -9172,16 +8974,16 @@ function applyForceTopLevelAsyncOverride(params, depth, forceTopLevelAsync) {
|
|
|
9172
8974
|
|
|
9173
8975
|
// src/runs/shared/worktree-cleanup-apply.ts
|
|
9174
8976
|
import { createHash as createHash5 } from "node:crypto";
|
|
9175
|
-
import { spawnSync as
|
|
9176
|
-
import * as
|
|
9177
|
-
import * as
|
|
8977
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
8978
|
+
import * as fs19 from "node:fs";
|
|
8979
|
+
import * as path17 from "node:path";
|
|
9178
8980
|
|
|
9179
8981
|
// src/runs/shared/worktree-cleanup-plan.ts
|
|
9180
8982
|
import { createHash as createHash4, randomUUID as randomUUID4 } from "node:crypto";
|
|
9181
|
-
import { spawnSync
|
|
9182
|
-
import * as
|
|
8983
|
+
import { spawnSync } from "node:child_process";
|
|
8984
|
+
import * as fs18 from "node:fs";
|
|
9183
8985
|
import * as os2 from "node:os";
|
|
9184
|
-
import * as
|
|
8986
|
+
import * as path16 from "node:path";
|
|
9185
8987
|
var WORKTREE_CLEANUP_PLAN_VERSION = 1;
|
|
9186
8988
|
var WORKTREE_CLEANUP_PLAN_TTL_MS = 30 * 60 * 1e3;
|
|
9187
8989
|
var MAX_DISCOVERED_HANDOFFS = 256;
|
|
@@ -9189,7 +8991,7 @@ var MAX_PLAN_ENTRIES = 512;
|
|
|
9189
8991
|
var MAX_METADATA_FILE_BYTES = 2 * 1024 * 1024;
|
|
9190
8992
|
function runGit(cwd, args) {
|
|
9191
8993
|
try {
|
|
9192
|
-
const result2 =
|
|
8994
|
+
const result2 = spawnSync("git", ["-C", cwd, ...args], { encoding: "utf-8", windowsHide: true });
|
|
9193
8995
|
return {
|
|
9194
8996
|
stdout: result2.stdout ?? "",
|
|
9195
8997
|
stderr: result2.stderr ?? "",
|
|
@@ -9213,7 +9015,7 @@ function runGitChecked(cwd, args) {
|
|
|
9213
9015
|
if (result2.status !== 0) throw new Error(gitFailure(result2, `git -C ${cwd} ${args.join(" ")}`));
|
|
9214
9016
|
return result2.stdout.trim();
|
|
9215
9017
|
}
|
|
9216
|
-
function realpathExisting(candidate, nativeRealpath =
|
|
9018
|
+
function realpathExisting(candidate, nativeRealpath = fs18.realpathSync.native, fallbackRealpath = fs18.realpathSync) {
|
|
9217
9019
|
try {
|
|
9218
9020
|
return nativeRealpath(candidate);
|
|
9219
9021
|
} catch {
|
|
@@ -9221,29 +9023,29 @@ function realpathExisting(candidate, nativeRealpath = fs19.realpathSync.native,
|
|
|
9221
9023
|
}
|
|
9222
9024
|
}
|
|
9223
9025
|
function comparablePath(candidate) {
|
|
9224
|
-
let current =
|
|
9026
|
+
let current = path16.resolve(candidate);
|
|
9225
9027
|
const missingSegments = [];
|
|
9226
9028
|
let canonical = current;
|
|
9227
9029
|
while (true) {
|
|
9228
9030
|
try {
|
|
9229
|
-
canonical =
|
|
9031
|
+
canonical = path16.join(realpathExisting(current), ...missingSegments.reverse());
|
|
9230
9032
|
break;
|
|
9231
9033
|
} catch {
|
|
9232
|
-
const parent =
|
|
9034
|
+
const parent = path16.dirname(current);
|
|
9233
9035
|
if (parent === current) break;
|
|
9234
|
-
missingSegments.push(
|
|
9036
|
+
missingSegments.push(path16.basename(current));
|
|
9235
9037
|
current = parent;
|
|
9236
9038
|
}
|
|
9237
9039
|
}
|
|
9238
|
-
const normalized =
|
|
9040
|
+
const normalized = path16.normalize(canonical);
|
|
9239
9041
|
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
9240
9042
|
}
|
|
9241
9043
|
function samePath(left, right) {
|
|
9242
9044
|
if (comparablePath(left) === comparablePath(right)) return true;
|
|
9243
9045
|
if (process.platform !== "win32") return false;
|
|
9244
9046
|
try {
|
|
9245
|
-
const leftStat =
|
|
9246
|
-
const rightStat =
|
|
9047
|
+
const leftStat = fs18.statSync(left, { bigint: true });
|
|
9048
|
+
const rightStat = fs18.statSync(right, { bigint: true });
|
|
9247
9049
|
return leftStat.dev !== 0n && leftStat.ino !== 0n && leftStat.dev === rightStat.dev && leftStat.ino === rightStat.ino;
|
|
9248
9050
|
} catch {
|
|
9249
9051
|
return false;
|
|
@@ -9253,15 +9055,15 @@ function resolveExistingPath(candidate) {
|
|
|
9253
9055
|
try {
|
|
9254
9056
|
return realpathExisting(candidate);
|
|
9255
9057
|
} catch {
|
|
9256
|
-
return
|
|
9058
|
+
return path16.resolve(candidate);
|
|
9257
9059
|
}
|
|
9258
9060
|
}
|
|
9259
9061
|
function pathInside(root, candidate, strict = false) {
|
|
9260
9062
|
const normalizedRoot = comparablePath(root);
|
|
9261
9063
|
const normalizedCandidate = comparablePath(candidate);
|
|
9262
9064
|
if (!strict && normalizedRoot === normalizedCandidate) return true;
|
|
9263
|
-
const relative3 =
|
|
9264
|
-
return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(`..${
|
|
9065
|
+
const relative3 = path16.relative(normalizedRoot, normalizedCandidate);
|
|
9066
|
+
return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(`..${path16.sep}`) && !path16.isAbsolute(relative3);
|
|
9265
9067
|
}
|
|
9266
9068
|
function sha2562(value) {
|
|
9267
9069
|
return createHash4("sha256").update(value, "utf-8").digest("hex");
|
|
@@ -9273,7 +9075,7 @@ function validatePlanId(planId) {
|
|
|
9273
9075
|
return planId;
|
|
9274
9076
|
}
|
|
9275
9077
|
function resolveCleanupRepoRoot(repo) {
|
|
9276
|
-
const requested =
|
|
9078
|
+
const requested = path16.resolve(repo);
|
|
9277
9079
|
const toplevel = runGitChecked(requested, ["rev-parse", "--show-toplevel"]);
|
|
9278
9080
|
return realpathExisting(toplevel);
|
|
9279
9081
|
}
|
|
@@ -9281,17 +9083,17 @@ function resolveCleanupBaseDir(repoRoot, configuredBaseDir) {
|
|
|
9281
9083
|
const raw = configuredBaseDir ?? process.env.PI_SUBAGENTS_WORKTREE_DIR;
|
|
9282
9084
|
let dedicatedRoot;
|
|
9283
9085
|
if (raw === void 0 || configuredBaseDir === void 0 && !raw.trim()) {
|
|
9284
|
-
dedicatedRoot =
|
|
9086
|
+
dedicatedRoot = path16.join(path16.dirname(repoRoot), "worktrees");
|
|
9285
9087
|
} else {
|
|
9286
9088
|
const trimmed = raw.trim();
|
|
9287
9089
|
if (!trimmed) throw new Error("worktree base directory cannot be empty");
|
|
9288
|
-
const expanded = trimmed.startsWith("~/") ?
|
|
9289
|
-
dedicatedRoot =
|
|
9090
|
+
const expanded = trimmed.startsWith("~/") ? path16.join(os2.homedir(), trimmed.slice(2)) : trimmed;
|
|
9091
|
+
dedicatedRoot = path16.isAbsolute(expanded) ? expanded : path16.resolve(repoRoot, expanded);
|
|
9290
9092
|
}
|
|
9291
|
-
return
|
|
9093
|
+
return path16.resolve(path16.join(dedicatedRoot, path16.basename(repoRoot)));
|
|
9292
9094
|
}
|
|
9293
9095
|
function cleanupContainmentInvalid(repoRoot, projectDir) {
|
|
9294
|
-
const extensionsDir =
|
|
9096
|
+
const extensionsDir = path16.join(getAgentDir(), "extensions");
|
|
9295
9097
|
return samePath(projectDir, repoRoot) || pathInside(repoRoot, projectDir, true) || pathInside(extensionsDir, projectDir);
|
|
9296
9098
|
}
|
|
9297
9099
|
function parseGitWorktreeList(raw) {
|
|
@@ -9330,14 +9132,14 @@ function isRegisteredCleanupWorktree(repoRoot, candidate) {
|
|
|
9330
9132
|
return listGitWorktrees(repoRoot).some((tree) => samePath(tree.path, candidate));
|
|
9331
9133
|
}
|
|
9332
9134
|
function readJsonFile(filePath) {
|
|
9333
|
-
return JSON.parse(
|
|
9135
|
+
return JSON.parse(fs18.readFileSync(filePath, "utf-8"));
|
|
9334
9136
|
}
|
|
9335
9137
|
function isRecord(value) {
|
|
9336
9138
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
9337
9139
|
}
|
|
9338
9140
|
function readManifest(manifestPath) {
|
|
9339
9141
|
try {
|
|
9340
|
-
const stat =
|
|
9142
|
+
const stat = fs18.lstatSync(manifestPath);
|
|
9341
9143
|
if (stat.isSymbolicLink() || !stat.isFile()) return { error: `parallel handoff manifest is not a regular file: ${manifestPath}` };
|
|
9342
9144
|
if (stat.size > MAX_METADATA_FILE_BYTES) return { error: `parallel handoff manifest exceeds the ${MAX_METADATA_FILE_BYTES}-byte limit: ${manifestPath}` };
|
|
9343
9145
|
const parsed = readParallelHandoffManifest(manifestPath);
|
|
@@ -9355,22 +9157,22 @@ function discoverHandoffPaths(repoRoot, input) {
|
|
|
9355
9157
|
const paths = [];
|
|
9356
9158
|
const warnings = [];
|
|
9357
9159
|
const add = (candidate) => {
|
|
9358
|
-
const resolved =
|
|
9160
|
+
const resolved = path16.isAbsolute(candidate) ? path16.resolve(candidate) : path16.resolve(repoRoot, candidate);
|
|
9359
9161
|
if (!paths.some((existing) => comparablePath(existing) === comparablePath(resolved))) paths.push(resolved);
|
|
9360
9162
|
};
|
|
9361
9163
|
if (input.handoffPath) add(input.handoffPath);
|
|
9362
9164
|
for (const candidate of input.handoffPaths ?? []) add(candidate);
|
|
9363
|
-
const artifactsDir =
|
|
9364
|
-
const projectHandoffPath =
|
|
9365
|
-
if (
|
|
9366
|
-
const handoffsDir =
|
|
9165
|
+
const artifactsDir = path16.join(repoRoot, PROJECT_SUBAGENTS_RELATIVE_DIR, "artifacts");
|
|
9166
|
+
const projectHandoffPath = path16.join(artifactsDir, "handoff.json");
|
|
9167
|
+
if (fs18.existsSync(projectHandoffPath)) add(projectHandoffPath);
|
|
9168
|
+
const handoffsDir = path16.join(artifactsDir, "handoffs");
|
|
9367
9169
|
try {
|
|
9368
|
-
const stat =
|
|
9170
|
+
const stat = fs18.lstatSync(handoffsDir);
|
|
9369
9171
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
9370
9172
|
warnings.push(`ignored non-directory handoff metadata root: ${handoffsDir}`);
|
|
9371
9173
|
} else {
|
|
9372
|
-
const entries =
|
|
9373
|
-
for (const entry of entries) add(
|
|
9174
|
+
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);
|
|
9175
|
+
for (const entry of entries) add(path16.join(handoffsDir, entry));
|
|
9374
9176
|
if (entries.length === MAX_DISCOVERED_HANDOFFS) warnings.push(`handoff metadata discovery capped at ${MAX_DISCOVERED_HANDOFFS} files`);
|
|
9375
9177
|
}
|
|
9376
9178
|
} catch (error) {
|
|
@@ -9380,9 +9182,9 @@ function discoverHandoffPaths(repoRoot, input) {
|
|
|
9380
9182
|
return { paths: paths.sort((left, right) => comparablePath(left).localeCompare(comparablePath(right))), warnings: [...new Set(warnings)].sort() };
|
|
9381
9183
|
}
|
|
9382
9184
|
function readStatusBesideManifest(manifestPath) {
|
|
9383
|
-
const statusPath =
|
|
9185
|
+
const statusPath = path16.join(path16.dirname(manifestPath), "status.json");
|
|
9384
9186
|
try {
|
|
9385
|
-
const stat =
|
|
9187
|
+
const stat = fs18.lstatSync(statusPath);
|
|
9386
9188
|
if (stat.isSymbolicLink() || !stat.isFile()) return { statusPath, error: `async status is not a regular file: ${statusPath}` };
|
|
9387
9189
|
if (stat.size > MAX_METADATA_FILE_BYTES) return { statusPath, error: `async status exceeds the ${MAX_METADATA_FILE_BYTES}-byte limit: ${statusPath}` };
|
|
9388
9190
|
const parsed = readJsonFile(statusPath);
|
|
@@ -9443,7 +9245,7 @@ function loadMetadata(input, repoRoot) {
|
|
|
9443
9245
|
continue;
|
|
9444
9246
|
}
|
|
9445
9247
|
const manifestRoot = resolveExistingPath(manifestPath);
|
|
9446
|
-
if (manifestRoot !== manifestPath &&
|
|
9248
|
+
if (manifestRoot !== manifestPath && fs18.lstatSync(manifestPath).isSymbolicLink()) {
|
|
9447
9249
|
warnings.push(`ignored symlinked handoff manifest: ${manifestPath}`);
|
|
9448
9250
|
continue;
|
|
9449
9251
|
}
|
|
@@ -9452,9 +9254,9 @@ function loadMetadata(input, repoRoot) {
|
|
|
9452
9254
|
return { records, paths: discovered.paths, warnings: [...new Set(warnings)].sort() };
|
|
9453
9255
|
}
|
|
9454
9256
|
function inspectPath(candidate) {
|
|
9455
|
-
const resolved =
|
|
9257
|
+
const resolved = path16.resolve(candidate);
|
|
9456
9258
|
try {
|
|
9457
|
-
const stat =
|
|
9259
|
+
const stat = fs18.lstatSync(resolved);
|
|
9458
9260
|
if (stat.isSymbolicLink()) return { resolved, missing: false, symlink: true, directory: false };
|
|
9459
9261
|
return { resolved, realpath: realpathExisting(resolved), missing: false, symlink: false, directory: stat.isDirectory() };
|
|
9460
9262
|
} catch (error) {
|
|
@@ -9464,7 +9266,7 @@ function inspectPath(candidate) {
|
|
|
9464
9266
|
}
|
|
9465
9267
|
}
|
|
9466
9268
|
function metadataPath(manifestPath, candidate) {
|
|
9467
|
-
return
|
|
9269
|
+
return path16.isAbsolute(candidate) ? path16.resolve(candidate) : path16.resolve(path16.dirname(manifestPath), candidate);
|
|
9468
9270
|
}
|
|
9469
9271
|
function metadataRecordPath(record2) {
|
|
9470
9272
|
return metadataPath(record2.manifestPath, record2.task.path);
|
|
@@ -9505,7 +9307,7 @@ function inspectRunState(record2, now, foregroundRunOwnership) {
|
|
|
9505
9307
|
}
|
|
9506
9308
|
}
|
|
9507
9309
|
try {
|
|
9508
|
-
const markerAge = activeRunMarkerAgeMs(
|
|
9310
|
+
const markerAge = activeRunMarkerAgeMs(path16.dirname(record2.statusPath), now);
|
|
9509
9311
|
if (markerAge !== void 0) {
|
|
9510
9312
|
if (markerAge <= DEFAULT_STALE_TERMINAL_ACTIVE_MARKER_MS) return { kind: "active", reason: "owning run still has a recent active marker" };
|
|
9511
9313
|
return { kind: "stale", reason: `terminal active marker is stale (${Math.floor(markerAge / 1e3)}s old)` };
|
|
@@ -9566,7 +9368,7 @@ function isPatchCaptured(record2, worktreePath, baseCommit) {
|
|
|
9566
9368
|
if (inspection.missing || !inspection.realpath) return { error: "captured handoff patch is missing" };
|
|
9567
9369
|
let patchStat;
|
|
9568
9370
|
try {
|
|
9569
|
-
patchStat =
|
|
9371
|
+
patchStat = fs18.statSync(patchPath);
|
|
9570
9372
|
} catch (error) {
|
|
9571
9373
|
return { error: `captured patch cannot be inspected: ${error instanceof Error ? error.message : String(error)}` };
|
|
9572
9374
|
}
|
|
@@ -9579,7 +9381,7 @@ function isPatchCaptured(record2, worktreePath, baseCommit) {
|
|
|
9579
9381
|
}
|
|
9580
9382
|
function buildManagedEntry(input) {
|
|
9581
9383
|
const { repoRoot, baseDir, targetHead, rootPath, git, record: record2, foregroundRunOwnership } = input;
|
|
9582
|
-
const gitWorktreePath =
|
|
9384
|
+
const gitWorktreePath = path16.resolve(git.path);
|
|
9583
9385
|
const metadataWorktreePath = metadataRecordPath(record2);
|
|
9584
9386
|
const worktreePath = resolveExistingPath(metadataWorktreePath);
|
|
9585
9387
|
const entry = buildEntryBase({ path: worktreePath, branch: git.branch ?? "", source: "both", git, metadata: record2, targetHead });
|
|
@@ -9596,13 +9398,13 @@ function buildManagedEntry(input) {
|
|
|
9596
9398
|
if (pathInspection.missing) return blockedEntry(entry, "stale", "unknown", "worktree path is missing from disk");
|
|
9597
9399
|
if (pathInspection.symlink) return blockedEntry(entry, "unknown", "unknown", "worktree path is a symlink; cleanup requires a real directory");
|
|
9598
9400
|
if (!pathInspection.directory || !pathInspection.realpath) return blockedEntry(entry, "unknown", "unknown", "worktree path is not a directory");
|
|
9599
|
-
const extensionsDir =
|
|
9401
|
+
const extensionsDir = path16.join(getAgentDir(), "extensions");
|
|
9600
9402
|
if (pathInside(extensionsDir, pathInspection.realpath)) return blockedEntry(entry, "ineligible", "keep", `worktree real path is inside Pi extensions directory '${extensionsDir}'`);
|
|
9601
9403
|
if (input.containmentInvalid || !pathInside(baseDir, pathInspection.realpath, true)) return blockedEntry(entry, "ineligible", "keep", `worktree real path is outside configured base directory '${baseDir}'`);
|
|
9602
9404
|
const recordedBaseDir = record2.task.recordedBaseDir;
|
|
9603
|
-
if (typeof recordedBaseDir !== "string" || !
|
|
9604
|
-
const creationRelative =
|
|
9605
|
-
if (!creationRelative || creationRelative === ".." || creationRelative.startsWith(`..${
|
|
9405
|
+
if (typeof recordedBaseDir !== "string" || !path16.isAbsolute(recordedBaseDir)) return blockedEntry(entry, "unknown", "keep", "handoff metadata has no creation-time base directory proof");
|
|
9406
|
+
const creationRelative = path16.relative(recordedBaseDir, pathInspection.realpath);
|
|
9407
|
+
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
9408
|
if (rootPath === comparablePath(pathInspection.realpath)) return blockedEntry(entry, "ineligible", "keep", "worktree is the repository root");
|
|
9607
9409
|
if (git.locked) return blockedEntry(entry, "ineligible", "keep", "Git worktree is locked");
|
|
9608
9410
|
if (!git.branch) return blockedEntry(entry, "unknown", "unknown", "detached worktrees have no metadata-recorded branch");
|
|
@@ -9781,7 +9583,7 @@ function buildWorktreeCleanupPlan(input) {
|
|
|
9781
9583
|
};
|
|
9782
9584
|
}
|
|
9783
9585
|
function worktreeCleanupPlanPath(repoRoot, planId) {
|
|
9784
|
-
return
|
|
9586
|
+
return path16.join(repoRoot, PROJECT_SUBAGENTS_RELATIVE_DIR, "cleanup-plans", `${validatePlanId(planId)}.json`);
|
|
9785
9587
|
}
|
|
9786
9588
|
function createWorktreeCleanupPlan(input) {
|
|
9787
9589
|
const plan = buildWorktreeCleanupPlan(input);
|
|
@@ -9829,13 +9631,13 @@ function formatWorktreeCleanupPlan(created) {
|
|
|
9829
9631
|
|
|
9830
9632
|
// src/runs/shared/worktree-cleanup-apply.ts
|
|
9831
9633
|
function assertContainedRealPath(repoRoot, candidate, create = false) {
|
|
9832
|
-
const relative3 =
|
|
9833
|
-
if (!relative3 || relative3 === ".." || relative3.startsWith(`..${
|
|
9634
|
+
const relative3 = path17.relative(repoRoot, candidate);
|
|
9635
|
+
if (!relative3 || relative3 === ".." || relative3.startsWith(`..${path17.sep}`) || path17.isAbsolute(relative3)) throw new Error("Cleanup evidence must be inside the selected repository.");
|
|
9834
9636
|
let current = repoRoot;
|
|
9835
|
-
for (const segment of relative3.split(
|
|
9836
|
-
current =
|
|
9837
|
-
if (!
|
|
9838
|
-
const stat =
|
|
9637
|
+
for (const segment of relative3.split(path17.sep)) {
|
|
9638
|
+
current = path17.join(current, segment);
|
|
9639
|
+
if (!fs19.existsSync(current) && create) fs19.mkdirSync(current, { mode: 448 });
|
|
9640
|
+
const stat = fs19.lstatSync(current);
|
|
9839
9641
|
if (stat.isSymbolicLink()) throw new Error(`Cleanup evidence cannot use a symlink: ${current}`);
|
|
9840
9642
|
}
|
|
9841
9643
|
}
|
|
@@ -9843,14 +9645,14 @@ function loadReviewedCleanupPlan(repo, planId, now = Date.now()) {
|
|
|
9843
9645
|
const repoRoot = resolveCleanupRepoRoot(repo);
|
|
9844
9646
|
const file = worktreeCleanupPlanPath(repoRoot, planId);
|
|
9845
9647
|
assertContainedRealPath(repoRoot, file);
|
|
9846
|
-
const stat =
|
|
9648
|
+
const stat = fs19.statSync(file);
|
|
9847
9649
|
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" &&
|
|
9650
|
+
const plan = JSON.parse(fs19.readFileSync(file, "utf-8"));
|
|
9651
|
+
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.");
|
|
9652
|
+
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
9653
|
const paths = /* @__PURE__ */ new Set();
|
|
9852
9654
|
for (const entry of plan.entries) {
|
|
9853
|
-
if (!entry || typeof entry.path !== "string" || !
|
|
9655
|
+
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
9656
|
paths.add(entry.path);
|
|
9855
9657
|
}
|
|
9856
9658
|
const hash = createHash5("sha256").update(JSON.stringify(worktreeCleanupContentPayload(plan))).digest("hex");
|
|
@@ -9860,7 +9662,7 @@ function loadReviewedCleanupPlan(repo, planId, now = Date.now()) {
|
|
|
9860
9662
|
function recordRemoval(entry) {
|
|
9861
9663
|
const manifest = readParallelHandoffManifest(entry.handoffPath);
|
|
9862
9664
|
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(
|
|
9665
|
+
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
9666
|
if (tasks.length !== 1) throw new Error("Handoff task no longer identifies exactly one removed worktree.");
|
|
9865
9667
|
if (tasks[0].worktreeRemoved) return;
|
|
9866
9668
|
tasks[0].worktreeRemoved = true;
|
|
@@ -9875,20 +9677,20 @@ async function applyReviewedCleanupPlan(input) {
|
|
|
9875
9677
|
return withWorktreeTransaction(() => withRepositoryWorktreeLock(input.repo, async () => {
|
|
9876
9678
|
const plan = loadReviewedCleanupPlan(input.repo, input.planId);
|
|
9877
9679
|
const claimPath = worktreeCleanupPlanPath(plan.repoRoot, plan.planId).replace(/\.json$/, ".claim");
|
|
9878
|
-
const receiptPath =
|
|
9879
|
-
assertContainedRealPath(plan.repoRoot,
|
|
9680
|
+
const receiptPath = path17.join(claimPath, "receipt.json");
|
|
9681
|
+
assertContainedRealPath(plan.repoRoot, path17.dirname(claimPath));
|
|
9880
9682
|
try {
|
|
9881
|
-
|
|
9683
|
+
fs19.mkdirSync(claimPath, { mode: 448 });
|
|
9882
9684
|
} catch (error) {
|
|
9883
9685
|
if (error.code !== "EEXIST") throw error;
|
|
9884
9686
|
assertContainedRealPath(plan.repoRoot, receiptPath);
|
|
9885
|
-
const receipt2 = JSON.parse(
|
|
9687
|
+
const receipt2 = JSON.parse(fs19.readFileSync(receiptPath, "utf-8"));
|
|
9886
9688
|
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
9689
|
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((
|
|
9690
|
+
await new Promise((resolve9) => setImmediate(resolve9));
|
|
9889
9691
|
input.signal?.throwIfAborted();
|
|
9890
9692
|
try {
|
|
9891
|
-
|
|
9693
|
+
fs19.lstatSync(entry.path);
|
|
9892
9694
|
continue;
|
|
9893
9695
|
} catch (error2) {
|
|
9894
9696
|
if (error2.code !== "ENOENT") throw error2;
|
|
@@ -9910,7 +9712,7 @@ async function applyReviewedCleanupPlan(input) {
|
|
|
9910
9712
|
try {
|
|
9911
9713
|
const selected = input.select?.(plan);
|
|
9912
9714
|
for (const entry of plan.entries.filter((item) => item.decision === "remove")) {
|
|
9913
|
-
await new Promise((
|
|
9715
|
+
await new Promise((resolve9) => setImmediate(resolve9));
|
|
9914
9716
|
input.signal?.throwIfAborted();
|
|
9915
9717
|
if (selected && !selected.has(entry.path)) {
|
|
9916
9718
|
receipt.entries.push({ path: entry.path, branch: entry.branch, state: "kept", reason: "outside the current retention excess" });
|
|
@@ -9920,7 +9722,7 @@ async function applyReviewedCleanupPlan(input) {
|
|
|
9920
9722
|
withHandoffWriteLock(entry.handoffPath, () => {
|
|
9921
9723
|
const manifest = readParallelHandoffManifest(entry.handoffPath);
|
|
9922
9724
|
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:
|
|
9725
|
+
const fresh = buildWorktreeCleanupPlan({ repo: plan.repoRoot, candidatePaths: [entry.path], handoffPaths: plan.metadataPaths, worktreeBaseDir: path17.dirname(plan.baseDirs[0]), foregroundRunOwnership: input.foregroundRunOwnership });
|
|
9924
9726
|
const candidate = fresh.entries.find((item) => item.path === entry.path);
|
|
9925
9727
|
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
9728
|
receipt.entries.push({ path: entry.path, branch: entry.branch, state: "kept", reason: candidate?.reasons.join("; ") || "reviewed ownership or Git facts changed" });
|
|
@@ -9930,7 +9732,7 @@ async function applyReviewedCleanupPlan(input) {
|
|
|
9930
9732
|
const journalEntry = { path: entry.path, branch: entry.branch, state: "removing", reason: "removal prepared; outcome not yet recorded" };
|
|
9931
9733
|
receipt.entries.push(journalEntry);
|
|
9932
9734
|
writePrivateAtomicJson(receiptPath, receipt);
|
|
9933
|
-
const removed =
|
|
9735
|
+
const removed = spawnSync2("git", ["-C", plan.repoRoot, "worktree", "remove", "--", entry.path], { encoding: "utf-8", windowsHide: true });
|
|
9934
9736
|
if (removed.status !== 0) throw new Error(removed.error?.message || removed.stderr.trim() || "git worktree remove failed");
|
|
9935
9737
|
journalEntry.state = "removed";
|
|
9936
9738
|
journalEntry.reason = "worktree removed; local branch retained";
|
|
@@ -10018,13 +9820,13 @@ function inheritedRunFanoutBudget(deps) {
|
|
|
10018
9820
|
return deps.childRuntime?.runFanoutBudget;
|
|
10019
9821
|
}
|
|
10020
9822
|
function resolveRequestedCwd(runtimeCwd, requestedCwd) {
|
|
10021
|
-
return requestedCwd ?
|
|
9823
|
+
return requestedCwd ? path18.resolve(runtimeCwd, requestedCwd) : runtimeCwd;
|
|
10022
9824
|
}
|
|
10023
9825
|
function readWorkflowScriptFile(requestedPath, requestedCwd, runtimeCwd) {
|
|
10024
|
-
const scriptPath =
|
|
9826
|
+
const scriptPath = path18.resolve(resolveRequestedCwd(runtimeCwd, requestedCwd), requestedPath);
|
|
10025
9827
|
let script;
|
|
10026
9828
|
try {
|
|
10027
|
-
script =
|
|
9829
|
+
script = fs20.readFileSync(scriptPath, "utf8");
|
|
10028
9830
|
} catch (error) {
|
|
10029
9831
|
return { error: `Failed to read workflow script '${scriptPath}': ${error instanceof Error ? error.message : String(error)}` };
|
|
10030
9832
|
}
|
|
@@ -10098,7 +9900,7 @@ function nestedResolutionScopeForExecutor(deps) {
|
|
|
10098
9900
|
};
|
|
10099
9901
|
}
|
|
10100
9902
|
function trustedSessionRoots(parentSessionFile, deps) {
|
|
10101
|
-
const roots = deps.config.defaultSessionDir ? [
|
|
9903
|
+
const roots = deps.config.defaultSessionDir ? [path18.resolve(deps.expandTilde(deps.config.defaultSessionDir))] : [];
|
|
10102
9904
|
if (parentSessionFile) for (const sessionFile of [parentSessionFile, ...inheritedSessionIds(parentSessionFile)]) roots.push(deps.getSubagentSessionRoot(sessionFile));
|
|
10103
9905
|
return [...new Set(roots)];
|
|
10104
9906
|
}
|
|
@@ -10428,9 +10230,9 @@ function resolveForegroundResumeTarget(params, state, options = {}) {
|
|
|
10428
10230
|
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
10231
|
const child = run.children[index];
|
|
10430
10232
|
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 (!
|
|
10233
|
+
if (path18.extname(child.sessionFile) !== ".jsonl") throw new Error(`Foreground run '${run.runId}' child ${index} session file must be a .jsonl file: ${child.sessionFile}`);
|
|
10234
|
+
const sessionFile = path18.resolve(child.sessionFile);
|
|
10235
|
+
if (!fs20.existsSync(sessionFile)) throw new Error(`Foreground run '${run.runId}' child ${index} session file does not exist: ${child.sessionFile}`);
|
|
10434
10236
|
return {
|
|
10435
10237
|
runId: run.runId,
|
|
10436
10238
|
mode: run.mode,
|
|
@@ -10481,7 +10283,7 @@ function resolveResumeTarget(params, state, options = {}) {
|
|
|
10481
10283
|
foregroundError = error;
|
|
10482
10284
|
}
|
|
10483
10285
|
try {
|
|
10484
|
-
const asyncParams = options.exactOnly && requested && !params.dir ? { ...params, dir:
|
|
10286
|
+
const asyncParams = options.exactOnly && requested && !params.dir ? { ...params, dir: path18.join(DIRS.async, requested) } : params;
|
|
10485
10287
|
asyncTarget = {
|
|
10486
10288
|
source: "async",
|
|
10487
10289
|
...resolveAsyncResumeTarget(asyncParams, {}, compactOptional({
|
|
@@ -10517,7 +10319,7 @@ function resolveResumeTarget(params, state, options = {}) {
|
|
|
10517
10319
|
function getAsyncInterruptTarget(state, runId, location, options = {}) {
|
|
10518
10320
|
if (location?.asyncDir) {
|
|
10519
10321
|
return {
|
|
10520
|
-
asyncId: location.resolvedId ?? runId ??
|
|
10322
|
+
asyncId: location.resolvedId ?? runId ?? path18.basename(location.asyncDir),
|
|
10521
10323
|
asyncDir: location.asyncDir
|
|
10522
10324
|
};
|
|
10523
10325
|
}
|
|
@@ -10566,7 +10368,7 @@ function persistAsyncWorkflowControlEvent(input) {
|
|
|
10566
10368
|
} : {}
|
|
10567
10369
|
};
|
|
10568
10370
|
try {
|
|
10569
|
-
|
|
10371
|
+
fs20.appendFileSync(path18.join(input.job.asyncDir, "events.jsonl"), `${JSON.stringify(record2)}
|
|
10570
10372
|
`, "utf-8");
|
|
10571
10373
|
} catch (error) {
|
|
10572
10374
|
if (!isStorageCapacityError(error)) throw error;
|
|
@@ -10886,25 +10688,25 @@ function nestedRunAgent(run) {
|
|
|
10886
10688
|
return run.agent ?? run.agents?.[0] ?? (run.steps?.length === 1 ? run.steps[0]?.agent : void 0);
|
|
10887
10689
|
}
|
|
10888
10690
|
function pathWithin(base, candidate) {
|
|
10889
|
-
const resolvedBase =
|
|
10890
|
-
const resolvedCandidate =
|
|
10891
|
-
return resolvedCandidate === resolvedBase || resolvedCandidate.startsWith(`${resolvedBase}${
|
|
10691
|
+
const resolvedBase = path18.resolve(base);
|
|
10692
|
+
const resolvedCandidate = path18.resolve(candidate);
|
|
10693
|
+
return resolvedCandidate === resolvedBase || resolvedCandidate.startsWith(`${resolvedBase}${path18.sep}`);
|
|
10892
10694
|
}
|
|
10893
10695
|
function validateNestedSessionFile(run, trustedSessionRoots2, resolveSource, visited = []) {
|
|
10894
10696
|
const sessionFile = nestedRunSessionFile(run);
|
|
10895
10697
|
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 =
|
|
10698
|
+
if (path18.extname(sessionFile) !== ".jsonl") throw new Error(`Nested run '${run.id}' session file must be a .jsonl file: ${sessionFile}`);
|
|
10699
|
+
const resolved = path18.resolve(sessionFile);
|
|
10700
|
+
if (!path18.isAbsolute(sessionFile)) throw new Error(`Nested run '${run.id}' session file must be absolute: ${sessionFile}`);
|
|
10701
|
+
if (!fs20.existsSync(resolved)) throw new Error(`Nested run '${run.id}' session file does not exist: ${sessionFile}`);
|
|
10702
|
+
const stat = fs20.lstatSync(resolved);
|
|
10901
10703
|
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) =>
|
|
10704
|
+
const realSessionFile = fs20.realpathSync(resolved);
|
|
10705
|
+
const trustedRoots = trustedSessionRoots2.filter((root) => fs20.existsSync(root)).map((root) => fs20.realpathSync(root));
|
|
10904
10706
|
if (!trustedRoots.some((root) => pathWithin(root, realSessionFile))) {
|
|
10905
10707
|
throw new Error(`Nested run '${run.id}' session file is outside trusted nested session roots: ${sessionFile}`);
|
|
10906
10708
|
}
|
|
10907
|
-
if (!realSessionFile.split(
|
|
10709
|
+
if (!realSessionFile.split(path18.sep).includes(run.id)) {
|
|
10908
10710
|
const sourceId = run.revivedFrom;
|
|
10909
10711
|
const chain = [...visited, run.id];
|
|
10910
10712
|
const source = sourceId && chain.length <= 8 && !chain.includes(sourceId) ? resolveSource(sourceId) : void 0;
|
|
@@ -10945,7 +10747,7 @@ function resolveNestedResumeTarget(match, trustedSessionRoots2, resolveSource) {
|
|
|
10945
10747
|
state,
|
|
10946
10748
|
agent,
|
|
10947
10749
|
index: 0,
|
|
10948
|
-
cwd: asyncDir ?
|
|
10750
|
+
cwd: asyncDir ? path18.dirname(asyncDir) : void 0,
|
|
10949
10751
|
sessionFile: validateNestedSessionFile(run, trustedSessionRoots2, resolveSource),
|
|
10950
10752
|
...run.capabilityCeiling ? { capabilityCeiling: run.capabilityCeiling } : {},
|
|
10951
10753
|
...recoveryDescriptor ? { recoveryDescriptor } : {}
|
|
@@ -10992,7 +10794,7 @@ async function waitForNestedControlResult(target, requestId, ignoredFiles, timeo
|
|
|
10992
10794
|
while (Date.now() < deadline) {
|
|
10993
10795
|
const result2 = findNestedControlResult(target.match.route, requestId, target.match.run.id, ignoredFiles);
|
|
10994
10796
|
if (result2) return result2;
|
|
10995
|
-
await new Promise((
|
|
10797
|
+
await new Promise((resolve9) => setTimeout(resolve9, 50));
|
|
10996
10798
|
}
|
|
10997
10799
|
return void 0;
|
|
10998
10800
|
}
|
|
@@ -11013,7 +10815,7 @@ function directNestedAsyncInterrupt(target) {
|
|
|
11013
10815
|
const run = target.match.run;
|
|
11014
10816
|
const asyncDir = resolveNestedAsyncDir(target.match.rootRunId, run);
|
|
11015
10817
|
if (!asyncDir) return void 0;
|
|
11016
|
-
const status = reconcileAsyncRun(asyncDir, { resultsDir:
|
|
10818
|
+
const status = reconcileAsyncRun(asyncDir, { resultsDir: path18.join(DIRS.results, "nested", target.match.rootRunId) }).status;
|
|
11017
10819
|
const pid = typeof status?.pid === "number" && status.pid > 0 ? status.pid : run.pid;
|
|
11018
10820
|
if (!status || status.state !== "running" || typeof pid !== "number" || pid <= 0) return void 0;
|
|
11019
10821
|
try {
|
|
@@ -11028,7 +10830,7 @@ async function directNestedAsyncSteer(input) {
|
|
|
11028
10830
|
const run = input.target.match.run;
|
|
11029
10831
|
const asyncDir = resolveNestedAsyncDir(input.target.match.rootRunId, run);
|
|
11030
10832
|
if (!asyncDir) return void 0;
|
|
11031
|
-
const status = reconcileAsyncRun(asyncDir, { resultsDir:
|
|
10833
|
+
const status = reconcileAsyncRun(asyncDir, { resultsDir: path18.join(DIRS.results, "nested", input.target.match.rootRunId) }).status;
|
|
11032
10834
|
if (!status || status.state !== "running" && status.state !== "queued") return void 0;
|
|
11033
10835
|
const steps = status.steps ?? [];
|
|
11034
10836
|
if (input.index !== void 0) {
|
|
@@ -11137,10 +10939,10 @@ async function resumeExternalJobFollowUp(input) {
|
|
|
11137
10939
|
const runId = externalJobFollowUpRunId(requestDigest);
|
|
11138
10940
|
const nestedRoute = inheritedNestedRoute(input.deps);
|
|
11139
10941
|
const runRoots = nestedRoute ? nestedRunScope(nestedRoute.rootRunId) : { asyncDirRoot: DIRS.async, resultsDir: DIRS.results };
|
|
11140
|
-
const asyncDir =
|
|
10942
|
+
const asyncDir = path18.join(runRoots.asyncDirRoot, runId);
|
|
11141
10943
|
const currentSessionId = input.deps.state.currentSessionId;
|
|
11142
10944
|
if (!currentSessionId) return { content: [{ type: "text", text: "External-job follow-up requires an active parent session." }], isError: true, details: { mode: "management", results: [] } };
|
|
11143
|
-
if (
|
|
10945
|
+
if (fs20.existsSync(asyncDir) || fs20.existsSync(resultFilePath(runRoots.resultsDir, runId))) {
|
|
11144
10946
|
return externalJobFollowUpStarted({ sourceRunId: input.target.runId, runId, asyncDir, duplicate: true, interactive: input.ctx.hasUI });
|
|
11145
10947
|
}
|
|
11146
10948
|
const depthState = checkSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime);
|
|
@@ -11341,7 +11143,7 @@ async function resumeAsyncRun(input) {
|
|
|
11341
11143
|
inheritGlobalContext: recoveryDescriptor.inheritGlobalContext,
|
|
11342
11144
|
inheritSkills: recoveryDescriptor.inheritSkills,
|
|
11343
11145
|
source: "project",
|
|
11344
|
-
filePath: recoveryDescriptor.agentFilePath ??
|
|
11146
|
+
filePath: recoveryDescriptor.agentFilePath ?? path18.join(getProjectSubagentsDir(recoveryDescriptor.cwd), "recovery-agent"),
|
|
11345
11147
|
...discovered.maxThinking ? { maxThinking: discovered.maxThinking } : {}
|
|
11346
11148
|
} : void 0);
|
|
11347
11149
|
if (!baseAgentConfig) {
|
|
@@ -11362,7 +11164,7 @@ async function resumeAsyncRun(input) {
|
|
|
11362
11164
|
if (target.source === "async" && target.managedWorktree && target.asyncDir) {
|
|
11363
11165
|
try {
|
|
11364
11166
|
const protectedCwd = await protectRetainedWorktreeForResume(parallelHandoffPath(target.asyncDir), target.runId, target.index, input.signal);
|
|
11365
|
-
if (
|
|
11167
|
+
if (path18.resolve(protectedCwd) !== path18.resolve(effectiveCwd)) throw new Error("Retained worktree cwd changed during resume admission.");
|
|
11366
11168
|
} catch (error) {
|
|
11367
11169
|
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
|
|
11368
11170
|
}
|
|
@@ -11415,7 +11217,7 @@ async function resumeAsyncRun(input) {
|
|
|
11415
11217
|
limit: resolveMaxActiveAsyncRunsPerSession(input.deps.config.maxActiveAsyncRunsPerSession),
|
|
11416
11218
|
runId: runId2,
|
|
11417
11219
|
kind: "runner",
|
|
11418
|
-
asyncDir:
|
|
11220
|
+
asyncDir: path18.join(DIRS.async, runId2)
|
|
11419
11221
|
}, { liveWorkflowRunIds: new Set(input.deps.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(input.deps.config.capacity?.abandonedSlotReleaseAfterMs) }) : void 0;
|
|
11420
11222
|
} catch (error) {
|
|
11421
11223
|
if (error instanceof ActiveAsyncCapacityError) return { content: [{ type: "text", text: error.message }], isError: true, details: { mode: "chain", results: [], activeAsyncCapacity: error.snapshot } };
|
|
@@ -11435,7 +11237,7 @@ async function resumeAsyncRun(input) {
|
|
|
11435
11237
|
goal,
|
|
11436
11238
|
attachRoot: {
|
|
11437
11239
|
runId: target.runId,
|
|
11438
|
-
asyncDir: target.asyncDir ??
|
|
11240
|
+
asyncDir: target.asyncDir ?? path18.join(DIRS.async, target.runId),
|
|
11439
11241
|
resultPath: resolveAsyncRootResultPath(DIRS.results, target.runId),
|
|
11440
11242
|
index: target.index,
|
|
11441
11243
|
agent: target.agent,
|
|
@@ -11539,13 +11341,13 @@ async function resumeAsyncRun(input) {
|
|
|
11539
11341
|
limit: resolveMaxActiveAsyncRunsPerSession(input.deps.config.maxActiveAsyncRunsPerSession),
|
|
11540
11342
|
sourceRunId: target.runId,
|
|
11541
11343
|
runId,
|
|
11542
|
-
asyncDir:
|
|
11344
|
+
asyncDir: path18.join(DIRS.async, runId)
|
|
11543
11345
|
}, { abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(input.deps.config.capacity?.abandonedSlotReleaseAfterMs) }) : acquireActiveAsyncCapacity({
|
|
11544
11346
|
sessionId: input.deps.state.currentSessionId,
|
|
11545
11347
|
limit: resolveMaxActiveAsyncRunsPerSession(input.deps.config.maxActiveAsyncRunsPerSession),
|
|
11546
11348
|
runId,
|
|
11547
11349
|
kind: "runner",
|
|
11548
|
-
asyncDir:
|
|
11350
|
+
asyncDir: path18.join(DIRS.async, runId)
|
|
11549
11351
|
}, { abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(input.deps.config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
11550
11352
|
} catch (error) {
|
|
11551
11353
|
if (error instanceof ActiveAsyncCapacityError) return { content: [{ type: "text", text: error.message }], isError: true, details: { mode: "single", results: [], activeAsyncCapacity: error.snapshot } };
|
|
@@ -11556,7 +11358,7 @@ async function resumeAsyncRun(input) {
|
|
|
11556
11358
|
const artifactsDir = recoveryDescriptor?.artifactsDir ?? getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
|
|
11557
11359
|
const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
11558
11360
|
const parentModel = input.parentModel;
|
|
11559
|
-
const revivalAsyncDir =
|
|
11361
|
+
const revivalAsyncDir = path18.join(DIRS.async, runId);
|
|
11560
11362
|
const revivalOutputBaseDir = resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId);
|
|
11561
11363
|
const inheritedOutput = foregroundContract?.output ?? recoveryDescriptor?.outputPath;
|
|
11562
11364
|
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;
|
|
@@ -11664,12 +11466,12 @@ async function resumeAsyncRun(input) {
|
|
|
11664
11466
|
return result2;
|
|
11665
11467
|
}
|
|
11666
11468
|
notifyLaunch();
|
|
11667
|
-
for (const brief of queuedBriefs)
|
|
11469
|
+
for (const brief of queuedBriefs) fs20.rmSync(brief.path, { force: true });
|
|
11668
11470
|
if (queuedBriefs.length > 0 && sourceAsyncDir) {
|
|
11669
11471
|
const sourceStatus = readStatus(sourceAsyncDir);
|
|
11670
11472
|
if (sourceStatus?.steering) {
|
|
11671
11473
|
for (const brief of queuedBriefs) updateSteeringTarget(sourceStatus.steering, brief.request.id, target.index, "delivered", Date.now());
|
|
11672
|
-
createCapacityResilientJsonWriter({ keepAlive: true }).write(
|
|
11474
|
+
createCapacityResilientJsonWriter({ keepAlive: true }).write(path18.join(sourceAsyncDir, "status.json"), sourceStatus);
|
|
11673
11475
|
}
|
|
11674
11476
|
}
|
|
11675
11477
|
const revivedId = result2.details.asyncId ?? runId;
|
|
@@ -11731,7 +11533,7 @@ function formatFailedSingleRunOutput(result2, displayOutput) {
|
|
|
11731
11533
|
cappedOutput = capInlineOutput(output, { artifactPath: result2.artifactPaths?.outputPath, savedPath: result2.savedOutputPath });
|
|
11732
11534
|
lines.push("", "Output:", cappedOutput);
|
|
11733
11535
|
}
|
|
11734
|
-
if (cappedOutput === output && result2.artifactPaths?.outputPath &&
|
|
11536
|
+
if (cappedOutput === output && result2.artifactPaths?.outputPath && fs20.existsSync(result2.artifactPaths.outputPath)) {
|
|
11735
11537
|
lines.push("", `Output artifact: ${result2.artifactPaths.outputPath}`);
|
|
11736
11538
|
}
|
|
11737
11539
|
return lines.join("\n");
|
|
@@ -11824,7 +11626,7 @@ async function maybeBuildForegroundIntercomReceipt(input) {
|
|
|
11824
11626
|
}
|
|
11825
11627
|
function diagnosticContextFromDiscovery(discovered, cwd, scope) {
|
|
11826
11628
|
if (discovered.cwd && discovered.scope && discovered.directories) return unknownAgentDiagnosticContext({ ...discovered, cwd: discovered.cwd, scope: discovered.scope, directories: discovered.directories });
|
|
11827
|
-
return unknownAgentDiagnosticContext(discoverAgents(
|
|
11629
|
+
return unknownAgentDiagnosticContext(discoverAgents(path18.resolve(cwd), scope));
|
|
11828
11630
|
}
|
|
11829
11631
|
function canonicalizeAgentName(name, agents, diagnostics, context) {
|
|
11830
11632
|
const resolved = resolveAgentName(name, agents);
|
|
@@ -11857,12 +11659,12 @@ function workflowValidationOptions(deps, params, runtimeCwd, parentModelProvider
|
|
|
11857
11659
|
};
|
|
11858
11660
|
}
|
|
11859
11661
|
function canonicalizeExecutionParams(params, agents, diagnostics, context) {
|
|
11860
|
-
const
|
|
11662
|
+
const resolve9 = (name, location) => {
|
|
11861
11663
|
const result2 = canonicalizeAgentName(name, agents, diagnostics, context);
|
|
11862
11664
|
return result2.error && location ? { error: `${result2.error} (${location})` } : result2;
|
|
11863
11665
|
};
|
|
11864
11666
|
if (params.agent) {
|
|
11865
|
-
const result2 =
|
|
11667
|
+
const result2 = resolve9(params.agent);
|
|
11866
11668
|
if (result2.error) return { error: result2.error };
|
|
11867
11669
|
params = omitUndefinedProperties({ ...params, agent: result2.name });
|
|
11868
11670
|
const agent = agents.find((candidate) => candidate.name === result2.name);
|
|
@@ -11881,7 +11683,7 @@ function canonicalizeExecutionParams(params, agents, diagnostics, context) {
|
|
|
11881
11683
|
const tasks = [];
|
|
11882
11684
|
for (let index = 0; index < params.tasks.length; index++) {
|
|
11883
11685
|
const task = params.tasks[index];
|
|
11884
|
-
const result2 =
|
|
11686
|
+
const result2 = resolve9(task.agent, `task ${index + 1}`);
|
|
11885
11687
|
if (result2.error) return { error: result2.error };
|
|
11886
11688
|
tasks.push({ ...task, agent: result2.name });
|
|
11887
11689
|
}
|
|
@@ -11895,7 +11697,7 @@ function canonicalizeExecutionParams(params, agents, diagnostics, context) {
|
|
|
11895
11697
|
const parallel = [];
|
|
11896
11698
|
for (let taskIndex = 0; taskIndex < step.parallel.length; taskIndex++) {
|
|
11897
11699
|
const task = step.parallel[taskIndex];
|
|
11898
|
-
const result2 =
|
|
11700
|
+
const result2 = resolve9(task.agent, `step ${index + 1}, task ${taskIndex + 1}`);
|
|
11899
11701
|
if (result2.error) return { error: result2.error };
|
|
11900
11702
|
parallel.push({ ...task, agent: result2.name });
|
|
11901
11703
|
}
|
|
@@ -11903,13 +11705,13 @@ function canonicalizeExecutionParams(params, agents, diagnostics, context) {
|
|
|
11903
11705
|
continue;
|
|
11904
11706
|
}
|
|
11905
11707
|
if (isDynamicParallelStep(step)) {
|
|
11906
|
-
const result2 =
|
|
11708
|
+
const result2 = resolve9(step.parallel.agent, `step ${index + 1}`);
|
|
11907
11709
|
if (result2.error) return { error: result2.error };
|
|
11908
11710
|
chain.push({ ...step, parallel: { ...step.parallel, agent: result2.name } });
|
|
11909
11711
|
continue;
|
|
11910
11712
|
}
|
|
11911
11713
|
if ("agent" in step && typeof step.agent === "string") {
|
|
11912
|
-
const result2 =
|
|
11714
|
+
const result2 = resolve9(step.agent, `step ${index + 1}`);
|
|
11913
11715
|
if (result2.error) return { error: result2.error };
|
|
11914
11716
|
chain.push({ ...step, agent: result2.name });
|
|
11915
11717
|
continue;
|
|
@@ -12776,7 +12578,7 @@ async function createSingleWorktreeSetup(cwd, runId, agent, setupHook, setupHook
|
|
|
12776
12578
|
}
|
|
12777
12579
|
}
|
|
12778
12580
|
function resolveConfiguredSingleRunOutputBaseDir(deps) {
|
|
12779
|
-
return deps.config.singleRunOutputBaseDir ?
|
|
12581
|
+
return deps.config.singleRunOutputBaseDir ? path18.resolve(deps.expandTilde(deps.config.singleRunOutputBaseDir)) : void 0;
|
|
12780
12582
|
}
|
|
12781
12583
|
function sanitizeRunPathSegment(value, maxBytes = 120) {
|
|
12782
12584
|
const sanitized = value.trim().replace(/[^A-Za-z0-9._-]+/g, "_").replace(/^_+|_+$/g, "");
|
|
@@ -12785,22 +12587,22 @@ function sanitizeRunPathSegment(value, maxBytes = 120) {
|
|
|
12785
12587
|
return sanitized.slice(0, maxBytes).replace(/_+$/, "") || "unknown";
|
|
12786
12588
|
}
|
|
12787
12589
|
function resolveSingleRunOutputBaseDir(deps, artifactsDir, runId) {
|
|
12788
|
-
return resolveConfiguredSingleRunOutputBaseDir(deps) ??
|
|
12590
|
+
return resolveConfiguredSingleRunOutputBaseDir(deps) ?? path18.join(artifactsDir, "outputs", sanitizeRunPathSegment(runId));
|
|
12789
12591
|
}
|
|
12790
12592
|
function suffixedOutputPath(outputPath, suffix) {
|
|
12791
|
-
const parsed =
|
|
12792
|
-
return
|
|
12593
|
+
const parsed = path18.parse(outputPath);
|
|
12594
|
+
return path18.join(parsed.dir, `${parsed.name}.${suffix}${parsed.ext || ".md"}`);
|
|
12793
12595
|
}
|
|
12794
12596
|
function workflowChildDefaultOutput(aggregateOutputPath, artifactsDir, workflowRunId, workflowKey) {
|
|
12795
12597
|
if (aggregateOutputPath) return suffixedOutputPath(aggregateOutputPath, workflowKey);
|
|
12796
|
-
return
|
|
12598
|
+
return path18.join(artifactsDir, "outputs", sanitizeRunPathSegment(workflowRunId), `${workflowKey}.md`);
|
|
12797
12599
|
}
|
|
12798
12600
|
function workflowHostCommandRunner(input) {
|
|
12799
12601
|
return async (key, params, signal) => {
|
|
12800
12602
|
const authorizationError = input.authorize?.(key, params);
|
|
12801
12603
|
if (authorizationError) throw new Error(authorizationError);
|
|
12802
|
-
const defaultOutputPath =
|
|
12803
|
-
const outputPath = params.output ?
|
|
12604
|
+
const defaultOutputPath = path18.join(input.artifactsDir, "outputs", sanitizeRunPathSegment(input.workflowRunId), "host", `${sanitizeRunPathSegment(key)}.log`);
|
|
12605
|
+
const outputPath = params.output ? path18.resolve(input.workflowCwd, params.output) : defaultOutputPath;
|
|
12804
12606
|
const claimPath = resolveWorkflowHostOutputClaimPath(outputPath);
|
|
12805
12607
|
const previous = input.claimedOutputPaths.get(claimPath);
|
|
12806
12608
|
if (previous) throw new Error(`runs.host('${key}') output path is already claimed by '${previous}': ${outputPath}.`);
|
|
@@ -12814,8 +12616,8 @@ function writeWorkflowAggregateOutput(outputPath, text, producedChildOutputPaths
|
|
|
12814
12616
|
if (!outputPath) return void 0;
|
|
12815
12617
|
try {
|
|
12816
12618
|
if (producedChildOutputPaths.has(resolveWorkflowHostOutputClaimPath(outputPath))) return void 0;
|
|
12817
|
-
|
|
12818
|
-
|
|
12619
|
+
fs20.mkdirSync(path18.dirname(outputPath), { recursive: true });
|
|
12620
|
+
fs20.writeFileSync(outputPath, text, "utf-8");
|
|
12819
12621
|
return void 0;
|
|
12820
12622
|
} catch (error) {
|
|
12821
12623
|
return `Output file error: ${outputPath}
|
|
@@ -12886,7 +12688,7 @@ function resolveWorkflowChildOutputPath(input) {
|
|
|
12886
12688
|
}
|
|
12887
12689
|
const output = rawOutput === true || rawOutput === "true" ? agentOutput : hasExplicitOutput ? rawOutput : input.aggregateOutputPath ? workflowChildDefaultOutput(input.aggregateOutputPath, input.artifactsDir, input.workflowRunId, input.key) : agentOutput;
|
|
12888
12690
|
return {
|
|
12889
|
-
path: resolveSingleOutputPath(output, input.ctxCwd, childCwd, input.configuredOutputBaseDir ??
|
|
12691
|
+
path: resolveSingleOutputPath(output, input.ctxCwd, childCwd, input.configuredOutputBaseDir ?? path18.join(input.artifactsDir, "outputs", sanitizeRunPathSegment(input.workflowRunId))),
|
|
12890
12692
|
inherited: !hasExplicitOutput && !input.aggregateOutputPath && agentOutput !== void 0
|
|
12891
12693
|
};
|
|
12892
12694
|
}
|
|
@@ -12951,7 +12753,7 @@ async function finalizeSingleWorktreeHandoff(input) {
|
|
|
12951
12753
|
try {
|
|
12952
12754
|
return await withWorktreeTransaction(() => {
|
|
12953
12755
|
admitted = true;
|
|
12954
|
-
const diffsDir =
|
|
12756
|
+
const diffsDir = path18.join(input.artifactsDir, "worktree-diffs", input.runId);
|
|
12955
12757
|
const diffs = diffWorktrees(input.worktreeSetup, [input.agent], diffsDir);
|
|
12956
12758
|
const diffSummary = formatWorktreeDiffSummary(diffs);
|
|
12957
12759
|
const manifestPath = parallelHandoffPath(input.artifactsDir, input.runId);
|
|
@@ -13197,7 +12999,7 @@ async function runSinglePath(data, deps) {
|
|
|
13197
12999
|
await cleanupSingleWorktree();
|
|
13198
13000
|
return { content: [{ type: "text", text: validationError }], isError: true, details: { mode: "single", results: [], parallelHandoff: pendingHandoff } };
|
|
13199
13001
|
}
|
|
13200
|
-
const structuredRuntime = params.outputSchema ? createStructuredOutputRuntime(params.outputSchema, artifactConfig.enabled ?
|
|
13002
|
+
const structuredRuntime = params.outputSchema ? createStructuredOutputRuntime(params.outputSchema, artifactConfig.enabled ? path18.join(artifactsDir, "structured-output", runId) : void 0, { acceptanceReport: resolveAcceptanceReportMode(params.acceptance) }) : void 0;
|
|
13201
13003
|
const reads = readsOverride !== void 0 ? readsOverride : agentConfig.defaultReads ?? false;
|
|
13202
13004
|
const readPaths = !foregroundMachine && Array.isArray(reads) ? resolveExistingReadPaths(reads, singleCwd) : [];
|
|
13203
13005
|
const readsInstruction = readPaths.length > 0 ? `[Read from: ${readPaths.join(", ")}]
|
|
@@ -13281,8 +13083,8 @@ async function runSinglePath(data, deps) {
|
|
|
13281
13083
|
};
|
|
13282
13084
|
let r;
|
|
13283
13085
|
let resolveDetachedWorkflowChild;
|
|
13284
|
-
const detachedWorkflowChild = params.workflowAwaitDetached === true ? new Promise((
|
|
13285
|
-
resolveDetachedWorkflowChild =
|
|
13086
|
+
const detachedWorkflowChild = params.workflowAwaitDetached === true ? new Promise((resolve9) => {
|
|
13087
|
+
resolveDetachedWorkflowChild = resolve9;
|
|
13286
13088
|
}) : void 0;
|
|
13287
13089
|
try {
|
|
13288
13090
|
const childSessionFile = sessionFileForTask(params.agent, 0, modelOverride, modelOverrideFromParent, modelOrigin);
|
|
@@ -13427,7 +13229,7 @@ async function runSinglePath(data, deps) {
|
|
|
13427
13229
|
}));
|
|
13428
13230
|
if (launched.detached && foregroundControl) {
|
|
13429
13231
|
foregroundControl.stopForRuntimeReplacement = () => {
|
|
13430
|
-
const sessionFile = childSessionFile &&
|
|
13232
|
+
const sessionFile = childSessionFile && fs20.existsSync(childSessionFile) ? childSessionFile : void 0;
|
|
13431
13233
|
const stopped = { ...launched, detached: void 0, stopped: true, exitCode: 1, error: RUNTIME_REPLACED_STOP_ERROR, finalOutput: RUNTIME_REPLACED_STOP_ERROR, sessionFile };
|
|
13432
13234
|
rememberForegroundRun(deps.state, { modelResponseAliases, runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, results: [stopped], params, effectiveOutput, effectiveOutputMode, extensionBindings: params.extensionBindings, requiredExtensions });
|
|
13433
13235
|
if (childSessionControls) childSessionControls.stop(RUNTIME_REPLACED_STOP_ERROR);
|
|
@@ -13595,8 +13397,8 @@ function bindMissionWorkflowChildAsyncLaunch(params, binding, asyncByDefault, as
|
|
|
13595
13397
|
const requestedAsync = params.async ?? asyncByDefault;
|
|
13596
13398
|
if (!binding || !requestedAsync || params.clarify === true) return params;
|
|
13597
13399
|
const id = asyncId.trim();
|
|
13598
|
-
if (!id ||
|
|
13599
|
-
writeMissionAsyncBinding(
|
|
13400
|
+
if (!id || path18.basename(id) !== id) throw new Error("workflow child async id must be a single path segment");
|
|
13401
|
+
writeMissionAsyncBinding(path18.join(nestedRootRunId ? nestedRunScope(nestedRootRunId).asyncDirRoot : DIRS.async, id), binding);
|
|
13600
13402
|
return { ...params, workflowChildAsyncId: id };
|
|
13601
13403
|
}
|
|
13602
13404
|
function workflowChildResult(key, result2, childParams = {}, resumeState, forcedTerminalOutcome) {
|
|
@@ -13737,7 +13539,7 @@ var WORKFLOW_STEP_STATES = /* @__PURE__ */ new Set(["pending", "running", "compl
|
|
|
13737
13539
|
function isSafeWorkflowChildRunId(value) {
|
|
13738
13540
|
if (typeof value !== "string") return false;
|
|
13739
13541
|
const runId = value.trim();
|
|
13740
|
-
return value === runId && Boolean(runId) && Buffer.byteLength(runId, "utf8") <= MAX_WORKFLOW_CHILD_RUN_ID_BYTES &&
|
|
13542
|
+
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
13543
|
}
|
|
13742
13544
|
function isMissingWorkflowReceiptDiagnostic(error, workflowRunId) {
|
|
13743
13545
|
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 +13548,7 @@ function missingWorkflowReceiptResumeHint(reference, state) {
|
|
|
13746
13548
|
try {
|
|
13747
13549
|
if (!state.currentSessionId) return void 0;
|
|
13748
13550
|
const workflowRunId = reference.workflowRunId.trim();
|
|
13749
|
-
const workflowStatus = readStatus(
|
|
13551
|
+
const workflowStatus = readStatus(path18.join(DIRS.async, workflowRunId));
|
|
13750
13552
|
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
13553
|
if (!step || typeof step !== "object" || Array.isArray(step)) return true;
|
|
13752
13554
|
const record2 = step;
|
|
@@ -13781,7 +13583,7 @@ function resolveWorkflowResume(reference, deps, parentSessionFile, index) {
|
|
|
13781
13583
|
if (status?.runId === runId && status.parentWorkflowRunId && status.workflowKey) {
|
|
13782
13584
|
const receiptPath = workflowReceiptPath(DIRS.async, status.parentWorkflowRunId);
|
|
13783
13585
|
try {
|
|
13784
|
-
|
|
13586
|
+
fs20.statSync(receiptPath);
|
|
13785
13587
|
} catch (error) {
|
|
13786
13588
|
if (error.code === "ENOENT") return { runId, runIds: [runId] };
|
|
13787
13589
|
throw error;
|
|
@@ -13840,11 +13642,11 @@ async function steerWorkflowChildByKey(input) {
|
|
|
13840
13642
|
});
|
|
13841
13643
|
if (!result2.details.steering?.targets.some((target) => target.reason === CHILD_SESSION_NOT_RUNNING_YET) || Date.now() >= deadline) return workflowSteerReceipt(input.key, result2);
|
|
13842
13644
|
}
|
|
13843
|
-
const workflowStatus = readStatus(
|
|
13645
|
+
const workflowStatus = readStatus(path18.join(asyncDirRoot, input.workflowRunId));
|
|
13844
13646
|
const step = workflowStatus?.steps?.find((candidate) => candidate.workflowKey === input.key);
|
|
13845
13647
|
const childRunId = step?.runId ?? input.resolveRunId?.();
|
|
13846
13648
|
if (childRunId) {
|
|
13847
|
-
const asyncDir =
|
|
13649
|
+
const asyncDir = path18.join(asyncDirRoot, childRunId);
|
|
13848
13650
|
const childStatus = reconcileAsyncRun(asyncDir).status;
|
|
13849
13651
|
if (childStatus && childStatus.state !== "running" && childStatus.state !== "queued" && (input.options.mode !== "follow_up" || !canQueueRetainedAsyncFollowUp(childStatus, input.options.index))) {
|
|
13850
13652
|
return { key: input.key, state: "missed", error: `Workflow child '${input.key}' is ${childStatus.state}.` };
|
|
@@ -13874,7 +13676,7 @@ async function steerWorkflowChildByKey(input) {
|
|
|
13874
13676
|
if (input.signal?.aborted || Date.now() >= deadline) {
|
|
13875
13677
|
return { key: input.key, state: "missed", error: `Workflow child '${input.key}' had no live steering route.` };
|
|
13876
13678
|
}
|
|
13877
|
-
await new Promise((
|
|
13679
|
+
await new Promise((resolve9) => setTimeout(resolve9, Math.min(10, Math.max(1, deadline - Date.now()))));
|
|
13878
13680
|
}
|
|
13879
13681
|
}
|
|
13880
13682
|
async function preflightWorkflowWorktrees(input) {
|
|
@@ -14320,7 +14122,7 @@ function createSubagentExecutor(deps) {
|
|
|
14320
14122
|
limit: resolveMaxActiveAsyncRunsPerSession(deps2.config.maxActiveAsyncRunsPerSession),
|
|
14321
14123
|
runId: workflowRunId,
|
|
14322
14124
|
kind: "workflow",
|
|
14323
|
-
asyncDir:
|
|
14125
|
+
asyncDir: path18.join(DIRS.async, workflowRunId)
|
|
14324
14126
|
}, { liveWorkflowRunIds: new Set(deps2.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps2.config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
14325
14127
|
} catch (error) {
|
|
14326
14128
|
if (error instanceof ActiveAsyncCapacityError) {
|
|
@@ -14366,11 +14168,11 @@ function createSubagentExecutor(deps) {
|
|
|
14366
14168
|
};
|
|
14367
14169
|
if (workflowRunId) {
|
|
14368
14170
|
const toolCallId = _id;
|
|
14369
|
-
const workflowSessionRoot = requestParams.sessionDir ?
|
|
14370
|
-
const asyncDir =
|
|
14171
|
+
const workflowSessionRoot = requestParams.sessionDir ? path18.resolve(deps2.expandTilde(requestParams.sessionDir)) : trustedSessionRoots(ctx.sessionManager.getSessionFile() ?? null, deps2)[0];
|
|
14172
|
+
const asyncDir = path18.join(DIRS.async, workflowRunId);
|
|
14371
14173
|
const resultPath = resultFilePath(DIRS.results, workflowRunId);
|
|
14372
|
-
const statusPath =
|
|
14373
|
-
const eventsPath =
|
|
14174
|
+
const statusPath = path18.join(asyncDir, "status.json");
|
|
14175
|
+
const eventsPath = path18.join(asyncDir, "events.jsonl");
|
|
14374
14176
|
const startedAt = Date.now();
|
|
14375
14177
|
const currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
14376
14178
|
const workflowCapabilityCeiling2 = intersectSubagentCapabilityCeilings(requestParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(currentSessionId));
|
|
@@ -14386,7 +14188,7 @@ function createSubagentExecutor(deps) {
|
|
|
14386
14188
|
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
14189
|
};
|
|
14388
14190
|
if (workflowReuseSource) {
|
|
14389
|
-
const reuseSourceDir =
|
|
14191
|
+
const reuseSourceDir = path18.join(DIRS.async, workflowReuseSource.runId);
|
|
14390
14192
|
let claim;
|
|
14391
14193
|
try {
|
|
14392
14194
|
claim = claimWorkflowResume(reuseSourceDir, { newRunId: workflowRunId, trigger: "reuse" });
|
|
@@ -14398,14 +14200,14 @@ function createSubagentExecutor(deps) {
|
|
|
14398
14200
|
return { content: [{ type: "text", text: claimedWorkflowRunRefusal(workflowReuseSource.runId, claim, requestParams.reuseFrom !== void 0) }], isError: true, details: { mode: "workflow", results: [] } };
|
|
14399
14201
|
}
|
|
14400
14202
|
pendingReuseClaimReleases.set(params, () => {
|
|
14401
|
-
if (!
|
|
14203
|
+
if (!fs20.existsSync(statusPath)) releaseWorkflowResumeClaim(reuseSourceDir, workflowRunId);
|
|
14402
14204
|
});
|
|
14403
14205
|
}
|
|
14404
14206
|
try {
|
|
14405
|
-
|
|
14207
|
+
fs20.mkdirSync(asyncDir, { recursive: true });
|
|
14406
14208
|
writeRunFanoutBudgetDescriptor(asyncDir, workflowFanoutBudget);
|
|
14407
14209
|
if (topLevelAsyncWorkflow && publicWorkflowLaunch) writeWorkflowLaunchRecord(asyncDir, workflowLaunchRecord(publicWorkflowLaunch, workflowCwd, workflowScriptSourceDigest));
|
|
14408
|
-
|
|
14210
|
+
fs20.mkdirSync(DIRS.results, { recursive: true });
|
|
14409
14211
|
} catch (error) {
|
|
14410
14212
|
return storageFailure(error);
|
|
14411
14213
|
}
|
|
@@ -14438,7 +14240,7 @@ function createSubagentExecutor(deps) {
|
|
|
14438
14240
|
};
|
|
14439
14241
|
const appendWorkflowEvent = (event) => {
|
|
14440
14242
|
try {
|
|
14441
|
-
|
|
14243
|
+
fs20.appendFileSync(eventsPath, `${JSON.stringify({ ts: Date.now(), runId: workflowRunId, ...event })}
|
|
14442
14244
|
`, "utf-8");
|
|
14443
14245
|
} catch (error) {
|
|
14444
14246
|
console.error(`Failed to append async workflow event '${eventsPath}':`, error);
|
|
@@ -14467,7 +14269,7 @@ function createSubagentExecutor(deps) {
|
|
|
14467
14269
|
const message = `Failed to write async workflow result ${resultPath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
14468
14270
|
console.error(message, error);
|
|
14469
14271
|
try {
|
|
14470
|
-
if (currentSessionId && fallbackResultPayloadPathForSessionRun(
|
|
14272
|
+
if (currentSessionId && fallbackResultPayloadPathForSessionRun(path18.dirname(resultPath), currentSessionId, workflowRunId)) return true;
|
|
14471
14273
|
} catch (pendingError) {
|
|
14472
14274
|
console.error(`Failed to verify pending async workflow result '${workflowRunId}':`, pendingError);
|
|
14473
14275
|
}
|
|
@@ -14575,8 +14377,8 @@ Parent action: ${STATUS_ACTION}`,
|
|
|
14575
14377
|
};
|
|
14576
14378
|
pendingResultPublication = void 0;
|
|
14577
14379
|
runPersistence.write(resultPath, payload);
|
|
14578
|
-
if (!resultPublished) pendingResultPublication = new Promise((
|
|
14579
|
-
settleResultPublication =
|
|
14380
|
+
if (!resultPublished) pendingResultPublication = new Promise((resolve9) => {
|
|
14381
|
+
settleResultPublication = resolve9;
|
|
14580
14382
|
});
|
|
14581
14383
|
return true;
|
|
14582
14384
|
} catch (error) {
|
|
@@ -14913,7 +14715,7 @@ Parent action: ${STATUS_ACTION}`,
|
|
|
14913
14715
|
const step = status.steps?.find((candidate) => candidate.workflowKey === key);
|
|
14914
14716
|
if (!step) return;
|
|
14915
14717
|
if (runId2) step.runId = runId2;
|
|
14916
|
-
step.async = runId2 !== void 0 &&
|
|
14718
|
+
step.async = runId2 !== void 0 && path18.basename(runId2) === runId2 && fs20.existsSync(path18.join(path18.dirname(asyncDir), runId2));
|
|
14917
14719
|
persist({ tolerateStatusWriteFailure: true });
|
|
14918
14720
|
};
|
|
14919
14721
|
try {
|
|
@@ -15014,7 +14816,7 @@ Parent action: ${COMPLETION_ACTION}` : content,
|
|
|
15014
14816
|
}
|
|
15015
14817
|
if (priorChild?.kind === "started") {
|
|
15016
14818
|
const priorRunId = priorChild.runId;
|
|
15017
|
-
const priorAsyncDir =
|
|
14819
|
+
const priorAsyncDir = path18.join(DIRS.async, priorRunId);
|
|
15018
14820
|
appendWorkflowChildJournal(asyncDir, { type: "start", key, fingerprint: journalFingerprint, runId: priorRunId });
|
|
15019
14821
|
recordReusedWorkflowChild(key, priorRunId);
|
|
15020
14822
|
const stopListener = stopAwaitedAsyncChildOnAbort(workflowSignal, deps2.state, priorRunId, priorAsyncDir, deps2.kill, true);
|
|
@@ -15236,7 +15038,7 @@ Parent action: ${COMPLETION_ACTION}` : content,
|
|
|
15236
15038
|
const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : void 0;
|
|
15237
15039
|
const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps2);
|
|
15238
15040
|
const workflowAggregateOutputPath = resolveSingleOutputPath(workflowOutput, ctx.cwd, workflowCwd, resolveSingleRunOutputBaseDir(deps2, workflowArtifactsDir, foregroundWorkflowRunId));
|
|
15239
|
-
const workflowFullResultPath =
|
|
15041
|
+
const workflowFullResultPath = path18.join(workflowArtifactsDir, `${sanitizeRunPathSegment(foregroundWorkflowRunId)}_workflow-result.md`);
|
|
15240
15042
|
const claimedOutputPaths = /* @__PURE__ */ new Map();
|
|
15241
15043
|
const childOutputOverrides = /* @__PURE__ */ new Map();
|
|
15242
15044
|
const childOutputClaimPaths = /* @__PURE__ */ new Map();
|
|
@@ -15507,7 +15309,7 @@ ${partial.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`]
|
|
|
15507
15309
|
if (paramsWithResolvedCwd.mode === "apply") {
|
|
15508
15310
|
try {
|
|
15509
15311
|
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 =
|
|
15312
|
+
const repo = path18.resolve(requestCwd, paramsWithResolvedCwd.repo);
|
|
15511
15313
|
const plan = loadReviewedCleanupPlan(repo, paramsWithResolvedCwd.planId);
|
|
15512
15314
|
const decision = resolveAuthorityDecision({ action: "discardWorktree", ...deps2.config.authorityPolicy === void 0 ? {} : { policy: deps2.config.authorityPolicy } });
|
|
15513
15315
|
if (decision === "forbid") throw new Error("Authority policy forbids worktree cleanup.");
|
|
@@ -15537,8 +15339,8 @@ Remove up to ${plan.entries.filter((entry) => entry.decision === "remove").lengt
|
|
|
15537
15339
|
}
|
|
15538
15340
|
try {
|
|
15539
15341
|
const created = createWorktreeCleanupPlan({
|
|
15540
|
-
repo: paramsWithResolvedCwd.repo?.trim() ?
|
|
15541
|
-
...paramsWithResolvedCwd.handoffPath ? { handoffPath:
|
|
15342
|
+
repo: paramsWithResolvedCwd.repo?.trim() ? path18.isAbsolute(paramsWithResolvedCwd.repo) ? paramsWithResolvedCwd.repo : path18.resolve(requestCwd, paramsWithResolvedCwd.repo) : requestCwd,
|
|
15343
|
+
...paramsWithResolvedCwd.handoffPath ? { handoffPath: path18.isAbsolute(paramsWithResolvedCwd.handoffPath) ? paramsWithResolvedCwd.handoffPath : path18.resolve(requestCwd, paramsWithResolvedCwd.handoffPath) } : {},
|
|
15542
15344
|
...deps2.config.worktreeBaseDir ? { worktreeBaseDir: deps2.config.worktreeBaseDir } : {},
|
|
15543
15345
|
foregroundRunOwnership: (runId2) => {
|
|
15544
15346
|
if (deps2.state.foregroundControls.has(runId2)) return "active";
|
|
@@ -15571,7 +15373,7 @@ ${paramsWithResolvedCwd.handoffPath}`);
|
|
|
15571
15373
|
}
|
|
15572
15374
|
if (!confirmed) return { content: [{ type: "text", text: "Worktree discard canceled; preserved worktrees were not changed." }], details: { mode: "management", results: [] } };
|
|
15573
15375
|
try {
|
|
15574
|
-
const manifestPath =
|
|
15376
|
+
const manifestPath = path18.isAbsolute(paramsWithResolvedCwd.handoffPath) ? paramsWithResolvedCwd.handoffPath : path18.resolve(requestCwd, paramsWithResolvedCwd.handoffPath);
|
|
15575
15377
|
const discarded = await withWorktreeTransaction(() => discardPreservedWorktrees(
|
|
15576
15378
|
manifestPath,
|
|
15577
15379
|
{ kind: decision === "confirm" ? "confirmed" : "policy", ...deps2.config.authorityPolicy ? { policy: deps2.config.authorityPolicy } : {} }
|
|
@@ -15589,7 +15391,7 @@ ${paramsWithResolvedCwd.handoffPath}`);
|
|
|
15589
15391
|
if (!laneId) return { content: [{ type: "text", text: `${action} requires laneId.` }], isError: true, details: { mode: "management", results: [] } };
|
|
15590
15392
|
const handoffPath = paramsWithResolvedCwd.handoffPath?.trim();
|
|
15591
15393
|
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 =
|
|
15394
|
+
const manifestPath = path18.isAbsolute(handoffPath) ? handoffPath : path18.resolve(requestCwd, handoffPath);
|
|
15593
15395
|
try {
|
|
15594
15396
|
if (action === "lane.status") {
|
|
15595
15397
|
let manifest;
|
|
@@ -15765,9 +15567,7 @@ Usage is not reset. Compaction keeps the same budget; a new parent session start
|
|
|
15765
15567
|
if (action === "guide") {
|
|
15766
15568
|
try {
|
|
15767
15569
|
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;
|
|
15570
|
+
const notice = paramsWithResolvedCwd.topic?.includes("/") ? disabledFeatureSectionNotice(disabledFeatures) : disabledFeatureNotice(disabledFeatures);
|
|
15771
15571
|
return {
|
|
15772
15572
|
content: [{ type: "text", text: notice ? `${notice}
|
|
15773
15573
|
|
|
@@ -15918,7 +15718,7 @@ ${item.text}` } : item)
|
|
|
15918
15718
|
if (paramsWithResolvedCwd.dir) {
|
|
15919
15719
|
try {
|
|
15920
15720
|
const location = resolveAsyncRunLocation(paramsWithResolvedCwd, DIRS.async, DIRS.results);
|
|
15921
|
-
const runId2 = location.resolvedId ?? targetRunId ??
|
|
15721
|
+
const runId2 = location.resolvedId ?? targetRunId ?? path18.basename(location.asyncDir ?? paramsWithResolvedCwd.dir);
|
|
15922
15722
|
const directoryStatus = location.asyncDir ? readStatus(location.asyncDir) : null;
|
|
15923
15723
|
if (directoryStatus?.mode === "workflow") {
|
|
15924
15724
|
return steerWorkflowRun({ state: deps2.state, runId: runId2, asyncDir: location.asyncDir, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
|
|
@@ -16051,7 +15851,7 @@ ${item.text}` } : item)
|
|
|
16051
15851
|
if (!stopChild) return { content: [{ type: "text", text: `Workflow ${workflowRunId} child stop is unavailable in this extension runtime.` }], isError: true, details: { mode: "management", results: [] } };
|
|
16052
15852
|
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
15853
|
try {
|
|
16054
|
-
|
|
15854
|
+
fs20.appendFileSync(path18.join(asyncJob.asyncDir, "events.jsonl"), `${JSON.stringify({
|
|
16055
15855
|
type: "subagent.child-status",
|
|
16056
15856
|
version: 1,
|
|
16057
15857
|
runId: workflowRunId,
|
|
@@ -16077,7 +15877,7 @@ ${item.text}` } : item)
|
|
|
16077
15877
|
if (paramsWithResolvedCwd.dir) {
|
|
16078
15878
|
try {
|
|
16079
15879
|
const location = resolveAsyncRunLocation(paramsWithResolvedCwd, DIRS.async, DIRS.results);
|
|
16080
|
-
const stopResult2 = stopAsyncRun(deps2.state, location.resolvedId ?? targetRunId ??
|
|
15880
|
+
const stopResult2 = stopAsyncRun(deps2.state, location.resolvedId ?? targetRunId ?? path18.basename(location.asyncDir ?? paramsWithResolvedCwd.dir), deps2.kill, location, paramsWithResolvedCwd.childId);
|
|
16081
15881
|
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
15882
|
} catch (error) {
|
|
16083
15883
|
const text = error instanceof Error ? error.message : String(error);
|
|
@@ -16325,7 +16125,7 @@ ${item.text}` } : item)
|
|
|
16325
16125
|
if (foregroundTimeout.error) return buildRequestedModeError(effectiveParams, foregroundTimeout.error);
|
|
16326
16126
|
const controlConfig = resolveControlConfig(deps2.config.control, effectiveParams.control);
|
|
16327
16127
|
const requestedWorkflowChildAsyncId = typeof effectiveParams.workflowChildAsyncId === "string" ? effectiveParams.workflowChildAsyncId.trim() : "";
|
|
16328
|
-
const asyncRunId = requestedWorkflowChildAsyncId &&
|
|
16128
|
+
const asyncRunId = requestedWorkflowChildAsyncId && path18.basename(requestedWorkflowChildAsyncId) === requestedWorkflowChildAsyncId ? requestedWorkflowChildAsyncId : randomUUID5();
|
|
16329
16129
|
const topLevelAsyncCapacityEligible = depth === 0 && !inheritedNestedRouteValue && !effectiveParams.workflowParentRunId;
|
|
16330
16130
|
const topLevelAsync = effectiveAsync && topLevelAsyncCapacityEligible;
|
|
16331
16131
|
let activeAsyncCapacity;
|
|
@@ -16337,7 +16137,7 @@ ${item.text}` } : item)
|
|
|
16337
16137
|
limit: activeLimit,
|
|
16338
16138
|
runId: asyncRunId,
|
|
16339
16139
|
kind: "runner",
|
|
16340
|
-
asyncDir:
|
|
16140
|
+
asyncDir: path18.join(DIRS.async, asyncRunId)
|
|
16341
16141
|
}, { liveWorkflowRunIds: new Set(deps2.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps2.config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
16342
16142
|
deps2.state.activeAsyncCapacity = getActiveAsyncCapacitySnapshot(requestSessionId, activeLimit, { liveWorkflowRunIds: new Set(deps2.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps2.config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
16343
16143
|
} catch (error) {
|
|
@@ -16373,13 +16173,13 @@ ${item.text}` } : item)
|
|
|
16373
16173
|
let sessionRoot;
|
|
16374
16174
|
sessionRunId = effectiveAsync ? asyncRunId : runId;
|
|
16375
16175
|
if (effectiveParams.sessionDir) {
|
|
16376
|
-
sessionRoot =
|
|
16176
|
+
sessionRoot = path18.join(path18.resolve(deps2.expandTilde(effectiveParams.sessionDir)), sessionRunId);
|
|
16377
16177
|
} else {
|
|
16378
|
-
const baseSessionRoot = deps2.config.defaultSessionDir ?
|
|
16379
|
-
sessionRoot =
|
|
16178
|
+
const baseSessionRoot = deps2.config.defaultSessionDir ? path18.resolve(deps2.expandTilde(deps2.config.defaultSessionDir)) : deps2.getSubagentSessionRoot(parentSessionFile);
|
|
16179
|
+
sessionRoot = path18.join(baseSessionRoot, sessionRunId);
|
|
16380
16180
|
}
|
|
16381
16181
|
try {
|
|
16382
|
-
|
|
16182
|
+
fs20.mkdirSync(sessionRoot, { recursive: true });
|
|
16383
16183
|
} catch (error) {
|
|
16384
16184
|
activeAsyncCapacity?.rollback();
|
|
16385
16185
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -16389,7 +16189,7 @@ ${item.text}` } : item)
|
|
|
16389
16189
|
contextPolicy.contextSummary
|
|
16390
16190
|
);
|
|
16391
16191
|
}
|
|
16392
|
-
const sessionDirForIndex = (idx) =>
|
|
16192
|
+
const sessionDirForIndex = (idx) => path18.join(sessionRoot, `run-${idx ?? 0}`);
|
|
16393
16193
|
const forkSessionFileForTask = (agentName, idx = 0) => {
|
|
16394
16194
|
if (!shouldForkAgent(contextPolicy, agentName)) return void 0;
|
|
16395
16195
|
return forkSessionFileForIndex(idx);
|
|
@@ -16399,8 +16199,8 @@ ${item.text}` } : item)
|
|
|
16399
16199
|
await prepareForkSessionForIndex(idx);
|
|
16400
16200
|
};
|
|
16401
16201
|
const thinkingOverrideForTask = () => delegatedThinkingOverride;
|
|
16402
|
-
const childSessionFileForTask = (agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) => forkSessionFileForTask(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) ??
|
|
16403
|
-
const childSessionFileForIndex = (idx) =>
|
|
16202
|
+
const childSessionFileForTask = (agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) => forkSessionFileForTask(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) ?? path18.join(sessionDirForIndex(idx), "session.jsonl");
|
|
16203
|
+
const childSessionFileForIndex = (idx) => path18.join(sessionDirForIndex(idx), "session.jsonl");
|
|
16404
16204
|
try {
|
|
16405
16205
|
assertLaunchableAgentsAllowedBeforeFork();
|
|
16406
16206
|
if (!(effectiveParams.clarify === true && ctx.hasUI) || deps2.config.forkContext?.mode === "pruned") {
|