@vietor/agent-core 0.7.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/LICENSE +21 -0
- package/README.md +849 -0
- package/dist/create-session.d.ts +4 -0
- package/dist/create-session.js +51 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +5 -0
- package/dist/llm/anthropic.d.ts +14 -0
- package/dist/llm/anthropic.js +200 -0
- package/dist/llm/base.d.ts +10 -0
- package/dist/llm/base.js +12 -0
- package/dist/llm/client.d.ts +4 -0
- package/dist/llm/client.js +61 -0
- package/dist/llm/completions.d.ts +8 -0
- package/dist/llm/completions.js +88 -0
- package/dist/llm/messages.d.ts +50 -0
- package/dist/llm/messages.js +28 -0
- package/dist/llm/responses.d.ts +14 -0
- package/dist/llm/responses.js +130 -0
- package/dist/llm/types.d.ts +40 -0
- package/dist/llm/types.js +1 -0
- package/dist/mcp/client.d.ts +15 -0
- package/dist/mcp/client.js +53 -0
- package/dist/mcp/manager.d.ts +18 -0
- package/dist/mcp/manager.js +155 -0
- package/dist/mcp/types.d.ts +26 -0
- package/dist/mcp/types.js +1 -0
- package/dist/runtime/agent.d.ts +56 -0
- package/dist/runtime/agent.js +253 -0
- package/dist/runtime/events.d.ts +69 -0
- package/dist/runtime/events.js +1 -0
- package/dist/runtime/prompts.d.ts +5 -0
- package/dist/runtime/prompts.js +45 -0
- package/dist/runtime/session-messages.d.ts +43 -0
- package/dist/runtime/session-messages.js +174 -0
- package/dist/runtime/session.d.ts +102 -0
- package/dist/runtime/session.js +375 -0
- package/dist/runtime/sub-agent-runner.d.ts +18 -0
- package/dist/runtime/sub-agent-runner.js +26 -0
- package/dist/runtime/timeline.d.ts +21 -0
- package/dist/runtime/timeline.js +146 -0
- package/dist/runtime/todo-store.d.ts +8 -0
- package/dist/runtime/todo-store.js +15 -0
- package/dist/skills/loader.d.ts +6 -0
- package/dist/skills/loader.js +43 -0
- package/dist/tools/ask-user.d.ts +3 -0
- package/dist/tools/ask-user.js +26 -0
- package/dist/tools/file-edit.d.ts +2 -0
- package/dist/tools/file-edit.js +43 -0
- package/dist/tools/file-read.d.ts +2 -0
- package/dist/tools/file-read.js +93 -0
- package/dist/tools/file-write.d.ts +2 -0
- package/dist/tools/file-write.js +25 -0
- package/dist/tools/glob.d.ts +2 -0
- package/dist/tools/glob.js +31 -0
- package/dist/tools/grep.d.ts +2 -0
- package/dist/tools/grep.js +67 -0
- package/dist/tools/registry.d.ts +30 -0
- package/dist/tools/registry.js +107 -0
- package/dist/tools/shell.d.ts +2 -0
- package/dist/tools/shell.js +57 -0
- package/dist/tools/skill.d.ts +3 -0
- package/dist/tools/skill.js +30 -0
- package/dist/tools/sub-agent.d.ts +7 -0
- package/dist/tools/sub-agent.js +81 -0
- package/dist/tools/todo-write.d.ts +3 -0
- package/dist/tools/todo-write.js +72 -0
- package/dist/tools/types.d.ts +32 -0
- package/dist/tools/types.js +4 -0
- package/dist/tools/web-fetch.d.ts +2 -0
- package/dist/tools/web-fetch.js +104 -0
- package/dist/util/async.d.ts +19 -0
- package/dist/util/async.js +93 -0
- package/dist/util/constants.d.ts +25 -0
- package/dist/util/constants.js +27 -0
- package/dist/util/emitter.d.ts +5 -0
- package/dist/util/emitter.js +15 -0
- package/dist/util/file.d.ts +3 -0
- package/dist/util/file.js +19 -0
- package/dist/util/html.d.ts +1 -0
- package/dist/util/html.js +14 -0
- package/dist/util/index.d.ts +7 -0
- package/dist/util/index.js +7 -0
- package/dist/util/net.d.ts +1 -0
- package/dist/util/net.js +31 -0
- package/dist/util/ripgrep.d.ts +10 -0
- package/dist/util/ripgrep.js +34 -0
- package/dist/util/subprocess.d.ts +15 -0
- package/dist/util/subprocess.js +113 -0
- package/dist/util/text.d.ts +15 -0
- package/dist/util/text.js +72 -0
- package/package.json +52 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import OpenAI from "openai";
|
|
2
|
+
import { EmptyAssistantMessageError, toText, } from "./messages.js";
|
|
3
|
+
import { BaseAdapter } from "./base.js";
|
|
4
|
+
import { netFetch } from "../util/net.js";
|
|
5
|
+
export class ResponsesAdapter extends BaseAdapter {
|
|
6
|
+
client;
|
|
7
|
+
constructor(config) {
|
|
8
|
+
super(config);
|
|
9
|
+
this.client = new OpenAI({
|
|
10
|
+
apiKey: config.apiKey,
|
|
11
|
+
baseURL: config.baseUrl || undefined,
|
|
12
|
+
maxRetries: 0,
|
|
13
|
+
fetch: netFetch,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
async stream(opts) {
|
|
17
|
+
const { messages, tools, onDelta, onThinking, onUsage, onToolCall, thinking, signal } = opts;
|
|
18
|
+
const useThinking = thinking !== false;
|
|
19
|
+
const params = {
|
|
20
|
+
model: this.model,
|
|
21
|
+
input: toResponsesInput(messages),
|
|
22
|
+
max_output_tokens: this.maxOutputTokens,
|
|
23
|
+
stream: true,
|
|
24
|
+
...(tools.length > 0 && { tools: tools.map(toResponsesTool) }),
|
|
25
|
+
...(useThinking && {
|
|
26
|
+
reasoning: { effort: this.thinkingEffort },
|
|
27
|
+
include: ["reasoning.summary_text"],
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
const stream = await this.client.responses.create(params, { signal });
|
|
31
|
+
let finalResponse;
|
|
32
|
+
for await (const event of stream) {
|
|
33
|
+
switch (event.type) {
|
|
34
|
+
case "response.output_text.delta":
|
|
35
|
+
onDelta?.(event.delta);
|
|
36
|
+
break;
|
|
37
|
+
case "response.refusal.delta":
|
|
38
|
+
onDelta?.(event.delta);
|
|
39
|
+
break;
|
|
40
|
+
case "response.reasoning_summary_text.delta":
|
|
41
|
+
onThinking?.(event.delta);
|
|
42
|
+
break;
|
|
43
|
+
case "response.output_item.added":
|
|
44
|
+
if (event.item.type === "function_call")
|
|
45
|
+
onToolCall?.();
|
|
46
|
+
break;
|
|
47
|
+
case "response.completed":
|
|
48
|
+
case "response.failed":
|
|
49
|
+
finalResponse = event.response;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (!finalResponse)
|
|
54
|
+
throw new EmptyAssistantMessageError();
|
|
55
|
+
if (finalResponse.status === "failed") {
|
|
56
|
+
const detail = finalResponse.error
|
|
57
|
+
? `${finalResponse.error.code}: ${finalResponse.error.message}`
|
|
58
|
+
: "unknown error";
|
|
59
|
+
throw new Error(`Responses API error: ${detail}`);
|
|
60
|
+
}
|
|
61
|
+
if (finalResponse.usage) {
|
|
62
|
+
onUsage?.(finalResponse.usage.input_tokens, finalResponse.usage.output_tokens);
|
|
63
|
+
}
|
|
64
|
+
const textParts = [];
|
|
65
|
+
const toolCalls = [];
|
|
66
|
+
for (const item of finalResponse.output) {
|
|
67
|
+
if (item.type === "message") {
|
|
68
|
+
for (const part of item.content) {
|
|
69
|
+
if (part.type === "output_text")
|
|
70
|
+
textParts.push(part.text);
|
|
71
|
+
else if (part.type === "refusal")
|
|
72
|
+
textParts.push(part.refusal);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else if (item.type === "function_call") {
|
|
76
|
+
toolCalls.push({
|
|
77
|
+
id: item.call_id,
|
|
78
|
+
type: "function",
|
|
79
|
+
function: { name: item.name, arguments: item.arguments },
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const content = textParts.join("") || null;
|
|
84
|
+
const message = { role: "assistant", content };
|
|
85
|
+
if (toolCalls.length)
|
|
86
|
+
message.tool_calls = toolCalls;
|
|
87
|
+
if (!content && !toolCalls.length) {
|
|
88
|
+
throw new EmptyAssistantMessageError();
|
|
89
|
+
}
|
|
90
|
+
return message;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export function toResponsesTool(schema) {
|
|
94
|
+
return {
|
|
95
|
+
type: "function",
|
|
96
|
+
name: schema.function.name,
|
|
97
|
+
description: schema.function.description,
|
|
98
|
+
parameters: schema.function.parameters,
|
|
99
|
+
strict: false,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export function toResponsesInput(messages) {
|
|
103
|
+
const items = [];
|
|
104
|
+
for (const m of messages) {
|
|
105
|
+
if (m.role === "system") {
|
|
106
|
+
const text = toText(m.content);
|
|
107
|
+
if (text)
|
|
108
|
+
items.push({ type: "message", role: "system", content: [{ type: "input_text", text }] });
|
|
109
|
+
}
|
|
110
|
+
else if (m.role === "user") {
|
|
111
|
+
const text = toText(m.content);
|
|
112
|
+
if (text)
|
|
113
|
+
items.push({ type: "message", role: "user", content: [{ type: "input_text", text }] });
|
|
114
|
+
}
|
|
115
|
+
else if (m.role === "tool") {
|
|
116
|
+
items.push({ type: "function_call_output", call_id: m.tool_call_id, output: m.content });
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
const text = toText(m.content);
|
|
120
|
+
if (text)
|
|
121
|
+
items.push({ type: "message", role: "assistant", content: [{ type: "input_text", text }] });
|
|
122
|
+
if (m.tool_calls) {
|
|
123
|
+
for (const tc of m.tool_calls) {
|
|
124
|
+
items.push({ type: "function_call", call_id: tc.id, name: tc.function.name, arguments: tc.function.arguments });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return items;
|
|
130
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ToolSchema } from "../tools/types.js";
|
|
2
|
+
import type { LLMAssistantMessage, LLMMessage } from "./messages.js";
|
|
3
|
+
export type LLMThinkingEffort = "high" | "max";
|
|
4
|
+
export type LLMBackend = "completions" | "anthropic" | "responses";
|
|
5
|
+
export interface LLMConfig {
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
apiKey: string;
|
|
8
|
+
model: string;
|
|
9
|
+
thinkingEffort?: LLMThinkingEffort;
|
|
10
|
+
backend?: LLMBackend;
|
|
11
|
+
maxInputTokens?: number;
|
|
12
|
+
maxOutputTokens?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface ResolvedLLMConfig extends LLMConfig {
|
|
15
|
+
thinkingEffort: LLMThinkingEffort;
|
|
16
|
+
backend: LLMBackend;
|
|
17
|
+
maxInputTokens: number;
|
|
18
|
+
maxOutputTokens: number;
|
|
19
|
+
}
|
|
20
|
+
export interface ChatOptions {
|
|
21
|
+
messages: LLMMessage[];
|
|
22
|
+
tools: ToolSchema[];
|
|
23
|
+
onDelta?: (text: string) => void;
|
|
24
|
+
onThinking?: (text: string) => void;
|
|
25
|
+
onRetry?: (attempt: number, max: number, error: unknown) => void;
|
|
26
|
+
onUsage?: (inputTokens: number, outputTokens: number) => void;
|
|
27
|
+
onToolCall?: () => void;
|
|
28
|
+
thinking?: boolean;
|
|
29
|
+
signal?: AbortSignal;
|
|
30
|
+
}
|
|
31
|
+
export interface LLMClient {
|
|
32
|
+
readonly model: string;
|
|
33
|
+
readonly thinkingEffort: LLMThinkingEffort;
|
|
34
|
+
readonly maxInputTokens: number;
|
|
35
|
+
readonly maxOutputTokens: number;
|
|
36
|
+
chat(opts: ChatOptions): Promise<LLMAssistantMessage>;
|
|
37
|
+
}
|
|
38
|
+
export interface Adapter extends Omit<LLMClient, "chat"> {
|
|
39
|
+
stream(opts: ChatOptions): Promise<LLMAssistantMessage>;
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MCPClientInfo } from "./types.js";
|
|
2
|
+
import type { MCPServerConfig } from "./types.js";
|
|
3
|
+
import type { CallToolResult, Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
export declare class MCPClient {
|
|
5
|
+
private client;
|
|
6
|
+
private transport;
|
|
7
|
+
private connectReject?;
|
|
8
|
+
private closing;
|
|
9
|
+
onClosed?: (error?: string) => void;
|
|
10
|
+
constructor(config: MCPServerConfig, clientInfo: MCPClientInfo);
|
|
11
|
+
connect(): Promise<void>;
|
|
12
|
+
listTools(): Promise<Tool[]>;
|
|
13
|
+
callTool(name: string, args: Record<string, unknown>, signal?: AbortSignal): Promise<CallToolResult>;
|
|
14
|
+
kill(): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { AbortedError } from "../util/async.js";
|
|
2
|
+
import { killProcessTree } from "../util/subprocess.js";
|
|
3
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
4
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
5
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
6
|
+
export class MCPClient {
|
|
7
|
+
client;
|
|
8
|
+
transport;
|
|
9
|
+
connectReject;
|
|
10
|
+
closing = false;
|
|
11
|
+
onClosed;
|
|
12
|
+
constructor(config, clientInfo) {
|
|
13
|
+
this.client = new Client(clientInfo, { capabilities: {} });
|
|
14
|
+
if (config.type === "stdio") {
|
|
15
|
+
this.transport = new StdioClientTransport({ ...config, stderr: "ignore" });
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
const opts = { requestInit: { headers: config.headers } };
|
|
19
|
+
const url = new URL(config.url);
|
|
20
|
+
this.transport = new StreamableHTTPClientTransport(url, opts);
|
|
21
|
+
}
|
|
22
|
+
this.transport.onerror = (e) => { if (!this.closing)
|
|
23
|
+
this.onClosed?.(e.message); };
|
|
24
|
+
this.transport.onclose = () => { if (!this.closing)
|
|
25
|
+
this.onClosed?.(); };
|
|
26
|
+
}
|
|
27
|
+
async connect() {
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
this.connectReject = reject;
|
|
30
|
+
this.client
|
|
31
|
+
.connect(this.transport)
|
|
32
|
+
.then(resolve, reject)
|
|
33
|
+
.finally(() => {
|
|
34
|
+
this.connectReject = undefined;
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
async listTools() {
|
|
39
|
+
return this.client.listTools().then((r) => r.tools);
|
|
40
|
+
}
|
|
41
|
+
async callTool(name, args, signal) {
|
|
42
|
+
return this.client.callTool({ name, arguments: args }, undefined, { signal });
|
|
43
|
+
}
|
|
44
|
+
kill() {
|
|
45
|
+
this.closing = true;
|
|
46
|
+
this.connectReject?.(new AbortedError());
|
|
47
|
+
this.connectReject = undefined;
|
|
48
|
+
this.client.close().catch(() => { });
|
|
49
|
+
if (this.transport instanceof StdioClientTransport) {
|
|
50
|
+
killProcessTree(this.transport.pid);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ToolRegistry } from "../tools/registry.js";
|
|
2
|
+
import type { MCPClientInfo, MCPServerConfig, MCPServerInfo } from "./types.js";
|
|
3
|
+
export declare class MCPServerManager {
|
|
4
|
+
private tools;
|
|
5
|
+
private clientInfo;
|
|
6
|
+
private servers;
|
|
7
|
+
private pending;
|
|
8
|
+
private disposed;
|
|
9
|
+
constructor(tools: ToolRegistry, clientInfo: MCPClientInfo);
|
|
10
|
+
connect(mcpServers?: Record<string, MCPServerConfig>): Promise<void>;
|
|
11
|
+
private connectServer;
|
|
12
|
+
private markFailed;
|
|
13
|
+
private unregisterServerTools;
|
|
14
|
+
private handleServerClosed;
|
|
15
|
+
private adapt;
|
|
16
|
+
list(): MCPServerInfo[];
|
|
17
|
+
kill(): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { toolError } from "../tools/types.js";
|
|
2
|
+
import { MCPClient } from "./client.js";
|
|
3
|
+
import { withTimeout, withTimeoutFn } from "../util/async.js";
|
|
4
|
+
import { CALL_TIMEOUT_MS, MCP_CONNECT_TIMEOUT_MS, NO_OUTPUT } from "../util/constants.js";
|
|
5
|
+
import { toErrorMessage } from "../util/text.js";
|
|
6
|
+
const SUMMARY_PRIORITY = ["url", "path", "file_path", "filePath", "command", "query", "pattern", "name", "text", "selector", "uid"];
|
|
7
|
+
function isStringProp(v) {
|
|
8
|
+
return typeof v === "object" && v !== null && v.type === "string";
|
|
9
|
+
}
|
|
10
|
+
function summaryCandidates(inputSchema) {
|
|
11
|
+
const props = inputSchema.properties;
|
|
12
|
+
if (!props)
|
|
13
|
+
return [];
|
|
14
|
+
const candidates = [];
|
|
15
|
+
for (const k of SUMMARY_PRIORITY)
|
|
16
|
+
if (k in props)
|
|
17
|
+
candidates.push(k);
|
|
18
|
+
const required = inputSchema.required;
|
|
19
|
+
if (Array.isArray(required)) {
|
|
20
|
+
for (const k of required) {
|
|
21
|
+
if (typeof k === "string" && !candidates.includes(k) && isStringProp(props[k]))
|
|
22
|
+
candidates.push(k);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
for (const [k, v] of Object.entries(props)) {
|
|
26
|
+
if (!candidates.includes(k) && isStringProp(v))
|
|
27
|
+
candidates.push(k);
|
|
28
|
+
}
|
|
29
|
+
return candidates;
|
|
30
|
+
}
|
|
31
|
+
function extractContent(result) {
|
|
32
|
+
const parts = [];
|
|
33
|
+
for (const c of result.content) {
|
|
34
|
+
switch (c.type) {
|
|
35
|
+
case "text":
|
|
36
|
+
parts.push(c.text);
|
|
37
|
+
break;
|
|
38
|
+
case "image":
|
|
39
|
+
parts.push(`[image: ${c.mimeType}]`);
|
|
40
|
+
break;
|
|
41
|
+
case "audio":
|
|
42
|
+
parts.push(`[audio: ${c.mimeType}]`);
|
|
43
|
+
break;
|
|
44
|
+
case "resource": {
|
|
45
|
+
const r = c.resource;
|
|
46
|
+
parts.push("text" in r ? r.text : `[resource: ${r.uri}]`);
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
default:
|
|
50
|
+
parts.push(`[${c.type}]`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (result.structuredContent) {
|
|
54
|
+
parts.push(`<structured>${JSON.stringify(result.structuredContent)}</structured>`);
|
|
55
|
+
}
|
|
56
|
+
return parts.join("\n");
|
|
57
|
+
}
|
|
58
|
+
function mcpToolName(server, tool) {
|
|
59
|
+
return `MCP__${server}__${tool}`;
|
|
60
|
+
}
|
|
61
|
+
export class MCPServerManager {
|
|
62
|
+
tools;
|
|
63
|
+
clientInfo;
|
|
64
|
+
servers = new Map();
|
|
65
|
+
pending = new Set();
|
|
66
|
+
disposed = false;
|
|
67
|
+
constructor(tools, clientInfo) {
|
|
68
|
+
this.tools = tools;
|
|
69
|
+
this.clientInfo = clientInfo;
|
|
70
|
+
}
|
|
71
|
+
async connect(mcpServers = {}) {
|
|
72
|
+
await Promise.all(Object.entries(mcpServers).map(([name, cfg]) => this.connectServer(name, cfg)));
|
|
73
|
+
}
|
|
74
|
+
async connectServer(name, cfg) {
|
|
75
|
+
if (this.disposed)
|
|
76
|
+
return;
|
|
77
|
+
const type = cfg.type;
|
|
78
|
+
if (cfg.enabled === false) {
|
|
79
|
+
this.servers.set(name, { type, status: "disabled", tools: [] });
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
this.servers.set(name, { type, status: "pending", tools: [] });
|
|
83
|
+
let client;
|
|
84
|
+
try {
|
|
85
|
+
client = new MCPClient(cfg, this.clientInfo);
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
this.markFailed(name, type, toErrorMessage(e));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
this.pending.add(client);
|
|
92
|
+
try {
|
|
93
|
+
await withTimeout(client.connect(), MCP_CONNECT_TIMEOUT_MS);
|
|
94
|
+
if (this.disposed)
|
|
95
|
+
return;
|
|
96
|
+
const mcpTools = await withTimeout(client.listTools(), MCP_CONNECT_TIMEOUT_MS);
|
|
97
|
+
if (this.disposed)
|
|
98
|
+
return;
|
|
99
|
+
this.servers.set(name, { type, status: "connected", client, tools: mcpTools.map((t) => t.name) });
|
|
100
|
+
this.tools.registerAll(mcpTools.map((t) => this.adapt(name, client, t)));
|
|
101
|
+
client.onClosed = (error) => this.handleServerClosed(name, client, error);
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
client.kill();
|
|
105
|
+
if (!this.disposed) {
|
|
106
|
+
this.markFailed(name, type, toErrorMessage(e));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
this.pending.delete(client);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
markFailed(name, type, error) {
|
|
114
|
+
this.servers.set(name, { type, status: "failed", tools: [], error });
|
|
115
|
+
}
|
|
116
|
+
unregisterServerTools(name, tools) {
|
|
117
|
+
for (const t of tools)
|
|
118
|
+
this.tools.unregister(mcpToolName(name, t));
|
|
119
|
+
}
|
|
120
|
+
handleServerClosed(name, client, error) {
|
|
121
|
+
const entry = this.servers.get(name);
|
|
122
|
+
if (!entry || entry.client !== client || entry.status !== "connected")
|
|
123
|
+
return;
|
|
124
|
+
this.unregisterServerTools(name, entry.tools);
|
|
125
|
+
this.markFailed(name, entry.type, error ?? "MCP server connection closed");
|
|
126
|
+
}
|
|
127
|
+
adapt(server, client, tool) {
|
|
128
|
+
const summaryKeys = summaryCandidates(tool.inputSchema);
|
|
129
|
+
return {
|
|
130
|
+
name: mcpToolName(server, tool.name),
|
|
131
|
+
description: tool.description ?? `${server} ${tool.name}`,
|
|
132
|
+
parameters: tool.inputSchema,
|
|
133
|
+
...(summaryKeys.length ? { summaryKeys } : {}),
|
|
134
|
+
async execute(args, ctx) {
|
|
135
|
+
const result = await withTimeoutFn((signal) => client.callTool(tool.name, args, signal), CALL_TIMEOUT_MS, ctx.signal, `MCP tool call timed out (${CALL_TIMEOUT_MS / 1000}s)`);
|
|
136
|
+
const text = extractContent(result);
|
|
137
|
+
return result.isError
|
|
138
|
+
? toolError(text)
|
|
139
|
+
: { content: text || NO_OUTPUT };
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
list() {
|
|
144
|
+
return [...this.servers.entries()].map(([name, s]) => ({ name, type: s.type, status: s.status, tools: s.tools, error: s.error }));
|
|
145
|
+
}
|
|
146
|
+
kill() {
|
|
147
|
+
this.disposed = true;
|
|
148
|
+
for (const { client } of this.servers.values())
|
|
149
|
+
client?.kill();
|
|
150
|
+
for (const client of this.pending)
|
|
151
|
+
client.kill();
|
|
152
|
+
this.servers.clear();
|
|
153
|
+
this.pending.clear();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface MCPClientInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
version: string;
|
|
4
|
+
}
|
|
5
|
+
export type ServerType = "stdio" | "http";
|
|
6
|
+
export type MCPServerConfig = StdioServerConfig | HttpServerConfig;
|
|
7
|
+
export interface StdioServerConfig {
|
|
8
|
+
type: "stdio";
|
|
9
|
+
command: string;
|
|
10
|
+
args?: string[];
|
|
11
|
+
env?: Record<string, string>;
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface HttpServerConfig {
|
|
15
|
+
type: "http";
|
|
16
|
+
url: string;
|
|
17
|
+
headers?: Record<string, string>;
|
|
18
|
+
enabled?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface MCPServerInfo {
|
|
21
|
+
name: string;
|
|
22
|
+
type: ServerType;
|
|
23
|
+
status: "pending" | "connected" | "failed" | "disabled";
|
|
24
|
+
tools: string[];
|
|
25
|
+
error?: string;
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { LLMClient } from "../llm/types.js";
|
|
2
|
+
import { SessionMessages, type SessionMessage } from "./session-messages.js";
|
|
3
|
+
import type { SessionEvent } from "./events.js";
|
|
4
|
+
import type { Skill } from "../skills/loader.js";
|
|
5
|
+
import type { ToolRegistry } from "../tools/registry.js";
|
|
6
|
+
import type { Todo } from "../tools/types.js";
|
|
7
|
+
export type RunStatus = "ok" | "aborted" | "error" | "stalled" | "maxTurns";
|
|
8
|
+
export interface AgentOptions {
|
|
9
|
+
llm: LLMClient;
|
|
10
|
+
conversation: SessionMessages;
|
|
11
|
+
tools: ToolRegistry;
|
|
12
|
+
cwd: string;
|
|
13
|
+
setTodos: (todos: Todo[]) => void;
|
|
14
|
+
getTodos: () => readonly Todo[];
|
|
15
|
+
stallThreshold: number;
|
|
16
|
+
maxTurns: number;
|
|
17
|
+
contextLimit: number;
|
|
18
|
+
resolveSkill?: (name: string) => Skill | undefined;
|
|
19
|
+
onCompact?: () => void;
|
|
20
|
+
}
|
|
21
|
+
export declare class Agent {
|
|
22
|
+
private llm;
|
|
23
|
+
private conversation;
|
|
24
|
+
private tools;
|
|
25
|
+
private cwd;
|
|
26
|
+
private setTodos;
|
|
27
|
+
private getTodos;
|
|
28
|
+
private stallThreshold;
|
|
29
|
+
private maxTurns;
|
|
30
|
+
readonly contextLimit: number;
|
|
31
|
+
private todoSnapshot;
|
|
32
|
+
private resolveSkill?;
|
|
33
|
+
private onCompact?;
|
|
34
|
+
private inputTokens;
|
|
35
|
+
private outputTokens;
|
|
36
|
+
constructor(opts: AgentOptions);
|
|
37
|
+
get contextTokens(): number;
|
|
38
|
+
get usage(): {
|
|
39
|
+
inputTokens: number;
|
|
40
|
+
outputTokens: number;
|
|
41
|
+
};
|
|
42
|
+
resetUsage(): void;
|
|
43
|
+
get model(): string;
|
|
44
|
+
get thinkingEffort(): import("../llm/types.js").LLMThinkingEffort;
|
|
45
|
+
clear(): void;
|
|
46
|
+
export(): SessionMessage[];
|
|
47
|
+
compact(onEvent?: (e: SessionEvent) => void, signal?: AbortSignal): Promise<RunStatus>;
|
|
48
|
+
run(userInput: string, onEvent?: (e: SessionEvent) => void, signal?: AbortSignal): Promise<RunStatus>;
|
|
49
|
+
runSkill(skill: Skill, onEvent?: (e: SessionEvent) => void, signal?: AbortSignal): Promise<RunStatus>;
|
|
50
|
+
private runTurn;
|
|
51
|
+
private loop;
|
|
52
|
+
private resolvePendingToolCalls;
|
|
53
|
+
private chatOnce;
|
|
54
|
+
private runToolCalls;
|
|
55
|
+
private executeToolCall;
|
|
56
|
+
}
|