Files
openclaw/src/plugin-sdk/memory-migration-source.ts
Peter Steinberger 4319ddbe8c feat(control-ui): import Codex and Claude Code memory (#106406)
* feat(control-ui): import coding assistant memory

* test(migrate): clean memory import temp dirs

* chore: move memory import note to PR body

* build: refresh memory import generated artifacts

* fix(control-ui): complete memory import checks

* build(control-ui): localize memory recovery labels

* fix(control-ui): harden memory import recovery

* refactor(migrate): keep memory import surfaces bounded

* fix(control-ui): use canonical agent row type

* fix(codex): drop dead migration type exports

* fix(migrate): restrict imported memory permissions

* fix(control-ui): preserve memory import recovery state

* fix(control-ui): retain memory import results

* build(control-ui): preserve translation memory history

* fix(control-ui): bind memory import state to agent

* fix(control-ui): unlock memory import after refresh

* test(gateway): preserve memory method suffix

* fix(migrate): bind reviewed memory imports

* fix(migrate): make memory imports retry-safe

* perf(ui): keep memory import lazy

* build: refresh generated artifacts after rebase

* chore: keep migration runtime within LOC guard

* build: refresh Swift protocol model
2026-07-13 15:30:06 -07:00

52 lines
1.6 KiB
TypeScript

import crypto from "node:crypto";
import { readRegularFile } from "../infra/fs-safe.js";
import type { MigrationItem, MigrationPlan } from "../plugins/types.js";
export const MAX_MEMORY_MIGRATION_FILE_BYTES = 64 * 1024 * 1024;
/** Bind copyable memory items to the exact source bytes reviewed by an embedded migration UI. */
export async function bindMemoryMigrationPlanSources(
plan: MigrationPlan,
opts: { includeConflicts?: boolean } = {},
): Promise<MigrationPlan> {
const items: MigrationItem[] = [];
for (const item of plan.items) {
if (
item.kind !== "memory" ||
item.action !== "copy" ||
(item.status !== "planned" && !(opts.includeConflicts && item.status === "conflict")) ||
!item.source
) {
items.push(item);
continue;
}
const { buffer } = await readRegularFile({
filePath: item.source,
maxBytes: MAX_MEMORY_MIGRATION_FILE_BYTES,
});
items.push({
...item,
sourceRevision: {
algorithm: "sha256",
digest: crypto.createHash("sha256").update(buffer).digest("hex"),
},
});
}
return { ...plan, items };
}
/** Reject source bytes that differ from an embedded migration's reviewed plan. */
export function assertMemoryMigrationSourceRevision(
item: MigrationItem,
sourceBuffer: Buffer,
): void {
const expectedSourceSha256 = item.sourceRevision?.digest;
if (
item.sourceRevision?.algorithm === "sha256" &&
typeof expectedSourceSha256 === "string" &&
crypto.createHash("sha256").update(sourceBuffer).digest("hex") !== expectedSourceSha256
) {
throw new Error("memory migration source changed; refresh the plan before importing");
}
}