@lleverage-ai/agent-sdk 0.0.11 → 0.0.13

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 CHANGED
@@ -65,7 +65,7 @@ const safeAgent = createAgent({
65
65
  });
66
66
  ```
67
67
 
68
- **Core tools included:** `read`, `write`, `edit`, `glob`, `grep`, `todo_write`, `bash` (requires backend with `enableBash: true`), `search_tools` (when enabled)
68
+ **Core tools included:** `read`, `write`, `edit`, `glob`, `grep`, `todo_write`, `bash` (requires backend with `enableBash: true`), `skill` (when skills are configured), `search_tools` (when enabled), `call_tool` (proxy mode only)
69
69
 
70
70
  ### Tools
71
71
 
@@ -164,6 +164,8 @@ See [Skills Documentation](./docs/skills.md) for complete details on the skills
164
164
 
165
165
  Create dynamic, context-aware system prompts from composable components. Instead of static strings, prompts automatically include tools, skills, backend capabilities, and more.
166
166
 
167
+ The default prompt builder is cache-friendly: it avoids per-turn dynamic context sections by default so repeated generations in the same thread can reuse provider prompt caches more effectively.
168
+
167
169
  **Using the default builder:**
168
170
  ```typescript
169
171
  const agent = createAgent({
@@ -354,7 +356,7 @@ export async function POST(req: Request) {
354
356
 
355
357
  - [Prompt Builder](./docs/prompt-builder.md) — Dynamic, context-aware system prompts
356
358
  - [Skills System](./docs/skills.md) — Progressive disclosure with Agent Skills spec compliance
357
- - [Tool Loading Strategies](./docs/tool-loading.md) — Eager, lazy, and dynamic tool loading
359
+ - [Tool Loading Strategies](./docs/tool-loading.md) — Eager, deferred discovery, and proxy tool loading
358
360
  - [Security & Production](./docs/security.md) — Security policies, guardrails, and secrets filtering
359
361
  - [Subagents](./docs/subagents.md) — Task delegation and background tasks
360
362
  - [MCP Integration](./docs/mcp.md) — Model Context Protocol tools and servers
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA4DH,OAAO,KAAK,EACV,KAAK,EACL,YAAY,EAwBb,MAAM,YAAY,CAAC;AA6pBpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,KAAK,CAoyFxD"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA4DH,OAAO,KAAK,EACV,KAAK,EACL,YAAY,EAwBb,MAAM,YAAY,CAAC;AA6pBpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,KAAK,CAw3FxD"}
package/dist/agent.js CHANGED
@@ -791,7 +791,8 @@ export function createAgent(options) {
791
791
  const hasSubagents = allSubagents.length > 0;
792
792
  // In proxy mode, create call_tool and configure search_tools with schema disclosure
793
793
  const isProxyMode = pluginLoadingMode === "proxy" || hasProxiedTools;
794
- if (isProxyMode && !options.disabledCoreTools?.includes("call_tool")) {
794
+ const hasProxyTargets = hasProxiedTools || mcpManager.hasExternalServers();
795
+ if (isProxyMode && hasProxyTargets && !options.disabledCoreTools?.includes("call_tool")) {
795
796
  coreTools.call_tool = createCallToolTool({
796
797
  mcpManager,
797
798
  });
@@ -805,8 +806,8 @@ export function createAgent(options) {
805
806
  // - Always auto-load tools when found (no manual load step) — unless proxy mode
806
807
  const shouldCreateSearchToolsForAutoThreshold = toolSearchEnabled === "auto" && totalPluginToolCount > toolSearchThreshold;
807
808
  const shouldCreateSearchTools = !options.disabledCoreTools?.includes("search_tools") &&
808
- (deferredLoadingActive ||
809
- isProxyMode ||
809
+ ((deferredLoadingActive && (totalPluginToolCount > 0 || mcpManager.hasExternalServers())) ||
810
+ (isProxyMode && hasProxyTargets) ||
810
811
  shouldCreateSearchToolsForAutoThreshold ||
811
812
  (mcpManager.hasExternalServers() && toolSearchEnabled !== "never"));
812
813
  if (shouldCreateSearchTools) {
@@ -919,6 +920,28 @@ export function createAgent(options) {
919
920
  // Build prompt using prompt builder
920
921
  return promptBuilder.build(context);
921
922
  };
923
+ const responseMessagesToModelMessages = (messages) => Array.isArray(messages) ? messages : [];
924
+ const appendResponseMessages = (baseMessages, responseMessages, fallbackAssistantText) => {
925
+ const normalized = responseMessagesToModelMessages(responseMessages);
926
+ if (normalized.length > 0) {
927
+ return [...baseMessages, ...normalized];
928
+ }
929
+ return fallbackAssistantText
930
+ ? [...baseMessages, { role: "assistant", content: fallbackAssistantText }]
931
+ : baseMessages;
932
+ };
933
+ const buildMessagesFromStepResponses = (baseMessages, steps, fallbackAssistantText) => {
934
+ const stepResponseMessages = steps.flatMap((step) => responseMessagesToModelMessages(step.response?.messages));
935
+ if (stepResponseMessages.length > 0) {
936
+ return [...baseMessages, ...stepResponseMessages];
937
+ }
938
+ // Fallback for providers that do not populate response.messages.
939
+ const lastText = steps.at(-1)?.text ?? fallbackAssistantText;
940
+ return [
941
+ ...baseMessages,
942
+ ...(lastText ? [{ role: "assistant", content: lastText }] : []),
943
+ ];
944
+ };
922
945
  // Runtime tools added/removed dynamically by plugins at runtime
923
946
  const runtimeTools = {};
924
947
  // Helper to get current active tools (core + runtime + MCP + dynamically loaded from registry)
@@ -1616,10 +1639,7 @@ export function createAgent(options) {
1616
1639
  // cannot find the checkpoint (or finds one without messages/interrupt).
1617
1640
  if (effectiveGenOptions.threadId && options.checkpointer) {
1618
1641
  const checkpointThreadId = forkedSessionId ?? effectiveGenOptions.threadId;
1619
- const finalMessages = [
1620
- ...messages,
1621
- ...(response.text ? [{ role: "assistant", content: response.text }] : []),
1622
- ];
1642
+ const finalMessages = appendResponseMessages(messages, response.response?.messages, response.text);
1623
1643
  const savedCheckpoint = await saveCheckpoint(checkpointThreadId, finalMessages, startStep + response.steps.length);
1624
1644
  if (savedCheckpoint) {
1625
1645
  const withInterrupt = updateCheckpoint(savedCheckpoint, {
@@ -1687,13 +1707,7 @@ export function createAgent(options) {
1687
1707
  // Save checkpoint - use forked session ID if forking, otherwise use original threadId
1688
1708
  const checkpointThreadId = forkedSessionId ?? effectiveGenOptions.threadId;
1689
1709
  if (checkpointThreadId && options.checkpointer) {
1690
- // Build final messages including the assistant response.
1691
- // Skip empty text to avoid invalid content blocks (e.g. when
1692
- // the model finishes with only tool calls).
1693
- const finalMessages = [
1694
- ...messages,
1695
- ...(response.text ? [{ role: "assistant", content: response.text }] : []),
1696
- ];
1710
+ const finalMessages = appendResponseMessages(messages, response.response?.messages, response.text);
1697
1711
  await saveCheckpoint(checkpointThreadId, finalMessages, startStep + response.steps.length);
1698
1712
  }
1699
1713
  // Invoke unified PostGenerate hooks
@@ -1727,12 +1741,7 @@ export function createAgent(options) {
1727
1741
  let lastResult = finalResult;
1728
1742
  let runningMessages = hasCheckpointing
1729
1743
  ? []
1730
- : [
1731
- ...messages,
1732
- ...(finalResult.text
1733
- ? [{ role: "assistant", content: finalResult.text }]
1734
- : []),
1735
- ];
1744
+ : appendResponseMessages(messages, response.response?.messages, finalResult.text);
1736
1745
  let followUpPrompt = await getNextTaskPrompt();
1737
1746
  while (followUpPrompt !== null) {
1738
1747
  lastResult = await agent.generate({
@@ -1981,6 +1990,31 @@ export function createAgent(options) {
1981
1990
  if (part.type === "text-delta") {
1982
1991
  yield { type: "text-delta", text: part.text };
1983
1992
  }
1993
+ else if (part.type === "reasoning-start") {
1994
+ yield {
1995
+ type: "reasoning-start",
1996
+ id: typeof part.id === "string" ? part.id : undefined,
1997
+ };
1998
+ }
1999
+ else if (part.type === "reasoning-delta") {
2000
+ // Normalize across SDK/provider variants (`text` vs `delta`).
2001
+ const rawPart = part;
2002
+ yield {
2003
+ type: "reasoning-delta",
2004
+ id: typeof rawPart.id === "string" ? rawPart.id : undefined,
2005
+ text: typeof rawPart.text === "string"
2006
+ ? rawPart.text
2007
+ : typeof rawPart.delta === "string"
2008
+ ? rawPart.delta
2009
+ : "",
2010
+ };
2011
+ }
2012
+ else if (part.type === "reasoning-end") {
2013
+ yield {
2014
+ type: "reasoning-end",
2015
+ id: typeof part.id === "string" ? part.id : undefined,
2016
+ };
2017
+ }
1984
2018
  else if (part.type === "tool-call") {
1985
2019
  yield {
1986
2020
  type: "tool-call",
@@ -2009,11 +2043,12 @@ export function createAgent(options) {
2009
2043
  }
2010
2044
  }
2011
2045
  // Get final result for hooks - need to await all properties
2012
- const [text, usage, finishReason, steps] = await Promise.all([
2046
+ const [text, usage, finishReason, steps, responseMeta] = await Promise.all([
2013
2047
  response.text,
2014
2048
  response.usage,
2015
2049
  response.finishReason,
2016
2050
  response.steps,
2051
+ response.response ?? Promise.resolve(undefined),
2017
2052
  ]);
2018
2053
  // Only access output if an output schema was provided
2019
2054
  let output;
@@ -2035,10 +2070,10 @@ export function createAgent(options) {
2035
2070
  };
2036
2071
  // Save checkpoint if threadId is provided
2037
2072
  if (checkpointThreadId && options.checkpointer) {
2038
- const finalMessages = [
2039
- ...messages,
2040
- ...(text ? [{ role: "assistant", content: text }] : []),
2041
- ];
2073
+ const responseMessages = responseMessagesToModelMessages(responseMeta?.messages);
2074
+ const finalMessages = responseMessages.length > 0
2075
+ ? [...messages, ...responseMessages]
2076
+ : buildMessagesFromStepResponses(messages, steps, text);
2042
2077
  await saveCheckpoint(checkpointThreadId, finalMessages, startStep + steps.length);
2043
2078
  }
2044
2079
  // Save pending interrupt to checkpoint (mirrors generate() pattern)
@@ -2086,9 +2121,12 @@ export function createAgent(options) {
2086
2121
  return;
2087
2122
  }
2088
2123
  const hasCheckpointing = !!(effectiveGenOptions.threadId && options.checkpointer);
2124
+ const initialResponseMessages = responseMessagesToModelMessages(responseMeta?.messages);
2089
2125
  let currentMessages = hasCheckpointing
2090
2126
  ? []
2091
- : [...messages, ...(text ? [{ role: "assistant", content: text }] : [])];
2127
+ : initialResponseMessages.length > 0
2128
+ ? [...messages, ...initialResponseMessages]
2129
+ : buildMessagesFromStepResponses(messages, steps, text);
2092
2130
  let followUpPrompt = await getNextTaskPrompt();
2093
2131
  while (followUpPrompt !== null) {
2094
2132
  const followUpGen = agent.stream({
@@ -2188,6 +2226,7 @@ export function createAgent(options) {
2188
2226
  const signalStopCondition = () => signalState.interrupt != null;
2189
2227
  // Track step count for incremental checkpointing
2190
2228
  let currentStepCount = 0;
2229
+ let streamedMessages = [...initialParams.messages];
2191
2230
  // Execute streamText OUTSIDE createUIMessageStream so errors propagate
2192
2231
  // to the retry loop (if streamText throws synchronously on creation,
2193
2232
  // e.g. rate limit, the catch block handles retry/fallback).
@@ -2211,14 +2250,17 @@ export function createAgent(options) {
2211
2250
  ? async (stepResult) => {
2212
2251
  if (effectiveGenOptions.threadId && options.checkpointer) {
2213
2252
  currentStepCount++;
2214
- // Build messages including this step's results
2215
- const stepMessages = [
2216
- ...initialParams.messages,
2217
- ...(stepResult.text
2218
- ? [{ role: "assistant", content: stepResult.text }]
2219
- : []),
2220
- ];
2221
- await saveCheckpoint(effectiveGenOptions.threadId, stepMessages, startStep + currentStepCount);
2253
+ const stepResponseMessages = responseMessagesToModelMessages(stepResult.response?.messages);
2254
+ streamedMessages =
2255
+ stepResponseMessages.length > 0
2256
+ ? [...streamedMessages, ...stepResponseMessages]
2257
+ : stepResult.text
2258
+ ? [
2259
+ ...streamedMessages,
2260
+ { role: "assistant", content: stepResult.text },
2261
+ ]
2262
+ : streamedMessages;
2263
+ await saveCheckpoint(effectiveGenOptions.threadId, streamedMessages, startStep + currentStepCount);
2222
2264
  }
2223
2265
  }
2224
2266
  : undefined,
@@ -2233,12 +2275,7 @@ export function createAgent(options) {
2233
2275
  });
2234
2276
  }
2235
2277
  if (effectiveGenOptions.threadId && options.checkpointer) {
2236
- const finalMessages = [
2237
- ...initialParams.messages,
2238
- ...(finishResult.text
2239
- ? [{ role: "assistant", content: finishResult.text }]
2240
- : []),
2241
- ];
2278
+ const finalMessages = buildMessagesFromStepResponses(initialParams.messages, finishResult.steps, finishResult.text);
2242
2279
  await saveCheckpoint(effectiveGenOptions.threadId, finalMessages, startStep + finishResult.steps.length);
2243
2280
  }
2244
2281
  // Invoke unified PostGenerate hooks
@@ -2269,8 +2306,9 @@ export function createAgent(options) {
2269
2306
  execute: async ({ writer }) => {
2270
2307
  // Merge initial generation into the stream
2271
2308
  writer.merge(result.toUIMessageStream());
2272
- // Wait for initial generation to complete to get final text
2273
- const text = await result.text;
2309
+ // Wait for initial generation to complete
2310
+ await result.text;
2311
+ const resultResponse = await (result.response ?? Promise.resolve(undefined));
2274
2312
  // --- Background task completion loop ---
2275
2313
  if (waitForBackgroundTasks) {
2276
2314
  // Track accumulated steps for checkpoint saves
@@ -2278,7 +2316,7 @@ export function createAgent(options) {
2278
2316
  let accumulatedStepCount = initialSteps.length;
2279
2317
  let currentMessages = [
2280
2318
  ...messages,
2281
- ...(text ? [{ role: "assistant", content: text }] : []),
2319
+ ...responseMessagesToModelMessages(resultResponse?.messages),
2282
2320
  ];
2283
2321
  let followUpPrompt = await getNextTaskPrompt();
2284
2322
  while (followUpPrompt !== null) {
@@ -2302,12 +2340,16 @@ export function createAgent(options) {
2302
2340
  });
2303
2341
  writer.merge(followUpResult.toUIMessageStream());
2304
2342
  const followUpText = await followUpResult.text;
2343
+ const followUpResponse = await (followUpResult.response ??
2344
+ Promise.resolve(undefined));
2305
2345
  currentMessages = [
2306
2346
  ...currentMessages,
2307
2347
  { role: "user", content: followUpPrompt },
2308
- ...(followUpText
2309
- ? [{ role: "assistant", content: followUpText }]
2310
- : []),
2348
+ ...(responseMessagesToModelMessages(followUpResponse?.messages).length > 0
2349
+ ? responseMessagesToModelMessages(followUpResponse?.messages)
2350
+ : followUpText
2351
+ ? [{ role: "assistant", content: followUpText }]
2352
+ : []),
2311
2353
  ];
2312
2354
  // --- Post-completion bookkeeping for follow-ups ---
2313
2355
  const followUpSteps = await followUpResult.steps;
@@ -2414,6 +2456,7 @@ export function createAgent(options) {
2414
2456
  };
2415
2457
  // Track step count for incremental checkpointing
2416
2458
  let currentStepCount = 0;
2459
+ let streamedMessages = [...initialParams.messages];
2417
2460
  // Stop condition: stop when an interrupt signal was caught, OR when
2418
2461
  // the step count reaches maxSteps.
2419
2462
  const signalStopCondition = () => signalState.interrupt != null;
@@ -2438,14 +2481,17 @@ export function createAgent(options) {
2438
2481
  ? async (stepResult) => {
2439
2482
  if (effectiveGenOptions.threadId && options.checkpointer) {
2440
2483
  currentStepCount++;
2441
- // Build messages including this step's results
2442
- const stepMessages = [
2443
- ...initialParams.messages,
2444
- ...(stepResult.text
2445
- ? [{ role: "assistant", content: stepResult.text }]
2446
- : []),
2447
- ];
2448
- await saveCheckpoint(effectiveGenOptions.threadId, stepMessages, startStep + currentStepCount);
2484
+ const stepResponseMessages = responseMessagesToModelMessages(stepResult.response?.messages);
2485
+ streamedMessages =
2486
+ stepResponseMessages.length > 0
2487
+ ? [...streamedMessages, ...stepResponseMessages]
2488
+ : stepResult.text
2489
+ ? [
2490
+ ...streamedMessages,
2491
+ { role: "assistant", content: stepResult.text },
2492
+ ]
2493
+ : streamedMessages;
2494
+ await saveCheckpoint(effectiveGenOptions.threadId, streamedMessages, startStep + currentStepCount);
2449
2495
  }
2450
2496
  }
2451
2497
  : undefined,
@@ -2460,12 +2506,7 @@ export function createAgent(options) {
2460
2506
  });
2461
2507
  }
2462
2508
  if (effectiveGenOptions.threadId && options.checkpointer) {
2463
- const finalMessages = [
2464
- ...initialParams.messages,
2465
- ...(finishResult.text
2466
- ? [{ role: "assistant", content: finishResult.text }]
2467
- : []),
2468
- ];
2509
+ const finalMessages = buildMessagesFromStepResponses(initialParams.messages, finishResult.steps, finishResult.text);
2469
2510
  await saveCheckpoint(effectiveGenOptions.threadId, finalMessages, startStep + finishResult.steps.length);
2470
2511
  }
2471
2512
  // Invoke unified PostGenerate hooks
@@ -2575,6 +2616,7 @@ export function createAgent(options) {
2575
2616
  };
2576
2617
  // Track step count for incremental checkpointing
2577
2618
  let currentStepCount = 0;
2619
+ let streamedMessages = [...initialParams.messages];
2578
2620
  // Stop condition: stop when a flow-control signal was caught, OR when
2579
2621
  // the step count reaches maxSteps.
2580
2622
  const signalStopCondition = () => signalState.interrupt != null;
@@ -2599,15 +2641,17 @@ export function createAgent(options) {
2599
2641
  ? async (stepResult) => {
2600
2642
  if (effectiveGenOptions.threadId && options.checkpointer) {
2601
2643
  currentStepCount++;
2602
- // Build messages including this step's results
2603
- const stepMessages = [
2604
- ...initialParams.messages,
2605
- {
2606
- role: "assistant",
2607
- content: stepResult.text,
2608
- },
2609
- ];
2610
- await saveCheckpoint(effectiveGenOptions.threadId, stepMessages, startStep + currentStepCount);
2644
+ const stepResponseMessages = responseMessagesToModelMessages(stepResult.response?.messages);
2645
+ streamedMessages =
2646
+ stepResponseMessages.length > 0
2647
+ ? [...streamedMessages, ...stepResponseMessages]
2648
+ : stepResult.text
2649
+ ? [
2650
+ ...streamedMessages,
2651
+ { role: "assistant", content: stepResult.text },
2652
+ ]
2653
+ : streamedMessages;
2654
+ await saveCheckpoint(effectiveGenOptions.threadId, streamedMessages, startStep + currentStepCount);
2611
2655
  }
2612
2656
  }
2613
2657
  : undefined,
@@ -2622,13 +2666,7 @@ export function createAgent(options) {
2622
2666
  });
2623
2667
  }
2624
2668
  if (effectiveGenOptions.threadId && options.checkpointer) {
2625
- const finalMessages = [
2626
- ...initialParams.messages,
2627
- {
2628
- role: "assistant",
2629
- content: finishResult.text,
2630
- },
2631
- ];
2669
+ const finalMessages = buildMessagesFromStepResponses(initialParams.messages, finishResult.steps, finishResult.text);
2632
2670
  await saveCheckpoint(effectiveGenOptions.threadId, finalMessages, startStep + finishResult.steps.length);
2633
2671
  }
2634
2672
  // Invoke unified PostGenerate hooks
@@ -2656,8 +2694,9 @@ export function createAgent(options) {
2656
2694
  });
2657
2695
  // Merge the streamText output into the UI message stream
2658
2696
  writer.merge(result.toUIMessageStream());
2659
- // Wait for initial generation to complete to get final text
2660
- const text = await result.text;
2697
+ // Wait for initial generation to complete
2698
+ await result.text;
2699
+ const resultResponse = await (result.response ?? Promise.resolve(undefined));
2661
2700
  // Save pending interrupt to checkpoint (mirrors stream() pattern)
2662
2701
  if (signalState.interrupt && effectiveGenOptions.threadId && options.checkpointer) {
2663
2702
  const interrupt = signalState.interrupt.interrupt;
@@ -2692,7 +2731,7 @@ export function createAgent(options) {
2692
2731
  let accumulatedStepCount = initialSteps.length;
2693
2732
  let currentMessages = [
2694
2733
  ...messages,
2695
- ...(text ? [{ role: "assistant", content: text }] : []),
2734
+ ...responseMessagesToModelMessages(resultResponse?.messages),
2696
2735
  ];
2697
2736
  let followUpPrompt = await getNextTaskPrompt();
2698
2737
  while (followUpPrompt !== null) {
@@ -2716,12 +2755,16 @@ export function createAgent(options) {
2716
2755
  });
2717
2756
  writer.merge(followUpResult.toUIMessageStream());
2718
2757
  const followUpText = await followUpResult.text;
2758
+ const followUpResponse = await (followUpResult.response ??
2759
+ Promise.resolve(undefined));
2719
2760
  currentMessages = [
2720
2761
  ...currentMessages,
2721
2762
  { role: "user", content: followUpPrompt },
2722
- ...(followUpText
2723
- ? [{ role: "assistant", content: followUpText }]
2724
- : []),
2763
+ ...(responseMessagesToModelMessages(followUpResponse?.messages).length > 0
2764
+ ? responseMessagesToModelMessages(followUpResponse?.messages)
2765
+ : followUpText
2766
+ ? [{ role: "assistant", content: followUpText }]
2767
+ : []),
2725
2768
  ];
2726
2769
  // --- Post-completion bookkeeping for follow-ups ---
2727
2770
  const followUpSteps = await followUpResult.steps;