






















@@ -1,3 +1,4 @@
1+import fs from "node:fs";
12import os from "node:os";
23import path from "node:path";
34import { resolveStateDir } from "../config/paths.js";
@@ -21,16 +22,48 @@ type PersistedSubagentRegistryV2 = {
2122type PersistedSubagentRegistry = PersistedSubagentRegistryV1 | PersistedSubagentRegistryV2;
22232324const REGISTRY_VERSION = 2 as const;
25+const MAX_SUBAGENT_REGISTRY_READ_CACHE_ENTRIES = 32;
24262527type PersistedSubagentRunRecord = SubagentRunRecord;
262829+type RegistryCacheEntry = {
30+signature: string;
31+runs: Map<string, SubagentRunRecord>;
32+};
33+2734type LegacySubagentRunRecord = PersistedSubagentRunRecord & {
2835announceCompletedAt?: unknown;
2936announceHandled?: unknown;
3037requesterChannel?: unknown;
3138requesterAccountId?: unknown;
3239};
334041+const registryReadCache = new Map<string, RegistryCacheEntry>();
42+43+function cloneSubagentRunRecord(entry: SubagentRunRecord): SubagentRunRecord {
44+return structuredClone(entry);
45+}
46+47+function cloneSubagentRunMap(runs: Map<string, SubagentRunRecord>): Map<string, SubagentRunRecord> {
48+return new Map([...runs].map(([runId, entry]) => [runId, cloneSubagentRunRecord(entry)]));
49+}
50+51+function setCachedRegistryRead(
52+pathname: string,
53+signature: string,
54+runs: Map<string, SubagentRunRecord>,
55+): void {
56+registryReadCache.delete(pathname);
57+registryReadCache.set(pathname, { signature, runs: cloneSubagentRunMap(runs) });
58+if (registryReadCache.size <= MAX_SUBAGENT_REGISTRY_READ_CACHE_ENTRIES) {
59+return;
60+}
61+const oldestKey = registryReadCache.keys().next().value;
62+if (typeof oldestKey === "string") {
63+registryReadCache.delete(oldestKey);
64+}
65+}
66+3467function resolveSubagentStateDir(env: NodeJS.ProcessEnv = process.env): string {
3568const explicit = env.OPENCLAW_STATE_DIR?.trim();
3669if (explicit) {
@@ -48,16 +81,30 @@ export function resolveSubagentRegistryPath(): string {
48814982export function loadSubagentRegistryFromDisk(): Map<string, SubagentRunRecord> {
5083const pathname = resolveSubagentRegistryPath();
84+const signature = statRegistryFileSignature(pathname);
85+if (signature === null) {
86+registryReadCache.delete(pathname);
87+return new Map();
88+}
89+const cached = registryReadCache.get(pathname);
90+if (cached?.signature === signature) {
91+registryReadCache.delete(pathname);
92+registryReadCache.set(pathname, cached);
93+return cloneSubagentRunMap(cached.runs);
94+}
5195const raw = loadJsonFile(pathname);
5296if (!raw || typeof raw !== "object") {
97+setCachedRegistryRead(pathname, signature, new Map());
5398return new Map();
5499}
55100const record = raw as Partial<PersistedSubagentRegistry>;
56101if (record.version !== 1 && record.version !== 2) {
102+setCachedRegistryRead(pathname, signature, new Map());
57103return new Map();
58104}
59105const runsRaw = record.runs;
60106if (!runsRaw || typeof runsRaw !== "object") {
107+setCachedRegistryRead(pathname, signature, new Map());
61108return new Map();
62109}
63110const out = new Map<string, SubagentRunRecord>();
@@ -123,6 +170,8 @@ export function loadSubagentRegistryFromDisk(): Map<string, SubagentRunRecord> {
123170} catch {
124171// ignore migration write failures
125172}
173+} else {
174+setCachedRegistryRead(pathname, signature, out);
126175}
127176return out;
128177}
@@ -138,4 +187,25 @@ export function saveSubagentRegistryToDisk(runs: Map<string, SubagentRunRecord>)
138187runs: serialized,
139188};
140189saveJsonFile(pathname, out);
190+const signature = statRegistryFileSignature(pathname);
191+if (signature === null) {
192+registryReadCache.delete(pathname);
193+} else {
194+setCachedRegistryRead(pathname, signature, runs);
195+}
196+}
197+198+function statRegistryFileSignature(pathname: string): string | null {
199+try {
200+const stat = fs.statSync(pathname, { bigint: true });
201+if (!stat.isFile()) {
202+return null;
203+}
204+return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
205+} catch (error) {
206+if ((error as NodeJS.ErrnoException).code === "ENOENT") {
207+return null;
208+}
209+throw error;
210+}
141211}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。