melchizedek-agents 1.0.3 → 1.2.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 (172) hide show
  1. package/.env.example +29 -2
  2. package/AGENT_SETUP.md +3 -1
  3. package/CHANGELOG.md +524 -0
  4. package/DOCUMENTATION.md +178 -43
  5. package/ONBOARDING.md +339 -0
  6. package/QUICKSTART.md +15 -2
  7. package/README.md +6 -2
  8. package/config/agents/README.md +2 -2
  9. package/config/agents/examples/ares.yaml +6 -10
  10. package/config/agents/examples/assistant.yaml +6 -6
  11. package/config/agents/examples/augustin.yaml +9 -15
  12. package/config/agents/examples/cartographers.yaml +6 -6
  13. package/config/agents/examples/council.yaml +7 -7
  14. package/config/agents/examples/critic.yaml +24 -24
  15. package/config/agents/examples/harness.yaml +4 -4
  16. package/config/agents/examples/image_production.yaml +37 -35
  17. package/config/agents/examples/librarian.yaml +4 -4
  18. package/config/agents/examples/patient_advocate.yaml +6 -10
  19. package/config/agents/examples/pipeline.yaml +20 -19
  20. package/config/agents/examples/research.yaml +36 -43
  21. package/config/agents/examples/scribe.yaml +31 -32
  22. package/config/agents/examples/scriptorium.yaml +6 -6
  23. package/config/agents/examples/structured_critic.yaml +82 -0
  24. package/config/agents/examples/style_council.yaml +9 -11
  25. package/config/agents/examples/syndicate.yaml +3 -5
  26. package/config/agents/examples/tutor.yaml +2 -2
  27. package/config/agents/examples/weather.yaml +2 -2
  28. package/config/agents/syndicate.schema.json +859 -13
  29. package/config/agents/syndicateSchema.yaml +122 -73
  30. package/config/agents/templates/README.md +1 -1
  31. package/config/agents/templates/account_memory.yaml +4 -4
  32. package/config/agents/templates/case_desk.yaml +6 -6
  33. package/config/agents/templates/conversational.yaml +2 -2
  34. package/config/agents/templates/draft_review.yaml +6 -6
  35. package/config/agents/templates/intake_extractor.yaml +64 -62
  36. package/config/agents/templates/research_brief.yaml +6 -6
  37. package/config/agents/templates/research_desk.yaml +6 -6
  38. package/config/agents/templates/review_panel.yaml +8 -8
  39. package/config/agents/templates/support_triage.yaml +30 -29
  40. package/config/agents/templates/systems_operator.yaml +24 -5
  41. package/db/migrations/0014_durable_runs.sql +31 -0
  42. package/dist/lib/a2a/app.d.ts +27 -0
  43. package/dist/lib/a2a/app.js +64 -5
  44. package/dist/lib/a2a/executor.d.ts +7 -1
  45. package/dist/lib/a2a/executor.js +43 -5
  46. package/dist/lib/a2a/oauthSetup.d.ts +106 -0
  47. package/dist/lib/a2a/oauthSetup.js +259 -0
  48. package/dist/lib/agentDialect.d.ts +76 -0
  49. package/dist/lib/agentDialect.js +151 -0
  50. package/dist/lib/compile.d.ts +42 -6
  51. package/dist/lib/compile.js +97 -45
  52. package/dist/lib/compileNative.d.ts +11 -3
  53. package/dist/lib/compileNative.js +20 -1
  54. package/dist/lib/dispatch.d.ts +10 -5
  55. package/dist/lib/dispatch.js +10 -5
  56. package/dist/lib/doctor.d.ts +110 -1
  57. package/dist/lib/doctor.js +205 -9
  58. package/dist/lib/loadSyndicate.d.ts +54 -2
  59. package/dist/lib/loadSyndicate.js +22 -2
  60. package/dist/lib/memory/namespace.d.ts +6 -5
  61. package/dist/lib/memory/namespace.js +6 -5
  62. package/dist/lib/memory/providers.js +3 -4
  63. package/dist/lib/models/capabilities.d.ts +25 -1
  64. package/dist/lib/models/capabilities.js +79 -1
  65. package/dist/lib/models/chatCompletionsAdapter.d.ts +11 -3
  66. package/dist/lib/models/chatCompletionsAdapter.js +19 -5
  67. package/dist/lib/models/claudeAdapter.js +4 -0
  68. package/dist/lib/models/contract.d.ts +5 -2
  69. package/dist/lib/models/gateway.d.ts +2 -1
  70. package/dist/lib/models/gateway.js +4 -2
  71. package/dist/lib/models/gatewayAdapter.d.ts +7 -1
  72. package/dist/lib/models/gatewayAdapter.js +8 -0
  73. package/dist/lib/models/geminiAdapter.js +8 -2
  74. package/dist/lib/models/genaiMapping.d.ts +8 -4
  75. package/dist/lib/models/genaiMapping.js +23 -9
  76. package/dist/lib/models/gptAdapter.d.ts +4 -2
  77. package/dist/lib/models/gptAdapter.js +11 -4
  78. package/dist/lib/models/grokAdapter.d.ts +3 -1
  79. package/dist/lib/models/grokAdapter.js +11 -3
  80. package/dist/lib/models/kimiAdapter.d.ts +2 -1
  81. package/dist/lib/models/kimiAdapter.js +3 -3
  82. package/dist/lib/models/reasoning.d.ts +30 -2
  83. package/dist/lib/models/reasoning.js +56 -5
  84. package/dist/lib/observability/tracer.d.ts +8 -1
  85. package/dist/lib/observability/tracer.js +33 -5
  86. package/dist/lib/onboarding.d.ts +134 -0
  87. package/dist/lib/onboarding.js +539 -0
  88. package/dist/lib/runtime/approvals.d.ts +8 -0
  89. package/dist/lib/runtime/approvals.js +4 -3
  90. package/dist/lib/runtime/credentials.d.ts +6 -0
  91. package/dist/lib/runtime/native/agentLoop.d.ts +23 -3
  92. package/dist/lib/runtime/native/agentLoop.js +107 -11
  93. package/dist/lib/runtime/native/checkpoint.d.ts +171 -0
  94. package/dist/lib/runtime/native/checkpoint.js +359 -0
  95. package/dist/lib/runtime/native/delegate.d.ts +222 -18
  96. package/dist/lib/runtime/native/delegate.js +390 -39
  97. package/dist/lib/runtime/native/interrupts.d.ts +184 -2
  98. package/dist/lib/runtime/native/interrupts.js +391 -1
  99. package/dist/lib/runtime/native/request.d.ts +13 -3
  100. package/dist/lib/runtime/native/request.js +28 -4
  101. package/dist/lib/runtime/native/telemetry.js +10 -15
  102. package/dist/lib/runtime/questions.d.ts +7 -4
  103. package/dist/lib/runtime/sessions.d.ts +1 -1
  104. package/dist/lib/runtime/syndicateTurn.d.ts +11 -3
  105. package/dist/lib/runtime/syndicateTurn.js +353 -68
  106. package/dist/lib/runtime/turnControl.d.ts +56 -0
  107. package/dist/lib/runtime/turnControl.js +10 -0
  108. package/dist/lib/session/transcript.d.ts +9 -0
  109. package/dist/lib/session/transcript.js +20 -1
  110. package/dist/lib/skills.d.ts +18 -0
  111. package/dist/lib/skills.js +73 -1
  112. package/dist/lib/storage/postgres/index.d.ts +12 -2
  113. package/dist/lib/storage/postgres/index.js +8 -3
  114. package/dist/lib/storage/postgres/taskQueue.d.ts +20 -4
  115. package/dist/lib/storage/postgres/taskQueue.js +29 -5
  116. package/dist/lib/storage/postgres/taskStore.d.ts +26 -2
  117. package/dist/lib/storage/postgres/taskStore.js +74 -8
  118. package/dist/lib/syndicateSchema.d.ts +316 -1
  119. package/dist/lib/syndicateSchema.js +203 -29
  120. package/dist/lib/tools/auth.d.ts +20 -6
  121. package/dist/lib/tools/auth.js +21 -5
  122. package/dist/lib/tools/credentialEnv.d.ts +28 -0
  123. package/dist/lib/tools/credentialEnv.js +52 -0
  124. package/dist/lib/tools/credentialHosts.d.ts +88 -0
  125. package/dist/lib/tools/credentialHosts.js +165 -0
  126. package/dist/lib/tools/credentialStore.js +9 -2
  127. package/dist/lib/tools/credentialUses.d.ts +92 -0
  128. package/dist/lib/tools/credentialUses.js +112 -0
  129. package/dist/lib/tools/mcpToolFactory.d.ts +25 -2
  130. package/dist/lib/tools/mcpToolFactory.js +179 -21
  131. package/dist/lib/tools/oauthConsent.js +8 -1
  132. package/dist/lib/tools/oauthHosts.d.ts +73 -0
  133. package/dist/lib/tools/oauthHosts.js +154 -0
  134. package/dist/lib/tools/oauthTools.d.ts +242 -0
  135. package/dist/lib/tools/oauthTools.js +447 -0
  136. package/dist/lib/tools/openapiTools.d.ts +11 -6
  137. package/dist/lib/tools/openapiTools.js +88 -38
  138. package/dist/lib/tools/taskTools.d.ts +61 -6
  139. package/dist/lib/tools/taskTools.js +145 -21
  140. package/dist/lib/workflow/turn.d.ts +22 -3
  141. package/dist/lib/workflow/turn.js +151 -31
  142. package/dist/lib/workflow.d.ts +14 -7
  143. package/dist/lib/workflow.js +14 -7
  144. package/dist/lib/workflowConfig.d.ts +6 -0
  145. package/dist/scripts/a2a_server.d.ts +36 -0
  146. package/dist/scripts/a2a_server.js +131 -1
  147. package/dist/scripts/assistant_worker.d.ts +12 -0
  148. package/dist/scripts/assistant_worker.js +81 -22
  149. package/dist/scripts/doctor.d.ts +7 -2
  150. package/dist/scripts/doctor.js +9 -4
  151. package/dist/scripts/setup.d.ts +23 -0
  152. package/dist/scripts/setup.js +154 -0
  153. package/dist/scripts/skills_install.d.ts +3 -1
  154. package/dist/scripts/skills_install.js +17 -2
  155. package/dist/scripts/yaml_codemod.d.ts +62 -0
  156. package/dist/scripts/yaml_codemod.js +652 -0
  157. package/package.json +8 -2
  158. package/skills/README.md +11 -2
  159. package/skills/briefs/_shared.md +1 -1
  160. package/skills/briefs/melchizedek-author.md +7 -5
  161. package/skills/briefs/melchizedek-models.md +7 -4
  162. package/skills/briefs/melchizedek.md +1 -0
  163. package/skills/briefs/skills-readme.md +3 -1
  164. package/skills/melchizedek/SKILL.md +1 -0
  165. package/skills/melchizedek-author/SKILL.md +7 -4
  166. package/skills/melchizedek-author/assets/minimal.yaml +13 -13
  167. package/skills/melchizedek-models/SKILL.md +11 -4
  168. package/skills/melchizedek-onboard/SKILL.md +49 -0
  169. package/skills/melchizedek-onboard-cloud/SKILL.md +38 -0
  170. package/skills/melchizedek-onboard-keys/SKILL.md +35 -0
  171. package/skills/melchizedek-onboard-local/SKILL.md +41 -0
  172. package/skills/melchizedek-onboard-serve/SKILL.md +43 -0
@@ -20,7 +20,7 @@
20
20
  import { PROVIDERS } from './providerMap.js';
21
21
  import { planTransport } from './gateway.js';
22
22
  import { nativeSearchOn, PLATFORMS_FOR, platformFromEnv } from './endpoints.js';
23
- import { claudeUrlImagesOn } from './claudeModels.js';
23
+ import { claudeGeneration, claudeUrlImagesOn } from './claudeModels.js';
24
24
  /** The platform a direct path uses (ADR 0023); a misconfigured one reads as direct here, the doctor reports it. */
25
25
  function directPlatform(provider) {
26
26
  try {
@@ -117,6 +117,7 @@ export const CAPABILITIES = [
117
117
  'delegation',
118
118
  'memory_tools',
119
119
  'structured_output',
120
+ 'structured_output_with_tools',
120
121
  'thinking_with_tools',
121
122
  'streaming',
122
123
  'vision',
@@ -126,6 +127,7 @@ export const CAPABILITY_LABELS = {
126
127
  delegation: 'delegation (subagents as tools)',
127
128
  memory_tools: 'memory tools (load_memory)',
128
129
  structured_output: 'structured output (outputSchema)',
130
+ structured_output_with_tools: 'structured output beside tools (an outputSchema on an agent that calls tools or delegates)',
129
131
  thinking_with_tools: 'thinking with tool use',
130
132
  streaming: 'token streaming',
131
133
  vision: 'image input',
@@ -138,12 +140,14 @@ const nativeSearch = (row) => SERVER_SIDE_TOOLS.web_search.includes(row)
138
140
  ? ok()
139
141
  : unsupported('no native search on this path; the web_search sentinel is dropped (use web_extract)');
140
142
  const responsesReasoningNote = (ids) => `encrypted reasoning items are replayed verbatim within the turn's tool loop, with store: false (ADR 0050); ${ids}`;
143
+ const setModelResponseNote = (why) => `the schema travels as a set_model_response tool beside the agent's tools, so the model is asked, not held, to answer in it: the turn ends when it calls the tool, and its arguments are the answer (${why}; ADR 0109)`;
141
144
  const CHAT_THINKING_NOTE = "reasoning: is the lever, sent as reasoning_effort in the model's word, a thinking budget as the level that covers it (ADR 0047); the reasoning is not carried between tool steps, so the model re-reasons each step";
142
145
  export const CAPABILITY_MATRIX = {
143
146
  gemini: {
144
147
  delegation: ok(),
145
148
  memory_tools: ok(),
146
149
  structured_output: ok(),
150
+ structured_output_with_tools: degraded(setModelResponseNote('on the Gemini API; Gemini 2 and later on Vertex AI take responseJsonSchema beside the tools in one request')),
147
151
  thinking_with_tools: ok(),
148
152
  streaming: ok(),
149
153
  vision: ok(),
@@ -153,6 +157,7 @@ export const CAPABILITY_MATRIX = {
153
157
  delegation: ok(),
154
158
  memory_tools: ok(),
155
159
  structured_output: ok('test', 'output_config.format (json_schema) from Opus 4.8, Sonnet 5 and Haiku 5.5 on; a forced tool call on Claude 4.6 and earlier and Opus 4.7, offered under tool_choice auto when thinking is on (ADR 0049)'),
160
+ structured_output_with_tools: ok('test', 'output_config.format beside the tools in one request from Opus 4.8, Sonnet 5 and Haiku 5.5 on; Claude 4.6 and earlier and Opus 4.7 get a set_model_response tool beside them (ADR 0109)'),
156
161
  thinking_with_tools: ok('test', "a thinking budget on Claude 4.6 and earlier, adaptive thinking with output_config.effort after (ADR 0049); signed thinking blocks are replayed verbatim within the turn's tool loop (ADR 0046), and where the model binds them to the conversation (Fable 5.1, Opus 5.5, Sonnet 5.5, Haiku 5.5) under drop_block, so a block whose history changed is dropped rather than rejected; with a budget, a step answering another model's tool call runs without thinking"),
157
162
  streaming: ok(),
158
163
  vision: ok('test', 'user-turn images only'),
@@ -162,6 +167,7 @@ export const CAPABILITY_MATRIX = {
162
167
  delegation: ok(),
163
168
  memory_tools: ok(),
164
169
  structured_output: ok(),
170
+ structured_output_with_tools: ok('test', 'text.format (strict json_schema) beside the tools in one request (ADR 0109)'),
165
171
  thinking_with_tools: ok('test', responsesReasoningNote('reasoning ids only (o-series, gpt-5*)')),
166
172
  streaming: ok(),
167
173
  vision: ok('test', 'user-turn images only'),
@@ -171,6 +177,7 @@ export const CAPABILITY_MATRIX = {
171
177
  delegation: ok(),
172
178
  memory_tools: ok(),
173
179
  structured_output: ok(),
180
+ structured_output_with_tools: degraded(setModelResponseNote('the schema is not yet sent beside tools on xAI')),
174
181
  thinking_with_tools: ok('test', responsesReasoningNote('grok-4.5, grok-4.6 and grok-4.7; other grok ids re-reason each step')),
175
182
  streaming: ok(),
176
183
  vision: ok('test', 'user-turn images only'),
@@ -180,6 +187,7 @@ export const CAPABILITY_MATRIX = {
180
187
  delegation: ok(),
181
188
  memory_tools: ok(),
182
189
  structured_output: ok('test', 'strict json_schema; kimi-k2.6 is documented as unstable on complex schemas ($ref, oneOf)'),
190
+ structured_output_with_tools: degraded(setModelResponseNote('the schema is not yet sent beside tools on Moonshot')),
183
191
  thinking_with_tools: ok('test', "reasoning_content is sent back on the turn's tool-loop assistant messages for the same model (ADR 0046) on kimi-k3, kimi-k2.6 and kimi-k2.7-code; earlier turns' reasoning is not, which K3 and K2.7 Code also ask for; effort travels as reasoning_effort (K3) or a thinking switch (K2.x)"),
184
192
  streaming: ok(),
185
193
  vision: ok('test', 'user-turn images only, sent as base64 (Moonshot takes no public image URLs)'),
@@ -189,6 +197,7 @@ export const CAPABILITY_MATRIX = {
189
197
  delegation: ok(),
190
198
  memory_tools: ok(),
191
199
  structured_output: ok('test', 'json_schema, enforced by grammar-constrained decoding from Ollama 0.5.0 (an older server ignores it); not enforced on Ollama Cloud (ADR 0096)'),
200
+ structured_output_with_tools: degraded(setModelResponseNote('grammar-constrained decoding to the schema would hold back the tool calls')),
192
201
  thinking_with_tools: degraded(CHAT_THINKING_NOTE),
193
202
  streaming: ok(),
194
203
  vision: ok('test', 'needs a vision model, e.g. ollama/qwen3-vl:8b'),
@@ -198,6 +207,7 @@ export const CAPABILITY_MATRIX = {
198
207
  delegation: ok(),
199
208
  memory_tools: ok(),
200
209
  structured_output: ok('test', 'strict json_schema; upstream support varies by model'),
210
+ structured_output_with_tools: degraded(setModelResponseNote('upstream support for a schema beside tools varies by model')),
201
211
  thinking_with_tools: degraded(CHAT_THINKING_NOTE),
202
212
  streaming: ok(),
203
213
  vision: ok('test', 'upstream model must accept images'),
@@ -215,6 +225,62 @@ export function capabilityOf(model, capability, opts = {}) {
215
225
  }
216
226
  return { row, ...CAPABILITY_MATRIX[row][capability] };
217
227
  }
228
+ /**
229
+ * The reasoning levels above `high` (ADR 0117), per row: what `reasoning:
230
+ * xhigh` and `reasoning: max` reach the provider as. A level a model lacks
231
+ * goes as its highest setting, and the request's `llm.request` span carries
232
+ * `llm.reasoning.weakened` with the level asked for. Asserted against the
233
+ * request bodies in tests/toolChoiceEffort.test.ts; the direct adapters read
234
+ * lib/models/reasoning.ts effortCeiling.
235
+ */
236
+ export const REASONING_ABOVE_HIGH = {
237
+ gemini: 'xhigh and max go as thinkingLevel HIGH (a 16,384-token budget on Gemini 2.x), weakened',
238
+ anthropic: 'output_config.effort xhigh and max as asked on the adaptive generations (Opus 4.7 and later, Sonnet 5 and later, Haiku 5.5); a 16,384-token budget, weakened, on Claude 4.6 and earlier',
239
+ openai: 'xhigh as asked on GPT-5.2 and later, and max goes as xhigh there, weakened; high, weakened, on the o-series, GPT-5 and GPT-5.1',
240
+ xai: 'xhigh as asked on grok-4.7, and max goes as xhigh there, weakened; high, weakened, on grok-4.5 and grok-4.6',
241
+ moonshot: 'max as asked on kimi-k3, and xhigh rounds up to max; the K2 generation takes a thinking switch, which these leave on',
242
+ ollama: 'reasoning_effort high, weakened',
243
+ gateway: 'reasoning_effort high, weakened, whatever the id: the words above high are not accepted across gateways',
244
+ };
245
+ /**
246
+ * Whether an outputSchema goes in the same request as the agent's tools, as
247
+ * the provider's own structured-output field, on the path `model` takes
248
+ * (ADR 0109). True only where the `structured_output_with_tools` cell is
249
+ * supported and the model's generation takes it: Claude from Opus 4.8,
250
+ * Sonnet 5 and Haiku 5.5 on (`output_config.format`), every OpenAI id
251
+ * (`text.format`), Gemini 2 and later on Vertex AI (`responseJsonSchema`).
252
+ * Elsewhere the native loop declares set_model_response beside the tools
253
+ * (lib/runtime/native/request.ts), which works on every path. A path that
254
+ * cannot be planned (a malformed gateway setting) answers false, the form
255
+ * that works everywhere.
256
+ */
257
+ export function outputSchemaBesideTools(model, opts = {}) {
258
+ let cell;
259
+ try {
260
+ cell = capabilityOf(model, 'structured_output_with_tools', opts);
261
+ }
262
+ catch {
263
+ return false;
264
+ }
265
+ if (cell.support !== 'supported')
266
+ return false;
267
+ switch (cell.row) {
268
+ case 'anthropic':
269
+ return claudeGeneration(model).structuredOutput === 'output_format';
270
+ case 'gemini':
271
+ return geminiMajorVersion(model) >= 2;
272
+ default:
273
+ return true;
274
+ }
275
+ }
276
+ /** A Gemini id's major version (a Vertex AI resource name's last segment), or 0 when it is not a `gemini-<n>` id. Read without a pattern. */
277
+ function geminiMajorVersion(model) {
278
+ const name = model.slice(model.lastIndexOf('/') + 1);
279
+ if (!name.startsWith('gemini-'))
280
+ return 0;
281
+ const major = Number.parseInt(name.slice('gemini-'.length), 10);
282
+ return Number.isFinite(major) ? major : 0;
283
+ }
218
284
  export function requiredCapabilities(agent) {
219
285
  const tools = agent.tools ?? [];
220
286
  const needs = new Set();
@@ -224,6 +290,8 @@ export function requiredCapabilities(agent) {
224
290
  needs.add('memory_tools');
225
291
  if (agent.outputSchema)
226
292
  needs.add('structured_output');
293
+ if (agent.outputSchema && (tools.length > 0 || agent.delegates))
294
+ needs.add('structured_output_with_tools');
227
295
  const thinking = agent.generateContentConfig?.thinkingConfig;
228
296
  const thinks = agent.reasoning !== undefined
229
297
  ? agent.reasoning !== 'none' && !(typeof agent.reasoning === 'object' && agent.reasoning.budget_tokens === 0)
@@ -256,6 +324,9 @@ export function capabilityGaps(model, agent, opts = {}) {
256
324
  export function platformCell(provider, platform, capability) {
257
325
  if (platform === 'direct')
258
326
  return undefined;
327
+ if (capability === 'structured_output_with_tools' && provider === 'gemini' && platform === 'vertex') {
328
+ return ok('test', 'responseJsonSchema beside the tools in one request on Gemini 2 and later; an earlier id gets set_model_response (ADR 0109)');
329
+ }
259
330
  if (capability === 'vision' && provider === 'anthropic' && !claudeUrlImagesOn(platform)) {
260
331
  return degraded(`user-turn images inline (base64) only; an image given by URL is dropped, since ${PLATFORM_LABEL[platform]} takes no URL image source`);
261
332
  }
@@ -293,6 +364,13 @@ export function renderCapabilityMatrix() {
293
364
  lines.push('');
294
365
  notes.forEach((n, i) => lines.push(`${i + 1}. ${n}`));
295
366
  lines.push('');
367
+ lines.push('**Reasoning above high** (ADR 0117): what `reasoning: xhigh` and `reasoning: max` reach each path as. "Weakened" means the model\'s highest setting is sent and the span carries `llm.reasoning.weakened`.');
368
+ lines.push('');
369
+ lines.push('| Path | xhigh and max |');
370
+ lines.push('|---|---|');
371
+ for (const r of rows)
372
+ lines.push(`| ${label(r)} | ${REASONING_ABOVE_HIGH[r]} |`);
373
+ lines.push('');
296
374
  lines.push('**Cloud platforms** (ADR 0023): the same adapter and request as the provider\'s own API, except as listed. These paths are tested against mocks, not against the live clouds.');
297
375
  lines.push('');
298
376
  lines.push('| Path | Differs from the provider row |');
@@ -54,7 +54,7 @@
54
54
  * ADR 0053): llm.transport, llm.finish_reason, llm.http_status,
55
55
  * llm.retries, llm.retry_without_thinking, and the search and drop marks.
56
56
  */
57
- import type { ModelAdapter, ModelRequest, ModelResponse, ReasoningSetting, ToolChoiceMode, Usage } from './contract.ts';
57
+ import type { ModelAdapter, ModelRequest, ModelResponse, ReasoningLevel, ReasoningSetting, ToolChoiceMode, Usage } from './contract.ts';
58
58
  type ChatContentPart = {
59
59
  type: 'text';
60
60
  text: string;
@@ -190,10 +190,18 @@ export declare abstract class ChatCompletionsAdapter implements ModelAdapter {
190
190
  /**
191
191
  * The body fields for the request's reasoning. Default: `reasoning_effort`
192
192
  * as the effort word ADR 0047 gives this model (lib/models/reasoning.ts),
193
- * the field every chat-completions provider and gateway reads; nothing
194
- * when the request sets no reasoning.
193
+ * the field every chat-completions provider and gateway reads, held at
194
+ * `reasoningCeiling(model)`; nothing when the request sets no reasoning.
195
195
  */
196
196
  protected reasoningFields(model: string, setting: ReasoningSetting | undefined): Record<string, unknown>;
197
+ /**
198
+ * The highest reasoning level this adapter sends as asked for `model`
199
+ * (ADR 0117). Default: the direct provider's (lib/models/reasoning.ts
200
+ * effortCeiling); the gateway holds every id at `high`.
201
+ */
202
+ protected reasoningCeiling(model: string): ReasoningLevel;
203
+ /** The effort word for one setting, held at reasoningCeiling; a held level is marked on the span (`llm.reasoning.weakened`). */
204
+ protected effortFor(model: string, setting: ReasoningSetting): string;
197
205
  /** Provider-specific body fields, merged last. */
198
206
  protected extraBodyFields(_request: ModelRequest): Record<string, unknown>;
199
207
  /**
@@ -56,7 +56,7 @@
56
56
  */
57
57
  import { errorDecision, errorText, statusDecision } from './errorResponse.js';
58
58
  import { currentTurnStart, providerStateOf } from './providerState.js';
59
- import { reasoningConfig } from './reasoning.js';
59
+ import { effortCeiling as effortCeilingOf, effortWord } from './reasoning.js';
60
60
  import { fetchWithRetry } from './retry.js';
61
61
  import { toStrictJsonSchema } from './schemaNormalize.js';
62
62
  import { setLlmSpanAttribute } from '../observability/tracer.js';
@@ -327,12 +327,26 @@ export class ChatCompletionsAdapter {
327
327
  /**
328
328
  * The body fields for the request's reasoning. Default: `reasoning_effort`
329
329
  * as the effort word ADR 0047 gives this model (lib/models/reasoning.ts),
330
- * the field every chat-completions provider and gateway reads; nothing
331
- * when the request sets no reasoning.
330
+ * the field every chat-completions provider and gateway reads, held at
331
+ * `reasoningCeiling(model)`; nothing when the request sets no reasoning.
332
332
  */
333
333
  reasoningFields(model, setting) {
334
- const word = setting !== undefined ? reasoningConfig(model, setting).reasoningEffort : undefined;
335
- return word !== undefined ? { reasoning_effort: word } : {};
334
+ return setting !== undefined ? { reasoning_effort: this.effortFor(model, setting) } : {};
335
+ }
336
+ /**
337
+ * The highest reasoning level this adapter sends as asked for `model`
338
+ * (ADR 0117). Default: the direct provider's (lib/models/reasoning.ts
339
+ * effortCeiling); the gateway holds every id at `high`.
340
+ */
341
+ reasoningCeiling(model) {
342
+ return effortCeilingOf(model);
343
+ }
344
+ /** The effort word for one setting, held at reasoningCeiling; a held level is marked on the span (`llm.reasoning.weakened`). */
345
+ effortFor(model, setting) {
346
+ const { word, weakened } = effortWord(model, setting, this.reasoningCeiling(model));
347
+ if (weakened)
348
+ setLlmSpanAttribute('llm.reasoning.weakened', weakened);
349
+ return word;
336
350
  }
337
351
  /** Provider-specific body fields, merged last. */
338
352
  extraBodyFields(_request) {
@@ -46,6 +46,7 @@ import { claudeClientSpec, endpointFromEnv, endpointLabel, instantiateClient, na
46
46
  import { currentTurnStart, providerStateOf, withProviderState } from './providerState.js';
47
47
  import { errorDecision, errorText } from './errorResponse.js';
48
48
  import { adaptiveThinkingFor, claudeGeneration, claudeReasoningOf, claudeUrlImagesOn, THINKING_BINDING_BETA } from './claudeModels.js';
49
+ import { isAboveHigh } from './reasoning.js';
49
50
  /** The provider id this adapter reports and writes its state under (lib/models/providerMap.ts). */
50
51
  export const ANTHROPIC_PROVIDER = 'anthropic';
51
52
  /** The providerState kind this adapter writes: the signed blocks that preceded a part. */
@@ -393,6 +394,9 @@ export class ClaudeAdapter {
393
394
  else {
394
395
  const requestedBudget = reasoning.budget;
395
396
  thinkingConfigured = typeof requestedBudget === 'number' && requestedBudget > 0;
397
+ // The budget generations stop at `high`'s budget (ADR 0117).
398
+ if (isAboveHigh(request.reasoning))
399
+ setLlmSpanAttribute('llm.reasoning.weakened', request.reasoning);
396
400
  if (thinkingConfigured && continuesUnsignedToolLoop(messages)) {
397
401
  // The tool call this step answers carries no signed thinking (another
398
402
  // provider or model made it, or its state was lost), and Anthropic
@@ -181,9 +181,12 @@ export type ToolChoice = 'auto' | 'none' | 'required' | {
181
181
  /**
182
182
  * How hard an agent reasons, on any provider (ADR 0047): a level, or a token
183
183
  * budget. A budget of 0 is `none`, and `none` means as little reasoning as
184
- * the model allows.
184
+ * the model allows. `xhigh` and `max` are the levels above `high` that some
185
+ * models take (ADR 0117): an adapter whose model stops lower sends its
186
+ * highest setting and marks the span `llm.reasoning.weakened`; a budget
187
+ * never reads as either.
185
188
  */
186
- export type ReasoningLevel = 'none' | 'low' | 'medium' | 'high';
189
+ export type ReasoningLevel = 'none' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
187
190
  export type ReasoningSetting = ReasoningLevel | {
188
191
  budget_tokens: number;
189
192
  };
@@ -70,7 +70,8 @@ export interface GatewayConfig {
70
70
  */
71
71
  export declare function gatewayConfig(): GatewayConfig | null;
72
72
  /** A human-readable misconfiguration, or undefined when the gateway is
73
- * either off or usable. */
73
+ * either off or usable. An unrecognised value is named as such and never
74
+ * repeated, since it may be a secret pasted into the wrong variable. */
74
75
  export declare function gatewayProblem(): string | undefined;
75
76
  /** True when a gateway is configured well enough to serve requests. */
76
77
  export declare function gatewayUsable(): boolean;
@@ -81,14 +81,16 @@ export function gatewayConfig() {
81
81
  };
82
82
  }
83
83
  /** A human-readable misconfiguration, or undefined when the gateway is
84
- * either off or usable. */
84
+ * either off or usable. An unrecognised value is named as such and never
85
+ * repeated, since it may be a secret pasted into the wrong variable. */
85
86
  export function gatewayProblem() {
86
87
  const id = (process.env[GATEWAY_ENV] ?? '').trim();
87
88
  if (!id)
88
89
  return undefined;
89
90
  const cfg = gatewayConfig();
90
91
  if (!cfg) {
91
- return `${GATEWAY_ENV}=${id} is not a known gateway (expected one of: ${Object.keys(GATEWAYS).join(', ')})`;
92
+ // The value is never echoed: a key pasted into the wrong variable is still a secret.
93
+ return `${GATEWAY_ENV} is set to an unrecognised value (not shown); it must be one of: ${Object.keys(GATEWAYS).join(', ')}`;
92
94
  }
93
95
  if (!cfg.keyPresent)
94
96
  return `${GATEWAY_ENV}=${cfg.gateway.id} is set but ${GATEWAY_KEY_ENV} is not`;
@@ -28,7 +28,7 @@
28
28
  * adapter replays no providerState: a chat-completions wire has no place
29
29
  * for Claude's signed blocks or OpenAI's reasoning items.
30
30
  */
31
- import type { ToolChoiceMode } from './contract.ts';
31
+ import type { ReasoningLevel, ToolChoiceMode } from './contract.ts';
32
32
  import { ChatCompletionsAdapter } from './chatCompletionsAdapter.ts';
33
33
  import type { ChatFailure } from './chatCompletionsAdapter.ts';
34
34
  export interface GatewayAdapterOptions {
@@ -44,6 +44,12 @@ export declare class GatewayAdapter extends ChatCompletionsAdapter {
44
44
  protected endpointUrl(): string;
45
45
  protected headers(): Record<string, string>;
46
46
  protected wireModelName(model: string): string;
47
+ /**
48
+ * A gateway reads `reasoning_effort` up to `high` whatever the id: the
49
+ * words above it (`xhigh`, `max`) are not accepted across gateways, so
50
+ * they go as `high`, marked on the span (ADR 0117).
51
+ */
52
+ protected reasoningCeiling(): ReasoningLevel;
47
53
  /** A gateway passes tool_choice to the upstream as asked. */
48
54
  protected toolChoiceModes(): readonly ToolChoiceMode[];
49
55
  protected missingRequirement(): ChatFailure | undefined;
@@ -52,6 +52,14 @@ export class GatewayAdapter extends ChatCompletionsAdapter {
52
52
  wireModelName(model) {
53
53
  return this.#cfg ? gatewayWireModel(model, this.#cfg.gateway) : model;
54
54
  }
55
+ /**
56
+ * A gateway reads `reasoning_effort` up to `high` whatever the id: the
57
+ * words above it (`xhigh`, `max`) are not accepted across gateways, so
58
+ * they go as `high`, marked on the span (ADR 0117).
59
+ */
60
+ reasoningCeiling() {
61
+ return 'high';
62
+ }
55
63
  /** A gateway passes tool_choice to the upstream as asked. */
56
64
  toolChoiceModes() {
57
65
  return ['auto', 'none', 'required', 'named'];
@@ -50,7 +50,7 @@ import { endpointFromEnv, endpointProblems, platformModel } from './endpoints.js
50
50
  import { CARRIED_PARTS_KIND, GEMINI_PROVIDER, GENAI_PART_KIND, MINTED_CALL_ID_PREFIX, PLACEHOLDER_THOUGHT_SIGNATURE, THOUGHT_SIGNATURE_KIND, isCarriedWirePart, } from './geminiState.js';
51
51
  import { currentTurnStart, providerStateOf } from './providerState.js';
52
52
  import { classifyError, errorStatus, retryUntilFirstYield } from './retry.js';
53
- import { reasoningConfig } from './reasoning.js';
53
+ import { isAboveHigh, reasoningConfig } from './reasoning.js';
54
54
  import { setLlmSpanAttribute } from '../observability/tracer.js';
55
55
  /**
56
56
  * The provider id this adapter reports and writes its state under, and the
@@ -445,11 +445,17 @@ function toolConfigFor(request) {
445
445
  : undefined;
446
446
  return mode ? { functionCallingConfig: { mode } } : undefined;
447
447
  }
448
- /** ADR 0047's mapping for this model, with the thoughts asked for unless the setting is `none`. */
448
+ /**
449
+ * ADR 0047's mapping for this model, with the thoughts asked for unless the
450
+ * setting is `none`. Gemini's top is `HIGH` (or `high`'s budget on 2.x), so
451
+ * `xhigh` and `max` go as that, marked on the span (ADR 0117).
452
+ */
449
453
  function thinkingConfigFor(model, setting) {
450
454
  const mapped = reasoningConfig(model, setting).thinkingConfig;
451
455
  if (!mapped)
452
456
  return undefined;
457
+ if (isAboveHigh(setting))
458
+ setLlmSpanAttribute('llm.reasoning.weakened', setting);
453
459
  const none = setting === 'none' || (typeof setting === 'object' && setting.budget_tokens <= 0);
454
460
  return none ? { ...mapped } : { ...mapped, includeThoughts: true };
455
461
  }
@@ -271,11 +271,15 @@ export interface ModelRequestOptions {
271
271
  * which every adapter maps to what the budget maps to today (0 included).
272
272
  * - else `reasoningEffort`: a level word is that level, and `minimal` is
273
273
  * `none` (ADR 0047's rendering of it on the first GPT-5 generation).
274
+ * The words above `high` (`xhigh`, `max`) win over a thinkingConfig that
275
+ * says `high` (`HIGH`, or `high`'s budget of 16,384), which is what the
276
+ * compiler writes beside them (ADR 0117); a thinkingConfig that says less
277
+ * keeps its own reading.
274
278
  * Lossy, and absent: a budget of -1 (Gemini's dynamic thinking, which is
275
- * the provider's default), `THINKING_LEVEL_UNSPECIFIED`, the effort words
276
- * `xhigh` and `max`, and `includeThoughts`. The compiler writes the effort
277
- * word beside thinkingConfig from one setting, so preferring thinkingConfig
278
- * loses nothing it wrote; only the older spelling can set the two apart.
279
+ * the provider's default), `THINKING_LEVEL_UNSPECIFIED`, and
280
+ * `includeThoughts`. The compiler writes the effort word beside
281
+ * thinkingConfig from one setting, so preferring thinkingConfig loses
282
+ * nothing it wrote; only the older spelling can set the two apart.
279
283
  */
280
284
  export declare function reasoningOf(config: GenerateContentConfig | undefined): ReasoningSetting | undefined;
281
285
  /** The function-calling mode as a ToolChoice, and VALIDATED as strict tools. The native request builder reads an agent's config through it too. */
@@ -152,7 +152,7 @@
152
152
  import { FinishReason, FunctionCallingConfigMode } from '@google/genai';
153
153
  import { ERROR_RETRYABLE_KEY, ERROR_STATUS_KEY, errorText, withRetryVerdict } from './errorResponse.js';
154
154
  import { CARRIED_PARTS_KIND, GEMINI_PROVIDER, GENAI_PART_KIND, MINTED_CALL_ID_PREFIX, THOUGHT_SIGNATURE_KIND } from './geminiState.js';
155
- import { reasoningConfig } from './reasoning.js';
155
+ import { REASONING_BUDGETS, reasoningConfig } from './reasoning.js';
156
156
  import { contractToolDeclaration, nativeToolOf, toContractJsonSchema } from './schemaNormalize.js';
157
157
  import { MAX_VALUE_DEPTH, nestedDeeperThan } from '../runtime/valueDepth.js';
158
158
  /**
@@ -473,7 +473,15 @@ export function messagesToContents(history) {
473
473
  }
474
474
  // Maps, not object literals: a config value such as `constructor` must read as nothing.
475
475
  const THINKING_LEVELS = new Map([['MINIMAL', 'none'], ['LOW', 'low'], ['MEDIUM', 'medium'], ['HIGH', 'high']]);
476
- const EFFORT_LEVELS = new Map([['none', 'none'], ['minimal', 'none'], ['low', 'low'], ['medium', 'medium'], ['high', 'high']]);
476
+ const EFFORT_LEVELS = new Map([
477
+ ['none', 'none'],
478
+ ['minimal', 'none'],
479
+ ['low', 'low'],
480
+ ['medium', 'medium'],
481
+ ['high', 'high'],
482
+ ['xhigh', 'xhigh'],
483
+ ['max', 'max'],
484
+ ]);
477
485
  /**
478
486
  * The ReasoningSetting a generateContentConfig asks for, where one is exact:
479
487
  * - `thinkingConfig.thinkingLevel`: `MINIMAL` is `none` (ADR 0047's
@@ -482,24 +490,30 @@ const EFFORT_LEVELS = new Map([['none', 'none'], ['minimal', 'none'], ['low', 'l
482
490
  * which every adapter maps to what the budget maps to today (0 included).
483
491
  * - else `reasoningEffort`: a level word is that level, and `minimal` is
484
492
  * `none` (ADR 0047's rendering of it on the first GPT-5 generation).
493
+ * The words above `high` (`xhigh`, `max`) win over a thinkingConfig that
494
+ * says `high` (`HIGH`, or `high`'s budget of 16,384), which is what the
495
+ * compiler writes beside them (ADR 0117); a thinkingConfig that says less
496
+ * keeps its own reading.
485
497
  * Lossy, and absent: a budget of -1 (Gemini's dynamic thinking, which is
486
- * the provider's default), `THINKING_LEVEL_UNSPECIFIED`, the effort words
487
- * `xhigh` and `max`, and `includeThoughts`. The compiler writes the effort
488
- * word beside thinkingConfig from one setting, so preferring thinkingConfig
489
- * loses nothing it wrote; only the older spelling can set the two apart.
498
+ * the provider's default), `THINKING_LEVEL_UNSPECIFIED`, and
499
+ * `includeThoughts`. The compiler writes the effort word beside
500
+ * thinkingConfig from one setting, so preferring thinkingConfig loses
501
+ * nothing it wrote; only the older spelling can set the two apart.
490
502
  */
491
503
  export function reasoningOf(config) {
492
504
  const cfg = (config ?? {});
505
+ const word = EFFORT_LEVELS.get(cfg.reasoningEffort);
506
+ const above = word === 'xhigh' || word === 'max' ? word : undefined;
493
507
  const thinking = cfg.thinkingConfig;
494
508
  if (isObject(thinking)) {
495
509
  const level = THINKING_LEVELS.get(thinking.thinkingLevel);
496
510
  if (level)
497
- return level;
511
+ return above && level === 'high' ? above : level;
498
512
  const budget = thinking.thinkingBudget;
499
513
  if (typeof budget === 'number' && Number.isInteger(budget) && budget >= 0)
500
- return { budget_tokens: budget };
514
+ return above && budget === REASONING_BUDGETS.high ? above : { budget_tokens: budget };
501
515
  }
502
- return EFFORT_LEVELS.get(cfg.reasoningEffort);
516
+ return word;
503
517
  }
504
518
  function nativeToolsOf(llmRequest) {
505
519
  const tools = new Set();
@@ -187,8 +187,10 @@ export declare class GptAdapter implements ModelAdapter {
187
187
  * The `reasoning` request field for one setting (ADR 0047), or undefined to
188
188
  * send none. OpenAI's reasoning ids ask for summaries, plus the effort in
189
189
  * that model's own word (`reasoningConfig`: `none` is `minimal` on the
190
- * first GPT-5 generation and `low` on the o-series). Other ids: nothing. A
191
- * 400 on the field is retried once without it.
190
+ * first GPT-5 generation and `low` on the o-series), held at the model's
191
+ * ceiling (`xhigh` from GPT-5.2, else `high`; ADR 0117), a held level
192
+ * marked on the span. Other ids: nothing. A 400 on the field is retried
193
+ * once without it.
192
194
  */
193
195
  reasoningParam(setting: ReasoningSetting | undefined, model?: string): Record<string, unknown> | undefined;
194
196
  /**
@@ -45,7 +45,7 @@
45
45
  import { endpointFromEnv, entraTokenSource, nativeSearchOn, platformModel } from './endpoints.js';
46
46
  import { errorDecision, errorText, statusDecision } from './errorResponse.js';
47
47
  import { currentTurnStart, providerStateOf } from './providerState.js';
48
- import { reasoningConfig } from './reasoning.js';
48
+ import { effortWord } from './reasoning.js';
49
49
  import { toContractJsonSchema } from './schemaNormalize.js';
50
50
  import { setLlmSpanAttribute } from '../observability/tracer.js';
51
51
  /** The providerState kind the Responses adapters write: the reasoning output items that preceded a part (ADR 0046, ADR 0050). */
@@ -423,13 +423,20 @@ export class GptAdapter {
423
423
  * The `reasoning` request field for one setting (ADR 0047), or undefined to
424
424
  * send none. OpenAI's reasoning ids ask for summaries, plus the effort in
425
425
  * that model's own word (`reasoningConfig`: `none` is `minimal` on the
426
- * first GPT-5 generation and `low` on the o-series). Other ids: nothing. A
427
- * 400 on the field is retried once without it.
426
+ * first GPT-5 generation and `low` on the o-series), held at the model's
427
+ * ceiling (`xhigh` from GPT-5.2, else `high`; ADR 0117), a held level
428
+ * marked on the span. Other ids: nothing. A 400 on the field is retried
429
+ * once without it.
428
430
  */
429
431
  reasoningParam(setting, model = this.model) {
430
432
  if (!isOpenAiReasoningModel(model))
431
433
  return undefined;
432
- return { summary: 'auto', ...(setting !== undefined ? { effort: reasoningConfig(model, setting).reasoningEffort } : {}) };
434
+ if (setting === undefined)
435
+ return { summary: 'auto' };
436
+ const { word, weakened } = effortWord(model, setting);
437
+ if (weakened)
438
+ setLlmSpanAttribute('llm.reasoning.weakened', weakened);
439
+ return { summary: 'auto', effort: word };
433
440
  }
434
441
  /**
435
442
  * Whether this id carries its reasoning across the steps of a tool loop
@@ -63,7 +63,9 @@ export declare class GrokAdapter extends GptAdapter {
63
63
  * `high`; xAI's own default is `high`, and reasoning cannot be turned off).
64
64
  * The request's setting maps through ADR 0047's table, which sends `none`
65
65
  * as `low`; with none set, DEFAULT_GROK_REASONING_EFFORT (medium) is pinned,
66
- * since effort is part of the price. Other grok ids take no field.
66
+ * since effort is part of the price. grok-4.7 also takes `xhigh`; `max`,
67
+ * and `xhigh` on 4.5 and 4.6, go as the model's highest word, marked on the
68
+ * span (ADR 0117). Other grok ids take no field.
67
69
  */
68
70
  reasoningParam(setting: ReasoningSetting | undefined, model?: string): Record<string, unknown> | undefined;
69
71
  /**
@@ -26,8 +26,9 @@
26
26
  */
27
27
  import { DEFAULT_GROK_REASONING_EFFORT } from '../config.js';
28
28
  import { GptAdapter } from './gptAdapter.js';
29
- import { reasoningConfig } from './reasoning.js';
29
+ import { effortWord } from './reasoning.js';
30
30
  import { collectionIdsFromEnv, collectionsMaxResultsFromEnv, xaiWebSearchParamsFromEnv, xSearchParamsFromEnv, } from '../tools/xaiSearchParams.js';
31
+ import { setLlmSpanAttribute } from '../observability/tracer.js';
31
32
  export const XAI_BASE_URL = 'https://api.x.ai/v1';
32
33
  /**
33
34
  * The ids that take a reasoning effort and return encrypted reasoning:
@@ -81,12 +82,19 @@ export class GrokAdapter extends GptAdapter {
81
82
  * `high`; xAI's own default is `high`, and reasoning cannot be turned off).
82
83
  * The request's setting maps through ADR 0047's table, which sends `none`
83
84
  * as `low`; with none set, DEFAULT_GROK_REASONING_EFFORT (medium) is pinned,
84
- * since effort is part of the price. Other grok ids take no field.
85
+ * since effort is part of the price. grok-4.7 also takes `xhigh`; `max`,
86
+ * and `xhigh` on 4.5 and 4.6, go as the model's highest word, marked on the
87
+ * span (ADR 0117). Other grok ids take no field.
85
88
  */
86
89
  reasoningParam(setting, model = this.model) {
87
90
  if (!GROK_REASONING_IDS.test(model))
88
91
  return undefined;
89
- return { effort: setting === undefined ? DEFAULT_GROK_REASONING_EFFORT : reasoningConfig(model, setting).reasoningEffort };
92
+ if (setting === undefined)
93
+ return { effort: DEFAULT_GROK_REASONING_EFFORT };
94
+ const { word, weakened } = effortWord(model, setting);
95
+ if (weakened)
96
+ setLlmSpanAttribute('llm.reasoning.weakened', weakened);
97
+ return { effort: word };
90
98
  }
91
99
  /**
92
100
  * grok-4.5, grok-4.6 and grok-4.7 carry their reasoning across a tool loop
@@ -68,7 +68,8 @@
68
68
  * goes as its Kimi word (`medium` → `high`, a budget → the level that
69
69
  * covers it), `none` as `low` since K3 cannot switch thinking off, and a
70
70
  * request with none pins DEFAULT_KIMI_REASONING_EFFORT (lib/config.ts).
71
- * `max` has no contract level, so no request sends it.
71
+ * `reasoning: max` sends `max`, and `xhigh` rounds up to it (ADR 0117):
72
+ * Moonshot's costliest effort, and its default, which the pin avoids.
72
73
  * K2.x models take no reasoning_effort: a `thinking: { type }` switch
73
74
  * instead, so `none` (or a budget of 0) sends `disabled` and any other
74
75
  * setting sends nothing. K2.7 Code (and its highspeed variant) cannot
@@ -68,7 +68,8 @@
68
68
  * goes as its Kimi word (`medium` → `high`, a budget → the level that
69
69
  * covers it), `none` as `low` since K3 cannot switch thinking off, and a
70
70
  * request with none pins DEFAULT_KIMI_REASONING_EFFORT (lib/config.ts).
71
- * `max` has no contract level, so no request sends it.
71
+ * `reasoning: max` sends `max`, and `xhigh` rounds up to it (ADR 0117):
72
+ * Moonshot's costliest effort, and its default, which the pin avoids.
72
73
  * K2.x models take no reasoning_effort: a `thinking: { type }` switch
73
74
  * instead, so `none` (or a budget of 0) sends `disabled` and any other
74
75
  * setting sends nothing. K2.7 Code (and its highspeed variant) cannot
@@ -118,7 +119,6 @@
118
119
  */
119
120
  import { DEFAULT_KIMI_REASONING_EFFORT } from '../config.js';
120
121
  import { ChatCompletionsAdapter, reasonsNotAtAll } from './chatCompletionsAdapter.js';
121
- import { reasoningConfig } from './reasoning.js';
122
122
  import { trimTrailingSlashes } from './urls.js';
123
123
  export const MOONSHOT_BASE_URL = 'https://api.moonshot.ai/v1';
124
124
  /** The flagship takes `reasoning_effort`; the K2 generation takes a `thinking` switch. */
@@ -191,7 +191,7 @@ export class KimiAdapter extends ChatCompletionsAdapter {
191
191
  if (isKimiK3(model)) {
192
192
  if (setting === undefined)
193
193
  return { reasoning_effort: DEFAULT_KIMI_REASONING_EFFORT };
194
- const word = reasoningConfig(model, setting).reasoningEffort;
194
+ const word = this.effortFor(model, setting);
195
195
  return { reasoning_effort: word === 'none' ? 'low' : word }; // K3 cannot switch thinking off
196
196
  }
197
197
  // K2.7 Code always thinks, and Moonshot refuses `disabled` for it.