reasonix 0.47.1 → 0.48.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 +5 -26
- package/README.zh-CN.md +5 -26
- package/dist/cli/{acp-GEOAKSTU.js → acp-4ROCGYNH.js} +17 -17
- package/dist/cli/{chat-YTPATMMG.js → chat-GZNB5625.js} +24 -24
- package/dist/cli/{chunk-BQ6HC66J.js → chunk-2QSTA2QV.js} +3 -13
- package/dist/cli/chunk-2QSTA2QV.js.map +1 -0
- package/dist/cli/{chunk-DQ6K5ZQ7.js → chunk-3WGTGXO4.js} +2 -2
- package/dist/cli/{chunk-6QC5RQLE.js → chunk-5OHHAQ4W.js} +2 -2
- package/dist/cli/{chunk-XMHP7BEE.js → chunk-6MZTZO7A.js} +514 -791
- package/dist/cli/chunk-6MZTZO7A.js.map +1 -0
- package/dist/cli/chunk-7M4YYMKW.js +5198 -0
- package/dist/cli/chunk-7M4YYMKW.js.map +1 -0
- package/dist/cli/{chunk-KYQVQ5X4.js → chunk-B5CZL2SE.js} +9 -4
- package/dist/cli/chunk-B5CZL2SE.js.map +1 -0
- package/dist/cli/chunk-CDVSFSAK.js +17732 -0
- package/dist/cli/chunk-CDVSFSAK.js.map +1 -0
- package/dist/cli/{chunk-TRWHTFG7.js → chunk-DOWEOA6E.js} +2 -2
- package/dist/cli/chunk-EMMENC4O.js +831 -0
- package/dist/cli/chunk-EMMENC4O.js.map +1 -0
- package/dist/cli/{chunk-TDHXB2ER.js → chunk-H4CCXMDD.js} +2 -2
- package/dist/cli/{chunk-T5A7EY6B.js → chunk-HR5NBKEM.js} +2 -2
- package/dist/cli/{chunk-CNG32VAB.js → chunk-I4M5QJNL.js} +2 -2
- package/dist/cli/{chunk-5QCB62C4.js → chunk-J2TQAWOM.js} +135 -18
- package/dist/cli/{chunk-5QCB62C4.js.map → chunk-J2TQAWOM.js.map} +1 -1
- package/dist/cli/{chunk-DN4B5S6Y.js → chunk-JMDE6IO3.js} +2 -2
- package/dist/cli/{chunk-4MFCAZ2W.js → chunk-MOJYKO2A.js} +3 -3
- package/dist/cli/{chunk-HUILPCYX.js → chunk-MRZG4GBF.js} +3 -3
- package/dist/cli/{chunk-GH7DC2Y5.js → chunk-NMQSUNLB.js} +2 -2
- package/dist/cli/{chunk-ZXSCAODE.js → chunk-OB4BUJBL.js} +67 -2
- package/dist/cli/chunk-OB4BUJBL.js.map +1 -0
- package/dist/cli/{chunk-QCFLPSPH.js → chunk-OG5JANQ4.js} +2 -2
- package/dist/cli/{chunk-JBH5RM7X.js → chunk-OPYALNTT.js} +326 -55
- package/dist/cli/chunk-OPYALNTT.js.map +1 -0
- package/dist/cli/{chunk-CCJAP7G3.js → chunk-RUDBUHO4.js} +2 -2
- package/dist/cli/{chunk-2XY77LW7.js → chunk-S2RMQULY.js} +56 -24
- package/dist/cli/chunk-S2RMQULY.js.map +1 -0
- package/dist/cli/{chunk-DWPAKZTY.js → chunk-TE5UIIFL.js} +2 -2
- package/dist/cli/{chunk-KVZZ5U75.js → chunk-V4Y732RQ.js} +2 -2
- package/dist/cli/{chunk-TRSAHHCL.js → chunk-WZGNXR6E.js} +3 -3
- package/dist/cli/chunk-WZGNXR6E.js.map +1 -0
- package/dist/cli/{chunk-NRQ5UP5T.js → chunk-YW63N3ZR.js} +116 -28
- package/dist/cli/chunk-YW63N3ZR.js.map +1 -0
- package/dist/cli/{code-Q4NRVEDG.js → code-PMPJWXEO.js} +30 -31
- package/dist/cli/code-PMPJWXEO.js.map +1 -0
- package/dist/cli/{commands-4CDI4GFM.js → commands-QS6TG4G3.js} +4 -4
- package/dist/cli/{commit-GW7LDQP5.js → commit-XPRSKUBF.js} +3 -3
- package/dist/cli/{desktop-EG6P5SF2.js → desktop-562OPWIU.js} +461 -43
- package/dist/cli/desktop-562OPWIU.js.map +1 -0
- package/dist/cli/{diff-VI2YX4FN.js → diff-I6W4AUWJ.js} +8 -8
- package/dist/cli/{doctor-CQTTZP27.js → doctor-6XVZKT4U.js} +9 -9
- package/dist/cli/index.js +52 -40
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{mcp-J2UCD4RZ.js → mcp-7W7ANO2Y.js} +2 -2
- package/dist/cli/{mcp-browse-GSX34JEK.js → mcp-browse-LA4I4YIZ.js} +2 -2
- package/dist/cli/{mcp-inspect-RRFYF4ZV.js → mcp-inspect-LWXXU7BY.js} +2 -2
- package/dist/cli/{prompt-5TQPIVHV.js → prompt-RKZD4X6Y.js} +3 -3
- package/dist/cli/{replay-MJCEMODU.js → replay-2X7MVXOI.js} +8 -8
- package/dist/cli/{run-P4D5VDYE.js → run-TPKXIJ27.js} +13 -13
- package/dist/cli/{server-C25JNNZV.js → server-NHQ3QXOZ.js} +15 -14
- package/dist/cli/{server-C25JNNZV.js.map → server-NHQ3QXOZ.js.map} +1 -1
- package/dist/cli/{sessions-QIONZJQ6.js → sessions-2A4DGSHA.js} +12 -12
- package/dist/cli/{setup-NLQ6G5G4.js → setup-GOLP7J4C.js} +5 -5
- package/dist/cli/{stats-DFZEXHP4.js → stats-CGDAFDKI.js} +6 -6
- package/dist/cli/{version-GR3X3MPI.js → version-FIL4ZFOS.js} +12 -12
- package/dist/grammars/tree-sitter-go.wasm +0 -0
- package/dist/grammars/tree-sitter-java.wasm +0 -0
- package/dist/grammars/tree-sitter-javascript.wasm +0 -0
- package/dist/grammars/tree-sitter-python.wasm +0 -0
- package/dist/grammars/tree-sitter-rust.wasm +0 -0
- package/dist/grammars/tree-sitter-tsx.wasm +0 -0
- package/dist/grammars/tree-sitter-typescript.wasm +0 -0
- package/dist/grammars/web-tree-sitter.wasm +0 -0
- package/dist/index.d.ts +38 -10
- package/dist/index.js +488 -87
- package/dist/index.js.map +1 -1
- package/package.json +13 -4
- package/dist/cli/chunk-2XY77LW7.js.map +0 -1
- package/dist/cli/chunk-6CRPCJAU.js +0 -3141
- package/dist/cli/chunk-6CRPCJAU.js.map +0 -1
- package/dist/cli/chunk-BQ6HC66J.js.map +0 -1
- package/dist/cli/chunk-JBH5RM7X.js.map +0 -1
- package/dist/cli/chunk-KYQVQ5X4.js.map +0 -1
- package/dist/cli/chunk-NRQ5UP5T.js.map +0 -1
- package/dist/cli/chunk-TRSAHHCL.js.map +0 -1
- package/dist/cli/chunk-XD6P7AFH.js +0 -375
- package/dist/cli/chunk-XD6P7AFH.js.map +0 -1
- package/dist/cli/chunk-XMHP7BEE.js.map +0 -1
- package/dist/cli/chunk-YFP3MYMY.js +0 -323
- package/dist/cli/chunk-YFP3MYMY.js.map +0 -1
- package/dist/cli/chunk-ZXSCAODE.js.map +0 -1
- package/dist/cli/code-Q4NRVEDG.js.map +0 -1
- package/dist/cli/desktop-EG6P5SF2.js.map +0 -1
- /package/dist/cli/{acp-GEOAKSTU.js.map → acp-4ROCGYNH.js.map} +0 -0
- /package/dist/cli/{chat-YTPATMMG.js.map → chat-GZNB5625.js.map} +0 -0
- /package/dist/cli/{chunk-DQ6K5ZQ7.js.map → chunk-3WGTGXO4.js.map} +0 -0
- /package/dist/cli/{chunk-6QC5RQLE.js.map → chunk-5OHHAQ4W.js.map} +0 -0
- /package/dist/cli/{chunk-TRWHTFG7.js.map → chunk-DOWEOA6E.js.map} +0 -0
- /package/dist/cli/{chunk-TDHXB2ER.js.map → chunk-H4CCXMDD.js.map} +0 -0
- /package/dist/cli/{chunk-T5A7EY6B.js.map → chunk-HR5NBKEM.js.map} +0 -0
- /package/dist/cli/{chunk-CNG32VAB.js.map → chunk-I4M5QJNL.js.map} +0 -0
- /package/dist/cli/{chunk-DN4B5S6Y.js.map → chunk-JMDE6IO3.js.map} +0 -0
- /package/dist/cli/{chunk-4MFCAZ2W.js.map → chunk-MOJYKO2A.js.map} +0 -0
- /package/dist/cli/{chunk-HUILPCYX.js.map → chunk-MRZG4GBF.js.map} +0 -0
- /package/dist/cli/{chunk-GH7DC2Y5.js.map → chunk-NMQSUNLB.js.map} +0 -0
- /package/dist/cli/{chunk-QCFLPSPH.js.map → chunk-OG5JANQ4.js.map} +0 -0
- /package/dist/cli/{chunk-CCJAP7G3.js.map → chunk-RUDBUHO4.js.map} +0 -0
- /package/dist/cli/{chunk-DWPAKZTY.js.map → chunk-TE5UIIFL.js.map} +0 -0
- /package/dist/cli/{chunk-KVZZ5U75.js.map → chunk-V4Y732RQ.js.map} +0 -0
- /package/dist/cli/{commands-4CDI4GFM.js.map → commands-QS6TG4G3.js.map} +0 -0
- /package/dist/cli/{commit-GW7LDQP5.js.map → commit-XPRSKUBF.js.map} +0 -0
- /package/dist/cli/{diff-VI2YX4FN.js.map → diff-I6W4AUWJ.js.map} +0 -0
- /package/dist/cli/{doctor-CQTTZP27.js.map → doctor-6XVZKT4U.js.map} +0 -0
- /package/dist/cli/{mcp-J2UCD4RZ.js.map → mcp-7W7ANO2Y.js.map} +0 -0
- /package/dist/cli/{mcp-browse-GSX34JEK.js.map → mcp-browse-LA4I4YIZ.js.map} +0 -0
- /package/dist/cli/{mcp-inspect-RRFYF4ZV.js.map → mcp-inspect-LWXXU7BY.js.map} +0 -0
- /package/dist/cli/{prompt-5TQPIVHV.js.map → prompt-RKZD4X6Y.js.map} +0 -0
- /package/dist/cli/{replay-MJCEMODU.js.map → replay-2X7MVXOI.js.map} +0 -0
- /package/dist/cli/{run-P4D5VDYE.js.map → run-TPKXIJ27.js.map} +0 -0
- /package/dist/cli/{sessions-QIONZJQ6.js.map → sessions-2A4DGSHA.js.map} +0 -0
- /package/dist/cli/{setup-NLQ6G5G4.js.map → setup-GOLP7J4C.js.map} +0 -0
- /package/dist/cli/{stats-DFZEXHP4.js.map → stats-CGDAFDKI.js.map} +0 -0
- /package/dist/cli/{version-GR3X3MPI.js.map → version-FIL4ZFOS.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { createParser } from "eventsource-parser";
|
|
|
5
5
|
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
6
6
|
import { homedir } from "os";
|
|
7
7
|
import { dirname, isAbsolute, join, resolve } from "path";
|
|
8
|
+
import { z } from "zod";
|
|
8
9
|
|
|
9
10
|
// src/cli/ui/theme/tokens.ts
|
|
10
11
|
function card(fg, tone) {
|
|
@@ -478,14 +479,65 @@ function loadTavilyApiKey(path2 = defaultConfigPath()) {
|
|
|
478
479
|
if (cfg && typeof cfg === "string" && cfg.trim()) return cfg.trim();
|
|
479
480
|
return void 0;
|
|
480
481
|
}
|
|
482
|
+
function loadPerplexityApiKey(path2 = defaultConfigPath()) {
|
|
483
|
+
if (process.env.PERPLEXITY_API_KEY) return process.env.PERPLEXITY_API_KEY.trim();
|
|
484
|
+
const cfg = readConfig(path2).perplexityApiKey;
|
|
485
|
+
if (cfg && typeof cfg === "string" && cfg.trim()) return cfg.trim();
|
|
486
|
+
return void 0;
|
|
487
|
+
}
|
|
488
|
+
function loadExaApiKey(path2 = defaultConfigPath()) {
|
|
489
|
+
if (process.env.EXA_API_KEY) return process.env.EXA_API_KEY.trim();
|
|
490
|
+
const cfg = readConfig(path2).exaApiKey;
|
|
491
|
+
if (cfg && typeof cfg === "string" && cfg.trim()) return cfg.trim();
|
|
492
|
+
return void 0;
|
|
493
|
+
}
|
|
481
494
|
function defaultConfigPath() {
|
|
482
495
|
return join(homedir(), ".reasonix", "config.json");
|
|
483
496
|
}
|
|
497
|
+
var STRING_ARRAY_FIELDS = [
|
|
498
|
+
["mcp"],
|
|
499
|
+
["mcpDisabled"],
|
|
500
|
+
["recentWorkspaces"],
|
|
501
|
+
["skills", "paths"]
|
|
502
|
+
];
|
|
503
|
+
var stringArraySchema = z.array(z.string());
|
|
504
|
+
function sanitizeStringArrayField(cfg, segments, filePath) {
|
|
505
|
+
if (segments.length === 0) return;
|
|
506
|
+
let parent = cfg;
|
|
507
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
508
|
+
const seg = segments[i];
|
|
509
|
+
const next = parent[seg];
|
|
510
|
+
if (!next || typeof next !== "object" || Array.isArray(next)) return;
|
|
511
|
+
parent = next;
|
|
512
|
+
}
|
|
513
|
+
const leaf = segments[segments.length - 1];
|
|
514
|
+
const value = parent[leaf];
|
|
515
|
+
if (value === void 0) return;
|
|
516
|
+
const fieldName = segments.join(".");
|
|
517
|
+
if (!Array.isArray(value)) {
|
|
518
|
+
console.warn(`reasonix: config "${filePath}" field "${fieldName}" is not an array \u2014 ignoring`);
|
|
519
|
+
delete parent[leaf];
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
const parsed = stringArraySchema.safeParse(value);
|
|
523
|
+
if (parsed.success) return;
|
|
524
|
+
const filtered = value.filter((x) => typeof x === "string");
|
|
525
|
+
console.warn(
|
|
526
|
+
`reasonix: config "${filePath}" field "${fieldName}" had ${value.length - filtered.length} non-string item(s) \u2014 dropped`
|
|
527
|
+
);
|
|
528
|
+
parent[leaf] = filtered;
|
|
529
|
+
}
|
|
484
530
|
function readConfig(path2 = defaultConfigPath()) {
|
|
485
531
|
try {
|
|
486
|
-
const raw = readFileSync(path2, "utf8");
|
|
532
|
+
const raw = readFileSync(path2, "utf8").replace(/^\uFEFF/, "");
|
|
487
533
|
const parsed = JSON.parse(raw);
|
|
488
|
-
if (parsed && typeof parsed === "object"
|
|
534
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
535
|
+
const cfg = parsed;
|
|
536
|
+
for (const segments of STRING_ARRAY_FIELDS) {
|
|
537
|
+
sanitizeStringArrayField(cfg, segments, path2);
|
|
538
|
+
}
|
|
539
|
+
return cfg;
|
|
540
|
+
}
|
|
489
541
|
} catch {
|
|
490
542
|
}
|
|
491
543
|
return {};
|
|
@@ -580,6 +632,8 @@ function webSearchEngine(path2 = defaultConfigPath()) {
|
|
|
580
632
|
if (cfg === "searxng") return "searxng";
|
|
581
633
|
if (cfg === "metaso") return "metaso";
|
|
582
634
|
if (cfg === "tavily") return "tavily";
|
|
635
|
+
if (cfg === "perplexity") return "perplexity";
|
|
636
|
+
if (cfg === "exa") return "exa";
|
|
583
637
|
return "mojeek";
|
|
584
638
|
}
|
|
585
639
|
function webSearchEndpoint(path2 = defaultConfigPath()) {
|
|
@@ -1286,6 +1340,8 @@ var EN = {
|
|
|
1286
1340
|
tipShownOnce: "shown once",
|
|
1287
1341
|
modelOverride: "override the default model",
|
|
1288
1342
|
noSession: "disable session persistence for this run",
|
|
1343
|
+
noMouseHint: "disable SGR mouse tracking; restores native drag-select and right-click",
|
|
1344
|
+
noProxyHint: "ignore HTTPS_PROXY / HTTP_PROXY for this run; go direct",
|
|
1289
1345
|
resumeHint: "force-resume the named session (even if idle)",
|
|
1290
1346
|
newHint: "force a fresh session (ignore --session / --continue)",
|
|
1291
1347
|
transcriptHint: "path to write the JSONL transcript",
|
|
@@ -1427,6 +1483,7 @@ var EN = {
|
|
|
1427
1483
|
},
|
|
1428
1484
|
stop: { description: "abort the current model turn (typed alternative to Esc)" },
|
|
1429
1485
|
feedback: { description: "open a GitHub issue with diagnostic info copied to clipboard" },
|
|
1486
|
+
about: { description: "project info \u2014 version, website, repo, license" },
|
|
1430
1487
|
keys: { description: "keyboard + mouse + copy/paste reference" },
|
|
1431
1488
|
plans: { description: "list this session's active + archived plans, newest first" },
|
|
1432
1489
|
replay: {
|
|
@@ -1502,8 +1559,8 @@ var EN = {
|
|
|
1502
1559
|
argsHint: "<question>"
|
|
1503
1560
|
},
|
|
1504
1561
|
"search-engine": {
|
|
1505
|
-
description: "switch web search backend \u2014 mojeek (default, no deps), searxng (self-hosted),
|
|
1506
|
-
argsHint: "<mojeek|searxng|metaso> [<
|
|
1562
|
+
description: "switch web search backend \u2014 mojeek (default, no deps), searxng (self-hosted), metaso (free 100/d), tavily (free 1000/mo), perplexity (AI-native), or exa (AI-native)",
|
|
1563
|
+
argsHint: "<mojeek|searxng|metaso|tavily|perplexity|exa> [<key>]"
|
|
1507
1564
|
}
|
|
1508
1565
|
},
|
|
1509
1566
|
wizard: {
|
|
@@ -1694,7 +1751,22 @@ var EN = {
|
|
|
1694
1751
|
continuingAfter: "\u25B8 continuing after {label}{counter}",
|
|
1695
1752
|
planStoppedAt: "\u25B8 plan stopped at {label}{counter}",
|
|
1696
1753
|
revisingAfter: "\u25B8 revising after {label} \u2014 {feedback}",
|
|
1697
|
-
historyScrollHint: " \u2191 reading history \xB7 End / PgDn returns to bottom \xB7 \u2193 advances one line"
|
|
1754
|
+
historyScrollHint: " \u2191 reading history \xB7 End / PgDn returns to bottom \xB7 \u2193 advances one line",
|
|
1755
|
+
editHistoryTitle: "Edit history (oldest first):",
|
|
1756
|
+
editHistoryNoCodeMode: "not in code mode",
|
|
1757
|
+
editHistoryNoEdits: "no edits recorded this session yet",
|
|
1758
|
+
editHistoryNoShowId: "usage: /show [id] [path] (omit id for newest; path from the per-file summary)",
|
|
1759
|
+
editHistoryIdNotFound: "no edit #{id} \u2014 run /history to see valid ids",
|
|
1760
|
+
editHistoryLookupFailed: "unexpected: history lookup failed",
|
|
1761
|
+
editHistoryBatchNoFile: `batch #{id} doesn't include "{path}" \u2014 files in this batch: {files}`,
|
|
1762
|
+
editHistoryNoEdits2: "no edits recorded this session \u2014 /history is empty",
|
|
1763
|
+
editHistoryStatusApplied: "applied",
|
|
1764
|
+
editHistoryStatusPartial: "PARTIAL",
|
|
1765
|
+
editHistoryStatusUndone: "UNDONE",
|
|
1766
|
+
editHistoryHelpShow: "/show <id> \u2192 per-file summary \xB7 /show <id> <path> \u2192 full diff of one file",
|
|
1767
|
+
editHistoryHelpUndo: "/undo \u2192 newest non-undone \xB7 /undo <id> [path] \u2192 target a specific batch or file",
|
|
1768
|
+
editHistoryAlreadyReverted: "(already reverted \u2014 /history shows the batch-level status)",
|
|
1769
|
+
editHistoryRevertFile: "/undo {id} {path} \u2192 revert just this file"
|
|
1698
1770
|
},
|
|
1699
1771
|
hooks: {
|
|
1700
1772
|
head: "hook {tag} `{cmd}` {decision}{truncTag}",
|
|
@@ -1795,6 +1867,10 @@ var EN = {
|
|
|
1795
1867
|
loopNoActiveHint: "no active loop. Start one with `/loop <interval> <prompt>` (e.g. /loop 30s npm test).\nCancels on: /loop stop \xB7 Esc \xB7 /clear /new \xB7 any user-typed prompt.",
|
|
1796
1868
|
loopStarted: '\u25B8 loop started \u2014 re-submitting "{prompt}" every {duration}. Type anything (or /loop stop) to cancel.',
|
|
1797
1869
|
keysNeedsTui: "/keys needs a TUI context (postKeys wired).",
|
|
1870
|
+
aboutHeader: "Reasonix v{version} \u2014 a cache-first DeepSeek coding agent",
|
|
1871
|
+
aboutWebsiteLabel: "Website",
|
|
1872
|
+
aboutRepoLabel: "GitHub ",
|
|
1873
|
+
aboutLicenseLabel: "License",
|
|
1798
1874
|
unknownCommand: "unknown command: /{cmd} \u2014 did you mean {list}?",
|
|
1799
1875
|
unknownCommandShort: "unknown command: /{cmd} (try /help)"
|
|
1800
1876
|
},
|
|
@@ -2112,6 +2188,8 @@ var EN = {
|
|
|
2112
2188
|
usageSearxngUrl: " /search-engine searxng <url> use SearXNG at custom endpoint",
|
|
2113
2189
|
usageMetaso: " /search-engine metaso use Metaso API (100/d free, configure your own API key for more)",
|
|
2114
2190
|
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)",
|
|
2191
|
+
usagePerplexity: " /search-engine perplexity use Perplexity AI (AI-native answer + citations \u2014 set PERPLEXITY_API_KEY or perplexityApiKey in config; get one at https://perplexity.ai/settings/api)",
|
|
2192
|
+
usageExa: " /search-engine exa use Exa API (AI-native answer + citations, free 1000/mo \u2014 set EXA_API_KEY or exaApiKey in config; sign up at https://exa.ai)",
|
|
2115
2193
|
alias: "Alias: /se",
|
|
2116
2194
|
searxngInfo: "SearXNG is a self-hosted metasearch engine (https://github.com/searxng/searxng).",
|
|
2117
2195
|
searxngInstall: "Install it with: docker run -d -p 8080:8080 searxng/searxng",
|
|
@@ -2119,7 +2197,11 @@ var EN = {
|
|
|
2119
2197
|
switchedSearxngNote: " Make sure SearXNG is running at {endpoint}.",
|
|
2120
2198
|
switchedMetasoNote: " There is a daily quota of 100 (configure your own API key for higher limits).",
|
|
2121
2199
|
switchedTavilyNote: " Set TAVILY_API_KEY or `tavilyApiKey` in config; free 1000/mo at https://tavily.com.",
|
|
2122
|
-
|
|
2200
|
+
switchedPerplexityNote: " Set PERPLEXITY_API_KEY or `perplexityApiKey` in config; get one at https://perplexity.ai/settings/api.",
|
|
2201
|
+
switchedExaNote: " Set EXA_API_KEY or `exaApiKey` in config; sign up at https://exa.ai.",
|
|
2202
|
+
keyNeeded: 'No API key configured for "{engine}".\n\n 1. Set the {envVar} environment variable\n 2. Or provide one inline: /search-engine {engine} <your-key>\n 3. Or add "{engine}ApiKey" to ~/.reasonix/config.json\n\nThen retry /search-engine {engine}.',
|
|
2203
|
+
keySaved: " API key saved to config.",
|
|
2204
|
+
confirmed: 'Web search engine set to "{engine}"{detail}. Next assistant turn will pick up the change.',
|
|
2123
2205
|
confirmedDetail: " ({endpoint})"
|
|
2124
2206
|
},
|
|
2125
2207
|
skill: {
|
|
@@ -2378,27 +2460,37 @@ var EN = {
|
|
|
2378
2460
|
probeFailed: "probe failed \u2014 {message}"
|
|
2379
2461
|
},
|
|
2380
2462
|
webErrors: {
|
|
2381
|
-
status: "web_search {status} \u2014 try: the search backend returned an error; rephrase the query, or switch engine with /search-engine mojeek|searxng",
|
|
2463
|
+
status: "web_search {status} \u2014 try: the search backend returned an error; rephrase the query, or switch engine with /search-engine mojeek|searxng|metaso|tavily|perplexity|exa",
|
|
2382
2464
|
rateLimit429: "web_search 429 \u2014 try: wait 10s before retrying, or rephrase the query; the search backend is rate-limiting this client",
|
|
2383
|
-
forbidden403: "web_search 403 \u2014 try: the search backend is blocking this client; switch engine with /search-engine mojeek|searxng, or wait and retry later",
|
|
2465
|
+
forbidden403: "web_search 403 \u2014 try: the search backend is blocking this client; switch engine with /search-engine mojeek|searxng|metaso|tavily|perplexity|exa, or wait and retry later",
|
|
2384
2466
|
serverError5xx: "web_search {status} \u2014 try: open the search URL in a browser; if it loads this is transient and a retry in 30s may help",
|
|
2385
|
-
mojeekBlocked: "web_search: Mojeek anti-bot page \u2014 rate-limited or blocked \u2014 try: wait 30s and retry, or switch engine with /search-engine searxng",
|
|
2386
|
-
mojeekNoResults: "web_search: 0 results but response doesn't look like a real empty page ({chars} chars, first 120: {preview}) \u2014 try: rephrase the query with simpler terms, or switch engine with /search-engine searxng",
|
|
2467
|
+
mojeekBlocked: "web_search: Mojeek anti-bot page \u2014 rate-limited or blocked \u2014 try: wait 30s and retry, or switch engine with /search-engine mojeek|searxng|metaso|tavily|perplexity|exa",
|
|
2468
|
+
mojeekNoResults: "web_search: 0 results but response doesn't look like a real empty page ({chars} chars, first 120: {preview}) \u2014 try: rephrase the query with simpler terms, or switch engine with /search-engine mojeek|searxng|metaso|tavily|perplexity|exa",
|
|
2387
2469
|
invalidEndpoint: 'web_search: invalid SearXNG endpoint "{endpoint}" \u2014 try: set a valid URL with /search-endpoint http://host:port',
|
|
2388
2470
|
endpointMustBeHttp: "web_search: SearXNG endpoint must be http(s), got {protocol} \u2014 try: set a valid URL with /search-endpoint http://host:port",
|
|
2389
|
-
cannotReach: "web_search: Cannot reach SearXNG server at {endpoint} \u2014 try: install and start SearXNG (https://github.com/searxng/searxng, e.g. `docker run -d -p 8080:8080 searxng/searxng`), or switch to
|
|
2390
|
-
searxngNoResults: "web_search: 0 results but SearXNG response doesn't look like an empty results page ({chars} chars) \u2014 try: rephrase the query with simpler terms, or switch engine with /search-engine mojeek",
|
|
2471
|
+
cannotReach: "web_search: Cannot reach SearXNG server at {endpoint} \u2014 try: install and start SearXNG (https://github.com/searxng/searxng, e.g. `docker run -d -p 8080:8080 searxng/searxng`), or switch to another engine with /search-engine mojeek|searxng|metaso|tavily|perplexity|exa",
|
|
2472
|
+
searxngNoResults: "web_search: 0 results but SearXNG response doesn't look like an empty results page ({chars} chars) \u2014 try: rephrase the query with simpler terms, or switch engine with /search-engine mojeek|searxng|metaso|tavily|perplexity|exa",
|
|
2391
2473
|
metasoDailyLimit: "web_search: daily search limit reached for the default API key \u2014 set your own METASO_API_KEY env var or get one at https://metaso.cn/search-api/playground",
|
|
2392
2474
|
metasoUnauthorized: "web_search: Metaso API key rejected \u2014 check METASO_API_KEY or get one at https://metaso.cn/search-api/playground",
|
|
2393
2475
|
metasoRateLimit: "web_search: Metaso rate-limited \u2014 wait and retry, or get your own API key at https://metaso.cn/search-api/playground",
|
|
2394
|
-
metasoServerError: "web_search: Metaso server error ({status}) \u2014 try again later, or switch engine with /search-engine mojeek",
|
|
2476
|
+
metasoServerError: "web_search: Metaso server error ({status}) \u2014 try again later, or switch engine with /search-engine mojeek|searxng|metaso|tavily|perplexity|exa",
|
|
2395
2477
|
metasoParseError: "web_search: Metaso returned unparseable response (HTTP {status}) \u2014 try again later",
|
|
2396
2478
|
metasoApiError: "web_search: Metaso API error (code {code}: {message}) \u2014 try again later",
|
|
2397
2479
|
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",
|
|
2398
2480
|
tavilyUnauthorized: "web_search: Tavily API key rejected \u2014 check TAVILY_API_KEY or get one at https://tavily.com",
|
|
2399
|
-
tavilyRateLimit: "web_search: Tavily rate-limited or monthly quota exceeded \u2014 wait, switch engine with /search-engine mojeek, or upgrade your Tavily plan",
|
|
2400
|
-
tavilyServerError: "web_search: Tavily server error ({status}) \u2014 try again later, or switch engine with /search-engine mojeek",
|
|
2481
|
+
tavilyRateLimit: "web_search: Tavily rate-limited or monthly quota exceeded \u2014 wait, switch engine with /search-engine mojeek|searxng|metaso|tavily|perplexity|exa, or upgrade your Tavily plan",
|
|
2482
|
+
tavilyServerError: "web_search: Tavily server error ({status}) \u2014 try again later, or switch engine with /search-engine mojeek|searxng|metaso|tavily|perplexity|exa",
|
|
2401
2483
|
tavilyParseError: "web_search: Tavily returned unparseable response (HTTP {status}) \u2014 try again later",
|
|
2484
|
+
perplexityMissingKey: "web_search: Perplexity backend requires an API key \u2014 set PERPLEXITY_API_KEY env var or `perplexityApiKey` in ~/.reasonix/config.json; get one at https://perplexity.ai/settings/api",
|
|
2485
|
+
perplexityUnauthorized: "web_search: Perplexity API key rejected \u2014 check PERPLEXITY_API_KEY or get one at https://perplexity.ai/settings/api",
|
|
2486
|
+
perplexityRateLimit: "web_search: Perplexity rate-limited \u2014 wait and retry, or switch engine with /search-engine mojeek|searxng|metaso|tavily|perplexity|exa",
|
|
2487
|
+
perplexityServerError: "web_search: Perplexity server error ({status}) \u2014 try again later, or switch engine with /search-engine mojeek|searxng|metaso|tavily|perplexity|exa",
|
|
2488
|
+
perplexityParseError: "web_search: Perplexity returned unparseable response (HTTP {status}) \u2014 try again later",
|
|
2489
|
+
exaMissingKey: "web_search: Exa backend requires an API key \u2014 set EXA_API_KEY env var or `exaApiKey` in ~/.reasonix/config.json; free 1000/mo signup at https://exa.ai",
|
|
2490
|
+
exaUnauthorized: "web_search: Exa API key rejected \u2014 check EXA_API_KEY or get one at https://exa.ai",
|
|
2491
|
+
exaRateLimit: "web_search: Exa API rate-limited or monthly quota exceeded \u2014 wait or upgrade at https://exa.ai/pricing",
|
|
2492
|
+
exaServerError: "web_search: Exa server error ({status}) \u2014 try again later, or switch engine with /search-engine mojeek|searxng|metaso|tavily|perplexity|exa",
|
|
2493
|
+
exaParseError: "web_search: Exa returned unparseable response (HTTP {status}) \u2014 try again later",
|
|
2402
2494
|
fetchStatus: "web_fetch {status} for {url} \u2014 try: confirm the URL resolves in a browser; status suggests the host returned an error page",
|
|
2403
2495
|
fetchRateLimit429: "web_fetch 429 for {url} \u2014 try: wait 10s before retrying; the host is rate-limiting this client",
|
|
2404
2496
|
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",
|
|
@@ -2566,6 +2658,12 @@ var EN = {
|
|
|
2566
2658
|
serverCount: "{count} server{s}",
|
|
2567
2659
|
footer: "\u2191\u2193 pick \xB7 [r] reconnect \xB7 [d] disable \xB7 esc quit"
|
|
2568
2660
|
},
|
|
2661
|
+
mcpBrowse: {
|
|
2662
|
+
noResources: "No resources on any connected MCP server (or no servers connected). `/mcp` shows the current set.",
|
|
2663
|
+
readOne: "Read one: `/resource <uri>` \u2014 or use Tab in the picker.",
|
|
2664
|
+
noPrompts: "No prompts on any connected MCP server (or no servers connected). `/mcp` shows the current set.",
|
|
2665
|
+
fetchOne: "Fetch one: `/prompt <name>` \u2014 args are not supported yet; prompts with required args will surface an error from the server."
|
|
2666
|
+
},
|
|
2569
2667
|
mcpLifecycle: {
|
|
2570
2668
|
handshake: "handshake\u2026",
|
|
2571
2669
|
connected: "connected",
|
|
@@ -2796,6 +2894,8 @@ var zhCN = {
|
|
|
2796
2894
|
tipShownOnce: "\u4EC5\u663E\u793A\u4E00\u6B21",
|
|
2797
2895
|
modelOverride: "\u8986\u76D6\u9ED8\u8BA4\u6A21\u578B",
|
|
2798
2896
|
noSession: "\u7981\u7528\u672C\u6B21\u8FD0\u884C\u7684\u4F1A\u8BDD\u6301\u4E45\u5316",
|
|
2897
|
+
noMouseHint: "\u5173\u95ED SGR \u9F20\u6807\u8DDF\u8E2A\uFF1B\u6062\u590D\u7EC8\u7AEF\u539F\u751F\u62D6\u9009\u548C\u53F3\u952E\u884C\u4E3A",
|
|
2898
|
+
noProxyHint: "\u672C\u6B21\u8FD0\u884C\u5FFD\u7565 HTTPS_PROXY / HTTP_PROXY\uFF0C\u76F4\u8FDE",
|
|
2799
2899
|
resumeHint: "\u5F3A\u5236\u6062\u590D\u6307\u5B9A\u4F1A\u8BDD\uFF08\u5373\u4F7F\u7A7A\u95F2\uFF09",
|
|
2800
2900
|
newHint: "\u5F3A\u5236\u521B\u5EFA\u65B0\u4F1A\u8BDD\uFF08\u5FFD\u7565 --session / --continue\uFF09",
|
|
2801
2901
|
transcriptHint: "JSONL \u8F6C\u5F55\u7A3F\u7684\u5199\u5165\u8DEF\u5F84",
|
|
@@ -2940,6 +3040,7 @@ var zhCN = {
|
|
|
2940
3040
|
},
|
|
2941
3041
|
stop: { description: "\u4E2D\u6B62\u5F53\u524D\u6A21\u578B\u56DE\u5408\uFF08\u6309 Esc \u7684\u66FF\u4EE3\u65B9\u5F0F\uFF09" },
|
|
2942
3042
|
feedback: { description: "\u6253\u5F00 GitHub Issue\uFF0C\u8BCA\u65AD\u4FE1\u606F\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F" },
|
|
3043
|
+
about: { description: "\u9879\u76EE\u4FE1\u606F \u2014 \u7248\u672C\u3001\u5B98\u7F51\u3001\u4ED3\u5E93\u3001\u534F\u8BAE" },
|
|
2943
3044
|
plans: { description: "\u5217\u51FA\u6B64\u4F1A\u8BDD\u7684\u6D3B\u8DC3 + \u5F52\u6863\u8BA1\u5212\uFF08\u6700\u65B0\u5728\u524D\uFF09" },
|
|
2944
3045
|
replay: {
|
|
2945
3046
|
description: "\u52A0\u8F7D\u5F52\u6863\u8BA1\u5212\u4E3A\u53EA\u8BFB\u7684\u65F6\u95F4\u65C5\u884C\u5FEB\u7167\uFF08\u9ED8\u8BA4\uFF1A\u6700\u65B0\uFF09",
|
|
@@ -3016,8 +3117,8 @@ var zhCN = {
|
|
|
3016
3117
|
argsHint: "<question>"
|
|
3017
3118
|
},
|
|
3018
3119
|
"search-engine": {
|
|
3019
|
-
description: "\u5207\u6362\u7F51\u7EDC\u641C\u7D22\u540E\u7AEF \u2014 mojeek\uFF08\u9ED8\u8BA4\uFF0C\u65E0\u4F9D\u8D56\uFF09\u3001searxng\uFF08\u81EA\u6258\u7BA1\uFF09\
|
|
3020
|
-
argsHint: "<mojeek|searxng|metaso> [<
|
|
3120
|
+
description: "\u5207\u6362\u7F51\u7EDC\u641C\u7D22\u540E\u7AEF \u2014 mojeek\uFF08\u9ED8\u8BA4\uFF0C\u65E0\u4F9D\u8D56\uFF09\u3001searxng\uFF08\u81EA\u6258\u7BA1\uFF09\u3001metaso\uFF08\u6BCF\u65E5 100 \u6B21\uFF09\u3001tavily\uFF08\u6BCF\u6708 1000 \u6B21\u514D\u8D39\uFF09\u3001perplexity\uFF08AI \u76F4\u63A5\u56DE\u7B54\uFF09\u6216 exa\uFF08AI \u76F4\u63A5\u56DE\u7B54\uFF09",
|
|
3121
|
+
argsHint: "<mojeek|searxng|metaso|tavily|perplexity|exa> [<key>]"
|
|
3021
3122
|
}
|
|
3022
3123
|
},
|
|
3023
3124
|
wizard: {
|
|
@@ -3208,7 +3309,22 @@ var zhCN = {
|
|
|
3208
3309
|
continuingAfter: "\u25B8 \u5728 {label}{counter} \u4E4B\u540E\u7EE7\u7EED",
|
|
3209
3310
|
planStoppedAt: "\u25B8 \u8BA1\u5212\u5728 {label}{counter} \u5904\u505C\u6B62",
|
|
3210
3311
|
revisingAfter: "\u25B8 \u5728 {label} \u4E4B\u540E\u4FEE\u8BA2 \u2014 {feedback}",
|
|
3211
|
-
historyScrollHint: " \u2191 \u6B63\u5728\u67E5\u770B\u5386\u53F2 \xB7 End / PgDn \u8FD4\u56DE\u5E95\u90E8 \xB7 \u2193 \u5411\u4E0B\u6EDA\u52A8\u4E00\u884C"
|
|
3312
|
+
historyScrollHint: " \u2191 \u6B63\u5728\u67E5\u770B\u5386\u53F2 \xB7 End / PgDn \u8FD4\u56DE\u5E95\u90E8 \xB7 \u2193 \u5411\u4E0B\u6EDA\u52A8\u4E00\u884C",
|
|
3313
|
+
editHistoryTitle: "\u7F16\u8F91\u5386\u53F2\uFF08\u4ECE\u65E7\u5230\u65B0\uFF09\uFF1A",
|
|
3314
|
+
editHistoryNoCodeMode: "\u4E0D\u5728\u4EE3\u7801\u6A21\u5F0F\u4E2D",
|
|
3315
|
+
editHistoryNoEdits: "\u6B64\u4F1A\u8BDD\u5C1A\u672A\u8BB0\u5F55\u4EFB\u4F55\u7F16\u8F91",
|
|
3316
|
+
editHistoryNoShowId: "\u7528\u6CD5\uFF1A/show [id] [path] \uFF08\u7701\u7565 id \u67E5\u770B\u6700\u65B0\uFF1Bpath \u6765\u81EA\u6587\u4EF6\u6458\u8981\uFF09",
|
|
3317
|
+
editHistoryIdNotFound: "\u672A\u627E\u5230\u7F16\u8F91 #{id} \u2014 \u8FD0\u884C /history \u67E5\u770B\u6709\u6548 ID",
|
|
3318
|
+
editHistoryLookupFailed: "\u610F\u5916\u9519\u8BEF\uFF1A\u5386\u53F2\u67E5\u627E\u5931\u8D25",
|
|
3319
|
+
editHistoryBatchNoFile: '\u6279\u6B21 #{id} \u4E0D\u5305\u542B "{path}" \u2014 \u6B64\u6279\u6B21\u4E2D\u7684\u6587\u4EF6\uFF1A{files}',
|
|
3320
|
+
editHistoryNoEdits2: "\u6B64\u4F1A\u8BDD\u5C1A\u672A\u8BB0\u5F55\u7F16\u8F91 \u2014 /history \u4E3A\u7A7A",
|
|
3321
|
+
editHistoryStatusApplied: "\u5DF2\u5E94\u7528",
|
|
3322
|
+
editHistoryStatusPartial: "\u90E8\u5206\u5E94\u7528",
|
|
3323
|
+
editHistoryStatusUndone: "\u5DF2\u64A4\u9500",
|
|
3324
|
+
editHistoryHelpShow: "/show <id> \u2192 \u6587\u4EF6\u6458\u8981 \xB7 /show <id> <path> \u2192 \u67D0\u4E2A\u6587\u4EF6\u7684\u5B8C\u6574 diff",
|
|
3325
|
+
editHistoryHelpUndo: "/undo \u2192 \u6700\u65B0\u7684\u672A\u64A4\u9500\u9879 \xB7 /undo <id> [path] \u2192 \u6307\u5B9A\u6279\u6B21\u6216\u6587\u4EF6",
|
|
3326
|
+
editHistoryAlreadyReverted: "\uFF08\u5DF2\u64A4\u9500 \u2014 /history \u663E\u793A\u6279\u6B21\u7EA7\u72B6\u6001\uFF09",
|
|
3327
|
+
editHistoryRevertFile: "/undo {id} {path} \u2192 \u4EC5\u8FD8\u539F\u6B64\u6587\u4EF6"
|
|
3212
3328
|
},
|
|
3213
3329
|
hooks: {
|
|
3214
3330
|
head: "\u94A9\u5B50 {tag} `{cmd}` {decision}{truncTag}",
|
|
@@ -3309,6 +3425,10 @@ var zhCN = {
|
|
|
3309
3425
|
loopNoActiveHint: "\u6CA1\u6709\u6D3B\u52A8\u7684\u5FAA\u73AF\u3002\u4F7F\u7528 `/loop <interval> <prompt>` \u542F\u52A8\u4E00\u4E2A\uFF08\u4F8B\u5982 /loop 30s npm test\uFF09\u3002\n\u53D6\u6D88\u65B9\u5F0F\uFF1A/loop stop \xB7 Esc \xB7 /clear /new \xB7 \u4EFB\u4F55\u7528\u6237\u8F93\u5165\u7684\u63D0\u793A\u3002",
|
|
3310
3426
|
loopStarted: '\u25B8 \u5FAA\u73AF\u5DF2\u542F\u52A8 \u2014 \u6BCF {duration} \u91CD\u65B0\u63D0\u4EA4 "{prompt}"\u3002\u8F93\u5165\u4EFB\u4F55\u5185\u5BB9\uFF08\u6216 /loop stop\uFF09\u53D6\u6D88\u3002',
|
|
3311
3427
|
keysNeedsTui: "/keys \u9700\u8981 TUI \u4E0A\u4E0B\u6587\uFF08postKeys \u5DF2\u8FDE\u63A5\uFF09\u3002",
|
|
3428
|
+
aboutHeader: "Reasonix v{version} \u2014 \u7F13\u5B58\u4F18\u5148\u7684 DeepSeek \u7F16\u7801\u4EE3\u7406",
|
|
3429
|
+
aboutWebsiteLabel: "\u5B98\u7F51",
|
|
3430
|
+
aboutRepoLabel: "\u4ED3\u5E93",
|
|
3431
|
+
aboutLicenseLabel: "\u534F\u8BAE",
|
|
3312
3432
|
unknownCommand: "\u672A\u77E5\u547D\u4EE4\uFF1A/{cmd} \u2014 \u4F60\u662F\u4E0D\u662F\u60F3\u7528 {list}\uFF1F",
|
|
3313
3433
|
unknownCommandShort: "\u672A\u77E5\u547D\u4EE4\uFF1A/{cmd} \uFF08\u8BD5\u8BD5 /help\uFF09"
|
|
3314
3434
|
},
|
|
@@ -3626,6 +3746,8 @@ var zhCN = {
|
|
|
3626
3746
|
usageSearxngUrl: " /search-engine searxng <url> \u4F7F\u7528 SearXNG \u81EA\u5B9A\u4E49\u7AEF\u70B9",
|
|
3627
3747
|
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",
|
|
3628
3748
|
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",
|
|
3749
|
+
usagePerplexity: " /search-engine perplexity \u4F7F\u7528 Perplexity AI\uFF08AI \u76F4\u63A5\u56DE\u7B54 + \u5F15\u7528 \u2014 \u8BBE\u7F6E PERPLEXITY_API_KEY \u6216 config \u7684 perplexityApiKey\uFF1B\u5728 https://perplexity.ai/settings/api \u83B7\u53D6\u5BC6\u94A5\uFF09",
|
|
3750
|
+
usageExa: " /search-engine exa \u4F7F\u7528 Exa API\uFF08AI \u76F4\u63A5\u56DE\u7B54 + \u5F15\u7528\uFF0C\u6BCF\u6708 1000 \u6B21\u514D\u8D39 \u2014 \u8BBE\u7F6E EXA_API_KEY \u6216 config \u7684 exaApiKey\uFF1B\u6CE8\u518C https://exa.ai\uFF09",
|
|
3629
3751
|
alias: "\u522B\u540D\uFF1A/se",
|
|
3630
3752
|
searxngInfo: "SearXNG \u662F\u4E00\u4E2A\u81EA\u6258\u7BA1\u7684\u5143\u641C\u7D22\u5F15\u64CE\uFF08https://github.com/searxng/searxng\uFF09\u3002",
|
|
3631
3753
|
searxngInstall: "\u5B89\u88C5\u547D\u4EE4\uFF1A docker run -d -p 8080:8080 searxng/searxng",
|
|
@@ -3633,7 +3755,11 @@ var zhCN = {
|
|
|
3633
3755
|
switchedSearxngNote: " \u8BF7\u786E\u4FDD SearXNG \u5728 {endpoint} \u8FD0\u884C\u3002",
|
|
3634
3756
|
switchedMetasoNote: " \u6BCF\u65E5\u9650\u989D 100 \u6B21\uFF08\u914D\u7F6E\u4F60\u81EA\u5DF1\u7684 API \u5BC6\u94A5\u53EF\u63D0\u5347\u9650\u989D\uFF09\u3002",
|
|
3635
3757
|
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",
|
|
3636
|
-
|
|
3758
|
+
switchedPerplexityNote: " \u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF PERPLEXITY_API_KEY \u6216 config \u4E2D\u7684 `perplexityApiKey`\uFF1B\u5728 https://perplexity.ai/settings/api \u83B7\u53D6\u5BC6\u94A5\u3002",
|
|
3759
|
+
switchedExaNote: " \u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF EXA_API_KEY \u6216 config \u4E2D\u7684 `exaApiKey`\uFF1B\u6CE8\u518C https://exa.ai\u3002",
|
|
3760
|
+
keyNeeded: '\u672A\u914D\u7F6E "{engine}" \u7684 API \u5BC6\u94A5\u3002\n\n 1. \u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF {envVar}\n 2. \u6216\u5185\u8054\u63D0\u4F9B\uFF1A/search-engine {engine} <your-key>\n 3. \u6216\u5728 ~/.reasonix/config.json \u4E2D\u6DFB\u52A0 "{engine}ApiKey"\n\n\u5B8C\u6210\u540E\u91CD\u65B0\u6267\u884C /search-engine {engine}\u3002',
|
|
3761
|
+
keySaved: " API \u5BC6\u94A5\u5DF2\u4FDD\u5B58\u5230\u914D\u7F6E\u3002",
|
|
3762
|
+
confirmed: '\u7F51\u9875\u641C\u7D22\u5F15\u64CE\u5DF2\u8BBE\u4E3A "{engine}"{detail}\u3002\u4E0B\u4E00\u8F6E\u6A21\u578B\u8C03\u7528\u5C06\u751F\u6548\u3002',
|
|
3637
3763
|
confirmedDetail: "\uFF08{endpoint}\uFF09"
|
|
3638
3764
|
},
|
|
3639
3765
|
skill: {
|
|
@@ -3892,27 +4018,37 @@ var zhCN = {
|
|
|
3892
4018
|
probeFailed: "\u63A2\u6D4B\u5931\u8D25 \u2014 {message}"
|
|
3893
4019
|
},
|
|
3894
4020
|
webErrors: {
|
|
3895
|
-
status: "web_search {status} \u2014 try: \u641C\u7D22\u540E\u7AEF\u8FD4\u56DE\u9519\u8BEF\uFF1B\u8BF7\u6539\u5199\u67E5\u8BE2\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek|searxng \u5207\u6362\u5F15\u64CE",
|
|
4021
|
+
status: "web_search {status} \u2014 try: \u641C\u7D22\u540E\u7AEF\u8FD4\u56DE\u9519\u8BEF\uFF1B\u8BF7\u6539\u5199\u67E5\u8BE2\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
3896
4022
|
rateLimit429: "web_search 429 \u2014 try: \u7B49\u5F85 10 \u79D2\u540E\u91CD\u8BD5\uFF0C\u6216\u6539\u5199\u67E5\u8BE2\uFF1B\u641C\u7D22\u540E\u7AEF\u6B63\u5728\u5BF9\u8BE5\u5BA2\u6237\u7AEF\u8FDB\u884C\u9650\u6D41",
|
|
3897
|
-
forbidden403: "web_search 403 \u2014 try: \u641C\u7D22\u540E\u7AEF\u62D2\u7EDD\u8BE5\u5BA2\u6237\u7AEF\u8BBF\u95EE\uFF1B\u4F7F\u7528 /search-engine mojeek|searxng \u5207\u6362\u5F15\u64CE\uFF0C\u6216\u7A0D\u540E\u91CD\u8BD5",
|
|
4023
|
+
forbidden403: "web_search 403 \u2014 try: \u641C\u7D22\u540E\u7AEF\u62D2\u7EDD\u8BE5\u5BA2\u6237\u7AEF\u8BBF\u95EE\uFF1B\u4F7F\u7528 /search-engine mojeek|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE\uFF0C\u6216\u7A0D\u540E\u91CD\u8BD5",
|
|
3898
4024
|
serverError5xx: "web_search {status} \u2014 try: \u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\u641C\u7D22 URL\uFF1B\u82E5\u80FD\u52A0\u8F7D\u5219\u5C5E\u4E34\u65F6\u6545\u969C\uFF0C\u7B49 30 \u79D2\u91CD\u8BD5\u5373\u53EF",
|
|
3899
|
-
mojeekBlocked: "web_search: Mojeek \u53CD\u722C\u9875\u9762 \u2014 \u9891\u7387\u9650\u5236\u6216\u88AB\u5C4F\u853D \u2014 try: \u7B49\u5F85 30 \u79D2\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine searxng \u5207\u6362\u5F15\u64CE",
|
|
3900
|
-
mojeekNoResults: "web_search: \u8FD4\u56DE 0 \u6761\u7ED3\u679C\u4F46\u54CD\u5E94\u770B\u8D77\u6765\u4E0D\u662F\u6B63\u5E38\u7A7A\u7ED3\u679C\u9875\uFF08{chars} \u5B57\u7B26\uFF0C\u524D 120 \u5B57\u7B26\uFF1A{preview}\uFF09\u2014 try: \u4F7F\u7528\u66F4\u7B80\u5355\u7684\u5173\u952E\u8BCD\u6539\u5199\u67E5\u8BE2\uFF0C\u6216\u4F7F\u7528 /search-engine searxng \u5207\u6362\u5F15\u64CE",
|
|
4025
|
+
mojeekBlocked: "web_search: Mojeek \u53CD\u722C\u9875\u9762 \u2014 \u9891\u7387\u9650\u5236\u6216\u88AB\u5C4F\u853D \u2014 try: \u7B49\u5F85 30 \u79D2\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
4026
|
+
mojeekNoResults: "web_search: \u8FD4\u56DE 0 \u6761\u7ED3\u679C\u4F46\u54CD\u5E94\u770B\u8D77\u6765\u4E0D\u662F\u6B63\u5E38\u7A7A\u7ED3\u679C\u9875\uFF08{chars} \u5B57\u7B26\uFF0C\u524D 120 \u5B57\u7B26\uFF1A{preview}\uFF09\u2014 try: \u4F7F\u7528\u66F4\u7B80\u5355\u7684\u5173\u952E\u8BCD\u6539\u5199\u67E5\u8BE2\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
3901
4027
|
invalidEndpoint: 'web_search: \u65E0\u6548\u7684 SearXNG \u7AEF\u70B9 "{endpoint}" \u2014 try: \u4F7F\u7528 /search-endpoint http://host:port \u8BBE\u7F6E\u6709\u6548\u7684 URL',
|
|
3902
4028
|
endpointMustBeHttp: "web_search: SearXNG \u7AEF\u70B9\u5FC5\u987B\u662F http(s) \u534F\u8BAE\uFF0C\u5F53\u524D\u4E3A {protocol} \u2014 try: \u4F7F\u7528 /search-endpoint http://host:port \u8BBE\u7F6E\u6709\u6548\u7684 URL",
|
|
3903
|
-
cannotReach: "web_search: \u65E0\u6CD5\u8BBF\u95EE SearXNG \u670D\u52A1\u5668 {endpoint} \u2014 try: \u5B89\u88C5\u5E76\u542F\u52A8 SearXNG\uFF08https://github.com/searxng/searxng\uFF0C\u4F8B\u5982 `docker run -d -p 8080:8080 searxng/searxng`\uFF09\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek \u5207\u6362\
|
|
3904
|
-
searxngNoResults: "web_search: \u8FD4\u56DE 0 \u6761\u7ED3\u679C\u4F46 SearXNG \u54CD\u5E94\u770B\u8D77\u6765\u4E0D\u662F\u6B63\u5E38\u7A7A\u7ED3\u679C\u9875\uFF08{chars} \u5B57\u7B26\uFF09\u2014 try: \u4F7F\u7528\u66F4\u7B80\u5355\u7684\u5173\u952E\u8BCD\u6539\u5199\u67E5\u8BE2\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek \u5207\u6362\u5F15\u64CE",
|
|
4029
|
+
cannotReach: "web_search: \u65E0\u6CD5\u8BBF\u95EE SearXNG \u670D\u52A1\u5668 {endpoint} \u2014 try: \u5B89\u88C5\u5E76\u542F\u52A8 SearXNG\uFF08https://github.com/searxng/searxng\uFF0C\u4F8B\u5982 `docker run -d -p 8080:8080 searxng/searxng`\uFF09\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
4030
|
+
searxngNoResults: "web_search: \u8FD4\u56DE 0 \u6761\u7ED3\u679C\u4F46 SearXNG \u54CD\u5E94\u770B\u8D77\u6765\u4E0D\u662F\u6B63\u5E38\u7A7A\u7ED3\u679C\u9875\uFF08{chars} \u5B57\u7B26\uFF09\u2014 try: \u4F7F\u7528\u66F4\u7B80\u5355\u7684\u5173\u952E\u8BCD\u6539\u5199\u67E5\u8BE2\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
3905
4031
|
metasoDailyLimit: "web_search: \u9ED8\u8BA4 API \u5BC6\u94A5\u7684\u6BCF\u65E5\u641C\u7D22\u6B21\u6570\u5DF2\u8FBE\u4E0A\u9650 \u2014 \u8BBE\u7F6E METASO_API_KEY \u73AF\u5883\u53D8\u91CF\uFF0C\u6216\u5728 https://metaso.cn/search-api/playground \u83B7\u53D6\u81EA\u5DF1\u7684\u5BC6\u94A5",
|
|
3906
4032
|
metasoUnauthorized: "web_search: Metaso API \u5BC6\u94A5\u88AB\u62D2\u7EDD \u2014 \u68C0\u67E5 METASO_API_KEY\uFF0C\u6216\u5728 https://metaso.cn/search-api/playground \u83B7\u53D6\u5BC6\u94A5",
|
|
3907
4033
|
metasoRateLimit: "web_search: Metaso \u8BF7\u6C42\u9891\u7387\u9650\u5236 \u2014 \u7B49\u5F85\u540E\u91CD\u8BD5\uFF0C\u6216\u5728 https://metaso.cn/search-api/playground \u83B7\u53D6\u81EA\u5DF1\u7684\u5BC6\u94A5",
|
|
3908
|
-
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",
|
|
4034
|
+
metasoServerError: "web_search: Metaso \u670D\u52A1\u5668\u9519\u8BEF\uFF08{status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
3909
4035
|
metasoParseError: "web_search: Metaso \u8FD4\u56DE\u65E0\u6CD5\u89E3\u6790\u7684\u54CD\u5E94\uFF08HTTP {status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5",
|
|
3910
4036
|
metasoApiError: "web_search: Metaso API \u9519\u8BEF\uFF08code {code}: {message}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5",
|
|
3911
4037
|
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",
|
|
3912
4038
|
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",
|
|
3913
|
-
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",
|
|
3914
|
-
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",
|
|
4039
|
+
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|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE\uFF0C\u6216\u5347\u7EA7 Tavily \u8BA1\u5212",
|
|
4040
|
+
tavilyServerError: "web_search: Tavily \u670D\u52A1\u5668\u9519\u8BEF\uFF08{status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
3915
4041
|
tavilyParseError: "web_search: Tavily \u8FD4\u56DE\u65E0\u6CD5\u89E3\u6790\u7684\u54CD\u5E94\uFF08HTTP {status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5",
|
|
4042
|
+
perplexityMissingKey: "web_search: Perplexity \u540E\u7AEF\u9700\u8981 API \u5BC6\u94A5 \u2014 \u8BBE\u7F6E PERPLEXITY_API_KEY \u73AF\u5883\u53D8\u91CF\uFF0C\u6216\u5728 ~/.reasonix/config.json \u4E2D\u914D\u7F6E `perplexityApiKey`\uFF1B\u5728 https://perplexity.ai/settings/api \u83B7\u53D6\u5BC6\u94A5",
|
|
4043
|
+
perplexityUnauthorized: "web_search: Perplexity API \u5BC6\u94A5\u88AB\u62D2\u7EDD \u2014 \u68C0\u67E5 PERPLEXITY_API_KEY\uFF0C\u6216\u5728 https://perplexity.ai/settings/api \u83B7\u53D6\u5BC6\u94A5",
|
|
4044
|
+
perplexityRateLimit: "web_search: Perplexity \u8BF7\u6C42\u9891\u7387\u9650\u5236 \u2014 \u7B49\u5F85\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
4045
|
+
perplexityServerError: "web_search: Perplexity \u670D\u52A1\u5668\u9519\u8BEF\uFF08{status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
4046
|
+
perplexityParseError: "web_search: Perplexity \u8FD4\u56DE\u65E0\u6CD5\u89E3\u6790\u7684\u54CD\u5E94\uFF08HTTP {status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5",
|
|
4047
|
+
exaMissingKey: "web_search: Exa \u540E\u7AEF\u9700\u8981 API \u5BC6\u94A5 \u2014 \u8BBE\u7F6E EXA_API_KEY \u73AF\u5883\u53D8\u91CF\uFF0C\u6216\u5728 ~/.reasonix/config.json \u4E2D\u914D\u7F6E `exaApiKey`\uFF1Bhttps://exa.ai \u6BCF\u6708 1000 \u6B21\u514D\u8D39",
|
|
4048
|
+
exaUnauthorized: "web_search: Exa API \u5BC6\u94A5\u88AB\u62D2\u7EDD \u2014 \u68C0\u67E5 EXA_API_KEY\uFF0C\u6216\u5728 https://exa.ai \u83B7\u53D6\u5BC6\u94A5",
|
|
4049
|
+
exaRateLimit: "web_search: Exa \u8BF7\u6C42\u9891\u7387\u9650\u5236\u6216\u6708\u5EA6\u914D\u989D\u7528\u5C3D \u2014 \u7B49\u5F85\u5347\u7EA7\uFF0C\u6216\u5728 https://exa.ai/pricing \u67E5\u770B\u8BA1\u5212",
|
|
4050
|
+
exaServerError: "web_search: Exa \u670D\u52A1\u5668\u9519\u8BEF\uFF08{status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
4051
|
+
exaParseError: "web_search: Exa \u8FD4\u56DE\u65E0\u6CD5\u89E3\u6790\u7684\u54CD\u5E94\uFF08HTTP {status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5",
|
|
3916
4052
|
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",
|
|
3917
4053
|
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",
|
|
3918
4054
|
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",
|
|
@@ -4080,6 +4216,12 @@ var zhCN = {
|
|
|
4080
4216
|
serverCount: "{count} \u4E2A\u670D\u52A1\u5668",
|
|
4081
4217
|
footer: "\u2191\u2193 \u9009\u62E9 \xB7 [r] \u91CD\u8FDE \xB7 [d] \u7981\u7528 \xB7 Esc \u9000\u51FA"
|
|
4082
4218
|
},
|
|
4219
|
+
mcpBrowse: {
|
|
4220
|
+
noResources: "\u6CA1\u6709\u4EFB\u4F55\u5DF2\u8FDE\u63A5 MCP \u670D\u52A1\u5668\u4E0A\u7684\u8D44\u6E90\uFF08\u6216\u65E0\u670D\u52A1\u5668\u8FDE\u63A5\uFF09\u3002`/mcp` \u663E\u793A\u5F53\u524D\u5217\u8868\u3002",
|
|
4221
|
+
readOne: "\u8BFB\u53D6\uFF1A`/resource <uri>` \u2014 \u6216\u5728\u9009\u62E9\u5668\u4E2D\u4F7F\u7528 Tab \u952E\u3002",
|
|
4222
|
+
noPrompts: "\u6CA1\u6709\u4EFB\u4F55\u5DF2\u8FDE\u63A5 MCP \u670D\u52A1\u5668\u4E0A\u7684\u63D0\u793A\uFF08\u6216\u65E0\u670D\u52A1\u5668\u8FDE\u63A5\uFF09\u3002`/mcp` \u663E\u793A\u5F53\u524D\u5217\u8868\u3002",
|
|
4223
|
+
fetchOne: "\u83B7\u53D6\uFF1A`/prompt <name>` \u2014 \u6682\u4E0D\u652F\u6301\u53C2\u6570\uFF1B\u5E26\u5FC5\u9700\u53C2\u6570\u7684\u63D0\u793A\u5C06\u8FD4\u56DE\u670D\u52A1\u5668\u9519\u8BEF\u3002"
|
|
4224
|
+
},
|
|
4083
4225
|
mcpLifecycle: {
|
|
4084
4226
|
handshake: "\u63E1\u624B\u4E2D\u2026",
|
|
4085
4227
|
connected: "\u5DF2\u8FDE\u63A5",
|
|
@@ -4826,8 +4968,8 @@ var ToolRegistry = class {
|
|
|
4826
4968
|
_resultAugmenter = null;
|
|
4827
4969
|
/** Per-tool fingerprint of the last call that failed schema validation. Cleared by any successful validation for that tool. */
|
|
4828
4970
|
_lastMalformed = /* @__PURE__ */ new Map();
|
|
4829
|
-
/** Per-tool fingerprint of the last host-side
|
|
4830
|
-
|
|
4971
|
+
/** Per-tool fingerprint of the last host-side gate rejection. */
|
|
4972
|
+
_lastGateRejection = /* @__PURE__ */ new Map();
|
|
4831
4973
|
constructor(opts = {}) {
|
|
4832
4974
|
this._autoFlatten = opts.autoFlatten !== false;
|
|
4833
4975
|
}
|
|
@@ -4914,20 +5056,21 @@ var ToolRegistry = class {
|
|
|
4914
5056
|
if (!tool) {
|
|
4915
5057
|
return JSON.stringify({ error: `unknown tool: ${name}` });
|
|
4916
5058
|
}
|
|
4917
|
-
const
|
|
5059
|
+
const rawFingerprint = rawFingerprintArgs(argumentsRaw);
|
|
4918
5060
|
let args;
|
|
4919
5061
|
try {
|
|
4920
5062
|
args = typeof argumentsRaw === "string" ? argumentsRaw.trim() ? JSON.parse(argumentsRaw) ?? {} : {} : argumentsRaw ?? {};
|
|
4921
5063
|
} catch (err) {
|
|
4922
5064
|
return this._noteMalformed(
|
|
4923
5065
|
name,
|
|
4924
|
-
|
|
5066
|
+
rawFingerprint,
|
|
4925
5067
|
`invalid tool arguments JSON: ${err.message}`
|
|
4926
5068
|
);
|
|
4927
5069
|
}
|
|
4928
5070
|
if (tool.flatSchema && args && typeof args === "object" && hasDotKey(args)) {
|
|
4929
5071
|
args = nestArguments(args);
|
|
4930
5072
|
}
|
|
5073
|
+
const fingerprint = fingerprintArgs(args);
|
|
4931
5074
|
const missing = tool.parameters ? missingRequiredParam(tool.parameters, args) : null;
|
|
4932
5075
|
if (missing) {
|
|
4933
5076
|
return this._noteMalformed(
|
|
@@ -4948,7 +5091,7 @@ var ToolRegistry = class {
|
|
|
4948
5091
|
try {
|
|
4949
5092
|
const short = await interceptor(name, args);
|
|
4950
5093
|
if (typeof short === "string") {
|
|
4951
|
-
const guarded = this.
|
|
5094
|
+
const guarded = this._noteGateRejection(name, fingerprint, short);
|
|
4952
5095
|
return this._augmentResult(name, args, guarded);
|
|
4953
5096
|
}
|
|
4954
5097
|
} catch (err) {
|
|
@@ -4957,7 +5100,6 @@ var ToolRegistry = class {
|
|
|
4957
5100
|
});
|
|
4958
5101
|
}
|
|
4959
5102
|
}
|
|
4960
|
-
this._lastInterceptorRejection.delete(name);
|
|
4961
5103
|
if (opts.signal?.aborted) {
|
|
4962
5104
|
return JSON.stringify({
|
|
4963
5105
|
error: `${name}: aborted before dispatch (user interrupt)`,
|
|
@@ -4995,6 +5137,7 @@ var ToolRegistry = class {
|
|
|
4995
5137
|
finalResult = JSON.stringify({ error: `${e.name}: ${e.message}` });
|
|
4996
5138
|
}
|
|
4997
5139
|
}
|
|
5140
|
+
finalResult = this._noteGateRejection(name, fingerprint, finalResult);
|
|
4998
5141
|
return this._augmentResult(name, args, finalResult);
|
|
4999
5142
|
}
|
|
5000
5143
|
_augmentResult(name, args, result) {
|
|
@@ -5018,18 +5161,18 @@ var ToolRegistry = class {
|
|
|
5018
5161
|
}
|
|
5019
5162
|
return JSON.stringify({ error: `${name}: ${detail}` });
|
|
5020
5163
|
}
|
|
5021
|
-
|
|
5022
|
-
const reason = rejectedReason(result);
|
|
5164
|
+
_noteGateRejection(name, fingerprint, result) {
|
|
5165
|
+
const reason = rejectedReason(name, result);
|
|
5023
5166
|
if (!reason) {
|
|
5024
|
-
this.
|
|
5167
|
+
this._lastGateRejection.delete(name);
|
|
5025
5168
|
return result;
|
|
5026
5169
|
}
|
|
5027
5170
|
const key = `${reason}:${fingerprint}`;
|
|
5028
|
-
const prev = this.
|
|
5029
|
-
this.
|
|
5171
|
+
const prev = this._lastGateRejection.get(name);
|
|
5172
|
+
this._lastGateRejection.set(name, key);
|
|
5030
5173
|
if (prev === key) {
|
|
5031
5174
|
return JSON.stringify({
|
|
5032
|
-
error: `${name}: same call was just rejected by ${reason} \u2014 do not retry identical args.
|
|
5175
|
+
error: `${name}: same call was just rejected by ${reason} \u2014 do not retry identical args. ${rejectionRecoveryHint(reason)}`,
|
|
5033
5176
|
rejectedReason: reason,
|
|
5034
5177
|
consecutiveInterceptorRejection: true
|
|
5035
5178
|
});
|
|
@@ -5037,16 +5180,44 @@ var ToolRegistry = class {
|
|
|
5037
5180
|
return result;
|
|
5038
5181
|
}
|
|
5039
5182
|
};
|
|
5040
|
-
function rejectedReason(result) {
|
|
5183
|
+
function rejectedReason(name, result) {
|
|
5184
|
+
const textReason = plainTextRejectedReason(name, result);
|
|
5185
|
+
if (textReason) return textReason;
|
|
5041
5186
|
try {
|
|
5042
5187
|
const parsed = JSON.parse(result);
|
|
5043
5188
|
if (!parsed || typeof parsed !== "object") return null;
|
|
5044
5189
|
const reason = parsed.rejectedReason;
|
|
5045
|
-
|
|
5190
|
+
if (typeof reason === "string" && reason) return reason;
|
|
5191
|
+
const error = parsed.error;
|
|
5192
|
+
if (typeof error === "string") return plainTextRejectedReason(name, error);
|
|
5193
|
+
return null;
|
|
5046
5194
|
} catch {
|
|
5047
5195
|
return null;
|
|
5048
5196
|
}
|
|
5049
5197
|
}
|
|
5198
|
+
function plainTextRejectedReason(name, result) {
|
|
5199
|
+
if ((name === "edit_file" || name === "write_file") && /rejected this edit/i.test(result)) {
|
|
5200
|
+
return "edit-gate";
|
|
5201
|
+
}
|
|
5202
|
+
if ((name === "run_command" || name === "run_background") && /\buser denied:/i.test(result)) {
|
|
5203
|
+
return "shell-gate";
|
|
5204
|
+
}
|
|
5205
|
+
return null;
|
|
5206
|
+
}
|
|
5207
|
+
function rejectionRecoveryHint(reason) {
|
|
5208
|
+
switch (reason) {
|
|
5209
|
+
case "edit-gate":
|
|
5210
|
+
return "Do not re-emit the same edit. Try a genuinely different edit or ask the user how to proceed.";
|
|
5211
|
+
case "shell-gate":
|
|
5212
|
+
return "Do not retry the same command. Use an allowlisted/read-only command, wait for approval, or ask the user how to proceed.";
|
|
5213
|
+
case "engineering-lifecycle":
|
|
5214
|
+
return "Switch to read-only exploration, submit or revise the plan, or choose a different tool call.";
|
|
5215
|
+
case "engineering-lifecycle-evidence":
|
|
5216
|
+
return "Submit completion evidence or revise/checkpoint the plan before marking the step complete.";
|
|
5217
|
+
default:
|
|
5218
|
+
return "Choose a different tool call or ask the user how to proceed.";
|
|
5219
|
+
}
|
|
5220
|
+
}
|
|
5050
5221
|
function isReadOnlyCall(tool, args) {
|
|
5051
5222
|
if (tool.readOnlyCheck) {
|
|
5052
5223
|
try {
|
|
@@ -5065,14 +5236,27 @@ function hasDotKey(obj) {
|
|
|
5065
5236
|
}
|
|
5066
5237
|
return false;
|
|
5067
5238
|
}
|
|
5068
|
-
function
|
|
5239
|
+
function rawFingerprintArgs(argumentsRaw) {
|
|
5069
5240
|
if (typeof argumentsRaw === "string") return argumentsRaw;
|
|
5241
|
+
return fingerprintArgs(argumentsRaw);
|
|
5242
|
+
}
|
|
5243
|
+
function fingerprintArgs(args) {
|
|
5070
5244
|
try {
|
|
5071
|
-
return JSON.stringify(
|
|
5245
|
+
return JSON.stringify(sortJson(args));
|
|
5072
5246
|
} catch {
|
|
5073
5247
|
return "";
|
|
5074
5248
|
}
|
|
5075
5249
|
}
|
|
5250
|
+
function sortJson(value) {
|
|
5251
|
+
if (Array.isArray(value)) return value.map(sortJson);
|
|
5252
|
+
if (!value || typeof value !== "object") return value;
|
|
5253
|
+
const out = {};
|
|
5254
|
+
for (const key of Object.keys(value).sort()) {
|
|
5255
|
+
const item = value[key];
|
|
5256
|
+
if (item !== void 0) out[key] = sortJson(item);
|
|
5257
|
+
}
|
|
5258
|
+
return out;
|
|
5259
|
+
}
|
|
5076
5260
|
function missingRequiredParam(schema, args) {
|
|
5077
5261
|
const required = schema.required;
|
|
5078
5262
|
if (!required || required.length === 0) return null;
|
|
@@ -5764,6 +5948,20 @@ var ContextManager = class {
|
|
|
5764
5948
|
this.deps = deps;
|
|
5765
5949
|
}
|
|
5766
5950
|
deps;
|
|
5951
|
+
/** Real-time token count of the current log — used by Desktop to refresh the
|
|
5952
|
+
* context meter after /compact when no API usage event is available. */
|
|
5953
|
+
getLogTokens() {
|
|
5954
|
+
const entries = this.deps.log.toMessages();
|
|
5955
|
+
let total = 0;
|
|
5956
|
+
for (const e of entries) {
|
|
5957
|
+
const content = typeof e.content === "string" ? e.content : "";
|
|
5958
|
+
total += countTokensBounded(content);
|
|
5959
|
+
if (e.role === "assistant" && Array.isArray(e.tool_calls) && e.tool_calls.length > 0) {
|
|
5960
|
+
total += countTokensBounded(JSON.stringify(e.tool_calls));
|
|
5961
|
+
}
|
|
5962
|
+
}
|
|
5963
|
+
return total;
|
|
5964
|
+
}
|
|
5767
5965
|
/** Decision after a turn's response — fold, exit with summary, or carry on. */
|
|
5768
5966
|
decideAfterUsage(usage, model, alreadyFoldedThisTurn) {
|
|
5769
5967
|
const ctxMax = DEEPSEEK_CONTEXT_TOKENS[model] ?? DEFAULT_CONTEXT_TOKENS;
|
|
@@ -6115,13 +6313,11 @@ async function* forceSummaryAfterIterLimit(ctx, opts) {
|
|
|
6115
6313
|
content: "The turn is being force-summarized (context guard or stuck-state). Summarize in plain prose what you learned from the tool results above. Do NOT emit any tool calls, function-call markup, DSML invocations, or SEARCH/REPLACE edit blocks \u2014 they will be silently discarded. Just plain text."
|
|
6116
6314
|
});
|
|
6117
6315
|
const summaryModel = "deepseek-v4-flash";
|
|
6118
|
-
const summaryEffort = "high";
|
|
6119
6316
|
const resp = await ctx.client.chat({
|
|
6120
6317
|
model: summaryModel,
|
|
6121
6318
|
messages,
|
|
6122
6319
|
signal: ctx.signal,
|
|
6123
|
-
thinking:
|
|
6124
|
-
reasoningEffort: summaryEffort
|
|
6320
|
+
thinking: "disabled"
|
|
6125
6321
|
});
|
|
6126
6322
|
const rawContent = resp.content?.trim() ?? "";
|
|
6127
6323
|
const cleaned = stripHallucinatedToolMarkup(rawContent);
|
|
@@ -6860,6 +7056,10 @@ var CacheFirstLoop = class {
|
|
|
6860
7056
|
async compactHistory(opts) {
|
|
6861
7057
|
return this.context.fold(this.model, opts);
|
|
6862
7058
|
}
|
|
7059
|
+
/** Real-time token count of the current log — forwarded to Desktop for meter refresh. */
|
|
7060
|
+
getCurrentLogTokens() {
|
|
7061
|
+
return this.context.getLogTokens();
|
|
7062
|
+
}
|
|
6863
7063
|
appendAndPersist(message) {
|
|
6864
7064
|
this.log.append(message);
|
|
6865
7065
|
if (this.sessionName) {
|
|
@@ -7175,21 +7375,24 @@ ${reason}`
|
|
|
7175
7375
|
const toolSpecs = this.prefix.tools();
|
|
7176
7376
|
for (let iter = 0; ; iter++) {
|
|
7177
7377
|
if (signal.aborted) {
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
|
|
7182
|
-
|
|
7183
|
-
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7378
|
+
try {
|
|
7379
|
+
yield {
|
|
7380
|
+
turn: this._turn,
|
|
7381
|
+
role: "warning",
|
|
7382
|
+
content: t("loop.abortedAtIter", { iter })
|
|
7383
|
+
};
|
|
7384
|
+
const stoppedMsg = "[aborted by user (Esc) \u2014 no summary produced. Ask again or /retry when ready; prior tool output is still in the log.]";
|
|
7385
|
+
this.appendAndPersist(buildSyntheticAssistantMessage(stoppedMsg, this.model));
|
|
7386
|
+
yield {
|
|
7387
|
+
turn: this._turn,
|
|
7388
|
+
role: "assistant_final",
|
|
7389
|
+
content: stoppedMsg,
|
|
7390
|
+
forcedSummary: true
|
|
7391
|
+
};
|
|
7392
|
+
yield { turn: this._turn, role: "done", content: stoppedMsg };
|
|
7393
|
+
} finally {
|
|
7394
|
+
this._turnAbort = new AbortController();
|
|
7395
|
+
}
|
|
7193
7396
|
return;
|
|
7194
7397
|
}
|
|
7195
7398
|
if (iter > 0) {
|
|
@@ -7365,8 +7568,11 @@ ${reason}`
|
|
|
7365
7568
|
}
|
|
7366
7569
|
} catch (err) {
|
|
7367
7570
|
if (signal.aborted) {
|
|
7368
|
-
|
|
7369
|
-
|
|
7571
|
+
try {
|
|
7572
|
+
yield { turn: this._turn, role: "done", content: "" };
|
|
7573
|
+
} finally {
|
|
7574
|
+
this._turnAbort = new AbortController();
|
|
7575
|
+
}
|
|
7370
7576
|
return;
|
|
7371
7577
|
}
|
|
7372
7578
|
const probe = is5xxError(err) ? await probeDeepSeekReachable(this.client) : void 0;
|
|
@@ -9841,7 +10047,9 @@ var DEFAULT_OUTLINE_THRESHOLD_BYTES = 64 * 1024;
|
|
|
9841
10047
|
var DEFAULT_MAX_LIST_BYTES = 256 * 1024;
|
|
9842
10048
|
var HARD_MAX_FILE_BYTES = 32 * 1024 * 1024;
|
|
9843
10049
|
var OUTLINE_HEAD_LINES = 80;
|
|
9844
|
-
var SKIP_DIR_NAMES = new Set(
|
|
10050
|
+
var SKIP_DIR_NAMES = new Set(
|
|
10051
|
+
DEFAULT_INDEX_EXCLUDES.dirs.filter((d) => d !== ".reasonix")
|
|
10052
|
+
);
|
|
9845
10053
|
var BINARY_EXTENSIONS = new Set(DEFAULT_INDEX_EXCLUDES.exts);
|
|
9846
10054
|
function displayRel4(rootDir, full) {
|
|
9847
10055
|
return pathMod5.relative(rootDir, full).replaceAll("\\", "/");
|
|
@@ -10868,6 +11076,21 @@ function sanitizeEvidence(raw) {
|
|
|
10868
11076
|
}
|
|
10869
11077
|
return out.length > 0 ? out : void 0;
|
|
10870
11078
|
}
|
|
11079
|
+
function summarizeEvidence(evidence) {
|
|
11080
|
+
if (!evidence || evidence.length === 0) return void 0;
|
|
11081
|
+
const parts = evidence.map((item) => `${item.kind}: ${item.summary}`);
|
|
11082
|
+
return parts.join("; ");
|
|
11083
|
+
}
|
|
11084
|
+
function compactStepCompletion(update) {
|
|
11085
|
+
const compact = {
|
|
11086
|
+
kind: "step_completed",
|
|
11087
|
+
stepId: update.stepId,
|
|
11088
|
+
result: update.result
|
|
11089
|
+
};
|
|
11090
|
+
const evidenceSummary = summarizeEvidence(update.evidence);
|
|
11091
|
+
if (evidenceSummary) compact.evidenceSummary = evidenceSummary;
|
|
11092
|
+
return compact;
|
|
11093
|
+
}
|
|
10871
11094
|
function registerSubmitPlan(registry, opts) {
|
|
10872
11095
|
registry.register({
|
|
10873
11096
|
name: "submit_plan",
|
|
@@ -10981,9 +11204,9 @@ function registerMarkStepComplete(registry, opts) {
|
|
|
10981
11204
|
opts.onStepCompleted?.(update);
|
|
10982
11205
|
const verdict = await (ctx?.confirmationGate ?? pauseGate).ask({
|
|
10983
11206
|
kind: "plan_checkpoint",
|
|
10984
|
-
payload: { stepId, title, result, notes }
|
|
11207
|
+
payload: { stepId, title, result, notes, completion: update }
|
|
10985
11208
|
});
|
|
10986
|
-
if (verdict.type === "continue") return JSON.stringify(update);
|
|
11209
|
+
if (verdict.type === "continue") return JSON.stringify(compactStepCompletion(update));
|
|
10987
11210
|
if (verdict.type === "revise") {
|
|
10988
11211
|
if (verdict.feedback) return `revision requested: ${verdict.feedback}`;
|
|
10989
11212
|
throw new Error("user requested revision at checkpoint");
|
|
@@ -11310,12 +11533,40 @@ async function spawnSubagent(opts) {
|
|
|
11310
11533
|
let toolIter = 0;
|
|
11311
11534
|
let summarisingEmitted = false;
|
|
11312
11535
|
let forcedSummaryFired = false;
|
|
11536
|
+
let outputChars = 0;
|
|
11537
|
+
let reasoningChars = 0;
|
|
11538
|
+
let toolReadChars = 0;
|
|
11539
|
+
let lastStreamEmitAt = 0;
|
|
11540
|
+
let charsSinceLastEmit = 0;
|
|
11541
|
+
const STREAM_EMIT_INTERVAL_MS = 200;
|
|
11542
|
+
const STREAM_EMIT_CHARS = 400;
|
|
11543
|
+
const maybeEmitStreamProgress = (now, force) => {
|
|
11544
|
+
if (!sink?.current) return;
|
|
11545
|
+
if (!force && now - lastStreamEmitAt < STREAM_EMIT_INTERVAL_MS && charsSinceLastEmit < STREAM_EMIT_CHARS) {
|
|
11546
|
+
return;
|
|
11547
|
+
}
|
|
11548
|
+
lastStreamEmitAt = now;
|
|
11549
|
+
charsSinceLastEmit = 0;
|
|
11550
|
+
sink.current({
|
|
11551
|
+
kind: "stream-progress",
|
|
11552
|
+
runId,
|
|
11553
|
+
task: taskPreview,
|
|
11554
|
+
skillName,
|
|
11555
|
+
model,
|
|
11556
|
+
iter: toolIter,
|
|
11557
|
+
elapsedMs: now - startedAt,
|
|
11558
|
+
outputChars,
|
|
11559
|
+
reasoningChars,
|
|
11560
|
+
toolReadChars
|
|
11561
|
+
});
|
|
11562
|
+
};
|
|
11313
11563
|
try {
|
|
11314
11564
|
for await (const ev of childLoop.step(opts.task)) {
|
|
11315
11565
|
sink?.current?.({ kind: "inner", runId, task: taskPreview, skillName, model, inner: ev });
|
|
11316
11566
|
if (ev.role === "tool") {
|
|
11317
11567
|
toolIter++;
|
|
11318
11568
|
summarisingEmitted = false;
|
|
11569
|
+
toolReadChars += ev.content?.length ?? 0;
|
|
11319
11570
|
sink?.current?.({
|
|
11320
11571
|
kind: "progress",
|
|
11321
11572
|
runId,
|
|
@@ -11325,6 +11576,17 @@ async function spawnSubagent(opts) {
|
|
|
11325
11576
|
iter: toolIter,
|
|
11326
11577
|
elapsedMs: Date.now() - startedAt
|
|
11327
11578
|
});
|
|
11579
|
+
maybeEmitStreamProgress(Date.now(), true);
|
|
11580
|
+
}
|
|
11581
|
+
if (ev.role === "assistant_delta") {
|
|
11582
|
+
const dContent = ev.content?.length ?? 0;
|
|
11583
|
+
const dReason = ev.reasoningDelta?.length ?? 0;
|
|
11584
|
+
if (dContent > 0 || dReason > 0) {
|
|
11585
|
+
outputChars += dContent;
|
|
11586
|
+
reasoningChars += dReason;
|
|
11587
|
+
charsSinceLastEmit += dContent + dReason;
|
|
11588
|
+
maybeEmitStreamProgress(Date.now(), false);
|
|
11589
|
+
}
|
|
11328
11590
|
}
|
|
11329
11591
|
if (ev.role === "assistant_delta" && !summarisingEmitted && (ev.content ?? "").length > 0) {
|
|
11330
11592
|
summarisingEmitted = true;
|
|
@@ -13040,7 +13302,7 @@ function registerShellTools(registry, opts) {
|
|
|
13040
13302
|
properties: {
|
|
13041
13303
|
command: {
|
|
13042
13304
|
type: "string",
|
|
13043
|
-
description:
|
|
13305
|
+
description: "Full command line. Quoting + chain/redirect rules per the top-level description."
|
|
13044
13306
|
},
|
|
13045
13307
|
timeoutSec: {
|
|
13046
13308
|
type: "integer",
|
|
@@ -13287,6 +13549,8 @@ var USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (
|
|
|
13287
13549
|
var MOJEEK_ENDPOINT = "https://www.mojeek.com/search";
|
|
13288
13550
|
var METASO_ENDPOINT = "https://metaso.cn/api/v1";
|
|
13289
13551
|
var TAVILY_ENDPOINT = "https://api.tavily.com/search";
|
|
13552
|
+
var PERPLEXITY_ENDPOINT = "https://api.perplexity.ai/chat/completions";
|
|
13553
|
+
var EXA_ENDPOINT = "https://api.exa.ai/answer";
|
|
13290
13554
|
function searchStatusError(status) {
|
|
13291
13555
|
if (status === 429) return t("webErrors.rateLimit429");
|
|
13292
13556
|
if (status === 403) return t("webErrors.forbidden403");
|
|
@@ -13309,6 +13573,12 @@ async function webSearch(query, opts = {}) {
|
|
|
13309
13573
|
if (opts.engine === "tavily") {
|
|
13310
13574
|
return searchTavily(query, opts);
|
|
13311
13575
|
}
|
|
13576
|
+
if (opts.engine === "perplexity") {
|
|
13577
|
+
return searchPerplexity(query, opts);
|
|
13578
|
+
}
|
|
13579
|
+
if (opts.engine === "exa") {
|
|
13580
|
+
return searchExa(query, opts);
|
|
13581
|
+
}
|
|
13312
13582
|
return searchMojeek(query, opts);
|
|
13313
13583
|
}
|
|
13314
13584
|
async function searchMojeek(query, opts = {}) {
|
|
@@ -13492,6 +13762,121 @@ async function searchTavily(query, opts = {}) {
|
|
|
13492
13762
|
snippet: r.content ?? ""
|
|
13493
13763
|
}));
|
|
13494
13764
|
}
|
|
13765
|
+
async function searchPerplexity(query, opts = {}) {
|
|
13766
|
+
const topK = Math.max(1, Math.min(20, opts.topK ?? DEFAULT_TOPK));
|
|
13767
|
+
const apiKey = loadPerplexityApiKey();
|
|
13768
|
+
if (!apiKey) throw new Error(t("webErrors.perplexityMissingKey"));
|
|
13769
|
+
let resp;
|
|
13770
|
+
try {
|
|
13771
|
+
resp = await fetch(PERPLEXITY_ENDPOINT, {
|
|
13772
|
+
method: "POST",
|
|
13773
|
+
headers: {
|
|
13774
|
+
Authorization: `Bearer ${apiKey}`,
|
|
13775
|
+
"Content-Type": "application/json"
|
|
13776
|
+
},
|
|
13777
|
+
body: JSON.stringify({
|
|
13778
|
+
model: "sonar",
|
|
13779
|
+
messages: [{ role: "user", content: query }],
|
|
13780
|
+
max_tokens: 1024,
|
|
13781
|
+
return_related_questions: false
|
|
13782
|
+
}),
|
|
13783
|
+
signal: opts.signal
|
|
13784
|
+
});
|
|
13785
|
+
} catch (err) {
|
|
13786
|
+
if (err instanceof TypeError && err.message.includes("fetch")) {
|
|
13787
|
+
throw new Error(t("webErrors.cannotReach", { endpoint: PERPLEXITY_ENDPOINT }));
|
|
13788
|
+
}
|
|
13789
|
+
throw err;
|
|
13790
|
+
}
|
|
13791
|
+
if (!resp.ok) {
|
|
13792
|
+
if (resp.status === 401 || resp.status === 403) {
|
|
13793
|
+
throw new Error(t("webErrors.perplexityUnauthorized"));
|
|
13794
|
+
}
|
|
13795
|
+
if (resp.status === 429) throw new Error(t("webErrors.perplexityRateLimit"));
|
|
13796
|
+
throw new Error(t("webErrors.perplexityServerError", { status: resp.status }));
|
|
13797
|
+
}
|
|
13798
|
+
const raw = await resp.text();
|
|
13799
|
+
let data;
|
|
13800
|
+
try {
|
|
13801
|
+
data = JSON.parse(raw);
|
|
13802
|
+
} catch {
|
|
13803
|
+
throw new Error(t("webErrors.perplexityParseError", { status: resp.status }));
|
|
13804
|
+
}
|
|
13805
|
+
const answer = data.choices?.[0]?.message?.content ?? "";
|
|
13806
|
+
const citations = Array.isArray(data.citations) ? data.citations : [];
|
|
13807
|
+
const results = [];
|
|
13808
|
+
if (answer) {
|
|
13809
|
+
results.push({ title: answer, url: "", snippet: "", answer });
|
|
13810
|
+
}
|
|
13811
|
+
const count = Math.min(citations.length, topK);
|
|
13812
|
+
for (let i = 0; i < count; i++) {
|
|
13813
|
+
const c = citations[i];
|
|
13814
|
+
if (typeof c === "string") {
|
|
13815
|
+
results.push({ title: `Source ${i + 1}`, url: c, snippet: "" });
|
|
13816
|
+
} else if (c && typeof c === "object" && typeof c.url === "string") {
|
|
13817
|
+
const item = c;
|
|
13818
|
+
results.push({
|
|
13819
|
+
title: typeof item.title === "string" ? item.title : `Source ${i + 1}`,
|
|
13820
|
+
url: item.url,
|
|
13821
|
+
snippet: ""
|
|
13822
|
+
});
|
|
13823
|
+
}
|
|
13824
|
+
}
|
|
13825
|
+
return results;
|
|
13826
|
+
}
|
|
13827
|
+
async function searchExa(query, opts = {}) {
|
|
13828
|
+
const topK = Math.max(1, Math.min(20, opts.topK ?? DEFAULT_TOPK));
|
|
13829
|
+
const apiKey = loadExaApiKey();
|
|
13830
|
+
if (!apiKey) throw new Error(t("webErrors.exaMissingKey"));
|
|
13831
|
+
let resp;
|
|
13832
|
+
try {
|
|
13833
|
+
resp = await fetch(EXA_ENDPOINT, {
|
|
13834
|
+
method: "POST",
|
|
13835
|
+
headers: {
|
|
13836
|
+
"x-api-key": apiKey,
|
|
13837
|
+
"Content-Type": "application/json"
|
|
13838
|
+
},
|
|
13839
|
+
body: JSON.stringify({ query, text: true }),
|
|
13840
|
+
signal: opts.signal
|
|
13841
|
+
});
|
|
13842
|
+
} catch (err) {
|
|
13843
|
+
if (err instanceof TypeError && err.message.includes("fetch")) {
|
|
13844
|
+
throw new Error(t("webErrors.cannotReach", { endpoint: EXA_ENDPOINT }));
|
|
13845
|
+
}
|
|
13846
|
+
throw err;
|
|
13847
|
+
}
|
|
13848
|
+
if (!resp.ok) {
|
|
13849
|
+
if (resp.status === 401 || resp.status === 403) {
|
|
13850
|
+
throw new Error(t("webErrors.exaUnauthorized"));
|
|
13851
|
+
}
|
|
13852
|
+
if (resp.status === 429) throw new Error(t("webErrors.exaRateLimit"));
|
|
13853
|
+
throw new Error(t("webErrors.exaServerError", { status: resp.status }));
|
|
13854
|
+
}
|
|
13855
|
+
const raw = await resp.text();
|
|
13856
|
+
let data;
|
|
13857
|
+
try {
|
|
13858
|
+
data = JSON.parse(raw);
|
|
13859
|
+
} catch {
|
|
13860
|
+
throw new Error(t("webErrors.exaParseError", { status: resp.status }));
|
|
13861
|
+
}
|
|
13862
|
+
const answer = data.answer ?? "";
|
|
13863
|
+
const citations = data.citations ?? [];
|
|
13864
|
+
const results = [];
|
|
13865
|
+
if (answer) {
|
|
13866
|
+
results.push({ title: answer, url: "", snippet: "", answer });
|
|
13867
|
+
}
|
|
13868
|
+
const count = Math.min(citations.length, topK);
|
|
13869
|
+
for (let i = 0; i < count; i++) {
|
|
13870
|
+
const c = citations[i];
|
|
13871
|
+
if (!c.url) continue;
|
|
13872
|
+
results.push({
|
|
13873
|
+
title: c.title || `Source ${i + 1}`,
|
|
13874
|
+
url: c.url,
|
|
13875
|
+
snippet: c.text ?? ""
|
|
13876
|
+
});
|
|
13877
|
+
}
|
|
13878
|
+
return results;
|
|
13879
|
+
}
|
|
13495
13880
|
function parseSearxngHtmlResults(html) {
|
|
13496
13881
|
const root = parseHtml(html);
|
|
13497
13882
|
const results = [];
|
|
@@ -13710,7 +14095,7 @@ function registerWebTools(registry, opts = {}) {
|
|
|
13710
14095
|
const maxFetchChars = opts.maxFetchChars ?? DEFAULT_FETCH_MAX_CHARS;
|
|
13711
14096
|
registry.register({
|
|
13712
14097
|
name: "web_search",
|
|
13713
|
-
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.
|
|
14098
|
+
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.",
|
|
13714
14099
|
readOnly: true,
|
|
13715
14100
|
parallelSafe: true,
|
|
13716
14101
|
parameters: {
|
|
@@ -13719,7 +14104,7 @@ function registerWebTools(registry, opts = {}) {
|
|
|
13719
14104
|
query: { type: "string", description: "Natural-language search query." },
|
|
13720
14105
|
topK: {
|
|
13721
14106
|
type: "integer",
|
|
13722
|
-
description: `Number of results to return
|
|
14107
|
+
description: `Number of results to return. Default ${defaultTopK}.`
|
|
13723
14108
|
}
|
|
13724
14109
|
},
|
|
13725
14110
|
required: ["query"]
|
|
@@ -13763,14 +14148,30 @@ ${page.text}`;
|
|
|
13763
14148
|
return registry;
|
|
13764
14149
|
}
|
|
13765
14150
|
function formatSearchResults(query, results) {
|
|
13766
|
-
const lines = [`query: ${query}
|
|
13767
|
-
|
|
13768
|
-
|
|
14151
|
+
const lines = [`query: ${query}`];
|
|
14152
|
+
const hasAnswer = results.length > 0 && results[0]?.url === "" && results[0]?.answer;
|
|
14153
|
+
if (hasAnswer) {
|
|
14154
|
+
lines.push("\nanswer:");
|
|
14155
|
+
lines.push(` ${results[0].answer}`);
|
|
14156
|
+
const sources = results.slice(1);
|
|
13769
14157
|
lines.push(`
|
|
14158
|
+
sources (${sources.length}):`);
|
|
14159
|
+
sources.forEach((r, i) => {
|
|
14160
|
+
lines.push(`
|
|
13770
14161
|
${i + 1}. ${r.title}`);
|
|
13771
|
-
|
|
13772
|
-
|
|
13773
|
-
|
|
14162
|
+
lines.push(` ${r.url}`);
|
|
14163
|
+
if (r.snippet) lines.push(` ${r.snippet}`);
|
|
14164
|
+
});
|
|
14165
|
+
} else {
|
|
14166
|
+
lines.push(`
|
|
14167
|
+
results (${results.length}):`);
|
|
14168
|
+
results.forEach((r, i) => {
|
|
14169
|
+
lines.push(`
|
|
14170
|
+
${i + 1}. ${r.title}`);
|
|
14171
|
+
lines.push(` ${r.url}`);
|
|
14172
|
+
if (r.snippet) lines.push(` ${r.snippet}`);
|
|
14173
|
+
});
|
|
14174
|
+
}
|
|
13774
14175
|
return lines.join("\n");
|
|
13775
14176
|
}
|
|
13776
14177
|
|
|
@@ -15132,6 +15533,14 @@ function applyEditBlock(block, rootDir) {
|
|
|
15132
15533
|
message: "SEARCH text does not match the current file content exactly"
|
|
15133
15534
|
};
|
|
15134
15535
|
}
|
|
15536
|
+
const nextIdx = content.indexOf(adaptedSearch, idx + 1);
|
|
15537
|
+
if (nextIdx !== -1) {
|
|
15538
|
+
return {
|
|
15539
|
+
path: block.path,
|
|
15540
|
+
status: "not-found",
|
|
15541
|
+
message: "SEARCH text appears multiple times; include more context to disambiguate"
|
|
15542
|
+
};
|
|
15543
|
+
}
|
|
15135
15544
|
const replaced = `${content.slice(0, idx)}${adaptedReplace}${content.slice(idx + adaptedSearch.length)}`;
|
|
15136
15545
|
const outBuf = Buffer.from(replaced, "utf8");
|
|
15137
15546
|
ftruncateSync(fd, outBuf.length);
|
|
@@ -15333,18 +15742,8 @@ You have BOTH \`semantic_search\` (vector index) and \`search_content\` (literal
|
|
|
15333
15742
|
- **Exact-token queries** (a specific identifier, regex, or "find every call to foo") \u2192 call \`search_content\`.
|
|
15334
15743
|
|
|
15335
15744
|
If \`semantic_search\` returns nothing useful (low scores, off-topic), THEN fall back to \`search_content\`. Don't go the other way \u2014 grepping a paraphrased question wastes turns.`;
|
|
15336
|
-
var ENGINEERING_LIFECYCLE_CONTRACT = `
|
|
15337
|
-
|
|
15338
|
-
# Engineering lifecycle contract
|
|
15339
|
-
|
|
15340
|
-
Reasonix may enforce a prefix-stable Engineering Lifecycle for explicitly enabled high-risk engineering work. The runtime keeps lifecycle state outside the system prompt and tool list, so do not expect stage-specific prompt changes or new tools to appear. Treat any lifecycle block as a host constraint, not as a suggestion.
|
|
15341
|
-
|
|
15342
|
-
When high-risk mutations are bounced with \`rejectedReason: "engineering-lifecycle"\`, switch to read-only exploration, then call \`submit_plan\` with concrete steps before trying the mutation again. Add optional per-step \`targets\`, \`acceptance\`, and \`verification\` fields when they clarify scope or success criteria. For medium/high-risk steps, steps with verification criteria, or steps that changed code, \`mark_step_complete\` requires \`evidence\` entries such as verification output, diff summary, checkpoint id, or manual rationale.`;
|
|
15343
15745
|
function codeSystemPrompt(rootDir, opts = {}) {
|
|
15344
|
-
|
|
15345
|
-
if (opts.engineeringLifecycleMode === "strict") {
|
|
15346
|
-
codeBase = `${codeBase}${ENGINEERING_LIFECYCLE_CONTRACT}`;
|
|
15347
|
-
}
|
|
15746
|
+
const codeBase = codeSystemBase(opts.modelId ?? DEFAULT_CODE_MODEL);
|
|
15348
15747
|
const base = opts.hasSemanticSearch ? `${codeBase}${SEMANTIC_SEARCH_ROUTING}` : codeBase;
|
|
15349
15748
|
const withMemory = applyMemoryStack(base, rootDir);
|
|
15350
15749
|
const gitignorePath = join15(rootDir, ".gitignore");
|
|
@@ -15698,8 +16097,10 @@ export {
|
|
|
15698
16097
|
loadApiKey,
|
|
15699
16098
|
loadBaseUrl,
|
|
15700
16099
|
loadDotenv,
|
|
16100
|
+
loadExaApiKey,
|
|
15701
16101
|
loadHooks,
|
|
15702
16102
|
loadMetasoApiKey,
|
|
16103
|
+
loadPerplexityApiKey,
|
|
15703
16104
|
loadSessionMessages,
|
|
15704
16105
|
matchesTool,
|
|
15705
16106
|
memoryEnabled,
|