@adonis-agora/agent
Version:
Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.
91 lines • 5.19 kB
TypeScript
import type { Actor, MessageUsage, ModelMessage } from '../types.js';
/** What a {@link HistoryWindow} needs to know about the run it's compacting history for. */
export interface HistoryWindowContext {
actor: Actor;
threadId: string;
}
/** How a window split the thread: what rides into the turn, and what the ceiling left out. */
export interface HistorySelection {
/** Sent to the model, oldest-first. */
keep: ModelMessage[];
/** Left out, oldest-first. Folded into a leading summary when the window implements `summarize`. */
drop: ModelMessage[];
}
/** A stand-in for the messages a window left out, plus what producing it cost. */
export interface HistorySummary {
/**
* Prose the loop hands the model in place of the dropped messages — appended to the turn's system
* prompt (see {@link HistoryWindow.renderSummary}), never sent as a message of the conversation.
*/
text: string;
/**
* What the summarizer spent, when it called a model. Recorded as a `summary` usage row, so a
* ceiling on context cost cannot itself become spend nothing accounts for — the quota sums the
* whole ledger without filtering purpose, so this also counts against the daily cap. Omit for a
* summarizer that calls no model (a rollup of tool names, a digest the app already stored).
*/
usage?: MessageUsage;
/** Accounting label for the model that produced it; falls back to `AgentLoopDeps.modelId`. */
modelId?: string;
}
/**
* Compaction seam for long-lived threads. The loop maps the ENTIRE persisted thread into
* `ModelMessage[]` before every turn (see `runAgentLoop` in `agent-loop.ts`); with no window
* configured that whole history rides every turn, so input tokens (cost + latency, and eventually
* the model's context limit) grow without bound as a thread accumulates messages. This package
* ships {@link import('../history-window.js').SlidingWindowHistory}; anything satisfying the seam
* works.
*
* The two halves run in deliberately different places, and that split is what lets a window exist
* without moving a single one of the loop's checkpoint positions — see each method.
*/
export interface HistoryWindow {
/**
* The most messages {@link select} can ever keep, where the ceiling can be stated as a row count.
*
* A hint the loop hands to the store, which then reads that many of the thread's newest rows
* rather than its whole transcript (see {@link import('./agent-store.js').ThreadTurnReader}).
* Declaring it is a PROMISE about `select`: that it keeps at most this many messages, and that
* they are the NEWEST ones — so a window of this size is indistinguishable, to `select`, from the
* full transcript. A window that can keep more than this, or something older than the newest
* `maxMessages`, must omit it rather than select over rows the store was never asked for.
*
* Omit where the ceiling is not a row count at all. Omitting costs only the bound on the READ; the
* prompt is identical either way.
*/
readonly maxMessages?: number;
/**
* Decide what the model sees.
*
* MUST be a pure function of `messages`. The loop calls it OUTSIDE any checkpoint, which is safe
* only because its input already IS one (`load:thread`'s cached result), so a replay re-runs it
* over the same messages and reaches the same split. Read a clock, a feature flag or a database
* here and the resumed run windows differently from the one that suspended: the model gets a
* different prompt, and any step whose existence depends on the split lands at a position the
* history has no room for. Anything non-deterministic belongs in {@link summarize}.
*
* The newest message must always be in `keep` — dropping it leaves the turn with nothing to answer.
*/
select(messages: ModelMessage[], ctx: HistoryWindowContext): HistorySelection;
/**
* Fold the dropped messages into prose the model reads in their place, appended to the turn's
* system prompt (the provider's instructions channel — AI SDK 7 refuses `system` messages in the
* conversation). Optional — without it, dropped messages are simply gone.
*
* Runs inside the loop's `history:summarize` checkpoint, so it may call a model or hit the
* network: the first attempt's result is journaled and every replay reads it back instead of
* re-summarizing (and instead of paying for it twice). It runs once per RUN, not per model step,
* and only when `select` actually dropped something.
*/
summarize?(dropped: ModelMessage[], ctx: HistoryWindowContext): Promise<HistorySummary>;
/**
* How a summary is worded in the system prompt. Undefined → {@link
* import('../history-window.js').renderHistorySummary}, a neutral English block. Override to word
* it in the agent's own language or house style.
*
* MUST be pure: it runs outside any checkpoint, over the journaled summary, so a replay renders
* the same prompt.
*/
renderSummary?(text: string): string;
}
//# sourceMappingURL=history-window.d.ts.map