reasonix 0.5.0 → 0.5.2
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 +317 -204
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +50 -1
- package/dist/index.js +351 -71
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -696,6 +696,170 @@ async function runHooks(opts) {
|
|
|
696
696
|
return { event, outcomes, blocked };
|
|
697
697
|
}
|
|
698
698
|
|
|
699
|
+
// src/tokenizer.ts
|
|
700
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
701
|
+
import { createRequire } from "module";
|
|
702
|
+
import { dirname as dirname2, join as join3 } from "path";
|
|
703
|
+
import { fileURLToPath } from "url";
|
|
704
|
+
import { gunzipSync } from "zlib";
|
|
705
|
+
function buildByteToChar() {
|
|
706
|
+
const result = new Array(256);
|
|
707
|
+
const bs = [];
|
|
708
|
+
for (let b = 33; b <= 126; b++) bs.push(b);
|
|
709
|
+
for (let b = 161; b <= 172; b++) bs.push(b);
|
|
710
|
+
for (let b = 174; b <= 255; b++) bs.push(b);
|
|
711
|
+
const cs = bs.slice();
|
|
712
|
+
let n = 0;
|
|
713
|
+
for (let b = 0; b < 256; b++) {
|
|
714
|
+
if (!bs.includes(b)) {
|
|
715
|
+
bs.push(b);
|
|
716
|
+
cs.push(256 + n);
|
|
717
|
+
n++;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
for (let i = 0; i < bs.length; i++) {
|
|
721
|
+
result[bs[i]] = String.fromCodePoint(cs[i]);
|
|
722
|
+
}
|
|
723
|
+
return result;
|
|
724
|
+
}
|
|
725
|
+
var cached = null;
|
|
726
|
+
function resolveDataPath() {
|
|
727
|
+
if (process.env.REASONIX_TOKENIZER_PATH) return process.env.REASONIX_TOKENIZER_PATH;
|
|
728
|
+
try {
|
|
729
|
+
const here = dirname2(fileURLToPath(import.meta.url));
|
|
730
|
+
return join3(here, "..", "data", "deepseek-tokenizer.json.gz");
|
|
731
|
+
} catch {
|
|
732
|
+
const req = createRequire(import.meta.url);
|
|
733
|
+
return join3(
|
|
734
|
+
dirname2(req.resolve("reasonix/package.json")),
|
|
735
|
+
"data",
|
|
736
|
+
"deepseek-tokenizer.json.gz"
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
function loadTokenizer() {
|
|
741
|
+
if (cached) return cached;
|
|
742
|
+
const buf = readFileSync3(resolveDataPath());
|
|
743
|
+
const json = gunzipSync(buf).toString("utf8");
|
|
744
|
+
const data = JSON.parse(json);
|
|
745
|
+
const mergeRank = /* @__PURE__ */ new Map();
|
|
746
|
+
for (let i = 0; i < data.model.merges.length; i++) {
|
|
747
|
+
mergeRank.set(data.model.merges[i], i);
|
|
748
|
+
}
|
|
749
|
+
const splitRegexes = [];
|
|
750
|
+
for (const p of data.pre_tokenizer.pretokenizers) {
|
|
751
|
+
if (p.type === "Split") {
|
|
752
|
+
splitRegexes.push(new RegExp(p.pattern.Regex, "gu"));
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
const addedMap = /* @__PURE__ */ new Map();
|
|
756
|
+
const addedContents = [];
|
|
757
|
+
for (const t of data.added_tokens) {
|
|
758
|
+
if (!t.special) {
|
|
759
|
+
addedMap.set(t.content, t.id);
|
|
760
|
+
addedContents.push(t.content);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
addedContents.sort((a, b) => b.length - a.length);
|
|
764
|
+
const addedPattern = addedContents.length ? new RegExp(addedContents.map(escapeRegex).join("|"), "g") : null;
|
|
765
|
+
cached = {
|
|
766
|
+
vocab: data.model.vocab,
|
|
767
|
+
mergeRank,
|
|
768
|
+
splitRegexes,
|
|
769
|
+
byteToChar: buildByteToChar(),
|
|
770
|
+
addedPattern,
|
|
771
|
+
addedMap
|
|
772
|
+
};
|
|
773
|
+
return cached;
|
|
774
|
+
}
|
|
775
|
+
function escapeRegex(s) {
|
|
776
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
777
|
+
}
|
|
778
|
+
function applySplit(chunks, re) {
|
|
779
|
+
const out = [];
|
|
780
|
+
for (const chunk of chunks) {
|
|
781
|
+
if (!chunk) continue;
|
|
782
|
+
re.lastIndex = 0;
|
|
783
|
+
let last = 0;
|
|
784
|
+
for (const m of chunk.matchAll(re)) {
|
|
785
|
+
const idx = m.index ?? 0;
|
|
786
|
+
if (idx > last) out.push(chunk.slice(last, idx));
|
|
787
|
+
if (m[0].length > 0) out.push(m[0]);
|
|
788
|
+
last = idx + m[0].length;
|
|
789
|
+
}
|
|
790
|
+
if (last < chunk.length) out.push(chunk.slice(last));
|
|
791
|
+
}
|
|
792
|
+
return out;
|
|
793
|
+
}
|
|
794
|
+
function byteLevelEncode(s, byteToChar) {
|
|
795
|
+
const bytes = new TextEncoder().encode(s);
|
|
796
|
+
let out = "";
|
|
797
|
+
for (let i = 0; i < bytes.length; i++) out += byteToChar[bytes[i]];
|
|
798
|
+
return out;
|
|
799
|
+
}
|
|
800
|
+
function bpeEncode(piece, mergeRank) {
|
|
801
|
+
if (piece.length <= 1) return piece ? [piece] : [];
|
|
802
|
+
let word = Array.from(piece);
|
|
803
|
+
while (true) {
|
|
804
|
+
let bestIdx = -1;
|
|
805
|
+
let bestRank = Number.POSITIVE_INFINITY;
|
|
806
|
+
for (let i = 0; i < word.length - 1; i++) {
|
|
807
|
+
const pair = `${word[i]} ${word[i + 1]}`;
|
|
808
|
+
const rank = mergeRank.get(pair);
|
|
809
|
+
if (rank !== void 0 && rank < bestRank) {
|
|
810
|
+
bestRank = rank;
|
|
811
|
+
bestIdx = i;
|
|
812
|
+
if (rank === 0) break;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
if (bestIdx < 0) break;
|
|
816
|
+
word = [
|
|
817
|
+
...word.slice(0, bestIdx),
|
|
818
|
+
word[bestIdx] + word[bestIdx + 1],
|
|
819
|
+
...word.slice(bestIdx + 2)
|
|
820
|
+
];
|
|
821
|
+
if (word.length === 1) break;
|
|
822
|
+
}
|
|
823
|
+
return word;
|
|
824
|
+
}
|
|
825
|
+
function encode(text) {
|
|
826
|
+
if (!text) return [];
|
|
827
|
+
const t = loadTokenizer();
|
|
828
|
+
const ids = [];
|
|
829
|
+
const process2 = (segment) => {
|
|
830
|
+
if (!segment) return;
|
|
831
|
+
let chunks = [segment];
|
|
832
|
+
for (const re of t.splitRegexes) chunks = applySplit(chunks, re);
|
|
833
|
+
for (const chunk of chunks) {
|
|
834
|
+
if (!chunk) continue;
|
|
835
|
+
const byteLevel = byteLevelEncode(chunk, t.byteToChar);
|
|
836
|
+
const pieces = bpeEncode(byteLevel, t.mergeRank);
|
|
837
|
+
for (const p of pieces) {
|
|
838
|
+
const id = t.vocab[p];
|
|
839
|
+
if (id !== void 0) ids.push(id);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
};
|
|
843
|
+
if (t.addedPattern) {
|
|
844
|
+
t.addedPattern.lastIndex = 0;
|
|
845
|
+
let last = 0;
|
|
846
|
+
for (const m of text.matchAll(t.addedPattern)) {
|
|
847
|
+
const idx = m.index ?? 0;
|
|
848
|
+
if (idx > last) process2(text.slice(last, idx));
|
|
849
|
+
const id = t.addedMap.get(m[0]);
|
|
850
|
+
if (id !== void 0) ids.push(id);
|
|
851
|
+
last = idx + m[0].length;
|
|
852
|
+
}
|
|
853
|
+
if (last < text.length) process2(text.slice(last));
|
|
854
|
+
} else {
|
|
855
|
+
process2(text);
|
|
856
|
+
}
|
|
857
|
+
return ids;
|
|
858
|
+
}
|
|
859
|
+
function countTokens(text) {
|
|
860
|
+
return encode(text).length;
|
|
861
|
+
}
|
|
862
|
+
|
|
699
863
|
// src/repair/flatten.ts
|
|
700
864
|
function analyzeSchema(schema) {
|
|
701
865
|
if (!schema) return { shouldFlatten: false, leafCount: 0, maxDepth: 0 };
|
|
@@ -846,7 +1010,14 @@ var ToolRegistry = class {
|
|
|
846
1010
|
try {
|
|
847
1011
|
const result = await tool.fn(args, { signal: opts.signal });
|
|
848
1012
|
const str = typeof result === "string" ? result : JSON.stringify(result);
|
|
849
|
-
|
|
1013
|
+
let clipped = str;
|
|
1014
|
+
if (opts.maxResultTokens !== void 0) {
|
|
1015
|
+
clipped = truncateForModelByTokens(clipped, opts.maxResultTokens);
|
|
1016
|
+
}
|
|
1017
|
+
if (opts.maxResultChars !== void 0) {
|
|
1018
|
+
clipped = truncateForModel(clipped, opts.maxResultChars);
|
|
1019
|
+
}
|
|
1020
|
+
return clipped;
|
|
850
1021
|
} catch (err) {
|
|
851
1022
|
const e = err;
|
|
852
1023
|
if (typeof e.toToolResult === "function") {
|
|
@@ -880,6 +1051,7 @@ function hasDotKey(obj) {
|
|
|
880
1051
|
|
|
881
1052
|
// src/mcp/registry.ts
|
|
882
1053
|
var DEFAULT_MAX_RESULT_CHARS = 32e3;
|
|
1054
|
+
var DEFAULT_MAX_RESULT_TOKENS = 8e3;
|
|
883
1055
|
async function bridgeMcpTools(client, opts = {}) {
|
|
884
1056
|
const registry = opts.registry ?? new ToolRegistry({ autoFlatten: opts.autoFlatten });
|
|
885
1057
|
const prefix = opts.namePrefix ?? "";
|
|
@@ -936,6 +1108,61 @@ function truncateForModel(s, maxChars) {
|
|
|
936
1108
|
|
|
937
1109
|
${tail}`;
|
|
938
1110
|
}
|
|
1111
|
+
function truncateForModelByTokens(s, maxTokens) {
|
|
1112
|
+
if (maxTokens <= 0) return "";
|
|
1113
|
+
if (s.length <= maxTokens) return s;
|
|
1114
|
+
if (s.length <= maxTokens * 4) {
|
|
1115
|
+
const tokens = countTokens(s);
|
|
1116
|
+
if (tokens <= maxTokens) return s;
|
|
1117
|
+
}
|
|
1118
|
+
const markerOverhead = 48;
|
|
1119
|
+
const contentBudget = Math.max(0, maxTokens - markerOverhead);
|
|
1120
|
+
const tailBudget = Math.min(256, Math.floor(contentBudget * 0.1));
|
|
1121
|
+
const headBudget = Math.max(0, contentBudget - tailBudget);
|
|
1122
|
+
const head = sizePrefixToTokens(s, headBudget);
|
|
1123
|
+
const tail = sizeSuffixToTokens(s, tailBudget);
|
|
1124
|
+
const droppedChars = s.length - head.length - tail.length;
|
|
1125
|
+
const headTokens = head ? countTokens(head) : 0;
|
|
1126
|
+
const tailTokens = tail ? countTokens(tail) : 0;
|
|
1127
|
+
const sampleChars = head.length + tail.length;
|
|
1128
|
+
const sampleTokens = headTokens + tailTokens;
|
|
1129
|
+
const ratio = sampleChars > 0 ? sampleTokens / sampleChars : 0.3;
|
|
1130
|
+
const estTotalTokens = Math.ceil(s.length * ratio);
|
|
1131
|
+
const droppedTokens = Math.max(0, estTotalTokens - sampleTokens);
|
|
1132
|
+
return `${head}
|
|
1133
|
+
|
|
1134
|
+
[\u2026truncated ~${droppedTokens} tokens (${droppedChars} chars) \u2014 raise BridgeOptions.maxResultTokens, or call the tool with a narrower scope (filter, head, pagination)\u2026]
|
|
1135
|
+
|
|
1136
|
+
${tail}`;
|
|
1137
|
+
}
|
|
1138
|
+
function sizePrefixToTokens(s, budget) {
|
|
1139
|
+
if (budget <= 0 || s.length === 0) return "";
|
|
1140
|
+
let size = Math.min(s.length, budget * 4);
|
|
1141
|
+
for (let iter = 0; iter < 6; iter++) {
|
|
1142
|
+
if (size <= 0) return "";
|
|
1143
|
+
const slice = s.slice(0, size);
|
|
1144
|
+
const count = countTokens(slice);
|
|
1145
|
+
if (count <= budget) return slice;
|
|
1146
|
+
const next = Math.floor(size * (budget / count) * 0.95);
|
|
1147
|
+
if (next >= size) return s.slice(0, Math.max(0, size - 1));
|
|
1148
|
+
size = next;
|
|
1149
|
+
}
|
|
1150
|
+
return s.slice(0, Math.max(0, size));
|
|
1151
|
+
}
|
|
1152
|
+
function sizeSuffixToTokens(s, budget) {
|
|
1153
|
+
if (budget <= 0 || s.length === 0) return "";
|
|
1154
|
+
let size = Math.min(s.length, budget * 4);
|
|
1155
|
+
for (let iter = 0; iter < 6; iter++) {
|
|
1156
|
+
if (size <= 0) return "";
|
|
1157
|
+
const slice = s.slice(-size);
|
|
1158
|
+
const count = countTokens(slice);
|
|
1159
|
+
if (count <= budget) return slice;
|
|
1160
|
+
const next = Math.floor(size * (budget / count) * 0.95);
|
|
1161
|
+
if (next >= size) return s.slice(-Math.max(0, size - 1));
|
|
1162
|
+
size = next;
|
|
1163
|
+
}
|
|
1164
|
+
return s.slice(-Math.max(0, size));
|
|
1165
|
+
}
|
|
939
1166
|
function blockToString(block) {
|
|
940
1167
|
if (block.type === "text") return block.text;
|
|
941
1168
|
if (block.type === "image") return `[image ${block.mimeType}, ${block.data.length} chars base64]`;
|
|
@@ -1321,19 +1548,19 @@ import {
|
|
|
1321
1548
|
chmodSync as chmodSync2,
|
|
1322
1549
|
existsSync as existsSync2,
|
|
1323
1550
|
mkdirSync as mkdirSync2,
|
|
1324
|
-
readFileSync as
|
|
1551
|
+
readFileSync as readFileSync4,
|
|
1325
1552
|
readdirSync,
|
|
1326
1553
|
statSync,
|
|
1327
1554
|
unlinkSync,
|
|
1328
1555
|
writeFileSync as writeFileSync2
|
|
1329
1556
|
} from "fs";
|
|
1330
1557
|
import { homedir as homedir3 } from "os";
|
|
1331
|
-
import { dirname as
|
|
1558
|
+
import { dirname as dirname3, join as join4 } from "path";
|
|
1332
1559
|
function sessionsDir() {
|
|
1333
|
-
return
|
|
1560
|
+
return join4(homedir3(), ".reasonix", "sessions");
|
|
1334
1561
|
}
|
|
1335
1562
|
function sessionPath(name) {
|
|
1336
|
-
return
|
|
1563
|
+
return join4(sessionsDir(), `${sanitizeName(name)}.jsonl`);
|
|
1337
1564
|
}
|
|
1338
1565
|
function sanitizeName(name) {
|
|
1339
1566
|
const cleaned = name.replace(/[^\w\-\u4e00-\u9fa5]/g, "_").slice(0, 64);
|
|
@@ -1343,7 +1570,7 @@ function loadSessionMessages(name) {
|
|
|
1343
1570
|
const path = sessionPath(name);
|
|
1344
1571
|
if (!existsSync2(path)) return [];
|
|
1345
1572
|
try {
|
|
1346
|
-
const raw =
|
|
1573
|
+
const raw = readFileSync4(path, "utf8");
|
|
1347
1574
|
const out = [];
|
|
1348
1575
|
for (const line of raw.split(/\r?\n/)) {
|
|
1349
1576
|
const trimmed = line.trim();
|
|
@@ -1361,7 +1588,7 @@ function loadSessionMessages(name) {
|
|
|
1361
1588
|
}
|
|
1362
1589
|
function appendSessionMessage(name, message) {
|
|
1363
1590
|
const path = sessionPath(name);
|
|
1364
|
-
mkdirSync2(
|
|
1591
|
+
mkdirSync2(dirname3(path), { recursive: true });
|
|
1365
1592
|
appendFileSync(path, `${JSON.stringify(message)}
|
|
1366
1593
|
`, "utf8");
|
|
1367
1594
|
try {
|
|
@@ -1375,7 +1602,7 @@ function listSessions() {
|
|
|
1375
1602
|
try {
|
|
1376
1603
|
const files = readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
|
|
1377
1604
|
return files.map((file) => {
|
|
1378
|
-
const path =
|
|
1605
|
+
const path = join4(dir, file);
|
|
1379
1606
|
const stat = statSync(path);
|
|
1380
1607
|
const name = file.replace(/\.jsonl$/, "");
|
|
1381
1608
|
const messageCount = countLines(path);
|
|
@@ -1396,7 +1623,7 @@ function deleteSession(name) {
|
|
|
1396
1623
|
}
|
|
1397
1624
|
function rewriteSession(name, messages) {
|
|
1398
1625
|
const path = sessionPath(name);
|
|
1399
|
-
mkdirSync2(
|
|
1626
|
+
mkdirSync2(dirname3(path), { recursive: true });
|
|
1400
1627
|
const body = messages.map((m) => JSON.stringify(m)).join("\n");
|
|
1401
1628
|
writeFileSync2(path, body ? `${body}
|
|
1402
1629
|
` : "", "utf8");
|
|
@@ -1407,7 +1634,7 @@ function rewriteSession(name, messages) {
|
|
|
1407
1634
|
}
|
|
1408
1635
|
function countLines(path) {
|
|
1409
1636
|
try {
|
|
1410
|
-
const raw =
|
|
1637
|
+
const raw = readFileSync4(path, "utf8");
|
|
1411
1638
|
return raw.split(/\r?\n/).filter((l) => l.trim()).length;
|
|
1412
1639
|
} catch {
|
|
1413
1640
|
return 0;
|
|
@@ -2054,7 +2281,7 @@ ${reason}`;
|
|
|
2054
2281
|
} else {
|
|
2055
2282
|
result = await this.tools.dispatch(name, args, {
|
|
2056
2283
|
signal,
|
|
2057
|
-
|
|
2284
|
+
maxResultTokens: DEFAULT_MAX_RESULT_TOKENS
|
|
2058
2285
|
});
|
|
2059
2286
|
const postReport = await runHooks({
|
|
2060
2287
|
hooks: this.hooks,
|
|
@@ -3162,6 +3389,50 @@ function tokenizeCommand(cmd) {
|
|
|
3162
3389
|
if (cur.length > 0) out.push(cur);
|
|
3163
3390
|
return out;
|
|
3164
3391
|
}
|
|
3392
|
+
function detectShellOperator(cmd) {
|
|
3393
|
+
const opPrefix = /^(?:2>&1|&>|\|{1,2}|&{1,2}|2>{1,2}|>{1,2}|<{1,2})/;
|
|
3394
|
+
let cur = "";
|
|
3395
|
+
let curQuoted = false;
|
|
3396
|
+
let quote = null;
|
|
3397
|
+
const check = () => {
|
|
3398
|
+
if (cur.length === 0 && !curQuoted) return null;
|
|
3399
|
+
if (!curQuoted) {
|
|
3400
|
+
const m = opPrefix.exec(cur);
|
|
3401
|
+
if (m) return m[0] ?? null;
|
|
3402
|
+
}
|
|
3403
|
+
return null;
|
|
3404
|
+
};
|
|
3405
|
+
for (let i = 0; i < cmd.length; i++) {
|
|
3406
|
+
const ch = cmd[i];
|
|
3407
|
+
if (quote) {
|
|
3408
|
+
if (ch === quote) {
|
|
3409
|
+
quote = null;
|
|
3410
|
+
} else if (ch === "\\" && quote === '"' && i + 1 < cmd.length) {
|
|
3411
|
+
cur += cmd[++i];
|
|
3412
|
+
curQuoted = true;
|
|
3413
|
+
} else {
|
|
3414
|
+
cur += ch;
|
|
3415
|
+
curQuoted = true;
|
|
3416
|
+
}
|
|
3417
|
+
continue;
|
|
3418
|
+
}
|
|
3419
|
+
if (ch === '"' || ch === "'") {
|
|
3420
|
+
quote = ch;
|
|
3421
|
+
curQuoted = true;
|
|
3422
|
+
continue;
|
|
3423
|
+
}
|
|
3424
|
+
if (ch === " " || ch === " ") {
|
|
3425
|
+
const op = check();
|
|
3426
|
+
if (op) return op;
|
|
3427
|
+
cur = "";
|
|
3428
|
+
curQuoted = false;
|
|
3429
|
+
continue;
|
|
3430
|
+
}
|
|
3431
|
+
cur += ch;
|
|
3432
|
+
}
|
|
3433
|
+
if (quote) return null;
|
|
3434
|
+
return check();
|
|
3435
|
+
}
|
|
3165
3436
|
function isAllowed(cmd, extra = []) {
|
|
3166
3437
|
const normalized = cmd.trim().replace(/\s+/g, " ");
|
|
3167
3438
|
const allowlist = [...BUILTIN_ALLOWLIST, ...extra];
|
|
@@ -3174,6 +3445,12 @@ function isAllowed(cmd, extra = []) {
|
|
|
3174
3445
|
async function runCommand(cmd, opts) {
|
|
3175
3446
|
const argv = tokenizeCommand(cmd);
|
|
3176
3447
|
if (argv.length === 0) throw new Error("run_command: empty command");
|
|
3448
|
+
const operator = detectShellOperator(cmd);
|
|
3449
|
+
if (operator !== null) {
|
|
3450
|
+
throw new Error(
|
|
3451
|
+
`run_command: shell operator "${operator}" is not supported \u2014 this tool spawns one process, no shell expansion. Split into separate run_command calls and combine the output in your reasoning (e.g. instead of \`grep foo *.ts | wc -l\`, call \`grep -c foo *.ts\` or two separate commands). To pass "${operator}" as a literal argument, wrap it in quotes.`
|
|
3452
|
+
);
|
|
3453
|
+
}
|
|
3177
3454
|
const timeoutMs = (opts.timeoutSec ?? DEFAULT_TIMEOUT_SEC) * 1e3;
|
|
3178
3455
|
const maxChars = opts.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
|
|
3179
3456
|
const spawnOpts = {
|
|
@@ -3351,7 +3628,7 @@ function registerShellTools(registry, opts) {
|
|
|
3351
3628
|
properties: {
|
|
3352
3629
|
command: {
|
|
3353
3630
|
type: "string",
|
|
3354
|
-
description:
|
|
3631
|
+
description: 'Full command line. Tokenized with POSIX-ish quoting; no shell expansion. Pipes (`|`), redirects (`>`, `<`, `2>`), and `&&`/`||` chaining are rejected with an error \u2014 split into separate calls instead. To pass an operator character as a literal argument (e.g. a regex), wrap it in quotes: `grep "a|b" file.txt`.'
|
|
3355
3632
|
},
|
|
3356
3633
|
timeoutSec: {
|
|
3357
3634
|
type: "integer",
|
|
@@ -3570,12 +3847,12 @@ ${i + 1}. ${r.title}`);
|
|
|
3570
3847
|
}
|
|
3571
3848
|
|
|
3572
3849
|
// src/env.ts
|
|
3573
|
-
import { readFileSync as
|
|
3850
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
3574
3851
|
import { resolve as resolve3 } from "path";
|
|
3575
3852
|
function loadDotenv(path = ".env") {
|
|
3576
3853
|
let raw;
|
|
3577
3854
|
try {
|
|
3578
|
-
raw =
|
|
3855
|
+
raw = readFileSync5(resolve3(process.cwd(), path), "utf8");
|
|
3579
3856
|
} catch {
|
|
3580
3857
|
return;
|
|
3581
3858
|
}
|
|
@@ -3594,7 +3871,7 @@ function loadDotenv(path = ".env") {
|
|
|
3594
3871
|
}
|
|
3595
3872
|
|
|
3596
3873
|
// src/transcript.ts
|
|
3597
|
-
import { createWriteStream, readFileSync as
|
|
3874
|
+
import { createWriteStream, readFileSync as readFileSync6 } from "fs";
|
|
3598
3875
|
function recordFromLoopEvent(ev, extra) {
|
|
3599
3876
|
const rec = {
|
|
3600
3877
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3645,7 +3922,7 @@ function openTranscriptFile(path, meta) {
|
|
|
3645
3922
|
return stream;
|
|
3646
3923
|
}
|
|
3647
3924
|
function readTranscript(path) {
|
|
3648
|
-
const raw =
|
|
3925
|
+
const raw = readFileSync6(path, "utf8");
|
|
3649
3926
|
return parseTranscript(raw);
|
|
3650
3927
|
}
|
|
3651
3928
|
function isPlanStateEmptyShape(s) {
|
|
@@ -4731,8 +5008,8 @@ async function trySection(load) {
|
|
|
4731
5008
|
}
|
|
4732
5009
|
|
|
4733
5010
|
// src/code/edit-blocks.ts
|
|
4734
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as
|
|
4735
|
-
import { dirname as
|
|
5011
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
5012
|
+
import { dirname as dirname5, resolve as resolve4 } from "path";
|
|
4736
5013
|
var BLOCK_RE = /^(\S[^\n]*)\n<{7} SEARCH\n([\s\S]*?)\n?={7}\n([\s\S]*?)\n?>{7} REPLACE/gm;
|
|
4737
5014
|
function parseEditBlocks(text) {
|
|
4738
5015
|
const out = [];
|
|
@@ -4770,11 +5047,11 @@ function applyEditBlock(block, rootDir) {
|
|
|
4770
5047
|
message: "file does not exist; to create it, use an empty SEARCH block"
|
|
4771
5048
|
};
|
|
4772
5049
|
}
|
|
4773
|
-
mkdirSync3(
|
|
5050
|
+
mkdirSync3(dirname5(absTarget), { recursive: true });
|
|
4774
5051
|
writeFileSync3(absTarget, block.replace, "utf8");
|
|
4775
5052
|
return { path: block.path, status: "created" };
|
|
4776
5053
|
}
|
|
4777
|
-
const content =
|
|
5054
|
+
const content = readFileSync7(absTarget, "utf8");
|
|
4778
5055
|
if (searchEmpty) {
|
|
4779
5056
|
return {
|
|
4780
5057
|
path: block.path,
|
|
@@ -4813,7 +5090,7 @@ function snapshotBeforeEdits(blocks, rootDir) {
|
|
|
4813
5090
|
continue;
|
|
4814
5091
|
}
|
|
4815
5092
|
try {
|
|
4816
|
-
snapshots.push({ path: b.path, prevContent:
|
|
5093
|
+
snapshots.push({ path: b.path, prevContent: readFileSync7(abs, "utf8") });
|
|
4817
5094
|
} catch {
|
|
4818
5095
|
snapshots.push({ path: b.path, prevContent: null });
|
|
4819
5096
|
}
|
|
@@ -4856,25 +5133,25 @@ function sep() {
|
|
|
4856
5133
|
}
|
|
4857
5134
|
|
|
4858
5135
|
// src/version.ts
|
|
4859
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as
|
|
5136
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
|
|
4860
5137
|
import { homedir as homedir4 } from "os";
|
|
4861
|
-
import { dirname as
|
|
4862
|
-
import { fileURLToPath } from "url";
|
|
5138
|
+
import { dirname as dirname6, join as join6 } from "path";
|
|
5139
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4863
5140
|
var REGISTRY_URL = "https://registry.npmjs.org/reasonix/latest";
|
|
4864
5141
|
var LATEST_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4865
5142
|
var LATEST_FETCH_TIMEOUT_MS = 2e3;
|
|
4866
5143
|
function readPackageVersion() {
|
|
4867
5144
|
try {
|
|
4868
|
-
let dir =
|
|
5145
|
+
let dir = dirname6(fileURLToPath2(import.meta.url));
|
|
4869
5146
|
for (let i = 0; i < 6; i++) {
|
|
4870
|
-
const p =
|
|
5147
|
+
const p = join6(dir, "package.json");
|
|
4871
5148
|
if (existsSync5(p)) {
|
|
4872
|
-
const pkg = JSON.parse(
|
|
5149
|
+
const pkg = JSON.parse(readFileSync8(p, "utf8"));
|
|
4873
5150
|
if (pkg?.name === "reasonix" && typeof pkg.version === "string") {
|
|
4874
5151
|
return pkg.version;
|
|
4875
5152
|
}
|
|
4876
5153
|
}
|
|
4877
|
-
const parent =
|
|
5154
|
+
const parent = dirname6(dir);
|
|
4878
5155
|
if (parent === dir) break;
|
|
4879
5156
|
dir = parent;
|
|
4880
5157
|
}
|
|
@@ -4884,11 +5161,11 @@ function readPackageVersion() {
|
|
|
4884
5161
|
}
|
|
4885
5162
|
var VERSION = readPackageVersion();
|
|
4886
5163
|
function cachePath(homeDirOverride) {
|
|
4887
|
-
return
|
|
5164
|
+
return join6(homeDirOverride ?? homedir4(), ".reasonix", "version-cache.json");
|
|
4888
5165
|
}
|
|
4889
5166
|
function readCache(homeDirOverride) {
|
|
4890
5167
|
try {
|
|
4891
|
-
const raw =
|
|
5168
|
+
const raw = readFileSync8(cachePath(homeDirOverride), "utf8");
|
|
4892
5169
|
const parsed = JSON.parse(raw);
|
|
4893
5170
|
if (parsed && typeof parsed.version === "string" && typeof parsed.checkedAt === "number") {
|
|
4894
5171
|
return parsed;
|
|
@@ -4900,7 +5177,7 @@ function readCache(homeDirOverride) {
|
|
|
4900
5177
|
function writeCache(entry, homeDirOverride) {
|
|
4901
5178
|
try {
|
|
4902
5179
|
const p = cachePath(homeDirOverride);
|
|
4903
|
-
mkdirSync4(
|
|
5180
|
+
mkdirSync4(dirname6(p), { recursive: true });
|
|
4904
5181
|
writeFileSync4(p, JSON.stringify(entry), "utf8");
|
|
4905
5182
|
} catch {
|
|
4906
5183
|
}
|
|
@@ -4957,11 +5234,11 @@ function isNpxInstall() {
|
|
|
4957
5234
|
}
|
|
4958
5235
|
|
|
4959
5236
|
// src/usage.ts
|
|
4960
|
-
import { appendFileSync as appendFileSync2, existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as
|
|
5237
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync9, statSync as statSync3 } from "fs";
|
|
4961
5238
|
import { homedir as homedir5 } from "os";
|
|
4962
|
-
import { dirname as
|
|
5239
|
+
import { dirname as dirname7, join as join7 } from "path";
|
|
4963
5240
|
function defaultUsageLogPath(homeDirOverride) {
|
|
4964
|
-
return
|
|
5241
|
+
return join7(homeDirOverride ?? homedir5(), ".reasonix", "usage.jsonl");
|
|
4965
5242
|
}
|
|
4966
5243
|
function appendUsage(input) {
|
|
4967
5244
|
const record = {
|
|
@@ -4977,7 +5254,7 @@ function appendUsage(input) {
|
|
|
4977
5254
|
};
|
|
4978
5255
|
const path = input.path ?? defaultUsageLogPath();
|
|
4979
5256
|
try {
|
|
4980
|
-
mkdirSync5(
|
|
5257
|
+
mkdirSync5(dirname7(path), { recursive: true });
|
|
4981
5258
|
appendFileSync2(path, `${JSON.stringify(record)}
|
|
4982
5259
|
`, "utf8");
|
|
4983
5260
|
} catch {
|
|
@@ -4988,7 +5265,7 @@ function readUsageLog(path = defaultUsageLogPath()) {
|
|
|
4988
5265
|
if (!existsSync6(path)) return [];
|
|
4989
5266
|
let raw;
|
|
4990
5267
|
try {
|
|
4991
|
-
raw =
|
|
5268
|
+
raw = readFileSync9(path, "utf8");
|
|
4992
5269
|
} catch {
|
|
4993
5270
|
return [];
|
|
4994
5271
|
}
|
|
@@ -5181,8 +5458,8 @@ function PlanStateBlock({ planState }) {
|
|
|
5181
5458
|
}
|
|
5182
5459
|
|
|
5183
5460
|
// src/cli/ui/markdown.tsx
|
|
5184
|
-
import { readFileSync as
|
|
5185
|
-
import { isAbsolute as isAbsolute3, join as
|
|
5461
|
+
import { readFileSync as readFileSync10, statSync as statSync4 } from "fs";
|
|
5462
|
+
import { isAbsolute as isAbsolute3, join as join8 } from "path";
|
|
5186
5463
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
5187
5464
|
import React2 from "react";
|
|
5188
5465
|
var SUPERSCRIPT = {
|
|
@@ -5260,7 +5537,7 @@ function parseCitationUrl(url) {
|
|
|
5260
5537
|
function validateCitation(url, projectRoot) {
|
|
5261
5538
|
const parts = parseCitationUrl(url);
|
|
5262
5539
|
if (!parts || !parts.path) return { ok: false, reason: "empty path" };
|
|
5263
|
-
const fullPath = isAbsolute3(parts.path) ? parts.path :
|
|
5540
|
+
const fullPath = isAbsolute3(parts.path) ? parts.path : join8(projectRoot, parts.path);
|
|
5264
5541
|
let stat;
|
|
5265
5542
|
try {
|
|
5266
5543
|
stat = statSync4(fullPath);
|
|
@@ -5271,7 +5548,7 @@ function validateCitation(url, projectRoot) {
|
|
|
5271
5548
|
if (parts.startLine === void 0) return { ok: true };
|
|
5272
5549
|
let lineCount;
|
|
5273
5550
|
try {
|
|
5274
|
-
lineCount =
|
|
5551
|
+
lineCount = readFileSync10(fullPath, "utf8").split("\n").length;
|
|
5275
5552
|
} catch {
|
|
5276
5553
|
return { ok: false, reason: "unreadable" };
|
|
5277
5554
|
}
|
|
@@ -6450,170 +6727,6 @@ function formatTokens(n) {
|
|
|
6450
6727
|
// src/cli/ui/slash.ts
|
|
6451
6728
|
import { spawnSync } from "child_process";
|
|
6452
6729
|
|
|
6453
|
-
// src/tokenizer.ts
|
|
6454
|
-
import { readFileSync as readFileSync10 } from "fs";
|
|
6455
|
-
import { createRequire } from "module";
|
|
6456
|
-
import { dirname as dirname7, join as join8 } from "path";
|
|
6457
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6458
|
-
import { gunzipSync } from "zlib";
|
|
6459
|
-
function buildByteToChar() {
|
|
6460
|
-
const result = new Array(256);
|
|
6461
|
-
const bs = [];
|
|
6462
|
-
for (let b = 33; b <= 126; b++) bs.push(b);
|
|
6463
|
-
for (let b = 161; b <= 172; b++) bs.push(b);
|
|
6464
|
-
for (let b = 174; b <= 255; b++) bs.push(b);
|
|
6465
|
-
const cs = bs.slice();
|
|
6466
|
-
let n = 0;
|
|
6467
|
-
for (let b = 0; b < 256; b++) {
|
|
6468
|
-
if (!bs.includes(b)) {
|
|
6469
|
-
bs.push(b);
|
|
6470
|
-
cs.push(256 + n);
|
|
6471
|
-
n++;
|
|
6472
|
-
}
|
|
6473
|
-
}
|
|
6474
|
-
for (let i = 0; i < bs.length; i++) {
|
|
6475
|
-
result[bs[i]] = String.fromCodePoint(cs[i]);
|
|
6476
|
-
}
|
|
6477
|
-
return result;
|
|
6478
|
-
}
|
|
6479
|
-
var cached = null;
|
|
6480
|
-
function resolveDataPath() {
|
|
6481
|
-
if (process.env.REASONIX_TOKENIZER_PATH) return process.env.REASONIX_TOKENIZER_PATH;
|
|
6482
|
-
try {
|
|
6483
|
-
const here = dirname7(fileURLToPath2(import.meta.url));
|
|
6484
|
-
return join8(here, "..", "data", "deepseek-tokenizer.json.gz");
|
|
6485
|
-
} catch {
|
|
6486
|
-
const req = createRequire(import.meta.url);
|
|
6487
|
-
return join8(
|
|
6488
|
-
dirname7(req.resolve("reasonix/package.json")),
|
|
6489
|
-
"data",
|
|
6490
|
-
"deepseek-tokenizer.json.gz"
|
|
6491
|
-
);
|
|
6492
|
-
}
|
|
6493
|
-
}
|
|
6494
|
-
function loadTokenizer() {
|
|
6495
|
-
if (cached) return cached;
|
|
6496
|
-
const buf = readFileSync10(resolveDataPath());
|
|
6497
|
-
const json = gunzipSync(buf).toString("utf8");
|
|
6498
|
-
const data = JSON.parse(json);
|
|
6499
|
-
const mergeRank = /* @__PURE__ */ new Map();
|
|
6500
|
-
for (let i = 0; i < data.model.merges.length; i++) {
|
|
6501
|
-
mergeRank.set(data.model.merges[i], i);
|
|
6502
|
-
}
|
|
6503
|
-
const splitRegexes = [];
|
|
6504
|
-
for (const p of data.pre_tokenizer.pretokenizers) {
|
|
6505
|
-
if (p.type === "Split") {
|
|
6506
|
-
splitRegexes.push(new RegExp(p.pattern.Regex, "gu"));
|
|
6507
|
-
}
|
|
6508
|
-
}
|
|
6509
|
-
const addedMap = /* @__PURE__ */ new Map();
|
|
6510
|
-
const addedContents = [];
|
|
6511
|
-
for (const t of data.added_tokens) {
|
|
6512
|
-
if (!t.special) {
|
|
6513
|
-
addedMap.set(t.content, t.id);
|
|
6514
|
-
addedContents.push(t.content);
|
|
6515
|
-
}
|
|
6516
|
-
}
|
|
6517
|
-
addedContents.sort((a, b) => b.length - a.length);
|
|
6518
|
-
const addedPattern = addedContents.length ? new RegExp(addedContents.map(escapeRegex).join("|"), "g") : null;
|
|
6519
|
-
cached = {
|
|
6520
|
-
vocab: data.model.vocab,
|
|
6521
|
-
mergeRank,
|
|
6522
|
-
splitRegexes,
|
|
6523
|
-
byteToChar: buildByteToChar(),
|
|
6524
|
-
addedPattern,
|
|
6525
|
-
addedMap
|
|
6526
|
-
};
|
|
6527
|
-
return cached;
|
|
6528
|
-
}
|
|
6529
|
-
function escapeRegex(s) {
|
|
6530
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6531
|
-
}
|
|
6532
|
-
function applySplit(chunks, re) {
|
|
6533
|
-
const out = [];
|
|
6534
|
-
for (const chunk of chunks) {
|
|
6535
|
-
if (!chunk) continue;
|
|
6536
|
-
re.lastIndex = 0;
|
|
6537
|
-
let last = 0;
|
|
6538
|
-
for (const m of chunk.matchAll(re)) {
|
|
6539
|
-
const idx = m.index ?? 0;
|
|
6540
|
-
if (idx > last) out.push(chunk.slice(last, idx));
|
|
6541
|
-
if (m[0].length > 0) out.push(m[0]);
|
|
6542
|
-
last = idx + m[0].length;
|
|
6543
|
-
}
|
|
6544
|
-
if (last < chunk.length) out.push(chunk.slice(last));
|
|
6545
|
-
}
|
|
6546
|
-
return out;
|
|
6547
|
-
}
|
|
6548
|
-
function byteLevelEncode(s, byteToChar) {
|
|
6549
|
-
const bytes = new TextEncoder().encode(s);
|
|
6550
|
-
let out = "";
|
|
6551
|
-
for (let i = 0; i < bytes.length; i++) out += byteToChar[bytes[i]];
|
|
6552
|
-
return out;
|
|
6553
|
-
}
|
|
6554
|
-
function bpeEncode(piece, mergeRank) {
|
|
6555
|
-
if (piece.length <= 1) return piece ? [piece] : [];
|
|
6556
|
-
let word = Array.from(piece);
|
|
6557
|
-
while (true) {
|
|
6558
|
-
let bestIdx = -1;
|
|
6559
|
-
let bestRank = Number.POSITIVE_INFINITY;
|
|
6560
|
-
for (let i = 0; i < word.length - 1; i++) {
|
|
6561
|
-
const pair = `${word[i]} ${word[i + 1]}`;
|
|
6562
|
-
const rank = mergeRank.get(pair);
|
|
6563
|
-
if (rank !== void 0 && rank < bestRank) {
|
|
6564
|
-
bestRank = rank;
|
|
6565
|
-
bestIdx = i;
|
|
6566
|
-
if (rank === 0) break;
|
|
6567
|
-
}
|
|
6568
|
-
}
|
|
6569
|
-
if (bestIdx < 0) break;
|
|
6570
|
-
word = [
|
|
6571
|
-
...word.slice(0, bestIdx),
|
|
6572
|
-
word[bestIdx] + word[bestIdx + 1],
|
|
6573
|
-
...word.slice(bestIdx + 2)
|
|
6574
|
-
];
|
|
6575
|
-
if (word.length === 1) break;
|
|
6576
|
-
}
|
|
6577
|
-
return word;
|
|
6578
|
-
}
|
|
6579
|
-
function encode(text) {
|
|
6580
|
-
if (!text) return [];
|
|
6581
|
-
const t = loadTokenizer();
|
|
6582
|
-
const ids = [];
|
|
6583
|
-
const process2 = (segment) => {
|
|
6584
|
-
if (!segment) return;
|
|
6585
|
-
let chunks = [segment];
|
|
6586
|
-
for (const re of t.splitRegexes) chunks = applySplit(chunks, re);
|
|
6587
|
-
for (const chunk of chunks) {
|
|
6588
|
-
if (!chunk) continue;
|
|
6589
|
-
const byteLevel = byteLevelEncode(chunk, t.byteToChar);
|
|
6590
|
-
const pieces = bpeEncode(byteLevel, t.mergeRank);
|
|
6591
|
-
for (const p of pieces) {
|
|
6592
|
-
const id = t.vocab[p];
|
|
6593
|
-
if (id !== void 0) ids.push(id);
|
|
6594
|
-
}
|
|
6595
|
-
}
|
|
6596
|
-
};
|
|
6597
|
-
if (t.addedPattern) {
|
|
6598
|
-
t.addedPattern.lastIndex = 0;
|
|
6599
|
-
let last = 0;
|
|
6600
|
-
for (const m of text.matchAll(t.addedPattern)) {
|
|
6601
|
-
const idx = m.index ?? 0;
|
|
6602
|
-
if (idx > last) process2(text.slice(last, idx));
|
|
6603
|
-
const id = t.addedMap.get(m[0]);
|
|
6604
|
-
if (id !== void 0) ids.push(id);
|
|
6605
|
-
last = idx + m[0].length;
|
|
6606
|
-
}
|
|
6607
|
-
if (last < text.length) process2(text.slice(last));
|
|
6608
|
-
} else {
|
|
6609
|
-
process2(text);
|
|
6610
|
-
}
|
|
6611
|
-
return ids;
|
|
6612
|
-
}
|
|
6613
|
-
function countTokens(text) {
|
|
6614
|
-
return encode(text).length;
|
|
6615
|
-
}
|
|
6616
|
-
|
|
6617
6730
|
// src/cli/commands/stats.ts
|
|
6618
6731
|
import { existsSync as existsSync7, readFileSync as readFileSync11 } from "fs";
|
|
6619
6732
|
function statsCommand(opts) {
|