@agentmemory/agentmemory 0.9.29 → 0.9.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/.env.example +83 -22
  2. package/AGENTS.md +17 -24
  3. package/README.md +447 -170
  4. package/dist/.env.example +83 -22
  5. package/dist/cli.d.mts.map +1 -1
  6. package/dist/cli.mjs +1221 -335
  7. package/dist/cli.mjs.map +1 -1
  8. package/dist/{connect-Qaeq15TQ.mjs → connect-B4_ca6BD.mjs} +30 -39
  9. package/dist/connect-B4_ca6BD.mjs.map +1 -0
  10. package/dist/docker-compose.yml +40 -14
  11. package/dist/hooks/_capture.d.mts +22 -0
  12. package/dist/hooks/_capture.d.mts.map +1 -0
  13. package/dist/hooks/_capture.mjs +908 -0
  14. package/dist/hooks/_capture.mjs.map +1 -0
  15. package/dist/hooks/antigravity-bridge.d.mts +1 -1
  16. package/dist/hooks/antigravity-bridge.d.mts.map +1 -1
  17. package/dist/hooks/antigravity-bridge.mjs +24 -15
  18. package/dist/hooks/antigravity-bridge.mjs.map +1 -1
  19. package/dist/hooks/notification.mjs +17 -25
  20. package/dist/hooks/notification.mjs.map +1 -1
  21. package/dist/hooks/post-commit.mjs +71 -1
  22. package/dist/hooks/post-commit.mjs.map +1 -1
  23. package/dist/hooks/post-tool-failure.mjs +59 -25
  24. package/dist/hooks/post-tool-failure.mjs.map +1 -1
  25. package/dist/hooks/post-tool-use.mjs +87 -35
  26. package/dist/hooks/post-tool-use.mjs.map +1 -1
  27. package/dist/hooks/pre-compact.mjs +83 -3
  28. package/dist/hooks/pre-compact.mjs.map +1 -1
  29. package/dist/hooks/pre-tool-use.mjs +71 -1
  30. package/dist/hooks/pre-tool-use.mjs.map +1 -1
  31. package/dist/hooks/prompt-submit.mjs +13 -21
  32. package/dist/hooks/prompt-submit.mjs.map +1 -1
  33. package/dist/hooks/session-end.mjs +25 -20
  34. package/dist/hooks/session-end.mjs.map +1 -1
  35. package/dist/hooks/session-start.mjs +80 -3
  36. package/dist/hooks/session-start.mjs.map +1 -1
  37. package/dist/hooks/stop.mjs +71 -1
  38. package/dist/hooks/stop.mjs.map +1 -1
  39. package/dist/hooks/subagent-start.mjs +16 -25
  40. package/dist/hooks/subagent-start.mjs.map +1 -1
  41. package/dist/hooks/subagent-stop.mjs +17 -25
  42. package/dist/hooks/subagent-stop.mjs.map +1 -1
  43. package/dist/hooks/task-completed.mjs +19 -27
  44. package/dist/hooks/task-completed.mjs.map +1 -1
  45. package/dist/iii-config.docker.yaml +10 -10
  46. package/dist/iii-config.yaml +3 -1
  47. package/dist/{image-refs-BKLmp_sD.mjs → image-refs-BaT-9oMU.mjs} +2 -2
  48. package/dist/image-refs-BaT-9oMU.mjs.map +1 -0
  49. package/dist/{image-refs-CJgZmb9X.mjs → image-refs-T_urbwHM.mjs} +3 -3
  50. package/dist/image-refs-T_urbwHM.mjs.map +1 -0
  51. package/dist/{keyed-mutex-DTHtcZrt.mjs → image-store-BsN4vyjr.mjs} +2 -14
  52. package/dist/image-store-BsN4vyjr.mjs.map +1 -0
  53. package/dist/index.mjs +12943 -6218
  54. package/dist/index.mjs.map +1 -1
  55. package/dist/{schema-BFZhNTF7.mjs → keyed-mutex-DchXsyfP.mjs} +28 -4
  56. package/dist/keyed-mutex-DchXsyfP.mjs.map +1 -0
  57. package/dist/secret-store-BnY3A50C.mjs +2348 -0
  58. package/dist/secret-store-BnY3A50C.mjs.map +1 -0
  59. package/dist/{src-G7yt8gGm.mjs → src-Jsq9LCeH.mjs} +5941 -2180
  60. package/dist/src-Jsq9LCeH.mjs.map +1 -0
  61. package/dist/{standalone-BqNJDJ04.mjs → standalone-DKCdrf1d.mjs} +39 -12
  62. package/dist/standalone-DKCdrf1d.mjs.map +1 -0
  63. package/dist/standalone.d.mts.map +1 -1
  64. package/dist/standalone.mjs +249 -14
  65. package/dist/standalone.mjs.map +1 -1
  66. package/dist/status-IRz7ta8-.mjs +2344 -0
  67. package/dist/status-IRz7ta8-.mjs.map +1 -0
  68. package/dist/viewer/index.html +7224 -2794
  69. package/docker-compose.yml +40 -14
  70. package/iii-config.docker.yaml +10 -10
  71. package/iii-config.yaml +3 -1
  72. package/integrations/pi/index.ts +2 -3
  73. package/integrations/pi/security.ts +52 -0
  74. package/package.json +13 -4
  75. package/plugin/.claude-plugin/plugin.json +2 -2
  76. package/plugin/.codex-plugin/plugin.json +26 -4
  77. package/plugin/.devin-plugin/plugin.json +1 -1
  78. package/plugin/.mcp.codex.json +12 -0
  79. package/plugin/.mcp.copilot.json +2 -7
  80. package/plugin/.mcp.json +2 -7
  81. package/plugin/assets/icon.svg +35 -0
  82. package/plugin/hooks/hooks.copilot.json +11 -11
  83. package/plugin/opencode/README.md +266 -30
  84. package/plugin/opencode/agentmemory-capture.ts +572 -3
  85. package/plugin/opencode/plugin.json +1 -1
  86. package/plugin/plugin.json +1 -1
  87. package/plugin/scripts/_capture.d.mts +22 -0
  88. package/plugin/scripts/_capture.d.mts.map +1 -0
  89. package/plugin/scripts/_capture.mjs +908 -0
  90. package/plugin/scripts/_capture.mjs.map +1 -0
  91. package/plugin/scripts/antigravity-bridge.d.mts +1 -1
  92. package/plugin/scripts/antigravity-bridge.d.mts.map +1 -1
  93. package/plugin/scripts/antigravity-bridge.mjs +24 -15
  94. package/plugin/scripts/antigravity-bridge.mjs.map +1 -1
  95. package/plugin/scripts/notification.mjs +17 -25
  96. package/plugin/scripts/notification.mjs.map +1 -1
  97. package/plugin/scripts/plugin-bridge.mjs +378 -0
  98. package/plugin/scripts/post-commit.mjs +71 -1
  99. package/plugin/scripts/post-commit.mjs.map +1 -1
  100. package/plugin/scripts/post-tool-failure.mjs +59 -25
  101. package/plugin/scripts/post-tool-failure.mjs.map +1 -1
  102. package/plugin/scripts/post-tool-use.mjs +87 -35
  103. package/plugin/scripts/post-tool-use.mjs.map +1 -1
  104. package/plugin/scripts/pre-compact.mjs +83 -3
  105. package/plugin/scripts/pre-compact.mjs.map +1 -1
  106. package/plugin/scripts/pre-tool-use.mjs +71 -1
  107. package/plugin/scripts/pre-tool-use.mjs.map +1 -1
  108. package/plugin/scripts/prompt-submit.mjs +13 -21
  109. package/plugin/scripts/prompt-submit.mjs.map +1 -1
  110. package/plugin/scripts/session-end.mjs +25 -20
  111. package/plugin/scripts/session-end.mjs.map +1 -1
  112. package/plugin/scripts/session-start.mjs +80 -3
  113. package/plugin/scripts/session-start.mjs.map +1 -1
  114. package/plugin/scripts/stop.mjs +71 -1
  115. package/plugin/scripts/stop.mjs.map +1 -1
  116. package/plugin/scripts/subagent-start.mjs +16 -25
  117. package/plugin/scripts/subagent-start.mjs.map +1 -1
  118. package/plugin/scripts/subagent-stop.mjs +17 -25
  119. package/plugin/scripts/subagent-stop.mjs.map +1 -1
  120. package/plugin/scripts/task-completed.mjs +19 -27
  121. package/plugin/scripts/task-completed.mjs.map +1 -1
  122. package/plugin/skills/_shared/TROUBLESHOOTING.md +22 -6
  123. package/plugin/skills/agentmemory-agents/REFERENCE.md +3 -3
  124. package/plugin/skills/agentmemory-config/REFERENCE.md +49 -1
  125. package/plugin/skills/agentmemory-config/SKILL.md +44 -7
  126. package/plugin/skills/agentmemory-mcp-tools/REFERENCE.md +1 -1
  127. package/plugin/skills/agentmemory-rest-api/REFERENCE.md +10 -2
  128. package/plugin/skills/handoff/SKILL.md +4 -2
  129. package/plugin/skills/memory-discipline/SKILL.md +13 -5
  130. package/dist/connect-Qaeq15TQ.mjs.map +0 -1
  131. package/dist/image-refs-BKLmp_sD.mjs.map +0 -1
  132. package/dist/image-refs-CJgZmb9X.mjs.map +0 -1
  133. package/dist/keyed-mutex-DTHtcZrt.mjs.map +0 -1
  134. package/dist/logger-BEB7pCI9.mjs +0 -43
  135. package/dist/logger-BEB7pCI9.mjs.map +0 -1
  136. package/dist/schema-BFZhNTF7.mjs.map +0 -1
  137. package/dist/src-G7yt8gGm.mjs.map +0 -1
  138. package/dist/standalone-BqNJDJ04.mjs.map +0 -1
  139. package/dist/tools-registry-BV_2bu7z.mjs +0 -1405
  140. package/dist/tools-registry-BV_2bu7z.mjs.map +0 -1
@@ -114,6 +114,7 @@ const KV = {
114
114
  health: "mem:health",
115
115
  embeddings: (obsId) => `mem:emb:${obsId}`,
116
116
  bm25Index: "mem:index:bm25",
117
+ vectorPendingLog: "mem:index:vec-pending",
117
118
  relations: "mem:relations",
118
119
  profiles: "mem:profiles",
119
120
  claudeBridge: "mem:claude-bridge",
@@ -129,6 +130,8 @@ const KV = {
129
130
  teamUsers: (teamId, userId) => `mem:team:${teamId}:users:${userId}`,
130
131
  teamProfile: (teamId) => `mem:team:${teamId}:profile`,
131
132
  audit: "mem:audit",
133
+ auditMonth: (month) => `mem:audit:${month}`,
134
+ auditMonths: "mem:audit:months",
132
135
  actions: "mem:actions",
133
136
  actionEdges: "mem:action-edges",
134
137
  leases: "mem:leases",
@@ -154,11 +157,15 @@ const KV = {
154
157
  globalSlots: "mem:slots:global",
155
158
  state: "mem:state",
156
159
  commits: "mem:commits",
157
- recentSearches: "mem:recent-searches"
160
+ recentSearches: "mem:recent-searches",
161
+ projectSessionsIndex: "mem:idx:project-sessions",
162
+ obsSessionIndex: (shard) => `mem:idx:obs:${shard}`,
163
+ captureInbox: "mem:capture:inbox",
164
+ captureEvents: (shard) => `mem:capture:events:${shard}`,
165
+ capturePrompts: "mem:capture:prompts"
158
166
  };
159
167
  const STREAM = {
160
168
  name: "mem-live",
161
- group: (sessionId) => sessionId,
162
169
  viewerGroup: "viewer"
163
170
  };
164
171
  function generateId(prefix) {
@@ -192,6 +199,23 @@ function jaccardSimilarity(a, b) {
192
199
  return intersection / (setA.size + setB.size - intersection);
193
200
  }
194
201
  //#endregion
195
- export { jaccardSimilarity as a, generateId as i, STREAM as n, hasCjk as o, fingerprintId as r, segmentCjk as s, KV as t };
202
+ //#region src/state/keyed-mutex.ts
203
+ const locks = /* @__PURE__ */ new Map();
204
+ function withKeyedLock(key, fn) {
205
+ const next = (locks.get(key) ?? Promise.resolve()).then(fn, fn);
206
+ const cleanup = next.then(() => {}, () => {});
207
+ locks.set(key, cleanup);
208
+ cleanup.then(() => {
209
+ if (locks.get(key) === cleanup) locks.delete(key);
210
+ });
211
+ return next;
212
+ }
213
+ function withKeyedLocks(keys, fn) {
214
+ const ordered = [...new Set(keys)].sort();
215
+ const acquire = (index) => index === ordered.length ? fn() : withKeyedLock(ordered[index], () => acquire(index + 1));
216
+ return acquire(0);
217
+ }
218
+ //#endregion
219
+ export { fingerprintId as a, hasCjk as c, STREAM as i, segmentCjk as l, withKeyedLocks as n, generateId as o, KV as r, jaccardSimilarity as s, withKeyedLock as t };
196
220
 
197
- //# sourceMappingURL=schema-BFZhNTF7.mjs.map
221
+ //# sourceMappingURL=keyed-mutex-DchXsyfP.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyed-mutex-DchXsyfP.mjs","names":[],"sources":["../src/state/cjk-segmenter.ts","../src/state/schema.ts","../src/state/keyed-mutex.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\n\nconst cjkRequire = createRequire(import.meta.url);\n\nconst CJK_RE = /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]/u;\nconst HAN_RE = /\\p{Script=Han}/u;\nconst KANA_RE = /[\\p{Script=Hiragana}\\p{Script=Katakana}]/u;\nconst HANGUL_RE = /\\p{Script=Hangul}/u;\nconst CJK_RUN_RE = /[\\p{Script=Han}\\p{Script=Hiragana}\\p{Script=Katakana}\\p{Script=Hangul}]+/gu;\nconst HANGUL_BLOCK_RE = /[가-힯]+/g;\n\ntype Script = \"han\" | \"kana\" | \"hangul\" | \"other\";\n\nconst hintShown = new Set<string>();\n\nexport function hasCjk(text: string): boolean {\n return CJK_RE.test(text);\n}\n\nexport function detectScript(text: string): Script {\n if (HAN_RE.test(text)) return \"han\";\n if (KANA_RE.test(text)) return \"kana\";\n if (HANGUL_RE.test(text)) return \"hangul\";\n return \"other\";\n}\n\nfunction showHintOnce(key: string, message: string): void {\n if (hintShown.has(key)) return;\n hintShown.add(key);\n if (typeof process !== \"undefined\" && process.stderr?.write) {\n process.stderr.write(`agentmemory: ${message}\\n`);\n }\n}\n\ninterface JiebaInstance {\n cut(text: string, hmm?: boolean): string[];\n}\n\nlet jiebaInstance: JiebaInstance | null = null;\nlet jiebaLoaded = false;\n\nfunction getJieba(): JiebaInstance | null {\n if (jiebaLoaded) return jiebaInstance;\n jiebaLoaded = true;\n try {\n const mod = cjkRequire(\"@node-rs/jieba\") as {\n Jieba: {\n new (): JiebaInstance;\n withDict(dict: Uint8Array): JiebaInstance;\n };\n };\n try {\n const dictMod = cjkRequire(\"@node-rs/jieba/dict\") as { dict: Uint8Array };\n jiebaInstance = mod.Jieba.withDict(dictMod.dict);\n } catch {\n jiebaInstance = new mod.Jieba();\n }\n return jiebaInstance;\n } catch {\n showHintOnce(\n \"jieba\",\n \"install @node-rs/jieba to improve Chinese search; falling back to whole-string tokenization\",\n );\n return null;\n }\n}\n\ninterface JaSegmenter {\n segment(text: string): string[];\n}\n\nlet jaSegmenterInstance: JaSegmenter | null = null;\nlet jaSegmenterLoaded = false;\n\nfunction getJaSegmenter(): JaSegmenter | null {\n if (jaSegmenterLoaded) return jaSegmenterInstance;\n jaSegmenterLoaded = true;\n try {\n const Ctor = cjkRequire(\"tiny-segmenter\") as new () => JaSegmenter;\n jaSegmenterInstance = new Ctor();\n return jaSegmenterInstance;\n } catch {\n showHintOnce(\n \"tiny-segmenter\",\n \"install tiny-segmenter to improve Japanese search; falling back to whole-string tokenization\",\n );\n return null;\n }\n}\n\nfunction cleanTokens(tokens: string[]): string[] {\n const out: string[] = [];\n for (const t of tokens) {\n const trimmed = t.trim();\n if (trimmed) out.push(trimmed);\n }\n return out;\n}\n\nfunction segmentHan(text: string): string[] {\n const j = getJieba();\n if (!j) return [text];\n try {\n return cleanTokens(j.cut(text, true));\n } catch {\n return [text];\n }\n}\n\nfunction segmentKana(text: string): string[] {\n const s = getJaSegmenter();\n if (!s) return [text];\n try {\n return cleanTokens(s.segment(text));\n } catch {\n return [text];\n }\n}\n\nfunction segmentHangul(text: string): string[] {\n const out: string[] = [];\n for (const m of text.matchAll(HANGUL_BLOCK_RE)) {\n if (m[0]) out.push(m[0]);\n }\n return out;\n}\n\nexport function segmentCjk(text: string): string[] {\n if (!hasCjk(text)) return [text];\n\n const out: string[] = [];\n let cursor = 0;\n\n for (const m of text.matchAll(CJK_RUN_RE)) {\n const start = m.index ?? 0;\n const run = m[0];\n const end = start + run.length;\n\n if (start > cursor) {\n const piece = text.slice(cursor, start).trim();\n if (piece) out.push(piece);\n }\n\n if (HANGUL_RE.test(run)) {\n out.push(...segmentHangul(run));\n } else if (KANA_RE.test(run)) {\n out.push(...segmentKana(run));\n } else {\n out.push(...segmentHan(run));\n }\n\n cursor = end;\n }\n\n if (cursor < text.length) {\n const trailing = text.slice(cursor).trim();\n if (trailing) out.push(trailing);\n }\n\n return out;\n}\n\nexport function __resetCjkSegmenterStateForTests(): void {\n hintShown.clear();\n jiebaInstance = null;\n jiebaLoaded = false;\n jaSegmenterInstance = null;\n jaSegmenterLoaded = false;\n}\n","import { createHash } from \"node:crypto\";\nimport { hasCjk, segmentCjk } from \"./cjk-segmenter.js\";\n\nexport const KV = {\n sessions: \"mem:sessions\",\n observations: (sessionId: string) => `mem:obs:${sessionId}`,\n memories: \"mem:memories\",\n summaries: \"mem:summaries\",\n config: \"mem:config\",\n metrics: \"mem:metrics\",\n health: \"mem:health\",\n embeddings: (obsId: string) => `mem:emb:${obsId}`,\n bm25Index: \"mem:index:bm25\",\n vectorPendingLog: \"mem:index:vec-pending\",\n relations: \"mem:relations\",\n profiles: \"mem:profiles\",\n claudeBridge: \"mem:claude-bridge\",\n graphNodes: \"mem:graph:nodes\",\n graphEdges: \"mem:graph:edges\",\n graphSnapshot: \"mem:graph:snapshot\",\n graphNameIndex: \"mem:graph:name-index\",\n graphEdgeKey: \"mem:graph:edge-key\",\n graphNodeDegree: \"mem:graph:node-degree\",\n semantic: \"mem:semantic\",\n procedural: \"mem:procedural\",\n teamShared: (teamId: string) => `mem:team:${teamId}:shared`,\n teamUsers: (teamId: string, userId: string) =>\n `mem:team:${teamId}:users:${userId}`,\n teamProfile: (teamId: string) => `mem:team:${teamId}:profile`,\n audit: \"mem:audit\",\n auditMonth: (month: string) => `mem:audit:${month}`,\n auditMonths: \"mem:audit:months\",\n actions: \"mem:actions\",\n actionEdges: \"mem:action-edges\",\n leases: \"mem:leases\",\n routines: \"mem:routines\",\n routineRuns: \"mem:routine-runs\",\n signals: \"mem:signals\",\n checkpoints: \"mem:checkpoints\",\n mesh: \"mem:mesh\",\n sketches: \"mem:sketches\",\n facets: \"mem:facets\",\n sentinels: \"mem:sentinels\",\n crystals: \"mem:crystals\",\n lessons: \"mem:lessons\",\n insights: \"mem:insights\",\n graphEdgeHistory: \"mem:graph:edge-history\",\n enrichedChunks: (sessionId: string) => `mem:enriched:${sessionId}`,\n latentEmbeddings: (obsId: string) => `mem:latent:${obsId}`,\n retentionScores: \"mem:retention\",\n accessLog: \"mem:access\",\n imageRefs: \"mem:image-refs\",\n imageEmbeddings: \"mem:image-embeddings\",\n slots: \"mem:slots\",\n globalSlots: \"mem:slots:global\",\n state: \"mem:state\",\n commits: \"mem:commits\",\n recentSearches: \"mem:recent-searches\",\n projectSessionsIndex: \"mem:idx:project-sessions\",\n obsSessionIndex: (shard: number) => `mem:idx:obs:${shard}`,\n captureInbox: \"mem:capture:inbox\",\n captureEvents: (shard: string) => `mem:capture:events:${shard}`,\n capturePrompts: \"mem:capture:prompts\",\n} as const;\n\nexport const STREAM = {\n name: \"mem-live\",\n viewerGroup: \"viewer\",\n} as const;\n\nexport function generateId(prefix: string): string {\n const ts = Date.now().toString(36);\n const rand = crypto.randomUUID().replace(/-/g, \"\").slice(0, 12);\n return `${prefix}_${ts}_${rand}`;\n}\n\nexport function fingerprintId(prefix: string, content: string): string {\n const hash = createHash(\"sha256\").update(content).digest(\"hex\");\n return `${prefix}_${hash.slice(0, 16)}`;\n}\n\n// CJK/Japanese/Thai text carries no inter-word whitespace, so a plain\n// split(/\\s+/) collapses a whole sentence into one token and the two token\n// sets never overlap — dedup silently stops working. When either input\n// contains CJK we segment the CJK runs (word tokens via the shared\n// segmenter, plus character bigram shingles so near-identical strings still\n// overlap even when the optional segmenter deps fall back to whole-string).\n// ASCII text keeps the original word-level behavior.\nfunction jaccardTokens(text: string): Set<string> {\n if (!hasCjk(text)) {\n return new Set(text.split(/\\s+/).filter((t) => t.length > 2));\n }\n const tokens = new Set<string>();\n for (const raw of text.split(/\\s+/)) {\n if (!raw) continue;\n if (hasCjk(raw)) {\n for (const seg of segmentCjk(raw)) {\n if (seg) tokens.add(seg);\n }\n // Character bigram shingles keep the CJK signal even when the\n // segmenter returns the whole run: overlapping bigrams make\n // near-identical strings match while unrelated ones (\"北京\" vs\n // \"上海\") share none. Never drop short CJK tokens.\n const chars = Array.from(raw);\n if (chars.length === 1) {\n tokens.add(chars[0]);\n } else {\n for (let i = 0; i < chars.length - 1; i++) {\n tokens.add(chars[i] + chars[i + 1]);\n }\n }\n } else if (raw.length > 2) {\n tokens.add(raw);\n }\n }\n return tokens;\n}\n\nexport function jaccardSimilarity(a: string, b: string): number {\n const na = a.normalize(\"NFC\");\n const nb = b.normalize(\"NFC\");\n const setA = jaccardTokens(na);\n const setB = jaccardTokens(nb);\n // An empty token set carries no signal for the overlap metric. Very short\n // ASCII text (\"AI\", \"a b\", \"go\") tokenizes to nothing because words <=2\n // chars are dropped. Fall back to exact normalized equality so re-saving\n // the identical short memory still supersedes, while unrelated short\n // strings (\"AI\" vs \"ML\") correctly score 0. Returning 1 unconditionally\n // here (as an \"both empty\" shortcut once did) would let any short memory\n // falsely supersede another, silently marking a real memory not-latest.\n if (setA.size === 0 || setB.size === 0) {\n return na.trim().replace(/\\s+/g, \" \") === nb.trim().replace(/\\s+/g, \" \")\n ? 1\n : 0;\n }\n let intersection = 0;\n for (const word of setA) {\n if (setB.has(word)) intersection++;\n }\n return intersection / (setA.size + setB.size - intersection);\n}\n","const locks = new Map<string, Promise<void>>();\n\nexport function withKeyedLock<T>(\n key: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const prev = locks.get(key) ?? Promise.resolve();\n const next = prev.then(fn, fn);\n const cleanup = next.then(\n () => {},\n () => {},\n );\n locks.set(key, cleanup);\n cleanup.then(() => {\n if (locks.get(key) === cleanup) locks.delete(key);\n });\n return next;\n}\n\nexport function withKeyedLocks<T>(keys: string[], fn: () => Promise<T>): Promise<T> {\n const ordered = [...new Set(keys)].sort();\n const acquire = (index: number): Promise<T> => index === ordered.length\n ? fn()\n : withKeyedLock(ordered[index], () => acquire(index + 1));\n return acquire(0);\n}\n"],"mappings":"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"}