@livx.cc/agentx 0.99.39 → 0.99.41

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-S-ArIR3o.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-D5eN0JyQ.js';
2
+ import { A as AgentTool } from './tools-S-ArIR3o.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"]}
package/dist/models.d.ts CHANGED
@@ -10,6 +10,20 @@
10
10
  declare const MODEL_ALIASES: Record<string, string>;
11
11
  /** Resolve an alias to a concrete id. A provider prefix ("anthropic/") is preserved; unknown ids pass through. */
12
12
  declare function resolveModelAlias(input: string): string;
13
+ /** The provider of a model id ("cursor/x" -> "cursor"). Provider-specific `providerOptions` are only
14
+ * portable within the same provider — leaking them across is a hard 400.
15
+ *
16
+ * A BARE id is the common case, not the exception: `resolveModelAlias('haiku')` yields
17
+ * `claude-haiku-4-5-20251001` with no prefix (ai.libx.js resolves it fuzzily downstream), so reading
18
+ * only the literal "/" made every aliased child look like a provider switch and silently dropped the
19
+ * parent's options. The fix belongs HERE — the alias table must keep emitting the ids ai.libx.js
20
+ * expects, and the comparison is provider identity, not string identity.
21
+ *
22
+ * A bare id we can't attribute is UNKNOWN, not a shared provider: it gets a per-id marker so two
23
+ * different unknown ids never compare equal (returning "" for both made them look like one provider
24
+ * and would have leaked one host's options onto the other's model). An unknown id still compares
25
+ * equal to ITSELF, so a child staying on the parent's exact model keeps the parent's options. */
26
+ declare function modelProvider(model: string): string;
13
27
  /** Short, human-friendly name for a resolved model id (e.g. "anthropic/claude-opus-4-8" → "opus"). */
14
28
  declare function modelShortLabel(model?: string): string;
15
29
  interface ResolveModelSwitchOpts {
@@ -39,4 +53,4 @@ interface ModelSwitch {
39
53
  */
40
54
  declare function resolveModelSwitch(opts: ResolveModelSwitchOpts): ModelSwitch;
41
55
 
42
- export { MODEL_ALIASES, type ModelSwitch, type ResolveModelSwitchOpts, modelShortLabel, resolveModelAlias, resolveModelSwitch };
56
+ export { MODEL_ALIASES, type ModelSwitch, type ResolveModelSwitchOpts, modelProvider, modelShortLabel, resolveModelAlias, resolveModelSwitch };
package/dist/models.js CHANGED
@@ -15,6 +15,12 @@ function resolveModelAlias(input) {
15
15
  const rest = slash === -1 ? input : input.slice(slash + 1);
16
16
  return prefix + (MODEL_ALIASES[rest.toLowerCase()] ?? rest);
17
17
  }
18
+ function modelProvider(model) {
19
+ const i = model.indexOf("/");
20
+ if (i !== -1) return model.slice(0, i);
21
+ if (/^claude-/i.test(model)) return "anthropic";
22
+ return `?${model}`;
23
+ }
18
24
  function modelShortLabel(model) {
19
25
  if (!model) return "default";
20
26
  const id = model.split("/").pop();
@@ -38,6 +44,7 @@ function resolveModelSwitch(opts) {
38
44
  }
39
45
  export {
40
46
  MODEL_ALIASES,
47
+ modelProvider,
41
48
  modelShortLabel,
42
49
  resolveModelAlias,
43
50
  resolveModelSwitch
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/models.ts"],"sourcesContent":["/**\n * Model aliasing + mid-conversation switch resolution — pure, dependency-free.\n *\n * Harness-level so every agentx consumer (unclaw, para-chat, …) resolves `[opus]`-style\n * aliases, gates by an allow-list, and announces a switch the same way. Policy (which models\n * a caller may use) and session state (the prior model) stay with the consumer; this module\n * is the shared mechanism.\n */\n\n/** Canonical short aliases → concrete Anthropic model ids. */\nexport const MODEL_ALIASES: Record<string, string> = {\n fable: 'claude-fable-5',\n 'fable-5': 'claude-fable-5',\n opus: 'claude-opus-4-8',\n 'opus-4-8': 'claude-opus-4-8',\n 'opus-4-7': 'claude-opus-4-7',\n 'opus-4-6': 'claude-opus-4-6',\n sonnet: 'claude-sonnet-4-6',\n haiku: 'claude-haiku-4-5-20251001',\n};\n\n/** Resolve an alias to a concrete id. A provider prefix (\"anthropic/\") is preserved; unknown ids pass through. */\nexport function resolveModelAlias(input: string): string {\n const slash = input.indexOf('/');\n const prefix = slash === -1 ? '' : input.slice(0, slash + 1);\n const rest = slash === -1 ? input : input.slice(slash + 1);\n return prefix + (MODEL_ALIASES[rest.toLowerCase()] ?? rest);\n}\n\n/** Short, human-friendly name for a resolved model id (e.g. \"anthropic/claude-opus-4-8\" → \"opus\"). */\nexport function modelShortLabel(model?: string): string {\n if (!model) return 'default';\n const id = model.split('/').pop()!;\n return id.replace(/^claude-/, '').replace(/-\\d.*$/, '');\n}\n\nexport interface ResolveModelSwitchOpts {\n /** Model requested for this turn (e.g. from an inline directive). Undefined = keep `current`. */\n requested?: string;\n /** The turn's default/current model when nothing is requested. */\n current: string;\n /** The session's prior resolved model — drives change detection + the notice. */\n prior?: string;\n /** If set, `requested` must match one of these (by short label) or it's denied and `current` is kept. */\n allowed?: string[];\n /** Announcement formatter. Default: `_[switching from x to y]_\\n\\n`. */\n format?: (from: string, to: string) => string;\n}\n\nexport interface ModelSwitch {\n /** The model to run this turn. */\n model: string;\n /** True when `requested` was rejected by `allowed` and `model` fell back to `current`. */\n denied: boolean;\n /** Inline notice to surface when the model actually changed vs `prior`, else undefined. */\n notice?: string;\n}\n\n/**\n * Resolve which model a turn runs, given an optional request, an allow-list, and the prior model.\n * Comparisons are by short label so provider-prefixed and dated ids unify\n * (\"anthropic/claude-opus-4-8\" ≡ \"claude-opus-4-8\" ≡ \"opus\").\n */\nexport function resolveModelSwitch(opts: ResolveModelSwitchOpts): ModelSwitch {\n const { requested, current, prior, allowed } = opts;\n if (!requested) return { model: current, denied: false };\n if (allowed && !allowed.some((a) => modelShortLabel(a) === modelShortLabel(requested))) {\n return { model: current, denied: true };\n }\n const fmt = opts.format ?? ((from: string, to: string) => `_[switching from ${from} to ${to}]_\\n\\n`);\n const changed = prior !== undefined && modelShortLabel(prior) !== modelShortLabel(requested);\n return {\n model: requested,\n denied: false,\n notice: changed ? fmt(modelShortLabel(prior!), modelShortLabel(requested)) : undefined,\n };\n}\n"],"mappings":";AAUO,IAAM,gBAAwC;AAAA,EACnD,OAAO;AAAA,EACP,WAAW;AAAA,EACX,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,OAAO;AACT;AAGO,SAAS,kBAAkB,OAAuB;AACvD,QAAM,QAAQ,MAAM,QAAQ,GAAG;AAC/B,QAAM,SAAS,UAAU,KAAK,KAAK,MAAM,MAAM,GAAG,QAAQ,CAAC;AAC3D,QAAM,OAAO,UAAU,KAAK,QAAQ,MAAM,MAAM,QAAQ,CAAC;AACzD,SAAO,UAAU,cAAc,KAAK,YAAY,CAAC,KAAK;AACxD;AAGO,SAAS,gBAAgB,OAAwB;AACtD,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,KAAK,MAAM,MAAM,GAAG,EAAE,IAAI;AAChC,SAAO,GAAG,QAAQ,YAAY,EAAE,EAAE,QAAQ,UAAU,EAAE;AACxD;AA6BO,SAAS,mBAAmB,MAA2C;AAC5E,QAAM,EAAE,WAAW,SAAS,OAAO,QAAQ,IAAI;AAC/C,MAAI,CAAC,UAAW,QAAO,EAAE,OAAO,SAAS,QAAQ,MAAM;AACvD,MAAI,WAAW,CAAC,QAAQ,KAAK,CAAC,MAAM,gBAAgB,CAAC,MAAM,gBAAgB,SAAS,CAAC,GAAG;AACtF,WAAO,EAAE,OAAO,SAAS,QAAQ,KAAK;AAAA,EACxC;AACA,QAAM,MAAM,KAAK,WAAW,CAAC,MAAc,OAAe,oBAAoB,IAAI,OAAO,EAAE;AAAA;AAAA;AAC3F,QAAM,UAAU,UAAU,UAAa,gBAAgB,KAAK,MAAM,gBAAgB,SAAS;AAC3F,SAAO;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ,UAAU,IAAI,gBAAgB,KAAM,GAAG,gBAAgB,SAAS,CAAC,IAAI;AAAA,EAC/E;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/models.ts"],"sourcesContent":["/**\n * Model aliasing + mid-conversation switch resolution — pure, dependency-free.\n *\n * Harness-level so every agentx consumer (unclaw, para-chat, …) resolves `[opus]`-style\n * aliases, gates by an allow-list, and announces a switch the same way. Policy (which models\n * a caller may use) and session state (the prior model) stay with the consumer; this module\n * is the shared mechanism.\n */\n\n/** Canonical short aliases → concrete Anthropic model ids. */\nexport const MODEL_ALIASES: Record<string, string> = {\n fable: 'claude-fable-5',\n 'fable-5': 'claude-fable-5',\n opus: 'claude-opus-4-8',\n 'opus-4-8': 'claude-opus-4-8',\n 'opus-4-7': 'claude-opus-4-7',\n 'opus-4-6': 'claude-opus-4-6',\n sonnet: 'claude-sonnet-4-6',\n haiku: 'claude-haiku-4-5-20251001',\n};\n\n/** Resolve an alias to a concrete id. A provider prefix (\"anthropic/\") is preserved; unknown ids pass through. */\nexport function resolveModelAlias(input: string): string {\n const slash = input.indexOf('/');\n const prefix = slash === -1 ? '' : input.slice(0, slash + 1);\n const rest = slash === -1 ? input : input.slice(slash + 1);\n return prefix + (MODEL_ALIASES[rest.toLowerCase()] ?? rest);\n}\n\n/** The provider of a model id (\"cursor/x\" -> \"cursor\"). Provider-specific `providerOptions` are only\n * portable within the same provider — leaking them across is a hard 400.\n *\n * A BARE id is the common case, not the exception: `resolveModelAlias('haiku')` yields\n * `claude-haiku-4-5-20251001` with no prefix (ai.libx.js resolves it fuzzily downstream), so reading\n * only the literal \"/\" made every aliased child look like a provider switch and silently dropped the\n * parent's options. The fix belongs HERE — the alias table must keep emitting the ids ai.libx.js\n * expects, and the comparison is provider identity, not string identity.\n *\n * A bare id we can't attribute is UNKNOWN, not a shared provider: it gets a per-id marker so two\n * different unknown ids never compare equal (returning \"\" for both made them look like one provider\n * and would have leaked one host's options onto the other's model). An unknown id still compares\n * equal to ITSELF, so a child staying on the parent's exact model keeps the parent's options. */\nexport function modelProvider(model: string): string {\n const i = model.indexOf('/');\n if (i !== -1) return model.slice(0, i);\n if (/^claude-/i.test(model)) return 'anthropic';\n return `?${model}`; // unknown: identity-scoped, never equal to a different unknown id\n}\n\n/** Short, human-friendly name for a resolved model id (e.g. \"anthropic/claude-opus-4-8\" → \"opus\"). */\nexport function modelShortLabel(model?: string): string {\n if (!model) return 'default';\n const id = model.split('/').pop()!;\n return id.replace(/^claude-/, '').replace(/-\\d.*$/, '');\n}\n\nexport interface ResolveModelSwitchOpts {\n /** Model requested for this turn (e.g. from an inline directive). Undefined = keep `current`. */\n requested?: string;\n /** The turn's default/current model when nothing is requested. */\n current: string;\n /** The session's prior resolved model — drives change detection + the notice. */\n prior?: string;\n /** If set, `requested` must match one of these (by short label) or it's denied and `current` is kept. */\n allowed?: string[];\n /** Announcement formatter. Default: `_[switching from x to y]_\\n\\n`. */\n format?: (from: string, to: string) => string;\n}\n\nexport interface ModelSwitch {\n /** The model to run this turn. */\n model: string;\n /** True when `requested` was rejected by `allowed` and `model` fell back to `current`. */\n denied: boolean;\n /** Inline notice to surface when the model actually changed vs `prior`, else undefined. */\n notice?: string;\n}\n\n/**\n * Resolve which model a turn runs, given an optional request, an allow-list, and the prior model.\n * Comparisons are by short label so provider-prefixed and dated ids unify\n * (\"anthropic/claude-opus-4-8\" ≡ \"claude-opus-4-8\" ≡ \"opus\").\n */\nexport function resolveModelSwitch(opts: ResolveModelSwitchOpts): ModelSwitch {\n const { requested, current, prior, allowed } = opts;\n if (!requested) return { model: current, denied: false };\n if (allowed && !allowed.some((a) => modelShortLabel(a) === modelShortLabel(requested))) {\n return { model: current, denied: true };\n }\n const fmt = opts.format ?? ((from: string, to: string) => `_[switching from ${from} to ${to}]_\\n\\n`);\n const changed = prior !== undefined && modelShortLabel(prior) !== modelShortLabel(requested);\n return {\n model: requested,\n denied: false,\n notice: changed ? fmt(modelShortLabel(prior!), modelShortLabel(requested)) : undefined,\n };\n}\n"],"mappings":";AAUO,IAAM,gBAAwC;AAAA,EACnD,OAAO;AAAA,EACP,WAAW;AAAA,EACX,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,OAAO;AACT;AAGO,SAAS,kBAAkB,OAAuB;AACvD,QAAM,QAAQ,MAAM,QAAQ,GAAG;AAC/B,QAAM,SAAS,UAAU,KAAK,KAAK,MAAM,MAAM,GAAG,QAAQ,CAAC;AAC3D,QAAM,OAAO,UAAU,KAAK,QAAQ,MAAM,MAAM,QAAQ,CAAC;AACzD,SAAO,UAAU,cAAc,KAAK,YAAY,CAAC,KAAK;AACxD;AAeO,SAAS,cAAc,OAAuB;AACnD,QAAM,IAAI,MAAM,QAAQ,GAAG;AAC3B,MAAI,MAAM,GAAI,QAAO,MAAM,MAAM,GAAG,CAAC;AACrC,MAAI,YAAY,KAAK,KAAK,EAAG,QAAO;AACpC,SAAO,IAAI,KAAK;AAClB;AAGO,SAAS,gBAAgB,OAAwB;AACtD,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,KAAK,MAAM,MAAM,GAAG,EAAE,IAAI;AAChC,SAAO,GAAG,QAAQ,YAAY,EAAE,EAAE,QAAQ,UAAU,EAAE;AACxD;AA6BO,SAAS,mBAAmB,MAA2C;AAC5E,QAAM,EAAE,WAAW,SAAS,OAAO,QAAQ,IAAI;AAC/C,MAAI,CAAC,UAAW,QAAO,EAAE,OAAO,SAAS,QAAQ,MAAM;AACvD,MAAI,WAAW,CAAC,QAAQ,KAAK,CAAC,MAAM,gBAAgB,CAAC,MAAM,gBAAgB,SAAS,CAAC,GAAG;AACtF,WAAO,EAAE,OAAO,SAAS,QAAQ,KAAK;AAAA,EACxC;AACA,QAAM,MAAM,KAAK,WAAW,CAAC,MAAc,OAAe,oBAAoB,IAAI,OAAO,EAAE;AAAA;AAAA;AAC3F,QAAM,UAAU,UAAU,UAAa,gBAAgB,KAAK,MAAM,gBAAgB,SAAS;AAC3F,SAAO;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ,UAAU,IAAI,gBAAgB,KAAM,GAAG,gBAAgB,SAAS,CAAC,IAAI;AAAA,EAC/E;AACF;","names":[]}
@@ -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?: {
@@ -392,6 +407,15 @@ interface AgentTool {
392
407
  data: string;
393
408
  }[];
394
409
  }>;
410
+ /** Optional: return a copy of this tool bound to a different real-disk working directory.
411
+ * Implemented by tools that escape the VFS (the real `Shell`), which are cwd-bound at construction
412
+ * and would otherwise keep pointing at the PARENT's tree when the tool instance is inherited by an
413
+ * isolated child agent. `subagent.ts` calls this for a worktree child; tools without it pass through.
414
+ * Return `undefined` for a capability that CANNOT follow the child (the background-job companions,
415
+ * bound to the parent's job registry) — the tool is then dropped from the child's toolbelt.
416
+ * An implementation must carry over any post-construction mutation the host made to the instance
417
+ * (a renamed `name`/`description`) — rebuilding from the factory's options alone loses it. */
418
+ withCwd?(cwd: string): AgentTool | undefined;
395
419
  }
396
420
  /** Build a tool context bound to a filesystem backend (Mem / Disk / …) and an optional host. */
397
421
  declare function makeContext(fs: IFilesystem, host?: HostBridge): ToolContext;
@@ -412,7 +436,10 @@ declare function defaultTools(): AgentTool[];
412
436
  * surface picks from this registry; embedders can build a custom tool set by name.
413
437
  */
414
438
  declare function toolRegistry(): Record<string, AgentTool>;
415
- /** Resolve a list of tool names against the registry (unknown names throw). */
416
- declare function toolsByName(names: string[]): AgentTool[];
439
+ /** Resolve a list of tool names against `available` (host-supplied tools, e.g. a parent agent's
440
+ * toolbelt or mounted MCP tools) first, then the built-in registry. Unknown names throw.
441
+ * Without `available` an allowlist could only ever name a built-in — so a def could not scope a
442
+ * child to a tool its parent injected (`Bash`, `ToolSearch`, …). */
443
+ declare function toolsByName(names: string[], available?: AgentTool[]): AgentTool[];
417
444
 
418
445
  export { type AgentTool as A, makeJobTools as B, type ChatLike as C, messageHasImage as D, messageHasImageRef as E, messageHasInlineImage as F, mintImageRef as G, type HostBridge as H, IMAGE_ELIDE_STUB as I, parseImageRef as J, readTool as K, remintImageRefs as L, type Message as M, sendBytes as N, sendBytesPer as O, toWireTools as P, todoWriteTool as Q, type Role as R, SandboxJobRegistry as S, type TodoItem as T, type UserQuestion as U, toolImageRef as V, toolRegistry as W, toolsByName as X, type ChatOptions as a, type ChatResponse as b, type ContentPart as c, type HostEvent as d, IMAGE_REF_SCHEME as e, type ImageRef as f, type MessageContent as g, type StreamChunk as h, type Tool as i, type ToolCall as j, type ToolContext as k, bashTool as l, clearImageRefs as m, contentBytes as n, contentText as o, defaultTools as p, editTool as q, elideImages as r, elideInlineImages as s, elideStaleImages as t, exitSessionTool as u, expandImagesForSend as v, imagePart as w, imageRefPart as x, imageRefResult as y, makeContext as z };
@@ -1,4 +1,4 @@
1
- import { A as AgentTool } from './tools-Byyh_bae.js';
1
+ import { A as AgentTool } from './tools-S-ArIR3o.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): {
@@ -112,7 +146,11 @@ declare class ShellJobRegistry {
112
146
  }
113
147
  /** Build an opt-in real-shell tool bound to `options.cwd`. */
114
148
  declare function makeRealShellTool(options: RealShellOptions): AgentTool;
115
- /** Build the background-job companion tools (ShellOutput / ShellStatus / ShellKill) over a registry. */
149
+ /** Build the background-job companion tools (ShellOutput / ShellStatus / ShellKill) over a registry.
150
+ * Each closes over THIS registry, i.e. this agent's jobs in this agent's cwd, so none of them can be
151
+ * rebound to an isolated child's root: `withCwd` returns `undefined` and they are dropped from the
152
+ * child's toolbelt. That keeps the capability coherent — the child's `Shell` cannot START a background
153
+ * job (its registry is dropped), so it must not be able to inspect or SIGTERM the parent's either. */
116
154
  declare function makeShellJobTools(registry: ShellJobRegistry): AgentTool[];
117
155
 
118
- export { type JobStatus, type RealShellOptions, type ShellJobConfig, ShellJobRegistry, type SpawnFn, type SpawnedProcess, makeRealShellTool, makeShellJobTools };
156
+ export { type JobExitHandler, type JobExitNotice, type JobStatus, type RealShellOptions, type ShellJobConfig, ShellJobRegistry, type SpawnFn, type SpawnedProcess, formatJobExit, makeRealShellTool, makeShellJobTools };