oc-chatgpt-multi-auth 4.9.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 (134) hide show
  1. package/LICENSE +37 -0
  2. package/README.md +507 -0
  3. package/assets/opencode-logo-ornate-dark.svg +18 -0
  4. package/assets/readme-hero.svg +31 -0
  5. package/config/README.md +110 -0
  6. package/config/minimal-opencode.json +13 -0
  7. package/config/opencode-legacy.json +572 -0
  8. package/config/opencode-modern.json +240 -0
  9. package/dist/index.d.ts +45 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +971 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/lib/accounts.d.ts +120 -0
  14. package/dist/lib/accounts.d.ts.map +1 -0
  15. package/dist/lib/accounts.js +579 -0
  16. package/dist/lib/accounts.js.map +1 -0
  17. package/dist/lib/auth/auth.d.ts +51 -0
  18. package/dist/lib/auth/auth.d.ts.map +1 -0
  19. package/dist/lib/auth/auth.js +180 -0
  20. package/dist/lib/auth/auth.js.map +1 -0
  21. package/dist/lib/auth/browser.d.ts +17 -0
  22. package/dist/lib/auth/browser.d.ts.map +1 -0
  23. package/dist/lib/auth/browser.js +83 -0
  24. package/dist/lib/auth/browser.js.map +1 -0
  25. package/dist/lib/auth/server.d.ts +10 -0
  26. package/dist/lib/auth/server.d.ts.map +1 -0
  27. package/dist/lib/auth/server.js +85 -0
  28. package/dist/lib/auth/server.js.map +1 -0
  29. package/dist/lib/auto-update-checker.d.ts +10 -0
  30. package/dist/lib/auto-update-checker.d.ts.map +1 -0
  31. package/dist/lib/auto-update-checker.js +129 -0
  32. package/dist/lib/auto-update-checker.js.map +1 -0
  33. package/dist/lib/cli.d.ts +9 -0
  34. package/dist/lib/cli.d.ts.map +1 -0
  35. package/dist/lib/cli.js +50 -0
  36. package/dist/lib/cli.js.map +1 -0
  37. package/dist/lib/config.d.ts +17 -0
  38. package/dist/lib/config.d.ts.map +1 -0
  39. package/dist/lib/config.js +102 -0
  40. package/dist/lib/config.js.map +1 -0
  41. package/dist/lib/constants.d.ts +74 -0
  42. package/dist/lib/constants.d.ts.map +1 -0
  43. package/dist/lib/constants.js +74 -0
  44. package/dist/lib/constants.js.map +1 -0
  45. package/dist/lib/context-overflow.d.ts +27 -0
  46. package/dist/lib/context-overflow.d.ts.map +1 -0
  47. package/dist/lib/context-overflow.js +124 -0
  48. package/dist/lib/context-overflow.js.map +1 -0
  49. package/dist/lib/index.d.ts +13 -0
  50. package/dist/lib/index.d.ts.map +1 -0
  51. package/dist/lib/index.js +13 -0
  52. package/dist/lib/index.js.map +1 -0
  53. package/dist/lib/logger.d.ts +22 -0
  54. package/dist/lib/logger.d.ts.map +1 -0
  55. package/dist/lib/logger.js +175 -0
  56. package/dist/lib/logger.js.map +1 -0
  57. package/dist/lib/oauth-success.html +712 -0
  58. package/dist/lib/prompts/codex-opencode-bridge.d.ts +19 -0
  59. package/dist/lib/prompts/codex-opencode-bridge.d.ts.map +1 -0
  60. package/dist/lib/prompts/codex-opencode-bridge.js +152 -0
  61. package/dist/lib/prompts/codex-opencode-bridge.js.map +1 -0
  62. package/dist/lib/prompts/codex.d.ts +32 -0
  63. package/dist/lib/prompts/codex.d.ts.map +1 -0
  64. package/dist/lib/prompts/codex.js +262 -0
  65. package/dist/lib/prompts/codex.js.map +1 -0
  66. package/dist/lib/prompts/opencode-codex.d.ts +21 -0
  67. package/dist/lib/prompts/opencode-codex.d.ts.map +1 -0
  68. package/dist/lib/prompts/opencode-codex.js +91 -0
  69. package/dist/lib/prompts/opencode-codex.js.map +1 -0
  70. package/dist/lib/recovery/constants.d.ts +12 -0
  71. package/dist/lib/recovery/constants.d.ts.map +1 -0
  72. package/dist/lib/recovery/constants.js +25 -0
  73. package/dist/lib/recovery/constants.js.map +1 -0
  74. package/dist/lib/recovery/index.d.ts +12 -0
  75. package/dist/lib/recovery/index.d.ts.map +1 -0
  76. package/dist/lib/recovery/index.js +12 -0
  77. package/dist/lib/recovery/index.js.map +1 -0
  78. package/dist/lib/recovery/storage.d.ts +24 -0
  79. package/dist/lib/recovery/storage.d.ts.map +1 -0
  80. package/dist/lib/recovery/storage.js +354 -0
  81. package/dist/lib/recovery/storage.js.map +1 -0
  82. package/dist/lib/recovery/types.d.ts +116 -0
  83. package/dist/lib/recovery/types.d.ts.map +1 -0
  84. package/dist/lib/recovery/types.js +7 -0
  85. package/dist/lib/recovery/types.js.map +1 -0
  86. package/dist/lib/recovery.d.ts +31 -0
  87. package/dist/lib/recovery.d.ts.map +1 -0
  88. package/dist/lib/recovery.js +308 -0
  89. package/dist/lib/recovery.js.map +1 -0
  90. package/dist/lib/refresh-queue.d.ts +100 -0
  91. package/dist/lib/refresh-queue.d.ts.map +1 -0
  92. package/dist/lib/refresh-queue.js +196 -0
  93. package/dist/lib/refresh-queue.js.map +1 -0
  94. package/dist/lib/request/fetch-helpers.d.ts +81 -0
  95. package/dist/lib/request/fetch-helpers.d.ts.map +1 -0
  96. package/dist/lib/request/fetch-helpers.js +325 -0
  97. package/dist/lib/request/fetch-helpers.js.map +1 -0
  98. package/dist/lib/request/helpers/input-utils.d.ts +7 -0
  99. package/dist/lib/request/helpers/input-utils.d.ts.map +1 -0
  100. package/dist/lib/request/helpers/input-utils.js +213 -0
  101. package/dist/lib/request/helpers/input-utils.js.map +1 -0
  102. package/dist/lib/request/helpers/model-map.d.ts +28 -0
  103. package/dist/lib/request/helpers/model-map.d.ts.map +1 -0
  104. package/dist/lib/request/helpers/model-map.js +109 -0
  105. package/dist/lib/request/helpers/model-map.js.map +1 -0
  106. package/dist/lib/request/rate-limit-backoff.d.ts +17 -0
  107. package/dist/lib/request/rate-limit-backoff.d.ts.map +1 -0
  108. package/dist/lib/request/rate-limit-backoff.js +74 -0
  109. package/dist/lib/request/rate-limit-backoff.js.map +1 -0
  110. package/dist/lib/request/request-transformer.d.ts +93 -0
  111. package/dist/lib/request/request-transformer.d.ts.map +1 -0
  112. package/dist/lib/request/request-transformer.js +405 -0
  113. package/dist/lib/request/request-transformer.js.map +1 -0
  114. package/dist/lib/request/response-handler.d.ts +14 -0
  115. package/dist/lib/request/response-handler.d.ts.map +1 -0
  116. package/dist/lib/request/response-handler.js +90 -0
  117. package/dist/lib/request/response-handler.js.map +1 -0
  118. package/dist/lib/rotation.d.ts +121 -0
  119. package/dist/lib/rotation.d.ts.map +1 -0
  120. package/dist/lib/rotation.js +248 -0
  121. package/dist/lib/rotation.js.map +1 -0
  122. package/dist/lib/storage.d.ts +91 -0
  123. package/dist/lib/storage.d.ts.map +1 -0
  124. package/dist/lib/storage.js +323 -0
  125. package/dist/lib/storage.js.map +1 -0
  126. package/dist/lib/types.d.ts +185 -0
  127. package/dist/lib/types.d.ts.map +1 -0
  128. package/dist/lib/types.js +2 -0
  129. package/dist/lib/types.js.map +1 -0
  130. package/package.json +86 -0
  131. package/scripts/copy-oauth-success.js +37 -0
  132. package/scripts/install-opencode-codex-auth.js +193 -0
  133. package/scripts/test-all-models.sh +260 -0
  134. package/scripts/validate-model-map.sh +97 -0
@@ -0,0 +1,405 @@
1
+ import { logDebug, logWarn } from "../logger.js";
2
+ import { TOOL_REMAP_MESSAGE } from "../prompts/codex.js";
3
+ import { CODEX_OPENCODE_BRIDGE } from "../prompts/codex-opencode-bridge.js";
4
+ import { getOpenCodeCodexPrompt } from "../prompts/opencode-codex.js";
5
+ import { getNormalizedModel } from "./helpers/model-map.js";
6
+ import { filterOpenCodeSystemPromptsWithCachedPrompt, normalizeOrphanedToolOutputs, injectMissingToolOutputs, } from "./helpers/input-utils.js";
7
+ export { isOpenCodeSystemPrompt, filterOpenCodeSystemPromptsWithCachedPrompt, } from "./helpers/input-utils.js";
8
+ /**
9
+ * Normalize model name to Codex-supported variants
10
+ *
11
+ * Uses explicit model map for known models, with fallback pattern matching
12
+ * for unknown/custom model names.
13
+ *
14
+ * @param model - Original model name (e.g., "gpt-5.1-codex-low", "openai/gpt-5-codex")
15
+ * @returns Normalized model name (e.g., "gpt-5.1-codex", "gpt-5-codex")
16
+ */
17
+ export function normalizeModel(model) {
18
+ if (!model)
19
+ return "gpt-5.1";
20
+ // Strip provider prefix if present (e.g., "openai/gpt-5-codex" → "gpt-5-codex")
21
+ const modelId = model.includes("/") ? model.split("/").pop() ?? model : model;
22
+ // Try explicit model map first (handles all known model variants)
23
+ const mappedModel = getNormalizedModel(modelId);
24
+ if (mappedModel) {
25
+ return mappedModel;
26
+ }
27
+ // Fallback: Pattern-based matching for unknown/custom model names
28
+ // This preserves backwards compatibility with old verbose names
29
+ // like "GPT 5 Codex Low (ChatGPT Subscription)"
30
+ const normalized = modelId.toLowerCase();
31
+ // Priority order for pattern matching (most specific first):
32
+ // 1. GPT-5.2 Codex (newest codex model)
33
+ if (normalized.includes("gpt-5.2-codex") ||
34
+ normalized.includes("gpt 5.2 codex")) {
35
+ return "gpt-5.2-codex";
36
+ }
37
+ // 2. GPT-5.2 (general purpose)
38
+ if (normalized.includes("gpt-5.2") || normalized.includes("gpt 5.2")) {
39
+ return "gpt-5.2";
40
+ }
41
+ // 3. GPT-5.1 Codex Max
42
+ if (normalized.includes("gpt-5.1-codex-max") ||
43
+ normalized.includes("gpt 5.1 codex max")) {
44
+ return "gpt-5.1-codex-max";
45
+ }
46
+ // 4. GPT-5.1 Codex Mini
47
+ if (normalized.includes("gpt-5.1-codex-mini") ||
48
+ normalized.includes("gpt 5.1 codex mini")) {
49
+ return "gpt-5.1-codex-mini";
50
+ }
51
+ // 5. Legacy Codex Mini
52
+ if (normalized.includes("codex-mini-latest") ||
53
+ normalized.includes("gpt-5-codex-mini") ||
54
+ normalized.includes("gpt 5 codex mini")) {
55
+ return "codex-mini-latest";
56
+ }
57
+ // 6. GPT-5.1 Codex
58
+ if (normalized.includes("gpt-5.1-codex") ||
59
+ normalized.includes("gpt 5.1 codex")) {
60
+ return "gpt-5.1-codex";
61
+ }
62
+ // 7. GPT-5.1 (general-purpose)
63
+ if (normalized.includes("gpt-5.1") || normalized.includes("gpt 5.1")) {
64
+ return "gpt-5.1";
65
+ }
66
+ // 8. GPT-5 Codex family (any variant with "codex")
67
+ if (normalized.includes("codex")) {
68
+ return "gpt-5.1-codex";
69
+ }
70
+ // 9. GPT-5 family (any variant) - default to 5.1 as 5 is being phased out
71
+ if (normalized.includes("gpt-5") || normalized.includes("gpt 5")) {
72
+ return "gpt-5.1";
73
+ }
74
+ // Default fallback - use gpt-5.1 as gpt-5 is being phased out
75
+ return "gpt-5.1";
76
+ }
77
+ /**
78
+ * Extract configuration for a specific model
79
+ * Merges global options with model-specific options (model-specific takes precedence)
80
+ * @param modelName - Model name (e.g., "gpt-5-codex")
81
+ * @param userConfig - Full user configuration object
82
+ * @returns Merged configuration for this model
83
+ */
84
+ export function getModelConfig(modelName, userConfig = { global: {}, models: {} }) {
85
+ const globalOptions = userConfig.global || {};
86
+ const modelOptions = userConfig.models?.[modelName]?.options || {};
87
+ // Model-specific options override global options
88
+ return { ...globalOptions, ...modelOptions };
89
+ }
90
+ function resolveReasoningConfig(modelName, modelConfig, body) {
91
+ const providerOpenAI = body.providerOptions?.openai;
92
+ const existingEffort = body.reasoning?.effort ?? providerOpenAI?.reasoningEffort;
93
+ const existingSummary = body.reasoning?.summary ?? providerOpenAI?.reasoningSummary;
94
+ const mergedConfig = {
95
+ ...modelConfig,
96
+ ...(existingEffort ? { reasoningEffort: existingEffort } : {}),
97
+ ...(existingSummary ? { reasoningSummary: existingSummary } : {}),
98
+ };
99
+ return getReasoningConfig(modelName, mergedConfig);
100
+ }
101
+ function resolveTextVerbosity(modelConfig, body) {
102
+ const providerOpenAI = body.providerOptions?.openai;
103
+ return (body.text?.verbosity ??
104
+ providerOpenAI?.textVerbosity ??
105
+ modelConfig.textVerbosity ??
106
+ "medium");
107
+ }
108
+ function resolveInclude(modelConfig, body) {
109
+ const providerOpenAI = body.providerOptions?.openai;
110
+ const base = body.include ??
111
+ providerOpenAI?.include ??
112
+ modelConfig.include ??
113
+ ["reasoning.encrypted_content"];
114
+ const include = Array.from(new Set(base.filter(Boolean)));
115
+ if (!include.includes("reasoning.encrypted_content")) {
116
+ include.push("reasoning.encrypted_content");
117
+ }
118
+ return include;
119
+ }
120
+ /**
121
+ * Configure reasoning parameters based on model variant and user config
122
+ *
123
+ * NOTE: This plugin follows Codex CLI defaults instead of opencode defaults because:
124
+ * - We're accessing the ChatGPT backend API (not OpenAI Platform API)
125
+ * - opencode explicitly excludes gpt-5-codex from automatic reasoning configuration
126
+ * - Codex CLI has been thoroughly tested against this backend
127
+ *
128
+ * @param originalModel - Original model name before normalization
129
+ * @param userConfig - User configuration object
130
+ * @returns Reasoning configuration
131
+ */
132
+ export function getReasoningConfig(modelName, userConfig = {}) {
133
+ const normalizedName = modelName?.toLowerCase() ?? "";
134
+ // GPT-5.2 Codex is the newest codex model (supports xhigh, but not "none")
135
+ const isGpt52Codex = normalizedName.includes("gpt-5.2-codex") ||
136
+ normalizedName.includes("gpt 5.2 codex");
137
+ // GPT-5.2 general purpose (not codex variant)
138
+ const isGpt52General = (normalizedName.includes("gpt-5.2") || normalizedName.includes("gpt 5.2")) &&
139
+ !isGpt52Codex;
140
+ const isCodexMax = normalizedName.includes("codex-max") ||
141
+ normalizedName.includes("codex max");
142
+ const isCodexMini = normalizedName.includes("codex-mini") ||
143
+ normalizedName.includes("codex mini") ||
144
+ normalizedName.includes("codex_mini") ||
145
+ normalizedName.includes("codex-mini-latest");
146
+ const isCodex = normalizedName.includes("codex") && !isCodexMini;
147
+ const isLightweight = !isCodexMini &&
148
+ (normalizedName.includes("nano") ||
149
+ normalizedName.includes("mini"));
150
+ // GPT-5.1 general purpose (not codex variants) - supports "none" per OpenAI API docs
151
+ const isGpt51General = (normalizedName.includes("gpt-5.1") || normalizedName.includes("gpt 5.1")) &&
152
+ !isCodex &&
153
+ !isCodexMax &&
154
+ !isCodexMini;
155
+ // GPT 5.2, GPT 5.2 Codex, and Codex Max support xhigh reasoning
156
+ const supportsXhigh = isGpt52General || isGpt52Codex || isCodexMax;
157
+ // GPT 5.1 general and GPT 5.2 general support "none" reasoning per:
158
+ // - OpenAI API docs: "gpt-5.1 defaults to none, supports: none, low, medium, high"
159
+ // - Codex CLI: ReasoningEffort enum includes None variant (codex-rs/protocol/src/openai_models.rs)
160
+ // - Codex CLI: docs/config.md lists "none" as valid for model_reasoning_effort
161
+ // - gpt-5.2 (being newer) also supports: none, low, medium, high, xhigh
162
+ // - Codex models (including GPT-5.2 Codex) do NOT support "none"
163
+ const supportsNone = isGpt52General || isGpt51General;
164
+ // Default based on model type (Codex CLI defaults)
165
+ // Note: OpenAI docs say gpt-5.1 defaults to "none", but we default to "medium"
166
+ // for better coding assistance unless user explicitly requests "none"
167
+ const defaultEffort = isCodexMini
168
+ ? "medium"
169
+ : supportsXhigh
170
+ ? "high"
171
+ : isLightweight
172
+ ? "minimal"
173
+ : "medium";
174
+ // Get user-requested effort
175
+ let effort = userConfig.reasoningEffort || defaultEffort;
176
+ if (isCodexMini) {
177
+ if (effort === "minimal" || effort === "low" || effort === "none") {
178
+ effort = "medium";
179
+ }
180
+ if (effort === "xhigh") {
181
+ effort = "high";
182
+ }
183
+ if (effort !== "high" && effort !== "medium") {
184
+ effort = "medium";
185
+ }
186
+ }
187
+ // For models that don't support xhigh, downgrade to high
188
+ if (!supportsXhigh && effort === "xhigh") {
189
+ effort = "high";
190
+ }
191
+ // For models that don't support "none", upgrade to "low"
192
+ // (Codex models don't support "none" - only GPT-5.1 and GPT-5.2 general purpose do)
193
+ if (!supportsNone && effort === "none") {
194
+ effort = "low";
195
+ }
196
+ // Normalize "minimal" to "low" for Codex families
197
+ // Codex CLI presets are low/medium/high (or xhigh for Codex Max / GPT-5.2 Codex)
198
+ if (isCodex && effort === "minimal") {
199
+ effort = "low";
200
+ }
201
+ return {
202
+ effort,
203
+ summary: userConfig.reasoningSummary || "auto", // Changed from "detailed" to match Codex CLI
204
+ };
205
+ }
206
+ /**
207
+ * Filter input array for stateless Codex API (store: false)
208
+ *
209
+ * Two transformations needed:
210
+ * 1. Remove AI SDK-specific items (not supported by Codex API)
211
+ * 2. Strip IDs from all remaining items (stateless mode)
212
+ *
213
+ * AI SDK constructs to REMOVE (not in OpenAI Responses API spec):
214
+ * - type: "item_reference" - AI SDK uses this for server-side state lookup
215
+ *
216
+ * Items to KEEP (strip IDs):
217
+ * - type: "message" - Conversation messages (provides context to LLM)
218
+ * - type: "function_call" - Tool calls from conversation
219
+ * - type: "function_call_output" - Tool results from conversation
220
+ *
221
+ * Context is maintained through:
222
+ * - Full message history (without IDs)
223
+ * - reasoning.encrypted_content (for reasoning continuity)
224
+ *
225
+ * @param input - Original input array from OpenCode/AI SDK
226
+ * @returns Filtered input array compatible with Codex API
227
+ */
228
+ export function filterInput(input) {
229
+ if (!Array.isArray(input))
230
+ return input;
231
+ return input
232
+ .filter((item) => {
233
+ // Remove AI SDK constructs not supported by Codex API
234
+ if (item.type === "item_reference") {
235
+ return false; // AI SDK only - references server state
236
+ }
237
+ return true; // Keep all other items
238
+ })
239
+ .map((item) => {
240
+ // Strip IDs from all items (Codex API stateless mode)
241
+ if (item.id) {
242
+ const { id: _omit, ...itemWithoutId } = item;
243
+ void _omit;
244
+ return itemWithoutId;
245
+ }
246
+ return item;
247
+ });
248
+ }
249
+ /**
250
+ * Filter out OpenCode system prompts from input
251
+ * Used in CODEX_MODE to replace OpenCode prompts with Codex-OpenCode bridge
252
+ * @param input - Input array
253
+ * @returns Input array without OpenCode system prompts
254
+ */
255
+ export async function filterOpenCodeSystemPrompts(input) {
256
+ if (!Array.isArray(input))
257
+ return input;
258
+ // Fetch cached OpenCode prompt for verification
259
+ let cachedPrompt = null;
260
+ try {
261
+ cachedPrompt = await getOpenCodeCodexPrompt();
262
+ }
263
+ catch {
264
+ // If fetch fails, fallback to text-based detection only
265
+ // This is safe because we still have the "starts with" check
266
+ }
267
+ return filterOpenCodeSystemPromptsWithCachedPrompt(input, cachedPrompt);
268
+ }
269
+ /**
270
+ * Add Codex-OpenCode bridge message to input if tools are present
271
+ * @param input - Input array
272
+ * @param hasTools - Whether tools are present in request
273
+ * @returns Input array with bridge message prepended if needed
274
+ */
275
+ export function addCodexBridgeMessage(input, hasTools) {
276
+ if (!hasTools || !Array.isArray(input))
277
+ return input;
278
+ const bridgeMessage = {
279
+ type: "message",
280
+ role: "developer",
281
+ content: [
282
+ {
283
+ type: "input_text",
284
+ text: CODEX_OPENCODE_BRIDGE,
285
+ },
286
+ ],
287
+ };
288
+ return [bridgeMessage, ...input];
289
+ }
290
+ /**
291
+ * Add tool remapping message to input if tools are present
292
+ * @param input - Input array
293
+ * @param hasTools - Whether tools are present in request
294
+ * @returns Input array with tool remap message prepended if needed
295
+ */
296
+ export function addToolRemapMessage(input, hasTools) {
297
+ if (!hasTools || !Array.isArray(input))
298
+ return input;
299
+ const toolRemapMessage = {
300
+ type: "message",
301
+ role: "developer",
302
+ content: [
303
+ {
304
+ type: "input_text",
305
+ text: TOOL_REMAP_MESSAGE,
306
+ },
307
+ ],
308
+ };
309
+ return [toolRemapMessage, ...input];
310
+ }
311
+ /**
312
+ * Transform request body for Codex API
313
+ *
314
+ * NOTE: Configuration follows Codex CLI patterns instead of opencode defaults:
315
+ * - opencode sets textVerbosity="low" for gpt-5, but Codex CLI uses "medium"
316
+ * - opencode excludes gpt-5-codex from reasoning configuration
317
+ * - This plugin uses store=false (stateless), requiring encrypted reasoning content
318
+ *
319
+ * @param body - Original request body
320
+ * @param codexInstructions - Codex system instructions
321
+ * @param userConfig - User configuration from loader
322
+ * @param codexMode - Enable CODEX_MODE (bridge prompt instead of tool remap) - defaults to true
323
+ * @returns Transformed request body
324
+ */
325
+ export async function transformRequestBody(body, codexInstructions, userConfig = { global: {}, models: {} }, codexMode = true) {
326
+ const originalModel = body.model;
327
+ const normalizedModel = normalizeModel(body.model);
328
+ // Get model-specific configuration using ORIGINAL model name (config key)
329
+ // This allows per-model options like "gpt-5-codex-low" to work correctly
330
+ const lookupModel = originalModel || normalizedModel;
331
+ const modelConfig = getModelConfig(lookupModel, userConfig);
332
+ // Debug: Log which config was resolved
333
+ logDebug(`Model config lookup: "${lookupModel}" → normalized to "${normalizedModel}" for API`, {
334
+ hasModelSpecificConfig: !!userConfig.models?.[lookupModel],
335
+ resolvedConfig: modelConfig,
336
+ });
337
+ // Normalize model name for API call
338
+ body.model = normalizedModel;
339
+ // Codex required fields
340
+ // ChatGPT backend REQUIRES store=false (confirmed via testing)
341
+ body.store = false;
342
+ // Always set stream=true for API - response handling detects original intent
343
+ body.stream = true;
344
+ body.instructions = codexInstructions;
345
+ // Prompt caching relies on the host providing a stable prompt_cache_key
346
+ // (OpenCode passes its session identifier). We no longer synthesize one here.
347
+ // Filter and transform input
348
+ if (body.input && Array.isArray(body.input)) {
349
+ // Debug: Log original input message IDs before filtering
350
+ const originalIds = body.input
351
+ .filter((item) => item.id)
352
+ .map((item) => item.id);
353
+ if (originalIds.length > 0) {
354
+ logDebug(`Filtering ${originalIds.length} message IDs from input:`, originalIds);
355
+ }
356
+ body.input = filterInput(body.input);
357
+ // Debug: Verify all IDs were removed
358
+ const remainingIds = (body.input || [])
359
+ .filter((item) => item.id)
360
+ .map((item) => item.id);
361
+ if (remainingIds.length > 0) {
362
+ logWarn(`WARNING: ${remainingIds.length} IDs still present after filtering:`, remainingIds);
363
+ }
364
+ else if (originalIds.length > 0) {
365
+ logDebug(`Successfully removed all ${originalIds.length} message IDs`);
366
+ }
367
+ if (codexMode) {
368
+ // CODEX_MODE: Remove OpenCode system prompt, add bridge prompt
369
+ body.input = await filterOpenCodeSystemPrompts(body.input);
370
+ body.input = addCodexBridgeMessage(body.input, !!body.tools);
371
+ }
372
+ else {
373
+ // DEFAULT MODE: Keep original behavior with tool remap message
374
+ body.input = addToolRemapMessage(body.input, !!body.tools);
375
+ }
376
+ // Handle orphaned function_call_output items (where function_call was an item_reference that got filtered)
377
+ // Instead of removing orphans (which causes infinite loops as LLM loses tool results),
378
+ // convert them to messages to preserve context while avoiding API errors
379
+ if (body.input) {
380
+ body.input = normalizeOrphanedToolOutputs(body.input);
381
+ body.input = injectMissingToolOutputs(body.input);
382
+ }
383
+ }
384
+ // Configure reasoning (prefer existing body/provider options, then config defaults)
385
+ const reasoningConfig = resolveReasoningConfig(normalizedModel, modelConfig, body);
386
+ body.reasoning = {
387
+ ...body.reasoning,
388
+ ...reasoningConfig,
389
+ };
390
+ // Configure text verbosity (support user config)
391
+ // Default: "medium" (matches Codex CLI default for all GPT-5 models)
392
+ body.text = {
393
+ ...body.text,
394
+ verbosity: resolveTextVerbosity(modelConfig, body),
395
+ };
396
+ // Add include for encrypted reasoning content
397
+ // Default: ["reasoning.encrypted_content"] (required for stateless operation with store=false)
398
+ // This allows reasoning context to persist across turns without server-side storage
399
+ body.include = resolveInclude(modelConfig, body);
400
+ // Remove unsupported parameters
401
+ body.max_output_tokens = undefined;
402
+ body.max_completion_tokens = undefined;
403
+ return body;
404
+ }
405
+ //# sourceMappingURL=request-transformer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-transformer.js","sourceRoot":"","sources":["../../../lib/request/request-transformer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Convert SSE stream response to JSON for generateText()
3
+ * @param response - Fetch response with SSE stream
4
+ * @param headers - Response headers
5
+ * @returns Response with JSON body
6
+ */
7
+ export declare function convertSseToJson(response: Response, headers: Headers): Promise<Response>;
8
+ /**
9
+ * Ensure response has content-type header
10
+ * @param headers - Response headers
11
+ * @returns Headers with content-type set
12
+ */
13
+ export declare function ensureContentType(headers: Headers): Headers;
14
+ //# sourceMappingURL=response-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-handler.d.ts","sourceRoot":"","sources":["../../../lib/request/response-handler.ts"],"names":[],"mappings":"AAiCA;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAoD9F;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAQ3D"}
@@ -0,0 +1,90 @@
1
+ import { createLogger, logRequest, LOGGING_ENABLED } from "../logger.js";
2
+ const log = createLogger("response-handler");
3
+ /**
4
+
5
+ * Parse SSE stream to extract final response
6
+ * @param sseText - Complete SSE stream text
7
+ * @returns Final response object or null if not found
8
+ */
9
+ function parseSseStream(sseText) {
10
+ const lines = sseText.split('\n');
11
+ for (const line of lines) {
12
+ if (line.startsWith('data: ')) {
13
+ try {
14
+ const data = JSON.parse(line.substring(6));
15
+ // Look for response.done event with final data
16
+ if (data.type === 'response.done' || data.type === 'response.completed') {
17
+ return data.response;
18
+ }
19
+ }
20
+ catch {
21
+ // Skip malformed JSON
22
+ }
23
+ }
24
+ }
25
+ return null;
26
+ }
27
+ /**
28
+ * Convert SSE stream response to JSON for generateText()
29
+ * @param response - Fetch response with SSE stream
30
+ * @param headers - Response headers
31
+ * @returns Response with JSON body
32
+ */
33
+ export async function convertSseToJson(response, headers) {
34
+ if (!response.body) {
35
+ throw new Error('[openai-codex-plugin] Response has no body');
36
+ }
37
+ const reader = response.body.getReader();
38
+ const decoder = new TextDecoder();
39
+ let fullText = '';
40
+ try {
41
+ // Consume the entire stream
42
+ while (true) {
43
+ const { done, value } = await reader.read();
44
+ if (done)
45
+ break;
46
+ fullText += decoder.decode(value, { stream: true });
47
+ }
48
+ if (LOGGING_ENABLED) {
49
+ logRequest("stream-full", { fullContent: fullText });
50
+ }
51
+ // Parse SSE events to extract the final response
52
+ const finalResponse = parseSseStream(fullText);
53
+ if (!finalResponse) {
54
+ log.warn("Could not find final response in SSE stream");
55
+ logRequest("stream-error", { error: "No response.done event found" });
56
+ // Return original stream if we can't parse
57
+ return new Response(fullText, {
58
+ status: response.status,
59
+ statusText: response.statusText,
60
+ headers: headers,
61
+ });
62
+ }
63
+ // Return as plain JSON (not SSE)
64
+ const jsonHeaders = new Headers(headers);
65
+ jsonHeaders.set('content-type', 'application/json; charset=utf-8');
66
+ return new Response(JSON.stringify(finalResponse), {
67
+ status: response.status,
68
+ statusText: response.statusText,
69
+ headers: jsonHeaders,
70
+ });
71
+ }
72
+ catch (error) {
73
+ log.error("Error converting stream", { error: String(error) });
74
+ logRequest("stream-error", { error: String(error) });
75
+ throw error;
76
+ }
77
+ }
78
+ /**
79
+ * Ensure response has content-type header
80
+ * @param headers - Response headers
81
+ * @returns Headers with content-type set
82
+ */
83
+ export function ensureContentType(headers) {
84
+ const responseHeaders = new Headers(headers);
85
+ if (!responseHeaders.has('content-type')) {
86
+ responseHeaders.set('content-type', 'text/event-stream; charset=utf-8');
87
+ }
88
+ return responseHeaders;
89
+ }
90
+ //# sourceMappingURL=response-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-handler.js","sourceRoot":"","sources":["../../../lib/request/response-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Rotation Strategy Module
3
+ *
4
+ * Implements health-based account selection with token bucket rate limiting.
5
+ * Ported from antigravity-auth rotation logic for optimal account rotation
6
+ * when rate limits are encountered.
7
+ */
8
+ export interface HealthScoreConfig {
9
+ /** Points added on successful request */
10
+ successDelta: number;
11
+ /** Points deducted on rate limit (negative) */
12
+ rateLimitDelta: number;
13
+ /** Points deducted on other failures (negative) */
14
+ failureDelta: number;
15
+ /** Maximum health score */
16
+ maxScore: number;
17
+ /** Minimum health score */
18
+ minScore: number;
19
+ /** Points recovered per hour of inactivity */
20
+ passiveRecoveryPerHour: number;
21
+ }
22
+ export declare const DEFAULT_HEALTH_SCORE_CONFIG: HealthScoreConfig;
23
+ /**
24
+ * Tracks health scores for accounts to prioritize healthy accounts.
25
+ * Accounts with higher health scores are preferred for selection.
26
+ */
27
+ export declare class HealthScoreTracker {
28
+ private entries;
29
+ private config;
30
+ constructor(config?: Partial<HealthScoreConfig>);
31
+ private getKey;
32
+ private applyPassiveRecovery;
33
+ getScore(accountIndex: number, quotaKey?: string): number;
34
+ getConsecutiveFailures(accountIndex: number, quotaKey?: string): number;
35
+ recordSuccess(accountIndex: number, quotaKey?: string): void;
36
+ recordRateLimit(accountIndex: number, quotaKey?: string): void;
37
+ recordFailure(accountIndex: number, quotaKey?: string): void;
38
+ reset(accountIndex: number, quotaKey?: string): void;
39
+ clear(): void;
40
+ }
41
+ export interface TokenBucketConfig {
42
+ /** Maximum tokens in bucket */
43
+ maxTokens: number;
44
+ /** Tokens regenerated per minute */
45
+ tokensPerMinute: number;
46
+ }
47
+ export declare const DEFAULT_TOKEN_BUCKET_CONFIG: TokenBucketConfig;
48
+ /**
49
+ * Client-side token bucket for rate limiting requests per account.
50
+ * Prevents sending requests to accounts that are likely to be rate-limited.
51
+ */
52
+ export declare class TokenBucketTracker {
53
+ private buckets;
54
+ private config;
55
+ constructor(config?: Partial<TokenBucketConfig>);
56
+ private getKey;
57
+ private refillTokens;
58
+ getTokens(accountIndex: number, quotaKey?: string): number;
59
+ /**
60
+ * Attempt to consume a token. Returns true if successful, false if bucket is empty.
61
+ */
62
+ tryConsume(accountIndex: number, quotaKey?: string): boolean;
63
+ /**
64
+ * Drain tokens on rate limit to prevent immediate retries.
65
+ */
66
+ drain(accountIndex: number, quotaKey?: string, drainAmount?: number): void;
67
+ reset(accountIndex: number, quotaKey?: string): void;
68
+ clear(): void;
69
+ }
70
+ export interface AccountWithMetrics {
71
+ index: number;
72
+ isAvailable: boolean;
73
+ lastUsed: number;
74
+ }
75
+ export interface HybridSelectionConfig {
76
+ /** Weight for health score (default: 2) */
77
+ healthWeight: number;
78
+ /** Weight for token count (default: 5) */
79
+ tokenWeight: number;
80
+ /** Weight for freshness/last used (default: 0.1) */
81
+ freshnessWeight: number;
82
+ }
83
+ export declare const DEFAULT_HYBRID_SELECTION_CONFIG: HybridSelectionConfig;
84
+ /**
85
+ * Selects the best account using a hybrid scoring strategy.
86
+ *
87
+ * Score = (health * healthWeight) + (tokens * tokenWeight) + (freshness * freshnessWeight)
88
+ *
89
+ * Where:
90
+ * - health: Account health score (0-100)
91
+ * - tokens: Available tokens in bucket (0-maxTokens)
92
+ * - freshness: Hours since last used (higher = more fresh for rotation)
93
+ */
94
+ export declare function selectHybridAccount(accounts: AccountWithMetrics[], healthTracker: HealthScoreTracker, tokenTracker: TokenBucketTracker, quotaKey?: string, config?: Partial<HybridSelectionConfig>): AccountWithMetrics | null;
95
+ /**
96
+ * Adds random jitter to a delay value.
97
+ * @param baseMs - Base delay in milliseconds
98
+ * @param jitterFactor - Jitter factor (0-1), default 0.1 (10%)
99
+ * @returns Delay with jitter applied
100
+ */
101
+ export declare function addJitter(baseMs: number, jitterFactor?: number): number;
102
+ /**
103
+ * Returns a random delay within a range.
104
+ * @param minMs - Minimum delay in milliseconds
105
+ * @param maxMs - Maximum delay in milliseconds
106
+ * @returns Random delay within range
107
+ */
108
+ export declare function randomDelay(minMs: number, maxMs: number): number;
109
+ /**
110
+ * Calculates exponential backoff with jitter.
111
+ * @param attempt - Attempt number (1-based)
112
+ * @param baseMs - Base delay in milliseconds
113
+ * @param maxMs - Maximum delay in milliseconds
114
+ * @param jitterFactor - Jitter factor (0-1)
115
+ * @returns Backoff delay with jitter
116
+ */
117
+ export declare function exponentialBackoff(attempt: number, baseMs?: number, maxMs?: number, jitterFactor?: number): number;
118
+ export declare function getHealthTracker(config?: Partial<HealthScoreConfig>): HealthScoreTracker;
119
+ export declare function getTokenTracker(config?: Partial<TokenBucketConfig>): TokenBucketTracker;
120
+ export declare function resetTrackers(): void;
121
+ //# sourceMappingURL=rotation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rotation.d.ts","sourceRoot":"","sources":["../../lib/rotation.ts"],"names":[],"mappings":"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"}