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

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