zidane 5.6.15 → 5.7.4

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.
Files changed (118) hide show
  1. package/README.md +3 -1
  2. package/dist/{agent-CkJp_ZOR.d.ts → agent-BNS2nx_T.d.ts} +535 -15
  3. package/dist/agent-BNS2nx_T.d.ts.map +1 -0
  4. package/dist/chat/pure.d.ts +4 -0
  5. package/dist/chat/pure.js +3 -0
  6. package/dist/chat.d.ts +31 -661
  7. package/dist/chat.d.ts.map +1 -1
  8. package/dist/chat.js +5 -3
  9. package/dist/chat.js.map +1 -1
  10. package/dist/contexts/docker.d.ts +1 -1
  11. package/dist/contexts/docker.d.ts.map +1 -1
  12. package/dist/contexts/docker.js.map +1 -1
  13. package/dist/{contexts-BOtMvzli.js → contexts-BD2U_xpi.js} +2 -2
  14. package/dist/{contexts-BOtMvzli.js.map → contexts-BD2U_xpi.js.map} +1 -1
  15. package/dist/contexts.d.ts +3 -3
  16. package/dist/contexts.js +1 -1
  17. package/dist/edit-utils-DnfNoj16.js +574 -0
  18. package/dist/edit-utils-DnfNoj16.js.map +1 -0
  19. package/dist/{errors-DdZXnyXE.js → errors-CoQnKRf1.js} +32 -2
  20. package/dist/{errors-DdZXnyXE.js.map → errors-CoQnKRf1.js.map} +1 -1
  21. package/dist/fetch-url-CPxfiXDa.js +518 -0
  22. package/dist/fetch-url-CPxfiXDa.js.map +1 -0
  23. package/dist/{image-sniff-CmlNMPMr.js → image-sniff-B7uFSNO1.js} +1 -1
  24. package/dist/{image-sniff-CmlNMPMr.js.map → image-sniff-B7uFSNO1.js.map} +1 -1
  25. package/dist/{index-CtIS28mN.d.ts → index-CZOwAJIX.d.ts} +2 -2
  26. package/dist/index-CZOwAJIX.d.ts.map +1 -0
  27. package/dist/{index-CsWckg9p.d.ts → index-Ck_AWt8P.d.ts} +3 -4
  28. package/dist/index-Ck_AWt8P.d.ts.map +1 -0
  29. package/dist/{index-BXQC3I4d.d.ts → index-KiS7w0dC.d.ts} +3 -3
  30. package/dist/index-KiS7w0dC.d.ts.map +1 -0
  31. package/dist/index.d.ts +6 -6
  32. package/dist/index.js +13 -12
  33. package/dist/index.js.map +1 -1
  34. package/dist/{interpolate-C55ZIcKz.js → interpolate-TySiqKzc.js} +23 -23
  35. package/dist/{interpolate-C55ZIcKz.js.map → interpolate-TySiqKzc.js.map} +1 -1
  36. package/dist/{login-CY9uShjX.js → login-BDeqENSe.js} +7 -58
  37. package/dist/login-BDeqENSe.js.map +1 -0
  38. package/dist/{mcp-DDOc8hOM.js → mcp-Kqzz-Rs_.js} +5 -5
  39. package/dist/{mcp-DDOc8hOM.js.map → mcp-Kqzz-Rs_.js.map} +1 -1
  40. package/dist/mcp.d.ts +2 -2
  41. package/dist/mcp.js +1 -1
  42. package/dist/{messages-B-tuI2Ur.js → messages-CvRQTdbR.js} +93 -30
  43. package/dist/messages-CvRQTdbR.js.map +1 -0
  44. package/dist/{presets-CMkLtFFW.js → presets-JuOnSI-i.js} +2 -2
  45. package/dist/{presets-CMkLtFFW.js.map → presets-JuOnSI-i.js.map} +1 -1
  46. package/dist/presets.d.ts +3 -3
  47. package/dist/presets.js +1 -1
  48. package/dist/{providers-CRQQDuxx.js → providers-h4HJPbbv.js} +485 -31
  49. package/dist/providers-h4HJPbbv.js.map +1 -0
  50. package/dist/providers.d.ts +2 -2
  51. package/dist/providers.js +3 -3
  52. package/dist/restate.d.ts +1 -1
  53. package/dist/restate.d.ts.map +1 -1
  54. package/dist/restate.js.map +1 -1
  55. package/dist/session/sqlite.d.ts +1 -1
  56. package/dist/session/sqlite.d.ts.map +1 -1
  57. package/dist/session/sqlite.js +1 -1
  58. package/dist/session/sqlite.js.map +1 -1
  59. package/dist/{session-BCT6eYxo.js → session-BzLou2_-.js} +2 -2
  60. package/dist/{session-BCT6eYxo.js.map → session-BzLou2_-.js.map} +1 -1
  61. package/dist/session.d.ts +2 -2
  62. package/dist/session.js +2 -2
  63. package/dist/skills.d.ts +3 -3
  64. package/dist/skills.js +1 -1
  65. package/dist/skills.js.map +1 -1
  66. package/dist/{stats-CIv4j3Sz.js → stats-DAKBEKjc.js} +12 -2
  67. package/dist/stats-DAKBEKjc.js.map +1 -0
  68. package/dist/{stdio-loader-OOOXzUvm.js → stdio-loader-Ce68wUmM.js} +4 -4
  69. package/dist/stdio-loader-Ce68wUmM.js.map +1 -0
  70. package/dist/tool-formatters-CU-j3a3e.d.ts +1471 -0
  71. package/dist/tool-formatters-CU-j3a3e.d.ts.map +1 -0
  72. package/dist/tools/fetch-url.d.ts +70 -0
  73. package/dist/tools/fetch-url.d.ts.map +1 -0
  74. package/dist/tools/fetch-url.js +2 -0
  75. package/dist/tools/web-search.d.ts +7 -0
  76. package/dist/tools/web-search.d.ts.map +1 -0
  77. package/dist/tools/web-search.js +190 -0
  78. package/dist/tools/web-search.js.map +1 -0
  79. package/dist/{tools-0Kolu2bY.js → tools-BGtJK0vo.js} +1365 -420
  80. package/dist/tools-BGtJK0vo.js.map +1 -0
  81. package/dist/tools.d.ts +3 -3
  82. package/dist/tools.js +1 -1
  83. package/dist/{turn-operations-DkLoiyF4.js → transcript-anchors-BTSZAPVc.js} +147 -2713
  84. package/dist/transcript-anchors-BTSZAPVc.js.map +1 -0
  85. package/dist/{transcript-anchors-C8IqWH4x.d.ts → transcript-anchors-DX90kXc4.d.ts} +13 -1299
  86. package/dist/transcript-anchors-DX90kXc4.d.ts.map +1 -0
  87. package/dist/tui.d.ts +58 -28
  88. package/dist/tui.d.ts.map +1 -1
  89. package/dist/tui.js +1348 -422
  90. package/dist/tui.js.map +1 -1
  91. package/dist/turn-operations-CCHfR9eC.js +1938 -0
  92. package/dist/turn-operations-CCHfR9eC.js.map +1 -0
  93. package/dist/turn-operations-DDIl4YVk.d.ts +658 -0
  94. package/dist/turn-operations-DDIl4YVk.d.ts.map +1 -0
  95. package/dist/{types-oKPBdCmL.js → types-BPw_i5vb.js} +1 -1
  96. package/dist/types-BPw_i5vb.js.map +1 -0
  97. package/dist/{types-2PMY5Rlc.d.ts → types-CEAMIUXw.d.ts} +1 -1
  98. package/dist/types-CEAMIUXw.d.ts.map +1 -0
  99. package/dist/types.d.ts +4 -4
  100. package/dist/types.js +3 -3
  101. package/docs/CHAT.md +53 -6
  102. package/docs/SKILL.md +3 -0
  103. package/docs/TUI.md +7 -0
  104. package/package.json +18 -2
  105. package/dist/agent-CkJp_ZOR.d.ts.map +0 -1
  106. package/dist/index-BXQC3I4d.d.ts.map +0 -1
  107. package/dist/index-CsWckg9p.d.ts.map +0 -1
  108. package/dist/index-CtIS28mN.d.ts.map +0 -1
  109. package/dist/login-CY9uShjX.js.map +0 -1
  110. package/dist/messages-B-tuI2Ur.js.map +0 -1
  111. package/dist/providers-CRQQDuxx.js.map +0 -1
  112. package/dist/stats-CIv4j3Sz.js.map +0 -1
  113. package/dist/stdio-loader-OOOXzUvm.js.map +0 -1
  114. package/dist/tools-0Kolu2bY.js.map +0 -1
  115. package/dist/transcript-anchors-C8IqWH4x.d.ts.map +0 -1
  116. package/dist/turn-operations-DkLoiyF4.js.map +0 -1
  117. package/dist/types-2PMY5Rlc.d.ts.map +0 -1
  118. package/dist/types-oKPBdCmL.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turn-operations-CCHfR9eC.js","names":[],"sources":["../src/chat/color-gradient.ts","../src/chat/completion-core.ts","../src/chat/completion-files.ts","../src/chat/completion-skills.ts","../src/chat/edit-approval.ts","../src/chat/edit-diff.ts","../src/chat/turn-selection.ts","../src/chat/model-catalog.ts","../src/chat/prompt-segments.ts","../src/chat/streaming-pure.ts","../src/chat/tool-formatters.ts","../src/chat/turn-operations.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// HSL color gradient helpers — shared between the Crush throbber (cycling\n// per-frame paint) and the title overlay (static per-character paint).\n//\n// Renderer-agnostic on purpose: a future GUI shell wants the same ramp\n// math against the same hex strings the theme palette already stores, so\n// this lives in `zidane/chat` next to the theme definition rather than\n// inside the TUI's OpenTUI layer.\n//\n// We blend in HSL with shortest-path hue interpolation rather than\n// straight sRGB lerp because saturated palette pairs (e.g. Charple →\n// Dolly = purple → magenta) would muddy through grey when interpolated\n// componentwise in RGB. HSL keeps the path along the hue ring, so the\n// gradient stays vivid.\n// ---------------------------------------------------------------------------\n\n/** Parse `#rrggbb` (case-insensitive) into `[r, g, b]` 0–255 integers. */\nfunction parseHex(hex: string): [number, number, number] {\n const h = hex.replace('#', '')\n return [\n Number.parseInt(h.slice(0, 2), 16),\n Number.parseInt(h.slice(2, 4), 16),\n Number.parseInt(h.slice(4, 6), 16),\n ]\n}\n\n/** Convert sRGB 0–255 → HSL 0–1. */\nfunction rgbToHsl(r: number, g: number, b: number): [number, number, number] {\n r /= 255\n g /= 255\n b /= 255\n const max = Math.max(r, g, b)\n const min = Math.min(r, g, b)\n const l = (max + min) / 2\n if (max === min)\n return [0, 0, l]\n const d = max - min\n const s = l > 0.5 ? d / (2 - max - min) : d / (max + min)\n let h: number\n if (max === r)\n h = (g - b) / d + (g < b ? 6 : 0)\n else if (max === g)\n h = (b - r) / d + 2\n else\n h = (r - g) / d + 4\n return [h / 6, s, l]\n}\n\n/** Convert HSL 0–1 → sRGB 0–255. Standard piecewise formula. */\nfunction hslToRgb(h: number, s: number, l: number): [number, number, number] {\n if (s === 0)\n return [l * 255, l * 255, l * 255]\n const hue2rgb = (p: number, q: number, t: number) => {\n if (t < 0)\n t += 1\n if (t > 1)\n t -= 1\n if (t < 1 / 6)\n return p + (q - p) * 6 * t\n if (t < 1 / 2)\n return q\n if (t < 2 / 3)\n return p + (q - p) * (2 / 3 - t) * 6\n return p\n }\n const q = l < 0.5 ? l * (1 + s) : l + s - l * s\n const p = 2 * l - q\n return [\n hue2rgb(p, q, h + 1 / 3) * 255,\n hue2rgb(p, q, h) * 255,\n hue2rgb(p, q, h - 1 / 3) * 255,\n ]\n}\n\nfunction toHex(rgb: readonly [number, number, number]): string {\n const pad = (v: number) => Math.round(Math.max(0, Math.min(255, v))).toString(16).padStart(2, '0')\n return `#${pad(rgb[0])}${pad(rgb[1])}${pad(rgb[2])}`\n}\n\n/**\n * Blend two hex colors in HSL space with shortest-path hue interpolation.\n * `t` ∈ [0, 1]; `t=0` returns `from`, `t=1` returns `to`.\n */\nexport function blendHsl(from: string, to: string, t: number): string {\n const [r1, g1, b1] = parseHex(from)\n const [r2, g2, b2] = parseHex(to)\n const [h1, s1, l1] = rgbToHsl(r1, g1, b1)\n const [h2, s2, l2] = rgbToHsl(r2, g2, b2)\n // Shortest path around the hue wheel — purple→pink shouldn't detour\n // through cyan just because the raw subtraction is positive.\n let dh = h2 - h1\n if (dh > 0.5)\n dh -= 1\n else if (dh < -0.5)\n dh += 1\n const h = (h1 + dh * t + 1) % 1\n const s = s1 + (s2 - s1) * t\n const l = l1 + (l2 - l1) * t\n return toHex(hslToRgb(h, s, l))\n}\n\n/**\n * Static gradient ramp of length `n` going from `from` (index 0) to\n * `to` (index n-1) in HSL space. For the cycling A→B→A→B ramp the\n * throbber uses, see `buildCycleRamp` in `src/tui/crush-throbber.tsx`.\n */\nexport function buildLinearRamp(from: string, to: string, n: number): string[] {\n if (n <= 0)\n return []\n if (n === 1)\n return [blendHsl(from, to, 0.5)]\n const ramp: string[] = []\n for (let i = 0; i < n; i++)\n ramp.push(blendHsl(from, to, i / (n - 1)))\n return ramp\n}\n","/**\n * Prompt autocompletion framework — pure core.\n *\n * Provider contract types + the renderer-agnostic span/reference helpers\n * (`findActiveTrigger`, `applyInsert`, `mergeReferences`, `collectReferences`).\n * No React, no node — so a browser-context renderer imports these via\n * `zidane/chat/pure`. The `useCompletion` React hook lives in `./completion`\n * (main barrel) and re-exports everything here for back-compat.\n *\n * Providers plug in by registering a `trigger` character (e.g. `/` for skills,\n * `@` for files) and exposing two operations:\n *\n * 1. `suggest(query)` — return ranked items for the live query.\n * 2. `parseReferences(text)` — find all references to the provider's\n * items in arbitrary text. Used to highlight in-prompt mentions and\n * drive submit-time side effects (activate the skill, attach the\n * file, …).\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * A discoverable, selectable thing — one row in the autocomplete popover.\n * `TItem` is provider-specific; consumers can inspect `data` when they\n * need the originating payload (e.g. the full `SkillConfig` for tooltips).\n */\nexport interface CompletionItem<TItem = unknown> {\n /** Stable identifier within the provider. Used as React key + selection equality. */\n id: string\n /** User-visible primary string (\"research\"). */\n label: string\n /** Optional one-line secondary string (\"In-depth research with citations\"). */\n description?: string\n /**\n * Text that replaces the active span (trigger + query) on commit. Usually\n * `${trigger}${label}` with a trailing space so the cursor lands ready\n * for the next token.\n */\n insertText: string\n /** Original provider-specific payload. */\n data: TItem\n}\n\n/**\n * A reference to a provider item inside arbitrary prompt text. Producers:\n * `provider.parseReferences(text)`. Consumers: the TUI for highlighting,\n * the run flow for \"activate every referenced skill before agent.run()\".\n *\n * Spans are half-open `[start, end)` codepoint offsets into the source\n * string. Overlapping spans from the same or different providers are\n * caller-resolved — the helpers below ship a \"first wins\" merger.\n */\nexport interface CompletionReference<TItem = unknown> {\n providerId: string\n start: number\n end: number\n itemId: string\n data: TItem\n}\n\n/**\n * Provider contract. Implementations decide their own ranking, fuzzy-match\n * rules, async loading behavior, and reference grammar.\n */\nexport interface CompletionProvider<TItem = unknown> {\n /** Stable id used for tagging references + React keys. */\n id: string\n /**\n * Single character that activates this provider. The engine considers a\n * trigger \"active\" when it appears at the start of the buffer or\n * immediately after whitespace, and is not closed by whitespace before\n * the cursor (so a trigger plus query is a contiguous token).\n */\n trigger: string\n /** Human-readable name. Reserved for future multi-provider popover headers. */\n label: string\n /**\n * Returns items for the active `query` (the text between the trigger and\n * the cursor, excluding the trigger itself). Synchronous return is the\n * common case; promises let providers paginate or hit a backend.\n *\n * `signal` is aborted when the query changes or the popover closes —\n * use it to cancel in-flight network calls.\n */\n suggest: (\n query: string,\n ctx: CompletionContext,\n signal: AbortSignal,\n ) => CompletionItem<TItem>[] | Promise<CompletionItem<TItem>[]>\n /**\n * Find every reference to this provider's items in `text`. Pure: must not\n * mutate ctx. The TUI calls this on every keystroke for highlighting, so\n * keep it cheap (linear scan, no I/O).\n */\n parseReferences: (\n text: string,\n ctx: CompletionContext,\n ) => CompletionReference<TItem>[]\n}\n\n/**\n * Read-only view of the active prompt buffer + cursor passed to provider\n * callbacks. Kept minimal so providers stay portable across renderers.\n */\nexport interface CompletionContext {\n /** Full prompt text. */\n text: string\n /** Codepoint offset of the cursor (0-based). */\n cursor: number\n}\n\n/**\n * Identified active trigger span. Returned by `findActiveTrigger` so\n * callers can show the popover, query the provider, and on commit replace\n * the span with the selected item's `insertText`.\n */\nexport interface ActiveTrigger<TItem = unknown> {\n provider: CompletionProvider<TItem>\n /** Substring after the trigger, up to the cursor. Empty if cursor sits right after the trigger. */\n query: string\n /** `[start, end)` — span covered by the trigger + query in the source. */\n span: { start: number, end: number }\n}\n\n// ---------------------------------------------------------------------------\n// Pure helpers — exported for unit tests + alternate renderers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the provider trigger active at `cursor`, or `null` when none fits.\n *\n * Rules:\n * - The trigger character must sit at position 0 of the buffer OR be\n * preceded by whitespace. This prevents `http://` from triggering the\n * `/`-bound skills provider mid-URL.\n * - The cursor must be at or past the trigger position.\n * - Nothing between the trigger and the cursor may be whitespace (the\n * query is one contiguous token).\n * - The query length is bounded — `maxQueryLength` defaults to 64 — so\n * a runaway buffer scan can't pin the renderer.\n */\nexport function findActiveTrigger<TItem>(\n text: string,\n cursor: number,\n providers: readonly CompletionProvider<TItem>[],\n options: { maxQueryLength?: number } = {},\n): ActiveTrigger<TItem> | null {\n if (providers.length === 0)\n return null\n const max = options.maxQueryLength ?? 64\n const safeCursor = Math.max(0, Math.min(cursor, text.length))\n // Whitespace test mirrors regex `\\s` so this surface agrees with the\n // built-in providers' `parseReferences` regex (which uses `\\s`). Without\n // this, pasting `text\\u00a0@README.md` would parse as a ref but the popover\n // wouldn't reopen on retype because NBSP wasn't recognized as a boundary.\n const isWhitespace = (ch: string | undefined) => ch === undefined ? false : /\\s/.test(ch)\n // Walk backward from the cursor to the nearest trigger char. Bail when\n // we hit whitespace (query token closed) or exceed `max`.\n for (let i = safeCursor - 1; i >= 0 && safeCursor - i <= max + 1; i--) {\n const ch = text[i]\n if (isWhitespace(ch))\n return null\n const provider = providers.find(p => p.trigger === ch)\n if (!provider)\n continue\n // Trigger must be at start of buffer or follow whitespace.\n const before = i > 0 ? text[i - 1] : ''\n if (before !== '' && !isWhitespace(before))\n continue\n const query = text.slice(i + 1, safeCursor)\n return {\n provider,\n query,\n span: { start: i, end: safeCursor },\n }\n }\n return null\n}\n\n/**\n * Replace `[span.start, span.end)` in `text` with `insertText`. Returns the\n * mutated text and the new cursor position (end of insertion).\n */\nexport function applyInsert(\n text: string,\n span: { start: number, end: number },\n insertText: string,\n): { text: string, cursor: number } {\n const next = text.slice(0, span.start) + insertText + text.slice(span.end)\n return { text: next, cursor: span.start + insertText.length }\n}\n\n/**\n * Merge reference lists from multiple providers into one ordered list with\n * earlier-start-wins disambiguation when spans overlap. Ties broken by\n * insertion order. Spans are sorted ascending so renderers can walk them\n * sequentially with a cursor through the source string.\n */\nexport function mergeReferences<TItem>(\n refs: readonly CompletionReference<TItem>[],\n): CompletionReference<TItem>[] {\n const sorted = [...refs].sort((a, b) => a.start - b.start)\n const merged: CompletionReference<TItem>[] = []\n let lastEnd = -1\n for (const ref of sorted) {\n if (ref.start < lastEnd)\n continue\n merged.push(ref)\n lastEnd = ref.end\n }\n return merged\n}\n\n/**\n * Collect every provider's references in one pass. Convenience wrapper —\n * the TUI textarea component calls this on every keystroke to highlight\n * in-prompt mentions.\n */\nexport function collectReferences<TItem>(\n text: string,\n providers: readonly CompletionProvider<TItem>[],\n cursor = text.length,\n): CompletionReference<TItem>[] {\n const ctx: CompletionContext = { text, cursor }\n const refs: CompletionReference<TItem>[] = []\n for (const p of providers) {\n for (const ref of p.parseReferences(text, ctx))\n refs.push(ref)\n }\n return mergeReferences(refs)\n}\n","/**\n * Completion provider that exposes the discovered project file catalog as\n * `@`-prefixed mentions. Trigger: `@`. Filtering: substring against\n * `name + path`, case-insensitive. Inserted text is `@{path} ` so the\n * cursor lands ready for the next token.\n *\n * Provider is pure UI — file *attachment* (reading bytes, injecting into\n * the prompt) happens at the submission boundary, where the host walks\n * `state.references` and decides what to do with each `FileEntry`.\n */\n\nimport type {\n CompletionContext,\n CompletionItem,\n CompletionProvider,\n CompletionReference,\n} from './completion-core'\nimport type { FileEntry } from './files-discovery'\nimport { Fzf } from 'fzf'\n\n/** Trigger character — `@` is the conventional file-mention prefix in chat UIs. */\nexport const FILES_TRIGGER = '@'\n\n/** Cap on returned items. Keeps the popover compact + render-cheap. */\nconst DEFAULT_RESULT_LIMIT = 50\n\n/** Identity formatter — preserves the discovery path verbatim. */\nconst IDENTITY_FORMAT = (entry: FileEntry): string => entry.path\n\n/**\n * Rank-and-slice a file catalog against a query. Hoisted to a module\n * helper so both the sync and async branches of `suggest()` share one\n * implementation (the async branch hits this once the lazy directory\n * walk resolves; sync branch hits it on every keystroke thereafter).\n *\n * `formatPath` rewrites the catalog's project-root-relative path into\n * the form the host wants emitted into the prompt (typically CWD-rel\n * or absolute when launched from a project subdir — see\n * `formatPathForCwd` in `path-display.ts`). Falls back to the raw\n * `entry.path` when omitted.\n */\nfunction scoreFiles(\n catalog: readonly FileEntry[],\n query: string,\n limit: number,\n formatPath: (entry: FileEntry) => string,\n): CompletionItem<FileEntry>[] {\n const q = query.trim().toLowerCase()\n // Pre-pass: fzf over the catalog's basenames feeds the rank-4 fuzzy\n // tier, catching small typos / omissions (`lop.ts` → `loop.ts`).\n // Scoped to basename — a path-wide fuzzy match would surface\n // near-random files on short queries. fzf enforces in-order\n // (subsequence) matching, so the fuzzy tier never degenerates into a\n // bag-of-chars filter. We keep only set membership (not fzf's score):\n // within-tier ordering stays alphabetical, and the Set keeps the loop\n // below O(1) per file.\n const fuzzyHits = q.length > 0\n ? new Set(new Fzf([...catalog], {\n selector: f => f.name,\n casing: 'case-insensitive',\n }).find(q).map(r => r.item))\n : null\n const scored: { entry: FileEntry, display: string, rank: number }[] = []\n for (const file of catalog) {\n const display = formatPath(file)\n const name = file.name.toLowerCase()\n const path = display.toLowerCase()\n if (q.length === 0) {\n scored.push({ entry: file, display, rank: 5 })\n continue\n }\n if (name === q) {\n scored.push({ entry: file, display, rank: 0 })\n continue\n }\n if (name.startsWith(q)) {\n scored.push({ entry: file, display, rank: 1 })\n continue\n }\n if (name.includes(q)) {\n scored.push({ entry: file, display, rank: 2 })\n continue\n }\n if (path.includes(q)) {\n scored.push({ entry: file, display, rank: 3 })\n continue\n }\n if (fuzzyHits?.has(file)) {\n scored.push({ entry: file, display, rank: 4 })\n continue\n }\n }\n scored.sort((a, b) => {\n if (a.rank !== b.rank)\n return a.rank - b.rank\n return a.display.localeCompare(b.display)\n })\n return scored.slice(0, limit).map<CompletionItem<FileEntry>>(({ entry, display }) => ({\n id: display,\n label: entry.name,\n // Description is the parent directory — gives the user disambiguation\n // signal when multiple files share a basename (`index.ts` × 12).\n //\n // Uses the catalog's RAW project-root-relative path, NOT the\n // cwd-formatted `display`. The cwd-formatted path flips a file's\n // visible shape based on whether it sits above or below\n // `process.cwd()` (absolute parent vs cwd-relative parent vs\n // empty for \"right here\"), which produces a popover that looks\n // half-broken when the same basename exists in multiple project\n // locations: `EDIT_THIS.md /Users/…/zidane` next to plain\n // `EDIT_THIS.md` for a sibling under the cwd. Anchoring the\n // description on `file.path` keeps the disambiguation column\n // stable + project-shaped regardless of where the user launched\n // the TUI from; the inserted text still uses `display` so the\n // tools can resolve it.\n description: parentDir(entry.path),\n insertText: `${FILES_TRIGGER}${display} `,\n data: entry,\n }))\n}\n\n/**\n * Build an `@`-prefixed files completion provider against a *live* catalog.\n *\n * The factory captures a getter so the catalog can be re-scanned (cwd\n * change, manual refresh) without re-instantiating the provider — the\n * App keeps one provider for the lifetime of the prompt block and just\n * mutates the underlying state.\n *\n * `limit` caps the result list so the popover stays bounded on huge\n * monorepos. Filtering is substring on `path` + `name`, case-insensitive,\n * with an fzf-scored basename fuzzy fallback so small typos / omissions\n * (`lop.ts` → `loop.ts`) still surface results; ranking prefers (in\n * order): exact name match, name prefix, name substring, path substring,\n * basename fuzzy match, alphabetical.\n */\nexport function createFilesCompletionProvider(opts: {\n /** Live file catalog. Re-evaluated per call so refreshes take effect immediately. */\n getCatalog: () => readonly FileEntry[]\n /**\n * Optional hook called the first time the host needs the catalog —\n * e.g. when the user opens the `@` popover. Hosts wire this to a\n * lazy directory walk so boot doesn't pay for it in monorepos\n * where the popover may never open. Idempotent contract: callers\n * may invoke it on every `suggest`; the host caches internally.\n *\n * When provided, `suggest()` returns a Promise on the very first\n * invocation if the catalog is still empty — the popover's loading\n * state surfaces while the walk completes; subsequent calls are\n * sync because `getCatalog()` then returns the populated state.\n */\n ensureCatalog?: () => Promise<readonly FileEntry[]>\n /** Max items returned to the popover. Default: 50. */\n limit?: number\n /**\n * Rewrite the catalog's project-root-relative path into the form\n * inserted into the prompt + matched by `parseReferences`. Wire this\n * to `formatPathForCwd` so paths emitted into the buffer line up\n * with the agent's CWD-resolving tools when the TUI launches from a\n * project subdirectory. Default: identity.\n *\n * Stable identity expected — `parseReferences` calls it once per\n * catalog entry per keystroke for the highlight pass; pure\n * pure-function shape keeps the popover responsive on huge repos.\n */\n formatPath?: (entry: FileEntry) => string\n}): CompletionProvider<FileEntry> {\n const limit = opts.limit ?? DEFAULT_RESULT_LIMIT\n const formatPath = opts.formatPath ?? IDENTITY_FORMAT\n return {\n id: 'files',\n trigger: FILES_TRIGGER,\n label: 'Files',\n suggest(query) {\n // Kick off lazy discovery (no-op on subsequent calls). If the\n // host wired a thunk and the catalog isn't ready yet, hand the\n // popover a Promise — it already supports `loading` and refreshes\n // `items` when the promise resolves.\n if (opts.ensureCatalog) {\n const pending = opts.ensureCatalog()\n const current = opts.getCatalog()\n if (current.length === 0) {\n return pending.then(loaded => scoreFiles(loaded, query, limit, formatPath))\n }\n }\n return scoreFiles(opts.getCatalog(), query, limit, formatPath)\n },\n parseReferences(text, _ctx: CompletionContext) {\n const catalog = opts.getCatalog()\n if (catalog.length === 0)\n return []\n // Keyed by the DISPLAYED path (what `insertText` emitted) so a\n // post-format buffer still resolves back to its `FileEntry`.\n const byPath = new Map<string, FileEntry>()\n for (const file of catalog) byPath.set(formatPath(file), file)\n const refs: CompletionReference<FileEntry>[] = []\n // Match `@<path>` at start-of-buffer or after whitespace. Paths are\n // non-whitespace sequences — files contain `/`, `.`, `-`, etc., so a\n // word-boundary rule won't do. We greedy-match `\\S+` then trim\n // trailing punctuation that's almost never part of the intended\n // reference (sentence end). The trimmed candidate must exist in the\n // catalog for the span to be highlighted — typos don't produce\n // false-positive references.\n // Capture groups: 1 = leading boundary (start-of-buffer or whitespace),\n // 2 = path body without the `@` sigil. `@` itself is matched but not\n // captured because the consumer only needs the raw path to look up\n // the catalog entry.\n const rx = /(^|\\s)@(\\S+)/g\n for (const m of text.matchAll(rx)) {\n const rawCandidate = m[2]\n // Strip trailing `.`, `,`, `;`, `:`, `)` , `]`, `}`, `!`, `?` — when\n // the exact candidate isn't in the catalog but a shorter prefix is,\n // the user almost certainly mentioned the file in regular prose.\n // Single regex replace + slice instead of a per-char loop keeps this\n // O(n) in candidate length rather than O(n²) on pathological tails.\n const stripped = byPath.has(rawCandidate)\n ? rawCandidate\n : rawCandidate.replace(/[.,;:)\\]}!?]+$/, '')\n const file = byPath.get(stripped)\n if (!file)\n continue\n const start = m.index + m[1].length\n // `trimmed` matches the visible \"@<stripped>\" span — recompute its\n // length from `stripped` so we don't over-paint on the trailing punct.\n const trimmedLen = 1 + stripped.length // `@` + path\n refs.push({\n providerId: 'files',\n start,\n end: start + trimmedLen,\n // `itemId` mirrors `suggest()`'s `id` (the displayed path)\n // so the engine's reference ↔ item correlation stays\n // consistent on either side of the formatter. `data.path`\n // still carries the raw project-root-relative path for\n // downstream consumers (`uniqueFilesFromReferences`,\n // submission attach hooks, …).\n itemId: stripped,\n data: file,\n })\n }\n return refs\n },\n }\n}\n\n/** Return the parent directory of a forward-slashed path, or `''` for root entries. */\nfunction parentDir(path: string): string {\n const lastSlash = path.lastIndexOf('/')\n return lastSlash <= 0 ? '' : path.slice(0, lastSlash)\n}\n\n/**\n * Walk a reference list and return the deduplicated set of files in\n * first-mention order — input to \"attach these files to the prompt\"\n * downstream logic.\n */\nexport function uniqueFilesFromReferences(\n references: readonly CompletionReference<unknown>[],\n): FileEntry[] {\n const out: FileEntry[] = []\n const seen = new Set<string>()\n for (const ref of references) {\n if (ref.providerId !== 'files')\n continue\n if (seen.has(ref.itemId))\n continue\n seen.add(ref.itemId)\n out.push(ref.data as FileEntry)\n }\n return out\n}\n","/**\n * Completion provider that exposes the discovered skills catalog as\n * slash-commands. Trigger: `/`. Filtering: substring against `name +\n * description`, case-insensitive. Inserted text is `/{name} ` so the\n * cursor lands ready for the next token.\n *\n * Skill activation is **not** performed here — the provider is pure UI.\n * Submission flow at the host (TUI `onSubmitPrompt`) walks the prompt for\n * skill references via `parseReferences` and calls `agent.activateSkill`\n * before `agent.run`.\n */\n\nimport type { SkillConfig } from '../skills'\nimport type {\n CompletionContext,\n CompletionItem,\n CompletionProvider,\n CompletionReference,\n} from './completion-core'\nimport { Fzf } from 'fzf'\n\n/** Trigger character — slash-commands convention. */\nexport const SKILLS_TRIGGER = '/'\n\n/** Valid skill-name shape (matches the parser): lowercase alnum + dashes. */\nconst SKILL_NAME_RX = /^[a-z0-9][a-z0-9-]*$/\n\n/**\n * Filter + rank visible skills against a query. Hoisted to a module\n * helper so the sync and async branches of `suggest()` share one\n * implementation (the async branch hits this once the lazy SKILL.md\n * scan resolves; sync branch hits it on every keystroke thereafter).\n */\nfunction scoreSkills(\n catalog: readonly SkillConfig[],\n query: string,\n): CompletionItem<SkillConfig>[] {\n const q = query.trim().toLowerCase()\n const valid = catalog.filter(skill => SKILL_NAME_RX.test(skill.name))\n // fzf pre-pass over skill *names* so the rank-3 fuzzy tier catches\n // small typos (`reseach` → `research`). Scoped to name only — fuzzy\n // matching against descriptions (free-form sentences) would surface\n // near-random skills on short queries.\n const fuzzyHits = q.length > 0\n ? new Set(new Fzf(valid, {\n selector: s => s.name,\n casing: 'case-insensitive',\n }).find(q).map(r => r.item))\n : null\n const scored: { skill: SkillConfig, rank: number }[] = []\n for (const skill of valid) {\n const name = skill.name.toLowerCase()\n const desc = skill.description.toLowerCase()\n if (q.length === 0) {\n scored.push({ skill, rank: 4 })\n continue\n }\n if (name.startsWith(q)) {\n scored.push({ skill, rank: 0 })\n continue\n }\n if (name.includes(q)) {\n scored.push({ skill, rank: 1 })\n continue\n }\n if (desc.includes(q)) {\n scored.push({ skill, rank: 2 })\n continue\n }\n if (fuzzyHits?.has(skill)) {\n scored.push({ skill, rank: 3 })\n continue\n }\n }\n scored.sort((a, b) => {\n if (a.rank !== b.rank)\n return a.rank - b.rank\n return a.skill.name.localeCompare(b.skill.name)\n })\n return scored.map<CompletionItem<SkillConfig>>(({ skill }) => ({\n id: skill.name,\n label: skill.name,\n description: skill.description,\n insertText: `${SKILLS_TRIGGER}${skill.name} `,\n data: skill,\n }))\n}\n\n/**\n * Build a slash-command completion provider against a *live* skills\n * catalog. The factory captures a getter so the catalog can change across\n * renders (toggles, reload) without re-instantiating the provider.\n *\n * Pass `getEnabled` to additionally hide skills the user has toggled off\n * — when undefined, every catalog entry is offered.\n */\nexport function createSkillsCompletionProvider(opts: {\n /** Live catalog. Re-evaluated per call so toggles take effect immediately. */\n getCatalog: () => readonly SkillConfig[]\n /** Optional enable-set filter; when undefined every catalog skill is offered. */\n getEnabled?: () => readonly string[] | undefined\n /**\n * Optional hook called the first time the host needs the catalog —\n * typically when the user opens the `/` popover. Mirror of the\n * files provider's `ensureCatalog`; same idempotent contract.\n *\n * Hosts wire this to a lazy SKILL.md scan so the boot path stays\n * free of disk reads in deeply-nested skill trees. Returns a Promise\n * on the very first `suggest()` call when the catalog is still\n * empty so the popover surfaces its loading state.\n */\n ensureCatalog?: () => Promise<readonly SkillConfig[]>\n}): CompletionProvider<SkillConfig> {\n const visible = (): SkillConfig[] => {\n const all = opts.getCatalog()\n const enabled = opts.getEnabled?.()\n if (enabled === undefined)\n return [...all]\n const allow = new Set(enabled)\n return all.filter(s => allow.has(s.name))\n }\n return {\n id: 'skills',\n trigger: SKILLS_TRIGGER,\n label: 'Skills',\n suggest(query) {\n // Lazy-load on first suggest if the host wired a thunk. Same\n // pattern as the files provider — popover loading state covers\n // the gap, subsequent suggests are sync.\n if (opts.ensureCatalog) {\n const pending = opts.ensureCatalog()\n if (opts.getCatalog().length === 0) {\n return pending.then(() => scoreSkills(visible(), query))\n }\n }\n return scoreSkills(visible(), query)\n },\n parseReferences(text, _ctx: CompletionContext) {\n const catalog = visible()\n if (catalog.length === 0)\n return []\n const byName = new Map<string, SkillConfig>()\n for (const skill of catalog) byName.set(skill.name, skill)\n const refs: CompletionReference<SkillConfig>[] = []\n // Match `/name` at start-of-buffer or after whitespace. The lookbehind\n // is implemented manually (no regex `\\b` because `/` isn't a word\n // character) so we work in environments that lack lookbehind support.\n // Grammar must match `SKILL_NAME_RX` — `[a-z0-9-]` only, no `_`. Using\n // `\\w` here would parse `/foo_bar` as a candidate, only for the catalog\n // lookup to drop it; aligning the two grammars keeps the parser honest.\n const rx = /(^|\\s)(\\/([a-z0-9][a-z0-9-]*))/g\n for (const m of text.matchAll(rx)) {\n const name = m[3]\n const skill = byName.get(name)\n if (!skill)\n continue\n const start = m.index + m[1].length\n refs.push({\n providerId: 'skills',\n start,\n end: start + m[2].length,\n itemId: skill.name,\n data: skill,\n })\n }\n return refs\n },\n }\n}\n\n/**\n * Walk a parsed prompt for skill references and return the deduplicated\n * list of skill names — input to `agent.activateSkill(name)` calls on\n * submit.\n */\nexport function uniqueSkillNamesFromReferences(\n references: readonly CompletionReference<unknown>[],\n): string[] {\n const out: string[] = []\n const seen = new Set<string>()\n for (const ref of references) {\n if (ref.providerId !== 'skills')\n continue\n if (seen.has(ref.itemId))\n continue\n seen.add(ref.itemId)\n out.push(ref.itemId)\n }\n return out\n}\n","/**\n * Pure helpers for the file-edit approval gate.\n *\n * Lives in `chat/` (not `tui/`) so unit tests can exercise the decision\n * logic without mounting React. The TUI binds these helpers in\n * `app.tsx`'s `applyGate` and in the modal's submit path; replay code\n * (`eventsFromTurns`) reuses the result parser.\n */\n\nimport type { ToolResultContent } from '../types'\nimport type { ApprovalDecision } from './safe-mode-context'\nimport type { EditOutcome, EditOutcomeKind, EditPayload } from './types'\n\n/**\n * Convert a per-hunk approval mask into an `EditOutcome[]`. `true` →\n * `applied`; `false` → `denied` with the supplied reason.\n *\n * Length is `Math.max(mask.length, fallbackLength)` so callers passing a\n * shorter mask still get a fully-populated array — missing entries\n * default to applied, matching the \"no decision => keep\" convention.\n */\nexport function maskToOutcomeKinds(\n mask: readonly boolean[],\n fallbackLength: number,\n deniedReason = 'denied by user',\n): EditOutcome[] {\n const len = Math.max(mask.length, fallbackLength)\n const out: EditOutcome[] = []\n for (let i = 0; i < len; i++) {\n const keep = i < mask.length ? mask[i] : true\n out.push(keep ? { kind: 'applied' } : { kind: 'denied', reason: deniedReason })\n }\n return out\n}\n\n/**\n * Apply an `ApprovalDecision` to a payload, returning the resolved\n * per-hunk outcomes + the gate-level verdict.\n *\n * Pure — does not mutate `input` or `payload`. The TUI's `applyGate`\n * consumes the result: stashes `outcomes` in the pending-annotation map\n * (keyed by callId) so `tool:transform` can append the\n * `<edit-outcomes>` block to the tool result, rebinds `ctx.input.edits`\n * to the approved subset for `partial`, and emits the `syntheticEvent`\n * for fully-denied or fully-blocked calls.\n */\nexport interface ResolvedApproval {\n /** Final state of every hunk after the decision (1:1 with payload.hunks). */\n outcomes: EditOutcome[]\n /** True when no hunk will be applied — gate should `block` the call. */\n shouldBlock: boolean\n /**\n * Synthetic `EditPayload` to render in the transcript. Identical to the\n * incoming payload but with `outcomes` set so the renderer can badge\n * each hunk. Only meaningful when at least one hunk was denied —\n * an all-applied decision returns `null` here and the normal\n * `tool:before` event suffices.\n */\n syntheticEvent: EditPayload | null\n}\n\nexport function resolveApprovalForPayload(\n decision: ApprovalDecision,\n payload: EditPayload,\n): ResolvedApproval {\n const total = payload.hunks.length\n\n if (decision === 'deny') {\n const outcomes: EditOutcome[] = Array.from(\n { length: total },\n () => ({ kind: 'denied' as const, reason: 'denied by user' }),\n )\n return {\n outcomes,\n shouldBlock: true,\n syntheticEvent: { ...payload, outcomes },\n }\n }\n\n if (typeof decision === 'object' && decision.kind === 'partial') {\n const outcomes = maskToOutcomeKinds(decision.mask, total)\n const anyApplied = outcomes.some(o => o.kind === 'applied')\n return {\n outcomes,\n shouldBlock: !anyApplied,\n syntheticEvent: { ...payload, outcomes },\n }\n }\n\n // accept-once / accept-session / accept-safelist: every hunk applies.\n // No synthetic event — `tool:before` will paint the standard payload.\n return {\n outcomes: Array.from({ length: total }, () => ({ kind: 'applied' as const })),\n shouldBlock: false,\n syntheticEvent: null,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Result annotation — XML-like sentinel block appended to edit tool\n// results so the renderer (live + replay) can reconstruct per-hunk\n// outcomes without a host-side side-channel on `tool_call.input`.\n//\n// Shape (one block per call):\n//\n// <edit-outcomes>\n// #1 applied\n// #2 denied: denied by user\n// #3 applied\n// </edit-outcomes>\n//\n// - Opening + closing tags each on their own line.\n// - One line per hunk: `#<1-based index> <kind>[: <reason>]`.\n// - `kind` is one of `applied | denied | skipped | failed`.\n//\n// The TUI's `tool:transform` hook appends the block when at least one\n// hunk is NOT applied — an all-applied call needs no annotation since\n// the legacy summary line conveys the same information.\n// ---------------------------------------------------------------------------\n\n/** Sentinel tags used by {@link buildEditOutcomesAnnotation} / parser. */\nconst ANNOTATION_OPEN = '<edit-outcomes>'\nconst ANNOTATION_CLOSE = '</edit-outcomes>'\n\nconst OUTCOME_KIND_RE = /^applied|denied|skipped|failed$/\nconst OUTCOME_LINE_RE = /^#(\\d+) (applied|denied|skipped|failed)(?:: ?(.*))?$/\n\n/**\n * Render an `EditOutcome[]` as the wire-format annotation block. Returns\n * the body to APPEND to a tool result; callers join with a leading\n * `\\n\\n` separator. Idempotent on missing reasons — bare `applied` lines\n * stay terse.\n */\nexport function buildEditOutcomesAnnotation(outcomes: readonly EditOutcome[]): string {\n const lines: string[] = [ANNOTATION_OPEN]\n for (let i = 0; i < outcomes.length; i++) {\n const o = outcomes[i]\n if (!OUTCOME_KIND_RE.test(o.kind))\n continue\n const reason = o.reason ? `: ${o.reason}` : ''\n lines.push(`#${i + 1} ${o.kind}${reason}`)\n }\n lines.push(ANNOTATION_CLOSE)\n return lines.join('\\n')\n}\n\n/**\n * Parse an `<edit-outcomes>…</edit-outcomes>` annotation block out of a\n * tool result body. Returns the outcomes keyed by 1-based hunk index, or\n * `null` when the block is missing / malformed.\n *\n * Anchored on the explicit tag pair so the parser doesn't false-positive\n * on natural prose that happens to contain `#1 applied`.\n */\nexport function parseEditOutcomesFromResult(\n result: string | readonly ToolResultContent[],\n): EditOutcome[] | null {\n const text = typeof result === 'string'\n ? result\n : result\n .filter((b): b is Extract<ToolResultContent, { type: 'text' }> => b.type === 'text')\n .map(b => b.text)\n .join('\\n')\n\n if (!text)\n return null\n\n const openIdx = text.indexOf(`\\n${ANNOTATION_OPEN}\\n`)\n const startIdx = openIdx >= 0 ? openIdx + 1 : (text.startsWith(`${ANNOTATION_OPEN}\\n`) ? 0 : -1)\n if (startIdx < 0)\n return null\n\n const closeNeedle = `\\n${ANNOTATION_CLOSE}`\n const closeIdx = text.indexOf(closeNeedle, startIdx)\n if (closeIdx < 0)\n return null\n\n const body = text.slice(startIdx + ANNOTATION_OPEN.length + 1, closeIdx)\n const found: Array<{ idx: number, outcome: EditOutcome }> = []\n for (const line of body.split('\\n')) {\n if (line.length === 0)\n continue\n const m = OUTCOME_LINE_RE.exec(line)\n if (!m)\n return null\n const idx = Number.parseInt(m[1], 10)\n if (!Number.isFinite(idx) || idx < 1)\n return null\n const kind = m[2] as EditOutcomeKind\n const reason = m[3]?.trim()\n found.push({\n idx,\n outcome: {\n kind,\n ...(reason ? { reason } : {}),\n },\n })\n }\n\n if (found.length === 0)\n return null\n\n const maxIdx = Math.max(...found.map(f => f.idx))\n const outcomes: EditOutcome[] = Array.from({ length: maxIdx }, () => ({ kind: 'applied' }))\n for (const { idx, outcome } of found)\n outcomes[idx - 1] = outcome\n return outcomes\n}\n\n/**\n * Strip the first `<edit-outcomes>…</edit-outcomes>` block out of a tool\n * result body, returning the surrounding text. Used by the\n * `tool:transform` hook to peel a body-emitted annotation before\n * re-appending the merged (approval ∪ body) version — otherwise the\n * result would carry two annotation blocks and\n * {@link parseEditOutcomesFromResult} would only see the first.\n *\n * Anchored on the same `\\n<edit-outcomes>\\n` / start-of-string newline\n * shape the parser uses, so prose that incidentally mentions\n * `<edit-outcomes>` (e.g. a model summarizing its own format) isn't\n * mistakenly stripped. Trims a single leading `\\n\\n` separator when\n * present so successive strips don't leave dangling blank lines.\n * Idempotent on inputs that don't contain a properly-anchored block.\n */\nexport function stripEditOutcomesAnnotation(text: string): string {\n // Mirror `parseEditOutcomesFromResult`'s anchor: either preceded by\n // a newline (the common case after a `\\n\\n` separator from the body)\n // OR at the very start of the body. A bare `<edit-outcomes>` inside\n // prose without that surrounding newline shape stays untouched.\n const newlineNeedle = `\\n${ANNOTATION_OPEN}\\n`\n const newlineIdx = text.indexOf(newlineNeedle)\n let openIdx: number\n if (newlineIdx >= 0)\n openIdx = newlineIdx + 1\n else if (text.startsWith(`${ANNOTATION_OPEN}\\n`))\n openIdx = 0\n else\n return text\n\n const closeIdx = text.indexOf(ANNOTATION_CLOSE, openIdx)\n if (closeIdx < 0)\n return text\n const blockEnd = closeIdx + ANNOTATION_CLOSE.length\n // Eat one preceding `\\n\\n` separator (the shape the writer emits)\n // so the trimmed text doesn't end with an awkward double newline.\n const sepStart = openIdx >= 2 && text.slice(openIdx - 2, openIdx) === '\\n\\n'\n ? openIdx - 2\n : openIdx\n return text.slice(0, sepStart) + text.slice(blockEnd)\n}\n\n/**\n * Merge body-side outcomes (keyed against the approved subset the tool\n * actually ran on, in subset-position order) into approval-side outcomes\n * (1:1 with the model's ORIGINAL `edits` list, with `denied` entries for\n * every hunk the user dropped).\n *\n * Algorithm: walk the approval array; every `applied` placeholder\n * corresponds to one approved hunk that the body ran. Consume body's\n * outcomes in order against those placeholders. Non-`applied` approval\n * entries (`denied`, `skipped`) stay untouched — they describe gate-\n * level decisions the body never saw.\n *\n * Pure. Returns a fresh array; never mutates either input.\n *\n * Edge cases:\n * - `body` is empty / shorter than the approved count → remaining\n * approval `applied` placeholders stay as `applied` (the body ran\n * happily; absence of a body entry means nothing failed).\n * - `body` longer than approved count → trailing body entries are\n * ignored. Shouldn't happen in practice (body sees the rebound\n * subset), but the guard keeps the merge total-pure.\n */\nexport function mergeApprovalAndBodyOutcomes(\n approval: readonly EditOutcome[],\n body: readonly EditOutcome[] | null,\n): EditOutcome[] {\n if (!body || body.length === 0)\n return approval.slice()\n const out: EditOutcome[] = []\n let bi = 0\n for (const entry of approval) {\n if (entry.kind === 'applied' && bi < body.length) {\n out.push(body[bi])\n bi++\n }\n else {\n out.push(entry)\n }\n }\n return out\n}\n\n/**\n * Rewrite a `multi_edit` body header so the totals reflect the model's\n * ORIGINAL edit list (the merged outcomes count) instead of the subset\n * the body actually saw after gate rebinding. Without this, a partially\n * approved call surfaces a misleading `applied 2 of 2 edits` (subset\n * counts) on the wire even when the original was `applied 2 of 3`.\n *\n * Three body-side shapes are handled (matching `multi_edit`'s emit):\n * 1. `Edited <path>: applied N edits (R replacements).`\n * 2. `Edited <path>: applied N of M edits (R replacements).`\n * 3. `multi_edit error: no edits applied to <path> (M attempted).`\n *\n * The replacements count is preserved verbatim — it's a body-side stat\n * the chat layer can't recompute. When the first line doesn't look like\n * any of the three shapes (e.g. an unrelated error preamble bubbled up),\n * the text is returned unchanged.\n */\nexport function rewriteMultiEditHeader(\n text: string,\n merged: readonly EditOutcome[],\n path: string,\n): string {\n const newlineIdx = text.indexOf('\\n')\n const firstLine = newlineIdx < 0 ? text : text.slice(0, newlineIdx)\n const rest = newlineIdx < 0 ? '' : text.slice(newlineIdx)\n\n // Match either success shape (with or without \"of M\") and capture R.\n // The path component uses `.+` greedy so colons (Windows `C:\\…`) and\n // parens (unusual but valid filenames) don't trip the match — the\n // regex engine backtracks to find the longest path that still lets\n // `: applied N…` line up against the trailing structure.\n const successMatch = firstLine.match(\n /^Edited .+: applied \\d+(?: of \\d+)? edits? \\((\\d+) replacement/,\n )\n // Match the all-failed shape (no replacements stat, defaults to 0).\n // `startsWith` instead of regex: the prefix is unique enough and\n // sidesteps the same path-content escaping concerns.\n const isFailedShape = firstLine.startsWith('multi_edit error: no edits applied to ')\n && firstLine.endsWith(' attempted).')\n if (!successMatch && !isFailedShape)\n return text\n\n const replacements = successMatch ? Number.parseInt(successMatch[1], 10) || 0 : 0\n const counts = summarizeOutcomes(merged)\n const applied = counts.applied\n const total = merged.length\n\n let newHeader: string\n if (applied === total) {\n newHeader = `Edited ${path}: applied ${total} edit${total === 1 ? '' : 's'} (${replacements} replacement${replacements === 1 ? '' : 's'}).`\n }\n else if (applied > 0) {\n newHeader = `Edited ${path}: applied ${applied} of ${total} edits (${replacements} replacement${replacements === 1 ? '' : 's'}).`\n }\n else {\n newHeader = `multi_edit error: no edits applied to ${path} (${total} attempted).`\n }\n\n return newHeader + rest\n}\n\n/**\n * Aggregate counts for the transcript's summary badge (`3 applied · 1\n * denied · 1 skipped`). Exported so renderers don't reimplement the\n * tally. Pure / O(n).\n */\nexport function summarizeOutcomes(outcomes: readonly EditOutcome[] | undefined): {\n applied: number\n denied: number\n skipped: number\n failed: number\n pending: number\n total: number\n} {\n const counts = { applied: 0, denied: 0, skipped: 0, failed: 0, pending: 0 }\n if (!outcomes)\n return { ...counts, total: 0 }\n for (const o of outcomes)\n counts[o.kind] += 1\n return { ...counts, total: outcomes.length }\n}\n","import type { ResolvedMatch } from '../tools/edit-utils'\nimport type { EditHunk, EditPayload } from './types'\nimport { resolveOldString, styleReplacementForVia } from '../tools/edit-utils'\n\n// ---------------------------------------------------------------------------\n// extractEditPayload — pulls `EditPayload` out of a tool call's raw input.\n//\n// Used by both the live `tool:before` hook and the historical replay path\n// (`eventsFromTurns`). Returns `undefined` when the tool isn't one of the\n// supported edit tools, or when the input shape is unexpected — in either\n// case the renderer falls back to the unstructured `↳ name(args)` line.\n//\n// Outcomes are NOT pulled from input — they live on the paired tool_result\n// text via the `<edit-outcomes>` annotation block written by the TUI's\n// `tool:transform` hook. `eventsFromTurns` pairs call ↔ result by callId\n// and attaches outcomes after the fact via `parseEditOutcomesFromResult`.\n//\n// `write_file` needs the *previous* on-disk content to render a diff;\n// that's the caller's responsibility (the TUI's hook reads it before the\n// tool runs). Pass it as `priorContent`; an empty string is fine for a\n// fresh create. Historical replay has no priorContent and falls through\n// to `oldString: ''` — every line renders as an addition, matching\n// `git diff` for newly-added files.\n// ---------------------------------------------------------------------------\n\ninterface EditStepInput {\n old_string: unknown\n new_string: unknown\n replace_all?: unknown\n}\n\nexport function extractEditPayload(\n name: string,\n input: Record<string, unknown>,\n priorContent?: string,\n): EditPayload | undefined {\n const path = input.path\n if (typeof path !== 'string' || path === '')\n return undefined\n\n if (name === 'edit') {\n const oldString = input.old_string\n const newString = input.new_string\n if (typeof oldString !== 'string' || typeof newString !== 'string')\n return undefined\n const hunks: EditHunk[] = [{\n oldString,\n newString,\n ...(input.replace_all === true ? { replaceAll: true } : {}),\n }]\n return {\n tool: 'edit',\n path,\n hunks,\n ...(priorContent !== undefined ? { priorContent } : {}),\n }\n }\n\n if (name === 'multi_edit') {\n const steps = input.edits\n if (!Array.isArray(steps) || steps.length === 0)\n return undefined\n const hunks: EditHunk[] = []\n for (const raw of steps as EditStepInput[]) {\n if (typeof raw?.old_string !== 'string' || typeof raw?.new_string !== 'string')\n return undefined\n hunks.push({\n oldString: raw.old_string,\n newString: raw.new_string,\n ...(raw.replace_all === true ? { replaceAll: true } : {}),\n })\n }\n return {\n tool: 'multi_edit',\n path,\n hunks,\n ...(priorContent !== undefined ? { priorContent } : {}),\n }\n }\n\n if (name === 'write_file') {\n const content = input.content\n if (typeof content !== 'string')\n return undefined\n const hunks: EditHunk[] = [{ oldString: priorContent ?? '', newString: content }]\n return {\n tool: 'write_file',\n path,\n hunks,\n ...(priorContent !== undefined ? { priorContent } : {}),\n }\n }\n\n return undefined\n}\n\n// ---------------------------------------------------------------------------\n// Line diff — LCS table walk, returns a sequence of `context | add | remove`\n// rows. Backed by the standard 2-D dynamic-programming LCS; O(n·m) time\n// and memory in the line counts of the two inputs. Edit blocks are short\n// (the agent's `old_string` / `new_string` rarely exceed a few dozen\n// lines), so we don't bother with the linear-space Hunt–Szymanski\n// optimization — clarity wins.\n//\n// Trailing newline policy: a string ending with `\\n` produces a trailing\n// empty `\"\"` line after split. We trim it so a single-line edit doesn't\n// emit a phantom empty `+` row below the actual content.\n// ---------------------------------------------------------------------------\n\nexport type DiffOp = 'context' | 'add' | 'remove'\n\nexport interface DiffLine {\n op: DiffOp\n text: string\n}\n\nexport function computeLineDiff(oldString: string, newString: string): DiffLine[] {\n const oldLines = splitLines(oldString)\n const newLines = splitLines(newString)\n\n // LCS table: lcs[i][j] = length of LCS of oldLines[0..i) and newLines[0..j).\n const n = oldLines.length\n const m = newLines.length\n const lcs: number[][] = Array.from({ length: n + 1 }, () => Array.from<number>({ length: m + 1 }).fill(0))\n for (let i = 0; i < n; i++) {\n for (let j = 0; j < m; j++) {\n lcs[i + 1][j + 1] = oldLines[i] === newLines[j]\n ? lcs[i][j] + 1\n : Math.max(lcs[i][j + 1], lcs[i + 1][j])\n }\n }\n\n // Walk the table from (n, m) → (0, 0) recording operations. The walk\n // produces ops in reverse order, so the result is reversed at the\n // end. Diagonals at equal lines → context; otherwise step into the\n // higher of (up, left) and emit remove/add respectively. Ties break\n // toward `remove` so adjacent remove/add pairs stay grouped (the\n // intra-line highlighter pairs them up).\n const out: DiffLine[] = []\n let i = n\n let j = m\n while (i > 0 || j > 0) {\n if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {\n out.push({ op: 'context', text: oldLines[i - 1] })\n i--\n j--\n continue\n }\n if (j > 0 && (i === 0 || lcs[i][j - 1] >= lcs[i - 1][j])) {\n out.push({ op: 'add', text: newLines[j - 1] })\n j--\n continue\n }\n out.push({ op: 'remove', text: oldLines[i - 1] })\n i--\n }\n out.reverse()\n return out\n}\n\n/**\n * Split a string into lines preserving empty lines but dropping the\n * implicit trailing `\"\"` produced by a final `\\n`. Exported only for\n * its tests — callers should use `computeLineDiff`.\n */\nexport function splitLines(s: string): string[] {\n if (s === '')\n return []\n const parts = s.split('\\n')\n if (parts[parts.length - 1] === '')\n parts.pop()\n return parts\n}\n\n// ---------------------------------------------------------------------------\n// Inline diff — word-level segments for a paired (remove, add) row.\n//\n// Same LCS recipe over **word tokens** so the renderer can bold the\n// genuinely-different runs inside each line. Tokenization keeps runs of\n// word characters + non-word characters as separate units so a rename\n// like `oldName → newName` highlights the single token rather than the\n// whole word boundary.\n//\n// Returns parallel segment arrays for the old and new lines — same\n// segment order, `changed: true` on the diverging tokens. Renderer\n// renders unchanged tokens in the line's base fg + changed tokens in\n// a brighter accent (with the row's bg still tinted).\n// ---------------------------------------------------------------------------\n\nexport interface InlineSegment {\n text: string\n changed: boolean\n}\n\nexport interface InlineDiff {\n oldSegments: InlineSegment[]\n newSegments: InlineSegment[]\n}\n\nexport function computeInlineDiff(oldLine: string, newLine: string): InlineDiff {\n const oldTokens = tokenize(oldLine)\n const newTokens = tokenize(newLine)\n\n const n = oldTokens.length\n const m = newTokens.length\n const lcs: number[][] = Array.from({ length: n + 1 }, () => Array.from<number>({ length: m + 1 }).fill(0))\n for (let i = 0; i < n; i++) {\n for (let j = 0; j < m; j++) {\n lcs[i + 1][j + 1] = oldTokens[i] === newTokens[j]\n ? lcs[i][j] + 1\n : Math.max(lcs[i][j + 1], lcs[i + 1][j])\n }\n }\n\n // Walk table — same as computeLineDiff but emit segments tagged\n // changed/unchanged into the side they belong to.\n const oldSegments: InlineSegment[] = []\n const newSegments: InlineSegment[] = []\n let i = n\n let j = m\n while (i > 0 || j > 0) {\n if (i > 0 && j > 0 && oldTokens[i - 1] === newTokens[j - 1]) {\n pushSegment(oldSegments, { text: oldTokens[i - 1], changed: false })\n pushSegment(newSegments, { text: newTokens[j - 1], changed: false })\n i--\n j--\n continue\n }\n if (j > 0 && (i === 0 || lcs[i][j - 1] >= lcs[i - 1][j])) {\n pushSegment(newSegments, { text: newTokens[j - 1], changed: true })\n j--\n continue\n }\n pushSegment(oldSegments, { text: oldTokens[i - 1], changed: true })\n i--\n }\n oldSegments.reverse()\n newSegments.reverse()\n return { oldSegments, newSegments }\n}\n\n/**\n * Coalesce adjacent same-state segments so the renderer emits one\n * `<span>` per run instead of one per token — keeps the React tree\n * shallow on dense lines without changing the visual output.\n *\n * Walking direction is reverse (we push during the reverse walk, then\n * the caller reverses the array), so we coalesce against the *tail*.\n */\nfunction pushSegment(buf: InlineSegment[], seg: InlineSegment): void {\n const tail = buf[buf.length - 1]\n if (tail && tail.changed === seg.changed)\n tail.text = seg.text + tail.text\n else\n buf.push(seg)\n}\n\n/**\n * Tokenize on word / non-word boundaries. Each run of `\\w+` is one\n * token; each run of `\\W+` (whitespace, punctuation) is another. This\n * gives the right granularity for renames (`oldName` → `newName`) and\n * for symbol swaps (`+ → -`) without exploding into per-char segments.\n *\n * Exported only for its tests.\n */\nexport function tokenize(s: string): string[] {\n if (s === '')\n return []\n const out: string[] = []\n // Greedy: alternating word / non-word runs. Avoids per-char tokens\n // which would inflate the LCS table on long lines.\n const re = /\\w+|\\W+/g\n for (const match of s.matchAll(re))\n out.push(match[0])\n return out\n}\n\n// ---------------------------------------------------------------------------\n// Unified diff serialization — feeds OpenTUI's native `<diff>` renderable.\n//\n// `<diff>` parses standard unified diff syntax (`--- a/path`, `+++ b/path`,\n// `@@ -l,n +l,m @@`, then `[ +-]<line>` rows) and handles per-language\n// syntax highlighting + bg coloring + wrap. We build the string from our\n// `EditPayload` and let the renderable do the rest.\n//\n// Line numbers in the chunk header are SYNTHETIC. `edit` only carries an\n// `old_string` snippet (no file position), `multi_edit` sequential edits\n// invalidate each other's positions, and `write_file` always starts at\n// line 1. Synthetic `@@ -1,N +1,M @@` is correct enough for the visual\n// gutter; users read the path in the header for context.\n//\n// New-file convention: when `oldString === ''` we emit `--- /dev/null`\n// and `@@ -0,0 +1,n @@`, matching `git diff` for newly added files.\n// ---------------------------------------------------------------------------\n\n/**\n * Apply the payload's hunks against `priorContent` and return the\n * resulting file body. Mirrors the agent's tool-side semantics:\n * - `replaceAll === true` → `String.replaceAll`\n * - otherwise → first-occurrence `String.replace`\n *\n * Hunks are applied in order — a `multi_edit` later hunk operates on\n * the output of the earlier ones, just like the actual tool.\n */\nexport function applyEditPayload(payload: EditPayload, priorContent: string): string {\n let out = priorContent\n for (const hunk of payload.hunks) {\n out = hunk.replaceAll\n ? out.replaceAll(hunk.oldString, hunk.newString)\n : out.replace(hunk.oldString, hunk.newString)\n }\n return out\n}\n\n/**\n * Like `buildUnifiedDiff` but operating against the full file content\n * so the diff carries *real* file line numbers and configurable\n * surrounding context.\n *\n * Strategy:\n * 1. Apply the payload to `priorContent` → `newContent`.\n * 2. Run `computeLineDiff` over the whole file.\n * 3. Group non-context ops into hunks, padding each with up to\n * `contextLines` of context above and below. Adjacent hunks\n * whose context regions touch are merged so we don't emit two\n * `@@` headers separated by zero context lines.\n *\n * The output line numbers in the `@@` header are 1-based and reflect\n * the change's position in the actual file — what the user expects\n * when reading a diff alongside their editor.\n *\n * For `write_file` creating a new file (priorContent === ''), this\n * falls back to the same `--- /dev/null` convention as\n * `buildUnifiedDiff`.\n */\nexport function buildContextualDiff(\n payload: EditPayload,\n priorContent: string,\n contextLines = 3,\n): string {\n const newContent = applyEditPayload(payload, priorContent)\n const isNewFile = priorContent === ''\n const ops = computeLineDiff(priorContent, newContent)\n\n // Pre-compute 1-based file line numbers for each op. `oldLine[i]` is\n // the original-file line `ops[i]` corresponds to (for context /\n // remove ops; undefined for adds since they don't exist in the old).\n // Same for `newLine[i]`. Add-only and remove-only ops still need a\n // base anchor for the hunk header, which we read off the *next*\n // context/anchor line — captured by tracking running counters.\n const oldLineFor: number[] = []\n const newLineFor: number[] = []\n let ol = 1\n let nl = 1\n for (const op of ops) {\n oldLineFor.push(ol)\n newLineFor.push(nl)\n if (op.op !== 'add')\n ol++\n if (op.op !== 'remove')\n nl++\n }\n\n // Group indices of change ops into hunks with `contextLines` padding.\n // Each entry: `[startIdx, endIdx]` inclusive into `ops`.\n const hunks: Array<[number, number]> = []\n for (let i = 0; i < ops.length; i++) {\n if (ops[i].op === 'context')\n continue\n const start = Math.max(0, i - contextLines)\n const end = Math.min(ops.length - 1, i + contextLines)\n const last = hunks[hunks.length - 1]\n if (last && start <= last[1] + 1)\n last[1] = Math.max(last[1], end)\n else\n hunks.push([start, end])\n }\n\n // No-op edit (e.g. `edit` with old_string === new_string) → empty diff.\n if (hunks.length === 0)\n return ''\n\n const parts: string[] = []\n parts.push(isNewFile ? '--- /dev/null' : `--- a/${payload.path}`)\n parts.push(`+++ b/${payload.path}`)\n\n for (const [start, end] of hunks) {\n const slice = ops.slice(start, end + 1)\n const oldCount = slice.filter(l => l.op !== 'add').length\n const newCount = slice.filter(l => l.op !== 'remove').length\n // For hunks that contain only adds (oldCount === 0), git emits the\n // line *before* the insertion, with count 0. We replicate that by\n // using `oldLineFor[start] - 1` clamped at 0.\n const oldStart = oldCount === 0\n ? Math.max(0, oldLineFor[start] - 1)\n : oldLineFor[start]\n const newStart = newCount === 0\n ? Math.max(0, newLineFor[start] - 1)\n : newLineFor[start]\n parts.push(`@@ -${oldStart},${oldCount} +${newStart},${newCount} @@`)\n for (const line of slice) {\n const prefix = line.op === 'add' ? '+' : line.op === 'remove' ? '-' : ' '\n parts.push(`${prefix}${line.text}`)\n }\n }\n return `${parts.join('\\n')}\\n`\n}\n\n// ---------------------------------------------------------------------------\n// summarizeEditPayload — compact-mode digest of an edit call.\n//\n// Produces per-hunk stats + a one-line preview suitable for the\n// transcript's compact diff view (`Settings.editDiffDisplay === 'compact'`).\n// Uses `priorContent` when available so each summary entry carries the\n// real file line number; falls back to per-hunk LCS over the snippet\n// pair when priorContent is absent (historical replay).\n//\n// Multi-file safety: this only summarizes the payload it's handed; the\n// caller is responsible for separating per-file payloads.\n// ---------------------------------------------------------------------------\n\nexport interface EditHunkSummary {\n /** 1-based line number in the new file where the change starts; undefined when unknown (no priorContent). */\n line?: number\n /** Number of lines added in this hunk. */\n added: number\n /** Number of lines removed in this hunk. */\n removed: number\n /** First removed line preview (trimmed, may be empty for pure additions). */\n firstOld?: string\n /** First added line preview (trimmed, may be empty for pure deletions). */\n firstNew?: string\n}\n\nexport interface EditSummary {\n totalAdded: number\n totalRemoved: number\n hunks: EditHunkSummary[]\n}\n\n/**\n * Build a per-hunk digest used by the compact diff view.\n *\n * Strategy:\n * - When `priorContent` is present and the payload describes a real\n * file transformation, compute the contextual diff once, then walk\n * the LCS ops splitting at runs of `add` / `remove` to anchor each\n * summary entry to the **real** file line. This guarantees the\n * summary's `L<n>` matches what the user sees in their editor.\n * - Otherwise, fall back to per-hunk LCS over the (oldString,\n * newString) snippet pair. Line numbers are absent because the\n * snippet has no file position.\n */\nexport function summarizeEditPayload(payload: EditPayload): EditSummary {\n const prior = payload.priorContent\n if (prior !== undefined) {\n const newContent = applyEditPayload(payload, prior)\n const ops = computeLineDiff(prior, newContent)\n return summarizeOpsByHunk(ops)\n }\n\n // No prior content — diff each (oldString, newString) snippet pair\n // in isolation. Line numbers can't be derived; we still get\n // meaningful +/− counts and a first-line preview.\n const hunks: EditHunkSummary[] = []\n let totalAdded = 0\n let totalRemoved = 0\n for (const hunk of payload.hunks) {\n const ops = computeLineDiff(hunk.oldString, hunk.newString)\n let added = 0\n let removed = 0\n let firstOld: string | undefined\n let firstNew: string | undefined\n for (const op of ops) {\n if (op.op === 'add') {\n added++\n if (firstNew === undefined)\n firstNew = op.text\n }\n else if (op.op === 'remove') {\n removed++\n if (firstOld === undefined)\n firstOld = op.text\n }\n }\n totalAdded += added\n totalRemoved += removed\n hunks.push({\n added,\n removed,\n ...(firstOld !== undefined ? { firstOld } : {}),\n ...(firstNew !== undefined ? { firstNew } : {}),\n })\n }\n return { totalAdded, totalRemoved, hunks }\n}\n\n/**\n * Walk an LCS op stream and emit one summary entry per *run* of\n * non-context ops, with the new-file line number where each run\n * starts. Adjacent add/remove ops collapse into the same entry —\n * matches git's hunk grouping at zero context.\n */\nfunction summarizeOpsByHunk(ops: readonly DiffLine[]): EditSummary {\n const hunks: EditHunkSummary[] = []\n let totalAdded = 0\n let totalRemoved = 0\n let nl = 1\n let i = 0\n while (i < ops.length) {\n const op = ops[i]\n if (op.op === 'context') {\n nl++\n i++\n continue\n }\n const runStartLine = nl\n let added = 0\n let removed = 0\n let firstOld: string | undefined\n let firstNew: string | undefined\n while (i < ops.length && ops[i].op !== 'context') {\n const cur = ops[i]\n if (cur.op === 'add') {\n added++\n if (firstNew === undefined)\n firstNew = cur.text\n nl++\n }\n else {\n removed++\n if (firstOld === undefined)\n firstOld = cur.text\n }\n i++\n }\n totalAdded += added\n totalRemoved += removed\n hunks.push({\n line: runStartLine,\n added,\n removed,\n ...(firstOld !== undefined ? { firstOld } : {}),\n ...(firstNew !== undefined ? { firstNew } : {}),\n })\n }\n return { totalAdded, totalRemoved, hunks }\n}\n\n// ---------------------------------------------------------------------------\n// previewEditPayload — model-faithful preview that mirrors the tool body's\n// lenient resolver (curly-quote recovery, line-number-prefix stripping,\n// model-side `<n>`→`<name>` desanitize). The naive `applyEditPayload`\n// only does exact `String.replace`, which renders a blank diff whenever\n// the model emits an `old_string` the tool body would still recover via\n// `resolveOldString`. The modal uses this so what the user previews\n// matches what the tool would actually apply.\n//\n// Returns per-hunk `resolution` metadata so the modal can:\n// - mark hunks the tool wouldn't find (`resolved: false`) with a\n// warning glyph in the list,\n// - render an explanatory fallback panel instead of an empty diff box.\n// ---------------------------------------------------------------------------\n\nexport interface HunkResolution {\n /**\n * True when the tool body would find this hunk's `old_string` AND apply\n * it without ambiguity. False when `resolveOldString` returned null OR\n * the match was ambiguous (multiple occurrences and `replace_all` off).\n */\n resolved: boolean\n /** Path the resolver took — `'exact'` when no recovery was needed. */\n via?: ResolvedMatch['via']\n /** Match count in the running content for the hunk. */\n occurrences?: number\n /** True when the resolver found >1 match without `replace_all`. */\n ambiguous?: boolean\n}\n\nexport interface PreviewResult {\n /** Full unified diff of the (resolvable) hunks against `priorContent`. */\n diffText: string\n /** 1:1 with `payload.hunks`. */\n resolution: HunkResolution[]\n /**\n * Per-hunk isolated diff text rendered against the cumulative content\n * after applying all earlier resolved hunks. Useful for the modal's\n * focused-hunk view so hunk #N's preview reflects the state hunks\n * 1..N-1 will leave the file in (same as the tool's order).\n */\n perHunkDiff: string[]\n /**\n * Resolved hunks with `oldString` rewritten to the haystack's actual\n * bytes and `newString` re-styled to preserve curly-quote typography\n * / line-prefix conventions. Pass this to `buildContextualDiff` to\n * paint the unified diff.\n */\n resolvedPayload: EditPayload\n}\n\nexport function previewEditPayload(\n payload: EditPayload,\n priorContent: string,\n contextLines = 3,\n): PreviewResult {\n const resolution: HunkResolution[] = []\n const resolvedHunks: EditHunk[] = []\n const perHunkDiff: string[] = []\n let running = priorContent\n\n for (const hunk of payload.hunks) {\n // `write_file` synthesizes a hunk with `oldString = priorContent`,\n // which is always a perfect match by construction.\n if (hunk.oldString === '' || hunk.oldString === running) {\n resolution.push({ resolved: true, via: 'exact', occurrences: 1 })\n resolvedHunks.push(hunk)\n perHunkDiff.push(buildContextualDiff(\n { ...payload, hunks: [hunk] },\n running,\n contextLines,\n ))\n running = hunk.newString\n continue\n }\n\n const match = resolveOldString(running, hunk.oldString)\n if (!match) {\n resolution.push({ resolved: false })\n resolvedHunks.push(hunk)\n perHunkDiff.push('')\n continue\n }\n\n const ambiguous = match.occurrences > 1 && !hunk.replaceAll\n const styledNew = styleReplacementForVia(hunk.newString, match.via, match.actual)\n const resolvedHunk: EditHunk = {\n oldString: match.actual,\n newString: styledNew,\n ...(hunk.replaceAll ? { replaceAll: true } : {}),\n }\n resolution.push({\n resolved: !ambiguous,\n via: match.via,\n occurrences: match.occurrences,\n ...(ambiguous ? { ambiguous: true } : {}),\n })\n resolvedHunks.push(resolvedHunk)\n // Ambiguous matches paint nothing — the tool would reject the call;\n // the modal renders the `UnresolvedHunkPanel` instead so the user\n // can see what went wrong rather than a misleading first-occurrence\n // preview.\n perHunkDiff.push(ambiguous\n ? ''\n : buildContextualDiff(\n { ...payload, hunks: [resolvedHunk] },\n running,\n contextLines,\n ))\n if (!ambiguous) {\n running = hunk.replaceAll\n ? running.replaceAll(match.actual, styledNew)\n : running.replace(match.actual, styledNew)\n }\n }\n\n const resolvedPayload: EditPayload = { ...payload, hunks: resolvedHunks }\n // Whole-payload diff renders only the hunks the tool would actually\n // apply. Ambiguous + unresolved hunks are surfaced via `resolution`\n // instead — letting them through here would paint misleading\n // first-occurrence diffs.\n const applicableHunks = resolvedHunks.filter((_, i) => resolution[i].resolved)\n const diffText = applicableHunks.length === 0\n ? ''\n : buildContextualDiff(\n { ...payload, hunks: applicableHunks },\n priorContent,\n contextLines,\n )\n\n return { diffText, resolution, perHunkDiff, resolvedPayload }\n}\n\nexport function buildUnifiedDiff(payload: EditPayload): string {\n const parts: string[] = []\n const isNewFile = payload.tool === 'write_file' && payload.hunks[0]?.oldString === ''\n // File header. Both `---` and `+++` lines are mandatory per the parser\n // (see `Missing \"--- ...\"` throw in the upstream parseDiff).\n parts.push(isNewFile ? `--- /dev/null` : `--- a/${payload.path}`)\n parts.push(`+++ b/${payload.path}`)\n\n for (const hunk of payload.hunks) {\n const lines = computeLineDiff(hunk.oldString, hunk.newString)\n const oldCount = lines.filter(l => l.op !== 'add').length\n const newCount = lines.filter(l => l.op !== 'remove').length\n const oldStart = oldCount === 0 ? 0 : 1\n const newStart = newCount === 0 ? 0 : 1\n parts.push(`@@ -${oldStart},${oldCount} +${newStart},${newCount} @@`)\n for (const line of lines) {\n const prefix = line.op === 'add' ? '+' : line.op === 'remove' ? '-' : ' '\n parts.push(`${prefix}${line.text}`)\n }\n }\n return `${parts.join('\\n')}\\n`\n}\n\n// ---------------------------------------------------------------------------\n// File extension → tree-sitter filetype name.\n//\n// Names must match what `setupTreeSitter` registered (`bash`, `python`,\n// `rust`, `go`, `json`, `yaml`, `html`, `css`) plus what OpenTUI ships\n// out of the box (`typescript`, `tsx`, `javascript`, `jsx`, `markdown`).\n// Unknown extensions return `undefined` — the `<diff>` renderable falls\n// back to plain text rendering without highlighting (no error, no crash).\n// ---------------------------------------------------------------------------\n\nconst FILETYPE_BY_EXT: Readonly<Record<string, string>> = {\n ts: 'typescript',\n mts: 'typescript',\n cts: 'typescript',\n tsx: 'tsx',\n js: 'javascript',\n mjs: 'javascript',\n cjs: 'javascript',\n jsx: 'jsx',\n py: 'python',\n pyi: 'python',\n rs: 'rust',\n go: 'go',\n json: 'json',\n jsonc: 'json',\n sh: 'bash',\n bash: 'bash',\n zsh: 'bash',\n yaml: 'yaml',\n yml: 'yaml',\n html: 'html',\n htm: 'html',\n css: 'css',\n md: 'markdown',\n markdown: 'markdown',\n}\n\nexport function filetypeFromPath(path: string): string | undefined {\n // Strip query / fragment so `path?ts=…` style references still match.\n const cleaned = path.split(/[?#]/, 1)[0]\n const lastDot = cleaned.lastIndexOf('.')\n if (lastDot === -1 || lastDot === cleaned.length - 1)\n return undefined\n const ext = cleaned.slice(lastDot + 1).toLowerCase()\n return FILETYPE_BY_EXT[ext]\n}\n","// ---------------------------------------------------------------------------\n// Transcript visibility + select-turn navigation — pure data rules consumed\n// by any renderer that walks a `StreamEvent[]`. They encode the user-facing\n// semantics (settings + edit-tool error prefixes) rather than any\n// presentation primitive, and import nothing node-bound — so they live in\n// their own leaf module that a browser-context renderer can import via\n// `zidane/chat/pure`. `store.ts` re-exports them for back-compat; the TUI\n// re-exports them via `zidane/tui`.\n// ---------------------------------------------------------------------------\n\nimport type { Settings, StreamEvent } from './types'\n\n/** Tools whose `tool-result` event is suppressed when `showEditDiffs` is on. */\nexport const EDIT_TOOL_NAMES: ReadonlySet<string> = new Set(['edit', 'multi_edit', 'write_file'])\n\n/**\n * Recognize a tool-result body as carrying NON-success information so the\n * renderer doesn't suppress it under `showEditDiffs`. Three categories:\n *\n * - `edit` → \"Edit error: …\"\n * - `write_file` permission errors wrapped by the loop → \"Tool failed: …\"\n * - `multi_edit` → legacy single-line error `multi_edit error: …`, OR\n * a result carrying an `<edit-outcomes>…</edit-outcomes>` annotation\n * block. The TUI only appends the annotation when at least one hunk\n * was NOT applied, so its mere presence is the signal — the result\n * body needs to stay visible next to the diff so the user can read\n * denial / skip / failure reasons longer than the per-hunk badge.\n * - Fully-denied gate emit (`[fully denied] <edit-outcomes>…`) likewise\n * stays visible.\n *\n * Exported for unit-testability of the visibility matrix.\n */\nexport function isEditErrorResult(text: string): boolean {\n if (text.startsWith('Edit error:'))\n return true\n if (text.startsWith('Tool failed:'))\n return true\n if (text.startsWith('multi_edit error:'))\n return true\n if (text.startsWith('[fully denied]'))\n return true\n // The annotation block only appears when at least one hunk was NOT\n // applied (see `src/tui/app.tsx` `annotateEditResult`), so a present\n // sentinel pair signals \"denial / skip / failure attached — surface\n // the result body so reasons are readable next to the diff badges\".\n if (text.includes('\\n<edit-outcomes>\\n') || text.startsWith('<edit-outcomes>\\n'))\n return true\n return false\n}\n\n/**\n * Per-event visibility — filters honor user toggles and the\n * `hideSubagentOutput` setting. When subagent output is hidden:\n * - Child-agent events are filtered down to the `spawn-start` /\n * `spawn-end` markers so the user still sees \"🌱 working… 🌳 done\".\n * - The parent's `tool-result` for `spawn` is hidden too. Its body\n * duplicates `spawn-end`'s stats line *and* the parent's next\n * markdown turn; showing it again produces an extra\n * `┃ [sub-agent child-1] Completed …` block users just want gone.\n *\n * Renderer-agnostic — returns plain `boolean` so TUI / GUI consumers\n * can filter events identically.\n */\nexport function isVisible(event: StreamEvent, settings: Settings): boolean {\n if (settings.hideSubagentOutput) {\n if ((event.depth ?? 0) > 0)\n return event.kind === 'spawn-start' || event.kind === 'spawn-end'\n if (event.kind === 'tool-result' && event.tool === 'spawn')\n return false\n }\n // Suppress successful `tool-result`s for edit/multi_edit/write_file when\n // the diff renderer is on — the diff itself is the success indicator;\n // the `Edited path: N replacements.` line just adds noise. Error\n // results bypass the suppression so failures stay visible.\n if (\n settings.showEditDiffs\n && event.kind === 'tool-result'\n && event.tool\n && EDIT_TOOL_NAMES.has(event.tool)\n && !isEditErrorResult(event.text)\n ) {\n return false\n }\n switch (event.kind) {\n case 'thinking': return settings.showThinking\n case 'tool': return settings.toolCallDisplay !== 'hidden'\n case 'tool-result': return settings.showToolResults\n default: return true\n }\n}\n\n/**\n * Build the `resultTurnId → owningAssistantTurnId` map used by the select-\n * turn mode to coalesce a tool-call's surrounding turns into ONE navigation\n * stop.\n *\n * Protocol shape: every `tool_call` block in an assistant turn is closed by\n * a matching `tool_result` block in the *next* user turn (the agent loop's\n * history validator depends on this). When the next user turn's only events\n * are `tool-result`s — i.e. it's pure plumbing for the prior assistant\n * turn — we map it back to that assistant turn here. The select-turn nav\n * index ({@link selectableTurnIds}) skips owned turns, and the renderer's\n * highlight gate ({@link isTurnHighlighted}) extends the selection accent\n * from the assistant turn to the events of any turn it owns. Net effect:\n *\n * - Navigation never lands the cursor on a result-only turn whose own\n * events may be hidden by `showToolResults: false` — the cursor\n * wouldn't be visible.\n * - Selecting an assistant turn highlights the call AND its result as\n * one unit, matching the user's mental model of \"one message\".\n *\n * Owner-lookup is conservative: result-only turns with no matching prior\n * assistant turn (orphaned — usually because the parent was deleted)\n * stay selectable so the user can act on them via the turn-details modal.\n *\n * Subagent (`childId` set) events are ignored — they live in a separate\n * conversation tree.\n */\nexport function turnSelectionOwnership(events: readonly StreamEvent[]): Map<string, string> {\n // Build per-turn event-kind summary in render order. We need:\n // - Whether a turn is \"result-only\" (every event is `tool-result`).\n // - The most recent previous turn that emitted a `tool` event (= the\n // candidate owner for the next result-only turn).\n const orderedTurnIds: string[] = []\n const eventKindsByTurn = new Map<string, StreamEvent['kind'][]>()\n for (const e of events) {\n if (!e.turnId)\n continue\n if (e.childId)\n continue\n if (!eventKindsByTurn.has(e.turnId)) {\n orderedTurnIds.push(e.turnId)\n eventKindsByTurn.set(e.turnId, [])\n }\n eventKindsByTurn.get(e.turnId)!.push(e.kind)\n }\n\n const ownership = new Map<string, string>()\n let lastToolEmitterTurnId: string | null = null\n for (const tid of orderedTurnIds) {\n const kinds = eventKindsByTurn.get(tid)!\n const isResultOnly = kinds.length > 0 && kinds.every(k => k === 'tool-result')\n if (isResultOnly) {\n if (lastToolEmitterTurnId)\n ownership.set(tid, lastToolEmitterTurnId)\n // No owner found — leave the orphan selectable. Don't update\n // `lastToolEmitterTurnId` either (a result-only turn never emits\n // a `tool` event itself).\n continue\n }\n if (kinds.includes('tool'))\n lastToolEmitterTurnId = tid\n }\n return ownership\n}\n\n/**\n * Render-time check: should `event` paint with the selection accent?\n *\n * `true` when the event's own turn is selected, OR when the selected turn\n * `owns` the event's turn via {@link turnSelectionOwnership} (the call and\n * its tool-result rows highlight together). `false` when nothing is\n * selected or the relationship doesn't apply.\n *\n * Pure. Renderer-agnostic — the TUI's `<Transcript>` uses it; a GUI's\n * equivalent walks the same rule.\n */\nexport function isTurnHighlighted(\n event: Pick<StreamEvent, 'turnId'>,\n selectedTurnId: string | null,\n ownership: ReadonlyMap<string, string>,\n): boolean {\n if (selectedTurnId === null || !event.turnId)\n return false\n if (event.turnId === selectedTurnId)\n return true\n return ownership.get(event.turnId) === selectedTurnId\n}\n\n/**\n * Deduplicated, in-order list of **parent-conversation** turn ids that appear\n * in a rendered transcript — the navigation index for the TUI's select-turn\n * mode. Three classes of turns are deliberately skipped:\n *\n * - **Subagent turns** (`childId` set). Nested execution detail; the\n * user's mental model of a \"message\" is the conversational exchange,\n * not each spawn turn. Also filtered out by `isVisible` under\n * `hideSubagentOutput: true` — selecting them would highlight nothing.\n * - **Result-only turns** — see {@link turnSelectionOwnership}. These get\n * coalesced into the assistant turn that emitted their tool_calls.\n * - **Settings-hidden turns** (when `settings` is supplied). A turn whose\n * every event fails {@link isVisible} would render no rows — landing\n * the cursor there hides it from the user entirely. The check is opt-\n * in so SDK callers without a Settings object keep the legacy\n * \"everything visible\" behavior.\n *\n * Synthetic events (separator, spawn-start, spawn-end) have no `turnId` and\n * are skipped naturally.\n */\nexport function selectableTurnIds(\n events: readonly StreamEvent[],\n settings?: Settings,\n): string[] {\n const ownership = turnSelectionOwnership(events)\n\n // Per-turn visible event count (only computed when settings is supplied).\n // We need this to drop turns whose every event is filtered out by user\n // toggles — otherwise `↑/↓` could land the cursor on a row that paints\n // nothing visible.\n const visibleCount = settings ? new Map<string, number>() : null\n if (settings && visibleCount) {\n for (const e of events) {\n if (!e.turnId || e.childId)\n continue\n if (!isVisible(e, settings))\n continue\n visibleCount.set(e.turnId, (visibleCount.get(e.turnId) ?? 0) + 1)\n }\n }\n\n const seen = new Set<string>()\n const ordered: string[] = []\n for (const e of events) {\n if (!e.turnId)\n continue\n if (e.childId)\n continue\n if (seen.has(e.turnId))\n continue\n if (ownership.has(e.turnId))\n continue // result-only — owned by an earlier assistant turn\n if (visibleCount && (visibleCount.get(e.turnId) ?? 0) === 0)\n continue // every event of this turn is hidden by user settings\n seen.add(e.turnId)\n ordered.push(e.turnId)\n }\n return ordered\n}\n","/**\n * Cross-provider model catalog — assembly + fuzzy filtering.\n *\n * Renderer-agnostic helpers consumed by `ModelPickerModal` (TUI) and any\n * future UI that wants a unified \"pick a model across all available\n * providers\" experience. Pure: no React, no I/O, no provider construction.\n *\n * The catalog is the flat union of every available provider's models,\n * each entry tagged with the provider it belongs to so the picker can\n * re-issue a `Picked` tuple `{ providerKey, modelId }` on commit.\n */\n\nimport type { ProviderAuth, ProviderKey } from './auth'\nimport type { ModelInfo } from './providers'\n\n/** A model entry as displayed in the cross-provider picker. */\nexport interface CatalogEntry {\n providerKey: ProviderKey\n providerLabel: string\n model: ModelInfo\n /**\n * Pre-computed lowercase corpus for substring search across the\n * provider key, label, model id, and display name. Built once at\n * catalog-assembly time so filtering on every keystroke is\n * O(catalogSize × queryLength), not O(catalogSize × fieldCount ×\n * lowercase-overhead × queryLength).\n */\n searchCorpus: string\n}\n\n/**\n * Build the unified catalog from a list of available providers.\n *\n * Provider order is preserved (callers typically pass the picker order\n * — alphabetical, auth-detection order, etc.); model order inside each\n * provider matches whatever `modelsFor` returns. The current selection\n * (when set) is bubbled to the top of its provider's section so it\n * shows first without disturbing relative ordering elsewhere.\n *\n * `modelsFor` is injected (not imported from `./providers`) so the same\n * helper works with hosts that supply their own model resolver via\n * `ResolvedConfig.modelsFor`.\n */\nexport function buildModelCatalog(opts: {\n providers: readonly ProviderAuth[]\n modelsFor: (key: ProviderKey) => readonly ModelInfo[]\n /** Optional currently-selected pair — promoted to the top of its provider group. */\n current?: { providerKey: ProviderKey, modelId: string } | null\n}): CatalogEntry[] {\n const entries: CatalogEntry[] = []\n for (const provider of opts.providers) {\n const models = opts.modelsFor(provider.key)\n if (models.length === 0)\n continue\n // Promote the currently-selected model to position 0 inside its\n // own provider section so the picker doesn't bury the user's\n // active row at the bottom of a long provider list.\n let ordered: readonly ModelInfo[] = models\n if (opts.current?.providerKey === provider.key) {\n const idx = models.findIndex(m => m.id === opts.current?.modelId)\n if (idx > 0) {\n const next = models.slice()\n const [active] = next.splice(idx, 1)\n next.unshift(active)\n ordered = next\n }\n }\n for (const model of ordered) {\n entries.push({\n providerKey: provider.key,\n providerLabel: provider.label,\n model,\n searchCorpus: buildSearchCorpus(provider, model),\n })\n }\n }\n return entries\n}\n\n/**\n * Filter `catalog` by a user query. Empty / whitespace-only queries\n * pass everything through unchanged (`O(1)` short-circuit). Multi-term\n * queries (space-separated) require EVERY term to appear somewhere in\n * the entry's search corpus — so `\"claude opus\"` matches `claude-opus-4`\n * regardless of how the words are interleaved with provider names.\n *\n * Match is case-insensitive (the corpus is pre-lowercased; the query\n * is lowercased once per call).\n */\nexport function filterModelCatalog(\n catalog: readonly CatalogEntry[],\n query: string,\n): CatalogEntry[] {\n const trimmed = query.trim().toLowerCase()\n if (!trimmed)\n return catalog.slice()\n const terms = trimmed.split(/\\s+/)\n return catalog.filter(entry => terms.every(t => entry.searchCorpus.includes(t)))\n}\n\n/**\n * Find a catalog entry's index by its `{providerKey, modelId}` tuple.\n * Returns `-1` when not present. Useful when re-rendering the picker\n * (a query just narrowed the list, where did the selection land?).\n */\nexport function indexOfEntry(\n catalog: readonly CatalogEntry[],\n target: { providerKey: ProviderKey, modelId: string } | null | undefined,\n): number {\n if (!target)\n return -1\n return catalog.findIndex(\n e => e.providerKey === target.providerKey && e.model.id === target.modelId,\n )\n}\n\nfunction buildSearchCorpus(provider: ProviderAuth, model: ModelInfo): string {\n const parts = [\n provider.key,\n provider.label,\n model.id,\n model.name ?? '',\n model.provider ?? '',\n ]\n return parts.join(' ').toLowerCase()\n}\n","/**\n * Pure string + reference math for rendering a submitted prompt with chip\n * pills around completion references. Renderer-agnostic — the TUI walks\n * the segments into OpenTUI `<text>` nodes, a GUI walks them into JSX\n * spans + `<span class=\"chip\">` pills. No layout engine assumptions.\n */\n\n/**\n * Highlight span — half-open `[start, end)` over the source string.\n *\n * Offsets are JS string indices (UTF-16 code units) — the same convention used\n * everywhere else in the chat layer (`text[i]`, `m.index`, `String.slice`).\n * Surrogate pairs that straddle a span boundary would render malformed, but\n * realistic prompts in this surface (terminal text + ASCII triggers) keep that\n * risk theoretical; callers feeding emoji-heavy content should normalize to\n * codepoint walks upstream.\n */\nexport interface PromptSegmentRef {\n start: number\n end: number\n /** Provider id tagging this span (`'skills'`, `'files'`, …). Free-form. */\n providerId: string\n}\n\n/**\n * One atomic unit emitted by {@link splitPromptSegments}. Plain segments\n * are word-sized so wraps land cleanly between words; chip segments\n * carry the source `providerId` (`'skills'`, `'files'`, …) so renderers\n * can pick per-kind colors without re-walking the ref list.\n */\nexport type PromptSegment\n = | { kind: 'plain', text: string }\n | { kind: 'chip', text: string, providerId: string }\n\n/**\n * Split a prompt buffer into word-sized atomic segments suitable for a\n * flex-row + flex-wrap renderer (TUI) or a `display: inline` flow with\n * inline-block chips (GUI). Each chip becomes one segment (atomic —\n * never broken across rows); each plain run is split into \"word +\n * trailing space\" units so wraps land at clean word boundaries.\n *\n * Robust to:\n * - Overlapping refs — sorted by start; later refs that overlap are\n * dropped via the first-wins rule.\n * - Out-of-bounds refs — dropped entirely when `end > text.length` or\n * `start >= text.length`. Partial clipping would silently truncate\n * a chip's label; the caller is in a better position to surface the\n * mismatch (typically a stale `refs` array referencing a previous text).\n * - Whitespace-only plain runs — emitted as their own plain segment\n * so chip-adjacent-to-chip cases keep the original spacing.\n *\n * Word splitter rationale: `\\S+\\s*` keeps trailing whitespace attached\n * to its preceding word so wrap boundaries land between words (cleanly).\n * A leading-whitespace-only segment is captured by `\\s+` so we don't\n * drop it entirely when the plain run starts with a space.\n */\nexport function splitPromptSegments(\n text: string,\n refs: readonly PromptSegmentRef[],\n): PromptSegment[] {\n const sorted = [...refs]\n .filter(r => r.end > r.start && r.start < text.length && r.end <= text.length)\n .sort((a, b) => a.start - b.start)\n const out: PromptSegment[] = []\n let cursor = 0\n for (const ref of sorted) {\n if (ref.start < cursor)\n continue // overlapping with a previous ref — drop\n if (ref.start > cursor) {\n const matches = text.slice(cursor, ref.start).match(/\\S+\\s*|\\s+/g) ?? []\n for (const m of matches)\n out.push({ kind: 'plain', text: m })\n }\n out.push({ kind: 'chip', text: text.slice(ref.start, ref.end), providerId: ref.providerId })\n cursor = ref.end\n }\n if (cursor < text.length) {\n const matches = text.slice(cursor).match(/\\S+\\s*|\\s+/g) ?? []\n for (const m of matches)\n out.push({ kind: 'plain', text: m })\n }\n return out\n}\n","/**\n * Streaming finalize reducers + context-window math — pure transforms.\n *\n * No React, no node — so a browser-context renderer imports these via\n * `zidane/chat/pure`. The `useStreamBuffer` React hook + its smooth-stream\n * ticker live in `./streaming` (main barrel) and re-export these for\n * back-compat; a renderer owns its own ticker against the same reducers.\n */\n\nimport type { TurnUsage } from '../types'\nimport type { Owner, StreamEvent } from './types'\nimport { effectiveInputFromTurn } from '../stats'\n\nconst PARENT_OWNER: Owner = 'parent'\n\nexport function ownerOf(event: StreamEvent): Owner {\n return event.childId ?? PARENT_OWNER\n}\n\n/** Flip any trailing streaming markdown blocks (any owner) to finalized. */\nexport function finalizeStreamingMarkdown(events: StreamEvent[]): StreamEvent[] {\n let changed = false\n const next = events.map((e) => {\n if (e.kind === 'markdown' && e.streaming) {\n changed = true\n return { ...e, streaming: false }\n }\n return e\n })\n return changed ? next : events\n}\n\n/** Flip the trailing streaming markdown block for one specific owner. */\nexport function finalizeStreamingMarkdownForOwner(events: StreamEvent[], owner: Owner): StreamEvent[] {\n for (let i = events.length - 1; i >= 0; i--) {\n const e = events[i]\n if (e.kind !== 'markdown')\n continue\n if (!e.streaming)\n continue\n if (ownerOf(e) !== owner)\n continue\n const next = events.slice()\n next[i] = { ...e, streaming: false }\n return next\n }\n return events\n}\n\n/**\n * Effective context size for a single turn.\n *\n * `usage.input` is misleading on its own when prompt caching is active: providers\n * (Anthropic, OpenRouter→Anthropic, Gemini) report `input` as the *new uncached*\n * tokens only — the cached prefix shows up in `cacheRead`, and newly-cached\n * tokens in `cacheCreation`. The model still saw all three buckets, so the real\n * context-window utilization is their sum.\n *\n * Non-caching providers leave `cacheRead`/`cacheCreation` undefined, so this\n * collapses to plain `input` for them.\n */\nexport function turnContextSize(usage: TurnUsage | undefined): number {\n return effectiveInputFromTurn(usage)\n}\n","/**\n * Per-tool display metadata + one-line formatters consumed by any\n * surface that renders a `tool` event in `'formatted'` mode (see\n * `Settings.toolCallDisplay`).\n *\n * Each native tool gets a curated entry — a `displayName` verb that\n * reads in sentence case (e.g. \"Read\", \"Shell\") and a `format` callback\n * that pulls the most informative bits out of the model's raw input to\n * a single scannable line. Unknown tools (MCP servers, host-added\n * tools, future zidane additions) fall back to {@link formatToolCall}\n * returning `null` — the renderer then shows a minimal `↳ <name>` line.\n *\n * Renderer-agnostic: returns plain data (`{ target, meta }`) so the\n * TUI's React/OpenTUI surface and any future GUI consumer can paint\n * the same shape in their own style. Lives in `zidane/chat` because\n * it has no rendering concerns; the TUI just consumes it.\n */\n\nimport { utf8ByteLength } from '../compact/utils'\n\nexport interface ToolFormatLine {\n /**\n * Primary target — typically a path, command, pattern, or\n * task description. Renderer paints this in the model accent color\n * so the eye lands on it first.\n */\n target?: string\n /**\n * Secondary annotations rendered after the target, joined with\n * ` · ` separators. Use for line ranges (`L10-25`), limits, flags,\n * or any short suffix that adds context without bloating the line.\n */\n meta?: readonly string[]\n}\n\nexport interface ToolDisplayMeta {\n /**\n * Title-case display verb (e.g. `Read`, `Shell`, `Edit`). When the\n * label depends on an input field (e.g. `skills_use`'s\n * `mode: 'activate' | 'deactivate'` → `Enable` / `Disable`), supply\n * a function instead and read the field defensively. The function\n * form is called whenever {@link displayNameFor} has the call's\n * input in scope; callers without input fall back to the function's\n * `input: undefined` branch, which should return a stable default.\n */\n displayName: string | ((input: Record<string, unknown> | undefined) => string)\n /**\n * Pull a {@link ToolFormatLine} out of the raw model input. Returns\n * `null` when the shape isn't what the tool expects (typed defensively\n * — we never want a malformed call to crash the transcript).\n */\n format: (input: Record<string, unknown>) => ToolFormatLine | null\n}\n\n// ---------------------------------------------------------------------------\n// Tool registry\n// ---------------------------------------------------------------------------\n\nexport const TOOL_DISPLAY: Readonly<Record<string, ToolDisplayMeta>> = {\n // ---- Pure reads ----\n read_file: {\n displayName: 'Read',\n format: (input) => {\n const path = stringField(input, 'path')\n if (!path)\n return null\n const meta: string[] = []\n const offset = numberField(input, 'offset')\n const limit = numberField(input, 'limit')\n if (offset !== undefined && limit !== undefined && limit > 0)\n meta.push(`L${offset}–${offset + limit - 1}`)\n else if (offset !== undefined)\n meta.push(`from L${offset}`)\n else if (limit !== undefined && limit > 0)\n meta.push(`${limit} lines`)\n return { target: path, meta }\n },\n },\n list_files: {\n displayName: 'List',\n format: (input) => {\n const path = stringField(input, 'path') ?? '.'\n return { target: path }\n },\n },\n glob: {\n displayName: 'Glob',\n format: (input) => {\n const pattern = stringField(input, 'pattern')\n if (!pattern)\n return null\n const meta: string[] = []\n const limit = numberField(input, 'limit')\n if (limit !== undefined)\n meta.push(`limit ${limit}`)\n return { target: pattern, meta }\n },\n },\n grep: {\n displayName: 'Grep',\n format: (input) => {\n const pattern = stringField(input, 'pattern')\n if (!pattern)\n return null\n const target = `/${pattern}/`\n const meta: string[] = []\n const path = stringField(input, 'path')\n if (path && path !== '.')\n meta.push(`in ${path}`)\n const glob = stringField(input, 'glob')\n if (glob)\n meta.push(glob)\n const type = stringField(input, 'type')\n if (type)\n meta.push(`type:${type}`)\n if (input['-i'] === true)\n meta.push('case-insensitive')\n const mode = stringField(input, 'output_mode')\n if (mode && mode !== 'files_with_matches')\n meta.push(mode)\n return { target, meta }\n },\n },\n\n // ---- Shell ----\n shell: {\n // Verb switches when the model dispatches a background task — same\n // pattern `skills_use` uses for activate/deactivate. The transcript\n // reads as `↳ Shell (background): npm run dev` so the user can spot\n // backgrounded invocations at a glance without reading the args.\n displayName: input => input?.run_in_background === true ? 'Shell (background)' : 'Shell',\n format: (input) => {\n const command = stringField(input, 'command')\n if (!command)\n return null\n // Model-provided intent string (\"why am I running this?\") — kept as\n // meta (not target) so the command itself stays the load-bearing\n // identifier in the row. Truncate aggressively: the description is\n // context, the command is the action.\n const description = stringField(input, 'description')\n const line: ToolFormatLine = { target: truncate(command, 200) }\n if (description && description.trim() !== '')\n line.meta = [truncate(description, 100)]\n return line\n },\n },\n shell_kill: {\n displayName: 'Kill task',\n format: (input) => {\n const taskId = stringField(input, 'task_id')\n if (!taskId)\n return null\n return { target: taskId }\n },\n },\n\n // ---- Edits (renderer falls through to EditDiffBlock when showEditDiffs is on) ----\n edit: {\n displayName: 'Edit',\n format: (input) => {\n const path = stringField(input, 'path')\n if (!path)\n return null\n return {\n target: path,\n meta: input.replace_all === true ? ['replace all'] : [],\n }\n },\n },\n multi_edit: {\n displayName: 'Multi-edit',\n format: (input) => {\n const path = stringField(input, 'path')\n if (!path)\n return null\n const edits = Array.isArray(input.edits) ? input.edits.length : 0\n return {\n target: path,\n meta: edits > 0 ? [`${edits} hunk${edits === 1 ? '' : 's'}`] : [],\n }\n },\n },\n write_file: {\n displayName: 'Write',\n format: (input) => {\n const path = stringField(input, 'path')\n if (!path)\n return null\n const content = stringField(input, 'content')\n const meta: string[] = []\n if (content !== undefined) {\n // Show byte count instead of line count — write_file's main\n // failure mode is \"did you mean to overwrite a 10KB file with\n // a 4-byte one?\", so bytes catch it faster than lines do.\n const bytes = utf8ByteLength(content)\n meta.push(`${formatBytes(bytes)}`)\n }\n return { target: path, meta }\n },\n },\n\n // ---- Spawn / orchestration ----\n spawn: {\n displayName: 'Agent',\n format: (input) => {\n const task = stringField(input, 'task')\n if (!task)\n return null\n return { target: truncate(task, 120) }\n },\n },\n\n // ---- Tool search (lazy-tool discovery) ----\n tool_search: {\n displayName: 'Search tools',\n format: (input) => {\n const query = stringField(input, 'query')\n const names = Array.isArray(input.names) ? input.names.length : 0\n if (query)\n return { target: `“${query}”` }\n if (names > 0)\n return { target: `${names} tool${names === 1 ? '' : 's'}` }\n return null\n },\n },\n\n // ---- Skills ----\n skills_use: {\n // `skills_use` straddles two intents — activation (default) and\n // deactivation (`mode: 'deactivate'`, the zidane extension that lets\n // the model recover from `AgentToolNotAllowedError`). Showing \"Activate\"\n // for both modes hid the deactivation from the transcript and made\n // the model's recovery flow harder to follow. We pick the verb from\n // `input.mode`; absent or unexpected values fall back to \"Enable\" to\n // match the spec's activate-only semantics.\n displayName: (input) => {\n const mode = input ? stringField(input, 'mode') : undefined\n return mode === 'deactivate' ? 'Disable skill' : 'Enable skill'\n },\n format: (input) => {\n const name = stringField(input, 'name')\n if (!name)\n return null\n return { target: name }\n },\n },\n skills_read: {\n displayName: 'Read skill',\n format: (input) => {\n const name = stringField(input, 'name')\n const path = stringField(input, 'path')\n if (!name)\n return null\n return { target: path ? `${name}/${path}` : name }\n },\n },\n skills_run_script: {\n displayName: 'Run script',\n format: (input) => {\n const name = stringField(input, 'name')\n const script = stringField(input, 'script')\n if (!name || !script)\n return null\n const meta: string[] = [`skill ${name}`]\n const args = Array.isArray(input.args) ? input.args : null\n if (args && args.length > 0)\n meta.push(truncate(args.map(String).join(' '), 80))\n return { target: script, meta }\n },\n },\n\n // ---- Todos ----\n todowrite: {\n displayName: 'Todos',\n format: (input) => {\n const todos = Array.isArray(input.todos) ? input.todos : null\n if (!todos)\n return null\n // Show item count + per-status tally — same shape the tool's\n // `tool_result` summary line uses, so the call line and result\n // line read consistently in the transcript.\n const counts = { pending: 0, in_progress: 0, completed: 0, cancelled: 0 }\n for (const t of todos) {\n if (!t || typeof t !== 'object')\n continue\n const status = (t as Record<string, unknown>).status\n if (typeof status === 'string' && status in counts)\n counts[status as keyof typeof counts] += 1\n }\n const meta: string[] = []\n if (counts.completed)\n meta.push(`${counts.completed} done`)\n if (counts.in_progress)\n meta.push(`${counts.in_progress} in progress`)\n if (counts.pending)\n meta.push(`${counts.pending} pending`)\n if (counts.cancelled)\n meta.push(`${counts.cancelled} cancelled`)\n return { target: `${todos.length} item${todos.length === 1 ? '' : 's'}`, meta }\n },\n },\n todoread: {\n displayName: 'Todos',\n format: () => ({ target: 'read' }),\n },\n\n // ---- Interactions (already surfaced as wizards, but the call line still renders) ----\n ask_user: {\n displayName: 'Ask user',\n format: (input) => {\n const questions = Array.isArray(input.questions) ? input.questions.length : 0\n if (questions === 0)\n return null\n return { target: `${questions} question${questions === 1 ? '' : 's'}` }\n },\n },\n present_plan: {\n displayName: 'Present plan',\n format: (input) => {\n const title = stringField(input, 'title')\n if (!title)\n return null\n return { target: title }\n },\n },\n}\n\n// ---------------------------------------------------------------------------\n// Public helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the display verb for a tool. Native tools use their curated\n * entry from {@link TOOL_DISPLAY}; everything else gets a sentence-case\n * version of the raw name (`my_host_tool` → `My host tool`) so an MCP /\n * host tool still reads cleanly in the transcript without shouting\n * Title Case at every word.\n *\n * MCP convention: every tool surfaced by `mcp/connectMcpServers` is\n * namespaced as `mcp_<server>_<tool>` (see `src/mcp/index.ts`). The\n * `mcp_` prefix is plumbing — strip it before casing so the label\n * reads as `Github create issue` instead of `Mcp github create issue`.\n * The server name leads, which doubles as a free visual grouping\n * affordance (\"everything starting with `Github` came from the github\n * MCP server\").\n */\nexport function displayNameFor(\n name: string,\n input?: Record<string, unknown>,\n): string {\n const entry = TOOL_DISPLAY[name]\n if (entry) {\n return typeof entry.displayName === 'function'\n ? entry.displayName(input)\n : entry.displayName\n }\n const stripped = name.startsWith('mcp_') ? name.slice(4) : name\n return sentenceCase(stripped)\n}\n\n/**\n * Run a tool's curated formatter and return the result, or `null` when\n * no formatter is registered / the input shape doesn't match. Renderer\n * decides what to do with `null` — typically: show `↳ <displayName>`\n * with no target / meta tail.\n */\nexport function formatToolCall(name: string, input: Record<string, unknown>): ToolFormatLine | null {\n const entry = TOOL_DISPLAY[name]\n if (!entry)\n return null\n try {\n return entry.format(input)\n }\n catch {\n // Defensive — a misshapen `input` should never crash the\n // transcript. Fall back to the unformatted line.\n return null\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction stringField(input: Record<string, unknown>, key: string): string | undefined {\n const v = input[key]\n return typeof v === 'string' && v.length > 0 ? v : undefined\n}\n\nfunction numberField(input: Record<string, unknown>, key: string): number | undefined {\n const v = input[key]\n return typeof v === 'number' && Number.isFinite(v) ? v : undefined\n}\n\n/** `snake_case` / `kebab-case` / lowercase → `Sentence case`. */\nfunction sentenceCase(s: string): string {\n const words = s.split(/[-_\\s]+/).filter(Boolean).map(w => w.toLowerCase())\n if (words.length === 0)\n return ''\n words[0] = (words[0][0]?.toUpperCase() ?? '') + words[0].slice(1)\n return words.join(' ')\n}\n\n/**\n * Collapse internal whitespace (including newlines) to single spaces\n * and clip to `max` columns with a trailing `…`. The whitespace\n * normalisation is the load-bearing bit — tool input strings like a\n * shell heredoc or a multi-line Python `-c` script otherwise render\n * across several rows in the transcript even though the `↳ Tool …`\n * line is meant to be a single-line scannable summary.\n */\nfunction truncate(s: string, max: number): string {\n const clean = s.replace(/\\s+/g, ' ').trim()\n return clean.length <= max ? clean : `${clean.slice(0, max - 1)}…`\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024)\n return `${bytes} B`\n if (bytes < 1024 * 1024)\n return `${(bytes / 1024).toFixed(1)} KB`\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`\n}\n","/**\n * Pure transforms on `SessionTurn[]` used by the TUI's turn-selection\n * modal actions (fork / delete / copy). Each helper is renderer- and\n * store-agnostic so it composes the same in TUI, SDK, or test code.\n *\n * Tool-call protocol invariant the helpers preserve:\n *\n * Every `tool_call` block emitted by an assistant turn MUST be matched\n * by a `tool_result` block (same `callId`) in a later turn before the\n * next assistant turn. Providers reject histories that violate this —\n * so any operation that mutates the turn list must either keep pairs\n * intact or strip both sides.\n */\n\nimport type { SessionContentBlock, SessionTurn } from '../types'\n\n/**\n * Fork — keep every turn up to and including `turnId`, then strip any\n * `tool_call` blocks left without a matching `tool_result` in the slice.\n *\n * Semantics:\n * - Include the selected turn (\"branch from HERE\" mental model — the\n * user wants the selected message to be the latest in the fork).\n * - If the selected turn is an assistant turn with unresolved\n * `tool_call` blocks (their `tool_result`s live in turns AFTER the\n * slice), strip those calls. Otherwise the fork would post an\n * assistant turn with no matching tool results, breaking the next\n * provider call.\n * - Drop turns that become empty (all blocks stripped).\n *\n * Returns `null` when `turnId` doesn't exist in `turns` — caller should\n * surface a \"turn not found\" error rather than silently no-op.\n */\nexport function truncateTurnsAt(turns: readonly SessionTurn[], turnId: string): SessionTurn[] | null {\n const idx = turns.findIndex(t => t.id === turnId)\n if (idx === -1)\n return null\n const slice = turns.slice(0, idx + 1)\n return stripOrphanToolBlocks(slice)\n}\n\n/**\n * Delete — remove the turn with `turnId` and any tool blocks left\n * orphaned by the removal. Returns `null` when `turnId` doesn't exist.\n *\n * Strategy:\n * 1. Drop the target turn.\n * 2. Scan the remaining turns for `tool_call`s without a matching\n * `tool_result` (orphaned by removing the user turn that carried\n * the result), and `tool_result`s without a matching `tool_call`\n * (orphaned by removing the assistant turn that issued the call).\n * Strip both sides.\n * 3. Drop turns whose content is now empty.\n *\n * This guarantees the resulting history is protocol-clean — a follow-up\n * `agent.run()` against the modified session can post turns without the\n * provider rejecting the history.\n */\nexport function deleteTurnSafely(turns: readonly SessionTurn[], turnId: string): SessionTurn[] | null {\n const idx = turns.findIndex(t => t.id === turnId)\n if (idx === -1)\n return null\n const without = [...turns.slice(0, idx), ...turns.slice(idx + 1)]\n return stripOrphanToolBlocks(without)\n}\n\n/**\n * Walk a turn list and remove any tool blocks whose counterpart is\n * missing. Drops turns left empty. Used by `truncateTurnsAt` (which can\n * leave `tool_call`s orphaned when their results are past the cut) and\n * `deleteTurnSafely` (which can orphan either side of a pair).\n *\n * Pure / total: returns a new array; never throws.\n */\nfunction stripOrphanToolBlocks(turns: readonly SessionTurn[]): SessionTurn[] {\n const callIds = new Set<string>()\n const resultIds = new Set<string>()\n for (const turn of turns) {\n for (const block of turn.content) {\n if (block.type === 'tool_call')\n callIds.add(block.id)\n else if (block.type === 'tool_result')\n resultIds.add(block.callId)\n }\n }\n const result: SessionTurn[] = []\n for (const turn of turns) {\n const filtered: SessionContentBlock[] = []\n for (const block of turn.content) {\n if (block.type === 'tool_call') {\n if (!resultIds.has(block.id))\n continue // call lost its result\n }\n else if (block.type === 'tool_result') {\n if (!callIds.has(block.callId))\n continue // result lost its call\n }\n filtered.push(block)\n }\n if (filtered.length === 0)\n continue // turn is now empty — drop it\n result.push(filtered.length === turn.content.length ? turn : { ...turn, content: filtered })\n }\n return result\n}\n\n/**\n * Serialize a turn's content to a clean text representation suited for\n * the clipboard. Joins text + thinking blocks verbatim; tool calls and\n * tool results get bracketed labels so the user can paste a readable\n * record of what happened without losing structure.\n *\n * Empty turns return `''`.\n */\nexport function turnAsText(turn: SessionTurn): string {\n const parts: string[] = []\n for (const block of turn.content) {\n if (block.type === 'text' && block.text.trim())\n parts.push(block.text)\n else if (block.type === 'thinking' && block.text.trim())\n parts.push(`[thinking]\\n${block.text}`)\n else if (block.type === 'tool_call')\n parts.push(`[tool call · ${block.name}]\\n${stringifyArgs(block.input)}`)\n else if (block.type === 'tool_result')\n parts.push(`[tool result]\\n${typeof block.output === 'string' ? block.output : JSON.stringify(block.output, null, 2)}`)\n else if (block.type === 'compact-summary')\n parts.push(`[compaction summary · ${block.replacesTurnIds.length} turn${block.replacesTurnIds.length === 1 ? '' : 's'}]\\n${block.summary}`)\n }\n return parts.join('\\n\\n')\n}\n\nfunction stringifyArgs(input: Record<string, unknown>): string {\n try {\n return JSON.stringify(input, null, 2)\n }\n catch {\n return String(input)\n }\n}\n\n/**\n * Count turns before / after the one identified by `turnId` in the\n * given list. Returns `null` when the id is missing. Used to label the\n * turn-details modal with `N before · M after`.\n */\nexport function countNeighbors(\n turnIds: readonly string[],\n turnId: string,\n): { before: number, after: number } | null {\n const idx = turnIds.indexOf(turnId)\n if (idx === -1)\n return null\n return { before: idx, after: turnIds.length - 1 - idx }\n}\n"],"mappings":"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"}