agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
83 lines • 3.95 kB
JavaScript
/**
* slidingWindow — keep the last N turns, drop what is older.
*
* Pattern: WindowStrategy implementation. Its own module, registering
* nothing at import.
* Role: core/ layer.
* Emits: N/A — the stage emits, records and costs; this file decides.
*
* The market's simplest window policy (LangChain's `trim_messages`, the
* "last N messages" every framework ships). Two things are different here:
*
* 1. **It refuses by name.** It goes through the SAME turn segmentation and
* the SAME refusal engine as compaction, so it will not drop half of a
* `tool_use` / `tool_result` pair, will not drop an unanswered tool call
* whose answer may still arrive, will not drop the tool a paused run is
* waiting on or a pending check-in, and cannot touch the system
* envelope. Message-counting trimmers split those pairs and the vendor
* rejects the request; this one steps over them and says so in the
* record.
* 2. **It says what it dropped.** Every dropped message is an eviction
* event with its measured lifetime, and the record names the stage ids
* the messages came from. The turns themselves stay in the commit log,
* byte for byte.
*
* It triggers on turn COUNT, not tokens — so it needs no summarizer, makes no
* LLM call, and runs on ANY provider, including the ones that report no usage
* at all. Nothing here is unmeasurable, so nothing here throws.
*/
import { dropOldestSpan } from './drop.js';
import { resolveSlidingWindowOptions } from '../options.js';
/** `WindowRecord.strategy` written by every record this strategy files. */
export const SLIDING_WINDOW = 'sliding-window';
/**
* Keep the most recent `keepRecentTurns` turns in the live window and drop
* the older ones — except anything that refuses.
*
* @example
* ```ts
* import { Agent, slidingWindow } from 'agentfootprint';
* import { ollama } from 'agentfootprint/providers';
*
* // Works on a provider that reports no usage at all: the trigger is turns.
* const agent = Agent.create({ provider: ollama('llama3.2'), model: 'llama3.2' })
* .window(slidingWindow({ keepRecentTurns: 12 }))
* .build();
* ```
*/
export function slidingWindow(options) {
const config = resolveSlidingWindowOptions(options, 'slidingWindow');
return {
name: SLIDING_WINDOW,
async plan(input) {
// Did not engage: the window is already within its target depth. No
// record — the ledger tracks what this strategy DID, not every time it
// looked and there was nothing to do.
if (input.turns.length <= config.keepRecentTurns)
return undefined;
const outcome = dropOldestSpan(input, config.keepRecentTurns, SLIDING_WINDOW);
const record = {
strategy: SLIDING_WINDOW,
iteration: input.iteration,
keepRecentTurns: config.keepRecentTurns,
removedStageIds: outcome.removedStageIds,
removedMessageCount: outcome.removedMessageCount,
windowCharsBefore: outcome.windowCharsBefore,
windowCharsAfter: outcome.windowCharsAfter,
turnsBefore: outcome.turnsBefore,
turnsAfter: outcome.turnsAfter,
refusals: outcome.refusals,
};
// No `budgetPressure`: this strategy has no token budget. Reporting a
// cap nobody configured would be the invented number the family exists
// to refuse — so it reports evictions and stays quiet about budgets.
return {
...(outcome.window !== undefined && { window: outcome.window }),
...(outcome.rebase !== undefined && { rebase: outcome.rebase }),
record,
evictions: outcome.evictions,
};
},
};
}
//# sourceMappingURL=slidingWindow.js.map