@cognipeer/agent-sdk 0.12.0 → 0.14.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.
package/dist/index.cjs CHANGED
@@ -2026,27 +2026,71 @@ function convertMessageListToSections(messageList) {
2026
2026
  }
2027
2027
  return sections.length > 0 ? sections : void 0;
2028
2028
  }
2029
- function createTraceSession(opts) {
2029
+ function readTraceContinuation(value) {
2030
+ if (!value || typeof value !== "object") return void 0;
2031
+ const raw = value;
2032
+ if (raw.v !== 1 || typeof raw.sessionId !== "string" || raw.sessionId.length === 0) return void 0;
2033
+ const nextSequence = Number(raw.nextSequence);
2034
+ const startedAt = Number(raw.startedAt);
2035
+ const part = Number(raw.part);
2036
+ if (!Number.isInteger(nextSequence) || nextSequence < 1) return void 0;
2037
+ if (!Number.isFinite(startedAt) || !Number.isInteger(part) || part < 1) return void 0;
2038
+ const summary = raw.summary && typeof raw.summary === "object" ? { ...createEmptySummary(), ...raw.summary } : createEmptySummary();
2039
+ summary.eventCounts = { ...raw.summary?.eventCounts ?? {} };
2040
+ return {
2041
+ v: 1,
2042
+ sessionId: raw.sessionId,
2043
+ traceId: typeof raw.traceId === "string" ? raw.traceId : void 0,
2044
+ rootSpanId: typeof raw.rootSpanId === "string" ? raw.rootSpanId : void 0,
2045
+ threadId: typeof raw.threadId === "string" ? raw.threadId : void 0,
2046
+ startedAt,
2047
+ nextSequence,
2048
+ part,
2049
+ summary,
2050
+ errors: Array.isArray(raw.errors) ? raw.errors : []
2051
+ };
2052
+ }
2053
+ function traceContinuationOf(session) {
2054
+ return {
2055
+ v: 1,
2056
+ sessionId: session.sessionId,
2057
+ traceId: session.traceId,
2058
+ rootSpanId: session.rootSpanId,
2059
+ threadId: session.threadId,
2060
+ startedAt: session.startedAt,
2061
+ nextSequence: (session.sequenceOffset ?? 0) + session.events.length + 1,
2062
+ part: session.part ?? 1,
2063
+ summary: JSON.parse(JSON.stringify(session.summary)),
2064
+ // Sink failures belong to the part that hit them; carrying them on would
2065
+ // mark every later part `partial`.
2066
+ errors: session.errors.filter((error) => error.type !== "sink")
2067
+ };
2068
+ }
2069
+ function createTraceSession(opts, continuation) {
2030
2070
  const cfg = withDefaults(opts.tracing);
2031
2071
  if (!cfg.enabled) return void 0;
2032
2072
  const providedSessionId = opts.tracing?.sessionId?.trim();
2033
- const sessionId = providedSessionId || generateSessionId();
2034
- const traceId = generateTraceId();
2035
- const rootSpanId = generateSpanId();
2073
+ const sessionId = continuation?.sessionId || providedSessionId || generateSessionId();
2074
+ const traceId = continuation?.traceId || generateTraceId();
2075
+ const rootSpanId = continuation?.rootSpanId || generateSpanId();
2036
2076
  const runtime = {
2037
2077
  sessionId,
2038
2078
  traceId,
2039
2079
  rootSpanId,
2040
- threadId: opts.tracing?.threadId,
2080
+ threadId: continuation?.threadId ?? opts.tracing?.threadId,
2041
2081
  configAgentName: opts.tracing?.agentName?.trim() || void 0,
2042
2082
  configMetadata: opts.tracing?.metadata && Object.keys(opts.tracing.metadata).length > 0 ? opts.tracing.metadata : void 0,
2043
- startedAt: Date.now(),
2083
+ startedAt: continuation?.startedAt ?? Date.now(),
2044
2084
  resolvedConfig: cfg,
2045
2085
  events: [],
2046
- summary: createEmptySummary(),
2086
+ summary: continuation ? continuation.summary : createEmptySummary(),
2047
2087
  status: "in_progress",
2048
- errors: [],
2049
- sessionStarted: false
2088
+ errors: continuation ? [...continuation.errors] : [],
2089
+ // A continued session was announced by its first part; announcing it
2090
+ // again would reset a streaming receiver's copy.
2091
+ sessionStarted: Boolean(continuation),
2092
+ sequenceOffset: continuation ? continuation.nextSequence - 1 : 0,
2093
+ part: continuation ? continuation.part + 1 : 1
2050
2094
  };
2051
2095
  if (cfg.sink.type === "file") {
2052
2096
  const baseDir = cfg.sink.baseDir;
@@ -2106,7 +2150,7 @@ async function startStreamingSession(session, agentRuntime) {
2106
2150
  }
2107
2151
  function recordTraceEvent(session, event) {
2108
2152
  if (!session) return void 0;
2109
- const sequence = session.events.length + 1;
2153
+ const sequence = (session.sequenceOffset ?? 0) + session.events.length + 1;
2110
2154
  const id = generateEventId(sequence);
2111
2155
  const timestampIso = event.timestamp || (/* @__PURE__ */ new Date()).toISOString();
2112
2156
  const status = event.status ?? "success";
@@ -2296,7 +2340,8 @@ async function finalizeTraceSession(session, params = {}) {
2296
2340
  summary: session.summary,
2297
2341
  events: session.events,
2298
2342
  status: status2,
2299
- errors: session.errors
2343
+ errors: session.errors,
2344
+ ...(session.part ?? 1) > 1 ? { continuation: { part: session.part, firstSequence: (session.sequenceOffset ?? 0) + 1 } } : {}
2300
2345
  });
2301
2346
  const payloadForSink = buildPayload(initialStatus);
2302
2347
  let sinkFailed = false;
@@ -2353,7 +2398,17 @@ async function finalizeTraceSession(session, params = {}) {
2353
2398
  if (sink.type === "file" && session.fileSessionDir) {
2354
2399
  try {
2355
2400
  const filePath = path__default.default.join(session.fileSessionDir, "trace.session.json");
2356
- await fs__default.default.promises.writeFile(filePath, JSON.stringify(finalPayload, null, 2), "utf8");
2401
+ let filePayload = finalPayload;
2402
+ if (finalPayload.continuation) {
2403
+ try {
2404
+ const previous = JSON.parse(await fs__default.default.promises.readFile(filePath, "utf8"));
2405
+ const earlier = (previous.events ?? []).filter((event) => event.sequence < finalPayload.continuation.firstSequence);
2406
+ filePayload = { ...finalPayload, events: [...earlier, ...finalPayload.events] };
2407
+ delete filePayload.continuation;
2408
+ } catch {
2409
+ }
2410
+ }
2411
+ await fs__default.default.promises.writeFile(filePath, JSON.stringify(filePayload, null, 2), "utf8");
2357
2412
  } catch (err) {
2358
2413
  sinkFailed = true;
2359
2414
  const message = err instanceof Error ? err.message : String(err);
@@ -4792,6 +4847,207 @@ ${append}`;
4792
4847
  };
4793
4848
  }
4794
4849
 
4850
+ // src/utils/traceDecisions.ts
4851
+ var TRACED_KEY = "__outcomeTraced";
4852
+ function waitedMs(from, to) {
4853
+ if (!from || !to) return void 0;
4854
+ const ms = Date.parse(to) - Date.parse(from);
4855
+ return Number.isFinite(ms) && ms >= 0 ? ms : void 0;
4856
+ }
4857
+ function sameJson(a, b) {
4858
+ try {
4859
+ return JSON.stringify(a) === JSON.stringify(b);
4860
+ } catch {
4861
+ return false;
4862
+ }
4863
+ }
4864
+ function recordApprovalRequested(session, entry) {
4865
+ if (!session) return;
4866
+ const logData = session.resolvedConfig.logData;
4867
+ const sections = [
4868
+ {
4869
+ kind: "metadata",
4870
+ label: "Approval",
4871
+ data: {
4872
+ approvalId: entry.id,
4873
+ toolCallId: entry.toolCallId,
4874
+ status: "pending",
4875
+ requestedAt: entry.requestedAt,
4876
+ ...entry.metadata?.prompt !== void 0 ? { prompt: entry.metadata.prompt } : {}
4877
+ }
4878
+ }
4879
+ ];
4880
+ if (logData) {
4881
+ sections.push({ kind: "tool_call", label: "Arguments to approve", tool: entry.toolName, arguments: sanitizeTracePayload(entry.args) });
4882
+ }
4883
+ recordTraceEvent(session, {
4884
+ type: "tool_approval",
4885
+ label: `Approval requested \xB7 ${entry.toolName}`,
4886
+ timestamp: entry.requestedAt,
4887
+ status: "pending",
4888
+ actor: { scope: "agent", name: entry.toolName, role: "approval_request" },
4889
+ sections
4890
+ });
4891
+ }
4892
+ function recordHumanDecisions(session, state) {
4893
+ if (!session) return;
4894
+ const logData = session.resolvedConfig.logData;
4895
+ for (const entry of state.pendingApprovals ?? []) {
4896
+ if (!entry.decidedAt || entry.metadata?.[TRACED_KEY]) continue;
4897
+ if (entry.status !== "approved" && entry.status !== "rejected") continue;
4898
+ const approved = entry.status === "approved";
4899
+ const edited = approved && entry.approvedArgs !== void 0 && !sameJson(entry.approvedArgs, entry.args);
4900
+ const sections = [
4901
+ {
4902
+ kind: "metadata",
4903
+ label: "Decision",
4904
+ data: {
4905
+ approvalId: entry.id,
4906
+ toolCallId: entry.toolCallId,
4907
+ decision: entry.status,
4908
+ ...entry.decidedBy ? { decidedBy: entry.decidedBy } : {},
4909
+ ...entry.comment ? { comment: entry.comment } : {},
4910
+ requestedAt: entry.requestedAt,
4911
+ decidedAt: entry.decidedAt,
4912
+ ...waitedMs(entry.requestedAt, entry.decidedAt) !== void 0 ? { waitedMs: waitedMs(entry.requestedAt, entry.decidedAt) } : {},
4913
+ ...edited ? { argumentsEdited: true } : {}
4914
+ }
4915
+ }
4916
+ ];
4917
+ if (logData) {
4918
+ sections.push({ kind: "tool_call", label: "Requested arguments", tool: entry.toolName, arguments: sanitizeTracePayload(entry.args) });
4919
+ if (edited) {
4920
+ sections.push({ kind: "tool_call", label: "Approved arguments", tool: entry.toolName, arguments: sanitizeTracePayload(entry.approvedArgs) });
4921
+ }
4922
+ }
4923
+ recordTraceEvent(session, {
4924
+ type: "tool_approval",
4925
+ label: `${approved ? "Approved" : "Rejected"} \xB7 ${entry.toolName}`,
4926
+ timestamp: entry.decidedAt,
4927
+ durationMs: waitedMs(entry.requestedAt, entry.decidedAt),
4928
+ status: "success",
4929
+ actor: { scope: "human", name: entry.decidedBy || "reviewer", role: "approver" },
4930
+ sections
4931
+ });
4932
+ entry.metadata = { ...entry.metadata || {}, [TRACED_KEY]: true };
4933
+ }
4934
+ for (const entry of state.pendingUserQuestions ?? []) {
4935
+ if (!entry.answeredAt || entry.metadata?.[TRACED_KEY]) continue;
4936
+ if (entry.status !== "answered" && entry.status !== "cancelled") continue;
4937
+ const clientTool = readClientTool(entry);
4938
+ if (clientTool) {
4939
+ recordClientToolResult(session, entry, clientTool, state.toolHistory);
4940
+ entry.metadata = { ...entry.metadata || {}, [TRACED_KEY]: true };
4941
+ continue;
4942
+ }
4943
+ const cancelled = entry.status === "cancelled";
4944
+ const data = {
4945
+ questionId: entry.id,
4946
+ toolCallId: entry.toolCallId,
4947
+ status: entry.status,
4948
+ ...entry.answeredBy ? { answeredBy: entry.answeredBy } : {},
4949
+ ...entry.notes ? { notes: entry.notes } : {},
4950
+ requestedAt: entry.requestedAt,
4951
+ answeredAt: entry.answeredAt,
4952
+ ...waitedMs(entry.requestedAt, entry.answeredAt) !== void 0 ? { waitedMs: waitedMs(entry.requestedAt, entry.answeredAt) } : {}
4953
+ };
4954
+ const sections = [{ kind: "metadata", label: "Answer", data }];
4955
+ if (logData) {
4956
+ sections.push({
4957
+ kind: "metadata",
4958
+ label: "Questions and answers",
4959
+ data: {
4960
+ items: (entry.questions ?? []).map((item) => ({
4961
+ question: item.question,
4962
+ ...item.header ? { header: item.header } : {},
4963
+ answer: sanitizeTracePayload(entry.answers?.[item.question] ?? (item.header ? entry.answers?.[item.header] : void 0) ?? null)
4964
+ }))
4965
+ }
4966
+ });
4967
+ }
4968
+ recordTraceEvent(session, {
4969
+ type: "user_question",
4970
+ label: cancelled ? "Question cancelled" : "Answer from user",
4971
+ timestamp: entry.answeredAt,
4972
+ durationMs: waitedMs(entry.requestedAt, entry.answeredAt),
4973
+ status: cancelled ? "skipped" : "success",
4974
+ actor: { scope: "human", name: entry.answeredBy || "user", role: "user" },
4975
+ sections
4976
+ });
4977
+ entry.metadata = { ...entry.metadata || {}, [TRACED_KEY]: true };
4978
+ }
4979
+ }
4980
+ function recordClientToolRequested(session, entry) {
4981
+ if (!session) return;
4982
+ const clientTool = readClientTool(entry);
4983
+ if (!clientTool) return;
4984
+ const sections = [
4985
+ {
4986
+ kind: "metadata",
4987
+ label: "Client tool",
4988
+ data: { questionId: entry.id, toolCallId: entry.toolCallId, status: "pending", requestedAt: entry.requestedAt }
4989
+ }
4990
+ ];
4991
+ if (session.resolvedConfig.logData) {
4992
+ sections.push({ kind: "tool_call", label: "Arguments", tool: clientTool.name, arguments: sanitizeTracePayload(clientTool.args) });
4993
+ }
4994
+ recordTraceEvent(session, {
4995
+ type: "tool_call",
4996
+ label: `Tool Execution - ${clientTool.name} (client)`,
4997
+ timestamp: entry.requestedAt,
4998
+ status: "pending",
4999
+ actor: { scope: "client", name: clientTool.name, role: "tool" },
5000
+ sections
5001
+ });
5002
+ }
5003
+ function readClientTool(entry) {
5004
+ const meta = entry.metadata?.clientTool;
5005
+ if (!meta || typeof meta !== "object" || typeof meta.name !== "string") return void 0;
5006
+ return { name: meta.name, args: meta.args };
5007
+ }
5008
+ function recordClientToolResult(session, entry, clientTool, toolHistory) {
5009
+ const step = [...toolHistory ?? []].reverse().find((item) => item?.tool_call_id === entry.toolCallId);
5010
+ const failed = step?.status === "error" || entry.status === "cancelled";
5011
+ const sections = [
5012
+ {
5013
+ kind: "metadata",
5014
+ label: "Client tool",
5015
+ data: {
5016
+ questionId: entry.id,
5017
+ toolCallId: entry.toolCallId,
5018
+ status: failed ? "error" : "answered",
5019
+ ...entry.answeredBy ? { answeredBy: entry.answeredBy } : {},
5020
+ requestedAt: entry.requestedAt,
5021
+ answeredAt: entry.answeredAt,
5022
+ ...waitedMs(entry.requestedAt, entry.answeredAt) !== void 0 ? { waitedMs: waitedMs(entry.requestedAt, entry.answeredAt) } : {}
5023
+ }
5024
+ }
5025
+ ];
5026
+ if (session.resolvedConfig.logData) {
5027
+ sections.push({ kind: "tool_call", label: "Arguments", tool: clientTool.name, arguments: sanitizeTracePayload(clientTool.args) });
5028
+ if (step) {
5029
+ const output = step.rawOutput ?? step.output;
5030
+ sections.push({
5031
+ kind: "tool_result",
5032
+ label: "Result from client",
5033
+ tool: clientTool.name,
5034
+ output: sanitizeTracePayload(output),
5035
+ summary: typeof step.output === "string" ? step.output.slice(0, 2e3) : void 0
5036
+ });
5037
+ }
5038
+ }
5039
+ const ranMs = typeof step?.durationMs === "number" ? step.durationMs : void 0;
5040
+ recordTraceEvent(session, {
5041
+ type: "tool_call",
5042
+ label: `Tool Execution - ${clientTool.name} (client)`,
5043
+ timestamp: entry.answeredAt,
5044
+ durationMs: waitedMs(entry.requestedAt, entry.answeredAt) ?? ranMs,
5045
+ status: failed ? "error" : "success",
5046
+ actor: { scope: "client", name: clientTool.name, role: "tool" },
5047
+ sections
5048
+ });
5049
+ }
5050
+
4795
5051
  // src/utils/traceSections.ts
4796
5052
  init_content();
4797
5053
  function truncate(text, max = 500) {
@@ -5105,10 +5361,10 @@ function createToolsNode(initialTools, opts) {
5105
5361
  const toolCallId = tc.id || `${tc.name}_${toolCount + 1}`;
5106
5362
  const validatedArgs = validateToolArgs(tool, args);
5107
5363
  if (!validatedArgs.ok && resolved.toolResponses.schemaValidation === "strict") {
5108
- const errorMessage = `Tool argument validation failed for ${tc.name}: ${validatedArgs.message}`;
5109
- push({ role: "tool", content: errorMessage, tool_call_id: toolCallId, name: tc.name });
5110
- markToolFailure(errorMessage, tc.name, toolCallId);
5111
- onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args, error: { message: errorMessage } });
5364
+ const errorMessage2 = `Tool argument validation failed for ${tc.name}: ${validatedArgs.message}`;
5365
+ push({ role: "tool", content: errorMessage2, tool_call_id: toolCallId, name: tc.name });
5366
+ markToolFailure(errorMessage2, tc.name, toolCallId);
5367
+ onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args, error: { message: errorMessage2 } });
5112
5368
  toolCount += 1;
5113
5369
  return { status: "error" };
5114
5370
  }
@@ -5260,6 +5516,7 @@ function createToolsNode(initialTools, opts) {
5260
5516
  };
5261
5517
  pendingApprovals.push(approvalEntry);
5262
5518
  pendingByCallId.set(toolCallId, approvalEntry);
5519
+ recordApprovalRequested(traceSession, approvalEntry);
5263
5520
  onEvent?.({ type: "tool_approval", status: "pending", id: approvalEntry.id, toolName: approvalEntry.toolName, toolCallId: approvalEntry.toolCallId, args });
5264
5521
  if (host?.has("notification")) {
5265
5522
  await host.runObservers("notification", {
@@ -5523,6 +5780,7 @@ function createToolsNode(initialTools, opts) {
5523
5780
  if (!pendingApproval) {
5524
5781
  throw new Error(`Tool ${toolName} requested approval but no pending approval entry was recorded.`);
5525
5782
  }
5783
+ recordApprovalRequested(traceSession, pendingApproval);
5526
5784
  awaitingApproval = true;
5527
5785
  return {
5528
5786
  status: "awaiting_approval",
@@ -5539,6 +5797,7 @@ function createToolsNode(initialTools, opts) {
5539
5797
  if (!pendingQuestion) {
5540
5798
  throw new Error(`Tool ${toolName} requested a user question but no pending question entry was recorded.`);
5541
5799
  }
5800
+ recordClientToolRequested(traceSession, pendingQuestion);
5542
5801
  awaitingUserQuestion = true;
5543
5802
  if (host?.has("notification")) {
5544
5803
  await host.runObservers("notification", {
@@ -6119,7 +6378,7 @@ var LEVEL_DEFAULTS = {
6119
6378
  };
6120
6379
  var VALID_LEVELS = ["minimal", "low", "medium", "high"];
6121
6380
  var VALID_CADENCES = ["off", "every_turn", "after_tool", "on_branch", "initial_then_after_tool"];
6122
- var VALID_EFFORTS = ["none", "minimal", "low", "medium", "high"];
6381
+ var VALID_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
6123
6382
  function validateReasoningConfig(input) {
6124
6383
  if (!input) return;
6125
6384
  if (input.level !== void 0 && !VALID_LEVELS.includes(input.level)) {
@@ -6635,16 +6894,20 @@ function createAskUserQuestionTool(stateRef, config) {
6635
6894
  });
6636
6895
  recordTraceEvent(ctx.__traceSession, {
6637
6896
  type: "user_question",
6638
- label: "Ask User",
6897
+ label: "Question to user",
6898
+ timestamp: entry.requestedAt,
6639
6899
  actor: { scope: "agent", name: "ask_user_question", role: "user_interaction" },
6640
- status: "success",
6900
+ status: "pending",
6641
6901
  sections: [
6642
6902
  {
6643
6903
  kind: "metadata",
6644
6904
  label: "Pending Question",
6645
6905
  data: {
6646
6906
  id: entry.id,
6907
+ questionId: entry.id,
6647
6908
  toolCallId: entry.toolCallId,
6909
+ status: "pending",
6910
+ requestedAt: entry.requestedAt,
6648
6911
  questions: entry.questions,
6649
6912
  allowFreeText
6650
6913
  }
@@ -7863,15 +8126,17 @@ function createAgent(opts) {
7863
8126
  }
7864
8127
  const traceSession = state.ctx?.__traceSession;
7865
8128
  if (traceSession) {
8129
+ const iterationNumber = (traceSession.summary.eventCounts.agent_iteration ?? 0) + 1;
7866
8130
  const iterEvent = recordTraceEvent(traceSession, {
7867
8131
  type: "agent_iteration",
7868
- label: `Iteration ${iterations}`,
8132
+ label: `Iteration ${iterationNumber}`,
7869
8133
  actor: { scope: "agent", name: opts.name || "agent", role: "orchestrator" },
7870
8134
  parentSpanId: traceSession.rootSpanId
7871
8135
  });
7872
8136
  if (iterEvent) {
7873
8137
  traceSession.currentIterationSpanId = iterEvent.spanId;
7874
8138
  }
8139
+ recordHumanDecisions(traceSession, state);
7875
8140
  }
7876
8141
  if (cancelIfRequested("loop")) break;
7877
8142
  const skippingAgent = resumeStage === "tools";
@@ -8152,7 +8417,7 @@ function createAgent(opts) {
8152
8417
  }
8153
8418
  emit({ type: "progress", ...progress });
8154
8419
  };
8155
- const traceSession = createTraceSession(opts);
8420
+ const traceSession = createTraceSession(opts, readTraceContinuation(input.ctx?.__traceContinuation));
8156
8421
  const ctx = {
8157
8422
  ...input.ctx || {},
8158
8423
  __onEvent: onEvent,
@@ -8160,6 +8425,7 @@ function createAgent(opts) {
8160
8425
  __onStream: onStream,
8161
8426
  __streaming: streamEnabled
8162
8427
  };
8428
+ delete ctx.__traceContinuation;
8163
8429
  if (traceSession) ctx.__traceSession = traceSession;
8164
8430
  delete ctx.__plugins;
8165
8431
  delete ctx.__applySystemPromptContribution;
@@ -8308,10 +8574,17 @@ function createAgent(opts) {
8308
8574
  }
8309
8575
  }
8310
8576
  }
8577
+ const pausedForHuman = statusFor(res) === "paused";
8578
+ const midTurn = pausedForHuman || Boolean(res.ctx?.__needsSummarization);
8311
8579
  await finalizeTraceSession(traceSession, {
8312
8580
  agentRuntime: res.agent || runtime,
8313
- status: "success"
8581
+ status: pausedForHuman ? "paused" : midTurn ? "in_progress" : "success"
8314
8582
  });
8583
+ if (traceSession && midTurn) {
8584
+ res.ctx = { ...res.ctx || {}, __traceContinuation: traceContinuationOf(traceSession) };
8585
+ } else if (res.ctx && "__traceContinuation" in res.ctx) {
8586
+ delete res.ctx.__traceContinuation;
8587
+ }
8315
8588
  const isTransientSummarizationExit = !!res.ctx?.__needsSummarization;
8316
8589
  const finalAssistantMsg = isTransientSummarizationExit ? void 0 : getLastAssistantMessage(res.messages);
8317
8590
  const finalIsSyntheticSummary = !isTransientSummarizationExit && finalAssistantMsg && isSyntheticSummaryMessage(finalAssistantMsg);
@@ -11215,6 +11488,14 @@ ${pluginAppend}`;
11215
11488
  } else if (lastResult.state) {
11216
11489
  lastResult = { ...lastResult, state: { ...lastResult.state, memoryFacts: state.memoryFacts, plan: state.plan, planVersion: state.planVersion, messages: rawMessages } };
11217
11490
  }
11491
+ if (lastResult.state) {
11492
+ const endCtx = lastResult.state.ctx;
11493
+ if (endCtx?.__traceContinuation && !(endCtx.__awaitingApproval || endCtx.__awaitingUserQuestion || endCtx.__paused)) {
11494
+ const strippedCtx = { ...endCtx };
11495
+ delete strippedCtx.__traceContinuation;
11496
+ lastResult = { ...lastResult, state: { ...lastResult.state, ctx: strippedCtx } };
11497
+ }
11498
+ }
11218
11499
  if (skillsEnabled && lastResult.state) {
11219
11500
  const persisted = skillStateFromRegistry(toolSet.skillRegistryRef);
11220
11501
  if (persisted) {
@@ -11589,6 +11870,7 @@ var BaseProvider = class {
11589
11870
  /** Applies the transport options shared by every provider config. */
11590
11871
  configureTransport(config) {
11591
11872
  this.fetchImpl = config.fetch;
11873
+ this.onModelRuleLearned = config.onModelRuleLearned;
11592
11874
  this.timeoutMs = typeof config.timeoutMs === "number" && config.timeoutMs > 0 ? config.timeoutMs : void 0;
11593
11875
  }
11594
11876
  /**
@@ -15256,32 +15538,805 @@ function languageGuard(config) {
15256
15538
  // src/index.ts
15257
15539
  init_content();
15258
15540
 
15541
+ // src/models/model-rules.json
15542
+ var model_rules_default = {
15543
+ version: 1,
15544
+ effortLadder: [
15545
+ "none",
15546
+ "minimal",
15547
+ "low",
15548
+ "medium",
15549
+ "high",
15550
+ "xhigh",
15551
+ "max"
15552
+ ],
15553
+ budgets: {
15554
+ minimal: 1024,
15555
+ low: 2048,
15556
+ medium: 8192,
15557
+ high: 16384,
15558
+ xhigh: 24576,
15559
+ max: 32768
15560
+ },
15561
+ geminiBudgets: {
15562
+ minimal: 512,
15563
+ low: 1024,
15564
+ medium: 4096,
15565
+ high: 12e3,
15566
+ xhigh: 24576,
15567
+ max: 24576
15568
+ },
15569
+ rules: [
15570
+ {
15571
+ id: "openai-chat-models",
15572
+ family: "openai",
15573
+ match: "^(?:chatgpt|gpt-[\\d.]+[a-z0-9.-]*?-chat)",
15574
+ reasoning: {
15575
+ style: "none",
15576
+ efforts: [],
15577
+ off: null
15578
+ },
15579
+ sampling: {
15580
+ temperature: "any",
15581
+ topP: true,
15582
+ topK: true,
15583
+ penalties: true,
15584
+ logprobs: true
15585
+ },
15586
+ maxTokensParam: "max_completion_tokens",
15587
+ chatTools: {
15588
+ reasoning: "any"
15589
+ },
15590
+ source: "docs",
15591
+ note: "Chat-tuned *-chat variants are not reasoning models: ordinary sampling, no reasoning field."
15592
+ },
15593
+ {
15594
+ id: "openai-gpt-6-plus",
15595
+ family: "openai",
15596
+ match: "^gpt-?(?:[6-9]|(?!35(?!\\d))[1-9]\\d+)(?:[.-]|$)",
15597
+ reasoning: {
15598
+ style: "effort",
15599
+ efforts: [
15600
+ "none",
15601
+ "low",
15602
+ "medium",
15603
+ "high",
15604
+ "xhigh"
15605
+ ],
15606
+ off: "none",
15607
+ responsesEfforts: [
15608
+ "none",
15609
+ "low",
15610
+ "medium",
15611
+ "high",
15612
+ "xhigh",
15613
+ "max"
15614
+ ]
15615
+ },
15616
+ sampling: {
15617
+ temperature: "default-only",
15618
+ topP: false,
15619
+ topK: false,
15620
+ penalties: false,
15621
+ logprobs: false
15622
+ },
15623
+ maxTokensParam: "max_completion_tokens",
15624
+ chatTools: {
15625
+ reasoning: "force-none"
15626
+ },
15627
+ source: "verified-2026-10-14",
15628
+ note: "gpt-N, N>=6. Chat Completions takes none/low/medium/high/xhigh (minimal and max are 400s); Responses also takes max. Function tools with an effort other than none are a 400 on Chat Completions and the default effort is not none, so none is sent explicitly. Rejects max_tokens, temperature != 1, top_p, penalties, logprobs."
15629
+ },
15630
+ {
15631
+ id: "openai-gpt-5x",
15632
+ family: "openai",
15633
+ match: "^gpt-?5\\.[1-9]",
15634
+ reasoning: {
15635
+ style: "effort",
15636
+ efforts: [
15637
+ "none",
15638
+ "low",
15639
+ "medium",
15640
+ "high",
15641
+ "xhigh"
15642
+ ],
15643
+ off: "none",
15644
+ responsesEfforts: [
15645
+ "none",
15646
+ "low",
15647
+ "medium",
15648
+ "high",
15649
+ "xhigh",
15650
+ "max"
15651
+ ]
15652
+ },
15653
+ sampling: {
15654
+ temperature: "default-only",
15655
+ topP: false,
15656
+ topK: false,
15657
+ penalties: false,
15658
+ logprobs: false
15659
+ },
15660
+ maxTokensParam: "max_completion_tokens",
15661
+ chatTools: {
15662
+ reasoning: "force-none"
15663
+ },
15664
+ source: "verified-2026-10-14",
15665
+ note: "gpt-5.1 and newer 5.x. Same wire rules as gpt-6: no minimal/max on Chat Completions (Responses takes max), function tools need reasoning_effort none on Chat Completions."
15666
+ },
15667
+ {
15668
+ id: "openai-gpt-5-base",
15669
+ family: "openai",
15670
+ match: "^gpt-?5(?![.\\d])",
15671
+ reasoning: {
15672
+ style: "effort",
15673
+ efforts: [
15674
+ "minimal",
15675
+ "low",
15676
+ "medium",
15677
+ "high"
15678
+ ],
15679
+ off: null
15680
+ },
15681
+ sampling: {
15682
+ temperature: "default-only",
15683
+ topP: false,
15684
+ topK: false,
15685
+ penalties: false,
15686
+ logprobs: false
15687
+ },
15688
+ maxTokensParam: "max_completion_tokens",
15689
+ chatTools: {
15690
+ reasoning: "any"
15691
+ },
15692
+ source: "docs",
15693
+ note: "gpt-5, -mini, -nano, -codex: minimal/low/medium/high, reasoning cannot be turned off (none maps to minimal)."
15694
+ },
15695
+ {
15696
+ id: "openai-o-series",
15697
+ family: "openai",
15698
+ match: "^o[1-9]\\d?(?![a-z0-9])",
15699
+ reasoning: {
15700
+ style: "effort",
15701
+ efforts: [
15702
+ "low",
15703
+ "medium",
15704
+ "high"
15705
+ ],
15706
+ off: null
15707
+ },
15708
+ sampling: {
15709
+ temperature: "default-only",
15710
+ topP: false,
15711
+ topK: false,
15712
+ penalties: false,
15713
+ logprobs: false
15714
+ },
15715
+ maxTokensParam: "max_completion_tokens",
15716
+ chatTools: {
15717
+ reasoning: "any"
15718
+ },
15719
+ source: "docs",
15720
+ note: "o1/o3/o4 family: low/medium/high, no sampling overrides."
15721
+ },
15722
+ {
15723
+ id: "openai-gpt-oss",
15724
+ family: "openai",
15725
+ match: "^gpt-?oss",
15726
+ reasoning: {
15727
+ style: "effort",
15728
+ efforts: [
15729
+ "low",
15730
+ "medium",
15731
+ "high"
15732
+ ],
15733
+ off: null
15734
+ },
15735
+ sampling: {
15736
+ temperature: "any",
15737
+ topP: true,
15738
+ topK: true,
15739
+ penalties: true,
15740
+ logprobs: true
15741
+ },
15742
+ maxTokensParam: "max_tokens",
15743
+ chatTools: {
15744
+ reasoning: "any"
15745
+ },
15746
+ source: "docs",
15747
+ note: "Open-weight gpt-oss: low/medium/high; sampling and max_tokens behave like an ordinary chat model."
15748
+ },
15749
+ {
15750
+ id: "openai-legacy-gpt",
15751
+ family: "openai",
15752
+ match: "^(?:gpt-?(?:[0-4]|35)(?![0-9])|text-|davinci|babbage)",
15753
+ reasoning: {
15754
+ style: "none",
15755
+ efforts: [],
15756
+ off: null
15757
+ },
15758
+ sampling: {
15759
+ temperature: "any",
15760
+ topP: true,
15761
+ topK: true,
15762
+ penalties: true,
15763
+ logprobs: true
15764
+ },
15765
+ maxTokensParam: "max_tokens",
15766
+ chatTools: {
15767
+ reasoning: "any"
15768
+ },
15769
+ source: "docs",
15770
+ note: "gpt-4o, gpt-4.1, gpt-3.5 and older: no reasoning, ordinary sampling."
15771
+ },
15772
+ {
15773
+ id: "openai-compatible-toggle",
15774
+ family: "openai",
15775
+ match: "^(?:deepseek-(?:r\\d|reasoner)|qwq|qwen-?3)",
15776
+ reasoning: {
15777
+ style: "toggle",
15778
+ efforts: [
15779
+ "none",
15780
+ "minimal",
15781
+ "low",
15782
+ "medium",
15783
+ "high",
15784
+ "xhigh",
15785
+ "max"
15786
+ ],
15787
+ off: "none"
15788
+ },
15789
+ sampling: {
15790
+ temperature: "any",
15791
+ topP: true,
15792
+ topK: true,
15793
+ penalties: true,
15794
+ logprobs: true
15795
+ },
15796
+ maxTokensParam: "max_tokens",
15797
+ chatTools: {
15798
+ reasoning: "any"
15799
+ },
15800
+ source: "docs",
15801
+ note: "DeepSeek R/reasoner, QwQ, Qwen3: on/off style hosts. The effort is forwarded as-is (including none, which switches thinking off on thinking-by-default models)."
15802
+ },
15803
+ {
15804
+ id: "openai-default",
15805
+ family: "openai",
15806
+ match: ".*",
15807
+ reasoning: {
15808
+ style: "effort",
15809
+ efforts: [
15810
+ "none",
15811
+ "minimal",
15812
+ "low",
15813
+ "medium",
15814
+ "high",
15815
+ "xhigh",
15816
+ "max"
15817
+ ],
15818
+ off: "none"
15819
+ },
15820
+ sampling: {
15821
+ temperature: "any",
15822
+ topP: true,
15823
+ topK: true,
15824
+ penalties: true,
15825
+ logprobs: true
15826
+ },
15827
+ maxTokensParam: "max_tokens",
15828
+ chatTools: {
15829
+ reasoning: "any"
15830
+ },
15831
+ source: "docs",
15832
+ note: "Permissive default for unknown OpenAI-wire models: pass what the caller asked; the provider learns from 400s."
15833
+ },
15834
+ {
15835
+ id: "claude-haiku-5",
15836
+ family: "anthropic",
15837
+ match: "^claude-(?:haiku-5|5(?:-\\d+)?-haiku)(?!\\d)",
15838
+ reasoning: {
15839
+ style: "adaptive",
15840
+ efforts: [
15841
+ "low",
15842
+ "medium",
15843
+ "high",
15844
+ "xhigh",
15845
+ "max"
15846
+ ],
15847
+ off: "disabled"
15848
+ },
15849
+ sampling: {
15850
+ temperature: "unsupported",
15851
+ topP: false,
15852
+ topK: false,
15853
+ penalties: false,
15854
+ logprobs: false
15855
+ },
15856
+ maxTokensParam: "max_tokens",
15857
+ chatTools: {
15858
+ reasoning: "any"
15859
+ },
15860
+ source: "verified-2026-10-14",
15861
+ note: "Claude Haiku 5.x: thinking:{type:adaptive} + output_config.effort; thinking:{type:enabled,budget_tokens} is rejected. Off = thinking:{type:disabled}. Sampling params are rejected."
15862
+ },
15863
+ {
15864
+ id: "claude-sonnet-5",
15865
+ family: "anthropic",
15866
+ match: "^claude-(?:sonnet-5|5(?:-\\d+)?-sonnet)(?!\\d)",
15867
+ reasoning: {
15868
+ style: "adaptive",
15869
+ efforts: [
15870
+ "low",
15871
+ "medium",
15872
+ "high",
15873
+ "xhigh",
15874
+ "max"
15875
+ ],
15876
+ off: "between_tools"
15877
+ },
15878
+ sampling: {
15879
+ temperature: "unsupported",
15880
+ topP: false,
15881
+ topK: false,
15882
+ penalties: false,
15883
+ logprobs: false
15884
+ },
15885
+ maxTokensParam: "max_tokens",
15886
+ chatTools: {
15887
+ reasoning: "any"
15888
+ },
15889
+ source: "verified-2026-10-14",
15890
+ note: "Claude Sonnet 5.x: adaptive + output_config.effort. thinking:{type:disabled} is rejected; off = thinking:{type:between_tools}. Sampling params are rejected."
15891
+ },
15892
+ {
15893
+ id: "claude-opus-5",
15894
+ family: "anthropic",
15895
+ match: "^claude-(?:opus-5|5(?:-\\d+)?-opus)(?!\\d)",
15896
+ reasoning: {
15897
+ style: "adaptive",
15898
+ efforts: [
15899
+ "low",
15900
+ "medium",
15901
+ "high",
15902
+ "xhigh",
15903
+ "max"
15904
+ ],
15905
+ off: null
15906
+ },
15907
+ sampling: {
15908
+ temperature: "unsupported",
15909
+ topP: false,
15910
+ topK: false,
15911
+ penalties: false,
15912
+ logprobs: false
15913
+ },
15914
+ maxTokensParam: "max_tokens",
15915
+ chatTools: {
15916
+ reasoning: "any"
15917
+ },
15918
+ source: "verified-2026-10-14",
15919
+ note: "Claude Opus 5.x: adaptive + output_config.effort; thinking cannot be turned off (none maps to the lowest effort). Sampling params are rejected."
15920
+ },
15921
+ {
15922
+ id: "claude-fable",
15923
+ family: "anthropic",
15924
+ match: "^claude-fable",
15925
+ reasoning: {
15926
+ style: "adaptive",
15927
+ efforts: [
15928
+ "low",
15929
+ "medium",
15930
+ "high",
15931
+ "xhigh",
15932
+ "max"
15933
+ ],
15934
+ off: null
15935
+ },
15936
+ sampling: {
15937
+ temperature: "unsupported",
15938
+ topP: false,
15939
+ topK: false,
15940
+ penalties: false,
15941
+ logprobs: false
15942
+ },
15943
+ maxTokensParam: "max_tokens",
15944
+ chatTools: {
15945
+ reasoning: "any"
15946
+ },
15947
+ source: "docs",
15948
+ note: "Claude Fable line: treated like the 5.x adaptive models."
15949
+ },
15950
+ {
15951
+ id: "claude-4-7",
15952
+ family: "anthropic",
15953
+ match: "^claude-(?:(?:opus|sonnet|haiku)-4[-.]7|4[-.]7-(?:opus|sonnet|haiku))(?!\\d)",
15954
+ reasoning: {
15955
+ style: "adaptive",
15956
+ efforts: [
15957
+ "low",
15958
+ "medium",
15959
+ "high",
15960
+ "xhigh",
15961
+ "max"
15962
+ ],
15963
+ off: "disabled"
15964
+ },
15965
+ sampling: {
15966
+ temperature: "thinking-requires-1",
15967
+ topP: true,
15968
+ topK: true,
15969
+ penalties: true,
15970
+ logprobs: true
15971
+ },
15972
+ maxTokensParam: "max_tokens",
15973
+ chatTools: {
15974
+ reasoning: "any"
15975
+ },
15976
+ source: "docs",
15977
+ note: "Claude 4.7: adaptive thinking, low/medium/high/xhigh/max."
15978
+ },
15979
+ {
15980
+ id: "claude-4-6",
15981
+ family: "anthropic",
15982
+ match: "^claude-(?:(?:opus|sonnet|haiku)-4[-.]6|4[-.]6-(?:opus|sonnet|haiku))(?!\\d)",
15983
+ reasoning: {
15984
+ style: "adaptive",
15985
+ efforts: [
15986
+ "low",
15987
+ "medium",
15988
+ "high",
15989
+ "max"
15990
+ ],
15991
+ off: "disabled"
15992
+ },
15993
+ sampling: {
15994
+ temperature: "thinking-requires-1",
15995
+ topP: true,
15996
+ topK: true,
15997
+ penalties: true,
15998
+ logprobs: true
15999
+ },
16000
+ maxTokensParam: "max_tokens",
16001
+ chatTools: {
16002
+ reasoning: "any"
16003
+ },
16004
+ source: "docs",
16005
+ note: "Claude 4.6: adaptive thinking, low/medium/high/max (no xhigh)."
16006
+ },
16007
+ {
16008
+ id: "claude-legacy-budget",
16009
+ family: "anthropic",
16010
+ match: "^claude-(?:3|4-|(?:opus|sonnet|haiku)-4)",
16011
+ reasoning: {
16012
+ style: "budget",
16013
+ efforts: [
16014
+ "none",
16015
+ "minimal",
16016
+ "low",
16017
+ "medium",
16018
+ "high",
16019
+ "xhigh",
16020
+ "max"
16021
+ ],
16022
+ off: null
16023
+ },
16024
+ sampling: {
16025
+ temperature: "thinking-requires-1",
16026
+ topP: true,
16027
+ topK: true,
16028
+ penalties: true,
16029
+ logprobs: true
16030
+ },
16031
+ maxTokensParam: "max_tokens",
16032
+ chatTools: {
16033
+ reasoning: "any"
16034
+ },
16035
+ source: "docs",
16036
+ note: "Claude 3.7 and 4.0-4.5: thinking:{type:enabled,budget_tokens}; every effort maps to a budget; sampling overrides are not allowed while thinking."
16037
+ },
16038
+ {
16039
+ id: "anthropic-default",
16040
+ family: "anthropic",
16041
+ match: ".*",
16042
+ reasoning: {
16043
+ style: "budget",
16044
+ efforts: [
16045
+ "none",
16046
+ "minimal",
16047
+ "low",
16048
+ "medium",
16049
+ "high",
16050
+ "xhigh",
16051
+ "max"
16052
+ ],
16053
+ off: null
16054
+ },
16055
+ sampling: {
16056
+ temperature: "thinking-requires-1",
16057
+ topP: true,
16058
+ topK: true,
16059
+ penalties: true,
16060
+ logprobs: true
16061
+ },
16062
+ maxTokensParam: "max_tokens",
16063
+ chatTools: {
16064
+ reasoning: "any"
16065
+ },
16066
+ source: "docs",
16067
+ note: "Permissive default for unknown Anthropic-wire models: budget style (the long-standing form); the provider flips the thinking form when the API says so."
16068
+ },
16069
+ {
16070
+ id: "gemini-budget",
16071
+ family: "gemini",
16072
+ match: "^gemini",
16073
+ reasoning: {
16074
+ style: "budget",
16075
+ efforts: [
16076
+ "none",
16077
+ "minimal",
16078
+ "low",
16079
+ "medium",
16080
+ "high",
16081
+ "xhigh",
16082
+ "max"
16083
+ ],
16084
+ off: "none"
16085
+ },
16086
+ sampling: {
16087
+ temperature: "any",
16088
+ topP: true,
16089
+ topK: true,
16090
+ penalties: true,
16091
+ logprobs: true
16092
+ },
16093
+ maxTokensParam: "max_tokens",
16094
+ chatTools: {
16095
+ reasoning: "any"
16096
+ },
16097
+ source: "docs",
16098
+ note: "Gemini 2.5-style thinkingBudget; effort maps to a token budget, none = budget 0."
16099
+ },
16100
+ {
16101
+ id: "gemini-default",
16102
+ family: "gemini",
16103
+ match: ".*",
16104
+ reasoning: {
16105
+ style: "budget",
16106
+ efforts: [
16107
+ "none",
16108
+ "minimal",
16109
+ "low",
16110
+ "medium",
16111
+ "high",
16112
+ "xhigh",
16113
+ "max"
16114
+ ],
16115
+ off: "none"
16116
+ },
16117
+ sampling: {
16118
+ temperature: "any",
16119
+ topP: true,
16120
+ topK: true,
16121
+ penalties: true,
16122
+ logprobs: true
16123
+ },
16124
+ maxTokensParam: "max_tokens",
16125
+ chatTools: {
16126
+ reasoning: "any"
16127
+ },
16128
+ source: "docs",
16129
+ note: "Permissive default for unknown Gemini-wire models."
16130
+ },
16131
+ {
16132
+ id: "other-default",
16133
+ family: "other",
16134
+ match: ".*",
16135
+ reasoning: {
16136
+ style: "effort",
16137
+ efforts: [
16138
+ "none",
16139
+ "minimal",
16140
+ "low",
16141
+ "medium",
16142
+ "high",
16143
+ "xhigh",
16144
+ "max"
16145
+ ],
16146
+ off: "none"
16147
+ },
16148
+ sampling: {
16149
+ temperature: "any",
16150
+ topP: true,
16151
+ topK: true,
16152
+ penalties: true,
16153
+ logprobs: true
16154
+ },
16155
+ maxTokensParam: "max_tokens",
16156
+ chatTools: {
16157
+ reasoning: "any"
16158
+ },
16159
+ source: "docs",
16160
+ note: "Permissive default for model families the SDK has no rules for."
16161
+ }
16162
+ ]
16163
+ };
16164
+
16165
+ // src/models/rules.ts
16166
+ var MODEL_RULES = model_rules_default;
16167
+ var LADDER = MODEL_RULES.effortLadder;
16168
+ var compiled = /* @__PURE__ */ new Map();
16169
+ function regexFor(rule) {
16170
+ let re = compiled.get(rule.id);
16171
+ if (!re) {
16172
+ re = new RegExp(rule.match, "i");
16173
+ compiled.set(rule.id, re);
16174
+ }
16175
+ return re;
16176
+ }
16177
+ var GATEWAY_PREFIX = /^(?:(?:global|us|eu|apac|jp|au|ca|us-gov)\.)?(?:(?:openai|anthropic|google|meta|amazon|deepseek|qwen|mistral|cohere|ai21)\.)?/;
16178
+ function modelIdCandidates(model) {
16179
+ if (!model) return [];
16180
+ const lower = String(model).trim().toLowerCase();
16181
+ const last = lower.split("/").pop() ?? lower;
16182
+ const out = [];
16183
+ for (const id of [lower, last]) {
16184
+ for (const c of [id, id.replace(GATEWAY_PREFIX, "")]) {
16185
+ if (c && !out.includes(c)) out.push(c);
16186
+ }
16187
+ }
16188
+ return out;
16189
+ }
16190
+ var OPENAI_WIRE = /* @__PURE__ */ new Set(["openai", "azure", "openai-compatible", "together", "openrouter", "groq", "fireworks", "ollama", "vllm", "litellm", "mistral", "deepseek", "xai", "jev"]);
16191
+ function familiesFor(provider) {
16192
+ const p = (provider ?? "").toLowerCase();
16193
+ if (OPENAI_WIRE.has(p)) return ["openai"];
16194
+ if (p === "anthropic") return ["anthropic"];
16195
+ if (p === "bedrock") return ["anthropic", "openai", "other"];
16196
+ if (p === "vertex" || p === "gemini" || p === "google") return ["gemini", "anthropic"];
16197
+ return ["openai", "anthropic", "gemini", "other"];
16198
+ }
16199
+ function matchRule(family, ids, allowDefault) {
16200
+ for (const rule of MODEL_RULES.rules) {
16201
+ if (rule.family !== family) continue;
16202
+ if (rule.match === ".*") {
16203
+ if (allowDefault) return rule;
16204
+ continue;
16205
+ }
16206
+ const re = regexFor(rule);
16207
+ if (ids.some((id) => re.test(id))) return rule;
16208
+ }
16209
+ return void 0;
16210
+ }
16211
+ function mergeRulePatch(base, patch) {
16212
+ if (!patch) return base;
16213
+ const out = { ...base };
16214
+ if (patch.reasoning) out.reasoning = { ...base.reasoning ?? {}, ...patch.reasoning };
16215
+ if (patch.sampling) out.sampling = { ...base.sampling ?? {}, ...patch.sampling };
16216
+ if (patch.chatTools) out.chatTools = { ...base.chatTools ?? {}, ...patch.chatTools };
16217
+ if (patch.maxTokensParam) out.maxTokensParam = patch.maxTokensParam;
16218
+ if (patch.dropParams) out.dropParams = [.../* @__PURE__ */ new Set([...base.dropParams ?? [], ...patch.dropParams])];
16219
+ return out;
16220
+ }
16221
+ var LEARNED_MAX = 500;
16222
+ var learned = /* @__PURE__ */ new Map();
16223
+ function learnedKey(provider, model, scope) {
16224
+ return `${(provider ?? "").toLowerCase()}@${scope ?? ""}::${(model ?? "").trim().toLowerCase()}`;
16225
+ }
16226
+ function rememberModelRulePatch(provider, model, patch, scope) {
16227
+ const key = learnedKey(provider, model, scope);
16228
+ const merged = mergeRulePatch(learned.get(key)?.patch ?? {}, patch);
16229
+ learned.delete(key);
16230
+ learned.set(key, { provider: provider.toLowerCase(), model: model.trim().toLowerCase(), scope, patch: merged });
16231
+ if (learned.size > LEARNED_MAX) learned.delete(learned.keys().next().value);
16232
+ }
16233
+ function getLearnedModelRulePatch(provider, model, scope) {
16234
+ return learned.get(learnedKey(provider, model, scope))?.patch;
16235
+ }
16236
+ function listLearnedModelRules() {
16237
+ return [...learned.values()].map((e) => ({ ...e }));
16238
+ }
16239
+ function clearLearnedModelRules() {
16240
+ learned.clear();
16241
+ }
16242
+ function resolveModelRule(opts) {
16243
+ const families = familiesFor(opts.provider);
16244
+ const ids = modelIdCandidates(opts.model);
16245
+ let rule;
16246
+ for (const family of families) {
16247
+ rule = matchRule(family, ids, false);
16248
+ if (rule) break;
16249
+ }
16250
+ let isDefault = false;
16251
+ if (!rule) {
16252
+ isDefault = true;
16253
+ rule = matchRule(families[0], ids, true) ?? MODEL_RULES.rules[MODEL_RULES.rules.length - 1];
16254
+ }
16255
+ let effective = rule;
16256
+ const known = opts.model ? learned.get(learnedKey(opts.provider, opts.model, opts.scope))?.patch : void 0;
16257
+ if (known) effective = mergeRulePatch(effective, known);
16258
+ if (opts.patch) effective = mergeRulePatch(effective, opts.patch);
16259
+ return { ...effective, matchedRuleId: rule.id, isDefault, learned: !!known };
16260
+ }
16261
+ function mapReasoningEffort(rule, effort, endpoint = "chat") {
16262
+ const efforts = endpoint === "responses" && rule.reasoning.responsesEfforts || rule.reasoning.efforts;
16263
+ if (rule.reasoning.style === "none" || efforts.length === 0) return { mapped: true };
16264
+ const supported = LADDER.filter((e) => efforts.includes(e));
16265
+ if (supported.includes(effort)) return { effort, mapped: false };
16266
+ if (effort === "none") {
16267
+ const off = rule.reasoning.off;
16268
+ if (off && off !== "none") return { off, mapped: true };
16269
+ const lowest = supported.find((e) => e !== "none");
16270
+ return lowest ? { effort: lowest, mapped: true } : { mapped: true };
16271
+ }
16272
+ const at = LADDER.indexOf(effort);
16273
+ if (at < 0) return { mapped: true };
16274
+ for (let i = at - 1; i >= 1; i--) if (supported.includes(LADDER[i])) return { effort: LADDER[i], mapped: true };
16275
+ for (let i = at + 1; i < LADDER.length; i++) if (supported.includes(LADDER[i])) return { effort: LADDER[i], mapped: true };
16276
+ return supported.includes("none") ? { effort: "none", mapped: true } : { mapped: true };
16277
+ }
16278
+ function allowsSampling(rule, field, value, thinkingOn = false) {
16279
+ const s = rule.sampling;
16280
+ switch (field) {
16281
+ case "temperature":
16282
+ switch (s.temperature) {
16283
+ case "any":
16284
+ return true;
16285
+ case "unsupported":
16286
+ return false;
16287
+ // The default is what the server applies anyway, so it is never sent.
16288
+ case "default-only":
16289
+ return false;
16290
+ case "thinking-requires-1":
16291
+ return !thinkingOn || value === 1;
16292
+ }
16293
+ return true;
16294
+ // Anthropic's thinking restriction covers top_p / top_k as well as temperature.
16295
+ case "topP":
16296
+ return s.topP && !(thinkingOn && s.temperature === "thinking-requires-1");
16297
+ case "topK":
16298
+ return s.topK && !(thinkingOn && s.temperature === "thinking-requires-1");
16299
+ case "presencePenalty":
16300
+ case "frequencyPenalty":
16301
+ return s.penalties;
16302
+ case "logprobs":
16303
+ return s.logprobs;
16304
+ }
16305
+ }
16306
+ function applyDropParams(body, rule) {
16307
+ for (const p of rule.dropParams ?? []) {
16308
+ const dot = p.indexOf(".");
16309
+ if (dot < 0) delete body[p];
16310
+ else if (body[p.slice(0, dot)] && typeof body[p.slice(0, dot)] === "object") delete body[p.slice(0, dot)][p.slice(dot + 1)];
16311
+ }
16312
+ }
16313
+ function effortBudget(effort, table = "anthropic") {
16314
+ if (effort === "none") return 0;
16315
+ return (table === "gemini" ? MODEL_RULES.geminiBudgets : MODEL_RULES.budgets)[effort] ?? 0;
16316
+ }
16317
+
15259
16318
  // src/providers/utils/reasoning.ts
15260
16319
  var ANTHROPIC_EFFORT_BUDGET = {
15261
16320
  // 0 = off. `applyAnthropicReasoning` bails on a falsy budget and emits no
15262
16321
  // thinking block, which on Anthropic IS off — thinking there is opt-in.
15263
16322
  none: 0,
15264
- minimal: 1024,
15265
- low: 2048,
15266
- medium: 8192,
15267
- high: 16384
16323
+ ...MODEL_RULES.budgets
15268
16324
  };
15269
16325
  var GEMINI_EFFORT_BUDGET = {
15270
16326
  // 0 is Gemini's own documented spelling for "do not think", and unlike
15271
16327
  // Anthropic it has to be SENT — hence `applyGeminiReasoning` testing for null
15272
16328
  // rather than falsiness.
15273
16329
  none: 0,
15274
- minimal: 512,
15275
- low: 1024,
15276
- medium: 4096,
15277
- high: 12e3
16330
+ ...MODEL_RULES.geminiBudgets
15278
16331
  };
15279
16332
  var THINKING_OUTPUT_RESERVE = 1024;
15280
16333
  var MIN_THINKING_BUDGET = 1024;
15281
16334
  var THINKING_MIN_MAX_TOKENS = 4096;
15282
- function applyOpenAIReasoning(body, reasoning, mode = "legacy_effort") {
16335
+ function applyOpenAIReasoning(body, reasoning, mode = "legacy_effort", model, rule) {
15283
16336
  if (!reasoning) return;
15284
- const effort = normalizeEffort(reasoning.effort);
16337
+ const requested = normalizeEffort(reasoning.effort);
16338
+ const r = rule ?? resolveModelRule({ provider: "openai", model: model ?? body.model });
16339
+ const effort = requested && mapReasoningEffort(r, requested, mode === "responses" ? "responses" : "chat").effort;
15285
16340
  if (mode === "responses") {
15286
16341
  const obj = {};
15287
16342
  if (effort) obj.effort = effort;
@@ -15295,48 +16350,54 @@ function applyOpenAIReasoning(body, reasoning, mode = "legacy_effort") {
15295
16350
  }
15296
16351
  function supportsAdaptiveThinking(model) {
15297
16352
  if (typeof model !== "string") return false;
15298
- const id = model.toLowerCase();
15299
- const at = id.indexOf("claude-");
15300
- if (at < 0) return false;
15301
- for (const tok of id.slice(at + 7).split("-")) {
15302
- if (tok === "fable") return true;
15303
- const m = /^(\d+)(?:@|$)/.exec(tok);
15304
- if (m) {
15305
- if (m[1].length <= 2) return Number(m[1]) >= 5;
15306
- return false;
15307
- }
15308
- }
15309
- return false;
16353
+ return resolveModelRule({ provider: "anthropic", model }).reasoning.style === "adaptive";
15310
16354
  }
15311
- function resolveThinkingForm(model, mode) {
16355
+ function resolveThinkingForm(model, mode, rule) {
15312
16356
  if (mode === "adaptive" || mode === "budget") return mode;
16357
+ if (rule) return rule.reasoning.style === "adaptive" ? "adaptive" : "budget";
15313
16358
  return supportsAdaptiveThinking(model) ? "adaptive" : "budget";
15314
16359
  }
15315
- function applyAdaptiveThinking(_body, reasoning, setThinking, setOutputConfig) {
15316
- const requested = normalizeEffort(reasoning.effort);
15317
- if (requested === "none") return false;
15318
- let effort;
15319
- if (requested) effort = requested === "minimal" ? "low" : requested;
15320
- else if (typeof reasoning.budgetTokens === "number" && reasoning.budgetTokens > 0) {
15321
- effort = reasoning.budgetTokens <= ANTHROPIC_EFFORT_BUDGET.low ? "low" : reasoning.budgetTokens <= ANTHROPIC_EFFORT_BUDGET.medium ? "medium" : "high";
16360
+ function enforceAnthropicSampling(target, rule, thinkingOn, bedrock = false) {
16361
+ const fields = bedrock ? [["temperature", "temperature"], ["topP", "topP"]] : [["temperature", "temperature"], ["top_p", "topP"], ["top_k", "topK"]];
16362
+ for (const [key, field] of fields) {
16363
+ if (key in target && !allowsSampling(rule, field, target[key], thinkingOn)) delete target[key];
15322
16364
  }
15323
- if (!effort) return false;
16365
+ }
16366
+ function applyAdaptiveThinking(reasoning, rule, setThinking, setOutputConfig) {
16367
+ let requested = normalizeEffort(reasoning.effort);
16368
+ if (!requested && typeof reasoning.budgetTokens === "number" && reasoning.budgetTokens > 0) {
16369
+ requested = reasoning.budgetTokens <= ANTHROPIC_EFFORT_BUDGET.low ? "low" : reasoning.budgetTokens <= ANTHROPIC_EFFORT_BUDGET.medium ? "medium" : "high";
16370
+ }
16371
+ if (!requested) return false;
16372
+ const mapped = mapReasoningEffort(rule, requested);
16373
+ if (mapped.off) {
16374
+ setThinking({ type: mapped.off });
16375
+ return false;
16376
+ }
16377
+ if (!mapped.effort || mapped.effort === "none") return false;
15324
16378
  const thinking = { type: "adaptive" };
15325
16379
  if (reasoning.includeThoughts !== false) thinking.display = "summarized";
15326
16380
  if (reasoning.providerExtras) Object.assign(thinking, reasoning.providerExtras);
15327
- setThinking({ thinking });
15328
- setOutputConfig({ effort });
16381
+ setThinking(thinking);
16382
+ setOutputConfig({ effort: mapped.effort });
15329
16383
  return true;
15330
16384
  }
15331
16385
  function applyAnthropicReasoning(body, reasoning, opts) {
15332
- if (!reasoning) return;
15333
- if (resolveThinkingForm(opts?.model ?? body.model, opts?.mode) === "adaptive") {
15334
- const on = applyAdaptiveThinking(body, reasoning, (extra) => body.thinking = extra.thinking, (o) => body.output_config = { ...body.output_config ?? {}, ...o });
15335
- if (on) stripSamplingParams(body, ["temperature", "top_p", "top_k"]);
16386
+ const rule = opts?.rule ?? resolveModelRule({ provider: "anthropic", model: opts?.model ?? body.model });
16387
+ if (!reasoning) {
16388
+ enforceAnthropicSampling(body, rule, false);
16389
+ return;
16390
+ }
16391
+ if (resolveThinkingForm(opts?.model ?? body.model, opts?.mode, rule) === "adaptive") {
16392
+ const on = applyAdaptiveThinking(reasoning, rule, (t) => body.thinking = t, (o) => body.output_config = { ...body.output_config ?? {}, ...o });
16393
+ enforceAnthropicSampling(body, rule, on);
16394
+ return;
16395
+ }
16396
+ let budget = resolveBudget(reasoning, ANTHROPIC_EFFORT_BUDGET, rule);
16397
+ if (!budget) {
16398
+ enforceAnthropicSampling(body, rule, false);
15336
16399
  return;
15337
16400
  }
15338
- let budget = resolveBudget(reasoning, ANTHROPIC_EFFORT_BUDGET);
15339
- if (!budget) return;
15340
16401
  const currentMax = typeof body.max_tokens === "number" ? body.max_tokens : 0;
15341
16402
  const requiredMax = Math.max(THINKING_MIN_MAX_TOKENS, budget + THINKING_OUTPUT_RESERVE);
15342
16403
  if (currentMax < requiredMax) {
@@ -15346,18 +16407,25 @@ function applyAnthropicReasoning(body, reasoning, opts) {
15346
16407
  const thinking = { type: "enabled", budget_tokens: budget };
15347
16408
  if (reasoning.providerExtras) Object.assign(thinking, reasoning.providerExtras);
15348
16409
  body.thinking = thinking;
15349
- stripSamplingParams(body, ["temperature", "top_p", "top_k"]);
16410
+ enforceAnthropicSampling(body, rule, true);
15350
16411
  }
15351
16412
  function applyBedrockReasoning(body, reasoning, opts) {
15352
- if (!reasoning) return;
15353
- if (resolveThinkingForm(opts?.model ?? body.modelId, opts?.mode) === "adaptive") {
16413
+ const rule = opts?.rule ?? resolveModelRule({ provider: "bedrock", model: opts?.model ?? body.modelId });
16414
+ if (!reasoning) {
16415
+ if (body.inferenceConfig) enforceAnthropicSampling(body.inferenceConfig, rule, false, true);
16416
+ return;
16417
+ }
16418
+ if (resolveThinkingForm(opts?.model ?? body.modelId, opts?.mode, rule) === "adaptive") {
15354
16419
  const fields = () => body.additionalModelRequestFields ?? (body.additionalModelRequestFields = {});
15355
- const on = applyAdaptiveThinking(body, reasoning, (extra2) => fields().thinking = extra2.thinking, (o) => fields().output_config = { ...fields().output_config ?? {}, ...o });
15356
- if (on && body.inferenceConfig) stripSamplingParams(body.inferenceConfig, ["temperature", "topP"]);
16420
+ const on = applyAdaptiveThinking(reasoning, rule, (t) => fields().thinking = t, (o) => fields().output_config = { ...fields().output_config ?? {}, ...o });
16421
+ if (body.inferenceConfig) enforceAnthropicSampling(body.inferenceConfig, rule, on, true);
16422
+ return;
16423
+ }
16424
+ let budget = resolveBudget(reasoning, ANTHROPIC_EFFORT_BUDGET, rule);
16425
+ if (!budget) {
16426
+ if (body.inferenceConfig) enforceAnthropicSampling(body.inferenceConfig, rule, false, true);
15357
16427
  return;
15358
16428
  }
15359
- let budget = resolveBudget(reasoning, ANTHROPIC_EFFORT_BUDGET);
15360
- if (!budget) return;
15361
16429
  const inference = body.inferenceConfig ?? (body.inferenceConfig = {});
15362
16430
  const currentMax = typeof inference.maxTokens === "number" ? inference.maxTokens : 0;
15363
16431
  const requiredMax = Math.max(THINKING_MIN_MAX_TOKENS, budget + THINKING_OUTPUT_RESERVE);
@@ -15369,7 +16437,7 @@ function applyBedrockReasoning(body, reasoning, opts) {
15369
16437
  const thinking = { type: "enabled", budget_tokens: budget };
15370
16438
  if (reasoning.providerExtras) Object.assign(thinking, reasoning.providerExtras);
15371
16439
  extra.thinking = thinking;
15372
- stripSamplingParams(inference, ["temperature", "topP"]);
16440
+ enforceAnthropicSampling(inference, rule, true, true);
15373
16441
  }
15374
16442
  function applyGeminiReasoning(generationConfig, reasoning) {
15375
16443
  if (!reasoning) return;
@@ -15381,11 +16449,6 @@ function applyGeminiReasoning(generationConfig, reasoning) {
15381
16449
  if (reasoning.providerExtras) Object.assign(thinkingConfig, reasoning.providerExtras);
15382
16450
  generationConfig.thinkingConfig = thinkingConfig;
15383
16451
  }
15384
- function stripSamplingParams(target, keys) {
15385
- for (const k of keys) {
15386
- if (k in target) delete target[k];
15387
- }
15388
- }
15389
16452
  function clampBudget(budget, maxTokens) {
15390
16453
  if (typeof maxTokens !== "number" || maxTokens <= 0) return budget;
15391
16454
  const ceiling = maxTokens - THINKING_OUTPUT_RESERVE;
@@ -15394,16 +16457,17 @@ function clampBudget(budget, maxTokens) {
15394
16457
  }
15395
16458
  function normalizeEffort(effort) {
15396
16459
  if (!effort) return void 0;
15397
- const allowed = ["none", "minimal", "low", "medium", "high"];
16460
+ const allowed = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
15398
16461
  return allowed.includes(effort) ? effort : void 0;
15399
16462
  }
15400
- function resolveBudget(reasoning, effortBudget) {
16463
+ function resolveBudget(reasoning, effortBudget2, rule) {
15401
16464
  const effort = normalizeEffort(reasoning.effort);
15402
16465
  if (effort === "none") return 0;
15403
16466
  if (typeof reasoning.budgetTokens === "number" && reasoning.budgetTokens > 0) {
15404
16467
  return Math.floor(reasoning.budgetTokens);
15405
16468
  }
15406
- if (effort && effortBudget[effort]) return effortBudget[effort];
16469
+ const mapped = effort && rule ? mapReasoningEffort(rule, effort).effort : effort;
16470
+ if (mapped && effortBudget2[mapped]) return effortBudget2[mapped];
15407
16471
  return void 0;
15408
16472
  }
15409
16473
 
@@ -15474,6 +16538,211 @@ function parseSSEBlock(block) {
15474
16538
  return { event, data: dataLines.join("\n"), id, retry };
15475
16539
  }
15476
16540
 
16541
+ // src/models/errors.ts
16542
+ var ModelRequestError = class extends ProviderError {
16543
+ constructor(init) {
16544
+ super(init.message, init.provider, init.status ?? 400, init.responseBody);
16545
+ this.status = init.status ?? 400;
16546
+ this.code = init.code;
16547
+ this.param = init.param;
16548
+ this.value = init.value;
16549
+ this.supportedValues = init.supportedValues;
16550
+ this.model = init.model;
16551
+ }
16552
+ };
16553
+
16554
+ // src/models/adjust.ts
16555
+ var MAX_ADJUSTMENTS = 3;
16556
+ var Q = "['\"`]";
16557
+ function errorMessage(text) {
16558
+ try {
16559
+ const j = JSON.parse(text);
16560
+ const e = j?.error ?? j;
16561
+ const message = typeof e?.message === "string" ? e.message : typeof j?.message === "string" ? j.message : text;
16562
+ return { message, param: typeof e?.param === "string" ? e.param : void 0, code: typeof e?.code === "string" ? e.code : void 0 };
16563
+ } catch {
16564
+ return { message: text };
16565
+ }
16566
+ }
16567
+ function quotedList(s) {
16568
+ const quoted = [...s.matchAll(new RegExp(`${Q}([^'"\`]+)${Q}`, "g"))].map((m) => m[1]);
16569
+ if (quoted.length > 0) return quoted;
16570
+ return s.split(/[,\s]+/).map((x) => x.replace(/[.\s]+$/, "")).filter(Boolean);
16571
+ }
16572
+ function parseModelError(text) {
16573
+ const { message, param: errParam } = errorMessage(String(text ?? ""));
16574
+ const m = message.toLowerCase();
16575
+ if (m.includes("function tools with reasoning_effort") && m.includes("not supported")) {
16576
+ return { kind: "chat_tools_effort", message };
16577
+ }
16578
+ if (m.includes("between_tools") && m.includes("thinking")) return { kind: "thinking_off_between_tools", message };
16579
+ if (m.includes("thinking.type.enabled") && m.includes("not supported")) return { kind: "thinking_enabled_unsupported", message };
16580
+ if (m.includes("thinking.type.adaptive") && m.includes("not supported")) return { kind: "thinking_adaptive_unsupported", message };
16581
+ if (m.includes("thinking.type.disabled") && m.includes("not supported")) return { kind: "thinking_disabled_unsupported", message };
16582
+ const tt = new RegExp(`${Q}?(temperature|top_p|top_k)${Q}?\\s+may only be set to 1 when thinking is enabled`, "i").exec(message);
16583
+ if (tt) return { kind: "temperature_thinking", param: tt[1].toLowerCase(), message };
16584
+ const dep = new RegExp(`${Q}([\\w.]+)${Q}\\s+is deprecated for this model`, "i").exec(message);
16585
+ if (dep) return { kind: "unsupported_parameter", param: dep[1], message };
16586
+ const rename = new RegExp(`use ${Q}(max_completion_tokens|max_tokens)${Q} instead`, "i").exec(message);
16587
+ if (rename) return { kind: "max_tokens_rename", renameTo: rename[1].toLowerCase(), param: rename[1].toLowerCase() === "max_tokens" ? "max_completion_tokens" : "max_tokens", message };
16588
+ const val = new RegExp(`unsupported value:?\\s*${Q}?([\\w.]+)${Q}?\\s+does not support\\s+(${Q}[^'"\`]*${Q}|[^\\s]+)`, "i").exec(message);
16589
+ if (val) {
16590
+ const param = val[1];
16591
+ const value = val[2].replace(/^['"`]|['"`]$/g, "");
16592
+ if (m.includes("only the default") && m.includes("value is supported")) return { kind: "default_only", param, value, message };
16593
+ const sup = /supported values? (?:are|is):?\s*([^\n]*)/i.exec(message);
16594
+ return { kind: "unsupported_value", param, value, supportedValues: sup ? quotedList(sup[1]) : void 0, message };
16595
+ }
16596
+ if (m.includes("only the default") && m.includes("value is supported")) {
16597
+ return { kind: "default_only", param: errParam ?? "temperature", message };
16598
+ }
16599
+ const prm = new RegExp(`unsupported parameter:?\\s*${Q}?([\\w.]+)${Q}?`, "i").exec(message);
16600
+ if (prm) return { kind: "unsupported_parameter", param: prm[1], message };
16601
+ return void 0;
16602
+ }
16603
+ var normParam = (p) => p.toLowerCase().replace(/[-\s]/g, "_");
16604
+ var LADDER2 = new Set(MODEL_RULES.effortLadder);
16605
+ function patchFromParsedError(parsed, endpoint = "chat") {
16606
+ const param = parsed.param ? normParam(parsed.param) : "";
16607
+ switch (parsed.kind) {
16608
+ case "chat_tools_effort":
16609
+ return { patch: { chatTools: { reasoning: "force-none" } }, adjustment: "chat-tools:force-effort-none" };
16610
+ case "max_tokens_rename":
16611
+ return { patch: { maxTokensParam: parsed.renameTo }, adjustment: `${parsed.param}->${parsed.renameTo}` };
16612
+ case "thinking_enabled_unsupported":
16613
+ return { patch: { reasoning: { style: "adaptive" } }, adjustment: "thinking:adaptive" };
16614
+ case "thinking_adaptive_unsupported":
16615
+ return { patch: { reasoning: { style: "budget" } }, adjustment: "thinking:budget" };
16616
+ case "thinking_off_between_tools":
16617
+ return { patch: { reasoning: { off: "between_tools" } }, adjustment: "thinking-off:between_tools" };
16618
+ case "thinking_disabled_unsupported":
16619
+ return { patch: { reasoning: { off: null } }, adjustment: "thinking-off:none" };
16620
+ case "temperature_thinking":
16621
+ return { patch: { sampling: { temperature: "unsupported" } }, adjustment: "drop:temperature" };
16622
+ case "default_only":
16623
+ if (param === "temperature") return { patch: { sampling: { temperature: "default-only" } }, adjustment: "temperature:default-only" };
16624
+ return { patch: { dropParams: [parsed.param ?? param] }, adjustment: `drop:${parsed.param ?? param}` };
16625
+ case "unsupported_value": {
16626
+ if (/^(reasoning_effort|reasoning\.effort|effort|output_config\.effort)$/.test(param)) {
16627
+ const list = (parsed.supportedValues ?? []).map((v) => v.toLowerCase()).filter((v) => LADDER2.has(v));
16628
+ if (list.length === 0) return void 0;
16629
+ const reasoning = endpoint === "responses" ? { responsesEfforts: list } : { efforts: list };
16630
+ return { patch: { reasoning }, adjustment: `efforts:${list.join("|")}` };
16631
+ }
16632
+ return paramDropPatch(param, parsed);
16633
+ }
16634
+ case "unsupported_parameter":
16635
+ return paramDropPatch(param, parsed);
16636
+ }
16637
+ }
16638
+ function paramDropPatch(param, parsed) {
16639
+ if (!param) return void 0;
16640
+ const label = `drop:${parsed.param ?? param}`;
16641
+ switch (param) {
16642
+ case "temperature":
16643
+ return { patch: { sampling: { temperature: "unsupported" } }, adjustment: label };
16644
+ case "top_p":
16645
+ return { patch: { sampling: { topP: false } }, adjustment: label };
16646
+ case "top_k":
16647
+ return { patch: { sampling: { topK: false } }, adjustment: label };
16648
+ case "presence_penalty":
16649
+ case "frequency_penalty":
16650
+ return { patch: { sampling: { penalties: false } }, adjustment: label };
16651
+ case "logprobs":
16652
+ case "top_logprobs":
16653
+ return { patch: { sampling: { logprobs: false } }, adjustment: label };
16654
+ case "max_tokens":
16655
+ return { patch: { maxTokensParam: "max_completion_tokens" }, adjustment: "max_tokens->max_completion_tokens" };
16656
+ case "max_completion_tokens":
16657
+ return { patch: { maxTokensParam: "max_tokens" }, adjustment: "max_completion_tokens->max_tokens" };
16658
+ case "reasoning_effort":
16659
+ case "reasoning.effort":
16660
+ case "reasoning":
16661
+ return { patch: { reasoning: { style: "none", efforts: [], responsesEfforts: [] }, dropParams: [param] }, adjustment: label };
16662
+ default:
16663
+ return { patch: { dropParams: [parsed.param ?? param] }, adjustment: label };
16664
+ }
16665
+ }
16666
+ function codeFor(parsed) {
16667
+ if (!parsed) return "model_request_invalid";
16668
+ if (parsed.kind === "unsupported_value" || parsed.kind === "default_only") return "unsupported_value";
16669
+ if (parsed.kind === "unsupported_parameter" || parsed.kind === "max_tokens_rename" || parsed.kind === "temperature_thinking") return "unsupported_parameter";
16670
+ return "model_request_invalid";
16671
+ }
16672
+ function toModelRequestError(err, parsed, provider, model) {
16673
+ return new ModelRequestError({
16674
+ status: err.statusCode ?? 400,
16675
+ code: codeFor(parsed),
16676
+ param: parsed?.param,
16677
+ value: parsed?.value,
16678
+ supportedValues: parsed?.supportedValues,
16679
+ provider: err.provider || provider,
16680
+ model,
16681
+ message: err.message,
16682
+ responseBody: err.responseBody
16683
+ });
16684
+ }
16685
+ var SameRequest = class extends Error {
16686
+ };
16687
+ function withoutCallerParams(step, callerParams) {
16688
+ const drops = step.patch.dropParams;
16689
+ if (!callerParams?.length || !drops) return step;
16690
+ const mine = (p) => callerParams.includes(p.split(".")[0]);
16691
+ if (!drops.some(mine)) return step;
16692
+ const { dropParams: _ignored, ...rest } = step.patch;
16693
+ const kept = drops.filter((p) => !mine(p));
16694
+ const patch = kept.length ? { ...rest, dropParams: kept } : rest;
16695
+ return Object.keys(patch).length ? { ...step, patch } : void 0;
16696
+ }
16697
+ async function runWithModelAdjustments(opts) {
16698
+ let overlay = {};
16699
+ const steps = [];
16700
+ let previous;
16701
+ let lastError;
16702
+ let lastParsed;
16703
+ for (; ; ) {
16704
+ const guard = (body) => {
16705
+ let json;
16706
+ try {
16707
+ json = JSON.stringify(body);
16708
+ } catch {
16709
+ return;
16710
+ }
16711
+ if (previous !== void 0 && json === previous) throw new SameRequest();
16712
+ previous = json;
16713
+ };
16714
+ try {
16715
+ const result = await opts.run(overlay, guard);
16716
+ for (const step of steps) {
16717
+ const s = withoutCallerParams(step, opts.callerParams);
16718
+ if (!s) continue;
16719
+ rememberModelRulePatch(opts.provider, opts.model, s.patch, opts.scope);
16720
+ if (opts.onLearned) {
16721
+ try {
16722
+ opts.onLearned({ provider: opts.provider, model: opts.model, adjustment: s.adjustment, patch: s.patch, reason: s.reason });
16723
+ } catch {
16724
+ }
16725
+ }
16726
+ }
16727
+ return result;
16728
+ } catch (err) {
16729
+ if (err instanceof SameRequest) throw toModelRequestError(lastError, lastParsed, opts.provider, opts.model);
16730
+ if (!(err instanceof ProviderError) || err instanceof ModelRequestError || err.statusCode !== 400) throw err;
16731
+ const parsed = parseModelError(String(err.responseBody ?? err.message));
16732
+ const fix = parsed ? patchFromParsedError(parsed, opts.endpoint) : void 0;
16733
+ lastError = err;
16734
+ lastParsed = parsed;
16735
+ if (!fix || steps.length >= MAX_ADJUSTMENTS) throw toModelRequestError(err, parsed, opts.provider, opts.model);
16736
+ overlay = mergeRulePatch(overlay, fix.patch);
16737
+ steps.push({ ...fix, reason: parsed.message });
16738
+ }
16739
+ }
16740
+ }
16741
+ function asModelRequestError(err, provider, model) {
16742
+ if (!(err instanceof ProviderError) || err instanceof ModelRequestError || err.statusCode !== 400) return err;
16743
+ return toModelRequestError(err, parseModelError(String(err.responseBody ?? err.message)), provider, model);
16744
+ }
16745
+
15477
16746
  // src/providers/utils/media.ts
15478
16747
  var EXTENSION_MIME = {
15479
16748
  jpg: "image/jpeg",
@@ -15774,8 +17043,7 @@ var OpenAIProvider = class extends BaseProvider {
15774
17043
  if (this.useResponsesApi(request)) {
15775
17044
  return this.completeResponses(request);
15776
17045
  }
15777
- const body = this.buildRequestBody(request, false);
15778
- const res = await this.doFetch(body, request.signal);
17046
+ const res = await this.fetchChat(request, false);
15779
17047
  const json = await res.json();
15780
17048
  return this.parseResponse(json);
15781
17049
  }
@@ -15799,8 +17067,7 @@ var OpenAIProvider = class extends BaseProvider {
15799
17067
  };
15800
17068
  return;
15801
17069
  }
15802
- const body = this.buildRequestBody(request, true);
15803
- const res = await this.doFetch(body, request.signal);
17070
+ const res = await this.fetchChat(request, true);
15804
17071
  if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
15805
17072
  let id = "";
15806
17073
  let model = "";
@@ -15904,7 +17171,43 @@ var OpenAIProvider = class extends BaseProvider {
15904
17171
  return res;
15905
17172
  }
15906
17173
  // ─── Internals ───────────────────────────────────────────────────────────
15907
- buildRequestBody(request, stream) {
17174
+ /** Chat Completions call that corrects itself from the provider's own 400s (see models/adjust.ts). */
17175
+ fetchChat(request, stream) {
17176
+ const model = request.model || this.defaultModel;
17177
+ return runWithModelAdjustments({
17178
+ provider: this.providerName,
17179
+ model,
17180
+ endpoint: "chat",
17181
+ scope: this.baseURL,
17182
+ callerParams: request.extra ? Object.keys(request.extra) : void 0,
17183
+ onLearned: this.onModelRuleLearned,
17184
+ run: (overlay, guard) => {
17185
+ const body = this.buildRequestBody(request, stream, overlay);
17186
+ guard(body);
17187
+ return this.doFetch(body, request.signal);
17188
+ }
17189
+ });
17190
+ }
17191
+ /** Same for the Responses API. */
17192
+ fetchResponses(request, adaptExtras, stream = false) {
17193
+ const model = request.model || this.defaultModel;
17194
+ return runWithModelAdjustments({
17195
+ provider: this.providerName,
17196
+ model,
17197
+ endpoint: "responses",
17198
+ scope: this.baseURL,
17199
+ callerParams: request.extra ? Object.keys(request.extra) : void 0,
17200
+ onLearned: this.onModelRuleLearned,
17201
+ run: (overlay, guard) => {
17202
+ const body = this.buildResponsesBody(request, overlay);
17203
+ if (adaptExtras) adaptChatExtrasForResponses(body, request.extra);
17204
+ if (stream) body.stream = true;
17205
+ guard(body);
17206
+ return this.responsesFetch(body, request.signal);
17207
+ }
17208
+ });
17209
+ }
17210
+ buildRequestBody(request, stream, overlay) {
15908
17211
  const body = {
15909
17212
  model: request.model || this.defaultModel,
15910
17213
  messages: request.messages.map((m) => this.toOpenAIMessage(m)),
@@ -15914,20 +17217,20 @@ var OpenAIProvider = class extends BaseProvider {
15914
17217
  body.stream_options = { include_usage: true };
15915
17218
  }
15916
17219
  const modelId = String(body.model);
15917
- const reasoningModel = isReasoningModel(modelId) && !/chat/i.test(modelId);
15918
- if (request.temperature != null && !reasoningModel) body.temperature = request.temperature;
17220
+ const rule = resolveModelRule({ provider: this.providerName, model: modelId, patch: overlay, scope: this.baseURL });
17221
+ if (request.temperature != null && allowsSampling(rule, "temperature", request.temperature)) body.temperature = request.temperature;
15919
17222
  if (request.maxTokens != null) {
15920
- const useCompletionTokens = this.maxTokensParam === "max_completion_tokens" || this.maxTokensParam === "auto" && isReasoningModel(modelId);
15921
- body[useCompletionTokens ? "max_completion_tokens" : "max_tokens"] = request.maxTokens;
17223
+ const param = this.maxTokensParam === "auto" ? rule.maxTokensParam : this.maxTokensParam;
17224
+ body[param] = request.maxTokens;
15922
17225
  }
15923
- if (request.topP != null && !reasoningModel) body.top_p = request.topP;
17226
+ if (request.topP != null && allowsSampling(rule, "topP")) body.top_p = request.topP;
15924
17227
  if (request.stop) body.stop = request.stop;
15925
- if (request.presencePenalty != null) body.presence_penalty = request.presencePenalty;
15926
- if (request.frequencyPenalty != null) body.frequency_penalty = request.frequencyPenalty;
17228
+ if (request.presencePenalty != null && allowsSampling(rule, "presencePenalty")) body.presence_penalty = request.presencePenalty;
17229
+ if (request.frequencyPenalty != null && allowsSampling(rule, "frequencyPenalty")) body.frequency_penalty = request.frequencyPenalty;
15927
17230
  if (request.seed != null) body.seed = request.seed;
15928
17231
  if (request.n != null) body.n = request.n;
15929
- if (request.logprobs != null) body.logprobs = request.logprobs;
15930
- if (request.topLogprobs != null) body.top_logprobs = request.topLogprobs;
17232
+ if (request.logprobs != null && allowsSampling(rule, "logprobs")) body.logprobs = request.logprobs;
17233
+ if (request.topLogprobs != null && allowsSampling(rule, "logprobs")) body.top_logprobs = request.topLogprobs;
15931
17234
  if (request.tools?.length) {
15932
17235
  body.tools = request.tools.map((t) => this.toOpenAITool(t));
15933
17236
  if (request.parallelToolCalls != null) body.parallel_tool_calls = request.parallelToolCalls;
@@ -15956,14 +17259,17 @@ var OpenAIProvider = class extends BaseProvider {
15956
17259
  if (request.extra) {
15957
17260
  Object.assign(body, request.extra);
15958
17261
  }
15959
- applyOpenAIReasoning(body, request.reasoning, "legacy_effort");
17262
+ applyOpenAIReasoning(body, request.reasoning, "legacy_effort", modelId, rule);
17263
+ if (body.tools?.length && rule.chatTools.reasoning === "force-none" && rule.reasoning.style !== "none") {
17264
+ body.reasoning_effort = "none";
17265
+ }
17266
+ applyDropParams(body, rule);
15960
17267
  return body;
15961
17268
  }
15962
17269
  // ─── Responses API (reasoning models) ──────────────────────────────────────
15963
17270
  async completeResponses(request) {
15964
- const body = this.buildResponsesBody(request);
15965
- if (this.apiResponsesNative || this.isResponsesApiModel(request)) adaptChatExtrasForResponses(body, request.extra);
15966
- const res = await this.responsesFetch(body, request.signal);
17271
+ const adapt = this.apiResponsesNative || this.isResponsesApiModel(request);
17272
+ const res = await this.fetchResponses(request, adapt);
15967
17273
  const json = await res.json();
15968
17274
  return this.parseResponsesResponse(json, request.model || this.defaultModel);
15969
17275
  }
@@ -15976,10 +17282,7 @@ var OpenAIProvider = class extends BaseProvider {
15976
17282
  * reads it). */
15977
17283
  async *streamResponses(request) {
15978
17284
  const modelId = request.model || this.defaultModel;
15979
- const body = this.buildResponsesBody(request);
15980
- adaptChatExtrasForResponses(body, request.extra);
15981
- body.stream = true;
15982
- const res = await this.responsesFetch(body, request.signal);
17285
+ const res = await this.fetchResponses(request, true, true);
15983
17286
  if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
15984
17287
  let id = "";
15985
17288
  let model = modelId;
@@ -16068,13 +17371,14 @@ var OpenAIProvider = class extends BaseProvider {
16068
17371
  finishReason: final?.finishReason ?? (calls.size > 0 ? "tool_calls" : "stop")
16069
17372
  };
16070
17373
  }
16071
- buildResponsesBody(request) {
17374
+ buildResponsesBody(request, overlay) {
16072
17375
  const body = {
16073
17376
  model: request.model || this.defaultModel,
16074
17377
  input: request.messages.map((m) => this.toResponsesInput(m))
16075
17378
  };
16076
17379
  if (request.maxTokens != null) body.max_output_tokens = request.maxTokens;
16077
- if (request.topP != null) body.top_p = request.topP;
17380
+ const rule = resolveModelRule({ provider: this.providerName, model: String(body.model), patch: overlay, scope: this.baseURL });
17381
+ if (request.topP != null && allowsSampling(rule, "topP")) body.top_p = request.topP;
16078
17382
  if (request.tools?.length) {
16079
17383
  body.tools = request.tools.map((t) => ({
16080
17384
  type: "function",
@@ -16106,7 +17410,8 @@ var OpenAIProvider = class extends BaseProvider {
16106
17410
  }
16107
17411
  }
16108
17412
  if (request.extra) Object.assign(body, request.extra);
16109
- applyOpenAIReasoning(body, request.reasoning, "responses");
17413
+ applyOpenAIReasoning(body, request.reasoning, "responses", String(body.model), rule);
17414
+ applyDropParams(body, rule);
16110
17415
  return body;
16111
17416
  }
16112
17417
  /** Maps a unified message to a Responses API `input` item. */
@@ -16371,8 +17676,8 @@ function pickProviderFields(top, message, choice) {
16371
17676
  }
16372
17677
  function isReasoningModel(model) {
16373
17678
  if (!model) return false;
16374
- const m = model.toLowerCase();
16375
- return /(^|[/_-])o[0-9]/.test(m) || m.startsWith("o1") || m.startsWith("o3") || m.startsWith("o4") || m.includes("gpt-5") || m.includes("gpt5");
17679
+ const rule = resolveModelRule({ provider: "openai", model });
17680
+ return !rule.isDefault && rule.reasoning.style === "effort" && rule.sampling.temperature !== "any";
16376
17681
  }
16377
17682
  function adaptChatExtrasForResponses(body, extra) {
16378
17683
  const native = extra ?? {};
@@ -16488,12 +17793,6 @@ function normalizeExplicitThinking(body) {
16488
17793
  }
16489
17794
  for (const k of ["temperature", "top_p", "top_k"]) delete body[k];
16490
17795
  }
16491
- function thinkingFormRejected(text) {
16492
- const t = text.toLowerCase();
16493
- if (t.indexOf("thinking.type.enabled") >= 0 && t.indexOf("not supported") >= 0) return "enabled";
16494
- if (t.indexOf("thinking.type.adaptive") >= 0 && t.indexOf("not supported") >= 0 && t.indexOf("thinking.type.enabled") < 0) return "adaptive";
16495
- return void 0;
16496
- }
16497
17796
  var AnthropicProvider = class extends BaseProvider {
16498
17797
  constructor(config) {
16499
17798
  super();
@@ -16644,7 +17943,7 @@ var AnthropicProvider = class extends BaseProvider {
16644
17943
  }
16645
17944
  }
16646
17945
  // ─── Internals ───────────────────────────────────────────────────────────
16647
- buildRequestBody(request, stream, modeOverride, downgradeToolChoice = false) {
17946
+ buildRequestBody(request, stream, overlay, downgradeToolChoice = false) {
16648
17947
  const { system, messages } = this.splitSystemMessages(request.messages);
16649
17948
  const body = {
16650
17949
  model: request.model || this.defaultModel,
@@ -16659,6 +17958,7 @@ var AnthropicProvider = class extends BaseProvider {
16659
17958
  body.system = system;
16660
17959
  }
16661
17960
  }
17961
+ const rule = resolveModelRule({ provider: this.providerName, model: String(body.model), patch: overlay });
16662
17962
  if (request.temperature != null) body.temperature = request.temperature;
16663
17963
  if (request.topP != null) body.top_p = request.topP;
16664
17964
  if (request.stop) body.stop_sequences = request.stop;
@@ -16685,9 +17985,11 @@ var AnthropicProvider = class extends BaseProvider {
16685
17985
  const explicitThinking = request.extra && typeof request.extra === "object" && "thinking" in request.extra;
16686
17986
  if (explicitThinking) {
16687
17987
  normalizeExplicitThinking(body);
17988
+ enforceAnthropicSampling(body, rule, body.thinking?.type === "enabled" || body.thinking?.type === "adaptive");
16688
17989
  } else {
16689
- applyAnthropicReasoning(body, request.reasoning, { model: body.model, mode: modeOverride ?? this.thinkingMode });
17990
+ applyAnthropicReasoning(body, request.reasoning, { model: body.model, mode: this.thinkingMode, rule });
16690
17991
  }
17992
+ applyDropParams(body, rule);
16691
17993
  if (typeof body.max_tokens !== "number" || !(body.max_tokens > 0)) {
16692
17994
  body.max_tokens = DEFAULT_MAX_TOKENS;
16693
17995
  }
@@ -16823,31 +18125,28 @@ var AnthropicProvider = class extends BaseProvider {
16823
18125
  }
16824
18126
  /** Builds and sends the request; retries once with the other thinking form
16825
18127
  * when the upstream rejects the one we picked. */
16826
- async send(request, stream) {
16827
- let mode;
18128
+ send(request, stream) {
16828
18129
  let downgrade = false;
16829
- let flippedThinking = false;
16830
- for (; ; ) {
16831
- const body = this.buildRequestBody(request, stream, mode, downgrade);
16832
- try {
16833
- return await this.doFetch(body, request.signal);
16834
- } catch (err) {
16835
- if (!(err instanceof ProviderError) || err.statusCode !== 400) throw err;
16836
- const text = String(err.responseBody ?? err.message);
16837
- if (!downgrade && body.tool_choice && body.tool_choice.type !== "auto" && forcedToolChoiceRejected(text)) {
16838
- downgrade = true;
16839
- continue;
18130
+ return runWithModelAdjustments({
18131
+ provider: this.providerName,
18132
+ model: request.model || this.defaultModel,
18133
+ endpoint: "messages",
18134
+ callerParams: request.extra ? Object.keys(request.extra) : void 0,
18135
+ onLearned: this.onModelRuleLearned,
18136
+ run: async (overlay, guard) => {
18137
+ for (; ; ) {
18138
+ const body = this.buildRequestBody(request, stream, overlay, downgrade);
18139
+ guard(body);
18140
+ try {
18141
+ return await this.doFetch(body, request.signal);
18142
+ } catch (err) {
18143
+ const rejected = err instanceof ProviderError && err.statusCode === 400 && !downgrade && body.tool_choice && body.tool_choice.type !== "auto" && forcedToolChoiceRejected(String(err.responseBody ?? err.message));
18144
+ if (!rejected) throw err;
18145
+ downgrade = true;
18146
+ }
16840
18147
  }
16841
- const explicit = request.extra && typeof request.extra === "object" && "thinking" in request.extra;
16842
- if (flippedThinking || explicit || !request.reasoning) throw err;
16843
- const wrong = thinkingFormRejected(text);
16844
- if (!wrong) throw err;
16845
- const used = resolveThinkingForm(request.model ?? this.defaultModel, this.thinkingMode);
16846
- if (wrong === "enabled" && used !== "budget" || wrong === "adaptive" && used !== "adaptive") throw err;
16847
- mode = wrong === "enabled" ? "adaptive" : "budget";
16848
- flippedThinking = true;
16849
18148
  }
16850
- }
18149
+ });
16851
18150
  }
16852
18151
  async doFetch(body, signal) {
16853
18152
  const url = `${this.baseURL}/v1/messages`;
@@ -16964,6 +18263,7 @@ var AzureProvider = class extends OpenAIProvider {
16964
18263
  retry: config.retry,
16965
18264
  fetch: config.fetch,
16966
18265
  timeoutMs: config.timeoutMs,
18266
+ onModelRuleLearned: config.onModelRuleLearned,
16967
18267
  maxTokensParam: config.maxTokensParam,
16968
18268
  api: config.api,
16969
18269
  responsesApi: config.responsesApi,
@@ -17092,6 +18392,7 @@ var OpenAICompatibleProvider = class extends OpenAIProvider {
17092
18392
  retry: config.retry,
17093
18393
  fetch: config.fetch,
17094
18394
  timeoutMs: config.timeoutMs,
18395
+ onModelRuleLearned: config.onModelRuleLearned,
17095
18396
  maxTokensParam: config.maxTokensParam,
17096
18397
  api: config.api,
17097
18398
  // Unlike OpenAI / Azure, "auto" on a compatible server means Chat
@@ -17447,7 +18748,7 @@ var BedrockProvider = class extends BaseProvider {
17447
18748
  return res;
17448
18749
  }
17449
18750
  // ─── Internals ───────────────────────────────────────────────────────────
17450
- buildConverseBody(request, downgradeToolChoice = false) {
18751
+ buildConverseBody(request, downgradeToolChoice = false, overlay) {
17451
18752
  const { system, messages } = this.splitSystemMessages(request.messages);
17452
18753
  const body = {
17453
18754
  messages: this.toBedrockMessages(messages)
@@ -17493,7 +18794,12 @@ var BedrockProvider = class extends BaseProvider {
17493
18794
  Object.assign(body, topLevel);
17494
18795
  if (Object.keys(fields).length > 0) body.additionalModelRequestFields = fields;
17495
18796
  }
17496
- applyBedrockReasoning(body, request.reasoning, { model: request.model || this.defaultModel, mode: this.thinkingMode });
18797
+ const rule = resolveModelRule({ provider: this.providerName, model: request.model || this.defaultModel, patch: overlay });
18798
+ applyBedrockReasoning(body, request.reasoning, { model: request.model || this.defaultModel, mode: this.thinkingMode, rule });
18799
+ if (body.inferenceConfig && rule.dropParams?.length) {
18800
+ for (const p of rule.dropParams) delete body.inferenceConfig[p === "top_p" ? "topP" : p === "max_tokens" ? "maxTokens" : p];
18801
+ }
18802
+ applyDropParams(body, rule);
17497
18803
  return body;
17498
18804
  }
17499
18805
  splitSystemMessages(messages) {
@@ -17618,16 +18924,29 @@ var BedrockProvider = class extends BaseProvider {
17618
18924
  }
17619
18925
  /** Sends the Converse call; retries once with toolChoice auto when the
17620
18926
  * model rejects forced tool use. */
17621
- async converseFetch(modelId, request, stream) {
17622
- const body = this.buildConverseBody(request);
17623
- try {
17624
- return await this.doFetch(modelId, body, request.signal, stream);
17625
- } catch (err) {
17626
- const tc = body.toolConfig?.toolChoice;
17627
- if (!(err instanceof ProviderError) || err.statusCode !== 400 || !tc || tc.auto) throw err;
17628
- if (!forcedToolChoiceRejected(String(err.responseBody ?? err.message))) throw err;
17629
- return this.doFetch(modelId, this.buildConverseBody(request, true), request.signal, stream);
17630
- }
18927
+ converseFetch(modelId, request, stream) {
18928
+ let downgrade = false;
18929
+ return runWithModelAdjustments({
18930
+ provider: this.providerName,
18931
+ model: modelId,
18932
+ endpoint: "converse",
18933
+ callerParams: request.extra ? Object.keys(request.extra) : void 0,
18934
+ onLearned: this.onModelRuleLearned,
18935
+ run: async (overlay, guard) => {
18936
+ for (; ; ) {
18937
+ const body = this.buildConverseBody(request, downgrade, overlay);
18938
+ guard(body);
18939
+ try {
18940
+ return await this.doFetch(modelId, body, request.signal, stream);
18941
+ } catch (err) {
18942
+ const tc = body.toolConfig?.toolChoice;
18943
+ const rejected = err instanceof ProviderError && err.statusCode === 400 && !downgrade && !!tc && !tc.auto && forcedToolChoiceRejected(String(err.responseBody ?? err.message));
18944
+ if (!rejected) throw err;
18945
+ downgrade = true;
18946
+ }
18947
+ }
18948
+ }
18949
+ });
17631
18950
  }
17632
18951
  async doFetch(modelId, body, signal, stream) {
17633
18952
  const host = `bedrock-runtime.${this.region}.amazonaws.com`;
@@ -17817,7 +19136,7 @@ var VertexProvider = class extends BaseProvider {
17817
19136
  const modelId = request.model || this.defaultModel;
17818
19137
  const body = this.buildGeminiBody(request);
17819
19138
  const url = this.buildUrl(modelId, false);
17820
- const res = await this.doFetch(url, body, request.signal);
19139
+ const res = await this.doFetch(url, body, request.signal, modelId);
17821
19140
  const json = await res.json();
17822
19141
  return this.parseResponse(json, modelId);
17823
19142
  }
@@ -17825,7 +19144,7 @@ var VertexProvider = class extends BaseProvider {
17825
19144
  const modelId = request.model || this.defaultModel;
17826
19145
  const body = this.buildGeminiBody(request);
17827
19146
  const url = this.buildUrl(modelId, true);
17828
- const res = await this.doFetch(url, body, request.signal);
19147
+ const res = await this.doFetch(url, body, request.signal, modelId);
17829
19148
  if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
17830
19149
  const counter = { next: 0 };
17831
19150
  const contentType = res.headers.get("content-type") ?? "";
@@ -17905,7 +19224,7 @@ var VertexProvider = class extends BaseProvider {
17905
19224
  };
17906
19225
  if (Object.keys(parameters).length > 0) body.parameters = parameters;
17907
19226
  Object.assign(body, extraRest);
17908
- const res = await this.doFetch(url, body, request.signal);
19227
+ const res = await this.doFetch(url, body, request.signal, model);
17909
19228
  const json = await res.json();
17910
19229
  raw.push(json);
17911
19230
  for (const p of json.predictions ?? []) {
@@ -18128,7 +19447,7 @@ var VertexProvider = class extends BaseProvider {
18128
19447
  const tokenJson = await tokenRes.json();
18129
19448
  return tokenJson.access_token;
18130
19449
  }
18131
- async doFetch(url, body, signal) {
19450
+ async doFetch(url, body, signal, model = "") {
18132
19451
  const token = await this.getAccessToken();
18133
19452
  const bodyStr = JSON.stringify(body);
18134
19453
  const res = await this.doFetchWithRetry(
@@ -18146,6 +19465,20 @@ var VertexProvider = class extends BaseProvider {
18146
19465
  );
18147
19466
  if (!res.ok) {
18148
19467
  const text = await res.text().catch(() => "");
19468
+ if (res.status === 400) {
19469
+ const parsed = parseModelError(text);
19470
+ throw new ModelRequestError({
19471
+ status: 400,
19472
+ code: parsed?.kind === "unsupported_value" ? "unsupported_value" : parsed?.kind === "unsupported_parameter" ? "unsupported_parameter" : "model_request_invalid",
19473
+ param: parsed?.param,
19474
+ value: parsed?.value,
19475
+ supportedValues: parsed?.supportedValues,
19476
+ provider: this.providerName,
19477
+ model,
19478
+ message: `Vertex AI API error ${res.status}: ${text}`,
19479
+ responseBody: text
19480
+ });
19481
+ }
18149
19482
  throw new ProviderError(
18150
19483
  `Vertex AI API error ${res.status}: ${text}`,
18151
19484
  this.providerName,
@@ -18327,6 +19660,9 @@ exports.HOOK_NAMES = HOOK_NAMES;
18327
19660
  exports.HookTimeoutError = HookTimeoutError;
18328
19661
  exports.InMemoryMemoryStore = InMemoryMemoryStore;
18329
19662
  exports.JevProvider = JevProvider;
19663
+ exports.MAX_ADJUSTMENTS = MAX_ADJUSTMENTS;
19664
+ exports.MODEL_RULES = MODEL_RULES;
19665
+ exports.ModelRequestError = ModelRequestError;
18330
19666
  exports.NativeJsonSchemaStrategy = NativeJsonSchemaStrategy;
18331
19667
  exports.OpenAICompatibleProvider = OpenAICompatibleProvider;
18332
19668
  exports.OpenAIProvider = OpenAIProvider;
@@ -18338,7 +19674,10 @@ exports.TOOL_INPUT_DIGEST_KEY = TOOL_INPUT_DIGEST_KEY;
18338
19674
  exports.ToolBasedStrategy = ToolBasedStrategy;
18339
19675
  exports.VertexProvider = VertexProvider;
18340
19676
  exports.agentVerdictRule = agentVerdictRule;
19677
+ exports.allowsSampling = allowsSampling;
18341
19678
  exports.appendBoundTools = appendBoundTools;
19679
+ exports.applyDropParams = applyDropParams;
19680
+ exports.asModelRequestError = asModelRequestError;
18342
19681
  exports.auditLog = auditLog;
18343
19682
  exports.azureContentSafety = azureContentSafety;
18344
19683
  exports.bedrockGuardrail = bedrockGuardrail;
@@ -18353,6 +19692,7 @@ exports.canOpenSkill = canOpenSkill;
18353
19692
  exports.canSpawn = canSpawn;
18354
19693
  exports.captureSnapshot = captureSnapshot;
18355
19694
  exports.checkpointing = checkpointing;
19695
+ exports.clearLearnedModelRules = clearLearnedModelRules;
18356
19696
  exports.codePresenceRule = codePresenceRule;
18357
19697
  exports.coerceToolArgs = coerceToolArgs;
18358
19698
  exports.cognipeerGuardrail = cognipeerGuardrail;
@@ -18415,6 +19755,7 @@ exports.detectTextFormat = detectTextFormat;
18415
19755
  exports.detectVolatileContent = detectVolatileContent;
18416
19756
  exports.digestToolInputArguments = digestToolInputArguments;
18417
19757
  exports.digestToolInputValue = digestToolInputValue;
19758
+ exports.effortBudget = effortBudget;
18418
19759
  exports.emptyUsage = emptyUsage;
18419
19760
  exports.evaluateGuardrails = evaluateGuardrails;
18420
19761
  exports.extractLatestUserQuery = extractLatestUserQuery;
@@ -18428,6 +19769,7 @@ exports.fromLangchainTools = fromLangchainTools;
18428
19769
  exports.fromNativeProvider = fromNativeProvider;
18429
19770
  exports.generateSpanId = generateSpanId;
18430
19771
  exports.generateTraceId = generateTraceId;
19772
+ exports.getLearnedModelRulePatch = getLearnedModelRulePatch;
18431
19773
  exports.getModelCapabilities = getModelCapabilities;
18432
19774
  exports.getNestedValue = getNestedValue;
18433
19775
  exports.getResolvedSmartConfig = getResolvedSmartConfig;
@@ -18448,12 +19790,16 @@ exports.jsonSchemaRule = jsonSchemaRule;
18448
19790
  exports.langfuseDefaultPayload = defaultBuildPayload;
18449
19791
  exports.langfuseTracing = langfuseTracing;
18450
19792
  exports.languageGuard = languageGuard;
19793
+ exports.listLearnedModelRules = listLearnedModelRules;
18451
19794
  exports.loadSkillsFromDisk = loadSkillsFromDisk;
18452
19795
  exports.mapConsoleHookVerdict = mapConsoleHookVerdict;
19796
+ exports.mapReasoningEffort = mapReasoningEffort;
18453
19797
  exports.mapTextParts = mapTextParts;
18454
19798
  exports.mcp = mcp;
18455
19799
  exports.mediaKindOf = mediaKindOf;
18456
19800
  exports.mediaPolicy = mediaPolicy;
19801
+ exports.mergeRulePatch = mergeRulePatch;
19802
+ exports.modelIdCandidates = modelIdCandidates;
18457
19803
  exports.normalizeSmartAgentOptions = normalizeSmartAgentOptions;
18458
19804
  exports.normalizeVerdicts = normalizeVerdicts;
18459
19805
  exports.openAIModeration = openAIModeration;
@@ -18461,6 +19807,7 @@ exports.otelTracing = otelTracing;
18461
19807
  exports.otlpSink = otlpSink;
18462
19808
  exports.outputGuard = outputGuard;
18463
19809
  exports.parseApprovalDecision = parseApprovalDecision;
19810
+ exports.parseModelError = parseModelError;
18464
19811
  exports.parseSkillFrontmatter = parseSkillFrontmatter;
18465
19812
  exports.pathSandbox = pathSandbox;
18466
19813
  exports.piiRedaction = piiRedaction;
@@ -18473,14 +19820,17 @@ exports.prepareStrictToolMenu = prepareStrictToolMenu;
18473
19820
  exports.promptInjectionGuard = promptInjectionGuard;
18474
19821
  exports.rateLimit = rateLimit;
18475
19822
  exports.readMemoryFacts = readMemoryFacts;
19823
+ exports.readTraceContinuation = readTraceContinuation;
18476
19824
  exports.redactText = redactText;
18477
19825
  exports.regexGuardrail = regexGuardrail;
18478
19826
  exports.regexRule = regexRule;
19827
+ exports.rememberModelRulePatch = rememberModelRulePatch;
18479
19828
  exports.replaceTextContent = replaceTextContent;
18480
19829
  exports.resolveAvailableSkills = resolveAvailableSkills;
18481
19830
  exports.resolveAvailableSubagents = resolveAvailableSubagents;
18482
19831
  exports.resolveInputRetention = resolveInputRetention;
18483
19832
  exports.resolveMemoryStore = resolveMemoryStore;
19833
+ exports.resolveModelRule = resolveModelRule;
18484
19834
  exports.resolveReasoning = resolveReasoning;
18485
19835
  exports.resolveStrategy = resolveStrategy;
18486
19836
  exports.resolveSubagentTools = resolveSubagentTools;
@@ -18491,6 +19841,7 @@ exports.responseCache = responseCache;
18491
19841
  exports.restoreSnapshot = restoreSnapshot;
18492
19842
  exports.restoreToolCalls = restoreToolCalls;
18493
19843
  exports.runSmartAgentEvalHarness = runSmartAgentEvalHarness;
19844
+ exports.runWithModelAdjustments = runWithModelAdjustments;
18494
19845
  exports.scoreItemsByRelevance = scoreItemsByRelevance;
18495
19846
  exports.searchSkills = searchSkills;
18496
19847
  exports.seedChildMessages = seedChildMessages;
@@ -18506,6 +19857,7 @@ exports.toStrictCompatible = toStrictCompatible;
18506
19857
  exports.toStrictToolSchema = toStrictToolSchema;
18507
19858
  exports.tokenize = tokenize;
18508
19859
  exports.toolPolicy = toolPolicy;
19860
+ exports.traceContinuationOf = traceContinuationOf;
18509
19861
  exports.traceSessionToOtlp = traceSessionToOtlp;
18510
19862
  exports.validateReasoningConfig = validateReasoningConfig;
18511
19863
  exports.validateToolArgs = validateToolArgs;