UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

274 lines (273 loc) • 13.9 kB
import { l as normalizeOptionalString } from "./string-coerce-1VuiLaaR.mjs"; import { a as resolveGlobalSingleton } from "./global-singleton-0m-JziJW.mjs"; import { r as assertExistingDatabaseIdentity } from "./sqlite-worker-identity-BRNRpRyc.mjs"; import "./session-key-DY3MOX4N.mjs"; import { h as parseAgentSessionKey } from "./session-key-utils-CDyBC9Gn.mjs"; import { l as resolveOpenClawAgentSqlitePath } from "./openclaw-agent-db.paths-BbmZlEP5.mjs"; import { i as toDatabaseOptions } from "./session-accessor.sqlite-scope-helpers-CfDTAEsL.mjs"; import { s as normalizeDeliveryContext } from "./delivery-context.shared-BYdFKp9k.mjs"; import { n as captureSessionStoreCandidateIdentities, t as assertSessionStoreReadCandidate } from "./session-store-read-candidates-CXD-JYGz.mjs"; import { t as captureSessionTranscriptStorageEnvironment } from "./transcript-target-binding-DpcuSIFy.mjs"; import { f as bindGatewayContextResolver } from "./gateway-request-scope-DV3FHtde.mjs"; import { a as composeSessionSourceAssertion, l as releaseSessionSourceAuthorities } from "./session-source-authority-h1UO3u0I.mjs"; import { i as rethrowIncognitoSessionError } from "./incognito-session-error-BUzgcwoZ.mjs"; import { s as createSessionWorkStartChangedError } from "./work-start-error-Dh9d0ImY.mjs"; import { i as prepareSqliteScope } from "./session-accessor.sqlite-scope-BkgwxatT.mjs"; import { t as captureIncognitoSessionBinding } from "./session-incognito-binding-Dp6RTei2.mjs"; import { t as loadExactSessionEntry } from "./session-accessor.sqlite-exact-read-HBi2gDDN.mjs"; import { i as retainSessionHistoryWorkerDatabase } from "./session-transcript-worker-runtime-BdmdicNg.mjs"; import { n as resolveSessionTranscriptReadFence } from "./session-transcript-read-fence-DqVaDIch.mjs"; import { n as decodeSessionTranscriptWorkerReadError } from "./session-history-worker-errors-DRVUiJbS.mjs"; import { h as normalizeInputProvenance } from "./input-provenance-B5YZS2Do.mjs"; import { t as captureSessionStoreReadCandidates } from "./session-store-target-inventory-C9ERDnSH.mjs"; import "./session-accessor-C3ljJ974.mjs"; import "./lifecycle-BJGMGqxC.mjs"; import { t as readAdmittedHarnessCompletionInput } from "./session-harness-completion-source.kernel-Uf_MmyFd.mjs"; import { n as sourceDeliveryTargetsMatch } from "./source-delivery-plan-6BT8ZkBu.mjs"; import { AsyncLocalStorage } from "node:async_hooks"; //#region src/agents/agent-harness-completion-scope.ts const scopeRegistry = resolveGlobalSingleton(Symbol.for("openclaw.agentHarnessCompletionScope.registry"), () => ({ hostIssuedScopes: /* @__PURE__ */ new WeakSet() })); /** Creates a host-issued requester scope for agent harness completion. */ function createAgentHarnessCompletionScope(params) { const requesterSessionKey = params.requesterSessionKey.trim(); if (!requesterSessionKey) throw new Error("Harness completion scope requires requesterSessionKey"); const requesterAgentId = params.requesterAgentId?.trim() || parseAgentSessionKey(requesterSessionKey)?.agentId; if (!requesterAgentId) throw new Error("Harness completion scope requires an exact requester agent"); const requesterOrigin = normalizeDeliveryContext(params.requesterOrigin); const scope = { requesterSessionKey, requesterAgentId, ...requesterOrigin ? { requesterOrigin } : {} }; scopeRegistry.hostIssuedScopes.add(scope); bindGatewayContextResolver(scope, params.gatewayContextResolver); return scope; } function assertAgentHarnessCompletionScope(scope) { if (!scopeRegistry.hostIssuedScopes.has(scope)) throw new Error("Harness completion requires a host-issued scope"); return scope; } const sourceAdmissions = resolveGlobalSingleton(Symbol.for("openclaw.harnessCompletionSourceAdmission"), () => new AsyncLocalStorage()); /** The native source owner admits delivery; the requester receipt owns accepted recovery thereafter. */ async function withAgentHarnessCompletionAdmission(params, operation) { const scope = assertAgentHarnessCompletionScope(params.scope); let active = true; const assertCurrent = () => { if (!active || !params.isSourceCurrent()) throw new Error("Harness completion source owner retired"); }; assertCurrent(); try { return await sourceAdmissions.run({ ...params, scope, assertCurrent }, operation); } finally { active = false; } } function assertHarnessCompletionSourceAdmission(params) { const admission = sourceAdmissions.getStore(); if (!admission || admission.scope.requesterSessionKey !== params.requesterSessionKey || admission.scope.requesterAgentId !== params.requesterAgentId || admission.requesterSessionId !== params.requesterSessionId || admission.requesterLifecycleRevision !== params.requesterLifecycleRevision || admission.sourceSessionKey !== params.sourceSessionKey || admission.sourceRunId !== params.sourceRunId) throw new Error("Harness completion requires exact host-issued source admission"); admission.assertCurrent(); return admission.assertCurrent; } //#endregion //#region src/agents/agent-harness-completion-recovery.ts /** These receipts are stricter than legacy live-return classification: omission is not success. */ function hasHarnessCompletionFinalReceipt(receipt) { const target = receipt.deliveryContext; if (!target?.channel || !target.to || receipt.payloadsTruncated || receipt.messagingToolSentTargetsTruncated || receipt.messagingToolAggregateEvidenceUnaccounted) return false; const requiredProvider = normalizeOptionalString(target.channel)?.toLowerCase(); if (!requiredProvider) return false; if (receipt.messagingToolSentTargets?.some((sent) => normalizeOptionalString(sent.provider)?.toLowerCase() === requiredProvider && normalizeOptionalString(sent.accountId) === normalizeOptionalString(target.accountId) && sent.sourceReplyFinal === true && sent.visible === true && sourceDeliveryTargetsMatch(sent, target))) return true; return receipt.deliveryStatus?.status === "sent" && (receipt.deliveryStatus.resultCount ?? 0) > 0 && receipt.payloads?.some((payload) => payload.visible === true) === true; } const admittedClaims = /* @__PURE__ */ new WeakMap(); function sameCompletionClaim(left, right) { return Boolean(left && left.taskId === right.taskId && left.taskRunId === right.taskRunId && left.sourceRunId === right.sourceRunId && left.requesterSessionKey === right.requesterSessionKey && left.requesterAgentId === right.requesterAgentId && left.sessionId === right.sessionId && left.lifecycleRevision === right.lifecycleRevision); } function captureHarnessCompletionRecovery(params) { const provenance = normalizeInputProvenance(params.inputProvenance); if (!params.runId.startsWith("announce:") || provenance?.kind !== "inter_session" || !["agent_harness_task", "agent_harness_completion"].includes(provenance.sourceTool ?? "") || provenance.sourceChannel !== "internal" || !provenance.sourceSessionKey) return; const assertSourceCurrent = assertHarnessCompletionSourceAdmission({ requesterSessionKey: params.sessionKey, requesterAgentId: params.agentId, requesterSessionId: params.entry.sessionId, requesterLifecycleRevision: params.entry.lifecycleRevision, sourceSessionKey: provenance.sourceSessionKey, sourceRunId: params.runId }); const claim = { taskId: provenance.sourceSessionKey, taskRunId: provenance.sourceSessionKey, taskStatus: "succeeded", sourceRunId: params.runId, requesterSessionKey: params.sessionKey, requesterAgentId: params.agentId, sessionId: params.entry.sessionId, ...params.entry.lifecycleRevision ? { lifecycleRevision: params.entry.lifecycleRevision } : {} }; admittedClaims.set(claim, assertSourceCurrent); return claim; } /** A current session incarnation and its exact admitted completion receipt own further effects. */ function getOwedHarnessCompletionTask(claim, entry) { if (entry.sessionId !== claim.sessionId || entry.lifecycleRevision !== claim.lifecycleRevision) return; if (entry.restartRecoveryTerminalDeliveryEvidence?.some((receipt) => sameCompletionClaim(receipt.harnessCompletion, claim) && hasHarnessCompletionFinalReceipt(receipt))) return; if (sameCompletionClaim(entry.restartRecoveryHarnessCompletion, claim) || entry.restartRecoveryTerminalDeliveryEvidence?.some((receipt) => sameCompletionClaim(receipt.harnessCompletion, claim))) return claim; const assertSourceCurrent = admittedClaims.get(claim); if (!assertSourceCurrent) return; try { assertSourceCurrent(); return claim; } catch (error) { rethrowIncognitoSessionError(error); return; } } /** The existing admitted execution guard rechecks this before execution and delegated effects. */ function createHarnessCompletionSourceAssertion(params) { const target = { agentId: params.claim.requesterAgentId, sessionKey: params.claim.requesterSessionKey, storePath: params.storePath }; const binding = captureIncognitoSessionBinding(target); const capturedClaim = binding?.actor.sessions.captureCurrent(target.sessionKey); const refuse = () => { throw createSessionWorkStartChangedError(target.sessionKey); }; const assertIncognitoClaim = () => { if (!binding) return refuse(); binding.admissionSignal?.throwIfAborted(); binding.actor.assertReadable(); capturedClaim?.assertCurrent(); const entry = binding.actor.sessions.readSteering(target.sessionKey); if (!entry || !getOwedHarnessCompletionTask(params.claim, entry)) return refuse(); return entry; }; const prepareIncognitoSource = async () => { if (!binding) return refuse(); assertIncognitoClaim(); const retained = await binding.actor.sessions.retainCompletionSource({ assertCurrent: assertIncognitoClaim }, { sessionKey: target.sessionKey, sessionId: params.claim.sessionId, lifecycleRevision: params.claim.lifecycleRevision, claim: params.claim, admission: resolveSessionTranscriptReadFence({ agentId: target.agentId, sessionId: params.claim.sessionId }) }, binding.admissionSignal); return { assertCurrent: () => { assertIncognitoClaim(); retained.assertCurrent(); }, checks: [], release: () => retained.release() }; }; const prepareSnapshot = (snapshot, source, assertStorageCurrent, release) => { const assertCurrent = () => { assertStorageCurrent(); if (!snapshot.entry || !getOwedHarnessCompletionTask(params.claim, snapshot.entry)) refuse(); if (snapshot.readError) throw decodeSessionTranscriptWorkerReadError(snapshot.readError); if (!snapshot.validInput) refuse(); }; assertCurrent(); return { assertCurrent, checks: [{ predicate: { source, sessionKey: target.sessionKey, fields: [ "sessionId", "lifecycleRevision", "restartRecoveryHarnessCompletion", "restartRecoveryTerminalDeliveryEvidence", "restartRecoveryDeliveryRunId", "restartRecoveryRuns" ], expected: snapshot.entry, ...snapshot.version ? { transcript: { sessionId: params.claim.sessionId, version: snapshot.version } } : {} }, refuse }], release }; }; const assertSource = () => { if (binding) { if (assertIncognitoClaim().restartRecoveryDeliveryRunId !== params.claim.sourceRunId) refuse(); return; } const current = loadExactSessionEntry({ ...target, readConsistency: "latest" }); if (!current || current.sessionKey !== params.claim.requesterSessionKey || !getOwedHarnessCompletionTask(params.claim, current.entry) || current.entry.restartRecoveryDeliveryRunId !== params.claim.sourceRunId && !readAdmittedHarnessCompletionInput({ claim: params.claim, entry: current.entry, storePath: params.storePath, operationalRunId: current.entry.restartRecoveryDeliveryRunId })) throw createSessionWorkStartChangedError(params.claim.requesterSessionKey); }; return composeSessionSourceAssertion([params.priorAssertion, Object.assign(assertSource, { prepareSessionSourceScope: binding ? prepareIncognitoSource : void 0, async prepareSessionSource() { const { claim } = params; if (binding) return prepareIncognitoSource(); const env = captureSessionTranscriptStorageEnvironment(process.env); const candidates = captureSessionStoreReadCandidates(params.storePath); const identities = captureSessionStoreCandidateIdentities(candidates); const admission = resolveSessionTranscriptReadFence({ agentId: claim.requesterAgentId, sessionId: claim.sessionId }); const resolved = await prepareSqliteScope({ agentId: claim.requesterAgentId, sessionKey: claim.requesterSessionKey, storePath: params.storePath, env }); const options = toDatabaseOptions(resolved); const path = resolveOpenClawAgentSqlitePath(options); const identity = identities.get(assertSessionStoreReadCandidate(path, candidates)); if (!identity?.key.startsWith("file:")) return refuse(); const source = { agentId: options.agentId, path, databaseIdentity: identity.key.slice(5), databaseBirthtime: identity.birthtime }; const retained = retainSessionHistoryWorkerDatabase({ ...options, path }); try { const snapshot = await retained.owner.readHarnessCompletionSource({ env, claim, source, ...admission ? { admission } : {} }); return prepareSnapshot(snapshot, source, () => { retained.owner.assertCurrent(); assertSessionStoreReadCandidate(path, candidates); assertExistingDatabaseIdentity(path, identity.key, identity.birthtime); }, retained.release); } catch (error) { await releaseSessionSourceAuthorities([retained], [error]); throw error; } } })]); } //#endregion export { assertAgentHarnessCompletionScope as a, hasHarnessCompletionFinalReceipt as i, createHarnessCompletionSourceAssertion as n, createAgentHarnessCompletionScope as o, getOwedHarnessCompletionTask as r, withAgentHarnessCompletionAdmission as s, captureHarnessCompletionRecovery as t };