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
@@ -1,39 +1,161 @@
1
1
  import { copyFile, mkdir, stat } from "node:fs/promises";
2
2
  import { basename, join, resolve } from "node:path";
3
- import { localGuardTimeout, withTimeout } from "../commands/timeouts.js";
3
+ import { ChatGPTControlError } from "../errors.js";
4
+ import { createDeadline, remainingMs } from "../commands/deadline.js";
5
+ import { withTimeout } from "../commands/timeouts.js";
6
+ import { coordinatedEventRegistrationBarrier, withCoordinatedMutationGuard } from "../runtime/coordinated-page.js";
7
+ export class DownloadReceiptTimeoutError extends ChatGPTControlError {
8
+ constructor(stage) {
9
+ super(`Download ${stage} did not complete before the deadline. Completion is unverified; inspect the existing download before retrying.`, "download_unavailable", false, undefined, { code: "download_receipt_timeout", resumable: false });
10
+ }
11
+ }
12
+ export class DownloadBrowserBlockedError extends ChatGPTControlError {
13
+ constructor() {
14
+ super("Chrome displayed ERR_BLOCKED_BY_CLIENT during the download. Inspect the existing download and Chrome restrictions before retrying; completion is unverified.", "download_unavailable", false, undefined, { code: "download_blocked_by_browser", resumable: false });
15
+ }
16
+ }
17
+ export class DownloadReceiptFailedError extends ChatGPTControlError {
18
+ constructor() {
19
+ super("The browser did not provide a verified download receipt. Inspect the existing download before retrying; completion is unverified.", "download_unavailable", false, undefined, { code: "download_receipt_failed", resumable: false });
20
+ }
21
+ }
22
+ export function isTerminalDownloadError(error) {
23
+ return error instanceof DownloadReceiptTimeoutError || error instanceof DownloadBrowserBlockedError || error instanceof DownloadReceiptFailedError;
24
+ }
25
+ export function isTerminalDownloadCode(code) {
26
+ return code === "download_receipt_timeout" || code === "download_blocked_by_browser" || code === "download_receipt_failed";
27
+ }
28
+ async function checkBrowserDownloadError(page, deadline) {
29
+ if (typeof page.evaluate !== "function")
30
+ return;
31
+ // A missing or stalled diagnostic must leave time for the native receipt.
32
+ // Both probes are read-only, bounded by the existing operation deadline,
33
+ // and return an enum instead of exporting error-page URLs or body text.
34
+ const timeoutMs = Math.min(1000, Math.floor(remainingMs(deadline) / 4));
35
+ if (timeoutMs <= 0)
36
+ return;
37
+ let state;
38
+ try {
39
+ state = await withTimeout(page.evaluate(function inspectBrowserDownloadError() {
40
+ if (!document.body?.getAttribute("class")?.split(/\s+/).includes("neterror"))
41
+ return "unavailable";
42
+ const frames = document.querySelectorAll("#main-frame-error");
43
+ if (frames.length !== 1)
44
+ return "unavailable";
45
+ const codes = frames[0].querySelectorAll(".error-code");
46
+ return codes.length === 1 && codes[0].textContent?.trim() === "ERR_BLOCKED_BY_CLIENT"
47
+ ? "blocked_by_client" : "unavailable";
48
+ }, undefined, { timeoutMs }), timeoutMs, "Browser download diagnostic timed out.");
49
+ }
50
+ catch {
51
+ return;
52
+ }
53
+ if (state === "blocked_by_client")
54
+ throw new DownloadBrowserBlockedError();
55
+ }
56
+ async function downloadStep(deadline, stage, run, capMs) {
57
+ const timeoutMs = Math.min(remainingMs(deadline), capMs ?? Number.MAX_SAFE_INTEGER);
58
+ const timeoutError = new DownloadReceiptTimeoutError(stage);
59
+ if (timeoutMs <= 0)
60
+ throw timeoutError;
61
+ try {
62
+ return await withTimeout(run(), timeoutMs, timeoutError.message);
63
+ }
64
+ catch (error) {
65
+ if (error instanceof Error && error.message === timeoutError.message)
66
+ throw timeoutError;
67
+ throw error;
68
+ }
69
+ }
4
70
  export async function waitForDownloadFromClick(page, click, destDir, timeoutMs, filenameHint) {
71
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2_147_483_647) {
72
+ throw new Error("Download timeout must be positive and within the supported timer range.");
73
+ }
74
+ const deadline = createDeadline(timeoutMs);
75
+ await checkBrowserDownloadError(page, deadline);
5
76
  const absoluteDest = resolve(destDir);
6
- await mkdir(absoluteDest, { recursive: true });
7
- const downloadPromise = page.waitForEvent?.("download", { timeout: timeoutMs, timeoutMs });
8
- if (downloadPromise === undefined) {
77
+ await downloadStep(deadline, "destination preparation", () => mkdir(absoluteDest, { recursive: true }));
78
+ if (typeof page.waitForEvent !== "function") {
9
79
  throw new Error("The active browser page does not expose download events.");
10
80
  }
11
- await withTimeout(click(), localGuardTimeout(timeoutMs, 10000), "Download control click did not complete before the local guard timeout.");
12
- const download = await downloadPromise;
13
- const sourcePath = typeof download.path === "function" ? await download.path() : null;
14
- const suggestedFilename = filenameHint
15
- ?? download.suggestedFilename?.()
16
- ?? (sourcePath === null ? undefined : basename(sourcePath))
17
- ?? `chatgpt-download-${Date.now()}`;
18
- const targetPath = join(absoluteDest, basename(suggestedFilename));
19
- if (typeof download.saveAs === "function") {
20
- await download.saveAs(targetPath);
21
- }
22
- else if (sourcePath !== null) {
23
- if (resolve(sourcePath) !== resolve(targetPath)) {
24
- await copyFile(sourcePath, targetPath);
81
+ // Arm before activation. Promise.all observes both rejections immediately,
82
+ // including a late event rejection after a failed or timed-out click.
83
+ const nativeTimeoutMs = remainingMs(deadline);
84
+ if (nativeTimeoutMs <= 0)
85
+ throw new DownloadReceiptTimeoutError("event registration");
86
+ const activation = new AbortController();
87
+ let failure;
88
+ const assertActive = () => {
89
+ if (failure !== undefined)
90
+ throw failure;
91
+ if (activation.signal.aborted || remainingMs(deadline) <= 0)
92
+ throw new DownloadReceiptTimeoutError("control click");
93
+ };
94
+ try {
95
+ const rawWait = page.waitForEvent("download", { timeout: nativeTimeoutMs, timeoutMs: nativeTimeoutMs });
96
+ const registration = coordinatedEventRegistrationBarrier(rawWait);
97
+ let receiptFailed = false;
98
+ const downloadPromise = downloadStep(deadline, "event receipt", () => rawWait).catch(error => {
99
+ receiptFailed = true;
100
+ failure = isTerminalDownloadError(error) ? error : new DownloadReceiptFailedError();
101
+ activation.abort(failure);
102
+ throw failure;
103
+ });
104
+ const [download] = await Promise.all([
105
+ downloadPromise,
106
+ (async () => {
107
+ // Actor fairness may choose a queued mutation before another read.
108
+ // Registration must finish before the activating click is even queued.
109
+ if (registration !== undefined)
110
+ await downloadStep(deadline, "event registration", () => registration);
111
+ try {
112
+ await downloadStep(deadline, "control click", () => withCoordinatedMutationGuard(activation.signal, assertActive, click), 10_000);
113
+ }
114
+ catch (error) {
115
+ if (!receiptFailed)
116
+ await checkBrowserDownloadError(page, deadline);
117
+ throw error;
118
+ }
119
+ if (!receiptFailed)
120
+ await checkBrowserDownloadError(page, deadline);
121
+ })()
122
+ ]);
123
+ // saveAs is already a complete capability; an optional path() call can hang
124
+ // on bridge implementations and is unnecessary when saveAs is available.
125
+ const sourcePath = typeof download.saveAs !== "function" && typeof download.path === "function"
126
+ ? await downloadStep(deadline, "source path", () => download.path({ timeoutMs: remainingMs(deadline) })) : null;
127
+ const suggestedFilename = filenameHint
128
+ ?? download.suggestedFilename?.()
129
+ ?? (sourcePath === null ? undefined : basename(sourcePath))
130
+ ?? `chatgpt-download-${Date.now()}`;
131
+ const targetPath = join(absoluteDest, basename(suggestedFilename));
132
+ if (typeof download.saveAs === "function") {
133
+ await downloadStep(deadline, "file save", () => download.saveAs(targetPath));
134
+ }
135
+ else if (sourcePath !== null) {
136
+ if (resolve(sourcePath) !== resolve(targetPath)) {
137
+ await downloadStep(deadline, "file copy", () => copyFile(sourcePath, targetPath));
138
+ }
139
+ }
140
+ else {
141
+ throw new Error("The browser download object exposes neither saveAs() nor a completed local path().");
25
142
  }
143
+ const saved = await downloadStep(deadline, "file verification", () => stat(targetPath));
144
+ if (saved.size <= 0) {
145
+ throw new Error(`Downloaded file is empty: ${targetPath}`);
146
+ }
147
+ return {
148
+ path: targetPath,
149
+ suggestedFilename,
150
+ bytes: saved.size
151
+ };
26
152
  }
27
- else {
28
- throw new Error("The browser download object exposes neither saveAs() nor a completed local path().");
153
+ catch (error) {
154
+ throw failure ?? (isTerminalDownloadError(error) ? error : new DownloadReceiptFailedError());
29
155
  }
30
- const saved = await stat(targetPath);
31
- if (saved.size <= 0) {
32
- throw new Error(`Downloaded file is empty: ${targetPath}`);
156
+ finally {
157
+ // Cancels locally queued activation only. A native download already in
158
+ // progress, or its native event listener, is not claimed to be cancelled.
159
+ activation.abort();
33
160
  }
34
- return {
35
- path: targetPath,
36
- suggestedFilename,
37
- bytes: saved.size
38
- };
39
161
  }
@@ -2,6 +2,7 @@ import { classifyVisibleText } from "../safety/blockers.js";
2
2
  import { compactVisibleText } from "../safety/redaction.js";
3
3
  import { escapeRegExp, localeLabels } from "../dom/locale-labels.js";
4
4
  import { withTimeout } from "../commands/timeouts.js";
5
+ import { chatGPTAttachmentContextUrl } from "./chatgpt-url.js";
5
6
  export function parseConversationId(url) {
6
7
  let parsed;
7
8
  try {
@@ -19,6 +20,11 @@ export function parseConversationId(url) {
19
20
  export async function readPageState(page) {
20
21
  const rawUrl = typeof page.url === "function" ? await Promise.resolve(page.url()).catch(() => "") : "";
21
22
  const url = typeof rawUrl === "string" ? rawUrl : "";
23
+ // This is a file transfer endpoint, not a conversation surface. Keep the
24
+ // internal URL for origin checks without inspecting error-page content.
25
+ if (chatGPTAttachmentContextUrl(url) !== undefined) {
26
+ return { url, visibleText: "", signedIn: false };
27
+ }
22
28
  const rawTitle = typeof page.title === "function" ? await page.title().catch(() => undefined) : undefined;
23
29
  const title = typeof rawTitle === "string" ? rawTitle : undefined;
24
30
  const surface = await readPageSurfaceSnapshot(page);
@@ -6,6 +6,12 @@ import type { ChatGPTAgent, ChatGPTAgentConfig, ChatGPTResponse, ChatGPTRunner,
6
6
  import { type ChatGPTResponsesCreateArgs } from "./runner/responses.js";
7
7
  import { type ReportRedactionOptions } from "./safety/report-redaction.js";
8
8
  import { type BlockerExplanation, type ExplainBlockerOptions } from "./diagnostics/blockers.js";
9
+ import { type OperationAdapterFactory, type OperationControlAdapterFactory, type OperationClientCollectOptions, type OperationClientControlOptions, type OperationClientRunOptions, type OperationClientSubmitOptions, type OperationHandleAdapterFactory } from "./operations/client.js";
10
+ import { type OperationBrowserAdapter } from "./operations/service.js";
11
+ import type { OperationControlRequestV1, OperationHandleV1, OperationSubmitRequestV1 } from "./operations/types.js";
12
+ import type { CollectorResult } from "./operations/collector.js";
13
+ import type { ControlResult } from "./operations/control.js";
14
+ import type { OperationInspectResult, OperationRunResult, OperationSubmitResult } from "./operations/service.js";
9
15
  export type ChatGPTClientOptions = RuntimeEnv & {
10
16
  defaults?: {
11
17
  experience?: Exclude<ChatGPTExperience, "unknown">;
@@ -18,7 +24,41 @@ export type ChatGPTClientOptions = RuntimeEnv & {
18
24
  };
19
25
  limits?: Partial<RunLimits>;
20
26
  reporting?: RunReportOptions;
27
+ /** Additive transactional operation surface. Construction remains synchronous; journal opening is lazy. */
28
+ operations?: ChatGPTOperationsOptions;
21
29
  };
30
+ /**
31
+ * Configuration for the additive transactional operation surface.
32
+ *
33
+ * The default state root is the platform application-state directory owned by
34
+ * `OperationJournal`. Set `stateRoot` explicitly when multiple cooperating
35
+ * clients must share a project-local journal. Browser adapters are opt-in:
36
+ * without one, durable inspection still works but browser-touching calls fail
37
+ * closed with an adapter blocker.
38
+ */
39
+ export type ChatGPTOperationsOptions = Readonly<{
40
+ stateRoot?: string;
41
+ /** Explicit private-file connection to a normal Node journal authority. */
42
+ journalService?: Readonly<{
43
+ descriptorPath: string;
44
+ timeoutMs?: number;
45
+ }>;
46
+ adapter?: OperationBrowserAdapter;
47
+ adapterFactory?: OperationAdapterFactory;
48
+ handleAdapterFactory?: OperationHandleAdapterFactory;
49
+ /** Fresh request-local adapter for Stop or Work steer; never cached. */
50
+ controlAdapterFactory?: OperationControlAdapterFactory;
51
+ maxCachedAdapters?: number;
52
+ maxCasRetries?: number;
53
+ }>;
54
+ /** Stable, lazily initialized facade exposed as `chatgpt.operations`. */
55
+ export type ChatGPTOperations = Readonly<{
56
+ submit(request: OperationSubmitRequestV1, options?: OperationClientSubmitOptions): Promise<OperationSubmitResult>;
57
+ collect(handle: OperationHandleV1, options?: OperationClientCollectOptions): Promise<CollectorResult>;
58
+ inspect(handle: OperationHandleV1): Promise<OperationInspectResult>;
59
+ control(request: OperationControlRequestV1, options?: OperationClientControlOptions): Promise<ControlResult>;
60
+ run(request: OperationSubmitRequestV1, options?: OperationClientRunOptions): Promise<OperationRunResult>;
61
+ }>;
22
62
  export type RunLimits = {
23
63
  maxPromptsPerRun: number;
24
64
  maxThreadsOpenedPerRun: number;
@@ -58,6 +98,8 @@ export type FileInput = string | {
58
98
  };
59
99
  export type AskWorkflowArgs = {
60
100
  prompt: string;
101
+ /** Caller-owned durable identity. Supplying it opts this invocation into the transactional operation path. */
102
+ operationId?: string;
61
103
  thread?: WorkflowThread;
62
104
  existingTab?: BootstrapArgs["existingTab"];
63
105
  preferExistingTab?: boolean;
@@ -107,6 +149,7 @@ export type ChatGPTClient = {
107
149
  agent<TOutput = string>(config: ChatGPTAgentConfig<TOutput>): ChatGPTAgent<TOutput>;
108
150
  run<TOutput = string>(agent: ChatGPTAgent<TOutput>, input: ChatGPTRunInput): Promise<ChatGPTRunResult<TOutput>>;
109
151
  runner: ChatGPTRunner;
152
+ operations: ChatGPTOperations;
110
153
  responses: {
111
154
  create(args: ChatGPTResponsesCreateArgs | Record<string, unknown>): Promise<ChatGPTResponse>;
112
155
  };