@iam-brain/opencode-codex-auth 1.5.1 → 1.7.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 (115) hide show
  1. package/README.md +7 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +5 -4
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/codex-native/accounts.d.ts +1 -1
  6. package/dist/lib/codex-native/accounts.d.ts.map +1 -1
  7. package/dist/lib/codex-native/accounts.js +54 -17
  8. package/dist/lib/codex-native/accounts.js.map +1 -1
  9. package/dist/lib/codex-native/acquire-auth.d.ts.map +1 -1
  10. package/dist/lib/codex-native/acquire-auth.js +11 -5
  11. package/dist/lib/codex-native/acquire-auth.js.map +1 -1
  12. package/dist/lib/codex-native/auth-menu-flow.js +4 -4
  13. package/dist/lib/codex-native/auth-menu-flow.js.map +1 -1
  14. package/dist/lib/codex-native/chat-hooks.d.ts +8 -1
  15. package/dist/lib/codex-native/chat-hooks.d.ts.map +1 -1
  16. package/dist/lib/codex-native/chat-hooks.js +76 -25
  17. package/dist/lib/codex-native/chat-hooks.js.map +1 -1
  18. package/dist/lib/codex-native/client-identity.js +1 -1
  19. package/dist/lib/codex-native/oauth-auth-methods.d.ts.map +1 -1
  20. package/dist/lib/codex-native/oauth-auth-methods.js +27 -2
  21. package/dist/lib/codex-native/oauth-auth-methods.js.map +1 -1
  22. package/dist/lib/codex-native/oauth-persistence.d.ts.map +1 -1
  23. package/dist/lib/codex-native/oauth-persistence.js +2 -0
  24. package/dist/lib/codex-native/oauth-persistence.js.map +1 -1
  25. package/dist/lib/codex-native/oauth-utils.d.ts +1 -0
  26. package/dist/lib/codex-native/oauth-utils.d.ts.map +1 -1
  27. package/dist/lib/codex-native/oauth-utils.js +35 -15
  28. package/dist/lib/codex-native/oauth-utils.js.map +1 -1
  29. package/dist/lib/codex-native/openai-loader-fetch.d.ts +3 -1
  30. package/dist/lib/codex-native/openai-loader-fetch.d.ts.map +1 -1
  31. package/dist/lib/codex-native/openai-loader-fetch.js +50 -7
  32. package/dist/lib/codex-native/openai-loader-fetch.js.map +1 -1
  33. package/dist/lib/codex-native/reasoning-summary.d.ts +30 -0
  34. package/dist/lib/codex-native/reasoning-summary.d.ts.map +1 -0
  35. package/dist/lib/codex-native/reasoning-summary.js +98 -0
  36. package/dist/lib/codex-native/reasoning-summary.js.map +1 -0
  37. package/dist/lib/codex-native/request-routing.d.ts.map +1 -1
  38. package/dist/lib/codex-native/request-routing.js +12 -4
  39. package/dist/lib/codex-native/request-routing.js.map +1 -1
  40. package/dist/lib/codex-native/request-transform-model-service-tier.d.ts +2 -0
  41. package/dist/lib/codex-native/request-transform-model-service-tier.d.ts.map +1 -1
  42. package/dist/lib/codex-native/request-transform-model-service-tier.js +35 -1
  43. package/dist/lib/codex-native/request-transform-model-service-tier.js.map +1 -1
  44. package/dist/lib/codex-native/request-transform-model.d.ts +42 -10
  45. package/dist/lib/codex-native/request-transform-model.d.ts.map +1 -1
  46. package/dist/lib/codex-native/request-transform-model.js +262 -98
  47. package/dist/lib/codex-native/request-transform-model.js.map +1 -1
  48. package/dist/lib/codex-native/request-transform-payload.d.ts +9 -1
  49. package/dist/lib/codex-native/request-transform-payload.d.ts.map +1 -1
  50. package/dist/lib/codex-native/request-transform-payload.js +175 -47
  51. package/dist/lib/codex-native/request-transform-payload.js.map +1 -1
  52. package/dist/lib/codex-native/session-messages.d.ts.map +1 -1
  53. package/dist/lib/codex-native/session-messages.js +18 -1
  54. package/dist/lib/codex-native/session-messages.js.map +1 -1
  55. package/dist/lib/codex-native.d.ts +2 -1
  56. package/dist/lib/codex-native.d.ts.map +1 -1
  57. package/dist/lib/codex-native.js +91 -9
  58. package/dist/lib/codex-native.js.map +1 -1
  59. package/dist/lib/config/file.d.ts +3 -1
  60. package/dist/lib/config/file.d.ts.map +1 -1
  61. package/dist/lib/config/file.js +461 -52
  62. package/dist/lib/config/file.js.map +1 -1
  63. package/dist/lib/config/resolve.d.ts +6 -2
  64. package/dist/lib/config/resolve.d.ts.map +1 -1
  65. package/dist/lib/config/resolve.js +69 -11
  66. package/dist/lib/config/resolve.js.map +1 -1
  67. package/dist/lib/config/types.d.ts +22 -5
  68. package/dist/lib/config/types.d.ts.map +1 -1
  69. package/dist/lib/config/types.js +76 -31
  70. package/dist/lib/config/types.js.map +1 -1
  71. package/dist/lib/config.d.ts +3 -3
  72. package/dist/lib/config.d.ts.map +1 -1
  73. package/dist/lib/config.js +3 -3
  74. package/dist/lib/config.js.map +1 -1
  75. package/dist/lib/fatal-errors.d.ts +5 -1
  76. package/dist/lib/fatal-errors.d.ts.map +1 -1
  77. package/dist/lib/fatal-errors.js +12 -2
  78. package/dist/lib/fatal-errors.js.map +1 -1
  79. package/dist/lib/fetch-orchestrator.d.ts.map +1 -1
  80. package/dist/lib/fetch-orchestrator.js +4 -1
  81. package/dist/lib/fetch-orchestrator.js.map +1 -1
  82. package/dist/lib/installer-cli.d.ts.map +1 -1
  83. package/dist/lib/installer-cli.js +38 -9
  84. package/dist/lib/installer-cli.js.map +1 -1
  85. package/dist/lib/model-catalog/provider.d.ts +4 -1
  86. package/dist/lib/model-catalog/provider.d.ts.map +1 -1
  87. package/dist/lib/model-catalog/provider.js +82 -9
  88. package/dist/lib/model-catalog/provider.js.map +1 -1
  89. package/dist/lib/model-catalog/shared.d.ts +27 -1
  90. package/dist/lib/model-catalog/shared.d.ts.map +1 -1
  91. package/dist/lib/model-catalog/shared.js +3 -2
  92. package/dist/lib/model-catalog/shared.js.map +1 -1
  93. package/dist/lib/model-catalog.d.ts +1 -1
  94. package/dist/lib/model-catalog.d.ts.map +1 -1
  95. package/dist/lib/model-catalog.js.map +1 -1
  96. package/dist/lib/persona-tool-cli.d.ts.map +1 -1
  97. package/dist/lib/persona-tool-cli.js +19 -7
  98. package/dist/lib/persona-tool-cli.js.map +1 -1
  99. package/dist/lib/personality-command.js +1 -1
  100. package/dist/lib/personality-create.d.ts.map +1 -1
  101. package/dist/lib/personality-create.js +28 -10
  102. package/dist/lib/personality-create.js.map +1 -1
  103. package/dist/lib/personality-skill.js +1 -1
  104. package/dist/lib/proactive-refresh.d.ts.map +1 -1
  105. package/dist/lib/proactive-refresh.js +11 -2
  106. package/dist/lib/proactive-refresh.js.map +1 -1
  107. package/dist/lib/storage/auth-state.d.ts.map +1 -1
  108. package/dist/lib/storage/auth-state.js +4 -3
  109. package/dist/lib/storage/auth-state.js.map +1 -1
  110. package/dist/lib/ui/auth-menu.d.ts +1 -0
  111. package/dist/lib/ui/auth-menu.d.ts.map +1 -1
  112. package/dist/lib/ui/auth-menu.js +1 -1
  113. package/dist/lib/ui/auth-menu.js.map +1 -1
  114. package/package.json +9 -6
  115. package/schemas/codex-config.schema.json +153 -11
@@ -3,7 +3,7 @@ import fsPromises from "node:fs/promises";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { isRecord } from "../util.js";
6
- import { CONFIG_FILE, DEFAULT_CODEX_CONFIG_TEMPLATE } from "./types.js";
6
+ import { CONFIG_FILE, DEFAULT_CODEX_CONFIG_TEMPLATE, LEGACY_CONFIG_FILE } from "./types.js";
7
7
  function describeValueType(value) {
8
8
  if (Array.isArray(value))
9
9
  return "array";
@@ -11,8 +11,23 @@ function describeValueType(value) {
11
11
  return "null";
12
12
  return typeof value;
13
13
  }
14
+ function describeValuePreview(value) {
15
+ if (typeof value === "string")
16
+ return JSON.stringify(value);
17
+ if (typeof value === "number" || typeof value === "boolean" || value === null || value === undefined) {
18
+ return String(value);
19
+ }
20
+ if (Array.isArray(value)) {
21
+ return `array(${value.length})`;
22
+ }
23
+ if (typeof value === "object") {
24
+ const keys = Object.keys(value);
25
+ return keys.length > 0 ? `object(${keys.slice(0, 3).join(", ")})` : "object";
26
+ }
27
+ return String(value);
28
+ }
14
29
  function pushValidationIssue(issues, input) {
15
- issues.push(`${input.path}: expected ${input.expected}, got ${describeValueType(input.actual)}`);
30
+ issues.push(`${input.path}: expected ${input.expected}, found ${describeValueType(input.actual)} (${describeValuePreview(input.actual)})`);
16
31
  }
17
32
  export function parseEnvBoolean(value) {
18
33
  if (value === undefined)
@@ -91,6 +106,85 @@ function stripJsonComments(raw) {
91
106
  }
92
107
  return out;
93
108
  }
109
+ const SUPPORTED_INCLUDE_OPTIONS = [
110
+ "reasoning.encrypted_content",
111
+ "file_search_call.results",
112
+ "message.output_text.logprobs"
113
+ ];
114
+ function normalizeNonEmptyString(value) {
115
+ if (typeof value !== "string")
116
+ return undefined;
117
+ const trimmed = value.trim();
118
+ return trimmed.length > 0 ? trimmed : undefined;
119
+ }
120
+ function deriveReasoningSummaryAlias(value) {
121
+ if (value === undefined)
122
+ return undefined;
123
+ return value !== "none";
124
+ }
125
+ function deriveVerbosityEnabledAlias(value) {
126
+ if (value === undefined)
127
+ return undefined;
128
+ return value !== "none";
129
+ }
130
+ function deriveVerbosityAlias(value) {
131
+ if (value === undefined || value === "none")
132
+ return undefined;
133
+ return value;
134
+ }
135
+ function normalizeReasoningSummaryOption(value) {
136
+ if (typeof value !== "string")
137
+ return undefined;
138
+ const normalized = value.trim().toLowerCase();
139
+ if (normalized === "auto" || normalized === "concise" || normalized === "detailed" || normalized === "none") {
140
+ return normalized;
141
+ }
142
+ return undefined;
143
+ }
144
+ export function normalizeTextVerbosityOption(value) {
145
+ if (typeof value !== "string")
146
+ return undefined;
147
+ const normalized = value.trim().toLowerCase();
148
+ if (normalized === "default" ||
149
+ normalized === "low" ||
150
+ normalized === "medium" ||
151
+ normalized === "high" ||
152
+ normalized === "none") {
153
+ return normalized;
154
+ }
155
+ return undefined;
156
+ }
157
+ function normalizeIncludeOptions(value) {
158
+ if (!Array.isArray(value))
159
+ return undefined;
160
+ const out = [];
161
+ const seen = new Set();
162
+ for (const entry of value) {
163
+ if (typeof entry !== "string")
164
+ continue;
165
+ const normalized = entry.trim().toLowerCase();
166
+ if (!SUPPORTED_INCLUDE_OPTIONS.includes(normalized))
167
+ continue;
168
+ if (seen.has(normalized))
169
+ continue;
170
+ seen.add(normalized);
171
+ out.push(normalized);
172
+ }
173
+ return out.length > 0 ? out : undefined;
174
+ }
175
+ function normalizeServiceTierInput(value) {
176
+ if (typeof value !== "string") {
177
+ return { value: undefined, usedDeprecatedDefaultAlias: false };
178
+ }
179
+ const normalized = value.trim().toLowerCase();
180
+ if (normalized === "default") {
181
+ return { value: "auto", usedDeprecatedDefaultAlias: true };
182
+ }
183
+ if (normalized === "auto" || normalized === "priority" || normalized === "flex") {
184
+ return { value: normalized, usedDeprecatedDefaultAlias: false };
185
+ }
186
+ return { value: undefined, usedDeprecatedDefaultAlias: false };
187
+ }
94
188
  function normalizeModelBehaviorSettings(raw) {
95
189
  if (!isRecord(raw))
96
190
  return undefined;
@@ -98,21 +192,58 @@ function normalizeModelBehaviorSettings(raw) {
98
192
  const personality = normalizePersonalityOption(raw.personality);
99
193
  if (personality)
100
194
  out.personality = personality;
101
- if (typeof raw.thinkingSummaries === "boolean")
102
- out.thinkingSummaries = raw.thinkingSummaries;
103
- if (typeof raw.verbosityEnabled === "boolean")
104
- out.verbosityEnabled = raw.verbosityEnabled;
105
- const verbosity = normalizeVerbosityOption(raw.verbosity);
106
- if (verbosity)
107
- out.verbosity = verbosity;
108
- const serviceTier = normalizeServiceTierOption(raw.serviceTier);
109
- if (serviceTier)
110
- out.serviceTier = serviceTier;
195
+ const reasoningEffort = normalizeNonEmptyString(raw.reasoningEffort);
196
+ if (reasoningEffort)
197
+ out.reasoningEffort = reasoningEffort;
198
+ const reasoningSummary = normalizeReasoningSummaryOption(raw.reasoningSummary);
199
+ if (reasoningSummary) {
200
+ out.reasoningSummary = reasoningSummary;
201
+ }
202
+ else if (typeof raw.reasoningSummaries === "boolean") {
203
+ out.reasoningSummary = raw.reasoningSummaries ? "auto" : "none";
204
+ }
205
+ else if (typeof raw.thinkingSummaries === "boolean") {
206
+ out.reasoningSummary = raw.thinkingSummaries ? "auto" : "none";
207
+ }
208
+ out.reasoningSummaries = deriveReasoningSummaryAlias(out.reasoningSummary);
209
+ const textVerbosity = normalizeTextVerbosityOption(raw.textVerbosity);
210
+ if (textVerbosity) {
211
+ out.textVerbosity = textVerbosity;
212
+ }
213
+ else {
214
+ const verbosityEnabled = typeof raw.verbosityEnabled === "boolean" ? raw.verbosityEnabled : undefined;
215
+ const verbosity = normalizeVerbosityOption(raw.verbosity);
216
+ if (verbosityEnabled === false) {
217
+ out.textVerbosity = "none";
218
+ }
219
+ else if (verbosity) {
220
+ out.textVerbosity = verbosity;
221
+ }
222
+ else if (verbosityEnabled === true) {
223
+ out.textVerbosity = "default";
224
+ }
225
+ }
226
+ out.verbosityEnabled = deriveVerbosityEnabledAlias(out.textVerbosity);
227
+ out.verbosity = deriveVerbosityAlias(out.textVerbosity);
228
+ const serviceTier = normalizeServiceTierInput(raw.serviceTier);
229
+ if (serviceTier.value)
230
+ out.serviceTier = serviceTier.value;
231
+ const include = normalizeIncludeOptions(raw.include);
232
+ if (include)
233
+ out.include = include;
234
+ if (typeof raw.parallelToolCalls === "boolean") {
235
+ out.parallelToolCalls = raw.parallelToolCalls;
236
+ }
111
237
  if (!out.personality &&
112
- out.thinkingSummaries === undefined &&
238
+ !out.reasoningEffort &&
239
+ out.reasoningSummary === undefined &&
240
+ out.reasoningSummaries === undefined &&
241
+ out.textVerbosity === undefined &&
113
242
  out.verbosityEnabled === undefined &&
114
243
  out.verbosity === undefined &&
115
- out.serviceTier === undefined) {
244
+ out.serviceTier === undefined &&
245
+ out.include === undefined &&
246
+ out.parallelToolCalls === undefined) {
116
247
  return undefined;
117
248
  }
118
249
  return out;
@@ -131,10 +262,15 @@ function normalizeModelConfigOverride(raw) {
131
262
  continue;
132
263
  variantMap[variantName] = {
133
264
  ...(normalized.personality ? { personality: normalized.personality } : {}),
134
- ...(normalized.thinkingSummaries !== undefined ? { thinkingSummaries: normalized.thinkingSummaries } : {}),
265
+ ...(normalized.reasoningEffort ? { reasoningEffort: normalized.reasoningEffort } : {}),
266
+ ...(normalized.reasoningSummary ? { reasoningSummary: normalized.reasoningSummary } : {}),
267
+ ...(normalized.reasoningSummaries !== undefined ? { reasoningSummaries: normalized.reasoningSummaries } : {}),
268
+ ...(normalized.textVerbosity ? { textVerbosity: normalized.textVerbosity } : {}),
135
269
  ...(normalized.verbosityEnabled !== undefined ? { verbosityEnabled: normalized.verbosityEnabled } : {}),
136
270
  ...(normalized.verbosity ? { verbosity: normalized.verbosity } : {}),
137
- ...(normalized.serviceTier ? { serviceTier: normalized.serviceTier } : {})
271
+ ...(normalized.serviceTier ? { serviceTier: normalized.serviceTier } : {}),
272
+ ...(normalized.include ? { include: normalized.include } : {}),
273
+ ...(normalized.parallelToolCalls !== undefined ? { parallelToolCalls: normalized.parallelToolCalls } : {})
138
274
  };
139
275
  }
140
276
  if (Object.keys(variantMap).length > 0) {
@@ -146,13 +282,36 @@ function normalizeModelConfigOverride(raw) {
146
282
  }
147
283
  return {
148
284
  ...(modelBehavior?.personality ? { personality: modelBehavior.personality } : {}),
149
- ...(modelBehavior?.thinkingSummaries !== undefined ? { thinkingSummaries: modelBehavior.thinkingSummaries } : {}),
285
+ ...(modelBehavior?.reasoningEffort ? { reasoningEffort: modelBehavior.reasoningEffort } : {}),
286
+ ...(modelBehavior?.reasoningSummary ? { reasoningSummary: modelBehavior.reasoningSummary } : {}),
287
+ ...(modelBehavior?.reasoningSummaries !== undefined
288
+ ? { reasoningSummaries: modelBehavior.reasoningSummaries }
289
+ : {}),
290
+ ...(modelBehavior?.textVerbosity ? { textVerbosity: modelBehavior.textVerbosity } : {}),
150
291
  ...(modelBehavior?.verbosityEnabled !== undefined ? { verbosityEnabled: modelBehavior.verbosityEnabled } : {}),
151
292
  ...(modelBehavior?.verbosity ? { verbosity: modelBehavior.verbosity } : {}),
152
293
  ...(modelBehavior?.serviceTier ? { serviceTier: modelBehavior.serviceTier } : {}),
294
+ ...(modelBehavior?.include ? { include: modelBehavior.include } : {}),
295
+ ...(modelBehavior?.parallelToolCalls !== undefined ? { parallelToolCalls: modelBehavior.parallelToolCalls } : {}),
153
296
  ...(variants ? { variants } : {})
154
297
  };
155
298
  }
299
+ function normalizeCustomModelConfig(raw) {
300
+ if (!isRecord(raw))
301
+ return undefined;
302
+ const behavior = normalizeModelConfigOverride(raw);
303
+ const targetModel = normalizeNonEmptyString(raw.targetModel);
304
+ const name = normalizeNonEmptyString(raw.name);
305
+ if (!targetModel && !behavior && !name)
306
+ return undefined;
307
+ if (!targetModel)
308
+ return undefined;
309
+ return {
310
+ targetModel,
311
+ ...(name ? { name } : {}),
312
+ ...(behavior ?? {})
313
+ };
314
+ }
156
315
  function normalizeNewBehaviorSections(raw) {
157
316
  const global = normalizeModelBehaviorSettings(raw.global);
158
317
  const perModelRaw = isRecord(raw.perModel) ? raw.perModel : undefined;
@@ -176,6 +335,22 @@ function normalizeNewBehaviorSections(raw) {
176
335
  ...(perModel ? { perModel } : {})
177
336
  };
178
337
  }
338
+ function normalizeCustomModels(raw) {
339
+ const customModelsRaw = isRecord(raw.customModels) ? raw.customModels : undefined;
340
+ if (!customModelsRaw)
341
+ return undefined;
342
+ const out = {};
343
+ for (const [slug, value] of Object.entries(customModelsRaw)) {
344
+ const normalizedSlug = normalizeNonEmptyString(slug)?.toLowerCase();
345
+ if (!normalizedSlug)
346
+ continue;
347
+ const normalized = normalizeCustomModelConfig(value);
348
+ if (!normalized)
349
+ continue;
350
+ out[normalizedSlug] = normalized;
351
+ }
352
+ return Object.keys(out).length > 0 ? out : undefined;
353
+ }
179
354
  function validateModelBehaviorShape(value, pathPrefix, issues) {
180
355
  if (!isRecord(value)) {
181
356
  pushValidationIssue(issues, { path: pathPrefix, expected: "object", actual: value });
@@ -184,6 +359,31 @@ function validateModelBehaviorShape(value, pathPrefix, issues) {
184
359
  if ("personality" in value && typeof value.personality !== "string") {
185
360
  pushValidationIssue(issues, { path: `${pathPrefix}.personality`, expected: "string", actual: value.personality });
186
361
  }
362
+ if ("reasoningEffort" in value && typeof value.reasoningEffort !== "string") {
363
+ pushValidationIssue(issues, {
364
+ path: `${pathPrefix}.reasoningEffort`,
365
+ expected: "string",
366
+ actual: value.reasoningEffort
367
+ });
368
+ }
369
+ if ("reasoningSummary" in value) {
370
+ const reasoningSummary = value.reasoningSummary;
371
+ const normalized = typeof reasoningSummary === "string" ? reasoningSummary.trim().toLowerCase() : "";
372
+ if (!(normalized === "auto" || normalized === "concise" || normalized === "detailed" || normalized === "none")) {
373
+ pushValidationIssue(issues, {
374
+ path: `${pathPrefix}.reasoningSummary`,
375
+ expected: '"auto" | "concise" | "detailed" | "none"',
376
+ actual: reasoningSummary
377
+ });
378
+ }
379
+ }
380
+ if ("reasoningSummaries" in value && typeof value.reasoningSummaries !== "boolean") {
381
+ pushValidationIssue(issues, {
382
+ path: `${pathPrefix}.reasoningSummaries`,
383
+ expected: "boolean",
384
+ actual: value.reasoningSummaries
385
+ });
386
+ }
187
387
  if ("thinkingSummaries" in value && typeof value.thinkingSummaries !== "boolean") {
188
388
  pushValidationIssue(issues, {
189
389
  path: `${pathPrefix}.thinkingSummaries`,
@@ -209,17 +409,82 @@ function validateModelBehaviorShape(value, pathPrefix, issues) {
209
409
  });
210
410
  }
211
411
  }
412
+ if ("textVerbosity" in value) {
413
+ const textVerbosity = value.textVerbosity;
414
+ const normalized = typeof textVerbosity === "string" ? textVerbosity.trim().toLowerCase() : "";
415
+ if (!(normalized === "default" ||
416
+ normalized === "low" ||
417
+ normalized === "medium" ||
418
+ normalized === "high" ||
419
+ normalized === "none")) {
420
+ pushValidationIssue(issues, {
421
+ path: `${pathPrefix}.textVerbosity`,
422
+ expected: '"default" | "low" | "medium" | "high" | "none"',
423
+ actual: textVerbosity
424
+ });
425
+ }
426
+ }
212
427
  if ("serviceTier" in value) {
213
428
  const serviceTier = value.serviceTier;
214
429
  const normalized = typeof serviceTier === "string" ? serviceTier.trim().toLowerCase() : "";
215
- if (!(normalized === "default" || normalized === "priority" || normalized === "flex")) {
430
+ if (!(normalized === "default" || normalized === "auto" || normalized === "priority" || normalized === "flex")) {
216
431
  pushValidationIssue(issues, {
217
432
  path: `${pathPrefix}.serviceTier`,
218
- expected: '"default" | "priority" | "flex"',
433
+ expected: '"auto" | "priority" | "flex" (deprecated alias: "default")',
219
434
  actual: serviceTier
220
435
  });
221
436
  }
222
437
  }
438
+ if ("include" in value) {
439
+ if (!Array.isArray(value.include)) {
440
+ pushValidationIssue(issues, {
441
+ path: `${pathPrefix}.include`,
442
+ expected: "array",
443
+ actual: value.include
444
+ });
445
+ }
446
+ else {
447
+ for (const entry of value.include) {
448
+ const normalized = typeof entry === "string" ? entry.trim().toLowerCase() : "";
449
+ if (!SUPPORTED_INCLUDE_OPTIONS.includes(normalized)) {
450
+ pushValidationIssue(issues, {
451
+ path: `${pathPrefix}.include`,
452
+ expected: SUPPORTED_INCLUDE_OPTIONS.map((item) => `"${item}"`).join(" | "),
453
+ actual: entry
454
+ });
455
+ }
456
+ }
457
+ }
458
+ }
459
+ if ("parallelToolCalls" in value && typeof value.parallelToolCalls !== "boolean") {
460
+ pushValidationIssue(issues, {
461
+ path: `${pathPrefix}.parallelToolCalls`,
462
+ expected: "boolean",
463
+ actual: value.parallelToolCalls
464
+ });
465
+ }
466
+ }
467
+ function validateCustomModelShape(value, pathPrefix, issues) {
468
+ validateModelBehaviorShape(value, pathPrefix, issues);
469
+ if (!isRecord(value))
470
+ return;
471
+ if ("targetModel" in value && typeof value.targetModel !== "string") {
472
+ pushValidationIssue(issues, {
473
+ path: `${pathPrefix}.targetModel`,
474
+ expected: "string",
475
+ actual: value.targetModel
476
+ });
477
+ }
478
+ if (!("targetModel" in value)) {
479
+ issues.push(`${pathPrefix}.targetModel: expected string, found missing (custom models require targetModel)`);
480
+ }
481
+ if ("name" in value && typeof value.name !== "string") {
482
+ pushValidationIssue(issues, {
483
+ path: `${pathPrefix}.name`,
484
+ expected: "string",
485
+ actual: value.name
486
+ });
487
+ }
223
488
  }
224
489
  export function parseConfigJsonWithComments(raw) {
225
490
  return JSON.parse(stripJsonComments(raw));
@@ -282,13 +547,7 @@ export function normalizeVerbosityOption(value) {
282
547
  return undefined;
283
548
  }
284
549
  export function normalizeServiceTierOption(value) {
285
- if (typeof value !== "string")
286
- return undefined;
287
- const normalized = value.trim().toLowerCase();
288
- if (normalized === "default" || normalized === "priority" || normalized === "flex") {
289
- return normalized;
290
- }
291
- return undefined;
550
+ return normalizeServiceTierInput(value).value;
292
551
  }
293
552
  export function validateConfigFileObject(raw) {
294
553
  const issues = [];
@@ -400,12 +659,96 @@ export function validateConfigFileObject(raw) {
400
659
  }
401
660
  }
402
661
  }
662
+ if ("customModels" in raw) {
663
+ if (!isRecord(raw.customModels)) {
664
+ pushValidationIssue(issues, { path: "customModels", expected: "object", actual: raw.customModels });
665
+ }
666
+ else {
667
+ for (const [slug, value] of Object.entries(raw.customModels)) {
668
+ validateCustomModelShape(value, `customModels.${slug}`, issues);
669
+ if (!isRecord(value) || !("variants" in value))
670
+ continue;
671
+ const variants = value.variants;
672
+ if (!isRecord(variants)) {
673
+ pushValidationIssue(issues, {
674
+ path: `customModels.${slug}.variants`,
675
+ expected: "object",
676
+ actual: variants
677
+ });
678
+ continue;
679
+ }
680
+ for (const [variantName, variantValue] of Object.entries(variants)) {
681
+ validateModelBehaviorShape(variantValue, `customModels.${slug}.variants.${variantName}`, issues);
682
+ }
683
+ }
684
+ }
685
+ }
403
686
  return { valid: issues.length === 0, issues };
404
687
  }
405
688
  export function parseConfigFileObject(raw) {
689
+ return parseConfigFileObjectWithMetadata(raw).config;
690
+ }
691
+ function collectDeprecatedModelBehaviorKeys(raw) {
406
692
  if (!isRecord(raw))
407
- return {};
693
+ return [];
694
+ const keys = [];
695
+ const collectBehaviorAlias = (value, pathPrefix) => {
696
+ if (!isRecord(value))
697
+ return;
698
+ if (typeof value.reasoningSummaries === "boolean") {
699
+ keys.push(`${pathPrefix}.reasoningSummaries`);
700
+ }
701
+ if (typeof value.thinkingSummaries === "boolean") {
702
+ keys.push(`${pathPrefix}.thinkingSummaries`);
703
+ }
704
+ if ("verbosityEnabled" in value) {
705
+ keys.push(`${pathPrefix}.verbosityEnabled`);
706
+ }
707
+ if ("verbosity" in value) {
708
+ keys.push(`${pathPrefix}.verbosity`);
709
+ }
710
+ if (typeof value.serviceTier === "string" && value.serviceTier.trim().toLowerCase() === "default") {
711
+ keys.push(`${pathPrefix}.serviceTier="default"`);
712
+ }
713
+ };
714
+ collectBehaviorAlias(raw.global, "global");
715
+ if (isRecord(raw.perModel)) {
716
+ for (const [modelName, modelValue] of Object.entries(raw.perModel)) {
717
+ collectBehaviorAlias(modelValue, `perModel.${modelName}`);
718
+ if (!isRecord(modelValue))
719
+ continue;
720
+ const variants = isRecord(modelValue.variants) ? modelValue.variants : undefined;
721
+ if (!variants)
722
+ continue;
723
+ for (const [variantName, variantValue] of Object.entries(variants)) {
724
+ collectBehaviorAlias(variantValue, `perModel.${modelName}.variants.${variantName}`);
725
+ }
726
+ }
727
+ }
728
+ if (isRecord(raw.customModels)) {
729
+ for (const [slug, modelValue] of Object.entries(raw.customModels)) {
730
+ collectBehaviorAlias(modelValue, `customModels.${slug}`);
731
+ if (!isRecord(modelValue))
732
+ continue;
733
+ const variants = isRecord(modelValue.variants) ? modelValue.variants : undefined;
734
+ if (!variants)
735
+ continue;
736
+ for (const [variantName, variantValue] of Object.entries(variants)) {
737
+ collectBehaviorAlias(variantValue, `customModels.${slug}.variants.${variantName}`);
738
+ }
739
+ }
740
+ }
741
+ return keys;
742
+ }
743
+ function parseConfigFileObjectWithMetadata(raw) {
744
+ if (!isRecord(raw)) {
745
+ return {
746
+ config: {},
747
+ deprecatedKeys: []
748
+ };
749
+ }
408
750
  const behaviorSettings = normalizeNewBehaviorSections(raw);
751
+ const customModels = normalizeCustomModels(raw);
409
752
  const personalityFromBehavior = behaviorSettings?.global?.personality;
410
753
  const runtime = isRecord(raw.runtime) ? raw.runtime : undefined;
411
754
  const debug = typeof raw.debug === "boolean" ? raw.debug : undefined;
@@ -426,28 +769,32 @@ export function parseConfigFileObject(raw) {
426
769
  const collaborationProfileEnabled = typeof runtime?.collaborationProfile === "boolean" ? runtime.collaborationProfile : undefined;
427
770
  const orchestratorSubagentsEnabled = typeof runtime?.orchestratorSubagents === "boolean" ? runtime.orchestratorSubagents : undefined;
428
771
  return {
429
- debug,
430
- proactiveRefresh,
431
- proactiveRefreshBufferMs,
432
- quiet: quietMode,
433
- quietMode,
434
- pidOffsetEnabled,
435
- personality: personalityFromBehavior,
436
- mode,
437
- rotationStrategy,
438
- promptCacheKeyStrategy,
439
- spoofMode,
440
- compatInputSanitizer,
441
- remapDeveloperMessagesToUser,
442
- codexCompactionOverride,
443
- headerSnapshots,
444
- headerSnapshotBodies,
445
- headerTransformDebug,
446
- collaborationProfile: collaborationProfileEnabled,
447
- collaborationProfileEnabled,
448
- orchestratorSubagents: orchestratorSubagentsEnabled,
449
- orchestratorSubagentsEnabled,
450
- behaviorSettings
772
+ config: {
773
+ debug,
774
+ proactiveRefresh,
775
+ proactiveRefreshBufferMs,
776
+ quiet: quietMode,
777
+ quietMode,
778
+ pidOffsetEnabled,
779
+ personality: personalityFromBehavior,
780
+ mode,
781
+ rotationStrategy,
782
+ promptCacheKeyStrategy,
783
+ spoofMode,
784
+ compatInputSanitizer,
785
+ remapDeveloperMessagesToUser,
786
+ codexCompactionOverride,
787
+ headerSnapshots,
788
+ headerSnapshotBodies,
789
+ headerTransformDebug,
790
+ collaborationProfile: collaborationProfileEnabled,
791
+ collaborationProfileEnabled,
792
+ orchestratorSubagents: orchestratorSubagentsEnabled,
793
+ orchestratorSubagentsEnabled,
794
+ behaviorSettings,
795
+ customModels
796
+ },
797
+ deprecatedKeys: collectDeprecatedModelBehaviorKeys(raw)
451
798
  };
452
799
  }
453
800
  export function resolveDefaultConfigPath(env) {
@@ -457,9 +804,51 @@ export function resolveDefaultConfigPath(env) {
457
804
  }
458
805
  return path.join(os.homedir(), ".config", "opencode", CONFIG_FILE);
459
806
  }
807
+ export function resolveLegacyDefaultConfigPath(env) {
808
+ const xdgRoot = env.XDG_CONFIG_HOME?.trim();
809
+ if (xdgRoot) {
810
+ return path.join(xdgRoot, "opencode", LEGACY_CONFIG_FILE);
811
+ }
812
+ return path.join(os.homedir(), ".config", "opencode", LEGACY_CONFIG_FILE);
813
+ }
814
+ function quarantineLegacyConfigSync(filePath) {
815
+ try {
816
+ const quarantineDir = path.join(path.dirname(filePath), "quarantine");
817
+ fs.mkdirSync(quarantineDir, { recursive: true });
818
+ const dest = path.join(quarantineDir, `${path.basename(filePath)}.${Date.now()}.quarantine.json`);
819
+ fs.renameSync(filePath, dest);
820
+ return dest;
821
+ }
822
+ catch {
823
+ return undefined;
824
+ }
825
+ }
826
+ function resolveDefaultConfigCandidates(env) {
827
+ const filePath = resolveDefaultConfigPath(env);
828
+ const legacyPath = resolveLegacyDefaultConfigPath(env);
829
+ const hasFile = fs.existsSync(filePath);
830
+ const hasLegacy = fs.existsSync(legacyPath);
831
+ if (hasFile && hasLegacy) {
832
+ return [filePath, legacyPath];
833
+ }
834
+ if (hasFile)
835
+ return [filePath];
836
+ if (hasLegacy)
837
+ return [legacyPath];
838
+ return [filePath];
839
+ }
460
840
  export async function ensureDefaultConfigFile(input = {}) {
461
841
  const env = input.env ?? process.env;
462
- const filePath = input.filePath ?? resolveDefaultConfigPath(env);
842
+ const filePath = input.filePath ??
843
+ (() => {
844
+ const canonicalPath = resolveDefaultConfigPath(env);
845
+ const legacyPath = resolveLegacyDefaultConfigPath(env);
846
+ if (fs.existsSync(canonicalPath))
847
+ return canonicalPath;
848
+ if (fs.existsSync(legacyPath))
849
+ return legacyPath;
850
+ return canonicalPath;
851
+ })();
463
852
  const overwrite = input.overwrite === true;
464
853
  if (!overwrite && fs.existsSync(filePath)) {
465
854
  return { filePath, created: false };
@@ -479,7 +868,15 @@ export async function ensureDefaultConfigFile(input = {}) {
479
868
  export function loadConfigFile(input = {}) {
480
869
  const env = input.env ?? process.env;
481
870
  const explicitPath = input.filePath ?? env.OPENCODE_OPENAI_MULTI_CONFIG_PATH?.trim();
482
- const candidates = explicitPath ? [explicitPath] : [resolveDefaultConfigPath(env)];
871
+ const candidates = explicitPath ? [explicitPath] : resolveDefaultConfigCandidates(env);
872
+ const canonicalPath = explicitPath ? undefined : resolveDefaultConfigPath(env);
873
+ const legacyPath = explicitPath ? undefined : resolveLegacyDefaultConfigPath(env);
874
+ const shouldQuarantineLegacyAfterCanonicalLoad = !explicitPath &&
875
+ canonicalPath !== undefined &&
876
+ legacyPath !== undefined &&
877
+ candidates.length > 1 &&
878
+ fs.existsSync(canonicalPath) &&
879
+ fs.existsSync(legacyPath);
483
880
  for (const filePath of candidates) {
484
881
  if (!filePath || !fs.existsSync(filePath))
485
882
  continue;
@@ -491,7 +888,19 @@ export function loadConfigFile(input = {}) {
491
888
  console.warn(`[opencode-codex-auth] Invalid codex-config at ${filePath}. ${validation.issues.join("; ")}`);
492
889
  continue;
493
890
  }
494
- return parseConfigFileObject(parsed);
891
+ const result = parseConfigFileObjectWithMetadata(parsed);
892
+ if (result.deprecatedKeys.length > 0) {
893
+ console.warn(`[opencode-codex-auth] Deprecated config key(s) in ${filePath}: ${result.deprecatedKeys.join(", ")}. Use reasoningSummary, textVerbosity, and serviceTier: "auto" instead.`);
894
+ }
895
+ if (shouldQuarantineLegacyAfterCanonicalLoad &&
896
+ filePath === canonicalPath &&
897
+ legacyPath &&
898
+ fs.existsSync(legacyPath)) {
899
+ const quarantinedPath = quarantineLegacyConfigSync(legacyPath);
900
+ const suffix = quarantinedPath ? ` Quarantined legacy file to ${quarantinedPath}.` : "";
901
+ console.warn(`[opencode-codex-auth] Found both ${CONFIG_FILE} and ${LEGACY_CONFIG_FILE}. Using ${CONFIG_FILE}.${suffix}`);
902
+ }
903
+ return result.config;
495
904
  }
496
905
  catch (error) {
497
906
  const detail = error instanceof Error ? error.message : String(error);