refactor: migrate subagent registry store v2
Co-authored-by: adam91holt <adam91holt@users.noreply.github.com>
This commit is contained in:
@@ -107,7 +107,7 @@ describe("subagent registry persistence", () => {
|
|||||||
|
|
||||||
const registryPath = path.join(tempStateDir, "subagents", "runs.json");
|
const registryPath = path.join(tempStateDir, "subagents", "runs.json");
|
||||||
const persisted = {
|
const persisted = {
|
||||||
version: 1,
|
version: 2,
|
||||||
runs: {
|
runs: {
|
||||||
"run-2": {
|
"run-2": {
|
||||||
runId: "run-2",
|
runId: "run-2",
|
||||||
@@ -177,6 +177,9 @@ describe("subagent registry persistence", () => {
|
|||||||
expect(entry?.cleanupCompletedAt).toBe(9);
|
expect(entry?.cleanupCompletedAt).toBe(9);
|
||||||
expect(entry?.requesterOrigin?.channel).toBe("whatsapp");
|
expect(entry?.requesterOrigin?.channel).toBe("whatsapp");
|
||||||
expect(entry?.requesterOrigin?.accountId).toBe("legacy-account");
|
expect(entry?.requesterOrigin?.accountId).toBe("legacy-account");
|
||||||
|
|
||||||
|
const after = JSON.parse(await fs.readFile(registryPath, "utf8")) as { version?: number };
|
||||||
|
expect(after.version).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("retries cleanup announce after a failed announce", async () => {
|
it("retries cleanup announce after a failed announce", async () => {
|
||||||
@@ -185,7 +188,7 @@ describe("subagent registry persistence", () => {
|
|||||||
|
|
||||||
const registryPath = path.join(tempStateDir, "subagents", "runs.json");
|
const registryPath = path.join(tempStateDir, "subagents", "runs.json");
|
||||||
const persisted = {
|
const persisted = {
|
||||||
version: 1,
|
version: 2,
|
||||||
runs: {
|
runs: {
|
||||||
"run-3": {
|
"run-3": {
|
||||||
runId: "run-3",
|
runId: "run-3",
|
||||||
|
|||||||
@@ -5,14 +5,21 @@ import { loadJsonFile, saveJsonFile } from "../infra/json-file.js";
|
|||||||
import { normalizeDeliveryContext } from "../utils/delivery-context.js";
|
import { normalizeDeliveryContext } from "../utils/delivery-context.js";
|
||||||
import type { SubagentRunRecord } from "./subagent-registry.js";
|
import type { SubagentRunRecord } from "./subagent-registry.js";
|
||||||
|
|
||||||
export type PersistedSubagentRegistryVersion = 1;
|
export type PersistedSubagentRegistryVersion = 1 | 2;
|
||||||
|
|
||||||
type PersistedSubagentRegistry = {
|
type PersistedSubagentRegistryV1 = {
|
||||||
version: 1;
|
version: 1;
|
||||||
|
runs: Record<string, LegacySubagentRunRecord>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PersistedSubagentRegistryV2 = {
|
||||||
|
version: 2;
|
||||||
runs: Record<string, PersistedSubagentRunRecord>;
|
runs: Record<string, PersistedSubagentRunRecord>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const REGISTRY_VERSION = 1 as const;
|
type PersistedSubagentRegistry = PersistedSubagentRegistryV1 | PersistedSubagentRegistryV2;
|
||||||
|
|
||||||
|
const REGISTRY_VERSION = 2 as const;
|
||||||
|
|
||||||
type PersistedSubagentRunRecord = SubagentRunRecord;
|
type PersistedSubagentRunRecord = SubagentRunRecord;
|
||||||
|
|
||||||
@@ -32,22 +39,30 @@ export function loadSubagentRegistryFromDisk(): Map<string, SubagentRunRecord> {
|
|||||||
const raw = loadJsonFile(pathname);
|
const raw = loadJsonFile(pathname);
|
||||||
if (!raw || typeof raw !== "object") return new Map();
|
if (!raw || typeof raw !== "object") return new Map();
|
||||||
const record = raw as Partial<PersistedSubagentRegistry>;
|
const record = raw as Partial<PersistedSubagentRegistry>;
|
||||||
if (record.version !== REGISTRY_VERSION) return new Map();
|
if (record.version !== 1 && record.version !== 2) return new Map();
|
||||||
const runsRaw = record.runs;
|
const runsRaw = record.runs;
|
||||||
if (!runsRaw || typeof runsRaw !== "object") return new Map();
|
if (!runsRaw || typeof runsRaw !== "object") return new Map();
|
||||||
const out = new Map<string, SubagentRunRecord>();
|
const out = new Map<string, SubagentRunRecord>();
|
||||||
|
const isLegacy = record.version === 1;
|
||||||
|
let migrated = false;
|
||||||
for (const [runId, entry] of Object.entries(runsRaw)) {
|
for (const [runId, entry] of Object.entries(runsRaw)) {
|
||||||
if (!entry || typeof entry !== "object") continue;
|
if (!entry || typeof entry !== "object") continue;
|
||||||
const typed = entry as LegacySubagentRunRecord;
|
const typed = entry as LegacySubagentRunRecord;
|
||||||
if (!typed.runId || typeof typed.runId !== "string") continue;
|
if (!typed.runId || typeof typed.runId !== "string") continue;
|
||||||
const legacyCompletedAt =
|
const legacyCompletedAt =
|
||||||
typeof typed.announceCompletedAt === "number" ? typed.announceCompletedAt : undefined;
|
isLegacy && typeof typed.announceCompletedAt === "number"
|
||||||
|
? typed.announceCompletedAt
|
||||||
|
: undefined;
|
||||||
const cleanupCompletedAt =
|
const cleanupCompletedAt =
|
||||||
typeof typed.cleanupCompletedAt === "number" ? typed.cleanupCompletedAt : legacyCompletedAt;
|
typeof typed.cleanupCompletedAt === "number"
|
||||||
|
? typed.cleanupCompletedAt
|
||||||
|
: legacyCompletedAt;
|
||||||
const cleanupHandled =
|
const cleanupHandled =
|
||||||
typeof typed.cleanupHandled === "boolean"
|
typeof typed.cleanupHandled === "boolean"
|
||||||
? typed.cleanupHandled
|
? typed.cleanupHandled
|
||||||
: Boolean(typed.announceHandled ?? cleanupCompletedAt);
|
: isLegacy
|
||||||
|
? Boolean(typed.announceHandled ?? cleanupCompletedAt)
|
||||||
|
: undefined;
|
||||||
const requesterOrigin = normalizeDeliveryContext(
|
const requesterOrigin = normalizeDeliveryContext(
|
||||||
typed.requesterOrigin ?? {
|
typed.requesterOrigin ?? {
|
||||||
channel: typeof typed.requesterChannel === "string" ? typed.requesterChannel : undefined,
|
channel: typeof typed.requesterChannel === "string" ? typed.requesterChannel : undefined,
|
||||||
@@ -68,6 +83,14 @@ export function loadSubagentRegistryFromDisk(): Map<string, SubagentRunRecord> {
|
|||||||
cleanupCompletedAt,
|
cleanupCompletedAt,
|
||||||
cleanupHandled,
|
cleanupHandled,
|
||||||
});
|
});
|
||||||
|
if (isLegacy) migrated = true;
|
||||||
|
}
|
||||||
|
if (migrated) {
|
||||||
|
try {
|
||||||
|
saveSubagentRegistryToDisk(out);
|
||||||
|
} catch {
|
||||||
|
// ignore migration write failures
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user