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

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 (191) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +27 -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/operation-action-prepared-event.json +42 -0
  7. package/contracts/v1/fixtures/operation-action.json +13 -0
  8. package/contracts/v1/fixtures/operation-artifact-receipt.json +14 -0
  9. package/contracts/v1/fixtures/operation-artifact-transfer-intent-event.json +24 -0
  10. package/contracts/v1/fixtures/operation-artifact-transfer-receipt-event.json +26 -0
  11. package/contracts/v1/fixtures/operation-artifact-transfer-state.json +178 -0
  12. package/contracts/v1/fixtures/operation-blocker.json +10 -0
  13. package/contracts/v1/fixtures/operation-collect-request.json +17 -0
  14. package/contracts/v1/fixtures/operation-collect-result.json +42 -0
  15. package/contracts/v1/fixtures/operation-control-receipt.json +13 -0
  16. package/contracts/v1/fixtures/operation-control-request.json +18 -0
  17. package/contracts/v1/fixtures/operation-control-result.json +31 -0
  18. package/contracts/v1/fixtures/operation-event.json +18 -0
  19. package/contracts/v1/fixtures/operation-handle.json +10 -0
  20. package/contracts/v1/fixtures/operation-inspect-request.json +13 -0
  21. package/contracts/v1/fixtures/operation-inspect-result.json +205 -0
  22. package/contracts/v1/fixtures/operation-ownership-baseline-event.json +34 -0
  23. package/contracts/v1/fixtures/operation-receipt.json +32 -0
  24. package/contracts/v1/fixtures/operation-recovery-decision.json +7 -0
  25. package/contracts/v1/fixtures/operation-recovery-observation.json +12 -0
  26. package/contracts/v1/fixtures/operation-request.json +33 -0
  27. package/contracts/v1/fixtures/operation-state.json +144 -0
  28. package/contracts/v1/fixtures/operation-submission-witness-event.json +20 -0
  29. package/contracts/v1/fixtures/operation-submission-witness.json +11 -0
  30. package/contracts/v1/fixtures/operation-submit-result.json +16 -0
  31. package/contracts/v1/fixtures/operation-target-established-event.json +21 -0
  32. package/contracts/v1/manifest.json +155 -1
  33. package/contracts/v1/parity-suite.json +96 -1
  34. package/contracts/v1/schemas/backend-compatibility.schema.json +65 -0
  35. package/contracts/v1/schemas/backend-request.schema.json +8 -2
  36. package/contracts/v1/schemas/capabilities.schema.json +81 -1
  37. package/contracts/v1/schemas/manifest.schema.json +57 -0
  38. package/contracts/v1/schemas/operation-action.schema.json +155 -0
  39. package/contracts/v1/schemas/operation-artifact-receipt.schema.json +89 -0
  40. package/contracts/v1/schemas/operation-blocker.schema.json +70 -0
  41. package/contracts/v1/schemas/operation-collect-request.schema.json +46 -0
  42. package/contracts/v1/schemas/operation-collect-result.schema.json +74 -0
  43. package/contracts/v1/schemas/operation-control-receipt.schema.json +108 -0
  44. package/contracts/v1/schemas/operation-control-request.schema.json +111 -0
  45. package/contracts/v1/schemas/operation-control-result.schema.json +112 -0
  46. package/contracts/v1/schemas/operation-event.schema.json +756 -0
  47. package/contracts/v1/schemas/operation-handle.schema.json +55 -0
  48. package/contracts/v1/schemas/operation-inspect-request.schema.json +42 -0
  49. package/contracts/v1/schemas/operation-inspect-result.schema.json +1662 -0
  50. package/contracts/v1/schemas/operation-receipt.schema.json +153 -0
  51. package/contracts/v1/schemas/operation-recovery.schema.json +314 -0
  52. package/contracts/v1/schemas/operation-request.schema.json +158 -0
  53. package/contracts/v1/schemas/operation-state.schema.json +691 -0
  54. package/contracts/v1/schemas/operation-submission-witness.schema.json +48 -0
  55. package/contracts/v1/schemas/operation-submit-result.schema.json +94 -0
  56. package/contracts/v1/surface-drift-policy.json +50 -0
  57. package/contracts/v1/vectors/operation-request-digest-v1.json +43 -0
  58. package/dist/codex-chatgpt-control-backend.mjs +43897 -9094
  59. package/dist/codex-chatgpt-control.bundle.mjs +46360 -10262
  60. package/dist/src/backend/client.d.ts +103 -2
  61. package/dist/src/backend/client.js +1436 -97
  62. package/dist/src/backend/compatibility.d.ts +12 -0
  63. package/dist/src/backend/compatibility.js +208 -0
  64. package/dist/src/backend/protocol.d.ts +84 -1
  65. package/dist/src/backend/protocol.js +31 -5
  66. package/dist/src/backend/runtime-identity.d.ts +10 -0
  67. package/dist/src/backend/runtime-identity.js +141 -0
  68. package/dist/src/backend/session.d.ts +10 -2
  69. package/dist/src/backend/session.js +478 -7
  70. package/dist/src/backend/stdio-server.d.ts +4 -1
  71. package/dist/src/backend/stdio-server.js +240 -28
  72. package/dist/src/browser/active-composer-file-input.d.ts +9 -0
  73. package/dist/src/browser/active-composer-file-input.js +33 -0
  74. package/dist/src/browser/attach.d.ts +4 -1
  75. package/dist/src/browser/attach.js +136 -28
  76. package/dist/src/browser/downloads.d.ts +2 -0
  77. package/dist/src/client.d.ts +38 -0
  78. package/dist/src/client.js +1222 -59
  79. package/dist/src/commands/configuration.js +13 -7
  80. package/dist/src/commands/doctor.d.ts +1 -1
  81. package/dist/src/commands/doctor.js +26 -3
  82. package/dist/src/commands/experience.js +35 -0
  83. package/dist/src/commands/files.js +4 -27
  84. package/dist/src/commands/modes.js +49 -17
  85. package/dist/src/commands/power-discovery.d.ts +130 -0
  86. package/dist/src/commands/power-discovery.js +627 -0
  87. package/dist/src/commands/project-sources.js +325 -31
  88. package/dist/src/commands/sequence.js +59 -32
  89. package/dist/src/commands/session.js +7 -2
  90. package/dist/src/commands/work.js +31 -0
  91. package/dist/src/errors.d.ts +9 -0
  92. package/dist/src/errors.js +22 -0
  93. package/dist/src/index.d.ts +6 -0
  94. package/dist/src/index.js +9 -0
  95. package/dist/src/operations/artifact-output.d.ts +69 -0
  96. package/dist/src/operations/artifact-output.js +1748 -0
  97. package/dist/src/operations/artifact-stream.d.ts +19 -0
  98. package/dist/src/operations/artifact-stream.js +35 -0
  99. package/dist/src/operations/artifact-transfer.d.ts +149 -0
  100. package/dist/src/operations/artifact-transfer.js +1312 -0
  101. package/dist/src/operations/browser-adapter.d.ts +161 -0
  102. package/dist/src/operations/browser-adapter.js +2023 -0
  103. package/dist/src/operations/browser-observation.d.ts +110 -0
  104. package/dist/src/operations/browser-observation.js +1208 -0
  105. package/dist/src/operations/browser-target.d.ts +103 -0
  106. package/dist/src/operations/browser-target.js +489 -0
  107. package/dist/src/operations/canonical.d.ts +4 -0
  108. package/dist/src/operations/canonical.js +412 -0
  109. package/dist/src/operations/chatgpt-runtime.d.ts +72 -0
  110. package/dist/src/operations/chatgpt-runtime.js +1150 -0
  111. package/dist/src/operations/client.d.ts +138 -0
  112. package/dist/src/operations/client.js +1143 -0
  113. package/dist/src/operations/collector.d.ts +195 -0
  114. package/dist/src/operations/collector.js +1039 -0
  115. package/dist/src/operations/control.d.ts +372 -0
  116. package/dist/src/operations/control.js +1498 -0
  117. package/dist/src/operations/file-identity.d.ts +33 -0
  118. package/dist/src/operations/file-identity.js +153 -0
  119. package/dist/src/operations/handle.d.ts +19 -0
  120. package/dist/src/operations/handle.js +727 -0
  121. package/dist/src/operations/index.d.ts +22 -0
  122. package/dist/src/operations/index.js +22 -0
  123. package/dist/src/operations/journal.d.ts +142 -0
  124. package/dist/src/operations/journal.js +1931 -0
  125. package/dist/src/operations/production-attachments.d.ts +116 -0
  126. package/dist/src/operations/production-attachments.js +1018 -0
  127. package/dist/src/operations/production-chatgpt-artifacts.d.ts +74 -0
  128. package/dist/src/operations/production-chatgpt-artifacts.js +1146 -0
  129. package/dist/src/operations/production-chatgpt-attachments.d.ts +71 -0
  130. package/dist/src/operations/production-chatgpt-attachments.js +1563 -0
  131. package/dist/src/operations/production-configuration.d.ts +42 -0
  132. package/dist/src/operations/production-configuration.js +1363 -0
  133. package/dist/src/operations/production-primitives.d.ts +53 -0
  134. package/dist/src/operations/production-primitives.js +1073 -0
  135. package/dist/src/operations/production-work-steer.d.ts +203 -0
  136. package/dist/src/operations/production-work-steer.js +1218 -0
  137. package/dist/src/operations/recovery.d.ts +80 -0
  138. package/dist/src/operations/recovery.js +174 -0
  139. package/dist/src/operations/runtime-adapter.d.ts +118 -0
  140. package/dist/src/operations/runtime-adapter.js +786 -0
  141. package/dist/src/operations/send-once.d.ts +224 -0
  142. package/dist/src/operations/send-once.js +1081 -0
  143. package/dist/src/operations/service.d.ts +281 -0
  144. package/dist/src/operations/service.js +2810 -0
  145. package/dist/src/operations/staging.d.ts +136 -0
  146. package/dist/src/operations/staging.js +630 -0
  147. package/dist/src/operations/state-machine.d.ts +29 -0
  148. package/dist/src/operations/state-machine.js +2047 -0
  149. package/dist/src/operations/submission.d.ts +423 -0
  150. package/dist/src/operations/submission.js +1676 -0
  151. package/dist/src/operations/turn-ownership.d.ts +202 -0
  152. package/dist/src/operations/turn-ownership.js +700 -0
  153. package/dist/src/operations/types.d.ts +454 -0
  154. package/dist/src/operations/types.js +20 -0
  155. package/dist/src/operations/wire-requests.d.ts +21 -0
  156. package/dist/src/operations/wire-requests.js +396 -0
  157. package/dist/src/operations/wire-results.d.ts +97 -0
  158. package/dist/src/operations/wire-results.js +818 -0
  159. package/dist/src/runner/responses.d.ts +3 -1
  160. package/dist/src/runner/responses.js +19 -1
  161. package/dist/src/runner/result.js +227 -55
  162. package/dist/src/runner/types.d.ts +12 -0
  163. package/dist/src/runtime/command-routing.d.ts +149 -0
  164. package/dist/src/runtime/command-routing.js +431 -0
  165. package/dist/src/runtime/coordinated-browser.d.ts +24 -0
  166. package/dist/src/runtime/coordinated-browser.js +315 -0
  167. package/dist/src/runtime/coordinated-page.d.ts +39 -0
  168. package/dist/src/runtime/coordinated-page.js +634 -0
  169. package/dist/src/runtime/operation-context.d.ts +142 -0
  170. package/dist/src/runtime/operation-context.js +410 -0
  171. package/dist/src/runtime/runtime-session.d.ts +95 -0
  172. package/dist/src/runtime/runtime-session.js +314 -0
  173. package/dist/src/runtime/tab-coordinator.d.ts +200 -0
  174. package/dist/src/runtime/tab-coordinator.js +1200 -0
  175. package/dist/src/runtime/value-boundaries.d.ts +18 -0
  176. package/dist/src/runtime/value-boundaries.js +49 -0
  177. package/dist/src/safety/untrusted-output.js +2 -1
  178. package/dist/src/scripts/backend-server.js +4 -1
  179. package/dist/src/scripts/live-smoke/harness.js +79 -7
  180. package/dist/src/scripts/live-smoke/scenarios.d.ts +28 -0
  181. package/dist/src/scripts/live-smoke/scenarios.js +110 -18
  182. package/dist/src/scripts/live-smoke/types.d.ts +3 -0
  183. package/dist/src/scripts/release-canary-module.js +17 -4
  184. package/dist/src/types.d.ts +38 -1
  185. package/package.json +1 -1
  186. package/references/2026-08-16-transactional-operations.md +459 -0
  187. package/references/agents-runner.md +34 -0
  188. package/references/backend-protocol.md +73 -0
  189. package/references/python-parity.md +72 -0
  190. package/references/responses-adapter.md +19 -0
  191. package/references/streaming.md +6 -0
@@ -0,0 +1,1150 @@
1
+ import { attachChatGPTBrowser, isChatGPTUrl, resolveChatGPTBrowser, tabIdFromPage } from "../browser/attach.js";
2
+ import { CHATGPT_HOME } from "../browser/chatgpt-url.js";
3
+ import { composerTextbox, sendButton } from "../dom/selectors.js";
4
+ import { parseConversationId } from "../browser/page-state.js";
5
+ import { detectExperienceFromSnapshot, openExperience, readSurfaceSnapshot } from "../commands/experience.js";
6
+ import { coordinatedBrowserResource, unwrapCoordinatedBrowser, } from "../runtime/coordinated-browser.js";
7
+ import { unwrapCoordinatedPage } from "../runtime/coordinated-page.js";
8
+ import { getProcessTabCoordinator } from "../runtime/tab-coordinator.js";
9
+ import { createProductionConfigurationStaging } from "./production-configuration.js";
10
+ import { revalidateOperationFile } from "./file-identity.js";
11
+ import { createChatGPTAttachmentProvider } from "./production-chatgpt-attachments.js";
12
+ import { createProductionOperationPrimitives } from "./production-primitives.js";
13
+ import { createRuntimeOperationBrowserAdapter } from "./runtime-adapter.js";
14
+ import { createProductionChatGPTArtifacts } from "./production-chatgpt-artifacts.js";
15
+ import { createProductionWorkSteerPrimitive } from "./production-work-steer.js";
16
+ import { observeBrowserPage } from "./browser-observation.js";
17
+ /**
18
+ * Create a fresh, prompt-bearing control adapter. The prompt is accepted only
19
+ * for this invocation and is never cached or forwarded through durable phase
20
+ * records. The factory binds the authenticated parent target by tab ID.
21
+ */
22
+ export function createChatGPTOperationControlAdapterFactory(options) {
23
+ const normalized = normalizeFactoryOptions(options);
24
+ return async (context) => {
25
+ const request = snapshotControlRequest(context.request);
26
+ const target = snapshotTargetBinding(context.target);
27
+ const targetRequest = Object.freeze({ type: "tab_id", tabId: target.tabId });
28
+ const parentOperationId = context.handle.operationId;
29
+ const parentRequestDigest = context.handle.requestDigest;
30
+ const targetBindingDigest = context.handle.targetBindingDigest;
31
+ if (targetBindingDigest === undefined)
32
+ throw new ChatGPTRuntimeFactoryError();
33
+ const adapterOptions = {
34
+ owner: normalized.owner,
35
+ evidenceDigest: normalized.evidenceDigest,
36
+ ...(normalized.coordinator === undefined ? {} : { coordinator: normalized.coordinator }),
37
+ ...(normalized.transactionTimeoutMs === undefined ? {} : { transactionTimeoutMs: normalized.transactionTimeoutMs }),
38
+ exposeStaging: false,
39
+ exposeControl: true,
40
+ recovery: Object.freeze({
41
+ operationId: parentOperationId,
42
+ requestDigest: parentRequestDigest,
43
+ surface: context.state.surface,
44
+ target,
45
+ targetRequest
46
+ }),
47
+ capture: async (captureRequest) => await captureChatGPTRequest({
48
+ ...normalized,
49
+ request: undefined,
50
+ files: Object.freeze([]),
51
+ captureRequest,
52
+ recoveryTarget: target,
53
+ control: Object.freeze({
54
+ request,
55
+ targetBindingDigest
56
+ })
57
+ })
58
+ };
59
+ return createRuntimeOperationBrowserAdapter(adapterOptions);
60
+ };
61
+ }
62
+ /** Alias for callers that use the shorter control-factory name. */
63
+ export const createChatGPTControlAdapterFactory = createChatGPTOperationControlAdapterFactory;
64
+ export class ChatGPTRuntimeFactoryError extends Error {
65
+ code = "chatgpt_runtime_unavailable";
66
+ constructor() {
67
+ super("The ChatGPT operation runtime could not prove the requested browser target safely.");
68
+ this.name = "ChatGPTRuntimeFactoryError";
69
+ }
70
+ }
71
+ const DEFAULT_SURFACE_TIMEOUT_MS = 30_000;
72
+ const MAX_SURFACE_TIMEOUT_MS = 120_000;
73
+ const ID_PATTERN = /^[A-Za-z0-9._:-]{1,512}$/u;
74
+ /**
75
+ * Create the normal request-scoped adapter factory for `ChatGPTClient`.
76
+ *
77
+ * The returned factory performs no browser work until its capture callback is
78
+ * reached by `resolveTarget`. It is therefore safe to construct while a
79
+ * client is being initialized or while the browser bridge is unavailable.
80
+ */
81
+ export function createChatGPTOperationAdapterFactory(options) {
82
+ const normalized = normalizeFactoryOptions(options);
83
+ return async (context) => {
84
+ const request = snapshotRequest(context.request);
85
+ const files = Object.freeze([...context.files]);
86
+ const adapterOptions = {
87
+ owner: normalized.owner,
88
+ evidenceDigest: normalized.evidenceDigest,
89
+ ...(normalized.coordinator === undefined ? {} : { coordinator: normalized.coordinator }),
90
+ ...(normalized.transactionTimeoutMs === undefined ? {} : { transactionTimeoutMs: normalized.transactionTimeoutMs }),
91
+ files,
92
+ fileManifestDigest: (ordinal, manifest) => normalized.evidenceDigest("file-manifest", { ordinal, ...manifest }),
93
+ exposeStaging: true,
94
+ exposeControl: true,
95
+ ...(hasTransferDestination(request) ? { exposeArtifacts: true } : {}),
96
+ capture: async (captureRequest) => {
97
+ // `captureRequest` is reached only after OperationService has created
98
+ // the operation record. Never move this call into factory creation.
99
+ return await captureChatGPTRequest({
100
+ ...normalized,
101
+ request,
102
+ files,
103
+ captureRequest,
104
+ recoveryTarget: undefined
105
+ });
106
+ }
107
+ };
108
+ return createRuntimeOperationBrowserAdapter(adapterOptions);
109
+ };
110
+ }
111
+ /** Alias for integrations that use the "runtime" naming first. */
112
+ export const createChatGPTOperationRuntimeFactory = createChatGPTOperationAdapterFactory;
113
+ /**
114
+ * Create the restart-safe handle factory. Recovery always attaches by the
115
+ * durable tab id; it never falls back to the selected tab or a replacement.
116
+ */
117
+ export function createChatGPTOperationHandleAdapterFactory(options) {
118
+ const normalized = normalizeFactoryOptions(options);
119
+ return async (context) => {
120
+ const target = snapshotTargetBinding(context.target);
121
+ const targetRequest = Object.freeze({
122
+ type: "tab_id",
123
+ tabId: target.tabId
124
+ });
125
+ const adapterOptions = {
126
+ owner: normalized.owner,
127
+ evidenceDigest: normalized.evidenceDigest,
128
+ ...(normalized.coordinator === undefined ? {} : { coordinator: normalized.coordinator }),
129
+ ...(normalized.transactionTimeoutMs === undefined ? {} : { transactionTimeoutMs: normalized.transactionTimeoutMs }),
130
+ exposeStaging: false,
131
+ exposeControl: true,
132
+ recovery: Object.freeze({
133
+ operationId: context.operationId,
134
+ requestDigest: context.requestDigest,
135
+ surface: context.surface,
136
+ target,
137
+ targetRequest
138
+ }),
139
+ capture: async (captureRequest) => await captureChatGPTRequest({
140
+ ...normalized,
141
+ request: undefined,
142
+ files: Object.freeze([]),
143
+ captureRequest,
144
+ recoveryTarget: target
145
+ })
146
+ };
147
+ return createRuntimeOperationBrowserAdapter(adapterOptions);
148
+ };
149
+ }
150
+ /** Alias retained for callers that call the restart path a recovery factory. */
151
+ export const createChatGPTOperationRecoveryFactory = createChatGPTOperationHandleAdapterFactory;
152
+ function normalizeFactoryOptions(value) {
153
+ if (value === null || typeof value !== "object")
154
+ throw new ChatGPTRuntimeFactoryError();
155
+ assertOwnDataKeys(value, [
156
+ "env",
157
+ "owner",
158
+ "evidenceDigest",
159
+ "coordinator",
160
+ "transactionTimeoutMs",
161
+ "surfaceTimeoutMs",
162
+ "capabilities",
163
+ "primitives"
164
+ ]);
165
+ const env = snapshotRuntimeEnv(readDataProperty(value, "env"));
166
+ const owner = snapshotOwner(readDataProperty(value, "owner"));
167
+ const evidenceDigest = readDataProperty(value, "evidenceDigest");
168
+ if (typeof evidenceDigest !== "function")
169
+ throw new ChatGPTRuntimeFactoryError();
170
+ const coordinatorValue = readDataProperty(value, "coordinator");
171
+ const coordinator = coordinatorValue ?? getProcessTabCoordinator();
172
+ if (coordinator === null || typeof coordinator !== "object"
173
+ || typeof coordinator.withBrowserAcquisition !== "function"
174
+ || typeof coordinator.withTabTransaction !== "function") {
175
+ throw new ChatGPTRuntimeFactoryError();
176
+ }
177
+ const transactionTimeoutMs = readDataProperty(value, "transactionTimeoutMs");
178
+ if (transactionTimeoutMs !== undefined
179
+ && (!Number.isSafeInteger(transactionTimeoutMs) || transactionTimeoutMs < 1 || transactionTimeoutMs > 120_000)) {
180
+ throw new ChatGPTRuntimeFactoryError();
181
+ }
182
+ const surfaceTimeoutMs = readDataProperty(value, "surfaceTimeoutMs") ?? DEFAULT_SURFACE_TIMEOUT_MS;
183
+ if (!Number.isSafeInteger(surfaceTimeoutMs) || surfaceTimeoutMs < 0 || surfaceTimeoutMs > MAX_SURFACE_TIMEOUT_MS) {
184
+ throw new ChatGPTRuntimeFactoryError();
185
+ }
186
+ const capabilities = snapshotCapabilities(readDataProperty(value, "capabilities"));
187
+ const primitives = readDataProperty(value, "primitives");
188
+ if (primitives !== undefined && typeof primitives !== "function") {
189
+ throw new ChatGPTRuntimeFactoryError();
190
+ }
191
+ return Object.freeze({
192
+ env,
193
+ owner,
194
+ evidenceDigest,
195
+ coordinator,
196
+ ...(transactionTimeoutMs === undefined ? {} : { transactionTimeoutMs }),
197
+ surfaceTimeoutMs,
198
+ capabilities,
199
+ ...(primitives === undefined ? {} : { primitives })
200
+ });
201
+ }
202
+ function snapshotOwner(value) {
203
+ if (value === null || typeof value !== "object")
204
+ throw new ChatGPTRuntimeFactoryError();
205
+ assertOwnDataKeys(value, ["backendSessionId", "ownerId", "operationId"]);
206
+ const backendSessionId = readDataProperty(value, "backendSessionId");
207
+ const ownerId = readDataProperty(value, "ownerId");
208
+ if (typeof backendSessionId !== "string" || !ID_PATTERN.test(backendSessionId)) {
209
+ throw new ChatGPTRuntimeFactoryError();
210
+ }
211
+ if (ownerId !== undefined && (typeof ownerId !== "string" || !ID_PATTERN.test(ownerId))) {
212
+ throw new ChatGPTRuntimeFactoryError();
213
+ }
214
+ return Object.freeze({
215
+ backendSessionId,
216
+ ...(ownerId === undefined ? {} : { ownerId })
217
+ });
218
+ }
219
+ function snapshotRuntimeEnv(value) {
220
+ if (value === null || typeof value !== "object")
221
+ throw new ChatGPTRuntimeFactoryError();
222
+ assertOwnDataKeys(value, ["agent", "browser", "page", "clipboard", "now", "expectedTabId"]);
223
+ const snapshot = {};
224
+ const agent = readDataProperty(value, "agent");
225
+ const browser = readDataProperty(value, "browser");
226
+ const page = readDataProperty(value, "page");
227
+ const clipboard = readDataProperty(value, "clipboard");
228
+ const now = readDataProperty(value, "now");
229
+ const expectedTabId = readDataProperty(value, "expectedTabId");
230
+ if (now !== undefined && typeof now !== "function")
231
+ throw new ChatGPTRuntimeFactoryError();
232
+ if (expectedTabId !== undefined && (typeof expectedTabId !== "string" || !ID_PATTERN.test(expectedTabId))) {
233
+ throw new ChatGPTRuntimeFactoryError();
234
+ }
235
+ if (agent !== undefined)
236
+ snapshot.agent = agent;
237
+ if (browser !== undefined)
238
+ snapshot.browser = unwrapCoordinatedBrowser(browser);
239
+ if (page !== undefined)
240
+ snapshot.page = unwrapCoordinatedPage(page);
241
+ if (clipboard !== undefined && clipboard !== null && typeof clipboard === "object") {
242
+ snapshot.clipboard = clipboard;
243
+ }
244
+ if (now !== undefined)
245
+ snapshot.now = now;
246
+ if (expectedTabId !== undefined)
247
+ snapshot.expectedTabId = expectedTabId;
248
+ return Object.freeze(snapshot);
249
+ }
250
+ function snapshotRequest(request) {
251
+ if (request === null || typeof request !== "object")
252
+ throw new ChatGPTRuntimeFactoryError();
253
+ assertOwnDataKeys(request, [
254
+ "schemaVersion",
255
+ "operationId",
256
+ "surface",
257
+ "prompt",
258
+ "target",
259
+ "configuration",
260
+ "files",
261
+ "capture",
262
+ "timeoutMs"
263
+ ]);
264
+ const operationId = readDataProperty(request, "operationId");
265
+ const surface = readDataProperty(request, "surface");
266
+ const prompt = readDataProperty(request, "prompt");
267
+ const target = readDataProperty(request, "target");
268
+ const configuration = readDataProperty(request, "configuration");
269
+ const capture = readDataProperty(request, "capture");
270
+ const files = readDataProperty(request, "files");
271
+ const schemaVersion = readDataProperty(request, "schemaVersion");
272
+ const timeoutMs = readDataProperty(request, "timeoutMs");
273
+ if (typeof operationId !== "string" || typeof surface !== "string" || typeof prompt !== "string"
274
+ || !isOperationSurface(surface) || target === undefined || typeof target !== "object"
275
+ || schemaVersion !== "chatgpt.browser_control.operation_request.v1") {
276
+ throw new ChatGPTRuntimeFactoryError();
277
+ }
278
+ const copy = {
279
+ schemaVersion,
280
+ operationId,
281
+ surface,
282
+ prompt,
283
+ target: snapshotTargetRequest(target)
284
+ };
285
+ if (configuration !== undefined)
286
+ copy.configuration = cloneSafeData(configuration);
287
+ if (capture !== undefined)
288
+ copy.capture = cloneSafeData(capture);
289
+ if (files !== undefined)
290
+ copy.files = cloneSafeData(files);
291
+ if (timeoutMs !== undefined)
292
+ copy.timeoutMs = timeoutMs;
293
+ return Object.freeze(copy);
294
+ }
295
+ function snapshotControlRequest(request) {
296
+ if (request === null || typeof request !== "object")
297
+ throw new ChatGPTRuntimeFactoryError();
298
+ assertOwnDataKeys(request, [
299
+ "schemaVersion",
300
+ "controlActionId",
301
+ "parent",
302
+ "action",
303
+ "expectedAssistantTurnId",
304
+ "steerPrompt",
305
+ "timeoutMs"
306
+ ]);
307
+ const schemaVersion = readDataProperty(request, "schemaVersion");
308
+ const controlActionId = readDataProperty(request, "controlActionId");
309
+ const parent = readDataProperty(request, "parent");
310
+ const action = readDataProperty(request, "action");
311
+ const expectedAssistantTurnId = readDataProperty(request, "expectedAssistantTurnId");
312
+ const steerPrompt = readDataProperty(request, "steerPrompt");
313
+ const timeoutMs = readDataProperty(request, "timeoutMs");
314
+ if (schemaVersion !== "chatgpt.browser_control.operation_control_request.v1"
315
+ || typeof controlActionId !== "string"
316
+ || !ID_PATTERN.test(controlActionId)
317
+ || parent === undefined
318
+ || typeof parent !== "object"
319
+ || Array.isArray(parent)
320
+ || (action !== "stop" && action !== "steer")
321
+ || typeof expectedAssistantTurnId !== "string"
322
+ || !ID_PATTERN.test(expectedAssistantTurnId)
323
+ || (steerPrompt !== undefined && typeof steerPrompt !== "string")
324
+ || (action === "steer" && typeof steerPrompt !== "string")
325
+ || (action === "stop" && steerPrompt !== undefined)
326
+ || (timeoutMs !== undefined && (typeof timeoutMs !== "number" || !Number.isSafeInteger(timeoutMs) || timeoutMs < 0))) {
327
+ throw new ChatGPTRuntimeFactoryError();
328
+ }
329
+ return Object.freeze({
330
+ schemaVersion,
331
+ controlActionId,
332
+ parent: cloneSafeData(parent),
333
+ action,
334
+ expectedAssistantTurnId,
335
+ ...(steerPrompt === undefined ? {} : { steerPrompt }),
336
+ ...(timeoutMs === undefined ? {} : { timeoutMs: timeoutMs })
337
+ });
338
+ }
339
+ function snapshotTargetRequest(value) {
340
+ if (value === null || typeof value !== "object")
341
+ throw new ChatGPTRuntimeFactoryError();
342
+ const type = readDataProperty(value, "type");
343
+ switch (type) {
344
+ case "new":
345
+ case "selected_tab":
346
+ assertOwnDataKeys(value, ["type"]);
347
+ return Object.freeze({ type });
348
+ case "tab_id": {
349
+ assertOwnDataKeys(value, ["type", "tabId"]);
350
+ const tabId = readDataProperty(value, "tabId");
351
+ if (typeof tabId !== "string" || !ID_PATTERN.test(tabId))
352
+ throw new ChatGPTRuntimeFactoryError();
353
+ return Object.freeze({ type, tabId });
354
+ }
355
+ case "conversation_id": {
356
+ assertOwnDataKeys(value, ["type", "conversationId"]);
357
+ const conversationId = readDataProperty(value, "conversationId");
358
+ if (typeof conversationId !== "string" || !ID_PATTERN.test(conversationId))
359
+ throw new ChatGPTRuntimeFactoryError();
360
+ return Object.freeze({ type, conversationId });
361
+ }
362
+ case "url": {
363
+ assertOwnDataKeys(value, ["type", "url"]);
364
+ const url = readDataProperty(value, "url");
365
+ if (typeof url !== "string")
366
+ throw new ChatGPTRuntimeFactoryError();
367
+ const canonical = canonicalChatGPTUrl(url);
368
+ if (canonical === undefined)
369
+ throw new ChatGPTRuntimeFactoryError();
370
+ return Object.freeze({ type, url: canonical });
371
+ }
372
+ default:
373
+ throw new ChatGPTRuntimeFactoryError();
374
+ }
375
+ }
376
+ function snapshotTargetBinding(value) {
377
+ const copy = cloneSafeData(value);
378
+ if (copy === null || typeof copy !== "object" || Array.isArray(copy))
379
+ throw new ChatGPTRuntimeFactoryError();
380
+ const target = copy;
381
+ if (typeof target.providerId !== "string" || typeof target.browserId !== "string"
382
+ || typeof target.tabId !== "string" || typeof target.coordinationScope !== "string"
383
+ || !ID_PATTERN.test(target.providerId) || !ID_PATTERN.test(target.browserId) || !ID_PATTERN.test(target.tabId)
384
+ || (target.coordinationScope !== "process" && target.coordinationScope !== "provider")) {
385
+ throw new ChatGPTRuntimeFactoryError();
386
+ }
387
+ if (target.targetLifecycle === "new_pending")
388
+ throw new ChatGPTRuntimeFactoryError();
389
+ return Object.freeze(target);
390
+ }
391
+ async function captureChatGPTRequest(options) {
392
+ const targetRequest = options.recoveryTarget === undefined
393
+ ? options.request === undefined ? undefined : options.request.target
394
+ : Object.freeze({ type: "tab_id", tabId: options.recoveryTarget.tabId });
395
+ if (targetRequest === undefined)
396
+ throw new ChatGPTRuntimeFactoryError();
397
+ const targetSurface = options.captureRequest.surface;
398
+ const bootstrap = bootstrapArgsForTarget(targetRequest);
399
+ const bootstrapEnv = bootstrapEnvironment(options.env, targetRequest, options.recoveryTarget);
400
+ const acquisitionOwner = Object.freeze({
401
+ ...options.owner,
402
+ operationId: options.captureRequest.operationId
403
+ });
404
+ const coordination = Object.freeze({
405
+ coordinator: options.coordinator,
406
+ owner: acquisitionOwner
407
+ });
408
+ try {
409
+ bootstrapEnv.browser = await resolveChatGPTBrowser(bootstrapEnv, coordination);
410
+ }
411
+ catch {
412
+ throw new ChatGPTRuntimeFactoryError();
413
+ }
414
+ let selectedTabId;
415
+ if (targetRequest.type === "selected_tab") {
416
+ // The browser wrapper performs this one read through the same process
417
+ // coordinator. Supplying the resulting exact page prevents the broader
418
+ // user-open-tab fallback from turning "selected" into "first ChatGPT tab".
419
+ const selected = await selectExactSelectedPage(bootstrapEnv.browser);
420
+ if (selected === undefined)
421
+ throw new ChatGPTRuntimeFactoryError();
422
+ selectedTabId = tabIdFromPage(selected);
423
+ if (selectedTabId === undefined || !ID_PATTERN.test(selectedTabId)) {
424
+ throw new ChatGPTRuntimeFactoryError();
425
+ }
426
+ bootstrapEnv.page = selected;
427
+ }
428
+ const env = Object.freeze(bootstrapEnv);
429
+ let attached;
430
+ try {
431
+ attached = await attachChatGPTBrowser(env, bootstrap, coordination);
432
+ }
433
+ catch {
434
+ throw new ChatGPTRuntimeFactoryError();
435
+ }
436
+ const rawPage = unwrapCoordinatedPage(attached.page);
437
+ const browserId = coordinatedBrowserResource(attached.browser).key;
438
+ const browserResource = coordinatedBrowserResource(attached.browser).key;
439
+ const tabId = tabIdFromPage(attached.page);
440
+ if (tabId === undefined || !ID_PATTERN.test(tabId))
441
+ throw new ChatGPTRuntimeFactoryError();
442
+ if (attached.tabId !== undefined && attached.tabId !== tabId)
443
+ throw new ChatGPTRuntimeFactoryError();
444
+ if (targetRequest.type === "tab_id" && tabId !== targetRequest.tabId) {
445
+ throw new ChatGPTRuntimeFactoryError();
446
+ }
447
+ if (targetRequest.type === "selected_tab" && (selectedTabId === undefined || tabId !== selectedTabId)) {
448
+ throw new ChatGPTRuntimeFactoryError();
449
+ }
450
+ if (options.recoveryTarget !== undefined) {
451
+ assertRecoveredBrowserIdentity(options.recoveryTarget, browserId, tabId);
452
+ }
453
+ await options.coordinator.withBrowserAcquisition(browserResource, {
454
+ owner: acquisitionOwner,
455
+ priority: "mutation",
456
+ signal: options.captureRequest.signal,
457
+ ...(options.transactionTimeoutMs === undefined ? {} : { timeoutMs: options.transactionTimeoutMs }),
458
+ label: "operation-target-prepare"
459
+ }, async () => {
460
+ await validateExactNavigation(rawPage, targetRequest);
461
+ await ensureSurface(rawPage, targetSurface, targetRequest.type === "new", options.surfaceTimeoutMs, tabId);
462
+ });
463
+ const capabilities = options.capabilities;
464
+ const observeCurrent = async (request) => {
465
+ const observationTarget = observationTargetForBinding(request.target);
466
+ const observed = await observePage(request.page, request.operationId, observationTarget, options.evidenceDigest);
467
+ return Object.freeze({ evidence: observed.snapshot.target });
468
+ };
469
+ const resolveTargetEvidence = async (request) => {
470
+ if (!sameTargetRequest(request.target, targetRequest)) {
471
+ throw new ChatGPTRuntimeFactoryError();
472
+ }
473
+ return await options.coordinator.withBrowserAcquisition(browserResource, {
474
+ owner: acquisitionOwner,
475
+ priority: "read",
476
+ signal: request.signal,
477
+ ...(options.transactionTimeoutMs === undefined ? {} : { timeoutMs: options.transactionTimeoutMs }),
478
+ label: "operation-target-bind"
479
+ }, async () => {
480
+ await validateExactNavigation(rawPage, request.target);
481
+ const observationTarget = observationTargetForRequest(request, browserId, tabId, targetRequest);
482
+ const observed = await observePage(rawPage, request.operationId, observationTarget, options.evidenceDigest);
483
+ const anchor = observed.newTargetAnchor;
484
+ return Object.freeze({
485
+ page: rawPage,
486
+ evidence: observed.snapshot.target,
487
+ ...(request.target.type === "new" ? { targetLifecycle: "new_pending" } : {}),
488
+ ...(anchor === undefined ? {} : {
489
+ newTargetAnchorDigest: anchor.anchorDigest,
490
+ blankTaskEvidenceDigest: anchor.blankTaskEvidenceDigest
491
+ }),
492
+ capabilities
493
+ });
494
+ });
495
+ };
496
+ const request = options.request;
497
+ const productionOptions = {
498
+ evidenceDigest: options.evidenceDigest,
499
+ operationId: options.captureRequest.operationId,
500
+ requestDigest: options.captureRequest.requestDigest,
501
+ ...(request === undefined ? {} : { desiredComposerText: request.prompt }),
502
+ ...(options.recoveryTarget === undefined ? {} : { target: options.recoveryTarget })
503
+ };
504
+ const attachments = request === undefined || options.files.length === 0
505
+ ? undefined
506
+ : createChatGPTAttachmentProvider({
507
+ evidenceDigest: options.evidenceDigest,
508
+ files: options.files,
509
+ identityDigest: (ordinal, manifest) => options.evidenceDigest("file-manifest", { ordinal, ...manifest }),
510
+ revalidateFile: identity => revalidateOperationFile(identity, {
511
+ signal: options.captureRequest.signal
512
+ }),
513
+ signal: options.captureRequest.signal
514
+ });
515
+ const production = createProductionOperationPrimitives({
516
+ ...productionOptions,
517
+ ...(attachments === undefined
518
+ ? {}
519
+ : { observeAttachments: attachments.observeAttachments })
520
+ });
521
+ const providerPrimitives = attachments === undefined
522
+ ? production
523
+ : mergePrimitivePorts(production, {
524
+ submission: {
525
+ handoffFiles: attachments.handoffFilesForAdapter,
526
+ observeAttachments: attachments.observeAttachments
527
+ }
528
+ });
529
+ const artifactSource = hasTransferDestination(request)
530
+ ? createProductionChatGPTArtifacts({
531
+ page: rawPage,
532
+ evidenceDigest: options.evidenceDigest,
533
+ signal: options.captureRequest.signal,
534
+ ...(options.transactionTimeoutMs === undefined ? {} : { timeoutMs: options.transactionTimeoutMs })
535
+ })
536
+ : undefined;
537
+ const artifactPrimitive = artifactSource === undefined
538
+ ? undefined
539
+ : productionArtifactPrimitive(artifactSource);
540
+ let capturedPrimitives = artifactPrimitive === undefined
541
+ ? providerPrimitives
542
+ : mergePrimitivePorts(providerPrimitives, { artifacts: artifactPrimitive });
543
+ if (options.control !== undefined && options.control.request.action === "steer") {
544
+ if (options.control.request.steerPrompt === undefined)
545
+ throw new ChatGPTRuntimeFactoryError();
546
+ const steerPrimitive = createProductionWorkSteerPrimitive({
547
+ evidenceDigest: options.evidenceDigest,
548
+ operationId: options.captureRequest.operationId,
549
+ parentRequestDigest: options.captureRequest.requestDigest,
550
+ targetBindingDigest: options.control.targetBindingDigest,
551
+ controlActionId: options.control.request.controlActionId,
552
+ expectedAssistantTurnId: options.control.request.expectedAssistantTurnId,
553
+ target: options.recoveryTarget ?? (() => { throw new ChatGPTRuntimeFactoryError(); })(),
554
+ prompt: options.control.request.steerPrompt,
555
+ observe: requestValue => observeWorkSteerPage(requestValue, options.evidenceDigest, options.recoveryTarget),
556
+ resolveComposer: requestValue => resolveWorkComposer(requestValue.page),
557
+ resolveSendControl: requestValue => resolveWorkSendControl(requestValue.page)
558
+ });
559
+ capturedPrimitives = mergePrimitivePorts(capturedPrimitives, {
560
+ control: workSteerControlPrimitive(steerPrimitive, capturedPrimitives.control)
561
+ });
562
+ }
563
+ const configuration = request?.configuration === undefined
564
+ ? undefined
565
+ : createProductionConfigurationStaging({
566
+ evidenceDigest: options.evidenceDigest,
567
+ operationId: options.captureRequest.operationId,
568
+ requestDigest: options.captureRequest.requestDigest,
569
+ surface: targetSurface,
570
+ configuration: request.configuration
571
+ });
572
+ let primitives = composePrimitives(capturedPrimitives, configuration, options, {
573
+ operationId: options.captureRequest.operationId,
574
+ requestDigest: options.captureRequest.requestDigest,
575
+ surface: targetSurface,
576
+ ...(request === undefined ? {} : {
577
+ prompt: request.prompt,
578
+ ...(request.configuration === undefined ? {} : { configuration: request.configuration })
579
+ }),
580
+ files: options.files,
581
+ signal: options.captureRequest.signal,
582
+ page: rawPage,
583
+ ...(options.recoveryTarget === undefined ? {} : { target: options.recoveryTarget })
584
+ });
585
+ if (artifactPrimitive === undefined && primitives.artifacts !== undefined) {
586
+ const { artifacts: _artifacts, ...withoutArtifacts } = primitives;
587
+ primitives = Object.freeze(withoutArtifacts);
588
+ }
589
+ return Object.freeze({
590
+ page: rawPage,
591
+ capabilities,
592
+ resolveTargetEvidence,
593
+ observeCurrentTarget: observeCurrent,
594
+ ...(artifactPrimitive === undefined || !hasTransferDestination(request)
595
+ ? {}
596
+ : { outputDirectory: request.capture.outputDirectory }),
597
+ primitives
598
+ });
599
+ }
600
+ function productionArtifactPrimitive(source) {
601
+ return Object.freeze({
602
+ acquireDownload: (request, page) => source.acquireDownload(request, page),
603
+ materializeDownload: source.materializeDownload
604
+ });
605
+ }
606
+ function workSteerControlPrimitive(primitive, existing) {
607
+ return Object.freeze({
608
+ ...(existing ?? {}),
609
+ prepareSteer: async (request, page) => mapWorkSteerResult(await primitive.prepare({ page, signal: request.signal, deadlineAt: request.deadlineAt }), request, "prepare"),
610
+ executeSteerPrepared: async (request, page) => mapWorkSteerResult(await primitive.executePrepared({ page, prepared: productionPreparedFromControl(request.prepared), signal: request.signal, deadlineAt: request.deadlineAt }), request, "execute_prepared", request.prepared),
611
+ verifySteer: async (request, page) => mapWorkSteerResult(await primitive.verify({ page, prepared: productionPreparedFromControl(request.prepared), signal: request.signal, deadlineAt: request.deadlineAt }), request, "verify", request.prepared),
612
+ recoverSteer: async (request, page) => mapWorkSteerResult(await primitive.recover({ page, prepared: productionPreparedFromControl(request.prepared), baseline: request.baseline, signal: request.signal, deadlineAt: request.deadlineAt }), request, "recovery", request.prepared)
613
+ });
614
+ }
615
+ function productionPreparedFromControl(prepared) {
616
+ return Object.freeze({
617
+ schemaVersion: "chatgpt.browser_control.production_work_steer.v1",
618
+ operationId: prepared.parentOperationId,
619
+ parentRequestDigest: prepared.parentRequestDigest,
620
+ targetBindingDigest: prepared.parentTargetBindingDigest,
621
+ controlActionId: prepared.controlActionId,
622
+ action: "work_steer",
623
+ expectedAssistantTurnId: prepared.expectedAssistantTurnId,
624
+ assistantBranchId: prepared.assistantBranchId,
625
+ assistantParentTurnId: prepared.assistantParentTurnId,
626
+ baselineSnapshotDigest: prepared.baselineSnapshotDigest,
627
+ preparedDigest: prepared.preparedDigest,
628
+ baseline: prepared.baseline
629
+ });
630
+ }
631
+ function mapWorkSteerResult(result, request, phase, prepared) {
632
+ const identity = "prepared" in request
633
+ ? request.prepared
634
+ : request;
635
+ const base = {
636
+ schemaVersion: "chatgpt.browser_control.operation_control_coordinator.v1",
637
+ phase,
638
+ parentOperationId: "prepared" in request ? identity.parentOperationId : identity.parentOperationId,
639
+ parentRequestDigest: "prepared" in request ? identity.parentRequestDigest : identity.parentRequestDigest,
640
+ parentTargetBindingDigest: "prepared" in request ? identity.parentTargetBindingDigest : identity.parentTargetBindingDigest,
641
+ controlActionId: "prepared" in request ? identity.controlActionId : identity.controlActionId,
642
+ action: "steer",
643
+ requestDigest: "prepared" in request ? identity.requestDigest : identity.requestDigest,
644
+ expectedAssistantTurnId: "prepared" in request ? identity.expectedAssistantTurnId : identity.expectedAssistantTurnId,
645
+ ...(prepared === undefined ? {} : {
646
+ assistantBranchId: prepared.assistantBranchId,
647
+ assistantParentTurnId: prepared.assistantParentTurnId,
648
+ baselineSnapshotDigest: prepared.baselineSnapshotDigest,
649
+ preparedDigest: prepared.preparedDigest
650
+ })
651
+ };
652
+ if (result.status === "prepared") {
653
+ const productionPrepared = result.prepared;
654
+ return Object.freeze({
655
+ ...base,
656
+ phase: "prepare",
657
+ status: "prepared",
658
+ observationRequired: false,
659
+ mutationBoundary: "none",
660
+ prepared: Object.freeze({
661
+ schemaVersion: base.schemaVersion,
662
+ parentOperationId: productionPrepared.operationId,
663
+ parentRequestDigest: productionPrepared.parentRequestDigest,
664
+ parentTargetBindingDigest: productionPrepared.targetBindingDigest,
665
+ controlActionId: productionPrepared.controlActionId,
666
+ action: "steer",
667
+ requestDigest: identity.requestDigest,
668
+ expectedAssistantTurnId: productionPrepared.expectedAssistantTurnId,
669
+ assistantBranchId: productionPrepared.assistantBranchId,
670
+ assistantParentTurnId: productionPrepared.assistantParentTurnId,
671
+ baselineSnapshotDigest: productionPrepared.baselineSnapshotDigest,
672
+ preparedDigest: productionPrepared.preparedDigest,
673
+ baseline: productionPrepared.baseline
674
+ })
675
+ });
676
+ }
677
+ if (result.status === "executed") {
678
+ return Object.freeze({
679
+ ...base,
680
+ phase: "execute_prepared",
681
+ status: "executed",
682
+ observationRequired: true,
683
+ mutationBoundary: "control_may_have_occurred"
684
+ });
685
+ }
686
+ if (result.status === "satisfied") {
687
+ const receipt = result.receipt;
688
+ return Object.freeze({
689
+ ...base,
690
+ phase: phase === "recovery" ? "recovery" : "verify",
691
+ status: "satisfied",
692
+ observationRequired: false,
693
+ mutationBoundary: "control_may_have_occurred",
694
+ assistantBranchId: result.assistantBranchId,
695
+ assistantParentTurnId: result.assistantParentTurnId,
696
+ baselineSnapshotDigest: result.baselineSnapshotDigest,
697
+ preparedDigest: result.preparedDigest,
698
+ receipt: Object.freeze({
699
+ schemaVersion: base.schemaVersion,
700
+ baselineSnapshotDigest: receipt.baselineSnapshotDigest,
701
+ preparedDigest: receipt.preparedDigest,
702
+ assistantTurnId: receipt.assistantTurnId,
703
+ assistantBranchId: receipt.assistantBranchId,
704
+ assistantParentTurnId: receipt.assistantParentTurnId,
705
+ userTurnId: receipt.userTurnId,
706
+ userTurnEvidenceDigest: receipt.userTurnEvidenceDigest,
707
+ postSendDeltaDigest: receipt.postSendDeltaDigest,
708
+ evidenceDigest: receipt.evidenceDigest
709
+ })
710
+ });
711
+ }
712
+ if (result.status === "blocked") {
713
+ return Object.freeze({
714
+ ...base,
715
+ status: "blocked",
716
+ blockerCode: result.blockerCode,
717
+ observationRequired: result.observationRequired,
718
+ mutationBoundary: result.mutationBoundary,
719
+ ...(result.evidenceDigest === undefined ? {} : { evidenceDigest: result.evidenceDigest })
720
+ });
721
+ }
722
+ return Object.freeze({
723
+ ...base,
724
+ status: "uncertain",
725
+ blockerCode: result.blockerCode,
726
+ observationRequired: true,
727
+ mutationBoundary: "control_may_have_occurred",
728
+ quarantine: result.quarantine,
729
+ ...(result.evidenceDigest === undefined ? {} : { evidenceDigest: result.evidenceDigest })
730
+ });
731
+ }
732
+ async function observeWorkSteerPage(request, evidenceDigest, targetBinding) {
733
+ return await observeBrowserPage(request.page, {
734
+ operationId: request.operationId,
735
+ target: observationTargetForBinding(targetBinding),
736
+ evidenceDigest,
737
+ responseContent: "metadata",
738
+ ...(request.baseline === undefined ? {} : { baseline: request.baseline })
739
+ });
740
+ }
741
+ async function resolveWorkComposer(page) {
742
+ try {
743
+ const locator = composerTextbox(page);
744
+ const count = typeof locator.count === "function" ? await locator.count() : 0;
745
+ const visible = count === 1 && typeof locator.isVisible === "function" && await locator.isVisible();
746
+ return visible ? { locator, capabilityKey: "chatgpt.composer", candidateCount: 1 } : undefined;
747
+ }
748
+ catch {
749
+ return undefined;
750
+ }
751
+ }
752
+ async function resolveWorkSendControl(page) {
753
+ try {
754
+ const locator = sendButton(page);
755
+ const count = typeof locator.count === "function" ? await locator.count() : 0;
756
+ const visible = count === 1 && typeof locator.isVisible === "function" && await locator.isVisible();
757
+ return visible ? { locator, capabilityKey: "chatgpt.send", localeKey: "locale.registry", candidateCount: 1 } : undefined;
758
+ }
759
+ catch {
760
+ return undefined;
761
+ }
762
+ }
763
+ function composePrimitives(production, configuration, options, context) {
764
+ const productionStaging = production.staging;
765
+ const productionReadCurrent = productionStaging?.readCurrent;
766
+ const productionMutateOnce = productionStaging?.mutateOnce;
767
+ const productionObserve = productionStaging?.observe;
768
+ const configurationReadCurrent = configuration?.readCurrent;
769
+ const configurationMutateOnce = configuration?.mutateOnce;
770
+ const configurationObserve = configuration?.observe;
771
+ const configured = configurationReadCurrent === undefined
772
+ || configurationMutateOnce === undefined
773
+ || configurationObserve === undefined
774
+ || productionReadCurrent === undefined
775
+ || productionMutateOnce === undefined
776
+ || productionObserve === undefined
777
+ ? productionStaging
778
+ : Object.freeze({
779
+ readCurrent: request => request.kind === "composer_set"
780
+ ? productionReadCurrent(request)
781
+ : configurationReadCurrent(request),
782
+ mutateOnce: request => request.kind === "composer_set"
783
+ ? productionMutateOnce(request)
784
+ : configurationMutateOnce(request),
785
+ observe: request => request.kind === "composer_set"
786
+ ? productionObserve(request)
787
+ : configurationObserve(request)
788
+ });
789
+ let result = Object.freeze({
790
+ ...production,
791
+ ...(configured === undefined ? {} : { staging: configured })
792
+ });
793
+ const augment = options.primitives?.(context);
794
+ if (augment !== undefined) {
795
+ // Provider integrations normally add one narrow method (for example the
796
+ // chooser handoff) to a production port. Replacing the whole nested port
797
+ // would silently discard the production Send observers or collector
798
+ // ownership logic, so merge at the method boundary instead.
799
+ result = mergePrimitivePorts(result, augment);
800
+ }
801
+ return result;
802
+ }
803
+ function mergePrimitivePorts(base, augment) {
804
+ const merge = (left, right) => {
805
+ if (left === undefined)
806
+ return right === undefined ? undefined : Object.freeze({ ...right });
807
+ if (right === undefined)
808
+ return left;
809
+ return Object.freeze({ ...left, ...right });
810
+ };
811
+ const staging = merge(base.staging, augment.staging);
812
+ const submission = merge(base.submission, augment.submission);
813
+ const collector = merge(base.collector, augment.collector);
814
+ const control = merge(base.control, augment.control);
815
+ const artifacts = merge(base.artifacts, augment.artifacts);
816
+ return Object.freeze({
817
+ ...(staging === undefined ? {} : { staging }),
818
+ ...(submission === undefined ? {} : { submission }),
819
+ ...(collector === undefined ? {} : { collector }),
820
+ ...(control === undefined ? {} : { control }),
821
+ ...(artifacts === undefined ? {} : { artifacts })
822
+ });
823
+ }
824
+ function bootstrapArgsForTarget(target) {
825
+ switch (target.type) {
826
+ case "new":
827
+ return Object.freeze({ url: CHATGPT_HOME, preferExistingTab: false });
828
+ case "selected_tab":
829
+ return Object.freeze({
830
+ existingTab: {
831
+ target: { type: "selected", host: "chatgpt" },
832
+ ifMissing: "block",
833
+ ifMultiple: "block",
834
+ requireChatGPT: true
835
+ }
836
+ });
837
+ case "tab_id":
838
+ return Object.freeze({
839
+ existingTab: {
840
+ target: { type: "tabId", tabId: target.tabId },
841
+ ifMissing: "block",
842
+ ifMultiple: "block",
843
+ requireChatGPT: true
844
+ }
845
+ });
846
+ case "conversation_id":
847
+ return Object.freeze({
848
+ url: new URL(`/c/${target.conversationId}`, CHATGPT_HOME).toString(),
849
+ existingTab: {
850
+ target: { type: "conversationId", conversationId: target.conversationId },
851
+ ifMissing: "open",
852
+ ifMultiple: "block",
853
+ requireChatGPT: true
854
+ }
855
+ });
856
+ case "url":
857
+ return Object.freeze({
858
+ url: target.url,
859
+ existingTab: {
860
+ target: { type: "url", url: target.url },
861
+ ifMissing: "open",
862
+ ifMultiple: "block",
863
+ requireChatGPT: true
864
+ }
865
+ });
866
+ }
867
+ }
868
+ function bootstrapEnvironment(env, target, recoveryTarget) {
869
+ const copy = { ...env };
870
+ // A selected-tab request must be proven through browser.tabs.selected. A
871
+ // cached arbitrary page would make `existingTab: selected` non-exact.
872
+ if (target.type === "new" || target.type === "selected_tab") {
873
+ delete copy.page;
874
+ }
875
+ if (recoveryTarget !== undefined) {
876
+ copy.expectedTabId = recoveryTarget.tabId;
877
+ if (copy.page !== undefined && tabIdFromPage(copy.page) !== recoveryTarget.tabId) {
878
+ delete copy.page;
879
+ }
880
+ }
881
+ return copy;
882
+ }
883
+ /**
884
+ * `attachChatGPTBrowser` also supports user-open-tab discovery. That is
885
+ * intentionally broader than an exact selected-tab request, so selected-tab
886
+ * capture is preflighted through the browser's selected-tab primitive and the
887
+ * resulting page is then handed back to attach as its cached exact page.
888
+ */
889
+ async function selectExactSelectedPage(browser) {
890
+ const tabs = browser?.tabs;
891
+ const selected = tabs?.selected;
892
+ if (typeof selected !== "function")
893
+ return undefined;
894
+ let page;
895
+ try {
896
+ const candidate = await Promise.resolve(selected.call(tabs));
897
+ if (candidate === undefined)
898
+ return undefined;
899
+ // `candidate` is a coordinated page returned by the coordinated browser.
900
+ // Validate its URL while retaining that wrapper so the read stays inside
901
+ // the browser actor; unwrap only after the exact selected-tab proof.
902
+ const url = await Promise.resolve(candidate.url?.()).catch(() => undefined);
903
+ if (!isChatGPTUrl(url))
904
+ return undefined;
905
+ page = unwrapCoordinatedPage(candidate);
906
+ }
907
+ catch {
908
+ return undefined;
909
+ }
910
+ if (page === undefined)
911
+ return undefined;
912
+ return page;
913
+ }
914
+ async function validateExactNavigation(page, target) {
915
+ if (target.type !== "conversation_id" && target.type !== "url")
916
+ return;
917
+ const actual = await Promise.resolve(page.url?.()).catch(() => undefined);
918
+ const actualCanonical = canonicalChatGPTUrl(actual);
919
+ if (actualCanonical === undefined)
920
+ throw new ChatGPTRuntimeFactoryError();
921
+ if (target.type === "url") {
922
+ const expected = canonicalChatGPTUrl(target.url);
923
+ if (expected === undefined || actualCanonical !== expected)
924
+ throw new ChatGPTRuntimeFactoryError();
925
+ return;
926
+ }
927
+ if (parseConversationId(actualCanonical) !== target.conversationId) {
928
+ throw new ChatGPTRuntimeFactoryError();
929
+ }
930
+ }
931
+ async function ensureSurface(page, surface, allowSwitch, timeoutMs, tabId) {
932
+ const before = detectExperienceFromSnapshot(await readSurfaceSnapshot(page));
933
+ if (before.experience === surface)
934
+ return;
935
+ // A fixed conversation/URL target must never be navigated to a surface home
936
+ // as a side effect of attachment. The exact target is more important than
937
+ // a best-effort surface guess; callers can explicitly switch it first.
938
+ if (!allowSwitch || before.experience === "unknown")
939
+ throw new ChatGPTRuntimeFactoryError();
940
+ const result = await openExperience(Object.freeze({ page, expectedTabId: tabId }), { experience: surface, timeoutMs });
941
+ if (!result.ok || result.data?.experience !== surface)
942
+ throw new ChatGPTRuntimeFactoryError();
943
+ const after = detectExperienceFromSnapshot(await readSurfaceSnapshot(page));
944
+ if (after.experience !== surface)
945
+ throw new ChatGPTRuntimeFactoryError();
946
+ }
947
+ async function observePage(page, operationId, target, evidenceDigest) {
948
+ return await observeBrowserPage(page, {
949
+ operationId,
950
+ target,
951
+ evidenceDigest,
952
+ responseContent: "metadata"
953
+ });
954
+ }
955
+ function observationTargetForRequest(request, browserId, tabId, target) {
956
+ const expectedConversationId = request.target.type === "conversation_id"
957
+ ? request.target.conversationId
958
+ : request.target.type === "url" ? parseConversationId(request.target.url) : undefined;
959
+ return {
960
+ providerId: "chatgpt",
961
+ browserId,
962
+ tabId,
963
+ coordinationScope: "process",
964
+ ...(target.type === "new" ? { targetLifecycle: "new_pending" } : {}),
965
+ ...(expectedConversationId === undefined ? {} : { expectedConversationId })
966
+ };
967
+ }
968
+ function observationTargetForBinding(target) {
969
+ return {
970
+ providerId: target.providerId,
971
+ browserId: target.browserId,
972
+ tabId: target.tabId,
973
+ coordinationScope: target.coordinationScope,
974
+ ...(target.targetLifecycle === undefined ? {} : { targetLifecycle: target.targetLifecycle }),
975
+ ...(target.conversationId === undefined ? {} : { expectedConversationId: target.conversationId })
976
+ };
977
+ }
978
+ function assertRecoveredBrowserIdentity(target, browserId, tabId) {
979
+ if (target.providerId !== "chatgpt" || target.browserId !== browserId || target.tabId !== tabId) {
980
+ throw new ChatGPTRuntimeFactoryError();
981
+ }
982
+ }
983
+ function canonicalChatGPTUrl(value) {
984
+ if (typeof value !== "string" || !isChatGPTUrl(value))
985
+ return undefined;
986
+ try {
987
+ const url = new URL(value);
988
+ if (url.search !== "" || url.hash !== "")
989
+ return undefined;
990
+ url.pathname = url.pathname.replace(/\/{2,}/gu, "/").replace(/\/$/u, "") || "/";
991
+ return url.toString();
992
+ }
993
+ catch {
994
+ return undefined;
995
+ }
996
+ }
997
+ function isAbsolutePath(value) {
998
+ return value.startsWith("/")
999
+ || /^[A-Za-z]:[\\/]/u.test(value)
1000
+ || value.startsWith("\\\\");
1001
+ }
1002
+ function hasTransferDestination(request) {
1003
+ const capture = request?.capture;
1004
+ if (capture === undefined || capture.artifacts !== "transfer" || typeof capture.outputDirectory !== "string") {
1005
+ return false;
1006
+ }
1007
+ return capture.outputDirectory.length > 0
1008
+ && capture.outputDirectory.length <= 4096
1009
+ && isAbsolutePath(capture.outputDirectory)
1010
+ && !/[\u0000-\u001f\u007f]/u.test(capture.outputDirectory);
1011
+ }
1012
+ function isOperationSurface(value) {
1013
+ return value === "chat" || value === "work";
1014
+ }
1015
+ function sameTargetRequest(left, right) {
1016
+ if (left.type !== right.type)
1017
+ return false;
1018
+ switch (left.type) {
1019
+ case "new":
1020
+ case "selected_tab":
1021
+ return true;
1022
+ case "tab_id":
1023
+ return right.type === "tab_id" && left.tabId === right.tabId;
1024
+ case "conversation_id":
1025
+ return right.type === "conversation_id" && left.conversationId === right.conversationId;
1026
+ case "url":
1027
+ return right.type === "url" && left.url === right.url;
1028
+ }
1029
+ }
1030
+ function readDataProperty(value, key) {
1031
+ try {
1032
+ let current = value;
1033
+ for (let depth = 0; current !== null && depth < 12; depth += 1) {
1034
+ const descriptor = Object.getOwnPropertyDescriptor(current, key);
1035
+ if (descriptor !== undefined) {
1036
+ if (!("value" in descriptor) || descriptor.get !== undefined || descriptor.set !== undefined) {
1037
+ throw new ChatGPTRuntimeFactoryError();
1038
+ }
1039
+ return descriptor.value;
1040
+ }
1041
+ const prototype = Object.getPrototypeOf(current);
1042
+ current = prototype !== null && (typeof prototype === "object" || typeof prototype === "function")
1043
+ ? prototype
1044
+ : null;
1045
+ }
1046
+ return undefined;
1047
+ }
1048
+ catch (error) {
1049
+ if (error instanceof ChatGPTRuntimeFactoryError)
1050
+ throw error;
1051
+ throw new ChatGPTRuntimeFactoryError();
1052
+ }
1053
+ }
1054
+ function snapshotCapabilities(value) {
1055
+ if (value === undefined) {
1056
+ return Object.freeze({
1057
+ stableProviderId: true,
1058
+ stableBrowserId: true,
1059
+ stableTabId: true,
1060
+ authoritativeTabClaim: false,
1061
+ concurrentTabs: false
1062
+ });
1063
+ }
1064
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
1065
+ throw new ChatGPTRuntimeFactoryError();
1066
+ }
1067
+ const keys = [
1068
+ "stableProviderId",
1069
+ "stableBrowserId",
1070
+ "stableTabId",
1071
+ "authoritativeTabClaim",
1072
+ "concurrentTabs"
1073
+ ];
1074
+ assertOwnDataKeys(value, keys);
1075
+ const result = {
1076
+ stableProviderId: readDataProperty(value, "stableProviderId") ?? false,
1077
+ stableBrowserId: readDataProperty(value, "stableBrowserId") ?? false,
1078
+ stableTabId: readDataProperty(value, "stableTabId") ?? false,
1079
+ authoritativeTabClaim: readDataProperty(value, "authoritativeTabClaim") ?? false,
1080
+ concurrentTabs: readDataProperty(value, "concurrentTabs") ?? false
1081
+ };
1082
+ if (Object.values(result).some(item => typeof item !== "boolean")) {
1083
+ throw new ChatGPTRuntimeFactoryError();
1084
+ }
1085
+ return Object.freeze(result);
1086
+ }
1087
+ function assertOwnDataKeys(value, allowed) {
1088
+ let descriptors;
1089
+ try {
1090
+ descriptors = Object.getOwnPropertyDescriptors(value);
1091
+ }
1092
+ catch {
1093
+ throw new ChatGPTRuntimeFactoryError();
1094
+ }
1095
+ const allowedSet = new Set(allowed);
1096
+ for (const key of Reflect.ownKeys(descriptors)) {
1097
+ if (typeof key !== "string" || !allowedSet.has(key))
1098
+ throw new ChatGPTRuntimeFactoryError();
1099
+ const descriptor = descriptors[key];
1100
+ if (descriptor === undefined || !("value" in descriptor) || descriptor.get !== undefined || descriptor.set !== undefined) {
1101
+ throw new ChatGPTRuntimeFactoryError();
1102
+ }
1103
+ }
1104
+ }
1105
+ function cloneSafeData(value, seen = new Set(), depth = 0) {
1106
+ if (value === null || typeof value !== "object") {
1107
+ if (typeof value === "function" || typeof value === "symbol" || typeof value === "bigint") {
1108
+ throw new ChatGPTRuntimeFactoryError();
1109
+ }
1110
+ return value;
1111
+ }
1112
+ if (depth > 16 || seen.has(value))
1113
+ throw new ChatGPTRuntimeFactoryError();
1114
+ seen.add(value);
1115
+ try {
1116
+ // Durable operation snapshots are string-keyed wire data. Silently
1117
+ // dropping a symbol would make the snapshot differ from the caller's
1118
+ // object while still accepting an unreviewed property.
1119
+ if (Reflect.ownKeys(value).some(key => typeof key !== "string")) {
1120
+ throw new ChatGPTRuntimeFactoryError();
1121
+ }
1122
+ if (Array.isArray(value)) {
1123
+ const result = value.map(item => cloneSafeData(item, seen, depth + 1));
1124
+ seen.delete(value);
1125
+ return Object.freeze(result);
1126
+ }
1127
+ const prototype = Object.getPrototypeOf(value);
1128
+ if (prototype !== Object.prototype && prototype !== null)
1129
+ throw new ChatGPTRuntimeFactoryError();
1130
+ const result = {};
1131
+ for (const key of Object.keys(value)) {
1132
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
1133
+ if (descriptor === undefined || !("value" in descriptor) || descriptor.get !== undefined || descriptor.set !== undefined) {
1134
+ throw new ChatGPTRuntimeFactoryError();
1135
+ }
1136
+ Object.defineProperty(result, key, {
1137
+ configurable: true,
1138
+ enumerable: true,
1139
+ value: cloneSafeData(descriptor.value, seen, depth + 1),
1140
+ writable: true
1141
+ });
1142
+ }
1143
+ seen.delete(value);
1144
+ return Object.freeze(result);
1145
+ }
1146
+ catch (error) {
1147
+ seen.delete(value);
1148
+ throw error;
1149
+ }
1150
+ }