@lleverage-ai/agent-sdk 0.0.12 → 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,CA2zFxD"}
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({
@@ -2034,11 +2043,12 @@ export function createAgent(options) {
2034
2043
  }
2035
2044
  }
2036
2045
  // Get final result for hooks - need to await all properties
2037
- const [text, usage, finishReason, steps] = await Promise.all([
2046
+ const [text, usage, finishReason, steps, responseMeta] = await Promise.all([
2038
2047
  response.text,
2039
2048
  response.usage,
2040
2049
  response.finishReason,
2041
2050
  response.steps,
2051
+ response.response ?? Promise.resolve(undefined),
2042
2052
  ]);
2043
2053
  // Only access output if an output schema was provided
2044
2054
  let output;
@@ -2060,10 +2070,10 @@ export function createAgent(options) {
2060
2070
  };
2061
2071
  // Save checkpoint if threadId is provided
2062
2072
  if (checkpointThreadId && options.checkpointer) {
2063
- const finalMessages = [
2064
- ...messages,
2065
- ...(text ? [{ role: "assistant", content: text }] : []),
2066
- ];
2073
+ const responseMessages = responseMessagesToModelMessages(responseMeta?.messages);
2074
+ const finalMessages = responseMessages.length > 0
2075
+ ? [...messages, ...responseMessages]
2076
+ : buildMessagesFromStepResponses(messages, steps, text);
2067
2077
  await saveCheckpoint(checkpointThreadId, finalMessages, startStep + steps.length);
2068
2078
  }
2069
2079
  // Save pending interrupt to checkpoint (mirrors generate() pattern)
@@ -2111,9 +2121,12 @@ export function createAgent(options) {
2111
2121
  return;
2112
2122
  }
2113
2123
  const hasCheckpointing = !!(effectiveGenOptions.threadId && options.checkpointer);
2124
+ const initialResponseMessages = responseMessagesToModelMessages(responseMeta?.messages);
2114
2125
  let currentMessages = hasCheckpointing
2115
2126
  ? []
2116
- : [...messages, ...(text ? [{ role: "assistant", content: text }] : [])];
2127
+ : initialResponseMessages.length > 0
2128
+ ? [...messages, ...initialResponseMessages]
2129
+ : buildMessagesFromStepResponses(messages, steps, text);
2117
2130
  let followUpPrompt = await getNextTaskPrompt();
2118
2131
  while (followUpPrompt !== null) {
2119
2132
  const followUpGen = agent.stream({
@@ -2213,6 +2226,7 @@ export function createAgent(options) {
2213
2226
  const signalStopCondition = () => signalState.interrupt != null;
2214
2227
  // Track step count for incremental checkpointing
2215
2228
  let currentStepCount = 0;
2229
+ let streamedMessages = [...initialParams.messages];
2216
2230
  // Execute streamText OUTSIDE createUIMessageStream so errors propagate
2217
2231
  // to the retry loop (if streamText throws synchronously on creation,
2218
2232
  // e.g. rate limit, the catch block handles retry/fallback).
@@ -2236,14 +2250,17 @@ export function createAgent(options) {
2236
2250
  ? async (stepResult) => {
2237
2251
  if (effectiveGenOptions.threadId && options.checkpointer) {
2238
2252
  currentStepCount++;
2239
- // Build messages including this step's results
2240
- const stepMessages = [
2241
- ...initialParams.messages,
2242
- ...(stepResult.text
2243
- ? [{ role: "assistant", content: stepResult.text }]
2244
- : []),
2245
- ];
2246
- 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);
2247
2264
  }
2248
2265
  }
2249
2266
  : undefined,
@@ -2258,12 +2275,7 @@ export function createAgent(options) {
2258
2275
  });
2259
2276
  }
2260
2277
  if (effectiveGenOptions.threadId && options.checkpointer) {
2261
- const finalMessages = [
2262
- ...initialParams.messages,
2263
- ...(finishResult.text
2264
- ? [{ role: "assistant", content: finishResult.text }]
2265
- : []),
2266
- ];
2278
+ const finalMessages = buildMessagesFromStepResponses(initialParams.messages, finishResult.steps, finishResult.text);
2267
2279
  await saveCheckpoint(effectiveGenOptions.threadId, finalMessages, startStep + finishResult.steps.length);
2268
2280
  }
2269
2281
  // Invoke unified PostGenerate hooks
@@ -2294,8 +2306,9 @@ export function createAgent(options) {
2294
2306
  execute: async ({ writer }) => {
2295
2307
  // Merge initial generation into the stream
2296
2308
  writer.merge(result.toUIMessageStream());
2297
- // Wait for initial generation to complete to get final text
2298
- 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));
2299
2312
  // --- Background task completion loop ---
2300
2313
  if (waitForBackgroundTasks) {
2301
2314
  // Track accumulated steps for checkpoint saves
@@ -2303,7 +2316,7 @@ export function createAgent(options) {
2303
2316
  let accumulatedStepCount = initialSteps.length;
2304
2317
  let currentMessages = [
2305
2318
  ...messages,
2306
- ...(text ? [{ role: "assistant", content: text }] : []),
2319
+ ...responseMessagesToModelMessages(resultResponse?.messages),
2307
2320
  ];
2308
2321
  let followUpPrompt = await getNextTaskPrompt();
2309
2322
  while (followUpPrompt !== null) {
@@ -2327,12 +2340,16 @@ export function createAgent(options) {
2327
2340
  });
2328
2341
  writer.merge(followUpResult.toUIMessageStream());
2329
2342
  const followUpText = await followUpResult.text;
2343
+ const followUpResponse = await (followUpResult.response ??
2344
+ Promise.resolve(undefined));
2330
2345
  currentMessages = [
2331
2346
  ...currentMessages,
2332
2347
  { role: "user", content: followUpPrompt },
2333
- ...(followUpText
2334
- ? [{ role: "assistant", content: followUpText }]
2335
- : []),
2348
+ ...(responseMessagesToModelMessages(followUpResponse?.messages).length > 0
2349
+ ? responseMessagesToModelMessages(followUpResponse?.messages)
2350
+ : followUpText
2351
+ ? [{ role: "assistant", content: followUpText }]
2352
+ : []),
2336
2353
  ];
2337
2354
  // --- Post-completion bookkeeping for follow-ups ---
2338
2355
  const followUpSteps = await followUpResult.steps;
@@ -2439,6 +2456,7 @@ export function createAgent(options) {
2439
2456
  };
2440
2457
  // Track step count for incremental checkpointing
2441
2458
  let currentStepCount = 0;
2459
+ let streamedMessages = [...initialParams.messages];
2442
2460
  // Stop condition: stop when an interrupt signal was caught, OR when
2443
2461
  // the step count reaches maxSteps.
2444
2462
  const signalStopCondition = () => signalState.interrupt != null;
@@ -2463,14 +2481,17 @@ export function createAgent(options) {
2463
2481
  ? async (stepResult) => {
2464
2482
  if (effectiveGenOptions.threadId && options.checkpointer) {
2465
2483
  currentStepCount++;
2466
- // Build messages including this step's results
2467
- const stepMessages = [
2468
- ...initialParams.messages,
2469
- ...(stepResult.text
2470
- ? [{ role: "assistant", content: stepResult.text }]
2471
- : []),
2472
- ];
2473
- 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);
2474
2495
  }
2475
2496
  }
2476
2497
  : undefined,
@@ -2485,12 +2506,7 @@ export function createAgent(options) {
2485
2506
  });
2486
2507
  }
2487
2508
  if (effectiveGenOptions.threadId && options.checkpointer) {
2488
- const finalMessages = [
2489
- ...initialParams.messages,
2490
- ...(finishResult.text
2491
- ? [{ role: "assistant", content: finishResult.text }]
2492
- : []),
2493
- ];
2509
+ const finalMessages = buildMessagesFromStepResponses(initialParams.messages, finishResult.steps, finishResult.text);
2494
2510
  await saveCheckpoint(effectiveGenOptions.threadId, finalMessages, startStep + finishResult.steps.length);
2495
2511
  }
2496
2512
  // Invoke unified PostGenerate hooks
@@ -2600,6 +2616,7 @@ export function createAgent(options) {
2600
2616
  };
2601
2617
  // Track step count for incremental checkpointing
2602
2618
  let currentStepCount = 0;
2619
+ let streamedMessages = [...initialParams.messages];
2603
2620
  // Stop condition: stop when a flow-control signal was caught, OR when
2604
2621
  // the step count reaches maxSteps.
2605
2622
  const signalStopCondition = () => signalState.interrupt != null;
@@ -2624,15 +2641,17 @@ export function createAgent(options) {
2624
2641
  ? async (stepResult) => {
2625
2642
  if (effectiveGenOptions.threadId && options.checkpointer) {
2626
2643
  currentStepCount++;
2627
- // Build messages including this step's results
2628
- const stepMessages = [
2629
- ...initialParams.messages,
2630
- {
2631
- role: "assistant",
2632
- content: stepResult.text,
2633
- },
2634
- ];
2635
- 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);
2636
2655
  }
2637
2656
  }
2638
2657
  : undefined,
@@ -2647,13 +2666,7 @@ export function createAgent(options) {
2647
2666
  });
2648
2667
  }
2649
2668
  if (effectiveGenOptions.threadId && options.checkpointer) {
2650
- const finalMessages = [
2651
- ...initialParams.messages,
2652
- {
2653
- role: "assistant",
2654
- content: finishResult.text,
2655
- },
2656
- ];
2669
+ const finalMessages = buildMessagesFromStepResponses(initialParams.messages, finishResult.steps, finishResult.text);
2657
2670
  await saveCheckpoint(effectiveGenOptions.threadId, finalMessages, startStep + finishResult.steps.length);
2658
2671
  }
2659
2672
  // Invoke unified PostGenerate hooks
@@ -2681,8 +2694,9 @@ export function createAgent(options) {
2681
2694
  });
2682
2695
  // Merge the streamText output into the UI message stream
2683
2696
  writer.merge(result.toUIMessageStream());
2684
- // Wait for initial generation to complete to get final text
2685
- 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));
2686
2700
  // Save pending interrupt to checkpoint (mirrors stream() pattern)
2687
2701
  if (signalState.interrupt && effectiveGenOptions.threadId && options.checkpointer) {
2688
2702
  const interrupt = signalState.interrupt.interrupt;
@@ -2717,7 +2731,7 @@ export function createAgent(options) {
2717
2731
  let accumulatedStepCount = initialSteps.length;
2718
2732
  let currentMessages = [
2719
2733
  ...messages,
2720
- ...(text ? [{ role: "assistant", content: text }] : []),
2734
+ ...responseMessagesToModelMessages(resultResponse?.messages),
2721
2735
  ];
2722
2736
  let followUpPrompt = await getNextTaskPrompt();
2723
2737
  while (followUpPrompt !== null) {
@@ -2741,12 +2755,16 @@ export function createAgent(options) {
2741
2755
  });
2742
2756
  writer.merge(followUpResult.toUIMessageStream());
2743
2757
  const followUpText = await followUpResult.text;
2758
+ const followUpResponse = await (followUpResult.response ??
2759
+ Promise.resolve(undefined));
2744
2760
  currentMessages = [
2745
2761
  ...currentMessages,
2746
2762
  { role: "user", content: followUpPrompt },
2747
- ...(followUpText
2748
- ? [{ role: "assistant", content: followUpText }]
2749
- : []),
2763
+ ...(responseMessagesToModelMessages(followUpResponse?.messages).length > 0
2764
+ ? responseMessagesToModelMessages(followUpResponse?.messages)
2765
+ : followUpText
2766
+ ? [{ role: "assistant", content: followUpText }]
2767
+ : []),
2750
2768
  ];
2751
2769
  // --- Post-completion bookkeeping for follow-ups ---
2752
2770
  const followUpSteps = await followUpResult.steps;