UNPKG

agentfootprint

Version:

The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.

95 lines • 4.32 kB
"use strict"; /** * tokenBudget — compaction's trigger discipline, without the summarizer. * * 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 other familiar policy: cap the window at a token budget * (Mastra's `TokenLimiter` is the closest sibling). What is different here is * the same thing that is different about compaction — the number is COUNTED, * never guessed: * * • the trigger reads the input tokens the PROVIDER reported for the last * call, not a character estimate and not a divide-by-four heuristic; * • a provider that reports no usage gets `CompactionUnmeasurableError` by * name, the same refusal `.compaction()` makes, rather than an invented * window size or a configured budget that silently never applies. * * When over budget it drops the oldest contiguous removable span — the same * span compaction would have folded, chosen by the same refusal engine — and * writes no summary at all. Nothing is claimed about what left, because * nothing was read: the record and the eviction events name it, and the * commit log still has it verbatim. * * Use it over `summarizeOldest` when you would rather lose the old turns than * pay a summarizer to paraphrase them, and over `slidingWindow` when the * thing you are actually defending is a token bill rather than a turn depth. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.tokenBudget = exports.TOKEN_BUDGET = void 0; const drop_js_1 = require("./drop.js"); const errors_js_1 = require("../errors.js"); const options_js_1 = require("../options.js"); /** `WindowRecord.strategy` written by every record this strategy files. */ exports.TOKEN_BUDGET = 'token-budget'; /** * Drop the oldest turns whenever the last call's adapter-reported input * tokens exceed `thresholdTokens`. * * @example * ```ts * import { Agent, tokenBudget } from 'agentfootprint'; * * const agent = Agent.create({ provider: anthropic(), model: 'claude-sonnet-4-5' }) * .window(tokenBudget({ thresholdTokens: 120_000 })) * .build(); * ``` */ function tokenBudget(options) { const config = (0, options_js_1.resolveTokenBudgetOptions)(options, 'tokenBudget'); return { name: exports.TOKEN_BUDGET, async plan(input) { const { measured } = input; // Iteration 1: nothing has been sent, so nothing has been counted. if (measured === undefined) return undefined; // Counted, not guessed — the same wall compaction hits, by the same name. if (measured.input === 0 && measured.output === 0) { throw new errors_js_1.CompactionUnmeasurableError(input.providerName); } if (measured.input <= config.thresholdTokens) return undefined; const outcome = (0, drop_js_1.dropOldestSpan)(input, config.keepRecentTurns, exports.TOKEN_BUDGET); const record = { strategy: exports.TOKEN_BUDGET, iteration: input.iteration, measuredTokens: measured.input, thresholdTokens: config.thresholdTokens, overBudget: true, keepRecentTurns: config.keepRecentTurns, removedStageIds: outcome.removedStageIds, removedMessageCount: outcome.removedMessageCount, windowCharsBefore: outcome.windowCharsBefore, windowCharsAfter: outcome.windowCharsAfter, refusals: outcome.refusals, }; return { ...(outcome.window !== undefined && { window: outcome.window }), ...(outcome.rebase !== undefined && { rebase: outcome.rebase }), record, evictions: outcome.evictions, budgetPressure: { capTokens: config.thresholdTokens, projectedTokens: measured.input, planAction: outcome.removedMessageCount > 0 ? 'evict' : 'none', }, }; }, }; } exports.tokenBudget = tokenBudget; //# sourceMappingURL=tokenBudget.js.map