zidane 6.2.11 → 6.2.13
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/dist/{acp-DuEMbOoQ.js → acp-DxYjSCQW.js} +5 -5
- package/dist/{acp-DuEMbOoQ.js.map → acp-DxYjSCQW.js.map} +1 -1
- package/dist/acp-cli.js +6 -6
- package/dist/acp.d.ts +1 -1
- package/dist/acp.js +1 -1
- package/dist/{agent-6iGNP46b.js → agent-DoEx_WD3.js} +86 -5
- package/dist/{agent-6iGNP46b.js.map → agent-DoEx_WD3.js.map} +1 -1
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +2 -2
- package/dist/aliasing-lVFbm81n.js +217 -0
- package/dist/aliasing-lVFbm81n.js.map +1 -0
- package/dist/{anthropic-BD-lwEKp.js → anthropic-BYsc6qWj.js} +89 -4
- package/dist/anthropic-BYsc6qWj.js.map +1 -0
- package/dist/{auth-BH6G59RL.js → auth-Bb479DFB.js} +4 -4
- package/dist/{auth-BH6G59RL.js.map → auth-Bb479DFB.js.map} +1 -1
- package/dist/chat/pure.d.ts +3 -3
- package/dist/chat.d.ts +4 -4
- package/dist/chat.js +3 -3
- package/dist/contexts/daytona.d.ts +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +1 -1
- package/dist/extensions.d.ts +1 -1
- package/dist/extensions.js +1 -1
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +3 -3
- package/dist/{index-BFNt9GAs.d.ts → index-l0wehkWU.d.ts} +44 -6
- package/dist/index-l0wehkWU.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +9 -9
- package/dist/{login-B5nMPF8w.js → login-Clf-R27F.js} +2 -2
- package/dist/{login-B5nMPF8w.js.map → login-Clf-R27F.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/{messages-BDpXLFoe.js → messages-CvHfYB7f.js} +3 -3
- package/dist/{messages-BDpXLFoe.js.map → messages-CvHfYB7f.js.map} +1 -1
- package/dist/{openai-compat-J_JZ7qsw.js → openai-compat-pHI0kBWH.js} +37 -8
- package/dist/openai-compat-pHI0kBWH.js.map +1 -0
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/anthropic.js +1 -1
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/arcee.js +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/baseten.js +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/cerebras.js +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/local.js +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai-compat.js +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.js +27 -10
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/xai.js +1 -1
- package/dist/{providers-CclOzGxc.js → providers-D9Y2RndM.js} +3 -3
- package/dist/{providers-CclOzGxc.js.map → providers-D9Y2RndM.js.map} +1 -1
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +4 -4
- package/dist/{read-state-Dq_TXUX1.js → read-state-rDmfeLmz.js} +3 -196
- package/dist/read-state-rDmfeLmz.js.map +1 -0
- package/dist/restate.d.ts +1 -1
- package/dist/restate.js +2 -1
- package/dist/restate.js.map +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/{session-CIXE-Bzp.js → session-DzrXKLw-.js} +2 -2
- package/dist/{session-CIXE-Bzp.js.map → session-DzrXKLw-.js.map} +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/{skills-CDpejYOf.js → skills-DKWqoaYH.js} +4 -3
- package/dist/skills-DKWqoaYH.js.map +1 -0
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +1 -1
- package/dist/{tool-formatters-B_zQsYfa.d.ts → tool-formatters-BSwVkSX2.d.ts} +2 -2
- package/dist/{tool-formatters-B_zQsYfa.d.ts.map → tool-formatters-BSwVkSX2.d.ts.map} +1 -1
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +2 -2
- package/dist/{transcript-anchors-2wafjV-s.d.ts → transcript-anchors-DKjXpQ0h.d.ts} +2 -2
- package/dist/{transcript-anchors-2wafjV-s.d.ts.map → transcript-anchors-DKjXpQ0h.d.ts.map} +1 -1
- package/dist/{transcript-anchors-B_kUHJ16.js → transcript-anchors-yogJHuBW.js} +106 -34
- package/dist/transcript-anchors-yogJHuBW.js.map +1 -0
- package/dist/tui.d.ts +3 -3
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +201 -116
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-DVA52KMG.d.ts → turn-operations-CjkgQKmR.d.ts} +3 -3
- package/dist/{turn-operations-DVA52KMG.d.ts.map → turn-operations-CjkgQKmR.d.ts.map} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/{xai-D-wALLuZ.js → xai-BwhzoehX.js} +2 -2
- package/dist/{xai-D-wALLuZ.js.map → xai-BwhzoehX.js.map} +1 -1
- package/package.json +1 -1
- package/dist/anthropic-BD-lwEKp.js.map +0 -1
- package/dist/hash-DJRzmcPI.js +0 -24
- package/dist/hash-DJRzmcPI.js.map +0 -1
- package/dist/index-BFNt9GAs.d.ts.map +0 -1
- package/dist/openai-compat-J_JZ7qsw.js.map +0 -1
- package/dist/read-state-Dq_TXUX1.js.map +0 -1
- package/dist/skills-CDpejYOf.js.map +0 -1
- package/dist/transcript-anchors-B_kUHJ16.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { h as toWireMessages } from "./messages-
|
|
1
|
+
import { h as toWireMessages } from "./messages-CvHfYB7f.js";
|
|
2
2
|
import { Buffer } from "node:buffer";
|
|
3
3
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
4
|
//#region src/session/file-map.ts
|
|
@@ -645,4 +645,4 @@ function assertJsonBounded(value, maxBytes, label) {
|
|
|
645
645
|
//#endregion
|
|
646
646
|
export { getExtensionSessionState as a, withExtensionSession as c, createFileMapStore as d, demoteStaleRunningStatus as i, createRemoteStore as l, currentExtensionSession as n, loadSession as o, deleteExtensionSessionState as r, setExtensionSessionState as s, createSession as t, createMemoryStore as u };
|
|
647
647
|
|
|
648
|
-
//# sourceMappingURL=session-
|
|
648
|
+
//# sourceMappingURL=session-DzrXKLw-.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-CIXE-Bzp.js","names":[],"sources":["../src/session/file-map.ts","../src/session/memory.ts","../src/session/remote.ts","../src/session/index.ts"],"sourcesContent":["/**\n * File-map session store.\n *\n * Wraps a narrow 3-method adapter (`get` / `save` / `delete`) that exchanges a flat\n * map of filename → string content. Useful for embedding zidane sessions inside\n * host-provided session backends that only speak in file maps (not zidane's native\n * `SessionStore` shape).\n *\n * Serialization format:\n * - `turns.jsonl` — one `SessionTurn` per line.\n * - `meta.json` — session metadata (id, agentId, status, runs, metadata, timestamps).\n *\n * JSONL for turns keeps history inspectable with tools like `jq` and resilient to\n * partial corruption — parse up to the first bad line and you still have a valid\n * prefix. Metadata lives in its own file so large turn logs don't bloat the\n * metadata path.\n *\n * Scope: each `createFileMapStore` handles a **single session** — the adapter's\n * file map holds at most one zidane session at a time. This matches how host SDKs\n * scope their session stores per conversation.\n *\n * Divergences from the built-in memory / sqlite stores:\n * - `appendTurns` / `updateStatus` / `updateRun` auto-create a minimal `SessionData`\n * record on first write, instead of silently no-oping when the session hasn't been\n * explicitly `save()`-ed. This matches the host-SDK integration path where\n * `createSession(...)` → `agent.run(...)` directly without an explicit `save()` call.\n * - `updateRun` inserts the run if not found in the cached record (rather than\n * silently dropping). Run records therefore always reach the adapter.\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '.'\nimport type { SessionTurn } from '../types'\n\n/**\n * Host-provided file-map adapter. Three methods exchanging `Record<string, string>`\n * payloads — the whole persistence surface the wrapper needs.\n */\nexport interface FileMapAdapter {\n /** Load the current file map. Returns an empty `files` record when nothing is persisted. */\n get: () => Promise<{ files: Record<string, string> }>\n /** Replace the persisted file map. Full-rewrite semantics. */\n save: (files: Record<string, string>) => Promise<void>\n /** Delete all persisted state. */\n delete: () => Promise<void>\n}\n\nexport interface FileMapStoreOptions {\n /** Filename for the JSONL turns log. Default: `turns.jsonl`. */\n turnsFile?: string\n /** Filename for the metadata JSON. Default: `meta.json`. */\n metaFile?: string\n}\n\ninterface MetaShape {\n id: string\n agentId?: string\n /** Optional — absent on files written before projectRoot persistence. */\n projectRoot?: string\n runs: SessionRun[]\n status: SessionData['status']\n metadata: Record<string, unknown>\n createdAt: number\n updatedAt: number\n}\n\nfunction toMeta(data: SessionData): MetaShape {\n return {\n id: data.id,\n agentId: data.agentId,\n projectRoot: data.projectRoot,\n runs: data.runs,\n status: data.status,\n metadata: data.metadata,\n createdAt: data.createdAt,\n updatedAt: data.updatedAt,\n }\n}\n\nfunction toData(meta: MetaShape, turns: SessionTurn[]): SessionData {\n return {\n id: meta.id,\n agentId: meta.agentId,\n ...(typeof meta.projectRoot === 'string' ? { projectRoot: meta.projectRoot } : {}),\n turns,\n runs: meta.runs,\n status: meta.status,\n metadata: meta.metadata,\n createdAt: meta.createdAt,\n updatedAt: meta.updatedAt,\n }\n}\n\nfunction parseTurnsJsonl(jsonl: string): SessionTurn[] {\n if (!jsonl)\n return []\n const turns: SessionTurn[] = []\n for (const line of jsonl.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed)\n continue\n try {\n turns.push(JSON.parse(trimmed) as SessionTurn)\n }\n catch {\n // Skip malformed lines — preserves the valid prefix on partial corruption.\n }\n }\n return turns\n}\n\nfunction serializeTurnsJsonl(turns: SessionTurn[]): string {\n if (turns.length === 0)\n return ''\n return `${turns.map(t => JSON.stringify(t)).join('\\n')}\\n`\n}\n\n/**\n * Create a single-session `SessionStore` backed by a file-map adapter.\n *\n * @example\n * ```ts\n * const session = await createSession({\n * store: createFileMapStore(hostSessionStore),\n * })\n * ```\n */\nexport function createFileMapStore(\n adapter: FileMapAdapter,\n options: FileMapStoreOptions = {},\n): SessionStore {\n const turnsFile = options.turnsFile ?? 'turns.jsonl'\n const metaFile = options.metaFile ?? 'meta.json'\n\n // Cached view of the persisted session. Populated lazily on first access so the\n // factory itself doesn't do I/O.\n let cached: SessionData | null = null\n let hydrated = false\n // In-flight hydrate memo: concurrent first calls share one adapter.get()\n // instead of racing two reads (the loser's view would clobber the winner's\n // subsequent writes — a lost-update race).\n let hydrating: Promise<void> | null = null\n // Turns salvaged from turns.jsonl when meta.json was present but corrupt.\n // We can't trust the (unparseable) meta for the session id, so we hold the\n // turns aside and re-attach them when the next write establishes an id —\n // the alternative (treating the session as missing) would let that write\n // overwrite an intact turns.jsonl with an empty one.\n let recoveredTurns: SessionTurn[] | null = null\n\n async function hydrate(): Promise<void> {\n if (hydrated)\n return\n if (!hydrating) {\n hydrating = (async () => {\n const { files } = await adapter.get()\n const metaRaw = files[metaFile]\n if (metaRaw) {\n let meta: MetaShape | null = null\n try {\n meta = JSON.parse(metaRaw) as MetaShape\n }\n catch {\n meta = null\n }\n if (meta) {\n cached = toData(meta, parseTurnsJsonl(files[turnsFile] ?? ''))\n }\n else {\n recoveredTurns = parseTurnsJsonl(files[turnsFile] ?? '')\n console.error(`[zidane/file-map] ${metaFile} exists but is not valid JSON — preserving ${recoveredTurns.length} turn(s) from ${turnsFile} for the next write instead of treating the session as missing`)\n }\n }\n hydrated = true\n })().finally(() => {\n hydrating = null\n })\n }\n return hydrating\n }\n\n async function persist(data: SessionData): Promise<void> {\n const meta = toMeta(data)\n await adapter.save({\n [metaFile]: JSON.stringify(meta, null, 2),\n [turnsFile]: serializeTurnsJsonl(data.turns),\n })\n }\n\n // Ensure `cached` exists for `sessionId`, creating a minimal record when first written.\n // Returns false when `cached` already holds a different sessionId (request ignored).\n async function ensureCachedFor(sessionId: string): Promise<boolean> {\n await hydrate()\n if (cached) {\n return cached.id === sessionId\n }\n const now = Date.now()\n cached = {\n id: sessionId,\n // Re-attach turns salvaged from a corrupt-meta hydrate so the next\n // persist() doesn't destroy an intact turns.jsonl.\n turns: recoveredTurns ?? [],\n runs: [],\n status: 'idle',\n metadata: {},\n createdAt: now,\n updatedAt: now,\n }\n recoveredTurns = null\n hydrated = true\n return true\n }\n\n return {\n async load(sessionId: string): Promise<SessionData | null> {\n await hydrate()\n if (!cached || cached.id !== sessionId)\n return null\n return structuredClone(cached)\n },\n\n async save(data: SessionData): Promise<void> {\n cached = structuredClone(data)\n hydrated = true\n await persist(cached)\n },\n\n async delete(sessionId: string): Promise<void> {\n await hydrate()\n if (cached && cached.id !== sessionId)\n return\n cached = null\n await adapter.delete()\n },\n\n async list(filter): Promise<string[]> {\n await hydrate()\n if (!cached)\n return []\n if (filter?.agentId && cached.agentId !== filter.agentId)\n return []\n // file-map stores exactly one session, so the projectRoot filter\n // either keeps it (match / axis off) or drops it entirely.\n if (filter && 'projectRoot' in filter) {\n const v = filter.projectRoot\n if (v === null && cached.projectRoot != null)\n return []\n if (typeof v === 'string' && cached.projectRoot !== v)\n return []\n }\n return [cached.id]\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]): Promise<void> {\n const ok = await ensureCachedFor(sessionId)\n if (!ok)\n return\n cached!.turns.push(...structuredClone(turns))\n cached!.updatedAt = Date.now()\n await persist(cached!)\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number): Promise<SessionTurn[]> {\n await hydrate()\n if (!cached || cached.id !== sessionId)\n return []\n const slice = cached.turns.slice(from, limit !== undefined ? from + limit : undefined)\n return structuredClone(slice) as SessionTurn[]\n },\n\n async updateRun(sessionId: string, run: SessionRun): Promise<void> {\n const ok = await ensureCachedFor(sessionId)\n if (!ok)\n return\n const idx = cached!.runs.findIndex(r => r.id === run.id)\n if (idx >= 0)\n cached!.runs[idx] = structuredClone(run)\n else\n cached!.runs.push(structuredClone(run))\n cached!.updatedAt = Date.now()\n await persist(cached!)\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']): Promise<void> {\n const ok = await ensureCachedFor(sessionId)\n if (!ok)\n return\n cached!.status = status\n cached!.updatedAt = Date.now()\n await persist(cached!)\n },\n }\n}\n","/**\n * In-memory session store.\n * Useful for development and testing. Data is lost when the process exits.\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '.'\nimport type { SessionTurn } from '../types'\n\nexport function createMemoryStore(): SessionStore {\n const sessions = new Map<string, SessionData>()\n\n // Auto-create a minimal record when turns arrive for an unknown id. Mirrors\n // the file-map store: `createSession({store})` + `agent.run()` appends\n // turns without an explicit `save()` first — a get-or-no-op `appendTurns`\n // would silently lose that history. Deliberately scoped to `appendTurns`\n // only: a late `updateRun`/`updateStatus` racing a `delete()` must stay a\n // no-op rather than resurrect a deleted session.\n function ensureSession(sessionId: string): SessionData {\n let data = sessions.get(sessionId)\n if (!data) {\n const now = Date.now()\n data = {\n id: sessionId,\n turns: [],\n runs: [],\n status: 'idle',\n metadata: {},\n createdAt: now,\n updatedAt: now,\n }\n sessions.set(sessionId, data)\n }\n return data\n }\n\n return {\n async load(sessionId: string) {\n const data = sessions.get(sessionId)\n return data ? structuredClone(data) : null\n },\n\n async save(session: SessionData) {\n sessions.set(session.id, structuredClone(session))\n },\n\n async delete(sessionId: string) {\n sessions.delete(sessionId)\n },\n\n async list(filter) {\n let ids = Array.from(sessions.keys())\n if (filter?.agentId) {\n ids = ids.filter(id => sessions.get(id)?.agentId === filter.agentId)\n }\n // `projectRoot` mirrors the SQLite store's tri-state contract:\n // string → only sessions tagged with that root\n // null → only UNTAGGED sessions (legacy / pre-v3 rows)\n // undefined → axis ignored (tagged + untagged both returned)\n if (filter && 'projectRoot' in filter) {\n const v = filter.projectRoot\n if (v === null)\n ids = ids.filter(id => sessions.get(id)?.projectRoot == null)\n else if (typeof v === 'string')\n ids = ids.filter(id => sessions.get(id)?.projectRoot === v)\n }\n if (filter?.limit) {\n ids = ids.slice(0, filter.limit)\n }\n return ids\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]) {\n const data = ensureSession(sessionId)\n data.turns.push(...structuredClone(turns))\n data.updatedAt = Date.now()\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number) {\n const data = sessions.get(sessionId)\n if (!data)\n return []\n const sliced = data.turns.slice(from, limit !== undefined ? from + limit : undefined)\n return structuredClone(sliced) as SessionTurn[]\n },\n\n async updateRun(sessionId: string, run: SessionRun) {\n const data = sessions.get(sessionId)\n if (!data)\n return\n // Upsert — see the matching comment in sqlite.ts's\n // `txnUpdateRun` for the failure mode. tl;dr: the agent's\n // `startRun()` only updates in-memory state; the store first\n // sees the run via `updateRun()` at finalize time, so an\n // update-only impl silently drops every new run.\n const idx = data.runs.findIndex(r => r.id === run.id)\n if (idx >= 0)\n data.runs[idx] = structuredClone(run)\n else\n data.runs.push(structuredClone(run))\n data.updatedAt = Date.now()\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']) {\n const data = sessions.get(sessionId)\n if (!data)\n return\n data.status = status\n data.updatedAt = Date.now()\n },\n }\n}\n","/**\n * Remote session store via HTTP API.\n *\n * Expects a REST API with:\n * GET {url}/sessions/{id} -> SessionData | 404\n * PUT {url}/sessions/{id} -> save SessionData\n * DELETE {url}/sessions/{id} -> delete\n * GET {url}/sessions?agentId=&limit=&projectRoot= -> { ids: string[] }\n * `projectRoot=__null__` is the wire encoding for \"untagged only\".\n * POST {url}/sessions/{id}/turns -> append turns\n * GET {url}/sessions/{id}/turns?from=&limit= -> SessionTurn[]\n * PUT {url}/sessions/{id}/runs/{runId} -> update run\n * PATCH {url}/sessions/{id} -> { status }\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '.'\nimport type { SessionTurn } from '../types'\n\nexport interface RemoteStoreOptions {\n /** Base URL of the session API */\n url: string\n /** Optional headers (e.g. for authentication) */\n headers?: Record<string, string>\n /** Per-request timeout in ms. Default 30s — bounds a hung backend. */\n timeoutMs?: number\n}\n\nconst TRAILING_SLASH = /\\/$/\nconst DEFAULT_TIMEOUT_MS = 30_000\n\nexport function createRemoteStore(options: RemoteStoreOptions): SessionStore {\n const baseUrl = options.url.replace(TRAILING_SLASH, '')\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const defaultHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...options.headers,\n }\n\n async function request(path: string, init?: RequestInit): Promise<Response> {\n const url = `${baseUrl}${path}`\n // Bound every call so a hung backend can't wedge the caller forever.\n const res = await fetch(url, {\n ...init,\n headers: { ...defaultHeaders, ...init?.headers },\n signal: init?.signal ?? AbortSignal.timeout(timeoutMs),\n })\n return res\n }\n\n /**\n * Parse a JSON response body, rethrowing parse failures with URL/status\n * context so a malformed backend reply produces a diagnosable error\n * instead of an opaque `Unexpected token` from deep in `res.json()`.\n */\n async function parseJson<T>(res: Response, path: string): Promise<T> {\n try {\n return await res.json() as T\n }\n catch (cause) {\n throw new Error(\n `Remote session: malformed JSON from ${path} (${res.status} ${res.statusText})`,\n { cause },\n )\n }\n }\n\n return {\n async load(sessionId: string) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}`)\n if (!res.ok) {\n if (res.status === 404)\n return null\n throw new Error(`Remote session load failed: ${res.status} ${res.statusText}`)\n }\n return await parseJson<SessionData>(res, `/sessions/${sessionId}`)\n },\n\n async save(session: SessionData) {\n const res = await request(`/sessions/${encodeURIComponent(session.id)}`, {\n method: 'PUT',\n body: JSON.stringify(session),\n })\n if (!res.ok) {\n throw new Error(`Remote session save failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async delete(sessionId: string) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}`, {\n method: 'DELETE',\n })\n if (!res.ok && res.status !== 404) {\n throw new Error(`Remote session delete failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async list(filter) {\n const params = new URLSearchParams()\n if (filter?.agentId)\n params.set('agentId', filter.agentId)\n if (filter?.limit)\n params.set('limit', String(filter.limit))\n if (filter && 'projectRoot' in filter) {\n const v = filter.projectRoot\n if (v === null)\n params.set('projectRoot', '__null__')\n else if (typeof v === 'string')\n params.set('projectRoot', v)\n }\n\n const query = params.toString()\n const path = query ? `/sessions?${query}` : '/sessions'\n const res = await request(path)\n\n if (!res.ok) {\n throw new Error(`Remote session list failed: ${res.status} ${res.statusText}`)\n }\n\n const body = await parseJson<{ ids: string[] }>(res, path)\n if (!Array.isArray(body?.ids)) {\n throw new TypeError(`Remote session list: expected { ids: string[] } from ${path}, got ${typeof body?.ids}`)\n }\n return body.ids\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}/turns`, {\n method: 'POST',\n body: JSON.stringify(turns),\n })\n if (!res.ok) {\n throw new Error(`Remote appendTurns failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number) {\n const params = new URLSearchParams()\n if (from)\n params.set('from', String(from))\n if (limit !== undefined)\n params.set('limit', String(limit))\n\n const query = params.toString()\n const path = `/sessions/${encodeURIComponent(sessionId)}/turns${query ? `?${query}` : ''}`\n const res = await request(path)\n\n if (!res.ok) {\n throw new Error(`Remote getTurns failed: ${res.status} ${res.statusText}`)\n }\n\n const turns = await parseJson<SessionTurn[]>(res, path)\n if (!Array.isArray(turns)) {\n throw new TypeError(`Remote getTurns: expected SessionTurn[] from ${path}, got ${typeof turns}`)\n }\n return turns\n },\n\n async updateRun(sessionId: string, run: SessionRun) {\n const res = await request(\n `/sessions/${encodeURIComponent(sessionId)}/runs/${encodeURIComponent(run.id)}`,\n {\n method: 'PUT',\n body: JSON.stringify(run),\n },\n )\n if (!res.ok) {\n throw new Error(`Remote updateRun failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}`, {\n method: 'PATCH',\n body: JSON.stringify({ status }),\n })\n if (!res.ok) {\n throw new Error(`Remote updateStatus failed: ${res.status} ${res.statusText}`)\n }\n },\n }\n}\n","/**\n * Session management for agents.\n *\n * A session tracks identity, turn history, and run metadata.\n * Plug in any storage backend by implementing the SessionStore interface,\n * or use one of the built-in stores: memory, sqlite, remote.\n */\n\nimport type { JsonValue, SessionMessage, SessionTurn, TurnUsage } from '../types'\nimport type { ToWireMessagesOptions } from './messages'\nimport { AsyncLocalStorage } from 'node:async_hooks'\nimport { Buffer } from 'node:buffer'\nimport { toWireMessages } from './messages'\n\nexport type { SessionContentBlock, SessionMessage, SessionTurn } from '../types'\nexport { createFileMapStore } from './file-map'\n\nconst EXTENSION_SESSION_METADATA_KEY = 'extensions'\nconst EXTENSION_SESSION_ITEM_MAX_BYTES = 16_384\nconst EXTENSION_SESSION_TOTAL_MAX_BYTES = 65_536\nconst DANGEROUS_RECORD_KEYS = new Set(['__proto__', 'prototype', 'constructor'])\nconst extensionSessionStorage = new AsyncLocalStorage<Session>()\n\n/**\n * Run `fn` with `session` bound as the active extension session for the\n * duration of the call (and every async continuation it spawns). Uses\n * `AsyncLocalStorage.run` rather than `enterWith` so concurrent agents in the\n * same process (e.g. the GUI's multi-session host) never observe each other's\n * session through `currentExtensionSession()`.\n */\nexport function withExtensionSession<T>(session: Session | undefined, fn: () => T): T {\n if (!session)\n return fn()\n return extensionSessionStorage.run(session, fn)\n}\n\nexport function currentExtensionSession(): Session | undefined {\n return extensionSessionStorage.getStore()\n}\n\nexport function getExtensionSessionState<T = JsonValue>(\n session: Session | undefined,\n extensionName: string,\n key: string,\n): T | undefined {\n if (!session || !isSafeExtensionStateKey(extensionName) || !isSafeExtensionStateKey(key))\n return undefined\n const root = session.metadata[EXTENSION_SESSION_METADATA_KEY]\n if (!root || typeof root !== 'object' || Array.isArray(root))\n return undefined\n const byExtension = (root as Record<string, unknown>)[extensionName]\n if (!byExtension || typeof byExtension !== 'object' || Array.isArray(byExtension))\n return undefined\n const stored = (byExtension as Record<string, unknown>)[key]\n if (stored === undefined)\n return undefined\n // Hand back a copy so a caller mutating the result can't reach into the live\n // session metadata behind `setMeta`/`save`.\n return jsonClone(stored as JsonValue) as T\n}\n\nexport async function setExtensionSessionState(\n session: Session | undefined,\n extensionName: string,\n key: string,\n value: JsonValue,\n): Promise<void> {\n if (!session)\n return\n assertSafeExtensionStateKey(extensionName, 'extensionName')\n assertSafeExtensionStateKey(key, 'key')\n assertJsonBounded(value, EXTENSION_SESSION_ITEM_MAX_BYTES, `extension session state \"${extensionName}.${key}\"`)\n\n const root = cloneExtensionStateRoot(session.metadata[EXTENSION_SESSION_METADATA_KEY])\n const byExtension = clonePlainRecord(root[extensionName])\n // Deep-copy on write so later mutations to the caller's object don't bleed\n // into persisted state.\n byExtension[key] = jsonClone(value)\n root[extensionName] = byExtension\n assertJsonBounded(root as JsonValue, EXTENSION_SESSION_TOTAL_MAX_BYTES, 'extension session state')\n session.setMeta(EXTENSION_SESSION_METADATA_KEY, root)\n if (session.store)\n await session.save()\n}\n\nexport async function deleteExtensionSessionState(\n session: Session | undefined,\n extensionName: string,\n key: string,\n): Promise<void> {\n if (!session || !isSafeExtensionStateKey(extensionName) || !isSafeExtensionStateKey(key))\n return\n const root = cloneExtensionStateRoot(session.metadata[EXTENSION_SESSION_METADATA_KEY])\n const byExtension = clonePlainRecord(root[extensionName])\n if (!(key in byExtension))\n return\n delete byExtension[key]\n if (Object.keys(byExtension).length > 0)\n root[extensionName] = byExtension\n else\n delete root[extensionName]\n session.setMeta(EXTENSION_SESSION_METADATA_KEY, root)\n if (session.store)\n await session.save()\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SessionRun {\n id: string\n startedAt: number\n endedAt?: number\n prompt: string\n status: 'running' | 'completed' | 'aborted' | 'error'\n turns?: number\n tokensIn?: number\n tokensOut?: number\n error?: string\n /** Per-turn usage breakdown */\n turnUsage?: TurnUsage[]\n /** Total usage across all turns */\n totalUsage?: TurnUsage\n /** Estimated cost in USD */\n cost?: number\n /**\n * The run that spawned this one, when the agent is a subagent sharing its\n * parent's session. Undefined on top-level `agent.run()`. Consumers can walk\n * `runs` by `parentRunId` to reconstruct the subagent tree.\n */\n parentRunId?: string\n /**\n * Zero-based subagent depth. 0 = top-level run, 1 = direct child, …\n * Recorded here so hosts can query/filter by level without walking the tree.\n */\n depth?: number\n /**\n * How this run was initiated when not a plain user prompt. `command` marks\n * a sub-run driven from a `/` command's `ctx.runAgent`; `display` is the\n * short line the transcript shows instead of the full injected prompt\n * (which often embeds bulky material like a diff). Presentation-only —\n * the wire/model payload is unaffected. Absent on normal runs.\n */\n origin?: { kind: 'command', display?: string }\n}\n\nexport interface SessionData {\n id: string\n agentId?: string\n /**\n * Absolute path of the project this session belongs to — typically\n * the git root resolved from `cwd` at creation time, falling back to\n * `cwd` itself when not in a git repo. Set ONCE on creation and never\n * mutated thereafter (the session \"belongs\" to that project forever).\n *\n * Used by the TUI's sessions list to filter rows by current project,\n * so the user only sees conversations relevant to where they are\n * working — without needing one `.{prefix}/` directory per project.\n *\n * `undefined` on pre-tagging legacy sessions; the chat layer treats\n * those as \"untagged\" and hides them unless `Settings.showAllProjects`\n * is on.\n */\n projectRoot?: string\n turns: SessionTurn[]\n runs: SessionRun[]\n status: 'idle' | 'running' | 'completed' | 'error'\n metadata: Record<string, unknown>\n createdAt: number\n updatedAt: number\n}\n\n/**\n * Compact per-session summary returned by {@link SessionStore.listMeta}.\n * Raw facts only — presentation (title derivation, fallback strings)\n * stays in the chat layer (`listSessionMeta` / `deriveSessionTitle`).\n */\nexport interface SessionMetaRow {\n id: string\n /** Header metadata (may carry a stored `title`). */\n metadata: Record<string, unknown>\n /** Absent for untagged (legacy) sessions. */\n projectRoot?: string\n updatedAt: number\n turnCount: number\n /** Count of `role: 'user'` turns. */\n userMessageCount: number\n runCount: number\n /**\n * First `role: 'user'` turn — the title-derivation source. Stores may\n * omit it when `metadata.title` is already set (no derivation needed).\n */\n firstUserTurn?: SessionTurn\n}\n\n// ---------------------------------------------------------------------------\n// SessionStore interface (pluggable backend)\n// ---------------------------------------------------------------------------\n\nexport interface SessionStore {\n /** Optional: generate a session ID server-side (e.g. a database-issued UUID). */\n generateSessionId?: () => string | Promise<string>\n\n /**\n * Optional: generate a turn ID server-side for session-managed user turns.\n *\n * Provider-loop assistant and tool-result turns use AgentClock.randomUUID()\n * instead, so durable-execution adapters can keep those IDs replay-stable.\n */\n generateTurnId?: () => string | Promise<string>\n\n /** Load a session by ID. Returns null if not found. */\n load: (sessionId: string) => Promise<SessionData | null>\n\n /** Save a session (create or update, full document). */\n save: (session: SessionData) => Promise<void>\n\n /** Delete a session. */\n delete: (sessionId: string) => Promise<void>\n\n /**\n * List session IDs, optionally filtered. `projectRoot` restricts to\n * sessions whose `SessionData.projectRoot` matches exactly — untagged\n * (legacy) sessions are NOT returned under that filter; pass `null`\n * explicitly to ask for untagged ones, or omit the field to ignore\n * the axis entirely. `agentId` filters by recorded agent; the two\n * conditions AND together when both are set.\n */\n list: (filter?: { agentId?: string, limit?: number, projectRoot?: string | null }) => Promise<string[]>\n\n /**\n * Optional: list session summaries without materializing full sessions.\n *\n * The generic fallback (`listSessionMeta` in `zidane/chat`) is `list()`\n * + `load()` per id — O(every turn in the store) because each `load`\n * parses the full turn history just to count rows and derive a title.\n * Stores that can answer from an index (SQL aggregates, a denormalized\n * header) should implement this; the sqlite store turns a multi-hundred-\n * ms TUI boot cost into a single query.\n *\n * Same filter contract and ordering (most recently updated first) as\n * {@link list}.\n */\n listMeta?: (filter?: { agentId?: string, limit?: number, projectRoot?: string | null }) => Promise<SessionMetaRow[]>\n\n /** Append new turns to a session (incremental, avoids full re-save). */\n appendTurns: (sessionId: string, turns: SessionTurn[]) => Promise<void>\n\n /** Return a slice of turns for a session. */\n getTurns: (sessionId: string, from?: number, limit?: number) => Promise<SessionTurn[]>\n\n /**\n * Persist a run record (called after completeRun / abortRun / errorRun).\n *\n * **Upsert semantics required.** The agent's run lifecycle calls\n * `session.startRun()` to register a new run in IN-MEMORY state and\n * then `updateRun()` at finalize. The store doesn't see the run any\n * earlier — `appendTurns` is the only intermediate call, and it\n * persists turns but not runs. An update-only implementation would\n * silently drop every newly-created run record at finalize, leaving\n * the on-disk `runs[]` missing entries that the persisted turns\n * reference (which `eventsFromTurns` then mis-classifies via\n * `ancestryOf` → broken depth, broken `child-N` labels, broken\n * `userPrompts` history).\n *\n * Implementations MUST insert when the id doesn't exist and update\n * when it does. The bundled `sqlite` / `memory` / `file-map` stores\n * all follow this contract; remote stores using HTTP PUT get upsert\n * for free via REST semantics.\n */\n updateRun: (sessionId: string, run: SessionRun) => Promise<void>\n\n /** Update the top-level status of a session. */\n updateStatus: (sessionId: string, status: SessionData['status']) => Promise<void>\n\n /**\n * Optional: release backend resources (e.g. close the SQLite handle).\n * Hosts should call this on teardown when present; stores backed by\n * memory or remote HTTP have nothing to release and may omit it.\n */\n close?: () => void\n}\n\n// ---------------------------------------------------------------------------\n// Session (live instance wrapping a SessionData)\n// ---------------------------------------------------------------------------\n\nexport interface Session {\n /** Session ID */\n readonly id: string\n\n /** Agent ID (optional label) */\n readonly agentId?: string\n\n /**\n * Project this session was created under — see {@link SessionData.projectRoot}.\n * Set once on creation; surfaces here for read-only inspection.\n */\n readonly projectRoot?: string\n\n /**\n * Raw persisted turn history — kept pristine for fidelity (the model's\n * reasoning chain stays intact across reloads).\n *\n * **DO NOT pass this directly to a provider request.** Mid-pair\n * persistence (interrupted runs, partial-result writes, compaction\n * boundaries) leaves orphan `tool_use` / `tool_result` blocks here that\n * every provider 400s on (`tool_result must be preceded by a tool_call\n * with the same toolCallId`). The agent's own loop repairs these\n * just-in-time at wire-send time and never writes the repair back to\n * `turns`.\n *\n * For provider requests built outside `agent.run()`, use\n * {@link Session.toMessages} — it applies the same defensive pairing\n * repair and resolves to a guaranteed-safe `SessionMessage[]`.\n */\n readonly turns: SessionTurn[]\n\n /**\n * True when this session has no turns yet.\n *\n * Use this as a first-prompt signal when setting up a run — e.g. writing initial\n * configuration only on fresh sessions. Equivalent to `turns.length === 0`.\n */\n readonly isEmpty: boolean\n\n /** Top-level session status */\n readonly status: SessionData['status']\n\n /** All runs in this session */\n readonly runs: SessionRun[]\n\n /** Arbitrary metadata */\n readonly metadata: Record<string, unknown>\n\n /**\n * Start tracking a new run. `extras.parentRunId` + `extras.depth` are\n * populated by the spawn tool when a child agent shares its parent's\n * session; regular top-level `agent.run()` calls omit them.\n */\n startRun: (runId: string, prompt?: string, extras?: { parentRunId?: string, depth?: number }) => void\n\n /** Mark a run as completed */\n completeRun: (runId: string, stats: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) => void\n\n /** Mark a run as aborted */\n /**\n * Optional `stats` lets the agent backfill the run's token totals when\n * the abort happened *after* the loop accumulated meaningful usage —\n * common when the user presses esc mid-streaming. Without it, the run\n * record reads `0 in / 0 out` on reload regardless of how much was\n * spent before the abort. Same shape as `completeRun`'s stats so the\n * persisted `totalUsage` aggregate stays consistent across paths.\n */\n abortRun: (runId: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) => void\n\n /** Mark a run as errored */\n /** Optional `stats` — same rationale as `abortRun.stats`. */\n errorRun: (runId: string, error: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) => void\n\n /** Append turns to in-memory history AND persist via store.appendTurns (if store present) */\n appendTurns: (turns: SessionTurn[]) => Promise<void>\n\n /** Replace all turns in-memory (does not persist — use save() for that) */\n setTurns: (turns: SessionTurn[]) => void\n\n /**\n * Replace all runs in-memory (does not persist — use save() for that).\n * Mirrors {@link setTurns} for the fork / restore case: callers that\n * bootstrap a session from an externally-derived snapshot (e.g.\n * `onForkTurn` copying parent runs into a child session) need this so\n * the cloned runs land in `data.runs` before the first `save()`.\n * Production agent runs continue to mutate runs via `startRun` /\n * `completeRun` / `updateRun`; this is the bulk-replace escape hatch.\n */\n setRuns: (runs: SessionRun[]) => void\n\n /** Update the session status in memory AND via store.updateStatus (if store present) */\n updateStatus: (status: SessionData['status']) => Promise<void>\n\n /** Persist an updated run record via store.updateRun (if store present) */\n updateRun: (run: SessionRun) => Promise<void>\n\n /** Generate a session-managed turn ID using store.generateTurnId if available, else crypto.randomUUID() */\n generateTurnId: () => string | Promise<string>\n\n /** Set metadata key */\n setMeta: (key: string, value: unknown) => void\n\n /** Persist the full session document to the store */\n save: () => Promise<void>\n\n /**\n * Project the session's persisted turns into wire-ready messages safe to\n * hand to any provider.\n *\n * The canonical answer to \"I need to send these turns to Anthropic /\n * OpenAI from outside `agent.run()`\" (sub-agent classifier, side-channel\n * summarizer, replay worker, …). Runs the same defensive pairing repair\n * the agent loop applies just-in-time at wire-send time\n * ({@link ensureToolResultPairing}), so orphan `tool_use` blocks and\n * dangling `tool_result`s never reach the provider as the 400\n * \"tool_result must be preceded by a tool_call with the same toolCallId\".\n *\n * Pass `provider` to also enforce the user-tail invariant\n * ({@link ensureEndsWithUserMessage}) — required for opus 4.7 / o-series\n * style models that reject assistant-prefill.\n *\n * Pure + idempotent. Resolves content refs only in the returned messages\n * and does NOT mutate `session.turns`. **Do not write the result back via\n * `setTurns` / `appendTurns`** — synthetic placeholders or resolved\n * payloads will contaminate persisted history.\n */\n toMessages: (options?: ToWireMessagesOptions) => Promise<SessionMessage[]>\n\n /** Serialize to SessionData */\n toJSON: () => SessionData\n\n /**\n * The backing store, when one was provided. Exposed so hosts can call\n * its optional `close()` on teardown (e.g. release the SQLite handle).\n */\n readonly store?: SessionStore\n}\n\n// ---------------------------------------------------------------------------\n// createSession\n// ---------------------------------------------------------------------------\n\nexport interface CreateSessionOptions {\n /** Session ID. If omitted and store provides generateSessionId, that is used. */\n id?: string\n /** Agent ID label */\n agentId?: string\n /**\n * Project tag — see {@link SessionData.projectRoot}. Stamped once on\n * creation; ignored when `_data` is set (restoring an existing\n * session preserves whatever was already persisted there). The TUI\n * resolves this from `findGitRoot(cwd) ?? cwd` so sessions started\n * from the same repo (no matter which subdir) share one tag.\n */\n projectRoot?: string\n /** Initial metadata */\n metadata?: Record<string, unknown>\n /** Storage backend (optional, enables save/load) */\n store?: SessionStore\n // @internal: restore from existing data (bypasses id/agentId/metadata options)\n _data?: SessionData\n}\n\n/**\n * Create a new session.\n * Async so stores that generate IDs server-side (e.g. a database) can be supported.\n */\nexport async function createSession(options: CreateSessionOptions = {}): Promise<Session> {\n const store = options.store\n const now = Date.now()\n\n let sessionId = options.id\n if (!sessionId && store?.generateSessionId) {\n sessionId = await store.generateSessionId()\n }\n if (!sessionId) {\n sessionId = generateId()\n }\n\n const data: SessionData = options._data ?? {\n id: sessionId,\n agentId: options.agentId,\n // Stamp the project tag at creation only — restored sessions (the\n // `_data` branch above) keep whatever was already persisted, even\n // if `options.projectRoot` differs. A session's project identity\n // is sticky for its lifetime; loading the same session from a\n // different cwd doesn't re-home it.\n ...(options.projectRoot ? { projectRoot: options.projectRoot } : {}),\n turns: [],\n runs: [],\n status: 'idle',\n metadata: options.metadata ?? {},\n createdAt: now,\n updatedAt: now,\n }\n\n function touch() {\n data.updatedAt = Date.now()\n }\n\n function findRun(runId: string): SessionRun | undefined {\n return data.runs.find(r => r.id === runId)\n }\n\n /**\n * Apply per-run usage stats onto a SessionRun. Shared by `completeRun`,\n * `abortRun`, and `errorRun` so the on-disk shape stays consistent across\n * exit paths — historically only `completeRun` filled these in, and\n * aborted/errored runs rendered `0 in / 0 out` on reload regardless of\n * how much was actually consumed.\n */\n function applyRunStats(run: SessionRun, stats: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n run.turns = stats.turns\n run.tokensIn = stats.tokensIn\n run.tokensOut = stats.tokensOut\n if (stats.turnUsage) {\n run.turnUsage = stats.turnUsage\n const total = stats.turnUsage.reduce((acc, t) => ({\n input: acc.input + t.input,\n output: acc.output + t.output,\n cacheCreation: (acc.cacheCreation ?? 0) + (t.cacheCreation ?? 0),\n cacheRead: (acc.cacheRead ?? 0) + (t.cacheRead ?? 0),\n thinking: (acc.thinking ?? 0) + (t.thinking ?? 0),\n }), { input: 0, output: 0, cacheCreation: 0, cacheRead: 0, thinking: 0 })\n run.totalUsage = {\n input: total.input,\n output: total.output,\n ...(total.cacheCreation ? { cacheCreation: total.cacheCreation } : {}),\n ...(total.cacheRead ? { cacheRead: total.cacheRead } : {}),\n ...(total.thinking ? { thinking: total.thinking } : {}),\n }\n }\n if (stats.cost !== undefined)\n run.cost = stats.cost\n }\n\n const session: Session = {\n ...(store ? { store } : {}),\n get id() { return data.id },\n get agentId() { return data.agentId },\n get projectRoot() { return data.projectRoot },\n get turns() { return data.turns },\n get isEmpty() { return data.turns.length === 0 },\n get status() { return data.status },\n get runs() { return data.runs },\n get metadata() { return data.metadata },\n\n startRun(runId: string, prompt?: string, extras?: { parentRunId?: string, depth?: number }) {\n data.runs.push({\n id: runId,\n startedAt: Date.now(),\n prompt: prompt ?? '',\n status: 'running',\n ...(extras?.parentRunId ? { parentRunId: extras.parentRunId } : {}),\n ...(typeof extras?.depth === 'number' ? { depth: extras.depth } : {}),\n })\n touch()\n },\n\n completeRun(runId: string, stats: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n const run = findRun(runId)\n if (run) {\n run.status = 'completed'\n run.endedAt = Date.now()\n applyRunStats(run, stats)\n }\n touch()\n },\n\n abortRun(runId: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n const run = findRun(runId)\n if (run) {\n run.status = 'aborted'\n run.endedAt = Date.now()\n // Backfill tokens when available so an aborted run's session\n // ledger reflects actual consumption rather than `0 in / 0 out`.\n if (stats)\n applyRunStats(run, stats)\n }\n touch()\n },\n\n errorRun(runId: string, error: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n const run = findRun(runId)\n if (run) {\n run.status = 'error'\n run.endedAt = Date.now()\n run.error = error\n if (stats)\n applyRunStats(run, stats)\n }\n touch()\n },\n\n async appendTurns(turns: SessionTurn[]) {\n data.turns.push(...turns)\n touch()\n if (store) {\n await store.appendTurns(data.id, turns)\n }\n },\n\n setTurns(turns: SessionTurn[]) {\n data.turns = turns\n touch()\n },\n\n setRuns(runs: SessionRun[]) {\n data.runs = runs\n touch()\n },\n\n async updateStatus(status: SessionData['status']) {\n data.status = status\n touch()\n if (store) {\n await store.updateStatus(data.id, status)\n }\n },\n\n async updateRun(run: SessionRun) {\n if (store) {\n await store.updateRun(data.id, run)\n }\n },\n\n generateTurnId() {\n if (store?.generateTurnId) {\n return store.generateTurnId()\n }\n return crypto.randomUUID()\n },\n\n setMeta(key: string, value: unknown) {\n data.metadata[key] = value\n touch()\n },\n\n async save() {\n if (!store) {\n throw new Error('No SessionStore configured. Pass a store to createSession() to enable persistence.')\n }\n await store.save(data)\n },\n\n toMessages(options?: ToWireMessagesOptions) {\n return toWireMessages(data.turns, options)\n },\n\n toJSON() {\n return structuredClone(data)\n },\n }\n\n return session\n}\n\n/**\n * Demote a persisted `'running'` status to `'idle'` (in place) and return the\n * data. A persisted `'running'` is necessarily stale: the run that set it lived\n * in another process (crash, kill -9, power loss) and cannot still be executing\n * in a freshly-loaded session. Call this on the result of a raw `store.load()`\n * so consumers don't render a ghost in-progress state or refuse a new run.\n * `loadSession` applies it automatically; direct `store.load()` callers (e.g.\n * the TUI) should run loaded data through this.\n *\n * Run RECORDS get the same treatment: the agent persists each record at\n * run start with `status: 'running'` (crash durability — see the\n * `SessionStore.updateRun` contract), so a freshly-loaded session may\n * carry `'running'` runs that died with their process. They demote to\n * `'aborted'` so replay renders a truthful \"aborted\" state instead of a\n * perpetually-in-flight run.\n */\nexport function demoteStaleRunningStatus(data: SessionData): SessionData {\n if (data.status === 'running')\n data.status = 'idle'\n for (const run of data.runs) {\n if (run.status === 'running') {\n run.status = 'aborted'\n run.endedAt ??= run.startedAt\n }\n }\n return data\n}\n\n/**\n * Load an existing session from a store.\n */\nexport async function loadSession(store: SessionStore, sessionId: string): Promise<Session | null> {\n const loaded = await store.load(sessionId)\n if (!loaded)\n return null\n\n demoteStaleRunningStatus(loaded)\n\n return createSession({ store, _data: loaded })\n}\n\n// ---------------------------------------------------------------------------\n// Re-export stores\n// ---------------------------------------------------------------------------\n\nexport type { FileMapAdapter, FileMapStoreOptions } from './file-map'\nexport { createMemoryStore } from './memory'\nexport { autoDetectAndConvert, fromAnthropic, fromOpenAI, toAnthropic, toOpenAI, toWireMessages } from './messages'\nexport type { ToWireMessagesOptions } from './messages'\nexport { createRemoteStore } from './remote'\nexport type { RemoteStoreOptions } from './remote'\n\n// NOTE: `createSqliteStore` is intentionally NOT re-exported here. It lives behind\n// the dedicated `zidane/session/sqlite` subpath so that non-Bun consumers don't\n// transitively evaluate `bun:sqlite` when they import from `zidane/session`.\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction generateId(): string {\n return `ses_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`\n}\n\nfunction isSafeExtensionStateKey(key: string): boolean {\n return typeof key === 'string' && key.length > 0 && !DANGEROUS_RECORD_KEYS.has(key)\n}\n\nfunction jsonClone<T extends JsonValue>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T\n}\n\nfunction assertSafeExtensionStateKey(key: string, label: string): void {\n if (!isSafeExtensionStateKey(key))\n throw new Error(`Invalid extension session state ${label}.`)\n}\n\nfunction cloneExtensionStateRoot(value: unknown): Record<string, Record<string, JsonValue>> {\n return clonePlainRecord(value) as Record<string, Record<string, JsonValue>>\n}\n\nfunction clonePlainRecord(value: unknown): Record<string, JsonValue> {\n if (!value || typeof value !== 'object' || Array.isArray(value))\n return {}\n const out: Record<string, JsonValue> = {}\n for (const [key, child] of Object.entries(value as Record<string, unknown>)) {\n if (!isSafeExtensionStateKey(key))\n continue\n if (isJsonValue(child))\n out[key] = child\n }\n return out\n}\n\nfunction isJsonValue(value: unknown, depth = 0): value is JsonValue {\n if (value === null)\n return true\n if (typeof value === 'string' || typeof value === 'boolean')\n return true\n if (typeof value === 'number')\n return Number.isFinite(value)\n if (typeof value !== 'object')\n return false\n if (depth >= 8)\n return false\n if (Array.isArray(value))\n return value.every(item => isJsonValue(item, depth + 1))\n return Object.entries(value).every(([key, child]) =>\n isSafeExtensionStateKey(key) && isJsonValue(child, depth + 1),\n )\n}\n\nfunction assertJsonBounded(value: JsonValue, maxBytes: number, label: string): void {\n const json = JSON.stringify(value)\n if (json === undefined)\n throw new Error(`${label} must be JSON serializable.`)\n if (Buffer.byteLength(json) > maxBytes)\n throw new Error(`${label} exceeds ${maxBytes} bytes.`)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"session-DzrXKLw-.js","names":[],"sources":["../src/session/file-map.ts","../src/session/memory.ts","../src/session/remote.ts","../src/session/index.ts"],"sourcesContent":["/**\n * File-map session store.\n *\n * Wraps a narrow 3-method adapter (`get` / `save` / `delete`) that exchanges a flat\n * map of filename → string content. Useful for embedding zidane sessions inside\n * host-provided session backends that only speak in file maps (not zidane's native\n * `SessionStore` shape).\n *\n * Serialization format:\n * - `turns.jsonl` — one `SessionTurn` per line.\n * - `meta.json` — session metadata (id, agentId, status, runs, metadata, timestamps).\n *\n * JSONL for turns keeps history inspectable with tools like `jq` and resilient to\n * partial corruption — parse up to the first bad line and you still have a valid\n * prefix. Metadata lives in its own file so large turn logs don't bloat the\n * metadata path.\n *\n * Scope: each `createFileMapStore` handles a **single session** — the adapter's\n * file map holds at most one zidane session at a time. This matches how host SDKs\n * scope their session stores per conversation.\n *\n * Divergences from the built-in memory / sqlite stores:\n * - `appendTurns` / `updateStatus` / `updateRun` auto-create a minimal `SessionData`\n * record on first write, instead of silently no-oping when the session hasn't been\n * explicitly `save()`-ed. This matches the host-SDK integration path where\n * `createSession(...)` → `agent.run(...)` directly without an explicit `save()` call.\n * - `updateRun` inserts the run if not found in the cached record (rather than\n * silently dropping). Run records therefore always reach the adapter.\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '.'\nimport type { SessionTurn } from '../types'\n\n/**\n * Host-provided file-map adapter. Three methods exchanging `Record<string, string>`\n * payloads — the whole persistence surface the wrapper needs.\n */\nexport interface FileMapAdapter {\n /** Load the current file map. Returns an empty `files` record when nothing is persisted. */\n get: () => Promise<{ files: Record<string, string> }>\n /** Replace the persisted file map. Full-rewrite semantics. */\n save: (files: Record<string, string>) => Promise<void>\n /** Delete all persisted state. */\n delete: () => Promise<void>\n}\n\nexport interface FileMapStoreOptions {\n /** Filename for the JSONL turns log. Default: `turns.jsonl`. */\n turnsFile?: string\n /** Filename for the metadata JSON. Default: `meta.json`. */\n metaFile?: string\n}\n\ninterface MetaShape {\n id: string\n agentId?: string\n /** Optional — absent on files written before projectRoot persistence. */\n projectRoot?: string\n runs: SessionRun[]\n status: SessionData['status']\n metadata: Record<string, unknown>\n createdAt: number\n updatedAt: number\n}\n\nfunction toMeta(data: SessionData): MetaShape {\n return {\n id: data.id,\n agentId: data.agentId,\n projectRoot: data.projectRoot,\n runs: data.runs,\n status: data.status,\n metadata: data.metadata,\n createdAt: data.createdAt,\n updatedAt: data.updatedAt,\n }\n}\n\nfunction toData(meta: MetaShape, turns: SessionTurn[]): SessionData {\n return {\n id: meta.id,\n agentId: meta.agentId,\n ...(typeof meta.projectRoot === 'string' ? { projectRoot: meta.projectRoot } : {}),\n turns,\n runs: meta.runs,\n status: meta.status,\n metadata: meta.metadata,\n createdAt: meta.createdAt,\n updatedAt: meta.updatedAt,\n }\n}\n\nfunction parseTurnsJsonl(jsonl: string): SessionTurn[] {\n if (!jsonl)\n return []\n const turns: SessionTurn[] = []\n for (const line of jsonl.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed)\n continue\n try {\n turns.push(JSON.parse(trimmed) as SessionTurn)\n }\n catch {\n // Skip malformed lines — preserves the valid prefix on partial corruption.\n }\n }\n return turns\n}\n\nfunction serializeTurnsJsonl(turns: SessionTurn[]): string {\n if (turns.length === 0)\n return ''\n return `${turns.map(t => JSON.stringify(t)).join('\\n')}\\n`\n}\n\n/**\n * Create a single-session `SessionStore` backed by a file-map adapter.\n *\n * @example\n * ```ts\n * const session = await createSession({\n * store: createFileMapStore(hostSessionStore),\n * })\n * ```\n */\nexport function createFileMapStore(\n adapter: FileMapAdapter,\n options: FileMapStoreOptions = {},\n): SessionStore {\n const turnsFile = options.turnsFile ?? 'turns.jsonl'\n const metaFile = options.metaFile ?? 'meta.json'\n\n // Cached view of the persisted session. Populated lazily on first access so the\n // factory itself doesn't do I/O.\n let cached: SessionData | null = null\n let hydrated = false\n // In-flight hydrate memo: concurrent first calls share one adapter.get()\n // instead of racing two reads (the loser's view would clobber the winner's\n // subsequent writes — a lost-update race).\n let hydrating: Promise<void> | null = null\n // Turns salvaged from turns.jsonl when meta.json was present but corrupt.\n // We can't trust the (unparseable) meta for the session id, so we hold the\n // turns aside and re-attach them when the next write establishes an id —\n // the alternative (treating the session as missing) would let that write\n // overwrite an intact turns.jsonl with an empty one.\n let recoveredTurns: SessionTurn[] | null = null\n\n async function hydrate(): Promise<void> {\n if (hydrated)\n return\n if (!hydrating) {\n hydrating = (async () => {\n const { files } = await adapter.get()\n const metaRaw = files[metaFile]\n if (metaRaw) {\n let meta: MetaShape | null = null\n try {\n meta = JSON.parse(metaRaw) as MetaShape\n }\n catch {\n meta = null\n }\n if (meta) {\n cached = toData(meta, parseTurnsJsonl(files[turnsFile] ?? ''))\n }\n else {\n recoveredTurns = parseTurnsJsonl(files[turnsFile] ?? '')\n console.error(`[zidane/file-map] ${metaFile} exists but is not valid JSON — preserving ${recoveredTurns.length} turn(s) from ${turnsFile} for the next write instead of treating the session as missing`)\n }\n }\n hydrated = true\n })().finally(() => {\n hydrating = null\n })\n }\n return hydrating\n }\n\n async function persist(data: SessionData): Promise<void> {\n const meta = toMeta(data)\n await adapter.save({\n [metaFile]: JSON.stringify(meta, null, 2),\n [turnsFile]: serializeTurnsJsonl(data.turns),\n })\n }\n\n // Ensure `cached` exists for `sessionId`, creating a minimal record when first written.\n // Returns false when `cached` already holds a different sessionId (request ignored).\n async function ensureCachedFor(sessionId: string): Promise<boolean> {\n await hydrate()\n if (cached) {\n return cached.id === sessionId\n }\n const now = Date.now()\n cached = {\n id: sessionId,\n // Re-attach turns salvaged from a corrupt-meta hydrate so the next\n // persist() doesn't destroy an intact turns.jsonl.\n turns: recoveredTurns ?? [],\n runs: [],\n status: 'idle',\n metadata: {},\n createdAt: now,\n updatedAt: now,\n }\n recoveredTurns = null\n hydrated = true\n return true\n }\n\n return {\n async load(sessionId: string): Promise<SessionData | null> {\n await hydrate()\n if (!cached || cached.id !== sessionId)\n return null\n return structuredClone(cached)\n },\n\n async save(data: SessionData): Promise<void> {\n cached = structuredClone(data)\n hydrated = true\n await persist(cached)\n },\n\n async delete(sessionId: string): Promise<void> {\n await hydrate()\n if (cached && cached.id !== sessionId)\n return\n cached = null\n await adapter.delete()\n },\n\n async list(filter): Promise<string[]> {\n await hydrate()\n if (!cached)\n return []\n if (filter?.agentId && cached.agentId !== filter.agentId)\n return []\n // file-map stores exactly one session, so the projectRoot filter\n // either keeps it (match / axis off) or drops it entirely.\n if (filter && 'projectRoot' in filter) {\n const v = filter.projectRoot\n if (v === null && cached.projectRoot != null)\n return []\n if (typeof v === 'string' && cached.projectRoot !== v)\n return []\n }\n return [cached.id]\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]): Promise<void> {\n const ok = await ensureCachedFor(sessionId)\n if (!ok)\n return\n cached!.turns.push(...structuredClone(turns))\n cached!.updatedAt = Date.now()\n await persist(cached!)\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number): Promise<SessionTurn[]> {\n await hydrate()\n if (!cached || cached.id !== sessionId)\n return []\n const slice = cached.turns.slice(from, limit !== undefined ? from + limit : undefined)\n return structuredClone(slice) as SessionTurn[]\n },\n\n async updateRun(sessionId: string, run: SessionRun): Promise<void> {\n const ok = await ensureCachedFor(sessionId)\n if (!ok)\n return\n const idx = cached!.runs.findIndex(r => r.id === run.id)\n if (idx >= 0)\n cached!.runs[idx] = structuredClone(run)\n else\n cached!.runs.push(structuredClone(run))\n cached!.updatedAt = Date.now()\n await persist(cached!)\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']): Promise<void> {\n const ok = await ensureCachedFor(sessionId)\n if (!ok)\n return\n cached!.status = status\n cached!.updatedAt = Date.now()\n await persist(cached!)\n },\n }\n}\n","/**\n * In-memory session store.\n * Useful for development and testing. Data is lost when the process exits.\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '.'\nimport type { SessionTurn } from '../types'\n\nexport function createMemoryStore(): SessionStore {\n const sessions = new Map<string, SessionData>()\n\n // Auto-create a minimal record when turns arrive for an unknown id. Mirrors\n // the file-map store: `createSession({store})` + `agent.run()` appends\n // turns without an explicit `save()` first — a get-or-no-op `appendTurns`\n // would silently lose that history. Deliberately scoped to `appendTurns`\n // only: a late `updateRun`/`updateStatus` racing a `delete()` must stay a\n // no-op rather than resurrect a deleted session.\n function ensureSession(sessionId: string): SessionData {\n let data = sessions.get(sessionId)\n if (!data) {\n const now = Date.now()\n data = {\n id: sessionId,\n turns: [],\n runs: [],\n status: 'idle',\n metadata: {},\n createdAt: now,\n updatedAt: now,\n }\n sessions.set(sessionId, data)\n }\n return data\n }\n\n return {\n async load(sessionId: string) {\n const data = sessions.get(sessionId)\n return data ? structuredClone(data) : null\n },\n\n async save(session: SessionData) {\n sessions.set(session.id, structuredClone(session))\n },\n\n async delete(sessionId: string) {\n sessions.delete(sessionId)\n },\n\n async list(filter) {\n let ids = Array.from(sessions.keys())\n if (filter?.agentId) {\n ids = ids.filter(id => sessions.get(id)?.agentId === filter.agentId)\n }\n // `projectRoot` mirrors the SQLite store's tri-state contract:\n // string → only sessions tagged with that root\n // null → only UNTAGGED sessions (legacy / pre-v3 rows)\n // undefined → axis ignored (tagged + untagged both returned)\n if (filter && 'projectRoot' in filter) {\n const v = filter.projectRoot\n if (v === null)\n ids = ids.filter(id => sessions.get(id)?.projectRoot == null)\n else if (typeof v === 'string')\n ids = ids.filter(id => sessions.get(id)?.projectRoot === v)\n }\n if (filter?.limit) {\n ids = ids.slice(0, filter.limit)\n }\n return ids\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]) {\n const data = ensureSession(sessionId)\n data.turns.push(...structuredClone(turns))\n data.updatedAt = Date.now()\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number) {\n const data = sessions.get(sessionId)\n if (!data)\n return []\n const sliced = data.turns.slice(from, limit !== undefined ? from + limit : undefined)\n return structuredClone(sliced) as SessionTurn[]\n },\n\n async updateRun(sessionId: string, run: SessionRun) {\n const data = sessions.get(sessionId)\n if (!data)\n return\n // Upsert — see the matching comment in sqlite.ts's\n // `txnUpdateRun` for the failure mode. tl;dr: the agent's\n // `startRun()` only updates in-memory state; the store first\n // sees the run via `updateRun()` at finalize time, so an\n // update-only impl silently drops every new run.\n const idx = data.runs.findIndex(r => r.id === run.id)\n if (idx >= 0)\n data.runs[idx] = structuredClone(run)\n else\n data.runs.push(structuredClone(run))\n data.updatedAt = Date.now()\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']) {\n const data = sessions.get(sessionId)\n if (!data)\n return\n data.status = status\n data.updatedAt = Date.now()\n },\n }\n}\n","/**\n * Remote session store via HTTP API.\n *\n * Expects a REST API with:\n * GET {url}/sessions/{id} -> SessionData | 404\n * PUT {url}/sessions/{id} -> save SessionData\n * DELETE {url}/sessions/{id} -> delete\n * GET {url}/sessions?agentId=&limit=&projectRoot= -> { ids: string[] }\n * `projectRoot=__null__` is the wire encoding for \"untagged only\".\n * POST {url}/sessions/{id}/turns -> append turns\n * GET {url}/sessions/{id}/turns?from=&limit= -> SessionTurn[]\n * PUT {url}/sessions/{id}/runs/{runId} -> update run\n * PATCH {url}/sessions/{id} -> { status }\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '.'\nimport type { SessionTurn } from '../types'\n\nexport interface RemoteStoreOptions {\n /** Base URL of the session API */\n url: string\n /** Optional headers (e.g. for authentication) */\n headers?: Record<string, string>\n /** Per-request timeout in ms. Default 30s — bounds a hung backend. */\n timeoutMs?: number\n}\n\nconst TRAILING_SLASH = /\\/$/\nconst DEFAULT_TIMEOUT_MS = 30_000\n\nexport function createRemoteStore(options: RemoteStoreOptions): SessionStore {\n const baseUrl = options.url.replace(TRAILING_SLASH, '')\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const defaultHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...options.headers,\n }\n\n async function request(path: string, init?: RequestInit): Promise<Response> {\n const url = `${baseUrl}${path}`\n // Bound every call so a hung backend can't wedge the caller forever.\n const res = await fetch(url, {\n ...init,\n headers: { ...defaultHeaders, ...init?.headers },\n signal: init?.signal ?? AbortSignal.timeout(timeoutMs),\n })\n return res\n }\n\n /**\n * Parse a JSON response body, rethrowing parse failures with URL/status\n * context so a malformed backend reply produces a diagnosable error\n * instead of an opaque `Unexpected token` from deep in `res.json()`.\n */\n async function parseJson<T>(res: Response, path: string): Promise<T> {\n try {\n return await res.json() as T\n }\n catch (cause) {\n throw new Error(\n `Remote session: malformed JSON from ${path} (${res.status} ${res.statusText})`,\n { cause },\n )\n }\n }\n\n return {\n async load(sessionId: string) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}`)\n if (!res.ok) {\n if (res.status === 404)\n return null\n throw new Error(`Remote session load failed: ${res.status} ${res.statusText}`)\n }\n return await parseJson<SessionData>(res, `/sessions/${sessionId}`)\n },\n\n async save(session: SessionData) {\n const res = await request(`/sessions/${encodeURIComponent(session.id)}`, {\n method: 'PUT',\n body: JSON.stringify(session),\n })\n if (!res.ok) {\n throw new Error(`Remote session save failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async delete(sessionId: string) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}`, {\n method: 'DELETE',\n })\n if (!res.ok && res.status !== 404) {\n throw new Error(`Remote session delete failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async list(filter) {\n const params = new URLSearchParams()\n if (filter?.agentId)\n params.set('agentId', filter.agentId)\n if (filter?.limit)\n params.set('limit', String(filter.limit))\n if (filter && 'projectRoot' in filter) {\n const v = filter.projectRoot\n if (v === null)\n params.set('projectRoot', '__null__')\n else if (typeof v === 'string')\n params.set('projectRoot', v)\n }\n\n const query = params.toString()\n const path = query ? `/sessions?${query}` : '/sessions'\n const res = await request(path)\n\n if (!res.ok) {\n throw new Error(`Remote session list failed: ${res.status} ${res.statusText}`)\n }\n\n const body = await parseJson<{ ids: string[] }>(res, path)\n if (!Array.isArray(body?.ids)) {\n throw new TypeError(`Remote session list: expected { ids: string[] } from ${path}, got ${typeof body?.ids}`)\n }\n return body.ids\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}/turns`, {\n method: 'POST',\n body: JSON.stringify(turns),\n })\n if (!res.ok) {\n throw new Error(`Remote appendTurns failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number) {\n const params = new URLSearchParams()\n if (from)\n params.set('from', String(from))\n if (limit !== undefined)\n params.set('limit', String(limit))\n\n const query = params.toString()\n const path = `/sessions/${encodeURIComponent(sessionId)}/turns${query ? `?${query}` : ''}`\n const res = await request(path)\n\n if (!res.ok) {\n throw new Error(`Remote getTurns failed: ${res.status} ${res.statusText}`)\n }\n\n const turns = await parseJson<SessionTurn[]>(res, path)\n if (!Array.isArray(turns)) {\n throw new TypeError(`Remote getTurns: expected SessionTurn[] from ${path}, got ${typeof turns}`)\n }\n return turns\n },\n\n async updateRun(sessionId: string, run: SessionRun) {\n const res = await request(\n `/sessions/${encodeURIComponent(sessionId)}/runs/${encodeURIComponent(run.id)}`,\n {\n method: 'PUT',\n body: JSON.stringify(run),\n },\n )\n if (!res.ok) {\n throw new Error(`Remote updateRun failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}`, {\n method: 'PATCH',\n body: JSON.stringify({ status }),\n })\n if (!res.ok) {\n throw new Error(`Remote updateStatus failed: ${res.status} ${res.statusText}`)\n }\n },\n }\n}\n","/**\n * Session management for agents.\n *\n * A session tracks identity, turn history, and run metadata.\n * Plug in any storage backend by implementing the SessionStore interface,\n * or use one of the built-in stores: memory, sqlite, remote.\n */\n\nimport type { JsonValue, SessionMessage, SessionTurn, TurnUsage } from '../types'\nimport type { ToWireMessagesOptions } from './messages'\nimport { AsyncLocalStorage } from 'node:async_hooks'\nimport { Buffer } from 'node:buffer'\nimport { toWireMessages } from './messages'\n\nexport type { SessionContentBlock, SessionMessage, SessionTurn } from '../types'\nexport { createFileMapStore } from './file-map'\n\nconst EXTENSION_SESSION_METADATA_KEY = 'extensions'\nconst EXTENSION_SESSION_ITEM_MAX_BYTES = 16_384\nconst EXTENSION_SESSION_TOTAL_MAX_BYTES = 65_536\nconst DANGEROUS_RECORD_KEYS = new Set(['__proto__', 'prototype', 'constructor'])\nconst extensionSessionStorage = new AsyncLocalStorage<Session>()\n\n/**\n * Run `fn` with `session` bound as the active extension session for the\n * duration of the call (and every async continuation it spawns). Uses\n * `AsyncLocalStorage.run` rather than `enterWith` so concurrent agents in the\n * same process (e.g. the GUI's multi-session host) never observe each other's\n * session through `currentExtensionSession()`.\n */\nexport function withExtensionSession<T>(session: Session | undefined, fn: () => T): T {\n if (!session)\n return fn()\n return extensionSessionStorage.run(session, fn)\n}\n\nexport function currentExtensionSession(): Session | undefined {\n return extensionSessionStorage.getStore()\n}\n\nexport function getExtensionSessionState<T = JsonValue>(\n session: Session | undefined,\n extensionName: string,\n key: string,\n): T | undefined {\n if (!session || !isSafeExtensionStateKey(extensionName) || !isSafeExtensionStateKey(key))\n return undefined\n const root = session.metadata[EXTENSION_SESSION_METADATA_KEY]\n if (!root || typeof root !== 'object' || Array.isArray(root))\n return undefined\n const byExtension = (root as Record<string, unknown>)[extensionName]\n if (!byExtension || typeof byExtension !== 'object' || Array.isArray(byExtension))\n return undefined\n const stored = (byExtension as Record<string, unknown>)[key]\n if (stored === undefined)\n return undefined\n // Hand back a copy so a caller mutating the result can't reach into the live\n // session metadata behind `setMeta`/`save`.\n return jsonClone(stored as JsonValue) as T\n}\n\nexport async function setExtensionSessionState(\n session: Session | undefined,\n extensionName: string,\n key: string,\n value: JsonValue,\n): Promise<void> {\n if (!session)\n return\n assertSafeExtensionStateKey(extensionName, 'extensionName')\n assertSafeExtensionStateKey(key, 'key')\n assertJsonBounded(value, EXTENSION_SESSION_ITEM_MAX_BYTES, `extension session state \"${extensionName}.${key}\"`)\n\n const root = cloneExtensionStateRoot(session.metadata[EXTENSION_SESSION_METADATA_KEY])\n const byExtension = clonePlainRecord(root[extensionName])\n // Deep-copy on write so later mutations to the caller's object don't bleed\n // into persisted state.\n byExtension[key] = jsonClone(value)\n root[extensionName] = byExtension\n assertJsonBounded(root as JsonValue, EXTENSION_SESSION_TOTAL_MAX_BYTES, 'extension session state')\n session.setMeta(EXTENSION_SESSION_METADATA_KEY, root)\n if (session.store)\n await session.save()\n}\n\nexport async function deleteExtensionSessionState(\n session: Session | undefined,\n extensionName: string,\n key: string,\n): Promise<void> {\n if (!session || !isSafeExtensionStateKey(extensionName) || !isSafeExtensionStateKey(key))\n return\n const root = cloneExtensionStateRoot(session.metadata[EXTENSION_SESSION_METADATA_KEY])\n const byExtension = clonePlainRecord(root[extensionName])\n if (!(key in byExtension))\n return\n delete byExtension[key]\n if (Object.keys(byExtension).length > 0)\n root[extensionName] = byExtension\n else\n delete root[extensionName]\n session.setMeta(EXTENSION_SESSION_METADATA_KEY, root)\n if (session.store)\n await session.save()\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SessionRun {\n id: string\n startedAt: number\n endedAt?: number\n prompt: string\n status: 'running' | 'completed' | 'aborted' | 'error'\n turns?: number\n tokensIn?: number\n tokensOut?: number\n error?: string\n /** Per-turn usage breakdown */\n turnUsage?: TurnUsage[]\n /** Total usage across all turns */\n totalUsage?: TurnUsage\n /** Estimated cost in USD */\n cost?: number\n /**\n * The run that spawned this one, when the agent is a subagent sharing its\n * parent's session. Undefined on top-level `agent.run()`. Consumers can walk\n * `runs` by `parentRunId` to reconstruct the subagent tree.\n */\n parentRunId?: string\n /**\n * Zero-based subagent depth. 0 = top-level run, 1 = direct child, …\n * Recorded here so hosts can query/filter by level without walking the tree.\n */\n depth?: number\n /**\n * How this run was initiated when not a plain user prompt. `command` marks\n * a sub-run driven from a `/` command's `ctx.runAgent`; `display` is the\n * short line the transcript shows instead of the full injected prompt\n * (which often embeds bulky material like a diff). Presentation-only —\n * the wire/model payload is unaffected. Absent on normal runs.\n */\n origin?: { kind: 'command', display?: string }\n}\n\nexport interface SessionData {\n id: string\n agentId?: string\n /**\n * Absolute path of the project this session belongs to — typically\n * the git root resolved from `cwd` at creation time, falling back to\n * `cwd` itself when not in a git repo. Set ONCE on creation and never\n * mutated thereafter (the session \"belongs\" to that project forever).\n *\n * Used by the TUI's sessions list to filter rows by current project,\n * so the user only sees conversations relevant to where they are\n * working — without needing one `.{prefix}/` directory per project.\n *\n * `undefined` on pre-tagging legacy sessions; the chat layer treats\n * those as \"untagged\" and hides them unless `Settings.showAllProjects`\n * is on.\n */\n projectRoot?: string\n turns: SessionTurn[]\n runs: SessionRun[]\n status: 'idle' | 'running' | 'completed' | 'error'\n metadata: Record<string, unknown>\n createdAt: number\n updatedAt: number\n}\n\n/**\n * Compact per-session summary returned by {@link SessionStore.listMeta}.\n * Raw facts only — presentation (title derivation, fallback strings)\n * stays in the chat layer (`listSessionMeta` / `deriveSessionTitle`).\n */\nexport interface SessionMetaRow {\n id: string\n /** Header metadata (may carry a stored `title`). */\n metadata: Record<string, unknown>\n /** Absent for untagged (legacy) sessions. */\n projectRoot?: string\n updatedAt: number\n turnCount: number\n /** Count of `role: 'user'` turns. */\n userMessageCount: number\n runCount: number\n /**\n * First `role: 'user'` turn — the title-derivation source. Stores may\n * omit it when `metadata.title` is already set (no derivation needed).\n */\n firstUserTurn?: SessionTurn\n}\n\n// ---------------------------------------------------------------------------\n// SessionStore interface (pluggable backend)\n// ---------------------------------------------------------------------------\n\nexport interface SessionStore {\n /** Optional: generate a session ID server-side (e.g. a database-issued UUID). */\n generateSessionId?: () => string | Promise<string>\n\n /**\n * Optional: generate a turn ID server-side for session-managed user turns.\n *\n * Provider-loop assistant and tool-result turns use AgentClock.randomUUID()\n * instead, so durable-execution adapters can keep those IDs replay-stable.\n */\n generateTurnId?: () => string | Promise<string>\n\n /** Load a session by ID. Returns null if not found. */\n load: (sessionId: string) => Promise<SessionData | null>\n\n /** Save a session (create or update, full document). */\n save: (session: SessionData) => Promise<void>\n\n /** Delete a session. */\n delete: (sessionId: string) => Promise<void>\n\n /**\n * List session IDs, optionally filtered. `projectRoot` restricts to\n * sessions whose `SessionData.projectRoot` matches exactly — untagged\n * (legacy) sessions are NOT returned under that filter; pass `null`\n * explicitly to ask for untagged ones, or omit the field to ignore\n * the axis entirely. `agentId` filters by recorded agent; the two\n * conditions AND together when both are set.\n */\n list: (filter?: { agentId?: string, limit?: number, projectRoot?: string | null }) => Promise<string[]>\n\n /**\n * Optional: list session summaries without materializing full sessions.\n *\n * The generic fallback (`listSessionMeta` in `zidane/chat`) is `list()`\n * + `load()` per id — O(every turn in the store) because each `load`\n * parses the full turn history just to count rows and derive a title.\n * Stores that can answer from an index (SQL aggregates, a denormalized\n * header) should implement this; the sqlite store turns a multi-hundred-\n * ms TUI boot cost into a single query.\n *\n * Same filter contract and ordering (most recently updated first) as\n * {@link list}.\n */\n listMeta?: (filter?: { agentId?: string, limit?: number, projectRoot?: string | null }) => Promise<SessionMetaRow[]>\n\n /** Append new turns to a session (incremental, avoids full re-save). */\n appendTurns: (sessionId: string, turns: SessionTurn[]) => Promise<void>\n\n /** Return a slice of turns for a session. */\n getTurns: (sessionId: string, from?: number, limit?: number) => Promise<SessionTurn[]>\n\n /**\n * Persist a run record (called after completeRun / abortRun / errorRun).\n *\n * **Upsert semantics required.** The agent's run lifecycle calls\n * `session.startRun()` to register a new run in IN-MEMORY state and\n * then `updateRun()` at finalize. The store doesn't see the run any\n * earlier — `appendTurns` is the only intermediate call, and it\n * persists turns but not runs. An update-only implementation would\n * silently drop every newly-created run record at finalize, leaving\n * the on-disk `runs[]` missing entries that the persisted turns\n * reference (which `eventsFromTurns` then mis-classifies via\n * `ancestryOf` → broken depth, broken `child-N` labels, broken\n * `userPrompts` history).\n *\n * Implementations MUST insert when the id doesn't exist and update\n * when it does. The bundled `sqlite` / `memory` / `file-map` stores\n * all follow this contract; remote stores using HTTP PUT get upsert\n * for free via REST semantics.\n */\n updateRun: (sessionId: string, run: SessionRun) => Promise<void>\n\n /** Update the top-level status of a session. */\n updateStatus: (sessionId: string, status: SessionData['status']) => Promise<void>\n\n /**\n * Optional: release backend resources (e.g. close the SQLite handle).\n * Hosts should call this on teardown when present; stores backed by\n * memory or remote HTTP have nothing to release and may omit it.\n */\n close?: () => void\n}\n\n// ---------------------------------------------------------------------------\n// Session (live instance wrapping a SessionData)\n// ---------------------------------------------------------------------------\n\nexport interface Session {\n /** Session ID */\n readonly id: string\n\n /** Agent ID (optional label) */\n readonly agentId?: string\n\n /**\n * Project this session was created under — see {@link SessionData.projectRoot}.\n * Set once on creation; surfaces here for read-only inspection.\n */\n readonly projectRoot?: string\n\n /**\n * Raw persisted turn history — kept pristine for fidelity (the model's\n * reasoning chain stays intact across reloads).\n *\n * **DO NOT pass this directly to a provider request.** Mid-pair\n * persistence (interrupted runs, partial-result writes, compaction\n * boundaries) leaves orphan `tool_use` / `tool_result` blocks here that\n * every provider 400s on (`tool_result must be preceded by a tool_call\n * with the same toolCallId`). The agent's own loop repairs these\n * just-in-time at wire-send time and never writes the repair back to\n * `turns`.\n *\n * For provider requests built outside `agent.run()`, use\n * {@link Session.toMessages} — it applies the same defensive pairing\n * repair and resolves to a guaranteed-safe `SessionMessage[]`.\n */\n readonly turns: SessionTurn[]\n\n /**\n * True when this session has no turns yet.\n *\n * Use this as a first-prompt signal when setting up a run — e.g. writing initial\n * configuration only on fresh sessions. Equivalent to `turns.length === 0`.\n */\n readonly isEmpty: boolean\n\n /** Top-level session status */\n readonly status: SessionData['status']\n\n /** All runs in this session */\n readonly runs: SessionRun[]\n\n /** Arbitrary metadata */\n readonly metadata: Record<string, unknown>\n\n /**\n * Start tracking a new run. `extras.parentRunId` + `extras.depth` are\n * populated by the spawn tool when a child agent shares its parent's\n * session; regular top-level `agent.run()` calls omit them.\n */\n startRun: (runId: string, prompt?: string, extras?: { parentRunId?: string, depth?: number }) => void\n\n /** Mark a run as completed */\n completeRun: (runId: string, stats: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) => void\n\n /** Mark a run as aborted */\n /**\n * Optional `stats` lets the agent backfill the run's token totals when\n * the abort happened *after* the loop accumulated meaningful usage —\n * common when the user presses esc mid-streaming. Without it, the run\n * record reads `0 in / 0 out` on reload regardless of how much was\n * spent before the abort. Same shape as `completeRun`'s stats so the\n * persisted `totalUsage` aggregate stays consistent across paths.\n */\n abortRun: (runId: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) => void\n\n /** Mark a run as errored */\n /** Optional `stats` — same rationale as `abortRun.stats`. */\n errorRun: (runId: string, error: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) => void\n\n /** Append turns to in-memory history AND persist via store.appendTurns (if store present) */\n appendTurns: (turns: SessionTurn[]) => Promise<void>\n\n /** Replace all turns in-memory (does not persist — use save() for that) */\n setTurns: (turns: SessionTurn[]) => void\n\n /**\n * Replace all runs in-memory (does not persist — use save() for that).\n * Mirrors {@link setTurns} for the fork / restore case: callers that\n * bootstrap a session from an externally-derived snapshot (e.g.\n * `onForkTurn` copying parent runs into a child session) need this so\n * the cloned runs land in `data.runs` before the first `save()`.\n * Production agent runs continue to mutate runs via `startRun` /\n * `completeRun` / `updateRun`; this is the bulk-replace escape hatch.\n */\n setRuns: (runs: SessionRun[]) => void\n\n /** Update the session status in memory AND via store.updateStatus (if store present) */\n updateStatus: (status: SessionData['status']) => Promise<void>\n\n /** Persist an updated run record via store.updateRun (if store present) */\n updateRun: (run: SessionRun) => Promise<void>\n\n /** Generate a session-managed turn ID using store.generateTurnId if available, else crypto.randomUUID() */\n generateTurnId: () => string | Promise<string>\n\n /** Set metadata key */\n setMeta: (key: string, value: unknown) => void\n\n /** Persist the full session document to the store */\n save: () => Promise<void>\n\n /**\n * Project the session's persisted turns into wire-ready messages safe to\n * hand to any provider.\n *\n * The canonical answer to \"I need to send these turns to Anthropic /\n * OpenAI from outside `agent.run()`\" (sub-agent classifier, side-channel\n * summarizer, replay worker, …). Runs the same defensive pairing repair\n * the agent loop applies just-in-time at wire-send time\n * ({@link ensureToolResultPairing}), so orphan `tool_use` blocks and\n * dangling `tool_result`s never reach the provider as the 400\n * \"tool_result must be preceded by a tool_call with the same toolCallId\".\n *\n * Pass `provider` to also enforce the user-tail invariant\n * ({@link ensureEndsWithUserMessage}) — required for opus 4.7 / o-series\n * style models that reject assistant-prefill.\n *\n * Pure + idempotent. Resolves content refs only in the returned messages\n * and does NOT mutate `session.turns`. **Do not write the result back via\n * `setTurns` / `appendTurns`** — synthetic placeholders or resolved\n * payloads will contaminate persisted history.\n */\n toMessages: (options?: ToWireMessagesOptions) => Promise<SessionMessage[]>\n\n /** Serialize to SessionData */\n toJSON: () => SessionData\n\n /**\n * The backing store, when one was provided. Exposed so hosts can call\n * its optional `close()` on teardown (e.g. release the SQLite handle).\n */\n readonly store?: SessionStore\n}\n\n// ---------------------------------------------------------------------------\n// createSession\n// ---------------------------------------------------------------------------\n\nexport interface CreateSessionOptions {\n /** Session ID. If omitted and store provides generateSessionId, that is used. */\n id?: string\n /** Agent ID label */\n agentId?: string\n /**\n * Project tag — see {@link SessionData.projectRoot}. Stamped once on\n * creation; ignored when `_data` is set (restoring an existing\n * session preserves whatever was already persisted there). The TUI\n * resolves this from `findGitRoot(cwd) ?? cwd` so sessions started\n * from the same repo (no matter which subdir) share one tag.\n */\n projectRoot?: string\n /** Initial metadata */\n metadata?: Record<string, unknown>\n /** Storage backend (optional, enables save/load) */\n store?: SessionStore\n // @internal: restore from existing data (bypasses id/agentId/metadata options)\n _data?: SessionData\n}\n\n/**\n * Create a new session.\n * Async so stores that generate IDs server-side (e.g. a database) can be supported.\n */\nexport async function createSession(options: CreateSessionOptions = {}): Promise<Session> {\n const store = options.store\n const now = Date.now()\n\n let sessionId = options.id\n if (!sessionId && store?.generateSessionId) {\n sessionId = await store.generateSessionId()\n }\n if (!sessionId) {\n sessionId = generateId()\n }\n\n const data: SessionData = options._data ?? {\n id: sessionId,\n agentId: options.agentId,\n // Stamp the project tag at creation only — restored sessions (the\n // `_data` branch above) keep whatever was already persisted, even\n // if `options.projectRoot` differs. A session's project identity\n // is sticky for its lifetime; loading the same session from a\n // different cwd doesn't re-home it.\n ...(options.projectRoot ? { projectRoot: options.projectRoot } : {}),\n turns: [],\n runs: [],\n status: 'idle',\n metadata: options.metadata ?? {},\n createdAt: now,\n updatedAt: now,\n }\n\n function touch() {\n data.updatedAt = Date.now()\n }\n\n function findRun(runId: string): SessionRun | undefined {\n return data.runs.find(r => r.id === runId)\n }\n\n /**\n * Apply per-run usage stats onto a SessionRun. Shared by `completeRun`,\n * `abortRun`, and `errorRun` so the on-disk shape stays consistent across\n * exit paths — historically only `completeRun` filled these in, and\n * aborted/errored runs rendered `0 in / 0 out` on reload regardless of\n * how much was actually consumed.\n */\n function applyRunStats(run: SessionRun, stats: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n run.turns = stats.turns\n run.tokensIn = stats.tokensIn\n run.tokensOut = stats.tokensOut\n if (stats.turnUsage) {\n run.turnUsage = stats.turnUsage\n const total = stats.turnUsage.reduce((acc, t) => ({\n input: acc.input + t.input,\n output: acc.output + t.output,\n cacheCreation: (acc.cacheCreation ?? 0) + (t.cacheCreation ?? 0),\n cacheRead: (acc.cacheRead ?? 0) + (t.cacheRead ?? 0),\n thinking: (acc.thinking ?? 0) + (t.thinking ?? 0),\n }), { input: 0, output: 0, cacheCreation: 0, cacheRead: 0, thinking: 0 })\n run.totalUsage = {\n input: total.input,\n output: total.output,\n ...(total.cacheCreation ? { cacheCreation: total.cacheCreation } : {}),\n ...(total.cacheRead ? { cacheRead: total.cacheRead } : {}),\n ...(total.thinking ? { thinking: total.thinking } : {}),\n }\n }\n if (stats.cost !== undefined)\n run.cost = stats.cost\n }\n\n const session: Session = {\n ...(store ? { store } : {}),\n get id() { return data.id },\n get agentId() { return data.agentId },\n get projectRoot() { return data.projectRoot },\n get turns() { return data.turns },\n get isEmpty() { return data.turns.length === 0 },\n get status() { return data.status },\n get runs() { return data.runs },\n get metadata() { return data.metadata },\n\n startRun(runId: string, prompt?: string, extras?: { parentRunId?: string, depth?: number }) {\n data.runs.push({\n id: runId,\n startedAt: Date.now(),\n prompt: prompt ?? '',\n status: 'running',\n ...(extras?.parentRunId ? { parentRunId: extras.parentRunId } : {}),\n ...(typeof extras?.depth === 'number' ? { depth: extras.depth } : {}),\n })\n touch()\n },\n\n completeRun(runId: string, stats: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n const run = findRun(runId)\n if (run) {\n run.status = 'completed'\n run.endedAt = Date.now()\n applyRunStats(run, stats)\n }\n touch()\n },\n\n abortRun(runId: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n const run = findRun(runId)\n if (run) {\n run.status = 'aborted'\n run.endedAt = Date.now()\n // Backfill tokens when available so an aborted run's session\n // ledger reflects actual consumption rather than `0 in / 0 out`.\n if (stats)\n applyRunStats(run, stats)\n }\n touch()\n },\n\n errorRun(runId: string, error: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n const run = findRun(runId)\n if (run) {\n run.status = 'error'\n run.endedAt = Date.now()\n run.error = error\n if (stats)\n applyRunStats(run, stats)\n }\n touch()\n },\n\n async appendTurns(turns: SessionTurn[]) {\n data.turns.push(...turns)\n touch()\n if (store) {\n await store.appendTurns(data.id, turns)\n }\n },\n\n setTurns(turns: SessionTurn[]) {\n data.turns = turns\n touch()\n },\n\n setRuns(runs: SessionRun[]) {\n data.runs = runs\n touch()\n },\n\n async updateStatus(status: SessionData['status']) {\n data.status = status\n touch()\n if (store) {\n await store.updateStatus(data.id, status)\n }\n },\n\n async updateRun(run: SessionRun) {\n if (store) {\n await store.updateRun(data.id, run)\n }\n },\n\n generateTurnId() {\n if (store?.generateTurnId) {\n return store.generateTurnId()\n }\n return crypto.randomUUID()\n },\n\n setMeta(key: string, value: unknown) {\n data.metadata[key] = value\n touch()\n },\n\n async save() {\n if (!store) {\n throw new Error('No SessionStore configured. Pass a store to createSession() to enable persistence.')\n }\n await store.save(data)\n },\n\n toMessages(options?: ToWireMessagesOptions) {\n return toWireMessages(data.turns, options)\n },\n\n toJSON() {\n return structuredClone(data)\n },\n }\n\n return session\n}\n\n/**\n * Demote a persisted `'running'` status to `'idle'` (in place) and return the\n * data. A persisted `'running'` is necessarily stale: the run that set it lived\n * in another process (crash, kill -9, power loss) and cannot still be executing\n * in a freshly-loaded session. Call this on the result of a raw `store.load()`\n * so consumers don't render a ghost in-progress state or refuse a new run.\n * `loadSession` applies it automatically; direct `store.load()` callers (e.g.\n * the TUI) should run loaded data through this.\n *\n * Run RECORDS get the same treatment: the agent persists each record at\n * run start with `status: 'running'` (crash durability — see the\n * `SessionStore.updateRun` contract), so a freshly-loaded session may\n * carry `'running'` runs that died with their process. They demote to\n * `'aborted'` so replay renders a truthful \"aborted\" state instead of a\n * perpetually-in-flight run.\n */\nexport function demoteStaleRunningStatus(data: SessionData): SessionData {\n if (data.status === 'running')\n data.status = 'idle'\n for (const run of data.runs) {\n if (run.status === 'running') {\n run.status = 'aborted'\n run.endedAt ??= run.startedAt\n }\n }\n return data\n}\n\n/**\n * Load an existing session from a store.\n */\nexport async function loadSession(store: SessionStore, sessionId: string): Promise<Session | null> {\n const loaded = await store.load(sessionId)\n if (!loaded)\n return null\n\n demoteStaleRunningStatus(loaded)\n\n return createSession({ store, _data: loaded })\n}\n\n// ---------------------------------------------------------------------------\n// Re-export stores\n// ---------------------------------------------------------------------------\n\nexport type { FileMapAdapter, FileMapStoreOptions } from './file-map'\nexport { createMemoryStore } from './memory'\nexport { autoDetectAndConvert, fromAnthropic, fromOpenAI, toAnthropic, toOpenAI, toWireMessages } from './messages'\nexport type { ToWireMessagesOptions } from './messages'\nexport { createRemoteStore } from './remote'\nexport type { RemoteStoreOptions } from './remote'\n\n// NOTE: `createSqliteStore` is intentionally NOT re-exported here. It lives behind\n// the dedicated `zidane/session/sqlite` subpath so that non-Bun consumers don't\n// transitively evaluate `bun:sqlite` when they import from `zidane/session`.\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction generateId(): string {\n return `ses_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`\n}\n\nfunction isSafeExtensionStateKey(key: string): boolean {\n return typeof key === 'string' && key.length > 0 && !DANGEROUS_RECORD_KEYS.has(key)\n}\n\nfunction jsonClone<T extends JsonValue>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T\n}\n\nfunction assertSafeExtensionStateKey(key: string, label: string): void {\n if (!isSafeExtensionStateKey(key))\n throw new Error(`Invalid extension session state ${label}.`)\n}\n\nfunction cloneExtensionStateRoot(value: unknown): Record<string, Record<string, JsonValue>> {\n return clonePlainRecord(value) as Record<string, Record<string, JsonValue>>\n}\n\nfunction clonePlainRecord(value: unknown): Record<string, JsonValue> {\n if (!value || typeof value !== 'object' || Array.isArray(value))\n return {}\n const out: Record<string, JsonValue> = {}\n for (const [key, child] of Object.entries(value as Record<string, unknown>)) {\n if (!isSafeExtensionStateKey(key))\n continue\n if (isJsonValue(child))\n out[key] = child\n }\n return out\n}\n\nfunction isJsonValue(value: unknown, depth = 0): value is JsonValue {\n if (value === null)\n return true\n if (typeof value === 'string' || typeof value === 'boolean')\n return true\n if (typeof value === 'number')\n return Number.isFinite(value)\n if (typeof value !== 'object')\n return false\n if (depth >= 8)\n return false\n if (Array.isArray(value))\n return value.every(item => isJsonValue(item, depth + 1))\n return Object.entries(value).every(([key, child]) =>\n isSafeExtensionStateKey(key) && isJsonValue(child, depth + 1),\n )\n}\n\nfunction assertJsonBounded(value: JsonValue, maxBytes: number, label: string): void {\n const json = JSON.stringify(value)\n if (json === undefined)\n throw new Error(`${label} must be JSON serializable.`)\n if (Buffer.byteLength(json) > maxBytes)\n throw new Error(`${label} exceeds ${maxBytes} bytes.`)\n}\n"],"mappings":"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"}
|
package/dist/session.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Cp as withExtensionSession, Dp as FileMapAdapter, Ep as createMemoryStore, Gp as fromOpenAI, Jp as toOpenAI, Lp as ToWireMessagesOptions, Op as FileMapStoreOptions, Sp as setExtensionSessionState, Tf as SessionTurn, Tp as createRemoteStore, Wp as fromAnthropic, Yp as toWireMessages, _p as currentExtensionSession, bp as getExtensionSessionState, dp as Session, fp as SessionData, gp as createSession, hp as SessionStore, kp as createFileMapStore, mp as SessionRun, pp as SessionMetaRow, qp as toAnthropic, up as CreateSessionOptions, vp as deleteExtensionSessionState, wf as SessionMessage, wp as RemoteStoreOptions, xf as SessionContentBlock, xp as loadSession, yp as demoteStaleRunningStatus, zp as autoDetectAndConvert } from "./index-
|
|
1
|
+
import { Cp as withExtensionSession, Dp as FileMapAdapter, Ep as createMemoryStore, Gp as fromOpenAI, Jp as toOpenAI, Lp as ToWireMessagesOptions, Op as FileMapStoreOptions, Sp as setExtensionSessionState, Tf as SessionTurn, Tp as createRemoteStore, Wp as fromAnthropic, Yp as toWireMessages, _p as currentExtensionSession, bp as getExtensionSessionState, dp as Session, fp as SessionData, gp as createSession, hp as SessionStore, kp as createFileMapStore, mp as SessionRun, pp as SessionMetaRow, qp as toAnthropic, up as CreateSessionOptions, vp as deleteExtensionSessionState, wf as SessionMessage, wp as RemoteStoreOptions, xf as SessionContentBlock, xp as loadSession, yp as demoteStaleRunningStatus, zp as autoDetectAndConvert } from "./index-l0wehkWU.js";
|
|
2
2
|
export { CreateSessionOptions, type FileMapAdapter, type FileMapStoreOptions, type RemoteStoreOptions, Session, type SessionContentBlock, SessionData, type SessionMessage, SessionMetaRow, SessionRun, SessionStore, type SessionTurn, type ToWireMessagesOptions, autoDetectAndConvert, createFileMapStore, createMemoryStore, createRemoteStore, createSession, currentExtensionSession, deleteExtensionSessionState, demoteStaleRunningStatus, fromAnthropic, fromOpenAI, getExtensionSessionState, loadSession, setExtensionSessionState, toAnthropic, toOpenAI, toWireMessages, withExtensionSession };
|
package/dist/session.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { h as toWireMessages, i as autoDetectAndConvert, l as fromAnthropic, m as toOpenAI, p as toAnthropic, u as fromOpenAI } from "./messages-
|
|
2
|
-
import { a as getExtensionSessionState, c as withExtensionSession, d as createFileMapStore, i as demoteStaleRunningStatus, l as createRemoteStore, n as currentExtensionSession, o as loadSession, r as deleteExtensionSessionState, s as setExtensionSessionState, t as createSession, u as createMemoryStore } from "./session-
|
|
1
|
+
import { h as toWireMessages, i as autoDetectAndConvert, l as fromAnthropic, m as toOpenAI, p as toAnthropic, u as fromOpenAI } from "./messages-CvHfYB7f.js";
|
|
2
|
+
import { a as getExtensionSessionState, c as withExtensionSession, d as createFileMapStore, i as demoteStaleRunningStatus, l as createRemoteStore, n as currentExtensionSession, o as loadSession, r as deleteExtensionSessionState, s as setExtensionSessionState, t as createSession, u as createMemoryStore } from "./session-DzrXKLw-.js";
|
|
3
3
|
export { autoDetectAndConvert, createFileMapStore, createMemoryStore, createRemoteStore, createSession, currentExtensionSession, deleteExtensionSessionState, demoteStaleRunningStatus, fromAnthropic, fromOpenAI, getExtensionSessionState, loadSession, setExtensionSessionState, toAnthropic, toOpenAI, toWireMessages, withExtensionSession };
|
|
@@ -196,8 +196,9 @@ async function validateSkillCandidate(candidate) {
|
|
|
196
196
|
async function installSkillFromSpec(opts) {
|
|
197
197
|
const which = (opts.io ?? {}).which ?? defaultWhich;
|
|
198
198
|
const spec = parseSkillInstallSpec(opts.source);
|
|
199
|
-
const
|
|
200
|
-
|
|
199
|
+
const batchMode = opts.all === true || (opts.skills?.length ?? 0) > 0;
|
|
200
|
+
const skillSelector = opts.skill ?? (batchMode ? void 0 : spec.skill);
|
|
201
|
+
if (!batchMode && skillSelector !== void 0 && (opts.preferSkillsCli ?? true) && which("skills")) return await installViaSkillsCli(spec, skillSelector, opts);
|
|
201
202
|
return await installNatively(spec, skillSelector, opts);
|
|
202
203
|
}
|
|
203
204
|
/**
|
|
@@ -416,4 +417,4 @@ function defineSkill(config) {
|
|
|
416
417
|
//#endregion
|
|
417
418
|
export { listSkillCandidates as a, installSkillFromSpec as i, SKILL_PROVENANCE_FILE as n, parseSkillInstallSpec as o, SkillInstallError as r, readSkillProvenance as s, defineSkill as t };
|
|
418
419
|
|
|
419
|
-
//# sourceMappingURL=skills-
|
|
420
|
+
//# sourceMappingURL=skills-DKWqoaYH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills-DKWqoaYH.js","names":[],"sources":["../src/skills/install.ts","../src/skills/index.ts"],"sourcesContent":["/**\n * Skill installation — fetch a skill (or a collection of skills) from\n * GitHub / npm / skills.sh and land it under the zidane skills root,\n * where the existing discovery scan picks it up.\n *\n * Built on the shared install layer (`src/install/source.ts`), so the\n * spec grammar, transports, download caps, and subdir containment are\n * byte-identical with the extensions installer. What differs:\n *\n * - **Validation never executes code.** A skill is data: candidates\n * are validated by parsing `SKILL.md` (lenient parser, same one\n * discovery uses) — unlike extensions, whose validation must import\n * the module. Installing a hostile skill can plant instructions,\n * never run a process.\n * - **skills.sh interop, two-way.** The spec grammar accepts pasted\n * `https://skills.sh/<owner>/<repo>[/<skill>]` catalog URLs (mapped\n * to the GitHub repo + a skill selector — skills.sh uses GitHub as\n * its registry). And when the `skills` CLI is on PATH,\n * {@link installSkillFromSpec} delegates to it (`skills add … -y`)\n * so installs land in the user's cross-agent `.agents/skills/`\n * layout and feed the skills.sh lockfile/leaderboard; without the\n * CLI the native fetch path installs the same repos directly.\n * - **Target roots**: `~/.zidane/skills/<name>` (user) or\n * `<gitRoot>/.zidane/skills/<name>` (project) — both already on\n * discovery's default scan paths.\n *\n * Same hardening as extensions: staged `mkdtemp` fetch, physical\n * symlink containment for `:subdir`, folder-safe names, overwrite with\n * backup/rollback, and a provenance file (`.zidane-install.json`) so a\n * future update flow can replay the spec.\n */\n\nimport type { InstallIo, InstallScope, InstallSource } from '../install/source'\nimport { cpSync, existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { basename, join, resolve } from 'node:path'\nimport { projectUserPaths } from '../chat/project-user-paths'\nimport { errorMessage } from '../errors'\nimport { defaultExec, defaultWhich, fetchInstallSource, InstallSourceError, lastLine, parseInstallSource, resolveSubdirRoot } from '../install/source'\nimport { parseSkillFile } from './discovery'\nimport { validateSkillName } from './validate'\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nexport type SkillInstallErrorCode\n = | 'invalid-source'\n | 'fetch-failed'\n | 'no-skill'\n | 'multiple-candidates'\n | 'validate-failed'\n | 'exists'\n | 'cli-failed'\n | 'io-failed'\n\nexport class SkillInstallError extends InstallSourceError {\n declare readonly code: SkillInstallErrorCode\n /**\n * Frontmatter metadata per candidate, parallel to `candidates` — set on\n * the selector-less `multiple-candidates` error so host pickers can show\n * descriptions without re-fetching the source. Best-effort: candidates\n * whose SKILL.md fails to parse appear with no description.\n */\n readonly details?: readonly SkillCandidateDetail[]\n constructor(code: SkillInstallErrorCode, message: string, candidates?: readonly string[], details?: readonly SkillCandidateDetail[]) {\n super(code, message, candidates)\n this.name = 'SkillInstallError'\n if (details)\n this.details = details\n }\n}\n\n/** One pickable candidate surfaced to host UIs — see {@link SkillInstallError.details}. */\nexport interface SkillCandidateDetail {\n /** Root-relative path — the unambiguous selector (`skills` option). */\n rel: string\n /** Frontmatter description, when the candidate's SKILL.md parses. */\n description?: string\n}\n\nfunction rethrowAsSkillError(err: unknown): never {\n if (err instanceof InstallSourceError && !(err instanceof SkillInstallError))\n throw new SkillInstallError(err.code as SkillInstallErrorCode, err.message, err.candidates)\n throw err\n}\n\n// ---------------------------------------------------------------------------\n// Spec parsing (shared grammar + the skills.sh catalog URL form)\n// ---------------------------------------------------------------------------\n\nconst SKILLS_SH_URL_RE = /^(?:https?:\\/\\/)?(?:www\\.)?skills\\.sh\\/([\\w.-]+)\\/([\\w.-]+)(?:\\/([\\w.-]+))?\\/?$/\n\n/**\n * Pasted install commands, as printed by skill READMEs and skills.sh\n * pages — `npx skills add owner/repo`, `bunx skills add …`,\n * `pnpm dlx skills add … --skill x`, or a bare `skills add …`. The\n * runner + `add` verb are stripped and the remaining tokens re-enter\n * the normal grammar, so users can copy a whole command into the\n * install input verbatim.\n */\nconst SKILLS_CLI_COMMAND_RE = /^(?:(?:npx|bunx|yarn\\s+dlx|pnpm\\s+dlx)\\s+)?skills(?:@\\S+)?\\s+add\\s+(\\S.*)$/i\n\nexport interface SkillInstallSpec {\n source: InstallSource\n /**\n * Skill name selector for multi-skill sources. From a skills.sh URL's\n * third segment or the caller's explicit pick after a\n * `multiple-candidates` error.\n */\n skill?: string\n /**\n * The spec string to hand the `skills` CLI (its `add` argument).\n * skills.sh URLs pass through verbatim — the CLI understands them —\n * while parsed shorthand re-serializes to `owner/repo`.\n */\n cliSpec: string\n}\n\n/**\n * Parse a raw skill source. Accepts everything the shared grammar does\n * (`owner/repo[#ref][:subdir]`, GitHub URLs, `pkg@version`, …) plus\n * pasted skills.sh catalog URLs — `skills.sh/vercel-labs/agent-skills/\n * react-best-practices` maps to the backing GitHub repo with the third\n * segment as the skill selector.\n */\nexport function parseSkillInstallSpec(raw: string): SkillInstallSpec {\n const spec = raw.trim()\n const cliCommand = SKILLS_CLI_COMMAND_RE.exec(spec)\n if (cliCommand)\n return parseCliAddCommand(cliCommand[1] as string)\n const skillsSh = SKILLS_SH_URL_RE.exec(spec)\n if (skillsSh) {\n return {\n source: { kind: 'github', owner: skillsSh[1] as string, repo: skillsSh[2] as string },\n ...(skillsSh[3] ? { skill: skillsSh[3] } : {}),\n cliSpec: `${skillsSh[1]}/${skillsSh[2]}`,\n }\n }\n try {\n const source = parseInstallSource(spec)\n return { source, cliSpec: spec }\n }\n catch (err) {\n rethrowAsSkillError(err)\n }\n}\n\n/**\n * Parse the argument tail of a pasted `skills add …` command. Honors\n * `--skill <name>` / `--skill=<name>` (their per-skill selector — an\n * explicit selector in the URL/shorthand form wins over it), ignores\n * flags that are the dialog's job here (`-y`, `-g`, `--global`, …),\n * and routes the first non-flag token back through\n * {@link parseSkillInstallSpec} so URLs and shorthands inside the\n * command parse identically to a bare paste.\n */\nfunction parseCliAddCommand(rest: string): SkillInstallSpec {\n const tokens = rest.split(/\\s+/).filter(t => t.length > 0)\n let sourceToken: string | undefined\n let skill: string | undefined\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i] as string\n if (token === '--skill' || token === '-s') {\n skill = tokens[i + 1]\n i += 1\n continue\n }\n const inline = /^--skill=(.+)$/.exec(token)\n if (inline) {\n skill = inline[1] as string\n continue\n }\n if (token.startsWith('-'))\n continue\n if (sourceToken === undefined)\n sourceToken = token\n }\n if (sourceToken === undefined)\n throw new SkillInstallError('invalid-source', 'Pasted skills command has no source — expected “skills add <owner/repo>”.')\n const parsed = parseSkillInstallSpec(sourceToken)\n return { ...parsed, ...(skill !== undefined && parsed.skill === undefined ? { skill } : {}) }\n}\n\n// ---------------------------------------------------------------------------\n// Candidate enumeration + validation (data-only — no code execution)\n// ---------------------------------------------------------------------------\n\nexport interface SkillInstallCandidate {\n /** Directory name candidate (validated against SKILL.md frontmatter later). */\n name: string\n dir: string\n /**\n * Root-relative path (`engineering/tdd`) — the unambiguous selector\n * when the same skill NAME exists in several category directories.\n */\n rel: string\n}\n\n/**\n * How deep the candidate walk descends. Real-world collections nest\n * skills under category directories (`skills/engineering/tdd/SKILL.md`\n * in mattpocock/skills; `.github/skills/…` elsewhere) — one-level\n * enumeration missed all of them. Four levels covers every observed\n * layout while keeping the walk trivially bounded on the staged tree.\n */\nconst CANDIDATE_WALK_MAX_DEPTH = 4\n\n/**\n * List installable skills inside a fetched tree.\n *\n * A skill is ANY directory containing a `SKILL.md` — found by a\n * bounded recursive walk (depth {@link CANDIDATE_WALK_MAX_DEPTH}),\n * skipping dot-dirs and `node_modules`, and never descending INTO a\n * skill (its `references/` / `scripts/` subtree ships with it, it\n * doesn't nest further candidates). A root-level `SKILL.md` means\n * \"repo IS the skill\" and short-circuits, named from the fallback.\n *\n * Candidates carry their root-relative path (`rel`) so same-named\n * skills in different categories stay individually addressable.\n * Sorted by `rel`.\n */\nexport function listSkillCandidates(dir: string, fallbackName: string): SkillInstallCandidate[] {\n if (existsSync(join(dir, 'SKILL.md')))\n return [{ name: sanitizeName(fallbackName), dir, rel: '.' }]\n const out: SkillInstallCandidate[] = []\n const walk = (current: string, rel: string, depth: number): void => {\n if (depth > CANDIDATE_WALK_MAX_DEPTH)\n return\n for (const entry of readdirSync(current, { withFileTypes: true })) {\n if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === 'node_modules')\n continue\n const childDir = join(current, entry.name)\n const childRel = rel.length === 0 ? entry.name : `${rel}/${entry.name}`\n if (existsSync(join(childDir, 'SKILL.md')))\n out.push({ name: entry.name, dir: childDir, rel: childRel })\n else\n walk(childDir, childRel, depth + 1)\n }\n }\n walk(dir, '', 1)\n return out.sort((a, b) => a.rel.localeCompare(b.rel))\n}\n\n/**\n * Frontmatter details for a host picker, parallel to the candidate list.\n * Parsing is best-effort per candidate — a malformed SKILL.md yields a\n * detail row without a description rather than failing the enumeration.\n */\nasync function candidateDetails(candidates: readonly SkillInstallCandidate[]): Promise<SkillCandidateDetail[]> {\n return Promise.all(candidates.map(async (c) => {\n const parsed = await parseSkillFile(join(c.dir, 'SKILL.md')).catch(() => null)\n const description = parsed?.description.trim()\n return { rel: c.rel, ...(description ? { description } : {}) }\n }))\n}\n\n/** Human-readable candidate list for error messages — capped, rel-qualified when ambiguous. */\nfunction describeCandidates(candidates: readonly SkillInstallCandidate[]): string {\n const nameCounts = new Map<string, number>()\n for (const c of candidates)\n nameCounts.set(c.name, (nameCounts.get(c.name) ?? 0) + 1)\n const labels = candidates.map(c => ((nameCounts.get(c.name) ?? 0) > 1 ? c.rel : c.name))\n const shown = labels.slice(0, 20)\n const more = labels.length - shown.length\n return shown.join(', ') + (more > 0 ? `, … and ${more} more` : '')\n}\n\n/**\n * Validate a staged candidate by PARSING its SKILL.md — the same lenient\n * parser discovery runs, so \"installs cleanly\" and \"discovers cleanly\"\n * are the same predicate. Returns the frontmatter name (which wins over\n * the directory-derived candidate name for the target folder).\n */\nasync function validateSkillCandidate(candidate: SkillInstallCandidate): Promise<{ name: string }> {\n const parsed = await parseSkillFile(join(candidate.dir, 'SKILL.md'))\n if (!parsed)\n throw new SkillInstallError('validate-failed', `${candidate.dir}/SKILL.md is not a valid skill (missing name or description in frontmatter).`)\n const name = parsed.name\n if (!validateSkillName(name))\n throw new SkillInstallError('validate-failed', `Skill name \"${name}\" is invalid (lowercase alphanumerics + single hyphens, max 64 chars).`)\n return { name }\n}\n\n// ---------------------------------------------------------------------------\n// Install\n// ---------------------------------------------------------------------------\n\nexport interface SkillInstallOptions {\n /** Raw source spec — see {@link parseSkillInstallSpec} for the grammar. */\n source: string\n scope: InstallScope\n /** Pick one skill from a multi-skill source (names from the `multiple-candidates` error). */\n skill?: string\n /**\n * Install EVERY skill a multi-skill source contains (the affirmative\n * answer to the host's \"import all?\" prompt after a\n * `multiple-candidates` error). Native engine only — already-installed\n * names are skipped (reported via `SkillInstallResult.skipped`) rather\n * than failing the batch, unless `overwrite` is also set. Overrides a\n * selector embedded in the source string; ignored when an explicit\n * `skill` option is present.\n */\n all?: boolean\n /**\n * Install a picked SUBSET of a multi-skill source — the host's checkbox\n * picker over the `multiple-candidates` list. Entries match a skill's\n * name or its root-relative path (same rules as `skill`); duplicates\n * collapse. Batch semantics mirror `all` (skip existing, pre-validate,\n * native engine). Takes precedence over `all` and over a selector\n * embedded in the source string; ignored when an explicit `skill`\n * option is present.\n */\n skills?: readonly string[]\n /** Overwrite an existing install of the same name. */\n overwrite?: boolean\n cwd?: string\n home?: string\n /** Storage prefix (default `.zidane`) — mirrors `ResolvedConfig.prefix`. */\n prefix?: string\n io?: InstallIo\n onProgress?: (message: string) => void\n /**\n * Prefer the skills.sh CLI when `skills` is on PATH (default true).\n * The CLI owns scope + layout decisions (`.agents/skills/`, their\n * lockfile); the native path is the fallback and the forced path in\n * tests. Set false to always install natively.\n */\n preferSkillsCli?: boolean\n}\n\nexport interface SkillInstallResult {\n /** Final skill name(s) installed. The CLI path reports the spec, not names. */\n installed: readonly string[]\n /** Names skipped during an `all` import because they were already installed. */\n skipped?: readonly string[]\n /** Candidate paths excluded from an `all` import because they failed validation. */\n failed?: readonly string[]\n /** Absolute target directory (native path only — the CLI owns its layout). */\n dir?: string\n /** `skills-cli` when delegated to the skills.sh CLI, else `native`. */\n engine: 'skills-cli' | 'native'\n provenance?: string\n}\n\n/**\n * Install a skill (or one skill out of a multi-skill source).\n *\n * Engine selection: targeted installs (an explicit skill selector) go\n * through the skills.sh CLI when present on PATH (unless\n * `preferSkillsCli: false`) — keeping their cross-agent layout and\n * lockfile — else the native fetch pipeline. Selector-less installs are\n * ALWAYS native: only native enumeration can tell single- from\n * multi-skill sources, and the host's \"import all?\" prompt rides on the\n * `multiple-candidates` error — delegating them to `skills add … -y`\n * would let the CLI's own multi-skill behavior silently decide instead.\n * `all: true` batches are native for the same reason (skip-existing +\n * per-skill provenance are our semantics).\n */\nexport async function installSkillFromSpec(opts: SkillInstallOptions): Promise<SkillInstallResult> {\n const io = opts.io ?? {}\n const which = io.which ?? defaultWhich\n const spec = parseSkillInstallSpec(opts.source)\n\n // Batch modes (`all` / `skills` subset) are always native — see JSDoc.\n // They also override a selector derived from the SOURCE string (a\n // skills.sh URL's third segment, a pasted `--skill`): the host's picker\n // re-runs with the same source after an ambiguous-selector error, and\n // keeping the spec selector would just re-throw the same error in a\n // loop. An explicit `opts.skill` still wins over everything.\n const batchMode = opts.all === true || (opts.skills?.length ?? 0) > 0\n const skillSelector = opts.skill ?? (batchMode ? undefined : spec.skill)\n if (!batchMode && skillSelector !== undefined && (opts.preferSkillsCli ?? true) && which('skills'))\n return await installViaSkillsCli(spec, skillSelector, opts)\n\n return await installNatively(spec, skillSelector, opts)\n}\n\n/**\n * Delegate to the skills.sh CLI. Non-interactive by construction: `-y`\n * skips confirmation prompts, `-g` maps our `user` scope to their\n * global scope (project scope is their default, keyed off cwd). The CLI\n * decides the on-disk layout (`.agents/skills/`), which zidane's\n * discovery already scans.\n */\nasync function installViaSkillsCli(\n spec: SkillInstallSpec,\n skillSelector: string | undefined,\n opts: SkillInstallOptions,\n): Promise<SkillInstallResult> {\n const exec = opts.io?.exec ?? defaultExec\n const args = ['add', spec.cliSpec, '-y']\n if (skillSelector)\n args.push('--skill', skillSelector)\n if (opts.scope === 'user')\n args.push('-g')\n opts.onProgress?.(`skills add ${spec.cliSpec}${skillSelector ? ` --skill ${skillSelector}` : ''}…`)\n const res = await exec('skills', args, { cwd: opts.cwd ?? process.cwd() })\n if (!res.ok)\n throw new SkillInstallError('cli-failed', `skills.sh CLI failed: ${lastLine(res.stderr) || lastLine(res.stdout) || 'unknown error'}`)\n return {\n installed: [skillSelector ?? spec.cliSpec],\n engine: 'skills-cli',\n }\n}\n\nasync function installNatively(\n spec: SkillInstallSpec,\n skillSelector: string | undefined,\n opts: SkillInstallOptions,\n): Promise<SkillInstallResult> {\n const io = opts.io ?? {}\n const source = spec.source\n const stage = mkdtempSync(join(tmpdir(), 'zidane-skill-install-'))\n try {\n const fetchedInto = join(stage, 'fetched')\n mkdirSync(fetchedInto, { recursive: true })\n let provenance: string\n try {\n provenance = (await fetchInstallSource(source, fetchedInto, io, opts.onProgress)).provenance\n }\n catch (err) {\n rethrowAsSkillError(err)\n }\n\n // `:subdir` re-roots the search — same physical containment as\n // extensions (hostile symlinks must not walk out of the stage).\n let candidateRoot = fetchedInto\n let fallbackName = source.kind === 'github' ? source.repo : source.pkg\n if (source.kind === 'github' && source.subdir) {\n const rooted = resolveSubdirRoot(fetchedInto, source.subdir)\n if (!rooted.ok) {\n if (rooted.reason === 'missing')\n throw new SkillInstallError('no-skill', `\"${source.subdir}\" is not a directory in ${source.owner}/${source.repo}.`)\n throw new SkillInstallError('invalid-source', `Subdirectory \"${source.subdir}\" escapes the repository root${rooted.reason === 'escape-symlink' ? ' through a symlink' : ''}.`)\n }\n candidateRoot = rooted.dir\n fallbackName = basename(resolve(fetchedInto, source.subdir))\n }\n\n const candidates = listSkillCandidates(candidateRoot, fallbackName)\n if (candidates.length === 0)\n throw new SkillInstallError('no-skill', `No skill found in ${opts.source} — no SKILL.md anywhere in the tree (searched ${CANDIDATE_WALK_MAX_DEPTH} levels deep).`)\n let picked: SkillInstallCandidate[]\n if (!skillSelector && opts.skills && opts.skills.length > 0) {\n // Host-picked subset (checkbox picker over the `multiple-candidates`\n // list). Each entry resolves with the same name-or-rel-path rules as\n // the single selector; duplicate picks collapse to one install.\n const byRel = new Map<string, SkillInstallCandidate>()\n for (const sel of opts.skills) {\n const matches = candidates.filter(c => c.name === sel || c.rel === sel)\n if (matches.length === 0)\n throw new SkillInstallError('no-skill', `No skill named \"${sel}\" in ${opts.source}. Available: ${describeCandidates(candidates)}`, candidates.map(c => c.rel))\n if (matches.length > 1)\n throw new SkillInstallError('multiple-candidates', `\"${sel}\" is ambiguous in ${opts.source} — pick a path: ${matches.map(c => c.rel).join(', ')}`, matches.map(c => c.rel))\n const match = matches[0] as SkillInstallCandidate\n byRel.set(match.rel, match)\n }\n picked = [...byRel.values()]\n }\n else if (skillSelector) {\n // Selector matches the skill name OR its root-relative path — the\n // path form disambiguates same-named skills in different category\n // directories (`engineering/tdd` vs `personal/tdd`).\n const matches = candidates.filter(c => c.name === skillSelector || c.rel === skillSelector)\n if (matches.length === 0)\n throw new SkillInstallError('no-skill', `No skill named \"${skillSelector}\" in ${opts.source}. Available: ${describeCandidates(candidates)}`, candidates.map(c => c.rel))\n if (matches.length > 1)\n throw new SkillInstallError('multiple-candidates', `\"${skillSelector}\" is ambiguous in ${opts.source} — pick a path: ${matches.map(c => c.rel).join(', ')}`, matches.map(c => c.rel))\n picked = [matches[0] as SkillInstallCandidate]\n }\n else if (candidates.length > 1 && !opts.all) {\n throw new SkillInstallError(\n 'multiple-candidates',\n `${opts.source} contains ${candidates.length} skills — pick one: ${describeCandidates(candidates)}`,\n candidates.map(c => c.rel),\n await candidateDetails(candidates),\n )\n }\n else {\n picked = [...candidates]\n }\n\n const root = scopeRoot(opts)\n const installed: string[] = []\n const skipped: string[] = []\n const failed: string[] = []\n let lastDir: string | undefined\n const batch = picked.length > 1\n\n // Validate the whole batch BEFORE installing anything — one malformed\n // candidate must not abort a half-applied import. Batch mode collects\n // failures (reported via `SkillInstallResult.failed`) and installs the\n // rest; a targeted single install keeps the hard throw.\n const resolved: Array<{ candidate: SkillInstallCandidate, name: string }> = []\n // Intra-batch name conflicts: two candidates whose frontmatter\n // validates to the same name (`engineering/tdd` + `personal/tdd`)\n // would otherwise install the first and mislabel the second as\n // \"already installed\" (or, with overwrite, silently last-wins).\n // First occurrence wins; later ones are reported as conflicts.\n const nameToRel = new Map<string, string>()\n for (const candidate of picked) {\n try {\n // Frontmatter name wins over the directory-derived candidate name —\n // mirrors \"target folder is named from the VALIDATED definition\".\n const { name } = await validateSkillCandidate(candidate)\n const conflict = nameToRel.get(name)\n if (conflict !== undefined) {\n // Only reachable in batch mode — a single pick can't conflict.\n failed.push(candidate.rel)\n opts.onProgress?.(`skipping \"${candidate.rel}\" — name \"${name}\" conflicts with \"${conflict}\"`)\n continue\n }\n nameToRel.set(name, candidate.rel)\n resolved.push({ candidate, name })\n }\n catch (err) {\n if (!batch)\n throw err\n failed.push(candidate.rel)\n opts.onProgress?.(`skipping \"${candidate.rel}\" — ${errorMessage(err)}`)\n }\n }\n\n for (const { candidate, name } of resolved) {\n const targetDir = join(root, name)\n const backupDir = `${targetDir}.zidane-backup`\n if (existsSync(targetDir) && !opts.overwrite) {\n // Batch imports skip collisions (already-installed skills are the\n // common case when re-importing a repo); a targeted single install\n // keeps the hard error so the overwrite decision stays explicit.\n if (batch) {\n skipped.push(name)\n opts.onProgress?.(`skipping \"${name}\" — already installed`)\n continue\n }\n throw new SkillInstallError('exists', `Skill \"${name}\" is already installed at ${targetDir}. Overwrite to replace it.`)\n }\n\n mkdirSync(root, { recursive: true })\n const hadPrevious = existsSync(targetDir)\n if (hadPrevious) {\n rmSync(backupDir, { recursive: true, force: true })\n renameSync(targetDir, backupDir)\n }\n try {\n // `dereference` materializes in-tree symlinked files so the install\n // has no dangling links once the stage is deleted.\n cpSync(candidate.dir, targetDir, { recursive: true, dereference: true })\n writeProvenance(targetDir, { spec: opts.source, ...(skillSelector ? { skill: skillSelector } : {}), scope: opts.scope, provenance, installedAt: Date.now() })\n }\n catch (err) {\n rmSync(targetDir, { recursive: true, force: true })\n if (hadPrevious)\n renameSync(backupDir, targetDir)\n throw new SkillInstallError('io-failed', `Writing ${targetDir} failed: ${errorMessage(err)}`)\n }\n rmSync(backupDir, { recursive: true, force: true })\n installed.push(name)\n lastDir = targetDir\n if (batch)\n opts.onProgress?.(`installed \"${name}\" (${installed.length}/${resolved.length})`)\n }\n\n if (installed.length === 0 && skipped.length > 0)\n throw new SkillInstallError('exists', `All ${skipped.length} installable skills from ${opts.source} are already installed: ${skipped.join(', ')}. Overwrite to replace them.`)\n if (installed.length === 0 && failed.length > 0)\n throw new SkillInstallError('validate-failed', `No skill from ${opts.source} was installable — invalid: ${failed.join(', ')}.`)\n\n return {\n installed,\n ...(skipped.length > 0 ? { skipped } : {}),\n ...(failed.length > 0 ? { failed } : {}),\n ...(lastDir !== undefined ? { dir: lastDir } : {}),\n engine: 'native',\n provenance,\n }\n }\n finally {\n rmSync(stage, { recursive: true, force: true })\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Provenance sidecar — lets a future update flow replay the recorded spec. */\nexport const SKILL_PROVENANCE_FILE = '.zidane-install.json'\n\ninterface SkillProvenance {\n spec: string\n skill?: string\n scope: InstallScope\n provenance: string\n installedAt: number\n}\n\nfunction writeProvenance(dir: string, data: SkillProvenance): void {\n writeFileSync(join(dir, SKILL_PROVENANCE_FILE), `${JSON.stringify(data, null, 2)}\\n`)\n}\n\n/** Read a skill directory's install provenance, `null` for hand-authored skills. */\nexport function readSkillProvenance(dir: string): (SkillProvenance & { spec: string }) | null {\n try {\n const raw = JSON.parse(readFileSync(join(dir, SKILL_PROVENANCE_FILE), 'utf8')) as Partial<SkillProvenance>\n return typeof raw.spec === 'string' && raw.spec.length > 0 ? (raw as SkillProvenance) : null\n }\n catch {\n return null\n }\n}\n\n/**\n * Native-install target root: the `<prefix>/skills` flavor of the\n * project/user convention (`~/.zidane/skills`, `<gitRoot>/.zidane/skills`).\n * The `.agents/skills` siblings stay read-only for us — they belong to\n * the cross-agent ecosystem (and the skills.sh CLI writes there).\n */\nfunction scopeRoot(opts: SkillInstallOptions): string {\n const roots = projectUserPaths({\n subPath: 'skills',\n ...(opts.cwd ? { cwd: opts.cwd } : {}),\n ...(opts.home ? { home: opts.home } : {}),\n ...(opts.prefix ? { prefix: opts.prefix } : {}),\n })\n const root = roots.find(r => r.source === opts.scope && r.path.includes(`.${(opts.prefix ?? '.zidane').replace(/^\\./, '')}`))\n if (!root)\n throw new SkillInstallError('io-failed', `No ${opts.scope} skills root resolved.`)\n return root.path\n}\n\nfunction sanitizeName(raw: string): string {\n const cleaned = raw.toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '').replace(/-{2,}/g, '-')\n return cleaned.length > 0 ? cleaned : 'skill'\n}\n","import type { SkillConfig } from './types'\nimport { validateSkillForWrite } from './validate'\n\nexport {\n type ActivationVia,\n type ActiveSkill,\n createSkillActivationState,\n type DeactivationReason,\n type SkillActivationState,\n type SkillActivationStateOptions,\n} from './activation'\nexport {\n IMPLICITLY_ALLOWED_SKILL_TOOLS,\n installAllowedToolsGate,\n} from './allowed-tools'\nexport { buildCatalog } from './catalog'\nexport {\n discoverSkills,\n getDefaultScanPaths,\n inferSource,\n parseFrontmatter,\n parseSkillFile,\n} from './discovery'\nexport type { SourcedScanPath } from './discovery'\nexport {\n installSkillFromSpec,\n listSkillCandidates,\n parseSkillInstallSpec,\n readSkillProvenance,\n SKILL_PROVENANCE_FILE,\n SkillInstallError,\n} from './install'\nexport type {\n SkillCandidateDetail,\n SkillInstallCandidate,\n SkillInstallErrorCode,\n SkillInstallOptions,\n SkillInstallResult,\n SkillInstallSpec,\n} from './install'\nexport { interpolateShellCommands, stripShellInterpolations } from './interpolate'\nexport type { InterpolateShellCommandsOptions } from './interpolate'\nexport { resolveSkills } from './resolve'\nexport type {\n ShellInterpolationApproval,\n SkillConfig,\n SkillDiagnostic,\n SkillResource,\n SkillsConfig,\n SkillSource,\n} from './types'\nexport {\n isToolAllowedByUnion,\n matchesAllowedTool,\n parseAllowedToolPattern,\n validateResourcePath,\n validateResourcePathReal,\n validateSkillForWrite,\n validateSkillName,\n} from './validate'\nexport type { SkillValidationIssue, SkillValidationResult } from './validate'\nexport { writeSkillsToDisk, writeSkillToDisk } from './writer'\n\n// ---------------------------------------------------------------------------\n// defineSkill helper\n// ---------------------------------------------------------------------------\n\n/**\n * Define an inline skill configuration.\n *\n * Strict-validates at author-time — throws if the config would be rejected by\n * `writeSkillToDisk` or the spec. Source defaults to `'inline'`.\n */\nexport function defineSkill(config: Omit<SkillConfig, 'source'> & { source?: SkillConfig['source'] }): SkillConfig {\n const normalized: SkillConfig = {\n ...config,\n source: config.source ?? 'inline',\n }\n const result = validateSkillForWrite(normalized)\n if (!result.valid) {\n const summary = result.errors.map(e => ` - [${e.code}] ${e.message}`).join('\\n')\n throw new Error(`Invalid skill \"${normalized.name}\":\\n${summary}`)\n }\n return normalized\n}\n"],"mappings":"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"}
|
package/dist/skills.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $s as SkillInstallResult, Bs as validateResourcePathReal, Fs as SkillValidationResult, Gs as interpolateShellCommands, Hs as validateSkillName, Is as isToolAllowedByUnion, Js as SkillCandidateDetail, Ks as stripShellInterpolations, Ls as matchesAllowedTool, Ms as writeSkillToDisk, Ns as writeSkillsToDisk, Ps as SkillValidationIssue, Qs as SkillInstallOptions, Rs as parseAllowedToolPattern, Us as resolveSkills, Vs as validateSkillForWrite, Ws as InterpolateShellCommandsOptions, Xs as SkillInstallError, Ys as SkillInstallCandidate, Zs as SkillInstallErrorCode, _c as SkillActivationState, ac as SourcedScanPath, ap as SkillConfig, cc as inferSource, cp as SkillSource, dc as buildCatalog, ec as SkillInstallSpec, fc as IMPLICITLY_ALLOWED_SKILL_TOOLS, gc as DeactivationReason, hc as ActiveSkill, ic as readSkillProvenance, ip as ShellInterpolationApproval, js as defineSkill, lc as parseFrontmatter, lp as SkillsConfig, mc as ActivationVia, nc as listSkillCandidates, oc as discoverSkills, op as SkillDiagnostic, pc as installAllowedToolsGate, qs as SKILL_PROVENANCE_FILE, rc as parseSkillInstallSpec, sc as getDefaultScanPaths, sp as SkillResource, tc as installSkillFromSpec, uc as parseSkillFile, vc as SkillActivationStateOptions, yc as createSkillActivationState, zs as validateResourcePath } from "./index-
|
|
1
|
+
import { $s as SkillInstallResult, Bs as validateResourcePathReal, Fs as SkillValidationResult, Gs as interpolateShellCommands, Hs as validateSkillName, Is as isToolAllowedByUnion, Js as SkillCandidateDetail, Ks as stripShellInterpolations, Ls as matchesAllowedTool, Ms as writeSkillToDisk, Ns as writeSkillsToDisk, Ps as SkillValidationIssue, Qs as SkillInstallOptions, Rs as parseAllowedToolPattern, Us as resolveSkills, Vs as validateSkillForWrite, Ws as InterpolateShellCommandsOptions, Xs as SkillInstallError, Ys as SkillInstallCandidate, Zs as SkillInstallErrorCode, _c as SkillActivationState, ac as SourcedScanPath, ap as SkillConfig, cc as inferSource, cp as SkillSource, dc as buildCatalog, ec as SkillInstallSpec, fc as IMPLICITLY_ALLOWED_SKILL_TOOLS, gc as DeactivationReason, hc as ActiveSkill, ic as readSkillProvenance, ip as ShellInterpolationApproval, js as defineSkill, lc as parseFrontmatter, lp as SkillsConfig, mc as ActivationVia, nc as listSkillCandidates, oc as discoverSkills, op as SkillDiagnostic, pc as installAllowedToolsGate, qs as SKILL_PROVENANCE_FILE, rc as parseSkillInstallSpec, sc as getDefaultScanPaths, sp as SkillResource, tc as installSkillFromSpec, uc as parseSkillFile, vc as SkillActivationStateOptions, yc as createSkillActivationState, zs as validateResourcePath } from "./index-l0wehkWU.js";
|
|
2
2
|
export { type ActivationVia, type ActiveSkill, type DeactivationReason, IMPLICITLY_ALLOWED_SKILL_TOOLS, type InterpolateShellCommandsOptions, SKILL_PROVENANCE_FILE, type ShellInterpolationApproval, type SkillActivationState, type SkillActivationStateOptions, type SkillCandidateDetail, type SkillConfig, type SkillDiagnostic, type SkillInstallCandidate, SkillInstallError, type SkillInstallErrorCode, type SkillInstallOptions, type SkillInstallResult, type SkillInstallSpec, type SkillResource, type SkillSource, type SkillValidationIssue, type SkillValidationResult, type SkillsConfig, type SourcedScanPath, buildCatalog, createSkillActivationState, defineSkill, discoverSkills, getDefaultScanPaths, inferSource, installAllowedToolsGate, installSkillFromSpec, interpolateShellCommands, isToolAllowedByUnion, listSkillCandidates, matchesAllowedTool, parseAllowedToolPattern, parseFrontmatter, parseSkillFile, parseSkillInstallSpec, readSkillProvenance, resolveSkills, stripShellInterpolations, validateResourcePath, validateResourcePathReal, validateSkillForWrite, validateSkillName, writeSkillToDisk, writeSkillsToDisk };
|
package/dist/skills.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { A as validateSkillName, C as stripShellInterpolations, D as validateResourcePath, E as parseAllowedToolPattern, O as validateResourcePathReal, S as interpolateShellCommands, T as matchesAllowedTool, a as getDefaultScanPaths, c as parseSkillFile, d as installAllowedToolsGate, f as createSkillActivationState, i as discoverSkills, k as validateSkillForWrite, l as buildCatalog, n as writeSkillToDisk, o as inferSource, r as writeSkillsToDisk, s as parseFrontmatter, t as resolveSkills, u as IMPLICITLY_ALLOWED_SKILL_TOOLS, w as isToolAllowedByUnion } from "./resolve-HtxmBWs9.js";
|
|
2
|
-
import { a as listSkillCandidates, i as installSkillFromSpec, n as SKILL_PROVENANCE_FILE, o as parseSkillInstallSpec, r as SkillInstallError, s as readSkillProvenance, t as defineSkill } from "./skills-
|
|
2
|
+
import { a as listSkillCandidates, i as installSkillFromSpec, n as SKILL_PROVENANCE_FILE, o as parseSkillInstallSpec, r as SkillInstallError, s as readSkillProvenance, t as defineSkill } from "./skills-DKWqoaYH.js";
|
|
3
3
|
export { IMPLICITLY_ALLOWED_SKILL_TOOLS, SKILL_PROVENANCE_FILE, SkillInstallError, buildCatalog, createSkillActivationState, defineSkill, discoverSkills, getDefaultScanPaths, inferSource, installAllowedToolsGate, installSkillFromSpec, interpolateShellCommands, isToolAllowedByUnion, listSkillCandidates, matchesAllowedTool, parseAllowedToolPattern, parseFrontmatter, parseSkillFile, parseSkillInstallSpec, readSkillProvenance, resolveSkills, stripShellInterpolations, validateResourcePath, validateResourcePathReal, validateSkillForWrite, validateSkillName, writeSkillToDisk, writeSkillsToDisk };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $u as ProviderKey, Bu as StreamEvent, Qu as ProviderAuth, rd as ModelInfo, zu as Settings } from "./index-
|
|
1
|
+
import { $u as ProviderKey, Bu as StreamEvent, Qu as ProviderAuth, rd as ModelInfo, zu as Settings } from "./index-l0wehkWU.js";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
3
|
|
|
4
4
|
//#region src/chat/turn-selection.d.ts
|
|
@@ -432,4 +432,4 @@ declare function displayNameFor(name: string, input?: Record<string, unknown>):
|
|
|
432
432
|
declare function formatToolCall(name: string, input: Record<string, unknown>): ToolFormatLine | null;
|
|
433
433
|
//#endregion
|
|
434
434
|
export { turnSelectionOwnership as A, useSafeModeActions as C, isTurnHighlighted as D, isEditErrorResult as E, isVisible as O, SafeModeProvider as S, EDIT_TOOL_NAMES as T, splitMarkdownCodeBlocks as _, formatToolCall as a, RequestApproval as b, splitPromptSegments as c, filterModelCatalog as d, indexOfEntry as f, normalizeMarkdownCodeFences as g, chunkMarkdownForRender as h, displayNameFor as i, selectableTurnIds as k, CatalogEntry as l, MarkdownSegment as m, ToolDisplayMeta as n, PromptSegment as o, MARKDOWN_RENDER_CHUNK_TARGET_BYTES as p, ToolFormatLine as r, PromptSegmentRef as s, TOOL_DISPLAY as t, buildModelCatalog as u, ApprovalDecision as v, useSafeModeQueue as w, SafeModeActions as x, ApprovalRequest as y };
|
|
435
|
-
//# sourceMappingURL=tool-formatters-
|
|
435
|
+
//# sourceMappingURL=tool-formatters-BSwVkSX2.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-formatters-
|
|
1
|
+
{"version":3,"file":"tool-formatters-BSwVkSX2.d.ts","names":[],"sources":["../src/chat/turn-selection.ts","../src/chat/safe-mode-context.tsx","../src/chat/markdown-segments.ts","../src/chat/model-catalog.ts","../src/chat/prompt-segments.ts","../src/chat/tool-formatters.ts"],"mappings":"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"}
|
package/dist/tools.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $f as hashContent, $n as waitTask, As as matchModelEntries, Cr as glob, Cs as SubagentOverride, Ds as SpawnModelEntry, Es as isSpawnTool, Jf as ToolMap, Kf as ToolContext, Os as createModelSearchTool, Qf as getReadState, Qn as WAIT_TASK_TIMED_OUT_PREFIX, Sr as grep, Ss as SubagentDef, Ts as createSpawnTool, Xf as ReadStateEntry, Yf as HydrateReadStateOptions, Zf as ReadStateMap, Zn as writeFile, _r as readFile, _s as SpawnTaskInput, ar as ToolSearchToolOptions, br as InteractionToolOptions, bs as SpawnToolOptions, cr as createSkillsUseTool, dr as SkillsReadToolOptions, ep as hydrateReadStateFromSession, er as ValidationResult, fr as createSkillsReadTool, gr as shell, gs as ExtensionSubagentDef, hr as createShellTool, hs as ChildAgent, ir as LazyToolEntry, ks as formatModelLine, lr as SkillsRunScriptToolOptions, mr as CreateShellToolOptions, np as readStateKey, nr as TailTruncateOptions, or as createToolSearchTool, pr as shellKill, qf as ToolDef, rp as resolveReadStateMap, rr as tailTruncate, sr as SkillsUseToolOptions, tp as hydrateReadStateFromSessionAsync, tr as validateToolArgs, ur as createSkillsRunScriptTool, vr as multiEdit, vs as SpawnTaskOutcome, wr as edit, ws as SubagentRegistry, xr as createInteractionTool, xs as SpawnToolState, yr as listFiles, ys as SpawnTool } from "./index-
|
|
1
|
+
import { $f as hashContent, $n as waitTask, As as matchModelEntries, Cr as glob, Cs as SubagentOverride, Ds as SpawnModelEntry, Es as isSpawnTool, Jf as ToolMap, Kf as ToolContext, Os as createModelSearchTool, Qf as getReadState, Qn as WAIT_TASK_TIMED_OUT_PREFIX, Sr as grep, Ss as SubagentDef, Ts as createSpawnTool, Xf as ReadStateEntry, Yf as HydrateReadStateOptions, Zf as ReadStateMap, Zn as writeFile, _r as readFile, _s as SpawnTaskInput, ar as ToolSearchToolOptions, br as InteractionToolOptions, bs as SpawnToolOptions, cr as createSkillsUseTool, dr as SkillsReadToolOptions, ep as hydrateReadStateFromSession, er as ValidationResult, fr as createSkillsReadTool, gr as shell, gs as ExtensionSubagentDef, hr as createShellTool, hs as ChildAgent, ir as LazyToolEntry, ks as formatModelLine, lr as SkillsRunScriptToolOptions, mr as CreateShellToolOptions, np as readStateKey, nr as TailTruncateOptions, or as createToolSearchTool, pr as shellKill, qf as ToolDef, rp as resolveReadStateMap, rr as tailTruncate, sr as SkillsUseToolOptions, tp as hydrateReadStateFromSessionAsync, tr as validateToolArgs, ur as createSkillsRunScriptTool, vr as multiEdit, vs as SpawnTaskOutcome, wr as edit, ws as SubagentRegistry, xr as createInteractionTool, xs as SpawnToolState, yr as listFiles, ys as SpawnTool } from "./index-l0wehkWU.js";
|
|
2
2
|
export { type ChildAgent, type CreateShellToolOptions, type ExtensionSubagentDef, type HydrateReadStateOptions, type InteractionToolOptions, type LazyToolEntry, type ReadStateEntry, type ReadStateMap, type SkillsReadToolOptions, type SkillsRunScriptToolOptions, type SkillsUseToolOptions, type SpawnModelEntry, type SpawnTaskInput, type SpawnTaskOutcome, type SpawnTool, type SpawnToolOptions, type SpawnToolState, type SubagentDef, type SubagentOverride, type SubagentRegistry, type TailTruncateOptions, type ToolContext, type ToolDef, type ToolMap, type ToolSearchToolOptions, type ValidationResult, WAIT_TASK_TIMED_OUT_PREFIX, createInteractionTool, createModelSearchTool, createShellTool, createSkillsReadTool, createSkillsRunScriptTool, createSkillsUseTool, createSpawnTool, createToolSearchTool, edit, formatModelLine, getReadState, glob, grep, hashContent, hydrateReadStateFromSession, hydrateReadStateFromSessionAsync, isSpawnTool, listFiles, matchModelEntries, multiEdit, readFile, readStateKey, resolveReadStateMap, shell, shellKill, tailTruncate, validateToolArgs, waitTask, writeFile };
|
package/dist/tools.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as hydrateReadStateFromSessionAsync, c as resolveReadStateMap, i as hydrateReadStateFromSession, r as hashContent, s as readStateKey, t as getReadState } from "./read-state-
|
|
2
|
-
import { At as createInteractionTool, Ct as readFile, Dt as formatModelLine, Et as createModelSearchTool, Mt as glob, Nt as edit, Ot as matchModelEntries, Pt as validateToolArgs, St as tailTruncate, Tt as multiEdit, _t as createSkillsRunScriptTool, bt as createShellTool, dt as WAIT_TASK_TIMED_OUT_PREFIX, ft as waitTask, gt as createSkillsUseTool, ht as isSpawnTool, jt as grep, kt as listFiles, mt as createSpawnTool, pt as createToolSearchTool, ut as writeFile, vt as createSkillsReadTool, xt as shell, yt as shellKill } from "./agent-
|
|
1
|
+
import { a as hydrateReadStateFromSessionAsync, c as resolveReadStateMap, i as hydrateReadStateFromSession, r as hashContent, s as readStateKey, t as getReadState } from "./read-state-rDmfeLmz.js";
|
|
2
|
+
import { At as createInteractionTool, Ct as readFile, Dt as formatModelLine, Et as createModelSearchTool, Mt as glob, Nt as edit, Ot as matchModelEntries, Pt as validateToolArgs, St as tailTruncate, Tt as multiEdit, _t as createSkillsRunScriptTool, bt as createShellTool, dt as WAIT_TASK_TIMED_OUT_PREFIX, ft as waitTask, gt as createSkillsUseTool, ht as isSpawnTool, jt as grep, kt as listFiles, mt as createSpawnTool, pt as createToolSearchTool, ut as writeFile, vt as createSkillsReadTool, xt as shell, yt as shellKill } from "./agent-DoEx_WD3.js";
|
|
3
3
|
export { WAIT_TASK_TIMED_OUT_PREFIX, createInteractionTool, createModelSearchTool, createShellTool, createSkillsReadTool, createSkillsRunScriptTool, createSkillsUseTool, createSpawnTool, createToolSearchTool, edit, formatModelLine, getReadState, glob, grep, hashContent, hydrateReadStateFromSession, hydrateReadStateFromSessionAsync, isSpawnTool, listFiles, matchModelEntries, multiEdit, readFile, readStateKey, resolveReadStateMap, shell, shellKill, tailTruncate, validateToolArgs, waitTask, writeFile };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $u as ProviderKey, Bu as StreamEvent, Cs as SubagentOverride, Do as ExtensionRegistry, Hu as ModelChainEntry, Iu as Picked, Ka as StoredExtensionConfigItem, Mu as EditOutcome, Nf as ToolResultContent, Of as ThinkingLevel, Pa as ExtensionConfigCategory, Qu as ProviderAuth, Ru as SessionMeta, Tf as SessionTurn, ad as ProviderDescriptor, bu as AgentRegistry, fp as SessionData, hp as SessionStore, mp as SessionRun, rd as ModelInfo, t as Preset, tf as McpServerConfig, ws as SubagentRegistry, xf as SessionContentBlock, zu as Settings } from "./index-
|
|
1
|
+
import { $u as ProviderKey, Bu as StreamEvent, Cs as SubagentOverride, Do as ExtensionRegistry, Hu as ModelChainEntry, Iu as Picked, Ka as StoredExtensionConfigItem, Mu as EditOutcome, Nf as ToolResultContent, Of as ThinkingLevel, Pa as ExtensionConfigCategory, Qu as ProviderAuth, Ru as SessionMeta, Tf as SessionTurn, ad as ProviderDescriptor, bu as AgentRegistry, fp as SessionData, hp as SessionStore, mp as SessionRun, rd as ModelInfo, t as Preset, tf as McpServerConfig, ws as SubagentRegistry, xf as SessionContentBlock, zu as Settings } from "./index-l0wehkWU.js";
|
|
2
2
|
//#region src/chat/hints.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Single hint entry — `<key> <label>`, optionally followed by an inline
|
|
@@ -1453,4 +1453,4 @@ declare function computeTurnAnchors(items: readonly TranscriptItem[]): {
|
|
|
1453
1453
|
};
|
|
1454
1454
|
//#endregion
|
|
1455
1455
|
export { KEYBINDING_KEY_COL_WIDTH as $, resolveStorageDirs as A, loadState as B, ResolvedPaths as C, StorageDirs as D, ResolveStorageDirsOptions as E, createStateStore as F, titleFromTurns as G, saveState as H, deriveSessionTitle as I, updateToolEventOutcomes as J, toolCallPreview as K, eventsFromTurns as L, StateStoreApi as M, TuiState as N, StorageMode as O, compactSummaryEvent as P, KEYBINDING_DEF_BY_ACTION as Q, lastContextSizeFromTurns as R, ResolvedConfig as S, resolveStoragePaths as T, stripSpawnTokensLine as U, marginTopFor as V, sumRunCosts as W, DEFAULT_KEYBINDINGS as X, usageByModel as Y, KEYBINDING_DEFS as Z, discoverProjectMcps as _, EMPTY_HINTS as _t, SessionExportTarget as a, ResolvedExtensionKeybinding as at, ChatOptions as b, hintsLength as bt, writeSessionExport as c, groupBindings as ct, useEnabledToggleSet as d, mergeKeybindings as dt, KeyAction as et, DiscoveredMcp as f, parseBindingSpec as ft, defaultMcpsConfigPaths as g, stripJsonComments as gt, buildMcpServers as h, resolveExtensionKeybindings as ht, SessionExportFormat as i, ParsedBinding as it, ModelUsageSummary as j, StorageSlot as k, EnabledAllowlistKey as l, keybindingsPath as lt, DiscoveryResult as m, readKeybindingsRaw as mt, computeTurnAnchors as n, KeyBindingSection as nt, renderSession as o, ensureKeybindingsFile as ot, DiscoveryError as p, readKeybindings as pt, toolResultText as q, SessionExportAnchor as r, KeyBindings as rt, resolveSessionExportTarget as s, formatBindingForDisplay as st, TranscriptItem as t, KeyBindingDef as tt, EnabledToggleSet as u, matchesBinding as ut, parseMcpsFile as v, Hint as vt, resolveConfig as w, ProviderRegistry as x, truncateTrailing as xt, AutoUpdateConfig as y, clipHintsToWidth as yt, listSessionMeta as z };
|
|
1456
|
-
//# sourceMappingURL=transcript-anchors-
|
|
1456
|
+
//# sourceMappingURL=transcript-anchors-DKjXpQ0h.d.ts.map
|