zidane 5.4.2 → 5.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/{agent-DxBoKDba.d.ts → agent-Yu8uhpy-.d.ts} +74 -3
- package/dist/{agent-DxBoKDba.d.ts.map → agent-Yu8uhpy-.d.ts.map} +1 -1
- package/dist/chat.d.ts +49 -6
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +2 -2
- package/dist/{errors-Byb0F8B9.js → errors-CDwtPIMX.js} +4 -2
- package/dist/{errors-Byb0F8B9.js.map → errors-CDwtPIMX.js.map} +1 -1
- package/dist/{index-BOtXdQkW.d.ts → index-DklfxeYy.d.ts} +8 -2
- package/dist/index-DklfxeYy.d.ts.map +1 -0
- package/dist/{index-B2VOOijU.d.ts → index-j9tY28ah.d.ts} +16 -3
- package/dist/index-j9tY28ah.d.ts.map +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +10 -10
- package/dist/{interpolate-ERgZUxgg.js → interpolate-CmtjEyRJ.js} +155 -18
- package/dist/interpolate-CmtjEyRJ.js.map +1 -0
- package/dist/{login-CJbeAadS.js → login-DxyAERe1.js} +3 -3
- package/dist/{login-CJbeAadS.js.map → login-DxyAERe1.js.map} +1 -1
- package/dist/{mcp-DhmmJfxK.js → mcp-CNUbvbsy.js} +2 -2
- package/dist/{mcp-DhmmJfxK.js.map → mcp-CNUbvbsy.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-D0xT979U.js → messages-fTR19Ga6.js} +2 -2
- package/dist/{messages-D0xT979U.js.map → messages-fTR19Ga6.js.map} +1 -1
- package/dist/{presets-MCcvxiNT.js → presets-D9IbaI40.js} +2 -2
- package/dist/{presets-MCcvxiNT.js.map → presets-D9IbaI40.js.map} +1 -1
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +1 -1
- package/dist/{providers-x3LZByR5.js → providers-CEzRFYtS.js} +3 -3
- package/dist/{providers-x3LZByR5.js.map → providers-CEzRFYtS.js.map} +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +2 -2
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session/sqlite.js +1 -1
- package/dist/{session-BHZwxmfr.js → session-kwsNnOmt.js} +2 -2
- package/dist/{session-BHZwxmfr.js.map → session-kwsNnOmt.js.map} +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/skills.d.ts +2 -2
- package/dist/skills.js +1 -1
- package/dist/{tools-BNfyY14s.js → tools-BK2vG9UX.js} +149 -32
- package/dist/tools-BK2vG9UX.js.map +1 -0
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-DonKvoh4.d.ts → transcript-anchors-DnaBcJej.d.ts} +52 -8
- package/dist/transcript-anchors-DnaBcJej.d.ts.map +1 -0
- package/dist/tui.d.ts +4 -2
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +651 -42
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-TKvy0q29.js → turn-operations-OzKEOXul.js} +240 -52
- package/dist/turn-operations-OzKEOXul.js.map +1 -0
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/docs/ARCHITECTURE.md +16 -3
- package/docs/CHAT.md +1 -1
- package/docs/SKILL.md +24 -14
- package/docs/TUI.md +24 -0
- package/package.json +3 -3
- package/dist/index-B2VOOijU.d.ts.map +0 -1
- package/dist/index-BOtXdQkW.d.ts.map +0 -1
- package/dist/interpolate-ERgZUxgg.js.map +0 -1
- package/dist/tools-BNfyY14s.js.map +0 -1
- package/dist/transcript-anchors-DonKvoh4.d.ts.map +0 -1
- package/dist/turn-operations-TKvy0q29.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-BHZwxmfr.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 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 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 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\n async function hydrate(): Promise<void> {\n if (hydrated)\n return\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 }\n hydrated = true\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 turns: [],\n runs: [],\n status: 'idle',\n metadata: {},\n createdAt: now,\n updatedAt: now,\n }\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 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 = sessions.get(sessionId)\n if (data) {\n data.turns.push(...structuredClone(turns))\n data.updatedAt = Date.now()\n }\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 const idx = data.runs.findIndex(r => r.id === run.id)\n if (idx >= 0) {\n data.runs[idx] = structuredClone(run)\n }\n data.updatedAt = Date.now()\n }\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']) {\n const data = sessions.get(sessionId)\n if (data) {\n data.status = status\n data.updatedAt = Date.now()\n }\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}\n\nconst TRAILING_SLASH = /\\/$/\n\nexport function createRemoteStore(options: RemoteStoreOptions): SessionStore {\n const baseUrl = options.url.replace(TRAILING_SLASH, '')\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 const res = await fetch(url, {\n ...init,\n headers: { ...defaultHeaders, ...init?.headers },\n })\n return res\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 res.json() as SessionData\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 res.json() as { ids: string[] }\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 return await res.json() as SessionTurn[]\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 { SessionTurn, TurnUsage } from '../types'\n\nexport type { SessionContentBlock, SessionMessage, SessionTurn } from '../types'\nexport { createFileMapStore } from './file-map'\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\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// SessionStore interface (pluggable backend)\n// ---------------------------------------------------------------------------\n\nexport interface SessionStore {\n /** Optional: generate a session ID server-side (e.g. Supabase UUID). */\n generateSessionId?: () => string | Promise<string>\n\n /** Optional: generate a turn ID server-side. */\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 /** 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 /** Persist an updated run record (called after completeRun / abortRun / errorRun). */\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// ---------------------------------------------------------------------------\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 /** Current turn history */\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 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 /** Serialize to SessionData */\n toJSON: () => SessionData\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. Supabase) 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 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 toJSON() {\n return structuredClone(data)\n },\n }\n\n return session\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 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 } 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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"session-kwsNnOmt.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 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 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 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\n async function hydrate(): Promise<void> {\n if (hydrated)\n return\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 }\n hydrated = true\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 turns: [],\n runs: [],\n status: 'idle',\n metadata: {},\n createdAt: now,\n updatedAt: now,\n }\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 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 = sessions.get(sessionId)\n if (data) {\n data.turns.push(...structuredClone(turns))\n data.updatedAt = Date.now()\n }\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 const idx = data.runs.findIndex(r => r.id === run.id)\n if (idx >= 0) {\n data.runs[idx] = structuredClone(run)\n }\n data.updatedAt = Date.now()\n }\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']) {\n const data = sessions.get(sessionId)\n if (data) {\n data.status = status\n data.updatedAt = Date.now()\n }\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}\n\nconst TRAILING_SLASH = /\\/$/\n\nexport function createRemoteStore(options: RemoteStoreOptions): SessionStore {\n const baseUrl = options.url.replace(TRAILING_SLASH, '')\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 const res = await fetch(url, {\n ...init,\n headers: { ...defaultHeaders, ...init?.headers },\n })\n return res\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 res.json() as SessionData\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 res.json() as { ids: string[] }\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 return await res.json() as SessionTurn[]\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 { SessionTurn, TurnUsage } from '../types'\n\nexport type { SessionContentBlock, SessionMessage, SessionTurn } from '../types'\nexport { createFileMapStore } from './file-map'\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\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// SessionStore interface (pluggable backend)\n// ---------------------------------------------------------------------------\n\nexport interface SessionStore {\n /** Optional: generate a session ID server-side (e.g. Supabase UUID). */\n generateSessionId?: () => string | Promise<string>\n\n /** Optional: generate a turn ID server-side. */\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 /** 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 /** Persist an updated run record (called after completeRun / abortRun / errorRun). */\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// ---------------------------------------------------------------------------\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 /** Current turn history */\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 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 /** Serialize to SessionData */\n toJSON: () => SessionData\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. Supabase) 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 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 toJSON() {\n return structuredClone(data)\n },\n }\n\n return session\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 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 } 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"],"mappings":"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"}
|
package/dist/session.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $ as fromOpenAI, B as createRemoteStore, F as SessionRun, I as SessionStore, J as autoDetectAndConvert, Kt as SessionContentBlock, L as createSession, M as CreateSessionOptions, N as Session, P as SessionData, Q as fromAnthropic, R as loadSession, Xt as SessionTurn, Yt as SessionMessage, at as createFileMapStore, et as toAnthropic, it as FileMapStoreOptions, nt as createMemoryStore, rt as FileMapAdapter, tt as toOpenAI, z as RemoteStoreOptions } from "./agent-
|
|
1
|
+
import { $ as fromOpenAI, B as createRemoteStore, F as SessionRun, I as SessionStore, J as autoDetectAndConvert, Kt as SessionContentBlock, L as createSession, M as CreateSessionOptions, N as Session, P as SessionData, Q as fromAnthropic, R as loadSession, Xt as SessionTurn, Yt as SessionMessage, at as createFileMapStore, et as toAnthropic, it as FileMapStoreOptions, nt as createMemoryStore, rt as FileMapAdapter, tt as toOpenAI, z as RemoteStoreOptions } from "./agent-Yu8uhpy-.js";
|
|
2
2
|
export { CreateSessionOptions, FileMapAdapter, FileMapStoreOptions, RemoteStoreOptions, Session, SessionContentBlock, SessionData, SessionMessage, SessionRun, SessionStore, SessionTurn, autoDetectAndConvert, createFileMapStore, createMemoryStore, createRemoteStore, createSession, fromAnthropic, fromOpenAI, loadSession, toAnthropic, toOpenAI };
|
package/dist/session.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { c as fromAnthropic, d as toOpenAI, i as autoDetectAndConvert, l as fromOpenAI, u as toAnthropic } from "./messages-
|
|
2
|
-
import { a as createFileMapStore, i as createMemoryStore, n as loadSession, r as createRemoteStore, t as createSession } from "./session-
|
|
1
|
+
import { c as fromAnthropic, d as toOpenAI, i as autoDetectAndConvert, l as fromOpenAI, u as toAnthropic } from "./messages-fTR19Ga6.js";
|
|
2
|
+
import { a as createFileMapStore, i as createMemoryStore, n as loadSession, r as createRemoteStore, t as createSession } from "./session-kwsNnOmt.js";
|
|
3
3
|
export { autoDetectAndConvert, createFileMapStore, createMemoryStore, createRemoteStore, createSession, fromAnthropic, fromOpenAI, loadSession, toAnthropic, toOpenAI };
|
package/dist/skills.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { A as SkillSource, D as SkillConfig, O as SkillDiagnostic, c as DeactivationReason, d as createSkillActivationState, j as SkillsConfig, k as SkillResource, l as SkillActivationState, o as ActivationVia, s as ActiveSkill, u as SkillActivationStateOptions } from "./agent-
|
|
2
|
-
import { S as installAllowedToolsGate, _ as inferSource, a as SkillValidationResult, b as buildCatalog, c as parseAllowedToolPattern, d as validateSkillName, f as resolveSkills, g as getDefaultScanPaths, h as discoverSkills, i as SkillValidationIssue, l as validateResourcePath, m as SourcedScanPath, n as writeSkillToDisk, o as isToolAllowedByUnion, p as interpolateShellCommands, r as writeSkillsToDisk, s as matchesAllowedTool, t as defineSkill, u as validateSkillForWrite, v as parseFrontmatter, x as IMPLICITLY_ALLOWED_SKILL_TOOLS, y as parseSkillFile } from "./index-
|
|
1
|
+
import { A as SkillSource, D as SkillConfig, O as SkillDiagnostic, c as DeactivationReason, d as createSkillActivationState, j as SkillsConfig, k as SkillResource, l as SkillActivationState, o as ActivationVia, s as ActiveSkill, u as SkillActivationStateOptions } from "./agent-Yu8uhpy-.js";
|
|
2
|
+
import { S as installAllowedToolsGate, _ as inferSource, a as SkillValidationResult, b as buildCatalog, c as parseAllowedToolPattern, d as validateSkillName, f as resolveSkills, g as getDefaultScanPaths, h as discoverSkills, i as SkillValidationIssue, l as validateResourcePath, m as SourcedScanPath, n as writeSkillToDisk, o as isToolAllowedByUnion, p as interpolateShellCommands, r as writeSkillsToDisk, s as matchesAllowedTool, t as defineSkill, u as validateSkillForWrite, v as parseFrontmatter, x as IMPLICITLY_ALLOWED_SKILL_TOOLS, y as parseSkillFile } from "./index-DklfxeYy.js";
|
|
3
3
|
export { ActivationVia, ActiveSkill, DeactivationReason, IMPLICITLY_ALLOWED_SKILL_TOOLS, SkillActivationState, SkillActivationStateOptions, SkillConfig, SkillDiagnostic, SkillResource, SkillSource, SkillValidationIssue, SkillValidationResult, SkillsConfig, SourcedScanPath, buildCatalog, createSkillActivationState, defineSkill, discoverSkills, getDefaultScanPaths, inferSource, installAllowedToolsGate, interpolateShellCommands, isToolAllowedByUnion, matchesAllowedTool, parseAllowedToolPattern, parseFrontmatter, parseSkillFile, resolveSkills, validateResourcePath, validateSkillForWrite, validateSkillName, writeSkillToDisk, writeSkillsToDisk };
|
package/dist/skills.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as validateResourcePath, a as discoverSkills, b as createSkillActivationState, c as parseFrontmatter, f as IMPLICITLY_ALLOWED_SKILL_TOOLS, g as parseAllowedToolPattern, h as matchesAllowedTool, i as writeSkillsToDisk, l as parseSkillFile, m as isToolAllowedByUnion, n as resolveSkills, o as getDefaultScanPaths, p as installAllowedToolsGate, r as writeSkillToDisk, s as inferSource, t as interpolateShellCommands, u as buildCatalog, v as validateSkillForWrite, y as validateSkillName } from "./interpolate-
|
|
1
|
+
import { _ as validateResourcePath, a as discoverSkills, b as createSkillActivationState, c as parseFrontmatter, f as IMPLICITLY_ALLOWED_SKILL_TOOLS, g as parseAllowedToolPattern, h as matchesAllowedTool, i as writeSkillsToDisk, l as parseSkillFile, m as isToolAllowedByUnion, n as resolveSkills, o as getDefaultScanPaths, p as installAllowedToolsGate, r as writeSkillToDisk, s as inferSource, t as interpolateShellCommands, u as buildCatalog, v as validateSkillForWrite, y as validateSkillName } from "./interpolate-CmtjEyRJ.js";
|
|
2
2
|
//#region src/skills/index.ts
|
|
3
3
|
/**
|
|
4
4
|
* Define an inline skill configuration.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { n as createProcessContext } from "./contexts-BwiHIr2w.js";
|
|
2
|
-
import { a as AgentToolPairingError, l as toTypedError, r as AgentProviderError, s as errorMessage, t as AgentAbortedError } from "./errors-
|
|
2
|
+
import { a as AgentToolPairingError, l as toTypedError, r as AgentProviderError, s as errorMessage, t as AgentAbortedError } from "./errors-CDwtPIMX.js";
|
|
3
3
|
import { t as toolOutputByteLength } from "./types-IcokUOyC.js";
|
|
4
|
-
import { a as detectTurnInterruption, n as SYNTHETIC_TOOL_RESULT_PLACEHOLDER, o as ensureToolResultPairing, s as filterUnresolvedToolUses } from "./messages-
|
|
5
|
-
import { t as connectMcpServers } from "./mcp-
|
|
6
|
-
import { _ as validateResourcePath, b as createSkillActivationState, d as escapeXml, n as resolveSkills, p as installAllowedToolsGate, t as interpolateShellCommands, u as buildCatalog } from "./interpolate-
|
|
4
|
+
import { a as detectTurnInterruption, n as SYNTHETIC_TOOL_RESULT_PLACEHOLDER, o as ensureToolResultPairing, s as filterUnresolvedToolUses } from "./messages-fTR19Ga6.js";
|
|
5
|
+
import { t as connectMcpServers } from "./mcp-CNUbvbsy.js";
|
|
6
|
+
import { _ as validateResourcePath, b as createSkillActivationState, d as escapeXml, n as resolveSkills, p as installAllowedToolsGate, t as interpolateShellCommands, u as buildCatalog } from "./interpolate-CmtjEyRJ.js";
|
|
7
7
|
import { n as formatTokenUsage, t as flattenTurns } from "./stats-DgOvY7wd.js";
|
|
8
8
|
import { createHooks } from "hookable";
|
|
9
9
|
import { mkdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
@@ -812,6 +812,27 @@ const INTERRUPT_MESSAGE_FOR_TOOL_USE = "[Request interrupted by user for tool us
|
|
|
812
812
|
*/
|
|
813
813
|
const TOOL_USE_SKIPPED_MESSAGE = "[Tool use skipped — superseded by user message]";
|
|
814
814
|
/**
|
|
815
|
+
* Canonical tool_result text emitted when a single tool call is cancelled
|
|
816
|
+
* mid-flight via `agent.cancelTool(callId)` (typically the TUI's
|
|
817
|
+
* "cancel this tool" affordance). Distinguished from
|
|
818
|
+
* {@link INTERRUPT_MESSAGE_FOR_TOOL_USE} (run-wide user abort) and
|
|
819
|
+
* {@link TOOL_USE_SKIPPED_MESSAGE} (steered) so the model — and downstream
|
|
820
|
+
* consumers — can tell the three apart by string match.
|
|
821
|
+
*
|
|
822
|
+
* Always paired with `isError: true` on the wire so the model treats the
|
|
823
|
+
* call as failed rather than as a successful response. The remaining tool
|
|
824
|
+
* calls in the batch continue running, in contrast with a full-run abort.
|
|
825
|
+
*/
|
|
826
|
+
const TOOL_USE_CANCELLED_MESSAGE = "[Tool call cancelled by user]";
|
|
827
|
+
/**
|
|
828
|
+
* Sentinel message rejected from the per-call cancellation promise inside
|
|
829
|
+
* {@link executeSingleTool}'s race. Plain-string and module-private — only
|
|
830
|
+
* the surrounding code reads it (via the `perCallAbort.signal.aborted`
|
|
831
|
+
* guard, NOT the message itself), so it just needs to be a stable
|
|
832
|
+
* non-empty identifier the rejection can carry.
|
|
833
|
+
*/
|
|
834
|
+
const CANCELLED_BY_USER_SENTINEL = "zidane:tool:cancelled-by-user";
|
|
835
|
+
/**
|
|
815
836
|
* Compute the effective thinking budget for a given run-relative turn, given
|
|
816
837
|
* the configured decay schedule. Pure helper — exported for tests and so
|
|
817
838
|
* downstream tooling can preview decay curves without spinning up the loop.
|
|
@@ -1670,6 +1691,29 @@ async function executeSingleTool(ctx, call, turnId) {
|
|
|
1670
1691
|
const callId = call.id;
|
|
1671
1692
|
const displayName = toWireName(call.name, ctx.aliasMaps);
|
|
1672
1693
|
const runToolCounts = Object.freeze({ ...ctx.runToolCounts });
|
|
1694
|
+
const perCallAbort = new AbortController();
|
|
1695
|
+
ctx.pendingToolCancels?.set(callId, perCallAbort);
|
|
1696
|
+
try {
|
|
1697
|
+
return await runSingleToolDispatch(ctx, call, turnId, {
|
|
1698
|
+
toolDef,
|
|
1699
|
+
callId,
|
|
1700
|
+
displayName,
|
|
1701
|
+
runToolCounts,
|
|
1702
|
+
perCallAbort
|
|
1703
|
+
});
|
|
1704
|
+
} finally {
|
|
1705
|
+
ctx.pendingToolCancels?.delete(callId);
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
/**
|
|
1709
|
+
* Body of {@link executeSingleTool}. Hoisted into its own function purely so
|
|
1710
|
+
* the per-call cancel registration (which spans every exit path) can sit in
|
|
1711
|
+
* a tight `try / finally` at the call site. Behavior is unchanged from the
|
|
1712
|
+
* pre-cancel implementation aside from the user-cancel branch in the
|
|
1713
|
+
* execute body — see {@link TOOL_USE_CANCELLED_MESSAGE}.
|
|
1714
|
+
*/
|
|
1715
|
+
async function runSingleToolDispatch(ctx, call, turnId, fixed) {
|
|
1716
|
+
const { toolDef, callId, displayName, runToolCounts, perCallAbort } = fixed;
|
|
1673
1717
|
const gateCtx = {
|
|
1674
1718
|
...buildToolHookBase(ctx, turnId, callId, call.name, displayName, call.input),
|
|
1675
1719
|
block: false,
|
|
@@ -1795,12 +1839,14 @@ async function executeSingleTool(ctx, call, turnId) {
|
|
|
1795
1839
|
runToolCounts,
|
|
1796
1840
|
...coercions ? { coercions } : {}
|
|
1797
1841
|
});
|
|
1798
|
-
let output;
|
|
1842
|
+
let output = "";
|
|
1799
1843
|
let isError = false;
|
|
1844
|
+
let cancelledByUser = false;
|
|
1845
|
+
const childSignal = typeof AbortSignal.any === "function" ? AbortSignal.any([ctx.signal, perCallAbort.signal]) : ctx.signal;
|
|
1800
1846
|
try {
|
|
1801
1847
|
const toolCtx = {
|
|
1802
1848
|
provider: ctx.provider,
|
|
1803
|
-
signal:
|
|
1849
|
+
signal: childSignal,
|
|
1804
1850
|
execution: ctx.execution,
|
|
1805
1851
|
handle: ctx.handle,
|
|
1806
1852
|
hooks: ctx.hooks,
|
|
@@ -1819,16 +1865,48 @@ async function executeSingleTool(ctx, call, turnId) {
|
|
|
1819
1865
|
...ctx.readState ? { readState: ctx.readState } : {},
|
|
1820
1866
|
...typeof ctx.depth === "number" ? { depth: ctx.depth } : {}
|
|
1821
1867
|
};
|
|
1822
|
-
|
|
1868
|
+
const bodyPromise = toolDef.execute(effectiveInput, toolCtx);
|
|
1869
|
+
bodyPromise.catch(() => {});
|
|
1870
|
+
let removeAbortListener;
|
|
1871
|
+
const cancellationPromise = new Promise((_, reject) => {
|
|
1872
|
+
if (perCallAbort.signal.aborted) {
|
|
1873
|
+
reject(new Error(CANCELLED_BY_USER_SENTINEL));
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1876
|
+
const onAbort = () => reject(new Error(CANCELLED_BY_USER_SENTINEL));
|
|
1877
|
+
perCallAbort.signal.addEventListener("abort", onAbort, { once: true });
|
|
1878
|
+
removeAbortListener = () => perCallAbort.signal.removeEventListener("abort", onAbort);
|
|
1879
|
+
});
|
|
1880
|
+
try {
|
|
1881
|
+
output = await Promise.race([bodyPromise, cancellationPromise]);
|
|
1882
|
+
} finally {
|
|
1883
|
+
removeAbortListener?.();
|
|
1884
|
+
}
|
|
1823
1885
|
} catch (err) {
|
|
1824
|
-
|
|
1825
|
-
|
|
1886
|
+
if (perCallAbort.signal.aborted && !ctx.signal.aborted) cancelledByUser = true;
|
|
1887
|
+
else {
|
|
1888
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1889
|
+
const errorCtx = {
|
|
1890
|
+
...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),
|
|
1891
|
+
error
|
|
1892
|
+
};
|
|
1893
|
+
await ctx.hooks.callHook("tool:error", errorCtx);
|
|
1894
|
+
output = errorCtx.result ?? `Tool error: ${error.message}`;
|
|
1895
|
+
isError = true;
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
if (cancelledByUser) {
|
|
1899
|
+
const reason = typeof perCallAbort.signal.reason === "string" ? perCallAbort.signal.reason : "cancelled-by-user";
|
|
1900
|
+
await ctx.hooks.callHook("tool:cancelled", {
|
|
1826
1901
|
...buildToolHookBase(ctx, turnId, callId, call.name, displayName, effectiveInput),
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1902
|
+
reason,
|
|
1903
|
+
runToolCounts
|
|
1904
|
+
});
|
|
1905
|
+
return { result: {
|
|
1906
|
+
id: callId,
|
|
1907
|
+
content: TOOL_USE_CANCELLED_MESSAGE,
|
|
1908
|
+
isError: true
|
|
1909
|
+
} };
|
|
1832
1910
|
}
|
|
1833
1911
|
const emitted = await emitToolResult(ctx, {
|
|
1834
1912
|
turnId,
|
|
@@ -2480,14 +2558,21 @@ function createSkillsUseTool(options) {
|
|
|
2480
2558
|
return {
|
|
2481
2559
|
spec: {
|
|
2482
2560
|
name: "skills_use",
|
|
2483
|
-
description: "Activate a specialized skill
|
|
2561
|
+
description: "Activate or deactivate a specialized skill. Call with `mode: \"activate\"` (default) to load a skill's full instructions when a task matches its catalog description. Call with `mode: \"deactivate\"` to release a skill whose allowed-tools restrictions are now in the way — e.g. when the skill's work is done or the active skill is blocking unrelated tool calls. After activating, follow the returned instructions; use skills_read to load referenced files and skills_run_script to execute bundled scripts.",
|
|
2484
2562
|
inputSchema: {
|
|
2485
2563
|
type: "object",
|
|
2486
|
-
properties: {
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2564
|
+
properties: {
|
|
2565
|
+
name: {
|
|
2566
|
+
type: "string",
|
|
2567
|
+
enum: options.catalog.map((s) => s.name),
|
|
2568
|
+
description: "The name of the skill to activate or deactivate (must be in the available skills catalog)."
|
|
2569
|
+
},
|
|
2570
|
+
mode: {
|
|
2571
|
+
type: "string",
|
|
2572
|
+
enum: ["activate", "deactivate"],
|
|
2573
|
+
description: "Whether to activate (load + apply the skill) or deactivate (release an active skill). Default: \"activate\"."
|
|
2574
|
+
}
|
|
2575
|
+
},
|
|
2491
2576
|
required: ["name"],
|
|
2492
2577
|
additionalProperties: false
|
|
2493
2578
|
}
|
|
@@ -2496,8 +2581,18 @@ function createSkillsUseTool(options) {
|
|
|
2496
2581
|
const skillName = input.name;
|
|
2497
2582
|
const skill = byName.get(skillName);
|
|
2498
2583
|
if (!skill) return `Error: unknown skill "${skillName}". Available skills: ${[...byName.keys()].join(", ") || "<none>"}.`;
|
|
2584
|
+
if ((input.mode ?? "activate") === "deactivate") {
|
|
2585
|
+
const removed = options.state.deactivate(skillName);
|
|
2586
|
+
if (!removed) return `Skill "${skillName}" was not active — nothing to deactivate.`;
|
|
2587
|
+
await options.hooks.callHook("skills:deactivate", {
|
|
2588
|
+
skill: removed.skill,
|
|
2589
|
+
reason: "model"
|
|
2590
|
+
});
|
|
2591
|
+
const remaining = options.state.active().map((a) => a.skill.name);
|
|
2592
|
+
return `Skill "${skillName}" deactivated — its allowed-tools restrictions no longer apply.${remaining.length > 0 ? ` Remaining active skills: ${remaining.join(", ")}.` : " No skills are currently active."}`;
|
|
2593
|
+
}
|
|
2499
2594
|
if (!options.state.isActive(skillName)) {
|
|
2500
|
-
if (options.state.activate(skill, "model") === "cap-reached") return `Error: cannot activate "${skillName}" — the maxActive skill cap has been reached. Currently active: ${options.state.active().map((a) => a.skill.name).join(", ")}.
|
|
2595
|
+
if (options.state.activate(skill, "model") === "cap-reached") return `Error: cannot activate "${skillName}" — the maxActive skill cap has been reached. Currently active: ${options.state.active().map((a) => a.skill.name).join(", ")}. Call \`skills_use\` with \`mode: "deactivate"\` and one of those names first.`;
|
|
2501
2596
|
await options.hooks.callHook("skills:activate", {
|
|
2502
2597
|
skill,
|
|
2503
2598
|
via: "model"
|
|
@@ -2673,6 +2768,7 @@ const HOOK_EVENT_SET = new Set([
|
|
|
2673
2768
|
"tool:before",
|
|
2674
2769
|
"tool:after",
|
|
2675
2770
|
"tool:error",
|
|
2771
|
+
"tool:cancelled",
|
|
2676
2772
|
"tool:transform",
|
|
2677
2773
|
"tool:unknown",
|
|
2678
2774
|
"validation:reject",
|
|
@@ -2694,6 +2790,7 @@ const HOOK_EVENT_SET = new Set([
|
|
|
2694
2790
|
"child:tool:after",
|
|
2695
2791
|
"child:tool:transform",
|
|
2696
2792
|
"child:tool:error",
|
|
2793
|
+
"child:tool:cancelled",
|
|
2697
2794
|
"child:turn:after",
|
|
2698
2795
|
"mcp:connect",
|
|
2699
2796
|
"mcp:error",
|
|
@@ -2973,6 +3070,7 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
2973
3070
|
let running = false;
|
|
2974
3071
|
let idleResolve;
|
|
2975
3072
|
let idlePromise;
|
|
3073
|
+
const pendingToolCancels = /* @__PURE__ */ new Map();
|
|
2976
3074
|
let executionHandle = null;
|
|
2977
3075
|
let mcpConnection = null;
|
|
2978
3076
|
let mcpWarmupPromise = null;
|
|
@@ -3090,20 +3188,27 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
3090
3188
|
await ensureSkillsResolved();
|
|
3091
3189
|
if (resolvedSkills && session && session.turns.length > 0 && skillActivationState.active().length === 0) {
|
|
3092
3190
|
const skillsByName = new Map(resolvedSkills.map((s) => [s.name, s]));
|
|
3191
|
+
const lastModeBySkill = /* @__PURE__ */ new Map();
|
|
3093
3192
|
for (const turn of session.turns) {
|
|
3094
3193
|
if (turn.role !== "assistant") continue;
|
|
3095
3194
|
for (const block of turn.content) {
|
|
3096
3195
|
if (block.type !== "tool_call" || block.name !== "skills_use") continue;
|
|
3097
|
-
const
|
|
3196
|
+
const input = block.input;
|
|
3197
|
+
const skillName = input?.name;
|
|
3098
3198
|
if (!skillName) continue;
|
|
3099
|
-
const
|
|
3100
|
-
|
|
3101
|
-
if (skillActivationState.activate(skill, "resume") === "ok") await hooks.callHook("skills:activate", {
|
|
3102
|
-
skill,
|
|
3103
|
-
via: "resume"
|
|
3104
|
-
});
|
|
3199
|
+
const mode = input?.mode === "deactivate" ? "deactivate" : "activate";
|
|
3200
|
+
lastModeBySkill.set(skillName, mode);
|
|
3105
3201
|
}
|
|
3106
3202
|
}
|
|
3203
|
+
for (const [skillName, mode] of lastModeBySkill) {
|
|
3204
|
+
if (mode !== "activate") continue;
|
|
3205
|
+
const skill = skillsByName.get(skillName);
|
|
3206
|
+
if (!skill) continue;
|
|
3207
|
+
if (skillActivationState.activate(skill, "resume") === "ok") await hooks.callHook("skills:activate", {
|
|
3208
|
+
skill,
|
|
3209
|
+
via: "resume"
|
|
3210
|
+
});
|
|
3211
|
+
}
|
|
3107
3212
|
}
|
|
3108
3213
|
const thinking = options.thinking ?? "off";
|
|
3109
3214
|
const model = options.model ?? provider.meta.defaultModel;
|
|
@@ -3301,7 +3406,8 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
3301
3406
|
...strictToolPairing ? { strictToolPairing: true } : {},
|
|
3302
3407
|
providerName: provider.name,
|
|
3303
3408
|
runStartMs,
|
|
3304
|
-
runToolCounts: {}
|
|
3409
|
+
runToolCounts: {},
|
|
3410
|
+
pendingToolCancels
|
|
3305
3411
|
});
|
|
3306
3412
|
const parentTurnCost = stats.turnUsage?.reduce((sum, t) => sum + (t.cost ?? 0), 0) ?? 0;
|
|
3307
3413
|
let childrenIn = 0;
|
|
@@ -3398,6 +3504,12 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
3398
3504
|
function abort() {
|
|
3399
3505
|
abortController?.abort();
|
|
3400
3506
|
}
|
|
3507
|
+
function cancelTool(callId, reason) {
|
|
3508
|
+
const controller = pendingToolCancels.get(callId);
|
|
3509
|
+
if (!controller || controller.signal.aborted) return false;
|
|
3510
|
+
controller.abort(reason ?? "user-cancelled-tool");
|
|
3511
|
+
return true;
|
|
3512
|
+
}
|
|
3401
3513
|
function steer(message) {
|
|
3402
3514
|
steeringQueue.push(message);
|
|
3403
3515
|
}
|
|
@@ -3491,6 +3603,8 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
3491
3603
|
async function destroy() {
|
|
3492
3604
|
if (destroyed) return;
|
|
3493
3605
|
destroyed = true;
|
|
3606
|
+
for (const controller of pendingToolCancels.values()) if (!controller.signal.aborted) controller.abort("agent-destroyed");
|
|
3607
|
+
pendingToolCancels.clear();
|
|
3494
3608
|
if (mcpWarmupPromise) try {
|
|
3495
3609
|
await mcpWarmupPromise;
|
|
3496
3610
|
} catch {}
|
|
@@ -3511,6 +3625,7 @@ function createAgent({ provider, name: agentName, system: agentSystem, tools: ag
|
|
|
3511
3625
|
hooks,
|
|
3512
3626
|
run,
|
|
3513
3627
|
abort,
|
|
3628
|
+
cancelTool,
|
|
3514
3629
|
steer,
|
|
3515
3630
|
followUp: followUpFn,
|
|
3516
3631
|
waitForIdle,
|
|
@@ -4902,6 +5017,7 @@ const BUBBLED_EVENTS = [
|
|
|
4902
5017
|
"tool:before",
|
|
4903
5018
|
"tool:after",
|
|
4904
5019
|
"tool:error",
|
|
5020
|
+
"tool:cancelled",
|
|
4905
5021
|
"turn:after"
|
|
4906
5022
|
];
|
|
4907
5023
|
const BUBBLED_MUTABLE_EVENTS = [
|
|
@@ -4918,6 +5034,7 @@ const CHILD_EVENT_NAME = {
|
|
|
4918
5034
|
"tool:before": "child:tool:before",
|
|
4919
5035
|
"tool:after": "child:tool:after",
|
|
4920
5036
|
"tool:error": "child:tool:error",
|
|
5037
|
+
"tool:cancelled": "child:tool:cancelled",
|
|
4921
5038
|
"turn:after": "child:turn:after"
|
|
4922
5039
|
};
|
|
4923
5040
|
const CHILD_MUTABLE_EVENT_NAME = {
|
|
@@ -5146,7 +5263,7 @@ function createSpawnTool(options = {}) {
|
|
|
5146
5263
|
});
|
|
5147
5264
|
if (forwardHooks) {
|
|
5148
5265
|
const unregisterEnricher = agent.hooks.hook("tool:before", async (toolCtx) => {
|
|
5149
|
-
if (toolCtx.name !== "write_file") return;
|
|
5266
|
+
if (toolCtx.name !== "write_file" && toolCtx.name !== "edit" && toolCtx.name !== "multi_edit") return;
|
|
5150
5267
|
if (!agent.handle) return;
|
|
5151
5268
|
const inputPath = toolCtx.input?.path;
|
|
5152
5269
|
if (typeof inputPath !== "string") return;
|
|
@@ -5321,6 +5438,6 @@ const writeFile$1 = {
|
|
|
5321
5438
|
}
|
|
5322
5439
|
};
|
|
5323
5440
|
//#endregion
|
|
5324
|
-
export {
|
|
5441
|
+
export { hashContent as A, PERSISTED_STUB_PREFIX as C, maybePersistToolResult as D, cleanupPersistedSession as E, resolveReadStateMap as M, resolvePersistDir as O, validateToolArgs as S, buildPersistedStub as T, createSkillsReadTool as _, multiEdit as a, TOOL_USE_CANCELLED_MESSAGE as b, grep as c, resolveOldString as d, styleReplacementForVia as f, createSkillsRunScriptTool as g, createSkillsUseTool as h, readFile$1 as i, readStateKey as j, getReadState as k, glob as l, createToolSearchTool as m, createSpawnTool as n, listFiles as o, createAgent as p, shell as r, createInteractionTool as s, writeFile$1 as t, edit as u, INTERRUPT_MESSAGE_FOR_TOOL_USE as v, PERSISTENCE_PREVIEW_BYTES as w, TOOL_USE_SKIPPED_MESSAGE as x, SHELL_CASCADE_CANCEL_MESSAGE as y };
|
|
5325
5442
|
|
|
5326
|
-
//# sourceMappingURL=tools-
|
|
5443
|
+
//# sourceMappingURL=tools-BK2vG9UX.js.map
|