reasonix 0.23.0 → 0.23.1
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 +508 -330
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +57 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -631,6 +631,7 @@ declare class CacheFirstLoop {
|
|
|
631
631
|
private _escalateThisTurn;
|
|
632
632
|
private _turnFailureCount;
|
|
633
633
|
private _turnFailureTypes;
|
|
634
|
+
private _turnSelfCorrected;
|
|
634
635
|
constructor(opts: CacheFirstLoopOptions);
|
|
635
636
|
/** Shrink huge edit_file/write_file args post-dispatch — tool result already explains. */
|
|
636
637
|
private compactToolCallArgsAfterResponse;
|
|
@@ -642,6 +643,8 @@ declare class CacheFirstLoop {
|
|
|
642
643
|
charsSaved: number;
|
|
643
644
|
};
|
|
644
645
|
appendAndPersist(message: ChatMessage): void;
|
|
646
|
+
/** Swap the just-appended assistant entry — used by self-correction to restore the original tool_calls without dropping reasoning_content. */
|
|
647
|
+
private replaceTailAssistantMessage;
|
|
645
648
|
/** "New chat" — drops messages but keeps session + immutable prefix (cache-first invariant). */
|
|
646
649
|
clearLog(): {
|
|
647
650
|
dropped: number;
|
package/dist/index.js
CHANGED
|
@@ -1573,7 +1573,7 @@ var StormBreaker = class {
|
|
|
1573
1573
|
if (count >= this.threshold - 1) {
|
|
1574
1574
|
return {
|
|
1575
1575
|
suppress: true,
|
|
1576
|
-
reason:
|
|
1576
|
+
reason: `${name} called with identical args ${count + 1} times \u2014 repeat-loop guard tripped`
|
|
1577
1577
|
};
|
|
1578
1578
|
}
|
|
1579
1579
|
this.recent.push({ name, args, readOnly });
|
|
@@ -1858,6 +1858,7 @@ var CacheFirstLoop = class {
|
|
|
1858
1858
|
_escalateThisTurn = false;
|
|
1859
1859
|
_turnFailureCount = 0;
|
|
1860
1860
|
_turnFailureTypes = {};
|
|
1861
|
+
_turnSelfCorrected = false;
|
|
1861
1862
|
constructor(opts) {
|
|
1862
1863
|
this.client = opts.client;
|
|
1863
1864
|
this.prefix = opts.prefix;
|
|
@@ -1902,7 +1903,12 @@ var CacheFirstLoop = class {
|
|
|
1902
1903
|
}
|
|
1903
1904
|
return def.readOnly !== true;
|
|
1904
1905
|
};
|
|
1905
|
-
this.repair = new ToolCallRepair({
|
|
1906
|
+
this.repair = new ToolCallRepair({
|
|
1907
|
+
allowedToolNames: allowedNames,
|
|
1908
|
+
isMutating,
|
|
1909
|
+
stormThreshold: parsePositiveIntEnv(process.env.REASONIX_STORM_THRESHOLD),
|
|
1910
|
+
stormWindow: parsePositiveIntEnv(process.env.REASONIX_STORM_WINDOW)
|
|
1911
|
+
});
|
|
1906
1912
|
this.sessionName = opts.session ?? null;
|
|
1907
1913
|
if (this.sessionName) {
|
|
1908
1914
|
const prior = loadSessionMessages(this.sessionName);
|
|
@@ -1984,6 +1990,21 @@ var CacheFirstLoop = class {
|
|
|
1984
1990
|
}
|
|
1985
1991
|
}
|
|
1986
1992
|
}
|
|
1993
|
+
/** Swap the just-appended assistant entry — used by self-correction to restore the original tool_calls without dropping reasoning_content. */
|
|
1994
|
+
replaceTailAssistantMessage(message) {
|
|
1995
|
+
const entries = this.log.entries;
|
|
1996
|
+
const tail = entries[entries.length - 1];
|
|
1997
|
+
if (!tail || tail.role !== "assistant") return;
|
|
1998
|
+
const kept = entries.slice(0, -1);
|
|
1999
|
+
kept.push(message);
|
|
2000
|
+
this.log.compactInPlace(kept);
|
|
2001
|
+
if (this.sessionName) {
|
|
2002
|
+
try {
|
|
2003
|
+
rewriteSession(this.sessionName, kept);
|
|
2004
|
+
} catch {
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
1987
2008
|
/** "New chat" — drops messages but keeps session + immutable prefix (cache-first invariant). */
|
|
1988
2009
|
clearLog() {
|
|
1989
2010
|
const dropped = this.log.length;
|
|
@@ -2084,7 +2105,7 @@ var CacheFirstLoop = class {
|
|
|
2084
2105
|
if (repair) {
|
|
2085
2106
|
if (repair.scavenged > 0) bump("scavenged", repair.scavenged);
|
|
2086
2107
|
if (repair.truncationsFixed > 0) bump("truncated", repair.truncationsFixed);
|
|
2087
|
-
if (repair.stormsBroken > 0) bump("
|
|
2108
|
+
if (repair.stormsBroken > 0) bump("repeat-loop", repair.stormsBroken);
|
|
2088
2109
|
}
|
|
2089
2110
|
if (bumped && !this._escalateThisTurn && this.autoEscalate && this._turnFailureCount >= FAILURE_ESCALATION_THRESHOLD) {
|
|
2090
2111
|
this._escalateThisTurn = true;
|
|
@@ -2154,6 +2175,7 @@ var CacheFirstLoop = class {
|
|
|
2154
2175
|
this.repair.resetStorm();
|
|
2155
2176
|
this._turnFailureCount = 0;
|
|
2156
2177
|
this._turnFailureTypes = {};
|
|
2178
|
+
this._turnSelfCorrected = false;
|
|
2157
2179
|
this._escalateThisTurn = false;
|
|
2158
2180
|
let armedConsumed = false;
|
|
2159
2181
|
if (this._proArmedForNextTurn) {
|
|
@@ -2505,10 +2527,35 @@ var CacheFirstLoop = class {
|
|
|
2505
2527
|
content: `\u21E7 auto-escalating to ${ESCALATION_MODEL} for the rest of this turn \u2014 flash hit ${this.formatFailureBreakdown()}. Next turn falls back to ${this.model} unless /pro is armed.`
|
|
2506
2528
|
};
|
|
2507
2529
|
}
|
|
2530
|
+
const allSuppressed = report.stormsBroken > 0 && repairedCalls.length === 0 && toolCalls.length > 0;
|
|
2531
|
+
if (allSuppressed && !this._turnSelfCorrected) {
|
|
2532
|
+
this._turnSelfCorrected = true;
|
|
2533
|
+
this.replaceTailAssistantMessage(
|
|
2534
|
+
this.assistantMessage(
|
|
2535
|
+
assistantContent,
|
|
2536
|
+
toolCalls,
|
|
2537
|
+
this.modelForCurrentCall(),
|
|
2538
|
+
reasoningContent
|
|
2539
|
+
)
|
|
2540
|
+
);
|
|
2541
|
+
for (const call of toolCalls) {
|
|
2542
|
+
this.appendAndPersist({
|
|
2543
|
+
role: "tool",
|
|
2544
|
+
tool_call_id: call.id ?? "",
|
|
2545
|
+
name: call.function?.name ?? "",
|
|
2546
|
+
content: "[repeat-loop guard] this call was suppressed because it was identical to a previous call in this turn. Earlier results for it are above \u2014 try a meaningfully different approach, or stop and answer if you have enough."
|
|
2547
|
+
});
|
|
2548
|
+
}
|
|
2549
|
+
yield {
|
|
2550
|
+
turn: this._turn,
|
|
2551
|
+
role: "warning",
|
|
2552
|
+
content: "Caught a repeated tool call \u2014 let the model see the issue and retry with a different approach."
|
|
2553
|
+
};
|
|
2554
|
+
continue;
|
|
2555
|
+
}
|
|
2508
2556
|
if (report.stormsBroken > 0) {
|
|
2509
2557
|
const noteTail = report.notes.length ? ` \u2014 ${report.notes[report.notes.length - 1]}` : "";
|
|
2510
|
-
const
|
|
2511
|
-
const phrase = allSuppressed ? `stopped the model from calling the same tool with identical args repeatedly (all ${toolCalls.length} call(s) this turn were already in the recent-repeat window). Likely a stuck retry \u2014 reword your instruction, rule out the underlying blocker, or try /retry after fixing it` : `suppressed ${report.stormsBroken} repeat tool call(s) that had fired 3+ times with identical args in a sliding window`;
|
|
2558
|
+
const phrase = allSuppressed ? "Stopped a stuck retry loop \u2014 the model kept calling the same tool with identical args after a self-correction nudge. Try /retry, rephrase, or rule out the underlying blocker." : `Suppressed ${report.stormsBroken} repeated tool call(s) \u2014 same name + args fired 3+ times.`;
|
|
2512
2559
|
yield {
|
|
2513
2560
|
turn: this._turn,
|
|
2514
2561
|
role: "warning",
|
|
@@ -2516,7 +2563,6 @@ var CacheFirstLoop = class {
|
|
|
2516
2563
|
};
|
|
2517
2564
|
}
|
|
2518
2565
|
if (repairedCalls.length === 0) {
|
|
2519
|
-
const allSuppressed = report.stormsBroken > 0 && toolCalls.length > 0;
|
|
2520
2566
|
if (allSuppressed) {
|
|
2521
2567
|
yield* this.forceSummaryAfterIterLimit({ reason: "stuck" });
|
|
2522
2568
|
return;
|
|
@@ -2741,6 +2787,11 @@ function stripHallucinatedToolMarkup(s) {
|
|
|
2741
2787
|
out = out.replace(/<|DSML|[\s\S]*$/g, "");
|
|
2742
2788
|
return out.trim();
|
|
2743
2789
|
}
|
|
2790
|
+
function parsePositiveIntEnv(raw) {
|
|
2791
|
+
if (!raw) return void 0;
|
|
2792
|
+
const n = Number.parseInt(raw, 10);
|
|
2793
|
+
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
2794
|
+
}
|
|
2744
2795
|
function safeParseToolArgs(raw) {
|
|
2745
2796
|
try {
|
|
2746
2797
|
return JSON.parse(raw);
|