pi-subagents 0.82.0 → 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 +46 -0
- package/README.md +1 -1
- package/bundle/agents.js +4 -4
- package/bundle/background-work.js +1 -1
- package/bundle/child-tool-plan.js +3 -3
- package/bundle/{chunk-7PCKZHR5.js → chunk-4BW55BTL.js} +6 -6
- package/bundle/{chunk-7IVSUMQQ.js → chunk-527E3D2J.js} +6 -6
- package/bundle/{chunk-NRQ75MGH.js → chunk-5ESXK62X.js} +40 -1076
- package/bundle/{chunk-ZQGSDHL6.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-ZLINIYTP.js → chunk-CDI3GMNV.js} +48 -32
- package/bundle/{chunk-GC4DSPMH.js → chunk-CGXWSNPA.js} +12 -18
- package/bundle/{chunk-SIJ4S3EN.js → chunk-CNLDYEH4.js} +12 -12
- package/bundle/{chunk-4SDU2BZX.js → chunk-CSSZRTKY.js} +2 -2
- package/bundle/{chunk-W2SZ22EM.js → chunk-CZTMUBUI.js} +10 -8
- package/bundle/{chunk-WVD4UEL4.js → chunk-HJJAVHFB.js} +28 -24
- package/bundle/{chunk-GJECHSAR.js → chunk-HLE22FSH.js} +2 -2
- package/bundle/{chunk-M2CTFYT3.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-B5EKXDHE.js → chunk-LDMXJIA6.js} +458 -657
- package/bundle/{chunk-43SCHIMD.js → chunk-MBDOYZ2V.js} +1 -1
- package/bundle/{chunk-XSWOTIA3.js → chunk-MHX22USZ.js} +1 -1
- package/bundle/{chunk-VH5OAWVG.js → chunk-OOFW75PV.js} +5 -5
- package/bundle/{chunk-RMQFMMAT.js → chunk-P2NCCTZP.js} +231 -10
- package/bundle/{chunk-NGGAFDL6.js → chunk-RZJN63GU.js} +12 -5
- package/bundle/{chunk-7YMXOYEU.js → chunk-TJQJ3MEA.js} +12 -11
- 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-MHZ4G23A.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/agents.md +6 -7
- 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/council-mode/SKILL.md +1 -4
- package/skills/pi-subagents/SKILL.md +130 -107
- package/skills/pi-subagents/references/orchestration.md +124 -0
- package/skills/pi-subagents/references/prompting-and-roles.md +96 -295
- 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 +5 -3
- package/src/extension/rpc.js +3 -1
- package/src/extension/subagent-guide.js +1 -2
- 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/async-status.d.ts +2 -0
- package/src/runs/background/async-status.js +1 -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/background/terminal-run-index.d.ts +1 -0
- package/src/runs/background/terminal-run-index.js +40 -1
- 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/tui/fleet.js +1 -0
- package/src/workflows/scripted-workflow.js +9 -1070
- package/workflow-script-worker.cjs +1067 -0
- package/skills/pi-subagents/references/constraints-and-recipes.md +0 -74
- package/skills/pi-subagents/references/execution-controls.md +0 -564
- package/skills/pi-subagents/references/management-authoring-rpc.md +0 -165
- package/skills/pi-subagents/references/multi-lane-orchestration.md +0 -61
- package/skills/pi-subagents/references/review-and-validation.md +0 -73
|
@@ -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,7 +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
|
|
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.`;
|
|
3991
3782
|
function readReplyWorkflowScript(sessionManager, toolCallId) {
|
|
3992
3783
|
const branch = sessionManager.getBranch();
|
|
3993
3784
|
for (let index = branch.length - 1; index >= 0; index--) {
|
|
@@ -4003,14 +3794,14 @@ function readReplyWorkflowScript(sessionManager, toolCallId) {
|
|
|
4003
3794
|
function scriptFromReply(content) {
|
|
4004
3795
|
const replyCalls = content.filter((block) => block.type === "toolCall" && block.name === "subagent" && (block.arguments?.workflow === true || block.arguments?.workflow === "true")).length;
|
|
4005
3796
|
if (replyCalls > 1) return { error: `This reply has ${replyCalls} subagent calls with workflow: true; a reply can carry only one. Pass other scripts as workflow file paths.` };
|
|
4006
|
-
if (!content.some((block) => block.type === "text")) return { error:
|
|
3797
|
+
if (!content.some((block) => block.type === "text")) return { error: `The assistant message that issued this call has no text in the session, so the reply's visible text, including any \`\`\`js workflow block, never reached it. ${SUMMARY_NOTE}` };
|
|
4007
3798
|
const text = content.flatMap((block) => block.type === "text" && typeof block.text === "string" ? [block.text] : []).join("\n");
|
|
4008
3799
|
const { tagged, plain, unclosed } = workflowBlocks(text);
|
|
4009
3800
|
if (unclosed) return { error: "The script's fenced block in this reply is not closed." };
|
|
4010
3801
|
const blocks = tagged.length > 0 ? tagged : plain.length === 1 ? plain : [];
|
|
4011
3802
|
if (blocks.length !== 1) {
|
|
4012
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".` : "";
|
|
4013
|
-
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}` };
|
|
4014
3805
|
}
|
|
4015
3806
|
const script = blocks[0];
|
|
4016
3807
|
if (!script.trim()) return { error: "The script's fenced block in this reply is empty." };
|
|
@@ -4037,14 +3828,14 @@ function workflowBlocks(text) {
|
|
|
4037
3828
|
}
|
|
4038
3829
|
|
|
4039
3830
|
// src/runs/foreground/execution.ts
|
|
4040
|
-
import { existsSync as
|
|
4041
|
-
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";
|
|
4042
3833
|
|
|
4043
3834
|
// src/shared/fork-session-cwd.ts
|
|
4044
|
-
import * as
|
|
4045
|
-
import * as
|
|
3835
|
+
import * as fs8 from "node:fs";
|
|
3836
|
+
import * as path7 from "node:path";
|
|
4046
3837
|
function alignForkedSessionCwd(sessionFile, cwd) {
|
|
4047
|
-
const lines =
|
|
3838
|
+
const lines = fs8.readFileSync(sessionFile, "utf-8").split("\n").filter((line) => line.trim().length > 0);
|
|
4048
3839
|
const entries = lines.map((line, index) => {
|
|
4049
3840
|
try {
|
|
4050
3841
|
return JSON.parse(line);
|
|
@@ -4055,16 +3846,16 @@ function alignForkedSessionCwd(sessionFile, cwd) {
|
|
|
4055
3846
|
});
|
|
4056
3847
|
const header = entries[0];
|
|
4057
3848
|
if (header?.type !== "session") throw new Error(`Forked session ${sessionFile} does not start with a session header.`);
|
|
4058
|
-
const effectiveCwd =
|
|
3849
|
+
const effectiveCwd = fs8.realpathSync.native(path7.resolve(cwd));
|
|
4059
3850
|
if (header.cwd === effectiveCwd) return;
|
|
4060
3851
|
header.cwd = effectiveCwd;
|
|
4061
|
-
|
|
3852
|
+
fs8.writeFileSync(sessionFile, `${entries.map((entry) => JSON.stringify(entry)).join("\n")}
|
|
4062
3853
|
`, "utf-8");
|
|
4063
3854
|
}
|
|
4064
3855
|
|
|
4065
3856
|
// src/shared/child-transcript.ts
|
|
4066
|
-
import * as
|
|
4067
|
-
import * as
|
|
3857
|
+
import * as fs9 from "node:fs";
|
|
3858
|
+
import * as path8 from "node:path";
|
|
4068
3859
|
var MAX_TOOL_PAYLOAD_BYTES = 32 * 1024;
|
|
4069
3860
|
var TOOL_PAYLOAD_TRUNCATION_MARKER = "\n\n\u2026 payload truncated";
|
|
4070
3861
|
function boundedPayload(value, maxBytes = MAX_TOOL_PAYLOAD_BYTES) {
|
|
@@ -4141,7 +3932,7 @@ function createChildTranscriptWriter(input) {
|
|
|
4141
3932
|
const markerBytes = Buffer.byteLength(marker, "utf-8");
|
|
4142
3933
|
if (bytesWritten + markerBytes > maxBytes) return false;
|
|
4143
3934
|
try {
|
|
4144
|
-
|
|
3935
|
+
fs9.appendFileSync(input.transcriptPath, marker, "utf-8");
|
|
4145
3936
|
bytesWritten += markerBytes;
|
|
4146
3937
|
return true;
|
|
4147
3938
|
} catch (error) {
|
|
@@ -4169,15 +3960,15 @@ function createChildTranscriptWriter(input) {
|
|
|
4169
3960
|
return;
|
|
4170
3961
|
}
|
|
4171
3962
|
try {
|
|
4172
|
-
|
|
3963
|
+
fs9.appendFileSync(input.transcriptPath, line, "utf-8");
|
|
4173
3964
|
bytesWritten += bytes;
|
|
4174
3965
|
} catch (error) {
|
|
4175
3966
|
writeError = `Failed to write child transcript '${input.transcriptPath}': ${errorMessage(error)}`;
|
|
4176
3967
|
}
|
|
4177
3968
|
};
|
|
4178
3969
|
try {
|
|
4179
|
-
|
|
4180
|
-
|
|
3970
|
+
fs9.mkdirSync(path8.dirname(input.transcriptPath), { recursive: true });
|
|
3971
|
+
fs9.writeFileSync(input.transcriptPath, "", "utf-8");
|
|
4181
3972
|
} catch (error) {
|
|
4182
3973
|
writeError = `Failed to initialize child transcript '${input.transcriptPath}': ${errorMessage(error)}`;
|
|
4183
3974
|
}
|
|
@@ -4299,7 +4090,7 @@ ${text}`;
|
|
|
4299
4090
|
}
|
|
4300
4091
|
|
|
4301
4092
|
// src/shared/jsonl-writer.ts
|
|
4302
|
-
import * as
|
|
4093
|
+
import * as fs10 from "node:fs";
|
|
4303
4094
|
var DEFAULT_MAX_JSONL_BYTES = 50 * 1024 * 1024;
|
|
4304
4095
|
function createJsonlWriter(filePath, source, deps = {}) {
|
|
4305
4096
|
if (!filePath) {
|
|
@@ -4310,7 +4101,7 @@ function createJsonlWriter(filePath, source, deps = {}) {
|
|
|
4310
4101
|
}
|
|
4311
4102
|
};
|
|
4312
4103
|
}
|
|
4313
|
-
const createWriteStream3 = deps.createWriteStream ?? ((targetPath) =>
|
|
4104
|
+
const createWriteStream3 = deps.createWriteStream ?? ((targetPath) => fs10.createWriteStream(targetPath, { flags: "a" }));
|
|
4314
4105
|
let stream;
|
|
4315
4106
|
try {
|
|
4316
4107
|
stream = createWriteStream3(filePath);
|
|
@@ -4360,15 +4151,15 @@ function createJsonlWriter(filePath, source, deps = {}) {
|
|
|
4360
4151
|
closed = true;
|
|
4361
4152
|
const current = stream;
|
|
4362
4153
|
stream = void 0;
|
|
4363
|
-
await new Promise((
|
|
4154
|
+
await new Promise((resolve9) => current.end(() => resolve9()));
|
|
4364
4155
|
}
|
|
4365
4156
|
};
|
|
4366
4157
|
}
|
|
4367
4158
|
|
|
4368
4159
|
// src/runs/shared/orca-progress-tabs.ts
|
|
4369
4160
|
import { spawn as spawn2 } from "node:child_process";
|
|
4370
|
-
import * as
|
|
4371
|
-
import * as
|
|
4161
|
+
import * as fs11 from "node:fs";
|
|
4162
|
+
import * as path9 from "node:path";
|
|
4372
4163
|
import { createHash, randomUUID as randomUUID3 } from "node:crypto";
|
|
4373
4164
|
var ORCA_CREATE_TIMEOUT_MS = 2e4;
|
|
4374
4165
|
var ORCA_KILL_GRACE_MS = 2e3;
|
|
@@ -4413,10 +4204,10 @@ var ORCA_CLEANUP_WATCHDOG_SCRIPT = [
|
|
|
4413
4204
|
].join("");
|
|
4414
4205
|
function executableFile(candidate) {
|
|
4415
4206
|
try {
|
|
4416
|
-
const stats =
|
|
4207
|
+
const stats = fs11.statSync(candidate);
|
|
4417
4208
|
if (!stats.isFile()) return false;
|
|
4418
4209
|
if (process.platform === "win32") return true;
|
|
4419
|
-
|
|
4210
|
+
fs11.accessSync(candidate, fs11.constants.X_OK);
|
|
4420
4211
|
return true;
|
|
4421
4212
|
} catch {
|
|
4422
4213
|
return false;
|
|
@@ -4430,9 +4221,9 @@ function executableNames(command, env) {
|
|
|
4430
4221
|
function resolveOrcaCommand(env = process.env) {
|
|
4431
4222
|
const override = env.PI_SUBAGENT_ORCA_BINARY?.trim();
|
|
4432
4223
|
if (override) return executableFile(override) ? override : void 0;
|
|
4433
|
-
for (const directory of (env.PATH ?? "").split(
|
|
4224
|
+
for (const directory of (env.PATH ?? "").split(path9.delimiter).filter(Boolean)) {
|
|
4434
4225
|
for (const name of executableNames("orca", env)) {
|
|
4435
|
-
const candidate =
|
|
4226
|
+
const candidate = path9.join(directory, name);
|
|
4436
4227
|
if (executableFile(candidate)) return candidate;
|
|
4437
4228
|
}
|
|
4438
4229
|
}
|
|
@@ -4469,15 +4260,15 @@ function viewerCommand(nodeExecutable, logPath, donePath) {
|
|
|
4469
4260
|
return invocation;
|
|
4470
4261
|
}
|
|
4471
4262
|
function progressRoot() {
|
|
4472
|
-
return
|
|
4263
|
+
return path9.join(TEMP_ROOT_DIR, "orca-progress");
|
|
4473
4264
|
}
|
|
4474
4265
|
function pruneStaleProgressFiles(root, now = Date.now()) {
|
|
4475
4266
|
try {
|
|
4476
|
-
for (const entry of
|
|
4267
|
+
for (const entry of fs11.readdirSync(root, { withFileTypes: true })) {
|
|
4477
4268
|
if (!entry.isFile() || !entry.name.endsWith(".log") && !entry.name.endsWith(".done") && !entry.name.endsWith(".ready") && !entry.name.endsWith(".pending")) continue;
|
|
4478
|
-
const file =
|
|
4269
|
+
const file = path9.join(root, entry.name);
|
|
4479
4270
|
try {
|
|
4480
|
-
if (now -
|
|
4271
|
+
if (now - fs11.statSync(file).mtimeMs > STALE_PROGRESS_MAX_AGE_MS) fs11.rmSync(file, { force: true });
|
|
4481
4272
|
} catch {
|
|
4482
4273
|
}
|
|
4483
4274
|
}
|
|
@@ -4490,9 +4281,9 @@ function safeSegment(value, fallback = "subagent") {
|
|
|
4490
4281
|
}
|
|
4491
4282
|
function observerManifestPath(cwd, stem) {
|
|
4492
4283
|
try {
|
|
4493
|
-
const dir =
|
|
4494
|
-
|
|
4495
|
-
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`);
|
|
4496
4287
|
} catch {
|
|
4497
4288
|
return void 0;
|
|
4498
4289
|
}
|
|
@@ -4500,7 +4291,7 @@ function observerManifestPath(cwd, stem) {
|
|
|
4500
4291
|
function writeObserverManifest(manifestPath, manifest) {
|
|
4501
4292
|
if (!manifestPath) return;
|
|
4502
4293
|
try {
|
|
4503
|
-
|
|
4294
|
+
fs11.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
4504
4295
|
`, { encoding: "utf-8", mode: 384 });
|
|
4505
4296
|
} catch {
|
|
4506
4297
|
}
|
|
@@ -4510,18 +4301,18 @@ function sleepSync(ms) {
|
|
|
4510
4301
|
Atomics.wait(signal, 0, 0, ms);
|
|
4511
4302
|
}
|
|
4512
4303
|
function resolveSequenceScope(cwd) {
|
|
4513
|
-
let current =
|
|
4304
|
+
let current = path9.resolve(cwd);
|
|
4514
4305
|
try {
|
|
4515
|
-
current =
|
|
4306
|
+
current = fs11.realpathSync(current);
|
|
4516
4307
|
} catch {
|
|
4517
4308
|
}
|
|
4518
4309
|
while (true) {
|
|
4519
4310
|
try {
|
|
4520
|
-
if (
|
|
4311
|
+
if (fs11.existsSync(path9.join(current, ".git"))) return current;
|
|
4521
4312
|
} catch {
|
|
4522
4313
|
}
|
|
4523
|
-
const parent =
|
|
4524
|
-
if (parent === current) return
|
|
4314
|
+
const parent = path9.dirname(current);
|
|
4315
|
+
if (parent === current) return path9.resolve(cwd);
|
|
4525
4316
|
current = parent;
|
|
4526
4317
|
}
|
|
4527
4318
|
}
|
|
@@ -4529,18 +4320,18 @@ function sequenceKey(cwd) {
|
|
|
4529
4320
|
return createHash("sha256").update(resolveSequenceScope(cwd)).digest("hex").slice(0, 20);
|
|
4530
4321
|
}
|
|
4531
4322
|
function withSequenceLock(root, key, fn) {
|
|
4532
|
-
const lockPath =
|
|
4323
|
+
const lockPath = path9.join(root, `counter-${key}.lock`);
|
|
4533
4324
|
let locked = false;
|
|
4534
4325
|
for (let attempt = 0; attempt < COUNTER_LOCK_RETRIES; attempt++) {
|
|
4535
4326
|
try {
|
|
4536
|
-
|
|
4327
|
+
fs11.mkdirSync(lockPath, { mode: 448 });
|
|
4537
4328
|
locked = true;
|
|
4538
4329
|
break;
|
|
4539
4330
|
} catch (error) {
|
|
4540
4331
|
if (error.code !== "EEXIST") break;
|
|
4541
4332
|
try {
|
|
4542
|
-
if (Date.now() -
|
|
4543
|
-
|
|
4333
|
+
if (Date.now() - fs11.statSync(lockPath).mtimeMs > COUNTER_LOCK_STALE_MS) {
|
|
4334
|
+
fs11.rmSync(lockPath, { recursive: true, force: true });
|
|
4544
4335
|
continue;
|
|
4545
4336
|
}
|
|
4546
4337
|
} catch {
|
|
@@ -4553,7 +4344,7 @@ function withSequenceLock(root, key, fn) {
|
|
|
4553
4344
|
return fn();
|
|
4554
4345
|
} finally {
|
|
4555
4346
|
try {
|
|
4556
|
-
|
|
4347
|
+
fs11.rmSync(lockPath, { recursive: true, force: true });
|
|
4557
4348
|
} catch {
|
|
4558
4349
|
}
|
|
4559
4350
|
}
|
|
@@ -4562,18 +4353,18 @@ function predecessorMarker(pathValue) {
|
|
|
4562
4353
|
if (!pathValue) return void 0;
|
|
4563
4354
|
const ready = pathValue.endsWith(".pending") ? pathValue.replace(/\.pending$/, ".ready") : pathValue;
|
|
4564
4355
|
const pending = ready.replace(/\.ready$/, ".pending");
|
|
4565
|
-
if (
|
|
4356
|
+
if (fs11.existsSync(ready) || fs11.existsSync(pending)) return pending;
|
|
4566
4357
|
return void 0;
|
|
4567
4358
|
}
|
|
4568
4359
|
function reserveTabSequence(root, cwd) {
|
|
4569
4360
|
const key = sequenceKey(cwd);
|
|
4570
|
-
const counterPath =
|
|
4571
|
-
const createDone =
|
|
4361
|
+
const counterPath = path9.join(root, `counter-${key}`);
|
|
4362
|
+
const createDone = path9.join(root, `create-${key}-${randomUUID3()}.pending`);
|
|
4572
4363
|
return withSequenceLock(root, key, () => {
|
|
4573
4364
|
let current = 0;
|
|
4574
4365
|
let previousCreateDone;
|
|
4575
4366
|
try {
|
|
4576
|
-
const raw =
|
|
4367
|
+
const raw = fs11.readFileSync(counterPath, "utf-8");
|
|
4577
4368
|
const [countLine, previousLine] = raw.split("\n");
|
|
4578
4369
|
const parsed = Number.parseInt(countLine ?? "", 10);
|
|
4579
4370
|
if (Number.isSafeInteger(parsed) && parsed >= 0) current = parsed;
|
|
@@ -4582,14 +4373,14 @@ function reserveTabSequence(root, cwd) {
|
|
|
4582
4373
|
}
|
|
4583
4374
|
const next = current + 1;
|
|
4584
4375
|
try {
|
|
4585
|
-
|
|
4586
|
-
|
|
4376
|
+
fs11.writeFileSync(createDone, "", { encoding: "utf-8", mode: 384 });
|
|
4377
|
+
fs11.writeFileSync(counterPath, `${next}
|
|
4587
4378
|
${createDone}
|
|
4588
4379
|
`, { encoding: "utf-8", mode: 384 });
|
|
4589
4380
|
return { sequence: next, previousCreateDone, createDone };
|
|
4590
4381
|
} catch {
|
|
4591
4382
|
try {
|
|
4592
|
-
|
|
4383
|
+
fs11.rmSync(createDone, { force: true });
|
|
4593
4384
|
} catch {
|
|
4594
4385
|
}
|
|
4595
4386
|
return void 0;
|
|
@@ -4611,8 +4402,8 @@ function scheduleCleanup(nodeExecutable, paths) {
|
|
|
4611
4402
|
}
|
|
4612
4403
|
function loadOrcaProgressTabsConfig() {
|
|
4613
4404
|
try {
|
|
4614
|
-
const configPath =
|
|
4615
|
-
const parsed = JSON.parse(
|
|
4405
|
+
const configPath = path9.join(getAgentDir(), "extensions", "subagent", "config.json");
|
|
4406
|
+
const parsed = JSON.parse(fs11.readFileSync(configPath, "utf-8"));
|
|
4616
4407
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
|
|
4617
4408
|
const value = parsed.orcaProgressTabs;
|
|
4618
4409
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
@@ -4631,7 +4422,7 @@ function createOrcaProgressTab(input) {
|
|
|
4631
4422
|
const nodeExecutable = resolveNodeExecutable();
|
|
4632
4423
|
const root = progressRoot();
|
|
4633
4424
|
try {
|
|
4634
|
-
|
|
4425
|
+
fs11.mkdirSync(root, { recursive: true, mode: 448 });
|
|
4635
4426
|
} catch {
|
|
4636
4427
|
return void 0;
|
|
4637
4428
|
}
|
|
@@ -4645,12 +4436,12 @@ function createOrcaProgressTab(input) {
|
|
|
4645
4436
|
const tabSequence = reservation.sequence;
|
|
4646
4437
|
const stem = `${runId}-${index}-${randomUUID3()}`;
|
|
4647
4438
|
const stepCount = typeof input.stepCount === "number" && Number.isInteger(input.stepCount) && input.stepCount > 0 ? input.stepCount : 1;
|
|
4648
|
-
const logPath =
|
|
4649
|
-
const donePath =
|
|
4439
|
+
const logPath = path9.join(root, `${stem}.log`);
|
|
4440
|
+
const donePath = path9.join(root, `${stem}.done`);
|
|
4650
4441
|
const title = `subagents \xB7 ${agent} \xB7 ${tabSequence}`;
|
|
4651
4442
|
const markCreateReady = () => {
|
|
4652
4443
|
try {
|
|
4653
|
-
|
|
4444
|
+
fs11.writeFileSync(reservation.createDone.replace(/\.pending$/, ".ready"), "");
|
|
4654
4445
|
} catch {
|
|
4655
4446
|
}
|
|
4656
4447
|
};
|
|
@@ -4662,36 +4453,36 @@ function createOrcaProgressTab(input) {
|
|
|
4662
4453
|
role: "run",
|
|
4663
4454
|
runId,
|
|
4664
4455
|
title,
|
|
4665
|
-
worktree:
|
|
4456
|
+
worktree: path9.resolve(cwd),
|
|
4666
4457
|
state: "opening",
|
|
4667
4458
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4668
4459
|
logPath
|
|
4669
4460
|
});
|
|
4670
4461
|
try {
|
|
4671
|
-
|
|
4462
|
+
fs11.writeFileSync(logPath, `pi-subagents / ${agent}
|
|
4672
4463
|
run ${runId} \xB7 ${stepCount === 1 ? "1 child" : `${stepCount} children`}
|
|
4673
4464
|
${"\u2500".repeat(48)}
|
|
4674
4465
|
`, { encoding: "utf-8", mode: 384 });
|
|
4675
|
-
|
|
4466
|
+
fs11.rmSync(donePath, { force: true });
|
|
4676
4467
|
} catch {
|
|
4677
4468
|
markCreateReady();
|
|
4678
4469
|
return void 0;
|
|
4679
4470
|
}
|
|
4680
4471
|
let available = true;
|
|
4681
4472
|
let truncated = false;
|
|
4682
|
-
let scheduledBytes =
|
|
4473
|
+
let scheduledBytes = fs11.statSync(logPath).size;
|
|
4683
4474
|
const progressByteLimit = MAX_MIRROR_BYTES - MIRROR_FOOTER_RESERVE_BYTES;
|
|
4684
|
-
const logStream =
|
|
4475
|
+
const logStream = fs11.createWriteStream(logPath, { flags: "a", mode: 384 });
|
|
4685
4476
|
const failObserver = () => {
|
|
4686
4477
|
if (!available) return;
|
|
4687
4478
|
available = false;
|
|
4688
4479
|
logStream.destroy();
|
|
4689
4480
|
try {
|
|
4690
|
-
|
|
4481
|
+
fs11.rmSync(logPath, { force: true });
|
|
4691
4482
|
} catch {
|
|
4692
4483
|
}
|
|
4693
4484
|
try {
|
|
4694
|
-
|
|
4485
|
+
fs11.rmSync(donePath, { force: true });
|
|
4695
4486
|
} catch {
|
|
4696
4487
|
}
|
|
4697
4488
|
};
|
|
@@ -4712,8 +4503,8 @@ ${"\u2500".repeat(48)}
|
|
|
4712
4503
|
};
|
|
4713
4504
|
let createSettled = false;
|
|
4714
4505
|
let resolveCreationSettled;
|
|
4715
|
-
const creationSettled = new Promise((
|
|
4716
|
-
resolveCreationSettled =
|
|
4506
|
+
const creationSettled = new Promise((resolve9) => {
|
|
4507
|
+
resolveCreationSettled = resolve9;
|
|
4717
4508
|
});
|
|
4718
4509
|
let cleanupPaths;
|
|
4719
4510
|
const scheduleDeferredCleanup = () => {
|
|
@@ -4735,7 +4526,7 @@ ${"\u2500".repeat(48)}
|
|
|
4735
4526
|
"terminal",
|
|
4736
4527
|
"create",
|
|
4737
4528
|
"--worktree",
|
|
4738
|
-
`path:${
|
|
4529
|
+
`path:${path9.resolve(cwd)}`,
|
|
4739
4530
|
"--title",
|
|
4740
4531
|
title,
|
|
4741
4532
|
"--command",
|
|
@@ -4803,7 +4594,7 @@ ${"\u2500".repeat(16)} child ${index2 + 1}/${count} \xB7 ${label} ${"\u2500".rep
|
|
|
4803
4594
|
let verifiedSessionFile;
|
|
4804
4595
|
if (sessionId && sessionFile) {
|
|
4805
4596
|
try {
|
|
4806
|
-
verifiedSessionFile =
|
|
4597
|
+
verifiedSessionFile = fs11.realpathSync(sessionFile);
|
|
4807
4598
|
} catch {
|
|
4808
4599
|
}
|
|
4809
4600
|
}
|
|
@@ -4818,18 +4609,18 @@ ${terminalMessage}
|
|
|
4818
4609
|
if (scheduledBytes + Buffer.byteLength(footer) > MAX_MIRROR_BYTES) footer = `
|
|
4819
4610
|
${status}
|
|
4820
4611
|
`;
|
|
4821
|
-
return new Promise((
|
|
4612
|
+
return new Promise((resolve9) => {
|
|
4822
4613
|
logStream.end(footer, () => {
|
|
4823
4614
|
if (available) {
|
|
4824
4615
|
try {
|
|
4825
|
-
|
|
4616
|
+
fs11.writeFileSync(donePath, `${status}
|
|
4826
4617
|
`, { encoding: "utf-8", mode: 384 });
|
|
4827
4618
|
} catch {
|
|
4828
4619
|
}
|
|
4829
4620
|
cleanupPaths = [logPath, donePath];
|
|
4830
4621
|
scheduleDeferredCleanup();
|
|
4831
4622
|
}
|
|
4832
|
-
|
|
4623
|
+
resolve9();
|
|
4833
4624
|
});
|
|
4834
4625
|
});
|
|
4835
4626
|
}
|
|
@@ -5455,7 +5246,7 @@ async function runSingleAttempt(runtimeCwd, agent, task, model, options, shared)
|
|
|
5455
5246
|
childIndex: options.index ?? 0
|
|
5456
5247
|
}) : void 0;
|
|
5457
5248
|
const permissionRules = resolvePermissionRules(options.permissions, agent.permissions);
|
|
5458
|
-
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;
|
|
5459
5250
|
let onWatchdogStatus;
|
|
5460
5251
|
const launch = buildInProcessChildLaunch({
|
|
5461
5252
|
machine: options.machine,
|
|
@@ -5646,7 +5437,7 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5646
5437
|
const childSessions = options.childSessionFactory ?? childSessionFactory();
|
|
5647
5438
|
const partialOutput = createPartialOutputTracker();
|
|
5648
5439
|
let childPromptFailed = false;
|
|
5649
|
-
const exitCode = await new Promise((
|
|
5440
|
+
const exitCode = await new Promise((resolve9) => {
|
|
5650
5441
|
const jsonlWriter = createJsonlWriter(shared.jsonlPath, { pause() {
|
|
5651
5442
|
}, resume() {
|
|
5652
5443
|
} });
|
|
@@ -5719,6 +5510,8 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5719
5510
|
let agentSettledReceived = false;
|
|
5720
5511
|
let queuedDrainHold = false;
|
|
5721
5512
|
let compactionStartedReceived = false;
|
|
5513
|
+
let terminalAgentEndReceived = false;
|
|
5514
|
+
let compactionAfterFinalOpen = false;
|
|
5722
5515
|
let finalDrainTimer;
|
|
5723
5516
|
let finalHardFinishTimer;
|
|
5724
5517
|
let watchdogTailTimer;
|
|
@@ -5761,7 +5554,7 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5761
5554
|
if (sessionSettled || finalDrainTimer || lifecycleFinished) return;
|
|
5762
5555
|
finalDrainTimer = setTimeout(() => {
|
|
5763
5556
|
if (lifecycleFinished || sessionSettled) return;
|
|
5764
|
-
if (capture.finalDrainHeld() || observeQueuedDrainHold()) {
|
|
5557
|
+
if (capture.finalDrainHeld() || observeQueuedDrainHold() || compactionAfterFinalOpen) {
|
|
5765
5558
|
finalDrainTimer = void 0;
|
|
5766
5559
|
armFinalDrainTimer();
|
|
5767
5560
|
return;
|
|
@@ -5775,9 +5568,7 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5775
5568
|
if (lifecycleFinished || sessionSettled) return;
|
|
5776
5569
|
settle(void 0, true);
|
|
5777
5570
|
}, HARD_FINISH_MS);
|
|
5778
|
-
finalHardFinishTimer.unref?.();
|
|
5779
5571
|
}, FINAL_STOP_GRACE_MS);
|
|
5780
|
-
finalDrainTimer.unref?.();
|
|
5781
5572
|
};
|
|
5782
5573
|
function armWatchdogTail() {
|
|
5783
5574
|
if (!cleanTerminalAssistantStopReceived && !agentSettledReceived || watchdogTailTimer || lifecycleFinished || sessionSettled) return;
|
|
@@ -5795,12 +5586,18 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5795
5586
|
}, childWatchdog?.watchdogTailTimeoutMs ?? 12e4);
|
|
5796
5587
|
watchdogTailTimer.unref?.();
|
|
5797
5588
|
}
|
|
5798
|
-
const applyChildLifecycle = (action) => {
|
|
5589
|
+
const applyChildLifecycle = (action, event) => {
|
|
5799
5590
|
if (action === "cancel-drain") {
|
|
5800
|
-
|
|
5591
|
+
terminalAgentEndReceived = event?.type === "agent_end" && event.willRetry !== true && cleanTerminalAssistantStopReceived;
|
|
5801
5592
|
agentSettledReceived = false;
|
|
5802
5593
|
clearFinalDrainTimers();
|
|
5803
5594
|
clearWatchdogTailTimer();
|
|
5595
|
+
if (terminalAgentEndReceived) {
|
|
5596
|
+
startFinalDrain();
|
|
5597
|
+
return;
|
|
5598
|
+
}
|
|
5599
|
+
cleanTerminalAssistantStopReceived = false;
|
|
5600
|
+
compactionAfterFinalOpen = false;
|
|
5804
5601
|
return;
|
|
5805
5602
|
}
|
|
5806
5603
|
if (action === "start-drain") startFinalDrain();
|
|
@@ -5850,7 +5647,7 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5850
5647
|
}
|
|
5851
5648
|
await session?.dispose();
|
|
5852
5649
|
}).catch(() => void 0).then(() => {
|
|
5853
|
-
|
|
5650
|
+
resolve9(code);
|
|
5854
5651
|
});
|
|
5855
5652
|
};
|
|
5856
5653
|
const drainPendingControlEvents = () => {
|
|
@@ -5882,13 +5679,13 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
5882
5679
|
};
|
|
5883
5680
|
const recordActiveToolCall = (event, args, now) => {
|
|
5884
5681
|
const key = toolTimeoutCallKey(event, ++activeToolSequence);
|
|
5885
|
-
const
|
|
5682
|
+
const path19 = resolveCurrentPath(event.toolName, args);
|
|
5886
5683
|
const active = {
|
|
5887
5684
|
key,
|
|
5888
5685
|
tool: event.toolName,
|
|
5889
5686
|
args: extractToolArgsPreview(args),
|
|
5890
5687
|
startedAt: now,
|
|
5891
|
-
...
|
|
5688
|
+
...path19 !== void 0 ? { path: path19 } : {}
|
|
5892
5689
|
};
|
|
5893
5690
|
activeToolCalls.set(key, active);
|
|
5894
5691
|
const keys = activeToolKeysByName.get(active.tool) ?? [];
|
|
@@ -6041,11 +5838,17 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
6041
5838
|
if (evt.type === "compaction_start") {
|
|
6042
5839
|
compactionStartedReceived = true;
|
|
6043
5840
|
if (agentSettledReceived) afterCompactionSettlement = true;
|
|
5841
|
+
if (terminalAgentEndReceived) compactionAfterFinalOpen = true;
|
|
6044
5842
|
}
|
|
6045
5843
|
if (evt.type === "compaction_end" && evt.willRetry === true) {
|
|
6046
5844
|
compactionStartedReceived = false;
|
|
6047
5845
|
afterCompactionSettlement = false;
|
|
6048
5846
|
}
|
|
5847
|
+
if (evt.type === "compaction_end" && evt.willRetry !== true && compactionAfterFinalOpen) {
|
|
5848
|
+
compactionAfterFinalOpen = false;
|
|
5849
|
+
clearFinalDrainTimers();
|
|
5850
|
+
startFinalDrain();
|
|
5851
|
+
}
|
|
6049
5852
|
if (evt.type === "turn_start" || evt.type === "agent_start" || evt.type === "auto_retry_start") {
|
|
6050
5853
|
queuedDrainHold = false;
|
|
6051
5854
|
}
|
|
@@ -6072,7 +5875,7 @@ ${shared.acceptancePrompt}` : shared.systemPrompt,
|
|
|
6072
5875
|
agentSettledReceived = true;
|
|
6073
5876
|
afterCompactionSettlement = compactionStartedReceived;
|
|
6074
5877
|
}
|
|
6075
|
-
applyChildLifecycle(lifecycleAction);
|
|
5878
|
+
applyChildLifecycle(lifecycleAction, evt);
|
|
6076
5879
|
if (isChildWatchdogStatusEvent(evt)) {
|
|
6077
5880
|
if (!childWatchdog) return;
|
|
6078
5881
|
const next = acceptChildWatchdogEvent({
|
|
@@ -6624,7 +6427,7 @@ ${resolvedOutput.saveError}` : resolvedOutput.saveError;
|
|
|
6624
6427
|
}
|
|
6625
6428
|
function writeStructuredOutputArtifacts(runtime, value, acceptanceReport) {
|
|
6626
6429
|
try {
|
|
6627
|
-
mkdirSync6(
|
|
6430
|
+
mkdirSync6(path10.dirname(runtime.outputPath), { recursive: true });
|
|
6628
6431
|
writeFileSync4(runtime.outputPath, JSON.stringify(value), { mode: 384 });
|
|
6629
6432
|
if (runtime.acceptanceReportPath) {
|
|
6630
6433
|
if (acceptanceReport !== void 0) writeFileSync4(runtime.acceptanceReportPath, JSON.stringify(acceptanceReport), { mode: 384 });
|
|
@@ -6654,7 +6457,7 @@ async function runSyncCompletionInner(runtimeCwd, agents, agentName, task, optio
|
|
|
6654
6457
|
const childSessionName = deriveChildSessionName({ agent: agentName, task });
|
|
6655
6458
|
const agent = agents.find((a) => a.name === agentName);
|
|
6656
6459
|
if (!agent) {
|
|
6657
|
-
const diagnosticContext = options.unknownAgentDiagnosticContext ?? unknownAgentDiagnosticContext(discoverAgents(
|
|
6460
|
+
const diagnosticContext = options.unknownAgentDiagnosticContext ?? unknownAgentDiagnosticContext(discoverAgents(path10.resolve(options.cwd ?? runtimeCwd), "both"));
|
|
6658
6461
|
return redactResultPrompt(withRunContext({
|
|
6659
6462
|
index: options.index ?? 0,
|
|
6660
6463
|
agent: agentName,
|
|
@@ -6747,7 +6550,7 @@ async function runSyncCompletionInner(runtimeCwd, agents, agentName, task, optio
|
|
|
6747
6550
|
${acceptancePrompt}` : task;
|
|
6748
6551
|
options.onEffectivePrompt?.(taskWithAcceptance);
|
|
6749
6552
|
const sessionEnabled = Boolean(options.sessionFile || options.sessionDir) || shareEnabled;
|
|
6750
|
-
if (options.context === "fork" && options.sessionFile &&
|
|
6553
|
+
if (options.context === "fork" && options.sessionFile && existsSync4(options.sessionFile)) {
|
|
6751
6554
|
alignForkedSessionCwd(options.sessionFile, options.cwd ?? runtimeCwd);
|
|
6752
6555
|
}
|
|
6753
6556
|
const skillNames = options.skills ?? agent.skills ?? [];
|
|
@@ -6757,7 +6560,7 @@ ${acceptancePrompt}` : task;
|
|
|
6757
6560
|
skillCwd,
|
|
6758
6561
|
runtimeCwd,
|
|
6759
6562
|
agent.skillPath,
|
|
6760
|
-
agent.filePath ?
|
|
6563
|
+
agent.filePath ? path10.dirname(agent.filePath) : skillCwd
|
|
6761
6564
|
);
|
|
6762
6565
|
if (skillNames.some((skill) => skill.trim() === "pi-subagents") && missingSkills.includes("pi-subagents")) {
|
|
6763
6566
|
return redactResultPrompt(withRunContext({
|
|
@@ -6912,7 +6715,7 @@ ${PROMPT_REDACTED}; live Prompt Audit only.
|
|
|
6912
6715
|
messages: attemptResult.messages ?? [],
|
|
6913
6716
|
error: attemptResult.error,
|
|
6914
6717
|
processSignal: attemptResult.processSignal,
|
|
6915
|
-
sessionAvailable: Boolean(options.sessionFile &&
|
|
6718
|
+
sessionAvailable: Boolean(options.sessionFile && existsSync4(options.sessionFile)),
|
|
6916
6719
|
alreadyResumed: attemptIndex > 0,
|
|
6917
6720
|
stopped: attemptResult.stopped || attemptResult.detached || Boolean(detachedReason) || Boolean(options.workflowChildPermitLaunch) || options.signal?.aborted,
|
|
6918
6721
|
interrupted: attemptResult.interrupted || options.interruptSignal?.aborted,
|
|
@@ -6983,7 +6786,7 @@ ${recovery.diagnostic}` : recovery.diagnostic;
|
|
|
6983
6786
|
result2.outputSaveError = result2.outputSaveError ? `${result2.outputSaveError}
|
|
6984
6787
|
${message}` : message;
|
|
6985
6788
|
}
|
|
6986
|
-
if (options.sessionFile && (
|
|
6789
|
+
if (options.sessionFile && (existsSync4(options.sessionFile) || result2.messages?.length)) {
|
|
6987
6790
|
result2.sessionFile = options.sessionFile;
|
|
6988
6791
|
} else if (shareEnabled && options.sessionDir) {
|
|
6989
6792
|
const sessionFile = findLatestSessionFile(options.sessionDir);
|
|
@@ -7088,8 +6891,8 @@ async function runSync(runtimeCwd, agents, agentName, task, options) {
|
|
|
7088
6891
|
let activeDetachAttempt;
|
|
7089
6892
|
let detachReadyPublished = false;
|
|
7090
6893
|
let resolveReceipt;
|
|
7091
|
-
const receipt = new Promise((
|
|
7092
|
-
resolveReceipt =
|
|
6894
|
+
const receipt = new Promise((resolve9) => {
|
|
6895
|
+
resolveReceipt = resolve9;
|
|
7093
6896
|
});
|
|
7094
6897
|
const originSignal = options.signal;
|
|
7095
6898
|
const originController = new AbortController();
|
|
@@ -7343,8 +7146,8 @@ var WATCHDOG_THINKING_VALUES = ["inherit", ...THINKING_LEVELS];
|
|
|
7343
7146
|
// src/shared/pruned-fork.ts
|
|
7344
7147
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
7345
7148
|
import { createHash as createHash2 } from "node:crypto";
|
|
7346
|
-
import * as
|
|
7347
|
-
import * as
|
|
7149
|
+
import * as fs12 from "node:fs";
|
|
7150
|
+
import * as path11 from "node:path";
|
|
7348
7151
|
var MAX_INHERITED_SESSION_BYTES = 64 * 1024;
|
|
7349
7152
|
var MIN_USEFUL_SPILL_BYTES = 320;
|
|
7350
7153
|
var MAX_ITEM_SUMMARY_CHARS = 160;
|
|
@@ -7373,7 +7176,7 @@ function stableJson(value) {
|
|
|
7373
7176
|
throw new Error(`Pruned fork cannot serialize ${typeof value} transcript data.`);
|
|
7374
7177
|
}
|
|
7375
7178
|
function readEntries(sessionFile) {
|
|
7376
|
-
return
|
|
7179
|
+
return fs12.readFileSync(sessionFile, "utf-8").split("\n").filter((line) => line.trim()).map((line, index) => {
|
|
7377
7180
|
try {
|
|
7378
7181
|
const parsed = JSON.parse(line);
|
|
7379
7182
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || typeof parsed.type !== "string") throw new Error("entry must be an object with a type");
|
|
@@ -7614,17 +7417,17 @@ function prunedForkRecoveryPath(sessionFile) {
|
|
|
7614
7417
|
return `${sessionFile}.pruned-recovery.json`;
|
|
7615
7418
|
}
|
|
7616
7419
|
function writeAtomicPrivate(filePath, content, mode) {
|
|
7617
|
-
const tempFile =
|
|
7420
|
+
const tempFile = path11.join(path11.dirname(filePath), `.${path11.basename(filePath)}.${process.pid}.tmp`);
|
|
7618
7421
|
try {
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7422
|
+
fs12.writeFileSync(tempFile, content, { encoding: "utf-8", mode });
|
|
7423
|
+
fs12.chmodSync(tempFile, mode);
|
|
7424
|
+
fs12.renameSync(tempFile, filePath);
|
|
7622
7425
|
} finally {
|
|
7623
|
-
|
|
7426
|
+
fs12.rmSync(tempFile, { force: true });
|
|
7624
7427
|
}
|
|
7625
7428
|
}
|
|
7626
7429
|
function writeEntriesAtomic(sessionFile, entries) {
|
|
7627
|
-
writeAtomicPrivate(sessionFile, serializedEntries(entries),
|
|
7430
|
+
writeAtomicPrivate(sessionFile, serializedEntries(entries), fs12.statSync(sessionFile).mode & 511);
|
|
7628
7431
|
}
|
|
7629
7432
|
async function pruneForkSessionFile(sessionFile, summarize, options = {}) {
|
|
7630
7433
|
const entries = readEntries(sessionFile);
|
|
@@ -7665,11 +7468,11 @@ async function pruneForkSessionFile(sessionFile, summarize, options = {}) {
|
|
|
7665
7468
|
try {
|
|
7666
7469
|
writeAtomicPrivate(recoveryPath, `${JSON.stringify(payload, null, " ")}
|
|
7667
7470
|
`, 384);
|
|
7668
|
-
const stored = JSON.parse(
|
|
7471
|
+
const stored = JSON.parse(fs12.readFileSync(recoveryPath, "utf-8"));
|
|
7669
7472
|
if (!validateRecovery(stored) || stableJson(stored) !== stableJson(payload)) throw new Error("Pruned fork stored recovery payload failed validation.");
|
|
7670
7473
|
writeEntriesAtomic(sessionFile, entries);
|
|
7671
7474
|
} catch (error) {
|
|
7672
|
-
|
|
7475
|
+
fs12.rmSync(recoveryPath, { force: true });
|
|
7673
7476
|
throw error;
|
|
7674
7477
|
}
|
|
7675
7478
|
return true;
|
|
@@ -7718,11 +7521,11 @@ async function createPrunedForkSessionWriter(ctx, config, signal) {
|
|
|
7718
7521
|
}
|
|
7719
7522
|
|
|
7720
7523
|
// src/runs/foreground/async-dismiss-action.ts
|
|
7721
|
-
import * as
|
|
7722
|
-
import * as
|
|
7524
|
+
import * as fs13 from "node:fs";
|
|
7525
|
+
import * as path12 from "node:path";
|
|
7723
7526
|
function dismissRecoveredWorkflow(state, location) {
|
|
7724
7527
|
const asyncDir = location.asyncDir;
|
|
7725
|
-
const runId = location.resolvedId ?? (asyncDir ?
|
|
7528
|
+
const runId = location.resolvedId ?? (asyncDir ? path12.basename(asyncDir) : "requested");
|
|
7726
7529
|
if (!asyncDir) {
|
|
7727
7530
|
return {
|
|
7728
7531
|
content: [{ type: "text", text: `Recovered workflow '${runId}' has no disk status to dismiss.` }],
|
|
@@ -7761,7 +7564,7 @@ function dismissRecoveredWorkflow(state, location) {
|
|
|
7761
7564
|
}
|
|
7762
7565
|
let latestStatus = status;
|
|
7763
7566
|
const resultPath = resultFilePath(DIRS.results, status.runId);
|
|
7764
|
-
if (
|
|
7567
|
+
if (fs13.existsSync(resultPath)) {
|
|
7765
7568
|
const reconciled = reconcileAsyncRun(asyncDir).status;
|
|
7766
7569
|
if (reconciled && reconciled.state !== "running") {
|
|
7767
7570
|
return {
|
|
@@ -7773,7 +7576,7 @@ function dismissRecoveredWorkflow(state, location) {
|
|
|
7773
7576
|
if (reconciled) latestStatus = reconciled;
|
|
7774
7577
|
}
|
|
7775
7578
|
const dismissed = { ...latestStatus, displayDismissedAt: Date.now() };
|
|
7776
|
-
writeAtomicJson(
|
|
7579
|
+
writeAtomicJson(path12.join(asyncDir, "status.json"), dismissed);
|
|
7777
7580
|
const repaired = reconcileAsyncRun(asyncDir).status;
|
|
7778
7581
|
if (repaired && repaired.state !== "running") {
|
|
7779
7582
|
return {
|
|
@@ -7794,8 +7597,8 @@ function dismissRecoveredWorkflow(state, location) {
|
|
|
7794
7597
|
}
|
|
7795
7598
|
|
|
7796
7599
|
// src/runs/foreground/workflow-detach-reconcile.ts
|
|
7797
|
-
import * as
|
|
7798
|
-
import * as
|
|
7600
|
+
import * as fs14 from "node:fs";
|
|
7601
|
+
import * as path13 from "node:path";
|
|
7799
7602
|
|
|
7800
7603
|
// src/workflows/workflow-settlement.ts
|
|
7801
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.";
|
|
@@ -8027,9 +7830,9 @@ function workflowResultChildren(status, childRunId, result2, existingResults, re
|
|
|
8027
7830
|
}));
|
|
8028
7831
|
}
|
|
8029
7832
|
function reconcileWorkflowReceipt(status, childRunId, result2, asyncDir, resolution) {
|
|
8030
|
-
const receiptRoot =
|
|
7833
|
+
const receiptRoot = path13.dirname(asyncDir);
|
|
8031
7834
|
const receiptPath = workflowReceiptPath(receiptRoot, status.runId);
|
|
8032
|
-
if (!
|
|
7835
|
+
if (!fs14.existsSync(receiptPath)) return void 0;
|
|
8033
7836
|
const receipt = readWorkflowReceipt(receiptRoot, status.runId);
|
|
8034
7837
|
const step = status.steps?.find((candidate) => candidate.runId === childRunId);
|
|
8035
7838
|
const key = step?.workflowKey;
|
|
@@ -8038,13 +7841,13 @@ function reconcileWorkflowReceipt(status, childRunId, result2, asyncDir, resolut
|
|
|
8038
7841
|
if (!entry) throw new Error(`Workflow receipt '${status.runId}' has no detached child key '${key}'.`);
|
|
8039
7842
|
let resumability;
|
|
8040
7843
|
try {
|
|
8041
|
-
const target = resolveAsyncResumeTarget({ id: childRunId, dir:
|
|
7844
|
+
const target = resolveAsyncResumeTarget({ id: childRunId, dir: path13.join(DIRS.async, childRunId) }, {}, { requireSessionFile: true, sessionId: status.sessionId });
|
|
8042
7845
|
resumability = target.kind === "revive" ? { state: "resumable" } : { state: "not-resumable", reason: "child is still running" };
|
|
8043
7846
|
} catch (error) {
|
|
8044
7847
|
resumability = { state: "not-resumable", reason: error instanceof Error ? error.message : String(error) };
|
|
8045
7848
|
}
|
|
8046
7849
|
const outputReference = result2.savedOutputPath ?? result2.outputReference?.path ?? entry.outputReference;
|
|
8047
|
-
const childStatus = readStatus(
|
|
7850
|
+
const childStatus = readStatus(path13.join(DIRS.async, childRunId));
|
|
8048
7851
|
const externalStep = childStatus?.steps?.length === 1 && childStatus.steps[0]?.runner?.type === "external-cli" ? childStatus.steps[0] : void 0;
|
|
8049
7852
|
const externalRunner = normalizeExternalCliRunnerStatus(result2.runner?.type === "external-cli" ? result2.runner : externalStep?.runner);
|
|
8050
7853
|
const externalProcess = result2.externalProcess ?? externalStep?.externalProcess;
|
|
@@ -8088,9 +7891,9 @@ function reconcileWorkflowReceipt(status, childRunId, result2, asyncDir, resolut
|
|
|
8088
7891
|
return { receipt: next, path: writeWorkflowReceipt(asyncDir, next) };
|
|
8089
7892
|
}
|
|
8090
7893
|
function appendDetachedWorkflowEvent(asyncDir, event) {
|
|
8091
|
-
const eventsPath =
|
|
7894
|
+
const eventsPath = path13.join(asyncDir, "events.jsonl");
|
|
8092
7895
|
try {
|
|
8093
|
-
|
|
7896
|
+
fs14.appendFileSync(eventsPath, `${JSON.stringify(event)}
|
|
8094
7897
|
`, "utf-8");
|
|
8095
7898
|
} catch (error) {
|
|
8096
7899
|
console.error(`Failed to append detached workflow event '${eventsPath}':`, error);
|
|
@@ -8098,7 +7901,7 @@ function appendDetachedWorkflowEvent(asyncDir, event) {
|
|
|
8098
7901
|
}
|
|
8099
7902
|
function reconcileDetachedWorkflowChildCompletion(input) {
|
|
8100
7903
|
const job = input.state.asyncJobs.get(input.workflowRunId);
|
|
8101
|
-
const asyncDir = job?.asyncDir ??
|
|
7904
|
+
const asyncDir = job?.asyncDir ?? path13.join(DIRS.async, input.workflowRunId);
|
|
8102
7905
|
const status = readStatus(asyncDir);
|
|
8103
7906
|
if (!status) return false;
|
|
8104
7907
|
const matchingControls = input.workflowKey === void 0 ? [] : [...input.state.foregroundControls?.values() ?? []].filter(
|
|
@@ -8123,7 +7926,7 @@ function reconcileDetachedWorkflowChildCompletion(input) {
|
|
|
8123
7926
|
const resultPath = resultFilePath(DIRS.results, input.workflowRunId);
|
|
8124
7927
|
let existing;
|
|
8125
7928
|
try {
|
|
8126
|
-
const parsed = JSON.parse(
|
|
7929
|
+
const parsed = JSON.parse(fs14.readFileSync(resultPath, "utf-8"));
|
|
8127
7930
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) existing = parsed;
|
|
8128
7931
|
} catch (error) {
|
|
8129
7932
|
if (error.code !== "ENOENT") throw error;
|
|
@@ -8169,7 +7972,7 @@ function reconcileDetachedWorkflowChildCompletion(input) {
|
|
|
8169
7972
|
terminalOutcome: receipt?.terminalOutcome,
|
|
8170
7973
|
eventMetadata: { reconciledFromDetachedChild: input.childRunId }
|
|
8171
7974
|
});
|
|
8172
|
-
writeAtomicJson(
|
|
7975
|
+
writeAtomicJson(path13.join(asyncDir, "status.json"), plan.status);
|
|
8173
7976
|
updateActiveRunIndex(asyncDir, plan.status.state, plan.status.toolCallId);
|
|
8174
7977
|
if (job) {
|
|
8175
7978
|
job.status = plan.status.state;
|
|
@@ -8221,14 +8024,14 @@ function reconcileDetachedWorkflowChildCompletion(input) {
|
|
|
8221
8024
|
}
|
|
8222
8025
|
|
|
8223
8026
|
// src/runs/background/chain-root-attachment.ts
|
|
8224
|
-
import * as
|
|
8225
|
-
import * as
|
|
8027
|
+
import * as fs15 from "node:fs";
|
|
8028
|
+
import * as path14 from "node:path";
|
|
8226
8029
|
var TERMINAL_STATES = /* @__PURE__ */ new Set(["complete", "failed", "partial", "paused", "stopped"]);
|
|
8227
8030
|
var TERMINAL_STEP_STATUSES = /* @__PURE__ */ new Set(["complete", "completed", "failed", "partial", "paused", "stopped"]);
|
|
8228
8031
|
function readResultFile(resultPath) {
|
|
8229
8032
|
let raw;
|
|
8230
8033
|
try {
|
|
8231
|
-
raw =
|
|
8034
|
+
raw = fs15.readFileSync(resultPath, "utf-8");
|
|
8232
8035
|
} catch (error) {
|
|
8233
8036
|
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
8234
8037
|
return void 0;
|
|
@@ -8240,7 +8043,7 @@ function readResultFile(resultPath) {
|
|
|
8240
8043
|
function readImportedResultFile(root, status) {
|
|
8241
8044
|
const direct = readResultFile(root.resultPath);
|
|
8242
8045
|
if (direct || !status?.sessionId) return direct;
|
|
8243
|
-
const indexedPath = resultPayloadPathForSessionRun(
|
|
8046
|
+
const indexedPath = resultPayloadPathForSessionRun(path14.dirname(root.resultPath), status.sessionId, root.runId);
|
|
8244
8047
|
return indexedPath ? readResultFile(indexedPath) : void 0;
|
|
8245
8048
|
}
|
|
8246
8049
|
function selectedStatusStep(status, index) {
|
|
@@ -8375,10 +8178,10 @@ async function waitForImportedAsyncRoot(root, options = {}) {
|
|
|
8375
8178
|
} else {
|
|
8376
8179
|
terminalSince = void 0;
|
|
8377
8180
|
}
|
|
8378
|
-
if (!status && !
|
|
8181
|
+
if (!status && !fs15.existsSync(root.asyncDir)) {
|
|
8379
8182
|
throw new Error(`Attached async root '${root.runId}' directory does not exist: ${root.asyncDir}`);
|
|
8380
8183
|
}
|
|
8381
|
-
await new Promise((
|
|
8184
|
+
await new Promise((resolve9) => setTimeout(resolve9, pollIntervalMs));
|
|
8382
8185
|
}
|
|
8383
8186
|
}
|
|
8384
8187
|
function resolveAsyncRootResultPath(resultsDir, runId) {
|
|
@@ -8386,7 +8189,7 @@ function resolveAsyncRootResultPath(resultsDir, runId) {
|
|
|
8386
8189
|
}
|
|
8387
8190
|
|
|
8388
8191
|
// src/runs/background/await-async-run.ts
|
|
8389
|
-
import * as
|
|
8192
|
+
import * as fs16 from "node:fs";
|
|
8390
8193
|
function errorMessage2(error) {
|
|
8391
8194
|
return error instanceof Error ? error.message : String(error);
|
|
8392
8195
|
}
|
|
@@ -8394,7 +8197,7 @@ function runnerExitedWithoutResult(asyncDir, runId, resultPath) {
|
|
|
8394
8197
|
const status = readStatus(asyncDir);
|
|
8395
8198
|
if (!status || status.state !== "running" && status.state !== "queued" || typeof status.pid !== "number") return false;
|
|
8396
8199
|
if (checkRecordedPidLiveness({ pid: status.pid, pidNamespaceScope: status.pidNamespaceScope, bootId: status.bootId }) !== "dead") return false;
|
|
8397
|
-
return !
|
|
8200
|
+
return !fs16.existsSync(resultPath) && !(status.sessionId && resultPayloadPathForSessionRun(asyncDir, status.sessionId, runId));
|
|
8398
8201
|
}
|
|
8399
8202
|
async function awaitExistingAsyncRun(asyncDir, runId, signal) {
|
|
8400
8203
|
signal.throwIfAborted();
|
|
@@ -8429,7 +8232,7 @@ function claimWorkflowAwaitedResult(asyncDir, claimant, read) {
|
|
|
8429
8232
|
try {
|
|
8430
8233
|
return withResultRunLease(asyncDir, read.runId, () => {
|
|
8431
8234
|
if (resultSnapshotReplaced(resultPath, read)) return void 0;
|
|
8432
|
-
|
|
8235
|
+
fs16.renameSync(resultPath, claimedPath);
|
|
8433
8236
|
return claimedPath;
|
|
8434
8237
|
});
|
|
8435
8238
|
} catch {
|
|
@@ -8438,8 +8241,8 @@ function claimWorkflowAwaitedResult(asyncDir, claimant, read) {
|
|
|
8438
8241
|
}
|
|
8439
8242
|
|
|
8440
8243
|
// src/runs/background/run-status.ts
|
|
8441
|
-
import * as
|
|
8442
|
-
import * as
|
|
8244
|
+
import * as fs17 from "node:fs";
|
|
8245
|
+
import * as path15 from "node:path";
|
|
8443
8246
|
var STRUCTURED_PREVIEW_WITH_PATH_CHARS = 500;
|
|
8444
8247
|
function formatProcessTerminal(value) {
|
|
8445
8248
|
if (!value) return "missing";
|
|
@@ -8449,7 +8252,7 @@ function debugProcessTerminal(asyncDir, status) {
|
|
|
8449
8252
|
const expected = { runId: status.runId, runnerProcessInstanceId: status.processTerminal?.runnerProcessInstanceId };
|
|
8450
8253
|
return {
|
|
8451
8254
|
sidecar: readProcessTerminal(asyncDir, expected),
|
|
8452
|
-
overlay: sanitizeProcessTerminal(status.processTerminal, expected,
|
|
8255
|
+
overlay: sanitizeProcessTerminal(status.processTerminal, expected, path15.join(asyncDir, "status.json"))
|
|
8453
8256
|
};
|
|
8454
8257
|
}
|
|
8455
8258
|
function formatCapacityOwner(inspect) {
|
|
@@ -8483,9 +8286,9 @@ function formatRunLifecycleDebug(input) {
|
|
|
8483
8286
|
"Run lifecycle debug",
|
|
8484
8287
|
`Run: ${status.runId}`,
|
|
8485
8288
|
`Dir: ${asyncDir}`,
|
|
8486
|
-
`Status file: ${
|
|
8289
|
+
`Status file: ${path15.join(asyncDir, "status.json")}`,
|
|
8487
8290
|
status.workflowReceiptPath ? `Workflow receipt: ${status.workflowReceiptPath}` : void 0,
|
|
8488
|
-
`Process terminal file: ${
|
|
8291
|
+
`Process terminal file: ${path15.join(asyncDir, "process-terminal.json")}`,
|
|
8489
8292
|
`Session: ${status.sessionId ?? "unknown"}`,
|
|
8490
8293
|
`State: ${status.state}`,
|
|
8491
8294
|
`Mode: ${status.mode}`,
|
|
@@ -8500,7 +8303,7 @@ function formatRunLifecycleDebug(input) {
|
|
|
8500
8303
|
return lines.join("\n");
|
|
8501
8304
|
}
|
|
8502
8305
|
function hasExistingSessionFile(value) {
|
|
8503
|
-
return typeof value === "string" &&
|
|
8306
|
+
return typeof value === "string" && fs17.existsSync(value);
|
|
8504
8307
|
}
|
|
8505
8308
|
function formatResumeGuidance(runId, children, fallbackSessionFile, options = {}) {
|
|
8506
8309
|
if (options.stopped) return "Resume: unavailable; stopped runs are not resumable. Start a new run instead.";
|
|
@@ -8563,9 +8366,9 @@ function externalJobFollowUpSupported(provider) {
|
|
|
8563
8366
|
}
|
|
8564
8367
|
function rememberedForegroundChildOutput(child) {
|
|
8565
8368
|
const outputPath = child.artifactPaths?.outputPath ?? child.savedOutputPath;
|
|
8566
|
-
if (outputPath &&
|
|
8369
|
+
if (outputPath && fs17.existsSync(outputPath)) {
|
|
8567
8370
|
try {
|
|
8568
|
-
const artifactOutput =
|
|
8371
|
+
const artifactOutput = fs17.readFileSync(outputPath, "utf-8").trim();
|
|
8569
8372
|
if (artifactOutput) return artifactOutput;
|
|
8570
8373
|
} catch {
|
|
8571
8374
|
}
|
|
@@ -8864,8 +8667,8 @@ No running work was terminated.` }],
|
|
|
8864
8667
|
};
|
|
8865
8668
|
}
|
|
8866
8669
|
const effectiveRunId = status?.runId ?? resolvedId ?? "unknown";
|
|
8867
|
-
const logPath =
|
|
8868
|
-
const eventsPath =
|
|
8670
|
+
const logPath = path15.join(asyncDir, `subagent-log-${effectiveRunId}.md`);
|
|
8671
|
+
const eventsPath = path15.join(asyncDir, "events.jsonl");
|
|
8869
8672
|
if (status) {
|
|
8870
8673
|
if (params.action === "debug.run") {
|
|
8871
8674
|
const { sidecar, overlay } = debugProcessTerminal(asyncDir, status);
|
|
@@ -8914,7 +8717,7 @@ No running work was terminated.` }],
|
|
|
8914
8717
|
const updated = status.lastUpdate ? new Date(status.lastUpdate).toISOString() : "n/a";
|
|
8915
8718
|
const statusActivityText = status.state === "running" ? formatActivityLabel(status.lastActivityAt, status.activityState) : void 0;
|
|
8916
8719
|
const steeringText = formatSteeringSummary(status);
|
|
8917
|
-
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"));
|
|
8918
8721
|
const runFanoutBudgetDescriptor = readRunFanoutBudgetDescriptor(asyncDir);
|
|
8919
8722
|
const runFanoutBudget = runFanoutBudgetDescriptor ? getRunFanoutBudgetSnapshot(runFanoutBudgetDescriptor) : status.runFanoutBudget;
|
|
8920
8723
|
let missionId;
|
|
@@ -8950,7 +8753,7 @@ Extensions denied: ${status.capabilityCeiling.denyExtensions ? "yes" : "no"} (so
|
|
|
8950
8753
|
status.parentWorkflowRunId ? `Workflow parent: ${status.parentWorkflowRunId}${status.workflowKey ? ` (${status.workflowKey})` : ""}` : void 0,
|
|
8951
8754
|
status.mode === "workflow" && workflowReturnPreview !== void 0 ? `Return: ${workflowReturnPreview}` : void 0,
|
|
8952
8755
|
status.mode === "workflow" && workflowEmitPreview !== void 0 ? `Latest emit: ${workflowEmitPreview}` : void 0,
|
|
8953
|
-
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,
|
|
8954
8757
|
`Progress: ${progressLabel}`,
|
|
8955
8758
|
...status.mode === "workflow" ? formatWorkflowChecklistText(projectWorkflowChecklist({
|
|
8956
8759
|
graph: status.workflowGraph,
|
|
@@ -8968,7 +8771,7 @@ Extensions denied: ${status.capabilityCeiling.denyExtensions ? "yes" : "no"} (so
|
|
|
8968
8771
|
outputPath ? `Output: ${outputPath}` : void 0,
|
|
8969
8772
|
status.parallelHandoff ? `Parallel handoff: ${status.parallelHandoff.path}` : void 0,
|
|
8970
8773
|
reconciliation.message ? `Diagnosis: ${reconciliation.message}` : void 0,
|
|
8971
|
-
reconciliation.resultPath &&
|
|
8774
|
+
reconciliation.resultPath && fs17.existsSync(reconciliation.resultPath) ? `Result: ${reconciliation.resultPath}` : void 0
|
|
8972
8775
|
].filter((line) => Boolean(line));
|
|
8973
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) : [];
|
|
8974
8777
|
let hasExternalJobFollowUpHint = false;
|
|
@@ -9030,8 +8833,8 @@ Extensions denied: ${status.capabilityCeiling.denyExtensions ? "yes" : "no"} (so
|
|
|
9030
8833
|
}
|
|
9031
8834
|
}
|
|
9032
8835
|
lines.push(...formatNestedRunStatusLines(step.children, { indent: " ", commandHints: true, maxLines: 20 }));
|
|
9033
|
-
const stepOutputPath =
|
|
9034
|
-
if (stepOutputPath !== outputPath &&
|
|
8836
|
+
const stepOutputPath = path15.join(asyncDir, `output-${index}.log`);
|
|
8837
|
+
if (stepOutputPath !== outputPath && fs17.existsSync(stepOutputPath)) lines.push(` Output: ${stepOutputPath}`);
|
|
9035
8838
|
if (step.status === "running" && step.runner?.type !== "external-cli" && step.runner?.type !== "external-job" && status.mode !== "workflow") {
|
|
9036
8839
|
lines.push(` Intercom target: ${resolveSubagentIntercomTarget(status.runId, step.agent, index)} (if registered)`);
|
|
9037
8840
|
lines.push(` Steer: subagent({ action: "steer", id: "${status.runId}", message: "...", options: { index: ${index} } })`);
|
|
@@ -9064,8 +8867,8 @@ Extensions denied: ${status.capabilityCeiling.denyExtensions ? "yes" : "no"} (so
|
|
|
9064
8867
|
if (status.state !== "running") {
|
|
9065
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) }));
|
|
9066
8869
|
}
|
|
9067
|
-
if (
|
|
9068
|
-
if (
|
|
8870
|
+
if (fs17.existsSync(logPath)) lines.push(`Log: ${logPath}`);
|
|
8871
|
+
if (fs17.existsSync(eventsPath)) lines.push(`Events: ${eventsPath}`);
|
|
9069
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 } } : {} } };
|
|
9070
8873
|
}
|
|
9071
8874
|
}
|
|
@@ -9078,7 +8881,7 @@ Extensions denied: ${status.capabilityCeiling.denyExtensions ? "yes" : "no"} (so
|
|
|
9078
8881
|
};
|
|
9079
8882
|
}
|
|
9080
8883
|
try {
|
|
9081
|
-
const raw =
|
|
8884
|
+
const raw = fs17.readFileSync(resultPath, "utf-8");
|
|
9082
8885
|
const data = JSON.parse(raw);
|
|
9083
8886
|
if (params.view === "transcript") {
|
|
9084
8887
|
try {
|
|
@@ -9171,16 +8974,16 @@ function applyForceTopLevelAsyncOverride(params, depth, forceTopLevelAsync) {
|
|
|
9171
8974
|
|
|
9172
8975
|
// src/runs/shared/worktree-cleanup-apply.ts
|
|
9173
8976
|
import { createHash as createHash5 } from "node:crypto";
|
|
9174
|
-
import { spawnSync as
|
|
9175
|
-
import * as
|
|
9176
|
-
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";
|
|
9177
8980
|
|
|
9178
8981
|
// src/runs/shared/worktree-cleanup-plan.ts
|
|
9179
8982
|
import { createHash as createHash4, randomUUID as randomUUID4 } from "node:crypto";
|
|
9180
|
-
import { spawnSync
|
|
9181
|
-
import * as
|
|
8983
|
+
import { spawnSync } from "node:child_process";
|
|
8984
|
+
import * as fs18 from "node:fs";
|
|
9182
8985
|
import * as os2 from "node:os";
|
|
9183
|
-
import * as
|
|
8986
|
+
import * as path16 from "node:path";
|
|
9184
8987
|
var WORKTREE_CLEANUP_PLAN_VERSION = 1;
|
|
9185
8988
|
var WORKTREE_CLEANUP_PLAN_TTL_MS = 30 * 60 * 1e3;
|
|
9186
8989
|
var MAX_DISCOVERED_HANDOFFS = 256;
|
|
@@ -9188,7 +8991,7 @@ var MAX_PLAN_ENTRIES = 512;
|
|
|
9188
8991
|
var MAX_METADATA_FILE_BYTES = 2 * 1024 * 1024;
|
|
9189
8992
|
function runGit(cwd, args) {
|
|
9190
8993
|
try {
|
|
9191
|
-
const result2 =
|
|
8994
|
+
const result2 = spawnSync("git", ["-C", cwd, ...args], { encoding: "utf-8", windowsHide: true });
|
|
9192
8995
|
return {
|
|
9193
8996
|
stdout: result2.stdout ?? "",
|
|
9194
8997
|
stderr: result2.stderr ?? "",
|
|
@@ -9212,7 +9015,7 @@ function runGitChecked(cwd, args) {
|
|
|
9212
9015
|
if (result2.status !== 0) throw new Error(gitFailure(result2, `git -C ${cwd} ${args.join(" ")}`));
|
|
9213
9016
|
return result2.stdout.trim();
|
|
9214
9017
|
}
|
|
9215
|
-
function realpathExisting(candidate, nativeRealpath =
|
|
9018
|
+
function realpathExisting(candidate, nativeRealpath = fs18.realpathSync.native, fallbackRealpath = fs18.realpathSync) {
|
|
9216
9019
|
try {
|
|
9217
9020
|
return nativeRealpath(candidate);
|
|
9218
9021
|
} catch {
|
|
@@ -9220,29 +9023,29 @@ function realpathExisting(candidate, nativeRealpath = fs19.realpathSync.native,
|
|
|
9220
9023
|
}
|
|
9221
9024
|
}
|
|
9222
9025
|
function comparablePath(candidate) {
|
|
9223
|
-
let current =
|
|
9026
|
+
let current = path16.resolve(candidate);
|
|
9224
9027
|
const missingSegments = [];
|
|
9225
9028
|
let canonical = current;
|
|
9226
9029
|
while (true) {
|
|
9227
9030
|
try {
|
|
9228
|
-
canonical =
|
|
9031
|
+
canonical = path16.join(realpathExisting(current), ...missingSegments.reverse());
|
|
9229
9032
|
break;
|
|
9230
9033
|
} catch {
|
|
9231
|
-
const parent =
|
|
9034
|
+
const parent = path16.dirname(current);
|
|
9232
9035
|
if (parent === current) break;
|
|
9233
|
-
missingSegments.push(
|
|
9036
|
+
missingSegments.push(path16.basename(current));
|
|
9234
9037
|
current = parent;
|
|
9235
9038
|
}
|
|
9236
9039
|
}
|
|
9237
|
-
const normalized =
|
|
9040
|
+
const normalized = path16.normalize(canonical);
|
|
9238
9041
|
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
9239
9042
|
}
|
|
9240
9043
|
function samePath(left, right) {
|
|
9241
9044
|
if (comparablePath(left) === comparablePath(right)) return true;
|
|
9242
9045
|
if (process.platform !== "win32") return false;
|
|
9243
9046
|
try {
|
|
9244
|
-
const leftStat =
|
|
9245
|
-
const rightStat =
|
|
9047
|
+
const leftStat = fs18.statSync(left, { bigint: true });
|
|
9048
|
+
const rightStat = fs18.statSync(right, { bigint: true });
|
|
9246
9049
|
return leftStat.dev !== 0n && leftStat.ino !== 0n && leftStat.dev === rightStat.dev && leftStat.ino === rightStat.ino;
|
|
9247
9050
|
} catch {
|
|
9248
9051
|
return false;
|
|
@@ -9252,15 +9055,15 @@ function resolveExistingPath(candidate) {
|
|
|
9252
9055
|
try {
|
|
9253
9056
|
return realpathExisting(candidate);
|
|
9254
9057
|
} catch {
|
|
9255
|
-
return
|
|
9058
|
+
return path16.resolve(candidate);
|
|
9256
9059
|
}
|
|
9257
9060
|
}
|
|
9258
9061
|
function pathInside(root, candidate, strict = false) {
|
|
9259
9062
|
const normalizedRoot = comparablePath(root);
|
|
9260
9063
|
const normalizedCandidate = comparablePath(candidate);
|
|
9261
9064
|
if (!strict && normalizedRoot === normalizedCandidate) return true;
|
|
9262
|
-
const relative3 =
|
|
9263
|
-
return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(`..${
|
|
9065
|
+
const relative3 = path16.relative(normalizedRoot, normalizedCandidate);
|
|
9066
|
+
return relative3 !== "" && relative3 !== ".." && !relative3.startsWith(`..${path16.sep}`) && !path16.isAbsolute(relative3);
|
|
9264
9067
|
}
|
|
9265
9068
|
function sha2562(value) {
|
|
9266
9069
|
return createHash4("sha256").update(value, "utf-8").digest("hex");
|
|
@@ -9272,7 +9075,7 @@ function validatePlanId(planId) {
|
|
|
9272
9075
|
return planId;
|
|
9273
9076
|
}
|
|
9274
9077
|
function resolveCleanupRepoRoot(repo) {
|
|
9275
|
-
const requested =
|
|
9078
|
+
const requested = path16.resolve(repo);
|
|
9276
9079
|
const toplevel = runGitChecked(requested, ["rev-parse", "--show-toplevel"]);
|
|
9277
9080
|
return realpathExisting(toplevel);
|
|
9278
9081
|
}
|
|
@@ -9280,17 +9083,17 @@ function resolveCleanupBaseDir(repoRoot, configuredBaseDir) {
|
|
|
9280
9083
|
const raw = configuredBaseDir ?? process.env.PI_SUBAGENTS_WORKTREE_DIR;
|
|
9281
9084
|
let dedicatedRoot;
|
|
9282
9085
|
if (raw === void 0 || configuredBaseDir === void 0 && !raw.trim()) {
|
|
9283
|
-
dedicatedRoot =
|
|
9086
|
+
dedicatedRoot = path16.join(path16.dirname(repoRoot), "worktrees");
|
|
9284
9087
|
} else {
|
|
9285
9088
|
const trimmed = raw.trim();
|
|
9286
9089
|
if (!trimmed) throw new Error("worktree base directory cannot be empty");
|
|
9287
|
-
const expanded = trimmed.startsWith("~/") ?
|
|
9288
|
-
dedicatedRoot =
|
|
9090
|
+
const expanded = trimmed.startsWith("~/") ? path16.join(os2.homedir(), trimmed.slice(2)) : trimmed;
|
|
9091
|
+
dedicatedRoot = path16.isAbsolute(expanded) ? expanded : path16.resolve(repoRoot, expanded);
|
|
9289
9092
|
}
|
|
9290
|
-
return
|
|
9093
|
+
return path16.resolve(path16.join(dedicatedRoot, path16.basename(repoRoot)));
|
|
9291
9094
|
}
|
|
9292
9095
|
function cleanupContainmentInvalid(repoRoot, projectDir) {
|
|
9293
|
-
const extensionsDir =
|
|
9096
|
+
const extensionsDir = path16.join(getAgentDir(), "extensions");
|
|
9294
9097
|
return samePath(projectDir, repoRoot) || pathInside(repoRoot, projectDir, true) || pathInside(extensionsDir, projectDir);
|
|
9295
9098
|
}
|
|
9296
9099
|
function parseGitWorktreeList(raw) {
|
|
@@ -9329,14 +9132,14 @@ function isRegisteredCleanupWorktree(repoRoot, candidate) {
|
|
|
9329
9132
|
return listGitWorktrees(repoRoot).some((tree) => samePath(tree.path, candidate));
|
|
9330
9133
|
}
|
|
9331
9134
|
function readJsonFile(filePath) {
|
|
9332
|
-
return JSON.parse(
|
|
9135
|
+
return JSON.parse(fs18.readFileSync(filePath, "utf-8"));
|
|
9333
9136
|
}
|
|
9334
9137
|
function isRecord(value) {
|
|
9335
9138
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
9336
9139
|
}
|
|
9337
9140
|
function readManifest(manifestPath) {
|
|
9338
9141
|
try {
|
|
9339
|
-
const stat =
|
|
9142
|
+
const stat = fs18.lstatSync(manifestPath);
|
|
9340
9143
|
if (stat.isSymbolicLink() || !stat.isFile()) return { error: `parallel handoff manifest is not a regular file: ${manifestPath}` };
|
|
9341
9144
|
if (stat.size > MAX_METADATA_FILE_BYTES) return { error: `parallel handoff manifest exceeds the ${MAX_METADATA_FILE_BYTES}-byte limit: ${manifestPath}` };
|
|
9342
9145
|
const parsed = readParallelHandoffManifest(manifestPath);
|
|
@@ -9354,22 +9157,22 @@ function discoverHandoffPaths(repoRoot, input) {
|
|
|
9354
9157
|
const paths = [];
|
|
9355
9158
|
const warnings = [];
|
|
9356
9159
|
const add = (candidate) => {
|
|
9357
|
-
const resolved =
|
|
9160
|
+
const resolved = path16.isAbsolute(candidate) ? path16.resolve(candidate) : path16.resolve(repoRoot, candidate);
|
|
9358
9161
|
if (!paths.some((existing) => comparablePath(existing) === comparablePath(resolved))) paths.push(resolved);
|
|
9359
9162
|
};
|
|
9360
9163
|
if (input.handoffPath) add(input.handoffPath);
|
|
9361
9164
|
for (const candidate of input.handoffPaths ?? []) add(candidate);
|
|
9362
|
-
const artifactsDir =
|
|
9363
|
-
const projectHandoffPath =
|
|
9364
|
-
if (
|
|
9365
|
-
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");
|
|
9366
9169
|
try {
|
|
9367
|
-
const stat =
|
|
9170
|
+
const stat = fs18.lstatSync(handoffsDir);
|
|
9368
9171
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
9369
9172
|
warnings.push(`ignored non-directory handoff metadata root: ${handoffsDir}`);
|
|
9370
9173
|
} else {
|
|
9371
|
-
const entries =
|
|
9372
|
-
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));
|
|
9373
9176
|
if (entries.length === MAX_DISCOVERED_HANDOFFS) warnings.push(`handoff metadata discovery capped at ${MAX_DISCOVERED_HANDOFFS} files`);
|
|
9374
9177
|
}
|
|
9375
9178
|
} catch (error) {
|
|
@@ -9379,9 +9182,9 @@ function discoverHandoffPaths(repoRoot, input) {
|
|
|
9379
9182
|
return { paths: paths.sort((left, right) => comparablePath(left).localeCompare(comparablePath(right))), warnings: [...new Set(warnings)].sort() };
|
|
9380
9183
|
}
|
|
9381
9184
|
function readStatusBesideManifest(manifestPath) {
|
|
9382
|
-
const statusPath =
|
|
9185
|
+
const statusPath = path16.join(path16.dirname(manifestPath), "status.json");
|
|
9383
9186
|
try {
|
|
9384
|
-
const stat =
|
|
9187
|
+
const stat = fs18.lstatSync(statusPath);
|
|
9385
9188
|
if (stat.isSymbolicLink() || !stat.isFile()) return { statusPath, error: `async status is not a regular file: ${statusPath}` };
|
|
9386
9189
|
if (stat.size > MAX_METADATA_FILE_BYTES) return { statusPath, error: `async status exceeds the ${MAX_METADATA_FILE_BYTES}-byte limit: ${statusPath}` };
|
|
9387
9190
|
const parsed = readJsonFile(statusPath);
|
|
@@ -9442,7 +9245,7 @@ function loadMetadata(input, repoRoot) {
|
|
|
9442
9245
|
continue;
|
|
9443
9246
|
}
|
|
9444
9247
|
const manifestRoot = resolveExistingPath(manifestPath);
|
|
9445
|
-
if (manifestRoot !== manifestPath &&
|
|
9248
|
+
if (manifestRoot !== manifestPath && fs18.lstatSync(manifestPath).isSymbolicLink()) {
|
|
9446
9249
|
warnings.push(`ignored symlinked handoff manifest: ${manifestPath}`);
|
|
9447
9250
|
continue;
|
|
9448
9251
|
}
|
|
@@ -9451,9 +9254,9 @@ function loadMetadata(input, repoRoot) {
|
|
|
9451
9254
|
return { records, paths: discovered.paths, warnings: [...new Set(warnings)].sort() };
|
|
9452
9255
|
}
|
|
9453
9256
|
function inspectPath(candidate) {
|
|
9454
|
-
const resolved =
|
|
9257
|
+
const resolved = path16.resolve(candidate);
|
|
9455
9258
|
try {
|
|
9456
|
-
const stat =
|
|
9259
|
+
const stat = fs18.lstatSync(resolved);
|
|
9457
9260
|
if (stat.isSymbolicLink()) return { resolved, missing: false, symlink: true, directory: false };
|
|
9458
9261
|
return { resolved, realpath: realpathExisting(resolved), missing: false, symlink: false, directory: stat.isDirectory() };
|
|
9459
9262
|
} catch (error) {
|
|
@@ -9463,7 +9266,7 @@ function inspectPath(candidate) {
|
|
|
9463
9266
|
}
|
|
9464
9267
|
}
|
|
9465
9268
|
function metadataPath(manifestPath, candidate) {
|
|
9466
|
-
return
|
|
9269
|
+
return path16.isAbsolute(candidate) ? path16.resolve(candidate) : path16.resolve(path16.dirname(manifestPath), candidate);
|
|
9467
9270
|
}
|
|
9468
9271
|
function metadataRecordPath(record2) {
|
|
9469
9272
|
return metadataPath(record2.manifestPath, record2.task.path);
|
|
@@ -9504,7 +9307,7 @@ function inspectRunState(record2, now, foregroundRunOwnership) {
|
|
|
9504
9307
|
}
|
|
9505
9308
|
}
|
|
9506
9309
|
try {
|
|
9507
|
-
const markerAge = activeRunMarkerAgeMs(
|
|
9310
|
+
const markerAge = activeRunMarkerAgeMs(path16.dirname(record2.statusPath), now);
|
|
9508
9311
|
if (markerAge !== void 0) {
|
|
9509
9312
|
if (markerAge <= DEFAULT_STALE_TERMINAL_ACTIVE_MARKER_MS) return { kind: "active", reason: "owning run still has a recent active marker" };
|
|
9510
9313
|
return { kind: "stale", reason: `terminal active marker is stale (${Math.floor(markerAge / 1e3)}s old)` };
|
|
@@ -9565,7 +9368,7 @@ function isPatchCaptured(record2, worktreePath, baseCommit) {
|
|
|
9565
9368
|
if (inspection.missing || !inspection.realpath) return { error: "captured handoff patch is missing" };
|
|
9566
9369
|
let patchStat;
|
|
9567
9370
|
try {
|
|
9568
|
-
patchStat =
|
|
9371
|
+
patchStat = fs18.statSync(patchPath);
|
|
9569
9372
|
} catch (error) {
|
|
9570
9373
|
return { error: `captured patch cannot be inspected: ${error instanceof Error ? error.message : String(error)}` };
|
|
9571
9374
|
}
|
|
@@ -9578,7 +9381,7 @@ function isPatchCaptured(record2, worktreePath, baseCommit) {
|
|
|
9578
9381
|
}
|
|
9579
9382
|
function buildManagedEntry(input) {
|
|
9580
9383
|
const { repoRoot, baseDir, targetHead, rootPath, git, record: record2, foregroundRunOwnership } = input;
|
|
9581
|
-
const gitWorktreePath =
|
|
9384
|
+
const gitWorktreePath = path16.resolve(git.path);
|
|
9582
9385
|
const metadataWorktreePath = metadataRecordPath(record2);
|
|
9583
9386
|
const worktreePath = resolveExistingPath(metadataWorktreePath);
|
|
9584
9387
|
const entry = buildEntryBase({ path: worktreePath, branch: git.branch ?? "", source: "both", git, metadata: record2, targetHead });
|
|
@@ -9595,13 +9398,13 @@ function buildManagedEntry(input) {
|
|
|
9595
9398
|
if (pathInspection.missing) return blockedEntry(entry, "stale", "unknown", "worktree path is missing from disk");
|
|
9596
9399
|
if (pathInspection.symlink) return blockedEntry(entry, "unknown", "unknown", "worktree path is a symlink; cleanup requires a real directory");
|
|
9597
9400
|
if (!pathInspection.directory || !pathInspection.realpath) return blockedEntry(entry, "unknown", "unknown", "worktree path is not a directory");
|
|
9598
|
-
const extensionsDir =
|
|
9401
|
+
const extensionsDir = path16.join(getAgentDir(), "extensions");
|
|
9599
9402
|
if (pathInside(extensionsDir, pathInspection.realpath)) return blockedEntry(entry, "ineligible", "keep", `worktree real path is inside Pi extensions directory '${extensionsDir}'`);
|
|
9600
9403
|
if (input.containmentInvalid || !pathInside(baseDir, pathInspection.realpath, true)) return blockedEntry(entry, "ineligible", "keep", `worktree real path is outside configured base directory '${baseDir}'`);
|
|
9601
9404
|
const recordedBaseDir = record2.task.recordedBaseDir;
|
|
9602
|
-
if (typeof recordedBaseDir !== "string" || !
|
|
9603
|
-
const creationRelative =
|
|
9604
|
-
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}'`);
|
|
9605
9408
|
if (rootPath === comparablePath(pathInspection.realpath)) return blockedEntry(entry, "ineligible", "keep", "worktree is the repository root");
|
|
9606
9409
|
if (git.locked) return blockedEntry(entry, "ineligible", "keep", "Git worktree is locked");
|
|
9607
9410
|
if (!git.branch) return blockedEntry(entry, "unknown", "unknown", "detached worktrees have no metadata-recorded branch");
|
|
@@ -9780,7 +9583,7 @@ function buildWorktreeCleanupPlan(input) {
|
|
|
9780
9583
|
};
|
|
9781
9584
|
}
|
|
9782
9585
|
function worktreeCleanupPlanPath(repoRoot, planId) {
|
|
9783
|
-
return
|
|
9586
|
+
return path16.join(repoRoot, PROJECT_SUBAGENTS_RELATIVE_DIR, "cleanup-plans", `${validatePlanId(planId)}.json`);
|
|
9784
9587
|
}
|
|
9785
9588
|
function createWorktreeCleanupPlan(input) {
|
|
9786
9589
|
const plan = buildWorktreeCleanupPlan(input);
|
|
@@ -9828,13 +9631,13 @@ function formatWorktreeCleanupPlan(created) {
|
|
|
9828
9631
|
|
|
9829
9632
|
// src/runs/shared/worktree-cleanup-apply.ts
|
|
9830
9633
|
function assertContainedRealPath(repoRoot, candidate, create = false) {
|
|
9831
|
-
const relative3 =
|
|
9832
|
-
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.");
|
|
9833
9636
|
let current = repoRoot;
|
|
9834
|
-
for (const segment of relative3.split(
|
|
9835
|
-
current =
|
|
9836
|
-
if (!
|
|
9837
|
-
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);
|
|
9838
9641
|
if (stat.isSymbolicLink()) throw new Error(`Cleanup evidence cannot use a symlink: ${current}`);
|
|
9839
9642
|
}
|
|
9840
9643
|
}
|
|
@@ -9842,14 +9645,14 @@ function loadReviewedCleanupPlan(repo, planId, now = Date.now()) {
|
|
|
9842
9645
|
const repoRoot = resolveCleanupRepoRoot(repo);
|
|
9843
9646
|
const file = worktreeCleanupPlanPath(repoRoot, planId);
|
|
9844
9647
|
assertContainedRealPath(repoRoot, file);
|
|
9845
|
-
const stat =
|
|
9648
|
+
const stat = fs19.statSync(file);
|
|
9846
9649
|
if (!stat.isFile() || stat.size > 2 * 1024 * 1024) throw new Error("Cleanup plan is not a bounded regular file.");
|
|
9847
|
-
const plan = JSON.parse(
|
|
9848
|
-
if (plan.version !== 1 || plan.planId !== planId || plan.repoRoot !== repoRoot || !Number.isFinite(plan.createdAt) || !Number.isFinite(plan.expiresAt) || plan.createdAt > now || plan.expiresAt <= now && !
|
|
9849
|
-
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.");
|
|
9850
9653
|
const paths = /* @__PURE__ */ new Set();
|
|
9851
9654
|
for (const entry of plan.entries) {
|
|
9852
|
-
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.");
|
|
9853
9656
|
paths.add(entry.path);
|
|
9854
9657
|
}
|
|
9855
9658
|
const hash = createHash5("sha256").update(JSON.stringify(worktreeCleanupContentPayload(plan))).digest("hex");
|
|
@@ -9859,7 +9662,7 @@ function loadReviewedCleanupPlan(repo, planId, now = Date.now()) {
|
|
|
9859
9662
|
function recordRemoval(entry) {
|
|
9860
9663
|
const manifest = readParallelHandoffManifest(entry.handoffPath);
|
|
9861
9664
|
if (!manifest || manifest.runId !== entry.runId) throw new Error("Handoff ownership changed before recording removal.");
|
|
9862
|
-
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);
|
|
9863
9666
|
if (tasks.length !== 1) throw new Error("Handoff task no longer identifies exactly one removed worktree.");
|
|
9864
9667
|
if (tasks[0].worktreeRemoved) return;
|
|
9865
9668
|
tasks[0].worktreeRemoved = true;
|
|
@@ -9874,20 +9677,20 @@ async function applyReviewedCleanupPlan(input) {
|
|
|
9874
9677
|
return withWorktreeTransaction(() => withRepositoryWorktreeLock(input.repo, async () => {
|
|
9875
9678
|
const plan = loadReviewedCleanupPlan(input.repo, input.planId);
|
|
9876
9679
|
const claimPath = worktreeCleanupPlanPath(plan.repoRoot, plan.planId).replace(/\.json$/, ".claim");
|
|
9877
|
-
const receiptPath =
|
|
9878
|
-
assertContainedRealPath(plan.repoRoot,
|
|
9680
|
+
const receiptPath = path17.join(claimPath, "receipt.json");
|
|
9681
|
+
assertContainedRealPath(plan.repoRoot, path17.dirname(claimPath));
|
|
9879
9682
|
try {
|
|
9880
|
-
|
|
9683
|
+
fs19.mkdirSync(claimPath, { mode: 448 });
|
|
9881
9684
|
} catch (error) {
|
|
9882
9685
|
if (error.code !== "EEXIST") throw error;
|
|
9883
9686
|
assertContainedRealPath(plan.repoRoot, receiptPath);
|
|
9884
|
-
const receipt2 = JSON.parse(
|
|
9687
|
+
const receipt2 = JSON.parse(fs19.readFileSync(receiptPath, "utf-8"));
|
|
9885
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.");
|
|
9886
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))) {
|
|
9887
|
-
await new Promise((
|
|
9690
|
+
await new Promise((resolve9) => setImmediate(resolve9));
|
|
9888
9691
|
input.signal?.throwIfAborted();
|
|
9889
9692
|
try {
|
|
9890
|
-
|
|
9693
|
+
fs19.lstatSync(entry.path);
|
|
9891
9694
|
continue;
|
|
9892
9695
|
} catch (error2) {
|
|
9893
9696
|
if (error2.code !== "ENOENT") throw error2;
|
|
@@ -9909,7 +9712,7 @@ async function applyReviewedCleanupPlan(input) {
|
|
|
9909
9712
|
try {
|
|
9910
9713
|
const selected = input.select?.(plan);
|
|
9911
9714
|
for (const entry of plan.entries.filter((item) => item.decision === "remove")) {
|
|
9912
|
-
await new Promise((
|
|
9715
|
+
await new Promise((resolve9) => setImmediate(resolve9));
|
|
9913
9716
|
input.signal?.throwIfAborted();
|
|
9914
9717
|
if (selected && !selected.has(entry.path)) {
|
|
9915
9718
|
receipt.entries.push({ path: entry.path, branch: entry.branch, state: "kept", reason: "outside the current retention excess" });
|
|
@@ -9919,7 +9722,7 @@ async function applyReviewedCleanupPlan(input) {
|
|
|
9919
9722
|
withHandoffWriteLock(entry.handoffPath, () => {
|
|
9920
9723
|
const manifest = readParallelHandoffManifest(entry.handoffPath);
|
|
9921
9724
|
if (!manifest || manifest.runId !== entry.runId) throw new Error("Handoff ownership changed before cleanup validation.");
|
|
9922
|
-
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 });
|
|
9923
9726
|
const candidate = fresh.entries.find((item) => item.path === entry.path);
|
|
9924
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)) {
|
|
9925
9728
|
receipt.entries.push({ path: entry.path, branch: entry.branch, state: "kept", reason: candidate?.reasons.join("; ") || "reviewed ownership or Git facts changed" });
|
|
@@ -9929,7 +9732,7 @@ async function applyReviewedCleanupPlan(input) {
|
|
|
9929
9732
|
const journalEntry = { path: entry.path, branch: entry.branch, state: "removing", reason: "removal prepared; outcome not yet recorded" };
|
|
9930
9733
|
receipt.entries.push(journalEntry);
|
|
9931
9734
|
writePrivateAtomicJson(receiptPath, receipt);
|
|
9932
|
-
const removed =
|
|
9735
|
+
const removed = spawnSync2("git", ["-C", plan.repoRoot, "worktree", "remove", "--", entry.path], { encoding: "utf-8", windowsHide: true });
|
|
9933
9736
|
if (removed.status !== 0) throw new Error(removed.error?.message || removed.stderr.trim() || "git worktree remove failed");
|
|
9934
9737
|
journalEntry.state = "removed";
|
|
9935
9738
|
journalEntry.reason = "worktree removed; local branch retained";
|
|
@@ -10017,13 +9820,13 @@ function inheritedRunFanoutBudget(deps) {
|
|
|
10017
9820
|
return deps.childRuntime?.runFanoutBudget;
|
|
10018
9821
|
}
|
|
10019
9822
|
function resolveRequestedCwd(runtimeCwd, requestedCwd) {
|
|
10020
|
-
return requestedCwd ?
|
|
9823
|
+
return requestedCwd ? path18.resolve(runtimeCwd, requestedCwd) : runtimeCwd;
|
|
10021
9824
|
}
|
|
10022
9825
|
function readWorkflowScriptFile(requestedPath, requestedCwd, runtimeCwd) {
|
|
10023
|
-
const scriptPath =
|
|
9826
|
+
const scriptPath = path18.resolve(resolveRequestedCwd(runtimeCwd, requestedCwd), requestedPath);
|
|
10024
9827
|
let script;
|
|
10025
9828
|
try {
|
|
10026
|
-
script =
|
|
9829
|
+
script = fs20.readFileSync(scriptPath, "utf8");
|
|
10027
9830
|
} catch (error) {
|
|
10028
9831
|
return { error: `Failed to read workflow script '${scriptPath}': ${error instanceof Error ? error.message : String(error)}` };
|
|
10029
9832
|
}
|
|
@@ -10097,7 +9900,7 @@ function nestedResolutionScopeForExecutor(deps) {
|
|
|
10097
9900
|
};
|
|
10098
9901
|
}
|
|
10099
9902
|
function trustedSessionRoots(parentSessionFile, deps) {
|
|
10100
|
-
const roots = deps.config.defaultSessionDir ? [
|
|
9903
|
+
const roots = deps.config.defaultSessionDir ? [path18.resolve(deps.expandTilde(deps.config.defaultSessionDir))] : [];
|
|
10101
9904
|
if (parentSessionFile) for (const sessionFile of [parentSessionFile, ...inheritedSessionIds(parentSessionFile)]) roots.push(deps.getSubagentSessionRoot(sessionFile));
|
|
10102
9905
|
return [...new Set(roots)];
|
|
10103
9906
|
}
|
|
@@ -10427,9 +10230,9 @@ function resolveForegroundResumeTarget(params, state, options = {}) {
|
|
|
10427
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.`);
|
|
10428
10231
|
const child = run.children[index];
|
|
10429
10232
|
if (!child.sessionFile) throw new Error(`Foreground run '${run.runId}' child ${index} does not have a persisted session file to resume from.`);
|
|
10430
|
-
if (
|
|
10431
|
-
const sessionFile =
|
|
10432
|
-
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}`);
|
|
10433
10236
|
return {
|
|
10434
10237
|
runId: run.runId,
|
|
10435
10238
|
mode: run.mode,
|
|
@@ -10480,7 +10283,7 @@ function resolveResumeTarget(params, state, options = {}) {
|
|
|
10480
10283
|
foregroundError = error;
|
|
10481
10284
|
}
|
|
10482
10285
|
try {
|
|
10483
|
-
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;
|
|
10484
10287
|
asyncTarget = {
|
|
10485
10288
|
source: "async",
|
|
10486
10289
|
...resolveAsyncResumeTarget(asyncParams, {}, compactOptional({
|
|
@@ -10516,7 +10319,7 @@ function resolveResumeTarget(params, state, options = {}) {
|
|
|
10516
10319
|
function getAsyncInterruptTarget(state, runId, location, options = {}) {
|
|
10517
10320
|
if (location?.asyncDir) {
|
|
10518
10321
|
return {
|
|
10519
|
-
asyncId: location.resolvedId ?? runId ??
|
|
10322
|
+
asyncId: location.resolvedId ?? runId ?? path18.basename(location.asyncDir),
|
|
10520
10323
|
asyncDir: location.asyncDir
|
|
10521
10324
|
};
|
|
10522
10325
|
}
|
|
@@ -10565,7 +10368,7 @@ function persistAsyncWorkflowControlEvent(input) {
|
|
|
10565
10368
|
} : {}
|
|
10566
10369
|
};
|
|
10567
10370
|
try {
|
|
10568
|
-
|
|
10371
|
+
fs20.appendFileSync(path18.join(input.job.asyncDir, "events.jsonl"), `${JSON.stringify(record2)}
|
|
10569
10372
|
`, "utf-8");
|
|
10570
10373
|
} catch (error) {
|
|
10571
10374
|
if (!isStorageCapacityError(error)) throw error;
|
|
@@ -10885,25 +10688,25 @@ function nestedRunAgent(run) {
|
|
|
10885
10688
|
return run.agent ?? run.agents?.[0] ?? (run.steps?.length === 1 ? run.steps[0]?.agent : void 0);
|
|
10886
10689
|
}
|
|
10887
10690
|
function pathWithin(base, candidate) {
|
|
10888
|
-
const resolvedBase =
|
|
10889
|
-
const resolvedCandidate =
|
|
10890
|
-
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}`);
|
|
10891
10694
|
}
|
|
10892
10695
|
function validateNestedSessionFile(run, trustedSessionRoots2, resolveSource, visited = []) {
|
|
10893
10696
|
const sessionFile = nestedRunSessionFile(run);
|
|
10894
10697
|
if (!sessionFile) throw new Error(`Nested run '${run.id}' does not have a persisted session file to resume from.`);
|
|
10895
|
-
if (
|
|
10896
|
-
const resolved =
|
|
10897
|
-
if (!
|
|
10898
|
-
if (!
|
|
10899
|
-
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);
|
|
10900
10703
|
if (!stat.isFile() || stat.isSymbolicLink()) throw new Error(`Nested run '${run.id}' session file is not a regular file: ${sessionFile}`);
|
|
10901
|
-
const realSessionFile =
|
|
10902
|
-
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));
|
|
10903
10706
|
if (!trustedRoots.some((root) => pathWithin(root, realSessionFile))) {
|
|
10904
10707
|
throw new Error(`Nested run '${run.id}' session file is outside trusted nested session roots: ${sessionFile}`);
|
|
10905
10708
|
}
|
|
10906
|
-
if (!realSessionFile.split(
|
|
10709
|
+
if (!realSessionFile.split(path18.sep).includes(run.id)) {
|
|
10907
10710
|
const sourceId = run.revivedFrom;
|
|
10908
10711
|
const chain = [...visited, run.id];
|
|
10909
10712
|
const source = sourceId && chain.length <= 8 && !chain.includes(sourceId) ? resolveSource(sourceId) : void 0;
|
|
@@ -10944,7 +10747,7 @@ function resolveNestedResumeTarget(match, trustedSessionRoots2, resolveSource) {
|
|
|
10944
10747
|
state,
|
|
10945
10748
|
agent,
|
|
10946
10749
|
index: 0,
|
|
10947
|
-
cwd: asyncDir ?
|
|
10750
|
+
cwd: asyncDir ? path18.dirname(asyncDir) : void 0,
|
|
10948
10751
|
sessionFile: validateNestedSessionFile(run, trustedSessionRoots2, resolveSource),
|
|
10949
10752
|
...run.capabilityCeiling ? { capabilityCeiling: run.capabilityCeiling } : {},
|
|
10950
10753
|
...recoveryDescriptor ? { recoveryDescriptor } : {}
|
|
@@ -10991,7 +10794,7 @@ async function waitForNestedControlResult(target, requestId, ignoredFiles, timeo
|
|
|
10991
10794
|
while (Date.now() < deadline) {
|
|
10992
10795
|
const result2 = findNestedControlResult(target.match.route, requestId, target.match.run.id, ignoredFiles);
|
|
10993
10796
|
if (result2) return result2;
|
|
10994
|
-
await new Promise((
|
|
10797
|
+
await new Promise((resolve9) => setTimeout(resolve9, 50));
|
|
10995
10798
|
}
|
|
10996
10799
|
return void 0;
|
|
10997
10800
|
}
|
|
@@ -11012,7 +10815,7 @@ function directNestedAsyncInterrupt(target) {
|
|
|
11012
10815
|
const run = target.match.run;
|
|
11013
10816
|
const asyncDir = resolveNestedAsyncDir(target.match.rootRunId, run);
|
|
11014
10817
|
if (!asyncDir) return void 0;
|
|
11015
|
-
const status = reconcileAsyncRun(asyncDir, { resultsDir:
|
|
10818
|
+
const status = reconcileAsyncRun(asyncDir, { resultsDir: path18.join(DIRS.results, "nested", target.match.rootRunId) }).status;
|
|
11016
10819
|
const pid = typeof status?.pid === "number" && status.pid > 0 ? status.pid : run.pid;
|
|
11017
10820
|
if (!status || status.state !== "running" || typeof pid !== "number" || pid <= 0) return void 0;
|
|
11018
10821
|
try {
|
|
@@ -11027,7 +10830,7 @@ async function directNestedAsyncSteer(input) {
|
|
|
11027
10830
|
const run = input.target.match.run;
|
|
11028
10831
|
const asyncDir = resolveNestedAsyncDir(input.target.match.rootRunId, run);
|
|
11029
10832
|
if (!asyncDir) return void 0;
|
|
11030
|
-
const status = reconcileAsyncRun(asyncDir, { resultsDir:
|
|
10833
|
+
const status = reconcileAsyncRun(asyncDir, { resultsDir: path18.join(DIRS.results, "nested", input.target.match.rootRunId) }).status;
|
|
11031
10834
|
if (!status || status.state !== "running" && status.state !== "queued") return void 0;
|
|
11032
10835
|
const steps = status.steps ?? [];
|
|
11033
10836
|
if (input.index !== void 0) {
|
|
@@ -11136,10 +10939,10 @@ async function resumeExternalJobFollowUp(input) {
|
|
|
11136
10939
|
const runId = externalJobFollowUpRunId(requestDigest);
|
|
11137
10940
|
const nestedRoute = inheritedNestedRoute(input.deps);
|
|
11138
10941
|
const runRoots = nestedRoute ? nestedRunScope(nestedRoute.rootRunId) : { asyncDirRoot: DIRS.async, resultsDir: DIRS.results };
|
|
11139
|
-
const asyncDir =
|
|
10942
|
+
const asyncDir = path18.join(runRoots.asyncDirRoot, runId);
|
|
11140
10943
|
const currentSessionId = input.deps.state.currentSessionId;
|
|
11141
10944
|
if (!currentSessionId) return { content: [{ type: "text", text: "External-job follow-up requires an active parent session." }], isError: true, details: { mode: "management", results: [] } };
|
|
11142
|
-
if (
|
|
10945
|
+
if (fs20.existsSync(asyncDir) || fs20.existsSync(resultFilePath(runRoots.resultsDir, runId))) {
|
|
11143
10946
|
return externalJobFollowUpStarted({ sourceRunId: input.target.runId, runId, asyncDir, duplicate: true, interactive: input.ctx.hasUI });
|
|
11144
10947
|
}
|
|
11145
10948
|
const depthState = checkSubagentDepth(input.deps.config.maxSubagentDepth, input.deps.childRuntime);
|
|
@@ -11340,7 +11143,7 @@ async function resumeAsyncRun(input) {
|
|
|
11340
11143
|
inheritGlobalContext: recoveryDescriptor.inheritGlobalContext,
|
|
11341
11144
|
inheritSkills: recoveryDescriptor.inheritSkills,
|
|
11342
11145
|
source: "project",
|
|
11343
|
-
filePath: recoveryDescriptor.agentFilePath ??
|
|
11146
|
+
filePath: recoveryDescriptor.agentFilePath ?? path18.join(getProjectSubagentsDir(recoveryDescriptor.cwd), "recovery-agent"),
|
|
11344
11147
|
...discovered.maxThinking ? { maxThinking: discovered.maxThinking } : {}
|
|
11345
11148
|
} : void 0);
|
|
11346
11149
|
if (!baseAgentConfig) {
|
|
@@ -11361,7 +11164,7 @@ async function resumeAsyncRun(input) {
|
|
|
11361
11164
|
if (target.source === "async" && target.managedWorktree && target.asyncDir) {
|
|
11362
11165
|
try {
|
|
11363
11166
|
const protectedCwd = await protectRetainedWorktreeForResume(parallelHandoffPath(target.asyncDir), target.runId, target.index, input.signal);
|
|
11364
|
-
if (
|
|
11167
|
+
if (path18.resolve(protectedCwd) !== path18.resolve(effectiveCwd)) throw new Error("Retained worktree cwd changed during resume admission.");
|
|
11365
11168
|
} catch (error) {
|
|
11366
11169
|
return { content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }], isError: true, details: { mode: "management", results: [] } };
|
|
11367
11170
|
}
|
|
@@ -11414,7 +11217,7 @@ async function resumeAsyncRun(input) {
|
|
|
11414
11217
|
limit: resolveMaxActiveAsyncRunsPerSession(input.deps.config.maxActiveAsyncRunsPerSession),
|
|
11415
11218
|
runId: runId2,
|
|
11416
11219
|
kind: "runner",
|
|
11417
|
-
asyncDir:
|
|
11220
|
+
asyncDir: path18.join(DIRS.async, runId2)
|
|
11418
11221
|
}, { liveWorkflowRunIds: new Set(input.deps.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(input.deps.config.capacity?.abandonedSlotReleaseAfterMs) }) : void 0;
|
|
11419
11222
|
} catch (error) {
|
|
11420
11223
|
if (error instanceof ActiveAsyncCapacityError) return { content: [{ type: "text", text: error.message }], isError: true, details: { mode: "chain", results: [], activeAsyncCapacity: error.snapshot } };
|
|
@@ -11434,7 +11237,7 @@ async function resumeAsyncRun(input) {
|
|
|
11434
11237
|
goal,
|
|
11435
11238
|
attachRoot: {
|
|
11436
11239
|
runId: target.runId,
|
|
11437
|
-
asyncDir: target.asyncDir ??
|
|
11240
|
+
asyncDir: target.asyncDir ?? path18.join(DIRS.async, target.runId),
|
|
11438
11241
|
resultPath: resolveAsyncRootResultPath(DIRS.results, target.runId),
|
|
11439
11242
|
index: target.index,
|
|
11440
11243
|
agent: target.agent,
|
|
@@ -11538,13 +11341,13 @@ async function resumeAsyncRun(input) {
|
|
|
11538
11341
|
limit: resolveMaxActiveAsyncRunsPerSession(input.deps.config.maxActiveAsyncRunsPerSession),
|
|
11539
11342
|
sourceRunId: target.runId,
|
|
11540
11343
|
runId,
|
|
11541
|
-
asyncDir:
|
|
11344
|
+
asyncDir: path18.join(DIRS.async, runId)
|
|
11542
11345
|
}, { abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(input.deps.config.capacity?.abandonedSlotReleaseAfterMs) }) : acquireActiveAsyncCapacity({
|
|
11543
11346
|
sessionId: input.deps.state.currentSessionId,
|
|
11544
11347
|
limit: resolveMaxActiveAsyncRunsPerSession(input.deps.config.maxActiveAsyncRunsPerSession),
|
|
11545
11348
|
runId,
|
|
11546
11349
|
kind: "runner",
|
|
11547
|
-
asyncDir:
|
|
11350
|
+
asyncDir: path18.join(DIRS.async, runId)
|
|
11548
11351
|
}, { abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(input.deps.config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
11549
11352
|
} catch (error) {
|
|
11550
11353
|
if (error instanceof ActiveAsyncCapacityError) return { content: [{ type: "text", text: error.message }], isError: true, details: { mode: "single", results: [], activeAsyncCapacity: error.snapshot } };
|
|
@@ -11555,7 +11358,7 @@ async function resumeAsyncRun(input) {
|
|
|
11555
11358
|
const artifactsDir = recoveryDescriptor?.artifactsDir ?? getArtifactsDir(parentSessionFile, effectiveCwd, artifactConfig.dir);
|
|
11556
11359
|
const availableModels = input.ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
11557
11360
|
const parentModel = input.parentModel;
|
|
11558
|
-
const revivalAsyncDir =
|
|
11361
|
+
const revivalAsyncDir = path18.join(DIRS.async, runId);
|
|
11559
11362
|
const revivalOutputBaseDir = resolveSingleRunOutputBaseDir(input.deps, artifactsDir, runId);
|
|
11560
11363
|
const inheritedOutput = foregroundContract?.output ?? recoveryDescriptor?.outputPath;
|
|
11561
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;
|
|
@@ -11663,12 +11466,12 @@ async function resumeAsyncRun(input) {
|
|
|
11663
11466
|
return result2;
|
|
11664
11467
|
}
|
|
11665
11468
|
notifyLaunch();
|
|
11666
|
-
for (const brief of queuedBriefs)
|
|
11469
|
+
for (const brief of queuedBriefs) fs20.rmSync(brief.path, { force: true });
|
|
11667
11470
|
if (queuedBriefs.length > 0 && sourceAsyncDir) {
|
|
11668
11471
|
const sourceStatus = readStatus(sourceAsyncDir);
|
|
11669
11472
|
if (sourceStatus?.steering) {
|
|
11670
11473
|
for (const brief of queuedBriefs) updateSteeringTarget(sourceStatus.steering, brief.request.id, target.index, "delivered", Date.now());
|
|
11671
|
-
createCapacityResilientJsonWriter({ keepAlive: true }).write(
|
|
11474
|
+
createCapacityResilientJsonWriter({ keepAlive: true }).write(path18.join(sourceAsyncDir, "status.json"), sourceStatus);
|
|
11672
11475
|
}
|
|
11673
11476
|
}
|
|
11674
11477
|
const revivedId = result2.details.asyncId ?? runId;
|
|
@@ -11730,7 +11533,7 @@ function formatFailedSingleRunOutput(result2, displayOutput) {
|
|
|
11730
11533
|
cappedOutput = capInlineOutput(output, { artifactPath: result2.artifactPaths?.outputPath, savedPath: result2.savedOutputPath });
|
|
11731
11534
|
lines.push("", "Output:", cappedOutput);
|
|
11732
11535
|
}
|
|
11733
|
-
if (cappedOutput === output && result2.artifactPaths?.outputPath &&
|
|
11536
|
+
if (cappedOutput === output && result2.artifactPaths?.outputPath && fs20.existsSync(result2.artifactPaths.outputPath)) {
|
|
11734
11537
|
lines.push("", `Output artifact: ${result2.artifactPaths.outputPath}`);
|
|
11735
11538
|
}
|
|
11736
11539
|
return lines.join("\n");
|
|
@@ -11823,7 +11626,7 @@ async function maybeBuildForegroundIntercomReceipt(input) {
|
|
|
11823
11626
|
}
|
|
11824
11627
|
function diagnosticContextFromDiscovery(discovered, cwd, scope) {
|
|
11825
11628
|
if (discovered.cwd && discovered.scope && discovered.directories) return unknownAgentDiagnosticContext({ ...discovered, cwd: discovered.cwd, scope: discovered.scope, directories: discovered.directories });
|
|
11826
|
-
return unknownAgentDiagnosticContext(discoverAgents(
|
|
11629
|
+
return unknownAgentDiagnosticContext(discoverAgents(path18.resolve(cwd), scope));
|
|
11827
11630
|
}
|
|
11828
11631
|
function canonicalizeAgentName(name, agents, diagnostics, context) {
|
|
11829
11632
|
const resolved = resolveAgentName(name, agents);
|
|
@@ -11856,12 +11659,12 @@ function workflowValidationOptions(deps, params, runtimeCwd, parentModelProvider
|
|
|
11856
11659
|
};
|
|
11857
11660
|
}
|
|
11858
11661
|
function canonicalizeExecutionParams(params, agents, diagnostics, context) {
|
|
11859
|
-
const
|
|
11662
|
+
const resolve9 = (name, location) => {
|
|
11860
11663
|
const result2 = canonicalizeAgentName(name, agents, diagnostics, context);
|
|
11861
11664
|
return result2.error && location ? { error: `${result2.error} (${location})` } : result2;
|
|
11862
11665
|
};
|
|
11863
11666
|
if (params.agent) {
|
|
11864
|
-
const result2 =
|
|
11667
|
+
const result2 = resolve9(params.agent);
|
|
11865
11668
|
if (result2.error) return { error: result2.error };
|
|
11866
11669
|
params = omitUndefinedProperties({ ...params, agent: result2.name });
|
|
11867
11670
|
const agent = agents.find((candidate) => candidate.name === result2.name);
|
|
@@ -11880,7 +11683,7 @@ function canonicalizeExecutionParams(params, agents, diagnostics, context) {
|
|
|
11880
11683
|
const tasks = [];
|
|
11881
11684
|
for (let index = 0; index < params.tasks.length; index++) {
|
|
11882
11685
|
const task = params.tasks[index];
|
|
11883
|
-
const result2 =
|
|
11686
|
+
const result2 = resolve9(task.agent, `task ${index + 1}`);
|
|
11884
11687
|
if (result2.error) return { error: result2.error };
|
|
11885
11688
|
tasks.push({ ...task, agent: result2.name });
|
|
11886
11689
|
}
|
|
@@ -11894,7 +11697,7 @@ function canonicalizeExecutionParams(params, agents, diagnostics, context) {
|
|
|
11894
11697
|
const parallel = [];
|
|
11895
11698
|
for (let taskIndex = 0; taskIndex < step.parallel.length; taskIndex++) {
|
|
11896
11699
|
const task = step.parallel[taskIndex];
|
|
11897
|
-
const result2 =
|
|
11700
|
+
const result2 = resolve9(task.agent, `step ${index + 1}, task ${taskIndex + 1}`);
|
|
11898
11701
|
if (result2.error) return { error: result2.error };
|
|
11899
11702
|
parallel.push({ ...task, agent: result2.name });
|
|
11900
11703
|
}
|
|
@@ -11902,13 +11705,13 @@ function canonicalizeExecutionParams(params, agents, diagnostics, context) {
|
|
|
11902
11705
|
continue;
|
|
11903
11706
|
}
|
|
11904
11707
|
if (isDynamicParallelStep(step)) {
|
|
11905
|
-
const result2 =
|
|
11708
|
+
const result2 = resolve9(step.parallel.agent, `step ${index + 1}`);
|
|
11906
11709
|
if (result2.error) return { error: result2.error };
|
|
11907
11710
|
chain.push({ ...step, parallel: { ...step.parallel, agent: result2.name } });
|
|
11908
11711
|
continue;
|
|
11909
11712
|
}
|
|
11910
11713
|
if ("agent" in step && typeof step.agent === "string") {
|
|
11911
|
-
const result2 =
|
|
11714
|
+
const result2 = resolve9(step.agent, `step ${index + 1}`);
|
|
11912
11715
|
if (result2.error) return { error: result2.error };
|
|
11913
11716
|
chain.push({ ...step, agent: result2.name });
|
|
11914
11717
|
continue;
|
|
@@ -12775,7 +12578,7 @@ async function createSingleWorktreeSetup(cwd, runId, agent, setupHook, setupHook
|
|
|
12775
12578
|
}
|
|
12776
12579
|
}
|
|
12777
12580
|
function resolveConfiguredSingleRunOutputBaseDir(deps) {
|
|
12778
|
-
return deps.config.singleRunOutputBaseDir ?
|
|
12581
|
+
return deps.config.singleRunOutputBaseDir ? path18.resolve(deps.expandTilde(deps.config.singleRunOutputBaseDir)) : void 0;
|
|
12779
12582
|
}
|
|
12780
12583
|
function sanitizeRunPathSegment(value, maxBytes = 120) {
|
|
12781
12584
|
const sanitized = value.trim().replace(/[^A-Za-z0-9._-]+/g, "_").replace(/^_+|_+$/g, "");
|
|
@@ -12784,22 +12587,22 @@ function sanitizeRunPathSegment(value, maxBytes = 120) {
|
|
|
12784
12587
|
return sanitized.slice(0, maxBytes).replace(/_+$/, "") || "unknown";
|
|
12785
12588
|
}
|
|
12786
12589
|
function resolveSingleRunOutputBaseDir(deps, artifactsDir, runId) {
|
|
12787
|
-
return resolveConfiguredSingleRunOutputBaseDir(deps) ??
|
|
12590
|
+
return resolveConfiguredSingleRunOutputBaseDir(deps) ?? path18.join(artifactsDir, "outputs", sanitizeRunPathSegment(runId));
|
|
12788
12591
|
}
|
|
12789
12592
|
function suffixedOutputPath(outputPath, suffix) {
|
|
12790
|
-
const parsed =
|
|
12791
|
-
return
|
|
12593
|
+
const parsed = path18.parse(outputPath);
|
|
12594
|
+
return path18.join(parsed.dir, `${parsed.name}.${suffix}${parsed.ext || ".md"}`);
|
|
12792
12595
|
}
|
|
12793
12596
|
function workflowChildDefaultOutput(aggregateOutputPath, artifactsDir, workflowRunId, workflowKey) {
|
|
12794
12597
|
if (aggregateOutputPath) return suffixedOutputPath(aggregateOutputPath, workflowKey);
|
|
12795
|
-
return
|
|
12598
|
+
return path18.join(artifactsDir, "outputs", sanitizeRunPathSegment(workflowRunId), `${workflowKey}.md`);
|
|
12796
12599
|
}
|
|
12797
12600
|
function workflowHostCommandRunner(input) {
|
|
12798
12601
|
return async (key, params, signal) => {
|
|
12799
12602
|
const authorizationError = input.authorize?.(key, params);
|
|
12800
12603
|
if (authorizationError) throw new Error(authorizationError);
|
|
12801
|
-
const defaultOutputPath =
|
|
12802
|
-
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;
|
|
12803
12606
|
const claimPath = resolveWorkflowHostOutputClaimPath(outputPath);
|
|
12804
12607
|
const previous = input.claimedOutputPaths.get(claimPath);
|
|
12805
12608
|
if (previous) throw new Error(`runs.host('${key}') output path is already claimed by '${previous}': ${outputPath}.`);
|
|
@@ -12813,8 +12616,8 @@ function writeWorkflowAggregateOutput(outputPath, text, producedChildOutputPaths
|
|
|
12813
12616
|
if (!outputPath) return void 0;
|
|
12814
12617
|
try {
|
|
12815
12618
|
if (producedChildOutputPaths.has(resolveWorkflowHostOutputClaimPath(outputPath))) return void 0;
|
|
12816
|
-
|
|
12817
|
-
|
|
12619
|
+
fs20.mkdirSync(path18.dirname(outputPath), { recursive: true });
|
|
12620
|
+
fs20.writeFileSync(outputPath, text, "utf-8");
|
|
12818
12621
|
return void 0;
|
|
12819
12622
|
} catch (error) {
|
|
12820
12623
|
return `Output file error: ${outputPath}
|
|
@@ -12885,7 +12688,7 @@ function resolveWorkflowChildOutputPath(input) {
|
|
|
12885
12688
|
}
|
|
12886
12689
|
const output = rawOutput === true || rawOutput === "true" ? agentOutput : hasExplicitOutput ? rawOutput : input.aggregateOutputPath ? workflowChildDefaultOutput(input.aggregateOutputPath, input.artifactsDir, input.workflowRunId, input.key) : agentOutput;
|
|
12887
12690
|
return {
|
|
12888
|
-
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))),
|
|
12889
12692
|
inherited: !hasExplicitOutput && !input.aggregateOutputPath && agentOutput !== void 0
|
|
12890
12693
|
};
|
|
12891
12694
|
}
|
|
@@ -12950,7 +12753,7 @@ async function finalizeSingleWorktreeHandoff(input) {
|
|
|
12950
12753
|
try {
|
|
12951
12754
|
return await withWorktreeTransaction(() => {
|
|
12952
12755
|
admitted = true;
|
|
12953
|
-
const diffsDir =
|
|
12756
|
+
const diffsDir = path18.join(input.artifactsDir, "worktree-diffs", input.runId);
|
|
12954
12757
|
const diffs = diffWorktrees(input.worktreeSetup, [input.agent], diffsDir);
|
|
12955
12758
|
const diffSummary = formatWorktreeDiffSummary(diffs);
|
|
12956
12759
|
const manifestPath = parallelHandoffPath(input.artifactsDir, input.runId);
|
|
@@ -13196,7 +12999,7 @@ async function runSinglePath(data, deps) {
|
|
|
13196
12999
|
await cleanupSingleWorktree();
|
|
13197
13000
|
return { content: [{ type: "text", text: validationError }], isError: true, details: { mode: "single", results: [], parallelHandoff: pendingHandoff } };
|
|
13198
13001
|
}
|
|
13199
|
-
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;
|
|
13200
13003
|
const reads = readsOverride !== void 0 ? readsOverride : agentConfig.defaultReads ?? false;
|
|
13201
13004
|
const readPaths = !foregroundMachine && Array.isArray(reads) ? resolveExistingReadPaths(reads, singleCwd) : [];
|
|
13202
13005
|
const readsInstruction = readPaths.length > 0 ? `[Read from: ${readPaths.join(", ")}]
|
|
@@ -13280,8 +13083,8 @@ async function runSinglePath(data, deps) {
|
|
|
13280
13083
|
};
|
|
13281
13084
|
let r;
|
|
13282
13085
|
let resolveDetachedWorkflowChild;
|
|
13283
|
-
const detachedWorkflowChild = params.workflowAwaitDetached === true ? new Promise((
|
|
13284
|
-
resolveDetachedWorkflowChild =
|
|
13086
|
+
const detachedWorkflowChild = params.workflowAwaitDetached === true ? new Promise((resolve9) => {
|
|
13087
|
+
resolveDetachedWorkflowChild = resolve9;
|
|
13285
13088
|
}) : void 0;
|
|
13286
13089
|
try {
|
|
13287
13090
|
const childSessionFile = sessionFileForTask(params.agent, 0, modelOverride, modelOverrideFromParent, modelOrigin);
|
|
@@ -13426,7 +13229,7 @@ async function runSinglePath(data, deps) {
|
|
|
13426
13229
|
}));
|
|
13427
13230
|
if (launched.detached && foregroundControl) {
|
|
13428
13231
|
foregroundControl.stopForRuntimeReplacement = () => {
|
|
13429
|
-
const sessionFile = childSessionFile &&
|
|
13232
|
+
const sessionFile = childSessionFile && fs20.existsSync(childSessionFile) ? childSessionFile : void 0;
|
|
13430
13233
|
const stopped = { ...launched, detached: void 0, stopped: true, exitCode: 1, error: RUNTIME_REPLACED_STOP_ERROR, finalOutput: RUNTIME_REPLACED_STOP_ERROR, sessionFile };
|
|
13431
13234
|
rememberForegroundRun(deps.state, { modelResponseAliases, runId, mode: "single", cwd: singleCwd, sessionId: data.parentSessionId, results: [stopped], params, effectiveOutput, effectiveOutputMode, extensionBindings: params.extensionBindings, requiredExtensions });
|
|
13432
13235
|
if (childSessionControls) childSessionControls.stop(RUNTIME_REPLACED_STOP_ERROR);
|
|
@@ -13594,8 +13397,8 @@ function bindMissionWorkflowChildAsyncLaunch(params, binding, asyncByDefault, as
|
|
|
13594
13397
|
const requestedAsync = params.async ?? asyncByDefault;
|
|
13595
13398
|
if (!binding || !requestedAsync || params.clarify === true) return params;
|
|
13596
13399
|
const id = asyncId.trim();
|
|
13597
|
-
if (!id ||
|
|
13598
|
-
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);
|
|
13599
13402
|
return { ...params, workflowChildAsyncId: id };
|
|
13600
13403
|
}
|
|
13601
13404
|
function workflowChildResult(key, result2, childParams = {}, resumeState, forcedTerminalOutcome) {
|
|
@@ -13736,7 +13539,7 @@ var WORKFLOW_STEP_STATES = /* @__PURE__ */ new Set(["pending", "running", "compl
|
|
|
13736
13539
|
function isSafeWorkflowChildRunId(value) {
|
|
13737
13540
|
if (typeof value !== "string") return false;
|
|
13738
13541
|
const runId = value.trim();
|
|
13739
|
-
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);
|
|
13740
13543
|
}
|
|
13741
13544
|
function isMissingWorkflowReceiptDiagnostic(error, workflowRunId) {
|
|
13742
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.`);
|
|
@@ -13745,7 +13548,7 @@ function missingWorkflowReceiptResumeHint(reference, state) {
|
|
|
13745
13548
|
try {
|
|
13746
13549
|
if (!state.currentSessionId) return void 0;
|
|
13747
13550
|
const workflowRunId = reference.workflowRunId.trim();
|
|
13748
|
-
const workflowStatus = readStatus(
|
|
13551
|
+
const workflowStatus = readStatus(path18.join(DIRS.async, workflowRunId));
|
|
13749
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) => {
|
|
13750
13553
|
if (!step || typeof step !== "object" || Array.isArray(step)) return true;
|
|
13751
13554
|
const record2 = step;
|
|
@@ -13780,7 +13583,7 @@ function resolveWorkflowResume(reference, deps, parentSessionFile, index) {
|
|
|
13780
13583
|
if (status?.runId === runId && status.parentWorkflowRunId && status.workflowKey) {
|
|
13781
13584
|
const receiptPath = workflowReceiptPath(DIRS.async, status.parentWorkflowRunId);
|
|
13782
13585
|
try {
|
|
13783
|
-
|
|
13586
|
+
fs20.statSync(receiptPath);
|
|
13784
13587
|
} catch (error) {
|
|
13785
13588
|
if (error.code === "ENOENT") return { runId, runIds: [runId] };
|
|
13786
13589
|
throw error;
|
|
@@ -13839,11 +13642,11 @@ async function steerWorkflowChildByKey(input) {
|
|
|
13839
13642
|
});
|
|
13840
13643
|
if (!result2.details.steering?.targets.some((target) => target.reason === CHILD_SESSION_NOT_RUNNING_YET) || Date.now() >= deadline) return workflowSteerReceipt(input.key, result2);
|
|
13841
13644
|
}
|
|
13842
|
-
const workflowStatus = readStatus(
|
|
13645
|
+
const workflowStatus = readStatus(path18.join(asyncDirRoot, input.workflowRunId));
|
|
13843
13646
|
const step = workflowStatus?.steps?.find((candidate) => candidate.workflowKey === input.key);
|
|
13844
13647
|
const childRunId = step?.runId ?? input.resolveRunId?.();
|
|
13845
13648
|
if (childRunId) {
|
|
13846
|
-
const asyncDir =
|
|
13649
|
+
const asyncDir = path18.join(asyncDirRoot, childRunId);
|
|
13847
13650
|
const childStatus = reconcileAsyncRun(asyncDir).status;
|
|
13848
13651
|
if (childStatus && childStatus.state !== "running" && childStatus.state !== "queued" && (input.options.mode !== "follow_up" || !canQueueRetainedAsyncFollowUp(childStatus, input.options.index))) {
|
|
13849
13652
|
return { key: input.key, state: "missed", error: `Workflow child '${input.key}' is ${childStatus.state}.` };
|
|
@@ -13873,7 +13676,7 @@ async function steerWorkflowChildByKey(input) {
|
|
|
13873
13676
|
if (input.signal?.aborted || Date.now() >= deadline) {
|
|
13874
13677
|
return { key: input.key, state: "missed", error: `Workflow child '${input.key}' had no live steering route.` };
|
|
13875
13678
|
}
|
|
13876
|
-
await new Promise((
|
|
13679
|
+
await new Promise((resolve9) => setTimeout(resolve9, Math.min(10, Math.max(1, deadline - Date.now()))));
|
|
13877
13680
|
}
|
|
13878
13681
|
}
|
|
13879
13682
|
async function preflightWorkflowWorktrees(input) {
|
|
@@ -14319,7 +14122,7 @@ function createSubagentExecutor(deps) {
|
|
|
14319
14122
|
limit: resolveMaxActiveAsyncRunsPerSession(deps2.config.maxActiveAsyncRunsPerSession),
|
|
14320
14123
|
runId: workflowRunId,
|
|
14321
14124
|
kind: "workflow",
|
|
14322
|
-
asyncDir:
|
|
14125
|
+
asyncDir: path18.join(DIRS.async, workflowRunId)
|
|
14323
14126
|
}, { liveWorkflowRunIds: new Set(deps2.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps2.config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
14324
14127
|
} catch (error) {
|
|
14325
14128
|
if (error instanceof ActiveAsyncCapacityError) {
|
|
@@ -14365,11 +14168,11 @@ function createSubagentExecutor(deps) {
|
|
|
14365
14168
|
};
|
|
14366
14169
|
if (workflowRunId) {
|
|
14367
14170
|
const toolCallId = _id;
|
|
14368
|
-
const workflowSessionRoot = requestParams.sessionDir ?
|
|
14369
|
-
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);
|
|
14370
14173
|
const resultPath = resultFilePath(DIRS.results, workflowRunId);
|
|
14371
|
-
const statusPath =
|
|
14372
|
-
const eventsPath =
|
|
14174
|
+
const statusPath = path18.join(asyncDir, "status.json");
|
|
14175
|
+
const eventsPath = path18.join(asyncDir, "events.jsonl");
|
|
14373
14176
|
const startedAt = Date.now();
|
|
14374
14177
|
const currentSessionId = resolveCurrentSessionId(ctx.sessionManager);
|
|
14375
14178
|
const workflowCapabilityCeiling2 = intersectSubagentCapabilityCeilings(requestParams.capabilityCeiling, resolveCurrentSubagentCapabilityCeiling(currentSessionId));
|
|
@@ -14385,7 +14188,7 @@ function createSubagentExecutor(deps) {
|
|
|
14385
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: [] } };
|
|
14386
14189
|
};
|
|
14387
14190
|
if (workflowReuseSource) {
|
|
14388
|
-
const reuseSourceDir =
|
|
14191
|
+
const reuseSourceDir = path18.join(DIRS.async, workflowReuseSource.runId);
|
|
14389
14192
|
let claim;
|
|
14390
14193
|
try {
|
|
14391
14194
|
claim = claimWorkflowResume(reuseSourceDir, { newRunId: workflowRunId, trigger: "reuse" });
|
|
@@ -14397,14 +14200,14 @@ function createSubagentExecutor(deps) {
|
|
|
14397
14200
|
return { content: [{ type: "text", text: claimedWorkflowRunRefusal(workflowReuseSource.runId, claim, requestParams.reuseFrom !== void 0) }], isError: true, details: { mode: "workflow", results: [] } };
|
|
14398
14201
|
}
|
|
14399
14202
|
pendingReuseClaimReleases.set(params, () => {
|
|
14400
|
-
if (!
|
|
14203
|
+
if (!fs20.existsSync(statusPath)) releaseWorkflowResumeClaim(reuseSourceDir, workflowRunId);
|
|
14401
14204
|
});
|
|
14402
14205
|
}
|
|
14403
14206
|
try {
|
|
14404
|
-
|
|
14207
|
+
fs20.mkdirSync(asyncDir, { recursive: true });
|
|
14405
14208
|
writeRunFanoutBudgetDescriptor(asyncDir, workflowFanoutBudget);
|
|
14406
14209
|
if (topLevelAsyncWorkflow && publicWorkflowLaunch) writeWorkflowLaunchRecord(asyncDir, workflowLaunchRecord(publicWorkflowLaunch, workflowCwd, workflowScriptSourceDigest));
|
|
14407
|
-
|
|
14210
|
+
fs20.mkdirSync(DIRS.results, { recursive: true });
|
|
14408
14211
|
} catch (error) {
|
|
14409
14212
|
return storageFailure(error);
|
|
14410
14213
|
}
|
|
@@ -14437,7 +14240,7 @@ function createSubagentExecutor(deps) {
|
|
|
14437
14240
|
};
|
|
14438
14241
|
const appendWorkflowEvent = (event) => {
|
|
14439
14242
|
try {
|
|
14440
|
-
|
|
14243
|
+
fs20.appendFileSync(eventsPath, `${JSON.stringify({ ts: Date.now(), runId: workflowRunId, ...event })}
|
|
14441
14244
|
`, "utf-8");
|
|
14442
14245
|
} catch (error) {
|
|
14443
14246
|
console.error(`Failed to append async workflow event '${eventsPath}':`, error);
|
|
@@ -14466,7 +14269,7 @@ function createSubagentExecutor(deps) {
|
|
|
14466
14269
|
const message = `Failed to write async workflow result ${resultPath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
14467
14270
|
console.error(message, error);
|
|
14468
14271
|
try {
|
|
14469
|
-
if (currentSessionId && fallbackResultPayloadPathForSessionRun(
|
|
14272
|
+
if (currentSessionId && fallbackResultPayloadPathForSessionRun(path18.dirname(resultPath), currentSessionId, workflowRunId)) return true;
|
|
14470
14273
|
} catch (pendingError) {
|
|
14471
14274
|
console.error(`Failed to verify pending async workflow result '${workflowRunId}':`, pendingError);
|
|
14472
14275
|
}
|
|
@@ -14574,8 +14377,8 @@ Parent action: ${STATUS_ACTION}`,
|
|
|
14574
14377
|
};
|
|
14575
14378
|
pendingResultPublication = void 0;
|
|
14576
14379
|
runPersistence.write(resultPath, payload);
|
|
14577
|
-
if (!resultPublished) pendingResultPublication = new Promise((
|
|
14578
|
-
settleResultPublication =
|
|
14380
|
+
if (!resultPublished) pendingResultPublication = new Promise((resolve9) => {
|
|
14381
|
+
settleResultPublication = resolve9;
|
|
14579
14382
|
});
|
|
14580
14383
|
return true;
|
|
14581
14384
|
} catch (error) {
|
|
@@ -14912,7 +14715,7 @@ Parent action: ${STATUS_ACTION}`,
|
|
|
14912
14715
|
const step = status.steps?.find((candidate) => candidate.workflowKey === key);
|
|
14913
14716
|
if (!step) return;
|
|
14914
14717
|
if (runId2) step.runId = runId2;
|
|
14915
|
-
step.async = runId2 !== void 0 &&
|
|
14718
|
+
step.async = runId2 !== void 0 && path18.basename(runId2) === runId2 && fs20.existsSync(path18.join(path18.dirname(asyncDir), runId2));
|
|
14916
14719
|
persist({ tolerateStatusWriteFailure: true });
|
|
14917
14720
|
};
|
|
14918
14721
|
try {
|
|
@@ -15013,7 +14816,7 @@ Parent action: ${COMPLETION_ACTION}` : content,
|
|
|
15013
14816
|
}
|
|
15014
14817
|
if (priorChild?.kind === "started") {
|
|
15015
14818
|
const priorRunId = priorChild.runId;
|
|
15016
|
-
const priorAsyncDir =
|
|
14819
|
+
const priorAsyncDir = path18.join(DIRS.async, priorRunId);
|
|
15017
14820
|
appendWorkflowChildJournal(asyncDir, { type: "start", key, fingerprint: journalFingerprint, runId: priorRunId });
|
|
15018
14821
|
recordReusedWorkflowChild(key, priorRunId);
|
|
15019
14822
|
const stopListener = stopAwaitedAsyncChildOnAbort(workflowSignal, deps2.state, priorRunId, priorAsyncDir, deps2.kill, true);
|
|
@@ -15235,7 +15038,7 @@ Parent action: ${COMPLETION_ACTION}` : content,
|
|
|
15235
15038
|
const workflowOutput = typeof workflowChildDefaults.output === "string" || typeof workflowChildDefaults.output === "boolean" ? workflowChildDefaults.output : void 0;
|
|
15236
15039
|
const configuredOutputBaseDir = resolveConfiguredSingleRunOutputBaseDir(deps2);
|
|
15237
15040
|
const workflowAggregateOutputPath = resolveSingleOutputPath(workflowOutput, ctx.cwd, workflowCwd, resolveSingleRunOutputBaseDir(deps2, workflowArtifactsDir, foregroundWorkflowRunId));
|
|
15238
|
-
const workflowFullResultPath =
|
|
15041
|
+
const workflowFullResultPath = path18.join(workflowArtifactsDir, `${sanitizeRunPathSegment(foregroundWorkflowRunId)}_workflow-result.md`);
|
|
15239
15042
|
const claimedOutputPaths = /* @__PURE__ */ new Map();
|
|
15240
15043
|
const childOutputOverrides = /* @__PURE__ */ new Map();
|
|
15241
15044
|
const childOutputClaimPaths = /* @__PURE__ */ new Map();
|
|
@@ -15506,7 +15309,7 @@ ${partial.console.map((entry) => `[${entry.level}] ${entry.text}`).join("\n")}`]
|
|
|
15506
15309
|
if (paramsWithResolvedCwd.mode === "apply") {
|
|
15507
15310
|
try {
|
|
15508
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.");
|
|
15509
|
-
const repo =
|
|
15312
|
+
const repo = path18.resolve(requestCwd, paramsWithResolvedCwd.repo);
|
|
15510
15313
|
const plan = loadReviewedCleanupPlan(repo, paramsWithResolvedCwd.planId);
|
|
15511
15314
|
const decision = resolveAuthorityDecision({ action: "discardWorktree", ...deps2.config.authorityPolicy === void 0 ? {} : { policy: deps2.config.authorityPolicy } });
|
|
15512
15315
|
if (decision === "forbid") throw new Error("Authority policy forbids worktree cleanup.");
|
|
@@ -15536,8 +15339,8 @@ Remove up to ${plan.entries.filter((entry) => entry.decision === "remove").lengt
|
|
|
15536
15339
|
}
|
|
15537
15340
|
try {
|
|
15538
15341
|
const created = createWorktreeCleanupPlan({
|
|
15539
|
-
repo: paramsWithResolvedCwd.repo?.trim() ?
|
|
15540
|
-
...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) } : {},
|
|
15541
15344
|
...deps2.config.worktreeBaseDir ? { worktreeBaseDir: deps2.config.worktreeBaseDir } : {},
|
|
15542
15345
|
foregroundRunOwnership: (runId2) => {
|
|
15543
15346
|
if (deps2.state.foregroundControls.has(runId2)) return "active";
|
|
@@ -15570,7 +15373,7 @@ ${paramsWithResolvedCwd.handoffPath}`);
|
|
|
15570
15373
|
}
|
|
15571
15374
|
if (!confirmed) return { content: [{ type: "text", text: "Worktree discard canceled; preserved worktrees were not changed." }], details: { mode: "management", results: [] } };
|
|
15572
15375
|
try {
|
|
15573
|
-
const manifestPath =
|
|
15376
|
+
const manifestPath = path18.isAbsolute(paramsWithResolvedCwd.handoffPath) ? paramsWithResolvedCwd.handoffPath : path18.resolve(requestCwd, paramsWithResolvedCwd.handoffPath);
|
|
15574
15377
|
const discarded = await withWorktreeTransaction(() => discardPreservedWorktrees(
|
|
15575
15378
|
manifestPath,
|
|
15576
15379
|
{ kind: decision === "confirm" ? "confirmed" : "policy", ...deps2.config.authorityPolicy ? { policy: deps2.config.authorityPolicy } : {} }
|
|
@@ -15588,7 +15391,7 @@ ${paramsWithResolvedCwd.handoffPath}`);
|
|
|
15588
15391
|
if (!laneId) return { content: [{ type: "text", text: `${action} requires laneId.` }], isError: true, details: { mode: "management", results: [] } };
|
|
15589
15392
|
const handoffPath = paramsWithResolvedCwd.handoffPath?.trim();
|
|
15590
15393
|
if (!handoffPath) return { content: [{ type: "text", text: `${action} requires handoffPath for the existing parallel handoff manifest.` }], isError: true, details: { mode: "management", results: [] } };
|
|
15591
|
-
const manifestPath =
|
|
15394
|
+
const manifestPath = path18.isAbsolute(handoffPath) ? handoffPath : path18.resolve(requestCwd, handoffPath);
|
|
15592
15395
|
try {
|
|
15593
15396
|
if (action === "lane.status") {
|
|
15594
15397
|
let manifest;
|
|
@@ -15764,9 +15567,7 @@ Usage is not reset. Compaction keeps the same budget; a new parent session start
|
|
|
15764
15567
|
if (action === "guide") {
|
|
15765
15568
|
try {
|
|
15766
15569
|
const guide = readSubagentGuide(paramsWithResolvedCwd.topic);
|
|
15767
|
-
const
|
|
15768
|
-
const fullNotice = topic === "tool-reference" ? disabledFeatureNotice(disabledFeatures) : void 0;
|
|
15769
|
-
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);
|
|
15770
15571
|
return {
|
|
15771
15572
|
content: [{ type: "text", text: notice ? `${notice}
|
|
15772
15573
|
|
|
@@ -15917,7 +15718,7 @@ ${item.text}` } : item)
|
|
|
15917
15718
|
if (paramsWithResolvedCwd.dir) {
|
|
15918
15719
|
try {
|
|
15919
15720
|
const location = resolveAsyncRunLocation(paramsWithResolvedCwd, DIRS.async, DIRS.results);
|
|
15920
|
-
const runId2 = location.resolvedId ?? targetRunId ??
|
|
15721
|
+
const runId2 = location.resolvedId ?? targetRunId ?? path18.basename(location.asyncDir ?? paramsWithResolvedCwd.dir);
|
|
15921
15722
|
const directoryStatus = location.asyncDir ? readStatus(location.asyncDir) : null;
|
|
15922
15723
|
if (directoryStatus?.mode === "workflow") {
|
|
15923
15724
|
return steerWorkflowRun({ state: deps2.state, runId: runId2, asyncDir: location.asyncDir, message, mode: resolveSteerDeliveryMode(paramsWithResolvedCwd.mode), index: paramsWithResolvedCwd.index, signal });
|
|
@@ -16050,7 +15851,7 @@ ${item.text}` } : item)
|
|
|
16050
15851
|
if (!stopChild) return { content: [{ type: "text", text: `Workflow ${workflowRunId} child stop is unavailable in this extension runtime.` }], isError: true, details: { mode: "management", results: [] } };
|
|
16051
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: [] } };
|
|
16052
15853
|
try {
|
|
16053
|
-
|
|
15854
|
+
fs20.appendFileSync(path18.join(asyncJob.asyncDir, "events.jsonl"), `${JSON.stringify({
|
|
16054
15855
|
type: "subagent.child-status",
|
|
16055
15856
|
version: 1,
|
|
16056
15857
|
runId: workflowRunId,
|
|
@@ -16076,7 +15877,7 @@ ${item.text}` } : item)
|
|
|
16076
15877
|
if (paramsWithResolvedCwd.dir) {
|
|
16077
15878
|
try {
|
|
16078
15879
|
const location = resolveAsyncRunLocation(paramsWithResolvedCwd, DIRS.async, DIRS.results);
|
|
16079
|
-
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);
|
|
16080
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: [] } };
|
|
16081
15882
|
} catch (error) {
|
|
16082
15883
|
const text = error instanceof Error ? error.message : String(error);
|
|
@@ -16324,7 +16125,7 @@ ${item.text}` } : item)
|
|
|
16324
16125
|
if (foregroundTimeout.error) return buildRequestedModeError(effectiveParams, foregroundTimeout.error);
|
|
16325
16126
|
const controlConfig = resolveControlConfig(deps2.config.control, effectiveParams.control);
|
|
16326
16127
|
const requestedWorkflowChildAsyncId = typeof effectiveParams.workflowChildAsyncId === "string" ? effectiveParams.workflowChildAsyncId.trim() : "";
|
|
16327
|
-
const asyncRunId = requestedWorkflowChildAsyncId &&
|
|
16128
|
+
const asyncRunId = requestedWorkflowChildAsyncId && path18.basename(requestedWorkflowChildAsyncId) === requestedWorkflowChildAsyncId ? requestedWorkflowChildAsyncId : randomUUID5();
|
|
16328
16129
|
const topLevelAsyncCapacityEligible = depth === 0 && !inheritedNestedRouteValue && !effectiveParams.workflowParentRunId;
|
|
16329
16130
|
const topLevelAsync = effectiveAsync && topLevelAsyncCapacityEligible;
|
|
16330
16131
|
let activeAsyncCapacity;
|
|
@@ -16336,7 +16137,7 @@ ${item.text}` } : item)
|
|
|
16336
16137
|
limit: activeLimit,
|
|
16337
16138
|
runId: asyncRunId,
|
|
16338
16139
|
kind: "runner",
|
|
16339
|
-
asyncDir:
|
|
16140
|
+
asyncDir: path18.join(DIRS.async, asyncRunId)
|
|
16340
16141
|
}, { liveWorkflowRunIds: new Set(deps2.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps2.config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
16341
16142
|
deps2.state.activeAsyncCapacity = getActiveAsyncCapacitySnapshot(requestSessionId, activeLimit, { liveWorkflowRunIds: new Set(deps2.state.workflowControllers?.keys() ?? []), abandonedSlotReleaseAfterMs: resolveAbandonedSlotReleaseAfterMs(deps2.config.capacity?.abandonedSlotReleaseAfterMs) });
|
|
16342
16143
|
} catch (error) {
|
|
@@ -16372,13 +16173,13 @@ ${item.text}` } : item)
|
|
|
16372
16173
|
let sessionRoot;
|
|
16373
16174
|
sessionRunId = effectiveAsync ? asyncRunId : runId;
|
|
16374
16175
|
if (effectiveParams.sessionDir) {
|
|
16375
|
-
sessionRoot =
|
|
16176
|
+
sessionRoot = path18.join(path18.resolve(deps2.expandTilde(effectiveParams.sessionDir)), sessionRunId);
|
|
16376
16177
|
} else {
|
|
16377
|
-
const baseSessionRoot = deps2.config.defaultSessionDir ?
|
|
16378
|
-
sessionRoot =
|
|
16178
|
+
const baseSessionRoot = deps2.config.defaultSessionDir ? path18.resolve(deps2.expandTilde(deps2.config.defaultSessionDir)) : deps2.getSubagentSessionRoot(parentSessionFile);
|
|
16179
|
+
sessionRoot = path18.join(baseSessionRoot, sessionRunId);
|
|
16379
16180
|
}
|
|
16380
16181
|
try {
|
|
16381
|
-
|
|
16182
|
+
fs20.mkdirSync(sessionRoot, { recursive: true });
|
|
16382
16183
|
} catch (error) {
|
|
16383
16184
|
activeAsyncCapacity?.rollback();
|
|
16384
16185
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -16388,7 +16189,7 @@ ${item.text}` } : item)
|
|
|
16388
16189
|
contextPolicy.contextSummary
|
|
16389
16190
|
);
|
|
16390
16191
|
}
|
|
16391
|
-
const sessionDirForIndex = (idx) =>
|
|
16192
|
+
const sessionDirForIndex = (idx) => path18.join(sessionRoot, `run-${idx ?? 0}`);
|
|
16392
16193
|
const forkSessionFileForTask = (agentName, idx = 0) => {
|
|
16393
16194
|
if (!shouldForkAgent(contextPolicy, agentName)) return void 0;
|
|
16394
16195
|
return forkSessionFileForIndex(idx);
|
|
@@ -16398,8 +16199,8 @@ ${item.text}` } : item)
|
|
|
16398
16199
|
await prepareForkSessionForIndex(idx);
|
|
16399
16200
|
};
|
|
16400
16201
|
const thinkingOverrideForTask = () => delegatedThinkingOverride;
|
|
16401
|
-
const childSessionFileForTask = (agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) => forkSessionFileForTask(agentName, idx, modelOverride, modelOverrideFromParent, modelOrigin) ??
|
|
16402
|
-
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");
|
|
16403
16204
|
try {
|
|
16404
16205
|
assertLaunchableAgentsAllowedBeforeFork();
|
|
16405
16206
|
if (!(effectiveParams.clarify === true && ctx.hasUI) || deps2.config.forkContext?.mode === "pruned") {
|