codex-chatgpt-control 0.5.1-alpha.2 → 0.5.1-alpha.4

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 (231) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +43 -0
  3. package/contracts/v1/fixtures/backend-capabilities.json +40 -1
  4. package/contracts/v1/fixtures/backend-compatibility.json +21 -0
  5. package/contracts/v1/fixtures/backend-version.json +6 -1
  6. package/contracts/v1/fixtures/download-blocked-by-browser.json +22 -0
  7. package/contracts/v1/fixtures/download-receipt-failed.json +22 -0
  8. package/contracts/v1/fixtures/download-receipt-timeout.json +22 -0
  9. package/contracts/v1/fixtures/journal-rpc-indeterminate.json +32 -0
  10. package/contracts/v1/fixtures/journal-rpc-unsupported-platform.json +32 -0
  11. package/contracts/v1/fixtures/journal-runtime-unavailable.json +32 -0
  12. package/contracts/v1/fixtures/operation-action-prepared-event.json +42 -0
  13. package/contracts/v1/fixtures/operation-action.json +13 -0
  14. package/contracts/v1/fixtures/operation-artifact-receipt.json +14 -0
  15. package/contracts/v1/fixtures/operation-artifact-transfer-intent-event.json +24 -0
  16. package/contracts/v1/fixtures/operation-artifact-transfer-receipt-event.json +26 -0
  17. package/contracts/v1/fixtures/operation-artifact-transfer-state.json +178 -0
  18. package/contracts/v1/fixtures/operation-blocker.json +10 -0
  19. package/contracts/v1/fixtures/operation-collect-request.json +17 -0
  20. package/contracts/v1/fixtures/operation-collect-result.json +42 -0
  21. package/contracts/v1/fixtures/operation-control-receipt.json +13 -0
  22. package/contracts/v1/fixtures/operation-control-request.json +18 -0
  23. package/contracts/v1/fixtures/operation-control-result.json +31 -0
  24. package/contracts/v1/fixtures/operation-event.json +18 -0
  25. package/contracts/v1/fixtures/operation-handle.json +10 -0
  26. package/contracts/v1/fixtures/operation-inspect-request.json +13 -0
  27. package/contracts/v1/fixtures/operation-inspect-result.json +205 -0
  28. package/contracts/v1/fixtures/operation-ownership-baseline-event.json +34 -0
  29. package/contracts/v1/fixtures/operation-receipt.json +32 -0
  30. package/contracts/v1/fixtures/operation-recovery-decision.json +7 -0
  31. package/contracts/v1/fixtures/operation-recovery-observation.json +12 -0
  32. package/contracts/v1/fixtures/operation-request.json +33 -0
  33. package/contracts/v1/fixtures/operation-state.json +144 -0
  34. package/contracts/v1/fixtures/operation-submission-witness-event.json +20 -0
  35. package/contracts/v1/fixtures/operation-submission-witness.json +11 -0
  36. package/contracts/v1/fixtures/operation-submit-result.json +16 -0
  37. package/contracts/v1/fixtures/operation-target-established-event.json +21 -0
  38. package/contracts/v1/manifest.json +185 -1
  39. package/contracts/v1/parity-suite.json +106 -8
  40. package/contracts/v1/schemas/backend-compatibility.schema.json +65 -0
  41. package/contracts/v1/schemas/backend-request.schema.json +8 -2
  42. package/contracts/v1/schemas/capabilities.schema.json +81 -1
  43. package/contracts/v1/schemas/manifest.schema.json +57 -0
  44. package/contracts/v1/schemas/operation-action.schema.json +155 -0
  45. package/contracts/v1/schemas/operation-artifact-receipt.schema.json +89 -0
  46. package/contracts/v1/schemas/operation-blocker.schema.json +70 -0
  47. package/contracts/v1/schemas/operation-collect-request.schema.json +46 -0
  48. package/contracts/v1/schemas/operation-collect-result.schema.json +74 -0
  49. package/contracts/v1/schemas/operation-control-receipt.schema.json +108 -0
  50. package/contracts/v1/schemas/operation-control-request.schema.json +111 -0
  51. package/contracts/v1/schemas/operation-control-result.schema.json +112 -0
  52. package/contracts/v1/schemas/operation-event.schema.json +756 -0
  53. package/contracts/v1/schemas/operation-handle.schema.json +55 -0
  54. package/contracts/v1/schemas/operation-inspect-request.schema.json +42 -0
  55. package/contracts/v1/schemas/operation-inspect-result.schema.json +1662 -0
  56. package/contracts/v1/schemas/operation-receipt.schema.json +153 -0
  57. package/contracts/v1/schemas/operation-recovery.schema.json +314 -0
  58. package/contracts/v1/schemas/operation-request.schema.json +158 -0
  59. package/contracts/v1/schemas/operation-state.schema.json +691 -0
  60. package/contracts/v1/schemas/operation-submission-witness.schema.json +48 -0
  61. package/contracts/v1/schemas/operation-submit-result.schema.json +94 -0
  62. package/contracts/v1/surface-drift-policy.json +50 -0
  63. package/contracts/v1/vectors/operation-request-digest-v1.json +43 -0
  64. package/dist/codex-chatgpt-control-backend.mjs +45882 -9361
  65. package/dist/codex-chatgpt-control-journal.mjs +5547 -0
  66. package/dist/codex-chatgpt-control-live-smoke.bundle.mjs +51677 -0
  67. package/dist/codex-chatgpt-control-release-canary.bundle.mjs +52104 -0
  68. package/dist/codex-chatgpt-control.bundle.mjs +48392 -10260
  69. package/dist/src/backend/client.d.ts +103 -2
  70. package/dist/src/backend/client.js +1436 -97
  71. package/dist/src/backend/compatibility.d.ts +12 -0
  72. package/dist/src/backend/compatibility.js +208 -0
  73. package/dist/src/backend/protocol.d.ts +84 -1
  74. package/dist/src/backend/protocol.js +31 -5
  75. package/dist/src/backend/runtime-identity.d.ts +10 -0
  76. package/dist/src/backend/runtime-identity.js +141 -0
  77. package/dist/src/backend/session.d.ts +10 -2
  78. package/dist/src/backend/session.js +478 -7
  79. package/dist/src/backend/stdio-server.d.ts +4 -1
  80. package/dist/src/backend/stdio-server.js +240 -28
  81. package/dist/src/browser/active-composer-file-input.d.ts +9 -0
  82. package/dist/src/browser/active-composer-file-input.js +33 -0
  83. package/dist/src/browser/attach.d.ts +4 -1
  84. package/dist/src/browser/attach.js +136 -28
  85. package/dist/src/browser/chatgpt-url.d.ts +2 -0
  86. package/dist/src/browser/chatgpt-url.js +9 -0
  87. package/dist/src/browser/downloads.d.ts +17 -1
  88. package/dist/src/browser/downloads.js +150 -28
  89. package/dist/src/browser/page-state.js +6 -0
  90. package/dist/src/client.d.ts +43 -0
  91. package/dist/src/client.js +1279 -59
  92. package/dist/src/commands/artifacts.js +6 -4
  93. package/dist/src/commands/chat-popover.d.ts +37 -0
  94. package/dist/src/commands/chat-popover.js +537 -0
  95. package/dist/src/commands/configuration.d.ts +5 -0
  96. package/dist/src/commands/configuration.js +134 -21
  97. package/dist/src/commands/context.js +10 -3
  98. package/dist/src/commands/doctor.d.ts +1 -1
  99. package/dist/src/commands/doctor.js +27 -4
  100. package/dist/src/commands/experience.d.ts +5 -0
  101. package/dist/src/commands/experience.js +104 -21
  102. package/dist/src/commands/files.js +14 -33
  103. package/dist/src/commands/modes.js +85 -21
  104. package/dist/src/commands/power-discovery.d.ts +130 -0
  105. package/dist/src/commands/power-discovery.js +669 -0
  106. package/dist/src/commands/project-sources.js +325 -31
  107. package/dist/src/commands/sequence.js +59 -32
  108. package/dist/src/commands/session.js +19 -6
  109. package/dist/src/commands/work.js +31 -0
  110. package/dist/src/dom/composer-text.d.ts +6 -0
  111. package/dist/src/dom/composer-text.js +85 -0
  112. package/dist/src/errors.d.ts +9 -0
  113. package/dist/src/errors.js +22 -0
  114. package/dist/src/index.d.ts +6 -0
  115. package/dist/src/index.js +9 -0
  116. package/dist/src/operations/artifact-output.d.ts +69 -0
  117. package/dist/src/operations/artifact-output.js +1748 -0
  118. package/dist/src/operations/artifact-stream.d.ts +19 -0
  119. package/dist/src/operations/artifact-stream.js +35 -0
  120. package/dist/src/operations/artifact-transfer.d.ts +149 -0
  121. package/dist/src/operations/artifact-transfer.js +1312 -0
  122. package/dist/src/operations/browser-adapter.d.ts +167 -0
  123. package/dist/src/operations/browser-adapter.js +2103 -0
  124. package/dist/src/operations/browser-observation.d.ts +110 -0
  125. package/dist/src/operations/browser-observation.js +1256 -0
  126. package/dist/src/operations/browser-target.d.ts +105 -0
  127. package/dist/src/operations/browser-target.js +531 -0
  128. package/dist/src/operations/canonical.d.ts +4 -0
  129. package/dist/src/operations/canonical.js +412 -0
  130. package/dist/src/operations/chatgpt-runtime.d.ts +72 -0
  131. package/dist/src/operations/chatgpt-runtime.js +1232 -0
  132. package/dist/src/operations/client.d.ts +138 -0
  133. package/dist/src/operations/client.js +1145 -0
  134. package/dist/src/operations/collector.d.ts +195 -0
  135. package/dist/src/operations/collector.js +1039 -0
  136. package/dist/src/operations/configuration-routing.d.ts +4 -0
  137. package/dist/src/operations/configuration-routing.js +14 -0
  138. package/dist/src/operations/control.d.ts +372 -0
  139. package/dist/src/operations/control.js +1498 -0
  140. package/dist/src/operations/file-identity.d.ts +33 -0
  141. package/dist/src/operations/file-identity.js +153 -0
  142. package/dist/src/operations/handle.d.ts +19 -0
  143. package/dist/src/operations/handle.js +727 -0
  144. package/dist/src/operations/index.d.ts +25 -0
  145. package/dist/src/operations/index.js +25 -0
  146. package/dist/src/operations/journal-authority.d.ts +12 -0
  147. package/dist/src/operations/journal-authority.js +1 -0
  148. package/dist/src/operations/journal-rpc-client.d.ts +12 -0
  149. package/dist/src/operations/journal-rpc-client.js +225 -0
  150. package/dist/src/operations/journal-rpc-protocol.d.ts +32 -0
  151. package/dist/src/operations/journal-rpc-protocol.js +214 -0
  152. package/dist/src/operations/journal-rpc-server.d.ts +15 -0
  153. package/dist/src/operations/journal-rpc-server.js +272 -0
  154. package/dist/src/operations/journal.d.ts +143 -0
  155. package/dist/src/operations/journal.js +1957 -0
  156. package/dist/src/operations/production-attachments.d.ts +118 -0
  157. package/dist/src/operations/production-attachments.js +1035 -0
  158. package/dist/src/operations/production-chatgpt-artifacts.d.ts +74 -0
  159. package/dist/src/operations/production-chatgpt-artifacts.js +1146 -0
  160. package/dist/src/operations/production-chatgpt-attachments.d.ts +80 -0
  161. package/dist/src/operations/production-chatgpt-attachments.js +1639 -0
  162. package/dist/src/operations/production-configuration.d.ts +42 -0
  163. package/dist/src/operations/production-configuration.js +1544 -0
  164. package/dist/src/operations/production-primitives.d.ts +53 -0
  165. package/dist/src/operations/production-primitives.js +856 -0
  166. package/dist/src/operations/production-work-steer.d.ts +203 -0
  167. package/dist/src/operations/production-work-steer.js +1218 -0
  168. package/dist/src/operations/recovery.d.ts +80 -0
  169. package/dist/src/operations/recovery.js +174 -0
  170. package/dist/src/operations/runtime-adapter.d.ts +126 -0
  171. package/dist/src/operations/runtime-adapter.js +802 -0
  172. package/dist/src/operations/send-once.d.ts +224 -0
  173. package/dist/src/operations/send-once.js +1081 -0
  174. package/dist/src/operations/service.d.ts +286 -0
  175. package/dist/src/operations/service.js +2831 -0
  176. package/dist/src/operations/staging.d.ts +136 -0
  177. package/dist/src/operations/staging.js +630 -0
  178. package/dist/src/operations/state-machine.d.ts +29 -0
  179. package/dist/src/operations/state-machine.js +2047 -0
  180. package/dist/src/operations/submission.d.ts +423 -0
  181. package/dist/src/operations/submission.js +1676 -0
  182. package/dist/src/operations/transactional-chat-power.d.ts +17 -0
  183. package/dist/src/operations/transactional-chat-power.js +212 -0
  184. package/dist/src/operations/turn-ownership.d.ts +202 -0
  185. package/dist/src/operations/turn-ownership.js +700 -0
  186. package/dist/src/operations/types.d.ts +454 -0
  187. package/dist/src/operations/types.js +20 -0
  188. package/dist/src/operations/wire-requests.d.ts +21 -0
  189. package/dist/src/operations/wire-requests.js +396 -0
  190. package/dist/src/operations/wire-results.d.ts +97 -0
  191. package/dist/src/operations/wire-results.js +818 -0
  192. package/dist/src/runner/responses.d.ts +3 -1
  193. package/dist/src/runner/responses.js +19 -1
  194. package/dist/src/runner/result.js +227 -55
  195. package/dist/src/runner/types.d.ts +12 -0
  196. package/dist/src/runtime/command-routing.d.ts +149 -0
  197. package/dist/src/runtime/command-routing.js +431 -0
  198. package/dist/src/runtime/coordinated-browser.d.ts +24 -0
  199. package/dist/src/runtime/coordinated-browser.js +315 -0
  200. package/dist/src/runtime/coordinated-page.d.ts +41 -0
  201. package/dist/src/runtime/coordinated-page.js +651 -0
  202. package/dist/src/runtime/operation-context.d.ts +142 -0
  203. package/dist/src/runtime/operation-context.js +410 -0
  204. package/dist/src/runtime/runtime-session.d.ts +95 -0
  205. package/dist/src/runtime/runtime-session.js +314 -0
  206. package/dist/src/runtime/tab-coordinator.d.ts +200 -0
  207. package/dist/src/runtime/tab-coordinator.js +1200 -0
  208. package/dist/src/runtime/value-boundaries.d.ts +18 -0
  209. package/dist/src/runtime/value-boundaries.js +49 -0
  210. package/dist/src/safety/blockers.js +8 -1
  211. package/dist/src/safety/untrusted-output.js +2 -1
  212. package/dist/src/scripts/backend-server.js +4 -1
  213. package/dist/src/scripts/capture-surface-profile.js +7 -2
  214. package/dist/src/scripts/journal-server.d.ts +2 -0
  215. package/dist/src/scripts/journal-server.js +37 -0
  216. package/dist/src/scripts/live-smoke/harness.js +79 -7
  217. package/dist/src/scripts/live-smoke/scenarios.d.ts +42 -1
  218. package/dist/src/scripts/live-smoke/scenarios.js +147 -30
  219. package/dist/src/scripts/live-smoke/transactional.d.ts +17 -0
  220. package/dist/src/scripts/live-smoke/transactional.js +254 -0
  221. package/dist/src/scripts/live-smoke/types.d.ts +3 -0
  222. package/dist/src/scripts/release-canary-module.js +17 -4
  223. package/dist/src/types.d.ts +38 -1
  224. package/package.json +8 -3
  225. package/references/2026-08-16-transactional-operations.md +459 -0
  226. package/references/2026-09-06-journal-service.md +140 -0
  227. package/references/agents-runner.md +34 -0
  228. package/references/backend-protocol.md +176 -0
  229. package/references/python-parity.md +137 -0
  230. package/references/responses-adapter.md +19 -0
  231. package/references/streaming.md +6 -0
@@ -0,0 +1,1312 @@
1
+ import { resolve } from "node:path";
2
+ import { commitOperationOutput, deriveOperationOutputKey } from "./artifact-output.js";
3
+ import { copyProviderChunk } from "./artifact-stream.js";
4
+ /**
5
+ * The transfer coordinator is deliberately provider agnostic. A provider
6
+ * supplies one already-authorized, exact artifact stream; this module owns
7
+ * the local effect, its durable intent, and its collision/recovery rules.
8
+ *
9
+ * The absolute destination is kept in the active call only. It is included
10
+ * in the keyed destination evidence supplied by the caller, but is never
11
+ * copied into an intent, receipt, lookup, or error.
12
+ */
13
+ export const ARTIFACT_TRANSFER_SCHEMA_VERSION = "chatgpt.browser_control.artifact_transfer.v1";
14
+ export const ARTIFACT_TRANSFER_INTENT_SCHEMA_VERSION = "chatgpt.browser_control.artifact_transfer_intent.v1";
15
+ export const ARTIFACT_TRANSFER_RECEIPT_SCHEMA_VERSION = "chatgpt.browser_control.artifact_transfer_receipt.v1";
16
+ const SCHEMA_VERSION = ARTIFACT_TRANSFER_SCHEMA_VERSION;
17
+ const INTENT_SCHEMA_VERSION = ARTIFACT_TRANSFER_INTENT_SCHEMA_VERSION;
18
+ const RECEIPT_SCHEMA_VERSION = ARTIFACT_TRANSFER_RECEIPT_SCHEMA_VERSION;
19
+ const DIGEST_PATTERN = /^hmac-sha256:[0-9a-f]{64}$/;
20
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
21
+ const SHA256_PATTERN = /^[0-9a-f]{64}$/;
22
+ const OUTPUT_KEY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
23
+ const CODE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
24
+ const INSTANT_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
25
+ const DEFAULT_MAX_BYTES = 128 * 1024 * 1024;
26
+ const MAX_MAX_BYTES = 512 * 1024 * 1024;
27
+ const DEFAULT_MAX_STRING_BYTES = 4096;
28
+ const MAX_MAX_STRING_BYTES = 16 * 1024;
29
+ const DEFAULT_MAX_COUNT = 256;
30
+ const MAX_MAX_COUNT = 1024;
31
+ const DEFAULT_MAX_DEPTH = 8;
32
+ const MAX_MAX_DEPTH = 32;
33
+ const MAX_GRAPH_NODES = 1024;
34
+ const MAX_DEADLINE_AT = Date.UTC(2100, 0, 1);
35
+ const MAX_TIMER_DELAY_MS = 2_147_483_647;
36
+ const PROVIDER_OPEN_TIMEOUT_MS = 30_000;
37
+ const PROVIDER_CLEANUP_TIMEOUT_MS = 30_000;
38
+ export class ArtifactTransferError extends Error {
39
+ code;
40
+ constructor(code, message) {
41
+ super(message);
42
+ this.code = code;
43
+ this.name = "ArtifactTransferError";
44
+ }
45
+ }
46
+ const inFlight = new Map();
47
+ class ProviderBoundaryError extends Error {
48
+ boundary;
49
+ constructor(boundary) {
50
+ super(boundary === "timeout" ? "The artifact provider exceeded its operation deadline." : "The artifact provider was aborted.");
51
+ this.boundary = boundary;
52
+ this.name = "ProviderBoundaryError";
53
+ }
54
+ }
55
+ function armProviderBoundary(prepared, fallbackTimeoutMs) {
56
+ const signal = prepared.signal;
57
+ let remaining;
58
+ if (prepared.deadlineAt !== undefined) {
59
+ const baseline = prepared.now();
60
+ if (prepared.clockFaulted()) {
61
+ throw new ArtifactTransferError("operation_clock_invalid", "Operation clock became invalid.");
62
+ }
63
+ remaining = prepared.deadlineAt - baseline;
64
+ }
65
+ else if (fallbackTimeoutMs !== undefined) {
66
+ remaining = fallbackTimeoutMs;
67
+ }
68
+ if (signal === undefined && remaining === undefined)
69
+ return undefined;
70
+ let resolveBoundary;
71
+ let timer;
72
+ let cancelled = false;
73
+ const promise = new Promise(resolve => {
74
+ resolveBoundary = resolve;
75
+ });
76
+ const listener = () => trigger("aborted");
77
+ const removeListener = () => {
78
+ if (signal === undefined)
79
+ return;
80
+ try {
81
+ signal.removeEventListener("abort", listener);
82
+ }
83
+ catch { /* fail closed through the boundary */ }
84
+ };
85
+ const clearTimer = () => {
86
+ if (timer !== undefined)
87
+ clearTimeout(timer);
88
+ timer = undefined;
89
+ };
90
+ function trigger(boundary) {
91
+ if (cancelled)
92
+ return;
93
+ cancelled = true;
94
+ clearTimer();
95
+ removeListener();
96
+ resolveBoundary(boundary);
97
+ }
98
+ const schedule = (delay) => {
99
+ if (cancelled)
100
+ return;
101
+ timer = setTimeout(() => {
102
+ if (delay > MAX_TIMER_DELAY_MS)
103
+ schedule(delay - MAX_TIMER_DELAY_MS);
104
+ else
105
+ trigger("timeout");
106
+ }, Math.min(delay, MAX_TIMER_DELAY_MS));
107
+ };
108
+ if (signal !== undefined) {
109
+ if (isSignalAborted(signal)) {
110
+ trigger("aborted");
111
+ return { promise, cancel: () => undefined };
112
+ }
113
+ try {
114
+ signal.addEventListener("abort", listener, { once: true });
115
+ }
116
+ catch {
117
+ trigger("aborted");
118
+ return { promise, cancel: () => undefined };
119
+ }
120
+ if (isSignalAborted(signal))
121
+ trigger("aborted");
122
+ }
123
+ if (!cancelled && remaining !== undefined) {
124
+ if (!Number.isFinite(remaining) || remaining <= 0)
125
+ trigger("timeout");
126
+ else
127
+ schedule(remaining);
128
+ }
129
+ return {
130
+ promise,
131
+ cancel: () => {
132
+ if (cancelled)
133
+ return;
134
+ cancelled = true;
135
+ clearTimer();
136
+ removeListener();
137
+ }
138
+ };
139
+ }
140
+ function observeLateProviderValue(value, onLateValue) {
141
+ if (onLateValue === undefined)
142
+ return;
143
+ try {
144
+ void Promise.resolve().then(() => onLateValue(value)).catch(() => undefined);
145
+ }
146
+ catch {
147
+ // A late provider capability is already outside the operation authority;
148
+ // cleanup failures remain redacted and cannot alter the durable outcome.
149
+ }
150
+ }
151
+ async function awaitProviderOperation(prepared, operation, fallbackTimeoutMs, onLateValue) {
152
+ const providerPromise = Promise.resolve().then(operation);
153
+ let valueSettled = false;
154
+ let valueForLate;
155
+ let boundaryTriggered = false;
156
+ let lateObserved = false;
157
+ const settled = providerPromise.then(value => {
158
+ valueSettled = true;
159
+ valueForLate = value;
160
+ if (boundaryTriggered && !lateObserved) {
161
+ lateObserved = true;
162
+ observeLateProviderValue(value, onLateValue);
163
+ }
164
+ return { kind: "value", value };
165
+ }, error => ({ kind: "error", error }));
166
+ let boundary;
167
+ try {
168
+ boundary = armProviderBoundary(prepared, fallbackTimeoutMs);
169
+ }
170
+ catch (error) {
171
+ if (onLateValue !== undefined) {
172
+ void providerPromise.then(value => observeLateProviderValue(value, onLateValue), () => undefined);
173
+ }
174
+ throw error;
175
+ }
176
+ let outcome;
177
+ try {
178
+ outcome = boundary === undefined
179
+ ? await settled
180
+ : await Promise.race([settled, boundary.promise]);
181
+ }
182
+ finally {
183
+ boundary?.cancel();
184
+ }
185
+ if (outcome === "timeout" || outcome === "aborted") {
186
+ boundaryTriggered = true;
187
+ if (valueSettled && !lateObserved) {
188
+ lateObserved = true;
189
+ observeLateProviderValue(valueForLate, onLateValue);
190
+ }
191
+ else if (!valueSettled && onLateValue !== undefined) {
192
+ void providerPromise.then(value => {
193
+ if (lateObserved)
194
+ return;
195
+ lateObserved = true;
196
+ observeLateProviderValue(value, onLateValue);
197
+ }, () => undefined);
198
+ }
199
+ throw new ProviderBoundaryError(outcome);
200
+ }
201
+ if (outcome.kind === "error")
202
+ throw outcome.error;
203
+ if (prepared.clockFaulted())
204
+ throw new ArtifactTransferError("operation_clock_invalid", "Operation clock became invalid.");
205
+ return outcome.value;
206
+ }
207
+ /**
208
+ * Transfer one exact operation-owned artifact. This function is safe to
209
+ * invoke repeatedly with the same action identity: completed receipts are
210
+ * replayed without provider access, and concurrent identical calls share one
211
+ * source/commit attempt.
212
+ */
213
+ export async function transferOperationArtifact(options) {
214
+ const prepared = await prepare(options);
215
+ const initialProgress = freshProgress();
216
+ if (isCancelledOrExpired(prepared)) {
217
+ if (prepared.clockFaulted())
218
+ return uncertainResult(initialProgress, "operation_state_corrupt");
219
+ return preIntentAbortResult(prepared, initialProgress);
220
+ }
221
+ const flightKey = `${prepared.operationId}\0${prepared.transferActionId}`;
222
+ const identity = transferIdentity(prepared);
223
+ const existing = inFlight.get(flightKey);
224
+ if (existing !== undefined) {
225
+ if (existing.identity !== identity)
226
+ return conflictResult(initialProgress);
227
+ return existing.promise;
228
+ }
229
+ const promise = runTransfer(prepared);
230
+ inFlight.set(flightKey, { identity, promise });
231
+ try {
232
+ return await promise;
233
+ }
234
+ finally {
235
+ const current = inFlight.get(flightKey);
236
+ if (current?.promise === promise)
237
+ inFlight.delete(flightKey);
238
+ }
239
+ }
240
+ /** Short additive alias for callers that prefer the noun-first operation name. */
241
+ export const transferArtifact = transferOperationArtifact;
242
+ async function runTransfer(prepared) {
243
+ const progress = {
244
+ intentPersistence: "not_attempted",
245
+ receiptPersistence: "not_attempted"
246
+ };
247
+ try {
248
+ let durable;
249
+ try {
250
+ durable = validateDurableState(await prepared.readActionState(prepared.lookup), prepared);
251
+ }
252
+ catch (error) {
253
+ if (error instanceof ArtifactTransferError && error.code === "operation_request_mismatch") {
254
+ return failureResult(error.code, progress);
255
+ }
256
+ // A receipt without its causal intent, malformed records, and unknown
257
+ // journal fields are corruption, not an invitation to retry the source.
258
+ return failureResult("operation_state_corrupt", progress);
259
+ }
260
+ if (durable !== undefined) {
261
+ progress.intentPersistence = "durable";
262
+ if (durable.receipt !== undefined) {
263
+ progress.receiptPersistence = "durable";
264
+ return await reconcileDurableReceipt(prepared, durable.receipt, progress);
265
+ }
266
+ // An intent without an exact receipt is evidence that the source handoff
267
+ // may already have occurred. There is no safe source retry. The local
268
+ // output primitive can only prove a leftover when an exact receipt gives
269
+ // it bytes+hash. Close the durable action with an explicit partial
270
+ // receipt so the parent operation can complete truthfully without ever
271
+ // reopening the provider source.
272
+ if (durable.intent !== undefined) {
273
+ progress.intentAt = durable.intent.intentAt;
274
+ return await persistUncertainReceipt(prepared, "artifact_transfer_partial", progress);
275
+ }
276
+ return failureResult("operation_state_corrupt", progress);
277
+ }
278
+ const expiredBeforeIntent = isCancelledOrExpired(prepared);
279
+ if (prepared.clockFaulted())
280
+ return uncertainResult(progress, "operation_state_corrupt");
281
+ if (expiredBeforeIntent) {
282
+ return preIntentAbortResult(prepared, progress);
283
+ }
284
+ const intent = makeIntent(prepared);
285
+ progress.intentAt = intent.intentAt;
286
+ const intentResolution = await persistIntentAndConverge(prepared, intent);
287
+ progress.intentPersistence = intentResolution.persistence;
288
+ if (intentResolution.blockerCode === "operation_request_mismatch") {
289
+ return failureResult(intentResolution.blockerCode, progress);
290
+ }
291
+ if (intentResolution.persistence === "indeterminate") {
292
+ return uncertainResult(progress, intentResolution.blockerCode ?? "operation_state_corrupt");
293
+ }
294
+ if (intentResolution.durableState?.receipt !== undefined) {
295
+ progress.receiptPersistence = "durable";
296
+ return await reconcileDurableReceipt(prepared, intentResolution.durableState.receipt, progress);
297
+ }
298
+ if (intentResolution.durableState?.intent !== undefined) {
299
+ progress.intentAt = intentResolution.durableState.intent.intentAt;
300
+ }
301
+ if (prepared.clockFaulted())
302
+ return uncertainResult(progress, "operation_state_corrupt");
303
+ const expiredAfterIntent = isCancelledOrExpired(prepared);
304
+ if (prepared.clockFaulted())
305
+ return uncertainResult(progress, "operation_state_corrupt");
306
+ if (expiredAfterIntent) {
307
+ return await persistUncertainReceipt(prepared, cancellationCode(prepared), progress);
308
+ }
309
+ let source;
310
+ try {
311
+ const raw = await awaitProviderOperation(prepared, () => prepared.openSource(sourceRequest(prepared)), PROVIDER_OPEN_TIMEOUT_MS, value => closeArtifactSourceBestEffort(value));
312
+ source = safeSource(raw, prepared.maxBytes);
313
+ }
314
+ catch {
315
+ return await persistUncertainReceipt(prepared, "artifact_transfer_partial", progress);
316
+ }
317
+ let output;
318
+ let outputFailed = false;
319
+ try {
320
+ output = await commitOperationOutput(commitOptions(prepared, source));
321
+ }
322
+ catch {
323
+ outputFailed = true;
324
+ }
325
+ // `artifact-output` may reject the destination during preflight without
326
+ // ever requesting a source chunk. Explicitly close the one-shot provider
327
+ // capability so its file handle and request temp are released on every
328
+ // branch, including that zero-read path.
329
+ const sourceClosed = await closeArtifactSource(source, prepared);
330
+ if (outputFailed || output === undefined) {
331
+ return await persistUncertainReceipt(prepared, "output_commit_indeterminate", progress);
332
+ }
333
+ if (!sourceClosed) {
334
+ return await persistUncertainReceipt(prepared, "artifact_transfer_partial", progress);
335
+ }
336
+ const mapped = mapOutput(prepared, output, progress);
337
+ return await persistResultReceipt(prepared, mapped.receipt, mapped.outcome, progress);
338
+ }
339
+ catch (error) {
340
+ // In particular, a testable/provider clock can fail after intent or after
341
+ // a local output commit. Do not throw, retry, or claim a write was lost;
342
+ // the only truthful result at this boundary is uncertain.
343
+ if (error instanceof ArtifactTransferError && error.code === "operation_request_mismatch") {
344
+ return failureResult(error.code, progress);
345
+ }
346
+ return uncertainResult(progress, "operation_state_corrupt");
347
+ }
348
+ }
349
+ function freshProgress() {
350
+ return {
351
+ intentPersistence: "not_attempted",
352
+ receiptPersistence: "not_attempted"
353
+ };
354
+ }
355
+ async function persistIntentAndConverge(prepared, intent) {
356
+ try {
357
+ await prepared.persistIntent(intent);
358
+ return { persistence: "durable" };
359
+ }
360
+ catch {
361
+ // A journal adapter may have committed the record before reporting an
362
+ // error. Re-read and require the exact intent before calling it durable.
363
+ let afterFailure;
364
+ try {
365
+ afterFailure = validateDurableState(await prepared.readActionState(prepared.lookup), prepared);
366
+ }
367
+ catch (error) {
368
+ return {
369
+ persistence: "indeterminate",
370
+ blockerCode: error instanceof ArtifactTransferError && error.code === "operation_request_mismatch"
371
+ ? "operation_request_mismatch"
372
+ : "operation_state_corrupt"
373
+ };
374
+ }
375
+ if (afterFailure?.intent !== undefined && sameIntent(afterFailure.intent, intent)) {
376
+ return { persistence: "durable", durableState: afterFailure };
377
+ }
378
+ // Undefined state is not proof that the failed write did not commit.
379
+ // Treat every non-converged write as an uncertain boundary.
380
+ return { persistence: "indeterminate", blockerCode: "operation_state_corrupt" };
381
+ }
382
+ }
383
+ async function reconcileDurableReceipt(prepared, receipt, progress) {
384
+ if (receipt.status !== "transferred" || receipt.bytes === undefined || receipt.sha256 === undefined || receipt.outputKey === undefined) {
385
+ return resultFromReceipt(receipt, "uncertain", true, progress);
386
+ }
387
+ // The source callback is intentionally not called. Passing a guard stream
388
+ // lets artifact-output inspect/reconcile an exact final or verified temp;
389
+ // if neither exists, the guard prevents a blind source retry.
390
+ let output;
391
+ try {
392
+ output = await commitOperationOutput(commitOptions(prepared, noSourceRetry(), {
393
+ bytes: receipt.bytes,
394
+ sha256: receipt.sha256
395
+ }));
396
+ }
397
+ catch {
398
+ return resultFromReceipt(receipt, "uncertain", true, progress);
399
+ }
400
+ if (output.status === "reconciled" || output.status === "committed") {
401
+ // Replay must be byte-for-byte receipt stable. The local primitive only
402
+ // proves the persisted receipt's expected output; it must not rewrite its
403
+ // observedAt or any other durable field while replaying.
404
+ return resultFromReceipt(receipt, "satisfied", true, progress);
405
+ }
406
+ if (output.status === "collision")
407
+ return resultFromReceipt(receipt, "not_satisfied", true, progress, "output_collision");
408
+ return resultFromReceipt(receipt, "uncertain", true, progress, "output_commit_indeterminate");
409
+ }
410
+ async function persistUncertainReceipt(prepared, blockerCode, progress) {
411
+ let observedAt;
412
+ try {
413
+ observedAt = preparedTimestamp(prepared, progress.intentAt);
414
+ }
415
+ catch {
416
+ return uncertainResult(progress, "operation_state_corrupt");
417
+ }
418
+ const receipt = {
419
+ schemaVersion: RECEIPT_SCHEMA_VERSION,
420
+ operationId: prepared.operationId,
421
+ requestDigest: prepared.requestDigest,
422
+ targetBindingDigest: prepared.targetBindingDigest,
423
+ assistantTurnId: prepared.assistantTurnId,
424
+ sourceIdentityDigest: prepared.sourceIdentityDigest,
425
+ kind: prepared.kind,
426
+ ordinal: prepared.ordinal,
427
+ transferActionId: prepared.transferActionId,
428
+ destinationIdentityDigest: prepared.destinationIdentityDigest,
429
+ outputKey: prepared.outputKey,
430
+ status: "partial",
431
+ blockerCode,
432
+ observedAt
433
+ };
434
+ return await persistResultReceipt(prepared, receipt, "uncertain", progress);
435
+ }
436
+ function uncertainResult(progress, blockerCode, receipt) {
437
+ return freezeRecord({
438
+ schemaVersion: SCHEMA_VERSION,
439
+ outcome: "uncertain",
440
+ replayed: false,
441
+ intentPersistence: progress.intentPersistence,
442
+ receiptPersistence: progress.receiptPersistence,
443
+ ...(receipt === undefined ? {} : { receipt: freezeRecord({ ...receipt }) }),
444
+ blockerCode
445
+ });
446
+ }
447
+ async function persistResultReceipt(prepared, receipt, outcome, progress) {
448
+ // Durable records are scalar, path-free snapshots. Freeze the exact
449
+ // record before handing it to an adapter or returning it so neither side
450
+ // can mutate the authority after validation.
451
+ receipt = freezeRecord({ ...receipt });
452
+ try {
453
+ await prepared.persistReceipt(receipt);
454
+ progress.receiptPersistence = "durable";
455
+ return resultFromReceipt(receipt, outcome, false, progress);
456
+ }
457
+ catch {
458
+ // As with intent, a receipt adapter can commit and then throw. Only an
459
+ // exact read-back makes the persistence durable; otherwise expose the
460
+ // receipt candidate but keep the write indeterminate and never retry the
461
+ // provider/source.
462
+ let afterFailure;
463
+ try {
464
+ afterFailure = validateDurableState(await prepared.readActionState(prepared.lookup), prepared);
465
+ }
466
+ catch {
467
+ progress.receiptPersistence = "indeterminate";
468
+ return uncertainResult(progress, "operation_state_corrupt", receipt);
469
+ }
470
+ if (afterFailure?.receipt !== undefined && sameReceipt(afterFailure.receipt, receipt)) {
471
+ progress.receiptPersistence = "durable";
472
+ return resultFromReceipt(afterFailure.receipt, outcome, false, progress);
473
+ }
474
+ progress.receiptPersistence = "indeterminate";
475
+ return uncertainResult(progress, "operation_state_corrupt", receipt);
476
+ }
477
+ }
478
+ function mapOutput(prepared, output, progress) {
479
+ const observedAt = preparedTimestamp(prepared, progress.intentAt);
480
+ if (output.status === "committed" || output.status === "reconciled") {
481
+ return {
482
+ outcome: "satisfied",
483
+ receipt: freezeRecord({
484
+ schemaVersion: RECEIPT_SCHEMA_VERSION,
485
+ operationId: prepared.operationId,
486
+ requestDigest: prepared.requestDigest,
487
+ targetBindingDigest: prepared.targetBindingDigest,
488
+ assistantTurnId: prepared.assistantTurnId,
489
+ sourceIdentityDigest: prepared.sourceIdentityDigest,
490
+ kind: prepared.kind,
491
+ ordinal: prepared.ordinal,
492
+ transferActionId: prepared.transferActionId,
493
+ destinationIdentityDigest: prepared.destinationIdentityDigest,
494
+ outputKey: output.outputKey,
495
+ bytes: output.bytes,
496
+ sha256: output.sha256,
497
+ status: "transferred",
498
+ observedAt
499
+ })
500
+ };
501
+ }
502
+ const blockerCode = output.status === "collision"
503
+ ? "output_collision"
504
+ : output.reason === "commit_indeterminate" || output.reason.startsWith("temp_")
505
+ ? "output_commit_indeterminate"
506
+ : output.reason === "destination_invalid"
507
+ ? "output_commit_indeterminate"
508
+ : "artifact_transfer_partial";
509
+ // The output primitive reports a zero/empty digest for preflight and other
510
+ // boundaries where no stream prefix was observed. Do not turn those
511
+ // placeholders into durable transfer facts. Stream-side failures retain
512
+ // only the prefix that the primitive actually hashed.
513
+ const observedPrefix = new Set([
514
+ "source_aborted", "source_invalid", "source_mismatch", "source_read_failed",
515
+ "byte_limit_exceeded", "write_failed", "file_sync_failed"
516
+ ]).has(output.reason)
517
+ ? { bytes: output.bytes, sha256: output.sha256 }
518
+ : {};
519
+ return {
520
+ outcome: output.status === "collision" ? "not_satisfied" : "uncertain",
521
+ receipt: freezeRecord({
522
+ schemaVersion: RECEIPT_SCHEMA_VERSION,
523
+ operationId: prepared.operationId,
524
+ requestDigest: prepared.requestDigest,
525
+ targetBindingDigest: prepared.targetBindingDigest,
526
+ assistantTurnId: prepared.assistantTurnId,
527
+ sourceIdentityDigest: prepared.sourceIdentityDigest,
528
+ kind: prepared.kind,
529
+ ordinal: prepared.ordinal,
530
+ transferActionId: prepared.transferActionId,
531
+ destinationIdentityDigest: prepared.destinationIdentityDigest,
532
+ outputKey: output.outputKey,
533
+ ...observedPrefix,
534
+ status: output.status === "collision" ? "blocked" : "partial",
535
+ blockerCode,
536
+ observedAt
537
+ })
538
+ };
539
+ }
540
+ function resultFromReceipt(receipt, outcome, replayed, progress, overrideBlocker) {
541
+ return freezeRecord({
542
+ schemaVersion: SCHEMA_VERSION,
543
+ outcome,
544
+ replayed,
545
+ intentPersistence: progress.intentPersistence,
546
+ receiptPersistence: progress.receiptPersistence,
547
+ receipt,
548
+ ...(overrideBlocker === undefined && receipt.blockerCode === undefined ? {} : {
549
+ blockerCode: overrideBlocker ?? receipt.blockerCode
550
+ })
551
+ });
552
+ }
553
+ function failureResult(blockerCode, progress) {
554
+ return freezeRecord({
555
+ schemaVersion: SCHEMA_VERSION,
556
+ outcome: "not_satisfied",
557
+ replayed: false,
558
+ intentPersistence: progress.intentPersistence,
559
+ receiptPersistence: progress.receiptPersistence,
560
+ blockerCode
561
+ });
562
+ }
563
+ function conflictResult(progress) {
564
+ return failureResult("operation_request_mismatch", progress);
565
+ }
566
+ function preIntentAbortResult(prepared, progress) {
567
+ return failureResult(cancellationCode(prepared), progress);
568
+ }
569
+ function cancellationCode(prepared) {
570
+ return isSignalAborted(prepared.signal) ? "operation_cancelled" : "operation_timeout";
571
+ }
572
+ function makeIntent(prepared) {
573
+ return freezeRecord({
574
+ schemaVersion: INTENT_SCHEMA_VERSION,
575
+ operationId: prepared.operationId,
576
+ requestDigest: prepared.requestDigest,
577
+ targetBindingDigest: prepared.targetBindingDigest,
578
+ assistantTurnId: prepared.assistantTurnId,
579
+ sourceIdentityDigest: prepared.sourceIdentityDigest,
580
+ kind: prepared.kind,
581
+ ordinal: prepared.ordinal,
582
+ transferActionId: prepared.transferActionId,
583
+ destinationIdentityDigest: prepared.destinationIdentityDigest,
584
+ actionKind: "local_output_commit",
585
+ repeatPolicy: "reconcile_local_effect",
586
+ intentAt: preparedTimestamp(prepared)
587
+ });
588
+ }
589
+ function sourceRequest(prepared) {
590
+ return freezeRecord({
591
+ operationId: prepared.operationId,
592
+ requestDigest: prepared.requestDigest,
593
+ targetBindingDigest: prepared.targetBindingDigest,
594
+ assistantTurnId: prepared.assistantTurnId,
595
+ sourceIdentityDigest: prepared.sourceIdentityDigest,
596
+ kind: prepared.kind,
597
+ ordinal: prepared.ordinal,
598
+ transferActionId: prepared.transferActionId,
599
+ destinationIdentityDigest: prepared.destinationIdentityDigest
600
+ });
601
+ }
602
+ function commitOptions(prepared, source, expected) {
603
+ return {
604
+ operationId: prepared.operationId,
605
+ artifactIdentity: artifactIdentity(prepared),
606
+ outputDirectory: prepared.outputDirectory,
607
+ source,
608
+ maxBytes: prepared.maxBytes,
609
+ ...(expected === undefined ? {} : { expected }),
610
+ ...(prepared.signal === undefined ? {} : { signal: prepared.signal }),
611
+ ...(prepared.deadlineAt === undefined ? {} : { deadlineAt: prepared.deadlineAt }),
612
+ // Artifact-output owns the local effect deadline, but it must use this
613
+ // operation's guarded clock. A backwards/invalid sample is not allowed
614
+ // to become a fresh filesystem timestamp or deadline decision.
615
+ now: () => {
616
+ const value = prepared.now();
617
+ if (prepared.clockFaulted())
618
+ throw new ArtifactTransferError("operation_clock_invalid", "Operation clock became invalid.");
619
+ return value;
620
+ },
621
+ ...(prepared.extensionHint === undefined ? {} : { extensionHint: prepared.extensionHint }),
622
+ ...(prepared.mimeTypeHint === undefined ? {} : { mimeTypeHint: prepared.mimeTypeHint })
623
+ };
624
+ }
625
+ function artifactIdentity(prepared) {
626
+ // Fixed property order is intentional: this is an opaque local key input,
627
+ // not a durable/public authority. It contains no destination path.
628
+ return JSON.stringify({
629
+ assistantTurnId: prepared.assistantTurnId,
630
+ sourceIdentityDigest: prepared.sourceIdentityDigest,
631
+ kind: prepared.kind,
632
+ ordinal: prepared.ordinal,
633
+ transferActionId: prepared.transferActionId,
634
+ destinationIdentityDigest: prepared.destinationIdentityDigest,
635
+ maxBytes: prepared.maxBytes
636
+ });
637
+ }
638
+ function transferIdentity(prepared) {
639
+ return [
640
+ prepared.operationId,
641
+ prepared.requestDigest,
642
+ prepared.targetBindingDigest,
643
+ prepared.assistantTurnId,
644
+ prepared.sourceIdentityDigest,
645
+ prepared.kind,
646
+ String(prepared.ordinal),
647
+ prepared.transferActionId,
648
+ prepared.destinationIdentityDigest,
649
+ String(prepared.maxBytes),
650
+ prepared.outputKey
651
+ ].join("\0");
652
+ }
653
+ async function prepare(options) {
654
+ const record = snapshotRecord(options, "options");
655
+ assertAllowedKeys(record, [
656
+ "operationId", "requestDigest", "targetBindingDigest", "assistantTurnId", "sourceIdentityDigest",
657
+ "kind", "ordinal", "transferActionId", "outputDirectory", "evidenceDigest", "openSource", "journal",
658
+ "signal", "deadlineAt", "now", "limits", "extensionHint", "mimeTypeHint"
659
+ ]);
660
+ const operationId = requiredString(record, "operationId", UUID_PATTERN, 128);
661
+ const requestDigest = requiredString(record, "requestDigest", DIGEST_PATTERN, 128);
662
+ const targetBindingDigest = requiredString(record, "targetBindingDigest", DIGEST_PATTERN, 128);
663
+ const assistantTurnId = requiredOpaqueId(record, "assistantTurnId");
664
+ const sourceIdentityDigest = requiredString(record, "sourceIdentityDigest", DIGEST_PATTERN, 128);
665
+ const kind = requiredKind(record.kind);
666
+ const limits = readLimits(record.limits);
667
+ // The caller-provided depth bound is meaningful only if applied to the
668
+ // complete option graph. Validate it after reading the scalar limits so a
669
+ // deeply nested/proxy-backed callback record cannot bypass the advertised
670
+ // bound.
671
+ assertGraphBounds(record, new Set(), 0, limits.maxDepth);
672
+ const ordinal = requiredOrdinal(record.ordinal, limits.maxCount);
673
+ const transferActionId = requiredString(record, "transferActionId", UUID_PATTERN, 128);
674
+ const outputDirectory = requiredString(record, "outputDirectory", undefined, limits.maxStringBytes);
675
+ if (!isAbsolutePath(outputDirectory))
676
+ throw invalidOptions();
677
+ const canonicalDestination = resolve(outputDirectory);
678
+ if (byteLength(canonicalDestination) > limits.maxStringBytes)
679
+ throw invalidOptions();
680
+ const evidenceDigest = requiredFunction(record, "evidenceDigest");
681
+ const openSource = requiredFunction(record, "openSource");
682
+ const journal = readJournal(record.journal);
683
+ const signal = readOptionalSignal(record.signal);
684
+ const deadlineAt = readOptionalDeadline(record.deadlineAt);
685
+ const clock = readNow(record.now);
686
+ if (deadlineAt !== undefined && deadlineAt > MAX_DEADLINE_AT)
687
+ throw invalidOptions();
688
+ if (limits.maxDepth < 2)
689
+ throw invalidOptions();
690
+ const extensionHint = readOptionalString(record, "extensionHint", limits.maxStringBytes);
691
+ const mimeTypeHint = readOptionalString(record, "mimeTypeHint", limits.maxStringBytes);
692
+ let destinationIdentityDigest;
693
+ try {
694
+ destinationIdentityDigest = await evidenceDigest("artifact-destination", freezeRecord({
695
+ schemaVersion: SCHEMA_VERSION,
696
+ operationId,
697
+ requestDigest,
698
+ targetBindingDigest,
699
+ assistantTurnId,
700
+ sourceIdentityDigest,
701
+ kind,
702
+ ordinal,
703
+ transferActionId,
704
+ canonicalDestination
705
+ }));
706
+ }
707
+ catch {
708
+ throw new ArtifactTransferError("invalid_evidence_digest", "Destination evidence could not be derived.");
709
+ }
710
+ if (!DIGEST_PATTERN.test(destinationIdentityDigest))
711
+ throw invalidOptions();
712
+ let outputKey;
713
+ try {
714
+ outputKey = deriveOperationOutputKey({
715
+ operationId,
716
+ artifactIdentity: JSON.stringify({ assistantTurnId, sourceIdentityDigest, kind, ordinal, transferActionId, destinationIdentityDigest, maxBytes: limits.maxBytes }),
717
+ ...(extensionHint === undefined ? {} : { extensionHint }),
718
+ ...(mimeTypeHint === undefined ? {} : { mimeTypeHint })
719
+ });
720
+ }
721
+ catch {
722
+ throw invalidOptions();
723
+ }
724
+ return {
725
+ operationId,
726
+ requestDigest,
727
+ targetBindingDigest,
728
+ assistantTurnId,
729
+ sourceIdentityDigest,
730
+ kind,
731
+ ordinal,
732
+ transferActionId,
733
+ outputDirectory: canonicalDestination,
734
+ destinationIdentityDigest,
735
+ outputKey,
736
+ ...(extensionHint === undefined ? {} : { extensionHint }),
737
+ ...(mimeTypeHint === undefined ? {} : { mimeTypeHint }),
738
+ maxBytes: limits.maxBytes,
739
+ maxCount: limits.maxCount,
740
+ maxDepth: limits.maxDepth,
741
+ ...(signal === undefined ? {} : { signal }),
742
+ ...(deadlineAt === undefined ? {} : { deadlineAt }),
743
+ now: clock.now,
744
+ clockFaulted: clock.faulted,
745
+ openSource,
746
+ readActionState: journal.readActionState,
747
+ persistIntent: journal.persistIntent,
748
+ persistReceipt: journal.persistReceipt,
749
+ lookup: freezeRecord({
750
+ operationId,
751
+ requestDigest,
752
+ targetBindingDigest,
753
+ assistantTurnId,
754
+ sourceIdentityDigest,
755
+ kind,
756
+ ordinal,
757
+ transferActionId,
758
+ destinationIdentityDigest
759
+ })
760
+ };
761
+ }
762
+ function readLimits(value) {
763
+ if (value === undefined)
764
+ return { maxBytes: DEFAULT_MAX_BYTES, maxStringBytes: DEFAULT_MAX_STRING_BYTES, maxCount: DEFAULT_MAX_COUNT, maxDepth: DEFAULT_MAX_DEPTH };
765
+ const record = snapshotRecord(value, "limits");
766
+ assertAllowedKeys(record, ["maxBytes", "maxStringBytes", "maxCount", "maxDepth"]);
767
+ const maxBytes = optionalBoundedNumber(record, "maxBytes", DEFAULT_MAX_BYTES, 0, MAX_MAX_BYTES);
768
+ const maxStringBytes = optionalBoundedNumber(record, "maxStringBytes", DEFAULT_MAX_STRING_BYTES, 32, MAX_MAX_STRING_BYTES);
769
+ const maxCount = optionalBoundedNumber(record, "maxCount", DEFAULT_MAX_COUNT, 1, MAX_MAX_COUNT);
770
+ const maxDepth = optionalBoundedNumber(record, "maxDepth", DEFAULT_MAX_DEPTH, 2, MAX_MAX_DEPTH);
771
+ return { maxBytes, maxStringBytes, maxCount, maxDepth };
772
+ }
773
+ function readJournal(value) {
774
+ const record = snapshotRecord(value, "journal");
775
+ assertAllowedKeys(record, ["readActionState", "persistIntent", "persistReceipt"]);
776
+ const readActionState = requiredFunction(record, "readActionState");
777
+ const persistIntent = requiredFunction(record, "persistIntent");
778
+ const persistReceipt = requiredFunction(record, "persistReceipt");
779
+ return {
780
+ readActionState: (lookup) => Reflect.apply(readActionState, value, [freezeRecord({ ...lookup })]),
781
+ persistIntent: (intent) => Reflect.apply(persistIntent, value, [freezeRecord({ ...intent })]),
782
+ persistReceipt: (receipt) => Reflect.apply(persistReceipt, value, [freezeRecord({ ...receipt })])
783
+ };
784
+ }
785
+ function validateDurableState(value, prepared) {
786
+ if (value === undefined)
787
+ return undefined;
788
+ const record = snapshotRecord(value, "durable state", prepared.maxDepth);
789
+ const allowed = new Set(["intent", "receipt"]);
790
+ for (const key of Object.keys(record))
791
+ if (!allowed.has(key))
792
+ throw invalidOptions();
793
+ const intentValue = record.intent;
794
+ const receiptValue = record.receipt;
795
+ if (intentValue === undefined && receiptValue === undefined)
796
+ throw invalidOptions();
797
+ const intent = intentValue === undefined ? undefined : validateIntent(intentValue, prepared);
798
+ const receipt = receiptValue === undefined ? undefined : validateReceipt(receiptValue, prepared);
799
+ // A receipt records completion of an intent-bound transfer. Accepting a
800
+ // receipt without its causal intent would make an untrusted/partial journal
801
+ // record look replayable and would erase the source handoff boundary.
802
+ if (receipt !== undefined && intent === undefined)
803
+ throw invalidOptions();
804
+ if (intent !== undefined && receipt !== undefined && !sameIdentity(intent, receipt))
805
+ throw invalidOptions();
806
+ if (intent !== undefined && receipt !== undefined && Date.parse(receipt.observedAt) < Date.parse(intent.intentAt)) {
807
+ // A durable observation predating its causal intent is evidence of a
808
+ // backwards/untrusted clock (or journal corruption), never a replayable
809
+ // completion.
810
+ throw invalidOptions();
811
+ }
812
+ return freezeRecord({
813
+ ...(intent === undefined ? {} : { intent }),
814
+ ...(receipt === undefined ? {} : { receipt })
815
+ });
816
+ }
817
+ function validateIntent(value, prepared) {
818
+ const record = snapshotExactRecord(value, [
819
+ "schemaVersion", "operationId", "requestDigest", "targetBindingDigest", "assistantTurnId",
820
+ "sourceIdentityDigest", "kind", "ordinal", "transferActionId", "destinationIdentityDigest",
821
+ "actionKind", "repeatPolicy", "intentAt"
822
+ ], prepared.maxDepth);
823
+ const intent = record;
824
+ if (intent.schemaVersion !== INTENT_SCHEMA_VERSION || intent.actionKind !== "local_output_commit" || intent.repeatPolicy !== "reconcile_local_effect")
825
+ throw invalidOptions();
826
+ validateIdentityFields(intent, prepared);
827
+ if (!isTimestamp(intent.intentAt))
828
+ throw invalidOptions();
829
+ return freezeRecord({ ...intent });
830
+ }
831
+ function validateReceipt(value, prepared) {
832
+ const record = snapshotRecord(value, "receipt", prepared.maxDepth);
833
+ const allowed = new Set([
834
+ "schemaVersion", "operationId", "requestDigest", "targetBindingDigest", "assistantTurnId",
835
+ "sourceIdentityDigest", "kind", "ordinal", "transferActionId", "destinationIdentityDigest",
836
+ "outputKey", "bytes", "sha256", "status", "blockerCode", "observedAt"
837
+ ]);
838
+ for (const key of Object.keys(record))
839
+ if (!allowed.has(key))
840
+ throw invalidOptions();
841
+ const receipt = record;
842
+ if (receipt.schemaVersion !== RECEIPT_SCHEMA_VERSION || !isArtifactStatus(receipt.status) || !isTimestamp(receipt.observedAt))
843
+ throw invalidOptions();
844
+ validateIdentityFields(receipt, prepared);
845
+ if (receipt.outputKey !== undefined && !OUTPUT_KEY_PATTERN.test(receipt.outputKey))
846
+ throw invalidOptions();
847
+ if (receipt.outputKey !== undefined && receipt.outputKey !== prepared.outputKey)
848
+ throw new ArtifactTransferError("operation_request_mismatch", "Durable artifact output identity does not match the request.");
849
+ if (receipt.bytes !== undefined && (!Number.isSafeInteger(receipt.bytes) || receipt.bytes < 0 || receipt.bytes > prepared.maxBytes))
850
+ throw invalidOptions();
851
+ if (receipt.sha256 !== undefined && !SHA256_PATTERN.test(receipt.sha256))
852
+ throw invalidOptions();
853
+ if (receipt.blockerCode !== undefined && !CODE_PATTERN.test(receipt.blockerCode))
854
+ throw invalidOptions();
855
+ if (receipt.status === "transferred" && (receipt.outputKey === undefined || receipt.bytes === undefined || receipt.sha256 === undefined || receipt.blockerCode !== undefined))
856
+ throw invalidOptions();
857
+ if (receipt.status !== "transferred" && receipt.blockerCode === undefined)
858
+ throw invalidOptions();
859
+ return freezeRecord({ ...receipt });
860
+ }
861
+ function validateIdentityFields(value, prepared) {
862
+ if (value.operationId !== prepared.operationId
863
+ || value.requestDigest !== prepared.requestDigest
864
+ || value.targetBindingDigest !== prepared.targetBindingDigest
865
+ || value.assistantTurnId !== prepared.assistantTurnId
866
+ || value.sourceIdentityDigest !== prepared.sourceIdentityDigest
867
+ || value.kind !== prepared.kind
868
+ || value.ordinal !== prepared.ordinal
869
+ || value.transferActionId !== prepared.transferActionId
870
+ || value.destinationIdentityDigest !== prepared.destinationIdentityDigest)
871
+ throw new ArtifactTransferError("operation_request_mismatch", "Durable artifact transfer identity does not match the request.");
872
+ }
873
+ function sameIdentity(left, right) {
874
+ return sameTransferIdentity(left, right);
875
+ }
876
+ function sameTransferIdentity(left, right) {
877
+ return left.operationId === right.operationId
878
+ && left.requestDigest === right.requestDigest
879
+ && left.targetBindingDigest === right.targetBindingDigest
880
+ && left.assistantTurnId === right.assistantTurnId
881
+ && left.sourceIdentityDigest === right.sourceIdentityDigest
882
+ && left.kind === right.kind
883
+ && left.ordinal === right.ordinal
884
+ && left.transferActionId === right.transferActionId
885
+ && left.destinationIdentityDigest === right.destinationIdentityDigest;
886
+ }
887
+ function sameIntent(left, right) {
888
+ return left.schemaVersion === right.schemaVersion
889
+ && left.actionKind === right.actionKind
890
+ && left.repeatPolicy === right.repeatPolicy
891
+ && left.intentAt === right.intentAt
892
+ && sameTransferIdentity(left, right);
893
+ }
894
+ function sameReceipt(left, right) {
895
+ return left.schemaVersion === right.schemaVersion
896
+ && left.outputKey === right.outputKey
897
+ && left.bytes === right.bytes
898
+ && left.sha256 === right.sha256
899
+ && left.status === right.status
900
+ && left.blockerCode === right.blockerCode
901
+ && left.observedAt === right.observedAt
902
+ && sameTransferIdentity(left, right);
903
+ }
904
+ async function closeArtifactSource(source, prepared) {
905
+ try {
906
+ const iteratorMethod = dataMethod(source, Symbol.asyncIterator);
907
+ if (typeof iteratorMethod !== "function")
908
+ return false;
909
+ const iterator = Reflect.apply(iteratorMethod, source, []);
910
+ const closeMethod = dataMethod(iterator, "return");
911
+ if (typeof closeMethod !== "function")
912
+ return false;
913
+ await awaitProviderOperation(prepared, () => Reflect.apply(closeMethod, iterator, []), PROVIDER_CLEANUP_TIMEOUT_MS);
914
+ return true;
915
+ }
916
+ catch {
917
+ return false;
918
+ }
919
+ }
920
+ /**
921
+ * A late source capability arrived after the durable handoff boundary. Close
922
+ * it opportunistically, but never await it or let a malformed provider result
923
+ * escape as an unhandled rejection. The transfer remains uncertain and is
924
+ * never reopened based on this cleanup attempt.
925
+ */
926
+ function closeArtifactSourceBestEffort(source) {
927
+ try {
928
+ const iteratorMethod = dataMethod(source, Symbol.asyncIterator);
929
+ if (typeof iteratorMethod !== "function")
930
+ return;
931
+ const iterator = Reflect.apply(iteratorMethod, source, []);
932
+ const closeMethod = dataMethod(iterator, "return");
933
+ if (typeof closeMethod !== "function")
934
+ return;
935
+ const result = Reflect.apply(closeMethod, iterator, []);
936
+ void Promise.resolve(result).then(() => undefined, () => undefined);
937
+ }
938
+ catch {
939
+ // The capability is already outside the request authority. There is no
940
+ // safe retry or durable fact to derive from a cleanup failure.
941
+ }
942
+ }
943
+ function safeSource(value, maxBytes) {
944
+ const iteratorMethod = dataMethod(value, Symbol.asyncIterator);
945
+ if (typeof iteratorMethod !== "function")
946
+ throw invalidOptions();
947
+ let iterator;
948
+ try {
949
+ iterator = Reflect.apply(iteratorMethod, value, []);
950
+ }
951
+ catch {
952
+ throw invalidOptions();
953
+ }
954
+ const nextMethod = dataMethod(iterator, "next");
955
+ if (typeof nextMethod !== "function")
956
+ throw invalidOptions();
957
+ const closeMethod = dataMethod(iterator, "return");
958
+ let closed = false;
959
+ const copyChunk = (value) => {
960
+ try {
961
+ // Make the copy before returning from the awaited `next` call. The
962
+ // provider retains ownership of the original view and may mutate it as
963
+ // soon as this boundary resolves. The internal marker lets the output
964
+ // sink consume this defensive copy without allocating a second one.
965
+ return copyProviderChunk(value, maxBytes);
966
+ }
967
+ catch {
968
+ throw new ArtifactTransferError("source_invalid", "Artifact source chunk is invalid.");
969
+ }
970
+ };
971
+ const close = async (value) => {
972
+ if (closed)
973
+ return { done: true, value: undefined };
974
+ closed = true;
975
+ if (typeof closeMethod !== "function")
976
+ return { done: true, value: undefined };
977
+ let raw;
978
+ try {
979
+ raw = await Reflect.apply(closeMethod, iterator, value === undefined ? [] : [value]);
980
+ }
981
+ catch {
982
+ throw new ArtifactTransferError("source_close_failed", "Artifact source cleanup failed.");
983
+ }
984
+ if (raw === undefined)
985
+ return { done: true, value: undefined };
986
+ try {
987
+ const result = snapshotExactRecord(raw, ["done", "value"]);
988
+ if (typeof result.done !== "boolean")
989
+ throw new Error("invalid done");
990
+ return result.done
991
+ ? { done: true, value: undefined }
992
+ : { done: false, value: copyChunk(result.value) };
993
+ }
994
+ catch {
995
+ throw new ArtifactTransferError("source_close_failed", "Artifact source cleanup failed.");
996
+ }
997
+ };
998
+ const safeIterator = {
999
+ next: async () => {
1000
+ if (closed)
1001
+ return { done: true, value: undefined };
1002
+ let raw;
1003
+ try {
1004
+ raw = await Reflect.apply(nextMethod, iterator, []);
1005
+ }
1006
+ catch {
1007
+ try {
1008
+ await close();
1009
+ }
1010
+ catch { /* preserve the primary source error */ }
1011
+ throw new ArtifactTransferError("source_read_failed", "Artifact source could not be read.");
1012
+ }
1013
+ try {
1014
+ const result = snapshotExactRecord(raw, ["done", "value"]);
1015
+ if (typeof result.done !== "boolean")
1016
+ throw new Error("invalid done");
1017
+ if (result.done) {
1018
+ closed = true;
1019
+ return { done: true, value: undefined };
1020
+ }
1021
+ return { done: false, value: copyChunk(result.value) };
1022
+ }
1023
+ catch (error) {
1024
+ try {
1025
+ await close();
1026
+ }
1027
+ catch { /* preserve the primary source error */ }
1028
+ if (error instanceof ArtifactTransferError && error.code === "source_invalid")
1029
+ throw error;
1030
+ throw new ArtifactTransferError("source_invalid", "Artifact source result is invalid.");
1031
+ }
1032
+ },
1033
+ return: close
1034
+ };
1035
+ Object.freeze(safeIterator);
1036
+ return Object.freeze({ [Symbol.asyncIterator]: () => safeIterator });
1037
+ }
1038
+ function noSourceRetry() {
1039
+ const iterator = {
1040
+ next: async () => {
1041
+ throw new ArtifactTransferError("source_retry_forbidden", "Provider source retry is forbidden after durable intent.");
1042
+ },
1043
+ return: async () => ({ done: true, value: undefined })
1044
+ };
1045
+ Object.freeze(iterator);
1046
+ return Object.freeze({
1047
+ [Symbol.asyncIterator]: () => iterator
1048
+ });
1049
+ }
1050
+ function dataMethod(value, key) {
1051
+ if (value === null || (typeof value !== "object" && typeof value !== "function"))
1052
+ return undefined;
1053
+ let current = value;
1054
+ try {
1055
+ for (let depth = 0; current !== null && depth < 16; depth += 1) {
1056
+ const descriptor = Object.getOwnPropertyDescriptor(current, key);
1057
+ if (descriptor !== undefined)
1058
+ return "value" in descriptor ? descriptor.value : undefined;
1059
+ current = Object.getPrototypeOf(current);
1060
+ }
1061
+ }
1062
+ catch {
1063
+ return undefined;
1064
+ }
1065
+ return undefined;
1066
+ }
1067
+ function snapshotRecord(value, label, maxDepth = MAX_MAX_DEPTH) {
1068
+ if (value === null || typeof value !== "object" || Array.isArray(value))
1069
+ throw invalidOptions();
1070
+ try {
1071
+ const prototype = Object.getPrototypeOf(value);
1072
+ if (prototype !== Object.prototype && prototype !== null)
1073
+ throw invalidOptions();
1074
+ if (Object.getOwnPropertySymbols(value).length !== 0)
1075
+ throw invalidOptions();
1076
+ const descriptors = Object.getOwnPropertyDescriptors(value);
1077
+ const result = {};
1078
+ for (const [key, descriptor] of Object.entries(descriptors)) {
1079
+ if (!("value" in descriptor) || descriptor.get !== undefined || descriptor.set !== undefined)
1080
+ throw invalidOptions();
1081
+ result[key] = descriptor.value;
1082
+ }
1083
+ assertGraphBounds(value, new Set(), 0, maxDepth);
1084
+ void label;
1085
+ return result;
1086
+ }
1087
+ catch (error) {
1088
+ if (error instanceof ArtifactTransferError)
1089
+ throw error;
1090
+ throw invalidOptions();
1091
+ }
1092
+ }
1093
+ function snapshotExactRecord(value, keys, maxDepth = MAX_MAX_DEPTH) {
1094
+ const record = snapshotRecord(value, "record", maxDepth);
1095
+ const expected = new Set(keys);
1096
+ if (Object.keys(record).length !== expected.size || Object.keys(record).some(key => !expected.has(key)))
1097
+ throw invalidOptions();
1098
+ return record;
1099
+ }
1100
+ /**
1101
+ * Walk only own data properties, never invoking accessors or traversing
1102
+ * prototypes. This bounds the option/journal/record graph while retaining
1103
+ * callback and AbortSignal objects as opaque capabilities once their own
1104
+ * descriptors have been inspected.
1105
+ */
1106
+ function assertGraphBounds(value, seen, depth, maxDepth) {
1107
+ if (value === null || typeof value !== "object" || typeof value === "function")
1108
+ return;
1109
+ // Byte views are validated/copied at their own boundary; enumerating every
1110
+ // byte as a graph node would reject otherwise bounded chunks.
1111
+ if (ArrayBuffer.isView(value))
1112
+ return;
1113
+ if (seen.has(value))
1114
+ return;
1115
+ if (depth > maxDepth || seen.size >= MAX_GRAPH_NODES)
1116
+ throw invalidOptions();
1117
+ seen.add(value);
1118
+ let descriptors;
1119
+ try {
1120
+ descriptors = Object.getOwnPropertyDescriptors(value);
1121
+ }
1122
+ catch {
1123
+ throw invalidOptions();
1124
+ }
1125
+ for (const descriptor of Object.values(descriptors)) {
1126
+ if (!("value" in descriptor) || descriptor.get !== undefined || descriptor.set !== undefined)
1127
+ throw invalidOptions();
1128
+ assertGraphBounds(descriptor.value, seen, depth + 1, maxDepth);
1129
+ }
1130
+ }
1131
+ function assertAllowedKeys(record, keys) {
1132
+ const allowed = new Set(keys);
1133
+ if (Object.keys(record).some(key => !allowed.has(key)))
1134
+ throw invalidOptions();
1135
+ }
1136
+ function requiredString(record, key, pattern, maxBytes) {
1137
+ const value = record[key];
1138
+ if (typeof value !== "string" || value.length === 0 || byteLength(value) > maxBytes || /[\u0000-\u001f\u007f]/u.test(value) || (pattern !== undefined && !pattern.test(value)))
1139
+ throw invalidOptions();
1140
+ return value;
1141
+ }
1142
+ function requiredOpaqueId(record, key) {
1143
+ const value = record[key];
1144
+ if (typeof value !== "string" || value.trim().length === 0 || value.length > 512 || /[\u0000-\u001f\u007f]/u.test(value))
1145
+ throw invalidOptions();
1146
+ return value;
1147
+ }
1148
+ function requiredKind(value) {
1149
+ if (value !== "file" && value !== "image" && value !== "other")
1150
+ throw invalidOptions();
1151
+ return value;
1152
+ }
1153
+ function requiredOrdinal(value, maxCount) {
1154
+ if (!Number.isSafeInteger(value) || value < 0 || value >= maxCount)
1155
+ throw invalidOptions();
1156
+ return value;
1157
+ }
1158
+ function requiredFunction(record, key) {
1159
+ const value = record[key];
1160
+ if (typeof value !== "function")
1161
+ throw invalidOptions();
1162
+ return value;
1163
+ }
1164
+ function readOptionalString(record, key, maxBytes) {
1165
+ const value = record[key];
1166
+ if (value === undefined)
1167
+ return undefined;
1168
+ if (typeof value !== "string" || byteLength(value) > maxBytes || /[\u0000-\u001f\u007f]/u.test(value))
1169
+ throw invalidOptions();
1170
+ return value;
1171
+ }
1172
+ function optionalBoundedNumber(record, key, fallback, minimum, maximum) {
1173
+ const value = record[key];
1174
+ if (value === undefined)
1175
+ return fallback;
1176
+ if (!Number.isSafeInteger(value) || value < minimum || value > maximum)
1177
+ throw invalidOptions();
1178
+ return value;
1179
+ }
1180
+ function readOptionalSignal(value) {
1181
+ if (value === undefined)
1182
+ return undefined;
1183
+ if (typeof AbortSignal !== "function" || value === null || typeof value !== "object")
1184
+ throw invalidOptions();
1185
+ try {
1186
+ if (!(value instanceof AbortSignal))
1187
+ throw invalidOptions();
1188
+ const own = Object.getOwnPropertyDescriptor(value, "aborted");
1189
+ if (own !== undefined)
1190
+ throw invalidOptions();
1191
+ const descriptor = Object.getOwnPropertyDescriptor(AbortSignal.prototype, "aborted");
1192
+ if (descriptor?.get === undefined || typeof descriptor.get.call(value) !== "boolean")
1193
+ throw invalidOptions();
1194
+ }
1195
+ catch (error) {
1196
+ if (error instanceof ArtifactTransferError)
1197
+ throw error;
1198
+ throw invalidOptions();
1199
+ }
1200
+ return value;
1201
+ }
1202
+ function readOptionalDeadline(value) {
1203
+ if (value === undefined)
1204
+ return undefined;
1205
+ if (!Number.isSafeInteger(value) || value < 0 || value > MAX_DEADLINE_AT)
1206
+ throw invalidOptions();
1207
+ return value;
1208
+ }
1209
+ function readNow(value) {
1210
+ if (value !== undefined && typeof value !== "function")
1211
+ throw invalidOptions();
1212
+ const source = value === undefined ? Date.now : value;
1213
+ let sample;
1214
+ try {
1215
+ sample = source();
1216
+ if (!Number.isSafeInteger(sample) || sample < 0 || sample > MAX_DEADLINE_AT)
1217
+ throw invalidOptions();
1218
+ }
1219
+ catch (error) {
1220
+ if (error instanceof ArtifactTransferError)
1221
+ throw error;
1222
+ throw invalidOptions();
1223
+ }
1224
+ let faulted = false;
1225
+ let lastValid = sample;
1226
+ const now = () => {
1227
+ if (faulted)
1228
+ return lastValid;
1229
+ try {
1230
+ const next = source();
1231
+ if (!Number.isSafeInteger(next) || next < 0 || next > MAX_DEADLINE_AT)
1232
+ throw new Error("invalid clock value");
1233
+ if (next < lastValid)
1234
+ throw new Error("clock moved backwards");
1235
+ lastValid = next;
1236
+ return next;
1237
+ }
1238
+ catch {
1239
+ // Retain the last valid sample so error handling can remain total and
1240
+ // deterministic. Callers inspect faulted() and return uncertainty
1241
+ // before using this value for a new durable fact.
1242
+ faulted = true;
1243
+ return lastValid;
1244
+ }
1245
+ };
1246
+ return { now, faulted: () => faulted };
1247
+ }
1248
+ function isCancelledOrExpired(prepared) {
1249
+ if (isSignalAborted(prepared.signal))
1250
+ return true;
1251
+ let current;
1252
+ try {
1253
+ current = prepared.now();
1254
+ }
1255
+ catch {
1256
+ return true;
1257
+ }
1258
+ if (prepared.clockFaulted())
1259
+ return true;
1260
+ return prepared.deadlineAt !== undefined && current >= prepared.deadlineAt;
1261
+ }
1262
+ function isSignalAborted(signal) {
1263
+ if (signal === undefined)
1264
+ return false;
1265
+ try {
1266
+ const descriptor = Object.getOwnPropertyDescriptor(AbortSignal.prototype, "aborted");
1267
+ return descriptor?.get?.call(signal) === true;
1268
+ }
1269
+ catch {
1270
+ return true;
1271
+ }
1272
+ }
1273
+ function timestamp(value) {
1274
+ if (!Number.isSafeInteger(value) || value < 0 || value > MAX_DEADLINE_AT)
1275
+ throw invalidOptions();
1276
+ const instant = new Date(value).toISOString();
1277
+ if (!INSTANT_PATTERN.test(instant))
1278
+ throw invalidOptions();
1279
+ return instant;
1280
+ }
1281
+ function preparedTimestamp(prepared, notBefore) {
1282
+ const value = prepared.now();
1283
+ if (prepared.clockFaulted()) {
1284
+ throw new ArtifactTransferError("operation_clock_invalid", "Operation clock became invalid.");
1285
+ }
1286
+ const instant = timestamp(value);
1287
+ if (notBefore !== undefined && (!isTimestamp(notBefore) || Date.parse(instant) < Date.parse(notBefore))) {
1288
+ throw new ArtifactTransferError("operation_clock_invalid", "Operation clock moved before the durable intent.");
1289
+ }
1290
+ return instant;
1291
+ }
1292
+ function isTimestamp(value) {
1293
+ if (typeof value !== "string" || !INSTANT_PATTERN.test(value))
1294
+ return false;
1295
+ const parsed = Date.parse(value);
1296
+ return Number.isFinite(parsed) && new Date(parsed).toISOString() === value;
1297
+ }
1298
+ function isArtifactStatus(value) {
1299
+ return value === "transferred" || value === "partial" || value === "blocked";
1300
+ }
1301
+ function isAbsolutePath(value) {
1302
+ return resolve(value) === value || value.startsWith("/");
1303
+ }
1304
+ function byteLength(value) {
1305
+ return Buffer.byteLength(value, "utf8");
1306
+ }
1307
+ function invalidOptions() {
1308
+ return new ArtifactTransferError("invalid_options", "Artifact transfer options are invalid.");
1309
+ }
1310
+ function freezeRecord(value) {
1311
+ return Object.freeze(value);
1312
+ }