UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

417 lines (416 loc) • 17.8 kB
import { r as resolveStateDir } from "./state-dir-CfAW6S2K.mjs"; import { n as sanitizeForLog } from "./ansi-6Z0Nu6M4.mjs"; import { n as runtimeProcessEntrypoints } from "./runtime-process-entrypoints-Bh4reDp0.mjs"; import { r as resolveRuntimeWorkerUrl } from "./runtime-worker-url-S0NIVxUR.mjs"; import { V as recordContextEngineTurnOutboxSchemaCommitted } from "./openclaw-agent-db-validation-cache-D5sa3PLF.mjs"; import { r as cloneEnvWithPlatformSemantics } from "./config-env-vars-oFV7TqIg.mjs"; import { c as resolveOpenClawAgentSqlitePath, i as isIncognitoOpenClawAgentSqlitePath } from "./openclaw-agent-db.paths-DiWMV5LG.mjs"; import { n as recordAgentCleanupFailure, r as runAgentCleanupStep } from "./run-cleanup-timeout-BptAdbpQ.mjs"; import { d as runOpenClawAgentWriteTransaction, p as withOpenClawAgentDatabaseAsync } from "./openclaw-agent-db-CFSa_Qik.mjs"; import { r as runOpenClawAgentWriteAdmission } from "./openclaw-agent-write-admission-xWSWPO-q.mjs"; import { l as resolveLogicalTurnContextEngines, r as hasSameContextEngineInstance, u as disposeContextEngineSources } from "./registry-Il2ImH3E.mjs"; import { t as captureOpenClawAgentDatabaseExecution } from "./openclaw-agent-execution-C-XzIRol.mjs"; import { t as openOpenClawAgentSqliteWorkerStore } from "./openclaw-agent-worker-store-BaoL_lT2.mjs"; import { n as executeContextEngineTurnOutboxCommand, t as drainContextEngineTurnOutbox } from "./context-engine-turn-outbox-iwoy2a8g.mjs"; import "./session-accessor-BM3Wa1Bx.mjs"; import { n as resolveSessionTranscriptDatabasePath } from "./session-accessor.transcript-target-7NtF-HsR.mjs"; import { a as evaluateContextEngineHostSupport, o as supportsContextEngineDurableTurnAdvancement } from "./host-compat-xESS3bi6.mjs"; import { t as ensureContextEnginesInitialized } from "./init-C0_4CXw5.mjs"; import { t as runContextEngineMaintenance } from "./context-engine-maintenance-15qf7_ni.mjs"; //#region src/agents/harness/context-engine-logical-turn.ts function selectContextEngineForTranscriptHost(params) { const admission = params.recorder?.getAdmissionReceipt(); if (params.recorder && !admission && params.recorder.hasPersisted()) return params.lease.degradeBeforeStart("current-turn transcript admission receipt is unavailable"); return params.lease.selectForHost({ host: params.host, operation: params.operation, requiresDurableCommit: params.recorder !== void 0 }); } async function createContextEngineLogicalTurnLease(params) { const { runId, sessionId } = params.identity; ensureContextEnginesInitialized(); const resolution = await resolveLogicalTurnContextEngines(params.config, { agentDir: params.agentDir, workspaceDir: params.workspaceDir, onCleanupFailure: recordAgentCleanupFailure }); let state = "unselected"; let effective = resolution.configured; let degradedReason = resolution.configuredFailure; let warned = false; const disposalHolds = /* @__PURE__ */ new Set(); const isBaselineEngineSelection = resolution.configuredFailure === void 0 && resolution.configured.registeredId === resolution.fallback.registeredId; const asEffective = () => Object.freeze({ ...effective, mode: degradedReason ? "legacy-degraded" : "configured", ...degradedReason ? { reason: degradedReason } : {} }); const warnOnce = (reason) => { if (warned) return; warned = true; (params.warn ?? console.warn)(`[context-engine] Context engine "${sanitizeForLog(resolution.configuredId)}" degraded to "${sanitizeForLog(resolution.fallback.registeredId)}" for this logical turn: ${sanitizeForLog(reason)}. The "${sanitizeForLog(resolution.fallback.registeredId)}" engine will handle only this turn; configuration is unchanged, and "${sanitizeForLog(resolution.configuredId)}" will be retried next turn.`); }; const degradeBeforeStart = (reason) => { if (state === "disposed") throw new Error("context-engine logical turn selection is already pinned"); if (isBaselineEngineSelection) { if (state === "unselected") state = "selected"; return asEffective(); } if (state === "started") throw new Error("context-engine logical turn selection is already pinned"); degradedReason ??= reason; effective = resolution.fallback; state = "selected"; warnOnce(degradedReason); return asEffective(); }; const resolveSelectionIssue = (selection) => { const support = evaluateContextEngineHostSupport({ contextEngineInfo: effective.engine.info, operation: selection.operation, host: selection.host }); if (!support.ok) return `host "${selection.host.id}" is missing ${support.missingCapabilities.join(", ")}`; if (isBaselineEngineSelection) return; if (selection.requiresDurableCommit && effective.engine.info.transcriptSemantics?.currentTurnFence !== "before-current-turn-entry-v1") return "current-turn transcript fencing is not declared"; if (selection.requiresDurableCommit && !supportsContextEngineDurableTurnAdvancement(effective.engine)) return "atomic idempotent turn advancement is not declared"; }; if (resolution.configuredFailure) degradeBeforeStart(resolution.configuredFailure); return { get engine() { return effective.engine; }, get effectiveEngine() { return effective.engine; }, get effectiveEngineId() { return effective.registeredId; }, get effectiveEnginePluginId() { return effective.ownerPluginId; }, get degraded() { return degradedReason !== void 0; }, get degradedReason() { return degradedReason; }, selectForHost(selection) { if (state === "disposed") throw new Error("context-engine logical turn lease is already disposed"); if (degradedReason) return asEffective(); const issue = resolveSelectionIssue(selection); if (issue) { if (state === "started") throw new Error(`context-engine logical turn cannot change to incompatible ${selection.host.label}: ${issue}`); return degradeBeforeStart(issue); } if (state === "unselected") state = "selected"; return asEffective(); }, degradeBeforeStart, begin() { if (state === "disposed") throw new Error("context-engine logical turn lease is already disposed"); state = "started"; return asEffective(); }, deferDisposalUntil(promise) { if (state === "disposed") throw new Error("context-engine logical turn lease is already disposed"); disposalHolds.add(promise); promise.finally(() => disposalHolds.delete(promise)).catch(() => {}); }, async dispose() { if (state === "disposed") return; state = "disposed"; const engines = [resolution.configured.engine, resolution.fallback.engine]; const distinctEngines = engines.filter((engine, index) => engines.slice(0, index).every((other) => !hasSameContextEngineInstance(engine, other))); const disposeEngines = async () => { await Promise.allSettled(distinctEngines.map((engine) => runAgentCleanupStep({ runId, sessionId, step: "context-engine-dispose", log: { warn: params.warn ?? console.warn }, cleanup: async () => { const sources = new Set(engines.filter((other) => hasSameContextEngineInstance(engine, other)).flatMap((other) => resolution.sourceResources?.get(other) ?? [])); await disposeContextEngineSources(engine, [...sources]); } }))); }; if (disposalHolds.size > 0) { Promise.allSettled(disposalHolds).then(disposeEngines); return; } await disposeEngines(); } }; } //#endregion //#region src/agents/harness/context-engine-turn-outbox-store.ts /** * Runs one outbox command in the agent database worker. The host thread only * awaits it, so a worker transaction waiting on this thread for its commit * grant is never blocked by synchronous SQLite here. */ async function runContextEngineTurnOutboxCommand(target, command) { const env = cloneEnvWithPlatformSemantics(process.env); env.OPENCLAW_STATE_DIR = resolveStateDir(env); const options = { agentId: target.agentId, env, path: resolveOpenClawAgentSqlitePath({ agentId: target.agentId, env, path: target.path }) }; if (isIncognitoOpenClawAgentSqlitePath(options.path, options)) { let committedDb; const result = await runOpenClawAgentWriteAdmission(options, () => runOpenClawAgentWriteTransaction(({ db }) => { committedDb = db; return executeContextEngineTurnOutboxCommand(db, command); }, options, { operationLabel: `context-engine.turn-outbox.${command.type}` }), true); if (committedDb) recordContextEngineTurnOutboxSchemaCommitted(committedDb); return result; } const execution = captureOpenClawAgentDatabaseExecution(options); const assertCurrent = () => execution.assertCurrent(); try { return await runOpenClawAgentWriteAdmission(options, () => withOpenClawAgentDatabaseAsync(options, async ({ db }) => { assertCurrent(); const worker = await openOpenClawAgentSqliteWorkerStore(options, db, { moduleUrl: resolveRuntimeWorkerUrl(runtimeProcessEntrypoints.contextEngineTurnOutbox), input: void 0 }); try { return await worker.execute(command, assertCurrent); } finally { await worker.close(); } }, assertCurrent), true); } finally { await execution.release(); } } function openContextEngineTurnOutboxWorkerStore(target) { const run = (command) => runContextEngineTurnOutboxCommand(target, command); return { prepareRun: (input) => run({ type: "prepareRun", input }), listPendingSessions: (input) => run({ type: "listPendingSessions", input }), readNextPending: (input) => run({ type: "readNextPending", input }), complete: async (advancementKey) => { await run({ type: "complete", input: { advancementKey } }); }, recordFailure: async (advancementKey, message, attemptedAt) => { await run({ type: "recordFailure", input: { advancementKey, message, attemptedAt } }); }, hasPending: (input) => run({ type: "hasPending", input }), enqueueIntent: (input) => run({ type: "enqueueIntent", input }), acceptIntent: (input) => run({ type: "acceptIntent", input }), publishClosedTurn: (input) => run({ type: "publishClosedTurn", input }), discardIntent: (input) => run({ type: "discardIntent", input }) }; } //#endregion //#region src/agents/harness/context-engine-turn-attempt.ts const ACCEPTED_TURN_MAX_EVENTS = 2e4; const ACCEPTED_TURN_MAX_BYTES = 8388608; async function drainPendingContextEngineTurnsBeforeRun(params) { if (!params.admission && !params.recorder || params.lease.degraded || !supportsContextEngineDurableTurnAdvancement(params.lease.engine)) return; const warn = params.warn ?? console.warn; try { const target = params.admission ?? params.sessionTarget; if (!target?.agentId || !target.sessionId || !target.sessionKey || !target.storePath) { params.lease.degradeBeforeStart("durable transcript target is unavailable before context assembly"); return; } const databasePath = params.admission ? params.admission.storePath : resolveSessionTranscriptDatabasePath({ agentId: target.agentId, sessionId: target.sessionId, sessionKey: target.sessionKey, storePath: target.storePath }); const store = openContextEngineTurnOutboxWorkerStore({ agentId: target.agentId, path: databasePath }); const owner = { engineId: params.lease.effectiveEngineId, ownerPluginId: params.lease.effectiveEnginePluginId }; const prepared = await store.prepareRun({ ...owner, admission: params.admission, isHeartbeat: params.isHeartbeat === true, sessionId: target.sessionId }); for (const message of prepared.warnings) warn(message); if (prepared.pending) { if ((await drainContextEngineTurnOutbox({ store, engine: params.lease.engine, ...owner, sessionId: target.sessionId, warn })).pending) { params.lease.degradeBeforeStart("pending durable turn advancement could not be completed before the next turn"); return; } } const enqueueAdmission = (admission) => { if (admission.agentId !== target.agentId || admission.sessionId !== target.sessionId || admission.sessionKey !== target.sessionKey || admission.storePath !== databasePath) throw new Error("context-engine transcript target changed before provider dispatch"); return store.enqueueIntent({ ...owner, admission, isHeartbeat: params.isHeartbeat === true }); }; if (params.admission) { if (!prepared.admitted) await enqueueAdmission(params.admission); return; } if (!params.recorder?.setAdmissionHandler) { params.lease.degradeBeforeStart("current-turn transcript admission cannot be recorded for durable advancement"); return; } params.recorder.setAdmissionHandler(enqueueAdmission); } catch (error) { warn(`[context-engine] failed to retry pending turn advancement: ${error instanceof Error ? error.message : String(error)}`); params.lease.degradeBeforeStart("pending durable turn advancement could not be checked before the next turn"); } } async function discardContextEngineTurnAttemptIntent(params) { const warn = params.warn ?? console.warn; try { const admission = params.facts.boundary.admission; await openContextEngineTurnOutboxWorkerStore({ agentId: admission.agentId, path: admission.storePath }).discardIntent({ admission, engineId: params.lease.effectiveEngineId, ownerPluginId: params.lease.effectiveEnginePluginId }); } catch (error) { warn(`[context-engine] failed to discard unaccepted turn intent: ${error instanceof Error ? error.message : String(error)}`); } } function assertAcceptedTranscriptTarget(facts) { const { admission, terminal } = facts.boundary; if (facts.sessionIdUsed !== admission.sessionId || terminal.agentId !== admission.agentId || terminal.sessionId !== admission.sessionId || terminal.sessionKey !== admission.sessionKey || terminal.storePath !== admission.storePath || facts.sessionKey !== void 0 && facts.sessionKey !== admission.sessionKey || facts.sessionTarget?.agentId !== void 0 && facts.sessionTarget.agentId !== admission.agentId || facts.sessionTarget?.sessionId !== void 0 && facts.sessionTarget.sessionId !== admission.sessionId || facts.sessionTarget?.sessionKey !== void 0 && facts.sessionTarget.sessionKey !== admission.sessionKey) throw new Error("accepted context-engine transcript target changed after admission"); } /** Commit the accepted transcript range and offer acknowledged turns to host-owned maintenance. */ async function finalizeAcceptedContextEngineTurn(params) { const declaresDurableAdvancement = params.lease.engine.info.transcriptSemantics?.turnAdvancementIdempotency !== void 0; const implementsDurableAdvancement = supportsContextEngineDurableTurnAdvancement(params.lease.engine); if (!declaresDurableAdvancement && !implementsDurableAdvancement) return; const warn = params.warn ?? console.warn; if (params.facts.promptError || params.facts.aborted || params.facts.yieldAborted) { await discardContextEngineTurnAttemptIntent({ facts: params.facts, lease: params.lease, warn }); return; } try { assertAcceptedTranscriptTarget(params.facts); if (params.lease.degraded || !declaresDurableAdvancement || !implementsDurableAdvancement) throw new Error("accepted context engine does not support durable turn advancement"); const admission = params.facts.boundary.admission; const store = openContextEngineTurnOutboxWorkerStore({ agentId: admission.agentId, path: admission.storePath }); const accepted = { boundary: params.facts.boundary, engineId: params.lease.effectiveEngineId, isHeartbeat: params.facts.isHeartbeat === true, ownerPluginId: params.lease.effectiveEnginePluginId, runtimeContext: params.facts.runtimeContext }; await store.acceptIntent(accepted); const closedTurnKind = await store.publishClosedTurn({ ...accepted, maxBytes: ACCEPTED_TURN_MAX_BYTES, maxEvents: ACCEPTED_TURN_MAX_EVENTS }); if (closedTurnKind !== "ok") throw new Error(`accepted context-engine transcript range is ${closedTurnKind}`); const maintenanceBySession = /* @__PURE__ */ new Map(); const onCommitted = (turn) => { maintenanceBySession.set(turn.sessionId, { contextEngine: params.lease.engine, sessionId: turn.sessionId, sessionKey: turn.sessionKey, sessionTarget: turn.sessionTarget, sessionFile: turn.admission.sessionKey, reason: "turn", runtimeContext: turn.runtimeContext, config: params.config, onDeferredMaintenance: (promise) => params.lease.deferDisposalUntil(promise) }); }; await drainContextEngineTurnOutbox({ store, engine: params.lease.engine, engineId: params.lease.effectiveEngineId, ownerPluginId: params.lease.effectiveEnginePluginId, sessionId: admission.sessionId, onCommitted, warn }); const retrySessionIds = await store.listPendingSessions({ engineId: params.lease.effectiveEngineId, ownerPluginId: params.lease.effectiveEnginePluginId, limit: 16 }); for (const sessionId of retrySessionIds) { if (sessionId === admission.sessionId) continue; await drainContextEngineTurnOutbox({ store, engine: params.lease.engine, engineId: params.lease.effectiveEngineId, ownerPluginId: params.lease.effectiveEnginePluginId, sessionId, limit: 1, onCommitted, warn }); } for (const maintenance of maintenanceBySession.values()) await runContextEngineMaintenance(maintenance); } catch (error) { warn(`[context-engine] skipped accepted turn advancement: ${error instanceof Error ? error.message : String(error)}`); } } //#endregion export { selectContextEngineForTranscriptHost as a, createContextEngineLogicalTurnLease as i, drainPendingContextEngineTurnsBeforeRun as n, finalizeAcceptedContextEngineTurn as r, discardContextEngineTurnAttemptIntent as t };