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,431 @@
1
+ import { coordinateRuntimeEnv } from "./coordinated-browser.js";
2
+ /** The complete routing inventory is intentionally explicit and reviewable. */
3
+ export const COMMAND_ROUTING_INVENTORY = Object.freeze({
4
+ browserFree: Object.freeze([
5
+ "backend.version",
6
+ "backend.health",
7
+ "backend.capabilities",
8
+ "backend.hello",
9
+ "runner.plan",
10
+ "createReport",
11
+ "reports.create",
12
+ "reports.redact",
13
+ "reports.summarize",
14
+ "commands",
15
+ "describe",
16
+ "help",
17
+ "redacted-run-report",
18
+ "files.preflight",
19
+ "projects.sources.planAdd",
20
+ "operations.inspect"
21
+ ]),
22
+ /**
23
+ * These commands opt into the operation facade only when an operation
24
+ * identity is present. With no identity they retain their legacy path.
25
+ */
26
+ operationOptIn: Object.freeze([
27
+ "runner.run",
28
+ "runner.stream",
29
+ "responses.create",
30
+ "ask",
31
+ "askInThread",
32
+ "askWithFiles",
33
+ "askAndDownload",
34
+ "work.start",
35
+ "work.steer",
36
+ "operations.submit",
37
+ "operations.collect",
38
+ "operations.control"
39
+ ]),
40
+ /**
41
+ * Legacy commands retain their public workflow and now receive a
42
+ * coordinator-backed PageLike/BrowserLike facade. This is a method-level
43
+ * guarantee: it does not turn the whole command or its polling loop into a
44
+ * single actor callback.
45
+ */
46
+ legacyPageFacade: Object.freeze([
47
+ "runMessages",
48
+ "openThread",
49
+ "copyLatest",
50
+ "readLatest",
51
+ "downloadLatest",
52
+ "runPlan",
53
+ "new-ask-read",
54
+ "find-open-ask-read",
55
+ "find-open-copy-latest",
56
+ "attach-ask-read",
57
+ "ask-and-download",
58
+ "two-turn",
59
+ "doctor-upload",
60
+ "doctor",
61
+ "session.bootstrap",
62
+ "experience.detect",
63
+ "experience.open",
64
+ "configuration.inspect",
65
+ "configuration.apply",
66
+ "work.status",
67
+ "work.wait",
68
+ "work.readLatest",
69
+ "threads.new",
70
+ "threads.search",
71
+ "threads.open",
72
+ "messages.compose",
73
+ "messages.submit",
74
+ "messages.ask",
75
+ "messages.wait",
76
+ "messages.readLatest",
77
+ "messages.status",
78
+ "messages.stop",
79
+ "messages.waitAndRead",
80
+ "artifacts.listLatest",
81
+ "artifacts.wait",
82
+ "artifacts.downloadLatest",
83
+ "files.attach",
84
+ "files.downloadLatest",
85
+ "projects.sources.list",
86
+ "projects.sources.add",
87
+ "response.copy",
88
+ "modes.set",
89
+ "modes.get",
90
+ "tools.select"
91
+ ]),
92
+ /**
93
+ * Browser acquisition seams not covered by the facade remain explicit
94
+ * migration gaps. The current command surface has none; discovery is
95
+ * coordinated in browser/attach.ts.
96
+ */
97
+ legacyBrowserUnrouted: Object.freeze([]),
98
+ /**
99
+ * No backend or sequence command currently invokes
100
+ * routeCommandBrowserTransaction. Keep this category explicit so a future
101
+ * migration cannot silently turn a classification into an enforcement
102
+ * claim. A command may enter this list only after its own bounded DOM seam
103
+ * is implemented and tested.
104
+ */
105
+ coordinatorEntrypoint: Object.freeze([])
106
+ });
107
+ /**
108
+ * The operation-aware public dispatch seams currently implemented by the
109
+ * client/backend path. This is separate from the coordinator entrypoint
110
+ * inventory: these commands route into the operation facade, whose adapter
111
+ * owns its own short tab transactions, rather than calling the generic helper
112
+ * below around the whole command.
113
+ */
114
+ export const OPERATION_AWARE_DISPATCH_COMMANDS = Object.freeze([
115
+ ...COMMAND_ROUTING_INVENTORY.operationOptIn
116
+ ]);
117
+ /**
118
+ * Precise migration ownership for every legacy browser command. The values
119
+ * intentionally name source seams rather than claiming that the command is
120
+ * safe merely because it appears in the inventory.
121
+ */
122
+ const LEGACY_PAGE_FACADE_OWNERS = Object.freeze({
123
+ runMessages: "src/client.ts -> src/commands/sequence.ts",
124
+ openThread: "src/client.ts -> src/commands/sequence.ts -> src/commands/threads.ts",
125
+ copyLatest: "src/client.ts -> src/commands/response-actions.ts",
126
+ readLatest: "src/client.ts -> src/commands/messages.ts",
127
+ downloadLatest: "src/client.ts -> src/commands/files.ts",
128
+ runPlan: "src/client.ts -> src/commands/sequence.ts",
129
+ "new-ask-read": "src/client.ts -> src/commands/sequence.ts -> src/commands/messages.ts",
130
+ "find-open-ask-read": "src/client.ts -> src/commands/sequence.ts -> src/commands/messages.ts",
131
+ "find-open-copy-latest": "src/client.ts -> src/commands/sequence.ts -> src/commands/response-actions.ts",
132
+ "attach-ask-read": "src/client.ts -> src/commands/sequence.ts -> src/commands/files.ts",
133
+ "ask-and-download": "src/client.ts -> src/commands/sequence.ts -> src/commands/files.ts",
134
+ "two-turn": "src/client.ts -> src/commands/sequence.ts -> src/commands/messages.ts",
135
+ "doctor-upload": "src/client.ts -> src/commands/doctor.ts",
136
+ doctor: "src/commands/doctor.ts",
137
+ "session.bootstrap": "src/commands/session.ts",
138
+ "experience.detect": "src/commands/experience.ts",
139
+ "experience.open": "src/commands/experience.ts",
140
+ "configuration.inspect": "src/commands/configuration.ts",
141
+ "configuration.apply": "src/commands/configuration.ts",
142
+ "work.status": "src/commands/work.ts",
143
+ "work.wait": "src/commands/work.ts",
144
+ "work.readLatest": "src/commands/work.ts",
145
+ "threads.new": "src/commands/threads.ts",
146
+ "threads.search": "src/commands/threads.ts",
147
+ "threads.open": "src/commands/threads.ts",
148
+ "messages.compose": "src/commands/messages.ts",
149
+ "messages.submit": "src/commands/messages.ts",
150
+ "messages.ask": "src/commands/messages.ts",
151
+ "messages.wait": "src/commands/messages.ts",
152
+ "messages.readLatest": "src/commands/messages.ts",
153
+ "messages.status": "src/commands/messages.ts",
154
+ "messages.stop": "src/commands/messages.ts",
155
+ "messages.waitAndRead": "src/commands/messages.ts",
156
+ "artifacts.listLatest": "src/commands/artifacts.ts",
157
+ "artifacts.wait": "src/commands/artifacts.ts",
158
+ "artifacts.downloadLatest": "src/commands/artifacts.ts",
159
+ "files.attach": "src/commands/files.ts",
160
+ "files.downloadLatest": "src/commands/files.ts",
161
+ "projects.sources.list": "src/commands/project-sources.ts",
162
+ "projects.sources.add": "src/commands/project-sources.ts",
163
+ "response.copy": "src/commands/response-actions.ts",
164
+ "modes.set": "src/commands/modes.ts",
165
+ "modes.get": "src/commands/modes.ts",
166
+ "tools.select": "src/commands/modes.ts"
167
+ });
168
+ export const COMMAND_ROUTING_GAPS = Object.freeze(COMMAND_ROUTING_INVENTORY.legacyBrowserUnrouted.map(command => Object.freeze({
169
+ command,
170
+ status: "legacy_browser_unrouted",
171
+ owner: LEGACY_PAGE_FACADE_OWNERS[command] ?? "src/browser/attach.ts -> coordinated browser/page facade",
172
+ requiredSeam: "bounded_tab_transaction",
173
+ reason: "legacy_command_dispatch_has_no_operation_aware_tab_seam"
174
+ })));
175
+ const commandRoutingClasses = new Map();
176
+ for (const name of COMMAND_ROUTING_INVENTORY.browserFree) {
177
+ if (commandRoutingClasses.has(name))
178
+ throw new Error("Duplicate command routing inventory entry.");
179
+ commandRoutingClasses.set(name, "browser_free");
180
+ }
181
+ for (const name of COMMAND_ROUTING_INVENTORY.operationOptIn) {
182
+ if (commandRoutingClasses.has(name))
183
+ throw new Error("Duplicate command routing inventory entry.");
184
+ commandRoutingClasses.set(name, "operation_opt_in");
185
+ }
186
+ for (const name of COMMAND_ROUTING_INVENTORY.legacyPageFacade) {
187
+ if (commandRoutingClasses.has(name))
188
+ throw new Error("Duplicate command routing inventory entry.");
189
+ commandRoutingClasses.set(name, "legacy_page_facade");
190
+ }
191
+ for (const name of COMMAND_ROUTING_INVENTORY.legacyBrowserUnrouted) {
192
+ if (commandRoutingClasses.has(name))
193
+ throw new Error("Duplicate command routing inventory entry.");
194
+ commandRoutingClasses.set(name, "legacy_browser_unrouted");
195
+ }
196
+ for (const name of COMMAND_ROUTING_INVENTORY.coordinatorEntrypoint) {
197
+ if (commandRoutingClasses.has(name))
198
+ throw new Error("Duplicate command routing inventory entry.");
199
+ commandRoutingClasses.set(name, "coordinator_entrypoint");
200
+ }
201
+ /** Return undefined for a command that has not been explicitly classified. */
202
+ export function classifyCommandRouting(command) {
203
+ return commandRoutingClasses.get(command);
204
+ }
205
+ export function isBrowserFreeCommand(command) {
206
+ return classifyCommandRouting(command) === "browser_free";
207
+ }
208
+ export function isOperationOptInCommand(command) {
209
+ return classifyCommandRouting(command) === "operation_opt_in";
210
+ }
211
+ export function isLegacyBrowserUnroutedCommand(command) {
212
+ return classifyCommandRouting(command) === "legacy_browser_unrouted";
213
+ }
214
+ export function isLegacyPageFacadeCommand(command) {
215
+ return classifyCommandRouting(command) === "legacy_page_facade";
216
+ }
217
+ export function isCoordinatorEntrypointCommand(command) {
218
+ return classifyCommandRouting(command) === "coordinator_entrypoint";
219
+ }
220
+ export function commandRoutingDisposition(command) {
221
+ const classification = classifyCommandRouting(command);
222
+ if (classification === undefined || classification === "legacy_browser_unrouted")
223
+ return undefined;
224
+ return classification === "browser_free" ? "browser_free" : "coordinator_routed";
225
+ }
226
+ export function isCoordinatorRoutedCommand(command) {
227
+ return commandRoutingDisposition(command) === "coordinator_routed";
228
+ }
229
+ /**
230
+ * Prepare the environment for one command invocation.
231
+ *
232
+ * This is deliberately an adapter rather than a whole-command coordinator
233
+ * transaction. `coordinateRuntimeEnv` returns PageLike/BrowserLike facades
234
+ * whose individual browser calls acquire the process-scoped actor for a
235
+ * bounded operation and release it before the next await. Consequently a
236
+ * command may wait for generation, poll, write a journal, or invoke caller
237
+ * code without retaining a tab actor. The coordinated browser/page values
238
+ * are copied back into the supplied invocation environment so legacy command
239
+ * mutations (notably `session.bootstrap`) remain visible to later sequence
240
+ * steps. Calling this on an already-coordinated environment is idempotent
241
+ * through the wrapper caches.
242
+ */
243
+ export function routeCommandRuntimeEnv(command, env, options) {
244
+ const classification = classifyCommandRouting(command);
245
+ if (classification === undefined) {
246
+ throw new CommandRoutingError("unclassified_command");
247
+ }
248
+ if (classification === "legacy_browser_unrouted") {
249
+ throw new CommandRoutingError("legacy_command_unrouted");
250
+ }
251
+ if (classification === "browser_free") {
252
+ // Browser-free work must not even inspect provider members.
253
+ return env;
254
+ }
255
+ // An operation-opt-in command still has a supported legacy path when its
256
+ // caller omits an operation identity. Coordinate that path as well. When an
257
+ // operation identity is present the public client dispatches through the
258
+ // operation facade, whose capture boundary deliberately unwraps these
259
+ // compatibility facades before establishing its exact tab resource. This
260
+ // therefore cannot nest the operation's own coordinator transaction.
261
+ const coordinated = coordinateRuntimeEnv(env, options);
262
+ try {
263
+ if (coordinated.browser !== undefined && env.browser !== coordinated.browser) {
264
+ env.browser = coordinated.browser;
265
+ }
266
+ if (coordinated.page !== undefined && env.page !== coordinated.page) {
267
+ env.page = coordinated.page;
268
+ }
269
+ }
270
+ catch {
271
+ // A caller may pass a frozen compatibility snapshot. Preserve the
272
+ // command's ability to use the coordinated facade even though later
273
+ // legacy mutations cannot be committed to that snapshot.
274
+ return coordinated;
275
+ }
276
+ return env;
277
+ }
278
+ /**
279
+ * Run one command with the bounded legacy environment adapter.
280
+ *
281
+ * The callback is invoked outside any coordinator callback. The returned
282
+ * PageLike/BrowserLike values enforce short method-level transactions, so a
283
+ * caller callback or a generation/poll sleep cannot deadlock a nested actor.
284
+ */
285
+ export function routeCommandExecution(command, env, callback, options) {
286
+ if (typeof callback !== "function") {
287
+ return Promise.reject(new CommandRoutingError("coordinator_context_required"));
288
+ }
289
+ return Promise.resolve().then(() => callback(routeCommandRuntimeEnv(command, env, options)));
290
+ }
291
+ /** Stable error for callers that try to route without enough ownership proof. */
292
+ export class CommandRoutingError extends Error {
293
+ code;
294
+ constructor(code) {
295
+ super(commandRoutingErrorMessage(code));
296
+ this.name = "CommandRoutingError";
297
+ this.code = code;
298
+ }
299
+ }
300
+ /**
301
+ * Execute one already-bounded browser transaction for an explicit coordinator
302
+ * entrypoint. The callback is intentionally the transaction boundary, not
303
+ * the command or request boundary. Callers must keep generation waits,
304
+ * polling sleeps, file hashing/transfers, journal writes, and report/caller
305
+ * callbacks outside this helper.
306
+ */
307
+ export function routeCommandBrowserTransaction(command, context, callback) {
308
+ const classification = classifyCommandRouting(command);
309
+ if (classification === undefined) {
310
+ return Promise.reject(new CommandRoutingError("unclassified_command"));
311
+ }
312
+ if (typeof callback !== "function") {
313
+ return Promise.reject(new CommandRoutingError("coordinator_context_required"));
314
+ }
315
+ if (classification === "browser_free") {
316
+ return Promise.resolve().then(() => callback(undefined));
317
+ }
318
+ if (classification === "legacy_browser_unrouted") {
319
+ return Promise.reject(new CommandRoutingError("legacy_command_unrouted"));
320
+ }
321
+ if (classification === "legacy_page_facade") {
322
+ return Promise.reject(new CommandRoutingError("legacy_page_facade"));
323
+ }
324
+ if (classification === "operation_opt_in") {
325
+ return Promise.reject(new CommandRoutingError("operation_facade_managed"));
326
+ }
327
+ if (context === undefined) {
328
+ return Promise.reject(new CommandRoutingError("coordinator_context_required"));
329
+ }
330
+ const resource = context.runtimeContext.coordinatorResource();
331
+ if (!resource.exactTabOwnership || resource.resourceKind !== "tab") {
332
+ return Promise.reject(new CommandRoutingError("exact_ownership_unavailable"));
333
+ }
334
+ const requestOptions = {
335
+ // The immutable operation context is the sole owner authority. Do not
336
+ // allow a caller-supplied diagnostic owner to acquire the exact tab under
337
+ // different ownership metadata.
338
+ owner: context.runtimeContext.owner,
339
+ ...(context.priority === undefined ? {} : { priority: context.priority }),
340
+ ...(context.signal === undefined ? {} : { signal: context.signal }),
341
+ ...(context.deadlineAt === undefined ? {} : { deadlineAt: context.deadlineAt }),
342
+ ...(context.timeoutMs === undefined ? {} : { timeoutMs: context.timeoutMs }),
343
+ ...(context.label === undefined ? {} : { label: context.label })
344
+ };
345
+ if (resource.resourceKind === "tab") {
346
+ return context.coordinator.withTabTransaction(resource.resourceKey, requestOptions, callback);
347
+ }
348
+ // The exact-ownership check above currently makes this branch unreachable;
349
+ // retain the defensive fallback so a future resource implementation cannot
350
+ // accidentally run a tab transaction against the browser actor.
351
+ return Promise.reject(new CommandRoutingError("exact_ownership_unavailable"));
352
+ }
353
+ /** Return a readonly snapshot useful for diagnostics and inventory tests. */
354
+ export function commandRoutingInventory() {
355
+ return Object.freeze(Object.fromEntries(commandRoutingClasses.entries()));
356
+ }
357
+ /**
358
+ * Return true when a request carries one of the reserved caller-owned
359
+ * operation identity fields. This is intentionally structural and never
360
+ * reads or serializes prompt/instruction values.
361
+ */
362
+ export function hasOperationIdentity(value) {
363
+ try {
364
+ return hasOperationIdentityInner(value, new Set(), 0);
365
+ }
366
+ catch {
367
+ // Routing inspection is a caller-controlled boundary. Proxies, accessors,
368
+ // and descriptor traps must never be invoked merely to decide whether a
369
+ // legacy browser handler is safe. An unreadable shape could conceal an
370
+ // operation locator, so fail closed and keep it away from the legacy path.
371
+ return true;
372
+ }
373
+ }
374
+ /**
375
+ * Backend and sequence dispatchers call this before direct command dispatch.
376
+ * Legacy callers without an operation identity remain byte-for-byte on the
377
+ * existing path. An operation-aware request is allowed only for explicitly
378
+ * migrated facade commands (or browser-free diagnostics); all other browser
379
+ * commands fail before their handler can touch the browser.
380
+ */
381
+ export function assertOperationAwareDispatchAllowed(command, payload) {
382
+ const classification = classifyCommandRouting(command);
383
+ if (classification === undefined) {
384
+ throw new CommandRoutingError("unclassified_command");
385
+ }
386
+ if (!hasOperationIdentity(payload))
387
+ return;
388
+ if (classification === "browser_free"
389
+ || classification === "operation_opt_in"
390
+ || classification === "coordinator_entrypoint")
391
+ return;
392
+ throw new CommandRoutingError("operation_routing_unavailable");
393
+ }
394
+ function commandRoutingErrorMessage(code) {
395
+ switch (code) {
396
+ case "unclassified_command":
397
+ return "The command has no explicit browser-routing classification.";
398
+ case "coordinator_context_required":
399
+ return "An operation runtime context is required for a bounded coordinator browser transaction.";
400
+ case "exact_ownership_unavailable":
401
+ return "Exact claimed-tab ownership is required for a bounded coordinator browser transaction.";
402
+ case "legacy_command_unrouted":
403
+ return "This legacy browser command has no bounded coordinator transaction seam.";
404
+ case "legacy_page_facade":
405
+ return "This legacy browser command coordinates individual PageLike/BrowserLike calls and cannot be wrapped as a whole-command transaction.";
406
+ case "operation_facade_managed":
407
+ return "This operation-aware command is managed by its operation facade and cannot be wrapped as a whole-command transaction.";
408
+ case "operation_routing_unavailable":
409
+ return "An operation identity was supplied to a legacy browser command without an operation-aware dispatch seam.";
410
+ }
411
+ }
412
+ function hasOperationIdentityInner(value, seen, depth) {
413
+ if (value === null || typeof value !== "object")
414
+ return false;
415
+ if (seen.has(value))
416
+ return false;
417
+ if (depth > 8)
418
+ return true;
419
+ seen.add(value);
420
+ const descriptors = Object.getOwnPropertyDescriptors(value);
421
+ for (const [key, descriptor] of Object.entries(descriptors)) {
422
+ if (key === "operationId" || key === "controlActionId" || key === "handle" || key === "parentHandle") {
423
+ return true;
424
+ }
425
+ if (!("value" in descriptor))
426
+ return true;
427
+ if (hasOperationIdentityInner(descriptor.value, seen, depth + 1))
428
+ return true;
429
+ }
430
+ return false;
431
+ }
@@ -0,0 +1,24 @@
1
+ import type { BrowserLike, PageLike, RuntimeEnv } from "../types.js";
2
+ import { type BrowserResourceKey, type CoordinatorOwner, type ProcessTabCoordinator } from "./tab-coordinator.js";
3
+ export declare const MAX_BROWSER_TAB_CANDIDATES = 256;
4
+ export type CoordinatedBrowserOptions = Readonly<{
5
+ coordinator?: ProcessTabCoordinator;
6
+ owner?: CoordinatorOwner;
7
+ }>;
8
+ export declare class CoordinatedBrowserError extends Error {
9
+ readonly code = "coordinated_browser_invalid";
10
+ constructor(message: string, options?: {
11
+ cause?: unknown;
12
+ });
13
+ }
14
+ export declare function coordinatedBrowserResource(browser?: BrowserLike): Readonly<{
15
+ kind: "browser";
16
+ key: BrowserResourceKey;
17
+ }>;
18
+ /** Wrap a browser using one browser-wide actor; no per-tab capability is inferred. */
19
+ export declare function createCoordinatedBrowser(browser: BrowserLike, options?: CoordinatedBrowserOptions): BrowserLike;
20
+ /** Wrap one initial/captured page with the browser-wide legacy actor. */
21
+ export declare function createCoordinatedPageForBrowser(page: PageLike, browser?: BrowserLike, options?: CoordinatedBrowserOptions): PageLike;
22
+ /** Make a fresh RuntimeEnv snapshot with only browser/page values coordinated. */
23
+ export declare function coordinateRuntimeEnv(env: RuntimeEnv, options?: CoordinatedBrowserOptions): RuntimeEnv;
24
+ export declare function unwrapCoordinatedBrowser(browser: BrowserLike): BrowserLike;