reasonix 0.7.0 → 0.7.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/index.js CHANGED
@@ -2554,6 +2554,11 @@ var CacheFirstLoop = class {
2554
2554
  };
2555
2555
  }
2556
2556
  if (repairedCalls.length === 0) {
2557
+ const allSuppressed = report.stormsBroken > 0 && toolCalls.length > 0;
2558
+ if (allSuppressed) {
2559
+ yield* this.forceSummaryAfterIterLimit({ reason: "stuck" });
2560
+ return;
2561
+ }
2557
2562
  this.autoCompactToolResultsOnTurnEnd();
2558
2563
  yield { turn: this._turn, role: "done", content: assistantContent };
2559
2564
  return;
@@ -2822,11 +2827,15 @@ function reasonPrefixFor(reason, iterCap) {
2822
2827
  if (reason === "context-guard") {
2823
2828
  return "[context budget running low \u2014 summarizing before the next call would overflow]";
2824
2829
  }
2830
+ if (reason === "stuck") {
2831
+ return "[stuck on a repeated tool call \u2014 explaining what was tried and what's blocking progress]";
2832
+ }
2825
2833
  return `[tool-call budget (${iterCap}) reached \u2014 forcing summary from what I found]`;
2826
2834
  }
2827
2835
  function errorLabelFor(reason, iterCap) {
2828
2836
  if (reason === "aborted") return "aborted by user";
2829
2837
  if (reason === "context-guard") return "context-guard triggered (prompt > 80% of window)";
2838
+ if (reason === "stuck") return "stuck (repeated tool call suppressed by storm-breaker)";
2830
2839
  return `tool-call budget (${iterCap}) reached`;
2831
2840
  }
2832
2841
  function summarizeBranch(chosen, samples) {
@@ -5569,7 +5578,15 @@ async function runCommand(cmd, opts) {
5569
5578
  shell: false,
5570
5579
  // no shell-expansion — see header comment
5571
5580
  windowsHide: true,
5572
- env: process.env
5581
+ // PYTHONIOENCODING + PYTHONUTF8 force any spawned Python child
5582
+ // (run_command running `python script.py`, etc.) to emit UTF-8
5583
+ // on stdout/stderr. Without this, Chinese-Windows defaults
5584
+ // Python's stdout encoder to GBK and `print("…")` raises
5585
+ // UnicodeEncodeError on emoji / non-GBK chars — the model then
5586
+ // sees a Python traceback instead of the script's real output
5587
+ // and goes around in circles trying to fix the wrong problem.
5588
+ // Harmless on non-Python processes (env vars they don't read).
5589
+ env: { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1" }
5573
5590
  };
5574
5591
  const { bin, args, spawnOverrides } = prepareSpawn(argv);
5575
5592
  const effectiveSpawnOpts = { ...spawnOpts, ...spawnOverrides };
@@ -6018,7 +6035,7 @@ function registerWebTools(registry, opts = {}) {
6018
6035
  const maxFetchChars = opts.maxFetchChars ?? DEFAULT_FETCH_MAX_CHARS;
6019
6036
  registry.register({
6020
6037
  name: "web_search",
6021
- description: "Search the public web. Returns ranked results with title, url, and snippet. Use this when the question needs information more current than your training data, when you're unsure of a factual detail, or when the user asks about a specific webpage/library/release you haven't seen.",
6038
+ description: "Search the public web. Returns ranked results with title, url, and snippet. Call this when the answer's correctness depends on current state \u2014 anything that changes over time (events, prices, releases, status of a thing in the real world). Composing such answers from training memory invents stale numbers; search first, then ground the answer in the results. For evergreen / definitional questions you don't need this.",
6022
6039
  readOnly: true,
6023
6040
  parameters: {
6024
6041
  type: "object",