@cognipeer/agent-sdk 0.6.0 → 0.6.2

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;
@@ -2097,8 +2099,6 @@ var BASE_DEFAULTS = {
2097
2099
  policy: "hybrid",
2098
2100
  lastTurnsToKeep: 8,
2099
2101
  toolResponsePolicy: "summarize_archive",
2100
- archiveLargeToolResponses: true,
2101
- retrieveArchivedToolResponseOnDemand: true,
2102
2102
  budget: {
2103
2103
  systemReserveTokens: 1200,
2104
2104
  goalsReserveTokens: 1200,
@@ -2131,8 +2131,7 @@ var BASE_DEFAULTS = {
2131
2131
  defaultPolicy: "summarize_archive",
2132
2132
  toolResponseRetentionByTool: {},
2133
2133
  criticalTools: [...DEFAULT_CRITICAL_TOOLS],
2134
- schemaValidation: "strict",
2135
- retryOnSchemaError: true
2134
+ schemaValidation: "strict"
2136
2135
  }
2137
2136
  };
2138
2137
  function buildProfile(overrides) {
@@ -2929,6 +2928,246 @@ function createToolLimitFinalizeNode(_opts) {
2929
2928
  return { messages: [...state.messages, notice], ctx };
2930
2929
  };
2931
2930
  }
2931
+ var REFLECTION_SYSTEM_NAME = "agent_reflection";
2932
+ var REFLECTION_ASK_NAME = "agent_reflection_ask";
2933
+ var DEFAULT_REFLECT_PROMPT = `Reflect briefly \u2014 plain text, no JSON, no tool calls.
2934
+ Structure your note (1\u20132 sentences each):
2935
+ 1) What the latest tool results actually tell us (facts, surprises).
2936
+ 2) What is still missing or uncertain before we can finish the user's task.
2937
+ 3) The single next step you plan to take, and why that step over alternatives.
2938
+ Be concrete. Do not repeat the task description. Do not apologize. Max ${"${maxChars}"} characters total.`;
2939
+ function trimToChars(text, maxChars) {
2940
+ if (text.length <= maxChars) return text;
2941
+ return text.slice(0, maxChars - 1).trimEnd() + "\u2026";
2942
+ }
2943
+ function countToolCallsInLastTurn(state) {
2944
+ const ids = [];
2945
+ const names = [];
2946
+ for (let i = state.messages.length - 1; i >= 0; i--) {
2947
+ const m = state.messages[i];
2948
+ if (m?.role === "tool") {
2949
+ if (m.tool_call_id) ids.push(m.tool_call_id);
2950
+ if (m.name) names.push(m.name);
2951
+ } else if (m?.role === "assistant") {
2952
+ break;
2953
+ }
2954
+ }
2955
+ return { ids, names };
2956
+ }
2957
+ function stripReflectionAskMessages(messages) {
2958
+ return messages.filter((m) => !(m?.role === "user" && m?.name === REFLECTION_ASK_NAME));
2959
+ }
2960
+ function shouldRunReflection(cadence, state, ranToolsThisTurn) {
2961
+ if (cadence === "off") return false;
2962
+ if (cadence === "every_turn") return true;
2963
+ if (cadence === "after_tool") return ranToolsThisTurn;
2964
+ if (cadence === "on_branch") {
2965
+ const reflections = state.reflections || [];
2966
+ const lastReflectionTurn = reflections[reflections.length - 1]?.turn ?? -1;
2967
+ const currentTurnId = state.toolCallCount ?? 0;
2968
+ return currentTurnId - lastReflectionTurn >= 2;
2969
+ }
2970
+ return false;
2971
+ }
2972
+ function createReflectionNode(opts, resolved) {
2973
+ return async function reflectNode(state, trigger) {
2974
+ if (!resolved.enabled) return {};
2975
+ const runtime = state.agent || {
2976
+ model: opts.model,
2977
+ name: opts.name,
2978
+ version: opts.version
2979
+ };
2980
+ const model = runtime.model;
2981
+ if (!model?.invoke && !model?.stream) return {};
2982
+ const onEvent = state.ctx?.__onEvent;
2983
+ const traceSession = state.ctx?.__traceSession;
2984
+ const maxChars = resolved.maxChars;
2985
+ const promptBody = (resolved.promptTemplate || DEFAULT_REFLECT_PROMPT).replace(/\$\{maxChars\}/g, String(maxChars));
2986
+ const baseMessages = stripReflectionAskMessages([...state.messages]);
2987
+ const askMessage = {
2988
+ role: "user",
2989
+ name: REFLECTION_ASK_NAME,
2990
+ content: promptBody
2991
+ };
2992
+ const messagesForCall = resolved.mode === "piggyback" ? [...baseMessages, askMessage] : buildSeparateCallMessages(baseMessages, promptBody);
2993
+ const start = Date.now();
2994
+ let response;
2995
+ try {
2996
+ response = await model.invoke(messagesForCall, {
2997
+ // tool_choice:"none" so the model won't try to call tools during reflection
2998
+ tool_choice: "none",
2999
+ toolChoice: "none",
3000
+ // Override max_tokens for the reflection call. BaseChatModel passes this through
3001
+ // to the adapter which forwards into the provider body.
3002
+ max_tokens: resolved.maxTokens,
3003
+ maxTokens: resolved.maxTokens
3004
+ });
3005
+ } catch (err) {
3006
+ recordTraceEvent(traceSession, {
3007
+ type: "reflection",
3008
+ label: "Reflection error",
3009
+ actor: { scope: "agent", name: opts.name || "agent", role: "reflection" },
3010
+ status: "error",
3011
+ error: { message: err?.message || String(err) }
3012
+ });
3013
+ return {};
3014
+ }
3015
+ const durationMs = Date.now() - start;
3016
+ const rawText = extractText(response);
3017
+ const text = trimToChars(String(rawText ?? "").trim(), maxChars);
3018
+ if (!text) return {};
3019
+ const rawUsage = response?.usage_metadata || response?.response_metadata?.usage || response?.usage || response?.additional_kwargs?.usage;
3020
+ const usage = normalizeUsage(rawUsage);
3021
+ const { ids, names } = countToolCallsInLastTurn(state);
3022
+ const turn = state.toolCallCount ?? 0;
3023
+ const record = {
3024
+ id: safeRandomId(),
3025
+ turn,
3026
+ text,
3027
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3028
+ durationMs,
3029
+ anchorMessageIndex: state.messages.length,
3030
+ trigger,
3031
+ usage: usage ? {
3032
+ inputTokens: usage.prompt_tokens,
3033
+ outputTokens: usage.completion_tokens,
3034
+ cachedInputTokens: usage.prompt_tokens_details?.cached_tokens,
3035
+ totalTokens: usage.total_tokens,
3036
+ reasoningTokens: usage.completion_tokens_details?.reasoning_tokens
3037
+ } : void 0,
3038
+ toolCallIds: ids.length > 0 ? ids : void 0
3039
+ };
3040
+ recordTraceEvent(traceSession, {
3041
+ type: "reflection",
3042
+ label: `Reflection turn ${turn}`,
3043
+ actor: { scope: "agent", name: opts.name || "agent", role: "reflection" },
3044
+ status: "success",
3045
+ durationMs,
3046
+ inputTokens: record.usage?.inputTokens,
3047
+ outputTokens: record.usage?.outputTokens,
3048
+ cachedInputTokens: record.usage?.cachedInputTokens,
3049
+ totalTokens: record.usage?.totalTokens,
3050
+ debug: { text, toolNames: names }
3051
+ });
3052
+ if (resolved.emitEvents) {
3053
+ try {
3054
+ onEvent?.({
3055
+ type: "reflection",
3056
+ id: record.id,
3057
+ turn,
3058
+ text,
3059
+ trigger,
3060
+ durationMs,
3061
+ usage: record.usage,
3062
+ toolCallIds: record.toolCallIds
3063
+ });
3064
+ } catch (e) {
3065
+ }
3066
+ }
3067
+ const nextMessages = stripReflectionAskMessages([...state.messages]);
3068
+ const reflections = [...state.reflections || [], record];
3069
+ const kept = reflections.slice(-Math.max(1, resolved.keepLast));
3070
+ const prunedMessages = nextMessages.filter(
3071
+ (m) => !(m?.role === "system" && m?.name === REFLECTION_SYSTEM_NAME)
3072
+ );
3073
+ for (const r of kept) {
3074
+ prunedMessages.push({
3075
+ role: "system",
3076
+ name: REFLECTION_SYSTEM_NAME,
3077
+ content: `<agent_reflection turn=${r.turn} at=${r.createdAt}>
3078
+ ${r.text}
3079
+ </agent_reflection>`,
3080
+ metadata: { reflectionId: r.id, reflectionTurn: r.turn, reflectionTrigger: r.trigger }
3081
+ });
3082
+ }
3083
+ return { messages: prunedMessages, reflections };
3084
+ };
3085
+ }
3086
+ function extractText(chunk) {
3087
+ if (chunk == null) return "";
3088
+ if (typeof chunk === "string") return chunk;
3089
+ if (typeof chunk?.content === "string") return chunk.content;
3090
+ if (Array.isArray(chunk?.content)) {
3091
+ return chunk.content.map((c) => typeof c === "string" ? c : c?.text ?? c?.content ?? "").join("");
3092
+ }
3093
+ if (typeof chunk?.text === "string") return chunk.text;
3094
+ return "";
3095
+ }
3096
+ function buildSeparateCallMessages(base, promptBody) {
3097
+ const systemMessages = base.filter((m) => m?.role === "system");
3098
+ const tail = base.slice(-8);
3099
+ return [
3100
+ ...systemMessages,
3101
+ ...tail,
3102
+ { role: "user", name: REFLECTION_ASK_NAME, content: promptBody }
3103
+ ];
3104
+ }
3105
+ function safeRandomId() {
3106
+ try {
3107
+ return randomUUID();
3108
+ } catch {
3109
+ return `r_${Math.random().toString(36).slice(2, 10)}_${Date.now().toString(36)}`;
3110
+ }
3111
+ }
3112
+
3113
+ // src/smart/reasoning.ts
3114
+ var LEVEL_DEFAULTS = {
3115
+ low: {
3116
+ native: { effort: "low" },
3117
+ reflection: { maxTokens: 200, maxChars: 600, cadence: "on_branch" }
3118
+ },
3119
+ medium: {
3120
+ native: { effort: "medium" },
3121
+ reflection: { maxTokens: 350, maxChars: 1200, cadence: "after_tool" }
3122
+ },
3123
+ high: {
3124
+ native: { effort: "high" },
3125
+ reflection: { maxTokens: 600, maxChars: 2e3, cadence: "after_tool" }
3126
+ }
3127
+ };
3128
+ function resolveReasoning(input) {
3129
+ if (!input || input.enabled === false) return void 0;
3130
+ const enabled = input.enabled ?? (input.native !== void 0 || input.reflection !== void 0 || input.level !== void 0);
3131
+ if (!enabled) return void 0;
3132
+ const level = input.level ?? "medium";
3133
+ const presets = LEVEL_DEFAULTS[level];
3134
+ let native;
3135
+ if (input.native === false) {
3136
+ native = void 0;
3137
+ } else {
3138
+ native = { ...presets.native, ...input.native ?? {} };
3139
+ }
3140
+ const reflection = resolveReflection(input.reflection, presets.reflection);
3141
+ return { enabled: true, level, native, reflection };
3142
+ }
3143
+ function resolveReflection(input, preset) {
3144
+ if (input === false) {
3145
+ return {
3146
+ enabled: false,
3147
+ cadence: "off",
3148
+ mode: "piggyback",
3149
+ maxTokens: preset.maxTokens,
3150
+ maxChars: preset.maxChars,
3151
+ keepLast: 3,
3152
+ summarize: false,
3153
+ emitEvents: true
3154
+ };
3155
+ }
3156
+ const cfg = input ?? {};
3157
+ const enabled = cfg.enabled ?? true;
3158
+ const cadence = cfg.cadence ?? preset.cadence;
3159
+ return {
3160
+ enabled: enabled && cadence !== "off",
3161
+ cadence: enabled ? cadence : "off",
3162
+ mode: cfg.mode ?? "piggyback",
3163
+ maxTokens: cfg.maxTokens ?? preset.maxTokens,
3164
+ maxChars: cfg.maxChars ?? preset.maxChars,
3165
+ keepLast: cfg.keepLast ?? 3,
3166
+ summarize: cfg.summarize ?? false,
3167
+ promptTemplate: cfg.promptTemplate,
3168
+ emitEvents: cfg.emitEvents ?? true
3169
+ };
3170
+ }
2932
3171
  function ensureArray(value) {
2933
3172
  return Array.isArray(value) ? value : [];
2934
3173
  }
@@ -3342,6 +3581,8 @@ function createAgent(opts) {
3342
3581
  }
3343
3582
  const toolsNode = createToolsNode(toolsBase, opts);
3344
3583
  const finalizeNode = createToolLimitFinalizeNode();
3584
+ const resolvedReasoning = resolveReasoning(opts.reasoning);
3585
+ const reflectNode = resolvedReasoning?.reflection.enabled ? createReflectionNode(opts, resolvedReasoning.reflection) : void 0;
3345
3586
  const mergeGuardrailOutcomes = (prev, next) => {
3346
3587
  if (!prev) return next;
3347
3588
  return {
@@ -3396,6 +3637,13 @@ function createAgent(opts) {
3396
3637
  if (summarizationThreshold === void 0) {
3397
3638
  state = clearNeedsSummarization(state);
3398
3639
  }
3640
+ if (resolvedReasoning?.native) {
3641
+ const ctx = { ...state.ctx || {} };
3642
+ if (!ctx.__reasoning) {
3643
+ ctx.__reasoning = resolvedReasoning.native;
3644
+ state = { ...state, ctx };
3645
+ }
3646
+ }
3399
3647
  const mergedLimits = {
3400
3648
  ...opts.limits || {},
3401
3649
  ...config?.limits || {}
@@ -3624,6 +3872,18 @@ function createAgent(opts) {
3624
3872
  if (state.ctx?.__awaitingApproval) break;
3625
3873
  if (checkpointIfRequested("after_tools")) break;
3626
3874
  if (state.ctx?.__finalizedDueToStructuredOutput) break;
3875
+ if (reflectNode && resolvedReasoning?.reflection.enabled) {
3876
+ const cadence = resolvedReasoning.reflection.cadence;
3877
+ if (shouldRunReflection(cadence, state, true)) {
3878
+ try {
3879
+ const patch = await reflectNode(state, cadence);
3880
+ if (patch && Object.keys(patch).length > 0) {
3881
+ state = { ...state, ...patch };
3882
+ }
3883
+ } catch {
3884
+ }
3885
+ }
3886
+ }
3627
3887
  }
3628
3888
  if (soManager && !state.ctx?.__finalizedDueToStructuredOutput) {
3629
3889
  const lastForParse = state.messages[state.messages.length - 1];
@@ -5633,6 +5893,52 @@ function parseSSEBlock(block) {
5633
5893
  return { event, data: dataLines.join("\n"), id, retry };
5634
5894
  }
5635
5895
 
5896
+ // src/providers/utils/reasoning.ts
5897
+ function applyOpenAIReasoning(body, reasoning, mode = "legacy_effort") {
5898
+ if (!reasoning) return;
5899
+ const effort = normalizeEffort(reasoning.effort);
5900
+ if (mode === "responses") {
5901
+ const obj = {};
5902
+ if (effort) obj.effort = effort;
5903
+ if (reasoning.includeThoughts) obj.summary = "auto";
5904
+ if (reasoning.providerExtras) Object.assign(obj, reasoning.providerExtras);
5905
+ if (Object.keys(obj).length > 0) body.reasoning = obj;
5906
+ } else {
5907
+ if (effort) body.reasoning_effort = effort;
5908
+ if (reasoning.providerExtras) Object.assign(body, reasoning.providerExtras);
5909
+ }
5910
+ }
5911
+ function applyAnthropicReasoning(body, reasoning) {
5912
+ if (!reasoning) return;
5913
+ const budget = resolveBudget(reasoning, { low: 2048, medium: 8192, high: 16384, minimal: 1024 });
5914
+ if (!budget) return;
5915
+ const thinking = { type: "enabled", budget_tokens: budget };
5916
+ if (reasoning.providerExtras) Object.assign(thinking, reasoning.providerExtras);
5917
+ body.thinking = thinking;
5918
+ }
5919
+ function applyGeminiReasoning(generationConfig, reasoning) {
5920
+ if (!reasoning) return;
5921
+ const budget = resolveBudget(reasoning, { low: 1024, medium: 4096, high: 12e3, minimal: 512 });
5922
+ if (budget == null) return;
5923
+ const thinkingConfig = { thinkingBudget: budget };
5924
+ if (reasoning.includeThoughts) thinkingConfig.includeThoughts = true;
5925
+ if (reasoning.providerExtras) Object.assign(thinkingConfig, reasoning.providerExtras);
5926
+ generationConfig.thinkingConfig = thinkingConfig;
5927
+ }
5928
+ function normalizeEffort(effort) {
5929
+ if (!effort) return void 0;
5930
+ const allowed = ["minimal", "low", "medium", "high"];
5931
+ return allowed.includes(effort) ? effort : void 0;
5932
+ }
5933
+ function resolveBudget(reasoning, effortBudget) {
5934
+ if (typeof reasoning.budgetTokens === "number" && reasoning.budgetTokens > 0) {
5935
+ return Math.floor(reasoning.budgetTokens);
5936
+ }
5937
+ const effort = normalizeEffort(reasoning.effort);
5938
+ if (effort && effortBudget[effort]) return effortBudget[effort];
5939
+ return void 0;
5940
+ }
5941
+
5636
5942
  // src/providers/openai.ts
5637
5943
  var OpenAIProvider = class extends BaseProvider {
5638
5944
  constructor(config) {
@@ -5748,6 +6054,7 @@ var OpenAIProvider = class extends BaseProvider {
5748
6054
  if (request.extra) {
5749
6055
  Object.assign(body, request.extra);
5750
6056
  }
6057
+ applyOpenAIReasoning(body, request.reasoning, "legacy_effort");
5751
6058
  return body;
5752
6059
  }
5753
6060
  toOpenAIMessage(m) {
@@ -6006,6 +6313,7 @@ var AnthropicProvider = class extends BaseProvider {
6006
6313
  if (request.extra) {
6007
6314
  Object.assign(body, request.extra);
6008
6315
  }
6316
+ applyAnthropicReasoning(body, request.reasoning);
6009
6317
  return body;
6010
6318
  }
6011
6319
  splitSystemMessages(messages) {
@@ -6660,6 +6968,7 @@ var VertexProvider = class extends BaseProvider {
6660
6968
  }
6661
6969
  }
6662
6970
  }
6971
+ applyGeminiReasoning(generationConfig, request.reasoning);
6663
6972
  if (Object.keys(generationConfig).length > 0) body.generationConfig = generationConfig;
6664
6973
  if (request.tools?.length) {
6665
6974
  body.tools = [
@@ -6998,6 +7307,13 @@ function fromNativeProvider(provider, options) {
6998
7307
  if (opts?.maxTokens != null) req.maxTokens = opts.maxTokens;
6999
7308
  if (tools?.length) req.tools = tools;
7000
7309
  if (opts?.extra) req.extra = opts.extra;
7310
+ const reasoningOverride = invokeOptions?.reasoning;
7311
+ const reasoningCfg = reasoningOverride ?? opts?.reasoning;
7312
+ if (reasoningCfg) req.reasoning = reasoningCfg;
7313
+ const tc = invokeOptions?.tool_choice ?? invokeOptions?.toolChoice;
7314
+ if (tc) {
7315
+ req.toolChoice = tc;
7316
+ }
7001
7317
  const rf = invokeOptions?.response_format;
7002
7318
  if (rf && typeof rf === "object" && rf.type) {
7003
7319
  if (rf.type === "json_schema" && rf.json_schema) {