reasonix 0.5.3 → 0.5.6
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/dist/cli/index.js +255 -86
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +107 -1
- package/dist/index.js +258 -102
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -126,8 +126,8 @@ function computeWait(attempt, initial, cap, retryAfter) {
|
|
|
126
126
|
}
|
|
127
127
|
function sleep(ms, signal) {
|
|
128
128
|
if (ms <= 0) return Promise.resolve();
|
|
129
|
-
return new Promise((
|
|
130
|
-
const timer = setTimeout(
|
|
129
|
+
return new Promise((resolve7, reject) => {
|
|
130
|
+
const timer = setTimeout(resolve7, ms);
|
|
131
131
|
if (signal) {
|
|
132
132
|
const onAbort = () => {
|
|
133
133
|
clearTimeout(timer);
|
|
@@ -599,7 +599,7 @@ function matchesTool(hook, toolName) {
|
|
|
599
599
|
}
|
|
600
600
|
}
|
|
601
601
|
function defaultSpawner(input) {
|
|
602
|
-
return new Promise((
|
|
602
|
+
return new Promise((resolve7) => {
|
|
603
603
|
const child = spawn(input.command, {
|
|
604
604
|
cwd: input.cwd,
|
|
605
605
|
shell: true,
|
|
@@ -626,7 +626,7 @@ function defaultSpawner(input) {
|
|
|
626
626
|
});
|
|
627
627
|
child.once("error", (err) => {
|
|
628
628
|
clearTimeout(timer);
|
|
629
|
-
|
|
629
|
+
resolve7({
|
|
630
630
|
exitCode: null,
|
|
631
631
|
stdout: stdout2,
|
|
632
632
|
stderr,
|
|
@@ -636,7 +636,7 @@ function defaultSpawner(input) {
|
|
|
636
636
|
});
|
|
637
637
|
child.once("close", (code) => {
|
|
638
638
|
clearTimeout(timer);
|
|
639
|
-
|
|
639
|
+
resolve7({
|
|
640
640
|
exitCode: code,
|
|
641
641
|
stdout: stdout2.trim(),
|
|
642
642
|
stderr: stderr.trim(),
|
|
@@ -697,7 +697,7 @@ async function runHooks(opts) {
|
|
|
697
697
|
}
|
|
698
698
|
|
|
699
699
|
// src/tokenizer.ts
|
|
700
|
-
import { readFileSync as readFileSync3 } from "fs";
|
|
700
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
|
|
701
701
|
import { createRequire } from "module";
|
|
702
702
|
import { dirname as dirname2, join as join3 } from "path";
|
|
703
703
|
import { fileURLToPath } from "url";
|
|
@@ -725,17 +725,24 @@ function buildByteToChar() {
|
|
|
725
725
|
var cached = null;
|
|
726
726
|
function resolveDataPath() {
|
|
727
727
|
if (process.env.REASONIX_TOKENIZER_PATH) return process.env.REASONIX_TOKENIZER_PATH;
|
|
728
|
+
const candidates = [];
|
|
728
729
|
try {
|
|
729
730
|
const here = dirname2(fileURLToPath(import.meta.url));
|
|
730
|
-
|
|
731
|
+
candidates.push(join3(here, "..", "data", "deepseek-tokenizer.json.gz"));
|
|
732
|
+
candidates.push(join3(here, "..", "..", "data", "deepseek-tokenizer.json.gz"));
|
|
731
733
|
} catch {
|
|
734
|
+
}
|
|
735
|
+
try {
|
|
732
736
|
const req = createRequire(import.meta.url);
|
|
733
|
-
|
|
734
|
-
dirname2(req.resolve("reasonix/package.json")),
|
|
735
|
-
"data",
|
|
736
|
-
"deepseek-tokenizer.json.gz"
|
|
737
|
+
candidates.push(
|
|
738
|
+
join3(dirname2(req.resolve("reasonix/package.json")), "data", "deepseek-tokenizer.json.gz")
|
|
737
739
|
);
|
|
740
|
+
} catch {
|
|
741
|
+
}
|
|
742
|
+
for (const p of candidates) {
|
|
743
|
+
if (existsSync2(p)) return p;
|
|
738
744
|
}
|
|
745
|
+
return candidates[0] ?? join3(process.cwd(), "data", "deepseek-tokenizer.json.gz");
|
|
739
746
|
}
|
|
740
747
|
function loadTokenizer() {
|
|
741
748
|
if (cached) return cached;
|
|
@@ -859,6 +866,25 @@ function encode(text) {
|
|
|
859
866
|
function countTokens(text) {
|
|
860
867
|
return encode(text).length;
|
|
861
868
|
}
|
|
869
|
+
function estimateConversationTokens(messages) {
|
|
870
|
+
let total = 0;
|
|
871
|
+
for (const m of messages) {
|
|
872
|
+
if (typeof m.content === "string" && m.content) {
|
|
873
|
+
total += countTokens(m.content);
|
|
874
|
+
}
|
|
875
|
+
if (m.tool_calls && Array.isArray(m.tool_calls) && m.tool_calls.length > 0) {
|
|
876
|
+
total += countTokens(JSON.stringify(m.tool_calls));
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return total;
|
|
880
|
+
}
|
|
881
|
+
function estimateRequestTokens(messages, toolSpecs) {
|
|
882
|
+
let total = estimateConversationTokens(messages);
|
|
883
|
+
if (toolSpecs && toolSpecs.length > 0) {
|
|
884
|
+
total += countTokens(JSON.stringify(toolSpecs));
|
|
885
|
+
}
|
|
886
|
+
return total;
|
|
887
|
+
}
|
|
862
888
|
|
|
863
889
|
// src/repair/flatten.ts
|
|
864
890
|
function analyzeSchema(schema) {
|
|
@@ -1546,7 +1572,7 @@ function signature2(call) {
|
|
|
1546
1572
|
import {
|
|
1547
1573
|
appendFileSync,
|
|
1548
1574
|
chmodSync as chmodSync2,
|
|
1549
|
-
existsSync as
|
|
1575
|
+
existsSync as existsSync3,
|
|
1550
1576
|
mkdirSync as mkdirSync2,
|
|
1551
1577
|
readFileSync as readFileSync4,
|
|
1552
1578
|
readdirSync,
|
|
@@ -1568,7 +1594,7 @@ function sanitizeName(name) {
|
|
|
1568
1594
|
}
|
|
1569
1595
|
function loadSessionMessages(name) {
|
|
1570
1596
|
const path = sessionPath(name);
|
|
1571
|
-
if (!
|
|
1597
|
+
if (!existsSync3(path)) return [];
|
|
1572
1598
|
try {
|
|
1573
1599
|
const raw = readFileSync4(path, "utf8");
|
|
1574
1600
|
const out = [];
|
|
@@ -1598,7 +1624,7 @@ function appendSessionMessage(name, message) {
|
|
|
1598
1624
|
}
|
|
1599
1625
|
function listSessions() {
|
|
1600
1626
|
const dir = sessionsDir();
|
|
1601
|
-
if (!
|
|
1627
|
+
if (!existsSync3(dir)) return [];
|
|
1602
1628
|
try {
|
|
1603
1629
|
const files = readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
|
|
1604
1630
|
return files.map((file) => {
|
|
@@ -1794,9 +1820,9 @@ var CacheFirstLoop = class {
|
|
|
1794
1820
|
this.sessionName = opts.session ?? null;
|
|
1795
1821
|
if (this.sessionName) {
|
|
1796
1822
|
const prior = loadSessionMessages(this.sessionName);
|
|
1797
|
-
const { messages, healedCount,
|
|
1823
|
+
const { messages, healedCount, tokensSaved } = healLoadedMessagesByTokens(
|
|
1798
1824
|
prior,
|
|
1799
|
-
|
|
1825
|
+
DEFAULT_MAX_RESULT_TOKENS
|
|
1800
1826
|
);
|
|
1801
1827
|
for (const msg of messages) this.log.append(msg);
|
|
1802
1828
|
this.resumedMessageCount = messages.length;
|
|
@@ -1806,7 +1832,7 @@ var CacheFirstLoop = class {
|
|
|
1806
1832
|
} catch {
|
|
1807
1833
|
}
|
|
1808
1834
|
process.stderr.write(
|
|
1809
|
-
`\u25B8 session "${this.sessionName}": healed ${healedCount} entr${healedCount === 1 ? "y" : "ies"}${
|
|
1835
|
+
`\u25B8 session "${this.sessionName}": healed ${healedCount} entr${healedCount === 1 ? "y" : "ies"}${tokensSaved > 0 ? ` (shrunk ${tokensSaved.toLocaleString()} tokens of oversized tool output)` : " (dropped dangling tool_calls tail)"}. Rewrote session file.
|
|
1810
1836
|
`
|
|
1811
1837
|
);
|
|
1812
1838
|
}
|
|
@@ -2002,7 +2028,32 @@ var CacheFirstLoop = class {
|
|
|
2002
2028
|
content: `${iter}/${this.maxToolIters} tool calls used \u2014 approaching budget. Press Esc to force a summary now.`
|
|
2003
2029
|
};
|
|
2004
2030
|
}
|
|
2005
|
-
|
|
2031
|
+
let messages = this.buildMessages(pendingUser);
|
|
2032
|
+
{
|
|
2033
|
+
const ctxMax2 = DEEPSEEK_CONTEXT_TOKENS[this.model] ?? DEFAULT_CONTEXT_TOKENS;
|
|
2034
|
+
const estimate = estimateRequestTokens(messages, this.prefix.toolSpecs);
|
|
2035
|
+
if (estimate / ctxMax2 > 0.95) {
|
|
2036
|
+
const result = this.compact(1e3);
|
|
2037
|
+
if (result.healedCount > 0) {
|
|
2038
|
+
yield {
|
|
2039
|
+
turn: this._turn,
|
|
2040
|
+
role: "warning",
|
|
2041
|
+
content: `preflight: request ~${estimate.toLocaleString()}/${ctxMax2.toLocaleString()} tokens (${Math.round(
|
|
2042
|
+
estimate / ctxMax2 * 100
|
|
2043
|
+
)}%) \u2014 pre-compacted ${result.healedCount} tool result(s), saved ${result.tokensSaved.toLocaleString()} tokens. Sending.`
|
|
2044
|
+
};
|
|
2045
|
+
messages = this.buildMessages(pendingUser);
|
|
2046
|
+
} else {
|
|
2047
|
+
yield {
|
|
2048
|
+
turn: this._turn,
|
|
2049
|
+
role: "warning",
|
|
2050
|
+
content: `preflight: request ~${estimate.toLocaleString()}/${ctxMax2.toLocaleString()} tokens (${Math.round(
|
|
2051
|
+
estimate / ctxMax2 * 100
|
|
2052
|
+
)}%) and nothing to auto-compact \u2014 DeepSeek will likely 400. Run /forget or /clear to start fresh.`
|
|
2053
|
+
};
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2006
2057
|
let assistantContent = "";
|
|
2007
2058
|
let reasoningContent = "";
|
|
2008
2059
|
let toolCalls = [];
|
|
@@ -2050,8 +2101,8 @@ var CacheFirstLoop = class {
|
|
|
2050
2101
|
}
|
|
2051
2102
|
);
|
|
2052
2103
|
for (let k = 0; k < budget; k++) {
|
|
2053
|
-
const sample = queue.shift() ?? await new Promise((
|
|
2054
|
-
waiter =
|
|
2104
|
+
const sample = queue.shift() ?? await new Promise((resolve7) => {
|
|
2105
|
+
waiter = resolve7;
|
|
2055
2106
|
});
|
|
2056
2107
|
yield {
|
|
2057
2108
|
turn: this._turn,
|
|
@@ -2450,15 +2501,12 @@ function shrinkOversizedToolResultsByTokens(messages, maxTokens) {
|
|
|
2450
2501
|
});
|
|
2451
2502
|
return { messages: out, healedCount, tokensSaved, charsSaved };
|
|
2452
2503
|
}
|
|
2453
|
-
function
|
|
2454
|
-
const shrunk = shrinkOversizedToolResults(messages, maxChars);
|
|
2455
|
-
let healedCount = shrunk.healedCount;
|
|
2504
|
+
function fixToolCallPairing(messages) {
|
|
2456
2505
|
const out = [];
|
|
2457
|
-
const openCallIds = /* @__PURE__ */ new Set();
|
|
2458
2506
|
let droppedAssistantCalls = 0;
|
|
2459
2507
|
let droppedStrayTools = 0;
|
|
2460
|
-
for (let i = 0; i <
|
|
2461
|
-
const msg =
|
|
2508
|
+
for (let i = 0; i < messages.length; i++) {
|
|
2509
|
+
const msg = messages[i];
|
|
2462
2510
|
if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0) {
|
|
2463
2511
|
const needed = /* @__PURE__ */ new Set();
|
|
2464
2512
|
for (const call of msg.tool_calls) {
|
|
@@ -2466,8 +2514,8 @@ function healLoadedMessages(messages, maxChars) {
|
|
|
2466
2514
|
}
|
|
2467
2515
|
const candidates = [];
|
|
2468
2516
|
let j = i + 1;
|
|
2469
|
-
while (j <
|
|
2470
|
-
const nxt =
|
|
2517
|
+
while (j < messages.length && needed.size > 0) {
|
|
2518
|
+
const nxt = messages[j];
|
|
2471
2519
|
if (nxt.role !== "tool") break;
|
|
2472
2520
|
const id = nxt.tool_call_id ?? "";
|
|
2473
2521
|
if (!needed.has(id)) break;
|
|
@@ -2492,8 +2540,24 @@ function healLoadedMessages(messages, maxChars) {
|
|
|
2492
2540
|
}
|
|
2493
2541
|
out.push(msg);
|
|
2494
2542
|
}
|
|
2495
|
-
|
|
2496
|
-
|
|
2543
|
+
return { messages: out, droppedAssistantCalls, droppedStrayTools };
|
|
2544
|
+
}
|
|
2545
|
+
function healLoadedMessages(messages, maxChars) {
|
|
2546
|
+
const shrunk = shrinkOversizedToolResults(messages, maxChars);
|
|
2547
|
+
const paired = fixToolCallPairing(shrunk.messages);
|
|
2548
|
+
const healedCount = shrunk.healedCount + paired.droppedAssistantCalls + paired.droppedStrayTools;
|
|
2549
|
+
return { messages: paired.messages, healedCount, healedFrom: shrunk.healedFrom };
|
|
2550
|
+
}
|
|
2551
|
+
function healLoadedMessagesByTokens(messages, maxTokens) {
|
|
2552
|
+
const shrunk = shrinkOversizedToolResultsByTokens(messages, maxTokens);
|
|
2553
|
+
const paired = fixToolCallPairing(shrunk.messages);
|
|
2554
|
+
const healedCount = shrunk.healedCount + paired.droppedAssistantCalls + paired.droppedStrayTools;
|
|
2555
|
+
return {
|
|
2556
|
+
messages: paired.messages,
|
|
2557
|
+
healedCount,
|
|
2558
|
+
tokensSaved: shrunk.tokensSaved,
|
|
2559
|
+
charsSaved: shrunk.charsSaved
|
|
2560
|
+
};
|
|
2497
2561
|
}
|
|
2498
2562
|
function formatLoopError(err) {
|
|
2499
2563
|
const msg = err.message ?? "";
|
|
@@ -2505,6 +2569,93 @@ function formatLoopError(err) {
|
|
|
2505
2569
|
return msg;
|
|
2506
2570
|
}
|
|
2507
2571
|
|
|
2572
|
+
// src/at-mentions.ts
|
|
2573
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
|
|
2574
|
+
import { isAbsolute, relative, resolve } from "path";
|
|
2575
|
+
var DEFAULT_AT_MENTION_MAX_BYTES = 64 * 1024;
|
|
2576
|
+
var AT_MENTION_PATTERN = /(?<=^|\s)@([a-zA-Z0-9_./\\-]+)/g;
|
|
2577
|
+
function expandAtMentions(text, rootDir, opts = {}) {
|
|
2578
|
+
const maxBytes = opts.maxBytes ?? DEFAULT_AT_MENTION_MAX_BYTES;
|
|
2579
|
+
const fs2 = opts.fs ?? defaultFs;
|
|
2580
|
+
const root = resolve(rootDir);
|
|
2581
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2582
|
+
const expansions = [];
|
|
2583
|
+
for (const match of text.matchAll(AT_MENTION_PATTERN)) {
|
|
2584
|
+
const rawPath = match[1] ?? "";
|
|
2585
|
+
const cleaned = rawPath.replace(/\.+$/, "");
|
|
2586
|
+
if (!cleaned) continue;
|
|
2587
|
+
const token = `@${cleaned}`;
|
|
2588
|
+
if (seen.has(token)) continue;
|
|
2589
|
+
const expansion = resolveMention(cleaned, root, maxBytes, fs2);
|
|
2590
|
+
seen.set(token, expansion);
|
|
2591
|
+
expansions.push(expansion);
|
|
2592
|
+
}
|
|
2593
|
+
if (expansions.length === 0) return { text, expansions };
|
|
2594
|
+
const blocks = [];
|
|
2595
|
+
for (const ex of expansions) {
|
|
2596
|
+
if (ex.ok) {
|
|
2597
|
+
const content = readSafe(root, ex.path, fs2);
|
|
2598
|
+
blocks.push(`<file path="${ex.path}">
|
|
2599
|
+
${content}
|
|
2600
|
+
</file>`);
|
|
2601
|
+
} else {
|
|
2602
|
+
blocks.push(`<file path="${ex.path}" skipped="${ex.skip}" />`);
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
const augmented = `${text}
|
|
2606
|
+
|
|
2607
|
+
[Referenced files]
|
|
2608
|
+
${blocks.join("\n\n")}`;
|
|
2609
|
+
return { text: augmented, expansions };
|
|
2610
|
+
}
|
|
2611
|
+
function resolveMention(rawPath, root, maxBytes, fs2) {
|
|
2612
|
+
if (isAbsolute(rawPath)) {
|
|
2613
|
+
return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "escape" };
|
|
2614
|
+
}
|
|
2615
|
+
const resolved = resolve(root, rawPath);
|
|
2616
|
+
const rel = relative(root, resolved);
|
|
2617
|
+
if (rel.startsWith("..") || isAbsolute(rel)) {
|
|
2618
|
+
return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "escape" };
|
|
2619
|
+
}
|
|
2620
|
+
if (!fs2.exists(resolved)) {
|
|
2621
|
+
return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "missing" };
|
|
2622
|
+
}
|
|
2623
|
+
if (!fs2.isFile(resolved)) {
|
|
2624
|
+
return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "not-file" };
|
|
2625
|
+
}
|
|
2626
|
+
const size = fs2.size(resolved);
|
|
2627
|
+
if (size > maxBytes) {
|
|
2628
|
+
return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "too-large", bytes: size };
|
|
2629
|
+
}
|
|
2630
|
+
return { token: `@${rawPath}`, path: rawPath, ok: true, bytes: size };
|
|
2631
|
+
}
|
|
2632
|
+
function readSafe(root, rawPath, fs2) {
|
|
2633
|
+
const resolved = resolve(root, rawPath);
|
|
2634
|
+
try {
|
|
2635
|
+
return fs2.read(resolved);
|
|
2636
|
+
} catch {
|
|
2637
|
+
return "(read failed)";
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
var defaultFs = {
|
|
2641
|
+
exists: (p) => existsSync4(p),
|
|
2642
|
+
isFile: (p) => {
|
|
2643
|
+
try {
|
|
2644
|
+
return statSync2(p).isFile();
|
|
2645
|
+
} catch {
|
|
2646
|
+
return false;
|
|
2647
|
+
}
|
|
2648
|
+
},
|
|
2649
|
+
size: (p) => {
|
|
2650
|
+
try {
|
|
2651
|
+
return statSync2(p).size;
|
|
2652
|
+
} catch {
|
|
2653
|
+
return 0;
|
|
2654
|
+
}
|
|
2655
|
+
},
|
|
2656
|
+
read: (p) => readFileSync5(p, "utf8")
|
|
2657
|
+
};
|
|
2658
|
+
|
|
2508
2659
|
// src/tools/filesystem.ts
|
|
2509
2660
|
import { promises as fs } from "fs";
|
|
2510
2661
|
import * as pathMod from "path";
|
|
@@ -3315,7 +3466,7 @@ function forkRegistryExcluding(parent, exclude) {
|
|
|
3315
3466
|
|
|
3316
3467
|
// src/tools/shell.ts
|
|
3317
3468
|
import { spawn as spawn2 } from "child_process";
|
|
3318
|
-
import { existsSync as
|
|
3469
|
+
import { existsSync as existsSync5, statSync as statSync3 } from "fs";
|
|
3319
3470
|
import * as pathMod2 from "path";
|
|
3320
3471
|
var DEFAULT_TIMEOUT_SEC = 60;
|
|
3321
3472
|
var DEFAULT_MAX_OUTPUT_CHARS = 32e3;
|
|
@@ -3485,7 +3636,7 @@ async function runCommand(cmd, opts) {
|
|
|
3485
3636
|
};
|
|
3486
3637
|
const { bin, args, spawnOverrides } = prepareSpawn(argv);
|
|
3487
3638
|
const effectiveSpawnOpts = { ...spawnOpts, ...spawnOverrides };
|
|
3488
|
-
return await new Promise((
|
|
3639
|
+
return await new Promise((resolve7, reject) => {
|
|
3489
3640
|
let child;
|
|
3490
3641
|
try {
|
|
3491
3642
|
child = spawn2(bin, args, effectiveSpawnOpts);
|
|
@@ -3518,7 +3669,7 @@ async function runCommand(cmd, opts) {
|
|
|
3518
3669
|
const output = buf.length > maxChars ? `${buf.slice(0, maxChars)}
|
|
3519
3670
|
|
|
3520
3671
|
[\u2026 truncated ${buf.length - maxChars} chars \u2026]` : buf;
|
|
3521
|
-
|
|
3672
|
+
resolve7({ exitCode: code, output, timedOut });
|
|
3522
3673
|
});
|
|
3523
3674
|
});
|
|
3524
3675
|
}
|
|
@@ -3543,7 +3694,7 @@ function resolveExecutable(cmd, opts = {}) {
|
|
|
3543
3694
|
}
|
|
3544
3695
|
function defaultIsFile(full) {
|
|
3545
3696
|
try {
|
|
3546
|
-
return
|
|
3697
|
+
return existsSync5(full) && statSync3(full).isFile();
|
|
3547
3698
|
} catch {
|
|
3548
3699
|
return false;
|
|
3549
3700
|
}
|
|
@@ -3870,12 +4021,12 @@ ${i + 1}. ${r.title}`);
|
|
|
3870
4021
|
}
|
|
3871
4022
|
|
|
3872
4023
|
// src/env.ts
|
|
3873
|
-
import { readFileSync as
|
|
3874
|
-
import { resolve as
|
|
4024
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
4025
|
+
import { resolve as resolve4 } from "path";
|
|
3875
4026
|
function loadDotenv(path = ".env") {
|
|
3876
4027
|
let raw;
|
|
3877
4028
|
try {
|
|
3878
|
-
raw =
|
|
4029
|
+
raw = readFileSync6(resolve4(process.cwd(), path), "utf8");
|
|
3879
4030
|
} catch {
|
|
3880
4031
|
return;
|
|
3881
4032
|
}
|
|
@@ -3894,7 +4045,7 @@ function loadDotenv(path = ".env") {
|
|
|
3894
4045
|
}
|
|
3895
4046
|
|
|
3896
4047
|
// src/transcript.ts
|
|
3897
|
-
import { createWriteStream, readFileSync as
|
|
4048
|
+
import { createWriteStream, readFileSync as readFileSync7 } from "fs";
|
|
3898
4049
|
function recordFromLoopEvent(ev, extra) {
|
|
3899
4050
|
const rec = {
|
|
3900
4051
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3945,7 +4096,7 @@ function openTranscriptFile(path, meta) {
|
|
|
3945
4096
|
return stream;
|
|
3946
4097
|
}
|
|
3947
4098
|
function readTranscript(path) {
|
|
3948
|
-
const raw =
|
|
4099
|
+
const raw = readFileSync7(path, "utf8");
|
|
3949
4100
|
return parseTranscript(raw);
|
|
3950
4101
|
}
|
|
3951
4102
|
function isPlanStateEmptyShape(s) {
|
|
@@ -4588,7 +4739,7 @@ var McpClient = class {
|
|
|
4588
4739
|
const id = this.nextId++;
|
|
4589
4740
|
const frame = { jsonrpc: "2.0", id, method, params };
|
|
4590
4741
|
let abortHandler = null;
|
|
4591
|
-
const promise = new Promise((
|
|
4742
|
+
const promise = new Promise((resolve7, reject) => {
|
|
4592
4743
|
const timeout = setTimeout(() => {
|
|
4593
4744
|
this.pending.delete(id);
|
|
4594
4745
|
if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
|
|
@@ -4597,7 +4748,7 @@ var McpClient = class {
|
|
|
4597
4748
|
);
|
|
4598
4749
|
}, this.requestTimeoutMs);
|
|
4599
4750
|
this.pending.set(id, {
|
|
4600
|
-
resolve:
|
|
4751
|
+
resolve: resolve7,
|
|
4601
4752
|
reject,
|
|
4602
4753
|
timeout
|
|
4603
4754
|
});
|
|
@@ -4720,12 +4871,12 @@ var StdioTransport = class {
|
|
|
4720
4871
|
}
|
|
4721
4872
|
async send(message) {
|
|
4722
4873
|
if (this.closed) throw new Error("MCP transport is closed");
|
|
4723
|
-
return new Promise((
|
|
4874
|
+
return new Promise((resolve7, reject) => {
|
|
4724
4875
|
const line = `${JSON.stringify(message)}
|
|
4725
4876
|
`;
|
|
4726
4877
|
this.child.stdin.write(line, "utf8", (err) => {
|
|
4727
4878
|
if (err) reject(err);
|
|
4728
|
-
else
|
|
4879
|
+
else resolve7();
|
|
4729
4880
|
});
|
|
4730
4881
|
});
|
|
4731
4882
|
}
|
|
@@ -4736,8 +4887,8 @@ var StdioTransport = class {
|
|
|
4736
4887
|
continue;
|
|
4737
4888
|
}
|
|
4738
4889
|
if (this.closed) return;
|
|
4739
|
-
const next = await new Promise((
|
|
4740
|
-
this.waiters.push(
|
|
4890
|
+
const next = await new Promise((resolve7) => {
|
|
4891
|
+
this.waiters.push(resolve7);
|
|
4741
4892
|
});
|
|
4742
4893
|
if (next === null) return;
|
|
4743
4894
|
yield next;
|
|
@@ -4803,8 +4954,8 @@ var SseTransport = class {
|
|
|
4803
4954
|
constructor(opts) {
|
|
4804
4955
|
this.url = opts.url;
|
|
4805
4956
|
this.headers = opts.headers ?? {};
|
|
4806
|
-
this.endpointReady = new Promise((
|
|
4807
|
-
this.resolveEndpoint =
|
|
4957
|
+
this.endpointReady = new Promise((resolve7, reject) => {
|
|
4958
|
+
this.resolveEndpoint = resolve7;
|
|
4808
4959
|
this.rejectEndpoint = reject;
|
|
4809
4960
|
});
|
|
4810
4961
|
this.endpointReady.catch(() => void 0);
|
|
@@ -4831,8 +4982,8 @@ var SseTransport = class {
|
|
|
4831
4982
|
continue;
|
|
4832
4983
|
}
|
|
4833
4984
|
if (this.closed) return;
|
|
4834
|
-
const next = await new Promise((
|
|
4835
|
-
this.waiters.push(
|
|
4985
|
+
const next = await new Promise((resolve7) => {
|
|
4986
|
+
this.waiters.push(resolve7);
|
|
4836
4987
|
});
|
|
4837
4988
|
if (next === null) return;
|
|
4838
4989
|
yield next;
|
|
@@ -5031,8 +5182,8 @@ async function trySection(load) {
|
|
|
5031
5182
|
}
|
|
5032
5183
|
|
|
5033
5184
|
// src/code/edit-blocks.ts
|
|
5034
|
-
import { existsSync as
|
|
5035
|
-
import { dirname as dirname5, resolve as
|
|
5185
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync8, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
5186
|
+
import { dirname as dirname5, resolve as resolve5 } from "path";
|
|
5036
5187
|
var BLOCK_RE = /^(\S[^\n]*)\n<{7} SEARCH\n([\s\S]*?)\n?={7}\n([\s\S]*?)\n?>{7} REPLACE/gm;
|
|
5037
5188
|
function parseEditBlocks(text) {
|
|
5038
5189
|
const out = [];
|
|
@@ -5050,8 +5201,8 @@ function parseEditBlocks(text) {
|
|
|
5050
5201
|
return out;
|
|
5051
5202
|
}
|
|
5052
5203
|
function applyEditBlock(block, rootDir) {
|
|
5053
|
-
const absRoot =
|
|
5054
|
-
const absTarget =
|
|
5204
|
+
const absRoot = resolve5(rootDir);
|
|
5205
|
+
const absTarget = resolve5(absRoot, block.path);
|
|
5055
5206
|
if (absTarget !== absRoot && !absTarget.startsWith(`${absRoot}${sep()}`)) {
|
|
5056
5207
|
return {
|
|
5057
5208
|
path: block.path,
|
|
@@ -5060,7 +5211,7 @@ function applyEditBlock(block, rootDir) {
|
|
|
5060
5211
|
};
|
|
5061
5212
|
}
|
|
5062
5213
|
const searchEmpty = block.search.length === 0;
|
|
5063
|
-
const exists =
|
|
5214
|
+
const exists = existsSync6(absTarget);
|
|
5064
5215
|
try {
|
|
5065
5216
|
if (!exists) {
|
|
5066
5217
|
if (!searchEmpty) {
|
|
@@ -5074,7 +5225,7 @@ function applyEditBlock(block, rootDir) {
|
|
|
5074
5225
|
writeFileSync3(absTarget, block.replace, "utf8");
|
|
5075
5226
|
return { path: block.path, status: "created" };
|
|
5076
5227
|
}
|
|
5077
|
-
const content =
|
|
5228
|
+
const content = readFileSync8(absTarget, "utf8");
|
|
5078
5229
|
if (searchEmpty) {
|
|
5079
5230
|
return {
|
|
5080
5231
|
path: block.path,
|
|
@@ -5101,19 +5252,19 @@ function applyEditBlocks(blocks, rootDir) {
|
|
|
5101
5252
|
return blocks.map((b) => applyEditBlock(b, rootDir));
|
|
5102
5253
|
}
|
|
5103
5254
|
function snapshotBeforeEdits(blocks, rootDir) {
|
|
5104
|
-
const absRoot =
|
|
5255
|
+
const absRoot = resolve5(rootDir);
|
|
5105
5256
|
const seen = /* @__PURE__ */ new Set();
|
|
5106
5257
|
const snapshots = [];
|
|
5107
5258
|
for (const b of blocks) {
|
|
5108
5259
|
if (seen.has(b.path)) continue;
|
|
5109
5260
|
seen.add(b.path);
|
|
5110
|
-
const abs =
|
|
5111
|
-
if (!
|
|
5261
|
+
const abs = resolve5(absRoot, b.path);
|
|
5262
|
+
if (!existsSync6(abs)) {
|
|
5112
5263
|
snapshots.push({ path: b.path, prevContent: null });
|
|
5113
5264
|
continue;
|
|
5114
5265
|
}
|
|
5115
5266
|
try {
|
|
5116
|
-
snapshots.push({ path: b.path, prevContent:
|
|
5267
|
+
snapshots.push({ path: b.path, prevContent: readFileSync8(abs, "utf8") });
|
|
5117
5268
|
} catch {
|
|
5118
5269
|
snapshots.push({ path: b.path, prevContent: null });
|
|
5119
5270
|
}
|
|
@@ -5121,9 +5272,9 @@ function snapshotBeforeEdits(blocks, rootDir) {
|
|
|
5121
5272
|
return snapshots;
|
|
5122
5273
|
}
|
|
5123
5274
|
function restoreSnapshots(snapshots, rootDir) {
|
|
5124
|
-
const absRoot =
|
|
5275
|
+
const absRoot = resolve5(rootDir);
|
|
5125
5276
|
return snapshots.map((snap) => {
|
|
5126
|
-
const abs =
|
|
5277
|
+
const abs = resolve5(absRoot, snap.path);
|
|
5127
5278
|
if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep()}`)) {
|
|
5128
5279
|
return {
|
|
5129
5280
|
path: snap.path,
|
|
@@ -5133,7 +5284,7 @@ function restoreSnapshots(snapshots, rootDir) {
|
|
|
5133
5284
|
}
|
|
5134
5285
|
try {
|
|
5135
5286
|
if (snap.prevContent === null) {
|
|
5136
|
-
if (
|
|
5287
|
+
if (existsSync6(abs)) unlinkSync2(abs);
|
|
5137
5288
|
return {
|
|
5138
5289
|
path: snap.path,
|
|
5139
5290
|
status: "applied",
|
|
@@ -5156,7 +5307,7 @@ function sep() {
|
|
|
5156
5307
|
}
|
|
5157
5308
|
|
|
5158
5309
|
// src/version.ts
|
|
5159
|
-
import { existsSync as
|
|
5310
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "fs";
|
|
5160
5311
|
import { homedir as homedir4 } from "os";
|
|
5161
5312
|
import { dirname as dirname6, join as join6 } from "path";
|
|
5162
5313
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -5168,8 +5319,8 @@ function readPackageVersion() {
|
|
|
5168
5319
|
let dir = dirname6(fileURLToPath2(import.meta.url));
|
|
5169
5320
|
for (let i = 0; i < 6; i++) {
|
|
5170
5321
|
const p = join6(dir, "package.json");
|
|
5171
|
-
if (
|
|
5172
|
-
const pkg = JSON.parse(
|
|
5322
|
+
if (existsSync7(p)) {
|
|
5323
|
+
const pkg = JSON.parse(readFileSync9(p, "utf8"));
|
|
5173
5324
|
if (pkg?.name === "reasonix" && typeof pkg.version === "string") {
|
|
5174
5325
|
return pkg.version;
|
|
5175
5326
|
}
|
|
@@ -5188,7 +5339,7 @@ function cachePath(homeDirOverride) {
|
|
|
5188
5339
|
}
|
|
5189
5340
|
function readCache(homeDirOverride) {
|
|
5190
5341
|
try {
|
|
5191
|
-
const raw =
|
|
5342
|
+
const raw = readFileSync9(cachePath(homeDirOverride), "utf8");
|
|
5192
5343
|
const parsed = JSON.parse(raw);
|
|
5193
5344
|
if (parsed && typeof parsed.version === "string" && typeof parsed.checkedAt === "number") {
|
|
5194
5345
|
return parsed;
|
|
@@ -5257,7 +5408,7 @@ function isNpxInstall() {
|
|
|
5257
5408
|
}
|
|
5258
5409
|
|
|
5259
5410
|
// src/usage.ts
|
|
5260
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
5411
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync10, statSync as statSync4 } from "fs";
|
|
5261
5412
|
import { homedir as homedir5 } from "os";
|
|
5262
5413
|
import { dirname as dirname7, join as join7 } from "path";
|
|
5263
5414
|
function defaultUsageLogPath(homeDirOverride) {
|
|
@@ -5285,10 +5436,10 @@ function appendUsage(input) {
|
|
|
5285
5436
|
return record;
|
|
5286
5437
|
}
|
|
5287
5438
|
function readUsageLog(path = defaultUsageLogPath()) {
|
|
5288
|
-
if (!
|
|
5439
|
+
if (!existsSync8(path)) return [];
|
|
5289
5440
|
let raw;
|
|
5290
5441
|
try {
|
|
5291
|
-
raw =
|
|
5442
|
+
raw = readFileSync10(path, "utf8");
|
|
5292
5443
|
} catch {
|
|
5293
5444
|
return [];
|
|
5294
5445
|
}
|
|
@@ -5370,9 +5521,9 @@ function aggregateUsage(records, opts = {}) {
|
|
|
5370
5521
|
};
|
|
5371
5522
|
}
|
|
5372
5523
|
function formatLogSize(path = defaultUsageLogPath()) {
|
|
5373
|
-
if (!
|
|
5524
|
+
if (!existsSync8(path)) return "";
|
|
5374
5525
|
try {
|
|
5375
|
-
const s =
|
|
5526
|
+
const s = statSync4(path);
|
|
5376
5527
|
const bytes = s.size;
|
|
5377
5528
|
if (bytes < 1024) return `${bytes} B`;
|
|
5378
5529
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
@@ -5383,7 +5534,7 @@ function formatLogSize(path = defaultUsageLogPath()) {
|
|
|
5383
5534
|
}
|
|
5384
5535
|
|
|
5385
5536
|
// src/cli/commands/chat.tsx
|
|
5386
|
-
import { existsSync as
|
|
5537
|
+
import { existsSync as existsSync10, statSync as statSync6 } from "fs";
|
|
5387
5538
|
import { render } from "ink";
|
|
5388
5539
|
import React15, { useState as useState7 } from "react";
|
|
5389
5540
|
|
|
@@ -5481,8 +5632,8 @@ function PlanStateBlock({ planState }) {
|
|
|
5481
5632
|
}
|
|
5482
5633
|
|
|
5483
5634
|
// src/cli/ui/markdown.tsx
|
|
5484
|
-
import { readFileSync as
|
|
5485
|
-
import { isAbsolute as
|
|
5635
|
+
import { readFileSync as readFileSync11, statSync as statSync5 } from "fs";
|
|
5636
|
+
import { isAbsolute as isAbsolute4, join as join8 } from "path";
|
|
5486
5637
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
5487
5638
|
import React2 from "react";
|
|
5488
5639
|
var SUPERSCRIPT = {
|
|
@@ -5560,10 +5711,10 @@ function parseCitationUrl(url) {
|
|
|
5560
5711
|
function validateCitation(url, projectRoot) {
|
|
5561
5712
|
const parts = parseCitationUrl(url);
|
|
5562
5713
|
if (!parts || !parts.path) return { ok: false, reason: "empty path" };
|
|
5563
|
-
const fullPath =
|
|
5714
|
+
const fullPath = isAbsolute4(parts.path) ? parts.path : join8(projectRoot, parts.path);
|
|
5564
5715
|
let stat;
|
|
5565
5716
|
try {
|
|
5566
|
-
stat =
|
|
5717
|
+
stat = statSync5(fullPath);
|
|
5567
5718
|
} catch {
|
|
5568
5719
|
return { ok: false, reason: "file not found" };
|
|
5569
5720
|
}
|
|
@@ -5571,7 +5722,7 @@ function validateCitation(url, projectRoot) {
|
|
|
5571
5722
|
if (parts.startLine === void 0) return { ok: true };
|
|
5572
5723
|
let lineCount;
|
|
5573
5724
|
try {
|
|
5574
|
-
lineCount =
|
|
5725
|
+
lineCount = readFileSync11(fullPath, "utf8").split("\n").length;
|
|
5575
5726
|
} catch {
|
|
5576
5727
|
return { ok: false, reason: "unreadable" };
|
|
5577
5728
|
}
|
|
@@ -6751,7 +6902,7 @@ function formatTokens(n) {
|
|
|
6751
6902
|
import { spawnSync } from "child_process";
|
|
6752
6903
|
|
|
6753
6904
|
// src/cli/commands/stats.ts
|
|
6754
|
-
import { existsSync as
|
|
6905
|
+
import { existsSync as existsSync9, readFileSync as readFileSync12 } from "fs";
|
|
6755
6906
|
function statsCommand(opts) {
|
|
6756
6907
|
if (opts.transcript) {
|
|
6757
6908
|
transcriptSummary(opts.transcript);
|
|
@@ -6760,11 +6911,11 @@ function statsCommand(opts) {
|
|
|
6760
6911
|
dashboard(opts);
|
|
6761
6912
|
}
|
|
6762
6913
|
function transcriptSummary(path) {
|
|
6763
|
-
if (!
|
|
6914
|
+
if (!existsSync9(path)) {
|
|
6764
6915
|
console.error(`no such transcript: ${path}`);
|
|
6765
6916
|
process.exit(1);
|
|
6766
6917
|
}
|
|
6767
|
-
const lines =
|
|
6918
|
+
const lines = readFileSync12(path, "utf8").split(/\r?\n/).filter(Boolean);
|
|
6768
6919
|
let assistantTurns = 0;
|
|
6769
6920
|
let toolCalls = 0;
|
|
6770
6921
|
let lastTurn = 0;
|
|
@@ -8298,8 +8449,26 @@ function App({
|
|
|
8298
8449
|
});
|
|
8299
8450
|
};
|
|
8300
8451
|
const timer = PLAIN_UI ? null : setInterval(flush, FLUSH_INTERVAL_MS);
|
|
8452
|
+
let modelInput = text;
|
|
8453
|
+
if (codeMode?.rootDir) {
|
|
8454
|
+
const expanded = expandAtMentions(text, codeMode.rootDir);
|
|
8455
|
+
if (expanded.expansions.length > 0) {
|
|
8456
|
+
modelInput = expanded.text;
|
|
8457
|
+
const inlined = expanded.expansions.filter((ex) => ex.ok).map((ex) => `${ex.path} (${(ex.bytes ?? 0).toLocaleString()} bytes)`);
|
|
8458
|
+
const skipped = expanded.expansions.filter((ex) => !ex.ok).map((ex) => `${ex.path} (${ex.skip})`);
|
|
8459
|
+
const parts = [];
|
|
8460
|
+
if (inlined.length > 0) parts.push(`inlined ${inlined.join(", ")}`);
|
|
8461
|
+
if (skipped.length > 0) parts.push(`skipped ${skipped.join(", ")}`);
|
|
8462
|
+
if (parts.length > 0) {
|
|
8463
|
+
setHistorical((prev) => [
|
|
8464
|
+
...prev,
|
|
8465
|
+
{ id: `at-${Date.now()}`, role: "info", text: `\u25B8 @mentions: ${parts.join("; ")}` }
|
|
8466
|
+
]);
|
|
8467
|
+
}
|
|
8468
|
+
}
|
|
8469
|
+
}
|
|
8301
8470
|
try {
|
|
8302
|
-
for await (const ev of loop.step(
|
|
8471
|
+
for await (const ev of loop.step(modelInput)) {
|
|
8303
8472
|
writeTranscript(ev);
|
|
8304
8473
|
if (ev.role !== "status") {
|
|
8305
8474
|
setStatusLine((cur) => cur ? null : cur);
|
|
@@ -9014,7 +9183,7 @@ async function chatCommand(opts) {
|
|
|
9014
9183
|
const prior = loadSessionMessages(opts.session);
|
|
9015
9184
|
if (prior.length > 0) {
|
|
9016
9185
|
const p = sessionPath(opts.session);
|
|
9017
|
-
const mtime =
|
|
9186
|
+
const mtime = existsSync10(p) ? statSync6(p).mtime : /* @__PURE__ */ new Date();
|
|
9018
9187
|
sessionPreview = { messageCount: prior.length, lastActive: mtime };
|
|
9019
9188
|
}
|
|
9020
9189
|
} else if (opts.session && opts.forceNew) {
|
|
@@ -9045,10 +9214,10 @@ async function chatCommand(opts) {
|
|
|
9045
9214
|
}
|
|
9046
9215
|
|
|
9047
9216
|
// src/cli/commands/code.tsx
|
|
9048
|
-
import { basename, resolve as
|
|
9217
|
+
import { basename, resolve as resolve6 } from "path";
|
|
9049
9218
|
async function codeCommand(opts = {}) {
|
|
9050
9219
|
const { codeSystemPrompt: codeSystemPrompt2 } = await import("./prompt-75XLIUTO.js");
|
|
9051
|
-
const rootDir =
|
|
9220
|
+
const rootDir = resolve6(opts.dir ?? process.cwd());
|
|
9052
9221
|
const session = opts.noSession ? void 0 : `code-${sanitizeName(basename(rootDir))}`;
|
|
9053
9222
|
const tools = new ToolRegistry();
|
|
9054
9223
|
registerFilesystemTools(tools, { rootDir });
|
|
@@ -10067,13 +10236,13 @@ function planUpdate(input) {
|
|
|
10067
10236
|
};
|
|
10068
10237
|
}
|
|
10069
10238
|
function defaultSpawn(argv) {
|
|
10070
|
-
return new Promise((
|
|
10239
|
+
return new Promise((resolve7, reject) => {
|
|
10071
10240
|
const child = spawn4(argv[0], argv.slice(1), {
|
|
10072
10241
|
stdio: "inherit",
|
|
10073
10242
|
shell: process.platform === "win32"
|
|
10074
10243
|
});
|
|
10075
10244
|
child.once("error", reject);
|
|
10076
|
-
child.once("exit", (code) =>
|
|
10245
|
+
child.once("exit", (code) => resolve7(code ?? 1));
|
|
10077
10246
|
});
|
|
10078
10247
|
}
|
|
10079
10248
|
async function updateCommand(opts = {}) {
|