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.
- package/dashboard/dist/app.js +1723 -574
- package/dashboard/dist/app.js.map +1 -1
- package/dist/cli/.-3G6VX5S7.js +327 -0
- package/dist/cli/.-6YRPB2C7.js +329 -0
- package/dist/cli/.-6YRPB2C7.js.map +1 -0
- package/dist/cli/.-EYSVINK3.js +317 -0
- package/dist/cli/.-EYSVINK3.js.map +1 -0
- package/dist/cli/banner-demo-QKOPDSTL.js +77 -0
- package/dist/cli/banner-demo-QKOPDSTL.js.map +1 -0
- package/dist/cli/card-demo-5TVXJISK.js +944 -0
- package/dist/cli/card-demo-5TVXJISK.js.map +1 -0
- package/dist/cli/chunk-2H7UOFLK.js +11 -0
- package/dist/cli/chunk-2H7UOFLK.js.map +1 -0
- package/dist/cli/chunk-BGTXZKNY.js +197 -0
- package/dist/cli/chunk-BGTXZKNY.js.map +1 -0
- package/dist/cli/chunk-JHXQDL7B.js +2056 -0
- package/dist/cli/chunk-JHXQDL7B.js.map +1 -0
- package/dist/cli/flicker-demo-MOB6GAW4.js +165 -0
- package/dist/cli/flicker-demo-MOB6GAW4.js.map +1 -0
- package/dist/cli/index.js +3818 -2021
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/preview-4FOEEE5G.js +224 -0
- package/dist/cli/preview-4FOEEE5G.js.map +1 -0
- package/dist/cli/{prompt-YUL7CYKY.js → prompt-VZQ2CPID.js} +2 -1
- package/dist/cli/prompt-VZQ2CPID.js.map +1 -0
- package/dist/cli/renderer-demo-2BIGEV2T.js +95 -0
- package/dist/cli/renderer-demo-2BIGEV2T.js.map +1 -0
- package/dist/cli/select-demo-OA5N34BJ.js +107 -0
- package/dist/cli/select-demo-OA5N34BJ.js.map +1 -0
- package/dist/cli/stress-demo-I7XRPQMM.js +211 -0
- package/dist/cli/stress-demo-I7XRPQMM.js.map +1 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +79 -15
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
- /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:
|
|
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({
|
|
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("
|
|
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
|
|
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
|
|
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(
|
|
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) +
|
|
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} (${
|
|
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(
|
|
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
|
-
|
|
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
|
|
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)}${
|
|
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";
|