UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

662 lines (661 loc) 26.9 kB
import { c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js"; import { i as formatErrorMessage } from "./errors-BXgSefBE.js"; import { s as resolveContextEngineOwnerPluginId } from "./registry-Dca-zXEr.js"; import { f as stripRuntimeContextCustomMessages } from "./internal-runtime-context-BH_40W4f.js"; import { a as failTaskRunByRunId, c as recordTaskRunProgressByRunId, l as setDetachedTaskDeliveryStatusByRunId, n as completeTaskRunByRunId, r as createQueuedTaskRun, u as startTaskRunByRunId } from "./detached-task-runtime-DbytjQtn.js"; import { n as sleepWithAbort } from "./backoff-S1yazmy1.js"; import { f as isGatewayDraining, i as enqueueCommandInLane, l as getQueueSize, n as GatewayDrainingError } from "./command-queue-DGvXlE4p.js"; import { u as findActiveSessionTask } from "./openclaw-tools-C0nKaVVY.js"; import { t as log } from "./logger-B8odRltZ.js"; import { r as resolveSessionLane } from "./lanes-CVttd5qX.js"; import { c as updateTaskNotifyPolicyForOwner, i as findTaskByRunIdForOwner, n as cancelTaskByIdForOwner } from "./task-owner-access-2_OeLR-p.js"; import { n as rewriteTranscriptEntriesInSessionManager, t as rewriteTranscriptEntriesInSessionFile } from "./transcript-rewrite-CFSWWhu_.js"; import { m as resolveContextEngineCapabilities, n as buildAfterTurnRuntimeContextFromUsage, t as buildAfterTurnRuntimeContext } from "./attempt.prompt-helpers-CgpMNs9B.js"; import { randomUUID } from "node:crypto"; //#region src/agents/embedded-agent-runner/context-engine-maintenance.ts /** * Schedules and runs deferred context-engine turn maintenance. */ const TURN_MAINTENANCE_TASK_KIND = "context_engine_turn_maintenance"; const TURN_MAINTENANCE_TASK_LABEL = "Context engine turn maintenance"; const TURN_MAINTENANCE_TASK_TASK = "Deferred context-engine maintenance after turn."; const TURN_MAINTENANCE_LANE_PREFIX = "context-engine-turn-maintenance:"; const TURN_MAINTENANCE_WAIT_POLL_MS = 100; const TURN_MAINTENANCE_LONG_WAIT_MS = 1e4; const DEFERRED_TURN_MAINTENANCE_ABORT_STATE_KEY = Symbol.for("openclaw.contextEngineTurnMaintenanceAbortState"); const activeDeferredTurnMaintenanceRuns = /* @__PURE__ */ new Map(); function resolveDeferredTurnMaintenanceAbortState(processLike) { const existing = processLike[DEFERRED_TURN_MAINTENANCE_ABORT_STATE_KEY]; if (existing) return existing; const created = { registered: false, controllers: /* @__PURE__ */ new Set(), cleanupHandlers: /* @__PURE__ */ new Map() }; processLike[DEFERRED_TURN_MAINTENANCE_ABORT_STATE_KEY] = created; return created; } function unregisterDeferredTurnMaintenanceAbortSignalHandlers(processLike, state) { if (!state.registered) return; for (const [signal, handler] of state.cleanupHandlers) processLike.off(signal, handler); state.cleanupHandlers.clear(); state.registered = false; } function normalizeSessionKey(sessionKey) { return normalizeOptionalString(sessionKey) || void 0; } function resolveDeferredTurnMaintenanceLane(sessionKey) { return `${TURN_MAINTENANCE_LANE_PREFIX}${sessionKey}`; } async function disposeDeferredMaintenanceContextEngine(contextEngine) { try { await contextEngine.dispose?.(); } catch (err) { log.warn("context engine dispose failed after deferred maintenance", { errorMessage: formatErrorMessage(err) }); } } function createDeferredTurnMaintenanceAbortSignal(params) { if (typeof AbortController === "undefined") return { abortSignal: void 0, dispose: () => {} }; const processLike = params?.processLike ?? process; const state = resolveDeferredTurnMaintenanceAbortState(processLike); const handleTerminationSignal = (signalName) => { const shouldReraise = typeof processLike.listenerCount === "function" ? processLike.listenerCount(signalName) === 1 : false; for (const activeController of state.controllers) if (!activeController.signal.aborted) activeController.abort(/* @__PURE__ */ new Error(`received ${signalName} while waiting for deferred maintenance`)); state.controllers.clear(); unregisterDeferredTurnMaintenanceAbortSignalHandlers(processLike, state); if (shouldReraise && typeof processLike.kill === "function") try { processLike.kill(processLike.pid ?? process.pid, signalName); } catch {} }; if (!state.registered) { state.registered = true; const onSigint = () => handleTerminationSignal("SIGINT"); const onSigterm = () => handleTerminationSignal("SIGTERM"); state.cleanupHandlers.set("SIGINT", onSigint); state.cleanupHandlers.set("SIGTERM", onSigterm); processLike.on("SIGINT", onSigint); processLike.on("SIGTERM", onSigterm); } const controller = new AbortController(); state.controllers.add(controller); let disposed = false; const cleanup = () => { if (disposed) return; disposed = true; state.controllers.delete(controller); if (state.controllers.size === 0) unregisterDeferredTurnMaintenanceAbortSignalHandlers(processLike, state); }; return { abortSignal: controller.signal, dispose: cleanup }; } function markDeferredTurnMaintenanceTaskScheduleFailure(params) { const errorMessage = formatErrorMessage(params.error); log.warn(`failed to schedule deferred context engine maintenance: ${errorMessage}`); cancelTaskByIdForOwner({ taskId: params.taskId, callerOwnerKey: params.sessionKey, endedAt: Date.now(), terminalSummary: `Deferred maintenance could not be scheduled: ${errorMessage}` }); } function buildTurnMaintenanceTaskDescriptor(params) { const runId = `turn-maint:${params.sessionKey}:${Date.now().toString(36)}:${randomUUID().slice(0, 8)}`; return createQueuedTaskRun({ runtime: "acp", taskKind: TURN_MAINTENANCE_TASK_KIND, sourceId: TURN_MAINTENANCE_TASK_KIND, requesterSessionKey: params.sessionKey, ownerKey: params.sessionKey, scopeKind: "session", runId, label: TURN_MAINTENANCE_TASK_LABEL, task: TURN_MAINTENANCE_TASK_TASK, notifyPolicy: "silent", deliveryStatus: "pending", preferMetadata: true }); } function promoteTurnMaintenanceTaskVisibility(params) { const task = findTaskByRunIdForOwner({ runId: params.runId, callerOwnerKey: params.sessionKey }); if (!task) return createQueuedTaskRun({ runtime: "acp", taskKind: TURN_MAINTENANCE_TASK_KIND, sourceId: TURN_MAINTENANCE_TASK_KIND, requesterSessionKey: params.sessionKey, ownerKey: params.sessionKey, scopeKind: "session", runId: params.runId, label: TURN_MAINTENANCE_TASK_LABEL, task: TURN_MAINTENANCE_TASK_TASK, notifyPolicy: params.notifyPolicy, deliveryStatus: "pending", preferMetadata: true }); setDetachedTaskDeliveryStatusByRunId({ runId: params.runId, runtime: "acp", sessionKey: params.sessionKey, deliveryStatus: "pending" }); if (task.notifyPolicy !== params.notifyPolicy) updateTaskNotifyPolicyForOwner({ taskId: task.taskId, callerOwnerKey: params.sessionKey, notifyPolicy: params.notifyPolicy }); return findTaskByRunIdForOwner({ runId: params.runId, callerOwnerKey: params.sessionKey }) ?? task; } /** * Attach runtime-owned transcript rewrite helpers to an existing * context-engine runtime context payload. */ function buildContextEngineMaintenanceRuntimeContext(params) { return { ...params.runtimeContext, ...resolveContextEngineCapabilities({ config: params.config, sessionKey: params.sessionKey, agentId: params.agentId, authProfileId: normalizeOptionalString(params.runtimeContext?.authProfileId), contextEnginePluginId: params.contextEnginePluginId, purpose: params.purpose ?? "context-engine.maintenance" }), ...params.allowDeferredCompactionExecution ? { allowDeferredCompactionExecution: true } : {}, rewriteTranscriptEntries: async (request) => { if (params.sessionManager) { const sessionManager = params.sessionManager; const rewriteSessionManagerEntries = () => rewriteTranscriptEntriesInSessionManager({ sessionManager, replacements: request.replacements }); return params.withSessionManagerRewriteLock ? await params.withSessionManagerRewriteLock(rewriteSessionManagerEntries) : rewriteSessionManagerEntries(); } const rewriteTranscriptEntriesInFile = async () => await rewriteTranscriptEntriesInSessionFile({ sessionFile: params.sessionFile, sessionId: params.sessionId, sessionKey: params.sessionKey, agentId: params.agentId, config: params.config, request }); const rewriteSessionKey = normalizeSessionKey(params.sessionKey ?? params.sessionId); if (params.deferTranscriptRewriteToSessionLane && rewriteSessionKey) return await enqueueCommandInLane(resolveSessionLane(rewriteSessionKey), async () => await rewriteTranscriptEntriesInFile()); return await rewriteTranscriptEntriesInFile(); } }; } async function executeContextEngineMaintenance(params) { if (typeof params.contextEngine.maintain !== "function") return; const result = await params.contextEngine.maintain({ sessionId: params.sessionId, sessionKey: params.sessionKey, sessionFile: params.sessionFile, runtimeContext: buildContextEngineMaintenanceRuntimeContext({ sessionId: params.sessionId, sessionKey: params.sessionKey, sessionFile: params.sessionFile, sessionManager: params.executionMode === "background" ? void 0 : params.sessionManager, withSessionManagerRewriteLock: params.executionMode === "background" ? void 0 : params.withSessionManagerRewriteLock, runtimeContext: params.runtimeContext, agentId: params.agentId, allowDeferredCompactionExecution: params.executionMode === "background", deferTranscriptRewriteToSessionLane: params.executionMode === "background", config: params.config, purpose: `context-engine.${params.reason}.maintenance`, contextEnginePluginId: resolveContextEngineOwnerPluginId(params.contextEngine) }) }); if (result.changed) log.info(`[context-engine] maintenance(${params.reason}) changed transcript rewrittenEntries=${result.rewrittenEntries} bytesFreed=${result.bytesFreed} sessionKey=${params.sessionKey ?? params.sessionId ?? "unknown"}`); return result; } async function runDeferredTurnMaintenanceWorker(params) { let surfacedUserNotice = false; let longRunningTimer = null; const shutdownAbort = createDeferredTurnMaintenanceAbortSignal(); const surfaceMaintenanceUpdate = (summary, eventSummary) => { promoteTurnMaintenanceTaskVisibility({ sessionKey: params.sessionKey, runId: params.runId, notifyPolicy: "state_changes" }); surfacedUserNotice = true; recordTaskRunProgressByRunId({ runId: params.runId, runtime: "acp", sessionKey: params.sessionKey, lastEventAt: Date.now(), progressSummary: summary, eventSummary }); }; try { const sessionLane = resolveSessionLane(params.sessionKey); const startedWaitingAt = Date.now(); let lastWaitNoticeAt = 0; for (;;) { while (getQueueSize(sessionLane) > 0) { const now = Date.now(); if (now - startedWaitingAt >= TURN_MAINTENANCE_LONG_WAIT_MS && now - lastWaitNoticeAt >= TURN_MAINTENANCE_LONG_WAIT_MS) { lastWaitNoticeAt = now; surfaceMaintenanceUpdate("Waiting for the session lane to go idle.", surfacedUserNotice ? "Still waiting for the session lane to go idle." : "Deferred maintenance is waiting for the session lane to go idle."); } await sleepWithAbort(TURN_MAINTENANCE_WAIT_POLL_MS, shutdownAbort.abortSignal); } await Promise.resolve(); if (getQueueSize(sessionLane) === 0) break; } const runningAt = Date.now(); startTaskRunByRunId({ runId: params.runId, runtime: "acp", sessionKey: params.sessionKey, startedAt: runningAt, lastEventAt: runningAt, progressSummary: "Running deferred maintenance.", eventSummary: "Starting deferred maintenance." }); longRunningTimer = setTimeout(() => { try { surfaceMaintenanceUpdate("Deferred maintenance is still running.", "Deferred maintenance is still running."); } catch (error) { log.warn(`failed to surface deferred maintenance progress: ${String(error)}`); } }, TURN_MAINTENANCE_LONG_WAIT_MS); const result = await executeContextEngineMaintenance({ contextEngine: params.contextEngine, sessionId: params.sessionId, sessionKey: params.sessionKey, sessionFile: params.sessionFile, reason: "turn", sessionManager: params.sessionManager, runtimeContext: params.runtimeContext, agentId: params.agentId, config: params.config, executionMode: "background" }); if (longRunningTimer) { clearTimeout(longRunningTimer); longRunningTimer = null; } const endedAt = Date.now(); completeTaskRunByRunId({ runId: params.runId, runtime: "acp", sessionKey: params.sessionKey, endedAt, lastEventAt: endedAt, progressSummary: result?.changed ? "Deferred maintenance completed with transcript changes." : "Deferred maintenance completed.", terminalSummary: result?.changed ? `Rewrote ${result.rewrittenEntries} transcript entr${result.rewrittenEntries === 1 ? "y" : "ies"} and freed ${result.bytesFreed} bytes.` : "No transcript changes were needed." }); } catch (err) { if (shutdownAbort.abortSignal?.aborted) { if (longRunningTimer) { clearTimeout(longRunningTimer); longRunningTimer = null; } const task = findTaskByRunIdForOwner({ runId: params.runId, callerOwnerKey: params.sessionKey }); if (task) cancelTaskByIdForOwner({ taskId: task.taskId, callerOwnerKey: params.sessionKey, endedAt: Date.now(), terminalSummary: "Deferred maintenance cancelled during shutdown." }); return; } if (longRunningTimer) { clearTimeout(longRunningTimer); longRunningTimer = null; } const endedAt = Date.now(); const reason = formatErrorMessage(err); if (!surfacedUserNotice) promoteTurnMaintenanceTaskVisibility({ sessionKey: params.sessionKey, runId: params.runId, notifyPolicy: "done_only" }); failTaskRunByRunId({ runId: params.runId, runtime: "acp", sessionKey: params.sessionKey, endedAt, lastEventAt: endedAt, error: reason, progressSummary: "Deferred maintenance failed.", terminalSummary: reason }); log.warn(`deferred context engine maintenance failed: ${reason}`); } finally { shutdownAbort.dispose(); if (params.disposeContextEngineAfterMaintenance) await disposeDeferredMaintenanceContextEngine(params.contextEngine); } } function scheduleDeferredTurnMaintenance(params) { const sessionKey = normalizeSessionKey(params.sessionKey); if (!sessionKey) return; if (isGatewayDraining()) { params.onScheduleFailure?.(new GatewayDrainingError()); return; } const activeRun = activeDeferredTurnMaintenanceRuns.get(sessionKey); if (activeRun) { const supersededParams = activeRun.rerunRequested ? activeRun.latestParams : void 0; activeRun.rerunRequested = true; activeRun.latestParams = { ...params, sessionKey }; if (supersededParams?.disposeContextEngineAfterMaintenance && supersededParams.contextEngine !== params.contextEngine) disposeDeferredMaintenanceContextEngine(supersededParams.contextEngine); return activeRun.promise; } const existingTask = findActiveSessionTask({ sessionKey, runtime: "acp", taskKind: TURN_MAINTENANCE_TASK_KIND }); const reusableTask = existingTask?.runId?.trim() ? existingTask : void 0; if (existingTask && !reusableTask) { updateTaskNotifyPolicyForOwner({ taskId: existingTask.taskId, callerOwnerKey: sessionKey, notifyPolicy: "silent" }); cancelTaskByIdForOwner({ taskId: existingTask.taskId, callerOwnerKey: sessionKey, endedAt: Date.now(), terminalSummary: "Superseded by refreshed deferred maintenance task." }); } const task = reusableTask ?? buildTurnMaintenanceTaskDescriptor({ sessionKey }); if (!task) { log.warn("[context-engine] failed to create deferred turn maintenance task", { sessionKey }); return; } log.info(`[context-engine] deferred turn maintenance ${reusableTask ? "resuming" : "queued"} taskId=${task.taskId} sessionKey=${sessionKey} lane=${resolveDeferredTurnMaintenanceLane(sessionKey)}`); const schedulerAbort = createDeferredTurnMaintenanceAbortSignal(); let runPromise; try { runPromise = enqueueCommandInLane(resolveDeferredTurnMaintenanceLane(sessionKey), async () => runDeferredTurnMaintenanceWorker({ contextEngine: params.contextEngine, sessionId: params.sessionId, sessionKey, sessionFile: params.sessionFile, sessionManager: params.sessionManager, runtimeContext: params.runtimeContext, agentId: params.agentId, config: params.config, runId: task.runId, disposeContextEngineAfterMaintenance: params.disposeContextEngineAfterMaintenance })); } catch (err) { schedulerAbort.dispose(); markDeferredTurnMaintenanceTaskScheduleFailure({ sessionKey, taskId: task.taskId, error: err }); return; } const cleanupDeferredTurnMaintenance = async () => { schedulerAbort.dispose(); const current = activeDeferredTurnMaintenanceRuns.get(sessionKey); if (current !== state) return; const shutdownTriggered = schedulerAbort.abortSignal?.aborted === true; const rerunParams = current.rerunRequested && !shutdownTriggered ? current.latestParams : void 0; const discardedRerunParams = current.rerunRequested && shutdownTriggered ? current.latestParams : void 0; activeDeferredTurnMaintenanceRuns.delete(sessionKey); if (rerunParams) await scheduleDeferredTurnMaintenance(rerunParams); else if (discardedRerunParams?.disposeContextEngineAfterMaintenance) await disposeDeferredMaintenanceContextEngine(discardedRerunParams.contextEngine); }; const trackedPromise = runPromise.catch((err) => { params.onScheduleFailure?.(err); markDeferredTurnMaintenanceTaskScheduleFailure({ sessionKey, taskId: task.taskId, error: err }); }).then(cleanupDeferredTurnMaintenance, async (err) => { await cleanupDeferredTurnMaintenance(); throw err; }); const state = { promise: trackedPromise, rerunRequested: false, latestParams: { ...params, sessionKey } }; activeDeferredTurnMaintenanceRuns.set(sessionKey, state); return trackedPromise; } /** * Run optional context-engine transcript maintenance and normalize the result. */ async function runContextEngineMaintenance(params) { if (typeof params.contextEngine?.maintain !== "function") return; const executionMode = params.executionMode ?? "foreground"; if (params.reason === "turn" && executionMode !== "background" && params.contextEngine.info.turnMaintenanceMode === "background") { try { const deferred = scheduleDeferredTurnMaintenance({ contextEngine: params.contextEngine, sessionId: params.sessionId, sessionKey: params.sessionKey ?? params.sessionId, sessionFile: params.sessionFile, sessionManager: params.sessionManager, runtimeContext: params.runtimeContext, agentId: params.agentId, config: params.config, disposeContextEngineAfterMaintenance: params.disposeDeferredContextEngineAfterMaintenance, onScheduleFailure: params.onDeferredMaintenanceFailure }); if (deferred) params.onDeferredMaintenance?.(deferred); } catch (err) { log.warn(`failed to schedule deferred context engine maintenance: ${String(err)}`); } return; } try { return await executeContextEngineMaintenance({ contextEngine: params.contextEngine, sessionId: params.sessionId, sessionKey: params.sessionKey, sessionFile: params.sessionFile, reason: params.reason, sessionManager: params.sessionManager, withSessionManagerRewriteLock: params.withSessionManagerRewriteLock, runtimeContext: params.runtimeContext, agentId: params.agentId, executionMode, config: params.config }); } catch (err) { log.warn(`context engine maintain failed (${params.reason}): ${String(err)}`); return; } } //#endregion //#region src/agents/harness/context-engine-lifecycle.ts /** * Run optional bootstrap + bootstrap maintenance for a harness-owned context engine. */ async function bootstrapHarnessContextEngine(params) { if (!params.hadSessionFile || !(params.contextEngine?.bootstrap || params.contextEngine?.maintain)) return; try { if (typeof params.contextEngine?.bootstrap === "function") await params.contextEngine.bootstrap({ sessionId: params.sessionId, sessionKey: params.sessionKey, sessionFile: params.sessionFile }); await (params.runMaintenance ?? runHarnessContextEngineMaintenance)({ contextEngine: params.contextEngine, sessionId: params.sessionId, sessionKey: params.sessionKey, sessionFile: params.sessionFile, reason: "bootstrap", sessionManager: params.sessionManager, runtimeContext: params.runtimeContext, config: params.config }); } catch (bootstrapErr) { params.warn(`context engine bootstrap failed: ${String(bootstrapErr)}`); } } /** * Assemble model context through the active harness-owned context engine. */ async function assembleHarnessContextEngine(params) { if (!params.contextEngine) return; const messages = stripRuntimeContextCustomMessages(params.messages); return ensureAssembleResultShape(await params.contextEngine.assemble({ sessionId: params.sessionId, sessionKey: params.sessionKey, messages, tokenBudget: params.tokenBudget, ...params.availableTools ? { availableTools: params.availableTools } : {}, ...params.citationsMode ? { citationsMode: params.citationsMode } : {}, model: params.modelId, ...params.prompt !== void 0 ? { prompt: params.prompt } : {} }), params.contextEngine.info.id); } /** * Validate that a context engine's assemble() return value matches the * AssembleResult contract before the runner consumes it. Engines that omit * `messages` or return a non-array previously crashed the runner downstream * when prompt assembly tried to read `activeSession.messages.length` (#75541). * * Throws a descriptive error so the runner's existing assemble try/catch can * log the offending engine id and fall back to the unmodified pipeline * messages instead of poisoning session state. */ function ensureAssembleResultShape(result, engineId) { if (!result || typeof result !== "object") throw new Error(`context engine "${engineId}" assemble() returned an invalid result: expected an object with a "messages" array (got ${describeAssembleResultType(result)})`); const candidate = result; if (!Array.isArray(candidate.messages)) throw new Error(`context engine "${engineId}" assemble() returned an invalid result: expected an object with a "messages" array (got messages of type ${describeAssembleResultType(candidate.messages)})`); return result; } function describeAssembleResultType(value) { if (value === null) return "null"; if (Array.isArray(value)) return "array"; return typeof value; } /** * Finalize a completed harness turn via afterTurn or ingest fallbacks. */ async function finalizeHarnessContextEngineTurn(params) { if (!params.contextEngine) return { postTurnFinalizationSucceeded: true }; const conversationSnapshot = buildContextEngineConversationSnapshot({ messagesSnapshot: params.messagesSnapshot, prePromptMessageCount: params.prePromptMessageCount }); let postTurnFinalizationSucceeded = true; if (typeof params.contextEngine.afterTurn === "function") try { await params.contextEngine.afterTurn({ sessionId: params.sessionIdUsed, sessionKey: params.sessionKey, sessionFile: params.sessionFile, messages: conversationSnapshot.messages, prePromptMessageCount: conversationSnapshot.prePromptMessageCount, tokenBudget: params.tokenBudget, runtimeContext: params.runtimeContext, isHeartbeat: params.isHeartbeat }); } catch (afterTurnErr) { postTurnFinalizationSucceeded = false; params.warn(`context engine afterTurn failed: ${String(afterTurnErr)}`); } else { const newMessages = conversationSnapshot.messages.slice(conversationSnapshot.prePromptMessageCount); if (newMessages.length > 0) if (typeof params.contextEngine.ingestBatch === "function") try { await params.contextEngine.ingestBatch({ sessionId: params.sessionIdUsed, sessionKey: params.sessionKey, messages: newMessages, isHeartbeat: params.isHeartbeat }); } catch (ingestErr) { postTurnFinalizationSucceeded = false; params.warn(`context engine ingest failed: ${String(ingestErr)}`); } else for (const msg of newMessages) try { await params.contextEngine.ingest?.({ sessionId: params.sessionIdUsed, sessionKey: params.sessionKey, message: msg, isHeartbeat: params.isHeartbeat }); } catch (ingestErr) { postTurnFinalizationSucceeded = false; params.warn(`context engine ingest failed: ${String(ingestErr)}`); } } if (!params.promptError && !params.aborted && !params.yieldAborted && postTurnFinalizationSucceeded) await (params.runMaintenance ?? runHarnessContextEngineMaintenance)({ contextEngine: params.contextEngine, sessionId: params.sessionIdUsed, sessionKey: params.sessionKey, sessionFile: params.sessionFile, reason: "turn", sessionManager: params.sessionManager, runtimeContext: params.runtimeContext, config: params.config }); return { postTurnFinalizationSucceeded }; } function buildContextEngineConversationSnapshot(params) { const prePromptMessages = stripRuntimeContextCustomMessages(params.messagesSnapshot.slice(0, params.prePromptMessageCount)); const turnMessages = stripRuntimeContextCustomMessages(params.messagesSnapshot.slice(params.prePromptMessageCount)); return { messages: [...prePromptMessages, ...turnMessages], prePromptMessageCount: prePromptMessages.length }; } /** * Build runtime context passed into harness context-engine hooks. */ function buildHarnessContextEngineRuntimeContext(params) { return buildAfterTurnRuntimeContext(params); } /** * Build runtime context passed into harness context-engine hooks from usage data. */ function buildHarnessContextEngineRuntimeContextFromUsage(params) { return buildAfterTurnRuntimeContextFromUsage(params); } /** * Run optional transcript maintenance for a harness-owned context engine. */ async function runHarnessContextEngineMaintenance(params) { return await runContextEngineMaintenance({ contextEngine: params.contextEngine, sessionId: params.sessionId, sessionKey: params.sessionKey, sessionFile: params.sessionFile, reason: params.reason, sessionManager: params.sessionManager, runtimeContext: params.runtimeContext, executionMode: params.executionMode, onDeferredMaintenance: params.onDeferredMaintenance, config: params.config }); } /** * Return true when a non-legacy context engine should affect plugin harness behavior. */ function isActiveHarnessContextEngine(contextEngine) { return Boolean(contextEngine && contextEngine.info.id !== "legacy"); } //#endregion export { finalizeHarnessContextEngineTurn as a, runContextEngineMaintenance as c, buildHarnessContextEngineRuntimeContextFromUsage as i, bootstrapHarnessContextEngine as n, isActiveHarnessContextEngine as o, buildHarnessContextEngineRuntimeContext as r, runHarnessContextEngineMaintenance as s, assembleHarnessContextEngine as t };