zidane 7.1.8 → 7.1.9
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-BxRfclPQ.js → acp-Bt-eb5Z8.js} +2 -2
- package/dist/{acp-BxRfclPQ.js.map → acp-Bt-eb5Z8.js.map} +1 -1
- package/dist/acp-cli.js +3 -3
- package/dist/acp.d.ts +1 -1
- package/dist/acp.js +1 -1
- package/dist/{agent-CJbmpTmA.js → agent-B6etKd_o.js} +44 -10
- package/dist/agent-B6etKd_o.js.map +1 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +2 -2
- package/dist/{approvals-DMxUgyVd.js → approvals-CIKGWmiU.js} +6 -6
- package/dist/{approvals-DMxUgyVd.js.map → approvals-CIKGWmiU.js.map} +1 -1
- package/dist/{auth-DvMdkpGD.js → auth-CDDNbEmd.js} +2 -2
- package/dist/{auth-DvMdkpGD.js.map → auth-CDDNbEmd.js.map} +1 -1
- package/dist/{cache-telemetry-ehVdWsg2.js → cache-telemetry-D0xemhgK.js} +2 -2
- package/dist/{cache-telemetry-ehVdWsg2.js.map → cache-telemetry-D0xemhgK.js.map} +1 -1
- package/dist/chat/pure.d.ts +57 -4
- package/dist/chat/pure.d.ts.map +1 -1
- package/dist/chat/pure.js +4 -4
- package/dist/chat.d.ts +5 -5
- package/dist/chat.js +5 -5
- package/dist/contexts/daytona.d.ts +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/daemon-cli.js +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/extensions.d.ts +2 -2
- package/dist/extensions.js +3 -2
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +1 -1
- package/dist/{index-BMOl2Bog.d.ts → index-C45NxBZm.d.ts} +5 -5
- package/dist/{index-BMOl2Bog.d.ts.map → index-C45NxBZm.d.ts.map} +1 -1
- package/dist/{index-DCYh6lOp.d.ts → index-dXnPmqFx.d.ts} +129 -17
- package/dist/index-dXnPmqFx.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/{ipc-BqEflvqC.js → ipc-BWsncell.js} +2 -2
- package/dist/{ipc-BqEflvqC.js.map → ipc-BWsncell.js.map} +1 -1
- package/dist/ipc.d.ts +5 -5
- package/dist/ipc.js +3 -3
- package/dist/mcp.d.ts +1 -1
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/{cred-keys-BbbBr4q3.js → panels-B6gRk4bf.js} +174 -2
- package/dist/panels-B6gRk4bf.js.map +1 -0
- package/dist/plugins.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/arcee.d.ts +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/restate.d.ts +1 -1
- package/dist/restate.js +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/{shell-path-DeSQjoHJ.js → shell-path-CY7nWoF8.js} +6 -6
- package/dist/{shell-path-DeSQjoHJ.js.map → shell-path-CY7nWoF8.js.map} +1 -1
- package/dist/skills.d.ts +1 -1
- package/dist/{todo-edits-Dw2oO2ON.js → todo-edits-PxMJvYIX.js} +91 -2
- package/dist/todo-edits-PxMJvYIX.js.map +1 -0
- package/dist/{tool-formatters-BDm0tjgy.d.ts → tool-formatters-Bl1afA8N.d.ts} +2 -2
- package/dist/{tool-formatters-BDm0tjgy.d.ts.map → tool-formatters-Bl1afA8N.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 +1 -1
- package/dist/{transcript-anchors-DjcEsOYq.d.ts → transcript-anchors-BfI3ZXh1.d.ts} +3 -3
- package/dist/transcript-anchors-BfI3ZXh1.d.ts.map +1 -0
- package/dist/tui.d.ts +3 -3
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +425 -67
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-Rqk3E0Ue.d.ts → turn-operations-D0x_g6XQ.d.ts} +14 -8
- package/dist/turn-operations-D0x_g6XQ.d.ts.map +1 -0
- package/dist/{turn-operations-JynC5ODZ.js → turn-operations-D_k_FEur.js} +21 -10
- package/dist/{turn-operations-JynC5ODZ.js.map → turn-operations-D_k_FEur.js.map} +1 -1
- package/dist/types.d.ts +1 -1
- package/docs/EXTENSIONS.md +27 -6
- package/docs/PANELS.md +86 -0
- package/docs/README.md +2 -0
- package/package.json +1 -1
- package/dist/agent-CJbmpTmA.js.map +0 -1
- package/dist/cred-keys-BbbBr4q3.js.map +0 -1
- package/dist/index-DCYh6lOp.d.ts.map +0 -1
- package/dist/todo-edits-Dw2oO2ON.js.map +0 -1
- package/dist/transcript-anchors-DjcEsOYq.d.ts.map +0 -1
- package/dist/turn-operations-Rqk3E0Ue.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ipc-BqEflvqC.js","names":[],"sources":["../src/ipc/attach.ts"],"sourcesContent":["/**\n * `zidane attach` — a thin terminal client over the daemon protocol.\n *\n * Deliberately minimal (readline, no TUI framework): it exists to prove and\n * exercise the multi-window contract — several attach clients plus the GUI\n * can watch the SAME session converge live, answer each other's approvals,\n * and steer the same run. The full OpenTUI front-end migrates onto\n * `connectIpcClient` separately.\n *\n * Everything here is presentation: all state lives in the daemon.\n */\nimport type { InteractionRequest, StreamEvent } from '../chat/index'\nimport type { AnswerValue } from '../chat/interactions'\nimport type { IpcClient } from './client'\nimport type { EnsureDaemonOptions } from './daemon/lifecycle'\nimport type { HostSignal, SessionRow, WireApprovalRequest } from './protocol'\nimport process from 'node:process'\nimport { createInterface } from 'node:readline'\nimport { findGitRoot } from '../chat/index'\nimport { connectOrSpawnDaemon } from './client'\nimport { defaultSocketPath } from './daemon/lifecycle'\n\nconst DIM = '\\u001B[2m'\nconst BOLD = '\\u001B[1m'\nconst CYAN = '\\u001B[36m'\nconst YELLOW = '\\u001B[33m'\nconst RED = '\\u001B[31m'\nconst RESET = '\\u001B[0m'\n\nexport interface AttachOptions {\n socketPath?: string\n /** Attach to this session id (prefix match against the list). */\n sessionId?: string\n /** Create a fresh session instead of picking. */\n createNew?: boolean\n cwd?: string\n appVersion?: string\n /** Spawn-on-demand command when no daemon is listening. */\n spawnCommand?: string[]\n /** Injectable streams for tests. */\n stdin?: NodeJS.ReadableStream\n stdout?: NodeJS.WritableStream\n}\n\ninterface PendingAsk {\n resolve: (line: string) => void\n}\n\nexport async function runAttach(options: AttachOptions = {}): Promise<void> {\n const out = options.stdout ?? process.stdout\n const write = (text: string): void => void out.write(text)\n const cwd = options.cwd ?? process.cwd()\n\n const ensureOptions: EnsureDaemonOptions = {\n socketPath: options.socketPath ?? defaultSocketPath(),\n ...(options.spawnCommand ? { spawnCommand: options.spawnCommand } : {}),\n }\n /*\n * Library-safe failure: `runAttach` is exported from `zidane/ipc`, so a\n * dropped daemon REJECTS (the CLI wrapper owns the exit code) instead\n * of killing an embedder's process from inside the library.\n */\n let failRun: ((error: Error) => void) | null = null\n\n // Drain-and-respawn on version mismatch — a stale daemon self-heals\n // instead of hard-failing the window.\n const client = await connectOrSpawnDaemon({\n ...ensureOptions,\n ...(options.appVersion ? { appVersion: options.appVersion } : {}),\n onClose: () => {\n write(`\\n${RED}daemon connection lost${RESET}\\n`)\n failRun?.(new Error('daemon connection lost'))\n },\n })\n\n write(`${DIM}connected to zidane daemon (pid ${client.hello.pid}, v${client.hello.appVersion})${RESET}\\n`)\n\n const rl = createInterface({\n input: (options.stdin ?? process.stdin) as NodeJS.ReadableStream,\n output: out as NodeJS.WritableStream,\n prompt: `${BOLD}> ${RESET}`,\n })\n\n /*\n * Line routing: normally a line is a prompt for the model, but while an\n * approval / interaction / picker question is pending, the next line\n * answers it instead. One slot suffices — questions are asked serially.\n * Registered BEFORE the session picker so `ask` works during picking.\n */\n let pendingAsk: PendingAsk | null = null\n let submitLine: ((line: string) => void) | null = null\n /** Prompt lines typed before the input loop is armed (mid-boot) queue up. */\n const earlyLines: string[] = []\n\n rl.on('line', (line) => {\n if (pendingAsk) {\n const slot = pendingAsk\n pendingAsk = null\n slot.resolve(line)\n return\n }\n if (submitLine)\n submitLine(line)\n else\n earlyLines.push(line)\n })\n\n const ask = (prompt: string): Promise<string> => {\n write(prompt)\n return new Promise<string>((resolve) => {\n pendingAsk = { resolve }\n })\n }\n\n /*\n * HITL prompts share the single `pendingAsk` slot, so approvals /\n * interactions MUST be asked one at a time — the startup rehydrate loops\n * and live signals can otherwise fire several handlers at once, each\n * overwriting the previous resolver and leaving those prompts hung\n * forever (unanswerable from this window). Chain every handler on the\n * previous one so they run strictly sequentially.\n */\n let hitlQueue: Promise<void> = Promise.resolve()\n\n /*\n * De-dup + resolution tracking for HITL prompts. A request can arrive\n * both via `*:listPending` backfill AND a live `*:request` signal, and\n * another window can answer it while it's still queued here. We track\n * every id we've queued (so we never prompt twice) and every id the host\n * reported resolved (so a queued-but-not-yet-prompted request is skipped\n * instead of blocking the queue on a no-op stdin answer).\n */\n const queuedHitlIds = new Set<string>()\n const resolvedHitlIds = new Set<string>()\n\n const enqueueHitl = (requestId: string, task: () => Promise<void>): void => {\n if (queuedHitlIds.has(requestId) || resolvedHitlIds.has(requestId))\n return\n queuedHitlIds.add(requestId)\n hitlQueue = hitlQueue.then(async () => {\n // Another window may have answered it while it waited in the queue.\n if (resolvedHitlIds.has(requestId))\n return\n await task()\n }, () => undefined)\n }\n\n const session = await pickSession(client, { ask, write, cwd, ...options })\n if (!session) {\n rl.close()\n client.close()\n return\n }\n\n write(`${BOLD}session${RESET} ${session.id} ${DIM}(${session.title})${RESET}\\n`)\n write(`${DIM}tools run in ${session.cwd} — /interrupt to stop a run, /exit to detach (run keeps going)${RESET}\\n\\n`)\n\n /*\n * Subscribe BEFORE replaying the transcript / snapshotting run state so\n * stream events emitted in that window aren't permanently lost from this\n * client's view. Signals that arrive during replay buffer here and drain\n * once the replay completes.\n */\n const bufferedSignals: HostSignal[] = []\n let signalSink: (signal: HostSignal) => void = s => bufferedSignals.push(s)\n const unsubscribe = client.onSignal(s => signalSink(s))\n\n await replayTranscript(client, session.id, write)\n\n let running = await client.request('chat.isRunning', { sessionId: session.id })\n if (running)\n write(`${DIM}a run is in flight — attaching to the live stream${RESET}\\n`)\n\n /* ----------------------------------------------------------------------\n * Signal rendering\n * -------------------------------------------------------------------- */\n\n let lastEventKind: StreamEvent['kind'] | null = null\n\n /**\n * The host broadcasts every window's user message as a `user-prompt`\n * event — render it so this window sees prompts posted elsewhere. Our\n * OWN just-sent messages are skipped once each (readline already echoed\n * them). A multiset (text → count) handles rapid identical sends and\n * never suppresses another window's matching message beyond our own\n * outstanding count.\n */\n const ownEchoes = new Map<string, number>()\n\n const renderEvent = (event: StreamEvent): void => {\n switch (event.kind) {\n case 'user-prompt': {\n const outstanding = ownEchoes.get(event.text) ?? 0\n if (outstanding > 0) {\n if (outstanding === 1)\n ownEchoes.delete(event.text)\n else\n ownEchoes.set(event.text, outstanding - 1)\n break\n }\n write(`\\n${BOLD}> ${event.text}${RESET}\\n`)\n break\n }\n case 'markdown':\n if (lastEventKind !== 'markdown' && lastEventKind !== null)\n write('\\n')\n write(event.text)\n break\n case 'thinking':\n // Streamed reasoning stays out of the transcript — show a pulse once.\n if (lastEventKind !== 'thinking')\n write(`${DIM}(thinking…)${RESET}\\n`)\n break\n case 'tool':\n write(`\\n${CYAN}▸ ${event.text}${RESET}\\n`)\n break\n case 'tool-result':\n write(`${DIM}${clipLines(event.text, 3)}${RESET}\\n`)\n break\n case 'task-notification':\n case 'info':\n write(`${DIM}${event.text}${RESET}\\n`)\n break\n case 'error':\n write(`${RED}${event.text}${RESET}\\n`)\n break\n default:\n break\n }\n lastEventKind = event.kind\n }\n\n const handleApproval = async (request: WireApprovalRequest): Promise<void> => {\n write(`\\n${YELLOW}approval needed${RESET} — ${BOLD}${request.tool}${RESET}\\n`)\n write(`${DIM}${clipLines(JSON.stringify(request.input, null, 2), 12)}${RESET}\\n`)\n const answer = await ask(`${YELLOW}[y]es once / [s]ession / [a]lways (safelist) / [n]o: ${RESET}`)\n const decision\n = answer.trim().toLowerCase() === 'y'\n ? 'accept-once'\n : answer.trim().toLowerCase() === 's'\n ? 'accept-session'\n : answer.trim().toLowerCase() === 'a'\n ? 'accept-safelist'\n : 'deny'\n await client.request('approvals.resolve', { requestId: request.id, decision })\n }\n\n const handleInteraction = async (request: InteractionRequest): Promise<void> => {\n if (request.kind === 'plan') {\n write(`\\n${YELLOW}plan:${RESET} ${BOLD}${request.payload.title}${RESET}\\n${request.payload.plan}\\n`)\n const answer = await ask(`${YELLOW}[a]pprove / [r]eject / re[v]ise (with comment): ${RESET}`)\n const c = answer.trim().toLowerCase()\n if (c === 'v') {\n const comment = await ask('comment: ')\n await client.request('interactions.respond', {\n requestId: request.id,\n response: { kind: 'plan', decision: 'revise', comment },\n })\n return\n }\n await client.request('interactions.respond', {\n requestId: request.id,\n response: { kind: 'plan', decision: c === 'a' ? 'approve' : 'reject' },\n })\n return\n }\n\n if (request.payload.intro)\n write(`\\n${request.payload.intro}\\n`)\n const answers: Record<string, AnswerValue> = {}\n for (const question of request.payload.questions) {\n if (question.type === 'confirm') {\n const answer = await ask(`${YELLOW}${question.prompt}${RESET} [y/n]: `)\n answers[question.id] = answer.trim().toLowerCase().startsWith('y')\n continue\n }\n if (question.type === 'select') {\n write(`${YELLOW}${question.prompt}${RESET}\\n`)\n question.choices.forEach((choice, i) => write(` ${i + 1}. ${choice.label}\\n`))\n const answer = await ask(question.multi ? 'pick (comma-separated numbers): ' : 'pick a number: ')\n const picks = answer\n .split(',')\n .map(part => question.choices[Number.parseInt(part.trim(), 10) - 1]?.id)\n .filter((id): id is string => typeof id === 'string')\n answers[question.id] = question.multi ? picks : (picks[0] ?? '')\n continue\n }\n answers[question.id] = await ask(`${YELLOW}${question.prompt}${RESET}: `)\n }\n await client.request('interactions.respond', {\n requestId: request.id,\n response: { kind: 'question', answers },\n })\n }\n\n const onSignal = (signal: HostSignal): void => {\n // Converge on resolution: mark the id answered so a queued-but-not-yet-\n // prompted request is skipped instead of blocking on stdin, and record\n // it so late backfill can't re-raise it.\n if (signal.kind === 'approval:resolved' || signal.kind === 'interaction:resolved') {\n resolvedHitlIds.add(signal.requestId)\n return\n }\n\n if (signal.kind === 'approval:request') {\n // Only surface approvals for the attached session — other windows'\n // sessions raise their own prompts in their own windows.\n if (signal.request.sessionId === session.id)\n enqueueHitl(signal.request.id, () => handleApproval(signal.request))\n return\n }\n\n if ('sessionId' in signal && signal.sessionId !== session.id)\n return\n\n switch (signal.kind) {\n case 'run:start':\n running = true\n lastEventKind = null\n break\n case 'run:end':\n running = false\n write(signal.ok ? '\\n' : `\\n${RED}run failed: ${signal.error ?? 'unknown error'}${RESET}\\n`)\n if (!pendingAsk)\n rl.prompt()\n break\n case 'event':\n renderEvent(signal.event)\n break\n case 'compacting:start':\n write(`${DIM}compacting…${RESET}\\n`)\n break\n case 'interaction:request':\n enqueueHitl(signal.request.id, () => handleInteraction(signal.request))\n break\n default:\n break\n }\n }\n\n // Surface HITL state left pending before this window attached. Done\n // BEFORE draining buffered signals so a live `*:request` that arrived\n // during backfill de-dups against the same id (via `queuedHitlIds`).\n for (const request of await client.request('approvals.listPending', {})) {\n if (request.sessionId === session.id)\n enqueueHitl(request.id, () => handleApproval(request))\n }\n for (const pending of await client.request('interactions.list', { sessionId: session.id })) {\n if (pending.live)\n enqueueHitl(pending.request.id, () => handleInteraction(pending.request))\n }\n\n // Drain signals buffered during replay/backfill, then hand live signals\n // straight to `onSignal`.\n signalSink = onSignal\n for (const buffered of bufferedSignals.splice(0))\n onSignal(buffered)\n\n /* ----------------------------------------------------------------------\n * Input loop\n * -------------------------------------------------------------------- */\n\n const detach = (): void => {\n unsubscribe()\n rl.close()\n client.close()\n write(`\\n${DIM}detached — the session keeps running in the daemon${RESET}\\n`)\n }\n\n rl.on('SIGINT', () => {\n if (running) {\n // Fire-and-forget: a dead daemon rejects the request, and an\n // uncaught rejection would crash the attach client.\n client.request('chat.interrupt', { sessionId: session.id }).catch(() => undefined)\n write(`\\n${DIM}interrupt sent${RESET}\\n`)\n return\n }\n detach()\n })\n\n await new Promise<void>((resolve, reject) => {\n failRun = (error) => {\n unsubscribe()\n rl.close()\n client.close()\n reject(error)\n }\n rl.on('close', () => resolve())\n\n const handleLine = (line: string): void => {\n const text = line.trim()\n if (text.length === 0) {\n rl.prompt()\n return\n }\n if (text === '/exit' || text === '/quit') {\n detach()\n resolve()\n return\n }\n if (text === '/interrupt') {\n client.request('chat.interrupt', { sessionId: session.id }).catch(() => undefined)\n return\n }\n\n ownEchoes.set(text, (ownEchoes.get(text) ?? 0) + 1)\n void client.request('chat.pushQueued', { sessionId: session.id, message: text }).then(({ delivered }) => {\n if (delivered === 'steer')\n write(`${DIM}(steered into the live run)${RESET}\\n`)\n }).catch((err) => {\n // Send failed: the host never broadcast the prompt, so release the\n // pending echo suppression to avoid eating a later identical one.\n const outstanding = ownEchoes.get(text) ?? 0\n if (outstanding <= 1)\n ownEchoes.delete(text)\n else\n ownEchoes.set(text, outstanding - 1)\n write(`${RED}${err instanceof Error ? err.message : String(err)}${RESET}\\n`)\n rl.prompt()\n })\n }\n\n submitLine = handleLine\n while (earlyLines.length > 0)\n handleLine(earlyLines.shift()!)\n\n if (!running)\n rl.prompt()\n })\n}\n\n/* --------------------------------------------------------------------------\n * Session picking\n * ------------------------------------------------------------------------ */\n\nasync function pickSession(\n client: IpcClient,\n ctx: {\n ask: (prompt: string) => Promise<string>\n write: (text: string) => void\n cwd: string\n sessionId?: string\n createNew?: boolean\n },\n): Promise<SessionRow | null> {\n const projectRoot = findGitRoot(ctx.cwd) ?? ctx.cwd\n\n if (ctx.createNew)\n return client.request('sessions.create', { cwd: ctx.cwd })\n\n const rows = await client.request('sessions.list', { projectRoot, archive: 'active' })\n\n if (ctx.sessionId) {\n const match = rows.find(r => r.id === ctx.sessionId || r.id.startsWith(ctx.sessionId!))\n if (match)\n return match\n // Not in this project — try the full list before giving up.\n const all = await client.request('sessions.list', {})\n const global = all.find(r => r.id === ctx.sessionId || r.id.startsWith(ctx.sessionId!))\n if (global)\n return global\n ctx.write(`${RED}no session matching \"${ctx.sessionId}\"${RESET}\\n`)\n return null\n }\n\n if (rows.length === 0)\n return client.request('sessions.create', { cwd: ctx.cwd })\n\n ctx.write(`${BOLD}sessions in ${projectRoot}:${RESET}\\n`)\n rows.slice(0, 15).forEach((row, i) => {\n const status = row.status === 'running' ? ` ${YELLOW}● running${RESET}` : ''\n ctx.write(` ${i + 1}. ${row.title} ${DIM}(${row.id.slice(0, 8)})${RESET}${status}\\n`)\n })\n\n const answer = await ctx.ask('pick a number, or [n]ew: ')\n if (answer.trim().toLowerCase() === 'n')\n return client.request('sessions.create', { cwd: ctx.cwd })\n\n const picked = rows[Number.parseInt(answer.trim(), 10) - 1]\n if (!picked) {\n ctx.write(`${RED}invalid pick${RESET}\\n`)\n return null\n }\n return picked\n}\n\n/* --------------------------------------------------------------------------\n * Transcript replay\n * ------------------------------------------------------------------------ */\n\nconst REPLAY_EVENT_CAP = 60\n\nasync function replayTranscript(\n client: IpcClient,\n sessionId: string,\n write: (text: string) => void,\n): Promise<void> {\n const events = await client.request('sessions.loadEvents', { sessionId })\n if (events.length === 0)\n return\n\n const tail = events.slice(-REPLAY_EVENT_CAP)\n if (events.length > tail.length)\n write(`${DIM}… ${events.length - tail.length} earlier events omitted${RESET}\\n`)\n\n for (const event of tail) {\n if (event.kind === 'user-prompt')\n write(`\\n${BOLD}> ${event.text}${RESET}\\n`)\n else if (event.kind === 'markdown')\n write(`${event.text}\\n`)\n else if (event.kind === 'tool')\n write(`${CYAN}▸ ${event.text}${RESET}\\n`)\n }\n write('\\n')\n}\n\nfunction clipLines(text: string, max: number): string {\n const lines = text.split('\\n')\n if (lines.length <= max)\n return text\n return `${lines.slice(0, max).join('\\n')}\\n${DIM}… ${lines.length - max} more lines${RESET}`\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ipc-BWsncell.js","names":[],"sources":["../src/ipc/attach.ts"],"sourcesContent":["/**\n * `zidane attach` — a thin terminal client over the daemon protocol.\n *\n * Deliberately minimal (readline, no TUI framework): it exists to prove and\n * exercise the multi-window contract — several attach clients plus the GUI\n * can watch the SAME session converge live, answer each other's approvals,\n * and steer the same run. The full OpenTUI front-end migrates onto\n * `connectIpcClient` separately.\n *\n * Everything here is presentation: all state lives in the daemon.\n */\nimport type { InteractionRequest, StreamEvent } from '../chat/index'\nimport type { AnswerValue } from '../chat/interactions'\nimport type { IpcClient } from './client'\nimport type { EnsureDaemonOptions } from './daemon/lifecycle'\nimport type { HostSignal, SessionRow, WireApprovalRequest } from './protocol'\nimport process from 'node:process'\nimport { createInterface } from 'node:readline'\nimport { findGitRoot } from '../chat/index'\nimport { connectOrSpawnDaemon } from './client'\nimport { defaultSocketPath } from './daemon/lifecycle'\n\nconst DIM = '\\u001B[2m'\nconst BOLD = '\\u001B[1m'\nconst CYAN = '\\u001B[36m'\nconst YELLOW = '\\u001B[33m'\nconst RED = '\\u001B[31m'\nconst RESET = '\\u001B[0m'\n\nexport interface AttachOptions {\n socketPath?: string\n /** Attach to this session id (prefix match against the list). */\n sessionId?: string\n /** Create a fresh session instead of picking. */\n createNew?: boolean\n cwd?: string\n appVersion?: string\n /** Spawn-on-demand command when no daemon is listening. */\n spawnCommand?: string[]\n /** Injectable streams for tests. */\n stdin?: NodeJS.ReadableStream\n stdout?: NodeJS.WritableStream\n}\n\ninterface PendingAsk {\n resolve: (line: string) => void\n}\n\nexport async function runAttach(options: AttachOptions = {}): Promise<void> {\n const out = options.stdout ?? process.stdout\n const write = (text: string): void => void out.write(text)\n const cwd = options.cwd ?? process.cwd()\n\n const ensureOptions: EnsureDaemonOptions = {\n socketPath: options.socketPath ?? defaultSocketPath(),\n ...(options.spawnCommand ? { spawnCommand: options.spawnCommand } : {}),\n }\n /*\n * Library-safe failure: `runAttach` is exported from `zidane/ipc`, so a\n * dropped daemon REJECTS (the CLI wrapper owns the exit code) instead\n * of killing an embedder's process from inside the library.\n */\n let failRun: ((error: Error) => void) | null = null\n\n // Drain-and-respawn on version mismatch — a stale daemon self-heals\n // instead of hard-failing the window.\n const client = await connectOrSpawnDaemon({\n ...ensureOptions,\n ...(options.appVersion ? { appVersion: options.appVersion } : {}),\n onClose: () => {\n write(`\\n${RED}daemon connection lost${RESET}\\n`)\n failRun?.(new Error('daemon connection lost'))\n },\n })\n\n write(`${DIM}connected to zidane daemon (pid ${client.hello.pid}, v${client.hello.appVersion})${RESET}\\n`)\n\n const rl = createInterface({\n input: (options.stdin ?? process.stdin) as NodeJS.ReadableStream,\n output: out as NodeJS.WritableStream,\n prompt: `${BOLD}> ${RESET}`,\n })\n\n /*\n * Line routing: normally a line is a prompt for the model, but while an\n * approval / interaction / picker question is pending, the next line\n * answers it instead. One slot suffices — questions are asked serially.\n * Registered BEFORE the session picker so `ask` works during picking.\n */\n let pendingAsk: PendingAsk | null = null\n let submitLine: ((line: string) => void) | null = null\n /** Prompt lines typed before the input loop is armed (mid-boot) queue up. */\n const earlyLines: string[] = []\n\n rl.on('line', (line) => {\n if (pendingAsk) {\n const slot = pendingAsk\n pendingAsk = null\n slot.resolve(line)\n return\n }\n if (submitLine)\n submitLine(line)\n else\n earlyLines.push(line)\n })\n\n const ask = (prompt: string): Promise<string> => {\n write(prompt)\n return new Promise<string>((resolve) => {\n pendingAsk = { resolve }\n })\n }\n\n /*\n * HITL prompts share the single `pendingAsk` slot, so approvals /\n * interactions MUST be asked one at a time — the startup rehydrate loops\n * and live signals can otherwise fire several handlers at once, each\n * overwriting the previous resolver and leaving those prompts hung\n * forever (unanswerable from this window). Chain every handler on the\n * previous one so they run strictly sequentially.\n */\n let hitlQueue: Promise<void> = Promise.resolve()\n\n /*\n * De-dup + resolution tracking for HITL prompts. A request can arrive\n * both via `*:listPending` backfill AND a live `*:request` signal, and\n * another window can answer it while it's still queued here. We track\n * every id we've queued (so we never prompt twice) and every id the host\n * reported resolved (so a queued-but-not-yet-prompted request is skipped\n * instead of blocking the queue on a no-op stdin answer).\n */\n const queuedHitlIds = new Set<string>()\n const resolvedHitlIds = new Set<string>()\n\n const enqueueHitl = (requestId: string, task: () => Promise<void>): void => {\n if (queuedHitlIds.has(requestId) || resolvedHitlIds.has(requestId))\n return\n queuedHitlIds.add(requestId)\n hitlQueue = hitlQueue.then(async () => {\n // Another window may have answered it while it waited in the queue.\n if (resolvedHitlIds.has(requestId))\n return\n await task()\n }, () => undefined)\n }\n\n const session = await pickSession(client, { ask, write, cwd, ...options })\n if (!session) {\n rl.close()\n client.close()\n return\n }\n\n write(`${BOLD}session${RESET} ${session.id} ${DIM}(${session.title})${RESET}\\n`)\n write(`${DIM}tools run in ${session.cwd} — /interrupt to stop a run, /exit to detach (run keeps going)${RESET}\\n\\n`)\n\n /*\n * Subscribe BEFORE replaying the transcript / snapshotting run state so\n * stream events emitted in that window aren't permanently lost from this\n * client's view. Signals that arrive during replay buffer here and drain\n * once the replay completes.\n */\n const bufferedSignals: HostSignal[] = []\n let signalSink: (signal: HostSignal) => void = s => bufferedSignals.push(s)\n const unsubscribe = client.onSignal(s => signalSink(s))\n\n await replayTranscript(client, session.id, write)\n\n let running = await client.request('chat.isRunning', { sessionId: session.id })\n if (running)\n write(`${DIM}a run is in flight — attaching to the live stream${RESET}\\n`)\n\n /* ----------------------------------------------------------------------\n * Signal rendering\n * -------------------------------------------------------------------- */\n\n let lastEventKind: StreamEvent['kind'] | null = null\n\n /**\n * The host broadcasts every window's user message as a `user-prompt`\n * event — render it so this window sees prompts posted elsewhere. Our\n * OWN just-sent messages are skipped once each (readline already echoed\n * them). A multiset (text → count) handles rapid identical sends and\n * never suppresses another window's matching message beyond our own\n * outstanding count.\n */\n const ownEchoes = new Map<string, number>()\n\n const renderEvent = (event: StreamEvent): void => {\n switch (event.kind) {\n case 'user-prompt': {\n const outstanding = ownEchoes.get(event.text) ?? 0\n if (outstanding > 0) {\n if (outstanding === 1)\n ownEchoes.delete(event.text)\n else\n ownEchoes.set(event.text, outstanding - 1)\n break\n }\n write(`\\n${BOLD}> ${event.text}${RESET}\\n`)\n break\n }\n case 'markdown':\n if (lastEventKind !== 'markdown' && lastEventKind !== null)\n write('\\n')\n write(event.text)\n break\n case 'thinking':\n // Streamed reasoning stays out of the transcript — show a pulse once.\n if (lastEventKind !== 'thinking')\n write(`${DIM}(thinking…)${RESET}\\n`)\n break\n case 'tool':\n write(`\\n${CYAN}▸ ${event.text}${RESET}\\n`)\n break\n case 'tool-result':\n write(`${DIM}${clipLines(event.text, 3)}${RESET}\\n`)\n break\n case 'task-notification':\n case 'info':\n write(`${DIM}${event.text}${RESET}\\n`)\n break\n case 'error':\n write(`${RED}${event.text}${RESET}\\n`)\n break\n default:\n break\n }\n lastEventKind = event.kind\n }\n\n const handleApproval = async (request: WireApprovalRequest): Promise<void> => {\n write(`\\n${YELLOW}approval needed${RESET} — ${BOLD}${request.tool}${RESET}\\n`)\n write(`${DIM}${clipLines(JSON.stringify(request.input, null, 2), 12)}${RESET}\\n`)\n const answer = await ask(`${YELLOW}[y]es once / [s]ession / [a]lways (safelist) / [n]o: ${RESET}`)\n const decision\n = answer.trim().toLowerCase() === 'y'\n ? 'accept-once'\n : answer.trim().toLowerCase() === 's'\n ? 'accept-session'\n : answer.trim().toLowerCase() === 'a'\n ? 'accept-safelist'\n : 'deny'\n await client.request('approvals.resolve', { requestId: request.id, decision })\n }\n\n const handleInteraction = async (request: InteractionRequest): Promise<void> => {\n if (request.kind === 'plan') {\n write(`\\n${YELLOW}plan:${RESET} ${BOLD}${request.payload.title}${RESET}\\n${request.payload.plan}\\n`)\n const answer = await ask(`${YELLOW}[a]pprove / [r]eject / re[v]ise (with comment): ${RESET}`)\n const c = answer.trim().toLowerCase()\n if (c === 'v') {\n const comment = await ask('comment: ')\n await client.request('interactions.respond', {\n requestId: request.id,\n response: { kind: 'plan', decision: 'revise', comment },\n })\n return\n }\n await client.request('interactions.respond', {\n requestId: request.id,\n response: { kind: 'plan', decision: c === 'a' ? 'approve' : 'reject' },\n })\n return\n }\n\n if (request.payload.intro)\n write(`\\n${request.payload.intro}\\n`)\n const answers: Record<string, AnswerValue> = {}\n for (const question of request.payload.questions) {\n if (question.type === 'confirm') {\n const answer = await ask(`${YELLOW}${question.prompt}${RESET} [y/n]: `)\n answers[question.id] = answer.trim().toLowerCase().startsWith('y')\n continue\n }\n if (question.type === 'select') {\n write(`${YELLOW}${question.prompt}${RESET}\\n`)\n question.choices.forEach((choice, i) => write(` ${i + 1}. ${choice.label}\\n`))\n const answer = await ask(question.multi ? 'pick (comma-separated numbers): ' : 'pick a number: ')\n const picks = answer\n .split(',')\n .map(part => question.choices[Number.parseInt(part.trim(), 10) - 1]?.id)\n .filter((id): id is string => typeof id === 'string')\n answers[question.id] = question.multi ? picks : (picks[0] ?? '')\n continue\n }\n answers[question.id] = await ask(`${YELLOW}${question.prompt}${RESET}: `)\n }\n await client.request('interactions.respond', {\n requestId: request.id,\n response: { kind: 'question', answers },\n })\n }\n\n const onSignal = (signal: HostSignal): void => {\n // Converge on resolution: mark the id answered so a queued-but-not-yet-\n // prompted request is skipped instead of blocking on stdin, and record\n // it so late backfill can't re-raise it.\n if (signal.kind === 'approval:resolved' || signal.kind === 'interaction:resolved') {\n resolvedHitlIds.add(signal.requestId)\n return\n }\n\n if (signal.kind === 'approval:request') {\n // Only surface approvals for the attached session — other windows'\n // sessions raise their own prompts in their own windows.\n if (signal.request.sessionId === session.id)\n enqueueHitl(signal.request.id, () => handleApproval(signal.request))\n return\n }\n\n if ('sessionId' in signal && signal.sessionId !== session.id)\n return\n\n switch (signal.kind) {\n case 'run:start':\n running = true\n lastEventKind = null\n break\n case 'run:end':\n running = false\n write(signal.ok ? '\\n' : `\\n${RED}run failed: ${signal.error ?? 'unknown error'}${RESET}\\n`)\n if (!pendingAsk)\n rl.prompt()\n break\n case 'event':\n renderEvent(signal.event)\n break\n case 'compacting:start':\n write(`${DIM}compacting…${RESET}\\n`)\n break\n case 'interaction:request':\n enqueueHitl(signal.request.id, () => handleInteraction(signal.request))\n break\n default:\n break\n }\n }\n\n // Surface HITL state left pending before this window attached. Done\n // BEFORE draining buffered signals so a live `*:request` that arrived\n // during backfill de-dups against the same id (via `queuedHitlIds`).\n for (const request of await client.request('approvals.listPending', {})) {\n if (request.sessionId === session.id)\n enqueueHitl(request.id, () => handleApproval(request))\n }\n for (const pending of await client.request('interactions.list', { sessionId: session.id })) {\n if (pending.live)\n enqueueHitl(pending.request.id, () => handleInteraction(pending.request))\n }\n\n // Drain signals buffered during replay/backfill, then hand live signals\n // straight to `onSignal`.\n signalSink = onSignal\n for (const buffered of bufferedSignals.splice(0))\n onSignal(buffered)\n\n /* ----------------------------------------------------------------------\n * Input loop\n * -------------------------------------------------------------------- */\n\n const detach = (): void => {\n unsubscribe()\n rl.close()\n client.close()\n write(`\\n${DIM}detached — the session keeps running in the daemon${RESET}\\n`)\n }\n\n rl.on('SIGINT', () => {\n if (running) {\n // Fire-and-forget: a dead daemon rejects the request, and an\n // uncaught rejection would crash the attach client.\n client.request('chat.interrupt', { sessionId: session.id }).catch(() => undefined)\n write(`\\n${DIM}interrupt sent${RESET}\\n`)\n return\n }\n detach()\n })\n\n await new Promise<void>((resolve, reject) => {\n failRun = (error) => {\n unsubscribe()\n rl.close()\n client.close()\n reject(error)\n }\n rl.on('close', () => resolve())\n\n const handleLine = (line: string): void => {\n const text = line.trim()\n if (text.length === 0) {\n rl.prompt()\n return\n }\n if (text === '/exit' || text === '/quit') {\n detach()\n resolve()\n return\n }\n if (text === '/interrupt') {\n client.request('chat.interrupt', { sessionId: session.id }).catch(() => undefined)\n return\n }\n\n ownEchoes.set(text, (ownEchoes.get(text) ?? 0) + 1)\n void client.request('chat.pushQueued', { sessionId: session.id, message: text }).then(({ delivered }) => {\n if (delivered === 'steer')\n write(`${DIM}(steered into the live run)${RESET}\\n`)\n }).catch((err) => {\n // Send failed: the host never broadcast the prompt, so release the\n // pending echo suppression to avoid eating a later identical one.\n const outstanding = ownEchoes.get(text) ?? 0\n if (outstanding <= 1)\n ownEchoes.delete(text)\n else\n ownEchoes.set(text, outstanding - 1)\n write(`${RED}${err instanceof Error ? err.message : String(err)}${RESET}\\n`)\n rl.prompt()\n })\n }\n\n submitLine = handleLine\n while (earlyLines.length > 0)\n handleLine(earlyLines.shift()!)\n\n if (!running)\n rl.prompt()\n })\n}\n\n/* --------------------------------------------------------------------------\n * Session picking\n * ------------------------------------------------------------------------ */\n\nasync function pickSession(\n client: IpcClient,\n ctx: {\n ask: (prompt: string) => Promise<string>\n write: (text: string) => void\n cwd: string\n sessionId?: string\n createNew?: boolean\n },\n): Promise<SessionRow | null> {\n const projectRoot = findGitRoot(ctx.cwd) ?? ctx.cwd\n\n if (ctx.createNew)\n return client.request('sessions.create', { cwd: ctx.cwd })\n\n const rows = await client.request('sessions.list', { projectRoot, archive: 'active' })\n\n if (ctx.sessionId) {\n const match = rows.find(r => r.id === ctx.sessionId || r.id.startsWith(ctx.sessionId!))\n if (match)\n return match\n // Not in this project — try the full list before giving up.\n const all = await client.request('sessions.list', {})\n const global = all.find(r => r.id === ctx.sessionId || r.id.startsWith(ctx.sessionId!))\n if (global)\n return global\n ctx.write(`${RED}no session matching \"${ctx.sessionId}\"${RESET}\\n`)\n return null\n }\n\n if (rows.length === 0)\n return client.request('sessions.create', { cwd: ctx.cwd })\n\n ctx.write(`${BOLD}sessions in ${projectRoot}:${RESET}\\n`)\n rows.slice(0, 15).forEach((row, i) => {\n const status = row.status === 'running' ? ` ${YELLOW}● running${RESET}` : ''\n ctx.write(` ${i + 1}. ${row.title} ${DIM}(${row.id.slice(0, 8)})${RESET}${status}\\n`)\n })\n\n const answer = await ctx.ask('pick a number, or [n]ew: ')\n if (answer.trim().toLowerCase() === 'n')\n return client.request('sessions.create', { cwd: ctx.cwd })\n\n const picked = rows[Number.parseInt(answer.trim(), 10) - 1]\n if (!picked) {\n ctx.write(`${RED}invalid pick${RESET}\\n`)\n return null\n }\n return picked\n}\n\n/* --------------------------------------------------------------------------\n * Transcript replay\n * ------------------------------------------------------------------------ */\n\nconst REPLAY_EVENT_CAP = 60\n\nasync function replayTranscript(\n client: IpcClient,\n sessionId: string,\n write: (text: string) => void,\n): Promise<void> {\n const events = await client.request('sessions.loadEvents', { sessionId })\n if (events.length === 0)\n return\n\n const tail = events.slice(-REPLAY_EVENT_CAP)\n if (events.length > tail.length)\n write(`${DIM}… ${events.length - tail.length} earlier events omitted${RESET}\\n`)\n\n for (const event of tail) {\n if (event.kind === 'user-prompt')\n write(`\\n${BOLD}> ${event.text}${RESET}\\n`)\n else if (event.kind === 'markdown')\n write(`${event.text}\\n`)\n else if (event.kind === 'tool')\n write(`${CYAN}▸ ${event.text}${RESET}\\n`)\n }\n write('\\n')\n}\n\nfunction clipLines(text: string, max: number): string {\n const lines = text.split('\\n')\n if (lines.length <= max)\n return text\n return `${lines.slice(0, max).join('\\n')}\\n${DIM}… ${lines.length - max} more lines${RESET}`\n}\n"],"mappings":"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"}
|
package/dist/ipc.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { An as createMemoryApprovalsStore, Cn as EditOutcomeLedger, Dn as ApprovalHub, En as runCompaction, Fn as CommandSummary, Gn as SessionPresence, Hn as RunningWorkItem, In as HostSettings, Jn as WireApprovalRequest, Ln as HostSignal, Mn as PendingApprovalsStore, Nn as ApprovalOriginator, On as ApprovalRequestInput, Pn as CommandArgSuggestionSummary, Rn as PendingInteraction, Sn as createInteractionHub, Tn as CompactionEntry, Un as SendOptions, Vn as QueuedItem, Wn as SerializedPreview, _n as createChatHost, bn as WorkerSummary, dn as extractEditPayloadForGate, fn as readFileSafe, gn as ContextSegmentsReport, hn as ChatHost, jn as ChatHostEnvironment, kn as createApprovalHub, mn as serializePreview, pn as resolveCwdPath, qn as TodosSnapshot, v as SystemMdFile, vn as CreateWorkerParams, wn as PendingAnnotation, xn as InteractionHub, yn as WorkerRow, zn as PromptAttachment } from "./index-
|
|
3
|
-
import { Y as ResolvedConfig } from "./transcript-anchors-
|
|
4
|
-
import { p as TodoItem } from "./turn-operations-
|
|
5
|
-
import { T as SessionHistoryMatch, V as ApprovalDecision, w as SearchTurnResult } from "./tool-formatters-
|
|
1
|
+
import { Gg as JsonValue, Ka as ExtensionBus, Kf as InteractionResponse, _y as SessionArtifact, dd as StreamEvent, df as ProviderKey, g_ as SessionTurn, sm as ContextBreakdown, w_ as ThinkingLevel } from "./index-dXnPmqFx.js";
|
|
2
|
+
import { An as createMemoryApprovalsStore, Cn as EditOutcomeLedger, Dn as ApprovalHub, En as runCompaction, Fn as CommandSummary, Gn as SessionPresence, Hn as RunningWorkItem, In as HostSettings, Jn as WireApprovalRequest, Ln as HostSignal, Mn as PendingApprovalsStore, Nn as ApprovalOriginator, On as ApprovalRequestInput, Pn as CommandArgSuggestionSummary, Rn as PendingInteraction, Sn as createInteractionHub, Tn as CompactionEntry, Un as SendOptions, Vn as QueuedItem, Wn as SerializedPreview, _n as createChatHost, bn as WorkerSummary, dn as extractEditPayloadForGate, fn as readFileSafe, gn as ContextSegmentsReport, hn as ChatHost, jn as ChatHostEnvironment, kn as createApprovalHub, mn as serializePreview, pn as resolveCwdPath, qn as TodosSnapshot, v as SystemMdFile, vn as CreateWorkerParams, wn as PendingAnnotation, xn as InteractionHub, yn as WorkerRow, zn as PromptAttachment } from "./index-C45NxBZm.js";
|
|
3
|
+
import { Y as ResolvedConfig } from "./transcript-anchors-BfI3ZXh1.js";
|
|
4
|
+
import { p as TodoItem } from "./turn-operations-D0x_g6XQ.js";
|
|
5
|
+
import { T as SessionHistoryMatch, V as ApprovalDecision, w as SearchTurnResult } from "./tool-formatters-Bl1afA8N.js";
|
|
6
6
|
|
|
7
7
|
//#region src/ipc/attach.d.ts
|
|
8
8
|
interface AttachOptions {
|
package/dist/ipc.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Bt as resolveCwdPath, Lt as createInteractionHub, Pt as createChatHost, Rt as extractEditPayloadForGate, Vt as serializePreview, n as createMemoryApprovalsStore, nn as EditOutcomeLedger, rn as runCompaction, t as createApprovalHub, zt as readFileSafe } from "./approvals-
|
|
2
|
-
import { _ as selfDaemonSpawnCommand, a as createDaemonEnvironment, c as connectIpcClient, d as IpcErrorCode, f as IPC_BUILD_ID, g as probeSocket, h as ensureDaemonRunning, i as startIpcDaemon, l as connectOrSpawnDaemon, m as defaultSocketPath, n as mergePath, o as createFileApprovalsStore, p as daemonSocketName, r as shellPath, s as IpcVersionMismatchError, t as fixPath, u as IPC_PROTOCOL_VERSION } from "./shell-path-
|
|
3
|
-
import { t as runAttach } from "./ipc-
|
|
1
|
+
import { Bt as resolveCwdPath, Lt as createInteractionHub, Pt as createChatHost, Rt as extractEditPayloadForGate, Vt as serializePreview, n as createMemoryApprovalsStore, nn as EditOutcomeLedger, rn as runCompaction, t as createApprovalHub, zt as readFileSafe } from "./approvals-CIKGWmiU.js";
|
|
2
|
+
import { _ as selfDaemonSpawnCommand, a as createDaemonEnvironment, c as connectIpcClient, d as IpcErrorCode, f as IPC_BUILD_ID, g as probeSocket, h as ensureDaemonRunning, i as startIpcDaemon, l as connectOrSpawnDaemon, m as defaultSocketPath, n as mergePath, o as createFileApprovalsStore, p as daemonSocketName, r as shellPath, s as IpcVersionMismatchError, t as fixPath, u as IPC_PROTOCOL_VERSION } from "./shell-path-CY7nWoF8.js";
|
|
3
|
+
import { t as runAttach } from "./ipc-BWsncell.js";
|
|
4
4
|
export { EditOutcomeLedger, IPC_BUILD_ID, IPC_PROTOCOL_VERSION, IpcErrorCode, IpcVersionMismatchError, connectIpcClient, connectOrSpawnDaemon, createApprovalHub, createChatHost, createDaemonEnvironment, createFileApprovalsStore, createInteractionHub, createMemoryApprovalsStore, daemonSocketName, defaultSocketPath, ensureDaemonRunning, extractEditPayloadForGate, fixPath, mergePath, probeSocket, readFileSafe, resolveCwdPath, runAttach, runCompaction, selfDaemonSpawnCommand, serializePreview, shellPath, startIpcDaemon };
|
package/dist/mcp.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Ay as deriveMcpToolMeta, By as isAllowedRemoteUrl, Dy as attachStderrWarnPump, Ey as McpExecutionBinding, Fy as normalizeMcpServers, Iy as resolveTransportConfig, Jg as McpToolSchema, Kg as McpServerConfig, Ly as resultToString, My as expandMcpConfigEnv, Ny as nonOwningMcpView, Oy as connectMcpServers, Py as normalizeMcpBlocks, Ry as wrapDiscoveredMcpTools, Ty as McpConnection, Vy as originPinnedFetch, jy as expandEnvPlaceholders, ky as createStderrLineEmitter, wy as ConnectMcpServersOptions, zy as PinnedFetch } from "./index-dXnPmqFx.js";
|
|
2
2
|
export { ConnectMcpServersOptions, McpConnection, McpExecutionBinding, type McpServerConfig, type McpToolSchema, type PinnedFetch, attachStderrWarnPump, connectMcpServers, createStderrLineEmitter, deriveMcpToolMeta, expandEnvPlaceholders, expandMcpConfigEnv, isAllowedRemoteUrl, nonOwningMcpView, normalizeMcpBlocks, normalizeMcpServers, originPinnedFetch, resolveTransportConfig, resultToString, wrapDiscoveredMcpTools };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $n as HeadlessResult, Jn as FormattedHeadlessTurnEvent, Qn as HeadlessOutputFormat, Xn as HeadlessEvent, qi as Agent, qn as FormattedHeadlessResult } from "../index-
|
|
1
|
+
import { $n as HeadlessResult, Jn as FormattedHeadlessTurnEvent, Qn as HeadlessOutputFormat, Xn as HeadlessEvent, qi as Agent, qn as FormattedHeadlessResult } from "../index-dXnPmqFx.js";
|
|
2
2
|
|
|
3
3
|
//#region src/output/stream-json.d.ts
|
|
4
4
|
type StreamJsonOutputFormat = HeadlessOutputFormat;
|
|
@@ -612,6 +612,178 @@ function isReservedCredKey(fileKey) {
|
|
|
612
612
|
return fileKey.includes("#");
|
|
613
613
|
}
|
|
614
614
|
//#endregion
|
|
615
|
-
|
|
615
|
+
//#region src/extensions/panels.ts
|
|
616
|
+
/** Caps applied before content crosses IPC or reaches a renderer. */
|
|
617
|
+
const PANEL_CONTENT_LIMITS = {
|
|
618
|
+
sections: 32,
|
|
619
|
+
itemsPerSection: 200,
|
|
620
|
+
actions: 8,
|
|
621
|
+
/** Per string field. */
|
|
622
|
+
textChars: 2e4,
|
|
623
|
+
/** Whole payload, JSON-serialised. */
|
|
624
|
+
bytes: 512 * 1024
|
|
625
|
+
};
|
|
626
|
+
function str(v, max = PANEL_CONTENT_LIMITS.textChars) {
|
|
627
|
+
return typeof v === "string" ? v.length > max ? `${v.slice(0, max - 1)}…` : v : void 0;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Coerce whatever `load()` returned into a well-formed `PanelContent`:
|
|
631
|
+
* unknown item kinds and malformed entries are dropped, strings are
|
|
632
|
+
* trimmed to the caps, and the whole thing is size-bounded. Never throws.
|
|
633
|
+
*/
|
|
634
|
+
function normalizePanelContent(input) {
|
|
635
|
+
const empty = { sections: [] };
|
|
636
|
+
if (!input || typeof input !== "object") return empty;
|
|
637
|
+
const o = input;
|
|
638
|
+
const sections = [];
|
|
639
|
+
if (Array.isArray(o.sections)) for (const raw of o.sections.slice(0, PANEL_CONTENT_LIMITS.sections)) {
|
|
640
|
+
const s = normalizeSection(raw, sections.length);
|
|
641
|
+
if (s) sections.push(s);
|
|
642
|
+
}
|
|
643
|
+
const actions = [];
|
|
644
|
+
if (Array.isArray(o.actions)) {
|
|
645
|
+
for (const raw of o.actions.slice(0, PANEL_CONTENT_LIMITS.actions)) if (raw && typeof raw === "object") {
|
|
646
|
+
const a = raw;
|
|
647
|
+
const id = str(a.id, 200);
|
|
648
|
+
const label = str(a.label, 200);
|
|
649
|
+
if (id && label) actions.push({
|
|
650
|
+
id,
|
|
651
|
+
label,
|
|
652
|
+
...a.tone === "danger" ? { tone: "danger" } : {}
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
const out = { sections };
|
|
657
|
+
const title = str(o.title, 200);
|
|
658
|
+
if (title) out.title = title;
|
|
659
|
+
if (typeof o.badge === "number" && Number.isFinite(o.badge)) out.badge = o.badge;
|
|
660
|
+
else if (typeof o.badge === "string" && o.badge) out.badge = o.badge.slice(0, 12);
|
|
661
|
+
if (actions.length > 0) out.actions = actions;
|
|
662
|
+
const emptyText = str(o.empty, 500);
|
|
663
|
+
if (emptyText) out.empty = emptyText;
|
|
664
|
+
return enforceBytes(out);
|
|
665
|
+
}
|
|
666
|
+
function normalizeSection(raw, index) {
|
|
667
|
+
if (!raw || typeof raw !== "object") return null;
|
|
668
|
+
const s = raw;
|
|
669
|
+
const items = [];
|
|
670
|
+
if (Array.isArray(s.items)) for (const item of s.items.slice(0, PANEL_CONTENT_LIMITS.itemsPerSection)) {
|
|
671
|
+
const n = normalizeItem(item);
|
|
672
|
+
if (n) items.push(n);
|
|
673
|
+
}
|
|
674
|
+
const out = {
|
|
675
|
+
id: str(s.id, 200) ?? `section-${index}`,
|
|
676
|
+
items
|
|
677
|
+
};
|
|
678
|
+
const title = str(s.title, 200);
|
|
679
|
+
if (title) out.title = title;
|
|
680
|
+
if (s.collapsed === true) out.collapsed = true;
|
|
681
|
+
return out;
|
|
682
|
+
}
|
|
683
|
+
function normalizeItem(raw) {
|
|
684
|
+
if (!raw || typeof raw !== "object") return null;
|
|
685
|
+
const i = raw;
|
|
686
|
+
switch (i.kind) {
|
|
687
|
+
case "row": {
|
|
688
|
+
const title = str(i.title, 500);
|
|
689
|
+
if (!title) return null;
|
|
690
|
+
const out = {
|
|
691
|
+
kind: "row",
|
|
692
|
+
title
|
|
693
|
+
};
|
|
694
|
+
const id = str(i.id, 200);
|
|
695
|
+
const subtitle = str(i.subtitle, 1e3);
|
|
696
|
+
const href = str(i.href, 2e3);
|
|
697
|
+
const status = str(i.status, 60);
|
|
698
|
+
const actionId = str(i.actionId, 200);
|
|
699
|
+
if (id) out.id = id;
|
|
700
|
+
if (subtitle) out.subtitle = subtitle;
|
|
701
|
+
if (href) out.href = href;
|
|
702
|
+
if (status) out.status = status;
|
|
703
|
+
if (actionId) out.actionId = actionId;
|
|
704
|
+
if (Array.isArray(i.badges)) out.badges = i.badges.filter(isBadge).slice(0, 8);
|
|
705
|
+
if (i.payload !== void 0 && isJsonValue(i.payload)) out.payload = i.payload;
|
|
706
|
+
return out;
|
|
707
|
+
}
|
|
708
|
+
case "markdown": {
|
|
709
|
+
const text = str(i.text);
|
|
710
|
+
return text === void 0 ? null : {
|
|
711
|
+
kind: "markdown",
|
|
712
|
+
text
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
case "kv":
|
|
716
|
+
if (!Array.isArray(i.rows)) return null;
|
|
717
|
+
return {
|
|
718
|
+
kind: "kv",
|
|
719
|
+
rows: i.rows.map((r) => {
|
|
720
|
+
if (!r || typeof r !== "object") return null;
|
|
721
|
+
const row = r;
|
|
722
|
+
const label = str(row.label, 200);
|
|
723
|
+
const value = str(row.value, 2e3);
|
|
724
|
+
return label !== void 0 && value !== void 0 ? {
|
|
725
|
+
label,
|
|
726
|
+
value
|
|
727
|
+
} : null;
|
|
728
|
+
}).filter((r) => r !== null).slice(0, PANEL_CONTENT_LIMITS.itemsPerSection)
|
|
729
|
+
};
|
|
730
|
+
case "custom": {
|
|
731
|
+
const renderer = str(i.renderer, 200);
|
|
732
|
+
const fallback = str(i.fallback);
|
|
733
|
+
if (!renderer || fallback === void 0) return null;
|
|
734
|
+
const out = {
|
|
735
|
+
kind: "custom",
|
|
736
|
+
renderer,
|
|
737
|
+
fallback
|
|
738
|
+
};
|
|
739
|
+
if (i.data !== void 0 && isJsonValue(i.data)) out.data = i.data;
|
|
740
|
+
return out;
|
|
741
|
+
}
|
|
742
|
+
default: return null;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
function isBadge(v) {
|
|
746
|
+
return Boolean(v && typeof v === "object" && typeof v.text === "string");
|
|
747
|
+
}
|
|
748
|
+
function isJsonValue(v) {
|
|
749
|
+
try {
|
|
750
|
+
JSON.stringify(v);
|
|
751
|
+
return v !== void 0 && typeof v !== "function" && typeof v !== "symbol";
|
|
752
|
+
} catch {
|
|
753
|
+
return false;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Trim sections from the end until the payload fits the byte budget. A
|
|
758
|
+
* panel that overflows keeps its first sections and gets an `empty`-style
|
|
759
|
+
* note appended so the truncation is visible.
|
|
760
|
+
*/
|
|
761
|
+
function enforceBytes(content) {
|
|
762
|
+
const size = (c) => JSON.stringify(c).length;
|
|
763
|
+
if (size(content) <= PANEL_CONTENT_LIMITS.bytes) return content;
|
|
764
|
+
const sections = [...content.sections];
|
|
765
|
+
while (sections.length > 0 && size({
|
|
766
|
+
...content,
|
|
767
|
+
sections
|
|
768
|
+
}) > PANEL_CONTENT_LIMITS.bytes) sections.pop();
|
|
769
|
+
return {
|
|
770
|
+
...content,
|
|
771
|
+
sections: [...sections, {
|
|
772
|
+
id: "truncated",
|
|
773
|
+
items: [{
|
|
774
|
+
kind: "markdown",
|
|
775
|
+
text: "_Panel content truncated (too large)._"
|
|
776
|
+
}]
|
|
777
|
+
}]
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
/** Tab badge as shown next to the label; `undefined` when nothing to show. */
|
|
781
|
+
function panelBadgeText(badge) {
|
|
782
|
+
if (badge === void 0 || badge === 0 || badge === "") return void 0;
|
|
783
|
+
if (typeof badge === "number") return badge > 99 ? "99+" : String(badge);
|
|
784
|
+
return badge;
|
|
785
|
+
}
|
|
786
|
+
//#endregion
|
|
787
|
+
export { resolveOldString as _, ALTERNATE_CRED_KEY_SUFFIX as a, accountCredKey as c, isReservedCredKey as d, nextAccountCredKey as f, describeVia as g, buildContextBreakdown as h, ACCOUNT_CRED_KEY_INFIX as i, alternateCredKey as l, buildContextSegments as m, normalizePanelContent as n, CRED_KEY_SLOT_SEPARATOR as o, oauthAccountLabel as p, panelBadgeText as r, DEFAULT_OAUTH_ACCOUNT_LABEL as s, PANEL_CONTENT_LIMITS as t, isAccountCredKey as u, stripLineNumberPrefixes as v, styleReplacementForVia as y };
|
|
616
788
|
|
|
617
|
-
//# sourceMappingURL=
|
|
789
|
+
//# sourceMappingURL=panels-B6gRk4bf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panels-B6gRk4bf.js","names":[],"sources":["../src/tools/edit-utils.ts","../src/chat/context-breakdown.ts","../src/chat/context-segments.ts","../src/chat/cred-keys.ts","../src/extensions/panels.ts"],"sourcesContent":["/**\n * Internal helpers shared between the `edit` and `multi_edit` tools.\n *\n * Not part of the public API — intentionally not re-exported from `tools/index.ts`\n * or the package barrel.\n */\n\n/**\n * Count exact (non-overlapping) occurrences of `needle` in `haystack`.\n * Returns 0 for an empty needle — both edit tools reject empty `old_string`\n * up front, so this branch is defensive rather than semantic.\n */\nexport function countExactMatches(haystack: string, needle: string): number {\n if (needle.length === 0)\n return 0\n let count = 0\n let idx = 0\n while (true) {\n const next = haystack.indexOf(needle, idx)\n if (next === -1)\n break\n count++\n idx = next + needle.length\n }\n return count\n}\n\n// ---------------------------------------------------------------------------\n// Curly-quote normalization + SDK-sanitization fallbacks\n// ---------------------------------------------------------------------------\n//\n// Models can't reliably emit Unicode curly quotes, and the Anthropic API\n// silently sanitizes a handful of XML-like tags before they hit the model\n// (`<name>` → `<n>`, etc.). When `old_string` doesn't match exactly, we retry\n// against the same content with both transforms applied so an \"old_string not\n// found\" failure that's only typographical resolves on the second pass.\n//\n// Mirrors the Claude Code Edit tool's recovery strategy. Verbatim port of the\n// quote constants and the desanitization table; the resolve loop below is\n// reorganized to share state between exact / quote-normalized / desanitized.\n\nexport const LEFT_SINGLE_CURLY_QUOTE = '\\u2018'\nexport const RIGHT_SINGLE_CURLY_QUOTE = '\\u2019'\nexport const LEFT_DOUBLE_CURLY_QUOTE = '\\u201C'\nexport const RIGHT_DOUBLE_CURLY_QUOTE = '\\u201D'\n\n/** Map curly quotes (any of the four) to their straight ASCII equivalents. */\nexport function normalizeQuotes(str: string): string {\n return str\n .replaceAll(LEFT_SINGLE_CURLY_QUOTE, '\\'')\n .replaceAll(RIGHT_SINGLE_CURLY_QUOTE, '\\'')\n .replaceAll(LEFT_DOUBLE_CURLY_QUOTE, '\"')\n .replaceAll(RIGHT_DOUBLE_CURLY_QUOTE, '\"')\n}\n\n/**\n * Substitutions Anthropic's API applies to assistant output before the model\n * sees it. The model emits the sanitized form; the file on disk contains the\n * unsanitized form. We undo the substitutions on `old_string` so the search\n * lands on the actual file contents.\n *\n * Verbatim from `claude-code/tools/FileEditTool/utils.ts`.\n */\nconst DESANITIZATIONS: ReadonlyArray<readonly [string, string]> = [\n ['<fnr>', '<function_results>'],\n ['<n>', '<name>'],\n ['</n>', '</name>'],\n ['<o>', '<output>'],\n ['</o>', '</output>'],\n ['<e>', '<error>'],\n ['</e>', '</error>'],\n ['<s>', '<system>'],\n ['</s>', '</system>'],\n ['<r>', '<result>'],\n ['</r>', '</result>'],\n ['< META_START >', '<META_START>'],\n ['< META_END >', '<META_END>'],\n ['< EOT >', '<EOT>'],\n ['< META >', '<META>'],\n ['< SOS >', '<SOS>'],\n ['\\n\\nH:', '\\n\\nHuman:'],\n ['\\n\\nA:', '\\n\\nAssistant:'],\n]\n\n/**\n * Apply the SDK desanitization table to a string. Exported so the edit tools\n * can apply it to `new_string` whenever `old_string` matched via a\n * desanitize-class fallback — keeps the file's unsanitized form on disk\n * instead of writing the model's abbreviated form back.\n */\nexport function desanitize(s: string): string {\n let out = s\n for (const [from, to] of DESANITIZATIONS)\n out = out.replaceAll(from, to)\n return out\n}\n\n/**\n * Strip line-number prefixes from each line of a needle, used as a recovery\n * fallback when the model pastes a `read_file` chunk verbatim into\n * `old_string` — the on-disk file doesn't carry the metadata prefix.\n *\n * Accepts three separator characters so a model that learned on a different\n * agent stack still works here: `\\t` (Claude Code compact, our default),\n * `|`, and `→`. Pattern: optional leading whitespace, 1-9 digits, then one\n * of `\\t | →`. The 9-digit ceiling covers files up to ~1B lines without\n * overshooting into legitimate `\\d{N}<sep>` content like Markdown table\n * cells with long numeric IDs.\n */\nconst LINE_NUMBER_PREFIX_RE = /^[ \\t]*\\d{1,9}[\\t|\\u2192]/gm\nexport function stripLineNumberPrefixes(s: string): string {\n return s.replace(LINE_NUMBER_PREFIX_RE, '')\n}\n\n/**\n * Locate the actual substring in `haystack` that corresponds to `needle`,\n * recovering from typographical mismatch on six escalating fallbacks:\n *\n * 1. Exact substring match (the happy path — no transformation).\n * 2. Curly-quote normalization on both sides — when the model emits straight\n * quotes but the file has curly ones (or vice versa), the slice of the\n * file at the matched position carries the file's actual typography.\n * 3. Anthropic-sanitization undo on the needle — the model's `<n>` becomes\n * the file's `<name>`, etc.\n * 4. Combined: desanitize + quote normalization on the needle.\n * 5. Line-number-prefix strip — when the model pasted a numbered `read_file`\n * chunk verbatim into `old_string` and the file carries no such prefix.\n * 6. Combined: line-number strip + quote normalization — paste-back of a\n * numbered chunk against a file whose typography differs from the model's.\n *\n * Returns `{ actual, occurrences }` — `actual` is the string that exists in\n * the file (use this to do the actual replace; preserves the file's\n * typography). `occurrences` is the count of those matches in the file.\n *\n * Fallbacks 3-4 (desanitize-class) only fire when the transformed needle is\n * UNIQUE in the file — they rewrite the needle's meaning rather than its\n * typography, so an ambiguous match risks silently retargeting the wrong\n * text (see the inline comment at step 3).\n *\n * Returns `null` when no recovery worked.\n */\nexport interface ResolvedMatch {\n actual: string\n occurrences: number\n /** Recovery path — `'exact'` when no transformation was applied. */\n via: 'exact' | 'quotes' | 'desanitize' | 'quotes+desanitize' | 'line-numbers' | 'quotes+line-numbers'\n}\n\n/**\n * Search `target` in `normFile` and slice the matching span out of the\n * original `haystack`, counting all non-overlapping occurrences. `normFile`\n * is the haystack with whatever transform (quotes / desanitize / combined)\n * was applied to make the indices align — slicing the original haystack\n * preserves the file's actual typography so `replace_all` writes back the\n * file's form, not the model's.\n *\n * Pre-condition: `normFile.length === haystack.length` (every transform\n * we use is one-to-one). Returns null on miss.\n */\nfunction locateAndCount(\n haystack: string,\n normFile: string,\n target: string,\n via: ResolvedMatch['via'],\n): ResolvedMatch | null {\n const idx = normFile.indexOf(target)\n if (idx === -1)\n return null\n const actual = haystack.slice(idx, idx + target.length)\n let occ = 0\n let cursor = 0\n while (true) {\n const next = normFile.indexOf(target, cursor)\n if (next === -1)\n break\n occ++\n cursor = next + target.length\n }\n return { actual, occurrences: occ, via }\n}\n\nexport function resolveOldString(haystack: string, needle: string): ResolvedMatch | null {\n // 1. Exact match — happy path.\n const exact = countExactMatches(haystack, needle)\n if (exact > 0)\n return { actual: needle, occurrences: exact, via: 'exact' }\n\n // 2. Curly-quote normalization. Either side can carry curly quotes:\n // model emitted straight + file has curly, OR model emitted curly +\n // file has straight. Normalize both and look there. Slice the original\n // haystack to recover the actual file-side typography.\n const normNeedle = normalizeQuotes(needle)\n const normFile = normalizeQuotes(haystack)\n if (normNeedle !== needle || normFile !== haystack) {\n const m = locateAndCount(haystack, normFile, normNeedle, 'quotes')\n if (m)\n return m\n }\n\n // 3. Desanitize the needle — model-side substitution undo (`<n>` → `<name>`,\n // etc.) The file always has the unsanitized form; only the model emits\n // the abbreviated version.\n //\n // Desanitize-class fallbacks additionally require a UNIQUE transformed\n // match. Unlike the quote / line-number transforms (typography-only /\n // verbatim paste-back), this one rewrites the needle's meaning — a model\n // that genuinely meant the literal `<n>` against a file full of `<name>`\n // would otherwise silently retarget every occurrence under `replace_all`.\n // A unique hit bounds the blast radius to one visible replacement.\n const desan = desanitize(needle)\n if (desan !== needle) {\n const desanCount = countExactMatches(haystack, desan)\n if (desanCount === 1)\n return { actual: desan, occurrences: desanCount, via: 'desanitize' }\n }\n\n // 4. Combined: desanitize + quote normalization. Hit when the model\n // emitted both sanitized tokens and straight quotes, but the file has\n // the unsanitized form with curly quotes. Same uniqueness requirement\n // as #3.\n const combo = desanitize(normNeedle)\n if (combo !== needle) {\n const m = locateAndCount(haystack, normFile, combo, 'quotes+desanitize')\n if (m && m.occurrences === 1)\n return m\n }\n\n // 5. Line-number-prefix strip. When `read_file` returns line-numbered\n // output and the model pasted the chunk verbatim into `old_string`,\n // the on-disk file doesn't carry those prefixes. Strip them and\n // retry; the file-side `actual` is the prefix-free needle so the\n // replacement lands on real bytes.\n //\n // Guard against a needle that strips down to whitespace-only (e.g.\n // `1\\t\\n2\\t\\n3\\t` → `\\n\\n`). Searching for `\\n\\n` would match arbitrary\n // blank-line patterns anywhere in the file — a high false-positive risk.\n // Require the stripped needle to carry at least one non-whitespace\n // character before we trust the result.\n const stripped = stripLineNumberPrefixes(needle)\n if (stripped !== needle && stripped.trim().length > 0) {\n const count = countExactMatches(haystack, stripped)\n if (count > 0)\n return { actual: stripped, occurrences: count, via: 'line-numbers' }\n\n // 6. Combined: line-number strip + curly-quote normalization. The\n // typography mismatch may live in the file, the needle, or both.\n const strippedNorm = normalizeQuotes(stripped)\n if (strippedNorm !== stripped || normFile !== haystack) {\n const m = locateAndCount(haystack, normFile, strippedNorm, 'quotes+line-numbers')\n if (m)\n return m\n }\n }\n\n return null\n}\n\n/**\n * Short human-readable label for a recovery path, used by `edit` /\n * `multi_edit` to flag non-exact matches in their result messages — when a\n * fallback mistargets, the model (and the user reading the transcript) can\n * see WHICH transform fired instead of silently trusting the edit.\n */\nexport function describeVia(via: ResolvedMatch['via']): string {\n switch (via) {\n case 'exact': return 'exact match'\n case 'quotes': return 'curly-quote normalization'\n case 'desanitize': return 'sanitization recovery (e.g. <n> → <name>)'\n case 'quotes+desanitize': return 'sanitization recovery + curly-quote normalization'\n case 'line-numbers': return 'line-number-prefix stripping'\n case 'quotes+line-numbers': return 'line-number-prefix stripping + curly-quote normalization'\n }\n}\n\n/**\n * Apply the same recovery transforms used to find `old_string` to\n * `new_string`, so the file gets back its native form: desanitize when\n * the model emitted `<n>` for `<name>`, strip line-number prefixes when\n * the match required them, then re-curlify when the match required\n * quote normalization. Shared between `edit` and `multi_edit`.\n */\nexport function styleReplacementForVia(\n replacement: string,\n via: ResolvedMatch['via'],\n actual: string,\n): string {\n let out = replacement\n if (via === 'desanitize' || via === 'quotes+desanitize')\n out = desanitize(out)\n if (via === 'line-numbers' || via === 'quotes+line-numbers')\n out = stripLineNumberPrefixes(out)\n if (via === 'quotes' || via === 'quotes+desanitize' || via === 'quotes+line-numbers')\n out = preserveQuoteStyle(actual, out)\n return out\n}\n\n/**\n * When `old_string` matched via curly-quote normalization, re-style\n * `new_string` so the file's typography is preserved across the edit.\n * Detects whether the matched file region had curly singles, doubles, or\n * both, and applies the matching curlification to the replacement.\n *\n * Apostrophes in contractions (`don't`, `it's`) get the right-single curly\n * quote regardless of opening context — that's the canonical typographer's\n * convention for English. Other quotes use a simple\n * preceded-by-whitespace-or-opening-punctuation heuristic.\n */\nexport function preserveQuoteStyle(actual: string, replacement: string): string {\n const hasDouble = actual.includes(LEFT_DOUBLE_CURLY_QUOTE) || actual.includes(RIGHT_DOUBLE_CURLY_QUOTE)\n const hasSingle = actual.includes(LEFT_SINGLE_CURLY_QUOTE) || actual.includes(RIGHT_SINGLE_CURLY_QUOTE)\n if (!hasDouble && !hasSingle)\n return replacement\n\n let out = replacement\n if (hasDouble)\n out = applyCurly(out, '\"', LEFT_DOUBLE_CURLY_QUOTE, RIGHT_DOUBLE_CURLY_QUOTE, false)\n if (hasSingle)\n out = applyCurly(out, '\\'', LEFT_SINGLE_CURLY_QUOTE, RIGHT_SINGLE_CURLY_QUOTE, true)\n return out\n}\n\nfunction applyCurly(\n s: string,\n straight: string,\n left: string,\n right: string,\n contractionAware: boolean,\n): string {\n const chars = [...s]\n const result: string[] = []\n for (let i = 0; i < chars.length; i++) {\n if (chars[i] !== straight) {\n result.push(chars[i])\n continue\n }\n if (contractionAware) {\n const prev = i > 0 ? chars[i - 1] : ''\n const next = i < chars.length - 1 ? chars[i + 1] : ''\n // Letter-quote-letter is a contraction, not a quote — always right.\n if (/\\p{L}/u.test(prev) && /\\p{L}/u.test(next)) {\n result.push(right)\n continue\n }\n }\n result.push(isOpeningContext(chars, i) ? left : right)\n }\n return result.join('')\n}\n\nfunction isOpeningContext(chars: string[], i: number): boolean {\n if (i === 0)\n return true\n const prev = chars[i - 1]\n return prev === ' ' || prev === '\\t' || prev === '\\n' || prev === '\\r'\n || prev === '(' || prev === '[' || prev === '{'\n || prev === '\\u2014' || prev === '\\u2013' // em / en dash\n}\n","/**\n * Context-usage breakdown — pure transforms.\n *\n * Turns a {@link ContextSnapshot} (the raw pieces the agent assembled for a\n * run: system prompt, wire tools, deferred tools, MCP tools/instructions,\n * skills catalog, ...) plus the real last-turn token total into a categorized\n * {@link ContextBreakdown} the TUI panel / GUI popover render.\n *\n * No React, no node — a browser-context renderer imports these via\n * `zidane/chat/pure`. The snapshot is captured main-side (TUI direct, GUI in\n * `main/chat`) because building it reads provider/agent state; only the pure\n * shaping lives here.\n *\n * Accuracy: per-category counts are estimates when no provider `countTokens`\n * is available — the heuristic ({@link estimateTokens}, ~4 bytes/token) sizes\n * each static segment and the bar is reconciled so it always sums to the real\n * last-turn total (`conversation = realTotal - sum(static)`). When a provider\n * exposes exact counts (Anthropic, OpenAI), the caller passes them in via\n * {@link ContextSnapshot.exact} and the corresponding categories drop their\n * `estimated` flag. Exact buckets (free space, autocompact buffer) are never\n * estimated.\n */\n\nimport type { SessionTurn } from '../types'\nimport { estimateTokens } from '../compact/utils'\n\n/**\n * Stable identifiers for each breakdown category. Drives swatch color +\n * ordering in both UIs. `*Deferred` buckets are tools/MCP that exist but are\n * NOT currently on the wire (lazy disclosure); `autocompactBuffer` is the\n * reserved output headroom; `freeSpace` is the remaining window.\n */\nexport type ContextCategoryId\n = | 'systemPrompt'\n | 'rules'\n | 'tools'\n | 'mcpTools'\n | 'mcpInstructions'\n | 'skills'\n | 'subagentDefs'\n | 'conversation'\n | 'mcpToolsDeferred'\n | 'toolsDeferred'\n | 'autocompactBuffer'\n | 'freeSpace'\n\n/**\n * Exact last-turn token usage split (provider-reported — always exact). Drives\n * the cache breakdown shown below the categories. `cacheRead + cacheCreation +\n * input` equals the live context total (`used`).\n */\nexport interface ContextUsageSplit {\n /** Fresh, uncached input tokens this turn. */\n input: number\n /** Tokens served from the prompt cache (the cached prefix). */\n cacheRead: number\n /** Tokens written to the cache this turn. */\n cacheCreation: number\n /** Assistant output tokens (not part of the input window; informational). */\n output: number\n}\n\n/** One display row contributed by an extension to the context panel. */\nexport interface ContextExtensionRow {\n label: string\n value: string\n}\n\n/** Extension-owned context panel section. Core treats rows as opaque display data. */\nexport interface ContextExtensionSection {\n id: string\n label: string\n /** Optional section-title foreground color. UIs fall back to their theme accent. */\n accentColor?: string\n rows: readonly ContextExtensionRow[]\n}\n\n/** Data exposed to extension context-breakdown contributors. */\nexport interface ContextBreakdownContributionContext {\n turns: readonly SessionTurn[]\n}\n\n/** Extension hook for adding opaque rows to the context panel. */\nexport type ContextBreakdownContributor\n = (ctx: ContextBreakdownContributionContext) =>\n | ContextExtensionSection\n | readonly ContextExtensionSection[]\n | null\n | undefined\n | Promise<ContextExtensionSection | readonly ContextExtensionSection[] | null | undefined>\n\n/** One expandable child row under a category (e.g. a single MCP tool / memory file). */\nexport interface ContextItem {\n /** Stable-ish key for rendering (tool name, file path). */\n id: string\n /** Display label. */\n label: string\n /** Token count for this item. */\n tokens: number\n /** True when `tokens` came from the heuristic rather than an exact count. */\n estimated: boolean\n /** Tool description (tool items only) — what a UI shows on hover. */\n description?: string\n /** Owning group when the category flattens several (MCP server name). */\n group?: string\n}\n\n/** One category row in the breakdown. */\nexport interface ContextCategory {\n id: ContextCategoryId\n /** Display label (e.g. \"Tool definitions\"). */\n label: string\n /** Token count for the category. */\n tokens: number\n /** True when `tokens` came from the heuristic rather than an exact count. */\n estimated: boolean\n /**\n * True for buckets that are NOT counted against the live window total\n * (deferred tools, free space, autocompact buffer) — the UI renders these\n * dimmed / below a divider, mirroring Claude Code's layout.\n */\n deferred?: boolean\n /** Optional expandable child rows (per MCP tool, per memory file, ...). */\n items?: ContextItem[]\n}\n\n/** The shaped, render-ready breakdown. */\nexport interface ContextBreakdown {\n /** Model id the snapshot was built for. */\n modelId: string\n /**\n * Real last-turn context size (provider-reported `input + cacheRead +\n * cacheCreation`) — the SAME number the footer/orb shows. The non-deferred\n * categories always sum to this.\n */\n used: number\n /** Effective window (raw - output reserve), the bar's denominator. */\n effectiveWindow: number\n /** `used / effectiveWindow`, clamped to [0, 1]. */\n fraction: number\n /** Live categories (sum to `used`) followed by deferred + free-space buckets. */\n categories: ContextCategory[]\n /** True when ANY live category is estimated (drives the panel's footnote). */\n hasEstimates: boolean\n /**\n * Exact last-turn usage split (cache read / write / fresh input / output),\n * when the host supplied it. Always exact — rendered as an informational\n * section below the categories. Absent when no usage is available yet.\n */\n usage?: ContextUsageSplit\n /** Extension-owned context panel sections. */\n extensionSections?: readonly ContextExtensionSection[]\n /**\n * Names of currently-active skills. Informational — an activated skill's body\n * lands in the conversation (tool-results), so its tokens are already counted\n * under Conversation; this just surfaces \"which skills are loaded\". Empty/absent\n * when none are active.\n */\n activeSkills?: string[]\n}\n\n/** Host-supplied options for `agent.getContextBreakdown`. */\nexport interface ContextBreakdownOptions {\n /**\n * Effective context window (`rawWindow - outputReserve`) for the active\n * model, resolved by the host via the chat model registry. Drives free-space\n * + fraction. When omitted, defaults to `used` (no headroom shown).\n */\n effectiveWindow?: number\n /**\n * Active model id — used for the exact `countTokens` call + the header label.\n * Only consulted on the PRE-RUN path (no snapshot yet); once a run has fired,\n * the snapshot's own model wins. When omitted, the provider default is used.\n */\n model?: string\n /** Tokens reserved for output (autocompact buffer). Defaults to 0. */\n autocompactBuffer?: number\n /**\n * Auto-compact threshold as a fraction of the window (0–1), when enabled.\n * When set, the \"Autocompact buffer\" bucket represents the ENTIRE tail from\n * the threshold to the end of the window (`(1 − threshold) × window`) rather\n * than just the reserved-output tokens — so the bar/row show exactly where\n * compaction fires. When omitted, `autocompactBuffer` is used as-is.\n */\n compactThreshold?: number\n /** Abort signal forwarded to the provider's `countTokens`. */\n signal?: AbortSignal\n}\n\n/** Tools grouped by MCP server, for the expandable per-server rows. */\nexport interface ContextMcpGroup {\n server: string\n /** Wire tool specs for this server (their JSON size is measured). */\n tools: { name: string, json: string, description?: string }[]\n}\n\n/**\n * Exact per-segment counts from a provider's `countTokens`, when available.\n * Each field is the count of the cumulative payload up to and including that\n * segment, so categories are derived by differencing (see\n * {@link buildContextBreakdown}). Any subset may be present; missing fields\n * fall back to the heuristic for that category.\n */\nexport interface ContextExactCounts {\n /** Tokens for system prompt alone. */\n system?: number\n /** Tokens for system + tools (so tools = systemAndTools - system). */\n systemAndTools?: number\n /** Tokens for the full payload (system + tools + messages) = the real total. */\n full?: number\n}\n\n/**\n * Raw inputs captured at agent prompt-assembly time. Everything here is what\n * actually went on (or was withheld from) the wire for the last run.\n */\nexport interface ContextSnapshot {\n modelId: string\n /** Full rendered system prompt (marker-stripped, as sent). */\n system: string\n /**\n * Host-composed base system BEFORE the agent appended skills / searchable /\n * MCP-instructions catalogs. Includes the rules block. The \"System prompt\"\n * (base doctrine) bucket = base − rules − env-noise. Defaults to `system`\n * when not provided (back-compat).\n */\n baseSystem?: string\n /** Rendered AGENTS.md/CLAUDE.md rules block (a substring of `baseSystem`). */\n rulesBlock?: string\n /** Source files behind the rules block, for per-file rows. */\n rulesFiles?: { path: string, source: string }[]\n /** Wire tool specs currently disclosed to the model, as JSON strings. */\n toolsJson: string[]\n /**\n * Per-tool detail for `toolsJson`, same order, when the host has it —\n * lets the breakdown list native tools like it lists MCP tools. Optional so\n * SDK callers assembling a snapshot by hand are unaffected.\n */\n toolItems?: { name: string, json: string, description?: string }[]\n /** Lazy tool entries NOT currently disclosed (deferred), as JSON strings. */\n deferredToolsJson: string[]\n /** MCP tools grouped by server (disclosed ones). */\n mcpGroups: ContextMcpGroup[]\n /** Deferred (undisclosed) MCP tools grouped by server. */\n deferredMcpGroups: ContextMcpGroup[]\n /** Rendered MCP instructions block, if any. */\n mcpInstructions?: string\n /** Rendered skills catalog text, if any. */\n skillsCatalog?: string\n /** Searchable / subagent (spawn) tool definitions text, if any. */\n subagentDefs?: string\n /** Memory / rules files in context. */\n /** Real last-turn total (input + cacheRead + cacheCreation). */\n used: number\n /** Effective window (raw - output reserve). */\n effectiveWindow: number\n /** Tokens reserved for output (autocompact buffer). */\n autocompactBuffer: number\n /** Exact per-segment counts from the provider, when available. */\n exact?: ContextExactCounts\n /** Exact last-turn usage split, when available (passed straight through). */\n usage?: ContextUsageSplit\n /** Extension-owned context panel sections. */\n extensionSections?: readonly ContextExtensionSection[]\n /** Names of currently-active skills (passed straight through). */\n activeSkills?: string[]\n}\n\nconst LABELS: Record<ContextCategoryId, string> = {\n systemPrompt: 'System prompt',\n rules: 'Rules (AGENTS.md)',\n tools: 'Tool definitions',\n mcpTools: 'MCP tools',\n mcpInstructions: 'MCP instructions',\n skills: 'Skills',\n subagentDefs: 'Subagent definitions',\n conversation: 'Conversation',\n mcpToolsDeferred: 'MCP tools (deferred)',\n toolsDeferred: 'System tools (deferred)',\n autocompactBuffer: 'Autocompact buffer',\n freeSpace: 'Free space',\n}\n\n/** Sum the heuristic token sizes of a list of strings. */\nfunction sumEstimate(parts: readonly string[]): number {\n let total = 0\n for (const p of parts)\n total += estimateTokens(p)\n return total\n}\n\nfunction mcpGroupTokens(group: ContextMcpGroup): { total: number, items: ContextItem[] } {\n const items: ContextItem[] = group.tools.map(t => ({\n id: `${group.server}:${t.name}`,\n label: t.name,\n tokens: estimateTokens(t.json),\n estimated: true,\n ...(t.description ? { description: t.description } : {}),\n group: group.server,\n }))\n let total = 0\n for (const it of items)\n total += it.tokens\n return { total, items }\n}\n\n/**\n * Shape a {@link ContextSnapshot} into a render-ready {@link ContextBreakdown}.\n *\n * Live categories (system prompt, tools, MCP tools/instructions, skills,\n * memory, subagent defs, conversation) sum to `snapshot.used` — `conversation`\n * absorbs the remainder so the bar matches the provider-reported total exactly.\n * Deferred + autocompact + free-space buckets follow.\n *\n * When `snapshot.exact` carries provider counts, the matching categories use\n * them (system, tools) and drop their `estimated` flag; everything else stays\n * on the heuristic.\n */\nexport function buildContextBreakdown(snapshot: ContextSnapshot): ContextBreakdown {\n const exact = snapshot.exact\n const systemExact = typeof exact?.system === 'number'\n\n // --- System-embedded sub-sections (heuristic) ----------------------------\n // The exact `count_tokens` system number covers the WHOLE system text. The\n // appended catalogs (rules / skills / MCP instructions / subagent defs) all\n // live inside it, so we size each heuristically and let the \"System prompt\"\n // (base doctrine) bucket absorb the remainder — keeping the system group's\n // sum exact while still attributing each section.\n const rulesTokens = snapshot.rulesBlock ? estimateTokens(snapshot.rulesBlock) : 0\n const rulesItems: ContextItem[] = (snapshot.rulesFiles ?? []).map(f => ({\n id: f.path,\n label: f.path,\n // Per-file size isn't separable from the rendered block; show the path only\n // (tokens 0, not estimated — there's nothing to estimate) so the row lists\n // sources without implying a per-file count or a `~` marker.\n tokens: 0,\n estimated: false,\n }))\n const skillsTokens = snapshot.skillsCatalog ? estimateTokens(snapshot.skillsCatalog) : 0\n const mcpInstructionsTokens = snapshot.mcpInstructions ? estimateTokens(snapshot.mcpInstructions) : 0\n const subagentTokens = snapshot.subagentDefs ? estimateTokens(snapshot.subagentDefs) : 0\n const embeddedSubsections = rulesTokens + skillsTokens + mcpInstructionsTokens + subagentTokens\n\n // System TOTAL (whole system text) — exact when available.\n const systemTotal = systemExact ? exact!.system! : estimateTokens(snapshot.system)\n // Base doctrine = system total minus the attributed sub-sections.\n const baseSystemTokens = Math.max(0, systemTotal - embeddedSubsections)\n\n // --- Native tool definitions (non-MCP, disclosed) ------------------------\n // When exact `systemAndTools` is available it covers BOTH native + MCP tools,\n // so we split that combined number proportionally by heuristic weight rather\n // than claiming exactness per sub-bucket.\n const nativeToolsEst = sumEstimate(snapshot.toolsJson)\n const mcpDisclosed = snapshot.mcpGroups.map(mcpGroupTokens)\n const mcpToolsEst = mcpDisclosed.reduce((a, g) => a + g.total, 0)\n\n let nativeToolsTokens = nativeToolsEst\n let mcpToolsTokens = mcpToolsEst\n let toolsEstimated = true\n if (systemExact && typeof exact?.systemAndTools === 'number') {\n const exactToolsTotal = Math.max(0, exact.systemAndTools - exact.system!)\n const estTotal = nativeToolsEst + mcpToolsEst\n if (estTotal > 0) {\n nativeToolsTokens = Math.round(exactToolsTotal * (nativeToolsEst / estTotal))\n mcpToolsTokens = exactToolsTotal - nativeToolsTokens\n }\n else {\n nativeToolsTokens = exactToolsTotal\n mcpToolsTokens = 0\n }\n toolsEstimated = false\n }\n\n // --- Live categories -----------------------------------------------------\n // Detailed split. The system-embedded sub-sections (rules / skills / MCP\n // instructions / subagent defs) are heuristic; the \"System prompt\" base\n // bucket carries the exact remainder so the whole system group still sums to\n // the exact system count. Tools/MCP-tools/conversation are split as before.\n const live: ContextCategory[] = []\n const push = (id: ContextCategoryId, tokens: number, estimated: boolean, items?: ContextItem[]) => {\n if (tokens <= 0 && !items?.length)\n return\n live.push({ id, label: LABELS[id], tokens: Math.max(0, tokens), estimated, ...(items?.length ? { items } : {}) })\n }\n\n // System group: base doctrine (exact remainder) + attributed sub-sections.\n push('systemPrompt', baseSystemTokens, !systemExact)\n push('rules', rulesTokens, true, rulesItems)\n push('skills', skillsTokens, true)\n push('mcpInstructions', mcpInstructionsTokens, true)\n push('subagentDefs', subagentTokens, true)\n // Tools.\n push('tools', nativeToolsTokens, toolsEstimated, snapshot.toolItems?.map(t => ({\n id: t.name,\n label: t.name,\n tokens: estimateTokens(t.json),\n estimated: true,\n ...(t.description ? { description: t.description } : {}),\n })))\n push('mcpTools', mcpToolsTokens, toolsEstimated, mcpDisclosed.flatMap(g => g.items))\n\n // --- Conversation = remainder (keeps the bar summing to `used`) ----------\n const accountedStatic = live.reduce((a, c) => a + c.tokens, 0)\n const conversationTokens = Math.max(0, snapshot.used - accountedStatic)\n // Exact when both system AND tools were exactly counted — then the remainder\n // (used − exactSystem − exactTools) is itself exact, since `used` is the\n // provider-reported total.\n const conversationEstimated = !(systemExact && !toolsEstimated)\n push('conversation', conversationTokens, conversationEstimated)\n\n // --- Deferred buckets (not counted against `used`) -----------------------\n const deferred: ContextCategory[] = []\n const deferredNativeTools = sumEstimate(snapshot.deferredToolsJson)\n if (deferredNativeTools > 0) {\n deferred.push({ id: 'toolsDeferred', label: LABELS.toolsDeferred, tokens: deferredNativeTools, estimated: true, deferred: true })\n }\n const deferredMcp = snapshot.deferredMcpGroups.map(mcpGroupTokens)\n const deferredMcpTotal = deferredMcp.reduce((a, g) => a + g.total, 0)\n if (deferredMcpTotal > 0) {\n deferred.push({\n id: 'mcpToolsDeferred',\n label: LABELS.mcpToolsDeferred,\n tokens: deferredMcpTotal,\n estimated: true,\n deferred: true,\n items: deferredMcp.flatMap(g => g.items),\n })\n }\n\n // --- Autocompact buffer + free space (exact) -----------------------------\n if (snapshot.autocompactBuffer > 0) {\n deferred.push({ id: 'autocompactBuffer', label: LABELS.autocompactBuffer, tokens: snapshot.autocompactBuffer, estimated: false, deferred: true })\n }\n const free = Math.max(0, snapshot.effectiveWindow - snapshot.used)\n deferred.push({ id: 'freeSpace', label: LABELS.freeSpace, tokens: free, estimated: false, deferred: true })\n\n const categories = [...live, ...deferred]\n const hasEstimates = live.some(c => c.estimated && c.tokens > 0)\n const fraction = snapshot.effectiveWindow > 0\n ? Math.max(0, Math.min(1, snapshot.used / snapshot.effectiveWindow))\n : 0\n\n return {\n modelId: snapshot.modelId,\n used: snapshot.used,\n effectiveWindow: snapshot.effectiveWindow,\n fraction,\n categories,\n hasEstimates,\n ...(snapshot.usage ? { usage: snapshot.usage } : {}),\n ...(snapshot.extensionSections?.length ? { extensionSections: snapshot.extensionSections } : {}),\n ...(snapshot.activeSkills?.length ? { activeSkills: snapshot.activeSkills } : {}),\n }\n}\n","/**\n * Context segments — pure transforms.\n *\n * Sibling to `context-breakdown.ts`: where that module answers \"how many\n * tokens is each part of my context\", this one answers \"what IS each part,\n * and where did it come from\". It turns the same assembled-prompt pieces into\n * a flat list of {@link ContextSegment}s carrying the actual TEXT plus its\n * provenance, so a UI can list every chunk loaded by default in a session,\n * let the user read it, and open the file behind it when there is one.\n *\n * No React, no node — a browser-context renderer imports these via\n * `zidane/chat/pure`.\n *\n * Provenance is honest about what can and can't be opened:\n * - `'file'` — a real path on disk (AGENTS.md / CLAUDE.md / ruler md).\n * The only origin that carries `path`, hence the only one\n * a host can hand to `$EDITOR`.\n * - `'built-in'` — compiled into the binary (base doctrine, subagent defs).\n * - `'generated'` — assembled at prompt time from discovered state (the\n * skills catalog is rendered from the skill manifests).\n * - `'mcp'` — served by a remote MCP server at connect time.\n */\n\nimport { estimateTokens } from '../compact/utils'\n\n/** Where a segment's text came from. Only `'file'` segments carry a `path`. */\nexport type ContextSegmentOrigin = 'built-in' | 'file' | 'generated' | 'mcp'\n\n/** One chunk of text present in the session's default context. */\nexport interface ContextSegment {\n /** Stable key for rendering (`'system'`, `'rules:<path>'`, `'skills'`, ...). */\n id: string\n /** Display label (e.g. \"System prompt\", or the file's basename). */\n label: string\n origin: ContextSegmentOrigin\n /**\n * Short right-hand qualifier — the instruction scope for files, the server\n * list for MCP, a count for the skills catalog. Absent when there's nothing\n * useful to add beyond the origin badge.\n */\n detail?: string\n /** The text itself, as it sits in the prompt. Empty segments are dropped. */\n text: string\n /**\n * Absolute path of the file backing this segment. Present only for\n * `origin: 'file'` — a host uses it as the \"open in $EDITOR\" target.\n */\n path?: string\n /**\n * Heuristic token size (~4 bytes/token). Always an estimate: exact\n * per-segment counts aren't separable from the single system-prompt string\n * the provider counts, which is why {@link ContextSegment} carries no\n * `estimated` flag — every value here is one.\n */\n tokens: number\n}\n\n/**\n * A host-discovered instruction file. Structurally compatible with\n * `AgentsMdFile` from `./agents-md` (whose `source` field is the SCOPE, not\n * the text — `contents` is the text), so callers can pass those straight in.\n */\nexport interface ContextSegmentFile {\n path: string\n /** Instruction scope — `'project'` / `'user'`, surfaced as the row detail. */\n source?: string\n contents: string\n}\n\n/**\n * The assembled-prompt pieces {@link buildContextSegments} shapes. Mirrors the\n * subset of the agent's `ContextAssembly` that carries text, plus host-side\n * instruction files (which the assembly only knows as paths).\n */\nexport interface ContextSegmentsInput {\n /**\n * Host-composed base system BEFORE the agent appended its catalogs. The\n * rules block is a substring of this; the \"System prompt\" segment is the\n * remainder. Falls back to `system` when absent, which over-attributes the\n * appended catalogs into the base row rather than losing them.\n */\n baseSystem?: string\n /** Full rendered system prompt as sent. Used as the `baseSystem` fallback. */\n system?: string\n /**\n * Rendered instruction block (a substring of `baseSystem`). Subtracted from\n * the base so its text isn't reported twice — once as doctrine and once as\n * per-file rows.\n */\n rulesBlock?: string\n /** The files behind `rulesBlock`, each with its own text. */\n rulesFiles?: readonly ContextSegmentFile[]\n /**\n * Absolute path of a `SYSTEM.md` override backing the base prompt. When set,\n * the \"System prompt\" segment reports `origin: 'file'` and becomes openable\n * — the doctrine is the user's file, not compiled-in text.\n */\n systemPath?: string\n /** Rendered skills catalog (the per-skill blurbs, not skill bodies). */\n skillsCatalog?: string\n /** Rendered MCP instructions block. */\n mcpInstructions?: string\n /** Searchable / subagent (spawn) tool catalog text. */\n subagentDefs?: string\n}\n\n/**\n * Remove the first occurrence of `part` from `whole`, collapsing the blank-line\n * run left AT THE JOIN.\n *\n * Used to carve the rules block out of the base system so the \"System prompt\"\n * segment reports only doctrine + env. Substring-based because that's the\n * contract the assembly documents (`rulesBlock` IS a substring of\n * `baseSystem`); a miss leaves `whole` untouched, which degrades to\n * over-reporting the base row rather than dropping text on the floor.\n *\n * The collapse is deliberately scoped to the seam. An earlier version ran\n * `/\\n{3,}/g` over the whole result, which rewrote blank-line runs ANYWHERE in\n * the doctrine — and this tab exists precisely so the user can read what is in\n * context, not a reflowed approximation of it. Interior bytes now survive\n * verbatim; only the newlines that met when `part` was excised are normalized,\n * plus a `trim()` so a segment can't open or close on blank lines.\n */\nfunction removeFirst(whole: string, part: string): string {\n if (!part)\n return whole\n const at = whole.indexOf(part)\n if (at < 0)\n return whole\n const before = whole.slice(0, at)\n const after = whole.slice(at + part.length)\n const trailing = /\\n*$/.exec(before)?.[0] ?? ''\n const leading = /^\\n*/.exec(after)?.[0] ?? ''\n // Two newlines (one blank line) is the separator the prompt builder uses\n // between sections, so that's what the seam becomes when either side had any.\n const seam = trailing.length + leading.length >= 2 ? '\\n\\n' : `${trailing}${leading}`\n const head = before.slice(0, before.length - trailing.length)\n return `${head}${seam}${after.slice(leading.length)}`.trim()\n}\n\n/**\n * Best-effort display name for an instruction file: its basename, with the\n * parent directory prefixed when the basename alone would be ambiguous\n * (`AGENTS.md` appears at up to four paths per scope). Path-separator handling\n * covers both POSIX and Windows so this stays node-free.\n */\nfunction fileLabel(path: string): string {\n const parts = path.split(/[/\\\\]/).filter(Boolean)\n const base = parts.at(-1) ?? path\n const parent = parts.at(-2)\n // A dotted parent (`.agents`, `.zidane`, `.claude`, `.ruler`) is the\n // meaningful discriminator between same-named instruction files.\n return parent?.startsWith('.') ? `${parent}/${base}` : base\n}\n\n/**\n * Shape assembled-prompt pieces into a flat, render-ready segment list.\n *\n * Order is prompt order — base doctrine, then instruction files (broadest\n * scope first, as `discoverAgentsMd` returns them), then the appended\n * catalogs. Empty / whitespace-only pieces are dropped so the list never\n * shows a zero-byte row.\n */\nexport function buildContextSegments(input: ContextSegmentsInput): ContextSegment[] {\n const segments: ContextSegment[] = []\n const push = (seg: Omit<ContextSegment, 'tokens'>) => {\n if (!seg.text.trim())\n return\n segments.push({ ...seg, tokens: estimateTokens(seg.text) })\n }\n\n // --- Base doctrine -------------------------------------------------------\n // `baseSystem` minus the rules block. The per-file rows below carry the\n // instruction text, so leaving the block in would double-report it.\n const base = input.baseSystem ?? input.system ?? ''\n const overridden = input.systemPath !== undefined\n push({\n id: 'system',\n label: 'System prompt',\n // An override makes this a real file on disk; without one it's compiled in.\n origin: overridden ? 'file' : 'built-in',\n detail: overridden ? 'SYSTEM.md override + environment' : 'doctrine + environment',\n text: input.rulesBlock ? removeFirst(base, input.rulesBlock) : base,\n ...(input.systemPath !== undefined ? { path: input.systemPath } : {}),\n })\n\n // --- Instruction files ---------------------------------------------------\n // The only file-backed segments, and so the only openable ones.\n for (const file of input.rulesFiles ?? []) {\n push({\n id: `rules:${file.path}`,\n label: fileLabel(file.path),\n origin: 'file',\n ...(file.source ? { detail: file.source } : {}),\n text: file.contents,\n path: file.path,\n })\n }\n\n // --- Appended catalogs ---------------------------------------------------\n push({\n id: 'skills',\n label: 'Skills catalog',\n origin: 'generated',\n detail: 'name + description per skill (bodies load on activation)',\n text: input.skillsCatalog ?? '',\n })\n push({\n id: 'mcp-instructions',\n label: 'MCP instructions',\n origin: 'mcp',\n detail: 'served by the connected servers',\n text: input.mcpInstructions ?? '',\n })\n push({\n id: 'subagent-defs',\n label: 'Subagent definitions',\n origin: 'built-in',\n text: input.subagentDefs ?? '',\n })\n\n return segments\n}\n","/**\n * Credential-file key arithmetic, split from `./credentials` so the rules can\n * be enforced where credentials themselves must not be pulled in.\n *\n * `./credentials` reaches the provider registry (and through it the pi-ai\n * factories) for `credKeyOf`; the extension loader — an edge entrypoint that\n * validates descriptor keys at registration time — must stay clear of that\n * graph. This module is string arithmetic with no imports.\n */\n\n/**\n * Separator between a provider's canonical key and a slot qualifier. Every\n * non-canonical slot (`#alt`, `#account:N`) starts with it, so the character is\n * reserved in descriptor credential keys outright.\n */\nexport const CRED_KEY_SLOT_SEPARATOR = '#'\n\n/**\n * Reserved suffix marking a provider's SECOND credential slot.\n *\n * Not usable as (or as the tail of) a descriptor's credential key: `foo#alt`\n * holds `foo`'s alternate credential, so a provider claiming that name would\n * share storage with it.\n */\nexport const ALTERNATE_CRED_KEY_SUFFIX = '#alt'\n\n/**\n * Reserved infix marking one of a provider's ADDITIONAL OAuth accounts:\n * `foo#account:1`, `foo#account:2`, …\n */\nexport const ACCOUNT_CRED_KEY_INFIX = '#account:'\n\n/**\n * File key holding a provider's second credential, when it has one of each kind.\n *\n * The canonical key always holds the credential the user prefers; this sibling\n * holds the other. That one invariant is what makes preference selection free:\n * every existing reader (`applyApiKeyEnv`, the harness provider factories)\n * already resolves the canonical key, so \"switch priority\" is just moving two\n * objects between two keys — no precedence flag to interpret, no resolution\n * order to keep in sync across layers.\n *\n * A sibling key rather than a nested field because the canonical entry gets\n * REPLACED wholesale on token refresh; anything nested inside it would be\n * dropped by the next rotation. And older zidane versions look credentials up\n * by `credKeyOf`, so a suffixed key is simply invisible to them — they read the\n * preferred credential and behave exactly as before, which is why this needs no\n * migration or schema version.\n */\nexport function alternateCredKey(fileKey: string): string {\n return `${fileKey}${ALTERNATE_CRED_KEY_SUFFIX}`\n}\n\n/**\n * File key holding one of a provider's non-active OAuth accounts.\n *\n * Same sibling-key reasoning as {@link alternateCredKey}: the canonical key is\n * the active one, so a provider with three OAuth grants stores the active one\n * canonically and the other two under `#account:N`. `#alt` stays dedicated to\n * the other-kind (API key) alternate so files written by builds that predate\n * multi-account keep meaning the same thing.\n */\nexport function accountCredKey(fileKey: string, n: number): string {\n return `${fileKey}${ACCOUNT_CRED_KEY_INFIX}${n}`\n}\n\n/** True when `key` is one of `fileKey`'s `#account:N` slots. */\nexport function isAccountCredKey(fileKey: string, key: string): boolean {\n const prefix = `${fileKey}${ACCOUNT_CRED_KEY_INFIX}`\n return key.startsWith(prefix) && /^\\d+$/.test(key.slice(prefix.length))\n}\n\n/**\n * Smallest `#account:N` key of `fileKey` not present in `existing`.\n *\n * Deterministic rather than random so a file hand-edited or written by two\n * processes in sequence stays readable; the write lock keeps it collision-free.\n */\nexport function nextAccountCredKey(fileKey: string, existing: Iterable<string>): string {\n const taken = new Set(existing)\n for (let n = 1; ; n++) {\n const key = accountCredKey(fileKey, n)\n if (!taken.has(key))\n return key\n }\n}\n\n/**\n * Name shown for an OAuth account that was never given one.\n *\n * Also what a first OAuth login is stored under, so a second account has\n * something to be told apart from. Pre-multi-account files hold unnamed\n * grants; {@link oauthAccountLabel} makes them read the same.\n */\nexport const DEFAULT_OAUTH_ACCOUNT_LABEL = 'Default'\n\n/** Trimmed user-given account name, or {@link DEFAULT_OAUTH_ACCOUNT_LABEL}. */\nexport function oauthAccountLabel(credential: { label?: unknown }): string {\n const label = typeof credential.label === 'string' ? credential.label.trim() : ''\n return label || DEFAULT_OAUTH_ACCOUNT_LABEL\n}\n\n/**\n * True when a key claims a reserved slot namespace.\n *\n * Built-in keys are plain identifiers (`anthropic`, `openai-codex`), but\n * `ProviderDescriptor.key` / `credentialFileKey` are open strings — so the\n * reservation is checked, not assumed.\n */\nexport function isReservedCredKey(fileKey: string): boolean {\n return fileKey.includes(CRED_KEY_SLOT_SEPARATOR)\n}\n","/**\n * Panels: renderer-agnostic side-panel contributions.\n *\n * A panel is a tab in the host's side rail (TUI) / side panel (GUI). The\n * extension supplies DATA, never UI: `load()` returns a JSON-safe\n * `PanelContent` the host paints with its own components, so one\n * registration works in-process (TUI) and across the Electron IPC boundary\n * (GUI, where extensions run in the main process and the panel paints in\n * the renderer). `custom` items carry a mandatory markdown `fallback` for\n * hosts without the named renderer, the same invariant transcript custom\n * blocks follow.\n *\n * Interaction flows back as ids: rows and buttons name an `actionId`; the\n * host calls `onAction(actionId, payload)` where the extension runs.\n */\nimport type { JsonValue } from '../types'\nimport type { ExtensionArtifactRow } from './types'\n\n/** Per-surface icon: an iconify class for the GUI, a single glyph for the TUI. */\nexport interface PanelIcon {\n gui?: string\n tui?: string\n}\n\nexport interface PanelAction {\n id: string\n label: string\n /** `danger` renders in the destructive tint. */\n tone?: 'default' | 'danger'\n}\n\n/** One line in a panel, same presentation as an artifact row. */\nexport interface PanelRowItem extends ExtensionArtifactRow {\n kind: 'row'\n /** Stable key for the host; defaults to the title. */\n id?: string\n /** Fired via `onAction` on click / Enter. `href` opens externally when no action is set. */\n actionId?: string\n /** Extra data handed back with the action. */\n payload?: JsonValue\n}\n\nexport interface PanelMarkdownItem {\n kind: 'markdown'\n text: string\n}\n\nexport interface PanelKeyValueItem {\n kind: 'kv'\n rows: readonly { label: string, value: string }[]\n}\n\n/**\n * Host-specific rich view. The TUI resolves `renderer` against\n * `ctx.tui.registerRenderer` ids (`<extension>/<id>`, bare ids are\n * prefixed at registration); the GUI, and any host without the renderer,\n * paints `fallback` as markdown.\n */\nexport interface PanelCustomItem {\n kind: 'custom'\n renderer: string\n data?: JsonValue\n fallback: string\n}\n\nexport type PanelItem = PanelRowItem | PanelMarkdownItem | PanelKeyValueItem | PanelCustomItem\n\nexport interface PanelSection {\n id: string\n title?: string\n items: readonly PanelItem[]\n /** Collapsed by default when true. */\n collapsed?: boolean\n}\n\nexport interface PanelContent {\n /** Overrides the tab label inside the panel header. */\n title?: string\n /** Count / short tag shown on the tab. `0` and `''` hide it. */\n badge?: number | string\n sections: readonly PanelSection[]\n /** Buttons in the panel footer. */\n actions?: readonly PanelAction[]\n /** Shown when `sections` are empty. */\n empty?: string\n}\n\nexport interface PanelLoadContext {\n /** Active session id, when a session is attached. */\n sessionId?: string\n cwd?: string\n}\n\nexport interface ExtensionPanel {\n /** Stable id; namespaced `<extension>/<id>` at registration. */\n id: string\n label: string\n icon?: PanelIcon\n /** Produce the current content. Called on open, on `subscribe` emits, and on explicit refresh. */\n load: (ctx: PanelLoadContext) => PanelContent | Promise<PanelContent>\n /**\n * Live updates: call `emit()` whenever `load()` would return something\n * new. Return an unsubscribe. Hosts debounce emits and only reload while\n * the panel is visible.\n */\n subscribe?: (emit: () => void) => () => void\n /** Row / button callbacks. Runs where the extension runs. */\n onAction?: (actionId: string, payload: JsonValue | undefined, ctx: PanelLoadContext) => void | Promise<void>\n}\n\n/** Registry entry: the definition plus its owning extension. */\nexport interface ExtensionPanelEntry extends ExtensionPanel {\n owner: string\n}\n\n// ---------------------------------------------------------------------------\n// Normalisation\n// ---------------------------------------------------------------------------\n\n/** Caps applied before content crosses IPC or reaches a renderer. */\nexport const PANEL_CONTENT_LIMITS = {\n sections: 32,\n itemsPerSection: 200,\n actions: 8,\n /** Per string field. */\n textChars: 20_000,\n /** Whole payload, JSON-serialised. */\n bytes: 512 * 1024,\n} as const\n\nfunction str(v: unknown, max: number = PANEL_CONTENT_LIMITS.textChars): string | undefined {\n return typeof v === 'string' ? (v.length > max ? `${v.slice(0, max - 1)}…` : v) : undefined\n}\n\n/**\n * Coerce whatever `load()` returned into a well-formed `PanelContent`:\n * unknown item kinds and malformed entries are dropped, strings are\n * trimmed to the caps, and the whole thing is size-bounded. Never throws.\n */\nexport function normalizePanelContent(input: unknown): PanelContent {\n const empty: PanelContent = { sections: [] }\n if (!input || typeof input !== 'object')\n return empty\n const o = input as Record<string, unknown>\n\n const sections: PanelSection[] = []\n if (Array.isArray(o.sections)) {\n for (const raw of o.sections.slice(0, PANEL_CONTENT_LIMITS.sections)) {\n const s = normalizeSection(raw, sections.length)\n if (s)\n sections.push(s)\n }\n }\n\n const actions: PanelAction[] = []\n if (Array.isArray(o.actions)) {\n for (const raw of o.actions.slice(0, PANEL_CONTENT_LIMITS.actions)) {\n if (raw && typeof raw === 'object') {\n const a = raw as Record<string, unknown>\n const id = str(a.id, 200)\n const label = str(a.label, 200)\n if (id && label)\n actions.push({ id, label, ...(a.tone === 'danger' ? { tone: 'danger' as const } : {}) })\n }\n }\n }\n\n const out: PanelContent = { sections }\n const title = str(o.title, 200)\n if (title)\n out.title = title\n if (typeof o.badge === 'number' && Number.isFinite(o.badge))\n out.badge = o.badge\n else if (typeof o.badge === 'string' && o.badge)\n out.badge = o.badge.slice(0, 12)\n if (actions.length > 0)\n out.actions = actions\n const emptyText = str(o.empty, 500)\n if (emptyText)\n out.empty = emptyText\n\n return enforceBytes(out)\n}\n\nfunction normalizeSection(raw: unknown, index: number): PanelSection | null {\n if (!raw || typeof raw !== 'object')\n return null\n const s = raw as Record<string, unknown>\n const items: PanelItem[] = []\n if (Array.isArray(s.items)) {\n for (const item of s.items.slice(0, PANEL_CONTENT_LIMITS.itemsPerSection)) {\n const n = normalizeItem(item)\n if (n)\n items.push(n)\n }\n }\n const out: PanelSection = { id: str(s.id, 200) ?? `section-${index}`, items }\n const title = str(s.title, 200)\n if (title)\n out.title = title\n if (s.collapsed === true)\n out.collapsed = true\n return out\n}\n\nfunction normalizeItem(raw: unknown): PanelItem | null {\n if (!raw || typeof raw !== 'object')\n return null\n const i = raw as Record<string, unknown>\n switch (i.kind) {\n case 'row': {\n const title = str(i.title, 500)\n if (!title)\n return null\n const out: PanelRowItem = { kind: 'row', title }\n const id = str(i.id, 200)\n const subtitle = str(i.subtitle, 1000)\n const href = str(i.href, 2000)\n const status = str(i.status, 60)\n const actionId = str(i.actionId, 200)\n if (id)\n out.id = id\n if (subtitle)\n out.subtitle = subtitle\n if (href)\n out.href = href\n if (status)\n out.status = status\n if (actionId)\n out.actionId = actionId\n if (Array.isArray(i.badges))\n out.badges = i.badges.filter(isBadge).slice(0, 8)\n if (i.payload !== undefined && isJsonValue(i.payload))\n out.payload = i.payload\n return out\n }\n case 'markdown': {\n const text = str(i.text)\n return text === undefined ? null : { kind: 'markdown', text }\n }\n case 'kv': {\n if (!Array.isArray(i.rows))\n return null\n const rows = i.rows\n .map((r) => {\n if (!r || typeof r !== 'object')\n return null\n const row = r as Record<string, unknown>\n const label = str(row.label, 200)\n const value = str(row.value, 2000)\n return label !== undefined && value !== undefined ? { label, value } : null\n })\n .filter((r): r is { label: string, value: string } => r !== null)\n .slice(0, PANEL_CONTENT_LIMITS.itemsPerSection)\n return { kind: 'kv', rows }\n }\n case 'custom': {\n const renderer = str(i.renderer, 200)\n const fallback = str(i.fallback)\n if (!renderer || fallback === undefined)\n return null\n const out: PanelCustomItem = { kind: 'custom', renderer, fallback }\n if (i.data !== undefined && isJsonValue(i.data))\n out.data = i.data\n return out\n }\n default:\n return null\n }\n}\n\nfunction isBadge(v: unknown): v is NonNullable<ExtensionArtifactRow['badges']>[number] {\n return Boolean(v && typeof v === 'object' && typeof (v as { text?: unknown }).text === 'string')\n}\n\nfunction isJsonValue(v: unknown): v is JsonValue {\n try {\n JSON.stringify(v)\n return v !== undefined && typeof v !== 'function' && typeof v !== 'symbol'\n }\n catch {\n return false\n }\n}\n\n/**\n * Trim sections from the end until the payload fits the byte budget. A\n * panel that overflows keeps its first sections and gets an `empty`-style\n * note appended so the truncation is visible.\n */\nfunction enforceBytes(content: PanelContent): PanelContent {\n const size = (c: PanelContent) => JSON.stringify(c).length\n if (size(content) <= PANEL_CONTENT_LIMITS.bytes)\n return content\n const sections = [...content.sections]\n while (sections.length > 0 && size({ ...content, sections }) > PANEL_CONTENT_LIMITS.bytes)\n sections.pop()\n return {\n ...content,\n sections: [\n ...sections,\n { id: 'truncated', items: [{ kind: 'markdown', text: '_Panel content truncated (too large)._' }] },\n ],\n }\n}\n\n/** Tab badge as shown next to the label; `undefined` when nothing to show. */\nexport function panelBadgeText(badge: PanelContent['badge']): string | undefined {\n if (badge === undefined || badge === 0 || badge === '')\n return undefined\n if (typeof badge === 'number')\n return badge > 99 ? '99+' : String(badge)\n return badge\n}\n"],"mappings":"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"}
|
package/dist/plugins.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $c as PluginAuthor, Gc as parsePluginManifest, Hc as PLUGIN_MCP_FILE, Jc as defaultPluginDataRoot, Kc as validatePluginManifest, Qc as LoadedPlugin, Uc as expandPluginVars, Vc as PLUGIN_SKILLS_DIR, Wc as ManifestValidation, Xc as AgentPluginsVersion, Yc as loadAgentPlugin, Zc as LoadAgentPluginOptions, al as agentPluginsSchemaId, el as PluginComponentStatus, il as SUPPORTED_AGENT_PLUGINS_VERSIONS, nl as PluginManifest, ol as isSupportedAgentPluginsVersion, qc as PLUGIN_MANIFEST_FILE, rl as PluginSchemaKind, sl as parseSchemaId, tl as PluginDiagnostic } from "./index-dXnPmqFx.js";
|
|
2
2
|
export { type AgentPluginsVersion, type LoadAgentPluginOptions, type LoadedPlugin, type ManifestValidation, PLUGIN_MANIFEST_FILE, PLUGIN_MCP_FILE, PLUGIN_SKILLS_DIR, type PluginAuthor, type PluginComponentStatus, type PluginDiagnostic, type PluginManifest, type PluginSchemaKind, SUPPORTED_AGENT_PLUGINS_VERSIONS, agentPluginsSchemaId, defaultPluginDataRoot, expandPluginVars, isSupportedAgentPluginsVersion, loadAgentPlugin, parsePluginManifest, parseSchemaId, validatePluginManifest };
|
package/dist/presets.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Ji as AgentHookMap, Ki as _default, a as basicTools, i as _default$1, n as composePresets, r as definePreset, t as Preset } from "./index-
|
|
1
|
+
import { Ji as AgentHookMap, Ki as _default, a as basicTools, i as _default$1, n as composePresets, r as definePreset, t as Preset } from "./index-dXnPmqFx.js";
|
|
2
2
|
export { type AgentHookMap, Preset, _default as artifacts, _default$1 as basic, basicTools, composePresets, definePreset };
|
package/dist/presets.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Ct as basic_default, St as basicTools, Xt as artifacts_default, bt as composePresets, xt as definePreset } from "./agent-
|
|
1
|
+
import { Ct as basic_default, St as basicTools, Xt as artifacts_default, bt as composePresets, xt as definePreset } from "./agent-B6etKd_o.js";
|
|
2
2
|
export { artifacts_default as artifacts, basic_default as basic, basicTools, composePresets, definePreset };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $h as cacheWriteTtlMultiplier, Gh as AnthropicParams, Jh as THINKING_BINDING_SETTING_ENV, Kh as AnthropicThinkingPlan, Qh as buildAnthropicClientOptions, Uh as AnthropicBedrockConfig, Wh as AnthropicContextManagement, Xh as anthropic, Yh as ThinkingBindingBehavior, Zh as applyAnthropicCacheBreakpoints, ag as resolveAnthropicBetas, eg as classifyAnthropicError, ig as resolveAnthropicAuthShape, ng as defaultAnthropicMaxTokens, og as resolveAnthropicMaxOutput, qh as EXTENDED_CACHE_TTL_BETA, rg as planAnthropicThinking, sg as resolveThinkingBinding, tg as credentialIdentity } from "../index-dXnPmqFx.js";
|
|
2
2
|
export { AnthropicBedrockConfig, AnthropicContextManagement, AnthropicParams, AnthropicThinkingPlan, EXTENDED_CACHE_TTL_BETA, THINKING_BINDING_SETTING_ENV, ThinkingBindingBehavior, anthropic, applyAnthropicCacheBreakpoints, buildAnthropicClientOptions, cacheWriteTtlMultiplier, classifyAnthropicError, credentialIdentity, defaultAnthropicMaxTokens, planAnthropicThinking, resolveAnthropicAuthShape, resolveAnthropicBetas, resolveAnthropicMaxOutput, resolveThinkingBinding };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Hh as arcee, Vh as ArceeParams } from "../index-dXnPmqFx.js";
|
|
2
2
|
export { ArceeParams, arcee };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Bh as planBasetenReasoning, Rh as BasetenParams, zh as baseten } from "../index-dXnPmqFx.js";
|
|
2
2
|
export { BasetenParams, baseten, planBasetenReasoning };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Ih as CerebrasParams, Lh as cerebras } from "../index-dXnPmqFx.js";
|
|
2
2
|
export { CerebrasParams, cerebras };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Mh as LocalParams, Nh as local } from "../index-dXnPmqFx.js";
|
|
2
2
|
export { LocalParams, local };
|