























@@ -3,7 +3,7 @@ import {
33resolveContextEngine,
44resolveContextEngineOwnerPluginId,
55} from "../../context-engine/registry.js";
6-import type { ContextEngineRuntimeContext } from "../../context-engine/types.js";
6+import type { ContextEngine, ContextEngineRuntimeContext } from "../../context-engine/types.js";
77import {
88captureCompactionCheckpointSnapshotAsync,
99cleanupCompactionCheckpointSnapshot,
@@ -26,6 +26,7 @@ import {
2626} from "../harness/selection.js";
2727import { resolveContextConfigProviderForRuntime } from "../openai-codex-routing.js";
2828import { ensureRuntimePluginsLoaded } from "../runtime-plugins.js";
29+import { DEFERRED_CONTEXT_ENGINE_COMPACTION_REASON } from "./compact-reasons.js";
2930import type { CompactEmbeddedPiSessionParams } from "./compact.types.js";
3031import { asCompactionHookRunner, runPostCompactionSideEffects } from "./compaction-hooks.js";
3132import {
@@ -64,6 +65,88 @@ function shouldFallbackAfterHarnessCompaction(
6465);
6566}
666768+const DEFERRED_CONTEXT_ENGINE_COMPACTION_SCHEDULE_FAILURE_REASON =
69+"failed to schedule background context-engine maintenance";
70+71+function shouldDeferOwningContextEngineBudgetCompaction(params: {
72+compactParams: CompactEmbeddedPiSessionParams;
73+contextEngine: ContextEngine;
74+}): boolean {
75+// Request-time budget compaction for context-engine-owned transcripts can
76+// spend the whole reply preflight budget. Only defer engines that explicitly
77+// advertise background turn maintenance, leaving native/current-session
78+// harness compaction synchronous.
79+return (
80+params.compactParams.deferOwningContextEngineCompaction === true &&
81+params.compactParams.trigger === "budget" &&
82+params.contextEngine.info.ownsCompaction === true &&
83+params.contextEngine.info.turnMaintenanceMode === "background" &&
84+typeof params.contextEngine.maintain === "function"
85+);
86+}
87+88+async function disposeContextEngine(contextEngine: ContextEngine): Promise<void> {
89+try {
90+await contextEngine.dispose?.();
91+} catch (err) {
92+log.warn("context engine dispose failed after deferred maintenance", {
93+errorMessage: formatErrorMessage(err),
94+});
95+}
96+}
97+98+async function deferOwningContextEngineBudgetCompaction(params: {
99+compactParams: CompactEmbeddedPiSessionParams;
100+contextEngine: ContextEngine;
101+contextEngineRuntimeContext: ContextEngineRuntimeContext;
102+}): Promise<EmbeddedPiCompactResult> {
103+let deferredScheduled = false;
104+try {
105+await runContextEngineMaintenance({
106+contextEngine: params.contextEngine,
107+sessionId: params.compactParams.sessionId,
108+sessionKey: params.compactParams.sessionKey,
109+sessionFile: params.compactParams.sessionFile,
110+reason: "turn",
111+runtimeContext: params.contextEngineRuntimeContext,
112+config: params.compactParams.config,
113+disposeDeferredContextEngineAfterMaintenance: true,
114+onDeferredMaintenance: () => {
115+deferredScheduled = true;
116+},
117+});
118+} catch (err) {
119+log.warn("failed to defer context-engine budget compaction", {
120+errorMessage: formatErrorMessage(err),
121+});
122+}
123+124+if (!deferredScheduled) {
125+await disposeContextEngine(params.contextEngine);
126+log.warn(
127+`[compaction] failed to schedule context-engine-owned budget compaction background maintenance ` +
128+`(sessionKey=${params.compactParams.sessionKey ?? params.compactParams.sessionId})`,
129+);
130+return {
131+ok: false,
132+compacted: false,
133+reason: DEFERRED_CONTEXT_ENGINE_COMPACTION_SCHEDULE_FAILURE_REASON,
134+failure: { reason: "deferred_compaction_not_scheduled" },
135+};
136+}
137+138+log.info(
139+`[compaction] deferred context-engine-owned budget compaction to background maintenance ` +
140+`(sessionKey=${params.compactParams.sessionKey ?? params.compactParams.sessionId} ` +
141+`scheduled=${String(deferredScheduled)})`,
142+);
143+return {
144+ok: true,
145+compacted: false,
146+reason: DEFERRED_CONTEXT_ENGINE_COMPACTION_REASON,
147+};
148+}
149+67150/**
68151 * Compacts a session with lane queueing (session lane + global lane).
69152 * Use this from outside a lane context. If already inside a lane, use
@@ -165,6 +248,18 @@ export async function compactEmbeddedPiSession(
165248`native harness compaction could not use its session binding; falling back to context engine: ${harnessResult.reason ?? "unknown"}`,
166249);
167250}
251+if (
252+shouldDeferOwningContextEngineBudgetCompaction({
253+compactParams: params,
254+ contextEngine,
255+})
256+) {
257+return await deferOwningContextEngineBudgetCompaction({
258+compactParams: params,
259+ contextEngine,
260+ contextEngineRuntimeContext,
261+});
262+}
168263const sessionLane = resolveSessionLane(params.sessionKey?.trim() || params.sessionId);
169264const globalLane = resolveGlobalLane(params.lane);
170265const enqueueGlobal =
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。