openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
184 lines (183 loc) • 7.66 kB
JavaScript
import { v as isNativeSessionDeletionUnresolved, y as wrapNativeSessionDeletionMutation } from "./session-accessor.sqlite-deletion-CHbs1aOr.mjs";
import { c as createNativeSessionBindingLifecycle } from "./agent-harness-session-runtime-Cu1e3v8y.mjs";
import { n as readRecord, t as bindingSchema } from "./agentsapi-binding-record-CO7NOYrI.mjs";
//#region extensions/agentsapi/agentsapi-bindings.ts
/** Native identity is plugin-owned; shared runtime owns mutation and lease coordination. */
function createAgentsApiBindings(runtime, nativeCleanup) {
const invalidRow = (key) => /* @__PURE__ */ new Error(`Invalid Agents API binding row: ${key}; original state retained. Back up OpenClaw state and restore this binding from a valid pre-upgrade backup before retrying.`);
const stateOptions = {
namespace: "agentsapi-sessions",
maxEntries: 1e5,
overflowPolicy: "reject-new"
};
const state = runtime.state.openSyncKeyedStore(stateOptions);
const mutationState = runtime.state.openKeyedStore(stateOptions);
const lifecycle = createNativeSessionBindingLifecycle({
lookup: state.lookup.bind(state),
deleteIf: state.deleteIf?.bind(state),
registerIfAbsent: state.registerIfAbsent.bind(state),
withCurrent(authority) {
if (!mutationState.withCurrent) throw new Error("Agents API bindings require action-bound plugin-state mutations");
return mutationState.withCurrent(authority);
}
}, {
workerCodec: "agentsapi",
readRecord,
lease: {
staleMs: 65e3,
waitMs: 7e4,
retryIntervalMs: 1e3,
renewIntervalMs: 21e3
},
releaseTtlMs: (_key, current) => current.sessionId ? void 0 : 1,
errors: {
atomicUpdatesRequired: "Agents API bindings require atomic plugin-state updates",
invalidRow,
lostLease: (key, cause) => new Error(`Agents API binding lease lost: ${key}`, { cause }),
leaseTimeout: (key) => /* @__PURE__ */ new Error(`Timed out waiting for Agents API binding lease: ${key}`),
acquisitionRejected: (key) => /* @__PURE__ */ new Error(`Agents API binding acquisition rejected: ${key}`),
mutationBlocked: "Agents API binding mutation blocked during an exclusive operation",
conditionalDeletionRequired: "Agents API deletion requires conditional plugin-state deletion",
deletionChanged: "Agents API binding changed before session deletion",
rollbackChanged: "Agents API binding changed before session deletion rollback"
}
});
const acquisition = (assertCurrent) => ({
assertCurrent,
prepareLease: (current, lease) => ({
...current,
lease
})
});
return {
withExclusiveMutationFence: lifecycle.withExclusiveMutationFence,
async withSession(localSessionId, assertCurrent, run, migrate) {
return await lifecycle.withMutation(() => lifecycle.withLease(localSessionId, async () => {
assertCurrent();
const assertLeaseCurrent = lifecycle.captureLeaseAssertion(localSessionId);
let active = true;
const bind = async (binding, legacy) => {
assertCurrent();
if (!active) throw new Error("Agents API binding operation is no longer active");
assertLeaseCurrent();
const validated = bindingSchema.parse(binding);
await lifecycle.transact(localSessionId, (current) => {
if (legacy && (current?.sessionId !== legacy.sessionId || current.authFingerprint !== legacy.authFingerprint)) throw new Error("Agents API legacy binding changed during migration; retry with the retained binding");
return {
next: {
...validated,
...current?.lease ? { lease: current.lease } : {}
},
result: void 0
};
}, void 0, assertCurrent);
assertCurrent();
};
try {
const stored = readRecord(state.lookup(localSessionId));
let binding;
if (stored?.sessionId && stored.authFingerprint) {
const assertMigrationCurrent = () => {
assertCurrent();
assertLeaseCurrent();
};
binding = await migrate({
sessionId: stored.sessionId,
authFingerprint: stored.authFingerprint
}, assertMigrationCurrent);
assertMigrationCurrent();
if (binding.sessionId !== stored.sessionId) throw new Error("Agents API migration cannot replace the saved native session");
await bind(binding, {
sessionId: stored.sessionId,
authFingerprint: stored.authFingerprint
});
} else if (stored?.sessionId && stored.configFingerprint) binding = bindingSchema.parse(stored);
return await run(binding, bind, assertLeaseCurrent);
} finally {
active = false;
}
}, acquisition(assertCurrent)));
},
async reset(localSessionId, assertCurrent, agentId) {
await lifecycle.withMutation(() => lifecycle.withLease(localSessionId, async () => {
const assertLeaseCurrent = lifecycle.captureLeaseAssertion(localSessionId);
const assertResetCurrent = () => {
assertCurrent();
assertLeaseCurrent();
};
const binding = nativeBinding(readRecord(state.lookup(localSessionId)));
if (binding) {
if (!nativeCleanup) throw new Error("Agents API native session cleanup is unavailable");
await nativeCleanup.settle(localSessionId, binding, assertResetCurrent, agentId);
assertResetCurrent();
if (binding.executor) {
await nativeCleanup.retire(localSessionId, binding, assertResetCurrent);
assertResetCurrent();
}
}
await lifecycle.transact(localSessionId, (current) => ({
next: current?.lease ? { lease: current.lease } : {},
result: void 0
}), void 0, assertCurrent);
}, acquisition(assertCurrent)));
},
resolveContextResetSessionId(sessionId, previousSessionId) {
const raw = state.lookup(sessionId);
const current = readRecord(raw);
if (raw !== void 0 && !current) throw invalidRow(sessionId);
return current?.sessionId || !previousSessionId ? sessionId : previousSessionId;
},
async withSessionDeletion(params, run, retireAfterCommit = false) {
return await lifecycle.withDeletion(params.sessionId, {
...acquisition(params.assertCurrent),
assertRecordCurrent: () => params.assertCurrent()
}, async (stored, mutation) => {
const binding = nativeBinding(stored);
if (binding) {
const assertLeaseCurrent = lifecycle.captureLeaseAssertion(params.sessionId);
const assertDeletionCurrent = () => {
params.assertCurrent();
assertLeaseCurrent();
};
const cleanup = nativeCleanup;
if (!cleanup) throw new Error("Agents API native session cleanup is unavailable");
await cleanup.settle(params.sessionId, binding, assertDeletionCurrent, params.agentId);
assertDeletionCurrent();
if (binding.executor && !retireAfterCommit) {
await cleanup.retire(params.sessionId, binding, assertDeletionCurrent);
assertDeletionCurrent();
}
}
if (!retireAfterCommit || !binding?.executor) return await run(mutation);
let committed = false;
try {
return await run(wrapNativeSessionDeletionMutation(mutation, {
assertCurrent: params.assertCurrent,
committed: () => {
committed = true;
},
rolledBack: () => {
committed = false;
}
}));
} finally {
if (committed && !isNativeSessionDeletionUnresolved(mutation)) {
params.assertCurrent();
await nativeCleanup.retire(params.sessionId, binding, params.assertCurrent);
params.assertCurrent();
}
}
});
}
};
}
function nativeBinding(row) {
return row?.sessionId ? {
sessionId: row.sessionId,
configFingerprint: row.configFingerprint,
executorControllerPluginId: row.executorControllerPluginId,
executor: row.executor
} : void 0;
}
//#endregion
export { createAgentsApiBindings as t };