refactor: neutralize context engine runtime bridge

This commit is contained in:
Peter Steinberger
2026-03-08 17:13:18 +00:00
parent f6cb77134c
commit 11be305609
7 changed files with 27 additions and 26 deletions

View File

@@ -933,7 +933,7 @@ export async function compactEmbeddedPiSession(
tokenBudget: ceCtxInfo.tokens,
customInstructions: params.customInstructions,
force: params.trigger === "manual",
legacyParams: params as Record<string, unknown>,
runtimeContext: params as Record<string, unknown>,
});
return {
ok: result.ok,

View File

@@ -1025,7 +1025,7 @@ export async function runEmbeddedPiAgent(
tokenBudget: ctxInfo.tokens,
force: true,
compactionTarget: "budget",
legacyParams: {
runtimeContext: {
sessionKey: params.sessionKey,
messageChannel: params.messageChannel,
messageProvider: params.messageProvider,

View File

@@ -2,7 +2,7 @@ import { describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../../config/config.js";
import { resolveOllamaBaseUrlForRun } from "../../ollama-stream.js";
import {
buildAfterTurnLegacyCompactionParams,
buildAfterTurnRuntimeContext,
composeSystemPromptWithHookContext,
isOllamaCompatProvider,
prependSystemPromptAddition,
@@ -638,9 +638,9 @@ describe("prependSystemPromptAddition", () => {
});
});
describe("buildAfterTurnLegacyCompactionParams", () => {
describe("buildAfterTurnRuntimeContext", () => {
it("uses primary model when compaction.model is not set", () => {
const legacy = buildAfterTurnLegacyCompactionParams({
const legacy = buildAfterTurnRuntimeContext({
attempt: {
sessionKey: "agent:main:session:abc",
messageChannel: "slack",
@@ -668,7 +668,7 @@ describe("buildAfterTurnLegacyCompactionParams", () => {
});
it("passes primary model through even when compaction.model is set (override resolved in compactDirect)", () => {
const legacy = buildAfterTurnLegacyCompactionParams({
const legacy = buildAfterTurnRuntimeContext({
attempt: {
sessionKey: "agent:main:session:abc",
messageChannel: "slack",
@@ -704,9 +704,8 @@ describe("buildAfterTurnLegacyCompactionParams", () => {
model: "gpt-5.3-codex",
});
});
it("includes resolved auth profile fields for context-engine afterTurn compaction", () => {
const legacy = buildAfterTurnLegacyCompactionParams({
const legacy = buildAfterTurnRuntimeContext({
attempt: {
sessionKey: "agent:main:session:abc",
messageChannel: "slack",

View File

@@ -636,8 +636,8 @@ export function prependSystemPromptAddition(params: {
return `${params.systemPromptAddition}\n\n${params.systemPrompt}`;
}
/** Build legacy compaction params passed into context-engine afterTurn hooks. */
export function buildAfterTurnLegacyCompactionParams(params: {
/** Build runtime context passed into context-engine afterTurn hooks. */
export function buildAfterTurnRuntimeContext(params: {
attempt: Pick<
EmbeddedRunAttemptParams,
| "sessionKey"
@@ -1884,7 +1884,7 @@ export async function runEmbeddedAttempt(
// Let the active context engine run its post-turn lifecycle.
if (params.contextEngine) {
const afterTurnLegacyCompactionParams = buildAfterTurnLegacyCompactionParams({
const afterTurnRuntimeContext = buildAfterTurnRuntimeContext({
attempt: params,
workspaceDir: effectiveWorkspace,
agentDir,
@@ -1898,7 +1898,7 @@ export async function runEmbeddedAttempt(
messages: messagesSnapshot,
prePromptMessageCount,
tokenBudget: params.contextTokenBudget,
legacyCompactionParams: afterTurnLegacyCompactionParams,
runtimeContext: afterTurnRuntimeContext,
});
} catch (afterTurnErr) {
log.warn(`context engine afterTurn failed: ${String(afterTurnErr)}`);

View File

@@ -67,7 +67,7 @@ class MockContextEngine implements ContextEngine {
tokenBudget?: number;
compactionTarget?: "budget" | "threshold";
customInstructions?: string;
legacyParams?: Record<string, unknown>;
runtimeContext?: Record<string, unknown>;
}): Promise<CompactResult> {
return {
ok: true,

View File

@@ -5,6 +5,7 @@ import type {
ContextEngineInfo,
AssembleResult,
CompactResult,
ContextEngineRuntimeContext,
IngestResult,
} from "./types.js";
@@ -54,7 +55,7 @@ export class LegacyContextEngine implements ContextEngine {
autoCompactionSummary?: string;
isHeartbeat?: boolean;
tokenBudget?: number;
legacyCompactionParams?: Record<string, unknown>;
runtimeContext?: ContextEngineRuntimeContext;
}): Promise<void> {
// No-op: legacy flow persists context directly in SessionManager.
}
@@ -67,26 +68,26 @@ export class LegacyContextEngine implements ContextEngine {
currentTokenCount?: number;
compactionTarget?: "budget" | "threshold";
customInstructions?: string;
legacyParams?: Record<string, unknown>;
runtimeContext?: ContextEngineRuntimeContext;
}): Promise<CompactResult> {
// Import through a dedicated runtime boundary so the lazy edge remains effective.
const { compactEmbeddedPiSessionDirect } =
await import("../agents/pi-embedded-runner/compact.runtime.js");
// legacyParams carries the full CompactEmbeddedPiSessionParams fields
// runtimeContext carries the full CompactEmbeddedPiSessionParams fields
// set by the caller in run.ts. We spread them and override the fields
// that come from the ContextEngine compact() signature directly.
const lp = params.legacyParams ?? {};
const runtimeContext = params.runtimeContext ?? {};
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- legacy bridge: legacyParams is an opaque bag matching CompactEmbeddedPiSessionParams
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- bridge runtimeContext matches CompactEmbeddedPiSessionParams
const result = await compactEmbeddedPiSessionDirect({
...lp,
...runtimeContext,
sessionId: params.sessionId,
sessionFile: params.sessionFile,
tokenBudget: params.tokenBudget,
force: params.force,
customInstructions: params.customInstructions,
workspaceDir: (lp.workspaceDir as string) ?? process.cwd(),
workspaceDir: (runtimeContext.workspaceDir as string) ?? process.cwd(),
} as Parameters<typeof compactEmbeddedPiSessionDirect>[0]);
return {

View File

@@ -57,6 +57,7 @@ export type SubagentSpawnPreparation = {
};
export type SubagentEndReason = "deleted" | "completed" | "swept" | "released";
export type ContextEngineRuntimeContext = Record<string, unknown>;
/**
* ContextEngine defines the pluggable contract for context management.
@@ -110,8 +111,8 @@ export interface ContextEngine {
isHeartbeat?: boolean;
/** Optional model context token budget for proactive compaction. */
tokenBudget?: number;
/** Backward-compat only: legacy compaction bridge runtime params. */
legacyCompactionParams?: Record<string, unknown>;
/** Optional runtime-owned context for engines that need caller state. */
runtimeContext?: ContextEngineRuntimeContext;
}): Promise<void>;
/**
@@ -132,15 +133,15 @@ export interface ContextEngine {
sessionId: string;
sessionFile: string;
tokenBudget?: number;
/** Backward-compat only: force legacy compaction behavior even below threshold. */
/** 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 for compatibility. */
/** Controls convergence target; defaults to budget. */
compactionTarget?: "budget" | "threshold";
customInstructions?: string;
/** Backward-compat only: full params bag for legacy compaction bridge. */
legacyParams?: Record<string, unknown>;
/** Optional runtime-owned context for engines that need caller state. */
runtimeContext?: ContextEngineRuntimeContext;
}): Promise<CompactResult>;
/**