reasonix 0.23.0 → 0.24.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.
Files changed (36) hide show
  1. package/dashboard/dist/app.js +1723 -574
  2. package/dashboard/dist/app.js.map +1 -1
  3. package/dist/cli/.-3G6VX5S7.js +327 -0
  4. package/dist/cli/.-6YRPB2C7.js +329 -0
  5. package/dist/cli/.-6YRPB2C7.js.map +1 -0
  6. package/dist/cli/.-EYSVINK3.js +317 -0
  7. package/dist/cli/.-EYSVINK3.js.map +1 -0
  8. package/dist/cli/banner-demo-QKOPDSTL.js +77 -0
  9. package/dist/cli/banner-demo-QKOPDSTL.js.map +1 -0
  10. package/dist/cli/card-demo-5TVXJISK.js +944 -0
  11. package/dist/cli/card-demo-5TVXJISK.js.map +1 -0
  12. package/dist/cli/chunk-2H7UOFLK.js +11 -0
  13. package/dist/cli/chunk-2H7UOFLK.js.map +1 -0
  14. package/dist/cli/chunk-BGTXZKNY.js +197 -0
  15. package/dist/cli/chunk-BGTXZKNY.js.map +1 -0
  16. package/dist/cli/chunk-JHXQDL7B.js +2056 -0
  17. package/dist/cli/chunk-JHXQDL7B.js.map +1 -0
  18. package/dist/cli/flicker-demo-MOB6GAW4.js +165 -0
  19. package/dist/cli/flicker-demo-MOB6GAW4.js.map +1 -0
  20. package/dist/cli/index.js +3818 -2021
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/cli/preview-4FOEEE5G.js +224 -0
  23. package/dist/cli/preview-4FOEEE5G.js.map +1 -0
  24. package/dist/cli/{prompt-YUL7CYKY.js → prompt-VZQ2CPID.js} +2 -1
  25. package/dist/cli/prompt-VZQ2CPID.js.map +1 -0
  26. package/dist/cli/renderer-demo-2BIGEV2T.js +95 -0
  27. package/dist/cli/renderer-demo-2BIGEV2T.js.map +1 -0
  28. package/dist/cli/select-demo-OA5N34BJ.js +107 -0
  29. package/dist/cli/select-demo-OA5N34BJ.js.map +1 -0
  30. package/dist/cli/stress-demo-I7XRPQMM.js +211 -0
  31. package/dist/cli/stress-demo-I7XRPQMM.js.map +1 -0
  32. package/dist/index.d.ts +6 -2
  33. package/dist/index.js +79 -15
  34. package/dist/index.js.map +1 -1
  35. package/package.json +6 -4
  36. /package/dist/cli/{prompt-YUL7CYKY.js.map → .-3G6VX5S7.js.map} +0 -0
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ CharPool,
4
+ HyperlinkPool,
5
+ StylePool,
6
+ ink_compat_exports,
7
+ mount,
8
+ useKeystroke
9
+ } from "./chunk-JHXQDL7B.js";
10
+ import "./chunk-2H7UOFLK.js";
11
+
12
+ // src/cli/commands/stress-demo.tsx
13
+ import React, { useEffect, useRef, useState } from "react";
14
+ var BRAND = "#79c0ff";
15
+ var FAINT = "#6e7681";
16
+ var META = "#8b949e";
17
+ var ACCENT = "#d2a8ff";
18
+ var OK = "#7ee787";
19
+ var PEND = "#484f58";
20
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
21
+ var SHELL_LINES = [
22
+ " PASS src/loop.test.ts",
23
+ " PASS src/parser.test.ts",
24
+ " PASS src/cli/index.test.ts",
25
+ " PASS src/cli/commands/chat.test.ts",
26
+ " PASS src/diff/cell.test.ts",
27
+ " PASS src/diff/screen.test.ts",
28
+ " PASS src/renderer/layout.test.ts",
29
+ " PASS src/renderer/diff.test.ts",
30
+ " PASS src/renderer/serialize.test.ts",
31
+ "",
32
+ "Test Suites: 9 passed, 9 total",
33
+ "Tests: 142 passed, 142 total"
34
+ ];
35
+ var RESPONSE = [
36
+ "Working through the failing test on src/loop.test.ts.",
37
+ "The assertion on line 42 expects the parser to drop the trailing tool-call",
38
+ "marker, but the new tokenizer keeps it. Two paths forward \u2014 patch the",
39
+ "tokenizer's strip step, or update the expectation."
40
+ ].join(" ");
41
+ var PLAN_STEPS = [
42
+ { label: "identify the failing test", status: "done" },
43
+ { label: "wire the regression check", status: "running" },
44
+ { label: "rebuild dist", status: "pending" },
45
+ { label: "publish patch", status: "pending" }
46
+ ];
47
+ function StatusRow({ elapsedMs }) {
48
+ const seconds = (elapsedMs / 1e3).toFixed(1);
49
+ const cost = elapsedMs / 1e3 * 8e-4;
50
+ return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 2 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: BRAND, bold: true }, "\u25C8 Reasonix"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: META }, "working"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, `${seconds}s elapsed`), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, `$${cost.toFixed(4)}`));
51
+ }
52
+ function PlanCard({ frame }) {
53
+ const spin = SPINNER_FRAMES[frame % SPINNER_FRAMES.length] ?? "\xB7";
54
+ return /* @__PURE__ */ React.createElement(
55
+ ink_compat_exports.Box,
56
+ {
57
+ flexDirection: "column",
58
+ borderStyle: "round",
59
+ borderColor: ACCENT,
60
+ paddingX: 1,
61
+ marginTop: 1
62
+ },
63
+ /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: ACCENT, bold: true }, "\u229E Plan"),
64
+ PLAN_STEPS.map((step) => {
65
+ const { glyph, color } = decoratePlan(step.status, spin);
66
+ return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { key: step.label, flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color }, glyph), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { dimColor: step.status === "pending" }, step.label));
67
+ })
68
+ );
69
+ }
70
+ function decoratePlan(status, spin) {
71
+ switch (status) {
72
+ case "done":
73
+ return { glyph: "\u2713", color: OK };
74
+ case "running":
75
+ return { glyph: spin, color: BRAND };
76
+ case "pending":
77
+ return { glyph: "\u25CB", color: PEND };
78
+ }
79
+ }
80
+ var SHELL_WINDOW = 5;
81
+ function ShellCard({ lines, frame }) {
82
+ const total = Math.min(lines, SHELL_LINES.length);
83
+ const startIdx = Math.max(0, total - SHELL_WINDOW);
84
+ const visible = SHELL_LINES.slice(startIdx, total);
85
+ const hidden = startIdx;
86
+ const running = lines < SHELL_LINES.length;
87
+ const spin = SPINNER_FRAMES[frame % SPINNER_FRAMES.length] ?? "\xB7";
88
+ return /* @__PURE__ */ React.createElement(
89
+ ink_compat_exports.Box,
90
+ {
91
+ flexDirection: "column",
92
+ borderStyle: "round",
93
+ borderColor: BRAND,
94
+ paddingX: 1,
95
+ marginTop: 1
96
+ },
97
+ /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: BRAND }, running ? spin : "\u2713"), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: BRAND, bold: true }, "npm test"), hidden > 0 ? /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: FAINT }, `(+${hidden} earlier)`) : null),
98
+ visible.map((line, i) => (
99
+ // biome-ignore lint/suspicious/noArrayIndexKey: shell output lines are positional + append-only
100
+ /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { key: `shell-${startIdx + i}`, dimColor: line.startsWith(" PASS") }, line || " ")
101
+ ))
102
+ );
103
+ }
104
+ function ResponseCard({
105
+ revealed,
106
+ frame
107
+ }) {
108
+ const text = RESPONSE.slice(0, revealed);
109
+ const done = revealed >= RESPONSE.length;
110
+ const glyph = done ? "\u2039" : SPINNER_FRAMES[frame % SPINNER_FRAMES.length] ?? "\xB7";
111
+ return /* @__PURE__ */ React.createElement(
112
+ ink_compat_exports.Box,
113
+ {
114
+ flexDirection: "column",
115
+ borderStyle: "round",
116
+ borderColor: OK,
117
+ paddingX: 1,
118
+ marginTop: 1
119
+ },
120
+ /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { color: done ? OK : BRAND }, glyph), /* @__PURE__ */ React.createElement(ink_compat_exports.Text, null, text || "thinking\u2026"))
121
+ );
122
+ }
123
+ function StressShell({ onExit }) {
124
+ const [elapsed, setElapsed] = useState(0);
125
+ const [planFrame, setPlanFrame] = useState(0);
126
+ const [shellLines, setShellLines] = useState(0);
127
+ const [shellFrame, setShellFrame] = useState(0);
128
+ const [revealed, setRevealed] = useState(0);
129
+ const [responseFrame, setResponseFrame] = useState(0);
130
+ const startedRef = useRef(Date.now());
131
+ useKeystroke((k) => {
132
+ if (k.escape) onExit();
133
+ });
134
+ useEffect(() => {
135
+ const id = setInterval(() => {
136
+ setElapsed(Date.now() - startedRef.current);
137
+ }, 100);
138
+ return () => clearInterval(id);
139
+ }, []);
140
+ useEffect(() => {
141
+ const id = setInterval(() => {
142
+ setPlanFrame((f) => f + 1);
143
+ }, 80);
144
+ return () => clearInterval(id);
145
+ }, []);
146
+ useEffect(() => {
147
+ const id = setInterval(() => {
148
+ setShellFrame((f) => f + 1);
149
+ }, 80);
150
+ return () => clearInterval(id);
151
+ }, []);
152
+ useEffect(() => {
153
+ const id = setInterval(() => {
154
+ setShellLines((n) => Math.min(SHELL_LINES.length, n + 1));
155
+ }, 250);
156
+ return () => clearInterval(id);
157
+ }, []);
158
+ useEffect(() => {
159
+ const id = setInterval(() => {
160
+ setResponseFrame((f) => f + 1);
161
+ }, 80);
162
+ return () => clearInterval(id);
163
+ }, []);
164
+ useEffect(() => {
165
+ const id = setInterval(() => {
166
+ setRevealed((r) => Math.min(RESPONSE.length, r + 4));
167
+ }, 33);
168
+ return () => clearInterval(id);
169
+ }, []);
170
+ return /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(StatusRow, { elapsedMs: elapsed }), /* @__PURE__ */ React.createElement(PlanCard, { frame: planFrame }), /* @__PURE__ */ React.createElement(ShellCard, { lines: shellLines, frame: shellFrame }), /* @__PURE__ */ React.createElement(ResponseCard, { revealed, frame: responseFrame }), /* @__PURE__ */ React.createElement(ink_compat_exports.Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(ink_compat_exports.Text, { dimColor: true }, "stress mode \xB7 4 concurrent live regions \xB7 Esc exit")));
171
+ }
172
+ async function runStressDemo(opts = {}) {
173
+ const stdout = opts.stdout ?? process.stdout;
174
+ const stdin = opts.stdin ?? process.stdin;
175
+ if (!stdin.isTTY || !stdout.isTTY) {
176
+ console.error("stress-demo requires an interactive TTY.");
177
+ process.exit(1);
178
+ }
179
+ const pools = {
180
+ char: new CharPool(),
181
+ style: new StylePool(),
182
+ hyperlink: new HyperlinkPool()
183
+ };
184
+ let resolveExit = () => {
185
+ };
186
+ const exited = new Promise((resolve) => {
187
+ resolveExit = resolve;
188
+ });
189
+ const handle = mount(/* @__PURE__ */ React.createElement(StressShell, { onExit: () => resolveExit() }), {
190
+ viewportWidth: stdout.columns ?? 80,
191
+ viewportHeight: stdout.rows ?? 30,
192
+ pools,
193
+ write: (bytes) => stdout.write(bytes),
194
+ stdin,
195
+ onExit: () => resolveExit()
196
+ });
197
+ const onResize = () => handle.resize(stdout.columns ?? 80, stdout.rows ?? 30);
198
+ stdout.on("resize", onResize);
199
+ try {
200
+ await exited;
201
+ } finally {
202
+ stdout.off("resize", onResize);
203
+ handle.destroy();
204
+ stdin.pause();
205
+ }
206
+ }
207
+ export {
208
+ StressShell,
209
+ runStressDemo
210
+ };
211
+ //# sourceMappingURL=stress-demo-I7XRPQMM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/commands/stress-demo.tsx"],"sourcesContent":["// biome-ignore lint/style/useImportType: tsconfig jsx=react needs React in value scope for JSX compilation\nimport React, { useEffect, useRef, useState } from \"react\";\nimport {\n CharPool,\n type Handle,\n HyperlinkPool,\n StylePool,\n inkCompat,\n mount,\n useKeystroke,\n} from \"../../renderer/index.js\";\n\nconst BRAND = \"#79c0ff\";\nconst FAINT = \"#6e7681\";\nconst META = \"#8b949e\";\nconst ACCENT = \"#d2a8ff\";\nconst OK = \"#7ee787\";\nconst PEND = \"#484f58\";\n\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"] as const;\nconst SHELL_LINES = [\n \" PASS src/loop.test.ts\",\n \" PASS src/parser.test.ts\",\n \" PASS src/cli/index.test.ts\",\n \" PASS src/cli/commands/chat.test.ts\",\n \" PASS src/diff/cell.test.ts\",\n \" PASS src/diff/screen.test.ts\",\n \" PASS src/renderer/layout.test.ts\",\n \" PASS src/renderer/diff.test.ts\",\n \" PASS src/renderer/serialize.test.ts\",\n \"\",\n \"Test Suites: 9 passed, 9 total\",\n \"Tests: 142 passed, 142 total\",\n] as const;\nconst RESPONSE = [\n \"Working through the failing test on src/loop.test.ts.\",\n \"The assertion on line 42 expects the parser to drop the trailing tool-call\",\n \"marker, but the new tokenizer keeps it. Two paths forward — patch the\",\n \"tokenizer's strip step, or update the expectation.\",\n].join(\" \");\n\ninterface PlanStep {\n readonly label: string;\n readonly status: \"done\" | \"running\" | \"pending\";\n}\n\nconst PLAN_STEPS: ReadonlyArray<PlanStep> = [\n { label: \"identify the failing test\", status: \"done\" },\n { label: \"wire the regression check\", status: \"running\" },\n { label: \"rebuild dist\", status: \"pending\" },\n { label: \"publish patch\", status: \"pending\" },\n];\n\nfunction StatusRow({ elapsedMs }: { elapsedMs: number }): React.ReactElement {\n const seconds = (elapsedMs / 1000).toFixed(1);\n const cost = (elapsedMs / 1000) * 0.0008;\n return (\n <inkCompat.Box flexDirection=\"row\" gap={2}>\n <inkCompat.Text color={BRAND} bold>\n ◈ Reasonix\n </inkCompat.Text>\n <inkCompat.Text color={META}>working</inkCompat.Text>\n <inkCompat.Text color={FAINT}>{`${seconds}s elapsed`}</inkCompat.Text>\n <inkCompat.Text color={FAINT}>{`$${cost.toFixed(4)}`}</inkCompat.Text>\n </inkCompat.Box>\n );\n}\n\nfunction PlanCard({ frame }: { frame: number }): React.ReactElement {\n const spin = SPINNER_FRAMES[frame % SPINNER_FRAMES.length] ?? \"·\";\n return (\n <inkCompat.Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor={ACCENT}\n paddingX={1}\n marginTop={1}\n >\n <inkCompat.Text color={ACCENT} bold>\n ⊞ Plan\n </inkCompat.Text>\n {PLAN_STEPS.map((step) => {\n const { glyph, color } = decoratePlan(step.status, spin);\n return (\n <inkCompat.Box key={step.label} flexDirection=\"row\" gap={1}>\n <inkCompat.Text color={color}>{glyph}</inkCompat.Text>\n <inkCompat.Text dimColor={step.status === \"pending\"}>{step.label}</inkCompat.Text>\n </inkCompat.Box>\n );\n })}\n </inkCompat.Box>\n );\n}\n\nfunction decoratePlan(status: PlanStep[\"status\"], spin: string): { glyph: string; color: string } {\n switch (status) {\n case \"done\":\n return { glyph: \"✓\", color: OK };\n case \"running\":\n return { glyph: spin, color: BRAND };\n case \"pending\":\n return { glyph: \"○\", color: PEND };\n }\n}\n\nconst SHELL_WINDOW = 5;\n\nfunction ShellCard({ lines, frame }: { lines: number; frame: number }): React.ReactElement {\n const total = Math.min(lines, SHELL_LINES.length);\n const startIdx = Math.max(0, total - SHELL_WINDOW);\n const visible = SHELL_LINES.slice(startIdx, total);\n const hidden = startIdx;\n const running = lines < SHELL_LINES.length;\n const spin = SPINNER_FRAMES[frame % SPINNER_FRAMES.length] ?? \"·\";\n return (\n <inkCompat.Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor={BRAND}\n paddingX={1}\n marginTop={1}\n >\n <inkCompat.Box flexDirection=\"row\" gap={1}>\n <inkCompat.Text color={BRAND}>{running ? spin : \"✓\"}</inkCompat.Text>\n <inkCompat.Text color={BRAND} bold>\n npm test\n </inkCompat.Text>\n {hidden > 0 ? (\n <inkCompat.Text color={FAINT}>{`(+${hidden} earlier)`}</inkCompat.Text>\n ) : null}\n </inkCompat.Box>\n {visible.map((line, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: shell output lines are positional + append-only\n <inkCompat.Text key={`shell-${startIdx + i}`} dimColor={line.startsWith(\" PASS\")}>\n {line || \" \"}\n </inkCompat.Text>\n ))}\n </inkCompat.Box>\n );\n}\n\nfunction ResponseCard({\n revealed,\n frame,\n}: { revealed: number; frame: number }): React.ReactElement {\n const text = RESPONSE.slice(0, revealed);\n const done = revealed >= RESPONSE.length;\n const glyph = done ? \"‹\" : (SPINNER_FRAMES[frame % SPINNER_FRAMES.length] ?? \"·\");\n return (\n <inkCompat.Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor={OK}\n paddingX={1}\n marginTop={1}\n >\n <inkCompat.Box flexDirection=\"row\" gap={1}>\n <inkCompat.Text color={done ? OK : BRAND}>{glyph}</inkCompat.Text>\n <inkCompat.Text>{text || \"thinking…\"}</inkCompat.Text>\n </inkCompat.Box>\n </inkCompat.Box>\n );\n}\n\ninterface ShellProps {\n onExit: () => void;\n}\n\nexport function StressShell({ onExit }: ShellProps): React.ReactElement {\n const [elapsed, setElapsed] = useState(0);\n const [planFrame, setPlanFrame] = useState(0);\n const [shellLines, setShellLines] = useState(0);\n const [shellFrame, setShellFrame] = useState(0);\n const [revealed, setRevealed] = useState(0);\n const [responseFrame, setResponseFrame] = useState(0);\n\n const startedRef = useRef(Date.now());\n\n useKeystroke((k) => {\n if (k.escape) onExit();\n });\n\n useEffect(() => {\n const id = setInterval(() => {\n setElapsed(Date.now() - startedRef.current);\n }, 100);\n return () => clearInterval(id);\n }, []);\n\n useEffect(() => {\n const id = setInterval(() => {\n setPlanFrame((f) => f + 1);\n }, 80);\n return () => clearInterval(id);\n }, []);\n\n useEffect(() => {\n const id = setInterval(() => {\n setShellFrame((f) => f + 1);\n }, 80);\n return () => clearInterval(id);\n }, []);\n\n useEffect(() => {\n const id = setInterval(() => {\n setShellLines((n) => Math.min(SHELL_LINES.length, n + 1));\n }, 250);\n return () => clearInterval(id);\n }, []);\n\n useEffect(() => {\n const id = setInterval(() => {\n setResponseFrame((f) => f + 1);\n }, 80);\n return () => clearInterval(id);\n }, []);\n\n useEffect(() => {\n const id = setInterval(() => {\n setRevealed((r) => Math.min(RESPONSE.length, r + 4));\n }, 33);\n return () => clearInterval(id);\n }, []);\n\n return (\n <inkCompat.Box flexDirection=\"column\">\n <StatusRow elapsedMs={elapsed} />\n <PlanCard frame={planFrame} />\n <ShellCard lines={shellLines} frame={shellFrame} />\n <ResponseCard revealed={revealed} frame={responseFrame} />\n <inkCompat.Box marginTop={1}>\n <inkCompat.Text dimColor>stress mode · 4 concurrent live regions · Esc exit</inkCompat.Text>\n </inkCompat.Box>\n </inkCompat.Box>\n );\n}\n\nexport interface StressDemoOptions {\n readonly stdout?: NodeJS.WriteStream;\n readonly stdin?: NodeJS.ReadStream;\n}\n\nexport async function runStressDemo(opts: StressDemoOptions = {}): Promise<void> {\n const stdout = opts.stdout ?? process.stdout;\n const stdin = opts.stdin ?? process.stdin;\n\n if (!stdin.isTTY || !stdout.isTTY) {\n console.error(\"stress-demo requires an interactive TTY.\");\n process.exit(1);\n }\n\n const pools = {\n char: new CharPool(),\n style: new StylePool(),\n hyperlink: new HyperlinkPool(),\n };\n\n let resolveExit: () => void = () => {};\n const exited = new Promise<void>((resolve) => {\n resolveExit = resolve;\n });\n\n const handle: Handle = mount(<StressShell onExit={() => resolveExit()} />, {\n viewportWidth: stdout.columns ?? 80,\n viewportHeight: stdout.rows ?? 30,\n pools,\n write: (bytes) => stdout.write(bytes),\n stdin,\n onExit: () => resolveExit(),\n });\n\n const onResize = () => handle.resize(stdout.columns ?? 80, stdout.rows ?? 30);\n stdout.on(\"resize\", onResize);\n\n try {\n await exited;\n } finally {\n stdout.off(\"resize\", onResize);\n handle.destroy();\n stdin.pause();\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -631,6 +631,7 @@ declare class CacheFirstLoop {
631
631
  private _escalateThisTurn;
632
632
  private _turnFailureCount;
633
633
  private _turnFailureTypes;
634
+ private _turnSelfCorrected;
634
635
  constructor(opts: CacheFirstLoopOptions);
635
636
  /** Shrink huge edit_file/write_file args post-dispatch — tool result already explains. */
636
637
  private compactToolCallArgsAfterResponse;
@@ -642,6 +643,8 @@ declare class CacheFirstLoop {
642
643
  charsSaved: number;
643
644
  };
644
645
  appendAndPersist(message: ChatMessage): void;
646
+ /** Swap the just-appended assistant entry — used by self-correction to restore the original tool_calls without dropping reasoning_content. */
647
+ private replaceTailAssistantMessage;
645
648
  /** "New chat" — drops messages but keeps session + immutable prefix (cache-first invariant). */
646
649
  clearLog(): {
647
650
  dropped: number;
@@ -1827,6 +1830,8 @@ interface CodeSystemPromptOptions {
1827
1830
  }
1828
1831
  declare function codeSystemPrompt(rootDir: string, opts?: CodeSystemPromptOptions): string;
1829
1832
 
1833
+ type LanguageCode = "EN" | "zh-CN";
1834
+
1830
1835
  /** Shared exclude defaults + resolver — chunker, directory_tree, and dashboard read from here. */
1831
1836
  interface IndexUserConfig {
1832
1837
  excludeDirs?: string[];
@@ -1847,6 +1852,7 @@ type ReasoningEffort = "high" | "max";
1847
1852
  interface ReasonixConfig {
1848
1853
  apiKey?: string;
1849
1854
  baseUrl?: string;
1855
+ lang?: LanguageCode;
1850
1856
  preset?: PresetName;
1851
1857
  editMode?: EditMode;
1852
1858
  editModeHintShown?: boolean;
@@ -1858,8 +1864,6 @@ interface ReasonixConfig {
1858
1864
  session?: string | null;
1859
1865
  setupCompleted?: boolean;
1860
1866
  search?: boolean;
1861
- /** Persisted sidebar visibility — undefined = auto (open on cols ≥ 120). */
1862
- sidebarOpen?: boolean;
1863
1867
  projects?: {
1864
1868
  [absoluteRootDir: string]: {
1865
1869
  shellAllowed?: string[];
package/dist/index.js CHANGED
@@ -974,7 +974,8 @@ var ToolRegistry = class {
974
974
  }
975
975
  if (this._planMode && !isReadOnlyCall(tool, args)) {
976
976
  return JSON.stringify({
977
- error: `${name}: unavailable in plan mode \u2014 this is a read-only exploration phase. Use read_file / list_directory / search_files / directory_tree / web_search / allowlisted shell commands to investigate. Call submit_plan with your proposed plan when you're ready for the user's review.`
977
+ error: `${name}: unavailable in plan mode \u2014 this is a read-only exploration phase. Use read_file / list_directory / search_files / directory_tree / web_search / allowlisted shell commands to investigate. Call submit_plan with your proposed plan when you're ready for the user's review.`,
978
+ rejectedReason: "plan-mode"
978
979
  });
979
980
  }
980
981
  if (this._interceptor) {
@@ -1573,7 +1574,7 @@ var StormBreaker = class {
1573
1574
  if (count >= this.threshold - 1) {
1574
1575
  return {
1575
1576
  suppress: true,
1576
- reason: `call-storm suppressed: ${name} called with identical args ${count + 1} times within window=${this.windowSize}`
1577
+ reason: `${name} called with identical args ${count + 1} times \u2014 repeat-loop guard tripped`
1577
1578
  };
1578
1579
  }
1579
1580
  this.recent.push({ name, args, readOnly });
@@ -1858,6 +1859,7 @@ var CacheFirstLoop = class {
1858
1859
  _escalateThisTurn = false;
1859
1860
  _turnFailureCount = 0;
1860
1861
  _turnFailureTypes = {};
1862
+ _turnSelfCorrected = false;
1861
1863
  constructor(opts) {
1862
1864
  this.client = opts.client;
1863
1865
  this.prefix = opts.prefix;
@@ -1902,7 +1904,12 @@ var CacheFirstLoop = class {
1902
1904
  }
1903
1905
  return def.readOnly !== true;
1904
1906
  };
1905
- this.repair = new ToolCallRepair({ allowedToolNames: allowedNames, isMutating });
1907
+ this.repair = new ToolCallRepair({
1908
+ allowedToolNames: allowedNames,
1909
+ isMutating,
1910
+ stormThreshold: parsePositiveIntEnv(process.env.REASONIX_STORM_THRESHOLD),
1911
+ stormWindow: parsePositiveIntEnv(process.env.REASONIX_STORM_WINDOW)
1912
+ });
1906
1913
  this.sessionName = opts.session ?? null;
1907
1914
  if (this.sessionName) {
1908
1915
  const prior = loadSessionMessages(this.sessionName);
@@ -1984,6 +1991,21 @@ var CacheFirstLoop = class {
1984
1991
  }
1985
1992
  }
1986
1993
  }
1994
+ /** Swap the just-appended assistant entry — used by self-correction to restore the original tool_calls without dropping reasoning_content. */
1995
+ replaceTailAssistantMessage(message) {
1996
+ const entries = this.log.entries;
1997
+ const tail = entries[entries.length - 1];
1998
+ if (!tail || tail.role !== "assistant") return;
1999
+ const kept = entries.slice(0, -1);
2000
+ kept.push(message);
2001
+ this.log.compactInPlace(kept);
2002
+ if (this.sessionName) {
2003
+ try {
2004
+ rewriteSession(this.sessionName, kept);
2005
+ } catch {
2006
+ }
2007
+ }
2008
+ }
1987
2009
  /** "New chat" — drops messages but keeps session + immutable prefix (cache-first invariant). */
1988
2010
  clearLog() {
1989
2011
  const dropped = this.log.length;
@@ -2084,7 +2106,7 @@ var CacheFirstLoop = class {
2084
2106
  if (repair) {
2085
2107
  if (repair.scavenged > 0) bump("scavenged", repair.scavenged);
2086
2108
  if (repair.truncationsFixed > 0) bump("truncated", repair.truncationsFixed);
2087
- if (repair.stormsBroken > 0) bump("storm-broken", repair.stormsBroken);
2109
+ if (repair.stormsBroken > 0) bump("repeat-loop", repair.stormsBroken);
2088
2110
  }
2089
2111
  if (bumped && !this._escalateThisTurn && this.autoEscalate && this._turnFailureCount >= FAILURE_ESCALATION_THRESHOLD) {
2090
2112
  this._escalateThisTurn = true;
@@ -2154,6 +2176,7 @@ var CacheFirstLoop = class {
2154
2176
  this.repair.resetStorm();
2155
2177
  this._turnFailureCount = 0;
2156
2178
  this._turnFailureTypes = {};
2179
+ this._turnSelfCorrected = false;
2157
2180
  this._escalateThisTurn = false;
2158
2181
  let armedConsumed = false;
2159
2182
  if (this._proArmedForNextTurn) {
@@ -2505,10 +2528,35 @@ var CacheFirstLoop = class {
2505
2528
  content: `\u21E7 auto-escalating to ${ESCALATION_MODEL} for the rest of this turn \u2014 flash hit ${this.formatFailureBreakdown()}. Next turn falls back to ${this.model} unless /pro is armed.`
2506
2529
  };
2507
2530
  }
2531
+ const allSuppressed = report.stormsBroken > 0 && repairedCalls.length === 0 && toolCalls.length > 0;
2532
+ if (allSuppressed && !this._turnSelfCorrected) {
2533
+ this._turnSelfCorrected = true;
2534
+ this.replaceTailAssistantMessage(
2535
+ this.assistantMessage(
2536
+ assistantContent,
2537
+ toolCalls,
2538
+ this.modelForCurrentCall(),
2539
+ reasoningContent
2540
+ )
2541
+ );
2542
+ for (const call of toolCalls) {
2543
+ this.appendAndPersist({
2544
+ role: "tool",
2545
+ tool_call_id: call.id ?? "",
2546
+ name: call.function?.name ?? "",
2547
+ content: "[repeat-loop guard] this call was suppressed because it was identical to a previous call in this turn. Earlier results for it are above \u2014 try a meaningfully different approach, or stop and answer if you have enough."
2548
+ });
2549
+ }
2550
+ yield {
2551
+ turn: this._turn,
2552
+ role: "warning",
2553
+ content: "Caught a repeated tool call \u2014 let the model see the issue and retry with a different approach."
2554
+ };
2555
+ continue;
2556
+ }
2508
2557
  if (report.stormsBroken > 0) {
2509
2558
  const noteTail = report.notes.length ? ` \u2014 ${report.notes[report.notes.length - 1]}` : "";
2510
- const allSuppressed = repairedCalls.length === 0 && toolCalls.length > 0;
2511
- const phrase = allSuppressed ? `stopped the model from calling the same tool with identical args repeatedly (all ${toolCalls.length} call(s) this turn were already in the recent-repeat window). Likely a stuck retry \u2014 reword your instruction, rule out the underlying blocker, or try /retry after fixing it` : `suppressed ${report.stormsBroken} repeat tool call(s) that had fired 3+ times with identical args in a sliding window`;
2559
+ const phrase = allSuppressed ? "Stopped a stuck retry loop \u2014 the model kept calling the same tool with identical args after a self-correction nudge. Try /retry, rephrase, or rule out the underlying blocker." : `Suppressed ${report.stormsBroken} repeated tool call(s) \u2014 same name + args fired 3+ times.`;
2512
2560
  yield {
2513
2561
  turn: this._turn,
2514
2562
  role: "warning",
@@ -2516,7 +2564,6 @@ var CacheFirstLoop = class {
2516
2564
  };
2517
2565
  }
2518
2566
  if (repairedCalls.length === 0) {
2519
- const allSuppressed = report.stormsBroken > 0 && toolCalls.length > 0;
2520
2567
  if (allSuppressed) {
2521
2568
  yield* this.forceSummaryAfterIterLimit({ reason: "stuck" });
2522
2569
  return;
@@ -2741,6 +2788,11 @@ function stripHallucinatedToolMarkup(s) {
2741
2788
  out = out.replace(/<|DSML|[\s\S]*$/g, "");
2742
2789
  return out.trim();
2743
2790
  }
2791
+ function parsePositiveIntEnv(raw) {
2792
+ if (!raw) return void 0;
2793
+ const n = Number.parseInt(raw, 10);
2794
+ return Number.isFinite(n) && n > 0 ? n : void 0;
2795
+ }
2744
2796
  function safeParseToolArgs(raw) {
2745
2797
  try {
2746
2798
  return JSON.parse(raw);
@@ -4522,22 +4574,25 @@ Prefer \`list_directory\` for a single-level view, \`search_files\` to find spec
4522
4574
  if (args.search.length === 0) {
4523
4575
  throw new Error("edit_file: search cannot be empty");
4524
4576
  }
4525
- const firstIdx = before.indexOf(args.search);
4577
+ const le = before.includes("\r\n") ? "\r\n" : "\n";
4578
+ const adaptedSearch = args.search.replace(/\r?\n/g, le);
4579
+ const adaptedReplace = args.replace.replace(/\r?\n/g, le);
4580
+ const firstIdx = before.indexOf(adaptedSearch);
4526
4581
  if (firstIdx < 0) {
4527
4582
  throw new Error(`edit_file: search text not found in ${pathMod.relative(rootDir, abs)}`);
4528
4583
  }
4529
- const nextIdx = before.indexOf(args.search, firstIdx + 1);
4584
+ const nextIdx = before.indexOf(adaptedSearch, firstIdx + 1);
4530
4585
  if (nextIdx >= 0) {
4531
4586
  throw new Error(
4532
4587
  `edit_file: search text appears multiple times in ${pathMod.relative(rootDir, abs)} \u2014 include more context to disambiguate`
4533
4588
  );
4534
4589
  }
4535
- const after = before.slice(0, firstIdx) + args.replace + before.slice(firstIdx + args.search.length);
4590
+ const after = before.slice(0, firstIdx) + adaptedReplace + before.slice(firstIdx + adaptedSearch.length);
4536
4591
  await fs.writeFile(abs, after, "utf8");
4537
4592
  const rel = pathMod.relative(rootDir, abs);
4538
- const header = `edited ${rel} (${args.search.length}\u2192${args.replace.length} chars)`;
4593
+ const header = `edited ${rel} (${adaptedSearch.length}\u2192${adaptedReplace.length} chars)`;
4539
4594
  const startLine = before.slice(0, firstIdx).split(/\r?\n/).length;
4540
- const diff = renderEditDiff(args.search, args.replace, startLine);
4595
+ const diff = renderEditDiff(adaptedSearch, adaptedReplace, startLine);
4541
4596
  return `${header}
4542
4597
  ${diff}`;
4543
4598
  }
@@ -7330,7 +7385,10 @@ var StdioTransport = class {
7330
7385
  } catch {
7331
7386
  }
7332
7387
  if (this.child.exitCode === null && !this.child.killed) {
7333
- this.child.kill("SIGTERM");
7388
+ try {
7389
+ this.child.kill(process.platform === "win32" ? void 0 : "SIGTERM");
7390
+ } catch {
7391
+ }
7334
7392
  }
7335
7393
  }
7336
7394
  /** Parse incoming stdout chunks into NDJSON messages. */
@@ -7818,7 +7876,10 @@ function applyEditBlock(block, rootDir) {
7818
7876
  message: "empty SEARCH only creates new files \u2014 this file already exists"
7819
7877
  };
7820
7878
  }
7821
- const idx = content.indexOf(block.search);
7879
+ const le = lineEndingOf(content);
7880
+ const adaptedSearch = block.search.replace(/\r?\n/g, le);
7881
+ const adaptedReplace = block.replace.replace(/\r?\n/g, le);
7882
+ const idx = content.indexOf(adaptedSearch);
7822
7883
  if (idx === -1) {
7823
7884
  return {
7824
7885
  path: block.path,
@@ -7826,7 +7887,7 @@ function applyEditBlock(block, rootDir) {
7826
7887
  message: "SEARCH text does not match the current file content exactly"
7827
7888
  };
7828
7889
  }
7829
- const replaced = `${content.slice(0, idx)}${block.replace}${content.slice(idx + block.search.length)}`;
7890
+ const replaced = `${content.slice(0, idx)}${adaptedReplace}${content.slice(idx + adaptedSearch.length)}`;
7830
7891
  writeFileSync4(absTarget, replaced, "utf8");
7831
7892
  return { path: block.path, status: "applied" };
7832
7893
  } catch (err) {
@@ -7890,6 +7951,9 @@ function restoreSnapshots(snapshots, rootDir) {
7890
7951
  function sep() {
7891
7952
  return process.platform === "win32" ? "\\" : "/";
7892
7953
  }
7954
+ function lineEndingOf(text) {
7955
+ return text.includes("\r\n") ? "\r\n" : "\n";
7956
+ }
7893
7957
 
7894
7958
  // src/code/prompt.ts
7895
7959
  import { existsSync as existsSync11, readFileSync as readFileSync13 } from "fs";