@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.
- package/README.md +41 -0
- package/dist/agent/agent-factory.d.ts +13 -0
- package/dist/agent/agent-factory.js +290 -63
- package/dist/agent/contract-spec-agent.d.ts +9 -0
- package/dist/agent/contract-spec-agent.js +287 -63
- package/dist/agent/index.js +353 -129
- package/dist/agent/json-runner.js +290 -66
- package/dist/agent/unified-agent.js +350 -126
- package/dist/exporters/claude-agent-exporter.js +12 -1
- package/dist/exporters/index.js +12 -1
- package/dist/exporters/opencode-exporter.js +11 -0
- package/dist/index.js +11 -0
- package/dist/interop/index.js +24 -2
- package/dist/interop/spec-consumer.js +11 -0
- package/dist/interop/tool-consumer.js +13 -2
- package/dist/node/agent/agent-factory.js +290 -63
- package/dist/node/agent/contract-spec-agent.js +287 -63
- package/dist/node/agent/index.js +353 -129
- package/dist/node/agent/json-runner.js +290 -66
- package/dist/node/agent/unified-agent.js +350 -126
- package/dist/node/exporters/claude-agent-exporter.js +12 -1
- package/dist/node/exporters/index.js +12 -1
- package/dist/node/exporters/opencode-exporter.js +11 -0
- package/dist/node/index.js +11 -0
- package/dist/node/interop/index.js +24 -2
- package/dist/node/interop/spec-consumer.js +11 -0
- package/dist/node/interop/tool-consumer.js +13 -2
- package/dist/node/providers/claude-agent-sdk/adapter.js +11 -0
- package/dist/node/providers/claude-agent-sdk/index.js +11 -0
- package/dist/node/providers/index.js +11 -0
- package/dist/node/providers/opencode-sdk/adapter.js +11 -0
- package/dist/node/providers/opencode-sdk/index.js +11 -0
- package/dist/node/spec/index.js +11 -0
- package/dist/node/spec/spec.js +11 -0
- package/dist/node/tools/agent-memory-store.js +24 -0
- package/dist/node/tools/in-memory-agent-memory-store.js +236 -0
- package/dist/node/tools/index.js +463 -42
- package/dist/node/tools/memory-tools.js +45 -0
- package/dist/node/tools/operation-tool-handler.js +35 -0
- package/dist/node/tools/subagent-tool.js +95 -0
- package/dist/node/tools/tool-adapter.js +192 -25
- package/dist/providers/claude-agent-sdk/adapter.js +11 -0
- package/dist/providers/claude-agent-sdk/index.js +11 -0
- package/dist/providers/index.js +11 -0
- package/dist/providers/opencode-sdk/adapter.js +11 -0
- package/dist/providers/opencode-sdk/index.js +11 -0
- package/dist/spec/index.js +11 -0
- package/dist/spec/spec.d.ts +69 -1
- package/dist/spec/spec.js +11 -0
- package/dist/tools/agent-memory-store.d.ts +26 -0
- package/dist/tools/agent-memory-store.js +24 -0
- package/dist/tools/agent-memory-store.test.d.ts +1 -0
- package/dist/tools/in-memory-agent-memory-store.d.ts +18 -0
- package/dist/tools/in-memory-agent-memory-store.js +236 -0
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.js +463 -42
- package/dist/tools/mcp-client.browser.d.ts +6 -6
- package/dist/tools/memory-tools.d.ts +29 -0
- package/dist/tools/memory-tools.js +45 -0
- package/dist/tools/operation-tool-handler.d.ts +24 -0
- package/dist/tools/operation-tool-handler.js +35 -0
- package/dist/tools/subagent-tool.d.ts +66 -0
- package/dist/tools/subagent-tool.js +95 -0
- package/dist/tools/tool-adapter.d.ts +26 -10
- package/dist/tools/tool-adapter.js +192 -25
- package/dist/types.d.ts +9 -3
- package/package.json +67 -7
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory tools for AI agents — Anthropic memory and custom operation-backed tools.
|
|
3
|
+
*
|
|
4
|
+
* @see https://ai-sdk.dev/docs/agents/memory
|
|
5
|
+
* @see https://console.anthropic.com/docs/en/agents-and-tools/tool-use/memory-tool
|
|
6
|
+
*/
|
|
7
|
+
import type { Tool } from 'ai';
|
|
8
|
+
import type { AgentMemoryStore } from './agent-memory-store';
|
|
9
|
+
/** Action shape from Anthropic memory_20250818 tool. */
|
|
10
|
+
export interface AnthropicMemoryAction {
|
|
11
|
+
command: 'view' | 'create' | 'str_replace' | 'insert' | 'delete' | 'rename';
|
|
12
|
+
path?: string;
|
|
13
|
+
view_range?: [number, number];
|
|
14
|
+
file_text?: string;
|
|
15
|
+
old_str?: string;
|
|
16
|
+
new_str?: string;
|
|
17
|
+
insert_line?: number;
|
|
18
|
+
insert_text?: string;
|
|
19
|
+
old_path?: string;
|
|
20
|
+
new_path?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Creates the Anthropic memory tool backed by an AgentMemoryStore.
|
|
24
|
+
* Use when provider is Anthropic and agent needs persistent memory.
|
|
25
|
+
*
|
|
26
|
+
* @param store - Storage backend (e.g. InMemoryAgentMemoryStore for dev)
|
|
27
|
+
* @returns AI SDK tool for use with ToolLoopAgent
|
|
28
|
+
*/
|
|
29
|
+
export declare function createAnthropicMemoryTool(store: AgentMemoryStore): Tool;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// @bun
|
|
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 = import.meta.require;
|
|
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
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operation-as-tool: creates a ToolHandler that executes a ContractSpec operation.
|
|
3
|
+
*
|
|
4
|
+
* One contract → REST, GraphQL, MCP, agent tool. When an agent tool has
|
|
5
|
+
* operationRef, this module provides the handler that delegates to
|
|
6
|
+
* OperationSpecRegistry.execute.
|
|
7
|
+
*/
|
|
8
|
+
import type { OperationSpecRegistry } from '@contractspec/lib.contracts-spec/operations/registry';
|
|
9
|
+
import type { OperationRef } from '../spec/spec';
|
|
10
|
+
import type { ToolHandler } from '../types';
|
|
11
|
+
/**
|
|
12
|
+
* Create a ToolHandler that executes a ContractSpec operation.
|
|
13
|
+
*
|
|
14
|
+
* @param registry - OperationSpecRegistry with the operation registered and handler bound
|
|
15
|
+
* @param operationRef - Reference to the operation (key, optional version)
|
|
16
|
+
* @returns ToolHandler that delegates to registry.execute
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const handler = createOperationToolHandler(opsRegistry, { key: 'knowledge.search', version: '1.0.0' });
|
|
21
|
+
* toolHandlers.set('search_knowledge', handler);
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function createOperationToolHandler(registry: OperationSpecRegistry, operationRef: OperationRef): ToolHandler;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// @bun
|
|
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 = import.meta.require;
|
|
14
|
+
|
|
15
|
+
// src/tools/operation-tool-handler.ts
|
|
16
|
+
function toolCtxToHandlerCtx(ctx) {
|
|
17
|
+
return {
|
|
18
|
+
traceId: ctx.metadata?.traceId,
|
|
19
|
+
organizationId: ctx.tenantId ?? null,
|
|
20
|
+
userId: ctx.actorId ?? null,
|
|
21
|
+
actor: ctx.actorId ? "user" : "anonymous",
|
|
22
|
+
channel: "agent",
|
|
23
|
+
roles: []
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function createOperationToolHandler(registry, operationRef) {
|
|
27
|
+
return async (input, context) => {
|
|
28
|
+
const handlerCtx = toolCtxToHandlerCtx(context);
|
|
29
|
+
const result = await registry.execute(operationRef.key, operationRef.version, input ?? {}, handlerCtx);
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
createOperationToolHandler
|
|
35
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subagent tool factory for AI SDK parent-child agent delegation.
|
|
3
|
+
*
|
|
4
|
+
* Creates a tool whose execute calls a subagent, optionally streaming
|
|
5
|
+
* preliminary results and controlling what the model sees via toModelOutput.
|
|
6
|
+
*
|
|
7
|
+
* @see https://ai-sdk.dev/docs/agents/subagents
|
|
8
|
+
*/
|
|
9
|
+
import { type Tool } from 'ai';
|
|
10
|
+
/** Subagent interface compatible with ToolLoopAgent stream. */
|
|
11
|
+
export interface SubagentLike {
|
|
12
|
+
stream(params: {
|
|
13
|
+
prompt: string;
|
|
14
|
+
abortSignal?: AbortSignal;
|
|
15
|
+
}): Promise<{
|
|
16
|
+
toUIMessageStream(): AsyncIterable<unknown> | ReadableStream<unknown>;
|
|
17
|
+
}>;
|
|
18
|
+
/** Optional: for passConversationHistory; when present, used instead of stream when messages are passed */
|
|
19
|
+
generate?(params: {
|
|
20
|
+
messages: {
|
|
21
|
+
role: string;
|
|
22
|
+
content: string | unknown[];
|
|
23
|
+
}[];
|
|
24
|
+
abortSignal?: AbortSignal;
|
|
25
|
+
}): Promise<{
|
|
26
|
+
text: string;
|
|
27
|
+
}>;
|
|
28
|
+
}
|
|
29
|
+
export interface CreateSubagentToolOptions {
|
|
30
|
+
/** Subagent to delegate to (ToolLoopAgent or compatible) */
|
|
31
|
+
subagent: SubagentLike;
|
|
32
|
+
/** Tool name (unique within the parent agent) */
|
|
33
|
+
name?: string;
|
|
34
|
+
/** Human-readable description for the LLM */
|
|
35
|
+
description?: string;
|
|
36
|
+
/** Input parameter name for the task (default: 'task') */
|
|
37
|
+
taskParam?: string;
|
|
38
|
+
/** Whether to extract summary for toModelOutput (default: true) */
|
|
39
|
+
toModelSummary?: boolean;
|
|
40
|
+
/** Pass full conversation history to subagent (opt-in; defeats context isolation; disables streaming) */
|
|
41
|
+
passConversationHistory?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Create an AI SDK tool that delegates to a subagent.
|
|
45
|
+
*
|
|
46
|
+
* Supports streaming preliminary results via async generator execute.
|
|
47
|
+
* When toModelSummary is true, toModelOutput extracts the last text part
|
|
48
|
+
* so the parent model sees only a summary.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* const researchSubagent = new ToolLoopAgent({ model, instructions, tools });
|
|
53
|
+
* const researchTool = createSubagentTool({
|
|
54
|
+
* subagent: researchSubagent,
|
|
55
|
+
* name: 'research',
|
|
56
|
+
* description: 'Research a topic in depth.',
|
|
57
|
+
* });
|
|
58
|
+
* const mainAgent = new ToolLoopAgent({
|
|
59
|
+
* model,
|
|
60
|
+
* tools: { research: researchTool },
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare function createSubagentTool(options: CreateSubagentToolOptions): Tool<{
|
|
65
|
+
task: string;
|
|
66
|
+
}, unknown>;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// @bun
|
|
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 = import.meta.require;
|
|
14
|
+
|
|
15
|
+
// src/tools/subagent-tool.ts
|
|
16
|
+
import { readUIMessageStream, tool } from "ai";
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
function toReadableStream(iterable) {
|
|
19
|
+
if (iterable instanceof ReadableStream) {
|
|
20
|
+
return iterable;
|
|
21
|
+
}
|
|
22
|
+
return new ReadableStream({
|
|
23
|
+
async start(controller) {
|
|
24
|
+
try {
|
|
25
|
+
for await (const chunk of iterable) {
|
|
26
|
+
controller.enqueue(chunk);
|
|
27
|
+
}
|
|
28
|
+
} finally {
|
|
29
|
+
controller.close();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function createSubagentTool(options) {
|
|
35
|
+
const {
|
|
36
|
+
subagent,
|
|
37
|
+
description = "Research a topic or question in depth.",
|
|
38
|
+
taskParam = "task",
|
|
39
|
+
toModelSummary = true,
|
|
40
|
+
passConversationHistory = false
|
|
41
|
+
} = options;
|
|
42
|
+
const inputSchema = z.object({
|
|
43
|
+
[taskParam]: z.string().describe("The research task to complete")
|
|
44
|
+
});
|
|
45
|
+
const execute = async function* (input, options2) {
|
|
46
|
+
const task = String(input[taskParam] ?? input.task ?? "");
|
|
47
|
+
const { abortSignal, messages } = options2 ?? {};
|
|
48
|
+
if (passConversationHistory && messages && messages.length > 0 && typeof subagent.generate === "function") {
|
|
49
|
+
const result2 = await subagent.generate({
|
|
50
|
+
messages: [...messages, { role: "user", content: task }],
|
|
51
|
+
abortSignal
|
|
52
|
+
});
|
|
53
|
+
yield { parts: [{ type: "text", text: result2.text }] };
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const result = await subagent.stream({
|
|
57
|
+
prompt: task,
|
|
58
|
+
abortSignal
|
|
59
|
+
});
|
|
60
|
+
const uiStream = result.toUIMessageStream();
|
|
61
|
+
const stream = toReadableStream(uiStream);
|
|
62
|
+
for await (const message of readUIMessageStream({
|
|
63
|
+
stream
|
|
64
|
+
})) {
|
|
65
|
+
yield message;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const toolOptions = {
|
|
69
|
+
description,
|
|
70
|
+
inputSchema,
|
|
71
|
+
execute,
|
|
72
|
+
...toModelSummary && {
|
|
73
|
+
toModelOutput: ({
|
|
74
|
+
output
|
|
75
|
+
}) => {
|
|
76
|
+
const parts = output?.parts;
|
|
77
|
+
if (!Array.isArray(parts)) {
|
|
78
|
+
return { type: "text", value: "Task completed." };
|
|
79
|
+
}
|
|
80
|
+
const lastTextPart = [...parts].reverse().find((p) => p?.type === "text");
|
|
81
|
+
return {
|
|
82
|
+
type: "text",
|
|
83
|
+
value: lastTextPart?.text ?? "Task completed."
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
return tool(toolOptions);
|
|
89
|
+
}
|
|
90
|
+
var init_subagent_tool = () => {};
|
|
91
|
+
init_subagent_tool();
|
|
92
|
+
|
|
93
|
+
export {
|
|
94
|
+
createSubagentTool
|
|
95
|
+
};
|
|
@@ -1,24 +1,40 @@
|
|
|
1
1
|
import { type Tool } from 'ai';
|
|
2
|
+
import * as z from 'zod';
|
|
3
|
+
import type { OperationSpecRegistry } from '@contractspec/lib.contracts-spec/operations/registry';
|
|
4
|
+
import type { AnyOperationSpec } from '@contractspec/lib.contracts-spec/operations/operation';
|
|
2
5
|
import type { AgentToolConfig } from '../spec/spec';
|
|
3
6
|
import type { ToolExecutionContext, ToolHandler } from '../types';
|
|
7
|
+
import { type SubagentLike } from './subagent-tool';
|
|
8
|
+
export declare function specToolToAISDKTool(specTool: AgentToolConfig, handler: ToolHandler, context?: Partial<ToolExecutionContext>, effectiveInputSchema?: z.ZodType,
|
|
9
|
+
/** Optional operation spec for output ref fallback when specTool has no output refs */
|
|
10
|
+
operationSpec?: AnyOperationSpec): Tool<any, any>;
|
|
4
11
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* @param specTool - The tool configuration from AgentSpec
|
|
8
|
-
* @param handler - The handler function for the tool
|
|
9
|
-
* @param context - Partial context to inject into handler calls
|
|
10
|
-
* @returns AI SDK CoreTool
|
|
12
|
+
* Registry for resolving subagents by agentId.
|
|
11
13
|
*/
|
|
12
|
-
export
|
|
14
|
+
export type SubagentRegistry = Map<string, SubagentLike>;
|
|
15
|
+
/**
|
|
16
|
+
* Options for specToolsToAISDKTools.
|
|
17
|
+
*/
|
|
18
|
+
export interface SpecToolsToAISDKToolsOptions {
|
|
19
|
+
/** Optional OperationSpecRegistry for operation-backed tools (operationRef) */
|
|
20
|
+
operationRegistry?: OperationSpecRegistry;
|
|
21
|
+
/** Optional registry for subagent-backed tools (subagentRef) */
|
|
22
|
+
subagentRegistry?: SubagentRegistry;
|
|
23
|
+
}
|
|
13
24
|
/**
|
|
14
25
|
* Convert multiple ContractSpec tool configs to AI SDK tools.
|
|
15
26
|
*
|
|
27
|
+
* When a tool has operationRef and operationRegistry is provided, the handler
|
|
28
|
+
* and input schema are derived from the operation. Otherwise, handlers must
|
|
29
|
+
* be supplied for each tool.
|
|
30
|
+
*
|
|
16
31
|
* @param specTools - Array of tool configurations
|
|
17
|
-
* @param handlers - Map of tool name to handler function
|
|
32
|
+
* @param handlers - Map of tool name to handler function (for inline tools)
|
|
18
33
|
* @param context - Partial context to inject into handler calls
|
|
34
|
+
* @param options - Optional operationRegistry for operation-backed tools
|
|
19
35
|
* @returns Record of AI SDK tools keyed by name
|
|
20
36
|
*/
|
|
21
|
-
export declare function specToolsToAISDKTools(specTools: AgentToolConfig[], handlers: Map<string, ToolHandler>, context?: Partial<ToolExecutionContext
|
|
37
|
+
export declare function specToolsToAISDKTools(specTools: AgentToolConfig[], handlers: Map<string, ToolHandler>, context?: Partial<ToolExecutionContext>, options?: SpecToolsToAISDKToolsOptions): Record<string, Tool<any, any>>;
|
|
22
38
|
/**
|
|
23
39
|
* Type-safe tool handler builder.
|
|
24
40
|
*
|
|
@@ -29,7 +45,7 @@ export declare function specToolsToAISDKTools(specTools: AgentToolConfig[], hand
|
|
|
29
45
|
* });
|
|
30
46
|
* ```
|
|
31
47
|
*/
|
|
32
|
-
export declare function createToolHandler<TInput = unknown, TOutput = string>(handler: (input: TInput, context: ToolExecutionContext) => Promise<TOutput> | TOutput): ToolHandler<TInput, TOutput>;
|
|
48
|
+
export declare function createToolHandler<TInput = unknown, TOutput = string>(handler: (input: TInput, context: ToolExecutionContext) => Promise<TOutput> | TOutput | AsyncGenerator<TOutput>): ToolHandler<TInput, TOutput>;
|
|
33
49
|
/**
|
|
34
50
|
* Build a tool handlers map from an object.
|
|
35
51
|
*
|
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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
|
|
2391
|
+
const signal = options?.abortSignal ?? context.signal;
|
|
2392
|
+
const { signal: timeoutSignal, dispose } = createTimeoutSignal(signal, timeoutMs);
|
|
2283
2393
|
try {
|
|
2284
|
-
const execution = handler(input,
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
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
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
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
|
-
|
|
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
|
|
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,6 +2547,7 @@ 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
|
init_tool_adapter();
|
|
2386
2553
|
|
package/dist/types.d.ts
CHANGED
|
@@ -90,8 +90,13 @@ export interface AgentSessionState {
|
|
|
90
90
|
metadata?: Record<string, string>;
|
|
91
91
|
}
|
|
92
92
|
export interface AgentGenerateParams {
|
|
93
|
-
/** User prompt
|
|
94
|
-
prompt
|
|
93
|
+
/** User prompt (required when messages is not provided) */
|
|
94
|
+
prompt?: string;
|
|
95
|
+
/** Full conversation history (for subagent passConversationHistory; when set, prompt is ignored) */
|
|
96
|
+
messages?: {
|
|
97
|
+
role: string;
|
|
98
|
+
content: string | unknown[];
|
|
99
|
+
}[];
|
|
95
100
|
/** System prompt override (appended to agent instructions) */
|
|
96
101
|
systemOverride?: string;
|
|
97
102
|
/** Runtime context options */
|
|
@@ -144,8 +149,9 @@ export interface ToolExecutionContext {
|
|
|
144
149
|
}
|
|
145
150
|
/**
|
|
146
151
|
* Handler function for a tool.
|
|
152
|
+
* May return a Promise (single result) or AsyncGenerator (streaming preliminary results).
|
|
147
153
|
*/
|
|
148
|
-
export type ToolHandler<TInput = unknown, TOutput = string> = (input: TInput, context: ToolExecutionContext) => Promise<TOutput>;
|
|
154
|
+
export type ToolHandler<TInput = unknown, TOutput = string> = (input: TInput, context: ToolExecutionContext) => Promise<TOutput> | TOutput | AsyncGenerator<TOutput>;
|
|
149
155
|
export interface AgentStepMetrics {
|
|
150
156
|
agentId: string;
|
|
151
157
|
stepIndex: number;
|