pi-subagents 0.81.0 → 0.82.1

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