@cognipeer/agent-sdk 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -3345,15 +3345,40 @@ function countToolCallsInLastTurn(state) {
3345
3345
  function stripReflectionAskMessages(messages) {
3346
3346
  return messages.filter((m) => !(m?.role === "user" && m?.name === REFLECTION_ASK_NAME));
3347
3347
  }
3348
+ function lastTwoToolTurns(state) {
3349
+ const groups = [];
3350
+ let cur = [];
3351
+ let sawTool = false;
3352
+ for (let i = state.messages.length - 1; i >= 0; i--) {
3353
+ const m = state.messages[i];
3354
+ if (m?.role === "tool") {
3355
+ if (m.name) cur.push(m.name);
3356
+ sawTool = true;
3357
+ } else if (m?.role === "assistant") {
3358
+ if (sawTool) {
3359
+ groups.push(cur);
3360
+ cur = [];
3361
+ sawTool = false;
3362
+ if (groups.length >= 2) break;
3363
+ }
3364
+ }
3365
+ }
3366
+ if (sawTool && cur.length) groups.push(cur);
3367
+ return { current: groups[0] ?? [], previous: groups[1] ?? [] };
3368
+ }
3348
3369
  function shouldRunReflection(cadence, state, ranToolsThisTurn) {
3349
3370
  if (cadence === "off") return false;
3350
3371
  if (cadence === "every_turn") return true;
3351
3372
  if (cadence === "after_tool") return ranToolsThisTurn;
3373
+ if (cadence === "initial_then_after_tool") return ranToolsThisTurn;
3352
3374
  if (cadence === "on_branch") {
3353
- const reflections = state.reflections || [];
3354
- const lastReflectionTurn = reflections[reflections.length - 1]?.turn ?? -1;
3355
- const currentTurnId = state.toolCallCount ?? 0;
3356
- return currentTurnId - lastReflectionTurn >= 2;
3375
+ const { current, previous } = lastTwoToolTurns(state);
3376
+ if (previous.length === 0) return true;
3377
+ const a = new Set(current);
3378
+ const b = new Set(previous);
3379
+ for (const n of a) if (!b.has(n)) return true;
3380
+ for (const n of b) if (!a.has(n)) return true;
3381
+ return false;
3357
3382
  }
3358
3383
  return false;
3359
3384
  }
@@ -3370,7 +3395,10 @@ function createReflectionNode(opts, resolved) {
3370
3395
  const onEvent = state.ctx?.__onEvent;
3371
3396
  const traceSession = state.ctx?.__traceSession;
3372
3397
  const maxChars = resolved.maxChars;
3373
- const promptBody = (resolved.promptTemplate || DEFAULT_REFLECT_PROMPT).replace(/\$\{maxChars\}/g, String(maxChars));
3398
+ const turnNow = state.toolCallCount ?? 0;
3399
+ const hookCtx = { state, turn: turnNow, trigger, ranToolsThisTurn: true };
3400
+ const defaultPrompt = (resolved.promptTemplate || DEFAULT_REFLECT_PROMPT).replace(/\$\{maxChars\}/g, String(maxChars));
3401
+ const promptBody = resolved.buildPrompt ? String(resolved.buildPrompt({ ...hookCtx, defaultPrompt, maxChars }) ?? defaultPrompt) : defaultPrompt;
3374
3402
  const baseMessages = stripReflectionAskMessages([...state.messages]);
3375
3403
  const askMessage = {
3376
3404
  role: "user",
@@ -3404,6 +3432,8 @@ function createReflectionNode(opts, resolved) {
3404
3432
  const rawText = extractText(response);
3405
3433
  const text = trimToChars(String(rawText ?? "").trim(), maxChars);
3406
3434
  if (!text) return {};
3435
+ const prevText = (state.reflections?.[state.reflections.length - 1]?.text || "").trim();
3436
+ if (prevText && isNearDuplicate(prevText, text)) return {};
3407
3437
  const rawUsage = response?.usage_metadata || response?.response_metadata?.usage || response?.usage || response?.additional_kwargs?.usage;
3408
3438
  const usage = normalizeUsage(rawUsage);
3409
3439
  const { ids, names } = countToolCallsInLastTurn(state);
@@ -3452,6 +3482,12 @@ function createReflectionNode(opts, resolved) {
3452
3482
  } catch (e) {
3453
3483
  }
3454
3484
  }
3485
+ if (resolved.onReflection) {
3486
+ try {
3487
+ resolved.onReflection(record, hookCtx);
3488
+ } catch {
3489
+ }
3490
+ }
3455
3491
  const nextMessages = stripReflectionAskMessages([...state.messages]);
3456
3492
  const reflections = [...state.reflections || [], record];
3457
3493
  const kept = reflections.slice(-Math.max(1, resolved.keepLast));
@@ -3468,7 +3504,20 @@ ${r.text}
3468
3504
  metadata: { reflectionId: r.id, reflectionTurn: r.turn, reflectionTrigger: r.trigger }
3469
3505
  });
3470
3506
  }
3471
- return { messages: prunedMessages, reflections };
3507
+ const patch = { messages: prunedMessages, reflections };
3508
+ if (resolved.feedTo === "memory") {
3509
+ const fact = {
3510
+ key: `reflection_turn_${turn}`,
3511
+ value: text,
3512
+ sourceTurn: turn,
3513
+ confidence: 0.3,
3514
+ tags: ["reflection"]
3515
+ };
3516
+ patch.memoryFacts = [...state.memoryFacts || [], fact];
3517
+ } else if (resolved.feedTo === "plan" && state.plan) {
3518
+ patch.plan = { ...state.plan, lastReflection: text };
3519
+ }
3520
+ return patch;
3472
3521
  };
3473
3522
  }
3474
3523
  function extractText(chunk) {
@@ -3497,24 +3546,75 @@ function safeRandomId() {
3497
3546
  return `r_${Math.random().toString(36).slice(2, 10)}_${Date.now().toString(36)}`;
3498
3547
  }
3499
3548
  }
3549
+ function isNearDuplicate(a, b) {
3550
+ const norm = (s) => s.toLowerCase().replace(/\s+/g, " ").trim();
3551
+ const na = norm(a);
3552
+ const nb = norm(b);
3553
+ if (na === nb) return true;
3554
+ const tokensA = new Set(na.split(" ").filter(Boolean));
3555
+ const tokensB = new Set(nb.split(" ").filter(Boolean));
3556
+ if (tokensA.size === 0 || tokensB.size === 0) return false;
3557
+ let inter = 0;
3558
+ for (const t of tokensA) if (tokensB.has(t)) inter++;
3559
+ const union = tokensA.size + tokensB.size - inter;
3560
+ return union > 0 && inter / union >= 0.9;
3561
+ }
3500
3562
 
3501
3563
  // src/smart/reasoning.ts
3502
3564
  var LEVEL_DEFAULTS = {
3565
+ minimal: {
3566
+ native: { effort: "minimal" },
3567
+ reflection: { maxTokens: 150, maxChars: 400, cadence: "off" }
3568
+ },
3503
3569
  low: {
3504
3570
  native: { effort: "low" },
3505
3571
  reflection: { maxTokens: 200, maxChars: 600, cadence: "on_branch" }
3506
3572
  },
3507
3573
  medium: {
3508
3574
  native: { effort: "medium" },
3509
- reflection: { maxTokens: 350, maxChars: 1200, cadence: "after_tool" }
3575
+ reflection: { maxTokens: 350, maxChars: 1200, cadence: "initial_then_after_tool" }
3510
3576
  },
3511
3577
  high: {
3512
3578
  native: { effort: "high" },
3513
- reflection: { maxTokens: 600, maxChars: 2e3, cadence: "after_tool" }
3579
+ reflection: { maxTokens: 600, maxChars: 2e3, cadence: "initial_then_after_tool" }
3514
3580
  }
3515
3581
  };
3582
+ var VALID_LEVELS = ["minimal", "low", "medium", "high"];
3583
+ var VALID_CADENCES = ["off", "every_turn", "after_tool", "on_branch", "initial_then_after_tool"];
3584
+ var VALID_EFFORTS = ["minimal", "low", "medium", "high"];
3585
+ function validateReasoningConfig(input) {
3586
+ if (!input) return;
3587
+ if (input.level !== void 0 && !VALID_LEVELS.includes(input.level)) {
3588
+ throw new Error(`reasoning.level must be one of ${VALID_LEVELS.join(", ")} (got "${input.level}")`);
3589
+ }
3590
+ if (input.native) {
3591
+ const n = input.native;
3592
+ if (n.effort !== void 0 && !VALID_EFFORTS.includes(n.effort)) {
3593
+ throw new Error(`reasoning.native.effort must be one of ${VALID_EFFORTS.join(", ")} (got "${n.effort}")`);
3594
+ }
3595
+ if (n.budgetTokens !== void 0 && (!Number.isFinite(n.budgetTokens) || n.budgetTokens < 0)) {
3596
+ throw new Error(`reasoning.native.budgetTokens must be a non-negative number (got ${n.budgetTokens})`);
3597
+ }
3598
+ }
3599
+ if (input.reflection) {
3600
+ const r = input.reflection;
3601
+ if (r.cadence !== void 0 && !VALID_CADENCES.includes(r.cadence)) {
3602
+ throw new Error(`reasoning.reflection.cadence must be one of ${VALID_CADENCES.join(", ")} (got "${r.cadence}")`);
3603
+ }
3604
+ if (r.maxPerRun !== void 0 && (!Number.isFinite(r.maxPerRun) || r.maxPerRun < 0)) {
3605
+ throw new Error(`reasoning.reflection.maxPerRun must be a non-negative number (got ${r.maxPerRun})`);
3606
+ }
3607
+ if (r.everyNTurns !== void 0 && (!Number.isFinite(r.everyNTurns) || r.everyNTurns < 1)) {
3608
+ throw new Error(`reasoning.reflection.everyNTurns must be >= 1 (got ${r.everyNTurns})`);
3609
+ }
3610
+ if (r.feedTo !== void 0 && !["memory", "plan", "none"].includes(r.feedTo)) {
3611
+ throw new Error(`reasoning.reflection.feedTo must be "memory", "plan", or "none" (got "${r.feedTo}")`);
3612
+ }
3613
+ }
3614
+ }
3516
3615
  function resolveReasoning(input) {
3517
3616
  if (!input || input.enabled === false) return void 0;
3617
+ validateReasoningConfig(input);
3518
3618
  const enabled = input.enabled ?? (input.native !== void 0 || input.reflection !== void 0 || input.level !== void 0);
3519
3619
  if (!enabled) return void 0;
3520
3620
  const level = input.level ?? "medium";
@@ -3539,7 +3639,8 @@ function resolveReflection(input, preset) {
3539
3639
  keepLast: 3,
3540
3640
  summarize: false,
3541
3641
  emitEvents: true,
3542
- everyNTurns: 1
3642
+ everyNTurns: 1,
3643
+ feedTo: "none"
3543
3644
  };
3544
3645
  }
3545
3646
  const cfg = input ?? {};
@@ -3556,7 +3657,11 @@ function resolveReflection(input, preset) {
3556
3657
  promptTemplate: cfg.promptTemplate,
3557
3658
  emitEvents: cfg.emitEvents ?? true,
3558
3659
  maxPerRun: typeof cfg.maxPerRun === "number" && cfg.maxPerRun >= 0 ? cfg.maxPerRun : void 0,
3559
- everyNTurns: Math.max(1, Math.trunc(cfg.everyNTurns ?? 1))
3660
+ everyNTurns: Math.max(1, Math.trunc(cfg.everyNTurns ?? 1)),
3661
+ feedTo: cfg.feedTo ?? "none",
3662
+ shouldReflect: cfg.shouldReflect,
3663
+ buildPrompt: cfg.buildPrompt,
3664
+ onReflection: cfg.onReflection
3560
3665
  };
3561
3666
  }
3562
3667
  function ensureArray(value) {
@@ -4282,12 +4387,14 @@ function createAgent(opts) {
4282
4387
  if (summarizationThreshold === void 0) {
4283
4388
  state = clearNeedsSummarization(state);
4284
4389
  }
4285
- if (resolvedReasoning?.native) {
4390
+ if (resolvedReasoning) {
4286
4391
  const ctx = { ...state.ctx || {} };
4287
- if (!ctx.__reasoning) {
4392
+ if (resolvedReasoning.native) {
4288
4393
  ctx.__reasoning = resolvedReasoning.native;
4289
- state = { ...state, ctx };
4394
+ } else {
4395
+ delete ctx.__reasoning;
4290
4396
  }
4397
+ state = { ...state, ctx: Object.keys(ctx).length > 0 ? ctx : void 0 };
4291
4398
  }
4292
4399
  const mergedLimits = {
4293
4400
  ...opts.limits || {},
@@ -4335,6 +4442,7 @@ function createAgent(opts) {
4335
4442
  return { breached: false };
4336
4443
  };
4337
4444
  let iterations = 0;
4445
+ let reflectionsThisRun = 0;
4338
4446
  const onStateChange = config?.onStateChange;
4339
4447
  const checkpointReason = config?.checkpointReason;
4340
4448
  let pausedStage = null;
@@ -4380,6 +4488,16 @@ function createAgent(opts) {
4380
4488
  pausedStage = stage;
4381
4489
  return true;
4382
4490
  };
4491
+ if (reflectNode && resolvedReasoning?.reflection.enabled && resolvedReasoning.reflection.cadence === "initial_then_after_tool" && resumeStage === null && !state.ctx?.__awaitingApproval && !state.ctx?.__awaitingUserQuestion && !state.messages.some((m) => m?.role === "assistant")) {
4492
+ try {
4493
+ const patch = await reflectNode(state, "initial_then_after_tool");
4494
+ if (patch && Object.keys(patch).length > 0) {
4495
+ state = { ...state, ...patch };
4496
+ reflectionsThisRun++;
4497
+ }
4498
+ } catch {
4499
+ }
4500
+ }
4383
4501
  while (iterations < iterationLimit) {
4384
4502
  iterations++;
4385
4503
  const budget = checkBudgetBreached();
@@ -4556,16 +4674,18 @@ function createAgent(opts) {
4556
4674
  if (reflectNode && resolvedReasoning?.reflection.enabled) {
4557
4675
  const cadence = resolvedReasoning.reflection.cadence;
4558
4676
  const refl = resolvedReasoning.reflection;
4559
- const reflectionsSoFar = state.reflections?.length ?? 0;
4560
4677
  const lastTurn = state.reflections?.at(-1)?.turn ?? -Infinity;
4561
4678
  const currentTurn = state.toolCallCount || 0;
4562
- const budgetExceeded = typeof refl.maxPerRun === "number" && reflectionsSoFar >= refl.maxPerRun;
4563
- const cadenceGap = currentTurn - (Number.isFinite(lastTurn) ? lastTurn : -refl.everyNTurns) < refl.everyNTurns;
4564
- if (!budgetExceeded && !cadenceGap && shouldRunReflection(cadence, state, true)) {
4679
+ const budgetExceeded = typeof refl.maxPerRun === "number" && reflectionsThisRun >= refl.maxPerRun;
4680
+ const sinceLast = currentTurn - (Number.isFinite(lastTurn) ? lastTurn : -refl.everyNTurns);
4681
+ const cadenceGap = sinceLast < refl.everyNTurns;
4682
+ const cadenceWantsIt = refl.shouldReflect ? !!refl.shouldReflect({ state, turn: currentTurn, trigger: cadence, ranToolsThisTurn: true }) : shouldRunReflection(cadence, state, true);
4683
+ if (!budgetExceeded && !cadenceGap && cadenceWantsIt) {
4565
4684
  try {
4566
4685
  const patch = await reflectNode(state, cadence);
4567
4686
  if (patch && Object.keys(patch).length > 0) {
4568
4687
  state = { ...state, ...patch };
4688
+ reflectionsThisRun++;
4569
4689
  }
4570
4690
  } catch {
4571
4691
  }
@@ -6828,6 +6948,20 @@ function parseSSEBlock(block) {
6828
6948
  }
6829
6949
 
6830
6950
  // src/providers/utils/reasoning.ts
6951
+ var ANTHROPIC_EFFORT_BUDGET = {
6952
+ minimal: 1024,
6953
+ low: 2048,
6954
+ medium: 8192,
6955
+ high: 16384
6956
+ };
6957
+ var GEMINI_EFFORT_BUDGET = {
6958
+ minimal: 512,
6959
+ low: 1024,
6960
+ medium: 4096,
6961
+ high: 12e3
6962
+ };
6963
+ var THINKING_OUTPUT_RESERVE = 1024;
6964
+ var THINKING_MIN_MAX_TOKENS = 4096;
6831
6965
  function applyOpenAIReasoning(body, reasoning, mode = "legacy_effort") {
6832
6966
  if (!reasoning) return;
6833
6967
  const effort = normalizeEffort(reasoning.effort);
@@ -6844,21 +6978,56 @@ function applyOpenAIReasoning(body, reasoning, mode = "legacy_effort") {
6844
6978
  }
6845
6979
  function applyAnthropicReasoning(body, reasoning) {
6846
6980
  if (!reasoning) return;
6847
- const budget = resolveBudget(reasoning, { low: 2048, medium: 8192, high: 16384, minimal: 1024 });
6981
+ let budget = resolveBudget(reasoning, ANTHROPIC_EFFORT_BUDGET);
6848
6982
  if (!budget) return;
6983
+ const currentMax = typeof body.max_tokens === "number" ? body.max_tokens : 0;
6984
+ const requiredMax = Math.max(THINKING_MIN_MAX_TOKENS, budget + THINKING_OUTPUT_RESERVE);
6985
+ if (currentMax < requiredMax) {
6986
+ body.max_tokens = requiredMax;
6987
+ }
6988
+ budget = clampBudget(budget, body.max_tokens);
6849
6989
  const thinking = { type: "enabled", budget_tokens: budget };
6850
6990
  if (reasoning.providerExtras) Object.assign(thinking, reasoning.providerExtras);
6851
6991
  body.thinking = thinking;
6992
+ stripSamplingParams(body, ["temperature", "top_p", "top_k"]);
6993
+ }
6994
+ function applyBedrockReasoning(body, reasoning) {
6995
+ if (!reasoning) return;
6996
+ let budget = resolveBudget(reasoning, ANTHROPIC_EFFORT_BUDGET);
6997
+ if (!budget) return;
6998
+ const inference = body.inferenceConfig ?? (body.inferenceConfig = {});
6999
+ const currentMax = typeof inference.maxTokens === "number" ? inference.maxTokens : 0;
7000
+ const requiredMax = Math.max(THINKING_MIN_MAX_TOKENS, budget + THINKING_OUTPUT_RESERVE);
7001
+ if (currentMax < requiredMax) {
7002
+ inference.maxTokens = requiredMax;
7003
+ }
7004
+ budget = clampBudget(budget, inference.maxTokens);
7005
+ const extra = body.additionalModelRequestFields ?? (body.additionalModelRequestFields = {});
7006
+ const thinking = { type: "enabled", budget_tokens: budget };
7007
+ if (reasoning.providerExtras) Object.assign(thinking, reasoning.providerExtras);
7008
+ extra.thinking = thinking;
7009
+ stripSamplingParams(inference, ["temperature", "topP"]);
6852
7010
  }
6853
7011
  function applyGeminiReasoning(generationConfig, reasoning) {
6854
7012
  if (!reasoning) return;
6855
- const budget = resolveBudget(reasoning, { low: 1024, medium: 4096, high: 12e3, minimal: 512 });
7013
+ const budget = resolveBudget(reasoning, GEMINI_EFFORT_BUDGET);
6856
7014
  if (budget == null) return;
6857
7015
  const thinkingConfig = { thinkingBudget: budget };
6858
7016
  if (reasoning.includeThoughts) thinkingConfig.includeThoughts = true;
6859
7017
  if (reasoning.providerExtras) Object.assign(thinkingConfig, reasoning.providerExtras);
6860
7018
  generationConfig.thinkingConfig = thinkingConfig;
6861
7019
  }
7020
+ function stripSamplingParams(target, keys) {
7021
+ for (const k of keys) {
7022
+ if (k in target) delete target[k];
7023
+ }
7024
+ }
7025
+ function clampBudget(budget, maxTokens) {
7026
+ if (typeof maxTokens !== "number" || maxTokens <= 0) return budget;
7027
+ const ceiling = maxTokens - THINKING_OUTPUT_RESERVE;
7028
+ if (ceiling < 1024) return Math.max(1024, Math.floor(maxTokens / 2));
7029
+ return Math.min(budget, ceiling);
7030
+ }
6862
7031
  function normalizeEffort(effort) {
6863
7032
  if (!effort) return void 0;
6864
7033
  const allowed = ["minimal", "low", "medium", "high"];
@@ -6887,13 +7056,38 @@ var OpenAIProvider = class extends BaseProvider {
6887
7056
  };
6888
7057
  if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
6889
7058
  }
7059
+ /** True when the request targets a reasoning model (o-series / gpt-5) so we
7060
+ * should route through the Responses API, which exposes `reasoning.effort`
7061
+ * and reasoning summaries. Chat Completions silently drops these. */
7062
+ useResponsesApi(request) {
7063
+ if (!request.reasoning) return false;
7064
+ return isReasoningModel(request.model || this.defaultModel);
7065
+ }
6890
7066
  async complete(request) {
7067
+ if (this.useResponsesApi(request)) {
7068
+ return this.completeResponses(request);
7069
+ }
6891
7070
  const body = this.buildRequestBody(request, false);
6892
7071
  const res = await this.doFetch(body);
6893
7072
  const json = await res.json();
6894
7073
  return this.parseResponse(json);
6895
7074
  }
6896
7075
  async *completeStream(request) {
7076
+ if (this.useResponsesApi(request)) {
7077
+ const response = await this.completeResponses(request);
7078
+ yield {
7079
+ id: response.id,
7080
+ model: response.model,
7081
+ delta: {
7082
+ content: response.content ?? void 0,
7083
+ toolCalls: response.toolCalls.length > 0 ? response.toolCalls : void 0,
7084
+ reasoning: response.reasoning
7085
+ },
7086
+ usage: response.usage,
7087
+ finishReason: response.finishReason
7088
+ };
7089
+ return;
7090
+ }
6897
7091
  const body = this.buildRequestBody(request, true);
6898
7092
  const res = await this.doFetch(body);
6899
7093
  if (!res.body) throw new ProviderError("No response body for stream", this.providerName);
@@ -6992,6 +7186,156 @@ var OpenAIProvider = class extends BaseProvider {
6992
7186
  applyOpenAIReasoning(body, request.reasoning, "legacy_effort");
6993
7187
  return body;
6994
7188
  }
7189
+ // ─── Responses API (reasoning models) ──────────────────────────────────────
7190
+ async completeResponses(request) {
7191
+ const body = this.buildResponsesBody(request);
7192
+ const res = await this.responsesFetch(body);
7193
+ const json = await res.json();
7194
+ return this.parseResponsesResponse(json, request.model || this.defaultModel);
7195
+ }
7196
+ buildResponsesBody(request) {
7197
+ const body = {
7198
+ model: request.model || this.defaultModel,
7199
+ input: request.messages.map((m) => this.toResponsesInput(m))
7200
+ };
7201
+ if (request.maxTokens != null) body.max_output_tokens = request.maxTokens;
7202
+ if (request.topP != null) body.top_p = request.topP;
7203
+ if (request.tools?.length) {
7204
+ body.tools = request.tools.map((t) => ({
7205
+ type: "function",
7206
+ name: t.name,
7207
+ description: t.description,
7208
+ parameters: t.parameters,
7209
+ ...t.strict ? { strict: true } : {}
7210
+ }));
7211
+ }
7212
+ if (request.toolChoice != null && request.tools?.length) {
7213
+ if (typeof request.toolChoice === "string") {
7214
+ body.tool_choice = request.toolChoice;
7215
+ } else {
7216
+ body.tool_choice = { type: "function", name: request.toolChoice.name };
7217
+ }
7218
+ }
7219
+ if (request.responseFormat) {
7220
+ if (request.responseFormat.type === "json_schema") {
7221
+ body.text = {
7222
+ format: {
7223
+ type: "json_schema",
7224
+ name: request.responseFormat.name ?? "response",
7225
+ schema: request.responseFormat.schema,
7226
+ strict: true
7227
+ }
7228
+ };
7229
+ } else if (request.responseFormat.type === "json_object") {
7230
+ body.text = { format: { type: "json_object" } };
7231
+ }
7232
+ }
7233
+ if (request.extra) Object.assign(body, request.extra);
7234
+ applyOpenAIReasoning(body, request.reasoning, "responses");
7235
+ return body;
7236
+ }
7237
+ /** Maps a unified message to a Responses API `input` item. */
7238
+ toResponsesInput(m) {
7239
+ if (m.role === "tool" && m.toolCallId) {
7240
+ const output = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
7241
+ return { type: "function_call_output", call_id: m.toolCallId, output };
7242
+ }
7243
+ if (m.role === "assistant" && m.toolCalls?.length) {
7244
+ return {
7245
+ __assistantWithCalls: true,
7246
+ text: typeof m.content === "string" ? m.content : "",
7247
+ calls: m.toolCalls.map((tc) => ({
7248
+ type: "function_call",
7249
+ call_id: tc.id,
7250
+ name: tc.name,
7251
+ arguments: tc.arguments
7252
+ }))
7253
+ };
7254
+ }
7255
+ const role = m.role === "system" ? "system" : m.role === "assistant" ? "assistant" : "user";
7256
+ const textType = role === "assistant" ? "output_text" : "input_text";
7257
+ if (typeof m.content === "string") {
7258
+ return { role, content: [{ type: textType, text: m.content }] };
7259
+ }
7260
+ const content = m.content.map((part) => {
7261
+ if (part.type === "text") return { type: textType, text: part.text };
7262
+ if (part.type === "image") {
7263
+ const src = part.source;
7264
+ const url = src.type === "url" ? src.url : `data:${src.mediaType};base64,${src.data}`;
7265
+ return { type: "input_image", image_url: url };
7266
+ }
7267
+ return { type: textType, text: "" };
7268
+ });
7269
+ return { role, content };
7270
+ }
7271
+ async responsesFetch(body) {
7272
+ body.input = expandResponsesInput(body.input);
7273
+ const url = `${this.baseURL}/responses`;
7274
+ const headers = {
7275
+ "Content-Type": "application/json",
7276
+ Authorization: `Bearer ${this.apiKey}`,
7277
+ ...this.defaultHeaders
7278
+ };
7279
+ const payload = JSON.stringify(body);
7280
+ const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload }));
7281
+ if (!res.ok) {
7282
+ const text = await res.text().catch(() => "");
7283
+ throw new ProviderError(
7284
+ `OpenAI Responses API error ${res.status}: ${text}`,
7285
+ this.providerName,
7286
+ res.status,
7287
+ text
7288
+ );
7289
+ }
7290
+ return res;
7291
+ }
7292
+ parseResponsesResponse(json, modelId) {
7293
+ const textParts = [];
7294
+ const toolCalls = [];
7295
+ const summaryParts = [];
7296
+ for (const item of json.output ?? []) {
7297
+ if (item.type === "message") {
7298
+ for (const c of item.content ?? []) {
7299
+ if (c.type === "output_text" && typeof c.text === "string") textParts.push(c.text);
7300
+ }
7301
+ } else if (item.type === "function_call") {
7302
+ toolCalls.push({
7303
+ id: item.call_id ?? item.id ?? "",
7304
+ name: item.name ?? "",
7305
+ arguments: typeof item.arguments === "string" ? item.arguments : JSON.stringify(item.arguments ?? {})
7306
+ });
7307
+ } else if (item.type === "reasoning") {
7308
+ for (const s of item.summary ?? []) {
7309
+ if (typeof s.text === "string") summaryParts.push(s.text);
7310
+ }
7311
+ }
7312
+ }
7313
+ if (textParts.length === 0 && typeof json.output_text === "string") {
7314
+ textParts.push(json.output_text);
7315
+ }
7316
+ const usage = json.usage ?? {};
7317
+ const reasoningTokens = usage.output_tokens_details?.reasoning_tokens ?? 0;
7318
+ const tokenUsage = {
7319
+ inputTokens: usage.input_tokens ?? 0,
7320
+ outputTokens: usage.output_tokens ?? 0,
7321
+ totalTokens: usage.total_tokens ?? (usage.input_tokens ?? 0) + (usage.output_tokens ?? 0),
7322
+ cachedInputTokens: usage.input_tokens_details?.cached_tokens ?? 0,
7323
+ cachedWriteTokens: 0,
7324
+ cachedOutputTokens: 0,
7325
+ reasoningTokens
7326
+ };
7327
+ const finishReason = toolCalls.length > 0 ? "tool_calls" : json.status === "incomplete" ? "length" : "stop";
7328
+ return {
7329
+ id: json.id ?? "",
7330
+ model: json.model ?? modelId,
7331
+ content: textParts.join("") || null,
7332
+ toolCalls,
7333
+ usage: tokenUsage,
7334
+ finishReason,
7335
+ reasoning: summaryParts.length > 0 ? { summary: summaryParts.join("\n").trim() || void 0 } : void 0,
7336
+ raw: json
7337
+ };
7338
+ }
6995
7339
  toOpenAIMessage(m) {
6996
7340
  const msg = { role: m.role };
6997
7341
  if (typeof m.content === "string") {
@@ -7084,6 +7428,25 @@ var OpenAIProvider = class extends BaseProvider {
7084
7428
  };
7085
7429
  }
7086
7430
  };
7431
+ function isReasoningModel(model) {
7432
+ if (!model) return false;
7433
+ const m = model.toLowerCase();
7434
+ return /(^|[/_-])o[0-9]/.test(m) || m.startsWith("o1") || m.startsWith("o3") || m.startsWith("o4") || m.includes("gpt-5") || m.includes("gpt5");
7435
+ }
7436
+ function expandResponsesInput(input) {
7437
+ const out = [];
7438
+ for (const item of input) {
7439
+ if (item && item.__assistantWithCalls) {
7440
+ if (item.text) {
7441
+ out.push({ role: "assistant", content: [{ type: "output_text", text: item.text }] });
7442
+ }
7443
+ for (const call of item.calls) out.push(call);
7444
+ } else {
7445
+ out.push(item);
7446
+ }
7447
+ }
7448
+ return out;
7449
+ }
7087
7450
  function mapFinishReason(reason) {
7088
7451
  switch (reason) {
7089
7452
  case "stop":
@@ -7129,6 +7492,7 @@ var AnthropicProvider = class extends BaseProvider {
7129
7492
  let cacheReadInputTokens = 0;
7130
7493
  let cacheCreationInputTokens = 0;
7131
7494
  const currentToolCalls = /* @__PURE__ */ new Map();
7495
+ const reasoningBlocks = /* @__PURE__ */ new Map();
7132
7496
  for await (const event of parseSSEStream(res.body)) {
7133
7497
  let data;
7134
7498
  try {
@@ -7158,6 +7522,10 @@ var AnthropicProvider = class extends BaseProvider {
7158
7522
  name: block.name ?? "",
7159
7523
  arguments: ""
7160
7524
  });
7525
+ } else if (block?.type === "thinking") {
7526
+ reasoningBlocks.set(idx, { type: "thinking", text: block.thinking ?? "", signature: block.signature });
7527
+ } else if (block?.type === "redacted_thinking") {
7528
+ reasoningBlocks.set(idx, { type: "redacted_thinking", data: block.data });
7161
7529
  }
7162
7530
  break;
7163
7531
  }
@@ -7170,6 +7538,19 @@ var AnthropicProvider = class extends BaseProvider {
7170
7538
  model,
7171
7539
  delta: { content: delta.text }
7172
7540
  };
7541
+ } else if (delta?.type === "thinking_delta" && delta.thinking) {
7542
+ const blk = reasoningBlocks.get(idx) ?? { type: "thinking", text: "" };
7543
+ blk.text = (blk.text ?? "") + delta.thinking;
7544
+ reasoningBlocks.set(idx, blk);
7545
+ yield {
7546
+ id,
7547
+ model,
7548
+ delta: { reasoning: { summary: delta.thinking } }
7549
+ };
7550
+ } else if (delta?.type === "signature_delta" && delta.signature) {
7551
+ const blk = reasoningBlocks.get(idx) ?? { type: "thinking", text: "" };
7552
+ blk.signature = (blk.signature ?? "") + delta.signature;
7553
+ reasoningBlocks.set(idx, blk);
7173
7554
  } else if (delta?.type === "input_json_delta" && delta.partial_json) {
7174
7555
  const tc = currentToolCalls.get(idx);
7175
7556
  if (tc) {
@@ -7202,10 +7583,13 @@ var AnthropicProvider = class extends BaseProvider {
7202
7583
  cachedOutputTokens: 0,
7203
7584
  reasoningTokens: 0
7204
7585
  };
7586
+ const blocks = [...reasoningBlocks.values()].filter(
7587
+ (b) => b.type === "thinking" && b.signature || b.type === "redacted_thinking" && b.data
7588
+ );
7205
7589
  yield {
7206
7590
  id,
7207
7591
  model,
7208
- delta: {},
7592
+ delta: blocks.length > 0 ? { reasoning: { blocks } } : {},
7209
7593
  finishReason,
7210
7594
  usage
7211
7595
  };
@@ -7297,6 +7681,7 @@ var AnthropicProvider = class extends BaseProvider {
7297
7681
  }
7298
7682
  if (m.role === "assistant" && m.toolCalls?.length) {
7299
7683
  const content = [];
7684
+ appendReasoningBlocks(content, m.reasoning?.blocks);
7300
7685
  if (typeof m.content === "string" && m.content) {
7301
7686
  content.push({ type: "text", text: m.content });
7302
7687
  }
@@ -7310,6 +7695,13 @@ var AnthropicProvider = class extends BaseProvider {
7310
7695
  }
7311
7696
  return { role: "assistant", content };
7312
7697
  }
7698
+ if (m.role === "assistant" && m.reasoning?.blocks?.length) {
7699
+ const content = [];
7700
+ appendReasoningBlocks(content, m.reasoning.blocks);
7701
+ const text = typeof m.content === "string" ? m.content : "";
7702
+ if (text) content.push({ type: "text", text });
7703
+ return { role: "assistant", content };
7704
+ }
7313
7705
  const msg = { role: m.role };
7314
7706
  if (typeof m.content === "string") {
7315
7707
  msg.content = m.content;
@@ -7365,9 +7757,20 @@ var AnthropicProvider = class extends BaseProvider {
7365
7757
  parseResponse(json) {
7366
7758
  const textParts = [];
7367
7759
  const toolCalls = [];
7760
+ const reasoningBlocks = [];
7761
+ const summaryParts = [];
7368
7762
  for (const block of json.content ?? []) {
7369
7763
  if (block.type === "text") {
7370
7764
  textParts.push(block.text);
7765
+ } else if (block.type === "thinking") {
7766
+ reasoningBlocks.push({
7767
+ type: "thinking",
7768
+ text: block.thinking ?? "",
7769
+ signature: block.signature
7770
+ });
7771
+ if (block.thinking) summaryParts.push(block.thinking);
7772
+ } else if (block.type === "redacted_thinking") {
7773
+ reasoningBlocks.push({ type: "redacted_thinking", data: block.data });
7371
7774
  } else if (block.type === "tool_use") {
7372
7775
  toolCalls.push({
7373
7776
  id: block.id,
@@ -7384,6 +7787,9 @@ var AnthropicProvider = class extends BaseProvider {
7384
7787
  cachedInputTokens: usage.cache_read_input_tokens ?? 0,
7385
7788
  cachedWriteTokens: usage.cache_creation_input_tokens ?? 0,
7386
7789
  cachedOutputTokens: 0,
7790
+ // Anthropic bills thinking tokens inside `output_tokens` and does not
7791
+ // expose a separate count, so reasoningTokens stays 0 here (cost is still
7792
+ // captured via outputTokens). See docs for the limitation.
7387
7793
  reasoningTokens: 0
7388
7794
  };
7389
7795
  return {
@@ -7393,6 +7799,7 @@ var AnthropicProvider = class extends BaseProvider {
7393
7799
  toolCalls,
7394
7800
  usage: tokenUsage,
7395
7801
  finishReason: mapAnthropicStopReason(json.stop_reason),
7802
+ reasoning: reasoningBlocks.length > 0 ? { blocks: reasoningBlocks, summary: summaryParts.join("\n").trim() || void 0 } : void 0,
7396
7803
  raw: json
7397
7804
  };
7398
7805
  }
@@ -7417,6 +7824,18 @@ function safeJsonParse(str) {
7417
7824
  return {};
7418
7825
  }
7419
7826
  }
7827
+ function appendReasoningBlocks(content, blocks) {
7828
+ if (!blocks?.length) return;
7829
+ for (const b of blocks) {
7830
+ if (b.type === "redacted_thinking") {
7831
+ if (b.data) content.push({ type: "redacted_thinking", data: b.data });
7832
+ } else if (b.type === "thinking") {
7833
+ if (b.signature) {
7834
+ content.push({ type: "thinking", thinking: b.text ?? "", signature: b.signature });
7835
+ }
7836
+ }
7837
+ }
7838
+ }
7420
7839
 
7421
7840
  // src/providers/azure.ts
7422
7841
  var AzureProvider = class extends OpenAIProvider {
@@ -7460,7 +7879,52 @@ var AzureProvider = class extends OpenAIProvider {
7460
7879
  }
7461
7880
  return res;
7462
7881
  }
7882
+ /** Azure exposes the Responses API at /openai/responses with api-key auth.
7883
+ * Reasoning summaries require a preview api-version; callers can override via
7884
+ * the `apiVersion` config. */
7885
+ async responsesFetch(body) {
7886
+ body.input = expandAzureResponsesInput(body.input);
7887
+ const deployment = this.deploymentName ?? body.model;
7888
+ if (!deployment) {
7889
+ throw new ProviderError("Azure requires a deploymentName or model", this.providerName);
7890
+ }
7891
+ const url = `${this.endpoint}/openai/responses?api-version=${this.apiVersion}`;
7892
+ const payload = JSON.stringify({ ...body, model: deployment });
7893
+ const res = await fetch(url, {
7894
+ method: "POST",
7895
+ headers: {
7896
+ "Content-Type": "application/json",
7897
+ "api-key": this.azureApiKey,
7898
+ ...this.defaultHeaders
7899
+ },
7900
+ body: payload
7901
+ });
7902
+ if (!res.ok) {
7903
+ const text = await res.text().catch(() => "");
7904
+ throw new ProviderError(
7905
+ `Azure OpenAI Responses API error ${res.status}: ${text}`,
7906
+ this.providerName,
7907
+ res.status,
7908
+ text
7909
+ );
7910
+ }
7911
+ return res;
7912
+ }
7463
7913
  };
7914
+ function expandAzureResponsesInput(input) {
7915
+ const out = [];
7916
+ for (const item of input) {
7917
+ if (item && item.__assistantWithCalls) {
7918
+ if (item.text) {
7919
+ out.push({ role: "assistant", content: [{ type: "output_text", text: item.text }] });
7920
+ }
7921
+ for (const call of item.calls) out.push(call);
7922
+ } else {
7923
+ out.push(item);
7924
+ }
7925
+ }
7926
+ return out;
7927
+ }
7464
7928
 
7465
7929
  // src/providers/openaiCompatible.ts
7466
7930
  var OpenAICompatibleProvider = class extends OpenAIProvider {
@@ -7633,6 +8097,7 @@ var BedrockProvider = class extends BaseProvider {
7633
8097
  if (request.extra) {
7634
8098
  Object.assign(body, request.extra);
7635
8099
  }
8100
+ applyBedrockReasoning(body, request.reasoning);
7636
8101
  return body;
7637
8102
  }
7638
8103
  splitSystemMessages(messages) {
@@ -7666,6 +8131,7 @@ var BedrockProvider = class extends BaseProvider {
7666
8131
  }
7667
8132
  if (m.role === "assistant" && m.toolCalls?.length) {
7668
8133
  const content2 = [];
8134
+ appendBedrockReasoning(content2, m.reasoning?.blocks);
7669
8135
  if (typeof m.content === "string" && m.content) {
7670
8136
  content2.push({ text: m.content });
7671
8137
  }
@@ -7746,9 +8212,23 @@ var BedrockProvider = class extends BaseProvider {
7746
8212
  const output = json.output?.message;
7747
8213
  const textParts = [];
7748
8214
  const toolCalls = [];
8215
+ const reasoningBlocks = [];
8216
+ const summaryParts = [];
7749
8217
  for (const block of output?.content ?? []) {
7750
8218
  if (block.text) {
7751
8219
  textParts.push(block.text);
8220
+ } else if (block.reasoningContent) {
8221
+ const rc = block.reasoningContent;
8222
+ if (rc.reasoningText) {
8223
+ reasoningBlocks.push({
8224
+ type: "thinking",
8225
+ text: rc.reasoningText.text ?? "",
8226
+ signature: rc.reasoningText.signature
8227
+ });
8228
+ if (rc.reasoningText.text) summaryParts.push(rc.reasoningText.text);
8229
+ } else if (rc.redactedContent) {
8230
+ reasoningBlocks.push({ type: "redacted_thinking", data: rc.redactedContent });
8231
+ }
7752
8232
  } else if (block.toolUse) {
7753
8233
  toolCalls.push({
7754
8234
  id: block.toolUse.toolUseId,
@@ -7765,6 +8245,7 @@ var BedrockProvider = class extends BaseProvider {
7765
8245
  cachedInputTokens: usage.cacheReadInputTokenCount ?? 0,
7766
8246
  cachedWriteTokens: usage.cacheWriteInputTokenCount ?? 0,
7767
8247
  cachedOutputTokens: 0,
8248
+ // Bedrock bills thinking tokens inside outputTokens; no separate count.
7768
8249
  reasoningTokens: 0
7769
8250
  };
7770
8251
  return {
@@ -7774,10 +8255,25 @@ var BedrockProvider = class extends BaseProvider {
7774
8255
  toolCalls,
7775
8256
  usage: tokenUsage,
7776
8257
  finishReason: mapBedrockStopReason(json.stopReason),
8258
+ reasoning: reasoningBlocks.length > 0 ? { blocks: reasoningBlocks, summary: summaryParts.join("\n").trim() || void 0 } : void 0,
7777
8259
  raw: json
7778
8260
  };
7779
8261
  }
7780
8262
  };
8263
+ function appendBedrockReasoning(content, blocks) {
8264
+ if (!blocks?.length) return;
8265
+ for (const b of blocks) {
8266
+ if (b.type === "redacted_thinking") {
8267
+ if (b.data) content.push({ reasoningContent: { redactedContent: b.data } });
8268
+ } else if (b.type === "thinking") {
8269
+ if (b.signature) {
8270
+ content.push({
8271
+ reasoningContent: { reasoningText: { text: b.text ?? "", signature: b.signature } }
8272
+ });
8273
+ }
8274
+ }
8275
+ }
8276
+ }
7781
8277
  function mapBedrockStopReason(reason) {
7782
8278
  switch (reason) {
7783
8279
  case "end_turn":
@@ -8100,10 +8596,15 @@ var VertexProvider = class extends BaseProvider {
8100
8596
  const parts = candidate?.content?.parts ?? [];
8101
8597
  const textParts = [];
8102
8598
  const toolCalls = [];
8599
+ const thoughtParts = [];
8103
8600
  let toolCallIdx = 0;
8104
8601
  for (const part of parts) {
8105
8602
  if (part.text) {
8106
- textParts.push(part.text);
8603
+ if (part.thought === true) {
8604
+ thoughtParts.push(part.text);
8605
+ } else {
8606
+ textParts.push(part.text);
8607
+ }
8107
8608
  } else if (part.functionCall) {
8108
8609
  toolCalls.push({
8109
8610
  id: `call_${toolCallIdx++}`,
@@ -8129,6 +8630,7 @@ var VertexProvider = class extends BaseProvider {
8129
8630
  toolCalls,
8130
8631
  usage: tokenUsage,
8131
8632
  finishReason: mapGeminiFinishReason(candidate?.finishReason),
8633
+ reasoning: thoughtParts.length > 0 ? { summary: thoughtParts.join("\n").trim() || void 0 } : void 0,
8132
8634
  raw: json
8133
8635
  };
8134
8636
  }
@@ -8138,7 +8640,11 @@ var VertexProvider = class extends BaseProvider {
8138
8640
  const delta = {};
8139
8641
  for (const part of parts) {
8140
8642
  if (part.text) {
8141
- delta.content = (delta.content ?? "") + part.text;
8643
+ if (part.thought === true) {
8644
+ delta.reasoning = { summary: (delta.reasoning?.summary ?? "") + part.text };
8645
+ } else {
8646
+ delta.content = (delta.content ?? "") + part.text;
8647
+ }
8142
8648
  } else if (part.functionCall) {
8143
8649
  if (!delta.toolCalls) delta.toolCalls = [];
8144
8650
  delta.toolCalls.push({
@@ -8212,12 +8718,16 @@ function fromNativeProvider(provider, options) {
8212
8718
  let fullContent = "";
8213
8719
  let allToolCalls = [];
8214
8720
  let lastUsage;
8721
+ let reasoningBlocks;
8215
8722
  const toolCallBuffers = /* @__PURE__ */ new Map();
8216
8723
  for await (const chunk of provider.completeStream(request)) {
8217
8724
  if (chunk.delta.content) {
8218
8725
  fullContent += chunk.delta.content;
8219
8726
  yield chunk.delta.content;
8220
8727
  }
8728
+ if (chunk.delta.reasoning?.blocks?.length) {
8729
+ reasoningBlocks = chunk.delta.reasoning.blocks;
8730
+ }
8221
8731
  if (chunk.delta.toolCalls) {
8222
8732
  for (const tc of chunk.delta.toolCalls) {
8223
8733
  if (tc.id) {
@@ -8238,6 +8748,9 @@ function fromNativeProvider(provider, options) {
8238
8748
  }
8239
8749
  allToolCalls = [...toolCallBuffers.values()];
8240
8750
  const finalMessage = assembleMessage(fullContent, allToolCalls, lastUsage);
8751
+ if (reasoningBlocks?.length) {
8752
+ finalMessage.reasoning = { blocks: reasoningBlocks };
8753
+ }
8241
8754
  yield finalMessage;
8242
8755
  },
8243
8756
  bindTools: (tools, _bindOptions) => {
@@ -8300,6 +8813,10 @@ function toUnifiedMessage(msg) {
8300
8813
  arguments: tc.function?.arguments ?? tc.arguments ?? "{}"
8301
8814
  }));
8302
8815
  }
8816
+ const reasoning = msg.reasoning;
8817
+ if (reasoning && (reasoning.blocks?.length || reasoning.summary)) {
8818
+ unified.reasoning = reasoning;
8819
+ }
8303
8820
  return unified;
8304
8821
  }
8305
8822
  function convertContent(content) {
@@ -8508,6 +9025,9 @@ function toBaseChatMessage(response) {
8508
9025
  function: { name: tc.name, arguments: tc.arguments }
8509
9026
  }));
8510
9027
  }
9028
+ if (response.reasoning && (response.reasoning.blocks?.length || response.reasoning.summary)) {
9029
+ msg.reasoning = response.reasoning;
9030
+ }
8511
9031
  return msg;
8512
9032
  }
8513
9033
  function assembleMessage(content, toolCalls, usage) {
@@ -8667,6 +9187,7 @@ exports.otlpSink = otlpSink;
8667
9187
  exports.readMemoryFacts = readMemoryFacts;
8668
9188
  exports.regexRule = regexRule;
8669
9189
  exports.resolveMemoryStore = resolveMemoryStore;
9190
+ exports.resolveReasoning = resolveReasoning;
8670
9191
  exports.resolveStrategy = resolveStrategy;
8671
9192
  exports.resolveToolApprovalState = resolveToolApprovalState;
8672
9193
  exports.resolveUserQuestionState = resolveUserQuestionState;
@@ -8675,6 +9196,7 @@ exports.runSmartAgentEvalHarness = runSmartAgentEvalHarness;
8675
9196
  exports.setTokenCounter = setTokenCounter;
8676
9197
  exports.startStreamingSession = startStreamingSession;
8677
9198
  exports.traceSessionToOtlp = traceSessionToOtlp;
9199
+ exports.validateReasoningConfig = validateReasoningConfig;
8678
9200
  exports.withTools = withTools;
8679
9201
  exports.writeSummaryFactsToMemory = writeSummaryFactsToMemory;
8680
9202
  //# sourceMappingURL=index.cjs.map