@livx.cc/agentx 0.99.46 → 0.99.48

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 +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/**\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":";AAAA,SAAS,aAAgC;AACzC,SAAS,kBAAkB;;;ACE3B,SAAS,WAAW;AAGb,IAAM,eAAe,CAAC,SAAiB,IAAI,aAAa,IAAI;;;ACAnE,IAAM,OAAO,oBAAI,IAAI;AAAA,EACnB;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAC1F;AAAA,EAAO;AAAA,EAAS;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAU;AAC5G,CAAC;AAGM,SAAS,SAAS,GAAwB;AAC/C,QAAM,MAAM,oBAAI,IAAY;AAC5B,aAAW,KAAK,OAAO,KAAK,EAAE,EAAE,YAAY,EAAE,MAAM,YAAY,KAAK,CAAC,GAAG;AACvE,QAAI,EAAE,UAAU,KAAK,CAAC,KAAK,IAAI,CAAC,EAAG,KAAI,IAAI,CAAC;AAAA,EAC9C;AACA,SAAO;AACT;AAGO,SAAS,WAAW,QAAuC;AAChE,QAAM,IAAI,OAAO;AACjB,MAAI,MAAM,EAAG,QAAO,oBAAI,IAAI;AAC5B,QAAM,KAAK,oBAAI,IAAoB;AACnC,aAAW,OAAO,OAAQ,YAAW,KAAK,SAAS,GAAG,EAAG,IAAG,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,KAAK,CAAC;AACvF,QAAM,MAAM,oBAAI,IAAoB;AACpC,aAAW,CAAC,GAAG,CAAC,KAAK,GAAI,KAAI,IAAI,GAAG,KAAK,KAAK,IAAI,MAAM,IAAI,EAAE,IAAI,CAAC;AACnE,SAAO;AACT;AAGO,SAAS,eAAe,MAAc,aAA0B,KAAmC;AACxG,MAAI,YAAY,SAAS,EAAG,QAAO;AACnC,QAAM,IAAI,SAAS,IAAI;AACvB,MAAI,QAAQ;AACZ,aAAW,KAAK,YAAa,KAAI,EAAE,IAAI,CAAC,EAAG,UAAS,KAAK,IAAI,CAAC,KAAK;AACnE,SAAO;AACT;AAQO,SAAS,eAAkB,OAAY,OAAe,MAAwB,GAAW,QAA6C;AAC3I,MAAI,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,EAAG,QAAO,EAAE,MAAM,OAAO,MAAM,CAAC,EAAE;AAClE,MAAI,MAAM,UAAU,KAAK,CAAC,MAAM,KAAK,EAAG,QAAO,EAAE,MAAM,OAAO,MAAM,CAAC,EAAE;AACvE,QAAM,IAAI,SAAS,KAAK;AACxB,MAAI,EAAE,SAAS,EAAG,QAAO,EAAE,MAAM,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,MAAM,MAAM,CAAC,EAAE;AACzE,QAAM,MAAM,QAAQ,SAAS,WAAW,MAAM,IAAI;AAClD,QAAM,SAAS,MAAM,IAAI,CAAC,GAAG,OAAO,EAAE,GAAG,GAAG,eAAe,KAAK,CAAC,GAAG,GAAG,GAAG,EAAE,EAAE;AAC9E,SAAO,KAAK,CAAC,GAAG,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC5C,QAAM,OAAO,IAAI,IAAI,OAAO,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;AACvD,SAAO,EAAE,MAAM,MAAM,OAAO,CAAC,GAAG,MAAM,KAAK,IAAI,CAAC,CAAC,GAAG,MAAM,MAAM,OAAO,CAAC,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,EAAE;AACjG;;;AC/BA,SAAS,SAAS,QAAgC;AAChD,MAAI,UAAU,KAAM,QAAO,EAAE,MAAM,GAAG;AACtC,MAAI,OAAO,WAAW,SAAU,QAAO,EAAE,MAAM,OAAO;AACtD,QAAM,UAAW,OAAe;AAChC,MAAI,MAAM,QAAQ,OAAO,GAAG;AAC1B,UAAM,QAAkB,CAAC;AACzB,UAAM,SAAqB,CAAC;AAC5B,eAAW,KAAK,SAAS;AACvB,UAAI,GAAG,SAAS,WAAW,OAAO,EAAE,SAAS,YAAY,EAAE,UAAU;AACnE,eAAO,KAAK,EAAE,UAAU,EAAE,UAAU,MAAM,EAAE,KAAK,CAAC;AAAA,MACpD,WAAW,OAAO,GAAG,SAAS,UAAU;AACtC,cAAM,KAAK,EAAE,IAAI;AAAA,MACnB,OAAO;AACL,cAAM,KAAK,KAAK,UAAU,CAAC,CAAC;AAAA,MAC9B;AAAA,IACF;AACA,UAAM,OAAO,MAAM,KAAK,IAAI;AAC5B,QAAI,QAAQ,OAAO,OAAQ,QAAO,EAAE,MAAM,GAAI,OAAO,SAAS,EAAE,OAAO,IAAI,CAAC,EAAG;AAAA,EACjF;AACA,SAAO,EAAE,MAAM,KAAK,UAAU,MAAM,EAAE;AACxC;AAUO,IAAM,sBAAsB;AACnC,IAAM,YAAY,uBAAO,aAAa;AACtC,eAAe,aAAgB,GAAe,IAA2C;AACvF,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,QAAQ,KAAK,CAAC,GAAG,IAAI,QAA0B,CAAC,QAAQ;AAAE,UAAI,WAAW,MAAM,IAAI,SAAS,GAAG,EAAE;AAAA,IAAG,CAAC,CAAC,CAAC;AAAA,EACtH,UAAE;AAAU,iBAAa,CAAC;AAAA,EAAG;AAC/B;AACA,IAAM,cAAc,CAAC,MAAc,KAAK,wBACtC,IAAI,IAAI,uBAAuB,KAAK,GAAI;AAInC,SAAS,mBAAmB,MAAmB,UAAmB,SAAS,SAAS,YAAY,qBAAgC;AACrI,SAAO;AAAA,IACL,MAAM,GAAG,MAAM,GAAG,KAAK,IAAI;AAAA,IAC3B,aAAa,KAAK,eAAe,YAAY,KAAK,IAAI;AAAA,IACtD,YAAY,KAAK,eAAe,EAAE,MAAM,UAAU,YAAY,CAAC,EAAE;AAAA,IACjE,eAAe;AAAA,IACf,MAAM,IAAI,MAAM,MAAM;AACpB,YAAM,MAAM,MAAM,aAAa,SAAS,KAAK,MAAM,QAAQ,CAAC,CAAC,GAAG,SAAS;AACzE,UAAI,QAAQ,UAAW,QAAO,YAAY,KAAK,MAAM,SAAS;AAC9D,YAAM,IAAI,SAAS,GAAG;AACtB,aAAO,EAAE,QAAQ,SAAS,IAAI,EAAE;AAAA,IAClC;AAAA,EACF;AACF;AAKO,SAAS,qBAAqB,OAAsB,UAAmB,SAAS,SAAS,QAAsD;AACpJ,UAAQ,SAAS,MAAM,OAAO,MAAM,IAAI,OAAO,IAAI,CAAC,MAAM,mBAAmB,GAAG,UAAU,MAAM,CAAC;AACnG;AAaA,SAAS,aAAa,GAAwB;AAC5C,QAAM,SAAS,EAAE,cAAc;AAAA,WAAc,KAAK,UAAU,EAAE,WAAW,CAAC,KAAK;AAC/E,SAAO,GAAG,EAAE,IAAI,WAAM,EAAE,eAAe,kBAAkB,GAAG,MAAM;AACpE;AAWO,SAAS,kBAAkB,OAAsB,UAAmB,UAAgC,CAAC,GAAgB;AAC1H,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,gBAAgB,QAAQ,aAAa;AAC3C,QAAM,SAAS,IAAI,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AACpD,QAAM,cAAc,GAAG,MAAM,MAAM;AAEnC,QAAM,aAAwB;AAAA,IAC5B,MAAM;AAAA,IACN,aAAa,8CAA8C,WAAW;AAAA,IACtE,YAAY,EAAE,MAAM,UAAU,UAAU,CAAC,OAAO,GAAG,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,aAAa,oDAAoD,EAAE,EAAE;AAAA,IAC/J,MAAM,IAAI,EAAE,MAAM,GAAG;AACnB,YAAM,IAAI,OAAO,SAAS,EAAE,EAAE,KAAK;AACnC,UAAI,CAAC,EAAG,QAAO;AACf,YAAM,EAAE,KAAK,IAAI,eAAe,OAAO,GAAG,CAAC,MAAM,GAAG,EAAE,IAAI,IAAI,EAAE,eAAe,EAAE,IAAI,UAAU;AAC/F,UAAI,CAAC,KAAK,OAAQ,QAAO,yBAAyB,CAAC;AACnD,aAAO,KAAK,IAAI,YAAY,EAAE,KAAK,IAAI;AAAA,IACzC;AAAA,EACF;AAEA,QAAM,cAAyB;AAAA,IAC7B,MAAM;AAAA,IACN,aAAa;AAAA,IACb,YAAY;AAAA,MACV,MAAM;AAAA,MACN,UAAU,CAAC,MAAM;AAAA,MACjB,YAAY;AAAA,QACV,MAAM,EAAE,MAAM,UAAU,aAAa,kCAAkC;AAAA,QACvE,MAAM,EAAE,MAAM,UAAU,aAAa,iDAAiD;AAAA,MACxF;AAAA,IACF;AAAA,IACA,eAAe;AAAA,IACf,MAAM,IAAI,EAAE,MAAM,KAAK,GAAG;AACxB,YAAM,IAAI,OAAO,QAAQ,EAAE;AAC3B,UAAI,CAAC,OAAO,IAAI,CAAC,EAAG,QAAO,4BAA4B,CAAC;AACxD,YAAM,MAAM,MAAM,aAAa,SAAS,GAAG,QAAQ,CAAC,CAAC,GAAG,aAAa;AACrE,UAAI,QAAQ,UAAW,QAAO,YAAY,GAAG,aAAa;AAC1D,YAAM,IAAI,SAAS,GAAG;AACtB,aAAO,EAAE,QAAQ,SAAS,IAAI,EAAE;AAAA,IAClC;AAAA,EACF;AAEA,SAAO,CAAC,YAAY,WAAW;AACjC;AAgBO,SAAS,gBAAgB,SAA4G;AAC1I,QAAM,QAAuB,CAAC;AAC9B,QAAM,SAAS,oBAAI,IAAsB;AACzC,aAAW,KAAK,SAAS;AACvB,eAAW,KAAK,EAAE,OAAO;AACvB,YAAM,OAAO,QAAQ,EAAE,IAAI,KAAK,EAAE,IAAI,GAAG,QAAQ,mBAAmB,GAAG,EAAE,MAAM,GAAG,GAAG;AACrF,UAAI,UAAU;AACd,eAAS,IAAI,GAAG,OAAO,IAAI,OAAO,GAAG,IAAK,WAAU,GAAG,KAAK,MAAM,GAAG,MAAM,OAAO,CAAC,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC;AACrG,YAAM,KAAK,EAAE,MAAM,SAAS,aAAa,EAAE,aAAa,aAAa,EAAE,YAAY,CAAC;AACpF,aAAO,IAAI,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,EAAE,KAAK,CAAC;AAAA,IACzD;AAAA,EACF;AACA,SAAO,EAAE,OAAO,OAAO;AACzB;AAOA,SAAS,kBAAkB,SAAmB,OAAsB,QAA+B,SAA2B,SAAgC;AAC5J,QAAM,QAAQ,MAAM,SAChB,kBAAkB,OAAO,CAAC,MAAM,SAAS;AACvC,UAAM,IAAI,OAAO,IAAI,IAAI;AACzB,QAAI,CAAC,EAAG,OAAM,IAAI,MAAM,qBAAqB,IAAI,6CAAwC;AACzF,WAAO,QAAQ,EAAE,QAAQ,EAAE,SAAS,QAAQ,CAAC,CAAC;AAAA,EAChD,GAAG,OAAO,IACV,CAAC;AACL,SAAO,EAAE,OAAO,aAAa,SAAS,WAAW,MAAM,OAAO;AAChE;AAQO,SAAS,6BACd,SACA,SACkE;AAClE,QAAM,EAAE,OAAO,OAAO,IAAI,gBAAgB,OAAO;AACjD,QAAM,SAAS,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AACtD,SAAO,kBAAkB,QAAQ,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,QAAQ,CAAC,QAAQ,SAAS,SAAS,OAAO,IAAI,MAAM,EAAG,OAAO,SAAS,SAAS,IAAI,GAAG,OAAO;AAC5J;AAQO,SAAS,sBACd,SACA,SACA,SACkE;AAClE,QAAM,EAAE,OAAO,OAAO,IAAI,gBAAgB,OAAO;AACjD,SAAO,kBAAkB,QAAQ,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,QAAQ,SAAS,OAAO;AACtF;;;AHlOA,IAAMA,OAAM,aAAa,KAAK;AAS9B,IAAM,mBAAmB;AACzB,IAAM,qBAAqB;AAqBpB,IAAM,iBAAN,MAA6C;AAAA,EAMlD,YAAoB,MAAuB;AAAvB;AAAA,EAAwB;AAAA,EAAxB;AAAA,EALZ;AAAA,EACA,MAAM;AAAA,EACN,SAAS;AAAA,EACT,UAAU,oBAAI,IAA2G;AAAA,EAIjI,MAAM,QAAuB;AAC3B,UAAM,EAAE,SAAS,OAAO,CAAC,GAAG,KAAK,IAAI,IAAI,KAAK;AAC9C,UAAM,OAAO,MAAM,SAAS,MAAM,EAAE,OAAO,CAAC,QAAQ,QAAQ,MAAM,GAAG,KAAK,EAAE,GAAG,QAAQ,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC;AAC3G,SAAK,OAAO;AACZ,SAAK,OAAQ,YAAY,MAAM;AAC/B,SAAK,OAAQ,GAAG,QAAQ,CAAC,UAAkB,KAAK,OAAO,KAAK,CAAC;AAC7D,SAAK,OAAQ,YAAY,MAAM;AAC/B,SAAK,OAAQ,GAAG,QAAQ,CAAC,UAAkBA,KAAI,MAAM,IAAI,OAAO,aAAa,MAAM,QAAQ,CAAC,CAAC;AAC7F,SAAK,GAAG,QAAQ,CAAC,SAAS,KAAK,QAAQ,IAAI,MAAM,eAAe,OAAO,kBAAkB,IAAI,GAAG,CAAC,CAAC;AAClG,SAAK,GAAG,SAAS,CAAC,MAAM,KAAK,QAAQ,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC;AAAA,EACrF;AAAA,EAEQ,OAAO,OAAqB;AAClC,SAAK,OAAO;AACZ,QAAI;AACJ,YAAQ,KAAK,KAAK,IAAI,QAAQ,IAAI,MAAM,GAAG;AACzC,YAAM,OAAO,KAAK,IAAI,MAAM,GAAG,EAAE,EAAE,KAAK;AACxC,WAAK,MAAM,KAAK,IAAI,MAAM,KAAK,CAAC;AAChC,UAAI,CAAC,KAAM;AACX,UAAI;AAAE,aAAK,SAAS,KAAK,MAAM,IAAI,CAAC;AAAA,MAAG,SAChC,GAAG;AAAE,QAAAA,KAAI,MAAM,2CAA2C,MAAM,CAAC;AAAA,MAAG;AAAA,IAC7E;AAAA,EACF;AAAA,EAEQ,SAAS,KAAgB;AAE/B,QAAI,KAAK,MAAM,QAAQ,CAAC,KAAK,QAAQ,IAAI,IAAI,EAAE,EAAG;AAClD,UAAM,IAAI,KAAK,QAAQ,IAAI,IAAI,EAAE;AACjC,SAAK,QAAQ,OAAO,IAAI,EAAE;AAC1B,iBAAa,EAAE,KAAK;AACpB,QAAI,IAAI,MAAO,GAAE,OAAO,IAAI,MAAM,IAAI,OAAO,WAAW,KAAK,UAAU,IAAI,KAAK,CAAC,CAAC;AAAA,QAC7E,GAAE,QAAQ,IAAI,MAAM;AAAA,EAC3B;AAAA,EAEQ,QAAQ,GAAgB;AAC9B,eAAW,KAAK,KAAK,QAAQ,OAAO,GAAG;AAAE,mBAAa,EAAE,KAAK;AAAG,QAAE,OAAO,CAAC;AAAA,IAAG;AAC7E,SAAK,QAAQ,MAAM;AAAA,EACrB;AAAA,EAEQ,MAAM,KAAoB;AAChC,QAAI,CAAC,KAAK,MAAM,MAAO,OAAM,IAAI,MAAM,iCAAiC;AACxE,SAAK,KAAK,MAAM,MAAM,KAAK,UAAU,GAAG,IAAI,IAAI;AAAA,EAClD;AAAA,EAEA,QAAQ,QAAgB,QAAgC;AACtD,UAAM,KAAK,KAAK;AAChB,UAAM,YAAY,KAAK,KAAK,aAAa;AACzC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,QAAQ,WAAW,MAAM;AAC7B,aAAK,QAAQ,OAAO,EAAE;AACtB,eAAO,IAAI,MAAM,gBAAgB,MAAM,qBAAqB,SAAS,IAAI,CAAC;AAAA,MAC5E,GAAG,SAAS;AACZ,WAAK,QAAQ,IAAI,IAAI,EAAE,SAAS,QAAQ,MAAM,CAAC;AAC/C,UAAI;AAAE,aAAK,MAAM,EAAE,SAAS,OAAO,IAAI,QAAQ,OAAO,CAAC;AAAA,MAAG,SACnD,GAAG;AAAE,qBAAa,KAAK;AAAG,aAAK,QAAQ,OAAO,EAAE;AAAG,eAAO,CAAC;AAAA,MAAG;AAAA,IACvE,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,OAAO,QAAgB,QAAiC;AAC5D,SAAK,MAAM,EAAE,SAAS,OAAO,QAAQ,OAAO,CAAC;AAAA,EAC/C;AAAA,EAEA,MAAM,QAAuB;AAC3B,SAAK,QAAQ,IAAI,MAAM,sBAAsB,CAAC;AAC9C,QAAI;AAAE,WAAK,MAAM,OAAO,IAAI;AAAA,IAAG,SAAS,GAAG;AAAE,MAAAA,KAAI,MAAM,oBAAoB,CAAC;AAAA,IAAG;AAC/E,SAAK,MAAM,KAAK;AAAA,EAClB;AACF;AAgBO,IAAM,eAAN,cAA2B,MAAM;AAAA,EAEtC,YAAmB,QAAuB,YAAqB;AAC7D,UAAM,aAAa,aAAa,KAAK,UAAU,MAAM,EAAE,kCAAkC,MAAM,qCAAgC;AAD9G;AAAuB;AAExC,SAAK,OAAO;AAAA,EACd;AAAA,EAHmB;AAAA,EAAuB;AAAA,EAD1C,YAAY;AAKd;AAEO,IAAM,gBAAN,MAA4C;AAAA,EAKjD,YAAoB,MAAsB,WAA0B;AAAhD;AAClB,SAAK,YAAY,aAAa;AAAA,EAChC;AAAA,EAFoB;AAAA,EAJZ,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EAMR,MAAM,QAAuB;AAAA,EAAC;AAAA,EAE9B,QAAQ,QAAgB,QAAgC;AACtD,WAAO,KAAK,KAAK,EAAE,SAAS,OAAO,IAAI,KAAK,UAAU,QAAQ,OAAO,GAAG,KAAK;AAAA,EAC/E;AAAA,EAEA,MAAM,OAAO,QAAgB,QAAiC;AAC5D,UAAM,KAAK,KAAK,EAAE,SAAS,OAAO,QAAQ,OAAO,GAAG,IAAI;AAAA,EAC1D;AAAA,EAEA,MAAc,KAAK,KAAc,UAAiC;AAChE,UAAM,UAAkC;AAAA,MACtC,gBAAgB;AAAA,MAChB,QAAQ;AAAA,MACR,GAAI,KAAK,KAAK,cAAc,EAAE,eAAe,UAAU,KAAK,KAAK,WAAW,GAAG,IAAI,CAAC;AAAA,MACpF,GAAG,KAAK,KAAK;AAAA;AAAA,MACb,GAAI,KAAK,YAAY,EAAE,kBAAkB,KAAK,UAAU,IAAI,CAAC;AAAA,IAC/D;AACA,UAAM,OAAO,MAAM,KAAK,UAAU,KAAK,KAAK,KAAK;AAAA,MAC/C,QAAQ;AAAA,MAAQ;AAAA,MAAS,MAAM,KAAK,UAAU,GAAG;AAAA,MACjD,QAAQ,YAAY,QAAQ,KAAK,KAAK,aAAa,kBAAkB;AAAA,IACvE,CAAC;AACD,UAAM,MAAM,KAAK,QAAQ,IAAI,gBAAgB;AAC7C,QAAI,IAAK,MAAK,YAAY;AAC1B,QAAI,KAAK,WAAW,OAAO,KAAK,WAAW,IAAK,OAAM,IAAI,aAAa,KAAK,MAAM;AAClF,QAAI,CAAC,KAAK,GAAI,OAAM,IAAI,MAAM,YAAY,KAAK,MAAM,IAAI,KAAK,UAAU,EAAE;AAC1E,QAAI,SAAU;AACd,UAAM,KAAK,KAAK,QAAQ,IAAI,cAAc,KAAK;AAC/C,UAAM,OAAO,GAAG,SAAS,mBAAmB,IAAI,iBAAiB,MAAM,KAAK,KAAK,CAAC,IAAI,MAAM,KAAK,KAAK;AACtG,QAAI,MAAM,MAAO,OAAM,IAAI,MAAM,KAAK,OAAO,WAAW,KAAK,UAAU,KAAK,KAAK,CAAC;AAClF,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,MAAM,QAAuB;AAAA,EAAC;AAChC;AAGA,SAAS,iBAAiB,MAAmB;AAC3C,aAAW,QAAQ,KAAK,MAAM,IAAI,GAAG;AACnC,UAAM,UAAU,KAAK,UAAU;AAC/B,QAAI,CAAC,QAAQ,WAAW,OAAO,EAAG;AAClC,QAAI;AACF,YAAM,MAAM,KAAK,MAAM,QAAQ,MAAM,CAAC,EAAE,KAAK,CAAC;AAC9C,UAAI,QAAQ,IAAI,WAAW,UAAa,IAAI,UAAU,QAAY,QAAO;AAAA,IAC3E,SAAS,GAAG;AAAE,MAAAA,KAAI,MAAM,sCAAsC,CAAC;AAAA,IAAG;AAAA,EACpE;AACA,SAAO,CAAC;AACV;AAKO,IAAM,YAAN,MAAgB;AAAA,EACrB,YAAmB,WAAiC,aAAa,EAAE,MAAM,UAAU,SAAS,IAAI,GAAG;AAAhF;AAAiC;AAAA,EAAgD;AAAA,EAAjF;AAAA,EAAiC;AAAA;AAAA,EAGpD,MAAM,UAA6F;AACjG,UAAM,KAAK,UAAU,MAAM;AAC3B,UAAM,SAAS,MAAM,KAAK,UAAU,QAAQ,cAAc;AAAA,MACxD,iBAAiB;AAAA,MACjB,cAAc,CAAC;AAAA,MACf,YAAY,KAAK;AAAA,IACnB,CAAC;AACD,UAAM,KAAK,UAAU,OAAO,2BAA2B;AACvD,WAAO,UAAU,CAAC;AAAA,EACpB;AAAA,EAEA,MAAM,YAAoC;AACxC,UAAM,IAAI,MAAM,KAAK,UAAU,QAAQ,YAAY;AACnD,WAAO,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE,QAAQ,CAAC;AAAA,EAC9C;AAAA,EAEA,MAAM,SAAS,MAAc,MAAiC;AAC5D,WAAO,KAAK,UAAU,QAAQ,cAAc,EAAE,MAAM,WAAW,QAAQ,CAAC,EAAE,CAAC;AAAA,EAC7E;AAAA,EAEA,MAAM,gBAAmF;AACvF,UAAM,IAAI,MAAM,KAAK,UAAU,QAAQ,gBAAgB;AACvD,WAAO,MAAM,QAAQ,GAAG,SAAS,IAAI,EAAE,YAAY,CAAC;AAAA,EACtD;AAAA,EAEA,MAAM,aAAa,KAA2B;AAC5C,WAAO,KAAK,UAAU,QAAQ,kBAAkB,EAAE,IAAI,CAAC;AAAA,EACzD;AAAA,EAEA,MAAM,QAAuB;AAC3B,UAAM,KAAK,UAAU,MAAM;AAAA,EAC7B;AACF;AAmCA,SAAS,eAAe,KAAoC;AAC1D,SAAO,IAAI,MACP,IAAI,cAAc,EAAE,KAAK,IAAI,KAAK,SAAS,IAAI,SAAS,aAAa,IAAI,aAAa,WAAW,IAAI,UAAU,CAAC,IAChH,IAAI,eAAe,EAAE,SAAS,IAAI,SAAU,MAAM,IAAI,MAAM,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,WAAW,IAAI,UAAU,CAAC;AACxH;AAIA,SAAS,YAAe,GAAe,IAAwB,OAA2B;AACxF,MAAI,CAAC,MAAM,MAAM,EAAG,QAAO;AAC3B,SAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,UAAM,QAAQ,WAAW,MAAM,OAAO,IAAI,MAAM,QAAQ,KAAK,oBAAoB,EAAE,IAAI,CAAC,GAAG,EAAE;AAC7F,IAAC,MAAc,QAAQ;AACvB,MAAE,KAAK,CAAC,MAAM;AAAE,mBAAa,KAAK;AAAG,cAAQ,CAAC;AAAA,IAAG,GAAG,CAAC,MAAM;AAAE,mBAAa,KAAK;AAAG,aAAO,CAAC;AAAA,IAAG,CAAC;AAAA,EAChG,CAAC;AACH;AAGA,eAAe,kBAAkB,MAAc,KAAsB,gBAA8C;AACjH,QAAM,SAAS,IAAI,UAAU,eAAe,GAAG,CAAC;AAChD,MAAI;AACF,WAAO,MAAM,aAAa,YAAY;AACpC,YAAM,OAAO,MAAM,OAAO,QAAQ;AAClC,YAAM,QAAQ,MAAM,OAAO,UAAU;AACrC,YAAM,QAAQ,qBAAqB,OAAO,CAAC,MAAM,MAAM,OAAO,SAAS,MAAM,CAAC,GAAG,QAAQ,IAAI,IAAI;AACjG,aAAO,EAAE,MAAM,QAAQ,OAAO,OAAO,YAAY,MAAM,YAAY,QAAQ,IAAI;AAAA,IACjF,GAAG,GAAG,gBAAgB,IAAI;AAAA,EAC5B,SAAS,GAAG;AACV,UAAM,OAAO,MAAM,EAAE,MAAM,CAAC,QAAQA,KAAI,MAAM,gCAAgC,IAAI,MAAM,GAAG,EAAE,CAAC;AAC9F,UAAM;AAAA,EACR;AACF;AAGO,SAAS,eAAe,MAAc,KAA2C;AACtF,SAAO,kBAAkB,MAAM,GAAG;AACpC;AAGA,SAAS,aAAa,SAA4E;AAChG,SAAO,OAAO,QAAQ,OAAO,EAAE,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM;AACrD,QAAI,CAAC,OAAO,IAAI,SAAU,QAAO;AACjC,QAAI,CAAC,IAAI,WAAW,CAAC,IAAI,KAAK;AAAE,MAAAA,KAAI,KAAK,eAAe,IAAI,yDAAoD;AAAG,aAAO;AAAA,IAAO;AACjI,WAAO;AAAA,EACT,CAAC;AACH;AAGA,SAAS,gBAAgB,MAAc,GAAc;AACnD,MAAI,aAAa,aAAc,CAAAA,KAAI,KAAK,QAAQ,IAAI,sBAAsB,EAAE,MAAM,4DAAuD;AAAA,MACpI,CAAAA,KAAI,MAAM,eAAe,IAAI,sBAAsB,GAAG,WAAW,CAAC,EAAE;AAC3E;AAMA,eAAsB,gBAAgB,UAA2C,CAAC,GAAG,OAAoC,CAAC,GAA0B;AAClJ,QAAM,UAAU,aAAa,OAAO;AACpC,QAAM,UAAU,MAAM,QAAQ,WAAW,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM,kBAAkB,MAAM,KAAK,KAAK,cAAc,CAAC,CAAC;AACxH,QAAM,MAAoB,CAAC;AAC3B,UAAQ,QAAQ,CAAC,GAAG,MAAM;AACxB,UAAM,OAAO,QAAQ,CAAC,EAAE,CAAC;AAEzB,QAAI,EAAE,WAAW,aAAa;AAAE,UAAI,KAAK,EAAE,KAAK;AAAG,MAAAA,KAAI,MAAM,QAAQ,IAAI,oBAAe,EAAE,MAAM,MAAM,MAAM,WAAW,EAAE,MAAM,YAAY,OAAO,SAAS,EAAE,MAAM,WAAW,IAAI,KAAK,EAAE,EAAE;AAAA,IAAG,MACzL,iBAAgB,MAAM,EAAE,MAAM;AAAA,EACrC,CAAC;AACD,SAAO;AACT;AASA,eAAsB,iBACpB,UAA2C,CAAC,GAC5C,SACkG;AAClG,QAAM,UAAU,MAAM,gBAAgB,SAAS,EAAE,gBAAgB,SAAS,eAAe,CAAC;AAC1F,SAAO,EAAE,GAAG,6BAA6B,SAAS,OAAO,GAAG,QAAQ;AACtE;AAeO,SAAS,aAAa,KAA8B;AACzD,QAAM,QAAQ,IAAI,MACd,CAAC,QAAQ,IAAI,KAAK,GAAG,OAAO,KAAK,IAAI,WAAW,CAAC,CAAC,EAAE,KAAK,CAAC,IAC1D,CAAC,SAAS,IAAI,WAAW,IAAI,GAAI,IAAI,QAAQ,CAAC,GAAI,IAAI,OAAO,IAAI,GAAG,OAAO,KAAK,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC;AACzG,SAAO,WAAW,QAAQ,EAAE,OAAO,MAAM,KAAK,IAAI,CAAC,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AAChF;AAIO,IAAM,gBAAN,MAA+C;AAAA,EAEpD,YAAoB,QAAQ,IAAI,KAAQ;AAApB;AAAA,EAAqB;AAAA,EAArB;AAAA,EADZ,IAAI,oBAAI,IAAmD;AAAA,EAEnE,IAAI,KAAmC;AACrC,UAAM,IAAI,KAAK,EAAE,IAAI,GAAG;AACxB,QAAI,CAAC,EAAG,QAAO;AACf,QAAI,KAAK,IAAI,IAAI,EAAE,KAAK;AAAE,WAAK,EAAE,OAAO,GAAG;AAAG,aAAO;AAAA,IAAM;AAC3D,WAAO,EAAE;AAAA,EACX;AAAA,EACA,IAAI,KAAa,OAA4B;AAAE,SAAK,EAAE,IAAI,KAAK,EAAE,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,MAAM,CAAC;AAAA,EAAG;AAC3G;AAKO,IAAM,UAAN,MAAc;AAAA,EAEnB,YAAoB,QAAQ,IAAI,KAAQ;AAApB;AAAA,EAAqB;AAAA,EAArB;AAAA,EADZ,OAAO,oBAAI,IAAyE;AAAA,EAE5F,IAAI,KAA+B;AAAE,UAAM,IAAI,KAAK,KAAK,IAAI,GAAG;AAAG,QAAI,CAAC,EAAG,QAAO;AAAM,SAAK,IAAI,KAAK,CAAC;AAAG,WAAO,EAAE;AAAA,EAAQ;AAAA,EAC3H,IAAI,KAAa,QAAyB;AACxC,UAAM,OAAO,KAAK,KAAK,IAAI,GAAG;AAC9B,QAAI,MAAM;AAAE,mBAAa,KAAK,KAAK;AAAG,UAAI,KAAK,WAAW,OAAQ,MAAK,KAAK,OAAO,MAAM,EAAE,MAAM,CAAC,QAAQA,KAAI,MAAM,mCAAmC,GAAG,EAAE,CAAC;AAAA,IAAG;AAChK,UAAM,IAAI,EAAE,QAAQ,OAAO,OAAiB;AAC5C,SAAK,KAAK,IAAI,KAAK,CAAC;AAAG,SAAK,IAAI,KAAK,CAAC;AAAA,EACxC;AAAA,EACQ,IAAI,KAAa,GAA4C;AACnE,iBAAa,EAAE,KAAK;AACpB,MAAE,QAAQ,WAAW,MAAM;AAAE,WAAK,KAAK,MAAM,GAAG;AAAA,IAAG,GAAG,KAAK,KAAK;AAChE,IAAC,EAAE,MAAc,QAAQ;AAAA,EAC3B;AAAA,EACA,MAAc,MAAM,KAA4B;AAC9C,UAAM,IAAI,KAAK,KAAK,IAAI,GAAG;AAAG,QAAI,CAAC,EAAG;AACtC,SAAK,KAAK,OAAO,GAAG;AACpB,UAAM,EAAE,OAAO,MAAM,EAAE,MAAM,CAAC,QAAQA,KAAI,MAAM,iCAAiC,GAAG,EAAE,CAAC;AAAA,EACzF;AAAA,EACA,MAAM,WAA0B;AAAE,eAAW,KAAK,KAAK,KAAK,OAAO,GAAG;AAAE,mBAAa,EAAE,KAAK;AAAG,YAAM,EAAE,OAAO,MAAM,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAAG;AAAE,SAAK,KAAK,MAAM;AAAA,EAAG;AAC9J;AAEA,IAAM,iBAAiB,IAAI,cAAc;AACzC,IAAM,cAAc,IAAI,QAAQ;AAChC,IAAM,kBAAkB,oBAAI,IAAoB;AAChD,IAAM,aAAa,oBAAI,IAAY;AACnC,IAAM,8BAA8B;AA4BpC,eAAe,eAAe,MAAc,KAAsB,KAAa,MAAqD;AAClI,QAAM,UAAU,KAAK,WAAW;AAChC,QAAM,OAAO,KAAK,QAAQ;AAC1B,QAAM,WAAW,KAAK,YAAY;AAClC,QAAM,SAAS,IAAI,UAAU,eAAe,GAAG,CAAC;AAChD,MAAI;AACF,UAAM,QAAQ,MAAM,aAAa,YAAY;AAAE,YAAM,OAAO,QAAQ;AAAG,aAAO,OAAO,UAAU;AAAA,IAAG,GAAG,GAAG,KAAK,gBAAgB,IAAI;AACjI,YAAQ,IAAI,KAAK,KAAK;AACtB,aAAS,OAAO,GAAG;AACnB,QAAI,KAAK,YAAY,CAAC,IAAI,IAAK,MAAK,IAAI,KAAK,MAAM;AAAA,QAC9C,OAAM,OAAO,MAAM,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AACxC,WAAO,EAAE,MAAM,KAAK,KAAK,MAAM;AAAA,EACjC,SAAS,GAAQ;AACf,UAAM,OAAO,MAAM,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AACnC,aAAS,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,qBAAqB,4BAA4B;AACtF,oBAAgB,MAAM,CAAC;AACvB,WAAO;AAAA,EACT;AACF;AAIA,eAAe,YAAY,MAAc,KAAsB,MAAqD;AAClH,QAAM,UAAU,KAAK,WAAW;AAChC,QAAM,WAAW,KAAK,YAAY;AAClC,QAAM,MAAM,aAAa,GAAG;AAC5B,QAAM,SAAS,QAAQ,IAAI,GAAG;AAC9B,MAAI,OAAQ,QAAO,EAAE,MAAM,KAAK,KAAK,OAAO,OAAO;AACnD,QAAM,QAAQ,SAAS,IAAI,GAAG;AAC9B,MAAI,SAAS,QAAQ,KAAK,IAAI,EAAG,QAAO;AACxC,MAAI,KAAK,aAAa,cAAc;AAClC,QAAI,CAAC,WAAW,IAAI,GAAG,GAAG;AAAE,iBAAW,IAAI,GAAG;AAAG,WAAK,eAAe,MAAM,KAAK,KAAK,IAAI,EAAE,QAAQ,MAAM,WAAW,OAAO,GAAG,CAAC;AAAA,IAAG;AAClI,WAAO;AAAA,EACT;AACA,SAAO,eAAe,MAAM,KAAK,KAAK,IAAI;AAC5C;AAgBA,eAAsB,gBACpB,UAA2C,CAAC,GAC5C,OAA0B,CAAC,GACmH;AAC9I,QAAM,OAAO,KAAK,QAAQ;AAC1B,QAAM,cAAc,MAAM,QAAQ,IAAI,aAAa,OAAO,EAAE,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM,YAAY,MAAM,KAAK,IAAI,CAAC,CAAC,GAAG,OAAO,OAAO;AAE/H,QAAM,SAAS,IAAI,IAAI,WAAW,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AACzD,QAAM,WAAW,oBAAI,IAAgC;AACrD,QAAM,OAAoB,CAAC;AAE3B,QAAM,UAAU,CAAC,SAAqC;AACpD,QAAI,IAAI,SAAS,IAAI,IAAI;AACzB,QAAI,EAAG,QAAO;AACd,SAAK,YAAY;AACf,YAAM,IAAI,OAAO,IAAI,IAAI;AACzB,UAAI,CAAC,EAAG,OAAM,IAAI,MAAM,eAAe,IAAI,yBAAyB;AACpE,YAAM,WAAW,KAAK,YAAY,CAAC,EAAE,IAAI;AACzC,UAAI,UAAU;AAAE,cAAM,IAAI,KAAK,IAAI,EAAE,GAAG;AAAG,YAAI,EAAG,QAAO;AAAA,MAAG;AAC5D,YAAM,SAAS,IAAI,UAAU,eAAe,EAAE,GAAG,CAAC;AAClD,YAAM,OAAO,QAAQ;AACrB,UAAI,SAAU,MAAK,IAAI,EAAE,KAAK,MAAM;AAAA,UAAQ,MAAK,KAAK,MAAM;AAC5D,aAAO;AAAA,IACT,GAAG;AACH,aAAS,IAAI,MAAM,CAAC;AACpB,WAAO;AAAA,EACT;AAEA,QAAM,SAAS;AAAA,IACb,WAAW,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,MAAM,EAAE;AAAA,IACxD,OAAO,QAAQ,SAAS,UAAU,MAAM,QAAQ,MAAM,GAAG,SAAS,SAAS,IAAI;AAAA,IAC/E;AAAA,EACF;AACA,QAAM,QAAQ,YAA2B;AAAE,eAAW,KAAK,KAAM,OAAM,EAAE,MAAM,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAG,SAAK,SAAS;AAAG,aAAS,MAAM;AAAA,EAAG;AACrI,SAAO,EAAE,GAAG,QAAQ,SAAS,MAAM;AACrC;AAQA,eAAsB,eACpB,UAA2C,CAAC,GAC5C,OAA0B,CAAC,GACyC;AACpE,QAAM,UAAU,aAAa,OAAO;AACpC,QAAM,cAAc,MAAM,QAAQ,IAAI,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM,YAAY,MAAM,KAAK,EAAE,GAAG,MAAM,UAAU,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,OAAO;AAC7I,QAAM,cAAc,IAAI,IAAI,WAAW,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AACzD,SAAO;AAAA,IACL,QAAQ,WAAW,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,IACpC,QAAQ,QAAQ,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,CAAC;AAAA,IACjE,WAAW,WAAW,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,MAAM,QAAQ,CAAC;AAAA,EAC9D;AACF;","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/** Hard cap for `tools/call`. NOT the 30s protocol timeout: a tool call is real work (an image render, a crawl)\n * that legitimately runs for minutes — the harness hands it to a job past its soft timeout and judges it by\n * progress/idle liveness (src/tools.jobs.ts). This only bounds a call nothing else ever stops. */\nexport const DEFAULT_CALL_TIMEOUT_MS = 30 * 60_000;\n\n/** An MCP `notifications/progress` payload. */\nexport interface McpProgress { progress?: number; total?: number; message?: string }\n\n/** Per-request options: cancellation, progress (sends a `_meta.progressToken`), and a deadline override. */\nexport interface McpRequestOptions {\n signal?: AbortSignal;\n onProgress?: (p: McpProgress) => void;\n timeoutMs?: number;\n}\n\n/** The deadline a request gets: the long call cap for `tools/call`, the protocol timeout for everything else. */\nfunction deadlineFor(method: string, spec: { timeoutMs?: number; callTimeoutMs?: number }, opts?: McpRequestOptions): number {\n if (opts?.timeoutMs != null) return opts.timeoutMs;\n if (method === 'tools/call') return spec.callTimeoutMs ?? Math.max(DEFAULT_CALL_TIMEOUT_MS, spec.timeoutMs ?? 0);\n return spec.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n}\n\n/** Attach a progress token (MCP `_meta.progressToken`) to request params. */\nfunction withProgressToken(params: unknown, token: string | number): unknown {\n const p = (params && typeof params === 'object' ? params : {}) as Record<string, any>;\n return { ...p, _meta: { ...(p._meta ?? {}), progressToken: token } };\n}\n\nconst abortError = (signal: AbortSignal): Error => (signal.reason instanceof Error ? signal.reason : Object.assign(new Error('MCP request cancelled'), { name: 'AbortError' }));\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, opts?: McpRequestOptions): 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 /** Protocol request deadline (initialize, tools/list, …). Default 30s. */\n timeoutMs?: number;\n /** `tools/call` hard cap. Default max(30 min, timeoutMs). */\n callTimeoutMs?: 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>; onProgress?: (p: McpProgress) => void; done?: () => void }>();\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 // Progress for one of our requests (token = its id) — a liveness signal for a long tool call.\n if (msg?.method === 'notifications/progress') {\n const p = this.pending.get(msg.params?.progressToken);\n try { p?.onProgress?.(msg.params ?? {}); } catch (e) { log.debug('onProgress handler threw', e); }\n return;\n }\n // Otherwise 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); p.done?.();\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.done?.(); 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, opts: McpRequestOptions = {}): Promise<any> {\n const id = this.nextId++;\n const timeoutMs = deadlineFor(method, this.spec, opts);\n const { signal, onProgress } = opts;\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(abortError(signal));\n const timer = timeoutMs > 0 ? setTimeout(() => {\n this.pending.delete(id); done();\n reject(new Error(`MCP request \"${method}\" timed out after ${timeoutMs}ms`));\n }, timeoutMs) : undefined;\n // Cancelled by the caller (a JobKill / stuck-job reap): stop waiting AND tell the server to stop working.\n const onAbort = () => {\n if (!this.pending.delete(id)) return;\n clearTimeout(timer);\n this.notify('notifications/cancelled', { requestId: id, reason: String((signal?.reason as any)?.message ?? 'cancelled') }).catch((e) => log.debug('cancel notify failed', e));\n reject(abortError(signal!));\n };\n const done = () => signal?.removeEventListener('abort', onAbort);\n signal?.addEventListener('abort', onAbort, { once: true });\n this.pending.set(id, { resolve, reject, timer, onProgress, done });\n try { this.write({ jsonrpc: '2.0', id, method, params: onProgress ? withProgressToken(params, id) : params }); }\n catch (e) { clearTimeout(timer); done(); 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 /** Protocol request deadline (initialize, tools/list, …). Default 30s. */\n timeoutMs?: number;\n /** `tools/call` hard cap. Default max(30 min, timeoutMs). */\n callTimeoutMs?: 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 async request(method: string, params?: unknown, opts: McpRequestOptions = {}): Promise<any> {\n const id = this.nextId++;\n const deadline = AbortSignal.timeout(deadlineFor(method, this.spec, opts));\n const signal = opts.signal ? AbortSignal.any([opts.signal, deadline]) : deadline;\n try {\n return await this.post({ jsonrpc: '2.0', id, method, params: opts.onProgress ? withProgressToken(params, id) : params }, false, signal, opts.onProgress);\n } catch (e) {\n if (opts.signal?.aborted) { // caller cancelled: tell the server to stop too (best-effort, never awaited by the caller)\n void this.notify('notifications/cancelled', { requestId: id, reason: String((opts.signal.reason as any)?.message ?? 'cancelled') }).catch((err) => log.debug('cancel notify failed', err));\n throw abortError(opts.signal);\n }\n throw e;\n }\n }\n\n async notify(method: string, params?: unknown): Promise<void> {\n await this.post({ jsonrpc: '2.0', method, params }, true, AbortSignal.timeout(this.spec.timeoutMs ?? DEFAULT_TIMEOUT_MS));\n }\n\n private async post(msg: any, isNotify: boolean, signal: AbortSignal, onProgress?: (p: McpProgress) => void): 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), signal,\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') ? await readSseResponse(resp, msg.id, onProgress) : 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/** One SSE `data:` line → the JSON-RPC message it carries (undefined when it isn't one). */\nfunction sseMessage(line: string): any {\n const trimmed = line.trimStart();\n if (!trimmed.startsWith('data:')) return undefined;\n try { return JSON.parse(trimmed.slice(5).trim()); }\n catch (e) { log.debug('skipping unparseable SSE data line', e); return undefined; }\n}\n\n/** Read an SSE response INCREMENTALLY: `notifications/progress` events are delivered as they arrive (liveness for a\n * long tool call), and the first response object (result/error) ends the read. */\nasync function readSseResponse(resp: Response, id: unknown, onProgress?: (p: McpProgress) => void): Promise<any> {\n const handle = (obj: any): any => {\n if (obj?.method === 'notifications/progress') { try { onProgress?.(obj.params ?? {}); } catch (e) { log.debug('onProgress handler threw', e); } return undefined; }\n if (obj && (obj.result !== undefined || obj.error !== undefined) && (obj.id == null || id == null || obj.id === id)) return obj;\n return undefined;\n };\n const reader = resp.body?.getReader?.();\n if (!reader) { for (const l of (await resp.text()).split('\\n')) { const r = handle(sseMessage(l)); if (r) return r; } return {}; }\n const dec = new TextDecoder();\n let buf = '';\n try {\n for (;;) {\n const { value, done } = await reader.read();\n buf += done ? dec.decode() : dec.decode(value, { stream: true });\n let nl: number;\n while ((nl = buf.indexOf('\\n')) >= 0 || (done && buf)) {\n const line = nl >= 0 ? buf.slice(0, nl) : buf;\n buf = nl >= 0 ? buf.slice(nl + 1) : '';\n const r = handle(sseMessage(line));\n if (r) return r;\n }\n if (done) return {};\n }\n } finally { reader.cancel().catch(() => {}); }\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 /** `tools/call`. Pass `onProgress` to request progress notifications (liveness) and `signal` to cancel. */\n async callTool(name: string, args: unknown, opts?: McpRequestOptions): Promise<unknown> {\n return this.transport.request('tools/call', { name, arguments: args ?? {} }, opts);\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 /** Protocol request deadline (initialize, tools/list, …). Default 30s. */\n timeoutMs?: number;\n /** `tools/call` hard cap (the call is handed to a job long before; this bounds what nothing else stops). Default 30 min. */\n callTimeoutMs?: 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, callTimeoutMs: cfg.callTimeoutMs })\n : new StdioTransport({ command: cfg.command!, args: cfg.args, env: cfg.env, cwd: cfg.cwd, timeoutMs: cfg.timeoutMs, callTimeoutMs: cfg.callTimeoutMs });\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, o) => client.callTool(tool, a, o), `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, o) => (await connect(server)).callTool(rawName, args, o),\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, ToolContext } 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/** Liveness + cancellation for one call: `onProgress` ⇒ the client requests MCP progress notifications. */\nexport interface McpCallOptions { signal?: AbortSignal; onProgress?: (p: { progress?: number; total?: number; message?: string }) => void }\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, opts?: McpCallOptions) => 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/**\n * Run one MCP call the way the harness can reason about it. Under an Agent (`ctx.toolJobs`) the call is a job:\n * progress notifications bump its liveness, and past the soft foreground timeout (`JobRegistryOptions.foregroundMs.mcp`,\n * default 30s) the model gets a job id instead of an error while the request KEEPS RUNNING — its result is delivered\n * when it lands, and only JobKill, the hard cap, or the idle reaper cancels it — the idle window applies only once the server has sent progress (then\n * stopped); a server that never reports progress is judged by its hard cap alone, since its silence is not evidence of being stuck. Without a registry (a bare\n * tool call) it keeps the old bounded foreground deadline.\n */\nasync function runMcpCall(name: string, call: (o: McpCallOptions) => Promise<unknown>, ctx: ToolContext | undefined, timeoutMs: number): Promise<string | McpToolResult> {\n const jobs = ctx?.toolJobs;\n const shape = (raw: unknown) => { const r = toResult(raw); return r.images?.length ? r : r.text; };\n if (!jobs) {\n const raw = await withDeadline(call({}), timeoutMs);\n return raw === TIMED_OUT ? mcpTimedOut(name, timeoutMs) : shape(raw);\n }\n const r = await jobs.runForeground(async ({ signal, touch, onChunk }) => {\n const raw = await call({\n signal,\n onProgress: (p) => { touch(); if (p?.message) onChunk(`[progress${p.progress != null ? ` ${p.progress}${p.total != null ? `/${p.total}` : ''}` : ''}] ${p.message}\\n`); },\n });\n const res = toResult(raw);\n onChunk(res.text + (res.images?.length ? `\\n[${res.images.length} image(s) in the result]` : ''));\n return res;\n }, { kind: 'mcp', label: name, idleAfterActivity: true }); // silence proves nothing until the server showed it reports progress\n if (r.done) return r.value.images?.length ? r.value : r.value.text;\n const soft = jobs.options.foregroundMs.mcp ?? 0;\n return `[still running] MCP call ${name} is still working after ${soft >= 1000 ? `${Math.round(soft / 1000)}s` : `${soft}ms`}, so it was handed to background job ${r.id} — NOT cancelled, the server keeps going. `\n + `Its result will be delivered to you when it finishes, so continue with other work; to block on it use JobWait({id:\"${r.id}\"}) instead of sleeping. `\n + `JobStatus/JobOutput show its progress; JobKill({id:\"${r.id}\"}) cancels it. Do NOT re-issue the same call — that would run it twice.`;\n}\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 run(args, ctx) {\n return runMcpCall(spec.name, (o) => callTool(spec.name, args ?? {}, o), ctx, timeoutMs);\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 }, ctx) {\n const n = String(name ?? '');\n if (!byName.has(n)) return `Error: unknown MCP tool '${n}'. Use ToolSearch to find valid names.`;\n return runMcpCall(n, (o) => callTool(n, args ?? {}, o), ctx, callTimeoutMs);\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, opts?: McpCallOptions): 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, opts?: McpCallOptions) => 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, o) => {\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 ?? {}, o);\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, o) => byName.get(server)!.client.callTool(rawName, args, o), 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":["r","log","p"]}
@@ -230,58 +230,195 @@ interface TodoItem {
230
230
  declare const todoWriteTool: AgentTool;
231
231
 
232
232
  /**
233
- * Sandbox background jobs — the in-process twin of `ShellJobRegistry` (src/tools.shell.ts), but for work
234
- * that runs *inside* the sandbox rather than as an OS child process. Backs `bash({background:true})` and
235
- * (later) background sub-agents.
233
+ * Job registry — ONE tracker for every async unit of tool work the harness may have to reason about:
234
+ * a sandbox `bash({background:true})`, a background sub-agent, a real OS shell job (`ShellJobRegistry`
235
+ * composes this), an MCP call handed off past its soft timeout, a delegated runtime's host/native tool.
236
+ * Edge-safe (no node imports): OS specifics (spawn, CPU probes) are injected by the producer.
236
237
  *
237
- * IMPORTANT — this is concurrency via async-I/O interleaving, NOT parallelism. The runtime is single
238
- * threaded: a CPU-bound JS command still blocks. Real overlap only happens when a job *awaits* — i.e. a
239
- * remote VFS backend (BodDbFilesystem, network per read) or a sub-agent (model calls). Over a pure in-mem
240
- * MemFilesystem, backgrounding a fast command buys nothing (the tool description says so).
238
+ * Each job carries LIVENESS, not just status: `lastActivityAt` is bumped by output chunks, MCP progress
239
+ * notifications, native tool events, or an injected CPU probe. That is what lets the harness tell a slow
240
+ * job from a stuck one — a job is only reaped when it has been silent past its kind's idle window (and a
241
+ * CPU probe, when one exists, says it is not burning CPU either), or when it passed its hard cap. A reaped
242
+ * job gets `status:'killed'` + a `reason` (`stuck: idle 600s`) the model sees; the run itself continues.
241
243
  *
242
- * The registry is kind-AGNOSTIC: it tracks any async producer's lifecycle + tail output. Overlay isolation
243
- * and commit-on-success are the *producer's* responsibility (IoC), so the registry stays a pure tracker.
244
+ * IMPORTANT (sandbox kinds): concurrency via async-I/O interleaving, NOT parallelism — a CPU-bound JS
245
+ * producer still blocks the thread. Overlay isolation / commit-on-success stay the PRODUCER's job (IoC).
244
246
  */
245
247
  type JobStatus = 'running' | 'done' | 'error' | 'killed';
246
- /** A producer: does the actual work, may stream progress via `onChunk`, returns its final text. */
248
+ /** Well-known kinds (free-form strings are allowed for host producers). */
249
+ type JobKind = 'shell' | 'mcp' | 'host' | 'native' | 'bash' | 'task' | (string & {});
250
+ interface JobCtx {
251
+ signal: AbortSignal;
252
+ /** Append output to the job's tail buffer (also counts as activity). */
253
+ onChunk: (s: string) => void;
254
+ /** Record liveness without output (e.g. an MCP progress notification). */
255
+ touch: (note?: string) => void;
256
+ }
257
+ /** A producer: does the work, may stream via `onChunk`/`touch`, resolves its result. */
247
258
  interface JobFn {
248
- (ctx: {
249
- signal: AbortSignal;
250
- onChunk: (s: string) => void;
251
- }): Promise<string>;
259
+ (ctx: JobCtx): Promise<unknown>;
260
+ }
261
+ interface JobTrackOptions {
262
+ kind?: JobKind;
263
+ label?: string;
264
+ /** Output produced before the registry took over (e.g. a foreground shell's output at handoff). */
265
+ seed?: string;
266
+ /** No activity for this long ⇒ stuck (0 = never idle-reaped). Default: `JobRegistryOptions.idleMs[kind]`. */
267
+ idleMs?: number;
268
+ /** Absolute wall-clock ceiling (0 = none). Default: `JobRegistryOptions.hardCapMs[kind]`. */
269
+ hardCapMs?: number;
270
+ /** Cumulative CPU seconds of the job's process(es), or undefined when unknown. Advancing CPU counts as activity. */
271
+ cpuTime?: () => Promise<number | undefined>;
272
+ /** How to actually stop the work (abort a request, SIGTERM a group). Called once, on kill. */
273
+ onKill?: (reason: string) => void;
274
+ /** Await this job in `drain()` (turn end). Default false for tracked jobs, true for `start()`. */
275
+ drain?: boolean;
276
+ /** Report the terminal state to `onExit` (the model). Default false; `detach()` turns it on. */
277
+ notify?: boolean;
278
+ /** Per-job terminal-state handler, used INSTEAD of the registry-wide `onExit` (a producer with its own notice format). */
279
+ onExit?: (n: JobNotice) => void;
280
+ /** Something is synchronously waiting on this job (a delegated runtime's stream) — it holds the model stall watchdog. */
281
+ holds?: boolean;
282
+ /** Arm the idle window only once the job has shown activity (`chunk`/`touch`). For producers whose silence proves
283
+ * nothing until they demonstrated a liveness channel — an MCP server that never sends progress is just working. */
284
+ idleAfterActivity?: boolean;
285
+ }
286
+ interface JobInfo {
287
+ id: string;
288
+ kind: JobKind;
289
+ label: string;
290
+ status: JobStatus;
291
+ startedAt: number;
292
+ lastActivityAt: number;
293
+ endedAt?: number;
294
+ /** Why it ended when that was not its own doing (`stuck: idle 600s`, `killed by JobKill`). */
295
+ reason?: string;
296
+ bytes: number;
297
+ holds: boolean;
298
+ }
299
+ interface JobNotice {
300
+ id: string;
301
+ kind: JobKind;
302
+ label: string;
303
+ status: JobStatus;
304
+ reason?: string;
305
+ tail: string;
306
+ result?: unknown;
307
+ }
308
+ declare class JobRegistryOptions {
309
+ /** Tail buffer cap per job (chars). */
310
+ maxBuffer: number;
311
+ /** Idle window per kind: no output/progress/CPU for this long ⇒ stuck. Kinds without an activity channel
312
+ * (host, native) are absent on purpose — silence is all they can ever show, so it proves nothing; their
313
+ * bound is the hard cap. Explicit `background:true` shell jobs opt out per job (a quiet server is healthy). */
314
+ idleMs: Record<string, number>;
315
+ /** Absolute ceiling per kind (0/absent = none). */
316
+ hardCapMs: Record<string, number>;
317
+ /** Soft foreground timeout per kind: past it the caller stops waiting and gets a job id instead. */
318
+ foregroundMs: Record<string, number>;
319
+ /** How often the reaper runs while reapable jobs exist. */
320
+ sweepMs: number;
321
+ /** Grace between the kill signal and a forced kill (producers that escalate read this). */
322
+ killGraceMs: number;
323
+ /** Finished jobs kept for inspection; older finished ones are pruned (every delegated tool call is a job). */
324
+ keepFinished: number;
325
+ /** Clock (injectable for tests). Late-bound so a patched Date.now is honoured. */
326
+ now: () => number;
327
+ /** Terminal-state notification for jobs with `notify` (the Agent wires this to `inject`). */
328
+ onExit?: (n: JobNotice) => void;
329
+ }
330
+ /** Handle to a tracked job: feed it activity, then settle it exactly once. */
331
+ interface JobHandle {
332
+ id: string;
333
+ chunk(s: string): void;
334
+ touch(note?: string): void;
335
+ finish(status: Exclude<JobStatus, 'running'>, result?: unknown, reason?: string): void;
336
+ /** Stop foreground-waiting: from now on the terminal state is reported via `onExit` and awaited by `drain()`. */
337
+ detach(): void;
338
+ readonly status: JobStatus;
252
339
  }
253
- /** Per-session registry of in-process background jobs. Bounded tail buffer; abortable; drainable at turn end. */
254
- declare class SandboxJobRegistry {
255
- private maxBuffer;
340
+ declare class JobRegistry {
341
+ options: JobRegistryOptions;
256
342
  private jobs;
257
343
  private seq;
258
- constructor(maxBuffer?: number);
259
- /** Start `fn` in the background and return a job id immediately (does not await completion). */
260
- start(fn: JobFn, opts?: {
261
- kind?: string;
262
- label?: string;
263
- }): string;
264
- /** Tail output for a job (null = no such job, '' = job exists but no output yet). */
344
+ private timer?;
345
+ constructor(options?: Partial<JobRegistryOptions>);
346
+ /** Register externally-driven work (a process, a request, a native tool) and get a handle to feed it. */
347
+ track(opts?: JobTrackOptions): JobHandle;
348
+ /** Start `fn` in the background and return a job id immediately (sandbox/subagent semantics: drained at turn end). */
349
+ start(fn: JobFn, opts?: JobTrackOptions): string;
350
+ /**
351
+ * Run `fn` in the FOREGROUND with a soft timeout: resolves `{done:true,value}` if it settles in time, else
352
+ * `{done:false,id}` — the work keeps going as a detached job whose completion is reported via `onExit`.
353
+ * A rejection before the timeout is rethrown (the caller's normal error path). `softTimeoutMs` ≤ 0 waits forever.
354
+ */
355
+ runForeground<T>(fn: (ctx: JobCtx) => Promise<T>, opts?: JobTrackOptions & {
356
+ softTimeoutMs?: number;
357
+ }): Promise<{
358
+ done: true;
359
+ value: T;
360
+ } | {
361
+ done: false;
362
+ id: string;
363
+ }>;
364
+ private run;
365
+ private append;
366
+ private end;
367
+ private prune;
368
+ /** Current tail output (null = no such job). */
265
369
  output(id: string): string | null;
370
+ /** Incremental read: output from absolute char `offset` (clamped to what the ring still holds). */
371
+ read(id: string, offset?: number): {
372
+ text: string;
373
+ next: number;
374
+ truncated: boolean;
375
+ } | null;
376
+ get(id: string): (JobInfo & {
377
+ result?: unknown;
378
+ }) | null;
266
379
  status(id: string): {
267
380
  status: JobStatus;
268
381
  bytes: number;
382
+ reason?: string;
269
383
  } | null;
270
- list(): Array<{
271
- id: string;
272
- kind: string;
273
- label: string;
274
- status: JobStatus;
275
- }>;
276
- /** Signal cancel (the producer decides what abort means — e.g. skip the overlay commit). */
277
- kill(id: string): boolean;
278
- killAll(): void;
279
- /** Await every still-running job. Called at turn end so a job's committed writes aren't lost from view
280
- * just because the model didn't poll it. Returns the ids that were still running when drain began. */
281
- drain(): Promise<string[]>;
384
+ list(): JobInfo[];
385
+ private info;
386
+ /** Wait (bounded) for a job to settle. Resolves the job's info either way; null for an unknown id. */
387
+ wait(id: string, timeoutMs: number): Promise<JobInfo | null>;
388
+ /** Stop a job: runs its `onKill` (abort/SIGTERM) and marks it killed with `reason`. */
389
+ kill(id: string, reason?: string): boolean;
390
+ /** Kill every running job — or, with `onlyDrainable`, only turn-scoped ones (sandbox/sub-agent/handed-off MCP),
391
+ * leaving jobs meant to outlive a run (a background dev server) alone. */
392
+ killAll(reason?: string, onlyDrainable?: boolean): void;
393
+ /** Jobs something is synchronously waiting on (see `JobTrackOptions.holds`) that are still running. */
394
+ holding(): JobInfo[];
395
+ /** Await every drainable running job (turn end). Returns the ids that were still running when drain began. */
396
+ drain(signal?: AbortSignal): Promise<string[]>;
397
+ /**
398
+ * The stuck-job decision, synchronous part: kill every running job past its hard cap, or past its idle
399
+ * window when it has no CPU probe to argue otherwise. Cheap — the stall watchdog calls it on every tick.
400
+ * Returns the killed ids. Jobs WITH a probe are left to `sweep()`.
401
+ */
402
+ reap(): string[];
403
+ /** Full sweep: sample CPU probes (advancing CPU = activity), then `reap()` including probed jobs. */
404
+ sweep(): Promise<string[]>;
405
+ private verdict;
406
+ private stuck;
407
+ /** Keep a reaper interval alive only while a reapable job is running (unref'd: never holds the process open). */
408
+ private arm;
409
+ /** Stop the reaper timer (tests / teardown). */
410
+ dispose(): void;
411
+ }
412
+ /** Back-compat name: the sandbox registry IS the generic registry. */
413
+ declare class SandboxJobRegistry extends JobRegistry {
414
+ constructor(maxBuffer?: number | Partial<JobRegistryOptions>);
282
415
  }
283
- /** Companion tools (JobOutput / JobStatus / JobKill) over a SandboxJobRegistry. */
284
- declare function makeJobTools(registry: SandboxJobRegistry): AgentTool[];
416
+ /** The text a job's terminal-state notice becomes when pushed to the model. */
417
+ declare function formatJobNotice(n: JobNotice): string;
418
+ /** Model-facing tools over a registry: JobOutput (tail / incremental), JobStatus (one / list), JobKill, JobWait (bounded). */
419
+ declare function makeJobTools(registry: JobRegistry, opts?: {
420
+ maxWaitMs?: number;
421
+ }): AgentTool[];
285
422
 
286
423
  /** A structured multiple-choice question the model can pose to a human. */
287
424
  interface UserQuestion {
@@ -370,7 +507,10 @@ interface ToolContext {
370
507
  ai?: ChatLike;
371
508
  model?: string;
372
509
  /** optional sandbox background-job registry; enables `bash({background:true})`. Absent => no backgrounding. */
373
- jobs?: SandboxJobRegistry;
510
+ jobs?: JobRegistry;
511
+ /** The Agent's job registry, always present under an Agent (independent of `backgroundJobs`): lets a tool that
512
+ * outlives its soft foreground timeout (an MCP call) hand itself off to a tracked job instead of failing. */
513
+ toolJobs?: JobRegistry;
374
514
  /** optional incremental-output channel: long-running tools (e.g. the real Shell) stream chunks
375
515
  * here mid-run. Wired PER CALL by Agent.dispatch to Hooks.onToolOutput (cleared when the call
376
516
  * settles — a late emit is a silent no-op). Fire-and-forget: never awaited, never part of the result. */
@@ -446,4 +586,4 @@ declare function toolRegistry(): Record<string, AgentTool>;
446
586
  * child to a tool its parent injected (`Bash`, `ToolSearch`, …). */
447
587
  declare function toolsByName(names: string[], available?: AgentTool[]): AgentTool[];
448
588
 
449
- 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 };
589
+ export { sendBytes as $, type AgentTool as A, elideInlineImages as B, type ChatLike as C, elideStaleImages as D, exitSessionTool as E, expandImagesForSend as F, formatJobNotice as G, type HostBridge as H, IMAGE_ELIDE_STUB as I, type JobCtx as J, imagePart as K, imageRefPart as L, type Message as M, imageRefResult as N, makeContext as O, makeJobTools as P, messageHasImage as Q, type Role as R, SandboxJobRegistry as S, type TodoItem as T, type UserQuestion as U, messageHasImageRef as V, messageHasInlineImage as W, mintImageRef as X, parseImageRef as Y, readTool as Z, remintImageRefs as _, type ChatOptions as a, sendBytesPer as a0, toWireTools as a1, todoWriteTool as a2, toolImageRef as a3, toolRegistry as a4, toolsByName as a5, type ChatResponse as b, type ContentPart as c, type HostEvent as d, IMAGE_REF_SCHEME as e, type ImageRef as f, type JobHandle as g, type JobInfo as h, type JobKind as i, type JobNotice as j, JobRegistry as k, JobRegistryOptions as l, type JobStatus as m, type JobTrackOptions as n, type MessageContent as o, type StreamChunk as p, type Tool as q, type ToolCall as r, type ToolContext as s, bashTool as t, clearImageRefs as u, contentBytes as v, contentText as w, defaultTools as x, editTool as y, elideImages as z };