agents
Version:
A home for your AI agents
461 lines (459 loc) • 17.4 kB
TypeScript
import {
a as RecentHistoryResult,
c as SessionChangeListener,
d as SessionRowStat,
f as SessionsOptions,
i as HistoryReadOptions,
l as SessionMessage,
m as WriteOptions,
n as AppendResult,
o as SearchResult,
p as StoredCompaction,
r as HistoryBatchReadOptions,
s as SessionChangeEvent,
t as AppendOptions
} from "./types-B7LojTe4.js";
//#region src/sessions/compaction-helpers.d.ts
/** Prefix for all compaction messages (overlays and summaries) */
declare const COMPACTION_PREFIX = "compaction_";
/** Check if a message is a compaction message */
declare function isCompactionMessage(msg: SessionMessage): boolean;
/**
* Result of a compaction function — describes the overlay to store.
*/
interface CompactResult {
/** First message ID in the compacted range */
fromMessageId: string;
/** Last message ID in the compacted range */
toMessageId: string;
/** Summary text to store as the overlay */
summary: string;
}
interface CompactOptions {
/** Calls the model to summarize; receives a prompt, returns its text. */
summarize: (prompt: string) => Promise<string>;
/**
* Token budget for the recent tail kept verbatim. Older messages above the
* protected head are summarized into one overlay. Default 20,000.
*/
keepRecentTokens?: number;
}
/**
* Reference compaction implementation.
*
* Implements the full hermes-style compaction algorithm:
* 1. Protect head messages (first N)
* 2. Protect tail by token budget (walk backward)
* 3. Align boundaries to tool call groups
* 4. Summarize middle section with LLM (structured format)
* 5. Iterative summary updates on subsequent compactions
*
* @example
* ```typescript
* import { createCompactFunction } from "agents/sessions";
*
* sessions
* .session()
* .onCompaction(
* createCompactFunction({
* summarize: (prompt) => generateText({ model, prompt }).then((r) => r.text)
* })
* )
* .compactAfter(100_000);
* ```
*/
declare function createCompactFunction(
opts: CompactOptions
): (messages: SessionMessage[]) => Promise<CompactResult | null>;
//#endregion
//#region src/sessions/io.d.ts
/** @internal SQLite and telemetry supplied by the Sessions capability. */
type SqlParam = ArrayBuffer | string | number | null;
interface SessionsIo {
/** Run a read and return every row. */
sql<T>(query: string, params: SqlParam[]): T[];
/** Run a write (or DDL) and return the rows it wrote. */
sqlWrite(query: string, params: SqlParam[]): number;
/** One synchronous SQLite transaction. */
transaction<T>(fn: () => T): T;
/** Capability telemetry event. */
emit(type: string, payload: Record<string, unknown>): void;
}
//#endregion
//#region src/sessions/core.d.ts
type UpdateOutcome = "missing" | "unchanged" | "updated";
declare class SessionsCore {
#private;
readonly io: SessionsIo;
constructor(options: SessionsOptions, io: SessionsIo);
ensureTables(): void;
/**
* Lift the legacy `assistant_*` message and compaction tables.
*
* The copy is pure SQL, so SQLite streams it rather than materializing rows
* in the isolate. Each source is then verified row by row against its
* destination and DROPPED: keeping tombstones would leave every upgraded
* object holding its history twice inside the same 10 GB. A table whose
* verification fails is left in place with a `session:migration:incomplete`
* event, and the method returns false so the caller leaves the schema
* version unstamped and retries on a later start. `assistant_config`
* belongs to Think, which lifts and drops it itself.
*/
migrateLegacy(): boolean;
subscribe(listener: SessionChangeListener): () => void;
/**
* Dispatch after a durable write. A listener that throws must not turn a
* committed write into a rejected call, so failures are reported through
* telemetry and dispatch continues.
*/
notify(event: SessionChangeEvent): Promise<void>;
exists(sessionId: string, id: string): boolean;
getMessage(sessionId: string, id: string): SessionMessage | null;
/**
* Drop the in-memory tail and token-total caches for a session. For a
* caller whose enclosing transaction rolled back after an append or update
* ran inside it: the rows are gone but the caches already moved. The next
* read re-derives both from storage.
*/
forgetCaches(sessionId: string): void;
latestLeafId(sessionId: string): string | null;
getLatestLeaf(sessionId: string): SessionMessage | null;
getBranches(sessionId: string, messageId: string): SessionMessage[];
/**
* The active branch path as content-free rows, root → leaf. Recurses over
* (id, parent_id) only — carrying content through the recursive queue
* materializes the transcript several times inside SQLite (#1710).
* `bytes` counts the message row, its continuation rows, AND the payloads
* it points at, charged at the size they take once inlined, so a byte
* budget over these rows bounds real hydrated memory.
*/
pathRowStats(sessionId: string, leafId?: string | null): SessionRowStat[];
/**
* Stream the path ending at `leafId` (default: active leaf), root → leaf
* (or leaf → root with `newestFirst`), compaction overlays collapsed. Peak
* memory is one bounded content window — never the whole transcript.
*/
streamHistory(
sessionId: string,
options: HistoryReadOptions
): AsyncGenerator<SessionMessage, void, undefined>;
getHistory(
sessionId: string,
options: HistoryReadOptions
): Promise<SessionMessage[]>;
/**
* Byte-budgeted read of the most recent messages on the active branch
* path — the longest suffix whose stored size fits `maxContentBytes`.
*
* There is no message-count floor: one used to exist, and it admitted
* rows regardless of size, so a window of media-heavy messages could
* hydrate far past the limit meant to bound it. The newest message is
* always returned even if it alone exceeds the budget, since returning
* nothing is worse. Overlays whose anchors fall outside the window are
* skipped, showing the raw recent messages.
*/
getRecentHistory(
sessionId: string,
maxContentBytes: number,
leafId?: string | null
): Promise<RecentHistoryResult>;
/**
* Heuristic token estimate for the active path with compaction overlays
* applied: stamped per-row estimates, minus compacted spans, plus their
* summaries. Derived from content-free rows on each call; it gates cheap
* triggers only, and model-reported usage stays authoritative.
*/
tokenEstimate(sessionId: string): number;
stripReservedMetadata(message: SessionMessage): SessionMessage;
/**
* Stamped row estimate: the part heuristic over the message as written,
* plus a weight per inline file payload so media never counts as zero.
*/
estimateRowTokens(message: SessionMessage): number;
/**
* Durable append. The caller has already sanitized the message. Message,
* continuation, attachment, and FTS rows commit in one synchronous SQLite
* transaction. Returns the stored message: the input itself when it was
* inserted (extraction is lossless, so the two are identical), or the row
* already holding the id when it was not.
*/
append(
sessionId: string,
message: SessionMessage,
parentId: string | null | undefined,
tokenEstimate: number
): {
inserted: boolean;
message: SessionMessage;
};
/**
* Durable update of an existing row. An identical row writes nothing: no
* row, no continuation, no FTS, no event. The no-op guard compares the
* FULL content, through the digest the last write stamped, so neither the
* payload nor a continuation row is read. A row without a digest compares
* the reassembled content once and is stamped.
*/
update(
sessionId: string,
message: SessionMessage,
tokenEstimate: number
): UpdateOutcome;
/**
* Delete rows, SPLICING children to their grandparent so a mid-chain
* delete never decapitates older history. Only surviving boundary children
* are rewired: a prefix delete writes one boundary child, not one child
* per deleted message.
*/
deleteMessages(sessionId: string, messageIds: string[]): void;
clearMessages(sessionId: string): void;
addCompaction(
sessionId: string,
summary: string,
fromMessageId: string,
toMessageId: string
): StoredCompaction;
getCompactions(sessionId: string): StoredCompaction[];
search(sessionId: string, query: string, limit: number): SearchResult[];
/**
* Import one historical message verbatim (migrations, cross-DO moves):
* explicit parent and timestamp, stamped estimate, no change-feed events.
*/
/** Returns `false` when the id already exists and nothing was written. */
importMessage(
sessionId: string,
message: SessionMessage,
options: {
parentId: string | null;
createdAt: number;
}
): boolean;
}
//#endregion
//#region src/sessions/mirror.d.ts
/**
* How a host keeps an in-memory transcript in step with one session's change
* feed. The mirror applies the reduction every such cache needs; the hooks
* carry the decisions only the host can make.
*/
interface SessionMirrorOptions<
M extends {
id: string;
}
> {
/**
* The cached transcript. Read on every event, so a host that reassigns its
* array (a public `messages` field, say) is followed rather than
* shadowed.
*/
get(): M[];
/** Install a new array: after a delete, or emptied by a clear. */
set(messages: M[]): void;
/** The cached form of a stored message. Defaults to the stored message. */
transform?(message: SessionMessage): M;
/**
* Handle an event instead of the default reduction, returning `true` when
* it did. A host whose cache cannot express a change in place — a branch
* append, a compaction — re-reads storage here.
*/
intercept?(event: SessionChangeEvent): boolean | Promise<boolean>;
/**
* After the default reduction wrote `message` for an append or update.
* `previous` is the cached entry it replaced, if there was one.
*/
onApplied?(
event: Extract<
SessionChangeEvent,
{
type: "append" | "update";
}
>,
message: M,
previous: M | undefined
): void;
}
//#endregion
//#region src/sessions/handle.d.ts
/** Summarizes a branch into an overlay. */
type CompactionFunction = (
messages: SessionMessage[]
) => Promise<CompactResult | null>;
declare class Session {
#private;
readonly sessionId: string;
/** @internal Constructed by the Sessions capability only. */
constructor(
sessionId: string,
core: SessionsCore,
ready: () => Promise<void>
);
/** Register the function `compact()` calls to summarize a branch. */
onCompaction(fn: CompactionFunction): this;
/**
* Auto-compact after an append once the estimated token count crosses the
* threshold. Requires `onCompaction()`. The estimate is derived from the
* stamped per-row estimates, never from the transcript.
*/
compactAfter(tokenThreshold: number): this;
/**
* Keep an in-memory transcript in step with this session's change feed,
* so write sites never hand-patch it. Returns the unsubscribe function.
*/
mirror<
M extends {
id: string;
}
>(options: SessionMirrorOptions<M>): () => void;
/**
* Stream the active branch path root → leaf (leaf → root with
* `newestFirst`) with compaction overlays applied. Peak memory is one
* bounded content window, never the whole transcript, and a consumer that
* breaks out early leaves the rows it never reached unread.
*/
history(
options?: HistoryReadOptions
): AsyncGenerator<SessionMessage, void, undefined>;
/**
* Stream history in bounded non-empty batches. Both message count and
* serialized bytes bound each batch; a single large message is yielded by
* itself.
*/
historyBatches(
options?: HistoryBatchReadOptions
): AsyncGenerator<SessionMessage[], void, undefined>;
/**
* Materialize the whole selected path. Prefer `history()` or
* `getRecentHistory()` inside a Durable Object: this holds every message
* of the branch in memory at once.
*/
getHistory(options?: HistoryReadOptions): Promise<SessionMessage[]>;
/**
* Byte-budgeted read of the most recent messages on the active branch path
* (always at least the leaf). The budget counts each row, its continuation
* rows, and the payloads it points at, so it bounds hydrated memory (#1710).
*/
getRecentHistory(
maxContentBytes: number,
options?: Pick<HistoryReadOptions, "leafId">
): Promise<RecentHistoryResult>;
/**
* Per-row stored sizes (row plus continuation rows and attachments) and
* stamped token estimates for the active branch path (root → leaf)
* WITHOUT loading message content.
*/
getHistoryRowStats(leafId?: string | null): Promise<SessionRowStat[]>;
getMessage(id: string): Promise<SessionMessage | null>;
getLatestLeaf(): Promise<SessionMessage | null>;
getBranches(messageId: string): Promise<SessionMessage[]>;
/**
* Full-text search over this session's text parts. The index is built on
* the first call and maintained from then on.
*/
search(
query: string,
options?: {
limit?: number;
}
): Promise<SearchResult[]>;
/**
* Append one message. Idempotent on id: a repeated append returns the row
* already stored and dispatches an `append` event with `inserted: false`.
*
* A compaction overlay (a `compaction_` id) is derived on read and is never
* stored. Clients post their transcript back after every compaction, so
* this and every other write drops one silently: nothing is written and no
* event is dispatched.
*/
appendMessage(
message: SessionMessage,
options?: AppendOptions
): Promise<AppendResult>;
/**
* Update one stored row. Returns the stored form, or `null` when the id is
* not in this session. An unchanged message writes nothing and dispatches
* no event. A compaction overlay is never a stored row, so it reports
* `null`.
*/
updateMessage(
message: SessionMessage,
options?: WriteOptions
): Promise<SessionMessage | null>;
/**
* @internal Synchronous write aperture for same-isolate first-party
* machinery that must commit a message inside a caller-owned SQLite
* transaction — the stream cutover, where the finished message and the
* discard of its stream rows land together. Bypasses readiness (the
* caller owns startup ordering) and defers everything that follows a
* write — the change-feed dispatch and auto-compaction: call the returned
* `after()` once the transaction commits, or subscribers (the host's
* message mirror) never hear about the write. If the transaction rolls
* back after an `upsert` ran inside it, call `abandon()`: the write is
* gone but the in-memory tail and token-total caches already moved.
* Will break without notice; never use from application code.
*/
__DO_NOT_USE_WILL_BREAK__sync(): {
upsert(
message: SessionMessage,
options?: AppendOptions
): {
result: AppendResult;
after: () => Promise<void>;
};
abandon(): void;
};
upsertMessage(
message: SessionMessage,
options?: AppendOptions
): Promise<AppendResult>;
/**
* Import one historical message verbatim (migrations, cross-object moves):
* explicit parent and timestamp. A row actually written dispatches an
* `import` change event so a host cache can mark itself stale; it is not
* an `append`, so a cache does not patch itself per imported row, and an
* id that already exists writes nothing and dispatches nothing.
*
* Unlike the other writes, a `compaction_` id is imported as given: a
* summary copied from another session's `history()` is the only record of
* the context it replaced, and later rows may be parented to it. History
* reads hide such a row only when it duplicates one of this session's own
* compaction overlays.
*/
importMessage(
message: SessionMessage,
options: {
parentId: string | null;
createdAt: number;
}
): Promise<void>;
deleteMessages(messageIds: string[]): Promise<void>;
clearMessages(): Promise<void>;
/**
* Store an overlay directly. Dispatches a `compaction` change event: the
* rows are untouched, but what a path read returns has changed.
*/
addCompaction(
summary: string,
fromMessageId: string,
toMessageId: string
): Promise<StoredCompaction>;
getCompactions(): Promise<StoredCompaction[]>;
/**
* Run the registered compaction function and store the result as an
* overlay. When `leafId` is given, compact that root-to-leaf branch instead
* of the active branch. Requires `onCompaction()`. A compaction function
* that throws is reported through the `session:error` capability event and
* the call returns `null`.
*/
compact(leafId?: string | null): Promise<CompactResult | null>;
}
//#endregion
export {
CompactOptions as a,
isCompactionMessage as c,
COMPACTION_PREFIX as i,
Session as n,
CompactResult as o,
SessionMirrorOptions as r,
createCompactFunction as s,
CompactionFunction as t
};
//# sourceMappingURL=handle-zAzh0UW2.d.ts.map