pi-subagents 0.81.0 → 0.82.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.
Files changed (141) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/bundle/agents.js +3 -3
  3. package/bundle/child-tool-plan.js +2 -2
  4. package/bundle/{chunk-LSRUTHT7.js → chunk-43SCHIMD.js} +1 -1
  5. package/bundle/{chunk-G5DKFLLI.js → chunk-4SDU2BZX.js} +15 -10
  6. package/bundle/{chunk-V2JWDB5U.js → chunk-52UBALJL.js} +2 -2
  7. package/bundle/{chunk-3DC5KBVP.js → chunk-5TYMSZX3.js} +1 -1
  8. package/bundle/{chunk-CEKS3TFO.js → chunk-7IVSUMQQ.js} +6 -6
  9. package/bundle/{chunk-KSWQVGDW.js → chunk-7PCKZHR5.js} +225 -18
  10. package/bundle/{chunk-563BHAZ5.js → chunk-7YMXOYEU.js} +33 -25
  11. package/bundle/{chunk-XMFLCKMQ.js → chunk-B5EKXDHE.js} +336 -130
  12. package/bundle/{chunk-ZBC44JJ3.js → chunk-CIZQXJ5Q.js} +22 -9
  13. package/bundle/{chunk-L5ZHE4L4.js → chunk-GC4DSPMH.js} +4 -4
  14. package/bundle/{chunk-ARRKBB2B.js → chunk-GJECHSAR.js} +2 -2
  15. package/bundle/{chunk-2ZSNH26H.js → chunk-M2CTFYT3.js} +7 -7
  16. package/bundle/{chunk-TUWLXTAM.js → chunk-MHZ4G23A.js} +681 -320
  17. package/bundle/{chunk-WSBNENFS.js → chunk-NGGAFDL6.js} +14 -4
  18. package/bundle/{chunk-VJHOELQP.js → chunk-NRQ75MGH.js} +160 -78
  19. package/bundle/{chunk-BJN5LUQY.js → chunk-OYF6AFDY.js} +1 -1
  20. package/bundle/{chunk-EYNJ6VL6.js → chunk-PLCCBLPY.js} +2 -2
  21. package/bundle/{chunk-74RT5YQE.js → chunk-RMQFMMAT.js} +123 -33
  22. package/bundle/{chunk-V6MR5TOQ.js → chunk-SIJ4S3EN.js} +25 -19
  23. package/bundle/{chunk-NNLASTQE.js → chunk-VH5OAWVG.js} +19 -5
  24. package/bundle/{chunk-BPS2AGBZ.js → chunk-W2SZ22EM.js} +93 -52
  25. package/bundle/{chunk-V3BIUPLU.js → chunk-WVD4UEL4.js} +256 -52
  26. package/bundle/{chunk-ZP3HODFH.js → chunk-XSWOTIA3.js} +1 -1
  27. package/bundle/{chunk-3IF2PGW7.js → chunk-YKEWIB4M.js} +1 -1
  28. package/bundle/{chunk-26C3WLYQ.js → chunk-ZLINIYTP.js} +497 -272
  29. package/bundle/{chunk-2XYWN7QJ.js → chunk-ZQGSDHL6.js} +2 -2
  30. package/bundle/control-channel.js +2 -2
  31. package/bundle/index.js +1 -1
  32. package/bundle/intercom-bridge.js +2 -2
  33. package/bundle/preflight.js +8 -8
  34. package/bundle/project-panes.js +3 -3
  35. package/docs/agents.md +31 -0
  36. package/docs/configuration.md +9 -1
  37. package/docs/tool-reference.md +3 -1
  38. package/docs/workflows.md +16 -0
  39. package/package.json +2 -2
  40. package/skills/pi-subagents/references/execution-controls.md +5 -4
  41. package/src/agents/agent-management.js +3 -3
  42. package/src/agents/agent-memory.js +3 -1
  43. package/src/agents/agents.js +6 -6
  44. package/src/agents/frontmatter.d.ts +2 -0
  45. package/src/agents/frontmatter.js +15 -1
  46. package/src/agents/skills.js +2 -1
  47. package/src/extension/config.js +3 -0
  48. package/src/extension/herdr-pi-bridge.js +17 -10
  49. package/src/extension/index.js +82 -2
  50. package/src/extension/schemas.js +3 -3
  51. package/src/intercom/native-supervisor-channel.js +20 -0
  52. package/src/missions/lifecycle.js +7 -1
  53. package/src/runs/background/active-async-capacity.d.ts +1 -0
  54. package/src/runs/background/active-async-capacity.js +9 -3
  55. package/src/runs/background/async-execution.js +20 -10
  56. package/src/runs/background/async-job-tracker.d.ts +6 -1
  57. package/src/runs/background/async-job-tracker.js +9 -4
  58. package/src/runs/background/async-status.d.ts +2 -0
  59. package/src/runs/background/async-status.js +21 -2
  60. package/src/runs/background/await-async-run.js +3 -7
  61. package/src/runs/background/boot-id.d.ts +12 -0
  62. package/src/runs/background/boot-id.js +47 -0
  63. package/src/runs/background/interrupted-elsewhere.d.ts +22 -0
  64. package/src/runs/background/interrupted-elsewhere.js +68 -0
  65. package/src/runs/background/notify.d.ts +2 -0
  66. package/src/runs/background/notify.js +13 -0
  67. package/src/runs/background/result-delivery-ownership.d.ts +6 -0
  68. package/src/runs/background/result-delivery-ownership.js +19 -1
  69. package/src/runs/background/result-watcher.d.ts +3 -1
  70. package/src/runs/background/result-watcher.js +24 -8
  71. package/src/runs/background/run-child-session.d.ts +1 -6
  72. package/src/runs/background/run-child-session.js +1 -1
  73. package/src/runs/background/run-status.js +2 -1
  74. package/src/runs/background/stale-run-reconciler.d.ts +25 -0
  75. package/src/runs/background/stale-run-reconciler.js +52 -13
  76. package/src/runs/background/subagent-runner-bootstrap.js +3 -0
  77. package/src/runs/background/subagent-runner.d.ts +4 -0
  78. package/src/runs/background/subagent-runner.js +13 -5
  79. package/src/runs/background/subagent-wait.d.ts +1 -1
  80. package/src/runs/background/subagent-wait.js +6 -1
  81. package/src/runs/background/terminal-run-index.d.ts +1 -0
  82. package/src/runs/background/terminal-run-index.js +4 -2
  83. package/src/runs/foreground/async-stop-action.js +3 -1
  84. package/src/runs/foreground/execution.js +6 -3
  85. package/src/runs/foreground/foreground-history.d.ts +16 -2
  86. package/src/runs/foreground/foreground-history.js +133 -31
  87. package/src/runs/foreground/subagent-executor.d.ts +11 -0
  88. package/src/runs/foreground/subagent-executor.js +277 -74
  89. package/src/runs/shared/child-launch.d.ts +2 -2
  90. package/src/runs/shared/child-session.d.ts +3 -2
  91. package/src/runs/shared/child-session.js +51 -8
  92. package/src/runs/shared/child-tool-plan.d.ts +7 -0
  93. package/src/runs/shared/child-tool-plan.js +21 -6
  94. package/src/runs/shared/herdr-connection.d.ts +17 -8
  95. package/src/runs/shared/herdr-connection.js +19 -7
  96. package/src/runs/shared/herdr-external-adapters.d.ts +3 -2
  97. package/src/runs/shared/herdr-external-adapters.js +1 -1
  98. package/src/runs/shared/herdr-machine.d.ts +12 -2
  99. package/src/runs/shared/herdr-machine.js +55 -9
  100. package/src/runs/shared/herdr-pane-host.d.ts +88 -0
  101. package/src/runs/shared/herdr-pane-host.js +155 -0
  102. package/src/runs/shared/herdr-placed-run.d.ts +27 -150
  103. package/src/runs/shared/herdr-placed-run.js +48 -395
  104. package/src/runs/shared/local-machine-access.d.ts +5 -0
  105. package/src/runs/shared/local-machine-access.js +28 -0
  106. package/src/runs/shared/orca-progress-tabs.d.ts +0 -1
  107. package/src/runs/shared/orca-progress-tabs.js +1 -24
  108. package/src/runs/shared/parallel-utils.d.ts +1 -1
  109. package/src/runs/shared/placed-run-owner.d.ts +49 -0
  110. package/src/runs/shared/placed-run-owner.js +93 -0
  111. package/src/runs/shared/placement-host.d.ts +99 -0
  112. package/src/runs/shared/placement-host.js +157 -0
  113. package/src/runs/shared/rex-machine.d.ts +25 -0
  114. package/src/runs/shared/rex-machine.js +83 -0
  115. package/src/runs/shared/rex-pane-host.d.ts +62 -0
  116. package/src/runs/shared/rex-pane-host.js +327 -0
  117. package/src/shared/completion-owner.d.ts +5 -0
  118. package/src/shared/completion-owner.js +5 -0
  119. package/src/shared/session-identity.d.ts +2 -0
  120. package/src/shared/session-identity.js +26 -0
  121. package/src/shared/types.d.ts +45 -6
  122. package/src/tui/fleet.d.ts +1 -0
  123. package/src/tui/fleet.js +24 -15
  124. package/src/tui/render.d.ts +6 -0
  125. package/src/tui/render.js +3 -3
  126. package/src/tui/subagent-messages.js +1 -0
  127. package/src/workflows/scripted-workflow.d.ts +1 -1
  128. package/src/workflows/workflow-auto-resume.d.ts +32 -0
  129. package/src/workflows/workflow-auto-resume.js +102 -0
  130. package/src/workflows/workflow-launch-record.d.ts +27 -0
  131. package/src/workflows/workflow-launch-record.js +48 -0
  132. package/src/workflows/workflow-resume-claim.d.ts +82 -0
  133. package/src/workflows/workflow-resume-claim.js +210 -0
  134. package/src/workflows/workflow-resume.d.ts +56 -0
  135. package/src/workflows/workflow-resume.js +106 -0
  136. package/src/workflows/workflow-reuse.d.ts +15 -9
  137. package/src/workflows/workflow-reuse.js +77 -14
  138. package/src/workflows/workflow-revival.d.ts +2 -0
  139. package/src/workflows/workflow-revival.js +2 -1
  140. package/src/workflows/workflow-stop-cause.d.ts +6 -0
  141. package/src/workflows/workflow-stop-cause.js +8 -0
@@ -11,27 +11,35 @@ import {
11
11
  startWatchdogDiffBaselineCapture,
12
12
  writeUserWatchdogEnabled,
13
13
  writeWatchdogModelSettings
14
- } from "./chunk-BPS2AGBZ.js";
14
+ } from "./chunk-W2SZ22EM.js";
15
15
  import {
16
16
  resolveGitRepositoryIdentity
17
- } from "./chunk-KSWQVGDW.js";
17
+ } from "./chunk-7PCKZHR5.js";
18
18
  import {
19
19
  listAsyncRuns,
20
+ ownsRun,
20
21
  parallelHandoffPath,
21
22
  parseWorkflowChildSummary,
22
23
  readAsyncRecoveryDescriptor,
23
- resolveRetainedWorktreeCwd,
24
- utf8Tail
25
- } from "./chunk-NNLASTQE.js";
24
+ resolveRetainedWorktreeCwd
25
+ } from "./chunk-VH5OAWVG.js";
26
26
  import {
27
27
  deepFreezeWorkflowArgs,
28
28
  normalizeWorkflowArgs
29
29
  } from "./chunk-NF7IEKBH.js";
30
30
  import {
31
31
  HOST_STEP_MAX_COUNT,
32
+ WORKFLOW_STOP_CAUSE_OWNER_EXITED,
33
+ WORKFLOW_STOP_CAUSE_RUNTIME_REPLACED,
32
34
  assertUniqueHostStepIds,
35
+ checkRecordedPidLiveness,
36
+ currentBootId,
37
+ currentPidNamespaceScope,
38
+ currentRecordedPid,
33
39
  hasLiveNestedDescendants,
34
40
  isActiveAsyncState,
41
+ isRecordedPid,
42
+ isReusableWorkflowStopCause,
35
43
  normalizeWorktreeBaseRef,
36
44
  parseHostStepNode,
37
45
  previewSimpleWorkflowRun,
@@ -41,23 +49,22 @@ import {
41
49
  reconcileAsyncRun,
42
50
  resolveMissionStoreLocation,
43
51
  validateMissionId,
44
- validateModelResponseAliases,
45
52
  workflowRunParamsFingerprint
46
- } from "./chunk-VJHOELQP.js";
53
+ } from "./chunk-NRQ75MGH.js";
47
54
  import {
48
55
  requestAsyncCommand,
49
56
  watchAsyncControlInbox
50
- } from "./chunk-BJN5LUQY.js";
57
+ } from "./chunk-OYF6AFDY.js";
51
58
  import {
52
59
  supervisorChannelDir
53
- } from "./chunk-G5DKFLLI.js";
60
+ } from "./chunk-4SDU2BZX.js";
54
61
  import {
55
62
  createHerdrClient,
56
63
  detectHerdr
57
- } from "./chunk-V2JWDB5U.js";
64
+ } from "./chunk-52UBALJL.js";
58
65
  import {
59
66
  getProjectSubagentsDir
60
- } from "./chunk-3DC5KBVP.js";
67
+ } from "./chunk-5TYMSZX3.js";
61
68
  import {
62
69
  ExternalJobProviderError,
63
70
  getExternalJobProvider,
@@ -67,7 +74,7 @@ import {
67
74
  } from "./chunk-YYREO77V.js";
68
75
  import {
69
76
  mergeRuntimeAgents
70
- } from "./chunk-3IF2PGW7.js";
77
+ } from "./chunk-YKEWIB4M.js";
71
78
  import {
72
79
  CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL,
73
80
  PI_CODING_AGENT_PACKAGE,
@@ -98,8 +105,8 @@ import {
98
105
  normalizeModelSegment,
99
106
  normalizeWorkflowLaneMetadata,
100
107
  parseFrontmatter,
101
- parseFrontmatterList,
102
108
  parsePackageName,
109
+ parseUnquotedList,
103
110
  readStatus,
104
111
  removeBuiltinAgentOverride,
105
112
  removeBuiltinAgentOverrideFields,
@@ -122,7 +129,7 @@ import {
122
129
  withFileLease,
123
130
  writeAtomicJson,
124
131
  writePrivateAtomicJson
125
- } from "./chunk-ZBC44JJ3.js";
132
+ } from "./chunk-CIZQXJ5Q.js";
126
133
  import {
127
134
  DIRS,
128
135
  INTERCOM_DETACH_REQUEST_EVENT,
@@ -3082,8 +3089,8 @@ function withDeclaredExtensionPaths(config, filePath) {
3082
3089
  const { extensions: _extensions, subagentOnlyExtensions: _subagentOnlyExtensions, ...withoutResolvedExtensions } = config;
3083
3090
  return {
3084
3091
  ...withoutResolvedExtensions,
3085
- ...frontmatter.extensions !== void 0 ? { extensions: parseFrontmatterList(frontmatter.extensions) } : {},
3086
- ...frontmatter.subagentOnlyExtensions !== void 0 ? { subagentOnlyExtensions: parseFrontmatterList(frontmatter.subagentOnlyExtensions) } : {}
3092
+ ...frontmatter.extensions !== void 0 ? { extensions: parseUnquotedList(frontmatter.extensions) } : {},
3093
+ ...frontmatter.subagentOnlyExtensions !== void 0 ? { subagentOnlyExtensions: parseUnquotedList(frontmatter.subagentOnlyExtensions) } : {}
3087
3094
  };
3088
3095
  }
3089
3096
  function editableAgentConfig(agent) {
@@ -5071,6 +5078,197 @@ function createParentWake(pi, now = Date.now) {
5071
5078
  };
5072
5079
  }
5073
5080
 
5081
+ // src/workflows/workflow-launch-record.ts
5082
+ import * as fs9 from "node:fs";
5083
+ import * as path8 from "node:path";
5084
+ var WORKFLOW_LAUNCH_RECORD_FILE = "launch.json";
5085
+ function launchRecordPath(asyncDir) {
5086
+ return path8.join(asyncDir, WORKFLOW_LAUNCH_RECORD_FILE);
5087
+ }
5088
+ function writeWorkflowLaunchRecord(asyncDir, record) {
5089
+ writeAtomicJson(launchRecordPath(asyncDir), record);
5090
+ }
5091
+ function isPlainObject(value) {
5092
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
5093
+ }
5094
+ function invalid(error2) {
5095
+ return { ok: false, missing: false, error: error2 };
5096
+ }
5097
+ function readWorkflowLaunchRecord(asyncDir) {
5098
+ let text2;
5099
+ try {
5100
+ text2 = fs9.readFileSync(launchRecordPath(asyncDir), "utf-8");
5101
+ } catch (error2) {
5102
+ if (error2.code === "ENOENT") return { ok: false, missing: true, error: "launch.json is missing" };
5103
+ return invalid(`could not be read: ${error2 instanceof Error ? error2.message : String(error2)}`);
5104
+ }
5105
+ let value;
5106
+ try {
5107
+ value = JSON.parse(text2);
5108
+ } catch (error2) {
5109
+ return invalid(`not valid JSON: ${error2 instanceof Error ? error2.message : String(error2)}`);
5110
+ }
5111
+ if (!isPlainObject(value)) return invalid("not a JSON object");
5112
+ if (value.version !== 1) return invalid("version must be 1");
5113
+ if (typeof value.scriptDigest !== "string" || !value.scriptDigest) return invalid("scriptDigest must be a non-empty string");
5114
+ if (value.source === "structured") return { ok: true, record: { version: 1, source: "structured", scriptDigest: value.scriptDigest } };
5115
+ if (value.source !== "script" && value.source !== "resource") return invalid('source must be "script", "resource", or "structured"');
5116
+ if (!isPlainObject(value.params)) return invalid("params must be an object");
5117
+ return { ok: true, record: { version: 1, source: value.source, params: value.params, scriptDigest: value.scriptDigest } };
5118
+ }
5119
+
5120
+ // src/workflows/workflow-resume-claim.ts
5121
+ import { randomUUID as randomUUID3 } from "node:crypto";
5122
+ import * as fs10 from "node:fs";
5123
+ import * as path9 from "node:path";
5124
+ var CLAIM_FILE_PATTERN = /^resumed-by\.([1-9][0-9]*)\.json$/;
5125
+ var RELEASED_CLAIM_FILE_PATTERN = /^resumed-by\.([1-9][0-9]*)\.json\.released-/;
5126
+ var REFUSAL_FILE = "resume-refused.json";
5127
+ var MAX_CLAIM_ROUNDS = 8;
5128
+ var heldClaimsKey = /* @__PURE__ */ Symbol.for("pi-subagents.workflow-resume-claims.v1");
5129
+ var heldClaimsGlobal = globalThis;
5130
+ var heldClaims = heldClaimsGlobal[heldClaimsKey] ?? (heldClaimsGlobal[heldClaimsKey] = /* @__PURE__ */ new Set());
5131
+ function claimFilePath(asyncDir, generation) {
5132
+ return path9.join(asyncDir, `resumed-by.${generation}.json`);
5133
+ }
5134
+ function isClaim(value) {
5135
+ if (!isRecordedPid(value) || Array.isArray(value)) return false;
5136
+ const claim = value;
5137
+ return claim.version === 1 && typeof claim.claimedAt === "number" && typeof claim.newRunId === "string" && claim.newRunId.length > 0 && (claim.trigger === "auto" || claim.trigger === "manual" || claim.trigger === "reuse");
5138
+ }
5139
+ function readTopClaim(asyncDir, retries = 3) {
5140
+ let generation = 0;
5141
+ let releasedGeneration = 0;
5142
+ try {
5143
+ for (const name of fs10.readdirSync(asyncDir)) {
5144
+ const live = CLAIM_FILE_PATTERN.exec(name);
5145
+ if (live) generation = Math.max(generation, Number(live[1]));
5146
+ const released = RELEASED_CLAIM_FILE_PATTERN.exec(name);
5147
+ if (released) releasedGeneration = Math.max(releasedGeneration, Number(released[1]));
5148
+ }
5149
+ } catch (error2) {
5150
+ return error2.code === "ENOENT" ? { kind: "open", generation: 0 } : { kind: "unreadable" };
5151
+ }
5152
+ if (releasedGeneration > generation || generation === 0) return { kind: "open", generation: Math.max(releasedGeneration, generation) };
5153
+ const filePath = claimFilePath(asyncDir, generation);
5154
+ let text2;
5155
+ try {
5156
+ text2 = fs10.readFileSync(filePath, "utf-8");
5157
+ } catch (error2) {
5158
+ if (error2.code === "ENOENT" && retries > 0) return readTopClaim(asyncDir, retries - 1);
5159
+ return { kind: "unreadable" };
5160
+ }
5161
+ try {
5162
+ const value = JSON.parse(text2);
5163
+ return isClaim(value) ? { kind: "claim", generation, filePath, claim: value } : { kind: "open", generation };
5164
+ } catch {
5165
+ return { kind: "open", generation };
5166
+ }
5167
+ }
5168
+ function readWorkflowResumeClaim(asyncDir) {
5169
+ const top = readTopClaim(asyncDir);
5170
+ return top.kind === "claim" ? top.claim : void 0;
5171
+ }
5172
+ function resumedRunStarted(sourceAsyncDir, newRunId) {
5173
+ return path9.basename(newRunId) === newRunId && fs10.existsSync(path9.join(path9.dirname(sourceAsyncDir), newRunId, "status.json"));
5174
+ }
5175
+ function claimantLiveness(filePath, claim, liveness) {
5176
+ if (claim.pid === process.pid && claim.bootId === currentBootId() && claim.pidNamespaceScope === currentPidNamespaceScope()) return heldClaims.has(filePath) ? "alive" : "dead";
5177
+ return liveness ? liveness(claim) : checkRecordedPidLiveness(claim);
5178
+ }
5179
+ function workflowResumeClaimState(asyncDir, options = {}) {
5180
+ const top = readTopClaim(asyncDir);
5181
+ if (top.kind === "unreadable") return { kind: "unreadable" };
5182
+ if (top.kind !== "claim") return { kind: "none" };
5183
+ if (resumedRunStarted(asyncDir, top.claim.newRunId)) return { kind: "resumed", newRunId: top.claim.newRunId };
5184
+ return claimantLiveness(top.filePath, top.claim, options.liveness) === "dead" ? { kind: "none" } : { kind: "busy", pid: top.claim.pid, newRunId: top.claim.newRunId };
5185
+ }
5186
+ function createClaimFile(filePath, claim) {
5187
+ const tempPath = `${filePath}.${process.pid}.${randomUUID3()}.tmp`;
5188
+ try {
5189
+ fs10.writeFileSync(tempPath, JSON.stringify(claim, null, 2), "utf-8");
5190
+ fs10.linkSync(tempPath, filePath);
5191
+ return true;
5192
+ } catch (error2) {
5193
+ if (error2.code === "EEXIST") return false;
5194
+ throw error2;
5195
+ } finally {
5196
+ fs10.rmSync(tempPath, { force: true });
5197
+ }
5198
+ }
5199
+ function claimWorkflowResume(asyncDir, input) {
5200
+ const claim = {
5201
+ version: 1,
5202
+ ...currentRecordedPid(),
5203
+ claimedAt: input.now ?? Date.now(),
5204
+ newRunId: input.newRunId,
5205
+ trigger: input.trigger
5206
+ };
5207
+ for (let round = 0; round < MAX_CLAIM_ROUNDS; round++) {
5208
+ const top2 = readTopClaim(asyncDir);
5209
+ if (top2.kind === "unreadable") return { kind: "unreadable" };
5210
+ if (top2.kind === "claim") {
5211
+ if (top2.claim.newRunId === input.newRunId && top2.claim.pid === process.pid) {
5212
+ heldClaims.add(top2.filePath);
5213
+ return { kind: "owner" };
5214
+ }
5215
+ if (resumedRunStarted(asyncDir, top2.claim.newRunId)) return { kind: "resumed", newRunId: top2.claim.newRunId };
5216
+ if (claimantLiveness(top2.filePath, top2.claim, input.liveness) !== "dead") return { kind: "busy", pid: top2.claim.pid };
5217
+ }
5218
+ const filePath = claimFilePath(asyncDir, top2.generation + 1);
5219
+ if (createClaimFile(filePath, claim)) {
5220
+ heldClaims.add(filePath);
5221
+ return { kind: "owner" };
5222
+ }
5223
+ }
5224
+ const top = readTopClaim(asyncDir);
5225
+ return top.kind === "claim" ? { kind: "busy", pid: top.claim.pid } : { kind: "unreadable" };
5226
+ }
5227
+ function releaseWorkflowResumeClaim(asyncDir, newRunId) {
5228
+ const top = readTopClaim(asyncDir);
5229
+ if (top.kind !== "claim" || top.claim.newRunId !== newRunId || top.claim.pid !== process.pid) return;
5230
+ try {
5231
+ fs10.renameSync(top.filePath, `${top.filePath}.released-${randomUUID3()}`);
5232
+ } catch (error2) {
5233
+ if (error2.code !== "ENOENT") throw error2;
5234
+ }
5235
+ heldClaims.delete(top.filePath);
5236
+ }
5237
+ function recordWorkflowResumeRefusal(asyncDir, refusal, now = Date.now()) {
5238
+ writeAtomicJson(path9.join(asyncDir, REFUSAL_FILE), { version: 1, ...refusal, refusedAt: now });
5239
+ }
5240
+ function readWorkflowResumeRefusal(asyncDir) {
5241
+ try {
5242
+ const value = JSON.parse(fs10.readFileSync(path9.join(asyncDir, REFUSAL_FILE), "utf-8"));
5243
+ return value?.version === 1 && typeof value.reason === "string" && typeof value.byHand === "string" && typeof value.refusedAt === "number" && (value.newRunId === void 0 || typeof value.newRunId === "string") ? value : void 0;
5244
+ } catch {
5245
+ return void 0;
5246
+ }
5247
+ }
5248
+ function resumeByHandText(runId) {
5249
+ return `Resume by hand: subagent({ action: "resume", id: "${runId}" })`;
5250
+ }
5251
+ function relaunchByHandText(runId) {
5252
+ return `Relaunch it by hand with options: { reuseFrom: "${runId}" }.`;
5253
+ }
5254
+ function refusalSentence(reason, byHand) {
5255
+ return `${reason.replace(/\.$/, "")}.${byHand ? ` ${byHand}` : ""}`;
5256
+ }
5257
+ function hasWorkflowResumeRefusal(asyncDir) {
5258
+ return fs10.existsSync(path9.join(asyncDir, REFUSAL_FILE));
5259
+ }
5260
+ var RESUMED_AS = { auto: "Auto-resumed as", manual: "Resumed as", reuse: "Relaunched as" };
5261
+ function describeWorkflowResume(asyncDir, runId) {
5262
+ const claim = readWorkflowResumeClaim(asyncDir);
5263
+ const refusal = readWorkflowResumeRefusal(asyncDir);
5264
+ if (refusal && (!claim || refusal.newRunId === claim.newRunId)) {
5265
+ return `Not auto-resumed: ${refusalSentence(refusal.reason, refusal.byHand)}`;
5266
+ }
5267
+ if (claim) return `${RESUMED_AS[claim.trigger]} ${claim.newRunId}.`;
5268
+ const launch = readWorkflowLaunchRecord(asyncDir);
5269
+ return launch.ok && launch.record.source !== "structured" ? resumeByHandText(runId) : relaunchByHandText(runId);
5270
+ }
5271
+
5074
5272
  // src/runs/background/notify.ts
5075
5273
  import { statSync as statSync2 } from "node:fs";
5076
5274
  import { debuglog } from "node:util";
@@ -5250,15 +5448,15 @@ function nonemptyPath(value) {
5250
5448
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
5251
5449
  }
5252
5450
  function retainedChildPath(value, producerConfirmed = false) {
5253
- const path15 = nonemptyPath(value);
5254
- if (!path15) return { status: "unavailable" };
5255
- if (producerConfirmed) return { status: "verified", path: path15 };
5451
+ const path17 = nonemptyPath(value);
5452
+ if (!path17) return { status: "unavailable" };
5453
+ if (producerConfirmed) return { status: "verified", path: path17 };
5256
5454
  try {
5257
- return statSync2(path15).isFile() ? { status: "verified", path: path15 } : { status: "unavailable" };
5455
+ return statSync2(path17).isFile() ? { status: "verified", path: path17 } : { status: "unavailable" };
5258
5456
  } catch (error2) {
5259
5457
  const code = typeof error2 === "object" && error2 !== null && "code" in error2 && typeof error2.code === "string" ? error2.code : void 0;
5260
5458
  if (code === "ENOENT" || code === "ENOTDIR") return { status: "unavailable" };
5261
- return { status: "error", error: { path: path15, ...code ? { code } : {}, message: error2 instanceof Error ? error2.message : String(error2) } };
5459
+ return { status: "error", error: { path: path17, ...code ? { code } : {}, message: error2 instanceof Error ? error2.message : String(error2) } };
5262
5460
  }
5263
5461
  }
5264
5462
  function formatRetainedPathError(label, error2) {
@@ -5362,10 +5560,12 @@ function formatCorrelationLines(details) {
5362
5560
  return [
5363
5561
  details.asyncDir ? `Retention-managed async directory: ${boundedSafeText(details.asyncDir)}` : void 0,
5364
5562
  details.workflowRunId ? `Workflow run: ${details.workflowRunId}` : void 0,
5563
+ details.resumeNotice,
5365
5564
  details.childRuns?.length ? `Child runs: ${details.childRuns.map(formatChildRun).join(", ")}` : void 0,
5366
5565
  details.reconciledFromDetachedChild ? `Reconciled detached child: ${details.reconciledFromDetachedChild}` : void 0
5367
5566
  ].filter((line) => line !== void 0);
5368
5567
  }
5568
+ var RESUME_NOTICE_PREFIXES = ["Auto-resumed as ", "Resumed as ", "Relaunched as ", "Not auto-resumed: ", "Resume by hand: ", "Relaunch it by hand with "];
5369
5569
  var WATCHDOG_BLOCKERS_HEADING = "Watchdog blockers:";
5370
5570
  function formatWatchdogBlockerLines(details) {
5371
5571
  if (!details.watchdogBlockers?.length) return [];
@@ -5448,6 +5648,8 @@ function parseSubagentNotifyContent(content) {
5448
5648
  const workflowRunIndex = body.findIndex((line) => line.startsWith("Workflow run: "));
5449
5649
  const childRunsIndex = body.findIndex((line) => line.startsWith("Child runs: "));
5450
5650
  const reconciledIndex = body.findIndex((line) => line.startsWith("Reconciled detached child: "));
5651
+ const resumeNoticeLine = workflowRunIndex >= 0 ? body[workflowRunIndex + 1] : void 0;
5652
+ const resumeNotice = resumeNoticeLine && RESUME_NOTICE_PREFIXES.some((prefix) => resumeNoticeLine.startsWith(prefix)) ? resumeNoticeLine : void 0;
5451
5653
  const watchdogIndex = body.findIndex((line) => line === WATCHDOG_BLOCKERS_HEADING);
5452
5654
  const watchdogBlockers = watchdogIndex >= 0 ? parseWatchdogBlockerLines(body.slice(watchdogIndex + 1)) : [];
5453
5655
  const metadataIndexes = [sessionIndex, handoffIndex, workflowRunIndex, childRunsIndex, reconciledIndex, watchdogIndex].filter((index) => index >= 0);
@@ -5481,6 +5683,7 @@ function parseSubagentNotifyContent(content) {
5481
5683
  resultPreview,
5482
5684
  ...handoffPath ? { handoffPath } : {},
5483
5685
  ...workflowRunId ? { workflowRunId } : {},
5686
+ ...resumeNotice ? { resumeNotice } : {},
5484
5687
  ...childRuns?.length ? { childRuns } : {},
5485
5688
  ...reconciledFromDetachedChild ? { reconciledFromDetachedChild } : {},
5486
5689
  ...watchdogBlockers.length ? { watchdogBlockers } : {},
@@ -5706,6 +5909,8 @@ ${outputPreview}` : outputPreview;
5706
5909
  }) : void 0;
5707
5910
  const reconciledFromDetachedChild = typeof result5.reconciledFromDetachedChild === "string" ? result5.reconciledFromDetachedChild : void 0;
5708
5911
  const asyncDir = nonemptyPath(result5.asyncDir);
5912
+ const stopCause = result5.workflow?.stopCause;
5913
+ const resumeNotice = workflowRunId && asyncDir && isReusableWorkflowStopCause(stopCause) ? describeWorkflowResume(asyncDir, workflowRunId) : void 0;
5709
5914
  const watchdogBlockers = [];
5710
5915
  const watchdogConcerns = [];
5711
5916
  const collectWatchdogFindings = (owner, progress) => {
@@ -5738,6 +5943,7 @@ ${watchdogConcerns.map((warning) => `- ${warning}`).join("\n")}` : resultPreview
5738
5943
  ...handoffPath ? { handoffPath } : {},
5739
5944
  ...rawRunId ? { runId: rawRunId } : {},
5740
5945
  ...workflowRunId ? { workflowRunId } : {},
5946
+ ...resumeNotice ? { resumeNotice } : {},
5741
5947
  ...childRuns.length ? { childRuns } : {},
5742
5948
  ...childOutputs?.length ? { childOutputs } : {},
5743
5949
  ...reconciledFromDetachedChild ? { reconciledFromDetachedChild } : {},
@@ -6391,9 +6597,9 @@ function registerPiWebSessionLiveness(registration) {
6391
6597
  }
6392
6598
 
6393
6599
  // src/runs/background/wait-subscriptions.ts
6394
- import * as fs9 from "node:fs";
6395
- import * as path8 from "node:path";
6396
- import { randomUUID as randomUUID3 } from "node:crypto";
6600
+ import * as fs11 from "node:fs";
6601
+ import * as path10 from "node:path";
6602
+ import { randomUUID as randomUUID4 } from "node:crypto";
6397
6603
  var SUBSCRIPTION_VERSION = 1;
6398
6604
  var STATUS_OUTCOMES = /* @__PURE__ */ new Set(["timed out", "could not be reconciled", "needs attention", "reconciliation failed"]);
6399
6605
  var RECONCILE_INTERVAL_MS = 1e3;
@@ -6412,7 +6618,7 @@ function needsAttention(run) {
6412
6618
  return run.activityState === "needs_attention" || run.steps.some((step) => step.activityState === "needs_attention");
6413
6619
  }
6414
6620
  function subscriptionFile(dir, token) {
6415
- return path8.join(dir, `${token}.json`);
6621
+ return path10.join(dir, `${token}.json`);
6416
6622
  }
6417
6623
  function formatWaitSubscriptions(state, now = Date.now()) {
6418
6624
  const subscriptions = [...state.waitSubscriptions?.values() ?? []].sort((left, right) => left.createdAt - right.createdAt);
@@ -6426,7 +6632,7 @@ function formatWaitSubscriptions(state, now = Date.now()) {
6426
6632
  function createWaitSubscriptionManager(pi, state, options = {}) {
6427
6633
  const asyncDirRoot = options.asyncDirRoot ?? DIRS.async;
6428
6634
  const resultsDir = options.resultsDir ?? DIRS.results;
6429
- const subscriptionsDir = options.subscriptionsDir ?? path8.join(path8.dirname(asyncDirRoot), "wait-subscriptions");
6635
+ const subscriptionsDir = options.subscriptionsDir ?? path10.join(path10.dirname(asyncDirRoot), "wait-subscriptions");
6430
6636
  const now = options.now ?? Date.now;
6431
6637
  const subscriptions = state.waitSubscriptions ?? /* @__PURE__ */ new Map();
6432
6638
  state.waitSubscriptions = subscriptions;
@@ -6440,16 +6646,16 @@ function createWaitSubscriptionManager(pi, state, options = {}) {
6440
6646
  lastForeignSweepAt = sweptAt;
6441
6647
  let files;
6442
6648
  try {
6443
- files = fs9.readdirSync(subscriptionsDir).filter((file) => file.endsWith(".json"));
6649
+ files = fs11.readdirSync(subscriptionsDir).filter((file) => file.endsWith(".json"));
6444
6650
  } catch (error2) {
6445
6651
  if (!isNotFound(error2)) console.error(`Failed to scan wait subscriptions in '${subscriptionsDir}':`, error2);
6446
6652
  return;
6447
6653
  }
6448
6654
  for (const file of files) {
6449
- const filePath = path8.join(subscriptionsDir, file);
6655
+ const filePath = path10.join(subscriptionsDir, file);
6450
6656
  let record;
6451
6657
  try {
6452
- record = parseRecord(JSON.parse(fs9.readFileSync(filePath, "utf-8")));
6658
+ record = parseRecord(JSON.parse(fs11.readFileSync(filePath, "utf-8")));
6453
6659
  } catch (error2) {
6454
6660
  console.error(`Ignoring invalid wait subscription '${filePath}':`, error2);
6455
6661
  continue;
@@ -6458,7 +6664,7 @@ function createWaitSubscriptionManager(pi, state, options = {}) {
6458
6664
  if (record.sessionId === state.currentSessionId) continue;
6459
6665
  if (sweptAt < record.expiresAt + FOREIGN_SWEEP_GRACE_MS) continue;
6460
6666
  try {
6461
- fs9.unlinkSync(filePath);
6667
+ fs11.unlinkSync(filePath);
6462
6668
  } catch (error2) {
6463
6669
  if (!isNotFound(error2)) console.error(`Failed to remove expired wait subscription '${filePath}':`, error2);
6464
6670
  }
@@ -6466,7 +6672,7 @@ function createWaitSubscriptionManager(pi, state, options = {}) {
6466
6672
  };
6467
6673
  const remove = (record) => {
6468
6674
  try {
6469
- fs9.unlinkSync(subscriptionFile(subscriptionsDir, record.token));
6675
+ fs11.unlinkSync(subscriptionFile(subscriptionsDir, record.token));
6470
6676
  } catch (error2) {
6471
6677
  if (!isNotFound(error2)) throw error2;
6472
6678
  }
@@ -6587,7 +6793,7 @@ Parent action: ${STATUS_OUTCOMES.has(outcome) ? STATUS_ACTION : COMPLETION_ACTIO
6587
6793
  const createdAt = now();
6588
6794
  const record = {
6589
6795
  version: SUBSCRIPTION_VERSION,
6590
- token: randomUUID3(),
6796
+ token: randomUUID4(),
6591
6797
  sessionId,
6592
6798
  targetKind: input.targetKind,
6593
6799
  runId: input.runId,
@@ -6595,7 +6801,7 @@ Parent action: ${STATUS_OUTCOMES.has(outcome) ? STATUS_ACTION : COMPLETION_ACTIO
6595
6801
  createdAt,
6596
6802
  expiresAt: createdAt + input.timeoutMs
6597
6803
  };
6598
- fs9.mkdirSync(subscriptionsDir, { recursive: true });
6804
+ fs11.mkdirSync(subscriptionsDir, { recursive: true });
6599
6805
  writeAtomicJson(subscriptionFile(subscriptionsDir, record.token), record);
6600
6806
  subscriptions.set(record.token, record);
6601
6807
  return record;
@@ -6607,21 +6813,21 @@ Parent action: ${STATUS_OUTCOMES.has(outcome) ? STATUS_ACTION : COMPLETION_ACTIO
6607
6813
  if (!state.currentSessionId) return;
6608
6814
  let files;
6609
6815
  try {
6610
- files = fs9.readdirSync(subscriptionsDir).filter((file) => file.endsWith(".json"));
6816
+ files = fs11.readdirSync(subscriptionsDir).filter((file) => file.endsWith(".json"));
6611
6817
  } catch (error2) {
6612
6818
  if (isNotFound(error2)) return;
6613
6819
  throw error2;
6614
6820
  }
6615
6821
  for (const file of files) {
6616
6822
  try {
6617
- const record = parseRecord(JSON.parse(fs9.readFileSync(path8.join(subscriptionsDir, file), "utf-8")));
6823
+ const record = parseRecord(JSON.parse(fs11.readFileSync(path10.join(subscriptionsDir, file), "utf-8")));
6618
6824
  if (!record || file !== `${record.token}.json`) continue;
6619
6825
  if (record.sessionId === state.currentSessionId) {
6620
6826
  subscriptions.set(record.token, record);
6621
6827
  if (record.targetKind === "foreground" && !state.foregroundRuns?.has(record.runId)) unresolvedRestoredForegroundTokens.add(record.token);
6622
6828
  }
6623
6829
  } catch (error2) {
6624
- console.error(`Ignoring invalid wait subscription '${path8.join(subscriptionsDir, file)}':`, error2);
6830
+ console.error(`Ignoring invalid wait subscription '${path10.join(subscriptionsDir, file)}':`, error2);
6625
6831
  }
6626
6832
  }
6627
6833
  reconcile();
@@ -6644,25 +6850,25 @@ Parent action: ${STATUS_OUTCOMES.has(outcome) ? STATUS_ACTION : COMPLETION_ACTIO
6644
6850
  }
6645
6851
 
6646
6852
  // src/runs/shared/child-session.ts
6647
- import * as fs12 from "node:fs";
6648
- import * as path11 from "node:path";
6853
+ import * as fs14 from "node:fs";
6854
+ import * as path13 from "node:path";
6649
6855
  import { pathToFileURL } from "node:url";
6650
6856
 
6651
6857
  // src/runs/shared/child-commands.ts
6652
- import { randomUUID as randomUUID4 } from "node:crypto";
6653
- import * as fs10 from "node:fs";
6654
- import * as path9 from "node:path";
6858
+ import { randomUUID as randomUUID5 } from "node:crypto";
6859
+ import * as fs12 from "node:fs";
6860
+ import * as path11 from "node:path";
6655
6861
  import { Type } from "typebox";
6656
6862
  var CHILD_COMMAND_TOOL = "subagent_command";
6657
6863
  var writeAtomicJson2 = createAtomicJsonWriter({ mode: 384 });
6658
6864
  var MAX_OUTPUT_BYTES = 8 * 1024;
6659
6865
  var RECENT_COMMANDS = 20;
6660
6866
  function childCommandStatePath(channelDir) {
6661
- return path9.join(channelDir, "commands.json");
6867
+ return path11.join(channelDir, "commands.json");
6662
6868
  }
6663
6869
  function readChildCommandState(channelDir) {
6664
6870
  try {
6665
- return JSON.parse(fs10.readFileSync(childCommandStatePath(channelDir), "utf8"));
6871
+ return JSON.parse(fs12.readFileSync(childCommandStatePath(channelDir), "utf8"));
6666
6872
  } catch (error2) {
6667
6873
  if (error2.code === "ENOENT") return void 0;
6668
6874
  throw error2;
@@ -6678,7 +6884,7 @@ function text(result5) {
6678
6884
  return Buffer.from(result5.content.filter((part) => part.type === "text").map((part) => part.text).join("\n")).subarray(-MAX_OUTPUT_BYTES).toString("utf8");
6679
6885
  }
6680
6886
  function createChildCommandRuntime(channelDir) {
6681
- const ownerId = randomUUID4();
6887
+ const ownerId = randomUUID5();
6682
6888
  const jobs = /* @__PURE__ */ new Map();
6683
6889
  const usedIds = /* @__PURE__ */ new Set();
6684
6890
  let closed = false;
@@ -6834,7 +7040,7 @@ async function waitForCommand(settled, waitMs, signal) {
6834
7040
  }
6835
7041
  }
6836
7042
  function commandReplyPath(channelDir, id) {
6837
- return path9.join(channelDir, "command-replies", `${id}.json`);
7043
+ return path11.join(channelDir, "command-replies", `${id}.json`);
6838
7044
  }
6839
7045
  async function controlChildCommand(channelDir, operation, toolCallId, signal) {
6840
7046
  const current = readChildCommandState(channelDir);
@@ -6849,7 +7055,7 @@ async function controlChildCommand(channelDir, operation, toolCallId, signal) {
6849
7055
  throw new Error("Child command controller is closed.");
6850
7056
  }
6851
7057
  if (operation !== "status" && !toolCallId) throw new Error("An exact toolCallId is required; command controls never target the next command.");
6852
- const id = randomUUID4();
7058
+ const id = randomUUID5();
6853
7059
  const deadlineAt = Date.now() + 5e3;
6854
7060
  const request = { type: "command", id, ownerId: current.ownerId, operation, ...toolCallId ? { toolCallId } : {}, deadlineAt };
6855
7061
  requestAsyncCommand(channelDir, request);
@@ -6858,7 +7064,7 @@ async function controlChildCommand(channelDir, operation, toolCallId, signal) {
6858
7064
  while (Date.now() < deadlineAt) {
6859
7065
  if (signal?.aborted) throw new Error("Command control wait aborted; delivery may still be pending.");
6860
7066
  try {
6861
- const reply = JSON.parse(fs10.readFileSync(replyPath, "utf8"));
7067
+ const reply = JSON.parse(fs12.readFileSync(replyPath, "utf8"));
6862
7068
  if (reply.error) throw new Error(reply.error);
6863
7069
  if (reply.state) return reply.state;
6864
7070
  } catch (error2) {
@@ -6868,7 +7074,7 @@ async function controlChildCommand(channelDir, operation, toolCallId, signal) {
6868
7074
  }
6869
7075
  throw new Error("Command control acknowledgement timed out. Inspect command status; cancellation is not confirmed.");
6870
7076
  } finally {
6871
- fs10.rmSync(replyPath, { force: true });
7077
+ fs12.rmSync(replyPath, { force: true });
6872
7078
  }
6873
7079
  }
6874
7080
 
@@ -6892,8 +7098,8 @@ function pinChildCacheRetention(agent, env) {
6892
7098
  }
6893
7099
 
6894
7100
  // src/runs/background/runner-aliases.ts
6895
- import * as fs11 from "node:fs";
6896
- import * as path10 from "node:path";
7101
+ import * as fs13 from "node:fs";
7102
+ import * as path12 from "node:path";
6897
7103
  var JITI_ALIAS_ENV = "JITI_ALIAS";
6898
7104
  var HOST_PEER_ALIASES = [
6899
7105
  { specifier: "@earendil-works/pi-coding-agent", pkg: "@earendil-works/pi-coding-agent", subpath: "." },
@@ -6916,7 +7122,7 @@ var CHORD_PEER_ALIASES = [
6916
7122
  ];
6917
7123
  function readManifest(packageDir) {
6918
7124
  try {
6919
- return JSON.parse(fs11.readFileSync(path10.join(packageDir, "package.json"), "utf-8"));
7125
+ return JSON.parse(fs13.readFileSync(path12.join(packageDir, "package.json"), "utf-8"));
6920
7126
  } catch {
6921
7127
  return void 0;
6922
7128
  }
@@ -6953,7 +7159,7 @@ function resolvePackageSubpath(packageDir, subpath) {
6953
7159
  const map = typeof exportsField === "string" || Array.isArray(exportsField) || exportsField && typeof exportsField === "object" && !Object.keys(exportsField).some((key) => key.startsWith(".")) ? { ".": exportsField } : exportsField;
6954
7160
  if (subpath in map) {
6955
7161
  const exact = exportTarget(map[subpath]);
6956
- return typeof exact === "string" ? path10.resolve(packageDir, exact) : void 0;
7162
+ return typeof exact === "string" ? path12.resolve(packageDir, exact) : void 0;
6957
7163
  }
6958
7164
  let best;
6959
7165
  for (const [pattern, entry] of Object.entries(map)) {
@@ -6968,23 +7174,23 @@ function resolvePackageSubpath(packageDir, subpath) {
6968
7174
  const target = exportTarget(best.entry);
6969
7175
  if (typeof target !== "string") return void 0;
6970
7176
  const wildcard = subpath.slice(best.prefix.length, subpath.length - best.suffix.length);
6971
- return path10.resolve(packageDir, target.replaceAll("*", () => wildcard));
7177
+ return path12.resolve(packageDir, target.replaceAll("*", () => wildcard));
6972
7178
  }
6973
7179
  if (subpath !== ".") return void 0;
6974
- return path10.resolve(packageDir, typeof manifest.main === "string" && manifest.main.trim() ? manifest.main : "index.js");
7180
+ return path12.resolve(packageDir, typeof manifest.main === "string" && manifest.main.trim() ? manifest.main : "index.js");
6975
7181
  }
6976
7182
  function findPeerPackageDir(piPackageRoot, pkg, hostName) {
6977
7183
  if (hostName === pkg) return piPackageRoot;
6978
- const candidates = [path10.join(piPackageRoot, "node_modules", pkg)];
7184
+ const candidates = [path12.join(piPackageRoot, "node_modules", pkg)];
6979
7185
  let dir = piPackageRoot;
6980
7186
  for (; ; ) {
6981
- const parent = path10.dirname(dir);
7187
+ const parent = path12.dirname(dir);
6982
7188
  if (parent === dir) break;
6983
- if (path10.basename(parent) === "node_modules" || path10.basename(path10.dirname(parent)) === "node_modules") {
6984
- const modulesRoot = path10.basename(parent) === "node_modules" ? parent : path10.dirname(parent);
6985
- candidates.push(path10.join(modulesRoot, pkg));
7189
+ if (path12.basename(parent) === "node_modules" || path12.basename(path12.dirname(parent)) === "node_modules") {
7190
+ const modulesRoot = path12.basename(parent) === "node_modules" ? parent : path12.dirname(parent);
7191
+ candidates.push(path12.join(modulesRoot, pkg));
6986
7192
  }
6987
- candidates.push(path10.join(parent, "node_modules", pkg));
7193
+ candidates.push(path12.join(parent, "node_modules", pkg));
6988
7194
  dir = parent;
6989
7195
  }
6990
7196
  return candidates.find((candidate) => readManifest(candidate)?.name === pkg);
@@ -7001,7 +7207,7 @@ function resolveHostPeerAliases(piPackageRoot) {
7001
7207
  const packageDir = findPeerPackageDir(piPackageRoot, pkg, hostManifest?.name);
7002
7208
  const target = packageDir ? resolvePackageSubpath(packageDir, subpath) : void 0;
7003
7209
  if (optional && packageDir && target === void 0) continue;
7004
- if (target && fs11.existsSync(target)) aliases[specifier] = fs11.realpathSync(target);
7210
+ if (target && fs13.existsSync(target)) aliases[specifier] = fs13.realpathSync(target);
7005
7211
  else if (pkg !== CHORD_PACKAGE || chordRequired) missing.push(specifier);
7006
7212
  }
7007
7213
  return { aliases, missing };
@@ -7049,14 +7255,36 @@ function inheritParentProviders(modelRuntime, parentProviders, claimedProviderId
7049
7255
  }
7050
7256
  var CHILD_PROMPT_RUNTIME_EXTENSION_PATH = "<inline:pi-subagents:prompt-runtime>";
7051
7257
  var CHILD_PROMPT_BOUNDARY_EXTENSION_PATH = "<inline:pi-subagents:prompt-boundary>";
7258
+ var PI_UNICODE_SPACES = new RegExp("[\\u00A0\\u2000-\\u200A\\u202F\\u205F\\u3000]", "g");
7259
+ function childExtensionFileIdentity(extensionPath) {
7260
+ if (!path13.isAbsolute(extensionPath)) return void 0;
7261
+ try {
7262
+ const stat = fs14.statSync(extensionPath, { bigint: true });
7263
+ return stat.isFile() ? `${stat.dev}:${stat.ino}` : void 0;
7264
+ } catch {
7265
+ return void 0;
7266
+ }
7267
+ }
7268
+ function createRequiredChildExtensionIdentity(requiredPaths) {
7269
+ const aliases = new Map([...requiredPaths].map((extensionPath) => [extensionPath, childExtensionFileIdentity(extensionPath) ?? extensionPath]));
7270
+ const identities = new Set(aliases.values());
7271
+ return (extensionPath) => {
7272
+ const known = aliases.get(extensionPath);
7273
+ if (known !== void 0) return known;
7274
+ const identity = childExtensionFileIdentity(extensionPath);
7275
+ if (identity === void 0 || !identities.has(identity)) return void 0;
7276
+ aliases.set(extensionPath, identity);
7277
+ return identity;
7278
+ };
7279
+ }
7052
7280
  function orderChildPromptHooks(result5) {
7053
- const runtime = result5.extensions.filter(({ path: path15 }) => path15 === CHILD_PROMPT_RUNTIME_EXTENSION_PATH);
7054
- const boundary = result5.extensions.filter(({ path: path15 }) => path15 === CHILD_PROMPT_BOUNDARY_EXTENSION_PATH);
7055
- const others = result5.extensions.filter(({ path: path15 }) => path15 !== CHILD_PROMPT_RUNTIME_EXTENSION_PATH && path15 !== CHILD_PROMPT_BOUNDARY_EXTENSION_PATH);
7281
+ const runtime = result5.extensions.filter(({ path: path17 }) => path17 === CHILD_PROMPT_RUNTIME_EXTENSION_PATH);
7282
+ const boundary = result5.extensions.filter(({ path: path17 }) => path17 === CHILD_PROMPT_BOUNDARY_EXTENSION_PATH);
7283
+ const others = result5.extensions.filter(({ path: path17 }) => path17 !== CHILD_PROMPT_RUNTIME_EXTENSION_PATH && path17 !== CHILD_PROMPT_BOUNDARY_EXTENSION_PATH);
7056
7284
  return { ...result5, extensions: [...runtime, ...others, ...boundary] };
7057
7285
  }
7058
7286
  function prioritizeChildCommandRuntime(result5) {
7059
- const index = result5.extensions.findIndex(({ path: path15 }) => path15 === "<inline:pi-subagents:commands>");
7287
+ const index = result5.extensions.findIndex(({ path: path17 }) => path17 === "<inline:pi-subagents:commands>");
7060
7288
  if (index < 0) return result5;
7061
7289
  if (result5.extensions.some((extension, otherIndex) => otherIndex !== index && extension.tools?.has("bash"))) {
7062
7290
  const extensions2 = result5.extensions.filter((_, otherIndex) => otherIndex !== index);
@@ -7160,7 +7388,7 @@ function missingBuiltinMcpTools(session, names) {
7160
7388
  const direct = new Set(session.getAllTools().filter((tool) => tool.exposure === "direct").map(({ name }) => name));
7161
7389
  return names.filter((name) => !active2.has(name) || !direct.has(name));
7162
7390
  }
7163
- function flushQueuedProviderRegistrations(loader, modelRuntime, onError, requiredPaths) {
7391
+ function flushQueuedProviderRegistrations(loader, modelRuntime, onError, isRequiredSource) {
7164
7392
  const claimedProviderIds = /* @__PURE__ */ new Set();
7165
7393
  if (!("getExtensions" in loader) || typeof loader.getExtensions !== "function") return { claimedProviderIds, registered: false };
7166
7394
  const { runtime } = loader.getExtensions();
@@ -7172,7 +7400,7 @@ function flushQueuedProviderRegistrations(loader, modelRuntime, onError, require
7172
7400
  registered = true;
7173
7401
  } catch (error2) {
7174
7402
  onError?.({ extensionPath, event: "register_provider", error: error2 });
7175
- if (requiredPaths.has(extensionPath)) throw new Error(`Required child extension provider registration failed for '${extensionPath}': ${error2 instanceof Error ? error2.message : String(error2)}`);
7403
+ if (isRequiredSource(extensionPath)) throw new Error(`Required child extension provider registration failed for '${extensionPath}': ${error2 instanceof Error ? error2.message : String(error2)}`);
7176
7404
  }
7177
7405
  }
7178
7406
  if (Array.isArray(runtime.pendingProviderRegistrations)) runtime.pendingProviderRegistrations = [];
@@ -7183,7 +7411,7 @@ function flushQueuedProviderRegistrations(loader, modelRuntime, onError, require
7183
7411
  registered = true;
7184
7412
  } catch (error2) {
7185
7413
  onError?.({ extensionPath, event: "register_provider", error: error2 });
7186
- if (requiredPaths.has(extensionPath)) throw new Error(`Required child extension provider registration failed for '${extensionPath}': ${error2 instanceof Error ? error2.message : String(error2)}`);
7414
+ if (isRequiredSource(extensionPath)) throw new Error(`Required child extension provider registration failed for '${extensionPath}': ${error2 instanceof Error ? error2.message : String(error2)}`);
7187
7415
  }
7188
7416
  }
7189
7417
  if (Array.isArray(runtime.pendingNativeProviderRegistrations)) runtime.pendingNativeProviderRegistrations = [];
@@ -7197,7 +7425,7 @@ function flushQueuedProviderRegistrations(loader, modelRuntime, onError, require
7197
7425
  registered = true;
7198
7426
  } catch (error2) {
7199
7427
  onError?.({ extensionPath, event: "register_virtual_model", error: error2 });
7200
- if (requiredPaths.has(extensionPath)) throw new Error(`Required child extension virtual model registration failed for '${extensionPath}': ${error2 instanceof Error ? error2.message : String(error2)}`);
7428
+ if (isRequiredSource(extensionPath)) throw new Error(`Required child extension virtual model registration failed for '${extensionPath}': ${error2 instanceof Error ? error2.message : String(error2)}`);
7201
7429
  }
7202
7430
  }
7203
7431
  if (Array.isArray(virtualModelQueue.pendingVirtualModelRegistrations)) virtualModelQueue.pendingVirtualModelRegistrations = [];
@@ -7207,14 +7435,14 @@ async function loadHostPiCodingAgent() {
7207
7435
  const overrideRoot = process.env[PI_CODING_AGENT_PACKAGE_ROOT_ENV]?.trim() || void 0;
7208
7436
  const root = resolveHostPiPackageRoot();
7209
7437
  if (root) {
7210
- const entry = fs12.realpathSync(resolveHostPackageEntry(root, root === overrideRoot ? overrideRoot : void 0));
7438
+ const entry = fs14.realpathSync(resolveHostPackageEntry(root, root === overrideRoot ? overrideRoot : void 0));
7211
7439
  return import(pathToFileURL(entry).href);
7212
7440
  }
7213
7441
  return import(PI_CODING_AGENT_PACKAGE);
7214
7442
  }
7215
7443
  function resolveHostPackageEntry(root, overrideRoot) {
7216
- const packageJson = path11.join(root, "package.json");
7217
- const source = fs12.readFileSync(packageJson, "utf8");
7444
+ const packageJson = path13.join(root, "package.json");
7445
+ const source = fs14.readFileSync(packageJson, "utf8");
7218
7446
  let parsed;
7219
7447
  try {
7220
7448
  parsed = JSON.parse(source);
@@ -7275,6 +7503,12 @@ function createDefaultChildSessionFactory(options = {}) {
7275
7503
  return typeof value === "function" ? value.bind(target) : value;
7276
7504
  }
7277
7505
  })) }] : [];
7506
+ const requiredPaths = new Set((launch.requiredExtensions ?? []).map(({ path: path17 }) => path17));
7507
+ const requiredExtensionIdentity = createRequiredChildExtensionIdentity(requiredPaths);
7508
+ const resolvedExtensionPaths = /* @__PURE__ */ new Map();
7509
+ const isRequiredSource = (extensionPath) => requiredPaths.size > 0 && requiredExtensionIdentity(
7510
+ resolvedExtensionPaths.get(extensionPath) ?? extensionPath.replace(PI_UNICODE_SPACES, " ")
7511
+ ) !== void 0;
7278
7512
  const loader = new pi.DefaultResourceLoader({
7279
7513
  cwd: launch.cwd,
7280
7514
  agentDir,
@@ -7311,13 +7545,18 @@ function createDefaultChildSessionFactory(options = {}) {
7311
7545
  };
7312
7546
  let inheritedProviders = /* @__PURE__ */ new Set();
7313
7547
  const open = async () => {
7314
- const requiredPaths = new Set((launch.requiredExtensions ?? []).map(({ path: path15 }) => path15));
7315
7548
  applyProcessEnv(launch.processEnv);
7316
7549
  if (!resetExtensionCacheOnReload(loader) && (launch.ambientExtensions || launch.extensionPaths.length)) launch.onExtensionError?.({ extensionPath: "<loader>", event: "load", error: new Error("pi's extension cache reset is unavailable; extensions loaded into this child share module state with other sessions in this process.") });
7317
7550
  await loader.reload();
7318
- const loadErrors = requiredPaths.size > 0 ? loader.getExtensions().errors.filter(({ path: path15 }) => requiredPaths.has(path15)) : [];
7319
- if (loadErrors.length > 0) throw new Error(`Required child extension failed to load: ${loadErrors.map(({ path: path15, error: error2 }) => `${path15}: ${error2}`).join("; ")}`);
7320
- const queued = flushQueuedProviderRegistrations(loader, modelRuntime, launch.onExtensionError, requiredPaths);
7551
+ if (requiredPaths.size > 0) for (const extension of loader.getExtensions().extensions) resolvedExtensionPaths.set(extension.path, extension.resolvedPath);
7552
+ const loadErrors = requiredPaths.size > 0 ? loader.getExtensions().errors.filter(({ path: path17 }) => isRequiredSource(path17)) : [];
7553
+ if (loadErrors.length > 0) throw new Error(`Required child extension failed to load: ${loadErrors.map(({ path: path17, error: error2 }) => `${path17}: ${error2}`).join("; ")}`);
7554
+ const queued = flushQueuedProviderRegistrations(loader, modelRuntime, launch.onExtensionError, isRequiredSource);
7555
+ if (requiredPaths.size > 0) {
7556
+ const loadedIdentities = new Set(loader.getExtensions().extensions.map(({ resolvedPath }) => requiredExtensionIdentity(resolvedPath)));
7557
+ const missing = [...requiredPaths].filter((extensionPath) => !loadedIdentities.has(requiredExtensionIdentity(extensionPath)));
7558
+ if (missing.length) throw new Error(`Required child extension failed to load: ${missing.join(", ")}`);
7559
+ }
7321
7560
  if (launch.parentProviderRegistry) inheritedProviders = inheritParentProviders(modelRuntime, launch.parentProviderRegistry, queued.claimedProviderIds, launch.onExtensionError);
7322
7561
  if (queued.registered || inheritedProviders.size > 0) {
7323
7562
  try {
@@ -7328,7 +7567,7 @@ function createDefaultChildSessionFactory(options = {}) {
7328
7567
  }
7329
7568
  }
7330
7569
  const newSessionOptions = launch.parentSessionFile ? { parentSession: launch.parentSessionFile } : void 0;
7331
- const newSessionFile = launch.storage.kind === "file" && !fs12.existsSync(launch.storage.sessionFile);
7570
+ const newSessionFile = launch.storage.kind === "file" && !fs14.existsSync(launch.storage.sessionFile);
7332
7571
  const sessionManager = launch.storage.kind === "file" ? pi.SessionManager.open(launch.storage.sessionFile, void 0, launch.cwd) : launch.storage.kind === "dir" ? pi.SessionManager.create(launch.cwd, launch.storage.sessionDir, newSessionOptions) : launch.storage.kind === "memory" ? pi.SessionManager.inMemory(launch.cwd, newSessionOptions) : pi.SessionManager.create(launch.cwd, void 0, newSessionOptions);
7333
7572
  const header = newSessionFile && launch.parentSessionFile ? sessionManager.getHeader() : null;
7334
7573
  if (header) header.parentSession = launch.parentSessionFile;
@@ -7354,7 +7593,7 @@ function createDefaultChildSessionFactory(options = {}) {
7354
7593
  await session2.bindExtensions({
7355
7594
  mode: "print",
7356
7595
  onError: (error2) => {
7357
- if (binding && requiredPaths.has(error2.extensionPath)) requiredStartupErrors.push(`${error2.extensionPath} (${error2.event}): ${error2.error}`);
7596
+ if (binding && isRequiredSource(error2.extensionPath)) requiredStartupErrors.push(`${error2.extensionPath} (${error2.event}): ${error2.error}`);
7358
7597
  launch.onExtensionError?.({ extensionPath: error2.extensionPath, event: error2.event, error: error2.error });
7359
7598
  }
7360
7599
  });
@@ -7382,7 +7621,7 @@ function createDefaultChildSessionFactory(options = {}) {
7382
7621
  if (disposals !== disposalsAtStart) cancel.abort();
7383
7622
  const ready = (async () => {
7384
7623
  try {
7385
- await builtinMcp.waitForTools(signal, () => loader.getExtensions().errors.find(({ path: path15 }) => path15 === "builtin:mcp")?.error);
7624
+ await builtinMcp.waitForTools(signal, () => loader.getExtensions().errors.find(({ path: path17 }) => path17 === "builtin:mcp")?.error);
7386
7625
  if (disposals !== disposalsAtStart) throw new Error("The child session factory was disposed while the child waited for its MCP tools.");
7387
7626
  if (signal.aborted) throw new Error("The child launch was cancelled while waiting for its MCP tools.");
7388
7627
  const missing = missingBuiltinMcpTools(session, builtinMcpTools.map(({ name }) => name));
@@ -7471,7 +7710,7 @@ function childSessionFactory() {
7471
7710
  }
7472
7711
  function createLazyPlacementFactory(local) {
7473
7712
  let placed;
7474
- const factory = async () => placed ??= (await import("./chunk-TUWLXTAM.js")).createPlacementAwareChildSessionFactory(local);
7713
+ const factory = async () => placed ??= (await import("./chunk-MHZ4G23A.js")).createPlacementAwareChildSessionFactory(local);
7475
7714
  return {
7476
7715
  async create(launch) {
7477
7716
  return launch.machine ? (await factory()).create(launch) : local.create(launch);
@@ -7493,13 +7732,10 @@ async function disposeChildSessions() {
7493
7732
 
7494
7733
  // src/workflows/workflow-reuse.ts
7495
7734
  import { createHash as createHash3 } from "node:crypto";
7496
- import * as fs13 from "node:fs";
7497
- import * as path12 from "node:path";
7498
- var WORKFLOW_STOP_CAUSE_RUNTIME_REPLACED = "runtime-replaced";
7735
+ import * as fs15 from "node:fs";
7736
+ import * as path14 from "node:path";
7499
7737
  var WORKFLOW_CHILD_JOURNAL_FILE = "workflow-children.jsonl";
7500
- function workflowRuntimeReplacedRelaunchNotice(runId) {
7501
- return `Async children that were still running keep running. Relaunching the same workflow script with the same args from this session reuses finished children and re-attaches to running ones; from another session, add options: { reuseFrom: "${runId}" }.`;
7502
- }
7738
+ var WORKFLOW_RUNTIME_REPLACED_STOP_NOTICE = "Async children that were still running keep running, and finished children are kept.";
7503
7739
  function runtimeReplacedAbortReason() {
7504
7740
  return Object.assign(new Error("Workflow stopped because the extension session was replaced or reloaded."), { workflowStopCause: WORKFLOW_STOP_CAUSE_RUNTIME_REPLACED });
7505
7741
  }
@@ -7516,9 +7752,9 @@ function workflowChildFingerprint(params) {
7516
7752
  return sha256(workflowRunParamsFingerprint(params));
7517
7753
  }
7518
7754
  function appendWorkflowChildJournal(workflowAsyncDir, record) {
7519
- const journalPath = path12.join(workflowAsyncDir, WORKFLOW_CHILD_JOURNAL_FILE);
7755
+ const journalPath = path14.join(workflowAsyncDir, WORKFLOW_CHILD_JOURNAL_FILE);
7520
7756
  try {
7521
- fs13.appendFileSync(journalPath, `${JSON.stringify(record)}
7757
+ fs15.appendFileSync(journalPath, `${JSON.stringify(record)}
7522
7758
  `, "utf-8");
7523
7759
  } catch (error2) {
7524
7760
  console.error(`Failed to append workflow child journal '${journalPath}':`, error2);
@@ -7528,7 +7764,7 @@ function readWorkflowChildJournal(runId, workflowAsyncDir) {
7528
7764
  const source = { runId, started: /* @__PURE__ */ new Map(), settled: /* @__PURE__ */ new Map() };
7529
7765
  let text2 = "";
7530
7766
  try {
7531
- text2 = fs13.readFileSync(path12.join(workflowAsyncDir, WORKFLOW_CHILD_JOURNAL_FILE), "utf-8");
7767
+ text2 = fs15.readFileSync(path14.join(workflowAsyncDir, WORKFLOW_CHILD_JOURNAL_FILE), "utf-8");
7532
7768
  } catch {
7533
7769
  return source;
7534
7770
  }
@@ -7548,6 +7784,44 @@ function readWorkflowChildJournal(runId, workflowAsyncDir) {
7548
7784
  }
7549
7785
  return source;
7550
7786
  }
7787
+ var WORKFLOW_REUSE_CHAIN_MAX_LINKS = 16;
7788
+ function readWorkflowReuseChain(asyncDirRoot, headRunId, headStatus) {
7789
+ const source = readWorkflowChildJournal(headRunId, path14.join(asyncDirRoot, headRunId));
7790
+ const visited = /* @__PURE__ */ new Set([headRunId]);
7791
+ let next = headStatus.workflow?.reusedFrom;
7792
+ for (let links = 0; isRunId(next) && links < WORKFLOW_REUSE_CHAIN_MAX_LINKS && !visited.has(next); links++) {
7793
+ visited.add(next);
7794
+ const asyncDir = path14.join(asyncDirRoot, next);
7795
+ let status;
7796
+ try {
7797
+ status = readStatus(asyncDir);
7798
+ } catch {
7799
+ break;
7800
+ }
7801
+ if (status?.mode !== "workflow" || status.workflow?.scriptDigest !== headStatus.workflow?.scriptDigest) break;
7802
+ const link = readWorkflowChildJournal(next, asyncDir);
7803
+ for (const key of /* @__PURE__ */ new Set([...link.started.keys(), ...link.settled.keys()])) {
7804
+ if (source.started.has(key) || source.settled.has(key)) continue;
7805
+ const started = link.started.get(key);
7806
+ const settled = link.settled.get(key);
7807
+ if (started) source.started.set(key, started);
7808
+ if (settled) source.settled.set(key, settled);
7809
+ }
7810
+ next = status.workflow?.reusedFrom;
7811
+ }
7812
+ return source;
7813
+ }
7814
+ function claimedWorkflowRunRefusal(runId, claim, named) {
7815
+ const run = `${named ? "options.reuseFrom: workflow" : "Workflow"} run '${runId}'`;
7816
+ if (claim.kind === "resumed") return `${run} was resumed as ${claim.newRunId}; wait on it or stop it.`;
7817
+ if (claim.kind === "busy") return `${run} is being resumed by pid ${claim.pid}; wait for it.`;
7818
+ return `${run} has a resume claim that cannot be read; wait or remove it.`;
7819
+ }
7820
+ function assertWorkflowRunNotClaimed(asyncDir, runId, named, claimOwnerRunId) {
7821
+ const claim = workflowResumeClaimState(asyncDir);
7822
+ if (claim.kind === "none" || claim.kind !== "unreadable" && claim.newRunId === claimOwnerRunId) return;
7823
+ throw new Error(claimedWorkflowRunRefusal(runId, claim, named));
7824
+ }
7551
7825
  function findWorkflowReuseSource(asyncDirRoot, sessionId, scriptDigest, argsDigest) {
7552
7826
  let runIds;
7553
7827
  try {
@@ -7556,7 +7830,7 @@ function findWorkflowReuseSource(asyncDirRoot, sessionId, scriptDigest, argsDige
7556
7830
  return void 0;
7557
7831
  }
7558
7832
  for (const runId of runIds) {
7559
- const asyncDir = path12.join(asyncDirRoot, runId);
7833
+ const asyncDir = path14.join(asyncDirRoot, runId);
7560
7834
  let status;
7561
7835
  try {
7562
7836
  status = readStatus(asyncDir);
@@ -7564,18 +7838,23 @@ function findWorkflowReuseSource(asyncDirRoot, sessionId, scriptDigest, argsDige
7564
7838
  continue;
7565
7839
  }
7566
7840
  if (status?.mode !== "workflow" || status.workflow?.scriptDigest !== scriptDigest || status.workflow.argsDigest !== argsDigest) continue;
7567
- return status.workflow.stopCause === WORKFLOW_STOP_CAUSE_RUNTIME_REPLACED ? readWorkflowChildJournal(runId, asyncDir) : void 0;
7841
+ if (!isReusableWorkflowStopCause(status.workflow.stopCause)) return void 0;
7842
+ assertWorkflowRunNotClaimed(asyncDir, runId, false);
7843
+ return readWorkflowReuseChain(asyncDirRoot, runId, status);
7568
7844
  }
7569
7845
  return void 0;
7570
7846
  }
7571
- function readNamedWorkflowReuseSource(asyncDirRoot, runId, scriptDigest, cwd) {
7572
- const status = runId === path12.basename(runId) && runId !== "." && runId !== ".." ? readStatus(path12.join(asyncDirRoot, runId)) : null;
7847
+ function readNamedWorkflowReuseSource(asyncDirRoot, runId, scriptDigest, cwd, claimOwnerRunId) {
7848
+ const runName = isRunId(runId);
7849
+ if (runName) reconcileAsyncRun(path14.join(asyncDirRoot, runId));
7850
+ const status = runName ? readStatus(path14.join(asyncDirRoot, runId)) : null;
7573
7851
  if (!status) throw new Error(`options.reuseFrom: workflow run '${runId}' was not found.`);
7574
7852
  if (status.mode !== "workflow") throw new Error(`options.reuseFrom: run '${runId}' is not a workflow run.`);
7575
- if (status.workflow?.stopCause !== WORKFLOW_STOP_CAUSE_RUNTIME_REPLACED) throw new Error(`options.reuseFrom: workflow run '${runId}' did not stop because its session was replaced, so its children cannot be reused.`);
7853
+ if (!isReusableWorkflowStopCause(status.workflow?.stopCause)) throw new Error(`options.reuseFrom: workflow run '${runId}' did not stop because its session was replaced or its Pi process exited, so its children cannot be reused.`);
7576
7854
  if (status.workflow.scriptDigest !== scriptDigest) throw new Error(`options.reuseFrom: workflow run '${runId}' ran a different workflow script.`);
7577
7855
  if (status.cwd !== cwd) throw new Error(`options.reuseFrom: workflow run '${runId}' ran in '${status.cwd}', not '${cwd}'.`);
7578
- return readWorkflowChildJournal(runId, path12.join(asyncDirRoot, runId));
7856
+ assertWorkflowRunNotClaimed(path14.join(asyncDirRoot, runId), runId, true, claimOwnerRunId);
7857
+ return readWorkflowReuseChain(asyncDirRoot, runId, status);
7579
7858
  }
7580
7859
  function matchWorkflowReuse(source, key, fingerprint) {
7581
7860
  const settled = source.settled.get(key);
@@ -7584,6 +7863,74 @@ function matchWorkflowReuse(source, key, fingerprint) {
7584
7863
  return started?.fingerprint === fingerprint ? { kind: "started", runId: started.runId } : void 0;
7585
7864
  }
7586
7865
 
7866
+ // src/workflows/workflow-resume.ts
7867
+ import { randomUUID as randomUUID6 } from "node:crypto";
7868
+ import * as path15 from "node:path";
7869
+ var MAX_WORKFLOW_RESUME_ATTEMPTS = 3;
7870
+ function workflowResumeRefusal(code, reason, runId) {
7871
+ const byHand = code === "launch-missing" || code === "launch-invalid" || code === "structured" || code === "digest" || code === "authority" ? relaunchByHandText(runId) : code === "capacity" || code === "attempts" || code === "launch-failed" ? resumeByHandText(runId) : "";
7872
+ return { code, reason, byHand };
7873
+ }
7874
+ function manualWorkflowResumeRefusalText(runId, refusal) {
7875
+ return `Cannot resume workflow '${runId}': ${refusalSentence(refusal.reason, refusal.byHand === relaunchByHandText(runId) ? refusal.byHand : "")}`;
7876
+ }
7877
+ function workflowResumeTargetId(asyncDirRoot, params) {
7878
+ if (params.message !== void 0 || params.task !== void 0 || params.index !== void 0) return void 0;
7879
+ if (Array.isArray(params.chain) && params.chain.length > 0) return void 0;
7880
+ const id = params.id ?? params.runId;
7881
+ if (!isRunId(id)) return void 0;
7882
+ let status;
7883
+ try {
7884
+ status = readStatus(path15.join(asyncDirRoot, id));
7885
+ } catch {
7886
+ return void 0;
7887
+ }
7888
+ return status?.mode === "workflow" && status.parentWorkflowRunId === void 0 ? id : void 0;
7889
+ }
7890
+ function prepareWorkflowResume(input) {
7891
+ const { asyncDirRoot, sourceRunId, trigger } = input;
7892
+ const refuse = (code, reason) => ({ ok: false, ...workflowResumeRefusal(code, reason, sourceRunId) });
7893
+ const sourceAsyncDir = path15.join(asyncDirRoot, sourceRunId);
7894
+ const stored = readStatus(sourceAsyncDir);
7895
+ if (stored && !ownsRun(input.currentSessionId, stored.sessionId)) return refuse("not-owned", "it was started in another session; reopen that session to resume it");
7896
+ if (trigger === "manual") reconcileAsyncRun(sourceAsyncDir);
7897
+ const status = readStatus(sourceAsyncDir);
7898
+ if (!status || status.mode !== "workflow" || !isReusableWorkflowStopCause(status.workflow?.stopCause)) return refuse("not-interrupted", "not interrupted; pass message to revive one of its children");
7899
+ const attempt = (status.workflow?.resumeOrigin?.attempt ?? 0) + (status.workflow?.stopCause === WORKFLOW_STOP_CAUSE_OWNER_EXITED ? 1 : 0);
7900
+ if (trigger === "auto" && attempt > MAX_WORKFLOW_RESUME_ATTEMPTS) return refuse("attempts", `${MAX_WORKFLOW_RESUME_ATTEMPTS} resumes already followed Pi exits`);
7901
+ const read = readWorkflowLaunchRecord(sourceAsyncDir);
7902
+ if (!read.ok) return read.missing ? refuse("launch-missing", "launch.json is missing (the run started before launches were saved)") : refuse("launch-invalid", `launch.json is invalid: ${read.error}`);
7903
+ const record = read.record;
7904
+ if (record.source === "structured") return refuse("structured", "launched from tasks/chain");
7905
+ if (record.source === "script" && (typeof record.params.workflowScript !== "string" || workflowScriptDigest(record.params.workflowScript) !== record.scriptDigest)) {
7906
+ return refuse("launch-invalid", "launch.json is invalid: its script does not match its scriptDigest");
7907
+ }
7908
+ const normalized = normalizePublicSubagentExecution(record.params);
7909
+ if (!normalized.ok) return refuse("launch-invalid", `launch.json is invalid: ${normalized.error}`);
7910
+ const newRunId = input.newRunId ?? randomUUID6();
7911
+ const claim = claimWorkflowResume(sourceAsyncDir, { newRunId, trigger, now: input.now });
7912
+ if (claim.kind === "resumed") return refuse("claim-resumed", `resumed as ${claim.newRunId}; wait on it or stop it`);
7913
+ if (claim.kind === "busy") return refuse("claim-busy", `being resumed by pid ${claim.pid}`);
7914
+ if (claim.kind === "unreadable") return refuse("claim-busy", "its resume claim cannot be read; wait or remove it");
7915
+ return {
7916
+ ok: true,
7917
+ prepared: {
7918
+ sourceRunId,
7919
+ sourceAsyncDir,
7920
+ newRunId,
7921
+ trigger,
7922
+ params: {
7923
+ ...record.params,
7924
+ async: true,
7925
+ reuseFrom: sourceRunId,
7926
+ ...status.admissionCapabilityCeiling ? { capabilityCeiling: status.admissionCapabilityCeiling } : {}
7927
+ },
7928
+ expectedScriptDigest: record.scriptDigest,
7929
+ resumeOrigin: { sourceRunId, attempt }
7930
+ }
7931
+ };
7932
+ }
7933
+
7587
7934
  // src/extension/subagent-options.ts
7588
7935
  import { isDeepStrictEqual } from "node:util";
7589
7936
  import { Compile } from "typebox/compile";
@@ -7622,140 +7969,8 @@ function flattenSubagentToolOptions(params, disabled) {
7622
7969
  return { ...topLevel, ...options };
7623
7970
  }
7624
7971
 
7625
- // src/runs/foreground/foreground-history.ts
7626
- import * as fs14 from "node:fs";
7627
- import * as path13 from "node:path";
7628
- var MAX_REMEMBERED_FOREGROUND_RUNS = 50;
7629
- var HISTORY_VERSION = 1;
7630
- var MAX_INLINE_OUTPUT_BYTES = 64 * 1024;
7631
- var MAX_RESUME_CONTRACT_BYTES = 64 * 1024;
7632
- function historyPath(resultsDir) {
7633
- return path13.join(resultsDir, "foreground-history.json");
7634
- }
7635
- function boundedTail(value) {
7636
- return utf8Tail(value, MAX_INLINE_OUTPUT_BYTES).text;
7637
- }
7638
- function compactChild(child) {
7639
- const outputPath = child.artifactPaths?.outputPath ?? child.savedOutputPath;
7640
- return {
7641
- agent: child.agent,
7642
- index: child.index,
7643
- ...child.sessionName ? { sessionName: child.sessionName } : {},
7644
- ...child.context ? { context: child.context } : {},
7645
- ...child.sessionFile ? { sessionFile: child.sessionFile } : {},
7646
- ...child.model ? { model: child.model } : {},
7647
- ...child.thinking ? { thinking: child.thinking } : {},
7648
- status: child.status,
7649
- ...child.activityState ? { activityState: child.activityState } : {},
7650
- ...child.lastActivityAt !== void 0 ? { lastActivityAt: child.lastActivityAt } : {},
7651
- ...child.currentTool ? { currentTool: child.currentTool } : {},
7652
- ...child.currentToolStartedAt !== void 0 ? { currentToolStartedAt: child.currentToolStartedAt } : {},
7653
- ...child.currentPath ? { currentPath: child.currentPath } : {},
7654
- ...child.turnCount !== void 0 ? { turnCount: child.turnCount } : {},
7655
- ...child.tokens !== void 0 ? { tokens: child.tokens } : {},
7656
- ...child.toolCount !== void 0 ? { toolCount: child.toolCount } : {},
7657
- ...child.exitCode !== void 0 ? { exitCode: child.exitCode } : {},
7658
- ...child.error ? { error: child.error } : {},
7659
- ...!outputPath && child.finalOutput ? { finalOutput: boundedTail(child.finalOutput) } : {},
7660
- ...child.outputState ? { outputState: child.outputState } : {},
7661
- ...child.outputPartial ? { outputPartial: true } : {},
7662
- ...child.outputMode ? { outputMode: child.outputMode } : {},
7663
- ...child.savedOutputPath ? { savedOutputPath: child.savedOutputPath } : {},
7664
- ...child.outputSaveError ? { outputSaveError: child.outputSaveError } : {},
7665
- ...child.artifactPaths ? { artifactPaths: child.artifactPaths } : {},
7666
- ...child.transcriptPath ? { transcriptPath: child.transcriptPath } : {},
7667
- ...child.transcriptError ? { transcriptError: child.transcriptError } : {},
7668
- ...child.acceptance ? { acceptance: child.acceptance } : {},
7669
- ...child.resumeContract ? { resumeContract: child.resumeContract } : {},
7670
- ...child.launchContractDigest ? { launchContractDigest: child.launchContractDigest } : {},
7671
- ...child.extensionBindings ? { extensionBindings: child.extensionBindings } : {},
7672
- ...child.requiredExtensions ? { requiredExtensions: child.requiredExtensions } : {},
7673
- ...child.capabilityCeiling ? { capabilityCeiling: child.capabilityCeiling } : {},
7674
- ...child.updatedAt !== void 0 ? { updatedAt: child.updatedAt } : {}
7675
- };
7676
- }
7677
- function isRestorableForegroundStatus(status) {
7678
- return status === "completed" || status === "failed" || status === "paused" || status === "stopped";
7679
- }
7680
- function isRestorableResumeContract(value) {
7681
- if (value === void 0) return true;
7682
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
7683
- try {
7684
- if (Buffer.byteLength(JSON.stringify(value), "utf8") > MAX_RESUME_CONTRACT_BYTES) return false;
7685
- validateModelResponseAliases(value?.modelResponseAliases);
7686
- } catch {
7687
- return false;
7688
- }
7689
- const contract = value;
7690
- if (Object.keys(contract).some((key) => !["modelResponseAliases", "outputSchema", "agentContract", "acceptance", "output", "outputMode"].includes(key))) return false;
7691
- if (contract.outputSchema !== void 0 && contract.outputSchema !== false && (!contract.outputSchema || typeof contract.outputSchema !== "object" || Array.isArray(contract.outputSchema))) return false;
7692
- if (contract.agentContract !== void 0 && (!contract.agentContract || typeof contract.agentContract !== "object" || Array.isArray(contract.agentContract) || contract.agentContract.version !== 1)) return false;
7693
- if (validateAcceptanceInput(contract.acceptance).length > 0) return false;
7694
- if (contract.output !== void 0 && typeof contract.output !== "string" && typeof contract.output !== "boolean") return false;
7695
- return contract.outputMode === void 0 || contract.outputMode === "inline" || contract.outputMode === "file-only";
7696
- }
7697
- function compactRun(run) {
7698
- if (!run.sessionId) return void 0;
7699
- if (run.children.length === 0 || !run.children.every((child) => isRestorableForegroundStatus(child.status) && isRestorableResumeContract(child.resumeContract))) return void 0;
7700
- return {
7701
- runId: run.runId,
7702
- mode: run.mode,
7703
- cwd: run.cwd,
7704
- sessionId: run.sessionId,
7705
- updatedAt: run.updatedAt,
7706
- children: run.children.map(compactChild)
7707
- };
7708
- }
7709
- function readIndex(resultsDir) {
7710
- const filePath = historyPath(resultsDir);
7711
- if (!fs14.existsSync(filePath)) return { version: HISTORY_VERSION, runs: [] };
7712
- try {
7713
- const parsed = JSON.parse(fs14.readFileSync(filePath, "utf-8"));
7714
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return { version: HISTORY_VERSION, runs: [] };
7715
- const record = parsed;
7716
- if (record.version !== HISTORY_VERSION || !Array.isArray(record.runs)) return { version: HISTORY_VERSION, runs: [] };
7717
- return { version: HISTORY_VERSION, runs: record.runs.filter(isRestorableRun) };
7718
- } catch {
7719
- return { version: HISTORY_VERSION, runs: [] };
7720
- }
7721
- }
7722
- function isRestorableRun(value) {
7723
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
7724
- const run = value;
7725
- return typeof run.runId === "string" && Boolean(run.runId) && (run.mode === "single" || run.mode === "parallel" || run.mode === "chain") && typeof run.cwd === "string" && Boolean(run.cwd) && typeof run.sessionId === "string" && Boolean(run.sessionId) && typeof run.updatedAt === "number" && Number.isFinite(run.updatedAt) && Array.isArray(run.children) && run.children.length > 0 && run.children.every((child) => Boolean(child && typeof child === "object" && !Array.isArray(child) && typeof child.agent === "string" && typeof child.index === "number" && isRestorableResumeContract(child.resumeContract) && isRestorableForegroundStatus(child.status)));
7726
- }
7727
- function sortAndBound(runs, limit) {
7728
- return [...runs].sort((left, right) => right.updatedAt - left.updatedAt).slice(0, limit);
7729
- }
7730
- function persistForegroundRunHistory(state, options = {}) {
7731
- const resultsDir = options.resultsDir ?? DIRS.results;
7732
- const limit = options.limit ?? MAX_REMEMBERED_FOREGROUND_RUNS;
7733
- const existing = readIndex(resultsDir);
7734
- const merged = new Map(existing.runs.map((run) => [run.runId, run]));
7735
- for (const run of state.foregroundRuns?.values() ?? []) {
7736
- const compact = compactRun(run);
7737
- if (compact) merged.set(compact.runId, compact);
7738
- }
7739
- const runs = sortAndBound([...merged.values()], limit);
7740
- writePrivateAtomicJson(historyPath(resultsDir), { version: HISTORY_VERSION, runs });
7741
- }
7742
- function restoreForegroundRunHistory(state, options = {}) {
7743
- const sessionId = options.sessionId ?? state.currentSessionId;
7744
- if (!sessionId) return 0;
7745
- const index = readIndex(options.resultsDir ?? DIRS.results);
7746
- const runs = sortAndBound(index.runs.filter((run) => run.sessionId === sessionId), options.limit ?? MAX_REMEMBERED_FOREGROUND_RUNS);
7747
- state.foregroundRuns ??= /* @__PURE__ */ new Map();
7748
- let restored = 0;
7749
- for (const run of runs) {
7750
- if (state.foregroundRuns.has(run.runId)) continue;
7751
- state.foregroundRuns.set(run.runId, run);
7752
- restored += 1;
7753
- }
7754
- return restored;
7755
- }
7756
-
7757
7972
  // src/runs/background/retained-children.ts
7758
- import * as fs15 from "node:fs";
7973
+ import * as fs16 from "node:fs";
7759
7974
  var MAX_RETAINED_CHILDREN = 10;
7760
7975
  var MAX_RETAINED_CHILD_CANDIDATES = 100;
7761
7976
  var MAX_TASK_SUMMARY_LENGTH = 120;
@@ -7769,7 +7984,7 @@ function retainedSessionFile(sessionFile) {
7769
7984
  if (!sessionFile) return { state: "not-resumable", reason: "no persisted session file" };
7770
7985
  if (!sessionFile.endsWith(".jsonl")) return { state: "not-resumable", reason: "persisted session file is not a .jsonl file" };
7771
7986
  try {
7772
- const stat = fs15.lstatSync(sessionFile);
7987
+ const stat = fs16.lstatSync(sessionFile);
7773
7988
  if (!stat.isFile() || stat.isSymbolicLink()) return { state: "not-resumable", reason: "persisted session file is not a regular file" };
7774
7989
  return { state: "resumable", sessionPath: sessionFile };
7775
7990
  } catch (error2) {
@@ -7796,7 +8011,7 @@ function childResumability(run, step) {
7796
8011
  let requiredCwd;
7797
8012
  try {
7798
8013
  requiredCwd = resolveRetainedWorktreeCwd(parallelHandoffPath(run.asyncDir), run.id, step.index) ?? recoveryDescriptor.cwd ?? run.cwd;
7799
- if (!requiredCwd || !fs15.statSync(requiredCwd).isDirectory()) return { state: "not-resumable", reason: `required cwd is missing: ${requiredCwd ?? "unknown"}` };
8014
+ if (!requiredCwd || !fs16.statSync(requiredCwd).isDirectory()) return { state: "not-resumable", reason: `required cwd is missing: ${requiredCwd ?? "unknown"}` };
7800
8015
  } catch (error2) {
7801
8016
  return { state: "not-resumable", reason: `resume dependency unavailable: ${error2 instanceof Error ? error2.message : String(error2)}` };
7802
8017
  }
@@ -7857,52 +8072,52 @@ function formatRetainedChildren(children) {
7857
8072
  }
7858
8073
 
7859
8074
  // src/runs/shared/external-job-bridge.ts
7860
- import { randomUUID as randomUUID5 } from "node:crypto";
7861
- import * as fs16 from "node:fs";
8075
+ import { randomUUID as randomUUID7 } from "node:crypto";
8076
+ import * as fs17 from "node:fs";
7862
8077
  import * as os2 from "node:os";
7863
- import * as path14 from "node:path";
8078
+ import * as path16 from "node:path";
7864
8079
  var EXTERNAL_JOB_BRIDGE_REQUEST_DIR = "external-job-requests";
7865
8080
  var EXTERNAL_JOB_BRIDGE_RESPONSE_DIR = "external-job-responses";
7866
8081
  var CHILD_BRIDGE_SWEEP_INTERVAL_MS = 1e3;
7867
8082
  var MAX_REQUESTS_PER_SWEEP = 100;
7868
8083
  var inFlight = /* @__PURE__ */ new Set();
7869
8084
  function requestDir(asyncDir) {
7870
- return path14.join(asyncDir, EXTERNAL_JOB_BRIDGE_REQUEST_DIR);
8085
+ return path16.join(asyncDir, EXTERNAL_JOB_BRIDGE_REQUEST_DIR);
7871
8086
  }
7872
8087
  function responseDir(asyncDir) {
7873
- return path14.join(asyncDir, EXTERNAL_JOB_BRIDGE_RESPONSE_DIR);
8088
+ return path16.join(asyncDir, EXTERNAL_JOB_BRIDGE_RESPONSE_DIR);
7874
8089
  }
7875
8090
  function requestPath(asyncDir, id) {
7876
- return path14.join(requestDir(asyncDir), `${id}.json`);
8091
+ return path16.join(requestDir(asyncDir), `${id}.json`);
7877
8092
  }
7878
8093
  function isDispatchOperation(operation) {
7879
8094
  return operation === "start" || operation === "follow-up";
7880
8095
  }
7881
8096
  function dispatchClaimDir(asyncDir, id) {
7882
- return path14.join(requestDir(asyncDir), `${id}.claim`);
8097
+ return path16.join(requestDir(asyncDir), `${id}.claim`);
7883
8098
  }
7884
8099
  function dispatchClaimTempDir(asyncDir, id) {
7885
- return path14.join(requestDir(asyncDir), `${id}.claim.tmp-${randomUUID5()}`);
8100
+ return path16.join(requestDir(asyncDir), `${id}.claim.tmp-${randomUUID7()}`);
7886
8101
  }
7887
8102
  function dispatchClaimCompletedPath(claimDir) {
7888
- return path14.join(claimDir, "completed.json");
8103
+ return path16.join(claimDir, "completed.json");
7889
8104
  }
7890
8105
  function dispatchClaimHandlePath(claimDir) {
7891
- return path14.join(claimDir, "handle.json");
8106
+ return path16.join(claimDir, "handle.json");
7892
8107
  }
7893
8108
  function completedDispatchClaimExists(asyncDir, id) {
7894
- return fs16.existsSync(dispatchClaimCompletedPath(dispatchClaimDir(asyncDir, id)));
8109
+ return fs17.existsSync(dispatchClaimCompletedPath(dispatchClaimDir(asyncDir, id)));
7895
8110
  }
7896
8111
  function responsePath(asyncDir, id) {
7897
- return path14.join(responseDir(asyncDir), `${id}.json`);
8112
+ return path16.join(responseDir(asyncDir), `${id}.json`);
7898
8113
  }
7899
8114
  function readJson2(filePath) {
7900
- return JSON.parse(fs16.readFileSync(filePath, "utf-8"));
8115
+ return JSON.parse(fs17.readFileSync(filePath, "utf-8"));
7901
8116
  }
7902
8117
  function processStartIdentity(pid) {
7903
8118
  if (process.platform === "linux") {
7904
8119
  try {
7905
- const stat = fs16.readFileSync(`/proc/${pid}/stat`, "utf-8");
8120
+ const stat = fs17.readFileSync(`/proc/${pid}/stat`, "utf-8");
7906
8121
  const commandEnd = stat.lastIndexOf(")");
7907
8122
  if (commandEnd === -1) return void 0;
7908
8123
  const fields = stat.slice(commandEnd + 1).trim().split(/\s+/);
@@ -8022,16 +8237,16 @@ function claimDispatchRequest(asyncDir, filePath, request) {
8022
8237
  const claimDir = dispatchClaimDir(asyncDir, request.id);
8023
8238
  const tempClaimDir = dispatchClaimTempDir(asyncDir, request.id);
8024
8239
  try {
8025
- fs16.mkdirSync(tempClaimDir);
8026
- writeAtomicJson(path14.join(tempClaimDir, "owner.json"), owner);
8027
- writeAtomicJson(path14.join(tempClaimDir, "request.json"), claimed);
8028
- fs16.renameSync(tempClaimDir, claimDir);
8240
+ fs17.mkdirSync(tempClaimDir);
8241
+ writeAtomicJson(path16.join(tempClaimDir, "owner.json"), owner);
8242
+ writeAtomicJson(path16.join(tempClaimDir, "request.json"), claimed);
8243
+ fs17.renameSync(tempClaimDir, claimDir);
8029
8244
  } catch (error2) {
8030
- fs16.rmSync(tempClaimDir, { recursive: true, force: true });
8245
+ fs17.rmSync(tempClaimDir, { recursive: true, force: true });
8031
8246
  if (isClaimConflictError(error2)) return void 0;
8032
8247
  throw error2;
8033
8248
  }
8034
- fs16.rmSync(filePath, { force: true });
8249
+ fs17.rmSync(filePath, { force: true });
8035
8250
  return { request: claimed, filePath: claimDir };
8036
8251
  }
8037
8252
  function parseClaimOwner(value) {
@@ -8051,7 +8266,7 @@ function claimOwnerIsDead(owner) {
8051
8266
  }
8052
8267
  function readClaimOwner(claimDir) {
8053
8268
  try {
8054
- return parseClaimOwner(readJson2(path14.join(claimDir, "owner.json")));
8269
+ return parseClaimOwner(readJson2(path16.join(claimDir, "owner.json")));
8055
8270
  } catch {
8056
8271
  return void 0;
8057
8272
  }
@@ -8113,23 +8328,23 @@ function createChildExternalJobBridgeSweeper() {
8113
8328
  function serviceExternalJobBridgeRequests(asyncDir) {
8114
8329
  let files;
8115
8330
  try {
8116
- files = fs16.readdirSync(requestDir(asyncDir), { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json") && !completedDispatchClaimExists(asyncDir, entry.name.replace(/\.json$/, "")) || entry.isDirectory() && entry.name.endsWith(".claim") && !fs16.existsSync(dispatchClaimCompletedPath(path14.join(requestDir(asyncDir), entry.name)))).map((entry) => entry.name).slice(0, MAX_REQUESTS_PER_SWEEP);
8331
+ files = fs17.readdirSync(requestDir(asyncDir), { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json") && !completedDispatchClaimExists(asyncDir, entry.name.replace(/\.json$/, "")) || entry.isDirectory() && entry.name.endsWith(".claim") && !fs17.existsSync(dispatchClaimCompletedPath(path16.join(requestDir(asyncDir), entry.name)))).map((entry) => entry.name).slice(0, MAX_REQUESTS_PER_SWEEP);
8117
8332
  } catch (error2) {
8118
8333
  if (error2.code === "ENOENT") return;
8119
8334
  throw error2;
8120
8335
  }
8121
- fs16.mkdirSync(responseDir(asyncDir), { recursive: true });
8336
+ fs17.mkdirSync(responseDir(asyncDir), { recursive: true });
8122
8337
  for (const file of files) {
8123
8338
  serviceExternalJobBridgeRequestFile(asyncDir, file);
8124
8339
  }
8125
8340
  }
8126
8341
  function serviceExternalJobBridgeRequestFile(asyncDir, file) {
8127
- fs16.mkdirSync(responseDir(asyncDir), { recursive: true });
8342
+ fs17.mkdirSync(responseDir(asyncDir), { recursive: true });
8128
8343
  if (file.endsWith(".claim")) {
8129
8344
  serviceExternalJobDispatchClaim(asyncDir, file);
8130
8345
  return;
8131
8346
  }
8132
- const filePath = path14.join(requestDir(asyncDir), file);
8347
+ const filePath = path16.join(requestDir(asyncDir), file);
8133
8348
  let request;
8134
8349
  try {
8135
8350
  request = assertRequest(readJson2(filePath), filePath);
@@ -8145,14 +8360,14 @@ function serviceExternalJobBridgeRequestFile(asyncDir, file) {
8145
8360
  message: error2 instanceof Error ? error2.message : String(error2),
8146
8361
  completedAt: Date.now()
8147
8362
  });
8148
- fs16.rmSync(filePath, { force: true });
8363
+ fs17.rmSync(filePath, { force: true });
8149
8364
  return;
8150
8365
  }
8151
8366
  if (isDispatchOperation(request.operation) && completedDispatchClaimExists(asyncDir, request.id)) {
8152
- fs16.rmSync(filePath, { force: true });
8367
+ fs17.rmSync(filePath, { force: true });
8153
8368
  return;
8154
8369
  }
8155
- if (inFlight.has(request.id) || fs16.existsSync(responsePath(asyncDir, request.id))) return;
8370
+ if (inFlight.has(request.id) || fs17.existsSync(responsePath(asyncDir, request.id))) return;
8156
8371
  if (isDispatchOperation(request.operation) && request.claimedAt !== void 0) return;
8157
8372
  const claimed = isDispatchOperation(request.operation) ? claimDispatchRequest(asyncDir, filePath, request) : { request, filePath };
8158
8373
  if (!claimed) return;
@@ -8163,7 +8378,7 @@ function serviceExternalJobBridgeRequestFile(asyncDir, file) {
8163
8378
  if (isDispatchOperation(claimedRequest.operation)) {
8164
8379
  writeAtomicJson(dispatchClaimCompletedPath(claimed.filePath), { completedAt: Date.now() });
8165
8380
  } else {
8166
- fs16.rmSync(claimed.filePath, { recursive: true, force: true });
8381
+ fs17.rmSync(claimed.filePath, { recursive: true, force: true });
8167
8382
  }
8168
8383
  }).catch((error2) => {
8169
8384
  writeAtomicJson(responsePath(asyncDir, claimedRequest.id), {
@@ -8181,21 +8396,21 @@ function serviceExternalJobBridgeRequestFile(asyncDir, file) {
8181
8396
  });
8182
8397
  }
8183
8398
  function serviceExternalJobDispatchClaim(asyncDir, file) {
8184
- const claimDir = path14.join(requestDir(asyncDir), file);
8185
- if (fs16.existsSync(dispatchClaimCompletedPath(claimDir))) return;
8399
+ const claimDir = path16.join(requestDir(asyncDir), file);
8400
+ if (fs17.existsSync(dispatchClaimCompletedPath(claimDir))) return;
8186
8401
  const owner = readClaimOwner(claimDir);
8187
- if (!owner && fs16.existsSync(requestPath(asyncDir, file.replace(/\.claim$/, ""))) && !fs16.existsSync(responsePath(asyncDir, file.replace(/\.claim$/, "")))) {
8188
- fs16.rmSync(claimDir, { recursive: true, force: true });
8402
+ if (!owner && fs17.existsSync(requestPath(asyncDir, file.replace(/\.claim$/, ""))) && !fs17.existsSync(responsePath(asyncDir, file.replace(/\.claim$/, "")))) {
8403
+ fs17.rmSync(claimDir, { recursive: true, force: true });
8189
8404
  return;
8190
8405
  }
8191
8406
  if (!claimOwnerIsDead(owner)) return;
8192
8407
  let request;
8193
8408
  try {
8194
- request = assertRequest(readJson2(path14.join(claimDir, "request.json")), path14.join(claimDir, "request.json"));
8409
+ request = assertRequest(readJson2(path16.join(claimDir, "request.json")), path16.join(claimDir, "request.json"));
8195
8410
  } catch {
8196
8411
  return;
8197
8412
  }
8198
- if (!isDispatchOperation(request.operation) || fs16.existsSync(responsePath(asyncDir, request.id))) return;
8413
+ if (!isDispatchOperation(request.operation) || fs17.existsSync(responsePath(asyncDir, request.id))) return;
8199
8414
  const handle = readClaimHandle(request.provider, claimDir);
8200
8415
  if (handle) {
8201
8416
  writeAtomicJson(responsePath(asyncDir, request.id), {
@@ -8207,7 +8422,7 @@ function serviceExternalJobDispatchClaim(asyncDir, file) {
8207
8422
  completedAt: Date.now()
8208
8423
  });
8209
8424
  writeAtomicJson(dispatchClaimCompletedPath(claimDir), { completedAt: Date.now() });
8210
- fs16.rmSync(requestPath(asyncDir, request.id), { force: true });
8425
+ fs17.rmSync(requestPath(asyncDir, request.id), { force: true });
8211
8426
  return;
8212
8427
  }
8213
8428
  writeAtomicJson(responsePath(asyncDir, request.id), {
@@ -8219,8 +8434,8 @@ function serviceExternalJobDispatchClaim(asyncDir, file) {
8219
8434
  message: `External-job ${request.operation} for provider '${request.provider}' was claimed by a host process that is no longer alive before a provider job id was committed. Refusing to redispatch the prompt automatically.`,
8220
8435
  completedAt: Date.now()
8221
8436
  });
8222
- fs16.rmSync(claimDir, { recursive: true, force: true });
8223
- fs16.rmSync(requestPath(asyncDir, request.id), { force: true });
8437
+ fs17.rmSync(claimDir, { recursive: true, force: true });
8438
+ fs17.rmSync(requestPath(asyncDir, request.id), { force: true });
8224
8439
  }
8225
8440
 
8226
8441
  // src/shared/provider-diagnostics.ts
@@ -8349,6 +8564,14 @@ export {
8349
8564
  COMPLETION_ACTION,
8350
8565
  STATUS_ACTION,
8351
8566
  createParentWake,
8567
+ writeWorkflowLaunchRecord,
8568
+ resumedRunStarted,
8569
+ workflowResumeClaimState,
8570
+ claimWorkflowResume,
8571
+ releaseWorkflowResumeClaim,
8572
+ recordWorkflowResumeRefusal,
8573
+ refusalSentence,
8574
+ hasWorkflowResumeRefusal,
8352
8575
  incrementalChildCompletionTriggersTurn,
8353
8576
  formatIncrementalChildCompletion,
8354
8577
  parseSubagentNotifyContent,
@@ -8370,19 +8593,21 @@ export {
8370
8593
  childSessionFactory,
8371
8594
  childSessionFactoryModule,
8372
8595
  disposeChildSessions,
8373
- workflowRuntimeReplacedRelaunchNotice,
8596
+ WORKFLOW_RUNTIME_REPLACED_STOP_NOTICE,
8374
8597
  runtimeReplacedAbortReason,
8375
8598
  workflowStopCause,
8376
8599
  workflowScriptDigest,
8377
8600
  workflowChildFingerprint,
8378
8601
  appendWorkflowChildJournal,
8602
+ claimedWorkflowRunRefusal,
8379
8603
  findWorkflowReuseSource,
8380
8604
  readNamedWorkflowReuseSource,
8381
8605
  matchWorkflowReuse,
8606
+ workflowResumeRefusal,
8607
+ manualWorkflowResumeRefusalText,
8608
+ workflowResumeTargetId,
8609
+ prepareWorkflowResume,
8382
8610
  flattenSubagentToolOptions,
8383
- MAX_REMEMBERED_FOREGROUND_RUNS,
8384
- persistForegroundRunHistory,
8385
- restoreForegroundRunHistory,
8386
8611
  listRetainedChildren,
8387
8612
  formatRetainedChildren
8388
8613
  };