reasonix 0.45.1 → 0.46.1
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 +3 -8
- package/README.zh-CN.md +3 -0
- package/dashboard/dist/app.js +76 -6
- package/dashboard/dist/app.js.map +1 -1
- package/dist/cli/{acp-LGBLHBKY.js → acp-LKJU5DZX.js} +19 -19
- package/dist/cli/chat-W7LAWEN6.js +51 -0
- package/dist/cli/{chunk-5I2C4JEO.js → chunk-2AASOSD5.js} +6 -6
- package/dist/cli/{chunk-5I2C4JEO.js.map → chunk-2AASOSD5.js.map} +1 -1
- package/dist/cli/{chunk-HVUZWNSP.js → chunk-3AAG2CUT.js} +2 -2
- package/dist/cli/{chunk-IJ7JA32V.js → chunk-6VANO7KB.js} +44 -8
- package/dist/cli/chunk-6VANO7KB.js.map +1 -0
- package/dist/cli/{chunk-LIR2HBQH.js → chunk-7LOJS3LV.js} +2 -2
- package/dist/cli/{chunk-AB2RED3C.js → chunk-7SGGXNB2.js} +16 -16
- package/dist/cli/{chunk-AB2RED3C.js.map → chunk-7SGGXNB2.js.map} +1 -1
- package/dist/cli/{chunk-CBIQWMS6.js → chunk-7YW6TPXK.js} +7 -7
- package/dist/cli/{chunk-A3TSSDS2.js → chunk-C72TNHDE.js} +2 -2
- package/dist/cli/{chunk-AVFXO2EZ.js → chunk-DGA5QYFM.js} +107 -4
- package/dist/cli/chunk-DGA5QYFM.js.map +1 -0
- package/dist/cli/{chunk-JNAQYELD.js → chunk-DHRVZJ2D.js} +2 -2
- package/dist/cli/{chunk-5ACMUK4Q.js → chunk-E7TAHQ4A.js} +2 -1
- package/dist/cli/{chunk-C53JQES5.js → chunk-EAOL43HB.js} +3 -3
- package/dist/cli/{chunk-QJDDIK3Z.js → chunk-IYQ325V7.js} +2 -2
- package/dist/cli/{chunk-4CTDEJUF.js → chunk-JLQDNLZF.js} +2 -2
- package/dist/cli/{chunk-RDRC3XDT.js → chunk-JVFEJAJX.js} +2 -2
- package/dist/cli/{chunk-GTZTQNX5.js → chunk-JVQT5IYP.js} +7 -7
- package/dist/cli/{chunk-IBJIK2DD.js → chunk-K3AIFMI6.js} +2 -2
- package/dist/cli/chunk-K3AIFMI6.js.map +1 -0
- package/dist/cli/{chunk-ZZYBBX5N.js → chunk-M4E5JK6S.js} +23 -9
- package/dist/cli/chunk-M4E5JK6S.js.map +1 -0
- package/dist/cli/{chunk-V26WPN3J.js → chunk-MIIZJD5O.js} +28 -1
- package/dist/cli/chunk-MIIZJD5O.js.map +1 -0
- package/dist/cli/{chunk-4HCP2UQW.js → chunk-NCBP5D6E.js} +2 -2
- package/dist/cli/{chunk-HKWSPKMU.js → chunk-R2ASNSEO.js} +8 -8
- package/dist/cli/{chunk-W7YGWUWU.js → chunk-SE7C5ZSI.js} +3 -3
- package/dist/cli/{chunk-OLLQ76U6.js → chunk-SPXN5JIT.js} +1536 -2500
- package/dist/cli/chunk-SPXN5JIT.js.map +1 -0
- package/dist/cli/{chunk-WK3UFQY3.js → chunk-TDSBASOF.js} +2 -2
- package/dist/cli/{chunk-XSU4QVFW.js → chunk-WQ6ZRDQM.js} +12 -3
- package/dist/cli/chunk-WQ6ZRDQM.js.map +1 -0
- package/dist/cli/{chunk-R3CTO2HM.js → chunk-WRONKNIH.js} +2 -2
- package/dist/cli/{chunk-MJ6W5UN3.js → chunk-XPAUNFOL.js} +2 -2
- package/dist/cli/{chunk-NVURFF27.js → chunk-YRLC2EDF.js} +2 -2
- package/dist/cli/{chunk-WL6SNQ5T.js → chunk-ZOQHVQON.js} +9 -93
- package/dist/cli/chunk-ZOQHVQON.js.map +1 -0
- package/dist/cli/{code-D7V2TQX2.js → code-2JIHL5M2.js} +30 -33
- package/dist/cli/code-2JIHL5M2.js.map +1 -0
- package/dist/cli/{commands-OCU42XG4.js → commands-OPT5AJNH.js} +4 -4
- package/dist/cli/{commit-XCQIQCYG.js → commit-KA37H6GM.js} +3 -3
- package/dist/cli/{desktop-ZCUG7LMF.js → desktop-5ONTRU3C.js} +20 -20
- package/dist/cli/{diff-66B2KWOJ.js → diff-SOIA7AKH.js} +8 -8
- package/dist/cli/{doctor-Y73CPPRZ.js → doctor-RCUP4XRV.js} +9 -9
- package/dist/cli/{events-NGZ2OJYH.js → events-6KHITNX4.js} +3 -3
- package/dist/cli/index.js +46 -102
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{mcp-MPVGBBJF.js → mcp-JP5OWD6R.js} +2 -2
- package/dist/cli/{mcp-browse-4XOTC3FJ.js → mcp-browse-ONCJJPJN.js} +2 -2
- package/dist/cli/{mcp-inspect-CEMGKKAH.js → mcp-inspect-TPLHW5JA.js} +4 -4
- package/dist/cli/{prompt-2D7ID24X.js → prompt-RJDNCQAP.js} +3 -3
- package/dist/cli/{prune-sessions-OJEYYLHY.js → prune-sessions-MKEATRVL.js} +2 -2
- package/dist/cli/{replay-HIQYWBEK.js → replay-4NILJG4U.js} +8 -8
- package/dist/cli/{run-5DPQFSP6.js → run-WFGXB4SB.js} +17 -18
- package/dist/cli/run-WFGXB4SB.js.map +1 -0
- package/dist/cli/{server-TQ2IHYQJ.js → server-5VFQP3PV.js} +13 -13
- package/dist/cli/{sessions-KY54NG45.js → sessions-5XDJDALO.js} +28 -14
- package/dist/cli/sessions-5XDJDALO.js.map +1 -0
- package/dist/cli/{setup-DDNOMMAB.js → setup-F6XSWLRA.js} +7 -7
- package/dist/cli/setup-F6XSWLRA.js.map +1 -0
- package/dist/cli/{stats-X2VTWKNS.js → stats-ALHBZICE.js} +6 -6
- package/dist/cli/{version-7O6A5T7Q.js → version-JVRAHBMM.js} +14 -14
- package/dist/index.d.ts +23 -13
- package/dist/index.js +1112 -1090
- package/dist/index.js.map +1 -1
- package/package.json +5 -16
- package/dist/cli/chat-I7UTQO5L.js +0 -51
- package/dist/cli/chunk-AVFXO2EZ.js.map +0 -1
- package/dist/cli/chunk-IBJIK2DD.js.map +0 -1
- package/dist/cli/chunk-IJ7JA32V.js.map +0 -1
- package/dist/cli/chunk-OLLQ76U6.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-D7V2TQX2.js.map +0 -1
- package/dist/cli/run-5DPQFSP6.js.map +0 -1
- package/dist/cli/sessions-KY54NG45.js.map +0 -1
- package/dist/cli/setup-DDNOMMAB.js.map +0 -1
- /package/dist/cli/{acp-LGBLHBKY.js.map → acp-LKJU5DZX.js.map} +0 -0
- /package/dist/cli/{chat-I7UTQO5L.js.map → chat-W7LAWEN6.js.map} +0 -0
- /package/dist/cli/{chunk-HVUZWNSP.js.map → chunk-3AAG2CUT.js.map} +0 -0
- /package/dist/cli/{chunk-LIR2HBQH.js.map → chunk-7LOJS3LV.js.map} +0 -0
- /package/dist/cli/{chunk-CBIQWMS6.js.map → chunk-7YW6TPXK.js.map} +0 -0
- /package/dist/cli/{chunk-A3TSSDS2.js.map → chunk-C72TNHDE.js.map} +0 -0
- /package/dist/cli/{chunk-JNAQYELD.js.map → chunk-DHRVZJ2D.js.map} +0 -0
- /package/dist/cli/{chunk-5ACMUK4Q.js.map → chunk-E7TAHQ4A.js.map} +0 -0
- /package/dist/cli/{chunk-C53JQES5.js.map → chunk-EAOL43HB.js.map} +0 -0
- /package/dist/cli/{chunk-QJDDIK3Z.js.map → chunk-IYQ325V7.js.map} +0 -0
- /package/dist/cli/{chunk-4CTDEJUF.js.map → chunk-JLQDNLZF.js.map} +0 -0
- /package/dist/cli/{chunk-RDRC3XDT.js.map → chunk-JVFEJAJX.js.map} +0 -0
- /package/dist/cli/{chunk-GTZTQNX5.js.map → chunk-JVQT5IYP.js.map} +0 -0
- /package/dist/cli/{chunk-4HCP2UQW.js.map → chunk-NCBP5D6E.js.map} +0 -0
- /package/dist/cli/{chunk-HKWSPKMU.js.map → chunk-R2ASNSEO.js.map} +0 -0
- /package/dist/cli/{chunk-W7YGWUWU.js.map → chunk-SE7C5ZSI.js.map} +0 -0
- /package/dist/cli/{chunk-WK3UFQY3.js.map → chunk-TDSBASOF.js.map} +0 -0
- /package/dist/cli/{chunk-R3CTO2HM.js.map → chunk-WRONKNIH.js.map} +0 -0
- /package/dist/cli/{chunk-MJ6W5UN3.js.map → chunk-XPAUNFOL.js.map} +0 -0
- /package/dist/cli/{chunk-NVURFF27.js.map → chunk-YRLC2EDF.js.map} +0 -0
- /package/dist/cli/{commands-OCU42XG4.js.map → commands-OPT5AJNH.js.map} +0 -0
- /package/dist/cli/{commit-XCQIQCYG.js.map → commit-KA37H6GM.js.map} +0 -0
- /package/dist/cli/{desktop-ZCUG7LMF.js.map → desktop-5ONTRU3C.js.map} +0 -0
- /package/dist/cli/{diff-66B2KWOJ.js.map → diff-SOIA7AKH.js.map} +0 -0
- /package/dist/cli/{doctor-Y73CPPRZ.js.map → doctor-RCUP4XRV.js.map} +0 -0
- /package/dist/cli/{events-NGZ2OJYH.js.map → events-6KHITNX4.js.map} +0 -0
- /package/dist/cli/{mcp-MPVGBBJF.js.map → mcp-JP5OWD6R.js.map} +0 -0
- /package/dist/cli/{mcp-browse-4XOTC3FJ.js.map → mcp-browse-ONCJJPJN.js.map} +0 -0
- /package/dist/cli/{mcp-inspect-CEMGKKAH.js.map → mcp-inspect-TPLHW5JA.js.map} +0 -0
- /package/dist/cli/{prompt-2D7ID24X.js.map → prompt-RJDNCQAP.js.map} +0 -0
- /package/dist/cli/{prune-sessions-OJEYYLHY.js.map → prune-sessions-MKEATRVL.js.map} +0 -0
- /package/dist/cli/{replay-HIQYWBEK.js.map → replay-4NILJG4U.js.map} +0 -0
- /package/dist/cli/{server-TQ2IHYQJ.js.map → server-5VFQP3PV.js.map} +0 -0
- /package/dist/cli/{stats-X2VTWKNS.js.map → stats-ALHBZICE.js.map} +0 -0
- /package/dist/cli/{version-7O6A5T7Q.js.map → version-JVRAHBMM.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,1045 +1,1096 @@
|
|
|
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(() => "")}`);
|
|
242
|
-
}
|
|
243
|
-
const queue = [];
|
|
244
|
-
let done = false;
|
|
245
|
-
const parser = createParser({
|
|
246
|
-
onEvent: (ev) => {
|
|
247
|
-
if (!ev.data || ev.data === "[DONE]") {
|
|
248
|
-
done = true;
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
try {
|
|
252
|
-
const json = JSON.parse(ev.data);
|
|
253
|
-
const delta = json.choices?.[0]?.delta ?? {};
|
|
254
|
-
const finishReason = json.choices?.[0]?.finish_reason ?? void 0;
|
|
255
|
-
const chunk = { raw: json, finishReason };
|
|
256
|
-
if (typeof delta.content === "string" && delta.content.length > 0) {
|
|
257
|
-
chunk.contentDelta = delta.content;
|
|
258
|
-
}
|
|
259
|
-
if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
|
|
260
|
-
chunk.reasoningDelta = delta.reasoning_content;
|
|
261
|
-
}
|
|
262
|
-
if (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) {
|
|
263
|
-
const tc = delta.tool_calls[0];
|
|
264
|
-
chunk.toolCallDelta = {
|
|
265
|
-
index: tc.index ?? 0,
|
|
266
|
-
id: tc.id,
|
|
267
|
-
name: tc.function?.name,
|
|
268
|
-
argumentsDelta: tc.function?.arguments
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
if (json.usage) {
|
|
272
|
-
chunk.usage = Usage.fromApi(json.usage);
|
|
273
|
-
}
|
|
274
|
-
queue.push(chunk);
|
|
275
|
-
} catch {
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
const reader = resp.body.getReader();
|
|
280
|
-
const decoder = new TextDecoder();
|
|
281
|
-
try {
|
|
282
|
-
while (true) {
|
|
283
|
-
if (queue.length > 0) {
|
|
284
|
-
yield queue.shift();
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
if (done) break;
|
|
288
|
-
const { value, done: streamDone } = await reader.read();
|
|
289
|
-
if (streamDone) break;
|
|
290
|
-
parser.feed(decoder.decode(value, { stream: true }));
|
|
291
|
-
}
|
|
292
|
-
while (queue.length > 0) yield queue.shift();
|
|
293
|
-
} finally {
|
|
294
|
-
clearTimeout(timer);
|
|
295
|
-
reader.releaseLock();
|
|
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));
|
|
296
258
|
}
|
|
297
|
-
}
|
|
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
|
+
]
|
|
298
349
|
};
|
|
350
|
+
var DEFAULT_MAX_FILE_BYTES = 256 * 1024;
|
|
299
351
|
|
|
300
|
-
// src/
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}
|
|
315
|
-
return new Promise((resolve13) => {
|
|
316
|
-
const id = this._nextId++;
|
|
317
|
-
const request = { id, kind, payload };
|
|
318
|
-
this._pending.set(id, { resolve: resolve13, request });
|
|
319
|
-
for (const fn of this._listeners) {
|
|
320
|
-
try {
|
|
321
|
-
fn(request);
|
|
322
|
-
} catch {
|
|
323
|
-
}
|
|
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;
|
|
324
366
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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));
|
|
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;
|
|
343
375
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
setAuditListener(fn) {
|
|
354
|
-
this._auditListener = fn;
|
|
355
|
-
}
|
|
356
|
-
/** Subscribe to new pause requests. Returns an unsubscribe function. */
|
|
357
|
-
on(fn) {
|
|
358
|
-
this._listeners.add(fn);
|
|
359
|
-
return () => {
|
|
360
|
-
this._listeners.delete(fn);
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
/** Current pending request, if any (polling fallback). */
|
|
364
|
-
get current() {
|
|
365
|
-
for (const [, p] of this._pending) return p.request;
|
|
366
|
-
return null;
|
|
367
|
-
}
|
|
368
|
-
emitAuditEvent(request, data) {
|
|
369
|
-
if (!this._auditListener) return;
|
|
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;
|
|
376
|
+
if (ch === '"' || ch === "'") {
|
|
377
|
+
quote = ch;
|
|
378
|
+
i++;
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
if (ch === " " || ch === " ") {
|
|
382
|
+
if (cur.length > 0) {
|
|
383
|
+
tokens.push(cur);
|
|
384
|
+
cur = "";
|
|
401
385
|
}
|
|
402
|
-
|
|
386
|
+
i++;
|
|
387
|
+
continue;
|
|
403
388
|
}
|
|
389
|
+
cur += ch;
|
|
390
|
+
i++;
|
|
404
391
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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" };
|
|
392
|
+
if (quote) {
|
|
393
|
+
throw new Error(
|
|
394
|
+
`shellSplit: unterminated ${quote === '"' ? "double" : "single"} quote in input`
|
|
395
|
+
);
|
|
420
396
|
}
|
|
397
|
+
if (cur.length > 0) tokens.push(cur);
|
|
398
|
+
return tokens;
|
|
421
399
|
}
|
|
422
|
-
var pauseGate = new PauseGate();
|
|
423
400
|
|
|
424
|
-
// src/
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
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");
|
|
409
|
+
}
|
|
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}`);
|
|
415
|
+
}
|
|
416
|
+
const streamMatch = STREAMABLE_PREFIX.exec(body);
|
|
417
|
+
if (streamMatch) {
|
|
418
|
+
return { transport: "streamable-http", name, url: streamMatch[1] };
|
|
419
|
+
}
|
|
420
|
+
if (HTTP_URL.test(body)) {
|
|
421
|
+
return { transport: "sse", name, url: body };
|
|
422
|
+
}
|
|
423
|
+
const argv = shellSplit(body);
|
|
424
|
+
if (argv.length === 0) {
|
|
425
|
+
throw new Error(`MCP spec has name but no command: ${input}`);
|
|
426
|
+
}
|
|
427
|
+
const [command, ...args] = argv;
|
|
428
|
+
return { transport: "stdio", name, command, args };
|
|
429
|
+
}
|
|
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
|
-
|
|
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;
|
|
456
459
|
}
|
|
457
|
-
function
|
|
458
|
-
|
|
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;
|
|
459
467
|
}
|
|
460
|
-
var
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
},
|
|
477
|
-
toneActive: {
|
|
478
|
-
brand: "#a5d6ff",
|
|
479
|
-
accent: "#e2c5ff",
|
|
480
|
-
violet: "#c8aaff",
|
|
481
|
-
ok: "#a8f5ad",
|
|
482
|
-
warn: "#ffc99e",
|
|
483
|
-
err: "#ffaba3",
|
|
484
|
-
info: "#a5d6ff"
|
|
485
|
-
},
|
|
486
|
-
surface: {
|
|
487
|
-
bg: "#0a0c10",
|
|
488
|
-
bgInput: "#0d1015",
|
|
489
|
-
bgCode: "#06080c",
|
|
490
|
-
bgElev: "#11141a"
|
|
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 defaultConfigPath() {
|
|
476
|
+
return join(homedir(), ".reasonix", "config.json");
|
|
477
|
+
}
|
|
478
|
+
function readConfig(path2 = defaultConfigPath()) {
|
|
479
|
+
try {
|
|
480
|
+
const raw = readFileSync(path2, "utf8");
|
|
481
|
+
const parsed = JSON.parse(raw);
|
|
482
|
+
if (parsed && typeof parsed === "object") return parsed;
|
|
483
|
+
} catch {
|
|
491
484
|
}
|
|
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"
|
|
485
|
+
return {};
|
|
486
|
+
}
|
|
487
|
+
function writeConfig(cfg, path2 = defaultConfigPath()) {
|
|
488
|
+
mkdirSync(dirname(path2), { recursive: true });
|
|
489
|
+
writeFileSync(path2, JSON.stringify(cfg, null, 2), "utf8");
|
|
490
|
+
try {
|
|
491
|
+
chmodSync(path2, 384);
|
|
492
|
+
} catch {
|
|
524
493
|
}
|
|
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"
|
|
494
|
+
}
|
|
495
|
+
function loadLanguage(path2 = defaultConfigPath()) {
|
|
496
|
+
return readConfig(path2).lang;
|
|
497
|
+
}
|
|
498
|
+
function loadApiKey(path2 = defaultConfigPath()) {
|
|
499
|
+
if (process.env.DEEPSEEK_API_KEY) return process.env.DEEPSEEK_API_KEY;
|
|
500
|
+
return readConfig(path2).apiKey;
|
|
501
|
+
}
|
|
502
|
+
function loadBaseUrl(path2 = defaultConfigPath()) {
|
|
503
|
+
if (process.env.DEEPSEEK_BASE_URL) return process.env.DEEPSEEK_BASE_URL;
|
|
504
|
+
return readConfig(path2).baseUrl;
|
|
505
|
+
}
|
|
506
|
+
function isNonNegativeNumber(value) {
|
|
507
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
508
|
+
}
|
|
509
|
+
function loadPricingOverride(path2 = defaultConfigPath()) {
|
|
510
|
+
const raw = readConfig(path2).pricingOverride;
|
|
511
|
+
if (!isPlainObject(raw)) return {};
|
|
512
|
+
const result = {};
|
|
513
|
+
for (const [model, value] of Object.entries(raw)) {
|
|
514
|
+
if (!isPlainObject(value)) continue;
|
|
515
|
+
const pricing = {};
|
|
516
|
+
if (isNonNegativeNumber(value.inputCacheHit)) pricing.inputCacheHit = value.inputCacheHit;
|
|
517
|
+
if (isNonNegativeNumber(value.inputCacheMiss)) pricing.inputCacheMiss = value.inputCacheMiss;
|
|
518
|
+
if (isNonNegativeNumber(value.output)) pricing.output = value.output;
|
|
519
|
+
if (Object.keys(pricing).length > 0) result[model] = pricing;
|
|
557
520
|
}
|
|
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"
|
|
521
|
+
return result;
|
|
522
|
+
}
|
|
523
|
+
function loadRateLimit(path2 = defaultConfigPath()) {
|
|
524
|
+
const rpm = readConfig(path2).rateLimit?.rpm;
|
|
525
|
+
if (typeof rpm !== "number" || !Number.isInteger(rpm) || rpm <= 0) return void 0;
|
|
526
|
+
return { rpm };
|
|
527
|
+
}
|
|
528
|
+
function saveBaseUrl(url, path2 = defaultConfigPath()) {
|
|
529
|
+
const cfg = readConfig(path2);
|
|
530
|
+
const trimmed = url.trim();
|
|
531
|
+
if (trimmed) {
|
|
532
|
+
cfg.baseUrl = trimmed;
|
|
533
|
+
} else {
|
|
534
|
+
cfg.baseUrl = void 0;
|
|
590
535
|
}
|
|
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"
|
|
536
|
+
writeConfig(cfg, path2);
|
|
537
|
+
}
|
|
538
|
+
function resolveSkillPath(raw, baseDir) {
|
|
539
|
+
const homeExpanded = expandCurrentUserHome(raw.trim());
|
|
540
|
+
return resolve(isAbsolute(homeExpanded) ? homeExpanded : join(baseDir, homeExpanded));
|
|
541
|
+
}
|
|
542
|
+
function normalizeSkillPathEntries(paths, baseDir) {
|
|
543
|
+
const out = [];
|
|
544
|
+
const seen = /* @__PURE__ */ new Set();
|
|
545
|
+
for (const value of paths) {
|
|
546
|
+
if (typeof value !== "string") continue;
|
|
547
|
+
const raw = value.trim();
|
|
548
|
+
if (!raw) continue;
|
|
549
|
+
const resolved = resolveSkillPath(raw, baseDir);
|
|
550
|
+
const key = skillPathKey(resolved);
|
|
551
|
+
if (seen.has(key)) continue;
|
|
552
|
+
seen.add(key);
|
|
553
|
+
out.push({ raw, resolved });
|
|
623
554
|
}
|
|
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
|
-
|
|
555
|
+
return out;
|
|
556
|
+
}
|
|
557
|
+
function resolveSkillPaths(paths, baseDir) {
|
|
558
|
+
return normalizeSkillPathEntries(paths, baseDir).map((entry) => entry.resolved);
|
|
559
|
+
}
|
|
560
|
+
function skillPathKey(path2) {
|
|
561
|
+
return process.platform === "win32" ? path2.toLowerCase() : path2;
|
|
562
|
+
}
|
|
563
|
+
function expandCurrentUserHome(path2) {
|
|
564
|
+
if (path2 === "~") return homedir();
|
|
565
|
+
if (path2.startsWith("~/") || path2.startsWith("~\\")) return join(homedir(), path2.slice(2));
|
|
566
|
+
return path2;
|
|
567
|
+
}
|
|
568
|
+
function loadResolvedSkillPaths(baseDir = process.cwd(), path2 = defaultConfigPath()) {
|
|
569
|
+
const raw = readConfig(path2).skills?.paths;
|
|
570
|
+
return Array.isArray(raw) ? resolveSkillPaths(raw, baseDir) : [];
|
|
571
|
+
}
|
|
572
|
+
function webSearchEngine(path2 = defaultConfigPath()) {
|
|
573
|
+
const cfg = readConfig(path2).webSearchEngine;
|
|
574
|
+
if (cfg === "searxng") return "searxng";
|
|
575
|
+
if (cfg === "metaso") return "metaso";
|
|
576
|
+
return "mojeek";
|
|
577
|
+
}
|
|
578
|
+
function webSearchEndpoint(path2 = defaultConfigPath()) {
|
|
579
|
+
const cfg = readConfig(path2).webSearchEndpoint;
|
|
580
|
+
if (cfg && typeof cfg === "string") return cfg;
|
|
581
|
+
return "http://localhost:8080";
|
|
582
|
+
}
|
|
583
|
+
function saveApiKey(key, path2 = defaultConfigPath()) {
|
|
584
|
+
const cfg = readConfig(path2);
|
|
585
|
+
cfg.apiKey = key.trim();
|
|
586
|
+
writeConfig(cfg, path2);
|
|
587
|
+
}
|
|
588
|
+
function findProjectKey(cfg, rootDir) {
|
|
589
|
+
const projects = cfg.projects;
|
|
590
|
+
if (!projects) return void 0;
|
|
591
|
+
if (Object.hasOwn(projects, rootDir)) return rootDir;
|
|
592
|
+
if (process.platform !== "win32") return void 0;
|
|
593
|
+
const lower = rootDir.toLowerCase();
|
|
594
|
+
for (const k of Object.keys(projects)) {
|
|
595
|
+
if (k.toLowerCase() === lower) return k;
|
|
656
596
|
}
|
|
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
|
-
|
|
597
|
+
return void 0;
|
|
598
|
+
}
|
|
599
|
+
function addProjectShellAllowed(rootDir, prefix, path2 = defaultConfigPath()) {
|
|
600
|
+
const trimmed = prefix.trim();
|
|
601
|
+
if (!trimmed) return;
|
|
602
|
+
const cfg = readConfig(path2);
|
|
603
|
+
if (!cfg.projects) cfg.projects = {};
|
|
604
|
+
const key = findProjectKey(cfg, rootDir) ?? rootDir;
|
|
605
|
+
if (!cfg.projects[key]) cfg.projects[key] = {};
|
|
606
|
+
const existing = cfg.projects[key].shellAllowed ?? [];
|
|
607
|
+
if (existing.includes(trimmed)) return;
|
|
608
|
+
cfg.projects[key].shellAllowed = [...existing, trimmed];
|
|
609
|
+
writeConfig(cfg, path2);
|
|
610
|
+
}
|
|
611
|
+
function loadProjectPathAllowed(rootDir, path2 = defaultConfigPath()) {
|
|
612
|
+
const cfg = readConfig(path2);
|
|
613
|
+
const key = findProjectKey(cfg, rootDir);
|
|
614
|
+
if (key === void 0) return [];
|
|
615
|
+
return cfg.projects?.[key]?.pathAllowed ?? [];
|
|
616
|
+
}
|
|
617
|
+
function addProjectPathAllowed(rootDir, prefix, path2 = defaultConfigPath()) {
|
|
618
|
+
const trimmed = prefix.trim();
|
|
619
|
+
if (!trimmed) return;
|
|
620
|
+
const cfg = readConfig(path2);
|
|
621
|
+
if (!cfg.projects) cfg.projects = {};
|
|
622
|
+
const key = findProjectKey(cfg, rootDir) ?? rootDir;
|
|
623
|
+
if (!cfg.projects[key]) cfg.projects[key] = {};
|
|
624
|
+
const existing = cfg.projects[key].pathAllowed ?? [];
|
|
625
|
+
if (existing.includes(trimmed)) return;
|
|
626
|
+
cfg.projects[key].pathAllowed = [...existing, trimmed];
|
|
627
|
+
writeConfig(cfg, path2);
|
|
628
|
+
}
|
|
629
|
+
function isPlausibleKey(key) {
|
|
630
|
+
const trimmed = key.trim();
|
|
631
|
+
if (trimmed.length < 16) return false;
|
|
632
|
+
return !/\s/.test(trimmed);
|
|
633
|
+
}
|
|
634
|
+
function redactKey(key) {
|
|
635
|
+
if (!key) return "";
|
|
636
|
+
if (key.length <= 12) return "****";
|
|
637
|
+
return `${key.slice(0, 6)}\u2026${key.slice(-4)}`;
|
|
638
|
+
}
|
|
639
|
+
function isPlainObject(value) {
|
|
640
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
641
|
+
const proto = Object.getPrototypeOf(value);
|
|
642
|
+
return proto === Object.prototype || proto === null;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// src/retry.ts
|
|
646
|
+
var DEFAULT_RETRYABLE_STATUSES = [408, 429, 500, 502, 503, 504];
|
|
647
|
+
async function fetchWithRetry(fetchFn, url, init, opts = {}) {
|
|
648
|
+
const maxAttempts = opts.maxAttempts ?? 4;
|
|
649
|
+
const initial = opts.initialBackoffMs ?? 500;
|
|
650
|
+
const cap = opts.maxBackoffMs ?? 1e4;
|
|
651
|
+
const retryable = new Set(opts.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES);
|
|
652
|
+
let lastError;
|
|
653
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
654
|
+
if (opts.signal?.aborted) throw new Error("aborted");
|
|
655
|
+
try {
|
|
656
|
+
const resp = await fetchFn(url, init);
|
|
657
|
+
if (resp.ok || !retryable.has(resp.status)) return resp;
|
|
658
|
+
if (attempt === maxAttempts - 1) return resp;
|
|
659
|
+
await resp.text().catch(() => void 0);
|
|
660
|
+
const waitMs = computeWait(attempt, initial, cap, resp.headers.get("Retry-After"));
|
|
661
|
+
opts.onRetry?.({ attempt: attempt + 1, reason: `http ${resp.status}`, waitMs });
|
|
662
|
+
await sleep(waitMs, opts.signal);
|
|
663
|
+
} catch (err) {
|
|
664
|
+
lastError = err;
|
|
665
|
+
if (isAbortError(err) || opts.signal?.aborted) throw err;
|
|
666
|
+
if (attempt === maxAttempts - 1) throw err;
|
|
667
|
+
const waitMs = computeWait(attempt, initial, cap, null);
|
|
668
|
+
opts.onRetry?.({
|
|
669
|
+
attempt: attempt + 1,
|
|
670
|
+
reason: `network: ${messageOf(err)}`,
|
|
671
|
+
waitMs
|
|
672
|
+
});
|
|
673
|
+
await sleep(waitMs, opts.signal);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
throw lastError ?? new Error("fetchWithRetry: loop exited unexpectedly");
|
|
677
|
+
}
|
|
678
|
+
function computeWait(attempt, initial, cap, retryAfter) {
|
|
679
|
+
if (retryAfter) {
|
|
680
|
+
const seconds = Number.parseFloat(retryAfter);
|
|
681
|
+
if (Number.isFinite(seconds) && seconds > 0) {
|
|
682
|
+
return Math.min(seconds * 1e3, cap);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
const exp = initial * 2 ** attempt;
|
|
686
|
+
const jitter = exp * (0.75 + Math.random() * 0.5);
|
|
687
|
+
return Math.min(Math.max(jitter, 0), cap);
|
|
688
|
+
}
|
|
689
|
+
function sleep(ms, signal) {
|
|
690
|
+
if (ms <= 0) return Promise.resolve();
|
|
691
|
+
return new Promise((resolve13, reject) => {
|
|
692
|
+
const timer = setTimeout(resolve13, ms);
|
|
693
|
+
if (signal) {
|
|
694
|
+
const onAbort = () => {
|
|
695
|
+
clearTimeout(timer);
|
|
696
|
+
reject(new Error("aborted"));
|
|
697
|
+
};
|
|
698
|
+
if (signal.aborted) onAbort();
|
|
699
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
684
700
|
}
|
|
685
701
|
});
|
|
686
702
|
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
703
|
+
function isAbortError(err) {
|
|
704
|
+
if (!err || typeof err !== "object") return false;
|
|
705
|
+
const name = err.name;
|
|
706
|
+
return name === "AbortError";
|
|
707
|
+
}
|
|
708
|
+
function messageOf(err) {
|
|
709
|
+
if (err instanceof Error) return err.message;
|
|
710
|
+
try {
|
|
711
|
+
return String(err);
|
|
712
|
+
} catch {
|
|
713
|
+
return "unknown error";
|
|
714
|
+
}
|
|
715
|
+
}
|
|
692
716
|
|
|
693
|
-
// src/
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
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
|
-
]
|
|
717
|
+
// src/client.ts
|
|
718
|
+
var Usage = class _Usage {
|
|
719
|
+
constructor(promptTokens = 0, completionTokens = 0, totalTokens = 0, promptCacheHitTokens = 0, promptCacheMissTokens = 0) {
|
|
720
|
+
this.promptTokens = promptTokens;
|
|
721
|
+
this.completionTokens = completionTokens;
|
|
722
|
+
this.totalTokens = totalTokens;
|
|
723
|
+
this.promptCacheHitTokens = promptCacheHitTokens;
|
|
724
|
+
this.promptCacheMissTokens = promptCacheMissTokens;
|
|
725
|
+
}
|
|
726
|
+
promptTokens;
|
|
727
|
+
completionTokens;
|
|
728
|
+
totalTokens;
|
|
729
|
+
promptCacheHitTokens;
|
|
730
|
+
promptCacheMissTokens;
|
|
731
|
+
get cacheHitRatio() {
|
|
732
|
+
const denom = this.promptCacheHitTokens + this.promptCacheMissTokens;
|
|
733
|
+
return denom > 0 ? this.promptCacheHitTokens / denom : 0;
|
|
734
|
+
}
|
|
735
|
+
static fromApi(raw) {
|
|
736
|
+
const u = raw ?? {};
|
|
737
|
+
const promptTokens = u.prompt_tokens ?? 0;
|
|
738
|
+
const cacheHitTokens = u.prompt_cache_hit_tokens ?? 0;
|
|
739
|
+
const cacheMissTokens = u.prompt_cache_miss_tokens ?? Math.max(0, promptTokens - cacheHitTokens);
|
|
740
|
+
return new _Usage(
|
|
741
|
+
promptTokens,
|
|
742
|
+
u.completion_tokens ?? 0,
|
|
743
|
+
u.total_tokens ?? 0,
|
|
744
|
+
cacheHitTokens,
|
|
745
|
+
cacheMissTokens
|
|
746
|
+
);
|
|
747
|
+
}
|
|
775
748
|
};
|
|
776
|
-
var
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
749
|
+
var DeepSeekClient = class {
|
|
750
|
+
apiKey;
|
|
751
|
+
baseUrl;
|
|
752
|
+
timeoutMs;
|
|
753
|
+
retry;
|
|
754
|
+
_fetch;
|
|
755
|
+
minChatIntervalMs;
|
|
756
|
+
nextChatRequestAt = 0;
|
|
757
|
+
constructor(opts = {}) {
|
|
758
|
+
const apiKey = opts.apiKey ?? process.env.DEEPSEEK_API_KEY;
|
|
759
|
+
if (!apiKey) {
|
|
760
|
+
throw new Error(
|
|
761
|
+
"DEEPSEEK_API_KEY is not set. Put it in .env or pass apiKey to DeepSeekClient."
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
this.apiKey = apiKey;
|
|
765
|
+
let url = opts.baseUrl ?? process.env.DEEPSEEK_BASE_URL ?? "https://api.deepseek.com";
|
|
766
|
+
while (url.endsWith("/")) url = url.slice(0, -1);
|
|
767
|
+
this.baseUrl = url;
|
|
768
|
+
this.timeoutMs = opts.timeoutMs ?? 66e4;
|
|
769
|
+
this._fetch = opts.fetch ?? globalThis.fetch.bind(globalThis);
|
|
770
|
+
this.retry = opts.retry ?? {};
|
|
771
|
+
const rpm = opts.rateLimit?.rpm ?? loadRateLimit()?.rpm;
|
|
772
|
+
this.minChatIntervalMs = rpm ? Math.ceil(6e4 / rpm) : 0;
|
|
773
|
+
}
|
|
774
|
+
async waitForChatRateLimit(signal) {
|
|
775
|
+
if (this.minChatIntervalMs <= 0) return;
|
|
776
|
+
const now = Date.now();
|
|
777
|
+
const waitMs = Math.max(0, this.nextChatRequestAt - now);
|
|
778
|
+
this.nextChatRequestAt = Math.max(now, this.nextChatRequestAt) + this.minChatIntervalMs;
|
|
779
|
+
if (waitMs <= 0) return;
|
|
780
|
+
await new Promise((resolve13, reject) => {
|
|
781
|
+
const timer = setTimeout(resolve13, waitMs);
|
|
782
|
+
signal?.addEventListener(
|
|
783
|
+
"abort",
|
|
784
|
+
() => {
|
|
785
|
+
clearTimeout(timer);
|
|
786
|
+
reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
787
|
+
},
|
|
788
|
+
{ once: true }
|
|
789
|
+
);
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
buildPayload(opts, stream) {
|
|
793
|
+
const payload = {
|
|
794
|
+
model: opts.model,
|
|
795
|
+
messages: opts.messages,
|
|
796
|
+
stream
|
|
797
|
+
};
|
|
798
|
+
if (opts.tools?.length) payload.tools = opts.tools;
|
|
799
|
+
if (opts.temperature !== void 0) payload.temperature = opts.temperature;
|
|
800
|
+
if (opts.maxTokens !== void 0) payload.max_tokens = opts.maxTokens;
|
|
801
|
+
if (opts.responseFormat) payload.response_format = opts.responseFormat;
|
|
802
|
+
if (opts.thinking) {
|
|
803
|
+
payload.extra_body = { thinking: { type: opts.thinking } };
|
|
804
|
+
}
|
|
805
|
+
if (opts.reasoningEffort) {
|
|
806
|
+
payload.reasoning_effort = opts.reasoningEffort;
|
|
807
|
+
}
|
|
808
|
+
return payload;
|
|
809
|
+
}
|
|
810
|
+
/** Returns null on failure so callers can degrade — session must keep working without balance UI. */
|
|
811
|
+
async getBalance(opts = {}) {
|
|
812
|
+
try {
|
|
813
|
+
const resp = await this._fetch(`${this.baseUrl}/user/balance`, {
|
|
814
|
+
method: "GET",
|
|
815
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
816
|
+
signal: opts.signal
|
|
817
|
+
});
|
|
818
|
+
if (!resp.ok) return null;
|
|
819
|
+
const data = await resp.json();
|
|
820
|
+
if (!data || !Array.isArray(data.balance_infos)) return null;
|
|
821
|
+
return data;
|
|
822
|
+
} catch {
|
|
823
|
+
return null;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
/** Returns null on failure — callers fall back to a hardcoded model hint. */
|
|
827
|
+
async listModels(opts = {}) {
|
|
828
|
+
try {
|
|
829
|
+
const resp = await this._fetch(`${this.baseUrl}/models`, {
|
|
830
|
+
method: "GET",
|
|
831
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
832
|
+
signal: opts.signal
|
|
833
|
+
});
|
|
834
|
+
if (!resp.ok) return null;
|
|
835
|
+
const data = await resp.json();
|
|
836
|
+
if (!data || !Array.isArray(data.data)) return null;
|
|
837
|
+
return data;
|
|
838
|
+
} catch {
|
|
839
|
+
return null;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
async chat(opts) {
|
|
843
|
+
const ctrl = new AbortController();
|
|
844
|
+
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
845
|
+
const signal = opts.signal ?? ctrl.signal;
|
|
846
|
+
try {
|
|
847
|
+
await this.waitForChatRateLimit(signal);
|
|
848
|
+
const resp = await fetchWithRetry(
|
|
849
|
+
this._fetch,
|
|
850
|
+
`${this.baseUrl}/chat/completions`,
|
|
851
|
+
{
|
|
852
|
+
method: "POST",
|
|
853
|
+
headers: {
|
|
854
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
855
|
+
"Content-Type": "application/json"
|
|
856
|
+
},
|
|
857
|
+
body: JSON.stringify(this.buildPayload(opts, false)),
|
|
858
|
+
signal
|
|
859
|
+
},
|
|
860
|
+
{ ...this.retry, signal }
|
|
861
|
+
);
|
|
862
|
+
if (!resp.ok) {
|
|
863
|
+
throw new Error(`DeepSeek ${resp.status}: ${await resp.text()}`);
|
|
797
864
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
865
|
+
const data = await resp.json();
|
|
866
|
+
const choice = data.choices?.[0]?.message ?? {};
|
|
867
|
+
return {
|
|
868
|
+
content: choice.content ?? "",
|
|
869
|
+
reasoningContent: choice.reasoning_content ?? null,
|
|
870
|
+
toolCalls: choice.tool_calls ?? [],
|
|
871
|
+
usage: Usage.fromApi(data.usage),
|
|
872
|
+
raw: data
|
|
873
|
+
};
|
|
874
|
+
} finally {
|
|
875
|
+
clearTimeout(timer);
|
|
801
876
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
877
|
+
}
|
|
878
|
+
async *stream(opts) {
|
|
879
|
+
const ctrl = new AbortController();
|
|
880
|
+
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
881
|
+
const signal = opts.signal ?? ctrl.signal;
|
|
882
|
+
let resp;
|
|
883
|
+
try {
|
|
884
|
+
await this.waitForChatRateLimit(signal);
|
|
885
|
+
resp = await fetchWithRetry(
|
|
886
|
+
this._fetch,
|
|
887
|
+
`${this.baseUrl}/chat/completions`,
|
|
888
|
+
{
|
|
889
|
+
method: "POST",
|
|
890
|
+
headers: {
|
|
891
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
892
|
+
"Content-Type": "application/json",
|
|
893
|
+
Accept: "text/event-stream"
|
|
894
|
+
},
|
|
895
|
+
body: JSON.stringify(this.buildPayload(opts, true)),
|
|
896
|
+
signal
|
|
897
|
+
},
|
|
898
|
+
{ ...this.retry, signal }
|
|
899
|
+
);
|
|
900
|
+
} catch (err) {
|
|
901
|
+
clearTimeout(timer);
|
|
902
|
+
throw err;
|
|
806
903
|
}
|
|
807
|
-
if (
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
904
|
+
if (!resp.ok || !resp.body) {
|
|
905
|
+
clearTimeout(timer);
|
|
906
|
+
throw new Error(`DeepSeek ${resp.status}: ${await resp.text().catch(() => "")}`);
|
|
907
|
+
}
|
|
908
|
+
const queue = [];
|
|
909
|
+
let done = false;
|
|
910
|
+
const parser = createParser({
|
|
911
|
+
onEvent: (ev) => {
|
|
912
|
+
if (!ev.data || ev.data === "[DONE]") {
|
|
913
|
+
done = true;
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
try {
|
|
917
|
+
const json = JSON.parse(ev.data);
|
|
918
|
+
const delta = json.choices?.[0]?.delta ?? {};
|
|
919
|
+
const finishReason = json.choices?.[0]?.finish_reason ?? void 0;
|
|
920
|
+
const chunk = { raw: json, finishReason };
|
|
921
|
+
if (typeof delta.content === "string" && delta.content.length > 0) {
|
|
922
|
+
chunk.contentDelta = delta.content;
|
|
923
|
+
}
|
|
924
|
+
if (typeof delta.reasoning_content === "string" && delta.reasoning_content.length > 0) {
|
|
925
|
+
chunk.reasoningDelta = delta.reasoning_content;
|
|
926
|
+
}
|
|
927
|
+
if (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) {
|
|
928
|
+
const tc = delta.tool_calls[0];
|
|
929
|
+
chunk.toolCallDelta = {
|
|
930
|
+
index: tc.index ?? 0,
|
|
931
|
+
id: tc.id,
|
|
932
|
+
name: tc.function?.name,
|
|
933
|
+
argumentsDelta: tc.function?.arguments
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
if (json.usage) {
|
|
937
|
+
chunk.usage = Usage.fromApi(json.usage);
|
|
938
|
+
}
|
|
939
|
+
queue.push(chunk);
|
|
940
|
+
} catch {
|
|
941
|
+
}
|
|
811
942
|
}
|
|
812
|
-
|
|
813
|
-
|
|
943
|
+
});
|
|
944
|
+
const reader = resp.body.getReader();
|
|
945
|
+
const decoder = new TextDecoder();
|
|
946
|
+
try {
|
|
947
|
+
while (true) {
|
|
948
|
+
if (queue.length > 0) {
|
|
949
|
+
yield queue.shift();
|
|
950
|
+
continue;
|
|
951
|
+
}
|
|
952
|
+
if (done) break;
|
|
953
|
+
const { value, done: streamDone } = await reader.read();
|
|
954
|
+
if (streamDone) break;
|
|
955
|
+
parser.feed(decoder.decode(value, { stream: true }));
|
|
956
|
+
}
|
|
957
|
+
while (queue.length > 0) yield queue.shift();
|
|
958
|
+
} finally {
|
|
959
|
+
clearTimeout(timer);
|
|
960
|
+
reader.releaseLock();
|
|
814
961
|
}
|
|
815
|
-
cur += ch;
|
|
816
|
-
i++;
|
|
817
|
-
}
|
|
818
|
-
if (quote) {
|
|
819
|
-
throw new Error(
|
|
820
|
-
`shellSplit: unterminated ${quote === '"' ? "double" : "single"} quote in input`
|
|
821
|
-
);
|
|
822
|
-
}
|
|
823
|
-
if (cur.length > 0) tokens.push(cur);
|
|
824
|
-
return tokens;
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
// src/mcp/spec.ts
|
|
828
|
-
var NAME_PREFIX = /^([a-zA-Z_][a-zA-Z0-9_-]*)=(.*)$/;
|
|
829
|
-
var HTTP_URL = /^https?:\/\//i;
|
|
830
|
-
var STREAMABLE_PREFIX = /^streamable\+(https?:\/\/.+)$/i;
|
|
831
|
-
function parseMcpSpec(input) {
|
|
832
|
-
const trimmed = input.trim();
|
|
833
|
-
if (!trimmed) {
|
|
834
|
-
throw new Error("empty MCP spec");
|
|
835
|
-
}
|
|
836
|
-
const nameMatch = NAME_PREFIX.exec(trimmed);
|
|
837
|
-
const name = nameMatch ? nameMatch[1] : null;
|
|
838
|
-
const body = (nameMatch ? nameMatch[2] : trimmed).trim();
|
|
839
|
-
if (!body) {
|
|
840
|
-
throw new Error(`MCP spec has name but no command: ${input}`);
|
|
841
|
-
}
|
|
842
|
-
const streamMatch = STREAMABLE_PREFIX.exec(body);
|
|
843
|
-
if (streamMatch) {
|
|
844
|
-
return { transport: "streamable-http", name, url: streamMatch[1] };
|
|
845
|
-
}
|
|
846
|
-
if (HTTP_URL.test(body)) {
|
|
847
|
-
return { transport: "sse", name, url: body };
|
|
848
|
-
}
|
|
849
|
-
const argv = shellSplit(body);
|
|
850
|
-
if (argv.length === 0) {
|
|
851
|
-
throw new Error(`MCP spec has name but no command: ${input}`);
|
|
852
962
|
}
|
|
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
963
|
};
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
entry.priority = raw.priority;
|
|
964
|
+
|
|
965
|
+
// src/core/pause-gate.ts
|
|
966
|
+
var PauseGate = class {
|
|
967
|
+
_nextId = 0;
|
|
968
|
+
_pending = /* @__PURE__ */ new Map();
|
|
969
|
+
_listeners = /* @__PURE__ */ new Set();
|
|
970
|
+
_auditListener = null;
|
|
971
|
+
/** Block until the user responds. Takes a named options object so the
|
|
972
|
+
* kind and payload fields don't get confused at the call site. */
|
|
973
|
+
ask(opts) {
|
|
974
|
+
const { kind, payload } = opts;
|
|
975
|
+
if (this._listeners.size === 0) {
|
|
976
|
+
throw new Error(
|
|
977
|
+
`${kind}: no confirmation listener registered \u2014 cannot prompt the user. This tool can only be used inside an interactive Reasonix session.`
|
|
978
|
+
);
|
|
880
979
|
}
|
|
881
|
-
|
|
882
|
-
|
|
980
|
+
return new Promise((resolve13) => {
|
|
981
|
+
const id = this._nextId++;
|
|
982
|
+
const request = { id, kind, payload };
|
|
983
|
+
this._pending.set(id, { resolve: resolve13, request });
|
|
984
|
+
for (const fn of this._listeners) {
|
|
985
|
+
try {
|
|
986
|
+
fn(request);
|
|
987
|
+
} catch {
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
});
|
|
883
991
|
}
|
|
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 {
|
|
992
|
+
/** Resolve a pending request. Called by the App's modal callback. */
|
|
993
|
+
resolve(id, data) {
|
|
994
|
+
const p = this._pending.get(id);
|
|
995
|
+
if (!p) return;
|
|
996
|
+
this._pending.delete(id);
|
|
997
|
+
this.emitAuditEvent(p.request, data);
|
|
998
|
+
p.resolve(data);
|
|
910
999
|
}
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
1000
|
+
/** Safe-cancel every outstanding request — frees stranded tool fns on Esc / /new. */
|
|
1001
|
+
cancelAll() {
|
|
1002
|
+
const ids = [...this._pending.keys()];
|
|
1003
|
+
for (const id of ids) {
|
|
1004
|
+
const p = this._pending.get(id);
|
|
1005
|
+
if (!p) continue;
|
|
1006
|
+
this._pending.delete(id);
|
|
1007
|
+
p.resolve(safeCancelVerdict(p.request.kind));
|
|
1008
|
+
}
|
|
919
1009
|
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
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;
|
|
1010
|
+
/** Cancel one pending request — used by multi-tab hosts that need per-scope abort. */
|
|
1011
|
+
cancel(id) {
|
|
1012
|
+
const p = this._pending.get(id);
|
|
1013
|
+
if (!p) return false;
|
|
1014
|
+
this._pending.delete(id);
|
|
1015
|
+
p.resolve(safeCancelVerdict(p.request.kind));
|
|
1016
|
+
return true;
|
|
939
1017
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
function resolveSkillPath(raw, baseDir) {
|
|
943
|
-
const homeExpanded = expandCurrentUserHome(raw.trim());
|
|
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 });
|
|
1018
|
+
setAuditListener(fn) {
|
|
1019
|
+
this._auditListener = fn;
|
|
958
1020
|
}
|
|
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
|
-
|
|
1021
|
+
/** Subscribe to new pause requests. Returns an unsubscribe function. */
|
|
1022
|
+
on(fn) {
|
|
1023
|
+
this._listeners.add(fn);
|
|
1024
|
+
return () => {
|
|
1025
|
+
this._listeners.delete(fn);
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
/** Current pending request, if any (polling fallback). */
|
|
1029
|
+
get current() {
|
|
1030
|
+
for (const [, p] of this._pending) return p.request;
|
|
1031
|
+
return null;
|
|
1032
|
+
}
|
|
1033
|
+
emitAuditEvent(request, data) {
|
|
1034
|
+
if (!this._auditListener) return;
|
|
1035
|
+
if (request.kind !== "run_command" && request.kind !== "run_background") return;
|
|
1036
|
+
if (!data || typeof data !== "object") return;
|
|
1037
|
+
const choice = data;
|
|
1038
|
+
try {
|
|
1039
|
+
switch (choice.type) {
|
|
1040
|
+
case "run_once":
|
|
1041
|
+
this._auditListener({
|
|
1042
|
+
type: "tool.confirm.allow",
|
|
1043
|
+
kind: request.kind,
|
|
1044
|
+
payload: request.payload
|
|
1045
|
+
});
|
|
1046
|
+
break;
|
|
1047
|
+
case "deny":
|
|
1048
|
+
this._auditListener({
|
|
1049
|
+
type: "tool.confirm.deny",
|
|
1050
|
+
kind: request.kind,
|
|
1051
|
+
payload: request.payload,
|
|
1052
|
+
denyContext: choice.denyContext
|
|
1053
|
+
});
|
|
1054
|
+
break;
|
|
1055
|
+
case "always_allow":
|
|
1056
|
+
if (typeof choice.prefix !== "string") return;
|
|
1057
|
+
this._auditListener({
|
|
1058
|
+
type: "tool.confirm.always_allow",
|
|
1059
|
+
kind: request.kind,
|
|
1060
|
+
payload: request.payload,
|
|
1061
|
+
prefix: choice.prefix
|
|
1062
|
+
});
|
|
1063
|
+
break;
|
|
1064
|
+
default:
|
|
1065
|
+
break;
|
|
1066
|
+
}
|
|
1067
|
+
} catch {
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
};
|
|
1071
|
+
function safeCancelVerdict(kind) {
|
|
1072
|
+
switch (kind) {
|
|
1073
|
+
case "run_command":
|
|
1074
|
+
case "run_background":
|
|
1075
|
+
case "path_access":
|
|
1076
|
+
return { type: "deny" };
|
|
1077
|
+
case "plan_proposed":
|
|
1078
|
+
return { type: "cancel" };
|
|
1079
|
+
case "plan_checkpoint":
|
|
1080
|
+
return { type: "stop" };
|
|
1081
|
+
case "plan_revision":
|
|
1082
|
+
return { type: "cancelled" };
|
|
1083
|
+
case "choice":
|
|
1084
|
+
return { type: "cancel" };
|
|
1000
1085
|
}
|
|
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
1086
|
}
|
|
1087
|
+
var pauseGate = new PauseGate();
|
|
1088
|
+
|
|
1089
|
+
// src/hooks.ts
|
|
1090
|
+
import { spawn } from "child_process";
|
|
1091
|
+
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
1092
|
+
import { homedir as homedir2 } from "os";
|
|
1093
|
+
import { join as join2 } from "path";
|
|
1043
1094
|
|
|
1044
1095
|
// src/i18n/EN.ts
|
|
1045
1096
|
var EN = {
|
|
@@ -1349,7 +1400,7 @@ var EN = {
|
|
|
1349
1400
|
},
|
|
1350
1401
|
cwd: {
|
|
1351
1402
|
description: "switch the workspace root mid-session \u2014 re-points fs / shell / memory tools, reloads project hooks, refreshes the at-mention walker",
|
|
1352
|
-
argsHint: "
|
|
1403
|
+
argsHint: "[path]"
|
|
1353
1404
|
},
|
|
1354
1405
|
stop: { description: "abort the current model turn (typed alternative to Esc)" },
|
|
1355
1406
|
feedback: { description: "open a GitHub issue with diagnostic info copied to clipboard" },
|
|
@@ -1603,6 +1654,10 @@ var EN = {
|
|
|
1603
1654
|
sessionTitleRenameFailed: '\u25B8 could not rename the session for title "{title}".',
|
|
1604
1655
|
sessionTitleRenamed: '\u25B8 session renamed to "{name}" \u2014 {title}',
|
|
1605
1656
|
sessionTitleAutoRenamed: '\u25B8 auto-named session "{name}" \u2014 {title}',
|
|
1657
|
+
workspaceSwitched: "\u25B8 workspace switched to {root}",
|
|
1658
|
+
semanticRepointed: "\u25B8 semantic_search re-pointed at {root}",
|
|
1659
|
+
semanticDisabledForRoot: "\u25B8 semantic_search disabled (no compatible index in {root})",
|
|
1660
|
+
semanticRebootstrapFailed: "\u25B8 semantic_search re-bootstrap failed: {reason}",
|
|
1606
1661
|
denied: "\u25B8 denied: {cmd}{context}",
|
|
1607
1662
|
alwaysAllowed: '\u25B8 always allowed "{prefix}" for {dir}',
|
|
1608
1663
|
runningCommand: "\u25B8 running: {cmd}",
|
|
@@ -1639,7 +1694,6 @@ var EN = {
|
|
|
1639
1694
|
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
1695
|
flashEscalation: "\u21E7 flash requested escalation \u2014 retrying this turn on {model}{reasonSuffix}",
|
|
1641
1696
|
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
1697
|
repeatToolCallWarning: "Caught a repeated tool call \u2014 let the model see the issue and retry with a different approach.",
|
|
1644
1698
|
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
1699
|
stormSuppressed: "Suppressed {count} repeated tool call(s) \u2014 same name + args fired 3+ times.",
|
|
@@ -2130,11 +2184,14 @@ var EN = {
|
|
|
2130
2184
|
messages: "{count} message",
|
|
2131
2185
|
messagesPlural: "{count} messages",
|
|
2132
2186
|
turns: "{count} turns",
|
|
2133
|
-
pickerHint: "\u2191\u2193 pick \xB7 \u23CE open \xB7 [n] new \xB7 [d] delete \xB7 [r] rename \xB7 esc quit",
|
|
2187
|
+
pickerHint: "\u2191\u2193 pick \xB7 / search \xB7 \u23CE open \xB7 [n] new \xB7 [d] delete \xB7 [r] rename \xB7 esc quit",
|
|
2134
2188
|
empty: " no saved sessions in this workspace yet \u2014 press ",
|
|
2135
2189
|
emptyNew: " to start a new one",
|
|
2136
2190
|
renamePrompt: ' rename "{from}" \u2192 ',
|
|
2137
2191
|
renameHint: " \u23CE confirm rename \xB7 esc cancel",
|
|
2192
|
+
searchPrompt: " search sessions: /",
|
|
2193
|
+
searchHint: " type to filter \xB7 \u23CE open match \xB7 esc clear",
|
|
2194
|
+
searchEmpty: " no sessions match this search",
|
|
2138
2195
|
emptyHint: " \u23CE new session \xB7 esc quit",
|
|
2139
2196
|
justNow: "just now",
|
|
2140
2197
|
minAgo: "{count} min ago",
|
|
@@ -2142,6 +2199,18 @@ var EN = {
|
|
|
2142
2199
|
hoursAgo: "{count}h ago",
|
|
2143
2200
|
daysAgo: "{count} days ago"
|
|
2144
2201
|
},
|
|
2202
|
+
workspacePicker: {
|
|
2203
|
+
header: " \u25C8 REASONIX \xB7 pick a workspace ",
|
|
2204
|
+
title: "pick a workspace \u2014 {workspace}",
|
|
2205
|
+
sessions: "{count} session",
|
|
2206
|
+
sessionsPlural: "{count} sessions",
|
|
2207
|
+
current: "current",
|
|
2208
|
+
pickerHint: "\u2191\u2193 pick \xB7 / search \xB7 \u23CE switch + pick session \xB7 esc quit \xB7 /cwd <path> adds one",
|
|
2209
|
+
empty: " no known workspaces yet \u2014 run /cwd <path> once to add one",
|
|
2210
|
+
searchPrompt: " search workspaces: /",
|
|
2211
|
+
searchHint: " type to filter \xB7 \u23CE switch + pick session \xB7 esc clear",
|
|
2212
|
+
searchEmpty: " no workspaces match this search"
|
|
2213
|
+
},
|
|
2145
2214
|
modelPicker: {
|
|
2146
2215
|
header: " \u25C8 REASONIX \xB7 pick a setup ",
|
|
2147
2216
|
loading: " \xB7 loading catalog\u2026",
|
|
@@ -2775,7 +2844,7 @@ var zhCN = {
|
|
|
2775
2844
|
keys: { description: "\u952E\u76D8 + \u9F20\u6807 + \u590D\u5236\u7C98\u8D34\u53C2\u8003" },
|
|
2776
2845
|
cwd: {
|
|
2777
2846
|
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: "
|
|
2847
|
+
argsHint: "[path]"
|
|
2779
2848
|
},
|
|
2780
2849
|
stop: { description: "\u4E2D\u6B62\u5F53\u524D\u6A21\u578B\u56DE\u5408\uFF08\u6309 Esc \u7684\u66FF\u4EE3\u65B9\u5F0F\uFF09" },
|
|
2781
2850
|
feedback: { description: "\u6253\u5F00 GitHub Issue\uFF0C\u8BCA\u65AD\u4FE1\u606F\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F" },
|
|
@@ -3030,6 +3099,10 @@ var zhCN = {
|
|
|
3030
3099
|
sessionTitleRenameFailed: '\u25B8 \u65E0\u6CD5\u6309\u6807\u9898 "{title}" \u91CD\u547D\u540D\u4F1A\u8BDD\u3002',
|
|
3031
3100
|
sessionTitleRenamed: '\u25B8 \u4F1A\u8BDD\u5DF2\u91CD\u547D\u540D\u4E3A "{name}" \u2014 {title}',
|
|
3032
3101
|
sessionTitleAutoRenamed: '\u25B8 \u5DF2\u81EA\u52A8\u547D\u540D\u4F1A\u8BDD "{name}" \u2014 {title}',
|
|
3102
|
+
workspaceSwitched: "\u25B8 \u5DE5\u4F5C\u533A\u5DF2\u5207\u6362\u5230 {root}",
|
|
3103
|
+
semanticRepointed: "\u25B8 semantic_search \u5DF2\u6307\u5411 {root}",
|
|
3104
|
+
semanticDisabledForRoot: "\u25B8 semantic_search \u5DF2\u7981\u7528\uFF08{root} \u6CA1\u6709\u517C\u5BB9\u7D22\u5F15\uFF09",
|
|
3105
|
+
semanticRebootstrapFailed: "\u25B8 semantic_search \u91CD\u65B0\u521D\u59CB\u5316\u5931\u8D25\uFF1A{reason}",
|
|
3033
3106
|
denied: "\u25B8 \u5DF2\u62D2\u7EDD\uFF1A{cmd}{context}",
|
|
3034
3107
|
alwaysAllowed: '\u25B8 \u5DF2\u5BF9 {dir} \u6C38\u4E45\u5141\u8BB8 "{prefix}"',
|
|
3035
3108
|
runningCommand: "\u25B8 \u6B63\u5728\u6267\u884C\uFF1A{cmd}",
|
|
@@ -3066,7 +3139,6 @@ var zhCN = {
|
|
|
3066
3139
|
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
3140
|
flashEscalation: "\u21E7 flash \u8BF7\u6C42\u5347\u7EA7 \u2014 \u672C\u8F6E\u6539\u7528 {model}{reasonSuffix}",
|
|
3068
3141
|
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
3142
|
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
3143
|
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
3144
|
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",
|
|
@@ -3557,11 +3629,14 @@ var zhCN = {
|
|
|
3557
3629
|
messages: "{count} \u6761\u6D88\u606F",
|
|
3558
3630
|
messagesPlural: "{count} \u6761\u6D88\u606F",
|
|
3559
3631
|
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",
|
|
3632
|
+
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
3633
|
empty: " \u6B64\u5DE5\u4F5C\u533A\u6682\u65E0\u5DF2\u4FDD\u5B58\u7684\u4F1A\u8BDD \u2014 \u6309 ",
|
|
3562
3634
|
emptyNew: " \u5F00\u59CB\u65B0\u4F1A\u8BDD",
|
|
3563
3635
|
renamePrompt: ' \u91CD\u547D\u540D "{from}" \u2192 ',
|
|
3564
3636
|
renameHint: " \u23CE \u786E\u8BA4\u91CD\u547D\u540D \xB7 Esc \u53D6\u6D88",
|
|
3637
|
+
searchPrompt: " \u641C\u7D22\u4F1A\u8BDD\uFF1A/",
|
|
3638
|
+
searchHint: " \u8F93\u5165\u8FC7\u6EE4 \xB7 \u23CE \u6253\u5F00\u5339\u914D\u9879 \xB7 Esc \u6E05\u9664",
|
|
3639
|
+
searchEmpty: " \u6CA1\u6709\u5339\u914D\u7684\u4F1A\u8BDD",
|
|
3565
3640
|
emptyHint: " \u23CE \u65B0\u5EFA\u4F1A\u8BDD \xB7 Esc \u9000\u51FA",
|
|
3566
3641
|
justNow: "\u521A\u521A",
|
|
3567
3642
|
minAgo: "{count} \u5206\u949F\u524D",
|
|
@@ -3569,6 +3644,18 @@ var zhCN = {
|
|
|
3569
3644
|
hoursAgo: "{count} \u5C0F\u65F6\u524D",
|
|
3570
3645
|
daysAgo: "{count} \u5929\u524D"
|
|
3571
3646
|
},
|
|
3647
|
+
workspacePicker: {
|
|
3648
|
+
header: " \u25C8 REASONIX \xB7 \u9009\u62E9\u5DE5\u4F5C\u533A ",
|
|
3649
|
+
title: "\u9009\u62E9\u5DE5\u4F5C\u533A \u2014 {workspace}",
|
|
3650
|
+
sessions: "{count} \u4E2A\u4F1A\u8BDD",
|
|
3651
|
+
sessionsPlural: "{count} \u4E2A\u4F1A\u8BDD",
|
|
3652
|
+
current: "\u5F53\u524D",
|
|
3653
|
+
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",
|
|
3654
|
+
empty: " \u6682\u65E0\u5DF2\u77E5\u5DE5\u4F5C\u533A \u2014 \u5148\u8FD0\u884C\u4E00\u6B21 /cwd <path> \u6DFB\u52A0",
|
|
3655
|
+
searchPrompt: " \u641C\u7D22\u5DE5\u4F5C\u533A\uFF1A/",
|
|
3656
|
+
searchHint: " \u8F93\u5165\u8FC7\u6EE4 \xB7 \u23CE \u5207\u6362\u5E76\u9009\u62E9\u4F1A\u8BDD \xB7 Esc \u6E05\u9664",
|
|
3657
|
+
searchEmpty: " \u6CA1\u6709\u5339\u914D\u7684\u5DE5\u4F5C\u533A"
|
|
3658
|
+
},
|
|
3572
3659
|
modelPicker: {
|
|
3573
3660
|
header: " \u25C8 REASONIX \xB7 \u9009\u62E9\u914D\u7F6E ",
|
|
3574
3661
|
loading: " \xB7 \u52A0\u8F7D\u76EE\u5F55\u2026",
|
|
@@ -5249,6 +5336,16 @@ var DEEPSEEK_PRICING = {
|
|
|
5249
5336
|
"deepseek-chat": { inputCacheHit: 28e-4, inputCacheMiss: 0.14, output: 0.28 },
|
|
5250
5337
|
"deepseek-reasoner": { inputCacheHit: 28e-4, inputCacheMiss: 0.14, output: 0.28 }
|
|
5251
5338
|
};
|
|
5339
|
+
function pricingFor(model, path2) {
|
|
5340
|
+
const defaults = DEEPSEEK_PRICING[model];
|
|
5341
|
+
const override = loadPricingOverride(path2)[model];
|
|
5342
|
+
if (!override) return defaults;
|
|
5343
|
+
const pricing = { ...defaults, ...override };
|
|
5344
|
+
if (pricing.inputCacheHit === void 0 || pricing.inputCacheMiss === void 0 || pricing.output === void 0) {
|
|
5345
|
+
return void 0;
|
|
5346
|
+
}
|
|
5347
|
+
return pricing;
|
|
5348
|
+
}
|
|
5252
5349
|
var CLAUDE_SONNET_PRICING = { input: 3, output: 15 };
|
|
5253
5350
|
var DEEPSEEK_CONTEXT_TOKENS = {
|
|
5254
5351
|
"deepseek-v4-flash": 1e6,
|
|
@@ -5257,24 +5354,24 @@ var DEEPSEEK_CONTEXT_TOKENS = {
|
|
|
5257
5354
|
"deepseek-reasoner": 1e6
|
|
5258
5355
|
};
|
|
5259
5356
|
var DEFAULT_CONTEXT_TOKENS = 131072;
|
|
5260
|
-
function costUsd(model, usage) {
|
|
5261
|
-
const p =
|
|
5357
|
+
function costUsd(model, usage, path2) {
|
|
5358
|
+
const p = pricingFor(model, path2);
|
|
5262
5359
|
if (!p) return 0;
|
|
5263
5360
|
return (usage.promptCacheHitTokens * p.inputCacheHit + usage.promptCacheMissTokens * p.inputCacheMiss + usage.completionTokens * p.output) / 1e6;
|
|
5264
5361
|
}
|
|
5265
|
-
function inputCostUsd(model, usage) {
|
|
5266
|
-
const p =
|
|
5362
|
+
function inputCostUsd(model, usage, path2) {
|
|
5363
|
+
const p = pricingFor(model, path2);
|
|
5267
5364
|
if (!p) return 0;
|
|
5268
5365
|
return (usage.promptCacheHitTokens * p.inputCacheHit + usage.promptCacheMissTokens * p.inputCacheMiss) / 1e6;
|
|
5269
5366
|
}
|
|
5270
|
-
function outputCostUsd(model, usage) {
|
|
5271
|
-
const p =
|
|
5367
|
+
function outputCostUsd(model, usage, path2) {
|
|
5368
|
+
const p = pricingFor(model, path2);
|
|
5272
5369
|
if (!p) return 0;
|
|
5273
5370
|
return usage.completionTokens * p.output / 1e6;
|
|
5274
5371
|
}
|
|
5275
|
-
function cacheSavingsUsd(model, hitTokens) {
|
|
5372
|
+
function cacheSavingsUsd(model, hitTokens, path2) {
|
|
5276
5373
|
if (hitTokens <= 0) return 0;
|
|
5277
|
-
const p =
|
|
5374
|
+
const p = pricingFor(model, path2);
|
|
5278
5375
|
if (!p) return 0;
|
|
5279
5376
|
return hitTokens * (p.inputCacheMiss - p.inputCacheHit) / 1e6;
|
|
5280
5377
|
}
|
|
@@ -5932,42 +6029,6 @@ function* hookWarnings(outcomes, turn) {
|
|
|
5932
6029
|
}
|
|
5933
6030
|
}
|
|
5934
6031
|
|
|
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
6032
|
// src/memory/runtime.ts
|
|
5972
6033
|
import { createHash } from "crypto";
|
|
5973
6034
|
var ImmutablePrefix = class {
|
|
@@ -6455,7 +6516,6 @@ var CacheFirstLoop = class {
|
|
|
6455
6516
|
}
|
|
6456
6517
|
_proArmedForNextTurn = false;
|
|
6457
6518
|
_escalateThisTurn = false;
|
|
6458
|
-
_turnFailures;
|
|
6459
6519
|
_turnSelfCorrected = false;
|
|
6460
6520
|
_foldedThisTurn = false;
|
|
6461
6521
|
context;
|
|
@@ -6474,9 +6534,6 @@ var CacheFirstLoop = class {
|
|
|
6474
6534
|
this.reasoningEffort = opts.reasoningEffort ?? "max";
|
|
6475
6535
|
if (opts.autoEscalate !== void 0) this.autoEscalate = opts.autoEscalate;
|
|
6476
6536
|
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
6537
|
this.hooks = opts.hooks ?? [];
|
|
6481
6538
|
this.hookCwd = opts.hookCwd ?? process.cwd();
|
|
6482
6539
|
this.confirmationGate = opts.confirmationGate ?? pauseGate;
|
|
@@ -6584,7 +6641,6 @@ var CacheFirstLoop = class {
|
|
|
6584
6641
|
this._inflight.clear();
|
|
6585
6642
|
this.stats.reset();
|
|
6586
6643
|
this._turn = 0;
|
|
6587
|
-
this._turnFailures.reset();
|
|
6588
6644
|
this._budgetWarned = false;
|
|
6589
6645
|
let systemRebuilt = false;
|
|
6590
6646
|
if (this._rebuildSystem) {
|
|
@@ -6655,13 +6711,6 @@ var CacheFirstLoop = class {
|
|
|
6655
6711
|
modelForCurrentCall() {
|
|
6656
6712
|
return this._escalateThisTurn ? ESCALATION_MODEL : this.model;
|
|
6657
6713
|
}
|
|
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
6714
|
/** A call counts as mutating when its definition reports `readOnly !== true` and any dynamic `readOnlyCheck` doesn't override that for these args. */
|
|
6666
6715
|
isMutating(call) {
|
|
6667
6716
|
const name = call.function?.name;
|
|
@@ -6816,7 +6865,6 @@ ${reason}`
|
|
|
6816
6865
|
this._turn++;
|
|
6817
6866
|
this.scratch.reset();
|
|
6818
6867
|
this.repair.resetStorm();
|
|
6819
|
-
this._turnFailures.reset();
|
|
6820
6868
|
this._turnSelfCorrected = false;
|
|
6821
6869
|
this._escalateThisTurn = false;
|
|
6822
6870
|
this._foldedThisTurn = false;
|
|
@@ -7087,17 +7135,6 @@ ${reason}`
|
|
|
7087
7135
|
stats: turnStats,
|
|
7088
7136
|
repair: report
|
|
7089
7137
|
};
|
|
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
7138
|
const allSuppressed = report.stormsBroken > 0 && repairedCalls.length === 0 && toolCalls.length > 0;
|
|
7102
7139
|
if (allSuppressed && !this._turnSelfCorrected) {
|
|
7103
7140
|
this._turnSelfCorrected = true;
|
|
@@ -7237,17 +7274,6 @@ ${reason}`
|
|
|
7237
7274
|
name,
|
|
7238
7275
|
content: result
|
|
7239
7276
|
});
|
|
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
7277
|
yield {
|
|
7252
7278
|
turn: this._turn,
|
|
7253
7279
|
role: "tool",
|
|
@@ -7285,19 +7311,6 @@ function parsePositiveIntEnv(raw) {
|
|
|
7285
7311
|
const n = Number.parseInt(raw, 10);
|
|
7286
7312
|
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
7287
7313
|
}
|
|
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
7314
|
|
|
7302
7315
|
// src/at-mentions.ts
|
|
7303
7316
|
import { existsSync as existsSync4, readFileSync as readFileSync6, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
@@ -13992,18 +14005,20 @@ var StdioTransport = class {
|
|
|
13992
14005
|
this.child = spawn5(line, [], {
|
|
13993
14006
|
env,
|
|
13994
14007
|
cwd: opts.cwd,
|
|
13995
|
-
stdio: ["pipe", "pipe", "
|
|
14008
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
13996
14009
|
shell: true
|
|
13997
14010
|
});
|
|
13998
14011
|
} else {
|
|
13999
14012
|
this.child = spawn5(opts.command, opts.args ?? [], {
|
|
14000
14013
|
env,
|
|
14001
14014
|
cwd: opts.cwd,
|
|
14002
|
-
stdio: ["pipe", "pipe", "
|
|
14015
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
14003
14016
|
});
|
|
14004
14017
|
}
|
|
14005
14018
|
this.child.stdout.setEncoding("utf8");
|
|
14006
14019
|
this.child.stdout.on("data", (chunk) => this.onStdout(chunk));
|
|
14020
|
+
this.child.stderr.setEncoding("utf8");
|
|
14021
|
+
this.child.stderr.on("data", (chunk) => this.onStderr(chunk));
|
|
14007
14022
|
this.child.on("close", () => this.onClose());
|
|
14008
14023
|
this.child.on("error", (err) => {
|
|
14009
14024
|
this.push({
|
|
@@ -14072,6 +14087,13 @@ var StdioTransport = class {
|
|
|
14072
14087
|
}
|
|
14073
14088
|
}
|
|
14074
14089
|
}
|
|
14090
|
+
// Python MCP SDK writes info logs (`server.py:534 ListPromptsRequest`)
|
|
14091
|
+
// to stderr — letting those through would corrupt the TUI render.
|
|
14092
|
+
onStderr(chunk) {
|
|
14093
|
+
if (process.env.REASONIX_DEBUG_MCP === "1") {
|
|
14094
|
+
process.stderr.write(chunk);
|
|
14095
|
+
}
|
|
14096
|
+
}
|
|
14075
14097
|
onClose() {
|
|
14076
14098
|
this.closed = true;
|
|
14077
14099
|
while (this.waiters.length > 0) this.waiters.shift()(null);
|