@remnic/core 9.66.10 → 9.66.12
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.
- package/dist/access-admin-ops-surface.d.ts +1 -1
- package/dist/access-admin-ops-surface.js +24 -23
- package/dist/access-authorization-probe.d.ts +1 -1
- package/dist/access-authorization-probe.js +25 -24
- package/dist/access-boundary.d.ts +1 -1
- package/dist/access-boundary.js +24 -23
- package/dist/access-cli.js +82 -81
- package/dist/access-cli.js.map +1 -1
- package/dist/access-extraction-force-flush.d.ts +1 -1
- package/dist/access-extraction-force-flush.js +24 -23
- package/dist/access-http-lcm-compaction.d.ts +1 -1
- package/dist/access-http-lifecycle-flush.d.ts +1 -1
- package/dist/access-http-offline-stream.d.ts +1 -1
- package/dist/access-http-query.js +26 -25
- package/dist/access-http.d.ts +2 -2
- package/dist/access-http.js +42 -41
- package/dist/access-identity-continuity-surface.js +24 -23
- package/dist/access-lcm-surface.d.ts +1 -1
- package/dist/access-lcm-surface.js +24 -23
- package/dist/access-mcp.d.ts +1 -1
- package/dist/access-mcp.js +33 -32
- package/dist/access-namespace-preflight.js +24 -23
- package/dist/access-observe-write-surface.d.ts +1 -1
- package/dist/access-observe-write-surface.js +24 -23
- package/dist/access-operations-batch.js +27 -26
- package/dist/access-operations.d.ts +6 -6
- package/dist/access-operations.js +32 -31
- package/dist/access-recall-concurrency.d.ts +1 -1
- package/dist/access-recall-concurrency.js +24 -23
- package/dist/access-recall-response.d.ts +1 -1
- package/dist/access-recall-response.js +24 -23
- package/dist/access-recall-surface.d.ts +1 -1
- package/dist/access-recall-surface.js +24 -23
- package/dist/access-schema.d.ts +89 -89
- package/dist/access-schema.js +4 -4
- package/dist/access-service-helpers.d.ts +1 -1
- package/dist/access-service.d.ts +1 -1
- package/dist/access-service.js +24 -23
- package/dist/access-surface-catalog.d.ts +1 -1
- package/dist/access-surface-catalog.js +26 -25
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/briefing.js +10 -10
- package/dist/buffer.js +4 -4
- package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-GWVG7LGC.js} +2 -2
- package/dist/causal-consolidation.js +12 -11
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/chunk-3UXOZBHV.js +20 -0
- package/dist/chunk-3UXOZBHV.js.map +1 -0
- package/dist/{chunk-XN33JLRF.js → chunk-46XRYYOJ.js} +3 -3
- package/dist/{chunk-WD424KYT.js → chunk-4L7OKRGP.js} +2 -2
- package/dist/{chunk-ZPZLGN4Y.js → chunk-6KGBI54C.js} +5 -21
- package/dist/chunk-6KGBI54C.js.map +1 -0
- package/dist/{chunk-245QROHZ.js → chunk-6SRUPUTY.js} +7 -7
- package/dist/{chunk-ZIGSNLYI.js → chunk-A6HVSRGM.js} +3 -3
- package/dist/{chunk-JBPKEARU.js → chunk-AU7Q3LSC.js} +4 -4
- package/dist/{chunk-7BS5UVXA.js → chunk-AWMS3IFB.js} +3 -3
- package/dist/{chunk-HJQZQUPC.js → chunk-BE4Y6GK6.js} +6 -6
- package/dist/{chunk-D3AUQXTJ.js → chunk-BT7AQZQX.js} +2 -2
- package/dist/{chunk-IPLYGWQF.js → chunk-BUKK5SWA.js} +6 -6
- package/dist/{chunk-CEH2AFXG.js → chunk-C24UZSFW.js} +6 -6
- package/dist/{chunk-HCERYDRI.js → chunk-CJTZO5MW.js} +2 -2
- package/dist/{chunk-4QKWBVEE.js → chunk-CR73YTWC.js} +13 -13
- package/dist/{chunk-LTWFU6VJ.js → chunk-DDAHG5ZD.js} +2 -2
- package/dist/{chunk-S47XNAWD.js → chunk-EU5PDHU2.js} +2 -2
- package/dist/{chunk-KT3ERTF6.js → chunk-F3H7VNM4.js} +2 -2
- package/dist/{chunk-EG4FHU5C.js → chunk-FJP6MVRK.js} +2 -2
- package/dist/{chunk-7FLINMUE.js → chunk-GEHEBLVY.js} +2 -2
- package/dist/{chunk-KWVXCRFZ.js → chunk-GHSJBVVQ.js} +95 -55
- package/dist/chunk-GHSJBVVQ.js.map +1 -0
- package/dist/{chunk-G3B3LP6A.js → chunk-GRZYF6KK.js} +14 -14
- package/dist/{chunk-E2PORO7O.js → chunk-H6VYVOVP.js} +2 -2
- package/dist/{chunk-QKTGCL6M.js → chunk-HGRRJDUH.js} +2 -2
- package/dist/{chunk-J2R275FN.js → chunk-HQJM3BKH.js} +2 -2
- package/dist/{chunk-ZRQPX2DW.js → chunk-I3X5NWDU.js} +2 -2
- package/dist/{chunk-TGKLVAML.js → chunk-I7FVQMAA.js} +2 -2
- package/dist/{chunk-NR36NW24.js → chunk-IDPFBRPJ.js} +72 -75
- package/dist/{chunk-NR36NW24.js.map → chunk-IDPFBRPJ.js.map} +1 -1
- package/dist/{chunk-CNZVEU4X.js → chunk-IE7N2YK6.js} +2 -2
- package/dist/{chunk-M2BY5IZL.js → chunk-IYFL3534.js} +6 -3
- package/dist/chunk-IYFL3534.js.map +1 -0
- package/dist/{chunk-X5SDZMO7.js → chunk-JGXMA6HL.js} +2 -2
- package/dist/{chunk-SQEIPTDV.js → chunk-JKXPFIPC.js} +2 -2
- package/dist/{chunk-Y2ECPAJI.js → chunk-K5JFIA44.js} +7 -7
- package/dist/{chunk-JNZSNJ43.js → chunk-K7VTD5AZ.js} +2 -2
- package/dist/{chunk-E62M4H4M.js → chunk-KDCPIXIY.js} +2 -2
- package/dist/{chunk-TS37MXC7.js → chunk-KZAN4JXY.js} +2 -2
- package/dist/{chunk-227UFSEQ.js → chunk-LEPOP46W.js} +4 -4
- package/dist/{chunk-5E7KXID5.js → chunk-M3BFW3VX.js} +1 -1
- package/dist/{chunk-VSQG4AXL.js → chunk-MUYJQ273.js} +2 -2
- package/dist/{chunk-5KVMT25I.js → chunk-MWLBRFLY.js} +2 -2
- package/dist/{chunk-2OXBS2LV.js → chunk-NROLO2HY.js} +2 -2
- package/dist/{chunk-YYDOUOYM.js → chunk-O2HDDAUM.js} +17 -12
- package/dist/chunk-O2HDDAUM.js.map +1 -0
- package/dist/{chunk-RLEJULGN.js → chunk-OMA2B7GY.js} +2 -2
- package/dist/{chunk-TXCLDIXJ.js → chunk-OQT34EVH.js} +2 -2
- package/dist/{chunk-2NLLXCJG.js → chunk-OWHERGF2.js} +2 -2
- package/dist/{chunk-4PWANX5V.js → chunk-PKRYQ222.js} +6 -6
- package/dist/{chunk-DJLH3XWM.js → chunk-QMIKXIAM.js} +2 -2
- package/dist/{chunk-Y6KDJQLK.js → chunk-QRHKJT7F.js} +7 -7
- package/dist/{chunk-7MOYAT4Y.js → chunk-QXDICB4J.js} +3 -3
- package/dist/{chunk-KWLDB64F.js → chunk-QZPSIQRB.js} +2 -2
- package/dist/{chunk-54BRW3BL.js → chunk-REJCULWA.js} +2 -2
- package/dist/{chunk-ISX7JODR.js → chunk-SBUQ4KIE.js} +2 -2
- package/dist/{chunk-HPGXB56F.js → chunk-STUPJFG7.js} +2 -2
- package/dist/{chunk-LPSTQR26.js → chunk-SV3MDTJI.js} +2 -2
- package/dist/{chunk-MK7EJLB4.js → chunk-UJICQ77Y.js} +2 -2
- package/dist/{chunk-4PHT7KE2.js → chunk-UUGU26P2.js} +2 -2
- package/dist/{chunk-X7Y7WX73.js → chunk-UVYI6VIX.js} +1 -1
- package/dist/{chunk-QB66LBXA.js → chunk-WGVAEVT7.js} +2 -2
- package/dist/{chunk-2L5FSNPC.js → chunk-X2NFFBTO.js} +7 -7
- package/dist/{chunk-EMAEDM2J.js → chunk-YWD5UIWY.js} +2 -2
- package/dist/{chunk-C6TMLF3A.js → chunk-ZFE7NXB7.js} +4 -4
- package/dist/{cli-CFpMhqz5.d.ts → cli-Bvy4jdBu.d.ts} +1 -1
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +58 -57
- package/dist/coding/export-okf-codegraph.js +10 -10
- package/dist/compounding/engine.js +10 -10
- package/dist/connectors/codex-materialize-runner.js +11 -10
- package/dist/connectors/index.js +11 -10
- package/dist/convergence-refresh.js +12 -12
- package/dist/conversation-index/backend.js +2 -2
- package/dist/corpus-watermark.js +12 -12
- package/dist/entity-retrieval.js +10 -10
- package/dist/external-wiki-access.d.ts +1 -1
- package/dist/external-wiki-access.js +25 -24
- package/dist/external-wiki-mcp-tools.d.ts +1 -1
- package/dist/extraction-span-provenance.d.ts +28 -0
- package/dist/extraction-span-provenance.js +24 -0
- package/dist/extraction-span-provenance.js.map +1 -0
- package/dist/extraction.js +14 -13
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/index.d.ts +700 -681
- package/dist/index.js +116 -109
- package/dist/lcm/engine.js +3 -3
- package/dist/lcm/index.js +6 -6
- package/dist/location/index.js +12 -12
- package/dist/maintenance/memory-governance.js +10 -10
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +10 -10
- package/dist/maintenance/rebuild-memory-projection.js +11 -11
- package/dist/mcp-memory-inspector-app.d.ts +1 -1
- package/dist/memory-subject-cli.d.ts +1 -1
- package/dist/memory-subject.d.ts +1 -1
- package/dist/namespaces/migrate.js +11 -11
- package/dist/namespaces/search.js +8 -8
- package/dist/namespaces/storage.js +10 -10
- package/dist/operator-doctor-corpus.js +13 -13
- package/dist/operator-toolkit.js +18 -18
- package/dist/orchestration/maintenance.js +18 -18
- package/dist/orchestrator.js +83 -82
- package/dist/{public-http-C9-CHc40.d.ts → public-http-2MyshWxl.d.ts} +1 -1
- package/dist/qmd.js +4 -4
- package/dist/recall-deep-rewrite.d.ts +23 -0
- package/dist/recall-deep-rewrite.js +29 -0
- package/dist/recall-deep-rewrite.js.map +1 -0
- package/dist/schemas.d.ts +176 -176
- package/dist/search/factory.js +7 -7
- package/dist/search/index.js +11 -11
- package/dist/semantic-consolidation.js +12 -11
- package/dist/semantic-rule-promotion.js +10 -10
- package/dist/semantic-rule-verifier.js +10 -10
- package/dist/shared-context/manager.d.ts +6 -6
- package/dist/source-agent-qualifier.js +13 -12
- package/dist/standup/build.js +10 -10
- package/dist/storage.js +9 -9
- package/dist/support-passport/index.d.ts +4 -4
- package/dist/support-passport/index.js +27 -26
- package/dist/transfer/autodetect.js +1 -1
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +3 -3
- package/dist/transfer/capsule-import.js +2 -2
- package/dist/transfer/export-okf.d.ts +8 -0
- package/dist/transfer/export-okf.js +12 -11
- package/dist/transfer/types.d.ts +78 -78
- package/dist/verified-recall.js +10 -10
- package/dist/who-knows-cli.d.ts +1 -1
- package/dist/who-knows-cli.js +11 -11
- package/dist/who-knows.d.ts +1 -1
- package/dist/who-knows.js +10 -10
- package/package.json +2 -2
- package/src/activity/timeline/analysis-failure.test.ts +104 -0
- package/src/activity/timeline/analysis-failure.ts +60 -0
- package/src/activity/timeline/index.ts +1 -0
- package/src/cli/okf-commands.ts +2 -5
- package/src/dedup/index.ts +7 -0
- package/src/dedup/merge-verdict.test.ts +74 -0
- package/src/dedup/merge-verdict.ts +31 -0
- package/src/extraction-span-provenance.test.ts +85 -0
- package/src/extraction-span-provenance.ts +41 -0
- package/src/okf/render.ts +11 -2
- package/src/recall-deep-rewrite.test.ts +199 -0
- package/src/recall-deep-rewrite.ts +50 -0
- package/src/transfer/export-okf.test.ts +77 -1
- package/src/transfer/export-okf.ts +25 -9
- package/dist/chunk-KWVXCRFZ.js.map +0 -1
- package/dist/chunk-M2BY5IZL.js.map +0 -1
- package/dist/chunk-YYDOUOYM.js.map +0 -1
- package/dist/chunk-ZPZLGN4Y.js.map +0 -1
- package/dist/{access-service-DcfM0Vf0.d.ts → access-service-LbwWl05g.d.ts} +64 -64
- /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-GWVG7LGC.js.map} +0 -0
- /package/dist/{chunk-XN33JLRF.js.map → chunk-46XRYYOJ.js.map} +0 -0
- /package/dist/{chunk-WD424KYT.js.map → chunk-4L7OKRGP.js.map} +0 -0
- /package/dist/{chunk-245QROHZ.js.map → chunk-6SRUPUTY.js.map} +0 -0
- /package/dist/{chunk-ZIGSNLYI.js.map → chunk-A6HVSRGM.js.map} +0 -0
- /package/dist/{chunk-JBPKEARU.js.map → chunk-AU7Q3LSC.js.map} +0 -0
- /package/dist/{chunk-7BS5UVXA.js.map → chunk-AWMS3IFB.js.map} +0 -0
- /package/dist/{chunk-HJQZQUPC.js.map → chunk-BE4Y6GK6.js.map} +0 -0
- /package/dist/{chunk-D3AUQXTJ.js.map → chunk-BT7AQZQX.js.map} +0 -0
- /package/dist/{chunk-IPLYGWQF.js.map → chunk-BUKK5SWA.js.map} +0 -0
- /package/dist/{chunk-CEH2AFXG.js.map → chunk-C24UZSFW.js.map} +0 -0
- /package/dist/{chunk-HCERYDRI.js.map → chunk-CJTZO5MW.js.map} +0 -0
- /package/dist/{chunk-4QKWBVEE.js.map → chunk-CR73YTWC.js.map} +0 -0
- /package/dist/{chunk-LTWFU6VJ.js.map → chunk-DDAHG5ZD.js.map} +0 -0
- /package/dist/{chunk-S47XNAWD.js.map → chunk-EU5PDHU2.js.map} +0 -0
- /package/dist/{chunk-KT3ERTF6.js.map → chunk-F3H7VNM4.js.map} +0 -0
- /package/dist/{chunk-EG4FHU5C.js.map → chunk-FJP6MVRK.js.map} +0 -0
- /package/dist/{chunk-7FLINMUE.js.map → chunk-GEHEBLVY.js.map} +0 -0
- /package/dist/{chunk-G3B3LP6A.js.map → chunk-GRZYF6KK.js.map} +0 -0
- /package/dist/{chunk-E2PORO7O.js.map → chunk-H6VYVOVP.js.map} +0 -0
- /package/dist/{chunk-QKTGCL6M.js.map → chunk-HGRRJDUH.js.map} +0 -0
- /package/dist/{chunk-J2R275FN.js.map → chunk-HQJM3BKH.js.map} +0 -0
- /package/dist/{chunk-ZRQPX2DW.js.map → chunk-I3X5NWDU.js.map} +0 -0
- /package/dist/{chunk-TGKLVAML.js.map → chunk-I7FVQMAA.js.map} +0 -0
- /package/dist/{chunk-CNZVEU4X.js.map → chunk-IE7N2YK6.js.map} +0 -0
- /package/dist/{chunk-X5SDZMO7.js.map → chunk-JGXMA6HL.js.map} +0 -0
- /package/dist/{chunk-SQEIPTDV.js.map → chunk-JKXPFIPC.js.map} +0 -0
- /package/dist/{chunk-Y2ECPAJI.js.map → chunk-K5JFIA44.js.map} +0 -0
- /package/dist/{chunk-JNZSNJ43.js.map → chunk-K7VTD5AZ.js.map} +0 -0
- /package/dist/{chunk-E62M4H4M.js.map → chunk-KDCPIXIY.js.map} +0 -0
- /package/dist/{chunk-TS37MXC7.js.map → chunk-KZAN4JXY.js.map} +0 -0
- /package/dist/{chunk-227UFSEQ.js.map → chunk-LEPOP46W.js.map} +0 -0
- /package/dist/{chunk-5E7KXID5.js.map → chunk-M3BFW3VX.js.map} +0 -0
- /package/dist/{chunk-VSQG4AXL.js.map → chunk-MUYJQ273.js.map} +0 -0
- /package/dist/{chunk-5KVMT25I.js.map → chunk-MWLBRFLY.js.map} +0 -0
- /package/dist/{chunk-2OXBS2LV.js.map → chunk-NROLO2HY.js.map} +0 -0
- /package/dist/{chunk-RLEJULGN.js.map → chunk-OMA2B7GY.js.map} +0 -0
- /package/dist/{chunk-TXCLDIXJ.js.map → chunk-OQT34EVH.js.map} +0 -0
- /package/dist/{chunk-2NLLXCJG.js.map → chunk-OWHERGF2.js.map} +0 -0
- /package/dist/{chunk-4PWANX5V.js.map → chunk-PKRYQ222.js.map} +0 -0
- /package/dist/{chunk-DJLH3XWM.js.map → chunk-QMIKXIAM.js.map} +0 -0
- /package/dist/{chunk-Y6KDJQLK.js.map → chunk-QRHKJT7F.js.map} +0 -0
- /package/dist/{chunk-7MOYAT4Y.js.map → chunk-QXDICB4J.js.map} +0 -0
- /package/dist/{chunk-KWLDB64F.js.map → chunk-QZPSIQRB.js.map} +0 -0
- /package/dist/{chunk-54BRW3BL.js.map → chunk-REJCULWA.js.map} +0 -0
- /package/dist/{chunk-ISX7JODR.js.map → chunk-SBUQ4KIE.js.map} +0 -0
- /package/dist/{chunk-HPGXB56F.js.map → chunk-STUPJFG7.js.map} +0 -0
- /package/dist/{chunk-LPSTQR26.js.map → chunk-SV3MDTJI.js.map} +0 -0
- /package/dist/{chunk-MK7EJLB4.js.map → chunk-UJICQ77Y.js.map} +0 -0
- /package/dist/{chunk-4PHT7KE2.js.map → chunk-UUGU26P2.js.map} +0 -0
- /package/dist/{chunk-X7Y7WX73.js.map → chunk-UVYI6VIX.js.map} +0 -0
- /package/dist/{chunk-QB66LBXA.js.map → chunk-WGVAEVT7.js.map} +0 -0
- /package/dist/{chunk-2L5FSNPC.js.map → chunk-X2NFFBTO.js.map} +0 -0
- /package/dist/{chunk-EMAEDM2J.js.map → chunk-YWD5UIWY.js.map} +0 -0
- /package/dist/{chunk-C6TMLF3A.js.map → chunk-ZFE7NXB7.js.map} +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate a deep-recall REFINE rewrite (issue #2332).
|
|
3
|
+
*
|
|
4
|
+
* Pure. The budget check runs before the rewrite is inspected, so an
|
|
5
|
+
* exhausted budget cannot be bypassed by a valid-looking query. An empty
|
|
6
|
+
* or no-progress rewrite is rejected and treated as STOP.
|
|
7
|
+
*/
|
|
8
|
+
export const MAX_REFINES_PER_INVOCATION = 2;
|
|
9
|
+
|
|
10
|
+
export type RefineRewriteResult =
|
|
11
|
+
| { ok: true; refinedQuery: string }
|
|
12
|
+
| {
|
|
13
|
+
ok: false;
|
|
14
|
+
stop: true;
|
|
15
|
+
reason: "empty_rewrite" | "identical_rewrite" | "refine_budget_spent";
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function validateRefineRewrite(input: {
|
|
19
|
+
currentQuery: string;
|
|
20
|
+
refinedQuery: unknown;
|
|
21
|
+
refinesUsed: number;
|
|
22
|
+
}): RefineRewriteResult {
|
|
23
|
+
if (!Number.isInteger(input.refinesUsed) || input.refinesUsed < 0) {
|
|
24
|
+
throw new RangeError(
|
|
25
|
+
`refinesUsed must be a non-negative integer; got ${JSON.stringify(input.refinesUsed)}`,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
if (input.refinesUsed >= MAX_REFINES_PER_INVOCATION) {
|
|
29
|
+
return { ok: false, stop: true, reason: "refine_budget_spent" };
|
|
30
|
+
}
|
|
31
|
+
if (
|
|
32
|
+
typeof input.refinedQuery !== "string" ||
|
|
33
|
+
input.refinedQuery.trim() === ""
|
|
34
|
+
) {
|
|
35
|
+
return { ok: false, stop: true, reason: "empty_rewrite" };
|
|
36
|
+
}
|
|
37
|
+
const trimmed = input.refinedQuery.trim();
|
|
38
|
+
// Comparison key: case-folded with internal whitespace runs collapsed, so
|
|
39
|
+
// pure case/spacing drift is not progress. The returned rewrite keeps
|
|
40
|
+
// internal whitespace as the model wrote it.
|
|
41
|
+
const currentKey = input.currentQuery
|
|
42
|
+
.trim()
|
|
43
|
+
.toLowerCase()
|
|
44
|
+
.replace(/\s+/g, " ");
|
|
45
|
+
const refinedKey = trimmed.toLowerCase().replace(/\s+/g, " ");
|
|
46
|
+
if (refinedKey === currentKey) {
|
|
47
|
+
return { ok: false, stop: true, reason: "identical_rewrite" };
|
|
48
|
+
}
|
|
49
|
+
return { ok: true, refinedQuery: trimmed };
|
|
50
|
+
}
|
|
@@ -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(
|
|
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,
|
|
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,
|
|
114
|
+
included.sort((a, b) => toRel(a.path, memoryDir).localeCompare(toRel(b.path, memoryDir)));
|
|
103
115
|
|
|
104
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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) {
|