@cognipeer/agent-sdk 0.6.3 → 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) {
@@ -2239,6 +2343,7 @@ function normalizeSmartAgentOptions(opts) {
2239
2343
  const summarizationOverride = typeof opts.summarization === "object" ? opts.summarization : {};
2240
2344
  const planningMode = opts.planning?.mode ?? customProfile.planning?.mode ?? (useTodoList ? "todo" : preset.planning.mode);
2241
2345
  const memoryStore = opts.memory?.store ?? customProfile.memory?.store;
2346
+ const contextToolResponsePolicy = opts.context?.toolResponsePolicy ?? customProfile.context?.toolResponsePolicy ?? preset.context.toolResponsePolicy;
2242
2347
  return {
2243
2348
  runtimeProfile,
2244
2349
  baseProfile,
@@ -2261,7 +2366,7 @@ function normalizeSmartAgentOptions(opts) {
2261
2366
  ...customProfile.context?.budget || {},
2262
2367
  ...opts.context?.budget || {}
2263
2368
  },
2264
- toolResponsePolicy: opts.context?.toolResponsePolicy ?? customProfile.context?.toolResponsePolicy ?? preset.context.toolResponsePolicy
2369
+ toolResponsePolicy: contextToolResponsePolicy
2265
2370
  },
2266
2371
  planning: {
2267
2372
  ...preset.planning,
@@ -2284,6 +2389,7 @@ function normalizeSmartAgentOptions(opts) {
2284
2389
  ...preset.toolResponses,
2285
2390
  ...customProfile.toolResponses || {},
2286
2391
  ...opts.toolResponses || {},
2392
+ defaultPolicy: opts.toolResponses?.defaultPolicy ?? customProfile.toolResponses?.defaultPolicy ?? contextToolResponsePolicy,
2287
2393
  toolResponseRetentionByTool: {
2288
2394
  ...preset.toolResponses.toolResponseRetentionByTool,
2289
2395
  ...customProfile.toolResponses?.toolResponseRetentionByTool || {},
@@ -2297,66 +2403,8 @@ function getResolvedSmartConfig(opts, runtime) {
2297
2403
  return runtime?.smart || normalizeSmartAgentOptions(opts);
2298
2404
  }
2299
2405
 
2300
- // src/utils/utilTokens.ts
2301
- function countApproxTokens(text) {
2302
- if (!text) return 0;
2303
- const nonAsciiCount = (text.match(/[^\x00-\x7F]/g) || []).length;
2304
- const asciiCount = text.length - nonAsciiCount;
2305
- return Math.ceil(asciiCount / 4 + nonAsciiCount / 1.5);
2306
- }
2307
- function extractMessageText(m) {
2308
- if (!m) return "";
2309
- const parts = [];
2310
- parts.push(m.role || "");
2311
- if (m.name) parts.push(m.name);
2312
- if (typeof m.content === "string") {
2313
- parts.push(m.content);
2314
- } else if (Array.isArray(m.content)) {
2315
- for (const c of m.content) {
2316
- if (typeof c === "string") parts.push(c);
2317
- else if (c?.text) parts.push(c.text);
2318
- else if (c?.content) parts.push(c.content);
2319
- else if (c?.type === "tool_use" && c?.input) {
2320
- parts.push(c.name || "");
2321
- parts.push(typeof c.input === "string" ? c.input : JSON.stringify(c.input));
2322
- } else if (c != null && typeof c === "object") {
2323
- parts.push(JSON.stringify(c));
2324
- }
2325
- }
2326
- } else if (m.content != null && typeof m.content === "object") {
2327
- parts.push(JSON.stringify(m.content));
2328
- }
2329
- if (Array.isArray(m.tool_calls)) {
2330
- for (const tc of m.tool_calls) {
2331
- if (tc?.function?.name) parts.push(tc.function.name);
2332
- if (tc?.function?.arguments) {
2333
- parts.push(
2334
- typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
2335
- );
2336
- }
2337
- if (tc?.name && !tc?.function?.name) parts.push(tc.name);
2338
- if (tc?.args && !tc?.function?.arguments) {
2339
- parts.push(typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args));
2340
- }
2341
- }
2342
- }
2343
- if (Array.isArray(m.additional_kwargs?.tool_calls)) {
2344
- for (const tc of m.additional_kwargs.tool_calls) {
2345
- if (tc?.function?.name) parts.push(tc.function.name);
2346
- if (tc?.function?.arguments) {
2347
- parts.push(
2348
- typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
2349
- );
2350
- }
2351
- }
2352
- }
2353
- return parts.join("\n");
2354
- }
2355
- function countMessagesTokens(messages) {
2356
- if (!Array.isArray(messages) || messages.length === 0) return 0;
2357
- const allText = messages.map(extractMessageText).join("\n");
2358
- return countApproxTokens(allText) + messages.length * 4;
2359
- }
2406
+ // src/smart/toolResponses.ts
2407
+ init_utilTokens();
2360
2408
 
2361
2409
  // src/utils/content.ts
2362
2410
  function contentToString(content) {
@@ -2556,6 +2604,7 @@ function createToolsNode(initialTools, opts) {
2556
2604
  anyTool._stateRef.messages = state.messages;
2557
2605
  anyTool._stateRef.ctx = state.ctx || (state.ctx = {});
2558
2606
  anyTool._stateRef.__onEvent = onEvent;
2607
+ anyTool._stateRef.parentRuntime = runtime;
2559
2608
  }
2560
2609
  }
2561
2610
  const last = state.messages[state.messages.length - 1];
@@ -2566,6 +2615,7 @@ function createToolsNode(initialTools, opts) {
2566
2615
  const remaining = Math.max(0, limits.maxToolCalls - toolCount);
2567
2616
  const planned = toolCalls.slice(0, remaining);
2568
2617
  const skipped = toolCalls.slice(remaining);
2618
+ const orderedSlots = planned.map(() => []);
2569
2619
  const isCancelled = () => {
2570
2620
  if (abortSignal?.aborted) return { cancelled: true, reason: "aborted" };
2571
2621
  if (cancellationToken && cancellationToken.isCancellationRequested) return { cancelled: true, reason: "cancelled" };
@@ -2582,7 +2632,9 @@ function createToolsNode(initialTools, opts) {
2582
2632
  }
2583
2633
  state.ctx = ctx;
2584
2634
  };
2585
- const runOne = async (tc) => {
2635
+ const runOne = async (tc, slot = appended) => {
2636
+ var _a;
2637
+ const push = (msg) => slot.push(msg);
2586
2638
  const cancelState = isCancelled();
2587
2639
  if (cancelState.cancelled) {
2588
2640
  const ctx = state.ctx = state.ctx || {};
@@ -2593,7 +2645,7 @@ function createToolsNode(initialTools, opts) {
2593
2645
  }
2594
2646
  const tool = toolByName.get(tc.name);
2595
2647
  if (!tool) {
2596
- 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 });
2597
2649
  onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args: tc.args, error: { message: "Tool not found" } });
2598
2650
  toolCount += 1;
2599
2651
  return { status: "error" };
@@ -2609,7 +2661,7 @@ function createToolsNode(initialTools, opts) {
2609
2661
  const validatedArgs = validateToolArgs(tool, args);
2610
2662
  if (!validatedArgs.ok && resolved.toolResponses.schemaValidation === "strict") {
2611
2663
  const errorMessage = `Tool argument validation failed for ${tc.name}: ${validatedArgs.message}`;
2612
- 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 });
2613
2665
  markToolFailure(errorMessage, tc.name, toolCallId);
2614
2666
  onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args, error: { message: errorMessage } });
2615
2667
  toolCount += 1;
@@ -2631,7 +2683,7 @@ function createToolsNode(initialTools, opts) {
2631
2683
  const currentExecutions = countSuccessfulToolExecutions(toolHistory, toolName);
2632
2684
  if (currentExecutions >= maxExecutionsPerRun) {
2633
2685
  const limitMessage = `Skipped tool due to per-tool execution limit: ${toolName} (${maxExecutionsPerRun}/run)`;
2634
- 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 });
2635
2687
  onEvent?.({ type: "tool_call", phase: "skipped", name: toolName, id: tc.id, args });
2636
2688
  recordTraceEvent(traceSession, {
2637
2689
  type: "tool_call",
@@ -2691,7 +2743,7 @@ function createToolsNode(initialTools, opts) {
2691
2743
  }
2692
2744
  if (approvalEntry.status === "rejected") {
2693
2745
  const rejectionMessage = approvalEntry.comment || "Tool call rejected by reviewer.";
2694
- 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 });
2695
2747
  approvalEntry.metadata = { ...approvalEntry.metadata || {}, resolution: "rejected" };
2696
2748
  approvalEntry.status = "executed";
2697
2749
  approvalEntry.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -2709,23 +2761,102 @@ function createToolsNode(initialTools, opts) {
2709
2761
  const start = Date.now();
2710
2762
  const sanitizedArgs = sanitizeTracePayload(args);
2711
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
+ }
2712
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
+ }
2713
2821
  onEvent?.({ type: "tool_call", phase: "start", name: toolName, id: tc.id, args });
2714
2822
  onProgress?.({ stage: "tools", message: `Running tool ${tc.name}` });
2715
2823
  const anyTool = tool;
2716
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
+ };
2717
2833
  let output;
2718
- if (typeof anyTool.func === "function") output = await anyTool.func(args, callOptions);
2719
- else if (typeof anyTool.invoke === "function") output = await anyTool.invoke(args, callOptions);
2720
- else if (typeof anyTool.call === "function") output = await anyTool.call(args, callOptions);
2721
- else if (typeof anyTool._call === "function") output = await anyTool._call(args, callOptions);
2722
- else if (typeof anyTool.run === "function") output = await anyTool.run(args, callOptions);
2723
- 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
+ }
2724
2855
  const durationMs = Date.now() - start;
2725
2856
  const executionId = nanoid.nanoid();
2726
2857
  if (output && typeof output === "object" && output.__handoff && output.__handoff.runtime) {
2727
2858
  toolHistory.push({ executionId, toolName, args, output: "handoff:ok", rawOutput: output, timestamp: (/* @__PURE__ */ new Date()).toISOString(), tool_call_id: tc.id, status: "handoff" });
2728
- 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 });
2729
2860
  state.agent = output.__handoff.runtime;
2730
2861
  onEvent?.({ type: "handoff", from: runtime.name, to: state.agent?.name, toolName });
2731
2862
  onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: "handoff", durationMs });
@@ -2777,7 +2908,7 @@ function createToolsNode(initialTools, opts) {
2777
2908
  archiveId: void 0,
2778
2909
  status: "success"
2779
2910
  });
2780
- 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 });
2781
2912
  onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: output, durationMs });
2782
2913
  onProgress?.({ stage: "tools", message: `Tool ${tc.name} completed`, detail: { durationMs } });
2783
2914
  if (needsApproval && approvalEntry) {
@@ -2833,7 +2964,7 @@ function createToolsNode(initialTools, opts) {
2833
2964
  const executionId = nanoid.nanoid();
2834
2965
  const message = error?.message || String(error);
2835
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" });
2836
- 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 });
2837
2968
  markToolFailure(message, toolName, toolCallId);
2838
2969
  onEvent?.({ type: "tool_call", phase: "error", name: toolName, id: tc.id, args, error: { message } });
2839
2970
  onProgress?.({ stage: "tools", message: `Tool ${tc.name} failed`, detail: { error: message } });
@@ -2870,22 +3001,67 @@ function createToolsNode(initialTools, opts) {
2870
3001
  return { status: "error", approval: approvalEntry };
2871
3002
  }
2872
3003
  };
2873
- for (const tc of planned) {
2874
- 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 = () => {
2875
3013
  const cancelState = isCancelled();
2876
3014
  if (cancelState.cancelled) {
2877
3015
  const ctx = state.ctx = state.ctx || {};
2878
3016
  ctx.__cancelled = { stage: "tools", reason: cancelState.reason, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
2879
3017
  onEvent?.({ type: "cancelled", stage: "tools", reason: cancelState.reason });
2880
3018
  onProgress?.({ stage: "tools", message: "Cancelled", detail: { reason: cancelState.reason } });
2881
- break;
3019
+ shouldStop = true;
3020
+ return true;
2882
3021
  }
2883
- 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]);
2884
3028
  if (result.status === "awaiting_approval") {
2885
3029
  awaitingApproval = true;
2886
3030
  break;
2887
3031
  }
2888
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
+ }
2889
3065
  for (const tc of skipped) {
2890
3066
  onEvent?.({ type: "tool_call", phase: "skipped", name: tc.name, id: tc.id, args: tc.args });
2891
3067
  const sanitizedArgs = sanitizeTracePayload(tc.args);
@@ -2921,11 +3097,23 @@ function createToolsNode(initialTools, opts) {
2921
3097
  }
2922
3098
  state.ctx = Object.keys(ctx).length > 0 ? ctx : void 0;
2923
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
+ }
2924
3106
  return {
2925
3107
  messages: [...state.messages, ...appended],
2926
3108
  toolCallCount: toolCount,
2927
3109
  toolHistory,
2928
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,
2929
3117
  agent: state.agent,
2930
3118
  pendingApprovals
2931
3119
  };
@@ -3162,7 +3350,8 @@ function resolveReflection(input, preset) {
3162
3350
  maxChars: preset.maxChars,
3163
3351
  keepLast: 3,
3164
3352
  summarize: false,
3165
- emitEvents: true
3353
+ emitEvents: true,
3354
+ everyNTurns: 1
3166
3355
  };
3167
3356
  }
3168
3357
  const cfg = input ?? {};
@@ -3177,7 +3366,9 @@ function resolveReflection(input, preset) {
3177
3366
  keepLast: cfg.keepLast ?? 3,
3178
3367
  summarize: cfg.summarize ?? false,
3179
3368
  promptTemplate: cfg.promptTemplate,
3180
- 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))
3181
3372
  };
3182
3373
  }
3183
3374
  function ensureArray(value) {
@@ -3390,6 +3581,9 @@ function resolveToolApprovalState(state, resolution) {
3390
3581
  };
3391
3582
  }
3392
3583
 
3584
+ // src/agent.ts
3585
+ init_utilTokens();
3586
+
3393
3587
  // src/utils/syntheticMessages.ts
3394
3588
  function isSyntheticSummaryMessage(message) {
3395
3589
  if (!message) return false;
@@ -3568,6 +3762,21 @@ function getActiveSummarizationThreshold(opts) {
3568
3762
  }
3569
3763
  return summarization.summaryTriggerTokens || summarization.maxTokens || opts.limits?.maxContextTokens || 5e4;
3570
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
+ }
3571
3780
  function clearNeedsSummarization(state) {
3572
3781
  if (!state.ctx?.__needsSummarization) {
3573
3782
  return state;
@@ -3662,6 +3871,45 @@ function createAgent(opts) {
3662
3871
  };
3663
3872
  const maxToolCalls = mergedLimits?.maxToolCalls === void 0 ? 50 : mergedLimits?.maxToolCalls;
3664
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
+ };
3665
3913
  let iterations = 0;
3666
3914
  const onStateChange = config?.onStateChange;
3667
3915
  const checkpointReason = config?.checkpointReason;
@@ -3710,6 +3958,13 @@ function createAgent(opts) {
3710
3958
  };
3711
3959
  while (iterations < iterationLimit) {
3712
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
+ }
3713
3968
  const traceSession = state.ctx?.__traceSession;
3714
3969
  if (traceSession) {
3715
3970
  const iterEvent = recordTraceEvent(traceSession, {
@@ -3765,22 +4020,11 @@ function createAgent(opts) {
3765
4020
  }
3766
4021
  }
3767
4022
  if (summarizationThreshold !== void 0) {
3768
- const tokCountMessages = (state.messages || []).filter((message) => {
3769
- if (isSyntheticSummaryMessage(message)) return false;
3770
- if (message.role === "system" && (message.name === "context_summary" || message.name === "memory_context")) return false;
3771
- return true;
3772
- });
3773
- const tokenCount = countMessagesTokens(tokCountMessages);
3774
- if (tokenCount > summarizationThreshold) {
3775
- const summarizationExhausted = !!state.ctx?.__summarizationExhausted;
3776
- const hasFreshSummary = (state.messages || []).some(
3777
- (m) => m.role === "tool" && typeof m.content === "string" && (m.content === "SUMMARIZED" || m.content.startsWith("SUMMARIZED_TOOL_RESPONSE"))
3778
- );
3779
- if (hasFreshSummary && tokenCount <= summarizationThreshold * 1.15) ; else if (summarizationExhausted) ; else {
3780
- const ctx = { ...state.ctx || {}, __needsSummarization: true };
3781
- state = { ...state, ctx };
3782
- break;
3783
- }
4023
+ const verdict = shouldSignalSummarization(state, summarizationThreshold);
4024
+ if (verdict === "trigger") {
4025
+ const ctx = { ...state.ctx || {}, __needsSummarization: true };
4026
+ state = { ...state, ctx };
4027
+ break;
3784
4028
  }
3785
4029
  }
3786
4030
  onProgress?.({ stage: "agent", message: "Invoking model" });
@@ -3886,7 +4130,13 @@ function createAgent(opts) {
3886
4130
  if (state.ctx?.__finalizedDueToStructuredOutput) break;
3887
4131
  if (reflectNode && resolvedReasoning?.reflection.enabled) {
3888
4132
  const cadence = resolvedReasoning.reflection.cadence;
3889
- 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)) {
3890
4140
  try {
3891
4141
  const patch = await reflectNode(state, cadence);
3892
4142
  if (patch && Object.keys(patch).length > 0) {
@@ -3946,7 +4196,7 @@ function createAgent(opts) {
3946
4196
  }
3947
4197
  const lastAfterLoop = state.messages[state.messages.length - 1];
3948
4198
  if (lastAfterLoop?.role === "tool" && !pausedStage) {
3949
- 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;
3950
4200
  if (!isExpectedExit) {
3951
4201
  throw new Error(
3952
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."
@@ -3959,6 +4209,11 @@ function createAgent(opts) {
3959
4209
  return state;
3960
4210
  }
3961
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
+ }
3962
4217
  const onEvent = config?.onEvent;
3963
4218
  const onProgress = config?.onProgress;
3964
4219
  const onStream = config?.onStream;
@@ -4016,7 +4271,19 @@ function createAgent(opts) {
4016
4271
  status: "error",
4017
4272
  error: { message: err?.message, stack: err?.stack }
4018
4273
  });
4274
+ if (userTokenCounter) {
4275
+ const { setTokenCounter: setTokenCounter2 } = await Promise.resolve().then(() => (init_utilTokens(), utilTokens_exports));
4276
+ setTokenCounter2(void 0);
4277
+ }
4019
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
+ }
4020
4287
  }
4021
4288
  await finalizeTraceSession(traceSession, {
4022
4289
  agentRuntime: res.agent || runtime,
@@ -4074,12 +4341,58 @@ function createAgent(opts) {
4074
4341
  resolveToolApproval,
4075
4342
  asTool: ({ toolName, description, inputDescription }) => {
4076
4343
  const schema = zod.z.object({ input: zod.z.string().describe(inputDescription || "Input for delegated agent") });
4077
- return createTool({
4344
+ const delegatedTool = createTool({
4078
4345
  name: toolName,
4079
4346
  description: description || `Delegate task to agent ${opts.name || "Agent"}`,
4080
4347
  schema,
4081
4348
  func: async ({ input }) => {
4082
- 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 });
4083
4396
  return {
4084
4397
  content: res.content,
4085
4398
  output: res.output,
@@ -4087,6 +4400,8 @@ function createAgent(opts) {
4087
4400
  };
4088
4401
  }
4089
4402
  });
4403
+ delegatedTool._stateRef = {};
4404
+ return delegatedTool;
4090
4405
  },
4091
4406
  asHandoff: ({ toolName, description, schema }) => {
4092
4407
  const finalName = toolName || `handoff_to_${runtime.name || "agent"}`;
@@ -4226,7 +4541,11 @@ function createAgentNode(opts) {
4226
4541
  };
4227
4542
  }
4228
4543
 
4544
+ // src/nodes/contextSummarize.ts
4545
+ init_utilTokens();
4546
+
4229
4547
  // src/smart/contextPolicy.ts
4548
+ init_utilTokens();
4230
4549
  function collectRecentTurns(messages, lastTurnsToKeep) {
4231
4550
  if (lastTurnsToKeep <= 0) return [];
4232
4551
  const systemPrefix = messages.filter((message, index) => index === 0 && message.role === "system");
@@ -4529,7 +4848,7 @@ function createContextSummarizeNode(opts) {
4529
4848
  const resolved = getResolvedSmartConfig(opts, state.agent);
4530
4849
  const config = resolved.summarization;
4531
4850
  if (!config.enable) return {};
4532
- const model = opts.model;
4851
+ const model = state.agent?.model || opts.model;
4533
4852
  const messages = state.messages || [];
4534
4853
  const onEvent = state.ctx?.__onEvent;
4535
4854
  const traceSession = state.ctx?.__traceSession;
@@ -4848,6 +5167,10 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
4848
5167
  };
4849
5168
  };
4850
5169
  }
5170
+
5171
+ // src/index.ts
5172
+ init_utilTokens();
5173
+ init_utilTokens();
4851
5174
  var TOOL_RESPONSE_RECOVERY_MARKERS = [
4852
5175
  /ARCHIVED_TOOL_RESPONSE\s*\[/,
4853
5176
  /STRUCTURED_TOOL_RESPONSE\s*\[/,
@@ -5166,6 +5489,7 @@ function createContextTools(stateRef, opts) {
5166
5489
  }
5167
5490
 
5168
5491
  // src/graph/decisions.ts
5492
+ init_utilTokens();
5169
5493
  function needsSummarization(state, opts, summarizationEnabled) {
5170
5494
  if (!summarizationEnabled) {
5171
5495
  return false;
@@ -5328,12 +5652,24 @@ function createSmartAgent(opts) {
5328
5652
  toolResponses: resolved.toolResponses,
5329
5653
  useTodoList: planningEnabled
5330
5654
  };
5331
- const stateRef = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
5332
- const contextTools = createContextTools(stateRef, { planningEnabled, includeGetToolResponse: false });
5333
- const getToolResponseTool = createGetToolResponseTool(stateRef);
5334
- const toolsWithoutRecovery = [...opts.tools ?? [], ...contextTools];
5335
- const toolsWithRecovery = [...toolsWithoutRecovery, getToolResponseTool];
5336
- 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 });
5337
5673
  base.__runtime.runtimeProfile = resolved.runtimeProfile;
5338
5674
  base.__runtime.smart = resolved;
5339
5675
  const summarizationEnabled = resolved.summarization.enable !== false;
@@ -5369,17 +5705,17 @@ function createSmartAgent(opts) {
5369
5705
  const memoryFacts = await writeSummaryFactsToMemory(memoryStore, resolved, latestSummary, Math.max(1, (state.messages || []).filter((message) => message.role === "user").length));
5370
5706
  return { ...state, memoryFacts };
5371
5707
  }
5372
- function syncPlanState(state) {
5373
- if (!Array.isArray(stateRef.todoList)) return state;
5708
+ function syncPlanStateWith(ref, state) {
5709
+ if (!Array.isArray(ref.todoList)) return state;
5374
5710
  return {
5375
5711
  ...state,
5376
5712
  plan: {
5377
- version: stateRef.planVersion || state.planVersion || 1,
5378
- steps: stateRef.todoList,
5713
+ version: ref.planVersion || state.planVersion || 1,
5714
+ steps: ref.todoList,
5379
5715
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
5380
- adherenceScore: stateRef.adherenceScore || 0
5716
+ adherenceScore: ref.adherenceScore || 0
5381
5717
  },
5382
- planVersion: stateRef.planVersion || state.planVersion || 1
5718
+ planVersion: ref.planVersion || state.planVersion || 1
5383
5719
  };
5384
5720
  }
5385
5721
  function systemMessage2() {
@@ -5391,9 +5727,9 @@ function createSmartAgent(opts) {
5391
5727
  );
5392
5728
  return { role: "system", content: sys };
5393
5729
  }
5394
- function syncRuntimeTools(currentState, currentMessages) {
5730
+ function syncRuntimeTools(currentState, currentMessages, toolSet) {
5395
5731
  const needsRecoveryTool = hasToolResponseRecoveryReference(currentMessages);
5396
- const nextTools = needsRecoveryTool ? toolsWithRecovery : toolsWithoutRecovery;
5732
+ const nextTools = needsRecoveryTool ? toolSet.toolsWithRecovery : toolSet.toolsWithoutRecovery;
5397
5733
  const currentRuntime = currentState.agent || base.__runtime;
5398
5734
  if (currentRuntime.tools === nextTools) {
5399
5735
  return currentState;
@@ -5412,7 +5748,7 @@ function createSmartAgent(opts) {
5412
5748
  const toolCalls = Array.isArray(lastAssistant.tool_calls) ? lastAssistant.tool_calls : [];
5413
5749
  return toolCalls.length === 0;
5414
5750
  }
5415
- async function trySummarize(currentState, currentRawMessages) {
5751
+ async function trySummarize(currentState, currentRawMessages, ref) {
5416
5752
  if (!summarizer) return { state: currentState, rawMessages: currentRawMessages, compressed: false };
5417
5753
  const delta = await summarizer(currentState);
5418
5754
  if (!delta || Object.keys(delta).length === 0) {
@@ -5421,28 +5757,31 @@ function createSmartAgent(opts) {
5421
5757
  }
5422
5758
  const ctx = { ...currentState.ctx || {} };
5423
5759
  delete ctx.__summarizationExhausted;
5424
- const updated = await persistLatestSummary(syncPlanState({ ...currentState, ...delta, ctx }));
5760
+ const updated = await persistLatestSummary(syncPlanStateWith(ref, { ...currentState, ...delta, ctx }));
5425
5761
  return { state: updated, rawMessages: [...updated.messages || currentRawMessages], compressed: true };
5426
5762
  }
5427
5763
  const instance = {
5428
5764
  invoke: async (input, config) => {
5765
+ const toolSet = buildInvokeToolSet();
5766
+ const stateRef = toolSet.stateRef;
5429
5767
  stateRef.toolHistory = input.toolHistory;
5430
5768
  stateRef.toolHistoryArchived = input.toolHistoryArchived;
5431
5769
  stateRef.todoList = input.plan?.steps;
5432
5770
  stateRef.planVersion = input.planVersion || input.plan?.version || 0;
5433
5771
  stateRef.adherenceScore = input.plan?.adherenceScore || 0;
5772
+ const syncPlanFromRef = (state2) => syncPlanStateWith(stateRef, state2);
5434
5773
  const alreadyHasSystem = Array.isArray(input.messages) && input.messages[0]?.role === "system";
5435
5774
  const seedMessages = alreadyHasSystem ? [...input.messages || []] : [systemMessage2(), ...input.messages || []];
5436
- let state = syncRuntimeTools(await syncMemory({ ...input, messages: seedMessages }), seedMessages);
5775
+ let state = syncRuntimeTools(await syncMemory({ ...input, messages: seedMessages }), seedMessages, toolSet);
5437
5776
  let lastResult = null;
5438
5777
  let rawMessages = [...seedMessages];
5439
5778
  const effectiveMaxToolCalls = config?.limits?.maxToolCalls ?? resolved.limits.maxToolCalls ?? 10;
5440
5779
  const iterationLimit = Math.max(effectiveMaxToolCalls * 3 + 5, 30);
5441
5780
  for (let i = 0; i < iterationLimit; i++) {
5442
- state = syncRuntimeTools({ ...state, messages: rawMessages }, rawMessages);
5781
+ state = syncRuntimeTools({ ...state, messages: rawMessages }, rawMessages, toolSet);
5443
5782
  const next = summarizationEnabled ? decideBefore(state) : "agent";
5444
5783
  if (next === "contextSummarize" && summarizer) {
5445
- const result = await trySummarize(state, rawMessages);
5784
+ const result = await trySummarize(state, rawMessages, stateRef);
5446
5785
  state = result.state;
5447
5786
  rawMessages = result.rawMessages;
5448
5787
  if (result.compressed) continue;
@@ -5466,14 +5805,14 @@ function createSmartAgent(opts) {
5466
5805
  if (currentPlan && !state.plan) {
5467
5806
  state = { ...state, plan: currentPlan };
5468
5807
  }
5469
- state = syncRuntimeTools(syncPlanState(await syncMemory(state)), rawMessages);
5808
+ state = syncRuntimeTools(syncPlanFromRef(await syncMemory(state)), rawMessages, toolSet);
5470
5809
  stateRef.toolHistory = state.toolHistory;
5471
5810
  stateRef.toolHistoryArchived = state.toolHistoryArchived;
5472
5811
  if (state.ctx?.__needsSummarization && summarizer) {
5473
5812
  const ctx = { ...state.ctx || {} };
5474
5813
  delete ctx.__needsSummarization;
5475
5814
  state = { ...state, ctx };
5476
- const result = await trySummarize(state, rawMessages);
5815
+ const result = await trySummarize(state, rawMessages, stateRef);
5477
5816
  state = result.state;
5478
5817
  rawMessages = result.rawMessages;
5479
5818
  continue;
@@ -5484,7 +5823,7 @@ function createSmartAgent(opts) {
5484
5823
  if (summarizationEnabled) {
5485
5824
  const after = decideAfter(state);
5486
5825
  if (after === "contextSummarize" && summarizer) {
5487
- const result = await trySummarize(state, rawMessages);
5826
+ const result = await trySummarize(state, rawMessages, stateRef);
5488
5827
  state = result.state;
5489
5828
  rawMessages = result.rawMessages;
5490
5829
  if (result.compressed) continue;
@@ -5923,7 +6262,50 @@ var ProviderError = class extends Error {
5923
6262
  };
5924
6263
 
5925
6264
  // src/providers/base.ts
6265
+ var DEFAULT_RETRY = {
6266
+ maxRetries: 3,
6267
+ baseDelayMs: 500,
6268
+ maxDelayMs: 8e3
6269
+ };
5926
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
+ }
5927
6309
  };
5928
6310
 
5929
6311
  // src/providers/utils/sse.ts
@@ -6050,6 +6432,7 @@ var OpenAIProvider = class extends BaseProvider {
6050
6432
  ...config.defaultHeaders,
6051
6433
  ...config.organization ? { "OpenAI-Organization": config.organization } : {}
6052
6434
  };
6435
+ if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
6053
6436
  }
6054
6437
  async complete(request) {
6055
6438
  const body = this.buildRequestBody(request, false);
@@ -6129,7 +6512,7 @@ var OpenAIProvider = class extends BaseProvider {
6129
6512
  if (request.tools?.length) {
6130
6513
  body.tools = request.tools.map((t) => this.toOpenAITool(t));
6131
6514
  }
6132
- if (request.toolChoice != null) {
6515
+ if (request.toolChoice != null && request.tools?.length) {
6133
6516
  if (typeof request.toolChoice === "string") {
6134
6517
  body.tool_choice = request.toolChoice;
6135
6518
  } else {
@@ -6197,15 +6580,13 @@ var OpenAIProvider = class extends BaseProvider {
6197
6580
  }
6198
6581
  async doFetch(body) {
6199
6582
  const url = `${this.baseURL}/chat/completions`;
6200
- const res = await fetch(url, {
6201
- method: "POST",
6202
- headers: {
6203
- "Content-Type": "application/json",
6204
- Authorization: `Bearer ${this.apiKey}`,
6205
- ...this.defaultHeaders
6206
- },
6207
- body: JSON.stringify(body)
6208
- });
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 }));
6209
6590
  if (!res.ok) {
6210
6591
  const text = await res.text().catch(() => "");
6211
6592
  throw new ProviderError(
@@ -6275,6 +6656,8 @@ var AnthropicProvider = class extends BaseProvider {
6275
6656
  this.defaultModel = config.defaultModel ?? "claude-sonnet-4-20250514";
6276
6657
  this.defaultHeaders = config.defaultHeaders ?? {};
6277
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 };
6278
6661
  }
6279
6662
  async complete(request) {
6280
6663
  const body = this.buildRequestBody(request, false);
@@ -6393,12 +6776,22 @@ var AnthropicProvider = class extends BaseProvider {
6393
6776
  max_tokens: request.maxTokens ?? 4096,
6394
6777
  stream
6395
6778
  };
6396
- 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
+ }
6397
6786
  if (request.temperature != null) body.temperature = request.temperature;
6398
6787
  if (request.topP != null) body.top_p = request.topP;
6399
6788
  if (request.stop) body.stop_sequences = request.stop;
6400
6789
  if (request.tools?.length) {
6401
- 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;
6402
6795
  }
6403
6796
  if (request.toolChoice != null) {
6404
6797
  if (request.toolChoice === "auto") {
@@ -6497,16 +6890,14 @@ var AnthropicProvider = class extends BaseProvider {
6497
6890
  }
6498
6891
  async doFetch(body) {
6499
6892
  const url = `${this.baseURL}/v1/messages`;
6500
- const res = await fetch(url, {
6501
- method: "POST",
6502
- headers: {
6503
- "Content-Type": "application/json",
6504
- "x-api-key": this.apiKey,
6505
- "anthropic-version": this.anthropicVersion,
6506
- ...this.defaultHeaders
6507
- },
6508
- body: JSON.stringify(body)
6509
- });
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 }));
6510
6901
  if (!res.ok) {
6511
6902
  const text = await res.text().catch(() => "");
6512
6903
  throw new ProviderError(
@@ -6718,6 +7109,8 @@ var BedrockProvider = class extends BaseProvider {
6718
7109
  this.region = config.region;
6719
7110
  this.defaultModel = config.defaultModel ?? "anthropic.claude-sonnet-4-20250514-v1:0";
6720
7111
  this.defaultHeaders = config.defaultHeaders ?? {};
7112
+ this.promptCachingEnabled = config.prompt_caching?.enabled === true;
7113
+ if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
6721
7114
  this.credentials = {
6722
7115
  accessKeyId: config.accessKeyId ?? process.env.AWS_ACCESS_KEY_ID ?? "",
6723
7116
  secretAccessKey: config.secretAccessKey ?? process.env.AWS_SECRET_ACCESS_KEY ?? "",
@@ -6756,7 +7149,9 @@ var BedrockProvider = class extends BaseProvider {
6756
7149
  messages: messages.map((m) => this.toBedrockMessage(m))
6757
7150
  };
6758
7151
  if (system.length > 0) {
6759
- 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;
6760
7155
  }
6761
7156
  const inferenceConfig = {};
6762
7157
  if (request.maxTokens != null) inferenceConfig.maxTokens = request.maxTokens;
@@ -6765,8 +7160,12 @@ var BedrockProvider = class extends BaseProvider {
6765
7160
  if (request.stop) inferenceConfig.stopSequences = request.stop;
6766
7161
  if (Object.keys(inferenceConfig).length > 0) body.inferenceConfig = inferenceConfig;
6767
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
+ }
6768
7167
  body.toolConfig = {
6769
- tools: request.tools.map((t) => this.toBedrockTool(t))
7168
+ tools
6770
7169
  };
6771
7170
  if (request.toolChoice != null) {
6772
7171
  if (request.toolChoice === "auto") {
@@ -6863,23 +7262,21 @@ var BedrockProvider = class extends BaseProvider {
6863
7262
  const host = `bedrock-runtime.${this.region}.amazonaws.com`;
6864
7263
  const url = `https://${host}/model/${encodeURIComponent(modelId)}/converse`;
6865
7264
  const bodyStr = JSON.stringify(body);
6866
- const headers = signRequest({
6867
- method: "POST",
6868
- url,
6869
- headers: {
6870
- "Content-Type": "application/json",
6871
- Accept: "application/json",
6872
- ...this.defaultHeaders
6873
- },
6874
- body: bodyStr,
6875
- region: this.region,
6876
- service: "bedrock",
6877
- credentials: this.credentials
6878
- });
6879
- const res = await fetch(url, {
6880
- method: "POST",
6881
- headers,
6882
- 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 });
6883
7280
  });
6884
7281
  if (!res.ok) {
6885
7282
  const text = await res.text().catch(() => "");
@@ -7410,7 +7807,7 @@ function fromNativeProvider(provider, options) {
7410
7807
  const reasoningCfg = reasoningOverride ?? opts?.reasoning;
7411
7808
  if (reasoningCfg) req.reasoning = reasoningCfg;
7412
7809
  const tc = invokeOptions?.tool_choice ?? invokeOptions?.toolChoice;
7413
- if (tc) {
7810
+ if (tc && tools?.length) {
7414
7811
  req.toolChoice = tc;
7415
7812
  }
7416
7813
  const rf = invokeOptions?.response_format;
@@ -7793,6 +8190,7 @@ exports.createToolLimitFinalizeNode = createToolLimitFinalizeNode;
7793
8190
  exports.createToolsNode = createToolsNode;
7794
8191
  exports.customCallbackRule = customCallbackRule;
7795
8192
  exports.customSink = customSink;
8193
+ exports.defaultTokenCounter = defaultTokenCounter;
7796
8194
  exports.emptyUsage = emptyUsage;
7797
8195
  exports.evaluateGuardrails = evaluateGuardrails;
7798
8196
  exports.fileSink = fileSink;
@@ -7805,6 +8203,7 @@ exports.generateTraceId = generateTraceId;
7805
8203
  exports.getModelCapabilities = getModelCapabilities;
7806
8204
  exports.getNestedValue = getNestedValue;
7807
8205
  exports.getResolvedSmartConfig = getResolvedSmartConfig;
8206
+ exports.getTokenCounter = getTokenCounter;
7808
8207
  exports.hasToolResponseRecoveryReference = hasToolResponseRecoveryReference;
7809
8208
  exports.httpSink = httpSink;
7810
8209
  exports.isSmartModel = isSmartModel;
@@ -7818,6 +8217,7 @@ exports.resolveStrategy = resolveStrategy;
7818
8217
  exports.resolveToolApprovalState = resolveToolApprovalState;
7819
8218
  exports.restoreSnapshot = restoreSnapshot;
7820
8219
  exports.runSmartAgentEvalHarness = runSmartAgentEvalHarness;
8220
+ exports.setTokenCounter = setTokenCounter;
7821
8221
  exports.startStreamingSession = startStreamingSession;
7822
8222
  exports.traceSessionToOtlp = traceSessionToOtlp;
7823
8223
  exports.withTools = withTools;