@cognipeer/agent-sdk 0.6.4 → 0.6.5

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
@@ -18,7 +18,99 @@ var path__default = /*#__PURE__*/_interopDefault(path);
18
18
  var crypto__default = /*#__PURE__*/_interopDefault(crypto);
19
19
  var AjvModule__default = /*#__PURE__*/_interopDefault(AjvModule);
20
20
 
21
- // src/adapters/langchain.ts
21
+ var __defProp = Object.defineProperty;
22
+ var __getOwnPropNames = Object.getOwnPropertyNames;
23
+ var __esm = (fn, res) => function __init() {
24
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
25
+ };
26
+ var __export = (target, all) => {
27
+ for (var name in all)
28
+ __defProp(target, name, { get: all[name], enumerable: true });
29
+ };
30
+
31
+ // src/utils/utilTokens.ts
32
+ var utilTokens_exports = {};
33
+ __export(utilTokens_exports, {
34
+ countApproxTokens: () => countApproxTokens,
35
+ countMessagesTokens: () => countMessagesTokens,
36
+ defaultTokenCounter: () => defaultTokenCounter,
37
+ getTokenCounter: () => getTokenCounter,
38
+ setTokenCounter: () => setTokenCounter
39
+ });
40
+ function defaultTokenCounter(text) {
41
+ if (!text) return 0;
42
+ const nonAsciiCount = (text.match(/[^\x00-\x7F]/g) || []).length;
43
+ const asciiCount = text.length - nonAsciiCount;
44
+ return Math.ceil(asciiCount / 4 + nonAsciiCount / 1.5);
45
+ }
46
+ function setTokenCounter(counter) {
47
+ activeTokenCounter = typeof counter === "function" ? counter : defaultTokenCounter;
48
+ }
49
+ function getTokenCounter() {
50
+ return activeTokenCounter;
51
+ }
52
+ function countApproxTokens(text) {
53
+ return activeTokenCounter(text || "");
54
+ }
55
+ function extractMessageText(m) {
56
+ if (!m) return "";
57
+ const parts = [];
58
+ parts.push(m.role || "");
59
+ if (m.name) parts.push(m.name);
60
+ if (typeof m.content === "string") {
61
+ parts.push(m.content);
62
+ } else if (Array.isArray(m.content)) {
63
+ for (const c of m.content) {
64
+ if (typeof c === "string") parts.push(c);
65
+ else if (c?.text) parts.push(c.text);
66
+ else if (c?.content) parts.push(c.content);
67
+ else if (c?.type === "tool_use" && c?.input) {
68
+ parts.push(c.name || "");
69
+ parts.push(typeof c.input === "string" ? c.input : JSON.stringify(c.input));
70
+ } else if (c != null && typeof c === "object") {
71
+ parts.push(JSON.stringify(c));
72
+ }
73
+ }
74
+ } else if (m.content != null && typeof m.content === "object") {
75
+ parts.push(JSON.stringify(m.content));
76
+ }
77
+ if (Array.isArray(m.tool_calls)) {
78
+ for (const tc of m.tool_calls) {
79
+ if (tc?.function?.name) parts.push(tc.function.name);
80
+ if (tc?.function?.arguments) {
81
+ parts.push(
82
+ typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
83
+ );
84
+ }
85
+ if (tc?.name && !tc?.function?.name) parts.push(tc.name);
86
+ if (tc?.args && !tc?.function?.arguments) {
87
+ parts.push(typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args));
88
+ }
89
+ }
90
+ }
91
+ if (Array.isArray(m.additional_kwargs?.tool_calls)) {
92
+ for (const tc of m.additional_kwargs.tool_calls) {
93
+ if (tc?.function?.name) parts.push(tc.function.name);
94
+ if (tc?.function?.arguments) {
95
+ parts.push(
96
+ typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
97
+ );
98
+ }
99
+ }
100
+ }
101
+ return parts.join("\n");
102
+ }
103
+ function countMessagesTokens(messages) {
104
+ if (!Array.isArray(messages) || messages.length === 0) return 0;
105
+ const allText = messages.map(extractMessageText).join("\n");
106
+ return countApproxTokens(allText) + messages.length * 4;
107
+ }
108
+ var activeTokenCounter;
109
+ var init_utilTokens = __esm({
110
+ "src/utils/utilTokens.ts"() {
111
+ activeTokenCounter = defaultTokenCounter;
112
+ }
113
+ });
22
114
  var moduleName = "@langchain/core/tools";
23
115
  var cachedToolFactory;
24
116
  var cachedHasAttemptedFactory = false;
@@ -326,7 +418,9 @@ function createTool({
326
418
  needsApproval,
327
419
  approvalPrompt,
328
420
  approvalDefaults,
329
- maxExecutionsPerRun
421
+ maxExecutionsPerRun,
422
+ cache,
423
+ retry
330
424
  }) {
331
425
  const execute = async (input) => func(input);
332
426
  const toolRecord = {
@@ -351,6 +445,12 @@ function createTool({
351
445
  if (maxExecutionsPerRun !== void 0) {
352
446
  toolRecord.maxExecutionsPerRun = maxExecutionsPerRun;
353
447
  }
448
+ if (cache !== void 0) {
449
+ toolRecord.cache = cache;
450
+ }
451
+ if (retry !== void 0) {
452
+ toolRecord.retry = retry;
453
+ }
354
454
  toolRecord.__source = toolRecord.__source || "smart";
355
455
  toolRecord.__impl = func;
356
456
  return toolRecord;
@@ -1892,7 +1992,8 @@ function createAgentCoreNode(opts) {
1892
1992
  todoListPrompt: opts.todoListPrompt,
1893
1993
  limits: opts.limits,
1894
1994
  outputSchema: opts.outputSchema,
1895
- tracing: opts.tracing
1995
+ tracing: opts.tracing,
1996
+ responseFormat: void 0
1896
1997
  };
1897
1998
  const tools = runtime.tools ?? [];
1898
1999
  const shouldUseStrictToolCalling = Boolean(
@@ -2094,7 +2195,7 @@ function createAgentCoreNode(opts) {
2094
2195
  // src/smart/runtimeConfig.ts
2095
2196
  var DEFAULT_CRITICAL_TOOLS = ["response", "manage_todo_list", "get_tool_response"];
2096
2197
  var BASE_DEFAULTS = {
2097
- limits: { maxToolCalls: 8, maxParallelTools: 2, maxContextTokens: 24e3 },
2198
+ limits: { maxToolCalls: 8, maxParallelTools: 2, maxContextTokens: 24e3, maxTotalOutputTokens: 0, maxCostUsd: 0, maxWallClockMs: 0 },
2098
2199
  summarization: {
2099
2200
  enable: true,
2100
2201
  maxTokens: 24e3,
@@ -2221,7 +2322,10 @@ function mergeLimits(base, override) {
2221
2322
  return {
2222
2323
  maxToolCalls: override?.maxToolCalls ?? base.maxToolCalls,
2223
2324
  maxParallelTools: override?.maxParallelTools ?? base.maxParallelTools,
2224
- maxContextTokens: override?.maxContextTokens ?? base.maxContextTokens
2325
+ maxContextTokens: override?.maxContextTokens ?? base.maxContextTokens,
2326
+ maxTotalOutputTokens: override?.maxTotalOutputTokens ?? base.maxTotalOutputTokens ?? 0,
2327
+ maxCostUsd: override?.maxCostUsd ?? base.maxCostUsd ?? 0,
2328
+ maxWallClockMs: override?.maxWallClockMs ?? base.maxWallClockMs ?? 0
2225
2329
  };
2226
2330
  }
2227
2331
  function resolveBaseProfile(opts, runtimeProfile) {
@@ -2299,66 +2403,8 @@ function getResolvedSmartConfig(opts, runtime) {
2299
2403
  return runtime?.smart || normalizeSmartAgentOptions(opts);
2300
2404
  }
2301
2405
 
2302
- // src/utils/utilTokens.ts
2303
- function countApproxTokens(text) {
2304
- if (!text) return 0;
2305
- const nonAsciiCount = (text.match(/[^\x00-\x7F]/g) || []).length;
2306
- const asciiCount = text.length - nonAsciiCount;
2307
- return Math.ceil(asciiCount / 4 + nonAsciiCount / 1.5);
2308
- }
2309
- function extractMessageText(m) {
2310
- if (!m) return "";
2311
- const parts = [];
2312
- parts.push(m.role || "");
2313
- if (m.name) parts.push(m.name);
2314
- if (typeof m.content === "string") {
2315
- parts.push(m.content);
2316
- } else if (Array.isArray(m.content)) {
2317
- for (const c of m.content) {
2318
- if (typeof c === "string") parts.push(c);
2319
- else if (c?.text) parts.push(c.text);
2320
- else if (c?.content) parts.push(c.content);
2321
- else if (c?.type === "tool_use" && c?.input) {
2322
- parts.push(c.name || "");
2323
- parts.push(typeof c.input === "string" ? c.input : JSON.stringify(c.input));
2324
- } else if (c != null && typeof c === "object") {
2325
- parts.push(JSON.stringify(c));
2326
- }
2327
- }
2328
- } else if (m.content != null && typeof m.content === "object") {
2329
- parts.push(JSON.stringify(m.content));
2330
- }
2331
- if (Array.isArray(m.tool_calls)) {
2332
- for (const tc of m.tool_calls) {
2333
- if (tc?.function?.name) parts.push(tc.function.name);
2334
- if (tc?.function?.arguments) {
2335
- parts.push(
2336
- typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
2337
- );
2338
- }
2339
- if (tc?.name && !tc?.function?.name) parts.push(tc.name);
2340
- if (tc?.args && !tc?.function?.arguments) {
2341
- parts.push(typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args));
2342
- }
2343
- }
2344
- }
2345
- if (Array.isArray(m.additional_kwargs?.tool_calls)) {
2346
- for (const tc of m.additional_kwargs.tool_calls) {
2347
- if (tc?.function?.name) parts.push(tc.function.name);
2348
- if (tc?.function?.arguments) {
2349
- parts.push(
2350
- typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
2351
- );
2352
- }
2353
- }
2354
- }
2355
- return parts.join("\n");
2356
- }
2357
- function countMessagesTokens(messages) {
2358
- if (!Array.isArray(messages) || messages.length === 0) return 0;
2359
- const allText = messages.map(extractMessageText).join("\n");
2360
- return countApproxTokens(allText) + messages.length * 4;
2361
- }
2406
+ // src/smart/toolResponses.ts
2407
+ init_utilTokens();
2362
2408
 
2363
2409
  // src/utils/content.ts
2364
2410
  function contentToString(content) {
@@ -2558,6 +2604,7 @@ function createToolsNode(initialTools, opts) {
2558
2604
  anyTool._stateRef.messages = state.messages;
2559
2605
  anyTool._stateRef.ctx = state.ctx || (state.ctx = {});
2560
2606
  anyTool._stateRef.__onEvent = onEvent;
2607
+ anyTool._stateRef.parentRuntime = runtime;
2561
2608
  }
2562
2609
  }
2563
2610
  const last = state.messages[state.messages.length - 1];
@@ -2568,6 +2615,7 @@ function createToolsNode(initialTools, opts) {
2568
2615
  const remaining = Math.max(0, limits.maxToolCalls - toolCount);
2569
2616
  const planned = toolCalls.slice(0, remaining);
2570
2617
  const skipped = toolCalls.slice(remaining);
2618
+ const orderedSlots = planned.map(() => []);
2571
2619
  const isCancelled = () => {
2572
2620
  if (abortSignal?.aborted) return { cancelled: true, reason: "aborted" };
2573
2621
  if (cancellationToken && cancellationToken.isCancellationRequested) return { cancelled: true, reason: "cancelled" };
@@ -2584,7 +2632,9 @@ function createToolsNode(initialTools, opts) {
2584
2632
  }
2585
2633
  state.ctx = ctx;
2586
2634
  };
2587
- const runOne = async (tc) => {
2635
+ const runOne = async (tc, slot = appended) => {
2636
+ var _a;
2637
+ const push = (msg) => slot.push(msg);
2588
2638
  const cancelState = isCancelled();
2589
2639
  if (cancelState.cancelled) {
2590
2640
  const ctx = state.ctx = state.ctx || {};
@@ -2595,7 +2645,7 @@ function createToolsNode(initialTools, opts) {
2595
2645
  }
2596
2646
  const tool = toolByName.get(tc.name);
2597
2647
  if (!tool) {
2598
- appended.push({ role: "tool", content: `Tool not found: ${tc.name}`, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
2648
+ push({ role: "tool", content: `Tool not found: ${tc.name}`, tool_call_id: tc.id || `${tc.name}_unknown`, name: tc.name });
2599
2649
  onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args: tc.args, error: { message: "Tool not found" } });
2600
2650
  toolCount += 1;
2601
2651
  return { status: "error" };
@@ -2611,7 +2661,7 @@ function createToolsNode(initialTools, opts) {
2611
2661
  const validatedArgs = validateToolArgs(tool, args);
2612
2662
  if (!validatedArgs.ok && resolved.toolResponses.schemaValidation === "strict") {
2613
2663
  const errorMessage = `Tool argument validation failed for ${tc.name}: ${validatedArgs.message}`;
2614
- appended.push({ role: "tool", content: errorMessage, tool_call_id: toolCallId, name: tc.name });
2664
+ push({ role: "tool", content: errorMessage, tool_call_id: toolCallId, name: tc.name });
2615
2665
  markToolFailure(errorMessage, tc.name, toolCallId);
2616
2666
  onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args, error: { message: errorMessage } });
2617
2667
  toolCount += 1;
@@ -2633,7 +2683,7 @@ function createToolsNode(initialTools, opts) {
2633
2683
  const currentExecutions = countSuccessfulToolExecutions(toolHistory, toolName);
2634
2684
  if (currentExecutions >= maxExecutionsPerRun) {
2635
2685
  const limitMessage = `Skipped tool due to per-tool execution limit: ${toolName} (${maxExecutionsPerRun}/run)`;
2636
- appended.push({ role: "tool", content: limitMessage, tool_call_id: toolCallId, name: tc.name });
2686
+ push({ role: "tool", content: limitMessage, tool_call_id: toolCallId, name: tc.name });
2637
2687
  onEvent?.({ type: "tool_call", phase: "skipped", name: toolName, id: tc.id, args });
2638
2688
  recordTraceEvent(traceSession, {
2639
2689
  type: "tool_call",
@@ -2693,7 +2743,7 @@ function createToolsNode(initialTools, opts) {
2693
2743
  }
2694
2744
  if (approvalEntry.status === "rejected") {
2695
2745
  const rejectionMessage = approvalEntry.comment || "Tool call rejected by reviewer.";
2696
- appended.push({ role: "tool", content: `Tool call rejected: ${rejectionMessage}`, tool_call_id: toolCallId, name: tc.name });
2746
+ push({ role: "tool", content: `Tool call rejected: ${rejectionMessage}`, tool_call_id: toolCallId, name: tc.name });
2697
2747
  approvalEntry.metadata = { ...approvalEntry.metadata || {}, resolution: "rejected" };
2698
2748
  approvalEntry.status = "executed";
2699
2749
  approvalEntry.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -2711,23 +2761,102 @@ function createToolsNode(initialTools, opts) {
2711
2761
  const start = Date.now();
2712
2762
  const sanitizedArgs = sanitizeTracePayload(args);
2713
2763
  const inputBytes = traceSession?.resolvedConfig.logData ? estimatePayloadBytes(sanitizedArgs) : void 0;
2764
+ const cacheCfg = tool.cache;
2765
+ const cacheEnabled = cacheCfg === true || cacheCfg && typeof cacheCfg === "object";
2766
+ const cacheKeyFn = cacheEnabled && typeof cacheCfg === "object" ? cacheCfg.keyFn : void 0;
2767
+ const stableJson = (value) => {
2768
+ try {
2769
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
2770
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
2771
+ const keys = Object.keys(value).sort();
2772
+ return `{${keys.map((k) => JSON.stringify(k) + ":" + stableJson(value[k])).join(",")}}`;
2773
+ } catch {
2774
+ return String(value);
2775
+ }
2776
+ };
2777
+ const cacheKey = cacheEnabled ? `${toolName}|${cacheKeyFn ? cacheKeyFn(args) : stableJson(args)}` : void 0;
2778
+ const toolCache = state.toolCache = state.toolCache || {};
2779
+ let cachedHit = void 0;
2780
+ if (cacheKey && Object.prototype.hasOwnProperty.call(toolCache, cacheKey)) {
2781
+ const entry = toolCache[cacheKey];
2782
+ const ttl = cacheEnabled && typeof cacheCfg === "object" ? cacheCfg.ttlMs : void 0;
2783
+ if (!ttl || entry?.timestamp && Date.now() - entry.timestamp <= ttl) {
2784
+ cachedHit = entry?.value;
2785
+ }
2786
+ }
2787
+ const retryCfg = tool.retry;
2788
+ const failureStats = (_a = state.ctx).__toolFailureCounts || (_a.__toolFailureCounts = {});
2789
+ if (retryCfg?.circuitBreakerThreshold && failureStats[toolName] >= retryCfg.circuitBreakerThreshold) {
2790
+ const breakerMsg = `Tool ${toolName} circuit breaker open after ${failureStats[toolName]} consecutive failures`;
2791
+ push({ role: "tool", content: breakerMsg, tool_call_id: toolCallId, name: tc.name });
2792
+ markToolFailure(breakerMsg, toolName, toolCallId);
2793
+ onEvent?.({ type: "tool_call", phase: "error", name: toolName, id: tc.id, args, error: { message: breakerMsg } });
2794
+ toolCount += 1;
2795
+ return { status: "error" };
2796
+ }
2714
2797
  try {
2798
+ if (cachedHit !== void 0) {
2799
+ const executionId2 = nanoid.nanoid();
2800
+ const responsePolicy2 = applyToolResponseHardCap(toolName, cachedHit, executionId2, resolved);
2801
+ toolHistory.push({
2802
+ executionId: executionId2,
2803
+ toolName,
2804
+ args,
2805
+ output: cachedHit,
2806
+ rawOutput: cachedHit,
2807
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2808
+ tool_call_id: tc.id,
2809
+ summarized: false,
2810
+ originalTokenCount: responsePolicy2.tokenCount,
2811
+ classification: responsePolicy2.classification,
2812
+ retentionPolicy: "keep_full",
2813
+ fromCache: true,
2814
+ status: "success"
2815
+ });
2816
+ push({ role: "tool", content: responsePolicy2.content, tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
2817
+ onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: cachedHit, durationMs: 0 });
2818
+ toolCount += 1;
2819
+ return { status: "success" };
2820
+ }
2715
2821
  onEvent?.({ type: "tool_call", phase: "start", name: toolName, id: tc.id, args });
2716
2822
  onProgress?.({ stage: "tools", message: `Running tool ${tc.name}` });
2717
2823
  const anyTool = tool;
2718
2824
  const callOptions = { cancellationToken, signal: abortSignal };
2825
+ const invokeOnce = async () => {
2826
+ if (typeof anyTool.func === "function") return anyTool.func(args, callOptions);
2827
+ if (typeof anyTool.invoke === "function") return anyTool.invoke(args, callOptions);
2828
+ if (typeof anyTool.call === "function") return anyTool.call(args, callOptions);
2829
+ if (typeof anyTool._call === "function") return anyTool._call(args, callOptions);
2830
+ if (typeof anyTool.run === "function") return anyTool.run(args, callOptions);
2831
+ throw new Error("Tool is not invokable");
2832
+ };
2719
2833
  let output;
2720
- if (typeof anyTool.func === "function") output = await anyTool.func(args, callOptions);
2721
- else if (typeof anyTool.invoke === "function") output = await anyTool.invoke(args, callOptions);
2722
- else if (typeof anyTool.call === "function") output = await anyTool.call(args, callOptions);
2723
- else if (typeof anyTool._call === "function") output = await anyTool._call(args, callOptions);
2724
- else if (typeof anyTool.run === "function") output = await anyTool.run(args, callOptions);
2725
- else throw new Error("Tool is not invokable");
2834
+ const maxRetries = Math.max(0, Number(retryCfg?.maxRetries) || 0);
2835
+ const backoffMs = Math.max(0, Number(retryCfg?.backoffMs) || 250);
2836
+ let attempt = 0;
2837
+ while (true) {
2838
+ try {
2839
+ output = await invokeOnce();
2840
+ failureStats[toolName] = 0;
2841
+ break;
2842
+ } catch (retryErr) {
2843
+ attempt += 1;
2844
+ const shouldRetry = retryCfg?.shouldRetry ? retryCfg.shouldRetry(retryErr, attempt) : true;
2845
+ if (attempt > maxRetries || !shouldRetry) {
2846
+ failureStats[toolName] = (failureStats[toolName] || 0) + 1;
2847
+ throw retryErr;
2848
+ }
2849
+ await new Promise((r) => setTimeout(r, backoffMs * Math.pow(2, attempt - 1)));
2850
+ }
2851
+ }
2852
+ if (cacheKey) {
2853
+ toolCache[cacheKey] = { value: output, timestamp: Date.now() };
2854
+ }
2726
2855
  const durationMs = Date.now() - start;
2727
2856
  const executionId = nanoid.nanoid();
2728
2857
  if (output && typeof output === "object" && output.__handoff && output.__handoff.runtime) {
2729
2858
  toolHistory.push({ executionId, toolName, args, output: "handoff:ok", rawOutput: output, timestamp: (/* @__PURE__ */ new Date()).toISOString(), tool_call_id: tc.id, status: "handoff" });
2730
- appended.push({ role: "tool", content: "ok", tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
2859
+ push({ role: "tool", content: "ok", tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
2731
2860
  state.agent = output.__handoff.runtime;
2732
2861
  onEvent?.({ type: "handoff", from: runtime.name, to: state.agent?.name, toolName });
2733
2862
  onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: "handoff", durationMs });
@@ -2779,7 +2908,7 @@ function createToolsNode(initialTools, opts) {
2779
2908
  archiveId: void 0,
2780
2909
  status: "success"
2781
2910
  });
2782
- appended.push({ role: "tool", content: responsePolicy.content, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
2911
+ push({ role: "tool", content: responsePolicy.content, tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
2783
2912
  onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: output, durationMs });
2784
2913
  onProgress?.({ stage: "tools", message: `Tool ${tc.name} completed`, detail: { durationMs } });
2785
2914
  if (needsApproval && approvalEntry) {
@@ -2835,7 +2964,7 @@ function createToolsNode(initialTools, opts) {
2835
2964
  const executionId = nanoid.nanoid();
2836
2965
  const message = error?.message || String(error);
2837
2966
  toolHistory.push({ executionId, toolName, args, output: `Error executing tool: ${message}`, rawOutput: null, timestamp: (/* @__PURE__ */ new Date()).toISOString(), tool_call_id: tc.id, status: "error" });
2838
- appended.push({ role: "tool", content: `Error executing tool: ${message}`, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
2967
+ push({ role: "tool", content: `Error executing tool: ${message}`, tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
2839
2968
  markToolFailure(message, toolName, toolCallId);
2840
2969
  onEvent?.({ type: "tool_call", phase: "error", name: toolName, id: tc.id, args, error: { message } });
2841
2970
  onProgress?.({ stage: "tools", message: `Tool ${tc.name} failed`, detail: { error: message } });
@@ -2872,22 +3001,67 @@ function createToolsNode(initialTools, opts) {
2872
3001
  return { status: "error", approval: approvalEntry };
2873
3002
  }
2874
3003
  };
2875
- for (const tc of planned) {
2876
- if (awaitingApproval) break;
3004
+ const approvalIndices = [];
3005
+ const parallelIndices = [];
3006
+ planned.forEach((tc, idx) => {
3007
+ const t = toolByName.get(tc.name);
3008
+ if (t && t.needsApproval) approvalIndices.push(idx);
3009
+ else parallelIndices.push(idx);
3010
+ });
3011
+ let shouldStop = false;
3012
+ const checkCancellationStop = () => {
2877
3013
  const cancelState = isCancelled();
2878
3014
  if (cancelState.cancelled) {
2879
3015
  const ctx = state.ctx = state.ctx || {};
2880
3016
  ctx.__cancelled = { stage: "tools", reason: cancelState.reason, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
2881
3017
  onEvent?.({ type: "cancelled", stage: "tools", reason: cancelState.reason });
2882
3018
  onProgress?.({ stage: "tools", message: "Cancelled", detail: { reason: cancelState.reason } });
2883
- break;
3019
+ shouldStop = true;
3020
+ return true;
2884
3021
  }
2885
- const result = await runOne(tc);
3022
+ return false;
3023
+ };
3024
+ for (const idx of approvalIndices) {
3025
+ if (awaitingApproval || shouldStop) break;
3026
+ if (checkCancellationStop()) break;
3027
+ const result = await runOne(planned[idx], orderedSlots[idx]);
2886
3028
  if (result.status === "awaiting_approval") {
2887
3029
  awaitingApproval = true;
2888
3030
  break;
2889
3031
  }
2890
3032
  }
3033
+ if (!awaitingApproval && !shouldStop && parallelIndices.length > 0) {
3034
+ const concurrency = Math.max(1, Math.min(limits.maxParallelTools, parallelIndices.length));
3035
+ let cursor = 0;
3036
+ const workerError = {};
3037
+ const workers = [];
3038
+ for (let w = 0; w < concurrency; w++) {
3039
+ workers.push((async () => {
3040
+ while (true) {
3041
+ if (workerError.err || shouldStop || awaitingApproval) return;
3042
+ const i = cursor++;
3043
+ if (i >= parallelIndices.length) return;
3044
+ if (checkCancellationStop()) return;
3045
+ const idx = parallelIndices[i];
3046
+ try {
3047
+ const result = await runOne(planned[idx], orderedSlots[idx]);
3048
+ if (result.status === "awaiting_approval") {
3049
+ awaitingApproval = true;
3050
+ return;
3051
+ }
3052
+ } catch (err) {
3053
+ workerError.err = err;
3054
+ return;
3055
+ }
3056
+ }
3057
+ })());
3058
+ }
3059
+ await Promise.all(workers);
3060
+ if (workerError.err) throw workerError.err;
3061
+ }
3062
+ for (const slot of orderedSlots) {
3063
+ if (slot.length > 0) appended.push(...slot);
3064
+ }
2891
3065
  for (const tc of skipped) {
2892
3066
  onEvent?.({ type: "tool_call", phase: "skipped", name: tc.name, id: tc.id, args: tc.args });
2893
3067
  const sanitizedArgs = sanitizeTracePayload(tc.args);
@@ -2923,11 +3097,23 @@ function createToolsNode(initialTools, opts) {
2923
3097
  }
2924
3098
  state.ctx = Object.keys(ctx).length > 0 ? ctx : void 0;
2925
3099
  }
3100
+ const appendedHasToolResult = appended.some((m) => m.role === "tool");
3101
+ if (appendedHasToolResult && state.ctx?.__summarizationExhausted) {
3102
+ const ctx = { ...state.ctx };
3103
+ delete ctx.__summarizationExhausted;
3104
+ state.ctx = Object.keys(ctx).length > 0 ? ctx : void 0;
3105
+ }
2926
3106
  return {
2927
3107
  messages: [...state.messages, ...appended],
2928
3108
  toolCallCount: toolCount,
2929
3109
  toolHistory,
2930
3110
  toolHistoryArchived,
3111
+ toolCache: state.toolCache,
3112
+ // Explicitly propagate ctx so callers using `{ ...state, ...delta }`
3113
+ // do not silently capture a stale ctx reference. Mutations to
3114
+ // __summarizationExhausted / __awaitingApproval / __structuredOutputParsed
3115
+ // are made on this object during execution.
3116
+ ctx: state.ctx,
2931
3117
  agent: state.agent,
2932
3118
  pendingApprovals
2933
3119
  };
@@ -3164,7 +3350,8 @@ function resolveReflection(input, preset) {
3164
3350
  maxChars: preset.maxChars,
3165
3351
  keepLast: 3,
3166
3352
  summarize: false,
3167
- emitEvents: true
3353
+ emitEvents: true,
3354
+ everyNTurns: 1
3168
3355
  };
3169
3356
  }
3170
3357
  const cfg = input ?? {};
@@ -3179,7 +3366,9 @@ function resolveReflection(input, preset) {
3179
3366
  keepLast: cfg.keepLast ?? 3,
3180
3367
  summarize: cfg.summarize ?? false,
3181
3368
  promptTemplate: cfg.promptTemplate,
3182
- emitEvents: cfg.emitEvents ?? true
3369
+ emitEvents: cfg.emitEvents ?? true,
3370
+ maxPerRun: typeof cfg.maxPerRun === "number" && cfg.maxPerRun >= 0 ? cfg.maxPerRun : void 0,
3371
+ everyNTurns: Math.max(1, Math.trunc(cfg.everyNTurns ?? 1))
3183
3372
  };
3184
3373
  }
3185
3374
  function ensureArray(value) {
@@ -3392,6 +3581,9 @@ function resolveToolApprovalState(state, resolution) {
3392
3581
  };
3393
3582
  }
3394
3583
 
3584
+ // src/agent.ts
3585
+ init_utilTokens();
3586
+
3395
3587
  // src/utils/syntheticMessages.ts
3396
3588
  function isSyntheticSummaryMessage(message) {
3397
3589
  if (!message) return false;
@@ -3570,6 +3762,21 @@ function getActiveSummarizationThreshold(opts) {
3570
3762
  }
3571
3763
  return summarization.summaryTriggerTokens || summarization.maxTokens || opts.limits?.maxContextTokens || 5e4;
3572
3764
  }
3765
+ function shouldSignalSummarization(state, threshold) {
3766
+ const tokCountMessages = (state.messages || []).filter((message) => {
3767
+ if (isSyntheticSummaryMessage(message)) return false;
3768
+ if (message.role === "system" && (message.name === "context_summary" || message.name === "memory_context")) return false;
3769
+ return true;
3770
+ });
3771
+ const tokenCount = countMessagesTokens(tokCountMessages);
3772
+ if (tokenCount <= threshold) return "skip";
3773
+ if (state.ctx?.__summarizationExhausted) return "skip";
3774
+ const hasFreshSummary = (state.messages || []).some(
3775
+ (m) => m.role === "tool" && typeof m.content === "string" && (m.content === "SUMMARIZED" || m.content.startsWith("SUMMARIZED_TOOL_RESPONSE"))
3776
+ );
3777
+ if (hasFreshSummary && tokenCount <= threshold * 1.15) return "skip";
3778
+ return "trigger";
3779
+ }
3573
3780
  function clearNeedsSummarization(state) {
3574
3781
  if (!state.ctx?.__needsSummarization) {
3575
3782
  return state;
@@ -3664,6 +3871,45 @@ function createAgent(opts) {
3664
3871
  };
3665
3872
  const maxToolCalls = mergedLimits?.maxToolCalls === void 0 ? 50 : mergedLimits?.maxToolCalls;
3666
3873
  const iterationLimit = maxToolCalls === Infinity ? 100 : Math.max(maxToolCalls * 3 + 10, 40);
3874
+ const wallClockStart = Date.now();
3875
+ const maxWallClockMs = mergedLimits?.maxWallClockMs && mergedLimits.maxWallClockMs > 0 ? mergedLimits.maxWallClockMs : void 0;
3876
+ const maxTotalOutputTokens = mergedLimits?.maxTotalOutputTokens && mergedLimits.maxTotalOutputTokens > 0 ? mergedLimits.maxTotalOutputTokens : void 0;
3877
+ const maxCostUsd = mergedLimits?.maxCostUsd && mergedLimits.maxCostUsd > 0 ? mergedLimits.maxCostUsd : void 0;
3878
+ const costEstimator = opts.costEstimator;
3879
+ const checkBudgetBreached = () => {
3880
+ if (maxWallClockMs && Date.now() - wallClockStart > maxWallClockMs) {
3881
+ return { breached: true, reason: `maxWallClockMs (${maxWallClockMs}ms) exceeded` };
3882
+ }
3883
+ if (maxTotalOutputTokens) {
3884
+ const totals = state.usage?.totals;
3885
+ if (totals) {
3886
+ let outputSum = 0;
3887
+ for (const v of Object.values(totals)) outputSum += Number(v?.output) || 0;
3888
+ if (outputSum > maxTotalOutputTokens) {
3889
+ return { breached: true, reason: `maxTotalOutputTokens (${maxTotalOutputTokens}) exceeded` };
3890
+ }
3891
+ }
3892
+ }
3893
+ if (maxCostUsd && costEstimator) {
3894
+ const perRequest = state.usage?.perRequest;
3895
+ if (perRequest) {
3896
+ let cost = 0;
3897
+ for (const r of perRequest) {
3898
+ cost += costEstimator({
3899
+ modelName: r.modelName,
3900
+ inputTokens: Number(r.usage?.prompt_tokens) || 0,
3901
+ outputTokens: Number(r.usage?.completion_tokens) || 0,
3902
+ cachedInputTokens: Number(r.usage?.prompt_tokens_details?.cached_tokens) || 0,
3903
+ reasoningTokens: Number(r.usage?.completion_tokens_details?.reasoning_tokens) || 0
3904
+ }) || 0;
3905
+ }
3906
+ if (cost > maxCostUsd) {
3907
+ return { breached: true, reason: `maxCostUsd ($${maxCostUsd}) exceeded (actual: $${cost.toFixed(4)})` };
3908
+ }
3909
+ }
3910
+ }
3911
+ return { breached: false };
3912
+ };
3667
3913
  let iterations = 0;
3668
3914
  const onStateChange = config?.onStateChange;
3669
3915
  const checkpointReason = config?.checkpointReason;
@@ -3712,6 +3958,13 @@ function createAgent(opts) {
3712
3958
  };
3713
3959
  while (iterations < iterationLimit) {
3714
3960
  iterations++;
3961
+ const budget = checkBudgetBreached();
3962
+ if (budget.breached) {
3963
+ emit?.({ type: "metadata", limitBreached: budget.reason });
3964
+ const ctx = { ...state.ctx || {}, __limitBreached: budget.reason };
3965
+ state = { ...state, ctx };
3966
+ break;
3967
+ }
3715
3968
  const traceSession = state.ctx?.__traceSession;
3716
3969
  if (traceSession) {
3717
3970
  const iterEvent = recordTraceEvent(traceSession, {
@@ -3767,22 +4020,11 @@ function createAgent(opts) {
3767
4020
  }
3768
4021
  }
3769
4022
  if (summarizationThreshold !== void 0) {
3770
- const tokCountMessages = (state.messages || []).filter((message) => {
3771
- if (isSyntheticSummaryMessage(message)) return false;
3772
- if (message.role === "system" && (message.name === "context_summary" || message.name === "memory_context")) return false;
3773
- return true;
3774
- });
3775
- const tokenCount = countMessagesTokens(tokCountMessages);
3776
- if (tokenCount > summarizationThreshold) {
3777
- const summarizationExhausted = !!state.ctx?.__summarizationExhausted;
3778
- const hasFreshSummary = (state.messages || []).some(
3779
- (m) => m.role === "tool" && typeof m.content === "string" && (m.content === "SUMMARIZED" || m.content.startsWith("SUMMARIZED_TOOL_RESPONSE"))
3780
- );
3781
- if (hasFreshSummary && tokenCount <= summarizationThreshold * 1.15) ; else if (summarizationExhausted) ; else {
3782
- const ctx = { ...state.ctx || {}, __needsSummarization: true };
3783
- state = { ...state, ctx };
3784
- break;
3785
- }
4023
+ const verdict = shouldSignalSummarization(state, summarizationThreshold);
4024
+ if (verdict === "trigger") {
4025
+ const ctx = { ...state.ctx || {}, __needsSummarization: true };
4026
+ state = { ...state, ctx };
4027
+ break;
3786
4028
  }
3787
4029
  }
3788
4030
  onProgress?.({ stage: "agent", message: "Invoking model" });
@@ -3888,7 +4130,13 @@ function createAgent(opts) {
3888
4130
  if (state.ctx?.__finalizedDueToStructuredOutput) break;
3889
4131
  if (reflectNode && resolvedReasoning?.reflection.enabled) {
3890
4132
  const cadence = resolvedReasoning.reflection.cadence;
3891
- if (shouldRunReflection(cadence, state, true)) {
4133
+ const refl = resolvedReasoning.reflection;
4134
+ const reflectionsSoFar = state.reflections?.length ?? 0;
4135
+ const lastTurn = state.reflections?.at(-1)?.turn ?? -Infinity;
4136
+ const currentTurn = state.toolCallCount || 0;
4137
+ const budgetExceeded = typeof refl.maxPerRun === "number" && reflectionsSoFar >= refl.maxPerRun;
4138
+ const cadenceGap = currentTurn - (Number.isFinite(lastTurn) ? lastTurn : -refl.everyNTurns) < refl.everyNTurns;
4139
+ if (!budgetExceeded && !cadenceGap && shouldRunReflection(cadence, state, true)) {
3892
4140
  try {
3893
4141
  const patch = await reflectNode(state, cadence);
3894
4142
  if (patch && Object.keys(patch).length > 0) {
@@ -3948,7 +4196,7 @@ function createAgent(opts) {
3948
4196
  }
3949
4197
  const lastAfterLoop = state.messages[state.messages.length - 1];
3950
4198
  if (lastAfterLoop?.role === "tool" && !pausedStage) {
3951
- const isExpectedExit = state.ctx?.__awaitingApproval || state.ctx?.__cancelled || state.ctx?.__finalizedDueToStructuredOutput || state.ctx?.__finalizedDueToToolLimit || state.ctx?.__needsSummarization;
4199
+ const isExpectedExit = state.ctx?.__awaitingApproval || state.ctx?.__cancelled || state.ctx?.__finalizedDueToStructuredOutput || state.ctx?.__finalizedDueToToolLimit || state.ctx?.__needsSummarization || state.ctx?.__limitBreached;
3952
4200
  if (!isExpectedExit) {
3953
4201
  throw new Error(
3954
4202
  "Agent loop terminated with a pending tool response but no subsequent model invocation. This usually indicates a model provider error or exhausted iteration budget."
@@ -3961,6 +4209,11 @@ function createAgent(opts) {
3961
4209
  return state;
3962
4210
  }
3963
4211
  const invokeAgent = async (input, config) => {
4212
+ const userTokenCounter = opts.tokenCounter;
4213
+ if (userTokenCounter) {
4214
+ const { setTokenCounter: setTokenCounter2 } = await Promise.resolve().then(() => (init_utilTokens(), utilTokens_exports));
4215
+ setTokenCounter2(userTokenCounter);
4216
+ }
3964
4217
  const onEvent = config?.onEvent;
3965
4218
  const onProgress = config?.onProgress;
3966
4219
  const onStream = config?.onStream;
@@ -4018,7 +4271,19 @@ function createAgent(opts) {
4018
4271
  status: "error",
4019
4272
  error: { message: err?.message, stack: err?.stack }
4020
4273
  });
4274
+ if (userTokenCounter) {
4275
+ const { setTokenCounter: setTokenCounter2 } = await Promise.resolve().then(() => (init_utilTokens(), utilTokens_exports));
4276
+ setTokenCounter2(void 0);
4277
+ }
4021
4278
  throw err;
4279
+ } finally {
4280
+ if (userTokenCounter) {
4281
+ try {
4282
+ const { setTokenCounter: setTokenCounter2 } = await Promise.resolve().then(() => (init_utilTokens(), utilTokens_exports));
4283
+ setTokenCounter2(void 0);
4284
+ } catch {
4285
+ }
4286
+ }
4022
4287
  }
4023
4288
  await finalizeTraceSession(traceSession, {
4024
4289
  agentRuntime: res.agent || runtime,
@@ -4076,12 +4341,58 @@ function createAgent(opts) {
4076
4341
  resolveToolApproval,
4077
4342
  asTool: ({ toolName, description, inputDescription }) => {
4078
4343
  const schema = zod.z.object({ input: zod.z.string().describe(inputDescription || "Input for delegated agent") });
4079
- return createTool({
4344
+ const delegatedTool = createTool({
4080
4345
  name: toolName,
4081
4346
  description: description || `Delegate task to agent ${opts.name || "Agent"}`,
4082
4347
  schema,
4083
4348
  func: async ({ input }) => {
4084
- const res = await instance.invoke({ messages: [{ role: "user", content: input }] });
4349
+ const ref = delegatedTool._stateRef;
4350
+ const parentCtx = ref.ctx || {};
4351
+ const parentMessages = Array.isArray(ref.messages) ? ref.messages : [];
4352
+ const parentRuntime = ref.parentRuntime;
4353
+ const resolvedSmart = parentRuntime?.smart || opts.__resolvedSmart;
4354
+ const delegationPolicy = resolvedSmart?.delegation || {
4355
+ mode: "role_based",
4356
+ maxDelegationDepth: 2,
4357
+ maxChildCalls: 4,
4358
+ childContextPolicy: "scoped"
4359
+ };
4360
+ const currentDepth = Number(parentCtx.__delegationDepth) || 0;
4361
+ if (delegationPolicy.mode === "off") {
4362
+ return { error: "Delegation disabled by runtime policy." };
4363
+ }
4364
+ if (currentDepth >= delegationPolicy.maxDelegationDepth) {
4365
+ return {
4366
+ error: `Delegation depth limit reached (${delegationPolicy.maxDelegationDepth}). Refusing nested call.`
4367
+ };
4368
+ }
4369
+ parentCtx.__delegatedCallCount = (parentCtx.__delegatedCallCount || 0) + 1;
4370
+ if (parentCtx.__delegatedCallCount > delegationPolicy.maxChildCalls) {
4371
+ return {
4372
+ error: `Child-call budget exhausted (${delegationPolicy.maxChildCalls}).`
4373
+ };
4374
+ }
4375
+ let childMessages;
4376
+ switch (delegationPolicy.childContextPolicy) {
4377
+ case "full":
4378
+ childMessages = [...parentMessages, { role: "user", content: input }];
4379
+ break;
4380
+ case "scoped": {
4381
+ const systemPrefix = parentMessages[0]?.role === "system" ? [parentMessages[0]] : [];
4382
+ const lastUser = [...parentMessages].reverse().find((m) => m?.role === "user");
4383
+ childMessages = [
4384
+ ...systemPrefix,
4385
+ ...lastUser ? [lastUser] : [],
4386
+ { role: "user", content: input }
4387
+ ];
4388
+ break;
4389
+ }
4390
+ case "minimal":
4391
+ default:
4392
+ childMessages = [{ role: "user", content: input }];
4393
+ }
4394
+ const childCtx = { __delegationDepth: currentDepth + 1 };
4395
+ const res = await instance.invoke({ messages: childMessages, ctx: childCtx });
4085
4396
  return {
4086
4397
  content: res.content,
4087
4398
  output: res.output,
@@ -4089,6 +4400,8 @@ function createAgent(opts) {
4089
4400
  };
4090
4401
  }
4091
4402
  });
4403
+ delegatedTool._stateRef = {};
4404
+ return delegatedTool;
4092
4405
  },
4093
4406
  asHandoff: ({ toolName, description, schema }) => {
4094
4407
  const finalName = toolName || `handoff_to_${runtime.name || "agent"}`;
@@ -4228,7 +4541,11 @@ function createAgentNode(opts) {
4228
4541
  };
4229
4542
  }
4230
4543
 
4544
+ // src/nodes/contextSummarize.ts
4545
+ init_utilTokens();
4546
+
4231
4547
  // src/smart/contextPolicy.ts
4548
+ init_utilTokens();
4232
4549
  function collectRecentTurns(messages, lastTurnsToKeep) {
4233
4550
  if (lastTurnsToKeep <= 0) return [];
4234
4551
  const systemPrefix = messages.filter((message, index) => index === 0 && message.role === "system");
@@ -4531,7 +4848,7 @@ function createContextSummarizeNode(opts) {
4531
4848
  const resolved = getResolvedSmartConfig(opts, state.agent);
4532
4849
  const config = resolved.summarization;
4533
4850
  if (!config.enable) return {};
4534
- const model = opts.model;
4851
+ const model = state.agent?.model || opts.model;
4535
4852
  const messages = state.messages || [];
4536
4853
  const onEvent = state.ctx?.__onEvent;
4537
4854
  const traceSession = state.ctx?.__traceSession;
@@ -4850,6 +5167,10 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
4850
5167
  };
4851
5168
  };
4852
5169
  }
5170
+
5171
+ // src/index.ts
5172
+ init_utilTokens();
5173
+ init_utilTokens();
4853
5174
  var TOOL_RESPONSE_RECOVERY_MARKERS = [
4854
5175
  /ARCHIVED_TOOL_RESPONSE\s*\[/,
4855
5176
  /STRUCTURED_TOOL_RESPONSE\s*\[/,
@@ -5168,6 +5489,7 @@ function createContextTools(stateRef, opts) {
5168
5489
  }
5169
5490
 
5170
5491
  // src/graph/decisions.ts
5492
+ init_utilTokens();
5171
5493
  function needsSummarization(state, opts, summarizationEnabled) {
5172
5494
  if (!summarizationEnabled) {
5173
5495
  return false;
@@ -5330,12 +5652,24 @@ function createSmartAgent(opts) {
5330
5652
  toolResponses: resolved.toolResponses,
5331
5653
  useTodoList: planningEnabled
5332
5654
  };
5333
- const stateRef = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
5334
- const contextTools = createContextTools(stateRef, { planningEnabled, includeGetToolResponse: false });
5335
- const getToolResponseTool = createGetToolResponseTool(stateRef);
5336
- const toolsWithoutRecovery = [...opts.tools ?? [], ...contextTools];
5337
- const toolsWithRecovery = [...toolsWithoutRecovery, getToolResponseTool];
5338
- const base = createAgent({ ...runtimeOpts, tools: toolsWithoutRecovery });
5655
+ const userTools = opts.tools ?? [];
5656
+ const factoryStateRef = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
5657
+ const factoryContextTools = createContextTools(factoryStateRef, { planningEnabled, includeGetToolResponse: false });
5658
+ const factoryToolsWithoutRecovery = [...userTools, ...factoryContextTools];
5659
+ const soManagerFactory = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
5660
+ const modelCapabilities = opts.outputSchema ? getModelCapabilities(opts.model) : void 0;
5661
+ const responseFinalizeTool = soManagerFactory && modelCapabilities?.structuredOutput !== "native" ? soManagerFactory.getResponseTool() : void 0;
5662
+ const buildInvokeToolSet = () => {
5663
+ const ref = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
5664
+ const ctxTools = createContextTools(ref, { planningEnabled, includeGetToolResponse: false });
5665
+ const recoveryTool = createGetToolResponseTool(ref);
5666
+ const base2 = [...userTools, ...ctxTools];
5667
+ if (responseFinalizeTool) base2.push(responseFinalizeTool);
5668
+ const without = base2;
5669
+ const withRec = [...without, recoveryTool];
5670
+ return { stateRef: ref, toolsWithoutRecovery: without, toolsWithRecovery: withRec };
5671
+ };
5672
+ const base = createAgent({ ...runtimeOpts, tools: factoryToolsWithoutRecovery });
5339
5673
  base.__runtime.runtimeProfile = resolved.runtimeProfile;
5340
5674
  base.__runtime.smart = resolved;
5341
5675
  const summarizationEnabled = resolved.summarization.enable !== false;
@@ -5371,17 +5705,17 @@ function createSmartAgent(opts) {
5371
5705
  const memoryFacts = await writeSummaryFactsToMemory(memoryStore, resolved, latestSummary, Math.max(1, (state.messages || []).filter((message) => message.role === "user").length));
5372
5706
  return { ...state, memoryFacts };
5373
5707
  }
5374
- function syncPlanState(state) {
5375
- if (!Array.isArray(stateRef.todoList)) return state;
5708
+ function syncPlanStateWith(ref, state) {
5709
+ if (!Array.isArray(ref.todoList)) return state;
5376
5710
  return {
5377
5711
  ...state,
5378
5712
  plan: {
5379
- version: stateRef.planVersion || state.planVersion || 1,
5380
- steps: stateRef.todoList,
5713
+ version: ref.planVersion || state.planVersion || 1,
5714
+ steps: ref.todoList,
5381
5715
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
5382
- adherenceScore: stateRef.adherenceScore || 0
5716
+ adherenceScore: ref.adherenceScore || 0
5383
5717
  },
5384
- planVersion: stateRef.planVersion || state.planVersion || 1
5718
+ planVersion: ref.planVersion || state.planVersion || 1
5385
5719
  };
5386
5720
  }
5387
5721
  function systemMessage2() {
@@ -5393,9 +5727,9 @@ function createSmartAgent(opts) {
5393
5727
  );
5394
5728
  return { role: "system", content: sys };
5395
5729
  }
5396
- function syncRuntimeTools(currentState, currentMessages) {
5730
+ function syncRuntimeTools(currentState, currentMessages, toolSet) {
5397
5731
  const needsRecoveryTool = hasToolResponseRecoveryReference(currentMessages);
5398
- const nextTools = needsRecoveryTool ? toolsWithRecovery : toolsWithoutRecovery;
5732
+ const nextTools = needsRecoveryTool ? toolSet.toolsWithRecovery : toolSet.toolsWithoutRecovery;
5399
5733
  const currentRuntime = currentState.agent || base.__runtime;
5400
5734
  if (currentRuntime.tools === nextTools) {
5401
5735
  return currentState;
@@ -5414,7 +5748,7 @@ function createSmartAgent(opts) {
5414
5748
  const toolCalls = Array.isArray(lastAssistant.tool_calls) ? lastAssistant.tool_calls : [];
5415
5749
  return toolCalls.length === 0;
5416
5750
  }
5417
- async function trySummarize(currentState, currentRawMessages) {
5751
+ async function trySummarize(currentState, currentRawMessages, ref) {
5418
5752
  if (!summarizer) return { state: currentState, rawMessages: currentRawMessages, compressed: false };
5419
5753
  const delta = await summarizer(currentState);
5420
5754
  if (!delta || Object.keys(delta).length === 0) {
@@ -5423,28 +5757,31 @@ function createSmartAgent(opts) {
5423
5757
  }
5424
5758
  const ctx = { ...currentState.ctx || {} };
5425
5759
  delete ctx.__summarizationExhausted;
5426
- const updated = await persistLatestSummary(syncPlanState({ ...currentState, ...delta, ctx }));
5760
+ const updated = await persistLatestSummary(syncPlanStateWith(ref, { ...currentState, ...delta, ctx }));
5427
5761
  return { state: updated, rawMessages: [...updated.messages || currentRawMessages], compressed: true };
5428
5762
  }
5429
5763
  const instance = {
5430
5764
  invoke: async (input, config) => {
5765
+ const toolSet = buildInvokeToolSet();
5766
+ const stateRef = toolSet.stateRef;
5431
5767
  stateRef.toolHistory = input.toolHistory;
5432
5768
  stateRef.toolHistoryArchived = input.toolHistoryArchived;
5433
5769
  stateRef.todoList = input.plan?.steps;
5434
5770
  stateRef.planVersion = input.planVersion || input.plan?.version || 0;
5435
5771
  stateRef.adherenceScore = input.plan?.adherenceScore || 0;
5772
+ const syncPlanFromRef = (state2) => syncPlanStateWith(stateRef, state2);
5436
5773
  const alreadyHasSystem = Array.isArray(input.messages) && input.messages[0]?.role === "system";
5437
5774
  const seedMessages = alreadyHasSystem ? [...input.messages || []] : [systemMessage2(), ...input.messages || []];
5438
- let state = syncRuntimeTools(await syncMemory({ ...input, messages: seedMessages }), seedMessages);
5775
+ let state = syncRuntimeTools(await syncMemory({ ...input, messages: seedMessages }), seedMessages, toolSet);
5439
5776
  let lastResult = null;
5440
5777
  let rawMessages = [...seedMessages];
5441
5778
  const effectiveMaxToolCalls = config?.limits?.maxToolCalls ?? resolved.limits.maxToolCalls ?? 10;
5442
5779
  const iterationLimit = Math.max(effectiveMaxToolCalls * 3 + 5, 30);
5443
5780
  for (let i = 0; i < iterationLimit; i++) {
5444
- state = syncRuntimeTools({ ...state, messages: rawMessages }, rawMessages);
5781
+ state = syncRuntimeTools({ ...state, messages: rawMessages }, rawMessages, toolSet);
5445
5782
  const next = summarizationEnabled ? decideBefore(state) : "agent";
5446
5783
  if (next === "contextSummarize" && summarizer) {
5447
- const result = await trySummarize(state, rawMessages);
5784
+ const result = await trySummarize(state, rawMessages, stateRef);
5448
5785
  state = result.state;
5449
5786
  rawMessages = result.rawMessages;
5450
5787
  if (result.compressed) continue;
@@ -5468,14 +5805,14 @@ function createSmartAgent(opts) {
5468
5805
  if (currentPlan && !state.plan) {
5469
5806
  state = { ...state, plan: currentPlan };
5470
5807
  }
5471
- state = syncRuntimeTools(syncPlanState(await syncMemory(state)), rawMessages);
5808
+ state = syncRuntimeTools(syncPlanFromRef(await syncMemory(state)), rawMessages, toolSet);
5472
5809
  stateRef.toolHistory = state.toolHistory;
5473
5810
  stateRef.toolHistoryArchived = state.toolHistoryArchived;
5474
5811
  if (state.ctx?.__needsSummarization && summarizer) {
5475
5812
  const ctx = { ...state.ctx || {} };
5476
5813
  delete ctx.__needsSummarization;
5477
5814
  state = { ...state, ctx };
5478
- const result = await trySummarize(state, rawMessages);
5815
+ const result = await trySummarize(state, rawMessages, stateRef);
5479
5816
  state = result.state;
5480
5817
  rawMessages = result.rawMessages;
5481
5818
  continue;
@@ -5486,7 +5823,7 @@ function createSmartAgent(opts) {
5486
5823
  if (summarizationEnabled) {
5487
5824
  const after = decideAfter(state);
5488
5825
  if (after === "contextSummarize" && summarizer) {
5489
- const result = await trySummarize(state, rawMessages);
5826
+ const result = await trySummarize(state, rawMessages, stateRef);
5490
5827
  state = result.state;
5491
5828
  rawMessages = result.rawMessages;
5492
5829
  if (result.compressed) continue;
@@ -5925,7 +6262,50 @@ var ProviderError = class extends Error {
5925
6262
  };
5926
6263
 
5927
6264
  // src/providers/base.ts
6265
+ var DEFAULT_RETRY = {
6266
+ maxRetries: 3,
6267
+ baseDelayMs: 500,
6268
+ maxDelayMs: 8e3
6269
+ };
5928
6270
  var BaseProvider = class {
6271
+ constructor() {
6272
+ this.retryPolicy = DEFAULT_RETRY;
6273
+ }
6274
+ /**
6275
+ * Helper concrete providers can wrap their fetch with. Retries 429/5xx
6276
+ * with exponential backoff and respects Retry-After if the response
6277
+ * carries one.
6278
+ */
6279
+ async doFetchWithRetry(execute) {
6280
+ const policy = this.retryPolicy;
6281
+ const maxRetries = Math.max(0, policy.maxRetries ?? DEFAULT_RETRY.maxRetries);
6282
+ const baseDelay = Math.max(0, policy.baseDelayMs ?? DEFAULT_RETRY.baseDelayMs);
6283
+ const maxDelay = Math.max(baseDelay, policy.maxDelayMs ?? DEFAULT_RETRY.maxDelayMs);
6284
+ let lastResp;
6285
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
6286
+ const res = await execute();
6287
+ const isRetryable = res.status === 429 || res.status >= 500 && res.status < 600;
6288
+ if (!isRetryable) return res;
6289
+ const predicate = policy.shouldRetry;
6290
+ if (predicate && !predicate({ status: res.status }, attempt)) return res;
6291
+ if (attempt === maxRetries) {
6292
+ lastResp = res;
6293
+ break;
6294
+ }
6295
+ const retryAfter = res.headers.get("retry-after");
6296
+ let delay = Math.min(maxDelay, baseDelay * Math.pow(2, attempt));
6297
+ if (retryAfter) {
6298
+ const asNumber = Number(retryAfter);
6299
+ if (!Number.isNaN(asNumber)) delay = Math.min(maxDelay, Math.max(delay, asNumber * 1e3));
6300
+ }
6301
+ try {
6302
+ await res.text();
6303
+ } catch {
6304
+ }
6305
+ await new Promise((r) => setTimeout(r, delay));
6306
+ }
6307
+ return lastResp;
6308
+ }
5929
6309
  };
5930
6310
 
5931
6311
  // src/providers/utils/sse.ts
@@ -6052,6 +6432,7 @@ var OpenAIProvider = class extends BaseProvider {
6052
6432
  ...config.defaultHeaders,
6053
6433
  ...config.organization ? { "OpenAI-Organization": config.organization } : {}
6054
6434
  };
6435
+ if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
6055
6436
  }
6056
6437
  async complete(request) {
6057
6438
  const body = this.buildRequestBody(request, false);
@@ -6199,15 +6580,13 @@ var OpenAIProvider = class extends BaseProvider {
6199
6580
  }
6200
6581
  async doFetch(body) {
6201
6582
  const url = `${this.baseURL}/chat/completions`;
6202
- const res = await fetch(url, {
6203
- method: "POST",
6204
- headers: {
6205
- "Content-Type": "application/json",
6206
- Authorization: `Bearer ${this.apiKey}`,
6207
- ...this.defaultHeaders
6208
- },
6209
- body: JSON.stringify(body)
6210
- });
6583
+ const headers = {
6584
+ "Content-Type": "application/json",
6585
+ Authorization: `Bearer ${this.apiKey}`,
6586
+ ...this.defaultHeaders
6587
+ };
6588
+ const payload = JSON.stringify(body);
6589
+ const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload }));
6211
6590
  if (!res.ok) {
6212
6591
  const text = await res.text().catch(() => "");
6213
6592
  throw new ProviderError(
@@ -6277,6 +6656,8 @@ var AnthropicProvider = class extends BaseProvider {
6277
6656
  this.defaultModel = config.defaultModel ?? "claude-sonnet-4-20250514";
6278
6657
  this.defaultHeaders = config.defaultHeaders ?? {};
6279
6658
  this.anthropicVersion = config.anthropicVersion ?? "2023-06-01";
6659
+ this.promptCachingEnabled = config.prompt_caching?.enabled === true;
6660
+ if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
6280
6661
  }
6281
6662
  async complete(request) {
6282
6663
  const body = this.buildRequestBody(request, false);
@@ -6395,12 +6776,22 @@ var AnthropicProvider = class extends BaseProvider {
6395
6776
  max_tokens: request.maxTokens ?? 4096,
6396
6777
  stream
6397
6778
  };
6398
- if (system) body.system = system;
6779
+ if (system) {
6780
+ if (this.promptCachingEnabled) {
6781
+ body.system = [{ type: "text", text: system, cache_control: { type: "ephemeral" } }];
6782
+ } else {
6783
+ body.system = system;
6784
+ }
6785
+ }
6399
6786
  if (request.temperature != null) body.temperature = request.temperature;
6400
6787
  if (request.topP != null) body.top_p = request.topP;
6401
6788
  if (request.stop) body.stop_sequences = request.stop;
6402
6789
  if (request.tools?.length) {
6403
- body.tools = request.tools.map((t) => this.toAnthropicTool(t));
6790
+ const tools = request.tools.map((t) => this.toAnthropicTool(t));
6791
+ if (this.promptCachingEnabled && tools.length > 0) {
6792
+ tools[tools.length - 1] = { ...tools[tools.length - 1], cache_control: { type: "ephemeral" } };
6793
+ }
6794
+ body.tools = tools;
6404
6795
  }
6405
6796
  if (request.toolChoice != null) {
6406
6797
  if (request.toolChoice === "auto") {
@@ -6499,16 +6890,14 @@ var AnthropicProvider = class extends BaseProvider {
6499
6890
  }
6500
6891
  async doFetch(body) {
6501
6892
  const url = `${this.baseURL}/v1/messages`;
6502
- const res = await fetch(url, {
6503
- method: "POST",
6504
- headers: {
6505
- "Content-Type": "application/json",
6506
- "x-api-key": this.apiKey,
6507
- "anthropic-version": this.anthropicVersion,
6508
- ...this.defaultHeaders
6509
- },
6510
- body: JSON.stringify(body)
6511
- });
6893
+ const headers = {
6894
+ "Content-Type": "application/json",
6895
+ "x-api-key": this.apiKey,
6896
+ "anthropic-version": this.anthropicVersion,
6897
+ ...this.defaultHeaders
6898
+ };
6899
+ const payload = JSON.stringify(body);
6900
+ const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload }));
6512
6901
  if (!res.ok) {
6513
6902
  const text = await res.text().catch(() => "");
6514
6903
  throw new ProviderError(
@@ -6720,6 +7109,8 @@ var BedrockProvider = class extends BaseProvider {
6720
7109
  this.region = config.region;
6721
7110
  this.defaultModel = config.defaultModel ?? "anthropic.claude-sonnet-4-20250514-v1:0";
6722
7111
  this.defaultHeaders = config.defaultHeaders ?? {};
7112
+ this.promptCachingEnabled = config.prompt_caching?.enabled === true;
7113
+ if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
6723
7114
  this.credentials = {
6724
7115
  accessKeyId: config.accessKeyId ?? process.env.AWS_ACCESS_KEY_ID ?? "",
6725
7116
  secretAccessKey: config.secretAccessKey ?? process.env.AWS_SECRET_ACCESS_KEY ?? "",
@@ -6758,7 +7149,9 @@ var BedrockProvider = class extends BaseProvider {
6758
7149
  messages: messages.map((m) => this.toBedrockMessage(m))
6759
7150
  };
6760
7151
  if (system.length > 0) {
6761
- body.system = system.map((s) => ({ text: s }));
7152
+ const systemBlocks = system.map((s) => ({ text: s }));
7153
+ if (this.promptCachingEnabled) systemBlocks.push({ cachePoint: { type: "default" } });
7154
+ body.system = systemBlocks;
6762
7155
  }
6763
7156
  const inferenceConfig = {};
6764
7157
  if (request.maxTokens != null) inferenceConfig.maxTokens = request.maxTokens;
@@ -6767,8 +7160,12 @@ var BedrockProvider = class extends BaseProvider {
6767
7160
  if (request.stop) inferenceConfig.stopSequences = request.stop;
6768
7161
  if (Object.keys(inferenceConfig).length > 0) body.inferenceConfig = inferenceConfig;
6769
7162
  if (request.tools?.length) {
7163
+ const tools = request.tools.map((t) => this.toBedrockTool(t));
7164
+ if (this.promptCachingEnabled && tools.length > 0) {
7165
+ tools.push({ cachePoint: { type: "default" } });
7166
+ }
6770
7167
  body.toolConfig = {
6771
- tools: request.tools.map((t) => this.toBedrockTool(t))
7168
+ tools
6772
7169
  };
6773
7170
  if (request.toolChoice != null) {
6774
7171
  if (request.toolChoice === "auto") {
@@ -6865,23 +7262,21 @@ var BedrockProvider = class extends BaseProvider {
6865
7262
  const host = `bedrock-runtime.${this.region}.amazonaws.com`;
6866
7263
  const url = `https://${host}/model/${encodeURIComponent(modelId)}/converse`;
6867
7264
  const bodyStr = JSON.stringify(body);
6868
- const headers = signRequest({
6869
- method: "POST",
6870
- url,
6871
- headers: {
6872
- "Content-Type": "application/json",
6873
- Accept: "application/json",
6874
- ...this.defaultHeaders
6875
- },
6876
- body: bodyStr,
6877
- region: this.region,
6878
- service: "bedrock",
6879
- credentials: this.credentials
6880
- });
6881
- const res = await fetch(url, {
6882
- method: "POST",
6883
- headers,
6884
- body: bodyStr
7265
+ const res = await this.doFetchWithRetry(() => {
7266
+ const headers = signRequest({
7267
+ method: "POST",
7268
+ url,
7269
+ headers: {
7270
+ "Content-Type": "application/json",
7271
+ Accept: "application/json",
7272
+ ...this.defaultHeaders
7273
+ },
7274
+ body: bodyStr,
7275
+ region: this.region,
7276
+ service: "bedrock",
7277
+ credentials: this.credentials
7278
+ });
7279
+ return fetch(url, { method: "POST", headers, body: bodyStr });
6885
7280
  });
6886
7281
  if (!res.ok) {
6887
7282
  const text = await res.text().catch(() => "");
@@ -7795,6 +8190,7 @@ exports.createToolLimitFinalizeNode = createToolLimitFinalizeNode;
7795
8190
  exports.createToolsNode = createToolsNode;
7796
8191
  exports.customCallbackRule = customCallbackRule;
7797
8192
  exports.customSink = customSink;
8193
+ exports.defaultTokenCounter = defaultTokenCounter;
7798
8194
  exports.emptyUsage = emptyUsage;
7799
8195
  exports.evaluateGuardrails = evaluateGuardrails;
7800
8196
  exports.fileSink = fileSink;
@@ -7807,6 +8203,7 @@ exports.generateTraceId = generateTraceId;
7807
8203
  exports.getModelCapabilities = getModelCapabilities;
7808
8204
  exports.getNestedValue = getNestedValue;
7809
8205
  exports.getResolvedSmartConfig = getResolvedSmartConfig;
8206
+ exports.getTokenCounter = getTokenCounter;
7810
8207
  exports.hasToolResponseRecoveryReference = hasToolResponseRecoveryReference;
7811
8208
  exports.httpSink = httpSink;
7812
8209
  exports.isSmartModel = isSmartModel;
@@ -7820,6 +8217,7 @@ exports.resolveStrategy = resolveStrategy;
7820
8217
  exports.resolveToolApprovalState = resolveToolApprovalState;
7821
8218
  exports.restoreSnapshot = restoreSnapshot;
7822
8219
  exports.runSmartAgentEvalHarness = runSmartAgentEvalHarness;
8220
+ exports.setTokenCounter = setTokenCounter;
7823
8221
  exports.startStreamingSession = startStreamingSession;
7824
8222
  exports.traceSessionToOtlp = traceSessionToOtlp;
7825
8223
  exports.withTools = withTools;