@benkhz/context-manager 2.0.0 → 2.0.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/README.md +5 -0
- package/package.json +1 -1
- package/src/AIContextManager.js +16 -7
package/README.md
CHANGED
|
@@ -223,6 +223,11 @@ The manager tracks two parallel message lists: the full **history** (everything
|
|
|
223
223
|
received, exposed via `getMessages()`) and the **active window** (`getActiveMessages()`) — the
|
|
224
224
|
slice actually sent to the LLM, which compaction and truncation shrink. History is never pruned.
|
|
225
225
|
|
|
226
|
+
This check runs at the start of every `send()` call, and also between tool-call iterations
|
|
227
|
+
*within* a single turn — a request that triggers several tool calls in a row can grow the active
|
|
228
|
+
window past `contextLimit` well before the turn finishes, so compaction can kick in mid-turn
|
|
229
|
+
rather than waiting for the next `send()`.
|
|
230
|
+
|
|
226
231
|
When the character count of the active window exceeds `contextLimit`:
|
|
227
232
|
|
|
228
233
|
1. `onContextLimit` hook is called — returns `'compact'` (default), `'truncate'`, or `'error'`
|
package/package.json
CHANGED
package/src/AIContextManager.js
CHANGED
|
@@ -73,13 +73,7 @@ export class AIContextManager {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// Auto-compact when approaching the context limit
|
|
76
|
-
|
|
77
|
-
if (chars > this._config.contextLimit) {
|
|
78
|
-
const policy = this._hooks.onContextLimit?.(chars, this._config.contextLimit) ?? 'compact'
|
|
79
|
-
if (policy === 'compact') await this.compact()
|
|
80
|
-
else if (policy === 'truncate') this._truncate()
|
|
81
|
-
else throw new Error(`AIContextManager: context limit exceeded (${chars} chars)`)
|
|
82
|
-
}
|
|
76
|
+
await this._enforceContextLimit()
|
|
83
77
|
|
|
84
78
|
try {
|
|
85
79
|
const assistantMsg = await this._runLoop(opts.system)
|
|
@@ -256,9 +250,24 @@ export class AIContextManager {
|
|
|
256
250
|
this._pushMessage({ role: 'tool', content: JSON.stringify(result), toolCallId: tc.id })
|
|
257
251
|
}
|
|
258
252
|
|
|
253
|
+
// Re-check the context limit between tool-call iterations, not just at the
|
|
254
|
+
// top of send() — a single turn can run several iterations and blow past
|
|
255
|
+
// the limit long before the next send() call ever re-evaluates it.
|
|
256
|
+
await this._enforceContextLimit()
|
|
257
|
+
|
|
259
258
|
return this._runLoop(system, depth + 1)
|
|
260
259
|
}
|
|
261
260
|
|
|
261
|
+
/** Check contextLimit and apply the configured policy (compact/truncate/error). */
|
|
262
|
+
async _enforceContextLimit() {
|
|
263
|
+
const chars = this._charCount()
|
|
264
|
+
if (chars <= this._config.contextLimit) return
|
|
265
|
+
const policy = this._hooks.onContextLimit?.(chars, this._config.contextLimit) ?? 'compact'
|
|
266
|
+
if (policy === 'compact') await this.compact()
|
|
267
|
+
else if (policy === 'truncate') this._truncate()
|
|
268
|
+
else throw new Error(`AIContextManager: context limit exceeded (${chars} chars)`)
|
|
269
|
+
}
|
|
270
|
+
|
|
262
271
|
/** Append a message to both the full history and the active LLM-facing window. */
|
|
263
272
|
_pushMessage(msg) {
|
|
264
273
|
this._history.push(msg)
|