File size: 19,594 Bytes
76289e7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 | // Context-engine public types define the pluggable context-management lifecycle.
import type { AgentMessage } from "../../packages/agent-core/src/types.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 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;
acceptedHostParams?: string[];
transcriptSemantics?: {
currentTurnFence?: "before-current-turn-entry-v1";
turnAdvancementIdempotency?: "atomic-idempotent-v1";
};
/** 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;
};
// Local-only: engines consume this shape structurally via rewriteTranscriptEntries;
// the last named importer was the deleted destructive rewrite path (#115271).
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 =
| "aggregateToolResultTruncation"
| "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;
/**
* Optional caller cancellation for maintenance work that may block.
* Engines should reject promptly when this signal aborts.
*/
abortSignal?: AbortSignal;
}): 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>;
/**
* Atomically and idempotently commit one accepted durable transcript turn.
* Messages span the admitted user entry through the accepted terminal entry.
* Hosts may retry the same advancement key after process or plugin failure.
*/
commitTurn?(params: {
advancementKey: string;
admission: import("../config/sessions/transcript-entry-anchor.js").TranscriptTurnAdmission;
terminal: import("../config/sessions/transcript-entry-anchor.js").TranscriptEntryAnchor;
messages: AgentMessage[];
sessionId: string;
sessionKey?: string;
sessionTarget?: ContextEngineSessionTarget;
runtimeSettings?: ContextEngineRuntimeSettings;
runtimeContext?: ContextEngineRuntimeContext;
isHeartbeat?: boolean;
}): Promise<{ status: "committed" | "duplicate" }>;
/**
* 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;
runtimeContext?: ContextEngineRuntimeContext;
}): 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>;
}
|