reasonix 0.12.14 → 0.12.16

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 CHANGED
@@ -11613,6 +11613,22 @@ function extOf(p) {
11613
11613
  const m = /\.[^./\\]+$/.exec(p);
11614
11614
  return m ? m[0] : "";
11615
11615
  }
11616
+ var lineCountCache = /* @__PURE__ */ new Map();
11617
+ var LINE_COUNT_CACHE_LIMIT = 256;
11618
+ function getCachedLineCount(fullPath, mtimeMs) {
11619
+ const hit = lineCountCache.get(fullPath);
11620
+ if (!hit || hit.mtimeMs !== mtimeMs) return null;
11621
+ lineCountCache.delete(fullPath);
11622
+ lineCountCache.set(fullPath, hit);
11623
+ return hit.lineCount;
11624
+ }
11625
+ function setCachedLineCount(fullPath, mtimeMs, lineCount) {
11626
+ if (lineCountCache.size >= LINE_COUNT_CACHE_LIMIT) {
11627
+ const oldest = lineCountCache.keys().next().value;
11628
+ if (oldest !== void 0) lineCountCache.delete(oldest);
11629
+ }
11630
+ lineCountCache.set(fullPath, { mtimeMs, lineCount });
11631
+ }
11616
11632
  function validateCitation(url, projectRoot) {
11617
11633
  const parts = parseCitationUrl(url);
11618
11634
  if (!parts || !parts.path) return { ok: false, reason: "empty path" };
@@ -11636,11 +11652,14 @@ function validateCitation(url, projectRoot) {
11636
11652
  if (!stat) return { ok: false, reason: "file not found" };
11637
11653
  if (!stat.isFile()) return { ok: false, reason: "not a file" };
11638
11654
  if (parts.startLine === void 0) return { ok: true };
11639
- let lineCount;
11640
- try {
11641
- lineCount = readFileSync19(fullPath, "utf8").split("\n").length;
11642
- } catch {
11643
- return { ok: false, reason: "unreadable" };
11655
+ let lineCount = getCachedLineCount(fullPath, stat.mtimeMs);
11656
+ if (lineCount === null) {
11657
+ try {
11658
+ lineCount = readFileSync19(fullPath, "utf8").split("\n").length;
11659
+ } catch {
11660
+ return { ok: false, reason: "unreadable" };
11661
+ }
11662
+ setCachedLineCount(fullPath, stat.mtimeMs, lineCount);
11644
11663
  }
11645
11664
  if (parts.startLine < 1 || parts.startLine > lineCount) {
11646
11665
  return { ok: false, reason: `line ${parts.startLine} > ${lineCount}` };
@@ -12216,23 +12235,33 @@ function gradientCells(width, glyph = GLYPH.block) {
12216
12235
 
12217
12236
  // src/cli/ui/ticker.tsx
12218
12237
  import React10, { createContext as createContext2, useContext as useContext2, useEffect as useEffect2, useState as useState3 } from "react";
12219
- var TICK_MS = 120;
12220
- var TickContext = createContext2(0);
12238
+ var FAST_TICK_MS = 120;
12239
+ var SLOW_TICK_MS = 1e3;
12240
+ var FastTickContext = createContext2(0);
12241
+ var SlowTickContext = createContext2(0);
12221
12242
  function TickerProvider({ children, disabled }) {
12222
- const [tick, setTick] = useState3(0);
12243
+ const [fast, setFast] = useState3(0);
12244
+ const [slow, setSlow] = useState3(0);
12223
12245
  useEffect2(() => {
12224
12246
  if (disabled) return;
12225
- const id = setInterval(() => setTick((t2) => t2 + 1), TICK_MS);
12226
- return () => clearInterval(id);
12247
+ const fastId = setInterval(() => setFast((t2) => t2 + 1), FAST_TICK_MS);
12248
+ const slowId = setInterval(() => setSlow((t2) => t2 + 1), SLOW_TICK_MS);
12249
+ return () => {
12250
+ clearInterval(fastId);
12251
+ clearInterval(slowId);
12252
+ };
12227
12253
  }, [disabled]);
12228
- return /* @__PURE__ */ React10.createElement(TickContext.Provider, { value: tick }, children);
12254
+ return /* @__PURE__ */ React10.createElement(FastTickContext.Provider, { value: fast }, /* @__PURE__ */ React10.createElement(SlowTickContext.Provider, { value: slow }, children));
12229
12255
  }
12230
12256
  function useTick() {
12231
- return useContext2(TickContext);
12257
+ return useContext2(FastTickContext);
12258
+ }
12259
+ function useSlowTick() {
12260
+ return useContext2(SlowTickContext);
12232
12261
  }
12233
12262
  function useElapsedSeconds() {
12234
12263
  const [start] = useState3(() => Date.now());
12235
- useTick();
12264
+ useSlowTick();
12236
12265
  return Math.floor((Date.now() - start) / 1e3);
12237
12266
  }
12238
12267
 
@@ -12563,9 +12592,7 @@ function Elapsed() {
12563
12592
  return /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, `${mm}:${ss}`);
12564
12593
  }
12565
12594
  function PulsingAssistantGlyph() {
12566
- const tick = useTick();
12567
- const on = Math.floor(tick / 4) % 2 === 0;
12568
- return /* @__PURE__ */ React11.createElement(Text8, { color: "green", bold: true }, on ? ROLE_GLYPH.assistant : ROLE_GLYPH.assistantPulse);
12595
+ return /* @__PURE__ */ React11.createElement(Text8, { color: "green", bold: true }, ROLE_GLYPH.assistant);
12569
12596
  }
12570
12597
  function StreamingAssistant({ event }) {
12571
12598
  if (event.branchProgress) {
@@ -12582,6 +12609,7 @@ function StreamingAssistant({ event }) {
12582
12609
  const preFirstByte = !event.text && !event.reasoning && !toolCallBuild;
12583
12610
  const reasoningOnly = !event.text && !!event.reasoning && !toolCallBuild;
12584
12611
  const toolCallOnly = !event.text && !event.reasoning && !!toolCallBuild;
12612
+ const PILL_WIDTH = 8;
12585
12613
  let pillBg;
12586
12614
  let pillText;
12587
12615
  let label;
@@ -12609,6 +12637,7 @@ function StreamingAssistant({ event }) {
12609
12637
  }
12610
12638
  label = parts.join(" \xB7 ");
12611
12639
  }
12640
+ pillText = pillText.padEnd(PILL_WIDTH);
12612
12641
  return /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(PulsingAssistantGlyph, null), /* @__PURE__ */ React11.createElement(Text8, null, " "), /* @__PURE__ */ React11.createElement(Pulse, null), /* @__PURE__ */ React11.createElement(Text8, null, " "), /* @__PURE__ */ React11.createElement(Text8, { backgroundColor: pillBg, color: "black", bold: true }, ` ${pillText} `), /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, ` ${label} `), /* @__PURE__ */ React11.createElement(Elapsed, null)), reasoningTail ? /* @__PURE__ */ React11.createElement(Box9, { paddingLeft: 3 }, /* @__PURE__ */ React11.createElement(Text8, { color: "#c4b5fd", italic: true, dimColor: true }, "\u21B3 ", reasoningTail)) : null, tail ? /* @__PURE__ */ React11.createElement(Box9, { paddingLeft: 3 }, /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, "\u25B8 ", tail)) : preFirstByte ? (
12613
12642
  // Non-dim amber: first-time users misread the dim version as
12614
12643
  // "app frozen". The reassurance has to be VISIBLE to do its job.
@@ -12631,7 +12660,8 @@ function formatReadyTail(tb) {
12631
12660
  return ` \xB7 ${n} ready`;
12632
12661
  }
12633
12662
  function lastLine(s, maxChars) {
12634
- const flat = s.replace(/\s+/g, " ").trim();
12663
+ const tailSlice = s.length > maxChars * 4 ? s.slice(-maxChars * 4) : s;
12664
+ const flat = tailSlice.replace(/\s+/g, " ").trim();
12635
12665
  if (!flat) return "";
12636
12666
  return flat.length <= maxChars ? flat : `\u2026${flat.slice(-maxChars)}`;
12637
12667
  }
@@ -12658,7 +12688,7 @@ function ModeStatusBar({
12658
12688
  undoArmed,
12659
12689
  jobs: jobs2
12660
12690
  }) {
12661
- useTick();
12691
+ useSlowTick();
12662
12692
  const running = jobs2?.runningCount() ?? 0;
12663
12693
  const jobsTag = running > 0 ? /* @__PURE__ */ React12.createElement(Text9, { color: "yellow", bold: true }, ` \xB7 \u23F5 ${running} job${running === 1 ? "" : "s"}`) : null;
12664
12694
  if (planMode) {
@@ -12685,7 +12715,7 @@ function ModePill({
12685
12715
  function UndoBanner({
12686
12716
  banner
12687
12717
  }) {
12688
- useTick();
12718
+ useSlowTick();
12689
12719
  const remainingMs = Math.max(0, banner.expiresAt - Date.now());
12690
12720
  const remainingSec = Math.ceil(remainingMs / 1e3);
12691
12721
  const ok = banner.results.filter((r) => r.status === "applied" || r.status === "created").length;
@@ -14553,9 +14583,9 @@ var SLASH_COMMANDS = [
14553
14583
  { cmd: "status", summary: "current model, flags, context, session" },
14554
14584
  {
14555
14585
  cmd: "preset",
14556
- argsHint: "<fast|smart|max>",
14557
- summary: "one-tap model + harvest + branch bundle",
14558
- argCompleter: ["fast", "smart", "max"]
14586
+ argsHint: "<auto|flash|pro>",
14587
+ summary: "model bundle \u2014 auto escalates flash \u2192 pro, flash/pro lock",
14588
+ argCompleter: ["auto", "flash", "pro"]
14559
14589
  },
14560
14590
  {
14561
14591
  cmd: "model",
@@ -15143,7 +15173,7 @@ var help = () => ({
15143
15173
  " /help this message",
15144
15174
  " /keys keyboard shortcuts + prompt prefixes (!, @, /)",
15145
15175
  " /status show current settings",
15146
- " /preset <fast|smart|max> one-tap presets \u2014 see below",
15176
+ " /preset <auto|flash|pro> model bundle \u2014 see below",
15147
15177
  " /model <id> deepseek-v4-flash or deepseek-v4-pro",
15148
15178
  " /pro [off] arm v4-pro for NEXT turn only (one-shot, auto-disarms)",
15149
15179
  " /harvest [on|off] Pillar 2: structured plan-state extraction (OPT-IN \u2014 costs extra)",
@@ -15205,9 +15235,9 @@ var help = () => ({
15205
15235
  " Trailing sentence punctuation (./,/)) is stripped automatically.",
15206
15236
  "",
15207
15237
  "Presets (branch + harvest are NEVER auto-enabled \u2014 opt-in only):",
15208
- " fast v4-flash \xB7 effort=high cheapest \xB7 quick Q&A, one-line edits",
15209
- " smart v4-flash \xB7 effort=max \u2190 default \xB7 day-to-day coding",
15210
- " max v4-pro \xB7 effort=max ~12\xD7 fast \xB7 hard single-shots",
15238
+ " auto v4-flash \u2192 v4-pro on hard turns \u2190 default \xB7 cheap when easy, smart when hard",
15239
+ " flash v4-flash always cheapest \xB7 predictable per-turn cost",
15240
+ " pro v4-pro always ~3\xD7 flash (5/31) \xB7 hard multi-turn work",
15211
15241
  "",
15212
15242
  "Sessions (auto-enabled by default, named 'default'):",
15213
15243
  " reasonix chat --session <name> use a different named session",
@@ -15977,39 +16007,43 @@ var preset = (args, loop2) => {
15977
16007
  } catch {
15978
16008
  }
15979
16009
  };
15980
- if (name === "fast" || name === "default") {
16010
+ if (name === "auto") {
16011
+ const p = PRESETS.auto;
15981
16012
  loop2.configure({
15982
- model: "deepseek-v4-flash",
15983
- reasoningEffort: "high",
15984
- harvest: false,
15985
- branch: 1
16013
+ model: p.model,
16014
+ autoEscalate: p.autoEscalate,
16015
+ reasoningEffort: p.reasoningEffort,
16016
+ harvest: p.harvest,
16017
+ branch: p.branch
15986
16018
  });
15987
- applyAndPersist("high");
15988
- return { info: "preset \u2192 fast (v4-flash \xB7 effort=high \xB7 cheapest)" };
16019
+ applyAndPersist(p.reasoningEffort);
16020
+ return { info: "preset \u2192 auto (v4-flash \u2192 v4-pro on hard turns \xB7 default)" };
15989
16021
  }
15990
- if (name === "smart") {
16022
+ if (name === "flash") {
16023
+ const p = PRESETS.flash;
15991
16024
  loop2.configure({
15992
- model: "deepseek-v4-flash",
15993
- reasoningEffort: "max",
15994
- harvest: false,
15995
- branch: 1
16025
+ model: p.model,
16026
+ autoEscalate: p.autoEscalate,
16027
+ reasoningEffort: p.reasoningEffort,
16028
+ harvest: p.harvest,
16029
+ branch: p.branch
15996
16030
  });
15997
- applyAndPersist("max");
15998
- return { info: "preset \u2192 smart (v4-flash \xB7 effort=max \xB7 default \xB7 ~1.5\xD7 fast)" };
16031
+ applyAndPersist(p.reasoningEffort);
16032
+ return { info: "preset \u2192 flash (v4-flash always \xB7 cheapest \xB7 /pro still bumps one turn)" };
15999
16033
  }
16000
- if (name === "max" || name === "best") {
16034
+ if (name === "pro") {
16035
+ const p = PRESETS.pro;
16001
16036
  loop2.configure({
16002
- model: "deepseek-v4-pro",
16003
- reasoningEffort: "max",
16004
- harvest: false,
16005
- branch: 1
16037
+ model: p.model,
16038
+ autoEscalate: p.autoEscalate,
16039
+ reasoningEffort: p.reasoningEffort,
16040
+ harvest: p.harvest,
16041
+ branch: p.branch
16006
16042
  });
16007
- applyAndPersist("max");
16008
- return {
16009
- info: "preset \u2192 max (v4-pro \xB7 effort=max \xB7 ~12\xD7 fast \xB7 save for hard tasks, or use /pro for a single-turn bump)"
16010
- };
16043
+ applyAndPersist(p.reasoningEffort);
16044
+ return { info: "preset \u2192 pro (v4-pro always \xB7 ~3\xD7 flash \xB7 for hard multi-turn work)" };
16011
16045
  }
16012
- return { info: "usage: /preset <fast|smart|max>" };
16046
+ return { info: "usage: /preset <auto|flash|pro>" };
16013
16047
  };
16014
16048
  var branch = (args, loop2) => {
16015
16049
  const raw = (args[0] ?? "").toLowerCase();
@@ -17266,7 +17300,13 @@ function useSubagent({ session, setHistorical }) {
17266
17300
  }
17267
17301
 
17268
17302
  // src/cli/ui/App.tsx
17269
- var FLUSH_INTERVAL_MS = 100;
17303
+ var FLUSH_INTERVAL_MS = (() => {
17304
+ const raw = process.env.REASONIX_FLUSH_MS;
17305
+ if (!raw) return 33;
17306
+ const parsed = Number(raw);
17307
+ if (!Number.isFinite(parsed) || parsed < 16 || parsed > 1e3) return 33;
17308
+ return Math.round(parsed);
17309
+ })();
17270
17310
  var PLAIN_UI = process.env.REASONIX_UI === "plain";
17271
17311
  function LoopStatusRow({
17272
17312
  loop: loop2
@@ -21065,7 +21105,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
21065
21105
  const [step, setStep] = useState15(existingApiKey ? "preset" : "apiKey");
21066
21106
  const [data, setData] = useState15({
21067
21107
  apiKey: existingApiKey ?? "",
21068
- preset: initial?.preset ?? "fast",
21108
+ preset: initial?.preset ?? "auto",
21069
21109
  selectedCatalog: deriveInitialCatalog(initial?.mcp ?? []),
21070
21110
  catalogArgs: {}
21071
21111
  });
@@ -21510,7 +21550,7 @@ program.command("setup").description("Interactive wizard \u2014 API key, preset,
21510
21550
  await setupCommand({});
21511
21551
  });
21512
21552
  program.command("code [dir]").description(
21513
- "Code-editing chat \u2014 filesystem tools rooted at <dir> (default: cwd), coding system prompt, v4-flash. Model proposes SEARCH/REPLACE blocks; Reasonix applies them to disk. Use /preset max or /pro to escalate to v4-pro on hard tasks."
21553
+ "Code-editing chat \u2014 filesystem tools rooted at <dir> (default: cwd), coding system prompt, v4-flash baseline. Model proposes SEARCH/REPLACE blocks; Reasonix applies them to disk. Use /preset pro or /pro to lock v4-pro on hard tasks."
21514
21554
  ).option("-m, --model <id>", "Override default model (v4-flash)").option("--no-session", "Disable session persistence for this run").option("-r, --resume", "Skip the session picker \u2014 always continue prior messages").option("-n, --new", "Skip the session picker \u2014 always wipe prior messages and start fresh").option("--transcript <path>", "Write a JSONL transcript to this path").option(
21515
21555
  "--harvest",
21516
21556
  "Opt-in Pillar-2 plan-state extraction. Adds one flash call per turn; off by default (no preset enables it)."
@@ -21531,7 +21571,7 @@ program.command("code [dir]").description(
21531
21571
  });
21532
21572
  program.command("chat").description("Interactive Ink TUI with live cache/cost panel.").option("-m, --model <id>", "DeepSeek model id (overrides preset)").option("-s, --system <prompt>", "System prompt (pinned in the immutable prefix)", DEFAULT_SYSTEM).option("--transcript <path>", "Write a JSONL transcript to this path").option(
21533
21573
  "--preset <name>",
21534
- "Model + effort bundle. One of: fast (flash\xB7high), smart (flash\xB7max, default), max (pro\xB7max). Overrides config.preset."
21574
+ "Model bundle. One of: auto (flash \u2192 pro on hard turns, default), flash (always flash), pro (always pro). Overrides config.preset."
21535
21575
  ).option(
21536
21576
  "--harvest",
21537
21577
  "Opt-in Pillar-2 plan-state extraction. Off by default \u2014 no preset enables it."
@@ -21584,7 +21624,7 @@ program.command("chat").description("Interactive Ink TUI with live cache/cost pa
21584
21624
  noDashboard: opts.dashboard === false
21585
21625
  });
21586
21626
  });
21587
- program.command("run <task>").description("Run a single task non-interactively, streaming output.").option("-m, --model <id>", "DeepSeek model id (overrides preset)").option("-s, --system <prompt>", "System prompt", DEFAULT_SYSTEM).option("--preset <name>", "Bundle of model + harvest + branch: fast | smart | max").option("--harvest", "Extract typed plan state from R1 reasoning (Pillar 2)").option(
21627
+ program.command("run <task>").description("Run a single task non-interactively, streaming output.").option("-m, --model <id>", "DeepSeek model id (overrides preset)").option("-s, --system <prompt>", "System prompt", DEFAULT_SYSTEM).option("--preset <name>", "Model bundle: auto | flash | pro (default: auto)").option("--harvest", "Extract typed plan state from R1 reasoning (Pillar 2)").option(
21588
21628
  "--branch <n>",
21589
21629
  "Self-consistency: run N parallel samples per turn and pick the most confident",
21590
21630
  (v) => Number.parseInt(v, 10)