openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
347 lines (346 loc) • 15.1 kB
JavaScript
import { i as resolveRuntimeWorkerUrl } from "./runtime-worker-url-CFqA9Dvi.mjs";
import { r as resolveStateDir } from "./state-dir-CYkBUd_G.mjs";
import { n as runtimeProcessEntrypoints } from "./runtime-process-entrypoints-De68tE_W.mjs";
import { i as isIncognitoOpenClawAgentSqlitePath, l as resolveOpenClawAgentSqlitePath } from "./openclaw-agent-db.paths-BbmZlEP5.mjs";
import { r as cloneEnvWithPlatformSemantics } from "./config-env-vars-BzUE_dOh.mjs";
import { c as runOpenClawAgentWriteTransaction, d as withOpenClawAgentDatabaseRuntime } from "./openclaw-agent-db-DWI9Bgur.mjs";
import { n as runOpenClawAgentWriteAdmission } from "./openclaw-agent-write-admission-CXJiE5VB.mjs";
import { n as captureOpenClawAgentDatabaseExecution } from "./openclaw-agent-execution-C2B2fucX.mjs";
import { i as rethrowIncognitoSessionError } from "./incognito-session-error-BUzgcwoZ.mjs";
import { n as openOpenClawAgentSqliteWorkerStore } from "./openclaw-agent-worker-store-BE9hhRoh.mjs";
import { r as captureIncognitoSessionOperation } from "./session-incognito-binding-Dp6RTei2.mjs";
import { n as executeContextEngineTurnOutboxCommand, t as drainContextEngineTurnOutbox } from "./context-engine-turn-outbox-CrKyGqEu.mjs";
import "./session-accessor-C3ljJ974.mjs";
import { i as resolveSessionTranscriptDatabasePath } from "./session-accessor.transcript-target-DdAdqCJ1.mjs";
import { o as supportsContextEngineDurableTurnAdvancement } from "./host-compat-DWLjZN89.mjs";
import { t as runContextEngineMaintenance } from "./context-engine-maintenance-lAvtCTBg.mjs";
//#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)) return runOpenClawAgentWriteAdmission(options, () => runOpenClawAgentWriteTransaction(({ db }) => executeContextEngineTurnOutboxCommand(db, command), options, { operationLabel: `context-engine.turn-outbox.${command.type}` }), true);
const execution = captureOpenClawAgentDatabaseExecution(options);
const assertCurrent = () => execution.assertCurrent();
try {
return await runOpenClawAgentWriteAdmission(options, () => withOpenClawAgentDatabaseRuntime(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 captured = { ...target };
const binding = target.incognito ?? captureIncognitoSessionOperation({
...target,
storePath: target.path
});
const incognito = binding && {
...binding,
sessionKey: target.incognito?.sessionKey ?? target.sessionKey,
sessionId: target.incognito?.sessionId ?? target.sessionId
};
if (incognito && (incognito.actor.agentId !== captured.agentId || incognito.actor.path !== captured.path)) throw new Error("Outbox target differs from its captured incognito actor");
const executeActor = incognito && (() => {
const { actor, authority } = incognito;
const { sessionKey, sessionId } = incognito;
if (!sessionKey || !sessionId) throw new Error("Incognito outbox requires its captured session target");
const scoped = (input) => ({
...input,
sessionKey,
sessionId
});
const runActor = (type, input) => actor.sessions.outbox(authority, {
type,
input
});
const commands = {
prepareRun: (input) => runActor("session.outbox.prepareRun", scoped(input)),
listPendingSessions: (input) => runActor("session.outbox.listPendingSessions", scoped(input)),
readNextPending: (input) => runActor("session.outbox.readNextPending", scoped(input)),
complete: (input) => runActor("session.outbox.complete", scoped(input)),
recordFailure: (input) => runActor("session.outbox.recordFailure", scoped(input)),
hasPending: (input) => runActor("session.outbox.hasPending", scoped(input)),
enqueueIntent: (input) => runActor("session.outbox.enqueueIntent", scoped(input)),
acceptIntent: (input) => runActor("session.outbox.acceptIntent", scoped(input)),
publishClosedTurn: (input) => runActor("session.outbox.publishClosedTurn", scoped(input)),
discardIntent: (input) => runActor("session.outbox.discardIntent", scoped(input))
};
const execute = ({ type, input }) => {
if ("sessionId" in input && input.sessionId !== void 0 && input.sessionId !== sessionId || "sessionKey" in input && input.sessionKey !== void 0 && input.sessionKey !== sessionKey) throw new Error("Outbox command differs from its captured incognito session");
return commands[type](input);
};
return execute;
})();
const run = (command) => {
if (executeActor) return executeActor(command).then((value) => {
if (command.type === "listPendingSessions" || command.type === "readNextPending") {
incognito?.authority.assertCurrent();
incognito?.actor.assertReadable();
}
return value;
});
return runContextEngineTurnOutboxCommand(captured, command);
};
return {
...incognito ? {
retain: (operation) => incognito.actor.sessions.withSharedState(operation),
assertReadable() {
incognito.authority.assertCurrent();
incognito.actor.assertReadable();
}
} : {},
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,
sessionKey: target.sessionKey,
sessionId: target.sessionId
});
const owner = {
engineId: params.lease.effectiveEngineId,
ownerPluginId: params.lease.effectiveEnginePluginId
};
const retainAdmission = (admission, write) => {
const pending = new Promise((resolve) => {
params.lease.onDispose(JSON.stringify([databasePath, admission.logicalTurnId]), async () => {
await pending.catch(() => {});
if (await store.discardIntent({
...owner,
admission
})) warn(`[context-engine] discarded unaccepted turn advancement: ${admission.logicalTurnId}: run ended without host acceptance`);
});
resolve(write());
});
return pending;
};
const sessionId = target.sessionId;
const prepare = () => store.prepareRun({
...owner,
admission: params.admission,
isHeartbeat: params.isHeartbeat === true,
sessionId
});
const prepared = await (params.admission ? retainAdmission(params.admission, prepare) : prepare());
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 = async (admission) => {
if (params.lease.disposed) return;
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");
await retainAdmission(admission, () => 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) {
rethrowIncognitoSessionError(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");
}
}
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) 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,
sessionKey: admission.sessionKey,
sessionId: admission.sessionId
});
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)
});
};
const drainSession = (sessionId, limit) => drainContextEngineTurnOutbox({
store,
engine: params.lease.engine,
engineId: params.lease.effectiveEngineId,
ownerPluginId: params.lease.effectiveEnginePluginId,
sessionId,
...limit === void 0 ? {} : { limit },
onCommitted,
warn
});
await drainSession(admission.sessionId);
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 drainSession(sessionId, 1);
}
for (const maintenance of maintenanceBySession.values()) await runContextEngineMaintenance(maintenance);
} catch (error) {
rethrowIncognitoSessionError(error);
warn(`[context-engine] skipped accepted turn advancement: ${error instanceof Error ? error.message : String(error)}`);
}
}
//#endregion
//#region src/agents/harness/context-engine-turn-begin.ts
async function beginContextEngineLogicalTurn(params) {
const { host, ...turn } = params;
const admission = turn.recorder?.getAdmissionReceipt();
if (turn.recorder && !admission && turn.recorder.hasPersisted()) turn.lease.degradeBeforeStart("current-turn transcript admission receipt is unavailable");
else turn.lease.selectForHost({
host,
operation: "agent-run",
requiresDurableCommit: turn.recorder !== void 0
});
await drainPendingContextEngineTurnsBeforeRun({
...turn,
admission: turn.recorder?.getAdmissionReceipt()
});
return turn.lease.begin();
}
//#endregion
export { finalizeAcceptedContextEngineTurn as n, beginContextEngineLogicalTurn as t };