@cognipeer/agent-sdk 0.5.4 → 0.6.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.mjs CHANGED
@@ -5,7 +5,7 @@ import fs from 'fs';
5
5
  import path from 'path';
6
6
  import { Buffer as Buffer$1 } from 'buffer';
7
7
  import { nanoid } from 'nanoid';
8
- import crypto, { createHash, createHmac } from 'crypto';
8
+ import crypto, { randomUUID, createHash, createHmac } from 'crypto';
9
9
  import AjvModule from 'ajv';
10
10
 
11
11
  // src/adapters/langchain.ts
@@ -1968,7 +1968,9 @@ function createAgentCoreNode(opts) {
1968
1968
  const cancellationToken = state.ctx?.__cancellationToken;
1969
1969
  const abortSignal = state.ctx?.__abortSignal;
1970
1970
  const responseFormat = runtime.responseFormat;
1971
- const extractText = (chunk) => {
1971
+ const reasoningForCall = state.ctx?.__reasoning;
1972
+ const reasoningInvokeOpts = reasoningForCall ? { reasoning: reasoningForCall } : void 0;
1973
+ const extractText2 = (chunk) => {
1972
1974
  if (chunk == null) return "";
1973
1975
  if (typeof chunk === "string") return chunk;
1974
1976
  if (typeof chunk?.content === "string") return chunk.content;
@@ -1986,7 +1988,7 @@ function createAgentCoreNode(opts) {
1986
1988
  if (streamingEnabled && typeof modelWithTools.stream === "function") {
1987
1989
  let streamedText = "";
1988
1990
  let streamedMessage;
1989
- const streamOptions = { signal: abortSignal, cancellationToken, ...responseFormat };
1991
+ const streamOptions = { signal: abortSignal, cancellationToken, ...responseFormat, ...reasoningInvokeOpts };
1990
1992
  for await (const chunk of modelWithTools.stream(normalizedMessages, streamOptions)) {
1991
1993
  if (cancellationToken && cancellationToken.isCancellationRequested || abortSignal?.aborted) {
1992
1994
  break;
@@ -1994,7 +1996,7 @@ function createAgentCoreNode(opts) {
1994
1996
  if (chunk && typeof chunk === "object" && chunk.role) {
1995
1997
  streamedMessage = chunk;
1996
1998
  }
1997
- const text = extractText(chunk);
1999
+ const text = extractText2(chunk);
1998
2000
  if (text) {
1999
2001
  streamedText += text;
2000
2002
  onStream?.({ text });
@@ -2010,7 +2012,7 @@ function createAgentCoreNode(opts) {
2010
2012
  response = { role: "assistant", content: streamedText };
2011
2013
  }
2012
2014
  } else {
2013
- response = await modelWithTools.invoke(normalizedMessages, { signal: abortSignal, cancellationToken, ...responseFormat });
2015
+ response = await modelWithTools.invoke(normalizedMessages, { signal: abortSignal, cancellationToken, ...responseFormat, ...reasoningInvokeOpts });
2014
2016
  }
2015
2017
  } catch (err) {
2016
2018
  const durationMs2 = Date.now() - start;
@@ -2126,18 +2128,13 @@ var BASE_DEFAULTS = {
2126
2128
  requireJsonOutputContract: true
2127
2129
  },
2128
2130
  toolResponses: {
2129
- maxToolResponseChars: 3600,
2130
- maxToolResponseTokens: 1200,
2131
- smallResponseChars: 0,
2132
- smallResponsePolicy: "keep_full",
2133
- defaultPolicy: "keep_structured",
2134
- largeResponsePolicy: "summarize_archive",
2131
+ maxToolResponseChars: 12e3,
2132
+ maxToolResponseTokens: 3e3,
2133
+ defaultPolicy: "summarize_archive",
2135
2134
  toolResponseRetentionByTool: {},
2136
2135
  criticalTools: [...DEFAULT_CRITICAL_TOOLS],
2137
2136
  schemaValidation: "strict",
2138
- retryOnSchemaError: true,
2139
- fallbackPolicy: "keep_full",
2140
- keepRecentFullCount: 0
2137
+ retryOnSchemaError: true
2141
2138
  }
2142
2139
  };
2143
2140
  function buildProfile(overrides) {
@@ -2176,7 +2173,7 @@ var DEFAULT_PROFILE_CONFIGS = {
2176
2173
  },
2177
2174
  memory: { readPolicy: "recent_only" },
2178
2175
  delegation: { mode: "off", maxDelegationDepth: 1, maxChildCalls: 2, maxParallelChild: 1, childContextPolicy: "minimal" },
2179
- toolResponses: { maxToolResponseChars: 2400, maxToolResponseTokens: 800 }
2176
+ toolResponses: { maxToolResponseChars: 8e3, maxToolResponseTokens: 2e3 }
2180
2177
  }),
2181
2178
  balanced: buildProfile({}),
2182
2179
  deep: buildProfile({
@@ -2193,7 +2190,7 @@ var DEFAULT_PROFILE_CONFIGS = {
2193
2190
  },
2194
2191
  memory: { scope: "workspace" },
2195
2192
  delegation: { maxDelegationDepth: 3, maxChildCalls: 6 },
2196
- toolResponses: { maxToolResponseChars: 5400, maxToolResponseTokens: 1800 }
2193
+ toolResponses: { maxToolResponseChars: 16e3, maxToolResponseTokens: 4e3 }
2197
2194
  }),
2198
2195
  research: buildProfile({
2199
2196
  limits: { maxToolCalls: 20, maxParallelTools: 4, maxContextTokens: 56e3 },
@@ -2209,7 +2206,7 @@ var DEFAULT_PROFILE_CONFIGS = {
2209
2206
  },
2210
2207
  memory: { scope: "workspace" },
2211
2208
  delegation: { mode: "automatic", maxDelegationDepth: 4, maxChildCalls: 8, maxParallelChild: 3, childContextPolicy: "full" },
2212
- toolResponses: { maxToolResponseChars: 7200, maxToolResponseTokens: 2200 }
2209
+ toolResponses: { maxToolResponseChars: 24e3, maxToolResponseTokens: 6e3 }
2213
2210
  })
2214
2211
  };
2215
2212
  function mergeLimits(base, override) {
@@ -2416,63 +2413,65 @@ function summarizeObject(value) {
2416
2413
  return safeStringify(value);
2417
2414
  }
2418
2415
  function classifyToolResponse(toolName, output, config) {
2419
- const serialized = safeStringify(output);
2420
- const tokenCount = countApproxTokens(serialized);
2421
2416
  const criticalTools = new Set(config.toolResponses.criticalTools);
2422
2417
  if (criticalTools.has(toolName)) return "critical";
2423
- if (serialized === "ok" || serialized === "true" || serialized === "null") {
2424
- return "redundant";
2425
- }
2418
+ const serialized = safeStringify(output);
2419
+ const tokenCount = countApproxTokens(serialized);
2426
2420
  if (tokenCount >= config.toolResponses.maxToolResponseTokens || serialized.length >= config.toolResponses.maxToolResponseChars) {
2427
2421
  return "verbose";
2428
2422
  }
2429
- const smallChars = config.toolResponses.smallResponseChars;
2430
- if (smallChars > 0 && serialized.length <= smallChars) {
2431
- return "small";
2432
- }
2433
2423
  return "informative";
2434
2424
  }
2435
- function resolveToolResponsePolicy(toolName, output, config) {
2425
+ function applyToolResponseHardCap(toolName, output, executionId, config) {
2436
2426
  const serialized = safeStringify(output);
2437
2427
  const tokenCount = countApproxTokens(serialized);
2438
2428
  const classification = classifyToolResponse(toolName, output, config);
2439
- const byTool = config.toolResponses.toolResponseRetentionByTool[toolName];
2440
- let retentionPolicy = byTool || config.toolResponses.defaultPolicy;
2441
- if (byTool) ; else if (classification === "critical") {
2442
- retentionPolicy = config.toolResponses.fallbackPolicy;
2443
- } else if (classification === "verbose") {
2444
- retentionPolicy = config.toolResponses.largeResponsePolicy;
2445
- } else if (classification === "small") {
2446
- retentionPolicy = config.toolResponses.smallResponsePolicy;
2447
- } else if (classification === "redundant") {
2448
- retentionPolicy = "drop";
2449
- }
2450
- const summary = summarizeObject(output);
2451
- if (retentionPolicy === "keep_full") {
2452
- return { classification, retentionPolicy, content: serialized, rawOutput: output, summary, tokenCount };
2453
- }
2454
- if (retentionPolicy === "keep_structured") {
2455
- return { classification, retentionPolicy, content: summary, rawOutput: output, summary, tokenCount };
2456
- }
2457
- if (retentionPolicy === "summarize_archive") {
2458
- return {
2459
- classification,
2460
- retentionPolicy,
2461
- content: `ARCHIVED_TOOL_RESPONSE: ${summary}`,
2462
- rawOutput: output,
2463
- summary,
2464
- tokenCount
2465
- };
2429
+ if (classification !== "verbose") {
2430
+ return { classification, content: serialized, rawOutput: output, tokenCount, truncated: false };
2466
2431
  }
2432
+ const cap = config.toolResponses.maxToolResponseChars;
2433
+ const head = serialized.slice(0, cap);
2434
+ const note = `
2435
+ ... [TRUNCATED ${serialized.length - cap} chars by hard cap. Use get_tool_response with executionId="${executionId}" to fetch the full payload.]`;
2467
2436
  return {
2468
2437
  classification,
2469
- retentionPolicy,
2470
- content: `DROPPED_TOOL_RESPONSE: ${summary}`,
2438
+ content: head + note,
2471
2439
  rawOutput: output,
2472
- summary,
2473
- tokenCount
2440
+ tokenCount,
2441
+ truncated: true
2474
2442
  };
2475
2443
  }
2444
+ function resolveSummarizationRetention(toolName, config) {
2445
+ const criticalTools = new Set(config.toolResponses.criticalTools);
2446
+ if (criticalTools.has(toolName)) return "keep_full";
2447
+ const byTool = config.toolResponses.toolResponseRetentionByTool[toolName];
2448
+ return byTool || config.toolResponses.defaultPolicy;
2449
+ }
2450
+ function renderRetainedToolMessage(args) {
2451
+ const { policy, rawOutput, toolName, toolCallId, executionId, prebuiltSummary } = args;
2452
+ if (policy === "keep_full") {
2453
+ return typeof rawOutput === "string" ? rawOutput : safeStringify(rawOutput);
2454
+ }
2455
+ const summary = prebuiltSummary || summarizeObject(rawOutput);
2456
+ const refs = [
2457
+ `toolName=${toolName}`,
2458
+ toolCallId ? `toolCallId=${toolCallId}` : null,
2459
+ executionId ? `executionId=${executionId}` : null
2460
+ ].filter(Boolean).join("; ");
2461
+ const refId = executionId || toolCallId || "";
2462
+ if (policy === "keep_structured") {
2463
+ return `STRUCTURED_TOOL_RESPONSE [${refs}]
2464
+ Preview: ${summary}
2465
+ Use get_tool_response with executionId "${refId}" to fetch the full payload when a specific field is needed.`;
2466
+ }
2467
+ if (policy === "summarize_archive") {
2468
+ return `ARCHIVED_TOOL_RESPONSE [${refs}]
2469
+ Summary: ${summary}
2470
+ Use get_tool_response with executionId "${refId}" to fetch the full payload.`;
2471
+ }
2472
+ return `DROPPED_TOOL_RESPONSE [${refs}]
2473
+ Use get_tool_response with executionId "${refId}" only if you must recover the original payload.`;
2474
+ }
2476
2475
  function validateToolArgs(tool, args) {
2477
2476
  const schema = tool.schema;
2478
2477
  if (!schema || typeof schema.parse !== "function") {
@@ -2751,52 +2750,25 @@ function createToolsNode(initialTools, opts) {
2751
2750
  state.ctx.__structuredOutputParsed = output.data;
2752
2751
  state.ctx.__finalizedDueToStructuredOutput = true;
2753
2752
  }
2754
- const responsePolicy = resolveToolResponsePolicy(toolName, output, resolved);
2753
+ const responsePolicy = applyToolResponseHardCap(toolName, output, executionId, resolved);
2755
2754
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
2756
- const storedOutput = responsePolicy.retentionPolicy === "keep_full" ? output : responsePolicy.summary;
2757
2755
  toolHistory.push({
2758
2756
  executionId,
2759
2757
  toolName,
2760
2758
  args,
2761
- output: storedOutput,
2759
+ output,
2762
2760
  rawOutput: output,
2763
2761
  timestamp,
2764
2762
  tool_call_id: tc.id,
2765
- summarized: responsePolicy.retentionPolicy !== "keep_full",
2763
+ summarized: false,
2766
2764
  originalTokenCount: responsePolicy.tokenCount,
2767
2765
  classification: responsePolicy.classification,
2768
- retentionPolicy: responsePolicy.retentionPolicy,
2769
- summary: responsePolicy.summary,
2770
- archiveId: responsePolicy.retentionPolicy === "summarize_archive" || responsePolicy.retentionPolicy === "drop" ? executionId : void 0,
2766
+ retentionPolicy: "keep_full",
2767
+ summary: void 0,
2768
+ archiveId: void 0,
2771
2769
  status: "success"
2772
2770
  });
2773
- if (resolved.context.archiveLargeToolResponses && (responsePolicy.retentionPolicy === "summarize_archive" || responsePolicy.retentionPolicy === "drop")) {
2774
- toolHistoryArchived.push({
2775
- executionId,
2776
- toolName,
2777
- args,
2778
- output,
2779
- rawOutput: output,
2780
- timestamp,
2781
- tool_call_id: tc.id,
2782
- summarized: true,
2783
- originalTokenCount: responsePolicy.tokenCount,
2784
- classification: responsePolicy.classification,
2785
- retentionPolicy: responsePolicy.retentionPolicy,
2786
- summary: responsePolicy.summary,
2787
- archiveId: executionId,
2788
- status: "success"
2789
- });
2790
- }
2791
- let toolMessageContent = responsePolicy.content;
2792
- if (responsePolicy.retentionPolicy === "summarize_archive") {
2793
- toolMessageContent = `ARCHIVED_TOOL_RESPONSE [executionId=${executionId}]: ${responsePolicy.summary}
2794
- Only use get_tool_response with executionId "${executionId}" if this summary is missing a specific field, id, snippet, or exact payload you still need.`;
2795
- } else if (responsePolicy.retentionPolicy === "drop") {
2796
- toolMessageContent = `DROPPED_TOOL_RESPONSE [executionId=${executionId}]: ${responsePolicy.summary}
2797
- Only use get_tool_response with executionId "${executionId}" if this summary is missing a specific field, id, snippet, or exact payload you still need.`;
2798
- }
2799
- appended.push({ role: "tool", content: toolMessageContent, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
2771
+ appended.push({ role: "tool", content: responsePolicy.content, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
2800
2772
  onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: output, durationMs });
2801
2773
  onProgress?.({ stage: "tools", message: `Tool ${tc.name} completed`, detail: { durationMs } });
2802
2774
  if (needsApproval && approvalEntry) {
@@ -2840,8 +2812,9 @@ Only use get_tool_response with executionId "${executionId}" if this summary is
2840
2812
  messageList,
2841
2813
  debug: {
2842
2814
  classification: responsePolicy.classification,
2843
- retentionPolicy: responsePolicy.retentionPolicy,
2844
- originalTokenCount: responsePolicy.tokenCount
2815
+ retentionPolicy: "keep_full",
2816
+ originalTokenCount: responsePolicy.tokenCount,
2817
+ truncated: responsePolicy.truncated
2845
2818
  }
2846
2819
  });
2847
2820
  toolCount += 1;
@@ -2958,6 +2931,246 @@ function createToolLimitFinalizeNode(_opts) {
2958
2931
  return { messages: [...state.messages, notice], ctx };
2959
2932
  };
2960
2933
  }
2934
+ var REFLECTION_SYSTEM_NAME = "agent_reflection";
2935
+ var REFLECTION_ASK_NAME = "agent_reflection_ask";
2936
+ var DEFAULT_REFLECT_PROMPT = `Reflect briefly \u2014 plain text, no JSON, no tool calls.
2937
+ Structure your note (1\u20132 sentences each):
2938
+ 1) What the latest tool results actually tell us (facts, surprises).
2939
+ 2) What is still missing or uncertain before we can finish the user's task.
2940
+ 3) The single next step you plan to take, and why that step over alternatives.
2941
+ Be concrete. Do not repeat the task description. Do not apologize. Max ${"${maxChars}"} characters total.`;
2942
+ function trimToChars(text, maxChars) {
2943
+ if (text.length <= maxChars) return text;
2944
+ return text.slice(0, maxChars - 1).trimEnd() + "\u2026";
2945
+ }
2946
+ function countToolCallsInLastTurn(state) {
2947
+ const ids = [];
2948
+ const names = [];
2949
+ for (let i = state.messages.length - 1; i >= 0; i--) {
2950
+ const m = state.messages[i];
2951
+ if (m?.role === "tool") {
2952
+ if (m.tool_call_id) ids.push(m.tool_call_id);
2953
+ if (m.name) names.push(m.name);
2954
+ } else if (m?.role === "assistant") {
2955
+ break;
2956
+ }
2957
+ }
2958
+ return { ids, names };
2959
+ }
2960
+ function stripReflectionAskMessages(messages) {
2961
+ return messages.filter((m) => !(m?.role === "user" && m?.name === REFLECTION_ASK_NAME));
2962
+ }
2963
+ function shouldRunReflection(cadence, state, ranToolsThisTurn) {
2964
+ if (cadence === "off") return false;
2965
+ if (cadence === "every_turn") return true;
2966
+ if (cadence === "after_tool") return ranToolsThisTurn;
2967
+ if (cadence === "on_branch") {
2968
+ const reflections = state.reflections || [];
2969
+ const lastReflectionTurn = reflections[reflections.length - 1]?.turn ?? -1;
2970
+ const currentTurnId = state.toolCallCount ?? 0;
2971
+ return currentTurnId - lastReflectionTurn >= 2;
2972
+ }
2973
+ return false;
2974
+ }
2975
+ function createReflectionNode(opts, resolved) {
2976
+ return async function reflectNode(state, trigger) {
2977
+ if (!resolved.enabled) return {};
2978
+ const runtime = state.agent || {
2979
+ model: opts.model,
2980
+ name: opts.name,
2981
+ version: opts.version
2982
+ };
2983
+ const model = runtime.model;
2984
+ if (!model?.invoke && !model?.stream) return {};
2985
+ const onEvent = state.ctx?.__onEvent;
2986
+ const traceSession = state.ctx?.__traceSession;
2987
+ const maxChars = resolved.maxChars;
2988
+ const promptBody = (resolved.promptTemplate || DEFAULT_REFLECT_PROMPT).replace(/\$\{maxChars\}/g, String(maxChars));
2989
+ const baseMessages = stripReflectionAskMessages([...state.messages]);
2990
+ const askMessage = {
2991
+ role: "user",
2992
+ name: REFLECTION_ASK_NAME,
2993
+ content: promptBody
2994
+ };
2995
+ const messagesForCall = resolved.mode === "piggyback" ? [...baseMessages, askMessage] : buildSeparateCallMessages(baseMessages, promptBody);
2996
+ const start = Date.now();
2997
+ let response;
2998
+ try {
2999
+ response = await model.invoke(messagesForCall, {
3000
+ // tool_choice:"none" so the model won't try to call tools during reflection
3001
+ tool_choice: "none",
3002
+ toolChoice: "none",
3003
+ // Override max_tokens for the reflection call. BaseChatModel passes this through
3004
+ // to the adapter which forwards into the provider body.
3005
+ max_tokens: resolved.maxTokens,
3006
+ maxTokens: resolved.maxTokens
3007
+ });
3008
+ } catch (err) {
3009
+ recordTraceEvent(traceSession, {
3010
+ type: "reflection",
3011
+ label: "Reflection error",
3012
+ actor: { scope: "agent", name: opts.name || "agent", role: "reflection" },
3013
+ status: "error",
3014
+ error: { message: err?.message || String(err) }
3015
+ });
3016
+ return {};
3017
+ }
3018
+ const durationMs = Date.now() - start;
3019
+ const rawText = extractText(response);
3020
+ const text = trimToChars(String(rawText ?? "").trim(), maxChars);
3021
+ if (!text) return {};
3022
+ const rawUsage = response?.usage_metadata || response?.response_metadata?.usage || response?.usage || response?.additional_kwargs?.usage;
3023
+ const usage = normalizeUsage(rawUsage);
3024
+ const { ids, names } = countToolCallsInLastTurn(state);
3025
+ const turn = state.toolCallCount ?? 0;
3026
+ const record = {
3027
+ id: safeRandomId(),
3028
+ turn,
3029
+ text,
3030
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3031
+ durationMs,
3032
+ anchorMessageIndex: state.messages.length,
3033
+ trigger,
3034
+ usage: usage ? {
3035
+ inputTokens: usage.prompt_tokens,
3036
+ outputTokens: usage.completion_tokens,
3037
+ cachedInputTokens: usage.prompt_tokens_details?.cached_tokens,
3038
+ totalTokens: usage.total_tokens,
3039
+ reasoningTokens: usage.completion_tokens_details?.reasoning_tokens
3040
+ } : void 0,
3041
+ toolCallIds: ids.length > 0 ? ids : void 0
3042
+ };
3043
+ recordTraceEvent(traceSession, {
3044
+ type: "reflection",
3045
+ label: `Reflection turn ${turn}`,
3046
+ actor: { scope: "agent", name: opts.name || "agent", role: "reflection" },
3047
+ status: "success",
3048
+ durationMs,
3049
+ inputTokens: record.usage?.inputTokens,
3050
+ outputTokens: record.usage?.outputTokens,
3051
+ cachedInputTokens: record.usage?.cachedInputTokens,
3052
+ totalTokens: record.usage?.totalTokens,
3053
+ debug: { text, toolNames: names }
3054
+ });
3055
+ if (resolved.emitEvents) {
3056
+ try {
3057
+ onEvent?.({
3058
+ type: "reflection",
3059
+ id: record.id,
3060
+ turn,
3061
+ text,
3062
+ trigger,
3063
+ durationMs,
3064
+ usage: record.usage,
3065
+ toolCallIds: record.toolCallIds
3066
+ });
3067
+ } catch (e) {
3068
+ }
3069
+ }
3070
+ const nextMessages = stripReflectionAskMessages([...state.messages]);
3071
+ const reflections = [...state.reflections || [], record];
3072
+ const kept = reflections.slice(-Math.max(1, resolved.keepLast));
3073
+ const prunedMessages = nextMessages.filter(
3074
+ (m) => !(m?.role === "system" && m?.name === REFLECTION_SYSTEM_NAME)
3075
+ );
3076
+ for (const r of kept) {
3077
+ prunedMessages.push({
3078
+ role: "system",
3079
+ name: REFLECTION_SYSTEM_NAME,
3080
+ content: `<agent_reflection turn=${r.turn} at=${r.createdAt}>
3081
+ ${r.text}
3082
+ </agent_reflection>`,
3083
+ metadata: { reflectionId: r.id, reflectionTurn: r.turn, reflectionTrigger: r.trigger }
3084
+ });
3085
+ }
3086
+ return { messages: prunedMessages, reflections };
3087
+ };
3088
+ }
3089
+ function extractText(chunk) {
3090
+ if (chunk == null) return "";
3091
+ if (typeof chunk === "string") return chunk;
3092
+ if (typeof chunk?.content === "string") return chunk.content;
3093
+ if (Array.isArray(chunk?.content)) {
3094
+ return chunk.content.map((c) => typeof c === "string" ? c : c?.text ?? c?.content ?? "").join("");
3095
+ }
3096
+ if (typeof chunk?.text === "string") return chunk.text;
3097
+ return "";
3098
+ }
3099
+ function buildSeparateCallMessages(base, promptBody) {
3100
+ const systemMessages = base.filter((m) => m?.role === "system");
3101
+ const tail = base.slice(-8);
3102
+ return [
3103
+ ...systemMessages,
3104
+ ...tail,
3105
+ { role: "user", name: REFLECTION_ASK_NAME, content: promptBody }
3106
+ ];
3107
+ }
3108
+ function safeRandomId() {
3109
+ try {
3110
+ return randomUUID();
3111
+ } catch {
3112
+ return `r_${Math.random().toString(36).slice(2, 10)}_${Date.now().toString(36)}`;
3113
+ }
3114
+ }
3115
+
3116
+ // src/smart/reasoning.ts
3117
+ var LEVEL_DEFAULTS = {
3118
+ low: {
3119
+ native: { effort: "low" },
3120
+ reflection: { maxTokens: 200, maxChars: 600, cadence: "on_branch" }
3121
+ },
3122
+ medium: {
3123
+ native: { effort: "medium" },
3124
+ reflection: { maxTokens: 350, maxChars: 1200, cadence: "after_tool" }
3125
+ },
3126
+ high: {
3127
+ native: { effort: "high" },
3128
+ reflection: { maxTokens: 600, maxChars: 2e3, cadence: "after_tool" }
3129
+ }
3130
+ };
3131
+ function resolveReasoning(input) {
3132
+ if (!input || input.enabled === false) return void 0;
3133
+ const enabled = input.enabled ?? (input.native !== void 0 || input.reflection !== void 0 || input.level !== void 0);
3134
+ if (!enabled) return void 0;
3135
+ const level = input.level ?? "medium";
3136
+ const presets = LEVEL_DEFAULTS[level];
3137
+ let native;
3138
+ if (input.native === false) {
3139
+ native = void 0;
3140
+ } else {
3141
+ native = { ...presets.native, ...input.native ?? {} };
3142
+ }
3143
+ const reflection = resolveReflection(input.reflection, presets.reflection);
3144
+ return { enabled: true, level, native, reflection };
3145
+ }
3146
+ function resolveReflection(input, preset) {
3147
+ if (input === false) {
3148
+ return {
3149
+ enabled: false,
3150
+ cadence: "off",
3151
+ mode: "piggyback",
3152
+ maxTokens: preset.maxTokens,
3153
+ maxChars: preset.maxChars,
3154
+ keepLast: 3,
3155
+ summarize: false,
3156
+ emitEvents: true
3157
+ };
3158
+ }
3159
+ const cfg = input ?? {};
3160
+ const enabled = cfg.enabled ?? true;
3161
+ const cadence = cfg.cadence ?? preset.cadence;
3162
+ return {
3163
+ enabled: enabled && cadence !== "off",
3164
+ cadence: enabled ? cadence : "off",
3165
+ mode: cfg.mode ?? "piggyback",
3166
+ maxTokens: cfg.maxTokens ?? preset.maxTokens,
3167
+ maxChars: cfg.maxChars ?? preset.maxChars,
3168
+ keepLast: cfg.keepLast ?? 3,
3169
+ summarize: cfg.summarize ?? false,
3170
+ promptTemplate: cfg.promptTemplate,
3171
+ emitEvents: cfg.emitEvents ?? true
3172
+ };
3173
+ }
2961
3174
  function ensureArray(value) {
2962
3175
  return Array.isArray(value) ? value : [];
2963
3176
  }
@@ -3371,6 +3584,8 @@ function createAgent(opts) {
3371
3584
  }
3372
3585
  const toolsNode = createToolsNode(toolsBase, opts);
3373
3586
  const finalizeNode = createToolLimitFinalizeNode();
3587
+ const resolvedReasoning = resolveReasoning(opts.reasoning);
3588
+ const reflectNode = resolvedReasoning?.reflection.enabled ? createReflectionNode(opts, resolvedReasoning.reflection) : void 0;
3374
3589
  const mergeGuardrailOutcomes = (prev, next) => {
3375
3590
  if (!prev) return next;
3376
3591
  return {
@@ -3425,6 +3640,13 @@ function createAgent(opts) {
3425
3640
  if (summarizationThreshold === void 0) {
3426
3641
  state = clearNeedsSummarization(state);
3427
3642
  }
3643
+ if (resolvedReasoning?.native) {
3644
+ const ctx = { ...state.ctx || {} };
3645
+ if (!ctx.__reasoning) {
3646
+ ctx.__reasoning = resolvedReasoning.native;
3647
+ state = { ...state, ctx };
3648
+ }
3649
+ }
3428
3650
  const mergedLimits = {
3429
3651
  ...opts.limits || {},
3430
3652
  ...config?.limits || {}
@@ -3653,6 +3875,18 @@ function createAgent(opts) {
3653
3875
  if (state.ctx?.__awaitingApproval) break;
3654
3876
  if (checkpointIfRequested("after_tools")) break;
3655
3877
  if (state.ctx?.__finalizedDueToStructuredOutput) break;
3878
+ if (reflectNode && resolvedReasoning?.reflection.enabled) {
3879
+ const cadence = resolvedReasoning.reflection.cadence;
3880
+ if (shouldRunReflection(cadence, state, true)) {
3881
+ try {
3882
+ const patch = await reflectNode(state, cadence);
3883
+ if (patch && Object.keys(patch).length > 0) {
3884
+ state = { ...state, ...patch };
3885
+ }
3886
+ } catch {
3887
+ }
3888
+ }
3889
+ }
3656
3890
  }
3657
3891
  if (soManager && !state.ctx?.__finalizedDueToStructuredOutput) {
3658
3892
  const lastForParse = state.messages[state.messages.length - 1];
@@ -3980,40 +4214,6 @@ function createAgentNode(opts) {
3980
4214
  }
3981
4215
 
3982
4216
  // src/smart/contextPolicy.ts
3983
- function collectRecentFullToolCallIds(state, keepRecentFullCount) {
3984
- if (!keepRecentFullCount || keepRecentFullCount <= 0) return /* @__PURE__ */ new Set();
3985
- const history = state.toolHistory || [];
3986
- const ids = [];
3987
- for (let index = history.length - 1; index >= 0 && ids.length < keepRecentFullCount; index -= 1) {
3988
- const entry = history[index];
3989
- if (!entry || !entry.tool_call_id) continue;
3990
- if (entry.status && entry.status !== "success") continue;
3991
- ids.push(entry.tool_call_id);
3992
- }
3993
- return new Set(ids);
3994
- }
3995
- function applyRecentFullOverride(messages, state, config) {
3996
- const keepN = config.toolResponses.keepRecentFullCount;
3997
- if (!keepN || keepN <= 0) return messages;
3998
- const recentIds = collectRecentFullToolCallIds(state, keepN);
3999
- if (recentIds.size === 0) return messages;
4000
- const history = state.toolHistory || [];
4001
- const byCallId = /* @__PURE__ */ new Map();
4002
- for (const entry of history) {
4003
- if (entry?.tool_call_id) byCallId.set(entry.tool_call_id, entry);
4004
- }
4005
- return messages.map((message) => {
4006
- if (message.role !== "tool" || !message.tool_call_id) return message;
4007
- if (!recentIds.has(message.tool_call_id)) return message;
4008
- const entry = byCallId.get(message.tool_call_id);
4009
- if (!entry) return message;
4010
- const raw = entry.rawOutput ?? entry.output;
4011
- if (raw === void 0) return message;
4012
- const fullContent = typeof raw === "string" ? raw : safeStringify(raw);
4013
- if (!fullContent || fullContent === message.content) return message;
4014
- return { ...message, content: fullContent };
4015
- });
4016
- }
4017
4217
  function collectRecentTurns(messages, lastTurnsToKeep) {
4018
4218
  if (lastTurnsToKeep <= 0) return [];
4019
4219
  const systemPrefix = messages.filter((message, index) => index === 0 && message.role === "system");
@@ -4114,7 +4314,7 @@ function clampToBudget(messages, maxContextTokens) {
4114
4314
  function buildModelMessages(state, config) {
4115
4315
  const rawMessages = state.messages || [];
4116
4316
  if (config.context.policy === "raw") {
4117
- return clampToBudget(applyRecentFullOverride(rawMessages, state, config), config.limits.maxContextTokens);
4317
+ return clampToBudget(rawMessages, config.limits.maxContextTokens);
4118
4318
  }
4119
4319
  const recentMessages = collectRecentTurns(rawMessages, config.context.lastTurnsToKeep);
4120
4320
  const latestSummary = state.summaryRecords?.[state.summaryRecords.length - 1];
@@ -4134,7 +4334,7 @@ function buildModelMessages(state, config) {
4134
4334
  ...syntheticContextMessages,
4135
4335
  ...config.context.policy === "summary_only" ? body.filter((message) => message.role === "user" || message.role === "assistant").slice(-2) : body
4136
4336
  ];
4137
- return clampToBudget(applyRecentFullOverride(assembled, state, config), config.limits.maxContextTokens);
4337
+ return clampToBudget(assembled, config.limits.maxContextTokens);
4138
4338
  }
4139
4339
 
4140
4340
  // src/nodes/contextSummarize.ts
@@ -4236,24 +4436,8 @@ function extractCanonicalFacts(messages) {
4236
4436
  }
4237
4437
  return [...facts.values()];
4238
4438
  }
4239
- function flattenToolMessageContent(message) {
4240
- if (typeof message.content === "string") {
4241
- return message.content;
4242
- }
4243
- if (Array.isArray(message.content)) {
4244
- return message.content.map((part) => typeof part === "string" ? part : part?.text ?? part?.content ?? String(part ?? "")).join(" ");
4245
- }
4246
- return "";
4247
- }
4248
4439
  function isSummarizedToolPlaceholder(content) {
4249
- return typeof content === "string" && (content === "SUMMARIZED" || content.startsWith("SUMMARIZED_TOOL_RESPONSE"));
4250
- }
4251
- function summarizeToolMessagePreview(message) {
4252
- const preview = flattenToolMessageContent(message).trim();
4253
- if (!preview) {
4254
- return "No summary available.";
4255
- }
4256
- return preview.length > 240 ? `${preview.slice(0, 240)}...` : preview;
4440
+ return typeof content === "string" && (content === "SUMMARIZED" || content.startsWith("SUMMARIZED_TOOL_RESPONSE") || content.startsWith("ARCHIVED_TOOL_RESPONSE") || content.startsWith("STRUCTURED_TOOL_RESPONSE") || content.startsWith("DROPPED_TOOL_RESPONSE"));
4257
4441
  }
4258
4442
  function findToolHistoryEntry(state, toolCallId, toolName) {
4259
4443
  const entries = [
@@ -4279,29 +4463,6 @@ function findToolHistoryEntry(state, toolCallId, toolName) {
4279
4463
  }
4280
4464
  return void 0;
4281
4465
  }
4282
- function buildSummarizedToolMessage(message, state) {
4283
- const toolName = message.name || "unknown_tool";
4284
- const toolCallId = message.tool_call_id;
4285
- const historyEntry = findToolHistoryEntry(state, toolCallId, toolName);
4286
- const executionId = historyEntry?.executionId;
4287
- const summary = historyEntry?.summary || summarizeToolMessagePreview(message);
4288
- const references = [
4289
- `toolName=${toolName}`,
4290
- toolCallId ? `toolCallId=${toolCallId}` : null,
4291
- executionId ? `executionId=${executionId}` : null
4292
- ].filter(Boolean).join("; ");
4293
- let retrievalHint = "Only use get_tool_response if this summary is missing a specific field, id, snippet, or exact payload you still need.";
4294
- if (executionId && toolCallId) {
4295
- retrievalHint = `Only use get_tool_response with executionId "${executionId}" or tool call id "${toolCallId}" if this summary is missing a specific field, id, snippet, or exact payload you still need.`;
4296
- } else if (executionId || toolCallId) {
4297
- retrievalHint = `Only use get_tool_response with executionId "${executionId || toolCallId}" if this summary is missing a specific field, id, snippet, or exact payload you still need.`;
4298
- }
4299
- return [
4300
- `SUMMARIZED_TOOL_RESPONSE [${references}]`,
4301
- `Summary: ${summary}`,
4302
- retrievalHint
4303
- ].join("\n");
4304
- }
4305
4466
  function mergeStableFacts(summary, additionalFacts) {
4306
4467
  if (additionalFacts.length === 0) return summary;
4307
4468
  const merged = new Map(summary.stable_facts.map((fact) => [fact.key, fact]));
@@ -4359,20 +4520,13 @@ function createContextSummarizeNode(opts) {
4359
4520
  const messages = state.messages || [];
4360
4521
  const onEvent = state.ctx?.__onEvent;
4361
4522
  const traceSession = state.ctx?.__traceSession;
4362
- const toolResponsesConfigEarly = resolved.toolResponses;
4363
- const recentFullToolCallIdsEarly = collectRecentFullToolCallIds(state, toolResponsesConfigEarly.keepRecentFullCount);
4364
4523
  const compressableMessages = messages.filter(
4365
4524
  (m) => {
4366
4525
  if (m.role !== "tool") return false;
4367
4526
  if (isSummarizedToolPlaceholder(m.content)) return false;
4368
4527
  if (isSyntheticSummaryMessage(m)) return false;
4369
- if (m.tool_call_id && recentFullToolCallIdsEarly.has(m.tool_call_id)) return false;
4370
- const histEntry = findToolHistoryEntry(state, m.tool_call_id, m.name);
4371
- if (histEntry?.retentionPolicy === "keep_full") return false;
4372
- if (!histEntry?.retentionPolicy) {
4373
- const byTool = toolResponsesConfigEarly.toolResponseRetentionByTool?.[m.name || ""];
4374
- if ((byTool || toolResponsesConfigEarly.defaultPolicy) === "keep_full") return false;
4375
- }
4528
+ const policy = resolveSummarizationRetention(m.name || "", resolved);
4529
+ if (policy === "keep_full") return false;
4376
4530
  return true;
4377
4531
  }
4378
4532
  );
@@ -4552,8 +4706,8 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
4552
4706
  break;
4553
4707
  }
4554
4708
  }
4555
- const toolResponsesConfig = resolved.toolResponses;
4556
- const recentFullToolCallIds = collectRecentFullToolCallIds(state, toolResponsesConfig.keepRecentFullCount);
4709
+ const archivedExecutions = [...state.toolHistoryArchived || []];
4710
+ const archivedIds = new Set(archivedExecutions.map((entry) => entry?.executionId).filter(Boolean));
4557
4711
  const newMessages = messages.map((m) => {
4558
4712
  if (m.role === "tool") {
4559
4713
  if (isSyntheticSummaryMessage(m)) {
@@ -4565,25 +4719,39 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
4565
4719
  }
4566
4720
  if (isSummarizedToolPlaceholder(m.content)) return m;
4567
4721
  if (toolCallId && protectedToolCallIds.has(toolCallId)) return m;
4568
- if (toolCallId && recentFullToolCallIds.has(toolCallId)) return m;
4569
4722
  const toolName = m.name || "";
4723
+ const policy = resolveSummarizationRetention(toolName, resolved);
4724
+ if (policy === "keep_full") return m;
4570
4725
  const historyEntry = findToolHistoryEntry(state, toolCallId, toolName);
4571
- const entryRetention = historyEntry?.retentionPolicy;
4572
- if (entryRetention === "keep_full") return m;
4573
- if (!entryRetention) {
4574
- const byTool = toolResponsesConfig.toolResponseRetentionByTool?.[toolName];
4575
- const configPolicy = byTool || toolResponsesConfig.defaultPolicy;
4576
- if (configPolicy === "keep_full") return m;
4726
+ const rawOutput = historyEntry?.rawOutput ?? historyEntry?.output ?? m.content;
4727
+ const executionId = historyEntry?.executionId;
4728
+ const prebuiltSummary = historyEntry?.summary || summarizeObject(rawOutput);
4729
+ if ((policy === "summarize_archive" || policy === "drop") && historyEntry && executionId && !archivedIds.has(executionId)) {
4730
+ archivedExecutions.push({
4731
+ ...historyEntry,
4732
+ summarized: true,
4733
+ retentionPolicy: policy,
4734
+ archiveId: executionId,
4735
+ summary: prebuiltSummary
4736
+ });
4737
+ archivedIds.add(executionId);
4577
4738
  }
4578
4739
  return {
4579
4740
  ...m,
4580
- content: buildSummarizedToolMessage(m, state)
4741
+ content: renderRetainedToolMessage({
4742
+ policy,
4743
+ rawOutput,
4744
+ toolName,
4745
+ toolCallId,
4746
+ executionId,
4747
+ prebuiltSummary
4748
+ })
4581
4749
  };
4582
4750
  }
4583
4751
  return m;
4584
4752
  }).filter((m) => m !== null);
4585
4753
  const actuallyCompressed = newMessages.filter(
4586
- (m) => m.role === "tool" && typeof m.content === "string" && m.content.startsWith("SUMMARIZED_TOOL_RESPONSE") && !messages.some((orig) => orig.tool_call_id === m.tool_call_id && isSummarizedToolPlaceholder(orig.content))
4754
+ (m) => m.role === "tool" && isSummarizedToolPlaceholder(m.content) && !messages.some((orig) => orig.tool_call_id === m.tool_call_id && isSummarizedToolPlaceholder(orig.content))
4587
4755
  );
4588
4756
  if (actuallyCompressed.length === 0) {
4589
4757
  return {};
@@ -4662,7 +4830,8 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
4662
4830
  return {
4663
4831
  messages: finalMessages,
4664
4832
  summaries,
4665
- summaryRecords
4833
+ summaryRecords,
4834
+ toolHistoryArchived: archivedExecutions
4666
4835
  };
4667
4836
  };
4668
4837
  }
@@ -5727,6 +5896,52 @@ function parseSSEBlock(block) {
5727
5896
  return { event, data: dataLines.join("\n"), id, retry };
5728
5897
  }
5729
5898
 
5899
+ // src/providers/utils/reasoning.ts
5900
+ function applyOpenAIReasoning(body, reasoning, mode = "legacy_effort") {
5901
+ if (!reasoning) return;
5902
+ const effort = normalizeEffort(reasoning.effort);
5903
+ if (mode === "responses") {
5904
+ const obj = {};
5905
+ if (effort) obj.effort = effort;
5906
+ if (reasoning.includeThoughts) obj.summary = "auto";
5907
+ if (reasoning.providerExtras) Object.assign(obj, reasoning.providerExtras);
5908
+ if (Object.keys(obj).length > 0) body.reasoning = obj;
5909
+ } else {
5910
+ if (effort) body.reasoning_effort = effort;
5911
+ if (reasoning.providerExtras) Object.assign(body, reasoning.providerExtras);
5912
+ }
5913
+ }
5914
+ function applyAnthropicReasoning(body, reasoning) {
5915
+ if (!reasoning) return;
5916
+ const budget = resolveBudget(reasoning, { low: 2048, medium: 8192, high: 16384, minimal: 1024 });
5917
+ if (!budget) return;
5918
+ const thinking = { type: "enabled", budget_tokens: budget };
5919
+ if (reasoning.providerExtras) Object.assign(thinking, reasoning.providerExtras);
5920
+ body.thinking = thinking;
5921
+ }
5922
+ function applyGeminiReasoning(generationConfig, reasoning) {
5923
+ if (!reasoning) return;
5924
+ const budget = resolveBudget(reasoning, { low: 1024, medium: 4096, high: 12e3, minimal: 512 });
5925
+ if (budget == null) return;
5926
+ const thinkingConfig = { thinkingBudget: budget };
5927
+ if (reasoning.includeThoughts) thinkingConfig.includeThoughts = true;
5928
+ if (reasoning.providerExtras) Object.assign(thinkingConfig, reasoning.providerExtras);
5929
+ generationConfig.thinkingConfig = thinkingConfig;
5930
+ }
5931
+ function normalizeEffort(effort) {
5932
+ if (!effort) return void 0;
5933
+ const allowed = ["minimal", "low", "medium", "high"];
5934
+ return allowed.includes(effort) ? effort : void 0;
5935
+ }
5936
+ function resolveBudget(reasoning, effortBudget) {
5937
+ if (typeof reasoning.budgetTokens === "number" && reasoning.budgetTokens > 0) {
5938
+ return Math.floor(reasoning.budgetTokens);
5939
+ }
5940
+ const effort = normalizeEffort(reasoning.effort);
5941
+ if (effort && effortBudget[effort]) return effortBudget[effort];
5942
+ return void 0;
5943
+ }
5944
+
5730
5945
  // src/providers/openai.ts
5731
5946
  var OpenAIProvider = class extends BaseProvider {
5732
5947
  constructor(config) {
@@ -5842,6 +6057,7 @@ var OpenAIProvider = class extends BaseProvider {
5842
6057
  if (request.extra) {
5843
6058
  Object.assign(body, request.extra);
5844
6059
  }
6060
+ applyOpenAIReasoning(body, request.reasoning, "legacy_effort");
5845
6061
  return body;
5846
6062
  }
5847
6063
  toOpenAIMessage(m) {
@@ -6100,6 +6316,7 @@ var AnthropicProvider = class extends BaseProvider {
6100
6316
  if (request.extra) {
6101
6317
  Object.assign(body, request.extra);
6102
6318
  }
6319
+ applyAnthropicReasoning(body, request.reasoning);
6103
6320
  return body;
6104
6321
  }
6105
6322
  splitSystemMessages(messages) {
@@ -6754,6 +6971,7 @@ var VertexProvider = class extends BaseProvider {
6754
6971
  }
6755
6972
  }
6756
6973
  }
6974
+ applyGeminiReasoning(generationConfig, request.reasoning);
6757
6975
  if (Object.keys(generationConfig).length > 0) body.generationConfig = generationConfig;
6758
6976
  if (request.tools?.length) {
6759
6977
  body.tools = [
@@ -7092,6 +7310,13 @@ function fromNativeProvider(provider, options) {
7092
7310
  if (opts?.maxTokens != null) req.maxTokens = opts.maxTokens;
7093
7311
  if (tools?.length) req.tools = tools;
7094
7312
  if (opts?.extra) req.extra = opts.extra;
7313
+ const reasoningOverride = invokeOptions?.reasoning;
7314
+ const reasoningCfg = reasoningOverride ?? opts?.reasoning;
7315
+ if (reasoningCfg) req.reasoning = reasoningCfg;
7316
+ const tc = invokeOptions?.tool_choice ?? invokeOptions?.toolChoice;
7317
+ if (tc) {
7318
+ req.toolChoice = tc;
7319
+ }
7095
7320
  const rf = invokeOptions?.response_format;
7096
7321
  if (rf && typeof rf === "object" && rf.type) {
7097
7322
  if (rf.type === "json_schema" && rf.json_schema) {