@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.mjs CHANGED
@@ -8,7 +8,99 @@ import { nanoid } from 'nanoid';
8
8
  import crypto, { randomUUID, createHash, createHmac } from 'crypto';
9
9
  import AjvModule from 'ajv';
10
10
 
11
- // src/adapters/langchain.ts
11
+ var __defProp = Object.defineProperty;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __esm = (fn, res) => function __init() {
14
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
15
+ };
16
+ var __export = (target, all) => {
17
+ for (var name in all)
18
+ __defProp(target, name, { get: all[name], enumerable: true });
19
+ };
20
+
21
+ // src/utils/utilTokens.ts
22
+ var utilTokens_exports = {};
23
+ __export(utilTokens_exports, {
24
+ countApproxTokens: () => countApproxTokens,
25
+ countMessagesTokens: () => countMessagesTokens,
26
+ defaultTokenCounter: () => defaultTokenCounter,
27
+ getTokenCounter: () => getTokenCounter,
28
+ setTokenCounter: () => setTokenCounter
29
+ });
30
+ function defaultTokenCounter(text) {
31
+ if (!text) return 0;
32
+ const nonAsciiCount = (text.match(/[^\x00-\x7F]/g) || []).length;
33
+ const asciiCount = text.length - nonAsciiCount;
34
+ return Math.ceil(asciiCount / 4 + nonAsciiCount / 1.5);
35
+ }
36
+ function setTokenCounter(counter) {
37
+ activeTokenCounter = typeof counter === "function" ? counter : defaultTokenCounter;
38
+ }
39
+ function getTokenCounter() {
40
+ return activeTokenCounter;
41
+ }
42
+ function countApproxTokens(text) {
43
+ return activeTokenCounter(text || "");
44
+ }
45
+ function extractMessageText(m) {
46
+ if (!m) return "";
47
+ const parts = [];
48
+ parts.push(m.role || "");
49
+ if (m.name) parts.push(m.name);
50
+ if (typeof m.content === "string") {
51
+ parts.push(m.content);
52
+ } else if (Array.isArray(m.content)) {
53
+ for (const c of m.content) {
54
+ if (typeof c === "string") parts.push(c);
55
+ else if (c?.text) parts.push(c.text);
56
+ else if (c?.content) parts.push(c.content);
57
+ else if (c?.type === "tool_use" && c?.input) {
58
+ parts.push(c.name || "");
59
+ parts.push(typeof c.input === "string" ? c.input : JSON.stringify(c.input));
60
+ } else if (c != null && typeof c === "object") {
61
+ parts.push(JSON.stringify(c));
62
+ }
63
+ }
64
+ } else if (m.content != null && typeof m.content === "object") {
65
+ parts.push(JSON.stringify(m.content));
66
+ }
67
+ if (Array.isArray(m.tool_calls)) {
68
+ for (const tc of m.tool_calls) {
69
+ if (tc?.function?.name) parts.push(tc.function.name);
70
+ if (tc?.function?.arguments) {
71
+ parts.push(
72
+ typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
73
+ );
74
+ }
75
+ if (tc?.name && !tc?.function?.name) parts.push(tc.name);
76
+ if (tc?.args && !tc?.function?.arguments) {
77
+ parts.push(typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args));
78
+ }
79
+ }
80
+ }
81
+ if (Array.isArray(m.additional_kwargs?.tool_calls)) {
82
+ for (const tc of m.additional_kwargs.tool_calls) {
83
+ if (tc?.function?.name) parts.push(tc.function.name);
84
+ if (tc?.function?.arguments) {
85
+ parts.push(
86
+ typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
87
+ );
88
+ }
89
+ }
90
+ }
91
+ return parts.join("\n");
92
+ }
93
+ function countMessagesTokens(messages) {
94
+ if (!Array.isArray(messages) || messages.length === 0) return 0;
95
+ const allText = messages.map(extractMessageText).join("\n");
96
+ return countApproxTokens(allText) + messages.length * 4;
97
+ }
98
+ var activeTokenCounter;
99
+ var init_utilTokens = __esm({
100
+ "src/utils/utilTokens.ts"() {
101
+ activeTokenCounter = defaultTokenCounter;
102
+ }
103
+ });
12
104
  var moduleName = "@langchain/core/tools";
13
105
  var cachedToolFactory;
14
106
  var cachedHasAttemptedFactory = false;
@@ -316,7 +408,9 @@ function createTool({
316
408
  needsApproval,
317
409
  approvalPrompt,
318
410
  approvalDefaults,
319
- maxExecutionsPerRun
411
+ maxExecutionsPerRun,
412
+ cache,
413
+ retry
320
414
  }) {
321
415
  const execute = async (input) => func(input);
322
416
  const toolRecord = {
@@ -341,6 +435,12 @@ function createTool({
341
435
  if (maxExecutionsPerRun !== void 0) {
342
436
  toolRecord.maxExecutionsPerRun = maxExecutionsPerRun;
343
437
  }
438
+ if (cache !== void 0) {
439
+ toolRecord.cache = cache;
440
+ }
441
+ if (retry !== void 0) {
442
+ toolRecord.retry = retry;
443
+ }
344
444
  toolRecord.__source = toolRecord.__source || "smart";
345
445
  toolRecord.__impl = func;
346
446
  return toolRecord;
@@ -1882,7 +1982,8 @@ function createAgentCoreNode(opts) {
1882
1982
  todoListPrompt: opts.todoListPrompt,
1883
1983
  limits: opts.limits,
1884
1984
  outputSchema: opts.outputSchema,
1885
- tracing: opts.tracing
1985
+ tracing: opts.tracing,
1986
+ responseFormat: void 0
1886
1987
  };
1887
1988
  const tools = runtime.tools ?? [];
1888
1989
  const shouldUseStrictToolCalling = Boolean(
@@ -2084,7 +2185,7 @@ function createAgentCoreNode(opts) {
2084
2185
  // src/smart/runtimeConfig.ts
2085
2186
  var DEFAULT_CRITICAL_TOOLS = ["response", "manage_todo_list", "get_tool_response"];
2086
2187
  var BASE_DEFAULTS = {
2087
- limits: { maxToolCalls: 8, maxParallelTools: 2, maxContextTokens: 24e3 },
2188
+ limits: { maxToolCalls: 8, maxParallelTools: 2, maxContextTokens: 24e3, maxTotalOutputTokens: 0, maxCostUsd: 0, maxWallClockMs: 0 },
2088
2189
  summarization: {
2089
2190
  enable: true,
2090
2191
  maxTokens: 24e3,
@@ -2211,7 +2312,10 @@ function mergeLimits(base, override) {
2211
2312
  return {
2212
2313
  maxToolCalls: override?.maxToolCalls ?? base.maxToolCalls,
2213
2314
  maxParallelTools: override?.maxParallelTools ?? base.maxParallelTools,
2214
- maxContextTokens: override?.maxContextTokens ?? base.maxContextTokens
2315
+ maxContextTokens: override?.maxContextTokens ?? base.maxContextTokens,
2316
+ maxTotalOutputTokens: override?.maxTotalOutputTokens ?? base.maxTotalOutputTokens ?? 0,
2317
+ maxCostUsd: override?.maxCostUsd ?? base.maxCostUsd ?? 0,
2318
+ maxWallClockMs: override?.maxWallClockMs ?? base.maxWallClockMs ?? 0
2215
2319
  };
2216
2320
  }
2217
2321
  function resolveBaseProfile(opts, runtimeProfile) {
@@ -2229,6 +2333,7 @@ function normalizeSmartAgentOptions(opts) {
2229
2333
  const summarizationOverride = typeof opts.summarization === "object" ? opts.summarization : {};
2230
2334
  const planningMode = opts.planning?.mode ?? customProfile.planning?.mode ?? (useTodoList ? "todo" : preset.planning.mode);
2231
2335
  const memoryStore = opts.memory?.store ?? customProfile.memory?.store;
2336
+ const contextToolResponsePolicy = opts.context?.toolResponsePolicy ?? customProfile.context?.toolResponsePolicy ?? preset.context.toolResponsePolicy;
2232
2337
  return {
2233
2338
  runtimeProfile,
2234
2339
  baseProfile,
@@ -2251,7 +2356,7 @@ function normalizeSmartAgentOptions(opts) {
2251
2356
  ...customProfile.context?.budget || {},
2252
2357
  ...opts.context?.budget || {}
2253
2358
  },
2254
- toolResponsePolicy: opts.context?.toolResponsePolicy ?? customProfile.context?.toolResponsePolicy ?? preset.context.toolResponsePolicy
2359
+ toolResponsePolicy: contextToolResponsePolicy
2255
2360
  },
2256
2361
  planning: {
2257
2362
  ...preset.planning,
@@ -2274,6 +2379,7 @@ function normalizeSmartAgentOptions(opts) {
2274
2379
  ...preset.toolResponses,
2275
2380
  ...customProfile.toolResponses || {},
2276
2381
  ...opts.toolResponses || {},
2382
+ defaultPolicy: opts.toolResponses?.defaultPolicy ?? customProfile.toolResponses?.defaultPolicy ?? contextToolResponsePolicy,
2277
2383
  toolResponseRetentionByTool: {
2278
2384
  ...preset.toolResponses.toolResponseRetentionByTool,
2279
2385
  ...customProfile.toolResponses?.toolResponseRetentionByTool || {},
@@ -2287,66 +2393,8 @@ function getResolvedSmartConfig(opts, runtime) {
2287
2393
  return runtime?.smart || normalizeSmartAgentOptions(opts);
2288
2394
  }
2289
2395
 
2290
- // src/utils/utilTokens.ts
2291
- function countApproxTokens(text) {
2292
- if (!text) return 0;
2293
- const nonAsciiCount = (text.match(/[^\x00-\x7F]/g) || []).length;
2294
- const asciiCount = text.length - nonAsciiCount;
2295
- return Math.ceil(asciiCount / 4 + nonAsciiCount / 1.5);
2296
- }
2297
- function extractMessageText(m) {
2298
- if (!m) return "";
2299
- const parts = [];
2300
- parts.push(m.role || "");
2301
- if (m.name) parts.push(m.name);
2302
- if (typeof m.content === "string") {
2303
- parts.push(m.content);
2304
- } else if (Array.isArray(m.content)) {
2305
- for (const c of m.content) {
2306
- if (typeof c === "string") parts.push(c);
2307
- else if (c?.text) parts.push(c.text);
2308
- else if (c?.content) parts.push(c.content);
2309
- else if (c?.type === "tool_use" && c?.input) {
2310
- parts.push(c.name || "");
2311
- parts.push(typeof c.input === "string" ? c.input : JSON.stringify(c.input));
2312
- } else if (c != null && typeof c === "object") {
2313
- parts.push(JSON.stringify(c));
2314
- }
2315
- }
2316
- } else if (m.content != null && typeof m.content === "object") {
2317
- parts.push(JSON.stringify(m.content));
2318
- }
2319
- if (Array.isArray(m.tool_calls)) {
2320
- for (const tc of m.tool_calls) {
2321
- if (tc?.function?.name) parts.push(tc.function.name);
2322
- if (tc?.function?.arguments) {
2323
- parts.push(
2324
- typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
2325
- );
2326
- }
2327
- if (tc?.name && !tc?.function?.name) parts.push(tc.name);
2328
- if (tc?.args && !tc?.function?.arguments) {
2329
- parts.push(typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args));
2330
- }
2331
- }
2332
- }
2333
- if (Array.isArray(m.additional_kwargs?.tool_calls)) {
2334
- for (const tc of m.additional_kwargs.tool_calls) {
2335
- if (tc?.function?.name) parts.push(tc.function.name);
2336
- if (tc?.function?.arguments) {
2337
- parts.push(
2338
- typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
2339
- );
2340
- }
2341
- }
2342
- }
2343
- return parts.join("\n");
2344
- }
2345
- function countMessagesTokens(messages) {
2346
- if (!Array.isArray(messages) || messages.length === 0) return 0;
2347
- const allText = messages.map(extractMessageText).join("\n");
2348
- return countApproxTokens(allText) + messages.length * 4;
2349
- }
2396
+ // src/smart/toolResponses.ts
2397
+ init_utilTokens();
2350
2398
 
2351
2399
  // src/utils/content.ts
2352
2400
  function contentToString(content) {
@@ -2546,6 +2594,7 @@ function createToolsNode(initialTools, opts) {
2546
2594
  anyTool._stateRef.messages = state.messages;
2547
2595
  anyTool._stateRef.ctx = state.ctx || (state.ctx = {});
2548
2596
  anyTool._stateRef.__onEvent = onEvent;
2597
+ anyTool._stateRef.parentRuntime = runtime;
2549
2598
  }
2550
2599
  }
2551
2600
  const last = state.messages[state.messages.length - 1];
@@ -2556,6 +2605,7 @@ function createToolsNode(initialTools, opts) {
2556
2605
  const remaining = Math.max(0, limits.maxToolCalls - toolCount);
2557
2606
  const planned = toolCalls.slice(0, remaining);
2558
2607
  const skipped = toolCalls.slice(remaining);
2608
+ const orderedSlots = planned.map(() => []);
2559
2609
  const isCancelled = () => {
2560
2610
  if (abortSignal?.aborted) return { cancelled: true, reason: "aborted" };
2561
2611
  if (cancellationToken && cancellationToken.isCancellationRequested) return { cancelled: true, reason: "cancelled" };
@@ -2572,7 +2622,9 @@ function createToolsNode(initialTools, opts) {
2572
2622
  }
2573
2623
  state.ctx = ctx;
2574
2624
  };
2575
- const runOne = async (tc) => {
2625
+ const runOne = async (tc, slot = appended) => {
2626
+ var _a;
2627
+ const push = (msg) => slot.push(msg);
2576
2628
  const cancelState = isCancelled();
2577
2629
  if (cancelState.cancelled) {
2578
2630
  const ctx = state.ctx = state.ctx || {};
@@ -2583,7 +2635,7 @@ function createToolsNode(initialTools, opts) {
2583
2635
  }
2584
2636
  const tool = toolByName.get(tc.name);
2585
2637
  if (!tool) {
2586
- appended.push({ role: "tool", content: `Tool not found: ${tc.name}`, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
2638
+ push({ role: "tool", content: `Tool not found: ${tc.name}`, tool_call_id: tc.id || `${tc.name}_unknown`, name: tc.name });
2587
2639
  onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args: tc.args, error: { message: "Tool not found" } });
2588
2640
  toolCount += 1;
2589
2641
  return { status: "error" };
@@ -2599,7 +2651,7 @@ function createToolsNode(initialTools, opts) {
2599
2651
  const validatedArgs = validateToolArgs(tool, args);
2600
2652
  if (!validatedArgs.ok && resolved.toolResponses.schemaValidation === "strict") {
2601
2653
  const errorMessage = `Tool argument validation failed for ${tc.name}: ${validatedArgs.message}`;
2602
- appended.push({ role: "tool", content: errorMessage, tool_call_id: toolCallId, name: tc.name });
2654
+ push({ role: "tool", content: errorMessage, tool_call_id: toolCallId, name: tc.name });
2603
2655
  markToolFailure(errorMessage, tc.name, toolCallId);
2604
2656
  onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args, error: { message: errorMessage } });
2605
2657
  toolCount += 1;
@@ -2621,7 +2673,7 @@ function createToolsNode(initialTools, opts) {
2621
2673
  const currentExecutions = countSuccessfulToolExecutions(toolHistory, toolName);
2622
2674
  if (currentExecutions >= maxExecutionsPerRun) {
2623
2675
  const limitMessage = `Skipped tool due to per-tool execution limit: ${toolName} (${maxExecutionsPerRun}/run)`;
2624
- appended.push({ role: "tool", content: limitMessage, tool_call_id: toolCallId, name: tc.name });
2676
+ push({ role: "tool", content: limitMessage, tool_call_id: toolCallId, name: tc.name });
2625
2677
  onEvent?.({ type: "tool_call", phase: "skipped", name: toolName, id: tc.id, args });
2626
2678
  recordTraceEvent(traceSession, {
2627
2679
  type: "tool_call",
@@ -2681,7 +2733,7 @@ function createToolsNode(initialTools, opts) {
2681
2733
  }
2682
2734
  if (approvalEntry.status === "rejected") {
2683
2735
  const rejectionMessage = approvalEntry.comment || "Tool call rejected by reviewer.";
2684
- appended.push({ role: "tool", content: `Tool call rejected: ${rejectionMessage}`, tool_call_id: toolCallId, name: tc.name });
2736
+ push({ role: "tool", content: `Tool call rejected: ${rejectionMessage}`, tool_call_id: toolCallId, name: tc.name });
2685
2737
  approvalEntry.metadata = { ...approvalEntry.metadata || {}, resolution: "rejected" };
2686
2738
  approvalEntry.status = "executed";
2687
2739
  approvalEntry.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -2699,23 +2751,102 @@ function createToolsNode(initialTools, opts) {
2699
2751
  const start = Date.now();
2700
2752
  const sanitizedArgs = sanitizeTracePayload(args);
2701
2753
  const inputBytes = traceSession?.resolvedConfig.logData ? estimatePayloadBytes(sanitizedArgs) : void 0;
2754
+ const cacheCfg = tool.cache;
2755
+ const cacheEnabled = cacheCfg === true || cacheCfg && typeof cacheCfg === "object";
2756
+ const cacheKeyFn = cacheEnabled && typeof cacheCfg === "object" ? cacheCfg.keyFn : void 0;
2757
+ const stableJson = (value) => {
2758
+ try {
2759
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
2760
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
2761
+ const keys = Object.keys(value).sort();
2762
+ return `{${keys.map((k) => JSON.stringify(k) + ":" + stableJson(value[k])).join(",")}}`;
2763
+ } catch {
2764
+ return String(value);
2765
+ }
2766
+ };
2767
+ const cacheKey = cacheEnabled ? `${toolName}|${cacheKeyFn ? cacheKeyFn(args) : stableJson(args)}` : void 0;
2768
+ const toolCache = state.toolCache = state.toolCache || {};
2769
+ let cachedHit = void 0;
2770
+ if (cacheKey && Object.prototype.hasOwnProperty.call(toolCache, cacheKey)) {
2771
+ const entry = toolCache[cacheKey];
2772
+ const ttl = cacheEnabled && typeof cacheCfg === "object" ? cacheCfg.ttlMs : void 0;
2773
+ if (!ttl || entry?.timestamp && Date.now() - entry.timestamp <= ttl) {
2774
+ cachedHit = entry?.value;
2775
+ }
2776
+ }
2777
+ const retryCfg = tool.retry;
2778
+ const failureStats = (_a = state.ctx).__toolFailureCounts || (_a.__toolFailureCounts = {});
2779
+ if (retryCfg?.circuitBreakerThreshold && failureStats[toolName] >= retryCfg.circuitBreakerThreshold) {
2780
+ const breakerMsg = `Tool ${toolName} circuit breaker open after ${failureStats[toolName]} consecutive failures`;
2781
+ push({ role: "tool", content: breakerMsg, tool_call_id: toolCallId, name: tc.name });
2782
+ markToolFailure(breakerMsg, toolName, toolCallId);
2783
+ onEvent?.({ type: "tool_call", phase: "error", name: toolName, id: tc.id, args, error: { message: breakerMsg } });
2784
+ toolCount += 1;
2785
+ return { status: "error" };
2786
+ }
2702
2787
  try {
2788
+ if (cachedHit !== void 0) {
2789
+ const executionId2 = nanoid();
2790
+ const responsePolicy2 = applyToolResponseHardCap(toolName, cachedHit, executionId2, resolved);
2791
+ toolHistory.push({
2792
+ executionId: executionId2,
2793
+ toolName,
2794
+ args,
2795
+ output: cachedHit,
2796
+ rawOutput: cachedHit,
2797
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2798
+ tool_call_id: tc.id,
2799
+ summarized: false,
2800
+ originalTokenCount: responsePolicy2.tokenCount,
2801
+ classification: responsePolicy2.classification,
2802
+ retentionPolicy: "keep_full",
2803
+ fromCache: true,
2804
+ status: "success"
2805
+ });
2806
+ push({ role: "tool", content: responsePolicy2.content, tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
2807
+ onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: cachedHit, durationMs: 0 });
2808
+ toolCount += 1;
2809
+ return { status: "success" };
2810
+ }
2703
2811
  onEvent?.({ type: "tool_call", phase: "start", name: toolName, id: tc.id, args });
2704
2812
  onProgress?.({ stage: "tools", message: `Running tool ${tc.name}` });
2705
2813
  const anyTool = tool;
2706
2814
  const callOptions = { cancellationToken, signal: abortSignal };
2815
+ const invokeOnce = async () => {
2816
+ if (typeof anyTool.func === "function") return anyTool.func(args, callOptions);
2817
+ if (typeof anyTool.invoke === "function") return anyTool.invoke(args, callOptions);
2818
+ if (typeof anyTool.call === "function") return anyTool.call(args, callOptions);
2819
+ if (typeof anyTool._call === "function") return anyTool._call(args, callOptions);
2820
+ if (typeof anyTool.run === "function") return anyTool.run(args, callOptions);
2821
+ throw new Error("Tool is not invokable");
2822
+ };
2707
2823
  let output;
2708
- if (typeof anyTool.func === "function") output = await anyTool.func(args, callOptions);
2709
- else if (typeof anyTool.invoke === "function") output = await anyTool.invoke(args, callOptions);
2710
- else if (typeof anyTool.call === "function") output = await anyTool.call(args, callOptions);
2711
- else if (typeof anyTool._call === "function") output = await anyTool._call(args, callOptions);
2712
- else if (typeof anyTool.run === "function") output = await anyTool.run(args, callOptions);
2713
- else throw new Error("Tool is not invokable");
2824
+ const maxRetries = Math.max(0, Number(retryCfg?.maxRetries) || 0);
2825
+ const backoffMs = Math.max(0, Number(retryCfg?.backoffMs) || 250);
2826
+ let attempt = 0;
2827
+ while (true) {
2828
+ try {
2829
+ output = await invokeOnce();
2830
+ failureStats[toolName] = 0;
2831
+ break;
2832
+ } catch (retryErr) {
2833
+ attempt += 1;
2834
+ const shouldRetry = retryCfg?.shouldRetry ? retryCfg.shouldRetry(retryErr, attempt) : true;
2835
+ if (attempt > maxRetries || !shouldRetry) {
2836
+ failureStats[toolName] = (failureStats[toolName] || 0) + 1;
2837
+ throw retryErr;
2838
+ }
2839
+ await new Promise((r) => setTimeout(r, backoffMs * Math.pow(2, attempt - 1)));
2840
+ }
2841
+ }
2842
+ if (cacheKey) {
2843
+ toolCache[cacheKey] = { value: output, timestamp: Date.now() };
2844
+ }
2714
2845
  const durationMs = Date.now() - start;
2715
2846
  const executionId = nanoid();
2716
2847
  if (output && typeof output === "object" && output.__handoff && output.__handoff.runtime) {
2717
2848
  toolHistory.push({ executionId, toolName, args, output: "handoff:ok", rawOutput: output, timestamp: (/* @__PURE__ */ new Date()).toISOString(), tool_call_id: tc.id, status: "handoff" });
2718
- appended.push({ role: "tool", content: "ok", tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
2849
+ push({ role: "tool", content: "ok", tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
2719
2850
  state.agent = output.__handoff.runtime;
2720
2851
  onEvent?.({ type: "handoff", from: runtime.name, to: state.agent?.name, toolName });
2721
2852
  onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: "handoff", durationMs });
@@ -2767,7 +2898,7 @@ function createToolsNode(initialTools, opts) {
2767
2898
  archiveId: void 0,
2768
2899
  status: "success"
2769
2900
  });
2770
- appended.push({ role: "tool", content: responsePolicy.content, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
2901
+ push({ role: "tool", content: responsePolicy.content, tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
2771
2902
  onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: output, durationMs });
2772
2903
  onProgress?.({ stage: "tools", message: `Tool ${tc.name} completed`, detail: { durationMs } });
2773
2904
  if (needsApproval && approvalEntry) {
@@ -2823,7 +2954,7 @@ function createToolsNode(initialTools, opts) {
2823
2954
  const executionId = nanoid();
2824
2955
  const message = error?.message || String(error);
2825
2956
  toolHistory.push({ executionId, toolName, args, output: `Error executing tool: ${message}`, rawOutput: null, timestamp: (/* @__PURE__ */ new Date()).toISOString(), tool_call_id: tc.id, status: "error" });
2826
- appended.push({ role: "tool", content: `Error executing tool: ${message}`, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
2957
+ push({ role: "tool", content: `Error executing tool: ${message}`, tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
2827
2958
  markToolFailure(message, toolName, toolCallId);
2828
2959
  onEvent?.({ type: "tool_call", phase: "error", name: toolName, id: tc.id, args, error: { message } });
2829
2960
  onProgress?.({ stage: "tools", message: `Tool ${tc.name} failed`, detail: { error: message } });
@@ -2860,22 +2991,67 @@ function createToolsNode(initialTools, opts) {
2860
2991
  return { status: "error", approval: approvalEntry };
2861
2992
  }
2862
2993
  };
2863
- for (const tc of planned) {
2864
- if (awaitingApproval) break;
2994
+ const approvalIndices = [];
2995
+ const parallelIndices = [];
2996
+ planned.forEach((tc, idx) => {
2997
+ const t = toolByName.get(tc.name);
2998
+ if (t && t.needsApproval) approvalIndices.push(idx);
2999
+ else parallelIndices.push(idx);
3000
+ });
3001
+ let shouldStop = false;
3002
+ const checkCancellationStop = () => {
2865
3003
  const cancelState = isCancelled();
2866
3004
  if (cancelState.cancelled) {
2867
3005
  const ctx = state.ctx = state.ctx || {};
2868
3006
  ctx.__cancelled = { stage: "tools", reason: cancelState.reason, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
2869
3007
  onEvent?.({ type: "cancelled", stage: "tools", reason: cancelState.reason });
2870
3008
  onProgress?.({ stage: "tools", message: "Cancelled", detail: { reason: cancelState.reason } });
2871
- break;
3009
+ shouldStop = true;
3010
+ return true;
2872
3011
  }
2873
- const result = await runOne(tc);
3012
+ return false;
3013
+ };
3014
+ for (const idx of approvalIndices) {
3015
+ if (awaitingApproval || shouldStop) break;
3016
+ if (checkCancellationStop()) break;
3017
+ const result = await runOne(planned[idx], orderedSlots[idx]);
2874
3018
  if (result.status === "awaiting_approval") {
2875
3019
  awaitingApproval = true;
2876
3020
  break;
2877
3021
  }
2878
3022
  }
3023
+ if (!awaitingApproval && !shouldStop && parallelIndices.length > 0) {
3024
+ const concurrency = Math.max(1, Math.min(limits.maxParallelTools, parallelIndices.length));
3025
+ let cursor = 0;
3026
+ const workerError = {};
3027
+ const workers = [];
3028
+ for (let w = 0; w < concurrency; w++) {
3029
+ workers.push((async () => {
3030
+ while (true) {
3031
+ if (workerError.err || shouldStop || awaitingApproval) return;
3032
+ const i = cursor++;
3033
+ if (i >= parallelIndices.length) return;
3034
+ if (checkCancellationStop()) return;
3035
+ const idx = parallelIndices[i];
3036
+ try {
3037
+ const result = await runOne(planned[idx], orderedSlots[idx]);
3038
+ if (result.status === "awaiting_approval") {
3039
+ awaitingApproval = true;
3040
+ return;
3041
+ }
3042
+ } catch (err) {
3043
+ workerError.err = err;
3044
+ return;
3045
+ }
3046
+ }
3047
+ })());
3048
+ }
3049
+ await Promise.all(workers);
3050
+ if (workerError.err) throw workerError.err;
3051
+ }
3052
+ for (const slot of orderedSlots) {
3053
+ if (slot.length > 0) appended.push(...slot);
3054
+ }
2879
3055
  for (const tc of skipped) {
2880
3056
  onEvent?.({ type: "tool_call", phase: "skipped", name: tc.name, id: tc.id, args: tc.args });
2881
3057
  const sanitizedArgs = sanitizeTracePayload(tc.args);
@@ -2911,11 +3087,23 @@ function createToolsNode(initialTools, opts) {
2911
3087
  }
2912
3088
  state.ctx = Object.keys(ctx).length > 0 ? ctx : void 0;
2913
3089
  }
3090
+ const appendedHasToolResult = appended.some((m) => m.role === "tool");
3091
+ if (appendedHasToolResult && state.ctx?.__summarizationExhausted) {
3092
+ const ctx = { ...state.ctx };
3093
+ delete ctx.__summarizationExhausted;
3094
+ state.ctx = Object.keys(ctx).length > 0 ? ctx : void 0;
3095
+ }
2914
3096
  return {
2915
3097
  messages: [...state.messages, ...appended],
2916
3098
  toolCallCount: toolCount,
2917
3099
  toolHistory,
2918
3100
  toolHistoryArchived,
3101
+ toolCache: state.toolCache,
3102
+ // Explicitly propagate ctx so callers using `{ ...state, ...delta }`
3103
+ // do not silently capture a stale ctx reference. Mutations to
3104
+ // __summarizationExhausted / __awaitingApproval / __structuredOutputParsed
3105
+ // are made on this object during execution.
3106
+ ctx: state.ctx,
2919
3107
  agent: state.agent,
2920
3108
  pendingApprovals
2921
3109
  };
@@ -3152,7 +3340,8 @@ function resolveReflection(input, preset) {
3152
3340
  maxChars: preset.maxChars,
3153
3341
  keepLast: 3,
3154
3342
  summarize: false,
3155
- emitEvents: true
3343
+ emitEvents: true,
3344
+ everyNTurns: 1
3156
3345
  };
3157
3346
  }
3158
3347
  const cfg = input ?? {};
@@ -3167,7 +3356,9 @@ function resolveReflection(input, preset) {
3167
3356
  keepLast: cfg.keepLast ?? 3,
3168
3357
  summarize: cfg.summarize ?? false,
3169
3358
  promptTemplate: cfg.promptTemplate,
3170
- emitEvents: cfg.emitEvents ?? true
3359
+ emitEvents: cfg.emitEvents ?? true,
3360
+ maxPerRun: typeof cfg.maxPerRun === "number" && cfg.maxPerRun >= 0 ? cfg.maxPerRun : void 0,
3361
+ everyNTurns: Math.max(1, Math.trunc(cfg.everyNTurns ?? 1))
3171
3362
  };
3172
3363
  }
3173
3364
  function ensureArray(value) {
@@ -3380,6 +3571,9 @@ function resolveToolApprovalState(state, resolution) {
3380
3571
  };
3381
3572
  }
3382
3573
 
3574
+ // src/agent.ts
3575
+ init_utilTokens();
3576
+
3383
3577
  // src/utils/syntheticMessages.ts
3384
3578
  function isSyntheticSummaryMessage(message) {
3385
3579
  if (!message) return false;
@@ -3558,6 +3752,21 @@ function getActiveSummarizationThreshold(opts) {
3558
3752
  }
3559
3753
  return summarization.summaryTriggerTokens || summarization.maxTokens || opts.limits?.maxContextTokens || 5e4;
3560
3754
  }
3755
+ function shouldSignalSummarization(state, threshold) {
3756
+ const tokCountMessages = (state.messages || []).filter((message) => {
3757
+ if (isSyntheticSummaryMessage(message)) return false;
3758
+ if (message.role === "system" && (message.name === "context_summary" || message.name === "memory_context")) return false;
3759
+ return true;
3760
+ });
3761
+ const tokenCount = countMessagesTokens(tokCountMessages);
3762
+ if (tokenCount <= threshold) return "skip";
3763
+ if (state.ctx?.__summarizationExhausted) return "skip";
3764
+ const hasFreshSummary = (state.messages || []).some(
3765
+ (m) => m.role === "tool" && typeof m.content === "string" && (m.content === "SUMMARIZED" || m.content.startsWith("SUMMARIZED_TOOL_RESPONSE"))
3766
+ );
3767
+ if (hasFreshSummary && tokenCount <= threshold * 1.15) return "skip";
3768
+ return "trigger";
3769
+ }
3561
3770
  function clearNeedsSummarization(state) {
3562
3771
  if (!state.ctx?.__needsSummarization) {
3563
3772
  return state;
@@ -3652,6 +3861,45 @@ function createAgent(opts) {
3652
3861
  };
3653
3862
  const maxToolCalls = mergedLimits?.maxToolCalls === void 0 ? 50 : mergedLimits?.maxToolCalls;
3654
3863
  const iterationLimit = maxToolCalls === Infinity ? 100 : Math.max(maxToolCalls * 3 + 10, 40);
3864
+ const wallClockStart = Date.now();
3865
+ const maxWallClockMs = mergedLimits?.maxWallClockMs && mergedLimits.maxWallClockMs > 0 ? mergedLimits.maxWallClockMs : void 0;
3866
+ const maxTotalOutputTokens = mergedLimits?.maxTotalOutputTokens && mergedLimits.maxTotalOutputTokens > 0 ? mergedLimits.maxTotalOutputTokens : void 0;
3867
+ const maxCostUsd = mergedLimits?.maxCostUsd && mergedLimits.maxCostUsd > 0 ? mergedLimits.maxCostUsd : void 0;
3868
+ const costEstimator = opts.costEstimator;
3869
+ const checkBudgetBreached = () => {
3870
+ if (maxWallClockMs && Date.now() - wallClockStart > maxWallClockMs) {
3871
+ return { breached: true, reason: `maxWallClockMs (${maxWallClockMs}ms) exceeded` };
3872
+ }
3873
+ if (maxTotalOutputTokens) {
3874
+ const totals = state.usage?.totals;
3875
+ if (totals) {
3876
+ let outputSum = 0;
3877
+ for (const v of Object.values(totals)) outputSum += Number(v?.output) || 0;
3878
+ if (outputSum > maxTotalOutputTokens) {
3879
+ return { breached: true, reason: `maxTotalOutputTokens (${maxTotalOutputTokens}) exceeded` };
3880
+ }
3881
+ }
3882
+ }
3883
+ if (maxCostUsd && costEstimator) {
3884
+ const perRequest = state.usage?.perRequest;
3885
+ if (perRequest) {
3886
+ let cost = 0;
3887
+ for (const r of perRequest) {
3888
+ cost += costEstimator({
3889
+ modelName: r.modelName,
3890
+ inputTokens: Number(r.usage?.prompt_tokens) || 0,
3891
+ outputTokens: Number(r.usage?.completion_tokens) || 0,
3892
+ cachedInputTokens: Number(r.usage?.prompt_tokens_details?.cached_tokens) || 0,
3893
+ reasoningTokens: Number(r.usage?.completion_tokens_details?.reasoning_tokens) || 0
3894
+ }) || 0;
3895
+ }
3896
+ if (cost > maxCostUsd) {
3897
+ return { breached: true, reason: `maxCostUsd ($${maxCostUsd}) exceeded (actual: $${cost.toFixed(4)})` };
3898
+ }
3899
+ }
3900
+ }
3901
+ return { breached: false };
3902
+ };
3655
3903
  let iterations = 0;
3656
3904
  const onStateChange = config?.onStateChange;
3657
3905
  const checkpointReason = config?.checkpointReason;
@@ -3700,6 +3948,13 @@ function createAgent(opts) {
3700
3948
  };
3701
3949
  while (iterations < iterationLimit) {
3702
3950
  iterations++;
3951
+ const budget = checkBudgetBreached();
3952
+ if (budget.breached) {
3953
+ emit?.({ type: "metadata", limitBreached: budget.reason });
3954
+ const ctx = { ...state.ctx || {}, __limitBreached: budget.reason };
3955
+ state = { ...state, ctx };
3956
+ break;
3957
+ }
3703
3958
  const traceSession = state.ctx?.__traceSession;
3704
3959
  if (traceSession) {
3705
3960
  const iterEvent = recordTraceEvent(traceSession, {
@@ -3755,22 +4010,11 @@ function createAgent(opts) {
3755
4010
  }
3756
4011
  }
3757
4012
  if (summarizationThreshold !== void 0) {
3758
- const tokCountMessages = (state.messages || []).filter((message) => {
3759
- if (isSyntheticSummaryMessage(message)) return false;
3760
- if (message.role === "system" && (message.name === "context_summary" || message.name === "memory_context")) return false;
3761
- return true;
3762
- });
3763
- const tokenCount = countMessagesTokens(tokCountMessages);
3764
- if (tokenCount > summarizationThreshold) {
3765
- const summarizationExhausted = !!state.ctx?.__summarizationExhausted;
3766
- const hasFreshSummary = (state.messages || []).some(
3767
- (m) => m.role === "tool" && typeof m.content === "string" && (m.content === "SUMMARIZED" || m.content.startsWith("SUMMARIZED_TOOL_RESPONSE"))
3768
- );
3769
- if (hasFreshSummary && tokenCount <= summarizationThreshold * 1.15) ; else if (summarizationExhausted) ; else {
3770
- const ctx = { ...state.ctx || {}, __needsSummarization: true };
3771
- state = { ...state, ctx };
3772
- break;
3773
- }
4013
+ const verdict = shouldSignalSummarization(state, summarizationThreshold);
4014
+ if (verdict === "trigger") {
4015
+ const ctx = { ...state.ctx || {}, __needsSummarization: true };
4016
+ state = { ...state, ctx };
4017
+ break;
3774
4018
  }
3775
4019
  }
3776
4020
  onProgress?.({ stage: "agent", message: "Invoking model" });
@@ -3876,7 +4120,13 @@ function createAgent(opts) {
3876
4120
  if (state.ctx?.__finalizedDueToStructuredOutput) break;
3877
4121
  if (reflectNode && resolvedReasoning?.reflection.enabled) {
3878
4122
  const cadence = resolvedReasoning.reflection.cadence;
3879
- if (shouldRunReflection(cadence, state, true)) {
4123
+ const refl = resolvedReasoning.reflection;
4124
+ const reflectionsSoFar = state.reflections?.length ?? 0;
4125
+ const lastTurn = state.reflections?.at(-1)?.turn ?? -Infinity;
4126
+ const currentTurn = state.toolCallCount || 0;
4127
+ const budgetExceeded = typeof refl.maxPerRun === "number" && reflectionsSoFar >= refl.maxPerRun;
4128
+ const cadenceGap = currentTurn - (Number.isFinite(lastTurn) ? lastTurn : -refl.everyNTurns) < refl.everyNTurns;
4129
+ if (!budgetExceeded && !cadenceGap && shouldRunReflection(cadence, state, true)) {
3880
4130
  try {
3881
4131
  const patch = await reflectNode(state, cadence);
3882
4132
  if (patch && Object.keys(patch).length > 0) {
@@ -3936,7 +4186,7 @@ function createAgent(opts) {
3936
4186
  }
3937
4187
  const lastAfterLoop = state.messages[state.messages.length - 1];
3938
4188
  if (lastAfterLoop?.role === "tool" && !pausedStage) {
3939
- const isExpectedExit = state.ctx?.__awaitingApproval || state.ctx?.__cancelled || state.ctx?.__finalizedDueToStructuredOutput || state.ctx?.__finalizedDueToToolLimit || state.ctx?.__needsSummarization;
4189
+ const isExpectedExit = state.ctx?.__awaitingApproval || state.ctx?.__cancelled || state.ctx?.__finalizedDueToStructuredOutput || state.ctx?.__finalizedDueToToolLimit || state.ctx?.__needsSummarization || state.ctx?.__limitBreached;
3940
4190
  if (!isExpectedExit) {
3941
4191
  throw new Error(
3942
4192
  "Agent loop terminated with a pending tool response but no subsequent model invocation. This usually indicates a model provider error or exhausted iteration budget."
@@ -3949,6 +4199,11 @@ function createAgent(opts) {
3949
4199
  return state;
3950
4200
  }
3951
4201
  const invokeAgent = async (input, config) => {
4202
+ const userTokenCounter = opts.tokenCounter;
4203
+ if (userTokenCounter) {
4204
+ const { setTokenCounter: setTokenCounter2 } = await Promise.resolve().then(() => (init_utilTokens(), utilTokens_exports));
4205
+ setTokenCounter2(userTokenCounter);
4206
+ }
3952
4207
  const onEvent = config?.onEvent;
3953
4208
  const onProgress = config?.onProgress;
3954
4209
  const onStream = config?.onStream;
@@ -4006,7 +4261,19 @@ function createAgent(opts) {
4006
4261
  status: "error",
4007
4262
  error: { message: err?.message, stack: err?.stack }
4008
4263
  });
4264
+ if (userTokenCounter) {
4265
+ const { setTokenCounter: setTokenCounter2 } = await Promise.resolve().then(() => (init_utilTokens(), utilTokens_exports));
4266
+ setTokenCounter2(void 0);
4267
+ }
4009
4268
  throw err;
4269
+ } finally {
4270
+ if (userTokenCounter) {
4271
+ try {
4272
+ const { setTokenCounter: setTokenCounter2 } = await Promise.resolve().then(() => (init_utilTokens(), utilTokens_exports));
4273
+ setTokenCounter2(void 0);
4274
+ } catch {
4275
+ }
4276
+ }
4010
4277
  }
4011
4278
  await finalizeTraceSession(traceSession, {
4012
4279
  agentRuntime: res.agent || runtime,
@@ -4064,12 +4331,58 @@ function createAgent(opts) {
4064
4331
  resolveToolApproval,
4065
4332
  asTool: ({ toolName, description, inputDescription }) => {
4066
4333
  const schema = z.object({ input: z.string().describe(inputDescription || "Input for delegated agent") });
4067
- return createTool({
4334
+ const delegatedTool = createTool({
4068
4335
  name: toolName,
4069
4336
  description: description || `Delegate task to agent ${opts.name || "Agent"}`,
4070
4337
  schema,
4071
4338
  func: async ({ input }) => {
4072
- const res = await instance.invoke({ messages: [{ role: "user", content: input }] });
4339
+ const ref = delegatedTool._stateRef;
4340
+ const parentCtx = ref.ctx || {};
4341
+ const parentMessages = Array.isArray(ref.messages) ? ref.messages : [];
4342
+ const parentRuntime = ref.parentRuntime;
4343
+ const resolvedSmart = parentRuntime?.smart || opts.__resolvedSmart;
4344
+ const delegationPolicy = resolvedSmart?.delegation || {
4345
+ mode: "role_based",
4346
+ maxDelegationDepth: 2,
4347
+ maxChildCalls: 4,
4348
+ childContextPolicy: "scoped"
4349
+ };
4350
+ const currentDepth = Number(parentCtx.__delegationDepth) || 0;
4351
+ if (delegationPolicy.mode === "off") {
4352
+ return { error: "Delegation disabled by runtime policy." };
4353
+ }
4354
+ if (currentDepth >= delegationPolicy.maxDelegationDepth) {
4355
+ return {
4356
+ error: `Delegation depth limit reached (${delegationPolicy.maxDelegationDepth}). Refusing nested call.`
4357
+ };
4358
+ }
4359
+ parentCtx.__delegatedCallCount = (parentCtx.__delegatedCallCount || 0) + 1;
4360
+ if (parentCtx.__delegatedCallCount > delegationPolicy.maxChildCalls) {
4361
+ return {
4362
+ error: `Child-call budget exhausted (${delegationPolicy.maxChildCalls}).`
4363
+ };
4364
+ }
4365
+ let childMessages;
4366
+ switch (delegationPolicy.childContextPolicy) {
4367
+ case "full":
4368
+ childMessages = [...parentMessages, { role: "user", content: input }];
4369
+ break;
4370
+ case "scoped": {
4371
+ const systemPrefix = parentMessages[0]?.role === "system" ? [parentMessages[0]] : [];
4372
+ const lastUser = [...parentMessages].reverse().find((m) => m?.role === "user");
4373
+ childMessages = [
4374
+ ...systemPrefix,
4375
+ ...lastUser ? [lastUser] : [],
4376
+ { role: "user", content: input }
4377
+ ];
4378
+ break;
4379
+ }
4380
+ case "minimal":
4381
+ default:
4382
+ childMessages = [{ role: "user", content: input }];
4383
+ }
4384
+ const childCtx = { __delegationDepth: currentDepth + 1 };
4385
+ const res = await instance.invoke({ messages: childMessages, ctx: childCtx });
4073
4386
  return {
4074
4387
  content: res.content,
4075
4388
  output: res.output,
@@ -4077,6 +4390,8 @@ function createAgent(opts) {
4077
4390
  };
4078
4391
  }
4079
4392
  });
4393
+ delegatedTool._stateRef = {};
4394
+ return delegatedTool;
4080
4395
  },
4081
4396
  asHandoff: ({ toolName, description, schema }) => {
4082
4397
  const finalName = toolName || `handoff_to_${runtime.name || "agent"}`;
@@ -4216,7 +4531,11 @@ function createAgentNode(opts) {
4216
4531
  };
4217
4532
  }
4218
4533
 
4534
+ // src/nodes/contextSummarize.ts
4535
+ init_utilTokens();
4536
+
4219
4537
  // src/smart/contextPolicy.ts
4538
+ init_utilTokens();
4220
4539
  function collectRecentTurns(messages, lastTurnsToKeep) {
4221
4540
  if (lastTurnsToKeep <= 0) return [];
4222
4541
  const systemPrefix = messages.filter((message, index) => index === 0 && message.role === "system");
@@ -4519,7 +4838,7 @@ function createContextSummarizeNode(opts) {
4519
4838
  const resolved = getResolvedSmartConfig(opts, state.agent);
4520
4839
  const config = resolved.summarization;
4521
4840
  if (!config.enable) return {};
4522
- const model = opts.model;
4841
+ const model = state.agent?.model || opts.model;
4523
4842
  const messages = state.messages || [];
4524
4843
  const onEvent = state.ctx?.__onEvent;
4525
4844
  const traceSession = state.ctx?.__traceSession;
@@ -4838,6 +5157,10 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
4838
5157
  };
4839
5158
  };
4840
5159
  }
5160
+
5161
+ // src/index.ts
5162
+ init_utilTokens();
5163
+ init_utilTokens();
4841
5164
  var TOOL_RESPONSE_RECOVERY_MARKERS = [
4842
5165
  /ARCHIVED_TOOL_RESPONSE\s*\[/,
4843
5166
  /STRUCTURED_TOOL_RESPONSE\s*\[/,
@@ -5156,6 +5479,7 @@ function createContextTools(stateRef, opts) {
5156
5479
  }
5157
5480
 
5158
5481
  // src/graph/decisions.ts
5482
+ init_utilTokens();
5159
5483
  function needsSummarization(state, opts, summarizationEnabled) {
5160
5484
  if (!summarizationEnabled) {
5161
5485
  return false;
@@ -5318,12 +5642,24 @@ function createSmartAgent(opts) {
5318
5642
  toolResponses: resolved.toolResponses,
5319
5643
  useTodoList: planningEnabled
5320
5644
  };
5321
- const stateRef = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
5322
- const contextTools = createContextTools(stateRef, { planningEnabled, includeGetToolResponse: false });
5323
- const getToolResponseTool = createGetToolResponseTool(stateRef);
5324
- const toolsWithoutRecovery = [...opts.tools ?? [], ...contextTools];
5325
- const toolsWithRecovery = [...toolsWithoutRecovery, getToolResponseTool];
5326
- const base = createAgent({ ...runtimeOpts, tools: toolsWithoutRecovery });
5645
+ const userTools = opts.tools ?? [];
5646
+ const factoryStateRef = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
5647
+ const factoryContextTools = createContextTools(factoryStateRef, { planningEnabled, includeGetToolResponse: false });
5648
+ const factoryToolsWithoutRecovery = [...userTools, ...factoryContextTools];
5649
+ const soManagerFactory = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
5650
+ const modelCapabilities = opts.outputSchema ? getModelCapabilities(opts.model) : void 0;
5651
+ const responseFinalizeTool = soManagerFactory && modelCapabilities?.structuredOutput !== "native" ? soManagerFactory.getResponseTool() : void 0;
5652
+ const buildInvokeToolSet = () => {
5653
+ const ref = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
5654
+ const ctxTools = createContextTools(ref, { planningEnabled, includeGetToolResponse: false });
5655
+ const recoveryTool = createGetToolResponseTool(ref);
5656
+ const base2 = [...userTools, ...ctxTools];
5657
+ if (responseFinalizeTool) base2.push(responseFinalizeTool);
5658
+ const without = base2;
5659
+ const withRec = [...without, recoveryTool];
5660
+ return { stateRef: ref, toolsWithoutRecovery: without, toolsWithRecovery: withRec };
5661
+ };
5662
+ const base = createAgent({ ...runtimeOpts, tools: factoryToolsWithoutRecovery });
5327
5663
  base.__runtime.runtimeProfile = resolved.runtimeProfile;
5328
5664
  base.__runtime.smart = resolved;
5329
5665
  const summarizationEnabled = resolved.summarization.enable !== false;
@@ -5359,17 +5695,17 @@ function createSmartAgent(opts) {
5359
5695
  const memoryFacts = await writeSummaryFactsToMemory(memoryStore, resolved, latestSummary, Math.max(1, (state.messages || []).filter((message) => message.role === "user").length));
5360
5696
  return { ...state, memoryFacts };
5361
5697
  }
5362
- function syncPlanState(state) {
5363
- if (!Array.isArray(stateRef.todoList)) return state;
5698
+ function syncPlanStateWith(ref, state) {
5699
+ if (!Array.isArray(ref.todoList)) return state;
5364
5700
  return {
5365
5701
  ...state,
5366
5702
  plan: {
5367
- version: stateRef.planVersion || state.planVersion || 1,
5368
- steps: stateRef.todoList,
5703
+ version: ref.planVersion || state.planVersion || 1,
5704
+ steps: ref.todoList,
5369
5705
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
5370
- adherenceScore: stateRef.adherenceScore || 0
5706
+ adherenceScore: ref.adherenceScore || 0
5371
5707
  },
5372
- planVersion: stateRef.planVersion || state.planVersion || 1
5708
+ planVersion: ref.planVersion || state.planVersion || 1
5373
5709
  };
5374
5710
  }
5375
5711
  function systemMessage2() {
@@ -5381,9 +5717,9 @@ function createSmartAgent(opts) {
5381
5717
  );
5382
5718
  return { role: "system", content: sys };
5383
5719
  }
5384
- function syncRuntimeTools(currentState, currentMessages) {
5720
+ function syncRuntimeTools(currentState, currentMessages, toolSet) {
5385
5721
  const needsRecoveryTool = hasToolResponseRecoveryReference(currentMessages);
5386
- const nextTools = needsRecoveryTool ? toolsWithRecovery : toolsWithoutRecovery;
5722
+ const nextTools = needsRecoveryTool ? toolSet.toolsWithRecovery : toolSet.toolsWithoutRecovery;
5387
5723
  const currentRuntime = currentState.agent || base.__runtime;
5388
5724
  if (currentRuntime.tools === nextTools) {
5389
5725
  return currentState;
@@ -5402,7 +5738,7 @@ function createSmartAgent(opts) {
5402
5738
  const toolCalls = Array.isArray(lastAssistant.tool_calls) ? lastAssistant.tool_calls : [];
5403
5739
  return toolCalls.length === 0;
5404
5740
  }
5405
- async function trySummarize(currentState, currentRawMessages) {
5741
+ async function trySummarize(currentState, currentRawMessages, ref) {
5406
5742
  if (!summarizer) return { state: currentState, rawMessages: currentRawMessages, compressed: false };
5407
5743
  const delta = await summarizer(currentState);
5408
5744
  if (!delta || Object.keys(delta).length === 0) {
@@ -5411,28 +5747,31 @@ function createSmartAgent(opts) {
5411
5747
  }
5412
5748
  const ctx = { ...currentState.ctx || {} };
5413
5749
  delete ctx.__summarizationExhausted;
5414
- const updated = await persistLatestSummary(syncPlanState({ ...currentState, ...delta, ctx }));
5750
+ const updated = await persistLatestSummary(syncPlanStateWith(ref, { ...currentState, ...delta, ctx }));
5415
5751
  return { state: updated, rawMessages: [...updated.messages || currentRawMessages], compressed: true };
5416
5752
  }
5417
5753
  const instance = {
5418
5754
  invoke: async (input, config) => {
5755
+ const toolSet = buildInvokeToolSet();
5756
+ const stateRef = toolSet.stateRef;
5419
5757
  stateRef.toolHistory = input.toolHistory;
5420
5758
  stateRef.toolHistoryArchived = input.toolHistoryArchived;
5421
5759
  stateRef.todoList = input.plan?.steps;
5422
5760
  stateRef.planVersion = input.planVersion || input.plan?.version || 0;
5423
5761
  stateRef.adherenceScore = input.plan?.adherenceScore || 0;
5762
+ const syncPlanFromRef = (state2) => syncPlanStateWith(stateRef, state2);
5424
5763
  const alreadyHasSystem = Array.isArray(input.messages) && input.messages[0]?.role === "system";
5425
5764
  const seedMessages = alreadyHasSystem ? [...input.messages || []] : [systemMessage2(), ...input.messages || []];
5426
- let state = syncRuntimeTools(await syncMemory({ ...input, messages: seedMessages }), seedMessages);
5765
+ let state = syncRuntimeTools(await syncMemory({ ...input, messages: seedMessages }), seedMessages, toolSet);
5427
5766
  let lastResult = null;
5428
5767
  let rawMessages = [...seedMessages];
5429
5768
  const effectiveMaxToolCalls = config?.limits?.maxToolCalls ?? resolved.limits.maxToolCalls ?? 10;
5430
5769
  const iterationLimit = Math.max(effectiveMaxToolCalls * 3 + 5, 30);
5431
5770
  for (let i = 0; i < iterationLimit; i++) {
5432
- state = syncRuntimeTools({ ...state, messages: rawMessages }, rawMessages);
5771
+ state = syncRuntimeTools({ ...state, messages: rawMessages }, rawMessages, toolSet);
5433
5772
  const next = summarizationEnabled ? decideBefore(state) : "agent";
5434
5773
  if (next === "contextSummarize" && summarizer) {
5435
- const result = await trySummarize(state, rawMessages);
5774
+ const result = await trySummarize(state, rawMessages, stateRef);
5436
5775
  state = result.state;
5437
5776
  rawMessages = result.rawMessages;
5438
5777
  if (result.compressed) continue;
@@ -5456,14 +5795,14 @@ function createSmartAgent(opts) {
5456
5795
  if (currentPlan && !state.plan) {
5457
5796
  state = { ...state, plan: currentPlan };
5458
5797
  }
5459
- state = syncRuntimeTools(syncPlanState(await syncMemory(state)), rawMessages);
5798
+ state = syncRuntimeTools(syncPlanFromRef(await syncMemory(state)), rawMessages, toolSet);
5460
5799
  stateRef.toolHistory = state.toolHistory;
5461
5800
  stateRef.toolHistoryArchived = state.toolHistoryArchived;
5462
5801
  if (state.ctx?.__needsSummarization && summarizer) {
5463
5802
  const ctx = { ...state.ctx || {} };
5464
5803
  delete ctx.__needsSummarization;
5465
5804
  state = { ...state, ctx };
5466
- const result = await trySummarize(state, rawMessages);
5805
+ const result = await trySummarize(state, rawMessages, stateRef);
5467
5806
  state = result.state;
5468
5807
  rawMessages = result.rawMessages;
5469
5808
  continue;
@@ -5474,7 +5813,7 @@ function createSmartAgent(opts) {
5474
5813
  if (summarizationEnabled) {
5475
5814
  const after = decideAfter(state);
5476
5815
  if (after === "contextSummarize" && summarizer) {
5477
- const result = await trySummarize(state, rawMessages);
5816
+ const result = await trySummarize(state, rawMessages, stateRef);
5478
5817
  state = result.state;
5479
5818
  rawMessages = result.rawMessages;
5480
5819
  if (result.compressed) continue;
@@ -5913,7 +6252,50 @@ var ProviderError = class extends Error {
5913
6252
  };
5914
6253
 
5915
6254
  // src/providers/base.ts
6255
+ var DEFAULT_RETRY = {
6256
+ maxRetries: 3,
6257
+ baseDelayMs: 500,
6258
+ maxDelayMs: 8e3
6259
+ };
5916
6260
  var BaseProvider = class {
6261
+ constructor() {
6262
+ this.retryPolicy = DEFAULT_RETRY;
6263
+ }
6264
+ /**
6265
+ * Helper concrete providers can wrap their fetch with. Retries 429/5xx
6266
+ * with exponential backoff and respects Retry-After if the response
6267
+ * carries one.
6268
+ */
6269
+ async doFetchWithRetry(execute) {
6270
+ const policy = this.retryPolicy;
6271
+ const maxRetries = Math.max(0, policy.maxRetries ?? DEFAULT_RETRY.maxRetries);
6272
+ const baseDelay = Math.max(0, policy.baseDelayMs ?? DEFAULT_RETRY.baseDelayMs);
6273
+ const maxDelay = Math.max(baseDelay, policy.maxDelayMs ?? DEFAULT_RETRY.maxDelayMs);
6274
+ let lastResp;
6275
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
6276
+ const res = await execute();
6277
+ const isRetryable = res.status === 429 || res.status >= 500 && res.status < 600;
6278
+ if (!isRetryable) return res;
6279
+ const predicate = policy.shouldRetry;
6280
+ if (predicate && !predicate({ status: res.status }, attempt)) return res;
6281
+ if (attempt === maxRetries) {
6282
+ lastResp = res;
6283
+ break;
6284
+ }
6285
+ const retryAfter = res.headers.get("retry-after");
6286
+ let delay = Math.min(maxDelay, baseDelay * Math.pow(2, attempt));
6287
+ if (retryAfter) {
6288
+ const asNumber = Number(retryAfter);
6289
+ if (!Number.isNaN(asNumber)) delay = Math.min(maxDelay, Math.max(delay, asNumber * 1e3));
6290
+ }
6291
+ try {
6292
+ await res.text();
6293
+ } catch {
6294
+ }
6295
+ await new Promise((r) => setTimeout(r, delay));
6296
+ }
6297
+ return lastResp;
6298
+ }
5917
6299
  };
5918
6300
 
5919
6301
  // src/providers/utils/sse.ts
@@ -6040,6 +6422,7 @@ var OpenAIProvider = class extends BaseProvider {
6040
6422
  ...config.defaultHeaders,
6041
6423
  ...config.organization ? { "OpenAI-Organization": config.organization } : {}
6042
6424
  };
6425
+ if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
6043
6426
  }
6044
6427
  async complete(request) {
6045
6428
  const body = this.buildRequestBody(request, false);
@@ -6119,7 +6502,7 @@ var OpenAIProvider = class extends BaseProvider {
6119
6502
  if (request.tools?.length) {
6120
6503
  body.tools = request.tools.map((t) => this.toOpenAITool(t));
6121
6504
  }
6122
- if (request.toolChoice != null) {
6505
+ if (request.toolChoice != null && request.tools?.length) {
6123
6506
  if (typeof request.toolChoice === "string") {
6124
6507
  body.tool_choice = request.toolChoice;
6125
6508
  } else {
@@ -6187,15 +6570,13 @@ var OpenAIProvider = class extends BaseProvider {
6187
6570
  }
6188
6571
  async doFetch(body) {
6189
6572
  const url = `${this.baseURL}/chat/completions`;
6190
- const res = await fetch(url, {
6191
- method: "POST",
6192
- headers: {
6193
- "Content-Type": "application/json",
6194
- Authorization: `Bearer ${this.apiKey}`,
6195
- ...this.defaultHeaders
6196
- },
6197
- body: JSON.stringify(body)
6198
- });
6573
+ const headers = {
6574
+ "Content-Type": "application/json",
6575
+ Authorization: `Bearer ${this.apiKey}`,
6576
+ ...this.defaultHeaders
6577
+ };
6578
+ const payload = JSON.stringify(body);
6579
+ const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload }));
6199
6580
  if (!res.ok) {
6200
6581
  const text = await res.text().catch(() => "");
6201
6582
  throw new ProviderError(
@@ -6265,6 +6646,8 @@ var AnthropicProvider = class extends BaseProvider {
6265
6646
  this.defaultModel = config.defaultModel ?? "claude-sonnet-4-20250514";
6266
6647
  this.defaultHeaders = config.defaultHeaders ?? {};
6267
6648
  this.anthropicVersion = config.anthropicVersion ?? "2023-06-01";
6649
+ this.promptCachingEnabled = config.prompt_caching?.enabled === true;
6650
+ if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
6268
6651
  }
6269
6652
  async complete(request) {
6270
6653
  const body = this.buildRequestBody(request, false);
@@ -6383,12 +6766,22 @@ var AnthropicProvider = class extends BaseProvider {
6383
6766
  max_tokens: request.maxTokens ?? 4096,
6384
6767
  stream
6385
6768
  };
6386
- if (system) body.system = system;
6769
+ if (system) {
6770
+ if (this.promptCachingEnabled) {
6771
+ body.system = [{ type: "text", text: system, cache_control: { type: "ephemeral" } }];
6772
+ } else {
6773
+ body.system = system;
6774
+ }
6775
+ }
6387
6776
  if (request.temperature != null) body.temperature = request.temperature;
6388
6777
  if (request.topP != null) body.top_p = request.topP;
6389
6778
  if (request.stop) body.stop_sequences = request.stop;
6390
6779
  if (request.tools?.length) {
6391
- body.tools = request.tools.map((t) => this.toAnthropicTool(t));
6780
+ const tools = request.tools.map((t) => this.toAnthropicTool(t));
6781
+ if (this.promptCachingEnabled && tools.length > 0) {
6782
+ tools[tools.length - 1] = { ...tools[tools.length - 1], cache_control: { type: "ephemeral" } };
6783
+ }
6784
+ body.tools = tools;
6392
6785
  }
6393
6786
  if (request.toolChoice != null) {
6394
6787
  if (request.toolChoice === "auto") {
@@ -6487,16 +6880,14 @@ var AnthropicProvider = class extends BaseProvider {
6487
6880
  }
6488
6881
  async doFetch(body) {
6489
6882
  const url = `${this.baseURL}/v1/messages`;
6490
- const res = await fetch(url, {
6491
- method: "POST",
6492
- headers: {
6493
- "Content-Type": "application/json",
6494
- "x-api-key": this.apiKey,
6495
- "anthropic-version": this.anthropicVersion,
6496
- ...this.defaultHeaders
6497
- },
6498
- body: JSON.stringify(body)
6499
- });
6883
+ const headers = {
6884
+ "Content-Type": "application/json",
6885
+ "x-api-key": this.apiKey,
6886
+ "anthropic-version": this.anthropicVersion,
6887
+ ...this.defaultHeaders
6888
+ };
6889
+ const payload = JSON.stringify(body);
6890
+ const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload }));
6500
6891
  if (!res.ok) {
6501
6892
  const text = await res.text().catch(() => "");
6502
6893
  throw new ProviderError(
@@ -6708,6 +7099,8 @@ var BedrockProvider = class extends BaseProvider {
6708
7099
  this.region = config.region;
6709
7100
  this.defaultModel = config.defaultModel ?? "anthropic.claude-sonnet-4-20250514-v1:0";
6710
7101
  this.defaultHeaders = config.defaultHeaders ?? {};
7102
+ this.promptCachingEnabled = config.prompt_caching?.enabled === true;
7103
+ if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
6711
7104
  this.credentials = {
6712
7105
  accessKeyId: config.accessKeyId ?? process.env.AWS_ACCESS_KEY_ID ?? "",
6713
7106
  secretAccessKey: config.secretAccessKey ?? process.env.AWS_SECRET_ACCESS_KEY ?? "",
@@ -6746,7 +7139,9 @@ var BedrockProvider = class extends BaseProvider {
6746
7139
  messages: messages.map((m) => this.toBedrockMessage(m))
6747
7140
  };
6748
7141
  if (system.length > 0) {
6749
- body.system = system.map((s) => ({ text: s }));
7142
+ const systemBlocks = system.map((s) => ({ text: s }));
7143
+ if (this.promptCachingEnabled) systemBlocks.push({ cachePoint: { type: "default" } });
7144
+ body.system = systemBlocks;
6750
7145
  }
6751
7146
  const inferenceConfig = {};
6752
7147
  if (request.maxTokens != null) inferenceConfig.maxTokens = request.maxTokens;
@@ -6755,8 +7150,12 @@ var BedrockProvider = class extends BaseProvider {
6755
7150
  if (request.stop) inferenceConfig.stopSequences = request.stop;
6756
7151
  if (Object.keys(inferenceConfig).length > 0) body.inferenceConfig = inferenceConfig;
6757
7152
  if (request.tools?.length) {
7153
+ const tools = request.tools.map((t) => this.toBedrockTool(t));
7154
+ if (this.promptCachingEnabled && tools.length > 0) {
7155
+ tools.push({ cachePoint: { type: "default" } });
7156
+ }
6758
7157
  body.toolConfig = {
6759
- tools: request.tools.map((t) => this.toBedrockTool(t))
7158
+ tools
6760
7159
  };
6761
7160
  if (request.toolChoice != null) {
6762
7161
  if (request.toolChoice === "auto") {
@@ -6853,23 +7252,21 @@ var BedrockProvider = class extends BaseProvider {
6853
7252
  const host = `bedrock-runtime.${this.region}.amazonaws.com`;
6854
7253
  const url = `https://${host}/model/${encodeURIComponent(modelId)}/converse`;
6855
7254
  const bodyStr = JSON.stringify(body);
6856
- const headers = signRequest({
6857
- method: "POST",
6858
- url,
6859
- headers: {
6860
- "Content-Type": "application/json",
6861
- Accept: "application/json",
6862
- ...this.defaultHeaders
6863
- },
6864
- body: bodyStr,
6865
- region: this.region,
6866
- service: "bedrock",
6867
- credentials: this.credentials
6868
- });
6869
- const res = await fetch(url, {
6870
- method: "POST",
6871
- headers,
6872
- body: bodyStr
7255
+ const res = await this.doFetchWithRetry(() => {
7256
+ const headers = signRequest({
7257
+ method: "POST",
7258
+ url,
7259
+ headers: {
7260
+ "Content-Type": "application/json",
7261
+ Accept: "application/json",
7262
+ ...this.defaultHeaders
7263
+ },
7264
+ body: bodyStr,
7265
+ region: this.region,
7266
+ service: "bedrock",
7267
+ credentials: this.credentials
7268
+ });
7269
+ return fetch(url, { method: "POST", headers, body: bodyStr });
6873
7270
  });
6874
7271
  if (!res.ok) {
6875
7272
  const text = await res.text().catch(() => "");
@@ -7400,7 +7797,7 @@ function fromNativeProvider(provider, options) {
7400
7797
  const reasoningCfg = reasoningOverride ?? opts?.reasoning;
7401
7798
  if (reasoningCfg) req.reasoning = reasoningCfg;
7402
7799
  const tc = invokeOptions?.tool_choice ?? invokeOptions?.toolChoice;
7403
- if (tc) {
7800
+ if (tc && tools?.length) {
7404
7801
  req.toolChoice = tc;
7405
7802
  }
7406
7803
  const rf = invokeOptions?.response_format;
@@ -7744,6 +8141,6 @@ function createProvider(config) {
7744
8141
  }
7745
8142
  }
7746
8143
 
7747
- export { AnthropicProvider, AzureProvider, BaseProvider, BedrockProvider, DEFAULT_PROFILE_CONFIGS, GuardrailPhase, InMemoryMemoryStore, NativeJsonSchemaStrategy, OpenAICompatibleProvider, OpenAIProvider, ProviderError, StructuredOutputManager, ToolBasedStrategy, VertexProvider, agentVerdictRule, buildSystemPrompt, captureSnapshot, codePresenceRule, cognipeerSink, countApproxTokens, countMessagesTokens, createAgent, createAgentNode, createCodeGuardrail, createContextSummarizeNode, createContextTools, createGetToolResponseTool, createGuardrail, createJsonGuardrail, createManageTodoTool, createProvider, createRegexGuardrail, createResolverNode, createSmartAgent, createTool, createToolLimitFinalizeNode, createToolsNode, customCallbackRule, customSink, emptyUsage, evaluateGuardrails, fileSink, formatZodError, fromLangchainModel, fromLangchainTools, fromNativeProvider, generateSpanId, generateTraceId, getModelCapabilities, getNestedValue, getResolvedSmartConfig, hasToolResponseRecoveryReference, httpSink, isSmartModel, jsonSchemaRule, normalizeSmartAgentOptions, otlpSink, readMemoryFacts, regexRule, resolveMemoryStore, resolveStrategy, resolveToolApprovalState, restoreSnapshot, runSmartAgentEvalHarness, startStreamingSession, traceSessionToOtlp, withTools, writeSummaryFactsToMemory };
8144
+ export { AnthropicProvider, AzureProvider, BaseProvider, BedrockProvider, DEFAULT_PROFILE_CONFIGS, GuardrailPhase, InMemoryMemoryStore, NativeJsonSchemaStrategy, OpenAICompatibleProvider, OpenAIProvider, ProviderError, StructuredOutputManager, ToolBasedStrategy, VertexProvider, agentVerdictRule, buildSystemPrompt, captureSnapshot, codePresenceRule, cognipeerSink, countApproxTokens, countMessagesTokens, createAgent, createAgentNode, createCodeGuardrail, createContextSummarizeNode, createContextTools, createGetToolResponseTool, createGuardrail, createJsonGuardrail, createManageTodoTool, createProvider, createRegexGuardrail, createResolverNode, createSmartAgent, createTool, createToolLimitFinalizeNode, createToolsNode, customCallbackRule, customSink, defaultTokenCounter, emptyUsage, evaluateGuardrails, fileSink, formatZodError, fromLangchainModel, fromLangchainTools, fromNativeProvider, generateSpanId, generateTraceId, getModelCapabilities, getNestedValue, getResolvedSmartConfig, getTokenCounter, hasToolResponseRecoveryReference, httpSink, isSmartModel, jsonSchemaRule, normalizeSmartAgentOptions, otlpSink, readMemoryFacts, regexRule, resolveMemoryStore, resolveStrategy, resolveToolApprovalState, restoreSnapshot, runSmartAgentEvalHarness, setTokenCounter, startStreamingSession, traceSessionToOtlp, withTools, writeSummaryFactsToMemory };
7748
8145
  //# sourceMappingURL=index.mjs.map
7749
8146
  //# sourceMappingURL=index.mjs.map