reasonix 0.5.4 → 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/index.js CHANGED
@@ -47,8 +47,8 @@ function computeWait(attempt, initial, cap, retryAfter) {
47
47
  }
48
48
  function sleep(ms, signal) {
49
49
  if (ms <= 0) return Promise.resolve();
50
- return new Promise((resolve7, reject) => {
51
- const timer = setTimeout(resolve7, ms);
50
+ return new Promise((resolve8, reject) => {
51
+ const timer = setTimeout(resolve8, ms);
52
52
  if (signal) {
53
53
  const onAbort = () => {
54
54
  clearTimeout(timer);
@@ -520,7 +520,7 @@ function matchesTool(hook, toolName) {
520
520
  }
521
521
  }
522
522
  function defaultSpawner(input) {
523
- return new Promise((resolve7) => {
523
+ return new Promise((resolve8) => {
524
524
  const child = spawn(input.command, {
525
525
  cwd: input.cwd,
526
526
  shell: true,
@@ -547,7 +547,7 @@ function defaultSpawner(input) {
547
547
  });
548
548
  child.once("error", (err) => {
549
549
  clearTimeout(timer);
550
- resolve7({
550
+ resolve8({
551
551
  exitCode: null,
552
552
  stdout,
553
553
  stderr,
@@ -557,7 +557,7 @@ function defaultSpawner(input) {
557
557
  });
558
558
  child.once("close", (code) => {
559
559
  clearTimeout(timer);
560
- resolve7({
560
+ resolve8({
561
561
  exitCode: code,
562
562
  stdout: stdout.trim(),
563
563
  stderr: stderr.trim(),
@@ -618,7 +618,7 @@ async function runHooks(opts) {
618
618
  }
619
619
 
620
620
  // src/tokenizer.ts
621
- import { readFileSync as readFileSync2 } from "fs";
621
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
622
622
  import { createRequire } from "module";
623
623
  import { dirname, join as join2 } from "path";
624
624
  import { fileURLToPath } from "url";
@@ -646,17 +646,24 @@ function buildByteToChar() {
646
646
  var cached = null;
647
647
  function resolveDataPath() {
648
648
  if (process.env.REASONIX_TOKENIZER_PATH) return process.env.REASONIX_TOKENIZER_PATH;
649
+ const candidates = [];
649
650
  try {
650
651
  const here = dirname(fileURLToPath(import.meta.url));
651
- return join2(here, "..", "data", "deepseek-tokenizer.json.gz");
652
+ candidates.push(join2(here, "..", "data", "deepseek-tokenizer.json.gz"));
653
+ candidates.push(join2(here, "..", "..", "data", "deepseek-tokenizer.json.gz"));
652
654
  } catch {
655
+ }
656
+ try {
653
657
  const req = createRequire(import.meta.url);
654
- return join2(
655
- dirname(req.resolve("reasonix/package.json")),
656
- "data",
657
- "deepseek-tokenizer.json.gz"
658
+ candidates.push(
659
+ join2(dirname(req.resolve("reasonix/package.json")), "data", "deepseek-tokenizer.json.gz")
658
660
  );
661
+ } catch {
659
662
  }
663
+ for (const p of candidates) {
664
+ if (existsSync2(p)) return p;
665
+ }
666
+ return candidates[0] ?? join2(process.cwd(), "data", "deepseek-tokenizer.json.gz");
660
667
  }
661
668
  function loadTokenizer() {
662
669
  if (cached) return cached;
@@ -1486,7 +1493,7 @@ function signature2(call) {
1486
1493
  import {
1487
1494
  appendFileSync,
1488
1495
  chmodSync,
1489
- existsSync as existsSync2,
1496
+ existsSync as existsSync3,
1490
1497
  mkdirSync,
1491
1498
  readFileSync as readFileSync3,
1492
1499
  readdirSync,
@@ -1508,7 +1515,7 @@ function sanitizeName(name) {
1508
1515
  }
1509
1516
  function loadSessionMessages(name) {
1510
1517
  const path = sessionPath(name);
1511
- if (!existsSync2(path)) return [];
1518
+ if (!existsSync3(path)) return [];
1512
1519
  try {
1513
1520
  const raw = readFileSync3(path, "utf8");
1514
1521
  const out = [];
@@ -1538,7 +1545,7 @@ function appendSessionMessage(name, message) {
1538
1545
  }
1539
1546
  function listSessions() {
1540
1547
  const dir = sessionsDir();
1541
- if (!existsSync2(dir)) return [];
1548
+ if (!existsSync3(dir)) return [];
1542
1549
  try {
1543
1550
  const files = readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
1544
1551
  return files.map((file) => {
@@ -1734,9 +1741,9 @@ var CacheFirstLoop = class {
1734
1741
  this.sessionName = opts.session ?? null;
1735
1742
  if (this.sessionName) {
1736
1743
  const prior = loadSessionMessages(this.sessionName);
1737
- const { messages, healedCount, healedFrom } = healLoadedMessages(
1744
+ const { messages, healedCount, tokensSaved } = healLoadedMessagesByTokens(
1738
1745
  prior,
1739
- DEFAULT_MAX_RESULT_CHARS
1746
+ DEFAULT_MAX_RESULT_TOKENS
1740
1747
  );
1741
1748
  for (const msg of messages) this.log.append(msg);
1742
1749
  this.resumedMessageCount = messages.length;
@@ -1746,7 +1753,7 @@ var CacheFirstLoop = class {
1746
1753
  } catch {
1747
1754
  }
1748
1755
  process.stderr.write(
1749
- `\u25B8 session "${this.sessionName}": healed ${healedCount} entr${healedCount === 1 ? "y" : "ies"}${healedFrom > 0 ? ` (was ${healedFrom.toLocaleString()} chars oversized)` : " (dropped dangling tool_calls tail)"}. Rewrote session file.
1756
+ `\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.
1750
1757
  `
1751
1758
  );
1752
1759
  }
@@ -2015,8 +2022,8 @@ var CacheFirstLoop = class {
2015
2022
  }
2016
2023
  );
2017
2024
  for (let k = 0; k < budget; k++) {
2018
- const sample = queue.shift() ?? await new Promise((resolve7) => {
2019
- waiter = resolve7;
2025
+ const sample = queue.shift() ?? await new Promise((resolve8) => {
2026
+ waiter = resolve8;
2020
2027
  });
2021
2028
  yield {
2022
2029
  turn: this._turn,
@@ -2415,15 +2422,12 @@ function shrinkOversizedToolResultsByTokens(messages, maxTokens) {
2415
2422
  });
2416
2423
  return { messages: out, healedCount, tokensSaved, charsSaved };
2417
2424
  }
2418
- function healLoadedMessages(messages, maxChars) {
2419
- const shrunk = shrinkOversizedToolResults(messages, maxChars);
2420
- let healedCount = shrunk.healedCount;
2425
+ function fixToolCallPairing(messages) {
2421
2426
  const out = [];
2422
- const openCallIds = /* @__PURE__ */ new Set();
2423
2427
  let droppedAssistantCalls = 0;
2424
2428
  let droppedStrayTools = 0;
2425
- for (let i = 0; i < shrunk.messages.length; i++) {
2426
- const msg = shrunk.messages[i];
2429
+ for (let i = 0; i < messages.length; i++) {
2430
+ const msg = messages[i];
2427
2431
  if (msg.role === "assistant" && Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0) {
2428
2432
  const needed = /* @__PURE__ */ new Set();
2429
2433
  for (const call of msg.tool_calls) {
@@ -2431,8 +2435,8 @@ function healLoadedMessages(messages, maxChars) {
2431
2435
  }
2432
2436
  const candidates = [];
2433
2437
  let j = i + 1;
2434
- while (j < shrunk.messages.length && needed.size > 0) {
2435
- const nxt = shrunk.messages[j];
2438
+ while (j < messages.length && needed.size > 0) {
2439
+ const nxt = messages[j];
2436
2440
  if (nxt.role !== "tool") break;
2437
2441
  const id = nxt.tool_call_id ?? "";
2438
2442
  if (!needed.has(id)) break;
@@ -2457,8 +2461,24 @@ function healLoadedMessages(messages, maxChars) {
2457
2461
  }
2458
2462
  out.push(msg);
2459
2463
  }
2460
- healedCount += droppedAssistantCalls + droppedStrayTools;
2461
- return { messages: out, healedCount, healedFrom: shrunk.healedFrom };
2464
+ return { messages: out, droppedAssistantCalls, droppedStrayTools };
2465
+ }
2466
+ function healLoadedMessages(messages, maxChars) {
2467
+ const shrunk = shrinkOversizedToolResults(messages, maxChars);
2468
+ const paired = fixToolCallPairing(shrunk.messages);
2469
+ const healedCount = shrunk.healedCount + paired.droppedAssistantCalls + paired.droppedStrayTools;
2470
+ return { messages: paired.messages, healedCount, healedFrom: shrunk.healedFrom };
2471
+ }
2472
+ function healLoadedMessagesByTokens(messages, maxTokens) {
2473
+ const shrunk = shrinkOversizedToolResultsByTokens(messages, maxTokens);
2474
+ const paired = fixToolCallPairing(shrunk.messages);
2475
+ const healedCount = shrunk.healedCount + paired.droppedAssistantCalls + paired.droppedStrayTools;
2476
+ return {
2477
+ messages: paired.messages,
2478
+ healedCount,
2479
+ tokensSaved: shrunk.tokensSaved,
2480
+ charsSaved: shrunk.charsSaved
2481
+ };
2462
2482
  }
2463
2483
  function formatLoopError(err) {
2464
2484
  const msg = err.message ?? "";
@@ -2470,17 +2490,104 @@ function formatLoopError(err) {
2470
2490
  return msg;
2471
2491
  }
2472
2492
 
2493
+ // src/at-mentions.ts
2494
+ import { existsSync as existsSync4, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
2495
+ import { isAbsolute, relative, resolve } from "path";
2496
+ var DEFAULT_AT_MENTION_MAX_BYTES = 64 * 1024;
2497
+ var AT_MENTION_PATTERN = /(?<=^|\s)@([a-zA-Z0-9_./\\-]+)/g;
2498
+ function expandAtMentions(text, rootDir, opts = {}) {
2499
+ const maxBytes = opts.maxBytes ?? DEFAULT_AT_MENTION_MAX_BYTES;
2500
+ const fs2 = opts.fs ?? defaultFs;
2501
+ const root = resolve(rootDir);
2502
+ const seen = /* @__PURE__ */ new Map();
2503
+ const expansions = [];
2504
+ for (const match of text.matchAll(AT_MENTION_PATTERN)) {
2505
+ const rawPath = match[1] ?? "";
2506
+ const cleaned = rawPath.replace(/\.+$/, "");
2507
+ if (!cleaned) continue;
2508
+ const token = `@${cleaned}`;
2509
+ if (seen.has(token)) continue;
2510
+ const expansion = resolveMention(cleaned, root, maxBytes, fs2);
2511
+ seen.set(token, expansion);
2512
+ expansions.push(expansion);
2513
+ }
2514
+ if (expansions.length === 0) return { text, expansions };
2515
+ const blocks = [];
2516
+ for (const ex of expansions) {
2517
+ if (ex.ok) {
2518
+ const content = readSafe(root, ex.path, fs2);
2519
+ blocks.push(`<file path="${ex.path}">
2520
+ ${content}
2521
+ </file>`);
2522
+ } else {
2523
+ blocks.push(`<file path="${ex.path}" skipped="${ex.skip}" />`);
2524
+ }
2525
+ }
2526
+ const augmented = `${text}
2527
+
2528
+ [Referenced files]
2529
+ ${blocks.join("\n\n")}`;
2530
+ return { text: augmented, expansions };
2531
+ }
2532
+ function resolveMention(rawPath, root, maxBytes, fs2) {
2533
+ if (isAbsolute(rawPath)) {
2534
+ return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "escape" };
2535
+ }
2536
+ const resolved = resolve(root, rawPath);
2537
+ const rel = relative(root, resolved);
2538
+ if (rel.startsWith("..") || isAbsolute(rel)) {
2539
+ return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "escape" };
2540
+ }
2541
+ if (!fs2.exists(resolved)) {
2542
+ return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "missing" };
2543
+ }
2544
+ if (!fs2.isFile(resolved)) {
2545
+ return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "not-file" };
2546
+ }
2547
+ const size = fs2.size(resolved);
2548
+ if (size > maxBytes) {
2549
+ return { token: `@${rawPath}`, path: rawPath, ok: false, skip: "too-large", bytes: size };
2550
+ }
2551
+ return { token: `@${rawPath}`, path: rawPath, ok: true, bytes: size };
2552
+ }
2553
+ function readSafe(root, rawPath, fs2) {
2554
+ const resolved = resolve(root, rawPath);
2555
+ try {
2556
+ return fs2.read(resolved);
2557
+ } catch {
2558
+ return "(read failed)";
2559
+ }
2560
+ }
2561
+ var defaultFs = {
2562
+ exists: (p) => existsSync4(p),
2563
+ isFile: (p) => {
2564
+ try {
2565
+ return statSync2(p).isFile();
2566
+ } catch {
2567
+ return false;
2568
+ }
2569
+ },
2570
+ size: (p) => {
2571
+ try {
2572
+ return statSync2(p).size;
2573
+ } catch {
2574
+ return 0;
2575
+ }
2576
+ },
2577
+ read: (p) => readFileSync4(p, "utf8")
2578
+ };
2579
+
2473
2580
  // src/project-memory.ts
2474
- import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
2581
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
2475
2582
  import { join as join4 } from "path";
2476
2583
  var PROJECT_MEMORY_FILE = "REASONIX.md";
2477
2584
  var PROJECT_MEMORY_MAX_CHARS = 8e3;
2478
2585
  function readProjectMemory(rootDir) {
2479
2586
  const path = join4(rootDir, PROJECT_MEMORY_FILE);
2480
- if (!existsSync3(path)) return null;
2587
+ if (!existsSync5(path)) return null;
2481
2588
  let raw;
2482
2589
  try {
2483
- raw = readFileSync4(path, "utf8");
2590
+ raw = readFileSync5(path, "utf8");
2484
2591
  } catch {
2485
2592
  return null;
2486
2593
  }
@@ -2516,20 +2623,20 @@ ${mem.content}
2516
2623
  // src/user-memory.ts
2517
2624
  import { createHash as createHash2 } from "crypto";
2518
2625
  import {
2519
- existsSync as existsSync5,
2626
+ existsSync as existsSync7,
2520
2627
  mkdirSync as mkdirSync2,
2521
- readFileSync as readFileSync6,
2628
+ readFileSync as readFileSync7,
2522
2629
  readdirSync as readdirSync3,
2523
2630
  unlinkSync as unlinkSync2,
2524
2631
  writeFileSync as writeFileSync2
2525
2632
  } from "fs";
2526
2633
  import { homedir as homedir4 } from "os";
2527
- import { join as join6, resolve as resolve2 } from "path";
2634
+ import { join as join6, resolve as resolve3 } from "path";
2528
2635
 
2529
2636
  // src/skills.ts
2530
- import { existsSync as existsSync4, readFileSync as readFileSync5, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
2637
+ import { existsSync as existsSync6, readFileSync as readFileSync6, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
2531
2638
  import { homedir as homedir3 } from "os";
2532
- import { join as join5, resolve } from "path";
2639
+ import { join as join5, resolve as resolve2 } from "path";
2533
2640
  var SKILLS_DIRNAME = "skills";
2534
2641
  var SKILL_FILE = "SKILL.md";
2535
2642
  var SKILLS_INDEX_MAX_CHARS = 4e3;
@@ -2560,7 +2667,7 @@ var SkillStore = class {
2560
2667
  disableBuiltins;
2561
2668
  constructor(opts = {}) {
2562
2669
  this.homeDir = opts.homeDir ?? homedir3();
2563
- this.projectRoot = opts.projectRoot ? resolve(opts.projectRoot) : void 0;
2670
+ this.projectRoot = opts.projectRoot ? resolve2(opts.projectRoot) : void 0;
2564
2671
  this.disableBuiltins = opts.disableBuiltins === true;
2565
2672
  }
2566
2673
  /** True iff this store was configured with a project root. */
@@ -2591,7 +2698,7 @@ var SkillStore = class {
2591
2698
  list() {
2592
2699
  const byName = /* @__PURE__ */ new Map();
2593
2700
  for (const { dir, scope } of this.roots()) {
2594
- if (!existsSync4(dir)) continue;
2701
+ if (!existsSync6(dir)) continue;
2595
2702
  let entries;
2596
2703
  try {
2597
2704
  entries = readdirSync2(dir, { withFileTypes: true });
@@ -2615,13 +2722,13 @@ var SkillStore = class {
2615
2722
  read(name) {
2616
2723
  if (!isValidSkillName(name)) return null;
2617
2724
  for (const { dir, scope } of this.roots()) {
2618
- if (!existsSync4(dir)) continue;
2725
+ if (!existsSync6(dir)) continue;
2619
2726
  const dirCandidate = join5(dir, name, SKILL_FILE);
2620
- if (existsSync4(dirCandidate) && statSync2(dirCandidate).isFile()) {
2727
+ if (existsSync6(dirCandidate) && statSync3(dirCandidate).isFile()) {
2621
2728
  return this.parse(dirCandidate, name, scope);
2622
2729
  }
2623
2730
  const flatCandidate = join5(dir, `${name}.md`);
2624
- if (existsSync4(flatCandidate) && statSync2(flatCandidate).isFile()) {
2731
+ if (existsSync6(flatCandidate) && statSync3(flatCandidate).isFile()) {
2625
2732
  return this.parse(flatCandidate, name, scope);
2626
2733
  }
2627
2734
  }
@@ -2636,7 +2743,7 @@ var SkillStore = class {
2636
2743
  if (entry.isDirectory()) {
2637
2744
  if (!isValidSkillName(entry.name)) return null;
2638
2745
  const file = join5(dir, entry.name, SKILL_FILE);
2639
- if (!existsSync4(file)) return null;
2746
+ if (!existsSync6(file)) return null;
2640
2747
  return this.parse(file, entry.name, scope);
2641
2748
  }
2642
2749
  if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -2649,7 +2756,7 @@ var SkillStore = class {
2649
2756
  parse(path, stem, scope) {
2650
2757
  let raw;
2651
2758
  try {
2652
- raw = readFileSync5(path, "utf8");
2759
+ raw = readFileSync6(path, "utf8");
2653
2760
  } catch {
2654
2761
  return null;
2655
2762
  }
@@ -2776,7 +2883,7 @@ function sanitizeMemoryName(raw) {
2776
2883
  return trimmed;
2777
2884
  }
2778
2885
  function projectHash(rootDir) {
2779
- const abs = resolve2(rootDir);
2886
+ const abs = resolve3(rootDir);
2780
2887
  return createHash2("sha1").update(abs).digest("hex").slice(0, 16);
2781
2888
  }
2782
2889
  function scopeDir(opts) {
@@ -2789,7 +2896,7 @@ function scopeDir(opts) {
2789
2896
  return join6(opts.homeDir, USER_MEMORY_DIR, projectHash(opts.projectRoot));
2790
2897
  }
2791
2898
  function ensureDir(p) {
2792
- if (!existsSync5(p)) mkdirSync2(p, { recursive: true });
2899
+ if (!existsSync7(p)) mkdirSync2(p, { recursive: true });
2793
2900
  }
2794
2901
  function parseFrontmatter2(raw) {
2795
2902
  const lines = raw.split(/\r?\n/);
@@ -2835,7 +2942,7 @@ var MemoryStore = class {
2835
2942
  projectRoot;
2836
2943
  constructor(opts = {}) {
2837
2944
  this.homeDir = opts.homeDir ?? join6(homedir4(), ".reasonix");
2838
- this.projectRoot = opts.projectRoot ? resolve2(opts.projectRoot) : void 0;
2945
+ this.projectRoot = opts.projectRoot ? resolve3(opts.projectRoot) : void 0;
2839
2946
  }
2840
2947
  /** Directory this store writes `scope` files into, creating it if needed. */
2841
2948
  dir(scope) {
@@ -2861,10 +2968,10 @@ var MemoryStore = class {
2861
2968
  scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot }),
2862
2969
  MEMORY_INDEX_FILE
2863
2970
  );
2864
- if (!existsSync5(file)) return null;
2971
+ if (!existsSync7(file)) return null;
2865
2972
  let raw;
2866
2973
  try {
2867
- raw = readFileSync6(file, "utf8");
2974
+ raw = readFileSync7(file, "utf8");
2868
2975
  } catch {
2869
2976
  return null;
2870
2977
  }
@@ -2879,10 +2986,10 @@ var MemoryStore = class {
2879
2986
  /** Read one memory file's body (frontmatter stripped). Throws if missing. */
2880
2987
  read(scope, name) {
2881
2988
  const file = this.pathFor(scope, name);
2882
- if (!existsSync5(file)) {
2989
+ if (!existsSync7(file)) {
2883
2990
  throw new Error(`memory not found: scope=${scope} name=${name}`);
2884
2991
  }
2885
- const raw = readFileSync6(file, "utf8");
2992
+ const raw = readFileSync7(file, "utf8");
2886
2993
  const { data, body } = parseFrontmatter2(raw);
2887
2994
  return {
2888
2995
  name: data.name ?? name,
@@ -2903,7 +3010,7 @@ var MemoryStore = class {
2903
3010
  const scopes = this.projectRoot ? ["global", "project"] : ["global"];
2904
3011
  for (const scope of scopes) {
2905
3012
  const dir = scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot });
2906
- if (!existsSync5(dir)) continue;
3013
+ if (!existsSync7(dir)) continue;
2907
3014
  let entries;
2908
3015
  try {
2909
3016
  entries = readdirSync3(dir);
@@ -2957,7 +3064,7 @@ var MemoryStore = class {
2957
3064
  throw new Error("cannot delete project-scoped memory: no projectRoot configured");
2958
3065
  }
2959
3066
  const file = this.pathFor(scope, rawName);
2960
- if (!existsSync5(file)) return false;
3067
+ if (!existsSync7(file)) return false;
2961
3068
  unlinkSync2(file);
2962
3069
  this.regenerateIndex(scope);
2963
3070
  return true;
@@ -2970,7 +3077,7 @@ var MemoryStore = class {
2970
3077
  */
2971
3078
  regenerateIndex(scope) {
2972
3079
  const dir = scopeDir({ homeDir: this.homeDir, scope, projectRoot: this.projectRoot });
2973
- if (!existsSync5(dir)) return;
3080
+ if (!existsSync7(dir)) return;
2974
3081
  let files;
2975
3082
  try {
2976
3083
  files = readdirSync3(dir);
@@ -2980,7 +3087,7 @@ var MemoryStore = class {
2980
3087
  const mdFiles = files.filter((f) => f !== MEMORY_INDEX_FILE && f.endsWith(".md")).sort((a, b) => a.localeCompare(b));
2981
3088
  const indexPath = join6(dir, MEMORY_INDEX_FILE);
2982
3089
  if (mdFiles.length === 0) {
2983
- if (existsSync5(indexPath)) unlinkSync2(indexPath);
3090
+ if (existsSync7(indexPath)) unlinkSync2(indexPath);
2984
3091
  return;
2985
3092
  }
2986
3093
  const lines = [];
@@ -3914,7 +4021,7 @@ function forkRegistryExcluding(parent, exclude) {
3914
4021
 
3915
4022
  // src/tools/shell.ts
3916
4023
  import { spawn as spawn2 } from "child_process";
3917
- import { existsSync as existsSync6, statSync as statSync3 } from "fs";
4024
+ import { existsSync as existsSync8, statSync as statSync4 } from "fs";
3918
4025
  import * as pathMod2 from "path";
3919
4026
  var DEFAULT_TIMEOUT_SEC = 60;
3920
4027
  var DEFAULT_MAX_OUTPUT_CHARS = 32e3;
@@ -4084,7 +4191,7 @@ async function runCommand(cmd, opts) {
4084
4191
  };
4085
4192
  const { bin, args, spawnOverrides } = prepareSpawn(argv);
4086
4193
  const effectiveSpawnOpts = { ...spawnOpts, ...spawnOverrides };
4087
- return await new Promise((resolve7, reject) => {
4194
+ return await new Promise((resolve8, reject) => {
4088
4195
  let child;
4089
4196
  try {
4090
4197
  child = spawn2(bin, args, effectiveSpawnOpts);
@@ -4117,7 +4224,7 @@ async function runCommand(cmd, opts) {
4117
4224
  const output = buf.length > maxChars ? `${buf.slice(0, maxChars)}
4118
4225
 
4119
4226
  [\u2026 truncated ${buf.length - maxChars} chars \u2026]` : buf;
4120
- resolve7({ exitCode: code, output, timedOut });
4227
+ resolve8({ exitCode: code, output, timedOut });
4121
4228
  });
4122
4229
  });
4123
4230
  }
@@ -4142,7 +4249,7 @@ function resolveExecutable(cmd, opts = {}) {
4142
4249
  }
4143
4250
  function defaultIsFile(full) {
4144
4251
  try {
4145
- return existsSync6(full) && statSync3(full).isFile();
4252
+ return existsSync8(full) && statSync4(full).isFile();
4146
4253
  } catch {
4147
4254
  return false;
4148
4255
  }
@@ -4469,12 +4576,12 @@ ${i + 1}. ${r.title}`);
4469
4576
  }
4470
4577
 
4471
4578
  // src/env.ts
4472
- import { readFileSync as readFileSync7 } from "fs";
4473
- import { resolve as resolve5 } from "path";
4579
+ import { readFileSync as readFileSync8 } from "fs";
4580
+ import { resolve as resolve6 } from "path";
4474
4581
  function loadDotenv(path = ".env") {
4475
4582
  let raw;
4476
4583
  try {
4477
- raw = readFileSync7(resolve5(process.cwd(), path), "utf8");
4584
+ raw = readFileSync8(resolve6(process.cwd(), path), "utf8");
4478
4585
  } catch {
4479
4586
  return;
4480
4587
  }
@@ -4493,7 +4600,7 @@ function loadDotenv(path = ".env") {
4493
4600
  }
4494
4601
 
4495
4602
  // src/transcript.ts
4496
- import { createWriteStream, readFileSync as readFileSync8 } from "fs";
4603
+ import { createWriteStream, readFileSync as readFileSync9 } from "fs";
4497
4604
  function recordFromLoopEvent(ev, extra) {
4498
4605
  const rec = {
4499
4606
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -4544,7 +4651,7 @@ function openTranscriptFile(path, meta) {
4544
4651
  return stream;
4545
4652
  }
4546
4653
  function readTranscript(path) {
4547
- const raw = readFileSync8(path, "utf8");
4654
+ const raw = readFileSync9(path, "utf8");
4548
4655
  return parseTranscript(raw);
4549
4656
  }
4550
4657
  function isPlanStateEmptyShape(s) {
@@ -5156,7 +5263,7 @@ var McpClient = class {
5156
5263
  const id = this.nextId++;
5157
5264
  const frame = { jsonrpc: "2.0", id, method, params };
5158
5265
  let abortHandler = null;
5159
- const promise = new Promise((resolve7, reject) => {
5266
+ const promise = new Promise((resolve8, reject) => {
5160
5267
  const timeout = setTimeout(() => {
5161
5268
  this.pending.delete(id);
5162
5269
  if (abortHandler && signal) signal.removeEventListener("abort", abortHandler);
@@ -5165,7 +5272,7 @@ var McpClient = class {
5165
5272
  );
5166
5273
  }, this.requestTimeoutMs);
5167
5274
  this.pending.set(id, {
5168
- resolve: resolve7,
5275
+ resolve: resolve8,
5169
5276
  reject,
5170
5277
  timeout
5171
5278
  });
@@ -5288,12 +5395,12 @@ var StdioTransport = class {
5288
5395
  }
5289
5396
  async send(message) {
5290
5397
  if (this.closed) throw new Error("MCP transport is closed");
5291
- return new Promise((resolve7, reject) => {
5398
+ return new Promise((resolve8, reject) => {
5292
5399
  const line = `${JSON.stringify(message)}
5293
5400
  `;
5294
5401
  this.child.stdin.write(line, "utf8", (err) => {
5295
5402
  if (err) reject(err);
5296
- else resolve7();
5403
+ else resolve8();
5297
5404
  });
5298
5405
  });
5299
5406
  }
@@ -5304,8 +5411,8 @@ var StdioTransport = class {
5304
5411
  continue;
5305
5412
  }
5306
5413
  if (this.closed) return;
5307
- const next = await new Promise((resolve7) => {
5308
- this.waiters.push(resolve7);
5414
+ const next = await new Promise((resolve8) => {
5415
+ this.waiters.push(resolve8);
5309
5416
  });
5310
5417
  if (next === null) return;
5311
5418
  yield next;
@@ -5371,8 +5478,8 @@ var SseTransport = class {
5371
5478
  constructor(opts) {
5372
5479
  this.url = opts.url;
5373
5480
  this.headers = opts.headers ?? {};
5374
- this.endpointReady = new Promise((resolve7, reject) => {
5375
- this.resolveEndpoint = resolve7;
5481
+ this.endpointReady = new Promise((resolve8, reject) => {
5482
+ this.resolveEndpoint = resolve8;
5376
5483
  this.rejectEndpoint = reject;
5377
5484
  });
5378
5485
  this.endpointReady.catch(() => void 0);
@@ -5399,8 +5506,8 @@ var SseTransport = class {
5399
5506
  continue;
5400
5507
  }
5401
5508
  if (this.closed) return;
5402
- const next = await new Promise((resolve7) => {
5403
- this.waiters.push(resolve7);
5509
+ const next = await new Promise((resolve8) => {
5510
+ this.waiters.push(resolve8);
5404
5511
  });
5405
5512
  if (next === null) return;
5406
5513
  yield next;
@@ -5599,8 +5706,8 @@ async function trySection(load) {
5599
5706
  }
5600
5707
 
5601
5708
  // src/code/edit-blocks.ts
5602
- import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync9, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "fs";
5603
- import { dirname as dirname4, resolve as resolve6 } from "path";
5709
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync10, unlinkSync as unlinkSync3, writeFileSync as writeFileSync3 } from "fs";
5710
+ import { dirname as dirname4, resolve as resolve7 } from "path";
5604
5711
  var BLOCK_RE = /^(\S[^\n]*)\n<{7} SEARCH\n([\s\S]*?)\n?={7}\n([\s\S]*?)\n?>{7} REPLACE/gm;
5605
5712
  function parseEditBlocks(text) {
5606
5713
  const out = [];
@@ -5618,8 +5725,8 @@ function parseEditBlocks(text) {
5618
5725
  return out;
5619
5726
  }
5620
5727
  function applyEditBlock(block, rootDir) {
5621
- const absRoot = resolve6(rootDir);
5622
- const absTarget = resolve6(absRoot, block.path);
5728
+ const absRoot = resolve7(rootDir);
5729
+ const absTarget = resolve7(absRoot, block.path);
5623
5730
  if (absTarget !== absRoot && !absTarget.startsWith(`${absRoot}${sep()}`)) {
5624
5731
  return {
5625
5732
  path: block.path,
@@ -5628,7 +5735,7 @@ function applyEditBlock(block, rootDir) {
5628
5735
  };
5629
5736
  }
5630
5737
  const searchEmpty = block.search.length === 0;
5631
- const exists = existsSync7(absTarget);
5738
+ const exists = existsSync9(absTarget);
5632
5739
  try {
5633
5740
  if (!exists) {
5634
5741
  if (!searchEmpty) {
@@ -5642,7 +5749,7 @@ function applyEditBlock(block, rootDir) {
5642
5749
  writeFileSync3(absTarget, block.replace, "utf8");
5643
5750
  return { path: block.path, status: "created" };
5644
5751
  }
5645
- const content = readFileSync9(absTarget, "utf8");
5752
+ const content = readFileSync10(absTarget, "utf8");
5646
5753
  if (searchEmpty) {
5647
5754
  return {
5648
5755
  path: block.path,
@@ -5669,19 +5776,19 @@ function applyEditBlocks(blocks, rootDir) {
5669
5776
  return blocks.map((b) => applyEditBlock(b, rootDir));
5670
5777
  }
5671
5778
  function snapshotBeforeEdits(blocks, rootDir) {
5672
- const absRoot = resolve6(rootDir);
5779
+ const absRoot = resolve7(rootDir);
5673
5780
  const seen = /* @__PURE__ */ new Set();
5674
5781
  const snapshots = [];
5675
5782
  for (const b of blocks) {
5676
5783
  if (seen.has(b.path)) continue;
5677
5784
  seen.add(b.path);
5678
- const abs = resolve6(absRoot, b.path);
5679
- if (!existsSync7(abs)) {
5785
+ const abs = resolve7(absRoot, b.path);
5786
+ if (!existsSync9(abs)) {
5680
5787
  snapshots.push({ path: b.path, prevContent: null });
5681
5788
  continue;
5682
5789
  }
5683
5790
  try {
5684
- snapshots.push({ path: b.path, prevContent: readFileSync9(abs, "utf8") });
5791
+ snapshots.push({ path: b.path, prevContent: readFileSync10(abs, "utf8") });
5685
5792
  } catch {
5686
5793
  snapshots.push({ path: b.path, prevContent: null });
5687
5794
  }
@@ -5689,9 +5796,9 @@ function snapshotBeforeEdits(blocks, rootDir) {
5689
5796
  return snapshots;
5690
5797
  }
5691
5798
  function restoreSnapshots(snapshots, rootDir) {
5692
- const absRoot = resolve6(rootDir);
5799
+ const absRoot = resolve7(rootDir);
5693
5800
  return snapshots.map((snap) => {
5694
- const abs = resolve6(absRoot, snap.path);
5801
+ const abs = resolve7(absRoot, snap.path);
5695
5802
  if (abs !== absRoot && !abs.startsWith(`${absRoot}${sep()}`)) {
5696
5803
  return {
5697
5804
  path: snap.path,
@@ -5701,7 +5808,7 @@ function restoreSnapshots(snapshots, rootDir) {
5701
5808
  }
5702
5809
  try {
5703
5810
  if (snap.prevContent === null) {
5704
- if (existsSync7(abs)) unlinkSync3(abs);
5811
+ if (existsSync9(abs)) unlinkSync3(abs);
5705
5812
  return {
5706
5813
  path: snap.path,
5707
5814
  status: "applied",
@@ -5724,7 +5831,7 @@ function sep() {
5724
5831
  }
5725
5832
 
5726
5833
  // src/code/prompt.ts
5727
- import { existsSync as existsSync8, readFileSync as readFileSync10 } from "fs";
5834
+ import { existsSync as existsSync10, readFileSync as readFileSync11 } from "fs";
5728
5835
  import { join as join8 } from "path";
5729
5836
  var CODE_SYSTEM_PROMPT = `You are Reasonix Code, a coding assistant. You have filesystem tools (read_file, write_file, list_directory, search_files, etc.) rooted at the user's working directory.
5730
5837
 
@@ -5847,10 +5954,10 @@ Two different rules depending on which tool:
5847
5954
  function codeSystemPrompt(rootDir) {
5848
5955
  const withMemory = applyMemoryStack(CODE_SYSTEM_PROMPT, rootDir);
5849
5956
  const gitignorePath = join8(rootDir, ".gitignore");
5850
- if (!existsSync8(gitignorePath)) return withMemory;
5957
+ if (!existsSync10(gitignorePath)) return withMemory;
5851
5958
  let content;
5852
5959
  try {
5853
- content = readFileSync10(gitignorePath, "utf8");
5960
+ content = readFileSync11(gitignorePath, "utf8");
5854
5961
  } catch {
5855
5962
  return withMemory;
5856
5963
  }
@@ -5870,7 +5977,7 @@ ${truncated}
5870
5977
  }
5871
5978
 
5872
5979
  // src/config.ts
5873
- import { chmodSync as chmodSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "fs";
5980
+ import { chmodSync as chmodSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync12, writeFileSync as writeFileSync4 } from "fs";
5874
5981
  import { homedir as homedir5 } from "os";
5875
5982
  import { dirname as dirname5, join as join9 } from "path";
5876
5983
  function defaultConfigPath() {
@@ -5878,7 +5985,7 @@ function defaultConfigPath() {
5878
5985
  }
5879
5986
  function readConfig(path = defaultConfigPath()) {
5880
5987
  try {
5881
- const raw = readFileSync11(path, "utf8");
5988
+ const raw = readFileSync12(path, "utf8");
5882
5989
  const parsed = JSON.parse(raw);
5883
5990
  if (parsed && typeof parsed === "object") return parsed;
5884
5991
  } catch {
@@ -5913,7 +6020,7 @@ function redactKey(key) {
5913
6020
  }
5914
6021
 
5915
6022
  // src/version.ts
5916
- import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync5 } from "fs";
6023
+ import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync13, writeFileSync as writeFileSync5 } from "fs";
5917
6024
  import { homedir as homedir6 } from "os";
5918
6025
  import { dirname as dirname6, join as join10 } from "path";
5919
6026
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -5925,8 +6032,8 @@ function readPackageVersion() {
5925
6032
  let dir = dirname6(fileURLToPath2(import.meta.url));
5926
6033
  for (let i = 0; i < 6; i++) {
5927
6034
  const p = join10(dir, "package.json");
5928
- if (existsSync9(p)) {
5929
- const pkg = JSON.parse(readFileSync12(p, "utf8"));
6035
+ if (existsSync11(p)) {
6036
+ const pkg = JSON.parse(readFileSync13(p, "utf8"));
5930
6037
  if (pkg?.name === "reasonix" && typeof pkg.version === "string") {
5931
6038
  return pkg.version;
5932
6039
  }
@@ -5945,7 +6052,7 @@ function cachePath(homeDirOverride) {
5945
6052
  }
5946
6053
  function readCache(homeDirOverride) {
5947
6054
  try {
5948
- const raw = readFileSync12(cachePath(homeDirOverride), "utf8");
6055
+ const raw = readFileSync13(cachePath(homeDirOverride), "utf8");
5949
6056
  const parsed = JSON.parse(raw);
5950
6057
  if (parsed && typeof parsed.version === "string" && typeof parsed.checkedAt === "number") {
5951
6058
  return parsed;
@@ -6014,7 +6121,7 @@ function isNpxInstall() {
6014
6121
  }
6015
6122
 
6016
6123
  // src/usage.ts
6017
- import { appendFileSync as appendFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync13, statSync as statSync4 } from "fs";
6124
+ import { appendFileSync as appendFileSync2, existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync14, statSync as statSync5 } from "fs";
6018
6125
  import { homedir as homedir7 } from "os";
6019
6126
  import { dirname as dirname7, join as join11 } from "path";
6020
6127
  function defaultUsageLogPath(homeDirOverride) {
@@ -6042,10 +6149,10 @@ function appendUsage(input) {
6042
6149
  return record;
6043
6150
  }
6044
6151
  function readUsageLog(path = defaultUsageLogPath()) {
6045
- if (!existsSync10(path)) return [];
6152
+ if (!existsSync12(path)) return [];
6046
6153
  let raw;
6047
6154
  try {
6048
- raw = readFileSync13(path, "utf8");
6155
+ raw = readFileSync14(path, "utf8");
6049
6156
  } catch {
6050
6157
  return [];
6051
6158
  }
@@ -6127,9 +6234,9 @@ function aggregateUsage(records, opts = {}) {
6127
6234
  };
6128
6235
  }
6129
6236
  function formatLogSize(path = defaultUsageLogPath()) {
6130
- if (!existsSync10(path)) return "";
6237
+ if (!existsSync12(path)) return "";
6131
6238
  try {
6132
- const s = statSync4(path);
6239
+ const s = statSync5(path);
6133
6240
  const bytes = s.size;
6134
6241
  if (bytes < 1024) return `${bytes} B`;
6135
6242
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -6139,9 +6246,11 @@ function formatLogSize(path = defaultUsageLogPath()) {
6139
6246
  }
6140
6247
  }
6141
6248
  export {
6249
+ AT_MENTION_PATTERN,
6142
6250
  AppendOnlyLog,
6143
6251
  CODE_SYSTEM_PROMPT,
6144
6252
  CacheFirstLoop,
6253
+ DEFAULT_AT_MENTION_MAX_BYTES,
6145
6254
  DEFAULT_MAX_RESULT_CHARS,
6146
6255
  DEFAULT_MAX_RESULT_TOKENS,
6147
6256
  DeepSeekClient,
@@ -6196,7 +6305,9 @@ export {
6196
6305
  detectShellOperator,
6197
6306
  diffTranscripts,
6198
6307
  emptyPlanState,
6308
+ expandAtMentions,
6199
6309
  fetchWithRetry,
6310
+ fixToolCallPairing,
6200
6311
  flattenMcpResult,
6201
6312
  flattenSchema,
6202
6313
  forkRegistryExcluding,
@@ -6209,6 +6320,7 @@ export {
6209
6320
  globalSettingsPath,
6210
6321
  harvest,
6211
6322
  healLoadedMessages,
6323
+ healLoadedMessagesByTokens,
6212
6324
  htmlToText,
6213
6325
  injectPowerShellUtf8,
6214
6326
  inputCostUsd,