reasonix 0.46.1 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -13
- package/README.zh-CN.md +52 -10
- package/dashboard/dist/app.js +217 -60
- package/dashboard/dist/app.js.map +1 -1
- package/dist/cli/{acp-LKJU5DZX.js → acp-QK3DMC53.js} +22 -22
- package/dist/cli/chat-VV5UWY4V.js +51 -0
- package/dist/cli/{chunk-DGA5QYFM.js → chunk-24A7FHGJ.js} +42 -13
- package/dist/cli/chunk-24A7FHGJ.js.map +1 -0
- package/dist/cli/{chunk-K3AIFMI6.js → chunk-6J6BSUCR.js} +2 -2
- package/dist/cli/{chunk-C72TNHDE.js → chunk-BWYVFFKR.js} +2 -2
- package/dist/cli/{chunk-EAOL43HB.js → chunk-BYYVYJDX.js} +3 -3
- package/dist/cli/{chunk-HNXDZGC6.js → chunk-CI2PF5QX.js} +2 -2
- package/dist/cli/{chunk-JVQT5IYP.js → chunk-COWPEX54.js} +19 -9
- package/dist/cli/chunk-COWPEX54.js.map +1 -0
- package/dist/cli/{chunk-IYQ325V7.js → chunk-E5WCLUIU.js} +2 -2
- package/dist/cli/{chunk-YRLC2EDF.js → chunk-EQATK2L2.js} +2 -2
- package/dist/cli/{chunk-R2ASNSEO.js → chunk-FDKOUJKZ.js} +8 -8
- package/dist/cli/{chunk-TEUDEGX2.js → chunk-FY4S7TJZ.js} +19 -5
- package/dist/cli/chunk-FY4S7TJZ.js.map +1 -0
- package/dist/cli/{chunk-JVFEJAJX.js → chunk-GDKB2PPK.js} +2 -2
- package/dist/cli/{chunk-WQ6ZRDQM.js → chunk-HIYTRCSW.js} +16 -12
- package/dist/cli/chunk-HIYTRCSW.js.map +1 -0
- package/dist/cli/{chunk-ZOQHVQON.js → chunk-ICAFSZHS.js} +307 -30
- package/dist/cli/chunk-ICAFSZHS.js.map +1 -0
- package/dist/cli/{chunk-SPXN5JIT.js → chunk-ICSYGIPN.js} +1386 -1021
- package/dist/cli/chunk-ICSYGIPN.js.map +1 -0
- package/dist/cli/{chunk-XPAUNFOL.js → chunk-K6GUKSXH.js} +3 -2
- package/dist/cli/chunk-K6GUKSXH.js.map +1 -0
- package/dist/cli/{chunk-6VANO7KB.js → chunk-KDRUEXII.js} +147 -20
- package/dist/cli/chunk-KDRUEXII.js.map +1 -0
- package/dist/cli/{chunk-NCBP5D6E.js → chunk-LBLR4CUZ.js} +2 -2
- package/dist/cli/{chunk-2425HK6U.js → chunk-LGEKVMMV.js} +7 -2
- package/dist/cli/{chunk-2425HK6U.js.map → chunk-LGEKVMMV.js.map} +1 -1
- package/dist/cli/{chunk-7SGGXNB2.js → chunk-OJVITDGB.js} +2 -2
- package/dist/cli/{chunk-SE7C5ZSI.js → chunk-QVDWH2A2.js} +3 -3
- package/dist/cli/{chunk-WRONKNIH.js → chunk-QVUFWDD2.js} +2 -2
- package/dist/cli/{chunk-3AAG2CUT.js → chunk-R6GQKKBW.js} +2 -2
- package/dist/cli/{chunk-E7TAHQ4A.js → chunk-RRXUIPWG.js} +19 -18
- package/dist/cli/chunk-RRXUIPWG.js.map +1 -0
- package/dist/cli/{chunk-CXVWUPA3.js → chunk-TKVXTQ3T.js} +26 -26
- package/dist/cli/chunk-TKVXTQ3T.js.map +1 -0
- package/dist/cli/{chunk-DHRVZJ2D.js → chunk-UDVFBEXC.js} +3 -3
- package/dist/cli/{chunk-7YW6TPXK.js → chunk-VC2CQA5D.js} +9 -9
- package/dist/cli/{chunk-M4E5JK6S.js → chunk-VJMBISEI.js} +2 -2
- package/dist/cli/{chunk-TDSBASOF.js → chunk-VKYSZKH2.js} +2 -2
- package/dist/cli/{chunk-7LOJS3LV.js → chunk-VMUUFWFF.js} +2 -2
- package/dist/cli/{chunk-MIIZJD5O.js → chunk-VNQGCA3Q.js} +2 -2
- package/dist/cli/{chunk-2AASOSD5.js → chunk-WF7TPVZM.js} +2 -2
- package/dist/cli/{chunk-JLQDNLZF.js → chunk-YDPLF7XR.js} +26 -14
- package/dist/cli/chunk-YDPLF7XR.js.map +1 -0
- package/dist/cli/{code-2JIHL5M2.js → code-C24TUAE5.js} +39 -34
- package/dist/cli/code-C24TUAE5.js.map +1 -0
- package/dist/cli/{commands-OPT5AJNH.js → commands-RR3GIYOK.js} +4 -4
- package/dist/cli/{commit-KA37H6GM.js → commit-FSHPIINM.js} +3 -3
- package/dist/cli/{desktop-5ONTRU3C.js → desktop-7NCHPEFB.js} +263 -36
- package/dist/cli/desktop-7NCHPEFB.js.map +1 -0
- package/dist/cli/{diff-SOIA7AKH.js → diff-RAAHHLHV.js} +8 -8
- package/dist/cli/{doctor-RCUP4XRV.js → doctor-PKVQIXRT.js} +9 -9
- package/dist/cli/{events-6KHITNX4.js → events-VRYXOSKI.js} +3 -3
- package/dist/cli/index.js +81 -40
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{mcp-JP5OWD6R.js → mcp-CRJ26PP4.js} +2 -2
- package/dist/cli/{mcp-browse-ONCJJPJN.js → mcp-browse-QPAOWZOP.js} +2 -2
- package/dist/cli/{mcp-inspect-TPLHW5JA.js → mcp-inspect-CVCLABRS.js} +4 -4
- package/dist/cli/{prompt-RJDNCQAP.js → prompt-SKYXERSI.js} +4 -4
- package/dist/cli/{prune-sessions-MKEATRVL.js → prune-sessions-SEWX7GP6.js} +2 -2
- package/dist/cli/{replay-4NILJG4U.js → replay-KPDW2ZMJ.js} +9 -9
- package/dist/cli/{run-WFGXB4SB.js → run-WIKDIXTG.js} +17 -17
- package/dist/cli/{server-5VFQP3PV.js → server-P6V2G3P6.js} +82 -34
- package/dist/cli/server-P6V2G3P6.js.map +1 -0
- package/dist/cli/{sessions-5XDJDALO.js → sessions-2NULRMSA.js} +15 -15
- package/dist/cli/{setup-F6XSWLRA.js → setup-Y5WDBQFL.js} +6 -6
- package/dist/cli/{stats-ALHBZICE.js → stats-T7BL2YOR.js} +6 -6
- package/dist/cli/{version-JVRAHBMM.js → version-3KWDNWLN.js} +15 -15
- package/dist/index.d.ts +31 -10
- package/dist/index.js +505 -66
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/chat-W7LAWEN6.js +0 -51
- package/dist/cli/chunk-6VANO7KB.js.map +0 -1
- package/dist/cli/chunk-CXVWUPA3.js.map +0 -1
- package/dist/cli/chunk-DGA5QYFM.js.map +0 -1
- package/dist/cli/chunk-E7TAHQ4A.js.map +0 -1
- package/dist/cli/chunk-JLQDNLZF.js.map +0 -1
- package/dist/cli/chunk-JVQT5IYP.js.map +0 -1
- package/dist/cli/chunk-SPXN5JIT.js.map +0 -1
- package/dist/cli/chunk-TEUDEGX2.js.map +0 -1
- package/dist/cli/chunk-WQ6ZRDQM.js.map +0 -1
- package/dist/cli/chunk-XPAUNFOL.js.map +0 -1
- package/dist/cli/chunk-ZOQHVQON.js.map +0 -1
- package/dist/cli/code-2JIHL5M2.js.map +0 -1
- package/dist/cli/desktop-5ONTRU3C.js.map +0 -1
- package/dist/cli/server-5VFQP3PV.js.map +0 -1
- /package/dist/cli/{acp-LKJU5DZX.js.map → acp-QK3DMC53.js.map} +0 -0
- /package/dist/cli/{chat-W7LAWEN6.js.map → chat-VV5UWY4V.js.map} +0 -0
- /package/dist/cli/{chunk-K3AIFMI6.js.map → chunk-6J6BSUCR.js.map} +0 -0
- /package/dist/cli/{chunk-C72TNHDE.js.map → chunk-BWYVFFKR.js.map} +0 -0
- /package/dist/cli/{chunk-EAOL43HB.js.map → chunk-BYYVYJDX.js.map} +0 -0
- /package/dist/cli/{chunk-HNXDZGC6.js.map → chunk-CI2PF5QX.js.map} +0 -0
- /package/dist/cli/{chunk-IYQ325V7.js.map → chunk-E5WCLUIU.js.map} +0 -0
- /package/dist/cli/{chunk-YRLC2EDF.js.map → chunk-EQATK2L2.js.map} +0 -0
- /package/dist/cli/{chunk-R2ASNSEO.js.map → chunk-FDKOUJKZ.js.map} +0 -0
- /package/dist/cli/{chunk-JVFEJAJX.js.map → chunk-GDKB2PPK.js.map} +0 -0
- /package/dist/cli/{chunk-NCBP5D6E.js.map → chunk-LBLR4CUZ.js.map} +0 -0
- /package/dist/cli/{chunk-7SGGXNB2.js.map → chunk-OJVITDGB.js.map} +0 -0
- /package/dist/cli/{chunk-SE7C5ZSI.js.map → chunk-QVDWH2A2.js.map} +0 -0
- /package/dist/cli/{chunk-WRONKNIH.js.map → chunk-QVUFWDD2.js.map} +0 -0
- /package/dist/cli/{chunk-3AAG2CUT.js.map → chunk-R6GQKKBW.js.map} +0 -0
- /package/dist/cli/{chunk-DHRVZJ2D.js.map → chunk-UDVFBEXC.js.map} +0 -0
- /package/dist/cli/{chunk-7YW6TPXK.js.map → chunk-VC2CQA5D.js.map} +0 -0
- /package/dist/cli/{chunk-M4E5JK6S.js.map → chunk-VJMBISEI.js.map} +0 -0
- /package/dist/cli/{chunk-TDSBASOF.js.map → chunk-VKYSZKH2.js.map} +0 -0
- /package/dist/cli/{chunk-7LOJS3LV.js.map → chunk-VMUUFWFF.js.map} +0 -0
- /package/dist/cli/{chunk-MIIZJD5O.js.map → chunk-VNQGCA3Q.js.map} +0 -0
- /package/dist/cli/{chunk-2AASOSD5.js.map → chunk-WF7TPVZM.js.map} +0 -0
- /package/dist/cli/{commands-OPT5AJNH.js.map → commands-RR3GIYOK.js.map} +0 -0
- /package/dist/cli/{commit-KA37H6GM.js.map → commit-FSHPIINM.js.map} +0 -0
- /package/dist/cli/{diff-SOIA7AKH.js.map → diff-RAAHHLHV.js.map} +0 -0
- /package/dist/cli/{doctor-RCUP4XRV.js.map → doctor-PKVQIXRT.js.map} +0 -0
- /package/dist/cli/{events-6KHITNX4.js.map → events-VRYXOSKI.js.map} +0 -0
- /package/dist/cli/{mcp-JP5OWD6R.js.map → mcp-CRJ26PP4.js.map} +0 -0
- /package/dist/cli/{mcp-browse-ONCJJPJN.js.map → mcp-browse-QPAOWZOP.js.map} +0 -0
- /package/dist/cli/{mcp-inspect-TPLHW5JA.js.map → mcp-inspect-CVCLABRS.js.map} +0 -0
- /package/dist/cli/{prompt-RJDNCQAP.js.map → prompt-SKYXERSI.js.map} +0 -0
- /package/dist/cli/{prune-sessions-MKEATRVL.js.map → prune-sessions-SEWX7GP6.js.map} +0 -0
- /package/dist/cli/{replay-4NILJG4U.js.map → replay-KPDW2ZMJ.js.map} +0 -0
- /package/dist/cli/{run-WFGXB4SB.js.map → run-WIKDIXTG.js.map} +0 -0
- /package/dist/cli/{sessions-5XDJDALO.js.map → sessions-2NULRMSA.js.map} +0 -0
- /package/dist/cli/{setup-F6XSWLRA.js.map → setup-Y5WDBQFL.js.map} +0 -0
- /package/dist/cli/{stats-ALHBZICE.js.map → stats-T7BL2YOR.js.map} +0 -0
- /package/dist/cli/{version-JVRAHBMM.js.map → version-3KWDNWLN.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -472,6 +472,12 @@ function loadMetasoApiKey(path2 = defaultConfigPath()) {
|
|
|
472
472
|
if (cfg && typeof cfg === "string" && cfg.trim()) return cfg.trim();
|
|
473
473
|
return DEFAULT_METASO_API_KEY;
|
|
474
474
|
}
|
|
475
|
+
function loadTavilyApiKey(path2 = defaultConfigPath()) {
|
|
476
|
+
if (process.env.TAVILY_API_KEY) return process.env.TAVILY_API_KEY.trim();
|
|
477
|
+
const cfg = readConfig(path2).tavilyApiKey;
|
|
478
|
+
if (cfg && typeof cfg === "string" && cfg.trim()) return cfg.trim();
|
|
479
|
+
return void 0;
|
|
480
|
+
}
|
|
475
481
|
function defaultConfigPath() {
|
|
476
482
|
return join(homedir(), ".reasonix", "config.json");
|
|
477
483
|
}
|
|
@@ -1191,8 +1197,9 @@ var EN = {
|
|
|
1191
1197
|
{ key: "wheel", text: "scrolls chat history (works on web/cloud/SSH terminals too)" },
|
|
1192
1198
|
{
|
|
1193
1199
|
key: "\u2191 / \u2193",
|
|
1194
|
-
text: "
|
|
1195
|
-
}
|
|
1200
|
+
text: "prompt history (or per-line cursor in a multi-line draft) \u2014 Ctrl+P / Ctrl+N alias"
|
|
1201
|
+
},
|
|
1202
|
+
{ key: "PgUp / PgDn", text: "scroll chat history (mouse wheel routes here too)" }
|
|
1196
1203
|
]
|
|
1197
1204
|
}
|
|
1198
1205
|
],
|
|
@@ -1206,11 +1213,11 @@ var EN = {
|
|
|
1206
1213
|
rows: [
|
|
1207
1214
|
{ key: "Enter", text: "submit the prompt" },
|
|
1208
1215
|
{ key: "Shift+Enter", text: "insert a newline in the prompt" },
|
|
1209
|
-
{ key: "\u2191 / \u2193", text: "scroll chat history (mouse wheel routes here too)" },
|
|
1210
1216
|
{
|
|
1211
|
-
key: "
|
|
1217
|
+
key: "\u2191 / \u2193",
|
|
1212
1218
|
text: "previous / next prompt history \xB7 cursor up / down in a multi-line draft"
|
|
1213
1219
|
},
|
|
1220
|
+
{ key: "Ctrl+P / Ctrl+N", text: "readline alias for \u2191 / \u2193" },
|
|
1214
1221
|
{ key: "Ctrl+A / Ctrl+E", text: "jump to start / end of the current line" },
|
|
1215
1222
|
{ key: "Ctrl+W", text: "delete the word before the cursor" },
|
|
1216
1223
|
{ key: "Ctrl+U", text: "clear the entire prompt buffer" },
|
|
@@ -1219,7 +1226,11 @@ var EN = {
|
|
|
1219
1226
|
{ key: "Esc", text: "dismiss picker \xB7 abort the running model turn" },
|
|
1220
1227
|
{ key: "Ctrl+C", text: "abort the running model turn (NOT copy \u2014 see clipboard)" },
|
|
1221
1228
|
{ key: "PgUp / PgDn", text: "scroll chat history a page at a time" },
|
|
1222
|
-
{ key: "End", text: "jump chat to the most recent line" }
|
|
1229
|
+
{ key: "End", text: "jump chat to the most recent line" },
|
|
1230
|
+
{
|
|
1231
|
+
key: "Ctrl+R",
|
|
1232
|
+
text: "toggle verbose mode \u2014 full reasoning + tool output, no head/tail elision"
|
|
1233
|
+
}
|
|
1223
1234
|
]
|
|
1224
1235
|
},
|
|
1225
1236
|
{
|
|
@@ -1258,7 +1269,7 @@ var EN = {
|
|
|
1258
1269
|
]
|
|
1259
1270
|
}
|
|
1260
1271
|
],
|
|
1261
|
-
footer: "Wheel
|
|
1272
|
+
footer: "Wheel scrolls chat on most terminals (web/cloud/SSH included) \u2014 SGR mouse tracking is on by default and stays out of the way of native drag-select and right-click. Pass --no-mouse to opt out."
|
|
1262
1273
|
},
|
|
1263
1274
|
tipShownOnce: "shown once",
|
|
1264
1275
|
modelOverride: "override the default model",
|
|
@@ -1413,7 +1424,7 @@ var EN = {
|
|
|
1413
1424
|
sessions: { description: "list saved sessions (current marked with \u25B8)" },
|
|
1414
1425
|
title: { description: "ask the model to rename this session from the conversation" },
|
|
1415
1426
|
qq: {
|
|
1416
|
-
description: "connect, inspect, or disconnect the QQ channel for this session",
|
|
1427
|
+
description: "connect, inspect, or disconnect the QQ channel for this session (first connect guides App ID / App Secret setup)",
|
|
1417
1428
|
argsHint: "[connect [appId appSecret [sandbox]]|status|disconnect]"
|
|
1418
1429
|
},
|
|
1419
1430
|
setup: { description: "reminds you to exit and run `reasonix setup`" },
|
|
@@ -1637,6 +1648,8 @@ var EN = {
|
|
|
1637
1648
|
notedVerbCreated: "created",
|
|
1638
1649
|
notedVerbAppended: "appended to",
|
|
1639
1650
|
memoryWriteFailed: "# memory write failed",
|
|
1651
|
+
verboseOn: "\u25B8 verbose mode on \u2014 full reasoning + tool output",
|
|
1652
|
+
verboseOff: "\u25B8 verbose mode off \u2014 head/tail elision restored",
|
|
1640
1653
|
commandFailed: "! command failed",
|
|
1641
1654
|
btwUsage: "\u25B8 /btw <question> \u2014 ask a side question without polluting the conversation context.",
|
|
1642
1655
|
btwHeader: "\u226B btw",
|
|
@@ -1771,6 +1784,48 @@ var EN = {
|
|
|
1771
1784
|
titleStarted: "\u25B8 naming session\u2026",
|
|
1772
1785
|
titleFailed: "\u25B8 session title failed: {reason}"
|
|
1773
1786
|
},
|
|
1787
|
+
qq: {
|
|
1788
|
+
unavailable: "/qq is not available in this session.",
|
|
1789
|
+
connecting: "QQ: connecting\u2026",
|
|
1790
|
+
connectFailed: "QQ connect failed: {reason}",
|
|
1791
|
+
disconnecting: "QQ: disconnecting\u2026",
|
|
1792
|
+
disconnectFailed: "QQ disconnect failed: {reason}",
|
|
1793
|
+
usage: "Usage: /qq connect [appId appSecret [sandbox]] | /qq status | /qq disconnect",
|
|
1794
|
+
promptAppId: "QQ setup: enter your QQ Open Platform App ID, then press Enter. Type /cancel to abort.",
|
|
1795
|
+
promptAppSecret: "QQ setup: enter your QQ Open Platform App Secret, then press Enter. Type /cancel to abort.",
|
|
1796
|
+
setupWaitingAppId: "waiting for App ID",
|
|
1797
|
+
setupWaitingAppSecret: "waiting for App Secret",
|
|
1798
|
+
setupCancelled: "QQ setup cancelled.",
|
|
1799
|
+
credentialsRequired: "QQ App ID and App Secret are required.",
|
|
1800
|
+
connected: "QQ connected in {mode} mode. It will auto-start on future launches.",
|
|
1801
|
+
alreadyConnected: "QQ is already connected in {mode} mode. Auto-start is enabled.",
|
|
1802
|
+
disconnected: "QQ disconnected. Auto-start is disabled.",
|
|
1803
|
+
status: "QQ: {connected}, auto-start {enabled}, credentials {configured}, appId {appId}, {sandbox}, access {access}, current mode {mode}.",
|
|
1804
|
+
statusSetup: "QQ: setup in progress \u2014 {step}",
|
|
1805
|
+
stateConnected: "connected",
|
|
1806
|
+
stateDisconnected: "disconnected",
|
|
1807
|
+
stateEnabled: "enabled",
|
|
1808
|
+
stateDisabled: "disabled",
|
|
1809
|
+
stateConfigured: "configured",
|
|
1810
|
+
stateNotConfigured: "not configured",
|
|
1811
|
+
sandbox: "sandbox",
|
|
1812
|
+
production: "production",
|
|
1813
|
+
none: "none",
|
|
1814
|
+
modeChat: "chat",
|
|
1815
|
+
modeCode: "code",
|
|
1816
|
+
accessOwner: "owner {owner}",
|
|
1817
|
+
accessOwnerWithAllowlist: "owner {owner}, allowlist {count}",
|
|
1818
|
+
accessAllowlist: "allowlist {count}",
|
|
1819
|
+
accessRuntime: "first-sender (runtime only, {owner})",
|
|
1820
|
+
accessOpen: "open (unbound)",
|
|
1821
|
+
lockAlreadyRunning: "QQ channel is already running in process {pid}. Stop that process before starting another QQ channel.",
|
|
1822
|
+
unauthorizedMessage: "QQ ignored message from unauthorized openid {openid}. Current access: {access}.",
|
|
1823
|
+
runtimeBound: "QQ temporarily bound this run to first sender {openid}. Set `qq.ownerOpenId` in config to persist access.",
|
|
1824
|
+
missingAppId: "QQ App ID is required. Run `/qq connect` to configure.",
|
|
1825
|
+
missingAppSecret: "QQ App Secret is required. Run `/qq connect` to configure.",
|
|
1826
|
+
authFailed: "QQ bot authentication failed \u2014 check your App ID and App Secret.",
|
|
1827
|
+
readyTimeout: "QQ bot did not receive READY within 15s \u2014 check your App ID and App Secret."
|
|
1828
|
+
},
|
|
1774
1829
|
admin: {
|
|
1775
1830
|
doctorNeedsTui: "/doctor needs a TUI context (postDoctor wired).",
|
|
1776
1831
|
doctorRunning: "\u2695 Doctor \u2014 running health checks\u2026",
|
|
@@ -2037,12 +2092,14 @@ var EN = {
|
|
|
2037
2092
|
usageSearxng: " /search-engine searxng use SearXNG at default endpoint",
|
|
2038
2093
|
usageSearxngUrl: " /search-engine searxng <url> use SearXNG at custom endpoint",
|
|
2039
2094
|
usageMetaso: " /search-engine metaso use Metaso API (100/d free, configure your own API key for more)",
|
|
2095
|
+
usageTavily: " /search-engine tavily use Tavily API (LLM-friendly, free 1000/mo \u2014 set TAVILY_API_KEY or tavilyApiKey in config; get one at https://tavily.com)",
|
|
2040
2096
|
alias: "Alias: /se",
|
|
2041
2097
|
searxngInfo: "SearXNG is a self-hosted metasearch engine (https://github.com/searxng/searxng).",
|
|
2042
2098
|
searxngInstall: "Install it with: docker run -d -p 8080:8080 searxng/searxng",
|
|
2043
2099
|
switched: 'Switched web search engine to "{engine}".{note}',
|
|
2044
2100
|
switchedSearxngNote: " Make sure SearXNG is running at {endpoint}.",
|
|
2045
2101
|
switchedMetasoNote: " There is a daily quota of 100 (configure your own API key for higher limits).",
|
|
2102
|
+
switchedTavilyNote: " Set TAVILY_API_KEY or `tavilyApiKey` in config; free 1000/mo at https://tavily.com.",
|
|
2046
2103
|
confirmed: '\u2713 Web search engine set to "{engine}"{detail}. Next assistant turn will pick up the change.',
|
|
2047
2104
|
confirmedDetail: " ({endpoint})"
|
|
2048
2105
|
},
|
|
@@ -2178,6 +2235,13 @@ var EN = {
|
|
|
2178
2235
|
linesBelow: " \u2193 {count} line below (\u2193/j or Space/PgDn)",
|
|
2179
2236
|
linesBelowPlural: " \u2193 {count} lines below (\u2193/j or Space/PgDn)"
|
|
2180
2237
|
},
|
|
2238
|
+
editPicker: {
|
|
2239
|
+
title: "edit a previous message",
|
|
2240
|
+
hint: "\u2191\u2193 pick \xB7 Enter to load into composer \xB7 Esc to cancel",
|
|
2241
|
+
empty: "no user turns yet \u2014 nothing to edit",
|
|
2242
|
+
dismiss: "Esc to dismiss",
|
|
2243
|
+
forked: "\u25B8 forked at turn #{turn} \u2014 buffer holds the original text"
|
|
2244
|
+
},
|
|
2181
2245
|
sessionPicker: {
|
|
2182
2246
|
header: " \u25C8 REASONIX \xB7 pick a session ",
|
|
2183
2247
|
title: "pick a session \u2014 {workspace}",
|
|
@@ -2311,6 +2375,11 @@ var EN = {
|
|
|
2311
2375
|
metasoServerError: "web_search: Metaso server error ({status}) \u2014 try again later, or switch engine with /search-engine mojeek",
|
|
2312
2376
|
metasoParseError: "web_search: Metaso returned unparseable response (HTTP {status}) \u2014 try again later",
|
|
2313
2377
|
metasoApiError: "web_search: Metaso API error (code {code}: {message}) \u2014 try again later",
|
|
2378
|
+
tavilyMissingKey: "web_search: Tavily backend requires an API key \u2014 set TAVILY_API_KEY env var or `tavilyApiKey` in ~/.reasonix/config.json; free 1000/mo signup at https://tavily.com",
|
|
2379
|
+
tavilyUnauthorized: "web_search: Tavily API key rejected \u2014 check TAVILY_API_KEY or get one at https://tavily.com",
|
|
2380
|
+
tavilyRateLimit: "web_search: Tavily rate-limited or monthly quota exceeded \u2014 wait, switch engine with /search-engine mojeek, or upgrade your Tavily plan",
|
|
2381
|
+
tavilyServerError: "web_search: Tavily server error ({status}) \u2014 try again later, or switch engine with /search-engine mojeek",
|
|
2382
|
+
tavilyParseError: "web_search: Tavily returned unparseable response (HTTP {status}) \u2014 try again later",
|
|
2314
2383
|
fetchStatus: "web_fetch {status} for {url} \u2014 try: confirm the URL resolves in a browser; status suggests the host returned an error page",
|
|
2315
2384
|
fetchRateLimit429: "web_fetch 429 for {url} \u2014 try: wait 10s before retrying; the host is rate-limiting this client",
|
|
2316
2385
|
fetchForbidden403: "web_fetch 403 for {url} \u2014 try: the host is blocking this client; the page may require login or block bots \u2014 use web_search snippets instead",
|
|
@@ -2429,7 +2498,8 @@ var EN = {
|
|
|
2429
2498
|
scrollAbove: " \u2191 {scroll} / {max} row above",
|
|
2430
2499
|
scrollAbovePlural: " \u2191 {scroll} / {max} rows above",
|
|
2431
2500
|
scrollMore: " \u2014 {remaining} more",
|
|
2432
|
-
scrollPgUp: " \xB7 PgUp / wheel
|
|
2501
|
+
scrollPgUp: " \xB7 PgUp / wheel",
|
|
2502
|
+
scrollCopy: " \xB7 /copy enters copy mode"
|
|
2433
2503
|
},
|
|
2434
2504
|
slashArgPicker: {
|
|
2435
2505
|
noMatch: 'no match for "{partial}"',
|
|
@@ -2487,7 +2557,8 @@ var EN = {
|
|
|
2487
2557
|
reconnectDetail: "tearing down \xB7 re-handshake \xB7 listing tools",
|
|
2488
2558
|
disabledDetail: "via /mcp disable {name}",
|
|
2489
2559
|
failedSetupHint: "\u2192 run `reasonix setup` to remove this entry, or fix the underlying issue (missing npm package, network, etc.).",
|
|
2490
|
-
failedSetupConfigHint: "\u2192 run `reasonix setup` to remove broken entries from your saved config."
|
|
2560
|
+
failedSetupConfigHint: "\u2192 run `reasonix setup` to remove broken entries from your saved config.",
|
|
2561
|
+
abortedHint: "MCP startup aborted \u2014 {count} server(s) skipped. Run /mcp to retry once you've fixed the underlying issue."
|
|
2491
2562
|
},
|
|
2492
2563
|
checkpointPicker: {
|
|
2493
2564
|
title: "restore a checkpoint \u2014 {workspace}",
|
|
@@ -2632,8 +2703,9 @@ var zhCN = {
|
|
|
2632
2703
|
{ key: "\u6EDA\u8F6E", text: "\u6EDA\u52A8\u804A\u5929\u8BB0\u5F55\uFF08Web / \u4E91\u7AEF / SSH \u7EC8\u7AEF\u4E5F\u80FD\u7528\uFF09" },
|
|
2633
2704
|
{
|
|
2634
2705
|
key: "\u2191 / \u2193",
|
|
2635
|
-
text: "\
|
|
2636
|
-
}
|
|
2706
|
+
text: "\u8F93\u5165\u5386\u53F2\uFF08\u591A\u884C\u8349\u7A3F\u65F6\u6309\u884C\u79FB\u52A8\u5149\u6807\uFF09\u2014 Ctrl+P / Ctrl+N \u540C\u4E49"
|
|
2707
|
+
},
|
|
2708
|
+
{ key: "PgUp / PgDn", text: "\u6EDA\u52A8\u804A\u5929\u8BB0\u5F55\uFF08\u9F20\u6807\u6EDA\u8F6E\u4E5F\u8D70\u8FD9\u6761\u8DEF\u5F84\uFF09" }
|
|
2637
2709
|
]
|
|
2638
2710
|
}
|
|
2639
2711
|
],
|
|
@@ -2647,11 +2719,11 @@ var zhCN = {
|
|
|
2647
2719
|
rows: [
|
|
2648
2720
|
{ key: "Enter", text: "\u63D0\u4EA4\u8F93\u5165" },
|
|
2649
2721
|
{ key: "Shift+Enter", text: "\u5728\u8F93\u5165\u6846\u4E2D\u63D2\u5165\u6362\u884C" },
|
|
2650
|
-
{ key: "\u2191 / \u2193", text: "\u6EDA\u52A8\u804A\u5929\u8BB0\u5F55\uFF08\u9F20\u6807\u6EDA\u8F6E\u4E5F\u8D70\u8FD9\u6761\u8DEF\u5F84\uFF09" },
|
|
2651
2722
|
{
|
|
2652
|
-
key: "
|
|
2723
|
+
key: "\u2191 / \u2193",
|
|
2653
2724
|
text: "\u4E0A\u4E00\u6761 / \u4E0B\u4E00\u6761\u8F93\u5165\u5386\u53F2 \xB7 \u591A\u884C\u8349\u7A3F\u4E2D\u6309\u884C\u79FB\u52A8\u5149\u6807"
|
|
2654
2725
|
},
|
|
2726
|
+
{ key: "Ctrl+P / Ctrl+N", text: "\u2191 / \u2193 \u7684 readline \u540C\u4E49\u952E" },
|
|
2655
2727
|
{ key: "Ctrl+A / Ctrl+E", text: "\u8DF3\u5230\u5F53\u524D\u884C\u7684\u5F00\u5934 / \u7ED3\u5C3E" },
|
|
2656
2728
|
{ key: "Ctrl+W", text: "\u5220\u9664\u5149\u6807\u524D\u7684\u4E00\u4E2A\u8BCD" },
|
|
2657
2729
|
{ key: "Ctrl+U", text: "\u6E05\u7A7A\u6574\u4E2A\u8F93\u5165\u7F13\u51B2\u533A" },
|
|
@@ -2660,7 +2732,8 @@ var zhCN = {
|
|
|
2660
2732
|
{ key: "Esc", text: "\u5173\u95ED\u5F39\u51FA\u9009\u62E9\u5668 \xB7 \u4E2D\u6B62\u5F53\u524D\u6A21\u578B\u56DE\u5408" },
|
|
2661
2733
|
{ key: "Ctrl+C", text: "\u4E2D\u6B62\u5F53\u524D\u6A21\u578B\u56DE\u5408\uFF08\u4E0D\u662F\u590D\u5236 \u2014 \u89C1\u526A\u8D34\u677F\u6BB5\uFF09" },
|
|
2662
2734
|
{ key: "PgUp / PgDn", text: "\u6574\u9875\u6EDA\u52A8\u804A\u5929\u8BB0\u5F55" },
|
|
2663
|
-
{ key: "End", text: "\u8DF3\u5230\u804A\u5929\u7684\u6700\u65B0\u4E00\u884C" }
|
|
2735
|
+
{ key: "End", text: "\u8DF3\u5230\u804A\u5929\u7684\u6700\u65B0\u4E00\u884C" },
|
|
2736
|
+
{ key: "Ctrl+R", text: "\u5207\u6362\u8BE6\u7EC6\u6A21\u5F0F \u2014 \u663E\u793A\u5B8C\u6574\u63A8\u7406 + \u5DE5\u5177\u8F93\u51FA\uFF0C\u4E0D\u7701\u7565" }
|
|
2664
2737
|
]
|
|
2665
2738
|
},
|
|
2666
2739
|
{
|
|
@@ -2699,7 +2772,7 @@ var zhCN = {
|
|
|
2699
2772
|
]
|
|
2700
2773
|
}
|
|
2701
2774
|
],
|
|
2702
|
-
footer: "\
|
|
2775
|
+
footer: "\u6EDA\u8F6E\u5728\u5927\u591A\u6570\u7EC8\u7AEF\uFF08\u542B Web / \u4E91\u7AEF / SSH\uFF09\u90FD\u80FD\u6EDA\u804A\u5929 \u2014 \u9ED8\u8BA4\u5F00\u542F SGR \u9F20\u6807\u8DDF\u8E2A\uFF0C\u4F46\u4E0D\u4F1A\u5F71\u54CD\u7EC8\u7AEF\u539F\u751F\u62D6\u9009\u548C\u53F3\u952E\u83DC\u5355\u3002\u76F4\u63A5\u62D6\u52A8\u9009\u4E2D\u6587\u672C\u65E0\u9700 Shift\u3002\u4F20\u5165 --no-mouse \u53EF\u5173\u95ED\u3002"
|
|
2703
2776
|
},
|
|
2704
2777
|
tipShownOnce: "\u4EC5\u663E\u793A\u4E00\u6B21",
|
|
2705
2778
|
modelOverride: "\u8986\u76D6\u9ED8\u8BA4\u6A21\u578B",
|
|
@@ -2856,7 +2929,7 @@ var zhCN = {
|
|
|
2856
2929
|
sessions: { description: "\u5217\u51FA\u5DF2\u4FDD\u5B58\u7684\u4F1A\u8BDD\uFF08\u5F53\u524D\u6807\u8BB0\u4E3A \u25B8\uFF09" },
|
|
2857
2930
|
title: { description: "\u8BA9\u6A21\u578B\u6839\u636E\u5F53\u524D\u5BF9\u8BDD\u91CD\u547D\u540D\u6B64\u4F1A\u8BDD" },
|
|
2858
2931
|
qq: {
|
|
2859
|
-
description: "\u8FDE\u63A5\u3001\u67E5\u770B\u6216\u65AD\u5F00\u5F53\u524D\u4F1A\u8BDD\u7684 QQ \u901A\u9053",
|
|
2932
|
+
description: "\u8FDE\u63A5\u3001\u67E5\u770B\u6216\u65AD\u5F00\u5F53\u524D\u4F1A\u8BDD\u7684 QQ \u901A\u9053\uFF08\u9996\u6B21\u8FDE\u63A5\u4F1A\u5F15\u5BFC\u5F55\u5165 App ID / App Secret\uFF09",
|
|
2860
2933
|
argsHint: "[connect [appId appSecret [sandbox]]|status|disconnect]"
|
|
2861
2934
|
},
|
|
2862
2935
|
setup: { description: "\u63D0\u9192\u60A8\u9000\u51FA\u5E76\u8FD0\u884C `reasonix setup`" },
|
|
@@ -3082,6 +3155,8 @@ var zhCN = {
|
|
|
3082
3155
|
notedVerbCreated: "\u521B\u5EFA",
|
|
3083
3156
|
notedVerbAppended: "\u8FFD\u52A0\u5230",
|
|
3084
3157
|
memoryWriteFailed: "# \u8BB0\u5FC6\u5199\u5165\u5931\u8D25",
|
|
3158
|
+
verboseOn: "\u25B8 \u8BE6\u7EC6\u6A21\u5F0F\u5DF2\u5F00 \u2014 \u663E\u793A\u5B8C\u6574\u63A8\u7406 + \u5DE5\u5177\u8F93\u51FA",
|
|
3159
|
+
verboseOff: "\u25B8 \u8BE6\u7EC6\u6A21\u5F0F\u5DF2\u5173 \u2014 \u6062\u590D\u5934\u5C3E\u7701\u7565",
|
|
3085
3160
|
commandFailed: "! \u547D\u4EE4\u5931\u8D25",
|
|
3086
3161
|
btwUsage: "\u25B8 /btw <\u95EE\u9898> \u2014 \u987A\u4FBF\u95EE\u4E2A\u9898\u5916\u8BDD\uFF0C\u4E0D\u4F1A\u5199\u5165\u5F53\u524D\u4F1A\u8BDD\u4E0A\u4E0B\u6587\u3002",
|
|
3087
3162
|
btwHeader: "\u226B btw",
|
|
@@ -3216,6 +3291,48 @@ var zhCN = {
|
|
|
3216
3291
|
titleStarted: "\u25B8 \u6B63\u5728\u547D\u540D\u4F1A\u8BDD\u2026",
|
|
3217
3292
|
titleFailed: "\u25B8 \u4F1A\u8BDD\u547D\u540D\u5931\u8D25\uFF1A{reason}"
|
|
3218
3293
|
},
|
|
3294
|
+
qq: {
|
|
3295
|
+
unavailable: "/qq \u5728\u5F53\u524D\u4F1A\u8BDD\u4E2D\u4E0D\u53EF\u7528\u3002",
|
|
3296
|
+
connecting: "QQ\uFF1A\u6B63\u5728\u8FDE\u63A5\u2026",
|
|
3297
|
+
connectFailed: "QQ \u8FDE\u63A5\u5931\u8D25\uFF1A{reason}",
|
|
3298
|
+
disconnecting: "QQ\uFF1A\u6B63\u5728\u65AD\u5F00\u2026",
|
|
3299
|
+
disconnectFailed: "QQ \u65AD\u5F00\u5931\u8D25\uFF1A{reason}",
|
|
3300
|
+
usage: "\u7528\u6CD5\uFF1A/qq connect [appId appSecret [sandbox]] | /qq status | /qq disconnect",
|
|
3301
|
+
promptAppId: "QQ \u9996\u6B21\u914D\u7F6E\uFF1A\u8BF7\u8F93\u5165 QQ \u5F00\u653E\u5E73\u53F0 App ID \u540E\u56DE\u8F66\u3002\u8F93\u5165 /cancel \u53EF\u53D6\u6D88\u3002",
|
|
3302
|
+
promptAppSecret: "QQ \u9996\u6B21\u914D\u7F6E\uFF1A\u8BF7\u8F93\u5165 QQ \u5F00\u653E\u5E73\u53F0 App Secret \u540E\u56DE\u8F66\u3002\u8F93\u5165 /cancel \u53EF\u53D6\u6D88\u3002",
|
|
3303
|
+
setupWaitingAppId: "\u7B49\u5F85\u8F93\u5165 App ID",
|
|
3304
|
+
setupWaitingAppSecret: "\u7B49\u5F85\u8F93\u5165 App Secret",
|
|
3305
|
+
setupCancelled: "QQ \u9996\u6B21\u914D\u7F6E\u5DF2\u53D6\u6D88\u3002",
|
|
3306
|
+
credentialsRequired: "QQ App ID \u548C App Secret \u4E0D\u80FD\u4E3A\u7A7A\u3002",
|
|
3307
|
+
connected: "QQ \u5DF2\u5728{mode}\u6A21\u5F0F\u4E0B\u8FDE\u63A5\u6210\u529F\uFF0C\u540E\u7EED\u542F\u52A8\u4F1A\u81EA\u52A8\u542F\u7528\u3002",
|
|
3308
|
+
alreadyConnected: "QQ \u5DF2\u5728{mode}\u6A21\u5F0F\u4E0B\u8FDE\u63A5\uFF0C\u81EA\u52A8\u542F\u52A8\u5DF2\u542F\u7528\u3002",
|
|
3309
|
+
disconnected: "QQ \u5DF2\u65AD\u5F00\u8FDE\u63A5\uFF0C\u81EA\u52A8\u542F\u52A8\u5DF2\u5173\u95ED\u3002",
|
|
3310
|
+
status: "QQ\uFF1A{connected}\uFF0C\u81EA\u52A8\u542F\u52A8{enabled}\uFF0C\u51ED\u636E{configured}\uFF0CappId {appId}\uFF0C{sandbox}\uFF0C\u8BBF\u95EE\u63A7\u5236 {access}\uFF0C\u5F53\u524D\u6A21\u5F0F {mode}\u3002",
|
|
3311
|
+
statusSetup: "QQ\uFF1A\u9996\u6B21\u914D\u7F6E\u8FDB\u884C\u4E2D \u2014\u2014 {step}",
|
|
3312
|
+
stateConnected: "\u5DF2\u8FDE\u63A5",
|
|
3313
|
+
stateDisconnected: "\u672A\u8FDE\u63A5",
|
|
3314
|
+
stateEnabled: "\u5DF2\u542F\u7528",
|
|
3315
|
+
stateDisabled: "\u672A\u542F\u7528",
|
|
3316
|
+
stateConfigured: "\u5DF2\u914D\u7F6E",
|
|
3317
|
+
stateNotConfigured: "\u672A\u914D\u7F6E",
|
|
3318
|
+
sandbox: "\u6C99\u7BB1\u73AF\u5883",
|
|
3319
|
+
production: "\u6B63\u5F0F\u73AF\u5883",
|
|
3320
|
+
none: "\u65E0",
|
|
3321
|
+
modeChat: "\u804A\u5929",
|
|
3322
|
+
modeCode: "\u4EE3\u7801",
|
|
3323
|
+
accessOwner: "\u6240\u6709\u8005 {owner}",
|
|
3324
|
+
accessOwnerWithAllowlist: "\u6240\u6709\u8005 {owner}\uFF0C\u767D\u540D\u5355 {count}",
|
|
3325
|
+
accessAllowlist: "\u767D\u540D\u5355 {count}",
|
|
3326
|
+
accessRuntime: "\u9996\u4E2A\u79C1\u804A\u7528\u6237\uFF08\u4EC5\u672C\u6B21\u8FD0\u884C\uFF0C{owner}\uFF09",
|
|
3327
|
+
accessOpen: "\u5F00\u653E\uFF08\u672A\u7ED1\u5B9A\uFF09",
|
|
3328
|
+
lockAlreadyRunning: "QQ \u901A\u9053\u5DF2\u5728\u8FDB\u7A0B {pid} \u4E2D\u8FD0\u884C\u3002\u8BF7\u5148\u505C\u6B62\u8BE5\u8FDB\u7A0B\uFF0C\u518D\u542F\u52A8\u65B0\u7684 QQ \u901A\u9053\u3002",
|
|
3329
|
+
unauthorizedMessage: "QQ \u5FFD\u7565\u4E86\u672A\u6388\u6743 openid {openid} \u7684\u6D88\u606F\u3002\u5F53\u524D\u8BBF\u95EE\u63A7\u5236\uFF1A{access}\u3002",
|
|
3330
|
+
runtimeBound: "QQ \u5DF2\u5728\u672C\u6B21\u8FD0\u884C\u4E2D\u4E34\u65F6\u7ED1\u5B9A\u5230\u9996\u4E2A\u53D1\u9001\u8005 {openid}\u3002\u5982\u9700\u6301\u4E45\u5316\uFF0C\u8BF7\u5728\u914D\u7F6E\u4E2D\u8BBE\u7F6E `qq.ownerOpenId`\u3002",
|
|
3331
|
+
missingAppId: "\u7F3A\u5C11 QQ App ID\u3002\u8BF7\u5148\u8FD0\u884C `/qq connect` \u5B8C\u6210\u914D\u7F6E\u3002",
|
|
3332
|
+
missingAppSecret: "\u7F3A\u5C11 QQ App Secret\u3002\u8BF7\u5148\u8FD0\u884C `/qq connect` \u5B8C\u6210\u914D\u7F6E\u3002",
|
|
3333
|
+
authFailed: "QQ \u673A\u5668\u4EBA\u9274\u6743\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5 App ID \u548C App Secret\u3002",
|
|
3334
|
+
readyTimeout: "QQ \u673A\u5668\u4EBA 15 \u79D2\u5185\u672A\u6536\u5230 READY\uFF0C\u8BF7\u68C0\u67E5 App ID \u548C App Secret\u3002"
|
|
3335
|
+
},
|
|
3219
3336
|
admin: {
|
|
3220
3337
|
doctorNeedsTui: "/doctor \u9700\u8981 TUI \u4E0A\u4E0B\u6587\uFF08postDoctor \u5DF2\u8FDE\u63A5\uFF09\u3002",
|
|
3221
3338
|
doctorRunning: "\u2695 \u5065\u5EB7\u68C0\u67E5 \u2014 \u6B63\u5728\u8FD0\u884C\u2026",
|
|
@@ -3482,12 +3599,14 @@ var zhCN = {
|
|
|
3482
3599
|
usageSearxng: " /search-engine searxng \u4F7F\u7528 SearXNG \u9ED8\u8BA4\u7AEF\u70B9",
|
|
3483
3600
|
usageSearxngUrl: " /search-engine searxng <url> \u4F7F\u7528 SearXNG \u81EA\u5B9A\u4E49\u7AEF\u70B9",
|
|
3484
3601
|
usageMetaso: " /search-engine metaso \u4F7F\u7528 Metaso API\uFF08\u6BCF\u5929 100 \u6B21\u514D\u8D39\uFF0C\u914D\u7F6E\u4F60\u81EA\u5DF1\u7684 API \u5BC6\u94A5\u53EF\u63D0\u5347\u9650\u989D\uFF09",
|
|
3602
|
+
usageTavily: " /search-engine tavily \u4F7F\u7528 Tavily API\uFF08LLM \u53CB\u597D\uFF0C\u6BCF\u6708 1000 \u6B21\u514D\u8D39 \u2014 \u8BBE\u7F6E TAVILY_API_KEY \u6216 config \u7684 tavilyApiKey\uFF1B\u6CE8\u518C https://tavily.com\uFF09",
|
|
3485
3603
|
alias: "\u522B\u540D\uFF1A/se",
|
|
3486
3604
|
searxngInfo: "SearXNG \u662F\u4E00\u4E2A\u81EA\u6258\u7BA1\u7684\u5143\u641C\u7D22\u5F15\u64CE\uFF08https://github.com/searxng/searxng\uFF09\u3002",
|
|
3487
3605
|
searxngInstall: "\u5B89\u88C5\u547D\u4EE4\uFF1A docker run -d -p 8080:8080 searxng/searxng",
|
|
3488
3606
|
switched: '\u5DF2\u5207\u6362\u7F51\u9875\u641C\u7D22\u5F15\u64CE\u4E3A "{engine}"\u3002{note}',
|
|
3489
3607
|
switchedSearxngNote: " \u8BF7\u786E\u4FDD SearXNG \u5728 {endpoint} \u8FD0\u884C\u3002",
|
|
3490
3608
|
switchedMetasoNote: " \u6BCF\u65E5\u9650\u989D 100 \u6B21\uFF08\u914D\u7F6E\u4F60\u81EA\u5DF1\u7684 API \u5BC6\u94A5\u53EF\u63D0\u5347\u9650\u989D\uFF09\u3002",
|
|
3609
|
+
switchedTavilyNote: " \u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF TAVILY_API_KEY \u6216 config \u4E2D\u7684 `tavilyApiKey`\uFF1Bhttps://tavily.com \u6BCF\u6708 1000 \u6B21\u514D\u8D39\u3002",
|
|
3491
3610
|
confirmed: '\u2713 \u7F51\u9875\u641C\u7D22\u5F15\u64CE\u5DF2\u8BBE\u4E3A "{engine}"{detail}\u3002\u4E0B\u4E00\u8F6E\u6A21\u578B\u8C03\u7528\u5C06\u751F\u6548\u3002',
|
|
3492
3611
|
confirmedDetail: "\uFF08{endpoint}\uFF09"
|
|
3493
3612
|
},
|
|
@@ -3623,6 +3742,13 @@ var zhCN = {
|
|
|
3623
3742
|
linesBelow: " \u2193 \u4E0B\u65B9 {count} \u884C\uFF08\u2193/j \u6216 Space/PgDn\uFF09",
|
|
3624
3743
|
linesBelowPlural: " \u2193 \u4E0B\u65B9 {count} \u884C\uFF08\u2193/j \u6216 Space/PgDn\uFF09"
|
|
3625
3744
|
},
|
|
3745
|
+
editPicker: {
|
|
3746
|
+
title: "\u7F16\u8F91\u4E4B\u524D\u7684\u6D88\u606F",
|
|
3747
|
+
hint: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u52A0\u8F7D\u5230\u8F93\u5165\u6846 \xB7 Esc \u53D6\u6D88",
|
|
3748
|
+
empty: "\u8FD8\u6CA1\u6709\u7528\u6237\u53D1\u8A00 \u2014 \u6CA1\u4EC0\u4E48\u53EF\u4EE5\u7F16\u8F91\u7684",
|
|
3749
|
+
dismiss: "Esc \u5173\u95ED",
|
|
3750
|
+
forked: "\u25B8 \u4ECE\u7B2C #{turn} \u8F6E\u5206\u53C9 \u2014 \u539F\u6587\u5DF2\u586B\u56DE\u8F93\u5165\u6846"
|
|
3751
|
+
},
|
|
3626
3752
|
sessionPicker: {
|
|
3627
3753
|
header: " \u25C8 REASONIX \xB7 \u9009\u62E9\u4F1A\u8BDD ",
|
|
3628
3754
|
title: "\u9009\u62E9\u4F1A\u8BDD \u2014 {workspace}",
|
|
@@ -3756,6 +3882,11 @@ var zhCN = {
|
|
|
3756
3882
|
metasoServerError: "web_search: Metaso \u670D\u52A1\u5668\u9519\u8BEF\uFF08{status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek \u5207\u6362\u5F15\u64CE",
|
|
3757
3883
|
metasoParseError: "web_search: Metaso \u8FD4\u56DE\u65E0\u6CD5\u89E3\u6790\u7684\u54CD\u5E94\uFF08HTTP {status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5",
|
|
3758
3884
|
metasoApiError: "web_search: Metaso API \u9519\u8BEF\uFF08code {code}: {message}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5",
|
|
3885
|
+
tavilyMissingKey: "web_search: Tavily \u540E\u7AEF\u9700\u8981 API \u5BC6\u94A5 \u2014 \u8BBE\u7F6E TAVILY_API_KEY \u73AF\u5883\u53D8\u91CF\uFF0C\u6216\u5728 ~/.reasonix/config.json \u4E2D\u914D\u7F6E `tavilyApiKey`\uFF1Bhttps://tavily.com \u6BCF\u6708 1000 \u6B21\u514D\u8D39",
|
|
3886
|
+
tavilyUnauthorized: "web_search: Tavily API \u5BC6\u94A5\u88AB\u62D2\u7EDD \u2014 \u68C0\u67E5 TAVILY_API_KEY\uFF0C\u6216\u5728 https://tavily.com \u83B7\u53D6\u5BC6\u94A5",
|
|
3887
|
+
tavilyRateLimit: "web_search: Tavily \u8BF7\u6C42\u9891\u7387\u9650\u5236\u6216\u6708\u5EA6\u914D\u989D\u7528\u5C3D \u2014 \u7B49\u5F85\u3001\u7528 /search-engine mojeek \u5207\u6362\u5F15\u64CE\uFF0C\u6216\u5347\u7EA7 Tavily \u8BA1\u5212",
|
|
3888
|
+
tavilyServerError: "web_search: Tavily \u670D\u52A1\u5668\u9519\u8BEF\uFF08{status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek \u5207\u6362\u5F15\u64CE",
|
|
3889
|
+
tavilyParseError: "web_search: Tavily \u8FD4\u56DE\u65E0\u6CD5\u89E3\u6790\u7684\u54CD\u5E94\uFF08HTTP {status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5",
|
|
3759
3890
|
fetchStatus: "web_fetch {status} for {url} \u2014 try: \u5728\u6D4F\u89C8\u5668\u4E2D\u786E\u8BA4\u8BE5 URL \u80FD\u5426\u8BBF\u95EE\uFF1B\u8BE5\u72B6\u6001\u7801\u8868\u660E\u76EE\u6807\u4E3B\u673A\u8FD4\u56DE\u4E86\u9519\u8BEF\u9875\u9762",
|
|
3760
3891
|
fetchRateLimit429: "web_fetch 429 for {url} \u2014 try: \u7B49\u5F85 10 \u79D2\u540E\u91CD\u8BD5\uFF1B\u76EE\u6807\u4E3B\u673A\u6B63\u5728\u5BF9\u8BE5\u5BA2\u6237\u7AEF\u8FDB\u884C\u9650\u6D41",
|
|
3761
3892
|
fetchForbidden403: "web_fetch 403 for {url} \u2014 try: \u76EE\u6807\u4E3B\u673A\u62D2\u7EDD\u8BE5\u5BA2\u6237\u7AEF\u8BBF\u95EE\uFF1B\u8BE5\u9875\u9762\u53EF\u80FD\u9700\u8981\u767B\u5F55\u6216\u5C4F\u853D\u722C\u866B \u2014 \u6539\u7528 web_search \u6458\u8981",
|
|
@@ -3874,7 +4005,8 @@ var zhCN = {
|
|
|
3874
4005
|
scrollAbove: " \u2191 {scroll}/{max} \u884C",
|
|
3875
4006
|
scrollAbovePlural: " \u2191 {scroll}/{max} \u884C",
|
|
3876
4007
|
scrollMore: " \u2014 \u8FD8\u6709 {remaining} \u884C",
|
|
3877
|
-
scrollPgUp: " \xB7 PgUp/\u6EDA\u8F6E
|
|
4008
|
+
scrollPgUp: " \xB7 PgUp/\u6EDA\u8F6E",
|
|
4009
|
+
scrollCopy: " \xB7 /copy \u8FDB\u5165\u590D\u5236\u6A21\u5F0F"
|
|
3878
4010
|
},
|
|
3879
4011
|
slashArgPicker: {
|
|
3880
4012
|
noMatch: '\u6CA1\u6709\u5339\u914D "{partial}"',
|
|
@@ -3932,7 +4064,8 @@ var zhCN = {
|
|
|
3932
4064
|
reconnectDetail: "\u65AD\u5F00\u65E7\u8FDE\u63A5 \xB7 \u91CD\u65B0\u63E1\u624B \xB7 \u5217\u51FA\u5DE5\u5177",
|
|
3933
4065
|
disabledDetail: "\u901A\u8FC7 /mcp disable {name}",
|
|
3934
4066
|
failedSetupHint: "\u2192 \u8FD0\u884C `reasonix setup` \u79FB\u9664\u6B64\u6761\u76EE\uFF0C\u6216\u4FEE\u590D\u5E95\u5C42\u95EE\u9898\uFF08\u7F3A\u5C11 npm \u5305\u3001\u7F51\u7EDC\u7B49\uFF09\u3002",
|
|
3935
|
-
failedSetupConfigHint: "\u2192 \u8FD0\u884C `reasonix setup` \u4ECE\u5DF2\u4FDD\u5B58\u914D\u7F6E\u4E2D\u79FB\u9664\u635F\u574F\u7684\u6761\u76EE\u3002"
|
|
4067
|
+
failedSetupConfigHint: "\u2192 \u8FD0\u884C `reasonix setup` \u4ECE\u5DF2\u4FDD\u5B58\u914D\u7F6E\u4E2D\u79FB\u9664\u635F\u574F\u7684\u6761\u76EE\u3002",
|
|
4068
|
+
abortedHint: "\u5DF2\u4E2D\u65AD MCP \u542F\u52A8 \u2014 \u8DF3\u8FC7 {count} \u4E2A\u670D\u52A1\u5668\u3002\u95EE\u9898\u4FEE\u590D\u540E\u7528 /mcp \u91CD\u65B0\u8FDE\u63A5\u3002"
|
|
3936
4069
|
},
|
|
3937
4070
|
checkpointPicker: {
|
|
3938
4071
|
title: "\u6062\u590D\u68C0\u67E5\u70B9 \u2014 {workspace}",
|
|
@@ -4779,6 +4912,12 @@ var ToolRegistry = class {
|
|
|
4779
4912
|
});
|
|
4780
4913
|
}
|
|
4781
4914
|
}
|
|
4915
|
+
if (opts.signal?.aborted) {
|
|
4916
|
+
return JSON.stringify({
|
|
4917
|
+
error: `${name}: aborted before dispatch (user interrupt)`,
|
|
4918
|
+
rejectedReason: "aborted"
|
|
4919
|
+
});
|
|
4920
|
+
}
|
|
4782
4921
|
let finalResult;
|
|
4783
4922
|
try {
|
|
4784
4923
|
try {
|
|
@@ -5011,11 +5150,42 @@ async function bridgeMcpTools(client, opts = {}) {
|
|
|
5011
5150
|
return { ...result, env };
|
|
5012
5151
|
}
|
|
5013
5152
|
function flattenMcpResult(result, opts = {}) {
|
|
5153
|
+
validateResultShape(result);
|
|
5014
5154
|
const parts = result.content.map(blockToString);
|
|
5015
5155
|
const joined = parts.join("\n").trim();
|
|
5016
5156
|
const prefixed = result.isError ? `ERROR: ${joined || "(no error message from server)"}` : joined;
|
|
5017
5157
|
return opts.maxChars ? truncateForModel(prefixed, opts.maxChars) : prefixed;
|
|
5018
5158
|
}
|
|
5159
|
+
function validateResultShape(result) {
|
|
5160
|
+
if (typeof result !== "object" || !result)
|
|
5161
|
+
throw new Error(`MCP server returned non-object result: ${typeof result}`);
|
|
5162
|
+
const { content, isError: _isError } = result;
|
|
5163
|
+
if (!Array.isArray(content))
|
|
5164
|
+
throw new Error(`MCP server returned result with non-array content: ${typeof content}`);
|
|
5165
|
+
for (let i = 0; i < content.length; i++) {
|
|
5166
|
+
const block = content[i];
|
|
5167
|
+
if (typeof block !== "object" || !block)
|
|
5168
|
+
throw new Error(`MCP server returned result.content[${i}] is not an object`);
|
|
5169
|
+
if (block.type !== "text" && block.type !== "image")
|
|
5170
|
+
throw new Error(
|
|
5171
|
+
`MCP server returned result.content[${i}] with unknown type ${JSON.stringify(block.type)}`
|
|
5172
|
+
);
|
|
5173
|
+
if (block.type === "text" && typeof block.text !== "string")
|
|
5174
|
+
throw new Error(
|
|
5175
|
+
`MCP server returned result.content[${i}] with non-string text (${typeof block.text})`
|
|
5176
|
+
);
|
|
5177
|
+
if (block.type === "image") {
|
|
5178
|
+
if (typeof block.data !== "string")
|
|
5179
|
+
throw new Error(
|
|
5180
|
+
`MCP server returned result.content[${i}] with non-string data (${typeof block.data})`
|
|
5181
|
+
);
|
|
5182
|
+
if (typeof block.mimeType !== "string")
|
|
5183
|
+
throw new Error(
|
|
5184
|
+
`MCP server returned result.content[${i}] with non-string mimeType (${typeof block.mimeType})`
|
|
5185
|
+
);
|
|
5186
|
+
}
|
|
5187
|
+
}
|
|
5188
|
+
}
|
|
5019
5189
|
function truncateForModel(s, maxChars) {
|
|
5020
5190
|
if (s.length <= maxChars) return s;
|
|
5021
5191
|
const tailBudget = Math.min(1024, Math.floor(maxChars * 0.1));
|
|
@@ -5198,31 +5368,38 @@ function appendSessionMessage(name, message) {
|
|
|
5198
5368
|
} catch {
|
|
5199
5369
|
}
|
|
5200
5370
|
}
|
|
5201
|
-
function listSessions() {
|
|
5371
|
+
function listSessions(opts) {
|
|
5202
5372
|
const dir = sessionsDir();
|
|
5203
5373
|
if (!existsSync3(dir)) return [];
|
|
5374
|
+
const want = opts?.workspaceFilter ? normalizeWorkspace(opts.workspaceFilter) : null;
|
|
5204
5375
|
try {
|
|
5205
5376
|
const files = readdirSync(dir).filter(
|
|
5206
5377
|
(f) => f.endsWith(".jsonl") && !f.endsWith(".events.jsonl")
|
|
5207
5378
|
);
|
|
5208
|
-
return files.
|
|
5379
|
+
return files.flatMap((file) => {
|
|
5209
5380
|
const path2 = join4(dir, file);
|
|
5210
|
-
const stat2 = statSync(path2);
|
|
5211
5381
|
const name = file.replace(/\.jsonl$/, "");
|
|
5382
|
+
const meta = loadSessionMeta(name);
|
|
5383
|
+
if (want !== null) {
|
|
5384
|
+
if (typeof meta.workspace !== "string") return [];
|
|
5385
|
+
if (normalizeWorkspace(meta.workspace) !== want) return [];
|
|
5386
|
+
}
|
|
5387
|
+
const stat2 = statSync(path2);
|
|
5212
5388
|
const messageCount = countLines(path2);
|
|
5213
|
-
return {
|
|
5214
|
-
name,
|
|
5215
|
-
path: path2,
|
|
5216
|
-
size: stat2.size,
|
|
5217
|
-
messageCount,
|
|
5218
|
-
mtime: stat2.mtime,
|
|
5219
|
-
meta: loadSessionMeta(name)
|
|
5220
|
-
};
|
|
5389
|
+
return [{ name, path: path2, size: stat2.size, messageCount, mtime: stat2.mtime, meta }];
|
|
5221
5390
|
}).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
5222
5391
|
} catch {
|
|
5223
5392
|
return [];
|
|
5224
5393
|
}
|
|
5225
5394
|
}
|
|
5395
|
+
function normalizeWorkspace(p, platform = process.platform) {
|
|
5396
|
+
if (typeof p !== "string" || p.length === 0) return "";
|
|
5397
|
+
if (platform === "win32") {
|
|
5398
|
+
const resolved = win32Path.resolve(p);
|
|
5399
|
+
return resolved.replace(/\\/g, "/").replace(/^([A-Z]):/i, (_, d) => `${d.toLowerCase()}:`);
|
|
5400
|
+
}
|
|
5401
|
+
return posixPath.resolve(p);
|
|
5402
|
+
}
|
|
5226
5403
|
function metaPath(name) {
|
|
5227
5404
|
return join4(sessionsDir(), `${sanitizeName(name)}.meta.json`);
|
|
5228
5405
|
}
|
|
@@ -5312,8 +5489,13 @@ function archiveSession(name) {
|
|
|
5312
5489
|
}
|
|
5313
5490
|
function countLines(path2) {
|
|
5314
5491
|
try {
|
|
5315
|
-
const
|
|
5316
|
-
|
|
5492
|
+
const buf = readFileSync4(path2);
|
|
5493
|
+
let count = 0;
|
|
5494
|
+
for (let i = 0; i < buf.length; i++) {
|
|
5495
|
+
if (buf[i] === 10) count++;
|
|
5496
|
+
}
|
|
5497
|
+
if (buf.length > 0 && buf[buf.length - 1] !== 10) count++;
|
|
5498
|
+
return count;
|
|
5317
5499
|
} catch {
|
|
5318
5500
|
return 0;
|
|
5319
5501
|
}
|
|
@@ -6834,6 +7016,32 @@ ${reason}`
|
|
|
6834
7016
|
}
|
|
6835
7017
|
return userText;
|
|
6836
7018
|
}
|
|
7019
|
+
/** Rewind to the N-th user turn (0-indexed). Drops that turn + everything after. */
|
|
7020
|
+
rewindToUserTurn(userTurnIndex) {
|
|
7021
|
+
const entries = this.log.entries;
|
|
7022
|
+
let count = 0;
|
|
7023
|
+
let targetIdx = -1;
|
|
7024
|
+
for (let i = 0; i < entries.length; i++) {
|
|
7025
|
+
if (entries[i].role !== "user") continue;
|
|
7026
|
+
if (count === userTurnIndex) {
|
|
7027
|
+
targetIdx = i;
|
|
7028
|
+
break;
|
|
7029
|
+
}
|
|
7030
|
+
count++;
|
|
7031
|
+
}
|
|
7032
|
+
if (targetIdx < 0) return null;
|
|
7033
|
+
const raw = entries[targetIdx].content;
|
|
7034
|
+
const userText = typeof raw === "string" ? raw : "";
|
|
7035
|
+
const preserved = entries.slice(0, targetIdx).map((m) => ({ ...m }));
|
|
7036
|
+
this.log.compactInPlace(preserved);
|
|
7037
|
+
if (this.sessionName) {
|
|
7038
|
+
try {
|
|
7039
|
+
rewriteSession(this.sessionName, preserved);
|
|
7040
|
+
} catch {
|
|
7041
|
+
}
|
|
7042
|
+
}
|
|
7043
|
+
return userText;
|
|
7044
|
+
}
|
|
6837
7045
|
async *step(userInput) {
|
|
6838
7046
|
this._steerConsumed = false;
|
|
6839
7047
|
if (this.budgetUsd !== null) {
|
|
@@ -8001,10 +8209,15 @@ var SkillStore = class {
|
|
|
8001
8209
|
dir: join7(this.projectRoot, ".agents", SKILLS_DIRNAME),
|
|
8002
8210
|
scope: "project"
|
|
8003
8211
|
});
|
|
8212
|
+
out.push({
|
|
8213
|
+
dir: join7(this.projectRoot, ".claude", SKILLS_DIRNAME),
|
|
8214
|
+
scope: "project"
|
|
8215
|
+
});
|
|
8004
8216
|
}
|
|
8005
8217
|
for (const dir of this.customSkillPaths) out.push({ dir, scope: "custom" });
|
|
8006
8218
|
out.push({ dir: join7(this.homeDir, ".reasonix", SKILLS_DIRNAME), scope: "global" });
|
|
8007
8219
|
out.push({ dir: join7(this.homeDir, ".agents", SKILLS_DIRNAME), scope: "global" });
|
|
8220
|
+
out.push({ dir: join7(this.homeDir, ".claude", SKILLS_DIRNAME), scope: "global" });
|
|
8008
8221
|
return out.map((root, priority) => ({ ...root, priority, status: skillPathStatus(root.dir) }));
|
|
8009
8222
|
}
|
|
8010
8223
|
customRoots() {
|
|
@@ -8107,14 +8320,20 @@ var SkillStore = class {
|
|
|
8107
8320
|
}
|
|
8108
8321
|
const { data, body } = parseFrontmatter(raw);
|
|
8109
8322
|
const name = data.name && isValidSkillName(data.name) ? data.name : stem;
|
|
8323
|
+
const description = (data.description ?? "").trim();
|
|
8324
|
+
if (!description) {
|
|
8325
|
+
console.warn(
|
|
8326
|
+
`[skills] "${name}" at ${path2} has no description: \u2014 it will be loaded but won't appear in the skills index.`
|
|
8327
|
+
);
|
|
8328
|
+
}
|
|
8110
8329
|
return {
|
|
8111
8330
|
name,
|
|
8112
|
-
description
|
|
8331
|
+
description,
|
|
8113
8332
|
body: body.trim(),
|
|
8114
8333
|
scope,
|
|
8115
8334
|
path: path2,
|
|
8116
8335
|
allowedTools: parseAllowedTools(data["allowed-tools"]),
|
|
8117
|
-
runAs: parseRunAs(data.runAs),
|
|
8336
|
+
runAs: parseRunAs(data.runAs, data.context, data.agent),
|
|
8118
8337
|
model: data.model?.startsWith("deepseek-") ? data.model : void 0
|
|
8119
8338
|
};
|
|
8120
8339
|
}
|
|
@@ -8147,8 +8366,11 @@ function skillPathStatus(dir) {
|
|
|
8147
8366
|
return "unreadable";
|
|
8148
8367
|
}
|
|
8149
8368
|
}
|
|
8150
|
-
function parseRunAs(raw) {
|
|
8151
|
-
|
|
8369
|
+
function parseRunAs(raw, context, agent) {
|
|
8370
|
+
if (raw?.trim() === "subagent") return "subagent";
|
|
8371
|
+
if (context?.trim().toLowerCase() === "fork") return "subagent";
|
|
8372
|
+
if (agent?.trim()) return "subagent";
|
|
8373
|
+
return "inline";
|
|
8152
8374
|
}
|
|
8153
8375
|
function skillStubBody(name) {
|
|
8154
8376
|
return `---
|
|
@@ -8710,13 +8932,13 @@ import picomatch3 from "picomatch";
|
|
|
8710
8932
|
// src/memory/subdir.ts
|
|
8711
8933
|
import { existsSync as existsSync8, readFileSync as readFileSync10 } from "fs";
|
|
8712
8934
|
import { dirname as dirname5, join as join9, relative as relative2, resolve as resolve5 } from "path";
|
|
8713
|
-
function
|
|
8935
|
+
function findDirMemory(absDir, rootDir) {
|
|
8714
8936
|
const root = resolve5(rootDir);
|
|
8715
|
-
const target = resolve5(
|
|
8937
|
+
const target = resolve5(absDir);
|
|
8716
8938
|
const rel = relative2(root, target);
|
|
8717
|
-
if (
|
|
8939
|
+
if (rel.startsWith("..")) return [];
|
|
8718
8940
|
const found = [];
|
|
8719
|
-
let cur =
|
|
8941
|
+
let cur = target;
|
|
8720
8942
|
while (cur !== root) {
|
|
8721
8943
|
const r = relative2(root, cur);
|
|
8722
8944
|
if (!r || r.startsWith("..")) break;
|
|
@@ -8733,6 +8955,9 @@ function findSubdirMemoryAncestors(absPath, rootDir) {
|
|
|
8733
8955
|
}
|
|
8734
8956
|
return found;
|
|
8735
8957
|
}
|
|
8958
|
+
function findSubdirMemoryAncestors(absPath, rootDir) {
|
|
8959
|
+
return findDirMemory(dirname5(resolve5(absPath)), rootDir);
|
|
8960
|
+
}
|
|
8736
8961
|
function readSubdirMemoryContent(path2) {
|
|
8737
8962
|
let raw;
|
|
8738
8963
|
try {
|
|
@@ -9157,6 +9382,129 @@ function formatOutline(entries) {
|
|
|
9157
9382
|
// src/tools/fs/search.ts
|
|
9158
9383
|
import { promises as fs3 } from "fs";
|
|
9159
9384
|
import * as pathMod4 from "path";
|
|
9385
|
+
|
|
9386
|
+
// src/tools/fs/regex-runner.ts
|
|
9387
|
+
import { Worker } from "worker_threads";
|
|
9388
|
+
var WORKER_SOURCE = `
|
|
9389
|
+
const { parentPort } = require("node:worker_threads");
|
|
9390
|
+
parentPort.on("message", (msg) => {
|
|
9391
|
+
const { id, text, source, flags } = msg;
|
|
9392
|
+
let re;
|
|
9393
|
+
try {
|
|
9394
|
+
re = new RegExp(source, flags);
|
|
9395
|
+
} catch (err) {
|
|
9396
|
+
parentPort.postMessage({ id, error: (err && err.message) ? err.message : String(err) });
|
|
9397
|
+
return;
|
|
9398
|
+
}
|
|
9399
|
+
const lines = text.split(/\\r?\\n/);
|
|
9400
|
+
const hits = [];
|
|
9401
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9402
|
+
if (re.test(lines[i])) hits.push(i);
|
|
9403
|
+
}
|
|
9404
|
+
parentPort.postMessage({ id, hits });
|
|
9405
|
+
});
|
|
9406
|
+
`;
|
|
9407
|
+
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
9408
|
+
var RegexRunner = class {
|
|
9409
|
+
worker = null;
|
|
9410
|
+
pending = /* @__PURE__ */ new Map();
|
|
9411
|
+
nextId = 1;
|
|
9412
|
+
defaultTimeoutMs;
|
|
9413
|
+
constructor(opts = {}) {
|
|
9414
|
+
this.defaultTimeoutMs = opts.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
9415
|
+
}
|
|
9416
|
+
testLines(text, source, flags, opts = {}) {
|
|
9417
|
+
return new Promise((resolve13, reject) => {
|
|
9418
|
+
if (opts.signal?.aborted) {
|
|
9419
|
+
reject(new Error("regex evaluation aborted"));
|
|
9420
|
+
return;
|
|
9421
|
+
}
|
|
9422
|
+
if (!this.worker) this.worker = this.spawn();
|
|
9423
|
+
const id = this.nextId++;
|
|
9424
|
+
const timeoutMs = opts.timeoutMs ?? this.defaultTimeoutMs;
|
|
9425
|
+
const timer = setTimeout(() => {
|
|
9426
|
+
this.pending.delete(id);
|
|
9427
|
+
this.killWorker();
|
|
9428
|
+
reject(new Error(`regex evaluation exceeded ${timeoutMs}ms`));
|
|
9429
|
+
}, timeoutMs);
|
|
9430
|
+
const entry = { resolve: resolve13, reject, timer };
|
|
9431
|
+
if (opts.signal) {
|
|
9432
|
+
entry.signal = opts.signal;
|
|
9433
|
+
entry.onAbort = () => {
|
|
9434
|
+
this.pending.delete(id);
|
|
9435
|
+
clearTimeout(timer);
|
|
9436
|
+
this.killWorker();
|
|
9437
|
+
reject(new Error("regex evaluation aborted"));
|
|
9438
|
+
};
|
|
9439
|
+
opts.signal.addEventListener("abort", entry.onAbort, { once: true });
|
|
9440
|
+
}
|
|
9441
|
+
this.pending.set(id, entry);
|
|
9442
|
+
this.worker.postMessage({ id, text, source, flags });
|
|
9443
|
+
});
|
|
9444
|
+
}
|
|
9445
|
+
async shutdown() {
|
|
9446
|
+
if (this.worker) {
|
|
9447
|
+
const w = this.worker;
|
|
9448
|
+
this.worker = null;
|
|
9449
|
+
await w.terminate();
|
|
9450
|
+
}
|
|
9451
|
+
for (const entry of this.pending.values()) {
|
|
9452
|
+
clearTimeout(entry.timer);
|
|
9453
|
+
if (entry.onAbort && entry.signal) {
|
|
9454
|
+
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
9455
|
+
}
|
|
9456
|
+
entry.reject(new Error("regex runner shut down"));
|
|
9457
|
+
}
|
|
9458
|
+
this.pending.clear();
|
|
9459
|
+
}
|
|
9460
|
+
spawn() {
|
|
9461
|
+
const w = new Worker(WORKER_SOURCE, { eval: true });
|
|
9462
|
+
w.on("message", (msg) => {
|
|
9463
|
+
const entry = this.pending.get(msg.id);
|
|
9464
|
+
if (!entry) return;
|
|
9465
|
+
clearTimeout(entry.timer);
|
|
9466
|
+
if (entry.onAbort && entry.signal) {
|
|
9467
|
+
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
9468
|
+
}
|
|
9469
|
+
this.pending.delete(msg.id);
|
|
9470
|
+
if (msg.error !== void 0) entry.reject(new Error(msg.error));
|
|
9471
|
+
else entry.resolve(msg.hits ?? []);
|
|
9472
|
+
});
|
|
9473
|
+
w.on("error", (err) => {
|
|
9474
|
+
if (this.worker !== w) return;
|
|
9475
|
+
this.failPending(err);
|
|
9476
|
+
});
|
|
9477
|
+
w.on("exit", () => {
|
|
9478
|
+
if (this.worker !== w) return;
|
|
9479
|
+
this.worker = null;
|
|
9480
|
+
if (this.pending.size > 0) this.failPending(new Error("regex worker exited"));
|
|
9481
|
+
});
|
|
9482
|
+
return w;
|
|
9483
|
+
}
|
|
9484
|
+
killWorker() {
|
|
9485
|
+
if (!this.worker) return;
|
|
9486
|
+
const w = this.worker;
|
|
9487
|
+
this.worker = null;
|
|
9488
|
+
void w.terminate();
|
|
9489
|
+
}
|
|
9490
|
+
failPending(err) {
|
|
9491
|
+
for (const entry of this.pending.values()) {
|
|
9492
|
+
clearTimeout(entry.timer);
|
|
9493
|
+
if (entry.onAbort && entry.signal) {
|
|
9494
|
+
entry.signal.removeEventListener("abort", entry.onAbort);
|
|
9495
|
+
}
|
|
9496
|
+
entry.reject(err);
|
|
9497
|
+
}
|
|
9498
|
+
this.pending.clear();
|
|
9499
|
+
}
|
|
9500
|
+
};
|
|
9501
|
+
var _runner = null;
|
|
9502
|
+
function getRegexRunner() {
|
|
9503
|
+
if (!_runner) _runner = new RegexRunner();
|
|
9504
|
+
return _runner;
|
|
9505
|
+
}
|
|
9506
|
+
|
|
9507
|
+
// src/tools/fs/search.ts
|
|
9160
9508
|
function throwIfAborted(signal) {
|
|
9161
9509
|
if (!signal?.aborted) return;
|
|
9162
9510
|
throw new DOMException("search aborted by user", "AbortError");
|
|
@@ -9209,17 +9557,20 @@ async function searchFiles(ctx, startAbs, args) {
|
|
|
9209
9557
|
}
|
|
9210
9558
|
var MAX_HITS_PER_FILE = 30;
|
|
9211
9559
|
var SUMMARY_MODE_TRIGGER_RATIO = 0.8;
|
|
9560
|
+
var WALK_DEADLINE_MS = 12e4;
|
|
9212
9561
|
async function searchContent(ctx, startAbs, args) {
|
|
9213
9562
|
throwIfAborted(args.signal);
|
|
9214
9563
|
const caseSensitive = args.case_sensitive === true;
|
|
9215
9564
|
const includeDeps = args.include_deps === true;
|
|
9216
9565
|
const ctxLines = Math.max(0, Math.min(20, Math.floor(args.context ?? 0)));
|
|
9217
9566
|
const summaryOnly = args.summary_only === true;
|
|
9218
|
-
|
|
9567
|
+
const reFlags = caseSensitive ? "" : "i";
|
|
9568
|
+
let reSource = null;
|
|
9219
9569
|
try {
|
|
9220
|
-
|
|
9570
|
+
new RegExp(args.pattern, reFlags);
|
|
9571
|
+
reSource = args.pattern;
|
|
9221
9572
|
} catch {
|
|
9222
|
-
|
|
9573
|
+
reSource = null;
|
|
9223
9574
|
}
|
|
9224
9575
|
const needle = caseSensitive ? args.pattern : args.pattern.toLowerCase();
|
|
9225
9576
|
const matches = [];
|
|
@@ -9229,6 +9580,15 @@ async function searchContent(ctx, startAbs, args) {
|
|
|
9229
9580
|
let summaryMode = summaryOnly;
|
|
9230
9581
|
let summaryNoticeEmitted = false;
|
|
9231
9582
|
const fileHitCounts = /* @__PURE__ */ new Map();
|
|
9583
|
+
const regexSkippedFiles = [];
|
|
9584
|
+
const t0 = Date.now();
|
|
9585
|
+
const throwIfTimedOut = () => {
|
|
9586
|
+
if (Date.now() - t0 > WALK_DEADLINE_MS) {
|
|
9587
|
+
throw new Error(
|
|
9588
|
+
`search_content exceeded ${WALK_DEADLINE_MS}ms \u2014 narrow the scope (path/glob) or simplify the pattern`
|
|
9589
|
+
);
|
|
9590
|
+
}
|
|
9591
|
+
};
|
|
9232
9592
|
const pushLine = (out) => {
|
|
9233
9593
|
if (totalBytes + out.length + 1 > ctx.maxListBytes) {
|
|
9234
9594
|
matches.push(`[\u2026 truncated at ${ctx.maxListBytes} bytes \u2014 refine pattern or path \u2026]`);
|
|
@@ -9263,6 +9623,7 @@ async function searchContent(ctx, startAbs, args) {
|
|
|
9263
9623
|
for (const e of entries) {
|
|
9264
9624
|
if (truncated) return;
|
|
9265
9625
|
throwIfAborted(args.signal);
|
|
9626
|
+
throwIfTimedOut();
|
|
9266
9627
|
if (e.isDirectory()) {
|
|
9267
9628
|
if (!includeDeps && ctx.skipDirNames.has(e.name)) continue;
|
|
9268
9629
|
await walk2(pathMod4.join(dir, e.name));
|
|
@@ -9299,13 +9660,25 @@ async function searchContent(ctx, startAbs, args) {
|
|
|
9299
9660
|
const text = raw.toString("utf8");
|
|
9300
9661
|
const rel = displayRel3(ctx.rootDir, full);
|
|
9301
9662
|
const lines = text.split(/\r?\n/);
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9663
|
+
let hits;
|
|
9664
|
+
if (reSource !== null) {
|
|
9665
|
+
try {
|
|
9666
|
+
hits = await getRegexRunner().testLines(text, reSource, reFlags, {
|
|
9667
|
+
signal: args.signal
|
|
9668
|
+
});
|
|
9669
|
+
} catch (err) {
|
|
9670
|
+
const reason = err.message;
|
|
9671
|
+
if (reason.includes("aborted")) throw err;
|
|
9672
|
+
regexSkippedFiles.push({ rel, reason });
|
|
9673
|
+
continue;
|
|
9674
|
+
}
|
|
9675
|
+
} else {
|
|
9676
|
+
hits = [];
|
|
9677
|
+
for (let li = 0; li < lines.length; li++) {
|
|
9678
|
+
throwIfAborted(args.signal);
|
|
9679
|
+
const lineForCheck = caseSensitive ? lines[li] : lines[li].toLowerCase();
|
|
9680
|
+
if (lineForCheck.includes(needle)) hits.push(li);
|
|
9681
|
+
}
|
|
9309
9682
|
}
|
|
9310
9683
|
scanned++;
|
|
9311
9684
|
if (hits.length === 0) continue;
|
|
@@ -9354,6 +9727,11 @@ async function searchContent(ctx, startAbs, args) {
|
|
|
9354
9727
|
}
|
|
9355
9728
|
};
|
|
9356
9729
|
await walk2(startAbs);
|
|
9730
|
+
if (regexSkippedFiles.length > 0) {
|
|
9731
|
+
pushLine(
|
|
9732
|
+
`[regex timed out on ${regexSkippedFiles.length} file${regexSkippedFiles.length === 1 ? "" : "s"} \u2014 pattern may have catastrophic backtracking; first: ${regexSkippedFiles[0].rel}]`
|
|
9733
|
+
);
|
|
9734
|
+
}
|
|
9357
9735
|
if (matches.length === 0) {
|
|
9358
9736
|
return scanned === 0 ? "(no files scanned \u2014 path empty or all files filtered out)" : `(no matches across ${scanned} file${scanned === 1 ? "" : "s"})`;
|
|
9359
9737
|
}
|
|
@@ -9418,11 +9796,15 @@ function registerFilesystemTools(registry, opts) {
|
|
|
9418
9796
|
const sessionApproved = /* @__PURE__ */ new Set();
|
|
9419
9797
|
const shownSubdirMemory = /* @__PURE__ */ new Set();
|
|
9420
9798
|
function withSubdirMemory(absPath, body) {
|
|
9421
|
-
|
|
9422
|
-
|
|
9423
|
-
|
|
9799
|
+
return prependMemorySections(findSubdirMemoryAncestors(absPath, rootDir), body);
|
|
9800
|
+
}
|
|
9801
|
+
function withDirMemory(absDir, body) {
|
|
9802
|
+
return prependMemorySections(findDirMemory(absDir, rootDir), body);
|
|
9803
|
+
}
|
|
9804
|
+
function prependMemorySections(memPaths, body) {
|
|
9805
|
+
if (!memoryEnabled() || memPaths.length === 0) return body;
|
|
9424
9806
|
const sections = [];
|
|
9425
|
-
for (const memPath of [...
|
|
9807
|
+
for (const memPath of [...memPaths].reverse()) {
|
|
9426
9808
|
if (shownSubdirMemory.has(memPath)) continue;
|
|
9427
9809
|
const content = readSubdirMemoryContent(memPath);
|
|
9428
9810
|
if (!content) continue;
|
|
@@ -9615,7 +9997,7 @@ ${slice.join("\n")}`);
|
|
|
9615
9997
|
for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
9616
9998
|
lines.push(e.isDirectory() ? `${e.name}/` : e.name);
|
|
9617
9999
|
}
|
|
9618
|
-
return lines.join("\n") || "(empty directory)";
|
|
10000
|
+
return withDirMemory(abs, lines.join("\n") || "(empty directory)");
|
|
9619
10001
|
}
|
|
9620
10002
|
});
|
|
9621
10003
|
registry.register({
|
|
@@ -12743,6 +13125,7 @@ var FETCH_MAX_BYTES = 10 * 1024 * 1024;
|
|
|
12743
13125
|
var USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
|
12744
13126
|
var MOJEEK_ENDPOINT = "https://www.mojeek.com/search";
|
|
12745
13127
|
var METASO_ENDPOINT = "https://metaso.cn/api/v1";
|
|
13128
|
+
var TAVILY_ENDPOINT = "https://api.tavily.com/search";
|
|
12746
13129
|
function searchStatusError(status) {
|
|
12747
13130
|
if (status === 429) return t("webErrors.rateLimit429");
|
|
12748
13131
|
if (status === 403) return t("webErrors.forbidden403");
|
|
@@ -12762,6 +13145,9 @@ async function webSearch(query, opts = {}) {
|
|
|
12762
13145
|
if (opts.engine === "searxng") {
|
|
12763
13146
|
return searchSearxng(query, opts);
|
|
12764
13147
|
}
|
|
13148
|
+
if (opts.engine === "tavily") {
|
|
13149
|
+
return searchTavily(query, opts);
|
|
13150
|
+
}
|
|
12765
13151
|
return searchMojeek(query, opts);
|
|
12766
13152
|
}
|
|
12767
13153
|
async function searchMojeek(query, opts = {}) {
|
|
@@ -12896,6 +13282,55 @@ async function searchMetaso(query, opts = {}) {
|
|
|
12896
13282
|
snippet: wp.snippet ?? wp.summary ?? ""
|
|
12897
13283
|
}));
|
|
12898
13284
|
}
|
|
13285
|
+
async function searchTavily(query, opts = {}) {
|
|
13286
|
+
const topK = Math.max(1, Math.min(20, opts.topK ?? DEFAULT_TOPK));
|
|
13287
|
+
const apiKey = loadTavilyApiKey();
|
|
13288
|
+
if (!apiKey) throw new Error(t("webErrors.tavilyMissingKey"));
|
|
13289
|
+
let resp;
|
|
13290
|
+
try {
|
|
13291
|
+
resp = await fetch(TAVILY_ENDPOINT, {
|
|
13292
|
+
method: "POST",
|
|
13293
|
+
headers: {
|
|
13294
|
+
"Content-Type": "application/json",
|
|
13295
|
+
Accept: "application/json"
|
|
13296
|
+
},
|
|
13297
|
+
body: JSON.stringify({
|
|
13298
|
+
api_key: apiKey,
|
|
13299
|
+
query,
|
|
13300
|
+
search_depth: "basic",
|
|
13301
|
+
max_results: topK,
|
|
13302
|
+
include_answer: false,
|
|
13303
|
+
include_raw_content: false,
|
|
13304
|
+
include_images: false
|
|
13305
|
+
}),
|
|
13306
|
+
signal: opts.signal
|
|
13307
|
+
});
|
|
13308
|
+
} catch (err) {
|
|
13309
|
+
if (err instanceof TypeError && err.message.includes("fetch")) {
|
|
13310
|
+
throw new Error(t("webErrors.cannotReach", { endpoint: TAVILY_ENDPOINT }));
|
|
13311
|
+
}
|
|
13312
|
+
throw err;
|
|
13313
|
+
}
|
|
13314
|
+
if (!resp.ok) {
|
|
13315
|
+
if (resp.status === 401 || resp.status === 403) {
|
|
13316
|
+
throw new Error(t("webErrors.tavilyUnauthorized"));
|
|
13317
|
+
}
|
|
13318
|
+
if (resp.status === 429) throw new Error(t("webErrors.tavilyRateLimit"));
|
|
13319
|
+
throw new Error(t("webErrors.tavilyServerError", { status: resp.status }));
|
|
13320
|
+
}
|
|
13321
|
+
let data;
|
|
13322
|
+
try {
|
|
13323
|
+
data = await resp.json();
|
|
13324
|
+
} catch {
|
|
13325
|
+
throw new Error(t("webErrors.tavilyParseError", { status: resp.status }));
|
|
13326
|
+
}
|
|
13327
|
+
const results = data.results ?? [];
|
|
13328
|
+
return results.slice(0, topK).map((r) => ({
|
|
13329
|
+
title: r.title,
|
|
13330
|
+
url: r.url,
|
|
13331
|
+
snippet: r.content ?? ""
|
|
13332
|
+
}));
|
|
13333
|
+
}
|
|
12899
13334
|
function parseSearxngHtmlResults(html) {
|
|
12900
13335
|
const root = parseHtml(html);
|
|
12901
13336
|
const results = [];
|
|
@@ -13114,7 +13549,7 @@ function registerWebTools(registry, opts = {}) {
|
|
|
13114
13549
|
const maxFetchChars = opts.maxFetchChars ?? DEFAULT_FETCH_MAX_CHARS;
|
|
13115
13550
|
registry.register({
|
|
13116
13551
|
name: "web_search",
|
|
13117
|
-
description: "Search the public web. Returns ranked results with title, url, and snippet. Call this when the answer's correctness depends on current state \u2014 anything that changes over time (events, prices, releases, status of a thing in the real world). Composing such answers from training memory invents stale numbers; search first, then ground the answer in the results. For evergreen / definitional questions you don't need this. To change the backend, use /search-engine mojeek|searxng|metaso.",
|
|
13552
|
+
description: "Search the public web. Returns ranked results with title, url, and snippet. Call this when the answer's correctness depends on current state \u2014 anything that changes over time (events, prices, releases, status of a thing in the real world). Composing such answers from training memory invents stale numbers; search first, then ground the answer in the results. For evergreen / definitional questions you don't need this. To change the backend, use /search-engine mojeek|searxng|metaso|tavily.",
|
|
13118
13553
|
readOnly: true,
|
|
13119
13554
|
parallelSafe: true,
|
|
13120
13555
|
parameters: {
|
|
@@ -13800,19 +14235,23 @@ var McpClient = class {
|
|
|
13800
14235
|
return this._instructions;
|
|
13801
14236
|
}
|
|
13802
14237
|
/** Compliant servers reject other methods until this completes. */
|
|
13803
|
-
async initialize() {
|
|
14238
|
+
async initialize(opts = {}) {
|
|
13804
14239
|
if (this.initialized) throw new Error("MCP client already initialized");
|
|
13805
14240
|
this.startReaderIfNeeded();
|
|
13806
|
-
const result = await this.request(
|
|
13807
|
-
|
|
13808
|
-
|
|
13809
|
-
|
|
13810
|
-
|
|
13811
|
-
|
|
13812
|
-
|
|
13813
|
-
|
|
13814
|
-
|
|
13815
|
-
|
|
14241
|
+
const result = await this.request(
|
|
14242
|
+
"initialize",
|
|
14243
|
+
{
|
|
14244
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
14245
|
+
// Advertise every method the client can consume so servers know
|
|
14246
|
+
// they can send listChanged notifications etc. Sub-feature flags
|
|
14247
|
+
// (e.g. `resources.subscribe`) are omitted — we don't implement
|
|
14248
|
+
// those yet and the empty object means "method-level support, no
|
|
14249
|
+
// sub-features."
|
|
14250
|
+
capabilities: { tools: {}, resources: {}, prompts: {} },
|
|
14251
|
+
clientInfo: this.clientInfo
|
|
14252
|
+
},
|
|
14253
|
+
opts.signal
|
|
14254
|
+
);
|
|
13816
14255
|
this._serverCapabilities = result.capabilities ?? {};
|
|
13817
14256
|
this._serverInfo = result.serverInfo ?? { name: "", version: "" };
|
|
13818
14257
|
this._protocolVersion = result.protocolVersion ?? "";
|