@remnic/core 9.3.664 → 9.3.665

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Orchestrator
3
- } from "./chunk-LIERUFPO.js";
3
+ } from "./chunk-WH4SKYPX.js";
4
4
  import "./chunk-4SKKVWLQ.js";
5
5
  import "./chunk-7HYPN2GC.js";
6
6
  import "./chunk-666A3MOW.js";
@@ -83,7 +83,7 @@ import "./chunk-AGRPGAKR.js";
83
83
  import "./chunk-S4DDLTPX.js";
84
84
  import "./chunk-SFQ6QNL7.js";
85
85
  import "./chunk-33JBK2XP.js";
86
- import "./chunk-7ILWCUWH.js";
86
+ import "./chunk-D7IXTY5E.js";
87
87
  import "./chunk-KFY3SGN7.js";
88
88
  import {
89
89
  resolvePluginEntry
@@ -104,9 +104,9 @@ declare const recallRequestSchema: z.ZodObject<{
104
104
  includeLowConfidence: z.ZodOptional<z.ZodBoolean>;
105
105
  }, "strip", z.ZodTypeAny, {
106
106
  query: string;
107
+ namespace?: string | undefined;
107
108
  sessionKey?: string | undefined;
108
109
  tags?: string[] | undefined;
109
- namespace?: string | undefined;
110
110
  topK?: number | undefined;
111
111
  mode?: "auto" | "no_recall" | "minimal" | "full" | "graph_mode" | undefined;
112
112
  cwd?: string | undefined;
@@ -125,9 +125,9 @@ declare const recallRequestSchema: z.ZodObject<{
125
125
  tagMatch?: "all" | "any" | undefined;
126
126
  }, {
127
127
  query: string;
128
+ namespace?: string | undefined;
128
129
  sessionKey?: string | undefined;
129
130
  tags?: string[] | undefined;
130
- namespace?: string | undefined;
131
131
  topK?: number | undefined;
132
132
  mode?: "auto" | "no_recall" | "minimal" | "full" | "graph_mode" | undefined;
133
133
  cwd?: string | undefined;
@@ -149,11 +149,11 @@ declare const recallExplainRequestSchema: z.ZodObject<{
149
149
  sessionKey: z.ZodOptional<z.ZodString>;
150
150
  namespace: z.ZodOptional<z.ZodString>;
151
151
  }, "strip", z.ZodTypeAny, {
152
- sessionKey?: string | undefined;
153
152
  namespace?: string | undefined;
154
- }, {
155
153
  sessionKey?: string | undefined;
154
+ }, {
156
155
  namespace?: string | undefined;
156
+ sessionKey?: string | undefined;
157
157
  }>;
158
158
  /**
159
159
  * Standalone "set coding context" request. Used by the HTTP endpoint
@@ -365,30 +365,30 @@ declare const memoryStoreRequestSchema: z.ZodObject<{
365
365
  projectTag: z.ZodOptional<z.ZodString>;
366
366
  }, "strip", z.ZodTypeAny, {
367
367
  content: string;
368
+ namespace?: string | undefined;
369
+ category?: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace" | undefined;
370
+ confidence?: number | undefined;
371
+ ttl?: string | undefined;
368
372
  schemaVersion?: number | undefined;
369
373
  sessionKey?: string | undefined;
370
374
  tags?: string[] | undefined;
371
375
  dryRun?: boolean | undefined;
372
- namespace?: string | undefined;
373
- category?: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace" | undefined;
374
- confidence?: number | undefined;
375
376
  entityRef?: string | undefined;
376
- ttl?: string | undefined;
377
377
  sourceReason?: string | undefined;
378
378
  cwd?: string | undefined;
379
379
  idempotencyKey?: string | undefined;
380
380
  projectTag?: string | undefined;
381
381
  }, {
382
382
  content: string;
383
+ namespace?: string | undefined;
384
+ category?: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace" | undefined;
385
+ confidence?: number | undefined;
386
+ ttl?: string | undefined;
383
387
  schemaVersion?: number | undefined;
384
388
  sessionKey?: string | undefined;
385
389
  tags?: string[] | undefined;
386
390
  dryRun?: boolean | undefined;
387
- namespace?: string | undefined;
388
- category?: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace" | undefined;
389
- confidence?: number | undefined;
390
391
  entityRef?: string | undefined;
391
- ttl?: string | undefined;
392
392
  sourceReason?: string | undefined;
393
393
  cwd?: string | undefined;
394
394
  idempotencyKey?: string | undefined;
@@ -411,30 +411,30 @@ declare const suggestionSubmitRequestSchema: z.ZodObject<{
411
411
  projectTag: z.ZodOptional<z.ZodString>;
412
412
  }, "strip", z.ZodTypeAny, {
413
413
  content: string;
414
+ namespace?: string | undefined;
415
+ category?: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace" | undefined;
416
+ confidence?: number | undefined;
417
+ ttl?: string | undefined;
414
418
  schemaVersion?: number | undefined;
415
419
  sessionKey?: string | undefined;
416
420
  tags?: string[] | undefined;
417
421
  dryRun?: boolean | undefined;
418
- namespace?: string | undefined;
419
- category?: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace" | undefined;
420
- confidence?: number | undefined;
421
422
  entityRef?: string | undefined;
422
- ttl?: string | undefined;
423
423
  sourceReason?: string | undefined;
424
424
  cwd?: string | undefined;
425
425
  idempotencyKey?: string | undefined;
426
426
  projectTag?: string | undefined;
427
427
  }, {
428
428
  content: string;
429
+ namespace?: string | undefined;
430
+ category?: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace" | undefined;
431
+ confidence?: number | undefined;
432
+ ttl?: string | undefined;
429
433
  schemaVersion?: number | undefined;
430
434
  sessionKey?: string | undefined;
431
435
  tags?: string[] | undefined;
432
436
  dryRun?: boolean | undefined;
433
- namespace?: string | undefined;
434
- category?: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace" | undefined;
435
- confidence?: number | undefined;
436
437
  entityRef?: string | undefined;
437
- ttl?: string | undefined;
438
438
  sourceReason?: string | undefined;
439
439
  cwd?: string | undefined;
440
440
  idempotencyKey?: string | undefined;
@@ -468,18 +468,18 @@ declare const trustZonePromoteRequestSchema: z.ZodObject<{
468
468
  recordId: string;
469
469
  targetZone: "working" | "trusted";
470
470
  promotionReason: string;
471
+ namespace?: string | undefined;
471
472
  recordedAt?: string | undefined;
472
473
  summary?: string | undefined;
473
474
  dryRun?: boolean | undefined;
474
- namespace?: string | undefined;
475
475
  }, {
476
476
  recordId: string;
477
477
  targetZone: "working" | "trusted";
478
478
  promotionReason: string;
479
+ namespace?: string | undefined;
479
480
  recordedAt?: string | undefined;
480
481
  summary?: string | undefined;
481
482
  dryRun?: boolean | undefined;
482
- namespace?: string | undefined;
483
483
  }>;
484
484
  declare const trustZoneDemoSeedRequestSchema: z.ZodObject<{
485
485
  scenario: z.ZodOptional<z.ZodString>;
@@ -487,14 +487,14 @@ declare const trustZoneDemoSeedRequestSchema: z.ZodObject<{
487
487
  dryRun: z.ZodOptional<z.ZodBoolean>;
488
488
  namespace: z.ZodOptional<z.ZodString>;
489
489
  }, "strip", z.ZodTypeAny, {
490
+ namespace?: string | undefined;
490
491
  recordedAt?: string | undefined;
491
492
  dryRun?: boolean | undefined;
492
- namespace?: string | undefined;
493
493
  scenario?: string | undefined;
494
494
  }, {
495
+ namespace?: string | undefined;
495
496
  recordedAt?: string | undefined;
496
497
  dryRun?: boolean | undefined;
497
- namespace?: string | undefined;
498
498
  scenario?: string | undefined;
499
499
  }>;
500
500
  declare const lcmSearchRequestSchema: z.ZodObject<{
@@ -505,15 +505,15 @@ declare const lcmSearchRequestSchema: z.ZodObject<{
505
505
  limit: z.ZodOptional<z.ZodNumber>;
506
506
  }, "strip", z.ZodTypeAny, {
507
507
  query: string;
508
- sessionKey?: string | undefined;
509
508
  namespace?: string | undefined;
510
509
  limit?: number | undefined;
510
+ sessionKey?: string | undefined;
511
511
  sessionPrefix?: string | undefined;
512
512
  }, {
513
513
  query: string;
514
- sessionKey?: string | undefined;
515
514
  namespace?: string | undefined;
516
515
  limit?: number | undefined;
516
+ sessionKey?: string | undefined;
517
517
  sessionPrefix?: string | undefined;
518
518
  }>;
519
519
  declare const lcmCompactionFlushRequestSchema: z.ZodObject<{
@@ -548,14 +548,14 @@ declare const daySummaryRequestSchema: z.ZodObject<{
548
548
  namespace: z.ZodOptional<z.ZodString>;
549
549
  timeZone: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
550
550
  }, "strip", z.ZodTypeAny, {
551
+ namespace?: string | undefined;
551
552
  sessionKey?: string | undefined;
552
553
  timeZone?: string | undefined;
553
- namespace?: string | undefined;
554
554
  memories?: string | undefined;
555
555
  }, {
556
+ namespace?: string | undefined;
556
557
  sessionKey?: string | undefined;
557
558
  timeZone?: string | undefined;
558
- namespace?: string | undefined;
559
559
  memories?: string | undefined;
560
560
  }>;
561
561
  declare const capsuleExportRequestSchema: z.ZodObject<{
@@ -603,11 +603,11 @@ declare const capsuleListRequestSchema: z.ZodObject<{
603
603
  namespace: z.ZodOptional<z.ZodString>;
604
604
  sessionKey: z.ZodOptional<z.ZodString>;
605
605
  }, "strip", z.ZodTypeAny, {
606
- sessionKey?: string | undefined;
607
606
  namespace?: string | undefined;
608
- }, {
609
607
  sessionKey?: string | undefined;
608
+ }, {
610
609
  namespace?: string | undefined;
610
+ sessionKey?: string | undefined;
611
611
  }>;
612
612
  declare const offlineSyncSnapshotRequestSchema: z.ZodObject<{
613
613
  namespace: z.ZodOptional<z.ZodString>;
@@ -748,11 +748,11 @@ declare const actionConfidenceRequestSchema: z.ZodObject<{
748
748
  safety: z.ZodEffects<z.ZodNullable<z.ZodOptional<z.ZodEnum<["safe", "requires-review", "blocked"]>>>, NonNullable<"blocked" | "safe" | "requires-review"> | undefined, "blocked" | "safe" | "requires-review" | null | undefined>;
749
749
  safetyReasons: z.ZodEffects<z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>, string[] | undefined, string[] | null | undefined>;
750
750
  }, "strict", z.ZodTypeAny, {
751
+ confidence?: number | undefined;
751
752
  source?: string | undefined;
752
753
  stale?: boolean | undefined;
753
754
  created?: string | undefined;
754
755
  updated?: string | undefined;
755
- confidence?: number | undefined;
756
756
  scope?: string | undefined;
757
757
  retrievalReason?: string | undefined;
758
758
  safety?: NonNullable<"blocked" | "safe" | "requires-review"> | undefined;
@@ -762,11 +762,11 @@ declare const actionConfidenceRequestSchema: z.ZodObject<{
762
762
  safetyReasons?: string[] | undefined;
763
763
  userContextScopes?: string[] | undefined;
764
764
  }, {
765
+ confidence?: number | null | undefined;
765
766
  source?: string | null | undefined;
766
767
  stale?: boolean | null | undefined;
767
768
  created?: string | null | undefined;
768
769
  updated?: string | null | undefined;
769
- confidence?: number | null | undefined;
770
770
  scope?: string | null | undefined;
771
771
  retrievalReason?: string | null | undefined;
772
772
  safety?: "blocked" | "safe" | "requires-review" | null | undefined;
@@ -776,11 +776,11 @@ declare const actionConfidenceRequestSchema: z.ZodObject<{
776
776
  safetyReasons?: string[] | null | undefined;
777
777
  userContextScopes?: string[] | null | undefined;
778
778
  }>, "many">>>, {
779
+ confidence?: number | undefined;
779
780
  source?: string | undefined;
780
781
  stale?: boolean | undefined;
781
782
  created?: string | undefined;
782
783
  updated?: string | undefined;
783
- confidence?: number | undefined;
784
784
  scope?: string | undefined;
785
785
  retrievalReason?: string | undefined;
786
786
  safety?: NonNullable<"blocked" | "safe" | "requires-review"> | undefined;
@@ -790,11 +790,11 @@ declare const actionConfidenceRequestSchema: z.ZodObject<{
790
790
  safetyReasons?: string[] | undefined;
791
791
  userContextScopes?: string[] | undefined;
792
792
  }[] | undefined, {
793
+ confidence?: number | null | undefined;
793
794
  source?: string | null | undefined;
794
795
  stale?: boolean | null | undefined;
795
796
  created?: string | null | undefined;
796
797
  updated?: string | null | undefined;
797
- confidence?: number | null | undefined;
798
798
  scope?: string | null | undefined;
799
799
  retrievalReason?: string | null | undefined;
800
800
  safety?: "blocked" | "safe" | "requires-review" | null | undefined;
@@ -816,11 +816,11 @@ declare const actionConfidenceRequestSchema: z.ZodObject<{
816
816
  matched?: boolean | undefined;
817
817
  }[] | undefined;
818
818
  retrievedMemories?: {
819
+ confidence?: number | undefined;
819
820
  source?: string | undefined;
820
821
  stale?: boolean | undefined;
821
822
  created?: string | undefined;
822
823
  updated?: string | undefined;
823
- confidence?: number | undefined;
824
824
  scope?: string | undefined;
825
825
  retrievalReason?: string | undefined;
826
826
  safety?: NonNullable<"blocked" | "safe" | "requires-review"> | undefined;
@@ -842,11 +842,11 @@ declare const actionConfidenceRequestSchema: z.ZodObject<{
842
842
  matched?: boolean | null | undefined;
843
843
  }[] | null | undefined;
844
844
  retrievedMemories?: {
845
+ confidence?: number | null | undefined;
845
846
  source?: string | null | undefined;
846
847
  stale?: boolean | null | undefined;
847
848
  created?: string | null | undefined;
848
849
  updated?: string | null | undefined;
849
- confidence?: number | null | undefined;
850
850
  scope?: string | null | undefined;
851
851
  retrievalReason?: string | null | undefined;
852
852
  safety?: "blocked" | "safe" | "requires-review" | null | undefined;
@@ -93,12 +93,12 @@ import {
93
93
  runOperatorInventory,
94
94
  runOperatorRepair,
95
95
  runOperatorSetup
96
- } from "./chunk-YKX63GBK.js";
96
+ } from "./chunk-XRSIGVTS.js";
97
97
  import {
98
98
  listNamespaces,
99
99
  runNamespaceMigration,
100
100
  verifyNamespaces
101
- } from "./chunk-XB5P5P2L.js";
101
+ } from "./chunk-7C4MPEPE.js";
102
102
  import {
103
103
  collectPatternMemories,
104
104
  explainPatternMemory,
@@ -7311,4 +7311,4 @@ export {
7311
7311
  listMemoryMarkdownFilePaths,
7312
7312
  registerCli
7313
7313
  };
7314
- //# sourceMappingURL=chunk-NLF54XMD.js.map
7314
+ //# sourceMappingURL=chunk-2OPARZ4B.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  NamespaceStorageRouter
3
- } from "./chunk-7ILWCUWH.js";
3
+ } from "./chunk-D7IXTY5E.js";
4
4
  import {
5
5
  namespaceCollectionName
6
6
  } from "./chunk-DQY7NJ5L.js";
@@ -204,4 +204,4 @@ export {
204
204
  verifyNamespaces,
205
205
  runNamespaceMigration
206
206
  };
207
- //# sourceMappingURL=chunk-XB5P5P2L.js.map
207
+ //# sourceMappingURL=chunk-7C4MPEPE.js.map
@@ -126,6 +126,11 @@ var NamespaceStorageRouter = class {
126
126
  // entry is always removed when the promise settles, so the map cannot grow
127
127
  // unbounded (one transient entry per concurrently-resolving namespace).
128
128
  inFlightResolved = /* @__PURE__ */ new Map();
129
+ // Tracks every in-flight resolve-hook promise so callers can deterministically
130
+ // await the fire-and-forget registrations that `storageFor()` kicks off (see
131
+ // `whenResolveHooksSettled`). Entries are removed as each hook settles, so the
132
+ // set holds at most one promise per concurrently-resolving namespace.
133
+ pendingResolveHooks = /* @__PURE__ */ new Set();
129
134
  // Normalized (trimmed) default namespace identity (NH-FH). `storageFor`
130
135
  // normalizes its input, so default-namespace branches must compare against the
131
136
  // normalized config default too — otherwise a whitespace-padded configured
@@ -180,7 +185,9 @@ var NamespaceStorageRouter = class {
180
185
  if (this.inFlightResolved.get(namespace) === storageDir) return;
181
186
  try {
182
187
  this.inFlightResolved.set(namespace, storageDir);
183
- Promise.resolve(hook(namespace, storageDir)).then(
188
+ const hookResult = Promise.resolve(hook(namespace, storageDir));
189
+ this.pendingResolveHooks.add(hookResult);
190
+ hookResult.then(
184
191
  (persisted) => {
185
192
  if (this.inFlightResolved.get(namespace) === storageDir) {
186
193
  this.inFlightResolved.delete(namespace);
@@ -188,6 +195,7 @@ var NamespaceStorageRouter = class {
188
195
  if (persisted !== false) {
189
196
  this.notifiedResolved.set(namespace, storageDir);
190
197
  }
198
+ this.pendingResolveHooks.delete(hookResult);
191
199
  },
192
200
  () => {
193
201
  if (this.inFlightResolved.get(namespace) === storageDir) {
@@ -196,6 +204,7 @@ var NamespaceStorageRouter = class {
196
204
  if (this.notifiedResolved.get(namespace) === storageDir) {
197
205
  this.notifiedResolved.delete(namespace);
198
206
  }
207
+ this.pendingResolveHooks.delete(hookResult);
199
208
  }
200
209
  );
201
210
  } catch {
@@ -204,6 +213,22 @@ var NamespaceStorageRouter = class {
204
213
  }
205
214
  }
206
215
  }
216
+ /**
217
+ * Resolve once every in-flight `onResolve` registration has settled.
218
+ *
219
+ * `storageFor()` fires the resolve hook fire-and-forget, so its catalog side
220
+ * effect (e.g. `registerResolved(...)`) is not observable the moment
221
+ * `storageFor()` returns. Callers that must act on that side effect — notably
222
+ * tests asserting the catalog was updated — should await this instead of
223
+ * racing a timer. Resolves immediately when no hook is registered or nothing
224
+ * is in flight. The loop re-checks because a settling hook could, in
225
+ * principle, trigger a follow-on resolution.
226
+ */
227
+ async whenResolveHooksSettled() {
228
+ while (this.pendingResolveHooks.size > 0) {
229
+ await Promise.allSettled([...this.pendingResolveHooks]);
230
+ }
231
+ }
207
232
  };
208
233
 
209
234
  export {
@@ -211,4 +236,4 @@ export {
211
236
  resolveNamespaceStorageRoot,
212
237
  NamespaceStorageRouter
213
238
  };
214
- //# sourceMappingURL=chunk-7ILWCUWH.js.map
239
+ //# sourceMappingURL=chunk-D7IXTY5E.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/namespaces/storage.ts"],"sourcesContent":["import path from \"node:path\";\nimport { access, lstat, readdir } from \"node:fs/promises\";\nimport { isSafeRouteNamespace } from \"../routing/engine.js\";\nimport { StorageManager } from \"../storage.js\";\nimport type { PluginConfig } from \"../types.js\";\nimport { ALL_CATEGORY_DIRS } from \"../utils/category-dir.js\";\nimport { namespaceIdentityToken, normalizeNamespaceIdentity } from \"./identity.js\";\n\nasync function exists(p: string): Promise<boolean> {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function hasStoredEntries(p: string): Promise<boolean> {\n try {\n const entry = await lstat(p);\n if (entry.isSymbolicLink()) return true;\n if (!entry.isDirectory()) return true;\n const children = await readdir(p, { withFileTypes: true });\n for (const child of children) {\n const childPath = path.join(p, child.name);\n if (child.isSymbolicLink() || child.isFile()) return true;\n if (child.isDirectory() && (await hasStoredEntries(childPath))) return true;\n }\n return false;\n } catch {\n return false;\n }\n}\n\n// Build a per-namespace directory under `<memoryDir>/namespaces` and assert the\n// resolved path stays inside that base. Namespace identifiers can originate from\n// operator config (config.defaultNamespace) and request-derived routing, so this\n// containment check prevents directory traversal (CodeQL js/path-injection).\n// For safe segments this returns exactly `path.join(base, segment)`, so there is\n// no behavioral change for valid namespaces.\nfunction resolveNamespaceDir(memoryDir: string, segment: string): string {\n // Mirror isSafeRouteNamespace's separator/parent-ref rejection (without its\n // 64-char cap, so identity tokens still pass). Rejecting separators and \"..\"\n // up front keeps the value a single contained child of <memoryDir>/namespaces.\n if (\n segment.length === 0 ||\n segment.includes(\"/\") ||\n segment.includes(\"\\\\\") ||\n segment.includes(\"..\") ||\n path.isAbsolute(segment)\n ) {\n throw new Error(`unsafe namespace path segment: ${segment}`);\n }\n return path.join(memoryDir, \"namespaces\", segment);\n}\n\nconst LEGACY_NAMESPACE_CONTENT_CHILDREN = [\n ...ALL_CATEGORY_DIRS,\n \"entities\",\n \"artifacts\",\n \"identity\",\n \"config\",\n \"summaries\",\n \"profile.md\",\n] as const;\n\nconst LEGACY_NAMESPACE_RUNTIME_CHILDREN = [\"state\"] as const;\n\nasync function hasAnyLegacyData(\n rootDir: string,\n options: { includeRuntimeState?: boolean } = {},\n): Promise<boolean> {\n const children = options.includeRuntimeState === true\n ? [...LEGACY_NAMESPACE_CONTENT_CHILDREN, ...LEGACY_NAMESPACE_RUNTIME_CHILDREN]\n : LEGACY_NAMESPACE_CONTENT_CHILDREN;\n for (const child of children) {\n if (await hasStoredEntries(path.join(rootDir, child))) return true;\n }\n return false;\n}\n\nasync function hasAnyNamespaceStorageMarker(\n rootDir: string,\n options: { includeRuntimeState?: boolean } = {},\n): Promise<boolean> {\n const children = options.includeRuntimeState === true\n ? [...LEGACY_NAMESPACE_CONTENT_CHILDREN, ...LEGACY_NAMESPACE_RUNTIME_CHILDREN]\n : LEGACY_NAMESPACE_CONTENT_CHILDREN;\n for (const child of children) {\n if (await exists(path.join(rootDir, child))) return true;\n }\n return false;\n}\n\n/**\n * Storage routing for namespaces.\n *\n * Compatibility note:\n * - When namespaces are enabled, existing raw namespace roots are preserved.\n * New namespace roots use tokenized names under `memoryDir/namespaces/<token>`.\n * - The default namespace continues to use the legacy `memoryDir` root unless the caller\n * has created `memoryDir/namespaces/<defaultNamespace>` (in which case we use that).\n *\n * This avoids surprising \"lost memories\" when an install flips namespaces on without\n * migrating existing data.\n */\n/**\n * Optional hooks for the storage router. `onResolve` fires whenever a namespace's\n * storage is resolved/created, so a downstream consumer (e.g. the namespace\n * catalog, issue #1499) can register the namespace. The hook MUST NOT throw into\n * the router; the router invokes it defensively and a hook failure never affects\n * storage resolution.\n *\n * The hook MAY return (or resolve to) a boolean indicating whether the\n * registration actually PERSISTED (round 6, codex P2 — NEFoX). When it resolves\n * to `false` (a dropped/no-op registration), the router does NOT mark the\n * (namespace, storageDir) pair as notified, so the next resolve RETRIES it\n * instead of suppressing it forever. A `void`/`undefined` result is treated as\n * success (legacy hooks).\n */\nexport interface NamespaceStorageRouterHooks {\n onResolve?: (\n namespace: string,\n storageDir: string,\n ) => void | boolean | Promise<void | boolean>;\n}\n\n/**\n * Resolve the runtime storage root for the configured DEFAULT namespace.\n *\n * Shared between the live router (`NamespaceStorageRouter.defaultNamespaceRoot`)\n * and the rebuildable catalog (`NamespaceCatalog.rebuildFromDisk`) so the two\n * can never diverge (CLAUDE.md rule #22/#42 — read & write paths resolve through\n * the same logic). The contract is: while legacy memory data still lives\n * directly under `memoryDir`, the default root stays `memoryDir`; only once the\n * legacy root is empty and a `namespaces/<default|token>` dir holds data does\n * the default migrate into that tokenized/legacy-named dir.\n */\nexport async function resolveDefaultNamespaceRoot(config: PluginConfig): Promise<string> {\n if (!config.namespacesEnabled) {\n return config.memoryDir;\n }\n\n // Build the legacy default root from the NORMALIZED (trimmed) name so a\n // whitespace-padded `defaultNamespace` still finds the live `namespaces/default`\n // root (NIabe). `storageFor()` classifies the trimmed value as the default, and\n // the on-disk legacy dir is created under the trimmed name; using the raw spaced\n // name here would look for `namespaces/<spaced>` and miss the real root, falling\n // back to memoryDir/tokenized. `namespaceIdentityToken` already normalizes\n // internally, so the tokenized path is unaffected.\n const defaultIdentity = normalizeNamespaceIdentity(config.defaultNamespace);\n const legacyNsDir = resolveNamespaceDir(config.memoryDir, defaultIdentity);\n const tokenizedNsDir = resolveNamespaceDir(\n config.memoryDir,\n namespaceIdentityToken(config.defaultNamespace),\n );\n const tokenizedHasData =\n (await exists(tokenizedNsDir)) &&\n (await hasAnyNamespaceStorageMarker(tokenizedNsDir, { includeRuntimeState: true }));\n const nsDir = tokenizedHasData\n ? tokenizedNsDir\n : (await exists(legacyNsDir))\n ? legacyNsDir\n : tokenizedNsDir;\n return (await exists(nsDir)) && !(await hasAnyLegacyData(config.memoryDir))\n ? nsDir\n : config.memoryDir;\n}\n\n/**\n * Resolve the runtime storage root for ANY namespace exactly as the live router\n * would (`NamespaceStorageRouter.namespaceRoot`). Shared so the rebuildable\n * catalog records the SAME on-disk root the router routes to — a recall/read\n * touch must not guess `namespaces/<token>` when the router actually serves a\n * legacy raw-name dir or a migrated default root (CLAUDE.md rule #22/#42; round\n * 4, cursor Medium). The default namespace delegates to `resolveDefaultNamespaceRoot`;\n * every other namespace prefers the tokenized root when it has a storage marker,\n * else a legacy raw-name dir when present, else the tokenized root.\n */\nexport async function resolveNamespaceStorageRoot(\n config: PluginConfig,\n namespace: string,\n): Promise<string> {\n if (!config.namespacesEnabled) return config.memoryDir;\n // Compare on NORMALIZED identity so a whitespace-padded configured default name\n // still routes to the default root rather than a tokenized non-default dir\n // (NH-FH). The catalog keys records by the same normalized identity.\n if (normalizeNamespaceIdentity(namespace) === normalizeNamespaceIdentity(config.defaultNamespace)) {\n return resolveDefaultNamespaceRoot(config);\n }\n const legacyRoot = resolveNamespaceDir(config.memoryDir, namespace);\n const tokenizedRoot = resolveNamespaceDir(config.memoryDir, namespaceIdentityToken(namespace));\n if (\n (await exists(tokenizedRoot)) &&\n (await hasAnyNamespaceStorageMarker(tokenizedRoot, { includeRuntimeState: true }))\n ) {\n return tokenizedRoot;\n }\n return (await exists(legacyRoot)) ? legacyRoot : tokenizedRoot;\n}\n\nexport class NamespaceStorageRouter {\n private readonly cache = new Map<string, StorageManager>();\n private defaultNsRootResolved: string | null = null;\n // Dedup the resolve hook (round 6, cursor Medium — NCNL2). Recall/extraction\n // call `storageFor` repeatedly; firing `onResolve` (→ catalog loadCompacted +\n // append) on every cache hit grows `namespaces.jsonl` without bound between\n // rebuilds. We fire the hook only when the (namespace, storageDir) pair is new\n // or its dir changed, so a steady-state cache hit is a no-op for the catalog.\n private readonly notifiedResolved = new Map<string, string>();\n // In-flight resolve-hook dedup (NFJV-, codex P2). The catalog's `onResolve`\n // hook is ASYNC (it returns `registerResolved(...)`), so `notifiedResolved` is\n // only set after the hook's promise SETTLES. Without tracking the in-flight\n // window, a burst of `storageFor()` cache hits for the SAME namespace before\n // the first registration finishes would each pass the `notifiedResolved` guard\n // and fire their OWN `onResolve` — queueing N duplicate catalog touches + lock\n // acquisitions despite the once-per-namespace intent. We therefore record the\n // (namespace → storageDir) being registered BEFORE awaiting the hook so a\n // concurrent call for the same pair skips firing. On SUCCESS the pair is\n // promoted to `notifiedResolved` (future calls skip permanently); on `false`\n // (dropped touch — e.g. rebuild-lock timeout) OR rejection the in-flight marker\n // is CLEARED so a later `storageFor()` can RETRY the dropped registration. The\n // entry is always removed when the promise settles, so the map cannot grow\n // unbounded (one transient entry per concurrently-resolving namespace).\n private readonly inFlightResolved = new Map<string, string>();\n // Tracks every in-flight resolve-hook promise so callers can deterministically\n // await the fire-and-forget registrations that `storageFor()` kicks off (see\n // `whenResolveHooksSettled`). Entries are removed as each hook settles, so the\n // set holds at most one promise per concurrently-resolving namespace.\n private readonly pendingResolveHooks = new Set<Promise<unknown>>();\n\n // Normalized (trimmed) default namespace identity (NH-FH). `storageFor`\n // normalizes its input, so default-namespace branches must compare against the\n // normalized config default too — otherwise a whitespace-padded configured\n // default name routes the default namespace to a tokenized non-default root.\n private readonly defaultNamespaceIdentity: string;\n\n constructor(\n private readonly config: PluginConfig,\n private readonly hooks: NamespaceStorageRouterHooks = {},\n ) {\n this.defaultNamespaceIdentity = normalizeNamespaceIdentity(config.defaultNamespace);\n }\n\n private async defaultNamespaceRoot(): Promise<string> {\n this.defaultNsRootResolved = await resolveDefaultNamespaceRoot(this.config);\n return this.defaultNsRootResolved;\n }\n\n private async namespaceRoot(namespace: string): Promise<string> {\n // NOTE: only used after defaultNamespaceRoot() resolution.\n if (!this.config.namespacesEnabled) return this.config.memoryDir;\n if (normalizeNamespaceIdentity(namespace) === this.defaultNamespaceIdentity) {\n return this.defaultNsRootResolved ?? this.config.memoryDir;\n }\n return resolveNamespaceStorageRoot(this.config, namespace);\n }\n\n async storageFor(namespace: string): Promise<StorageManager> {\n const ns = normalizeNamespaceIdentity(namespace || this.config.defaultNamespace);\n if (ns !== this.defaultNamespaceIdentity && !isSafeRouteNamespace(ns)) {\n throw new Error(`unsafe namespace: ${ns}`);\n }\n // Even when the default namespace is exempt from the check above, every\n // on-disk path is built through resolveNamespaceDir(), which rejects\n // traversal segments — so an unsafe configured default still cannot escape\n // <memoryDir>/namespaces (CodeQL js/path-injection).\n\n let root: string;\n if (ns === this.defaultNamespaceIdentity) {\n root = await this.defaultNamespaceRoot();\n const cached = this.cache.get(ns);\n if (cached && cached.dir === root) {\n this.notifyResolved(ns, root);\n return cached;\n }\n } else {\n const cached = this.cache.get(ns);\n root = await this.namespaceRoot(ns);\n if (cached && cached.dir === root) {\n this.notifyResolved(ns, root);\n return cached;\n }\n }\n\n const sm = new StorageManager(root, this.config.entitySchemas);\n // Propagate the inline-attribution template so that router-created storages\n // (used by extraction and shared-promotion paths) strip citations consistently,\n // matching the behaviour of the primary this.storage instance in the orchestrator.\n sm.citationTemplate = this.config.inlineSourceAttributionFormat;\n this.cache.set(ns, sm);\n this.notifyResolved(ns, root);\n return sm;\n }\n\n /**\n * Fire the resolve hook defensively. A hook failure (e.g. a catalog write\n * error) MUST NOT crash storage resolution — see CLAUDE.md gotcha #13.\n */\n private notifyResolved(namespace: string, storageDir: string): void {\n const hook = this.hooks.onResolve;\n if (!hook) return;\n // Skip when we've already SUCCESSFULLY notified this exact (namespace,\n // storageDir) — a steady-state cache hit must not re-append to the catalog\n // log (NCNL2). A changed dir (rare: migration/realignment) still re-fires\n // once. We mark the pair as notified ONLY AFTER the hook succeeds, and CLEAR\n // it on failure, so a dropped registration (e.g. rebuild-lock timeout) is\n // RETRIED on the next cache hit instead of being suppressed forever (round 6,\n // cursor Medium — ND3EJ).\n if (this.notifiedResolved.get(namespace) === storageDir) return;\n // In-flight dedup (NFJV-, codex P2): if a registration for this exact\n // (namespace, storageDir) is already AWAITING its async hook, do not fire a\n // second one. Without this, concurrent cache-hit bursts before the first\n // append settles each pass the `notifiedResolved` guard above and queue\n // duplicate catalog touches/lock acquisitions. A pair with a DIFFERENT\n // in-flight dir (rare mid-migration realignment) still fires once.\n if (this.inFlightResolved.get(namespace) === storageDir) return;\n try {\n // Handle BOTH synchronous throws and asynchronous rejections (round 6,\n // codex P2 — NDo8C). The hook may be `async`; its rejected promise would\n // bypass this try/catch and, where unhandled rejections are fatal, crash\n // storage resolution. Mark the dedup pair as notified ONLY when the hook\n // resolves to a PERSISTED result (round 6, codex P2 — NEFoX): a result of\n // `false` means the registration was dropped/no-op (e.g. rebuild-lock\n // timeout), so we must NOT suppress its retry. `void`/`undefined` is treated\n // as success for legacy hooks. On rejection we leave it un-notified to retry.\n //\n // Record the in-flight marker BEFORE awaiting so concurrent calls for the\n // same pair skip (NFJV-). It is always cleared once the promise settles, so\n // the map holds at most one transient entry per concurrently-resolving\n // namespace and cannot grow unbounded.\n this.inFlightResolved.set(namespace, storageDir);\n const hookResult = Promise.resolve(hook(namespace, storageDir));\n // Track the in-flight promise so `whenResolveHooksSettled()` can await it.\n this.pendingResolveHooks.add(hookResult);\n hookResult.then(\n (persisted) => {\n // Clear the in-flight marker ONLY if it is still ours (a newer resolve\n // for a different dir may have replaced it).\n if (this.inFlightResolved.get(namespace) === storageDir) {\n this.inFlightResolved.delete(namespace);\n }\n if (persisted !== false) {\n this.notifiedResolved.set(namespace, storageDir);\n }\n // On `false` (dropped touch) we intentionally do NOT mark notified, so\n // a later `storageFor()` retries the registration. Clearing the\n // in-flight marker above is what re-enables that retry.\n this.pendingResolveHooks.delete(hookResult);\n },\n () => {\n // Registration failed — clear in-flight AND do NOT mark as notified, so\n // it is retried on the next cache hit.\n if (this.inFlightResolved.get(namespace) === storageDir) {\n this.inFlightResolved.delete(namespace);\n }\n if (this.notifiedResolved.get(namespace) === storageDir) {\n this.notifiedResolved.delete(namespace);\n }\n this.pendingResolveHooks.delete(hookResult);\n },\n );\n } catch {\n // Synchronous throw: clear any in-flight marker we just set and leave the\n // pair un-notified so a later resolve retries.\n if (this.inFlightResolved.get(namespace) === storageDir) {\n this.inFlightResolved.delete(namespace);\n }\n }\n }\n\n /**\n * Resolve once every in-flight `onResolve` registration has settled.\n *\n * `storageFor()` fires the resolve hook fire-and-forget, so its catalog side\n * effect (e.g. `registerResolved(...)`) is not observable the moment\n * `storageFor()` returns. Callers that must act on that side effect — notably\n * tests asserting the catalog was updated — should await this instead of\n * racing a timer. Resolves immediately when no hook is registered or nothing\n * is in flight. The loop re-checks because a settling hook could, in\n * principle, trigger a follow-on resolution.\n */\n async whenResolveHooksSettled(): Promise<void> {\n while (this.pendingResolveHooks.size > 0) {\n await Promise.allSettled([...this.pendingResolveHooks]);\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -236,7 +236,7 @@ import {
236
236
  NamespaceStorageRouter,
237
237
  resolveDefaultNamespaceRoot,
238
238
  resolveNamespaceStorageRoot
239
- } from "./chunk-7ILWCUWH.js";
239
+ } from "./chunk-D7IXTY5E.js";
240
240
  import {
241
241
  isAboveImportanceThreshold,
242
242
  scoreImportance
@@ -17357,4 +17357,4 @@ export {
17357
17357
  resolvePersistedMemoryRelativePath,
17358
17358
  Orchestrator
17359
17359
  };
17360
- //# sourceMappingURL=chunk-LIERUFPO.js.map
17360
+ //# sourceMappingURL=chunk-WH4SKYPX.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  listNamespaces
3
- } from "./chunk-XB5P5P2L.js";
3
+ } from "./chunk-7C4MPEPE.js";
4
4
  import {
5
5
  runConsolidationProvenanceCheck
6
6
  } from "./chunk-7XH7VJN4.js";
@@ -1336,4 +1336,4 @@ export {
1336
1336
  runBenchmarkRecall,
1337
1337
  runOperatorRepair
1338
1338
  };
1339
- //# sourceMappingURL=chunk-YKX63GBK.js.map
1339
+ //# sourceMappingURL=chunk-XRSIGVTS.js.map
package/dist/cli.js CHANGED
@@ -90,7 +90,7 @@ import {
90
90
  runWorkProductStatusCliCommand,
91
91
  runWorkProjectCliCommand,
92
92
  runWorkTaskCliCommand
93
- } from "./chunk-NLF54XMD.js";
93
+ } from "./chunk-2OPARZ4B.js";
94
94
  import "./chunk-MC4FJXPA.js";
95
95
  import "./chunk-LQHDIS7L.js";
96
96
  import "./chunk-7F7Z6MOS.js";
@@ -123,8 +123,8 @@ import "./chunk-IHG6CC7T.js";
123
123
  import "./chunk-TGQ2NTWH.js";
124
124
  import "./chunk-PEPHBH2W.js";
125
125
  import "./chunk-SXYCVRLK.js";
126
- import "./chunk-YKX63GBK.js";
127
- import "./chunk-XB5P5P2L.js";
126
+ import "./chunk-XRSIGVTS.js";
127
+ import "./chunk-7C4MPEPE.js";
128
128
  import "./chunk-3T74IZB3.js";
129
129
  import "./chunk-HL4DB7TO.js";
130
130
  import "./chunk-YNDLCWXS.js";
@@ -145,7 +145,7 @@ import "./chunk-AGRPGAKR.js";
145
145
  import "./chunk-S4DDLTPX.js";
146
146
  import "./chunk-SFQ6QNL7.js";
147
147
  import "./chunk-33JBK2XP.js";
148
- import "./chunk-7ILWCUWH.js";
148
+ import "./chunk-D7IXTY5E.js";
149
149
  import "./chunk-KFY3SGN7.js";
150
150
  import "./chunk-CMTINOFS.js";
151
151
  import "./chunk-TQNRI55H.js";
package/dist/index.js CHANGED
@@ -94,7 +94,7 @@ import {
94
94
  registerTrainingExportAdapter,
95
95
  runBulkImportCliCommand,
96
96
  runWearablesCliCommand
97
- } from "./chunk-NLF54XMD.js";
97
+ } from "./chunk-2OPARZ4B.js";
98
98
  import "./chunk-MC4FJXPA.js";
99
99
  import "./chunk-LQHDIS7L.js";
100
100
  import "./chunk-7F7Z6MOS.js";
@@ -147,8 +147,8 @@ import {
147
147
  parseXrayBudgetFlag,
148
148
  parseXrayCliOptions
149
149
  } from "./chunk-SXYCVRLK.js";
150
- import "./chunk-YKX63GBK.js";
151
- import "./chunk-XB5P5P2L.js";
150
+ import "./chunk-XRSIGVTS.js";
151
+ import "./chunk-7C4MPEPE.js";
152
152
  import {
153
153
  parseStrictCliDate
154
154
  } from "./chunk-3T74IZB3.js";
@@ -192,7 +192,7 @@ import {
192
192
  saveTaxonomy,
193
193
  validateSlug,
194
194
  validateTaxonomy
195
- } from "./chunk-LIERUFPO.js";
195
+ } from "./chunk-WH4SKYPX.js";
196
196
  import {
197
197
  WEARABLE_SOURCE_PREFIX,
198
198
  buildExtractionTurns,
@@ -399,7 +399,7 @@ import {
399
399
  } from "./chunk-S4DDLTPX.js";
400
400
  import "./chunk-SFQ6QNL7.js";
401
401
  import "./chunk-33JBK2XP.js";
402
- import "./chunk-7ILWCUWH.js";
402
+ import "./chunk-D7IXTY5E.js";
403
403
  import "./chunk-KFY3SGN7.js";
404
404
  import {
405
405
  resolvePluginEntry
@@ -2,8 +2,8 @@ import {
2
2
  listNamespaces,
3
3
  runNamespaceMigration,
4
4
  verifyNamespaces
5
- } from "../chunk-XB5P5P2L.js";
6
- import "../chunk-7ILWCUWH.js";
5
+ } from "../chunk-7C4MPEPE.js";
6
+ import "../chunk-D7IXTY5E.js";
7
7
  import "../chunk-DQY7NJ5L.js";
8
8
  import "../chunk-ROHLEUTH.js";
9
9
  import "../chunk-DOCTITOP.js";
@@ -68,6 +68,7 @@ declare class NamespaceStorageRouter {
68
68
  private defaultNsRootResolved;
69
69
  private readonly notifiedResolved;
70
70
  private readonly inFlightResolved;
71
+ private readonly pendingResolveHooks;
71
72
  private readonly defaultNamespaceIdentity;
72
73
  constructor(config: PluginConfig, hooks?: NamespaceStorageRouterHooks);
73
74
  private defaultNamespaceRoot;
@@ -78,6 +79,18 @@ declare class NamespaceStorageRouter {
78
79
  * error) MUST NOT crash storage resolution — see CLAUDE.md gotcha #13.
79
80
  */
80
81
  private notifyResolved;
82
+ /**
83
+ * Resolve once every in-flight `onResolve` registration has settled.
84
+ *
85
+ * `storageFor()` fires the resolve hook fire-and-forget, so its catalog side
86
+ * effect (e.g. `registerResolved(...)`) is not observable the moment
87
+ * `storageFor()` returns. Callers that must act on that side effect — notably
88
+ * tests asserting the catalog was updated — should await this instead of
89
+ * racing a timer. Resolves immediately when no hook is registered or nothing
90
+ * is in flight. The loop re-checks because a settling hook could, in
91
+ * principle, trigger a follow-on resolution.
92
+ */
93
+ whenResolveHooksSettled(): Promise<void>;
81
94
  }
82
95
 
83
96
  export { NamespaceStorageRouter, type NamespaceStorageRouterHooks, resolveDefaultNamespaceRoot, resolveNamespaceStorageRoot };
@@ -2,7 +2,7 @@ import {
2
2
  NamespaceStorageRouter,
3
3
  resolveDefaultNamespaceRoot,
4
4
  resolveNamespaceStorageRoot
5
- } from "../chunk-7ILWCUWH.js";
5
+ } from "../chunk-D7IXTY5E.js";
6
6
  import "../chunk-ZFXCQPNO.js";
7
7
  import "../chunk-VH6EIKVS.js";
8
8
  import "../chunk-M7XQSUBB.js";
@@ -11,11 +11,11 @@ import {
11
11
  summarizeMemoryWorthLegacyCounters,
12
12
  summarizeObservationThroughput,
13
13
  summarizeTierDistribution
14
- } from "./chunk-YKX63GBK.js";
15
- import "./chunk-XB5P5P2L.js";
14
+ } from "./chunk-XRSIGVTS.js";
15
+ import "./chunk-7C4MPEPE.js";
16
16
  import "./chunk-7XH7VJN4.js";
17
17
  import "./chunk-YBPYIAA5.js";
18
- import "./chunk-7ILWCUWH.js";
18
+ import "./chunk-D7IXTY5E.js";
19
19
  import "./chunk-KFY3SGN7.js";
20
20
  import "./chunk-CMTINOFS.js";
21
21
  import "./chunk-BEUDU7Y4.js";
@@ -28,7 +28,7 @@ import {
28
28
  sanitizeSessionKeyForFilename,
29
29
  shouldFilterLifecycleRecallCandidate,
30
30
  summarizeGraphShadowComparison
31
- } from "./chunk-LIERUFPO.js";
31
+ } from "./chunk-WH4SKYPX.js";
32
32
  import "./chunk-4SKKVWLQ.js";
33
33
  import "./chunk-7HYPN2GC.js";
34
34
  import "./chunk-666A3MOW.js";
@@ -111,7 +111,7 @@ import "./chunk-AGRPGAKR.js";
111
111
  import "./chunk-S4DDLTPX.js";
112
112
  import "./chunk-SFQ6QNL7.js";
113
113
  import "./chunk-33JBK2XP.js";
114
- import "./chunk-7ILWCUWH.js";
114
+ import "./chunk-D7IXTY5E.js";
115
115
  import "./chunk-KFY3SGN7.js";
116
116
  import "./chunk-TQNRI55H.js";
117
117
  import "./chunk-6GUG4YNM.js";
package/dist/schemas.d.ts CHANGED
@@ -7,13 +7,13 @@ declare const MemoryActionEligibilityContextSchema: z.ZodObject<{
7
7
  importance: z.ZodNumber;
8
8
  source: z.ZodEnum<["extraction", "consolidation", "replay", "manual", "unknown"]>;
9
9
  }, "strict", z.ZodTypeAny, {
10
- source: "manual" | "extraction" | "consolidation" | "replay" | "unknown";
11
10
  confidence: number;
11
+ source: "unknown" | "manual" | "extraction" | "consolidation" | "replay";
12
12
  lifecycleState: "active" | "archived" | "candidate" | "validated" | "stale";
13
13
  importance: number;
14
14
  }, {
15
- source: "manual" | "extraction" | "consolidation" | "replay" | "unknown";
16
15
  confidence: number;
16
+ source: "unknown" | "manual" | "extraction" | "consolidation" | "replay";
17
17
  lifecycleState: "active" | "archived" | "candidate" | "validated" | "stale";
18
18
  importance: number;
19
19
  }>;
@@ -149,10 +149,10 @@ declare const ExtractedFactSchema: z.ZodEffects<z.ZodObject<{
149
149
  observed_outcome?: string | null | undefined;
150
150
  }>>>;
151
151
  }, "strip", z.ZodTypeAny, {
152
- tags: string[];
153
- content: string;
154
152
  category: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace";
153
+ content: string;
155
154
  confidence: number;
155
+ tags: string[];
156
156
  entityRef?: string | null | undefined;
157
157
  structuredAttributes?: Record<string, string> | null | undefined;
158
158
  promptedByQuestion?: string | null | undefined;
@@ -178,10 +178,10 @@ declare const ExtractedFactSchema: z.ZodEffects<z.ZodObject<{
178
178
  observed_outcome?: string | null | undefined;
179
179
  } | null | undefined;
180
180
  }, {
181
- tags: string[];
182
- content: string;
183
181
  category: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace";
182
+ content: string;
184
183
  confidence: number;
184
+ tags: string[];
185
185
  entityRef?: string | null | undefined;
186
186
  structuredAttributes?: Record<string, string> | null | undefined;
187
187
  promptedByQuestion?: string | null | undefined;
@@ -207,10 +207,10 @@ declare const ExtractedFactSchema: z.ZodEffects<z.ZodObject<{
207
207
  observed_outcome?: string | null | undefined;
208
208
  } | null | undefined;
209
209
  }>, {
210
- tags: string[];
211
- content: string;
212
210
  category: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace";
211
+ content: string;
213
212
  confidence: number;
213
+ tags: string[];
214
214
  entityRef?: string | null | undefined;
215
215
  structuredAttributes?: Record<string, string> | null | undefined;
216
216
  promptedByQuestion?: string | null | undefined;
@@ -236,10 +236,10 @@ declare const ExtractedFactSchema: z.ZodEffects<z.ZodObject<{
236
236
  observed_outcome?: string | null | undefined;
237
237
  } | null | undefined;
238
238
  }, {
239
- tags: string[];
240
- content: string;
241
239
  category: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace";
240
+ content: string;
242
241
  confidence: number;
242
+ tags: string[];
243
243
  entityRef?: string | null | undefined;
244
244
  structuredAttributes?: Record<string, string> | null | undefined;
245
245
  promptedByQuestion?: string | null | undefined;
@@ -457,10 +457,10 @@ declare const ProactiveExtractionResultSchema: z.ZodObject<{
457
457
  observed_outcome?: string | null | undefined;
458
458
  }>>>;
459
459
  }, "strip", z.ZodTypeAny, {
460
- tags: string[];
461
- content: string;
462
460
  category: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace";
461
+ content: string;
463
462
  confidence: number;
463
+ tags: string[];
464
464
  entityRef?: string | null | undefined;
465
465
  structuredAttributes?: Record<string, string> | null | undefined;
466
466
  promptedByQuestion?: string | null | undefined;
@@ -486,10 +486,10 @@ declare const ProactiveExtractionResultSchema: z.ZodObject<{
486
486
  observed_outcome?: string | null | undefined;
487
487
  } | null | undefined;
488
488
  }, {
489
- tags: string[];
490
- content: string;
491
489
  category: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace";
490
+ content: string;
492
491
  confidence: number;
492
+ tags: string[];
493
493
  entityRef?: string | null | undefined;
494
494
  structuredAttributes?: Record<string, string> | null | undefined;
495
495
  promptedByQuestion?: string | null | undefined;
@@ -515,10 +515,10 @@ declare const ProactiveExtractionResultSchema: z.ZodObject<{
515
515
  observed_outcome?: string | null | undefined;
516
516
  } | null | undefined;
517
517
  }>, {
518
- tags: string[];
519
- content: string;
520
518
  category: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace";
519
+ content: string;
521
520
  confidence: number;
521
+ tags: string[];
522
522
  entityRef?: string | null | undefined;
523
523
  structuredAttributes?: Record<string, string> | null | undefined;
524
524
  promptedByQuestion?: string | null | undefined;
@@ -544,10 +544,10 @@ declare const ProactiveExtractionResultSchema: z.ZodObject<{
544
544
  observed_outcome?: string | null | undefined;
545
545
  } | null | undefined;
546
546
  }, {
547
- tags: string[];
548
- content: string;
549
547
  category: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace";
548
+ content: string;
550
549
  confidence: number;
550
+ tags: string[];
551
551
  entityRef?: string | null | undefined;
552
552
  structuredAttributes?: Record<string, string> | null | undefined;
553
553
  promptedByQuestion?: string | null | undefined;
@@ -631,10 +631,10 @@ declare const ProactiveExtractionResultSchema: z.ZodObject<{
631
631
  }>, "many">>>;
632
632
  }, "strip", z.ZodTypeAny, {
633
633
  facts: {
634
- tags: string[];
635
- content: string;
636
634
  category: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace";
635
+ content: string;
637
636
  confidence: number;
637
+ tags: string[];
638
638
  entityRef?: string | null | undefined;
639
639
  structuredAttributes?: Record<string, string> | null | undefined;
640
640
  promptedByQuestion?: string | null | undefined;
@@ -680,10 +680,10 @@ declare const ProactiveExtractionResultSchema: z.ZodObject<{
680
680
  }[] | null | undefined;
681
681
  }, {
682
682
  facts: {
683
- tags: string[];
684
- content: string;
685
683
  category: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace";
684
+ content: string;
686
685
  confidence: number;
686
+ tags: string[];
687
687
  entityRef?: string | null | undefined;
688
688
  structuredAttributes?: Record<string, string> | null | undefined;
689
689
  promptedByQuestion?: string | null | undefined;
@@ -825,10 +825,10 @@ declare const ExtractionResultSchema: z.ZodObject<{
825
825
  observed_outcome?: string | null | undefined;
826
826
  }>>>;
827
827
  }, "strip", z.ZodTypeAny, {
828
- tags: string[];
829
- content: string;
830
828
  category: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace";
829
+ content: string;
831
830
  confidence: number;
831
+ tags: string[];
832
832
  entityRef?: string | null | undefined;
833
833
  structuredAttributes?: Record<string, string> | null | undefined;
834
834
  promptedByQuestion?: string | null | undefined;
@@ -854,10 +854,10 @@ declare const ExtractionResultSchema: z.ZodObject<{
854
854
  observed_outcome?: string | null | undefined;
855
855
  } | null | undefined;
856
856
  }, {
857
- tags: string[];
858
- content: string;
859
857
  category: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace";
858
+ content: string;
860
859
  confidence: number;
860
+ tags: string[];
861
861
  entityRef?: string | null | undefined;
862
862
  structuredAttributes?: Record<string, string> | null | undefined;
863
863
  promptedByQuestion?: string | null | undefined;
@@ -883,10 +883,10 @@ declare const ExtractionResultSchema: z.ZodObject<{
883
883
  observed_outcome?: string | null | undefined;
884
884
  } | null | undefined;
885
885
  }>, {
886
- tags: string[];
887
- content: string;
888
886
  category: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace";
887
+ content: string;
889
888
  confidence: number;
889
+ tags: string[];
890
890
  entityRef?: string | null | undefined;
891
891
  structuredAttributes?: Record<string, string> | null | undefined;
892
892
  promptedByQuestion?: string | null | undefined;
@@ -912,10 +912,10 @@ declare const ExtractionResultSchema: z.ZodObject<{
912
912
  observed_outcome?: string | null | undefined;
913
913
  } | null | undefined;
914
914
  }, {
915
- tags: string[];
916
- content: string;
917
915
  category: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace";
916
+ content: string;
918
917
  confidence: number;
918
+ tags: string[];
919
919
  entityRef?: string | null | undefined;
920
920
  structuredAttributes?: Record<string, string> | null | undefined;
921
921
  promptedByQuestion?: string | null | undefined;
@@ -1013,10 +1013,10 @@ declare const ExtractionResultSchema: z.ZodObject<{
1013
1013
  }>, "many">>>;
1014
1014
  }, "strip", z.ZodTypeAny, {
1015
1015
  facts: {
1016
- tags: string[];
1017
- content: string;
1018
1016
  category: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace";
1017
+ content: string;
1019
1018
  confidence: number;
1019
+ tags: string[];
1020
1020
  entityRef?: string | null | undefined;
1021
1021
  structuredAttributes?: Record<string, string> | null | undefined;
1022
1022
  promptedByQuestion?: string | null | undefined;
@@ -1068,10 +1068,10 @@ declare const ExtractionResultSchema: z.ZodObject<{
1068
1068
  identityReflection?: string | null | undefined;
1069
1069
  }, {
1070
1070
  facts: {
1071
- tags: string[];
1072
- content: string;
1073
1071
  category: "fact" | "preference" | "correction" | "entity" | "decision" | "relationship" | "principle" | "commitment" | "moment" | "skill" | "rule" | "procedure" | "reasoning_trace";
1072
+ content: string;
1074
1073
  confidence: number;
1074
+ tags: string[];
1075
1075
  entityRef?: string | null | undefined;
1076
1076
  structuredAttributes?: Record<string, string> | null | undefined;
1077
1077
  promptedByQuestion?: string | null | undefined;
@@ -1285,13 +1285,13 @@ declare const ContradictionVerificationSchema: z.ZodObject<{
1285
1285
  reasoning: z.ZodString;
1286
1286
  whichIsNewer: z.ZodEnum<["first", "second", "unclear"]>;
1287
1287
  }, "strip", z.ZodTypeAny, {
1288
- reasoning: string;
1289
1288
  confidence: number;
1289
+ reasoning: string;
1290
1290
  isContradiction: boolean;
1291
1291
  whichIsNewer: "second" | "first" | "unclear";
1292
1292
  }, {
1293
- reasoning: string;
1294
1293
  confidence: number;
1294
+ reasoning: string;
1295
1295
  isContradiction: boolean;
1296
1296
  whichIsNewer: "second" | "first" | "unclear";
1297
1297
  }>;
@@ -1386,8 +1386,8 @@ declare const BehaviorLoopAdjustmentSchema: z.ZodObject<{
1386
1386
  reason: z.ZodString;
1387
1387
  appliedAt: z.ZodString;
1388
1388
  }, "strip", z.ZodTypeAny, {
1389
- reason: string;
1390
1389
  confidence: number;
1390
+ reason: string;
1391
1391
  parameter: string;
1392
1392
  previousValue: number;
1393
1393
  nextValue: number;
@@ -1395,8 +1395,8 @@ declare const BehaviorLoopAdjustmentSchema: z.ZodObject<{
1395
1395
  evidenceCount: number;
1396
1396
  appliedAt: string;
1397
1397
  }, {
1398
- reason: string;
1399
1398
  confidence: number;
1399
+ reason: string;
1400
1400
  parameter: string;
1401
1401
  previousValue: number;
1402
1402
  nextValue: number;
@@ -1420,8 +1420,8 @@ declare const BehaviorLoopPolicyStateSchema: z.ZodObject<{
1420
1420
  reason: z.ZodString;
1421
1421
  appliedAt: z.ZodString;
1422
1422
  }, "strip", z.ZodTypeAny, {
1423
- reason: string;
1424
1423
  confidence: number;
1424
+ reason: string;
1425
1425
  parameter: string;
1426
1426
  previousValue: number;
1427
1427
  nextValue: number;
@@ -1429,8 +1429,8 @@ declare const BehaviorLoopPolicyStateSchema: z.ZodObject<{
1429
1429
  evidenceCount: number;
1430
1430
  appliedAt: string;
1431
1431
  }, {
1432
- reason: string;
1433
1432
  confidence: number;
1433
+ reason: string;
1434
1434
  parameter: string;
1435
1435
  previousValue: number;
1436
1436
  nextValue: number;
@@ -1447,8 +1447,8 @@ declare const BehaviorLoopPolicyStateSchema: z.ZodObject<{
1447
1447
  maxDeltaPerCycle: number;
1448
1448
  protectedParams: string[];
1449
1449
  adjustments: {
1450
- reason: string;
1451
1450
  confidence: number;
1451
+ reason: string;
1452
1452
  parameter: string;
1453
1453
  previousValue: number;
1454
1454
  nextValue: number;
@@ -1464,8 +1464,8 @@ declare const BehaviorLoopPolicyStateSchema: z.ZodObject<{
1464
1464
  maxDeltaPerCycle: number;
1465
1465
  protectedParams: string[];
1466
1466
  adjustments: {
1467
- reason: string;
1468
1467
  confidence: number;
1468
+ reason: string;
1469
1469
  parameter: string;
1470
1470
  previousValue: number;
1471
1471
  nextValue: number;
@@ -22,9 +22,9 @@ declare const SharedFeedbackEntrySchema: z.ZodObject<{
22
22
  date: string;
23
23
  agent: string;
24
24
  reason: string;
25
+ confidence?: number | undefined;
25
26
  workflow?: string | undefined;
26
27
  tags?: string[] | undefined;
27
- confidence?: number | undefined;
28
28
  severity?: "high" | "low" | "medium" | undefined;
29
29
  outcome?: string | undefined;
30
30
  refs?: string[] | undefined;
@@ -36,9 +36,9 @@ declare const SharedFeedbackEntrySchema: z.ZodObject<{
36
36
  date: string;
37
37
  agent: string;
38
38
  reason: string;
39
+ confidence?: number | undefined;
39
40
  workflow?: string | undefined;
40
41
  tags?: string[] | undefined;
41
- confidence?: number | undefined;
42
42
  severity?: "high" | "low" | "medium" | undefined;
43
43
  outcome?: string | undefined;
44
44
  refs?: string[] | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/core",
3
- "version": "9.3.664",
3
+ "version": "9.3.665",
4
4
  "description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -487,13 +487,13 @@ test("StorageRouter integration: catalog registers namespace on storageFor", asy
487
487
  const config = makeConfig(memoryDir);
488
488
  const catalog = new NamespaceCatalog(config);
489
489
  const router = new NamespaceStorageRouter(config, {
490
- onResolve: (namespace, storageDir) => {
491
- void catalog.registerResolved(namespace, storageDir);
492
- },
490
+ // Return the registration promise so the router tracks it as in-flight and
491
+ // `whenResolveHooksSettled()` can await it deterministically (no timer race).
492
+ onResolve: (namespace, storageDir) => catalog.registerResolved(namespace, storageDir),
493
493
  });
494
494
  await router.storageFor("project-origin-abc123");
495
- // allow the fire-and-forget registration to settle
496
- await new Promise((r) => setTimeout(r, 10));
495
+ // Deterministically await the fire-and-forget registration instead of sleeping.
496
+ await router.whenResolveHooksSettled();
497
497
 
498
498
  const record = await catalog.getNamespaceRecord("project-origin-abc123");
499
499
  assert.ok(record, "storageFor should have registered the namespace");
@@ -2523,8 +2523,8 @@ test("an async onResolve hook rejection does not crash storage resolution", asyn
2523
2523
  // Must not throw or produce an unhandled rejection that fails the test.
2524
2524
  const sm = await router.storageFor("default");
2525
2525
  assert.ok(sm, "storage resolution succeeds despite a rejecting async hook");
2526
- // Give the swallowed rejection a tick to settle.
2527
- await new Promise((r) => setTimeout(r, 10));
2526
+ // Deterministically await the swallowed rejection instead of sleeping.
2527
+ await router.whenResolveHooksSettled();
2528
2528
  assert.ok(called >= 1, "the async hook was invoked");
2529
2529
  } finally {
2530
2530
  await rm(memoryDir, { recursive: true, force: true });
@@ -2564,7 +2564,7 @@ test("concurrent storageFor() for one namespace fires the resolve hook ONCE whil
2564
2564
  // Let the in-flight registration settle, then a steady-state cache hit must
2565
2565
  // still be a catalog no-op (now deduped via notifiedResolved).
2566
2566
  release();
2567
- await new Promise((r) => setTimeout(r, 10));
2567
+ await router.whenResolveHooksSettled();
2568
2568
  await router.storageFor("project-origin-inflight");
2569
2569
  assert.equal(calls, 1, "a steady-state cache hit after settle must not re-fire the hook");
2570
2570
  } finally {
@@ -2589,20 +2589,20 @@ test("a dropped resolve registration (hook returns false) is retried on a later
2589
2589
  });
2590
2590
 
2591
2591
  await router.storageFor("project-origin-retry");
2592
- // Give the async hook a tick to settle and clear the in-flight marker.
2593
- await new Promise((r) => setTimeout(r, 10));
2592
+ // Deterministically await the async hook so the in-flight marker is cleared.
2593
+ await router.whenResolveHooksSettled();
2594
2594
  assert.equal(calls, 1, "the hook fired once for the dropped registration");
2595
2595
 
2596
2596
  // Now the registration will succeed; a later resolve must RETRY (not be
2597
2597
  // suppressed by a stale in-flight/notified marker from the dropped attempt).
2598
2598
  result = undefined; // success (legacy void)
2599
2599
  await router.storageFor("project-origin-retry");
2600
- await new Promise((r) => setTimeout(r, 10));
2600
+ await router.whenResolveHooksSettled();
2601
2601
  assert.equal(calls, 2, "a dropped registration must be retried on the next storageFor()");
2602
2602
 
2603
2603
  // After a SUCCESSFUL registration, further cache hits are deduped (no retry).
2604
2604
  await router.storageFor("project-origin-retry");
2605
- await new Promise((r) => setTimeout(r, 10));
2605
+ await router.whenResolveHooksSettled();
2606
2606
  assert.equal(calls, 2, "a successful registration is not re-fired on subsequent cache hits");
2607
2607
  } finally {
2608
2608
  await rm(memoryDir, { recursive: true, force: true });
@@ -223,6 +223,11 @@ export class NamespaceStorageRouter {
223
223
  // entry is always removed when the promise settles, so the map cannot grow
224
224
  // unbounded (one transient entry per concurrently-resolving namespace).
225
225
  private readonly inFlightResolved = new Map<string, string>();
226
+ // Tracks every in-flight resolve-hook promise so callers can deterministically
227
+ // await the fire-and-forget registrations that `storageFor()` kicks off (see
228
+ // `whenResolveHooksSettled`). Entries are removed as each hook settles, so the
229
+ // set holds at most one promise per concurrently-resolving namespace.
230
+ private readonly pendingResolveHooks = new Set<Promise<unknown>>();
226
231
 
227
232
  // Normalized (trimmed) default namespace identity (NH-FH). `storageFor`
228
233
  // normalizes its input, so default-namespace branches must compare against the
@@ -325,7 +330,10 @@ export class NamespaceStorageRouter {
325
330
  // the map holds at most one transient entry per concurrently-resolving
326
331
  // namespace and cannot grow unbounded.
327
332
  this.inFlightResolved.set(namespace, storageDir);
328
- Promise.resolve(hook(namespace, storageDir)).then(
333
+ const hookResult = Promise.resolve(hook(namespace, storageDir));
334
+ // Track the in-flight promise so `whenResolveHooksSettled()` can await it.
335
+ this.pendingResolveHooks.add(hookResult);
336
+ hookResult.then(
329
337
  (persisted) => {
330
338
  // Clear the in-flight marker ONLY if it is still ours (a newer resolve
331
339
  // for a different dir may have replaced it).
@@ -338,6 +346,7 @@ export class NamespaceStorageRouter {
338
346
  // On `false` (dropped touch) we intentionally do NOT mark notified, so
339
347
  // a later `storageFor()` retries the registration. Clearing the
340
348
  // in-flight marker above is what re-enables that retry.
349
+ this.pendingResolveHooks.delete(hookResult);
341
350
  },
342
351
  () => {
343
352
  // Registration failed — clear in-flight AND do NOT mark as notified, so
@@ -348,6 +357,7 @@ export class NamespaceStorageRouter {
348
357
  if (this.notifiedResolved.get(namespace) === storageDir) {
349
358
  this.notifiedResolved.delete(namespace);
350
359
  }
360
+ this.pendingResolveHooks.delete(hookResult);
351
361
  },
352
362
  );
353
363
  } catch {
@@ -358,4 +368,21 @@ export class NamespaceStorageRouter {
358
368
  }
359
369
  }
360
370
  }
371
+
372
+ /**
373
+ * Resolve once every in-flight `onResolve` registration has settled.
374
+ *
375
+ * `storageFor()` fires the resolve hook fire-and-forget, so its catalog side
376
+ * effect (e.g. `registerResolved(...)`) is not observable the moment
377
+ * `storageFor()` returns. Callers that must act on that side effect — notably
378
+ * tests asserting the catalog was updated — should await this instead of
379
+ * racing a timer. Resolves immediately when no hook is registered or nothing
380
+ * is in flight. The loop re-checks because a settling hook could, in
381
+ * principle, trigger a follow-on resolution.
382
+ */
383
+ async whenResolveHooksSettled(): Promise<void> {
384
+ while (this.pendingResolveHooks.size > 0) {
385
+ await Promise.allSettled([...this.pendingResolveHooks]);
386
+ }
387
+ }
361
388
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/namespaces/storage.ts"],"sourcesContent":["import path from \"node:path\";\nimport { access, lstat, readdir } from \"node:fs/promises\";\nimport { isSafeRouteNamespace } from \"../routing/engine.js\";\nimport { StorageManager } from \"../storage.js\";\nimport type { PluginConfig } from \"../types.js\";\nimport { ALL_CATEGORY_DIRS } from \"../utils/category-dir.js\";\nimport { namespaceIdentityToken, normalizeNamespaceIdentity } from \"./identity.js\";\n\nasync function exists(p: string): Promise<boolean> {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function hasStoredEntries(p: string): Promise<boolean> {\n try {\n const entry = await lstat(p);\n if (entry.isSymbolicLink()) return true;\n if (!entry.isDirectory()) return true;\n const children = await readdir(p, { withFileTypes: true });\n for (const child of children) {\n const childPath = path.join(p, child.name);\n if (child.isSymbolicLink() || child.isFile()) return true;\n if (child.isDirectory() && (await hasStoredEntries(childPath))) return true;\n }\n return false;\n } catch {\n return false;\n }\n}\n\n// Build a per-namespace directory under `<memoryDir>/namespaces` and assert the\n// resolved path stays inside that base. Namespace identifiers can originate from\n// operator config (config.defaultNamespace) and request-derived routing, so this\n// containment check prevents directory traversal (CodeQL js/path-injection).\n// For safe segments this returns exactly `path.join(base, segment)`, so there is\n// no behavioral change for valid namespaces.\nfunction resolveNamespaceDir(memoryDir: string, segment: string): string {\n // Mirror isSafeRouteNamespace's separator/parent-ref rejection (without its\n // 64-char cap, so identity tokens still pass). Rejecting separators and \"..\"\n // up front keeps the value a single contained child of <memoryDir>/namespaces.\n if (\n segment.length === 0 ||\n segment.includes(\"/\") ||\n segment.includes(\"\\\\\") ||\n segment.includes(\"..\") ||\n path.isAbsolute(segment)\n ) {\n throw new Error(`unsafe namespace path segment: ${segment}`);\n }\n return path.join(memoryDir, \"namespaces\", segment);\n}\n\nconst LEGACY_NAMESPACE_CONTENT_CHILDREN = [\n ...ALL_CATEGORY_DIRS,\n \"entities\",\n \"artifacts\",\n \"identity\",\n \"config\",\n \"summaries\",\n \"profile.md\",\n] as const;\n\nconst LEGACY_NAMESPACE_RUNTIME_CHILDREN = [\"state\"] as const;\n\nasync function hasAnyLegacyData(\n rootDir: string,\n options: { includeRuntimeState?: boolean } = {},\n): Promise<boolean> {\n const children = options.includeRuntimeState === true\n ? [...LEGACY_NAMESPACE_CONTENT_CHILDREN, ...LEGACY_NAMESPACE_RUNTIME_CHILDREN]\n : LEGACY_NAMESPACE_CONTENT_CHILDREN;\n for (const child of children) {\n if (await hasStoredEntries(path.join(rootDir, child))) return true;\n }\n return false;\n}\n\nasync function hasAnyNamespaceStorageMarker(\n rootDir: string,\n options: { includeRuntimeState?: boolean } = {},\n): Promise<boolean> {\n const children = options.includeRuntimeState === true\n ? [...LEGACY_NAMESPACE_CONTENT_CHILDREN, ...LEGACY_NAMESPACE_RUNTIME_CHILDREN]\n : LEGACY_NAMESPACE_CONTENT_CHILDREN;\n for (const child of children) {\n if (await exists(path.join(rootDir, child))) return true;\n }\n return false;\n}\n\n/**\n * Storage routing for namespaces.\n *\n * Compatibility note:\n * - When namespaces are enabled, existing raw namespace roots are preserved.\n * New namespace roots use tokenized names under `memoryDir/namespaces/<token>`.\n * - The default namespace continues to use the legacy `memoryDir` root unless the caller\n * has created `memoryDir/namespaces/<defaultNamespace>` (in which case we use that).\n *\n * This avoids surprising \"lost memories\" when an install flips namespaces on without\n * migrating existing data.\n */\n/**\n * Optional hooks for the storage router. `onResolve` fires whenever a namespace's\n * storage is resolved/created, so a downstream consumer (e.g. the namespace\n * catalog, issue #1499) can register the namespace. The hook MUST NOT throw into\n * the router; the router invokes it defensively and a hook failure never affects\n * storage resolution.\n *\n * The hook MAY return (or resolve to) a boolean indicating whether the\n * registration actually PERSISTED (round 6, codex P2 — NEFoX). When it resolves\n * to `false` (a dropped/no-op registration), the router does NOT mark the\n * (namespace, storageDir) pair as notified, so the next resolve RETRIES it\n * instead of suppressing it forever. A `void`/`undefined` result is treated as\n * success (legacy hooks).\n */\nexport interface NamespaceStorageRouterHooks {\n onResolve?: (\n namespace: string,\n storageDir: string,\n ) => void | boolean | Promise<void | boolean>;\n}\n\n/**\n * Resolve the runtime storage root for the configured DEFAULT namespace.\n *\n * Shared between the live router (`NamespaceStorageRouter.defaultNamespaceRoot`)\n * and the rebuildable catalog (`NamespaceCatalog.rebuildFromDisk`) so the two\n * can never diverge (CLAUDE.md rule #22/#42 — read & write paths resolve through\n * the same logic). The contract is: while legacy memory data still lives\n * directly under `memoryDir`, the default root stays `memoryDir`; only once the\n * legacy root is empty and a `namespaces/<default|token>` dir holds data does\n * the default migrate into that tokenized/legacy-named dir.\n */\nexport async function resolveDefaultNamespaceRoot(config: PluginConfig): Promise<string> {\n if (!config.namespacesEnabled) {\n return config.memoryDir;\n }\n\n // Build the legacy default root from the NORMALIZED (trimmed) name so a\n // whitespace-padded `defaultNamespace` still finds the live `namespaces/default`\n // root (NIabe). `storageFor()` classifies the trimmed value as the default, and\n // the on-disk legacy dir is created under the trimmed name; using the raw spaced\n // name here would look for `namespaces/<spaced>` and miss the real root, falling\n // back to memoryDir/tokenized. `namespaceIdentityToken` already normalizes\n // internally, so the tokenized path is unaffected.\n const defaultIdentity = normalizeNamespaceIdentity(config.defaultNamespace);\n const legacyNsDir = resolveNamespaceDir(config.memoryDir, defaultIdentity);\n const tokenizedNsDir = resolveNamespaceDir(\n config.memoryDir,\n namespaceIdentityToken(config.defaultNamespace),\n );\n const tokenizedHasData =\n (await exists(tokenizedNsDir)) &&\n (await hasAnyNamespaceStorageMarker(tokenizedNsDir, { includeRuntimeState: true }));\n const nsDir = tokenizedHasData\n ? tokenizedNsDir\n : (await exists(legacyNsDir))\n ? legacyNsDir\n : tokenizedNsDir;\n return (await exists(nsDir)) && !(await hasAnyLegacyData(config.memoryDir))\n ? nsDir\n : config.memoryDir;\n}\n\n/**\n * Resolve the runtime storage root for ANY namespace exactly as the live router\n * would (`NamespaceStorageRouter.namespaceRoot`). Shared so the rebuildable\n * catalog records the SAME on-disk root the router routes to — a recall/read\n * touch must not guess `namespaces/<token>` when the router actually serves a\n * legacy raw-name dir or a migrated default root (CLAUDE.md rule #22/#42; round\n * 4, cursor Medium). The default namespace delegates to `resolveDefaultNamespaceRoot`;\n * every other namespace prefers the tokenized root when it has a storage marker,\n * else a legacy raw-name dir when present, else the tokenized root.\n */\nexport async function resolveNamespaceStorageRoot(\n config: PluginConfig,\n namespace: string,\n): Promise<string> {\n if (!config.namespacesEnabled) return config.memoryDir;\n // Compare on NORMALIZED identity so a whitespace-padded configured default name\n // still routes to the default root rather than a tokenized non-default dir\n // (NH-FH). The catalog keys records by the same normalized identity.\n if (normalizeNamespaceIdentity(namespace) === normalizeNamespaceIdentity(config.defaultNamespace)) {\n return resolveDefaultNamespaceRoot(config);\n }\n const legacyRoot = resolveNamespaceDir(config.memoryDir, namespace);\n const tokenizedRoot = resolveNamespaceDir(config.memoryDir, namespaceIdentityToken(namespace));\n if (\n (await exists(tokenizedRoot)) &&\n (await hasAnyNamespaceStorageMarker(tokenizedRoot, { includeRuntimeState: true }))\n ) {\n return tokenizedRoot;\n }\n return (await exists(legacyRoot)) ? legacyRoot : tokenizedRoot;\n}\n\nexport class NamespaceStorageRouter {\n private readonly cache = new Map<string, StorageManager>();\n private defaultNsRootResolved: string | null = null;\n // Dedup the resolve hook (round 6, cursor Medium — NCNL2). Recall/extraction\n // call `storageFor` repeatedly; firing `onResolve` (→ catalog loadCompacted +\n // append) on every cache hit grows `namespaces.jsonl` without bound between\n // rebuilds. We fire the hook only when the (namespace, storageDir) pair is new\n // or its dir changed, so a steady-state cache hit is a no-op for the catalog.\n private readonly notifiedResolved = new Map<string, string>();\n // In-flight resolve-hook dedup (NFJV-, codex P2). The catalog's `onResolve`\n // hook is ASYNC (it returns `registerResolved(...)`), so `notifiedResolved` is\n // only set after the hook's promise SETTLES. Without tracking the in-flight\n // window, a burst of `storageFor()` cache hits for the SAME namespace before\n // the first registration finishes would each pass the `notifiedResolved` guard\n // and fire their OWN `onResolve` — queueing N duplicate catalog touches + lock\n // acquisitions despite the once-per-namespace intent. We therefore record the\n // (namespace → storageDir) being registered BEFORE awaiting the hook so a\n // concurrent call for the same pair skips firing. On SUCCESS the pair is\n // promoted to `notifiedResolved` (future calls skip permanently); on `false`\n // (dropped touch — e.g. rebuild-lock timeout) OR rejection the in-flight marker\n // is CLEARED so a later `storageFor()` can RETRY the dropped registration. The\n // entry is always removed when the promise settles, so the map cannot grow\n // unbounded (one transient entry per concurrently-resolving namespace).\n private readonly inFlightResolved = new Map<string, string>();\n\n // Normalized (trimmed) default namespace identity (NH-FH). `storageFor`\n // normalizes its input, so default-namespace branches must compare against the\n // normalized config default too — otherwise a whitespace-padded configured\n // default name routes the default namespace to a tokenized non-default root.\n private readonly defaultNamespaceIdentity: string;\n\n constructor(\n private readonly config: PluginConfig,\n private readonly hooks: NamespaceStorageRouterHooks = {},\n ) {\n this.defaultNamespaceIdentity = normalizeNamespaceIdentity(config.defaultNamespace);\n }\n\n private async defaultNamespaceRoot(): Promise<string> {\n this.defaultNsRootResolved = await resolveDefaultNamespaceRoot(this.config);\n return this.defaultNsRootResolved;\n }\n\n private async namespaceRoot(namespace: string): Promise<string> {\n // NOTE: only used after defaultNamespaceRoot() resolution.\n if (!this.config.namespacesEnabled) return this.config.memoryDir;\n if (normalizeNamespaceIdentity(namespace) === this.defaultNamespaceIdentity) {\n return this.defaultNsRootResolved ?? this.config.memoryDir;\n }\n return resolveNamespaceStorageRoot(this.config, namespace);\n }\n\n async storageFor(namespace: string): Promise<StorageManager> {\n const ns = normalizeNamespaceIdentity(namespace || this.config.defaultNamespace);\n if (ns !== this.defaultNamespaceIdentity && !isSafeRouteNamespace(ns)) {\n throw new Error(`unsafe namespace: ${ns}`);\n }\n // Even when the default namespace is exempt from the check above, every\n // on-disk path is built through resolveNamespaceDir(), which rejects\n // traversal segments — so an unsafe configured default still cannot escape\n // <memoryDir>/namespaces (CodeQL js/path-injection).\n\n let root: string;\n if (ns === this.defaultNamespaceIdentity) {\n root = await this.defaultNamespaceRoot();\n const cached = this.cache.get(ns);\n if (cached && cached.dir === root) {\n this.notifyResolved(ns, root);\n return cached;\n }\n } else {\n const cached = this.cache.get(ns);\n root = await this.namespaceRoot(ns);\n if (cached && cached.dir === root) {\n this.notifyResolved(ns, root);\n return cached;\n }\n }\n\n const sm = new StorageManager(root, this.config.entitySchemas);\n // Propagate the inline-attribution template so that router-created storages\n // (used by extraction and shared-promotion paths) strip citations consistently,\n // matching the behaviour of the primary this.storage instance in the orchestrator.\n sm.citationTemplate = this.config.inlineSourceAttributionFormat;\n this.cache.set(ns, sm);\n this.notifyResolved(ns, root);\n return sm;\n }\n\n /**\n * Fire the resolve hook defensively. A hook failure (e.g. a catalog write\n * error) MUST NOT crash storage resolution — see CLAUDE.md gotcha #13.\n */\n private notifyResolved(namespace: string, storageDir: string): void {\n const hook = this.hooks.onResolve;\n if (!hook) return;\n // Skip when we've already SUCCESSFULLY notified this exact (namespace,\n // storageDir) — a steady-state cache hit must not re-append to the catalog\n // log (NCNL2). A changed dir (rare: migration/realignment) still re-fires\n // once. We mark the pair as notified ONLY AFTER the hook succeeds, and CLEAR\n // it on failure, so a dropped registration (e.g. rebuild-lock timeout) is\n // RETRIED on the next cache hit instead of being suppressed forever (round 6,\n // cursor Medium — ND3EJ).\n if (this.notifiedResolved.get(namespace) === storageDir) return;\n // In-flight dedup (NFJV-, codex P2): if a registration for this exact\n // (namespace, storageDir) is already AWAITING its async hook, do not fire a\n // second one. Without this, concurrent cache-hit bursts before the first\n // append settles each pass the `notifiedResolved` guard above and queue\n // duplicate catalog touches/lock acquisitions. A pair with a DIFFERENT\n // in-flight dir (rare mid-migration realignment) still fires once.\n if (this.inFlightResolved.get(namespace) === storageDir) return;\n try {\n // Handle BOTH synchronous throws and asynchronous rejections (round 6,\n // codex P2 — NDo8C). The hook may be `async`; its rejected promise would\n // bypass this try/catch and, where unhandled rejections are fatal, crash\n // storage resolution. Mark the dedup pair as notified ONLY when the hook\n // resolves to a PERSISTED result (round 6, codex P2 — NEFoX): a result of\n // `false` means the registration was dropped/no-op (e.g. rebuild-lock\n // timeout), so we must NOT suppress its retry. `void`/`undefined` is treated\n // as success for legacy hooks. On rejection we leave it un-notified to retry.\n //\n // Record the in-flight marker BEFORE awaiting so concurrent calls for the\n // same pair skip (NFJV-). It is always cleared once the promise settles, so\n // the map holds at most one transient entry per concurrently-resolving\n // namespace and cannot grow unbounded.\n this.inFlightResolved.set(namespace, storageDir);\n Promise.resolve(hook(namespace, storageDir)).then(\n (persisted) => {\n // Clear the in-flight marker ONLY if it is still ours (a newer resolve\n // for a different dir may have replaced it).\n if (this.inFlightResolved.get(namespace) === storageDir) {\n this.inFlightResolved.delete(namespace);\n }\n if (persisted !== false) {\n this.notifiedResolved.set(namespace, storageDir);\n }\n // On `false` (dropped touch) we intentionally do NOT mark notified, so\n // a later `storageFor()` retries the registration. Clearing the\n // in-flight marker above is what re-enables that retry.\n },\n () => {\n // Registration failed — clear in-flight AND do NOT mark as notified, so\n // it is retried on the next cache hit.\n if (this.inFlightResolved.get(namespace) === storageDir) {\n this.inFlightResolved.delete(namespace);\n }\n if (this.notifiedResolved.get(namespace) === storageDir) {\n this.notifiedResolved.delete(namespace);\n }\n },\n );\n } catch {\n // Synchronous throw: clear any in-flight marker we just set and leave the\n // pair un-notified so a later resolve retries.\n if (this.inFlightResolved.get(namespace) === storageDir) {\n this.inFlightResolved.delete(namespace);\n }\n }\n }\n}\n"],"mappings":"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","names":[]}