@caupulican/pi-adaptative 0.86.2 → 0.86.5

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 (226) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/core/agent-paths.d.ts +13 -0
  3. package/dist/core/agent-paths.d.ts.map +1 -1
  4. package/dist/core/agent-paths.js +23 -0
  5. package/dist/core/agent-paths.js.map +1 -1
  6. package/dist/core/autonomy/bounded-completion.d.ts +7 -0
  7. package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
  8. package/dist/core/autonomy/bounded-completion.js +12 -4
  9. package/dist/core/autonomy/bounded-completion.js.map +1 -1
  10. package/dist/core/autonomy/lane-tool-surface.d.ts +2 -0
  11. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
  12. package/dist/core/autonomy/lane-tool-surface.js +6 -3
  13. package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
  14. package/dist/core/autonomy/subagent-prompt.js +1 -1
  15. package/dist/core/autonomy/subagent-prompt.js.map +1 -1
  16. package/dist/core/background-lane-controller.d.ts +25 -2
  17. package/dist/core/background-lane-controller.d.ts.map +1 -1
  18. package/dist/core/background-lane-controller.js +67 -0
  19. package/dist/core/background-lane-controller.js.map +1 -1
  20. package/dist/core/delegation/replay-safe-mailbox-bounds.d.ts +4 -0
  21. package/dist/core/delegation/replay-safe-mailbox-bounds.d.ts.map +1 -0
  22. package/dist/core/delegation/replay-safe-mailbox-bounds.js +20 -0
  23. package/dist/core/delegation/replay-safe-mailbox-bounds.js.map +1 -0
  24. package/dist/core/delegation/sanitized-context-fork.d.ts +25 -0
  25. package/dist/core/delegation/sanitized-context-fork.d.ts.map +1 -0
  26. package/dist/core/delegation/sanitized-context-fork.js +299 -0
  27. package/dist/core/delegation/sanitized-context-fork.js.map +1 -0
  28. package/dist/core/delegation/session-root-mailbox.d.ts +75 -0
  29. package/dist/core/delegation/session-root-mailbox.d.ts.map +1 -0
  30. package/dist/core/delegation/session-root-mailbox.js +627 -0
  31. package/dist/core/delegation/session-root-mailbox.js.map +1 -0
  32. package/dist/core/delegation/worker-agent-control-coordinator.d.ts +102 -9
  33. package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -1
  34. package/dist/core/delegation/worker-agent-control-coordinator.js +1307 -125
  35. package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -1
  36. package/dist/core/delegation/worker-agent-control.d.ts +183 -8
  37. package/dist/core/delegation/worker-agent-control.d.ts.map +1 -1
  38. package/dist/core/delegation/worker-agent-control.js +778 -48
  39. package/dist/core/delegation/worker-agent-control.js.map +1 -1
  40. package/dist/core/delegation/worker-attempt-executor.d.ts +9 -0
  41. package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -1
  42. package/dist/core/delegation/worker-attempt-executor.js +479 -214
  43. package/dist/core/delegation/worker-attempt-executor.js.map +1 -1
  44. package/dist/core/delegation/worker-authority-resolver.d.ts +9 -0
  45. package/dist/core/delegation/worker-authority-resolver.d.ts.map +1 -1
  46. package/dist/core/delegation/worker-authority-resolver.js +22 -14
  47. package/dist/core/delegation/worker-authority-resolver.js.map +1 -1
  48. package/dist/core/delegation/worker-context-fork-store.d.ts +71 -0
  49. package/dist/core/delegation/worker-context-fork-store.d.ts.map +1 -0
  50. package/dist/core/delegation/worker-context-fork-store.js +437 -0
  51. package/dist/core/delegation/worker-context-fork-store.js.map +1 -0
  52. package/dist/core/delegation/worker-context-inheritance-policy.d.ts +18 -0
  53. package/dist/core/delegation/worker-context-inheritance-policy.d.ts.map +1 -0
  54. package/dist/core/delegation/worker-context-inheritance-policy.js +28 -0
  55. package/dist/core/delegation/worker-context-inheritance-policy.js.map +1 -0
  56. package/dist/core/delegation/worker-conversation-revision.d.ts +31 -0
  57. package/dist/core/delegation/worker-conversation-revision.d.ts.map +1 -0
  58. package/dist/core/delegation/worker-conversation-revision.js +203 -0
  59. package/dist/core/delegation/worker-conversation-revision.js.map +1 -0
  60. package/dist/core/delegation/worker-conversation-store.d.ts +130 -18
  61. package/dist/core/delegation/worker-conversation-store.d.ts.map +1 -1
  62. package/dist/core/delegation/worker-conversation-store.js +1263 -79
  63. package/dist/core/delegation/worker-conversation-store.js.map +1 -1
  64. package/dist/core/delegation/worker-delegation-controller.d.ts +29 -2
  65. package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
  66. package/dist/core/delegation/worker-delegation-controller.js +625 -111
  67. package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
  68. package/dist/core/delegation/worker-delegation-request.d.ts +2 -0
  69. package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -1
  70. package/dist/core/delegation/worker-delegation-request.js.map +1 -1
  71. package/dist/core/delegation/worker-dispatch-scheduler.d.ts +20 -1
  72. package/dist/core/delegation/worker-dispatch-scheduler.d.ts.map +1 -1
  73. package/dist/core/delegation/worker-dispatch-scheduler.js +210 -37
  74. package/dist/core/delegation/worker-dispatch-scheduler.js.map +1 -1
  75. package/dist/core/delegation/worker-fleet-limits.d.ts +38 -0
  76. package/dist/core/delegation/worker-fleet-limits.d.ts.map +1 -0
  77. package/dist/core/delegation/worker-fleet-limits.js +113 -0
  78. package/dist/core/delegation/worker-fleet-limits.js.map +1 -0
  79. package/dist/core/delegation/worker-lane-projection.d.ts +1 -0
  80. package/dist/core/delegation/worker-lane-projection.d.ts.map +1 -1
  81. package/dist/core/delegation/worker-lane-projection.js +6 -2
  82. package/dist/core/delegation/worker-lane-projection.js.map +1 -1
  83. package/dist/core/delegation/worker-lifecycle.d.ts +20 -3
  84. package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -1
  85. package/dist/core/delegation/worker-lifecycle.js +42 -18
  86. package/dist/core/delegation/worker-lifecycle.js.map +1 -1
  87. package/dist/core/delegation/worker-notification-coordinator.d.ts +4 -0
  88. package/dist/core/delegation/worker-notification-coordinator.d.ts.map +1 -1
  89. package/dist/core/delegation/worker-notification-coordinator.js +45 -4
  90. package/dist/core/delegation/worker-notification-coordinator.js.map +1 -1
  91. package/dist/core/delegation/worker-provider-turn-protocol.d.ts +50 -0
  92. package/dist/core/delegation/worker-provider-turn-protocol.d.ts.map +1 -0
  93. package/dist/core/delegation/worker-provider-turn-protocol.js +185 -0
  94. package/dist/core/delegation/worker-provider-turn-protocol.js.map +1 -0
  95. package/dist/core/delegation/worker-recovery-coordinator.d.ts +45 -4
  96. package/dist/core/delegation/worker-recovery-coordinator.d.ts.map +1 -1
  97. package/dist/core/delegation/worker-recovery-coordinator.js +223 -17
  98. package/dist/core/delegation/worker-recovery-coordinator.js.map +1 -1
  99. package/dist/core/delegation/worker-retry-policy.d.ts +36 -0
  100. package/dist/core/delegation/worker-retry-policy.d.ts.map +1 -0
  101. package/dist/core/delegation/worker-retry-policy.js +41 -0
  102. package/dist/core/delegation/worker-retry-policy.js.map +1 -0
  103. package/dist/core/delegation/worker-runner.d.ts +3 -1
  104. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  105. package/dist/core/delegation/worker-runner.js +4 -2
  106. package/dist/core/delegation/worker-runner.js.map +1 -1
  107. package/dist/core/delegation/worker-task-view.d.ts +43 -0
  108. package/dist/core/delegation/worker-task-view.d.ts.map +1 -0
  109. package/dist/core/delegation/worker-task-view.js +194 -0
  110. package/dist/core/delegation/worker-task-view.js.map +1 -0
  111. package/dist/core/delegation/worker-terminal-handoff-coordinator.d.ts +55 -0
  112. package/dist/core/delegation/worker-terminal-handoff-coordinator.d.ts.map +1 -0
  113. package/dist/core/delegation/worker-terminal-handoff-coordinator.js +179 -0
  114. package/dist/core/delegation/worker-terminal-handoff-coordinator.js.map +1 -0
  115. package/dist/core/delegation/worker-tree-budget-coordinator.d.ts +2 -2
  116. package/dist/core/delegation/worker-tree-budget-coordinator.d.ts.map +1 -1
  117. package/dist/core/delegation/worker-tree-budget-coordinator.js +12 -46
  118. package/dist/core/delegation/worker-tree-budget-coordinator.js.map +1 -1
  119. package/dist/core/keybindings.d.ts +10 -0
  120. package/dist/core/keybindings.d.ts.map +1 -1
  121. package/dist/core/keybindings.js +2 -0
  122. package/dist/core/keybindings.js.map +1 -1
  123. package/dist/core/orchestration/attempt-ordering.d.ts +9 -0
  124. package/dist/core/orchestration/attempt-ordering.d.ts.map +1 -0
  125. package/dist/core/orchestration/attempt-ordering.js +29 -0
  126. package/dist/core/orchestration/attempt-ordering.js.map +1 -0
  127. package/dist/core/orchestration/capability-gateway.d.ts +19 -0
  128. package/dist/core/orchestration/capability-gateway.d.ts.map +1 -1
  129. package/dist/core/orchestration/capability-gateway.js +19 -2
  130. package/dist/core/orchestration/capability-gateway.js.map +1 -1
  131. package/dist/core/orchestration/contracts.d.ts +43 -3
  132. package/dist/core/orchestration/contracts.d.ts.map +1 -1
  133. package/dist/core/orchestration/contracts.js +24 -0
  134. package/dist/core/orchestration/contracts.js.map +1 -1
  135. package/dist/core/orchestration/delegation-ledger.d.ts +8 -3
  136. package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -1
  137. package/dist/core/orchestration/delegation-ledger.js +148 -55
  138. package/dist/core/orchestration/delegation-ledger.js.map +1 -1
  139. package/dist/core/orchestration/event-store.d.ts +6 -3
  140. package/dist/core/orchestration/event-store.d.ts.map +1 -1
  141. package/dist/core/orchestration/event-store.js +228 -30
  142. package/dist/core/orchestration/event-store.js.map +1 -1
  143. package/dist/core/orchestration/task-runtime-codecs.d.ts +31 -0
  144. package/dist/core/orchestration/task-runtime-codecs.d.ts.map +1 -0
  145. package/dist/core/orchestration/task-runtime-codecs.js +1471 -0
  146. package/dist/core/orchestration/task-runtime-codecs.js.map +1 -0
  147. package/dist/core/orchestration/task-runtime-projection.d.ts +30 -0
  148. package/dist/core/orchestration/task-runtime-projection.d.ts.map +1 -0
  149. package/dist/core/orchestration/task-runtime-projection.js +340 -0
  150. package/dist/core/orchestration/task-runtime-projection.js.map +1 -0
  151. package/dist/core/orchestration/task-runtime-reducer.d.ts +38 -0
  152. package/dist/core/orchestration/task-runtime-reducer.d.ts.map +1 -0
  153. package/dist/core/orchestration/task-runtime-reducer.js +1270 -0
  154. package/dist/core/orchestration/task-runtime-reducer.js.map +1 -0
  155. package/dist/core/orchestration/task-runtime-state.d.ts +142 -0
  156. package/dist/core/orchestration/task-runtime-state.d.ts.map +1 -0
  157. package/dist/core/orchestration/task-runtime-state.js +14 -0
  158. package/dist/core/orchestration/task-runtime-state.js.map +1 -0
  159. package/dist/core/orchestration/task-runtime.d.ts +44 -98
  160. package/dist/core/orchestration/task-runtime.d.ts.map +1 -1
  161. package/dist/core/orchestration/task-runtime.js +357 -1113
  162. package/dist/core/orchestration/task-runtime.js.map +1 -1
  163. package/dist/core/orchestration/worker-context-fork-reference.d.ts +20 -0
  164. package/dist/core/orchestration/worker-context-fork-reference.d.ts.map +1 -0
  165. package/dist/core/orchestration/worker-context-fork-reference.js +53 -0
  166. package/dist/core/orchestration/worker-context-fork-reference.js.map +1 -0
  167. package/dist/core/research/research-runner.d.ts +2 -0
  168. package/dist/core/research/research-runner.d.ts.map +1 -1
  169. package/dist/core/research/research-runner.js +1 -0
  170. package/dist/core/research/research-runner.js.map +1 -1
  171. package/dist/core/runtime-builder.d.ts.map +1 -1
  172. package/dist/core/runtime-builder.js +8 -0
  173. package/dist/core/runtime-builder.js.map +1 -1
  174. package/dist/core/secrets/credential-project.d.ts.map +1 -1
  175. package/dist/core/secrets/credential-project.js +24 -2
  176. package/dist/core/secrets/credential-project.js.map +1 -1
  177. package/dist/core/security/secret-text.d.ts +3 -0
  178. package/dist/core/security/secret-text.d.ts.map +1 -1
  179. package/dist/core/security/secret-text.js +15 -0
  180. package/dist/core/security/secret-text.js.map +1 -1
  181. package/dist/core/settings-manager.d.ts +3 -0
  182. package/dist/core/settings-manager.d.ts.map +1 -1
  183. package/dist/core/settings-manager.js +160 -8
  184. package/dist/core/settings-manager.js.map +1 -1
  185. package/dist/core/tools/delegate.d.ts +31 -4
  186. package/dist/core/tools/delegate.d.ts.map +1 -1
  187. package/dist/core/tools/delegate.js +751 -98
  188. package/dist/core/tools/delegate.js.map +1 -1
  189. package/dist/main.d.ts.map +1 -1
  190. package/dist/main.js +1 -0
  191. package/dist/main.js.map +1 -1
  192. package/dist/modes/interactive/components/activity-lane.d.ts +5 -0
  193. package/dist/modes/interactive/components/activity-lane.d.ts.map +1 -1
  194. package/dist/modes/interactive/components/activity-lane.js +133 -49
  195. package/dist/modes/interactive/components/activity-lane.js.map +1 -1
  196. package/dist/modes/interactive/components/agents-overlay.d.ts +40 -0
  197. package/dist/modes/interactive/components/agents-overlay.d.ts.map +1 -0
  198. package/dist/modes/interactive/components/agents-overlay.js +153 -0
  199. package/dist/modes/interactive/components/agents-overlay.js.map +1 -0
  200. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  201. package/dist/modes/interactive/components/settings-selector.js +18 -13
  202. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  203. package/dist/modes/interactive/interactive-event-controller.d.ts +1 -1
  204. package/dist/modes/interactive/interactive-event-controller.d.ts.map +1 -1
  205. package/dist/modes/interactive/interactive-event-controller.js +6 -3
  206. package/dist/modes/interactive/interactive-event-controller.js.map +1 -1
  207. package/dist/modes/interactive/interactive-mode.d.ts +5 -0
  208. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  209. package/dist/modes/interactive/interactive-mode.js +51 -0
  210. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  211. package/dist/modes/interactive/key-handlers.d.ts +1 -0
  212. package/dist/modes/interactive/key-handlers.d.ts.map +1 -1
  213. package/dist/modes/interactive/key-handlers.js +1 -0
  214. package/dist/modes/interactive/key-handlers.js.map +1 -1
  215. package/docs/harness-architecture.md +61 -17
  216. package/docs/settings.md +17 -9
  217. package/docs/worker-profiles.md +6 -2
  218. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  219. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  220. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  221. package/examples/extensions/sandbox/package-lock.json +2 -2
  222. package/examples/extensions/sandbox/package.json +1 -1
  223. package/examples/extensions/with-deps/package-lock.json +2 -2
  224. package/examples/extensions/with-deps/package.json +1 -1
  225. package/npm-shrinkwrap.json +12 -12
  226. package/package.json +4 -4
@@ -1,25 +1,114 @@
1
1
  import path from "node:path";
2
+ import { isDeepStrictEqual } from "node:util";
2
3
  import { compact } from "@caupulican/pi-agent-core/compaction/compaction";
4
+ import { classifyFailure, computeRetryDelayMs, sleepAbortable, } from "@caupulican/pi-agent-core/reliability";
3
5
  import { sanitizeToolFailureContext } from "@caupulican/pi-agent-core/tool-failure-memory";
4
6
  import { addUsage, createEmptyUsage } from "@caupulican/pi-agent-core/usage";
5
7
  import { safeRealpathSync } from "../autonomy/path-scope.js";
6
8
  import { composeSubagentSystemPrompt } from "../autonomy/subagent-prompt.js";
7
9
  import { attemptUsageFromGatewayUsage, EMPTY_ATTEMPT_USAGE } from "../orchestration/attempt-usage.js";
10
+ import { CapabilityGatewayDeniedError } from "../orchestration/capability-gateway.js";
8
11
  import { WorkerActionJournal } from "./worker-action-journal.js";
12
+ import { WorkerConversationOwnershipError } from "./worker-conversation-revision.js";
13
+ import { WorkerCompletionProtocolError, WorkerProviderTurnProtocol } from "./worker-provider-turn-protocol.js";
9
14
  import { runWorker } from "./worker-runner.js";
10
- function recordAssistantUsage(surface, message) {
11
- surface.gateway?.recordUsage({
12
- inputTokens: message.usage.input,
13
- outputTokens: message.usage.output,
14
- cacheReadTokens: message.usage.cacheRead,
15
- cacheWriteTokens: message.usage.cacheWrite,
16
- totalTokens: message.usage.totalTokens,
17
- costUsd: message.usage.cost.total,
18
- });
15
+ import { WorkerTreeBudgetExceededError } from "./worker-tree-budget-coordinator.js";
16
+ /**
17
+ * Backoff policy for transient worker provider failures. Without it, a dropped provider socket
18
+ * kills the attempt instantly at $0 spend and an immediate re-dispatch hits the same dead
19
+ * connection (field session 019fd4dc: paired $0 `completion_error` lanes ~10s apart). Jitter
20
+ * de-synchronizes sibling workers that all lost the same connection at once.
21
+ */
22
+ const WORKER_PROVIDER_RETRY_POLICY = {
23
+ maxAttempts: 3,
24
+ baseDelayMs: 2_000,
25
+ maxDelayMs: 30_000,
26
+ jitterRatio: 0.2,
27
+ };
28
+ export async function runProviderCompletionWithBackoff(args) {
29
+ for (let attempt = 1;; attempt++) {
30
+ try {
31
+ return await args.attempt();
32
+ }
33
+ catch (error) {
34
+ args.onAttemptFailure();
35
+ if (args.signal?.aborted)
36
+ throw error;
37
+ if (error instanceof WorkerCompletionProtocolError ||
38
+ error instanceof WorkerConversationOwnershipError ||
39
+ error instanceof CapabilityGatewayDeniedError ||
40
+ error instanceof WorkerTreeBudgetExceededError) {
41
+ throw error;
42
+ }
43
+ const classified = classifyFailure({
44
+ message: error instanceof Error ? error.message : String(error),
45
+ provider: args.provider,
46
+ });
47
+ if (!classified.retryable || attempt >= WORKER_PROVIDER_RETRY_POLICY.maxAttempts)
48
+ throw error;
49
+ const delayMs = computeRetryDelayMs(WORKER_PROVIDER_RETRY_POLICY, attempt, {
50
+ ...(classified.retryAfterMs !== undefined ? { retryAfterMs: classified.retryAfterMs } : {}),
51
+ });
52
+ args.warn(`Worker ${args.laneId} provider request failed (${classified.reason}); retrying in ${Math.ceil(delayMs / 1000)}s (attempt ${attempt + 1}/${WORKER_PROVIDER_RETRY_POLICY.maxAttempts}).`);
53
+ await sleepAbortable(delayMs, args.signal);
54
+ }
55
+ }
19
56
  }
20
57
  function isToolRequest(message) {
21
58
  return message.role === "assistant" && message.content.some((content) => content.type === "toolCall");
22
59
  }
60
+ function workerCompletionCallbackFailure(error) {
61
+ if (error instanceof WorkerCompletionProtocolError ||
62
+ error instanceof WorkerConversationOwnershipError ||
63
+ error instanceof CapabilityGatewayDeniedError ||
64
+ error instanceof WorkerTreeBudgetExceededError) {
65
+ return error;
66
+ }
67
+ return new WorkerCompletionProtocolError("Worker completion callback failed before its authority and durable transcript were verified.");
68
+ }
69
+ function callbackEvidencedCompletion(result, historyLength, emittedMessages) {
70
+ if (result.messages !== undefined && result.messages.length < historyLength) {
71
+ throw new WorkerCompletionProtocolError("Worker completion protocol returned fewer messages than its input history.");
72
+ }
73
+ let suffix;
74
+ if (result.messages !== undefined) {
75
+ suffix = result.messages.slice(historyLength);
76
+ if (!isDeepStrictEqual(suffix, emittedMessages)) {
77
+ throw new WorkerCompletionProtocolError("Worker completion protocol returned a suffix without exact callback evidence.");
78
+ }
79
+ }
80
+ else {
81
+ suffix = [...emittedMessages];
82
+ }
83
+ if (suffix.length === 0) {
84
+ throw new WorkerCompletionProtocolError("Worker completion protocol returned no durable message suffix.");
85
+ }
86
+ if (!suffix.some((message) => message.role === "assistant")) {
87
+ throw new WorkerCompletionProtocolError("Worker completion protocol returned no assistant in its durable suffix.");
88
+ }
89
+ const assistants = emittedMessages.filter((message) => message.role === "assistant");
90
+ const finalMessage = emittedMessages.at(-1);
91
+ if (!finalMessage || finalMessage.role !== "assistant") {
92
+ throw new WorkerCompletionProtocolError("Worker completion protocol did not durably emit an assistant as its terminal message.");
93
+ }
94
+ const usage = createEmptyUsage();
95
+ for (const assistant of assistants)
96
+ addUsage(usage, assistant.usage);
97
+ const text = finalMessage.content.flatMap((content) => (content.type === "text" ? [content.text] : [])).join("");
98
+ if (result.text !== text ||
99
+ result.stopReason !== finalMessage.stopReason ||
100
+ !isDeepStrictEqual(result.usage, usage)) {
101
+ throw new WorkerCompletionProtocolError("Worker completion result disagrees with its durable callback-evidenced terminal assistant.");
102
+ }
103
+ return {
104
+ suffix,
105
+ completion: {
106
+ text,
107
+ usage,
108
+ stopReason: finalMessage.stopReason,
109
+ },
110
+ };
111
+ }
23
112
  /**
24
113
  * Constructs one execution exactly once. The caller registers {@link ledger} before calling
25
114
  * {@link run}, allowing synchronous disposal to observe mutations and cumulative usage while the
@@ -47,7 +136,6 @@ export function createWorkerAttemptExecutor(options) {
47
136
  fencingToken: options.durableHandle.fencingToken,
48
137
  })
49
138
  : undefined;
50
- let providerContextCompacted = options.conversation.hasProviderCompaction();
51
139
  let retentionWarningEmitted = false;
52
140
  let firstMailboxPoll = true;
53
141
  let ran = false;
@@ -69,74 +157,122 @@ export function createWorkerAttemptExecutor(options) {
69
157
  throw signal.reason;
70
158
  return { maxTokens: requestedMaxTokens, release: () => undefined };
71
159
  };
72
- let failedCompactionUsage;
73
- const retentionPolicy = options.retentionPolicy && {
74
- ...options.retentionPolicy,
75
- generateVerifiedCompaction: async (preparation) => {
76
- const compactionUsage = createEmptyUsage();
77
- failedCompactionUsage = compactionUsage;
78
- return compact(preparation, options.model, undefined, undefined, undefined, options.signal, options.thinkingLevel, undefined, undefined, {
79
- completion: async (model, context, requestOptions) => {
80
- if (model.provider !== options.model.provider || model.id !== options.model.id) {
81
- throw new Error("Worker compaction attempted to select a model outside the lane binding.");
82
- }
83
- options.toolSurface.gateway?.assertBudgetAvailable("worker_compaction_provider_completion");
84
- const availableTokens = remainingAttemptTokens();
85
- if (availableTokens !== undefined && availableTokens <= 0) {
86
- throw new Error("Worker token budget exhausted before compaction provider completion.");
87
- }
88
- const requestedMaxTokens = requestOptions.maxTokens;
89
- const configuredMaxTokens = typeof requestedMaxTokens === "number" &&
90
- Number.isSafeInteger(requestedMaxTokens) &&
91
- requestedMaxTokens > 0
92
- ? requestedMaxTokens
93
- : options.laneCapability.laneMaxOutputTokens;
94
- const maxTokens = Math.min(configuredMaxTokens, availableTokens ?? configuredMaxTokens);
95
- if (!Number.isSafeInteger(maxTokens) || maxTokens <= 0) {
96
- throw new Error("Worker compaction provider completion has no valid remaining token budget.");
97
- }
98
- let reservation;
99
- try {
100
- const completion = await options.runIsolatedCompletion({
101
- systemPrompt: context.systemPrompt ?? "",
102
- messages: context.messages,
103
- model: options.model,
104
- thinkingLevel: options.thinkingLevel,
105
- maxTokens,
106
- requestPreflight: async () => {
107
- reservation?.release();
108
- reservation = await reserveProviderBudget(maxTokens, "worker_compaction_provider_completion", requestOptions.signal ?? options.signal);
109
- return { maxTokens: reservation.maxTokens };
160
+ const createRetentionPolicy = (completeSignal) => {
161
+ if (!options.retentionPolicy)
162
+ return undefined;
163
+ let failedCompactionUsage;
164
+ return {
165
+ ...options.retentionPolicy,
166
+ generateVerifiedCompaction: async (preparation) => {
167
+ const compactionUsage = createEmptyUsage();
168
+ failedCompactionUsage = compactionUsage;
169
+ return compact(preparation, options.model, undefined, undefined, undefined, completeSignal, options.thinkingLevel, undefined, undefined, {
170
+ completion: async (model, context, requestOptions) => {
171
+ const requestSignal = requestOptions.signal ?? completeSignal;
172
+ requestSignal.throwIfAborted();
173
+ if (model.provider !== options.model.provider || model.id !== options.model.id) {
174
+ throw new Error("Worker compaction attempted to select a model outside the lane binding.");
175
+ }
176
+ options.toolSurface.gateway?.assertBudgetAvailable("worker_compaction_provider_completion");
177
+ const availableTokens = remainingAttemptTokens();
178
+ if (availableTokens !== undefined && availableTokens <= 0) {
179
+ throw new Error("Worker token budget exhausted before compaction provider completion.");
180
+ }
181
+ const requestedMaxTokens = requestOptions.maxTokens;
182
+ const configuredMaxTokens = typeof requestedMaxTokens === "number" &&
183
+ Number.isSafeInteger(requestedMaxTokens) &&
184
+ requestedMaxTokens > 0
185
+ ? requestedMaxTokens
186
+ : options.laneCapability.laneMaxOutputTokens;
187
+ const maxTokens = Math.min(configuredMaxTokens, availableTokens ?? configuredMaxTokens);
188
+ if (!Number.isSafeInteger(maxTokens) || maxTokens <= 0) {
189
+ throw new Error("Worker compaction provider completion has no valid remaining token budget.");
190
+ }
191
+ let preflightFailed = false;
192
+ let preflightFailure;
193
+ const providerTurn = new WorkerProviderTurnProtocol({
194
+ acquireReservation: () => reserveProviderBudget(maxTokens, "worker_compaction_provider_completion", requestSignal),
195
+ signal: requestSignal,
196
+ onFailure: (error) => {
197
+ if (preflightFailed)
198
+ return;
199
+ preflightFailed = true;
200
+ preflightFailure = error;
110
201
  },
111
- signal: requestOptions.signal ?? options.signal,
112
- cacheRetention: "none",
113
- laneKind: "worker-compaction",
114
- });
115
- addUsage(compactionUsage, completion.usage);
116
- const response = {
117
- role: "assistant",
118
- content: completion.text ? [{ type: "text", text: completion.text }] : [],
119
- api: options.model.api,
120
- provider: options.model.provider,
121
- model: options.model.id,
122
- usage: completion.usage,
123
- stopReason: completion.stopReason,
124
- timestamp: Date.now(),
125
- ...(completion.stopReason === "error"
126
- ? { errorMessage: completion.text || "Worker compaction provider request failed." }
202
+ ...(options.toolSurface.gateway
203
+ ? { recordUsage: (delta) => options.toolSurface.gateway?.recordUsage(delta) }
127
204
  : {}),
128
- };
129
- recordAssistantUsage(options.toolSurface, response);
130
- checkpointUsage("Persisted worker compaction provider usage before verification.");
131
- return response;
132
- }
133
- finally {
134
- reservation?.release();
135
- }
136
- },
137
- });
138
- },
139
- getFailedCompactionUsage: () => failedCompactionUsage,
205
+ });
206
+ try {
207
+ let completion;
208
+ try {
209
+ completion = await options.runIsolatedCompletion({
210
+ systemPrompt: context.systemPrompt ?? "",
211
+ messages: context.messages,
212
+ model: options.model,
213
+ thinkingLevel: options.thinkingLevel,
214
+ maxTokens,
215
+ requestPreflight: () => providerTurn.requestPreflight(),
216
+ signal: requestSignal,
217
+ cacheRetention: "none",
218
+ laneKind: "worker-compaction",
219
+ });
220
+ }
221
+ catch (error) {
222
+ providerTurn.close();
223
+ requestSignal.throwIfAborted();
224
+ if (preflightFailed)
225
+ throw workerCompletionCallbackFailure(preflightFailure);
226
+ throw error;
227
+ }
228
+ requestSignal.throwIfAborted();
229
+ const response = {
230
+ role: "assistant",
231
+ content: completion.text ? [{ type: "text", text: completion.text }] : [],
232
+ api: options.model.api,
233
+ provider: options.model.provider,
234
+ model: options.model.id,
235
+ usage: completion.usage,
236
+ stopReason: completion.stopReason,
237
+ timestamp: Date.now(),
238
+ ...(completion.stopReason === "error"
239
+ ? { errorMessage: completion.text || "Worker compaction provider request failed." }
240
+ : {}),
241
+ };
242
+ addUsage(compactionUsage, completion.usage);
243
+ if (providerTurn.hasOutstandingAssistantReservation()) {
244
+ providerTurn.accountAssistantUsage(completion.usage);
245
+ }
246
+ else {
247
+ providerTurn.accountUnverifiedResultUsageDelta(completion.usage);
248
+ }
249
+ checkpointUsage("Persisted worker compaction provider usage before verification.");
250
+ if (providerTurn.hasSuccessfulPreflight()) {
251
+ try {
252
+ providerTurn.consumeTerminalAssistantAndHold();
253
+ }
254
+ catch (error) {
255
+ if (!preflightFailed) {
256
+ preflightFailed = true;
257
+ preflightFailure = error;
258
+ }
259
+ }
260
+ }
261
+ providerTurn.close();
262
+ requestSignal.throwIfAborted();
263
+ if (preflightFailed)
264
+ throw workerCompletionCallbackFailure(preflightFailure);
265
+ providerTurn.assertProviderOutputPreflight("compaction");
266
+ return response;
267
+ }
268
+ finally {
269
+ providerTurn.close();
270
+ }
271
+ },
272
+ });
273
+ },
274
+ getFailedCompactionUsage: () => failedCompactionUsage,
275
+ };
140
276
  };
141
277
  const ledger = {
142
278
  changedFiles,
@@ -149,6 +285,7 @@ export function createWorkerAttemptExecutor(options) {
149
285
  if (ran)
150
286
  throw new Error("A worker attempt executor may run only once.");
151
287
  ran = true;
288
+ options.conversation.beginAttemptUsage(options.durableHandle.attemptId);
152
289
  if (!options.hasPersistedUsageCheckpoint) {
153
290
  checkpointUsage("Persisted deterministic cumulative usage baseline for the durable worker transcript.");
154
291
  }
@@ -184,171 +321,299 @@ export function createWorkerAttemptExecutor(options) {
184
321
  stopReason: options.recoveredTerminal.stopReason,
185
322
  };
186
323
  }
324
+ const retentionPolicy = createRetentionPolicy(signal);
187
325
  const persistedToolAssistantIds = new Set();
188
326
  const pendingToolAssistants = new Map();
189
- let providerReservation;
190
- const releaseProviderReservation = () => {
191
- providerReservation?.release();
192
- providerReservation = undefined;
193
- };
194
- const persistToolRequest = (message) => {
195
- const toolCallIds = message.content.flatMap((content) => content.type === "toolCall" ? [content.id] : []);
196
- if (toolCallIds.length === 0 || toolCallIds.every((id) => persistedToolAssistantIds.has(id)))
197
- return;
198
- recordAssistantUsage(options.toolSurface, message);
199
- options.conversation.appendMessage(message);
200
- for (const id of toolCallIds) {
201
- persistedToolAssistantIds.add(id);
202
- pendingToolAssistants.delete(id);
203
- }
204
- checkpointUsage("Persisted worker assistant tool request and its cumulative provider usage.");
205
- releaseProviderReservation();
327
+ let activeProviderTurn;
328
+ const closeActiveProviderTurn = () => {
329
+ const active = activeProviderTurn;
330
+ activeProviderTurn = undefined;
331
+ active?.close();
206
332
  };
207
333
  options.toolSurface.gateway?.assertBudgetAvailable("worker_provider_completion");
208
334
  const availableTokens = remainingAttemptTokens();
209
335
  if (availableTokens !== undefined && availableTokens <= 0) {
210
336
  throw new Error("Worker token budget exhausted before provider completion.");
211
337
  }
212
- let history = options.conversation.getProviderMessages();
213
- if (history.length === 0) {
214
- options.conversation.appendMessage({
215
- role: "user",
216
- content: [{ type: "text", text: userPrompt }],
217
- timestamp: Date.now(),
218
- });
219
- history = options.conversation.getProviderMessages();
220
- }
338
+ options.conversation.ensureAttemptUserPrompt(options.durableHandle.attemptId, userPrompt);
339
+ let history = [];
221
340
  let completion;
222
- try {
223
- completion = await options.runIsolatedCompletion({
224
- systemPrompt: composeSubagentSystemPrompt({
225
- soul: options.soul,
226
- rolePrompt: [systemPrompt, options.workerResourceSystemPrompt].filter(Boolean).join("\n\n"),
227
- }),
228
- history,
229
- messages: [],
230
- model: options.model,
231
- thinkingLevel: options.thinkingLevel,
232
- maxTokens: Math.min(options.laneCapability.laneMaxOutputTokens, availableTokens ?? Number.POSITIVE_INFINITY),
233
- tools: options.toolSurface.tools,
234
- requestPreflight: async () => {
235
- releaseProviderReservation();
236
- providerReservation = await reserveProviderBudget(options.laneCapability.laneMaxOutputTokens, "worker_provider_completion", signal);
237
- return { maxTokens: providerReservation.maxTokens };
238
- },
239
- beforeToolCall: async (context, toolSignal) => {
240
- persistToolRequest(context.assistantMessage);
241
- const decision = await options.toolSurface.beforeToolCall(context, toolSignal);
242
- if (decision?.block) {
243
- toolIssues.add(`${context.toolCall.name} blocked: ${decision.reason ?? "capability denied"}`);
244
- }
245
- else {
246
- checkpointUsage(`Authorized worker tool '${context.toolCall.name}' under its durable grant.`);
247
- }
248
- return decision;
249
- },
250
- afterToolCall: async ({ toolCall, args, isError }) => {
251
- if ((toolCall.name === "write" || toolCall.name === "edit") &&
252
- args &&
253
- typeof args === "object" &&
254
- !Array.isArray(args)) {
255
- const rawPath = args.path;
256
- if (typeof rawPath === "string" && rawPath.length > 0) {
257
- const absolutePath = path.isAbsolute(rawPath)
258
- ? path.resolve(rawPath)
259
- : path.resolve(options.cwd, rawPath);
260
- let canonicalPath = absolutePath;
341
+ const attemptProviderCompletion = async () => {
342
+ // Later attempts resume from the durably persisted transcript, not a stale snapshot.
343
+ const transcriptCommit = options.conversation.beginTranscriptCommit();
344
+ history = transcriptCommit.history;
345
+ const historyLength = history.length;
346
+ const transcriptCursor = transcriptCommit.cursor;
347
+ const durableCallbackMessages = [];
348
+ let callbackFailed = false;
349
+ let callbackFailure;
350
+ const retainCallbackFailure = (error) => {
351
+ if (callbackFailed)
352
+ return;
353
+ callbackFailed = true;
354
+ callbackFailure = error;
355
+ };
356
+ const providerTurn = new WorkerProviderTurnProtocol({
357
+ acquireReservation: () => reserveProviderBudget(options.laneCapability.laneMaxOutputTokens, "worker_provider_completion", signal),
358
+ signal,
359
+ onFailure: retainCallbackFailure,
360
+ ...(options.toolSurface.gateway
361
+ ? { recordUsage: (delta) => options.toolSurface.gateway?.recordUsage(delta) }
362
+ : {}),
363
+ });
364
+ activeProviderTurn = providerTurn;
365
+ const persistToolRequest = (message) => {
366
+ signal.throwIfAborted();
367
+ const toolCallIds = message.content.flatMap((content) => content.type === "toolCall" ? [content.id] : []);
368
+ if (toolCallIds.length === 0 || toolCallIds.every((id) => persistedToolAssistantIds.has(id)))
369
+ return;
370
+ providerTurn.accountAssistantUsage(message.usage);
371
+ options.conversation.appendMessage(message);
372
+ for (const id of toolCallIds) {
373
+ persistedToolAssistantIds.add(id);
374
+ pendingToolAssistants.delete(id);
375
+ }
376
+ checkpointUsage("Persisted worker assistant tool request and its cumulative provider usage.");
377
+ providerTurn.consumeToolAssistantAndRelease();
378
+ durableCallbackMessages.push(message);
379
+ };
380
+ let committed = false;
381
+ const abortTranscriptCursor = () => {
382
+ providerTurn.close();
383
+ options.conversation.abortTranscriptCommit(transcriptCursor);
384
+ };
385
+ signal.addEventListener("abort", abortTranscriptCursor, { once: true });
386
+ try {
387
+ signal.throwIfAborted();
388
+ let result;
389
+ try {
390
+ result = await options.runIsolatedCompletion({
391
+ systemPrompt: composeSubagentSystemPrompt({
392
+ soul: options.soul,
393
+ rolePrompt: [systemPrompt, options.workerResourceSystemPrompt]
394
+ .filter(Boolean)
395
+ .join("\n\n"),
396
+ }),
397
+ history,
398
+ messages: [],
399
+ model: options.model,
400
+ thinkingLevel: options.thinkingLevel,
401
+ maxTokens: Math.min(options.laneCapability.laneMaxOutputTokens, availableTokens ?? Number.POSITIVE_INFINITY),
402
+ tools: options.toolSurface.tools,
403
+ requestPreflight: () => providerTurn.requestPreflight(),
404
+ beforeToolCall: async (context, toolSignal) => {
261
405
  try {
262
- canonicalPath = safeRealpathSync(absolutePath);
406
+ signal.throwIfAborted();
407
+ persistToolRequest(context.assistantMessage);
408
+ const decision = await options.toolSurface.beforeToolCall(context, toolSignal);
409
+ signal.throwIfAborted();
410
+ if (decision?.block) {
411
+ toolIssues.add(`${context.toolCall.name} blocked: ${decision.reason ?? "capability denied"}`);
412
+ }
413
+ else {
414
+ checkpointUsage(`Authorized worker tool '${context.toolCall.name}' under its durable grant.`);
415
+ }
416
+ return decision;
263
417
  }
264
- catch {
265
- // The operation entered execution; retain its lexical target if canonicalization failed.
418
+ catch (error) {
419
+ retainCallbackFailure(error);
420
+ throw error;
266
421
  }
267
- recordChangedFile(path.relative(options.cwd, canonicalPath).split(path.sep).join("/"));
268
- }
269
- }
270
- if (isError)
271
- toolIssues.add(`${toolCall.name} failed during isolated execution`);
272
- return undefined;
273
- },
274
- onMessage: (message) => {
275
- if (message.role === "assistant" && isToolRequest(message)) {
276
- // Known calls are normalized before beforeToolCall and persist from that hook.
277
- // Retain the request only so immediate unknown/malformed results can close the
278
- // transcript without freezing pre-repair arguments into durable history.
279
- for (const content of message.content) {
280
- if (content.type === "toolCall")
281
- pendingToolAssistants.set(content.id, message);
282
- }
283
- return;
284
- }
285
- if (message.role === "toolResult" && !persistedToolAssistantIds.has(message.toolCallId)) {
286
- const pending = pendingToolAssistants.get(message.toolCallId);
287
- if (pending)
288
- persistToolRequest(pending);
289
- }
290
- if (message.role === "assistant")
291
- recordAssistantUsage(options.toolSurface, message);
292
- options.conversation.appendMessage(message);
293
- options.agentControl.acknowledgeMailboxMessage(options.agentId, message);
294
- if (message.role === "assistant") {
295
- checkpointUsage("Persisted worker assistant response and its cumulative provider usage.");
296
- releaseProviderReservation();
297
- }
298
- if (message.role === "toolResult")
299
- checkpointUsage(`Persisted worker tool result '${message.toolCallId}'.`);
300
- },
301
- getSteeringMessages: async () => {
302
- const includeFollowUp = firstMailboxPoll;
303
- firstMailboxPoll = false;
304
- return options.agentControl.mailboxMessagesForConversation(options.agentId, options.conversation, includeFollowUp);
305
- },
306
- getFollowUpMessages: async () => options.agentControl.mailboxMessagesForConversation(options.agentId, options.conversation, true),
307
- ...(retentionPolicy
308
- ? {
309
- transformContext: async (messages) => {
422
+ },
423
+ afterToolCall: async ({ toolCall, args, isError }) => {
310
424
  try {
311
- const retained = await options.conversation.compactProviderContext(retentionPolicy, options.signal);
312
- if (retained.status === "compacted_verified" ||
313
- retained.status === "compacted_deterministic") {
314
- providerContextCompacted = true;
425
+ if ((toolCall.name === "write" || toolCall.name === "edit") &&
426
+ args &&
427
+ typeof args === "object" &&
428
+ !Array.isArray(args)) {
429
+ const rawPath = args.path;
430
+ if (typeof rawPath === "string" && rawPath.length > 0) {
431
+ const absolutePath = path.isAbsolute(rawPath)
432
+ ? path.resolve(rawPath)
433
+ : path.resolve(options.cwd, rawPath);
434
+ let canonicalPath = absolutePath;
435
+ try {
436
+ canonicalPath = safeRealpathSync(absolutePath);
437
+ }
438
+ catch {
439
+ // The operation entered execution; retain its lexical target if canonicalization failed.
440
+ }
441
+ recordChangedFile(path.relative(options.cwd, canonicalPath).split(path.sep).join("/"));
442
+ }
315
443
  }
316
- if (retained.contextUsage.tokens > retentionPolicy.maxContextTokens &&
317
- !retentionWarningEmitted) {
318
- retentionWarningEmitted = true;
319
- options.warn(`Worker ${options.laneId} has one retained turn larger than its context policy; provider overflow recovery may be required.`);
444
+ if (isError)
445
+ toolIssues.add(`${toolCall.name} failed during isolated execution`);
446
+ signal.throwIfAborted();
447
+ return undefined;
448
+ }
449
+ catch (error) {
450
+ retainCallbackFailure(error);
451
+ throw error;
452
+ }
453
+ },
454
+ onMessage: (message) => {
455
+ try {
456
+ signal.throwIfAborted();
457
+ if (message.role === "assistant" && isToolRequest(message)) {
458
+ // Known calls are normalized before beforeToolCall and persist from that hook.
459
+ // Retain the request only so immediate unknown/malformed results can close the
460
+ // transcript without freezing pre-repair arguments into durable history.
461
+ for (const content of message.content) {
462
+ if (content.type === "toolCall")
463
+ pendingToolAssistants.set(content.id, message);
464
+ }
465
+ return;
320
466
  }
321
- if (retained.status !== "compacted_verified" &&
322
- retained.status !== "compacted_deterministic") {
323
- return messages;
467
+ if (message.role === "toolResult" &&
468
+ !persistedToolAssistantIds.has(message.toolCallId)) {
469
+ const pending = pendingToolAssistants.get(message.toolCallId);
470
+ if (pending)
471
+ persistToolRequest(pending);
324
472
  }
325
- return sanitizeToolFailureContext(retained.context.messages, "").messages;
473
+ if (message.role === "assistant") {
474
+ providerTurn.accountAssistantUsage(message.usage);
475
+ }
476
+ options.conversation.appendMessage(message);
477
+ options.agentControl.acknowledgeMailboxMessage(options.agentId, message);
478
+ if (message.role === "assistant") {
479
+ checkpointUsage("Persisted worker assistant response and its cumulative provider usage.");
480
+ providerTurn.consumeTerminalAssistantAndHold();
481
+ }
482
+ if (message.role === "toolResult")
483
+ checkpointUsage(`Persisted worker tool result '${message.toolCallId}'.`);
484
+ durableCallbackMessages.push(message);
326
485
  }
327
486
  catch (error) {
328
- if (!retentionWarningEmitted) {
329
- retentionWarningEmitted = true;
330
- options.warn(`Worker context retention failed: ${error instanceof Error ? error.message : String(error)}`);
331
- }
487
+ retainCallbackFailure(error);
488
+ throw error;
489
+ }
490
+ },
491
+ getSteeringMessages: async () => {
492
+ try {
493
+ signal.throwIfAborted();
494
+ const includeFollowUp = firstMailboxPoll;
495
+ firstMailboxPoll = false;
496
+ const messages = options.agentControl.mailboxMessagesForConversation(options.agentId, options.conversation, includeFollowUp);
497
+ signal.throwIfAborted();
332
498
  return messages;
333
499
  }
500
+ catch (error) {
501
+ retainCallbackFailure(error);
502
+ throw error;
503
+ }
334
504
  },
335
- }
336
- : {}),
337
- signal,
338
- cacheRetention: "short",
339
- laneKind: "worker",
505
+ getFollowUpMessages: async () => {
506
+ try {
507
+ signal.throwIfAborted();
508
+ const messages = options.agentControl.mailboxMessagesForConversation(options.agentId, options.conversation, true);
509
+ signal.throwIfAborted();
510
+ return messages;
511
+ }
512
+ catch (error) {
513
+ retainCallbackFailure(error);
514
+ throw error;
515
+ }
516
+ },
517
+ ...(retentionPolicy
518
+ ? {
519
+ transformContext: async (messages) => {
520
+ try {
521
+ signal.throwIfAborted();
522
+ const retained = await options.conversation.compactProviderContext(retentionPolicy, signal);
523
+ signal.throwIfAborted();
524
+ if (retained.contextUsage.tokens > retentionPolicy.maxContextTokens &&
525
+ !retentionWarningEmitted) {
526
+ retentionWarningEmitted = true;
527
+ options.warn(`Worker ${options.laneId} has one retained turn larger than its context policy; provider overflow recovery may be required.`);
528
+ }
529
+ if (retained.status !== "compacted_verified" &&
530
+ retained.status !== "compacted_deterministic") {
531
+ return messages;
532
+ }
533
+ return sanitizeToolFailureContext(retained.context.messages, "").messages;
534
+ }
535
+ catch (error) {
536
+ if (signal.aborted) {
537
+ retainCallbackFailure(error);
538
+ signal.throwIfAborted();
539
+ }
540
+ if (error instanceof WorkerConversationOwnershipError) {
541
+ retainCallbackFailure(error);
542
+ throw error;
543
+ }
544
+ if (!retentionWarningEmitted) {
545
+ retentionWarningEmitted = true;
546
+ options.warn(`Worker context retention failed: ${error instanceof Error ? error.message : String(error)}`);
547
+ }
548
+ return messages;
549
+ }
550
+ },
551
+ }
552
+ : {}),
553
+ signal,
554
+ cacheRetention: "short",
555
+ laneKind: "worker",
556
+ });
557
+ }
558
+ catch (error) {
559
+ providerTurn.close();
560
+ if (signal.aborted)
561
+ throw error;
562
+ if (callbackFailed)
563
+ throw workerCompletionCallbackFailure(callbackFailure);
564
+ throw error;
565
+ }
566
+ try {
567
+ providerTurn.assertEverySuccessfulPreflightConsumed();
568
+ }
569
+ catch (error) {
570
+ retainCallbackFailure(error);
571
+ }
572
+ signal.throwIfAborted();
573
+ if (providerTurn.accountUnverifiedResultUsageDelta(result.usage)) {
574
+ checkpointUsage("Persisted supplemental provider result usage before rejecting unverified completion evidence.");
575
+ }
576
+ providerTurn.close();
577
+ if (callbackFailed)
578
+ throw workerCompletionCallbackFailure(callbackFailure);
579
+ providerTurn.assertProviderOutputPreflight();
580
+ const evidenced = callbackEvidencedCompletion(result, historyLength, durableCallbackMessages);
581
+ signal.throwIfAborted();
582
+ const appended = options.conversation.commitTranscript(transcriptCursor, evidenced.suffix, {
583
+ appendMissing: false,
584
+ });
585
+ if (appended !== 0) {
586
+ throw new WorkerConversationOwnershipError("Worker callback transcript verification unexpectedly appended missing messages.");
587
+ }
588
+ committed = true;
589
+ return evidenced.completion;
590
+ }
591
+ finally {
592
+ signal.removeEventListener("abort", abortTranscriptCursor);
593
+ providerTurn.close();
594
+ if (activeProviderTurn === providerTurn)
595
+ activeProviderTurn = undefined;
596
+ if (!committed)
597
+ options.conversation.abortTranscriptCommit(transcriptCursor);
598
+ }
599
+ };
600
+ try {
601
+ completion = await runProviderCompletionWithBackoff({
602
+ attempt: attemptProviderCompletion,
603
+ onAttemptFailure: closeActiveProviderTurn,
604
+ provider: options.model.provider,
605
+ laneId: options.laneId,
606
+ warn: options.warn,
607
+ ...(signal ? { signal } : {}),
340
608
  });
341
609
  }
342
610
  finally {
343
- releaseProviderReservation();
344
- }
345
- if (!providerContextCompacted) {
346
- options.conversation.commitTranscript(completion.messages ?? options.conversation.getProviderMessages());
611
+ closeActiveProviderTurn();
347
612
  }
348
- checkpointUsage(`Persisted worker conversation through ${options.conversation.getProviderContext().messages.length} messages.`);
613
+ const cumulativeUsage = checkpointUsage("Verified the callback-persisted worker conversation terminal suffix.");
349
614
  return {
350
615
  text: completion.text,
351
- costUsd: completion.usage.cost.total,
616
+ costUsd: cumulativeUsage.costUsd,
352
617
  stopReason: String(completion.stopReason),
353
618
  changedFiles: [...changedFiles],
354
619
  blockers: [...toolIssues],