zidane 4.1.8 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -2
- package/dist/{index-bgh-k8Mv.d.ts → agent-JhicgLOV.d.ts} +2082 -1969
- package/dist/agent-JhicgLOV.d.ts.map +1 -0
- package/dist/chat.d.ts +340 -9
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +2 -2
- package/dist/contexts.d.ts +1 -1
- package/dist/{index-DRoG_udt.d.ts → index-2yLUyTbc.d.ts} +34 -4
- package/dist/{index-DRoG_udt.d.ts.map → index-2yLUyTbc.d.ts.map} +1 -1
- package/dist/{index-BB4kuRh3.d.ts → index-CXVvqTQj.d.ts} +1 -1
- package/dist/{index-BB4kuRh3.d.ts.map → index-CXVvqTQj.d.ts.map} +1 -1
- package/dist/{index-Ds5YpvfZ.d.ts → index-t_W9i7Ql.d.ts} +9 -4
- package/dist/index-t_W9i7Ql.d.ts.map +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +6 -6
- package/dist/{interpolate-CukJwP2G.js → interpolate-Ck970-61.js} +11 -2
- package/dist/interpolate-Ck970-61.js.map +1 -0
- package/dist/{mcp-8wClKY-3.js → mcp-Dw-fRPVk.js} +61 -65
- package/dist/mcp-Dw-fRPVk.js.map +1 -0
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/presets-BRFH2qsQ.js +90 -0
- package/dist/presets-BRFH2qsQ.js.map +1 -0
- package/dist/presets.d.ts +3 -2
- package/dist/presets.js +2 -2
- package/dist/providers.d.ts +1 -1
- package/dist/session/sqlite.d.ts +13 -2
- package/dist/session/sqlite.d.ts.map +1 -1
- package/dist/session/sqlite.js +96 -38
- package/dist/session/sqlite.js.map +1 -1
- package/dist/{session-Cn68UASv.js → session-791hhrFa.js} +65 -30
- package/dist/session-791hhrFa.js.map +1 -0
- package/dist/session.d.ts +1 -1
- package/dist/session.js +1 -1
- package/dist/skills.d.ts +2 -2
- package/dist/skills.js +1 -1
- package/dist/{stats-BT9l57RS.js → stats-DZIsGqzu.js} +15 -5
- package/dist/stats-DZIsGqzu.js.map +1 -0
- package/dist/theme-pJv47erq.d.ts +1202 -0
- package/dist/theme-pJv47erq.d.ts.map +1 -0
- package/dist/{tools-C8kDot0H.js → tools-CLazLRb4.js} +475 -318
- package/dist/tools-CLazLRb4.js.map +1 -0
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +1 -1
- package/dist/tui.d.ts +303 -18
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +3305 -509
- package/dist/tui.js.map +1 -1
- package/dist/turn-operations-5aQu4dJg.js +3587 -0
- package/dist/turn-operations-5aQu4dJg.js.map +1 -0
- package/dist/types.d.ts +3 -3
- package/dist/types.js +1 -1
- package/package.json +6 -1
- package/dist/index-Ds5YpvfZ.d.ts.map +0 -1
- package/dist/index-bgh-k8Mv.d.ts.map +0 -1
- package/dist/interpolate-CukJwP2G.js.map +0 -1
- package/dist/mcp-8wClKY-3.js.map +0 -1
- package/dist/presets-BzkJDW1K.js +0 -39
- package/dist/presets-BzkJDW1K.js.map +0 -1
- package/dist/session-Cn68UASv.js.map +0 -1
- package/dist/stats-BT9l57RS.js.map +0 -1
- package/dist/theme-BlXO6yHe.d.ts +0 -503
- package/dist/theme-BlXO6yHe.d.ts.map +0 -1
- package/dist/theme-context-MungM3SY.js +0 -1713
- package/dist/theme-context-MungM3SY.js.map +0 -1
- package/dist/tools-C8kDot0H.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-Dw-fRPVk.js","names":[],"sources":["../src/mcp/sse-to-json-fetch.ts","../src/mcp/tolerant-client.ts","../src/mcp/index.ts"],"sourcesContent":["/**\n * `fetch` shim that converts streamable-http POST responses with\n * `Content-Type: text/event-stream` into synthetic `application/json`\n * responses, preserving every original header (notably `mcp-session-id`).\n *\n * Why this exists\n * ----------------\n * The MCP SDK's streamable-http transport handles POST responses two ways\n * depending on `content-type`:\n *\n * - `application/json` → `await response.json()` (works on every runtime).\n * - `text/event-stream` → `body.pipeThrough(TextDecoderStream)\n * .pipeThrough(EventSourceParserStream)`\n * and forward each parsed event to `onmessage`.\n *\n * On Bun + MCP SDK 1.29.x the second pipeline silently drops the parsed\n * event: the full `event: message\\ndata: {...}\\n\\n` arrives intact, the\n * stream closes, but `onmessage` is never called. Bootstrap then waits the\n * full `bootstrapTimeout` (10s default) and the agent loses every tool the\n * server would have exposed.\n *\n * The MCP spec lets clients accept either content type\n * (`Accept: application/json, text/event-stream`), so flipping the stream\n * to JSON at the boundary is a transparent, server-agnostic workaround.\n *\n * Scope of the shim\n * -----------------\n * - POST + `text/event-stream` → drain, parse SSE message events, return\n * a synthetic `application/json` response. Single event becomes a JSON\n * object (matching the shape the SDK expects from non-streaming\n * servers); multiple events become a JSON array (the SDK already\n * iterates `Array.isArray(data)` from a JSON response).\n * - GET (long-lived `_startOrAuthSse` listener) → untouched. Per-event\n * latency matters there and the SSE pipeline isn't always broken on\n * GET in the same way (different code path inside Bun's stream impl).\n * - 202 / non-SSE / malformed SSE / no body → passthrough.\n *\n * Runtime gating\n * --------------\n * `sseToJsonFetchIfNeeded()` only returns a wrapper on Bun. Node + browser\n * runtimes get `undefined` and the SDK uses global `fetch` directly — no\n * extra buffer-and-redrain on the happy path, and no risk of collapsing a\n * future progress-streaming response into a single batched array. If you\n * need to apply the shim unconditionally (testing, custom hosts), call\n * `sseToJsonFetch()` directly.\n *\n * Cleanup\n * -------\n * Remove this file and its `fetch:` injection in `createTransport` once\n * either Bun fixes the `pipeThrough` chain or the SDK switches off the\n * streaming pipeline by default.\n */\n\n/**\n * Detect whether we're running on Bun. The `Bun` global is set by the\n * runtime itself and isn't faked by Bun's Node-compat layer.\n */\nfunction isBunRuntime(): boolean {\n return typeof (globalThis as { Bun?: unknown }).Bun !== 'undefined'\n}\n\n/**\n * Returns the `sseToJsonFetch` wrapper only on runtimes that need it\n * (currently Bun). Returns `undefined` everywhere else, which makes the\n * SDK fall back to the global `fetch` and keeps the streaming pipeline\n * intact on Node where it works correctly.\n *\n * Designed as the value to pass directly to `StreamableHTTPClientTransport`'s\n * `fetch` option:\n *\n * new StreamableHTTPClientTransport(url, {\n * fetch: sseToJsonFetchIfNeeded(),\n * })\n */\nexport function sseToJsonFetchIfNeeded(): typeof fetch | undefined {\n return isBunRuntime() ? sseToJsonFetch() : undefined\n}\n\n/**\n * Wrap a `fetch` implementation so streamable-http POST responses that come\n * back as `text/event-stream` are converted to JSON. Pass the result as\n * `opts.fetch` to `StreamableHTTPClientTransport`.\n *\n * Always-on (i.e. unconditional) — for the runtime-gated entry point,\n * use {@link sseToJsonFetchIfNeeded} instead.\n */\nexport function sseToJsonFetch(baseFetch: typeof fetch = fetch): typeof fetch {\n return async function sseToJsonWrappedFetch(input, init) {\n const response = await baseFetch(input, init)\n\n // Only intercept POSTs. The SDK's GET path opens a long-lived SSE\n // listener (`_startOrAuthSse`) that we must not buffer-drain.\n const method = (init?.method ?? 'GET').toString().toUpperCase()\n if (method !== 'POST')\n return response\n\n const contentType = response.headers.get('content-type')\n if (!contentType || !contentType.includes('text/event-stream'))\n return response\n\n if (!response.body)\n return response\n\n let raw: string\n try {\n raw = await response.text()\n }\n catch {\n // Bun edge case: if even .text() fails, surrender — the original\n // response is already drained, so we can't recover. Return it; the\n // SDK will surface the underlying read error.\n return response\n }\n\n const events = parseSseDataEvents(raw)\n // Single event → bare object; multi-event or zero → array. The SDK's\n // JSON branch handles both shapes (`Array.isArray(data) ? data.map(…) : …`).\n const payload = events.length === 1 ? events[0] : events\n return synthesizeJsonResponse(response, payload)\n } as typeof fetch\n}\n\n/**\n * Parse a buffered SSE body into the JSON payloads of its `message` events.\n *\n * Skips:\n * - SSE comments (lines starting with `:`).\n * - Non-default event types (`event: foo` ≠ `message`). The MCP server\n * only ever emits `message` events for JSON-RPC; anything else is out of\n * band and the SDK wouldn't have surfaced it to `onmessage` either.\n * - Malformed `data:` payloads (anything that fails `JSON.parse`).\n */\nfunction parseSseDataEvents(raw: string): unknown[] {\n const events: unknown[] = []\n for (const block of raw.split(/\\r?\\n\\r?\\n/)) {\n if (!block.trim())\n continue\n\n const dataLines: string[] = []\n let isMessageEvent = true\n for (const line of block.split(/\\r?\\n/)) {\n if (line.startsWith(':'))\n continue\n if (line.startsWith('event:')) {\n const eventType = line.slice('event:'.length).trim()\n if (eventType && eventType !== 'message')\n isMessageEvent = false\n }\n else if (line.startsWith('data:')) {\n // Per SSE spec: a single leading space after `data:` is part of the\n // separator, not the payload. Anything beyond it is.\n const value = line.slice('data:'.length)\n dataLines.push(value.startsWith(' ') ? value.slice(1) : value)\n }\n }\n\n if (!isMessageEvent || dataLines.length === 0)\n continue\n\n try {\n events.push(JSON.parse(dataLines.join('\\n')))\n }\n catch {\n // Drop malformed events. The SDK's broken pipe would have dropped\n // them too — staying silent here matches that baseline.\n }\n }\n return events\n}\n\n/**\n * Build a `Response` mirroring the original's status / statusText / headers\n * but with a JSON body and `content-type: application/json`. Header\n * preservation is the whole point — `mcp-session-id` is set by the server\n * on the initialize POST and must round-trip into the SDK's\n * `_sessionId` capture (`response.headers.get('mcp-session-id')`).\n */\nfunction synthesizeJsonResponse(original: Response, payload: unknown): Response {\n const headers = new Headers(original.headers)\n headers.set('content-type', 'application/json')\n return new Response(JSON.stringify(payload), {\n status: original.status,\n statusText: original.statusText,\n headers,\n })\n}\n","/**\n * Drop-in `Client` subclass whose `connect()` mirrors the upstream MCP SDK\n * sequence but treats `notifications/initialized` as best-effort.\n *\n * The MCP spec marks that notification fire-and-forget, but the SDK awaits\n * the underlying transport `send()` and rethrows on any HTTP 4xx. Several\n * real-world streamable-http servers (e.g. browser-codemode, custom MCPs\n * that gate non-initialize routes on a session id that didn't yet exist when\n * the notification posted) reject the notification with a 4xx and still\n * accept every subsequent request. The base `Client.connect()` then closes\n * the transport, the bootstrap fails, and the agent silently loses every\n * tool the server would have exposed.\n *\n * This module changes only that single step — log + continue if the\n * notification throws. Initialize, capability/version capture,\n * `setProtocolVersion`, listChanged handler wiring, and the close-on-failure\n * path for everything else are preserved verbatim from the SDK.\n *\n * Lazy SDK load: `@modelcontextprotocol/sdk` is an *optional* peer dep, so\n * the class is built inside `createTolerantClient()` via dynamic imports.\n * Importing this module does NOT trigger SDK resolution — callers only\n * incur the cost when they actually instantiate a client.\n *\n * Should be removed when the SDK lands an opt-in tolerance flag upstream.\n */\nimport type { Client } from '@modelcontextprotocol/sdk/client/index.js'\n\n// Private fields the override needs to read/write to mirror upstream behavior.\n// Cast surface is isolated to this one type so the rest of the file stays clean.\ninterface ClientPrivates {\n _capabilities: unknown\n _clientInfo: unknown\n _serverCapabilities: unknown\n _serverVersion: unknown\n _instructions: unknown\n _pendingListChangedConfig?: unknown\n _setupListChangedHandlers: (config: unknown) => void\n}\n\ninterface MaybeSessionedTransport {\n sessionId?: unknown\n setProtocolVersion?: (v: string) => void\n}\n\n/**\n * Options forwarded to the upstream `Client` constructor. Mirrors the\n * publicly-observable shape so we don't have to re-export `Client`'s\n * private `ClientOptions` type (which would force consumers of the lazy\n * factory below to install the SDK at type-check time too).\n */\nexport interface TolerantClientOptions {\n /** Capabilities advertised to the server on `initialize`. */\n capabilities?: Record<string, unknown>\n /** When true, the SDK rejects requests for methods the server didn't advertise. */\n enforceStrictCapabilities?: boolean\n /** Per-request timeout in ms; the SDK falls back to its own default when unset. */\n defaultRequestTimeout?: number\n}\n\n/**\n * Async factory — builds an instance of the tolerant MCP client. The\n * actual `Client` subclass definition lives inside the factory so the\n * `@modelcontextprotocol/sdk` import only resolves when MCP is in use.\n *\n * Second `options` arg forwards directly into the upstream `Client`\n * constructor so tests / advanced consumers can supply capabilities,\n * `enforceStrictCapabilities`, etc.\n */\nexport async function createTolerantClient(\n info: { name: string, version: string },\n options?: TolerantClientOptions,\n): Promise<Client> {\n const { Client } = await import('@modelcontextprotocol/sdk/client/index.js')\n const { Protocol } = await import('@modelcontextprotocol/sdk/shared/protocol.js')\n const { InitializeResultSchema, LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS } = await import('@modelcontextprotocol/sdk/types.js')\n\n class TolerantMcpClient extends Client {\n async connect(\n transport: Parameters<Client['connect']>[0],\n options?: Parameters<Client['connect']>[1],\n ): Promise<void> {\n // Wire the transport (Protocol.connect — onmessage/onclose/onerror hooks\n // and transport.start) without sending any messages. We MUST NOT call\n // `super.connect()` here — that's `Client.connect`, which would\n // re-execute the very initialize+notification dance we're overriding.\n await Protocol.prototype.connect.call(this as never, transport)\n\n // Reconnect path: the upstream client short-circuits on a transport that\n // already carries a session id, so do we.\n if ((transport as MaybeSessionedTransport).sessionId !== undefined)\n return\n\n const self = this as unknown as ClientPrivates\n\n try {\n const result = await this.request(\n {\n method: 'initialize',\n params: {\n protocolVersion: LATEST_PROTOCOL_VERSION,\n capabilities: self._capabilities,\n clientInfo: self._clientInfo,\n },\n },\n InitializeResultSchema,\n options,\n )\n\n if (result === undefined)\n throw new Error(`Server sent invalid initialize result: ${result}`)\n if (!SUPPORTED_PROTOCOL_VERSIONS.includes(result.protocolVersion))\n throw new Error(`Server's protocol version is not supported: ${result.protocolVersion}`)\n\n self._serverCapabilities = result.capabilities\n self._serverVersion = result.serverInfo\n\n const setProtocolVersion = (transport as MaybeSessionedTransport).setProtocolVersion\n if (setProtocolVersion)\n setProtocolVersion.call(transport, result.protocolVersion)\n\n self._instructions = result.instructions\n\n // Best-effort. The session id (if any) is already captured by the\n // transport from the `initialize` response headers, so subsequent\n // requests like `tools/list` still authenticate. Don't bring down the\n // whole connection on a 4xx for a fire-and-forget notification.\n try {\n await this.notification({ method: 'notifications/initialized' })\n }\n catch (notifyError) {\n const msg = notifyError instanceof Error ? notifyError.message : String(notifyError)\n console.warn(`[zidane:mcp] server rejected notifications/initialized (continuing): ${msg}`)\n }\n\n if (self._pendingListChangedConfig) {\n self._setupListChangedHandlers(self._pendingListChangedConfig)\n self._pendingListChangedConfig = undefined\n }\n }\n catch (error) {\n // Same failure path as upstream Client — anything *other* than the\n // tolerated notification failure tears the transport down so the\n // bootstrap surfaces a clear error.\n void this.close()\n throw error\n }\n }\n }\n\n return new TolerantMcpClient(info, options)\n}\n","/**\n * MCP (Model Context Protocol) server support.\n *\n * Connects to one or more MCP servers, discovers their tools,\n * and wraps them as zidane ToolDefs for use in agent loops.\n */\n\nimport type { Client } from '@modelcontextprotocol/sdk/client/index.js'\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from '../agent'\nimport type { ToolContext, ToolDef } from '../tools/types'\nimport type { McpServerConfig, ToolResultContent } from '../types'\nimport { Buffer } from 'node:buffer'\nimport { toolOutputByteLength } from '../types'\nimport { sseToJsonFetchIfNeeded } from './sse-to-json-fetch'\nimport { createTolerantClient } from './tolerant-client'\n\n// NOTE: `@modelcontextprotocol/sdk` is an *optional* peer dependency\n// (see package.json). Static imports here would force every consumer of\n// `zidane` to install the SDK even if they never wire any MCP servers —\n// which fails on install with `--no-optional` or in restricted CI\n// environments. All runtime imports are inside `createTransport` /\n// `loadTolerantClient` below so the SDK is only resolved when MCP is\n// actually used. Type-only imports above are erased at compile time.\n\nexport type { McpServerConfig } from '../types'\n\nexport interface McpConnection {\n tools: Record<string, ToolDef>\n close: () => Promise<void>\n}\n\n// ---------------------------------------------------------------------------\n// Shape normalization\n// ---------------------------------------------------------------------------\n\ninterface RawServerShape {\n name?: string\n transport?: string\n type?: string\n command?: string\n args?: string[]\n env?: Record<string, string>\n strictEnv?: boolean\n url?: string\n httpUrl?: string\n sseUrl?: string\n headers?: Record<string, string>\n bootstrapTimeout?: number\n toolTimeout?: number\n enabledTools?: string[]\n disabledTools?: string[]\n toolFilter?: McpServerConfig['toolFilter']\n [key: string]: unknown\n}\n\nconst DEFAULT_MCP_BOOTSTRAP_TIMEOUT_MS = 10_000\n\nfunction inferTransport(raw: RawServerShape): McpServerConfig['transport'] {\n if (raw.transport === 'stdio' || raw.transport === 'sse' || raw.transport === 'streamable-http')\n return raw.transport\n if (raw.type === 'stdio' || raw.type === 'sse' || raw.type === 'streamable-http' || raw.type === 'http')\n return raw.type === 'http' ? 'streamable-http' : raw.type\n if (raw.command)\n return 'stdio'\n if (raw.httpUrl)\n return 'streamable-http'\n if (raw.sseUrl)\n return 'sse'\n if (raw.url)\n return 'streamable-http'\n throw new Error(`Cannot infer MCP transport from config: ${JSON.stringify(raw)}`)\n}\n\nfunction normalizeOne(name: string, raw: RawServerShape): McpServerConfig {\n const transport = inferTransport(raw)\n const url = raw.url ?? raw.httpUrl ?? raw.sseUrl\n\n const config: McpServerConfig = { name, transport }\n if (raw.command)\n config.command = raw.command\n if (raw.args)\n config.args = raw.args\n if (raw.env)\n config.env = raw.env\n if (raw.strictEnv === true)\n config.strictEnv = true\n if (url)\n config.url = url\n if (raw.headers)\n config.headers = raw.headers\n if (typeof raw.bootstrapTimeout === 'number')\n config.bootstrapTimeout = raw.bootstrapTimeout\n if (typeof raw.toolTimeout === 'number')\n config.toolTimeout = raw.toolTimeout\n if (Array.isArray(raw.enabledTools))\n config.enabledTools = raw.enabledTools\n if (Array.isArray(raw.disabledTools))\n config.disabledTools = raw.disabledTools\n if (typeof raw.toolFilter === 'function')\n config.toolFilter = raw.toolFilter\n\n return config\n}\n\n/**\n * True when the input looks like a single config object (flat fields like `command`,\n * `transport`, `url`) rather than a record whose values are configs.\n */\nfunction looksLikeSingleConfig(obj: Record<string, unknown>): boolean {\n const singleConfigKeys = ['transport', 'type', 'command', 'url', 'httpUrl', 'sseUrl']\n return singleConfigKeys.some(key => typeof obj[key] === 'string')\n}\n\n/**\n * Normalize MCP server configs from any common shape to `McpServerConfig[]`.\n *\n * Accepts:\n * - `McpServerConfig[]` — zidane native (pass-through).\n * - `McpServerConfig` — a single config object (wrapped to a 1-element array).\n * - `Record<string, RawShape>` — name-keyed map (common in host-SDK configs), where the key is the server name.\n * - Mixed shapes with `type` vs `transport`, `httpUrl`/`sseUrl` vs `url`.\n *\n * Returns `[]` when `input` is nullish. Throws a descriptive error when the transport\n * cannot be inferred from a given entry, or when the input shape is unsupported.\n */\nexport function normalizeMcpServers(input: unknown): McpServerConfig[] {\n if (input == null)\n return []\n\n if (Array.isArray(input)) {\n return input.map((raw, idx) => {\n const obj = raw as RawServerShape\n const name = obj.name ?? `mcp_${idx}`\n return normalizeOne(name, obj)\n })\n }\n\n if (typeof input === 'object') {\n const obj = input as Record<string, unknown>\n // Single-config heuristic: flat fields like `transport`/`command`/`url` at the top\n // level indicate a single McpServerConfig, not a record of configs.\n if (looksLikeSingleConfig(obj)) {\n const raw = obj as RawServerShape\n const name = raw.name ?? 'mcp_0'\n return [normalizeOne(name, raw)]\n }\n return Object.entries(obj as Record<string, RawServerShape>).map(\n ([name, raw]) => normalizeOne(name, raw ?? {}),\n )\n }\n\n throw new Error(`Unsupported MCP server config shape: ${typeof input}`)\n}\n\n/**\n * Lossy flattener — converts MCP `CallToolResult.content` blocks to a single\n * string. Text blocks are extracted; non-text blocks are JSON-stringified.\n *\n * Use this only at UI / log boundaries that require a string. The agent\n * loop itself routes through {@link normalizeMcpBlocks} so image blocks\n * survive into provider-native tool_result content (Anthropic blocks,\n * OpenAI companion-user-message).\n */\nexport function resultToString(content: unknown[]): string {\n if (!content || !Array.isArray(content))\n return ''\n return content\n .map((block) => {\n if (block && typeof block === 'object' && (block as { type?: unknown }).type === 'text') {\n const text = (block as { text?: unknown }).text\n if (typeof text === 'string')\n return text\n }\n return JSON.stringify(block)\n })\n .join('\\n')\n}\n\n/**\n * Normalize MCP `CallToolResult.content` to zidane's {@link ToolResultContent[]} shape.\n *\n * Handles the four MCP content block types:\n * - `text` → preserved as `{type:'text', text}`\n * - `image` → preserved as `{type:'image', mediaType, data}` (MCP uses `mimeType`)\n * - `resource` with embedded text → flattened to a text block\n * - `resource` with embedded blob whose `mimeType` is `image/*` → flattened to an image block\n * - Any unrecognized block → JSON-stringified fallback text block (lossy but safe)\n *\n * Returns `null` when the input is not an array — callers should fall back to an empty\n * result in that case.\n */\nexport function normalizeMcpBlocks(content: unknown): ToolResultContent[] | null {\n if (!Array.isArray(content))\n return null\n\n const out: ToolResultContent[] = []\n for (const raw of content) {\n if (!raw || typeof raw !== 'object')\n continue\n const block = raw as Record<string, unknown>\n\n if (block.type === 'text' && typeof block.text === 'string') {\n out.push({ type: 'text', text: block.text })\n continue\n }\n\n if (block.type === 'image' && typeof block.data === 'string') {\n const mediaType = typeof block.mimeType === 'string'\n ? block.mimeType\n : (typeof block.mediaType === 'string' ? block.mediaType : 'image/png')\n out.push({ type: 'image', mediaType, data: block.data })\n continue\n }\n\n if (block.type === 'resource' && block.resource && typeof block.resource === 'object') {\n const res = block.resource as Record<string, unknown>\n if (typeof res.text === 'string') {\n out.push({ type: 'text', text: res.text })\n continue\n }\n if (typeof res.blob === 'string' && typeof res.mimeType === 'string' && res.mimeType.startsWith('image/')) {\n out.push({ type: 'image', mediaType: res.mimeType, data: res.blob })\n continue\n }\n }\n\n // Audio, resource_link, and unknown block shapes — fall back to a JSON-stringified\n // text block. Lossy but keeps the information addressable by the model.\n out.push({ type: 'text', text: JSON.stringify(block) })\n }\n\n return out\n}\n\n/**\n * Route the MCP result content through the narrowest appropriate zidane shape:\n *\n * - All blocks are `text` → return a joined string (smaller wire payload,\n * string-friendly for hook consumers that don't need structured access).\n * - Any block is non-text → return a structured `ToolResultContent[]`.\n * - Empty / non-array input → return `''`.\n */\nfunction packMcpResult(content: unknown): string | ToolResultContent[] {\n const normalized = normalizeMcpBlocks(content)\n if (!normalized || normalized.length === 0)\n return ''\n\n // Single pass: build the joined string as we go. Bail to the structured array\n // the moment we hit a non-text block.\n const parts: string[] = []\n for (const block of normalized) {\n if (block.type !== 'text')\n return normalized\n parts.push(block.text)\n }\n return parts.join('\\n')\n}\n\n/**\n * Create the appropriate MCP transport for a server config.\n *\n * For stdio: when `config.env` is provided, it is merged on top of the MCP SDK's\n * `getDefaultEnvironment()` whitelist (`PATH`, `HOME`, `LANG`, `SHELL`, `USER` on\n * POSIX; `APPDATA`, `PATH`, ... on Win32). Without this defensive merge, older\n * MCP SDK versions strip `PATH` the moment a consumer sets any env, breaking\n * `spawn('node', ...)` with ENOENT. Pass `strictEnv: true` to opt out and send\n * `env` verbatim.\n */\nasync function createTransport(config: McpServerConfig) {\n switch (config.transport) {\n case 'stdio': {\n const { StdioClientTransport, getDefaultEnvironment } = await import('@modelcontextprotocol/sdk/client/stdio.js')\n const mergedEnv = config.env && !config.strictEnv\n ? { ...getDefaultEnvironment(), ...config.env }\n : config.env\n return new StdioClientTransport({\n command: config.command!,\n args: config.args,\n env: mergedEnv,\n })\n }\n case 'sse': {\n const { SSEClientTransport } = await import('@modelcontextprotocol/sdk/client/sse.js')\n return new SSEClientTransport(new URL(config.url!), {\n requestInit: config.headers ? { headers: config.headers } : undefined,\n })\n }\n case 'streamable-http': {\n // `fetch: sseToJsonFetchIfNeeded()` works around a Bun + MCP SDK\n // 1.29.x bug where `text/event-stream` POST responses are buffered\n // correctly but never surface to `onmessage`, dead-locking bootstrap.\n // The helper returns the wrapper only on Bun and `undefined` on Node\n // (so the SDK's streaming pipeline stays intact where it works).\n // See ./sse-to-json-fetch.ts.\n const { StreamableHTTPClientTransport } = await import('@modelcontextprotocol/sdk/client/streamableHttp.js')\n return new StreamableHTTPClientTransport(new URL(config.url!), {\n requestInit: config.headers ? { headers: config.headers } : undefined,\n fetch: sseToJsonFetchIfNeeded(),\n })\n }\n default:\n throw new Error(`Unknown MCP transport: ${config.transport}`)\n }\n}\n\n/**\n * Connect to MCP servers and discover their tools.\n *\n * Each tool is namespaced as `mcp_{serverName}_{toolName}` to avoid\n * collisions with agent tools or tools from other servers.\n *\n * @param configs - Array of MCP server configurations\n * @param _clientFactory - Internal: override client construction for testing\n * @param hooks - Optional agent hooks for firing mcp:connect, mcp:error, mcp:close events\n */\nexport async function connectMcpServers(\n configs: McpServerConfig[],\n _clientFactory?: () => Client,\n hooks?: Hookable<AgentHooks>,\n): Promise<McpConnection> {\n const connections: { name: string, client: Client }[] = []\n const tools: Record<string, ToolDef> = {}\n const errors: { name: string, error: Error }[] = []\n let closed = false\n\n // Bootstrap every server in parallel. Previously this was a sequential for-loop,\n // which meant a single slow server (GitHub MCP, cold streamable-http endpoints,\n // anything on a flaky network) blocked the whole first `agent.run()` for up to\n // N × bootstrapTimeout. With `Promise.all` + per-server try/catch, wall-clock\n // collapses to the slowest server, and the existing partial-failure tolerance\n // keeps the agent alive when any subset connects.\n const bootstrapResults = await Promise.all(configs.map(config => bootstrapServer(config, _clientFactory, hooks)))\n\n for (const result of bootstrapResults) {\n if (!result.ok) {\n errors.push({ name: result.name, error: result.error })\n await hooks?.callHook('mcp:error', { name: result.name, error: result.error })\n continue\n }\n\n connections.push({ name: result.name, client: result.client })\n const toolNames: string[] = []\n for (const tool of result.tools) {\n const namespacedName = `mcp_${result.config.name}_${tool.name}`\n toolNames.push(namespacedName)\n tools[namespacedName] = buildMcpToolDef(result.config, result.client, tool, namespacedName, hooks)\n }\n await hooks?.callHook('mcp:connect', {\n name: result.name,\n transport: result.config.transport,\n tools: toolNames,\n })\n }\n\n // If all servers failed, throw; partial failures are tolerated\n if (errors.length > 0 && connections.length === 0) {\n const messages = errors.map(e => `${e.name}: ${e.error.message}`).join(', ')\n throw new Error(`All MCP servers failed to connect: ${messages}`)\n }\n\n return {\n tools,\n close: async () => {\n // Idempotent — double-close on `agent.destroy()` retry should not\n // re-fire the hook or call `client.close()` twice (the stdio transport\n // treats a second close as a no-op but some transports throw).\n if (closed)\n return\n closed = true\n await Promise.allSettled(\n connections.map(async ({ name, client }) => {\n await hooks?.callHook('mcp:close', { name })\n await client.close()\n }),\n )\n },\n }\n}\n\n/**\n * Discriminated result returned by `bootstrapServer` so `connectMcpServers` can\n * aggregate successes + failures without letting a rejected promise tear down\n * the whole parallel batch.\n */\ntype BootstrapResult\n = | { ok: true, name: string, config: McpServerConfig, client: Client, tools: Array<{ name: string, description?: string | null, inputSchema?: unknown }> }\n | { ok: false, name: string, error: Error }\n\n/**\n * Connect one MCP server and list its tools, wrapped in a single race against\n * `config.bootstrapTimeout` (default 10s). Always emits `mcp:bootstrap:start`\n * before network I/O and `mcp:bootstrap:end` with `durationMs` + outcome, so a\n * host can build a timing view even when every server succeeds.\n *\n * Errors are captured into the `{ ok: false }` result rather than thrown — the\n * parent uses `Promise.all` across every server and we can't let one rejection\n * short-circuit the batch.\n */\nasync function bootstrapServer(\n config: McpServerConfig,\n _clientFactory: (() => Client) | undefined,\n hooks: Hookable<AgentHooks> | undefined,\n): Promise<BootstrapResult> {\n const start = Date.now()\n // Validate mutually-exclusive filter fields up-front so the consumer gets a\n // typed `mcp:bootstrap:end` failure rather than a silent later-stage drop.\n if (config.enabledTools && config.disabledTools) {\n const error = new Error(\n `MCP server \"${config.name}\": enabledTools and disabledTools are mutually exclusive — set one or the other, not both.`,\n )\n await hooks?.callHook('mcp:bootstrap:start', { name: config.name, transport: config.transport })\n await hooks?.callHook('mcp:bootstrap:end', {\n name: config.name,\n transport: config.transport,\n durationMs: 0,\n ok: false,\n error,\n })\n return { ok: false, name: config.name, error }\n }\n\n await hooks?.callHook('mcp:bootstrap:start', { name: config.name, transport: config.transport })\n\n let client: Client | null = null\n try {\n // TolerantMcpClient is a drop-in subclass that survives a 4xx on the\n // `notifications/initialized` post (some real-world servers reject it\n // without that breaking subsequent `tools/list` etc.). See\n // ./tolerant-client.ts for the exact semantics.\n client = _clientFactory\n ? _clientFactory()\n : await createTolerantClient({ name: 'zidane', version: '1.0.0' })\n const currentClient = client\n\n const transport = await createTransport(config)\n const bootstrapTimeout = config.bootstrapTimeout ?? DEFAULT_MCP_BOOTSTRAP_TIMEOUT_MS\n const { tools: mcpTools } = await raceWithTimeout(\n async () => {\n await currentClient.connect(transport)\n return await currentClient.listTools()\n },\n bootstrapTimeout,\n `MCP server \"${config.name}\" bootstrap timed out after ${bootstrapTimeout}ms`,\n )\n\n // Per-tool filtering — config-side first (static host policy), then the\n // `mcp:tools:filter` hook (runtime / dynamic decisions). Both compose AND-\n // style: a tool needs to pass every stage to land in the agent registry.\n const filteredTools = await applyMcpToolFilters(config, mcpTools, hooks)\n\n const durationMs = Date.now() - start\n await hooks?.callHook('mcp:bootstrap:end', {\n name: config.name,\n transport: config.transport,\n durationMs,\n ok: true,\n toolCount: filteredTools.length,\n })\n return { ok: true, name: config.name, config, client: currentClient, tools: filteredTools }\n }\n catch (err) {\n const error = err instanceof Error ? err : new Error(String(err))\n await closeClientQuietly(client)\n const durationMs = Date.now() - start\n await hooks?.callHook('mcp:bootstrap:end', {\n name: config.name,\n transport: config.transport,\n durationMs,\n ok: false,\n error,\n })\n return { ok: false, name: config.name, error }\n }\n}\n\n/**\n * Apply config-side filters (`enabledTools` / `disabledTools` / `toolFilter`)\n * and then the `mcp:tools:filter` hook to the upstream tool list.\n *\n * Composition order — narrowest-to-widest:\n * 1. Allow-list (`enabledTools`) — keep only listed tools.\n * 2. Deny-list (`disabledTools`) — drop listed tools.\n * 3. Predicate (`toolFilter`) — fine-grained metadata filtering.\n * 4. Hook (`mcp:tools:filter`) — runtime / per-host decisions.\n *\n * The mutual exclusion of `enabledTools` and `disabledTools` is checked in\n * `bootstrapServer` so this stays a pure data transform.\n */\nasync function applyMcpToolFilters(\n config: McpServerConfig,\n tools: Array<{ name: string, description?: string | null, inputSchema?: unknown }>,\n hooks: Hookable<AgentHooks> | undefined,\n): Promise<Array<{ name: string, description?: string | null, inputSchema?: unknown }>> {\n let filtered = tools\n\n if (config.enabledTools && config.enabledTools.length > 0) {\n const allow = new Set(config.enabledTools)\n filtered = filtered.filter(t => allow.has(t.name))\n }\n\n if (config.disabledTools && config.disabledTools.length > 0) {\n const deny = new Set(config.disabledTools)\n filtered = filtered.filter(t => !deny.has(t.name))\n }\n\n if (config.toolFilter) {\n const predicate = config.toolFilter\n filtered = filtered.filter(t => predicate(t))\n }\n\n if (hooks) {\n // Pass a fresh array so handlers cannot retroactively mutate the caller's\n // upstream list; mutations they make to `ctx.tools` are scoped to this turn.\n const ctx = { server: config.name, transport: config.transport, tools: [...filtered] }\n await hooks.callHook('mcp:tools:filter', ctx)\n filtered = ctx.tools\n }\n\n return filtered\n}\n\n/**\n * Build the zidane `ToolDef` that wraps a single MCP tool. Extracted so the\n * parallel bootstrap path can assemble tools from a results array without\n * inlining a 70-line closure inside the collector loop.\n *\n * The returned `execute` closes over the bootstrapped `client` — reconnects /\n * swap-outs must go through a fresh `connectMcpServers` call rather than\n * rewiring the tool in place.\n */\nfunction buildMcpToolDef(\n config: McpServerConfig,\n client: Client,\n tool: { name: string, description?: string | null, inputSchema?: unknown },\n namespacedName: string,\n hooks: Hookable<AgentHooks> | undefined,\n): ToolDef {\n return {\n spec: {\n name: namespacedName,\n description: tool.description || '',\n inputSchema: (tool.inputSchema ?? { type: 'object', properties: {} }) as Record<string, unknown>,\n },\n execute: async (input: Record<string, unknown>, ctx: ToolContext) => {\n const { turnId, callId, signal } = ctx\n const displayName = ctx.toolAliases?.[namespacedName] ?? namespacedName\n\n // Gate — block MCP tool execution or substitute a synthetic result.\n const gateCtx: {\n turnId: string\n callId: string\n server: string\n tool: string\n displayName: string\n input: Record<string, unknown>\n block: boolean\n reason: string\n result?: string | ToolResultContent[]\n } = {\n turnId,\n callId,\n server: config.name,\n tool: tool.name,\n displayName,\n input,\n block: false,\n reason: 'MCP tool execution was blocked',\n }\n await hooks?.callHook('mcp:tool:gate', gateCtx)\n\n // Conflict resolution mirrors the loop's `tool:gate`: when both `block`\n // and `result` are set (e.g. a policy gate refuses on top of a consumer\n // cache substitute), `block` wins.\n if (gateCtx.block)\n return `Blocked: ${gateCtx.reason}`\n\n const effectiveInput = gateCtx.input\n\n // MCP gate `result` substitute — skip the upstream callTool, fire the\n // transform + after hooks so consumers see the substitute consistently\n // with normally-executed calls.\n if (gateCtx.result !== undefined) {\n let substitute: string | ToolResultContent[] = gateCtx.result\n const transformCtx = {\n turnId,\n callId,\n server: config.name,\n tool: tool.name,\n displayName,\n input: effectiveInput,\n result: substitute,\n outputBytes: toolOutputByteLength(substitute),\n }\n await hooks?.callHook('mcp:tool:transform', transformCtx)\n substitute = transformCtx.result\n await hooks?.callHook('mcp:tool:after', {\n turnId,\n callId,\n server: config.name,\n tool: tool.name,\n displayName,\n input: effectiveInput,\n result: substitute,\n outputBytes: toolOutputByteLength(substitute),\n })\n return substitute\n }\n await hooks?.callHook('mcp:tool:before', {\n turnId,\n callId,\n server: config.name,\n tool: tool.name,\n displayName,\n input: effectiveInput,\n })\n const timeout = config.toolTimeout ?? 30_000\n try {\n // Race the call against the configured timeout AND the run-level\n // abort signal. Aborting the run has to propagate into MCP calls\n // — otherwise a hung stdio server keeps the tool call alive\n // until `destroy()` eventually closes the client, which can be\n // minutes if the agent is waiting on `waitForIdle()`.\n const result = await raceWithTimeoutAndSignal(\n () => client.callTool({ name: tool.name, arguments: effectiveInput }),\n timeout,\n `MCP tool \"${tool.name}\" on server \"${config.name}\" timed out after ${timeout}ms`,\n signal,\n )\n let output: string | ToolResultContent[] = packMcpResult(result.content)\n\n // Transform — mutate result before returning. `outputBytes` reflects\n // the size before any consumer mutation so a truncation hook can size-budget.\n const transformCtx = {\n turnId,\n callId,\n server: config.name,\n tool: tool.name,\n displayName,\n input: effectiveInput,\n result: output,\n outputBytes: toolOutputByteLength(output),\n }\n await hooks?.callHook('mcp:tool:transform', transformCtx)\n output = transformCtx.result\n\n await hooks?.callHook('mcp:tool:after', {\n turnId,\n callId,\n server: config.name,\n tool: tool.name,\n displayName,\n input: effectiveInput,\n result: output,\n outputBytes: toolOutputByteLength(output),\n })\n return output\n }\n catch (err) {\n const error = err instanceof Error ? err : new Error(String(err))\n await hooks?.callHook('mcp:tool:error', {\n turnId,\n callId,\n server: config.name,\n tool: tool.name,\n displayName,\n input: effectiveInput,\n error,\n })\n await hooks?.callHook('mcp:tool:after', {\n turnId,\n callId,\n server: config.name,\n tool: tool.name,\n displayName,\n input: effectiveInput,\n result: error.message,\n outputBytes: Buffer.byteLength(error.message),\n })\n throw error\n }\n },\n }\n}\n\nasync function closeClientQuietly(client: Pick<Client, 'close'> | null | undefined): Promise<void> {\n if (!client)\n return\n try {\n await client.close()\n }\n catch {\n // Best-effort cleanup — original bootstrap error is more actionable.\n }\n}\n\nasync function raceWithTimeout<T>(\n task: () => Promise<T>,\n timeoutMs: number,\n timeoutMessage: string,\n): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined\n try {\n return await new Promise<T>((resolvePromise, rejectPromise) => {\n timer = setTimeout(() => rejectPromise(new Error(timeoutMessage)), timeoutMs)\n task().then(resolvePromise, rejectPromise)\n })\n }\n finally {\n if (timer)\n clearTimeout(timer)\n }\n}\n\n/**\n * Race a promise-returning task against (a) a timeout and (b) an optional\n * abort signal. Cleans up its timer and abort listener on every exit path.\n *\n * The task itself isn't cancellable (the MCP SDK doesn't take a signal), so\n * the rejection unblocks the tool call while the underlying RPC completes in\n * the background. When the agent subsequently calls `connection.close()` the\n * stdio transport kills the subprocess, which clears any stuck in-flight work.\n */\nasync function raceWithTimeoutAndSignal<T>(\n task: () => Promise<T>,\n timeoutMs: number,\n timeoutMessage: string,\n signal: AbortSignal | undefined,\n): Promise<T> {\n if (signal?.aborted)\n throw new Error('MCP tool call aborted')\n\n let timer: ReturnType<typeof setTimeout> | undefined\n let onAbort: (() => void) | undefined\n try {\n return await new Promise<T>((resolvePromise, rejectPromise) => {\n timer = setTimeout(() => rejectPromise(new Error(timeoutMessage)), timeoutMs)\n if (signal) {\n onAbort = () => rejectPromise(new Error('MCP tool call aborted'))\n signal.addEventListener('abort', onAbort, { once: true })\n }\n task().then(resolvePromise, rejectPromise)\n })\n }\n finally {\n if (timer)\n clearTimeout(timer)\n if (signal && onAbort)\n signal.removeEventListener('abort', onAbort)\n }\n}\n"],"mappings":"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"}
|
package/dist/mcp.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { f as McpConnection, g as resultToString, h as normalizeMcpServers, ht as McpServerConfig, m as normalizeMcpBlocks, p as connectMcpServers } from "./agent-JhicgLOV.js";
|
|
2
2
|
export { McpConnection, McpServerConfig, connectMcpServers, normalizeMcpBlocks, normalizeMcpServers, resultToString };
|
package/dist/mcp.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as resultToString, n as normalizeMcpBlocks, r as normalizeMcpServers, t as connectMcpServers } from "./mcp-
|
|
1
|
+
import { i as resultToString, n as normalizeMcpBlocks, r as normalizeMcpServers, t as connectMcpServers } from "./mcp-Dw-fRPVk.js";
|
|
2
2
|
export { connectMcpServers, normalizeMcpBlocks, normalizeMcpServers, resultToString };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { a as multiEdit, i as readFile, n as createSpawnTool, o as listFiles, r as shell, t as writeFile, u as edit } from "./tools-CLazLRb4.js";
|
|
2
|
+
//#region src/presets/basic.ts
|
|
3
|
+
/**
|
|
4
|
+
* Core tools available in every basic preset (without spawn).
|
|
5
|
+
*
|
|
6
|
+
* `edit` and `multi_edit` ship in the basic set because surgical edits are the
|
|
7
|
+
* default modality for production agents — `write_file` is for full overwrites.
|
|
8
|
+
* `glob` and `grep` are exported but opt-in: not every agent needs codebase
|
|
9
|
+
* search, and shipping them by default would force `tool:gate` work onto
|
|
10
|
+
* consumers that prefer the model to use `shell` + classic Unix tools.
|
|
11
|
+
*/
|
|
12
|
+
const basicTools = {
|
|
13
|
+
shell,
|
|
14
|
+
readFile,
|
|
15
|
+
writeFile,
|
|
16
|
+
listFiles,
|
|
17
|
+
edit,
|
|
18
|
+
multiEdit
|
|
19
|
+
};
|
|
20
|
+
var basic_default = definePreset({
|
|
21
|
+
name: "basic",
|
|
22
|
+
system: "You are a helpful assistant with access to shell, file reading, file writing, surgical and multi-edit tools, directory listing, and sub-agent spawning. Prefer `edit` / `multi_edit` for in-place changes and `write_file` for full file overwrites. Use them to accomplish tasks in the project directory.",
|
|
23
|
+
tools: {
|
|
24
|
+
...basicTools,
|
|
25
|
+
spawn: createSpawnTool({ persist: true })
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/presets/index.ts
|
|
30
|
+
/**
|
|
31
|
+
* Identity helper for type inference when defining a preset.
|
|
32
|
+
*/
|
|
33
|
+
function definePreset(config) {
|
|
34
|
+
return config;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Field-aware composition of presets. Right-most preset wins for scalar fields;
|
|
38
|
+
* objects shallow-merge; arrays and hook handler lists concatenate. Designed so
|
|
39
|
+
* stacking presets does the obvious thing without the spread-collision footgun:
|
|
40
|
+
*
|
|
41
|
+
* - `name`, `system`, `eager`, `skills` → last-defined wins
|
|
42
|
+
* - `tools`, `toolAliases`, `behavior` → shallow-merge (later keys override)
|
|
43
|
+
* - `mcpServers` → concat with last-wins on `name` collision
|
|
44
|
+
* - `hooks` → per-event concat; every handler fires
|
|
45
|
+
*
|
|
46
|
+
* `hooks` always emerges as `event → handler[]` so downstream registration
|
|
47
|
+
* (in `createAgent`) sees a uniform shape. Order of handlers within an event
|
|
48
|
+
* follows preset order: earlier presets register first.
|
|
49
|
+
*
|
|
50
|
+
* `mcpServers` is deduped by `name` because shipping two servers with the same
|
|
51
|
+
* name would trip the connector at runtime — a later preset overriding an
|
|
52
|
+
* earlier preset's `github` server is the practical intent.
|
|
53
|
+
*/
|
|
54
|
+
function composePresets(...presets) {
|
|
55
|
+
const out = {};
|
|
56
|
+
const hooksByEvent = {};
|
|
57
|
+
const mcpByName = /* @__PURE__ */ new Map();
|
|
58
|
+
for (const p of presets) {
|
|
59
|
+
if (p.name !== void 0) out.name = p.name;
|
|
60
|
+
if (p.system !== void 0) out.system = p.system;
|
|
61
|
+
if (p.eager !== void 0) out.eager = p.eager;
|
|
62
|
+
if (p.skills !== void 0) out.skills = p.skills;
|
|
63
|
+
if (p.tools) out.tools = {
|
|
64
|
+
...out.tools,
|
|
65
|
+
...p.tools
|
|
66
|
+
};
|
|
67
|
+
if (p.toolAliases) out.toolAliases = {
|
|
68
|
+
...out.toolAliases,
|
|
69
|
+
...p.toolAliases
|
|
70
|
+
};
|
|
71
|
+
if (p.behavior) out.behavior = {
|
|
72
|
+
...out.behavior,
|
|
73
|
+
...p.behavior
|
|
74
|
+
};
|
|
75
|
+
if (p.mcpServers) for (const server of p.mcpServers) mcpByName.set(server.name, server);
|
|
76
|
+
if (p.hooks) for (const [event, handler] of Object.entries(p.hooks)) {
|
|
77
|
+
if (handler === void 0) continue;
|
|
78
|
+
const list = Array.isArray(handler) ? handler : [handler];
|
|
79
|
+
const key = event;
|
|
80
|
+
(hooksByEvent[key] ??= []).push(...list);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (mcpByName.size > 0) out.mcpServers = [...mcpByName.values()];
|
|
84
|
+
if (Object.keys(hooksByEvent).length > 0) out.hooks = hooksByEvent;
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
export { basic_default as i, definePreset as n, basicTools as r, composePresets as t };
|
|
89
|
+
|
|
90
|
+
//# sourceMappingURL=presets-BRFH2qsQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presets-BRFH2qsQ.js","names":[],"sources":["../src/presets/basic.ts","../src/presets/index.ts"],"sourcesContent":["import { definePreset } from '.'\nimport { edit, listFiles, multiEdit, readFile, shell, writeFile } from '../tools'\nimport { createSpawnTool } from '../tools/spawn'\n\n/**\n * Core tools available in every basic preset (without spawn).\n *\n * `edit` and `multi_edit` ship in the basic set because surgical edits are the\n * default modality for production agents — `write_file` is for full overwrites.\n * `glob` and `grep` are exported but opt-in: not every agent needs codebase\n * search, and shipping them by default would force `tool:gate` work onto\n * consumers that prefer the model to use `shell` + classic Unix tools.\n */\nexport const basicTools = { shell, readFile, writeFile, listFiles, edit, multiEdit }\n\nexport default definePreset({\n name: 'basic',\n system: 'You are a helpful assistant with access to shell, file reading, file writing, surgical and multi-edit tools, directory listing, and sub-agent spawning. Prefer `edit` / `multi_edit` for in-place changes and `write_file` for full file overwrites. Use them to accomplish tasks in the project directory.',\n // `persist: true` shares the parent's session with every child agent — child\n // turns land in `session.turns` tagged with their own `runId`, and the run\n // itself is recorded in `session.runs` with `parentRunId` + `depth`. That's\n // what lets a reloaded TUI session reconstruct the full subagent tree (see\n // `eventsFromTurns` in `tui/store.ts`). Hosts that want children in-memory\n // only can construct their own preset with `createSpawnTool()`.\n tools: { ...basicTools, spawn: createSpawnTool({ persist: true }) },\n})\n","import type { AgentHooks, AgentOptions } from '../agent'\n\nexport type { AgentHookMap } from '../agent'\n\n/**\n * A preset is a reusable slice of `AgentOptions` — spread it into `createAgent()`\n * to configure tools, a default system prompt, aliases, behavior defaults, and\n * agent-lifetime hooks.\n *\n * `provider`, `execution`, `session`, and internal fields are excluded so presets\n * remain shareable and composable.\n *\n * ```ts\n * import { basic } from 'zidane/presets'\n * createAgent({ ...basic, provider })\n * ```\n *\n * ### Composing multiple presets\n *\n * Bare `...spread` is shallow — `{ ...a, ...b }` overwrites every key `b`\n * defines, including `hooks`. Use {@link composePresets} when you want\n * field-aware merging (per-event hook concat, tools shallow-merge, etc.):\n *\n * ```ts\n * createAgent({ ...composePresets(basic, telemetry, mine), provider })\n * ```\n */\nexport type Preset = Omit<Partial<AgentOptions>, 'provider' | 'execution' | 'session' | 'mcpConnector'>\n\n/**\n * Identity helper for type inference when defining a preset.\n */\nexport function definePreset(config: Preset): Preset {\n return config\n}\n\n/**\n * Field-aware composition of presets. Right-most preset wins for scalar fields;\n * objects shallow-merge; arrays and hook handler lists concatenate. Designed so\n * stacking presets does the obvious thing without the spread-collision footgun:\n *\n * - `name`, `system`, `eager`, `skills` → last-defined wins\n * - `tools`, `toolAliases`, `behavior` → shallow-merge (later keys override)\n * - `mcpServers` → concat with last-wins on `name` collision\n * - `hooks` → per-event concat; every handler fires\n *\n * `hooks` always emerges as `event → handler[]` so downstream registration\n * (in `createAgent`) sees a uniform shape. Order of handlers within an event\n * follows preset order: earlier presets register first.\n *\n * `mcpServers` is deduped by `name` because shipping two servers with the same\n * name would trip the connector at runtime — a later preset overriding an\n * earlier preset's `github` server is the practical intent.\n */\nexport function composePresets(...presets: Preset[]): Preset {\n const out: Preset = {}\n const hooksByEvent: { [K in keyof AgentHooks]?: AgentHooks[K][] } = {}\n // Keep mcpServers in source-order on first sight, but allow later\n // declarations to override earlier ones with the same `name`. A `Map`\n // keyed by name gives O(1) override + stable iteration.\n const mcpByName = new Map<string, NonNullable<Preset['mcpServers']>[number]>()\n\n for (const p of presets) {\n if (p.name !== undefined)\n out.name = p.name\n if (p.system !== undefined)\n out.system = p.system\n if (p.eager !== undefined)\n out.eager = p.eager\n if (p.skills !== undefined)\n out.skills = p.skills\n if (p.tools)\n out.tools = { ...out.tools, ...p.tools }\n if (p.toolAliases)\n out.toolAliases = { ...out.toolAliases, ...p.toolAliases }\n if (p.behavior)\n out.behavior = { ...out.behavior, ...p.behavior }\n if (p.mcpServers) {\n for (const server of p.mcpServers)\n mcpByName.set(server.name, server)\n }\n if (p.hooks) {\n for (const [event, handler] of Object.entries(p.hooks)) {\n if (handler === undefined)\n continue\n const list = Array.isArray(handler) ? handler : [handler]\n const key = event as keyof AgentHooks\n // Safe cast: we read the loose `AgentHookMap` shape (handler-or-array)\n // and re-emit only as arrays. Each `list` element matches the event's\n // handler signature by construction (the input was typed `AgentHookMap`).\n const bucket = (hooksByEvent[key] ??= []) as unknown[]\n bucket.push(...(list as unknown[]))\n }\n }\n }\n\n if (mcpByName.size > 0)\n out.mcpServers = [...mcpByName.values()]\n\n if (Object.keys(hooksByEvent).length > 0)\n out.hooks = hooksByEvent\n\n return out\n}\n\nexport { default as basic, basicTools } from './basic'\n"],"mappings":";;;;;;;;;;;AAaA,MAAa,aAAa;CAAE;CAAO;CAAU;CAAW;CAAW;CAAM;CAAW;AAEpF,IAAA,gBAAe,aAAa;CAC1B,MAAM;CACN,QAAQ;CAOR,OAAO;EAAE,GAAG;EAAY,OAAO,gBAAgB,EAAE,SAAS,MAAM,CAAC;EAAE;CACpE,CAAC;;;;;;ACOF,SAAgB,aAAa,QAAwB;CACnD,OAAO;;;;;;;;;;;;;;;;;;;;AAqBT,SAAgB,eAAe,GAAG,SAA2B;CAC3D,MAAM,MAAc,EAAE;CACtB,MAAM,eAA8D,EAAE;CAItE,MAAM,4BAAY,IAAI,KAAwD;CAE9E,KAAK,MAAM,KAAK,SAAS;EACvB,IAAI,EAAE,SAAS,KAAA,GACb,IAAI,OAAO,EAAE;EACf,IAAI,EAAE,WAAW,KAAA,GACf,IAAI,SAAS,EAAE;EACjB,IAAI,EAAE,UAAU,KAAA,GACd,IAAI,QAAQ,EAAE;EAChB,IAAI,EAAE,WAAW,KAAA,GACf,IAAI,SAAS,EAAE;EACjB,IAAI,EAAE,OACJ,IAAI,QAAQ;GAAE,GAAG,IAAI;GAAO,GAAG,EAAE;GAAO;EAC1C,IAAI,EAAE,aACJ,IAAI,cAAc;GAAE,GAAG,IAAI;GAAa,GAAG,EAAE;GAAa;EAC5D,IAAI,EAAE,UACJ,IAAI,WAAW;GAAE,GAAG,IAAI;GAAU,GAAG,EAAE;GAAU;EACnD,IAAI,EAAE,YACJ,KAAK,MAAM,UAAU,EAAE,YACrB,UAAU,IAAI,OAAO,MAAM,OAAO;EAEtC,IAAI,EAAE,OACJ,KAAK,MAAM,CAAC,OAAO,YAAY,OAAO,QAAQ,EAAE,MAAM,EAAE;GACtD,IAAI,YAAY,KAAA,GACd;GACF,MAAM,OAAO,MAAM,QAAQ,QAAQ,GAAG,UAAU,CAAC,QAAQ;GACzD,MAAM,MAAM;GAKZ,CADgB,aAAa,SAAS,EAAE,EACjC,KAAK,GAAI,KAAmB;;;CAKzC,IAAI,UAAU,OAAO,GACnB,IAAI,aAAa,CAAC,GAAG,UAAU,QAAQ,CAAC;CAE1C,IAAI,OAAO,KAAK,aAAa,CAAC,SAAS,GACrC,IAAI,QAAQ;CAEd,OAAO"}
|
package/dist/presets.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { n as AgentHookMap } from "./agent-JhicgLOV.js";
|
|
2
|
+
import { a as basicTools, i as _default, n as composePresets, r as definePreset, t as Preset } from "./index-2yLUyTbc.js";
|
|
3
|
+
export { AgentHookMap, Preset, _default as basic, basicTools, composePresets, definePreset };
|
package/dist/presets.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
export { basic_default as basic, basicTools, definePreset };
|
|
1
|
+
import { i as basic_default, n as definePreset, r as basicTools, t as composePresets } from "./presets-BRFH2qsQ.js";
|
|
2
|
+
export { basic_default as basic, basicTools, composePresets, definePreset };
|
package/dist/providers.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $ as OpenAICompatAuthHeader, G as StreamCallbacks, J as ToolResult, K as StreamOptions, Q as openrouter, U as Provider, W as ProviderCapabilities, X as TurnResult, Y as ToolSpec, Z as OpenRouterParams, at as OpenAIParams, ct as cerebras, et as OpenAICompatHttpError, it as openaiCompat, lt as AnthropicParams, nt as classifyOpenAICompatError, ot as openai, q as ToolCall, rt as mapOAIFinishReason, st as CerebrasParams, tt as OpenAICompatParams, ut as anthropic } from "./agent-JhicgLOV.js";
|
|
2
2
|
export { AnthropicParams, CerebrasParams, OpenAICompatAuthHeader, OpenAICompatHttpError, OpenAICompatParams, OpenAIParams, OpenRouterParams, Provider, ProviderCapabilities, StreamCallbacks, StreamOptions, ToolCall, ToolResult, ToolSpec, TurnResult, anthropic, cerebras, classifyOpenAICompatError, mapOAIFinishReason, openai, openaiCompat, openrouter };
|
package/dist/session/sqlite.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { k as SessionStore } from "../agent-JhicgLOV.js";
|
|
2
2
|
|
|
3
3
|
//#region src/session/sqlite.d.ts
|
|
4
4
|
interface SqliteStoreOptions {
|
|
@@ -6,6 +6,17 @@ interface SqliteStoreOptions {
|
|
|
6
6
|
path: string;
|
|
7
7
|
}
|
|
8
8
|
declare function createSqliteStore(options: SqliteStoreOptions): SessionStore;
|
|
9
|
+
/**
|
|
10
|
+
* Convenience: open a SQLite store at `dbPath`, creating its parent
|
|
11
|
+
* directory if it doesn't yet exist. Used by the TUI launcher to spin up
|
|
12
|
+
* the default session store at `~/.zidane/sessions.db`.
|
|
13
|
+
*
|
|
14
|
+
* Lives here (and not in `chat/store.ts`) so the renderer-agnostic
|
|
15
|
+
* `zidane/chat` entry stays free of `bun:sqlite` — non-Bun consumers
|
|
16
|
+
* (a future GUI, an SDK) can import `zidane/chat` without pulling in the
|
|
17
|
+
* Bun-only binding.
|
|
18
|
+
*/
|
|
19
|
+
declare function createTuiStore(dbPath: string): SessionStore;
|
|
9
20
|
//#endregion
|
|
10
|
-
export { SqliteStoreOptions, createSqliteStore };
|
|
21
|
+
export { SqliteStoreOptions, createSqliteStore, createTuiStore };
|
|
11
22
|
//# sourceMappingURL=sqlite.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.d.ts","names":[],"sources":["../../src/session/sqlite.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","names":[],"sources":["../../src/session/sqlite.ts"],"mappings":";;;UAgDiB,kBAAA;;EAEf,IAAA;AAAA;AAAA,iBAGc,iBAAA,CAAkB,OAAA,EAAS,kBAAA,GAAqB,YAAA;;;;;;;;;;;iBA2LhD,cAAA,CAAe,MAAA,WAAiB,YAAA"}
|
package/dist/session/sqlite.js
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
|
+
import { dirname } from "node:path";
|
|
2
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
1
3
|
import { Database } from "bun:sqlite";
|
|
2
4
|
//#region src/session/sqlite.ts
|
|
5
|
+
/**
|
|
6
|
+
* Current on-disk schema revision for the SQLite session store.
|
|
7
|
+
*
|
|
8
|
+
* 1 — initial schema (sessions table, no version pragma)
|
|
9
|
+
* 2 — `parentRunId` / `depth` on runs; cache-aware `totalUsage`
|
|
10
|
+
* fields; cache-aware token rendering. Pre-2 rows load via
|
|
11
|
+
* defensive `?? 0` fallbacks.
|
|
12
|
+
* 3 — `project_root` column + index. Sessions are tagged with the
|
|
13
|
+
* project they belong to (git root / cwd) so the TUI can list
|
|
14
|
+
* per-project. v2→v3 migration is non-destructive: we add the
|
|
15
|
+
* column NULL, existing rows stay readable as "untagged".
|
|
16
|
+
*/
|
|
17
|
+
const SCHEMA_VERSION = 3;
|
|
3
18
|
function createSqliteStore(options) {
|
|
4
19
|
const db = new Database(options.path);
|
|
5
20
|
db.run("PRAGMA journal_mode = WAL");
|
|
@@ -7,79 +22,122 @@ function createSqliteStore(options) {
|
|
|
7
22
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
8
23
|
id TEXT PRIMARY KEY,
|
|
9
24
|
agent_id TEXT,
|
|
25
|
+
project_root TEXT,
|
|
10
26
|
data TEXT NOT NULL,
|
|
11
27
|
created_at INTEGER NOT NULL,
|
|
12
28
|
updated_at INTEGER NOT NULL
|
|
13
29
|
)
|
|
14
30
|
`);
|
|
15
31
|
db.run(`CREATE INDEX IF NOT EXISTS idx_sessions_agent_id ON sessions(agent_id)`);
|
|
32
|
+
if (!db.query("PRAGMA table_info(sessions)").all().some((c) => c.name === "project_root")) db.run("ALTER TABLE sessions ADD COLUMN project_root TEXT");
|
|
33
|
+
db.run("CREATE INDEX IF NOT EXISTS idx_sessions_project_root ON sessions(project_root)");
|
|
34
|
+
if ((db.query("PRAGMA user_version").get()?.user_version ?? 0) < SCHEMA_VERSION) db.run(`PRAGMA user_version = ${SCHEMA_VERSION}`);
|
|
16
35
|
const stmtLoad = db.prepare("SELECT data FROM sessions WHERE id = ?");
|
|
17
36
|
const stmtUpsert = db.prepare(`
|
|
18
|
-
INSERT INTO sessions (id, agent_id, data, created_at, updated_at)
|
|
19
|
-
VALUES (?, ?, ?, ?, ?)
|
|
37
|
+
INSERT INTO sessions (id, agent_id, project_root, data, created_at, updated_at)
|
|
38
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
20
39
|
ON CONFLICT(id) DO UPDATE SET
|
|
21
40
|
agent_id = excluded.agent_id,
|
|
41
|
+
project_root = excluded.project_root,
|
|
22
42
|
data = excluded.data,
|
|
23
43
|
updated_at = excluded.updated_at
|
|
24
44
|
`);
|
|
25
45
|
const stmtDelete = db.prepare("DELETE FROM sessions WHERE id = ?");
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
46
|
+
const loadSync = (sessionId) => {
|
|
47
|
+
const row = stmtLoad.get(sessionId);
|
|
48
|
+
if (!row) return null;
|
|
49
|
+
return JSON.parse(row.data);
|
|
50
|
+
};
|
|
51
|
+
const saveSync = (session) => {
|
|
52
|
+
stmtUpsert.run(session.id, session.agentId ?? null, session.projectRoot ?? null, JSON.stringify(session), session.createdAt, session.updatedAt);
|
|
53
|
+
};
|
|
54
|
+
const txnAppendTurns = db.transaction((sessionId, turns) => {
|
|
55
|
+
const data = loadSync(sessionId);
|
|
56
|
+
if (!data) return;
|
|
57
|
+
data.turns.push(...turns);
|
|
58
|
+
data.updatedAt = Date.now();
|
|
59
|
+
saveSync(data);
|
|
60
|
+
});
|
|
61
|
+
const txnUpdateRun = db.transaction((sessionId, run) => {
|
|
62
|
+
const data = loadSync(sessionId);
|
|
63
|
+
if (!data) return;
|
|
64
|
+
const idx = data.runs.findIndex((r) => r.id === run.id);
|
|
65
|
+
if (idx >= 0) data.runs[idx] = run;
|
|
66
|
+
data.updatedAt = Date.now();
|
|
67
|
+
saveSync(data);
|
|
68
|
+
});
|
|
69
|
+
const txnUpdateStatus = db.transaction((sessionId, status) => {
|
|
70
|
+
const data = loadSync(sessionId);
|
|
71
|
+
if (!data) return;
|
|
72
|
+
data.status = status;
|
|
73
|
+
data.updatedAt = Date.now();
|
|
74
|
+
saveSync(data);
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
31
77
|
async load(sessionId) {
|
|
32
|
-
|
|
33
|
-
if (!row) return null;
|
|
34
|
-
return JSON.parse(row.data);
|
|
78
|
+
return loadSync(sessionId);
|
|
35
79
|
},
|
|
36
80
|
async save(session) {
|
|
37
|
-
|
|
81
|
+
saveSync(session);
|
|
38
82
|
},
|
|
39
83
|
async delete(sessionId) {
|
|
40
84
|
stmtDelete.run(sessionId);
|
|
41
85
|
},
|
|
42
86
|
async list(filter) {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
if (filter?.agentId)
|
|
46
|
-
|
|
47
|
-
|
|
87
|
+
const conditions = [];
|
|
88
|
+
const params = [];
|
|
89
|
+
if (filter?.agentId) {
|
|
90
|
+
conditions.push("agent_id = ?");
|
|
91
|
+
params.push(filter.agentId);
|
|
92
|
+
}
|
|
93
|
+
if ("projectRoot" in (filter ?? {})) {
|
|
94
|
+
const v = filter.projectRoot;
|
|
95
|
+
if (v === null) conditions.push("project_root IS NULL");
|
|
96
|
+
else if (typeof v === "string") {
|
|
97
|
+
conditions.push("project_root = ?");
|
|
98
|
+
params.push(v);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const sql = `SELECT id FROM sessions ${conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : ""} ORDER BY updated_at DESC ${typeof filter?.limit === "number" && filter.limit > 0 ? `LIMIT ${filter.limit}` : ""}`.replace(/\s+/g, " ").trim();
|
|
102
|
+
return db.query(sql).all(...params).map((r) => r.id);
|
|
48
103
|
},
|
|
49
104
|
async appendTurns(sessionId, turns) {
|
|
50
|
-
|
|
51
|
-
if (data) {
|
|
52
|
-
data.turns.push(...turns);
|
|
53
|
-
data.updatedAt = Date.now();
|
|
54
|
-
await store.save(data);
|
|
55
|
-
}
|
|
105
|
+
txnAppendTurns.immediate(sessionId, turns);
|
|
56
106
|
},
|
|
57
107
|
async getTurns(sessionId, from = 0, limit) {
|
|
58
|
-
const data =
|
|
108
|
+
const data = loadSync(sessionId);
|
|
59
109
|
if (!data) return [];
|
|
60
110
|
return data.turns.slice(from, limit !== void 0 ? from + limit : void 0);
|
|
61
111
|
},
|
|
62
112
|
async updateRun(sessionId, run) {
|
|
63
|
-
|
|
64
|
-
if (data) {
|
|
65
|
-
const idx = data.runs.findIndex((r) => r.id === run.id);
|
|
66
|
-
if (idx >= 0) data.runs[idx] = run;
|
|
67
|
-
data.updatedAt = Date.now();
|
|
68
|
-
await store.save(data);
|
|
69
|
-
}
|
|
113
|
+
txnUpdateRun.immediate(sessionId, run);
|
|
70
114
|
},
|
|
71
115
|
async updateStatus(sessionId, status) {
|
|
72
|
-
|
|
73
|
-
if (data) {
|
|
74
|
-
data.status = status;
|
|
75
|
-
data.updatedAt = Date.now();
|
|
76
|
-
await store.save(data);
|
|
77
|
-
}
|
|
116
|
+
txnUpdateStatus.immediate(sessionId, status);
|
|
78
117
|
}
|
|
79
118
|
};
|
|
80
|
-
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Convenience: open a SQLite store at `dbPath`, creating its parent
|
|
122
|
+
* directory if it doesn't yet exist. Used by the TUI launcher to spin up
|
|
123
|
+
* the default session store at `~/.zidane/sessions.db`.
|
|
124
|
+
*
|
|
125
|
+
* Lives here (and not in `chat/store.ts`) so the renderer-agnostic
|
|
126
|
+
* `zidane/chat` entry stays free of `bun:sqlite` — non-Bun consumers
|
|
127
|
+
* (a future GUI, an SDK) can import `zidane/chat` without pulling in the
|
|
128
|
+
* Bun-only binding.
|
|
129
|
+
*/
|
|
130
|
+
function createTuiStore(dbPath) {
|
|
131
|
+
const dir = dirname(dbPath);
|
|
132
|
+
if (!existsSync(dir)) try {
|
|
133
|
+
mkdirSync(dir, { recursive: true });
|
|
134
|
+
} catch (err) {
|
|
135
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
136
|
+
throw new Error(`Could not create TUI storage directory at "${dir}". Override the location via \`runTui({ storageDir, prefix })\` or the \`ZIDANE_STORAGE_DIR\` env var. Original error: ${message}`);
|
|
137
|
+
}
|
|
138
|
+
return createSqliteStore({ path: dbPath });
|
|
81
139
|
}
|
|
82
140
|
//#endregion
|
|
83
|
-
export { createSqliteStore };
|
|
141
|
+
export { createSqliteStore, createTuiStore };
|
|
84
142
|
|
|
85
143
|
//# sourceMappingURL=sqlite.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.js","names":[],"sources":["../../src/session/sqlite.ts"],"sourcesContent":["/**\n * SQLite session store using Bun's built-in bun:sqlite.\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '.'\nimport type { SessionTurn } from '../types'\nimport { Database } from 'bun:sqlite'\n\nexport interface SqliteStoreOptions {\n /** Path to the SQLite database file */\n path: string\n}\n\nexport function createSqliteStore(options: SqliteStoreOptions): SessionStore {\n const db = new Database(options.path)\n\n // Enable WAL mode for better concurrent read performance\n db.run('PRAGMA journal_mode = WAL')\n\n // Create table if it doesn't exist\n db.run(`\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n agent_id TEXT,\n data TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n )\n `)\n db.run(`CREATE INDEX IF NOT EXISTS idx_sessions_agent_id ON sessions(agent_id)`)\n\n const stmtLoad = db.prepare('SELECT data FROM sessions WHERE id = ?')\n const stmtUpsert = db.prepare(`\n INSERT INTO sessions (id, agent_id, data, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n agent_id = excluded.agent_id,\n data = excluded.data,\n updated_at = excluded.updated_at\n `)\n const stmtDelete = db.prepare('DELETE FROM sessions WHERE id = ?')\n const stmtList = db.prepare('SELECT id FROM sessions ORDER BY updated_at DESC')\n const stmtListLimited = db.prepare('SELECT id FROM sessions ORDER BY updated_at DESC LIMIT ?')\n const stmtListByAgent = db.prepare('SELECT id FROM sessions WHERE agent_id = ? ORDER BY updated_at DESC')\n const stmtListByAgentLimited = db.prepare('SELECT id FROM sessions WHERE agent_id = ? ORDER BY updated_at DESC LIMIT ?')\n\n const store: SessionStore = {\n async load(sessionId: string) {\n const row = stmtLoad.get(sessionId) as { data: string } | null\n if (!row)\n return null\n return JSON.parse(row.data) as SessionData\n },\n\n async save(session: SessionData) {\n stmtUpsert.run(\n session.id,\n session.agentId ?? null,\n JSON.stringify(session),\n session.createdAt,\n session.updatedAt,\n )\n },\n\n async delete(sessionId: string) {\n stmtDelete.run(sessionId)\n },\n\n async list(filter) {\n // Push LIMIT into the SQL so we don't materialize every row into JS\n // just to slice the first N — meaningful on stores with many sessions.\n const limit = typeof filter?.limit === 'number' && filter.limit > 0 ? filter.limit : undefined\n let rows: { id: string }[]\n\n if (filter?.agentId) {\n rows = (limit\n ? stmtListByAgentLimited.all(filter.agentId, limit)\n : stmtListByAgent.all(filter.agentId)) as { id: string }[]\n }\n else {\n rows = (limit ? stmtListLimited.all(limit) : stmtList.all()) as { id: string }[]\n }\n\n return rows.map(r => r.id)\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]) {\n const data = await store.load(sessionId)\n if (data) {\n data.turns.push(...turns)\n data.updatedAt = Date.now()\n await store.save(data)\n }\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number) {\n const data = await store.load(sessionId)\n if (!data)\n return []\n return data.turns.slice(from, limit !== undefined ? from + limit : undefined)\n },\n\n async updateRun(sessionId: string, run: SessionRun) {\n const data = await store.load(sessionId)\n if (data) {\n const idx = data.runs.findIndex(r => r.id === run.id)\n if (idx >= 0) {\n data.runs[idx] = run\n }\n data.updatedAt = Date.now()\n await store.save(data)\n }\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']) {\n const data = await store.load(sessionId)\n if (data) {\n data.status = status\n data.updatedAt = Date.now()\n await store.save(data)\n }\n },\n }\n\n return store\n}\n"],"mappings":";;AAaA,SAAgB,kBAAkB,SAA2C;CAC3E,MAAM,KAAK,IAAI,SAAS,QAAQ,KAAK;CAGrC,GAAG,IAAI,4BAA4B;CAGnC,GAAG,IAAI;;;;;;;;IAQL;CACF,GAAG,IAAI,yEAAyE;CAEhF,MAAM,WAAW,GAAG,QAAQ,yCAAyC;CACrE,MAAM,aAAa,GAAG,QAAQ;;;;;;;IAO5B;CACF,MAAM,aAAa,GAAG,QAAQ,oCAAoC;CAClE,MAAM,WAAW,GAAG,QAAQ,mDAAmD;CAC/E,MAAM,kBAAkB,GAAG,QAAQ,2DAA2D;CAC9F,MAAM,kBAAkB,GAAG,QAAQ,sEAAsE;CACzG,MAAM,yBAAyB,GAAG,QAAQ,8EAA8E;CAExH,MAAM,QAAsB;EAC1B,MAAM,KAAK,WAAmB;GAC5B,MAAM,MAAM,SAAS,IAAI,UAAU;GACnC,IAAI,CAAC,KACH,OAAO;GACT,OAAO,KAAK,MAAM,IAAI,KAAK;;EAG7B,MAAM,KAAK,SAAsB;GAC/B,WAAW,IACT,QAAQ,IACR,QAAQ,WAAW,MACnB,KAAK,UAAU,QAAQ,EACvB,QAAQ,WACR,QAAQ,UACT;;EAGH,MAAM,OAAO,WAAmB;GAC9B,WAAW,IAAI,UAAU;;EAG3B,MAAM,KAAK,QAAQ;GAGjB,MAAM,QAAQ,OAAO,QAAQ,UAAU,YAAY,OAAO,QAAQ,IAAI,OAAO,QAAQ,KAAA;GACrF,IAAI;GAEJ,IAAI,QAAQ,SACV,OAAQ,QACJ,uBAAuB,IAAI,OAAO,SAAS,MAAM,GACjD,gBAAgB,IAAI,OAAO,QAAQ;QAGvC,OAAQ,QAAQ,gBAAgB,IAAI,MAAM,GAAG,SAAS,KAAK;GAG7D,OAAO,KAAK,KAAI,MAAK,EAAE,GAAG;;EAG5B,MAAM,YAAY,WAAmB,OAAsB;GACzD,MAAM,OAAO,MAAM,MAAM,KAAK,UAAU;GACxC,IAAI,MAAM;IACR,KAAK,MAAM,KAAK,GAAG,MAAM;IACzB,KAAK,YAAY,KAAK,KAAK;IAC3B,MAAM,MAAM,KAAK,KAAK;;;EAI1B,MAAM,SAAS,WAAmB,OAAO,GAAG,OAAgB;GAC1D,MAAM,OAAO,MAAM,MAAM,KAAK,UAAU;GACxC,IAAI,CAAC,MACH,OAAO,EAAE;GACX,OAAO,KAAK,MAAM,MAAM,MAAM,UAAU,KAAA,IAAY,OAAO,QAAQ,KAAA,EAAU;;EAG/E,MAAM,UAAU,WAAmB,KAAiB;GAClD,MAAM,OAAO,MAAM,MAAM,KAAK,UAAU;GACxC,IAAI,MAAM;IACR,MAAM,MAAM,KAAK,KAAK,WAAU,MAAK,EAAE,OAAO,IAAI,GAAG;IACrD,IAAI,OAAO,GACT,KAAK,KAAK,OAAO;IAEnB,KAAK,YAAY,KAAK,KAAK;IAC3B,MAAM,MAAM,KAAK,KAAK;;;EAI1B,MAAM,aAAa,WAAmB,QAA+B;GACnE,MAAM,OAAO,MAAM,MAAM,KAAK,UAAU;GACxC,IAAI,MAAM;IACR,KAAK,SAAS;IACd,KAAK,YAAY,KAAK,KAAK;IAC3B,MAAM,MAAM,KAAK,KAAK;;;EAG3B;CAED,OAAO"}
|
|
1
|
+
{"version":3,"file":"sqlite.js","names":[],"sources":["../../src/session/sqlite.ts"],"sourcesContent":["/**\n * SQLite session store using Bun's built-in bun:sqlite.\n *\n * Concurrency model\n * -----------------\n * Mutating methods (`appendTurns`, `updateRun`, `updateStatus`) read the\n * existing row, mutate the parsed `SessionData`, and write it back. Under\n * `maxConcurrent > 1` spawns with `persist: true` two child runs can fire\n * `tool-results:after` on the same session at the same time — both reading\n * version N, both writing N+1, last writer silently clobbering the other.\n *\n * Every mutation here therefore runs inside a `BEGIN IMMEDIATE` transaction\n * via `bun:sqlite`'s `db.transaction(fn).immediate(args)` API. IMMEDIATE\n * acquires the database-level write lock at `BEGIN` time, so the second\n * writer blocks on `BEGIN IMMEDIATE` until the first commits — guaranteeing\n * its `SELECT` sees the freshly-written row.\n *\n * Schema versioning\n * -----------------\n * `PRAGMA user_version` is the canonical place to store schema version in\n * SQLite. `SCHEMA_VERSION` below is bumped whenever the on-disk shape of\n * `SessionData` changes in a way readers need to know about. The constructor\n * runs forward migrations; older databases get upgraded in place. Readers\n * still defensively fall back to `?? 0` etc. since the JSON blob is the\n * authoritative shape — `user_version` is for observability + future\n * structural migrations.\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '.'\nimport type { SessionTurn } from '../types'\nimport { existsSync, mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { Database } from 'bun:sqlite'\n\n/**\n * Current on-disk schema revision for the SQLite session store.\n *\n * 1 — initial schema (sessions table, no version pragma)\n * 2 — `parentRunId` / `depth` on runs; cache-aware `totalUsage`\n * fields; cache-aware token rendering. Pre-2 rows load via\n * defensive `?? 0` fallbacks.\n * 3 — `project_root` column + index. Sessions are tagged with the\n * project they belong to (git root / cwd) so the TUI can list\n * per-project. v2→v3 migration is non-destructive: we add the\n * column NULL, existing rows stay readable as \"untagged\".\n */\nconst SCHEMA_VERSION = 3\n\nexport interface SqliteStoreOptions {\n /** Path to the SQLite database file */\n path: string\n}\n\nexport function createSqliteStore(options: SqliteStoreOptions): SessionStore {\n const db = new Database(options.path)\n\n // WAL gives concurrent reads while a writer holds the lock — the\n // BEGIN IMMEDIATE serialization below still applies for writes.\n db.run('PRAGMA journal_mode = WAL')\n\n db.run(`\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n agent_id TEXT,\n project_root TEXT,\n data TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n )\n `)\n db.run(`CREATE INDEX IF NOT EXISTS idx_sessions_agent_id ON sessions(agent_id)`)\n\n // Schema migration. Forward-only — we only ever ADD columns / indexes.\n //\n // v2 → v3 : adds `project_root` for per-project session filtering.\n // Existing rows get NULL (untagged); they remain readable\n // and load fine — the TUI just hides them from project-\n // scoped lists unless `showAllProjects` is on.\n //\n // `PRAGMA table_info` is the canonical way to feature-detect columns\n // on bun:sqlite without parsing `sqlite_master` text. Idempotent —\n // running it on a fresh DB (where `CREATE TABLE IF NOT EXISTS` above\n // already includes the column) is a no-op.\n const cols = db.query('PRAGMA table_info(sessions)').all() as { name: string }[]\n if (!cols.some(c => c.name === 'project_root'))\n db.run('ALTER TABLE sessions ADD COLUMN project_root TEXT')\n db.run('CREATE INDEX IF NOT EXISTS idx_sessions_project_root ON sessions(project_root)')\n\n const currentVersionRow = db.query('PRAGMA user_version').get() as { user_version: number } | null\n const currentVersion = currentVersionRow?.user_version ?? 0\n if (currentVersion < SCHEMA_VERSION)\n db.run(`PRAGMA user_version = ${SCHEMA_VERSION}`)\n\n const stmtLoad = db.prepare('SELECT data FROM sessions WHERE id = ?')\n const stmtUpsert = db.prepare(`\n INSERT INTO sessions (id, agent_id, project_root, data, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n agent_id = excluded.agent_id,\n project_root = excluded.project_root,\n data = excluded.data,\n updated_at = excluded.updated_at\n `)\n const stmtDelete = db.prepare('DELETE FROM sessions WHERE id = ?')\n\n // --- Synchronous primitives used inside transactions ----------------------\n //\n // bun:sqlite statements are synchronous; `db.transaction(fn).immediate(...)`\n // requires `fn` to be sync. Wrapping these as standalone helpers makes both\n // the transactional and the simple (non-mutating) paths share one\n // implementation.\n const loadSync = (sessionId: string): SessionData | null => {\n const row = stmtLoad.get(sessionId) as { data: string } | null\n if (!row)\n return null\n return JSON.parse(row.data) as SessionData\n }\n const saveSync = (session: SessionData): void => {\n stmtUpsert.run(\n session.id,\n session.agentId ?? null,\n session.projectRoot ?? null,\n JSON.stringify(session),\n session.createdAt,\n session.updatedAt,\n )\n }\n\n // Mutation transactions. `.immediate(...)` acquires the write lock at\n // `BEGIN IMMEDIATE` time so two concurrent writers serialize on the\n // transaction boundary, not on the (already-too-late) save call.\n const txnAppendTurns = db.transaction((sessionId: string, turns: SessionTurn[]) => {\n const data = loadSync(sessionId)\n if (!data)\n return\n data.turns.push(...turns)\n data.updatedAt = Date.now()\n saveSync(data)\n })\n const txnUpdateRun = db.transaction((sessionId: string, run: SessionRun) => {\n const data = loadSync(sessionId)\n if (!data)\n return\n const idx = data.runs.findIndex(r => r.id === run.id)\n if (idx >= 0)\n data.runs[idx] = run\n data.updatedAt = Date.now()\n saveSync(data)\n })\n const txnUpdateStatus = db.transaction((sessionId: string, status: SessionData['status']) => {\n const data = loadSync(sessionId)\n if (!data)\n return\n data.status = status\n data.updatedAt = Date.now()\n saveSync(data)\n })\n\n const store: SessionStore = {\n async load(sessionId: string) {\n return loadSync(sessionId)\n },\n\n async save(session: SessionData) {\n saveSync(session)\n },\n\n async delete(sessionId: string) {\n stmtDelete.run(sessionId)\n },\n\n async list(filter) {\n // Build the SQL on the fly so the filter axes compose cleanly —\n // four prepared statements collapsed into one dynamic query. The\n // shape is always `SELECT id FROM sessions [WHERE …] ORDER BY\n // updated_at DESC [LIMIT N]`; only the WHERE varies.\n //\n // `projectRoot: string` → `WHERE project_root = ?` (excludes untagged).\n // `projectRoot: null` → `WHERE project_root IS NULL` (untagged only).\n // `projectRoot: undefined` → axis omitted (untagged + tagged).\n //\n // The `null` case lets a future \"show legacy\" debug toggle query\n // pre-v3 sessions explicitly without leaking them into normal\n // per-project lists.\n const conditions: string[] = []\n const params: string[] = []\n if (filter?.agentId) {\n conditions.push('agent_id = ?')\n params.push(filter.agentId)\n }\n if ('projectRoot' in (filter ?? {})) {\n const v = filter!.projectRoot\n if (v === null) {\n conditions.push('project_root IS NULL')\n }\n else if (typeof v === 'string') {\n conditions.push('project_root = ?')\n params.push(v)\n }\n }\n const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''\n const limit = typeof filter?.limit === 'number' && filter.limit > 0 ? `LIMIT ${filter.limit}` : ''\n const sql = `SELECT id FROM sessions ${where} ORDER BY updated_at DESC ${limit}`.replace(/\\s+/g, ' ').trim()\n const rows = db.query(sql).all(...params) as { id: string }[]\n return rows.map(r => r.id)\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]) {\n txnAppendTurns.immediate(sessionId, turns)\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number) {\n const data = loadSync(sessionId)\n if (!data)\n return []\n return data.turns.slice(from, limit !== undefined ? from + limit : undefined)\n },\n\n async updateRun(sessionId: string, run: SessionRun) {\n txnUpdateRun.immediate(sessionId, run)\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']) {\n txnUpdateStatus.immediate(sessionId, status)\n },\n }\n\n return store\n}\n\n/**\n * Convenience: open a SQLite store at `dbPath`, creating its parent\n * directory if it doesn't yet exist. Used by the TUI launcher to spin up\n * the default session store at `~/.zidane/sessions.db`.\n *\n * Lives here (and not in `chat/store.ts`) so the renderer-agnostic\n * `zidane/chat` entry stays free of `bun:sqlite` — non-Bun consumers\n * (a future GUI, an SDK) can import `zidane/chat` without pulling in the\n * Bun-only binding.\n */\nexport function createTuiStore(dbPath: string): SessionStore {\n const dir = dirname(dbPath)\n if (!existsSync(dir)) {\n try {\n mkdirSync(dir, { recursive: true })\n }\n catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new Error(\n `Could not create TUI storage directory at \"${dir}\". `\n + `Override the location via \\`runTui({ storageDir, prefix })\\` or the `\n + `\\`ZIDANE_STORAGE_DIR\\` env var. Original error: ${message}`,\n )\n }\n }\n return createSqliteStore({ path: dbPath })\n}\n"],"mappings":"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"}
|