@cognipeer/agent-sdk 0.6.4 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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) {
@@ -2289,66 +2393,8 @@ function getResolvedSmartConfig(opts, runtime) {
2289
2393
  return runtime?.smart || normalizeSmartAgentOptions(opts);
2290
2394
  }
2291
2395
 
2292
- // src/utils/utilTokens.ts
2293
- function countApproxTokens(text) {
2294
- if (!text) return 0;
2295
- const nonAsciiCount = (text.match(/[^\x00-\x7F]/g) || []).length;
2296
- const asciiCount = text.length - nonAsciiCount;
2297
- return Math.ceil(asciiCount / 4 + nonAsciiCount / 1.5);
2298
- }
2299
- function extractMessageText(m) {
2300
- if (!m) return "";
2301
- const parts = [];
2302
- parts.push(m.role || "");
2303
- if (m.name) parts.push(m.name);
2304
- if (typeof m.content === "string") {
2305
- parts.push(m.content);
2306
- } else if (Array.isArray(m.content)) {
2307
- for (const c of m.content) {
2308
- if (typeof c === "string") parts.push(c);
2309
- else if (c?.text) parts.push(c.text);
2310
- else if (c?.content) parts.push(c.content);
2311
- else if (c?.type === "tool_use" && c?.input) {
2312
- parts.push(c.name || "");
2313
- parts.push(typeof c.input === "string" ? c.input : JSON.stringify(c.input));
2314
- } else if (c != null && typeof c === "object") {
2315
- parts.push(JSON.stringify(c));
2316
- }
2317
- }
2318
- } else if (m.content != null && typeof m.content === "object") {
2319
- parts.push(JSON.stringify(m.content));
2320
- }
2321
- if (Array.isArray(m.tool_calls)) {
2322
- for (const tc of m.tool_calls) {
2323
- if (tc?.function?.name) parts.push(tc.function.name);
2324
- if (tc?.function?.arguments) {
2325
- parts.push(
2326
- typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
2327
- );
2328
- }
2329
- if (tc?.name && !tc?.function?.name) parts.push(tc.name);
2330
- if (tc?.args && !tc?.function?.arguments) {
2331
- parts.push(typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args));
2332
- }
2333
- }
2334
- }
2335
- if (Array.isArray(m.additional_kwargs?.tool_calls)) {
2336
- for (const tc of m.additional_kwargs.tool_calls) {
2337
- if (tc?.function?.name) parts.push(tc.function.name);
2338
- if (tc?.function?.arguments) {
2339
- parts.push(
2340
- typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
2341
- );
2342
- }
2343
- }
2344
- }
2345
- return parts.join("\n");
2346
- }
2347
- function countMessagesTokens(messages) {
2348
- if (!Array.isArray(messages) || messages.length === 0) return 0;
2349
- const allText = messages.map(extractMessageText).join("\n");
2350
- return countApproxTokens(allText) + messages.length * 4;
2351
- }
2396
+ // src/smart/toolResponses.ts
2397
+ init_utilTokens();
2352
2398
 
2353
2399
  // src/utils/content.ts
2354
2400
  function contentToString(content) {
@@ -2548,6 +2594,7 @@ function createToolsNode(initialTools, opts) {
2548
2594
  anyTool._stateRef.messages = state.messages;
2549
2595
  anyTool._stateRef.ctx = state.ctx || (state.ctx = {});
2550
2596
  anyTool._stateRef.__onEvent = onEvent;
2597
+ anyTool._stateRef.parentRuntime = runtime;
2551
2598
  }
2552
2599
  }
2553
2600
  const last = state.messages[state.messages.length - 1];
@@ -2558,6 +2605,7 @@ function createToolsNode(initialTools, opts) {
2558
2605
  const remaining = Math.max(0, limits.maxToolCalls - toolCount);
2559
2606
  const planned = toolCalls.slice(0, remaining);
2560
2607
  const skipped = toolCalls.slice(remaining);
2608
+ const orderedSlots = planned.map(() => []);
2561
2609
  const isCancelled = () => {
2562
2610
  if (abortSignal?.aborted) return { cancelled: true, reason: "aborted" };
2563
2611
  if (cancellationToken && cancellationToken.isCancellationRequested) return { cancelled: true, reason: "cancelled" };
@@ -2574,7 +2622,9 @@ function createToolsNode(initialTools, opts) {
2574
2622
  }
2575
2623
  state.ctx = ctx;
2576
2624
  };
2577
- const runOne = async (tc) => {
2625
+ const runOne = async (tc, slot = appended) => {
2626
+ var _a;
2627
+ const push = (msg) => slot.push(msg);
2578
2628
  const cancelState = isCancelled();
2579
2629
  if (cancelState.cancelled) {
2580
2630
  const ctx = state.ctx = state.ctx || {};
@@ -2585,7 +2635,7 @@ function createToolsNode(initialTools, opts) {
2585
2635
  }
2586
2636
  const tool = toolByName.get(tc.name);
2587
2637
  if (!tool) {
2588
- 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 });
2589
2639
  onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args: tc.args, error: { message: "Tool not found" } });
2590
2640
  toolCount += 1;
2591
2641
  return { status: "error" };
@@ -2601,7 +2651,7 @@ function createToolsNode(initialTools, opts) {
2601
2651
  const validatedArgs = validateToolArgs(tool, args);
2602
2652
  if (!validatedArgs.ok && resolved.toolResponses.schemaValidation === "strict") {
2603
2653
  const errorMessage = `Tool argument validation failed for ${tc.name}: ${validatedArgs.message}`;
2604
- 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 });
2605
2655
  markToolFailure(errorMessage, tc.name, toolCallId);
2606
2656
  onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args, error: { message: errorMessage } });
2607
2657
  toolCount += 1;
@@ -2623,7 +2673,7 @@ function createToolsNode(initialTools, opts) {
2623
2673
  const currentExecutions = countSuccessfulToolExecutions(toolHistory, toolName);
2624
2674
  if (currentExecutions >= maxExecutionsPerRun) {
2625
2675
  const limitMessage = `Skipped tool due to per-tool execution limit: ${toolName} (${maxExecutionsPerRun}/run)`;
2626
- 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 });
2627
2677
  onEvent?.({ type: "tool_call", phase: "skipped", name: toolName, id: tc.id, args });
2628
2678
  recordTraceEvent(traceSession, {
2629
2679
  type: "tool_call",
@@ -2683,7 +2733,7 @@ function createToolsNode(initialTools, opts) {
2683
2733
  }
2684
2734
  if (approvalEntry.status === "rejected") {
2685
2735
  const rejectionMessage = approvalEntry.comment || "Tool call rejected by reviewer.";
2686
- 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 });
2687
2737
  approvalEntry.metadata = { ...approvalEntry.metadata || {}, resolution: "rejected" };
2688
2738
  approvalEntry.status = "executed";
2689
2739
  approvalEntry.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -2701,23 +2751,102 @@ function createToolsNode(initialTools, opts) {
2701
2751
  const start = Date.now();
2702
2752
  const sanitizedArgs = sanitizeTracePayload(args);
2703
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
+ }
2704
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
+ }
2705
2811
  onEvent?.({ type: "tool_call", phase: "start", name: toolName, id: tc.id, args });
2706
2812
  onProgress?.({ stage: "tools", message: `Running tool ${tc.name}` });
2707
2813
  const anyTool = tool;
2708
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
+ };
2709
2823
  let output;
2710
- if (typeof anyTool.func === "function") output = await anyTool.func(args, callOptions);
2711
- else if (typeof anyTool.invoke === "function") output = await anyTool.invoke(args, callOptions);
2712
- else if (typeof anyTool.call === "function") output = await anyTool.call(args, callOptions);
2713
- else if (typeof anyTool._call === "function") output = await anyTool._call(args, callOptions);
2714
- else if (typeof anyTool.run === "function") output = await anyTool.run(args, callOptions);
2715
- 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
+ }
2716
2845
  const durationMs = Date.now() - start;
2717
2846
  const executionId = nanoid();
2718
2847
  if (output && typeof output === "object" && output.__handoff && output.__handoff.runtime) {
2719
2848
  toolHistory.push({ executionId, toolName, args, output: "handoff:ok", rawOutput: output, timestamp: (/* @__PURE__ */ new Date()).toISOString(), tool_call_id: tc.id, status: "handoff" });
2720
- 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 });
2721
2850
  state.agent = output.__handoff.runtime;
2722
2851
  onEvent?.({ type: "handoff", from: runtime.name, to: state.agent?.name, toolName });
2723
2852
  onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: "handoff", durationMs });
@@ -2769,7 +2898,7 @@ function createToolsNode(initialTools, opts) {
2769
2898
  archiveId: void 0,
2770
2899
  status: "success"
2771
2900
  });
2772
- 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 });
2773
2902
  onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: output, durationMs });
2774
2903
  onProgress?.({ stage: "tools", message: `Tool ${tc.name} completed`, detail: { durationMs } });
2775
2904
  if (needsApproval && approvalEntry) {
@@ -2825,7 +2954,7 @@ function createToolsNode(initialTools, opts) {
2825
2954
  const executionId = nanoid();
2826
2955
  const message = error?.message || String(error);
2827
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" });
2828
- 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 });
2829
2958
  markToolFailure(message, toolName, toolCallId);
2830
2959
  onEvent?.({ type: "tool_call", phase: "error", name: toolName, id: tc.id, args, error: { message } });
2831
2960
  onProgress?.({ stage: "tools", message: `Tool ${tc.name} failed`, detail: { error: message } });
@@ -2862,22 +2991,67 @@ function createToolsNode(initialTools, opts) {
2862
2991
  return { status: "error", approval: approvalEntry };
2863
2992
  }
2864
2993
  };
2865
- for (const tc of planned) {
2866
- 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 = () => {
2867
3003
  const cancelState = isCancelled();
2868
3004
  if (cancelState.cancelled) {
2869
3005
  const ctx = state.ctx = state.ctx || {};
2870
3006
  ctx.__cancelled = { stage: "tools", reason: cancelState.reason, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
2871
3007
  onEvent?.({ type: "cancelled", stage: "tools", reason: cancelState.reason });
2872
3008
  onProgress?.({ stage: "tools", message: "Cancelled", detail: { reason: cancelState.reason } });
2873
- break;
3009
+ shouldStop = true;
3010
+ return true;
2874
3011
  }
2875
- 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]);
2876
3018
  if (result.status === "awaiting_approval") {
2877
3019
  awaitingApproval = true;
2878
3020
  break;
2879
3021
  }
2880
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
+ }
2881
3055
  for (const tc of skipped) {
2882
3056
  onEvent?.({ type: "tool_call", phase: "skipped", name: tc.name, id: tc.id, args: tc.args });
2883
3057
  const sanitizedArgs = sanitizeTracePayload(tc.args);
@@ -2913,11 +3087,23 @@ function createToolsNode(initialTools, opts) {
2913
3087
  }
2914
3088
  state.ctx = Object.keys(ctx).length > 0 ? ctx : void 0;
2915
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
+ }
2916
3096
  return {
2917
3097
  messages: [...state.messages, ...appended],
2918
3098
  toolCallCount: toolCount,
2919
3099
  toolHistory,
2920
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,
2921
3107
  agent: state.agent,
2922
3108
  pendingApprovals
2923
3109
  };
@@ -3154,7 +3340,8 @@ function resolveReflection(input, preset) {
3154
3340
  maxChars: preset.maxChars,
3155
3341
  keepLast: 3,
3156
3342
  summarize: false,
3157
- emitEvents: true
3343
+ emitEvents: true,
3344
+ everyNTurns: 1
3158
3345
  };
3159
3346
  }
3160
3347
  const cfg = input ?? {};
@@ -3169,7 +3356,9 @@ function resolveReflection(input, preset) {
3169
3356
  keepLast: cfg.keepLast ?? 3,
3170
3357
  summarize: cfg.summarize ?? false,
3171
3358
  promptTemplate: cfg.promptTemplate,
3172
- 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))
3173
3362
  };
3174
3363
  }
3175
3364
  function ensureArray(value) {
@@ -3382,6 +3571,9 @@ function resolveToolApprovalState(state, resolution) {
3382
3571
  };
3383
3572
  }
3384
3573
 
3574
+ // src/agent.ts
3575
+ init_utilTokens();
3576
+
3385
3577
  // src/utils/syntheticMessages.ts
3386
3578
  function isSyntheticSummaryMessage(message) {
3387
3579
  if (!message) return false;
@@ -3560,6 +3752,21 @@ function getActiveSummarizationThreshold(opts) {
3560
3752
  }
3561
3753
  return summarization.summaryTriggerTokens || summarization.maxTokens || opts.limits?.maxContextTokens || 5e4;
3562
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
+ }
3563
3770
  function clearNeedsSummarization(state) {
3564
3771
  if (!state.ctx?.__needsSummarization) {
3565
3772
  return state;
@@ -3654,6 +3861,45 @@ function createAgent(opts) {
3654
3861
  };
3655
3862
  const maxToolCalls = mergedLimits?.maxToolCalls === void 0 ? 50 : mergedLimits?.maxToolCalls;
3656
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
+ };
3657
3903
  let iterations = 0;
3658
3904
  const onStateChange = config?.onStateChange;
3659
3905
  const checkpointReason = config?.checkpointReason;
@@ -3702,6 +3948,13 @@ function createAgent(opts) {
3702
3948
  };
3703
3949
  while (iterations < iterationLimit) {
3704
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
+ }
3705
3958
  const traceSession = state.ctx?.__traceSession;
3706
3959
  if (traceSession) {
3707
3960
  const iterEvent = recordTraceEvent(traceSession, {
@@ -3757,22 +4010,11 @@ function createAgent(opts) {
3757
4010
  }
3758
4011
  }
3759
4012
  if (summarizationThreshold !== void 0) {
3760
- const tokCountMessages = (state.messages || []).filter((message) => {
3761
- if (isSyntheticSummaryMessage(message)) return false;
3762
- if (message.role === "system" && (message.name === "context_summary" || message.name === "memory_context")) return false;
3763
- return true;
3764
- });
3765
- const tokenCount = countMessagesTokens(tokCountMessages);
3766
- if (tokenCount > summarizationThreshold) {
3767
- const summarizationExhausted = !!state.ctx?.__summarizationExhausted;
3768
- const hasFreshSummary = (state.messages || []).some(
3769
- (m) => m.role === "tool" && typeof m.content === "string" && (m.content === "SUMMARIZED" || m.content.startsWith("SUMMARIZED_TOOL_RESPONSE"))
3770
- );
3771
- if (hasFreshSummary && tokenCount <= summarizationThreshold * 1.15) ; else if (summarizationExhausted) ; else {
3772
- const ctx = { ...state.ctx || {}, __needsSummarization: true };
3773
- state = { ...state, ctx };
3774
- break;
3775
- }
4013
+ const verdict = shouldSignalSummarization(state, summarizationThreshold);
4014
+ if (verdict === "trigger") {
4015
+ const ctx = { ...state.ctx || {}, __needsSummarization: true };
4016
+ state = { ...state, ctx };
4017
+ break;
3776
4018
  }
3777
4019
  }
3778
4020
  onProgress?.({ stage: "agent", message: "Invoking model" });
@@ -3878,7 +4120,13 @@ function createAgent(opts) {
3878
4120
  if (state.ctx?.__finalizedDueToStructuredOutput) break;
3879
4121
  if (reflectNode && resolvedReasoning?.reflection.enabled) {
3880
4122
  const cadence = resolvedReasoning.reflection.cadence;
3881
- 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)) {
3882
4130
  try {
3883
4131
  const patch = await reflectNode(state, cadence);
3884
4132
  if (patch && Object.keys(patch).length > 0) {
@@ -3938,7 +4186,7 @@ function createAgent(opts) {
3938
4186
  }
3939
4187
  const lastAfterLoop = state.messages[state.messages.length - 1];
3940
4188
  if (lastAfterLoop?.role === "tool" && !pausedStage) {
3941
- 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;
3942
4190
  if (!isExpectedExit) {
3943
4191
  throw new Error(
3944
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."
@@ -3951,6 +4199,11 @@ function createAgent(opts) {
3951
4199
  return state;
3952
4200
  }
3953
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
+ }
3954
4207
  const onEvent = config?.onEvent;
3955
4208
  const onProgress = config?.onProgress;
3956
4209
  const onStream = config?.onStream;
@@ -4008,7 +4261,19 @@ function createAgent(opts) {
4008
4261
  status: "error",
4009
4262
  error: { message: err?.message, stack: err?.stack }
4010
4263
  });
4264
+ if (userTokenCounter) {
4265
+ const { setTokenCounter: setTokenCounter2 } = await Promise.resolve().then(() => (init_utilTokens(), utilTokens_exports));
4266
+ setTokenCounter2(void 0);
4267
+ }
4011
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
+ }
4012
4277
  }
4013
4278
  await finalizeTraceSession(traceSession, {
4014
4279
  agentRuntime: res.agent || runtime,
@@ -4066,12 +4331,58 @@ function createAgent(opts) {
4066
4331
  resolveToolApproval,
4067
4332
  asTool: ({ toolName, description, inputDescription }) => {
4068
4333
  const schema = z.object({ input: z.string().describe(inputDescription || "Input for delegated agent") });
4069
- return createTool({
4334
+ const delegatedTool = createTool({
4070
4335
  name: toolName,
4071
4336
  description: description || `Delegate task to agent ${opts.name || "Agent"}`,
4072
4337
  schema,
4073
4338
  func: async ({ input }) => {
4074
- 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 });
4075
4386
  return {
4076
4387
  content: res.content,
4077
4388
  output: res.output,
@@ -4079,6 +4390,8 @@ function createAgent(opts) {
4079
4390
  };
4080
4391
  }
4081
4392
  });
4393
+ delegatedTool._stateRef = {};
4394
+ return delegatedTool;
4082
4395
  },
4083
4396
  asHandoff: ({ toolName, description, schema }) => {
4084
4397
  const finalName = toolName || `handoff_to_${runtime.name || "agent"}`;
@@ -4218,7 +4531,11 @@ function createAgentNode(opts) {
4218
4531
  };
4219
4532
  }
4220
4533
 
4534
+ // src/nodes/contextSummarize.ts
4535
+ init_utilTokens();
4536
+
4221
4537
  // src/smart/contextPolicy.ts
4538
+ init_utilTokens();
4222
4539
  function collectRecentTurns(messages, lastTurnsToKeep) {
4223
4540
  if (lastTurnsToKeep <= 0) return [];
4224
4541
  const systemPrefix = messages.filter((message, index) => index === 0 && message.role === "system");
@@ -4521,7 +4838,7 @@ function createContextSummarizeNode(opts) {
4521
4838
  const resolved = getResolvedSmartConfig(opts, state.agent);
4522
4839
  const config = resolved.summarization;
4523
4840
  if (!config.enable) return {};
4524
- const model = opts.model;
4841
+ const model = state.agent?.model || opts.model;
4525
4842
  const messages = state.messages || [];
4526
4843
  const onEvent = state.ctx?.__onEvent;
4527
4844
  const traceSession = state.ctx?.__traceSession;
@@ -4840,6 +5157,10 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
4840
5157
  };
4841
5158
  };
4842
5159
  }
5160
+
5161
+ // src/index.ts
5162
+ init_utilTokens();
5163
+ init_utilTokens();
4843
5164
  var TOOL_RESPONSE_RECOVERY_MARKERS = [
4844
5165
  /ARCHIVED_TOOL_RESPONSE\s*\[/,
4845
5166
  /STRUCTURED_TOOL_RESPONSE\s*\[/,
@@ -5158,6 +5479,7 @@ function createContextTools(stateRef, opts) {
5158
5479
  }
5159
5480
 
5160
5481
  // src/graph/decisions.ts
5482
+ init_utilTokens();
5161
5483
  function needsSummarization(state, opts, summarizationEnabled) {
5162
5484
  if (!summarizationEnabled) {
5163
5485
  return false;
@@ -5320,12 +5642,24 @@ function createSmartAgent(opts) {
5320
5642
  toolResponses: resolved.toolResponses,
5321
5643
  useTodoList: planningEnabled
5322
5644
  };
5323
- const stateRef = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
5324
- const contextTools = createContextTools(stateRef, { planningEnabled, includeGetToolResponse: false });
5325
- const getToolResponseTool = createGetToolResponseTool(stateRef);
5326
- const toolsWithoutRecovery = [...opts.tools ?? [], ...contextTools];
5327
- const toolsWithRecovery = [...toolsWithoutRecovery, getToolResponseTool];
5328
- 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 });
5329
5663
  base.__runtime.runtimeProfile = resolved.runtimeProfile;
5330
5664
  base.__runtime.smart = resolved;
5331
5665
  const summarizationEnabled = resolved.summarization.enable !== false;
@@ -5361,17 +5695,17 @@ function createSmartAgent(opts) {
5361
5695
  const memoryFacts = await writeSummaryFactsToMemory(memoryStore, resolved, latestSummary, Math.max(1, (state.messages || []).filter((message) => message.role === "user").length));
5362
5696
  return { ...state, memoryFacts };
5363
5697
  }
5364
- function syncPlanState(state) {
5365
- if (!Array.isArray(stateRef.todoList)) return state;
5698
+ function syncPlanStateWith(ref, state) {
5699
+ if (!Array.isArray(ref.todoList)) return state;
5366
5700
  return {
5367
5701
  ...state,
5368
5702
  plan: {
5369
- version: stateRef.planVersion || state.planVersion || 1,
5370
- steps: stateRef.todoList,
5703
+ version: ref.planVersion || state.planVersion || 1,
5704
+ steps: ref.todoList,
5371
5705
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
5372
- adherenceScore: stateRef.adherenceScore || 0
5706
+ adherenceScore: ref.adherenceScore || 0
5373
5707
  },
5374
- planVersion: stateRef.planVersion || state.planVersion || 1
5708
+ planVersion: ref.planVersion || state.planVersion || 1
5375
5709
  };
5376
5710
  }
5377
5711
  function systemMessage2() {
@@ -5383,9 +5717,9 @@ function createSmartAgent(opts) {
5383
5717
  );
5384
5718
  return { role: "system", content: sys };
5385
5719
  }
5386
- function syncRuntimeTools(currentState, currentMessages) {
5720
+ function syncRuntimeTools(currentState, currentMessages, toolSet) {
5387
5721
  const needsRecoveryTool = hasToolResponseRecoveryReference(currentMessages);
5388
- const nextTools = needsRecoveryTool ? toolsWithRecovery : toolsWithoutRecovery;
5722
+ const nextTools = needsRecoveryTool ? toolSet.toolsWithRecovery : toolSet.toolsWithoutRecovery;
5389
5723
  const currentRuntime = currentState.agent || base.__runtime;
5390
5724
  if (currentRuntime.tools === nextTools) {
5391
5725
  return currentState;
@@ -5404,7 +5738,7 @@ function createSmartAgent(opts) {
5404
5738
  const toolCalls = Array.isArray(lastAssistant.tool_calls) ? lastAssistant.tool_calls : [];
5405
5739
  return toolCalls.length === 0;
5406
5740
  }
5407
- async function trySummarize(currentState, currentRawMessages) {
5741
+ async function trySummarize(currentState, currentRawMessages, ref) {
5408
5742
  if (!summarizer) return { state: currentState, rawMessages: currentRawMessages, compressed: false };
5409
5743
  const delta = await summarizer(currentState);
5410
5744
  if (!delta || Object.keys(delta).length === 0) {
@@ -5413,28 +5747,31 @@ function createSmartAgent(opts) {
5413
5747
  }
5414
5748
  const ctx = { ...currentState.ctx || {} };
5415
5749
  delete ctx.__summarizationExhausted;
5416
- const updated = await persistLatestSummary(syncPlanState({ ...currentState, ...delta, ctx }));
5750
+ const updated = await persistLatestSummary(syncPlanStateWith(ref, { ...currentState, ...delta, ctx }));
5417
5751
  return { state: updated, rawMessages: [...updated.messages || currentRawMessages], compressed: true };
5418
5752
  }
5419
5753
  const instance = {
5420
5754
  invoke: async (input, config) => {
5755
+ const toolSet = buildInvokeToolSet();
5756
+ const stateRef = toolSet.stateRef;
5421
5757
  stateRef.toolHistory = input.toolHistory;
5422
5758
  stateRef.toolHistoryArchived = input.toolHistoryArchived;
5423
5759
  stateRef.todoList = input.plan?.steps;
5424
5760
  stateRef.planVersion = input.planVersion || input.plan?.version || 0;
5425
5761
  stateRef.adherenceScore = input.plan?.adherenceScore || 0;
5762
+ const syncPlanFromRef = (state2) => syncPlanStateWith(stateRef, state2);
5426
5763
  const alreadyHasSystem = Array.isArray(input.messages) && input.messages[0]?.role === "system";
5427
5764
  const seedMessages = alreadyHasSystem ? [...input.messages || []] : [systemMessage2(), ...input.messages || []];
5428
- let state = syncRuntimeTools(await syncMemory({ ...input, messages: seedMessages }), seedMessages);
5765
+ let state = syncRuntimeTools(await syncMemory({ ...input, messages: seedMessages }), seedMessages, toolSet);
5429
5766
  let lastResult = null;
5430
5767
  let rawMessages = [...seedMessages];
5431
5768
  const effectiveMaxToolCalls = config?.limits?.maxToolCalls ?? resolved.limits.maxToolCalls ?? 10;
5432
5769
  const iterationLimit = Math.max(effectiveMaxToolCalls * 3 + 5, 30);
5433
5770
  for (let i = 0; i < iterationLimit; i++) {
5434
- state = syncRuntimeTools({ ...state, messages: rawMessages }, rawMessages);
5771
+ state = syncRuntimeTools({ ...state, messages: rawMessages }, rawMessages, toolSet);
5435
5772
  const next = summarizationEnabled ? decideBefore(state) : "agent";
5436
5773
  if (next === "contextSummarize" && summarizer) {
5437
- const result = await trySummarize(state, rawMessages);
5774
+ const result = await trySummarize(state, rawMessages, stateRef);
5438
5775
  state = result.state;
5439
5776
  rawMessages = result.rawMessages;
5440
5777
  if (result.compressed) continue;
@@ -5458,14 +5795,14 @@ function createSmartAgent(opts) {
5458
5795
  if (currentPlan && !state.plan) {
5459
5796
  state = { ...state, plan: currentPlan };
5460
5797
  }
5461
- state = syncRuntimeTools(syncPlanState(await syncMemory(state)), rawMessages);
5798
+ state = syncRuntimeTools(syncPlanFromRef(await syncMemory(state)), rawMessages, toolSet);
5462
5799
  stateRef.toolHistory = state.toolHistory;
5463
5800
  stateRef.toolHistoryArchived = state.toolHistoryArchived;
5464
5801
  if (state.ctx?.__needsSummarization && summarizer) {
5465
5802
  const ctx = { ...state.ctx || {} };
5466
5803
  delete ctx.__needsSummarization;
5467
5804
  state = { ...state, ctx };
5468
- const result = await trySummarize(state, rawMessages);
5805
+ const result = await trySummarize(state, rawMessages, stateRef);
5469
5806
  state = result.state;
5470
5807
  rawMessages = result.rawMessages;
5471
5808
  continue;
@@ -5476,7 +5813,7 @@ function createSmartAgent(opts) {
5476
5813
  if (summarizationEnabled) {
5477
5814
  const after = decideAfter(state);
5478
5815
  if (after === "contextSummarize" && summarizer) {
5479
- const result = await trySummarize(state, rawMessages);
5816
+ const result = await trySummarize(state, rawMessages, stateRef);
5480
5817
  state = result.state;
5481
5818
  rawMessages = result.rawMessages;
5482
5819
  if (result.compressed) continue;
@@ -5915,7 +6252,50 @@ var ProviderError = class extends Error {
5915
6252
  };
5916
6253
 
5917
6254
  // src/providers/base.ts
6255
+ var DEFAULT_RETRY = {
6256
+ maxRetries: 3,
6257
+ baseDelayMs: 500,
6258
+ maxDelayMs: 8e3
6259
+ };
5918
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
+ }
5919
6299
  };
5920
6300
 
5921
6301
  // src/providers/utils/sse.ts
@@ -6042,6 +6422,7 @@ var OpenAIProvider = class extends BaseProvider {
6042
6422
  ...config.defaultHeaders,
6043
6423
  ...config.organization ? { "OpenAI-Organization": config.organization } : {}
6044
6424
  };
6425
+ if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
6045
6426
  }
6046
6427
  async complete(request) {
6047
6428
  const body = this.buildRequestBody(request, false);
@@ -6189,15 +6570,13 @@ var OpenAIProvider = class extends BaseProvider {
6189
6570
  }
6190
6571
  async doFetch(body) {
6191
6572
  const url = `${this.baseURL}/chat/completions`;
6192
- const res = await fetch(url, {
6193
- method: "POST",
6194
- headers: {
6195
- "Content-Type": "application/json",
6196
- Authorization: `Bearer ${this.apiKey}`,
6197
- ...this.defaultHeaders
6198
- },
6199
- body: JSON.stringify(body)
6200
- });
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 }));
6201
6580
  if (!res.ok) {
6202
6581
  const text = await res.text().catch(() => "");
6203
6582
  throw new ProviderError(
@@ -6267,6 +6646,8 @@ var AnthropicProvider = class extends BaseProvider {
6267
6646
  this.defaultModel = config.defaultModel ?? "claude-sonnet-4-20250514";
6268
6647
  this.defaultHeaders = config.defaultHeaders ?? {};
6269
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 };
6270
6651
  }
6271
6652
  async complete(request) {
6272
6653
  const body = this.buildRequestBody(request, false);
@@ -6385,12 +6766,22 @@ var AnthropicProvider = class extends BaseProvider {
6385
6766
  max_tokens: request.maxTokens ?? 4096,
6386
6767
  stream
6387
6768
  };
6388
- 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
+ }
6389
6776
  if (request.temperature != null) body.temperature = request.temperature;
6390
6777
  if (request.topP != null) body.top_p = request.topP;
6391
6778
  if (request.stop) body.stop_sequences = request.stop;
6392
6779
  if (request.tools?.length) {
6393
- 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;
6394
6785
  }
6395
6786
  if (request.toolChoice != null) {
6396
6787
  if (request.toolChoice === "auto") {
@@ -6489,16 +6880,14 @@ var AnthropicProvider = class extends BaseProvider {
6489
6880
  }
6490
6881
  async doFetch(body) {
6491
6882
  const url = `${this.baseURL}/v1/messages`;
6492
- const res = await fetch(url, {
6493
- method: "POST",
6494
- headers: {
6495
- "Content-Type": "application/json",
6496
- "x-api-key": this.apiKey,
6497
- "anthropic-version": this.anthropicVersion,
6498
- ...this.defaultHeaders
6499
- },
6500
- body: JSON.stringify(body)
6501
- });
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 }));
6502
6891
  if (!res.ok) {
6503
6892
  const text = await res.text().catch(() => "");
6504
6893
  throw new ProviderError(
@@ -6710,6 +7099,8 @@ var BedrockProvider = class extends BaseProvider {
6710
7099
  this.region = config.region;
6711
7100
  this.defaultModel = config.defaultModel ?? "anthropic.claude-sonnet-4-20250514-v1:0";
6712
7101
  this.defaultHeaders = config.defaultHeaders ?? {};
7102
+ this.promptCachingEnabled = config.prompt_caching?.enabled === true;
7103
+ if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
6713
7104
  this.credentials = {
6714
7105
  accessKeyId: config.accessKeyId ?? process.env.AWS_ACCESS_KEY_ID ?? "",
6715
7106
  secretAccessKey: config.secretAccessKey ?? process.env.AWS_SECRET_ACCESS_KEY ?? "",
@@ -6748,7 +7139,9 @@ var BedrockProvider = class extends BaseProvider {
6748
7139
  messages: messages.map((m) => this.toBedrockMessage(m))
6749
7140
  };
6750
7141
  if (system.length > 0) {
6751
- 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;
6752
7145
  }
6753
7146
  const inferenceConfig = {};
6754
7147
  if (request.maxTokens != null) inferenceConfig.maxTokens = request.maxTokens;
@@ -6757,8 +7150,12 @@ var BedrockProvider = class extends BaseProvider {
6757
7150
  if (request.stop) inferenceConfig.stopSequences = request.stop;
6758
7151
  if (Object.keys(inferenceConfig).length > 0) body.inferenceConfig = inferenceConfig;
6759
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
+ }
6760
7157
  body.toolConfig = {
6761
- tools: request.tools.map((t) => this.toBedrockTool(t))
7158
+ tools
6762
7159
  };
6763
7160
  if (request.toolChoice != null) {
6764
7161
  if (request.toolChoice === "auto") {
@@ -6855,23 +7252,21 @@ var BedrockProvider = class extends BaseProvider {
6855
7252
  const host = `bedrock-runtime.${this.region}.amazonaws.com`;
6856
7253
  const url = `https://${host}/model/${encodeURIComponent(modelId)}/converse`;
6857
7254
  const bodyStr = JSON.stringify(body);
6858
- const headers = signRequest({
6859
- method: "POST",
6860
- url,
6861
- headers: {
6862
- "Content-Type": "application/json",
6863
- Accept: "application/json",
6864
- ...this.defaultHeaders
6865
- },
6866
- body: bodyStr,
6867
- region: this.region,
6868
- service: "bedrock",
6869
- credentials: this.credentials
6870
- });
6871
- const res = await fetch(url, {
6872
- method: "POST",
6873
- headers,
6874
- 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 });
6875
7270
  });
6876
7271
  if (!res.ok) {
6877
7272
  const text = await res.text().catch(() => "");
@@ -7746,6 +8141,6 @@ function createProvider(config) {
7746
8141
  }
7747
8142
  }
7748
8143
 
7749
- 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 };
7750
8145
  //# sourceMappingURL=index.mjs.map
7751
8146
  //# sourceMappingURL=index.mjs.map