@contractspec/lib.ai-agent 5.0.4 → 6.0.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.
Files changed (67) hide show
  1. package/README.md +41 -0
  2. package/dist/agent/agent-factory.d.ts +13 -0
  3. package/dist/agent/agent-factory.js +290 -63
  4. package/dist/agent/contract-spec-agent.d.ts +9 -0
  5. package/dist/agent/contract-spec-agent.js +287 -63
  6. package/dist/agent/index.js +353 -129
  7. package/dist/agent/json-runner.js +290 -66
  8. package/dist/agent/unified-agent.js +350 -126
  9. package/dist/exporters/claude-agent-exporter.js +12 -1
  10. package/dist/exporters/index.js +12 -1
  11. package/dist/exporters/opencode-exporter.js +11 -0
  12. package/dist/index.js +11 -0
  13. package/dist/interop/index.js +24 -2
  14. package/dist/interop/spec-consumer.js +11 -0
  15. package/dist/interop/tool-consumer.js +13 -2
  16. package/dist/node/agent/agent-factory.js +290 -63
  17. package/dist/node/agent/contract-spec-agent.js +287 -63
  18. package/dist/node/agent/index.js +353 -129
  19. package/dist/node/agent/json-runner.js +290 -66
  20. package/dist/node/agent/unified-agent.js +350 -126
  21. package/dist/node/exporters/claude-agent-exporter.js +12 -1
  22. package/dist/node/exporters/index.js +12 -1
  23. package/dist/node/exporters/opencode-exporter.js +11 -0
  24. package/dist/node/index.js +11 -0
  25. package/dist/node/interop/index.js +24 -2
  26. package/dist/node/interop/spec-consumer.js +11 -0
  27. package/dist/node/interop/tool-consumer.js +13 -2
  28. package/dist/node/providers/claude-agent-sdk/adapter.js +11 -0
  29. package/dist/node/providers/claude-agent-sdk/index.js +11 -0
  30. package/dist/node/providers/index.js +11 -0
  31. package/dist/node/providers/opencode-sdk/adapter.js +11 -0
  32. package/dist/node/providers/opencode-sdk/index.js +11 -0
  33. package/dist/node/spec/index.js +11 -0
  34. package/dist/node/spec/spec.js +11 -0
  35. package/dist/node/tools/agent-memory-store.js +24 -0
  36. package/dist/node/tools/in-memory-agent-memory-store.js +236 -0
  37. package/dist/node/tools/index.js +463 -42
  38. package/dist/node/tools/memory-tools.js +45 -0
  39. package/dist/node/tools/operation-tool-handler.js +35 -0
  40. package/dist/node/tools/subagent-tool.js +95 -0
  41. package/dist/node/tools/tool-adapter.js +192 -25
  42. package/dist/providers/claude-agent-sdk/adapter.js +11 -0
  43. package/dist/providers/claude-agent-sdk/index.js +11 -0
  44. package/dist/providers/index.js +11 -0
  45. package/dist/providers/opencode-sdk/adapter.js +11 -0
  46. package/dist/providers/opencode-sdk/index.js +11 -0
  47. package/dist/spec/index.js +11 -0
  48. package/dist/spec/spec.d.ts +69 -1
  49. package/dist/spec/spec.js +11 -0
  50. package/dist/tools/agent-memory-store.d.ts +26 -0
  51. package/dist/tools/agent-memory-store.js +24 -0
  52. package/dist/tools/agent-memory-store.test.d.ts +1 -0
  53. package/dist/tools/in-memory-agent-memory-store.d.ts +18 -0
  54. package/dist/tools/in-memory-agent-memory-store.js +236 -0
  55. package/dist/tools/index.d.ts +5 -0
  56. package/dist/tools/index.js +463 -42
  57. package/dist/tools/mcp-client.browser.d.ts +6 -6
  58. package/dist/tools/memory-tools.d.ts +29 -0
  59. package/dist/tools/memory-tools.js +45 -0
  60. package/dist/tools/operation-tool-handler.d.ts +24 -0
  61. package/dist/tools/operation-tool-handler.js +35 -0
  62. package/dist/tools/subagent-tool.d.ts +66 -0
  63. package/dist/tools/subagent-tool.js +95 -0
  64. package/dist/tools/tool-adapter.d.ts +26 -10
  65. package/dist/tools/tool-adapter.js +192 -25
  66. package/dist/types.d.ts +9 -3
  67. package/package.json +67 -7
@@ -2260,15 +2260,124 @@ function jsonSchemaToZodSafe(schema) {
2260
2260
  }
2261
2261
  var init_json_schema_to_zod = () => {};
2262
2262
 
2263
+ // src/tools/operation-tool-handler.ts
2264
+ function toolCtxToHandlerCtx(ctx) {
2265
+ return {
2266
+ traceId: ctx.metadata?.traceId,
2267
+ organizationId: ctx.tenantId ?? null,
2268
+ userId: ctx.actorId ?? null,
2269
+ actor: ctx.actorId ? "user" : "anonymous",
2270
+ channel: "agent",
2271
+ roles: []
2272
+ };
2273
+ }
2274
+ function createOperationToolHandler(registry, operationRef) {
2275
+ return async (input, context) => {
2276
+ const handlerCtx = toolCtxToHandlerCtx(context);
2277
+ const result = await registry.execute(operationRef.key, operationRef.version, input ?? {}, handlerCtx);
2278
+ return result;
2279
+ };
2280
+ }
2281
+
2282
+ // src/tools/subagent-tool.ts
2283
+ import { readUIMessageStream, tool } from "ai";
2284
+ import { z as z2 } from "zod";
2285
+ function toReadableStream(iterable) {
2286
+ if (iterable instanceof ReadableStream) {
2287
+ return iterable;
2288
+ }
2289
+ return new ReadableStream({
2290
+ async start(controller) {
2291
+ try {
2292
+ for await (const chunk of iterable) {
2293
+ controller.enqueue(chunk);
2294
+ }
2295
+ } finally {
2296
+ controller.close();
2297
+ }
2298
+ }
2299
+ });
2300
+ }
2301
+ function createSubagentTool(options) {
2302
+ const {
2303
+ subagent,
2304
+ description = "Research a topic or question in depth.",
2305
+ taskParam = "task",
2306
+ toModelSummary = true,
2307
+ passConversationHistory = false
2308
+ } = options;
2309
+ const inputSchema = z2.object({
2310
+ [taskParam]: z2.string().describe("The research task to complete")
2311
+ });
2312
+ const execute = async function* (input, options2) {
2313
+ const task = String(input[taskParam] ?? input.task ?? "");
2314
+ const { abortSignal, messages } = options2 ?? {};
2315
+ if (passConversationHistory && messages && messages.length > 0 && typeof subagent.generate === "function") {
2316
+ const result2 = await subagent.generate({
2317
+ messages: [...messages, { role: "user", content: task }],
2318
+ abortSignal
2319
+ });
2320
+ yield { parts: [{ type: "text", text: result2.text }] };
2321
+ return;
2322
+ }
2323
+ const result = await subagent.stream({
2324
+ prompt: task,
2325
+ abortSignal
2326
+ });
2327
+ const uiStream = result.toUIMessageStream();
2328
+ const stream = toReadableStream(uiStream);
2329
+ for await (const message of readUIMessageStream({
2330
+ stream
2331
+ })) {
2332
+ yield message;
2333
+ }
2334
+ };
2335
+ const toolOptions = {
2336
+ description,
2337
+ inputSchema,
2338
+ execute,
2339
+ ...toModelSummary && {
2340
+ toModelOutput: ({
2341
+ output
2342
+ }) => {
2343
+ const parts = output?.parts;
2344
+ if (!Array.isArray(parts)) {
2345
+ return { type: "text", value: "Task completed." };
2346
+ }
2347
+ const lastTextPart = [...parts].reverse().find((p) => p?.type === "text");
2348
+ return {
2349
+ type: "text",
2350
+ value: lastTextPart?.text ?? "Task completed."
2351
+ };
2352
+ }
2353
+ }
2354
+ };
2355
+ return tool(toolOptions);
2356
+ }
2357
+ var init_subagent_tool = () => {};
2358
+
2263
2359
  // src/tools/tool-adapter.ts
2264
- import { tool } from "ai";
2265
- function specToolToAISDKTool(specTool, handler, context = {}) {
2360
+ import { tool as tool2 } from "ai";
2361
+ function isAsyncGenerator(value) {
2362
+ return typeof value === "object" && value !== null && typeof value.next === "function" && typeof value[Symbol.asyncIterator] === "function";
2363
+ }
2364
+ function specToolToAISDKTool(specTool, handler, context = {}, effectiveInputSchema, operationSpec) {
2266
2365
  let lastInvocationAt;
2267
- return tool({
2366
+ const inputSchema = effectiveInputSchema ?? jsonSchemaToZodSafe(specTool.schema);
2367
+ const buildContext = (signal) => ({
2368
+ agentId: context.agentId ?? "unknown",
2369
+ sessionId: context.sessionId ?? "unknown",
2370
+ tenantId: context.tenantId,
2371
+ actorId: context.actorId,
2372
+ locale: context.locale,
2373
+ metadata: context.metadata,
2374
+ signal
2375
+ });
2376
+ return tool2({
2268
2377
  description: specTool.description ?? specTool.name,
2269
- inputSchema: jsonSchemaToZodSafe(specTool.schema),
2378
+ inputSchema,
2270
2379
  needsApproval: specTool.requiresApproval ?? !specTool.automationSafe,
2271
- execute: async (input) => {
2380
+ execute: async function* (input, options) {
2272
2381
  const now = Date.now();
2273
2382
  const cooldownMs = normalizeDuration(specTool.cooldownMs);
2274
2383
  if (cooldownMs && lastInvocationAt !== undefined) {
@@ -2279,19 +2388,20 @@ function specToolToAISDKTool(specTool, handler, context = {}) {
2279
2388
  }
2280
2389
  }
2281
2390
  const timeoutMs = normalizeDuration(specTool.timeoutMs);
2282
- const { signal, dispose } = createTimeoutSignal(context.signal, timeoutMs);
2391
+ const signal = options?.abortSignal ?? context.signal;
2392
+ const { signal: timeoutSignal, dispose } = createTimeoutSignal(signal, timeoutMs);
2283
2393
  try {
2284
- const execution = handler(input, {
2285
- agentId: context.agentId ?? "unknown",
2286
- sessionId: context.sessionId ?? "unknown",
2287
- tenantId: context.tenantId,
2288
- actorId: context.actorId,
2289
- locale: context.locale,
2290
- metadata: context.metadata,
2291
- signal
2292
- });
2293
- const result = timeoutMs ? await withTimeout(execution, timeoutMs, specTool.name) : await execution;
2294
- return typeof result === "string" ? result : JSON.stringify(result);
2394
+ const execution = handler(input, buildContext(timeoutSignal));
2395
+ if (isAsyncGenerator(execution)) {
2396
+ for await (const raw of execution) {
2397
+ const wrapped = wrapToolOutputForRendering(specTool, raw, operationSpec);
2398
+ yield typeof wrapped === "string" ? wrapped : wrapped;
2399
+ }
2400
+ } else {
2401
+ const raw = timeoutMs ? await withTimeout(Promise.resolve(execution), timeoutMs, specTool.name) : await Promise.resolve(execution);
2402
+ const wrapped = wrapToolOutputForRendering(specTool, raw, operationSpec);
2403
+ yield typeof wrapped === "string" ? wrapped : wrapped;
2404
+ }
2295
2405
  } finally {
2296
2406
  dispose();
2297
2407
  lastInvocationAt = Date.now();
@@ -2299,27 +2409,83 @@ function specToolToAISDKTool(specTool, handler, context = {}) {
2299
2409
  }
2300
2410
  });
2301
2411
  }
2302
- function specToolsToAISDKTools(specTools, handlers, context = {}) {
2412
+ function specToolsToAISDKTools(specTools, handlers, context = {}, options) {
2303
2413
  const tools = {};
2304
2414
  for (const specTool of specTools) {
2305
- const handler = handlers.get(specTool.name);
2306
- if (!handler) {
2307
- throw new Error(createAgentI18n(context.locale).t("error.missingToolHandler", {
2308
- name: specTool.name
2309
- }));
2415
+ if (specTool.subagentRef && options?.subagentRegistry) {
2416
+ const subagent = options.subagentRegistry.get(specTool.subagentRef.agentId);
2417
+ if (!subagent) {
2418
+ throw new Error(`Subagent not found: ${specTool.subagentRef.agentId}. Register it in subagentRegistry.`);
2419
+ }
2420
+ if (specTool.requiresApproval === true || specTool.automationSafe === false) {
2421
+ console.warn(`[ContractSpec] Subagent tool "${specTool.name}" cannot use needsApproval. ` + `requiresApproval and automationSafe are ignored for subagent tools (AI SDK limitation). ` + `See https://ai-sdk.dev/docs/agents/subagents#no-tool-approvals-in-subagents`);
2422
+ }
2423
+ tools[specTool.name] = createSubagentTool({
2424
+ subagent,
2425
+ description: specTool.description ?? specTool.name,
2426
+ toModelSummary: specTool.subagentRef.toModelSummary ?? true,
2427
+ passConversationHistory: specTool.subagentRef.passConversationHistory
2428
+ });
2429
+ continue;
2310
2430
  }
2311
- tools[specTool.name] = specToolToAISDKTool(specTool, handler, context);
2431
+ let handler;
2432
+ let effectiveInputSchema;
2433
+ let op;
2434
+ if (specTool.operationRef && options?.operationRegistry) {
2435
+ op = options.operationRegistry.get(specTool.operationRef.key, specTool.operationRef.version);
2436
+ if (!op) {
2437
+ throw new Error(`Operation not found: ${specTool.operationRef.key}${specTool.operationRef.version ? `.v${specTool.operationRef.version}` : ""}`);
2438
+ }
2439
+ handler = createOperationToolHandler(options.operationRegistry, specTool.operationRef);
2440
+ effectiveInputSchema = op.io.input?.getZod?.();
2441
+ } else {
2442
+ const manualHandler = handlers.get(specTool.name);
2443
+ if (!manualHandler) {
2444
+ if (specTool.subagentRef) {
2445
+ throw new Error(`Subagent tool "${specTool.name}" requires subagentRegistry. Pass subagentRegistry in ContractSpecAgentConfig.`);
2446
+ }
2447
+ throw new Error(createAgentI18n(context.locale).t("error.missingToolHandler", {
2448
+ name: specTool.name
2449
+ }));
2450
+ }
2451
+ handler = manualHandler;
2452
+ }
2453
+ tools[specTool.name] = specToolToAISDKTool(specTool, handler, context, effectiveInputSchema, op);
2312
2454
  }
2313
2455
  return tools;
2314
2456
  }
2315
2457
  function createToolHandler(handler) {
2316
- return async (input, context) => {
2458
+ return (input, context) => {
2317
2459
  return handler(input, context);
2318
2460
  };
2319
2461
  }
2320
2462
  function buildToolHandlers(handlersObj) {
2321
2463
  return new Map(Object.entries(handlersObj));
2322
2464
  }
2465
+ function wrapToolOutputForRendering(specTool, result, operationSpec) {
2466
+ const presentation = specTool.outputPresentation ?? operationSpec?.outputPresentation;
2467
+ const form = specTool.outputForm ?? operationSpec?.outputForm;
2468
+ const dataView = specTool.outputDataView ?? operationSpec?.outputDataView;
2469
+ if (presentation) {
2470
+ return {
2471
+ presentationKey: presentation.key,
2472
+ data: result
2473
+ };
2474
+ }
2475
+ if (form) {
2476
+ return {
2477
+ formKey: form.key,
2478
+ defaultValues: typeof result === "object" && result !== null ? result : {}
2479
+ };
2480
+ }
2481
+ if (dataView) {
2482
+ return {
2483
+ dataViewKey: dataView.key,
2484
+ items: Array.isArray(result) ? result : result != null ? [result] : []
2485
+ };
2486
+ }
2487
+ return result;
2488
+ }
2323
2489
  function normalizeDuration(value) {
2324
2490
  if (value === undefined) {
2325
2491
  return;
@@ -2381,11 +2547,12 @@ function createToolExecutionError(message, code, retryAfterMs) {
2381
2547
  var init_tool_adapter = __esm(() => {
2382
2548
  init_json_schema_to_zod();
2383
2549
  init_i18n();
2550
+ init_subagent_tool();
2384
2551
  });
2385
2552
 
2386
2553
  // src/tools/knowledge-tool.ts
2387
- import { tool as tool2 } from "ai";
2388
- import * as z2 from "zod";
2554
+ import { tool as tool3 } from "ai";
2555
+ import * as z3 from "zod";
2389
2556
  function createKnowledgeQueryTool(retriever, knowledgeRefs, locale) {
2390
2557
  const i18n = createAgentI18n(locale);
2391
2558
  const optionalSpaces = knowledgeRefs.filter((k) => !k.required).map((k) => k.key).filter((key) => retriever.supportsSpace(key));
@@ -2394,15 +2561,15 @@ function createKnowledgeQueryTool(retriever, knowledgeRefs, locale) {
2394
2561
  }
2395
2562
  const spaceDescriptions = knowledgeRefs.filter((k) => !k.required && retriever.supportsSpace(k.key)).map((k) => `- ${k.key}: ${k.instructions ?? i18n.t("tool.knowledge.spaceDefault")}`).join(`
2396
2563
  `);
2397
- return tool2({
2564
+ return tool3({
2398
2565
  description: `${i18n.t("tool.knowledge.description")}
2399
2566
 
2400
2567
  ${i18n.t("tool.knowledge.availableSpaces")}
2401
2568
  ${spaceDescriptions}`,
2402
- inputSchema: z2.object({
2403
- query: z2.string().describe(i18n.t("tool.knowledge.param.query")),
2404
- spaceKey: z2.enum(optionalSpaces).optional().describe(i18n.t("tool.knowledge.param.spaceKey")),
2405
- topK: z2.number().optional().default(5).describe(i18n.t("tool.knowledge.param.topK"))
2569
+ inputSchema: z3.object({
2570
+ query: z3.string().describe(i18n.t("tool.knowledge.param.query")),
2571
+ spaceKey: z3.enum(optionalSpaces).optional().describe(i18n.t("tool.knowledge.param.spaceKey")),
2572
+ topK: z3.number().optional().default(5).describe(i18n.t("tool.knowledge.param.topK"))
2406
2573
  }),
2407
2574
  execute: async ({ query, spaceKey, topK }) => {
2408
2575
  const spacesToSearch = spaceKey ? [spaceKey] : optionalSpaces;
@@ -2442,6 +2609,33 @@ var init_knowledge_tool = __esm(() => {
2442
2609
  init_i18n();
2443
2610
  });
2444
2611
 
2612
+ // src/tools/memory-tools.ts
2613
+ import { anthropic } from "@ai-sdk/anthropic";
2614
+ function createAnthropicMemoryTool(store) {
2615
+ const memory = anthropic.tools.memory_20250818({
2616
+ execute: async (action) => {
2617
+ switch (action.command) {
2618
+ case "view":
2619
+ return store.view(action.path ?? "/memories", action.view_range);
2620
+ case "create":
2621
+ return store.create(action.path ?? "/memories/untitled", action.file_text ?? "");
2622
+ case "str_replace":
2623
+ return store.strReplace(action.path ?? "/memories", action.old_str ?? "", action.new_str ?? "");
2624
+ case "insert":
2625
+ return store.insert(action.path ?? "/memories", action.insert_line ?? 0, action.insert_text ?? "");
2626
+ case "delete":
2627
+ return store.delete(action.path ?? "/memories");
2628
+ case "rename":
2629
+ return store.rename(action.old_path ?? "/memories", action.new_path ?? "/memories");
2630
+ default:
2631
+ return `Unknown command: ${action.command}`;
2632
+ }
2633
+ }
2634
+ });
2635
+ return memory;
2636
+ }
2637
+ var init_memory_tools = () => {};
2638
+
2445
2639
  // src/tools/mcp-client-helpers.ts
2446
2640
  import {
2447
2641
  Experimental_StdioMCPTransport as StdioClientTransport
@@ -2472,8 +2666,8 @@ function prefixToolNames(config, tools) {
2472
2666
  return tools;
2473
2667
  }
2474
2668
  const prefixedTools = {};
2475
- for (const [toolName, tool3] of Object.entries(tools)) {
2476
- prefixedTools[`${prefix}_${toolName}`] = tool3;
2669
+ for (const [toolName, tool4] of Object.entries(tools)) {
2670
+ prefixedTools[`${prefix}_${toolName}`] = tool4;
2477
2671
  }
2478
2672
  return prefixedTools;
2479
2673
  }
@@ -2579,12 +2773,12 @@ async function createMcpToolsets(configs, options = {}) {
2579
2773
  for (const [serverName, toolNames] of Object.entries(result.serverToolNames)) {
2580
2774
  serverToolNames[serverName] = toolNames;
2581
2775
  }
2582
- for (const [toolName, tool3] of Object.entries(result.tools)) {
2776
+ for (const [toolName, tool4] of Object.entries(result.tools)) {
2583
2777
  const hasCollision = combinedTools[toolName] !== undefined;
2584
2778
  if (hasCollision && collisionStrategy === "error") {
2585
2779
  throw new Error(`Duplicate MCP tool name "${toolName}" detected. Use "toolPrefix" or set onNameCollision to "overwrite".`);
2586
2780
  }
2587
- combinedTools[toolName] = tool3;
2781
+ combinedTools[toolName] = tool4;
2588
2782
  }
2589
2783
  }
2590
2784
  } catch (error) {
@@ -2607,11 +2801,231 @@ var init_mcp_client = __esm(() => {
2607
2801
  init_mcp_client_helpers();
2608
2802
  });
2609
2803
 
2804
+ // src/tools/agent-memory-store.ts
2805
+ function validateMemoryPath(path) {
2806
+ const normalized = path.replace(/\\/g, "/").replace(/\/+/g, "/");
2807
+ if (!normalized.startsWith("/memories") || normalized.includes("..") || normalized === "/memories/.." || normalized.startsWith("/memories/../")) {
2808
+ throw new Error(`Invalid memory path: ${path}. Path must be under /memories and cannot contain traversal sequences.`);
2809
+ }
2810
+ }
2811
+
2812
+ // src/tools/in-memory-agent-memory-store.ts
2813
+ function formatSize(bytes) {
2814
+ if (bytes < 1024)
2815
+ return `${bytes}B`;
2816
+ if (bytes < 1024 * 1024)
2817
+ return `${(bytes / 1024).toFixed(1)}K`;
2818
+ return `${(bytes / (1024 * 1024)).toFixed(1)}M`;
2819
+ }
2820
+
2821
+ class InMemoryAgentMemoryStore {
2822
+ store = new Map;
2823
+ async view(path, viewRange) {
2824
+ validateMemoryPath(path);
2825
+ const normalized = path.replace(/\/+/g, "/").replace(/\/$/, "") || "/memories";
2826
+ if (this.isDirectory(normalized)) {
2827
+ return this.listDirectory(normalized);
2828
+ }
2829
+ const content = this.store.get(normalized);
2830
+ if (content === undefined) {
2831
+ return `The path ${path} does not exist. Please provide a valid path.`;
2832
+ }
2833
+ const lines = content.split(`
2834
+ `);
2835
+ if (lines.length > 999999) {
2836
+ return `File ${path} exceeds maximum line limit of 999,999 lines.`;
2837
+ }
2838
+ const [start, end] = viewRange ?? [1, lines.length];
2839
+ const from = Math.max(0, start - 1);
2840
+ const to = Math.min(lines.length, end);
2841
+ const selected = lines.slice(from, to);
2842
+ const numbered = selected.map((line, i) => {
2843
+ const num = from + i + 1;
2844
+ return `${String(num).padStart(6)} ${line}`;
2845
+ }).join(`
2846
+ `);
2847
+ return `Here's the content of ${path} with line numbers:
2848
+ ${numbered}`;
2849
+ }
2850
+ async create(path, fileText) {
2851
+ validateMemoryPath(path);
2852
+ const normalized = path.replace(/\/+/g, "/");
2853
+ if (this.store.has(normalized)) {
2854
+ return `Error: File ${path} already exists`;
2855
+ }
2856
+ this.ensureParentDir(normalized);
2857
+ this.store.set(normalized, fileText);
2858
+ return `File created successfully at: ${path}`;
2859
+ }
2860
+ async strReplace(path, oldStr, newStr) {
2861
+ validateMemoryPath(path);
2862
+ const normalized = path.replace(/\/+/g, "/");
2863
+ const content = this.store.get(normalized);
2864
+ if (content === undefined) {
2865
+ return `Error: The path ${path} does not exist. Please provide a valid path.`;
2866
+ }
2867
+ const count = (content.match(new RegExp(escapeRegex(oldStr), "g")) ?? []).length;
2868
+ if (count > 1) {
2869
+ const lines = content.split(`
2870
+ `);
2871
+ const lineNums = [];
2872
+ lines.forEach((line, i) => {
2873
+ if (line.includes(oldStr))
2874
+ lineNums.push(i + 1);
2875
+ });
2876
+ return `No replacement was performed. Multiple occurrences of old_str \`${oldStr}\` in lines: ${lineNums.join(", ")}. Please ensure it is unique`;
2877
+ }
2878
+ if (count === 0) {
2879
+ return `No replacement was performed, old_str \`${oldStr}\` did not appear verbatim in ${path}.`;
2880
+ }
2881
+ const newContent = content.replace(oldStr, newStr);
2882
+ this.store.set(normalized, newContent);
2883
+ const snippet = newContent.split(`
2884
+ `).slice(0, 5);
2885
+ const numbered = snippet.map((line, i) => `${String(i + 1).padStart(6)} ${line}`).join(`
2886
+ `);
2887
+ return `The memory file has been edited.
2888
+ ${numbered}`;
2889
+ }
2890
+ async insert(path, insertLine, insertText) {
2891
+ validateMemoryPath(path);
2892
+ const normalized = path.replace(/\/+/g, "/");
2893
+ const content = this.store.get(normalized);
2894
+ if (content === undefined) {
2895
+ return `Error: The path ${path} does not exist`;
2896
+ }
2897
+ const lines = content.split(`
2898
+ `);
2899
+ const n = lines.length;
2900
+ if (insertLine < 0 || insertLine > n) {
2901
+ return `Error: Invalid \`insert_line\` parameter: ${insertLine}. It should be within the range of lines of the file: [0, ${n}]`;
2902
+ }
2903
+ lines.splice(insertLine, 0, insertText.replace(/\n$/, ""));
2904
+ this.store.set(normalized, lines.join(`
2905
+ `));
2906
+ return `The file ${path} has been edited.`;
2907
+ }
2908
+ async delete(path) {
2909
+ validateMemoryPath(path);
2910
+ const normalized = path.replace(/\/+/g, "/");
2911
+ if (this.isDirectory(normalized)) {
2912
+ const prefix = normalized === "/memories" ? "/memories/" : `${normalized}/`;
2913
+ for (const key of this.store.keys()) {
2914
+ if (key.startsWith(prefix) || key === normalized) {
2915
+ this.store.delete(key);
2916
+ }
2917
+ }
2918
+ } else {
2919
+ if (!this.store.has(normalized)) {
2920
+ return `Error: The path ${path} does not exist`;
2921
+ }
2922
+ this.store.delete(normalized);
2923
+ }
2924
+ return `Successfully deleted ${path}`;
2925
+ }
2926
+ async rename(oldPath, newPath) {
2927
+ validateMemoryPath(oldPath);
2928
+ validateMemoryPath(newPath);
2929
+ const oldNorm = oldPath.replace(/\/+/g, "/");
2930
+ const newNorm = newPath.replace(/\/+/g, "/");
2931
+ if (this.store.has(newNorm) || this.hasAnyChild(newNorm)) {
2932
+ return `Error: The destination ${newPath} already exists`;
2933
+ }
2934
+ if (this.isDirectory(oldNorm)) {
2935
+ const oldPrefix = `${oldNorm}/`;
2936
+ const entries = Array.from(this.store.entries()).filter(([k]) => k.startsWith(oldPrefix) || k === oldNorm);
2937
+ const newPrefix = `${newNorm}/`;
2938
+ for (const [k, v] of entries) {
2939
+ this.store.delete(k);
2940
+ const newKey = k === oldNorm ? newNorm : newPrefix + k.slice(oldPrefix.length);
2941
+ this.store.set(newKey, v);
2942
+ }
2943
+ } else {
2944
+ const content = this.store.get(oldNorm);
2945
+ if (content === undefined) {
2946
+ return `Error: The path ${oldPath} does not exist`;
2947
+ }
2948
+ this.store.delete(oldNorm);
2949
+ this.ensureParentDir(newNorm);
2950
+ this.store.set(newNorm, content);
2951
+ }
2952
+ return `Successfully renamed ${oldPath} to ${newPath}`;
2953
+ }
2954
+ isDirectory(path) {
2955
+ if (path === "/memories")
2956
+ return true;
2957
+ for (const key of this.store.keys()) {
2958
+ if (key.startsWith(path + "/"))
2959
+ return true;
2960
+ }
2961
+ return false;
2962
+ }
2963
+ hasAnyChild(path) {
2964
+ const prefix = path.endsWith("/") ? path : `${path}/`;
2965
+ for (const key of this.store.keys()) {
2966
+ if (key.startsWith(prefix))
2967
+ return true;
2968
+ }
2969
+ return false;
2970
+ }
2971
+ ensureParentDir(path) {
2972
+ const parts = path.split("/").filter(Boolean);
2973
+ parts.pop();
2974
+ for (let i = 1;i <= parts.length; i++) {
2975
+ const p = "/" + parts.slice(0, i).join("/");
2976
+ if (!this.store.has(p)) {
2977
+ this.store.set(p, "");
2978
+ }
2979
+ }
2980
+ }
2981
+ listDirectory(path) {
2982
+ const prefix = path === "/memories" ? "/memories/" : `${path}/`;
2983
+ const seen = new Set;
2984
+ const entries = [];
2985
+ if (path === "/memories") {
2986
+ entries.push({
2987
+ path: "/memories",
2988
+ size: Array.from(this.store.keys()).filter((k) => k.startsWith("/memories/")).reduce((acc, k) => acc + (this.store.get(k)?.length ?? 0), 0)
2989
+ });
2990
+ }
2991
+ for (const key of this.store.keys()) {
2992
+ if (!key.startsWith(prefix) && key !== path)
2993
+ continue;
2994
+ const rel = key.slice(prefix.length);
2995
+ const first = rel.split("/")[0];
2996
+ if (!first || first.startsWith(".") || first === "node_modules")
2997
+ continue;
2998
+ const fullPath = path === "/memories" ? `/memories/${first}` : `${path}/${first}`;
2999
+ if (seen.has(fullPath))
3000
+ continue;
3001
+ seen.add(fullPath);
3002
+ let size = 0;
3003
+ if (this.store.has(key)) {
3004
+ size = (this.store.get(key) ?? "").length;
3005
+ } else {
3006
+ for (const k of this.store.keys()) {
3007
+ if (k.startsWith(fullPath + "/") || k === fullPath) {
3008
+ size += (this.store.get(k) ?? "").length;
3009
+ }
3010
+ }
3011
+ }
3012
+ entries.push({ path: fullPath, size });
3013
+ }
3014
+ const lines = entries.slice(0, 50).map((e) => `${formatSize(e.size)} ${e.path}`).join(`
3015
+ `);
3016
+ return `Here're the files and directories up to 2 levels deep in ${path}, excluding hidden items and node_modules:
3017
+ ${lines}`;
3018
+ }
3019
+ }
3020
+ function escapeRegex(s) {
3021
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3022
+ }
3023
+
2610
3024
  // src/tools/mcp-server.ts
2611
3025
  init_json_schema_to_zod();
2612
3026
  init_i18n();
2613
3027
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2614
- import * as z3 from "zod";
3028
+ import * as z4 from "zod";
2615
3029
  import { sanitizeMcpName } from "@contractspec/lib.contracts-spec/jsonschema";
2616
3030
  function agentToMcpServer(agent, spec) {
2617
3031
  const i18n = createAgentI18n(spec.locale);
@@ -2621,9 +3035,9 @@ function agentToMcpServer(agent, spec) {
2621
3035
  });
2622
3036
  server.registerTool(sanitizeMcpName(spec.meta.key), {
2623
3037
  description: spec.description ?? i18n.t("tool.mcp.agentDescription", { key: spec.meta.key }),
2624
- inputSchema: z3.object({
2625
- message: z3.string().describe(i18n.t("tool.mcp.param.message")),
2626
- sessionId: z3.string().optional().describe(i18n.t("tool.mcp.param.sessionId"))
3038
+ inputSchema: z4.object({
3039
+ message: z4.string().describe(i18n.t("tool.mcp.param.message")),
3040
+ sessionId: z4.string().optional().describe(i18n.t("tool.mcp.param.sessionId"))
2627
3041
  })
2628
3042
  }, async (args) => {
2629
3043
  const { message, sessionId } = args;
@@ -2641,7 +3055,7 @@ function agentToMcpServer(agent, spec) {
2641
3055
  };
2642
3056
  });
2643
3057
  for (const toolConfig of spec.tools) {
2644
- const inputSchema = toolConfig.schema ? jsonSchemaToZodSafe(toolConfig.schema) : z3.object({});
3058
+ const inputSchema = toolConfig.schema ? jsonSchemaToZodSafe(toolConfig.schema) : z4.object({});
2645
3059
  server.registerTool(sanitizeMcpName(`${spec.meta.key}.${toolConfig.name}`), {
2646
3060
  description: toolConfig.description ?? i18n.t("tool.mcp.toolDescription", { name: toolConfig.name }),
2647
3061
  inputSchema
@@ -2671,15 +3085,22 @@ function createAgentMcpServer(config) {
2671
3085
  // src/tools/index.ts
2672
3086
  init_tool_adapter();
2673
3087
  init_knowledge_tool();
3088
+ init_subagent_tool();
2674
3089
  init_mcp_client();
3090
+ init_memory_tools();
2675
3091
  export {
3092
+ validateMemoryPath,
2676
3093
  specToolsToAISDKTools,
2677
3094
  specToolToAISDKTool,
2678
3095
  mcpServerToTools,
2679
3096
  createToolHandler,
3097
+ createSubagentTool,
3098
+ createOperationToolHandler,
2680
3099
  createMcpToolsets,
2681
3100
  createKnowledgeQueryTool,
3101
+ createAnthropicMemoryTool,
2682
3102
  createAgentMcpServer,
2683
3103
  buildToolHandlers,
2684
- agentToMcpServer
3104
+ agentToMcpServer,
3105
+ InMemoryAgentMemoryStore
2685
3106
  };
@@ -0,0 +1,45 @@
1
+ import { createRequire } from "node:module";
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true,
8
+ configurable: true,
9
+ set: (newValue) => all[name] = () => newValue
10
+ });
11
+ };
12
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
13
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
14
+
15
+ // src/tools/memory-tools.ts
16
+ import { anthropic } from "@ai-sdk/anthropic";
17
+ function createAnthropicMemoryTool(store) {
18
+ const memory = anthropic.tools.memory_20250818({
19
+ execute: async (action) => {
20
+ switch (action.command) {
21
+ case "view":
22
+ return store.view(action.path ?? "/memories", action.view_range);
23
+ case "create":
24
+ return store.create(action.path ?? "/memories/untitled", action.file_text ?? "");
25
+ case "str_replace":
26
+ return store.strReplace(action.path ?? "/memories", action.old_str ?? "", action.new_str ?? "");
27
+ case "insert":
28
+ return store.insert(action.path ?? "/memories", action.insert_line ?? 0, action.insert_text ?? "");
29
+ case "delete":
30
+ return store.delete(action.path ?? "/memories");
31
+ case "rename":
32
+ return store.rename(action.old_path ?? "/memories", action.new_path ?? "/memories");
33
+ default:
34
+ return `Unknown command: ${action.command}`;
35
+ }
36
+ }
37
+ });
38
+ return memory;
39
+ }
40
+ var init_memory_tools = () => {};
41
+ init_memory_tools();
42
+
43
+ export {
44
+ createAnthropicMemoryTool
45
+ };