@remnic/core 9.66.10 → 9.66.11

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 (61) hide show
  1. package/dist/access-admin-ops-surface.js +2 -1
  2. package/dist/access-authorization-probe.js +2 -1
  3. package/dist/access-boundary.js +2 -1
  4. package/dist/access-cli.js +8 -7
  5. package/dist/access-cli.js.map +1 -1
  6. package/dist/access-extraction-force-flush.js +2 -1
  7. package/dist/access-http-query.js +2 -1
  8. package/dist/access-http.js +2 -1
  9. package/dist/access-identity-continuity-surface.js +2 -1
  10. package/dist/access-lcm-surface.js +2 -1
  11. package/dist/access-mcp.js +2 -1
  12. package/dist/access-namespace-preflight.js +2 -1
  13. package/dist/access-observe-write-surface.js +2 -1
  14. package/dist/access-operations-batch.js +2 -1
  15. package/dist/access-operations.js +2 -1
  16. package/dist/access-recall-concurrency.js +2 -1
  17. package/dist/access-recall-response.js +2 -1
  18. package/dist/access-recall-surface.js +2 -1
  19. package/dist/access-service.js +2 -1
  20. package/dist/access-surface-catalog.js +2 -1
  21. package/dist/access-surface-catalog.js.map +1 -1
  22. package/dist/causal-consolidation.js +3 -2
  23. package/dist/causal-consolidation.js.map +1 -1
  24. package/dist/chunk-3UXOZBHV.js +20 -0
  25. package/dist/chunk-3UXOZBHV.js.map +1 -0
  26. package/dist/{chunk-YYDOUOYM.js → chunk-6H7JWYTC.js} +16 -11
  27. package/dist/chunk-6H7JWYTC.js.map +1 -0
  28. package/dist/{chunk-KWVXCRFZ.js → chunk-BCM6ZAJ3.js} +4 -4
  29. package/dist/{chunk-TXCLDIXJ.js → chunk-K6PWPC7O.js} +2 -2
  30. package/dist/{chunk-M2BY5IZL.js → chunk-PVV42HM4.js} +5 -2
  31. package/dist/chunk-PVV42HM4.js.map +1 -0
  32. package/dist/{chunk-VSQG4AXL.js → chunk-PWVRMD37.js} +1 -1
  33. package/dist/{chunk-NR36NW24.js → chunk-UWOA5L6N.js} +40 -43
  34. package/dist/{chunk-NR36NW24.js.map → chunk-UWOA5L6N.js.map} +1 -1
  35. package/dist/{chunk-ZPZLGN4Y.js → chunk-XZ7QLOTJ.js} +4 -20
  36. package/dist/chunk-XZ7QLOTJ.js.map +1 -0
  37. package/dist/cli.js +5 -4
  38. package/dist/coding/export-okf-codegraph.js +1 -1
  39. package/dist/compounding/engine.js +1 -1
  40. package/dist/connectors/codex-materialize-runner.js +2 -1
  41. package/dist/connectors/index.js +2 -1
  42. package/dist/external-wiki-access.js +2 -1
  43. package/dist/extraction.js +2 -1
  44. package/dist/index.js +8 -7
  45. package/dist/orchestrator.js +8 -7
  46. package/dist/semantic-consolidation.js +3 -2
  47. package/dist/source-agent-qualifier.js +2 -1
  48. package/dist/support-passport/index.js +2 -1
  49. package/dist/transfer/export-okf.d.ts +8 -0
  50. package/dist/transfer/export-okf.js +3 -2
  51. package/package.json +2 -2
  52. package/src/cli/okf-commands.ts +2 -5
  53. package/src/okf/render.ts +11 -2
  54. package/src/transfer/export-okf.test.ts +77 -1
  55. package/src/transfer/export-okf.ts +25 -9
  56. package/dist/chunk-M2BY5IZL.js.map +0 -1
  57. package/dist/chunk-YYDOUOYM.js.map +0 -1
  58. package/dist/chunk-ZPZLGN4Y.js.map +0 -1
  59. /package/dist/{chunk-KWVXCRFZ.js.map → chunk-BCM6ZAJ3.js.map} +0 -0
  60. /package/dist/{chunk-TXCLDIXJ.js.map → chunk-K6PWPC7O.js.map} +0 -0
  61. /package/dist/{chunk-VSQG4AXL.js.map → chunk-PWVRMD37.js.map} +0 -0
@@ -1,3 +1,6 @@
1
+ import {
2
+ resolveNamespaceChildRoot
3
+ } from "./chunk-3UXOZBHV.js";
1
4
  import {
2
5
  hasCodexMaterializeSentinel,
3
6
  materializeForNamespace
@@ -23,24 +26,6 @@ import {
23
26
 
24
27
  // src/connectors/codex-materialize-runner.ts
25
28
  import { existsSync } from "fs";
26
-
27
- // src/namespaces/path.ts
28
- import path from "path";
29
- function assertNamespacePathInsideRoot(root, candidate, namespace, label = "namespace path") {
30
- const relative = path.relative(root, candidate);
31
- if (relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative)) {
32
- return;
33
- }
34
- throw new Error(`invalid ${label}: ${namespace}`);
35
- }
36
- function resolveNamespaceChildRoot(memoryDir, namespace, label = "namespace path") {
37
- const namespaceRoot = path.resolve(memoryDir, "namespaces");
38
- const candidate = path.resolve(namespaceRoot, namespace);
39
- assertNamespacePathInsideRoot(namespaceRoot, candidate, namespace, label);
40
- return candidate;
41
- }
42
-
43
- // src/connectors/codex-materialize-runner.ts
44
29
  async function runCodexMaterialize(options) {
45
30
  const cfg = options.config;
46
31
  if (!cfg.codexMaterializeMemories) {
@@ -146,8 +131,7 @@ function resolveNamespaceDir(memoryDir, namespace, cfg) {
146
131
  }
147
132
 
148
133
  export {
149
- resolveNamespaceChildRoot,
150
134
  runCodexMaterialize,
151
135
  runPostConsolidationMaterialize
152
136
  };
153
- //# sourceMappingURL=chunk-ZPZLGN4Y.js.map
137
+ //# sourceMappingURL=chunk-XZ7QLOTJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/connectors/codex-materialize-runner.ts"],"sourcesContent":["/**\n * codex-materialize-runner.ts — Thin I/O bridge for the Codex materializer.\n *\n * The pure rendering logic lives in {@link ./codex-materialize.js}. This file\n * is the place callers (consolidation hooks, CLI, session-end hook) go when\n * they want the whole \"load memories from storage → render → write\" flow.\n *\n * Kept deliberately small so #378 never has to reach into orchestrator.ts /\n * importance.ts — the two files Wave 1 agents are editing concurrently.\n */\n\nimport { existsSync } from \"node:fs\";\n\nimport { log } from \"../logger.js\";\nimport { resolveNamespaceChildRoot } from \"../namespaces/path.js\";\nimport { isSafeRouteNamespace } from \"../routing/engine.js\";\nimport { projectProceduresToSkills } from \"../procedural/skill-projection.js\";\nimport { BUILTIN_SKILLS } from \"../skills-registry.js\";\nimport { StorageManager } from \"../storage.js\";\nimport { excludeSupportPassportPrivateMemories } from \"../support-passport/card-projection.js\";\nimport type { PluginConfig, MemoryFile } from \"../types.js\";\nimport {\n hasCodexMaterializeSentinel,\n materializeForNamespace,\n type MaterializeResult,\n type RolloutSummaryInput,\n} from \"./codex-materialize.js\";\n\n/** Options accepted by the shared post-consolidation materialize helper. */\nexport interface PostConsolidationMaterializeOptions {\n config: PluginConfig;\n namespace?: string;\n memories?: MemoryFile[];\n memoryDir?: string;\n codexHome?: string;\n rolloutSummaries?: RolloutSummaryInput[];\n now?: Date;\n}\n\n/** Options accepted by the runner. */\nexport interface RunMaterializeOptions {\n /** Remnic config — we only read the `codexMaterialize*` fields. */\n config: PluginConfig;\n /** Namespace to materialize. Overrides the config's `codexMaterializeNamespace`. */\n namespace?: string;\n /** Override the memory directory (defaults to `config.memoryDir`). */\n memoryDir?: string;\n /** Override `<codex_home>` (useful for tests). */\n codexHome?: string;\n /** Optional pre-loaded memories (bypasses disk read — used in tests). */\n memories?: MemoryFile[];\n /** Optional rollout summaries supplied by the caller. */\n rolloutSummaries?: RolloutSummaryInput[];\n /** Current time injection for deterministic runs. */\n now?: Date;\n /** Reason string — logged for observability. */\n reason?: \"consolidation\" | \"session_end\" | \"manual\" | \"cli\";\n}\n\n/**\n * Run the Codex materialization end-to-end. Returns `null` when the feature\n * is disabled in config or when the user hasn't opted in via the sentinel.\n * Never throws for \"expected\" skips; only throws on schema validation or I/O\n * errors that callers actually need to surface.\n */\nexport async function runCodexMaterialize(\n options: RunMaterializeOptions,\n): Promise<MaterializeResult | null> {\n const cfg = options.config;\n if (!cfg.codexMaterializeMemories) {\n log.debug(`[codex-materialize] skipped — codexMaterializeMemories=false`);\n return null;\n }\n\n // Per-trigger gate: session-end runs must honor codexMaterializeOnSessionEnd.\n // The Codex Stop hook (remnic-codex-hook.cjs, session-end event) passes\n // reason=\"session_end\"; when the user has turned off the session-end trigger\n // we short-circuit here without touching disk.\n if (options.reason === \"session_end\" && cfg.codexMaterializeOnSessionEnd === false) {\n log.debug(\n `[codex-materialize] skipped — session-end disabled via codexMaterializeOnSessionEnd=false`,\n );\n return null;\n }\n\n const namespace = resolveNamespace(options.namespace, cfg);\n const memoryDir = options.memoryDir ?? cfg.memoryDir;\n const codexHome = options.codexHome ?? cfg.codex?.codexHome ?? undefined;\n if (!memoryDir) {\n log.warn(`[codex-materialize] skipped — no memoryDir available`);\n return null;\n }\n\n let memories: MemoryFile[];\n if (options.memories) {\n memories = options.memories;\n } else {\n if (!hasCodexMaterializeSentinel(codexHome)) {\n return materializeForNamespace(namespace, {\n memories: [],\n codexHome,\n maxSummaryTokens: cfg.codexMaterializeMaxSummaryTokens,\n rolloutRetentionDays: cfg.codexMaterializeRolloutRetentionDays,\n rolloutSummaries: options.rolloutSummaries,\n now: options.now,\n });\n }\n const nsDir = resolveNamespaceDir(memoryDir, namespace, cfg);\n const storage = new StorageManager(nsDir);\n memories = await storage.readAllMemories();\n }\n memories = excludeSupportPassportPrivateMemories(memories);\n\n // Skill projection (issue #2369). Gated on `procedural.enabled` AND\n // `procedural.skillProjection.enabled`; `maxSkills: 0` disables it too.\n // Passing `undefined` leaves `skills/` untouched, so a disabled gate never\n // deletes a folder the user curated.\n const skillProjection = cfg.procedural?.skillProjection;\n const skills =\n cfg.procedural?.enabled === true && skillProjection?.enabled === true\n ? projectProceduresToSkills(memories, {\n maxSkills: skillProjection.maxSkills,\n reservedSlugs: BUILTIN_SKILLS.map((skill) => skill.slug),\n })\n : undefined;\n\n // Intentionally NOT catching here: per the JSDoc contract above,\n // schema-validation and I/O errors from `materializeForNamespace` must\n // surface to callers so they can exit non-zero (CLI) or log + recover\n // (consolidation post-hook, which has its own narrower try/catch).\n // Catching everything would make a broken MEMORY.md render look like a\n // successful skip, and invisible failures are strictly worse than loud\n // ones for a feature that writes to `~/.codex/memories`.\n const result = materializeForNamespace(namespace, {\n memories,\n codexHome,\n maxSummaryTokens: cfg.codexMaterializeMaxSummaryTokens,\n rolloutRetentionDays: cfg.codexMaterializeRolloutRetentionDays,\n rolloutSummaries: options.rolloutSummaries,\n skills,\n now: options.now,\n });\n if (options.reason) {\n log.debug(\n `[codex-materialize] ran reason=${options.reason} wrote=${result.wrote} files=${result.filesWritten.length}`,\n );\n }\n return result;\n}\n\n/**\n * Shared helper for post-consolidation materialize hooks.\n *\n * `materializeAfterSemanticConsolidation` and `materializeAfterCausalConsolidation`\n * used to be two nearly-identical copies of this logic; keeping the actual\n * body here means any future guard/logging change happens in one place.\n *\n * The only per-caller knob is `logPrefix`, which is used to tag the\n * non-fatal warning emitted when the materializer throws.\n */\nexport async function runPostConsolidationMaterialize(\n logPrefix: string,\n options: PostConsolidationMaterializeOptions,\n): Promise<MaterializeResult | null> {\n if (!options.config.codexMaterializeMemories) return null;\n if (!options.config.codexMaterializeOnConsolidation) return null;\n try {\n return await runCodexMaterialize({\n config: options.config,\n namespace: options.namespace,\n memories: options.memories,\n memoryDir: options.memoryDir,\n codexHome: options.codexHome,\n rolloutSummaries: options.rolloutSummaries,\n now: options.now,\n reason: \"consolidation\",\n });\n } catch (error) {\n log.warn(\n `${logPrefix} Codex materialize post-hook failed (non-fatal): ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return null;\n }\n}\n\nfunction resolveNamespace(override: string | undefined, cfg: PluginConfig): string {\n const requested = (override ?? cfg.codexMaterializeNamespace ?? \"auto\").trim();\n const defaultNamespace = (cfg.defaultNamespace ?? \"\").trim();\n const namespace =\n requested.length === 0 || requested === \"auto\"\n ? (defaultNamespace.length > 0 ? defaultNamespace : \"default\")\n : requested;\n if (!isSafeRouteNamespace(namespace)) {\n throw new Error(`invalid materialize namespace: ${namespace}`);\n }\n return namespace;\n}\n\n/**\n * Resolve the on-disk storage root for a namespace, matching\n * `NamespaceStorageRouter` in `packages/remnic-core/src/namespaces/storage.ts`.\n *\n * Contract:\n * - When namespaces are disabled, every namespace maps to `memoryDir` itself.\n * - When namespaces are enabled, non-default namespaces always live under\n * `memoryDir/namespaces/<namespace>`.\n * - The default namespace prefers `memoryDir/namespaces/<defaultNamespace>`\n * when that directory already exists (migrated install); otherwise it\n * falls back to the legacy `memoryDir` root so materialization does not\n * silently switch directories out from under an existing install.\n */\nfunction resolveNamespaceDir(\n memoryDir: string,\n namespace: string,\n cfg: PluginConfig,\n): string {\n if (!cfg.namespacesEnabled) return memoryDir;\n\n const configuredDefaultNamespace = (cfg.defaultNamespace ?? \"\").trim();\n const defaultNamespace =\n configuredDefaultNamespace.length > 0 ? configuredDefaultNamespace : \"default\";\n const ns = (namespace || defaultNamespace).trim();\n if (!isSafeRouteNamespace(ns)) {\n throw new Error(`invalid materialize namespace: ${ns}`);\n }\n const namespacedRoot = resolveNamespaceChildRoot(memoryDir, ns, \"materialize namespace path\");\n\n if (ns === defaultNamespace) {\n return existsSync(namespacedRoot) ? namespacedRoot : memoryDir;\n }\n return namespacedRoot;\n}\n"],"mappings":"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","names":[]}
package/dist/cli.js CHANGED
@@ -91,14 +91,14 @@ import {
91
91
  runWorkProductStatusCliCommand,
92
92
  runWorkProjectCliCommand,
93
93
  runWorkTaskCliCommand
94
- } from "./chunk-NR36NW24.js";
94
+ } from "./chunk-UWOA5L6N.js";
95
95
  import "./chunk-MMACM5G2.js";
96
96
  import "./chunk-54BGIBCR.js";
97
97
  import "./chunk-H4CKS5B2.js";
98
98
  import "./chunk-FXZTL7F7.js";
99
99
  import "./chunk-PYHOAXTI.js";
100
100
  import "./chunk-UP6MOYCB.js";
101
- import "./chunk-YYDOUOYM.js";
101
+ import "./chunk-6H7JWYTC.js";
102
102
  import "./chunk-BV2TIZ4F.js";
103
103
  import "./chunk-Z734BLO3.js";
104
104
  import "./chunk-HDDRVXX4.js";
@@ -120,7 +120,7 @@ import "./chunk-YLBE6PLR.js";
120
120
  import "./chunk-CDKHKZL5.js";
121
121
  import "./chunk-HSSERO3E.js";
122
122
  import "./chunk-NOUJNHWG.js";
123
- import "./chunk-M2BY5IZL.js";
123
+ import "./chunk-PVV42HM4.js";
124
124
  import "./chunk-HDQUQBPW.js";
125
125
  import "./chunk-6FC3IZ26.js";
126
126
  import "./chunk-KWLDB64F.js";
@@ -271,7 +271,8 @@ import "./chunk-D3AUQXTJ.js";
271
271
  import "./chunk-7MOYAT4Y.js";
272
272
  import "./chunk-LTWFU6VJ.js";
273
273
  import "./chunk-UFGFMWAD.js";
274
- import "./chunk-ZPZLGN4Y.js";
274
+ import "./chunk-XZ7QLOTJ.js";
275
+ import "./chunk-3UXOZBHV.js";
275
276
  import "./chunk-ZXBTTMGZ.js";
276
277
  import "./chunk-YMOPYG3K.js";
277
278
  import "./chunk-2LSZVONP.js";
@@ -5,7 +5,7 @@ import {
5
5
  rejectSymlinkPath,
6
6
  renderFrontmatter,
7
7
  writeBundleFile
8
- } from "../chunk-M2BY5IZL.js";
8
+ } from "../chunk-PVV42HM4.js";
9
9
  import {
10
10
  ACTIVE_DECISION_STATUSES,
11
11
  CodegraphRuntimeError,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  CompoundingEngine,
3
3
  defaultTierMigrationCycleBudget
4
- } from "../chunk-VSQG4AXL.js";
4
+ } from "../chunk-PWVRMD37.js";
5
5
  import "../chunk-VI637ILW.js";
6
6
  import "../chunk-N4F7ZSSG.js";
7
7
  import "../chunk-4PHT7KE2.js";
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  runCodexMaterialize,
3
3
  runPostConsolidationMaterialize
4
- } from "../chunk-ZPZLGN4Y.js";
4
+ } from "../chunk-XZ7QLOTJ.js";
5
+ import "../chunk-3UXOZBHV.js";
5
6
  import "../chunk-ZXBTTMGZ.js";
6
7
  import "../chunk-4PHT7KE2.js";
7
8
  import "../chunk-UHVXE24Y.js";
@@ -33,7 +33,8 @@ import {
33
33
  import "../chunk-EMDLM3LW.js";
34
34
  import {
35
35
  runCodexMaterialize
36
- } from "../chunk-ZPZLGN4Y.js";
36
+ } from "../chunk-XZ7QLOTJ.js";
37
+ import "../chunk-3UXOZBHV.js";
37
38
  import {
38
39
  MATERIALIZE_VERSION,
39
40
  SENTINEL_FILE,
@@ -47,7 +47,8 @@ import "./chunk-D3AUQXTJ.js";
47
47
  import "./chunk-7MOYAT4Y.js";
48
48
  import "./chunk-LTWFU6VJ.js";
49
49
  import "./chunk-UFGFMWAD.js";
50
- import "./chunk-ZPZLGN4Y.js";
50
+ import "./chunk-XZ7QLOTJ.js";
51
+ import "./chunk-3UXOZBHV.js";
51
52
  import "./chunk-ZXBTTMGZ.js";
52
53
  import "./chunk-YMOPYG3K.js";
53
54
  import "./chunk-2LSZVONP.js";
@@ -30,7 +30,8 @@ import "./chunk-NLE6TSLG.js";
30
30
  import "./chunk-IVKIODHO.js";
31
31
  import "./chunk-WKDFJXW5.js";
32
32
  import "./chunk-JUXOTY6J.js";
33
- import "./chunk-ZPZLGN4Y.js";
33
+ import "./chunk-XZ7QLOTJ.js";
34
+ import "./chunk-3UXOZBHV.js";
34
35
  import "./chunk-ZXBTTMGZ.js";
35
36
  import "./chunk-4PHT7KE2.js";
36
37
  import "./chunk-UHVXE24Y.js";
package/dist/index.js CHANGED
@@ -172,7 +172,7 @@ import {
172
172
  weeklySnapshotPath,
173
173
  writeManifest,
174
174
  writeMeetingEpisodeMemory
175
- } from "./chunk-KWVXCRFZ.js";
175
+ } from "./chunk-BCM6ZAJ3.js";
176
176
  import {
177
177
  WEARABLE_SOURCE_PREFIX,
178
178
  buildExtractionTurns,
@@ -271,7 +271,7 @@ import "./chunk-UXPUUL3G.js";
271
271
  import "./chunk-V5OCT34X.js";
272
272
  import "./chunk-TECVW3JP.js";
273
273
  import "./chunk-C5JF6EZ7.js";
274
- import "./chunk-VSQG4AXL.js";
274
+ import "./chunk-PWVRMD37.js";
275
275
  import "./chunk-VI637ILW.js";
276
276
  import "./chunk-SEF4IOFE.js";
277
277
  import {
@@ -498,14 +498,14 @@ import {
498
498
  runOkfCliCommand,
499
499
  runWearablesCliCommand,
500
500
  screenCandidateFact
501
- } from "./chunk-NR36NW24.js";
501
+ } from "./chunk-UWOA5L6N.js";
502
502
  import "./chunk-MMACM5G2.js";
503
503
  import "./chunk-54BGIBCR.js";
504
504
  import "./chunk-H4CKS5B2.js";
505
505
  import "./chunk-FXZTL7F7.js";
506
506
  import "./chunk-PYHOAXTI.js";
507
507
  import "./chunk-UP6MOYCB.js";
508
- import "./chunk-YYDOUOYM.js";
508
+ import "./chunk-6H7JWYTC.js";
509
509
  import "./chunk-BV2TIZ4F.js";
510
510
  import "./chunk-Z734BLO3.js";
511
511
  import "./chunk-HDDRVXX4.js";
@@ -540,7 +540,7 @@ import {
540
540
  parseIsoTimestamp,
541
541
  validateImportTurn
542
542
  } from "./chunk-NOUJNHWG.js";
543
- import "./chunk-M2BY5IZL.js";
543
+ import "./chunk-PVV42HM4.js";
544
544
  import "./chunk-HDQUQBPW.js";
545
545
  import "./chunk-6FC3IZ26.js";
546
546
  import "./chunk-KWLDB64F.js";
@@ -622,7 +622,7 @@ import {
622
622
  } from "./chunk-3I4FUAVJ.js";
623
623
  import {
624
624
  buildExtensionsBlockForConsolidation
625
- } from "./chunk-TXCLDIXJ.js";
625
+ } from "./chunk-K6PWPC7O.js";
626
626
  import {
627
627
  REMNIC_EXTENSIONS_TOTAL_TOKEN_LIMIT,
628
628
  discoverMemoryExtensions,
@@ -1064,7 +1064,8 @@ import {
1064
1064
  import {
1065
1065
  runCodexMaterialize,
1066
1066
  runPostConsolidationMaterialize
1067
- } from "./chunk-ZPZLGN4Y.js";
1067
+ } from "./chunk-XZ7QLOTJ.js";
1068
+ import "./chunk-3UXOZBHV.js";
1068
1069
  import {
1069
1070
  MATERIALIZE_VERSION,
1070
1071
  SENTINEL_FILE,
@@ -3,7 +3,7 @@ import {
3
3
  blendGraphExpandedRecallScore,
4
4
  graphPathRelativeToStorage,
5
5
  mergeGraphExpandedResults
6
- } from "./chunk-KWVXCRFZ.js";
6
+ } from "./chunk-BCM6ZAJ3.js";
7
7
  import "./chunk-3TSU662L.js";
8
8
  import "./chunk-JNZSNJ43.js";
9
9
  import "./chunk-I74SUMNI.js";
@@ -29,7 +29,7 @@ import "./chunk-UXPUUL3G.js";
29
29
  import "./chunk-V5OCT34X.js";
30
30
  import "./chunk-TECVW3JP.js";
31
31
  import "./chunk-C5JF6EZ7.js";
32
- import "./chunk-VSQG4AXL.js";
32
+ import "./chunk-PWVRMD37.js";
33
33
  import "./chunk-VI637ILW.js";
34
34
  import "./chunk-SEF4IOFE.js";
35
35
  import "./chunk-2OXBS2LV.js";
@@ -108,14 +108,14 @@ import "./chunk-VY47VI2T.js";
108
108
  import "./chunk-PYIR4SSB.js";
109
109
  import "./chunk-DFEODULB.js";
110
110
  import "./chunk-EG4FHU5C.js";
111
- import "./chunk-NR36NW24.js";
111
+ import "./chunk-UWOA5L6N.js";
112
112
  import "./chunk-MMACM5G2.js";
113
113
  import "./chunk-54BGIBCR.js";
114
114
  import "./chunk-H4CKS5B2.js";
115
115
  import "./chunk-FXZTL7F7.js";
116
116
  import "./chunk-PYHOAXTI.js";
117
117
  import "./chunk-UP6MOYCB.js";
118
- import "./chunk-YYDOUOYM.js";
118
+ import "./chunk-6H7JWYTC.js";
119
119
  import "./chunk-BV2TIZ4F.js";
120
120
  import "./chunk-Z734BLO3.js";
121
121
  import "./chunk-HDDRVXX4.js";
@@ -137,7 +137,7 @@ import "./chunk-YLBE6PLR.js";
137
137
  import "./chunk-CDKHKZL5.js";
138
138
  import "./chunk-HSSERO3E.js";
139
139
  import "./chunk-NOUJNHWG.js";
140
- import "./chunk-M2BY5IZL.js";
140
+ import "./chunk-PVV42HM4.js";
141
141
  import "./chunk-HDQUQBPW.js";
142
142
  import "./chunk-6FC3IZ26.js";
143
143
  import "./chunk-KWLDB64F.js";
@@ -177,7 +177,7 @@ import "./chunk-OKTXM5H4.js";
177
177
  import "./chunk-DUAJKRR2.js";
178
178
  import "./chunk-NOB2T2FC.js";
179
179
  import "./chunk-3I4FUAVJ.js";
180
- import "./chunk-TXCLDIXJ.js";
180
+ import "./chunk-K6PWPC7O.js";
181
181
  import "./chunk-5JMAGU26.js";
182
182
  import "./chunk-YBPYIAA5.js";
183
183
  import "./chunk-HK6KYIF6.js";
@@ -309,7 +309,8 @@ import "./chunk-D3AUQXTJ.js";
309
309
  import "./chunk-7MOYAT4Y.js";
310
310
  import "./chunk-LTWFU6VJ.js";
311
311
  import "./chunk-UFGFMWAD.js";
312
- import "./chunk-ZPZLGN4Y.js";
312
+ import "./chunk-XZ7QLOTJ.js";
313
+ import "./chunk-3UXOZBHV.js";
313
314
  import "./chunk-ZXBTTMGZ.js";
314
315
  import "./chunk-YMOPYG3K.js";
315
316
  import "./chunk-2LSZVONP.js";
@@ -7,11 +7,12 @@ import {
7
7
  materializeAfterSemanticConsolidation,
8
8
  parseConsolidationResponse,
9
9
  parseOperatorAwareConsolidationResponse
10
- } from "./chunk-TXCLDIXJ.js";
10
+ } from "./chunk-K6PWPC7O.js";
11
11
  import {
12
12
  resolveExtensionsRoot
13
13
  } from "./chunk-5JMAGU26.js";
14
- import "./chunk-ZPZLGN4Y.js";
14
+ import "./chunk-XZ7QLOTJ.js";
15
+ import "./chunk-3UXOZBHV.js";
15
16
  import "./chunk-ZXBTTMGZ.js";
16
17
  import "./chunk-4PHT7KE2.js";
17
18
  import "./chunk-UHVXE24Y.js";
@@ -11,7 +11,8 @@ import "./chunk-VEWZZM3H.js";
11
11
  import "./chunk-J3JEW5CT.js";
12
12
  import "./chunk-5TBO7CDR.js";
13
13
  import "./chunk-EMDLM3LW.js";
14
- import "./chunk-ZPZLGN4Y.js";
14
+ import "./chunk-XZ7QLOTJ.js";
15
+ import "./chunk-3UXOZBHV.js";
15
16
  import "./chunk-ZXBTTMGZ.js";
16
17
  import "./chunk-4PHT7KE2.js";
17
18
  import "./chunk-UHVXE24Y.js";
@@ -100,7 +100,8 @@ import "../chunk-D3AUQXTJ.js";
100
100
  import "../chunk-7MOYAT4Y.js";
101
101
  import "../chunk-LTWFU6VJ.js";
102
102
  import "../chunk-UFGFMWAD.js";
103
- import "../chunk-ZPZLGN4Y.js";
103
+ import "../chunk-XZ7QLOTJ.js";
104
+ import "../chunk-3UXOZBHV.js";
104
105
  import "../chunk-ZXBTTMGZ.js";
105
106
  import "../chunk-YMOPYG3K.js";
106
107
  import "../chunk-2LSZVONP.js";
@@ -17,6 +17,14 @@ declare const OKF_LOG_TRUNCATION_MARKER = "<!-- okf-log-truncated -->";
17
17
  declare const DEFAULT_OKF_LOG_MAX_ENTRIES = 500;
18
18
  interface ExportOkfOptions {
19
19
  memoryDir: string;
20
+ /**
21
+ * Optional namespace segment. Resolved here, once, through the shared
22
+ * containment guard: both CLI entry points used to join the raw operator
23
+ * value onto `memoryDir/namespaces`, so `--namespace ../../..` escaped the
24
+ * namespace root and exported an arbitrary tree (rule 9 — one resolver, not
25
+ * a per-caller guard).
26
+ */
27
+ namespace?: string;
20
28
  outDir: string;
21
29
  includeStatus?: readonly string[];
22
30
  includeCategories?: readonly string[];
@@ -3,11 +3,12 @@ import {
3
3
  OKF_LOG_TRUNCATION_MARKER,
4
4
  exportOkfBundle,
5
5
  parseIncludeStatus
6
- } from "../chunk-YYDOUOYM.js";
6
+ } from "../chunk-6H7JWYTC.js";
7
7
  import {
8
8
  OKF_EXPORT_VERSION
9
- } from "../chunk-M2BY5IZL.js";
9
+ } from "../chunk-PVV42HM4.js";
10
10
  import "../chunk-D24OXEPB.js";
11
+ import "../chunk-3UXOZBHV.js";
11
12
  import "../chunk-4PHT7KE2.js";
12
13
  import "../chunk-UHVXE24Y.js";
13
14
  import "../chunk-4ZWCQJNM.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/core",
3
- "version": "9.66.10",
3
+ "version": "9.66.11",
4
4
  "description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -3217,7 +3217,7 @@
3217
3217
  "core"
3218
3218
  ],
3219
3219
  "peerDependencies": {
3220
- "@remnic/coding-graph": "^9.66.10"
3220
+ "@remnic/coding-graph": "^9.66.11"
3221
3221
  },
3222
3222
  "peerDependenciesMeta": {
3223
3223
  "@remnic/coding-graph": {
@@ -1,4 +1,3 @@
1
- import path from "node:path";
2
1
  import type { Orchestrator } from "../orchestrator.js";
3
2
  import type { CliCommand } from "../cli.js";
4
3
  import { runOkfCliCommand } from "../okf/cli.js";
@@ -44,11 +43,9 @@ export function registerExportOkfCommand(exportCmd: CliCommand, orchestrator: Or
44
43
  if (!out) throw new Error("Missing --out");
45
44
  const includeStatus = parseIncludeStatus(options.includeStatus);
46
45
  const namespace = options.namespace ? String(options.namespace) : "";
47
- const memoryDir = namespace
48
- ? path.join(orchestrator.config.memoryDir, "namespaces", namespace)
49
- : orchestrator.config.memoryDir;
50
46
  const result = await exportOkfBundle({
51
- memoryDir,
47
+ memoryDir: orchestrator.config.memoryDir,
48
+ namespace,
52
49
  outDir: out,
53
50
  includeStatus,
54
51
  includeCategories: options.includeCategories ? String(options.includeCategories).split(",") : undefined,
package/src/okf/render.ts CHANGED
@@ -9,6 +9,7 @@
9
9
  * bundles are byte-compatible in convention (rule 38 — deterministic
10
10
  * rendering discipline; rule 9 — stable seam over shared mechanics).
11
11
  */
12
+ import { randomUUID } from "node:crypto";
12
13
  import { lstatSync, mkdirSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
13
14
  import path from "node:path";
14
15
 
@@ -76,6 +77,7 @@ export function publishBundle(staging: string, outDir: string, force: boolean):
76
77
  try {
77
78
  const stat = lstatSync(outDir);
78
79
  if (stat.isSymbolicLink()) throw new Error(`--out must not be a symlink: ${outDir}`);
80
+ if (!stat.isDirectory()) throw new Error(`--out exists and is not a directory: ${outDir}`);
79
81
  exists = true;
80
82
  const entries = readdirSync(outDir).filter((name) => name !== "." && name !== "..");
81
83
  if (entries.length > 0 && !force) {
@@ -87,7 +89,12 @@ export function publishBundle(staging: string, outDir: string, force: boolean):
87
89
  }
88
90
  mkdirSync(path.dirname(outDir), { recursive: true });
89
91
  if (exists && force) {
90
- const backup = `${outDir}.okf-prev`;
92
+ // A fixed backup name is not reusable: an earlier crash between the two
93
+ // renames, or an unrelated directory a user created, leaves the path
94
+ // occupied and every later --force export fails on the rename. Each
95
+ // attempt takes its own name and removes it on both the success and the
96
+ // rollback path.
97
+ const backup = `${outDir}.okf-prev-${randomUUID().slice(0, 8)}`;
91
98
  try {
92
99
  renameSync(outDir, backup);
93
100
  } catch {
@@ -100,8 +107,10 @@ export function publishBundle(staging: string, outDir: string, force: boolean):
100
107
  } catch (err) {
101
108
  try {
102
109
  renameSync(backup, outDir);
110
+ rmSync(staging, { recursive: true, force: true });
103
111
  } catch {
104
- // Keep backup if restore fails.
112
+ // Keep the backup when the restore itself fails: it is the only
113
+ // surviving copy of the operator's previous bundle.
105
114
  }
106
115
  throw err;
107
116
  }
@@ -1,5 +1,5 @@
1
1
  import assert from "node:assert/strict";
2
- import { mkdtemp, readFile, rm, writeFile, symlink } from "node:fs/promises";
2
+ import { mkdir, mkdtemp, readFile, rm, writeFile, symlink } from "node:fs/promises";
3
3
  import { existsSync, lstatSync, readdirSync } from "node:fs";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
@@ -162,3 +162,79 @@ function collectFiles(root: string): string[] {
162
162
  walk(root);
163
163
  return out.sort();
164
164
  }
165
+
166
+ test("--namespace cannot escape the namespace root", async () => {
167
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-okf-ns-"));
168
+ const outDir = path.join(await mkdtemp(path.join(os.tmpdir(), "remnic-okf-nsout-")), "bundle");
169
+ try {
170
+ await seedStore(memoryDir);
171
+ // A raw path.join on the operator value used to resolve outside
172
+ // <memoryDir>/namespaces and export an arbitrary tree.
173
+ for (const namespace of ["../../..", "../sibling", path.resolve(memoryDir)]) {
174
+ await assert.rejects(
175
+ () => exportOkfBundle({ memoryDir, namespace, outDir }),
176
+ /invalid --namespace/,
177
+ `namespace ${namespace} must be rejected`,
178
+ );
179
+ }
180
+ assert.equal(existsSync(outDir), false, "a rejected namespace must not create the bundle");
181
+ } finally {
182
+ await rm(memoryDir, { recursive: true, force: true });
183
+ }
184
+ });
185
+
186
+ test("a plain --namespace still exports from the namespace subtree", async () => {
187
+ const root = await mkdtemp(path.join(os.tmpdir(), "remnic-okf-nsok-"));
188
+ const outDir = path.join(root, "bundle");
189
+ try {
190
+ await seedStore(path.join(root, "namespaces", "team-a"));
191
+ const result = await exportOkfBundle({ memoryDir: root, namespace: "team-a", outDir });
192
+ assert.ok(result.exported > 0, "namespaced export must find its own memories");
193
+ assert.ok(existsSync(path.join(outDir, "index.md")));
194
+ } finally {
195
+ await rm(root, { recursive: true, force: true });
196
+ }
197
+ });
198
+
199
+ test("a leftover backup directory does not block a forced export", async () => {
200
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-okf-bk-"));
201
+ const root = await mkdtemp(path.join(os.tmpdir(), "remnic-okf-bkout-"));
202
+ const outDir = path.join(root, "bundle");
203
+ try {
204
+ await seedStore(memoryDir);
205
+ await exportOkfBundle({ memoryDir, outDir });
206
+ // Simulate a crash between the two publish renames, or any directory a
207
+ // user happens to own at the old fixed backup path.
208
+ const stale = `${outDir}.okf-prev`;
209
+ await mkdir(stale, { recursive: true });
210
+ await writeFile(path.join(stale, "keep.md"), "stale\n", "utf8");
211
+
212
+ const again = await exportOkfBundle({ memoryDir, outDir, force: true });
213
+ assert.ok(again.exported > 0);
214
+ assert.ok(existsSync(path.join(outDir, "index.md")), "forced export must republish");
215
+ assert.equal(existsSync(path.join(stale, "keep.md")), true, "an unrelated directory is left alone");
216
+ const leftovers = readdirSync(root).filter((name) => name.startsWith("bundle.okf-prev-"));
217
+ assert.deepEqual(leftovers, [], "each attempt removes its own backup");
218
+ } finally {
219
+ await rm(memoryDir, { recursive: true, force: true });
220
+ await rm(root, { recursive: true, force: true });
221
+ }
222
+ });
223
+
224
+ test("--out that is a file is rejected before anything is written", async () => {
225
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-okf-file-"));
226
+ const root = await mkdtemp(path.join(os.tmpdir(), "remnic-okf-fileout-"));
227
+ const outDir = path.join(root, "bundle");
228
+ try {
229
+ await seedStore(memoryDir);
230
+ await writeFile(outDir, "not a directory\n", "utf8");
231
+ await assert.rejects(
232
+ () => exportOkfBundle({ memoryDir, outDir, force: true }),
233
+ /--out exists and is not a directory/,
234
+ );
235
+ assert.equal(await readFile(outDir, "utf8"), "not a directory\n", "the file is left untouched");
236
+ } finally {
237
+ await rm(memoryDir, { recursive: true, force: true });
238
+ await rm(root, { recursive: true, force: true });
239
+ }
240
+ });
@@ -1,6 +1,5 @@
1
- import { rmSync } from "node:fs";
1
+ import { mkdirSync, rmSync } from "node:fs";
2
2
  import { mkdtemp } from "node:fs/promises";
3
- import os from "node:os";
4
3
  import path from "node:path";
5
4
 
6
5
  import { parseOaiMemCitation } from "../citations.js";
@@ -8,6 +7,7 @@ import { inferMemoryStatus } from "../memory-lifecycle-ledger-utils.js";
8
7
  import { normalizeProjectionPreview } from "../memory-projection-format.js";
9
8
  import { lintOkfDir } from "../okf/lint.js";
10
9
  import { okfTypeForMemory, OKF_PROFILE_TYPE } from "../okf/type-mapping.js";
10
+ import { resolveNamespaceChildRoot } from "../namespaces/path.js";
11
11
  import {
12
12
  OKF_EXPORT_VERSION,
13
13
  publishBundle,
@@ -38,6 +38,14 @@ const MEMORY_STATUSES: readonly MemoryStatus[] = [
38
38
 
39
39
  export interface ExportOkfOptions {
40
40
  memoryDir: string;
41
+ /**
42
+ * Optional namespace segment. Resolved here, once, through the shared
43
+ * containment guard: both CLI entry points used to join the raw operator
44
+ * value onto `memoryDir/namespaces`, so `--namespace ../../..` escaped the
45
+ * namespace root and exported an arbitrary tree (rule 9 — one resolver, not
46
+ * a per-caller guard).
47
+ */
48
+ namespace?: string;
41
49
  outDir: string;
42
50
  includeStatus?: readonly string[];
43
51
  includeCategories?: readonly string[];
@@ -71,15 +79,19 @@ export function parseIncludeStatus(raw: unknown): MemoryStatus[] {
71
79
  export async function exportOkfBundle(opts: ExportOkfOptions): Promise<ExportOkfResult> {
72
80
  const outDir = path.resolve(opts.outDir);
73
81
  rejectSymlinkPath(outDir);
82
+ const namespace = (opts.namespace ?? "").trim();
83
+ const memoryDir = namespace
84
+ ? resolveNamespaceChildRoot(opts.memoryDir, namespace, "--namespace")
85
+ : opts.memoryDir;
74
86
  const includeStatus = new Set(parseIncludeStatus(opts.includeStatus));
75
87
  const includeCategories = opts.includeCategories?.length ? new Set(opts.includeCategories) : null;
76
88
  const excludeTags = new Set(opts.excludeTags ?? []);
77
- const storage = new StorageManager(opts.memoryDir);
89
+ const storage = new StorageManager(memoryDir);
78
90
  const memories = await storage.readAllMemories();
79
91
  const included: MemoryFile[] = [];
80
92
  let excluded = 0;
81
93
  for (const memory of memories) {
82
- const rel = toRel(memory.path, opts.memoryDir);
94
+ const rel = toRel(memory.path, memoryDir);
83
95
  if (!opts.includeWearables && rel.startsWith("wearables/")) {
84
96
  excluded += 1;
85
97
  continue;
@@ -99,17 +111,21 @@ export async function exportOkfBundle(opts: ExportOkfOptions): Promise<ExportOkf
99
111
  }
100
112
  included.push(memory);
101
113
  }
102
- included.sort((a, b) => toRel(a.path, opts.memoryDir).localeCompare(toRel(b.path, opts.memoryDir)));
114
+ included.sort((a, b) => toRel(a.path, memoryDir).localeCompare(toRel(b.path, memoryDir)));
103
115
 
104
- const staging = await mkdtemp(path.join(os.tmpdir(), "remnic-okf-export-"));
116
+ // Stage inside the destination's parent, not the OS temp dir: publishBundle
117
+ // finishes with a rename, and a rename across filesystems fails with EXDEV
118
+ // whenever --out lives on a different mount than /tmp.
119
+ mkdirSync(path.dirname(outDir), { recursive: true });
120
+ const staging = await mkdtemp(path.join(path.dirname(outDir), ".remnic-okf-export-"));
105
121
  const byCategory: Record<string, number> = {};
106
122
  const idToRel = new Map<string, string>();
107
123
  for (const memory of included) {
108
- const rel = toRel(memory.path, opts.memoryDir);
124
+ const rel = toRel(memory.path, memoryDir);
109
125
  idToRel.set(memory.frontmatter.id, rel);
110
126
  }
111
127
  for (const memory of included) {
112
- const rel = toRel(memory.path, opts.memoryDir);
128
+ const rel = toRel(memory.path, memoryDir);
113
129
  const rendered = renderMemory(memory, rel, idToRel);
114
130
  writeBundleFile(staging, rel, rendered);
115
131
  const category = memory.frontmatter.category;
@@ -140,7 +156,7 @@ export async function exportOkfBundle(opts: ExportOkfOptions): Promise<ExportOkf
140
156
  writeBundleFile(staging, "log.md", renderLog(events, idToRel, opts.logMaxEntries ?? DEFAULT_OKF_LOG_MAX_ENTRIES));
141
157
  }
142
158
 
143
- writeIndexes(staging, included, opts.memoryDir);
159
+ writeIndexes(staging, included, memoryDir);
144
160
  const lint = lintOkfDir(staging);
145
161
  const blocking = lint.findings.filter((f) => f.code !== "skipped_encrypted" && f.code !== "reserved_basename");
146
162
  if (blocking.length > 0) {