reasonix 0.46.0 → 0.47.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/README.md +64 -12
- package/README.zh-CN.md +54 -9
- package/dashboard/dist/app.js +293 -66
- package/dashboard/dist/app.js.map +1 -1
- package/dist/cli/{acp-LGBLHBKY.js → acp-QK3DMC53.js} +22 -22
- package/dist/cli/chat-VV5UWY4V.js +51 -0
- package/dist/cli/{chunk-AVFXO2EZ.js → chunk-24A7FHGJ.js} +148 -16
- package/dist/cli/chunk-24A7FHGJ.js.map +1 -0
- package/dist/cli/chunk-25T6CVUP.js +0 -0
- package/dist/cli/chunk-2UQP6H6T.js +0 -0
- package/dist/cli/chunk-5QCB62C4.js +0 -0
- package/dist/cli/{chunk-YY227BIQ.js → chunk-6J6BSUCR.js} +2 -2
- package/dist/cli/chunk-6OWJV3YW.js +0 -0
- package/dist/cli/{chunk-A3TSSDS2.js → chunk-BWYVFFKR.js} +2 -2
- package/dist/cli/{chunk-C53JQES5.js → chunk-BYYVYJDX.js} +3 -3
- package/dist/cli/{chunk-HNXDZGC6.js → chunk-CI2PF5QX.js} +2 -2
- package/dist/cli/{chunk-GTZTQNX5.js → chunk-COWPEX54.js} +19 -9
- package/dist/cli/chunk-COWPEX54.js.map +1 -0
- package/dist/cli/{chunk-QJDDIK3Z.js → chunk-E5WCLUIU.js} +2 -2
- package/dist/cli/{chunk-NVURFF27.js → chunk-EQATK2L2.js} +2 -2
- package/dist/cli/{chunk-HKWSPKMU.js → chunk-FDKOUJKZ.js} +8 -8
- package/dist/cli/chunk-FEZK652I.js +0 -0
- package/dist/cli/{chunk-TEUDEGX2.js → chunk-FY4S7TJZ.js} +19 -5
- package/dist/cli/chunk-FY4S7TJZ.js.map +1 -0
- package/dist/cli/{chunk-RDRC3XDT.js → chunk-GDKB2PPK.js} +2 -2
- package/dist/cli/{chunk-XSU4QVFW.js → chunk-HIYTRCSW.js} +27 -14
- package/dist/cli/chunk-HIYTRCSW.js.map +1 -0
- package/dist/cli/{chunk-WL6SNQ5T.js → chunk-ICAFSZHS.js} +307 -114
- package/dist/cli/chunk-ICAFSZHS.js.map +1 -0
- package/dist/cli/{chunk-KQU2TYIL.js → chunk-ICSYGIPN.js} +1916 -1098
- package/dist/cli/chunk-ICSYGIPN.js.map +1 -0
- package/dist/cli/chunk-J5XJHLWM.js +0 -0
- package/dist/cli/chunk-JMBMLOBP.js +0 -0
- package/dist/cli/{chunk-MJ6W5UN3.js → chunk-K6GUKSXH.js} +3 -2
- package/dist/cli/chunk-K6GUKSXH.js.map +1 -0
- package/dist/cli/{chunk-IJ7JA32V.js → chunk-KDRUEXII.js} +189 -26
- package/dist/cli/chunk-KDRUEXII.js.map +1 -0
- package/dist/cli/{chunk-4HCP2UQW.js → chunk-LBLR4CUZ.js} +2 -2
- package/dist/cli/{chunk-2425HK6U.js → chunk-LGEKVMMV.js} +7 -2
- package/dist/cli/{chunk-2425HK6U.js.map → chunk-LGEKVMMV.js.map} +1 -1
- package/dist/cli/{chunk-I4L2GTSE.js → chunk-OJVITDGB.js} +2 -2
- package/dist/cli/chunk-PLHAZOLZ.js +0 -0
- package/dist/cli/{chunk-W7YGWUWU.js → chunk-QVDWH2A2.js} +3 -3
- package/dist/cli/{chunk-R3CTO2HM.js → chunk-QVUFWDD2.js} +2 -2
- package/dist/cli/{chunk-HVUZWNSP.js → chunk-R6GQKKBW.js} +2 -2
- package/dist/cli/{chunk-5ACMUK4Q.js → chunk-RRXUIPWG.js} +20 -18
- package/dist/cli/chunk-RRXUIPWG.js.map +1 -0
- package/dist/cli/chunk-S4XVGLRW.js +0 -0
- package/dist/cli/chunk-SZ5XES2N.js +0 -0
- package/dist/cli/{chunk-CXVWUPA3.js → chunk-TKVXTQ3T.js} +26 -26
- package/dist/cli/chunk-TKVXTQ3T.js.map +1 -0
- package/dist/cli/chunk-TUK7OWJA.js +0 -0
- package/dist/cli/{chunk-JNAQYELD.js → chunk-UDVFBEXC.js} +3 -3
- package/dist/cli/{chunk-CBIQWMS6.js → chunk-VC2CQA5D.js} +9 -9
- package/dist/cli/{chunk-ZZYBBX5N.js → chunk-VJMBISEI.js} +23 -9
- package/dist/cli/chunk-VJMBISEI.js.map +1 -0
- package/dist/cli/{chunk-WK3UFQY3.js → chunk-VKYSZKH2.js} +2 -2
- package/dist/cli/{chunk-LIR2HBQH.js → chunk-VMUUFWFF.js} +2 -2
- package/dist/cli/{chunk-V26WPN3J.js → chunk-VNQGCA3Q.js} +28 -1
- package/dist/cli/chunk-VNQGCA3Q.js.map +1 -0
- package/dist/cli/{chunk-5I2C4JEO.js → chunk-WF7TPVZM.js} +6 -6
- package/dist/cli/{chunk-5I2C4JEO.js.map → chunk-WF7TPVZM.js.map} +1 -1
- package/dist/cli/chunk-X53B3JIX.js +0 -0
- package/dist/cli/chunk-XJXDHAES.js +0 -0
- package/dist/cli/chunk-XXC2BYTV.js +0 -0
- package/dist/cli/{chunk-4CTDEJUF.js → chunk-YDPLF7XR.js} +26 -14
- package/dist/cli/chunk-YDPLF7XR.js.map +1 -0
- package/dist/cli/chunk-ZZM6QJ4W.js +0 -0
- package/dist/cli/{code-DFHSASJ4.js → code-C24TUAE5.js} +39 -35
- package/dist/cli/code-C24TUAE5.js.map +1 -0
- package/dist/cli/{commands-OCU42XG4.js → commands-RR3GIYOK.js} +4 -4
- package/dist/cli/{commit-XCQIQCYG.js → commit-FSHPIINM.js} +3 -3
- package/dist/cli/{desktop-ZCUG7LMF.js → desktop-7NCHPEFB.js} +263 -36
- package/dist/cli/desktop-7NCHPEFB.js.map +1 -0
- package/dist/cli/devtools-HW3WDT3Q.js +0 -0
- package/dist/cli/{diff-66B2KWOJ.js → diff-RAAHHLHV.js} +8 -8
- package/dist/cli/{doctor-Y73CPPRZ.js → doctor-PKVQIXRT.js} +9 -9
- package/dist/cli/{events-NGZ2OJYH.js → events-VRYXOSKI.js} +3 -3
- package/dist/cli/index.js +84 -92
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{mcp-MPVGBBJF.js → mcp-CRJ26PP4.js} +2 -2
- package/dist/cli/{mcp-browse-4XOTC3FJ.js → mcp-browse-QPAOWZOP.js} +2 -2
- package/dist/cli/{mcp-inspect-CEMGKKAH.js → mcp-inspect-CVCLABRS.js} +4 -4
- package/dist/cli/{prompt-2D7ID24X.js → prompt-SKYXERSI.js} +4 -4
- package/dist/cli/{prune-sessions-OJEYYLHY.js → prune-sessions-SEWX7GP6.js} +2 -2
- package/dist/cli/{replay-AKYQNAQJ.js → replay-KPDW2ZMJ.js} +9 -9
- package/dist/cli/{run-5DPQFSP6.js → run-WIKDIXTG.js} +18 -19
- package/dist/cli/run-WIKDIXTG.js.map +1 -0
- package/dist/cli/{server-TQ2IHYQJ.js → server-P6V2G3P6.js} +82 -34
- package/dist/cli/server-P6V2G3P6.js.map +1 -0
- package/dist/cli/{sessions-KY54NG45.js → sessions-2NULRMSA.js} +29 -15
- package/dist/cli/sessions-2NULRMSA.js.map +1 -0
- package/dist/cli/{setup-XPIOZWS7.js → setup-Y5WDBQFL.js} +8 -8
- package/dist/cli/setup-Y5WDBQFL.js.map +1 -0
- package/dist/cli/{stats-X2VTWKNS.js → stats-T7BL2YOR.js} +6 -6
- package/dist/cli/update-6ITLPRDV.js +0 -0
- package/dist/cli/{version-7O6A5T7Q.js → version-3KWDNWLN.js} +15 -15
- package/dist/index.d.ts +54 -23
- package/dist/index.js +1613 -1152
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/.-3G6VX5S7.js +0 -327
- package/dist/cli/.-6YRPB2C7.js +0 -329
- package/dist/cli/.-EYSVINK3.js +0 -317
- package/dist/cli/chat-ECK5ZGMV.js +0 -51
- package/dist/cli/chunk-4CTDEJUF.js.map +0 -1
- package/dist/cli/chunk-5ACMUK4Q.js.map +0 -1
- package/dist/cli/chunk-AVFXO2EZ.js.map +0 -1
- package/dist/cli/chunk-CXVWUPA3.js.map +0 -1
- package/dist/cli/chunk-GTZTQNX5.js.map +0 -1
- package/dist/cli/chunk-IJ7JA32V.js.map +0 -1
- package/dist/cli/chunk-KQU2TYIL.js.map +0 -1
- package/dist/cli/chunk-MJ6W5UN3.js.map +0 -1
- package/dist/cli/chunk-TEUDEGX2.js.map +0 -1
- package/dist/cli/chunk-V26WPN3J.js.map +0 -1
- package/dist/cli/chunk-WL6SNQ5T.js.map +0 -1
- package/dist/cli/chunk-XSU4QVFW.js.map +0 -1
- package/dist/cli/chunk-ZZYBBX5N.js.map +0 -1
- package/dist/cli/code-DFHSASJ4.js.map +0 -1
- package/dist/cli/desktop-ZCUG7LMF.js.map +0 -1
- package/dist/cli/doctor-Y73CPPRZ.js.map +0 -1
- package/dist/cli/prompt-2D7ID24X.js.map +0 -1
- package/dist/cli/run-5DPQFSP6.js.map +0 -1
- package/dist/cli/server-TQ2IHYQJ.js.map +0 -1
- package/dist/cli/sessions-KY54NG45.js.map +0 -1
- package/dist/cli/setup-XPIOZWS7.js.map +0 -1
- package/dist/cli/stats-X2VTWKNS.js.map +0 -1
- /package/dist/cli/{acp-LGBLHBKY.js.map → acp-QK3DMC53.js.map} +0 -0
- /package/dist/cli/{.-3G6VX5S7.js.map → chat-VV5UWY4V.js.map} +0 -0
- /package/dist/cli/{chunk-YY227BIQ.js.map → chunk-6J6BSUCR.js.map} +0 -0
- /package/dist/cli/{chunk-A3TSSDS2.js.map → chunk-BWYVFFKR.js.map} +0 -0
- /package/dist/cli/{chunk-C53JQES5.js.map → chunk-BYYVYJDX.js.map} +0 -0
- /package/dist/cli/{chunk-HNXDZGC6.js.map → chunk-CI2PF5QX.js.map} +0 -0
- /package/dist/cli/{chunk-QJDDIK3Z.js.map → chunk-E5WCLUIU.js.map} +0 -0
- /package/dist/cli/{chunk-NVURFF27.js.map → chunk-EQATK2L2.js.map} +0 -0
- /package/dist/cli/{chunk-HKWSPKMU.js.map → chunk-FDKOUJKZ.js.map} +0 -0
- /package/dist/cli/{chunk-RDRC3XDT.js.map → chunk-GDKB2PPK.js.map} +0 -0
- /package/dist/cli/{chunk-4HCP2UQW.js.map → chunk-LBLR4CUZ.js.map} +0 -0
- /package/dist/cli/{chunk-I4L2GTSE.js.map → chunk-OJVITDGB.js.map} +0 -0
- /package/dist/cli/{chunk-W7YGWUWU.js.map → chunk-QVDWH2A2.js.map} +0 -0
- /package/dist/cli/{chunk-R3CTO2HM.js.map → chunk-QVUFWDD2.js.map} +0 -0
- /package/dist/cli/{chunk-HVUZWNSP.js.map → chunk-R6GQKKBW.js.map} +0 -0
- /package/dist/cli/{chunk-JNAQYELD.js.map → chunk-UDVFBEXC.js.map} +0 -0
- /package/dist/cli/{chunk-CBIQWMS6.js.map → chunk-VC2CQA5D.js.map} +0 -0
- /package/dist/cli/{chunk-WK3UFQY3.js.map → chunk-VKYSZKH2.js.map} +0 -0
- /package/dist/cli/{chunk-LIR2HBQH.js.map → chunk-VMUUFWFF.js.map} +0 -0
- /package/dist/cli/{commands-OCU42XG4.js.map → commands-RR3GIYOK.js.map} +0 -0
- /package/dist/cli/{commit-XCQIQCYG.js.map → commit-FSHPIINM.js.map} +0 -0
- /package/dist/cli/{diff-66B2KWOJ.js.map → diff-RAAHHLHV.js.map} +0 -0
- /package/dist/cli/{.-6YRPB2C7.js.map → doctor-PKVQIXRT.js.map} +0 -0
- /package/dist/cli/{events-NGZ2OJYH.js.map → events-VRYXOSKI.js.map} +0 -0
- /package/dist/cli/{mcp-MPVGBBJF.js.map → mcp-CRJ26PP4.js.map} +0 -0
- /package/dist/cli/{mcp-browse-4XOTC3FJ.js.map → mcp-browse-QPAOWZOP.js.map} +0 -0
- /package/dist/cli/{mcp-inspect-CEMGKKAH.js.map → mcp-inspect-CVCLABRS.js.map} +0 -0
- /package/dist/cli/{.-EYSVINK3.js.map → prompt-SKYXERSI.js.map} +0 -0
- /package/dist/cli/{prune-sessions-OJEYYLHY.js.map → prune-sessions-SEWX7GP6.js.map} +0 -0
- /package/dist/cli/{replay-AKYQNAQJ.js.map → replay-KPDW2ZMJ.js.map} +0 -0
- /package/dist/cli/{chat-ECK5ZGMV.js.map → stats-T7BL2YOR.js.map} +0 -0
- /package/dist/cli/{version-7O6A5T7Q.js.map → version-3KWDNWLN.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,1045 +1,1102 @@
|
|
|
1
1
|
// src/client.ts
|
|
2
2
|
import { createParser } from "eventsource-parser";
|
|
3
3
|
|
|
4
|
-
// src/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
waitMs
|
|
31
|
-
});
|
|
32
|
-
await sleep(waitMs, opts.signal);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
throw lastError ?? new Error("fetchWithRetry: loop exited unexpectedly");
|
|
36
|
-
}
|
|
37
|
-
function computeWait(attempt, initial, cap, retryAfter) {
|
|
38
|
-
if (retryAfter) {
|
|
39
|
-
const seconds = Number.parseFloat(retryAfter);
|
|
40
|
-
if (Number.isFinite(seconds) && seconds > 0) {
|
|
41
|
-
return Math.min(seconds * 1e3, cap);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
const exp = initial * 2 ** attempt;
|
|
45
|
-
const jitter = exp * (0.75 + Math.random() * 0.5);
|
|
46
|
-
return Math.min(Math.max(jitter, 0), cap);
|
|
47
|
-
}
|
|
48
|
-
function sleep(ms, signal) {
|
|
49
|
-
if (ms <= 0) return Promise.resolve();
|
|
50
|
-
return new Promise((resolve13, reject) => {
|
|
51
|
-
const timer = setTimeout(resolve13, ms);
|
|
52
|
-
if (signal) {
|
|
53
|
-
const onAbort = () => {
|
|
54
|
-
clearTimeout(timer);
|
|
55
|
-
reject(new Error("aborted"));
|
|
56
|
-
};
|
|
57
|
-
if (signal.aborted) onAbort();
|
|
58
|
-
else signal.addEventListener("abort", onAbort, { once: true });
|
|
59
|
-
}
|
|
60
|
-
});
|
|
4
|
+
// src/config.ts
|
|
5
|
+
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
6
|
+
import { homedir } from "os";
|
|
7
|
+
import { dirname, isAbsolute, join, resolve } from "path";
|
|
8
|
+
|
|
9
|
+
// src/cli/ui/theme/tokens.ts
|
|
10
|
+
function card(fg, tone) {
|
|
11
|
+
return {
|
|
12
|
+
user: { color: tone.brand, glyph: "\u25C7" },
|
|
13
|
+
reasoning: { color: tone.accent, glyph: "\u25C6" },
|
|
14
|
+
streaming: { color: tone.brand, glyph: "\u25C8" },
|
|
15
|
+
task: { color: tone.warn, glyph: "\u25B6" },
|
|
16
|
+
tool: { color: tone.info, glyph: "\u25A3" },
|
|
17
|
+
plan: { color: tone.accent, glyph: "\u229E" },
|
|
18
|
+
diff: { color: tone.ok, glyph: "\xB1" },
|
|
19
|
+
error: { color: tone.err, glyph: "\u2716" },
|
|
20
|
+
warn: { color: tone.warn, glyph: "\u26A0" },
|
|
21
|
+
usage: { color: fg.meta, glyph: "\u03A3" },
|
|
22
|
+
subagent: { color: tone.violet, glyph: "\u232C" },
|
|
23
|
+
approval: { color: tone.warn, glyph: "?" },
|
|
24
|
+
search: { color: tone.info, glyph: "\u2299" },
|
|
25
|
+
memory: { color: fg.meta, glyph: "\u2311" },
|
|
26
|
+
ctx: { color: tone.brand, glyph: "\u25D4" },
|
|
27
|
+
doctor: { color: fg.meta, glyph: "\u2695" },
|
|
28
|
+
branch: { color: tone.violet, glyph: "\u2387" }
|
|
29
|
+
};
|
|
61
30
|
}
|
|
62
|
-
function
|
|
63
|
-
|
|
64
|
-
const name = err.name;
|
|
65
|
-
return name === "AbortError";
|
|
31
|
+
function defineTheme(base) {
|
|
32
|
+
return { ...base, card: card(base.fg, base.tone) };
|
|
66
33
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
34
|
+
var githubDark = defineTheme({
|
|
35
|
+
fg: {
|
|
36
|
+
strong: "#e6edf3",
|
|
37
|
+
body: "#c9d1d9",
|
|
38
|
+
sub: "#8b949e",
|
|
39
|
+
meta: "#6e7681",
|
|
40
|
+
faint: "#484f58"
|
|
41
|
+
},
|
|
42
|
+
tone: {
|
|
43
|
+
brand: "#79c0ff",
|
|
44
|
+
accent: "#d2a8ff",
|
|
45
|
+
violet: "#b395f5",
|
|
46
|
+
ok: "#7ee787",
|
|
47
|
+
warn: "#f0b07d",
|
|
48
|
+
err: "#ff8b81",
|
|
49
|
+
info: "#79c0ff"
|
|
50
|
+
},
|
|
51
|
+
toneActive: {
|
|
52
|
+
brand: "#a5d6ff",
|
|
53
|
+
accent: "#e2c5ff",
|
|
54
|
+
violet: "#c8aaff",
|
|
55
|
+
ok: "#a8f5ad",
|
|
56
|
+
warn: "#ffc99e",
|
|
57
|
+
err: "#ffaba3",
|
|
58
|
+
info: "#a5d6ff"
|
|
59
|
+
},
|
|
60
|
+
surface: {
|
|
61
|
+
bg: "#0a0c10",
|
|
62
|
+
bgInput: "#0d1015",
|
|
63
|
+
bgCode: "#06080c",
|
|
64
|
+
bgElev: "#11141a"
|
|
73
65
|
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
66
|
+
});
|
|
67
|
+
var dark = defineTheme({
|
|
68
|
+
fg: {
|
|
69
|
+
strong: "#f4f7fb",
|
|
70
|
+
body: "#d8dee9",
|
|
71
|
+
sub: "#a7b1c2",
|
|
72
|
+
meta: "#778294",
|
|
73
|
+
faint: "#4d5666"
|
|
74
|
+
},
|
|
75
|
+
tone: {
|
|
76
|
+
brand: "#7dd3fc",
|
|
77
|
+
accent: "#c084fc",
|
|
78
|
+
violet: "#a78bfa",
|
|
79
|
+
ok: "#86efac",
|
|
80
|
+
warn: "#fbbf24",
|
|
81
|
+
err: "#f87171",
|
|
82
|
+
info: "#60a5fa"
|
|
83
|
+
},
|
|
84
|
+
toneActive: {
|
|
85
|
+
brand: "#bae6fd",
|
|
86
|
+
accent: "#e9d5ff",
|
|
87
|
+
violet: "#ddd6fe",
|
|
88
|
+
ok: "#bbf7d0",
|
|
89
|
+
warn: "#fde68a",
|
|
90
|
+
err: "#fecaca",
|
|
91
|
+
info: "#bfdbfe"
|
|
92
|
+
},
|
|
93
|
+
surface: {
|
|
94
|
+
bg: "#0b1020",
|
|
95
|
+
bgInput: "#111827",
|
|
96
|
+
bgCode: "#080c16",
|
|
97
|
+
bgElev: "#151d2f"
|
|
84
98
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
99
|
+
});
|
|
100
|
+
var light = defineTheme({
|
|
101
|
+
fg: {
|
|
102
|
+
strong: "#111827",
|
|
103
|
+
body: "#1f2937",
|
|
104
|
+
sub: "#4b5563",
|
|
105
|
+
meta: "#6b7280",
|
|
106
|
+
faint: "#9ca3af"
|
|
107
|
+
},
|
|
108
|
+
tone: {
|
|
109
|
+
brand: "#2563eb",
|
|
110
|
+
accent: "#7c3aed",
|
|
111
|
+
violet: "#6d28d9",
|
|
112
|
+
ok: "#15803d",
|
|
113
|
+
warn: "#b45309",
|
|
114
|
+
err: "#dc2626",
|
|
115
|
+
info: "#0369a1"
|
|
116
|
+
},
|
|
117
|
+
toneActive: {
|
|
118
|
+
brand: "#1d4ed8",
|
|
119
|
+
accent: "#6d28d9",
|
|
120
|
+
violet: "#5b21b6",
|
|
121
|
+
ok: "#166534",
|
|
122
|
+
warn: "#92400e",
|
|
123
|
+
err: "#b91c1c",
|
|
124
|
+
info: "#075985"
|
|
125
|
+
},
|
|
126
|
+
surface: {
|
|
127
|
+
bg: "#ffffff",
|
|
128
|
+
bgInput: "#f8fafc",
|
|
129
|
+
bgCode: "#f3f4f6",
|
|
130
|
+
bgElev: "#eef2f7"
|
|
93
131
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
132
|
+
});
|
|
133
|
+
var tokyoNight = defineTheme({
|
|
134
|
+
fg: {
|
|
135
|
+
strong: "#c0caf5",
|
|
136
|
+
body: "#a9b1d6",
|
|
137
|
+
sub: "#9aa5ce",
|
|
138
|
+
meta: "#565f89",
|
|
139
|
+
faint: "#414868"
|
|
140
|
+
},
|
|
141
|
+
tone: {
|
|
142
|
+
brand: "#7aa2f7",
|
|
143
|
+
accent: "#bb9af7",
|
|
144
|
+
violet: "#9d7cd8",
|
|
145
|
+
ok: "#9ece6a",
|
|
146
|
+
warn: "#e0af68",
|
|
147
|
+
err: "#f7768e",
|
|
148
|
+
info: "#2ac3de"
|
|
149
|
+
},
|
|
150
|
+
toneActive: {
|
|
151
|
+
brand: "#a9c7ff",
|
|
152
|
+
accent: "#d7b9ff",
|
|
153
|
+
violet: "#c6a0f6",
|
|
154
|
+
ok: "#b9f27c",
|
|
155
|
+
warn: "#ffd089",
|
|
156
|
+
err: "#ff9cac",
|
|
157
|
+
info: "#7dcfff"
|
|
158
|
+
},
|
|
159
|
+
surface: {
|
|
160
|
+
bg: "#1a1b26",
|
|
161
|
+
bgInput: "#1f2335",
|
|
162
|
+
bgCode: "#16161e",
|
|
163
|
+
bgElev: "#24283b"
|
|
106
164
|
}
|
|
107
|
-
};
|
|
108
|
-
var
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (opts.thinking) {
|
|
140
|
-
payload.extra_body = { thinking: { type: opts.thinking } };
|
|
141
|
-
}
|
|
142
|
-
if (opts.reasoningEffort) {
|
|
143
|
-
payload.reasoning_effort = opts.reasoningEffort;
|
|
144
|
-
}
|
|
145
|
-
return payload;
|
|
146
|
-
}
|
|
147
|
-
/** Returns null on failure so callers can degrade — session must keep working without balance UI. */
|
|
148
|
-
async getBalance(opts = {}) {
|
|
149
|
-
try {
|
|
150
|
-
const resp = await this._fetch(`${this.baseUrl}/user/balance`, {
|
|
151
|
-
method: "GET",
|
|
152
|
-
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
153
|
-
signal: opts.signal
|
|
154
|
-
});
|
|
155
|
-
if (!resp.ok) return null;
|
|
156
|
-
const data = await resp.json();
|
|
157
|
-
if (!data || !Array.isArray(data.balance_infos)) return null;
|
|
158
|
-
return data;
|
|
159
|
-
} catch {
|
|
160
|
-
return null;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
/** Returns null on failure — callers fall back to a hardcoded model hint. */
|
|
164
|
-
async listModels(opts = {}) {
|
|
165
|
-
try {
|
|
166
|
-
const resp = await this._fetch(`${this.baseUrl}/models`, {
|
|
167
|
-
method: "GET",
|
|
168
|
-
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
169
|
-
signal: opts.signal
|
|
170
|
-
});
|
|
171
|
-
if (!resp.ok) return null;
|
|
172
|
-
const data = await resp.json();
|
|
173
|
-
if (!data || !Array.isArray(data.data)) return null;
|
|
174
|
-
return data;
|
|
175
|
-
} catch {
|
|
176
|
-
return null;
|
|
177
|
-
}
|
|
165
|
+
});
|
|
166
|
+
var githubLight = defineTheme({
|
|
167
|
+
fg: {
|
|
168
|
+
strong: "#1f2328",
|
|
169
|
+
body: "#24292f",
|
|
170
|
+
sub: "#57606a",
|
|
171
|
+
meta: "#6e7781",
|
|
172
|
+
faint: "#8c959f"
|
|
173
|
+
},
|
|
174
|
+
tone: {
|
|
175
|
+
brand: "#0969da",
|
|
176
|
+
accent: "#8250df",
|
|
177
|
+
violet: "#6639ba",
|
|
178
|
+
ok: "#1a7f37",
|
|
179
|
+
warn: "#9a6700",
|
|
180
|
+
err: "#cf222e",
|
|
181
|
+
info: "#0969da"
|
|
182
|
+
},
|
|
183
|
+
toneActive: {
|
|
184
|
+
brand: "#0550ae",
|
|
185
|
+
accent: "#6639ba",
|
|
186
|
+
violet: "#512a97",
|
|
187
|
+
ok: "#116329",
|
|
188
|
+
warn: "#7d4e00",
|
|
189
|
+
err: "#a40e26",
|
|
190
|
+
info: "#0550ae"
|
|
191
|
+
},
|
|
192
|
+
surface: {
|
|
193
|
+
bg: "#ffffff",
|
|
194
|
+
bgInput: "#f6f8fa",
|
|
195
|
+
bgCode: "#f6f8fa",
|
|
196
|
+
bgElev: "#eaeef2"
|
|
178
197
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
clearTimeout(timer);
|
|
212
|
-
}
|
|
198
|
+
});
|
|
199
|
+
var highContrast = defineTheme({
|
|
200
|
+
fg: {
|
|
201
|
+
strong: "#ffffff",
|
|
202
|
+
body: "#f5f5f5",
|
|
203
|
+
sub: "#d4d4d4",
|
|
204
|
+
meta: "#bdbdbd",
|
|
205
|
+
faint: "#8a8a8a"
|
|
206
|
+
},
|
|
207
|
+
tone: {
|
|
208
|
+
brand: "#00e5ff",
|
|
209
|
+
accent: "#ff4dff",
|
|
210
|
+
violet: "#b388ff",
|
|
211
|
+
ok: "#00ff66",
|
|
212
|
+
warn: "#ffdd00",
|
|
213
|
+
err: "#ff4d4d",
|
|
214
|
+
info: "#4da3ff"
|
|
215
|
+
},
|
|
216
|
+
toneActive: {
|
|
217
|
+
brand: "#80f2ff",
|
|
218
|
+
accent: "#ff99ff",
|
|
219
|
+
violet: "#d0b3ff",
|
|
220
|
+
ok: "#80ffb3",
|
|
221
|
+
warn: "#ffee80",
|
|
222
|
+
err: "#ff9999",
|
|
223
|
+
info: "#99c9ff"
|
|
224
|
+
},
|
|
225
|
+
surface: {
|
|
226
|
+
bg: "#000000",
|
|
227
|
+
bgInput: "#0a0a0a",
|
|
228
|
+
bgCode: "#050505",
|
|
229
|
+
bgElev: "#141414"
|
|
213
230
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
throw new Error(`DeepSeek ${resp.status}: ${await resp.text().catch(() => "")}`);
|
|
231
|
+
});
|
|
232
|
+
var THEMES = {
|
|
233
|
+
default: githubDark,
|
|
234
|
+
dark,
|
|
235
|
+
light,
|
|
236
|
+
"tokyo-night": tokyoNight,
|
|
237
|
+
"github-dark": githubDark,
|
|
238
|
+
"github-light": githubLight,
|
|
239
|
+
"high-contrast": highContrast
|
|
240
|
+
};
|
|
241
|
+
var DEFAULT_THEME_NAME = "default";
|
|
242
|
+
var DEFAULT_THEME = THEMES[DEFAULT_THEME_NAME];
|
|
243
|
+
var activeTheme = DEFAULT_THEME;
|
|
244
|
+
function proxyTokens(select) {
|
|
245
|
+
const target = select(DEFAULT_THEME);
|
|
246
|
+
return new Proxy(target, {
|
|
247
|
+
get(_target, prop) {
|
|
248
|
+
return select(activeTheme)[prop];
|
|
249
|
+
},
|
|
250
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
251
|
+
return Reflect.getOwnPropertyDescriptor(select(activeTheme), prop);
|
|
252
|
+
},
|
|
253
|
+
has(_target, prop) {
|
|
254
|
+
return prop in select(activeTheme);
|
|
255
|
+
},
|
|
256
|
+
ownKeys() {
|
|
257
|
+
return Reflect.ownKeys(select(activeTheme));
|
|
242
258
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
var FG = proxyTokens((theme) => theme.fg);
|
|
262
|
+
var TONE = proxyTokens((theme) => theme.tone);
|
|
263
|
+
var TONE_ACTIVE = proxyTokens((theme) => theme.toneActive);
|
|
264
|
+
var SURFACE = proxyTokens((theme) => theme.surface);
|
|
265
|
+
var CARD = proxyTokens((theme) => theme.card);
|
|
266
|
+
|
|
267
|
+
// src/index/config.ts
|
|
268
|
+
import picomatch from "picomatch";
|
|
269
|
+
var DEFAULT_INDEX_EXCLUDES = {
|
|
270
|
+
dirs: [
|
|
271
|
+
"node_modules",
|
|
272
|
+
".git",
|
|
273
|
+
".hg",
|
|
274
|
+
".svn",
|
|
275
|
+
"dist",
|
|
276
|
+
"build",
|
|
277
|
+
"out",
|
|
278
|
+
".next",
|
|
279
|
+
".nuxt",
|
|
280
|
+
"target",
|
|
281
|
+
".venv",
|
|
282
|
+
"venv",
|
|
283
|
+
"__pycache__",
|
|
284
|
+
".pytest_cache",
|
|
285
|
+
".mypy_cache",
|
|
286
|
+
".cache",
|
|
287
|
+
"coverage",
|
|
288
|
+
".turbo",
|
|
289
|
+
".vercel",
|
|
290
|
+
".reasonix"
|
|
291
|
+
],
|
|
292
|
+
files: [
|
|
293
|
+
"package-lock.json",
|
|
294
|
+
"yarn.lock",
|
|
295
|
+
"pnpm-lock.yaml",
|
|
296
|
+
"Cargo.lock",
|
|
297
|
+
"poetry.lock",
|
|
298
|
+
"Pipfile.lock",
|
|
299
|
+
"go.sum",
|
|
300
|
+
".DS_Store"
|
|
301
|
+
],
|
|
302
|
+
exts: [
|
|
303
|
+
".png",
|
|
304
|
+
".jpg",
|
|
305
|
+
".jpeg",
|
|
306
|
+
".gif",
|
|
307
|
+
".webp",
|
|
308
|
+
".bmp",
|
|
309
|
+
".ico",
|
|
310
|
+
".tiff",
|
|
311
|
+
".woff",
|
|
312
|
+
".woff2",
|
|
313
|
+
".ttf",
|
|
314
|
+
".otf",
|
|
315
|
+
".eot",
|
|
316
|
+
".zip",
|
|
317
|
+
".tar",
|
|
318
|
+
".gz",
|
|
319
|
+
".bz2",
|
|
320
|
+
".xz",
|
|
321
|
+
".rar",
|
|
322
|
+
".7z",
|
|
323
|
+
".exe",
|
|
324
|
+
".dll",
|
|
325
|
+
".so",
|
|
326
|
+
".dylib",
|
|
327
|
+
".bin",
|
|
328
|
+
".class",
|
|
329
|
+
".jar",
|
|
330
|
+
".war",
|
|
331
|
+
".wasm",
|
|
332
|
+
".o",
|
|
333
|
+
".obj",
|
|
334
|
+
".lib",
|
|
335
|
+
".a",
|
|
336
|
+
".pyc",
|
|
337
|
+
".pyo",
|
|
338
|
+
".mp3",
|
|
339
|
+
".mp4",
|
|
340
|
+
".wav",
|
|
341
|
+
".ogg",
|
|
342
|
+
".webm",
|
|
343
|
+
".mov",
|
|
344
|
+
".avi",
|
|
345
|
+
".pdf",
|
|
346
|
+
".sqlite",
|
|
347
|
+
".db"
|
|
348
|
+
]
|
|
349
|
+
};
|
|
350
|
+
var DEFAULT_MAX_FILE_BYTES = 256 * 1024;
|
|
351
|
+
|
|
352
|
+
// src/mcp/shell-split.ts
|
|
353
|
+
function shellSplit(input) {
|
|
354
|
+
const tokens = [];
|
|
355
|
+
let cur = "";
|
|
356
|
+
let quote = null;
|
|
357
|
+
let i = 0;
|
|
358
|
+
const s = input;
|
|
359
|
+
while (i < s.length) {
|
|
360
|
+
const ch = s[i];
|
|
361
|
+
if (quote) {
|
|
362
|
+
if (ch === quote) {
|
|
363
|
+
quote = null;
|
|
364
|
+
i++;
|
|
365
|
+
continue;
|
|
291
366
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
367
|
+
if (ch === "\\" && quote === '"' && i + 1 < s.length) {
|
|
368
|
+
cur += s[i + 1];
|
|
369
|
+
i += 2;
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
cur += ch;
|
|
373
|
+
i++;
|
|
374
|
+
continue;
|
|
296
375
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
var PauseGate = class {
|
|
302
|
-
_nextId = 0;
|
|
303
|
-
_pending = /* @__PURE__ */ new Map();
|
|
304
|
-
_listeners = /* @__PURE__ */ new Set();
|
|
305
|
-
_auditListener = null;
|
|
306
|
-
/** Block until the user responds. Takes a named options object so the
|
|
307
|
-
* kind and payload fields don't get confused at the call site. */
|
|
308
|
-
ask(opts) {
|
|
309
|
-
const { kind, payload } = opts;
|
|
310
|
-
if (this._listeners.size === 0) {
|
|
311
|
-
throw new Error(
|
|
312
|
-
`${kind}: no confirmation listener registered \u2014 cannot prompt the user. This tool can only be used inside an interactive Reasonix session.`
|
|
313
|
-
);
|
|
376
|
+
if (ch === '"' || ch === "'") {
|
|
377
|
+
quote = ch;
|
|
378
|
+
i++;
|
|
379
|
+
continue;
|
|
314
380
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
for (const fn of this._listeners) {
|
|
320
|
-
try {
|
|
321
|
-
fn(request);
|
|
322
|
-
} catch {
|
|
323
|
-
}
|
|
381
|
+
if (ch === " " || ch === " ") {
|
|
382
|
+
if (cur.length > 0) {
|
|
383
|
+
tokens.push(cur);
|
|
384
|
+
cur = "";
|
|
324
385
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
/** Resolve a pending request. Called by the App's modal callback. */
|
|
328
|
-
resolve(id, data) {
|
|
329
|
-
const p = this._pending.get(id);
|
|
330
|
-
if (!p) return;
|
|
331
|
-
this._pending.delete(id);
|
|
332
|
-
this.emitAuditEvent(p.request, data);
|
|
333
|
-
p.resolve(data);
|
|
334
|
-
}
|
|
335
|
-
/** Safe-cancel every outstanding request — frees stranded tool fns on Esc / /new. */
|
|
336
|
-
cancelAll() {
|
|
337
|
-
const ids = [...this._pending.keys()];
|
|
338
|
-
for (const id of ids) {
|
|
339
|
-
const p = this._pending.get(id);
|
|
340
|
-
if (!p) continue;
|
|
341
|
-
this._pending.delete(id);
|
|
342
|
-
p.resolve(safeCancelVerdict(p.request.kind));
|
|
386
|
+
i++;
|
|
387
|
+
continue;
|
|
343
388
|
}
|
|
389
|
+
cur += ch;
|
|
390
|
+
i++;
|
|
344
391
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
this._pending.delete(id);
|
|
350
|
-
p.resolve(safeCancelVerdict(p.request.kind));
|
|
351
|
-
return true;
|
|
392
|
+
if (quote) {
|
|
393
|
+
throw new Error(
|
|
394
|
+
`shellSplit: unterminated ${quote === '"' ? "double" : "single"} quote in input`
|
|
395
|
+
);
|
|
352
396
|
}
|
|
353
|
-
|
|
354
|
-
|
|
397
|
+
if (cur.length > 0) tokens.push(cur);
|
|
398
|
+
return tokens;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// src/mcp/spec.ts
|
|
402
|
+
var NAME_PREFIX = /^([a-zA-Z_][a-zA-Z0-9_-]*)=(.*)$/;
|
|
403
|
+
var HTTP_URL = /^https?:\/\//i;
|
|
404
|
+
var STREAMABLE_PREFIX = /^streamable\+(https?:\/\/.+)$/i;
|
|
405
|
+
function parseMcpSpec(input) {
|
|
406
|
+
const trimmed = input.trim();
|
|
407
|
+
if (!trimmed) {
|
|
408
|
+
throw new Error("empty MCP spec");
|
|
355
409
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
};
|
|
410
|
+
const nameMatch = NAME_PREFIX.exec(trimmed);
|
|
411
|
+
const name = nameMatch ? nameMatch[1] : null;
|
|
412
|
+
const body = (nameMatch ? nameMatch[2] : trimmed).trim();
|
|
413
|
+
if (!body) {
|
|
414
|
+
throw new Error(`MCP spec has name but no command: ${input}`);
|
|
362
415
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
return null;
|
|
416
|
+
const streamMatch = STREAMABLE_PREFIX.exec(body);
|
|
417
|
+
if (streamMatch) {
|
|
418
|
+
return { transport: "streamable-http", name, url: streamMatch[1] };
|
|
367
419
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
if (request.kind !== "run_command" && request.kind !== "run_background") return;
|
|
371
|
-
if (!data || typeof data !== "object") return;
|
|
372
|
-
const choice = data;
|
|
373
|
-
try {
|
|
374
|
-
switch (choice.type) {
|
|
375
|
-
case "run_once":
|
|
376
|
-
this._auditListener({
|
|
377
|
-
type: "tool.confirm.allow",
|
|
378
|
-
kind: request.kind,
|
|
379
|
-
payload: request.payload
|
|
380
|
-
});
|
|
381
|
-
break;
|
|
382
|
-
case "deny":
|
|
383
|
-
this._auditListener({
|
|
384
|
-
type: "tool.confirm.deny",
|
|
385
|
-
kind: request.kind,
|
|
386
|
-
payload: request.payload,
|
|
387
|
-
denyContext: choice.denyContext
|
|
388
|
-
});
|
|
389
|
-
break;
|
|
390
|
-
case "always_allow":
|
|
391
|
-
if (typeof choice.prefix !== "string") return;
|
|
392
|
-
this._auditListener({
|
|
393
|
-
type: "tool.confirm.always_allow",
|
|
394
|
-
kind: request.kind,
|
|
395
|
-
payload: request.payload,
|
|
396
|
-
prefix: choice.prefix
|
|
397
|
-
});
|
|
398
|
-
break;
|
|
399
|
-
default:
|
|
400
|
-
break;
|
|
401
|
-
}
|
|
402
|
-
} catch {
|
|
403
|
-
}
|
|
420
|
+
if (HTTP_URL.test(body)) {
|
|
421
|
+
return { transport: "sse", name, url: body };
|
|
404
422
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
case "run_command":
|
|
409
|
-
case "run_background":
|
|
410
|
-
case "path_access":
|
|
411
|
-
return { type: "deny" };
|
|
412
|
-
case "plan_proposed":
|
|
413
|
-
return { type: "cancel" };
|
|
414
|
-
case "plan_checkpoint":
|
|
415
|
-
return { type: "stop" };
|
|
416
|
-
case "plan_revision":
|
|
417
|
-
return { type: "cancelled" };
|
|
418
|
-
case "choice":
|
|
419
|
-
return { type: "cancel" };
|
|
423
|
+
const argv = shellSplit(body);
|
|
424
|
+
if (argv.length === 0) {
|
|
425
|
+
throw new Error(`MCP spec has name but no command: ${input}`);
|
|
420
426
|
}
|
|
427
|
+
const [command, ...args] = argv;
|
|
428
|
+
return { transport: "stdio", name, command, args };
|
|
421
429
|
}
|
|
422
|
-
var pauseGate = new PauseGate();
|
|
423
|
-
|
|
424
|
-
// src/hooks.ts
|
|
425
|
-
import { spawn } from "child_process";
|
|
426
|
-
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
427
|
-
import { homedir as homedir2 } from "os";
|
|
428
|
-
import { join as join2 } from "path";
|
|
429
430
|
|
|
430
431
|
// src/config.ts
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
return { ...base, card: card(base.fg, base.tone) };
|
|
432
|
+
var BUILTIN_TYPE_DOCS = {
|
|
433
|
+
user: "role / skills / preferences",
|
|
434
|
+
feedback: "corrections or confirmed approaches",
|
|
435
|
+
project: "facts / decisions about the current work",
|
|
436
|
+
reference: "pointers to external systems the user uses"
|
|
437
|
+
};
|
|
438
|
+
function loadMemoryTypeRegistry(cfg = readConfig()) {
|
|
439
|
+
const out = [];
|
|
440
|
+
for (const name of ["user", "feedback", "project", "reference"]) {
|
|
441
|
+
out.push({ name, builtin: true, description: BUILTIN_TYPE_DOCS[name] });
|
|
442
|
+
}
|
|
443
|
+
const seen = new Set(out.map((e) => e.name));
|
|
444
|
+
for (const raw of cfg.memory?.customTypes ?? []) {
|
|
445
|
+
if (!raw || typeof raw.name !== "string") continue;
|
|
446
|
+
const name = raw.name.trim();
|
|
447
|
+
if (!name || !/^[a-zA-Z][a-zA-Z0-9_-]{0,31}$/.test(name)) continue;
|
|
448
|
+
if (seen.has(name)) continue;
|
|
449
|
+
seen.add(name);
|
|
450
|
+
const entry = { name, builtin: false };
|
|
451
|
+
if (typeof raw.description === "string") entry.description = raw.description;
|
|
452
|
+
if (raw.priority === "low" || raw.priority === "medium" || raw.priority === "high") {
|
|
453
|
+
entry.priority = raw.priority;
|
|
454
|
+
}
|
|
455
|
+
if (raw.expires === "project_end") entry.expires = raw.expires;
|
|
456
|
+
out.push(entry);
|
|
457
|
+
}
|
|
458
|
+
return out;
|
|
459
459
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
bgElev: "#11141a"
|
|
460
|
+
function memoryTypeDefaults(typeName, cfg = readConfig()) {
|
|
461
|
+
const found = loadMemoryTypeRegistry(cfg).find((e) => e.name === typeName);
|
|
462
|
+
if (!found) return {};
|
|
463
|
+
const out = {};
|
|
464
|
+
if (found.priority) out.priority = found.priority;
|
|
465
|
+
if (found.expires) out.expires = found.expires;
|
|
466
|
+
return out;
|
|
467
|
+
}
|
|
468
|
+
var DEFAULT_METASO_API_KEY = "mk-E384C1DD5E8501BB7EFE27C949AFDE5B";
|
|
469
|
+
function loadMetasoApiKey(path2 = defaultConfigPath()) {
|
|
470
|
+
if (process.env.METASO_API_KEY) return process.env.METASO_API_KEY;
|
|
471
|
+
const cfg = readConfig(path2).metasoApiKey;
|
|
472
|
+
if (cfg && typeof cfg === "string" && cfg.trim()) return cfg.trim();
|
|
473
|
+
return DEFAULT_METASO_API_KEY;
|
|
474
|
+
}
|
|
475
|
+
function loadTavilyApiKey(path2 = defaultConfigPath()) {
|
|
476
|
+
if (process.env.TAVILY_API_KEY) return process.env.TAVILY_API_KEY.trim();
|
|
477
|
+
const cfg = readConfig(path2).tavilyApiKey;
|
|
478
|
+
if (cfg && typeof cfg === "string" && cfg.trim()) return cfg.trim();
|
|
479
|
+
return void 0;
|
|
480
|
+
}
|
|
481
|
+
function defaultConfigPath() {
|
|
482
|
+
return join(homedir(), ".reasonix", "config.json");
|
|
483
|
+
}
|
|
484
|
+
function readConfig(path2 = defaultConfigPath()) {
|
|
485
|
+
try {
|
|
486
|
+
const raw = readFileSync(path2, "utf8");
|
|
487
|
+
const parsed = JSON.parse(raw);
|
|
488
|
+
if (parsed && typeof parsed === "object") return parsed;
|
|
489
|
+
} catch {
|
|
491
490
|
}
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
},
|
|
501
|
-
tone: {
|
|
502
|
-
brand: "#7dd3fc",
|
|
503
|
-
accent: "#c084fc",
|
|
504
|
-
violet: "#a78bfa",
|
|
505
|
-
ok: "#86efac",
|
|
506
|
-
warn: "#fbbf24",
|
|
507
|
-
err: "#f87171",
|
|
508
|
-
info: "#60a5fa"
|
|
509
|
-
},
|
|
510
|
-
toneActive: {
|
|
511
|
-
brand: "#bae6fd",
|
|
512
|
-
accent: "#e9d5ff",
|
|
513
|
-
violet: "#ddd6fe",
|
|
514
|
-
ok: "#bbf7d0",
|
|
515
|
-
warn: "#fde68a",
|
|
516
|
-
err: "#fecaca",
|
|
517
|
-
info: "#bfdbfe"
|
|
518
|
-
},
|
|
519
|
-
surface: {
|
|
520
|
-
bg: "#0b1020",
|
|
521
|
-
bgInput: "#111827",
|
|
522
|
-
bgCode: "#080c16",
|
|
523
|
-
bgElev: "#151d2f"
|
|
491
|
+
return {};
|
|
492
|
+
}
|
|
493
|
+
function writeConfig(cfg, path2 = defaultConfigPath()) {
|
|
494
|
+
mkdirSync(dirname(path2), { recursive: true });
|
|
495
|
+
writeFileSync(path2, JSON.stringify(cfg, null, 2), "utf8");
|
|
496
|
+
try {
|
|
497
|
+
chmodSync(path2, 384);
|
|
498
|
+
} catch {
|
|
524
499
|
}
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
},
|
|
552
|
-
surface: {
|
|
553
|
-
bg: "#ffffff",
|
|
554
|
-
bgInput: "#f8fafc",
|
|
555
|
-
bgCode: "#f3f4f6",
|
|
556
|
-
bgElev: "#eef2f7"
|
|
500
|
+
}
|
|
501
|
+
function loadLanguage(path2 = defaultConfigPath()) {
|
|
502
|
+
return readConfig(path2).lang;
|
|
503
|
+
}
|
|
504
|
+
function loadApiKey(path2 = defaultConfigPath()) {
|
|
505
|
+
if (process.env.DEEPSEEK_API_KEY) return process.env.DEEPSEEK_API_KEY;
|
|
506
|
+
return readConfig(path2).apiKey;
|
|
507
|
+
}
|
|
508
|
+
function loadBaseUrl(path2 = defaultConfigPath()) {
|
|
509
|
+
if (process.env.DEEPSEEK_BASE_URL) return process.env.DEEPSEEK_BASE_URL;
|
|
510
|
+
return readConfig(path2).baseUrl;
|
|
511
|
+
}
|
|
512
|
+
function isNonNegativeNumber(value) {
|
|
513
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
514
|
+
}
|
|
515
|
+
function loadPricingOverride(path2 = defaultConfigPath()) {
|
|
516
|
+
const raw = readConfig(path2).pricingOverride;
|
|
517
|
+
if (!isPlainObject(raw)) return {};
|
|
518
|
+
const result = {};
|
|
519
|
+
for (const [model, value] of Object.entries(raw)) {
|
|
520
|
+
if (!isPlainObject(value)) continue;
|
|
521
|
+
const pricing = {};
|
|
522
|
+
if (isNonNegativeNumber(value.inputCacheHit)) pricing.inputCacheHit = value.inputCacheHit;
|
|
523
|
+
if (isNonNegativeNumber(value.inputCacheMiss)) pricing.inputCacheMiss = value.inputCacheMiss;
|
|
524
|
+
if (isNonNegativeNumber(value.output)) pricing.output = value.output;
|
|
525
|
+
if (Object.keys(pricing).length > 0) result[model] = pricing;
|
|
557
526
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
warn: "#e0af68",
|
|
573
|
-
err: "#f7768e",
|
|
574
|
-
info: "#2ac3de"
|
|
575
|
-
},
|
|
576
|
-
toneActive: {
|
|
577
|
-
brand: "#a9c7ff",
|
|
578
|
-
accent: "#d7b9ff",
|
|
579
|
-
violet: "#c6a0f6",
|
|
580
|
-
ok: "#b9f27c",
|
|
581
|
-
warn: "#ffd089",
|
|
582
|
-
err: "#ff9cac",
|
|
583
|
-
info: "#7dcfff"
|
|
584
|
-
},
|
|
585
|
-
surface: {
|
|
586
|
-
bg: "#1a1b26",
|
|
587
|
-
bgInput: "#1f2335",
|
|
588
|
-
bgCode: "#16161e",
|
|
589
|
-
bgElev: "#24283b"
|
|
527
|
+
return result;
|
|
528
|
+
}
|
|
529
|
+
function loadRateLimit(path2 = defaultConfigPath()) {
|
|
530
|
+
const rpm = readConfig(path2).rateLimit?.rpm;
|
|
531
|
+
if (typeof rpm !== "number" || !Number.isInteger(rpm) || rpm <= 0) return void 0;
|
|
532
|
+
return { rpm };
|
|
533
|
+
}
|
|
534
|
+
function saveBaseUrl(url, path2 = defaultConfigPath()) {
|
|
535
|
+
const cfg = readConfig(path2);
|
|
536
|
+
const trimmed = url.trim();
|
|
537
|
+
if (trimmed) {
|
|
538
|
+
cfg.baseUrl = trimmed;
|
|
539
|
+
} else {
|
|
540
|
+
cfg.baseUrl = void 0;
|
|
590
541
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
toneActive: {
|
|
610
|
-
brand: "#0550ae",
|
|
611
|
-
accent: "#6639ba",
|
|
612
|
-
violet: "#512a97",
|
|
613
|
-
ok: "#116329",
|
|
614
|
-
warn: "#7d4e00",
|
|
615
|
-
err: "#a40e26",
|
|
616
|
-
info: "#0550ae"
|
|
617
|
-
},
|
|
618
|
-
surface: {
|
|
619
|
-
bg: "#ffffff",
|
|
620
|
-
bgInput: "#f6f8fa",
|
|
621
|
-
bgCode: "#f6f8fa",
|
|
622
|
-
bgElev: "#eaeef2"
|
|
542
|
+
writeConfig(cfg, path2);
|
|
543
|
+
}
|
|
544
|
+
function resolveSkillPath(raw, baseDir) {
|
|
545
|
+
const homeExpanded = expandCurrentUserHome(raw.trim());
|
|
546
|
+
return resolve(isAbsolute(homeExpanded) ? homeExpanded : join(baseDir, homeExpanded));
|
|
547
|
+
}
|
|
548
|
+
function normalizeSkillPathEntries(paths, baseDir) {
|
|
549
|
+
const out = [];
|
|
550
|
+
const seen = /* @__PURE__ */ new Set();
|
|
551
|
+
for (const value of paths) {
|
|
552
|
+
if (typeof value !== "string") continue;
|
|
553
|
+
const raw = value.trim();
|
|
554
|
+
if (!raw) continue;
|
|
555
|
+
const resolved = resolveSkillPath(raw, baseDir);
|
|
556
|
+
const key = skillPathKey(resolved);
|
|
557
|
+
if (seen.has(key)) continue;
|
|
558
|
+
seen.add(key);
|
|
559
|
+
out.push({ raw, resolved });
|
|
623
560
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
561
|
+
return out;
|
|
562
|
+
}
|
|
563
|
+
function resolveSkillPaths(paths, baseDir) {
|
|
564
|
+
return normalizeSkillPathEntries(paths, baseDir).map((entry) => entry.resolved);
|
|
565
|
+
}
|
|
566
|
+
function skillPathKey(path2) {
|
|
567
|
+
return process.platform === "win32" ? path2.toLowerCase() : path2;
|
|
568
|
+
}
|
|
569
|
+
function expandCurrentUserHome(path2) {
|
|
570
|
+
if (path2 === "~") return homedir();
|
|
571
|
+
if (path2.startsWith("~/") || path2.startsWith("~\\")) return join(homedir(), path2.slice(2));
|
|
572
|
+
return path2;
|
|
573
|
+
}
|
|
574
|
+
function loadResolvedSkillPaths(baseDir = process.cwd(), path2 = defaultConfigPath()) {
|
|
575
|
+
const raw = readConfig(path2).skills?.paths;
|
|
576
|
+
return Array.isArray(raw) ? resolveSkillPaths(raw, baseDir) : [];
|
|
577
|
+
}
|
|
578
|
+
function webSearchEngine(path2 = defaultConfigPath()) {
|
|
579
|
+
const cfg = readConfig(path2).webSearchEngine;
|
|
580
|
+
if (cfg === "searxng") return "searxng";
|
|
581
|
+
if (cfg === "metaso") return "metaso";
|
|
582
|
+
return "mojeek";
|
|
583
|
+
}
|
|
584
|
+
function webSearchEndpoint(path2 = defaultConfigPath()) {
|
|
585
|
+
const cfg = readConfig(path2).webSearchEndpoint;
|
|
586
|
+
if (cfg && typeof cfg === "string") return cfg;
|
|
587
|
+
return "http://localhost:8080";
|
|
588
|
+
}
|
|
589
|
+
function saveApiKey(key, path2 = defaultConfigPath()) {
|
|
590
|
+
const cfg = readConfig(path2);
|
|
591
|
+
cfg.apiKey = key.trim();
|
|
592
|
+
writeConfig(cfg, path2);
|
|
593
|
+
}
|
|
594
|
+
function findProjectKey(cfg, rootDir) {
|
|
595
|
+
const projects = cfg.projects;
|
|
596
|
+
if (!projects) return void 0;
|
|
597
|
+
if (Object.hasOwn(projects, rootDir)) return rootDir;
|
|
598
|
+
if (process.platform !== "win32") return void 0;
|
|
599
|
+
const lower = rootDir.toLowerCase();
|
|
600
|
+
for (const k of Object.keys(projects)) {
|
|
601
|
+
if (k.toLowerCase() === lower) return k;
|
|
656
602
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
603
|
+
return void 0;
|
|
604
|
+
}
|
|
605
|
+
function addProjectShellAllowed(rootDir, prefix, path2 = defaultConfigPath()) {
|
|
606
|
+
const trimmed = prefix.trim();
|
|
607
|
+
if (!trimmed) return;
|
|
608
|
+
const cfg = readConfig(path2);
|
|
609
|
+
if (!cfg.projects) cfg.projects = {};
|
|
610
|
+
const key = findProjectKey(cfg, rootDir) ?? rootDir;
|
|
611
|
+
if (!cfg.projects[key]) cfg.projects[key] = {};
|
|
612
|
+
const existing = cfg.projects[key].shellAllowed ?? [];
|
|
613
|
+
if (existing.includes(trimmed)) return;
|
|
614
|
+
cfg.projects[key].shellAllowed = [...existing, trimmed];
|
|
615
|
+
writeConfig(cfg, path2);
|
|
616
|
+
}
|
|
617
|
+
function loadProjectPathAllowed(rootDir, path2 = defaultConfigPath()) {
|
|
618
|
+
const cfg = readConfig(path2);
|
|
619
|
+
const key = findProjectKey(cfg, rootDir);
|
|
620
|
+
if (key === void 0) return [];
|
|
621
|
+
return cfg.projects?.[key]?.pathAllowed ?? [];
|
|
622
|
+
}
|
|
623
|
+
function addProjectPathAllowed(rootDir, prefix, path2 = defaultConfigPath()) {
|
|
624
|
+
const trimmed = prefix.trim();
|
|
625
|
+
if (!trimmed) return;
|
|
626
|
+
const cfg = readConfig(path2);
|
|
627
|
+
if (!cfg.projects) cfg.projects = {};
|
|
628
|
+
const key = findProjectKey(cfg, rootDir) ?? rootDir;
|
|
629
|
+
if (!cfg.projects[key]) cfg.projects[key] = {};
|
|
630
|
+
const existing = cfg.projects[key].pathAllowed ?? [];
|
|
631
|
+
if (existing.includes(trimmed)) return;
|
|
632
|
+
cfg.projects[key].pathAllowed = [...existing, trimmed];
|
|
633
|
+
writeConfig(cfg, path2);
|
|
634
|
+
}
|
|
635
|
+
function isPlausibleKey(key) {
|
|
636
|
+
const trimmed = key.trim();
|
|
637
|
+
if (trimmed.length < 16) return false;
|
|
638
|
+
return !/\s/.test(trimmed);
|
|
639
|
+
}
|
|
640
|
+
function redactKey(key) {
|
|
641
|
+
if (!key) return "";
|
|
642
|
+
if (key.length <= 12) return "****";
|
|
643
|
+
return `${key.slice(0, 6)}\u2026${key.slice(-4)}`;
|
|
644
|
+
}
|
|
645
|
+
function isPlainObject(value) {
|
|
646
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
647
|
+
const proto = Object.getPrototypeOf(value);
|
|
648
|
+
return proto === Object.prototype || proto === null;
|
|
686
649
|
}
|
|
687
|
-
var FG = proxyTokens((theme) => theme.fg);
|
|
688
|
-
var TONE = proxyTokens((theme) => theme.tone);
|
|
689
|
-
var TONE_ACTIVE = proxyTokens((theme) => theme.toneActive);
|
|
690
|
-
var SURFACE = proxyTokens((theme) => theme.surface);
|
|
691
|
-
var CARD = proxyTokens((theme) => theme.card);
|
|
692
|
-
|
|
693
|
-
// src/index/config.ts
|
|
694
|
-
import picomatch from "picomatch";
|
|
695
|
-
var DEFAULT_INDEX_EXCLUDES = {
|
|
696
|
-
dirs: [
|
|
697
|
-
"node_modules",
|
|
698
|
-
".git",
|
|
699
|
-
".hg",
|
|
700
|
-
".svn",
|
|
701
|
-
"dist",
|
|
702
|
-
"build",
|
|
703
|
-
"out",
|
|
704
|
-
".next",
|
|
705
|
-
".nuxt",
|
|
706
|
-
"target",
|
|
707
|
-
".venv",
|
|
708
|
-
"venv",
|
|
709
|
-
"__pycache__",
|
|
710
|
-
".pytest_cache",
|
|
711
|
-
".mypy_cache",
|
|
712
|
-
".cache",
|
|
713
|
-
"coverage",
|
|
714
|
-
".turbo",
|
|
715
|
-
".vercel",
|
|
716
|
-
".reasonix"
|
|
717
|
-
],
|
|
718
|
-
files: [
|
|
719
|
-
"package-lock.json",
|
|
720
|
-
"yarn.lock",
|
|
721
|
-
"pnpm-lock.yaml",
|
|
722
|
-
"Cargo.lock",
|
|
723
|
-
"poetry.lock",
|
|
724
|
-
"Pipfile.lock",
|
|
725
|
-
"go.sum",
|
|
726
|
-
".DS_Store"
|
|
727
|
-
],
|
|
728
|
-
exts: [
|
|
729
|
-
".png",
|
|
730
|
-
".jpg",
|
|
731
|
-
".jpeg",
|
|
732
|
-
".gif",
|
|
733
|
-
".webp",
|
|
734
|
-
".bmp",
|
|
735
|
-
".ico",
|
|
736
|
-
".tiff",
|
|
737
|
-
".woff",
|
|
738
|
-
".woff2",
|
|
739
|
-
".ttf",
|
|
740
|
-
".otf",
|
|
741
|
-
".eot",
|
|
742
|
-
".zip",
|
|
743
|
-
".tar",
|
|
744
|
-
".gz",
|
|
745
|
-
".bz2",
|
|
746
|
-
".xz",
|
|
747
|
-
".rar",
|
|
748
|
-
".7z",
|
|
749
|
-
".exe",
|
|
750
|
-
".dll",
|
|
751
|
-
".so",
|
|
752
|
-
".dylib",
|
|
753
|
-
".bin",
|
|
754
|
-
".class",
|
|
755
|
-
".jar",
|
|
756
|
-
".war",
|
|
757
|
-
".wasm",
|
|
758
|
-
".o",
|
|
759
|
-
".obj",
|
|
760
|
-
".lib",
|
|
761
|
-
".a",
|
|
762
|
-
".pyc",
|
|
763
|
-
".pyo",
|
|
764
|
-
".mp3",
|
|
765
|
-
".mp4",
|
|
766
|
-
".wav",
|
|
767
|
-
".ogg",
|
|
768
|
-
".webm",
|
|
769
|
-
".mov",
|
|
770
|
-
".avi",
|
|
771
|
-
".pdf",
|
|
772
|
-
".sqlite",
|
|
773
|
-
".db"
|
|
774
|
-
]
|
|
775
|
-
};
|
|
776
|
-
var DEFAULT_MAX_FILE_BYTES = 256 * 1024;
|
|
777
650
|
|
|
778
|
-
// src/
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
const
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
if (
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
651
|
+
// src/retry.ts
|
|
652
|
+
var DEFAULT_RETRYABLE_STATUSES = [408, 429, 500, 502, 503, 504];
|
|
653
|
+
async function fetchWithRetry(fetchFn, url, init, opts = {}) {
|
|
654
|
+
const maxAttempts = opts.maxAttempts ?? 4;
|
|
655
|
+
const initial = opts.initialBackoffMs ?? 500;
|
|
656
|
+
const cap = opts.maxBackoffMs ?? 1e4;
|
|
657
|
+
const retryable = new Set(opts.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES);
|
|
658
|
+
let lastError;
|
|
659
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
660
|
+
if (opts.signal?.aborted) throw new Error("aborted");
|
|
661
|
+
try {
|
|
662
|
+
const resp = await fetchFn(url, init);
|
|
663
|
+
if (resp.ok || !retryable.has(resp.status)) return resp;
|
|
664
|
+
if (attempt === maxAttempts - 1) return resp;
|
|
665
|
+
await resp.text().catch(() => void 0);
|
|
666
|
+
const waitMs = computeWait(attempt, initial, cap, resp.headers.get("Retry-After"));
|
|
667
|
+
opts.onRetry?.({ attempt: attempt + 1, reason: `http ${resp.status}`, waitMs });
|
|
668
|
+
await sleep(waitMs, opts.signal);
|
|
669
|
+
} catch (err) {
|
|
670
|
+
lastError = err;
|
|
671
|
+
if (isAbortError(err) || opts.signal?.aborted) throw err;
|
|
672
|
+
if (attempt === maxAttempts - 1) throw err;
|
|
673
|
+
const waitMs = computeWait(attempt, initial, cap, null);
|
|
674
|
+
opts.onRetry?.({
|
|
675
|
+
attempt: attempt + 1,
|
|
676
|
+
reason: `network: ${messageOf(err)}`,
|
|
677
|
+
waitMs
|
|
678
|
+
});
|
|
679
|
+
await sleep(waitMs, opts.signal);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
throw lastError ?? new Error("fetchWithRetry: loop exited unexpectedly");
|
|
683
|
+
}
|
|
684
|
+
function computeWait(attempt, initial, cap, retryAfter) {
|
|
685
|
+
if (retryAfter) {
|
|
686
|
+
const seconds = Number.parseFloat(retryAfter);
|
|
687
|
+
if (Number.isFinite(seconds) && seconds > 0) {
|
|
688
|
+
return Math.min(seconds * 1e3, cap);
|
|
814
689
|
}
|
|
815
|
-
cur += ch;
|
|
816
|
-
i++;
|
|
817
690
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
691
|
+
const exp = initial * 2 ** attempt;
|
|
692
|
+
const jitter = exp * (0.75 + Math.random() * 0.5);
|
|
693
|
+
return Math.min(Math.max(jitter, 0), cap);
|
|
694
|
+
}
|
|
695
|
+
function sleep(ms, signal) {
|
|
696
|
+
if (ms <= 0) return Promise.resolve();
|
|
697
|
+
return new Promise((resolve13, reject) => {
|
|
698
|
+
const timer = setTimeout(resolve13, ms);
|
|
699
|
+
if (signal) {
|
|
700
|
+
const onAbort = () => {
|
|
701
|
+
clearTimeout(timer);
|
|
702
|
+
reject(new Error("aborted"));
|
|
703
|
+
};
|
|
704
|
+
if (signal.aborted) onAbort();
|
|
705
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
function isAbortError(err) {
|
|
710
|
+
if (!err || typeof err !== "object") return false;
|
|
711
|
+
const name = err.name;
|
|
712
|
+
return name === "AbortError";
|
|
713
|
+
}
|
|
714
|
+
function messageOf(err) {
|
|
715
|
+
if (err instanceof Error) return err.message;
|
|
716
|
+
try {
|
|
717
|
+
return String(err);
|
|
718
|
+
} catch {
|
|
719
|
+
return "unknown error";
|
|
822
720
|
}
|
|
823
|
-
if (cur.length > 0) tokens.push(cur);
|
|
824
|
-
return tokens;
|
|
825
721
|
}
|
|
826
722
|
|
|
827
|
-
// src/
|
|
828
|
-
var
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
723
|
+
// src/client.ts
|
|
724
|
+
var Usage = class _Usage {
|
|
725
|
+
constructor(promptTokens = 0, completionTokens = 0, totalTokens = 0, promptCacheHitTokens = 0, promptCacheMissTokens = 0) {
|
|
726
|
+
this.promptTokens = promptTokens;
|
|
727
|
+
this.completionTokens = completionTokens;
|
|
728
|
+
this.totalTokens = totalTokens;
|
|
729
|
+
this.promptCacheHitTokens = promptCacheHitTokens;
|
|
730
|
+
this.promptCacheMissTokens = promptCacheMissTokens;
|
|
835
731
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
732
|
+
promptTokens;
|
|
733
|
+
completionTokens;
|
|
734
|
+
totalTokens;
|
|
735
|
+
promptCacheHitTokens;
|
|
736
|
+
promptCacheMissTokens;
|
|
737
|
+
get cacheHitRatio() {
|
|
738
|
+
const denom = this.promptCacheHitTokens + this.promptCacheMissTokens;
|
|
739
|
+
return denom > 0 ? this.promptCacheHitTokens / denom : 0;
|
|
841
740
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
741
|
+
static fromApi(raw) {
|
|
742
|
+
const u = raw ?? {};
|
|
743
|
+
const promptTokens = u.prompt_tokens ?? 0;
|
|
744
|
+
const cacheHitTokens = u.prompt_cache_hit_tokens ?? 0;
|
|
745
|
+
const cacheMissTokens = u.prompt_cache_miss_tokens ?? Math.max(0, promptTokens - cacheHitTokens);
|
|
746
|
+
return new _Usage(
|
|
747
|
+
promptTokens,
|
|
748
|
+
u.completion_tokens ?? 0,
|
|
749
|
+
u.total_tokens ?? 0,
|
|
750
|
+
cacheHitTokens,
|
|
751
|
+
cacheMissTokens
|
|
752
|
+
);
|
|
845
753
|
}
|
|
846
|
-
|
|
847
|
-
|
|
754
|
+
};
|
|
755
|
+
var DeepSeekClient = class {
|
|
756
|
+
apiKey;
|
|
757
|
+
baseUrl;
|
|
758
|
+
timeoutMs;
|
|
759
|
+
retry;
|
|
760
|
+
_fetch;
|
|
761
|
+
minChatIntervalMs;
|
|
762
|
+
nextChatRequestAt = 0;
|
|
763
|
+
constructor(opts = {}) {
|
|
764
|
+
const apiKey = opts.apiKey ?? process.env.DEEPSEEK_API_KEY;
|
|
765
|
+
if (!apiKey) {
|
|
766
|
+
throw new Error(
|
|
767
|
+
"DEEPSEEK_API_KEY is not set. Put it in .env or pass apiKey to DeepSeekClient."
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
this.apiKey = apiKey;
|
|
771
|
+
let url = opts.baseUrl ?? process.env.DEEPSEEK_BASE_URL ?? "https://api.deepseek.com";
|
|
772
|
+
while (url.endsWith("/")) url = url.slice(0, -1);
|
|
773
|
+
this.baseUrl = url;
|
|
774
|
+
this.timeoutMs = opts.timeoutMs ?? 66e4;
|
|
775
|
+
this._fetch = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
776
|
+
this.retry = opts.retry ?? {};
|
|
777
|
+
const rpm = opts.rateLimit?.rpm ?? loadRateLimit()?.rpm;
|
|
778
|
+
this.minChatIntervalMs = rpm ? Math.ceil(6e4 / rpm) : 0;
|
|
848
779
|
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
780
|
+
async waitForChatRateLimit(signal) {
|
|
781
|
+
if (this.minChatIntervalMs <= 0) return;
|
|
782
|
+
const now = Date.now();
|
|
783
|
+
const waitMs = Math.max(0, this.nextChatRequestAt - now);
|
|
784
|
+
this.nextChatRequestAt = Math.max(now, this.nextChatRequestAt) + this.minChatIntervalMs;
|
|
785
|
+
if (waitMs <= 0) return;
|
|
786
|
+
await new Promise((resolve13, reject) => {
|
|
787
|
+
const timer = setTimeout(resolve13, waitMs);
|
|
788
|
+
signal?.addEventListener(
|
|
789
|
+
"abort",
|
|
790
|
+
() => {
|
|
791
|
+
clearTimeout(timer);
|
|
792
|
+
reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
793
|
+
},
|
|
794
|
+
{ once: true }
|
|
795
|
+
);
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
buildPayload(opts, stream) {
|
|
799
|
+
const payload = {
|
|
800
|
+
model: opts.model,
|
|
801
|
+
messages: opts.messages,
|
|
802
|
+
stream
|
|
803
|
+
};
|
|
804
|
+
if (opts.tools?.length) payload.tools = opts.tools;
|
|
805
|
+
if (opts.temperature !== void 0) payload.temperature = opts.temperature;
|
|
806
|
+
if (opts.maxTokens !== void 0) payload.max_tokens = opts.maxTokens;
|
|
807
|
+
if (opts.responseFormat) payload.response_format = opts.responseFormat;
|
|
808
|
+
if (opts.thinking) {
|
|
809
|
+
payload.extra_body = { thinking: { type: opts.thinking } };
|
|
810
|
+
}
|
|
811
|
+
if (opts.reasoningEffort) {
|
|
812
|
+
payload.reasoning_effort = opts.reasoningEffort;
|
|
813
|
+
}
|
|
814
|
+
return payload;
|
|
815
|
+
}
|
|
816
|
+
/** Returns null on failure so callers can degrade — session must keep working without balance UI. */
|
|
817
|
+
async getBalance(opts = {}) {
|
|
818
|
+
try {
|
|
819
|
+
const resp = await this._fetch(`${this.baseUrl}/user/balance`, {
|
|
820
|
+
method: "GET",
|
|
821
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
822
|
+
signal: opts.signal
|
|
823
|
+
});
|
|
824
|
+
if (!resp.ok) return null;
|
|
825
|
+
const data = await resp.json();
|
|
826
|
+
if (!data || !Array.isArray(data.balance_infos)) return null;
|
|
827
|
+
return data;
|
|
828
|
+
} catch {
|
|
829
|
+
return null;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
/** Returns null on failure — callers fall back to a hardcoded model hint. */
|
|
833
|
+
async listModels(opts = {}) {
|
|
834
|
+
try {
|
|
835
|
+
const resp = await this._fetch(`${this.baseUrl}/models`, {
|
|
836
|
+
method: "GET",
|
|
837
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
838
|
+
signal: opts.signal
|
|
839
|
+
});
|
|
840
|
+
if (!resp.ok) return null;
|
|
841
|
+
const data = await resp.json();
|
|
842
|
+
if (!data || !Array.isArray(data.data)) return null;
|
|
843
|
+
return data;
|
|
844
|
+
} catch {
|
|
845
|
+
return null;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
async chat(opts) {
|
|
849
|
+
const ctrl = new AbortController();
|
|
850
|
+
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
851
|
+
const signal = opts.signal ?? ctrl.signal;
|
|
852
|
+
try {
|
|
853
|
+
await this.waitForChatRateLimit(signal);
|
|
854
|
+
const resp = await fetchWithRetry(
|
|
855
|
+
this._fetch,
|
|
856
|
+
`${this.baseUrl}/chat/completions`,
|
|
857
|
+
{
|
|
858
|
+
method: "POST",
|
|
859
|
+
headers: {
|
|
860
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
861
|
+
"Content-Type": "application/json"
|
|
862
|
+
},
|
|
863
|
+
body: JSON.stringify(this.buildPayload(opts, false)),
|
|
864
|
+
signal
|
|
865
|
+
},
|
|
866
|
+
{ ...this.retry, signal }
|
|
867
|
+
);
|
|
868
|
+
if (!resp.ok) {
|
|
869
|
+
throw new Error(`DeepSeek ${resp.status}: ${await resp.text()}`);
|
|
870
|
+
}
|
|
871
|
+
const data = await resp.json();
|
|
872
|
+
const choice = data.choices?.[0]?.message ?? {};
|
|
873
|
+
return {
|
|
874
|
+
content: choice.content ?? "",
|
|
875
|
+
reasoningContent: choice.reasoning_content ?? null,
|
|
876
|
+
toolCalls: choice.tool_calls ?? [],
|
|
877
|
+
usage: Usage.fromApi(data.usage),
|
|
878
|
+
raw: data
|
|
879
|
+
};
|
|
880
|
+
} finally {
|
|
881
|
+
clearTimeout(timer);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
async *stream(opts) {
|
|
885
|
+
const ctrl = new AbortController();
|
|
886
|
+
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
887
|
+
const signal = opts.signal ?? ctrl.signal;
|
|
888
|
+
let resp;
|
|
889
|
+
try {
|
|
890
|
+
await this.waitForChatRateLimit(signal);
|
|
891
|
+
resp = await fetchWithRetry(
|
|
892
|
+
this._fetch,
|
|
893
|
+
`${this.baseUrl}/chat/completions`,
|
|
894
|
+
{
|
|
895
|
+
method: "POST",
|
|
896
|
+
headers: {
|
|
897
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
898
|
+
"Content-Type": "application/json",
|
|
899
|
+
Accept: "text/event-stream"
|
|
900
|
+
},
|
|
901
|
+
body: JSON.stringify(this.buildPayload(opts, true)),
|
|
902
|
+
signal
|
|
903
|
+
},
|
|
904
|
+
{ ...this.retry, signal }
|
|
905
|
+
);
|
|
906
|
+
} catch (err) {
|
|
907
|
+
clearTimeout(timer);
|
|
908
|
+
throw err;
|
|
909
|
+
}
|
|
910
|
+
if (!resp.ok || !resp.body) {
|
|
911
|
+
clearTimeout(timer);
|
|
912
|
+
throw new Error(`DeepSeek ${resp.status}: ${await resp.text().catch(() => "")}`);
|
|
913
|
+
}
|
|
914
|
+
const queue = [];
|
|
915
|
+
let done = false;
|
|
916
|
+
const parser = createParser({
|
|
917
|
+
onEvent: (ev) => {
|
|
918
|
+
if (!ev.data || ev.data === "[DONE]") {
|
|
919
|
+
done = true;
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
try {
|
|
923
|
+
const json = JSON.parse(ev.data);
|
|
924
|
+
const delta = json.choices?.[0]?.delta ?? {};
|
|
925
|
+
const finishReason = json.choices?.[0]?.finish_reason ?? void 0;
|
|
926
|
+
const chunk = { raw: json, finishReason };
|
|
927
|
+
if (typeof delta.content === "string" && delta.content.length > 0) {
|
|
928
|
+
chunk.contentDelta = delta.content;
|
|
929
|
+
}
|
|
930
|
+
if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
|
|
931
|
+
chunk.reasoningDelta = delta.reasoning_content;
|
|
932
|
+
}
|
|
933
|
+
if (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) {
|
|
934
|
+
const tc = delta.tool_calls[0];
|
|
935
|
+
chunk.toolCallDelta = {
|
|
936
|
+
index: tc.index ?? 0,
|
|
937
|
+
id: tc.id,
|
|
938
|
+
name: tc.function?.name,
|
|
939
|
+
argumentsDelta: tc.function?.arguments
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
if (json.usage) {
|
|
943
|
+
chunk.usage = Usage.fromApi(json.usage);
|
|
944
|
+
}
|
|
945
|
+
queue.push(chunk);
|
|
946
|
+
} catch {
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
});
|
|
950
|
+
const reader = resp.body.getReader();
|
|
951
|
+
const decoder = new TextDecoder();
|
|
952
|
+
try {
|
|
953
|
+
while (true) {
|
|
954
|
+
if (queue.length > 0) {
|
|
955
|
+
yield queue.shift();
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
958
|
+
if (done) break;
|
|
959
|
+
const { value, done: streamDone } = await reader.read();
|
|
960
|
+
if (streamDone) break;
|
|
961
|
+
parser.feed(decoder.decode(value, { stream: true }));
|
|
962
|
+
}
|
|
963
|
+
while (queue.length > 0) yield queue.shift();
|
|
964
|
+
} finally {
|
|
965
|
+
clearTimeout(timer);
|
|
966
|
+
reader.releaseLock();
|
|
967
|
+
}
|
|
852
968
|
}
|
|
853
|
-
const [command, ...args] = argv;
|
|
854
|
-
return { transport: "stdio", name, command, args };
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
// src/config.ts
|
|
858
|
-
var BUILTIN_TYPE_DOCS = {
|
|
859
|
-
user: "role / skills / preferences",
|
|
860
|
-
feedback: "corrections or confirmed approaches",
|
|
861
|
-
project: "facts / decisions about the current work",
|
|
862
|
-
reference: "pointers to external systems the user uses"
|
|
863
969
|
};
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
970
|
+
|
|
971
|
+
// src/core/pause-gate.ts
|
|
972
|
+
var PauseGate = class {
|
|
973
|
+
_nextId = 0;
|
|
974
|
+
_pending = /* @__PURE__ */ new Map();
|
|
975
|
+
_listeners = /* @__PURE__ */ new Set();
|
|
976
|
+
_auditListener = null;
|
|
977
|
+
/** Block until the user responds. Takes a named options object so the
|
|
978
|
+
* kind and payload fields don't get confused at the call site. */
|
|
979
|
+
ask(opts) {
|
|
980
|
+
const { kind, payload } = opts;
|
|
981
|
+
if (this._listeners.size === 0) {
|
|
982
|
+
throw new Error(
|
|
983
|
+
`${kind}: no confirmation listener registered \u2014 cannot prompt the user. This tool can only be used inside an interactive Reasonix session.`
|
|
984
|
+
);
|
|
985
|
+
}
|
|
986
|
+
return new Promise((resolve13) => {
|
|
987
|
+
const id = this._nextId++;
|
|
988
|
+
const request = { id, kind, payload };
|
|
989
|
+
this._pending.set(id, { resolve: resolve13, request });
|
|
990
|
+
for (const fn of this._listeners) {
|
|
991
|
+
try {
|
|
992
|
+
fn(request);
|
|
993
|
+
} catch {
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
});
|
|
868
997
|
}
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
998
|
+
/** Resolve a pending request. Called by the App's modal callback. */
|
|
999
|
+
resolve(id, data) {
|
|
1000
|
+
const p = this._pending.get(id);
|
|
1001
|
+
if (!p) return;
|
|
1002
|
+
this._pending.delete(id);
|
|
1003
|
+
this.emitAuditEvent(p.request, data);
|
|
1004
|
+
p.resolve(data);
|
|
1005
|
+
}
|
|
1006
|
+
/** Safe-cancel every outstanding request — frees stranded tool fns on Esc / /new. */
|
|
1007
|
+
cancelAll() {
|
|
1008
|
+
const ids = [...this._pending.keys()];
|
|
1009
|
+
for (const id of ids) {
|
|
1010
|
+
const p = this._pending.get(id);
|
|
1011
|
+
if (!p) continue;
|
|
1012
|
+
this._pending.delete(id);
|
|
1013
|
+
p.resolve(safeCancelVerdict(p.request.kind));
|
|
880
1014
|
}
|
|
881
|
-
if (raw.expires === "project_end") entry.expires = raw.expires;
|
|
882
|
-
out.push(entry);
|
|
883
1015
|
}
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
if (found.expires) out.expires = found.expires;
|
|
892
|
-
return out;
|
|
893
|
-
}
|
|
894
|
-
var DEFAULT_METASO_API_KEY = "mk-E384C1DD5E8501BB7EFE27C949AFDE5B";
|
|
895
|
-
function loadMetasoApiKey(path2 = defaultConfigPath()) {
|
|
896
|
-
if (process.env.METASO_API_KEY) return process.env.METASO_API_KEY;
|
|
897
|
-
const cfg = readConfig(path2).metasoApiKey;
|
|
898
|
-
if (cfg && typeof cfg === "string" && cfg.trim()) return cfg.trim();
|
|
899
|
-
return DEFAULT_METASO_API_KEY;
|
|
900
|
-
}
|
|
901
|
-
function defaultConfigPath() {
|
|
902
|
-
return join(homedir(), ".reasonix", "config.json");
|
|
903
|
-
}
|
|
904
|
-
function readConfig(path2 = defaultConfigPath()) {
|
|
905
|
-
try {
|
|
906
|
-
const raw = readFileSync(path2, "utf8");
|
|
907
|
-
const parsed = JSON.parse(raw);
|
|
908
|
-
if (parsed && typeof parsed === "object") return parsed;
|
|
909
|
-
} catch {
|
|
1016
|
+
/** Cancel one pending request — used by multi-tab hosts that need per-scope abort. */
|
|
1017
|
+
cancel(id) {
|
|
1018
|
+
const p = this._pending.get(id);
|
|
1019
|
+
if (!p) return false;
|
|
1020
|
+
this._pending.delete(id);
|
|
1021
|
+
p.resolve(safeCancelVerdict(p.request.kind));
|
|
1022
|
+
return true;
|
|
910
1023
|
}
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
function writeConfig(cfg, path2 = defaultConfigPath()) {
|
|
914
|
-
mkdirSync(dirname(path2), { recursive: true });
|
|
915
|
-
writeFileSync(path2, JSON.stringify(cfg, null, 2), "utf8");
|
|
916
|
-
try {
|
|
917
|
-
chmodSync(path2, 384);
|
|
918
|
-
} catch {
|
|
1024
|
+
setAuditListener(fn) {
|
|
1025
|
+
this._auditListener = fn;
|
|
919
1026
|
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
return readConfig(path2).apiKey;
|
|
927
|
-
}
|
|
928
|
-
function loadBaseUrl(path2 = defaultConfigPath()) {
|
|
929
|
-
if (process.env.DEEPSEEK_BASE_URL) return process.env.DEEPSEEK_BASE_URL;
|
|
930
|
-
return readConfig(path2).baseUrl;
|
|
931
|
-
}
|
|
932
|
-
function saveBaseUrl(url, path2 = defaultConfigPath()) {
|
|
933
|
-
const cfg = readConfig(path2);
|
|
934
|
-
const trimmed = url.trim();
|
|
935
|
-
if (trimmed) {
|
|
936
|
-
cfg.baseUrl = trimmed;
|
|
937
|
-
} else {
|
|
938
|
-
cfg.baseUrl = void 0;
|
|
1027
|
+
/** Subscribe to new pause requests. Returns an unsubscribe function. */
|
|
1028
|
+
on(fn) {
|
|
1029
|
+
this._listeners.add(fn);
|
|
1030
|
+
return () => {
|
|
1031
|
+
this._listeners.delete(fn);
|
|
1032
|
+
};
|
|
939
1033
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
return resolve(isAbsolute(homeExpanded) ? homeExpanded : join(baseDir, homeExpanded));
|
|
945
|
-
}
|
|
946
|
-
function normalizeSkillPathEntries(paths, baseDir) {
|
|
947
|
-
const out = [];
|
|
948
|
-
const seen = /* @__PURE__ */ new Set();
|
|
949
|
-
for (const value of paths) {
|
|
950
|
-
if (typeof value !== "string") continue;
|
|
951
|
-
const raw = value.trim();
|
|
952
|
-
if (!raw) continue;
|
|
953
|
-
const resolved = resolveSkillPath(raw, baseDir);
|
|
954
|
-
const key = skillPathKey(resolved);
|
|
955
|
-
if (seen.has(key)) continue;
|
|
956
|
-
seen.add(key);
|
|
957
|
-
out.push({ raw, resolved });
|
|
1034
|
+
/** Current pending request, if any (polling fallback). */
|
|
1035
|
+
get current() {
|
|
1036
|
+
for (const [, p] of this._pending) return p.request;
|
|
1037
|
+
return null;
|
|
958
1038
|
}
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1039
|
+
emitAuditEvent(request, data) {
|
|
1040
|
+
if (!this._auditListener) return;
|
|
1041
|
+
if (request.kind !== "run_command" && request.kind !== "run_background") return;
|
|
1042
|
+
if (!data || typeof data !== "object") return;
|
|
1043
|
+
const choice = data;
|
|
1044
|
+
try {
|
|
1045
|
+
switch (choice.type) {
|
|
1046
|
+
case "run_once":
|
|
1047
|
+
this._auditListener({
|
|
1048
|
+
type: "tool.confirm.allow",
|
|
1049
|
+
kind: request.kind,
|
|
1050
|
+
payload: request.payload
|
|
1051
|
+
});
|
|
1052
|
+
break;
|
|
1053
|
+
case "deny":
|
|
1054
|
+
this._auditListener({
|
|
1055
|
+
type: "tool.confirm.deny",
|
|
1056
|
+
kind: request.kind,
|
|
1057
|
+
payload: request.payload,
|
|
1058
|
+
denyContext: choice.denyContext
|
|
1059
|
+
});
|
|
1060
|
+
break;
|
|
1061
|
+
case "always_allow":
|
|
1062
|
+
if (typeof choice.prefix !== "string") return;
|
|
1063
|
+
this._auditListener({
|
|
1064
|
+
type: "tool.confirm.always_allow",
|
|
1065
|
+
kind: request.kind,
|
|
1066
|
+
payload: request.payload,
|
|
1067
|
+
prefix: choice.prefix
|
|
1068
|
+
});
|
|
1069
|
+
break;
|
|
1070
|
+
default:
|
|
1071
|
+
break;
|
|
1072
|
+
}
|
|
1073
|
+
} catch {
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
function safeCancelVerdict(kind) {
|
|
1078
|
+
switch (kind) {
|
|
1079
|
+
case "run_command":
|
|
1080
|
+
case "run_background":
|
|
1081
|
+
case "path_access":
|
|
1082
|
+
return { type: "deny" };
|
|
1083
|
+
case "plan_proposed":
|
|
1084
|
+
return { type: "cancel" };
|
|
1085
|
+
case "plan_checkpoint":
|
|
1086
|
+
return { type: "stop" };
|
|
1087
|
+
case "plan_revision":
|
|
1088
|
+
return { type: "cancelled" };
|
|
1089
|
+
case "choice":
|
|
1090
|
+
return { type: "cancel" };
|
|
1000
1091
|
}
|
|
1001
|
-
return void 0;
|
|
1002
|
-
}
|
|
1003
|
-
function addProjectShellAllowed(rootDir, prefix, path2 = defaultConfigPath()) {
|
|
1004
|
-
const trimmed = prefix.trim();
|
|
1005
|
-
if (!trimmed) return;
|
|
1006
|
-
const cfg = readConfig(path2);
|
|
1007
|
-
if (!cfg.projects) cfg.projects = {};
|
|
1008
|
-
const key = findProjectKey(cfg, rootDir) ?? rootDir;
|
|
1009
|
-
if (!cfg.projects[key]) cfg.projects[key] = {};
|
|
1010
|
-
const existing = cfg.projects[key].shellAllowed ?? [];
|
|
1011
|
-
if (existing.includes(trimmed)) return;
|
|
1012
|
-
cfg.projects[key].shellAllowed = [...existing, trimmed];
|
|
1013
|
-
writeConfig(cfg, path2);
|
|
1014
|
-
}
|
|
1015
|
-
function loadProjectPathAllowed(rootDir, path2 = defaultConfigPath()) {
|
|
1016
|
-
const cfg = readConfig(path2);
|
|
1017
|
-
const key = findProjectKey(cfg, rootDir);
|
|
1018
|
-
if (key === void 0) return [];
|
|
1019
|
-
return cfg.projects?.[key]?.pathAllowed ?? [];
|
|
1020
|
-
}
|
|
1021
|
-
function addProjectPathAllowed(rootDir, prefix, path2 = defaultConfigPath()) {
|
|
1022
|
-
const trimmed = prefix.trim();
|
|
1023
|
-
if (!trimmed) return;
|
|
1024
|
-
const cfg = readConfig(path2);
|
|
1025
|
-
if (!cfg.projects) cfg.projects = {};
|
|
1026
|
-
const key = findProjectKey(cfg, rootDir) ?? rootDir;
|
|
1027
|
-
if (!cfg.projects[key]) cfg.projects[key] = {};
|
|
1028
|
-
const existing = cfg.projects[key].pathAllowed ?? [];
|
|
1029
|
-
if (existing.includes(trimmed)) return;
|
|
1030
|
-
cfg.projects[key].pathAllowed = [...existing, trimmed];
|
|
1031
|
-
writeConfig(cfg, path2);
|
|
1032
|
-
}
|
|
1033
|
-
function isPlausibleKey(key) {
|
|
1034
|
-
const trimmed = key.trim();
|
|
1035
|
-
if (trimmed.length < 16) return false;
|
|
1036
|
-
return !/\s/.test(trimmed);
|
|
1037
|
-
}
|
|
1038
|
-
function redactKey(key) {
|
|
1039
|
-
if (!key) return "";
|
|
1040
|
-
if (key.length <= 12) return "****";
|
|
1041
|
-
return `${key.slice(0, 6)}\u2026${key.slice(-4)}`;
|
|
1042
1092
|
}
|
|
1093
|
+
var pauseGate = new PauseGate();
|
|
1094
|
+
|
|
1095
|
+
// src/hooks.ts
|
|
1096
|
+
import { spawn } from "child_process";
|
|
1097
|
+
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
1098
|
+
import { homedir as homedir2 } from "os";
|
|
1099
|
+
import { join as join2 } from "path";
|
|
1043
1100
|
|
|
1044
1101
|
// src/i18n/EN.ts
|
|
1045
1102
|
var EN = {
|
|
@@ -1140,8 +1197,9 @@ var EN = {
|
|
|
1140
1197
|
{ key: "wheel", text: "scrolls chat history (works on web/cloud/SSH terminals too)" },
|
|
1141
1198
|
{
|
|
1142
1199
|
key: "\u2191 / \u2193",
|
|
1143
|
-
text: "
|
|
1144
|
-
}
|
|
1200
|
+
text: "prompt history (or per-line cursor in a multi-line draft) \u2014 Ctrl+P / Ctrl+N alias"
|
|
1201
|
+
},
|
|
1202
|
+
{ key: "PgUp / PgDn", text: "scroll chat history (mouse wheel routes here too)" }
|
|
1145
1203
|
]
|
|
1146
1204
|
}
|
|
1147
1205
|
],
|
|
@@ -1155,11 +1213,11 @@ var EN = {
|
|
|
1155
1213
|
rows: [
|
|
1156
1214
|
{ key: "Enter", text: "submit the prompt" },
|
|
1157
1215
|
{ key: "Shift+Enter", text: "insert a newline in the prompt" },
|
|
1158
|
-
{ key: "\u2191 / \u2193", text: "scroll chat history (mouse wheel routes here too)" },
|
|
1159
1216
|
{
|
|
1160
|
-
key: "
|
|
1217
|
+
key: "\u2191 / \u2193",
|
|
1161
1218
|
text: "previous / next prompt history \xB7 cursor up / down in a multi-line draft"
|
|
1162
1219
|
},
|
|
1220
|
+
{ key: "Ctrl+P / Ctrl+N", text: "readline alias for \u2191 / \u2193" },
|
|
1163
1221
|
{ key: "Ctrl+A / Ctrl+E", text: "jump to start / end of the current line" },
|
|
1164
1222
|
{ key: "Ctrl+W", text: "delete the word before the cursor" },
|
|
1165
1223
|
{ key: "Ctrl+U", text: "clear the entire prompt buffer" },
|
|
@@ -1168,7 +1226,11 @@ var EN = {
|
|
|
1168
1226
|
{ key: "Esc", text: "dismiss picker \xB7 abort the running model turn" },
|
|
1169
1227
|
{ key: "Ctrl+C", text: "abort the running model turn (NOT copy \u2014 see clipboard)" },
|
|
1170
1228
|
{ key: "PgUp / PgDn", text: "scroll chat history a page at a time" },
|
|
1171
|
-
{ key: "End", text: "jump chat to the most recent line" }
|
|
1229
|
+
{ key: "End", text: "jump chat to the most recent line" },
|
|
1230
|
+
{
|
|
1231
|
+
key: "Ctrl+R",
|
|
1232
|
+
text: "toggle verbose mode \u2014 full reasoning + tool output, no head/tail elision"
|
|
1233
|
+
}
|
|
1172
1234
|
]
|
|
1173
1235
|
},
|
|
1174
1236
|
{
|
|
@@ -1207,7 +1269,7 @@ var EN = {
|
|
|
1207
1269
|
]
|
|
1208
1270
|
}
|
|
1209
1271
|
],
|
|
1210
|
-
footer: "Wheel
|
|
1272
|
+
footer: "Wheel scrolls chat on most terminals (web/cloud/SSH included) \u2014 SGR mouse tracking is on by default and stays out of the way of native drag-select and right-click. Pass --no-mouse to opt out."
|
|
1211
1273
|
},
|
|
1212
1274
|
tipShownOnce: "shown once",
|
|
1213
1275
|
modelOverride: "override the default model",
|
|
@@ -1349,7 +1411,7 @@ var EN = {
|
|
|
1349
1411
|
},
|
|
1350
1412
|
cwd: {
|
|
1351
1413
|
description: "switch the workspace root mid-session \u2014 re-points fs / shell / memory tools, reloads project hooks, refreshes the at-mention walker",
|
|
1352
|
-
argsHint: "
|
|
1414
|
+
argsHint: "[path]"
|
|
1353
1415
|
},
|
|
1354
1416
|
stop: { description: "abort the current model turn (typed alternative to Esc)" },
|
|
1355
1417
|
feedback: { description: "open a GitHub issue with diagnostic info copied to clipboard" },
|
|
@@ -1362,7 +1424,7 @@ var EN = {
|
|
|
1362
1424
|
sessions: { description: "list saved sessions (current marked with \u25B8)" },
|
|
1363
1425
|
title: { description: "ask the model to rename this session from the conversation" },
|
|
1364
1426
|
qq: {
|
|
1365
|
-
description: "connect, inspect, or disconnect the QQ channel for this session",
|
|
1427
|
+
description: "connect, inspect, or disconnect the QQ channel for this session (first connect guides App ID / App Secret setup)",
|
|
1366
1428
|
argsHint: "[connect [appId appSecret [sandbox]]|status|disconnect]"
|
|
1367
1429
|
},
|
|
1368
1430
|
setup: { description: "reminds you to exit and run `reasonix setup`" },
|
|
@@ -1586,6 +1648,8 @@ var EN = {
|
|
|
1586
1648
|
notedVerbCreated: "created",
|
|
1587
1649
|
notedVerbAppended: "appended to",
|
|
1588
1650
|
memoryWriteFailed: "# memory write failed",
|
|
1651
|
+
verboseOn: "\u25B8 verbose mode on \u2014 full reasoning + tool output",
|
|
1652
|
+
verboseOff: "\u25B8 verbose mode off \u2014 head/tail elision restored",
|
|
1589
1653
|
commandFailed: "! command failed",
|
|
1590
1654
|
btwUsage: "\u25B8 /btw <question> \u2014 ask a side question without polluting the conversation context.",
|
|
1591
1655
|
btwHeader: "\u226B btw",
|
|
@@ -1603,6 +1667,10 @@ var EN = {
|
|
|
1603
1667
|
sessionTitleRenameFailed: '\u25B8 could not rename the session for title "{title}".',
|
|
1604
1668
|
sessionTitleRenamed: '\u25B8 session renamed to "{name}" \u2014 {title}',
|
|
1605
1669
|
sessionTitleAutoRenamed: '\u25B8 auto-named session "{name}" \u2014 {title}',
|
|
1670
|
+
workspaceSwitched: "\u25B8 workspace switched to {root}",
|
|
1671
|
+
semanticRepointed: "\u25B8 semantic_search re-pointed at {root}",
|
|
1672
|
+
semanticDisabledForRoot: "\u25B8 semantic_search disabled (no compatible index in {root})",
|
|
1673
|
+
semanticRebootstrapFailed: "\u25B8 semantic_search re-bootstrap failed: {reason}",
|
|
1606
1674
|
denied: "\u25B8 denied: {cmd}{context}",
|
|
1607
1675
|
alwaysAllowed: '\u25B8 always allowed "{prefix}" for {dir}',
|
|
1608
1676
|
runningCommand: "\u25B8 running: {cmd}",
|
|
@@ -1639,7 +1707,6 @@ var EN = {
|
|
|
1639
1707
|
preflightNoFold: "preflight: request ~{estimate}/{ctxMax} tokens ({pct}%) and nothing left to truncate \u2014 DeepSeek will likely 400. Run /clear or /new to start fresh.",
|
|
1640
1708
|
flashEscalation: "\u21E7 flash requested escalation \u2014 retrying this turn on {model}{reasonSuffix}",
|
|
1641
1709
|
harvestStatus: "extracting plan state from reasoning\u2026",
|
|
1642
|
-
autoEscalation: "\u21E7 auto-escalating to {model} for the rest of this turn \u2014 flash hit {breakdown}. Next turn falls back to {fallback} unless /pro is armed.",
|
|
1643
1710
|
repeatToolCallWarning: "Caught a repeated tool call \u2014 let the model see the issue and retry with a different approach.",
|
|
1644
1711
|
stormStuck: "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.",
|
|
1645
1712
|
stormSuppressed: "Suppressed {count} repeated tool call(s) \u2014 same name + args fired 3+ times.",
|
|
@@ -1717,6 +1784,48 @@ var EN = {
|
|
|
1717
1784
|
titleStarted: "\u25B8 naming session\u2026",
|
|
1718
1785
|
titleFailed: "\u25B8 session title failed: {reason}"
|
|
1719
1786
|
},
|
|
1787
|
+
qq: {
|
|
1788
|
+
unavailable: "/qq is not available in this session.",
|
|
1789
|
+
connecting: "QQ: connecting\u2026",
|
|
1790
|
+
connectFailed: "QQ connect failed: {reason}",
|
|
1791
|
+
disconnecting: "QQ: disconnecting\u2026",
|
|
1792
|
+
disconnectFailed: "QQ disconnect failed: {reason}",
|
|
1793
|
+
usage: "Usage: /qq connect [appId appSecret [sandbox]] | /qq status | /qq disconnect",
|
|
1794
|
+
promptAppId: "QQ setup: enter your QQ Open Platform App ID, then press Enter. Type /cancel to abort.",
|
|
1795
|
+
promptAppSecret: "QQ setup: enter your QQ Open Platform App Secret, then press Enter. Type /cancel to abort.",
|
|
1796
|
+
setupWaitingAppId: "waiting for App ID",
|
|
1797
|
+
setupWaitingAppSecret: "waiting for App Secret",
|
|
1798
|
+
setupCancelled: "QQ setup cancelled.",
|
|
1799
|
+
credentialsRequired: "QQ App ID and App Secret are required.",
|
|
1800
|
+
connected: "QQ connected in {mode} mode. It will auto-start on future launches.",
|
|
1801
|
+
alreadyConnected: "QQ is already connected in {mode} mode. Auto-start is enabled.",
|
|
1802
|
+
disconnected: "QQ disconnected. Auto-start is disabled.",
|
|
1803
|
+
status: "QQ: {connected}, auto-start {enabled}, credentials {configured}, appId {appId}, {sandbox}, access {access}, current mode {mode}.",
|
|
1804
|
+
statusSetup: "QQ: setup in progress \u2014 {step}",
|
|
1805
|
+
stateConnected: "connected",
|
|
1806
|
+
stateDisconnected: "disconnected",
|
|
1807
|
+
stateEnabled: "enabled",
|
|
1808
|
+
stateDisabled: "disabled",
|
|
1809
|
+
stateConfigured: "configured",
|
|
1810
|
+
stateNotConfigured: "not configured",
|
|
1811
|
+
sandbox: "sandbox",
|
|
1812
|
+
production: "production",
|
|
1813
|
+
none: "none",
|
|
1814
|
+
modeChat: "chat",
|
|
1815
|
+
modeCode: "code",
|
|
1816
|
+
accessOwner: "owner {owner}",
|
|
1817
|
+
accessOwnerWithAllowlist: "owner {owner}, allowlist {count}",
|
|
1818
|
+
accessAllowlist: "allowlist {count}",
|
|
1819
|
+
accessRuntime: "first-sender (runtime only, {owner})",
|
|
1820
|
+
accessOpen: "open (unbound)",
|
|
1821
|
+
lockAlreadyRunning: "QQ channel is already running in process {pid}. Stop that process before starting another QQ channel.",
|
|
1822
|
+
unauthorizedMessage: "QQ ignored message from unauthorized openid {openid}. Current access: {access}.",
|
|
1823
|
+
runtimeBound: "QQ temporarily bound this run to first sender {openid}. Set `qq.ownerOpenId` in config to persist access.",
|
|
1824
|
+
missingAppId: "QQ App ID is required. Run `/qq connect` to configure.",
|
|
1825
|
+
missingAppSecret: "QQ App Secret is required. Run `/qq connect` to configure.",
|
|
1826
|
+
authFailed: "QQ bot authentication failed \u2014 check your App ID and App Secret.",
|
|
1827
|
+
readyTimeout: "QQ bot did not receive READY within 15s \u2014 check your App ID and App Secret."
|
|
1828
|
+
},
|
|
1720
1829
|
admin: {
|
|
1721
1830
|
doctorNeedsTui: "/doctor needs a TUI context (postDoctor wired).",
|
|
1722
1831
|
doctorRunning: "\u2695 Doctor \u2014 running health checks\u2026",
|
|
@@ -1983,12 +2092,14 @@ var EN = {
|
|
|
1983
2092
|
usageSearxng: " /search-engine searxng use SearXNG at default endpoint",
|
|
1984
2093
|
usageSearxngUrl: " /search-engine searxng <url> use SearXNG at custom endpoint",
|
|
1985
2094
|
usageMetaso: " /search-engine metaso use Metaso API (100/d free, configure your own API key for more)",
|
|
2095
|
+
usageTavily: " /search-engine tavily use Tavily API (LLM-friendly, free 1000/mo \u2014 set TAVILY_API_KEY or tavilyApiKey in config; get one at https://tavily.com)",
|
|
1986
2096
|
alias: "Alias: /se",
|
|
1987
2097
|
searxngInfo: "SearXNG is a self-hosted metasearch engine (https://github.com/searxng/searxng).",
|
|
1988
2098
|
searxngInstall: "Install it with: docker run -d -p 8080:8080 searxng/searxng",
|
|
1989
2099
|
switched: 'Switched web search engine to "{engine}".{note}',
|
|
1990
2100
|
switchedSearxngNote: " Make sure SearXNG is running at {endpoint}.",
|
|
1991
2101
|
switchedMetasoNote: " There is a daily quota of 100 (configure your own API key for higher limits).",
|
|
2102
|
+
switchedTavilyNote: " Set TAVILY_API_KEY or `tavilyApiKey` in config; free 1000/mo at https://tavily.com.",
|
|
1992
2103
|
confirmed: '\u2713 Web search engine set to "{engine}"{detail}. Next assistant turn will pick up the change.',
|
|
1993
2104
|
confirmedDetail: " ({endpoint})"
|
|
1994
2105
|
},
|
|
@@ -2124,17 +2235,27 @@ var EN = {
|
|
|
2124
2235
|
linesBelow: " \u2193 {count} line below (\u2193/j or Space/PgDn)",
|
|
2125
2236
|
linesBelowPlural: " \u2193 {count} lines below (\u2193/j or Space/PgDn)"
|
|
2126
2237
|
},
|
|
2238
|
+
editPicker: {
|
|
2239
|
+
title: "edit a previous message",
|
|
2240
|
+
hint: "\u2191\u2193 pick \xB7 Enter to load into composer \xB7 Esc to cancel",
|
|
2241
|
+
empty: "no user turns yet \u2014 nothing to edit",
|
|
2242
|
+
dismiss: "Esc to dismiss",
|
|
2243
|
+
forked: "\u25B8 forked at turn #{turn} \u2014 buffer holds the original text"
|
|
2244
|
+
},
|
|
2127
2245
|
sessionPicker: {
|
|
2128
2246
|
header: " \u25C8 REASONIX \xB7 pick a session ",
|
|
2129
2247
|
title: "pick a session \u2014 {workspace}",
|
|
2130
2248
|
messages: "{count} message",
|
|
2131
2249
|
messagesPlural: "{count} messages",
|
|
2132
2250
|
turns: "{count} turns",
|
|
2133
|
-
pickerHint: "\u2191\u2193 pick \xB7 \u23CE open \xB7 [n] new \xB7 [d] delete \xB7 [r] rename \xB7 esc quit",
|
|
2251
|
+
pickerHint: "\u2191\u2193 pick \xB7 / search \xB7 \u23CE open \xB7 [n] new \xB7 [d] delete \xB7 [r] rename \xB7 esc quit",
|
|
2134
2252
|
empty: " no saved sessions in this workspace yet \u2014 press ",
|
|
2135
2253
|
emptyNew: " to start a new one",
|
|
2136
2254
|
renamePrompt: ' rename "{from}" \u2192 ',
|
|
2137
2255
|
renameHint: " \u23CE confirm rename \xB7 esc cancel",
|
|
2256
|
+
searchPrompt: " search sessions: /",
|
|
2257
|
+
searchHint: " type to filter \xB7 \u23CE open match \xB7 esc clear",
|
|
2258
|
+
searchEmpty: " no sessions match this search",
|
|
2138
2259
|
emptyHint: " \u23CE new session \xB7 esc quit",
|
|
2139
2260
|
justNow: "just now",
|
|
2140
2261
|
minAgo: "{count} min ago",
|
|
@@ -2142,6 +2263,18 @@ var EN = {
|
|
|
2142
2263
|
hoursAgo: "{count}h ago",
|
|
2143
2264
|
daysAgo: "{count} days ago"
|
|
2144
2265
|
},
|
|
2266
|
+
workspacePicker: {
|
|
2267
|
+
header: " \u25C8 REASONIX \xB7 pick a workspace ",
|
|
2268
|
+
title: "pick a workspace \u2014 {workspace}",
|
|
2269
|
+
sessions: "{count} session",
|
|
2270
|
+
sessionsPlural: "{count} sessions",
|
|
2271
|
+
current: "current",
|
|
2272
|
+
pickerHint: "\u2191\u2193 pick \xB7 / search \xB7 \u23CE switch + pick session \xB7 esc quit \xB7 /cwd <path> adds one",
|
|
2273
|
+
empty: " no known workspaces yet \u2014 run /cwd <path> once to add one",
|
|
2274
|
+
searchPrompt: " search workspaces: /",
|
|
2275
|
+
searchHint: " type to filter \xB7 \u23CE switch + pick session \xB7 esc clear",
|
|
2276
|
+
searchEmpty: " no workspaces match this search"
|
|
2277
|
+
},
|
|
2145
2278
|
modelPicker: {
|
|
2146
2279
|
header: " \u25C8 REASONIX \xB7 pick a setup ",
|
|
2147
2280
|
loading: " \xB7 loading catalog\u2026",
|
|
@@ -2242,6 +2375,11 @@ var EN = {
|
|
|
2242
2375
|
metasoServerError: "web_search: Metaso server error ({status}) \u2014 try again later, or switch engine with /search-engine mojeek",
|
|
2243
2376
|
metasoParseError: "web_search: Metaso returned unparseable response (HTTP {status}) \u2014 try again later",
|
|
2244
2377
|
metasoApiError: "web_search: Metaso API error (code {code}: {message}) \u2014 try again later",
|
|
2378
|
+
tavilyMissingKey: "web_search: Tavily backend requires an API key \u2014 set TAVILY_API_KEY env var or `tavilyApiKey` in ~/.reasonix/config.json; free 1000/mo signup at https://tavily.com",
|
|
2379
|
+
tavilyUnauthorized: "web_search: Tavily API key rejected \u2014 check TAVILY_API_KEY or get one at https://tavily.com",
|
|
2380
|
+
tavilyRateLimit: "web_search: Tavily rate-limited or monthly quota exceeded \u2014 wait, switch engine with /search-engine mojeek, or upgrade your Tavily plan",
|
|
2381
|
+
tavilyServerError: "web_search: Tavily server error ({status}) \u2014 try again later, or switch engine with /search-engine mojeek",
|
|
2382
|
+
tavilyParseError: "web_search: Tavily returned unparseable response (HTTP {status}) \u2014 try again later",
|
|
2245
2383
|
fetchStatus: "web_fetch {status} for {url} \u2014 try: confirm the URL resolves in a browser; status suggests the host returned an error page",
|
|
2246
2384
|
fetchRateLimit429: "web_fetch 429 for {url} \u2014 try: wait 10s before retrying; the host is rate-limiting this client",
|
|
2247
2385
|
fetchForbidden403: "web_fetch 403 for {url} \u2014 try: the host is blocking this client; the page may require login or block bots \u2014 use web_search snippets instead",
|
|
@@ -2360,7 +2498,8 @@ var EN = {
|
|
|
2360
2498
|
scrollAbove: " \u2191 {scroll} / {max} row above",
|
|
2361
2499
|
scrollAbovePlural: " \u2191 {scroll} / {max} rows above",
|
|
2362
2500
|
scrollMore: " \u2014 {remaining} more",
|
|
2363
|
-
scrollPgUp: " \xB7 PgUp / wheel
|
|
2501
|
+
scrollPgUp: " \xB7 PgUp / wheel",
|
|
2502
|
+
scrollCopy: " \xB7 /copy enters copy mode"
|
|
2364
2503
|
},
|
|
2365
2504
|
slashArgPicker: {
|
|
2366
2505
|
noMatch: 'no match for "{partial}"',
|
|
@@ -2418,7 +2557,8 @@ var EN = {
|
|
|
2418
2557
|
reconnectDetail: "tearing down \xB7 re-handshake \xB7 listing tools",
|
|
2419
2558
|
disabledDetail: "via /mcp disable {name}",
|
|
2420
2559
|
failedSetupHint: "\u2192 run `reasonix setup` to remove this entry, or fix the underlying issue (missing npm package, network, etc.).",
|
|
2421
|
-
failedSetupConfigHint: "\u2192 run `reasonix setup` to remove broken entries from your saved config."
|
|
2560
|
+
failedSetupConfigHint: "\u2192 run `reasonix setup` to remove broken entries from your saved config.",
|
|
2561
|
+
abortedHint: "MCP startup aborted \u2014 {count} server(s) skipped. Run /mcp to retry once you've fixed the underlying issue."
|
|
2422
2562
|
},
|
|
2423
2563
|
checkpointPicker: {
|
|
2424
2564
|
title: "restore a checkpoint \u2014 {workspace}",
|
|
@@ -2563,8 +2703,9 @@ var zhCN = {
|
|
|
2563
2703
|
{ key: "\u6EDA\u8F6E", text: "\u6EDA\u52A8\u804A\u5929\u8BB0\u5F55\uFF08Web / \u4E91\u7AEF / SSH \u7EC8\u7AEF\u4E5F\u80FD\u7528\uFF09" },
|
|
2564
2704
|
{
|
|
2565
2705
|
key: "\u2191 / \u2193",
|
|
2566
|
-
text: "\
|
|
2567
|
-
}
|
|
2706
|
+
text: "\u8F93\u5165\u5386\u53F2\uFF08\u591A\u884C\u8349\u7A3F\u65F6\u6309\u884C\u79FB\u52A8\u5149\u6807\uFF09\u2014 Ctrl+P / Ctrl+N \u540C\u4E49"
|
|
2707
|
+
},
|
|
2708
|
+
{ key: "PgUp / PgDn", text: "\u6EDA\u52A8\u804A\u5929\u8BB0\u5F55\uFF08\u9F20\u6807\u6EDA\u8F6E\u4E5F\u8D70\u8FD9\u6761\u8DEF\u5F84\uFF09" }
|
|
2568
2709
|
]
|
|
2569
2710
|
}
|
|
2570
2711
|
],
|
|
@@ -2578,11 +2719,11 @@ var zhCN = {
|
|
|
2578
2719
|
rows: [
|
|
2579
2720
|
{ key: "Enter", text: "\u63D0\u4EA4\u8F93\u5165" },
|
|
2580
2721
|
{ key: "Shift+Enter", text: "\u5728\u8F93\u5165\u6846\u4E2D\u63D2\u5165\u6362\u884C" },
|
|
2581
|
-
{ key: "\u2191 / \u2193", text: "\u6EDA\u52A8\u804A\u5929\u8BB0\u5F55\uFF08\u9F20\u6807\u6EDA\u8F6E\u4E5F\u8D70\u8FD9\u6761\u8DEF\u5F84\uFF09" },
|
|
2582
2722
|
{
|
|
2583
|
-
key: "
|
|
2723
|
+
key: "\u2191 / \u2193",
|
|
2584
2724
|
text: "\u4E0A\u4E00\u6761 / \u4E0B\u4E00\u6761\u8F93\u5165\u5386\u53F2 \xB7 \u591A\u884C\u8349\u7A3F\u4E2D\u6309\u884C\u79FB\u52A8\u5149\u6807"
|
|
2585
2725
|
},
|
|
2726
|
+
{ key: "Ctrl+P / Ctrl+N", text: "\u2191 / \u2193 \u7684 readline \u540C\u4E49\u952E" },
|
|
2586
2727
|
{ key: "Ctrl+A / Ctrl+E", text: "\u8DF3\u5230\u5F53\u524D\u884C\u7684\u5F00\u5934 / \u7ED3\u5C3E" },
|
|
2587
2728
|
{ key: "Ctrl+W", text: "\u5220\u9664\u5149\u6807\u524D\u7684\u4E00\u4E2A\u8BCD" },
|
|
2588
2729
|
{ key: "Ctrl+U", text: "\u6E05\u7A7A\u6574\u4E2A\u8F93\u5165\u7F13\u51B2\u533A" },
|
|
@@ -2591,7 +2732,8 @@ var zhCN = {
|
|
|
2591
2732
|
{ key: "Esc", text: "\u5173\u95ED\u5F39\u51FA\u9009\u62E9\u5668 \xB7 \u4E2D\u6B62\u5F53\u524D\u6A21\u578B\u56DE\u5408" },
|
|
2592
2733
|
{ key: "Ctrl+C", text: "\u4E2D\u6B62\u5F53\u524D\u6A21\u578B\u56DE\u5408\uFF08\u4E0D\u662F\u590D\u5236 \u2014 \u89C1\u526A\u8D34\u677F\u6BB5\uFF09" },
|
|
2593
2734
|
{ key: "PgUp / PgDn", text: "\u6574\u9875\u6EDA\u52A8\u804A\u5929\u8BB0\u5F55" },
|
|
2594
|
-
{ key: "End", text: "\u8DF3\u5230\u804A\u5929\u7684\u6700\u65B0\u4E00\u884C" }
|
|
2735
|
+
{ key: "End", text: "\u8DF3\u5230\u804A\u5929\u7684\u6700\u65B0\u4E00\u884C" },
|
|
2736
|
+
{ key: "Ctrl+R", text: "\u5207\u6362\u8BE6\u7EC6\u6A21\u5F0F \u2014 \u663E\u793A\u5B8C\u6574\u63A8\u7406 + \u5DE5\u5177\u8F93\u51FA\uFF0C\u4E0D\u7701\u7565" }
|
|
2595
2737
|
]
|
|
2596
2738
|
},
|
|
2597
2739
|
{
|
|
@@ -2630,7 +2772,7 @@ var zhCN = {
|
|
|
2630
2772
|
]
|
|
2631
2773
|
}
|
|
2632
2774
|
],
|
|
2633
|
-
footer: "\
|
|
2775
|
+
footer: "\u6EDA\u8F6E\u5728\u5927\u591A\u6570\u7EC8\u7AEF\uFF08\u542B Web / \u4E91\u7AEF / SSH\uFF09\u90FD\u80FD\u6EDA\u804A\u5929 \u2014 \u9ED8\u8BA4\u5F00\u542F SGR \u9F20\u6807\u8DDF\u8E2A\uFF0C\u4F46\u4E0D\u4F1A\u5F71\u54CD\u7EC8\u7AEF\u539F\u751F\u62D6\u9009\u548C\u53F3\u952E\u83DC\u5355\u3002\u76F4\u63A5\u62D6\u52A8\u9009\u4E2D\u6587\u672C\u65E0\u9700 Shift\u3002\u4F20\u5165 --no-mouse \u53EF\u5173\u95ED\u3002"
|
|
2634
2776
|
},
|
|
2635
2777
|
tipShownOnce: "\u4EC5\u663E\u793A\u4E00\u6B21",
|
|
2636
2778
|
modelOverride: "\u8986\u76D6\u9ED8\u8BA4\u6A21\u578B",
|
|
@@ -2775,7 +2917,7 @@ var zhCN = {
|
|
|
2775
2917
|
keys: { description: "\u952E\u76D8 + \u9F20\u6807 + \u590D\u5236\u7C98\u8D34\u53C2\u8003" },
|
|
2776
2918
|
cwd: {
|
|
2777
2919
|
description: "\u5207\u6362\u5DE5\u4F5C\u533A\u6839\u76EE\u5F55 \u2014 \u91CD\u65B0\u6307\u5411\u6587\u4EF6/Shell/\u8BB0\u5FC6\u5DE5\u5177\uFF0C\u91CD\u8F7D\u9879\u76EE hooks\uFF0C\u5237\u65B0 @ \u5F15\u7528\u904D\u5386\u5668",
|
|
2778
|
-
argsHint: "
|
|
2920
|
+
argsHint: "[path]"
|
|
2779
2921
|
},
|
|
2780
2922
|
stop: { description: "\u4E2D\u6B62\u5F53\u524D\u6A21\u578B\u56DE\u5408\uFF08\u6309 Esc \u7684\u66FF\u4EE3\u65B9\u5F0F\uFF09" },
|
|
2781
2923
|
feedback: { description: "\u6253\u5F00 GitHub Issue\uFF0C\u8BCA\u65AD\u4FE1\u606F\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F" },
|
|
@@ -2787,7 +2929,7 @@ var zhCN = {
|
|
|
2787
2929
|
sessions: { description: "\u5217\u51FA\u5DF2\u4FDD\u5B58\u7684\u4F1A\u8BDD\uFF08\u5F53\u524D\u6807\u8BB0\u4E3A \u25B8\uFF09" },
|
|
2788
2930
|
title: { description: "\u8BA9\u6A21\u578B\u6839\u636E\u5F53\u524D\u5BF9\u8BDD\u91CD\u547D\u540D\u6B64\u4F1A\u8BDD" },
|
|
2789
2931
|
qq: {
|
|
2790
|
-
description: "\u8FDE\u63A5\u3001\u67E5\u770B\u6216\u65AD\u5F00\u5F53\u524D\u4F1A\u8BDD\u7684 QQ \u901A\u9053",
|
|
2932
|
+
description: "\u8FDE\u63A5\u3001\u67E5\u770B\u6216\u65AD\u5F00\u5F53\u524D\u4F1A\u8BDD\u7684 QQ \u901A\u9053\uFF08\u9996\u6B21\u8FDE\u63A5\u4F1A\u5F15\u5BFC\u5F55\u5165 App ID / App Secret\uFF09",
|
|
2791
2933
|
argsHint: "[connect [appId appSecret [sandbox]]|status|disconnect]"
|
|
2792
2934
|
},
|
|
2793
2935
|
setup: { description: "\u63D0\u9192\u60A8\u9000\u51FA\u5E76\u8FD0\u884C `reasonix setup`" },
|
|
@@ -3013,6 +3155,8 @@ var zhCN = {
|
|
|
3013
3155
|
notedVerbCreated: "\u521B\u5EFA",
|
|
3014
3156
|
notedVerbAppended: "\u8FFD\u52A0\u5230",
|
|
3015
3157
|
memoryWriteFailed: "# \u8BB0\u5FC6\u5199\u5165\u5931\u8D25",
|
|
3158
|
+
verboseOn: "\u25B8 \u8BE6\u7EC6\u6A21\u5F0F\u5DF2\u5F00 \u2014 \u663E\u793A\u5B8C\u6574\u63A8\u7406 + \u5DE5\u5177\u8F93\u51FA",
|
|
3159
|
+
verboseOff: "\u25B8 \u8BE6\u7EC6\u6A21\u5F0F\u5DF2\u5173 \u2014 \u6062\u590D\u5934\u5C3E\u7701\u7565",
|
|
3016
3160
|
commandFailed: "! \u547D\u4EE4\u5931\u8D25",
|
|
3017
3161
|
btwUsage: "\u25B8 /btw <\u95EE\u9898> \u2014 \u987A\u4FBF\u95EE\u4E2A\u9898\u5916\u8BDD\uFF0C\u4E0D\u4F1A\u5199\u5165\u5F53\u524D\u4F1A\u8BDD\u4E0A\u4E0B\u6587\u3002",
|
|
3018
3162
|
btwHeader: "\u226B btw",
|
|
@@ -3030,6 +3174,10 @@ var zhCN = {
|
|
|
3030
3174
|
sessionTitleRenameFailed: '\u25B8 \u65E0\u6CD5\u6309\u6807\u9898 "{title}" \u91CD\u547D\u540D\u4F1A\u8BDD\u3002',
|
|
3031
3175
|
sessionTitleRenamed: '\u25B8 \u4F1A\u8BDD\u5DF2\u91CD\u547D\u540D\u4E3A "{name}" \u2014 {title}',
|
|
3032
3176
|
sessionTitleAutoRenamed: '\u25B8 \u5DF2\u81EA\u52A8\u547D\u540D\u4F1A\u8BDD "{name}" \u2014 {title}',
|
|
3177
|
+
workspaceSwitched: "\u25B8 \u5DE5\u4F5C\u533A\u5DF2\u5207\u6362\u5230 {root}",
|
|
3178
|
+
semanticRepointed: "\u25B8 semantic_search \u5DF2\u6307\u5411 {root}",
|
|
3179
|
+
semanticDisabledForRoot: "\u25B8 semantic_search \u5DF2\u7981\u7528\uFF08{root} \u6CA1\u6709\u517C\u5BB9\u7D22\u5F15\uFF09",
|
|
3180
|
+
semanticRebootstrapFailed: "\u25B8 semantic_search \u91CD\u65B0\u521D\u59CB\u5316\u5931\u8D25\uFF1A{reason}",
|
|
3033
3181
|
denied: "\u25B8 \u5DF2\u62D2\u7EDD\uFF1A{cmd}{context}",
|
|
3034
3182
|
alwaysAllowed: '\u25B8 \u5DF2\u5BF9 {dir} \u6C38\u4E45\u5141\u8BB8 "{prefix}"',
|
|
3035
3183
|
runningCommand: "\u25B8 \u6B63\u5728\u6267\u884C\uFF1A{cmd}",
|
|
@@ -3066,7 +3214,6 @@ var zhCN = {
|
|
|
3066
3214
|
preflightNoFold: "\u9884\u68C0\uFF1A\u8BF7\u6C42\u7EA6 {estimate}/{ctxMax} tokens\uFF08{pct}%\uFF09\u4E14\u6CA1\u6709\u53EF\u88C1\u526A\u7684\u5185\u5BB9 \u2014 DeepSeek \u5927\u6982\u7387\u4F1A\u8FD4\u56DE 400\u3002\u8BF7\u8FD0\u884C /clear \u6216 /new \u91CD\u65B0\u5F00\u59CB\u3002",
|
|
3067
3215
|
flashEscalation: "\u21E7 flash \u8BF7\u6C42\u5347\u7EA7 \u2014 \u672C\u8F6E\u6539\u7528 {model}{reasonSuffix}",
|
|
3068
3216
|
harvestStatus: "\u6B63\u5728\u4ECE\u63A8\u7406\u8FC7\u7A0B\u63D0\u53D6\u8BA1\u5212\u72B6\u6001\u2026",
|
|
3069
|
-
autoEscalation: "\u21E7 \u672C\u8F6E\u5269\u4F59\u8C03\u7528\u81EA\u52A8\u5347\u7EA7\u5230 {model} \u2014 flash \u547D\u4E2D {breakdown}\u3002\u4E0B\u4E00\u8F6E\u56DE\u9000\u5230 {fallback}\uFF0C\u9664\u975E\u5DF2\u88C5\u5907 /pro\u3002",
|
|
3070
3217
|
repeatToolCallWarning: "\u62E6\u622A\u5230\u91CD\u590D\u5DE5\u5177\u8C03\u7528 \u2014 \u8BA9\u6A21\u578B\u5BDF\u89C9\u95EE\u9898\u5E76\u6362\u79CD\u65B9\u5F0F\u91CD\u8BD5\u3002",
|
|
3071
3218
|
stormStuck: "\u5DF2\u505C\u6B62\u5361\u6B7B\u7684\u91CD\u8BD5\u5FAA\u73AF \u2014 \u6A21\u578B\u5728\u81EA\u7EA0\u63D0\u793A\u540E\u4ECD\u4EE5\u76F8\u540C\u53C2\u6570\u91CD\u590D\u8C03\u7528\u540C\u4E00\u5DE5\u5177\u3002\u8BF7\u5C1D\u8BD5 /retry\u3001\u6362\u79CD\u8BF4\u6CD5\uFF0C\u6216\u6392\u67E5\u5E95\u5C42\u963B\u585E\u3002",
|
|
3072
3219
|
stormSuppressed: "\u5DF2\u6291\u5236 {count} \u6B21\u91CD\u590D\u5DE5\u5177\u8C03\u7528 \u2014 \u540C\u4E00\u540D\u79F0 + \u53C2\u6570\u89E6\u53D1 3 \u6B21\u4EE5\u4E0A\u3002",
|
|
@@ -3144,6 +3291,48 @@ var zhCN = {
|
|
|
3144
3291
|
titleStarted: "\u25B8 \u6B63\u5728\u547D\u540D\u4F1A\u8BDD\u2026",
|
|
3145
3292
|
titleFailed: "\u25B8 \u4F1A\u8BDD\u547D\u540D\u5931\u8D25\uFF1A{reason}"
|
|
3146
3293
|
},
|
|
3294
|
+
qq: {
|
|
3295
|
+
unavailable: "/qq \u5728\u5F53\u524D\u4F1A\u8BDD\u4E2D\u4E0D\u53EF\u7528\u3002",
|
|
3296
|
+
connecting: "QQ\uFF1A\u6B63\u5728\u8FDE\u63A5\u2026",
|
|
3297
|
+
connectFailed: "QQ \u8FDE\u63A5\u5931\u8D25\uFF1A{reason}",
|
|
3298
|
+
disconnecting: "QQ\uFF1A\u6B63\u5728\u65AD\u5F00\u2026",
|
|
3299
|
+
disconnectFailed: "QQ \u65AD\u5F00\u5931\u8D25\uFF1A{reason}",
|
|
3300
|
+
usage: "\u7528\u6CD5\uFF1A/qq connect [appId appSecret [sandbox]] | /qq status | /qq disconnect",
|
|
3301
|
+
promptAppId: "QQ \u9996\u6B21\u914D\u7F6E\uFF1A\u8BF7\u8F93\u5165 QQ \u5F00\u653E\u5E73\u53F0 App ID \u540E\u56DE\u8F66\u3002\u8F93\u5165 /cancel \u53EF\u53D6\u6D88\u3002",
|
|
3302
|
+
promptAppSecret: "QQ \u9996\u6B21\u914D\u7F6E\uFF1A\u8BF7\u8F93\u5165 QQ \u5F00\u653E\u5E73\u53F0 App Secret \u540E\u56DE\u8F66\u3002\u8F93\u5165 /cancel \u53EF\u53D6\u6D88\u3002",
|
|
3303
|
+
setupWaitingAppId: "\u7B49\u5F85\u8F93\u5165 App ID",
|
|
3304
|
+
setupWaitingAppSecret: "\u7B49\u5F85\u8F93\u5165 App Secret",
|
|
3305
|
+
setupCancelled: "QQ \u9996\u6B21\u914D\u7F6E\u5DF2\u53D6\u6D88\u3002",
|
|
3306
|
+
credentialsRequired: "QQ App ID \u548C App Secret \u4E0D\u80FD\u4E3A\u7A7A\u3002",
|
|
3307
|
+
connected: "QQ \u5DF2\u5728{mode}\u6A21\u5F0F\u4E0B\u8FDE\u63A5\u6210\u529F\uFF0C\u540E\u7EED\u542F\u52A8\u4F1A\u81EA\u52A8\u542F\u7528\u3002",
|
|
3308
|
+
alreadyConnected: "QQ \u5DF2\u5728{mode}\u6A21\u5F0F\u4E0B\u8FDE\u63A5\uFF0C\u81EA\u52A8\u542F\u52A8\u5DF2\u542F\u7528\u3002",
|
|
3309
|
+
disconnected: "QQ \u5DF2\u65AD\u5F00\u8FDE\u63A5\uFF0C\u81EA\u52A8\u542F\u52A8\u5DF2\u5173\u95ED\u3002",
|
|
3310
|
+
status: "QQ\uFF1A{connected}\uFF0C\u81EA\u52A8\u542F\u52A8{enabled}\uFF0C\u51ED\u636E{configured}\uFF0CappId {appId}\uFF0C{sandbox}\uFF0C\u8BBF\u95EE\u63A7\u5236 {access}\uFF0C\u5F53\u524D\u6A21\u5F0F {mode}\u3002",
|
|
3311
|
+
statusSetup: "QQ\uFF1A\u9996\u6B21\u914D\u7F6E\u8FDB\u884C\u4E2D \u2014\u2014 {step}",
|
|
3312
|
+
stateConnected: "\u5DF2\u8FDE\u63A5",
|
|
3313
|
+
stateDisconnected: "\u672A\u8FDE\u63A5",
|
|
3314
|
+
stateEnabled: "\u5DF2\u542F\u7528",
|
|
3315
|
+
stateDisabled: "\u672A\u542F\u7528",
|
|
3316
|
+
stateConfigured: "\u5DF2\u914D\u7F6E",
|
|
3317
|
+
stateNotConfigured: "\u672A\u914D\u7F6E",
|
|
3318
|
+
sandbox: "\u6C99\u7BB1\u73AF\u5883",
|
|
3319
|
+
production: "\u6B63\u5F0F\u73AF\u5883",
|
|
3320
|
+
none: "\u65E0",
|
|
3321
|
+
modeChat: "\u804A\u5929",
|
|
3322
|
+
modeCode: "\u4EE3\u7801",
|
|
3323
|
+
accessOwner: "\u6240\u6709\u8005 {owner}",
|
|
3324
|
+
accessOwnerWithAllowlist: "\u6240\u6709\u8005 {owner}\uFF0C\u767D\u540D\u5355 {count}",
|
|
3325
|
+
accessAllowlist: "\u767D\u540D\u5355 {count}",
|
|
3326
|
+
accessRuntime: "\u9996\u4E2A\u79C1\u804A\u7528\u6237\uFF08\u4EC5\u672C\u6B21\u8FD0\u884C\uFF0C{owner}\uFF09",
|
|
3327
|
+
accessOpen: "\u5F00\u653E\uFF08\u672A\u7ED1\u5B9A\uFF09",
|
|
3328
|
+
lockAlreadyRunning: "QQ \u901A\u9053\u5DF2\u5728\u8FDB\u7A0B {pid} \u4E2D\u8FD0\u884C\u3002\u8BF7\u5148\u505C\u6B62\u8BE5\u8FDB\u7A0B\uFF0C\u518D\u542F\u52A8\u65B0\u7684 QQ \u901A\u9053\u3002",
|
|
3329
|
+
unauthorizedMessage: "QQ \u5FFD\u7565\u4E86\u672A\u6388\u6743 openid {openid} \u7684\u6D88\u606F\u3002\u5F53\u524D\u8BBF\u95EE\u63A7\u5236\uFF1A{access}\u3002",
|
|
3330
|
+
runtimeBound: "QQ \u5DF2\u5728\u672C\u6B21\u8FD0\u884C\u4E2D\u4E34\u65F6\u7ED1\u5B9A\u5230\u9996\u4E2A\u53D1\u9001\u8005 {openid}\u3002\u5982\u9700\u6301\u4E45\u5316\uFF0C\u8BF7\u5728\u914D\u7F6E\u4E2D\u8BBE\u7F6E `qq.ownerOpenId`\u3002",
|
|
3331
|
+
missingAppId: "\u7F3A\u5C11 QQ App ID\u3002\u8BF7\u5148\u8FD0\u884C `/qq connect` \u5B8C\u6210\u914D\u7F6E\u3002",
|
|
3332
|
+
missingAppSecret: "\u7F3A\u5C11 QQ App Secret\u3002\u8BF7\u5148\u8FD0\u884C `/qq connect` \u5B8C\u6210\u914D\u7F6E\u3002",
|
|
3333
|
+
authFailed: "QQ \u673A\u5668\u4EBA\u9274\u6743\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5 App ID \u548C App Secret\u3002",
|
|
3334
|
+
readyTimeout: "QQ \u673A\u5668\u4EBA 15 \u79D2\u5185\u672A\u6536\u5230 READY\uFF0C\u8BF7\u68C0\u67E5 App ID \u548C App Secret\u3002"
|
|
3335
|
+
},
|
|
3147
3336
|
admin: {
|
|
3148
3337
|
doctorNeedsTui: "/doctor \u9700\u8981 TUI \u4E0A\u4E0B\u6587\uFF08postDoctor \u5DF2\u8FDE\u63A5\uFF09\u3002",
|
|
3149
3338
|
doctorRunning: "\u2695 \u5065\u5EB7\u68C0\u67E5 \u2014 \u6B63\u5728\u8FD0\u884C\u2026",
|
|
@@ -3410,12 +3599,14 @@ var zhCN = {
|
|
|
3410
3599
|
usageSearxng: " /search-engine searxng \u4F7F\u7528 SearXNG \u9ED8\u8BA4\u7AEF\u70B9",
|
|
3411
3600
|
usageSearxngUrl: " /search-engine searxng <url> \u4F7F\u7528 SearXNG \u81EA\u5B9A\u4E49\u7AEF\u70B9",
|
|
3412
3601
|
usageMetaso: " /search-engine metaso \u4F7F\u7528 Metaso API\uFF08\u6BCF\u5929 100 \u6B21\u514D\u8D39\uFF0C\u914D\u7F6E\u4F60\u81EA\u5DF1\u7684 API \u5BC6\u94A5\u53EF\u63D0\u5347\u9650\u989D\uFF09",
|
|
3602
|
+
usageTavily: " /search-engine tavily \u4F7F\u7528 Tavily API\uFF08LLM \u53CB\u597D\uFF0C\u6BCF\u6708 1000 \u6B21\u514D\u8D39 \u2014 \u8BBE\u7F6E TAVILY_API_KEY \u6216 config \u7684 tavilyApiKey\uFF1B\u6CE8\u518C https://tavily.com\uFF09",
|
|
3413
3603
|
alias: "\u522B\u540D\uFF1A/se",
|
|
3414
3604
|
searxngInfo: "SearXNG \u662F\u4E00\u4E2A\u81EA\u6258\u7BA1\u7684\u5143\u641C\u7D22\u5F15\u64CE\uFF08https://github.com/searxng/searxng\uFF09\u3002",
|
|
3415
3605
|
searxngInstall: "\u5B89\u88C5\u547D\u4EE4\uFF1A docker run -d -p 8080:8080 searxng/searxng",
|
|
3416
3606
|
switched: '\u5DF2\u5207\u6362\u7F51\u9875\u641C\u7D22\u5F15\u64CE\u4E3A "{engine}"\u3002{note}',
|
|
3417
3607
|
switchedSearxngNote: " \u8BF7\u786E\u4FDD SearXNG \u5728 {endpoint} \u8FD0\u884C\u3002",
|
|
3418
3608
|
switchedMetasoNote: " \u6BCF\u65E5\u9650\u989D 100 \u6B21\uFF08\u914D\u7F6E\u4F60\u81EA\u5DF1\u7684 API \u5BC6\u94A5\u53EF\u63D0\u5347\u9650\u989D\uFF09\u3002",
|
|
3609
|
+
switchedTavilyNote: " \u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF TAVILY_API_KEY \u6216 config \u4E2D\u7684 `tavilyApiKey`\uFF1Bhttps://tavily.com \u6BCF\u6708 1000 \u6B21\u514D\u8D39\u3002",
|
|
3419
3610
|
confirmed: '\u2713 \u7F51\u9875\u641C\u7D22\u5F15\u64CE\u5DF2\u8BBE\u4E3A "{engine}"{detail}\u3002\u4E0B\u4E00\u8F6E\u6A21\u578B\u8C03\u7528\u5C06\u751F\u6548\u3002',
|
|
3420
3611
|
confirmedDetail: "\uFF08{endpoint}\uFF09"
|
|
3421
3612
|
},
|
|
@@ -3551,17 +3742,27 @@ var zhCN = {
|
|
|
3551
3742
|
linesBelow: " \u2193 \u4E0B\u65B9 {count} \u884C\uFF08\u2193/j \u6216 Space/PgDn\uFF09",
|
|
3552
3743
|
linesBelowPlural: " \u2193 \u4E0B\u65B9 {count} \u884C\uFF08\u2193/j \u6216 Space/PgDn\uFF09"
|
|
3553
3744
|
},
|
|
3745
|
+
editPicker: {
|
|
3746
|
+
title: "\u7F16\u8F91\u4E4B\u524D\u7684\u6D88\u606F",
|
|
3747
|
+
hint: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u52A0\u8F7D\u5230\u8F93\u5165\u6846 \xB7 Esc \u53D6\u6D88",
|
|
3748
|
+
empty: "\u8FD8\u6CA1\u6709\u7528\u6237\u53D1\u8A00 \u2014 \u6CA1\u4EC0\u4E48\u53EF\u4EE5\u7F16\u8F91\u7684",
|
|
3749
|
+
dismiss: "Esc \u5173\u95ED",
|
|
3750
|
+
forked: "\u25B8 \u4ECE\u7B2C #{turn} \u8F6E\u5206\u53C9 \u2014 \u539F\u6587\u5DF2\u586B\u56DE\u8F93\u5165\u6846"
|
|
3751
|
+
},
|
|
3554
3752
|
sessionPicker: {
|
|
3555
3753
|
header: " \u25C8 REASONIX \xB7 \u9009\u62E9\u4F1A\u8BDD ",
|
|
3556
3754
|
title: "\u9009\u62E9\u4F1A\u8BDD \u2014 {workspace}",
|
|
3557
3755
|
messages: "{count} \u6761\u6D88\u606F",
|
|
3558
3756
|
messagesPlural: "{count} \u6761\u6D88\u606F",
|
|
3559
3757
|
turns: "{count} \u8F6E",
|
|
3560
|
-
pickerHint: "\u2191\u2193 \u9009\u62E9 \xB7 \u23CE \u6253\u5F00 \xB7 [n] \u65B0\u5EFA \xB7 [d] \u5220\u9664 \xB7 [r] \u91CD\u547D\u540D \xB7 Esc \u9000\u51FA",
|
|
3758
|
+
pickerHint: "\u2191\u2193 \u9009\u62E9 \xB7 / \u641C\u7D22 \xB7 \u23CE \u6253\u5F00 \xB7 [n] \u65B0\u5EFA \xB7 [d] \u5220\u9664 \xB7 [r] \u91CD\u547D\u540D \xB7 Esc \u9000\u51FA",
|
|
3561
3759
|
empty: " \u6B64\u5DE5\u4F5C\u533A\u6682\u65E0\u5DF2\u4FDD\u5B58\u7684\u4F1A\u8BDD \u2014 \u6309 ",
|
|
3562
3760
|
emptyNew: " \u5F00\u59CB\u65B0\u4F1A\u8BDD",
|
|
3563
3761
|
renamePrompt: ' \u91CD\u547D\u540D "{from}" \u2192 ',
|
|
3564
3762
|
renameHint: " \u23CE \u786E\u8BA4\u91CD\u547D\u540D \xB7 Esc \u53D6\u6D88",
|
|
3763
|
+
searchPrompt: " \u641C\u7D22\u4F1A\u8BDD\uFF1A/",
|
|
3764
|
+
searchHint: " \u8F93\u5165\u8FC7\u6EE4 \xB7 \u23CE \u6253\u5F00\u5339\u914D\u9879 \xB7 Esc \u6E05\u9664",
|
|
3765
|
+
searchEmpty: " \u6CA1\u6709\u5339\u914D\u7684\u4F1A\u8BDD",
|
|
3565
3766
|
emptyHint: " \u23CE \u65B0\u5EFA\u4F1A\u8BDD \xB7 Esc \u9000\u51FA",
|
|
3566
3767
|
justNow: "\u521A\u521A",
|
|
3567
3768
|
minAgo: "{count} \u5206\u949F\u524D",
|
|
@@ -3569,6 +3770,18 @@ var zhCN = {
|
|
|
3569
3770
|
hoursAgo: "{count} \u5C0F\u65F6\u524D",
|
|
3570
3771
|
daysAgo: "{count} \u5929\u524D"
|
|
3571
3772
|
},
|
|
3773
|
+
workspacePicker: {
|
|
3774
|
+
header: " \u25C8 REASONIX \xB7 \u9009\u62E9\u5DE5\u4F5C\u533A ",
|
|
3775
|
+
title: "\u9009\u62E9\u5DE5\u4F5C\u533A \u2014 {workspace}",
|
|
3776
|
+
sessions: "{count} \u4E2A\u4F1A\u8BDD",
|
|
3777
|
+
sessionsPlural: "{count} \u4E2A\u4F1A\u8BDD",
|
|
3778
|
+
current: "\u5F53\u524D",
|
|
3779
|
+
pickerHint: "\u2191\u2193 \u9009\u62E9 \xB7 / \u641C\u7D22 \xB7 \u23CE \u5207\u6362\u5E76\u9009\u62E9\u4F1A\u8BDD \xB7 Esc \u9000\u51FA \xB7 /cwd <path> \u6DFB\u52A0",
|
|
3780
|
+
empty: " \u6682\u65E0\u5DF2\u77E5\u5DE5\u4F5C\u533A \u2014 \u5148\u8FD0\u884C\u4E00\u6B21 /cwd <path> \u6DFB\u52A0",
|
|
3781
|
+
searchPrompt: " \u641C\u7D22\u5DE5\u4F5C\u533A\uFF1A/",
|
|
3782
|
+
searchHint: " \u8F93\u5165\u8FC7\u6EE4 \xB7 \u23CE \u5207\u6362\u5E76\u9009\u62E9\u4F1A\u8BDD \xB7 Esc \u6E05\u9664",
|
|
3783
|
+
searchEmpty: " \u6CA1\u6709\u5339\u914D\u7684\u5DE5\u4F5C\u533A"
|
|
3784
|
+
},
|
|
3572
3785
|
modelPicker: {
|
|
3573
3786
|
header: " \u25C8 REASONIX \xB7 \u9009\u62E9\u914D\u7F6E ",
|
|
3574
3787
|
loading: " \xB7 \u52A0\u8F7D\u76EE\u5F55\u2026",
|
|
@@ -3669,6 +3882,11 @@ var zhCN = {
|
|
|
3669
3882
|
metasoServerError: "web_search: Metaso \u670D\u52A1\u5668\u9519\u8BEF\uFF08{status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek \u5207\u6362\u5F15\u64CE",
|
|
3670
3883
|
metasoParseError: "web_search: Metaso \u8FD4\u56DE\u65E0\u6CD5\u89E3\u6790\u7684\u54CD\u5E94\uFF08HTTP {status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5",
|
|
3671
3884
|
metasoApiError: "web_search: Metaso API \u9519\u8BEF\uFF08code {code}: {message}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5",
|
|
3885
|
+
tavilyMissingKey: "web_search: Tavily \u540E\u7AEF\u9700\u8981 API \u5BC6\u94A5 \u2014 \u8BBE\u7F6E TAVILY_API_KEY \u73AF\u5883\u53D8\u91CF\uFF0C\u6216\u5728 ~/.reasonix/config.json \u4E2D\u914D\u7F6E `tavilyApiKey`\uFF1Bhttps://tavily.com \u6BCF\u6708 1000 \u6B21\u514D\u8D39",
|
|
3886
|
+
tavilyUnauthorized: "web_search: Tavily API \u5BC6\u94A5\u88AB\u62D2\u7EDD \u2014 \u68C0\u67E5 TAVILY_API_KEY\uFF0C\u6216\u5728 https://tavily.com \u83B7\u53D6\u5BC6\u94A5",
|
|
3887
|
+
tavilyRateLimit: "web_search: Tavily \u8BF7\u6C42\u9891\u7387\u9650\u5236\u6216\u6708\u5EA6\u914D\u989D\u7528\u5C3D \u2014 \u7B49\u5F85\u3001\u7528 /search-engine mojeek \u5207\u6362\u5F15\u64CE\uFF0C\u6216\u5347\u7EA7 Tavily \u8BA1\u5212",
|
|
3888
|
+
tavilyServerError: "web_search: Tavily \u670D\u52A1\u5668\u9519\u8BEF\uFF08{status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek \u5207\u6362\u5F15\u64CE",
|
|
3889
|
+
tavilyParseError: "web_search: Tavily \u8FD4\u56DE\u65E0\u6CD5\u89E3\u6790\u7684\u54CD\u5E94\uFF08HTTP {status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5",
|
|
3672
3890
|
fetchStatus: "web_fetch {status} for {url} \u2014 try: \u5728\u6D4F\u89C8\u5668\u4E2D\u786E\u8BA4\u8BE5 URL \u80FD\u5426\u8BBF\u95EE\uFF1B\u8BE5\u72B6\u6001\u7801\u8868\u660E\u76EE\u6807\u4E3B\u673A\u8FD4\u56DE\u4E86\u9519\u8BEF\u9875\u9762",
|
|
3673
3891
|
fetchRateLimit429: "web_fetch 429 for {url} \u2014 try: \u7B49\u5F85 10 \u79D2\u540E\u91CD\u8BD5\uFF1B\u76EE\u6807\u4E3B\u673A\u6B63\u5728\u5BF9\u8BE5\u5BA2\u6237\u7AEF\u8FDB\u884C\u9650\u6D41",
|
|
3674
3892
|
fetchForbidden403: "web_fetch 403 for {url} \u2014 try: \u76EE\u6807\u4E3B\u673A\u62D2\u7EDD\u8BE5\u5BA2\u6237\u7AEF\u8BBF\u95EE\uFF1B\u8BE5\u9875\u9762\u53EF\u80FD\u9700\u8981\u767B\u5F55\u6216\u5C4F\u853D\u722C\u866B \u2014 \u6539\u7528 web_search \u6458\u8981",
|
|
@@ -3787,7 +4005,8 @@ var zhCN = {
|
|
|
3787
4005
|
scrollAbove: " \u2191 {scroll}/{max} \u884C",
|
|
3788
4006
|
scrollAbovePlural: " \u2191 {scroll}/{max} \u884C",
|
|
3789
4007
|
scrollMore: " \u2014 \u8FD8\u6709 {remaining} \u884C",
|
|
3790
|
-
scrollPgUp: " \xB7 PgUp/\u6EDA\u8F6E
|
|
4008
|
+
scrollPgUp: " \xB7 PgUp/\u6EDA\u8F6E",
|
|
4009
|
+
scrollCopy: " \xB7 /copy \u8FDB\u5165\u590D\u5236\u6A21\u5F0F"
|
|
3791
4010
|
},
|
|
3792
4011
|
slashArgPicker: {
|
|
3793
4012
|
noMatch: '\u6CA1\u6709\u5339\u914D "{partial}"',
|
|
@@ -3845,7 +4064,8 @@ var zhCN = {
|
|
|
3845
4064
|
reconnectDetail: "\u65AD\u5F00\u65E7\u8FDE\u63A5 \xB7 \u91CD\u65B0\u63E1\u624B \xB7 \u5217\u51FA\u5DE5\u5177",
|
|
3846
4065
|
disabledDetail: "\u901A\u8FC7 /mcp disable {name}",
|
|
3847
4066
|
failedSetupHint: "\u2192 \u8FD0\u884C `reasonix setup` \u79FB\u9664\u6B64\u6761\u76EE\uFF0C\u6216\u4FEE\u590D\u5E95\u5C42\u95EE\u9898\uFF08\u7F3A\u5C11 npm \u5305\u3001\u7F51\u7EDC\u7B49\uFF09\u3002",
|
|
3848
|
-
failedSetupConfigHint: "\u2192 \u8FD0\u884C `reasonix setup` \u4ECE\u5DF2\u4FDD\u5B58\u914D\u7F6E\u4E2D\u79FB\u9664\u635F\u574F\u7684\u6761\u76EE\u3002"
|
|
4067
|
+
failedSetupConfigHint: "\u2192 \u8FD0\u884C `reasonix setup` \u4ECE\u5DF2\u4FDD\u5B58\u914D\u7F6E\u4E2D\u79FB\u9664\u635F\u574F\u7684\u6761\u76EE\u3002",
|
|
4068
|
+
abortedHint: "\u5DF2\u4E2D\u65AD MCP \u542F\u52A8 \u2014 \u8DF3\u8FC7 {count} \u4E2A\u670D\u52A1\u5668\u3002\u95EE\u9898\u4FEE\u590D\u540E\u7528 /mcp \u91CD\u65B0\u8FDE\u63A5\u3002"
|
|
3849
4069
|
},
|
|
3850
4070
|
checkpointPicker: {
|
|
3851
4071
|
title: "\u6062\u590D\u68C0\u67E5\u70B9 \u2014 {workspace}",
|
|
@@ -4692,6 +4912,12 @@ var ToolRegistry = class {
|
|
|
4692
4912
|
});
|
|
4693
4913
|
}
|
|
4694
4914
|
}
|
|
4915
|
+
if (opts.signal?.aborted) {
|
|
4916
|
+
return JSON.stringify({
|
|
4917
|
+
error: `${name}: aborted before dispatch (user interrupt)`,
|
|
4918
|
+
rejectedReason: "aborted"
|
|
4919
|
+
});
|
|
4920
|
+
}
|
|
4695
4921
|
let finalResult;
|
|
4696
4922
|
try {
|
|
4697
4923
|
try {
|
|
@@ -4924,11 +5150,42 @@ async function bridgeMcpTools(client, opts = {}) {
|
|
|
4924
5150
|
return { ...result, env };
|
|
4925
5151
|
}
|
|
4926
5152
|
function flattenMcpResult(result, opts = {}) {
|
|
5153
|
+
validateResultShape(result);
|
|
4927
5154
|
const parts = result.content.map(blockToString);
|
|
4928
5155
|
const joined = parts.join("\n").trim();
|
|
4929
5156
|
const prefixed = result.isError ? `ERROR: ${joined || "(no error message from server)"}` : joined;
|
|
4930
5157
|
return opts.maxChars ? truncateForModel(prefixed, opts.maxChars) : prefixed;
|
|
4931
5158
|
}
|
|
5159
|
+
function validateResultShape(result) {
|
|
5160
|
+
if (typeof result !== "object" || !result)
|
|
5161
|
+
throw new Error(`MCP server returned non-object result: ${typeof result}`);
|
|
5162
|
+
const { content, isError: _isError } = result;
|
|
5163
|
+
if (!Array.isArray(content))
|
|
5164
|
+
throw new Error(`MCP server returned result with non-array content: ${typeof content}`);
|
|
5165
|
+
for (let i = 0; i < content.length; i++) {
|
|
5166
|
+
const block = content[i];
|
|
5167
|
+
if (typeof block !== "object" || !block)
|
|
5168
|
+
throw new Error(`MCP server returned result.content[${i}] is not an object`);
|
|
5169
|
+
if (block.type !== "text" && block.type !== "image")
|
|
5170
|
+
throw new Error(
|
|
5171
|
+
`MCP server returned result.content[${i}] with unknown type ${JSON.stringify(block.type)}`
|
|
5172
|
+
);
|
|
5173
|
+
if (block.type === "text" && typeof block.text !== "string")
|
|
5174
|
+
throw new Error(
|
|
5175
|
+
`MCP server returned result.content[${i}] with non-string text (${typeof block.text})`
|
|
5176
|
+
);
|
|
5177
|
+
if (block.type === "image") {
|
|
5178
|
+
if (typeof block.data !== "string")
|
|
5179
|
+
throw new Error(
|
|
5180
|
+
`MCP server returned result.content[${i}] with non-string data (${typeof block.data})`
|
|
5181
|
+
);
|
|
5182
|
+
if (typeof block.mimeType !== "string")
|
|
5183
|
+
throw new Error(
|
|
5184
|
+
`MCP server returned result.content[${i}] with non-string mimeType (${typeof block.mimeType})`
|
|
5185
|
+
);
|
|
5186
|
+
}
|
|
5187
|
+
}
|
|
5188
|
+
}
|
|
4932
5189
|
function truncateForModel(s, maxChars) {
|
|
4933
5190
|
if (s.length <= maxChars) return s;
|
|
4934
5191
|
const tailBudget = Math.min(1024, Math.floor(maxChars * 0.1));
|
|
@@ -5111,31 +5368,38 @@ function appendSessionMessage(name, message) {
|
|
|
5111
5368
|
} catch {
|
|
5112
5369
|
}
|
|
5113
5370
|
}
|
|
5114
|
-
function listSessions() {
|
|
5371
|
+
function listSessions(opts) {
|
|
5115
5372
|
const dir = sessionsDir();
|
|
5116
5373
|
if (!existsSync3(dir)) return [];
|
|
5374
|
+
const want = opts?.workspaceFilter ? normalizeWorkspace(opts.workspaceFilter) : null;
|
|
5117
5375
|
try {
|
|
5118
5376
|
const files = readdirSync(dir).filter(
|
|
5119
5377
|
(f) => f.endsWith(".jsonl") && !f.endsWith(".events.jsonl")
|
|
5120
5378
|
);
|
|
5121
|
-
return files.
|
|
5379
|
+
return files.flatMap((file) => {
|
|
5122
5380
|
const path2 = join4(dir, file);
|
|
5123
|
-
const stat2 = statSync(path2);
|
|
5124
5381
|
const name = file.replace(/\.jsonl$/, "");
|
|
5382
|
+
const meta = loadSessionMeta(name);
|
|
5383
|
+
if (want !== null) {
|
|
5384
|
+
if (typeof meta.workspace !== "string") return [];
|
|
5385
|
+
if (normalizeWorkspace(meta.workspace) !== want) return [];
|
|
5386
|
+
}
|
|
5387
|
+
const stat2 = statSync(path2);
|
|
5125
5388
|
const messageCount = countLines(path2);
|
|
5126
|
-
return {
|
|
5127
|
-
name,
|
|
5128
|
-
path: path2,
|
|
5129
|
-
size: stat2.size,
|
|
5130
|
-
messageCount,
|
|
5131
|
-
mtime: stat2.mtime,
|
|
5132
|
-
meta: loadSessionMeta(name)
|
|
5133
|
-
};
|
|
5389
|
+
return [{ name, path: path2, size: stat2.size, messageCount, mtime: stat2.mtime, meta }];
|
|
5134
5390
|
}).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
5135
5391
|
} catch {
|
|
5136
5392
|
return [];
|
|
5137
5393
|
}
|
|
5138
5394
|
}
|
|
5395
|
+
function normalizeWorkspace(p, platform = process.platform) {
|
|
5396
|
+
if (typeof p !== "string" || p.length === 0) return "";
|
|
5397
|
+
if (platform === "win32") {
|
|
5398
|
+
const resolved = win32Path.resolve(p);
|
|
5399
|
+
return resolved.replace(/\\/g, "/").replace(/^([A-Z]):/i, (_, d) => `${d.toLowerCase()}:`);
|
|
5400
|
+
}
|
|
5401
|
+
return posixPath.resolve(p);
|
|
5402
|
+
}
|
|
5139
5403
|
function metaPath(name) {
|
|
5140
5404
|
return join4(sessionsDir(), `${sanitizeName(name)}.meta.json`);
|
|
5141
5405
|
}
|
|
@@ -5225,8 +5489,13 @@ function archiveSession(name) {
|
|
|
5225
5489
|
}
|
|
5226
5490
|
function countLines(path2) {
|
|
5227
5491
|
try {
|
|
5228
|
-
const
|
|
5229
|
-
|
|
5492
|
+
const buf = readFileSync4(path2);
|
|
5493
|
+
let count = 0;
|
|
5494
|
+
for (let i = 0; i < buf.length; i++) {
|
|
5495
|
+
if (buf[i] === 10) count++;
|
|
5496
|
+
}
|
|
5497
|
+
if (buf.length > 0 && buf[buf.length - 1] !== 10) count++;
|
|
5498
|
+
return count;
|
|
5230
5499
|
} catch {
|
|
5231
5500
|
return 0;
|
|
5232
5501
|
}
|
|
@@ -5249,6 +5518,16 @@ var DEEPSEEK_PRICING = {
|
|
|
5249
5518
|
"deepseek-chat": { inputCacheHit: 28e-4, inputCacheMiss: 0.14, output: 0.28 },
|
|
5250
5519
|
"deepseek-reasoner": { inputCacheHit: 28e-4, inputCacheMiss: 0.14, output: 0.28 }
|
|
5251
5520
|
};
|
|
5521
|
+
function pricingFor(model, path2) {
|
|
5522
|
+
const defaults = DEEPSEEK_PRICING[model];
|
|
5523
|
+
const override = loadPricingOverride(path2)[model];
|
|
5524
|
+
if (!override) return defaults;
|
|
5525
|
+
const pricing = { ...defaults, ...override };
|
|
5526
|
+
if (pricing.inputCacheHit === void 0 || pricing.inputCacheMiss === void 0 || pricing.output === void 0) {
|
|
5527
|
+
return void 0;
|
|
5528
|
+
}
|
|
5529
|
+
return pricing;
|
|
5530
|
+
}
|
|
5252
5531
|
var CLAUDE_SONNET_PRICING = { input: 3, output: 15 };
|
|
5253
5532
|
var DEEPSEEK_CONTEXT_TOKENS = {
|
|
5254
5533
|
"deepseek-v4-flash": 1e6,
|
|
@@ -5257,24 +5536,24 @@ var DEEPSEEK_CONTEXT_TOKENS = {
|
|
|
5257
5536
|
"deepseek-reasoner": 1e6
|
|
5258
5537
|
};
|
|
5259
5538
|
var DEFAULT_CONTEXT_TOKENS = 131072;
|
|
5260
|
-
function costUsd(model, usage) {
|
|
5261
|
-
const p =
|
|
5539
|
+
function costUsd(model, usage, path2) {
|
|
5540
|
+
const p = pricingFor(model, path2);
|
|
5262
5541
|
if (!p) return 0;
|
|
5263
5542
|
return (usage.promptCacheHitTokens * p.inputCacheHit + usage.promptCacheMissTokens * p.inputCacheMiss + usage.completionTokens * p.output) / 1e6;
|
|
5264
5543
|
}
|
|
5265
|
-
function inputCostUsd(model, usage) {
|
|
5266
|
-
const p =
|
|
5544
|
+
function inputCostUsd(model, usage, path2) {
|
|
5545
|
+
const p = pricingFor(model, path2);
|
|
5267
5546
|
if (!p) return 0;
|
|
5268
5547
|
return (usage.promptCacheHitTokens * p.inputCacheHit + usage.promptCacheMissTokens * p.inputCacheMiss) / 1e6;
|
|
5269
5548
|
}
|
|
5270
|
-
function outputCostUsd(model, usage) {
|
|
5271
|
-
const p =
|
|
5549
|
+
function outputCostUsd(model, usage, path2) {
|
|
5550
|
+
const p = pricingFor(model, path2);
|
|
5272
5551
|
if (!p) return 0;
|
|
5273
5552
|
return usage.completionTokens * p.output / 1e6;
|
|
5274
5553
|
}
|
|
5275
|
-
function cacheSavingsUsd(model, hitTokens) {
|
|
5554
|
+
function cacheSavingsUsd(model, hitTokens, path2) {
|
|
5276
5555
|
if (hitTokens <= 0) return 0;
|
|
5277
|
-
const p =
|
|
5556
|
+
const p = pricingFor(model, path2);
|
|
5278
5557
|
if (!p) return 0;
|
|
5279
5558
|
return hitTokens * (p.inputCacheMiss - p.inputCacheHit) / 1e6;
|
|
5280
5559
|
}
|
|
@@ -5932,42 +6211,6 @@ function* hookWarnings(outcomes, turn) {
|
|
|
5932
6211
|
}
|
|
5933
6212
|
}
|
|
5934
6213
|
|
|
5935
|
-
// src/loop/turn-failure-tracker.ts
|
|
5936
|
-
var FAILURE_ESCALATION_THRESHOLD = 3;
|
|
5937
|
-
var TurnFailureTracker = class {
|
|
5938
|
-
count = 0;
|
|
5939
|
-
types = {};
|
|
5940
|
-
threshold;
|
|
5941
|
-
constructor(threshold = FAILURE_ESCALATION_THRESHOLD) {
|
|
5942
|
-
this.threshold = threshold;
|
|
5943
|
-
}
|
|
5944
|
-
reset() {
|
|
5945
|
-
this.count = 0;
|
|
5946
|
-
this.types = {};
|
|
5947
|
-
}
|
|
5948
|
-
/** True ONLY on the call where the count crosses the configured threshold. */
|
|
5949
|
-
noteAndCrossedThreshold(resultJson, repair) {
|
|
5950
|
-
const before = this.count;
|
|
5951
|
-
const bump = (kind, by = 1) => {
|
|
5952
|
-
this.count += by;
|
|
5953
|
-
this.types[kind] = (this.types[kind] ?? 0) + by;
|
|
5954
|
-
};
|
|
5955
|
-
if (resultJson.includes('"error"') && resultJson.includes("search text not found")) {
|
|
5956
|
-
bump("search-mismatch");
|
|
5957
|
-
}
|
|
5958
|
-
if (repair) {
|
|
5959
|
-
if (repair.scavenged > 0) bump("scavenged", repair.scavenged);
|
|
5960
|
-
if (repair.truncationsFixed > 0) bump("truncated", repair.truncationsFixed);
|
|
5961
|
-
if (repair.stormsBroken > 0) bump("repeat-loop", repair.stormsBroken);
|
|
5962
|
-
}
|
|
5963
|
-
return before < this.threshold && this.count >= this.threshold;
|
|
5964
|
-
}
|
|
5965
|
-
formatBreakdown() {
|
|
5966
|
-
const parts = Object.entries(this.types).filter(([, n]) => n > 0).map(([kind, n]) => `${n}\xD7 ${kind}`);
|
|
5967
|
-
return parts.length > 0 ? parts.join(", ") : `${this.count} repair/error signal(s)`;
|
|
5968
|
-
}
|
|
5969
|
-
};
|
|
5970
|
-
|
|
5971
6214
|
// src/memory/runtime.ts
|
|
5972
6215
|
import { createHash } from "crypto";
|
|
5973
6216
|
var ImmutablePrefix = class {
|
|
@@ -6455,7 +6698,6 @@ var CacheFirstLoop = class {
|
|
|
6455
6698
|
}
|
|
6456
6699
|
_proArmedForNextTurn = false;
|
|
6457
6700
|
_escalateThisTurn = false;
|
|
6458
|
-
_turnFailures;
|
|
6459
6701
|
_turnSelfCorrected = false;
|
|
6460
6702
|
_foldedThisTurn = false;
|
|
6461
6703
|
context;
|
|
@@ -6474,9 +6716,6 @@ var CacheFirstLoop = class {
|
|
|
6474
6716
|
this.reasoningEffort = opts.reasoningEffort ?? "max";
|
|
6475
6717
|
if (opts.autoEscalate !== void 0) this.autoEscalate = opts.autoEscalate;
|
|
6476
6718
|
this.budgetUsd = typeof opts.budgetUsd === "number" && opts.budgetUsd > 0 ? opts.budgetUsd : null;
|
|
6477
|
-
this._turnFailures = new TurnFailureTracker(
|
|
6478
|
-
resolveFailureThreshold(opts.failureThreshold, FAILURE_ESCALATION_THRESHOLD)
|
|
6479
|
-
);
|
|
6480
6719
|
this.hooks = opts.hooks ?? [];
|
|
6481
6720
|
this.hookCwd = opts.hookCwd ?? process.cwd();
|
|
6482
6721
|
this.confirmationGate = opts.confirmationGate ?? pauseGate;
|
|
@@ -6584,7 +6823,6 @@ var CacheFirstLoop = class {
|
|
|
6584
6823
|
this._inflight.clear();
|
|
6585
6824
|
this.stats.reset();
|
|
6586
6825
|
this._turn = 0;
|
|
6587
|
-
this._turnFailures.reset();
|
|
6588
6826
|
this._budgetWarned = false;
|
|
6589
6827
|
let systemRebuilt = false;
|
|
6590
6828
|
if (this._rebuildSystem) {
|
|
@@ -6655,13 +6893,6 @@ var CacheFirstLoop = class {
|
|
|
6655
6893
|
modelForCurrentCall() {
|
|
6656
6894
|
return this._escalateThisTurn ? ESCALATION_MODEL : this.model;
|
|
6657
6895
|
}
|
|
6658
|
-
/** Returns true ONLY on the tipping call — caller surfaces a one-shot warning. */
|
|
6659
|
-
noteToolFailureSignal(resultJson, repair) {
|
|
6660
|
-
if (!this._turnFailures.noteAndCrossedThreshold(resultJson, repair)) return false;
|
|
6661
|
-
if (this._escalateThisTurn || !this.autoEscalate) return false;
|
|
6662
|
-
this._escalateThisTurn = true;
|
|
6663
|
-
return true;
|
|
6664
|
-
}
|
|
6665
6896
|
/** A call counts as mutating when its definition reports `readOnly !== true` and any dynamic `readOnlyCheck` doesn't override that for these args. */
|
|
6666
6897
|
isMutating(call) {
|
|
6667
6898
|
const name = call.function?.name;
|
|
@@ -6785,6 +7016,32 @@ ${reason}`
|
|
|
6785
7016
|
}
|
|
6786
7017
|
return userText;
|
|
6787
7018
|
}
|
|
7019
|
+
/** Rewind to the N-th user turn (0-indexed). Drops that turn + everything after. */
|
|
7020
|
+
rewindToUserTurn(userTurnIndex) {
|
|
7021
|
+
const entries = this.log.entries;
|
|
7022
|
+
let count = 0;
|
|
7023
|
+
let targetIdx = -1;
|
|
7024
|
+
for (let i = 0; i < entries.length; i++) {
|
|
7025
|
+
if (entries[i].role !== "user") continue;
|
|
7026
|
+
if (count === userTurnIndex) {
|
|
7027
|
+
targetIdx = i;
|
|
7028
|
+
break;
|
|
7029
|
+
}
|
|
7030
|
+
count++;
|
|
7031
|
+
}
|
|
7032
|
+
if (targetIdx < 0) return null;
|
|
7033
|
+
const raw = entries[targetIdx].content;
|
|
7034
|
+
const userText = typeof raw === "string" ? raw : "";
|
|
7035
|
+
const preserved = entries.slice(0, targetIdx).map((m) => ({ ...m }));
|
|
7036
|
+
this.log.compactInPlace(preserved);
|
|
7037
|
+
if (this.sessionName) {
|
|
7038
|
+
try {
|
|
7039
|
+
rewriteSession(this.sessionName, preserved);
|
|
7040
|
+
} catch {
|
|
7041
|
+
}
|
|
7042
|
+
}
|
|
7043
|
+
return userText;
|
|
7044
|
+
}
|
|
6788
7045
|
async *step(userInput) {
|
|
6789
7046
|
this._steerConsumed = false;
|
|
6790
7047
|
if (this.budgetUsd !== null) {
|
|
@@ -6816,7 +7073,6 @@ ${reason}`
|
|
|
6816
7073
|
this._turn++;
|
|
6817
7074
|
this.scratch.reset();
|
|
6818
7075
|
this.repair.resetStorm();
|
|
6819
|
-
this._turnFailures.reset();
|
|
6820
7076
|
this._turnSelfCorrected = false;
|
|
6821
7077
|
this._escalateThisTurn = false;
|
|
6822
7078
|
this._foldedThisTurn = false;
|
|
@@ -7087,17 +7343,6 @@ ${reason}`
|
|
|
7087
7343
|
stats: turnStats,
|
|
7088
7344
|
repair: report
|
|
7089
7345
|
};
|
|
7090
|
-
if (this.noteToolFailureSignal("", report)) {
|
|
7091
|
-
yield {
|
|
7092
|
-
turn: this._turn,
|
|
7093
|
-
role: "warning",
|
|
7094
|
-
content: t("loop.autoEscalation", {
|
|
7095
|
-
model: ESCALATION_MODEL,
|
|
7096
|
-
breakdown: this._turnFailures.formatBreakdown(),
|
|
7097
|
-
fallback: this.model
|
|
7098
|
-
})
|
|
7099
|
-
};
|
|
7100
|
-
}
|
|
7101
7346
|
const allSuppressed = report.stormsBroken > 0 && repairedCalls.length === 0 && toolCalls.length > 0;
|
|
7102
7347
|
if (allSuppressed && !this._turnSelfCorrected) {
|
|
7103
7348
|
this._turnSelfCorrected = true;
|
|
@@ -7237,17 +7482,6 @@ ${reason}`
|
|
|
7237
7482
|
name,
|
|
7238
7483
|
content: result
|
|
7239
7484
|
});
|
|
7240
|
-
if (this.noteToolFailureSignal(result)) {
|
|
7241
|
-
yield {
|
|
7242
|
-
turn: this._turn,
|
|
7243
|
-
role: "warning",
|
|
7244
|
-
content: t("loop.autoEscalation", {
|
|
7245
|
-
model: ESCALATION_MODEL,
|
|
7246
|
-
breakdown: this._turnFailures.formatBreakdown(),
|
|
7247
|
-
fallback: this.model
|
|
7248
|
-
})
|
|
7249
|
-
};
|
|
7250
|
-
}
|
|
7251
7485
|
yield {
|
|
7252
7486
|
turn: this._turn,
|
|
7253
7487
|
role: "tool",
|
|
@@ -7285,19 +7519,6 @@ function parsePositiveIntEnv(raw) {
|
|
|
7285
7519
|
const n = Number.parseInt(raw, 10);
|
|
7286
7520
|
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
7287
7521
|
}
|
|
7288
|
-
var FAILURE_THRESHOLD_MIN = 1;
|
|
7289
|
-
var FAILURE_THRESHOLD_MAX = 20;
|
|
7290
|
-
function resolveFailureThreshold(raw, fallback) {
|
|
7291
|
-
if (raw === void 0) return fallback;
|
|
7292
|
-
if (!Number.isInteger(raw) || raw < FAILURE_THRESHOLD_MIN || raw > FAILURE_THRESHOLD_MAX) {
|
|
7293
|
-
process.stderr.write(
|
|
7294
|
-
`\u25B2 ignoring escalation failureThreshold=${raw} (must be an integer in [${FAILURE_THRESHOLD_MIN},${FAILURE_THRESHOLD_MAX}]) \u2014 using default ${fallback}
|
|
7295
|
-
`
|
|
7296
|
-
);
|
|
7297
|
-
return fallback;
|
|
7298
|
-
}
|
|
7299
|
-
return raw;
|
|
7300
|
-
}
|
|
7301
7522
|
|
|
7302
7523
|
// src/at-mentions.ts
|
|
7303
7524
|
import { existsSync as existsSync4, readFileSync as readFileSync6, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
@@ -7988,10 +8209,15 @@ var SkillStore = class {
|
|
|
7988
8209
|
dir: join7(this.projectRoot, ".agents", SKILLS_DIRNAME),
|
|
7989
8210
|
scope: "project"
|
|
7990
8211
|
});
|
|
8212
|
+
out.push({
|
|
8213
|
+
dir: join7(this.projectRoot, ".claude", SKILLS_DIRNAME),
|
|
8214
|
+
scope: "project"
|
|
8215
|
+
});
|
|
7991
8216
|
}
|
|
7992
8217
|
for (const dir of this.customSkillPaths) out.push({ dir, scope: "custom" });
|
|
7993
8218
|
out.push({ dir: join7(this.homeDir, ".reasonix", SKILLS_DIRNAME), scope: "global" });
|
|
7994
8219
|
out.push({ dir: join7(this.homeDir, ".agents", SKILLS_DIRNAME), scope: "global" });
|
|
8220
|
+
out.push({ dir: join7(this.homeDir, ".claude", SKILLS_DIRNAME), scope: "global" });
|
|
7995
8221
|
return out.map((root, priority) => ({ ...root, priority, status: skillPathStatus(root.dir) }));
|
|
7996
8222
|
}
|
|
7997
8223
|
customRoots() {
|
|
@@ -8094,14 +8320,20 @@ var SkillStore = class {
|
|
|
8094
8320
|
}
|
|
8095
8321
|
const { data, body } = parseFrontmatter(raw);
|
|
8096
8322
|
const name = data.name && isValidSkillName(data.name) ? data.name : stem;
|
|
8323
|
+
const description = (data.description ?? "").trim();
|
|
8324
|
+
if (!description) {
|
|
8325
|
+
console.warn(
|
|
8326
|
+
`[skills] "${name}" at ${path2} has no description: \u2014 it will be loaded but won't appear in the skills index.`
|
|
8327
|
+
);
|
|
8328
|
+
}
|
|
8097
8329
|
return {
|
|
8098
8330
|
name,
|
|
8099
|
-
description
|
|
8331
|
+
description,
|
|
8100
8332
|
body: body.trim(),
|
|
8101
8333
|
scope,
|
|
8102
8334
|
path: path2,
|
|
8103
8335
|
allowedTools: parseAllowedTools(data["allowed-tools"]),
|
|
8104
|
-
runAs: parseRunAs(data.runAs),
|
|
8336
|
+
runAs: parseRunAs(data.runAs, data.context, data.agent),
|
|
8105
8337
|
model: data.model?.startsWith("deepseek-") ? data.model : void 0
|
|
8106
8338
|
};
|
|
8107
8339
|
}
|
|
@@ -8134,8 +8366,11 @@ function skillPathStatus(dir) {
|
|
|
8134
8366
|
return "unreadable";
|
|
8135
8367
|
}
|
|
8136
8368
|
}
|
|
8137
|
-
function parseRunAs(raw) {
|
|
8138
|
-
|
|
8369
|
+
function parseRunAs(raw, context, agent) {
|
|
8370
|
+
if (raw?.trim() === "subagent") return "subagent";
|
|
8371
|
+
if (context?.trim().toLowerCase() === "fork") return "subagent";
|
|
8372
|
+
if (agent?.trim()) return "subagent";
|
|
8373
|
+
return "inline";
|
|
8139
8374
|
}
|
|
8140
8375
|
function skillStubBody(name) {
|
|
8141
8376
|
return `---
|
|
@@ -8697,13 +8932,13 @@ import picomatch3 from "picomatch";
|
|
|
8697
8932
|
// src/memory/subdir.ts
|
|
8698
8933
|
import { existsSync as existsSync8, readFileSync as readFileSync10 } from "fs";
|
|
8699
8934
|
import { dirname as dirname5, join as join9, relative as relative2, resolve as resolve5 } from "path";
|
|
8700
|
-
function
|
|
8935
|
+
function findDirMemory(absDir, rootDir) {
|
|
8701
8936
|
const root = resolve5(rootDir);
|
|
8702
|
-
const target = resolve5(
|
|
8937
|
+
const target = resolve5(absDir);
|
|
8703
8938
|
const rel = relative2(root, target);
|
|
8704
|
-
if (
|
|
8939
|
+
if (rel.startsWith("..")) return [];
|
|
8705
8940
|
const found = [];
|
|
8706
|
-
let cur =
|
|
8941
|
+
let cur = target;
|
|
8707
8942
|
while (cur !== root) {
|
|
8708
8943
|
const r = relative2(root, cur);
|
|
8709
8944
|
if (!r || r.startsWith("..")) break;
|
|
@@ -8720,6 +8955,9 @@ function findSubdirMemoryAncestors(absPath, rootDir) {
|
|
|
8720
8955
|
}
|
|
8721
8956
|
return found;
|
|
8722
8957
|
}
|
|
8958
|
+
function findSubdirMemoryAncestors(absPath, rootDir) {
|
|
8959
|
+
return findDirMemory(dirname5(resolve5(absPath)), rootDir);
|
|
8960
|
+
}
|
|
8723
8961
|
function readSubdirMemoryContent(path2) {
|
|
8724
8962
|
let raw;
|
|
8725
8963
|
try {
|
|
@@ -9144,6 +9382,129 @@ function formatOutline(entries) {
|
|
|
9144
9382
|
// src/tools/fs/search.ts
|
|
9145
9383
|
import { promises as fs3 } from "fs";
|
|
9146
9384
|
import * as pathMod4 from "path";
|
|
9385
|
+
|
|
9386
|
+
// src/tools/fs/regex-runner.ts
|
|
9387
|
+
import { Worker } from "worker_threads";
|
|
9388
|
+
var WORKER_SOURCE = `
|
|
9389
|
+
const { parentPort } = require("node:worker_threads");
|
|
9390
|
+
parentPort.on("message", (msg) => {
|
|
9391
|
+
const { id, text, source, flags } = msg;
|
|
9392
|
+
let re;
|
|
9393
|
+
try {
|
|
9394
|
+
re = new RegExp(source, flags);
|
|
9395
|
+
} catch (err) {
|
|
9396
|
+
parentPort.postMessage({ id, error: (err && err.message) ? err.message : String(err) });
|
|
9397
|
+
return;
|
|
9398
|
+
}
|
|
9399
|
+
const lines = text.split(/\\r?\\n/);
|
|
9400
|
+
const hits = [];
|
|
9401
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9402
|
+
if (re.test(lines[i])) hits.push(i);
|
|
9403
|
+
}
|
|
9404
|
+
parentPort.postMessage({ id, hits });
|
|
9405
|
+
});
|
|
9406
|
+
`;
|
|
9407
|
+
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
9408
|
+
var RegexRunner = class {
|
|
9409
|
+
worker = null;
|
|
9410
|
+
pending = /* @__PURE__ */ new Map();
|
|
9411
|
+
nextId = 1;
|
|
9412
|
+
defaultTimeoutMs;
|
|
9413
|
+
constructor(opts = {}) {
|
|
9414
|
+
this.defaultTimeoutMs = opts.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
9415
|
+
}
|
|
9416
|
+
testLines(text, source, flags, opts = {}) {
|
|
9417
|
+
return new Promise((resolve13, reject) => {
|
|
9418
|
+
if (opts.signal?.aborted) {
|
|
9419
|
+
reject(new Error("regex evaluation aborted"));
|
|
9420
|
+
return;
|
|
9421
|
+
}
|
|
9422
|
+
if (!this.worker) this.worker = this.spawn();
|
|
9423
|
+
const id = this.nextId++;
|
|
9424
|
+
const timeoutMs = opts.timeoutMs ?? this.defaultTimeoutMs;
|
|
9425
|
+
const timer = setTimeout(() => {
|
|
9426
|
+
this.pending.delete(id);
|
|
9427
|
+
this.killWorker();
|
|
9428
|
+
reject(new Error(`regex evaluation exceeded ${timeoutMs}ms`));
|
|
9429
|
+
}, timeoutMs);
|
|
9430
|
+
const entry = { resolve: resolve13, reject, timer };
|
|
9431
|
+
if (opts.signal) {
|
|
9432
|
+
entry.signal = opts.signal;
|
|
9433
|
+
entry.onAbort = () => {
|
|
9434
|
+
this.pending.delete(id);
|
|
9435
|
+
clearTimeout(timer);
|
|
9436
|
+
this.killWorker();
|
|
9437
|
+
reject(new Error("regex evaluation aborted"));
|
|
9438
|
+
};
|
|
9439
|
+
opts.signal.addEventListener("abort", entry.onAbort, { once: true });
|
|
9440
|
+
}
|
|
9441
|
+
this.pending.set(id, entry);
|
|
9442
|
+
this.worker.postMessage({ id, text, source, flags });
|
|
9443
|
+
});
|
|
9444
|
+
}
|
|
9445
|
+
async shutdown() {
|
|
9446
|
+
if (this.worker) {
|
|
9447
|
+
const w = this.worker;
|
|
9448
|
+
this.worker = null;
|
|
9449
|
+
await w.terminate();
|
|
9450
|
+
}
|
|
9451
|
+
for (const entry of this.pending.values()) {
|
|
9452
|
+
clearTimeout(entry.timer);
|
|
9453
|
+
if (entry.onAbort && entry.signal) {
|
|
9454
|
+
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
9455
|
+
}
|
|
9456
|
+
entry.reject(new Error("regex runner shut down"));
|
|
9457
|
+
}
|
|
9458
|
+
this.pending.clear();
|
|
9459
|
+
}
|
|
9460
|
+
spawn() {
|
|
9461
|
+
const w = new Worker(WORKER_SOURCE, { eval: true });
|
|
9462
|
+
w.on("message", (msg) => {
|
|
9463
|
+
const entry = this.pending.get(msg.id);
|
|
9464
|
+
if (!entry) return;
|
|
9465
|
+
clearTimeout(entry.timer);
|
|
9466
|
+
if (entry.onAbort && entry.signal) {
|
|
9467
|
+
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
9468
|
+
}
|
|
9469
|
+
this.pending.delete(msg.id);
|
|
9470
|
+
if (msg.error !== void 0) entry.reject(new Error(msg.error));
|
|
9471
|
+
else entry.resolve(msg.hits ?? []);
|
|
9472
|
+
});
|
|
9473
|
+
w.on("error", (err) => {
|
|
9474
|
+
if (this.worker !== w) return;
|
|
9475
|
+
this.failPending(err);
|
|
9476
|
+
});
|
|
9477
|
+
w.on("exit", () => {
|
|
9478
|
+
if (this.worker !== w) return;
|
|
9479
|
+
this.worker = null;
|
|
9480
|
+
if (this.pending.size > 0) this.failPending(new Error("regex worker exited"));
|
|
9481
|
+
});
|
|
9482
|
+
return w;
|
|
9483
|
+
}
|
|
9484
|
+
killWorker() {
|
|
9485
|
+
if (!this.worker) return;
|
|
9486
|
+
const w = this.worker;
|
|
9487
|
+
this.worker = null;
|
|
9488
|
+
void w.terminate();
|
|
9489
|
+
}
|
|
9490
|
+
failPending(err) {
|
|
9491
|
+
for (const entry of this.pending.values()) {
|
|
9492
|
+
clearTimeout(entry.timer);
|
|
9493
|
+
if (entry.onAbort && entry.signal) {
|
|
9494
|
+
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
9495
|
+
}
|
|
9496
|
+
entry.reject(err);
|
|
9497
|
+
}
|
|
9498
|
+
this.pending.clear();
|
|
9499
|
+
}
|
|
9500
|
+
};
|
|
9501
|
+
var _runner = null;
|
|
9502
|
+
function getRegexRunner() {
|
|
9503
|
+
if (!_runner) _runner = new RegexRunner();
|
|
9504
|
+
return _runner;
|
|
9505
|
+
}
|
|
9506
|
+
|
|
9507
|
+
// src/tools/fs/search.ts
|
|
9147
9508
|
function throwIfAborted(signal) {
|
|
9148
9509
|
if (!signal?.aborted) return;
|
|
9149
9510
|
throw new DOMException("search aborted by user", "AbortError");
|
|
@@ -9196,17 +9557,20 @@ async function searchFiles(ctx, startAbs, args) {
|
|
|
9196
9557
|
}
|
|
9197
9558
|
var MAX_HITS_PER_FILE = 30;
|
|
9198
9559
|
var SUMMARY_MODE_TRIGGER_RATIO = 0.8;
|
|
9560
|
+
var WALK_DEADLINE_MS = 12e4;
|
|
9199
9561
|
async function searchContent(ctx, startAbs, args) {
|
|
9200
9562
|
throwIfAborted(args.signal);
|
|
9201
9563
|
const caseSensitive = args.case_sensitive === true;
|
|
9202
9564
|
const includeDeps = args.include_deps === true;
|
|
9203
9565
|
const ctxLines = Math.max(0, Math.min(20, Math.floor(args.context ?? 0)));
|
|
9204
9566
|
const summaryOnly = args.summary_only === true;
|
|
9205
|
-
|
|
9567
|
+
const reFlags = caseSensitive ? "" : "i";
|
|
9568
|
+
let reSource = null;
|
|
9206
9569
|
try {
|
|
9207
|
-
|
|
9570
|
+
new RegExp(args.pattern, reFlags);
|
|
9571
|
+
reSource = args.pattern;
|
|
9208
9572
|
} catch {
|
|
9209
|
-
|
|
9573
|
+
reSource = null;
|
|
9210
9574
|
}
|
|
9211
9575
|
const needle = caseSensitive ? args.pattern : args.pattern.toLowerCase();
|
|
9212
9576
|
const matches = [];
|
|
@@ -9216,6 +9580,15 @@ async function searchContent(ctx, startAbs, args) {
|
|
|
9216
9580
|
let summaryMode = summaryOnly;
|
|
9217
9581
|
let summaryNoticeEmitted = false;
|
|
9218
9582
|
const fileHitCounts = /* @__PURE__ */ new Map();
|
|
9583
|
+
const regexSkippedFiles = [];
|
|
9584
|
+
const t0 = Date.now();
|
|
9585
|
+
const throwIfTimedOut = () => {
|
|
9586
|
+
if (Date.now() - t0 > WALK_DEADLINE_MS) {
|
|
9587
|
+
throw new Error(
|
|
9588
|
+
`search_content exceeded ${WALK_DEADLINE_MS}ms \u2014 narrow the scope (path/glob) or simplify the pattern`
|
|
9589
|
+
);
|
|
9590
|
+
}
|
|
9591
|
+
};
|
|
9219
9592
|
const pushLine = (out) => {
|
|
9220
9593
|
if (totalBytes + out.length + 1 > ctx.maxListBytes) {
|
|
9221
9594
|
matches.push(`[\u2026 truncated at ${ctx.maxListBytes} bytes \u2014 refine pattern or path \u2026]`);
|
|
@@ -9250,6 +9623,7 @@ async function searchContent(ctx, startAbs, args) {
|
|
|
9250
9623
|
for (const e of entries) {
|
|
9251
9624
|
if (truncated) return;
|
|
9252
9625
|
throwIfAborted(args.signal);
|
|
9626
|
+
throwIfTimedOut();
|
|
9253
9627
|
if (e.isDirectory()) {
|
|
9254
9628
|
if (!includeDeps && ctx.skipDirNames.has(e.name)) continue;
|
|
9255
9629
|
await walk2(pathMod4.join(dir, e.name));
|
|
@@ -9286,13 +9660,25 @@ async function searchContent(ctx, startAbs, args) {
|
|
|
9286
9660
|
const text = raw.toString("utf8");
|
|
9287
9661
|
const rel = displayRel3(ctx.rootDir, full);
|
|
9288
9662
|
const lines = text.split(/\r?\n/);
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
|
|
9663
|
+
let hits;
|
|
9664
|
+
if (reSource !== null) {
|
|
9665
|
+
try {
|
|
9666
|
+
hits = await getRegexRunner().testLines(text, reSource, reFlags, {
|
|
9667
|
+
signal: args.signal
|
|
9668
|
+
});
|
|
9669
|
+
} catch (err) {
|
|
9670
|
+
const reason = err.message;
|
|
9671
|
+
if (reason.includes("aborted")) throw err;
|
|
9672
|
+
regexSkippedFiles.push({ rel, reason });
|
|
9673
|
+
continue;
|
|
9674
|
+
}
|
|
9675
|
+
} else {
|
|
9676
|
+
hits = [];
|
|
9677
|
+
for (let li = 0; li < lines.length; li++) {
|
|
9678
|
+
throwIfAborted(args.signal);
|
|
9679
|
+
const lineForCheck = caseSensitive ? lines[li] : lines[li].toLowerCase();
|
|
9680
|
+
if (lineForCheck.includes(needle)) hits.push(li);
|
|
9681
|
+
}
|
|
9296
9682
|
}
|
|
9297
9683
|
scanned++;
|
|
9298
9684
|
if (hits.length === 0) continue;
|
|
@@ -9341,6 +9727,11 @@ async function searchContent(ctx, startAbs, args) {
|
|
|
9341
9727
|
}
|
|
9342
9728
|
};
|
|
9343
9729
|
await walk2(startAbs);
|
|
9730
|
+
if (regexSkippedFiles.length > 0) {
|
|
9731
|
+
pushLine(
|
|
9732
|
+
`[regex timed out on ${regexSkippedFiles.length} file${regexSkippedFiles.length === 1 ? "" : "s"} \u2014 pattern may have catastrophic backtracking; first: ${regexSkippedFiles[0].rel}]`
|
|
9733
|
+
);
|
|
9734
|
+
}
|
|
9344
9735
|
if (matches.length === 0) {
|
|
9345
9736
|
return scanned === 0 ? "(no files scanned \u2014 path empty or all files filtered out)" : `(no matches across ${scanned} file${scanned === 1 ? "" : "s"})`;
|
|
9346
9737
|
}
|
|
@@ -9405,11 +9796,15 @@ function registerFilesystemTools(registry, opts) {
|
|
|
9405
9796
|
const sessionApproved = /* @__PURE__ */ new Set();
|
|
9406
9797
|
const shownSubdirMemory = /* @__PURE__ */ new Set();
|
|
9407
9798
|
function withSubdirMemory(absPath, body) {
|
|
9408
|
-
|
|
9409
|
-
|
|
9410
|
-
|
|
9799
|
+
return prependMemorySections(findSubdirMemoryAncestors(absPath, rootDir), body);
|
|
9800
|
+
}
|
|
9801
|
+
function withDirMemory(absDir, body) {
|
|
9802
|
+
return prependMemorySections(findDirMemory(absDir, rootDir), body);
|
|
9803
|
+
}
|
|
9804
|
+
function prependMemorySections(memPaths, body) {
|
|
9805
|
+
if (!memoryEnabled() || memPaths.length === 0) return body;
|
|
9411
9806
|
const sections = [];
|
|
9412
|
-
for (const memPath of [...
|
|
9807
|
+
for (const memPath of [...memPaths].reverse()) {
|
|
9413
9808
|
if (shownSubdirMemory.has(memPath)) continue;
|
|
9414
9809
|
const content = readSubdirMemoryContent(memPath);
|
|
9415
9810
|
if (!content) continue;
|
|
@@ -9602,7 +9997,7 @@ ${slice.join("\n")}`);
|
|
|
9602
9997
|
for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
9603
9998
|
lines.push(e.isDirectory() ? `${e.name}/` : e.name);
|
|
9604
9999
|
}
|
|
9605
|
-
return lines.join("\n") || "(empty directory)";
|
|
10000
|
+
return withDirMemory(abs, lines.join("\n") || "(empty directory)");
|
|
9606
10001
|
}
|
|
9607
10002
|
});
|
|
9608
10003
|
registry.register({
|
|
@@ -12730,6 +13125,7 @@ var FETCH_MAX_BYTES = 10 * 1024 * 1024;
|
|
|
12730
13125
|
var USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
|
12731
13126
|
var MOJEEK_ENDPOINT = "https://www.mojeek.com/search";
|
|
12732
13127
|
var METASO_ENDPOINT = "https://metaso.cn/api/v1";
|
|
13128
|
+
var TAVILY_ENDPOINT = "https://api.tavily.com/search";
|
|
12733
13129
|
function searchStatusError(status) {
|
|
12734
13130
|
if (status === 429) return t("webErrors.rateLimit429");
|
|
12735
13131
|
if (status === 403) return t("webErrors.forbidden403");
|
|
@@ -12749,6 +13145,9 @@ async function webSearch(query, opts = {}) {
|
|
|
12749
13145
|
if (opts.engine === "searxng") {
|
|
12750
13146
|
return searchSearxng(query, opts);
|
|
12751
13147
|
}
|
|
13148
|
+
if (opts.engine === "tavily") {
|
|
13149
|
+
return searchTavily(query, opts);
|
|
13150
|
+
}
|
|
12752
13151
|
return searchMojeek(query, opts);
|
|
12753
13152
|
}
|
|
12754
13153
|
async function searchMojeek(query, opts = {}) {
|
|
@@ -12883,6 +13282,55 @@ async function searchMetaso(query, opts = {}) {
|
|
|
12883
13282
|
snippet: wp.snippet ?? wp.summary ?? ""
|
|
12884
13283
|
}));
|
|
12885
13284
|
}
|
|
13285
|
+
async function searchTavily(query, opts = {}) {
|
|
13286
|
+
const topK = Math.max(1, Math.min(20, opts.topK ?? DEFAULT_TOPK));
|
|
13287
|
+
const apiKey = loadTavilyApiKey();
|
|
13288
|
+
if (!apiKey) throw new Error(t("webErrors.tavilyMissingKey"));
|
|
13289
|
+
let resp;
|
|
13290
|
+
try {
|
|
13291
|
+
resp = await fetch(TAVILY_ENDPOINT, {
|
|
13292
|
+
method: "POST",
|
|
13293
|
+
headers: {
|
|
13294
|
+
"Content-Type": "application/json",
|
|
13295
|
+
Accept: "application/json"
|
|
13296
|
+
},
|
|
13297
|
+
body: JSON.stringify({
|
|
13298
|
+
api_key: apiKey,
|
|
13299
|
+
query,
|
|
13300
|
+
search_depth: "basic",
|
|
13301
|
+
max_results: topK,
|
|
13302
|
+
include_answer: false,
|
|
13303
|
+
include_raw_content: false,
|
|
13304
|
+
include_images: false
|
|
13305
|
+
}),
|
|
13306
|
+
signal: opts.signal
|
|
13307
|
+
});
|
|
13308
|
+
} catch (err) {
|
|
13309
|
+
if (err instanceof TypeError && err.message.includes("fetch")) {
|
|
13310
|
+
throw new Error(t("webErrors.cannotReach", { endpoint: TAVILY_ENDPOINT }));
|
|
13311
|
+
}
|
|
13312
|
+
throw err;
|
|
13313
|
+
}
|
|
13314
|
+
if (!resp.ok) {
|
|
13315
|
+
if (resp.status === 401 || resp.status === 403) {
|
|
13316
|
+
throw new Error(t("webErrors.tavilyUnauthorized"));
|
|
13317
|
+
}
|
|
13318
|
+
if (resp.status === 429) throw new Error(t("webErrors.tavilyRateLimit"));
|
|
13319
|
+
throw new Error(t("webErrors.tavilyServerError", { status: resp.status }));
|
|
13320
|
+
}
|
|
13321
|
+
let data;
|
|
13322
|
+
try {
|
|
13323
|
+
data = await resp.json();
|
|
13324
|
+
} catch {
|
|
13325
|
+
throw new Error(t("webErrors.tavilyParseError", { status: resp.status }));
|
|
13326
|
+
}
|
|
13327
|
+
const results = data.results ?? [];
|
|
13328
|
+
return results.slice(0, topK).map((r) => ({
|
|
13329
|
+
title: r.title,
|
|
13330
|
+
url: r.url,
|
|
13331
|
+
snippet: r.content ?? ""
|
|
13332
|
+
}));
|
|
13333
|
+
}
|
|
12886
13334
|
function parseSearxngHtmlResults(html) {
|
|
12887
13335
|
const root = parseHtml(html);
|
|
12888
13336
|
const results = [];
|
|
@@ -13101,7 +13549,7 @@ function registerWebTools(registry, opts = {}) {
|
|
|
13101
13549
|
const maxFetchChars = opts.maxFetchChars ?? DEFAULT_FETCH_MAX_CHARS;
|
|
13102
13550
|
registry.register({
|
|
13103
13551
|
name: "web_search",
|
|
13104
|
-
description: "Search the public web. Returns ranked results with title, url, and snippet. Call this when the answer's correctness depends on current state \u2014 anything that changes over time (events, prices, releases, status of a thing in the real world). Composing such answers from training memory invents stale numbers; search first, then ground the answer in the results. For evergreen / definitional questions you don't need this. To change the backend, use /search-engine mojeek|searxng|metaso.",
|
|
13552
|
+
description: "Search the public web. Returns ranked results with title, url, and snippet. Call this when the answer's correctness depends on current state \u2014 anything that changes over time (events, prices, releases, status of a thing in the real world). Composing such answers from training memory invents stale numbers; search first, then ground the answer in the results. For evergreen / definitional questions you don't need this. To change the backend, use /search-engine mojeek|searxng|metaso|tavily.",
|
|
13105
13553
|
readOnly: true,
|
|
13106
13554
|
parallelSafe: true,
|
|
13107
13555
|
parameters: {
|
|
@@ -13787,19 +14235,23 @@ var McpClient = class {
|
|
|
13787
14235
|
return this._instructions;
|
|
13788
14236
|
}
|
|
13789
14237
|
/** Compliant servers reject other methods until this completes. */
|
|
13790
|
-
async initialize() {
|
|
14238
|
+
async initialize(opts = {}) {
|
|
13791
14239
|
if (this.initialized) throw new Error("MCP client already initialized");
|
|
13792
14240
|
this.startReaderIfNeeded();
|
|
13793
|
-
const result = await this.request(
|
|
13794
|
-
|
|
13795
|
-
|
|
13796
|
-
|
|
13797
|
-
|
|
13798
|
-
|
|
13799
|
-
|
|
13800
|
-
|
|
13801
|
-
|
|
13802
|
-
|
|
14241
|
+
const result = await this.request(
|
|
14242
|
+
"initialize",
|
|
14243
|
+
{
|
|
14244
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
14245
|
+
// Advertise every method the client can consume so servers know
|
|
14246
|
+
// they can send listChanged notifications etc. Sub-feature flags
|
|
14247
|
+
// (e.g. `resources.subscribe`) are omitted — we don't implement
|
|
14248
|
+
// those yet and the empty object means "method-level support, no
|
|
14249
|
+
// sub-features."
|
|
14250
|
+
capabilities: { tools: {}, resources: {}, prompts: {} },
|
|
14251
|
+
clientInfo: this.clientInfo
|
|
14252
|
+
},
|
|
14253
|
+
opts.signal
|
|
14254
|
+
);
|
|
13803
14255
|
this._serverCapabilities = result.capabilities ?? {};
|
|
13804
14256
|
this._serverInfo = result.serverInfo ?? { name: "", version: "" };
|
|
13805
14257
|
this._protocolVersion = result.protocolVersion ?? "";
|
|
@@ -13992,18 +14444,20 @@ var StdioTransport = class {
|
|
|
13992
14444
|
this.child = spawn5(line, [], {
|
|
13993
14445
|
env,
|
|
13994
14446
|
cwd: opts.cwd,
|
|
13995
|
-
stdio: ["pipe", "pipe", "
|
|
14447
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
13996
14448
|
shell: true
|
|
13997
14449
|
});
|
|
13998
14450
|
} else {
|
|
13999
14451
|
this.child = spawn5(opts.command, opts.args ?? [], {
|
|
14000
14452
|
env,
|
|
14001
14453
|
cwd: opts.cwd,
|
|
14002
|
-
stdio: ["pipe", "pipe", "
|
|
14454
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
14003
14455
|
});
|
|
14004
14456
|
}
|
|
14005
14457
|
this.child.stdout.setEncoding("utf8");
|
|
14006
14458
|
this.child.stdout.on("data", (chunk) => this.onStdout(chunk));
|
|
14459
|
+
this.child.stderr.setEncoding("utf8");
|
|
14460
|
+
this.child.stderr.on("data", (chunk) => this.onStderr(chunk));
|
|
14007
14461
|
this.child.on("close", () => this.onClose());
|
|
14008
14462
|
this.child.on("error", (err) => {
|
|
14009
14463
|
this.push({
|
|
@@ -14072,6 +14526,13 @@ var StdioTransport = class {
|
|
|
14072
14526
|
}
|
|
14073
14527
|
}
|
|
14074
14528
|
}
|
|
14529
|
+
// Python MCP SDK writes info logs (`server.py:534 ListPromptsRequest`)
|
|
14530
|
+
// to stderr — letting those through would corrupt the TUI render.
|
|
14531
|
+
onStderr(chunk) {
|
|
14532
|
+
if (process.env.REASONIX_DEBUG_MCP === "1") {
|
|
14533
|
+
process.stderr.write(chunk);
|
|
14534
|
+
}
|
|
14535
|
+
}
|
|
14075
14536
|
onClose() {
|
|
14076
14537
|
this.closed = true;
|
|
14077
14538
|
while (this.waiters.length > 0) this.waiters.shift()(null);
|