@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
@@ -2156,6 +2156,17 @@ function defineAgent(spec) {
2156
2156
  }));
2157
2157
  }
2158
2158
  toolNames.add(tool.name);
2159
+ if (tool.subagentRef && tool.operationRef) {
2160
+ throw new Error(`Agent ${spec.meta.key} tool "${tool.name}" cannot have both subagentRef and operationRef. Use one.`);
2161
+ }
2162
+ const outputRefCount = [
2163
+ tool.outputPresentation,
2164
+ tool.outputForm,
2165
+ tool.outputDataView
2166
+ ].filter(Boolean).length;
2167
+ if (outputRefCount > 1) {
2168
+ throw new Error(`Agent ${spec.meta.key} tool "${tool.name}" has multiple output refs (outputPresentation, outputForm, outputDataView). Use at most one.`);
2169
+ }
2159
2170
  }
2160
2171
  return Object.freeze(spec);
2161
2172
  }
@@ -2299,15 +2310,124 @@ function jsonSchemaToZodSafe(schema) {
2299
2310
  }
2300
2311
  var init_json_schema_to_zod = () => {};
2301
2312
 
2313
+ // src/tools/operation-tool-handler.ts
2314
+ function toolCtxToHandlerCtx(ctx) {
2315
+ return {
2316
+ traceId: ctx.metadata?.traceId,
2317
+ organizationId: ctx.tenantId ?? null,
2318
+ userId: ctx.actorId ?? null,
2319
+ actor: ctx.actorId ? "user" : "anonymous",
2320
+ channel: "agent",
2321
+ roles: []
2322
+ };
2323
+ }
2324
+ function createOperationToolHandler(registry, operationRef) {
2325
+ return async (input, context) => {
2326
+ const handlerCtx = toolCtxToHandlerCtx(context);
2327
+ const result = await registry.execute(operationRef.key, operationRef.version, input ?? {}, handlerCtx);
2328
+ return result;
2329
+ };
2330
+ }
2331
+
2332
+ // src/tools/subagent-tool.ts
2333
+ import { readUIMessageStream, tool } from "ai";
2334
+ import { z as z2 } from "zod";
2335
+ function toReadableStream(iterable) {
2336
+ if (iterable instanceof ReadableStream) {
2337
+ return iterable;
2338
+ }
2339
+ return new ReadableStream({
2340
+ async start(controller) {
2341
+ try {
2342
+ for await (const chunk of iterable) {
2343
+ controller.enqueue(chunk);
2344
+ }
2345
+ } finally {
2346
+ controller.close();
2347
+ }
2348
+ }
2349
+ });
2350
+ }
2351
+ function createSubagentTool(options) {
2352
+ const {
2353
+ subagent,
2354
+ description = "Research a topic or question in depth.",
2355
+ taskParam = "task",
2356
+ toModelSummary = true,
2357
+ passConversationHistory = false
2358
+ } = options;
2359
+ const inputSchema = z2.object({
2360
+ [taskParam]: z2.string().describe("The research task to complete")
2361
+ });
2362
+ const execute = async function* (input, options2) {
2363
+ const task = String(input[taskParam] ?? input.task ?? "");
2364
+ const { abortSignal, messages } = options2 ?? {};
2365
+ if (passConversationHistory && messages && messages.length > 0 && typeof subagent.generate === "function") {
2366
+ const result2 = await subagent.generate({
2367
+ messages: [...messages, { role: "user", content: task }],
2368
+ abortSignal
2369
+ });
2370
+ yield { parts: [{ type: "text", text: result2.text }] };
2371
+ return;
2372
+ }
2373
+ const result = await subagent.stream({
2374
+ prompt: task,
2375
+ abortSignal
2376
+ });
2377
+ const uiStream = result.toUIMessageStream();
2378
+ const stream = toReadableStream(uiStream);
2379
+ for await (const message of readUIMessageStream({
2380
+ stream
2381
+ })) {
2382
+ yield message;
2383
+ }
2384
+ };
2385
+ const toolOptions = {
2386
+ description,
2387
+ inputSchema,
2388
+ execute,
2389
+ ...toModelSummary && {
2390
+ toModelOutput: ({
2391
+ output
2392
+ }) => {
2393
+ const parts = output?.parts;
2394
+ if (!Array.isArray(parts)) {
2395
+ return { type: "text", value: "Task completed." };
2396
+ }
2397
+ const lastTextPart = [...parts].reverse().find((p) => p?.type === "text");
2398
+ return {
2399
+ type: "text",
2400
+ value: lastTextPart?.text ?? "Task completed."
2401
+ };
2402
+ }
2403
+ }
2404
+ };
2405
+ return tool(toolOptions);
2406
+ }
2407
+ var init_subagent_tool = () => {};
2408
+
2302
2409
  // src/tools/tool-adapter.ts
2303
- import { tool } from "ai";
2304
- function specToolToAISDKTool(specTool, handler, context = {}) {
2410
+ import { tool as tool2 } from "ai";
2411
+ function isAsyncGenerator(value) {
2412
+ return typeof value === "object" && value !== null && typeof value.next === "function" && typeof value[Symbol.asyncIterator] === "function";
2413
+ }
2414
+ function specToolToAISDKTool(specTool, handler, context = {}, effectiveInputSchema, operationSpec) {
2305
2415
  let lastInvocationAt;
2306
- return tool({
2416
+ const inputSchema = effectiveInputSchema ?? jsonSchemaToZodSafe(specTool.schema);
2417
+ const buildContext = (signal) => ({
2418
+ agentId: context.agentId ?? "unknown",
2419
+ sessionId: context.sessionId ?? "unknown",
2420
+ tenantId: context.tenantId,
2421
+ actorId: context.actorId,
2422
+ locale: context.locale,
2423
+ metadata: context.metadata,
2424
+ signal
2425
+ });
2426
+ return tool2({
2307
2427
  description: specTool.description ?? specTool.name,
2308
- inputSchema: jsonSchemaToZodSafe(specTool.schema),
2428
+ inputSchema,
2309
2429
  needsApproval: specTool.requiresApproval ?? !specTool.automationSafe,
2310
- execute: async (input) => {
2430
+ execute: async function* (input, options) {
2311
2431
  const now = Date.now();
2312
2432
  const cooldownMs = normalizeDuration(specTool.cooldownMs);
2313
2433
  if (cooldownMs && lastInvocationAt !== undefined) {
@@ -2318,19 +2438,20 @@ function specToolToAISDKTool(specTool, handler, context = {}) {
2318
2438
  }
2319
2439
  }
2320
2440
  const timeoutMs = normalizeDuration(specTool.timeoutMs);
2321
- const { signal, dispose } = createTimeoutSignal(context.signal, timeoutMs);
2441
+ const signal = options?.abortSignal ?? context.signal;
2442
+ const { signal: timeoutSignal, dispose } = createTimeoutSignal(signal, timeoutMs);
2322
2443
  try {
2323
- const execution = handler(input, {
2324
- agentId: context.agentId ?? "unknown",
2325
- sessionId: context.sessionId ?? "unknown",
2326
- tenantId: context.tenantId,
2327
- actorId: context.actorId,
2328
- locale: context.locale,
2329
- metadata: context.metadata,
2330
- signal
2331
- });
2332
- const result = timeoutMs ? await withTimeout(execution, timeoutMs, specTool.name) : await execution;
2333
- return typeof result === "string" ? result : JSON.stringify(result);
2444
+ const execution = handler(input, buildContext(timeoutSignal));
2445
+ if (isAsyncGenerator(execution)) {
2446
+ for await (const raw of execution) {
2447
+ const wrapped = wrapToolOutputForRendering(specTool, raw, operationSpec);
2448
+ yield typeof wrapped === "string" ? wrapped : wrapped;
2449
+ }
2450
+ } else {
2451
+ const raw = timeoutMs ? await withTimeout(Promise.resolve(execution), timeoutMs, specTool.name) : await Promise.resolve(execution);
2452
+ const wrapped = wrapToolOutputForRendering(specTool, raw, operationSpec);
2453
+ yield typeof wrapped === "string" ? wrapped : wrapped;
2454
+ }
2334
2455
  } finally {
2335
2456
  dispose();
2336
2457
  lastInvocationAt = Date.now();
@@ -2338,27 +2459,83 @@ function specToolToAISDKTool(specTool, handler, context = {}) {
2338
2459
  }
2339
2460
  });
2340
2461
  }
2341
- function specToolsToAISDKTools(specTools, handlers, context = {}) {
2462
+ function specToolsToAISDKTools(specTools, handlers, context = {}, options) {
2342
2463
  const tools = {};
2343
2464
  for (const specTool of specTools) {
2344
- const handler = handlers.get(specTool.name);
2345
- if (!handler) {
2346
- throw new Error(createAgentI18n(context.locale).t("error.missingToolHandler", {
2347
- name: specTool.name
2348
- }));
2465
+ if (specTool.subagentRef && options?.subagentRegistry) {
2466
+ const subagent = options.subagentRegistry.get(specTool.subagentRef.agentId);
2467
+ if (!subagent) {
2468
+ throw new Error(`Subagent not found: ${specTool.subagentRef.agentId}. Register it in subagentRegistry.`);
2469
+ }
2470
+ if (specTool.requiresApproval === true || specTool.automationSafe === false) {
2471
+ 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`);
2472
+ }
2473
+ tools[specTool.name] = createSubagentTool({
2474
+ subagent,
2475
+ description: specTool.description ?? specTool.name,
2476
+ toModelSummary: specTool.subagentRef.toModelSummary ?? true,
2477
+ passConversationHistory: specTool.subagentRef.passConversationHistory
2478
+ });
2479
+ continue;
2349
2480
  }
2350
- tools[specTool.name] = specToolToAISDKTool(specTool, handler, context);
2481
+ let handler;
2482
+ let effectiveInputSchema;
2483
+ let op;
2484
+ if (specTool.operationRef && options?.operationRegistry) {
2485
+ op = options.operationRegistry.get(specTool.operationRef.key, specTool.operationRef.version);
2486
+ if (!op) {
2487
+ throw new Error(`Operation not found: ${specTool.operationRef.key}${specTool.operationRef.version ? `.v${specTool.operationRef.version}` : ""}`);
2488
+ }
2489
+ handler = createOperationToolHandler(options.operationRegistry, specTool.operationRef);
2490
+ effectiveInputSchema = op.io.input?.getZod?.();
2491
+ } else {
2492
+ const manualHandler = handlers.get(specTool.name);
2493
+ if (!manualHandler) {
2494
+ if (specTool.subagentRef) {
2495
+ throw new Error(`Subagent tool "${specTool.name}" requires subagentRegistry. Pass subagentRegistry in ContractSpecAgentConfig.`);
2496
+ }
2497
+ throw new Error(createAgentI18n(context.locale).t("error.missingToolHandler", {
2498
+ name: specTool.name
2499
+ }));
2500
+ }
2501
+ handler = manualHandler;
2502
+ }
2503
+ tools[specTool.name] = specToolToAISDKTool(specTool, handler, context, effectiveInputSchema, op);
2351
2504
  }
2352
2505
  return tools;
2353
2506
  }
2354
2507
  function createToolHandler(handler) {
2355
- return async (input, context) => {
2508
+ return (input, context) => {
2356
2509
  return handler(input, context);
2357
2510
  };
2358
2511
  }
2359
2512
  function buildToolHandlers(handlersObj) {
2360
2513
  return new Map(Object.entries(handlersObj));
2361
2514
  }
2515
+ function wrapToolOutputForRendering(specTool, result, operationSpec) {
2516
+ const presentation = specTool.outputPresentation ?? operationSpec?.outputPresentation;
2517
+ const form = specTool.outputForm ?? operationSpec?.outputForm;
2518
+ const dataView = specTool.outputDataView ?? operationSpec?.outputDataView;
2519
+ if (presentation) {
2520
+ return {
2521
+ presentationKey: presentation.key,
2522
+ data: result
2523
+ };
2524
+ }
2525
+ if (form) {
2526
+ return {
2527
+ formKey: form.key,
2528
+ defaultValues: typeof result === "object" && result !== null ? result : {}
2529
+ };
2530
+ }
2531
+ if (dataView) {
2532
+ return {
2533
+ dataViewKey: dataView.key,
2534
+ items: Array.isArray(result) ? result : result != null ? [result] : []
2535
+ };
2536
+ }
2537
+ return result;
2538
+ }
2362
2539
  function normalizeDuration(value) {
2363
2540
  if (value === undefined) {
2364
2541
  return;
@@ -2420,11 +2597,12 @@ function createToolExecutionError(message, code, retryAfterMs) {
2420
2597
  var init_tool_adapter = __esm(() => {
2421
2598
  init_json_schema_to_zod();
2422
2599
  init_i18n();
2600
+ init_subagent_tool();
2423
2601
  });
2424
2602
 
2425
2603
  // src/tools/knowledge-tool.ts
2426
- import { tool as tool2 } from "ai";
2427
- import * as z2 from "zod";
2604
+ import { tool as tool3 } from "ai";
2605
+ import * as z3 from "zod";
2428
2606
  function createKnowledgeQueryTool(retriever, knowledgeRefs, locale) {
2429
2607
  const i18n = createAgentI18n(locale);
2430
2608
  const optionalSpaces = knowledgeRefs.filter((k) => !k.required).map((k) => k.key).filter((key) => retriever.supportsSpace(key));
@@ -2433,15 +2611,15 @@ function createKnowledgeQueryTool(retriever, knowledgeRefs, locale) {
2433
2611
  }
2434
2612
  const spaceDescriptions = knowledgeRefs.filter((k) => !k.required && retriever.supportsSpace(k.key)).map((k) => `- ${k.key}: ${k.instructions ?? i18n.t("tool.knowledge.spaceDefault")}`).join(`
2435
2613
  `);
2436
- return tool2({
2614
+ return tool3({
2437
2615
  description: `${i18n.t("tool.knowledge.description")}
2438
2616
 
2439
2617
  ${i18n.t("tool.knowledge.availableSpaces")}
2440
2618
  ${spaceDescriptions}`,
2441
- inputSchema: z2.object({
2442
- query: z2.string().describe(i18n.t("tool.knowledge.param.query")),
2443
- spaceKey: z2.enum(optionalSpaces).optional().describe(i18n.t("tool.knowledge.param.spaceKey")),
2444
- topK: z2.number().optional().default(5).describe(i18n.t("tool.knowledge.param.topK"))
2619
+ inputSchema: z3.object({
2620
+ query: z3.string().describe(i18n.t("tool.knowledge.param.query")),
2621
+ spaceKey: z3.enum(optionalSpaces).optional().describe(i18n.t("tool.knowledge.param.spaceKey")),
2622
+ topK: z3.number().optional().default(5).describe(i18n.t("tool.knowledge.param.topK"))
2445
2623
  }),
2446
2624
  execute: async ({ query, spaceKey, topK }) => {
2447
2625
  const spacesToSearch = spaceKey ? [spaceKey] : optionalSpaces;
@@ -2481,6 +2659,33 @@ var init_knowledge_tool = __esm(() => {
2481
2659
  init_i18n();
2482
2660
  });
2483
2661
 
2662
+ // src/tools/memory-tools.ts
2663
+ import { anthropic } from "@ai-sdk/anthropic";
2664
+ function createAnthropicMemoryTool(store) {
2665
+ const memory = anthropic.tools.memory_20250818({
2666
+ execute: async (action) => {
2667
+ switch (action.command) {
2668
+ case "view":
2669
+ return store.view(action.path ?? "/memories", action.view_range);
2670
+ case "create":
2671
+ return store.create(action.path ?? "/memories/untitled", action.file_text ?? "");
2672
+ case "str_replace":
2673
+ return store.strReplace(action.path ?? "/memories", action.old_str ?? "", action.new_str ?? "");
2674
+ case "insert":
2675
+ return store.insert(action.path ?? "/memories", action.insert_line ?? 0, action.insert_text ?? "");
2676
+ case "delete":
2677
+ return store.delete(action.path ?? "/memories");
2678
+ case "rename":
2679
+ return store.rename(action.old_path ?? "/memories", action.new_path ?? "/memories");
2680
+ default:
2681
+ return `Unknown command: ${action.command}`;
2682
+ }
2683
+ }
2684
+ });
2685
+ return memory;
2686
+ }
2687
+ var init_memory_tools = () => {};
2688
+
2484
2689
  // src/tools/mcp-client-helpers.ts
2485
2690
  import {
2486
2691
  Experimental_StdioMCPTransport as StdioClientTransport
@@ -2511,8 +2716,8 @@ function prefixToolNames(config, tools) {
2511
2716
  return tools;
2512
2717
  }
2513
2718
  const prefixedTools = {};
2514
- for (const [toolName, tool3] of Object.entries(tools)) {
2515
- prefixedTools[`${prefix}_${toolName}`] = tool3;
2719
+ for (const [toolName, tool4] of Object.entries(tools)) {
2720
+ prefixedTools[`${prefix}_${toolName}`] = tool4;
2516
2721
  }
2517
2722
  return prefixedTools;
2518
2723
  }
@@ -2618,12 +2823,12 @@ async function createMcpToolsets(configs, options = {}) {
2618
2823
  for (const [serverName, toolNames] of Object.entries(result.serverToolNames)) {
2619
2824
  serverToolNames[serverName] = toolNames;
2620
2825
  }
2621
- for (const [toolName, tool3] of Object.entries(result.tools)) {
2826
+ for (const [toolName, tool4] of Object.entries(result.tools)) {
2622
2827
  const hasCollision = combinedTools[toolName] !== undefined;
2623
2828
  if (hasCollision && collisionStrategy === "error") {
2624
2829
  throw new Error(`Duplicate MCP tool name "${toolName}" detected. Use "toolPrefix" or set onNameCollision to "overwrite".`);
2625
2830
  }
2626
- combinedTools[toolName] = tool3;
2831
+ combinedTools[toolName] = tool4;
2627
2832
  }
2628
2833
  }
2629
2834
  } catch (error) {
@@ -3146,7 +3351,7 @@ import {
3146
3351
  stepCountIs
3147
3352
  } from "ai";
3148
3353
  import { randomUUID } from "node:crypto";
3149
- import * as z3 from "zod";
3354
+ import * as z4 from "zod";
3150
3355
 
3151
3356
  class ContractSpecAgent {
3152
3357
  version = "agent-v1";
@@ -3175,12 +3380,19 @@ class ContractSpecAgent {
3175
3380
  }
3176
3381
  try {
3177
3382
  const instructions = await injectStaticKnowledge(effectiveConfig.spec.instructions, effectiveConfig.spec.knowledge ?? [], effectiveConfig.knowledgeRetriever);
3178
- const specTools = specToolsToAISDKTools(effectiveConfig.spec.tools, effectiveConfig.toolHandlers, { agentId: agentKey(effectiveConfig.spec.meta) });
3383
+ const specTools = specToolsToAISDKTools(effectiveConfig.spec.tools, effectiveConfig.toolHandlers, { agentId: agentKey(effectiveConfig.spec.meta) }, {
3384
+ operationRegistry: effectiveConfig.operationRegistry,
3385
+ subagentRegistry: effectiveConfig.subagentRegistry
3386
+ });
3179
3387
  const knowledgeTool = effectiveConfig.knowledgeRetriever ? createKnowledgeQueryTool(effectiveConfig.knowledgeRetriever, effectiveConfig.spec.knowledge ?? []) : null;
3388
+ const memoryTool = effectiveConfig.spec.memoryTools?.provider === "anthropic" && effectiveConfig.agentMemoryStore ? createAnthropicMemoryTool(effectiveConfig.agentMemoryStore) : null;
3180
3389
  const reservedToolNames = new Set(Object.keys(specTools));
3181
3390
  if (knowledgeTool) {
3182
3391
  reservedToolNames.add("query_knowledge");
3183
3392
  }
3393
+ if (memoryTool) {
3394
+ reservedToolNames.add("memory");
3395
+ }
3184
3396
  const conflictingMcpTools = Object.keys(mcpToolset?.tools ?? {}).filter((toolName) => reservedToolNames.has(toolName));
3185
3397
  if (conflictingMcpTools.length > 0) {
3186
3398
  throw new Error(`MCP tools conflict with agent tools: ${conflictingMcpTools.join(", ")}. Configure MCP toolPrefix values to avoid collisions.`);
@@ -3188,6 +3400,7 @@ class ContractSpecAgent {
3188
3400
  const tools = {
3189
3401
  ...specTools,
3190
3402
  ...knowledgeTool ? { query_knowledge: knowledgeTool } : {},
3403
+ ...memoryTool ? { memory: memoryTool } : {},
3191
3404
  ...mcpToolset?.tools ?? {},
3192
3405
  ...effectiveConfig.additionalTools ?? {}
3193
3406
  };
@@ -3219,7 +3432,7 @@ class ContractSpecAgent {
3219
3432
  stepIndex: 0,
3220
3433
  stepStartedAt: new Date
3221
3434
  });
3222
- if (this.config.sessionStore) {
3435
+ if (this.config.sessionStore && !params.messages?.length) {
3223
3436
  const existing = await this.config.sessionStore.get(sessionId);
3224
3437
  if (!existing) {
3225
3438
  await this.config.sessionStore.create({
@@ -3237,14 +3450,9 @@ class ContractSpecAgent {
3237
3450
  }
3238
3451
  await this.config.sessionStore.appendMessage(sessionId, {
3239
3452
  role: "user",
3240
- content: params.prompt
3453
+ content: params.prompt ?? ""
3241
3454
  });
3242
3455
  }
3243
- const prompt = params.systemOverride ? `${this.instructions}
3244
-
3245
- ${params.systemOverride}
3246
-
3247
- ${params.prompt}` : params.prompt;
3248
3456
  const model = await this.resolveModelForCall({
3249
3457
  sessionId,
3250
3458
  traceId,
@@ -3252,18 +3460,33 @@ ${params.prompt}` : params.prompt;
3252
3460
  });
3253
3461
  const effectiveMaxSteps = resolveMaxSteps(params.maxSteps, this.spec.maxSteps);
3254
3462
  const inner = this.createInnerAgent(model, effectiveMaxSteps);
3463
+ const generateOptions = {
3464
+ abortSignal: params.signal,
3465
+ options: {
3466
+ tenantId: params.options?.tenantId,
3467
+ actorId: params.options?.actorId,
3468
+ sessionId,
3469
+ metadata: params.options?.metadata
3470
+ }
3471
+ };
3255
3472
  let result;
3256
3473
  try {
3257
- result = await inner.generate({
3258
- prompt,
3259
- abortSignal: params.signal,
3260
- options: {
3261
- tenantId: params.options?.tenantId,
3262
- actorId: params.options?.actorId,
3263
- sessionId,
3264
- metadata: params.options?.metadata
3265
- }
3266
- });
3474
+ if (params.messages && params.messages.length > 0) {
3475
+ result = await inner.generate({
3476
+ messages: params.messages,
3477
+ ...generateOptions
3478
+ });
3479
+ } else {
3480
+ const prompt = params.systemOverride && params.prompt ? `${this.instructions}
3481
+
3482
+ ${params.systemOverride}
3483
+
3484
+ ${params.prompt}` : params.prompt ?? "";
3485
+ result = await inner.generate({
3486
+ prompt,
3487
+ ...generateOptions
3488
+ });
3489
+ }
3267
3490
  } catch (error) {
3268
3491
  if (this.config.sessionStore) {
3269
3492
  await this.config.sessionStore.update(sessionId, {
@@ -3323,11 +3546,11 @@ ${params.prompt}` : params.prompt;
3323
3546
  stepIndex: 0,
3324
3547
  stepStartedAt: new Date
3325
3548
  });
3326
- const prompt = params.systemOverride ? `${this.instructions}
3549
+ const prompt = params.systemOverride && params.prompt ? `${this.instructions}
3327
3550
 
3328
3551
  ${params.systemOverride}
3329
3552
 
3330
- ${params.prompt}` : params.prompt;
3553
+ ${params.prompt}` : params.prompt ?? "";
3331
3554
  const model = await this.resolveModelForCall({
3332
3555
  sessionId,
3333
3556
  traceId,
@@ -3351,7 +3574,7 @@ ${params.prompt}` : params.prompt;
3351
3574
  }
3352
3575
  await this.config.sessionStore.appendMessage(sessionId, {
3353
3576
  role: "user",
3354
- content: params.prompt
3577
+ content: prompt
3355
3578
  });
3356
3579
  await this.config.sessionStore.update(sessionId, { status: "running" });
3357
3580
  }
@@ -3482,14 +3705,15 @@ var init_contract_spec_agent = __esm(() => {
3482
3705
  init_spec();
3483
3706
  init_tool_adapter();
3484
3707
  init_knowledge_tool();
3708
+ init_memory_tools();
3485
3709
  init_mcp_client();
3486
3710
  init_injector();
3487
3711
  init_adapter();
3488
- ContractSpecCallOptionsSchema = z3.object({
3489
- tenantId: z3.string().optional(),
3490
- actorId: z3.string().optional(),
3491
- sessionId: z3.string().optional(),
3492
- metadata: z3.record(z3.string(), z3.unknown()).optional()
3712
+ ContractSpecCallOptionsSchema = z4.object({
3713
+ tenantId: z4.string().optional(),
3714
+ actorId: z4.string().optional(),
3715
+ sessionId: z4.string().optional(),
3716
+ metadata: z4.record(z4.string(), z4.unknown()).optional()
3493
3717
  });
3494
3718
  });
3495
3719
 
@@ -3534,12 +3758,12 @@ var init_types = __esm(() => {
3534
3758
  });
3535
3759
 
3536
3760
  // src/providers/claude-agent-sdk/tool-bridge.ts
3537
- function specToolToClaudeAgentTool(tool3, handler, context) {
3761
+ function specToolToClaudeAgentTool(tool4, handler, context) {
3538
3762
  return {
3539
- name: tool3.name,
3540
- description: tool3.description ?? createAgentI18n().t("tool.fallbackDescription", { name: tool3.name }),
3541
- input_schema: normalizeSchema(tool3.schema),
3542
- requires_confirmation: tool3.requiresApproval ?? !tool3.automationSafe,
3763
+ name: tool4.name,
3764
+ description: tool4.description ?? createAgentI18n().t("tool.fallbackDescription", { name: tool4.name }),
3765
+ input_schema: normalizeSchema(tool4.schema),
3766
+ requires_confirmation: tool4.requiresApproval ?? !tool4.automationSafe,
3543
3767
  execute: async (input) => {
3544
3768
  const fullContext = {
3545
3769
  agentId: context.agentId ?? "unknown",
@@ -3554,14 +3778,14 @@ function specToolToClaudeAgentTool(tool3, handler, context) {
3554
3778
  };
3555
3779
  }
3556
3780
  function specToolsToClaudeAgentTools(tools, handlers, context) {
3557
- return tools.filter((tool3) => handlers.has(tool3.name)).map((tool3) => {
3558
- const handler = handlers.get(tool3.name);
3781
+ return tools.filter((tool4) => handlers.has(tool4.name)).map((tool4) => {
3782
+ const handler = handlers.get(tool4.name);
3559
3783
  if (!handler) {
3560
3784
  throw new Error(createAgentI18n().t("error.handlerNotFoundForTool", {
3561
- name: tool3.name
3785
+ name: tool4.name
3562
3786
  }));
3563
3787
  }
3564
- return specToolToClaudeAgentTool(tool3, handler, context);
3788
+ return specToolToClaudeAgentTool(tool4, handler, context);
3565
3789
  });
3566
3790
  }
3567
3791
  function claudeAgentToolToSpecTool(claudeTool, execute) {
@@ -3587,12 +3811,12 @@ function claudeAgentToolsToSpecTools(claudeTools) {
3587
3811
  }
3588
3812
  return { configs, handlers };
3589
3813
  }
3590
- function specToolToExternalTool(tool3, handler, context) {
3814
+ function specToolToExternalTool(tool4, handler, context) {
3591
3815
  return {
3592
- name: tool3.name,
3593
- description: tool3.description ?? createAgentI18n().t("tool.fallbackDescription", { name: tool3.name }),
3594
- inputSchema: tool3.schema ?? { type: "object" },
3595
- requiresApproval: tool3.requiresApproval ?? !tool3.automationSafe,
3816
+ name: tool4.name,
3817
+ description: tool4.description ?? createAgentI18n().t("tool.fallbackDescription", { name: tool4.name }),
3818
+ inputSchema: tool4.schema ?? { type: "object" },
3819
+ requiresApproval: tool4.requiresApproval ?? !tool4.automationSafe,
3596
3820
  execute: handler ? async (input) => {
3597
3821
  const fullContext = {
3598
3822
  agentId: context?.agentId ?? "unknown",
@@ -3793,8 +4017,8 @@ class ClaudeAgentSDKProvider {
3793
4017
  let mcpToolset = null;
3794
4018
  try {
3795
4019
  const toolSet = {};
3796
- for (const tool3 of spec.tools) {
3797
- toolSet[tool3.name] = specToolToExternalTool(tool3);
4020
+ for (const tool4 of spec.tools) {
4021
+ toolSet[tool4.name] = specToolToExternalTool(tool4);
3798
4022
  }
3799
4023
  if ((this.config.mcpServers?.length ?? 0) > 0) {
3800
4024
  mcpToolset = await createMcpToolsets(this.config.mcpServers ?? [], {
@@ -4008,20 +4232,20 @@ ${params.systemOverride}` : context.spec.instructions;
4008
4232
  }
4009
4233
  return toolsForSdk;
4010
4234
  }
4011
- mcpToolToExternalTool(toolName, tool3) {
4235
+ mcpToolToExternalTool(toolName, tool4) {
4012
4236
  return {
4013
4237
  name: toolName,
4014
- description: tool3.description ?? createAgentI18n(this.config.locale).t("tool.fallbackDescription", {
4238
+ description: tool4.description ?? createAgentI18n(this.config.locale).t("tool.fallbackDescription", {
4015
4239
  name: toolName
4016
4240
  }),
4017
- inputSchema: this.normalizeExternalInputSchema(tool3.inputSchema),
4241
+ inputSchema: this.normalizeExternalInputSchema(tool4.inputSchema),
4018
4242
  execute: async (input) => {
4019
- if (!tool3.execute) {
4243
+ if (!tool4.execute) {
4020
4244
  throw new Error(createAgentI18n(this.config.locale).t("error.toolNoExecuteHandler", {
4021
4245
  name: toolName
4022
4246
  }));
4023
4247
  }
4024
- return tool3.execute(input, {
4248
+ return tool4.execute(input, {
4025
4249
  toolCallId: `mcp-${randomUUID2()}`,
4026
4250
  messages: []
4027
4251
  });
@@ -4061,8 +4285,8 @@ ${params.systemOverride}` : context.spec.instructions;
4061
4285
  return typeof value === "object" && value !== null;
4062
4286
  }
4063
4287
  async executeTool(toolCall, context, _params) {
4064
- const tool3 = context.tools[toolCall.toolName];
4065
- if (!tool3?.execute) {
4288
+ const tool4 = context.tools[toolCall.toolName];
4289
+ if (!tool4?.execute) {
4066
4290
  return {
4067
4291
  toolCallId: toolCall.toolCallId,
4068
4292
  toolName: toolCall.toolName,
@@ -4072,7 +4296,7 @@ ${params.systemOverride}` : context.spec.instructions;
4072
4296
  };
4073
4297
  }
4074
4298
  try {
4075
- const output = await tool3.execute(toolCall.args);
4299
+ const output = await tool4.execute(toolCall.args);
4076
4300
  return {
4077
4301
  toolCallId: toolCall.toolCallId,
4078
4302
  toolName: toolCall.toolName,
@@ -4149,22 +4373,22 @@ var init_claude_agent_sdk = __esm(() => {
4149
4373
  });
4150
4374
 
4151
4375
  // src/providers/opencode-sdk/tool-bridge.ts
4152
- function specToolToOpenCodeTool(tool3) {
4376
+ function specToolToOpenCodeTool(tool4) {
4153
4377
  return {
4154
- name: tool3.name,
4155
- description: tool3.description ?? createAgentI18n().t("tool.fallbackDescription", { name: tool3.name }),
4156
- parameters: normalizeToOpenCodeParameters(tool3.schema),
4157
- permission: getPermissionLevel(tool3)
4378
+ name: tool4.name,
4379
+ description: tool4.description ?? createAgentI18n().t("tool.fallbackDescription", { name: tool4.name }),
4380
+ parameters: normalizeToOpenCodeParameters(tool4.schema),
4381
+ permission: getPermissionLevel(tool4)
4158
4382
  };
4159
4383
  }
4160
4384
  function specToolsToOpenCodeTools(tools) {
4161
4385
  return tools.map(specToolToOpenCodeTool);
4162
4386
  }
4163
- function getPermissionLevel(tool3) {
4164
- if (tool3.requiresApproval) {
4387
+ function getPermissionLevel(tool4) {
4388
+ if (tool4.requiresApproval) {
4165
4389
  return "ask";
4166
4390
  }
4167
- if (tool3.automationSafe === false) {
4391
+ if (tool4.automationSafe === false) {
4168
4392
  return "ask";
4169
4393
  }
4170
4394
  return "allow";
@@ -4181,12 +4405,12 @@ function openCodeToolToSpecTool(openCodeTool) {
4181
4405
  function openCodeToolsToSpecTools(openCodeTools) {
4182
4406
  return openCodeTools.map(openCodeToolToSpecTool);
4183
4407
  }
4184
- function specToolToExternalToolForOpenCode(tool3, handler, context) {
4408
+ function specToolToExternalToolForOpenCode(tool4, handler, context) {
4185
4409
  return {
4186
- name: tool3.name,
4187
- description: tool3.description ?? createAgentI18n().t("tool.fallbackDescription", { name: tool3.name }),
4188
- inputSchema: tool3.schema ?? { type: "object" },
4189
- requiresApproval: tool3.requiresApproval ?? !tool3.automationSafe,
4410
+ name: tool4.name,
4411
+ description: tool4.description ?? createAgentI18n().t("tool.fallbackDescription", { name: tool4.name }),
4412
+ inputSchema: tool4.schema ?? { type: "object" },
4413
+ requiresApproval: tool4.requiresApproval ?? !tool4.automationSafe,
4190
4414
  execute: handler ? async (input) => {
4191
4415
  const fullContext = {
4192
4416
  agentId: context?.agentId ?? "unknown",
@@ -4236,9 +4460,9 @@ function convertToOpenCodeParameter(schema) {
4236
4460
  }
4237
4461
  function createToolHandlerMap(tools) {
4238
4462
  const handlers = new Map;
4239
- for (const [name, tool3] of Object.entries(tools)) {
4240
- if (tool3.execute) {
4241
- handlers.set(name, tool3.execute);
4463
+ for (const [name, tool4] of Object.entries(tools)) {
4464
+ if (tool4.execute) {
4465
+ handlers.set(name, tool4.execute);
4242
4466
  }
4243
4467
  }
4244
4468
  return handlers;
@@ -4274,7 +4498,7 @@ var init_tool_bridge2 = __esm(() => {
4274
4498
 
4275
4499
  // src/providers/opencode-sdk/agent-bridge.ts
4276
4500
  function inferAgentType(spec) {
4277
- const hasCodeTools = spec.tools.some((tool3) => [
4501
+ const hasCodeTools = spec.tools.some((tool4) => [
4278
4502
  "write_file",
4279
4503
  "edit_file",
4280
4504
  "create_file",
@@ -4283,7 +4507,7 @@ function inferAgentType(spec) {
4283
4507
  "execute",
4284
4508
  "run_command",
4285
4509
  "terminal"
4286
- ].includes(tool3.name.toLowerCase()));
4510
+ ].includes(tool4.name.toLowerCase()));
4287
4511
  const instructionsLower = spec.instructions.toLowerCase();
4288
4512
  const hasPlanningKeywords = /\b(plan|design|architect|strategy|analyze|review|assess|evaluate)\b/.test(instructionsLower);
4289
4513
  const hasExplorationKeywords = /\b(search|find|explore|discover|locate|grep|pattern)\b/.test(instructionsLower);
@@ -4318,13 +4542,13 @@ function specToOpenCodeConfig(spec, options) {
4318
4542
  }
4319
4543
  function buildPermissions(spec) {
4320
4544
  const permissions = {};
4321
- for (const tool3 of spec.tools) {
4322
- if (tool3.requiresApproval) {
4323
- permissions[tool3.name] = "ask";
4324
- } else if (tool3.automationSafe === false) {
4325
- permissions[tool3.name] = "ask";
4545
+ for (const tool4 of spec.tools) {
4546
+ if (tool4.requiresApproval) {
4547
+ permissions[tool4.name] = "ask";
4548
+ } else if (tool4.automationSafe === false) {
4549
+ permissions[tool4.name] = "ask";
4326
4550
  } else {
4327
- permissions[tool3.name] = "allow";
4551
+ permissions[tool4.name] = "allow";
4328
4552
  }
4329
4553
  }
4330
4554
  return permissions;
@@ -4360,9 +4584,9 @@ function buildMarkdownBody(spec) {
4360
4584
  if (spec.tools.length > 0) {
4361
4585
  lines.push(i18n.t("export.tools"));
4362
4586
  lines.push("");
4363
- for (const tool3 of spec.tools) {
4364
- const permission = tool3.requiresApproval ? i18n.t("export.bridge.requiresApproval") : tool3.automationSafe === false ? i18n.t("export.bridge.askMode") : "";
4365
- lines.push(`- **${tool3.name}**: ${tool3.description ?? i18n.t("export.noDescription")} ${permission}`.trim());
4587
+ for (const tool4 of spec.tools) {
4588
+ const permission = tool4.requiresApproval ? i18n.t("export.bridge.requiresApproval") : tool4.automationSafe === false ? i18n.t("export.bridge.askMode") : "";
4589
+ lines.push(`- **${tool4.name}**: ${tool4.description ?? i18n.t("export.noDescription")} ${permission}`.trim());
4366
4590
  }
4367
4591
  lines.push("");
4368
4592
  }
@@ -4397,8 +4621,8 @@ function serializeOpenCodeMarkdown(markdown) {
4397
4621
  }
4398
4622
  if (markdown.frontmatter.tools && markdown.frontmatter.tools.length > 0) {
4399
4623
  lines.push(`tools:`);
4400
- for (const tool3 of markdown.frontmatter.tools) {
4401
- lines.push(` - ${tool3}`);
4624
+ for (const tool4 of markdown.frontmatter.tools) {
4625
+ lines.push(` - ${tool4}`);
4402
4626
  }
4403
4627
  }
4404
4628
  lines.push("---");
@@ -4419,12 +4643,12 @@ function openCodeConfigToSpec(config) {
4419
4643
  },
4420
4644
  description: config.description,
4421
4645
  instructions: config.instructions ?? "",
4422
- tools: config.tools?.map((tool3) => ({
4423
- name: tool3.name,
4424
- description: tool3.description,
4425
- schema: tool3.parameters,
4426
- requiresApproval: tool3.permission === "ask",
4427
- automationSafe: tool3.permission === "allow"
4646
+ tools: config.tools?.map((tool4) => ({
4647
+ name: tool4.name,
4648
+ description: tool4.description,
4649
+ schema: tool4.parameters,
4650
+ requiresApproval: tool4.permission === "ask",
4651
+ automationSafe: tool4.permission === "allow"
4428
4652
  })) ?? [],
4429
4653
  maxSteps: config.config?.max_steps ?? 10
4430
4654
  };
@@ -4481,8 +4705,8 @@ class OpenCodeSDKProvider {
4481
4705
  model: this.config.model
4482
4706
  });
4483
4707
  const toolSet = {};
4484
- for (const tool3 of spec.tools) {
4485
- toolSet[tool3.name] = specToolToExternalToolForOpenCode(tool3);
4708
+ for (const tool4 of spec.tools) {
4709
+ toolSet[tool4.name] = specToolToExternalToolForOpenCode(tool4);
4486
4710
  }
4487
4711
  const instructions = await injectStaticKnowledge(spec.instructions, spec.knowledge ?? [], undefined);
4488
4712
  const contextId = `opencode-${agentKey(spec.meta)}-${Date.now()}`;
@@ -4914,8 +5138,8 @@ class UnifiedAgent {
4914
5138
  const { createProvider } = await import("@contractspec/lib.ai-providers/factory");
4915
5139
  model = createProvider(backendConfig.provider).getModel();
4916
5140
  } else {
4917
- const { anthropic } = await import("@ai-sdk/anthropic");
4918
- model = anthropic(backendConfig?.model ?? "claude-3-5-sonnet-20240620");
5141
+ const { anthropic: anthropic2 } = await import("@ai-sdk/anthropic");
5142
+ model = anthropic2(backendConfig?.model ?? "claude-3-5-sonnet-20240620");
4919
5143
  }
4920
5144
  return this.applyModelSettings(model, {
4921
5145
  temperature: backendConfig?.temperature,