Files
openclaw/src/context-engine/types.ts
Peter Steinberger e2ec8283c4 refactor(deadcode): trim mid-size src exports (#105888)
* refactor(deadcode): trim auto-reply and CLI exports

* refactor(deadcode): trim cron and task exports

* refactor(deadcode): trim fleet and process exports

* test(deadcode): exercise live task and process seams

* test(fleet): cover stream redaction through owner module

* refactor(security): trim dead internal exports

* refactor(secrets): trim dead internal exports

* refactor(deadcode): trim remaining src exports

* refactor(deadcode): remove test-only runtime exports

* refactor(deadcode): trim pairing test exports

* refactor(deadcode): reconcile refreshed baseline

* test(auto-reply): deduplicate queue state imports
2026-07-13 00:42:56 -07:00

524 lines
18 KiB
TypeScript

// Context-engine public types define the pluggable context-management lifecycle.
import type { AgentMessage } from "../agents/runtime/index.js";
import type { MemoryCitationsMode } from "../config/types.memory.js";
// Result types
export type AssembleResult = {
/** Ordered messages to use as model context */
messages: AgentMessage[];
/** Estimated total tokens in assembled context */
estimatedTokens: number;
/**
* Controls which token estimate the runner treats as authoritative for
* preemptive overflow prechecks. The returned `messages` are always the
* prompt sent to the model; this only affects the precheck's token comparison.
*
* - "assembled": the generic precheck uses only the assembled prompt's estimate
* unless the engine owns compaction; owning engines manage prompt admission.
* - "preassembly_may_overflow": the precheck takes the maximum of the
* assembled estimate and the pre-assembly (unwindowed) session-history
* estimate. Engines opt into this when their assembled view can hide an
* overflow that would still affect the underlying transcript. This opt-in
* keeps the generic precheck active even for engines that own compaction.
*
* Defaults to "assembled".
*/
promptAuthority?: "assembled" | "preassembly_may_overflow";
/** Optional context-engine-provided instructions prepended to the runtime system prompt */
systemPromptAddition?: string;
/**
* Optional projection lifecycle for hosts with persistent backend threads.
*
* Context engines that return `thread_bootstrap` ask the host to inject the
* assembled context once for the supplied epoch, then reuse the backend
* thread until the epoch changes. Engines that omit this field retain the
* legacy per-turn projection behavior.
*/
contextProjection?: ContextEngineProjection;
};
export type ContextEngineProjection = {
/** How the assembled context should be projected into the backend runtime. */
mode: "per_turn" | "thread_bootstrap";
/** Stable context epoch. Changing this tells persistent backends to rotate. */
epoch?: string;
/** Optional diagnostic fingerprint for the projected context payload. */
fingerprint?: string;
};
export type ContextEngineOperation = "agent-run" | "manual-compact" | "subagent-spawn";
export type ContextEngineRuntimeMode = "normal" | "fallback" | "degraded";
export type ContextEngineSelectionSource = "configured" | "default" | "unknown";
export type ContextEngineRuntimeReasonCode =
| "provider_timeout"
| "provider_unavailable"
| "rate_limited"
| "context_overflow"
| "runtime_unavailable"
| "unknown";
export type ContextEngineHostCapability =
| "bootstrap"
| "assemble-before-prompt"
| "after-turn"
| "maintain"
| "compact"
| "runtime-llm-complete"
| "thread-bootstrap-projection";
export type ContextEngineHostRequirements = {
/** Host capabilities required before the engine can safely serve this operation. */
requiredCapabilities: ContextEngineHostCapability[];
/** Optional engine-authored guidance appended to the host compatibility error. */
unsupportedMessage?: string;
};
export type ContextEngineRuntimeSettings = {
schemaVersion: 1;
runtime: {
host: "openclaw";
mode: ContextEngineRuntimeMode;
harnessId: string | null;
runtimeId: string | null;
};
model: {
requested: string | null;
resolved: string | null;
provider: string | null;
family: string | null;
};
contextEngineSelection: {
selectedId: string | null;
source: ContextEngineSelectionSource;
};
executionHost: {
id: string | null;
label: string | null;
};
limits: {
promptTokenBudget: number | null;
maxOutputTokens: number | null;
};
diagnostics: {
fallbackReason: ContextEngineRuntimeReasonCode | null;
degradedReason: ContextEngineRuntimeReasonCode | null;
};
};
export class ContextEngineRuntimeSettingsUnavailableError extends Error {
readonly code = "context_engine_runtime_settings_unavailable";
constructor(message?: string) {
super(message);
this.name = "ContextEngineRuntimeSettingsUnavailableError";
}
}
export class ContextEngineRuntimeSettingsUnsupportedError extends Error {
readonly code = "context_engine_runtime_settings_unsupported";
constructor(message?: string) {
super(message);
this.name = "ContextEngineRuntimeSettingsUnsupportedError";
}
}
export type CompactResult = {
ok: boolean;
compacted: boolean;
reason?: string;
result?: {
summary?: string;
firstKeptEntryId?: string;
tokensBefore: number;
tokensAfter?: number;
details?: unknown;
/** Session id after compaction, when the runtime rotated transcripts. */
sessionId?: string;
/** Typed post-compaction live session target; successor when the runtime rotated transcripts. */
sessionTarget?: ContextEngineSessionTarget;
/**
* Raw session file path after compaction.
*
* @deprecated Use `sessionTarget`. Shipped plugin-sdk contract: released
* third-party context engines (v2026.6.x and earlier) report rotated
* transcripts through this field. Remove once typed session targets are
* the only successor contract.
*/
sessionFile?: string;
};
};
/**
* Resolve the post-compaction live transcript identity from a compact result.
*
* Prefers the typed `sessionTarget`. Reading the raw fields is the named
* compat path for shipped third-party engines that predate `sessionTarget`;
* it is removed together with the deprecated `sessionFile` result field.
*/
export function resolveCompactionSuccessorTranscript(result: CompactResult): {
sessionId?: string;
sessionFile?: string;
} {
const target = result.result?.sessionTarget;
return {
sessionId: target?.sessionId ?? result.result?.sessionId,
// Storage-neutral targets carry no raw locator; the deprecated raw field on
// the result remains the only file-era locator shipped engines report.
sessionFile: result.result?.sessionFile,
};
}
export type IngestResult = {
/** Whether the message was ingested (false if duplicate or no-op) */
ingested: boolean;
};
export type IngestBatchResult = {
/** Number of messages ingested from the supplied batch */
ingestedCount: number;
};
export type BootstrapResult = {
/** Whether bootstrap ran and initialized the engine's store */
bootstrapped: boolean;
/** Number of historical messages imported (if applicable) */
importedMessages?: number;
/** Optional reason when bootstrap was skipped */
reason?: string;
};
export type ContextEngineInfo = {
id: string;
name: string;
version?: string;
/** True when the engine manages its own compaction lifecycle. */
ownsCompaction?: boolean;
/**
* Controls how turn-triggered maintenance should be executed.
*
* Engines remain compatible by default unless the host explicitly opts into
* background turn maintenance.
*/
turnMaintenanceMode?: "foreground" | "background";
/**
* Host capability requirements for operations where using an unsupported
* runtime would silently degrade or corrupt the engine's behavior.
*/
hostRequirements?: Partial<Record<ContextEngineOperation, ContextEngineHostRequirements>>;
};
export type SubagentSpawnPreparation = {
/** Roll back pre-spawn setup when subagent launch fails. */
rollback: () => void | Promise<void>;
};
export type SubagentEndReason = "deleted" | "completed" | "swept" | "released";
export type TranscriptRewriteReplacement = {
/** Existing transcript entry id to replace on the active branch. */
entryId: string;
/** Replacement message content for that entry. */
message: AgentMessage;
};
export type TranscriptRewriteRequest = {
/** Message entry replacements to apply in one branch-and-reappend pass. */
replacements: TranscriptRewriteReplacement[];
/** Optional entry-id set that must cover every active-branch entry from the first replacement onward. */
allowedRewriteSuffixEntryIds?: string[];
};
export type TranscriptRewriteResult = {
/** Whether the active branch changed. */
changed: boolean;
/** Estimated bytes removed from the active branch message payloads. */
bytesFreed: number;
/** Number of transcript message entries rewritten. */
rewrittenEntries: number;
/** Optional reason when no rewrite occurred. */
reason?: string;
};
export type ContextEngineMaintenanceResult = TranscriptRewriteResult;
type ContextEnginePromptCacheRetention = "none" | "short" | "long" | "in_memory" | "24h";
type ContextEnginePromptCacheUsage = {
input?: number;
output?: number;
cacheRead?: number;
cacheWrite?: number;
contextUsage?:
| { state: "available"; promptTokens: number; totalTokens: number }
| { state: "unavailable" };
total?: number;
};
type ContextEnginePromptCacheObservationChangeCode =
| "cacheRetention"
| "model"
| "streamStrategy"
| "systemPrompt"
| "tools"
| "transport";
type ContextEnginePromptCacheObservationChange = {
code: ContextEnginePromptCacheObservationChangeCode;
detail: string;
};
type ContextEnginePromptCacheObservation = {
broke: boolean;
previousCacheRead?: number;
cacheRead?: number;
changes?: ContextEnginePromptCacheObservationChange[];
};
export type ContextEnginePromptCacheInfo = {
/** Runtime-resolved retention for the actual provider/model/request path. */
retention?: ContextEnginePromptCacheRetention;
/** Usage from the most recent API call, not accumulated retry/tool-loop totals. */
lastCallUsage?: ContextEnginePromptCacheUsage;
/** Result from the runtime's prompt-cache observability heuristic. */
observation?: ContextEnginePromptCacheObservation;
/** Last known cache-touch timestamp from runtime-managed cache-TTL bookkeeping. */
lastCacheTouchAt?: number;
/** Known cache expiry time when the runtime can source it confidently. */
expiresAt?: number;
};
type ContextEngineTranscriptStorageInfo = {
/**
* Authoritative transcript backend for this runtime turn.
*
* Hosts may still pass legacy locator fields such as `sessionFile` for older
* plugin contracts, but context engines should use this field to decide
* whether that locator is a live transcript source.
*/
kind: "sqlite";
};
export type ContextEngineSessionTarget = {
/** Agent that owns the session in the runtime store. */
agentId?: string;
/** Runtime session id to compact. */
sessionId?: string;
/** Stable session key used for aliases, policy, and store resolution. */
sessionKey?: string;
/** Session store path that scopes the SQLite-backed runtime session. */
storePath?: string;
/** Optional transport thread identity for session target resolution. */
threadId?: string | number;
};
export type ContextEngineRuntimeContext = Record<string, unknown> & {
/** Runtime task working directory; workspaceDir remains the agent bootstrap workspace. */
cwd?: string;
/**
* True when the host has explicitly opted this maintenance run into
* consuming deferred compaction debt.
*/
allowDeferredCompactionExecution?: boolean;
/** Runtime-resolved context window budget for the active model call. */
tokenBudget?: number;
/** Selected agent harness id when compaction delegates back to the runtime. */
agentHarnessId?: string;
/** Best-effort current prompt/context token estimate for this turn. */
currentTokenCount?: number;
/** Optional prompt-cache telemetry for cache-aware engines. */
promptCache?: ContextEnginePromptCacheInfo;
/** Authoritative transcript backend for this turn. */
transcriptStorage?: ContextEngineTranscriptStorageInfo;
/** Storage-neutral runtime session target for compaction delegation. */
sessionTarget?: ContextEngineSessionTarget;
/**
* Safe transcript rewrite helper implemented by the runtime.
*
* Engines decide what is safe to rewrite; the runtime owns how the session
* DAG is updated on disk.
*/
rewriteTranscriptEntries?: (
request: TranscriptRewriteRequest,
) => Promise<TranscriptRewriteResult>;
/** LLM completion capability for engines that need model inference. */
llm?: {
complete: (
params: import("../plugins/runtime/types-core.js").LlmCompleteParams,
) => Promise<import("../plugins/runtime/types-core.js").LlmCompleteResult>;
};
};
/**
* ContextEngine defines the pluggable contract for context management.
*
* Required methods define a generic lifecycle; optional methods allow engines
* to provide additional capabilities (retrieval, lineage, etc.).
*/
export interface ContextEngine {
/** Engine identifier and metadata */
readonly info: ContextEngineInfo;
/**
* Initialize engine state for a session, optionally importing historical context.
*/
bootstrap?(params: {
sessionId: string;
sessionKey?: string;
/** Storage-neutral runtime session target for transcript/session SDK helpers. */
sessionTarget?: ContextEngineSessionTarget;
sessionFile: string;
runtimeSettings?: ContextEngineRuntimeSettings;
runtimeContext?: ContextEngineRuntimeContext;
}): Promise<BootstrapResult>;
/**
* Run transcript maintenance after bootstrap, successful turns, or compaction.
*
* Engines can use runtimeContext.rewriteTranscriptEntries() to request safe
* branch-and-reappend transcript rewrites without depending on runner internals.
*/
maintain?(params: {
sessionId: string;
sessionKey?: string;
/** Storage-neutral runtime session target for transcript/session SDK helpers. */
sessionTarget?: ContextEngineSessionTarget;
sessionFile: string;
runtimeSettings?: ContextEngineRuntimeSettings;
runtimeContext?: ContextEngineRuntimeContext;
}): Promise<ContextEngineMaintenanceResult>;
/**
* Ingest a single message into the engine's store.
*/
ingest(params: {
sessionId: string;
sessionKey?: string;
message: AgentMessage;
/** True when the message belongs to a heartbeat run. */
isHeartbeat?: boolean;
}): Promise<IngestResult>;
/**
* Ingest a completed turn batch as a single unit.
*/
ingestBatch?(params: {
sessionId: string;
sessionKey?: string;
messages: AgentMessage[];
/** True when the batch belongs to a heartbeat run. */
isHeartbeat?: boolean;
}): Promise<IngestBatchResult>;
/**
* Execute optional post-turn lifecycle work after a run attempt completes.
* Engines can use this to persist canonical context and trigger background
* compaction decisions.
*/
afterTurn?(params: {
sessionId: string;
sessionKey?: string;
/** Storage-neutral runtime session target for transcript/session SDK helpers. */
sessionTarget?: ContextEngineSessionTarget;
sessionFile: string;
messages: AgentMessage[];
/** Number of messages that existed before the prompt was sent. */
prePromptMessageCount: number;
/** Optional auto-compaction summary emitted by the runtime. */
autoCompactionSummary?: string;
/** True when this turn belongs to a heartbeat run. */
isHeartbeat?: boolean;
/** Optional model context token budget for proactive compaction. */
tokenBudget?: number;
/** Optional runtime-owned context for engines that need caller state. */
runtimeSettings?: ContextEngineRuntimeSettings;
runtimeContext?: ContextEngineRuntimeContext;
}): Promise<void>;
/**
* Assemble model context under a token budget.
* Returns an ordered set of messages ready for the model.
*/
assemble(params: {
sessionId: string;
sessionKey?: string;
messages: AgentMessage[];
tokenBudget?: number;
/** Tool names available for this run so engines can align prompt guidance with runtime tool access. */
availableTools?: Set<string>;
/** Active memory citation mode when engines want to mirror memory prompt guidance. */
citationsMode?: MemoryCitationsMode;
/** Current model identifier (e.g. "claude-opus-4", "gpt-4o", "qwen2.5-7b").
* Allows context engine plugins to adapt formatting per model. */
model?: string;
/** The incoming user prompt for this turn (useful for retrieval-oriented engines). */
prompt?: string;
runtimeSettings?: ContextEngineRuntimeSettings;
}): Promise<AssembleResult>;
/**
* Compact context to reduce token usage.
* May create summaries, prune old turns, etc.
*
* The host always bounds this call with a finite safety timeout (the same
* one that protects native runtime compaction). Engines that run long
* operations SHOULD additionally honor `abortSignal` so an in-flight
* compaction can be canceled promptly on run abort or host timeout instead
* of running to completion in the background.
*/
compact(params: {
sessionId: string;
sessionKey: string;
/** Caller-resolved owner agent for global session aliases. */
agentId?: string;
/** Storage-neutral runtime session target for delegated compaction. */
sessionTarget?: ContextEngineSessionTarget;
tokenBudget?: number;
/** Force compaction even below the default trigger threshold. */
force?: boolean;
/** Optional live token estimate from the caller's active context. */
currentTokenCount?: number;
/** Controls convergence target; defaults to budget. */
compactionTarget?: "budget" | "threshold";
customInstructions?: string;
/** Optional runtime-owned context for engines that need caller state. */
runtimeSettings?: ContextEngineRuntimeSettings;
runtimeContext?: ContextEngineRuntimeContext;
/**
* Optional abort signal honored before and during compaction. The host
* aborts it on run-level abort or when its compaction safety timeout
* fires; engines should stop work and reject promptly when it aborts.
*/
abortSignal?: AbortSignal;
}): Promise<CompactResult>;
/**
* Prepare context-engine-managed subagent state before the child run starts.
*
* Implementations can return a rollback handle that is invoked when spawn
* fails after preparation succeeds.
*/
prepareSubagentSpawn?(params: {
parentSessionKey: string;
childSessionKey: string;
contextMode?: "isolated" | "fork";
parentSessionId?: string;
parentSessionFile?: string;
childSessionId?: string;
childSessionFile?: string;
ttlMs?: number;
}): Promise<SubagentSpawnPreparation | undefined>;
/**
* Notify the context engine that a subagent lifecycle ended.
*/
onSubagentEnded?(params: { childSessionKey: string; reason: SubagentEndReason }): Promise<void>;
/**
* Dispose of any resources held by the engine.
*/
dispose?(): Promise<void>;
}