reasonix 0.5.2 → 0.5.3

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.d.ts CHANGED
@@ -903,17 +903,23 @@ declare class CacheFirstLoop {
903
903
  constructor(opts: CacheFirstLoopOptions);
904
904
  /**
905
905
  * Shrink the log by re-truncating oversized tool results to a tighter
906
- * cap, and persist the result back to disk so the next launch doesn't
907
- * re-inherit a fat session file. Returns a summary the TUI can
908
- * display.
906
+ * token cap, and persist the result back to disk so the next launch
907
+ * doesn't re-inherit a fat session file. Returns a summary the TUI
908
+ * can display.
909
+ *
910
+ * The cap is in DeepSeek V3 tokens (not chars) — so CJK text gets
911
+ * capped at the same effective context footprint as English instead
912
+ * of slipping past a char cap at 2× the token cost. Default 4000
913
+ * tokens, matching the token-aware dispatch cap from 0.5.2.
909
914
  *
910
915
  * Only tool-role messages are touched (same rationale as
911
916
  * {@link healLoadedMessages}). User and assistant messages carry
912
917
  * authored intent we can't mechanically shrink without losing
913
918
  * meaning.
914
919
  */
915
- compact(tightCapChars?: number): {
920
+ compact(maxTokens?: number): {
916
921
  healedCount: number;
922
+ tokensSaved: number;
917
923
  charsSaved: number;
918
924
  };
919
925
  private appendAndPersist;
package/dist/index.js CHANGED
@@ -1737,20 +1737,26 @@ var CacheFirstLoop = class {
1737
1737
  }
1738
1738
  /**
1739
1739
  * Shrink the log by re-truncating oversized tool results to a tighter
1740
- * cap, and persist the result back to disk so the next launch doesn't
1741
- * re-inherit a fat session file. Returns a summary the TUI can
1742
- * display.
1740
+ * token cap, and persist the result back to disk so the next launch
1741
+ * doesn't re-inherit a fat session file. Returns a summary the TUI
1742
+ * can display.
1743
+ *
1744
+ * The cap is in DeepSeek V3 tokens (not chars) — so CJK text gets
1745
+ * capped at the same effective context footprint as English instead
1746
+ * of slipping past a char cap at 2× the token cost. Default 4000
1747
+ * tokens, matching the token-aware dispatch cap from 0.5.2.
1743
1748
  *
1744
1749
  * Only tool-role messages are touched (same rationale as
1745
1750
  * {@link healLoadedMessages}). User and assistant messages carry
1746
1751
  * authored intent we can't mechanically shrink without losing
1747
1752
  * meaning.
1748
1753
  */
1749
- compact(tightCapChars = 4e3) {
1754
+ compact(maxTokens = 4e3) {
1750
1755
  const before = this.log.toMessages();
1751
- const { messages, healedCount, healedFrom } = shrinkOversizedToolResults(before, tightCapChars);
1752
- const afterBytes = messages.filter((m) => m.role === "tool").reduce((s, m) => s + (typeof m.content === "string" ? m.content.length : 0), 0);
1753
- const charsSaved = healedFrom - afterBytes;
1756
+ const { messages, healedCount, tokensSaved, charsSaved } = shrinkOversizedToolResultsByTokens(
1757
+ before,
1758
+ maxTokens
1759
+ );
1754
1760
  if (healedCount > 0) {
1755
1761
  this.log.compactInPlace(messages);
1756
1762
  if (this.sessionName) {
@@ -1760,7 +1766,7 @@ var CacheFirstLoop = class {
1760
1766
  }
1761
1767
  }
1762
1768
  }
1763
- return { healedCount, charsSaved };
1769
+ return { healedCount, tokensSaved, charsSaved };
1764
1770
  }
1765
1771
  appendAndPersist(message) {
1766
1772
  this.log.append(message);
@@ -2124,30 +2130,28 @@ var CacheFirstLoop = class {
2124
2130
  const ratio = usage.promptTokens / ctxMax;
2125
2131
  if (ratio > 0.6 && ratio <= 0.8) {
2126
2132
  const before = usage.promptTokens;
2127
- const soft = this.compact(16e3);
2133
+ const soft = this.compact(4e3);
2128
2134
  if (soft.healedCount > 0) {
2129
- const approxSaved = Math.round(soft.charsSaved / 4);
2130
- const after = Math.max(0, before - approxSaved);
2135
+ const after = Math.max(0, before - soft.tokensSaved);
2131
2136
  yield {
2132
2137
  turn: this._turn,
2133
2138
  role: "warning",
2134
2139
  content: `context ${before.toLocaleString()}/${ctxMax.toLocaleString()} (${Math.round(
2135
2140
  ratio * 100
2136
- )}%) \u2014 proactively compacted ${soft.healedCount} tool result(s) to 16k, saved ~${approxSaved.toLocaleString()} tokens (now ~${after.toLocaleString()}). Staying ahead of the 80% guard.`
2141
+ )}%) \u2014 proactively compacted ${soft.healedCount} tool result(s) to 4k tokens, saved ${soft.tokensSaved.toLocaleString()} tokens (now ~${after.toLocaleString()}). Staying ahead of the 80% guard.`
2137
2142
  };
2138
2143
  }
2139
2144
  }
2140
2145
  }
2141
2146
  if (usage && usage.promptTokens / ctxMax > 0.8) {
2142
2147
  const before = usage.promptTokens;
2143
- const compactResult = this.compact(4e3);
2148
+ const compactResult = this.compact(1e3);
2144
2149
  if (compactResult.healedCount > 0) {
2145
- const approxSaved = Math.round(compactResult.charsSaved / 4);
2146
- const after = before - approxSaved;
2150
+ const after = Math.max(0, before - compactResult.tokensSaved);
2147
2151
  yield {
2148
2152
  turn: this._turn,
2149
2153
  role: "warning",
2150
- content: `context ${before.toLocaleString()}/${ctxMax.toLocaleString()} \u2014 auto-compacted ${compactResult.healedCount} oversized tool result(s), saved ~${approxSaved.toLocaleString()} tokens (now ~${after.toLocaleString()}). Continuing.`
2154
+ content: `context ${before.toLocaleString()}/${ctxMax.toLocaleString()} \u2014 auto-compacted ${compactResult.healedCount} oversized tool result(s), saved ${compactResult.tokensSaved.toLocaleString()} tokens (now ~${after.toLocaleString()}). Continuing.`
2151
2155
  };
2152
2156
  } else {
2153
2157
  yield {
@@ -2348,6 +2352,25 @@ function shrinkOversizedToolResults(messages, maxChars) {
2348
2352
  });
2349
2353
  return { messages: out, healedCount, healedFrom };
2350
2354
  }
2355
+ function shrinkOversizedToolResultsByTokens(messages, maxTokens) {
2356
+ let healedCount = 0;
2357
+ let tokensSaved = 0;
2358
+ let charsSaved = 0;
2359
+ const out = messages.map((msg) => {
2360
+ if (msg.role !== "tool") return msg;
2361
+ const content = typeof msg.content === "string" ? msg.content : "";
2362
+ if (content.length <= maxTokens) return msg;
2363
+ const beforeTokens = countTokens(content);
2364
+ if (beforeTokens <= maxTokens) return msg;
2365
+ const truncated = truncateForModelByTokens(content, maxTokens);
2366
+ const afterTokens = countTokens(truncated);
2367
+ healedCount += 1;
2368
+ tokensSaved += Math.max(0, beforeTokens - afterTokens);
2369
+ charsSaved += Math.max(0, content.length - truncated.length);
2370
+ return { ...msg, content: truncated };
2371
+ });
2372
+ return { messages: out, healedCount, tokensSaved, charsSaved };
2373
+ }
2351
2374
  function healLoadedMessages(messages, maxChars) {
2352
2375
  const shrunk = shrinkOversizedToolResults(messages, maxChars);
2353
2376
  let healedCount = shrunk.healedCount;