PUBLIC MODULE · lib/context.js

Agent.Context

lib/context.js — Context: the shared conversation data model.

PUBLIC MODULE (owner of lib/context/). At the bottom of the dependency chain, it imports no other public module; Env, IO, Agent, CLI, and TUI depend on it, never the reverse. Private helpers live in lib/context/; consumers use this façade's named exports or the Context class (instances are stateful contexts; statics are the data model).

class Context class

The conversation context: an ordered message list (a private array — read it through length/at/messages()/iteration, change it through the methods below), optionally NAMED (id/name) and LOGGED to a session file in dir (save). Crash safety is the owner's: an Agent flushes its context from a process-finish hook.

Defined in lib/context/store.js

append(message, options) method

Append a message, MERGING with the last one when possible (consecutive same-type messages / same-sub-type blocks fold — Context.appendMessage); {merge: false} keeps a boundary.

message object

Returns object|undefined — the stored message, or `undefined` when an empty message is skipped

at(i) method

The message at index i (negative counts from the end).

i number

Returns object|undefined

blockAt(i, j) method

Block j of message i (RangeError when either index is out of range).

i number
j number

Returns object

close() method

Flush and close; subsequent mutations fail. Flush errors propagate and leave the context open.

Returns Promise<void> — Resolves once durable; on failure reopens for retry.

constructor(options = {…}) constructor

Create a context, optionally named and logged (the file is created on the first flush of a started conversation).

[options] Object
[options.id] string|false
session id (default: random UUID); false, "0", "false", "anon" normalize to an anonymous context with no id or logging
[options.dir] string
the sessions folder (env.settings.sessions); required to log
[options.messages] Array
initial messages, deep-copied into this context; no objects are shared with another context
[options.origin] string
the folder the session RUNS in (default: process.cwd()) — recorded in the file's metadata line; resume's scans list only sessions of the current folder
[options.uuid] string
carry an existing sessionUUID forward (Context.resume() only — a fresh session always gets a new one)
[options.name] string
carry an existing stored NAME forward (Context.resume() only — see the module doc)
[options.settings] object
the session-owned agent settings snapshot (Agent-managed; see the settings setter)
[options.save] boolean
whether the context is logged to disk (default: whenever a `dir` is given); false or an anonymous id keeps it in memory

Throws — {TypeError} Invalid options container or unknown own top-level key (including symbols) before reading metadata or creating a folder; stored settings remain unrestricted.

contextUsage property

Read: get contextUsage()

Provider-reported context consumption and model window (tokens). A stable immutable snapshot until the next accepted report or invalidation; identity changes even when a report repeats the same numbers. Values survive replacing IO and are not persisted.

Returns {used: number|undefined, total: number|undefined} — The current Context's report.

Effects — None.

Write: set contextUsage({ used, total } = {…})

Merge a provider report into this conversation. Nonnegative finite used and positive finite total values are floored; invalid values do not replace previous data.

Returns void — No result.

Effects — Updates in-memory Context usage; reports survive changing transport IO.

contextUsageInvalidate({ window = false } = {…}) method

Invalidate exact input consumption after structural changes (e.g. compaction). On a different model identity, also discard the old model's window; an IO replacement for the same model does not require invalidation.

Returns void — No result.

Effects — Clears in-memory `used`, and optionally `total`; does not affect quota or persisted messages.

created property

Read: get created()

Creation timestamp in ISO form.

Returns string — Creation timestamp in ISO form.

static deleteAll({ dir } = {…}) method

Delete EVERY session file in the folder (the /sessions-delete-all! contract — the user confirmed; foreign files stay untouched).

[options] Object
[options.dir] string

Returns {deleted: number}

static deleteById({ id, dir } = {…}) method

Permanently delete a STORED session by id: every session file whose metadata carries that id (a leftover duplicate goes with it). A store still holding it open would re-persist it — close it first.

options Object
options.id string
[options.dir] string

Returns {deleted: number}

dir property

Read: get dir()

Session logging directory.

Returns string|undefined — Session logging directory.

dirty property

Read: get dirty()

Where the context changed since its entries were last accepted: false when nothing did, else the index of the first changed entry (an edit, insertion, removal or reorder there; may be 0). Every append accepts the current entries again, except a changed message that still carries replay data (provider fields on text, thinking or tool-call blocks, such as a thinking signature): that one stays dirty until the replay data is removed, so a provider sees an edit of history it already answered even when new messages followed. Removing trailing entries is not a change.

Returns false|number

edit(i, message) method

Replace message i (rebuilt from recognized fields; stale provider identifiers dropped).

i number
message object

Returns object — the stored message

editBlock(i, j, block) method

Replace block j of message i (the message is rebuilt).

i number
j number
block object

Returns object — the stored block

errorPop() method

Retract a trailing FAILED RESPONSE before the context is submitted again: an assistant message carrying error that is still the last message was not responded to — the user continued as it stands — so it goes and the request is re-attempted. Anything added after it (a user reply) keeps it: then it is part of the conversation.

Returns object|undefined — the removed message

file property

Read: get file()

Backing session file, when a logging directory exists.

Returns string|undefined — Backing session file, when a logging directory exists.

static fileOf({ id, dir } = {…}) method

The session file holding id in dir (a metadata-line scan), or undefined.

Returns string|undefined

flush() method

Synchronously make the CURRENT context durable (choosing remove, none, append or full rewrite). Idempotent; a no-op when nothing changed since the last flush. Filesystem errors propagate.

Returns void

flushAsync() method

Async counterpart for a live loop (Agent's request/tool turns); the synchronous flush() remains the crash/exit contract. "remove"/"append" plans are tiny and interleave-free, so they commit synchronously; only a "full" rewrite yields, snapshotting one version so a mutation while I/O is pending remains dirty for the following flush rather than being lost. I/O errors reject the returned promise.

Returns Promise<void>

id property

Read: get id()

Canonical identifier; undefined for anonymous memory-only contexts. Use rename() to name one.

Returns string|undefined — Canonical identifier; undefined for anonymous memory-only contexts. Use rename() to name one.

static latest({ dir, cwd } = {…}) method

The id of the most recently modified session in the folder (of the cwd origin when given), or undefined when the folder has none.

[options] Object
[options.dir] string
[options.cwd] string
only sessions of THIS origin folder

Returns string|undefined

length property

Read: get length()

the message count

Returns number — the message count

static list({ dir, cwd, limit = 50 } = {…}) method

Every session in the folder, LATEST FIRST (one entry per id), each with a small preview: the first meaningful line of the first user message (see meaningfulLine; whitespace-folded, capped) and agent, the stored agent name when it is not a default agent-N one. With cwd, only sessions whose metadata records THAT origin folder list (the resume contract: a project sees its own sessions; foreign metadata-less files are skipped by the FIRST-LINE scan, never parsed in full). Previews are read only for the newest limit files (stats are cheap; parsing is not). Unreadable files list without a preview, never an error.

[options] Object
[options.dir] string
[options.cwd] string
only sessions of THIS origin folder
[options.limit] number
max sessions returned (default 50)

Returns Array<{id: string, file: string, mtime: number, messages: number, preview: string, agent?: string}>

static listAsync({ dir, cwd, limit = 50 } = {…}) method

Nonblocking counterpart of list(). It deliberately has its own async folder identity rather than calling sameFolder(), whose realpathSync would stall the interactive loop.

Returns Promise<Array<{id: string, file: string, mtime: number, messages: number, preview: string, agent?: string}>>

messages() method

A new array of LIVE message objects. Mutating a message changes this context only, without marking it dirty; use edit/update for persistence bookkeeping. Construction deep-copies seeds, so forks never share message objects.

Returns object[] — A new array of LIVE message objects. Mutating a message changes this context only, without marking it dirty; use edit/update for persistence bookkeeping. Construction deep-copies seeds, so forks never share message objects.

name field

Stored or automatically derived session label; remains writable.

Returns string|undefined — Stored or automatically derived session label; remains writable.

origin property

Read: get origin()

Origin project folder recorded in metadata.

Returns string — Origin project folder recorded in metadata.

static originOf({ id, dir } = {…}) method

The ORIGIN FOLDER recorded in a session file's metadata line (the cwd the session ran in), or undefined (no such session). A session resumes only in an Env of this folder; an explicit --resume <id> makes it the process cwd before the Env is built.

options Object
options.id string
[options.dir] string

Returns string|undefined

planUsage property

Read: get planUsage()

Last complete provider quota/metadata deep snapshot. Replaces reports; never persisted.

Write: set planUsage(report = {…})

Replace a provider report with an independent bounded canonical snapshot. Canonical quota fields outrank count/percentage and snake-case aliases; windowSeconds is a duration, never a reset. Known Anthropic unified header details become window/account state; unconsumed details remain available.

[report={}] object
Complete plan report; neither mutated nor retained.

Returns void — No result. Idempotent; no failures for JSON data.

Effects — Replaces in-memory quotas/metadata only; never persisted or added to Agent lifetime totals.

pop() method

the removed last message

Returns object|undefined — the removed last message

prepend(messages) method

Insert messages at the FRONT — e.g. an Agent's seeded system prompt, always the first message(s) of a fresh context.

messages object[]

remove(indexes) method

Remove the messages at indexes.

indexes number[]

Returns object[] — the removed messages

rename(newId) method

Rename the session: BOTH the stable id and the file's NAME segment become newId (the same session — the date/uuid8 prefix carries over unchanged) and the old file is gone. Refuses to clobber an EXISTING other session (a directory scan by id, the file name no longer being a direct function of it).

newId string

Returns {id: string, file: string}

static renameById({ id, name, dir } = {…}) method

Rename a STORED session by id (the sidebar/menu path for a session no store holds open — a live one renames through its store): loads it, renames it (see rename()) and closes it again.

options Object
options.id string
options.name string
the new id/name
[options.dir] string

Returns {id: string, file: string}

Throws — {Error} when no such session exists or the name is taken

static resume({ id, dir, save = true } = {…}) method

Load a session file into a fresh store holding its live context. The file is found by a directory scan matching id against each file's metadata line (the file name is a presentation detail, not a direct function of id — see the module doc). Non-message records in the file are quietly ignored (tolerant reader — see loadMessages).

options Object
options.id string
[options.dir] string
[options.save=true] boolean
whether the resumed context keeps logging

Returns Context — the loaded context

rollback(i) method

Remove every message at index >= i (RangeError unless i is an existing index).

i number

Returns object[] — the removed messages

save property

Read: get save()

whether this context is logged to disk

Returns boolean — whether this context is logged to disk

Write: set save(value)

Enable or disable persistence without replacing the live context. Enabling saving gives an anonymous context a fresh id and makes the complete current context eligible for the next flush.

value boolean

Returns void — Read save after assignment for the logging state.

settings property

Read: get settings()

the owner's settings snapshot riding the session file (undefined: none recorded — resume keeps the caller's configuration)

Returns object|undefined — the owner's settings snapshot riding the session file (undefined: none recorded — resume keeps the caller's configuration)

Write: set settings(value)

Record the owner's SETTINGS snapshot (an Agent's (safe, thinking, endpoint/model, name, … — Agent writes it; the store only persists it; a default agent-N name never rides the file). Only JSON-typed values ride the file; undefined entries are dropped. Marks the store dirty (the next flush is a FULL rewrite: the metadata line changes).

value object

summary property

Read: get summary()

one line for status/command output: <id> — <file> while logging, <id> — not logged (memory only) otherwise

Returns string — one line for status/command output: `<id> — <file>` while logging, `<id> — not logged (memory only)` otherwise

toJSON() method

JSON form: the message array.

update(fn) method

A batch in-place change (a repair pass): fn receives the live message array and returns true when it changed anything — the context then logs a full rewrite. Stored messages are replaced, never mutated.

fn (messages: object[]) => boolean

Returns boolean — whether anything changed

uuid property

Read: get uuid()

Persistent session UUID.

Returns string — Persistent session UUID.

ContentType = Object.freeze({…}) constant

Content block discriminators.

Defined in lib/context/types.js

EventType = Object.freeze({…}) constant

The full normalized event vocabulary.

Defined in lib/context/events.js

FALLBACK_CONTEXT_WINDOWS = Object.freeze({…}) constant

Curated context windows for the CURRENT OpenAI tier, from the models docs (developers.openai.com/api/docs/models, reviewed 2026-09-28 — the gpt-6 family lists 1.05M; o-series 200K). OpenAI's API-key surface publishes NO window anywhere (/models, /models/{id}, and the response envelope are all bare — probed with ai-tmp/probe-openai-models.js), so the status gauge reads this table while a lazy registry lookup (lib/env/model-windows.js) fills the endpoint's model cache. Any provider-published or looked-up value outranks it. Review when a new tier ships.

Defined in lib/context/usage.js

MIME_BY_EXTENSION = Object.freeze({…}) constant

Context-owned media-type map and byte detection for content blocks.

Defined in lib/context/mime.js

MessageType = Object.freeze({…}) constant

Numeric message types. 0 is reserved.

Defined in lib/context/types.js

TOKENS_PER_WORD = 4 / 3 constant

tokens ≈ words × 4/3 (token-per-word likelihood ratio)

Defined in lib/context/usage.js

assemblyCallbacks(assembler) function

Build the camelCase callback set used by Context to feed response events to an assembler. Includes callbacks for start, text/thinking block events, tool-call events, done, and error.

assembler ReturnType<typeof createAssembler>
Assembler receiving the events; no default is provided.

Returns Object<string, function(object): void> — Callbacks keyed by names such as `onStart`, `onTextDelta`, `onDone`, and `onError`.

Effects — Creates a new callback object; each callback delegates to assembler.consume and therefore mutates that assembler.

Throws — Throws if assembler is missing or does not provide consume, or if event processing throws when a callback is invoked.

Defined in lib/context/assemble.js

createAssembler() function

Create an assembler that folds normalized response events into one assistant message. Events mutate the in-progress message; valid message snapshots on start, done, or error replace it. Unknown event types are ignored, and malformed tool-call JSON is retained as raw text. The returned message remains partial until completion and is exposed by reference.

Returns {consume: (event: object) => void, message: () => object} — The event consumer and accessor for the current message.

Throws — Creating the assembler does not intentionally throw. Its returned `consume` method can propagate errors from malformed events or message mutations; JSON parse errors for tool arguments are suppressed.

Defined in lib/context/assemble.js

normalizeCallbacks(callbacks = {…}, binding = {…}) function

Build the full callback set for a binding.

[callbacks={}] Object
consumer-supplied callbacks, keyed by camelCase callback name. A function is used as-is; explicit `false` or `null` selects a no-op; an omitted or `undefined` callback gets the binding default.
[binding={}] Object
binding-appropriate default handlers.
[binding.onData] (event:object)=>void
receives every event whose callback was omitted (stdout replacement)
[binding.onLog] (line:string)=>void
receives error reports (stderr replacement)
[binding.onTerminal] (event:object)=>void|false|null
completes the binding; called for `done` and `error` even when the consumer supplied its own terminal callback. `false` or `null` disables this additional call.

Returns Object — Complete callback set keyed by camelCase names.

Throws — {Error} If an invoked binding handler throws; its error propagates to the caller.

Defined in lib/context/events.js

binaryContent(path, buffer) function

Build a canonical base64 binary Context block without exposing the source path.

path string
Non-empty local path; only its basename is retained.
buffer Uint8Array
Binary data to encode.

Returns {type: "binary", mimetype: string, filename: string, content: string} — A block containing the basename, detected media type, and base64 data.

Throws — {TypeError} If path is not a non-empty string or buffer is not a Uint8Array.

Defined in lib/context/mime.js

contentIndexer() function

Create a per-request content-block indexer for a provider translator. of reuses an index for a previously seen wire-item key, while next always reserves a fresh index; indexes are dense and follow first appearance.

Returns {of: (key: *) => number, next: () => number} — Indexing operations.

Defined in lib/context/assemble.js

textContent(text) function

Wrap a value's string representation in a text content block.

text *
Value converted with `String()`.

Returns TextContent — A block with `type: ContentType.Text` and the converted text.

Throws — {*} Propagates a thrown value encountered during string conversion.

Defined in lib/context/types.js

callbackName(eventName) function

Map an event name to its camelCase callback name. "text_delta" -> "onTextDelta"; "tool_call_start" -> "onToolCallStart".

eventName string
normalized event name to convert.

Returns string — The corresponding camelCase callback name.

Throws — {TypeError} If `eventName` does not support `split` (for example, it is not a string).

Defined in lib/context/events.js

dispatch(set, event) function

Dispatch one event through a normalized callback set. Validates the event before invoking its matching callback; callback errors propagate to the caller.

set Object
callback set returned by `normalizeCallbacks`.
event object
normalized response event to dispatch.

Returns void

Throws — {TypeError} If `event` is invalid or the callback cannot be invoked; errors thrown by the callback also propagate unchanged.

Defined in lib/context/events.js

isResponseEvent(event) function

Is this a well-formed normalized response event (a known type; indexed events carry a non-negative integer contentIndex)?

event *
value to check.

Returns boolean — `true` for an object with a known event type and, for indexed events, a non-negative integer `contentIndex`; otherwise `false`.

Defined in lib/context/events.js

validateResponseEvent(event) function

Validate a normalized response event (used by IO to check connector output). Returns the same object; throws on violation.

event *
value to validate.

Returns object — The same event object when it is valid.

Throws — {TypeError} If the value is not a well-formed normalized response event.

Defined in lib/context/events.js

fallbackContextWindow(model) function

The curated fallback window for one model id, or null: an exact key, then the longest table prefix the id starts with ("gpt-6-sol" -> "gpt-6", "o3-2025-04-16" -> "o3").

model *
bare model id; non-string or empty values are rejected

Returns number|null — matching fallback token window, or `null` when none

Defined in lib/context/usage.js

appendMessage(context, message, { merge = true } = {…}) function

Append a message to a caller-owned context, MERGING when possible: the message's own adjacent same-sub-type blocks fold first; when the context's last message is mergeable with it (same type, no linkage), their content concatenates (and re-folds) instead of appending a new message. Consecutive same-type text messages — queued user input, repeated /system, a cancel-partial assistant followed by the next turn's — become ONE message, so the context (and every replay of it) shows one merged block per sub-type, not a chain of fragments. Mutates the array in place.

context Array<object>
Caller-owned context array, mutated in place.
message object
Message or metadata record to append.

Returns object|undefined — The stored message (the previous message when merged, otherwise the appended value), or `undefined` when an empty message is skipped.

Throws — {TypeError} If context/message validation fails.

Throws — {TypeError} If folding or merge comparisons fail during processing.

Defined in lib/context/merge.js

assistantMessage(content = []) function

Create an assistant message using the supplied content blocks.

[content=[] Content[]
] - Ordered content blocks; defaults to an empty array.

Returns Message — An assistant message whose content is the supplied array; no validation or copying is performed.

Defined in lib/context/types.js

errorText(msg) function

Get the presentation text of a normalized assistant failure; tool-result boolean errors are distinct and produce an empty string.

msg *
Value whose error field is inspected.

Returns string — The error string, `error.message`, or an empty string.

Defined in lib/context/validate.js

fileMessage(path, buffer) function

Wrap one local file's bytes in the canonical user-message content structure.

path string
Non-empty local file path passed to {@link binaryContent}.
buffer Uint8Array
File bytes passed to {@link binaryContent}.

Returns {type: 2, content: Array<{type: "binary", mimetype: string, filename: string, content: string}>} — A user message containing a single binary content block.

Throws — {TypeError} If either argument fails {@link binaryContent}'s validation.

Defined in lib/context/mime.js

hasContent(msg) function

Has this message any CONTENT a provider can consume? A block with no payload — a text/thinking block whose text is empty, a block reduced to its discriminator alone — is not content; an empty content array is not content either. Chat-completions dialects serialize an empty assistant message as content: null and providers 400 the whole request over it (corrupted sessions), so emptiness is a hard contract, not a style point.

msg *
Value whose `content` blocks are inspected.

Returns boolean — True if at least one block carries provider-consumable content.

Defined in lib/context/validate.js

hasError(msg) function

Does this message report a FAILED RESPONSE? A provider or transport failure is normalized as the response message carrying error — a text string or {message, retry} for timed rate limits — beside whatever content arrived before it (possibly none: the error is then its whole payload). A tool result's boolean error: true is not one: that is the tool's own (answered) failure.

msg *
Value whose normalized response error is inspected.

Returns boolean — True for a nonempty error string or nonempty `error.message`.

Defined in lib/context/validate.js

isRecord(msg) function

Test whether a value is a metadata record: a non-array object with a string type (not a numeric message type). Records may share context and session files with messages; they are not messages and are skipped by message validators. Other components pass them through unmerged or filter them from provider-bound contexts as appropriate.

msg *
Value to test.

Returns boolean — True when `msg` carries a string `type`.

Defined in lib/context/validate.js

rebuildMessage(msg) function

Rebuild a message from recognized schema fields only — the stale provider/cache identifier cleanup. See the module header for the field policy.

msg object
Message to validate and rebuild.

Returns object — A new message containing recognized fields only.

Throws — {TypeError} If the message is invalid.

Defined in lib/context/edit.js

systemMessage(text) function

Create a system message containing one text block made from the given value.

text *
Value converted to a string for the text block.

Returns Message — A system message with one text content block.

Throws — {*} Propagates a thrown value encountered during string conversion.

Defined in lib/context/types.js

userMessage(text, metadata = undefined) function

Create a user message from plain text or an existing ordered block array. Non-array input is converted to a text block; array input is used as-is. If supplied, metadata is spread over the message and can override its fields.

text string|Content[]|*
Plain-text value to convert, or an array of content blocks to preserve.
[metadata=undefined] *
Optional value whose enumerable own properties are spread last onto the message.

Returns Message — The resulting user message.

Throws — {*} Propagates a thrown value from text conversion or metadata getters/spread.

Defined in lib/context/types.js

isMessage(msg) function

Test whether a value has the core message shape: a non-array object with a numeric type and an array content.

msg *
Value to test.

Returns boolean — True when `msg` has the core message shape.

Defined in lib/context/validate.js

validateMessage(msg, at = "message") function

Validate a message's CORE SHAPE, throwing on a violation. Returns the SAME object reference — metadata is never copied, stripped, or reordered. Emptiness is deliberately NOT part of the shape: appendMessage WARNS and drops an empty incoming message, and IO DROPS empty messages from every provider-bound context (no dialect can carry one), while a merge that consumed an incoming message's whole payload into the previous message may legitimately leave it empty inside the live array.

msg *
Value to validate.
[at="message"] string
Address hint for error messages (e.g. "context[2]").

Returns object — The same `msg` reference, unchanged.

Throws — {TypeError} If `msg` is not a non-array object with a numeric `type` and array `content`; the error includes the address hint.

Defined in lib/context/validate.js

parseContext(input) function

Parse buffered CLI input into a context array using the shared grammar. First, the entire input is parsed as JSON; if that is an array, it is returned unchanged. Otherwise, each nonblank line is parsed independently: message objects and valid messages in arrays are included, while other values become user text messages containing the original line.

input string
Complete stdin text after EOF; there is no default.

Returns Array<object> — The resulting context array. This is synchronous and returns no Promise.

Throws — {TypeError} If input is not a string and line processing attempts to call `split` on it.

Effects — Pure parser: does not read stdin or otherwise perform I/O. JSON parse errors are caught and treated as non-JSON input.

Defined in lib/context/parse.js

isContext(ctx) function

Test whether a value is a context array containing only core-shaped messages and/or metadata records.

ctx *
Value to test.

Returns boolean — True when `ctx` is such an array; an empty array passes.

Defined in lib/context/validate.js

validateContext(ctx) function

Validate a context array, allowing metadata records and validating every other entry as a message. Returns the same array reference without mutation.

ctx *
Value to validate as a context.

Returns Array — The same `ctx` array, unchanged.

Throws — {TypeError} If `ctx` is not an array or any non-record entry fails message validation; entry errors identify their context index.

Defined in lib/context/validate.js

detectMime({ path, buffer } = {…}) function

Determine a media type from a filename extension, then recognized leading bytes.

Returns string — The matching media type, or `application/octet-stream` when neither input identifies a supported type.

Throws — {TypeError} If options is `null` and therefore cannot be destructured.

Defined in lib/context/mime.js

mimetypeOf(block) function

Return a content block's media type, preferring its non-empty mimetype field and then its non-empty mime field.

block Object|null|undefined
Block to inspect; nullish values are allowed.

Returns string|undefined — The selected media type, or `undefined` when neither field is a non-empty string.

Throws — {*} Propagates a thrown value from a getter for `mimetype` or `mime`; accessors may be read more than once.

Defined in lib/context/types.js

estimateTokens(text) function

Estimate a token count for text, rounding the heuristic up to a whole token. Non-string inputs produce zero.

text *
value to estimate when it is a string

Returns number — estimated whole-token count

Defined in lib/context/usage.js

estimateContextTokens(context = []) function

Estimated token count of a whole context (the input side of a request — also the live "window consumption" readout when no provider-reported count exists yet). Counts words per block: the historical one-giant-string + word-array version allocated several full copies of the context per call, per frame. Reuses per-block scans only while the current text/argument string values are unchanged; live in-place field and content-array edits are observed on the next call.

[context=[] Array
] - context messages; each message's supported content-block text and arguments are counted

Returns number — estimated input tokens, rounded up to a whole token

Throws — {TypeError} if `context` is not iterable

Defined in lib/context/usage.js

usageAdd(total, reported) function

Add one request report to lifetime usage. Sum only schema counters; recompute totalTokens; replace (and clear absent) metadata/details with the latest report.

total object
Previous totals; neither mutated nor retained.
reported *
Request usage, normalized or wire-shaped; never retained.

Returns object — New independent totals. Non-idempotent: call once per billed attempt.

Effects — None. Invalid reports are ignored; no failures for JSON input.

Defined in lib/context/usage-data.js

usageDetails(value) function

Copy bounded JSON-safe usage/plan extensions, omitting invalid values, cycles, attribution and prototype keys. Limits: 256 nodes, depth 6, 16 array entries, 1024 characters/string, 128/key and 16K total text. Truncation adds no counters.

value *
Untrusted details; neither mutated nor retained.

Returns * — Independent bounded JSON value, or undefined for unsupported input.

Effects — None. Idempotent; no failures for JSON data. Work is bounded by these limits.

Defined in lib/context/usage-data.js

estimateUsage(context = [], message) function

Estimate usage from the request context and the assembled response.

[context=[] Array
] - request messages used for input estimation
[message=undefined] object
assembled assistant message used for output estimation; its content text/arguments are concatenated before estimating

Returns {inputTokens:number, outputTokens:number, source:"estimate"} — estimated usage

Throws — {TypeError} if `context` is not iterable

Defined in lib/context/usage.js

finalizeUsage(reported, context, message) function

Normalize provider-reported usage, or fall back to estimation. Provider token counts win only as a complete pair: partial/invalid counts use estimation (no mixed token sources), retaining normalized counters, finite reported cost and bounded extensions. Input is neither mutated nor retained. Repeated calls with the same report/context/message are idempotent; no side effects.

reported *
connector-extracted usage; accepted only when both inputTokens and outputTokens (or known aliases) are nonnegative finite numbers. Known aliases are consumed; unknown fields become bounded latest-report details
[context=undefined] Array
request context for fallback input estimation; when omitted, the estimator's empty-array default applies
[message=undefined] object
assembled message for fallback output estimation

Returns {inputTokens:number, outputTokens:number, totalTokens:number, source:string, cost?:number, [key: string]: *} — independent canonical snapshot with reported or estimated token counts

Throws — {TypeError} if fallback estimation is needed and `context` is not iterable

Defined in lib/context/usage.js

usageHeaders(headers, { exclude = [] } = {…}) function

Capture bounded unrepresented rate-limit header extensions, excluding unrelated headers.

headers Headers|object
Response headers exposing forEach; neither retained nor mutated; absent input returns an empty map.
[options={}] object
Header filtering options.
[options.exclude=[] string[]
] Header names already represented as quotas, case-insensitive.

Returns Object<string, string> — Independent bounded header map. Idempotent, no side effects.

Throws — {TypeError} If exclude is not an array of strings; correct options before retrying.

Defined in lib/context/usage.js

usageNormalize(reported) function

Normalize a request's usage without estimating tokens. Canonical fields outrank aliases; cache/reasoning detail fields outrank legacy root fields, reasoning outranks thinking. All token counts include cached/reasoning subsets, not extras. Unknown extensions become bounded latest-report details; consumed aliases disappear.

reported *
Provider data; neither mutated nor retained. Invalid fields are omitted.

Returns object — Independent snapshot with nonnegative counters, finite reported cost, derived totalTokens, optional serviceTier/inferenceGeo and bounded details. Idempotent.

Effects — None. No failures for JSON input; detail processing is bounded.

Defined in lib/context/usage-data.js

wordCount(text) function

Count runs of non-whitespace characters without allocating a split array. Non-string and empty inputs produce zero.

text *
value to count when it is a string

Returns number — number of whitespace-separated words

Defined in lib/context/usage.js