@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.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
|
-
|
|
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;
|
|
@@ -643,6 +743,7 @@ function createResolverNode() {
|
|
|
643
743
|
toolHistory: state.toolHistory || [],
|
|
644
744
|
toolHistoryArchived: state.toolHistoryArchived || [],
|
|
645
745
|
pendingApprovals: state.pendingApprovals || [],
|
|
746
|
+
pendingUserQuestions: state.pendingUserQuestions || [],
|
|
646
747
|
plan: state.plan || null,
|
|
647
748
|
planVersion: state.planVersion || 0
|
|
648
749
|
};
|
|
@@ -1882,7 +1983,8 @@ function createAgentCoreNode(opts) {
|
|
|
1882
1983
|
todoListPrompt: opts.todoListPrompt,
|
|
1883
1984
|
limits: opts.limits,
|
|
1884
1985
|
outputSchema: opts.outputSchema,
|
|
1885
|
-
tracing: opts.tracing
|
|
1986
|
+
tracing: opts.tracing,
|
|
1987
|
+
responseFormat: void 0
|
|
1886
1988
|
};
|
|
1887
1989
|
const tools = runtime.tools ?? [];
|
|
1888
1990
|
const shouldUseStrictToolCalling = Boolean(
|
|
@@ -2084,7 +2186,7 @@ function createAgentCoreNode(opts) {
|
|
|
2084
2186
|
// src/smart/runtimeConfig.ts
|
|
2085
2187
|
var DEFAULT_CRITICAL_TOOLS = ["response", "manage_todo_list", "get_tool_response"];
|
|
2086
2188
|
var BASE_DEFAULTS = {
|
|
2087
|
-
limits: { maxToolCalls: 8, maxParallelTools: 2, maxContextTokens: 24e3 },
|
|
2189
|
+
limits: { maxToolCalls: 8, maxParallelTools: 2, maxContextTokens: 24e3, maxTotalOutputTokens: 0, maxCostUsd: 0, maxWallClockMs: 0 },
|
|
2088
2190
|
summarization: {
|
|
2089
2191
|
enable: true,
|
|
2090
2192
|
maxTokens: 24e3,
|
|
@@ -2211,7 +2313,10 @@ function mergeLimits(base, override) {
|
|
|
2211
2313
|
return {
|
|
2212
2314
|
maxToolCalls: override?.maxToolCalls ?? base.maxToolCalls,
|
|
2213
2315
|
maxParallelTools: override?.maxParallelTools ?? base.maxParallelTools,
|
|
2214
|
-
maxContextTokens: override?.maxContextTokens ?? base.maxContextTokens
|
|
2316
|
+
maxContextTokens: override?.maxContextTokens ?? base.maxContextTokens,
|
|
2317
|
+
maxTotalOutputTokens: override?.maxTotalOutputTokens ?? base.maxTotalOutputTokens ?? 0,
|
|
2318
|
+
maxCostUsd: override?.maxCostUsd ?? base.maxCostUsd ?? 0,
|
|
2319
|
+
maxWallClockMs: override?.maxWallClockMs ?? base.maxWallClockMs ?? 0
|
|
2215
2320
|
};
|
|
2216
2321
|
}
|
|
2217
2322
|
function resolveBaseProfile(opts, runtimeProfile) {
|
|
@@ -2289,66 +2394,8 @@ function getResolvedSmartConfig(opts, runtime) {
|
|
|
2289
2394
|
return runtime?.smart || normalizeSmartAgentOptions(opts);
|
|
2290
2395
|
}
|
|
2291
2396
|
|
|
2292
|
-
// src/
|
|
2293
|
-
|
|
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
|
-
}
|
|
2397
|
+
// src/smart/toolResponses.ts
|
|
2398
|
+
init_utilTokens();
|
|
2352
2399
|
|
|
2353
2400
|
// src/utils/content.ts
|
|
2354
2401
|
function contentToString(content) {
|
|
@@ -2538,16 +2585,20 @@ function createToolsNode(initialTools, opts) {
|
|
|
2538
2585
|
const traceSession = state.ctx?.__traceSession;
|
|
2539
2586
|
const pendingApprovals = Array.isArray(state.pendingApprovals) ? state.pendingApprovals.map((entry) => ({ ...entry })) : [];
|
|
2540
2587
|
const pendingByCallId = new Map(pendingApprovals.map((entry) => [entry.toolCallId, entry]));
|
|
2588
|
+
const pendingUserQuestions = Array.isArray(state.pendingUserQuestions) ? state.pendingUserQuestions.map((entry) => ({ ...entry })) : [];
|
|
2541
2589
|
let awaitingApproval = false;
|
|
2590
|
+
let awaitingUserQuestion = false;
|
|
2542
2591
|
for (const tool of toolByName.values()) {
|
|
2543
2592
|
const anyTool = tool;
|
|
2544
2593
|
if (anyTool._stateRef && typeof anyTool._stateRef === "object") {
|
|
2545
2594
|
anyTool._stateRef.toolHistory = state.toolHistory;
|
|
2546
2595
|
anyTool._stateRef.toolHistoryArchived = state.toolHistoryArchived;
|
|
2547
2596
|
anyTool._stateRef.pendingApprovals = pendingApprovals;
|
|
2597
|
+
anyTool._stateRef.pendingUserQuestions = pendingUserQuestions;
|
|
2548
2598
|
anyTool._stateRef.messages = state.messages;
|
|
2549
2599
|
anyTool._stateRef.ctx = state.ctx || (state.ctx = {});
|
|
2550
2600
|
anyTool._stateRef.__onEvent = onEvent;
|
|
2601
|
+
anyTool._stateRef.parentRuntime = runtime;
|
|
2551
2602
|
}
|
|
2552
2603
|
}
|
|
2553
2604
|
const last = state.messages[state.messages.length - 1];
|
|
@@ -2558,6 +2609,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
2558
2609
|
const remaining = Math.max(0, limits.maxToolCalls - toolCount);
|
|
2559
2610
|
const planned = toolCalls.slice(0, remaining);
|
|
2560
2611
|
const skipped = toolCalls.slice(remaining);
|
|
2612
|
+
const orderedSlots = planned.map(() => []);
|
|
2561
2613
|
const isCancelled = () => {
|
|
2562
2614
|
if (abortSignal?.aborted) return { cancelled: true, reason: "aborted" };
|
|
2563
2615
|
if (cancellationToken && cancellationToken.isCancellationRequested) return { cancelled: true, reason: "cancelled" };
|
|
@@ -2574,7 +2626,9 @@ function createToolsNode(initialTools, opts) {
|
|
|
2574
2626
|
}
|
|
2575
2627
|
state.ctx = ctx;
|
|
2576
2628
|
};
|
|
2577
|
-
const runOne = async (tc) => {
|
|
2629
|
+
const runOne = async (tc, slot = appended) => {
|
|
2630
|
+
var _a;
|
|
2631
|
+
const push = (msg) => slot.push(msg);
|
|
2578
2632
|
const cancelState = isCancelled();
|
|
2579
2633
|
if (cancelState.cancelled) {
|
|
2580
2634
|
const ctx = state.ctx = state.ctx || {};
|
|
@@ -2585,7 +2639,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
2585
2639
|
}
|
|
2586
2640
|
const tool = toolByName.get(tc.name);
|
|
2587
2641
|
if (!tool) {
|
|
2588
|
-
|
|
2642
|
+
push({ role: "tool", content: `Tool not found: ${tc.name}`, tool_call_id: tc.id || `${tc.name}_unknown`, name: tc.name });
|
|
2589
2643
|
onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args: tc.args, error: { message: "Tool not found" } });
|
|
2590
2644
|
toolCount += 1;
|
|
2591
2645
|
return { status: "error" };
|
|
@@ -2601,7 +2655,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
2601
2655
|
const validatedArgs = validateToolArgs(tool, args);
|
|
2602
2656
|
if (!validatedArgs.ok && resolved.toolResponses.schemaValidation === "strict") {
|
|
2603
2657
|
const errorMessage = `Tool argument validation failed for ${tc.name}: ${validatedArgs.message}`;
|
|
2604
|
-
|
|
2658
|
+
push({ role: "tool", content: errorMessage, tool_call_id: toolCallId, name: tc.name });
|
|
2605
2659
|
markToolFailure(errorMessage, tc.name, toolCallId);
|
|
2606
2660
|
onEvent?.({ type: "tool_call", phase: "error", name: tc.name, id: tc.id, args, error: { message: errorMessage } });
|
|
2607
2661
|
toolCount += 1;
|
|
@@ -2614,6 +2668,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
2614
2668
|
const toolStateRef = tool._stateRef && typeof tool._stateRef === "object" ? tool._stateRef : null;
|
|
2615
2669
|
if (toolStateRef) {
|
|
2616
2670
|
toolStateRef.pendingApprovals = pendingApprovals;
|
|
2671
|
+
toolStateRef.pendingUserQuestions = pendingUserQuestions;
|
|
2617
2672
|
toolStateRef.ctx = state.ctx || (state.ctx = {});
|
|
2618
2673
|
toolStateRef.__currentToolCallId = toolCallId;
|
|
2619
2674
|
delete toolStateRef.__awaitingApproval;
|
|
@@ -2623,7 +2678,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
2623
2678
|
const currentExecutions = countSuccessfulToolExecutions(toolHistory, toolName);
|
|
2624
2679
|
if (currentExecutions >= maxExecutionsPerRun) {
|
|
2625
2680
|
const limitMessage = `Skipped tool due to per-tool execution limit: ${toolName} (${maxExecutionsPerRun}/run)`;
|
|
2626
|
-
|
|
2681
|
+
push({ role: "tool", content: limitMessage, tool_call_id: toolCallId, name: tc.name });
|
|
2627
2682
|
onEvent?.({ type: "tool_call", phase: "skipped", name: toolName, id: tc.id, args });
|
|
2628
2683
|
recordTraceEvent(traceSession, {
|
|
2629
2684
|
type: "tool_call",
|
|
@@ -2683,7 +2738,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
2683
2738
|
}
|
|
2684
2739
|
if (approvalEntry.status === "rejected") {
|
|
2685
2740
|
const rejectionMessage = approvalEntry.comment || "Tool call rejected by reviewer.";
|
|
2686
|
-
|
|
2741
|
+
push({ role: "tool", content: `Tool call rejected: ${rejectionMessage}`, tool_call_id: toolCallId, name: tc.name });
|
|
2687
2742
|
approvalEntry.metadata = { ...approvalEntry.metadata || {}, resolution: "rejected" };
|
|
2688
2743
|
approvalEntry.status = "executed";
|
|
2689
2744
|
approvalEntry.resolvedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -2701,23 +2756,102 @@ function createToolsNode(initialTools, opts) {
|
|
|
2701
2756
|
const start = Date.now();
|
|
2702
2757
|
const sanitizedArgs = sanitizeTracePayload(args);
|
|
2703
2758
|
const inputBytes = traceSession?.resolvedConfig.logData ? estimatePayloadBytes(sanitizedArgs) : void 0;
|
|
2759
|
+
const cacheCfg = tool.cache;
|
|
2760
|
+
const cacheEnabled = cacheCfg === true || cacheCfg && typeof cacheCfg === "object";
|
|
2761
|
+
const cacheKeyFn = cacheEnabled && typeof cacheCfg === "object" ? cacheCfg.keyFn : void 0;
|
|
2762
|
+
const stableJson = (value) => {
|
|
2763
|
+
try {
|
|
2764
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
2765
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
2766
|
+
const keys = Object.keys(value).sort();
|
|
2767
|
+
return `{${keys.map((k) => JSON.stringify(k) + ":" + stableJson(value[k])).join(",")}}`;
|
|
2768
|
+
} catch {
|
|
2769
|
+
return String(value);
|
|
2770
|
+
}
|
|
2771
|
+
};
|
|
2772
|
+
const cacheKey = cacheEnabled ? `${toolName}|${cacheKeyFn ? cacheKeyFn(args) : stableJson(args)}` : void 0;
|
|
2773
|
+
const toolCache = state.toolCache = state.toolCache || {};
|
|
2774
|
+
let cachedHit = void 0;
|
|
2775
|
+
if (cacheKey && Object.prototype.hasOwnProperty.call(toolCache, cacheKey)) {
|
|
2776
|
+
const entry = toolCache[cacheKey];
|
|
2777
|
+
const ttl = cacheEnabled && typeof cacheCfg === "object" ? cacheCfg.ttlMs : void 0;
|
|
2778
|
+
if (!ttl || entry?.timestamp && Date.now() - entry.timestamp <= ttl) {
|
|
2779
|
+
cachedHit = entry?.value;
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
const retryCfg = tool.retry;
|
|
2783
|
+
const failureStats = (_a = state.ctx).__toolFailureCounts || (_a.__toolFailureCounts = {});
|
|
2784
|
+
if (retryCfg?.circuitBreakerThreshold && failureStats[toolName] >= retryCfg.circuitBreakerThreshold) {
|
|
2785
|
+
const breakerMsg = `Tool ${toolName} circuit breaker open after ${failureStats[toolName]} consecutive failures`;
|
|
2786
|
+
push({ role: "tool", content: breakerMsg, tool_call_id: toolCallId, name: tc.name });
|
|
2787
|
+
markToolFailure(breakerMsg, toolName, toolCallId);
|
|
2788
|
+
onEvent?.({ type: "tool_call", phase: "error", name: toolName, id: tc.id, args, error: { message: breakerMsg } });
|
|
2789
|
+
toolCount += 1;
|
|
2790
|
+
return { status: "error" };
|
|
2791
|
+
}
|
|
2704
2792
|
try {
|
|
2793
|
+
if (cachedHit !== void 0) {
|
|
2794
|
+
const executionId2 = nanoid();
|
|
2795
|
+
const responsePolicy2 = applyToolResponseHardCap(toolName, cachedHit, executionId2, resolved);
|
|
2796
|
+
toolHistory.push({
|
|
2797
|
+
executionId: executionId2,
|
|
2798
|
+
toolName,
|
|
2799
|
+
args,
|
|
2800
|
+
output: cachedHit,
|
|
2801
|
+
rawOutput: cachedHit,
|
|
2802
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2803
|
+
tool_call_id: tc.id,
|
|
2804
|
+
summarized: false,
|
|
2805
|
+
originalTokenCount: responsePolicy2.tokenCount,
|
|
2806
|
+
classification: responsePolicy2.classification,
|
|
2807
|
+
retentionPolicy: "keep_full",
|
|
2808
|
+
fromCache: true,
|
|
2809
|
+
status: "success"
|
|
2810
|
+
});
|
|
2811
|
+
push({ role: "tool", content: responsePolicy2.content, tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
|
|
2812
|
+
onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: cachedHit, durationMs: 0 });
|
|
2813
|
+
toolCount += 1;
|
|
2814
|
+
return { status: "success" };
|
|
2815
|
+
}
|
|
2705
2816
|
onEvent?.({ type: "tool_call", phase: "start", name: toolName, id: tc.id, args });
|
|
2706
2817
|
onProgress?.({ stage: "tools", message: `Running tool ${tc.name}` });
|
|
2707
2818
|
const anyTool = tool;
|
|
2708
2819
|
const callOptions = { cancellationToken, signal: abortSignal };
|
|
2820
|
+
const invokeOnce = async () => {
|
|
2821
|
+
if (typeof anyTool.func === "function") return anyTool.func(args, callOptions);
|
|
2822
|
+
if (typeof anyTool.invoke === "function") return anyTool.invoke(args, callOptions);
|
|
2823
|
+
if (typeof anyTool.call === "function") return anyTool.call(args, callOptions);
|
|
2824
|
+
if (typeof anyTool._call === "function") return anyTool._call(args, callOptions);
|
|
2825
|
+
if (typeof anyTool.run === "function") return anyTool.run(args, callOptions);
|
|
2826
|
+
throw new Error("Tool is not invokable");
|
|
2827
|
+
};
|
|
2709
2828
|
let output;
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2829
|
+
const maxRetries = Math.max(0, Number(retryCfg?.maxRetries) || 0);
|
|
2830
|
+
const backoffMs = Math.max(0, Number(retryCfg?.backoffMs) || 250);
|
|
2831
|
+
let attempt = 0;
|
|
2832
|
+
while (true) {
|
|
2833
|
+
try {
|
|
2834
|
+
output = await invokeOnce();
|
|
2835
|
+
failureStats[toolName] = 0;
|
|
2836
|
+
break;
|
|
2837
|
+
} catch (retryErr) {
|
|
2838
|
+
attempt += 1;
|
|
2839
|
+
const shouldRetry = retryCfg?.shouldRetry ? retryCfg.shouldRetry(retryErr, attempt) : true;
|
|
2840
|
+
if (attempt > maxRetries || !shouldRetry) {
|
|
2841
|
+
failureStats[toolName] = (failureStats[toolName] || 0) + 1;
|
|
2842
|
+
throw retryErr;
|
|
2843
|
+
}
|
|
2844
|
+
await new Promise((r) => setTimeout(r, backoffMs * Math.pow(2, attempt - 1)));
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
if (cacheKey) {
|
|
2848
|
+
toolCache[cacheKey] = { value: output, timestamp: Date.now() };
|
|
2849
|
+
}
|
|
2716
2850
|
const durationMs = Date.now() - start;
|
|
2717
2851
|
const executionId = nanoid();
|
|
2718
2852
|
if (output && typeof output === "object" && output.__handoff && output.__handoff.runtime) {
|
|
2719
2853
|
toolHistory.push({ executionId, toolName, args, output: "handoff:ok", rawOutput: output, timestamp: (/* @__PURE__ */ new Date()).toISOString(), tool_call_id: tc.id, status: "handoff" });
|
|
2720
|
-
|
|
2854
|
+
push({ role: "tool", content: "ok", tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
|
|
2721
2855
|
state.agent = output.__handoff.runtime;
|
|
2722
2856
|
onEvent?.({ type: "handoff", from: runtime.name, to: state.agent?.name, toolName });
|
|
2723
2857
|
onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: "handoff", durationMs });
|
|
@@ -2746,6 +2880,22 @@ function createToolsNode(initialTools, opts) {
|
|
|
2746
2880
|
approval: pendingApproval
|
|
2747
2881
|
};
|
|
2748
2882
|
}
|
|
2883
|
+
if (output && typeof output === "object" && output.__awaitingUserQuestion) {
|
|
2884
|
+
if (toolStateRef?.ctx && typeof toolStateRef.ctx === "object") {
|
|
2885
|
+
state.ctx = toolStateRef.ctx;
|
|
2886
|
+
}
|
|
2887
|
+
const pendingQuestion = pendingUserQuestions.find(
|
|
2888
|
+
(entry) => entry.toolCallId === output.toolCallId || entry.id === output.id
|
|
2889
|
+
);
|
|
2890
|
+
if (!pendingQuestion) {
|
|
2891
|
+
throw new Error(`Tool ${toolName} requested a user question but no pending question entry was recorded.`);
|
|
2892
|
+
}
|
|
2893
|
+
awaitingUserQuestion = true;
|
|
2894
|
+
return {
|
|
2895
|
+
status: "awaiting_user_question",
|
|
2896
|
+
userQuestion: pendingQuestion
|
|
2897
|
+
};
|
|
2898
|
+
}
|
|
2749
2899
|
if (output && typeof output === "object" && output.__finalStructuredOutput) {
|
|
2750
2900
|
if (!state.ctx) state.ctx = {};
|
|
2751
2901
|
state.ctx.__structuredOutputParsed = output.data;
|
|
@@ -2769,7 +2919,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
2769
2919
|
archiveId: void 0,
|
|
2770
2920
|
status: "success"
|
|
2771
2921
|
});
|
|
2772
|
-
|
|
2922
|
+
push({ role: "tool", content: responsePolicy.content, tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
|
|
2773
2923
|
onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: output, durationMs });
|
|
2774
2924
|
onProgress?.({ stage: "tools", message: `Tool ${tc.name} completed`, detail: { durationMs } });
|
|
2775
2925
|
if (needsApproval && approvalEntry) {
|
|
@@ -2825,7 +2975,7 @@ function createToolsNode(initialTools, opts) {
|
|
|
2825
2975
|
const executionId = nanoid();
|
|
2826
2976
|
const message = error?.message || String(error);
|
|
2827
2977
|
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
|
-
|
|
2978
|
+
push({ role: "tool", content: `Error executing tool: ${message}`, tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
|
|
2829
2979
|
markToolFailure(message, toolName, toolCallId);
|
|
2830
2980
|
onEvent?.({ type: "tool_call", phase: "error", name: toolName, id: tc.id, args, error: { message } });
|
|
2831
2981
|
onProgress?.({ stage: "tools", message: `Tool ${tc.name} failed`, detail: { error: message } });
|
|
@@ -2862,21 +3012,74 @@ function createToolsNode(initialTools, opts) {
|
|
|
2862
3012
|
return { status: "error", approval: approvalEntry };
|
|
2863
3013
|
}
|
|
2864
3014
|
};
|
|
2865
|
-
|
|
2866
|
-
|
|
3015
|
+
const approvalIndices = [];
|
|
3016
|
+
const parallelIndices = [];
|
|
3017
|
+
planned.forEach((tc, idx) => {
|
|
3018
|
+
const t = toolByName.get(tc.name);
|
|
3019
|
+
if (t && t.needsApproval) approvalIndices.push(idx);
|
|
3020
|
+
else parallelIndices.push(idx);
|
|
3021
|
+
});
|
|
3022
|
+
let shouldStop = false;
|
|
3023
|
+
const checkCancellationStop = () => {
|
|
2867
3024
|
const cancelState = isCancelled();
|
|
2868
3025
|
if (cancelState.cancelled) {
|
|
2869
3026
|
const ctx = state.ctx = state.ctx || {};
|
|
2870
3027
|
ctx.__cancelled = { stage: "tools", reason: cancelState.reason, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2871
3028
|
onEvent?.({ type: "cancelled", stage: "tools", reason: cancelState.reason });
|
|
2872
3029
|
onProgress?.({ stage: "tools", message: "Cancelled", detail: { reason: cancelState.reason } });
|
|
2873
|
-
|
|
3030
|
+
shouldStop = true;
|
|
3031
|
+
return true;
|
|
2874
3032
|
}
|
|
2875
|
-
|
|
3033
|
+
return false;
|
|
3034
|
+
};
|
|
3035
|
+
for (const idx of approvalIndices) {
|
|
3036
|
+
if (awaitingApproval || awaitingUserQuestion || shouldStop) break;
|
|
3037
|
+
if (checkCancellationStop()) break;
|
|
3038
|
+
const result = await runOne(planned[idx], orderedSlots[idx]);
|
|
2876
3039
|
if (result.status === "awaiting_approval") {
|
|
2877
3040
|
awaitingApproval = true;
|
|
2878
3041
|
break;
|
|
2879
3042
|
}
|
|
3043
|
+
if (result.status === "awaiting_user_question") {
|
|
3044
|
+
awaitingUserQuestion = true;
|
|
3045
|
+
break;
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
if (!awaitingApproval && !awaitingUserQuestion && !shouldStop && parallelIndices.length > 0) {
|
|
3049
|
+
const concurrency = Math.max(1, Math.min(limits.maxParallelTools, parallelIndices.length));
|
|
3050
|
+
let cursor = 0;
|
|
3051
|
+
const workerError = {};
|
|
3052
|
+
const workers = [];
|
|
3053
|
+
for (let w = 0; w < concurrency; w++) {
|
|
3054
|
+
workers.push((async () => {
|
|
3055
|
+
while (true) {
|
|
3056
|
+
if (workerError.err || shouldStop || awaitingApproval || awaitingUserQuestion) return;
|
|
3057
|
+
const i = cursor++;
|
|
3058
|
+
if (i >= parallelIndices.length) return;
|
|
3059
|
+
if (checkCancellationStop()) return;
|
|
3060
|
+
const idx = parallelIndices[i];
|
|
3061
|
+
try {
|
|
3062
|
+
const result = await runOne(planned[idx], orderedSlots[idx]);
|
|
3063
|
+
if (result.status === "awaiting_approval") {
|
|
3064
|
+
awaitingApproval = true;
|
|
3065
|
+
return;
|
|
3066
|
+
}
|
|
3067
|
+
if (result.status === "awaiting_user_question") {
|
|
3068
|
+
awaitingUserQuestion = true;
|
|
3069
|
+
return;
|
|
3070
|
+
}
|
|
3071
|
+
} catch (err) {
|
|
3072
|
+
workerError.err = err;
|
|
3073
|
+
return;
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
})());
|
|
3077
|
+
}
|
|
3078
|
+
await Promise.all(workers);
|
|
3079
|
+
if (workerError.err) throw workerError.err;
|
|
3080
|
+
}
|
|
3081
|
+
for (const slot of orderedSlots) {
|
|
3082
|
+
if (slot.length > 0) appended.push(...slot);
|
|
2880
3083
|
}
|
|
2881
3084
|
for (const tc of skipped) {
|
|
2882
3085
|
onEvent?.({ type: "tool_call", phase: "skipped", name: tc.name, id: tc.id, args: tc.args });
|
|
@@ -2913,13 +3116,26 @@ function createToolsNode(initialTools, opts) {
|
|
|
2913
3116
|
}
|
|
2914
3117
|
state.ctx = Object.keys(ctx).length > 0 ? ctx : void 0;
|
|
2915
3118
|
}
|
|
3119
|
+
const appendedHasToolResult = appended.some((m) => m.role === "tool");
|
|
3120
|
+
if (appendedHasToolResult && state.ctx?.__summarizationExhausted) {
|
|
3121
|
+
const ctx = { ...state.ctx };
|
|
3122
|
+
delete ctx.__summarizationExhausted;
|
|
3123
|
+
state.ctx = Object.keys(ctx).length > 0 ? ctx : void 0;
|
|
3124
|
+
}
|
|
2916
3125
|
return {
|
|
2917
3126
|
messages: [...state.messages, ...appended],
|
|
2918
3127
|
toolCallCount: toolCount,
|
|
2919
3128
|
toolHistory,
|
|
2920
3129
|
toolHistoryArchived,
|
|
3130
|
+
toolCache: state.toolCache,
|
|
3131
|
+
// Explicitly propagate ctx so callers using `{ ...state, ...delta }`
|
|
3132
|
+
// do not silently capture a stale ctx reference. Mutations to
|
|
3133
|
+
// __summarizationExhausted / __awaitingApproval / __awaitingUserQuestion /
|
|
3134
|
+
// __structuredOutputParsed are made on this object during execution.
|
|
3135
|
+
ctx: state.ctx,
|
|
2921
3136
|
agent: state.agent,
|
|
2922
|
-
pendingApprovals
|
|
3137
|
+
pendingApprovals,
|
|
3138
|
+
pendingUserQuestions
|
|
2923
3139
|
};
|
|
2924
3140
|
};
|
|
2925
3141
|
}
|
|
@@ -3154,7 +3370,8 @@ function resolveReflection(input, preset) {
|
|
|
3154
3370
|
maxChars: preset.maxChars,
|
|
3155
3371
|
keepLast: 3,
|
|
3156
3372
|
summarize: false,
|
|
3157
|
-
emitEvents: true
|
|
3373
|
+
emitEvents: true,
|
|
3374
|
+
everyNTurns: 1
|
|
3158
3375
|
};
|
|
3159
3376
|
}
|
|
3160
3377
|
const cfg = input ?? {};
|
|
@@ -3169,7 +3386,9 @@ function resolveReflection(input, preset) {
|
|
|
3169
3386
|
keepLast: cfg.keepLast ?? 3,
|
|
3170
3387
|
summarize: cfg.summarize ?? false,
|
|
3171
3388
|
promptTemplate: cfg.promptTemplate,
|
|
3172
|
-
emitEvents: cfg.emitEvents ?? true
|
|
3389
|
+
emitEvents: cfg.emitEvents ?? true,
|
|
3390
|
+
maxPerRun: typeof cfg.maxPerRun === "number" && cfg.maxPerRun >= 0 ? cfg.maxPerRun : void 0,
|
|
3391
|
+
everyNTurns: Math.max(1, Math.trunc(cfg.everyNTurns ?? 1))
|
|
3173
3392
|
};
|
|
3174
3393
|
}
|
|
3175
3394
|
function ensureArray(value) {
|
|
@@ -3382,6 +3601,229 @@ function resolveToolApprovalState(state, resolution) {
|
|
|
3382
3601
|
};
|
|
3383
3602
|
}
|
|
3384
3603
|
|
|
3604
|
+
// src/utils/userQuestions.ts
|
|
3605
|
+
var cloneQuestions = (questions) => Array.isArray(questions) ? questions.map((entry) => ({ ...entry })) : [];
|
|
3606
|
+
function validateAnswers(entry, answers, cancelled) {
|
|
3607
|
+
if (cancelled) {
|
|
3608
|
+
return answers || {};
|
|
3609
|
+
}
|
|
3610
|
+
const allowFreeText = entry.allowFreeText !== false;
|
|
3611
|
+
const out = {};
|
|
3612
|
+
const provided = answers || {};
|
|
3613
|
+
for (const question of entry.questions) {
|
|
3614
|
+
const required = question.required !== false;
|
|
3615
|
+
const key = question.question;
|
|
3616
|
+
const answer = provided[key] ?? provided[question.header || ""];
|
|
3617
|
+
if (!answer) {
|
|
3618
|
+
if (required) {
|
|
3619
|
+
throw new Error(`Missing answer for required question: "${key}".`);
|
|
3620
|
+
}
|
|
3621
|
+
continue;
|
|
3622
|
+
}
|
|
3623
|
+
const values = Array.isArray(answer.values) ? answer.values.filter((v) => typeof v === "string") : [];
|
|
3624
|
+
const freeText = typeof answer.freeText === "string" ? answer.freeText.trim() : "";
|
|
3625
|
+
const notes = typeof answer.notes === "string" ? answer.notes : void 0;
|
|
3626
|
+
if (freeText && !allowFreeText) {
|
|
3627
|
+
throw new Error(
|
|
3628
|
+
`Free-text answers are disabled for this agent (question: "${key}"). Pick one of the provided options.`
|
|
3629
|
+
);
|
|
3630
|
+
}
|
|
3631
|
+
if (values.length === 0 && !freeText) {
|
|
3632
|
+
if (required) {
|
|
3633
|
+
throw new Error(`Question "${key}" requires either a selected option or free-text answer.`);
|
|
3634
|
+
}
|
|
3635
|
+
continue;
|
|
3636
|
+
}
|
|
3637
|
+
if (values.length > 1 && !question.multiSelect) {
|
|
3638
|
+
throw new Error(`Question "${key}" is single-select but received multiple values.`);
|
|
3639
|
+
}
|
|
3640
|
+
if (Array.isArray(question.options) && question.options.length > 0 && values.length > 0) {
|
|
3641
|
+
const allowedValues = new Set(
|
|
3642
|
+
question.options.map((option) => option.value ?? option.label)
|
|
3643
|
+
);
|
|
3644
|
+
for (const value of values) {
|
|
3645
|
+
if (!allowedValues.has(value)) {
|
|
3646
|
+
if (!allowFreeText) {
|
|
3647
|
+
throw new Error(
|
|
3648
|
+
`Value "${value}" is not one of the allowed options for question "${key}".`
|
|
3649
|
+
);
|
|
3650
|
+
}
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
out[key] = { values, ...freeText ? { freeText } : {}, ...notes ? { notes } : {} };
|
|
3655
|
+
}
|
|
3656
|
+
return out;
|
|
3657
|
+
}
|
|
3658
|
+
function buildToolResponseContent(entry, answers, cancelled, notes) {
|
|
3659
|
+
if (cancelled) {
|
|
3660
|
+
return JSON.stringify({
|
|
3661
|
+
status: "cancelled",
|
|
3662
|
+
message: notes || "User dismissed the question without answering."
|
|
3663
|
+
});
|
|
3664
|
+
}
|
|
3665
|
+
const payload = {
|
|
3666
|
+
status: "answered",
|
|
3667
|
+
answers: entry.questions.map((question) => {
|
|
3668
|
+
const key = question.question;
|
|
3669
|
+
const ans = answers[key];
|
|
3670
|
+
return {
|
|
3671
|
+
question: key,
|
|
3672
|
+
header: question.header,
|
|
3673
|
+
values: ans?.values ?? [],
|
|
3674
|
+
freeText: ans?.freeText,
|
|
3675
|
+
notes: ans?.notes
|
|
3676
|
+
};
|
|
3677
|
+
}),
|
|
3678
|
+
...notes ? { reviewerNotes: notes } : {}
|
|
3679
|
+
};
|
|
3680
|
+
return JSON.stringify(payload);
|
|
3681
|
+
}
|
|
3682
|
+
function resolveUserQuestionState(state, resolution) {
|
|
3683
|
+
const pending = cloneQuestions(state.pendingUserQuestions);
|
|
3684
|
+
const matchIndex = pending.findIndex(
|
|
3685
|
+
(entry2) => entry2.id === resolution.id || entry2.toolCallId === resolution.id
|
|
3686
|
+
);
|
|
3687
|
+
if (matchIndex === -1) {
|
|
3688
|
+
throw new Error(`Pending user question not found for id: ${resolution.id}`);
|
|
3689
|
+
}
|
|
3690
|
+
const entry = { ...pending[matchIndex] };
|
|
3691
|
+
if (entry.status === "answered" || entry.status === "executed" || entry.status === "cancelled") {
|
|
3692
|
+
throw new Error(`User question ${entry.id} already resolved (status: ${entry.status}).`);
|
|
3693
|
+
}
|
|
3694
|
+
const cancelled = resolution.cancelled === true;
|
|
3695
|
+
const validated = validateAnswers(entry, resolution.answers, cancelled);
|
|
3696
|
+
entry.status = cancelled ? "cancelled" : "answered";
|
|
3697
|
+
entry.answers = validated;
|
|
3698
|
+
entry.cancelled = cancelled || void 0;
|
|
3699
|
+
entry.answeredBy = resolution.answeredBy;
|
|
3700
|
+
entry.notes = resolution.notes;
|
|
3701
|
+
entry.answeredAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3702
|
+
pending[matchIndex] = entry;
|
|
3703
|
+
const ctx = { ...state.ctx || {} };
|
|
3704
|
+
delete ctx.__awaitingUserQuestion;
|
|
3705
|
+
if (ctx.__resumeStage === "tools") {
|
|
3706
|
+
delete ctx.__resumeStage;
|
|
3707
|
+
}
|
|
3708
|
+
ctx.__userQuestionResolved = { id: entry.id, status: entry.status };
|
|
3709
|
+
const toolMessage = {
|
|
3710
|
+
role: "tool",
|
|
3711
|
+
name: "ask_user_question",
|
|
3712
|
+
tool_call_id: entry.toolCallId,
|
|
3713
|
+
content: buildToolResponseContent(entry, validated, cancelled, resolution.notes)
|
|
3714
|
+
};
|
|
3715
|
+
return {
|
|
3716
|
+
...state,
|
|
3717
|
+
pendingUserQuestions: pending,
|
|
3718
|
+
messages: [...state.messages || [], toolMessage],
|
|
3719
|
+
ctx
|
|
3720
|
+
};
|
|
3721
|
+
}
|
|
3722
|
+
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.";
|
|
3723
|
+
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.";
|
|
3724
|
+
function buildOptionSchema() {
|
|
3725
|
+
return z.object({
|
|
3726
|
+
label: z.string().min(1).describe("Display text the user sees"),
|
|
3727
|
+
value: z.string().min(1).optional().describe("Value returned in the answer; defaults to label"),
|
|
3728
|
+
description: z.string().optional(),
|
|
3729
|
+
preview: z.string().optional().describe("Optional code / ASCII preview rendered when this option is focused")
|
|
3730
|
+
}).strict();
|
|
3731
|
+
}
|
|
3732
|
+
function buildQuestionSchema(allowFreeText) {
|
|
3733
|
+
const optionSchema = buildOptionSchema();
|
|
3734
|
+
const baseShape = {
|
|
3735
|
+
question: z.string().min(1).describe("Full question shown to the user"),
|
|
3736
|
+
header: z.string().max(40).optional().describe("Short chip / tag rendered next to the question (max ~12 chars recommended)"),
|
|
3737
|
+
multiSelect: z.boolean().optional().describe("Allow multiple selections. Default: false"),
|
|
3738
|
+
placeholder: z.string().optional(),
|
|
3739
|
+
required: z.boolean().optional().describe("Default: true")
|
|
3740
|
+
};
|
|
3741
|
+
if (allowFreeText) {
|
|
3742
|
+
return z.object({
|
|
3743
|
+
...baseShape,
|
|
3744
|
+
options: z.array(optionSchema).min(2).max(12).optional()
|
|
3745
|
+
}).strict();
|
|
3746
|
+
}
|
|
3747
|
+
return z.object({
|
|
3748
|
+
...baseShape,
|
|
3749
|
+
options: z.array(optionSchema).min(2).max(12).describe("Required: free-text answers are disabled for this agent")
|
|
3750
|
+
}).strict();
|
|
3751
|
+
}
|
|
3752
|
+
function createAskUserQuestionTool(stateRef, config) {
|
|
3753
|
+
const allowFreeText = config.allowFreeText !== false;
|
|
3754
|
+
const description = config.promptOverride ?? (allowFreeText ? DEFAULT_DESCRIPTION_WITH_FREETEXT : DEFAULT_DESCRIPTION_NO_FREETEXT);
|
|
3755
|
+
const questionSchema = buildQuestionSchema(allowFreeText);
|
|
3756
|
+
const askUser = createTool({
|
|
3757
|
+
name: "ask_user_question",
|
|
3758
|
+
description,
|
|
3759
|
+
schema: z.object({
|
|
3760
|
+
questions: z.array(questionSchema).min(1).max(4).describe("1-4 related questions to ask in a single pause.")
|
|
3761
|
+
}).strict(),
|
|
3762
|
+
func: async ({ questions }) => {
|
|
3763
|
+
const ref = askUser._stateRef || (askUser._stateRef = {});
|
|
3764
|
+
const onEvent = ref.__onEvent;
|
|
3765
|
+
if (!ref.ctx) ref.ctx = {};
|
|
3766
|
+
const ctx = ref.ctx;
|
|
3767
|
+
const toolCallId = ref.__currentToolCallId || `ask_user_${nanoid(6)}`;
|
|
3768
|
+
if (!ref.pendingUserQuestions) ref.pendingUserQuestions = [];
|
|
3769
|
+
const pendingList = ref.pendingUserQuestions;
|
|
3770
|
+
const entry = {
|
|
3771
|
+
id: nanoid(),
|
|
3772
|
+
toolCallId,
|
|
3773
|
+
toolName: "ask_user_question",
|
|
3774
|
+
questions,
|
|
3775
|
+
status: "pending",
|
|
3776
|
+
requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3777
|
+
allowFreeText
|
|
3778
|
+
};
|
|
3779
|
+
pendingList.push(entry);
|
|
3780
|
+
ctx.__awaitingUserQuestion = {
|
|
3781
|
+
id: entry.id,
|
|
3782
|
+
toolCallId: entry.toolCallId,
|
|
3783
|
+
requestedAt: entry.requestedAt
|
|
3784
|
+
};
|
|
3785
|
+
ctx.__resumeStage = "tools";
|
|
3786
|
+
onEvent?.({
|
|
3787
|
+
type: "user_question",
|
|
3788
|
+
status: "pending",
|
|
3789
|
+
id: entry.id,
|
|
3790
|
+
toolCallId: entry.toolCallId,
|
|
3791
|
+
questions: entry.questions,
|
|
3792
|
+
allowFreeText
|
|
3793
|
+
});
|
|
3794
|
+
recordTraceEvent(ctx.__traceSession, {
|
|
3795
|
+
type: "user_question",
|
|
3796
|
+
label: "Ask User",
|
|
3797
|
+
actor: { scope: "agent", name: "ask_user_question", role: "user_interaction" },
|
|
3798
|
+
status: "success",
|
|
3799
|
+
sections: [
|
|
3800
|
+
{
|
|
3801
|
+
kind: "metadata",
|
|
3802
|
+
label: "Pending Question",
|
|
3803
|
+
data: {
|
|
3804
|
+
id: entry.id,
|
|
3805
|
+
toolCallId: entry.toolCallId,
|
|
3806
|
+
questions: entry.questions,
|
|
3807
|
+
allowFreeText
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
]
|
|
3811
|
+
});
|
|
3812
|
+
return {
|
|
3813
|
+
__awaitingUserQuestion: true,
|
|
3814
|
+
id: entry.id,
|
|
3815
|
+
toolCallId: entry.toolCallId
|
|
3816
|
+
};
|
|
3817
|
+
}
|
|
3818
|
+
});
|
|
3819
|
+
askUser._stateRef = stateRef;
|
|
3820
|
+
askUser.__source = "human-in-the-loop";
|
|
3821
|
+
return askUser;
|
|
3822
|
+
}
|
|
3823
|
+
|
|
3824
|
+
// src/agent.ts
|
|
3825
|
+
init_utilTokens();
|
|
3826
|
+
|
|
3385
3827
|
// src/utils/syntheticMessages.ts
|
|
3386
3828
|
function isSyntheticSummaryMessage(message) {
|
|
3387
3829
|
if (!message) return false;
|
|
@@ -3560,6 +4002,21 @@ function getActiveSummarizationThreshold(opts) {
|
|
|
3560
4002
|
}
|
|
3561
4003
|
return summarization.summaryTriggerTokens || summarization.maxTokens || opts.limits?.maxContextTokens || 5e4;
|
|
3562
4004
|
}
|
|
4005
|
+
function shouldSignalSummarization(state, threshold) {
|
|
4006
|
+
const tokCountMessages = (state.messages || []).filter((message) => {
|
|
4007
|
+
if (isSyntheticSummaryMessage(message)) return false;
|
|
4008
|
+
if (message.role === "system" && (message.name === "context_summary" || message.name === "memory_context")) return false;
|
|
4009
|
+
return true;
|
|
4010
|
+
});
|
|
4011
|
+
const tokenCount = countMessagesTokens(tokCountMessages);
|
|
4012
|
+
if (tokenCount <= threshold) return "skip";
|
|
4013
|
+
if (state.ctx?.__summarizationExhausted) return "skip";
|
|
4014
|
+
const hasFreshSummary = (state.messages || []).some(
|
|
4015
|
+
(m) => m.role === "tool" && typeof m.content === "string" && (m.content === "SUMMARIZED" || m.content.startsWith("SUMMARIZED_TOOL_RESPONSE"))
|
|
4016
|
+
);
|
|
4017
|
+
if (hasFreshSummary && tokenCount <= threshold * 1.15) return "skip";
|
|
4018
|
+
return "trigger";
|
|
4019
|
+
}
|
|
3563
4020
|
function clearNeedsSummarization(state) {
|
|
3564
4021
|
if (!state.ctx?.__needsSummarization) {
|
|
3565
4022
|
return state;
|
|
@@ -3575,6 +4032,21 @@ function createAgent(opts) {
|
|
|
3575
4032
|
const resolver = createResolverNode();
|
|
3576
4033
|
const agentCore = createAgentCoreNode(opts);
|
|
3577
4034
|
const toolsBase = [...opts.tools ?? []];
|
|
4035
|
+
const askUserConfig = (() => {
|
|
4036
|
+
const raw = opts.humanInTheLoop?.askUser;
|
|
4037
|
+
if (!raw) return void 0;
|
|
4038
|
+
if (raw === true) return { enabled: true, allowFreeText: true };
|
|
4039
|
+
return {
|
|
4040
|
+
enabled: true,
|
|
4041
|
+
allowFreeText: raw.allowFreeText !== false,
|
|
4042
|
+
promptOverride: raw.promptOverride
|
|
4043
|
+
};
|
|
4044
|
+
})();
|
|
4045
|
+
const askUserOnQuestion = typeof opts.humanInTheLoop?.askUser === "object" ? opts.humanInTheLoop?.askUser?.onQuestion : void 0;
|
|
4046
|
+
const askUserStateRef = askUserConfig ? { pendingUserQuestions: void 0, ctx: void 0, __onEvent: void 0, __currentToolCallId: void 0 } : void 0;
|
|
4047
|
+
if (askUserConfig && askUserStateRef) {
|
|
4048
|
+
toolsBase.push(createAskUserQuestionTool(askUserStateRef, askUserConfig));
|
|
4049
|
+
}
|
|
3578
4050
|
const soManager = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
|
|
3579
4051
|
if (soManager) {
|
|
3580
4052
|
const modelCapabilities = getModelCapabilities(opts.model);
|
|
@@ -3619,7 +4091,8 @@ function createAgent(opts) {
|
|
|
3619
4091
|
useTodoList: void 0,
|
|
3620
4092
|
outputSchema: opts.outputSchema,
|
|
3621
4093
|
responseFormat: soManager?.getResponseFormat(),
|
|
3622
|
-
tracing: opts.tracing
|
|
4094
|
+
tracing: opts.tracing,
|
|
4095
|
+
humanInTheLoop: askUserConfig ? { askUser: askUserConfig } : void 0
|
|
3623
4096
|
};
|
|
3624
4097
|
const summarizationThreshold = getActiveSummarizationThreshold(opts);
|
|
3625
4098
|
async function runLoop(initial, config, emit) {
|
|
@@ -3654,6 +4127,45 @@ function createAgent(opts) {
|
|
|
3654
4127
|
};
|
|
3655
4128
|
const maxToolCalls = mergedLimits?.maxToolCalls === void 0 ? 50 : mergedLimits?.maxToolCalls;
|
|
3656
4129
|
const iterationLimit = maxToolCalls === Infinity ? 100 : Math.max(maxToolCalls * 3 + 10, 40);
|
|
4130
|
+
const wallClockStart = Date.now();
|
|
4131
|
+
const maxWallClockMs = mergedLimits?.maxWallClockMs && mergedLimits.maxWallClockMs > 0 ? mergedLimits.maxWallClockMs : void 0;
|
|
4132
|
+
const maxTotalOutputTokens = mergedLimits?.maxTotalOutputTokens && mergedLimits.maxTotalOutputTokens > 0 ? mergedLimits.maxTotalOutputTokens : void 0;
|
|
4133
|
+
const maxCostUsd = mergedLimits?.maxCostUsd && mergedLimits.maxCostUsd > 0 ? mergedLimits.maxCostUsd : void 0;
|
|
4134
|
+
const costEstimator = opts.costEstimator;
|
|
4135
|
+
const checkBudgetBreached = () => {
|
|
4136
|
+
if (maxWallClockMs && Date.now() - wallClockStart > maxWallClockMs) {
|
|
4137
|
+
return { breached: true, reason: `maxWallClockMs (${maxWallClockMs}ms) exceeded` };
|
|
4138
|
+
}
|
|
4139
|
+
if (maxTotalOutputTokens) {
|
|
4140
|
+
const totals = state.usage?.totals;
|
|
4141
|
+
if (totals) {
|
|
4142
|
+
let outputSum = 0;
|
|
4143
|
+
for (const v of Object.values(totals)) outputSum += Number(v?.output) || 0;
|
|
4144
|
+
if (outputSum > maxTotalOutputTokens) {
|
|
4145
|
+
return { breached: true, reason: `maxTotalOutputTokens (${maxTotalOutputTokens}) exceeded` };
|
|
4146
|
+
}
|
|
4147
|
+
}
|
|
4148
|
+
}
|
|
4149
|
+
if (maxCostUsd && costEstimator) {
|
|
4150
|
+
const perRequest = state.usage?.perRequest;
|
|
4151
|
+
if (perRequest) {
|
|
4152
|
+
let cost = 0;
|
|
4153
|
+
for (const r of perRequest) {
|
|
4154
|
+
cost += costEstimator({
|
|
4155
|
+
modelName: r.modelName,
|
|
4156
|
+
inputTokens: Number(r.usage?.prompt_tokens) || 0,
|
|
4157
|
+
outputTokens: Number(r.usage?.completion_tokens) || 0,
|
|
4158
|
+
cachedInputTokens: Number(r.usage?.prompt_tokens_details?.cached_tokens) || 0,
|
|
4159
|
+
reasoningTokens: Number(r.usage?.completion_tokens_details?.reasoning_tokens) || 0
|
|
4160
|
+
}) || 0;
|
|
4161
|
+
}
|
|
4162
|
+
if (cost > maxCostUsd) {
|
|
4163
|
+
return { breached: true, reason: `maxCostUsd ($${maxCostUsd}) exceeded (actual: $${cost.toFixed(4)})` };
|
|
4164
|
+
}
|
|
4165
|
+
}
|
|
4166
|
+
}
|
|
4167
|
+
return { breached: false };
|
|
4168
|
+
};
|
|
3657
4169
|
let iterations = 0;
|
|
3658
4170
|
const onStateChange = config?.onStateChange;
|
|
3659
4171
|
const checkpointReason = config?.checkpointReason;
|
|
@@ -3702,6 +4214,13 @@ function createAgent(opts) {
|
|
|
3702
4214
|
};
|
|
3703
4215
|
while (iterations < iterationLimit) {
|
|
3704
4216
|
iterations++;
|
|
4217
|
+
const budget = checkBudgetBreached();
|
|
4218
|
+
if (budget.breached) {
|
|
4219
|
+
emit?.({ type: "metadata", limitBreached: budget.reason });
|
|
4220
|
+
const ctx = { ...state.ctx || {}, __limitBreached: budget.reason };
|
|
4221
|
+
state = { ...state, ctx };
|
|
4222
|
+
break;
|
|
4223
|
+
}
|
|
3705
4224
|
const traceSession = state.ctx?.__traceSession;
|
|
3706
4225
|
if (traceSession) {
|
|
3707
4226
|
const iterEvent = recordTraceEvent(traceSession, {
|
|
@@ -3757,22 +4276,11 @@ function createAgent(opts) {
|
|
|
3757
4276
|
}
|
|
3758
4277
|
}
|
|
3759
4278
|
if (summarizationThreshold !== void 0) {
|
|
3760
|
-
const
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
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
|
-
}
|
|
4279
|
+
const verdict = shouldSignalSummarization(state, summarizationThreshold);
|
|
4280
|
+
if (verdict === "trigger") {
|
|
4281
|
+
const ctx = { ...state.ctx || {}, __needsSummarization: true };
|
|
4282
|
+
state = { ...state, ctx };
|
|
4283
|
+
break;
|
|
3776
4284
|
}
|
|
3777
4285
|
}
|
|
3778
4286
|
onProgress?.({ stage: "agent", message: "Invoking model" });
|
|
@@ -3874,11 +4382,18 @@ function createAgent(opts) {
|
|
|
3874
4382
|
state = { ...state, ...await toolsNode(state) };
|
|
3875
4383
|
onProgress?.({ stage: "tools", message: "Tools finished" });
|
|
3876
4384
|
if (state.ctx?.__awaitingApproval) break;
|
|
4385
|
+
if (state.ctx?.__awaitingUserQuestion) break;
|
|
3877
4386
|
if (checkpointIfRequested("after_tools")) break;
|
|
3878
4387
|
if (state.ctx?.__finalizedDueToStructuredOutput) break;
|
|
3879
4388
|
if (reflectNode && resolvedReasoning?.reflection.enabled) {
|
|
3880
4389
|
const cadence = resolvedReasoning.reflection.cadence;
|
|
3881
|
-
|
|
4390
|
+
const refl = resolvedReasoning.reflection;
|
|
4391
|
+
const reflectionsSoFar = state.reflections?.length ?? 0;
|
|
4392
|
+
const lastTurn = state.reflections?.at(-1)?.turn ?? -Infinity;
|
|
4393
|
+
const currentTurn = state.toolCallCount || 0;
|
|
4394
|
+
const budgetExceeded = typeof refl.maxPerRun === "number" && reflectionsSoFar >= refl.maxPerRun;
|
|
4395
|
+
const cadenceGap = currentTurn - (Number.isFinite(lastTurn) ? lastTurn : -refl.everyNTurns) < refl.everyNTurns;
|
|
4396
|
+
if (!budgetExceeded && !cadenceGap && shouldRunReflection(cadence, state, true)) {
|
|
3882
4397
|
try {
|
|
3883
4398
|
const patch = await reflectNode(state, cadence);
|
|
3884
4399
|
if (patch && Object.keys(patch).length > 0) {
|
|
@@ -3938,7 +4453,7 @@ function createAgent(opts) {
|
|
|
3938
4453
|
}
|
|
3939
4454
|
const lastAfterLoop = state.messages[state.messages.length - 1];
|
|
3940
4455
|
if (lastAfterLoop?.role === "tool" && !pausedStage) {
|
|
3941
|
-
const isExpectedExit = state.ctx?.__awaitingApproval || state.ctx?.__cancelled || state.ctx?.__finalizedDueToStructuredOutput || state.ctx?.__finalizedDueToToolLimit || state.ctx?.__needsSummarization;
|
|
4456
|
+
const isExpectedExit = state.ctx?.__awaitingApproval || state.ctx?.__awaitingUserQuestion || state.ctx?.__cancelled || state.ctx?.__finalizedDueToStructuredOutput || state.ctx?.__finalizedDueToToolLimit || state.ctx?.__needsSummarization || state.ctx?.__limitBreached;
|
|
3942
4457
|
if (!isExpectedExit) {
|
|
3943
4458
|
throw new Error(
|
|
3944
4459
|
"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,7 +4466,22 @@ function createAgent(opts) {
|
|
|
3951
4466
|
return state;
|
|
3952
4467
|
}
|
|
3953
4468
|
const invokeAgent = async (input, config) => {
|
|
3954
|
-
const
|
|
4469
|
+
const userTokenCounter = opts.tokenCounter;
|
|
4470
|
+
if (userTokenCounter) {
|
|
4471
|
+
const { setTokenCounter: setTokenCounter2 } = await Promise.resolve().then(() => (init_utilTokens(), utilTokens_exports));
|
|
4472
|
+
setTokenCounter2(userTokenCounter);
|
|
4473
|
+
}
|
|
4474
|
+
const callerOnEvent = config?.onEvent;
|
|
4475
|
+
const onEvent = askUserOnQuestion ? (event) => {
|
|
4476
|
+
if (event && event.type === "user_question") {
|
|
4477
|
+
try {
|
|
4478
|
+
askUserOnQuestion(event);
|
|
4479
|
+
} catch (err) {
|
|
4480
|
+
console.warn("[agent-sdk] humanInTheLoop.askUser.onQuestion error:", err);
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4483
|
+
callerOnEvent?.(event);
|
|
4484
|
+
} : callerOnEvent;
|
|
3955
4485
|
const onProgress = config?.onProgress;
|
|
3956
4486
|
const onStream = config?.onStream;
|
|
3957
4487
|
const streamEnabled = config?.stream === true;
|
|
@@ -3995,6 +4525,7 @@ function createAgent(opts) {
|
|
|
3995
4525
|
metadata: input.metadata,
|
|
3996
4526
|
ctx,
|
|
3997
4527
|
pendingApprovals: input.pendingApprovals || [],
|
|
4528
|
+
pendingUserQuestions: input.pendingUserQuestions || [],
|
|
3998
4529
|
agent: input.agent || runtimeWithInvokeLimits,
|
|
3999
4530
|
usage: input.usage || { perRequest: [], totals: {} }
|
|
4000
4531
|
};
|
|
@@ -4008,7 +4539,19 @@ function createAgent(opts) {
|
|
|
4008
4539
|
status: "error",
|
|
4009
4540
|
error: { message: err?.message, stack: err?.stack }
|
|
4010
4541
|
});
|
|
4542
|
+
if (userTokenCounter) {
|
|
4543
|
+
const { setTokenCounter: setTokenCounter2 } = await Promise.resolve().then(() => (init_utilTokens(), utilTokens_exports));
|
|
4544
|
+
setTokenCounter2(void 0);
|
|
4545
|
+
}
|
|
4011
4546
|
throw err;
|
|
4547
|
+
} finally {
|
|
4548
|
+
if (userTokenCounter) {
|
|
4549
|
+
try {
|
|
4550
|
+
const { setTokenCounter: setTokenCounter2 } = await Promise.resolve().then(() => (init_utilTokens(), utilTokens_exports));
|
|
4551
|
+
setTokenCounter2(void 0);
|
|
4552
|
+
} catch {
|
|
4553
|
+
}
|
|
4554
|
+
}
|
|
4012
4555
|
}
|
|
4013
4556
|
await finalizeTraceSession(traceSession, {
|
|
4014
4557
|
agentRuntime: res.agent || runtime,
|
|
@@ -4059,19 +4602,67 @@ function createAgent(opts) {
|
|
|
4059
4602
|
return invokeAgent(restoredState, config);
|
|
4060
4603
|
};
|
|
4061
4604
|
const resolveToolApproval = (state, resolution) => resolveToolApprovalState(state, resolution);
|
|
4605
|
+
const resolveUserQuestion = (state, resolution) => resolveUserQuestionState(state, resolution);
|
|
4062
4606
|
const instance = {
|
|
4063
4607
|
invoke: invokeAgent,
|
|
4064
4608
|
snapshot: snapshotState,
|
|
4065
4609
|
resume: resumeAgent,
|
|
4066
4610
|
resolveToolApproval,
|
|
4611
|
+
resolveUserQuestion,
|
|
4067
4612
|
asTool: ({ toolName, description, inputDescription }) => {
|
|
4068
4613
|
const schema = z.object({ input: z.string().describe(inputDescription || "Input for delegated agent") });
|
|
4069
|
-
|
|
4614
|
+
const delegatedTool = createTool({
|
|
4070
4615
|
name: toolName,
|
|
4071
4616
|
description: description || `Delegate task to agent ${opts.name || "Agent"}`,
|
|
4072
4617
|
schema,
|
|
4073
4618
|
func: async ({ input }) => {
|
|
4074
|
-
const
|
|
4619
|
+
const ref = delegatedTool._stateRef;
|
|
4620
|
+
const parentCtx = ref.ctx || {};
|
|
4621
|
+
const parentMessages = Array.isArray(ref.messages) ? ref.messages : [];
|
|
4622
|
+
const parentRuntime = ref.parentRuntime;
|
|
4623
|
+
const resolvedSmart = parentRuntime?.smart || opts.__resolvedSmart;
|
|
4624
|
+
const delegationPolicy = resolvedSmart?.delegation || {
|
|
4625
|
+
mode: "role_based",
|
|
4626
|
+
maxDelegationDepth: 2,
|
|
4627
|
+
maxChildCalls: 4,
|
|
4628
|
+
childContextPolicy: "scoped"
|
|
4629
|
+
};
|
|
4630
|
+
const currentDepth = Number(parentCtx.__delegationDepth) || 0;
|
|
4631
|
+
if (delegationPolicy.mode === "off") {
|
|
4632
|
+
return { error: "Delegation disabled by runtime policy." };
|
|
4633
|
+
}
|
|
4634
|
+
if (currentDepth >= delegationPolicy.maxDelegationDepth) {
|
|
4635
|
+
return {
|
|
4636
|
+
error: `Delegation depth limit reached (${delegationPolicy.maxDelegationDepth}). Refusing nested call.`
|
|
4637
|
+
};
|
|
4638
|
+
}
|
|
4639
|
+
parentCtx.__delegatedCallCount = (parentCtx.__delegatedCallCount || 0) + 1;
|
|
4640
|
+
if (parentCtx.__delegatedCallCount > delegationPolicy.maxChildCalls) {
|
|
4641
|
+
return {
|
|
4642
|
+
error: `Child-call budget exhausted (${delegationPolicy.maxChildCalls}).`
|
|
4643
|
+
};
|
|
4644
|
+
}
|
|
4645
|
+
let childMessages;
|
|
4646
|
+
switch (delegationPolicy.childContextPolicy) {
|
|
4647
|
+
case "full":
|
|
4648
|
+
childMessages = [...parentMessages, { role: "user", content: input }];
|
|
4649
|
+
break;
|
|
4650
|
+
case "scoped": {
|
|
4651
|
+
const systemPrefix = parentMessages[0]?.role === "system" ? [parentMessages[0]] : [];
|
|
4652
|
+
const lastUser = [...parentMessages].reverse().find((m) => m?.role === "user");
|
|
4653
|
+
childMessages = [
|
|
4654
|
+
...systemPrefix,
|
|
4655
|
+
...lastUser ? [lastUser] : [],
|
|
4656
|
+
{ role: "user", content: input }
|
|
4657
|
+
];
|
|
4658
|
+
break;
|
|
4659
|
+
}
|
|
4660
|
+
case "minimal":
|
|
4661
|
+
default:
|
|
4662
|
+
childMessages = [{ role: "user", content: input }];
|
|
4663
|
+
}
|
|
4664
|
+
const childCtx = { __delegationDepth: currentDepth + 1 };
|
|
4665
|
+
const res = await instance.invoke({ messages: childMessages, ctx: childCtx });
|
|
4075
4666
|
return {
|
|
4076
4667
|
content: res.content,
|
|
4077
4668
|
output: res.output,
|
|
@@ -4079,6 +4670,8 @@ function createAgent(opts) {
|
|
|
4079
4670
|
};
|
|
4080
4671
|
}
|
|
4081
4672
|
});
|
|
4673
|
+
delegatedTool._stateRef = {};
|
|
4674
|
+
return delegatedTool;
|
|
4082
4675
|
},
|
|
4083
4676
|
asHandoff: ({ toolName, description, schema }) => {
|
|
4084
4677
|
const finalName = toolName || `handoff_to_${runtime.name || "agent"}`;
|
|
@@ -4218,7 +4811,11 @@ function createAgentNode(opts) {
|
|
|
4218
4811
|
};
|
|
4219
4812
|
}
|
|
4220
4813
|
|
|
4814
|
+
// src/nodes/contextSummarize.ts
|
|
4815
|
+
init_utilTokens();
|
|
4816
|
+
|
|
4221
4817
|
// src/smart/contextPolicy.ts
|
|
4818
|
+
init_utilTokens();
|
|
4222
4819
|
function collectRecentTurns(messages, lastTurnsToKeep) {
|
|
4223
4820
|
if (lastTurnsToKeep <= 0) return [];
|
|
4224
4821
|
const systemPrefix = messages.filter((message, index) => index === 0 && message.role === "system");
|
|
@@ -4521,7 +5118,7 @@ function createContextSummarizeNode(opts) {
|
|
|
4521
5118
|
const resolved = getResolvedSmartConfig(opts, state.agent);
|
|
4522
5119
|
const config = resolved.summarization;
|
|
4523
5120
|
if (!config.enable) return {};
|
|
4524
|
-
const model = opts.model;
|
|
5121
|
+
const model = state.agent?.model || opts.model;
|
|
4525
5122
|
const messages = state.messages || [];
|
|
4526
5123
|
const onEvent = state.ctx?.__onEvent;
|
|
4527
5124
|
const traceSession = state.ctx?.__traceSession;
|
|
@@ -4840,6 +5437,10 @@ ${canonicalFacts.length > 0 ? canonicalFacts.map((fact) => `- ${fact.key}: ${fac
|
|
|
4840
5437
|
};
|
|
4841
5438
|
};
|
|
4842
5439
|
}
|
|
5440
|
+
|
|
5441
|
+
// src/index.ts
|
|
5442
|
+
init_utilTokens();
|
|
5443
|
+
init_utilTokens();
|
|
4843
5444
|
var TOOL_RESPONSE_RECOVERY_MARKERS = [
|
|
4844
5445
|
/ARCHIVED_TOOL_RESPONSE\s*\[/,
|
|
4845
5446
|
/STRUCTURED_TOOL_RESPONSE\s*\[/,
|
|
@@ -5158,6 +5759,7 @@ function createContextTools(stateRef, opts) {
|
|
|
5158
5759
|
}
|
|
5159
5760
|
|
|
5160
5761
|
// src/graph/decisions.ts
|
|
5762
|
+
init_utilTokens();
|
|
5161
5763
|
function needsSummarization(state, opts, summarizationEnabled) {
|
|
5162
5764
|
if (!summarizationEnabled) {
|
|
5163
5765
|
return false;
|
|
@@ -5320,12 +5922,38 @@ function createSmartAgent(opts) {
|
|
|
5320
5922
|
toolResponses: resolved.toolResponses,
|
|
5321
5923
|
useTodoList: planningEnabled
|
|
5322
5924
|
};
|
|
5323
|
-
const
|
|
5324
|
-
const
|
|
5325
|
-
const
|
|
5326
|
-
const
|
|
5327
|
-
|
|
5328
|
-
|
|
5925
|
+
const userTools = opts.tools ?? [];
|
|
5926
|
+
const factoryStateRef = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
|
|
5927
|
+
const factoryContextTools = createContextTools(factoryStateRef, { planningEnabled, includeGetToolResponse: false });
|
|
5928
|
+
const askUserConfig = (() => {
|
|
5929
|
+
const raw = opts.humanInTheLoop?.askUser;
|
|
5930
|
+
if (!raw) return void 0;
|
|
5931
|
+
if (raw === true) return { enabled: true, allowFreeText: true };
|
|
5932
|
+
return {
|
|
5933
|
+
enabled: true,
|
|
5934
|
+
allowFreeText: raw.allowFreeText !== false,
|
|
5935
|
+
promptOverride: raw.promptOverride
|
|
5936
|
+
};
|
|
5937
|
+
})();
|
|
5938
|
+
const factoryToolsWithoutRecovery = [...userTools, ...factoryContextTools];
|
|
5939
|
+
if (askUserConfig) {
|
|
5940
|
+
factoryToolsWithoutRecovery.push(createAskUserQuestionTool(factoryStateRef, askUserConfig));
|
|
5941
|
+
}
|
|
5942
|
+
const soManagerFactory = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
|
|
5943
|
+
const modelCapabilities = opts.outputSchema ? getModelCapabilities(opts.model) : void 0;
|
|
5944
|
+
const responseFinalizeTool = soManagerFactory && modelCapabilities?.structuredOutput !== "native" ? soManagerFactory.getResponseTool() : void 0;
|
|
5945
|
+
const buildInvokeToolSet = () => {
|
|
5946
|
+
const ref = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
|
|
5947
|
+
const ctxTools = createContextTools(ref, { planningEnabled, includeGetToolResponse: false });
|
|
5948
|
+
const recoveryTool = createGetToolResponseTool(ref);
|
|
5949
|
+
const base2 = [...userTools, ...ctxTools];
|
|
5950
|
+
if (responseFinalizeTool) base2.push(responseFinalizeTool);
|
|
5951
|
+
if (askUserConfig) base2.push(createAskUserQuestionTool(ref, askUserConfig));
|
|
5952
|
+
const without = base2;
|
|
5953
|
+
const withRec = [...without, recoveryTool];
|
|
5954
|
+
return { stateRef: ref, toolsWithoutRecovery: without, toolsWithRecovery: withRec };
|
|
5955
|
+
};
|
|
5956
|
+
const base = createAgent({ ...runtimeOpts, tools: factoryToolsWithoutRecovery });
|
|
5329
5957
|
base.__runtime.runtimeProfile = resolved.runtimeProfile;
|
|
5330
5958
|
base.__runtime.smart = resolved;
|
|
5331
5959
|
const summarizationEnabled = resolved.summarization.enable !== false;
|
|
@@ -5361,17 +5989,17 @@ function createSmartAgent(opts) {
|
|
|
5361
5989
|
const memoryFacts = await writeSummaryFactsToMemory(memoryStore, resolved, latestSummary, Math.max(1, (state.messages || []).filter((message) => message.role === "user").length));
|
|
5362
5990
|
return { ...state, memoryFacts };
|
|
5363
5991
|
}
|
|
5364
|
-
function
|
|
5365
|
-
if (!Array.isArray(
|
|
5992
|
+
function syncPlanStateWith(ref, state) {
|
|
5993
|
+
if (!Array.isArray(ref.todoList)) return state;
|
|
5366
5994
|
return {
|
|
5367
5995
|
...state,
|
|
5368
5996
|
plan: {
|
|
5369
|
-
version:
|
|
5370
|
-
steps:
|
|
5997
|
+
version: ref.planVersion || state.planVersion || 1,
|
|
5998
|
+
steps: ref.todoList,
|
|
5371
5999
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5372
|
-
adherenceScore:
|
|
6000
|
+
adherenceScore: ref.adherenceScore || 0
|
|
5373
6001
|
},
|
|
5374
|
-
planVersion:
|
|
6002
|
+
planVersion: ref.planVersion || state.planVersion || 1
|
|
5375
6003
|
};
|
|
5376
6004
|
}
|
|
5377
6005
|
function systemMessage2() {
|
|
@@ -5383,9 +6011,9 @@ function createSmartAgent(opts) {
|
|
|
5383
6011
|
);
|
|
5384
6012
|
return { role: "system", content: sys };
|
|
5385
6013
|
}
|
|
5386
|
-
function syncRuntimeTools(currentState, currentMessages) {
|
|
6014
|
+
function syncRuntimeTools(currentState, currentMessages, toolSet) {
|
|
5387
6015
|
const needsRecoveryTool = hasToolResponseRecoveryReference(currentMessages);
|
|
5388
|
-
const nextTools = needsRecoveryTool ? toolsWithRecovery : toolsWithoutRecovery;
|
|
6016
|
+
const nextTools = needsRecoveryTool ? toolSet.toolsWithRecovery : toolSet.toolsWithoutRecovery;
|
|
5389
6017
|
const currentRuntime = currentState.agent || base.__runtime;
|
|
5390
6018
|
if (currentRuntime.tools === nextTools) {
|
|
5391
6019
|
return currentState;
|
|
@@ -5404,7 +6032,7 @@ function createSmartAgent(opts) {
|
|
|
5404
6032
|
const toolCalls = Array.isArray(lastAssistant.tool_calls) ? lastAssistant.tool_calls : [];
|
|
5405
6033
|
return toolCalls.length === 0;
|
|
5406
6034
|
}
|
|
5407
|
-
async function trySummarize(currentState, currentRawMessages) {
|
|
6035
|
+
async function trySummarize(currentState, currentRawMessages, ref) {
|
|
5408
6036
|
if (!summarizer) return { state: currentState, rawMessages: currentRawMessages, compressed: false };
|
|
5409
6037
|
const delta = await summarizer(currentState);
|
|
5410
6038
|
if (!delta || Object.keys(delta).length === 0) {
|
|
@@ -5413,28 +6041,31 @@ function createSmartAgent(opts) {
|
|
|
5413
6041
|
}
|
|
5414
6042
|
const ctx = { ...currentState.ctx || {} };
|
|
5415
6043
|
delete ctx.__summarizationExhausted;
|
|
5416
|
-
const updated = await persistLatestSummary(
|
|
6044
|
+
const updated = await persistLatestSummary(syncPlanStateWith(ref, { ...currentState, ...delta, ctx }));
|
|
5417
6045
|
return { state: updated, rawMessages: [...updated.messages || currentRawMessages], compressed: true };
|
|
5418
6046
|
}
|
|
5419
6047
|
const instance = {
|
|
5420
6048
|
invoke: async (input, config) => {
|
|
6049
|
+
const toolSet = buildInvokeToolSet();
|
|
6050
|
+
const stateRef = toolSet.stateRef;
|
|
5421
6051
|
stateRef.toolHistory = input.toolHistory;
|
|
5422
6052
|
stateRef.toolHistoryArchived = input.toolHistoryArchived;
|
|
5423
6053
|
stateRef.todoList = input.plan?.steps;
|
|
5424
6054
|
stateRef.planVersion = input.planVersion || input.plan?.version || 0;
|
|
5425
6055
|
stateRef.adherenceScore = input.plan?.adherenceScore || 0;
|
|
6056
|
+
const syncPlanFromRef = (state2) => syncPlanStateWith(stateRef, state2);
|
|
5426
6057
|
const alreadyHasSystem = Array.isArray(input.messages) && input.messages[0]?.role === "system";
|
|
5427
6058
|
const seedMessages = alreadyHasSystem ? [...input.messages || []] : [systemMessage2(), ...input.messages || []];
|
|
5428
|
-
let state = syncRuntimeTools(await syncMemory({ ...input, messages: seedMessages }), seedMessages);
|
|
6059
|
+
let state = syncRuntimeTools(await syncMemory({ ...input, messages: seedMessages }), seedMessages, toolSet);
|
|
5429
6060
|
let lastResult = null;
|
|
5430
6061
|
let rawMessages = [...seedMessages];
|
|
5431
6062
|
const effectiveMaxToolCalls = config?.limits?.maxToolCalls ?? resolved.limits.maxToolCalls ?? 10;
|
|
5432
6063
|
const iterationLimit = Math.max(effectiveMaxToolCalls * 3 + 5, 30);
|
|
5433
6064
|
for (let i = 0; i < iterationLimit; i++) {
|
|
5434
|
-
state = syncRuntimeTools({ ...state, messages: rawMessages }, rawMessages);
|
|
6065
|
+
state = syncRuntimeTools({ ...state, messages: rawMessages }, rawMessages, toolSet);
|
|
5435
6066
|
const next = summarizationEnabled ? decideBefore(state) : "agent";
|
|
5436
6067
|
if (next === "contextSummarize" && summarizer) {
|
|
5437
|
-
const result = await trySummarize(state, rawMessages);
|
|
6068
|
+
const result = await trySummarize(state, rawMessages, stateRef);
|
|
5438
6069
|
state = result.state;
|
|
5439
6070
|
rawMessages = result.rawMessages;
|
|
5440
6071
|
if (result.compressed) continue;
|
|
@@ -5458,14 +6089,14 @@ function createSmartAgent(opts) {
|
|
|
5458
6089
|
if (currentPlan && !state.plan) {
|
|
5459
6090
|
state = { ...state, plan: currentPlan };
|
|
5460
6091
|
}
|
|
5461
|
-
state = syncRuntimeTools(
|
|
6092
|
+
state = syncRuntimeTools(syncPlanFromRef(await syncMemory(state)), rawMessages, toolSet);
|
|
5462
6093
|
stateRef.toolHistory = state.toolHistory;
|
|
5463
6094
|
stateRef.toolHistoryArchived = state.toolHistoryArchived;
|
|
5464
6095
|
if (state.ctx?.__needsSummarization && summarizer) {
|
|
5465
6096
|
const ctx = { ...state.ctx || {} };
|
|
5466
6097
|
delete ctx.__needsSummarization;
|
|
5467
6098
|
state = { ...state, ctx };
|
|
5468
|
-
const result = await trySummarize(state, rawMessages);
|
|
6099
|
+
const result = await trySummarize(state, rawMessages, stateRef);
|
|
5469
6100
|
state = result.state;
|
|
5470
6101
|
rawMessages = result.rawMessages;
|
|
5471
6102
|
continue;
|
|
@@ -5476,7 +6107,7 @@ function createSmartAgent(opts) {
|
|
|
5476
6107
|
if (summarizationEnabled) {
|
|
5477
6108
|
const after = decideAfter(state);
|
|
5478
6109
|
if (after === "contextSummarize" && summarizer) {
|
|
5479
|
-
const result = await trySummarize(state, rawMessages);
|
|
6110
|
+
const result = await trySummarize(state, rawMessages, stateRef);
|
|
5480
6111
|
state = result.state;
|
|
5481
6112
|
rawMessages = result.rawMessages;
|
|
5482
6113
|
if (result.compressed) continue;
|
|
@@ -5502,6 +6133,7 @@ function createSmartAgent(opts) {
|
|
|
5502
6133
|
snapshot: base.snapshot,
|
|
5503
6134
|
resume: base.resume,
|
|
5504
6135
|
resolveToolApproval: base.resolveToolApproval,
|
|
6136
|
+
resolveUserQuestion: base.resolveUserQuestion,
|
|
5505
6137
|
asTool: base.asTool,
|
|
5506
6138
|
asHandoff: base.asHandoff,
|
|
5507
6139
|
__runtime: base.__runtime
|
|
@@ -5915,7 +6547,50 @@ var ProviderError = class extends Error {
|
|
|
5915
6547
|
};
|
|
5916
6548
|
|
|
5917
6549
|
// src/providers/base.ts
|
|
6550
|
+
var DEFAULT_RETRY = {
|
|
6551
|
+
maxRetries: 3,
|
|
6552
|
+
baseDelayMs: 500,
|
|
6553
|
+
maxDelayMs: 8e3
|
|
6554
|
+
};
|
|
5918
6555
|
var BaseProvider = class {
|
|
6556
|
+
constructor() {
|
|
6557
|
+
this.retryPolicy = DEFAULT_RETRY;
|
|
6558
|
+
}
|
|
6559
|
+
/**
|
|
6560
|
+
* Helper concrete providers can wrap their fetch with. Retries 429/5xx
|
|
6561
|
+
* with exponential backoff and respects Retry-After if the response
|
|
6562
|
+
* carries one.
|
|
6563
|
+
*/
|
|
6564
|
+
async doFetchWithRetry(execute) {
|
|
6565
|
+
const policy = this.retryPolicy;
|
|
6566
|
+
const maxRetries = Math.max(0, policy.maxRetries ?? DEFAULT_RETRY.maxRetries);
|
|
6567
|
+
const baseDelay = Math.max(0, policy.baseDelayMs ?? DEFAULT_RETRY.baseDelayMs);
|
|
6568
|
+
const maxDelay = Math.max(baseDelay, policy.maxDelayMs ?? DEFAULT_RETRY.maxDelayMs);
|
|
6569
|
+
let lastResp;
|
|
6570
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
6571
|
+
const res = await execute();
|
|
6572
|
+
const isRetryable = res.status === 429 || res.status >= 500 && res.status < 600;
|
|
6573
|
+
if (!isRetryable) return res;
|
|
6574
|
+
const predicate = policy.shouldRetry;
|
|
6575
|
+
if (predicate && !predicate({ status: res.status }, attempt)) return res;
|
|
6576
|
+
if (attempt === maxRetries) {
|
|
6577
|
+
lastResp = res;
|
|
6578
|
+
break;
|
|
6579
|
+
}
|
|
6580
|
+
const retryAfter = res.headers.get("retry-after");
|
|
6581
|
+
let delay = Math.min(maxDelay, baseDelay * Math.pow(2, attempt));
|
|
6582
|
+
if (retryAfter) {
|
|
6583
|
+
const asNumber = Number(retryAfter);
|
|
6584
|
+
if (!Number.isNaN(asNumber)) delay = Math.min(maxDelay, Math.max(delay, asNumber * 1e3));
|
|
6585
|
+
}
|
|
6586
|
+
try {
|
|
6587
|
+
await res.text();
|
|
6588
|
+
} catch {
|
|
6589
|
+
}
|
|
6590
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
6591
|
+
}
|
|
6592
|
+
return lastResp;
|
|
6593
|
+
}
|
|
5919
6594
|
};
|
|
5920
6595
|
|
|
5921
6596
|
// src/providers/utils/sse.ts
|
|
@@ -6042,6 +6717,7 @@ var OpenAIProvider = class extends BaseProvider {
|
|
|
6042
6717
|
...config.defaultHeaders,
|
|
6043
6718
|
...config.organization ? { "OpenAI-Organization": config.organization } : {}
|
|
6044
6719
|
};
|
|
6720
|
+
if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
|
|
6045
6721
|
}
|
|
6046
6722
|
async complete(request) {
|
|
6047
6723
|
const body = this.buildRequestBody(request, false);
|
|
@@ -6189,15 +6865,13 @@ var OpenAIProvider = class extends BaseProvider {
|
|
|
6189
6865
|
}
|
|
6190
6866
|
async doFetch(body) {
|
|
6191
6867
|
const url = `${this.baseURL}/chat/completions`;
|
|
6192
|
-
const
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
body: JSON.stringify(body)
|
|
6200
|
-
});
|
|
6868
|
+
const headers = {
|
|
6869
|
+
"Content-Type": "application/json",
|
|
6870
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
6871
|
+
...this.defaultHeaders
|
|
6872
|
+
};
|
|
6873
|
+
const payload = JSON.stringify(body);
|
|
6874
|
+
const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload }));
|
|
6201
6875
|
if (!res.ok) {
|
|
6202
6876
|
const text = await res.text().catch(() => "");
|
|
6203
6877
|
throw new ProviderError(
|
|
@@ -6267,6 +6941,8 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
6267
6941
|
this.defaultModel = config.defaultModel ?? "claude-sonnet-4-20250514";
|
|
6268
6942
|
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
6269
6943
|
this.anthropicVersion = config.anthropicVersion ?? "2023-06-01";
|
|
6944
|
+
this.promptCachingEnabled = config.prompt_caching?.enabled === true;
|
|
6945
|
+
if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
|
|
6270
6946
|
}
|
|
6271
6947
|
async complete(request) {
|
|
6272
6948
|
const body = this.buildRequestBody(request, false);
|
|
@@ -6385,12 +7061,22 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
6385
7061
|
max_tokens: request.maxTokens ?? 4096,
|
|
6386
7062
|
stream
|
|
6387
7063
|
};
|
|
6388
|
-
if (system)
|
|
7064
|
+
if (system) {
|
|
7065
|
+
if (this.promptCachingEnabled) {
|
|
7066
|
+
body.system = [{ type: "text", text: system, cache_control: { type: "ephemeral" } }];
|
|
7067
|
+
} else {
|
|
7068
|
+
body.system = system;
|
|
7069
|
+
}
|
|
7070
|
+
}
|
|
6389
7071
|
if (request.temperature != null) body.temperature = request.temperature;
|
|
6390
7072
|
if (request.topP != null) body.top_p = request.topP;
|
|
6391
7073
|
if (request.stop) body.stop_sequences = request.stop;
|
|
6392
7074
|
if (request.tools?.length) {
|
|
6393
|
-
|
|
7075
|
+
const tools = request.tools.map((t) => this.toAnthropicTool(t));
|
|
7076
|
+
if (this.promptCachingEnabled && tools.length > 0) {
|
|
7077
|
+
tools[tools.length - 1] = { ...tools[tools.length - 1], cache_control: { type: "ephemeral" } };
|
|
7078
|
+
}
|
|
7079
|
+
body.tools = tools;
|
|
6394
7080
|
}
|
|
6395
7081
|
if (request.toolChoice != null) {
|
|
6396
7082
|
if (request.toolChoice === "auto") {
|
|
@@ -6489,16 +7175,14 @@ var AnthropicProvider = class extends BaseProvider {
|
|
|
6489
7175
|
}
|
|
6490
7176
|
async doFetch(body) {
|
|
6491
7177
|
const url = `${this.baseURL}/v1/messages`;
|
|
6492
|
-
const
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
body: JSON.stringify(body)
|
|
6501
|
-
});
|
|
7178
|
+
const headers = {
|
|
7179
|
+
"Content-Type": "application/json",
|
|
7180
|
+
"x-api-key": this.apiKey,
|
|
7181
|
+
"anthropic-version": this.anthropicVersion,
|
|
7182
|
+
...this.defaultHeaders
|
|
7183
|
+
};
|
|
7184
|
+
const payload = JSON.stringify(body);
|
|
7185
|
+
const res = await this.doFetchWithRetry(() => fetch(url, { method: "POST", headers, body: payload }));
|
|
6502
7186
|
if (!res.ok) {
|
|
6503
7187
|
const text = await res.text().catch(() => "");
|
|
6504
7188
|
throw new ProviderError(
|
|
@@ -6710,6 +7394,8 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
6710
7394
|
this.region = config.region;
|
|
6711
7395
|
this.defaultModel = config.defaultModel ?? "anthropic.claude-sonnet-4-20250514-v1:0";
|
|
6712
7396
|
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
7397
|
+
this.promptCachingEnabled = config.prompt_caching?.enabled === true;
|
|
7398
|
+
if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
|
|
6713
7399
|
this.credentials = {
|
|
6714
7400
|
accessKeyId: config.accessKeyId ?? process.env.AWS_ACCESS_KEY_ID ?? "",
|
|
6715
7401
|
secretAccessKey: config.secretAccessKey ?? process.env.AWS_SECRET_ACCESS_KEY ?? "",
|
|
@@ -6748,7 +7434,9 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
6748
7434
|
messages: messages.map((m) => this.toBedrockMessage(m))
|
|
6749
7435
|
};
|
|
6750
7436
|
if (system.length > 0) {
|
|
6751
|
-
|
|
7437
|
+
const systemBlocks = system.map((s) => ({ text: s }));
|
|
7438
|
+
if (this.promptCachingEnabled) systemBlocks.push({ cachePoint: { type: "default" } });
|
|
7439
|
+
body.system = systemBlocks;
|
|
6752
7440
|
}
|
|
6753
7441
|
const inferenceConfig = {};
|
|
6754
7442
|
if (request.maxTokens != null) inferenceConfig.maxTokens = request.maxTokens;
|
|
@@ -6757,8 +7445,12 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
6757
7445
|
if (request.stop) inferenceConfig.stopSequences = request.stop;
|
|
6758
7446
|
if (Object.keys(inferenceConfig).length > 0) body.inferenceConfig = inferenceConfig;
|
|
6759
7447
|
if (request.tools?.length) {
|
|
7448
|
+
const tools = request.tools.map((t) => this.toBedrockTool(t));
|
|
7449
|
+
if (this.promptCachingEnabled && tools.length > 0) {
|
|
7450
|
+
tools.push({ cachePoint: { type: "default" } });
|
|
7451
|
+
}
|
|
6760
7452
|
body.toolConfig = {
|
|
6761
|
-
tools
|
|
7453
|
+
tools
|
|
6762
7454
|
};
|
|
6763
7455
|
if (request.toolChoice != null) {
|
|
6764
7456
|
if (request.toolChoice === "auto") {
|
|
@@ -6855,23 +7547,21 @@ var BedrockProvider = class extends BaseProvider {
|
|
|
6855
7547
|
const host = `bedrock-runtime.${this.region}.amazonaws.com`;
|
|
6856
7548
|
const url = `https://${host}/model/${encodeURIComponent(modelId)}/converse`;
|
|
6857
7549
|
const bodyStr = JSON.stringify(body);
|
|
6858
|
-
const
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
method: "POST",
|
|
6873
|
-
headers,
|
|
6874
|
-
body: bodyStr
|
|
7550
|
+
const res = await this.doFetchWithRetry(() => {
|
|
7551
|
+
const headers = signRequest({
|
|
7552
|
+
method: "POST",
|
|
7553
|
+
url,
|
|
7554
|
+
headers: {
|
|
7555
|
+
"Content-Type": "application/json",
|
|
7556
|
+
Accept: "application/json",
|
|
7557
|
+
...this.defaultHeaders
|
|
7558
|
+
},
|
|
7559
|
+
body: bodyStr,
|
|
7560
|
+
region: this.region,
|
|
7561
|
+
service: "bedrock",
|
|
7562
|
+
credentials: this.credentials
|
|
7563
|
+
});
|
|
7564
|
+
return fetch(url, { method: "POST", headers, body: bodyStr });
|
|
6875
7565
|
});
|
|
6876
7566
|
if (!res.ok) {
|
|
6877
7567
|
const text = await res.text().catch(() => "");
|
|
@@ -7746,6 +8436,6 @@ function createProvider(config) {
|
|
|
7746
8436
|
}
|
|
7747
8437
|
}
|
|
7748
8438
|
|
|
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 };
|
|
8439
|
+
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, createAskUserQuestionTool, 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, resolveUserQuestionState, restoreSnapshot, runSmartAgentEvalHarness, setTokenCounter, startStreamingSession, traceSessionToOtlp, withTools, writeSummaryFactsToMemory };
|
|
7750
8440
|
//# sourceMappingURL=index.mjs.map
|
|
7751
8441
|
//# sourceMappingURL=index.mjs.map
|