@moxxy/plugin-provider-openai-codex 0.21.1

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 (67) hide show
  1. package/LICENSE +21 -0
  2. package/dist/codex/headers.d.ts +5 -0
  3. package/dist/codex/headers.d.ts.map +1 -0
  4. package/dist/codex/headers.js +39 -0
  5. package/dist/codex/headers.js.map +1 -0
  6. package/dist/codex/sse-event-handler.d.ts +16 -0
  7. package/dist/codex/sse-event-handler.d.ts.map +1 -0
  8. package/dist/codex/sse-event-handler.js +155 -0
  9. package/dist/codex/sse-event-handler.js.map +1 -0
  10. package/dist/codex/stream-consumer.d.ts +4 -0
  11. package/dist/codex/stream-consumer.d.ts.map +1 -0
  12. package/dist/codex/stream-consumer.js +176 -0
  13. package/dist/codex/stream-consumer.js.map +1 -0
  14. package/dist/codex/stream-types.d.ts +61 -0
  15. package/dist/codex/stream-types.d.ts.map +1 -0
  16. package/dist/codex/stream-types.js +19 -0
  17. package/dist/codex/stream-types.js.map +1 -0
  18. package/dist/index.d.ts +11 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +31 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/login.d.ts +33 -0
  23. package/dist/login.d.ts.map +1 -0
  24. package/dist/login.js +94 -0
  25. package/dist/login.js.map +1 -0
  26. package/dist/models.d.ts +10 -0
  27. package/dist/models.d.ts.map +1 -0
  28. package/dist/models.js +19 -0
  29. package/dist/models.js.map +1 -0
  30. package/dist/oauth.d.ts +74 -0
  31. package/dist/oauth.d.ts.map +1 -0
  32. package/dist/oauth.js +170 -0
  33. package/dist/oauth.js.map +1 -0
  34. package/dist/profile.d.ts +13 -0
  35. package/dist/profile.d.ts.map +1 -0
  36. package/dist/profile.js +39 -0
  37. package/dist/profile.js.map +1 -0
  38. package/dist/provider.d.ts +79 -0
  39. package/dist/provider.d.ts.map +1 -0
  40. package/dist/provider.js +306 -0
  41. package/dist/provider.js.map +1 -0
  42. package/dist/translate.d.ts +77 -0
  43. package/dist/translate.d.ts.map +1 -0
  44. package/dist/translate.js +172 -0
  45. package/dist/translate.js.map +1 -0
  46. package/dist/types.d.ts +27 -0
  47. package/dist/types.d.ts.map +1 -0
  48. package/dist/types.js +2 -0
  49. package/dist/types.js.map +1 -0
  50. package/package.json +66 -0
  51. package/src/codex/headers.ts +41 -0
  52. package/src/codex/sse-event-handler.test.ts +85 -0
  53. package/src/codex/sse-event-handler.ts +173 -0
  54. package/src/codex/stream-consumer.test.ts +280 -0
  55. package/src/codex/stream-consumer.ts +181 -0
  56. package/src/codex/stream-types.ts +61 -0
  57. package/src/index.ts +65 -0
  58. package/src/login.ts +121 -0
  59. package/src/models.ts +21 -0
  60. package/src/oauth.test.ts +223 -0
  61. package/src/oauth.ts +200 -0
  62. package/src/profile.ts +52 -0
  63. package/src/provider.test.ts +507 -0
  64. package/src/provider.ts +360 -0
  65. package/src/translate.test.ts +95 -0
  66. package/src/translate.ts +224 -0
  67. package/src/types.ts +28 -0
@@ -0,0 +1,306 @@
1
+ import { webcrypto } from 'node:crypto';
2
+ import { classifyHttpStatus, estimateTextTokens } from '@moxxy/sdk';
3
+ import { isAuthRejection, withCredentialLock } from '@moxxy/plugin-oauth';
4
+ import { CODEX_RESPONSES_URL, refreshTokens } from './oauth.js';
5
+ import { codexModels, DEFAULT_CODEX_MODEL } from './models.js';
6
+ import { toResponsesBody } from './translate.js';
7
+ import { buildCodexHeaders } from './codex/headers.js';
8
+ import { consumeResponsesSse, toErrorEvent } from './codex/stream-consumer.js';
9
+ /**
10
+ * Internal idle timeout (ms). Guards the whole streaming path against a backend
11
+ * that accepts the POST but then stalls — pre-headers slow-loris, a dropped TCP
12
+ * with no RST, or a wedged proxy — when the caller supplied no AbortSignal. The
13
+ * watchdog is reset on every received byte, so a slow-but-alive reasoning stream
14
+ * is never killed; only a stream that goes silent for this long aborts.
15
+ */
16
+ const CODEX_IDLE_TIMEOUT_MS = 120_000;
17
+ /**
18
+ * Read the error-response body but cap how much we pull into memory: a hostile
19
+ * or broken backend (or MITM) could otherwise stream a multi-megabyte error body
20
+ * that we'd buffer in full and then interpolate into an error string.
21
+ */
22
+ const MAX_ERROR_BODY_CHARS = 2048;
23
+ /**
24
+ * Flat token estimate charged per non-text content block (image/document) in
25
+ * `countTokens`. A coarse stand-in for the provider's real multimodal token
26
+ * accounting — enough to keep pre-flight budgeting from treating a multimodal
27
+ * request as if it were nearly empty.
28
+ */
29
+ const NON_TEXT_BLOCK_TOKENS = 256;
30
+ /**
31
+ * LLMProvider implementation against the ChatGPT-plan Codex backend. Auth is
32
+ * an OAuth bearer plus the optional ChatGPT-Account-Id header; the rest of
33
+ * the request body is the OpenAI Responses-API shape.
34
+ *
35
+ * Request-param support: `req.maxTokens` maps to the Responses
36
+ * `max_output_tokens` field; `req.temperature` is NOT forwarded — the Codex
37
+ * backend only serves gpt-5-family reasoning models, which reject sampling
38
+ * params with a 400, so it is dropped (with a one-shot MOXXY_DEBUG note)
39
+ * instead of breaking the request. Reasoning effort is configurable via
40
+ * `CodexProviderConfig.reasoningEffort` (default `'medium'`).
41
+ */
42
+ export class CodexProvider {
43
+ name = 'openai-codex';
44
+ models = codexModels;
45
+ tokens;
46
+ onTokensRefreshed;
47
+ reloadTokens;
48
+ defaultModel;
49
+ reasoningEffort;
50
+ fetchImpl;
51
+ sessionIdProvider;
52
+ idleTimeoutMs;
53
+ constructor(config = {}) {
54
+ if (config.tokens)
55
+ this.tokens = config.tokens;
56
+ if (config.onTokensRefreshed)
57
+ this.onTokensRefreshed = config.onTokensRefreshed;
58
+ if (config.reloadTokens)
59
+ this.reloadTokens = config.reloadTokens;
60
+ this.defaultModel = config.defaultModel ?? DEFAULT_CODEX_MODEL;
61
+ if (config.reasoningEffort)
62
+ this.reasoningEffort = config.reasoningEffort;
63
+ this.fetchImpl = config.fetch ?? fetch;
64
+ // Default to ONE id for the instance's lifetime, not a fresh uuid per call.
65
+ // The session id becomes the `prompt_cache_key`, so it must be stable
66
+ // across a session's turns for the Responses prefix cache to hit (and the
67
+ // `session_id` header should be stable for one logical session too).
68
+ const defaultSessionId = webcrypto.randomUUID();
69
+ this.sessionIdProvider = config.sessionIdProvider ?? (() => defaultSessionId);
70
+ this.idleTimeoutMs =
71
+ config.idleTimeoutMs && config.idleTimeoutMs > 0
72
+ ? config.idleTimeoutMs
73
+ : CODEX_IDLE_TIMEOUT_MS;
74
+ }
75
+ async *stream(req) {
76
+ const model = req.model || this.defaultModel;
77
+ yield { type: 'message_start', model };
78
+ try {
79
+ await this.ensureFresh();
80
+ }
81
+ catch (err) {
82
+ yield toErrorEvent(err);
83
+ return;
84
+ }
85
+ const sessionId = this.sessionIdProvider();
86
+ // Reasoning preview is gated by the per-provider toggle (`req.reasoning`):
87
+ // when off we behave exactly as before (discard reasoning). The per-call
88
+ // effort, when set, overrides the instance default from provider config.
89
+ const emitReasoning = req.reasoning != null && req.reasoning !== false;
90
+ const reqEffort = typeof req.reasoning === 'object' ? req.reasoning.effort : undefined;
91
+ const reasoningEffort = reqEffort ?? this.reasoningEffort;
92
+ // Pass the session id as the Responses prefix-cache key so repeated turns
93
+ // in a session reuse the cached prefix (cheaper + faster). Codex DOES
94
+ // support this even though the Chat-Completions providers ignore cacheHints.
95
+ const body = toResponsesBody({ ...req, model }, {
96
+ sessionHint: sessionId,
97
+ ...(reasoningEffort ? { reasoningEffort } : {}),
98
+ });
99
+ // Internal idle watchdog: aborts the request if the backend stalls (accepts
100
+ // the POST but never sends headers/body, or goes silent mid-stream) so the
101
+ // turn can't hang forever when the caller supplied no AbortSignal. Composed
102
+ // with `req.signal` so a caller cancellation still wins, and reset on every
103
+ // received byte by `consumeResponsesSse`'s onActivity callback.
104
+ const idleController = new AbortController();
105
+ let idleTimer;
106
+ const armIdle = () => {
107
+ if (idleTimer)
108
+ clearTimeout(idleTimer);
109
+ idleTimer = setTimeout(() => idleController.abort(new Error('Codex request timed out (no response from server)')), this.idleTimeoutMs);
110
+ // A bare timer must not keep the event loop alive on its own.
111
+ idleTimer.unref?.();
112
+ };
113
+ const disarmIdle = () => {
114
+ if (idleTimer)
115
+ clearTimeout(idleTimer);
116
+ idleTimer = undefined;
117
+ };
118
+ const signal = req.signal
119
+ ? AbortSignal.any([req.signal, idleController.signal])
120
+ : idleController.signal;
121
+ try {
122
+ armIdle();
123
+ let response;
124
+ try {
125
+ response = await this.postCodex(body, sessionId, signal);
126
+ }
127
+ catch (err) {
128
+ yield toErrorEvent(err);
129
+ return;
130
+ }
131
+ if (response.status === 401) {
132
+ // Token might've been revoked between our pre-check and send; try one
133
+ // forced refresh and replay. A second 401 is fatal.
134
+ try {
135
+ await this.refreshNow();
136
+ armIdle();
137
+ response = await this.postCodex(body, sessionId, signal);
138
+ }
139
+ catch (err) {
140
+ yield toErrorEvent(err);
141
+ return;
142
+ }
143
+ // A 401 that survives a forced refresh means the OAuth grant itself was
144
+ // rejected (expired/revoked), not a transient skew. Surface the same
145
+ // actionable re-auth guidance as ensureFresh rather than the generic
146
+ // "returned 401" HTTP message below.
147
+ if (response.status === 401) {
148
+ yield {
149
+ type: 'error',
150
+ message: 'ChatGPT OAuth credentials were rejected after a token refresh. Run `moxxy login openai-codex` to re-authenticate.',
151
+ retryable: false,
152
+ };
153
+ return;
154
+ }
155
+ }
156
+ if (!response.ok || !response.body) {
157
+ // Cap the buffered error body: a hostile/broken backend could otherwise
158
+ // stream a huge body that we'd hold in full and then put into a string.
159
+ const text = await readCappedText(response, MAX_ERROR_BODY_CHARS);
160
+ // Derive the retryable verdict from the SDK's status classifier so it
161
+ // stays consistent with the rest of moxxy (429 + 5xx are retryable).
162
+ const classified = classifyHttpStatus(response.status);
163
+ const retryable = classified?.code === 'PROVIDER_RATE_LIMITED' ||
164
+ classified?.code === 'PROVIDER_SERVER_ERROR';
165
+ yield {
166
+ type: 'error',
167
+ message: `Codex /responses returned ${response.status}: ${text || response.statusText}`,
168
+ retryable,
169
+ };
170
+ return;
171
+ }
172
+ yield* consumeResponsesSse(response.body, signal, emitReasoning, armIdle);
173
+ }
174
+ finally {
175
+ disarmIdle();
176
+ }
177
+ }
178
+ async countTokens(req) {
179
+ // Only sum text. For non-text blocks (image/document) we add a flat per-block
180
+ // estimate instead of JSON.stringify-ing the base64 `data`, which would be a
181
+ // multi-megabyte transient allocation AND a meaningless token count (base64
182
+ // char length, not visual tokens) that skews any pre-flight budgeting.
183
+ let blob = req.system ?? '';
184
+ let nonTextBlocks = 0;
185
+ for (const m of req.messages) {
186
+ for (const c of m.content) {
187
+ if ('text' in c && typeof c.text === 'string')
188
+ blob += c.text;
189
+ else
190
+ nonTextBlocks += 1;
191
+ }
192
+ }
193
+ blob += (req.tools ?? []).map((t) => t.name + t.description).join('');
194
+ return estimateTextTokens(blob) + nonTextBlocks * NON_TEXT_BLOCK_TOKENS;
195
+ }
196
+ postCodex(body, sessionId, signal) {
197
+ if (!this.tokens)
198
+ throw new Error('No tokens');
199
+ return this.fetchImpl(CODEX_RESPONSES_URL, {
200
+ method: 'POST',
201
+ headers: buildCodexHeaders(this.tokens, sessionId),
202
+ body: JSON.stringify(body),
203
+ ...(signal ? { signal } : {}),
204
+ });
205
+ }
206
+ async ensureFresh() {
207
+ if (!this.tokens) {
208
+ throw new Error('No ChatGPT OAuth credentials available. Run `moxxy login openai-codex` to sign in.');
209
+ }
210
+ // 60s skew window — refresh proactively if the token will die very soon.
211
+ if (this.tokens.expires > Date.now() + 60_000)
212
+ return;
213
+ await this.refreshNow();
214
+ }
215
+ /**
216
+ * Refresh + persist under the per-credential lock. The refresh token is
217
+ * SINGLE-USE (rotated + invalidated on every refresh), so concurrent
218
+ * refreshers — a second stream in this process, the whisper-stt
219
+ * transcriber sharing this credential, or another moxxy process — must
220
+ * serialize and coalesce: whoever holds the lock refreshes once, everyone
221
+ * queued behind it adopts the rotated bundle instead of burning it.
222
+ */
223
+ async refreshNow() {
224
+ const entry = this.tokens;
225
+ if (!entry) {
226
+ throw new Error('Cannot refresh — no stored tokens.');
227
+ }
228
+ await withCredentialLock(`oauth-${this.name}`, async () => {
229
+ // Coalesce in-process: another stream refreshed while we waited.
230
+ if (this.tokens && this.tokens.access !== entry.access)
231
+ return;
232
+ // Coalesce cross-process/cross-consumer: adopt a fresher persisted
233
+ // bundle (and at minimum its rotated refresh token) when available.
234
+ let attempt = this.tokens ?? entry;
235
+ if (this.reloadTokens) {
236
+ const latest = await this.reloadTokens().catch(() => null);
237
+ if (latest && latest.access !== attempt.access && latest.expires > Date.now() + 60_000) {
238
+ this.tokens = withAccountId(latest, latest.accountId ?? attempt.accountId);
239
+ return;
240
+ }
241
+ if (latest?.refresh)
242
+ attempt = { ...attempt, refresh: latest.refresh };
243
+ }
244
+ let next;
245
+ try {
246
+ next = await refreshTokens(attempt.refresh, this.fetchImpl);
247
+ }
248
+ catch (err) {
249
+ // invalid_grant-style rejection: someone rotated our refresh token
250
+ // away after the reload above. Re-read once and retry with the
251
+ // fresher token before surfacing the failure.
252
+ const latest = isAuthRejection(err) && this.reloadTokens
253
+ ? await this.reloadTokens().catch(() => null)
254
+ : null;
255
+ if (!latest?.refresh || latest.refresh === attempt.refresh)
256
+ throw err;
257
+ next = await refreshTokens(latest.refresh, this.fetchImpl);
258
+ }
259
+ // Preserve a previously known accountId if the refresh response didn't
260
+ // re-issue an id_token. Without this we'd silently lose the
261
+ // ChatGPT-Account-Id header on every refresh.
262
+ const merged = withAccountId(next, next.accountId ?? attempt.accountId);
263
+ this.tokens = merged;
264
+ if (this.onTokensRefreshed) {
265
+ // Persist BEFORE the caller issues the API call so a crash here
266
+ // doesn't strand an unwritten refresh token in memory.
267
+ await this.onTokensRefreshed(merged);
268
+ }
269
+ });
270
+ }
271
+ }
272
+ function withAccountId(tokens, accountId) {
273
+ return accountId
274
+ ? { access: tokens.access, refresh: tokens.refresh, expires: tokens.expires, accountId }
275
+ : { access: tokens.access, refresh: tokens.refresh, expires: tokens.expires };
276
+ }
277
+ /**
278
+ * Read an error-response body but stop once `maxChars` is reached, then release
279
+ * the socket. Avoids pulling a hostile/broken multi-megabyte error body fully
280
+ * into memory just to put a prefix of it in an error message. Decode failures or
281
+ * a missing body yield `''` so the caller falls back to `response.statusText`.
282
+ */
283
+ async function readCappedText(response, maxChars) {
284
+ const body = response.body;
285
+ if (!body)
286
+ return '';
287
+ const reader = body.getReader();
288
+ const decoder = new TextDecoder('utf-8');
289
+ let out = '';
290
+ try {
291
+ while (out.length < maxChars) {
292
+ const { done, value } = await reader.read();
293
+ if (done)
294
+ break;
295
+ out += decoder.decode(value, { stream: true });
296
+ }
297
+ return out.slice(0, maxChars);
298
+ }
299
+ catch {
300
+ return out.slice(0, maxChars);
301
+ }
302
+ finally {
303
+ reader.cancel().catch(() => { });
304
+ }
305
+ }
306
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ import { type ProviderMessage, type ProviderRequest, type ToolDef } from '@moxxy/sdk';
2
+ /**
3
+ * Responses-API "input" item shape. We only emit the subset codex uses:
4
+ * - `message` items with role + a string-or-rich content
5
+ * - `function_call` items (assistant tool invocations replayed back)
6
+ * - `function_call_output` items (tool results)
7
+ */
8
+ type ResponsesInputItem = {
9
+ type: 'message';
10
+ role: 'user' | 'assistant' | 'system';
11
+ content: ReadonlyArray<ResponsesContent>;
12
+ } | {
13
+ type: 'function_call';
14
+ call_id: string;
15
+ name: string;
16
+ arguments: string;
17
+ } | {
18
+ type: 'function_call_output';
19
+ call_id: string;
20
+ output: string;
21
+ };
22
+ type ResponsesContent = {
23
+ type: 'input_text';
24
+ text: string;
25
+ } | {
26
+ type: 'output_text';
27
+ text: string;
28
+ } | {
29
+ type: 'input_image';
30
+ image_url: string;
31
+ } | {
32
+ type: 'input_file';
33
+ filename?: string;
34
+ file_data: string;
35
+ };
36
+ interface ResponsesTool {
37
+ type: 'function';
38
+ name: string;
39
+ description: string;
40
+ parameters: unknown;
41
+ strict?: boolean;
42
+ }
43
+ export interface ResponsesBody {
44
+ model: string;
45
+ instructions?: string;
46
+ input: ResponsesInputItem[];
47
+ tools?: ResponsesTool[];
48
+ parallel_tool_calls?: boolean;
49
+ reasoning?: {
50
+ effort?: 'low' | 'medium' | 'high';
51
+ summary?: 'auto' | 'detailed';
52
+ };
53
+ store?: boolean;
54
+ stream: true;
55
+ prompt_cache_key?: string;
56
+ include?: string[];
57
+ }
58
+ /**
59
+ * Extract any system-role text from a message array. Used to hoist the
60
+ * system prompt into the top-level `instructions` field — the Responses
61
+ * API rejects requests with a missing/empty `instructions`, and our
62
+ * upstream loop helpers push the system prompt in as a `role: 'system'`
63
+ * message. `explicitSystem` (`ProviderRequest.system`) is the
64
+ * hook-injection side channel — extra per-request system text appended
65
+ * AFTER the message-derived prompt, matching how the other providers
66
+ * deliver it.
67
+ */
68
+ export declare function extractSystemText(messages: ReadonlyArray<ProviderMessage>, explicitSystem?: string): string;
69
+ export declare function toResponsesInput(messages: ReadonlyArray<ProviderMessage>): ResponsesInputItem[];
70
+ export declare function toResponsesTools(tools: ReadonlyArray<ToolDef>): ResponsesTool[];
71
+ export interface BuildBodyOptions {
72
+ readonly sessionHint?: string;
73
+ readonly reasoningEffort?: 'low' | 'medium' | 'high';
74
+ }
75
+ export declare function toResponsesBody(req: ProviderRequest, opts?: BuildBodyOptions): ResponsesBody;
76
+ export {};
77
+ //# sourceMappingURL=translate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../src/translate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAEvG;;;;;GAKG;AACH,KAAK,kBAAkB,GACnB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IAAC,OAAO,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAA;CAAE,GACpG;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC3E;IAAE,IAAI,EAAE,sBAAsB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE,KAAK,gBAAgB,GACjB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjE,UAAU,aAAa;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,KAAK,CAAC,EAAE,aAAa,EAAE,CAAC;IACxB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,SAAS,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAC;IAClF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,IAAI,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CAMpB;AAuBD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,aAAa,CAAC,eAAe,CAAC,EACxC,cAAc,CAAC,EAAE,MAAM,GACtB,MAAM,CAUR;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,aAAa,CAAC,eAAe,CAAC,GAAG,kBAAkB,EAAE,CAmD/F;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG,aAAa,EAAE,CAO/E;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;CACtD;AAkDD,wBAAgB,eAAe,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,GAAE,gBAAqB,GAAG,aAAa,CAiBhG"}
@@ -0,0 +1,172 @@
1
+ import { zodToJsonSchema } from '@moxxy/sdk';
2
+ function contentBlocksToInputText(role, blocks) {
3
+ const out = [];
4
+ for (const block of blocks) {
5
+ if (block.type === 'text') {
6
+ out.push(role === 'assistant' ? { type: 'output_text', text: block.text } : { type: 'input_text', text: block.text });
7
+ }
8
+ else if (block.type === 'image') {
9
+ out.push({ type: 'input_image', image_url: `data:${block.mediaType};base64,${block.data}` });
10
+ }
11
+ else if (block.type === 'document') {
12
+ out.push({
13
+ type: 'input_file',
14
+ ...(block.name ? { filename: block.name } : {}),
15
+ file_data: `data:${block.mediaType};base64,${block.data}`,
16
+ });
17
+ }
18
+ }
19
+ return out;
20
+ }
21
+ /**
22
+ * Extract any system-role text from a message array. Used to hoist the
23
+ * system prompt into the top-level `instructions` field — the Responses
24
+ * API rejects requests with a missing/empty `instructions`, and our
25
+ * upstream loop helpers push the system prompt in as a `role: 'system'`
26
+ * message. `explicitSystem` (`ProviderRequest.system`) is the
27
+ * hook-injection side channel — extra per-request system text appended
28
+ * AFTER the message-derived prompt, matching how the other providers
29
+ * deliver it.
30
+ */
31
+ export function extractSystemText(messages, explicitSystem) {
32
+ const parts = [];
33
+ for (const msg of messages) {
34
+ if (msg.role !== 'system')
35
+ continue;
36
+ for (const block of msg.content) {
37
+ if (block.type === 'text' && block.text)
38
+ parts.push(block.text);
39
+ }
40
+ }
41
+ if (explicitSystem && explicitSystem.trim())
42
+ parts.push(explicitSystem);
43
+ return parts.join('\n\n');
44
+ }
45
+ export function toResponsesInput(messages) {
46
+ const out = [];
47
+ for (const msg of messages) {
48
+ if (msg.role === 'system') {
49
+ // System prompt is hoisted to top-level `instructions` by
50
+ // `toResponsesBody`; don't duplicate it as an input message too.
51
+ continue;
52
+ }
53
+ if (msg.role === 'user') {
54
+ const content = contentBlocksToInputText('user', msg.content);
55
+ if (content.length > 0) {
56
+ out.push({ type: 'message', role: 'user', content });
57
+ }
58
+ else if (msg.content.length > 0) {
59
+ // The message carried blocks but none translated (unhandled block types):
60
+ // keep the turn with an empty input_text so we don't silently drop it
61
+ // from the request and lose conversational context.
62
+ out.push({ type: 'message', role: 'user', content: [{ type: 'input_text', text: '' }] });
63
+ }
64
+ continue;
65
+ }
66
+ if (msg.role === 'assistant') {
67
+ const text = msg.content
68
+ .filter((c) => c.type === 'text')
69
+ .map((c) => c.text)
70
+ .join('');
71
+ if (text)
72
+ out.push({ type: 'message', role: 'assistant', content: [{ type: 'output_text', text }] });
73
+ for (const block of msg.content) {
74
+ if (block.type === 'tool_use') {
75
+ out.push({
76
+ type: 'function_call',
77
+ call_id: block.id,
78
+ name: block.name,
79
+ arguments: JSON.stringify(block.input ?? {}),
80
+ });
81
+ }
82
+ }
83
+ continue;
84
+ }
85
+ if (msg.role === 'tool_result') {
86
+ for (const block of msg.content) {
87
+ if (block.type === 'tool_result') {
88
+ out.push({
89
+ type: 'function_call_output',
90
+ call_id: block.toolUseId,
91
+ output: block.content,
92
+ });
93
+ }
94
+ }
95
+ }
96
+ }
97
+ return out;
98
+ }
99
+ export function toResponsesTools(tools) {
100
+ return tools.map((t) => ({
101
+ type: 'function',
102
+ name: t.name,
103
+ description: t.description,
104
+ parameters: (t.inputJsonSchema ?? zodToJsonSchema(t.inputSchema)),
105
+ }));
106
+ }
107
+ /**
108
+ * Default `instructions` value used when the caller supplies neither
109
+ * `req.system` nor a system-role message. The Codex backend rejects
110
+ * requests with an empty `instructions` field (`400: Instructions are
111
+ * required`), so we always send something — falling back to a minimal
112
+ * agent identity matches what codex-rs does for plain `codex exec` runs.
113
+ */
114
+ const DEFAULT_INSTRUCTIONS = 'You are a helpful coding assistant.';
115
+ /**
116
+ * One-shot debug note for the unsupported `temperature` param. The Codex
117
+ * backend only serves gpt-5-family reasoning models, which reject
118
+ * sampling params with `400: Unsupported parameter: 'temperature'` — so
119
+ * rather than forward it and break every request, we drop it and (once
120
+ * per process, only with MOXXY_DEBUG set) say so instead of silently
121
+ * eating the option.
122
+ */
123
+ let temperatureNoteShown = false;
124
+ function noteTemperatureUnsupported() {
125
+ if (temperatureNoteShown)
126
+ return;
127
+ temperatureNoteShown = true;
128
+ if (process.env.MOXXY_DEBUG) {
129
+ console.debug('[openai-codex] ProviderRequest.temperature is not supported by the Codex ' +
130
+ 'Responses backend (gpt-5 reasoning models reject sampling params); ignoring it.');
131
+ }
132
+ }
133
+ /**
134
+ * One-shot debug note for the unsupported `maxTokens` param. Unlike the
135
+ * platform Responses API, the ChatGPT-plan Codex backend rejects
136
+ * `max_output_tokens` with `400: {"detail":"Unsupported parameter:
137
+ * max_output_tokens"}` — observed live when `workflow_create` passed a draft
138
+ * budget. Same policy as temperature: drop it instead of breaking the request.
139
+ */
140
+ let maxTokensNoteShown = false;
141
+ function noteMaxTokensUnsupported() {
142
+ if (maxTokensNoteShown)
143
+ return;
144
+ maxTokensNoteShown = true;
145
+ if (process.env.MOXXY_DEBUG) {
146
+ console.debug('[openai-codex] ProviderRequest.maxTokens is not supported by the Codex ' +
147
+ 'Responses backend (400 Unsupported parameter: max_output_tokens); ignoring it.');
148
+ }
149
+ }
150
+ export function toResponsesBody(req, opts = {}) {
151
+ const instructions = extractSystemText(req.messages, req.system) || DEFAULT_INSTRUCTIONS;
152
+ const body = {
153
+ model: req.model,
154
+ instructions,
155
+ input: toResponsesInput(req.messages),
156
+ stream: true,
157
+ store: false,
158
+ parallel_tool_calls: true,
159
+ reasoning: { effort: opts.reasoningEffort ?? 'medium', summary: 'auto' },
160
+ include: ['reasoning.encrypted_content'],
161
+ };
162
+ if (req.tools && req.tools.length > 0)
163
+ body.tools = toResponsesTools(req.tools);
164
+ if (opts.sessionHint)
165
+ body.prompt_cache_key = opts.sessionHint;
166
+ if (req.maxTokens !== undefined)
167
+ noteMaxTokensUnsupported();
168
+ if (req.temperature !== undefined)
169
+ noteTemperatureUnsupported();
170
+ return body;
171
+ }
172
+ //# sourceMappingURL=translate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translate.js","sourceRoot":"","sources":["../src/translate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * OAuth credentials for the OpenAI Codex (ChatGPT Pro/Plus) provider.
3
+ * `expires` is an absolute epoch-millis timestamp so a paused process resumes
4
+ * with a correct refresh decision regardless of clock drift across runs.
5
+ */
6
+ export interface CodexTokens {
7
+ readonly access: string;
8
+ readonly refresh: string;
9
+ readonly expires: number;
10
+ readonly accountId?: string;
11
+ }
12
+ export interface PkceCodes {
13
+ readonly verifier: string;
14
+ readonly challenge: string;
15
+ }
16
+ /**
17
+ * Raw shape returned by https://auth.openai.com/oauth/token. Used internally
18
+ * by `exchangeCodeForTokens` / `refreshTokens` before normalization.
19
+ */
20
+ export interface OAuthTokenResponse {
21
+ readonly id_token?: string;
22
+ readonly access_token: string;
23
+ readonly refresh_token: string;
24
+ readonly expires_in?: number;
25
+ readonly token_type?: string;
26
+ }
27
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}