reasonix 0.48.1 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dashboard/dist/app.js +123 -16
- package/dashboard/dist/app.js.map +1 -1
- package/dist/cli/{acp-QJGGHQLA.js → acp-WFQIC6SO.js} +18 -18
- package/dist/cli/chat-D32JGNVH.js +51 -0
- package/dist/cli/{chunk-TIJ4ZHD6.js → chunk-23ZPCIPR.js} +12 -9
- package/dist/cli/chunk-23ZPCIPR.js.map +1 -0
- package/dist/cli/{chunk-TKVXTQ3T.js → chunk-3ZZXQ3CZ.js} +27 -27
- package/dist/cli/chunk-3ZZXQ3CZ.js.map +1 -0
- package/dist/cli/{chunk-H2F4LDNH.js → chunk-7AST3QQ3.js} +2 -2
- package/dist/cli/{chunk-XMR2VCGT.js → chunk-7JTKBJ2G.js} +3 -3
- package/dist/cli/{chunk-X53B3JIX.js → chunk-7X4JJOO7.js} +2 -61
- package/dist/cli/{chunk-X53B3JIX.js.map → chunk-7X4JJOO7.js.map} +1 -1
- package/dist/cli/{chunk-WKOXKCF3.js → chunk-ASOLXV67.js} +3 -3
- package/dist/cli/{chunk-43ROGEX2.js → chunk-AWEULQG6.js} +10 -9
- package/dist/cli/{chunk-43ROGEX2.js.map → chunk-AWEULQG6.js.map} +1 -1
- package/dist/cli/{chunk-5AW6NIHU.js → chunk-DFX5ZH5L.js} +2 -2
- package/dist/cli/{chunk-4E2BHJU4.js → chunk-GNS7BAT2.js} +2 -2
- package/dist/cli/{chunk-5U5LMMFF.js → chunk-J2IHQGPQ.js} +12 -6
- package/dist/cli/chunk-J2IHQGPQ.js.map +1 -0
- package/dist/cli/{chunk-O3AGYTG2.js → chunk-JGTX4RRQ.js} +3 -3
- package/dist/cli/{chunk-SLAFMXAZ.js → chunk-JNTMOX7G.js} +2 -2
- package/dist/cli/{chunk-RCLS63KE.js → chunk-MGTBP7GG.js} +2 -2
- package/dist/cli/{chunk-JFBGSWQB.js → chunk-MQWO32ZD.js} +323 -159
- package/dist/cli/chunk-MQWO32ZD.js.map +1 -0
- package/dist/cli/{chunk-FD7SNDWW.js → chunk-O5LIHAMP.js} +8 -4
- package/dist/cli/chunk-O5LIHAMP.js.map +1 -0
- package/dist/cli/{chunk-PJIQIYTV.js → chunk-PB3MAFEI.js} +3 -3
- package/dist/cli/{chunk-NQZ5U37J.js → chunk-PEMG6CUB.js} +2 -2
- package/dist/cli/{chunk-KH5JIJJD.js → chunk-PXBQ6IZ7.js} +3 -3
- package/dist/cli/{chunk-J2TQAWOM.js → chunk-Q46B3Z7H.js} +25 -10
- package/dist/cli/{chunk-J2TQAWOM.js.map → chunk-Q46B3Z7H.js.map} +1 -1
- package/dist/cli/{chunk-DABAOQSV.js → chunk-QF32ROX2.js} +1260 -1754
- package/dist/cli/chunk-QF32ROX2.js.map +1 -0
- package/dist/cli/{chunk-IKSYVBBZ.js → chunk-QX5TWXRZ.js} +2 -2
- package/dist/cli/{chunk-R7U44O3Y.js → chunk-TAIKVL35.js} +2 -2
- package/dist/cli/{chunk-B5CZL2SE.js → chunk-TEDWJKEI.js} +4 -9
- package/dist/cli/chunk-TEDWJKEI.js.map +1 -0
- package/dist/cli/{chunk-EO6RPTJG.js → chunk-U5XQDCK7.js} +5 -5
- package/dist/cli/{chunk-SWUMD2LX.js → chunk-W46ZMNKO.js} +3 -3
- package/dist/cli/{chunk-FPME5QOO.js → chunk-WMTMMSXU.js} +166 -5
- package/dist/cli/chunk-WMTMMSXU.js.map +1 -0
- package/dist/cli/{chunk-PIC5HJRD.js → chunk-YEF7C4XI.js} +154 -86
- package/dist/cli/chunk-YEF7C4XI.js.map +1 -0
- package/dist/cli/{chunk-3FULTFRB.js → chunk-ZAEJWKXB.js} +2 -2
- package/dist/cli/chunk-ZWHSHFDP.js +6173 -0
- package/dist/cli/chunk-ZWHSHFDP.js.map +1 -0
- package/dist/cli/{code-OKA5YPOH.js → code-R4IHI7SR.js} +30 -30
- package/dist/cli/{commands-3U6PUVSS.js → commands-DRHFCYMO.js} +4 -4
- package/dist/cli/{commit-HFB7SRBU.js → commit-AG5KB4YP.js} +3 -3
- package/dist/cli/{desktop-G7UMW3CJ.js → desktop-JGL6GORA.js} +19 -19
- package/dist/cli/{diff-PGXW4YZD.js → diff-4Z7ETWZO.js} +9 -9
- package/dist/cli/{doctor-WWJFLVCB.js → doctor-VA3RHQLB.js} +9 -9
- package/dist/cli/index.js +37 -36
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{mcp-Y3VKIK3T.js → mcp-LZO4HXFA.js} +34 -23
- package/dist/cli/mcp-LZO4HXFA.js.map +1 -0
- package/dist/cli/{mcp-browse-4IN2QIFR.js → mcp-browse-C3GXVMYZ.js} +3 -3
- package/dist/cli/{mcp-inspect-BUXFXDHB.js → mcp-inspect-ZMYUNFDS.js} +2 -2
- package/dist/cli/{prompt-US57R6BA.js → prompt-MC3U5KRP.js} +5 -5
- package/dist/cli/{prune-sessions-SEWX7GP6.js → prune-sessions-OEPFH4N6.js} +11 -7
- package/dist/cli/prune-sessions-OEPFH4N6.js.map +1 -0
- package/dist/cli/{replay-EQJMZRB3.js → replay-4TP7ZUMZ.js} +10 -10
- package/dist/cli/{run-KVEI66TR.js → run-6MXQYBOE.js} +16 -15
- package/dist/cli/run-6MXQYBOE.js.map +1 -0
- package/dist/cli/{server-D6C4GHWN.js → server-Z3IMJNNI.js} +63 -12
- package/dist/cli/server-Z3IMJNNI.js.map +1 -0
- package/dist/cli/{sessions-TGVS2RQZ.js → sessions-NXQ5SAV7.js} +18 -18
- package/dist/cli/sessions-NXQ5SAV7.js.map +1 -0
- package/dist/cli/{setup-WLKX6GGG.js → setup-LHZELI6I.js} +6 -6
- package/dist/cli/{stats-TCD7Q6MB.js → stats-SUIJ3QWY.js} +6 -6
- package/dist/cli/{version-BCWWS2OU.js → version-BIFONEUB.js} +13 -13
- package/dist/index.d.ts +63 -15
- package/dist/index.js +855 -360
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/cli/chat-ZXF227MP.js +0 -51
- package/dist/cli/chunk-5U5LMMFF.js.map +0 -1
- package/dist/cli/chunk-6FRNXWDZ.js +0 -2265
- package/dist/cli/chunk-6FRNXWDZ.js.map +0 -1
- package/dist/cli/chunk-B5CZL2SE.js.map +0 -1
- package/dist/cli/chunk-DABAOQSV.js.map +0 -1
- package/dist/cli/chunk-FD7SNDWW.js.map +0 -1
- package/dist/cli/chunk-FPME5QOO.js.map +0 -1
- package/dist/cli/chunk-JFBGSWQB.js.map +0 -1
- package/dist/cli/chunk-PIC5HJRD.js.map +0 -1
- package/dist/cli/chunk-TIJ4ZHD6.js.map +0 -1
- package/dist/cli/chunk-TKVXTQ3T.js.map +0 -1
- package/dist/cli/mcp-Y3VKIK3T.js.map +0 -1
- package/dist/cli/prune-sessions-SEWX7GP6.js.map +0 -1
- package/dist/cli/run-KVEI66TR.js.map +0 -1
- package/dist/cli/server-D6C4GHWN.js.map +0 -1
- package/dist/cli/sessions-TGVS2RQZ.js.map +0 -1
- /package/dist/cli/{acp-QJGGHQLA.js.map → acp-WFQIC6SO.js.map} +0 -0
- /package/dist/cli/{chat-ZXF227MP.js.map → chat-D32JGNVH.js.map} +0 -0
- /package/dist/cli/{chunk-H2F4LDNH.js.map → chunk-7AST3QQ3.js.map} +0 -0
- /package/dist/cli/{chunk-XMR2VCGT.js.map → chunk-7JTKBJ2G.js.map} +0 -0
- /package/dist/cli/{chunk-WKOXKCF3.js.map → chunk-ASOLXV67.js.map} +0 -0
- /package/dist/cli/{chunk-5AW6NIHU.js.map → chunk-DFX5ZH5L.js.map} +0 -0
- /package/dist/cli/{chunk-4E2BHJU4.js.map → chunk-GNS7BAT2.js.map} +0 -0
- /package/dist/cli/{chunk-O3AGYTG2.js.map → chunk-JGTX4RRQ.js.map} +0 -0
- /package/dist/cli/{chunk-SLAFMXAZ.js.map → chunk-JNTMOX7G.js.map} +0 -0
- /package/dist/cli/{chunk-RCLS63KE.js.map → chunk-MGTBP7GG.js.map} +0 -0
- /package/dist/cli/{chunk-PJIQIYTV.js.map → chunk-PB3MAFEI.js.map} +0 -0
- /package/dist/cli/{chunk-NQZ5U37J.js.map → chunk-PEMG6CUB.js.map} +0 -0
- /package/dist/cli/{chunk-KH5JIJJD.js.map → chunk-PXBQ6IZ7.js.map} +0 -0
- /package/dist/cli/{chunk-IKSYVBBZ.js.map → chunk-QX5TWXRZ.js.map} +0 -0
- /package/dist/cli/{chunk-R7U44O3Y.js.map → chunk-TAIKVL35.js.map} +0 -0
- /package/dist/cli/{chunk-EO6RPTJG.js.map → chunk-U5XQDCK7.js.map} +0 -0
- /package/dist/cli/{chunk-SWUMD2LX.js.map → chunk-W46ZMNKO.js.map} +0 -0
- /package/dist/cli/{chunk-3FULTFRB.js.map → chunk-ZAEJWKXB.js.map} +0 -0
- /package/dist/cli/{code-OKA5YPOH.js.map → code-R4IHI7SR.js.map} +0 -0
- /package/dist/cli/{commands-3U6PUVSS.js.map → commands-DRHFCYMO.js.map} +0 -0
- /package/dist/cli/{commit-HFB7SRBU.js.map → commit-AG5KB4YP.js.map} +0 -0
- /package/dist/cli/{desktop-G7UMW3CJ.js.map → desktop-JGL6GORA.js.map} +0 -0
- /package/dist/cli/{diff-PGXW4YZD.js.map → diff-4Z7ETWZO.js.map} +0 -0
- /package/dist/cli/{doctor-WWJFLVCB.js.map → doctor-VA3RHQLB.js.map} +0 -0
- /package/dist/cli/{mcp-browse-4IN2QIFR.js.map → mcp-browse-C3GXVMYZ.js.map} +0 -0
- /package/dist/cli/{mcp-inspect-BUXFXDHB.js.map → mcp-inspect-ZMYUNFDS.js.map} +0 -0
- /package/dist/cli/{prompt-US57R6BA.js.map → prompt-MC3U5KRP.js.map} +0 -0
- /package/dist/cli/{replay-EQJMZRB3.js.map → replay-4TP7ZUMZ.js.map} +0 -0
- /package/dist/cli/{setup-WLKX6GGG.js.map → setup-LHZELI6I.js.map} +0 -0
- /package/dist/cli/{stats-TCD7Q6MB.js.map → stats-SUIJ3QWY.js.map} +0 -0
- /package/dist/cli/{version-BCWWS2OU.js.map → version-BIFONEUB.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
import { createParser } from "eventsource-parser";
|
|
3
3
|
|
|
4
4
|
// src/config.ts
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
appendFileSync,
|
|
7
|
+
chmodSync,
|
|
8
|
+
mkdirSync,
|
|
9
|
+
readFileSync,
|
|
10
|
+
renameSync,
|
|
11
|
+
writeFileSync
|
|
12
|
+
} from "fs";
|
|
6
13
|
import { homedir } from "os";
|
|
7
14
|
import { dirname, isAbsolute, join, resolve } from "path";
|
|
8
15
|
import { z } from "zod";
|
|
@@ -429,6 +436,117 @@ function parseMcpSpec(input) {
|
|
|
429
436
|
return { transport: "stdio", name, command, args };
|
|
430
437
|
}
|
|
431
438
|
|
|
439
|
+
// src/tools/rate-limit.ts
|
|
440
|
+
var DEFAULT_TOOL_RATE_LIMIT = {
|
|
441
|
+
aggregate: { maxCalls: 200, windowSeconds: 60 },
|
|
442
|
+
tools: {
|
|
443
|
+
run_command: { maxCalls: 60, windowSeconds: 60 },
|
|
444
|
+
run_background: { maxCalls: 10, windowSeconds: 60 }
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
var ToolRateLimiter = class {
|
|
448
|
+
config;
|
|
449
|
+
clock;
|
|
450
|
+
aggregate = [];
|
|
451
|
+
tools = /* @__PURE__ */ new Map();
|
|
452
|
+
constructor(config = {}, clock = () => Date.now()) {
|
|
453
|
+
this.config = normalizeToolRateLimitConfig(config);
|
|
454
|
+
this.clock = clock;
|
|
455
|
+
}
|
|
456
|
+
get policy() {
|
|
457
|
+
return this.config;
|
|
458
|
+
}
|
|
459
|
+
consume(tool) {
|
|
460
|
+
if (this.config === false) return { allowed: true };
|
|
461
|
+
const now = this.clock();
|
|
462
|
+
const toolBucket = this.config.tools[tool];
|
|
463
|
+
if (toolBucket !== false && toolBucket !== void 0) {
|
|
464
|
+
const timestamps = this.timestampsFor(tool);
|
|
465
|
+
const blocked = inspectBucket(tool, timestamps, toolBucket, now);
|
|
466
|
+
if (blocked) return { allowed: false, result: blocked };
|
|
467
|
+
}
|
|
468
|
+
const aggregateBlocked = inspectBucket(
|
|
469
|
+
tool,
|
|
470
|
+
this.aggregate,
|
|
471
|
+
this.config.aggregate,
|
|
472
|
+
now,
|
|
473
|
+
"all_tools"
|
|
474
|
+
);
|
|
475
|
+
if (aggregateBlocked) return { allowed: false, result: aggregateBlocked };
|
|
476
|
+
this.aggregate.push(now);
|
|
477
|
+
if (toolBucket !== false && toolBucket !== void 0) this.timestampsFor(tool).push(now);
|
|
478
|
+
return { allowed: true };
|
|
479
|
+
}
|
|
480
|
+
timestampsFor(tool) {
|
|
481
|
+
const existing = this.tools.get(tool);
|
|
482
|
+
if (existing) return existing;
|
|
483
|
+
const created = [];
|
|
484
|
+
this.tools.set(tool, created);
|
|
485
|
+
return created;
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
function normalizeToolRateLimitConfig(config) {
|
|
489
|
+
if (config === false || config?.enabled === false) return false;
|
|
490
|
+
const aggregate = normalizeBucket(config?.aggregate, DEFAULT_TOOL_RATE_LIMIT.aggregate);
|
|
491
|
+
const tools = {
|
|
492
|
+
...DEFAULT_TOOL_RATE_LIMIT.tools
|
|
493
|
+
};
|
|
494
|
+
for (const [name, value] of Object.entries(config?.tools ?? {})) {
|
|
495
|
+
if (value === false) {
|
|
496
|
+
tools[name] = false;
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
const fallback = DEFAULT_TOOL_RATE_LIMIT.tools[name];
|
|
500
|
+
tools[name] = normalizeBucket(
|
|
501
|
+
value,
|
|
502
|
+
fallback === false || fallback === void 0 ? DEFAULT_TOOL_RATE_LIMIT.aggregate : fallback
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
return { aggregate, tools };
|
|
506
|
+
}
|
|
507
|
+
function parseRateLimitedToolResult(result) {
|
|
508
|
+
try {
|
|
509
|
+
const parsed = JSON.parse(result);
|
|
510
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
511
|
+
const value = parsed;
|
|
512
|
+
if (value.error !== "rate_limited") return null;
|
|
513
|
+
if (typeof value.tool !== "string" || typeof value.scope !== "string") return null;
|
|
514
|
+
if (typeof value.limit !== "number" || typeof value.windowSeconds !== "number") return null;
|
|
515
|
+
if (typeof value.retryAfterMs !== "number" || typeof value.message !== "string") return null;
|
|
516
|
+
return value;
|
|
517
|
+
} catch {
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
function normalizeBucket(raw, fallback) {
|
|
522
|
+
return {
|
|
523
|
+
maxCalls: positiveInteger(raw?.maxCalls) ?? fallback.maxCalls,
|
|
524
|
+
windowSeconds: positiveInteger(raw?.windowSeconds) ?? fallback.windowSeconds
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
function positiveInteger(value) {
|
|
528
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
|
|
529
|
+
}
|
|
530
|
+
function inspectBucket(tool, timestamps, bucket, now, scope = tool) {
|
|
531
|
+
const windowMs = bucket.windowSeconds * 1e3;
|
|
532
|
+
while (timestamps.length > 0 && now - timestamps[0] >= windowMs) timestamps.shift();
|
|
533
|
+
if (timestamps.length < bucket.maxCalls) return null;
|
|
534
|
+
const retryAfterMs = Math.max(0, timestamps[0] + windowMs - now);
|
|
535
|
+
return {
|
|
536
|
+
error: "rate_limited",
|
|
537
|
+
tool,
|
|
538
|
+
scope,
|
|
539
|
+
limit: bucket.maxCalls,
|
|
540
|
+
windowSeconds: bucket.windowSeconds,
|
|
541
|
+
retryAfterMs,
|
|
542
|
+
message: `${scope} rate-limited: ${bucket.maxCalls} calls / ${bucket.windowSeconds}s. Wait ${formatWait(retryAfterMs)} or summarize what you know.`
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
function formatWait(ms) {
|
|
546
|
+
const seconds = ms / 1e3;
|
|
547
|
+
return `${Number.isInteger(seconds) ? seconds.toFixed(0) : seconds.toFixed(1)}s`;
|
|
548
|
+
}
|
|
549
|
+
|
|
432
550
|
// src/config.ts
|
|
433
551
|
var BUILTIN_TYPE_DOCS = {
|
|
434
552
|
user: "role / skills / preferences",
|
|
@@ -542,10 +660,30 @@ function readConfig(path2 = defaultConfigPath()) {
|
|
|
542
660
|
return {};
|
|
543
661
|
}
|
|
544
662
|
function writeConfig(cfg, path2 = defaultConfigPath()) {
|
|
663
|
+
debugLogConfigWrite(cfg, path2);
|
|
545
664
|
mkdirSync(dirname(path2), { recursive: true });
|
|
546
|
-
|
|
665
|
+
const tmp = `${path2}.${process.pid}.tmp`;
|
|
666
|
+
writeFileSync(tmp, JSON.stringify(cfg, null, 2), "utf8");
|
|
547
667
|
try {
|
|
548
|
-
chmodSync(
|
|
668
|
+
chmodSync(tmp, 384);
|
|
669
|
+
} catch {
|
|
670
|
+
}
|
|
671
|
+
renameSync(tmp, path2);
|
|
672
|
+
}
|
|
673
|
+
function debugLogConfigWrite(cfg, configPath) {
|
|
674
|
+
const debugPath = process.env.REASONIX_DEBUG_PRESET;
|
|
675
|
+
if (!debugPath) return;
|
|
676
|
+
try {
|
|
677
|
+
const stack = new Error("trace").stack ?? "";
|
|
678
|
+
const keys = Object.keys(cfg).sort().join(",");
|
|
679
|
+
const presetField = cfg.preset === void 0 ? "(absent)" : JSON.stringify(cfg.preset);
|
|
680
|
+
const line = `${(/* @__PURE__ */ new Date()).toISOString()} writeConfig pid=${process.pid} \u2192 ${configPath}
|
|
681
|
+
keys=[${keys}]
|
|
682
|
+
preset=${presetField}
|
|
683
|
+
${stack.split("\n").slice(1, 10).map((l) => ` ${l.trim()}`).join("\n")}
|
|
684
|
+
|
|
685
|
+
`;
|
|
686
|
+
appendFileSync(debugPath, line);
|
|
549
687
|
} catch {
|
|
550
688
|
}
|
|
551
689
|
}
|
|
@@ -633,7 +771,7 @@ function webSearchEngine(path2 = defaultConfigPath()) {
|
|
|
633
771
|
if (cfg === "tavily") return "tavily";
|
|
634
772
|
if (cfg === "perplexity") return "perplexity";
|
|
635
773
|
if (cfg === "exa") return "exa";
|
|
636
|
-
return "
|
|
774
|
+
return "bing";
|
|
637
775
|
}
|
|
638
776
|
function webSearchEndpoint(path2 = defaultConfigPath()) {
|
|
639
777
|
const cfg = readConfig(path2).webSearchEndpoint;
|
|
@@ -753,8 +891,8 @@ function computeWait(attempt, initial, cap, retryAfter) {
|
|
|
753
891
|
}
|
|
754
892
|
function sleep(ms, signal) {
|
|
755
893
|
if (ms <= 0) return Promise.resolve();
|
|
756
|
-
return new Promise((
|
|
757
|
-
const timer = setTimeout(
|
|
894
|
+
return new Promise((resolve14, reject) => {
|
|
895
|
+
const timer = setTimeout(resolve14, ms);
|
|
758
896
|
if (signal) {
|
|
759
897
|
const onAbort = () => {
|
|
760
898
|
clearTimeout(timer);
|
|
@@ -842,8 +980,8 @@ var DeepSeekClient = class {
|
|
|
842
980
|
const waitMs = Math.max(0, this.nextChatRequestAt - now);
|
|
843
981
|
this.nextChatRequestAt = Math.max(now, this.nextChatRequestAt) + this.minChatIntervalMs;
|
|
844
982
|
if (waitMs <= 0) return;
|
|
845
|
-
await new Promise((
|
|
846
|
-
const timer = setTimeout(
|
|
983
|
+
await new Promise((resolve14, reject) => {
|
|
984
|
+
const timer = setTimeout(resolve14, waitMs);
|
|
847
985
|
signal?.addEventListener(
|
|
848
986
|
"abort",
|
|
849
987
|
() => {
|
|
@@ -917,8 +1055,11 @@ var DeepSeekClient = class {
|
|
|
917
1055
|
}
|
|
918
1056
|
async chat(opts) {
|
|
919
1057
|
const ctrl = new AbortController();
|
|
920
|
-
const timer = setTimeout(
|
|
921
|
-
|
|
1058
|
+
const timer = setTimeout(
|
|
1059
|
+
() => ctrl.abort(new Error(`DeepSeek request timed out after ${this.timeoutMs}ms`)),
|
|
1060
|
+
this.timeoutMs
|
|
1061
|
+
);
|
|
1062
|
+
const signal = opts.signal ? AbortSignal.any([opts.signal, ctrl.signal]) : ctrl.signal;
|
|
922
1063
|
try {
|
|
923
1064
|
await this.waitForChatRateLimit(signal);
|
|
924
1065
|
const resp = await fetchWithRetry(
|
|
@@ -953,8 +1094,11 @@ var DeepSeekClient = class {
|
|
|
953
1094
|
}
|
|
954
1095
|
async *stream(opts) {
|
|
955
1096
|
const ctrl = new AbortController();
|
|
956
|
-
const timer = setTimeout(
|
|
957
|
-
|
|
1097
|
+
const timer = setTimeout(
|
|
1098
|
+
() => ctrl.abort(new Error(`DeepSeek stream timed out after ${this.timeoutMs}ms`)),
|
|
1099
|
+
this.timeoutMs
|
|
1100
|
+
);
|
|
1101
|
+
const signal = opts.signal ? AbortSignal.any([opts.signal, ctrl.signal]) : ctrl.signal;
|
|
958
1102
|
let resp;
|
|
959
1103
|
try {
|
|
960
1104
|
await this.waitForChatRateLimit(signal);
|
|
@@ -1053,10 +1197,10 @@ var PauseGate = class {
|
|
|
1053
1197
|
`${kind}: no confirmation listener registered \u2014 cannot prompt the user. This tool can only be used inside an interactive Reasonix session.`
|
|
1054
1198
|
);
|
|
1055
1199
|
}
|
|
1056
|
-
return new Promise((
|
|
1200
|
+
return new Promise((resolve14) => {
|
|
1057
1201
|
const id = this._nextId++;
|
|
1058
1202
|
const request = { id, kind, payload };
|
|
1059
|
-
this._pending.set(id, { resolve:
|
|
1203
|
+
this._pending.set(id, { resolve: resolve14, request });
|
|
1060
1204
|
for (const fn of this._listeners) {
|
|
1061
1205
|
try {
|
|
1062
1206
|
fn(request);
|
|
@@ -1211,7 +1355,17 @@ var EN = {
|
|
|
1211
1355
|
missingApiKey: "DEEPSEEK_API_KEY is not set and stdin is not a TTY (cannot prompt).\nSet the env var, or run `reasonix chat` once interactively to save a key.\n"
|
|
1212
1356
|
},
|
|
1213
1357
|
sessions: {
|
|
1214
|
-
emptyHint: "no saved sessions yet \u2014 run `reasonix chat` (sessions are auto-saved unless --no-session)."
|
|
1358
|
+
emptyHint: "no saved sessions yet \u2014 run `reasonix chat` (sessions are auto-saved unless --no-session).",
|
|
1359
|
+
listHeader: "Saved sessions (~/.reasonix/sessions/):",
|
|
1360
|
+
inspectHint: "Inspect: reasonix sessions <name>",
|
|
1361
|
+
resumeHint: "Resume: reasonix chat --session <name>",
|
|
1362
|
+
noSession: 'no session named "{name}" (or it\u2019s empty).',
|
|
1363
|
+
lookedAt: "looked at: {path}",
|
|
1364
|
+
noIdleSessions: "no sessions idle \u2265{days} days. Nothing pruned.",
|
|
1365
|
+
wouldPrune: "would prune {count} session(s) idle \u2265{days} days:",
|
|
1366
|
+
dryRunHint: "re-run without --dry-run to actually delete.",
|
|
1367
|
+
prunedCount: "pruned {count} session(s) idle \u2265{days} days:",
|
|
1368
|
+
daysInvalid: "--days must be a positive integer (got {days})."
|
|
1215
1369
|
},
|
|
1216
1370
|
ui: {
|
|
1217
1371
|
welcome: "Run `reasonix` any time to start chatting \u2014 your settings are remembered.",
|
|
@@ -1315,10 +1469,6 @@ var EN = {
|
|
|
1315
1469
|
title: "copy / paste",
|
|
1316
1470
|
rows: [
|
|
1317
1471
|
{ key: "select text", text: "drag to select \u2014 terminal-native (no modifier needed)" },
|
|
1318
|
-
{
|
|
1319
|
-
key: "/copy",
|
|
1320
|
-
text: "vim/tmux-style copy mode \u2014 works in SSH/mosh/tmux where drag-select can't extend past the viewport"
|
|
1321
|
-
},
|
|
1322
1472
|
{
|
|
1323
1473
|
key: "copy",
|
|
1324
1474
|
text: "Ctrl+Shift+C (Win/Linux) \xB7 Cmd+C (macOS) \u2014 or auto-copy-on-select if your terminal does it"
|
|
@@ -1409,9 +1559,6 @@ var EN = {
|
|
|
1409
1559
|
},
|
|
1410
1560
|
slash: {
|
|
1411
1561
|
help: { description: "show the full command reference" },
|
|
1412
|
-
copy: {
|
|
1413
|
-
description: "open vim/tmux-style copy mode \u2014 j/k navigate, v select, y yank to clipboard"
|
|
1414
|
-
},
|
|
1415
1562
|
status: { description: "current model, flags, context, session" },
|
|
1416
1563
|
preset: {
|
|
1417
1564
|
description: "model bundle \u2014 auto escalates flash \u2192 pro, flash/pro lock",
|
|
@@ -1563,8 +1710,8 @@ var EN = {
|
|
|
1563
1710
|
argsHint: "<question>"
|
|
1564
1711
|
},
|
|
1565
1712
|
"search-engine": {
|
|
1566
|
-
description: "switch web search backend \u2014
|
|
1567
|
-
argsHint: "<
|
|
1713
|
+
description: "switch web search backend \u2014 bing (default, works from CN without proxy), searxng (self-hosted), metaso (free 100/d), tavily (free 1000/mo), perplexity (AI-native), or exa (AI-native)",
|
|
1714
|
+
argsHint: "<bing|searxng|metaso|tavily|perplexity|exa> [<key>]"
|
|
1568
1715
|
}
|
|
1569
1716
|
},
|
|
1570
1717
|
wizard: {
|
|
@@ -1727,6 +1874,8 @@ var EN = {
|
|
|
1727
1874
|
verboseOn: "\u25B8 verbose mode on \u2014 full reasoning + tool output",
|
|
1728
1875
|
verboseOff: "\u25B8 verbose mode off \u2014 head/tail elision restored",
|
|
1729
1876
|
commandFailed: "! command failed",
|
|
1877
|
+
steerInjected: "\u25B8 steering queued \u2014 will be added after the current step",
|
|
1878
|
+
steerCommandRejected: "\u25B8 commands are disabled while steering a busy turn",
|
|
1730
1879
|
btwUsage: "\u25B8 /btw <question> \u2014 ask a side question without polluting the conversation context.",
|
|
1731
1880
|
btwHeader: "\u226B btw",
|
|
1732
1881
|
btwFailed: "/btw failed",
|
|
@@ -1772,7 +1921,9 @@ var EN = {
|
|
|
1772
1921
|
editHistoryAlreadyReverted: "(already reverted \u2014 /history shows the batch-level status)",
|
|
1773
1922
|
editHistoryRevertFile: "/undo {id} {path} \u2192 revert just this file",
|
|
1774
1923
|
mcpFailed: "MCP {name} failed",
|
|
1775
|
-
mcpWarn: "MCP {name} warn"
|
|
1924
|
+
mcpWarn: "MCP {name} warn",
|
|
1925
|
+
unknownTheme: "unknown theme: {name}\navailable: {choices}",
|
|
1926
|
+
themeSaved: "theme saved: {name}\nactive on next launch: {active}"
|
|
1776
1927
|
},
|
|
1777
1928
|
hooks: {
|
|
1778
1929
|
head: "hook {tag} `{cmd}` {decision}{truncTag}",
|
|
@@ -1816,6 +1967,7 @@ var EN = {
|
|
|
1816
1967
|
balance402: "Out of balance (DeepSeek 402): {inner}. Top up at https://platform.deepseek.com/top_up \u2014 the panel header shows your balance once it's non-zero.",
|
|
1817
1968
|
badparam422: "Invalid parameter (DeepSeek 422): {inner}",
|
|
1818
1969
|
badrequest400: "Bad request (DeepSeek 400): {inner}",
|
|
1970
|
+
concurrency429: "DeepSeek concurrency limit hit (429): {inner}. The account has too many in-flight requests (cap: 500 for v4-pro, 2500 for v4-flash, summed across API keys account-wide). Usually means another Reasonix process is sharing the same key, or a parallel subagent fan-out overshot. Wait a few seconds and retry, reduce parallelism, or request a higher cap at https://platform.deepseek.com.",
|
|
1819
1971
|
deepseek5xxHead: "DeepSeek service unavailable ({status}) \u2014 this is a DeepSeek-side problem, not Reasonix. Already retried 4\xD7 with backoff.",
|
|
1820
1972
|
deepseek5xxReachable: " DeepSeek's main API answered our health check, but /chat/completions is failing \u2014 partial outage on their side.",
|
|
1821
1973
|
deepseek5xxUnreachable: " DeepSeek API is unreachable from your network \u2014 could be a wider DS outage or a local network issue.",
|
|
@@ -2101,6 +2253,9 @@ var EN = {
|
|
|
2101
2253
|
activeNone: "\u25B8 active plan: (none)",
|
|
2102
2254
|
noArchives: "no archived plans yet for this session \u2014 they auto-archive when every step is done",
|
|
2103
2255
|
archivedHeader: "Archived ({count}):",
|
|
2256
|
+
evidencePending: " ! evidence pending \u2014 current step needs verification/diff/checkpoint/manual evidence",
|
|
2257
|
+
evidenceLine: " evidence {stepId}: {summary}",
|
|
2258
|
+
archivedEvidenceLine: " evidence: {summary}",
|
|
2104
2259
|
replayNoSession: "no session attached \u2014 `/replay` is per-session. Run `reasonix code` in a project to get a session.",
|
|
2105
2260
|
replayNoArchives: "no archived plans yet for this session \u2014 `/replay` lights up once a plan completes (auto-archives when every step is done).",
|
|
2106
2261
|
replayInvalidIndex: "invalid index \u2014 `/replay` takes 1..{max} (newest = 1). Use `/plans` to see the list.",
|
|
@@ -2195,7 +2350,7 @@ var EN = {
|
|
|
2195
2350
|
currentEngine: "Current web search engine: {engine}",
|
|
2196
2351
|
endpoint: "SearXNG endpoint: {url}",
|
|
2197
2352
|
usageHeader: "Usage:",
|
|
2198
|
-
|
|
2353
|
+
usageBing: " /search-engine bing use Bing (default, works from CN without proxy)",
|
|
2199
2354
|
usageSearxng: " /search-engine searxng use SearXNG at default endpoint",
|
|
2200
2355
|
usageSearxngUrl: " /search-engine searxng <url> use SearXNG at custom endpoint",
|
|
2201
2356
|
usageMetaso: " /search-engine metaso use Metaso API (100/d free, configure your own API key for more)",
|
|
@@ -2291,7 +2446,9 @@ var EN = {
|
|
|
2291
2446
|
editorFailed: "external editor:",
|
|
2292
2447
|
editorMissing: "no $EDITOR / $VISUAL / $GIT_EDITOR set \u2014 export one (e.g. `export EDITOR=nano`) and retry",
|
|
2293
2448
|
editorExited: "editor exited with code {code}",
|
|
2294
|
-
typeaheadStaged: "\u25B8 {count} line(s) staged \xB7 esc recall"
|
|
2449
|
+
typeaheadStaged: "\u25B8 {count} line(s) staged \xB7 esc recall",
|
|
2450
|
+
steerPlaceholder: "type to steer the current task \u2014 commands are disabled while busy",
|
|
2451
|
+
steerHint: "send \u2014 injected mid-turn"
|
|
2295
2452
|
},
|
|
2296
2453
|
pathConfirm: {
|
|
2297
2454
|
title: "Outside-sandbox path",
|
|
@@ -2310,7 +2467,13 @@ var EN = {
|
|
|
2310
2467
|
denyDesc: "press Tab to add context telling the model why",
|
|
2311
2468
|
pathLabel: "path",
|
|
2312
2469
|
sandboxLabel: "sandbox",
|
|
2313
|
-
allowPrefixLabel: "prefix"
|
|
2470
|
+
allowPrefixLabel: "prefix",
|
|
2471
|
+
promptTitleRead: "Access path \u2014 read",
|
|
2472
|
+
promptTitleWrite: "Access path \u2014 write",
|
|
2473
|
+
actionAllowRead: "Allow read",
|
|
2474
|
+
actionAllowWrite: "Allow write",
|
|
2475
|
+
actionAlwaysAllow: "Always allow \u2014 {prefix}",
|
|
2476
|
+
actionDeny: "Deny"
|
|
2314
2477
|
},
|
|
2315
2478
|
shellConfirm: {
|
|
2316
2479
|
title: "Shell command",
|
|
@@ -2332,7 +2495,12 @@ var EN = {
|
|
|
2332
2495
|
timeoutLabel: "timeout",
|
|
2333
2496
|
waitLabel: "wait",
|
|
2334
2497
|
previewMore: "\u2026 {n} more line hidden \u2014 press esc, ask the model to split it",
|
|
2335
|
-
previewMorePlural: "\u2026 {n} more lines hidden \u2014 press esc, ask the model to split it"
|
|
2498
|
+
previewMorePlural: "\u2026 {n} more lines hidden \u2014 press esc, ask the model to split it",
|
|
2499
|
+
promptTitleRunCommand: "Run command",
|
|
2500
|
+
promptTitleRunBackground: "Run background command",
|
|
2501
|
+
actionRunOnce: "Run once",
|
|
2502
|
+
actionAlwaysAllow: "Always allow \u2014 {prefix}",
|
|
2503
|
+
actionDeny: "Deny"
|
|
2336
2504
|
},
|
|
2337
2505
|
editConfirm: {
|
|
2338
2506
|
footer: "[y/Enter] apply \xB7 [n] reject with reason \xB7 [a] apply rest \xB7 [A] flip AUTO \xB7 [\u2191\u2193/Space] scroll \xB7 [Esc] abort",
|
|
@@ -2473,37 +2641,37 @@ var EN = {
|
|
|
2473
2641
|
probeFailed: "probe failed \u2014 {message}"
|
|
2474
2642
|
},
|
|
2475
2643
|
webErrors: {
|
|
2476
|
-
status: "web_search {status} \u2014 try: the search backend returned an error; rephrase the query, or switch engine with /search-engine
|
|
2644
|
+
status: "web_search {status} \u2014 try: the search backend returned an error; rephrase the query, or switch engine with /search-engine bing|searxng|metaso|tavily|perplexity|exa",
|
|
2477
2645
|
rateLimit429: "web_search 429 \u2014 try: wait 10s before retrying, or rephrase the query; the search backend is rate-limiting this client",
|
|
2478
|
-
forbidden403: "web_search 403 \u2014 try: the search backend is blocking this client; switch engine with /search-engine
|
|
2646
|
+
forbidden403: "web_search 403 \u2014 try: the search backend is blocking this client; switch engine with /search-engine bing|searxng|metaso|tavily|perplexity|exa, or wait and retry later",
|
|
2479
2647
|
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",
|
|
2480
|
-
|
|
2481
|
-
|
|
2648
|
+
bingBlocked: "web_search: Bing anti-bot page \u2014 rate-limited or blocked \u2014 try: wait 30s and retry, or switch engine with /search-engine bing|searxng|metaso|tavily|perplexity|exa",
|
|
2649
|
+
bingNoResults: "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 bing|searxng|metaso|tavily|perplexity|exa",
|
|
2482
2650
|
invalidEndpoint: 'web_search: invalid SearXNG endpoint "{endpoint}" \u2014 try: set a valid URL with /search-endpoint http://host:port',
|
|
2483
2651
|
endpointMustBeHttp: "web_search: SearXNG endpoint must be http(s), got {protocol} \u2014 try: set a valid URL with /search-endpoint http://host:port",
|
|
2484
|
-
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
|
|
2485
|
-
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
|
|
2652
|
+
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 bing|searxng|metaso|tavily|perplexity|exa",
|
|
2653
|
+
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 bing|searxng|metaso|tavily|perplexity|exa",
|
|
2486
2654
|
metasoMissingKey: "web_search: Metaso requires an API key \u2014 set METASO_API_KEY or configure one with /search-engine metaso <key>. Get one at https://metaso.cn/search-api/playground",
|
|
2487
2655
|
metasoDailyLimit: "web_search: Metaso daily search limit reached \u2014 set METASO_API_KEY or get a key at https://metaso.cn/search-api/playground",
|
|
2488
2656
|
metasoUnauthorized: "web_search: Metaso API key rejected \u2014 check METASO_API_KEY or get one at https://metaso.cn/search-api/playground",
|
|
2489
2657
|
metasoRateLimit: "web_search: Metaso rate-limited \u2014 wait and retry, or get your own API key at https://metaso.cn/search-api/playground",
|
|
2490
|
-
metasoServerError: "web_search: Metaso server error ({status}) \u2014 try again later, or switch engine with /search-engine
|
|
2658
|
+
metasoServerError: "web_search: Metaso server error ({status}) \u2014 try again later, or switch engine with /search-engine bing|searxng|metaso|tavily|perplexity|exa",
|
|
2491
2659
|
metasoParseError: "web_search: Metaso returned unparseable response (HTTP {status}) \u2014 try again later",
|
|
2492
2660
|
metasoApiError: "web_search: Metaso API error (code {code}: {message}) \u2014 try again later",
|
|
2493
2661
|
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",
|
|
2494
2662
|
tavilyUnauthorized: "web_search: Tavily API key rejected \u2014 check TAVILY_API_KEY or get one at https://tavily.com",
|
|
2495
|
-
tavilyRateLimit: "web_search: Tavily rate-limited or monthly quota exceeded \u2014 wait, switch engine with /search-engine
|
|
2496
|
-
tavilyServerError: "web_search: Tavily server error ({status}) \u2014 try again later, or switch engine with /search-engine
|
|
2663
|
+
tavilyRateLimit: "web_search: Tavily rate-limited or monthly quota exceeded \u2014 wait, switch engine with /search-engine bing|searxng|metaso|tavily|perplexity|exa, or upgrade your Tavily plan",
|
|
2664
|
+
tavilyServerError: "web_search: Tavily server error ({status}) \u2014 try again later, or switch engine with /search-engine bing|searxng|metaso|tavily|perplexity|exa",
|
|
2497
2665
|
tavilyParseError: "web_search: Tavily returned unparseable response (HTTP {status}) \u2014 try again later",
|
|
2498
2666
|
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",
|
|
2499
2667
|
perplexityUnauthorized: "web_search: Perplexity API key rejected \u2014 check PERPLEXITY_API_KEY or get one at https://perplexity.ai/settings/api",
|
|
2500
|
-
perplexityRateLimit: "web_search: Perplexity rate-limited \u2014 wait and retry, or switch engine with /search-engine
|
|
2501
|
-
perplexityServerError: "web_search: Perplexity server error ({status}) \u2014 try again later, or switch engine with /search-engine
|
|
2668
|
+
perplexityRateLimit: "web_search: Perplexity rate-limited \u2014 wait and retry, or switch engine with /search-engine bing|searxng|metaso|tavily|perplexity|exa",
|
|
2669
|
+
perplexityServerError: "web_search: Perplexity server error ({status}) \u2014 try again later, or switch engine with /search-engine bing|searxng|metaso|tavily|perplexity|exa",
|
|
2502
2670
|
perplexityParseError: "web_search: Perplexity returned unparseable response (HTTP {status}) \u2014 try again later",
|
|
2503
2671
|
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",
|
|
2504
2672
|
exaUnauthorized: "web_search: Exa API key rejected \u2014 check EXA_API_KEY or get one at https://exa.ai",
|
|
2505
2673
|
exaRateLimit: "web_search: Exa API rate-limited or monthly quota exceeded \u2014 wait or upgrade at https://exa.ai/pricing",
|
|
2506
|
-
exaServerError: "web_search: Exa server error ({status}) \u2014 try again later, or switch engine with /search-engine
|
|
2674
|
+
exaServerError: "web_search: Exa server error ({status}) \u2014 try again later, or switch engine with /search-engine bing|searxng|metaso|tavily|perplexity|exa",
|
|
2507
2675
|
exaParseError: "web_search: Exa returned unparseable response (HTTP {status}) \u2014 try again later",
|
|
2508
2676
|
fetchStatus: "web_fetch {status} for {url} \u2014 try: confirm the URL resolves in a browser; status suggests the host returned an error page",
|
|
2509
2677
|
fetchRateLimit429: "web_fetch 429 for {url} \u2014 try: wait 10s before retrying; the host is rate-limiting this client",
|
|
@@ -2595,19 +2763,6 @@ var EN = {
|
|
|
2595
2763
|
categoryProject: "project",
|
|
2596
2764
|
categoryReference: "reference"
|
|
2597
2765
|
},
|
|
2598
|
-
copyMode: {
|
|
2599
|
-
title: "\u2500\u2500 COPY MODE \u2500\u2500",
|
|
2600
|
-
help: "j/k or \u2191/\u2193 move \xB7 v select \xB7 y yank \xB7 g/G top/bottom \xB7 q quit",
|
|
2601
|
-
statusBar: "line {cur}/{total} \xB7 selection: {sel}",
|
|
2602
|
-
statusYanked: "yanked {size} chars (osc52={osc52})",
|
|
2603
|
-
statusEmpty: "nothing selected",
|
|
2604
|
-
empty: "(no chat content yet \u2014 say something to the model first)",
|
|
2605
|
-
labelUser: "you",
|
|
2606
|
-
labelAssistant: "assistant",
|
|
2607
|
-
labelReasoning: "reasoning",
|
|
2608
|
-
yankedToast: "\u25B8 copied {size} chars to clipboard (osc52)",
|
|
2609
|
-
yankedToastFile: "\u25B8 copied {size} chars \xB7 file: {path}"
|
|
2610
|
-
},
|
|
2611
2766
|
mcpHealth: {
|
|
2612
2767
|
noData: "no inspect data",
|
|
2613
2768
|
healthy: "healthy \xB7 {ms}ms",
|
|
@@ -2759,9 +2914,11 @@ var EN = {
|
|
|
2759
2914
|
groupSystem: "System",
|
|
2760
2915
|
descEnter: "Send message",
|
|
2761
2916
|
descShiftEnter: "New line",
|
|
2917
|
+
descCtrlEnter: "New line",
|
|
2918
|
+
descCtrlJ: "New line",
|
|
2762
2919
|
descCtrlU: "Clear input",
|
|
2763
2920
|
descCtrlW: "Delete word",
|
|
2764
|
-
descCtrlP: "
|
|
2921
|
+
descCtrlP: "Show/hide shortcuts",
|
|
2765
2922
|
descCtrlX: "Open in editor",
|
|
2766
2923
|
descArrows: "Input history",
|
|
2767
2924
|
descPgUpDown: "Scroll page",
|
|
@@ -2775,9 +2932,30 @@ var EN = {
|
|
|
2775
2932
|
descCtrlC: "Quit",
|
|
2776
2933
|
descEsc: "Stop / Cancel",
|
|
2777
2934
|
descCtrlR: "Toggle verbose",
|
|
2778
|
-
descCtrlO: "Expand
|
|
2935
|
+
descCtrlO: "Expand reply (streaming only)",
|
|
2779
2936
|
descHelp: "Show all commands",
|
|
2780
2937
|
descShiftTab: "Switch edit mode"
|
|
2938
|
+
},
|
|
2939
|
+
mcpCli: {
|
|
2940
|
+
bundledCatalog: "Bundled MCP servers (offline catalog):",
|
|
2941
|
+
justFetched: "just fetched",
|
|
2942
|
+
cachedAge: "cached, {age}",
|
|
2943
|
+
moreAvailable: "more available",
|
|
2944
|
+
allLoaded: "all loaded",
|
|
2945
|
+
morePagesAvailable: "\u25B8 more pages available \u2014 `reasonix mcp list --pages <n>` or --all",
|
|
2946
|
+
installHint: "Install: reasonix mcp install <name>",
|
|
2947
|
+
usageSearch: "usage: reasonix mcp search <query>",
|
|
2948
|
+
usageInstall: "usage: reasonix mcp install <name>",
|
|
2949
|
+
noMatchesFor: 'No matches for "{q}" across {count} loaded entries ({source})',
|
|
2950
|
+
matchCount: '{count} match(es) for "{q}" in {source} registry ({loaded} entries scanned):',
|
|
2951
|
+
moreLoaded: "\u2026 {count} more loaded \u2014 use `reasonix mcp search <query>` to filter",
|
|
2952
|
+
moreMatches: "\u2026 {count} more matches",
|
|
2953
|
+
installed: "Installed: {spec}",
|
|
2954
|
+
noServerFound: 'No MCP server named "{target}" found after walking {pages} page(s) of the {source} registry.',
|
|
2955
|
+
noServerTryMore: "Try: reasonix mcp install {target} --max-pages 100",
|
|
2956
|
+
noInstallMeta: 'Could not derive install metadata for "{name}" \u2014 try `npx -y @smithery/cli install {name}` directly.',
|
|
2957
|
+
buildSpecFailed: "Cannot build install spec for {name}: {message}",
|
|
2958
|
+
alreadyInstalled: "Already installed: {spec}"
|
|
2781
2959
|
}
|
|
2782
2960
|
};
|
|
2783
2961
|
|
|
@@ -2824,7 +3002,17 @@ var zhCN = {
|
|
|
2824
3002
|
missingApiKey: "\u672A\u8BBE\u7F6E DEEPSEEK_API_KEY \u4E14\u6807\u51C6\u8F93\u5165\u4E0D\u662F TTY\uFF08\u65E0\u6CD5\u4EA4\u4E92\u5F0F\u8F93\u5165\uFF09\u3002\n\u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF\uFF0C\u6216\u5148\u8FD0\u884C `reasonix chat` \u4EA4\u4E92\u4E00\u6B21\u4EE5\u4FDD\u5B58\u5BC6\u94A5\u3002\n"
|
|
2825
3003
|
},
|
|
2826
3004
|
sessions: {
|
|
2827
|
-
emptyHint: "\u6682\u65E0\u5DF2\u4FDD\u5B58\u7684\u4F1A\u8BDD \u2014 \u8FD0\u884C `reasonix chat`\uFF08\u4F1A\u8BDD\u4F1A\u81EA\u52A8\u4FDD\u5B58\uFF0C\u9664\u975E\u4F7F\u7528\u4E86 --no-session\uFF09\u3002"
|
|
3005
|
+
emptyHint: "\u6682\u65E0\u5DF2\u4FDD\u5B58\u7684\u4F1A\u8BDD \u2014 \u8FD0\u884C `reasonix chat`\uFF08\u4F1A\u8BDD\u4F1A\u81EA\u52A8\u4FDD\u5B58\uFF0C\u9664\u975E\u4F7F\u7528\u4E86 --no-session\uFF09\u3002",
|
|
3006
|
+
listHeader: "\u4FDD\u5B58\u7684\u4F1A\u8BDD (~/.reasonix/sessions/)\uFF1A",
|
|
3007
|
+
inspectHint: "\u67E5\u770B\uFF1Areasonix sessions <name>",
|
|
3008
|
+
resumeHint: "\u6062\u590D\uFF1Areasonix chat --session <name>",
|
|
3009
|
+
noSession: '\u627E\u4E0D\u5230\u4F1A\u8BDD "{name}"\uFF08\u6216\u4E3A\u7A7A\uFF09\u3002',
|
|
3010
|
+
lookedAt: "\u4F4D\u7F6E\uFF1A{path}",
|
|
3011
|
+
noIdleSessions: "\u6CA1\u6709\u95F2\u7F6E \u2265{days} \u5929\u7684\u4F1A\u8BDD\u3002\u65E0\u9700\u6E05\u7406\u3002",
|
|
3012
|
+
wouldPrune: "\u5C06\u6E05\u7406 {count} \u4E2A\u95F2\u7F6E \u2265{days} \u5929\u7684\u4F1A\u8BDD\uFF1A",
|
|
3013
|
+
dryRunHint: "\u53BB\u6389 --dry-run \u53EF\u5B9E\u9645\u6267\u884C\u5220\u9664\u3002",
|
|
3014
|
+
prunedCount: "\u5DF2\u6E05\u7406 {count} \u4E2A\u95F2\u7F6E \u2265{days} \u5929\u7684\u4F1A\u8BDD\uFF1A",
|
|
3015
|
+
daysInvalid: "--days \u5FC5\u987B\u662F\u6B63\u6574\u6570\uFF08\u4F20\u5165\u4E86 {days}\uFF09\u3002"
|
|
2828
3016
|
},
|
|
2829
3017
|
ui: {
|
|
2830
3018
|
welcome: "\u968F\u65F6\u8FD0\u884C `reasonix` \u5F00\u59CB\u804A\u5929 \u2014 \u60A8\u7684\u8BBE\u7F6E\u5C06\u88AB\u8BB0\u4F4F\u3002",
|
|
@@ -2922,10 +3110,6 @@ var zhCN = {
|
|
|
2922
3110
|
title: "\u590D\u5236 / \u7C98\u8D34",
|
|
2923
3111
|
rows: [
|
|
2924
3112
|
{ key: "\u9009\u4E2D\u6587\u5B57", text: "\u76F4\u63A5\u62D6\u52A8 \u2014 \u7EC8\u7AEF\u539F\u751F\uFF08\u4E0D\u9700\u8981\u4EFB\u4F55\u4FEE\u9970\u952E\uFF09" },
|
|
2925
|
-
{
|
|
2926
|
-
key: "/copy",
|
|
2927
|
-
text: "vim/tmux \u98CE\u683C\u590D\u5236\u6A21\u5F0F \u2014 SSH / mosh / tmux \u4E0B\u62D6\u9009\u8D8A\u8FC7\u53EF\u89C6\u533A\u65E0\u6548\u65F6\u7528\u8FD9\u4E2A"
|
|
2928
|
-
},
|
|
2929
3113
|
{
|
|
2930
3114
|
key: "\u590D\u5236",
|
|
2931
3115
|
text: "Ctrl+Shift+C\uFF08Win/Linux\uFF09\xB7 Cmd+C\uFF08macOS\uFF09\u2014 \u6216\u9009\u4E2D\u5373\u590D\u5236\uFF08\u770B\u7EC8\u7AEF\u8BBE\u7F6E\uFF09"
|
|
@@ -3016,9 +3200,6 @@ var zhCN = {
|
|
|
3016
3200
|
},
|
|
3017
3201
|
slash: {
|
|
3018
3202
|
help: { description: "\u663E\u793A\u5B8C\u6574\u547D\u4EE4\u53C2\u8003" },
|
|
3019
|
-
copy: {
|
|
3020
|
-
description: "\u8FDB\u5165 vim/tmux \u98CE\u683C\u590D\u5236\u6A21\u5F0F \u2014 j/k \u79FB\u52A8\u3001v \u8D77\u9009\u533A\u3001y \u590D\u5236\u5230\u526A\u8D34\u677F"
|
|
3021
|
-
},
|
|
3022
3203
|
status: { description: "\u5F53\u524D\u6A21\u578B\u3001\u6807\u5FD7\u3001\u4E0A\u4E0B\u6587\u3001\u4F1A\u8BDD" },
|
|
3023
3204
|
preset: {
|
|
3024
3205
|
description: "\u6A21\u578B\u7EC4\u5408 \u2014 \u81EA\u52A8\u5728 flash \u2192 pro \u4E4B\u95F4\u5207\u6362\uFF0C\u6216\u9501\u5B9A flash/pro",
|
|
@@ -3174,8 +3355,8 @@ var zhCN = {
|
|
|
3174
3355
|
argsHint: "<question>"
|
|
3175
3356
|
},
|
|
3176
3357
|
"search-engine": {
|
|
3177
|
-
description: "\u5207\u6362\u7F51\u7EDC\u641C\u7D22\u540E\u7AEF \u2014
|
|
3178
|
-
argsHint: "<
|
|
3358
|
+
description: "\u5207\u6362\u7F51\u7EDC\u641C\u7D22\u540E\u7AEF \u2014 bing\uFF08\u9ED8\u8BA4\uFF0C\u56FD\u5185\u88F8 IP \u76F4\u8FDE\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",
|
|
3359
|
+
argsHint: "<bing|searxng|metaso|tavily|perplexity|exa> [<key>]"
|
|
3179
3360
|
}
|
|
3180
3361
|
},
|
|
3181
3362
|
wizard: {
|
|
@@ -3338,6 +3519,8 @@ var zhCN = {
|
|
|
3338
3519
|
verboseOn: "\u25B8 \u8BE6\u7EC6\u6A21\u5F0F\u5DF2\u5F00 \u2014 \u663E\u793A\u5B8C\u6574\u63A8\u7406 + \u5DE5\u5177\u8F93\u51FA",
|
|
3339
3520
|
verboseOff: "\u25B8 \u8BE6\u7EC6\u6A21\u5F0F\u5DF2\u5173 \u2014 \u6062\u590D\u5934\u5C3E\u7701\u7565",
|
|
3340
3521
|
commandFailed: "! \u547D\u4EE4\u5931\u8D25",
|
|
3522
|
+
steerInjected: "\u25B8 \u5DF2\u52A0\u5165\u5F15\u5BFC\u961F\u5217 \u2014 \u5C06\u5728\u5F53\u524D\u6B65\u9AA4\u540E\u6CE8\u5165",
|
|
3523
|
+
steerCommandRejected: "\u25B8 \u5F53\u524D\u8F6E\u6B21\u5FD9\u788C\u65F6\u4E0D\u80FD\u63D0\u4EA4\u547D\u4EE4\uFF0C\u53EA\u80FD\u8F93\u5165\u666E\u901A\u5F15\u5BFC\u6D88\u606F",
|
|
3341
3524
|
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",
|
|
3342
3525
|
btwHeader: "\u226B btw",
|
|
3343
3526
|
btwFailed: "/btw \u8C03\u7528\u5931\u8D25",
|
|
@@ -3383,7 +3566,9 @@ var zhCN = {
|
|
|
3383
3566
|
editHistoryAlreadyReverted: "\uFF08\u5DF2\u64A4\u9500 \u2014 /history \u663E\u793A\u6279\u6B21\u7EA7\u72B6\u6001\uFF09",
|
|
3384
3567
|
editHistoryRevertFile: "/undo {id} {path} \u2192 \u4EC5\u8FD8\u539F\u6B64\u6587\u4EF6",
|
|
3385
3568
|
mcpFailed: "MCP {name} \u5931\u8D25",
|
|
3386
|
-
mcpWarn: "MCP {name} \u8B66\u544A"
|
|
3569
|
+
mcpWarn: "MCP {name} \u8B66\u544A",
|
|
3570
|
+
unknownTheme: "\u672A\u77E5\u4E3B\u9898\uFF1A{name}\n\u53EF\u7528\u4E3B\u9898\uFF1A{choices}",
|
|
3571
|
+
themeSaved: "\u4E3B\u9898\u5DF2\u4FDD\u5B58\uFF1A{name}\n\u4E0B\u6B21\u542F\u52A8\u751F\u6548\uFF1A{active}"
|
|
3387
3572
|
},
|
|
3388
3573
|
hooks: {
|
|
3389
3574
|
head: "\u94A9\u5B50 {tag} `{cmd}` {decision}{truncTag}",
|
|
@@ -3427,6 +3612,7 @@ var zhCN = {
|
|
|
3427
3612
|
balance402: "\u4F59\u989D\u4E0D\u8DB3\uFF08DeepSeek 402\uFF09\uFF1A{inner}\u3002\u5728 https://platform.deepseek.com/top_up \u5145\u503C \u2014 \u4F59\u989D\u975E\u96F6\u65F6\u9762\u677F\u9876\u680F\u4F1A\u663E\u793A\u3002",
|
|
3428
3613
|
badparam422: "\u53C2\u6570\u9519\u8BEF\uFF08DeepSeek 422\uFF09\uFF1A{inner}",
|
|
3429
3614
|
badrequest400: "\u8BF7\u6C42\u9519\u8BEF\uFF08DeepSeek 400\uFF09\uFF1A{inner}",
|
|
3615
|
+
concurrency429: "DeepSeek \u5E76\u53D1\u8D85\u9650\uFF08429\uFF09\uFF1A{inner}\u3002\u8D26\u53F7\u5728\u8DD1\u7684\u8BF7\u6C42\u8D85\u8FC7\u4E0A\u9650\uFF08v4-pro 500\u3001v4-flash 2500\uFF0C\u8D26\u53F7\u4E0B\u6240\u6709 API key \u7D2F\u52A0\uFF09\u3002\u901A\u5E38\u662F\u540C\u4E00\u8D26\u53F7\u5F00\u4E86\u591A\u4E2A Reasonix \u8FDB\u7A0B\uFF0C\u6216\u8005\u5E76\u884C subagent \u4E00\u6B21\u53D1\u592A\u591A\u3002\u7B49\u51E0\u79D2\u91CD\u8BD5\u3001\u51CF\u5C11\u5E76\u884C\uFF0C\u6216\u5728 https://platform.deepseek.com \u7533\u8BF7\u6269\u5BB9\u3002",
|
|
3430
3616
|
deepseek5xxHead: "DeepSeek \u670D\u52A1\u4E0D\u53EF\u7528\uFF08{status}\uFF09 \u2014 \u8FD9\u662F DeepSeek \u670D\u52A1\u7AEF\u95EE\u9898\uFF0C\u4E0D\u662F Reasonix \u6545\u969C\u3002\u5DF2\u6309\u6307\u6570\u9000\u907F\u91CD\u8BD5 4 \u6B21\u3002",
|
|
3431
3617
|
deepseek5xxReachable: " DeepSeek \u4E3B API \u5065\u5EB7\u68C0\u67E5\u901A\u8FC7\uFF0C\u4F46 /chat/completions \u5728\u6302 \u2014 \u4ED6\u4EEC\u90A3\u8FB9\u90E8\u5206\u670D\u52A1\u5F02\u5E38\u3002",
|
|
3432
3618
|
deepseek5xxUnreachable: " \u65E0\u6CD5\u4ECE\u4F60\u7684\u7F51\u7EDC\u8BBF\u95EE DeepSeek API \u2014 \u53EF\u80FD\u662F DS \u6574\u4F53\u6545\u969C\uFF0C\u4E5F\u53EF\u80FD\u662F\u672C\u5730\u7F51\u7EDC\u95EE\u9898\u3002",
|
|
@@ -3712,6 +3898,9 @@ var zhCN = {
|
|
|
3712
3898
|
activeNone: "\u25B8 \u6D3B\u8DC3\u8BA1\u5212\uFF1A\uFF08\u65E0\uFF09",
|
|
3713
3899
|
noArchives: "\u6B64\u4F1A\u8BDD\u5C1A\u65E0\u5F52\u6863\u8BA1\u5212 \u2014 \u5F53\u6BCF\u4E2A\u6B65\u9AA4\u5B8C\u6210\u65F6\u81EA\u52A8\u5F52\u6863",
|
|
3714
3900
|
archivedHeader: "\u5DF2\u5F52\u6863\uFF08{count}\uFF09\uFF1A",
|
|
3901
|
+
evidencePending: " ! \u7B49\u5F85 evidence \u2014 \u5F53\u524D\u6B65\u9AA4\u9700\u8981 verification/diff/checkpoint/manual evidence",
|
|
3902
|
+
evidenceLine: " evidence {stepId}: {summary}",
|
|
3903
|
+
archivedEvidenceLine: " evidence: {summary}",
|
|
3715
3904
|
replayNoSession: "\u672A\u9644\u52A0\u4F1A\u8BDD \u2014 `/replay` \u662F\u6309\u4F1A\u8BDD\u7684\u3002\u5728\u9879\u76EE\u4E2D\u8FD0\u884C `reasonix code` \u4EE5\u83B7\u53D6\u4F1A\u8BDD\u3002",
|
|
3716
3905
|
replayNoArchives: "\u6B64\u4F1A\u8BDD\u5C1A\u65E0\u5F52\u6863\u8BA1\u5212 \u2014 `/replay` \u5728\u8BA1\u5212\u5B8C\u6210\u540E\u542F\u7528\uFF08\u6BCF\u4E2A\u6B65\u9AA4\u5B8C\u6210\u65F6\u81EA\u52A8\u5F52\u6863\uFF09\u3002",
|
|
3717
3906
|
replayInvalidIndex: "\u65E0\u6548\u7D22\u5F15 \u2014 `/replay` \u63A5\u53D7 1..{max}\uFF08\u6700\u65B0 = 1\uFF09\u3002\u4F7F\u7528 `/plans` \u67E5\u770B\u5217\u8868\u3002",
|
|
@@ -3806,7 +3995,7 @@ var zhCN = {
|
|
|
3806
3995
|
currentEngine: "\u5F53\u524D\u7F51\u9875\u641C\u7D22\u5F15\u64CE\uFF1A{engine}",
|
|
3807
3996
|
endpoint: "SearXNG \u7AEF\u70B9\uFF1A{url}",
|
|
3808
3997
|
usageHeader: "\u7528\u6CD5\uFF1A",
|
|
3809
|
-
|
|
3998
|
+
usageBing: " /search-engine bing \u4F7F\u7528 Bing\uFF08\u9ED8\u8BA4\uFF0C\u56FD\u5185\u88F8 IP \u76F4\u8FDE\uFF0C\u65E0\u9700\u4EE3\u7406\uFF09",
|
|
3810
3999
|
usageSearxng: " /search-engine searxng \u4F7F\u7528 SearXNG \u9ED8\u8BA4\u7AEF\u70B9",
|
|
3811
4000
|
usageSearxngUrl: " /search-engine searxng <url> \u4F7F\u7528 SearXNG \u81EA\u5B9A\u4E49\u7AEF\u70B9",
|
|
3812
4001
|
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",
|
|
@@ -3902,7 +4091,9 @@ var zhCN = {
|
|
|
3902
4091
|
editorFailed: "\u5916\u90E8\u7F16\u8F91\u5668\uFF1A",
|
|
3903
4092
|
editorMissing: "\u672A\u8BBE\u7F6E $EDITOR / $VISUAL / $GIT_EDITOR \u2014 \u8BF7\u5BFC\u51FA\u73AF\u5883\u53D8\u91CF\uFF08\u4F8B\u5982 `export EDITOR=nano`\uFF09\u540E\u91CD\u8BD5",
|
|
3904
4093
|
editorExited: "\u7F16\u8F91\u5668\u5F02\u5E38\u9000\u51FA\uFF0C\u8FD4\u56DE\u7801 {code}",
|
|
3905
|
-
typeaheadStaged: "\u25B8 {count} \u884C\u5DF2\u6682\u5B58 \xB7 esc \u53EC\u56DE"
|
|
4094
|
+
typeaheadStaged: "\u25B8 {count} \u884C\u5DF2\u6682\u5B58 \xB7 esc \u53EC\u56DE",
|
|
4095
|
+
steerPlaceholder: "\u8F93\u5165\u6D88\u606F\u4EE5\u5F15\u5BFC\u5F53\u524D\u4EFB\u52A1 \u2014 \u5FD9\u788C\u65F6\u4E0D\u652F\u6301\u547D\u4EE4",
|
|
4096
|
+
steerHint: "\u53D1\u9001 \u2014 \u56DE\u5408\u5185\u6CE8\u5165"
|
|
3906
4097
|
},
|
|
3907
4098
|
pathConfirm: {
|
|
3908
4099
|
title: "\u6C99\u7BB1\u5916\u8DEF\u5F84",
|
|
@@ -3921,7 +4112,13 @@ var zhCN = {
|
|
|
3921
4112
|
denyDesc: "\u6309 Tab \u6DFB\u52A0\u8BF4\u660E\uFF0C\u544A\u8BC9\u6A21\u578B\u539F\u56E0",
|
|
3922
4113
|
pathLabel: "\u8DEF\u5F84",
|
|
3923
4114
|
sandboxLabel: "\u6C99\u7BB1",
|
|
3924
|
-
allowPrefixLabel: "\u524D\u7F00"
|
|
4115
|
+
allowPrefixLabel: "\u524D\u7F00",
|
|
4116
|
+
promptTitleRead: "\u8BBF\u95EE\u8DEF\u5F84 \u2014 \u8BFB\u53D6",
|
|
4117
|
+
promptTitleWrite: "\u8BBF\u95EE\u8DEF\u5F84 \u2014 \u5199\u5165",
|
|
4118
|
+
actionAllowRead: "\u5141\u8BB8\u8BFB\u53D6",
|
|
4119
|
+
actionAllowWrite: "\u5141\u8BB8\u5199\u5165",
|
|
4120
|
+
actionAlwaysAllow: "\u59CB\u7EC8\u5141\u8BB8 \u2014 {prefix}",
|
|
4121
|
+
actionDeny: "\u62D2\u7EDD"
|
|
3925
4122
|
},
|
|
3926
4123
|
shellConfirm: {
|
|
3927
4124
|
title: "Shell \u547D\u4EE4",
|
|
@@ -3943,7 +4140,12 @@ var zhCN = {
|
|
|
3943
4140
|
timeoutLabel: "\u8D85\u65F6",
|
|
3944
4141
|
waitLabel: "\u7B49\u5F85",
|
|
3945
4142
|
previewMore: "\u2026 \u8FD8\u6709 {n} \u884C\u672A\u663E\u793A \u2014 \u6309 esc \u53D6\u6D88\uFF0C\u8BA9\u6A21\u578B\u62C6\u5206\u540E\u518D\u8BD5",
|
|
3946
|
-
previewMorePlural: "\u2026 \u8FD8\u6709 {n} \u884C\u672A\u663E\u793A \u2014 \u6309 esc \u53D6\u6D88\uFF0C\u8BA9\u6A21\u578B\u62C6\u5206\u540E\u518D\u8BD5"
|
|
4143
|
+
previewMorePlural: "\u2026 \u8FD8\u6709 {n} \u884C\u672A\u663E\u793A \u2014 \u6309 esc \u53D6\u6D88\uFF0C\u8BA9\u6A21\u578B\u62C6\u5206\u540E\u518D\u8BD5",
|
|
4144
|
+
promptTitleRunCommand: "\u8FD0\u884C\u547D\u4EE4",
|
|
4145
|
+
promptTitleRunBackground: "\u8FD0\u884C\u540E\u53F0\u547D\u4EE4",
|
|
4146
|
+
actionRunOnce: "\u8FD0\u884C\u4E00\u6B21",
|
|
4147
|
+
actionAlwaysAllow: "\u59CB\u7EC8\u5141\u8BB8 \u2014 {prefix}",
|
|
4148
|
+
actionDeny: "\u62D2\u7EDD"
|
|
3947
4149
|
},
|
|
3948
4150
|
editConfirm: {
|
|
3949
4151
|
footer: "[y/Enter] \u5E94\u7528 \xB7 [n] \u62D2\u7EDD\u5E76\u8BF4\u660E \xB7 [a] \u5E94\u7528\u5269\u4F59 \xB7 [A] \u5207\u6362 AUTO \xB7 [\u2191\u2193/Space] \u6EDA\u52A8 \xB7 [Esc] \u4E2D\u6B62",
|
|
@@ -4084,37 +4286,37 @@ var zhCN = {
|
|
|
4084
4286
|
probeFailed: "\u63A2\u6D4B\u5931\u8D25 \u2014 {message}"
|
|
4085
4287
|
},
|
|
4086
4288
|
webErrors: {
|
|
4087
|
-
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
|
|
4289
|
+
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 bing|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
4088
4290
|
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",
|
|
4089
|
-
forbidden403: "web_search 403 \u2014 try: \u641C\u7D22\u540E\u7AEF\u62D2\u7EDD\u8BE5\u5BA2\u6237\u7AEF\u8BBF\u95EE\uFF1B\u4F7F\u7528 /search-engine
|
|
4291
|
+
forbidden403: "web_search 403 \u2014 try: \u641C\u7D22\u540E\u7AEF\u62D2\u7EDD\u8BE5\u5BA2\u6237\u7AEF\u8BBF\u95EE\uFF1B\u4F7F\u7528 /search-engine bing|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE\uFF0C\u6216\u7A0D\u540E\u91CD\u8BD5",
|
|
4090
4292
|
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",
|
|
4091
|
-
|
|
4092
|
-
|
|
4293
|
+
bingBlocked: "web_search: Bing \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 bing|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
4294
|
+
bingNoResults: "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 bing|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
4093
4295
|
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',
|
|
4094
4296
|
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",
|
|
4095
|
-
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
|
|
4096
|
-
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
|
|
4297
|
+
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 bing|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
4298
|
+
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 bing|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
4097
4299
|
metasoMissingKey: "web_search: Metaso \u9700\u8981 API \u5BC6\u94A5 \u2014 \u8BBE\u7F6E METASO_API_KEY\uFF0C\u6216\u4F7F\u7528 /search-engine metaso <key> \u914D\u7F6E\uFF1B\u53EF\u5728 https://metaso.cn/search-api/playground \u83B7\u53D6\u5BC6\u94A5",
|
|
4098
4300
|
metasoDailyLimit: "web_search: Metaso \u6BCF\u65E5\u641C\u7D22\u6B21\u6570\u5DF2\u8FBE\u4E0A\u9650 \u2014 \u8BBE\u7F6E METASO_API_KEY\uFF0C\u6216\u5728 https://metaso.cn/search-api/playground \u83B7\u53D6\u5BC6\u94A5",
|
|
4099
4301
|
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",
|
|
4100
4302
|
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",
|
|
4101
|
-
metasoServerError: "web_search: Metaso \u670D\u52A1\u5668\u9519\u8BEF\uFF08{status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine
|
|
4303
|
+
metasoServerError: "web_search: Metaso \u670D\u52A1\u5668\u9519\u8BEF\uFF08{status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine bing|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
4102
4304
|
metasoParseError: "web_search: Metaso \u8FD4\u56DE\u65E0\u6CD5\u89E3\u6790\u7684\u54CD\u5E94\uFF08HTTP {status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5",
|
|
4103
4305
|
metasoApiError: "web_search: Metaso API \u9519\u8BEF\uFF08code {code}: {message}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5",
|
|
4104
4306
|
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",
|
|
4105
4307
|
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",
|
|
4106
|
-
tavilyRateLimit: "web_search: Tavily \u8BF7\u6C42\u9891\u7387\u9650\u5236\u6216\u6708\u5EA6\u914D\u989D\u7528\u5C3D \u2014 \u7B49\u5F85\u3001\u7528 /search-engine
|
|
4107
|
-
tavilyServerError: "web_search: Tavily \u670D\u52A1\u5668\u9519\u8BEF\uFF08{status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine
|
|
4308
|
+
tavilyRateLimit: "web_search: Tavily \u8BF7\u6C42\u9891\u7387\u9650\u5236\u6216\u6708\u5EA6\u914D\u989D\u7528\u5C3D \u2014 \u7B49\u5F85\u3001\u7528 /search-engine bing|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE\uFF0C\u6216\u5347\u7EA7 Tavily \u8BA1\u5212",
|
|
4309
|
+
tavilyServerError: "web_search: Tavily \u670D\u52A1\u5668\u9519\u8BEF\uFF08{status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine bing|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
4108
4310
|
tavilyParseError: "web_search: Tavily \u8FD4\u56DE\u65E0\u6CD5\u89E3\u6790\u7684\u54CD\u5E94\uFF08HTTP {status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5",
|
|
4109
4311
|
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",
|
|
4110
4312
|
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",
|
|
4111
|
-
perplexityRateLimit: "web_search: Perplexity \u8BF7\u6C42\u9891\u7387\u9650\u5236 \u2014 \u7B49\u5F85\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine
|
|
4112
|
-
perplexityServerError: "web_search: Perplexity \u670D\u52A1\u5668\u9519\u8BEF\uFF08{status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine
|
|
4313
|
+
perplexityRateLimit: "web_search: Perplexity \u8BF7\u6C42\u9891\u7387\u9650\u5236 \u2014 \u7B49\u5F85\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine bing|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
4314
|
+
perplexityServerError: "web_search: Perplexity \u670D\u52A1\u5668\u9519\u8BEF\uFF08{status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine bing|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
4113
4315
|
perplexityParseError: "web_search: Perplexity \u8FD4\u56DE\u65E0\u6CD5\u89E3\u6790\u7684\u54CD\u5E94\uFF08HTTP {status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5",
|
|
4114
4316
|
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",
|
|
4115
4317
|
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",
|
|
4116
4318
|
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",
|
|
4117
|
-
exaServerError: "web_search: Exa \u670D\u52A1\u5668\u9519\u8BEF\uFF08{status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine
|
|
4319
|
+
exaServerError: "web_search: Exa \u670D\u52A1\u5668\u9519\u8BEF\uFF08{status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u4F7F\u7528 /search-engine bing|searxng|metaso|tavily|perplexity|exa \u5207\u6362\u5F15\u64CE",
|
|
4118
4320
|
exaParseError: "web_search: Exa \u8FD4\u56DE\u65E0\u6CD5\u89E3\u6790\u7684\u54CD\u5E94\uFF08HTTP {status}\uFF09\u2014 \u7A0D\u540E\u91CD\u8BD5",
|
|
4119
4321
|
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",
|
|
4120
4322
|
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",
|
|
@@ -4206,19 +4408,6 @@ var zhCN = {
|
|
|
4206
4408
|
categoryProject: "\u9879\u76EE",
|
|
4207
4409
|
categoryReference: "\u53C2\u8003"
|
|
4208
4410
|
},
|
|
4209
|
-
copyMode: {
|
|
4210
|
-
title: "\u2500\u2500 \u590D\u5236\u6A21\u5F0F \u2500\u2500",
|
|
4211
|
-
help: "j/k \u6216 \u2191/\u2193 \u79FB\u52A8 \xB7 v \u8D77\u9009\u533A \xB7 y \u590D\u5236 \xB7 g/G \u9876/\u5E95 \xB7 q \u9000\u51FA",
|
|
4212
|
-
statusBar: "\u7B2C {cur}/{total} \u884C \xB7 \u9009\u533A\uFF1A{sel}",
|
|
4213
|
-
statusYanked: "\u5DF2\u590D\u5236 {size} \u5B57\u7B26\uFF08osc52={osc52}\uFF09",
|
|
4214
|
-
statusEmpty: "\u672A\u9009\u4E2D\u5185\u5BB9",
|
|
4215
|
-
empty: "\uFF08\u8FD8\u6CA1\u6709\u804A\u5929\u5185\u5BB9 \u2014 \u5148\u548C\u6A21\u578B\u8BF4\u70B9\u4EC0\u4E48\uFF09",
|
|
4216
|
-
labelUser: "\u4F60",
|
|
4217
|
-
labelAssistant: "\u52A9\u624B",
|
|
4218
|
-
labelReasoning: "\u63A8\u7406",
|
|
4219
|
-
yankedToast: "\u25B8 \u5DF2\u590D\u5236 {size} \u5B57\u7B26\u5230\u526A\u8D34\u677F (osc52)",
|
|
4220
|
-
yankedToastFile: "\u25B8 \u5DF2\u590D\u5236 {size} \u5B57\u7B26 \xB7 \u6587\u4EF6\uFF1A{path}"
|
|
4221
|
-
},
|
|
4222
4411
|
mcpHealth: {
|
|
4223
4412
|
noData: "\u65E0\u68C0\u67E5\u6570\u636E",
|
|
4224
4413
|
healthy: "\u6B63\u5E38 \xB7 {ms}ms",
|
|
@@ -4370,9 +4559,11 @@ var zhCN = {
|
|
|
4370
4559
|
groupSystem: "\u7CFB\u7EDF",
|
|
4371
4560
|
descEnter: "\u53D1\u9001\u6D88\u606F",
|
|
4372
4561
|
descShiftEnter: "\u6362\u884C",
|
|
4562
|
+
descCtrlEnter: "\u6362\u884C",
|
|
4563
|
+
descCtrlJ: "\u6362\u884C",
|
|
4373
4564
|
descCtrlU: "\u6E05\u7A7A\u8F93\u5165",
|
|
4374
4565
|
descCtrlW: "\u5220\u9664\u5355\u8BCD",
|
|
4375
|
-
descCtrlP: "\
|
|
4566
|
+
descCtrlP: "\u663E\u793A/\u9690\u85CF\u5FEB\u6377\u952E",
|
|
4376
4567
|
descCtrlX: "\u5728\u7F16\u8F91\u5668\u4E2D\u6253\u5F00",
|
|
4377
4568
|
descArrows: "\u6D4F\u89C8\u8F93\u5165\u5386\u53F2",
|
|
4378
4569
|
descPgUpDown: "\u7FFB\u9875",
|
|
@@ -4386,9 +4577,30 @@ var zhCN = {
|
|
|
4386
4577
|
descCtrlC: "\u9000\u51FA",
|
|
4387
4578
|
descEsc: "\u505C\u6B62/\u53D6\u6D88",
|
|
4388
4579
|
descCtrlR: "\u5207\u6362\u8BE6\u7EC6\u6A21\u5F0F",
|
|
4389
|
-
descCtrlO: "\u5C55\u5F00\u6D41\u5F0F\u8F93\u51FA",
|
|
4580
|
+
descCtrlO: "\u5C55\u5F00\u56DE\u590D\uFF08\u4EC5\u6D41\u5F0F\u8F93\u51FA\u671F\u95F4\uFF09",
|
|
4390
4581
|
descHelp: "\u663E\u793A\u6240\u6709\u547D\u4EE4",
|
|
4391
4582
|
descShiftTab: "\u5207\u6362\u7F16\u8F91\u6A21\u5F0F"
|
|
4583
|
+
},
|
|
4584
|
+
mcpCli: {
|
|
4585
|
+
bundledCatalog: "\u5DF2\u6253\u5305\u7684 MCP \u670D\u52A1\u5668\uFF08\u79BB\u7EBF\u76EE\u5F55\uFF09\uFF1A",
|
|
4586
|
+
justFetched: "\u521A\u521A\u83B7\u53D6",
|
|
4587
|
+
cachedAge: "\u7F13\u5B58\uFF0C{age}",
|
|
4588
|
+
moreAvailable: "\u8FD8\u6709\u66F4\u591A",
|
|
4589
|
+
allLoaded: "\u5DF2\u5168\u90E8\u52A0\u8F7D",
|
|
4590
|
+
morePagesAvailable: "\u25B8 \u8FD8\u6709\u66F4\u591A\u9875\u53EF\u7528 \u2014 `reasonix mcp list --pages <n>` \u6216 --all",
|
|
4591
|
+
installHint: "\u5B89\u88C5\uFF1Areasonix mcp install <name>",
|
|
4592
|
+
usageSearch: "\u7528\u6CD5\uFF1Areasonix mcp search <query>",
|
|
4593
|
+
usageInstall: "\u7528\u6CD5\uFF1Areasonix mcp install <name>",
|
|
4594
|
+
noMatchesFor: '\u672A\u627E\u5230 "{q}" \u7684\u5339\u914D\u9879\uFF08\u5DF2\u68C0\u7D22 {count} \u6761\u8BB0\u5F55\uFF0C\u6765\u6E90\uFF1A{source}\uFF09',
|
|
4595
|
+
matchCount: '\u5728 {source} \u4E2D\u627E\u5230 {count} \u6761 "{q}" \u7684\u5339\u914D\u9879\uFF08\u5DF2\u626B\u63CF {loaded} \u6761\u8BB0\u5F55\uFF09\uFF1A',
|
|
4596
|
+
moreLoaded: "\u2026 \u8FD8\u6709 {count} \u6761\u5DF2\u52A0\u8F7D \u2014 \u4F7F\u7528 `reasonix mcp search <query>` \u7B5B\u9009",
|
|
4597
|
+
moreMatches: "\u2026 \u8FD8\u6709 {count} \u6761\u5339\u914D\u9879",
|
|
4598
|
+
installed: "\u5DF2\u5B89\u88C5\uFF1A{spec}",
|
|
4599
|
+
noServerFound: '\u5728 {source} \u4E2D\u904D\u5386\u4E86 {pages} \u9875\u540E\u672A\u627E\u5230\u540D\u4E3A "{target}" \u7684 MCP \u670D\u52A1\u5668\u3002',
|
|
4600
|
+
noServerTryMore: "\u8BD5\u8BD5\uFF1Areasonix mcp install {target} --max-pages 100",
|
|
4601
|
+
noInstallMeta: '\u65E0\u6CD5\u4E3A "{name}" \u83B7\u53D6\u5B89\u88C5\u5143\u6570\u636E \u2014 \u8BD5\u8BD5 `npx -y @smithery/cli install {name}`\u3002',
|
|
4602
|
+
buildSpecFailed: "\u65E0\u6CD5\u4E3A {name} \u6784\u5EFA\u5B89\u88C5 spec\uFF1A{message}",
|
|
4603
|
+
alreadyInstalled: "\u5DF2\u5B89\u88C5\uFF1A{spec}"
|
|
4392
4604
|
}
|
|
4393
4605
|
};
|
|
4394
4606
|
|
|
@@ -4498,7 +4710,7 @@ function matchesTool(hook, toolName) {
|
|
|
4498
4710
|
}
|
|
4499
4711
|
var HOOK_OUTPUT_CAP_BYTES = 256 * 1024;
|
|
4500
4712
|
function defaultSpawner(input) {
|
|
4501
|
-
return new Promise((
|
|
4713
|
+
return new Promise((resolve14) => {
|
|
4502
4714
|
const child = spawn(input.command, {
|
|
4503
4715
|
cwd: input.cwd,
|
|
4504
4716
|
shell: true,
|
|
@@ -4543,7 +4755,7 @@ function defaultSpawner(input) {
|
|
|
4543
4755
|
child.stderr.on("data", (chunk) => onChunk("stderr", chunk));
|
|
4544
4756
|
child.once("error", (err) => {
|
|
4545
4757
|
clearTimeout(timer);
|
|
4546
|
-
|
|
4758
|
+
resolve14({
|
|
4547
4759
|
exitCode: null,
|
|
4548
4760
|
stdout: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
4549
4761
|
stderr: Buffer.concat(stderrChunks).toString("utf8"),
|
|
@@ -4554,7 +4766,7 @@ function defaultSpawner(input) {
|
|
|
4554
4766
|
});
|
|
4555
4767
|
child.once("close", (code) => {
|
|
4556
4768
|
clearTimeout(timer);
|
|
4557
|
-
|
|
4769
|
+
resolve14({
|
|
4558
4770
|
exitCode: code,
|
|
4559
4771
|
stdout: Buffer.concat(stdoutChunks).toString("utf8").trim(),
|
|
4560
4772
|
stderr: Buffer.concat(stderrChunks).toString("utf8").trim(),
|
|
@@ -5076,12 +5288,14 @@ var ToolRegistry = class {
|
|
|
5076
5288
|
_interceptors = [];
|
|
5077
5289
|
_auditListener = null;
|
|
5078
5290
|
_resultAugmenter = null;
|
|
5291
|
+
_rateLimiter;
|
|
5079
5292
|
/** Per-tool fingerprint of the last call that failed schema validation. Cleared by any successful validation for that tool. */
|
|
5080
5293
|
_lastMalformed = /* @__PURE__ */ new Map();
|
|
5081
5294
|
/** Per-tool fingerprint of the last host-side gate rejection. */
|
|
5082
5295
|
_lastGateRejection = /* @__PURE__ */ new Map();
|
|
5083
5296
|
constructor(opts = {}) {
|
|
5084
5297
|
this._autoFlatten = opts.autoFlatten !== false;
|
|
5298
|
+
this._rateLimiter = new ToolRateLimiter(opts.rateLimit);
|
|
5085
5299
|
}
|
|
5086
5300
|
/** Enable / disable plan-mode enforcement at dispatch. */
|
|
5087
5301
|
setPlanMode(on) {
|
|
@@ -5118,6 +5332,9 @@ var ToolRegistry = class {
|
|
|
5118
5332
|
get hasResultAugmenter() {
|
|
5119
5333
|
return this._resultAugmenter !== null;
|
|
5120
5334
|
}
|
|
5335
|
+
get rateLimitPolicy() {
|
|
5336
|
+
return this._rateLimiter.policy;
|
|
5337
|
+
}
|
|
5121
5338
|
register(def) {
|
|
5122
5339
|
if (!def.name) throw new Error("tool requires a name");
|
|
5123
5340
|
const internal = { ...def };
|
|
@@ -5216,6 +5433,10 @@ var ToolRegistry = class {
|
|
|
5216
5433
|
rejectedReason: "aborted"
|
|
5217
5434
|
});
|
|
5218
5435
|
}
|
|
5436
|
+
const rateLimit = this._rateLimiter.consume(name);
|
|
5437
|
+
if (!rateLimit.allowed) {
|
|
5438
|
+
return JSON.stringify(rateLimit.result);
|
|
5439
|
+
}
|
|
5219
5440
|
let finalResult;
|
|
5220
5441
|
try {
|
|
5221
5442
|
try {
|
|
@@ -5224,7 +5445,8 @@ var ToolRegistry = class {
|
|
|
5224
5445
|
}
|
|
5225
5446
|
const result = await tool.fn(args, {
|
|
5226
5447
|
signal: opts.signal,
|
|
5227
|
-
confirmationGate: opts.confirmationGate
|
|
5448
|
+
confirmationGate: opts.confirmationGate,
|
|
5449
|
+
readTracker: opts.readTracker
|
|
5228
5450
|
});
|
|
5229
5451
|
const str = typeof result === "string" ? result : JSON.stringify(result);
|
|
5230
5452
|
let clipped = str;
|
|
@@ -5309,6 +5531,9 @@ function plainTextRejectedReason(name, result) {
|
|
|
5309
5531
|
if ((name === "edit_file" || name === "write_file") && /rejected this edit/i.test(result)) {
|
|
5310
5532
|
return "edit-gate";
|
|
5311
5533
|
}
|
|
5534
|
+
if ((name === "edit_file" || name === "multi_edit") && /read_file first/i.test(result)) {
|
|
5535
|
+
return "read-before-edit";
|
|
5536
|
+
}
|
|
5312
5537
|
if ((name === "run_command" || name === "run_background") && /\buser denied:/i.test(result)) {
|
|
5313
5538
|
return "shell-gate";
|
|
5314
5539
|
}
|
|
@@ -5318,6 +5543,8 @@ function rejectionRecoveryHint(reason) {
|
|
|
5318
5543
|
switch (reason) {
|
|
5319
5544
|
case "edit-gate":
|
|
5320
5545
|
return "Do not re-emit the same edit. Try a genuinely different edit or ask the user how to proceed.";
|
|
5546
|
+
case "read-before-edit":
|
|
5547
|
+
return "Call read_file on the target path first, then re-issue the edit.";
|
|
5321
5548
|
case "shell-gate":
|
|
5322
5549
|
return "Do not retry the same command. Use an allowlisted/read-only command, wait for approval, or ask the user how to proceed.";
|
|
5323
5550
|
case "engineering-lifecycle":
|
|
@@ -5446,12 +5673,12 @@ async function waitForReady(ready, timeoutMs, serverName, signal) {
|
|
|
5446
5673
|
let timer;
|
|
5447
5674
|
let onAbort;
|
|
5448
5675
|
try {
|
|
5449
|
-
await new Promise((
|
|
5676
|
+
await new Promise((resolve14, reject) => {
|
|
5450
5677
|
ready.then(
|
|
5451
5678
|
() => {
|
|
5452
5679
|
if (settled) return;
|
|
5453
5680
|
settled = true;
|
|
5454
|
-
|
|
5681
|
+
resolve14();
|
|
5455
5682
|
},
|
|
5456
5683
|
(err) => {
|
|
5457
5684
|
if (settled) return;
|
|
@@ -5668,14 +5895,14 @@ function buildSyntheticAssistantMessage(content, fallbackModel) {
|
|
|
5668
5895
|
// src/memory/session.ts
|
|
5669
5896
|
import { execFileSync } from "child_process";
|
|
5670
5897
|
import {
|
|
5671
|
-
appendFileSync,
|
|
5898
|
+
appendFileSync as appendFileSync2,
|
|
5672
5899
|
chmodSync as chmodSync2,
|
|
5673
5900
|
copyFileSync,
|
|
5674
5901
|
existsSync as existsSync3,
|
|
5675
5902
|
mkdirSync as mkdirSync2,
|
|
5676
5903
|
readFileSync as readFileSync4,
|
|
5677
5904
|
readdirSync,
|
|
5678
|
-
renameSync,
|
|
5905
|
+
renameSync as renameSync2,
|
|
5679
5906
|
statSync,
|
|
5680
5907
|
unlinkSync,
|
|
5681
5908
|
writeFileSync as writeFileSync2
|
|
@@ -5732,7 +5959,7 @@ function readSessionMessages(path2) {
|
|
|
5732
5959
|
function appendSessionMessage(name, message) {
|
|
5733
5960
|
const path2 = sessionPath(name);
|
|
5734
5961
|
mkdirSync2(dirname3(path2), { recursive: true });
|
|
5735
|
-
|
|
5962
|
+
appendFileSync2(path2, `${JSON.stringify(message)}
|
|
5736
5963
|
`, "utf8");
|
|
5737
5964
|
try {
|
|
5738
5965
|
chmodSync2(path2, 384);
|
|
@@ -5791,13 +6018,13 @@ function renameSession(oldName, newName) {
|
|
|
5791
6018
|
const oldJsonl = sessionPath(oldName);
|
|
5792
6019
|
const newJsonl = sessionPath(newName);
|
|
5793
6020
|
if (!existsSync3(oldJsonl) || existsSync3(newJsonl)) return false;
|
|
5794
|
-
|
|
6021
|
+
renameSync2(oldJsonl, newJsonl);
|
|
5795
6022
|
for (const ext of SESSION_SIDECAR_EXTS) {
|
|
5796
6023
|
const oldP = oldJsonl.replace(/\.jsonl$/, ext);
|
|
5797
6024
|
const newP = newJsonl.replace(/\.jsonl$/, ext);
|
|
5798
6025
|
if (existsSync3(oldP)) {
|
|
5799
6026
|
try {
|
|
5800
|
-
|
|
6027
|
+
renameSync2(oldP, newP);
|
|
5801
6028
|
} catch {
|
|
5802
6029
|
}
|
|
5803
6030
|
}
|
|
@@ -5834,7 +6061,7 @@ function rewriteSession(name, messages) {
|
|
|
5834
6061
|
copyFileSync(path2, backup);
|
|
5835
6062
|
chmodPrivate(backup);
|
|
5836
6063
|
}
|
|
5837
|
-
|
|
6064
|
+
renameSync2(tmp, path2);
|
|
5838
6065
|
chmodPrivate(path2);
|
|
5839
6066
|
} catch (err) {
|
|
5840
6067
|
try {
|
|
@@ -6026,6 +6253,10 @@ function round(n, digits) {
|
|
|
6026
6253
|
}
|
|
6027
6254
|
|
|
6028
6255
|
// src/context-manager.ts
|
|
6256
|
+
function extractPinnedConstraints(systemPrompt) {
|
|
6257
|
+
const pattern = /# (?:HIGH PRIORITY constraints|User memory|Project memory)[\s\S]*?(?=\n# |\n---|$)/g;
|
|
6258
|
+
return Array.from(systemPrompt.matchAll(pattern), (m) => m[0]).join("\n\n");
|
|
6259
|
+
}
|
|
6029
6260
|
var HISTORY_FOLD_THRESHOLD = 0.75;
|
|
6030
6261
|
var HISTORY_FOLD_TAIL_FRACTION = 0.2;
|
|
6031
6262
|
var HISTORY_FOLD_AGGRESSIVE_THRESHOLD = 0.78;
|
|
@@ -6040,20 +6271,25 @@ var HISTORY_FOLD_SUMMARY_TIMEOUT_MS = 15e3;
|
|
|
6040
6271
|
var HISTORY_FOLD_MARKER = "[CONVERSATION HISTORY SUMMARY \u2014 earlier turns folded for context efficiency]\n\n";
|
|
6041
6272
|
var SKILL_PIN_MEMO_HEADER = "[Active skill memos \u2014 preserved verbatim across the fold:]";
|
|
6042
6273
|
var SKILL_PIN_REGEX = /<skill-pin name="([^"]+)">\n[\s\S]*?\n<\/skill-pin>/g;
|
|
6043
|
-
function
|
|
6274
|
+
function buildFoldSummaryInstruction(pinnedSkillNames) {
|
|
6275
|
+
const base = "Summarize the conversation above as one self-contained prose recap. Preserve the user's ORIGINAL OBJECTIVE (never paraphrase away negative constraints like 'do NOT do X'), all 'do not' / 'never' / 'avoid' instructions, decisions reached, files inspected or modified, tool results still relevant, and any open todos. Skip turn-by-turn play-by-play. Output plain prose only \u2014 no tool calls, no markdown headings, no SEARCH/REPLACE blocks.";
|
|
6276
|
+
if (pinnedSkillNames.length === 0) return base;
|
|
6277
|
+
const list = pinnedSkillNames.map((n) => `"${n}"`).join(", ");
|
|
6278
|
+
return `${base} The following skill memos are pinned verbatim and appended after your summary \u2014 do NOT quote or paraphrase their bodies: ${list}.`;
|
|
6279
|
+
}
|
|
6280
|
+
function collectPinnedSkills(head) {
|
|
6044
6281
|
const pinned = /* @__PURE__ */ new Map();
|
|
6045
|
-
const
|
|
6046
|
-
if (typeof msg.content !== "string")
|
|
6047
|
-
|
|
6048
|
-
const
|
|
6282
|
+
for (const msg of head) {
|
|
6283
|
+
if (typeof msg.content !== "string") continue;
|
|
6284
|
+
SKILL_PIN_REGEX.lastIndex = 0;
|
|
6285
|
+
for (const match of msg.content.matchAll(SKILL_PIN_REGEX)) {
|
|
6286
|
+
const name = match[1];
|
|
6287
|
+
const full = match[0];
|
|
6049
6288
|
pinned.delete(name);
|
|
6050
6289
|
pinned.set(name, full);
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
return hit ? { ...msg, content: next } : msg;
|
|
6055
|
-
});
|
|
6056
|
-
return { stubbedHead, pinnedBodies: [...pinned.values()] };
|
|
6290
|
+
}
|
|
6291
|
+
}
|
|
6292
|
+
return { names: [...pinned.keys()], bodies: [...pinned.values()] };
|
|
6057
6293
|
}
|
|
6058
6294
|
var ContextManager = class {
|
|
6059
6295
|
constructor(deps) {
|
|
@@ -6149,16 +6385,22 @@ var ContextManager = class {
|
|
|
6149
6385
|
const tail = all.slice(boundary);
|
|
6150
6386
|
const headTokens = totalTokens - cumTokens;
|
|
6151
6387
|
if (headTokens < totalTokens * HISTORY_FOLD_MIN_SAVINGS_FRACTION) return noop;
|
|
6152
|
-
const {
|
|
6153
|
-
const summary = await this.summarizeForFold(
|
|
6388
|
+
const { names: pinnedNames, bodies: pinnedBodies } = collectPinnedSkills(head);
|
|
6389
|
+
const summary = await this.summarizeForFold(head, pinnedNames);
|
|
6154
6390
|
if (!summary.content) return noop;
|
|
6155
6391
|
const memoTail = pinnedBodies.length > 0 ? `
|
|
6156
6392
|
|
|
6157
6393
|
${SKILL_PIN_MEMO_HEADER}
|
|
6158
6394
|
|
|
6159
6395
|
${pinnedBodies.join("\n\n")}` : "";
|
|
6396
|
+
const constraints = extractPinnedConstraints(this.deps.getSystemPrompt());
|
|
6397
|
+
const constraintTail = constraints ? `
|
|
6398
|
+
|
|
6399
|
+
[PINNED CONSTRAINTS \u2014 preserved verbatim]
|
|
6400
|
+
|
|
6401
|
+
${constraints}` : "";
|
|
6160
6402
|
const summaryMsg = buildAssistantMessage(
|
|
6161
|
-
HISTORY_FOLD_MARKER + summary.content + memoTail,
|
|
6403
|
+
HISTORY_FOLD_MARKER + summary.content + memoTail + constraintTail,
|
|
6162
6404
|
[],
|
|
6163
6405
|
model,
|
|
6164
6406
|
summary.reasoningContent
|
|
@@ -6166,6 +6408,7 @@ ${pinnedBodies.join("\n\n")}` : "";
|
|
|
6166
6408
|
const replacement = [summaryMsg, ...tail];
|
|
6167
6409
|
this.deps.log.compactInPlace(replacement);
|
|
6168
6410
|
this.persistRewrite(replacement);
|
|
6411
|
+
this.deps.onLogRewrite?.();
|
|
6169
6412
|
return {
|
|
6170
6413
|
folded: true,
|
|
6171
6414
|
beforeMessages: all.length,
|
|
@@ -6217,6 +6460,7 @@ ${pinnedBodies.join("\n\n")}` : "";
|
|
|
6217
6460
|
if (replacement.length === all.length) return noop;
|
|
6218
6461
|
this.deps.log.compactInPlace(replacement);
|
|
6219
6462
|
this.persistRewrite(replacement);
|
|
6463
|
+
this.deps.onLogRewrite?.();
|
|
6220
6464
|
return {
|
|
6221
6465
|
folded: true,
|
|
6222
6466
|
beforeMessages: all.length,
|
|
@@ -6235,17 +6479,18 @@ ${pinnedBodies.join("\n\n")}` : "";
|
|
|
6235
6479
|
this.persistRewrite([...kept]);
|
|
6236
6480
|
return true;
|
|
6237
6481
|
}
|
|
6238
|
-
async summarizeForFold(messagesToSummarize) {
|
|
6482
|
+
async summarizeForFold(messagesToSummarize, pinnedSkillNames) {
|
|
6239
6483
|
const summaryModel = "deepseek-v4-flash";
|
|
6240
|
-
const systemPrompt = "You compress conversation history for a coding agent. Output one prose recap that preserves: the user's overall goal, decisions and conclusions reached, files inspected or modified, important tool results still relevant to ongoing work, and any open todos. Skip turn-by-turn play-by-play. No tool calls, no markdown headings, no SEARCH/REPLACE blocks \u2014 plain prose only.";
|
|
6241
6484
|
const healed = healLoadedMessages(messagesToSummarize, DEFAULT_MAX_RESULT_CHARS).messages;
|
|
6485
|
+
const agentSystem = this.deps.getSystemPrompt();
|
|
6486
|
+
const fewShots = this.deps.getFewShots?.() ?? [];
|
|
6487
|
+
const tools = this.deps.getToolSpecs?.() ?? [];
|
|
6488
|
+
const instruction = buildFoldSummaryInstruction(pinnedSkillNames);
|
|
6242
6489
|
const messages = [
|
|
6243
|
-
{ role: "system", content:
|
|
6490
|
+
{ role: "system", content: agentSystem },
|
|
6491
|
+
...fewShots.map((m) => ({ ...m })),
|
|
6244
6492
|
...healed,
|
|
6245
|
-
{
|
|
6246
|
-
role: "user",
|
|
6247
|
-
content: "Summarize the conversation above as plain prose. This summary replaces the original turns to free context \u2014 make it self-contained."
|
|
6248
|
-
}
|
|
6493
|
+
{ role: "user", content: instruction }
|
|
6249
6494
|
];
|
|
6250
6495
|
const turnSignal = this.deps.getAbortSignal();
|
|
6251
6496
|
const foldCtrl = new AbortController();
|
|
@@ -6275,9 +6520,9 @@ ${pinnedBodies.join("\n\n")}` : "";
|
|
|
6275
6520
|
this.deps.client.chat({
|
|
6276
6521
|
model: summaryModel,
|
|
6277
6522
|
messages,
|
|
6523
|
+
tools: tools.length ? tools : void 0,
|
|
6278
6524
|
signal: foldCtrl.signal,
|
|
6279
|
-
thinking:
|
|
6280
|
-
reasoningEffort: "high"
|
|
6525
|
+
thinking: "disabled"
|
|
6281
6526
|
}),
|
|
6282
6527
|
abortPromise,
|
|
6283
6528
|
timeoutPromise
|
|
@@ -6362,6 +6607,7 @@ function formatLoopError(err, probe) {
|
|
|
6362
6607
|
if (status === "402") return t("errors.balance402", { inner });
|
|
6363
6608
|
if (status === "422") return t("errors.badparam422", { inner });
|
|
6364
6609
|
if (status === "400") return t("errors.badrequest400", { inner });
|
|
6610
|
+
if (status === "429") return t("errors.concurrency429", { inner });
|
|
6365
6611
|
if (is5xxStatus(status)) return formatDeepSeek5xx(status, probe);
|
|
6366
6612
|
return msg;
|
|
6367
6613
|
}
|
|
@@ -7052,8 +7298,34 @@ function signature(call) {
|
|
|
7052
7298
|
return `${call.function?.name ?? ""}::${call.function?.arguments ?? ""}`;
|
|
7053
7299
|
}
|
|
7054
7300
|
|
|
7301
|
+
// src/tools/read-tracker.ts
|
|
7302
|
+
import * as pathMod from "path";
|
|
7303
|
+
var ReadTracker = class _ReadTracker {
|
|
7304
|
+
_seen = /* @__PURE__ */ new Set();
|
|
7305
|
+
static norm(abs) {
|
|
7306
|
+
const resolved = pathMod.resolve(abs);
|
|
7307
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
7308
|
+
}
|
|
7309
|
+
markRead(abs) {
|
|
7310
|
+
this._seen.add(_ReadTracker.norm(abs));
|
|
7311
|
+
}
|
|
7312
|
+
hasRead(abs) {
|
|
7313
|
+
return this._seen.has(_ReadTracker.norm(abs));
|
|
7314
|
+
}
|
|
7315
|
+
reset() {
|
|
7316
|
+
this._seen.clear();
|
|
7317
|
+
}
|
|
7318
|
+
get size() {
|
|
7319
|
+
return this._seen.size;
|
|
7320
|
+
}
|
|
7321
|
+
};
|
|
7322
|
+
|
|
7055
7323
|
// src/loop.ts
|
|
7056
7324
|
var ESCALATION_MODEL = "deepseek-v4-pro";
|
|
7325
|
+
var MID_TURN_STEER_WRAPPER = "[Mid-turn steer queued by the user. Do not treat this as a new task; use it only as additional guidance for the current task after completing the current step.]";
|
|
7326
|
+
function formatSteerUserMessage(content) {
|
|
7327
|
+
return [MID_TURN_STEER_WRAPPER, content].join("\n");
|
|
7328
|
+
}
|
|
7057
7329
|
var CacheFirstLoop = class {
|
|
7058
7330
|
client;
|
|
7059
7331
|
prefix;
|
|
@@ -7062,6 +7334,8 @@ var CacheFirstLoop = class {
|
|
|
7062
7334
|
scratch = new VolatileScratch();
|
|
7063
7335
|
stats = new SessionStats();
|
|
7064
7336
|
repair;
|
|
7337
|
+
/** Files the model has read this session; gates edit_file / multi_edit so SEARCH text matches on-disk bytes. Cleared on fold / mechanical truncate (the model's byte-level view of the elided history is gone). In-memory only — naturally empty on resume. */
|
|
7338
|
+
readTracker = new ReadTracker();
|
|
7065
7339
|
// Mutable via configure() — slash commands in the TUI / library callers tweak
|
|
7066
7340
|
// these mid-session so users don't have to restart.
|
|
7067
7341
|
model;
|
|
@@ -7085,15 +7359,19 @@ var CacheFirstLoop = class {
|
|
|
7085
7359
|
_turnAbort = new AbortController();
|
|
7086
7360
|
/** Authoritative running-id set — UI cards consult this instead of trusting end-event delivery. Insert at dispatch entry, delete in finally. */
|
|
7087
7361
|
_inflight = new InflightSet();
|
|
7088
|
-
/** Typeahead steer
|
|
7089
|
-
|
|
7362
|
+
/** Typeahead steer messages set by the UI; step() consumes one at each iter boundary. */
|
|
7363
|
+
_steerQueue = [];
|
|
7090
7364
|
/** Set true when a steer was consumed this turn; cleared on next step() entry. */
|
|
7091
7365
|
_steerConsumed = false;
|
|
7092
7366
|
/** UI calls this to inject a mid-turn steer message without aborting the current turn.
|
|
7093
|
-
* New text resets steerConsumed
|
|
7367
|
+
* New text resets steerConsumed because a fresh steer is queued. */
|
|
7094
7368
|
steer(text) {
|
|
7095
|
-
|
|
7096
|
-
|
|
7369
|
+
if (text === null) {
|
|
7370
|
+
this._steerQueue.length = 0;
|
|
7371
|
+
return;
|
|
7372
|
+
}
|
|
7373
|
+
this._steerQueue.push(text);
|
|
7374
|
+
this._steerConsumed = false;
|
|
7097
7375
|
}
|
|
7098
7376
|
/** True when a steer was consumed this turn (UI gate to avoid double-submit). */
|
|
7099
7377
|
get steerConsumed() {
|
|
@@ -7179,7 +7457,11 @@ var CacheFirstLoop = class {
|
|
|
7179
7457
|
stats: this.stats,
|
|
7180
7458
|
sessionName: this.sessionName,
|
|
7181
7459
|
getAbortSignal: () => this._turnAbort.signal,
|
|
7182
|
-
getCurrentTurn: () => this._turn
|
|
7460
|
+
getCurrentTurn: () => this._turn,
|
|
7461
|
+
getSystemPrompt: () => this.prefix.system,
|
|
7462
|
+
getToolSpecs: () => this.prefix.toolSpecs,
|
|
7463
|
+
getFewShots: () => this.prefix.fewShots,
|
|
7464
|
+
onLogRewrite: () => this.readTracker.reset()
|
|
7183
7465
|
});
|
|
7184
7466
|
}
|
|
7185
7467
|
/** Replace older turns with one summary message; keep tail within keepRecentTokens budget. */
|
|
@@ -7350,7 +7632,8 @@ ${reason}`
|
|
|
7350
7632
|
const result = await this.tools.dispatch(name, args, {
|
|
7351
7633
|
signal,
|
|
7352
7634
|
maxResultTokens: DEFAULT_MAX_RESULT_TOKENS,
|
|
7353
|
-
confirmationGate: this.confirmationGate
|
|
7635
|
+
confirmationGate: this.confirmationGate,
|
|
7636
|
+
readTracker: this.readTracker
|
|
7354
7637
|
});
|
|
7355
7638
|
const postReport = await runHooks({
|
|
7356
7639
|
hooks: this.hooks,
|
|
@@ -7378,11 +7661,9 @@ ${reason}`
|
|
|
7378
7661
|
return generated;
|
|
7379
7662
|
}
|
|
7380
7663
|
_inflightCounter = 0;
|
|
7381
|
-
buildMessages(
|
|
7664
|
+
buildMessages() {
|
|
7382
7665
|
const healedMessages = this.healActiveLogBeforeSend();
|
|
7383
|
-
|
|
7384
|
-
if (pendingUser !== null) msgs.push({ role: "user", content: pendingUser });
|
|
7385
|
-
return msgs;
|
|
7666
|
+
return [...this.prefix.toMessages(), ...healedMessages];
|
|
7386
7667
|
}
|
|
7387
7668
|
healActiveLogBeforeSend() {
|
|
7388
7669
|
const current = this.log.toMessages();
|
|
@@ -7463,6 +7744,7 @@ ${reason}`
|
|
|
7463
7744
|
cap: this.budgetUsd.toFixed(2)
|
|
7464
7745
|
})
|
|
7465
7746
|
};
|
|
7747
|
+
this._steerQueue.length = 0;
|
|
7466
7748
|
return;
|
|
7467
7749
|
}
|
|
7468
7750
|
if (!this._budgetWarned && spent >= this.budgetUsd * 0.8) {
|
|
@@ -7501,8 +7783,8 @@ ${reason}`
|
|
|
7501
7783
|
};
|
|
7502
7784
|
}
|
|
7503
7785
|
this.appendAndPersist({ role: "user", content: userInput });
|
|
7504
|
-
let pendingUser = null;
|
|
7505
7786
|
const toolSpecs = this.prefix.tools();
|
|
7787
|
+
let rateLimitWarningShown = false;
|
|
7506
7788
|
for (let iter = 0; ; iter++) {
|
|
7507
7789
|
if (signal.aborted) {
|
|
7508
7790
|
try {
|
|
@@ -7523,6 +7805,7 @@ ${reason}`
|
|
|
7523
7805
|
} finally {
|
|
7524
7806
|
this._turnAbort = new AbortController();
|
|
7525
7807
|
}
|
|
7808
|
+
this._steerQueue.length = 0;
|
|
7526
7809
|
return;
|
|
7527
7810
|
}
|
|
7528
7811
|
if (iter > 0) {
|
|
@@ -7532,14 +7815,15 @@ ${reason}`
|
|
|
7532
7815
|
content: t("loop.toolUploadStatus")
|
|
7533
7816
|
};
|
|
7534
7817
|
}
|
|
7535
|
-
let messages = this.buildMessages(
|
|
7536
|
-
if (this.
|
|
7537
|
-
const steer = this.
|
|
7538
|
-
this.
|
|
7539
|
-
this.
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7818
|
+
let messages = this.buildMessages();
|
|
7819
|
+
if (this._steerQueue.length > 0) {
|
|
7820
|
+
const steer = this._steerQueue.shift();
|
|
7821
|
+
this._steerConsumed = this._steerQueue.length === 0;
|
|
7822
|
+
this.appendAndPersist({
|
|
7823
|
+
role: "user",
|
|
7824
|
+
content: formatSteerUserMessage(steer)
|
|
7825
|
+
});
|
|
7826
|
+
messages = this.buildMessages();
|
|
7543
7827
|
yield {
|
|
7544
7828
|
turn: this._turn,
|
|
7545
7829
|
role: "steer",
|
|
@@ -7556,10 +7840,10 @@ ${reason}`
|
|
|
7556
7840
|
content: t("loop.preflightTruncateStatus")
|
|
7557
7841
|
};
|
|
7558
7842
|
const result = this.context.mechanicalTruncate(this.model, {
|
|
7559
|
-
allowEmpty:
|
|
7843
|
+
allowEmpty: false
|
|
7560
7844
|
});
|
|
7561
7845
|
if (result.folded) {
|
|
7562
|
-
messages = this.buildMessages(
|
|
7846
|
+
messages = this.buildMessages();
|
|
7563
7847
|
const after = this.context.decidePreflight(messages, this.prefix.toolSpecs, this.model);
|
|
7564
7848
|
const stillFull = after.needsAction;
|
|
7565
7849
|
yield {
|
|
@@ -7705,6 +7989,7 @@ ${reason}`
|
|
|
7705
7989
|
} finally {
|
|
7706
7990
|
this._turnAbort = new AbortController();
|
|
7707
7991
|
}
|
|
7992
|
+
this._steerQueue.length = 0;
|
|
7708
7993
|
return;
|
|
7709
7994
|
}
|
|
7710
7995
|
const probe = is5xxError(err) ? await probeDeepSeekReachable(this.client) : void 0;
|
|
@@ -7714,6 +7999,7 @@ ${reason}`
|
|
|
7714
7999
|
content: "",
|
|
7715
8000
|
error: formatLoopError(err, probe)
|
|
7716
8001
|
};
|
|
8002
|
+
this._steerQueue.length = 0;
|
|
7717
8003
|
return;
|
|
7718
8004
|
}
|
|
7719
8005
|
if (this.autoEscalate && this.modelForCurrentCall() !== ESCALATION_MODEL && isEscalationRequest(assistantContent)) {
|
|
@@ -7794,11 +8080,16 @@ ${reason}`
|
|
|
7794
8080
|
};
|
|
7795
8081
|
}
|
|
7796
8082
|
if (repairedCalls.length === 0) {
|
|
8083
|
+
if (this._steerQueue.length > 0) {
|
|
8084
|
+
continue;
|
|
8085
|
+
}
|
|
7797
8086
|
if (allSuppressed) {
|
|
7798
8087
|
yield* forceSummaryAfterIterLimit(this.summaryContext(), { reason: "stuck" });
|
|
8088
|
+
this._steerQueue.length = 0;
|
|
7799
8089
|
return;
|
|
7800
8090
|
}
|
|
7801
8091
|
yield { turn: this._turn, role: "done", content: assistantContent };
|
|
8092
|
+
this._steerQueue.length = 0;
|
|
7802
8093
|
return;
|
|
7803
8094
|
}
|
|
7804
8095
|
const decision = this.context.decideAfterUsage(usage, this.model, this._foldedThisTurn);
|
|
@@ -7844,6 +8135,7 @@ ${reason}`
|
|
|
7844
8135
|
};
|
|
7845
8136
|
this.context.trimTrailingToolCalls();
|
|
7846
8137
|
yield* forceSummaryAfterIterLimit(this.summaryContext(), { reason: "context-guard" });
|
|
8138
|
+
this._steerQueue.length = 0;
|
|
7847
8139
|
return;
|
|
7848
8140
|
}
|
|
7849
8141
|
const dispatchSerial = (process.env.REASONIX_TOOL_DISPATCH ?? "auto").toLowerCase() === "serial";
|
|
@@ -7891,6 +8183,15 @@ ${reason}`
|
|
|
7891
8183
|
}
|
|
7892
8184
|
for (const w of preWarnings) yield w;
|
|
7893
8185
|
for (const w of postWarnings) yield w;
|
|
8186
|
+
const rateLimited = parseRateLimitedToolResult(result);
|
|
8187
|
+
if (rateLimited && !rateLimitWarningShown) {
|
|
8188
|
+
rateLimitWarningShown = true;
|
|
8189
|
+
yield {
|
|
8190
|
+
turn: this._turn,
|
|
8191
|
+
role: "warning",
|
|
8192
|
+
content: rateLimited.message
|
|
8193
|
+
};
|
|
8194
|
+
}
|
|
7894
8195
|
this.appendAndPersist({
|
|
7895
8196
|
role: "tool",
|
|
7896
8197
|
tool_call_id: call.id ?? "",
|
|
@@ -7913,7 +8214,7 @@ ${reason}`
|
|
|
7913
8214
|
return {
|
|
7914
8215
|
client: this.client,
|
|
7915
8216
|
signal: this._turnAbort.signal,
|
|
7916
|
-
buildMessages: () => this.buildMessages(
|
|
8217
|
+
buildMessages: () => this.buildMessages(),
|
|
7917
8218
|
appendAndPersist: (m) => this.appendAndPersist(m),
|
|
7918
8219
|
recordStats: (model, usage) => this.stats.record(this._turn, model, usage),
|
|
7919
8220
|
turn: this._turn
|
|
@@ -7938,7 +8239,7 @@ function parsePositiveIntEnv(raw) {
|
|
|
7938
8239
|
// src/at-mentions.ts
|
|
7939
8240
|
import { existsSync as existsSync4, readFileSync as readFileSync6, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
7940
8241
|
import { readdir, stat } from "fs/promises";
|
|
7941
|
-
import { isAbsolute as isAbsolute2, join as join5, relative, resolve as
|
|
8242
|
+
import { isAbsolute as isAbsolute2, join as join5, relative, resolve as resolve3 } from "path";
|
|
7942
8243
|
|
|
7943
8244
|
// src/gitignore.ts
|
|
7944
8245
|
import { readFileSync as readFileSync5 } from "fs";
|
|
@@ -7993,7 +8294,7 @@ function listFilesSync(root, opts = {}) {
|
|
|
7993
8294
|
function listFilesWithStatsSync(root, opts = {}) {
|
|
7994
8295
|
const maxResults = Math.max(1, opts.maxResults ?? 2e3);
|
|
7995
8296
|
const ignoreDirs = new Set(opts.ignoreDirs ?? DEFAULT_PICKER_IGNORE_DIRS);
|
|
7996
|
-
const rootAbs =
|
|
8297
|
+
const rootAbs = resolve3(root);
|
|
7997
8298
|
const respectGi = opts.respectGitignore !== false;
|
|
7998
8299
|
const out = [];
|
|
7999
8300
|
const walk2 = (dirAbs, dirRel, layers) => {
|
|
@@ -8057,7 +8358,7 @@ async function listFilesWithStatsAsync(root, opts = {}) {
|
|
|
8057
8358
|
async function walkFilesStream(root, opts) {
|
|
8058
8359
|
const ignoreDirs = new Set(opts.ignoreDirs ?? DEFAULT_PICKER_IGNORE_DIRS);
|
|
8059
8360
|
const respectGi = opts.respectGitignore !== false;
|
|
8060
|
-
const rootAbs =
|
|
8361
|
+
const rootAbs = resolve3(root);
|
|
8061
8362
|
const progressGap = Math.max(0, opts.progressIntervalMs ?? 100);
|
|
8062
8363
|
let scanned = 0;
|
|
8063
8364
|
let halted = false;
|
|
@@ -8135,8 +8436,8 @@ async function flushFiles(ents, dirAbs, dirRel, layers, emit) {
|
|
|
8135
8436
|
async function listDirectory(root, relDir, opts = {}) {
|
|
8136
8437
|
const ignoreDirs = new Set(opts.ignoreDirs ?? DEFAULT_PICKER_IGNORE_DIRS);
|
|
8137
8438
|
const respectGi = opts.respectGitignore !== false;
|
|
8138
|
-
const rootAbs =
|
|
8139
|
-
const dirAbs =
|
|
8439
|
+
const rootAbs = resolve3(root);
|
|
8440
|
+
const dirAbs = resolve3(rootAbs, relDir);
|
|
8140
8441
|
const rel = relative(rootAbs, dirAbs);
|
|
8141
8442
|
if (rel.startsWith("..") || isAbsolute2(rel)) return [];
|
|
8142
8443
|
const layers = [];
|
|
@@ -8301,7 +8602,7 @@ function expandAtMentions(text, rootDir, opts = {}) {
|
|
|
8301
8602
|
const maxBytes = opts.maxBytes ?? DEFAULT_AT_MENTION_MAX_BYTES;
|
|
8302
8603
|
const maxDirEntries = Math.max(1, opts.maxDirEntries ?? DEFAULT_AT_DIR_MAX_ENTRIES);
|
|
8303
8604
|
const fs5 = opts.fs ?? defaultFs;
|
|
8304
|
-
const root =
|
|
8605
|
+
const root = resolve3(rootDir);
|
|
8305
8606
|
const seen = /* @__PURE__ */ new Map();
|
|
8306
8607
|
const expansions = [];
|
|
8307
8608
|
const dirListings = /* @__PURE__ */ new Map();
|
|
@@ -8348,7 +8649,7 @@ function resolveMention(rawPath, root, maxBytes, maxDirEntries, fs5, dirListings
|
|
|
8348
8649
|
if (isAbsolute2(rawPath)) {
|
|
8349
8650
|
return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "escape" };
|
|
8350
8651
|
}
|
|
8351
|
-
const resolved =
|
|
8652
|
+
const resolved = resolve3(root, rawPath);
|
|
8352
8653
|
const rel = relative(root, resolved);
|
|
8353
8654
|
if (rel.startsWith("..") || isAbsolute2(rel)) {
|
|
8354
8655
|
return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "escape" };
|
|
@@ -8378,7 +8679,7 @@ function resolveMention(rawPath, root, maxBytes, maxDirEntries, fs5, dirListings
|
|
|
8378
8679
|
return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "not-file" };
|
|
8379
8680
|
}
|
|
8380
8681
|
function readSafe(root, rawPath, fs5) {
|
|
8381
|
-
const resolved =
|
|
8682
|
+
const resolved = resolve3(root, rawPath);
|
|
8382
8683
|
try {
|
|
8383
8684
|
return fs5.read(resolved);
|
|
8384
8685
|
} catch {
|
|
@@ -8488,7 +8789,7 @@ import {
|
|
|
8488
8789
|
writeFileSync as writeFileSync4
|
|
8489
8790
|
} from "fs";
|
|
8490
8791
|
import { homedir as homedir5 } from "os";
|
|
8491
|
-
import { join as join8, resolve as
|
|
8792
|
+
import { join as join8, resolve as resolve5 } from "path";
|
|
8492
8793
|
|
|
8493
8794
|
// src/frontmatter.ts
|
|
8494
8795
|
var KEY_RE = /^([a-zA-Z_][a-zA-Z0-9_-]*):\s*(.*)$/;
|
|
@@ -8549,7 +8850,7 @@ import {
|
|
|
8549
8850
|
} from "fs";
|
|
8550
8851
|
import { accessSync } from "fs";
|
|
8551
8852
|
import { homedir as homedir4 } from "os";
|
|
8552
|
-
import { dirname as dirname4, isAbsolute as isAbsolute3, join as join7, resolve as
|
|
8853
|
+
import { dirname as dirname4, isAbsolute as isAbsolute3, join as join7, resolve as resolve4 } from "path";
|
|
8553
8854
|
|
|
8554
8855
|
// src/prompt-fragments.ts
|
|
8555
8856
|
var TUI_FORMATTING_RULES = `Formatting (rendered in a TUI with a real markdown renderer):
|
|
@@ -8601,7 +8902,7 @@ var SkillStore = class {
|
|
|
8601
8902
|
disableBuiltins;
|
|
8602
8903
|
constructor(opts = {}) {
|
|
8603
8904
|
this.homeDir = opts.homeDir ?? homedir4();
|
|
8604
|
-
this.projectRoot = opts.projectRoot ?
|
|
8905
|
+
this.projectRoot = opts.projectRoot ? resolve4(opts.projectRoot) : void 0;
|
|
8605
8906
|
const baseDir = this.projectRoot ?? process.cwd();
|
|
8606
8907
|
this.customSkillPaths = dedupePaths(
|
|
8607
8908
|
opts.customSkillPaths?.map((p) => resolveCustomSkillPath(p, baseDir, this.homeDir)) ?? []
|
|
@@ -8767,7 +9068,7 @@ function dedupePaths(paths) {
|
|
|
8767
9068
|
function resolveCustomSkillPath(path2, baseDir, homeDir) {
|
|
8768
9069
|
const trimmed = path2.trim();
|
|
8769
9070
|
const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") || trimmed.startsWith("~\\") ? join7(homeDir, trimmed.slice(2)) : trimmed;
|
|
8770
|
-
return
|
|
9071
|
+
return resolve4(isAbsolute3(expanded) ? expanded : join7(baseDir, expanded));
|
|
8771
9072
|
}
|
|
8772
9073
|
function skillPathStatus(dir) {
|
|
8773
9074
|
try {
|
|
@@ -9040,7 +9341,7 @@ function sanitizeMemoryName(raw) {
|
|
|
9040
9341
|
return trimmed;
|
|
9041
9342
|
}
|
|
9042
9343
|
function projectHash(rootDir) {
|
|
9043
|
-
const abs =
|
|
9344
|
+
const abs = resolve5(rootDir);
|
|
9044
9345
|
return createHash2("sha1").update(abs).digest("hex").slice(0, 16);
|
|
9045
9346
|
}
|
|
9046
9347
|
function scopeDir(opts) {
|
|
@@ -9090,7 +9391,7 @@ var MemoryStore = class {
|
|
|
9090
9391
|
projectRoot;
|
|
9091
9392
|
constructor(opts = {}) {
|
|
9092
9393
|
this.homeDir = opts.homeDir ?? join8(homedir5(), ".reasonix");
|
|
9093
|
-
this.projectRoot = opts.projectRoot ?
|
|
9394
|
+
this.projectRoot = opts.projectRoot ? resolve5(opts.projectRoot) : void 0;
|
|
9094
9395
|
}
|
|
9095
9396
|
/** Directory this store writes `scope` files into, creating it if needed. */
|
|
9096
9397
|
dir(scope) {
|
|
@@ -9346,15 +9647,44 @@ function applyMemoryStack(basePrompt, rootDir, opts = {}) {
|
|
|
9346
9647
|
|
|
9347
9648
|
// src/tools/filesystem.ts
|
|
9348
9649
|
import { promises as fs4 } from "fs";
|
|
9349
|
-
import * as
|
|
9650
|
+
import * as pathMod6 from "path";
|
|
9350
9651
|
import picomatch3 from "picomatch";
|
|
9351
9652
|
|
|
9653
|
+
// src/code/file-encoding.ts
|
|
9654
|
+
import { promises as fsp, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
|
|
9655
|
+
import iconv from "iconv-lite";
|
|
9656
|
+
var UTF8_BOM = Buffer.from([239, 187, 191]);
|
|
9657
|
+
function decodeFileBuffer(buf) {
|
|
9658
|
+
if (buf.length >= 3 && buf[0] === 239 && buf[1] === 187 && buf[2] === 191) {
|
|
9659
|
+
return { text: buf.subarray(3).toString("utf8"), encoding: "utf8-bom" };
|
|
9660
|
+
}
|
|
9661
|
+
try {
|
|
9662
|
+
return { text: new TextDecoder("utf-8", { fatal: true }).decode(buf), encoding: "utf8" };
|
|
9663
|
+
} catch {
|
|
9664
|
+
}
|
|
9665
|
+
try {
|
|
9666
|
+
return {
|
|
9667
|
+
text: new TextDecoder("gb18030", { fatal: true }).decode(buf),
|
|
9668
|
+
encoding: "gb18030"
|
|
9669
|
+
};
|
|
9670
|
+
} catch {
|
|
9671
|
+
}
|
|
9672
|
+
return { text: buf.toString("utf8"), encoding: "utf8" };
|
|
9673
|
+
}
|
|
9674
|
+
function encodeFile(text, encoding) {
|
|
9675
|
+
if (encoding === "utf8") return Buffer.from(text, "utf8");
|
|
9676
|
+
if (encoding === "utf8-bom") {
|
|
9677
|
+
return Buffer.concat([UTF8_BOM, Buffer.from(text, "utf8")]);
|
|
9678
|
+
}
|
|
9679
|
+
return iconv.encode(text, "gb18030");
|
|
9680
|
+
}
|
|
9681
|
+
|
|
9352
9682
|
// src/memory/subdir.ts
|
|
9353
|
-
import { existsSync as existsSync8, readFileSync as
|
|
9354
|
-
import { dirname as dirname5, join as join9, relative as relative2, resolve as
|
|
9683
|
+
import { existsSync as existsSync8, readFileSync as readFileSync11 } from "fs";
|
|
9684
|
+
import { dirname as dirname5, join as join9, relative as relative2, resolve as resolve6 } from "path";
|
|
9355
9685
|
function findDirMemory(absDir, rootDir) {
|
|
9356
|
-
const root =
|
|
9357
|
-
const target =
|
|
9686
|
+
const root = resolve6(rootDir);
|
|
9687
|
+
const target = resolve6(absDir);
|
|
9358
9688
|
const rel = relative2(root, target);
|
|
9359
9689
|
if (rel.startsWith("..")) return [];
|
|
9360
9690
|
const found = [];
|
|
@@ -9376,12 +9706,12 @@ function findDirMemory(absDir, rootDir) {
|
|
|
9376
9706
|
return found;
|
|
9377
9707
|
}
|
|
9378
9708
|
function findSubdirMemoryAncestors(absPath, rootDir) {
|
|
9379
|
-
return findDirMemory(dirname5(
|
|
9709
|
+
return findDirMemory(dirname5(resolve6(absPath)), rootDir);
|
|
9380
9710
|
}
|
|
9381
9711
|
function readSubdirMemoryContent(path2) {
|
|
9382
9712
|
let raw;
|
|
9383
9713
|
try {
|
|
9384
|
-
raw =
|
|
9714
|
+
raw = readFileSync11(path2, "utf8");
|
|
9385
9715
|
} catch {
|
|
9386
9716
|
return null;
|
|
9387
9717
|
}
|
|
@@ -9399,15 +9729,22 @@ ${content}`;
|
|
|
9399
9729
|
|
|
9400
9730
|
// src/tools/fs/edit.ts
|
|
9401
9731
|
import { promises as fs } from "fs";
|
|
9402
|
-
import * as
|
|
9732
|
+
import * as pathMod2 from "path";
|
|
9403
9733
|
function displayRel(rootDir, full) {
|
|
9404
|
-
return
|
|
9734
|
+
return pathMod2.relative(rootDir, full).replaceAll("\\", "/");
|
|
9405
9735
|
}
|
|
9406
|
-
|
|
9736
|
+
var READ_BEFORE_EDIT_MARKER = "read_file first";
|
|
9737
|
+
async function applyEdit(rootDir, abs, args, hasRead) {
|
|
9407
9738
|
if (args.search.length === 0) {
|
|
9408
9739
|
throw new Error("edit_file: search cannot be empty");
|
|
9409
9740
|
}
|
|
9410
|
-
|
|
9741
|
+
if (hasRead && !hasRead(abs)) {
|
|
9742
|
+
throw new Error(
|
|
9743
|
+
`edit_file: ${displayRel(rootDir, abs)} was not read this session \u2014 ${READ_BEFORE_EDIT_MARKER} so your SEARCH matches the bytes on disk.`
|
|
9744
|
+
);
|
|
9745
|
+
}
|
|
9746
|
+
const beforeBuf = await fs.readFile(abs);
|
|
9747
|
+
const { text: before, encoding } = decodeFileBuffer(beforeBuf);
|
|
9411
9748
|
const le = before.includes("\r\n") ? "\r\n" : "\n";
|
|
9412
9749
|
const adaptedSearch = args.search.replace(/\r?\n/g, le);
|
|
9413
9750
|
const adaptedReplace = args.replace.replace(/\r?\n/g, le);
|
|
@@ -9422,7 +9759,7 @@ async function applyEdit(rootDir, abs, args) {
|
|
|
9422
9759
|
);
|
|
9423
9760
|
}
|
|
9424
9761
|
const after = before.slice(0, firstIdx) + adaptedReplace + before.slice(firstIdx + adaptedSearch.length);
|
|
9425
|
-
await fs.writeFile(abs, after,
|
|
9762
|
+
await fs.writeFile(abs, encodeFile(after, encoding));
|
|
9426
9763
|
const rel = displayRel(rootDir, abs);
|
|
9427
9764
|
const header = `edited ${rel} (${adaptedSearch.length}\u2192${adaptedReplace.length} chars)`;
|
|
9428
9765
|
const startLine = before.slice(0, firstIdx).split(/\r?\n/).length;
|
|
@@ -9430,7 +9767,7 @@ async function applyEdit(rootDir, abs, args) {
|
|
|
9430
9767
|
return `${header}
|
|
9431
9768
|
${diff}`;
|
|
9432
9769
|
}
|
|
9433
|
-
async function applyMultiEdit(rootDir, edits) {
|
|
9770
|
+
async function applyMultiEdit(rootDir, edits, hasRead) {
|
|
9434
9771
|
if (edits.length === 0) {
|
|
9435
9772
|
throw new Error("multi_edit: edits must contain at least one entry");
|
|
9436
9773
|
}
|
|
@@ -9456,16 +9793,23 @@ async function applyMultiEdit(rootDir, edits) {
|
|
|
9456
9793
|
}
|
|
9457
9794
|
let state = filesByPath.get(e.abs);
|
|
9458
9795
|
if (!state) {
|
|
9796
|
+
if (hasRead && !hasRead(e.abs)) {
|
|
9797
|
+
throw new Error(
|
|
9798
|
+
`multi_edit: edit #${i + 1} target ${rel} was not read this session \u2014 ${READ_BEFORE_EDIT_MARKER} (no edits applied)`
|
|
9799
|
+
);
|
|
9800
|
+
}
|
|
9459
9801
|
let before;
|
|
9802
|
+
let encoding;
|
|
9460
9803
|
try {
|
|
9461
|
-
|
|
9804
|
+
const buf = await fs.readFile(e.abs);
|
|
9805
|
+
({ text: before, encoding } = decodeFileBuffer(buf));
|
|
9462
9806
|
} catch (err) {
|
|
9463
9807
|
throw new Error(
|
|
9464
9808
|
`multi_edit: edit #${i + 1} cannot read ${rel}: ${err.message} (no edits applied)`
|
|
9465
9809
|
);
|
|
9466
9810
|
}
|
|
9467
9811
|
const le = before.includes("\r\n") ? "\r\n" : "\n";
|
|
9468
|
-
state = { before, buf: before, le, hunks: [], deltaChars: 0, touched: 0 };
|
|
9812
|
+
state = { before, buf: before, le, hunks: [], deltaChars: 0, touched: 0, encoding };
|
|
9469
9813
|
filesByPath.set(e.abs, state);
|
|
9470
9814
|
}
|
|
9471
9815
|
const adaptedSearch = e.search.replace(/\r?\n/g, state.le);
|
|
@@ -9492,14 +9836,14 @@ ${renderEditDiff(adaptedSearch, adaptedReplace, startLine)}`);
|
|
|
9492
9836
|
const attempted = [];
|
|
9493
9837
|
try {
|
|
9494
9838
|
for (const [abs, state] of filesByPath) {
|
|
9495
|
-
attempted.push({ abs, before: state.before });
|
|
9496
|
-
await fs.writeFile(abs, state.buf,
|
|
9839
|
+
attempted.push({ abs, before: state.before, encoding: state.encoding });
|
|
9840
|
+
await fs.writeFile(abs, encodeFile(state.buf, state.encoding));
|
|
9497
9841
|
}
|
|
9498
9842
|
} catch (writeErr) {
|
|
9499
9843
|
const rollbackFailures = [];
|
|
9500
9844
|
for (const item of [...attempted].reverse()) {
|
|
9501
9845
|
try {
|
|
9502
|
-
await fs.writeFile(item.abs, item.before,
|
|
9846
|
+
await fs.writeFile(item.abs, encodeFile(item.before, item.encoding));
|
|
9503
9847
|
} catch (restoreErr) {
|
|
9504
9848
|
rollbackFailures.push(`${displayRel(rootDir, item.abs)}: ${restoreErr.message}`);
|
|
9505
9849
|
}
|
|
@@ -9576,10 +9920,10 @@ function lineDiff(a, b) {
|
|
|
9576
9920
|
|
|
9577
9921
|
// src/tools/fs/glob.ts
|
|
9578
9922
|
import { promises as fs2 } from "fs";
|
|
9579
|
-
import * as
|
|
9923
|
+
import * as pathMod3 from "path";
|
|
9580
9924
|
import picomatch2 from "picomatch";
|
|
9581
9925
|
function displayRel2(rootDir, full) {
|
|
9582
|
-
return
|
|
9926
|
+
return pathMod3.relative(rootDir, full).replaceAll("\\", "/");
|
|
9583
9927
|
}
|
|
9584
9928
|
async function globFiles(ctx, startAbs, args) {
|
|
9585
9929
|
if (args.signal?.aborted) {
|
|
@@ -9601,7 +9945,7 @@ async function globFiles(ctx, startAbs, args) {
|
|
|
9601
9945
|
return;
|
|
9602
9946
|
}
|
|
9603
9947
|
for (const e of entries) {
|
|
9604
|
-
const full =
|
|
9948
|
+
const full = pathMod3.join(dir, e.name);
|
|
9605
9949
|
if (e.isDirectory()) {
|
|
9606
9950
|
if (!includeDeps && ctx.skipDirNames.has(e.name)) continue;
|
|
9607
9951
|
await walk2(full);
|
|
@@ -9638,7 +9982,7 @@ async function globFiles(ctx, startAbs, args) {
|
|
|
9638
9982
|
}
|
|
9639
9983
|
|
|
9640
9984
|
// src/tools/fs/outline.ts
|
|
9641
|
-
import * as
|
|
9985
|
+
import * as pathMod4 from "path";
|
|
9642
9986
|
var OUTLINE_MAX_ENTRIES = 30;
|
|
9643
9987
|
var OUTLINE_TAIL_KEEP = 5;
|
|
9644
9988
|
var TS_EXPORT_RE = /^export\s+(?:default\s+)?(?:async\s+)?(function|class|const|let|var|interface|type|enum)\s+\*?\s*(\w+)/;
|
|
@@ -9681,7 +10025,7 @@ var EXT_TO_LANG = {
|
|
|
9681
10025
|
".text": "txt"
|
|
9682
10026
|
};
|
|
9683
10027
|
function extractOutline(filename, lines) {
|
|
9684
|
-
const ext =
|
|
10028
|
+
const ext = pathMod4.extname(filename).toLowerCase();
|
|
9685
10029
|
const lang = EXT_TO_LANG[ext];
|
|
9686
10030
|
if (!lang) return [];
|
|
9687
10031
|
switch (lang) {
|
|
@@ -9822,7 +10166,7 @@ function formatOutline(entries) {
|
|
|
9822
10166
|
|
|
9823
10167
|
// src/tools/fs/search.ts
|
|
9824
10168
|
import { promises as fs3 } from "fs";
|
|
9825
|
-
import * as
|
|
10169
|
+
import * as pathMod5 from "path";
|
|
9826
10170
|
|
|
9827
10171
|
// src/tools/fs/regex-runner.ts
|
|
9828
10172
|
import { Worker } from "worker_threads";
|
|
@@ -9855,7 +10199,7 @@ var RegexRunner = class {
|
|
|
9855
10199
|
this.defaultTimeoutMs = opts.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
9856
10200
|
}
|
|
9857
10201
|
testLines(text, source, flags, opts = {}) {
|
|
9858
|
-
return new Promise((
|
|
10202
|
+
return new Promise((resolve14, reject) => {
|
|
9859
10203
|
if (opts.signal?.aborted) {
|
|
9860
10204
|
reject(new Error("regex evaluation aborted"));
|
|
9861
10205
|
return;
|
|
@@ -9868,7 +10212,7 @@ var RegexRunner = class {
|
|
|
9868
10212
|
this.killWorker();
|
|
9869
10213
|
reject(new Error(`regex evaluation exceeded ${timeoutMs}ms`));
|
|
9870
10214
|
}, timeoutMs);
|
|
9871
|
-
const entry = { resolve:
|
|
10215
|
+
const entry = { resolve: resolve14, reject, timer };
|
|
9872
10216
|
if (opts.signal) {
|
|
9873
10217
|
entry.signal = opts.signal;
|
|
9874
10218
|
entry.onAbort = () => {
|
|
@@ -9951,7 +10295,7 @@ function throwIfAborted(signal) {
|
|
|
9951
10295
|
throw new DOMException("search aborted by user", "AbortError");
|
|
9952
10296
|
}
|
|
9953
10297
|
function displayRel3(rootDir, full) {
|
|
9954
|
-
return
|
|
10298
|
+
return pathMod5.relative(rootDir, full).replaceAll("\\", "/");
|
|
9955
10299
|
}
|
|
9956
10300
|
async function searchFiles(ctx, startAbs, args) {
|
|
9957
10301
|
throwIfAborted(args.signal);
|
|
@@ -9975,7 +10319,7 @@ async function searchFiles(ctx, startAbs, args) {
|
|
|
9975
10319
|
}
|
|
9976
10320
|
for (const e of entries) {
|
|
9977
10321
|
throwIfAborted(args.signal);
|
|
9978
|
-
const full =
|
|
10322
|
+
const full = pathMod5.join(dir, e.name);
|
|
9979
10323
|
const lower = e.name.toLowerCase();
|
|
9980
10324
|
const hit = re ? re.test(e.name) : lower.includes(needle);
|
|
9981
10325
|
if (hit) {
|
|
@@ -10067,11 +10411,11 @@ async function searchContent(ctx, startAbs, args) {
|
|
|
10067
10411
|
throwIfTimedOut();
|
|
10068
10412
|
if (e.isDirectory()) {
|
|
10069
10413
|
if (!includeDeps && ctx.skipDirNames.has(e.name)) continue;
|
|
10070
|
-
await walk2(
|
|
10414
|
+
await walk2(pathMod5.join(dir, e.name));
|
|
10071
10415
|
continue;
|
|
10072
10416
|
}
|
|
10073
10417
|
if (!e.isFile()) continue;
|
|
10074
|
-
const full =
|
|
10418
|
+
const full = pathMod5.join(dir, e.name);
|
|
10075
10419
|
if (ctx.nameMatch && !ctx.nameMatch(e.name, displayRel3(ctx.rootDir, full))) continue;
|
|
10076
10420
|
if (ctx.isBinaryByName(e.name)) continue;
|
|
10077
10421
|
let fh;
|
|
@@ -10189,7 +10533,7 @@ var SKIP_DIR_NAMES = new Set(
|
|
|
10189
10533
|
);
|
|
10190
10534
|
var BINARY_EXTENSIONS = new Set(DEFAULT_INDEX_EXCLUDES.exts);
|
|
10191
10535
|
function displayRel4(rootDir, full) {
|
|
10192
|
-
return
|
|
10536
|
+
return pathMod6.relative(rootDir, full).replaceAll("\\", "/");
|
|
10193
10537
|
}
|
|
10194
10538
|
function looksLikeAbsoluteSystemPath(raw) {
|
|
10195
10539
|
if (/^[A-Za-z]:[\\/]/.test(raw)) return true;
|
|
@@ -10198,8 +10542,8 @@ function looksLikeAbsoluteSystemPath(raw) {
|
|
|
10198
10542
|
);
|
|
10199
10543
|
}
|
|
10200
10544
|
function pathIsUnder(child, parent) {
|
|
10201
|
-
const rel =
|
|
10202
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
10545
|
+
const rel = pathMod6.relative(parent, child);
|
|
10546
|
+
return rel === "" || !rel.startsWith("..") && !pathMod6.isAbsolute(rel);
|
|
10203
10547
|
}
|
|
10204
10548
|
var GLOB_METACHARS = /[*?{[]/;
|
|
10205
10549
|
function compileNameFilter(filter) {
|
|
@@ -10231,11 +10575,11 @@ function formatBytes(n) {
|
|
|
10231
10575
|
return `${(n / (1024 * 1024 * 1024)).toFixed(2)} GiB`;
|
|
10232
10576
|
}
|
|
10233
10577
|
function registerFilesystemTools(registry, opts) {
|
|
10234
|
-
const rootDir =
|
|
10578
|
+
const rootDir = pathMod6.resolve(opts.rootDir);
|
|
10235
10579
|
const allowWriting = opts.allowWriting !== false;
|
|
10236
10580
|
const outlineThresholdBytes = opts.outlineThresholdBytes ?? DEFAULT_OUTLINE_THRESHOLD_BYTES;
|
|
10237
10581
|
const maxListBytes = opts.maxListBytes ?? DEFAULT_MAX_LIST_BYTES;
|
|
10238
|
-
const normRoot =
|
|
10582
|
+
const normRoot = pathMod6.resolve(rootDir);
|
|
10239
10583
|
const sessionApproved = /* @__PURE__ */ new Set();
|
|
10240
10584
|
const shownSubdirMemory = /* @__PURE__ */ new Set();
|
|
10241
10585
|
function withSubdirMemory(absPath, body) {
|
|
@@ -10268,7 +10612,7 @@ ${body}`;
|
|
|
10268
10612
|
if (pathIsUnder(abs, dir)) return;
|
|
10269
10613
|
}
|
|
10270
10614
|
const stat2 = await safeLstat(abs);
|
|
10271
|
-
const allowPrefix = stat2?.isDirectory() ? abs :
|
|
10615
|
+
const allowPrefix = stat2?.isDirectory() ? abs : pathMod6.dirname(abs);
|
|
10272
10616
|
let pending = inflightGate.get(allowPrefix);
|
|
10273
10617
|
if (!pending) {
|
|
10274
10618
|
const gate = ctx?.confirmationGate ?? pauseGate;
|
|
@@ -10296,7 +10640,7 @@ ${body}`;
|
|
|
10296
10640
|
throw new Error("path must be a non-empty string");
|
|
10297
10641
|
}
|
|
10298
10642
|
if (looksLikeAbsoluteSystemPath(raw)) {
|
|
10299
|
-
const abs =
|
|
10643
|
+
const abs = pathMod6.resolve(raw);
|
|
10300
10644
|
if (pathIsUnder(abs, normRoot)) return abs;
|
|
10301
10645
|
await ensureOutsideSandboxAllowed(abs, intent, toolName, ctx);
|
|
10302
10646
|
return abs;
|
|
@@ -10306,7 +10650,7 @@ ${body}`;
|
|
|
10306
10650
|
normalized = normalized.slice(1);
|
|
10307
10651
|
}
|
|
10308
10652
|
if (normalized.length === 0) normalized = ".";
|
|
10309
|
-
const resolved =
|
|
10653
|
+
const resolved = pathMod6.resolve(rootDir, normalized);
|
|
10310
10654
|
if (!pathIsUnder(resolved, normRoot)) {
|
|
10311
10655
|
throw new Error(
|
|
10312
10656
|
`path escapes sandbox root (${normRoot}): ${raw} \u2014 use an absolute system path like /Users/foo or C:\\Users\\foo to request approved outside-sandbox access`
|
|
@@ -10368,7 +10712,8 @@ ${body}`;
|
|
|
10368
10712
|
if (looksBinary(raw)) {
|
|
10369
10713
|
return `[refused: ${rel} appears to be binary (${formatBytes(sizeBytes)}) \u2014 read_file returns text only. Use get_file_info for stat.]`;
|
|
10370
10714
|
}
|
|
10371
|
-
const text = raw
|
|
10715
|
+
const { text } = decodeFileBuffer(raw);
|
|
10716
|
+
ctx?.readTracker?.markRead(abs);
|
|
10372
10717
|
let lines = text.split(/\r?\n/);
|
|
10373
10718
|
if (lines.length > 0 && lines[lines.length - 1] === "") lines = lines.slice(0, -1);
|
|
10374
10719
|
const totalLines = lines.length;
|
|
@@ -10506,7 +10851,7 @@ ${slice.join("\n")}`);
|
|
|
10506
10851
|
lines.push(line);
|
|
10507
10852
|
emitted++;
|
|
10508
10853
|
if (e.isDirectory() && !skip) {
|
|
10509
|
-
await walk2(
|
|
10854
|
+
await walk2(pathMod6.join(dir, e.name), depth + 1);
|
|
10510
10855
|
}
|
|
10511
10856
|
}
|
|
10512
10857
|
};
|
|
@@ -10666,14 +11011,20 @@ ${slice.join("\n")}`);
|
|
|
10666
11011
|
},
|
|
10667
11012
|
fn: async (args, ctx) => {
|
|
10668
11013
|
const abs = await safePath(args.path, "write_file", ctx, "write");
|
|
10669
|
-
await fs4.mkdir(
|
|
10670
|
-
|
|
11014
|
+
await fs4.mkdir(pathMod6.dirname(abs), { recursive: true });
|
|
11015
|
+
let encoding = "utf8";
|
|
11016
|
+
try {
|
|
11017
|
+
encoding = decodeFileBuffer(await fs4.readFile(abs)).encoding;
|
|
11018
|
+
} catch {
|
|
11019
|
+
}
|
|
11020
|
+
await fs4.writeFile(abs, encodeFile(args.content, encoding));
|
|
11021
|
+
ctx?.readTracker?.markRead(abs);
|
|
10671
11022
|
return `wrote ${args.content.length} chars to ${displayRel4(rootDir, abs)}`;
|
|
10672
11023
|
}
|
|
10673
11024
|
});
|
|
10674
11025
|
registry.register({
|
|
10675
11026
|
name: "edit_file",
|
|
10676
|
-
description: "Apply a SEARCH/REPLACE edit to an existing file. `
|
|
11027
|
+
description: "Apply a SEARCH/REPLACE edit to an existing file. Call `read_file` on this path first this session \u2014 the tool refuses otherwise, since SEARCH must match on-disk bytes exactly. `search` is whitespace-sensitive plain text (no regex) and must be unique in the file; otherwise the edit is refused to avoid surprise rewrites.",
|
|
10677
11028
|
parameters: {
|
|
10678
11029
|
type: "object",
|
|
10679
11030
|
properties: {
|
|
@@ -10683,11 +11034,16 @@ ${slice.join("\n")}`);
|
|
|
10683
11034
|
},
|
|
10684
11035
|
required: ["path", "search", "replace"]
|
|
10685
11036
|
},
|
|
10686
|
-
fn: async (args, ctx) => applyEdit(
|
|
11037
|
+
fn: async (args, ctx) => applyEdit(
|
|
11038
|
+
rootDir,
|
|
11039
|
+
await safePath(args.path, "edit_file", ctx, "write"),
|
|
11040
|
+
args,
|
|
11041
|
+
ctx?.readTracker ? (abs) => ctx.readTracker.hasRead(abs) : void 0
|
|
11042
|
+
)
|
|
10687
11043
|
});
|
|
10688
11044
|
registry.register({
|
|
10689
11045
|
name: "multi_edit",
|
|
10690
|
-
description: "Apply N SEARCH/REPLACE edits across ONE OR MORE files in one call. Edits validate across the full batch before writing. Validation failures leave all files untouched; disk write failures trigger best-effort rollback of files that may have been modified. Per-file edits run in array order, so a later edit can match text inserted by an earlier one. Same per-edit rules as edit_file: `search` is exact text (whitespace sensitive, no regex) and must be unique in its target file at the moment that edit applies. Use this for renames spanning multiple files, cross-file refactors, or any batch where you'd otherwise loop edit_file.",
|
|
11046
|
+
description: "Apply N SEARCH/REPLACE edits across ONE OR MORE files in one call. Every target file must have been `read_file`'d this session \u2014 the tool refuses the whole batch otherwise. Edits validate across the full batch before writing. Validation failures leave all files untouched; disk write failures trigger best-effort rollback of files that may have been modified. Per-file edits run in array order, so a later edit can match text inserted by an earlier one. Same per-edit rules as edit_file: `search` is exact text (whitespace sensitive, no regex) and must be unique in its target file at the moment that edit applies. Use this for renames spanning multiple files, cross-file refactors, or any batch where you'd otherwise loop edit_file.",
|
|
10691
11047
|
parameters: {
|
|
10692
11048
|
type: "object",
|
|
10693
11049
|
properties: {
|
|
@@ -10721,7 +11077,11 @@ ${slice.join("\n")}`);
|
|
|
10721
11077
|
replace: e?.replace
|
|
10722
11078
|
}))
|
|
10723
11079
|
);
|
|
10724
|
-
return applyMultiEdit(
|
|
11080
|
+
return applyMultiEdit(
|
|
11081
|
+
rootDir,
|
|
11082
|
+
resolved,
|
|
11083
|
+
ctx?.readTracker ? (abs) => ctx.readTracker.hasRead(abs) : void 0
|
|
11084
|
+
);
|
|
10725
11085
|
}
|
|
10726
11086
|
});
|
|
10727
11087
|
registry.register({
|
|
@@ -10752,7 +11112,7 @@ ${slice.join("\n")}`);
|
|
|
10752
11112
|
fn: async (args, ctx) => {
|
|
10753
11113
|
const src = await safePath(args.source, "move_file", ctx, "write");
|
|
10754
11114
|
const dst = await safePath(args.destination, "move_file", ctx, "write");
|
|
10755
|
-
await fs4.mkdir(
|
|
11115
|
+
await fs4.mkdir(pathMod6.dirname(dst), { recursive: true });
|
|
10756
11116
|
await fs4.rename(src, dst);
|
|
10757
11117
|
return `moved ${displayRel4(rootDir, src)} \u2192 ${displayRel4(rootDir, dst)}`;
|
|
10758
11118
|
}
|
|
@@ -10820,7 +11180,7 @@ ${slice.join("\n")}`);
|
|
|
10820
11180
|
fn: async (args, ctx) => {
|
|
10821
11181
|
const src = await safePath(args.source, "copy_file", ctx);
|
|
10822
11182
|
const dst = await safePath(args.destination, "copy_file", ctx, "write");
|
|
10823
|
-
await fs4.mkdir(
|
|
11183
|
+
await fs4.mkdir(pathMod6.dirname(dst), { recursive: true });
|
|
10824
11184
|
await fs4.cp(src, dst, { recursive: true, force: false, errorOnExist: true });
|
|
10825
11185
|
return `copied ${displayRel4(rootDir, src)} \u2192 ${displayRel4(rootDir, dst)}`;
|
|
10826
11186
|
}
|
|
@@ -11920,7 +12280,7 @@ ${hint}` : formatted;
|
|
|
11920
12280
|
return parentRegistry;
|
|
11921
12281
|
}
|
|
11922
12282
|
function forkRegistryExcluding(parent, exclude) {
|
|
11923
|
-
const child = new ToolRegistry();
|
|
12283
|
+
const child = new ToolRegistry({ rateLimit: parent.rateLimitPolicy });
|
|
11924
12284
|
for (const spec of parent.specs()) {
|
|
11925
12285
|
const name = spec.function.name;
|
|
11926
12286
|
if (exclude.has(name)) continue;
|
|
@@ -11932,7 +12292,7 @@ function forkRegistryExcluding(parent, exclude) {
|
|
|
11932
12292
|
return child;
|
|
11933
12293
|
}
|
|
11934
12294
|
function forkRegistryWithAllowList(parent, allow, alsoExclude) {
|
|
11935
|
-
const child = new ToolRegistry();
|
|
12295
|
+
const child = new ToolRegistry({ rateLimit: parent.rateLimitPolicy });
|
|
11936
12296
|
for (const spec of parent.specs()) {
|
|
11937
12297
|
const name = spec.function.name;
|
|
11938
12298
|
if (!allow.has(name)) continue;
|
|
@@ -12038,11 +12398,11 @@ var SubagentTelemetry = class {
|
|
|
12038
12398
|
};
|
|
12039
12399
|
|
|
12040
12400
|
// src/tools/shell.ts
|
|
12041
|
-
import * as
|
|
12401
|
+
import * as pathMod11 from "path";
|
|
12042
12402
|
|
|
12043
12403
|
// src/tools/jobs.ts
|
|
12044
12404
|
import { spawn as spawn2 } from "child_process";
|
|
12045
|
-
import * as
|
|
12405
|
+
import * as pathMod7 from "path";
|
|
12046
12406
|
function killProcessTree(pid, signal) {
|
|
12047
12407
|
if (process.platform === "win32") {
|
|
12048
12408
|
const args = ["/pid", String(pid), "/T"];
|
|
@@ -12102,7 +12462,7 @@ var JobRegistry = class {
|
|
|
12102
12462
|
const maxBytes = opts.maxBufferBytes ?? DEFAULT_OUTPUT_CAP_BYTES;
|
|
12103
12463
|
const { bin, args, spawnOverrides } = prepareSpawn(argv);
|
|
12104
12464
|
const spawnOpts = {
|
|
12105
|
-
cwd:
|
|
12465
|
+
cwd: pathMod7.resolve(opts.cwd),
|
|
12106
12466
|
shell: false,
|
|
12107
12467
|
windowsHide: true,
|
|
12108
12468
|
env: process.env,
|
|
@@ -12287,16 +12647,16 @@ ${job.output.slice(start)}`;
|
|
|
12287
12647
|
let wakeOutput = null;
|
|
12288
12648
|
if (waitFor === "output-or-exit") {
|
|
12289
12649
|
racers.push(
|
|
12290
|
-
new Promise((
|
|
12291
|
-
wakeOutput =
|
|
12292
|
-
job.outputWaiters.add(
|
|
12650
|
+
new Promise((resolve14) => {
|
|
12651
|
+
wakeOutput = resolve14;
|
|
12652
|
+
job.outputWaiters.add(resolve14);
|
|
12293
12653
|
})
|
|
12294
12654
|
);
|
|
12295
12655
|
}
|
|
12296
12656
|
let timer = null;
|
|
12297
12657
|
racers.push(
|
|
12298
|
-
new Promise((
|
|
12299
|
-
timer = setTimeout(
|
|
12658
|
+
new Promise((resolve14) => {
|
|
12659
|
+
timer = setTimeout(resolve14, timeoutMs);
|
|
12300
12660
|
})
|
|
12301
12661
|
);
|
|
12302
12662
|
await Promise.race(racers);
|
|
@@ -12406,13 +12766,13 @@ function latestOutputSince(before, after) {
|
|
|
12406
12766
|
// src/tools/shell/exec.ts
|
|
12407
12767
|
import { spawn as spawn4, spawnSync } from "child_process";
|
|
12408
12768
|
import { existsSync as existsSync9, statSync as statSync5 } from "fs";
|
|
12409
|
-
import * as
|
|
12769
|
+
import * as pathMod10 from "path";
|
|
12410
12770
|
|
|
12411
12771
|
// src/tools/shell-chain.ts
|
|
12412
12772
|
import { spawn as spawn3 } from "child_process";
|
|
12413
|
-
import { closeSync, openSync } from "fs";
|
|
12773
|
+
import { constants as constants2, closeSync, lstatSync, openSync, realpathSync } from "fs";
|
|
12414
12774
|
import { devNull } from "os";
|
|
12415
|
-
import * as
|
|
12775
|
+
import * as pathMod8 from "path";
|
|
12416
12776
|
var UnsupportedSyntaxError = class extends Error {
|
|
12417
12777
|
constructor(detail) {
|
|
12418
12778
|
super(`run_command: ${detail}`);
|
|
@@ -12677,7 +13037,49 @@ function isNullDeviceAlias(target) {
|
|
|
12677
13037
|
if (process.platform === "win32" && lower === "nul") return true;
|
|
12678
13038
|
return false;
|
|
12679
13039
|
}
|
|
13040
|
+
function pathIsUnder2(child, parent) {
|
|
13041
|
+
const rel = pathMod8.relative(parent, child);
|
|
13042
|
+
return rel === "" || !rel.startsWith("..") && !pathMod8.isAbsolute(rel);
|
|
13043
|
+
}
|
|
13044
|
+
function openFlags(mode) {
|
|
13045
|
+
const noFollow = "O_NOFOLLOW" in constants2 ? constants2.O_NOFOLLOW : 0;
|
|
13046
|
+
if (mode === "r") return constants2.O_RDONLY | noFollow;
|
|
13047
|
+
if (mode === "w") return constants2.O_WRONLY | constants2.O_CREAT | constants2.O_TRUNC | noFollow;
|
|
13048
|
+
return constants2.O_WRONLY | constants2.O_CREAT | constants2.O_APPEND | noFollow;
|
|
13049
|
+
}
|
|
13050
|
+
function ensureUnderSandbox(path2, sandboxRoot, target) {
|
|
13051
|
+
if (!pathIsUnder2(path2, sandboxRoot)) {
|
|
13052
|
+
throw new Error(
|
|
13053
|
+
`redirect target "${target}" resolves outside the workspace sandbox (${sandboxRoot})`
|
|
13054
|
+
);
|
|
13055
|
+
}
|
|
13056
|
+
}
|
|
13057
|
+
function resolveRedirectTarget(target, cwd) {
|
|
13058
|
+
const lexicalRoot = pathMod8.resolve(cwd);
|
|
13059
|
+
const sandboxRoot = realpathSync(lexicalRoot);
|
|
13060
|
+
const resolved = pathMod8.resolve(lexicalRoot, target);
|
|
13061
|
+
ensureUnderSandbox(resolved, lexicalRoot, target);
|
|
13062
|
+
try {
|
|
13063
|
+
const stat2 = lstatSync(resolved);
|
|
13064
|
+
if (stat2.isSymbolicLink()) {
|
|
13065
|
+
throw new Error(`redirect target "${target}" is a symbolic link`);
|
|
13066
|
+
}
|
|
13067
|
+
ensureUnderSandbox(realpathSync(resolved), sandboxRoot, target);
|
|
13068
|
+
} catch (err) {
|
|
13069
|
+
const code = err.code;
|
|
13070
|
+
if (code !== "ENOENT") throw err;
|
|
13071
|
+
ensureUnderSandbox(realpathSync(pathMod8.dirname(resolved)), sandboxRoot, target);
|
|
13072
|
+
}
|
|
13073
|
+
return resolved;
|
|
13074
|
+
}
|
|
13075
|
+
function validateRedirectTargets(redirects, cwd) {
|
|
13076
|
+
for (const r of redirects) {
|
|
13077
|
+
if (r.kind === "2>&1" || !r.target || isNullDeviceAlias(r.target)) continue;
|
|
13078
|
+
resolveRedirectTarget(r.target, cwd);
|
|
13079
|
+
}
|
|
13080
|
+
}
|
|
12680
13081
|
function openRedirects(redirects, cwd) {
|
|
13082
|
+
validateRedirectTargets(redirects, cwd);
|
|
12681
13083
|
let stdinFd = null;
|
|
12682
13084
|
let stdoutFd = null;
|
|
12683
13085
|
let stderrFd = null;
|
|
@@ -12685,8 +13087,8 @@ function openRedirects(redirects, cwd) {
|
|
|
12685
13087
|
let bothFd = null;
|
|
12686
13088
|
const toClose = [];
|
|
12687
13089
|
const open = (target, flags) => {
|
|
12688
|
-
const resolved = isNullDeviceAlias(target) ? devNull :
|
|
12689
|
-
const fd = openSync(resolved, flags);
|
|
13090
|
+
const resolved = isNullDeviceAlias(target) ? devNull : resolveRedirectTarget(target, cwd);
|
|
13091
|
+
const fd = openSync(resolved, openFlags(flags), 438);
|
|
12690
13092
|
toClose.push(fd);
|
|
12691
13093
|
return fd;
|
|
12692
13094
|
};
|
|
@@ -12793,9 +13195,9 @@ async function runPipeGroup(segments, opts) {
|
|
|
12793
13195
|
}
|
|
12794
13196
|
const exits = await Promise.all(
|
|
12795
13197
|
children.map(
|
|
12796
|
-
(c) => new Promise((
|
|
12797
|
-
c.once("error", () =>
|
|
12798
|
-
c.once("close", (code) =>
|
|
13198
|
+
(c) => new Promise((resolve14) => {
|
|
13199
|
+
c.once("error", () => resolve14(null));
|
|
13200
|
+
c.once("close", (code) => resolve14(code));
|
|
12799
13201
|
})
|
|
12800
13202
|
)
|
|
12801
13203
|
);
|
|
@@ -12840,7 +13242,7 @@ var OutputBuffer = class {
|
|
|
12840
13242
|
|
|
12841
13243
|
// src/tools/shell/parse.ts
|
|
12842
13244
|
import { homedir as homedir7 } from "os";
|
|
12843
|
-
import * as
|
|
13245
|
+
import * as pathMod9 from "path";
|
|
12844
13246
|
|
|
12845
13247
|
// packages/core-utils/src/tildeify.ts
|
|
12846
13248
|
import { homedir as homedir6 } from "os";
|
|
@@ -13045,16 +13447,16 @@ function resolveSensitivePath(token, projectRoot) {
|
|
|
13045
13447
|
return null;
|
|
13046
13448
|
let expanded = token;
|
|
13047
13449
|
if (expanded.startsWith("~")) {
|
|
13048
|
-
expanded =
|
|
13450
|
+
expanded = pathMod9.join(homedir7(), expanded.slice(1));
|
|
13049
13451
|
}
|
|
13050
|
-
return
|
|
13452
|
+
return pathMod9.resolve(projectRoot, expanded);
|
|
13051
13453
|
}
|
|
13052
13454
|
function expandPrefix(prefix) {
|
|
13053
|
-
if (prefix.startsWith("~")) return
|
|
13054
|
-
return
|
|
13455
|
+
if (prefix.startsWith("~")) return pathMod9.join(homedir7(), prefix.slice(1));
|
|
13456
|
+
return pathMod9.resolve(prefix);
|
|
13055
13457
|
}
|
|
13056
13458
|
function pathStartsWithPrefix(normalized, prefix) {
|
|
13057
|
-
return normalized === prefix || normalized.startsWith(`${prefix}${
|
|
13459
|
+
return normalized === prefix || normalized.startsWith(`${prefix}${pathMod9.sep}`);
|
|
13058
13460
|
}
|
|
13059
13461
|
function matchesGlob(name, pattern) {
|
|
13060
13462
|
const regex = new RegExp(
|
|
@@ -13069,17 +13471,39 @@ function hasSensitivePathArgs(argv, projectRoot, extraPrefixes = [], extraPatter
|
|
|
13069
13471
|
for (const token of argv) {
|
|
13070
13472
|
const resolved = resolveSensitivePath(token, projectRoot);
|
|
13071
13473
|
if (!resolved) continue;
|
|
13072
|
-
const normalized =
|
|
13474
|
+
const normalized = pathMod9.normalize(resolved);
|
|
13073
13475
|
for (const pfx of prefixes) {
|
|
13074
13476
|
if (pathStartsWithPrefix(normalized, pfx)) return true;
|
|
13075
13477
|
}
|
|
13076
|
-
const base =
|
|
13478
|
+
const base = pathMod9.basename(normalized);
|
|
13077
13479
|
for (const pat of patterns) {
|
|
13078
13480
|
if (matchesGlob(base, pat)) return true;
|
|
13079
13481
|
}
|
|
13080
13482
|
}
|
|
13081
13483
|
return false;
|
|
13082
13484
|
}
|
|
13485
|
+
function pathIsUnder3(child, parent) {
|
|
13486
|
+
const rel = pathMod9.relative(parent, child);
|
|
13487
|
+
return rel === "" || !rel.startsWith("..") && !pathMod9.isAbsolute(rel);
|
|
13488
|
+
}
|
|
13489
|
+
function redirectTargets(chain) {
|
|
13490
|
+
const targets = [];
|
|
13491
|
+
for (const seg of chain.segments) {
|
|
13492
|
+
for (const r of seg.redirects) {
|
|
13493
|
+
if (r.kind === "2>&1" || !r.target || isNullDeviceAlias(r.target)) continue;
|
|
13494
|
+
targets.push(r.target);
|
|
13495
|
+
}
|
|
13496
|
+
}
|
|
13497
|
+
return targets;
|
|
13498
|
+
}
|
|
13499
|
+
function redirectsEscapeSandbox(chain, projectRoot) {
|
|
13500
|
+
const root = pathMod9.resolve(projectRoot);
|
|
13501
|
+
for (const target of redirectTargets(chain)) {
|
|
13502
|
+
const resolved = pathMod9.resolve(root, target);
|
|
13503
|
+
if (!pathIsUnder3(resolved, root)) return true;
|
|
13504
|
+
}
|
|
13505
|
+
return false;
|
|
13506
|
+
}
|
|
13083
13507
|
function isAllowed(cmd, extra = [], projectRoot, sensitivePathConfig) {
|
|
13084
13508
|
let argv;
|
|
13085
13509
|
try {
|
|
@@ -13121,6 +13545,18 @@ function isCommandAllowed(cmd, extra = [], projectRoot, sensitivePathConfig) {
|
|
|
13121
13545
|
return false;
|
|
13122
13546
|
}
|
|
13123
13547
|
if (chain === null) return isAllowed(cmd, extra, projectRoot, sensitivePathConfig);
|
|
13548
|
+
const targets = redirectTargets(chain);
|
|
13549
|
+
if (targets.length > 0 && !projectRoot) return false;
|
|
13550
|
+
if (projectRoot) {
|
|
13551
|
+
if (redirectsEscapeSandbox(chain, projectRoot)) return false;
|
|
13552
|
+
if (hasSensitivePathArgs(
|
|
13553
|
+
targets,
|
|
13554
|
+
projectRoot,
|
|
13555
|
+
sensitivePathConfig?.prefixes,
|
|
13556
|
+
sensitivePathConfig?.patterns
|
|
13557
|
+
))
|
|
13558
|
+
return false;
|
|
13559
|
+
}
|
|
13124
13560
|
return chainAllowed(chain, (seg) => isAllowed(seg, extra, projectRoot, sensitivePathConfig));
|
|
13125
13561
|
}
|
|
13126
13562
|
|
|
@@ -13189,7 +13625,7 @@ async function runCommand(cmd, opts) {
|
|
|
13189
13625
|
};
|
|
13190
13626
|
const { bin, args, spawnOverrides } = prepareSpawn(argv, { env: normalizedEnv });
|
|
13191
13627
|
const effectiveSpawnOpts = { ...spawnOpts, ...spawnOverrides };
|
|
13192
|
-
return await new Promise((
|
|
13628
|
+
return await new Promise((resolve14, reject) => {
|
|
13193
13629
|
let child;
|
|
13194
13630
|
try {
|
|
13195
13631
|
child = spawn4(bin, args, effectiveSpawnOpts);
|
|
@@ -13243,7 +13679,7 @@ async function runCommand(cmd, opts) {
|
|
|
13243
13679
|
const output = buf.length > maxChars ? `${buf.slice(0, maxChars)}
|
|
13244
13680
|
|
|
13245
13681
|
[\u2026 truncated ${buf.length - maxChars} chars \u2026]` : buf;
|
|
13246
|
-
|
|
13682
|
+
resolve14({ exitCode: code, output, timedOut });
|
|
13247
13683
|
});
|
|
13248
13684
|
});
|
|
13249
13685
|
}
|
|
@@ -13265,16 +13701,16 @@ function resolveExecutable(cmd, opts = {}) {
|
|
|
13265
13701
|
const platform = opts.platform ?? process.platform;
|
|
13266
13702
|
if (platform !== "win32") return cmd;
|
|
13267
13703
|
if (!cmd) return cmd;
|
|
13268
|
-
if (cmd.includes("/") || cmd.includes("\\") ||
|
|
13269
|
-
if (
|
|
13704
|
+
if (cmd.includes("/") || cmd.includes("\\") || pathMod10.isAbsolute(cmd)) return cmd;
|
|
13705
|
+
if (pathMod10.extname(cmd)) return cmd;
|
|
13270
13706
|
const env = opts.env ?? process.env;
|
|
13271
13707
|
const pathExt = (getEnvCaseInsensitive(env, "PATHEXT") ?? ".COM;.EXE;.BAT;.CMD").split(";").map((e) => e.trim()).filter(Boolean);
|
|
13272
|
-
const delimiter2 = opts.pathDelimiter ?? (platform === "win32" ? ";" :
|
|
13708
|
+
const delimiter2 = opts.pathDelimiter ?? (platform === "win32" ? ";" : pathMod10.delimiter);
|
|
13273
13709
|
const pathDirs = (getEnvCaseInsensitive(env, "PATH") ?? "").split(delimiter2).filter(Boolean);
|
|
13274
13710
|
const isFile = opts.isFile ?? defaultIsFile;
|
|
13275
13711
|
for (const dir of pathDirs) {
|
|
13276
13712
|
for (const ext of pathExt) {
|
|
13277
|
-
const full =
|
|
13713
|
+
const full = pathMod10.win32.join(dir, cmd + ext);
|
|
13278
13714
|
if (isFile(full)) return full;
|
|
13279
13715
|
}
|
|
13280
13716
|
}
|
|
@@ -13390,8 +13826,8 @@ function withUtf8Codepage(cmdline) {
|
|
|
13390
13826
|
function isBareWindowsName(s) {
|
|
13391
13827
|
if (!s) return false;
|
|
13392
13828
|
if (s.includes("/") || s.includes("\\")) return false;
|
|
13393
|
-
if (
|
|
13394
|
-
if (
|
|
13829
|
+
if (pathMod10.isAbsolute(s)) return false;
|
|
13830
|
+
if (pathMod10.extname(s)) return false;
|
|
13395
13831
|
return true;
|
|
13396
13832
|
}
|
|
13397
13833
|
function quoteForCmdExe(arg) {
|
|
@@ -13412,7 +13848,7 @@ var NeedsConfirmationError = class extends Error {
|
|
|
13412
13848
|
}
|
|
13413
13849
|
};
|
|
13414
13850
|
function registerShellTools(registry, opts) {
|
|
13415
|
-
const rootDir =
|
|
13851
|
+
const rootDir = pathMod11.resolve(opts.rootDir);
|
|
13416
13852
|
const timeoutSec = opts.timeoutSec ?? DEFAULT_TIMEOUT_SEC;
|
|
13417
13853
|
const maxOutputChars = opts.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
|
|
13418
13854
|
const jobs = opts.jobs ?? new JobRegistry();
|
|
@@ -13423,7 +13859,7 @@ function registerShellTools(registry, opts) {
|
|
|
13423
13859
|
const isAllowAll = typeof opts.allowAll === "function" ? opts.allowAll : () => opts.allowAll === true;
|
|
13424
13860
|
registry.register({
|
|
13425
13861
|
name: "run_command",
|
|
13426
|
-
description: 'Run a shell command in the project root; returns combined stdout+stderr. Allowlisted read-only / test / lint / typecheck commands run immediately; mutating / network / install commands gate on user confirmation.\n\nNo real shell \u2014 argv parsed natively for cross-platform parity:\n\u2022 Supported: chains `|`/`||`/`&&`/`;` (each segment allowlist-checked) and file redirects `>`/`>>`/`<`/`2>`/`2>>`/`2>&1`/`&>`.\n\u2022 Rejected: background `&`, heredoc `<<`, `$(\u2026)`, subshells, `$VAR` expansion, glob expansion. Quote operator chars as literals (`grep "a|b" file`).\n\u2022 `cd` does NOT persist \u2014 between calls OR within a chain. Use `npm --prefix <dir>`, `git -C <dir>`, `cargo -C <dir>` instead.\n\u2022 Filter at source \u2014 `grep -c` / `wc -l` / narrower paths over unbounded dumps.',
|
|
13862
|
+
description: 'Run a shell command in the project root; returns combined stdout+stderr. Allowlisted read-only / test / lint / typecheck commands run immediately; mutating / network / install commands gate on user confirmation.\n\nDO NOT use run_command for file operations \u2014 use write_file, edit_file, multi_edit, copy_file, move_file, or delete_file instead. Shell utilities (echo, cp, sed, cat, tee, perl, python -c, etc.) bypass validation, lack rollback, and will trigger user confirmation gates that waste turns.\n\nNo real shell \u2014 argv parsed natively for cross-platform parity:\n\u2022 Supported: chains `|`/`||`/`&&`/`;` (each segment allowlist-checked) and file redirects `>`/`>>`/`<`/`2>`/`2>>`/`2>&1`/`&>`.\n\u2022 Rejected: background `&`, heredoc `<<`, `$(\u2026)`, subshells, `$VAR` expansion, glob expansion. Quote operator chars as literals (`grep "a|b" file`).\n\u2022 `cd` does NOT persist \u2014 between calls OR within a chain. Use `npm --prefix <dir>`, `git -C <dir>`, `cargo -C <dir>` instead.\n\u2022 Filter at source \u2014 `grep -c` / `wc -l` / narrower paths over unbounded dumps.',
|
|
13427
13863
|
// Plan-mode gate: allow allowlisted commands through (git status,
|
|
13428
13864
|
// cargo check, ls, grep …) so the model can actually investigate
|
|
13429
13865
|
// during planning. Anything that would otherwise trigger a
|
|
@@ -13625,11 +14061,11 @@ function registerShellTools(registry, opts) {
|
|
|
13625
14061
|
return registry;
|
|
13626
14062
|
}
|
|
13627
14063
|
function resolveCwdInsideRoot(rootDir, raw) {
|
|
13628
|
-
const root =
|
|
14064
|
+
const root = pathMod11.resolve(rootDir);
|
|
13629
14065
|
if (!raw || !raw.trim()) return root;
|
|
13630
|
-
const resolved =
|
|
13631
|
-
const rel =
|
|
13632
|
-
if (rel.startsWith("..") ||
|
|
14066
|
+
const resolved = pathMod11.resolve(root, raw);
|
|
14067
|
+
const rel = pathMod11.relative(root, resolved);
|
|
14068
|
+
if (rel.startsWith("..") || pathMod11.isAbsolute(rel)) {
|
|
13633
14069
|
throw new Error(
|
|
13634
14070
|
`run_background: cwd "${raw}" resolves outside the workspace root (${root}). Pass a workspace-relative path.`
|
|
13635
14071
|
);
|
|
@@ -13677,17 +14113,20 @@ ${r.output}` : header;
|
|
|
13677
14113
|
}
|
|
13678
14114
|
|
|
13679
14115
|
// src/tools/web.ts
|
|
14116
|
+
import { lookup } from "dns/promises";
|
|
14117
|
+
import { isIP } from "net";
|
|
13680
14118
|
import { parse as parseHtml } from "node-html-parser";
|
|
13681
14119
|
var DEFAULT_FETCH_MAX_CHARS = 32e3;
|
|
13682
14120
|
var DEFAULT_FETCH_TIMEOUT_MS = 15e3;
|
|
13683
14121
|
var DEFAULT_TOPK = 5;
|
|
13684
14122
|
var FETCH_MAX_BYTES = 10 * 1024 * 1024;
|
|
13685
14123
|
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";
|
|
13686
|
-
var
|
|
14124
|
+
var BING_ENDPOINT = "https://cn.bing.com/search";
|
|
13687
14125
|
var METASO_ENDPOINT = "https://metaso.cn/api/v1";
|
|
13688
14126
|
var TAVILY_ENDPOINT = "https://api.tavily.com/search";
|
|
13689
14127
|
var PERPLEXITY_ENDPOINT = "https://api.perplexity.ai/chat/completions";
|
|
13690
14128
|
var EXA_ENDPOINT = "https://api.exa.ai/answer";
|
|
14129
|
+
var FETCH_MAX_REDIRECTS = 5;
|
|
13691
14130
|
function searchStatusError(status) {
|
|
13692
14131
|
if (status === 429) return t("webErrors.rateLimit429");
|
|
13693
14132
|
if (status === 403) return t("webErrors.forbidden403");
|
|
@@ -13700,6 +14139,63 @@ function fetchStatusError(status, url) {
|
|
|
13700
14139
|
if (status >= 500 && status <= 599) return t("webErrors.fetchServerError5xx", { status, url });
|
|
13701
14140
|
return t("webErrors.fetchStatus", { status, url });
|
|
13702
14141
|
}
|
|
14142
|
+
function parseIpv4(address) {
|
|
14143
|
+
const parts = address.split(".");
|
|
14144
|
+
if (parts.length !== 4) return null;
|
|
14145
|
+
let out = 0;
|
|
14146
|
+
for (const part of parts) {
|
|
14147
|
+
if (!/^\d+$/.test(part)) return null;
|
|
14148
|
+
const n = Number(part);
|
|
14149
|
+
if (!Number.isInteger(n) || n < 0 || n > 255) return null;
|
|
14150
|
+
out = (out << 8) + n;
|
|
14151
|
+
}
|
|
14152
|
+
return out >>> 0;
|
|
14153
|
+
}
|
|
14154
|
+
function ipv4InRange(value, base, bits) {
|
|
14155
|
+
const parsed = parseIpv4(base);
|
|
14156
|
+
if (parsed === null) return false;
|
|
14157
|
+
const mask = bits === 0 ? 0 : 4294967295 << 32 - bits >>> 0;
|
|
14158
|
+
return (value & mask) === (parsed & mask);
|
|
14159
|
+
}
|
|
14160
|
+
function isPrivateIpv4(address) {
|
|
14161
|
+
const value = parseIpv4(address);
|
|
14162
|
+
if (value === null) return false;
|
|
14163
|
+
return ipv4InRange(value, "0.0.0.0", 8) || ipv4InRange(value, "10.0.0.0", 8) || ipv4InRange(value, "100.64.0.0", 10) || ipv4InRange(value, "127.0.0.0", 8) || ipv4InRange(value, "169.254.0.0", 16) || ipv4InRange(value, "172.16.0.0", 12) || ipv4InRange(value, "192.0.0.0", 24) || ipv4InRange(value, "192.0.2.0", 24) || ipv4InRange(value, "192.168.0.0", 16) || ipv4InRange(value, "198.18.0.0", 15) || ipv4InRange(value, "198.51.100.0", 24) || ipv4InRange(value, "203.0.113.0", 24) || ipv4InRange(value, "224.0.0.0", 4) || ipv4InRange(value, "240.0.0.0", 4);
|
|
14164
|
+
}
|
|
14165
|
+
function normalizeIpv6(address) {
|
|
14166
|
+
return address.toLowerCase().replace(/(^|:)0+([0-9a-f])/g, "$1$2");
|
|
14167
|
+
}
|
|
14168
|
+
function isPrivateIpv6(address) {
|
|
14169
|
+
const normalized = normalizeIpv6(address);
|
|
14170
|
+
const mapped = /^::ffff:(?:0+:)?(\d+\.\d+\.\d+\.\d+)$/i.exec(normalized);
|
|
14171
|
+
if (mapped) return isPrivateIpv4(mapped[1]);
|
|
14172
|
+
return normalized === "::" || normalized === "::1" || normalized.startsWith("fc") || normalized.startsWith("fd") || normalized.startsWith("fe8") || normalized.startsWith("fe9") || normalized.startsWith("fea") || normalized.startsWith("feb") || normalized.startsWith("ff");
|
|
14173
|
+
}
|
|
14174
|
+
function isInternalAddress(address) {
|
|
14175
|
+
const family = isIP(address);
|
|
14176
|
+
if (family === 4) return isPrivateIpv4(address);
|
|
14177
|
+
if (family === 6) return isPrivateIpv6(address);
|
|
14178
|
+
return false;
|
|
14179
|
+
}
|
|
14180
|
+
async function assertPublicHttpUrl(rawUrl) {
|
|
14181
|
+
const url = new URL(rawUrl);
|
|
14182
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
14183
|
+
throw new Error(`web_fetch refuses non-HTTP URL: ${url.protocol}`);
|
|
14184
|
+
}
|
|
14185
|
+
const host = url.hostname;
|
|
14186
|
+
const literal = isIP(host);
|
|
14187
|
+
const addresses = literal ? [host] : (await lookup(host, { all: true, verbatim: true })).map((entry) => entry.address);
|
|
14188
|
+
if (addresses.length === 0 || addresses.some(isInternalAddress)) {
|
|
14189
|
+
throw new Error(`web_fetch refuses internal or reserved host: ${host}`);
|
|
14190
|
+
}
|
|
14191
|
+
return url;
|
|
14192
|
+
}
|
|
14193
|
+
function redirectLocation(resp, currentUrl) {
|
|
14194
|
+
if (resp.status < 300 || resp.status > 399) return null;
|
|
14195
|
+
const location = resp.headers.get("location");
|
|
14196
|
+
if (!location) return null;
|
|
14197
|
+
return new URL(location, currentUrl).toString();
|
|
14198
|
+
}
|
|
13703
14199
|
async function webSearch(query, opts = {}) {
|
|
13704
14200
|
if (opts.engine === "metaso") {
|
|
13705
14201
|
return searchMetaso(query, opts);
|
|
@@ -13716,29 +14212,29 @@ async function webSearch(query, opts = {}) {
|
|
|
13716
14212
|
if (opts.engine === "exa") {
|
|
13717
14213
|
return searchExa(query, opts);
|
|
13718
14214
|
}
|
|
13719
|
-
return
|
|
14215
|
+
return searchBing(query, opts);
|
|
13720
14216
|
}
|
|
13721
|
-
async function
|
|
14217
|
+
async function searchBing(query, opts = {}) {
|
|
13722
14218
|
const topK = Math.max(1, Math.min(10, opts.topK ?? DEFAULT_TOPK));
|
|
13723
|
-
const resp = await fetch(`${
|
|
14219
|
+
const resp = await fetch(`${BING_ENDPOINT}?q=${encodeURIComponent(query)}`, {
|
|
13724
14220
|
headers: {
|
|
13725
14221
|
"User-Agent": USER_AGENT,
|
|
13726
14222
|
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9",
|
|
13727
|
-
"Accept-Language": "
|
|
14223
|
+
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8"
|
|
13728
14224
|
},
|
|
13729
14225
|
signal: opts.signal,
|
|
13730
14226
|
redirect: "follow"
|
|
13731
14227
|
});
|
|
13732
14228
|
if (!resp.ok) throw new Error(searchStatusError(resp.status));
|
|
13733
14229
|
const html = await resp.text();
|
|
13734
|
-
const results =
|
|
14230
|
+
const results = parseBingResults(html).slice(0, topK);
|
|
13735
14231
|
if (results.length === 0) {
|
|
13736
14232
|
if (/no results found|did not match any documents/i.test(html)) return [];
|
|
13737
14233
|
if (/captcha|verify you are human|access denied|forbidden/i.test(html)) {
|
|
13738
|
-
throw new Error(t("webErrors.
|
|
14234
|
+
throw new Error(t("webErrors.bingBlocked"));
|
|
13739
14235
|
}
|
|
13740
14236
|
throw new Error(
|
|
13741
|
-
t("webErrors.
|
|
14237
|
+
t("webErrors.bingNoResults", {
|
|
13742
14238
|
chars: html.length,
|
|
13743
14239
|
preview: html.slice(0, 120).replace(/\s+/g, " ")
|
|
13744
14240
|
})
|
|
@@ -14055,35 +14551,19 @@ function parseSearxngHtmlResults(html) {
|
|
|
14055
14551
|
}
|
|
14056
14552
|
return results;
|
|
14057
14553
|
}
|
|
14058
|
-
function
|
|
14059
|
-
const
|
|
14060
|
-
const titleAnchorRe = /<a\b[^>]*\bclass="title"[^>]*>[\s\S]*?<\/a>/g;
|
|
14061
|
-
let m;
|
|
14062
|
-
while (true) {
|
|
14063
|
-
m = titleAnchorRe.exec(html);
|
|
14064
|
-
if (m === null) break;
|
|
14065
|
-
titles.push(m[0]);
|
|
14066
|
-
}
|
|
14067
|
-
const snippets = [];
|
|
14068
|
-
const snippetRe = /<p\b[^>]*\bclass="s"[^>]*>([\s\S]*?)<\/p>/g;
|
|
14069
|
-
while (true) {
|
|
14070
|
-
m = snippetRe.exec(html);
|
|
14071
|
-
if (m === null) break;
|
|
14072
|
-
snippets.push(m[1] ?? "");
|
|
14073
|
-
}
|
|
14074
|
-
const hrefRe = /href="([^"]+)"/;
|
|
14075
|
-
const innerRe = /<a\b[^>]*>([\s\S]*?)<\/a>/;
|
|
14554
|
+
function parseBingResults(html) {
|
|
14555
|
+
const root = parseHtml(html);
|
|
14076
14556
|
const results = [];
|
|
14077
|
-
for (
|
|
14078
|
-
const anchor =
|
|
14079
|
-
|
|
14080
|
-
const
|
|
14081
|
-
if (!
|
|
14082
|
-
|
|
14083
|
-
|
|
14084
|
-
|
|
14085
|
-
|
|
14086
|
-
});
|
|
14557
|
+
for (const li of root.querySelectorAll("li.b_algo")) {
|
|
14558
|
+
const anchor = li.querySelector("h2 a[href]");
|
|
14559
|
+
if (!anchor) continue;
|
|
14560
|
+
const href = anchor.getAttribute("href");
|
|
14561
|
+
if (!href) continue;
|
|
14562
|
+
const title = anchor.textContent.trim();
|
|
14563
|
+
if (!title) continue;
|
|
14564
|
+
const cap = li.querySelector("div.b_caption p");
|
|
14565
|
+
const snippet = cap ? cap.textContent.trim().replace(/\s+/g, " ") : "";
|
|
14566
|
+
results.push({ title, url: href, snippet });
|
|
14087
14567
|
}
|
|
14088
14568
|
return results;
|
|
14089
14569
|
}
|
|
@@ -14099,12 +14579,23 @@ async function webFetch(url, opts = {}) {
|
|
|
14099
14579
|
const cancel = () => ctl.abort();
|
|
14100
14580
|
opts.signal?.addEventListener("abort", cancel, { once: true });
|
|
14101
14581
|
let resp;
|
|
14582
|
+
let currentUrl = url;
|
|
14102
14583
|
try {
|
|
14103
|
-
|
|
14104
|
-
|
|
14105
|
-
|
|
14106
|
-
|
|
14107
|
-
|
|
14584
|
+
for (let redirects = 0; ; redirects++) {
|
|
14585
|
+
const parsed = await assertPublicHttpUrl(currentUrl);
|
|
14586
|
+
if (ctl.signal.aborted) throw new DOMException("aborted", "AbortError");
|
|
14587
|
+
resp = await fetch(parsed, {
|
|
14588
|
+
headers: { "User-Agent": USER_AGENT, Accept: "text/html,text/plain,*/*" },
|
|
14589
|
+
signal: ctl.signal,
|
|
14590
|
+
redirect: "manual"
|
|
14591
|
+
});
|
|
14592
|
+
const nextUrl = redirectLocation(resp, parsed.toString());
|
|
14593
|
+
if (!nextUrl) break;
|
|
14594
|
+
if (redirects >= FETCH_MAX_REDIRECTS) {
|
|
14595
|
+
throw new Error(`web_fetch redirect limit exceeded for ${url}`);
|
|
14596
|
+
}
|
|
14597
|
+
currentUrl = nextUrl;
|
|
14598
|
+
}
|
|
14108
14599
|
} catch (err) {
|
|
14109
14600
|
if (timedOut) {
|
|
14110
14601
|
throw new Error(t("webErrors.fetchTimeout", { ms: timeoutMs, url }));
|
|
@@ -14127,7 +14618,7 @@ async function webFetch(url, opts = {}) {
|
|
|
14127
14618
|
const finalText = truncated ? `${text.slice(0, maxChars)}
|
|
14128
14619
|
|
|
14129
14620
|
[\u2026 truncated ${text.length - maxChars} chars \u2026]` : text;
|
|
14130
|
-
return { url, title, text: finalText, truncated };
|
|
14621
|
+
return { url: currentUrl, title, text: finalText, truncated };
|
|
14131
14622
|
}
|
|
14132
14623
|
async function readBodyCapped(resp, maxBytes) {
|
|
14133
14624
|
if (!resp.body) return await resp.text();
|
|
@@ -14199,9 +14690,6 @@ function walkExtract(node, out) {
|
|
|
14199
14690
|
for (const child of node.childNodes) walkExtract(child, out);
|
|
14200
14691
|
if (isBreak) out.push("\n");
|
|
14201
14692
|
}
|
|
14202
|
-
function stripHtml(s) {
|
|
14203
|
-
return parseHtml(s).text;
|
|
14204
|
-
}
|
|
14205
14693
|
var HTML_ENTITIES = {
|
|
14206
14694
|
amp: "&",
|
|
14207
14695
|
lt: "<",
|
|
@@ -14314,12 +14802,12 @@ ${i + 1}. ${r.title}`);
|
|
|
14314
14802
|
}
|
|
14315
14803
|
|
|
14316
14804
|
// src/env.ts
|
|
14317
|
-
import { readFileSync as
|
|
14318
|
-
import { resolve as
|
|
14805
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
14806
|
+
import { resolve as resolve12 } from "path";
|
|
14319
14807
|
function loadDotenv(path2 = ".env") {
|
|
14320
14808
|
let raw;
|
|
14321
14809
|
try {
|
|
14322
|
-
raw =
|
|
14810
|
+
raw = readFileSync12(resolve12(process.cwd(), path2), "utf8");
|
|
14323
14811
|
} catch {
|
|
14324
14812
|
return;
|
|
14325
14813
|
}
|
|
@@ -14338,7 +14826,7 @@ function loadDotenv(path2 = ".env") {
|
|
|
14338
14826
|
}
|
|
14339
14827
|
|
|
14340
14828
|
// src/transcript/log.ts
|
|
14341
|
-
import { createWriteStream, readFileSync as
|
|
14829
|
+
import { createWriteStream, readFileSync as readFileSync13 } from "fs";
|
|
14342
14830
|
function recordFromLoopEvent(ev, extra) {
|
|
14343
14831
|
const rec = {
|
|
14344
14832
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -14381,7 +14869,7 @@ function openTranscriptFile(path2, meta) {
|
|
|
14381
14869
|
return stream;
|
|
14382
14870
|
}
|
|
14383
14871
|
function readTranscript(path2) {
|
|
14384
|
-
const raw =
|
|
14872
|
+
const raw = readFileSync13(path2, "utf8");
|
|
14385
14873
|
return parseTranscript(raw);
|
|
14386
14874
|
}
|
|
14387
14875
|
function parseTranscript(raw) {
|
|
@@ -14768,25 +15256,25 @@ function truncate(s, n) {
|
|
|
14768
15256
|
}
|
|
14769
15257
|
|
|
14770
15258
|
// src/version.ts
|
|
14771
|
-
import { existsSync as existsSync10, mkdirSync as mkdirSync5, readFileSync as
|
|
15259
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync5, readFileSync as readFileSync14, writeFileSync as writeFileSync6 } from "fs";
|
|
14772
15260
|
import { homedir as homedir8 } from "os";
|
|
14773
|
-
import { dirname as
|
|
15261
|
+
import { dirname as dirname8, join as join14 } from "path";
|
|
14774
15262
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
14775
15263
|
var REGISTRY_URL = "https://registry.npmjs.org/reasonix/latest";
|
|
14776
15264
|
var LATEST_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
14777
15265
|
var LATEST_FETCH_TIMEOUT_MS = 2e3;
|
|
14778
15266
|
function readPackageVersion() {
|
|
14779
15267
|
try {
|
|
14780
|
-
let dir =
|
|
15268
|
+
let dir = dirname8(fileURLToPath2(import.meta.url));
|
|
14781
15269
|
for (let i = 0; i < 6; i++) {
|
|
14782
15270
|
const p = join14(dir, "package.json");
|
|
14783
15271
|
if (existsSync10(p)) {
|
|
14784
|
-
const pkg = JSON.parse(
|
|
15272
|
+
const pkg = JSON.parse(readFileSync14(p, "utf8"));
|
|
14785
15273
|
if (pkg?.name === "reasonix" && typeof pkg.version === "string") {
|
|
14786
15274
|
return pkg.version;
|
|
14787
15275
|
}
|
|
14788
15276
|
}
|
|
14789
|
-
const parent =
|
|
15277
|
+
const parent = dirname8(dir);
|
|
14790
15278
|
if (parent === dir) break;
|
|
14791
15279
|
dir = parent;
|
|
14792
15280
|
}
|
|
@@ -14800,7 +15288,7 @@ function cachePath(homeDirOverride) {
|
|
|
14800
15288
|
}
|
|
14801
15289
|
function readCache(homeDirOverride) {
|
|
14802
15290
|
try {
|
|
14803
|
-
const raw =
|
|
15291
|
+
const raw = readFileSync14(cachePath(homeDirOverride), "utf8");
|
|
14804
15292
|
const parsed = JSON.parse(raw);
|
|
14805
15293
|
if (parsed && typeof parsed.version === "string" && typeof parsed.checkedAt === "number") {
|
|
14806
15294
|
return parsed;
|
|
@@ -14812,8 +15300,8 @@ function readCache(homeDirOverride) {
|
|
|
14812
15300
|
function writeCache(entry, homeDirOverride) {
|
|
14813
15301
|
try {
|
|
14814
15302
|
const p = cachePath(homeDirOverride);
|
|
14815
|
-
mkdirSync5(
|
|
14816
|
-
|
|
15303
|
+
mkdirSync5(dirname8(p), { recursive: true });
|
|
15304
|
+
writeFileSync6(p, JSON.stringify(entry), "utf8");
|
|
14817
15305
|
} catch {
|
|
14818
15306
|
}
|
|
14819
15307
|
}
|
|
@@ -15028,7 +15516,7 @@ var McpClient = class {
|
|
|
15028
15516
|
const id = this.nextId++;
|
|
15029
15517
|
const frame = { jsonrpc: "2.0", id, method, params };
|
|
15030
15518
|
let abortHandler = null;
|
|
15031
|
-
const promise = new Promise((
|
|
15519
|
+
const promise = new Promise((resolve14, reject) => {
|
|
15032
15520
|
const timeout = setTimeout(() => {
|
|
15033
15521
|
this.pending.delete(id);
|
|
15034
15522
|
if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
|
|
@@ -15037,7 +15525,7 @@ var McpClient = class {
|
|
|
15037
15525
|
);
|
|
15038
15526
|
}, this.requestTimeoutMs);
|
|
15039
15527
|
this.pending.set(id, {
|
|
15040
|
-
resolve:
|
|
15528
|
+
resolve: resolve14,
|
|
15041
15529
|
reject,
|
|
15042
15530
|
timeout
|
|
15043
15531
|
});
|
|
@@ -15169,12 +15657,12 @@ var StdioTransport = class {
|
|
|
15169
15657
|
}
|
|
15170
15658
|
async send(message) {
|
|
15171
15659
|
if (this.closed) throw new Error("MCP transport is closed");
|
|
15172
|
-
return new Promise((
|
|
15660
|
+
return new Promise((resolve14, reject) => {
|
|
15173
15661
|
const line = `${JSON.stringify(message)}
|
|
15174
15662
|
`;
|
|
15175
15663
|
this.child.stdin.write(line, "utf8", (err) => {
|
|
15176
15664
|
if (err) reject(err);
|
|
15177
|
-
else
|
|
15665
|
+
else resolve14();
|
|
15178
15666
|
});
|
|
15179
15667
|
});
|
|
15180
15668
|
}
|
|
@@ -15185,8 +15673,8 @@ var StdioTransport = class {
|
|
|
15185
15673
|
continue;
|
|
15186
15674
|
}
|
|
15187
15675
|
if (this.closed) return;
|
|
15188
|
-
const next = await new Promise((
|
|
15189
|
-
this.waiters.push(
|
|
15676
|
+
const next = await new Promise((resolve14) => {
|
|
15677
|
+
this.waiters.push(resolve14);
|
|
15190
15678
|
});
|
|
15191
15679
|
if (next === null) return;
|
|
15192
15680
|
yield next;
|
|
@@ -15266,8 +15754,8 @@ var SseTransport = class {
|
|
|
15266
15754
|
constructor(opts) {
|
|
15267
15755
|
this.url = opts.url;
|
|
15268
15756
|
this.headers = opts.headers ?? {};
|
|
15269
|
-
this.endpointReady = new Promise((
|
|
15270
|
-
this.resolveEndpoint =
|
|
15757
|
+
this.endpointReady = new Promise((resolve14, reject) => {
|
|
15758
|
+
this.resolveEndpoint = resolve14;
|
|
15271
15759
|
this.rejectEndpoint = reject;
|
|
15272
15760
|
});
|
|
15273
15761
|
this.endpointReady.catch(() => void 0);
|
|
@@ -15294,8 +15782,8 @@ var SseTransport = class {
|
|
|
15294
15782
|
continue;
|
|
15295
15783
|
}
|
|
15296
15784
|
if (this.closed) return;
|
|
15297
|
-
const next = await new Promise((
|
|
15298
|
-
this.waiters.push(
|
|
15785
|
+
const next = await new Promise((resolve14) => {
|
|
15786
|
+
this.waiters.push(resolve14);
|
|
15299
15787
|
});
|
|
15300
15788
|
if (next === null) return;
|
|
15301
15789
|
yield next;
|
|
@@ -15481,8 +15969,8 @@ var StreamableHttpTransport = class {
|
|
|
15481
15969
|
continue;
|
|
15482
15970
|
}
|
|
15483
15971
|
if (this.closed) return;
|
|
15484
|
-
const next = await new Promise((
|
|
15485
|
-
this.waiters.push(
|
|
15972
|
+
const next = await new Promise((resolve14) => {
|
|
15973
|
+
this.waiters.push(resolve14);
|
|
15486
15974
|
});
|
|
15487
15975
|
if (next === null) return;
|
|
15488
15976
|
yield next;
|
|
@@ -15580,13 +16068,13 @@ import {
|
|
|
15580
16068
|
ftruncateSync,
|
|
15581
16069
|
mkdirSync as mkdirSync6,
|
|
15582
16070
|
openSync as openSync2,
|
|
15583
|
-
readFileSync as
|
|
16071
|
+
readFileSync as readFileSync15,
|
|
15584
16072
|
readSync,
|
|
15585
16073
|
unlinkSync as unlinkSync3,
|
|
15586
|
-
writeFileSync as
|
|
16074
|
+
writeFileSync as writeFileSync7,
|
|
15587
16075
|
writeSync
|
|
15588
16076
|
} from "fs";
|
|
15589
|
-
import { dirname as
|
|
16077
|
+
import { dirname as dirname9, isAbsolute as isAbsolute9, relative as relative10, resolve as resolve13 } from "path";
|
|
15590
16078
|
var BLOCK_RE = /^(\S[^\n]*)\n<{7} SEARCH\n([\s\S]*?)\n?={7}\n([\s\S]*?)\n?>{7} REPLACE/gm;
|
|
15591
16079
|
function parseEditBlocks(text) {
|
|
15592
16080
|
const out = [];
|
|
@@ -15604,29 +16092,29 @@ function parseEditBlocks(text) {
|
|
|
15604
16092
|
return out;
|
|
15605
16093
|
}
|
|
15606
16094
|
function resolveEditPath(rootDir, rawPath) {
|
|
15607
|
-
const absRoot =
|
|
16095
|
+
const absRoot = resolve13(rootDir);
|
|
15608
16096
|
if (/^[A-Za-z]:[\\/]/.test(rawPath) || looksLikeAbsoluteSystemPath2(rawPath)) {
|
|
15609
|
-
return
|
|
16097
|
+
return resolve13(rawPath);
|
|
15610
16098
|
}
|
|
15611
16099
|
let rooted = rawPath;
|
|
15612
16100
|
while (rooted.startsWith("/") || rooted.startsWith("\\")) {
|
|
15613
16101
|
rooted = rooted.slice(1);
|
|
15614
16102
|
}
|
|
15615
|
-
return
|
|
16103
|
+
return resolve13(absRoot, rooted || ".");
|
|
15616
16104
|
}
|
|
15617
16105
|
function looksLikeAbsoluteSystemPath2(rawPath) {
|
|
15618
16106
|
return /^\/(?:home|Users|etc|var|opt|tmp|usr|mnt|Library|Volumes|proc|sys|dev|run|srv|media|Applications|System|root|boot|private)(?:[/\\]|$)/.test(
|
|
15619
16107
|
rawPath
|
|
15620
16108
|
);
|
|
15621
16109
|
}
|
|
15622
|
-
function
|
|
15623
|
-
const rel =
|
|
15624
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
16110
|
+
function pathIsUnder4(child, parent) {
|
|
16111
|
+
const rel = relative10(parent, child);
|
|
16112
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute9(rel);
|
|
15625
16113
|
}
|
|
15626
16114
|
function applyEditBlock(block, rootDir) {
|
|
15627
|
-
const absRoot =
|
|
16115
|
+
const absRoot = resolve13(rootDir);
|
|
15628
16116
|
const absTarget = resolveEditPath(rootDir, block.path);
|
|
15629
|
-
if (!
|
|
16117
|
+
if (!pathIsUnder4(absTarget, absRoot)) {
|
|
15630
16118
|
return {
|
|
15631
16119
|
path: block.path,
|
|
15632
16120
|
status: "path-escape",
|
|
@@ -15636,7 +16124,7 @@ function applyEditBlock(block, rootDir) {
|
|
|
15636
16124
|
const searchEmpty = block.search.length === 0;
|
|
15637
16125
|
if (searchEmpty) {
|
|
15638
16126
|
try {
|
|
15639
|
-
mkdirSync6(
|
|
16127
|
+
mkdirSync6(dirname9(absTarget), { recursive: true });
|
|
15640
16128
|
const fd = openSync2(absTarget, "wx");
|
|
15641
16129
|
try {
|
|
15642
16130
|
writeSync(fd, block.replace);
|
|
@@ -15679,7 +16167,7 @@ function applyEditBlock(block, rootDir) {
|
|
|
15679
16167
|
if (n <= 0) break;
|
|
15680
16168
|
readBytes += n;
|
|
15681
16169
|
}
|
|
15682
|
-
const content = inBuf.
|
|
16170
|
+
const { text: content, encoding } = decodeFileBuffer(inBuf.subarray(0, readBytes));
|
|
15683
16171
|
const le = lineEndingOf(content);
|
|
15684
16172
|
const adaptedSearch = block.search.replace(/\r?\n/g, le);
|
|
15685
16173
|
const adaptedReplace = block.replace.replace(/\r?\n/g, le);
|
|
@@ -15700,7 +16188,7 @@ function applyEditBlock(block, rootDir) {
|
|
|
15700
16188
|
};
|
|
15701
16189
|
}
|
|
15702
16190
|
const replaced = `${content.slice(0, idx)}${adaptedReplace}${content.slice(idx + adaptedSearch.length)}`;
|
|
15703
|
-
const outBuf =
|
|
16191
|
+
const outBuf = encodeFile(replaced, encoding);
|
|
15704
16192
|
ftruncateSync(fd, outBuf.length);
|
|
15705
16193
|
let written = 0;
|
|
15706
16194
|
while (written < outBuf.length) {
|
|
@@ -15720,10 +16208,12 @@ function applyEditBlocks(blocks, rootDir) {
|
|
|
15720
16208
|
return blocks.map((b) => applyEditBlock(b, rootDir));
|
|
15721
16209
|
}
|
|
15722
16210
|
function snapshotBeforeEdits(blocks, rootDir) {
|
|
16211
|
+
const absRoot = resolve13(rootDir);
|
|
15723
16212
|
const seen = /* @__PURE__ */ new Set();
|
|
15724
16213
|
const snapshots = [];
|
|
15725
16214
|
for (const b of blocks) {
|
|
15726
16215
|
const abs = resolveEditPath(rootDir, b.path);
|
|
16216
|
+
if (!pathIsUnder4(abs, absRoot)) continue;
|
|
15727
16217
|
if (seen.has(abs)) continue;
|
|
15728
16218
|
seen.add(abs);
|
|
15729
16219
|
if (!existsSync11(abs)) {
|
|
@@ -15731,7 +16221,8 @@ function snapshotBeforeEdits(blocks, rootDir) {
|
|
|
15731
16221
|
continue;
|
|
15732
16222
|
}
|
|
15733
16223
|
try {
|
|
15734
|
-
|
|
16224
|
+
const { text, encoding } = decodeFileBuffer(readFileSync15(abs));
|
|
16225
|
+
snapshots.push({ path: b.path, prevContent: text, prevEncoding: encoding });
|
|
15735
16226
|
} catch {
|
|
15736
16227
|
snapshots.push({ path: b.path, prevContent: null });
|
|
15737
16228
|
}
|
|
@@ -15739,10 +16230,10 @@ function snapshotBeforeEdits(blocks, rootDir) {
|
|
|
15739
16230
|
return snapshots;
|
|
15740
16231
|
}
|
|
15741
16232
|
function restoreSnapshots(snapshots, rootDir) {
|
|
15742
|
-
const absRoot =
|
|
16233
|
+
const absRoot = resolve13(rootDir);
|
|
15743
16234
|
return snapshots.map((snap) => {
|
|
15744
16235
|
const abs = resolveEditPath(rootDir, snap.path);
|
|
15745
|
-
if (!
|
|
16236
|
+
if (!pathIsUnder4(abs, absRoot)) {
|
|
15746
16237
|
return {
|
|
15747
16238
|
path: snap.path,
|
|
15748
16239
|
status: "path-escape",
|
|
@@ -15758,7 +16249,7 @@ function restoreSnapshots(snapshots, rootDir) {
|
|
|
15758
16249
|
message: "removed (the edit had created it)"
|
|
15759
16250
|
};
|
|
15760
16251
|
}
|
|
15761
|
-
|
|
16252
|
+
writeFileSync7(abs, encodeFile(snap.prevContent, snap.prevEncoding ?? "utf8"));
|
|
15762
16253
|
return {
|
|
15763
16254
|
path: snap.path,
|
|
15764
16255
|
status: "applied",
|
|
@@ -15774,7 +16265,7 @@ function lineEndingOf(text) {
|
|
|
15774
16265
|
}
|
|
15775
16266
|
|
|
15776
16267
|
// src/code/prompt.ts
|
|
15777
|
-
import { existsSync as existsSync12, readFileSync as
|
|
16268
|
+
import { existsSync as existsSync12, readFileSync as readFileSync16 } from "fs";
|
|
15778
16269
|
import { join as join15 } from "path";
|
|
15779
16270
|
var DEFAULT_CODE_MODEL = "deepseek-v4-flash";
|
|
15780
16271
|
function codeSystemBase(modelId) {
|
|
@@ -15838,7 +16329,7 @@ the new lines
|
|
|
15838
16329
|
>>>>>>> REPLACE
|
|
15839
16330
|
|
|
15840
16331
|
Rules:
|
|
15841
|
-
- read_file
|
|
16332
|
+
- **Read before edit (enforced).** You MUST call \`read_file\` on the target this session before \`edit_file\` / \`multi_edit\` will accept it \u2014 the tool refuses unread targets up front, so SEARCH text is grounded in on-disk bytes, not a guess. A fold / mechanical truncate clears the tracker, so re-read after one of those before mutating. \`write_file\` counts as a read for that path (the content is what you just wrote).
|
|
15842
16333
|
- One edit per block; multiple blocks per response are fine.
|
|
15843
16334
|
- Create a new file with empty SEARCH:
|
|
15844
16335
|
path/to/new.ts
|
|
@@ -15881,6 +16372,10 @@ When the user says run / start / launch / serve / boot up: start it, verify it c
|
|
|
15881
16372
|
- One short paragraph explaining *why*, then the blocks.
|
|
15882
16373
|
- Silence during exploration is fine \u2014 tool calls first, prose after.
|
|
15883
16374
|
|
|
16375
|
+
# Task integrity \u2014 non-negotiable
|
|
16376
|
+
|
|
16377
|
+
The user's original objective and ALL constraints (especially "do NOT do X", "avoid Y", "never Z") remain in force for the entire session. You may NOT unilaterally simplify, narrow, or change the objective to save tokens, time, or steps. If you believe the objective needs adjustment, ask the user \u2014 do NOT decide on your own.
|
|
16378
|
+
|
|
15884
16379
|
__ESCALATION_CONTRACT__
|
|
15885
16380
|
|
|
15886
16381
|
${TUI_FORMATTING_RULES}
|
|
@@ -15905,7 +16400,7 @@ function codeSystemPrompt(rootDir, opts = {}) {
|
|
|
15905
16400
|
if (existsSync12(gitignorePath)) {
|
|
15906
16401
|
let content;
|
|
15907
16402
|
try {
|
|
15908
|
-
content =
|
|
16403
|
+
content = readFileSync16(gitignorePath, "utf8");
|
|
15909
16404
|
} catch {
|
|
15910
16405
|
}
|
|
15911
16406
|
if (content !== void 0) {
|
|
@@ -15937,21 +16432,21 @@ ${appendParts.join("\n\n")}`;
|
|
|
15937
16432
|
|
|
15938
16433
|
// src/telemetry/usage.ts
|
|
15939
16434
|
import {
|
|
15940
|
-
appendFileSync as
|
|
16435
|
+
appendFileSync as appendFileSync3,
|
|
15941
16436
|
closeSync as closeSync3,
|
|
15942
16437
|
existsSync as existsSync13,
|
|
15943
16438
|
fstatSync as fstatSync2,
|
|
15944
16439
|
mkdirSync as mkdirSync7,
|
|
15945
16440
|
openSync as openSync3,
|
|
15946
|
-
readFileSync as
|
|
16441
|
+
readFileSync as readFileSync17,
|
|
15947
16442
|
readSync as readSync2,
|
|
15948
|
-
renameSync as
|
|
16443
|
+
renameSync as renameSync3,
|
|
15949
16444
|
statSync as statSync6,
|
|
15950
16445
|
unlinkSync as unlinkSync4,
|
|
15951
|
-
writeFileSync as
|
|
16446
|
+
writeFileSync as writeFileSync8
|
|
15952
16447
|
} from "fs";
|
|
15953
16448
|
import { homedir as homedir9 } from "os";
|
|
15954
|
-
import { dirname as
|
|
16449
|
+
import { dirname as dirname10, join as join16 } from "path";
|
|
15955
16450
|
function defaultUsageLogPath(homeDirOverride) {
|
|
15956
16451
|
return join16(homeDirOverride ?? homedir9(), ".reasonix", "usage.jsonl");
|
|
15957
16452
|
}
|
|
@@ -15992,9 +16487,9 @@ function compactUsageLogIfLarge(path2, now) {
|
|
|
15992
16487
|
if (kept.length === lines.filter((l) => l.trim()).length) return;
|
|
15993
16488
|
const tmp = `${path2}.compacting`;
|
|
15994
16489
|
try {
|
|
15995
|
-
|
|
16490
|
+
writeFileSync8(tmp, kept.length > 0 ? `${kept.join("\n")}
|
|
15996
16491
|
` : "", "utf8");
|
|
15997
|
-
|
|
16492
|
+
renameSync3(tmp, path2);
|
|
15998
16493
|
} catch {
|
|
15999
16494
|
try {
|
|
16000
16495
|
unlinkSync4(tmp);
|
|
@@ -16018,8 +16513,8 @@ function appendUsage(input) {
|
|
|
16018
16513
|
if (input.subagent) record.subagent = input.subagent;
|
|
16019
16514
|
const path2 = input.path ?? defaultUsageLogPath();
|
|
16020
16515
|
try {
|
|
16021
|
-
mkdirSync7(
|
|
16022
|
-
|
|
16516
|
+
mkdirSync7(dirname10(path2), { recursive: true });
|
|
16517
|
+
appendFileSync3(path2, `${JSON.stringify(record)}
|
|
16023
16518
|
`, "utf8");
|
|
16024
16519
|
compactUsageLogIfLarge(path2, record.ts);
|
|
16025
16520
|
} catch {
|
|
@@ -16030,7 +16525,7 @@ function readUsageLog(path2 = defaultUsageLogPath()) {
|
|
|
16030
16525
|
if (!existsSync13(path2)) return [];
|
|
16031
16526
|
let raw;
|
|
16032
16527
|
try {
|
|
16033
|
-
raw =
|
|
16528
|
+
raw = readFileSync17(path2, "utf8");
|
|
16034
16529
|
} catch {
|
|
16035
16530
|
return [];
|
|
16036
16531
|
}
|
|
@@ -16262,9 +16757,9 @@ export {
|
|
|
16262
16757
|
openTranscriptFile,
|
|
16263
16758
|
outputCostUsd,
|
|
16264
16759
|
parseAtQuery,
|
|
16760
|
+
parseBingResults,
|
|
16265
16761
|
parseEditBlocks,
|
|
16266
16762
|
parseMcpSpec,
|
|
16267
|
-
parseMojeekResults,
|
|
16268
16763
|
parseSearxngHtmlResults,
|
|
16269
16764
|
parseTranscript,
|
|
16270
16765
|
prepareSpawn,
|