reasonix 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  CODE_SYSTEM_PROMPT,
4
4
  codeSystemPrompt
5
- } from "./chunk-NXYPGKA3.js";
5
+ } from "./chunk-5DZMZCCW.js";
6
6
  export {
7
7
  CODE_SYSTEM_PROMPT,
8
8
  codeSystemPrompt
9
9
  };
10
- //# sourceMappingURL=prompt-KX6A4DVX.js.map
10
+ //# sourceMappingURL=prompt-2OABSPAW.js.map
package/dist/index.d.ts CHANGED
@@ -1007,6 +1007,14 @@ declare class CacheFirstLoop {
1007
1007
  * the user doesn't watch flash retry the same edit 5 times.
1008
1008
  */
1009
1009
  private _turnFailureCount;
1010
+ /**
1011
+ * Per-type breakdown of failure signals counted toward the turn's
1012
+ * auto-escalation threshold. Surfaced in the warning when the
1013
+ * threshold trips so the user sees what kind of trouble flash
1014
+ * actually hit ("3× search-mismatch, 2× truncated") rather than
1015
+ * just a bare count. Reset alongside _turnFailureCount.
1016
+ */
1017
+ private _turnFailureTypes;
1010
1018
  constructor(opts: CacheFirstLoopOptions);
1011
1019
  /**
1012
1020
  * Shrink the log by re-truncating oversized tool results to a tighter
@@ -1128,14 +1136,25 @@ declare class CacheFirstLoop {
1128
1136
  */
1129
1137
  private modelForCurrentCall;
1130
1138
  /**
1131
- * True when the assistant's content is a self-reported escalation
1132
- * request. Only the FIRST line matters the model is instructed
1133
- * to emit the marker as the first output token if at all. Matching
1134
- * anywhere else in the text is a normal content reference (e.g.
1135
- * the user asked about the marker itself, or prose that happens
1136
- * to contain angle-brackets).
1139
+ * Parse the escalation marker out of the model's leading content.
1140
+ * Returns `{ matched: true, reason? }` for both bare and reason-
1141
+ * carrying forms. Only the FIRST line matters the model is
1142
+ * instructed to emit the marker as the first output token if at
1143
+ * all. Matches anywhere else in the text are normal content
1144
+ * references (e.g. the user asked about the marker itself).
1137
1145
  */
1146
+ private parseEscalationMarker;
1147
+ /** Convenience boolean — same gate the streaming path used to call. */
1138
1148
  private isEscalationRequest;
1149
+ /**
1150
+ * Could `buf` STILL plausibly become the full marker as more chunks
1151
+ * arrive? Drives the streaming buffer's flush decision: while this
1152
+ * is true we keep accumulating; once it's false (or the buffer
1153
+ * exceeds the byte limit) we flush so the user isn't staring at a
1154
+ * delayed display for arbitrary content that just happens to start
1155
+ * with `<`.
1156
+ */
1157
+ private looksLikePartialEscalationMarker;
1139
1158
  /**
1140
1159
  * Check whether a tool result string looks like a "flash struggled"
1141
1160
  * signal and, if so, increment the turn's failure counter. Escalates
@@ -1147,6 +1166,13 @@ declare class CacheFirstLoop {
1147
1166
  * mode (so the loop can surface a one-time warning to the user).
1148
1167
  */
1149
1168
  private noteToolFailureSignal;
1169
+ /**
1170
+ * Render `_turnFailureTypes` as a comma-separated breakdown like
1171
+ * "2× search-mismatch, 1× truncated" for the auto-escalation
1172
+ * warning. Empty if no types have been recorded yet (defensive —
1173
+ * the warning sites only call this after a bump).
1174
+ */
1175
+ private formatFailureBreakdown;
1150
1176
  private buildMessages;
1151
1177
  /**
1152
1178
  * Signal the currently-running {@link step} to stop **now**. Cancels
@@ -1172,14 +1198,29 @@ declare class CacheFirstLoop {
1172
1198
  step(userInput: string): AsyncGenerator<LoopEvent>;
1173
1199
  private forceSummaryAfterIterLimit;
1174
1200
  run(userInput: string, onEvent?: (ev: LoopEvent) => void): Promise<string>;
1201
+ /**
1202
+ * Build an assistant message for the log. The `producingModel` arg is
1203
+ * the model that actually generated this turn (flash, pro, the
1204
+ * forced-summary flash call, `this.model` for synthetics, etc.) —
1205
+ * NOT `this.model`, because escalation + forced-summary can both
1206
+ * route a single turn to a different model.
1207
+ *
1208
+ * The single invariant this encodes: if the producing model is
1209
+ * thinking-mode, `reasoning_content` MUST be present on the
1210
+ * persisted message — even as an empty string. DeepSeek's validator
1211
+ * 400s the NEXT request if any historical thinking-mode assistant
1212
+ * turn is missing it. We used to gate on `reasoning.length > 0`,
1213
+ * which silently dropped the field whenever the stream emitted zero
1214
+ * reasoning deltas or the API returned `reasoning_content: null` —
1215
+ * both legitimate edge cases the 0.5.15/0.5.18 fixes missed.
1216
+ */
1175
1217
  private assistantMessage;
1176
1218
  /**
1177
- * Build a synthetic assistant message we insert into the log without
1178
- * a real API round trip (abort notices, future system injections).
1179
- * Reasoner models reject follow-up requests whose assistant history
1180
- * is missing `reasoning_content`, so we stamp an empty-string
1181
- * placeholder on reasoner sessions to satisfy the validator. V3
1182
- * doesn't care — field stays absent there.
1219
+ * Synthetic assistant message (abort notices, future system injections)
1220
+ * no real API round trip. Delegates to {@link assistantMessage} with
1221
+ * `this.model` as the stand-in producer, so the same thinking-mode
1222
+ * invariant applies: reasoner sessions get an empty-string
1223
+ * `reasoning_content`; V3 sessions get nothing.
1183
1224
  */
1184
1225
  private syntheticAssistantMessage;
1185
1226
  }
@@ -1659,53 +1700,210 @@ interface MemoryToolsOptions {
1659
1700
  declare function registerMemoryTools(registry: ToolRegistry, opts?: MemoryToolsOptions): ToolRegistry;
1660
1701
 
1661
1702
  /**
1662
- * Plan Mode read-only exploration phase for `reasonix code`.
1663
- *
1664
- * Shape (mirrors claude-code's plan/act split, adapted for Reasonix):
1703
+ * ask_choicethe primitive for "user needs to pick between alternatives".
1665
1704
  *
1666
- * 1. User types `/plan` registry switches to plan-mode enforcement
1667
- * (write tools refused at dispatch; reads + allowlisted shell
1668
- * still work).
1669
- * 2. Model explores, then calls `submit_plan` with a markdown plan.
1670
- * 3. `submit_plan` throws `PlanProposedError`, which the TUI renders
1671
- * as a picker: Approve / Refine / Cancel.
1672
- * 4. Approve → registry leaves plan mode, a synthetic user message
1673
- * "The plan has been approved. Implement it now." is pushed into
1674
- * the loop so the next turn executes.
1705
+ * Why it exists: `submit_plan` is for ONE concrete plan the user approves.
1706
+ * Models routinely misused it to present A/B/C option menus, leaving the
1707
+ * user stuck with an approve/refine/cancel picker that had no way to
1708
+ * select a route. `ask_choice` gives branching its own tool so plan
1709
+ * mode stays about one actionable thing at a time.
1675
1710
  *
1676
- * The read-only enforcement lives in `ToolRegistry.dispatch` via
1677
- * `readOnly` / `readOnlyCheck`; this file only ships the `submit_plan`
1678
- * escape hatch and the error type that carries the plan out of the
1679
- * registry without stuffing it into the message.
1711
+ * Shape mirrors `submit_plan`:
1712
+ * 1. Model calls `ask_choice` with a question and 2–4 options.
1713
+ * 2. The tool throws `ChoiceRequestedError`; the registry serializes
1714
+ * the payload via `toToolResult`.
1715
+ * 3. TUI parses the tagged error, mounts `ChoiceConfirm`, user picks
1716
+ * one option (or types a custom answer via the escape hatch, or
1717
+ * cancels).
1718
+ * 4. A synthetic user message feeds the choice back — "user picked
1719
+ * <id>" or "user answered: <text>" — and the loop resumes.
1680
1720
  *
1681
- * We do not change `ImmutablePrefix.toolSpecs` when plan mode toggles
1682
- * that would break Pillar 1's prefix cache. Instead the same full spec
1683
- * list stays pinned, and the registry enforces mode at dispatch time.
1684
- * The refusal string teaches the model the rule; cache hits stay
1685
- * intact.
1721
+ * Auto-flatten note: the `options` array of objects is exactly the
1722
+ * schema shape that DeepSeek V3/R1 is known to drop. `ToolRegistry`
1723
+ * auto-flattens and re-nests on dispatch (Pillar 3), so we don't need
1724
+ * to hand-flatten here. We still `sanitizeOptions` at runtime because
1725
+ * even with flatten-repair, models occasionally emit empty strings or
1726
+ * miss fields entirely.
1686
1727
  */
1687
1728
 
1688
1729
  /**
1689
- * Thrown by `submit_plan` when plan mode is active, carrying the plan
1690
- * text the TUI will render for the user's approval.
1730
+ * One option in a branching question. `id` is what gets fed back to
1731
+ * the model when the user picks; keep it short and stable (A, B, C,
1732
+ * or option-1 / option-2 / ...). `summary` is optional extra context
1733
+ * the UI shows as a dimmed sub-line under the title.
1734
+ */
1735
+ interface ChoiceOption {
1736
+ id: string;
1737
+ title: string;
1738
+ summary?: string;
1739
+ }
1740
+ /**
1741
+ * Thrown by `ask_choice`. Carries the branching question plus the
1742
+ * options list out to the TUI via the `toToolResult` protocol. The
1743
+ * error message tells the model to STOP so it doesn't race past the
1744
+ * picker with more tool calls — same pattern as `PlanProposedError`.
1745
+ */
1746
+ declare class ChoiceRequestedError extends Error {
1747
+ readonly question: string;
1748
+ readonly options: ChoiceOption[];
1749
+ readonly allowCustom: boolean;
1750
+ constructor(question: string, options: ChoiceOption[], allowCustom: boolean);
1751
+ toToolResult(): {
1752
+ error: string;
1753
+ question: string;
1754
+ options: ChoiceOption[];
1755
+ allowCustom: boolean;
1756
+ };
1757
+ }
1758
+ interface ChoiceToolOptions {
1759
+ /**
1760
+ * Side-channel preview fired when the model asks. The tool-result
1761
+ * event also carries the payload; this is the earlier hook for
1762
+ * test harnesses or alternative UIs that don't want to parse JSON.
1763
+ */
1764
+ onChoiceRequested?: (question: string, options: ChoiceOption[]) => void;
1765
+ }
1766
+ declare function registerChoiceTool(registry: ToolRegistry, opts?: ChoiceToolOptions): ToolRegistry;
1767
+
1768
+ /**
1769
+ * Shared types for Plan Mode. Consumed by plan-errors.ts (error
1770
+ * classes carry these as fields) and plan-core.ts (tool registration
1771
+ * validates against them). Kept in a separate module so a consumer
1772
+ * that only wants the types doesn't pull in either the error classes
1773
+ * or the registration machinery.
1774
+ */
1775
+ type PlanStepRisk = "low" | "med" | "high";
1776
+ /**
1777
+ * Structured step in a submitted plan. Optional — plans can still be
1778
+ * pure markdown. When provided, each step is addressable by `id` so
1779
+ * the model can later mark it complete via `mark_step_complete`.
1780
+ */
1781
+ interface PlanStep {
1782
+ id: string;
1783
+ title: string;
1784
+ action: string;
1785
+ /**
1786
+ * Optional self-reported risk level. Drives the colored dot gutter
1787
+ * in PlanConfirm / PlanCheckpointConfirm: green (low) / yellow
1788
+ * (med) / red (high). High-risk steps are the ones the user should
1789
+ * actually read before approving — everything else is noise.
1790
+ * Omitted when the model didn't categorize (treated as neutral).
1791
+ */
1792
+ risk?: PlanStepRisk;
1793
+ }
1794
+ /**
1795
+ * Payload surfaced by `mark_step_complete` via `PlanCheckpointError`.
1796
+ * The TUI parses the tool result JSON, pushes a `✓ step` progress row,
1797
+ * and mounts the checkpoint picker. `kind` is kept on the payload so
1798
+ * consumers that peek at the JSON can dispatch on a stable tag.
1799
+ */
1800
+ interface StepCompletion {
1801
+ kind: "step_completed";
1802
+ stepId: string;
1803
+ title?: string;
1804
+ result: string;
1805
+ notes?: string;
1806
+ }
1807
+
1808
+ /**
1809
+ * Error classes for Plan Mode tools. Each one implements the
1810
+ * `toToolResult` protocol so `ToolRegistry.dispatch` serializes the
1811
+ * structured payload into the tool-result JSON — the TUI parses that
1812
+ * shape to mount the right picker (approve / checkpoint / revise).
1691
1813
  *
1692
- * Implements the `toToolResult` protocol so `ToolRegistry.dispatch`
1693
- * serializes the full plan into the tool-result JSON (not just the
1694
- * error message). The TUI parses `{ error, plan }` from the tool event
1695
- * and mounts the `PlanConfirm` picker.
1814
+ * Types live in plan-types.ts; registration logic in plan-core.ts.
1815
+ * Dependency direction: plan-core plan-errors plan-types.
1816
+ */
1817
+
1818
+ /**
1819
+ * Thrown by `submit_plan` when the model has produced a plan for the
1820
+ * user to approve. Carries the markdown body, optional structured
1821
+ * steps, and an optional one-line summary. The TUI uses all three to
1822
+ * render the PlanConfirm picker.
1696
1823
  */
1697
1824
  declare class PlanProposedError extends Error {
1698
1825
  readonly plan: string;
1699
- constructor(plan: string);
1826
+ readonly steps?: PlanStep[];
1827
+ readonly summary?: string;
1828
+ constructor(plan: string, steps?: PlanStep[], summary?: string);
1700
1829
  /**
1701
1830
  * Structured tool-result shape. Consumed by the TUI to extract the
1702
- * plan without regex-scraping the error message.
1831
+ * plan without regex-scraping the error message. Optional fields
1832
+ * are omitted from the payload when absent so consumers don't see
1833
+ * `undefined` keys in the JSON.
1703
1834
  */
1704
1835
  toToolResult(): {
1705
1836
  error: string;
1706
1837
  plan: string;
1838
+ steps?: PlanStep[];
1839
+ summary?: string;
1840
+ };
1841
+ }
1842
+ /**
1843
+ * Thrown by `mark_step_complete`. The registry serializes the
1844
+ * structured payload via `toToolResult`, the TUI catches the error
1845
+ * tag and pauses the loop until the user decides continue / revise /
1846
+ * stop. The error message tells the model to stop calling tools so
1847
+ * it doesn't race past the picker.
1848
+ */
1849
+ declare class PlanCheckpointError extends Error {
1850
+ readonly stepId: string;
1851
+ readonly title?: string;
1852
+ readonly result: string;
1853
+ readonly notes?: string;
1854
+ constructor(update: {
1855
+ stepId: string;
1856
+ title?: string;
1857
+ result: string;
1858
+ notes?: string;
1859
+ });
1860
+ toToolResult(): {
1861
+ error: string;
1862
+ } & StepCompletion;
1863
+ }
1864
+ /**
1865
+ * Thrown by `revise_plan`. Carries the proposed remaining-step list,
1866
+ * a one-sentence reason, and an optional updated summary out to the
1867
+ * TUI. Mirrors PlanProposedError / PlanCheckpointError. The picker
1868
+ * shows a diff between the current remaining steps and the proposed
1869
+ * ones; the user accepts (replaces) or rejects (keeps current).
1870
+ *
1871
+ * Why a separate tool from submit_plan: revising is surgical (replace
1872
+ * the tail of an in-flight plan), submitting is a fresh proposal.
1873
+ * Different intent, different UI. Calling submit_plan again mid-
1874
+ * execution would reset the whole plan including done steps, which
1875
+ * is heavier than usually needed.
1876
+ */
1877
+ declare class PlanRevisionProposedError extends Error {
1878
+ readonly reason: string;
1879
+ readonly remainingSteps: PlanStep[];
1880
+ readonly summary?: string;
1881
+ constructor(reason: string, remainingSteps: PlanStep[], summary?: string);
1882
+ toToolResult(): {
1883
+ error: string;
1884
+ reason: string;
1885
+ remainingSteps: PlanStep[];
1886
+ summary?: string;
1707
1887
  };
1708
1888
  }
1889
+
1890
+ /**
1891
+ * Plan Mode tool registration. Owns `registerPlanTool` — which wires
1892
+ * `submit_plan`, `mark_step_complete`, and `revise_plan` into a
1893
+ * ToolRegistry — plus the arg sanitizers these tools share.
1894
+ *
1895
+ * Structure rationale: the three registrations are parallel in shape
1896
+ * (each throws a structured error the TUI renders as a picker), so
1897
+ * they're broken out into `registerSubmitPlan` / `registerMarkStep` /
1898
+ * `registerRevisePlan` — one per screen of logic rather than one
1899
+ * 230-line `registerPlanTool` body. Tool descriptions live at the top
1900
+ * as named constants so the function bodies stay readable; the strings
1901
+ * themselves are long because they teach the model when to call each
1902
+ * tool, which is load-bearing behavior.
1903
+ *
1904
+ * Dependency direction: plan-core → plan-errors → plan-types.
1905
+ */
1906
+
1709
1907
  interface PlanToolOptions {
1710
1908
  /**
1711
1909
  * Optional side-channel callback fired when the model submits a plan.
@@ -1713,7 +1911,20 @@ interface PlanToolOptions {
1713
1911
  * event is also emitted; this is just earlier and friendlier to
1714
1912
  * test harnesses that don't want to parse JSON).
1715
1913
  */
1716
- onPlanSubmitted?: (plan: string) => void;
1914
+ onPlanSubmitted?: (plan: string, steps?: PlanStep[]) => void;
1915
+ /**
1916
+ * Optional callback fired when the model marks a step complete via
1917
+ * `mark_step_complete`. Analogous to `onPlanSubmitted` — the tool
1918
+ * event carries the same payload, but this firing point is earlier
1919
+ * and avoids JSON parsing for consumers that don't need it.
1920
+ */
1921
+ onStepCompleted?: (update: StepCompletion) => void;
1922
+ /**
1923
+ * Optional preview callback fired when the model proposes a plan
1924
+ * revision via `revise_plan`. Same earlier-than-event timing as
1925
+ * the other on* hooks.
1926
+ */
1927
+ onPlanRevisionProposed?: (reason: string, remainingSteps: PlanStep[], summary?: string) => void;
1717
1928
  }
1718
1929
  declare function registerPlanTool(registry: ToolRegistry, opts?: PlanToolOptions): ToolRegistry;
1719
1930
 
@@ -1811,7 +2022,7 @@ interface SubagentToolOptions {
1811
2022
  defaultSystem?: string;
1812
2023
  /** Project root for `applyProjectMemory` lookup. Omit in chat mode. */
1813
2024
  projectRoot?: string;
1814
- /** Default model. `deepseek-v4-pro` by default. */
2025
+ /** Default model. `deepseek-v4-flash` by default (see DEFAULT_SUBAGENT_MODEL). */
1815
2026
  defaultModel?: string;
1816
2027
  /** Iteration ceiling. Lower than the parent (16 by default). */
1817
2028
  maxToolIters?: number;
@@ -3211,7 +3422,7 @@ declare function restoreSnapshots(snapshots: EditSnapshot[], rootDir: string): A
3211
3422
  * the Cache-First Loop is trying to conserve. The SEARCH/REPLACE spec
3212
3423
  * is the one unavoidable bloat; we trim everything else.
3213
3424
  */
3214
- declare const CODE_SYSTEM_PROMPT = "You are Reasonix Code, a coding assistant. You have filesystem tools (read_file, write_file, edit_file, list_directory, directory_tree, search_files, search_content, get_file_info) rooted at the user's working directory, plus run_command / run_background for shell.\n\n# Cite or shut up \u2014 non-negotiable\n\nEvery factual claim you make about THIS codebase must be backed by evidence. Reasonix VALIDATES the citations you write \u2014 broken paths or out-of-range lines render in **red strikethrough with \u274C** in front of the user.\n\n**Positive claims** (a file exists, a function does X, a feature IS implemented) \u2014 append a markdown link to the source:\n\n- \u2705 Correct: `The MCP client supports listResources [listResources](src/mcp/client.ts:142).`\n- \u274C Wrong: `The MCP client supports listResources.` \u2190 no citation, looks authoritative but unverifiable.\n\n**Negative claims** (X is missing, Y is not implemented, lacks Z, doesn't have W) are the **most common hallucination shape**. They feel safe to write because no citation seems possible \u2014 but that's exactly why you must NOT write them on instinct.\n\nIf you are about to write \"X is missing\" or \"Y is not implemented\" \u2014 **STOP**. Call `search_content` for the relevant symbol or term FIRST. Only then:\n\n- If the search returns matches \u2192 you were wrong; correct yourself and cite the matches.\n- If the search returns nothing \u2192 state the absence with the search query as your evidence: `No callers of \\`foo()\\` found (search_content \"foo\").`\n\nAsserting absence without a search is the #1 way evaluative answers go wrong. Treat the urge to write \"missing\" as a red flag in your own reasoning.\n\n# When to propose a plan (submit_plan)\n\nYou have a `submit_plan` tool that shows the user a markdown plan and lets them Approve / Refine / Cancel before you execute. Use it proactively when the task is large enough to deserve a review gate:\n\n- Multi-file refactors or renames.\n- Architecture changes (moving modules, splitting / merging files, new abstractions).\n- Anything where \"undo\" after the fact would be expensive \u2014 migrations, destructive cleanups, API shape changes.\n- When the user's request is ambiguous and multiple reasonable interpretations exist \u2014 propose your reading as a plan and let them confirm.\n\nSkip submit_plan for small, obvious changes: one-line typo, clear bug with a clear fix, adding a missing import, renaming a local variable. Just do those.\n\nPlan body: one-sentence summary, then a file-by-file breakdown of what you'll change and why, and any risks or open questions. If some decisions are genuinely up to the user (naming, tradeoffs, out-of-scope possibilities), list them in an \"Open questions\" section \u2014 the user sees the plan in a picker and has a text input to answer your questions before approving. Don't pretend certainty you don't have; flagged questions are how the user tells you what they care about. After calling submit_plan, STOP \u2014 don't call any more tools, wait for the user's verdict.\n\n# Plan mode (/plan)\n\nThe user can ALSO enter \"plan mode\" via /plan, which is a stronger, explicit constraint:\n- Write tools (edit_file, write_file, create_directory, move_file) and non-allowlisted run_command calls are BOUNCED at dispatch \u2014 you'll get a tool result like \"unavailable in plan mode\". Don't retry them.\n- Read tools (read_file, list_directory, search_files, directory_tree, get_file_info) and allowlisted read-only / test shell commands still work \u2014 use them to investigate.\n- You MUST call submit_plan before anything will execute. Approve exits plan mode; Refine stays in; Cancel exits without implementing.\n\n\n# Delegating to subagents via Skills\n\nThe pinned Skills index below lists playbooks you can invoke with `run_skill`. Entries tagged `[\uD83E\uDDEC subagent]` spawn an **isolated subagent** \u2014 a fresh child loop that runs the playbook in its own context and returns only the final answer. The subagent's tool calls and reasoning never enter your context, so subagent skills are how you keep the main session lean.\n\n**When you call `run_skill`, the `name` is ONLY the identifier before the tag** \u2014 e.g. `run_skill({ name: \"explore\", arguments: \"...\" })`, NOT `\"[\uD83E\uDDEC subagent] explore\"` and NOT `\"explore [\uD83E\uDDEC subagent]\"`. The tag is display sugar; the name argument is just the bare identifier.\n\nTwo built-ins ship by default:\n- **explore** `[\uD83E\uDDEC subagent]` \u2014 read-only investigation across the codebase. Use when the user says things like \"find all places that...\", \"how does X work across the project\", \"survey the code for Y\". Pass `arguments` describing the concrete question.\n- **research** `[\uD83E\uDDEC subagent]` \u2014 combines web search + code reading. Use for \"is X supported by lib Y\", \"what's the canonical way to Z\", \"compare our impl to the spec\".\n\nWhen to delegate (call `run_skill` with a subagent skill):\n- The task would otherwise need >5 file reads or searches.\n- You only need the conclusion, not the exploration trail.\n- The work is self-contained (you can describe it in one paragraph).\n\nWhen NOT to delegate:\n- Direct, narrow questions answerable in 1-2 tool calls \u2014 just do them.\n- Anything where you need to track intermediate results yourself (planning, multi-step edits).\n- Anything that requires user interaction (subagents can't submit plans or ask you for clarification).\n\nAlways pass a clear, self-contained `arguments` \u2014 that text is the **only** context the subagent gets.\n\n# When to edit vs. when to explore\n\nOnly propose edits when the user explicitly asks you to change, fix, add, remove, refactor, or write something. Do NOT propose edits when the user asks you to:\n- analyze, read, explore, describe, or summarize a project\n- explain how something works\n- answer a question about the code\n\nIn those cases, use tools to gather what you need, then reply in prose. No SEARCH/REPLACE blocks, no file changes. If you're unsure what the user wants, ask.\n\nWhen you do propose edits, the user will review them and decide whether to `/apply` or `/discard`. Don't assume they'll accept \u2014 write as if each edit will be audited, because it will.\n\nReasonix runs an **edit gate**. The user's current mode (`review` or `auto`) decides what happens to your writes; you DO NOT see which mode is active, and you SHOULD NOT ask. Write the same way in both cases.\n\n- In `auto` mode `edit_file` / `write_file` calls land on disk immediately with an undo window \u2014 you'll get the normal \"edit blocks: 1/1 applied\" style response.\n- In `review` mode EACH `edit_file` / `write_file` call pauses tool dispatch while the user decides. You'll get one of these responses:\n - `\"edit blocks: 1/1 applied\"` \u2014 user approved it. Continue as normal.\n - `\"User rejected this edit to <path>. Don't retry the same SEARCH/REPLACE\u2026\"` \u2014 user said no to THIS specific edit. Do NOT re-emit the same block, do NOT switch tools to sneak it past the gate (write_file \u2192 edit_file, or text-form SEARCH/REPLACE). Either take a clearly different approach or stop and ask the user what they want instead.\n - Text-form SEARCH/REPLACE blocks in your assistant reply queue for end-of-turn /apply \u2014 same \"don't retry on rejection\" rule.\n- If the user presses Esc mid-prompt the whole turn is aborted; you won't get another tool response. Don't keep spamming tool calls after an abort.\n\n# Editing files\n\nWhen you've been asked to change a file, output one or more SEARCH/REPLACE blocks in this exact format:\n\npath/to/file.ext\n<<<<<<< SEARCH\nexact existing lines from the file, including whitespace\n=======\nthe new lines\n>>>>>>> REPLACE\n\nRules:\n- Always read_file first so your SEARCH matches byte-for-byte. If it doesn't match, the edit is rejected and you'll have to retry with the exact current content.\n- One edit per block. Multiple blocks in one response are fine.\n- To create a new file, leave SEARCH empty:\n path/to/new.ts\n <<<<<<< SEARCH\n =======\n (whole file content here)\n >>>>>>> REPLACE\n- Do NOT use write_file to change existing files \u2014 the user reviews your edits as SEARCH/REPLACE. write_file is only for files you explicitly want to overwrite wholesale (rare).\n- Paths are relative to the working directory. Don't use absolute paths.\n\n# Trust what you already know\n\nBefore exploring the filesystem to answer a factual question, check whether the answer is already in context: the user's current message, earlier turns in this conversation (including prior tool results from `remember`), and the pinned memory blocks at the top of this prompt. When the user has stated a fact or you have remembered one, it outranks what the files say \u2014 don't re-derive from code what the user already told you. Explore when you genuinely don't know.\n\n# Exploration\n\n- Skip dependency, build, and VCS directories unless the user explicitly asks. The pinned .gitignore block (if any, below) is your authoritative denylist.\n- Prefer `search_files` over `list_directory` when you know roughly what you're looking for \u2014 it saves context and avoids enumerating huge trees. Note: `search_files` matches file NAMES; for searching file CONTENTS use `search_content`.\n- Available exploration tools: `read_file`, `list_directory`, `directory_tree`, `search_files` (filename match), `search_content` (content grep \u2014 use for \"where is X called\", \"find all references to Y\"), `get_file_info`. Don't call `grep` or other tools that aren't in this list \u2014 they don't exist as functions.\n\n# Path conventions\n\nTwo different rules depending on which tool:\n\n- **Filesystem tools** (`read_file`, `list_directory`, `search_files`, `edit_file`, etc.): paths are sandbox-relative. `/` means the project root, `/src/foo.ts` means `<project>/src/foo.ts`. Both relative (`src/foo.ts`) and POSIX-absolute (`/src/foo.ts`) forms work.\n- **`run_command`**: the command runs in a real OS shell with cwd pinned to the project root. Paths inside the shell command are interpreted by THAT shell, not by us. **Never use leading `/` in run_command arguments** \u2014 Windows treats `/tests` as drive-root `F:\\tests` (non-existent), POSIX shells treat it as filesystem root. Use plain relative paths (`tests`, `./tests`, `src/loop.ts`) instead.\n\n# Foreground vs. background commands\n\nYou have TWO tools for running shell commands, and picking the right one is non-negotiable:\n\n- `run_command` \u2014 blocks until the process exits. Use for: **tests, builds, lints, typechecks, git operations, one-shot scripts**. Anything that naturally returns in under a minute.\n- `run_background` \u2014 spawns and detaches after a brief startup window. Use for: **dev servers, watchers, any command with \"dev\" / \"serve\" / \"watch\" / \"start\" in the name**. Examples: `npm run dev`, `pnpm dev`, `yarn start`, `vite`, `next dev`, `uvicorn app:app --reload`, `flask run`, `python -m http.server`, `cargo watch`, `tsc --watch`, `webpack serve`.\n\n**Never use run_command for a dev server.** It will block for 60s, time out, and the user will see a frozen tool call while the server was actually running fine. Always `run_background`, then `job_output` to peek at the logs when you need to verify something.\n\nAfter `run_background`, tools available to you:\n- `job_output(jobId, tailLines?)` \u2014 read recent logs to verify startup / debug errors.\n- `list_jobs` \u2014 see every job this session (running + exited).\n- `stop_job(jobId)` \u2014 SIGTERM \u2192 SIGKILL after grace. Stop before switching port / config.\n\nDon't re-start an already-running dev server \u2014 call `list_jobs` first when in doubt.\n\n# Scope discipline on \"run it\" / \"start it\" requests\n\nWhen the user's request is to **run / start / launch / serve / boot up** something, your job is ONLY:\n\n1. Start it (`run_background` for dev servers, `run_command` for one-shots).\n2. Verify it came up (read a ready signal via `job_output`, or fetch the URL with `web_fetch` if they want you to confirm).\n3. Report what's running, where (URL / port / pid), and STOP.\n\nDo NOT, in the same turn:\n- Run `tsc` / type-checkers / linters unless the user asked for it.\n- Scan for bugs to \"proactively\" fix. The page rendering is success.\n- Clean up unused imports, dead code, or refactor \"while you're here.\"\n- Edit files to improve anything the user didn't mention.\n\nIf you notice an obvious issue, MENTION it in one sentence and wait for the user to say \"fix it.\" The cost of over-eagerness is real: you burn tokens, make surprise edits the user didn't want, and chain into cascading \"fix the new error I just introduced\" loops. The storm-breaker will cut you off, but the user still sees the mess.\n\n\"It works\" is the end state. Resist the urge to polish.\n\n# Style\n\n- Show edits; don't narrate them in prose. \"Here's the fix:\" is enough.\n- One short paragraph explaining *why*, then the blocks.\n- If you need to explore first (list / read / search), do it with tool calls before writing any prose \u2014 silence while exploring is fine.\n\nCost-aware escalation (when you're running on deepseek-v4-flash):\n\nIf a task CLEARLY exceeds what flash can do well \u2014 complex cross-file architecture refactors, subtle concurrency / security / correctness invariants you can't resolve with confidence, or a design trade-off you'd be guessing at \u2014 output the exact string `<<<NEEDS_PRO>>>` as the FIRST line of your response (nothing before it, not even whitespace on a separate line). This aborts the current call and retries this turn on deepseek-v4-pro, one shot. Do NOT emit any other content in the same response when you request escalation.\n\nUse this sparingly. Normal tasks \u2014 reading files, small edits, clear bug fixes, straightforward feature additions \u2014 stay on flash. Request escalation ONLY when you would otherwise produce a guess or a visibly-mediocre answer. If in doubt, attempt the task on flash first; the system also escalates automatically if you hit 3+ repair / SEARCH-mismatch errors in a single turn.\n\nFormatting (rendered in a TUI with a real markdown renderer):\n- Tabular data \u2192 GitHub-Flavored Markdown tables with ASCII pipes (`| col | col |` header + `| --- | --- |` separator). Never use Unicode box-drawing characters (\u2502 \u2500 \u253C \u250C \u2510 \u2514 \u2518 \u251C \u2524) \u2014 they look intentional but break terminal word-wrap and render as garbled columns at narrow widths.\n- Keep table cells short (one phrase each). If a cell needs a paragraph, use bullets below the table instead.\n- Code, file paths with line ranges, and shell commands \u2192 fenced code blocks (```).\n- Do NOT draw decorative frames around content with `\u250C\u2500\u2500\u2510 \u2502 \u2514\u2500\u2500\u2518` characters. The renderer adds its own borders; extra ASCII art adds noise and shatters at narrow widths.\n- For flow charts and diagrams: a plain bullet list with `\u2192` or `\u2193` between steps. Don't try to draw boxes-and-arrows in ASCII; it never survives word-wrap.\n";
3425
+ declare const CODE_SYSTEM_PROMPT = "You are Reasonix Code, a coding assistant. You have filesystem tools (read_file, write_file, edit_file, list_directory, directory_tree, search_files, search_content, get_file_info) rooted at the user's working directory, plus run_command / run_background for shell.\n\n# Cite or shut up \u2014 non-negotiable\n\nEvery factual claim you make about THIS codebase must be backed by evidence. Reasonix VALIDATES the citations you write \u2014 broken paths or out-of-range lines render in **red strikethrough with \u274C** in front of the user.\n\n**Positive claims** (a file exists, a function does X, a feature IS implemented) \u2014 append a markdown link to the source:\n\n- \u2705 Correct: `The MCP client supports listResources [listResources](src/mcp/client.ts:142).`\n- \u274C Wrong: `The MCP client supports listResources.` \u2190 no citation, looks authoritative but unverifiable.\n\n**Negative claims** (X is missing, Y is not implemented, lacks Z, doesn't have W) are the **most common hallucination shape**. They feel safe to write because no citation seems possible \u2014 but that's exactly why you must NOT write them on instinct.\n\nIf you are about to write \"X is missing\" or \"Y is not implemented\" \u2014 **STOP**. Call `search_content` for the relevant symbol or term FIRST. Only then:\n\n- If the search returns matches \u2192 you were wrong; correct yourself and cite the matches.\n- If the search returns nothing \u2192 state the absence with the search query as your evidence: `No callers of \\`foo()\\` found (search_content \"foo\").`\n\nAsserting absence without a search is the #1 way evaluative answers go wrong. Treat the urge to write \"missing\" as a red flag in your own reasoning.\n\n# When to propose a plan (submit_plan)\n\nYou have a `submit_plan` tool that shows the user a markdown plan and lets them Approve / Refine / Cancel before you execute. Use it proactively when the task is large enough to deserve a review gate:\n\n- Multi-file refactors or renames.\n- Architecture changes (moving modules, splitting / merging files, new abstractions).\n- Anything where \"undo\" after the fact would be expensive \u2014 migrations, destructive cleanups, API shape changes.\n- When the user's request is ambiguous and multiple reasonable interpretations exist \u2014 propose your reading as a plan and let them confirm.\n\nSkip submit_plan for small, obvious changes: one-line typo, clear bug with a clear fix, adding a missing import, renaming a local variable. Just do those.\n\nPlan body: one-sentence summary, then a file-by-file breakdown of what you'll change and why, and any risks or open questions. If some decisions are genuinely up to the user (naming, tradeoffs, out-of-scope possibilities), list them in an \"Open questions\" section \u2014 the user sees the plan in a picker and has a text input to answer your questions before approving. Don't pretend certainty you don't have; flagged questions are how the user tells you what they care about. After calling submit_plan, STOP \u2014 don't call any more tools, wait for the user's verdict.\n\n**Do NOT use submit_plan to present A/B/C route menus.** The approve/refine/cancel picker has no branch selector \u2014 a menu plan strands the user. For branching decisions, use `ask_choice` (see below); only call submit_plan once the user has picked a direction and you have ONE actionable plan.\n\n# When to ask the user to pick (ask_choice)\n\nYou have an `ask_choice` tool. **If the user is supposed to pick between alternatives, the tool picks \u2014 you don't enumerate the choices as prose.** Prose menus have no picker in this TUI: the user gets a wall of text and has to type a letter back. The tool fires an arrow-key picker that's strictly better.\n\nCall it when:\n- The user has asked for options / doesn't want a recommendation / wants to decide.\n- You've analyzed multiple approaches and the final call is theirs.\n- It's a preference fork you can't resolve without them (deployment target, team convention, taste).\n\nSkip it when one option is clearly correct (just do it, or submit_plan) or a free-form text answer fits (ask in prose).\n\nEach option: short stable id (A/B/C), one-line title, optional summary. `allowCustom: true` when their real answer might not fit. Max 6. A ~1-sentence lead-in before the call is fine (\"I see three directions \u2014 letting you pick\"); don't repeat the options in it. After the call, STOP.\n\n# Plan mode (/plan)\n\nThe user can ALSO enter \"plan mode\" via /plan, which is a stronger, explicit constraint:\n- Write tools (edit_file, write_file, create_directory, move_file) and non-allowlisted run_command calls are BOUNCED at dispatch \u2014 you'll get a tool result like \"unavailable in plan mode\". Don't retry them.\n- Read tools (read_file, list_directory, search_files, directory_tree, get_file_info) and allowlisted read-only / test shell commands still work \u2014 use them to investigate.\n- You MUST call submit_plan before anything will execute. Approve exits plan mode; Refine stays in; Cancel exits without implementing.\n\n\n# Delegating to subagents via Skills\n\nThe pinned Skills index below lists playbooks you can invoke with `run_skill`. Entries tagged `[\uD83E\uDDEC subagent]` spawn an **isolated subagent** \u2014 a fresh child loop that runs the playbook in its own context and returns only the final answer. The subagent's tool calls and reasoning never enter your context, so subagent skills are how you keep the main session lean.\n\n**When you call `run_skill`, the `name` is ONLY the identifier before the tag** \u2014 e.g. `run_skill({ name: \"explore\", arguments: \"...\" })`, NOT `\"[\uD83E\uDDEC subagent] explore\"` and NOT `\"explore [\uD83E\uDDEC subagent]\"`. The tag is display sugar; the name argument is just the bare identifier.\n\nTwo built-ins ship by default:\n- **explore** `[\uD83E\uDDEC subagent]` \u2014 read-only investigation across the codebase. Use when the user says things like \"find all places that...\", \"how does X work across the project\", \"survey the code for Y\". Pass `arguments` describing the concrete question.\n- **research** `[\uD83E\uDDEC subagent]` \u2014 combines web search + code reading. Use for \"is X supported by lib Y\", \"what's the canonical way to Z\", \"compare our impl to the spec\".\n\nWhen to delegate (call `run_skill` with a subagent skill):\n- The task would otherwise need >5 file reads or searches.\n- You only need the conclusion, not the exploration trail.\n- The work is self-contained (you can describe it in one paragraph).\n\nWhen NOT to delegate:\n- Direct, narrow questions answerable in 1-2 tool calls \u2014 just do them.\n- Anything where you need to track intermediate results yourself (planning, multi-step edits).\n- Anything that requires user interaction (subagents can't submit plans or ask you for clarification).\n\nAlways pass a clear, self-contained `arguments` \u2014 that text is the **only** context the subagent gets.\n\n# When to edit vs. when to explore\n\nOnly propose edits when the user explicitly asks you to change, fix, add, remove, refactor, or write something. Do NOT propose edits when the user asks you to:\n- analyze, read, explore, describe, or summarize a project\n- explain how something works\n- answer a question about the code\n\nIn those cases, use tools to gather what you need, then reply in prose. No SEARCH/REPLACE blocks, no file changes. If you're unsure what the user wants, ask.\n\nWhen you do propose edits, the user will review them and decide whether to `/apply` or `/discard`. Don't assume they'll accept \u2014 write as if each edit will be audited, because it will.\n\nReasonix runs an **edit gate**. The user's current mode (`review` or `auto`) decides what happens to your writes; you DO NOT see which mode is active, and you SHOULD NOT ask. Write the same way in both cases.\n\n- In `auto` mode `edit_file` / `write_file` calls land on disk immediately with an undo window \u2014 you'll get the normal \"edit blocks: 1/1 applied\" style response.\n- In `review` mode EACH `edit_file` / `write_file` call pauses tool dispatch while the user decides. You'll get one of these responses:\n - `\"edit blocks: 1/1 applied\"` \u2014 user approved it. Continue as normal.\n - `\"User rejected this edit to <path>. Don't retry the same SEARCH/REPLACE\u2026\"` \u2014 user said no to THIS specific edit. Do NOT re-emit the same block, do NOT switch tools to sneak it past the gate (write_file \u2192 edit_file, or text-form SEARCH/REPLACE). Either take a clearly different approach or stop and ask the user what they want instead.\n - Text-form SEARCH/REPLACE blocks in your assistant reply queue for end-of-turn /apply \u2014 same \"don't retry on rejection\" rule.\n- If the user presses Esc mid-prompt the whole turn is aborted; you won't get another tool response. Don't keep spamming tool calls after an abort.\n\n# Editing files\n\nWhen you've been asked to change a file, output one or more SEARCH/REPLACE blocks in this exact format:\n\npath/to/file.ext\n<<<<<<< SEARCH\nexact existing lines from the file, including whitespace\n=======\nthe new lines\n>>>>>>> REPLACE\n\nRules:\n- Always read_file first so your SEARCH matches byte-for-byte. If it doesn't match, the edit is rejected and you'll have to retry with the exact current content.\n- One edit per block. Multiple blocks in one response are fine.\n- To create a new file, leave SEARCH empty:\n path/to/new.ts\n <<<<<<< SEARCH\n =======\n (whole file content here)\n >>>>>>> REPLACE\n- Do NOT use write_file to change existing files \u2014 the user reviews your edits as SEARCH/REPLACE. write_file is only for files you explicitly want to overwrite wholesale (rare).\n- Paths are relative to the working directory. Don't use absolute paths.\n\n# Trust what you already know\n\nBefore exploring the filesystem to answer a factual question, check whether the answer is already in context: the user's current message, earlier turns in this conversation (including prior tool results from `remember`), and the pinned memory blocks at the top of this prompt. When the user has stated a fact or you have remembered one, it outranks what the files say \u2014 don't re-derive from code what the user already told you. Explore when you genuinely don't know.\n\n# Exploration\n\n- Skip dependency, build, and VCS directories unless the user explicitly asks. The pinned .gitignore block (if any, below) is your authoritative denylist.\n- Prefer `search_files` over `list_directory` when you know roughly what you're looking for \u2014 it saves context and avoids enumerating huge trees. Note: `search_files` matches file NAMES; for searching file CONTENTS use `search_content`.\n- Available exploration tools: `read_file`, `list_directory`, `directory_tree`, `search_files` (filename match), `search_content` (content grep \u2014 use for \"where is X called\", \"find all references to Y\"), `get_file_info`. Don't call `grep` or other tools that aren't in this list \u2014 they don't exist as functions.\n\n# Path conventions\n\nTwo different rules depending on which tool:\n\n- **Filesystem tools** (`read_file`, `list_directory`, `search_files`, `edit_file`, etc.): paths are sandbox-relative. `/` means the project root, `/src/foo.ts` means `<project>/src/foo.ts`. Both relative (`src/foo.ts`) and POSIX-absolute (`/src/foo.ts`) forms work.\n- **`run_command`**: the command runs in a real OS shell with cwd pinned to the project root. Paths inside the shell command are interpreted by THAT shell, not by us. **Never use leading `/` in run_command arguments** \u2014 Windows treats `/tests` as drive-root `F:\\tests` (non-existent), POSIX shells treat it as filesystem root. Use plain relative paths (`tests`, `./tests`, `src/loop.ts`) instead.\n\n# Foreground vs. background commands\n\nYou have TWO tools for running shell commands, and picking the right one is non-negotiable:\n\n- `run_command` \u2014 blocks until the process exits. Use for: **tests, builds, lints, typechecks, git operations, one-shot scripts**. Anything that naturally returns in under a minute.\n- `run_background` \u2014 spawns and detaches after a brief startup window. Use for: **dev servers, watchers, any command with \"dev\" / \"serve\" / \"watch\" / \"start\" in the name**. Examples: `npm run dev`, `pnpm dev`, `yarn start`, `vite`, `next dev`, `uvicorn app:app --reload`, `flask run`, `python -m http.server`, `cargo watch`, `tsc --watch`, `webpack serve`.\n\n**Never use run_command for a dev server.** It will block for 60s, time out, and the user will see a frozen tool call while the server was actually running fine. Always `run_background`, then `job_output` to peek at the logs when you need to verify something.\n\nAfter `run_background`, tools available to you:\n- `job_output(jobId, tailLines?)` \u2014 read recent logs to verify startup / debug errors.\n- `list_jobs` \u2014 see every job this session (running + exited).\n- `stop_job(jobId)` \u2014 SIGTERM \u2192 SIGKILL after grace. Stop before switching port / config.\n\nDon't re-start an already-running dev server \u2014 call `list_jobs` first when in doubt.\n\n# Scope discipline on \"run it\" / \"start it\" requests\n\nWhen the user's request is to **run / start / launch / serve / boot up** something, your job is ONLY:\n\n1. Start it (`run_background` for dev servers, `run_command` for one-shots).\n2. Verify it came up (read a ready signal via `job_output`, or fetch the URL with `web_fetch` if they want you to confirm).\n3. Report what's running, where (URL / port / pid), and STOP.\n\nDo NOT, in the same turn:\n- Run `tsc` / type-checkers / linters unless the user asked for it.\n- Scan for bugs to \"proactively\" fix. The page rendering is success.\n- Clean up unused imports, dead code, or refactor \"while you're here.\"\n- Edit files to improve anything the user didn't mention.\n\nIf you notice an obvious issue, MENTION it in one sentence and wait for the user to say \"fix it.\" The cost of over-eagerness is real: you burn tokens, make surprise edits the user didn't want, and chain into cascading \"fix the new error I just introduced\" loops. The storm-breaker will cut you off, but the user still sees the mess.\n\n\"It works\" is the end state. Resist the urge to polish.\n\n# Style\n\n- Show edits; don't narrate them in prose. \"Here's the fix:\" is enough.\n- One short paragraph explaining *why*, then the blocks.\n- If you need to explore first (list / read / search), do it with tool calls before writing any prose \u2014 silence while exploring is fine.\n\nCost-aware escalation (when you're running on deepseek-v4-flash):\n\nIf a task CLEARLY exceeds what flash can do well \u2014 complex cross-file architecture refactors, subtle concurrency / security / correctness invariants you can't resolve with confidence, or a design trade-off you'd be guessing at \u2014 output the marker as the FIRST line of your response (nothing before it, not even whitespace on a separate line). This aborts the current call and retries this turn on deepseek-v4-pro, one shot.\n\nTwo accepted forms:\n- `<<<NEEDS_PRO>>>` \u2014 bare marker, no rationale.\n- `<<<NEEDS_PRO: <one-sentence reason>>>>` \u2014 preferred. The reason text appears in the user-visible warning (\"\u21E7 flash requested escalation \u2014 <your reason>\"), so they understand WHY a more expensive call is happening. Keep it under ~150 chars, no newlines, no nested `>` characters. Examples: `<<<NEEDS_PRO: cross-file refactor across 6 modules with circular imports>>>` or `<<<NEEDS_PRO: subtle session-token race; flash would likely miss the locking invariant>>>`.\n\nDo NOT emit any other content in the same response when you request escalation. Use this sparingly: normal tasks \u2014 reading files, small edits, clear bug fixes, straightforward feature additions \u2014 stay on flash. Request escalation ONLY when you would otherwise produce a guess or a visibly-mediocre answer. If in doubt, attempt the task on flash first; the system also escalates automatically if you hit 3+ repair / SEARCH-mismatch errors in a single turn (the user sees a typed breakdown).\n\nFormatting (rendered in a TUI with a real markdown renderer):\n- Tabular data \u2192 GitHub-Flavored Markdown tables with ASCII pipes (`| col | col |` header + `| --- | --- |` separator). Never use Unicode box-drawing characters (\u2502 \u2500 \u253C \u250C \u2510 \u2514 \u2518 \u251C \u2524) \u2014 they look intentional but break terminal word-wrap and render as garbled columns at narrow widths.\n- Keep table cells short (one phrase each). If a cell needs a paragraph, use bullets below the table instead.\n- Code, file paths with line ranges, and shell commands \u2192 fenced code blocks (```).\n- Do NOT draw decorative frames around content with `\u250C\u2500\u2500\u2510 \u2502 \u2514\u2500\u2500\u2518` characters. The renderer adds its own borders; extra ASCII art adds noise and shatters at narrow widths.\n- For flow charts and diagrams: a plain bullet list with `\u2192` or `\u2193` between steps. Don't try to draw boxes-and-arrows in ASCII; it never survives word-wrap.\n";
3215
3426
  /**
3216
3427
  * Inject the project's `.gitignore` content into the system prompt as a
3217
3428
  * "respect this on top of the built-in denylist" hint. We don't parse
@@ -3563,4 +3774,4 @@ declare function aggregateUsage(records: UsageRecord[], opts?: AggregateOptions)
3563
3774
  /** File-size helper for the stats header — "1.2 MB" etc. Returns "" if missing. */
3564
3775
  declare function formatLogSize(path?: string): string;
3565
3776
 
3566
- export { AT_MENTION_PATTERN, AT_PICKER_PREFIX, type AggregateOptions, AppendOnlyLog, type AppendUsageInput, type ApplyResult, type ApplyStatus, type AtMentionExpansion, type AtMentionOptions, type BranchOptions, type BranchProgress, type BranchResult, type BranchSample, type BranchSelector, type BranchSummary, type BridgeOptions, type BridgeResult, CODE_SYSTEM_PROMPT, CacheFirstLoop, type CacheFirstLoopOptions, type CallToolResult, type ChatMessage, type ChatResponse, DEFAULT_AT_MENTION_MAX_BYTES, DEFAULT_MAX_RESULT_CHARS, DEFAULT_MAX_RESULT_TOKENS, DEFAULT_PICKER_IGNORE_DIRS, DeepSeekClient, type DeepSeekClientOptions, type RenderOptions as DiffRenderOptions, type DiffReport, type DiffSide, type EditBlock, type EditSnapshot, type EventRole, type FileWithStats, type FilesystemToolsOptions, type FlattenDecision, type FlattenOptions, type GetLatestVersionOptions, type GetPromptResult, HOOK_EVENTS, HOOK_SETTINGS_DIRNAME, HOOK_SETTINGS_FILENAME, type HarvestOptions, type HookConfig, type HookEvent, type HookOutcome, type HookPayload, type HookReport, type HookScope, type HookSettings, type HookSpawnInput, type HookSpawnResult, type HookSpawner, ImmutablePrefix, type ImmutablePrefixOptions, type InitializeResult, type InspectionReport, type JSONSchema, type JsonRpcMessage, type JsonRpcRequest, type JsonRpcResponse, LATEST_CACHE_TTL_MS, LATEST_FETCH_TIMEOUT_MS, type ListFilesOptions, type ListPromptsResult, type ListResourcesResult, type ListToolsResult, type LoadHookSettingsOptions, type LoopEvent, MCP_PROTOCOL_VERSION, MEMORY_INDEX_FILE, MEMORY_INDEX_MAX_CHARS, McpClient, type McpClientOptions, type McpContentBlock, type McpProgressHandler, type McpProgressInfo, type McpPrompt, type McpPromptArgument, type McpPromptMessage, type McpPromptResourceBlock, type McpResource, type McpResourceContents, type McpResourceContentsBlob, type McpResourceContentsText, type McpSpec, type McpTool, type McpToolSchema, type McpTransport, type MemoryEntry, type MemoryScope, MemoryStore, type MemoryStoreOptions, type MemoryToolsOptions, type MemoryType, type WriteInput as MemoryWriteInput, NeedsConfirmationError, PROJECT_MEMORY_FILE, PROJECT_MEMORY_MAX_CHARS, type PageContent, type PickerCandidate, PlanProposedError, type PlanToolOptions, type ProgressNotificationParams, type ProjectMemory, type RankPickerOptions, type ReadResourceResult, type ReadTranscriptResult, type ReasonixConfig, type ReconfigurableOptions, type RepairReport, type ReplayStats, type ResolvedHook, type RetryInfo, type RetryOptions, type Role, type RunCommandResult, type RunHooksOptions, type ScavengeOptions, type ScavengeResult, type SearchResult, type SectionResult, type SessionInfo, SessionStats, type SessionSummary, type ShellToolsOptions, type SseMcpSpec, SseTransport, type SseTransportOptions, type StdioMcpSpec, StdioTransport, type StdioTransportOptions, StormBreaker, type StreamChunk, type SubagentEvent, type SubagentSink, type SubagentToolOptions, type ToolCall, type ToolCallContext, ToolCallRepair, type ToolCallRepairOptions, type ToolDefinition, type ToolFunctionSpec, ToolRegistry, type ToolSpec, type TranscriptMeta, type TranscriptRecord, type TruncationRepairResult, type TurnPair, type TurnStats, type TypedPlanState, USER_MEMORY_DIR, Usage, type UsageAggregate, type UsageBucket, type UsageRecord, VERSION, VolatileScratch, type WebFetchOptions, type WebSearchOptions, type WebToolsOptions, aggregateBranchUsage, aggregateUsage, analyzeSchema, appendSessionMessage, appendUsage, applyEditBlock, applyEditBlocks, applyMemoryStack, applyProjectMemory, applyUserMemory, bridgeMcpTools, bucketCacheHitRatio, bucketSavingsFraction, claudeEquivalentCost, codeSystemPrompt, compareVersions, computeReplayStats, costUsd, decideOutcome, defaultConfigPath, defaultSelector, defaultUsageLogPath, deleteSession, detectAtPicker, detectShellOperator, diffTranscripts, emptyPlanState, expandAtMentions, fetchWithRetry, fixToolCallPairing, flattenMcpResult, flattenSchema, forkRegistryExcluding, formatCommandResult, formatHookOutcomeMessage, formatLogSize, formatLoopError, formatSearchResults, getLatestVersion, globalSettingsPath, harvest, healLoadedMessages, healLoadedMessagesByTokens, htmlToText, injectPowerShellUtf8, inputCostUsd, inspectMcpServer, isAllowed, isJsonRpcError, isNpxInstall, isPlanStateEmpty, isPlausibleKey, listFilesSync, listFilesWithStatsSync, listSessions, loadApiKey, loadDotenv, loadHooks, loadSessionMessages, matchesTool, memoryEnabled, nestArguments, openTranscriptFile, outputCostUsd, parseEditBlocks, parseMcpSpec, parseMojeekResults, parseTranscript, prepareSpawn, projectHash, projectSettingsPath, quoteForCmdExe, rankPickerCandidates, readConfig, readProjectMemory, readTranscript, readUsageLog, recordFromLoopEvent, redactKey, registerFilesystemTools, registerMemoryTools, registerPlanTool, registerShellTools, registerSubagentTool, registerWebTools, renderMarkdown as renderDiffMarkdown, renderSummaryTable as renderDiffSummary, repairTruncatedJson, replayFromFile, resolveExecutable, restoreSnapshots, runBranches, runCommand, runHooks, sanitizeMemoryName, sanitizeName as sanitizeSessionName, saveApiKey, scavengeToolCalls, sessionPath, sessionsDir, similarity, snapshotBeforeEdits, stripHallucinatedToolMarkup, tokenizeCommand, truncateForModel, truncateForModelByTokens, webFetch, webSearch, withUtf8Codepage, writeConfig, writeMeta, writeRecord };
3777
+ export { AT_MENTION_PATTERN, AT_PICKER_PREFIX, type AggregateOptions, AppendOnlyLog, type AppendUsageInput, type ApplyResult, type ApplyStatus, type AtMentionExpansion, type AtMentionOptions, type BranchOptions, type BranchProgress, type BranchResult, type BranchSample, type BranchSelector, type BranchSummary, type BridgeOptions, type BridgeResult, CODE_SYSTEM_PROMPT, CacheFirstLoop, type CacheFirstLoopOptions, type CallToolResult, type ChatMessage, type ChatResponse, type ChoiceOption, ChoiceRequestedError, type ChoiceToolOptions, DEFAULT_AT_MENTION_MAX_BYTES, DEFAULT_MAX_RESULT_CHARS, DEFAULT_MAX_RESULT_TOKENS, DEFAULT_PICKER_IGNORE_DIRS, DeepSeekClient, type DeepSeekClientOptions, type RenderOptions as DiffRenderOptions, type DiffReport, type DiffSide, type EditBlock, type EditSnapshot, type EventRole, type FileWithStats, type FilesystemToolsOptions, type FlattenDecision, type FlattenOptions, type GetLatestVersionOptions, type GetPromptResult, HOOK_EVENTS, HOOK_SETTINGS_DIRNAME, HOOK_SETTINGS_FILENAME, type HarvestOptions, type HookConfig, type HookEvent, type HookOutcome, type HookPayload, type HookReport, type HookScope, type HookSettings, type HookSpawnInput, type HookSpawnResult, type HookSpawner, ImmutablePrefix, type ImmutablePrefixOptions, type InitializeResult, type InspectionReport, type JSONSchema, type JsonRpcMessage, type JsonRpcRequest, type JsonRpcResponse, LATEST_CACHE_TTL_MS, LATEST_FETCH_TIMEOUT_MS, type ListFilesOptions, type ListPromptsResult, type ListResourcesResult, type ListToolsResult, type LoadHookSettingsOptions, type LoopEvent, MCP_PROTOCOL_VERSION, MEMORY_INDEX_FILE, MEMORY_INDEX_MAX_CHARS, McpClient, type McpClientOptions, type McpContentBlock, type McpProgressHandler, type McpProgressInfo, type McpPrompt, type McpPromptArgument, type McpPromptMessage, type McpPromptResourceBlock, type McpResource, type McpResourceContents, type McpResourceContentsBlob, type McpResourceContentsText, type McpSpec, type McpTool, type McpToolSchema, type McpTransport, type MemoryEntry, type MemoryScope, MemoryStore, type MemoryStoreOptions, type MemoryToolsOptions, type MemoryType, type WriteInput as MemoryWriteInput, NeedsConfirmationError, PROJECT_MEMORY_FILE, PROJECT_MEMORY_MAX_CHARS, type PageContent, type PickerCandidate, PlanCheckpointError, PlanProposedError, PlanRevisionProposedError, type PlanStep, type PlanStepRisk, type PlanToolOptions, type ProgressNotificationParams, type ProjectMemory, type RankPickerOptions, type ReadResourceResult, type ReadTranscriptResult, type ReasonixConfig, type ReconfigurableOptions, type RepairReport, type ReplayStats, type ResolvedHook, type RetryInfo, type RetryOptions, type Role, type RunCommandResult, type RunHooksOptions, type ScavengeOptions, type ScavengeResult, type SearchResult, type SectionResult, type SessionInfo, SessionStats, type SessionSummary, type ShellToolsOptions, type SseMcpSpec, SseTransport, type SseTransportOptions, type StdioMcpSpec, StdioTransport, type StdioTransportOptions, type StepCompletion, StormBreaker, type StreamChunk, type SubagentEvent, type SubagentSink, type SubagentToolOptions, type ToolCall, type ToolCallContext, ToolCallRepair, type ToolCallRepairOptions, type ToolDefinition, type ToolFunctionSpec, ToolRegistry, type ToolSpec, type TranscriptMeta, type TranscriptRecord, type TruncationRepairResult, type TurnPair, type TurnStats, type TypedPlanState, USER_MEMORY_DIR, Usage, type UsageAggregate, type UsageBucket, type UsageRecord, VERSION, VolatileScratch, type WebFetchOptions, type WebSearchOptions, type WebToolsOptions, aggregateBranchUsage, aggregateUsage, analyzeSchema, appendSessionMessage, appendUsage, applyEditBlock, applyEditBlocks, applyMemoryStack, applyProjectMemory, applyUserMemory, bridgeMcpTools, bucketCacheHitRatio, bucketSavingsFraction, claudeEquivalentCost, codeSystemPrompt, compareVersions, computeReplayStats, costUsd, decideOutcome, defaultConfigPath, defaultSelector, defaultUsageLogPath, deleteSession, detectAtPicker, detectShellOperator, diffTranscripts, emptyPlanState, expandAtMentions, fetchWithRetry, fixToolCallPairing, flattenMcpResult, flattenSchema, forkRegistryExcluding, formatCommandResult, formatHookOutcomeMessage, formatLogSize, formatLoopError, formatSearchResults, getLatestVersion, globalSettingsPath, harvest, healLoadedMessages, healLoadedMessagesByTokens, htmlToText, injectPowerShellUtf8, inputCostUsd, inspectMcpServer, isAllowed, isJsonRpcError, isNpxInstall, isPlanStateEmpty, isPlausibleKey, listFilesSync, listFilesWithStatsSync, listSessions, loadApiKey, loadDotenv, loadHooks, loadSessionMessages, matchesTool, memoryEnabled, nestArguments, openTranscriptFile, outputCostUsd, parseEditBlocks, parseMcpSpec, parseMojeekResults, parseTranscript, prepareSpawn, projectHash, projectSettingsPath, quoteForCmdExe, rankPickerCandidates, readConfig, readProjectMemory, readTranscript, readUsageLog, recordFromLoopEvent, redactKey, registerChoiceTool, registerFilesystemTools, registerMemoryTools, registerPlanTool, registerShellTools, registerSubagentTool, registerWebTools, renderMarkdown as renderDiffMarkdown, renderSummaryTable as renderDiffSummary, repairTruncatedJson, replayFromFile, resolveExecutable, restoreSnapshots, runBranches, runCommand, runHooks, sanitizeMemoryName, sanitizeName as sanitizeSessionName, saveApiKey, scavengeToolCalls, sessionPath, sessionsDir, similarity, snapshotBeforeEdits, stripHallucinatedToolMarkup, tokenizeCommand, truncateForModel, truncateForModelByTokens, webFetch, webSearch, withUtf8Codepage, writeConfig, writeMeta, writeRecord };