UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

296 lines (295 loc) 10.2 kB
import "./src-vebZIeLe.js"; import { t as stableStringify } from "./stable-stringify-C8X7niaI.js"; import { n as normalizeAgentId, t as isValidAgentId } from "./agent-id-CeT3w4ap.js"; import { m as resolveAgentWorkspaceDir, o as listAgentIds } from "./agent-scope-config-DcbEhP0R.js"; import "./session-key-BnWWjqNc.js"; import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js"; import "./agent-scope-DbtJyKUL.js"; import { t as ErrorCodes } from "./gateway-error-details-w0nAGBBp.js"; import { Od as validateMigrationsMemoryApplyParams, kd as validateMigrationsMemoryPlanParams } from "./src-BiL5aQto.js"; import { d as errorShape } from "./error-codes-Bo8q2D1o.js"; import { y as summarizeMigrationItems } from "./migration-q6QzXKht.js"; import { t as assertValidParams } from "./validation-pzrlzFvo.js"; import { n as listMemoryMigrationProviders, r as planProviderMemoryImport, t as applyProviderMemoryImport } from "./memory-import-BiHk0sas.js"; import crypto from "node:crypto"; //#region src/gateway/server-methods/migrations.ts const MEMORY_APPLY_DEDUPE_PREFIX = "migrations.memory.apply:"; const activeApplies = /* @__PURE__ */ new Set(); function emptySummary() { return summarizeMigrationItems([]); } const inFlightMemoryApplies = /* @__PURE__ */ new WeakMap(); function memoryApplyInflightMap(dedupe) { let active = inFlightMemoryApplies.get(dedupe); if (!active) { active = /* @__PURE__ */ new Map(); inFlightMemoryApplies.set(dedupe, active); } return active; } function memoryApplyRequestFingerprint(params) { return stableStringify({ agentId: params.agentId, providerId: params.providerId, planFingerprint: params.planFingerprint, itemIds: params.itemIds, overwrite: params.overwrite === true }); } function isCachedMemoryApply(value) { if (!value || typeof value !== "object") return false; const candidate = value; return typeof candidate.requestFingerprint === "string" && candidate.result !== void 0; } function toWireItem(item) { return { id: item.id, status: item.status, ...item.source ? { source: item.source } : {}, ...item.target ? { target: item.target } : {}, ...item.message !== void 0 ? { message: item.message } : {}, ...item.reason !== void 0 ? { reason: item.reason } : {}, ...item.details !== void 0 ? { details: item.details } : {} }; } function fingerprintMemoryPlan(params) { return crypto.createHash("sha256").update(stableStringify({ version: 3, agentId: params.agentId, workspace: params.workspace, providerId: params.providerId, overwrite: params.overwrite === true, plan: params.plan })).digest("hex"); } function targetAgentOrRespond(rawAgentId, config, respond) { if (!isValidAgentId(rawAgentId)) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid agent id")); return; } const agentId = normalizeAgentId(rawAgentId); if (!new Set(listAgentIds(config)).has(agentId)) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "unknown agent id")); return; } return agentId; } async function planMemoryProvider(params) { const base = { providerId: params.provider.id, label: params.provider.label, ...params.provider.description ? { description: params.provider.description } : {} }; try { const { detection, plan } = await planProviderMemoryImport({ provider: params.provider, config: params.config, agentId: params.agentId, overwrite: params.overwrite }); if (detection && !detection.found) return { ...base, found: false, ...detection.source ? { source: detection.source } : {}, ...detection.confidence ? { confidence: detection.confidence } : {}, ...detection.message ? { message: detection.message } : {}, summary: emptySummary(), items: [] }; const found = plan.items.length > 0; const workspace = resolveAgentWorkspaceDir(params.config, params.agentId); return { ...base, found, planFingerprint: fingerprintMemoryPlan({ agentId: params.agentId, workspace, providerId: params.provider.id, overwrite: params.overwrite, plan }), source: plan.source, ...plan.target ? { target: plan.target } : {}, ...detection?.confidence ? { confidence: detection.confidence } : {}, ...detection?.message ? { message: detection.message } : {}, summary: plan.summary, items: plan.items.map(toWireItem), ...plan.warnings?.length ? { warnings: plan.warnings } : {} }; } catch (error) { return { ...base, found: false, error: formatErrorMessage(error), summary: emptySummary(), items: [] }; } } function findMemoryProvider(providers, providerId) { return providers.find((provider) => provider.id === providerId); } const migrationsHandlers = { "migrations.memory.plan": async ({ params, respond, context }) => { if (!assertValidParams(params, validateMigrationsMemoryPlanParams, "migrations.memory.plan", respond)) return; const config = context.getRuntimeConfig(); const agentId = targetAgentOrRespond(params.agentId, config, respond); if (!agentId) return; const providers = listMemoryMigrationProviders(config); const planned = await Promise.all(providers.map(async (provider) => await planMemoryProvider({ provider, config, agentId, overwrite: params.overwrite }))); respond(true, { agentId, workspace: resolveAgentWorkspaceDir(config, agentId), providers: planned }, void 0); }, "migrations.memory.apply": async ({ params, respond, context }) => { if (!assertValidParams(params, validateMigrationsMemoryApplyParams, "migrations.memory.apply", respond)) return; const config = context.getRuntimeConfig(); const agentId = targetAgentOrRespond(params.agentId, config, respond); if (!agentId) return; const requestFingerprint = memoryApplyRequestFingerprint({ agentId, providerId: params.providerId, planFingerprint: params.planFingerprint, itemIds: params.itemIds, overwrite: params.overwrite }); const dedupeKey = `${MEMORY_APPLY_DEDUPE_PREFIX}${params.idempotencyKey}`; const cached = context.dedupe.get(dedupeKey); if (cached && isCachedMemoryApply(cached.payload)) { if (cached.payload.requestFingerprint !== requestFingerprint) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "memory import idempotency key was reused")); return; } respond(true, cached.payload.result, void 0, { cached: true }); return; } const provider = findMemoryProvider(listMemoryMigrationProviders(config), params.providerId); if (!provider) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "unknown memory migration provider")); return; } const inFlightMap = memoryApplyInflightMap(context.dedupe); const inFlight = inFlightMap.get(dedupeKey); if (inFlight) { if (inFlight.requestFingerprint !== requestFingerprint) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "memory import idempotency key was reused")); return; } const outcome = await inFlight.completion; if (outcome.ok) respond(true, outcome.result, void 0, { cached: true }); else respond(false, void 0, outcome.error, { cached: true }); return; } let settle; const completion = new Promise((resolve) => { settle = resolve; }); inFlightMap.set(dedupeKey, { requestFingerprint, completion }); const complete = (outcome) => { settle(outcome); if (outcome.ok) respond(true, outcome.result, void 0); else respond(false, void 0, outcome.error); }; const applyKey = `${agentId}:${provider.id}`; if (activeApplies.has(applyKey)) { complete({ ok: false, error: errorShape(ErrorCodes.UNAVAILABLE, "memory import already running", { retryable: true, retryAfterMs: 1e3 }) }); inFlightMap.delete(dedupeKey); return; } activeApplies.add(applyKey); try { const { plan } = await planProviderMemoryImport({ provider, config, agentId, overwrite: params.overwrite }); if (fingerprintMemoryPlan({ agentId, workspace: resolveAgentWorkspaceDir(config, agentId), providerId: provider.id, overwrite: params.overwrite, plan }) !== params.planFingerprint) { complete({ ok: false, error: errorShape(ErrorCodes.INVALID_REQUEST, "memory migration plan changed; refresh the plan before importing") }); return; } const selectable = new Map(plan.items.filter((item) => item.status === "planned" || item.status === "conflict").map((item) => [item.id, item])); const unavailable = params.itemIds.filter((id) => !selectable.has(id)); if (unavailable.length > 0) { complete({ ok: false, error: errorShape(ErrorCodes.INVALID_REQUEST, `memory migration items changed; refresh the plan (${unavailable.join(", ")})`) }); return; } const selectedConflicts = params.itemIds.filter((id) => selectable.get(id)?.status === "conflict"); if (!params.overwrite && selectedConflicts.length > 0) { complete({ ok: false, error: errorShape(ErrorCodes.INVALID_REQUEST, "selected memory was already imported; enable replacement and refresh the plan") }); return; } const applied = await applyProviderMemoryImport({ provider, config, agentId, itemIds: params.itemIds, overwrite: params.overwrite, preflightPlan: plan }); const result = { providerId: applied.providerId, source: applied.source, ...applied.target ? { target: applied.target } : {}, summary: applied.summary, items: applied.items.map(toWireItem), ...applied.warnings?.length ? { warnings: applied.warnings } : {}, ...applied.backupPath ? { backupPath: applied.backupPath } : {}, ...applied.reportDir ? { reportDir: applied.reportDir } : {} }; context.dedupe.set(dedupeKey, { ts: Date.now(), ok: true, payload: { requestFingerprint, result } }); complete({ ok: true, result }); } catch (error) { complete({ ok: false, error: errorShape(ErrorCodes.UNAVAILABLE, formatErrorMessage(error)) }); } finally { activeApplies.delete(applyKey); inFlightMap.delete(dedupeKey); } } }; //#endregion export { migrationsHandlers };