@livx.cc/agentx 0.99.38 → 0.99.40

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.
@@ -1,4 +1,4 @@
1
- import { A as AgentTool } from './tools-Byyh_bae.js';
1
+ import { A as AgentTool } from './tools-BWzPQVy0.js';
2
2
 
3
3
  /**
4
4
  * MCP bridge — adapt a Model Context Protocol tool list into the agent's AgentTool[],
@@ -25,7 +25,7 @@ interface McpToolResult {
25
25
  images?: McpImage[];
26
26
  }
27
27
  /** Adapt one MCP tool spec into an AgentTool backed by `callTool`. */
28
- declare function mcpToolToAgentTool(spec: McpToolSpec, callTool: McpCall, prefix?: string): AgentTool;
28
+ declare function mcpToolToAgentTool(spec: McpToolSpec, callTool: McpCall, prefix?: string, timeoutMs?: number): AgentTool;
29
29
  /** Adapt a whole MCP tool list into AgentTool[] (names prefixed to avoid collisions).
30
30
  * Optional `filter` pre-narrows the set at mount time (host allowlist), so a server with
31
31
  * hundreds of tools needn't mount them all eagerly. */
@@ -35,6 +35,8 @@ interface McpToolSearchOptions {
35
35
  prefix?: string;
36
36
  /** Max tools returned per `ToolSearch` query. Default 10. */
37
37
  maxResults?: number;
38
+ /** Per-call deadline for `McpCall`. Default MCP_CALL_TIMEOUT_MS. */
39
+ timeoutMs?: number;
38
40
  }
39
41
  /**
40
42
  * Deferred-mount mode (ToolSearch-equivalent) for large MCP tool sets. Instead of mounting N
@@ -1,5 +1,5 @@
1
- import { e as McpToolSearchOptions, f as McpToolSpec } from './mcp-Bd19NiCL.js';
2
- import { A as AgentTool } from './tools-Byyh_bae.js';
1
+ import { e as McpToolSearchOptions, f as McpToolSpec } from './mcp-aONOXRNk.js';
2
+ import { A as AgentTool } from './tools-BWzPQVy0.js';
3
3
  import '@livx.cc/wcli/core';
4
4
 
5
5
  /** A JSON-RPC 2.0 transport: request (awaits a result), notify (fire-and-forget), close. */
@@ -93,13 +93,29 @@ function toResult(result) {
93
93
  }
94
94
  return { text: JSON.stringify(result) };
95
95
  }
96
- function mcpToolToAgentTool(spec, callTool, prefix = "mcp__") {
96
+ var MCP_CALL_TIMEOUT_MS = 3e5;
97
+ var TIMED_OUT = /* @__PURE__ */ Symbol("mcp-timeout");
98
+ async function withDeadline(p, ms) {
99
+ let t;
100
+ try {
101
+ return await Promise.race([p, new Promise((res) => {
102
+ t = setTimeout(() => res(TIMED_OUT), ms);
103
+ })]);
104
+ } finally {
105
+ clearTimeout(t);
106
+ }
107
+ }
108
+ var mcpTimedOut = (name, ms = MCP_CALL_TIMEOUT_MS) => `[${name}] no response after ${ms / 1e3}s \u2014 the MCP server never answered. The call's outcome is UNKNOWN: do not assume it ran or that it failed. Verify the state (or pick a bounded/narrower call) before retrying.`;
109
+ function mcpToolToAgentTool(spec, callTool, prefix = "mcp__", timeoutMs = MCP_CALL_TIMEOUT_MS) {
97
110
  return {
98
111
  name: `${prefix}${spec.name}`,
99
112
  description: spec.description ?? `MCP tool ${spec.name}`,
100
113
  parameters: spec.inputSchema ?? { type: "object", properties: {} },
114
+ maxDurationMs: timeoutMs,
101
115
  async run(args, _ctx) {
102
- const r = toResult(await callTool(spec.name, args ?? {}));
116
+ const raw = await withDeadline(callTool(spec.name, args ?? {}), timeoutMs);
117
+ if (raw === TIMED_OUT) return mcpTimedOut(spec.name, timeoutMs);
118
+ const r = toResult(raw);
103
119
  return r.images?.length ? r : r.text;
104
120
  }
105
121
  };
@@ -114,6 +130,7 @@ function describeSpec(s) {
114
130
  }
115
131
  function makeMcpToolSearch(specs, callTool, options = {}) {
116
132
  const maxResults = options.maxResults ?? 10;
133
+ const callTimeoutMs = options.timeoutMs ?? MCP_CALL_TIMEOUT_MS;
117
134
  const byName = new Map(specs.map((s) => [s.name, s]));
118
135
  const catalogLine = `${specs.length} MCP tool(s) available \u2014 search by keyword, then call by exact name.`;
119
136
  const searchTool = {
@@ -139,10 +156,13 @@ function makeMcpToolSearch(specs, callTool, options = {}) {
139
156
  args: { type: "object", description: "arguments object for the tool (per its schema)" }
140
157
  }
141
158
  },
159
+ maxDurationMs: callTimeoutMs,
142
160
  async run({ name, args }) {
143
161
  const n = String(name ?? "");
144
162
  if (!byName.has(n)) return `Error: unknown MCP tool '${n}'. Use ToolSearch to find valid names.`;
145
- const r = toResult(await callTool(n, args ?? {}));
163
+ const raw = await withDeadline(callTool(n, args ?? {}), callTimeoutMs);
164
+ if (raw === TIMED_OUT) return mcpTimedOut(n, callTimeoutMs);
165
+ const r = toResult(raw);
146
166
  return r.images?.length ? r : r.text;
147
167
  }
148
168
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mcp.client.ts","../src/logging.ts","../src/relevance.ts","../src/mcp.ts"],"sourcesContent":["import { spawn, type ChildProcess } from 'node:child_process';\nimport { createHash } from 'node:crypto';\nimport { forComponent } from './logging';\nimport { mcpToolsToAgentTools, makeMcpToolSearchFromMounted, makeLazyMcpToolSearch, type McpToolSpec, type McpToolSearchOptions } from './mcp';\nimport type { AgentTool } from './tools';\n\nconst log = forComponent('mcp');\n\n/**\n * Real MCP (Model Context Protocol) client — node-only, deliberately kept OUT of the edge-safe\n * `src/index.ts` entry. It speaks JSON-RPC 2.0 over a transport (stdio child process or HTTP),\n * performs the `initialize` handshake + `tools/list` discovery, then hands `(specs, callTool)` to\n * the pure `src/mcp.ts` adapter — so the browser/edge build keeps working without importing this.\n */\n\nconst PROTOCOL_VERSION = '2025-06-18';\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/** A JSON-RPC 2.0 transport: request (awaits a result), notify (fire-and-forget), close. */\nexport interface McpTransport {\n start(): Promise<void>;\n request(method: string, params?: unknown): Promise<any>;\n notify(method: string, params?: unknown): Promise<void>;\n close(): Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// stdio transport — newline-delimited JSON-RPC over a spawned child's stdin/stdout.\n// ---------------------------------------------------------------------------\nexport interface StdioServerSpec {\n command: string;\n args?: string[];\n env?: Record<string, string>;\n cwd?: string;\n timeoutMs?: number;\n}\n\nexport class StdioTransport implements McpTransport {\n private proc?: ChildProcess;\n private buf = '';\n private nextId = 1;\n private pending = new Map<number, { resolve: (v: any) => void; reject: (e: any) => void; timer: ReturnType<typeof setTimeout> }>();\n\n constructor(private spec: StdioServerSpec) {}\n\n async start(): Promise<void> {\n const { command, args = [], env, cwd } = this.spec;\n const proc = spawn(command, args, { stdio: ['pipe', 'pipe', 'pipe'], env: { ...process.env, ...env }, cwd });\n this.proc = proc;\n proc.stdout!.setEncoding('utf8');\n proc.stdout!.on('data', (chunk: string) => this.onData(chunk));\n proc.stderr!.setEncoding('utf8');\n proc.stderr!.on('data', (chunk: string) => log.debug(`[${command}] stderr:`, chunk.trimEnd())); // server diagnostics\n proc.on('exit', (code) => this.failAll(new Error(`MCP server \"${command}\" exited (code ${code})`)));\n proc.on('error', (e) => this.failAll(e instanceof Error ? e : new Error(String(e))));\n }\n\n private onData(chunk: string): void {\n this.buf += chunk;\n let nl: number;\n while ((nl = this.buf.indexOf('\\n')) >= 0) {\n const line = this.buf.slice(0, nl).trim();\n this.buf = this.buf.slice(nl + 1);\n if (!line) continue;\n try { this.dispatch(JSON.parse(line)); }\n catch (e) { log.debug('dropping non-JSON line from MCP server:', line, e); } // never throw out of the stream handler\n }\n }\n\n private dispatch(msg: any): void {\n // We're a minimal client: only correlate responses to our requests; ignore server→client calls.\n if (msg?.id == null || !this.pending.has(msg.id)) return;\n const p = this.pending.get(msg.id)!;\n this.pending.delete(msg.id);\n clearTimeout(p.timer);\n if (msg.error) p.reject(new Error(msg.error?.message ?? JSON.stringify(msg.error)));\n else p.resolve(msg.result);\n }\n\n private failAll(e: Error): void {\n for (const p of this.pending.values()) { clearTimeout(p.timer); p.reject(e); }\n this.pending.clear();\n }\n\n private write(msg: unknown): void {\n if (!this.proc?.stdin) throw new Error('MCP stdio transport not started');\n this.proc.stdin.write(JSON.stringify(msg) + '\\n');\n }\n\n request(method: string, params?: unknown): Promise<any> {\n const id = this.nextId++;\n const timeoutMs = this.spec.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pending.delete(id);\n reject(new Error(`MCP request \"${method}\" timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n this.pending.set(id, { resolve, reject, timer });\n try { this.write({ jsonrpc: '2.0', id, method, params }); }\n catch (e) { clearTimeout(timer); this.pending.delete(id); reject(e); }\n });\n }\n\n async notify(method: string, params?: unknown): Promise<void> {\n this.write({ jsonrpc: '2.0', method, params });\n }\n\n async close(): Promise<void> {\n this.failAll(new Error('MCP transport closed'));\n try { this.proc?.stdin?.end(); } catch (e) { log.debug('stdin end failed', e); }\n this.proc?.kill();\n }\n}\n\n// ---------------------------------------------------------------------------\n// HTTP transport — Streamable HTTP: POST JSON-RPC, parse JSON or an SSE response.\n// ---------------------------------------------------------------------------\nexport interface HttpServerSpec {\n url: string;\n headers?: Record<string, string>;\n /** Sugar over `headers` → `Authorization: Bearer <token>`. The headless-friendly path (no OAuth loopback);\n * inject a static token (from env / VFS secret) per our CLAUDE.md rule preferring API keys for cron/edge. */\n bearerToken?: string;\n timeoutMs?: number;\n}\n\n/** Thrown when an HTTP MCP server rejects auth (401/403). Carries `needsAuth` so callers can surface a hint\n * (\"set bearerToken / headers\") instead of a generic mount failure — without attempting an interactive flow. */\nexport class McpAuthError extends Error {\n needsAuth = true;\n constructor(public status: number, public serverName?: string) {\n super(`MCP server${serverName ? ` \"${serverName}\"` : ''} requires authentication (HTTP ${status}) — set bearerToken or headers`);\n this.name = 'McpAuthError';\n }\n}\n\nexport class HttpTransport implements McpTransport {\n private nextId = 1;\n private sessionId?: string;\n private fetchImpl: typeof fetch;\n\n constructor(private spec: HttpServerSpec, fetchImpl?: typeof fetch) {\n this.fetchImpl = fetchImpl ?? fetch;\n }\n\n async start(): Promise<void> {}\n\n request(method: string, params?: unknown): Promise<any> {\n return this.post({ jsonrpc: '2.0', id: this.nextId++, method, params }, false);\n }\n\n async notify(method: string, params?: unknown): Promise<void> {\n await this.post({ jsonrpc: '2.0', method, params }, true);\n }\n\n private async post(msg: unknown, isNotify: boolean): Promise<any> {\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n accept: 'application/json, text/event-stream',\n ...(this.spec.bearerToken ? { authorization: `Bearer ${this.spec.bearerToken}` } : {}),\n ...this.spec.headers, // explicit headers win — lets a caller override the bearer sugar if needed\n ...(this.sessionId ? { 'mcp-session-id': this.sessionId } : {}),\n };\n const resp = await this.fetchImpl(this.spec.url, {\n method: 'POST', headers, body: JSON.stringify(msg),\n signal: AbortSignal.timeout(this.spec.timeoutMs ?? DEFAULT_TIMEOUT_MS),\n });\n const sid = resp.headers.get('mcp-session-id');\n if (sid) this.sessionId = sid; // adopt the server-assigned session for subsequent calls\n if (resp.status === 401 || resp.status === 403) throw new McpAuthError(resp.status);\n if (!resp.ok) throw new Error(`MCP HTTP ${resp.status} ${resp.statusText}`);\n if (isNotify) return;\n const ct = resp.headers.get('content-type') ?? '';\n const data = ct.includes('text/event-stream') ? parseSseResponse(await resp.text()) : await resp.json();\n if (data?.error) throw new Error(data.error?.message ?? JSON.stringify(data.error));\n return data?.result;\n }\n\n async close(): Promise<void> {}\n}\n\n/** Pull the JSON-RPC response object out of an SSE body (the first `data:` payload that carries one). */\nfunction parseSseResponse(body: string): any {\n for (const line of body.split('\\n')) {\n const trimmed = line.trimStart();\n if (!trimmed.startsWith('data:')) continue;\n try {\n const obj = JSON.parse(trimmed.slice(5).trim());\n if (obj && (obj.result !== undefined || obj.error !== undefined)) return obj;\n } catch (e) { log.debug('skipping unparseable SSE data line', e); }\n }\n return {};\n}\n\n// ---------------------------------------------------------------------------\n// Client — handshake + discovery on top of a transport.\n// ---------------------------------------------------------------------------\nexport class McpClient {\n constructor(public transport: McpTransport, private clientInfo = { name: 'agentx', version: '0' }) {}\n\n /** `initialize` handshake → `notifications/initialized`. Returns the server's init result. */\n async connect(): Promise<{ serverInfo?: { name?: string; version?: string }; capabilities?: any }> {\n await this.transport.start();\n const result = await this.transport.request('initialize', {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: {},\n clientInfo: this.clientInfo,\n });\n await this.transport.notify('notifications/initialized');\n return result ?? {};\n }\n\n async listTools(): Promise<McpToolSpec[]> {\n const r = await this.transport.request('tools/list');\n return Array.isArray(r?.tools) ? r.tools : [];\n }\n\n async callTool(name: string, args: unknown): Promise<unknown> {\n return this.transport.request('tools/call', { name, arguments: args ?? {} });\n }\n\n async listResources(): Promise<Array<{ uri: string; name?: string; mimeType?: string }>> {\n const r = await this.transport.request('resources/list');\n return Array.isArray(r?.resources) ? r.resources : [];\n }\n\n async readResource(uri: string): Promise<any> {\n return this.transport.request('resources/read', { uri });\n }\n\n async close(): Promise<void> {\n await this.transport.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Config-driven mounting — Claude-style `mcpServers: { name: {...} }`.\n// ---------------------------------------------------------------------------\n/** One server: stdio (`command`+`args`+`env`) OR http (`url`+`headers`). `disabled` skips it. */\nexport interface McpServerConfig {\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n cwd?: string;\n url?: string;\n headers?: Record<string, string>;\n /** `Authorization: Bearer <token>` sugar for http servers (headless-friendly; no OAuth flow). */\n bearerToken?: string;\n /** Auth strategy for http servers. `'oauth'` → the CLI resolves a token via the attended OAuth flow\n * (cli/mcpOAuth.ts) and injects it as `bearerToken` at mount; the core never runs the flow. Default none. */\n auth?: 'oauth' | 'none';\n timeoutMs?: number;\n disabled?: boolean;\n}\n\nexport interface MountedMcp {\n name: string;\n client: McpClient;\n tools: AgentTool[];\n /** Raw tool specs discovered at mount (already fetched internally) — exposed so deferred-mount\n * glue (`makeMcpToolSearchFromMounted`) needn't re-call `tools/list`. */\n specs: McpToolSpec[];\n serverInfo?: { name?: string; version?: string };\n /** The config this server was mounted with — lets a host remount it (`/mcp reconnect`). */\n config: McpServerConfig;\n}\n\n/** Build the transport for a server config (http when `url`, else stdio). Shared by every mount path. */\nfunction buildTransport(cfg: McpServerConfig): McpTransport {\n return cfg.url\n ? new HttpTransport({ url: cfg.url, headers: cfg.headers, bearerToken: cfg.bearerToken, timeoutMs: cfg.timeoutMs })\n : new StdioTransport({ command: cfg.command!, args: cfg.args, env: cfg.env, cwd: cfg.cwd, timeoutMs: cfg.timeoutMs });\n}\n\n/** Race `p` against `ms` (a per-server mount deadline). `ms` falsy/≤0 → no deadline. The caller\n * closes the half-open client on rejection so a hung server leaks nothing. */\nfunction withTimeout<T>(p: Promise<T>, ms: number | undefined, label: string): Promise<T> {\n if (!ms || ms <= 0) return p;\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`MCP \"${label}\" mount exceeded ${ms}ms`)), ms);\n (timer as any).unref?.();\n p.then((v) => { clearTimeout(timer); resolve(v); }, (e) => { clearTimeout(timer); reject(e); });\n });\n}\n\n/** Connect + discover + adapt, with optional per-server deadline; closes the client if it fails. */\nasync function mountWithDeadline(name: string, cfg: McpServerConfig, mountTimeoutMs?: number): Promise<MountedMcp> {\n const client = new McpClient(buildTransport(cfg));\n try {\n return await withTimeout((async () => {\n const init = await client.connect();\n const specs = await client.listTools();\n const tools = mcpToolsToAgentTools(specs, (tool, a) => client.callTool(tool, a), `mcp__${name}__`);\n return { name, client, tools, specs, serverInfo: init?.serverInfo, config: cfg } as MountedMcp;\n })(), mountTimeoutMs, name);\n } catch (e) {\n await client.close().catch((err) => log.debug(`close after failed mount of \"${name}\": ${err}`));\n throw e;\n }\n}\n\n/** Connect one server, discover its tools, and adapt them to prefixed AgentTools (`mcp__<name>__`). */\nexport function mountMcpServer(name: string, cfg: McpServerConfig): Promise<MountedMcp> {\n return mountWithDeadline(name, cfg);\n}\n\n/** Drop disabled/incomplete entries, warning on the incomplete ones. */\nfunction validEntries(servers: Record<string, McpServerConfig>): Array<[string, McpServerConfig]> {\n return Object.entries(servers).filter(([name, cfg]) => {\n if (!cfg || cfg.disabled) return false;\n if (!cfg.command && !cfg.url) { log.warn(`MCP server \"${name}\" needs a command (stdio) or url (http) — skipping`); return false; }\n return true;\n });\n}\n\n/** Log a mount failure (auth vs generic) without throwing — one broken server never blocks startup. */\nfunction logMountFailure(name: string, e: any): void {\n if (e instanceof McpAuthError) log.warn(`MCP \"${name}\" needs-auth: HTTP ${e.status} — set bearerToken or headers in its config; skipping`);\n else log.error(`MCP server \"${name}\" failed to mount: ${e?.message ?? e}`); // logged, then skipped\n}\n\n/**\n * Mount every configured server in PARALLEL (one slow/dead server no longer serializes the rest);\n * each may carry a `mountTimeoutMs` deadline. A server that fails is logged and skipped.\n */\nexport async function mountMcpServers(servers: Record<string, McpServerConfig> = {}, opts: { mountTimeoutMs?: number } = {}): Promise<MountedMcp[]> {\n const entries = validEntries(servers);\n const settled = await Promise.allSettled(entries.map(([name, cfg]) => mountWithDeadline(name, cfg, opts.mountTimeoutMs)));\n const out: MountedMcp[] = [];\n settled.forEach((r, i) => {\n const name = entries[i][0];\n // debug (not info): the CLI prints its own one-line mount summary — an always-on info here double-prints\n if (r.status === 'fulfilled') { out.push(r.value); log.debug(`MCP \"${name}\" mounted — ${r.value.tools.length} tool(s)${r.value.serverInfo?.name ? ` from ${r.value.serverInfo.name}` : ''}`); }\n else logMountFailure(name, r.reason);\n });\n return out;\n}\n\n/**\n * One-shot deferred MCP mount: connect every entry (failures dropped, never block startup) and\n * fold the survivors into a single `ToolSearch`/`McpCall` pair via `makeMcpToolSearchFromMounted`.\n * The node-only counterpart to that edge-safe helper — returns `mounted` too so callers can close\n * the clients on shutdown. Eager: it connects all servers up front. For lazy connect + a cached\n * catalog (zero connections on a turn that uses no MCP tool), use `mountMcpCatalog`.\n */\nexport async function mountMcpDeferred(\n servers: Record<string, McpServerConfig> = {},\n options?: McpToolSearchOptions & { mountTimeoutMs?: number },\n): Promise<{ tools: AgentTool[]; serverNames: string[]; toolCount: number; mounted: MountedMcp[] }> {\n const mounted = await mountMcpServers(servers, { mountTimeoutMs: options?.mountTimeoutMs });\n return { ...makeMcpToolSearchFromMounted(mounted, options), mounted };\n}\n\n// ---------------------------------------------------------------------------\n// Lazy connect + catalog cache — defer the CONNECTION, not just the schemas.\n// ---------------------------------------------------------------------------\n/** A persistent tool catalog: lets `mountMcpCatalog` build `ToolSearch` WITHOUT connecting when a\n * fresh entry exists. The lib defines the interface; the consumer supplies the store (RAM/disk).\n * Key = a hash of the server's resolved config (see `mcpConfigKey`). TTL/versioning is the store's. */\nexport interface McpCatalogStore {\n get(key: string): McpToolSpec[] | null;\n set(key: string, specs: McpToolSpec[]): void;\n}\n\n/** Stable cache key for a server config — command/args/cwd + env NAMES (not secret values), or\n * url + header names. Changing any of these invalidates the cached catalog; rotating a secret does not. */\nexport function mcpConfigKey(cfg: McpServerConfig): string {\n const parts = cfg.url\n ? ['http', cfg.url, ...Object.keys(cfg.headers ?? {}).sort()]\n : ['stdio', cfg.command ?? '', ...(cfg.args ?? []), cfg.cwd ?? '', ...Object.keys(cfg.env ?? {}).sort()];\n return createHash('sha256').update(parts.join('\\0')).digest('hex').slice(0, 16);\n}\n\n/** Default in-memory catalog: process-lifetime, hash-keyed, TTL-expiring (so a server that gains\n * tools is picked up after the TTL without a restart). Shared module singleton → cross-turn reuse. */\nexport class MemMcpCatalog implements McpCatalogStore {\n private m = new Map<string, { specs: McpToolSpec[]; exp: number }>();\n constructor(private ttlMs = 5 * 60_000) {}\n get(key: string): McpToolSpec[] | null {\n const e = this.m.get(key);\n if (!e) return null;\n if (Date.now() > e.exp) { this.m.delete(key); return null; }\n return e.specs;\n }\n set(key: string, specs: McpToolSpec[]): void { this.m.set(key, { specs, exp: Date.now() + this.ttlMs }); }\n}\n\n/** Opt-in warm pool: keeps stdio MCP clients connected across turns, reaping each after `ttlMs`\n * idle. Most stdio MCPs are per-turn by design, so this is off by default; HTTP servers are\n * stateless and never pooled. */\nexport class McpPool {\n private warm = new Map<string, { client: McpClient; timer: ReturnType<typeof setTimeout> }>();\n constructor(private ttlMs = 5 * 60_000) {}\n get(key: string): McpClient | null { const e = this.warm.get(key); if (!e) return null; this.arm(key, e); return e.client; }\n put(key: string, client: McpClient): void {\n const prev = this.warm.get(key); // overwrite: cancel the stale timer + close the displaced client\n if (prev) { clearTimeout(prev.timer); if (prev.client !== client) void prev.client.close().catch((err) => log.debug(`warm-pool replace close failed: ${err}`)); }\n const e = { client, timer: undefined as any };\n this.warm.set(key, e); this.arm(key, e);\n }\n private arm(key: string, e: { client: McpClient; timer: any }): void {\n clearTimeout(e.timer);\n e.timer = setTimeout(() => { void this.evict(key); }, this.ttlMs);\n (e.timer as any).unref?.();\n }\n private async evict(key: string): Promise<void> {\n const e = this.warm.get(key); if (!e) return;\n this.warm.delete(key);\n await e.client.close().catch((err) => log.debug(`warm-pool evict close failed: ${err}`));\n }\n async closeAll(): Promise<void> { for (const e of this.warm.values()) { clearTimeout(e.timer); await e.client.close().catch(() => {}); } this.warm.clear(); }\n}\n\nconst defaultCatalog = new MemMcpCatalog();\nconst defaultPool = new McpPool();\nconst defaultFailures = new Map<string, number>(); // configKey → cooldown-until epoch ms (negative cache)\nconst bgInflight = new Set<string>(); // configKey → background discovery in flight (dedupe)\nconst DEFAULT_FAILURE_COOLDOWN_MS = 60_000;\n\nexport interface McpCatalogOptions extends McpToolSearchOptions {\n /** Where to read/write discovered specs. Default: a shared process-lifetime `MemMcpCatalog`. */\n catalog?: McpCatalogStore;\n /** Per-server deadline for the connect+list on a cache MISS. A hung server is skipped, not blocking. */\n mountTimeoutMs?: number;\n /** Opt-in: keep stdio clients warm across turns (see `McpPool`). HTTP servers are never pooled. */\n keepWarm?: boolean;\n /** Warm-client pool. Default: a shared process-lifetime `McpPool` (only used when `keepWarm`). */\n pool?: McpPool;\n /** Negative cache: after a server fails/times out discovery, skip it for this long so it never\n * re-floors a turn at the deadline. Default 60s; the server is re-probed once the cooldown lapses. */\n failureCooldownMs?: number;\n /** Shared failure-cooldown map (configKey → until-epoch-ms). Default: a process-lifetime singleton. */\n failures?: Map<string, number>;\n /** Discovery policy on a cache MISS:\n * - `'connect'` (default): synchronously connect+list (deadline-bounded) — blocks until ready.\n * - `'cache-only'`: NEVER block the turn — serve only cached servers, and kick uncached discovery\n * to the background (so it's warm next turn). Pair with a boot/timer `warmMcpCatalog` so the\n * first turn is already a cache hit. */\n discover?: 'connect' | 'cache-only';\n}\n\ninterface Discovered { name: string; cfg: McpServerConfig; key: string; specs: McpToolSpec[] }\n\n/** Connect+list one server (deadline-bounded), populate the catalog on success / negative-cache on\n * failure. Closes the client unless `keepWarm` keeps it for the lazy-call phase. */\nasync function connectAndList(name: string, cfg: McpServerConfig, key: string, opts: McpCatalogOptions): Promise<Discovered | null> {\n const catalog = opts.catalog ?? defaultCatalog;\n const pool = opts.pool ?? defaultPool;\n const failures = opts.failures ?? defaultFailures;\n const client = new McpClient(buildTransport(cfg));\n try {\n const specs = await withTimeout((async () => { await client.connect(); return client.listTools(); })(), opts.mountTimeoutMs, name);\n catalog.set(key, specs);\n failures.delete(key); // recovered\n if (opts.keepWarm && !cfg.url) pool.put(key, client); // reuse this connection for the lazy-call phase\n else await client.close().catch(() => {});\n return { name, cfg, key, specs };\n } catch (e: any) {\n await client.close().catch(() => {});\n failures.set(key, Date.now() + (opts.failureCooldownMs ?? DEFAULT_FAILURE_COOLDOWN_MS));\n logMountFailure(name, e);\n return null;\n }\n}\n\n/** Resolve one server to its specs: cache hit → no connection; in failure-cooldown → skip;\n * otherwise connect (or, in `cache-only` mode, kick background discovery and skip for now). */\nasync function discoverOne(name: string, cfg: McpServerConfig, opts: McpCatalogOptions): Promise<Discovered | null> {\n const catalog = opts.catalog ?? defaultCatalog;\n const failures = opts.failures ?? defaultFailures;\n const key = mcpConfigKey(cfg);\n const cached = catalog.get(key);\n if (cached) return { name, cfg, key, specs: cached };\n const until = failures.get(key);\n if (until && until > Date.now()) return null; // negative cache → known-down, skip\n if (opts.discover === 'cache-only') {\n if (!bgInflight.has(key)) { bgInflight.add(key); void connectAndList(name, cfg, key, opts).finally(() => bgInflight.delete(key)); }\n return null; // not available THIS turn; warms for the next one\n }\n return connectAndList(name, cfg, key, opts);\n}\n\n/**\n * Lazy + cached MCP mount. Builds the `ToolSearch`/`McpCall` pair from the CACHED catalog when one\n * exists — connecting NOTHING. On a cache miss it (by default) connects once (parallel, deadline-\n * bounded), lists, caches, then disconnects (or keeps warm if `keepWarm`). A server is connected only\n * when one of its tools is actually invoked via `McpCall` (memoized per turn; reused from the warm\n * pool if enabled). A server that fails/times out is negative-cached (`failureCooldownMs`) so it\n * never re-floors a later turn.\n *\n * For zero turn-path latency even on a cold process, set `discover: 'cache-only'` and call\n * `warmMcpCatalog` at boot + on a timer: the turn then NEVER synchronously connects — it serves\n * cached servers and warms the rest in the background.\n *\n * Per-turn cost: a turn using NO MCP tool → 0 connections; a turn using one → exactly one.\n */\nexport async function mountMcpCatalog(\n servers: Record<string, McpServerConfig> = {},\n opts: McpCatalogOptions = {},\n): Promise<{ tools: AgentTool[]; serverNames: string[]; toolCount: number; connect(name: string): Promise<McpClient>; close(): Promise<void> }> {\n const pool = opts.pool ?? defaultPool;\n const discovered = (await Promise.all(validEntries(servers).map(([name, cfg]) => discoverOne(name, cfg, opts)))).filter(Boolean) as Discovered[];\n\n const byName = new Map(discovered.map((d) => [d.name, d]));\n const inflight = new Map<string, Promise<McpClient>>(); // memoize per server → concurrent calls share one connect\n const live: McpClient[] = []; // non-warm clients opened this turn (closed on close())\n\n const connect = (name: string): Promise<McpClient> => {\n let p = inflight.get(name);\n if (p) return p;\n p = (async () => {\n const d = byName.get(name);\n if (!d) throw new Error(`MCP server '${name}' is not in the catalog`);\n const warmable = opts.keepWarm && !d.cfg.url;\n if (warmable) { const w = pool.get(d.key); if (w) return w; }\n const client = new McpClient(buildTransport(d.cfg));\n await client.connect();\n if (warmable) pool.put(d.key, client); else live.push(client);\n return client;\n })();\n inflight.set(name, p);\n return p;\n };\n\n const search = makeLazyMcpToolSearch(\n discovered.map((d) => ({ name: d.name, specs: d.specs })),\n async (server, rawName, args) => (await connect(server)).callTool(rawName, args),\n opts,\n );\n const close = async (): Promise<void> => { for (const c of live) await c.close().catch(() => {}); live.length = 0; inflight.clear(); };\n return { ...search, connect, close };\n}\n\n/**\n * Off-turn catalog warm-up: do the one cold discovery pass (connect + list, parallel, deadline-\n * bounded) so a later `mountMcpCatalog(..., { discover: 'cache-only' })` is a cache HIT. Call at\n * server boot and on a timer (cadence < the catalog TTL). Cache hits cost nothing; down servers are\n * negative-cached so turns never touch them. Returns which servers warmed vs failed.\n */\nexport async function warmMcpCatalog(\n servers: Record<string, McpServerConfig> = {},\n opts: McpCatalogOptions = {},\n): Promise<{ warmed: string[]; failed: string[]; toolCount: number }> {\n const entries = validEntries(servers);\n const discovered = (await Promise.all(entries.map(([name, cfg]) => discoverOne(name, cfg, { ...opts, discover: 'connect' })))).filter(Boolean) as Discovered[];\n const warmedNames = new Set(discovered.map((d) => d.name));\n return {\n warmed: discovered.map((d) => d.name),\n failed: entries.map(([n]) => n).filter((n) => !warmedNames.has(n)),\n toolCount: discovered.reduce((s, d) => s + d.specs.length, 0),\n };\n}\n","// Import the log module directly from libx.js source: libx.js's main bundle\n// doesn't re-export `log` as a named ESM export, and source-importing keeps\n// libx.js patches live (no rebuild) — matching the `bun link` workflow.\nimport { log } from 'libx.js/src/modules/log';\n\n/** Component-scoped logger (libx.js). debug/verbose gated via DEBUG env/localStorage. */\nexport const forComponent = (name: string) => log.forComponent(name);\nexport { log };\n","/**\n * Lexical relevance — no deps, deterministic, edge-safe. Ranks catalog entries (skills,\n * commands, memories, docs) by a task hint so a LARGE catalog doesn't dump every entry into\n * the prompt. TF-IDF weighted: rare terms in the corpus score higher than common ones.\n */\n\nconst STOP = new Set([\n 'the', 'and', 'for', 'with', 'that', 'this', 'from', 'into', 'your', 'you', 'are', 'was', 'will',\n 'use', 'using', 'run', 'add', 'fix', 'make', 'file', 'files', 'code', 'please', 'need', 'want', 'should', 'all',\n]);\n\n/** Lowercased alphanumeric tokens of length ≥ 3, minus a few stopwords. */\nexport function tokenize(s: string): Set<string> {\n const out = new Set<string>();\n for (const w of String(s ?? '').toLowerCase().match(/[a-z0-9]+/g) ?? []) {\n if (w.length >= 3 && !STOP.has(w)) out.add(w);\n }\n return out;\n}\n\n/** Build IDF weights from a corpus of texts. Rare terms get higher weight. */\nexport function idfWeights(corpus: string[]): Map<string, number> {\n const N = corpus.length;\n if (N === 0) return new Map();\n const df = new Map<string, number>();\n for (const doc of corpus) for (const t of tokenize(doc)) df.set(t, (df.get(t) ?? 0) + 1);\n const idf = new Map<string, number>();\n for (const [t, n] of df) idf.set(t, Math.log((N + 1) / (n + 1)) + 1); // smooth IDF\n return idf;\n}\n\n/** TF-IDF weighted relevance score. Without IDF, falls back to flat token overlap (backward-compatible). */\nexport function relevanceScore(text: string, queryTokens: Set<string>, idf?: Map<string, number>): number {\n if (queryTokens.size === 0) return 0;\n const t = tokenize(text);\n let score = 0;\n for (const q of queryTokens) if (t.has(q)) score += idf?.get(q) ?? 1;\n return score;\n}\n\n/**\n * Split `items` into the `k` most relevant to `query` (by TF-IDF overlap with `text(item)`)\n * and the rest, preserving each item's original order within its group. Returns everything in\n * `kept` (empty `rest`) when `items.length <= k` or `query` is blank — so it's a no-op for\n * small lists. When `corpus` is provided, builds IDF weights for better ranking.\n */\nexport function topByRelevance<T>(items: T[], query: string, text: (x: T) => string, k: number, corpus?: string[]): { kept: T[]; rest: T[] } {\n if (!Number.isInteger(k) || k < 1) return { kept: items, rest: [] }; // invalid k → no-op (keep all), never hide\n if (items.length <= k || !query.trim()) return { kept: items, rest: [] };\n const q = tokenize(query);\n if (q.size === 0) return { kept: items.slice(0, k), rest: items.slice(k) };\n const idf = corpus?.length ? idfWeights(corpus) : undefined;\n const scored = items.map((x, i) => ({ i, s: relevanceScore(text(x), q, idf) }));\n scored.sort((a, b) => b.s - a.s || a.i - b.i); // best score first; stable by original index\n const keep = new Set(scored.slice(0, k).map((e) => e.i));\n return { kept: items.filter((_, i) => keep.has(i)), rest: items.filter((_, i) => !keep.has(i)) };\n}\n","import type { AgentTool } from './tools';\nimport { topByRelevance } from './relevance';\n\n/**\n * MCP bridge — adapt a Model Context Protocol tool list into the agent's AgentTool[],\n * so any MCP server's tools become first-class agent tools (edge-safe: you supply the\n * transport via `callTool`; this module has no node/network dependency of its own).\n *\n * Pass the server's advertised tools + a `callTool(name, args)` that performs the call\n * (over stdio/HTTP/whatever you wire up); each becomes an AgentTool the Agent can use.\n */\nexport interface McpToolSpec {\n name: string;\n description?: string;\n /** JSON Schema for the tool's arguments (MCP's `inputSchema`). */\n inputSchema?: object;\n}\n\n/** Perform an MCP tool call; return its textual result. Throw to surface an error to the model. */\nexport type McpCall = (name: string, args: any) => Promise<unknown>;\n\nexport interface McpImage { mimeType: string; data: string }\nexport interface McpToolResult { text: string; images?: McpImage[] }\n\n/** Normalize an MCP call result into text + optional image blocks. */\nfunction toResult(result: unknown): McpToolResult {\n if (result == null) return { text: '' };\n if (typeof result === 'string') return { text: result };\n const content = (result as any).content;\n if (Array.isArray(content)) {\n const texts: string[] = [];\n const images: McpImage[] = [];\n for (const c of content) {\n if (c?.type === 'image' && typeof c.data === 'string' && c.mimeType) {\n images.push({ mimeType: c.mimeType, data: c.data });\n } else if (typeof c?.text === 'string') {\n texts.push(c.text);\n } else {\n texts.push(JSON.stringify(c));\n }\n }\n const text = texts.join('\\n');\n if (text || images.length) return { text, ...(images.length ? { images } : {}) };\n }\n return { text: JSON.stringify(result) };\n}\n\nfunction toText(result: unknown): string { return toResult(result).text; }\n\n/** Adapt one MCP tool spec into an AgentTool backed by `callTool`. */\nexport function mcpToolToAgentTool(spec: McpToolSpec, callTool: McpCall, prefix = 'mcp__'): AgentTool {\n return {\n name: `${prefix}${spec.name}`,\n description: spec.description ?? `MCP tool ${spec.name}`,\n parameters: spec.inputSchema ?? { type: 'object', properties: {} },\n async run(args, _ctx) {\n const r = toResult(await callTool(spec.name, args ?? {}));\n return r.images?.length ? r : r.text;\n },\n };\n}\n\n/** Adapt a whole MCP tool list into AgentTool[] (names prefixed to avoid collisions).\n * Optional `filter` pre-narrows the set at mount time (host allowlist), so a server with\n * hundreds of tools needn't mount them all eagerly. */\nexport function mcpToolsToAgentTools(specs: McpToolSpec[], callTool: McpCall, prefix = 'mcp__', filter?: (spec: McpToolSpec) => boolean): AgentTool[] {\n return (filter ? specs.filter(filter) : specs).map((s) => mcpToolToAgentTool(s, callTool, prefix));\n}\n\nexport interface McpToolSearchOptions {\n /** Prefix stripped from / shown on tool names (cosmetic; mirrors the adapter prefix). Default 'mcp__'. */\n prefix?: string;\n /** Max tools returned per `ToolSearch` query. Default 10. */\n maxResults?: number;\n}\n\n/** Render one spec for the search result: name, description, and its argument schema (so the\n * model knows how to call it via `McpCall`). */\nfunction describeSpec(s: McpToolSpec): string {\n const schema = s.inputSchema ? `\\n args: ${JSON.stringify(s.inputSchema)}` : '';\n return `${s.name} — ${s.description ?? '(no description)'}${schema}`;\n}\n\n/**\n * Deferred-mount mode (ToolSearch-equivalent) for large MCP tool sets. Instead of mounting N\n * tools into the wire schema (cost + latency + model confusion past a few dozen), mount exactly\n * TWO bounded tools regardless of N:\n * - `ToolSearch({ query })` — ranks the catalog by relevance and returns the top matches with\n * their argument schemas, so the model discovers what's available on demand.\n * - `McpCall({ name, args })` — invokes any catalog tool by name through the same transport.\n * Keep `mcpToolsToAgentTools` (eager) as the default for small sets.\n */\nexport function makeMcpToolSearch(specs: McpToolSpec[], callTool: McpCall, options: McpToolSearchOptions = {}): AgentTool[] {\n const maxResults = options.maxResults ?? 10;\n const byName = new Map(specs.map((s) => [s.name, s]));\n const catalogLine = `${specs.length} MCP tool(s) available — search by keyword, then call by exact name.`;\n\n const searchTool: AgentTool = {\n name: 'ToolSearch',\n description: `Search the available MCP tools by keyword (${catalogLine}). Returns matching tool names + their argument schemas; call one with \\`McpCall\\`.`,\n parameters: { type: 'object', required: ['query'], properties: { query: { type: 'string', description: 'keywords to match against tool name + description' } } },\n async run({ query }) {\n const q = String(query ?? '').trim();\n if (!q) return catalogLine;\n const { kept } = topByRelevance(specs, q, (s) => `${s.name} ${s.description ?? ''}`, maxResults);\n if (!kept.length) return `(no MCP tool matches \"${q}\" — try broader keywords)`;\n return kept.map(describeSpec).join('\\n');\n },\n };\n\n const callMcpTool: AgentTool = {\n name: 'McpCall',\n description: 'Call an MCP tool discovered via `ToolSearch`, by its exact name. Pass its arguments as `args`.',\n parameters: {\n type: 'object',\n required: ['name'],\n properties: {\n name: { type: 'string', description: 'exact tool name from ToolSearch' },\n args: { type: 'object', description: 'arguments object for the tool (per its schema)' },\n },\n },\n async run({ name, args }) {\n const n = String(name ?? '');\n if (!byName.has(n)) return `Error: unknown MCP tool '${n}'. Use ToolSearch to find valid names.`;\n const r = toResult(await callTool(n, args ?? {}));\n return r.images?.length ? r : r.text;\n },\n };\n\n return [searchTool, callMcpTool];\n}\n\n/** Minimal shape of a mounted MCP server this module needs — structurally satisfied by\n * `MountedMcp` from `mcp.client.ts`, kept local so `mcp.ts` stays node-free/edge-safe. */\nexport interface MountedMcpLike {\n name: string;\n specs: McpToolSpec[];\n client: { callTool(name: string, args: unknown): Promise<unknown> };\n}\n\n/** A flattened catalog entry's origin: which server owns it + the un-prefixed raw tool name. */\nexport interface McpRoute { server: string; rawName: string }\n\n/** Flatten servers' specs into one `mcp__<server>__<tool>` namespace + a display→origin map.\n * Sanitizing/truncating to provider name rules can MERGE distinct names (`a/b` & `a:b` → `a_b`),\n * so dedupe with a numeric suffix — a silent overwrite would orphan a tool. */\nexport function buildMcpCatalog(servers: { name: string; specs: McpToolSpec[] }[]): { specs: McpToolSpec[]; routes: Map<string, McpRoute> } {\n const specs: McpToolSpec[] = [];\n const routes = new Map<string, McpRoute>();\n for (const m of servers) {\n for (const s of m.specs) {\n const base = `mcp__${m.name}__${s.name}`.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 128);\n let display = base;\n for (let i = 2; routes.has(display); i++) display = `${base.slice(0, 128 - String(i).length - 1)}_${i}`;\n specs.push({ name: display, description: s.description, inputSchema: s.inputSchema });\n routes.set(display, { server: m.name, rawName: s.name });\n }\n }\n return { specs, routes };\n}\n\n/** Resolve a routed MCP call to its result. Throw to surface an error to the model. */\nexport type McpRouteResolver = (server: string, rawName: string, args: any) => Promise<unknown>;\n\n/** Shared core: wire the `ToolSearch`/`McpCall` pair over a prebuilt catalog, dispatching each\n * call through `resolve(server, rawName, args)` (eager → a mounted client; lazy → connect-on-call). */\nfunction searchOverCatalog(servers: string[], specs: McpToolSpec[], routes: Map<string, McpRoute>, resolve: McpRouteResolver, options?: McpToolSearchOptions) {\n const tools = specs.length\n ? makeMcpToolSearch(specs, (name, args) => {\n const r = routes.get(name);\n if (!r) throw new Error(`unknown MCP tool '${name}' — use ToolSearch to find valid names`);\n return resolve(r.server, r.rawName, args ?? {});\n }, options)\n : [];\n return { tools, serverNames: servers, toolCount: specs.length };\n}\n\n/**\n * Ergonomic deferred-mount over already-mounted MCP servers: flattens their specs into one\n * `mcp__<server>__<tool>` namespace and wires a single `ToolSearch`/`McpCall` pair that routes\n * each call to the owning server's RAW `callTool` (so result normalization happens exactly once —\n * double-normalizing corrupts image blocks).\n */\nexport function makeMcpToolSearchFromMounted(\n mounted: MountedMcpLike[],\n options?: McpToolSearchOptions,\n): { tools: AgentTool[]; serverNames: string[]; toolCount: number } {\n const { specs, routes } = buildMcpCatalog(mounted);\n const byName = new Map(mounted.map((m) => [m.name, m]));\n return searchOverCatalog(mounted.map((m) => m.name), specs, routes, (server, rawName, args) => byName.get(server)!.client.callTool(rawName, args), options);\n}\n\n/**\n * Lazy variant: same `ToolSearch`/`McpCall` surface, but the server isn't required to be connected.\n * Each `McpCall` resolves the owning server from the catalog and `resolve(server, rawName, args)`\n * connects-on-demand — so a turn that calls no MCP tool opens ZERO connections. Edge-safe: the\n * caller (`mountMcpCatalog` in `mcp.client.ts`) supplies the node-side connect/pool logic.\n */\nexport function makeLazyMcpToolSearch(\n servers: { name: string; specs: McpToolSpec[] }[],\n resolve: McpRouteResolver,\n options?: McpToolSearchOptions,\n): { tools: AgentTool[]; serverNames: string[]; toolCount: number } {\n const { specs, routes } = buildMcpCatalog(servers);\n return searchOverCatalog(servers.map((s) => s.name), specs, routes, resolve, options);\n}\n"],"mappings":"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","names":["log"]}
1
+ {"version":3,"sources":["../src/mcp.client.ts","../src/logging.ts","../src/relevance.ts","../src/mcp.ts"],"sourcesContent":["import { spawn, type ChildProcess } from 'node:child_process';\nimport { createHash } from 'node:crypto';\nimport { forComponent } from './logging';\nimport { mcpToolsToAgentTools, makeMcpToolSearchFromMounted, makeLazyMcpToolSearch, type McpToolSpec, type McpToolSearchOptions } from './mcp';\nimport type { AgentTool } from './tools';\n\nconst log = forComponent('mcp');\n\n/**\n * Real MCP (Model Context Protocol) client — node-only, deliberately kept OUT of the edge-safe\n * `src/index.ts` entry. It speaks JSON-RPC 2.0 over a transport (stdio child process or HTTP),\n * performs the `initialize` handshake + `tools/list` discovery, then hands `(specs, callTool)` to\n * the pure `src/mcp.ts` adapter — so the browser/edge build keeps working without importing this.\n */\n\nconst PROTOCOL_VERSION = '2025-06-18';\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/** A JSON-RPC 2.0 transport: request (awaits a result), notify (fire-and-forget), close. */\nexport interface McpTransport {\n start(): Promise<void>;\n request(method: string, params?: unknown): Promise<any>;\n notify(method: string, params?: unknown): Promise<void>;\n close(): Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// stdio transport — newline-delimited JSON-RPC over a spawned child's stdin/stdout.\n// ---------------------------------------------------------------------------\nexport interface StdioServerSpec {\n command: string;\n args?: string[];\n env?: Record<string, string>;\n cwd?: string;\n timeoutMs?: number;\n}\n\nexport class StdioTransport implements McpTransport {\n private proc?: ChildProcess;\n private buf = '';\n private nextId = 1;\n private pending = new Map<number, { resolve: (v: any) => void; reject: (e: any) => void; timer: ReturnType<typeof setTimeout> }>();\n\n constructor(private spec: StdioServerSpec) {}\n\n async start(): Promise<void> {\n const { command, args = [], env, cwd } = this.spec;\n const proc = spawn(command, args, { stdio: ['pipe', 'pipe', 'pipe'], env: { ...process.env, ...env }, cwd });\n this.proc = proc;\n proc.stdout!.setEncoding('utf8');\n proc.stdout!.on('data', (chunk: string) => this.onData(chunk));\n proc.stderr!.setEncoding('utf8');\n proc.stderr!.on('data', (chunk: string) => log.debug(`[${command}] stderr:`, chunk.trimEnd())); // server diagnostics\n proc.on('exit', (code) => this.failAll(new Error(`MCP server \"${command}\" exited (code ${code})`)));\n proc.on('error', (e) => this.failAll(e instanceof Error ? e : new Error(String(e))));\n }\n\n private onData(chunk: string): void {\n this.buf += chunk;\n let nl: number;\n while ((nl = this.buf.indexOf('\\n')) >= 0) {\n const line = this.buf.slice(0, nl).trim();\n this.buf = this.buf.slice(nl + 1);\n if (!line) continue;\n try { this.dispatch(JSON.parse(line)); }\n catch (e) { log.debug('dropping non-JSON line from MCP server:', line, e); } // never throw out of the stream handler\n }\n }\n\n private dispatch(msg: any): void {\n // We're a minimal client: only correlate responses to our requests; ignore server→client calls.\n if (msg?.id == null || !this.pending.has(msg.id)) return;\n const p = this.pending.get(msg.id)!;\n this.pending.delete(msg.id);\n clearTimeout(p.timer);\n if (msg.error) p.reject(new Error(msg.error?.message ?? JSON.stringify(msg.error)));\n else p.resolve(msg.result);\n }\n\n private failAll(e: Error): void {\n for (const p of this.pending.values()) { clearTimeout(p.timer); p.reject(e); }\n this.pending.clear();\n }\n\n private write(msg: unknown): void {\n if (!this.proc?.stdin) throw new Error('MCP stdio transport not started');\n this.proc.stdin.write(JSON.stringify(msg) + '\\n');\n }\n\n request(method: string, params?: unknown): Promise<any> {\n const id = this.nextId++;\n const timeoutMs = this.spec.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n this.pending.delete(id);\n reject(new Error(`MCP request \"${method}\" timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n this.pending.set(id, { resolve, reject, timer });\n try { this.write({ jsonrpc: '2.0', id, method, params }); }\n catch (e) { clearTimeout(timer); this.pending.delete(id); reject(e); }\n });\n }\n\n async notify(method: string, params?: unknown): Promise<void> {\n this.write({ jsonrpc: '2.0', method, params });\n }\n\n async close(): Promise<void> {\n this.failAll(new Error('MCP transport closed'));\n try { this.proc?.stdin?.end(); } catch (e) { log.debug('stdin end failed', e); }\n this.proc?.kill();\n }\n}\n\n// ---------------------------------------------------------------------------\n// HTTP transport — Streamable HTTP: POST JSON-RPC, parse JSON or an SSE response.\n// ---------------------------------------------------------------------------\nexport interface HttpServerSpec {\n url: string;\n headers?: Record<string, string>;\n /** Sugar over `headers` → `Authorization: Bearer <token>`. The headless-friendly path (no OAuth loopback);\n * inject a static token (from env / VFS secret) per our CLAUDE.md rule preferring API keys for cron/edge. */\n bearerToken?: string;\n timeoutMs?: number;\n}\n\n/** Thrown when an HTTP MCP server rejects auth (401/403). Carries `needsAuth` so callers can surface a hint\n * (\"set bearerToken / headers\") instead of a generic mount failure — without attempting an interactive flow. */\nexport class McpAuthError extends Error {\n needsAuth = true;\n constructor(public status: number, public serverName?: string) {\n super(`MCP server${serverName ? ` \"${serverName}\"` : ''} requires authentication (HTTP ${status}) — set bearerToken or headers`);\n this.name = 'McpAuthError';\n }\n}\n\nexport class HttpTransport implements McpTransport {\n private nextId = 1;\n private sessionId?: string;\n private fetchImpl: typeof fetch;\n\n constructor(private spec: HttpServerSpec, fetchImpl?: typeof fetch) {\n this.fetchImpl = fetchImpl ?? fetch;\n }\n\n async start(): Promise<void> {}\n\n request(method: string, params?: unknown): Promise<any> {\n return this.post({ jsonrpc: '2.0', id: this.nextId++, method, params }, false);\n }\n\n async notify(method: string, params?: unknown): Promise<void> {\n await this.post({ jsonrpc: '2.0', method, params }, true);\n }\n\n private async post(msg: unknown, isNotify: boolean): Promise<any> {\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n accept: 'application/json, text/event-stream',\n ...(this.spec.bearerToken ? { authorization: `Bearer ${this.spec.bearerToken}` } : {}),\n ...this.spec.headers, // explicit headers win — lets a caller override the bearer sugar if needed\n ...(this.sessionId ? { 'mcp-session-id': this.sessionId } : {}),\n };\n const resp = await this.fetchImpl(this.spec.url, {\n method: 'POST', headers, body: JSON.stringify(msg),\n signal: AbortSignal.timeout(this.spec.timeoutMs ?? DEFAULT_TIMEOUT_MS),\n });\n const sid = resp.headers.get('mcp-session-id');\n if (sid) this.sessionId = sid; // adopt the server-assigned session for subsequent calls\n if (resp.status === 401 || resp.status === 403) throw new McpAuthError(resp.status);\n if (!resp.ok) throw new Error(`MCP HTTP ${resp.status} ${resp.statusText}`);\n if (isNotify) return;\n const ct = resp.headers.get('content-type') ?? '';\n const data = ct.includes('text/event-stream') ? parseSseResponse(await resp.text()) : await resp.json();\n if (data?.error) throw new Error(data.error?.message ?? JSON.stringify(data.error));\n return data?.result;\n }\n\n async close(): Promise<void> {}\n}\n\n/** Pull the JSON-RPC response object out of an SSE body (the first `data:` payload that carries one). */\nfunction parseSseResponse(body: string): any {\n for (const line of body.split('\\n')) {\n const trimmed = line.trimStart();\n if (!trimmed.startsWith('data:')) continue;\n try {\n const obj = JSON.parse(trimmed.slice(5).trim());\n if (obj && (obj.result !== undefined || obj.error !== undefined)) return obj;\n } catch (e) { log.debug('skipping unparseable SSE data line', e); }\n }\n return {};\n}\n\n// ---------------------------------------------------------------------------\n// Client — handshake + discovery on top of a transport.\n// ---------------------------------------------------------------------------\nexport class McpClient {\n constructor(public transport: McpTransport, private clientInfo = { name: 'agentx', version: '0' }) {}\n\n /** `initialize` handshake → `notifications/initialized`. Returns the server's init result. */\n async connect(): Promise<{ serverInfo?: { name?: string; version?: string }; capabilities?: any }> {\n await this.transport.start();\n const result = await this.transport.request('initialize', {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: {},\n clientInfo: this.clientInfo,\n });\n await this.transport.notify('notifications/initialized');\n return result ?? {};\n }\n\n async listTools(): Promise<McpToolSpec[]> {\n const r = await this.transport.request('tools/list');\n return Array.isArray(r?.tools) ? r.tools : [];\n }\n\n async callTool(name: string, args: unknown): Promise<unknown> {\n return this.transport.request('tools/call', { name, arguments: args ?? {} });\n }\n\n async listResources(): Promise<Array<{ uri: string; name?: string; mimeType?: string }>> {\n const r = await this.transport.request('resources/list');\n return Array.isArray(r?.resources) ? r.resources : [];\n }\n\n async readResource(uri: string): Promise<any> {\n return this.transport.request('resources/read', { uri });\n }\n\n async close(): Promise<void> {\n await this.transport.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Config-driven mounting — Claude-style `mcpServers: { name: {...} }`.\n// ---------------------------------------------------------------------------\n/** One server: stdio (`command`+`args`+`env`) OR http (`url`+`headers`). `disabled` skips it. */\nexport interface McpServerConfig {\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n cwd?: string;\n url?: string;\n headers?: Record<string, string>;\n /** `Authorization: Bearer <token>` sugar for http servers (headless-friendly; no OAuth flow). */\n bearerToken?: string;\n /** Auth strategy for http servers. `'oauth'` → the CLI resolves a token via the attended OAuth flow\n * (cli/mcpOAuth.ts) and injects it as `bearerToken` at mount; the core never runs the flow. Default none. */\n auth?: 'oauth' | 'none';\n timeoutMs?: number;\n disabled?: boolean;\n}\n\nexport interface MountedMcp {\n name: string;\n client: McpClient;\n tools: AgentTool[];\n /** Raw tool specs discovered at mount (already fetched internally) — exposed so deferred-mount\n * glue (`makeMcpToolSearchFromMounted`) needn't re-call `tools/list`. */\n specs: McpToolSpec[];\n serverInfo?: { name?: string; version?: string };\n /** The config this server was mounted with — lets a host remount it (`/mcp reconnect`). */\n config: McpServerConfig;\n}\n\n/** Build the transport for a server config (http when `url`, else stdio). Shared by every mount path. */\nfunction buildTransport(cfg: McpServerConfig): McpTransport {\n return cfg.url\n ? new HttpTransport({ url: cfg.url, headers: cfg.headers, bearerToken: cfg.bearerToken, timeoutMs: cfg.timeoutMs })\n : new StdioTransport({ command: cfg.command!, args: cfg.args, env: cfg.env, cwd: cfg.cwd, timeoutMs: cfg.timeoutMs });\n}\n\n/** Race `p` against `ms` (a per-server mount deadline). `ms` falsy/≤0 → no deadline. The caller\n * closes the half-open client on rejection so a hung server leaks nothing. */\nfunction withTimeout<T>(p: Promise<T>, ms: number | undefined, label: string): Promise<T> {\n if (!ms || ms <= 0) return p;\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`MCP \"${label}\" mount exceeded ${ms}ms`)), ms);\n (timer as any).unref?.();\n p.then((v) => { clearTimeout(timer); resolve(v); }, (e) => { clearTimeout(timer); reject(e); });\n });\n}\n\n/** Connect + discover + adapt, with optional per-server deadline; closes the client if it fails. */\nasync function mountWithDeadline(name: string, cfg: McpServerConfig, mountTimeoutMs?: number): Promise<MountedMcp> {\n const client = new McpClient(buildTransport(cfg));\n try {\n return await withTimeout((async () => {\n const init = await client.connect();\n const specs = await client.listTools();\n const tools = mcpToolsToAgentTools(specs, (tool, a) => client.callTool(tool, a), `mcp__${name}__`);\n return { name, client, tools, specs, serverInfo: init?.serverInfo, config: cfg } as MountedMcp;\n })(), mountTimeoutMs, name);\n } catch (e) {\n await client.close().catch((err) => log.debug(`close after failed mount of \"${name}\": ${err}`));\n throw e;\n }\n}\n\n/** Connect one server, discover its tools, and adapt them to prefixed AgentTools (`mcp__<name>__`). */\nexport function mountMcpServer(name: string, cfg: McpServerConfig): Promise<MountedMcp> {\n return mountWithDeadline(name, cfg);\n}\n\n/** Drop disabled/incomplete entries, warning on the incomplete ones. */\nfunction validEntries(servers: Record<string, McpServerConfig>): Array<[string, McpServerConfig]> {\n return Object.entries(servers).filter(([name, cfg]) => {\n if (!cfg || cfg.disabled) return false;\n if (!cfg.command && !cfg.url) { log.warn(`MCP server \"${name}\" needs a command (stdio) or url (http) — skipping`); return false; }\n return true;\n });\n}\n\n/** Log a mount failure (auth vs generic) without throwing — one broken server never blocks startup. */\nfunction logMountFailure(name: string, e: any): void {\n if (e instanceof McpAuthError) log.warn(`MCP \"${name}\" needs-auth: HTTP ${e.status} — set bearerToken or headers in its config; skipping`);\n else log.error(`MCP server \"${name}\" failed to mount: ${e?.message ?? e}`); // logged, then skipped\n}\n\n/**\n * Mount every configured server in PARALLEL (one slow/dead server no longer serializes the rest);\n * each may carry a `mountTimeoutMs` deadline. A server that fails is logged and skipped.\n */\nexport async function mountMcpServers(servers: Record<string, McpServerConfig> = {}, opts: { mountTimeoutMs?: number } = {}): Promise<MountedMcp[]> {\n const entries = validEntries(servers);\n const settled = await Promise.allSettled(entries.map(([name, cfg]) => mountWithDeadline(name, cfg, opts.mountTimeoutMs)));\n const out: MountedMcp[] = [];\n settled.forEach((r, i) => {\n const name = entries[i][0];\n // debug (not info): the CLI prints its own one-line mount summary — an always-on info here double-prints\n if (r.status === 'fulfilled') { out.push(r.value); log.debug(`MCP \"${name}\" mounted — ${r.value.tools.length} tool(s)${r.value.serverInfo?.name ? ` from ${r.value.serverInfo.name}` : ''}`); }\n else logMountFailure(name, r.reason);\n });\n return out;\n}\n\n/**\n * One-shot deferred MCP mount: connect every entry (failures dropped, never block startup) and\n * fold the survivors into a single `ToolSearch`/`McpCall` pair via `makeMcpToolSearchFromMounted`.\n * The node-only counterpart to that edge-safe helper — returns `mounted` too so callers can close\n * the clients on shutdown. Eager: it connects all servers up front. For lazy connect + a cached\n * catalog (zero connections on a turn that uses no MCP tool), use `mountMcpCatalog`.\n */\nexport async function mountMcpDeferred(\n servers: Record<string, McpServerConfig> = {},\n options?: McpToolSearchOptions & { mountTimeoutMs?: number },\n): Promise<{ tools: AgentTool[]; serverNames: string[]; toolCount: number; mounted: MountedMcp[] }> {\n const mounted = await mountMcpServers(servers, { mountTimeoutMs: options?.mountTimeoutMs });\n return { ...makeMcpToolSearchFromMounted(mounted, options), mounted };\n}\n\n// ---------------------------------------------------------------------------\n// Lazy connect + catalog cache — defer the CONNECTION, not just the schemas.\n// ---------------------------------------------------------------------------\n/** A persistent tool catalog: lets `mountMcpCatalog` build `ToolSearch` WITHOUT connecting when a\n * fresh entry exists. The lib defines the interface; the consumer supplies the store (RAM/disk).\n * Key = a hash of the server's resolved config (see `mcpConfigKey`). TTL/versioning is the store's. */\nexport interface McpCatalogStore {\n get(key: string): McpToolSpec[] | null;\n set(key: string, specs: McpToolSpec[]): void;\n}\n\n/** Stable cache key for a server config — command/args/cwd + env NAMES (not secret values), or\n * url + header names. Changing any of these invalidates the cached catalog; rotating a secret does not. */\nexport function mcpConfigKey(cfg: McpServerConfig): string {\n const parts = cfg.url\n ? ['http', cfg.url, ...Object.keys(cfg.headers ?? {}).sort()]\n : ['stdio', cfg.command ?? '', ...(cfg.args ?? []), cfg.cwd ?? '', ...Object.keys(cfg.env ?? {}).sort()];\n return createHash('sha256').update(parts.join('\\0')).digest('hex').slice(0, 16);\n}\n\n/** Default in-memory catalog: process-lifetime, hash-keyed, TTL-expiring (so a server that gains\n * tools is picked up after the TTL without a restart). Shared module singleton → cross-turn reuse. */\nexport class MemMcpCatalog implements McpCatalogStore {\n private m = new Map<string, { specs: McpToolSpec[]; exp: number }>();\n constructor(private ttlMs = 5 * 60_000) {}\n get(key: string): McpToolSpec[] | null {\n const e = this.m.get(key);\n if (!e) return null;\n if (Date.now() > e.exp) { this.m.delete(key); return null; }\n return e.specs;\n }\n set(key: string, specs: McpToolSpec[]): void { this.m.set(key, { specs, exp: Date.now() + this.ttlMs }); }\n}\n\n/** Opt-in warm pool: keeps stdio MCP clients connected across turns, reaping each after `ttlMs`\n * idle. Most stdio MCPs are per-turn by design, so this is off by default; HTTP servers are\n * stateless and never pooled. */\nexport class McpPool {\n private warm = new Map<string, { client: McpClient; timer: ReturnType<typeof setTimeout> }>();\n constructor(private ttlMs = 5 * 60_000) {}\n get(key: string): McpClient | null { const e = this.warm.get(key); if (!e) return null; this.arm(key, e); return e.client; }\n put(key: string, client: McpClient): void {\n const prev = this.warm.get(key); // overwrite: cancel the stale timer + close the displaced client\n if (prev) { clearTimeout(prev.timer); if (prev.client !== client) void prev.client.close().catch((err) => log.debug(`warm-pool replace close failed: ${err}`)); }\n const e = { client, timer: undefined as any };\n this.warm.set(key, e); this.arm(key, e);\n }\n private arm(key: string, e: { client: McpClient; timer: any }): void {\n clearTimeout(e.timer);\n e.timer = setTimeout(() => { void this.evict(key); }, this.ttlMs);\n (e.timer as any).unref?.();\n }\n private async evict(key: string): Promise<void> {\n const e = this.warm.get(key); if (!e) return;\n this.warm.delete(key);\n await e.client.close().catch((err) => log.debug(`warm-pool evict close failed: ${err}`));\n }\n async closeAll(): Promise<void> { for (const e of this.warm.values()) { clearTimeout(e.timer); await e.client.close().catch(() => {}); } this.warm.clear(); }\n}\n\nconst defaultCatalog = new MemMcpCatalog();\nconst defaultPool = new McpPool();\nconst defaultFailures = new Map<string, number>(); // configKey → cooldown-until epoch ms (negative cache)\nconst bgInflight = new Set<string>(); // configKey → background discovery in flight (dedupe)\nconst DEFAULT_FAILURE_COOLDOWN_MS = 60_000;\n\nexport interface McpCatalogOptions extends McpToolSearchOptions {\n /** Where to read/write discovered specs. Default: a shared process-lifetime `MemMcpCatalog`. */\n catalog?: McpCatalogStore;\n /** Per-server deadline for the connect+list on a cache MISS. A hung server is skipped, not blocking. */\n mountTimeoutMs?: number;\n /** Opt-in: keep stdio clients warm across turns (see `McpPool`). HTTP servers are never pooled. */\n keepWarm?: boolean;\n /** Warm-client pool. Default: a shared process-lifetime `McpPool` (only used when `keepWarm`). */\n pool?: McpPool;\n /** Negative cache: after a server fails/times out discovery, skip it for this long so it never\n * re-floors a turn at the deadline. Default 60s; the server is re-probed once the cooldown lapses. */\n failureCooldownMs?: number;\n /** Shared failure-cooldown map (configKey → until-epoch-ms). Default: a process-lifetime singleton. */\n failures?: Map<string, number>;\n /** Discovery policy on a cache MISS:\n * - `'connect'` (default): synchronously connect+list (deadline-bounded) — blocks until ready.\n * - `'cache-only'`: NEVER block the turn — serve only cached servers, and kick uncached discovery\n * to the background (so it's warm next turn). Pair with a boot/timer `warmMcpCatalog` so the\n * first turn is already a cache hit. */\n discover?: 'connect' | 'cache-only';\n}\n\ninterface Discovered { name: string; cfg: McpServerConfig; key: string; specs: McpToolSpec[] }\n\n/** Connect+list one server (deadline-bounded), populate the catalog on success / negative-cache on\n * failure. Closes the client unless `keepWarm` keeps it for the lazy-call phase. */\nasync function connectAndList(name: string, cfg: McpServerConfig, key: string, opts: McpCatalogOptions): Promise<Discovered | null> {\n const catalog = opts.catalog ?? defaultCatalog;\n const pool = opts.pool ?? defaultPool;\n const failures = opts.failures ?? defaultFailures;\n const client = new McpClient(buildTransport(cfg));\n try {\n const specs = await withTimeout((async () => { await client.connect(); return client.listTools(); })(), opts.mountTimeoutMs, name);\n catalog.set(key, specs);\n failures.delete(key); // recovered\n if (opts.keepWarm && !cfg.url) pool.put(key, client); // reuse this connection for the lazy-call phase\n else await client.close().catch(() => {});\n return { name, cfg, key, specs };\n } catch (e: any) {\n await client.close().catch(() => {});\n failures.set(key, Date.now() + (opts.failureCooldownMs ?? DEFAULT_FAILURE_COOLDOWN_MS));\n logMountFailure(name, e);\n return null;\n }\n}\n\n/** Resolve one server to its specs: cache hit → no connection; in failure-cooldown → skip;\n * otherwise connect (or, in `cache-only` mode, kick background discovery and skip for now). */\nasync function discoverOne(name: string, cfg: McpServerConfig, opts: McpCatalogOptions): Promise<Discovered | null> {\n const catalog = opts.catalog ?? defaultCatalog;\n const failures = opts.failures ?? defaultFailures;\n const key = mcpConfigKey(cfg);\n const cached = catalog.get(key);\n if (cached) return { name, cfg, key, specs: cached };\n const until = failures.get(key);\n if (until && until > Date.now()) return null; // negative cache → known-down, skip\n if (opts.discover === 'cache-only') {\n if (!bgInflight.has(key)) { bgInflight.add(key); void connectAndList(name, cfg, key, opts).finally(() => bgInflight.delete(key)); }\n return null; // not available THIS turn; warms for the next one\n }\n return connectAndList(name, cfg, key, opts);\n}\n\n/**\n * Lazy + cached MCP mount. Builds the `ToolSearch`/`McpCall` pair from the CACHED catalog when one\n * exists — connecting NOTHING. On a cache miss it (by default) connects once (parallel, deadline-\n * bounded), lists, caches, then disconnects (or keeps warm if `keepWarm`). A server is connected only\n * when one of its tools is actually invoked via `McpCall` (memoized per turn; reused from the warm\n * pool if enabled). A server that fails/times out is negative-cached (`failureCooldownMs`) so it\n * never re-floors a later turn.\n *\n * For zero turn-path latency even on a cold process, set `discover: 'cache-only'` and call\n * `warmMcpCatalog` at boot + on a timer: the turn then NEVER synchronously connects — it serves\n * cached servers and warms the rest in the background.\n *\n * Per-turn cost: a turn using NO MCP tool → 0 connections; a turn using one → exactly one.\n */\nexport async function mountMcpCatalog(\n servers: Record<string, McpServerConfig> = {},\n opts: McpCatalogOptions = {},\n): Promise<{ tools: AgentTool[]; serverNames: string[]; toolCount: number; connect(name: string): Promise<McpClient>; close(): Promise<void> }> {\n const pool = opts.pool ?? defaultPool;\n const discovered = (await Promise.all(validEntries(servers).map(([name, cfg]) => discoverOne(name, cfg, opts)))).filter(Boolean) as Discovered[];\n\n const byName = new Map(discovered.map((d) => [d.name, d]));\n const inflight = new Map<string, Promise<McpClient>>(); // memoize per server → concurrent calls share one connect\n const live: McpClient[] = []; // non-warm clients opened this turn (closed on close())\n\n const connect = (name: string): Promise<McpClient> => {\n let p = inflight.get(name);\n if (p) return p;\n p = (async () => {\n const d = byName.get(name);\n if (!d) throw new Error(`MCP server '${name}' is not in the catalog`);\n const warmable = opts.keepWarm && !d.cfg.url;\n if (warmable) { const w = pool.get(d.key); if (w) return w; }\n const client = new McpClient(buildTransport(d.cfg));\n await client.connect();\n if (warmable) pool.put(d.key, client); else live.push(client);\n return client;\n })();\n inflight.set(name, p);\n return p;\n };\n\n const search = makeLazyMcpToolSearch(\n discovered.map((d) => ({ name: d.name, specs: d.specs })),\n async (server, rawName, args) => (await connect(server)).callTool(rawName, args),\n opts,\n );\n const close = async (): Promise<void> => { for (const c of live) await c.close().catch(() => {}); live.length = 0; inflight.clear(); };\n return { ...search, connect, close };\n}\n\n/**\n * Off-turn catalog warm-up: do the one cold discovery pass (connect + list, parallel, deadline-\n * bounded) so a later `mountMcpCatalog(..., { discover: 'cache-only' })` is a cache HIT. Call at\n * server boot and on a timer (cadence < the catalog TTL). Cache hits cost nothing; down servers are\n * negative-cached so turns never touch them. Returns which servers warmed vs failed.\n */\nexport async function warmMcpCatalog(\n servers: Record<string, McpServerConfig> = {},\n opts: McpCatalogOptions = {},\n): Promise<{ warmed: string[]; failed: string[]; toolCount: number }> {\n const entries = validEntries(servers);\n const discovered = (await Promise.all(entries.map(([name, cfg]) => discoverOne(name, cfg, { ...opts, discover: 'connect' })))).filter(Boolean) as Discovered[];\n const warmedNames = new Set(discovered.map((d) => d.name));\n return {\n warmed: discovered.map((d) => d.name),\n failed: entries.map(([n]) => n).filter((n) => !warmedNames.has(n)),\n toolCount: discovered.reduce((s, d) => s + d.specs.length, 0),\n };\n}\n","// Import the log module directly from libx.js source: libx.js's main bundle\n// doesn't re-export `log` as a named ESM export, and source-importing keeps\n// libx.js patches live (no rebuild) — matching the `bun link` workflow.\nimport { log } from 'libx.js/src/modules/log';\n\n/** Component-scoped logger (libx.js). debug/verbose gated via DEBUG env/localStorage. */\nexport const forComponent = (name: string) => log.forComponent(name);\nexport { log };\n","/**\n * Lexical relevance — no deps, deterministic, edge-safe. Ranks catalog entries (skills,\n * commands, memories, docs) by a task hint so a LARGE catalog doesn't dump every entry into\n * the prompt. TF-IDF weighted: rare terms in the corpus score higher than common ones.\n */\n\nconst STOP = new Set([\n 'the', 'and', 'for', 'with', 'that', 'this', 'from', 'into', 'your', 'you', 'are', 'was', 'will',\n 'use', 'using', 'run', 'add', 'fix', 'make', 'file', 'files', 'code', 'please', 'need', 'want', 'should', 'all',\n]);\n\n/** Lowercased alphanumeric tokens of length ≥ 3, minus a few stopwords. */\nexport function tokenize(s: string): Set<string> {\n const out = new Set<string>();\n for (const w of String(s ?? '').toLowerCase().match(/[a-z0-9]+/g) ?? []) {\n if (w.length >= 3 && !STOP.has(w)) out.add(w);\n }\n return out;\n}\n\n/** Build IDF weights from a corpus of texts. Rare terms get higher weight. */\nexport function idfWeights(corpus: string[]): Map<string, number> {\n const N = corpus.length;\n if (N === 0) return new Map();\n const df = new Map<string, number>();\n for (const doc of corpus) for (const t of tokenize(doc)) df.set(t, (df.get(t) ?? 0) + 1);\n const idf = new Map<string, number>();\n for (const [t, n] of df) idf.set(t, Math.log((N + 1) / (n + 1)) + 1); // smooth IDF\n return idf;\n}\n\n/** TF-IDF weighted relevance score. Without IDF, falls back to flat token overlap (backward-compatible). */\nexport function relevanceScore(text: string, queryTokens: Set<string>, idf?: Map<string, number>): number {\n if (queryTokens.size === 0) return 0;\n const t = tokenize(text);\n let score = 0;\n for (const q of queryTokens) if (t.has(q)) score += idf?.get(q) ?? 1;\n return score;\n}\n\n/**\n * Split `items` into the `k` most relevant to `query` (by TF-IDF overlap with `text(item)`)\n * and the rest, preserving each item's original order within its group. Returns everything in\n * `kept` (empty `rest`) when `items.length <= k` or `query` is blank — so it's a no-op for\n * small lists. When `corpus` is provided, builds IDF weights for better ranking.\n */\nexport function topByRelevance<T>(items: T[], query: string, text: (x: T) => string, k: number, corpus?: string[]): { kept: T[]; rest: T[] } {\n if (!Number.isInteger(k) || k < 1) return { kept: items, rest: [] }; // invalid k → no-op (keep all), never hide\n if (items.length <= k || !query.trim()) return { kept: items, rest: [] };\n const q = tokenize(query);\n if (q.size === 0) return { kept: items.slice(0, k), rest: items.slice(k) };\n const idf = corpus?.length ? idfWeights(corpus) : undefined;\n const scored = items.map((x, i) => ({ i, s: relevanceScore(text(x), q, idf) }));\n scored.sort((a, b) => b.s - a.s || a.i - b.i); // best score first; stable by original index\n const keep = new Set(scored.slice(0, k).map((e) => e.i));\n return { kept: items.filter((_, i) => keep.has(i)), rest: items.filter((_, i) => !keep.has(i)) };\n}\n","import type { AgentTool } from './tools';\nimport { topByRelevance } from './relevance';\n\n/**\n * MCP bridge — adapt a Model Context Protocol tool list into the agent's AgentTool[],\n * so any MCP server's tools become first-class agent tools (edge-safe: you supply the\n * transport via `callTool`; this module has no node/network dependency of its own).\n *\n * Pass the server's advertised tools + a `callTool(name, args)` that performs the call\n * (over stdio/HTTP/whatever you wire up); each becomes an AgentTool the Agent can use.\n */\nexport interface McpToolSpec {\n name: string;\n description?: string;\n /** JSON Schema for the tool's arguments (MCP's `inputSchema`). */\n inputSchema?: object;\n}\n\n/** Perform an MCP tool call; return its textual result. Throw to surface an error to the model. */\nexport type McpCall = (name: string, args: any) => Promise<unknown>;\n\nexport interface McpImage { mimeType: string; data: string }\nexport interface McpToolResult { text: string; images?: McpImage[] }\n\n/** Normalize an MCP call result into text + optional image blocks. */\nfunction toResult(result: unknown): McpToolResult {\n if (result == null) return { text: '' };\n if (typeof result === 'string') return { text: result };\n const content = (result as any).content;\n if (Array.isArray(content)) {\n const texts: string[] = [];\n const images: McpImage[] = [];\n for (const c of content) {\n if (c?.type === 'image' && typeof c.data === 'string' && c.mimeType) {\n images.push({ mimeType: c.mimeType, data: c.data });\n } else if (typeof c?.text === 'string') {\n texts.push(c.text);\n } else {\n texts.push(JSON.stringify(c));\n }\n }\n const text = texts.join('\\n');\n if (text || images.length) return { text, ...(images.length ? { images } : {}) };\n }\n return { text: JSON.stringify(result) };\n}\n\nfunction toText(result: unknown): string { return toResult(result).text; }\n\n/**\n * An MCP call had NO deadline of its own: a server that never answers hung the tool call forever, and\n * (declaring nothing) it was silently assumed prompt by every transport above the loop. Bound it here so\n * the tool can honestly declare `maxDurationMs`, and so a hung server produces a statement of what\n * happened rather than a stall.\n */\nexport const MCP_CALL_TIMEOUT_MS = 300_000;\nconst TIMED_OUT = Symbol('mcp-timeout');\nasync function withDeadline<T>(p: Promise<T>, ms: number): Promise<T | typeof TIMED_OUT> {\n let t: any;\n try {\n return await Promise.race([p, new Promise<typeof TIMED_OUT>((res) => { t = setTimeout(() => res(TIMED_OUT), ms); })]);\n } finally { clearTimeout(t); }\n}\nconst mcpTimedOut = (name: string, ms = MCP_CALL_TIMEOUT_MS) =>\n `[${name}] no response after ${ms / 1000}s — the MCP server never answered. The call's outcome is UNKNOWN: `\n + `do not assume it ran or that it failed. Verify the state (or pick a bounded/narrower call) before retrying.`;\n\n/** Adapt one MCP tool spec into an AgentTool backed by `callTool`. */\nexport function mcpToolToAgentTool(spec: McpToolSpec, callTool: McpCall, prefix = 'mcp__', timeoutMs = MCP_CALL_TIMEOUT_MS): AgentTool {\n return {\n name: `${prefix}${spec.name}`,\n description: spec.description ?? `MCP tool ${spec.name}`,\n parameters: spec.inputSchema ?? { type: 'object', properties: {} },\n maxDurationMs: timeoutMs,\n async run(args, _ctx) {\n const raw = await withDeadline(callTool(spec.name, args ?? {}), timeoutMs);\n if (raw === TIMED_OUT) return mcpTimedOut(spec.name, timeoutMs);\n const r = toResult(raw);\n return r.images?.length ? r : r.text;\n },\n };\n}\n\n/** Adapt a whole MCP tool list into AgentTool[] (names prefixed to avoid collisions).\n * Optional `filter` pre-narrows the set at mount time (host allowlist), so a server with\n * hundreds of tools needn't mount them all eagerly. */\nexport function mcpToolsToAgentTools(specs: McpToolSpec[], callTool: McpCall, prefix = 'mcp__', filter?: (spec: McpToolSpec) => boolean): AgentTool[] {\n return (filter ? specs.filter(filter) : specs).map((s) => mcpToolToAgentTool(s, callTool, prefix));\n}\n\nexport interface McpToolSearchOptions {\n /** Prefix stripped from / shown on tool names (cosmetic; mirrors the adapter prefix). Default 'mcp__'. */\n prefix?: string;\n /** Max tools returned per `ToolSearch` query. Default 10. */\n maxResults?: number;\n /** Per-call deadline for `McpCall`. Default MCP_CALL_TIMEOUT_MS. */\n timeoutMs?: number;\n}\n\n/** Render one spec for the search result: name, description, and its argument schema (so the\n * model knows how to call it via `McpCall`). */\nfunction describeSpec(s: McpToolSpec): string {\n const schema = s.inputSchema ? `\\n args: ${JSON.stringify(s.inputSchema)}` : '';\n return `${s.name} — ${s.description ?? '(no description)'}${schema}`;\n}\n\n/**\n * Deferred-mount mode (ToolSearch-equivalent) for large MCP tool sets. Instead of mounting N\n * tools into the wire schema (cost + latency + model confusion past a few dozen), mount exactly\n * TWO bounded tools regardless of N:\n * - `ToolSearch({ query })` — ranks the catalog by relevance and returns the top matches with\n * their argument schemas, so the model discovers what's available on demand.\n * - `McpCall({ name, args })` — invokes any catalog tool by name through the same transport.\n * Keep `mcpToolsToAgentTools` (eager) as the default for small sets.\n */\nexport function makeMcpToolSearch(specs: McpToolSpec[], callTool: McpCall, options: McpToolSearchOptions = {}): AgentTool[] {\n const maxResults = options.maxResults ?? 10;\n const callTimeoutMs = options.timeoutMs ?? MCP_CALL_TIMEOUT_MS;\n const byName = new Map(specs.map((s) => [s.name, s]));\n const catalogLine = `${specs.length} MCP tool(s) available — search by keyword, then call by exact name.`;\n\n const searchTool: AgentTool = {\n name: 'ToolSearch',\n description: `Search the available MCP tools by keyword (${catalogLine}). Returns matching tool names + their argument schemas; call one with \\`McpCall\\`.`,\n parameters: { type: 'object', required: ['query'], properties: { query: { type: 'string', description: 'keywords to match against tool name + description' } } },\n async run({ query }) {\n const q = String(query ?? '').trim();\n if (!q) return catalogLine;\n const { kept } = topByRelevance(specs, q, (s) => `${s.name} ${s.description ?? ''}`, maxResults);\n if (!kept.length) return `(no MCP tool matches \"${q}\" — try broader keywords)`;\n return kept.map(describeSpec).join('\\n');\n },\n };\n\n const callMcpTool: AgentTool = {\n name: 'McpCall',\n description: 'Call an MCP tool discovered via `ToolSearch`, by its exact name. Pass its arguments as `args`.',\n parameters: {\n type: 'object',\n required: ['name'],\n properties: {\n name: { type: 'string', description: 'exact tool name from ToolSearch' },\n args: { type: 'object', description: 'arguments object for the tool (per its schema)' },\n },\n },\n maxDurationMs: callTimeoutMs,\n async run({ name, args }) {\n const n = String(name ?? '');\n if (!byName.has(n)) return `Error: unknown MCP tool '${n}'. Use ToolSearch to find valid names.`;\n const raw = await withDeadline(callTool(n, args ?? {}), callTimeoutMs);\n if (raw === TIMED_OUT) return mcpTimedOut(n, callTimeoutMs);\n const r = toResult(raw);\n return r.images?.length ? r : r.text;\n },\n };\n\n return [searchTool, callMcpTool];\n}\n\n/** Minimal shape of a mounted MCP server this module needs — structurally satisfied by\n * `MountedMcp` from `mcp.client.ts`, kept local so `mcp.ts` stays node-free/edge-safe. */\nexport interface MountedMcpLike {\n name: string;\n specs: McpToolSpec[];\n client: { callTool(name: string, args: unknown): Promise<unknown> };\n}\n\n/** A flattened catalog entry's origin: which server owns it + the un-prefixed raw tool name. */\nexport interface McpRoute { server: string; rawName: string }\n\n/** Flatten servers' specs into one `mcp__<server>__<tool>` namespace + a display→origin map.\n * Sanitizing/truncating to provider name rules can MERGE distinct names (`a/b` & `a:b` → `a_b`),\n * so dedupe with a numeric suffix — a silent overwrite would orphan a tool. */\nexport function buildMcpCatalog(servers: { name: string; specs: McpToolSpec[] }[]): { specs: McpToolSpec[]; routes: Map<string, McpRoute> } {\n const specs: McpToolSpec[] = [];\n const routes = new Map<string, McpRoute>();\n for (const m of servers) {\n for (const s of m.specs) {\n const base = `mcp__${m.name}__${s.name}`.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 128);\n let display = base;\n for (let i = 2; routes.has(display); i++) display = `${base.slice(0, 128 - String(i).length - 1)}_${i}`;\n specs.push({ name: display, description: s.description, inputSchema: s.inputSchema });\n routes.set(display, { server: m.name, rawName: s.name });\n }\n }\n return { specs, routes };\n}\n\n/** Resolve a routed MCP call to its result. Throw to surface an error to the model. */\nexport type McpRouteResolver = (server: string, rawName: string, args: any) => Promise<unknown>;\n\n/** Shared core: wire the `ToolSearch`/`McpCall` pair over a prebuilt catalog, dispatching each\n * call through `resolve(server, rawName, args)` (eager → a mounted client; lazy → connect-on-call). */\nfunction searchOverCatalog(servers: string[], specs: McpToolSpec[], routes: Map<string, McpRoute>, resolve: McpRouteResolver, options?: McpToolSearchOptions) {\n const tools = specs.length\n ? makeMcpToolSearch(specs, (name, args) => {\n const r = routes.get(name);\n if (!r) throw new Error(`unknown MCP tool '${name}' — use ToolSearch to find valid names`);\n return resolve(r.server, r.rawName, args ?? {});\n }, options)\n : [];\n return { tools, serverNames: servers, toolCount: specs.length };\n}\n\n/**\n * Ergonomic deferred-mount over already-mounted MCP servers: flattens their specs into one\n * `mcp__<server>__<tool>` namespace and wires a single `ToolSearch`/`McpCall` pair that routes\n * each call to the owning server's RAW `callTool` (so result normalization happens exactly once —\n * double-normalizing corrupts image blocks).\n */\nexport function makeMcpToolSearchFromMounted(\n mounted: MountedMcpLike[],\n options?: McpToolSearchOptions,\n): { tools: AgentTool[]; serverNames: string[]; toolCount: number } {\n const { specs, routes } = buildMcpCatalog(mounted);\n const byName = new Map(mounted.map((m) => [m.name, m]));\n return searchOverCatalog(mounted.map((m) => m.name), specs, routes, (server, rawName, args) => byName.get(server)!.client.callTool(rawName, args), options);\n}\n\n/**\n * Lazy variant: same `ToolSearch`/`McpCall` surface, but the server isn't required to be connected.\n * Each `McpCall` resolves the owning server from the catalog and `resolve(server, rawName, args)`\n * connects-on-demand — so a turn that calls no MCP tool opens ZERO connections. Edge-safe: the\n * caller (`mountMcpCatalog` in `mcp.client.ts`) supplies the node-side connect/pool logic.\n */\nexport function makeLazyMcpToolSearch(\n servers: { name: string; specs: McpToolSpec[] }[],\n resolve: McpRouteResolver,\n options?: McpToolSearchOptions,\n): { tools: AgentTool[]; serverNames: string[]; toolCount: number } {\n const { specs, routes } = buildMcpCatalog(servers);\n return searchOverCatalog(servers.map((s) => s.name), specs, routes, resolve, options);\n}\n"],"mappings":"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","names":["log"]}
@@ -385,6 +385,21 @@ interface AgentTool {
385
385
  name: string;
386
386
  description: string;
387
387
  parameters: object;
388
+ /**
389
+ * Longest wall-clock time this tool can legitimately take before it returns on its own.
390
+ * Declared so a TRANSPORT above the loop (e.g. the cursor helper's per-call watchdog) can derive a
391
+ * deadline with real headroom instead of carrying an independently-maintained magic number that
392
+ * drifts into a tie with the tool's own cap — a tie the transport wins, destroying the tool's real
393
+ * result. Omit for tools that return promptly.
394
+ */
395
+ maxDurationMs?: number;
396
+ /**
397
+ * Is `maxDurationMs` SELF-ENFORCED — does this tool abort itself at that bound and return?
398
+ * An enforced bound cannot be overrun, so a transport above it needs only modest slack; an ADVISORY
399
+ * one (the default) may be exceeded, so the transport keeps generous headroom over it. Declaring a
400
+ * bound you do not actually enforce is worse than declaring none — say `true` only when you abort.
401
+ */
402
+ maxDurationEnforced?: boolean;
388
403
  run(args: any, ctx: ToolContext): Promise<string | {
389
404
  text: string;
390
405
  images?: {
@@ -1,4 +1,4 @@
1
- import { A as AgentTool } from './tools-Byyh_bae.js';
1
+ import { A as AgentTool } from './tools-BWzPQVy0.js';
2
2
  import '@livx.cc/wcli/core';
3
3
 
4
4
  /**
@@ -70,6 +70,10 @@ interface RealShellOptions {
70
70
  * cwd+tmp, network blocked unless granted. `true` = defaults; commands FAIL (don't silently run
71
71
  * unsandboxed) if no wrapper exists on this platform. See src/shell.sandbox.ts. */
72
72
  osSandbox?: boolean | Partial<OsSandboxOptions>;
73
+ /** Does a background job outlive the current run? False on one-shot hosts (the process exits and
74
+ * `killOnExit` reaps the job), which the Shell tool's description must say rather than promise
75
+ * cross-turn survival it won't get. Default true. */
76
+ jobsSurviveRun?: boolean;
73
77
  }
74
78
  type JobStatus = 'running' | 'exited' | 'killed' | 'error';
75
79
  interface ShellJobConfig {
@@ -83,7 +87,25 @@ interface ShellJobConfig {
83
87
  killOnExit?: boolean;
84
88
  /** Tier-1 OS sandbox for background jobs too (same semantics as RealShellOptions.osSandbox). */
85
89
  osSandbox?: boolean | Partial<OsSandboxOptions>;
90
+ /**
91
+ * Called once when a background job reaches a terminal state. Claude-Code parity: a background task
92
+ * that finishes has to reach the MODEL, or `background:true` is a write-only hole — the model starts
93
+ * a job, forgets it, and never collects the result. The host wires this to `agent.inject(...)`, which
94
+ * folds the notice in at the next step boundary (and re-opens the turn if the model was about to stop).
95
+ */
96
+ onExit?: JobExitHandler;
86
97
  }
98
+ interface JobExitNotice {
99
+ id: string;
100
+ command: string;
101
+ status: JobStatus;
102
+ exitCode?: number;
103
+ tail: string;
104
+ }
105
+ type JobExitHandler = (n: JobExitNotice) => void;
106
+ /** The exact text a completion notice becomes. Shared so every host (CLI, presets, embedders) pushes the
107
+ * same thing back to the model instead of each inventing its own wording. */
108
+ declare function formatJobExit(n: JobExitNotice): string;
87
109
  /**
88
110
  * Per-session registry of background `/bin/sh` jobs. Backs `Shell({background:true})` and the
89
111
  * `ShellOutput`/`ShellStatus`/`ShellKill` tools. Output accumulates into a tail-capped ring so a
@@ -95,6 +117,18 @@ declare class ShellJobRegistry {
95
117
  private seq;
96
118
  constructor(cfg: ShellJobConfig);
97
119
  start(command: string): Promise<string>;
120
+ /** Fire `onExit` at most once per job, with the tail so the model can act without a second round-trip. */
121
+ private notified;
122
+ private notifyExit;
123
+ /**
124
+ * Wire (or rewire) the completion callback AFTER construction. A host that only gets its agent handle
125
+ * once the Agent is constructed (the library's own `fullAgentOptions` preset, any embedder) could not
126
+ * pass `onExit` up front, so background completions were silently REPL-only. Set it here instead.
127
+ */
128
+ setOnExit(fn: JobExitHandler | undefined): void;
129
+ /** Whether a finished job will actually be reported to the model. The Shell tool's description reads
130
+ * this so it can't promise a completion notice on a host that discards it. */
131
+ get notifiesOnExit(): boolean;
98
132
  /** Current tail output for a job (null = no such job). */
99
133
  output(id: string): string | null;
100
134
  status(id: string): {
@@ -115,4 +149,4 @@ declare function makeRealShellTool(options: RealShellOptions): AgentTool;
115
149
  /** Build the background-job companion tools (ShellOutput / ShellStatus / ShellKill) over a registry. */
116
150
  declare function makeShellJobTools(registry: ShellJobRegistry): AgentTool[];
117
151
 
118
- export { type JobStatus, type RealShellOptions, type ShellJobConfig, ShellJobRegistry, type SpawnFn, type SpawnedProcess, makeRealShellTool, makeShellJobTools };
152
+ export { type JobExitHandler, type JobExitNotice, type JobStatus, type RealShellOptions, type ShellJobConfig, ShellJobRegistry, type SpawnFn, type SpawnedProcess, formatJobExit, makeRealShellTool, makeShellJobTools };
@@ -287,6 +287,8 @@ function makeWebFetchTool(options = {}) {
287
287
  return {
288
288
  name: "WebFetch",
289
289
  description: "Fetch an http/https URL and return its readable text (HTML is stripped to text). Use to read docs or web pages. Returns the status line then up to ~100k chars of content.",
290
+ // Declared so a transport above the loop sizes its deadline around this one (see AgentTool.maxDurationMs).
291
+ maxDurationMs: timeoutMs,
290
292
  parameters: { type: "object", required: ["url"], properties: { url: { type: "string", description: "absolute http(s) URL" } } },
291
293
  async run({ url }) {
292
294
  const doFetch = options.fetch ?? globalThis.fetch;
@@ -564,6 +566,11 @@ async function nodeSpawn() {
564
566
  if (!_spawn) _spawn = (await import("child_process")).spawn;
565
567
  return _spawn;
566
568
  }
569
+ function formatJobExit(n) {
570
+ return `[background job ${n.id} ${n.status}${n.exitCode != null ? ` exit ${n.exitCode}` : ""}] \`${n.command}\`
571
+ ` + (n.tail ? `${n.tail}
572
+ ` : "(no output)\n") + `Read the full output with ShellOutput({id:"${n.id}"}).`;
573
+ }
567
574
  var ShellJobRegistry = class {
568
575
  constructor(cfg) {
569
576
  this.cfg = cfg;
@@ -592,12 +599,14 @@ var ShellJobRegistry = class {
592
599
  job.status = "error";
593
600
  append(`
594
601
  [error] ${err?.message ?? err}`);
602
+ this.notifyExit(id, job);
595
603
  }
596
604
  });
597
605
  proc.on("close", (code) => {
598
606
  if (job.status === "running") {
599
607
  job.status = "exited";
600
608
  job.exitCode = code ?? void 0;
609
+ this.notifyExit(id, job);
601
610
  }
602
611
  });
603
612
  } catch (e) {
@@ -607,6 +616,29 @@ var ShellJobRegistry = class {
607
616
  this.jobs.set(id, job);
608
617
  return id;
609
618
  }
619
+ /** Fire `onExit` at most once per job, with the tail so the model can act without a second round-trip. */
620
+ notified = /* @__PURE__ */ new Set();
621
+ notifyExit(id, job) {
622
+ if (this.notified.has(id) || !this.cfg.onExit) return;
623
+ this.notified.add(id);
624
+ try {
625
+ this.cfg.onExit({ id, command: job.command, status: job.status, exitCode: job.exitCode, tail: clean(job.buf).slice(-4e3) });
626
+ } catch {
627
+ }
628
+ }
629
+ /**
630
+ * Wire (or rewire) the completion callback AFTER construction. A host that only gets its agent handle
631
+ * once the Agent is constructed (the library's own `fullAgentOptions` preset, any embedder) could not
632
+ * pass `onExit` up front, so background completions were silently REPL-only. Set it here instead.
633
+ */
634
+ setOnExit(fn) {
635
+ this.cfg.onExit = fn;
636
+ }
637
+ /** Whether a finished job will actually be reported to the model. The Shell tool's description reads
638
+ * this so it can't promise a completion notice on a host that discards it. */
639
+ get notifiesOnExit() {
640
+ return !!this.cfg.onExit;
641
+ }
610
642
  /** Current tail output for a job (null = no such job). */
611
643
  output(id) {
612
644
  return this.jobs.get(id)?.buf ?? (this.jobs.has(id) ? "" : null);
@@ -639,9 +671,19 @@ var ShellJobRegistry = class {
639
671
  function makeRealShellTool(options) {
640
672
  const defaultTimeoutMs = options.timeoutMs ?? 12e4;
641
673
  const maxTimeoutMs = Math.max(options.maxTimeoutMs ?? 6e5, defaultTimeoutMs);
674
+ const backgroundDoc = () => {
675
+ const base = "Set `background:true` for long-running processes (servers, watchers) and for anything that already timed out in the foreground \u2014 returns a job id immediately; poll with ShellOutput/ShellStatus, stop with ShellKill. ";
676
+ const notify = options.registry?.notifiesOnExit ? "Its completion is reported back to you when it finishes." : "NOTE: nothing will tell you when it finishes \u2014 poll ShellOutput/ShellStatus yourself, or you will never see its result.";
677
+ const survival = options.jobsSurviveRun === false ? "The job does NOT survive the end of this run, so do not stop while you still need its result." : "It keeps running across turns.";
678
+ return `${base}${notify} ${survival}`;
679
+ };
642
680
  return {
643
681
  name: "Shell",
644
- description: `Run a shell command via /bin/sh in the working directory. Executes any installed binary \u2014 ls, cat, grep, git, bun, node, curl, scripts, etc. Returns combined stdout+stderr; non-zero exits are prefixed \`[exit N]\`. Runs non-interactively with no terminal (stdin is /dev/null): commands that prompt for input fail fast rather than hang \u2014 for privileged actions use a non-interactive flag (e.g. \`sudo -n\`), or ask the user to run the command themselves. Each command is killed after ${defaultTimeoutMs}ms (result \`[exit 124]\` with whatever output it produced) \u2014 pass \`timeoutMs\` (max ${maxTimeoutMs}) for a legitimately slower command, and always bound network commands yourself (e.g. \`curl -m 10\`). Set \`background:true\` for long-running processes (servers, watchers) \u2014 returns a job id immediately; poll with ShellOutput, stop with ShellKill.`,
682
+ get description() {
683
+ return `Run a shell command via /bin/sh in the working directory. Executes any installed binary \u2014 ls, cat, grep, git, bun, node, curl, scripts, etc. Returns combined stdout+stderr; non-zero exits are prefixed \`[exit N]\`. Runs non-interactively with no terminal (stdin is /dev/null): commands that prompt for input fail fast rather than hang \u2014 for privileged actions use a non-interactive flag (e.g. \`sudo -n\`), or ask the user to run the command themselves. Each command is killed after ${defaultTimeoutMs}ms (result \`[exit 124]\` with whatever output it produced) \u2014 pass \`timeoutMs\` (max ${maxTimeoutMs}) for a legitimately slower command, and always bound network commands yourself (e.g. \`curl -m 10\`). ` + backgroundDoc();
684
+ },
685
+ // Declared so transports above the agent loop can size their own deadline with headroom (see AgentTool.maxDurationMs).
686
+ maxDurationMs: maxTimeoutMs,
645
687
  parameters: {
646
688
  type: "object",
647
689
  required: ["command"],
@@ -651,9 +693,10 @@ function makeRealShellTool(options) {
651
693
  timeoutMs: { type: "number", description: "wall-clock cap for THIS command, overriding the default (clamped to the tool max)" }
652
694
  }
653
695
  },
654
- async run({ command, background, timeoutMs: askedTimeoutMs }, ctx) {
696
+ async run({ command, background, timeoutMs: askedTimeoutMs, timeout: aliasTimeout }, ctx) {
655
697
  const cmd = String(command ?? "");
656
- const timeoutMs = Number.isFinite(askedTimeoutMs) && Number(askedTimeoutMs) > 0 ? Math.min(Number(askedTimeoutMs), maxTimeoutMs) : defaultTimeoutMs;
698
+ const asked = Number.isFinite(askedTimeoutMs) ? askedTimeoutMs : aliasTimeout;
699
+ const timeoutMs = Number.isFinite(asked) && Number(asked) > 0 ? Math.min(Number(asked), maxTimeoutMs) : defaultTimeoutMs;
657
700
  if (!cmd.trim()) return "[exit 1] empty command";
658
701
  if (background) {
659
702
  if (!options.registry) return "Error: background execution is not enabled in this host (no job registry).";
@@ -742,8 +785,9 @@ function makeRealShellTool(options) {
742
785
  };
743
786
  }
744
787
  function reasonFor(timedOut, timeoutMs, body) {
745
- const head = timedOut ? `[exit 124] timed out after ${timeoutMs}ms (killed)` : "[exit 130] cancelled (killed)";
788
+ const head = timedOut ? `[exit 124] timed out after ${timeoutMs}ms (killed). Re-running this command unchanged will time out again. Either re-run it with background:true (returns a job id immediately; poll with ShellOutput/ShellStatus, stop with ShellKill) or narrow it so it can finish \u2014 bound it, scope it, or ask for less.` : "[exit 130] cancelled (killed)";
746
789
  return body ? `${head}
790
+ Partial output before the kill:
747
791
  ${body}` : head;
748
792
  }
749
793
  var NO_JOB = (id) => `Error: no background job '${id}'. Use ShellStatus with no id to list jobs, or start one with Shell({background:true}).`;
@@ -787,6 +831,7 @@ ${clean(out) || "(no output yet)"}`;
787
831
  }
788
832
  export {
789
833
  ShellJobRegistry,
834
+ formatJobExit,
790
835
  makeRealShellTool,
791
836
  makeShellJobTools
792
837
  };