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,142 @@
1
+ import { type BrowserResourceKey, type TabResourceKey } from "./tab-coordinator.js";
2
+ export type OperationRuntimeCoordinationScope = "tab" | "provider";
3
+ export type OperationRuntimeClaimReason = "missing" | "unsupported" | "unverifiable";
4
+ export type OperationRuntimeAuthoritativeClaim = Readonly<{
5
+ status: "available";
6
+ token: string;
7
+ epoch: number;
8
+ } | {
9
+ status: "unavailable";
10
+ reason: OperationRuntimeClaimReason;
11
+ }>;
12
+ /**
13
+ * Capabilities are provider-advertised facts. Missing fields intentionally
14
+ * mean false; an older or partial provider must never be treated as
15
+ * concurrent-tab safe by inference.
16
+ */
17
+ export type OperationRuntimeCapabilities = Readonly<{
18
+ stableProviderId: boolean;
19
+ stableBrowserId: boolean;
20
+ stableTabId: boolean;
21
+ authoritativeTabClaim: boolean;
22
+ concurrentTabs: boolean;
23
+ }>;
24
+ export type OperationRuntimeOwner = Readonly<{
25
+ backendSessionId: string;
26
+ operationId: string;
27
+ }>;
28
+ export type OperationRuntimeContextInput<Page extends object = object> = Readonly<{
29
+ providerId?: string;
30
+ browserId?: string;
31
+ tabId?: string;
32
+ page: Page;
33
+ authoritativeClaim?: OperationRuntimeAuthoritativeClaim;
34
+ owner: OperationRuntimeOwner;
35
+ capabilities?: Partial<OperationRuntimeCapabilities>;
36
+ targetBindingDigest: string;
37
+ /** Reject construction if an exact tab-scoped owner cannot be proven. */
38
+ requireExactTabOwnership?: boolean;
39
+ }>;
40
+ export type OperationRuntimeContextChildOptions = Readonly<{
41
+ operationId?: string;
42
+ targetBindingDigest?: string;
43
+ }>;
44
+ export type OperationRuntimeAffinityObservation = Readonly<{
45
+ tabId?: string;
46
+ authoritativeClaim?: OperationRuntimeAuthoritativeClaim;
47
+ }>;
48
+ export type OperationRuntimeResourceSelection = Readonly<{
49
+ scope: OperationRuntimeCoordinationScope;
50
+ resourceKind: "tab" | "browser";
51
+ resourceKey: TabResourceKey | BrowserResourceKey;
52
+ exactTabOwnership: boolean;
53
+ downgraded: boolean;
54
+ downgradeReasons: readonly OperationRuntimeDowngradeReason[];
55
+ }>;
56
+ export type OperationRuntimeDowngradeReason = "provider_identity_unavailable" | "browser_identity_unavailable" | "tab_identity_unavailable" | "provider_identity_not_advertised" | "browser_identity_not_advertised" | "tab_identity_not_advertised" | "authoritative_claim_unavailable" | "authoritative_claim_not_advertised" | "concurrent_tabs_not_advertised";
57
+ export type OperationRuntimeContextDiagnostics = Readonly<{
58
+ status: "ready";
59
+ scope: OperationRuntimeCoordinationScope;
60
+ resourceKind: "tab" | "browser";
61
+ exactTabOwnership: boolean;
62
+ downgraded: boolean;
63
+ downgradeReasons: readonly OperationRuntimeDowngradeReason[];
64
+ identities: Readonly<{
65
+ provider: "available" | "unavailable";
66
+ browser: "available" | "unavailable";
67
+ tab: "available" | "unavailable";
68
+ }>;
69
+ page: "bound";
70
+ authoritativeClaim: "available" | "unavailable";
71
+ owner: "bound";
72
+ targetBinding: "bound";
73
+ }>;
74
+ export type OperationRuntimeContextCapture<Page extends object = object> = Readonly<{
75
+ page: Page;
76
+ providerId?: string;
77
+ browserId?: string;
78
+ tabId?: string;
79
+ authoritativeClaim: OperationRuntimeAuthoritativeClaim;
80
+ owner: OperationRuntimeOwner;
81
+ capabilities: OperationRuntimeCapabilities;
82
+ coordinationScope: OperationRuntimeCoordinationScope;
83
+ targetBindingDigest: string;
84
+ resource: OperationRuntimeResourceSelection;
85
+ assertPageAffinity: (page: unknown, observation?: OperationRuntimeAffinityObservation) => OperationRuntimeAffinityResult;
86
+ }>;
87
+ export type OperationRuntimeAffinityResult = Readonly<{
88
+ pageMatches: true;
89
+ tabMatches: boolean;
90
+ claimMatches: boolean;
91
+ exactTabOwnership: boolean;
92
+ }>;
93
+ export type OperationRuntimeContextErrorCode = "invalid_context" | "exact_ownership_unavailable" | "page_affinity_mismatch" | "tab_affinity_mismatch" | "claim_drift";
94
+ /** Error diagnostics contain statuses only; no IDs, digests, claim tokens, or page values. */
95
+ export declare class OperationRuntimeContextError extends Error {
96
+ readonly code: OperationRuntimeContextErrorCode;
97
+ readonly diagnostics?: OperationRuntimeContextDiagnostics;
98
+ constructor(code: OperationRuntimeContextErrorCode, message: string, diagnostics?: OperationRuntimeContextDiagnostics);
99
+ }
100
+ /**
101
+ * Immutable operation-scoped browser context. A context is intentionally
102
+ * independent from RuntimeEnv and can be passed to a future command adapter
103
+ * without changing legacy command behavior.
104
+ */
105
+ export declare class OperationRuntimeContext<Page extends object = object> {
106
+ #private;
107
+ readonly providerId: string | undefined;
108
+ readonly browserId: string | undefined;
109
+ readonly tabId: string | undefined;
110
+ readonly page: Page;
111
+ readonly authoritativeClaim: OperationRuntimeAuthoritativeClaim;
112
+ readonly owner: OperationRuntimeOwner;
113
+ readonly capabilities: OperationRuntimeCapabilities;
114
+ readonly coordinationScope: OperationRuntimeCoordinationScope;
115
+ readonly targetBindingDigest: string;
116
+ private constructor();
117
+ /** Fail-closed context construction with deterministic provider-wide fallback. */
118
+ static create<Page extends object = object>(input: OperationRuntimeContextInput<Page>): OperationRuntimeContext<Page>;
119
+ /** Alias for adapters that prefer a factory-style name. */
120
+ static bind<Page extends object = object>(input: OperationRuntimeContextInput<Page>): OperationRuntimeContext<Page>;
121
+ /** The exact coordinator actor resource; never returns tab scope after downgrade. */
122
+ coordinatorResource(): OperationRuntimeResourceSelection;
123
+ /** Status-only diagnostics suitable for logs and blockers. */
124
+ diagnostics(): OperationRuntimeContextDiagnostics;
125
+ /**
126
+ * Create a child operation view without permitting a new page, tab, claim, or
127
+ * capability to be smuggled into the existing ownership domain.
128
+ */
129
+ child(options?: OperationRuntimeContextChildOptions): OperationRuntimeContext<Page>;
130
+ /**
131
+ * Capture an immutable page-aware view. It retains the same page identity;
132
+ * it cannot be used to replace this context's page or ownership metadata.
133
+ */
134
+ capture(): OperationRuntimeContextCapture<Page>;
135
+ /**
136
+ * Verify both page object identity and all available stable ownership
137
+ * evidence. A context with an exact tab binding requires an explicit
138
+ * observed stable tab ID and (when bound) the current authoritative claim.
139
+ */
140
+ assertPageAffinity(page: unknown, observation?: OperationRuntimeAffinityObservation): OperationRuntimeAffinityResult;
141
+ }
142
+ export declare function createOperationRuntimeContext<Page extends object = object>(input: OperationRuntimeContextInput<Page>): OperationRuntimeContext<Page>;
@@ -0,0 +1,410 @@
1
+ import { createBrowserResourceKey, createTabResourceKey } from "./tab-coordinator.js";
2
+ /**
3
+ * An operation context owns one page reference for the lifetime of an
4
+ * operation. It deliberately does not accept a RuntimeEnv: a mutable
5
+ * `RuntimeEnv.page` can be replaced by another request while a command is
6
+ * awaiting a browser call. Contexts instead capture a page once and expose
7
+ * an explicit affinity check at every adapter boundary.
8
+ */
9
+ const INVALID_ID_VALUES = new Set(["unknown", "undefined", "null", "n/a", "na"]);
10
+ const UNKNOWN_PROVIDER_ID = "operation-context-provider-identity-unavailable";
11
+ const UNKNOWN_BROWSER_ID = "operation-context-browser-identity-unavailable";
12
+ const MAX_ID_LENGTH = 512;
13
+ const MAX_DIGEST_LENGTH = 1024;
14
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
15
+ const HMAC_DIGEST_PATTERN = /^hmac-sha256:[0-9a-f]{64}$/;
16
+ /** Error diagnostics contain statuses only; no IDs, digests, claim tokens, or page values. */
17
+ export class OperationRuntimeContextError extends Error {
18
+ code;
19
+ diagnostics;
20
+ constructor(code, message, diagnostics) {
21
+ super(message);
22
+ this.name = "OperationRuntimeContextError";
23
+ this.code = code;
24
+ if (diagnostics !== undefined)
25
+ this.diagnostics = diagnostics;
26
+ }
27
+ }
28
+ function isRecord(value) {
29
+ if (value === null || typeof value !== "object" || Array.isArray(value))
30
+ return false;
31
+ try {
32
+ const prototype = Object.getPrototypeOf(value);
33
+ return prototype === Object.prototype || prototype === null;
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ function assertRecord(value, label) {
40
+ if (!isRecord(value))
41
+ throw invalid(`${label} must be a plain object`);
42
+ try {
43
+ for (const key of Object.keys(value)) {
44
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
45
+ if (descriptor?.get !== undefined || descriptor?.set !== undefined) {
46
+ throw invalid(`${label} must not contain accessor properties`);
47
+ }
48
+ }
49
+ }
50
+ catch (error) {
51
+ if (error instanceof OperationRuntimeContextError)
52
+ throw error;
53
+ throw invalid(`${label} must be a stable plain object`);
54
+ }
55
+ }
56
+ function assertExactKeys(value, label, allowed) {
57
+ const allowedSet = new Set(allowed);
58
+ try {
59
+ for (const key of Object.keys(value)) {
60
+ if (key.startsWith("_") || !allowedSet.has(key)) {
61
+ throw invalid(`${label} contains an unsupported field`);
62
+ }
63
+ }
64
+ }
65
+ catch (error) {
66
+ if (error instanceof OperationRuntimeContextError)
67
+ throw error;
68
+ throw invalid(`${label} contains unsupported fields`);
69
+ }
70
+ }
71
+ function invalid(message) {
72
+ // Do not interpolate caller-controlled values into context errors. This
73
+ // protects diagnostics from leaking IDs, paths, digests, or page metadata.
74
+ return new OperationRuntimeContextError("invalid_context", message);
75
+ }
76
+ function validateBoolean(value, label) {
77
+ if (typeof value !== "boolean")
78
+ throw invalid(`${label} must be a boolean`);
79
+ return value;
80
+ }
81
+ function validateRequiredString(value, label, maxLength = MAX_ID_LENGTH) {
82
+ if (typeof value !== "string")
83
+ throw invalid(`${label} must be a string`);
84
+ const normalized = value.trim();
85
+ if (normalized.length === 0 ||
86
+ normalized.length > maxLength ||
87
+ INVALID_ID_VALUES.has(normalized.toLowerCase()) ||
88
+ /[\u0000-\u001f\u007f]/u.test(normalized)) {
89
+ throw invalid(`${label} must be a known stable value`);
90
+ }
91
+ return normalized;
92
+ }
93
+ function validateTargetBindingDigest(value, label = "targetBindingDigest") {
94
+ const digest = validateRequiredString(value, label, MAX_DIGEST_LENGTH);
95
+ if (!HMAC_DIGEST_PATTERN.test(digest))
96
+ throw invalid(`${label} must be a canonical HMAC digest`);
97
+ return digest;
98
+ }
99
+ function normalizeOptionalIdentity(value, label) {
100
+ if (value === undefined || value === null)
101
+ return undefined;
102
+ if (typeof value !== "string")
103
+ throw invalid(`${label} must be a string when present`);
104
+ const normalized = value.trim();
105
+ if (normalized.length === 0 || INVALID_ID_VALUES.has(normalized.toLowerCase()))
106
+ return undefined;
107
+ if (normalized.length > MAX_ID_LENGTH || /[\u0000-\u001f\u007f]/u.test(normalized)) {
108
+ throw invalid(`${label} is not a valid stable identity`);
109
+ }
110
+ return normalized;
111
+ }
112
+ function normalizeCapabilities(value) {
113
+ if (value === undefined) {
114
+ return Object.freeze({
115
+ stableProviderId: false,
116
+ stableBrowserId: false,
117
+ stableTabId: false,
118
+ authoritativeTabClaim: false,
119
+ concurrentTabs: false
120
+ });
121
+ }
122
+ assertRecord(value, "capabilities");
123
+ assertExactKeys(value, "capabilities", [
124
+ "stableProviderId",
125
+ "stableBrowserId",
126
+ "stableTabId",
127
+ "authoritativeTabClaim",
128
+ "concurrentTabs"
129
+ ]);
130
+ return Object.freeze({
131
+ stableProviderId: value.stableProviderId === undefined ? false : validateBoolean(value.stableProviderId, "capabilities.stableProviderId"),
132
+ stableBrowserId: value.stableBrowserId === undefined ? false : validateBoolean(value.stableBrowserId, "capabilities.stableBrowserId"),
133
+ stableTabId: value.stableTabId === undefined ? false : validateBoolean(value.stableTabId, "capabilities.stableTabId"),
134
+ authoritativeTabClaim: value.authoritativeTabClaim === undefined ? false : validateBoolean(value.authoritativeTabClaim, "capabilities.authoritativeTabClaim"),
135
+ concurrentTabs: value.concurrentTabs === undefined ? false : validateBoolean(value.concurrentTabs, "capabilities.concurrentTabs")
136
+ });
137
+ }
138
+ function normalizeOwner(value) {
139
+ assertRecord(value, "owner");
140
+ assertExactKeys(value, "owner", ["backendSessionId", "operationId"]);
141
+ const backendSessionId = validateRequiredString(value.backendSessionId, "owner.backendSessionId");
142
+ const operationId = validateRequiredString(value.operationId, "owner.operationId");
143
+ if (!UUID_PATTERN.test(backendSessionId))
144
+ throw invalid("owner.backendSessionId must be a canonical UUID");
145
+ if (!UUID_PATTERN.test(operationId))
146
+ throw invalid("owner.operationId must be a canonical UUID");
147
+ return Object.freeze({
148
+ backendSessionId,
149
+ operationId
150
+ });
151
+ }
152
+ function normalizeClaim(value) {
153
+ if (value === undefined)
154
+ return Object.freeze({ status: "unavailable", reason: "missing" });
155
+ assertRecord(value, "authoritativeClaim");
156
+ if (value.status === "available") {
157
+ assertExactKeys(value, "authoritativeClaim", ["status", "token", "epoch"]);
158
+ if (!Number.isSafeInteger(value.epoch) || value.epoch < 0) {
159
+ throw invalid("authoritativeClaim.epoch must be a non-negative safe integer");
160
+ }
161
+ return Object.freeze({
162
+ status: "available",
163
+ token: validateRequiredString(value.token, "authoritativeClaim.token"),
164
+ epoch: value.epoch
165
+ });
166
+ }
167
+ if (value.status === "unavailable") {
168
+ assertExactKeys(value, "authoritativeClaim", ["status", "reason"]);
169
+ if (value.reason !== "missing" && value.reason !== "unsupported" && value.reason !== "unverifiable") {
170
+ throw invalid("authoritativeClaim.reason is invalid");
171
+ }
172
+ return Object.freeze({ status: "unavailable", reason: value.reason });
173
+ }
174
+ throw invalid("authoritativeClaim.status is invalid");
175
+ }
176
+ function claimMatches(expected, observed) {
177
+ if (expected.status === "unavailable")
178
+ return true;
179
+ return observed?.status === "available"
180
+ && observed.token === expected.token
181
+ && observed.epoch === expected.epoch;
182
+ }
183
+ function cloneClaim(claim) {
184
+ return claim.status === "available"
185
+ ? Object.freeze({ status: "available", token: claim.token, epoch: claim.epoch })
186
+ : Object.freeze({ status: "unavailable", reason: claim.reason });
187
+ }
188
+ function createFallbackBrowserKey(providerId, browserId) {
189
+ if (providerId === undefined || browserId === undefined) {
190
+ return createBrowserResourceKey(UNKNOWN_PROVIDER_ID, UNKNOWN_BROWSER_ID);
191
+ }
192
+ return createBrowserResourceKey(providerId, browserId);
193
+ }
194
+ function computeResource(providerId, browserId, tabId, claim, capabilities) {
195
+ const providerAvailable = providerId !== undefined;
196
+ const browserAvailable = browserId !== undefined;
197
+ const tabAvailable = tabId !== undefined;
198
+ const reasons = [];
199
+ if (!providerAvailable)
200
+ reasons.push("provider_identity_unavailable");
201
+ if (!browserAvailable)
202
+ reasons.push("browser_identity_unavailable");
203
+ if (!tabAvailable)
204
+ reasons.push("tab_identity_unavailable");
205
+ if (!capabilities.stableProviderId)
206
+ reasons.push("provider_identity_not_advertised");
207
+ if (!capabilities.stableBrowserId)
208
+ reasons.push("browser_identity_not_advertised");
209
+ if (!capabilities.stableTabId)
210
+ reasons.push("tab_identity_not_advertised");
211
+ if (claim.status !== "available")
212
+ reasons.push("authoritative_claim_unavailable");
213
+ if (!capabilities.authoritativeTabClaim)
214
+ reasons.push("authoritative_claim_not_advertised");
215
+ if (!capabilities.concurrentTabs)
216
+ reasons.push("concurrent_tabs_not_advertised");
217
+ const exactTabOwnership = providerAvailable
218
+ && browserAvailable
219
+ && tabAvailable
220
+ && capabilities.stableProviderId
221
+ && capabilities.stableBrowserId
222
+ && capabilities.stableTabId
223
+ && capabilities.authoritativeTabClaim
224
+ && claim.status === "available";
225
+ if (exactTabOwnership && capabilities.concurrentTabs) {
226
+ return Object.freeze({
227
+ scope: "tab",
228
+ resourceKind: "tab",
229
+ resourceKey: createTabResourceKey(providerId, browserId, tabId),
230
+ exactTabOwnership: true,
231
+ downgraded: false,
232
+ downgradeReasons: Object.freeze([])
233
+ });
234
+ }
235
+ return Object.freeze({
236
+ scope: "provider",
237
+ resourceKind: "browser",
238
+ resourceKey: createFallbackBrowserKey(providerId, browserId),
239
+ exactTabOwnership,
240
+ downgraded: true,
241
+ downgradeReasons: Object.freeze([...new Set(reasons)])
242
+ });
243
+ }
244
+ function redactedDiagnostics(resource, providerId, browserId, tabId, claim) {
245
+ return Object.freeze({
246
+ status: "ready",
247
+ scope: resource.scope,
248
+ resourceKind: resource.resourceKind,
249
+ exactTabOwnership: resource.exactTabOwnership,
250
+ downgraded: resource.downgraded,
251
+ downgradeReasons: Object.freeze([...resource.downgradeReasons]),
252
+ identities: Object.freeze({
253
+ provider: providerId === undefined ? "unavailable" : "available",
254
+ browser: browserId === undefined ? "unavailable" : "available",
255
+ tab: tabId === undefined ? "unavailable" : "available"
256
+ }),
257
+ page: "bound",
258
+ authoritativeClaim: claim.status,
259
+ owner: "bound",
260
+ targetBinding: "bound"
261
+ });
262
+ }
263
+ function validateAffinityObservation(value) {
264
+ if (value === undefined)
265
+ return Object.freeze({});
266
+ assertRecord(value, "affinity observation");
267
+ assertExactKeys(value, "affinity observation", ["tabId", "authoritativeClaim"]);
268
+ const tabId = normalizeOptionalIdentity(value.tabId, "affinity observation.tabId");
269
+ const authoritativeClaim = value.authoritativeClaim === undefined
270
+ ? undefined
271
+ : normalizeClaim(value.authoritativeClaim);
272
+ return Object.freeze({
273
+ ...(tabId === undefined ? {} : { tabId }),
274
+ ...(authoritativeClaim === undefined ? {} : { authoritativeClaim })
275
+ });
276
+ }
277
+ /**
278
+ * Immutable operation-scoped browser context. A context is intentionally
279
+ * independent from RuntimeEnv and can be passed to a future command adapter
280
+ * without changing legacy command behavior.
281
+ */
282
+ export class OperationRuntimeContext {
283
+ providerId;
284
+ browserId;
285
+ tabId;
286
+ page;
287
+ authoritativeClaim;
288
+ owner;
289
+ capabilities;
290
+ coordinationScope;
291
+ targetBindingDigest;
292
+ #resourceSelection;
293
+ #diagnosticsView;
294
+ constructor(providerId, browserId, tabId, page, authoritativeClaim, owner, capabilities, targetBindingDigest, requireExactTabOwnership) {
295
+ this.providerId = providerId;
296
+ this.browserId = browserId;
297
+ this.tabId = tabId;
298
+ this.page = page;
299
+ this.authoritativeClaim = cloneClaim(authoritativeClaim);
300
+ this.owner = Object.freeze({ ...owner });
301
+ this.capabilities = Object.freeze({ ...capabilities });
302
+ this.targetBindingDigest = targetBindingDigest;
303
+ this.#resourceSelection = computeResource(providerId, browserId, tabId, this.authoritativeClaim, this.capabilities);
304
+ this.coordinationScope = this.#resourceSelection.scope;
305
+ this.#diagnosticsView = redactedDiagnostics(this.#resourceSelection, providerId, browserId, tabId, this.authoritativeClaim);
306
+ if (requireExactTabOwnership && !this.#resourceSelection.exactTabOwnership) {
307
+ throw new OperationRuntimeContextError("exact_ownership_unavailable", "Exact tab ownership is unavailable for this context.", this.#diagnosticsView);
308
+ }
309
+ Object.freeze(this);
310
+ }
311
+ /** Fail-closed context construction with deterministic provider-wide fallback. */
312
+ static create(input) {
313
+ assertRecord(input, "operation context");
314
+ assertExactKeys(input, "operation context", [
315
+ "providerId",
316
+ "browserId",
317
+ "tabId",
318
+ "page",
319
+ "authoritativeClaim",
320
+ "owner",
321
+ "capabilities",
322
+ "targetBindingDigest",
323
+ "requireExactTabOwnership"
324
+ ]);
325
+ if (typeof input.page !== "object" || input.page === null || Array.isArray(input.page)) {
326
+ throw invalid("page must be a non-null object");
327
+ }
328
+ const requireExactTabOwnership = input.requireExactTabOwnership === undefined
329
+ ? false
330
+ : validateBoolean(input.requireExactTabOwnership, "requireExactTabOwnership");
331
+ const context = new OperationRuntimeContext(normalizeOptionalIdentity(input.providerId, "providerId"), normalizeOptionalIdentity(input.browserId, "browserId"), normalizeOptionalIdentity(input.tabId, "tabId"), input.page, normalizeClaim(input.authoritativeClaim), normalizeOwner(input.owner), normalizeCapabilities(input.capabilities), validateTargetBindingDigest(input.targetBindingDigest), requireExactTabOwnership);
332
+ return context;
333
+ }
334
+ /** Alias for adapters that prefer a factory-style name. */
335
+ static bind(input) {
336
+ return OperationRuntimeContext.create(input);
337
+ }
338
+ /** The exact coordinator actor resource; never returns tab scope after downgrade. */
339
+ coordinatorResource() {
340
+ return this.#resourceSelection;
341
+ }
342
+ /** Status-only diagnostics suitable for logs and blockers. */
343
+ diagnostics() {
344
+ return this.#diagnosticsView;
345
+ }
346
+ /**
347
+ * Create a child operation view without permitting a new page, tab, claim, or
348
+ * capability to be smuggled into the existing ownership domain.
349
+ */
350
+ child(options = {}) {
351
+ assertRecord(options, "child options");
352
+ assertExactKeys(options, "child options", ["operationId", "targetBindingDigest"]);
353
+ const operationId = options.operationId === undefined
354
+ ? this.owner.operationId
355
+ : validateRequiredString(options.operationId, "child options.operationId");
356
+ if (!UUID_PATTERN.test(operationId))
357
+ throw invalid("child options.operationId must be a canonical UUID");
358
+ const targetBindingDigest = options.targetBindingDigest === undefined
359
+ ? this.targetBindingDigest
360
+ : validateTargetBindingDigest(options.targetBindingDigest, "child options.targetBindingDigest");
361
+ return new OperationRuntimeContext(this.providerId, this.browserId, this.tabId, this.page, this.authoritativeClaim, Object.freeze({ backendSessionId: this.owner.backendSessionId, operationId }), this.capabilities, targetBindingDigest, false);
362
+ }
363
+ /**
364
+ * Capture an immutable page-aware view. It retains the same page identity;
365
+ * it cannot be used to replace this context's page or ownership metadata.
366
+ */
367
+ capture() {
368
+ const context = this;
369
+ return Object.freeze({
370
+ page: this.page,
371
+ ...(this.providerId === undefined ? {} : { providerId: this.providerId }),
372
+ ...(this.browserId === undefined ? {} : { browserId: this.browserId }),
373
+ ...(this.tabId === undefined ? {} : { tabId: this.tabId }),
374
+ authoritativeClaim: cloneClaim(this.authoritativeClaim),
375
+ owner: Object.freeze({ ...this.owner }),
376
+ capabilities: Object.freeze({ ...this.capabilities }),
377
+ coordinationScope: this.coordinationScope,
378
+ targetBindingDigest: this.targetBindingDigest,
379
+ resource: this.#resourceSelection,
380
+ assertPageAffinity: (page, observation) => context.assertPageAffinity(page, observation)
381
+ });
382
+ }
383
+ /**
384
+ * Verify both page object identity and all available stable ownership
385
+ * evidence. A context with an exact tab binding requires an explicit
386
+ * observed stable tab ID and (when bound) the current authoritative claim.
387
+ */
388
+ assertPageAffinity(page, observation) {
389
+ if (page !== this.page) {
390
+ throw new OperationRuntimeContextError("page_affinity_mismatch", "The supplied page is not the page bound to this operation context.", this.#diagnosticsView);
391
+ }
392
+ const observed = validateAffinityObservation(observation);
393
+ const needsTabEvidence = this.tabId !== undefined;
394
+ if (needsTabEvidence && (observed.tabId === undefined || observed.tabId !== this.tabId)) {
395
+ throw new OperationRuntimeContextError("tab_affinity_mismatch", "The supplied tab evidence does not match the operation context.", this.#diagnosticsView);
396
+ }
397
+ if (this.authoritativeClaim.status === "available" && !claimMatches(this.authoritativeClaim, observed.authoritativeClaim)) {
398
+ throw new OperationRuntimeContextError("claim_drift", "The authoritative tab claim no longer matches the operation context.", this.#diagnosticsView);
399
+ }
400
+ return Object.freeze({
401
+ pageMatches: true,
402
+ tabMatches: !needsTabEvidence || observed.tabId === this.tabId,
403
+ claimMatches: claimMatches(this.authoritativeClaim, observed.authoritativeClaim),
404
+ exactTabOwnership: this.#resourceSelection.exactTabOwnership
405
+ });
406
+ }
407
+ }
408
+ export function createOperationRuntimeContext(input) {
409
+ return OperationRuntimeContext.create(input);
410
+ }
@@ -0,0 +1,95 @@
1
+ import type { BrowserLike, ClipboardLike, PageLike, RuntimeEnv } from "../types.js";
2
+ /** The mutable fields carried between invocations of a legacy RuntimeEnv. */
3
+ export type RuntimeEnvSessionMutableField = "browser" | "page" | "expectedTabId";
4
+ declare const STATIC_ERROR_MESSAGES: Readonly<{
5
+ readonly invalid_options: "RuntimeEnvSession options are invalid.";
6
+ readonly invalid_capture: "RuntimeEnvSession capture contains an unsupported value.";
7
+ readonly capture_closed: "RuntimeEnvSession capture is already closed.";
8
+ readonly commit_conflict: "RuntimeEnvSession commit conflicts with a newer invocation.";
9
+ readonly revision_exhausted: "RuntimeEnvSession revision capacity is exhausted.";
10
+ }>;
11
+ export type RuntimeEnvSessionErrorCode = keyof typeof STATIC_ERROR_MESSAGES;
12
+ /**
13
+ * Errors intentionally have a fixed message. In particular, no browser,
14
+ * page, tab id, caller object, or native error is interpolated into a
15
+ * RuntimeEnvSession diagnostic.
16
+ */
17
+ export declare class RuntimeEnvSessionError extends Error {
18
+ readonly code: RuntimeEnvSessionErrorCode;
19
+ constructor(code: RuntimeEnvSessionErrorCode);
20
+ }
21
+ /** Initial provider/base and mutable snapshot values for a new session. */
22
+ export type RuntimeEnvSessionOptions = Readonly<{
23
+ agent?: unknown;
24
+ browser?: BrowserLike;
25
+ page?: PageLike;
26
+ clipboard?: ClipboardLike;
27
+ now?: () => Date;
28
+ expectedTabId?: string;
29
+ }>;
30
+ export type RuntimeEnvSessionFieldPresence = "set" | "unset";
31
+ export type RuntimeEnvSessionDiagnostics = Readonly<{
32
+ revision: number;
33
+ captures: number;
34
+ openCaptures: number;
35
+ base: Readonly<{
36
+ agent: RuntimeEnvSessionFieldPresence;
37
+ clipboard: RuntimeEnvSessionFieldPresence;
38
+ now: RuntimeEnvSessionFieldPresence;
39
+ }>;
40
+ snapshot: Readonly<{
41
+ browser: RuntimeEnvSessionFieldPresence;
42
+ page: RuntimeEnvSessionFieldPresence;
43
+ expectedTabId: RuntimeEnvSessionFieldPresence;
44
+ }>;
45
+ }>;
46
+ export type RuntimeEnvSessionCaptureStatus = "open" | "committed" | "abandoned";
47
+ export type RuntimeEnvSessionCaptureDiagnostics = Readonly<{
48
+ status: RuntimeEnvSessionCaptureStatus;
49
+ revision: number;
50
+ }>;
51
+ export type RuntimeEnvSessionCommitResult = Readonly<{
52
+ revision: number;
53
+ /** Fields the invocation changed relative to its captured snapshot. */
54
+ changedFields: readonly RuntimeEnvSessionMutableField[];
55
+ /** Fields that changed the session's durable snapshot in this commit. */
56
+ appliedFields: readonly RuntimeEnvSessionMutableField[];
57
+ /** True when a stale capture was accepted by same-value convergence. */
58
+ converged: boolean;
59
+ }>;
60
+ /**
61
+ * One invocation's isolated mutable RuntimeEnv and its one-shot lifecycle.
62
+ * The RuntimeEnv itself is deliberately mutable for legacy command
63
+ * compatibility. Its provider/base fields are non-writable; only the
64
+ * browser/page/tab snapshot fields may be changed before commit.
65
+ */
66
+ export type RuntimeEnvSessionCapture = Readonly<{
67
+ env: RuntimeEnv;
68
+ revision: number;
69
+ commit: () => RuntimeEnvSessionCommitResult;
70
+ abandon: () => void;
71
+ diagnostics: () => RuntimeEnvSessionCaptureDiagnostics;
72
+ }>;
73
+ export type RuntimeEnvSessionRunCallback<T> = (env: RuntimeEnv) => T | PromiseLike<T>;
74
+ /**
75
+ * Owns the mutable browser/page/tab snapshot used by invocation-scoped
76
+ * RuntimeEnv captures. It intentionally performs no browser locking or
77
+ * command dispatch; this is the synchronous in-process snapshot/CAS boundary
78
+ * used by `createChatGPT` to isolate concurrent legacy invocations.
79
+ */
80
+ export declare class RuntimeEnvSession {
81
+ private readonly base;
82
+ private state;
83
+ private captureCount;
84
+ private openCaptureCount;
85
+ constructor(options?: RuntimeEnvSessionOptions);
86
+ /** Current revision; no browser or page value is exposed. */
87
+ get revision(): number;
88
+ /** Return frozen, redacted state diagnostics. */
89
+ diagnostics(): RuntimeEnvSessionDiagnostics;
90
+ capture(): RuntimeEnvSessionCapture;
91
+ /** Capture, run one invocation, and publish its snapshot only on success. */
92
+ run<T>(callback: RuntimeEnvSessionRunCallback<T>): Promise<T>;
93
+ }
94
+ export declare function createRuntimeEnvSession(options?: RuntimeEnvSessionOptions): RuntimeEnvSession;
95
+ export {};