aboutsummaryrefslogtreecommitdiff
path: root/modules/core/core-bundle.js
diff options
context:
space:
mode:
Diffstat (limited to 'modules/core/core-bundle.js')
-rw-r--r--modules/core/core-bundle.js30
1 files changed, 24 insertions, 6 deletions
diff --git a/modules/core/core-bundle.js b/modules/core/core-bundle.js
index 6e9542e..8d6cbda 100644
--- a/modules/core/core-bundle.js
+++ b/modules/core/core-bundle.js
@@ -9841,6 +9841,23 @@ buildSystemPrompt() {
}
return result.substring(0, maxLen) + '...(truncated from ' + result.length + ' chars)';
}
+ // MAINT-004: the loop guard read warnAt/blockAt/hardStopAt while one config path wrote
+ // `threshold`, and `enabled` was never consulted at all — turning the guard off in
+ // config changed nothing. One resolver, one set of defaults, explicit disable.
+ _loopPolicy(cfg) {
+ const c = cfg || {};
+ const threshold = Number(c.threshold);
+ const blockAt = Number(c.blockAt) || (Number.isFinite(threshold) && threshold > 0 ? threshold : 6);
+ const warnAt = Number(c.warnAt) || Math.max(2, blockAt - 3);
+ const hardStopAt = Number(c.hardStopAt) || blockAt + 3;
+ return {
+ enabled: c.enabled !== false,
+ windowMs: Number(c.windowMs) || 30000,
+ warnAt: Math.min(warnAt, blockAt),
+ blockAt,
+ hardStopAt: Math.max(hardStopAt, blockAt + 1)
+ };
+ }
// MAINT-023: one agentic turn used to grow without limit — every role:'tool' body was
// pushed verbatim and only this.context was trimmed BETWEEN turns, so a measured turn
// went 6 → 112 705 tokens across 8 model calls (maxContextTokens is 12 000). Two
@@ -10043,6 +10060,7 @@ buildSystemPrompt() {
} catch (_e) {}
};
var loopDetect = loopCfg.loopDetection || { enabled: true, threshold: 6, windowMs: 30000 };
+ var _lp = this._loopPolicy(loopDetect);
var confEsc = loopCfg.confidenceEscalation || { enabled: true, threshold: 90 };
var finalPrompt = loopCfg.finalTurnPrompt || 'This is your last tool turn. You MUST provide a final answer after this tool call. Synthesize what you know.';
// Memory-augmented planning: inject prior loop lessons as negative constraints
@@ -10077,10 +10095,10 @@ buildSystemPrompt() {
} catch (e) { /* VFS not available — skip memory-augmented planning */ }
let toolTurns = 0;
const _seenCalls = new Map(); // sig -> {count, ts, resultHash, blocked, lastResult}
- const _loopWindow = loopDetect.windowMs || 30000;
- const _warnAt = loopDetect.warnAt || 3;
- const _blockAt = loopDetect.blockAt || 6;
- const _hardStopAt = loopDetect.hardStopAt || 9;
+ const _loopWindow = _lp.windowMs;
+ const _warnAt = _lp.warnAt;
+ const _blockAt = _lp.blockAt;
+ const _hardStopAt = _lp.hardStopAt;
const _agentTag = opts.agentTag || 'main';
this._pendingBlockedReplays = null;
const _sigHash = (s) => {
@@ -10166,13 +10184,13 @@ buildSystemPrompt() {
const argPreview = String(_argKey || '')
.replace(/(token|key|secret|password|bearer)[^,}]*/ig, '$1:[redacted]')
.slice(0, 60);
- if (entry.blocked || entry.count >= _blockAt) {
+ if (_lp.enabled && (entry.blocked || entry.count >= _blockAt)) {
entry.blocked = true;
blocked.push({ tc, entry });
this.terminal.print(`🔄 Loop guard: ${_agentTag} repeated ${tc.function.name}(${argPreview}) ${entry.count}x with an unchanged result — serving cached result, not re-running`, 'warning');
continue;
}
- if (entry.count === _warnAt) {
+ if (_lp.enabled && entry.count === _warnAt) {
this.terminal.print(`⚠️ Loop warning: ${_agentTag} called ${tc.function.name}(${argPreview}) ${entry.count}x — reuse the previous result or change the request`, 'warning');
}
}