@raybase/openai-subscription 0.0.0 → 0.1.0

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 (73) hide show
  1. package/ARCHITECTURE.md +80 -0
  2. package/LICENSE +21 -0
  3. package/README.md +198 -2
  4. package/compatibility/fixtures/device-usercode.json +5 -0
  5. package/compatibility/fixtures/responses-function-call.sse +23 -0
  6. package/compatibility/fixtures/responses-image-input.sse +15 -0
  7. package/compatibility/fixtures/responses-image-request.json +32 -0
  8. package/compatibility/fixtures/responses-invalid-function-parameters.json +8 -0
  9. package/compatibility/fixtures/responses-invalid-json-schema.json +8 -0
  10. package/compatibility/fixtures/responses-mixed.sse +41 -0
  11. package/compatibility/fixtures/responses-multi-tool.sse +35 -0
  12. package/compatibility/fixtures/responses.sse +26 -0
  13. package/compatibility/manifest.json +50 -0
  14. package/dist/crypto.d.ts +12 -0
  15. package/dist/crypto.d.ts.map +1 -0
  16. package/dist/crypto.js +86 -0
  17. package/dist/crypto.js.map +1 -0
  18. package/dist/health.d.ts +9 -0
  19. package/dist/health.d.ts.map +1 -0
  20. package/dist/health.js +22 -0
  21. package/dist/health.js.map +1 -0
  22. package/dist/index.d.ts +8 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +8 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/module.d.ts +6 -0
  27. package/dist/module.d.ts.map +1 -0
  28. package/dist/module.js +341 -0
  29. package/dist/module.js.map +1 -0
  30. package/dist/provider.d.ts +8 -0
  31. package/dist/provider.d.ts.map +1 -0
  32. package/dist/provider.js +361 -0
  33. package/dist/provider.js.map +1 -0
  34. package/dist/store.d.ts +51 -0
  35. package/dist/store.d.ts.map +1 -0
  36. package/dist/store.js +342 -0
  37. package/dist/store.js.map +1 -0
  38. package/dist/system-tables.d.ts +7 -0
  39. package/dist/system-tables.d.ts.map +1 -0
  40. package/dist/system-tables.js +59 -0
  41. package/dist/system-tables.js.map +1 -0
  42. package/dist/testing.d.ts +4 -0
  43. package/dist/testing.d.ts.map +1 -0
  44. package/dist/testing.js +4 -0
  45. package/dist/testing.js.map +1 -0
  46. package/dist/transport.d.ts +104 -0
  47. package/dist/transport.d.ts.map +1 -0
  48. package/dist/transport.js +1115 -0
  49. package/dist/transport.js.map +1 -0
  50. package/dist/types.d.ts +89 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +29 -0
  53. package/dist/types.js.map +1 -0
  54. package/package.json +39 -8
  55. package/skills/raybase-openai-subscription/SKILL.md +57 -0
  56. package/skills/raybase-openai-subscription/references/compatibility-and-testing.md +129 -0
  57. package/skills/raybase-openai-subscription/references/module-contract.md +133 -0
  58. package/skills/raybase-openai-subscription/references/structured-outputs.md +159 -0
  59. package/templates/astro-emdash/.agents/skills/raybase-openai-subscription/SKILL.md +57 -0
  60. package/templates/astro-emdash/.agents/skills/raybase-openai-subscription/references/compatibility-and-testing.md +129 -0
  61. package/templates/astro-emdash/.agents/skills/raybase-openai-subscription/references/module-contract.md +133 -0
  62. package/templates/astro-emdash/.agents/skills/raybase-openai-subscription/references/structured-outputs.md +159 -0
  63. package/templates/astro-emdash/src/openai-subscription.ts +11 -0
  64. package/templates/react-router/.agents/skills/raybase-openai-subscription/SKILL.md +57 -0
  65. package/templates/react-router/.agents/skills/raybase-openai-subscription/references/compatibility-and-testing.md +129 -0
  66. package/templates/react-router/.agents/skills/raybase-openai-subscription/references/module-contract.md +133 -0
  67. package/templates/react-router/.agents/skills/raybase-openai-subscription/references/structured-outputs.md +159 -0
  68. package/templates/react-router/app/openai-subscription.ts +11 -0
  69. package/templates/tanstack-start/.agents/skills/raybase-openai-subscription/SKILL.md +57 -0
  70. package/templates/tanstack-start/.agents/skills/raybase-openai-subscription/references/compatibility-and-testing.md +129 -0
  71. package/templates/tanstack-start/.agents/skills/raybase-openai-subscription/references/module-contract.md +133 -0
  72. package/templates/tanstack-start/.agents/skills/raybase-openai-subscription/references/structured-outputs.md +159 -0
  73. package/templates/tanstack-start/src/openai-subscription.ts +11 -0
@@ -0,0 +1,80 @@
1
+ # OpenAI subscription inference decision
2
+
3
+ Status: accepted for the experimental `0.1.x` module line.
4
+
5
+ Raybase Core exposes a named, request-scoped inference-provider factory. The
6
+ optional `@raybase/openai-subscription` module registers exactly one provider,
7
+ `openai-codex-subscription`. Apps select it explicitly through
8
+ `appConfig.ai.provider`; duplicate or missing provider ids fail at startup.
9
+ Chat and streaming never fall back after selection. The provider intentionally
10
+ omits embeddings, so the existing explicit Workers AI embedding path remains
11
+ available.
12
+
13
+ The module owns the compatibility-sensitive Codex transport, hidden system
14
+ tables, scope resolver, encrypted credential envelopes, connection routes, and
15
+ health state. Core remains provider-neutral. Credential identity is the exact
16
+ application, organization, mode, and subject tuple, authenticated as AES-GCM
17
+ AAD. The existing organization-scoped Pro database is the primary tenant
18
+ boundary; the tuple is an additional boundary.
19
+
20
+ Production endpoints are fixed to the reviewed compatibility manifest.
21
+ Unknown protocol shapes fail closed as `protocol_update_required`. A bounded,
22
+ parseable Codex `400` with a usable machine `error.code` or `error.type` is
23
+ instead a caller/request rejection with a sanitized
24
+ `protocol:codex_400_<token>` fingerprint and no health transition. Empty,
25
+ malformed, oversized, non-object, or machine-detail-free `400` bodies remain
26
+ the conservative protocol fallback. Upstream free-form messages never cross
27
+ the transport boundary. Credentials are never module options, settings, or
28
+ manifest data. Background work is disabled unless its mode-specific execution
29
+ scope is explicitly configured.
30
+
31
+ This direct private-protocol adapter is isolated behind an internal transport
32
+ boundary so a future reviewed broker or Codex app-server adapter can implement
33
+ the same provider contract without changing application configuration.
34
+
35
+ Origin validation and endpoint authorization are deliberately separate.
36
+ Transport construction accepts only bare HTTPS origins; request dispatch then
37
+ checks the configured origin and exact fixed method/path immediately before
38
+ fetch. Arbitrary origins and fetchers exist only in the package's `./testing`
39
+ entry point. Ordinary module and provider construction cannot change canonical
40
+ destinations.
41
+
42
+ Cloudflare Worker deployments can select a request-scoped VPC Network Fetcher
43
+ binding for the canonical Codex responses endpoint. The binding is taken from
44
+ the current provider or route environment, is structurally limited to
45
+ `fetch()`, and is never captured as a global capability. Auth traffic remains
46
+ direct; Codex traffic uses the VPC binding exactly once. Missing bindings,
47
+ throws, redirects, declared non-SSE responses, and 403 HTML responses fail
48
+ closed and never fall back to global fetch. The canonical Codex endpoint may
49
+ omit `Content-Type`; only a missing or empty value is admitted to the same
50
+ bounded strict SSE parser, which must still reach a completed response. A
51
+ declared non-SSE response is rejected before body read. Diagnostics retain
52
+ status, normalized content type, retry metadata, and safe `cf-ray` correlation
53
+ only; bodies and credential or account data are discarded. A bounded
54
+ `x-oai-request-id` may travel as opaque `oaiRequestId` metadata on a thrown
55
+ error, independently of its fingerprint, but is not written into health,
56
+ incident, notification, or audit state.
57
+
58
+ Local workerd cannot reach the canonical Codex endpoint through its direct
59
+ client fingerprint. Under `raybase dev`, apps instead select `local-relay`.
60
+ The CLI owns an ephemeral Node server bound to IPv4 loopback and injects its
61
+ URL plus a 256-bit per-boot secret into the local Worker. The transport accepts
62
+ only a loopback relay URL while `RAYBASE_ENV=local`, adds the relay guard only
63
+ to that hop, and leaves auth traffic direct. The relay strips its guard,
64
+ allows only the fixed Codex responses method/path, forwards the original body
65
+ and request headers, uses manual redirects, and streams the response. It emits
66
+ no request logs. This capability is dev-only and never replaces the
67
+ production VPC route.
68
+
69
+ Core owns output-budget admission and accounting above this adapter. It passes
70
+ the effective `maxOutputTokens` through the provider-neutral request so the
71
+ reservation happens before dispatch, while the adapter omits the unsupported
72
+ `max_output_tokens` subscription field. The existing 4 MB SSE limit is the
73
+ remaining per-response size ceiling.
74
+
75
+ The VPC mechanism follows Cloudflare's beta Workers VPC and Gateway egress
76
+ documentation. Unit and mock Worker coverage cannot establish edge
77
+ reachability: release acceptance requires the repository-owned source-checkout
78
+ Worker fixture to finish a minimal `gpt-5.6-terra` SSE response through
79
+ `network_id: "cf1:network"`. A remaining 403 is reported as a platform blocker,
80
+ not treated as expired auth or an invitation to imitate a browser.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Itself & Co
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,199 @@
1
- # @raybase/openai-subscription
1
+ # `@raybase/openai-subscription`
2
2
 
3
- This name is reserved for Raybase. Nothing is released under it yet, and this package holds no code.
3
+ Experimental, explicitly opt-in Raybase module for ChatGPT/Codex
4
+ subscription-backed Raybase Agent inference.
5
+
6
+ This is compatibility code, not the public OpenAI API and not API-key billing.
7
+ The Worker-native transport is pinned to the reviewed upstream Codex revision
8
+ recorded in `compatibility/manifest.json`. Unknown protocol shapes fail closed
9
+ as `protocol_update_required`.
10
+
11
+ Codex `400` responses are classified only from bounded machine fields. A
12
+ parseable `error.code`, or `error.type` when the code is absent or unusable,
13
+ becomes a lowercase, punctuation-normalized token capped at 32 characters in a
14
+ fingerprint such as `protocol:codex_400_invalid_json_schema`. This is a
15
+ health-neutral request failure: it does not change a connected subscription or
16
+ create a protocol incident. Free-form upstream `message` text is discarded,
17
+ and `param` is used only by the exact `invalid_function_parameters` tool-schema
18
+ case. Empty, malformed, oversized, non-object, and detail-free `400` bodies
19
+ remain `protocol:unexpected_http_status_400` with
20
+ `protocol_update_required` because ownership cannot be established safely.
21
+
22
+ A trimmed, non-empty `x-oai-request-id` of at most 128 characters is retained
23
+ as opaque `oaiRequestId` metadata on the thrown transport/request error. It is
24
+ not part of the fingerprint and is not automatically persisted or displayed
25
+ in subscription health, incidents, notifications, or audits.
26
+
27
+ ```ts
28
+ import {
29
+ openaiSubscriptionModule,
30
+ type OpenAISubscriptionFetcher
31
+ } from "@raybase/openai-subscription";
32
+ import type { RaybaseEnv } from "@raybase/core/worker";
33
+
34
+ type Env = RaybaseEnv & { CODEX_EGRESS: OpenAISubscriptionFetcher };
35
+
36
+ openaiSubscriptionModule<Env>({
37
+ credentialMode: "user",
38
+ background: { strategy: "run-as-user" },
39
+ codexEgress: import.meta.env.DEV
40
+ ? { strategy: "local-relay" }
41
+ : { strategy: "cloudflare-vpc", binding: "CODEX_EGRESS" }
42
+ });
43
+ ```
44
+
45
+ For a production Cloudflare Worker, add the matching Workers VPC Network
46
+ binding to `wrangler.jsonc`:
47
+
48
+ ```jsonc
49
+ {
50
+ "vpc_networks": [
51
+ {
52
+ "binding": "CODEX_EGRESS",
53
+ "network_id": "cf1:network",
54
+ "remote": true
55
+ }
56
+ ]
57
+ }
58
+ ```
59
+
60
+ `raybase modules add openai-subscription` writes this binding to production.
61
+ `raybase modules doctor` reports a missing or malformed production binding.
62
+
63
+ For local development, the installed module selects `local-relay`.
64
+ `raybase dev` starts an ephemeral Node relay bound to `127.0.0.1`, injects a
65
+ per-boot URL and shared secret into the local Worker, and removes them on clean
66
+ shutdown. Only Codex response traffic uses that relay; device and token traffic
67
+ continues directly to `https://auth.openai.com`. The relay accepts only the
68
+ Codex responses route and never logs request header values. `local-relay`
69
+ fails closed outside a Worker whose `RAYBASE_ENV` is `local`.
70
+
71
+ The binding is resolved from the active request's Worker environment. Codex
72
+ response traffic goes only through `CODEX_EGRESS.fetch()`; device and token
73
+ traffic remains on canonical `https://auth.openai.com` through normal fetch.
74
+ A missing, malformed, throwing, or 403 VPC binding fails closed. There is no
75
+ retry through global fetch. Standalone Node tools may omit `codexEgress` or use
76
+ `{ strategy: "direct" }` for backward-compatible direct traffic.
77
+
78
+ Cloudflare documents this public-Internet Gateway route in the
79
+ [Workers VPC overview](https://developers.cloudflare.com/workers-vpc/),
80
+ [binding API](https://developers.cloudflare.com/workers-vpc/api/), and
81
+ [Gateway egress announcement](https://developers.cloudflare.com/changelog/post/2026-06-05-gateway-egress/).
82
+ Workers VPC is currently beta, so re-check those documents before deployment.
83
+
84
+ `maxOutputTokens` remains a Raybase admission and accounting input. Core
85
+ reserves the effective value before provider dispatch and records observed
86
+ usage, but the subscription wire does not send unsupported
87
+ `max_output_tokens`. An admitted response is bounded by the transport's 4 MB
88
+ SSE ceiling, not by server-side per-response token truncation.
89
+
90
+ ## Image inputs
91
+
92
+ User messages may interleave text, HTTPS image URLs, and inline base64/data-URI
93
+ images. The subscription adapter preserves that order and emits Responses
94
+ `input_text` and `input_image` items; an image always uses the verified
95
+ `input_image.image_url` field. Supported media types are PNG, JPEG, WEBP, and
96
+ non-animated GIF. System, assistant, and tool message content remains
97
+ string-only.
98
+
99
+ This boundary is intentionally strict. Unsupported kinds such as audio or
100
+ files, malformed MIME/base64/URL values, and extra fields are rejected before
101
+ fetch with caller errors that name the kind or field. In particular, Raybase
102
+ does not expose per-image `detail` or `quality`: it omits both and uses the
103
+ endpoint's accepted omitted-field behavior. See the dated evidence and exact
104
+ unsupported-kind inventory in
105
+ `skills/raybase-openai-subscription/references/compatibility-and-testing.md`.
106
+
107
+ ## Live Worker verification
108
+
109
+ Mock Fetcher tests prove routing and fail-closed behavior, not ChatGPT
110
+ reachability. The fixture is a Raybase repository source-checkout acceptance
111
+ gate under `packages/openai-subscription/test-worker/`; it is intentionally
112
+ not included in the published package and published consumers receive no
113
+ fixture scripts. From the repository root:
114
+
115
+ 1. Run `pnpm deploy:openai-subscription-worker-egress:remote` for the narrowly
116
+ named ephemeral Worker in
117
+ `packages/openai-subscription/test-worker/wrangler.jsonc`.
118
+ 2. Run `pnpm exec wrangler secret put OPENAI_SUBSCRIPTION_TOKEN --config packages/openai-subscription/test-worker/wrangler.jsonc`
119
+ and the same command for `OPENAI_SUBSCRIPTION_VERIFICATION_KEY`. Let
120
+ Wrangler prompt on stdin; never
121
+ put either value in configuration, source, command arguments, logs, or
122
+ output.
123
+ 3. POST `/verify` with the one-time key in the Authorization header. A pass
124
+ reports only route, model, status, the validated SSE protocol, and final
125
+ token counts. The canonical endpoint can omit `Content-Type`; a missing or
126
+ empty value is accepted only when the bounded strict SSE parser reaches a
127
+ completed response. A declared non-SSE media type fails before body read
128
+ and reports only normalized content type, `cf-ray` presence, and redirect
129
+ state; the upstream body remains unread and is cancelled.
130
+ 4. Immediately run `pnpm delete:openai-subscription-worker-egress:remote`. A 403
131
+ is a platform blocker; do not add browser headers or a direct-fetch retry.
132
+ 5. Toby must rotate the furnished development subscription token after the
133
+ verification evidence is captured.
134
+
135
+ ### The `cloudflare-vpc` lane, verified 2026-08-10
136
+
137
+ Acceptance item **met**: one real turn through `CODEX_EGRESS` from a genuinely
138
+ deployed Worker (`wrangler deploy`, binding `cf1:network`), carrying a
139
+ credential minted by this module's own device-code flow, returned inner status
140
+ `200`, validated SSE, `gpt-5.6-terra`, usage 11 in / 5 out. The spike ran from
141
+ committed `75dfca7` — that is, **without** the outbound header allowlist below
142
+ — which is what makes it decisive: neither the Workers runtime nor Cloudflare's
143
+ VPC egress decorates those subrequests with the headers that block the front
144
+ door. Production device-auth traffic was never affected.
145
+
146
+ chatgpt.com sits behind Cloudflare, and its front door blocks any request
147
+ carrying Cloudflare's own hop metadata. `cf-worker` — which workerd stamps on
148
+ every outbound subrequest — plus `cf-connecting-ip` and `cf-visitor` each
149
+ return `403 text/html` with the blocked page and **no `x-oai-request-id`**,
150
+ because the request never reaches the Codex API. (`cf-ray` and `cf-ipcountry`
151
+ do not trigger it.) This, not any property of the credential, is what blocked
152
+ the local lane through August 2026: `raybase dev`'s relay moved the TCP client
153
+ to Node but forwarded workerd's `cf-worker` header unchanged.
154
+
155
+ Two boundaries now guard this: the relay strips that header class at its own
156
+ edge, and the transport authors its outbound header set from an allowlist
157
+ (`content-type`, `authorization`, `chatgpt-account-id`, `originator`,
158
+ `accept`) for every egress strategy.
159
+
160
+ The allowlist is a guard, not a repair: the module has never authored a `cf-*`
161
+ header on any strategy, and the deploy above proves the runtime does not add
162
+ one either. It exists so a future edit cannot reintroduce the dev failure on
163
+ the VPC path, which has no relay boundary to catch it.
164
+
165
+ **What still cannot be observed locally:** headers appended after `fetch()`
166
+ leaves the module. That is now evidenced rather than assumed, but the evidence
167
+ is a point-in-time deploy — re-run the turn above if Cloudflare changes VPC
168
+ egress behaviour, since Workers VPC is still beta.
169
+
170
+ A `403` there is diagnosable in one glance: `codex_egress_forbidden`
171
+ (`disconnected`) is the front door and names the hop-header cause;
172
+ `codex_account_forbidden` (`reauth_required`) is the Codex API refusing the
173
+ account. Per step 4 above, a front-door 403 is a platform blocker — do not
174
+ answer it with browser headers or a direct-fetch retry.
175
+
176
+ Set `appConfig.ai.provider` to `openai-codex-subscription`. Create
177
+ `RAYBASE_OPENAI_SUBSCRIPTION_KEY` with at least 32 random bytes encoded as
178
+ base64/base64url and store it only as a local/deployed Worker secret. Never add
179
+ its value to Wrangler configuration, settings, source, or manifests.
180
+
181
+ Credential modes are explicit:
182
+
183
+ - `operator`: one designated responsible operator subject.
184
+ - `user`: each signed-in user links their own eligible subscription.
185
+ - `organization`: an organization-controlled account, scoped to the resolved
186
+ organization runtime.
187
+
188
+ `protocol_update_required` means update to a newly published
189
+ `@raybase/openai-subscription` version containing a reviewed upstream
190
+ pin/schema/fixture update. Do not edit the production allowlist or retry
191
+ blindly. A `protocol:codex_400_<token>` fingerprint without a health field is a
192
+ request rejection instead: fix the caller-owned request or schema and use a
193
+ bounded `oaiRequestId`, when present on the thrown error, for upstream support
194
+ correlation. For key rotation, deploy the reviewed replacement key and
195
+ reconnect each scope; old ciphertext is intentionally not decryptable under an
196
+ unrelated key.
197
+
198
+ The module informs operators about usage scope; it does not certify or enforce
199
+ eligibility.
@@ -0,0 +1,5 @@
1
+ {
2
+ "device_auth_id": "fixture-device-id",
3
+ "user_code": "ABCD-EFGH",
4
+ "interval": "5"
5
+ }
@@ -0,0 +1,23 @@
1
+ event: response.created
2
+ data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_function_fixture","status":"in_progress","output":[]}}
3
+
4
+ event: response.output_item.added
5
+ data: {"type":"response.output_item.added","sequence_number":1,"output_index":0,"item":{"id":"fc_function_fixture","type":"function_call","status":"in_progress","arguments":"","call_id":"call_function_fixture","name":"lookup_customer"}}
6
+
7
+ event: response.function_call_arguments.delta
8
+ data: {"type":"response.function_call_arguments.delta","sequence_number":2,"item_id":"fc_function_fixture","output_index":0,"delta":"{\"customerId\":"}
9
+
10
+ event: response.function_call_arguments.delta
11
+ data: {"type":"response.function_call_arguments.delta","sequence_number":3,"item_id":"fc_function_fixture","output_index":0,"delta":"\"cust_fixture\","}
12
+
13
+ event: response.function_call_arguments.delta
14
+ data: {"type":"response.function_call_arguments.delta","sequence_number":4,"item_id":"fc_function_fixture","output_index":0,"delta":"\"includeOrders\":true}"}
15
+
16
+ event: response.function_call_arguments.done
17
+ data: {"type":"response.function_call_arguments.done","sequence_number":5,"item_id":"fc_function_fixture","output_index":0,"name":"lookup_customer","arguments":"{\"customerId\":\"cust_fixture\",\"includeOrders\":true}"}
18
+
19
+ event: response.output_item.done
20
+ data: {"type":"response.output_item.done","sequence_number":6,"output_index":0,"item":{"id":"fc_function_fixture","type":"function_call","status":"completed","arguments":"{\"customerId\":\"cust_fixture\",\"includeOrders\":true}","call_id":"call_function_fixture","name":"lookup_customer"}}
21
+
22
+ event: response.completed
23
+ data: {"type":"response.completed","sequence_number":7,"response":{"id":"resp_function_fixture","status":"completed","output":[{"id":"fc_function_fixture","type":"function_call","status":"completed","arguments":"{\"customerId\":\"cust_fixture\",\"includeOrders\":true}","call_id":"call_function_fixture","name":"lookup_customer"}],"usage":{"input_tokens":12,"input_tokens_details":{"cached_tokens":0},"output_tokens":8,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":20}}}
@@ -0,0 +1,15 @@
1
+ event: response.created
2
+ data: {"type":"response.created","response":{"id":"resp_image_fixture","status":"in_progress"}}
3
+
4
+ event: response.output_item.added
5
+ data: {"type":"response.output_item.added","item":{"id":"msg_image_fixture","type":"message","role":"assistant"}}
6
+
7
+ event: response.output_text.delta
8
+ data: {"type":"response.output_text.delta","delta":"fixture image answer"}
9
+
10
+ event: response.output_text.done
11
+ data: {"type":"response.output_text.done","text":"fixture image answer"}
12
+
13
+ event: response.completed
14
+ data: {"type":"response.completed","response":{"id":"resp_image_fixture","status":"completed","usage":{"input_tokens":19,"output_tokens":3}}}
15
+
@@ -0,0 +1,32 @@
1
+ {
2
+ "model": "gpt-5.6-terra",
3
+ "instructions": "Answer briefly.",
4
+ "input": [
5
+ {
6
+ "role": "user",
7
+ "content": [
8
+ {
9
+ "type": "input_text",
10
+ "text": "Compare these images: "
11
+ },
12
+ {
13
+ "type": "input_image",
14
+ "image_url": "https://example.test/fixture.png"
15
+ },
16
+ {
17
+ "type": "input_text",
18
+ "text": " and "
19
+ },
20
+ {
21
+ "type": "input_image",
22
+ "image_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII="
23
+ }
24
+ ]
25
+ }
26
+ ],
27
+ "tools": [],
28
+ "tool_choice": "none",
29
+ "parallel_tool_calls": true,
30
+ "stream": true,
31
+ "store": false
32
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "error": {
3
+ "message": "Fixture function parameter schema is invalid.",
4
+ "type": "invalid_request_error",
5
+ "param": "tools[3].parameters",
6
+ "code": "invalid_function_parameters"
7
+ }
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "error": {
3
+ "message": "Fixture response format schema is invalid.",
4
+ "type": "invalid_request_error",
5
+ "param": "text.format.schema",
6
+ "code": "invalid_json_schema"
7
+ }
8
+ }
@@ -0,0 +1,41 @@
1
+ event: response.created
2
+ data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_mixed_fixture","status":"in_progress","output":[]}}
3
+
4
+ event: response.output_item.added
5
+ data: {"type":"response.output_item.added","sequence_number":1,"output_index":0,"item":{"id":"msg_mixed_fixture","type":"message","status":"in_progress","role":"assistant","content":[]}}
6
+
7
+ event: response.content_part.added
8
+ data: {"type":"response.content_part.added","sequence_number":2,"item_id":"msg_mixed_fixture","output_index":0,"content_index":0,"part":{"type":"output_text","text":"","annotations":[]}}
9
+
10
+ event: response.output_text.delta
11
+ data: {"type":"response.output_text.delta","sequence_number":3,"item_id":"msg_mixed_fixture","output_index":0,"content_index":0,"delta":"I'll check "}
12
+
13
+ event: response.output_text.delta
14
+ data: {"type":"response.output_text.delta","sequence_number":4,"item_id":"msg_mixed_fixture","output_index":0,"content_index":0,"delta":"the order."}
15
+
16
+ event: response.output_text.done
17
+ data: {"type":"response.output_text.done","sequence_number":5,"item_id":"msg_mixed_fixture","output_index":0,"content_index":0,"text":"I'll check the order."}
18
+
19
+ event: response.content_part.done
20
+ data: {"type":"response.content_part.done","sequence_number":6,"item_id":"msg_mixed_fixture","output_index":0,"content_index":0,"part":{"type":"output_text","text":"I'll check the order.","annotations":[]}}
21
+
22
+ event: response.output_item.done
23
+ data: {"type":"response.output_item.done","sequence_number":7,"output_index":0,"item":{"id":"msg_mixed_fixture","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"I'll check the order.","annotations":[]}]}}
24
+
25
+ event: response.output_item.added
26
+ data: {"type":"response.output_item.added","sequence_number":8,"output_index":1,"item":{"id":"fc_mixed_fixture","type":"function_call","status":"in_progress","arguments":"","call_id":"call_mixed_fixture","name":"lookup_order"}}
27
+
28
+ event: response.function_call_arguments.delta
29
+ data: {"type":"response.function_call_arguments.delta","sequence_number":9,"item_id":"fc_mixed_fixture","output_index":1,"delta":"{\"orderId\":"}
30
+
31
+ event: response.function_call_arguments.delta
32
+ data: {"type":"response.function_call_arguments.delta","sequence_number":10,"item_id":"fc_mixed_fixture","output_index":1,"delta":"\"order_fixture\"}"}
33
+
34
+ event: response.function_call_arguments.done
35
+ data: {"type":"response.function_call_arguments.done","sequence_number":11,"item_id":"fc_mixed_fixture","output_index":1,"name":"lookup_order","arguments":"{\"orderId\":\"order_fixture\"}"}
36
+
37
+ event: response.output_item.done
38
+ data: {"type":"response.output_item.done","sequence_number":12,"output_index":1,"item":{"id":"fc_mixed_fixture","type":"function_call","status":"completed","arguments":"{\"orderId\":\"order_fixture\"}","call_id":"call_mixed_fixture","name":"lookup_order"}}
39
+
40
+ event: response.completed
41
+ data: {"type":"response.completed","sequence_number":13,"response":{"id":"resp_mixed_fixture","status":"completed","output":[{"id":"msg_mixed_fixture","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"I'll check the order.","annotations":[]}]},{"id":"fc_mixed_fixture","type":"function_call","status":"completed","arguments":"{\"orderId\":\"order_fixture\"}","call_id":"call_mixed_fixture","name":"lookup_order"}],"usage":{"input_tokens":21,"input_tokens_details":{"cached_tokens":4},"output_tokens":14,"output_tokens_details":{"reasoning_tokens":3},"total_tokens":35}}}
@@ -0,0 +1,35 @@
1
+ event: response.created
2
+ data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_multi_tool_fixture","status":"in_progress","output":[]}}
3
+
4
+ event: response.output_item.added
5
+ data: {"type":"response.output_item.added","sequence_number":1,"output_index":0,"item":{"id":"fc_weather_fixture","type":"function_call","status":"in_progress","arguments":"","call_id":"call_weather_fixture","name":"lookup_weather"}}
6
+
7
+ event: response.output_item.added
8
+ data: {"type":"response.output_item.added","sequence_number":2,"output_index":1,"item":{"id":"fc_time_fixture","type":"function_call","status":"in_progress","arguments":"","call_id":"call_time_fixture","name":"lookup_time"}}
9
+
10
+ event: response.function_call_arguments.delta
11
+ data: {"type":"response.function_call_arguments.delta","sequence_number":3,"item_id":"fc_weather_fixture","output_index":0,"delta":"{\"city\":\"Par"}
12
+
13
+ event: response.function_call_arguments.delta
14
+ data: {"type":"response.function_call_arguments.delta","sequence_number":4,"item_id":"fc_time_fixture","output_index":1,"delta":"{\"timezone\":"}
15
+
16
+ event: response.function_call_arguments.delta
17
+ data: {"type":"response.function_call_arguments.delta","sequence_number":5,"item_id":"fc_weather_fixture","output_index":0,"delta":"is\",\"units\":\"celsius\"}"}
18
+
19
+ event: response.function_call_arguments.delta
20
+ data: {"type":"response.function_call_arguments.delta","sequence_number":6,"item_id":"fc_time_fixture","output_index":1,"delta":"\"Europe/Paris\"}"}
21
+
22
+ event: response.function_call_arguments.done
23
+ data: {"type":"response.function_call_arguments.done","sequence_number":7,"item_id":"fc_time_fixture","output_index":1,"name":"lookup_time","arguments":"{\"timezone\":\"Europe/Paris\"}"}
24
+
25
+ event: response.function_call_arguments.done
26
+ data: {"type":"response.function_call_arguments.done","sequence_number":8,"item_id":"fc_weather_fixture","output_index":0,"name":"lookup_weather","arguments":"{\"city\":\"Paris\",\"units\":\"celsius\"}"}
27
+
28
+ event: response.output_item.done
29
+ data: {"type":"response.output_item.done","sequence_number":9,"output_index":1,"item":{"id":"fc_time_fixture","type":"function_call","status":"completed","arguments":"{\"timezone\":\"Europe/Paris\"}","call_id":"call_time_fixture","name":"lookup_time"}}
30
+
31
+ event: response.output_item.done
32
+ data: {"type":"response.output_item.done","sequence_number":10,"output_index":0,"item":{"id":"fc_weather_fixture","type":"function_call","status":"completed","arguments":"{\"city\":\"Paris\",\"units\":\"celsius\"}","call_id":"call_weather_fixture","name":"lookup_weather"}}
33
+
34
+ event: response.completed
35
+ data: {"type":"response.completed","sequence_number":11,"response":{"id":"resp_multi_tool_fixture","status":"completed","output":[{"id":"fc_weather_fixture","type":"function_call","status":"completed","arguments":"{\"city\":\"Paris\",\"units\":\"celsius\"}","call_id":"call_weather_fixture","name":"lookup_weather"},{"id":"fc_time_fixture","type":"function_call","status":"completed","arguments":"{\"timezone\":\"Europe/Paris\"}","call_id":"call_time_fixture","name":"lookup_time"}],"usage":{"input_tokens":31,"input_tokens_details":{"cached_tokens":6},"output_tokens":19,"output_tokens_details":{"reasoning_tokens":2},"total_tokens":50}}}
@@ -0,0 +1,26 @@
1
+ event: response.created
2
+ data: {"type":"response.created","sequence_number":0,"response":{"id":"resp_text_fixture","status":"in_progress","output":[]}}
3
+
4
+ event: response.in_progress
5
+ data: {"type":"response.in_progress","sequence_number":1,"response":{"id":"resp_text_fixture","status":"in_progress","output":[]}}
6
+
7
+ event: response.output_item.added
8
+ data: {"type":"response.output_item.added","sequence_number":2,"output_index":0,"item":{"id":"msg_text_fixture","type":"message","status":"in_progress","role":"assistant","content":[]}}
9
+
10
+ event: response.content_part.added
11
+ data: {"type":"response.content_part.added","sequence_number":3,"item_id":"msg_text_fixture","output_index":0,"content_index":0,"part":{"type":"output_text","text":"","annotations":[]}}
12
+
13
+ event: response.output_text.delta
14
+ data: {"type":"response.output_text.delta","sequence_number":4,"item_id":"msg_text_fixture","output_index":0,"content_index":0,"delta":"fixture"}
15
+
16
+ event: response.output_text.done
17
+ data: {"type":"response.output_text.done","sequence_number":5,"item_id":"msg_text_fixture","output_index":0,"content_index":0,"text":"fixture"}
18
+
19
+ event: response.content_part.done
20
+ data: {"type":"response.content_part.done","sequence_number":6,"item_id":"msg_text_fixture","output_index":0,"content_index":0,"part":{"type":"output_text","text":"fixture","annotations":[]}}
21
+
22
+ event: response.output_item.done
23
+ data: {"type":"response.output_item.done","sequence_number":7,"output_index":0,"item":{"id":"msg_text_fixture","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"fixture","annotations":[]}]}}
24
+
25
+ event: response.completed
26
+ data: {"type":"response.completed","sequence_number":8,"response":{"id":"resp_text_fixture","status":"completed","output":[{"id":"msg_text_fixture","type":"message","status":"completed","role":"assistant","content":[{"type":"output_text","text":"fixture","annotations":[]}]}],"usage":{"input_tokens":3,"input_tokens_details":{"cached_tokens":0},"output_tokens":1,"output_tokens_details":{"reasoning_tokens":0},"total_tokens":4}}}
@@ -0,0 +1,50 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "moduleProtocolVersion": "codex-subscription-2026-07-29",
4
+ "observedAt": "2026-07-29",
5
+ "upstream": {
6
+ "repository": "https://github.com/openai/codex",
7
+ "commit": "df326d31cda41a84f0641ec6519db08020248281",
8
+ "evidence": [
9
+ "codex-rs/login/src/device_code_auth.rs",
10
+ "codex-rs/login/src/auth/manager.rs",
11
+ "codex-rs/model-provider-info/src/lib.rs",
12
+ "codex-rs/model-provider/src/auth.rs",
13
+ "codex-rs/codex-api/src/endpoint/responses.rs",
14
+ "codex-rs/codex-api/src/sse/responses.rs"
15
+ ]
16
+ },
17
+ "productionAllowlist": [
18
+ { "origin": "https://auth.openai.com", "method": "POST", "path": "/api/accounts/deviceauth/usercode" },
19
+ { "origin": "https://auth.openai.com", "method": "POST", "path": "/api/accounts/deviceauth/token" },
20
+ { "origin": "https://auth.openai.com", "method": "POST", "path": "/oauth/token" },
21
+ { "origin": "https://chatgpt.com", "method": "POST", "path": "/backend-api/codex/responses" }
22
+ ],
23
+ "requestContentEvidence": {
24
+ "observedAt": "2026-08-11",
25
+ "scope": "Redacted local image-input probe only; authentication and SSE protocol pins were not re-reviewed.",
26
+ "wireField": "input_image.image_url",
27
+ "completedMediaTypes": ["image/png", "image/jpeg", "image/webp", "image/gif"],
28
+ "completedDetailValues": ["omitted", "auto", "low", "high", "original"],
29
+ "qualityResult": "400 unknown_parameter",
30
+ "largestCompletedEncodedRequestBytes": 19573864,
31
+ "largestCompletedDecodedImageBytes": 14680144,
32
+ "exactPrivateLimitVerified": false,
33
+ "fixtures": ["responses-image-request.json", "responses-image-input.sse"]
34
+ },
35
+ "fixtures": {
36
+ "provenance": "Sanitized synthetic fixtures derived from the pinned Rust response structs, SSE event matches, and observed machine-readable Codex refusal envelopes. They contain no live credentials, identifiers, prompts, completions, schema names, or request IDs.",
37
+ "inventory": {
38
+ "successfulSse": [
39
+ "responses.sse",
40
+ "responses-function-call.sse",
41
+ "responses-mixed.sse",
42
+ "responses-multi-tool.sse"
43
+ ],
44
+ "sanitizedErrorEnvelopes": [
45
+ "responses-invalid-json-schema.json",
46
+ "responses-invalid-function-parameters.json"
47
+ ]
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,12 @@
1
+ import type { OpenAISubscriptionCredentialScope } from "./types.js";
2
+ export type OpenAISubscriptionEncryptedEnvelope = {
3
+ version: 1;
4
+ keyVersion: 1;
5
+ nonce: string;
6
+ ciphertext: string;
7
+ };
8
+ export declare function validateOpenAISubscriptionSecret(secret: string | undefined): Uint8Array;
9
+ export declare function encryptOpenAISubscriptionEnvelope(secret: string, scope: OpenAISubscriptionCredentialScope, value: unknown): Promise<OpenAISubscriptionEncryptedEnvelope>;
10
+ export declare function decryptOpenAISubscriptionEnvelope<T>(secret: string, scope: OpenAISubscriptionCredentialScope, envelope: OpenAISubscriptionEncryptedEnvelope): Promise<T>;
11
+ export declare function openAISubscriptionScopeKey(scope: OpenAISubscriptionCredentialScope): Promise<string>;
12
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,YAAY,CAAC;AAEpE,MAAM,MAAM,mCAAmC,GAAG;IAChD,OAAO,EAAE,CAAC,CAAC;IACX,UAAU,EAAE,CAAC,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAKF,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAgBvF;AAED,wBAAsB,iCAAiC,CACrD,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,iCAAiC,EACxC,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,mCAAmC,CAAC,CAc9C;AAED,wBAAsB,iCAAiC,CAAC,CAAC,EACvD,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,iCAAiC,EACxC,QAAQ,EAAE,mCAAmC,GAC5C,OAAO,CAAC,CAAC,CAAC,CAgBZ;AAED,wBAAsB,0BAA0B,CAAC,KAAK,EAAE,iCAAiC,GAAG,OAAO,CAAC,MAAM,CAAC,CAG1G"}