@cognipeer/agent-sdk 0.6.4 → 0.6.6
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/README.md +26 -7
- package/dist/index.cjs +866 -171
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +258 -5
- package/dist/index.d.ts +258 -5
- package/dist/index.mjs +862 -172
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -18,7 +18,99 @@ var path__default = /*#__PURE__*/_interopDefault(path);
|
|
|
18
18
|
var crypto__default = /*#__PURE__*/_interopDefault(crypto);
|
|
19
19
|
var AjvModule__default = /*#__PURE__*/_interopDefault(AjvModule);
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
var __defProp = Object.defineProperty;
|
|
22
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
23
|
+
var __esm = (fn, res) => function __init() {
|
|
24
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
25
|
+
};
|
|
26
|
+
var __export = (target, all) => {
|
|
27
|
+
for (var name in all)
|
|
28
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// src/utils/utilTokens.ts
|
|
32
|
+
var utilTokens_exports = {};
|
|
33
|
+
__export(utilTokens_exports, {
|
|
34
|
+
countApproxTokens: () => countApproxTokens,
|
|
35
|
+
countMessagesTokens: () => countMessagesTokens,
|
|
36
|
+
defaultTokenCounter: () => defaultTokenCounter,
|
|
37
|
+
getTokenCounter: () => getTokenCounter,
|
|
38
|
+
setTokenCounter: () => setTokenCounter
|
|
39
|
+
});
|
|
40
|
+
function defaultTokenCounter(text) {
|
|
41
|
+
if (!text) return 0;
|
|
42
|
+
const nonAsciiCount = (text.match(/[^\x00-\x7F]/g) || []).length;
|
|
43
|
+
const asciiCount = text.length - nonAsciiCount;
|
|
44
|
+
return Math.ceil(asciiCount / 4 + nonAsciiCount / 1.5);
|
|
45
|
+
}
|
|
46
|
+
function setTokenCounter(counter) {
|
|
47
|
+
activeTokenCounter = typeof counter === "function" ? counter : defaultTokenCounter;
|
|
48
|
+
}
|
|
49
|
+
function getTokenCounter() {
|
|
50
|
+
return activeTokenCounter;
|
|
51
|
+
}
|
|
52
|
+
function countApproxTokens(text) {
|
|
53
|
+
return activeTokenCounter(text || "");
|
|
54
|
+
}
|
|
55
|
+
function extractMessageText(m) {
|
|
56
|
+
if (!m) return "";
|
|
57
|
+
const parts = [];
|
|
58
|
+
parts.push(m.role || "");
|
|
59
|
+
if (m.name) parts.push(m.name);
|
|
60
|
+
if (typeof m.content === "string") {
|
|
61
|
+
parts.push(m.content);
|
|
62
|
+
} else if (Array.isArray(m.content)) {
|
|
63
|
+
for (const c of m.content) {
|
|
64
|
+
if (typeof c === "string") parts.push(c);
|
|
65
|
+
else if (c?.text) parts.push(c.text);
|
|
66
|
+
else if (c?.content) parts.push(c.content);
|
|
67
|
+
else if (c?.type === "tool_use" && c?.input) {
|
|
68
|
+
parts.push(c.name || "");
|
|
69
|
+
parts.push(typeof c.input === "string" ? c.input : JSON.stringify(c.input));
|
|
70
|
+
} else if (c != null && typeof c === "object") {
|
|
71
|
+
parts.push(JSON.stringify(c));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
} else if (m.content != null && typeof m.content === "object") {
|
|
75
|
+
parts.push(JSON.stringify(m.content));
|
|
76
|
+
}
|
|
77
|
+
if (Array.isArray(m.tool_calls)) {
|
|
78
|
+
for (const tc of m.tool_calls) {
|
|
79
|
+
if (tc?.function?.name) parts.push(tc.function.name);
|
|
80
|
+
if (tc?.function?.arguments) {
|
|
81
|
+
parts.push(
|
|
82
|
+
typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
if (tc?.name && !tc?.function?.name) parts.push(tc.name);
|
|
86
|
+
if (tc?.args && !tc?.function?.arguments) {
|
|
87
|
+
parts.push(typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (Array.isArray(m.additional_kwargs?.tool_calls)) {
|
|
92
|
+
for (const tc of m.additional_kwargs.tool_calls) {
|
|
93
|
+
if (tc?.function?.name) parts.push(tc.function.name);
|
|
94
|
+
if (tc?.function?.arguments) {
|
|
95
|
+
parts.push(
|
|
96
|
+
typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return parts.join("\n");
|
|
102
|
+
}
|
|
103
|
+
function countMessagesTokens(messages) {
|
|
104
|
+
if (!Array.isArray(messages) || messages.length === 0) return 0;
|
|
105
|
+
const allText = messages.map(extractMessageText).join("\n");
|
|
106
|
+
return countApproxTokens(allText) + messages.length * 4;
|
|
107
|
+
}
|
|
108
|
+
var activeTokenCounter;
|
|
109
|
+
var init_utilTokens = __esm({
|
|
110
|
+
"src/utils/utilTokens.ts"() {
|
|
111
|
+
activeTokenCounter = defaultTokenCounter;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
22
114
|
var moduleName = "@langchain/core/tools";
|
|
23
115
|
var cachedToolFactory;
|
|
24
116
|
var cachedHasAttemptedFactory = false;
|
|
@@ -326,7 +418,9 @@ function createTool({
|
|
|
326
418
|
needsApproval,
|
|
327
419
|
approvalPrompt,
|
|
328
420
|
approvalDefaults,
|
|
329
|
-
maxExecutionsPerRun
|
|
421
|
+
maxExecutionsPerRun,
|
|
422
|
+
cache,
|
|
423
|
+
retry
|
|
330
424
|
}) {
|
|
331
425
|
const execute = async (input) => func(input);
|
|
332
426
|
const toolRecord = {
|
|
@@ -351,6 +445,12 @@ function createTool({
|
|
|
351
445
|
if (maxExecutionsPerRun !== void 0) {
|
|
352
446
|
toolRecord.maxExecutionsPerRun = maxExecutionsPerRun;
|
|
353
447
|
}
|
|
448
|
+
if (cache !== void 0) {
|
|
449
|
+
toolRecord.cache = cache;
|
|
450
|
+
}
|
|
451
|
+
if (retry !== void 0) {
|
|
452
|
+
toolRecord.retry = retry;
|
|
453
|
+
}
|
|
354
454
|
toolRecord.__source = toolRecord.__source || "smart";
|
|
355
455
|
toolRecord.__impl = func;
|
|
356
456
|
return toolRecord;
|
|
@@ -653,6 +753,7 @@ function createResolverNode() {
|
|
|
653
753
|
toolHistory: state.toolHistory || [],
|
|
654
754
|
toolHistoryArchived: state.toolHistoryArchived || [],
|
|
655
755
|
pendingApprovals: state.pendingApprovals || [],
|
|
756
|
+
pendingUserQuestions: state.pendingUserQuestions || [],
|
|
656
757
|
plan: state.plan || null,
|
|
657
758
|
planVersion: state.planVersion || 0
|
|
658
759
|
};
|
|
@@ -1892,7 +1993,8 @@ function createAgentCoreNode(opts) {
|
|
|
1892
1993
|
todoListPrompt: opts.todoListPrompt,
|
|
1893
1994
|
limits: opts.limits,
|
|
1894
1995
|
outputSchema: opts.outputSchema,
|
|
1895
|
-
tracing: opts.tracing
|
|
1996
|
+
tracing: opts.tracing,
|
|
1997
|
+
responseFormat: void 0
|
|
1896
1998
|
};
|
|
1897
1999
|
const tools = runtime.tools ?? [];
|
|
1898
2000
|
const shouldUseStrictToolCalling = Boolean(
|
|
@@ -2094,7 +2196,7 @@ function createAgentCoreNode(opts) {
|
|
|
2094
2196
|
// src/smart/runtimeConfig.ts
|
|
2095
2197
|
var DEFAULT_CRITICAL_TOOLS = ["response", "manage_todo_list", "get_tool_response"];
|
|
2096
2198
|
var BASE_DEFAULTS = {
|
|
2097
|
-
limits: { maxToolCalls: 8, maxParallelTools: 2, maxContextTokens: 24e3 },
|
|
2199
|
+
limits: { maxToolCalls: 8, maxParallelTools: 2, maxContextTokens: 24e3, maxTotalOutputTokens: 0, maxCostUsd: 0, maxWallClockMs: 0 },
|
|
2098
2200
|
summarization: {
|
|
2099
2201
|
enable: true,
|
|
2100
2202
|
maxTokens: 24e3,
|
|
@@ -2221,7 +2323,10 @@ function mergeLimits(base, override) {
|
|
|
2221
2323
|
return {
|
|
2222
2324
|
maxToolCalls: override?.maxToolCalls ?? base.maxToolCalls,
|
|
2223
2325
|
maxParallelTools: override?.maxParallelTools ?? base.maxParallelTools,
|
|
2224
|
-
maxContextTokens: override?.maxContextTokens ?? base.maxContextTokens
|
|
2326
|
+
maxContextTokens: override?.maxContextTokens ?? base.maxContextTokens,
|
|
2327
|
+
maxTotalOutputTokens: override?.maxTotalOutputTokens ?? base.maxTotalOutputTokens ?? 0,
|
|
2328
|
+
maxCostUsd: override?.maxCostUsd ?? base.maxCostUsd ?? 0,
|
|
2329
|
+
maxWallClockMs: override?.maxWallClockMs ?? base.maxWallClockMs ?? 0
|
|
2225
2330
|
};
|
|
2226
2331
|
}
|
|
2227
2332
|
function resolveBaseProfile(opts, runtimeProfile) {
|
|
@@ -2299,66 +2404,8 @@ function getResolvedSmartConfig(opts, runtime) {
|
|
|
2299
2404
|
return runtime?.smart || normalizeSmartAgentOptions(opts);
|
|
2300
2405
|
}
|
|
2301
2406
|
|
|
2302
|
-
// src/
|
|
2303
|
-
|
|
2304
|
-
if (!text) return 0;
|
|
2305
|
-
const nonAsciiCount = (text.match(/[^\x00-\x7F]/g) || []).length;
|
|
2306
|
-
const asciiCount = text.length - nonAsciiCount;
|
|
2307
|
-
return Math.ceil(asciiCount / 4 + nonAsciiCount / 1.5);
|
|
2308
|
-
}
|
|
2309
|
-
function extractMessageText(m) {
|
|
2310
|
-
if (!m) return "";
|
|
2311
|
-
const parts = [];
|
|
2312
|
-
parts.push(m.role || "");
|
|
2313
|
-
if (m.name) parts.push(m.name);
|
|
2314
|
-
if (typeof m.content === "string") {
|
|
2315
|
-
parts.push(m.content);
|
|
2316
|
-
} else if (Array.isArray(m.content)) {
|
|
2317
|
-
for (const c of m.content) {
|
|
2318
|
-
if (typeof c === "string") parts.push(c);
|
|
2319
|
-
else if (c?.text) parts.push(c.text);
|
|
2320
|
-
else if (c?.content) parts.push(c.content);
|
|
2321
|
-
else if (c?.type === "tool_use" && c?.input) {
|
|
2322
|
-
parts.push(c.name || "");
|
|
2323
|
-
parts.push(typeof c.input === "string" ? c.input : JSON.stringify(c.input));
|
|
2324
|
-
} else if (c != null && typeof c === "object") {
|
|
2325
|
-
parts.push(JSON.stringify(c));
|
|
2326
|
-
}
|
|
2327
|
-
}
|
|
2328
|
-
} else if (m.content != null && typeof m.content === "object") {
|
|
2329
|
-
parts.push(JSON.stringify(m.content));
|
|
2330
|
-
}
|
|
2331
|
-
if (Array.isArray(m.tool_calls)) {
|
|
2332
|
-
for (const tc of m.tool_calls) {
|
|
2333
|
-
if (tc?.function?.name) parts.push(tc.function.name);
|
|
2334
|
-
if (tc?.function?.arguments) {
|
|
2335
|
-
parts.push(
|
|
2336
|
-
typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
|
|
2337
|
-
);
|
|
2338
|
-
}
|
|
2339
|
-
if (tc?.name && !tc?.function?.name) parts.push(tc.name);
|
|
2340
|
-
if (tc?.args && !tc?.function?.arguments) {
|
|
2341
|
-
parts.push(typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args));
|
|
2342
|
-
}
|
|
2343
|
-
}
|
|
2344
|
-
}
|
|
2345
|
-
if (Array.isArray(m.additional_kwargs?.tool_calls)) {
|
|
2346
|
-
for (const tc of m.additional_kwargs.tool_calls) {
|
|
2347
|
-
if (tc?.function?.name) parts.push(tc.function.name);
|
|
2348
|
-
if (tc?.function?.arguments) {
|
|
2349
|
-
parts.push(
|
|
2350
|
-
typeof tc.function.arguments === "string" ? tc.function.arguments : JSON.stringify(tc.function.arguments)
|
|
2351
|
-
);
|
|
2352
|
-
}
|
|
2353
|
-
}
|
|
2354
|
-
}
|
|
2355
|
-
return parts.join("\n");
|
|
2356
|
-
}
|
|
2357
|
-
function countMessagesTokens(messages) {
|
|
2358
|
-
if (!Array.isArray(messages) || messages.length === 0) return 0;
|
|
2359
|
-
const allText = messages.map(extractMessageText).join("\n");
|
|
2360
|
-
return countApproxTokens(allText) + messages.length * 4;
|
|
2361
|
-
}
|
|
2407
|
+
// src/smart/toolResponses.ts
|
|
2408
|
+
init_utilTokens();
|
|
2362
2409
|
|
|
2363
2410
|
// src/utils/content.ts
|
|
2364
2411
|
function contentToString(content) {
|
|
@@ -2548,16 +2595,20 @@ function createToolsNode(initialTools, opts) {
|
|
|
2548
2595
|
const traceSession = state.ctx?.__traceSession;
|
|
2549
2596
|
const pendingApprovals = Array.isArray(state.pendingApprovals) ? state.pendingApprovals.map((entry) => ({ ...entry })) : [];
|
|
2550
2597
|
const pendingByCallId = new Map(pendingApprovals.map((entry) => [entry.toolCallId, entry]));
|
|
2598
|
+
const pendingUserQuestions = Array.isArray(state.pendingUserQuestions) ? state.pendingUserQuestions.map((entry) => ({ ...entry })) : [];
|
|
2551
2599
|
let awaitingApproval = false;
|
|
2600
|
+
let awaitingUserQuestion = false;
|
|
2552
2601
|
for (const tool of toolByName.values()) {
|
|
2553
2602
|
const anyTool = tool;
|
|
2554
2603
|
if (anyTool._stateRef && typeof anyTool._stateRef === "object") {
|
|
2555
2604
|
anyTool._stateRef.toolHistory = state.toolHistory;
|
|
2556
2605
|
anyTool._stateRef.toolHistoryArchived = state.toolHistoryArchived;
|
|
2557
2606
|
anyTool._stateRef.pendingApprovals = pendingApprovals;
|
|
2607
|
+
anyTool._stateRef.pendingUserQuestions = pendingUserQuestions;
|
|
2558
2608
|
anyTool._stateRef.messages = state.messages;
|
|
2559
2609
|
anyTool._stateRef.ctx = state.ctx || (state.ctx = {});
|
|
2560
2610
|
anyTool._stateRef.__onEvent = onEvent;
|
|
2611
|
+
anyTool._stateRef.parentRuntime = runtime;
|
|
2561
2612
|
}
|
|
2562
2613
|
}
|
|
2563
2614
|
const last = state.messages[state.messages.length - 1];
|
|
@@ -2568,6 +2619,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
2568
2619
|
const remaining = Math.max(0, limits.maxToolCalls - toolCount);
|
|
2569
2620
|
const planned = toolCalls.slice(0, remaining);
|
|
2570
2621
|
const skipped = toolCalls.slice(remaining);
|
|
2622
|
+
const orderedSlots = planned.map(() => []);
|
|
2571
2623
|
const isCancelled = () => {
|
|
2572
2624
|
if (abortSignal?.aborted) return { cancelled: true, reason: "aborted" };
|
|
2573
2625
|
if (cancellationToken && cancellationToken.isCancellationRequested) return { cancelled: true, reason: "cancelled" };
|
|
@@ -2584,7 +2636,9 @@ function createToolsNode(initialTools, opts) {
|
|
|
2584
2636
|
}
|
|
2585
2637
|
state.ctx = ctx;
|
|
2586
2638
|
};
|
|
2587
|
-
const runOne = async (tc) => {
|
|
2639
|
+
const runOne = async (tc, slot = appended) => {
|
|
2640
|
+
var _a;
|
|
2641
|
+
const push = (msg) => slot.push(msg);
|
|
2588
2642
|
const cancelState = isCancelled();
|
|
2589
2643
|
if (cancelState.cancelled) {
|
|
2590
2644
|
const ctx = state.ctx = state.ctx || {};
|
|
@@ -2595,7 +2649,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
2595
2649
|
}
|
|
2596
2650
|
const tool = toolByName.get(tc.name);
|
|
2597
2651
|
if (!tool) {
|
|
2598
|
-
|
|
2652
|
+
push({ role: "tool", content: `Tool not found: ${tc.name}`, tool_call_id: tc.id || `${tc.name}_unknown`, name: tc.name });
|
|
2599
2653
|
onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args: tc.args, error: { message: "Tool not found" } });
|
|
2600
2654
|
toolCount += 1;
|
|
2601
2655
|
return { status: "error" };
|
|
@@ -2611,7 +2665,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
2611
2665
|
const validatedArgs = validateToolArgs(tool, args);
|
|
2612
2666
|
if (!validatedArgs.ok && resolved.toolResponses.schemaValidation === "strict") {
|
|
2613
2667
|
const errorMessage = `Tool argument validation failed for ${tc.name}: ${validatedArgs.message}`;
|
|
2614
|
-
|
|
2668
|
+
push({ role: "tool", content: errorMessage, tool_call_id: toolCallId, name: tc.name });
|
|
2615
2669
|
markToolFailure(errorMessage, tc.name, toolCallId);
|
|
2616
2670
|
onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args, error: { message: errorMessage } });
|
|
2617
2671
|
toolCount += 1;
|
|
@@ -2624,6 +2678,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
2624
2678
|
const toolStateRef = tool._stateRef && typeof tool._stateRef === "object" ? tool._stateRef : null;
|
|
2625
2679
|
if (toolStateRef) {
|
|
2626
2680
|
toolStateRef.pendingApprovals = pendingApprovals;
|
|
2681
|
+
toolStateRef.pendingUserQuestions = pendingUserQuestions;
|
|
2627
2682
|
toolStateRef.ctx = state.ctx || (state.ctx = {});
|
|
2628
2683
|
toolStateRef.__currentToolCallId = toolCallId;
|
|
2629
2684
|
delete toolStateRef.__awaitingApproval;
|
|
@@ -2633,7 +2688,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
2633
2688
|
const currentExecutions = countSuccessfulToolExecutions(toolHistory, toolName);
|
|
2634
2689
|
if (currentExecutions >= maxExecutionsPerRun) {
|
|
2635
2690
|
const limitMessage = `Skipped tool due to per-tool execution limit: ${toolName} (${maxExecutionsPerRun}/run)`;
|
|
2636
|
-
|
|
2691
|
+
push({ role: "tool", content: limitMessage, tool_call_id: toolCallId, name: tc.name });
|
|
2637
2692
|
onEvent?.({ type: "tool_call", phase: "skipped", name: toolName, id: tc.id, args });
|
|
2638
2693
|
recordTraceEvent(traceSession, {
|
|
2639
2694
|
type: "tool_call",
|
|
@@ -2693,7 +2748,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
2693
2748
|
}
|
|
2694
2749
|
if (approvalEntry.status === "rejected") {
|
|
2695
2750
|
const rejectionMessage = approvalEntry.comment || "Tool call rejected by reviewer.";
|
|
2696
|
-
|
|
2751
|
+
push({ role: "tool", content: `Tool call rejected: ${rejectionMessage}`, tool_call_id: toolCallId, name: tc.name });
|
|
2697
2752
|
approvalEntry.metadata = { ...approvalEntry.metadata || {}, resolution: "rejected" };
|
|
2698
2753
|
approvalEntry.status = "executed";
|
|
2699
2754
|
approvalEntry.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -2711,23 +2766,102 @@ function createToolsNode(initialTools, opts) {
|
|
|
2711
2766
|
const start = Date.now();
|
|
2712
2767
|
const sanitizedArgs = sanitizeTracePayload(args);
|
|
2713
2768
|
const inputBytes = traceSession?.resolvedConfig.logData ? estimatePayloadBytes(sanitizedArgs) : void 0;
|
|
2769
|
+
const cacheCfg = tool.cache;
|
|
2770
|
+
const cacheEnabled = cacheCfg === true || cacheCfg && typeof cacheCfg === "object";
|
|
2771
|
+
const cacheKeyFn = cacheEnabled && typeof cacheCfg === "object" ? cacheCfg.keyFn : void 0;
|
|
2772
|
+
const stableJson = (value) => {
|
|
2773
|
+
try {
|
|
2774
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
2775
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
2776
|
+
const keys = Object.keys(value).sort();
|
|
2777
|
+
return `{${keys.map((k) => JSON.stringify(k) + ":" + stableJson(value[k])).join(",")}}`;
|
|
2778
|
+
} catch {
|
|
2779
|
+
return String(value);
|
|
2780
|
+
}
|
|
2781
|
+
};
|
|
2782
|
+
const cacheKey = cacheEnabled ? `${toolName}|${cacheKeyFn ? cacheKeyFn(args) : stableJson(args)}` : void 0;
|
|
2783
|
+
const toolCache = state.toolCache = state.toolCache || {};
|
|
2784
|
+
let cachedHit = void 0;
|
|
2785
|
+
if (cacheKey && Object.prototype.hasOwnProperty.call(toolCache, cacheKey)) {
|
|
2786
|
+
const entry = toolCache[cacheKey];
|
|
2787
|
+
const ttl = cacheEnabled && typeof cacheCfg === "object" ? cacheCfg.ttlMs : void 0;
|
|
2788
|
+
if (!ttl || entry?.timestamp && Date.now() - entry.timestamp <= ttl) {
|
|
2789
|
+
cachedHit = entry?.value;
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
const retryCfg = tool.retry;
|
|
2793
|
+
const failureStats = (_a = state.ctx).__toolFailureCounts || (_a.__toolFailureCounts = {});
|
|
2794
|
+
if (retryCfg?.circuitBreakerThreshold && failureStats[toolName] >= retryCfg.circuitBreakerThreshold) {
|
|
2795
|
+
const breakerMsg = `Tool ${toolName} circuit breaker open after ${failureStats[toolName]} consecutive failures`;
|
|
2796
|
+
push({ role: "tool", content: breakerMsg, tool_call_id: toolCallId, name: tc.name });
|
|
2797
|
+
markToolFailure(breakerMsg, toolName, toolCallId);
|
|
2798
|
+
onEvent?.({ type: "tool_call", phase: "error", name: toolName, id: tc.id, args, error: { message: breakerMsg } });
|
|
2799
|
+
toolCount += 1;
|
|
2800
|
+
return { status: "error" };
|
|
2801
|
+
}
|
|
2714
2802
|
try {
|
|
2803
|
+
if (cachedHit !== void 0) {
|
|
2804
|
+
const executionId2 = nanoid.nanoid();
|
|
2805
|
+
const responsePolicy2 = applyToolResponseHardCap(toolName, cachedHit, executionId2, resolved);
|
|
2806
|
+
toolHistory.push({
|
|
2807
|
+
executionId: executionId2,
|
|
2808
|
+
toolName,
|
|
2809
|
+
args,
|
|
2810
|
+
output: cachedHit,
|
|
2811
|
+
rawOutput: cachedHit,
|
|
2812
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2813
|
+
tool_call_id: tc.id,
|
|
2814
|
+
summarized: false,
|
|
2815
|
+
originalTokenCount: responsePolicy2.tokenCount,
|
|
2816
|
+
classification: responsePolicy2.classification,
|
|
2817
|
+
retentionPolicy: "keep_full",
|
|
2818
|
+
fromCache: true,
|
|
2819
|
+
status: "success"
|
|
2820
|
+
});
|
|
2821
|
+
push({ role: "tool", content: responsePolicy2.content, tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
|
|
2822
|
+
onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: cachedHit, durationMs: 0 });
|
|
2823
|
+
toolCount += 1;
|
|
2824
|
+
return { status: "success" };
|
|
2825
|
+
}
|
|
2715
2826
|
onEvent?.({ type: "tool_call", phase: "start", name: toolName, id: tc.id, args });
|
|
2716
2827
|
onProgress?.({ stage: "tools", message: `Running tool ${tc.name}` });
|
|
2717
2828
|
const anyTool = tool;
|
|
2718
2829
|
const callOptions = { cancellationToken, signal: abortSignal };
|
|
2830
|
+
const invokeOnce = async () => {
|
|
2831
|
+
if (typeof anyTool.func === "function") return anyTool.func(args, callOptions);
|
|
2832
|
+
if (typeof anyTool.invoke === "function") return anyTool.invoke(args, callOptions);
|
|
2833
|
+
if (typeof anyTool.call === "function") return anyTool.call(args, callOptions);
|
|
2834
|
+
if (typeof anyTool._call === "function") return anyTool._call(args, callOptions);
|
|
2835
|
+
if (typeof anyTool.run === "function") return anyTool.run(args, callOptions);
|
|
2836
|
+
throw new Error("Tool is not invokable");
|
|
2837
|
+
};
|
|
2719
2838
|
let output;
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2839
|
+
const maxRetries = Math.max(0, Number(retryCfg?.maxRetries) || 0);
|
|
2840
|
+
const backoffMs = Math.max(0, Number(retryCfg?.backoffMs) || 250);
|
|
2841
|
+
let attempt = 0;
|
|
2842
|
+
while (true) {
|
|
2843
|
+
try {
|
|
2844
|
+
output = await invokeOnce();
|
|
2845
|
+
failureStats[toolName] = 0;
|
|
2846
|
+
break;
|
|
2847
|
+
} catch (retryErr) {
|
|
2848
|
+
attempt += 1;
|
|
2849
|
+
const shouldRetry = retryCfg?.shouldRetry ? retryCfg.shouldRetry(retryErr, attempt) : true;
|
|
2850
|
+
if (attempt > maxRetries || !shouldRetry) {
|
|
2851
|
+
failureStats[toolName] = (failureStats[toolName] || 0) + 1;
|
|
2852
|
+
throw retryErr;
|
|
2853
|
+
}
|
|
2854
|
+
await new Promise((r) => setTimeout(r, backoffMs * Math.pow(2, attempt - 1)));
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
if (cacheKey) {
|
|
2858
|
+
toolCache[cacheKey] = { value: output, timestamp: Date.now() };
|
|
2859
|
+
}
|
|
2726
2860
|
const durationMs = Date.now() - start;
|
|
2727
2861
|
const executionId = nanoid.nanoid();
|
|
2728
2862
|
if (output && typeof output === "object" && output.__handoff && output.__handoff.runtime) {
|
|
2729
2863
|
toolHistory.push({ executionId, toolName, args, output: "handoff:ok", rawOutput: output, timestamp: (/* @__PURE__ */ new Date()).toISOString(), tool_call_id: tc.id, status: "handoff" });
|
|
2730
|
-
|
|
2864
|
+
push({ role: "tool", content: "ok", tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
|
|
2731
2865
|
state.agent = output.__handoff.runtime;
|
|
2732
2866
|
onEvent?.({ type: "handoff", from: runtime.name, to: state.agent?.name, toolName });
|
|
2733
2867
|
onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: "handoff", durationMs });
|
|
@@ -2756,6 +2890,22 @@ function createToolsNode(initialTools, opts) {
|
|
|
2756
2890
|
approval: pendingApproval
|
|
2757
2891
|
};
|
|
2758
2892
|
}
|
|
2893
|
+
if (output && typeof output === "object" && output.__awaitingUserQuestion) {
|
|
2894
|
+
if (toolStateRef?.ctx && typeof toolStateRef.ctx === "object") {
|
|
2895
|
+
state.ctx = toolStateRef.ctx;
|
|
2896
|
+
}
|
|
2897
|
+
const pendingQuestion = pendingUserQuestions.find(
|
|
2898
|
+
(entry) => entry.toolCallId === output.toolCallId || entry.id === output.id
|
|
2899
|
+
);
|
|
2900
|
+
if (!pendingQuestion) {
|
|
2901
|
+
throw new Error(`Tool ${toolName} requested a user question but no pending question entry was recorded.`);
|
|
2902
|
+
}
|
|
2903
|
+
awaitingUserQuestion = true;
|
|
2904
|
+
return {
|
|
2905
|
+
status: "awaiting_user_question",
|
|
2906
|
+
userQuestion: pendingQuestion
|
|
2907
|
+
};
|
|
2908
|
+
}
|
|
2759
2909
|
if (output && typeof output === "object" && output.__finalStructuredOutput) {
|
|
2760
2910
|
if (!state.ctx) state.ctx = {};
|
|
2761
2911
|
state.ctx.__structuredOutputParsed = output.data;
|
|
@@ -2779,7 +2929,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
2779
2929
|
archiveId: void 0,
|
|
2780
2930
|
status: "success"
|
|
2781
2931
|
});
|
|
2782
|
-
|
|
2932
|
+
push({ role: "tool", content: responsePolicy.content, tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
|
|
2783
2933
|
onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: output, durationMs });
|
|
2784
2934
|
onProgress?.({ stage: "tools", message: `Tool ${tc.name} completed`, detail: { durationMs } });
|
|
2785
2935
|
if (needsApproval && approvalEntry) {
|
|
@@ -2835,7 +2985,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
2835
2985
|
const executionId = nanoid.nanoid();
|
|
2836
2986
|
const message = error?.message || String(error);
|
|
2837
2987
|
toolHistory.push({ executionId, toolName, args, output: `Error executing tool: ${message}`, rawOutput: null, timestamp: (/* @__PURE__ */ new Date()).toISOString(), tool_call_id: tc.id, status: "error" });
|
|
2838
|
-
|
|
2988
|
+
push({ role: "tool", content: `Error executing tool: ${message}`, tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
|
|
2839
2989
|
markToolFailure(message, toolName, toolCallId);
|
|
2840
2990
|
onEvent?.({ type: "tool_call", phase: "error", name: toolName, id: tc.id, args, error: { message } });
|
|
2841
2991
|
onProgress?.({ stage: "tools", message: `Tool ${tc.name} failed`, detail: { error: message } });
|
|
@@ -2872,21 +3022,74 @@ function createToolsNode(initialTools, opts) {
|
|
|
2872
3022
|
return { status: "error", approval: approvalEntry };
|
|
2873
3023
|
}
|
|
2874
3024
|
};
|
|
2875
|
-
|
|
2876
|
-
|
|
3025
|
+
const approvalIndices = [];
|
|
3026
|
+
const parallelIndices = [];
|
|
3027
|
+
planned.forEach((tc, idx) => {
|
|
3028
|
+
const t = toolByName.get(tc.name);
|
|
3029
|
+
if (t && t.needsApproval) approvalIndices.push(idx);
|
|
3030
|
+
else parallelIndices.push(idx);
|
|
3031
|
+
});
|
|
3032
|
+
let shouldStop = false;
|
|
3033
|
+
const checkCancellationStop = () => {
|
|
2877
3034
|
const cancelState = isCancelled();
|
|
2878
3035
|
if (cancelState.cancelled) {
|
|
2879
3036
|
const ctx = state.ctx = state.ctx || {};
|
|
2880
3037
|
ctx.__cancelled = { stage: "tools", reason: cancelState.reason, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2881
3038
|
onEvent?.({ type: "cancelled", stage: "tools", reason: cancelState.reason });
|
|
2882
3039
|
onProgress?.({ stage: "tools", message: "Cancelled", detail: { reason: cancelState.reason } });
|
|
2883
|
-
|
|
3040
|
+
shouldStop = true;
|
|
3041
|
+
return true;
|
|
2884
3042
|
}
|
|
2885
|
-
|
|
3043
|
+
return false;
|
|
3044
|
+
};
|
|
3045
|
+
for (const idx of approvalIndices) {
|
|
3046
|
+
if (awaitingApproval || awaitingUserQuestion || shouldStop) break;
|
|
3047
|
+
if (checkCancellationStop()) break;
|
|
3048
|
+
const result = await runOne(planned[idx], orderedSlots[idx]);
|
|
2886
3049
|
if (result.status === "awaiting_approval") {
|
|
2887
3050
|
awaitingApproval = true;
|
|
2888
3051
|
break;
|
|
2889
3052
|
}
|
|
3053
|
+
if (result.status === "awaiting_user_question") {
|
|
3054
|
+
awaitingUserQuestion = true;
|
|
3055
|
+
break;
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
if (!awaitingApproval && !awaitingUserQuestion && !shouldStop && parallelIndices.length > 0) {
|
|
3059
|
+
const concurrency = Math.max(1, Math.min(limits.maxParallelTools, parallelIndices.length));
|
|
3060
|
+
let cursor = 0;
|
|
3061
|
+
const workerError = {};
|
|
3062
|
+
const workers = [];
|
|
3063
|
+
for (let w = 0; w < concurrency; w++) {
|
|
3064
|
+
workers.push((async () => {
|
|
3065
|
+
while (true) {
|
|
3066
|
+
if (workerError.err || shouldStop || awaitingApproval || awaitingUserQuestion) return;
|
|
3067
|
+
const i = cursor++;
|
|
3068
|
+
if (i >= parallelIndices.length) return;
|
|
3069
|
+
if (checkCancellationStop()) return;
|
|
3070
|
+
const idx = parallelIndices[i];
|
|
3071
|
+
try {
|
|
3072
|
+
const result = await runOne(planned[idx], orderedSlots[idx]);
|
|
3073
|
+
if (result.status === "awaiting_approval") {
|
|
3074
|
+
awaitingApproval = true;
|
|
3075
|
+
return;
|
|
3076
|
+
}
|
|
3077
|
+
if (result.status === "awaiting_user_question") {
|
|
3078
|
+
awaitingUserQuestion = true;
|
|
3079
|
+
return;
|
|
3080
|
+
}
|
|
3081
|
+
} catch (err) {
|
|
3082
|
+
workerError.err = err;
|
|
3083
|
+
return;
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
})());
|
|
3087
|
+
}
|
|
3088
|
+
await Promise.all(workers);
|
|
3089
|
+
if (workerError.err) throw workerError.err;
|
|
3090
|
+
}
|
|
3091
|
+
for (const slot of orderedSlots) {
|
|
3092
|
+
if (slot.length > 0) appended.push(...slot);
|
|
2890
3093
|
}
|
|
2891
3094
|
for (const tc of skipped) {
|
|
2892
3095
|
onEvent?.({ type: "tool_call", phase: "skipped", name: tc.name, id: tc.id, args: tc.args });
|
|
@@ -2923,13 +3126,26 @@ function createToolsNode(initialTools, opts) {
|
|
|
2923
3126
|
}
|
|
2924
3127
|
state.ctx = Object.keys(ctx).length > 0 ? ctx : void 0;
|
|
2925
3128
|
}
|
|
3129
|
+
const appendedHasToolResult = appended.some((m) => m.role === "tool");
|
|
3130
|
+
if (appendedHasToolResult && state.ctx?.__summarizationExhausted) {
|
|
3131
|
+
const ctx = { ...state.ctx };
|
|
3132
|
+
delete ctx.__summarizationExhausted;
|
|
3133
|
+
state.ctx = Object.keys(ctx).length > 0 ? ctx : void 0;
|
|
3134
|
+
}
|
|
2926
3135
|
return {
|
|
2927
3136
|
messages: [...state.messages, ...appended],
|
|
2928
3137
|
toolCallCount: toolCount,
|
|
2929
3138
|
toolHistory,
|
|
2930
3139
|
toolHistoryArchived,
|
|
3140
|
+
toolCache: state.toolCache,
|
|
3141
|
+
// Explicitly propagate ctx so callers using `{ ...state, ...delta }`
|
|
3142
|
+
// do not silently capture a stale ctx reference. Mutations to
|
|
3143
|
+
// __summarizationExhausted / __awaitingApproval / __awaitingUserQuestion /
|
|
3144
|
+
// __structuredOutputParsed are made on this object during execution.
|
|
3145
|
+
ctx: state.ctx,
|
|
2931
3146
|
agent: state.agent,
|
|
2932
|
-
pendingApprovals
|
|
3147
|
+
pendingApprovals,
|
|
3148
|
+
pendingUserQuestions
|
|
2933
3149
|
};
|
|
2934
3150
|
};
|
|
2935
3151
|
}
|
|
@@ -3164,7 +3380,8 @@ function resolveReflection(input, preset) {
|
|
|
3164
3380
|
maxChars: preset.maxChars,
|
|
3165
3381
|
keepLast: 3,
|
|
3166
3382
|
summarize: false,
|
|
3167
|
-
emitEvents: true
|
|
3383
|
+
emitEvents: true,
|
|
3384
|
+
everyNTurns: 1
|
|
3168
3385
|
};
|
|
3169
3386
|
}
|
|
3170
3387
|
const cfg = input ?? {};
|
|
@@ -3179,7 +3396,9 @@ function resolveReflection(input, preset) {
|
|
|
3179
3396
|
keepLast: cfg.keepLast ?? 3,
|
|
3180
3397
|
summarize: cfg.summarize ?? false,
|
|
3181
3398
|
promptTemplate: cfg.promptTemplate,
|
|
3182
|
-
emitEvents: cfg.emitEvents ?? true
|
|
3399
|
+
emitEvents: cfg.emitEvents ?? true,
|
|
3400
|
+
maxPerRun: typeof cfg.maxPerRun === "number" && cfg.maxPerRun >= 0 ? cfg.maxPerRun : void 0,
|
|
3401
|
+
everyNTurns: Math.max(1, Math.trunc(cfg.everyNTurns ?? 1))
|
|
3183
3402
|
};
|
|
3184
3403
|
}
|
|
3185
3404
|
function ensureArray(value) {
|
|
@@ -3392,6 +3611,229 @@ function resolveToolApprovalState(state, resolution) {
|
|
|
3392
3611
|
};
|
|
3393
3612
|
}
|
|
3394
3613
|
|
|
3614
|
+
// src/utils/userQuestions.ts
|
|
3615
|
+
var cloneQuestions = (questions) => Array.isArray(questions) ? questions.map((entry) => ({ ...entry })) : [];
|
|
3616
|
+
function validateAnswers(entry, answers, cancelled) {
|
|
3617
|
+
if (cancelled) {
|
|
3618
|
+
return answers || {};
|
|
3619
|
+
}
|
|
3620
|
+
const allowFreeText = entry.allowFreeText !== false;
|
|
3621
|
+
const out = {};
|
|
3622
|
+
const provided = answers || {};
|
|
3623
|
+
for (const question of entry.questions) {
|
|
3624
|
+
const required = question.required !== false;
|
|
3625
|
+
const key = question.question;
|
|
3626
|
+
const answer = provided[key] ?? provided[question.header || ""];
|
|
3627
|
+
if (!answer) {
|
|
3628
|
+
if (required) {
|
|
3629
|
+
throw new Error(`Missing answer for required question: "${key}".`);
|
|
3630
|
+
}
|
|
3631
|
+
continue;
|
|
3632
|
+
}
|
|
3633
|
+
const values = Array.isArray(answer.values) ? answer.values.filter((v) => typeof v === "string") : [];
|
|
3634
|
+
const freeText = typeof answer.freeText === "string" ? answer.freeText.trim() : "";
|
|
3635
|
+
const notes = typeof answer.notes === "string" ? answer.notes : void 0;
|
|
3636
|
+
if (freeText && !allowFreeText) {
|
|
3637
|
+
throw new Error(
|
|
3638
|
+
`Free-text answers are disabled for this agent (question: "${key}"). Pick one of the provided options.`
|
|
3639
|
+
);
|
|
3640
|
+
}
|
|
3641
|
+
if (values.length === 0 && !freeText) {
|
|
3642
|
+
if (required) {
|
|
3643
|
+
throw new Error(`Question "${key}" requires either a selected option or free-text answer.`);
|
|
3644
|
+
}
|
|
3645
|
+
continue;
|
|
3646
|
+
}
|
|
3647
|
+
if (values.length > 1 && !question.multiSelect) {
|
|
3648
|
+
throw new Error(`Question "${key}" is single-select but received multiple values.`);
|
|
3649
|
+
}
|
|
3650
|
+
if (Array.isArray(question.options) && question.options.length > 0 && values.length > 0) {
|
|
3651
|
+
const allowedValues = new Set(
|
|
3652
|
+
question.options.map((option) => option.value ?? option.label)
|
|
3653
|
+
);
|
|
3654
|
+
for (const value of values) {
|
|
3655
|
+
if (!allowedValues.has(value)) {
|
|
3656
|
+
if (!allowFreeText) {
|
|
3657
|
+
throw new Error(
|
|
3658
|
+
`Value "${value}" is not one of the allowed options for question "${key}".`
|
|
3659
|
+
);
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
out[key] = { values, ...freeText ? { freeText } : {}, ...notes ? { notes } : {} };
|
|
3665
|
+
}
|
|
3666
|
+
return out;
|
|
3667
|
+
}
|
|
3668
|
+
function buildToolResponseContent(entry, answers, cancelled, notes) {
|
|
3669
|
+
if (cancelled) {
|
|
3670
|
+
return JSON.stringify({
|
|
3671
|
+
status: "cancelled",
|
|
3672
|
+
message: notes || "User dismissed the question without answering."
|
|
3673
|
+
});
|
|
3674
|
+
}
|
|
3675
|
+
const payload = {
|
|
3676
|
+
status: "answered",
|
|
3677
|
+
answers: entry.questions.map((question) => {
|
|
3678
|
+
const key = question.question;
|
|
3679
|
+
const ans = answers[key];
|
|
3680
|
+
return {
|
|
3681
|
+
question: key,
|
|
3682
|
+
header: question.header,
|
|
3683
|
+
values: ans?.values ?? [],
|
|
3684
|
+
freeText: ans?.freeText,
|
|
3685
|
+
notes: ans?.notes
|
|
3686
|
+
};
|
|
3687
|
+
}),
|
|
3688
|
+
...notes ? { reviewerNotes: notes } : {}
|
|
3689
|
+
};
|
|
3690
|
+
return JSON.stringify(payload);
|
|
3691
|
+
}
|
|
3692
|
+
function resolveUserQuestionState(state, resolution) {
|
|
3693
|
+
const pending = cloneQuestions(state.pendingUserQuestions);
|
|
3694
|
+
const matchIndex = pending.findIndex(
|
|
3695
|
+
(entry2) => entry2.id === resolution.id || entry2.toolCallId === resolution.id
|
|
3696
|
+
);
|
|
3697
|
+
if (matchIndex === -1) {
|
|
3698
|
+
throw new Error(`Pending user question not found for id: ${resolution.id}`);
|
|
3699
|
+
}
|
|
3700
|
+
const entry = { ...pending[matchIndex] };
|
|
3701
|
+
if (entry.status === "answered" || entry.status === "executed" || entry.status === "cancelled") {
|
|
3702
|
+
throw new Error(`User question ${entry.id} already resolved (status: ${entry.status}).`);
|
|
3703
|
+
}
|
|
3704
|
+
const cancelled = resolution.cancelled === true;
|
|
3705
|
+
const validated = validateAnswers(entry, resolution.answers, cancelled);
|
|
3706
|
+
entry.status = cancelled ? "cancelled" : "answered";
|
|
3707
|
+
entry.answers = validated;
|
|
3708
|
+
entry.cancelled = cancelled || void 0;
|
|
3709
|
+
entry.answeredBy = resolution.answeredBy;
|
|
3710
|
+
entry.notes = resolution.notes;
|
|
3711
|
+
entry.answeredAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3712
|
+
pending[matchIndex] = entry;
|
|
3713
|
+
const ctx = { ...state.ctx || {} };
|
|
3714
|
+
delete ctx.__awaitingUserQuestion;
|
|
3715
|
+
if (ctx.__resumeStage === "tools") {
|
|
3716
|
+
delete ctx.__resumeStage;
|
|
3717
|
+
}
|
|
3718
|
+
ctx.__userQuestionResolved = { id: entry.id, status: entry.status };
|
|
3719
|
+
const toolMessage = {
|
|
3720
|
+
role: "tool",
|
|
3721
|
+
name: "ask_user_question",
|
|
3722
|
+
tool_call_id: entry.toolCallId,
|
|
3723
|
+
content: buildToolResponseContent(entry, validated, cancelled, resolution.notes)
|
|
3724
|
+
};
|
|
3725
|
+
return {
|
|
3726
|
+
...state,
|
|
3727
|
+
pendingUserQuestions: pending,
|
|
3728
|
+
messages: [...state.messages || [], toolMessage],
|
|
3729
|
+
ctx
|
|
3730
|
+
};
|
|
3731
|
+
}
|
|
3732
|
+
var DEFAULT_DESCRIPTION_WITH_FREETEXT = "Pause the agent and surface a structured prompt to the human user when you genuinely cannot proceed without their input. Group up to 4 closely related questions into a single call. Prefer concrete options the user can pick over open-ended free-text \u2014 list `options` whenever you can anticipate the choices. The user may type a custom answer for any question. Do not use this tool for things you can deduce from the conversation, the system prompt, or another tool. After the user answers, their selections come back as the tool result.";
|
|
3733
|
+
var DEFAULT_DESCRIPTION_NO_FREETEXT = "Pause the agent and surface a structured multi-choice prompt to the human user when you genuinely cannot proceed without their input. Group up to 4 closely related questions into a single call. Every question MUST include at least two `options` \u2014 free-text answers are disabled for this agent. Do not use this tool for things you can deduce from the conversation, the system prompt, or another tool. After the user picks, their selections come back as the tool result.";
|
|
3734
|
+
function buildOptionSchema() {
|
|
3735
|
+
return zod.z.object({
|
|
3736
|
+
label: zod.z.string().min(1).describe("Display text the user sees"),
|
|
3737
|
+
value: zod.z.string().min(1).optional().describe("Value returned in the answer; defaults to label"),
|
|
3738
|
+
description: zod.z.string().optional(),
|
|
3739
|
+
preview: zod.z.string().optional().describe("Optional code / ASCII preview rendered when this option is focused")
|
|
3740
|
+
}).strict();
|
|
3741
|
+
}
|
|
3742
|
+
function buildQuestionSchema(allowFreeText) {
|
|
3743
|
+
const optionSchema = buildOptionSchema();
|
|
3744
|
+
const baseShape = {
|
|
3745
|
+
question: zod.z.string().min(1).describe("Full question shown to the user"),
|
|
3746
|
+
header: zod.z.string().max(40).optional().describe("Short chip / tag rendered next to the question (max ~12 chars recommended)"),
|
|
3747
|
+
multiSelect: zod.z.boolean().optional().describe("Allow multiple selections. Default: false"),
|
|
3748
|
+
placeholder: zod.z.string().optional(),
|
|
3749
|
+
required: zod.z.boolean().optional().describe("Default: true")
|
|
3750
|
+
};
|
|
3751
|
+
if (allowFreeText) {
|
|
3752
|
+
return zod.z.object({
|
|
3753
|
+
...baseShape,
|
|
3754
|
+
options: zod.z.array(optionSchema).min(2).max(12).optional()
|
|
3755
|
+
}).strict();
|
|
3756
|
+
}
|
|
3757
|
+
return zod.z.object({
|
|
3758
|
+
...baseShape,
|
|
3759
|
+
options: zod.z.array(optionSchema).min(2).max(12).describe("Required: free-text answers are disabled for this agent")
|
|
3760
|
+
}).strict();
|
|
3761
|
+
}
|
|
3762
|
+
function createAskUserQuestionTool(stateRef, config) {
|
|
3763
|
+
const allowFreeText = config.allowFreeText !== false;
|
|
3764
|
+
const description = config.promptOverride ?? (allowFreeText ? DEFAULT_DESCRIPTION_WITH_FREETEXT : DEFAULT_DESCRIPTION_NO_FREETEXT);
|
|
3765
|
+
const questionSchema = buildQuestionSchema(allowFreeText);
|
|
3766
|
+
const askUser = createTool({
|
|
3767
|
+
name: "ask_user_question",
|
|
3768
|
+
description,
|
|
3769
|
+
schema: zod.z.object({
|
|
3770
|
+
questions: zod.z.array(questionSchema).min(1).max(4).describe("1-4 related questions to ask in a single pause.")
|
|
3771
|
+
}).strict(),
|
|
3772
|
+
func: async ({ questions }) => {
|
|
3773
|
+
const ref = askUser._stateRef || (askUser._stateRef = {});
|
|
3774
|
+
const onEvent = ref.__onEvent;
|
|
3775
|
+
if (!ref.ctx) ref.ctx = {};
|
|
3776
|
+
const ctx = ref.ctx;
|
|
3777
|
+
const toolCallId = ref.__currentToolCallId || `ask_user_${nanoid.nanoid(6)}`;
|
|
3778
|
+
if (!ref.pendingUserQuestions) ref.pendingUserQuestions = [];
|
|
3779
|
+
const pendingList = ref.pendingUserQuestions;
|
|
3780
|
+
const entry = {
|
|
3781
|
+
id: nanoid.nanoid(),
|
|
3782
|
+
toolCallId,
|
|
3783
|
+
toolName: "ask_user_question",
|
|
3784
|
+
questions,
|
|
3785
|
+
status: "pending",
|
|
3786
|
+
requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3787
|
+
allowFreeText
|
|
3788
|
+
};
|
|
3789
|
+
pendingList.push(entry);
|
|
3790
|
+
ctx.__awaitingUserQuestion = {
|
|
3791
|
+
id: entry.id,
|
|
3792
|
+
toolCallId: entry.toolCallId,
|
|
3793
|
+
requestedAt: entry.requestedAt
|
|
3794
|
+
};
|
|
3795
|
+
ctx.__resumeStage = "tools";
|
|
3796
|
+
onEvent?.({
|
|
3797
|
+
type: "user_question",
|
|
3798
|
+
status: "pending",
|
|
3799
|
+
id: entry.id,
|
|
3800
|
+
toolCallId: entry.toolCallId,
|
|
3801
|
+
questions: entry.questions,
|
|
3802
|
+
allowFreeText
|
|
3803
|
+
});
|
|
3804
|
+
recordTraceEvent(ctx.__traceSession, {
|
|
3805
|
+
type: "user_question",
|
|
3806
|
+
label: "Ask User",
|
|
3807
|
+
actor: { scope: "agent", name: "ask_user_question", role: "user_interaction" },
|
|
3808
|
+
status: "success",
|
|
3809
|
+
sections: [
|
|
3810
|
+
{
|
|
3811
|
+
kind: "metadata",
|
|
3812
|
+
label: "Pending Question",
|
|
3813
|
+
data: {
|
|
3814
|
+
id: entry.id,
|
|
3815
|
+
toolCallId: entry.toolCallId,
|
|
3816
|
+
questions: entry.questions,
|
|
3817
|
+
allowFreeText
|
|
3818
|
+
}
|
|
3819
|
+
}
|
|
3820
|
+
]
|
|
3821
|
+
});
|
|
3822
|
+
return {
|
|
3823
|
+
__awaitingUserQuestion: true,
|
|
3824
|
+
id: entry.id,
|
|
3825
|
+
toolCallId: entry.toolCallId
|
|
3826
|
+
};
|
|
3827
|
+
}
|
|
3828
|
+
});
|
|
3829
|
+
askUser._stateRef = stateRef;
|
|
3830
|
+
askUser.__source = "human-in-the-loop";
|
|
3831
|
+
return askUser;
|
|
3832
|
+
}
|
|
3833
|
+
|
|
3834
|
+
// src/agent.ts
|
|
3835
|
+
init_utilTokens();
|
|
3836
|
+
|
|
3395
3837
|
// src/utils/syntheticMessages.ts
|
|
3396
3838
|
function isSyntheticSummaryMessage(message) {
|
|
3397
3839
|
if (!message) return false;
|
|
@@ -3570,6 +4012,21 @@ function getActiveSummarizationThreshold(opts) {
|
|
|
3570
4012
|
}
|
|
3571
4013
|
return summarization.summaryTriggerTokens || summarization.maxTokens || opts.limits?.maxContextTokens || 5e4;
|
|
3572
4014
|
}
|
|
4015
|
+
function shouldSignalSummarization(state, threshold) {
|
|
4016
|
+
const tokCountMessages = (state.messages || []).filter((message) => {
|
|
4017
|
+
if (isSyntheticSummaryMessage(message)) return false;
|
|
4018
|
+
if (message.role === "system" && (message.name === "context_summary" || message.name === "memory_context")) return false;
|
|
4019
|
+
return true;
|
|
4020
|
+
});
|
|
4021
|
+
const tokenCount = countMessagesTokens(tokCountMessages);
|
|
4022
|
+
if (tokenCount <= threshold) return "skip";
|
|
4023
|
+
if (state.ctx?.__summarizationExhausted) return "skip";
|
|
4024
|
+
const hasFreshSummary = (state.messages || []).some(
|
|
4025
|
+
(m) => m.role === "tool" && typeof m.content === "string" && (m.content === "SUMMARIZED" || m.content.startsWith("SUMMARIZED_TOOL_RESPONSE"))
|
|
4026
|
+
);
|
|
4027
|
+
if (hasFreshSummary && tokenCount <= threshold * 1.15) return "skip";
|
|
4028
|
+
return "trigger";
|
|
4029
|
+
}
|
|
3573
4030
|
function clearNeedsSummarization(state) {
|
|
3574
4031
|
if (!state.ctx?.__needsSummarization) {
|
|
3575
4032
|
return state;
|
|
@@ -3585,6 +4042,21 @@ function createAgent(opts) {
|
|
|
3585
4042
|
const resolver = createResolverNode();
|
|
3586
4043
|
const agentCore = createAgentCoreNode(opts);
|
|
3587
4044
|
const toolsBase = [...opts.tools ?? []];
|
|
4045
|
+
const askUserConfig = (() => {
|
|
4046
|
+
const raw = opts.humanInTheLoop?.askUser;
|
|
4047
|
+
if (!raw) return void 0;
|
|
4048
|
+
if (raw === true) return { enabled: true, allowFreeText: true };
|
|
4049
|
+
return {
|
|
4050
|
+
enabled: true,
|
|
4051
|
+
allowFreeText: raw.allowFreeText !== false,
|
|
4052
|
+
promptOverride: raw.promptOverride
|
|
4053
|
+
};
|
|
4054
|
+
})();
|
|
4055
|
+
const askUserOnQuestion = typeof opts.humanInTheLoop?.askUser === "object" ? opts.humanInTheLoop?.askUser?.onQuestion : void 0;
|
|
4056
|
+
const askUserStateRef = askUserConfig ? { pendingUserQuestions: void 0, ctx: void 0, __onEvent: void 0, __currentToolCallId: void 0 } : void 0;
|
|
4057
|
+
if (askUserConfig && askUserStateRef) {
|
|
4058
|
+
toolsBase.push(createAskUserQuestionTool(askUserStateRef, askUserConfig));
|
|
4059
|
+
}
|
|
3588
4060
|
const soManager = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
|
|
3589
4061
|
if (soManager) {
|
|
3590
4062
|
const modelCapabilities = getModelCapabilities(opts.model);
|
|
@@ -3629,7 +4101,8 @@ function createAgent(opts) {
|
|
|
3629
4101
|
useTodoList: void 0,
|
|
3630
4102
|
outputSchema: opts.outputSchema,
|
|
3631
4103
|
responseFormat: soManager?.getResponseFormat(),
|
|
3632
|
-
tracing: opts.tracing
|
|
4104
|
+
tracing: opts.tracing,
|
|
4105
|
+
humanInTheLoop: askUserConfig ? { askUser: askUserConfig } : void 0
|
|
3633
4106
|
};
|
|
3634
4107
|
const summarizationThreshold = getActiveSummarizationThreshold(opts);
|
|
3635
4108
|
async function runLoop(initial, config, emit) {
|
|
@@ -3664,6 +4137,45 @@ function createAgent(opts) {
|
|
|
3664
4137
|
};
|
|
3665
4138
|
const maxToolCalls = mergedLimits?.maxToolCalls === void 0 ? 50 : mergedLimits?.maxToolCalls;
|
|
3666
4139
|
const iterationLimit = maxToolCalls === Infinity ? 100 : Math.max(maxToolCalls * 3 + 10, 40);
|
|
4140
|
+
const wallClockStart = Date.now();
|
|
4141
|
+
const maxWallClockMs = mergedLimits?.maxWallClockMs && mergedLimits.maxWallClockMs > 0 ? mergedLimits.maxWallClockMs : void 0;
|
|
4142
|
+
const maxTotalOutputTokens = mergedLimits?.maxTotalOutputTokens && mergedLimits.maxTotalOutputTokens > 0 ? mergedLimits.maxTotalOutputTokens : void 0;
|
|
4143
|
+
const maxCostUsd = mergedLimits?.maxCostUsd && mergedLimits.maxCostUsd > 0 ? mergedLimits.maxCostUsd : void 0;
|
|
4144
|
+
const costEstimator = opts.costEstimator;
|
|
4145
|
+
const checkBudgetBreached = () => {
|
|
4146
|
+
if (maxWallClockMs && Date.now() - wallClockStart > maxWallClockMs) {
|
|
4147
|
+
return { breached: true, reason: `maxWallClockMs (${maxWallClockMs}ms) exceeded` };
|
|
4148
|
+
}
|
|
4149
|
+
if (maxTotalOutputTokens) {
|
|
4150
|
+
const totals = state.usage?.totals;
|
|
4151
|
+
if (totals) {
|
|
4152
|
+
let outputSum = 0;
|
|
4153
|
+
for (const v of Object.values(totals)) outputSum += Number(v?.output) || 0;
|
|
4154
|
+
if (outputSum > maxTotalOutputTokens) {
|
|
4155
|
+
return { breached: true, reason: `maxTotalOutputTokens (${maxTotalOutputTokens}) exceeded` };
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
}
|
|
4159
|
+
if (maxCostUsd && costEstimator) {
|
|
4160
|
+
const perRequest = state.usage?.perRequest;
|
|
4161
|
+
if (perRequest) {
|
|
4162
|
+
let cost = 0;
|
|
4163
|
+
for (const r of perRequest) {
|
|
4164
|
+
cost += costEstimator({
|
|
4165
|
+
modelName: r.modelName,
|
|
4166
|
+
inputTokens: Number(r.usage?.prompt_tokens) || 0,
|
|
4167
|
+
outputTokens: Number(r.usage?.completion_tokens) || 0,
|
|
4168
|
+
cachedInputTokens: Number(r.usage?.prompt_tokens_details?.cached_tokens) || 0,
|
|
4169
|
+
reasoningTokens: Number(r.usage?.completion_tokens_details?.reasoning_tokens) || 0
|
|
4170
|
+
}) || 0;
|
|
4171
|
+
}
|
|
4172
|
+
if (cost > maxCostUsd) {
|
|
4173
|
+
return { breached: true, reason: `maxCostUsd ($${maxCostUsd}) exceeded (actual: $${cost.toFixed(4)})` };
|
|
4174
|
+
}
|
|
4175
|
+
}
|
|
4176
|
+
}
|
|
4177
|
+
return { breached: false };
|
|
4178
|
+
};
|
|
3667
4179
|
let iterations = 0;
|
|
3668
4180
|
const onStateChange = config?.onStateChange;
|
|
3669
4181
|
const checkpointReason = config?.checkpointReason;
|
|
@@ -3712,6 +4224,13 @@ function createAgent(opts) {
|
|
|
3712
4224
|
};
|
|
3713
4225
|
while (iterations < iterationLimit) {
|
|
3714
4226
|
iterations++;
|
|
4227
|
+
const budget = checkBudgetBreached();
|
|
4228
|
+
if (budget.breached) {
|
|
4229
|
+
emit?.({ type: "metadata", limitBreached: budget.reason });
|
|
4230
|
+
const ctx = { ...state.ctx || {}, __limitBreached: budget.reason };
|
|
4231
|
+
state = { ...state, ctx };
|
|
4232
|
+
break;
|
|
4233
|
+
}
|
|
3715
4234
|
const traceSession = state.ctx?.__traceSession;
|
|
3716
4235
|
if (traceSession) {
|
|
3717
4236
|
const iterEvent = recordTraceEvent(traceSession, {
|
|
@@ -3767,22 +4286,11 @@ function createAgent(opts) {
|
|
|
3767
4286
|
}
|
|
3768
4287
|
}
|
|
3769
4288
|
if (summarizationThreshold !== void 0) {
|
|
3770
|
-
const
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
const tokenCount = countMessagesTokens(tokCountMessages);
|
|
3776
|
-
if (tokenCount > summarizationThreshold) {
|
|
3777
|
-
const summarizationExhausted = !!state.ctx?.__summarizationExhausted;
|
|
3778
|
-
const hasFreshSummary = (state.messages || []).some(
|
|
3779
|
-
(m) => m.role === "tool" && typeof m.content === "string" && (m.content === "SUMMARIZED" || m.content.startsWith("SUMMARIZED_TOOL_RESPONSE"))
|
|
3780
|
-
);
|
|
3781
|
-
if (hasFreshSummary && tokenCount <= summarizationThreshold * 1.15) ; else if (summarizationExhausted) ; else {
|
|
3782
|
-
const ctx = { ...state.ctx || {}, __needsSummarization: true };
|
|
3783
|
-
state = { ...state, ctx };
|
|
3784
|
-
break;
|
|
3785
|
-
}
|
|
4289
|
+
const verdict = shouldSignalSummarization(state, summarizationThreshold);
|
|
4290
|
+
if (verdict === "trigger") {
|
|
4291
|
+
const ctx = { ...state.ctx || {}, __needsSummarization: true };
|
|
4292
|
+
state = { ...state, ctx };
|
|
4293
|
+
break;
|
|
3786
4294
|
}
|
|
3787
4295
|
}
|
|
3788
4296
|
onProgress?.({ stage: "agent", message: "Invoking model" });
|
|
@@ -3884,11 +4392,18 @@ function createAgent(opts) {
|
|
|
3884
4392
|
state = { ...state, ...await toolsNode(state) };
|
|
3885
4393
|
onProgress?.({ stage: "tools", message: "Tools finished" });
|
|
3886
4394
|
if (state.ctx?.__awaitingApproval) break;
|
|
4395
|
+
if (state.ctx?.__awaitingUserQuestion) break;
|
|
3887
4396
|
if (checkpointIfRequested("after_tools")) break;
|
|
3888
4397
|
if (state.ctx?.__finalizedDueToStructuredOutput) break;
|
|
3889
4398
|
if (reflectNode && resolvedReasoning?.reflection.enabled) {
|
|
3890
4399
|
const cadence = resolvedReasoning.reflection.cadence;
|
|
3891
|
-
|
|
4400
|
+
const refl = resolvedReasoning.reflection;
|
|
4401
|
+
const reflectionsSoFar = state.reflections?.length ?? 0;
|
|
4402
|
+
const lastTurn = state.reflections?.at(-1)?.turn ?? -Infinity;
|
|
4403
|
+
const currentTurn = state.toolCallCount || 0;
|
|
4404
|
+
const budgetExceeded = typeof refl.maxPerRun === "number" && reflectionsSoFar >= refl.maxPerRun;
|
|
4405
|
+
const cadenceGap = currentTurn - (Number.isFinite(lastTurn) ? lastTurn : -refl.everyNTurns) < refl.everyNTurns;
|
|
4406
|
+
if (!budgetExceeded && !cadenceGap && shouldRunReflection(cadence, state, true)) {
|
|
3892
4407
|
try {
|
|
3893
4408
|
const patch = await reflectNode(state, cadence);
|
|
3894
4409
|
if (patch && Object.keys(patch).length > 0) {
|
|
@@ -3948,7 +4463,7 @@ function createAgent(opts) {
|
|
|
3948
4463
|
}
|
|
3949
4464
|
const lastAfterLoop = state.messages[state.messages.length - 1];
|
|
3950
4465
|
if (lastAfterLoop?.role === "tool" && !pausedStage) {
|
|
3951
|
-
const isExpectedExit = state.ctx?.__awaitingApproval || state.ctx?.__cancelled || state.ctx?.__finalizedDueToStructuredOutput || state.ctx?.__finalizedDueToToolLimit || state.ctx?.__needsSummarization;
|
|
4466
|
+
const isExpectedExit = state.ctx?.__awaitingApproval || state.ctx?.__awaitingUserQuestion || state.ctx?.__cancelled || state.ctx?.__finalizedDueToStructuredOutput || state.ctx?.__finalizedDueToToolLimit || state.ctx?.__needsSummarization || state.ctx?.__limitBreached;
|
|
3952
4467
|
if (!isExpectedExit) {
|
|
3953
4468
|
throw new Error(
|
|
3954
4469
|
"Agent loop terminated with a pending tool response but no subsequent model invocation. This usually indicates a model provider error or exhausted iteration budget."
|
|
@@ -3961,7 +4476,22 @@ function createAgent(opts) {
|
|
|
3961
4476
|
return state;
|
|
3962
4477
|
}
|
|
3963
4478
|
const invokeAgent = async (input, config) => {
|
|
3964
|
-
const
|
|
4479
|
+
const userTokenCounter = opts.tokenCounter;
|
|
4480
|
+
if (userTokenCounter) {
|
|
4481
|
+
const { setTokenCounter: setTokenCounter2 } = await Promise.resolve().then(() => (init_utilTokens(), utilTokens_exports));
|
|
4482
|
+
setTokenCounter2(userTokenCounter);
|
|
4483
|
+
}
|
|
4484
|
+
const callerOnEvent = config?.onEvent;
|
|
4485
|
+
const onEvent = askUserOnQuestion ? (event) => {
|
|
4486
|
+
if (event && event.type === "user_question") {
|
|
4487
|
+
try {
|
|
4488
|
+
askUserOnQuestion(event);
|
|
4489
|
+
} catch (err) {
|
|
4490
|
+
console.warn("[agent-sdk] humanInTheLoop.askUser.onQuestion error:", err);
|
|
4491
|
+
}
|
|
4492
|
+
}
|
|
4493
|
+
callerOnEvent?.(event);
|
|
4494
|
+
} : callerOnEvent;
|
|
3965
4495
|
const onProgress = config?.onProgress;
|
|
3966
4496
|
const onStream = config?.onStream;
|
|
3967
4497
|
const streamEnabled = config?.stream === true;
|
|
@@ -4005,6 +4535,7 @@ function createAgent(opts) {
|
|
|
4005
4535
|
metadata: input.metadata,
|
|
4006
4536
|
ctx,
|
|
4007
4537
|
pendingApprovals: input.pendingApprovals || [],
|
|
4538
|
+
pendingUserQuestions: input.pendingUserQuestions || [],
|
|
4008
4539
|
agent: input.agent || runtimeWithInvokeLimits,
|
|
4009
4540
|
usage: input.usage || { perRequest: [], totals: {} }
|
|
4010
4541
|
};
|
|
@@ -4018,7 +4549,19 @@ function createAgent(opts) {
|
|
|
4018
4549
|
status: "error",
|
|
4019
4550
|
error: { message: err?.message, stack: err?.stack }
|
|
4020
4551
|
});
|
|
4552
|
+
if (userTokenCounter) {
|
|
4553
|
+
const { setTokenCounter: setTokenCounter2 } = await Promise.resolve().then(() => (init_utilTokens(), utilTokens_exports));
|
|
4554
|
+
setTokenCounter2(void 0);
|
|
4555
|
+
}
|
|
4021
4556
|
throw err;
|
|
4557
|
+
} finally {
|
|
4558
|
+
if (userTokenCounter) {
|
|
4559
|
+
try {
|
|
4560
|
+
const { setTokenCounter: setTokenCounter2 } = await Promise.resolve().then(() => (init_utilTokens(), utilTokens_exports));
|
|
4561
|
+
setTokenCounter2(void 0);
|
|
4562
|
+
} catch {
|
|
4563
|
+
}
|
|
4564
|
+
}
|
|
4022
4565
|
}
|
|
4023
4566
|
await finalizeTraceSession(traceSession, {
|
|
4024
4567
|
agentRuntime: res.agent || runtime,
|
|
@@ -4069,19 +4612,67 @@ function createAgent(opts) {
|
|
|
4069
4612
|
return invokeAgent(restoredState, config);
|
|
4070
4613
|
};
|
|
4071
4614
|
const resolveToolApproval = (state, resolution) => resolveToolApprovalState(state, resolution);
|
|
4615
|
+
const resolveUserQuestion = (state, resolution) => resolveUserQuestionState(state, resolution);
|
|
4072
4616
|
const instance = {
|
|
4073
4617
|
invoke: invokeAgent,
|
|
4074
4618
|
snapshot: snapshotState,
|
|
4075
4619
|
resume: resumeAgent,
|
|
4076
4620
|
resolveToolApproval,
|
|
4621
|
+
resolveUserQuestion,
|
|
4077
4622
|
asTool: ({ toolName, description, inputDescription }) => {
|
|
4078
4623
|
const schema = zod.z.object({ input: zod.z.string().describe(inputDescription || "Input for delegated agent") });
|
|
4079
|
-
|
|
4624
|
+
const delegatedTool = createTool({
|
|
4080
4625
|
name: toolName,
|
|
4081
4626
|
description: description || `Delegate task to agent ${opts.name || "Agent"}`,
|
|
4082
4627
|
schema,
|
|
4083
4628
|
func: async ({ input }) => {
|
|
4084
|
-
const
|
|
4629
|
+
const ref = delegatedTool._stateRef;
|
|
4630
|
+
const parentCtx = ref.ctx || {};
|
|
4631
|
+
const parentMessages = Array.isArray(ref.messages) ? ref.messages : [];
|
|
4632
|
+
const parentRuntime = ref.parentRuntime;
|
|
4633
|
+
const resolvedSmart = parentRuntime?.smart || opts.__resolvedSmart;
|
|
4634
|
+
const delegationPolicy = resolvedSmart?.delegation || {
|
|
4635
|
+
mode: "role_based",
|
|
4636
|
+
maxDelegationDepth: 2,
|
|
4637
|
+
maxChildCalls: 4,
|
|
4638
|
+
childContextPolicy: "scoped"
|
|
4639
|
+
};
|
|
4640
|
+
const currentDepth = Number(parentCtx.__delegationDepth) || 0;
|
|
4641
|
+
if (delegationPolicy.mode === "off") {
|
|
4642
|
+
return { error: "Delegation disabled by runtime policy." };
|
|
4643
|
+
}
|
|
4644
|
+
if (currentDepth >= delegationPolicy.maxDelegationDepth) {
|
|
4645
|
+
return {
|
|
4646
|
+
error: `Delegation depth limit reached (${delegationPolicy.maxDelegationDepth}). Refusing nested call.`
|
|
4647
|
+
};
|
|
4648
|
+
}
|
|
4649
|
+
parentCtx.__delegatedCallCount = (parentCtx.__delegatedCallCount || 0) + 1;
|
|
4650
|
+
if (parentCtx.__delegatedCallCount > delegationPolicy.maxChildCalls) {
|
|
4651
|
+
return {
|
|
4652
|
+
error: `Child-call budget exhausted (${delegationPolicy.maxChildCalls}).`
|
|
4653
|
+
};
|
|
4654
|
+
}
|
|
4655
|
+
let childMessages;
|
|
4656
|
+
switch (delegationPolicy.childContextPolicy) {
|
|
4657
|
+
case "full":
|
|
4658
|
+
childMessages = [...parentMessages, { role: "user", content: input }];
|
|
4659
|
+
break;
|
|
4660
|
+
case "scoped": {
|
|
4661
|
+
const systemPrefix = parentMessages[0]?.role === "system" ? [parentMessages[0]] : [];
|
|
4662
|
+
const lastUser = [...parentMessages].reverse().find((m) => m?.role === "user");
|
|
4663
|
+
childMessages = [
|
|
4664
|
+
...systemPrefix,
|
|
4665
|
+
...lastUser ? [lastUser] : [],
|
|
4666
|
+
{ role: "user", content: input }
|
|
4667
|
+
];
|
|
4668
|
+
break;
|
|
4669
|
+
}
|
|
4670
|
+
case "minimal":
|
|
4671
|
+
default:
|
|
4672
|
+
childMessages = [{ role: "user", content: input }];
|
|
4673
|
+
}
|
|
4674
|
+
const childCtx = { __delegationDepth: currentDepth + 1 };
|
|
4675
|
+
const res = await instance.invoke({ messages: childMessages, ctx: childCtx });
|
|
4085
4676
|
return {
|
|
4086
4677
|
content: res.content,
|
|
4087
4678
|
output: res.output,
|
|
@@ -4089,6 +4680,8 @@ function createAgent(opts) {
|
|
|
4089
4680
|
};
|
|
4090
4681
|
}
|
|
4091
4682
|
});
|
|
4683
|
+
delegatedTool._stateRef = {};
|
|
4684
|
+
return delegatedTool;
|
|
4092
4685
|
},
|
|
4093
4686
|
asHandoff: ({ toolName, description, schema }) => {
|
|
4094
4687
|
const finalName = toolName || `handoff_to_${runtime.name || "agent"}`;
|
|
@@ -4228,7 +4821,11 @@ function createAgentNode(opts) {
|
|
|
4228
4821
|
};
|
|
4229
4822
|
}
|
|
4230
4823
|
|
|
4824
|
+
// src/nodes/contextSummarize.ts
|
|
4825
|
+
init_utilTokens();
|
|
4826
|
+
|
|
4231
4827
|
// src/smart/contextPolicy.ts
|
|
4828
|
+
init_utilTokens();
|
|
4232
4829
|
function collectRecentTurns(messages, lastTurnsToKeep) {
|
|
4233
4830
|
if (lastTurnsToKeep <= 0) return [];
|
|
4234
4831
|
const systemPrefix = messages.filter((message, index) => index === 0 && message.role === "system");
|
|
@@ -4531,7 +5128,7 @@ function createContextSummarizeNode(opts) {
|
|
|
4531
5128
|
const resolved = getResolvedSmartConfig(opts, state.agent);
|
|
4532
5129
|
const config = resolved.summarization;
|
|
4533
5130
|
if (!config.enable) return {};
|
|
4534
|
-
const model = opts.model;
|
|
5131
|
+
const model = state.agent?.model || opts.model;
|
|
4535
5132
|
const messages = state.messages || [];
|
|
4536
5133
|
const onEvent = state.ctx?.__onEvent;
|
|
4537
5134
|
const traceSession = state.ctx?.__traceSession;
|
|
@@ -4850,6 +5447,10 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
|
|
|
4850
5447
|
};
|
|
4851
5448
|
};
|
|
4852
5449
|
}
|
|
5450
|
+
|
|
5451
|
+
// src/index.ts
|
|
5452
|
+
init_utilTokens();
|
|
5453
|
+
init_utilTokens();
|
|
4853
5454
|
var TOOL_RESPONSE_RECOVERY_MARKERS = [
|
|
4854
5455
|
/ARCHIVED_TOOL_RESPONSE\s*\[/,
|
|
4855
5456
|
/STRUCTURED_TOOL_RESPONSE\s*\[/,
|
|
@@ -5168,6 +5769,7 @@ function createContextTools(stateRef, opts) {
|
|
|
5168
5769
|
}
|
|
5169
5770
|
|
|
5170
5771
|
// src/graph/decisions.ts
|
|
5772
|
+
init_utilTokens();
|
|
5171
5773
|
function needsSummarization(state, opts, summarizationEnabled) {
|
|
5172
5774
|
if (!summarizationEnabled) {
|
|
5173
5775
|
return false;
|
|
@@ -5330,12 +5932,38 @@ function createSmartAgent(opts) {
|
|
|
5330
5932
|
toolResponses: resolved.toolResponses,
|
|
5331
5933
|
useTodoList: planningEnabled
|
|
5332
5934
|
};
|
|
5333
|
-
const
|
|
5334
|
-
const
|
|
5335
|
-
const
|
|
5336
|
-
const
|
|
5337
|
-
|
|
5338
|
-
|
|
5935
|
+
const userTools = opts.tools ?? [];
|
|
5936
|
+
const factoryStateRef = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
|
|
5937
|
+
const factoryContextTools = createContextTools(factoryStateRef, { planningEnabled, includeGetToolResponse: false });
|
|
5938
|
+
const askUserConfig = (() => {
|
|
5939
|
+
const raw = opts.humanInTheLoop?.askUser;
|
|
5940
|
+
if (!raw) return void 0;
|
|
5941
|
+
if (raw === true) return { enabled: true, allowFreeText: true };
|
|
5942
|
+
return {
|
|
5943
|
+
enabled: true,
|
|
5944
|
+
allowFreeText: raw.allowFreeText !== false,
|
|
5945
|
+
promptOverride: raw.promptOverride
|
|
5946
|
+
};
|
|
5947
|
+
})();
|
|
5948
|
+
const factoryToolsWithoutRecovery = [...userTools, ...factoryContextTools];
|
|
5949
|
+
if (askUserConfig) {
|
|
5950
|
+
factoryToolsWithoutRecovery.push(createAskUserQuestionTool(factoryStateRef, askUserConfig));
|
|
5951
|
+
}
|
|
5952
|
+
const soManagerFactory = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
|
|
5953
|
+
const modelCapabilities = opts.outputSchema ? getModelCapabilities(opts.model) : void 0;
|
|
5954
|
+
const responseFinalizeTool = soManagerFactory && modelCapabilities?.structuredOutput !== "native" ? soManagerFactory.getResponseTool() : void 0;
|
|
5955
|
+
const buildInvokeToolSet = () => {
|
|
5956
|
+
const ref = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
|
|
5957
|
+
const ctxTools = createContextTools(ref, { planningEnabled, includeGetToolResponse: false });
|
|
5958
|
+
const recoveryTool = createGetToolResponseTool(ref);
|
|
5959
|
+
const base2 = [...userTools, ...ctxTools];
|
|
5960
|
+
if (responseFinalizeTool) base2.push(responseFinalizeTool);
|
|
5961
|
+
if (askUserConfig) base2.push(createAskUserQuestionTool(ref, askUserConfig));
|
|
5962
|
+
const without = base2;
|
|
5963
|
+
const withRec = [...without, recoveryTool];
|
|
5964
|
+
return { stateRef: ref, toolsWithoutRecovery: without, toolsWithRecovery: withRec };
|
|
5965
|
+
};
|
|
5966
|
+
const base = createAgent({ ...runtimeOpts, tools: factoryToolsWithoutRecovery });
|
|
5339
5967
|
base.__runtime.runtimeProfile = resolved.runtimeProfile;
|
|
5340
5968
|
base.__runtime.smart = resolved;
|
|
5341
5969
|
const summarizationEnabled = resolved.summarization.enable !== false;
|
|
@@ -5371,17 +5999,17 @@ function createSmartAgent(opts) {
|
|
|
5371
5999
|
const memoryFacts = await writeSummaryFactsToMemory(memoryStore, resolved, latestSummary, Math.max(1, (state.messages || []).filter((message) => message.role === "user").length));
|
|
5372
6000
|
return { ...state, memoryFacts };
|
|
5373
6001
|
}
|
|
5374
|
-
function
|
|
5375
|
-
if (!Array.isArray(
|
|
6002
|
+
function syncPlanStateWith(ref, state) {
|
|
6003
|
+
if (!Array.isArray(ref.todoList)) return state;
|
|
5376
6004
|
return {
|
|
5377
6005
|
...state,
|
|
5378
6006
|
plan: {
|
|
5379
|
-
version:
|
|
5380
|
-
steps:
|
|
6007
|
+
version: ref.planVersion || state.planVersion || 1,
|
|
6008
|
+
steps: ref.todoList,
|
|
5381
6009
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5382
|
-
adherenceScore:
|
|
6010
|
+
adherenceScore: ref.adherenceScore || 0
|
|
5383
6011
|
},
|
|
5384
|
-
planVersion:
|
|
6012
|
+
planVersion: ref.planVersion || state.planVersion || 1
|
|
5385
6013
|
};
|
|
5386
6014
|
}
|
|
5387
6015
|
function systemMessage2() {
|
|
@@ -5393,9 +6021,9 @@ function createSmartAgent(opts) {
|
|
|
5393
6021
|
);
|
|
5394
6022
|
return { role: "system", content: sys };
|
|
5395
6023
|
}
|
|
5396
|
-
function syncRuntimeTools(currentState, currentMessages) {
|
|
6024
|
+
function syncRuntimeTools(currentState, currentMessages, toolSet) {
|
|
5397
6025
|
const needsRecoveryTool = hasToolResponseRecoveryReference(currentMessages);
|
|
5398
|
-
const nextTools = needsRecoveryTool ? toolsWithRecovery : toolsWithoutRecovery;
|
|
6026
|
+
const nextTools = needsRecoveryTool ? toolSet.toolsWithRecovery : toolSet.toolsWithoutRecovery;
|
|
5399
6027
|
const currentRuntime = currentState.agent || base.__runtime;
|
|
5400
6028
|
if (currentRuntime.tools === nextTools) {
|
|
5401
6029
|
return currentState;
|
|
@@ -5414,7 +6042,7 @@ function createSmartAgent(opts) {
|
|
|
5414
6042
|
const toolCalls = Array.isArray(lastAssistant.tool_calls) ? lastAssistant.tool_calls : [];
|
|
5415
6043
|
return toolCalls.length === 0;
|
|
5416
6044
|
}
|
|
5417
|
-
async function trySummarize(currentState, currentRawMessages) {
|
|
6045
|
+
async function trySummarize(currentState, currentRawMessages, ref) {
|
|
5418
6046
|
if (!summarizer) return { state: currentState, rawMessages: currentRawMessages, compressed: false };
|
|
5419
6047
|
const delta = await summarizer(currentState);
|
|
5420
6048
|
if (!delta || Object.keys(delta).length === 0) {
|
|
@@ -5423,28 +6051,31 @@ function createSmartAgent(opts) {
|
|
|
5423
6051
|
}
|
|
5424
6052
|
const ctx = { ...currentState.ctx || {} };
|
|
5425
6053
|
delete ctx.__summarizationExhausted;
|
|
5426
|
-
const updated = await persistLatestSummary(
|
|
6054
|
+
const updated = await persistLatestSummary(syncPlanStateWith(ref, { ...currentState, ...delta, ctx }));
|
|
5427
6055
|
return { state: updated, rawMessages: [...updated.messages || currentRawMessages], compressed: true };
|
|
5428
6056
|
}
|
|
5429
6057
|
const instance = {
|
|
5430
6058
|
invoke: async (input, config) => {
|
|
6059
|
+
const toolSet = buildInvokeToolSet();
|
|
6060
|
+
const stateRef = toolSet.stateRef;
|
|
5431
6061
|
stateRef.toolHistory = input.toolHistory;
|
|
5432
6062
|
stateRef.toolHistoryArchived = input.toolHistoryArchived;
|
|
5433
6063
|
stateRef.todoList = input.plan?.steps;
|
|
5434
6064
|
stateRef.planVersion = input.planVersion || input.plan?.version || 0;
|
|
5435
6065
|
stateRef.adherenceScore = input.plan?.adherenceScore || 0;
|
|
6066
|
+
const syncPlanFromRef = (state2) => syncPlanStateWith(stateRef, state2);
|
|
5436
6067
|
const alreadyHasSystem = Array.isArray(input.messages) && input.messages[0]?.role === "system";
|
|
5437
6068
|
const seedMessages = alreadyHasSystem ? [...input.messages || []] : [systemMessage2(), ...input.messages || []];
|
|
5438
|
-
let state = syncRuntimeTools(await syncMemory({ ...input, messages: seedMessages }), seedMessages);
|
|
6069
|
+
let state = syncRuntimeTools(await syncMemory({ ...input, messages: seedMessages }), seedMessages, toolSet);
|
|
5439
6070
|
let lastResult = null;
|
|
5440
6071
|
let rawMessages = [...seedMessages];
|
|
5441
6072
|
const effectiveMaxToolCalls = config?.limits?.maxToolCalls ?? resolved.limits.maxToolCalls ?? 10;
|
|
5442
6073
|
const iterationLimit = Math.max(effectiveMaxToolCalls * 3 + 5, 30);
|
|
5443
6074
|
for (let i = 0; i < iterationLimit; i++) {
|
|
5444
|
-
state = syncRuntimeTools({ ...state, messages: rawMessages }, rawMessages);
|
|
6075
|
+
state = syncRuntimeTools({ ...state, messages: rawMessages }, rawMessages, toolSet);
|
|
5445
6076
|
const next = summarizationEnabled ? decideBefore(state) : "agent";
|
|
5446
6077
|
if (next === "contextSummarize" && summarizer) {
|
|
5447
|
-
const result = await trySummarize(state, rawMessages);
|
|
6078
|
+
const result = await trySummarize(state, rawMessages, stateRef);
|
|
5448
6079
|
state = result.state;
|
|
5449
6080
|
rawMessages = result.rawMessages;
|
|
5450
6081
|
if (result.compressed) continue;
|
|
@@ -5468,14 +6099,14 @@ function createSmartAgent(opts) {
|
|
|
5468
6099
|
if (currentPlan && !state.plan) {
|
|
5469
6100
|
state = { ...state, plan: currentPlan };
|
|
5470
6101
|
}
|
|
5471
|
-
state = syncRuntimeTools(
|
|
6102
|
+
state = syncRuntimeTools(syncPlanFromRef(await syncMemory(state)), rawMessages, toolSet);
|
|
5472
6103
|
stateRef.toolHistory = state.toolHistory;
|
|
5473
6104
|
stateRef.toolHistoryArchived = state.toolHistoryArchived;
|
|
5474
6105
|
if (state.ctx?.__needsSummarization && summarizer) {
|
|
5475
6106
|
const ctx = { ...state.ctx || {} };
|
|
5476
6107
|
delete ctx.__needsSummarization;
|
|
5477
6108
|
state = { ...state, ctx };
|
|
5478
|
-
const result = await trySummarize(state, rawMessages);
|
|
6109
|
+
const result = await trySummarize(state, rawMessages, stateRef);
|
|
5479
6110
|
state = result.state;
|
|
5480
6111
|
rawMessages = result.rawMessages;
|
|
5481
6112
|
continue;
|
|
@@ -5486,7 +6117,7 @@ function createSmartAgent(opts) {
|
|
|
5486
6117
|
if (summarizationEnabled) {
|
|
5487
6118
|
const after = decideAfter(state);
|
|
5488
6119
|
if (after === "contextSummarize" && summarizer) {
|
|
5489
|
-
const result = await trySummarize(state, rawMessages);
|
|
6120
|
+
const result = await trySummarize(state, rawMessages, stateRef);
|
|
5490
6121
|
state = result.state;
|
|
5491
6122
|
rawMessages = result.rawMessages;
|
|
5492
6123
|
if (result.compressed) continue;
|
|
@@ -5512,6 +6143,7 @@ function createSmartAgent(opts) {
|
|
|
5512
6143
|
snapshot: base.snapshot,
|
|
5513
6144
|
resume: base.resume,
|
|
5514
6145
|
resolveToolApproval: base.resolveToolApproval,
|
|
6146
|
+
resolveUserQuestion: base.resolveUserQuestion,
|
|
5515
6147
|
asTool: base.asTool,
|
|
5516
6148
|
asHandoff: base.asHandoff,
|
|
5517
6149
|
__runtime: base.__runtime
|
|
@@ -5925,7 +6557,50 @@ var ProviderError = class extends Error {
|
|
|
5925
6557
|
};
|
|
5926
6558
|
|
|
5927
6559
|
// src/providers/base.ts
|
|
6560
|
+
var DEFAULT_RETRY = {
|
|
6561
|
+
maxRetries: 3,
|
|
6562
|
+
baseDelayMs: 500,
|
|
6563
|
+
maxDelayMs: 8e3
|
|
6564
|
+
};
|
|
5928
6565
|
var BaseProvider = class {
|
|
6566
|
+
constructor() {
|
|
6567
|
+
this.retryPolicy = DEFAULT_RETRY;
|
|
6568
|
+
}
|
|
6569
|
+
/**
|
|
6570
|
+
* Helper concrete providers can wrap their fetch with. Retries 429/5xx
|
|
6571
|
+
* with exponential backoff and respects Retry-After if the response
|
|
6572
|
+
* carries one.
|
|
6573
|
+
*/
|
|
6574
|
+
async doFetchWithRetry(execute) {
|
|
6575
|
+
const policy = this.retryPolicy;
|
|
6576
|
+
const maxRetries = Math.max(0, policy.maxRetries ?? DEFAULT_RETRY.maxRetries);
|
|
6577
|
+
const baseDelay = Math.max(0, policy.baseDelayMs ?? DEFAULT_RETRY.baseDelayMs);
|
|
6578
|
+
const maxDelay = Math.max(baseDelay, policy.maxDelayMs ?? DEFAULT_RETRY.maxDelayMs);
|
|
6579
|
+
let lastResp;
|
|
6580
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
6581
|
+
const res = await execute();
|
|
6582
|
+
const isRetryable = res.status === 429 || res.status >= 500 && res.status < 600;
|
|
6583
|
+
if (!isRetryable) return res;
|
|
6584
|
+
const predicate = policy.shouldRetry;
|
|
6585
|
+
if (predicate && !predicate({ status: res.status }, attempt)) return res;
|
|
6586
|
+
if (attempt === maxRetries) {
|
|
6587
|
+
lastResp = res;
|
|
6588
|
+
break;
|
|
6589
|
+
}
|
|
6590
|
+
const retryAfter = res.headers.get("retry-after");
|
|
6591
|
+
let delay = Math.min(maxDelay, baseDelay * Math.pow(2, attempt));
|
|
6592
|
+
if (retryAfter) {
|
|
6593
|
+
const asNumber = Number(retryAfter);
|
|
6594
|
+
if (!Number.isNaN(asNumber)) delay = Math.min(maxDelay, Math.max(delay, asNumber * 1e3));
|
|
6595
|
+
}
|
|
6596
|
+
try {
|
|
6597
|
+
await res.text();
|
|
6598
|
+
} catch {
|
|
6599
|
+
}
|
|
6600
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
6601
|
+
}
|
|
6602
|
+
return lastResp;
|
|
6603
|
+
}
|
|
5929
6604
|
};
|
|
5930
6605
|
|
|
5931
6606
|
// src/providers/utils/sse.ts
|
|
@@ -6052,6 +6727,7 @@ var OpenAIProvider = class extends BaseProvider {
|
|
|
6052
6727
|
...config.defaultHeaders,
|
|
6053
6728
|
...config.organization ? { "OpenAI-Organization": config.organization } : {}
|
|
6054
6729
|
};
|
|
6730
|
+
if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
|
|
6055
6731
|
}
|
|
6056
6732
|
async complete(request) {
|
|
6057
6733
|
const body = this.buildRequestBody(request, false);
|
|
@@ -6199,15 +6875,13 @@ var OpenAIProvider = class extends BaseProvider {
|
|
|
6199
6875
|
}
|
|
6200
6876
|
async doFetch(body) {
|
|
6201
6877
|
const url = `${this.baseURL}/chat/completions`;
|
|
6202
|
-
const
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
body: JSON.stringify(body)
|
|
6210
|
-
});
|
|
6878
|
+
const headers = {
|
|
6879
|
+
"Content-Type": "application/json",
|
|
6880
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
6881
|
+
...this.defaultHeaders
|
|
6882
|
+
};
|
|
6883
|
+
const payload = JSON.stringify(body);
|
|
6884
|
+
const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload }));
|
|
6211
6885
|
if (!res.ok) {
|
|
6212
6886
|
const text = await res.text().catch(() => "");
|
|
6213
6887
|
throw new ProviderError(
|
|
@@ -6277,6 +6951,8 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
6277
6951
|
this.defaultModel = config.defaultModel ?? "claude-sonnet-4-20250514";
|
|
6278
6952
|
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
6279
6953
|
this.anthropicVersion = config.anthropicVersion ?? "2023-06-01";
|
|
6954
|
+
this.promptCachingEnabled = config.prompt_caching?.enabled === true;
|
|
6955
|
+
if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
|
|
6280
6956
|
}
|
|
6281
6957
|
async complete(request) {
|
|
6282
6958
|
const body = this.buildRequestBody(request, false);
|
|
@@ -6395,12 +7071,22 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
6395
7071
|
max_tokens: request.maxTokens ?? 4096,
|
|
6396
7072
|
stream
|
|
6397
7073
|
};
|
|
6398
|
-
if (system)
|
|
7074
|
+
if (system) {
|
|
7075
|
+
if (this.promptCachingEnabled) {
|
|
7076
|
+
body.system = [{ type: "text", text: system, cache_control: { type: "ephemeral" } }];
|
|
7077
|
+
} else {
|
|
7078
|
+
body.system = system;
|
|
7079
|
+
}
|
|
7080
|
+
}
|
|
6399
7081
|
if (request.temperature != null) body.temperature = request.temperature;
|
|
6400
7082
|
if (request.topP != null) body.top_p = request.topP;
|
|
6401
7083
|
if (request.stop) body.stop_sequences = request.stop;
|
|
6402
7084
|
if (request.tools?.length) {
|
|
6403
|
-
|
|
7085
|
+
const tools = request.tools.map((t) => this.toAnthropicTool(t));
|
|
7086
|
+
if (this.promptCachingEnabled && tools.length > 0) {
|
|
7087
|
+
tools[tools.length - 1] = { ...tools[tools.length - 1], cache_control: { type: "ephemeral" } };
|
|
7088
|
+
}
|
|
7089
|
+
body.tools = tools;
|
|
6404
7090
|
}
|
|
6405
7091
|
if (request.toolChoice != null) {
|
|
6406
7092
|
if (request.toolChoice === "auto") {
|
|
@@ -6499,16 +7185,14 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
6499
7185
|
}
|
|
6500
7186
|
async doFetch(body) {
|
|
6501
7187
|
const url = `${this.baseURL}/v1/messages`;
|
|
6502
|
-
const
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
body: JSON.stringify(body)
|
|
6511
|
-
});
|
|
7188
|
+
const headers = {
|
|
7189
|
+
"Content-Type": "application/json",
|
|
7190
|
+
"x-api-key": this.apiKey,
|
|
7191
|
+
"anthropic-version": this.anthropicVersion,
|
|
7192
|
+
...this.defaultHeaders
|
|
7193
|
+
};
|
|
7194
|
+
const payload = JSON.stringify(body);
|
|
7195
|
+
const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload }));
|
|
6512
7196
|
if (!res.ok) {
|
|
6513
7197
|
const text = await res.text().catch(() => "");
|
|
6514
7198
|
throw new ProviderError(
|
|
@@ -6720,6 +7404,8 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
6720
7404
|
this.region = config.region;
|
|
6721
7405
|
this.defaultModel = config.defaultModel ?? "anthropic.claude-sonnet-4-20250514-v1:0";
|
|
6722
7406
|
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
7407
|
+
this.promptCachingEnabled = config.prompt_caching?.enabled === true;
|
|
7408
|
+
if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
|
|
6723
7409
|
this.credentials = {
|
|
6724
7410
|
accessKeyId: config.accessKeyId ?? process.env.AWS_ACCESS_KEY_ID ?? "",
|
|
6725
7411
|
secretAccessKey: config.secretAccessKey ?? process.env.AWS_SECRET_ACCESS_KEY ?? "",
|
|
@@ -6758,7 +7444,9 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
6758
7444
|
messages: messages.map((m) => this.toBedrockMessage(m))
|
|
6759
7445
|
};
|
|
6760
7446
|
if (system.length > 0) {
|
|
6761
|
-
|
|
7447
|
+
const systemBlocks = system.map((s) => ({ text: s }));
|
|
7448
|
+
if (this.promptCachingEnabled) systemBlocks.push({ cachePoint: { type: "default" } });
|
|
7449
|
+
body.system = systemBlocks;
|
|
6762
7450
|
}
|
|
6763
7451
|
const inferenceConfig = {};
|
|
6764
7452
|
if (request.maxTokens != null) inferenceConfig.maxTokens = request.maxTokens;
|
|
@@ -6767,8 +7455,12 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
6767
7455
|
if (request.stop) inferenceConfig.stopSequences = request.stop;
|
|
6768
7456
|
if (Object.keys(inferenceConfig).length > 0) body.inferenceConfig = inferenceConfig;
|
|
6769
7457
|
if (request.tools?.length) {
|
|
7458
|
+
const tools = request.tools.map((t) => this.toBedrockTool(t));
|
|
7459
|
+
if (this.promptCachingEnabled && tools.length > 0) {
|
|
7460
|
+
tools.push({ cachePoint: { type: "default" } });
|
|
7461
|
+
}
|
|
6770
7462
|
body.toolConfig = {
|
|
6771
|
-
tools
|
|
7463
|
+
tools
|
|
6772
7464
|
};
|
|
6773
7465
|
if (request.toolChoice != null) {
|
|
6774
7466
|
if (request.toolChoice === "auto") {
|
|
@@ -6865,23 +7557,21 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
6865
7557
|
const host = `bedrock-runtime.${this.region}.amazonaws.com`;
|
|
6866
7558
|
const url = `https://${host}/model/${encodeURIComponent(modelId)}/converse`;
|
|
6867
7559
|
const bodyStr = JSON.stringify(body);
|
|
6868
|
-
const
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
method: "POST",
|
|
6883
|
-
headers,
|
|
6884
|
-
body: bodyStr
|
|
7560
|
+
const res = await this.doFetchWithRetry(() => {
|
|
7561
|
+
const headers = signRequest({
|
|
7562
|
+
method: "POST",
|
|
7563
|
+
url,
|
|
7564
|
+
headers: {
|
|
7565
|
+
"Content-Type": "application/json",
|
|
7566
|
+
Accept: "application/json",
|
|
7567
|
+
...this.defaultHeaders
|
|
7568
|
+
},
|
|
7569
|
+
body: bodyStr,
|
|
7570
|
+
region: this.region,
|
|
7571
|
+
service: "bedrock",
|
|
7572
|
+
credentials: this.credentials
|
|
7573
|
+
});
|
|
7574
|
+
return fetch(url, { method: "POST", headers, body: bodyStr });
|
|
6885
7575
|
});
|
|
6886
7576
|
if (!res.ok) {
|
|
6887
7577
|
const text = await res.text().catch(() => "");
|
|
@@ -7779,6 +8469,7 @@ exports.countApproxTokens = countApproxTokens;
|
|
|
7779
8469
|
exports.countMessagesTokens = countMessagesTokens;
|
|
7780
8470
|
exports.createAgent = createAgent;
|
|
7781
8471
|
exports.createAgentNode = createAgentNode;
|
|
8472
|
+
exports.createAskUserQuestionTool = createAskUserQuestionTool;
|
|
7782
8473
|
exports.createCodeGuardrail = createCodeGuardrail;
|
|
7783
8474
|
exports.createContextSummarizeNode = createContextSummarizeNode;
|
|
7784
8475
|
exports.createContextTools = createContextTools;
|
|
@@ -7795,6 +8486,7 @@ exports.createToolLimitFinalizeNode = createToolLimitFinalizeNode;
|
|
|
7795
8486
|
exports.createToolsNode = createToolsNode;
|
|
7796
8487
|
exports.customCallbackRule = customCallbackRule;
|
|
7797
8488
|
exports.customSink = customSink;
|
|
8489
|
+
exports.defaultTokenCounter = defaultTokenCounter;
|
|
7798
8490
|
exports.emptyUsage = emptyUsage;
|
|
7799
8491
|
exports.evaluateGuardrails = evaluateGuardrails;
|
|
7800
8492
|
exports.fileSink = fileSink;
|
|
@@ -7807,6 +8499,7 @@ exports.generateTraceId = generateTraceId;
|
|
|
7807
8499
|
exports.getModelCapabilities = getModelCapabilities;
|
|
7808
8500
|
exports.getNestedValue = getNestedValue;
|
|
7809
8501
|
exports.getResolvedSmartConfig = getResolvedSmartConfig;
|
|
8502
|
+
exports.getTokenCounter = getTokenCounter;
|
|
7810
8503
|
exports.hasToolResponseRecoveryReference = hasToolResponseRecoveryReference;
|
|
7811
8504
|
exports.httpSink = httpSink;
|
|
7812
8505
|
exports.isSmartModel = isSmartModel;
|
|
@@ -7818,8 +8511,10 @@ exports.regexRule = regexRule;
|
|
|
7818
8511
|
exports.resolveMemoryStore = resolveMemoryStore;
|
|
7819
8512
|
exports.resolveStrategy = resolveStrategy;
|
|
7820
8513
|
exports.resolveToolApprovalState = resolveToolApprovalState;
|
|
8514
|
+
exports.resolveUserQuestionState = resolveUserQuestionState;
|
|
7821
8515
|
exports.restoreSnapshot = restoreSnapshot;
|
|
7822
8516
|
exports.runSmartAgentEvalHarness = runSmartAgentEvalHarness;
|
|
8517
|
+
exports.setTokenCounter = setTokenCounter;
|
|
7823
8518
|
exports.startStreamingSession = startStreamingSession;
|
|
7824
8519
|
exports.traceSessionToOtlp = traceSessionToOtlp;
|
|
7825
8520
|
exports.withTools = withTools;
|