@cognipeer/agent-sdk 0.6.5 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -42,7 +42,7 @@ function getTokenCounter() {
42
42
  function countApproxTokens(text) {
43
43
  return activeTokenCounter(text || "");
44
44
  }
45
- function extractMessageText(m) {
45
+ function extractMessageText2(m) {
46
46
  if (!m) return "";
47
47
  const parts = [];
48
48
  parts.push(m.role || "");
@@ -92,7 +92,7 @@ function extractMessageText(m) {
92
92
  }
93
93
  function countMessagesTokens(messages) {
94
94
  if (!Array.isArray(messages) || messages.length === 0) return 0;
95
- const allText = messages.map(extractMessageText).join("\n");
95
+ const allText = messages.map(extractMessageText2).join("\n");
96
96
  return countApproxTokens(allText) + messages.length * 4;
97
97
  }
98
98
  var activeTokenCounter;
@@ -743,6 +743,7 @@ function createResolverNode() {
743
743
  toolHistory: state.toolHistory || [],
744
744
  toolHistoryArchived: state.toolHistoryArchived || [],
745
745
  pendingApprovals: state.pendingApprovals || [],
746
+ pendingUserQuestions: state.pendingUserQuestions || [],
746
747
  plan: state.plan || null,
747
748
  planVersion: state.planVersion || 0
748
749
  };
@@ -1261,6 +1262,7 @@ function eventToOtlpSpan(event) {
1261
1262
  stringAttr("cognipeer.actor.role", event.actor?.role),
1262
1263
  stringAttr("cognipeer.model", event.model),
1263
1264
  stringAttr("cognipeer.provider", event.provider),
1265
+ stringAttr("cognipeer.tool.details", event.toolDetails ? JSON.stringify(event.toolDetails) : void 0),
1264
1266
  intAttr("cognipeer.tokens.input", event.inputTokens),
1265
1267
  intAttr("cognipeer.tokens.output", event.outputTokens),
1266
1268
  intAttr("cognipeer.tokens.total", event.totalTokens),
@@ -1732,6 +1734,7 @@ function recordTraceEvent(session, event) {
1732
1734
  responseBytes,
1733
1735
  model: resolvedModel,
1734
1736
  provider: resolvedProvider,
1737
+ toolDetails: session.resolvedConfig.logData ? sanitizeTracePayload(event.toolDetails) : void 0,
1735
1738
  toolExecutionId: event.toolExecutionId,
1736
1739
  retryOf: event.retryOf,
1737
1740
  error: event.error ?? (status === "error" ? { message: "Unknown error" } : void 0) ?? void 0,
@@ -2182,16 +2185,99 @@ function createAgentCoreNode(opts) {
2182
2185
  };
2183
2186
  }
2184
2187
 
2188
+ // src/utils/content.ts
2189
+ function contentToString(content) {
2190
+ try {
2191
+ if (content == null) return "";
2192
+ if (typeof content === "string") return content;
2193
+ if (Array.isArray(content)) {
2194
+ return content.map((p) => {
2195
+ if (!p || typeof p !== "object") return String(p);
2196
+ if (p.type === "text") return String(p.text ?? "");
2197
+ if (p.type === "image_url") {
2198
+ const img = p.image_url;
2199
+ const url = typeof img === "string" ? img : img?.url;
2200
+ const detail = typeof img === "string" ? void 0 : img?.detail;
2201
+ return `[image:${url || "unknown"}${detail ? ` detail=${detail}` : ""}]`;
2202
+ }
2203
+ return JSON.stringify(p);
2204
+ }).join("\n");
2205
+ }
2206
+ return JSON.stringify(content);
2207
+ } catch {
2208
+ return String(content);
2209
+ }
2210
+ }
2211
+ function mergeContentsToString(contents) {
2212
+ return contents.map((c) => contentToString(c)).join("\n");
2213
+ }
2214
+ function safeStringify(value, pretty = false) {
2215
+ if (value == null) return "";
2216
+ if (typeof value === "string") return value;
2217
+ try {
2218
+ return JSON.stringify(value, null, pretty ? 2 : void 0) ?? String(value);
2219
+ } catch {
2220
+ return String(value);
2221
+ }
2222
+ }
2223
+ function extractMessageText(message) {
2224
+ if (!message) return "";
2225
+ const content = message.content;
2226
+ if (typeof content === "string") return content;
2227
+ if (Array.isArray(content)) {
2228
+ return content.map((part) => typeof part === "string" ? part : part?.text ?? part?.content ?? "").join("");
2229
+ }
2230
+ return "";
2231
+ }
2232
+
2233
+ // src/utils/traceSections.ts
2234
+ function truncate(text, max = 500) {
2235
+ if (!text) return "";
2236
+ return text.length > max ? `${text.slice(0, max)}\u2026` : text;
2237
+ }
2238
+ function summarizeToolOutput(output) {
2239
+ if (output == null) return "";
2240
+ if (typeof output === "string") return truncate(output.trim());
2241
+ if (Array.isArray(output)) {
2242
+ const preview = output.slice(0, 3).map((item, index) => `${index + 1}. ${truncate(safeStringify(item))}`).join("\n");
2243
+ const suffix = output.length > 3 ? `
2244
+ \u2026 (+${output.length - 3} more)` : "";
2245
+ return `${preview}${suffix}`.trim();
2246
+ }
2247
+ if (typeof output === "object") {
2248
+ if (Array.isArray(output.items)) {
2249
+ return summarizeToolOutput(output.items);
2250
+ }
2251
+ return truncate(safeStringify(output));
2252
+ }
2253
+ return truncate(String(output));
2254
+ }
2255
+ function extractToolItems(output) {
2256
+ const source = Array.isArray(output) ? output : Array.isArray(output?.items) ? output.items : void 0;
2257
+ if (!Array.isArray(source) || source.length === 0) return void 0;
2258
+ return source.slice(0, 5).map((item) => {
2259
+ if (item && typeof item === "object") {
2260
+ const snippetSource = item.content ?? item.snippet ?? item.description ?? item.text ?? item.summary ?? item;
2261
+ return {
2262
+ title: typeof item.title === "string" ? item.title : void 0,
2263
+ url: typeof item.url === "string" ? item.url : void 0,
2264
+ snippet: truncate(safeStringify(snippetSource))
2265
+ };
2266
+ }
2267
+ return { snippet: truncate(safeStringify(item)) };
2268
+ });
2269
+ }
2270
+
2185
2271
  // src/smart/runtimeConfig.ts
2186
2272
  var DEFAULT_CRITICAL_TOOLS = ["response", "manage_todo_list", "get_tool_response"];
2187
2273
  var BASE_DEFAULTS = {
2188
- limits: { maxToolCalls: 8, maxParallelTools: 2, maxContextTokens: 24e3, maxTotalOutputTokens: 0, maxCostUsd: 0, maxWallClockMs: 0 },
2274
+ limits: { maxToolCalls: 20, maxParallelTools: 5, maxContextTokens: 96e3, maxTotalOutputTokens: 0, maxCostUsd: 0, maxWallClockMs: 0 },
2189
2275
  summarization: {
2190
2276
  enable: true,
2191
- maxTokens: 24e3,
2192
- summaryTriggerTokens: 17e3,
2193
- summaryPromptMaxTokens: 7e3,
2194
- summaryCompressionRatioTarget: 0.35,
2277
+ maxTokens: 96e3,
2278
+ summaryTriggerTokens: 72e3,
2279
+ summaryPromptMaxTokens: 12e3,
2280
+ summaryCompressionRatioTarget: 0.45,
2195
2281
  summaryMode: "incremental",
2196
2282
  promptTemplate: "",
2197
2283
  toolFreeCall: true,
@@ -2199,13 +2285,13 @@ var BASE_DEFAULTS = {
2199
2285
  },
2200
2286
  context: {
2201
2287
  policy: "hybrid",
2202
- lastTurnsToKeep: 8,
2288
+ lastTurnsToKeep: 16,
2203
2289
  toolResponsePolicy: "summarize_archive",
2204
2290
  budget: {
2205
- systemReserveTokens: 1200,
2206
- goalsReserveTokens: 1200,
2207
- recentTurnsReserveTokens: 5200,
2208
- toolResponseReserveTokens: 2800
2291
+ systemReserveTokens: 2e3,
2292
+ goalsReserveTokens: 2e3,
2293
+ recentTurnsReserveTokens: 18e3,
2294
+ toolResponseReserveTokens: 9e3
2209
2295
  }
2210
2296
  },
2211
2297
  planning: {
@@ -2222,14 +2308,14 @@ var BASE_DEFAULTS = {
2222
2308
  delegation: {
2223
2309
  mode: "role_based",
2224
2310
  maxDelegationDepth: 2,
2225
- maxChildCalls: 4,
2226
- maxParallelChild: 2,
2311
+ maxChildCalls: 6,
2312
+ maxParallelChild: 3,
2227
2313
  childContextPolicy: "scoped",
2228
2314
  requireJsonOutputContract: true
2229
2315
  },
2230
2316
  toolResponses: {
2231
- maxToolResponseChars: 12e3,
2232
- maxToolResponseTokens: 3e3,
2317
+ maxToolResponseChars: 32e3,
2318
+ maxToolResponseTokens: 8e3,
2233
2319
  defaultPolicy: "summarize_archive",
2234
2320
  toolResponseRetentionByTool: {},
2235
2321
  criticalTools: [...DEFAULT_CRITICAL_TOOLS],
@@ -2258,54 +2344,54 @@ function buildProfile(overrides) {
2258
2344
  }
2259
2345
  var DEFAULT_PROFILE_CONFIGS = {
2260
2346
  fast: buildProfile({
2261
- limits: { maxToolCalls: 4, maxParallelTools: 1, maxContextTokens: 12e3 },
2347
+ limits: { maxToolCalls: 8, maxParallelTools: 3, maxContextTokens: 32e3 },
2262
2348
  summarization: {
2263
- maxTokens: 12e3,
2264
- summaryTriggerTokens: 9e3,
2265
- summaryPromptMaxTokens: 5e3,
2266
- summaryCompressionRatioTarget: 0.4
2349
+ maxTokens: 32e3,
2350
+ summaryTriggerTokens: 24e3,
2351
+ summaryPromptMaxTokens: 6e3,
2352
+ summaryCompressionRatioTarget: 0.5
2267
2353
  },
2268
2354
  context: {
2269
- lastTurnsToKeep: 6,
2355
+ lastTurnsToKeep: 8,
2270
2356
  toolResponsePolicy: "keep_structured",
2271
- budget: { systemReserveTokens: 1e3, goalsReserveTokens: 900, recentTurnsReserveTokens: 3200, toolResponseReserveTokens: 1800 }
2357
+ budget: { systemReserveTokens: 1500, goalsReserveTokens: 1200, recentTurnsReserveTokens: 6e3, toolResponseReserveTokens: 3500 }
2272
2358
  },
2273
2359
  memory: { readPolicy: "recent_only" },
2274
2360
  delegation: { mode: "off", maxDelegationDepth: 1, maxChildCalls: 2, maxParallelChild: 1, childContextPolicy: "minimal" },
2275
- toolResponses: { maxToolResponseChars: 8e3, maxToolResponseTokens: 2e3 }
2361
+ toolResponses: { maxToolResponseChars: 16e3, maxToolResponseTokens: 4e3 }
2276
2362
  }),
2277
2363
  balanced: buildProfile({}),
2278
2364
  deep: buildProfile({
2279
- limits: { maxToolCalls: 14, maxParallelTools: 3, maxContextTokens: 42e3 },
2365
+ limits: { maxToolCalls: 40, maxParallelTools: 8, maxContextTokens: 2e5 },
2280
2366
  summarization: {
2281
- maxTokens: 42e3,
2282
- summaryTriggerTokens: 3e4,
2283
- summaryPromptMaxTokens: 9e3,
2284
- summaryCompressionRatioTarget: 0.3
2367
+ maxTokens: 2e5,
2368
+ summaryTriggerTokens: 15e4,
2369
+ summaryPromptMaxTokens: 16e3,
2370
+ summaryCompressionRatioTarget: 0.4
2285
2371
  },
2286
2372
  context: {
2287
- lastTurnsToKeep: 12,
2288
- budget: { systemReserveTokens: 1400, goalsReserveTokens: 1600, recentTurnsReserveTokens: 7e3, toolResponseReserveTokens: 3600 }
2373
+ lastTurnsToKeep: 30,
2374
+ budget: { systemReserveTokens: 2500, goalsReserveTokens: 2500, recentTurnsReserveTokens: 36e3, toolResponseReserveTokens: 18e3 }
2289
2375
  },
2290
2376
  memory: { scope: "workspace" },
2291
- delegation: { maxDelegationDepth: 3, maxChildCalls: 6 },
2292
- toolResponses: { maxToolResponseChars: 16e3, maxToolResponseTokens: 4e3 }
2377
+ delegation: { maxDelegationDepth: 3, maxChildCalls: 10, maxParallelChild: 4 },
2378
+ toolResponses: { maxToolResponseChars: 64e3, maxToolResponseTokens: 16e3 }
2293
2379
  }),
2294
2380
  research: buildProfile({
2295
- limits: { maxToolCalls: 20, maxParallelTools: 4, maxContextTokens: 56e3 },
2381
+ limits: { maxToolCalls: 80, maxParallelTools: 10, maxContextTokens: 4e5 },
2296
2382
  summarization: {
2297
- maxTokens: 56e3,
2298
- summaryTriggerTokens: 42e3,
2299
- summaryPromptMaxTokens: 1e4,
2300
- summaryCompressionRatioTarget: 0.28
2383
+ maxTokens: 4e5,
2384
+ summaryTriggerTokens: 3e5,
2385
+ summaryPromptMaxTokens: 24e3,
2386
+ summaryCompressionRatioTarget: 0.35
2301
2387
  },
2302
2388
  context: {
2303
- lastTurnsToKeep: 20,
2304
- budget: { systemReserveTokens: 1600, goalsReserveTokens: 1800, recentTurnsReserveTokens: 9e3, toolResponseReserveTokens: 4800 }
2389
+ lastTurnsToKeep: 60,
2390
+ budget: { systemReserveTokens: 3e3, goalsReserveTokens: 3e3, recentTurnsReserveTokens: 72e3, toolResponseReserveTokens: 32e3 }
2305
2391
  },
2306
2392
  memory: { scope: "workspace" },
2307
- delegation: { mode: "automatic", maxDelegationDepth: 4, maxChildCalls: 8, maxParallelChild: 3, childContextPolicy: "full" },
2308
- toolResponses: { maxToolResponseChars: 24e3, maxToolResponseTokens: 6e3 }
2393
+ delegation: { mode: "automatic", maxDelegationDepth: 4, maxChildCalls: 16, maxParallelChild: 6, childContextPolicy: "full" },
2394
+ toolResponses: { maxToolResponseChars: 96e3, maxToolResponseTokens: 24e3 }
2309
2395
  })
2310
2396
  };
2311
2397
  function mergeLimits(base, override) {
@@ -2395,53 +2481,6 @@ function getResolvedSmartConfig(opts, runtime) {
2395
2481
 
2396
2482
  // src/smart/toolResponses.ts
2397
2483
  init_utilTokens();
2398
-
2399
- // src/utils/content.ts
2400
- function contentToString(content) {
2401
- try {
2402
- if (content == null) return "";
2403
- if (typeof content === "string") return content;
2404
- if (Array.isArray(content)) {
2405
- return content.map((p) => {
2406
- if (!p || typeof p !== "object") return String(p);
2407
- if (p.type === "text") return String(p.text ?? "");
2408
- if (p.type === "image_url") {
2409
- const img = p.image_url;
2410
- const url = typeof img === "string" ? img : img?.url;
2411
- const detail = typeof img === "string" ? void 0 : img?.detail;
2412
- return `[image:${url || "unknown"}${detail ? ` detail=${detail}` : ""}]`;
2413
- }
2414
- return JSON.stringify(p);
2415
- }).join("\n");
2416
- }
2417
- return JSON.stringify(content);
2418
- } catch {
2419
- return String(content);
2420
- }
2421
- }
2422
- function mergeContentsToString(contents) {
2423
- return contents.map((c) => contentToString(c)).join("\n");
2424
- }
2425
- function safeStringify(value, pretty = false) {
2426
- if (value == null) return "";
2427
- if (typeof value === "string") return value;
2428
- try {
2429
- return JSON.stringify(value, null, pretty ? 2 : void 0) ?? String(value);
2430
- } catch {
2431
- return String(value);
2432
- }
2433
- }
2434
- function extractMessageText2(message) {
2435
- if (!message) return "";
2436
- const content = message.content;
2437
- if (typeof content === "string") return content;
2438
- if (Array.isArray(content)) {
2439
- return content.map((part) => typeof part === "string" ? part : part?.text ?? part?.content ?? "").join("");
2440
- }
2441
- return "";
2442
- }
2443
-
2444
- // src/smart/toolResponses.ts
2445
2484
  function summarizeObject(value) {
2446
2485
  if (value == null) return "null";
2447
2486
  if (typeof value === "string") {
@@ -2556,6 +2595,97 @@ function normalizeToolCall(call) {
2556
2595
  args: call.args ?? call.arguments ?? call.input ?? call.function?.arguments ?? call.function_call?.arguments
2557
2596
  };
2558
2597
  }
2598
+ function normalizeToolSchema(tool, toolName) {
2599
+ const schema = tool.schema;
2600
+ if (!schema) return void 0;
2601
+ if (typeof schema === "object" && schema._def) {
2602
+ try {
2603
+ return sanitizeTracePayload(zodToJsonSchema(schema, { name: toolName }));
2604
+ } catch {
2605
+ }
2606
+ }
2607
+ if (schema && typeof schema === "object" && typeof schema.toJSON === "function") {
2608
+ try {
2609
+ return sanitizeTracePayload(schema.toJSON());
2610
+ } catch {
2611
+ }
2612
+ }
2613
+ return sanitizeTracePayload(schema);
2614
+ }
2615
+ function buildToolDetails(tool, toolName) {
2616
+ if (!tool) return void 0;
2617
+ const details = { name: toolName };
2618
+ if (typeof tool.description === "string" && tool.description.trim().length > 0) {
2619
+ details.description = tool.description;
2620
+ }
2621
+ const inputSchema = normalizeToolSchema(tool, toolName);
2622
+ if (inputSchema !== void 0) details.inputSchema = inputSchema;
2623
+ const needsApproval = typeof tool.needsApproval === "boolean" ? tool.needsApproval : void 0;
2624
+ if (needsApproval !== void 0 || tool.approvalPrompt !== void 0 || tool.approvalDefaults !== void 0) {
2625
+ details.approval = {
2626
+ required: needsApproval,
2627
+ prompt: typeof tool.approvalPrompt === "string" ? tool.approvalPrompt : void 0,
2628
+ defaults: tool.approvalDefaults !== void 0 ? sanitizeTracePayload(tool.approvalDefaults) : void 0
2629
+ };
2630
+ }
2631
+ const cache = tool.cache;
2632
+ if (cache !== void 0) {
2633
+ details.cache = {
2634
+ enabled: Boolean(cache),
2635
+ scope: cache && typeof cache === "object" && typeof cache.scope === "string" ? cache.scope : void 0,
2636
+ ttlMs: cache && typeof cache === "object" && typeof cache.ttlMs === "number" ? cache.ttlMs : void 0,
2637
+ hasKeyFn: Boolean(cache && typeof cache === "object" && typeof cache.keyFn === "function")
2638
+ };
2639
+ }
2640
+ const retry = tool.retry;
2641
+ if (retry && typeof retry === "object") {
2642
+ details.retry = {
2643
+ maxRetries: typeof retry.maxRetries === "number" ? retry.maxRetries : void 0,
2644
+ backoffMs: typeof retry.backoffMs === "number" ? retry.backoffMs : void 0,
2645
+ circuitBreakerThreshold: typeof retry.circuitBreakerThreshold === "number" ? retry.circuitBreakerThreshold : void 0,
2646
+ hasShouldRetry: typeof retry.shouldRetry === "function"
2647
+ };
2648
+ }
2649
+ if (tool.maxExecutionsPerRun !== void 0) {
2650
+ details.limits = { maxExecutionsPerRun: tool.maxExecutionsPerRun };
2651
+ }
2652
+ if (typeof tool.__source === "string") {
2653
+ details.source = tool.__source;
2654
+ }
2655
+ return details;
2656
+ }
2657
+ function buildToolTraceSections(params) {
2658
+ const sanitizedArgs = sanitizeTracePayload(params.args ?? {});
2659
+ const sanitizedOutput = sanitizeTracePayload(params.output);
2660
+ const summary = params.summary ?? summarizeToolOutput(sanitizedOutput);
2661
+ const items = extractToolItems(sanitizedOutput);
2662
+ return [
2663
+ {
2664
+ kind: "tool_call",
2665
+ label: `Tool Call: ${params.toolName}`,
2666
+ tool: params.toolName,
2667
+ arguments: sanitizedArgs,
2668
+ toolDetails: params.toolDetails
2669
+ },
2670
+ {
2671
+ kind: "tool_result",
2672
+ label: `Tool Result: ${params.toolName}`,
2673
+ tool: params.toolName,
2674
+ summary,
2675
+ items,
2676
+ output: sanitizedOutput,
2677
+ toolDetails: params.toolDetails,
2678
+ execution: {
2679
+ id: params.executionId,
2680
+ status: params.status,
2681
+ durationMs: params.durationMs,
2682
+ fromCache: params.fromCache
2683
+ },
2684
+ classification: params.classification,
2685
+ retentionPolicy: params.retentionPolicy
2686
+ }
2687
+ ];
2688
+ }
2559
2689
  function createToolsNode(initialTools, opts) {
2560
2690
  return async (state) => {
2561
2691
  const runtime = state.agent || {
@@ -2584,13 +2714,16 @@ function createToolsNode(initialTools, opts) {
2584
2714
  const traceSession = state.ctx?.__traceSession;
2585
2715
  const pendingApprovals = Array.isArray(state.pendingApprovals) ? state.pendingApprovals.map((entry) => ({ ...entry })) : [];
2586
2716
  const pendingByCallId = new Map(pendingApprovals.map((entry) => [entry.toolCallId, entry]));
2717
+ const pendingUserQuestions = Array.isArray(state.pendingUserQuestions) ? state.pendingUserQuestions.map((entry) => ({ ...entry })) : [];
2587
2718
  let awaitingApproval = false;
2719
+ let awaitingUserQuestion = false;
2588
2720
  for (const tool of toolByName.values()) {
2589
2721
  const anyTool = tool;
2590
2722
  if (anyTool._stateRef && typeof anyTool._stateRef === "object") {
2591
2723
  anyTool._stateRef.toolHistory = state.toolHistory;
2592
2724
  anyTool._stateRef.toolHistoryArchived = state.toolHistoryArchived;
2593
2725
  anyTool._stateRef.pendingApprovals = pendingApprovals;
2726
+ anyTool._stateRef.pendingUserQuestions = pendingUserQuestions;
2594
2727
  anyTool._stateRef.messages = state.messages;
2595
2728
  anyTool._stateRef.ctx = state.ctx || (state.ctx = {});
2596
2729
  anyTool._stateRef.__onEvent = onEvent;
@@ -2661,9 +2794,11 @@ function createToolsNode(initialTools, opts) {
2661
2794
  args = validatedArgs.value;
2662
2795
  }
2663
2796
  const toolName = tool.name || tc.name;
2797
+ const toolDetails = buildToolDetails(tool, toolName);
2664
2798
  const toolStateRef = tool._stateRef && typeof tool._stateRef === "object" ? tool._stateRef : null;
2665
2799
  if (toolStateRef) {
2666
2800
  toolStateRef.pendingApprovals = pendingApprovals;
2801
+ toolStateRef.pendingUserQuestions = pendingUserQuestions;
2667
2802
  toolStateRef.ctx = state.ctx || (state.ctx = {});
2668
2803
  toolStateRef.__currentToolCallId = toolCallId;
2669
2804
  delete toolStateRef.__awaitingApproval;
@@ -2680,21 +2815,17 @@ function createToolsNode(initialTools, opts) {
2680
2815
  label: `Tool Skipped - ${toolName}`,
2681
2816
  actor: { scope: "tool", name: toolName, role: "tool" },
2682
2817
  status: "skipped",
2818
+ toolDetails,
2683
2819
  toolExecutionId: tc.id,
2684
- messageList: [
2685
- {
2686
- role: "assistant",
2687
- name: toolName,
2688
- content: limitMessage,
2689
- tool_calls: [
2690
- {
2691
- id: tc.id,
2692
- type: "function",
2693
- function: { name: toolName, arguments: sanitizeTracePayload(args) }
2694
- }
2695
- ]
2696
- }
2697
- ]
2820
+ sections: buildToolTraceSections({
2821
+ args,
2822
+ executionId: tc.id,
2823
+ output: limitMessage,
2824
+ status: "skipped",
2825
+ summary: limitMessage,
2826
+ toolDetails,
2827
+ toolName
2828
+ })
2698
2829
  });
2699
2830
  toolCount += 1;
2700
2831
  return { status: "error" };
@@ -2781,6 +2912,25 @@ function createToolsNode(initialTools, opts) {
2781
2912
  push({ role: "tool", content: breakerMsg, tool_call_id: toolCallId, name: tc.name });
2782
2913
  markToolFailure(breakerMsg, toolName, toolCallId);
2783
2914
  onEvent?.({ type: "tool_call", phase: "error", name: toolName, id: tc.id, args, error: { message: breakerMsg } });
2915
+ recordTraceEvent(traceSession, {
2916
+ type: "tool_call",
2917
+ label: `Tool Error - ${toolName}`,
2918
+ actor: { scope: "tool", name: toolName, role: "tool" },
2919
+ status: "error",
2920
+ requestBytes: inputBytes,
2921
+ toolDetails,
2922
+ toolExecutionId: toolCallId,
2923
+ error: { message: breakerMsg },
2924
+ sections: buildToolTraceSections({
2925
+ args,
2926
+ executionId: toolCallId,
2927
+ output: { message: breakerMsg },
2928
+ status: "error",
2929
+ summary: breakerMsg,
2930
+ toolDetails,
2931
+ toolName
2932
+ })
2933
+ });
2784
2934
  toolCount += 1;
2785
2935
  return { status: "error" };
2786
2936
  }
@@ -2805,6 +2955,38 @@ function createToolsNode(initialTools, opts) {
2805
2955
  });
2806
2956
  push({ role: "tool", content: responsePolicy2.content, tool_call_id: tc.id || `${tc.name}_${toolCount}`, name: tc.name });
2807
2957
  onEvent?.({ type: "tool_call", phase: "success", name: toolName, id: tc.id, args, result: cachedHit, durationMs: 0 });
2958
+ const sanitizedCachedHit = sanitizeTracePayload(cachedHit);
2959
+ const outputBytes2 = traceSession?.resolvedConfig.logData ? estimatePayloadBytes(sanitizedCachedHit) : void 0;
2960
+ recordTraceEvent(traceSession, {
2961
+ type: "tool_call",
2962
+ label: `Tool Execution - ${toolName}`,
2963
+ actor: { scope: "tool", name: toolName, role: "tool" },
2964
+ durationMs: 0,
2965
+ requestBytes: inputBytes,
2966
+ responseBytes: outputBytes2,
2967
+ toolDetails,
2968
+ toolExecutionId: executionId2,
2969
+ sections: buildToolTraceSections({
2970
+ args,
2971
+ classification: responsePolicy2.classification,
2972
+ durationMs: 0,
2973
+ executionId: executionId2,
2974
+ fromCache: true,
2975
+ output: sanitizedCachedHit,
2976
+ retentionPolicy: "keep_full",
2977
+ status: "cached",
2978
+ summary: responsePolicy2.content,
2979
+ toolDetails,
2980
+ toolName
2981
+ }),
2982
+ debug: {
2983
+ classification: responsePolicy2.classification,
2984
+ retentionPolicy: "keep_full",
2985
+ originalTokenCount: responsePolicy2.tokenCount,
2986
+ truncated: responsePolicy2.truncated,
2987
+ fromCache: true
2988
+ }
2989
+ });
2808
2990
  toolCount += 1;
2809
2991
  return { status: "success" };
2810
2992
  }
@@ -2875,6 +3057,22 @@ function createToolsNode(initialTools, opts) {
2875
3057
  approval: pendingApproval
2876
3058
  };
2877
3059
  }
3060
+ if (output && typeof output === "object" && output.__awaitingUserQuestion) {
3061
+ if (toolStateRef?.ctx && typeof toolStateRef.ctx === "object") {
3062
+ state.ctx = toolStateRef.ctx;
3063
+ }
3064
+ const pendingQuestion = pendingUserQuestions.find(
3065
+ (entry) => entry.toolCallId === output.toolCallId || entry.id === output.id
3066
+ );
3067
+ if (!pendingQuestion) {
3068
+ throw new Error(`Tool ${toolName} requested a user question but no pending question entry was recorded.`);
3069
+ }
3070
+ awaitingUserQuestion = true;
3071
+ return {
3072
+ status: "awaiting_user_question",
3073
+ userQuestion: pendingQuestion
3074
+ };
3075
+ }
2878
3076
  if (output && typeof output === "object" && output.__finalStructuredOutput) {
2879
3077
  if (!state.ctx) state.ctx = {};
2880
3078
  state.ctx.__structuredOutputParsed = output.data;
@@ -2909,28 +3107,6 @@ function createToolsNode(initialTools, opts) {
2909
3107
  }
2910
3108
  const sanitizedOutput = sanitizeTracePayload(output);
2911
3109
  const outputBytes = traceSession?.resolvedConfig.logData ? estimatePayloadBytes(sanitizedOutput) : void 0;
2912
- const messageList = [
2913
- {
2914
- role: "assistant",
2915
- name: toolName,
2916
- content: "",
2917
- tool_calls: [
2918
- {
2919
- id: tc.id || executionId,
2920
- type: "function",
2921
- function: {
2922
- name: toolName,
2923
- arguments: sanitizedArgs
2924
- }
2925
- }
2926
- ]
2927
- },
2928
- {
2929
- role: "tool",
2930
- name: toolName,
2931
- content: responsePolicy.content
2932
- }
2933
- ];
2934
3110
  recordTraceEvent(traceSession, {
2935
3111
  type: "tool_call",
2936
3112
  label: `Tool Execution - ${toolName}`,
@@ -2938,8 +3114,20 @@ function createToolsNode(initialTools, opts) {
2938
3114
  durationMs,
2939
3115
  requestBytes: inputBytes,
2940
3116
  responseBytes: outputBytes,
3117
+ toolDetails,
2941
3118
  toolExecutionId: executionId,
2942
- messageList,
3119
+ sections: buildToolTraceSections({
3120
+ args: sanitizedArgs,
3121
+ classification: responsePolicy.classification,
3122
+ durationMs,
3123
+ executionId,
3124
+ output: sanitizedOutput,
3125
+ retentionPolicy: "keep_full",
3126
+ status: "success",
3127
+ summary: responsePolicy.content,
3128
+ toolDetails,
3129
+ toolName
3130
+ }),
2943
3131
  debug: {
2944
3132
  classification: responsePolicy.classification,
2945
3133
  retentionPolicy: "keep_full",
@@ -2958,6 +3146,7 @@ function createToolsNode(initialTools, opts) {
2958
3146
  markToolFailure(message, toolName, toolCallId);
2959
3147
  onEvent?.({ type: "tool_call", phase: "error", name: toolName, id: tc.id, args, error: { message } });
2960
3148
  onProgress?.({ stage: "tools", message: `Tool ${tc.name} failed`, detail: { error: message } });
3149
+ const errorOutput = { message, stack: error?.stack };
2961
3150
  recordTraceEvent(traceSession, {
2962
3151
  type: "tool_call",
2963
3152
  label: `Tool Error - ${toolName}`,
@@ -2965,27 +3154,20 @@ function createToolsNode(initialTools, opts) {
2965
3154
  status: "error",
2966
3155
  durationMs,
2967
3156
  requestBytes: inputBytes,
3157
+ responseBytes: traceSession?.resolvedConfig.logData ? estimatePayloadBytes(errorOutput) : void 0,
3158
+ toolDetails,
2968
3159
  toolExecutionId: executionId,
2969
3160
  error: { message, stack: error?.stack },
2970
- messageList: [
2971
- {
2972
- role: "assistant",
2973
- name: toolName,
2974
- content: "",
2975
- tool_calls: [
2976
- {
2977
- id: tc.id || executionId,
2978
- type: "function",
2979
- function: { name: toolName, arguments: sanitizedArgs }
2980
- }
2981
- ]
2982
- },
2983
- {
2984
- role: "tool",
2985
- name: toolName,
2986
- content: `Error executing tool: ${message}`
2987
- }
2988
- ]
3161
+ sections: buildToolTraceSections({
3162
+ args: sanitizedArgs,
3163
+ durationMs,
3164
+ executionId,
3165
+ output: errorOutput,
3166
+ status: "error",
3167
+ summary: `Error executing tool: ${message}`,
3168
+ toolDetails,
3169
+ toolName
3170
+ })
2989
3171
  });
2990
3172
  toolCount += 1;
2991
3173
  return { status: "error", approval: approvalEntry };
@@ -3012,15 +3194,19 @@ function createToolsNode(initialTools, opts) {
3012
3194
  return false;
3013
3195
  };
3014
3196
  for (const idx of approvalIndices) {
3015
- if (awaitingApproval || shouldStop) break;
3197
+ if (awaitingApproval || awaitingUserQuestion || shouldStop) break;
3016
3198
  if (checkCancellationStop()) break;
3017
3199
  const result = await runOne(planned[idx], orderedSlots[idx]);
3018
3200
  if (result.status === "awaiting_approval") {
3019
3201
  awaitingApproval = true;
3020
3202
  break;
3021
3203
  }
3204
+ if (result.status === "awaiting_user_question") {
3205
+ awaitingUserQuestion = true;
3206
+ break;
3207
+ }
3022
3208
  }
3023
- if (!awaitingApproval && !shouldStop && parallelIndices.length > 0) {
3209
+ if (!awaitingApproval && !awaitingUserQuestion && !shouldStop && parallelIndices.length > 0) {
3024
3210
  const concurrency = Math.max(1, Math.min(limits.maxParallelTools, parallelIndices.length));
3025
3211
  let cursor = 0;
3026
3212
  const workerError = {};
@@ -3028,7 +3214,7 @@ function createToolsNode(initialTools, opts) {
3028
3214
  for (let w = 0; w < concurrency; w++) {
3029
3215
  workers.push((async () => {
3030
3216
  while (true) {
3031
- if (workerError.err || shouldStop || awaitingApproval) return;
3217
+ if (workerError.err || shouldStop || awaitingApproval || awaitingUserQuestion) return;
3032
3218
  const i = cursor++;
3033
3219
  if (i >= parallelIndices.length) return;
3034
3220
  if (checkCancellationStop()) return;
@@ -3039,6 +3225,10 @@ function createToolsNode(initialTools, opts) {
3039
3225
  awaitingApproval = true;
3040
3226
  return;
3041
3227
  }
3228
+ if (result.status === "awaiting_user_question") {
3229
+ awaitingUserQuestion = true;
3230
+ return;
3231
+ }
3042
3232
  } catch (err) {
3043
3233
  workerError.err = err;
3044
3234
  return;
@@ -3054,29 +3244,26 @@ function createToolsNode(initialTools, opts) {
3054
3244
  }
3055
3245
  for (const tc of skipped) {
3056
3246
  onEvent?.({ type: "tool_call", phase: "skipped", name: tc.name, id: tc.id, args: tc.args });
3057
- const sanitizedArgs = sanitizeTracePayload(tc.args);
3247
+ const toolDetails = buildToolDetails(toolByName.get(tc.name), tc.name);
3248
+ const skipMessage = `Skipped tool due to tool-call limit: ${tc.name}`;
3058
3249
  recordTraceEvent(traceSession, {
3059
3250
  type: "tool_call",
3060
3251
  label: `Tool Skipped - ${tc.name}`,
3061
3252
  actor: { scope: "tool", name: tc.name, role: "tool" },
3062
3253
  status: "skipped",
3254
+ toolDetails,
3063
3255
  toolExecutionId: tc.id,
3064
- messageList: [
3065
- {
3066
- role: "assistant",
3067
- name: tc.name,
3068
- content: `Skipped tool due to tool-call limit: ${tc.name}`,
3069
- tool_calls: [
3070
- {
3071
- id: tc.id,
3072
- type: "function",
3073
- function: { name: tc.name, arguments: sanitizedArgs }
3074
- }
3075
- ]
3076
- }
3077
- ]
3256
+ sections: buildToolTraceSections({
3257
+ args: tc.args,
3258
+ executionId: tc.id,
3259
+ output: skipMessage,
3260
+ status: "skipped",
3261
+ summary: skipMessage,
3262
+ toolDetails,
3263
+ toolName: tc.name
3264
+ })
3078
3265
  });
3079
- appended.push({ role: "tool", content: `Skipped tool due to tool-call limit: ${tc.name}`, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
3266
+ appended.push({ role: "tool", content: skipMessage, tool_call_id: tc.id || `${tc.name}_${appended.length}`, name: tc.name });
3080
3267
  toolCount += 1;
3081
3268
  }
3082
3269
  if (!awaitingApproval && state.ctx?.__awaitingApproval) {
@@ -3101,11 +3288,12 @@ function createToolsNode(initialTools, opts) {
3101
3288
  toolCache: state.toolCache,
3102
3289
  // Explicitly propagate ctx so callers using `{ ...state, ...delta }`
3103
3290
  // do not silently capture a stale ctx reference. Mutations to
3104
- // __summarizationExhausted / __awaitingApproval / __structuredOutputParsed
3105
- // are made on this object during execution.
3291
+ // __summarizationExhausted / __awaitingApproval / __awaitingUserQuestion /
3292
+ // __structuredOutputParsed are made on this object during execution.
3106
3293
  ctx: state.ctx,
3107
3294
  agent: state.agent,
3108
- pendingApprovals
3295
+ pendingApprovals,
3296
+ pendingUserQuestions
3109
3297
  };
3110
3298
  };
3111
3299
  }
@@ -3571,6 +3759,226 @@ function resolveToolApprovalState(state, resolution) {
3571
3759
  };
3572
3760
  }
3573
3761
 
3762
+ // src/utils/userQuestions.ts
3763
+ var cloneQuestions = (questions) => Array.isArray(questions) ? questions.map((entry) => ({ ...entry })) : [];
3764
+ function validateAnswers(entry, answers, cancelled) {
3765
+ if (cancelled) {
3766
+ return answers || {};
3767
+ }
3768
+ const allowFreeText = entry.allowFreeText !== false;
3769
+ const out = {};
3770
+ const provided = answers || {};
3771
+ for (const question of entry.questions) {
3772
+ const required = question.required !== false;
3773
+ const key = question.question;
3774
+ const answer = provided[key] ?? provided[question.header || ""];
3775
+ if (!answer) {
3776
+ if (required) {
3777
+ throw new Error(`Missing answer for required question: "${key}".`);
3778
+ }
3779
+ continue;
3780
+ }
3781
+ const values = Array.isArray(answer.values) ? answer.values.filter((v) => typeof v === "string") : [];
3782
+ const freeText = typeof answer.freeText === "string" ? answer.freeText.trim() : "";
3783
+ const notes = typeof answer.notes === "string" ? answer.notes : void 0;
3784
+ if (freeText && !allowFreeText) {
3785
+ throw new Error(
3786
+ `Free-text answers are disabled for this agent (question: "${key}"). Pick one of the provided options.`
3787
+ );
3788
+ }
3789
+ if (values.length === 0 && !freeText) {
3790
+ if (required) {
3791
+ throw new Error(`Question "${key}" requires either a selected option or free-text answer.`);
3792
+ }
3793
+ continue;
3794
+ }
3795
+ if (values.length > 1 && !question.multiSelect) {
3796
+ throw new Error(`Question "${key}" is single-select but received multiple values.`);
3797
+ }
3798
+ if (Array.isArray(question.options) && question.options.length > 0 && values.length > 0) {
3799
+ const allowedValues = new Set(
3800
+ question.options.map((option) => option.value ?? option.label)
3801
+ );
3802
+ for (const value of values) {
3803
+ if (!allowedValues.has(value)) {
3804
+ if (!allowFreeText) {
3805
+ throw new Error(
3806
+ `Value "${value}" is not one of the allowed options for question "${key}".`
3807
+ );
3808
+ }
3809
+ }
3810
+ }
3811
+ }
3812
+ out[key] = { values, ...freeText ? { freeText } : {}, ...notes ? { notes } : {} };
3813
+ }
3814
+ return out;
3815
+ }
3816
+ function buildToolResponseContent(entry, answers, cancelled, notes) {
3817
+ if (cancelled) {
3818
+ return JSON.stringify({
3819
+ status: "cancelled",
3820
+ message: notes || "User dismissed the question without answering."
3821
+ });
3822
+ }
3823
+ const payload = {
3824
+ status: "answered",
3825
+ answers: entry.questions.map((question) => {
3826
+ const key = question.question;
3827
+ const ans = answers[key];
3828
+ return {
3829
+ question: key,
3830
+ header: question.header,
3831
+ values: ans?.values ?? [],
3832
+ freeText: ans?.freeText,
3833
+ notes: ans?.notes
3834
+ };
3835
+ }),
3836
+ ...notes ? { reviewerNotes: notes } : {}
3837
+ };
3838
+ return JSON.stringify(payload);
3839
+ }
3840
+ function resolveUserQuestionState(state, resolution) {
3841
+ const pending = cloneQuestions(state.pendingUserQuestions);
3842
+ const matchIndex = pending.findIndex(
3843
+ (entry2) => entry2.id === resolution.id || entry2.toolCallId === resolution.id
3844
+ );
3845
+ if (matchIndex === -1) {
3846
+ throw new Error(`Pending user question not found for id: ${resolution.id}`);
3847
+ }
3848
+ const entry = { ...pending[matchIndex] };
3849
+ if (entry.status === "answered" || entry.status === "executed" || entry.status === "cancelled") {
3850
+ throw new Error(`User question ${entry.id} already resolved (status: ${entry.status}).`);
3851
+ }
3852
+ const cancelled = resolution.cancelled === true;
3853
+ const validated = validateAnswers(entry, resolution.answers, cancelled);
3854
+ entry.status = cancelled ? "cancelled" : "answered";
3855
+ entry.answers = validated;
3856
+ entry.cancelled = cancelled || void 0;
3857
+ entry.answeredBy = resolution.answeredBy;
3858
+ entry.notes = resolution.notes;
3859
+ entry.answeredAt = (/* @__PURE__ */ new Date()).toISOString();
3860
+ pending[matchIndex] = entry;
3861
+ const ctx = { ...state.ctx || {} };
3862
+ delete ctx.__awaitingUserQuestion;
3863
+ if (ctx.__resumeStage === "tools") {
3864
+ delete ctx.__resumeStage;
3865
+ }
3866
+ ctx.__userQuestionResolved = { id: entry.id, status: entry.status };
3867
+ const toolMessage = {
3868
+ role: "tool",
3869
+ name: "ask_user_question",
3870
+ tool_call_id: entry.toolCallId,
3871
+ content: buildToolResponseContent(entry, validated, cancelled, resolution.notes)
3872
+ };
3873
+ return {
3874
+ ...state,
3875
+ pendingUserQuestions: pending,
3876
+ messages: [...state.messages || [], toolMessage],
3877
+ ctx
3878
+ };
3879
+ }
3880
+ 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.";
3881
+ 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.";
3882
+ function buildOptionSchema() {
3883
+ return z.object({
3884
+ label: z.string().min(1).describe("Display text the user sees"),
3885
+ value: z.string().min(1).optional().describe("Value returned in the answer; defaults to label"),
3886
+ description: z.string().optional(),
3887
+ preview: z.string().optional().describe("Optional code / ASCII preview rendered when this option is focused")
3888
+ }).strict();
3889
+ }
3890
+ function buildQuestionSchema(allowFreeText) {
3891
+ const optionSchema = buildOptionSchema();
3892
+ const baseShape = {
3893
+ question: z.string().min(1).describe("Full question shown to the user"),
3894
+ header: z.string().max(40).optional().describe("Short chip / tag rendered next to the question (max ~12 chars recommended)"),
3895
+ multiSelect: z.boolean().optional().describe("Allow multiple selections. Default: false"),
3896
+ placeholder: z.string().optional(),
3897
+ required: z.boolean().optional().describe("Default: true")
3898
+ };
3899
+ if (allowFreeText) {
3900
+ return z.object({
3901
+ ...baseShape,
3902
+ options: z.array(optionSchema).min(2).max(12).optional()
3903
+ }).strict();
3904
+ }
3905
+ return z.object({
3906
+ ...baseShape,
3907
+ options: z.array(optionSchema).min(2).max(12).describe("Required: free-text answers are disabled for this agent")
3908
+ }).strict();
3909
+ }
3910
+ function createAskUserQuestionTool(stateRef, config) {
3911
+ const allowFreeText = config.allowFreeText !== false;
3912
+ const description = config.promptOverride ?? (allowFreeText ? DEFAULT_DESCRIPTION_WITH_FREETEXT : DEFAULT_DESCRIPTION_NO_FREETEXT);
3913
+ const questionSchema = buildQuestionSchema(allowFreeText);
3914
+ const askUser = createTool({
3915
+ name: "ask_user_question",
3916
+ description,
3917
+ schema: z.object({
3918
+ questions: z.array(questionSchema).min(1).max(4).describe("1-4 related questions to ask in a single pause.")
3919
+ }).strict(),
3920
+ func: async ({ questions }) => {
3921
+ const ref = askUser._stateRef || (askUser._stateRef = {});
3922
+ const onEvent = ref.__onEvent;
3923
+ if (!ref.ctx) ref.ctx = {};
3924
+ const ctx = ref.ctx;
3925
+ const toolCallId = ref.__currentToolCallId || `ask_user_${nanoid(6)}`;
3926
+ if (!ref.pendingUserQuestions) ref.pendingUserQuestions = [];
3927
+ const pendingList = ref.pendingUserQuestions;
3928
+ const entry = {
3929
+ id: nanoid(),
3930
+ toolCallId,
3931
+ toolName: "ask_user_question",
3932
+ questions,
3933
+ status: "pending",
3934
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
3935
+ allowFreeText
3936
+ };
3937
+ pendingList.push(entry);
3938
+ ctx.__awaitingUserQuestion = {
3939
+ id: entry.id,
3940
+ toolCallId: entry.toolCallId,
3941
+ requestedAt: entry.requestedAt
3942
+ };
3943
+ ctx.__resumeStage = "tools";
3944
+ onEvent?.({
3945
+ type: "user_question",
3946
+ status: "pending",
3947
+ id: entry.id,
3948
+ toolCallId: entry.toolCallId,
3949
+ questions: entry.questions,
3950
+ allowFreeText
3951
+ });
3952
+ recordTraceEvent(ctx.__traceSession, {
3953
+ type: "user_question",
3954
+ label: "Ask User",
3955
+ actor: { scope: "agent", name: "ask_user_question", role: "user_interaction" },
3956
+ status: "success",
3957
+ sections: [
3958
+ {
3959
+ kind: "metadata",
3960
+ label: "Pending Question",
3961
+ data: {
3962
+ id: entry.id,
3963
+ toolCallId: entry.toolCallId,
3964
+ questions: entry.questions,
3965
+ allowFreeText
3966
+ }
3967
+ }
3968
+ ]
3969
+ });
3970
+ return {
3971
+ __awaitingUserQuestion: true,
3972
+ id: entry.id,
3973
+ toolCallId: entry.toolCallId
3974
+ };
3975
+ }
3976
+ });
3977
+ askUser._stateRef = stateRef;
3978
+ askUser.__source = "human-in-the-loop";
3979
+ return askUser;
3980
+ }
3981
+
3574
3982
  // src/agent.ts
3575
3983
  init_utilTokens();
3576
3984
 
@@ -3782,6 +4190,21 @@ function createAgent(opts) {
3782
4190
  const resolver = createResolverNode();
3783
4191
  const agentCore = createAgentCoreNode(opts);
3784
4192
  const toolsBase = [...opts.tools ?? []];
4193
+ const askUserConfig = (() => {
4194
+ const raw = opts.humanInTheLoop?.askUser;
4195
+ if (!raw) return void 0;
4196
+ if (raw === true) return { enabled: true, allowFreeText: true };
4197
+ return {
4198
+ enabled: true,
4199
+ allowFreeText: raw.allowFreeText !== false,
4200
+ promptOverride: raw.promptOverride
4201
+ };
4202
+ })();
4203
+ const askUserOnQuestion = typeof opts.humanInTheLoop?.askUser === "object" ? opts.humanInTheLoop?.askUser?.onQuestion : void 0;
4204
+ const askUserStateRef = askUserConfig ? { pendingUserQuestions: void 0, ctx: void 0, __onEvent: void 0, __currentToolCallId: void 0 } : void 0;
4205
+ if (askUserConfig && askUserStateRef) {
4206
+ toolsBase.push(createAskUserQuestionTool(askUserStateRef, askUserConfig));
4207
+ }
3785
4208
  const soManager = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
3786
4209
  if (soManager) {
3787
4210
  const modelCapabilities = getModelCapabilities(opts.model);
@@ -3826,7 +4249,8 @@ function createAgent(opts) {
3826
4249
  useTodoList: void 0,
3827
4250
  outputSchema: opts.outputSchema,
3828
4251
  responseFormat: soManager?.getResponseFormat(),
3829
- tracing: opts.tracing
4252
+ tracing: opts.tracing,
4253
+ humanInTheLoop: askUserConfig ? { askUser: askUserConfig } : void 0
3830
4254
  };
3831
4255
  const summarizationThreshold = getActiveSummarizationThreshold(opts);
3832
4256
  async function runLoop(initial, config, emit) {
@@ -4090,7 +4514,7 @@ function createAgent(opts) {
4090
4514
  if (toolCalls.length === 0) {
4091
4515
  if (soManager && !state.ctx?.__finalizedDueToStructuredOutput && !state.ctx?.__structuredOutputForceFinalize) {
4092
4516
  if (soManager.strategy.kind === "native") {
4093
- const assistantText = extractMessageText2(lastMsg);
4517
+ const assistantText = extractMessageText(lastMsg);
4094
4518
  if (assistantText) {
4095
4519
  const parsed = soManager.parseFromContent(assistantText);
4096
4520
  if (parsed.success) {
@@ -4116,6 +4540,7 @@ function createAgent(opts) {
4116
4540
  state = { ...state, ...await toolsNode(state) };
4117
4541
  onProgress?.({ stage: "tools", message: "Tools finished" });
4118
4542
  if (state.ctx?.__awaitingApproval) break;
4543
+ if (state.ctx?.__awaitingUserQuestion) break;
4119
4544
  if (checkpointIfRequested("after_tools")) break;
4120
4545
  if (state.ctx?.__finalizedDueToStructuredOutput) break;
4121
4546
  if (reflectNode && resolvedReasoning?.reflection.enabled) {
@@ -4140,7 +4565,7 @@ function createAgent(opts) {
4140
4565
  if (soManager && !state.ctx?.__finalizedDueToStructuredOutput) {
4141
4566
  const lastForParse = state.messages[state.messages.length - 1];
4142
4567
  if (lastForParse?.role === "assistant") {
4143
- const assistantText = extractMessageText2(lastForParse);
4568
+ const assistantText = extractMessageText(lastForParse);
4144
4569
  if (assistantText) {
4145
4570
  const parsed = soManager.parseFromContent(assistantText);
4146
4571
  if (parsed.success) {
@@ -4186,7 +4611,7 @@ function createAgent(opts) {
4186
4611
  }
4187
4612
  const lastAfterLoop = state.messages[state.messages.length - 1];
4188
4613
  if (lastAfterLoop?.role === "tool" && !pausedStage) {
4189
- const isExpectedExit = state.ctx?.__awaitingApproval || state.ctx?.__cancelled || state.ctx?.__finalizedDueToStructuredOutput || state.ctx?.__finalizedDueToToolLimit || state.ctx?.__needsSummarization || state.ctx?.__limitBreached;
4614
+ const isExpectedExit = state.ctx?.__awaitingApproval || state.ctx?.__awaitingUserQuestion || state.ctx?.__cancelled || state.ctx?.__finalizedDueToStructuredOutput || state.ctx?.__finalizedDueToToolLimit || state.ctx?.__needsSummarization || state.ctx?.__limitBreached;
4190
4615
  if (!isExpectedExit) {
4191
4616
  throw new Error(
4192
4617
  "Agent loop terminated with a pending tool response but no subsequent model invocation. This usually indicates a model provider error or exhausted iteration budget."
@@ -4204,7 +4629,17 @@ function createAgent(opts) {
4204
4629
  const { setTokenCounter: setTokenCounter2 } = await Promise.resolve().then(() => (init_utilTokens(), utilTokens_exports));
4205
4630
  setTokenCounter2(userTokenCounter);
4206
4631
  }
4207
- const onEvent = config?.onEvent;
4632
+ const callerOnEvent = config?.onEvent;
4633
+ const onEvent = askUserOnQuestion ? (event) => {
4634
+ if (event && event.type === "user_question") {
4635
+ try {
4636
+ askUserOnQuestion(event);
4637
+ } catch (err) {
4638
+ console.warn("[agent-sdk] humanInTheLoop.askUser.onQuestion error:", err);
4639
+ }
4640
+ }
4641
+ callerOnEvent?.(event);
4642
+ } : callerOnEvent;
4208
4643
  const onProgress = config?.onProgress;
4209
4644
  const onStream = config?.onStream;
4210
4645
  const streamEnabled = config?.stream === true;
@@ -4248,6 +4683,7 @@ function createAgent(opts) {
4248
4683
  metadata: input.metadata,
4249
4684
  ctx,
4250
4685
  pendingApprovals: input.pendingApprovals || [],
4686
+ pendingUserQuestions: input.pendingUserQuestions || [],
4251
4687
  agent: input.agent || runtimeWithInvokeLimits,
4252
4688
  usage: input.usage || { perRequest: [], totals: {} }
4253
4689
  };
@@ -4282,7 +4718,7 @@ function createAgent(opts) {
4282
4718
  const isTransientSummarizationExit = !!res.ctx?.__needsSummarization;
4283
4719
  const finalAssistantMsg = isTransientSummarizationExit ? void 0 : getLastAssistantMessage(res.messages);
4284
4720
  const finalIsSyntheticSummary = !isTransientSummarizationExit && finalAssistantMsg && isSyntheticSummaryMessage(finalAssistantMsg);
4285
- const content = isTransientSummarizationExit || finalIsSyntheticSummary ? "" : extractMessageText2(finalAssistantMsg);
4721
+ const content = isTransientSummarizationExit || finalIsSyntheticSummary ? "" : extractMessageText(finalAssistantMsg);
4286
4722
  let parsed = void 0;
4287
4723
  let outputError = void 0;
4288
4724
  if (soManager) {
@@ -4324,11 +4760,13 @@ function createAgent(opts) {
4324
4760
  return invokeAgent(restoredState, config);
4325
4761
  };
4326
4762
  const resolveToolApproval = (state, resolution) => resolveToolApprovalState(state, resolution);
4763
+ const resolveUserQuestion = (state, resolution) => resolveUserQuestionState(state, resolution);
4327
4764
  const instance = {
4328
4765
  invoke: invokeAgent,
4329
4766
  snapshot: snapshotState,
4330
4767
  resume: resumeAgent,
4331
4768
  resolveToolApproval,
4769
+ resolveUserQuestion,
4332
4770
  asTool: ({ toolName, description, inputDescription }) => {
4333
4771
  const schema = z.object({ input: z.string().describe(inputDescription || "Input for delegated agent") });
4334
4772
  const delegatedTool = createTool({
@@ -4588,7 +5026,7 @@ function renderMemoryBlock(facts) {
4588
5026
  function clampToBudget(messages, maxContextTokens) {
4589
5027
  let working = [...messages];
4590
5028
  while (working.length > 2) {
4591
- const tokenCount = countApproxTokens(working.map(extractMessageText2).join("\n"));
5029
+ const tokenCount = countApproxTokens(working.map(extractMessageText).join("\n"));
4592
5030
  if (tokenCount <= maxContextTokens) return working;
4593
5031
  const firstNonSystem = working.findIndex((message, index) => !(index === 0 && message.role === "system"));
4594
5032
  if (firstNonSystem < 0) break;
@@ -5645,7 +6083,20 @@ function createSmartAgent(opts) {
5645
6083
  const userTools = opts.tools ?? [];
5646
6084
  const factoryStateRef = { toolHistory: void 0, toolHistoryArchived: void 0, todoList: void 0, planVersion: 0, adherenceScore: 0 };
5647
6085
  const factoryContextTools = createContextTools(factoryStateRef, { planningEnabled, includeGetToolResponse: false });
6086
+ const askUserConfig = (() => {
6087
+ const raw = opts.humanInTheLoop?.askUser;
6088
+ if (!raw) return void 0;
6089
+ if (raw === true) return { enabled: true, allowFreeText: true };
6090
+ return {
6091
+ enabled: true,
6092
+ allowFreeText: raw.allowFreeText !== false,
6093
+ promptOverride: raw.promptOverride
6094
+ };
6095
+ })();
5648
6096
  const factoryToolsWithoutRecovery = [...userTools, ...factoryContextTools];
6097
+ if (askUserConfig) {
6098
+ factoryToolsWithoutRecovery.push(createAskUserQuestionTool(factoryStateRef, askUserConfig));
6099
+ }
5649
6100
  const soManagerFactory = opts.outputSchema ? new StructuredOutputManager(opts.outputSchema, resolveStrategy(opts.model)) : void 0;
5650
6101
  const modelCapabilities = opts.outputSchema ? getModelCapabilities(opts.model) : void 0;
5651
6102
  const responseFinalizeTool = soManagerFactory && modelCapabilities?.structuredOutput !== "native" ? soManagerFactory.getResponseTool() : void 0;
@@ -5655,6 +6106,7 @@ function createSmartAgent(opts) {
5655
6106
  const recoveryTool = createGetToolResponseTool(ref);
5656
6107
  const base2 = [...userTools, ...ctxTools];
5657
6108
  if (responseFinalizeTool) base2.push(responseFinalizeTool);
6109
+ if (askUserConfig) base2.push(createAskUserQuestionTool(ref, askUserConfig));
5658
6110
  const without = base2;
5659
6111
  const withRec = [...without, recoveryTool];
5660
6112
  return { stateRef: ref, toolsWithoutRecovery: without, toolsWithRecovery: withRec };
@@ -5677,7 +6129,7 @@ function createSmartAgent(opts) {
5677
6129
  ].join("\n");
5678
6130
  function latestUserPrompt(messages) {
5679
6131
  const latest = [...messages].reverse().find((message) => message.role === "user");
5680
- return latest ? extractMessageText2(latest) : "";
6132
+ return latest ? extractMessageText(latest) : "";
5681
6133
  }
5682
6134
  async function syncMemory(state) {
5683
6135
  const query = latestUserPrompt(state.messages || []);
@@ -5839,6 +6291,7 @@ function createSmartAgent(opts) {
5839
6291
  snapshot: base.snapshot,
5840
6292
  resume: base.resume,
5841
6293
  resolveToolApproval: base.resolveToolApproval,
6294
+ resolveUserQuestion: base.resolveUserQuestion,
5842
6295
  asTool: base.asTool,
5843
6296
  asHandoff: base.asHandoff,
5844
6297
  __runtime: base.__runtime
@@ -8141,6 +8594,6 @@ function createProvider(config) {
8141
8594
  }
8142
8595
  }
8143
8596
 
8144
- export { AnthropicProvider, AzureProvider, BaseProvider, BedrockProvider, DEFAULT_PROFILE_CONFIGS, GuardrailPhase, InMemoryMemoryStore, NativeJsonSchemaStrategy, OpenAICompatibleProvider, OpenAIProvider, ProviderError, StructuredOutputManager, ToolBasedStrategy, VertexProvider, agentVerdictRule, buildSystemPrompt, captureSnapshot, codePresenceRule, cognipeerSink, countApproxTokens, countMessagesTokens, createAgent, createAgentNode, createCodeGuardrail, createContextSummarizeNode, createContextTools, createGetToolResponseTool, createGuardrail, createJsonGuardrail, createManageTodoTool, createProvider, createRegexGuardrail, createResolverNode, createSmartAgent, createTool, createToolLimitFinalizeNode, createToolsNode, customCallbackRule, customSink, defaultTokenCounter, emptyUsage, evaluateGuardrails, fileSink, formatZodError, fromLangchainModel, fromLangchainTools, fromNativeProvider, generateSpanId, generateTraceId, getModelCapabilities, getNestedValue, getResolvedSmartConfig, getTokenCounter, hasToolResponseRecoveryReference, httpSink, isSmartModel, jsonSchemaRule, normalizeSmartAgentOptions, otlpSink, readMemoryFacts, regexRule, resolveMemoryStore, resolveStrategy, resolveToolApprovalState, restoreSnapshot, runSmartAgentEvalHarness, setTokenCounter, startStreamingSession, traceSessionToOtlp, withTools, writeSummaryFactsToMemory };
8597
+ 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 };
8145
8598
  //# sourceMappingURL=index.mjs.map
8146
8599
  //# sourceMappingURL=index.mjs.map