@remnic/core 9.57.4 → 9.57.5
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.js +23 -23
- package/dist/access-authorization-probe.js +24 -24
- package/dist/access-boundary.js +23 -23
- package/dist/access-cli.js +57 -57
- package/dist/access-extraction-force-flush.js +23 -23
- package/dist/access-http-query.js +23 -23
- package/dist/access-http.js +34 -34
- package/dist/access-identity-continuity-surface.js +23 -23
- package/dist/access-lcm-surface.js +23 -23
- package/dist/access-mcp.js +30 -30
- package/dist/access-memory-search-fanout.js +4 -3
- package/dist/access-namespace-preflight.js +23 -23
- package/dist/access-observe-write-surface.js +23 -23
- package/dist/access-offline-manifest.js +10 -10
- package/dist/access-operations-batch.js +26 -26
- package/dist/access-operations.js +29 -29
- package/dist/access-recall-concurrency.js +23 -23
- package/dist/access-recall-response.js +23 -23
- package/dist/access-recall-surface.js +23 -23
- package/dist/access-service.js +23 -23
- package/dist/access-surface-catalog.js +25 -25
- package/dist/active-memory-bridge.js +5 -5
- package/dist/briefing.js +5 -5
- package/dist/buffer.js +6 -5
- package/dist/causal-consolidation.js +7 -7
- package/dist/{chunk-O54B5YNM.js → chunk-24PNX2GF.js} +3 -3
- package/dist/{chunk-ONCWJBWV.js → chunk-3AJOJOFP.js} +2 -2
- package/dist/{chunk-QSDPVZPD.js → chunk-4O6R3GPK.js} +8 -8
- package/dist/{chunk-IXGQP4RZ.js → chunk-4SGDYYWV.js} +4 -4
- package/dist/{chunk-UTDMY476.js → chunk-4UUF2EX3.js} +3 -3
- package/dist/{chunk-T6EQPZ6E.js → chunk-4YZCIK5E.js} +16 -14
- package/dist/chunk-4YZCIK5E.js.map +1 -0
- package/dist/{chunk-S6AH7WSF.js → chunk-5PGYCWA7.js} +5 -5
- package/dist/{chunk-7NSC4IVA.js → chunk-6RK2X7BR.js} +3 -3
- package/dist/{chunk-VWP5DEHU.js → chunk-6SX4MY5Q.js} +2 -2
- package/dist/{chunk-GUNIXT36.js → chunk-7BY7V4YX.js} +2 -2
- package/dist/{chunk-R7UHWPYI.js → chunk-7PJARJS4.js} +3 -3
- package/dist/{chunk-V3IT3XNG.js → chunk-7VSHDLJQ.js} +6 -6
- package/dist/{chunk-SFCJCMZQ.js → chunk-A7266TRJ.js} +2 -2
- package/dist/{chunk-4THNU7OF.js → chunk-AW4A4XOP.js} +22 -22
- package/dist/{chunk-IFFW25NG.js → chunk-C5B3CIMJ.js} +34 -34
- package/dist/{chunk-TF3LMM5E.js → chunk-CSV34HIW.js} +3 -3
- package/dist/{chunk-FK7T55BA.js → chunk-CZUHNJHX.js} +19 -19
- package/dist/{chunk-NFQB6RJT.js → chunk-D4OCJSLV.js} +3 -3
- package/dist/{chunk-JKL53MOY.js → chunk-DLQQU4EG.js} +3 -3
- package/dist/{chunk-CME347TM.js → chunk-DNC4A7RS.js} +2 -2
- package/dist/{chunk-F2JRDPON.js → chunk-EGBDFJ36.js} +2 -2
- package/dist/{chunk-TFXYQNBH.js → chunk-FI3PIOOI.js} +9 -9
- package/dist/{chunk-CKSEJD7K.js → chunk-FKTD7IVS.js} +2 -2
- package/dist/{chunk-FONW766A.js → chunk-GXIY65UH.js} +2 -2
- package/dist/{chunk-2YREVL4T.js → chunk-HZC3OECF.js} +2 -2
- package/dist/{chunk-X5OIPQME.js → chunk-HZNYJN77.js} +6 -6
- package/dist/{chunk-LYPG7GOO.js → chunk-I2GIYTRU.js} +2 -2
- package/dist/{chunk-FR3FNPCJ.js → chunk-IT57AOMC.js} +4 -4
- package/dist/{chunk-CMRUIKVA.js → chunk-KCT36JAW.js} +25 -9
- package/dist/chunk-KCT36JAW.js.map +1 -0
- package/dist/{chunk-KJH6W2I7.js → chunk-L3CHRBHE.js} +4 -4
- package/dist/{chunk-PSH3VFZ7.js → chunk-L3LRWDJW.js} +7 -7
- package/dist/{chunk-JFPZEWY3.js → chunk-LIF6DO6U.js} +37 -13
- package/dist/chunk-LIF6DO6U.js.map +1 -0
- package/dist/{chunk-QZR3WT3L.js → chunk-LJJWFV2Q.js} +2 -2
- package/dist/{chunk-TLVLJSWS.js → chunk-LRDP2AC2.js} +2 -2
- package/dist/{chunk-QV7OEHFN.js → chunk-LTBKKSCX.js} +2 -2
- package/dist/{chunk-IGWVMA4Y.js → chunk-MRV7VSQK.js} +20 -10
- package/dist/chunk-MRV7VSQK.js.map +1 -0
- package/dist/{chunk-6P2NUVM5.js → chunk-NEJQ65NV.js} +2 -2
- package/dist/{chunk-7PKNOXYB.js → chunk-NTJ6TY44.js} +2 -2
- package/dist/{chunk-HYVBJLEK.js → chunk-O5AFC5KM.js} +2 -2
- package/dist/{chunk-U3PN77QT.js → chunk-PCXDUHQH.js} +11 -4
- package/dist/chunk-PCXDUHQH.js.map +1 -0
- package/dist/{chunk-N655QIA2.js → chunk-PEDYKCJI.js} +3 -3
- package/dist/{chunk-TVKWT2CY.js → chunk-Q57GOHLF.js} +11 -9
- package/dist/{chunk-TVKWT2CY.js.map → chunk-Q57GOHLF.js.map} +1 -1
- package/dist/{chunk-4QFCAKNM.js → chunk-RUZM6Z52.js} +1 -1
- package/dist/{chunk-O4TTIMOP.js → chunk-SFR7J2G6.js} +2 -2
- package/dist/{chunk-ION47U3Z.js → chunk-SNW3YRO6.js} +2 -2
- package/dist/{chunk-XL24QQ63.js → chunk-TBUEE7XF.js} +2 -2
- package/dist/{chunk-TAPFJ3ET.js → chunk-U7XLOSG2.js} +3 -3
- package/dist/{chunk-T3HC7RJ7.js → chunk-USAUBGBC.js} +2 -2
- package/dist/{chunk-TAMY4NKO.js → chunk-USGKHF36.js} +2 -2
- package/dist/{chunk-XRPPHIRI.js → chunk-VUDALPI5.js} +2 -2
- package/dist/{chunk-EGGE52UU.js → chunk-VUFL7X3H.js} +13 -6
- package/dist/chunk-VUFL7X3H.js.map +1 -0
- package/dist/{chunk-HC55YCNK.js → chunk-VYIXCMUP.js} +4 -4
- package/dist/{chunk-HQ2VGCGP.js → chunk-XBGLEOID.js} +8 -8
- package/dist/{chunk-XX4HPFFM.js → chunk-XDL5CYZN.js} +4 -4
- package/dist/{chunk-THACANBI.js → chunk-YOKSRIFI.js} +3 -3
- package/dist/{chunk-BWJMBJCY.js → chunk-YTZA7U6Q.js} +6 -6
- package/dist/{chunk-4KM3V6VS.js → chunk-ZN7SMKAK.js} +2 -2
- package/dist/cli.js +45 -45
- package/dist/compounding/engine.js +5 -5
- package/dist/connectors/codex-materialize-runner.js +6 -6
- package/dist/connectors/index.js +6 -6
- package/dist/contradiction/index.js +3 -3
- package/dist/{contradiction-scan-522AYLQC.js → contradiction-scan-VOTQRRSX.js} +4 -4
- package/dist/convergence-refresh.js +7 -7
- package/dist/corpus-watermark.js +8 -8
- package/dist/entity-retrieval.js +5 -5
- package/dist/explicit-capture.js +4 -4
- package/dist/external-wiki-access.js +24 -24
- package/dist/extraction.js +11 -11
- package/dist/{first-start-migration-HZ4VPRFQ.js → first-start-migration-4NETATFF.js} +4 -4
- package/dist/{graph-edge-decay-ZXGBXNPW.js → graph-edge-decay-A4SDFMMS.js} +4 -3
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/index.js +57 -57
- package/dist/maintenance/memory-governance.js +5 -5
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +5 -5
- package/dist/maintenance/rebuild-memory-projection.js +6 -6
- package/dist/namespaces/migrate.js +7 -7
- package/dist/namespaces/principal.js +3 -2
- package/dist/namespaces/search.js +2 -2
- package/dist/namespaces/storage.js +6 -6
- package/dist/offline-sync-impression-drain.js +6 -6
- package/dist/operator-doctor-corpus.js +9 -9
- package/dist/operator-toolkit.js +16 -16
- package/dist/orchestration/maintenance.js +14 -14
- package/dist/orchestrator.js +57 -57
- package/dist/reconcile/manifest.js +3 -3
- package/dist/reconcile/plan.js +2 -2
- package/dist/routing/engine.js +2 -1
- package/dist/routing/store.js +3 -2
- package/dist/semantic-consolidation.js +7 -7
- package/dist/semantic-rule-promotion.js +5 -5
- package/dist/semantic-rule-verifier.js +5 -5
- package/dist/source-agent-qualifier.js +10 -10
- package/dist/storage.js +4 -4
- package/dist/support-passport/index.js +26 -26
- package/dist/tier-routing.js +3 -3
- package/dist/{tier-stats-7E6OBUSD.js → tier-stats-4B5IJ3NE.js} +4 -4
- package/dist/verified-recall.js +5 -5
- package/package.json +2 -2
- package/src/namespaces/catalog.ts +9 -9
- package/src/namespaces/identity.ts +15 -6
- package/src/namespaces/principal.test.ts +10 -1
- package/src/namespaces/principal.ts +17 -9
- package/src/namespaces/scope-profiles.ts +35 -13
- package/src/namespaces/storage.ts +63 -13
- package/src/namespaces/unicode-routing.test.ts +71 -0
- package/src/routing/engine.test.ts +30 -0
- package/src/routing/engine.ts +12 -3
- package/src/scopes/scope-plan.ts +8 -2
- package/src/storage-contract/namespace-routing.test.ts +18 -1
- package/dist/chunk-CMRUIKVA.js.map +0 -1
- package/dist/chunk-EGGE52UU.js.map +0 -1
- package/dist/chunk-IGWVMA4Y.js.map +0 -1
- package/dist/chunk-JFPZEWY3.js.map +0 -1
- package/dist/chunk-T6EQPZ6E.js.map +0 -1
- package/dist/chunk-U3PN77QT.js.map +0 -1
- /package/dist/{chunk-O54B5YNM.js.map → chunk-24PNX2GF.js.map} +0 -0
- /package/dist/{chunk-ONCWJBWV.js.map → chunk-3AJOJOFP.js.map} +0 -0
- /package/dist/{chunk-QSDPVZPD.js.map → chunk-4O6R3GPK.js.map} +0 -0
- /package/dist/{chunk-IXGQP4RZ.js.map → chunk-4SGDYYWV.js.map} +0 -0
- /package/dist/{chunk-UTDMY476.js.map → chunk-4UUF2EX3.js.map} +0 -0
- /package/dist/{chunk-S6AH7WSF.js.map → chunk-5PGYCWA7.js.map} +0 -0
- /package/dist/{chunk-7NSC4IVA.js.map → chunk-6RK2X7BR.js.map} +0 -0
- /package/dist/{chunk-VWP5DEHU.js.map → chunk-6SX4MY5Q.js.map} +0 -0
- /package/dist/{chunk-GUNIXT36.js.map → chunk-7BY7V4YX.js.map} +0 -0
- /package/dist/{chunk-R7UHWPYI.js.map → chunk-7PJARJS4.js.map} +0 -0
- /package/dist/{chunk-V3IT3XNG.js.map → chunk-7VSHDLJQ.js.map} +0 -0
- /package/dist/{chunk-SFCJCMZQ.js.map → chunk-A7266TRJ.js.map} +0 -0
- /package/dist/{chunk-4THNU7OF.js.map → chunk-AW4A4XOP.js.map} +0 -0
- /package/dist/{chunk-IFFW25NG.js.map → chunk-C5B3CIMJ.js.map} +0 -0
- /package/dist/{chunk-TF3LMM5E.js.map → chunk-CSV34HIW.js.map} +0 -0
- /package/dist/{chunk-FK7T55BA.js.map → chunk-CZUHNJHX.js.map} +0 -0
- /package/dist/{chunk-NFQB6RJT.js.map → chunk-D4OCJSLV.js.map} +0 -0
- /package/dist/{chunk-JKL53MOY.js.map → chunk-DLQQU4EG.js.map} +0 -0
- /package/dist/{chunk-CME347TM.js.map → chunk-DNC4A7RS.js.map} +0 -0
- /package/dist/{chunk-F2JRDPON.js.map → chunk-EGBDFJ36.js.map} +0 -0
- /package/dist/{chunk-TFXYQNBH.js.map → chunk-FI3PIOOI.js.map} +0 -0
- /package/dist/{chunk-CKSEJD7K.js.map → chunk-FKTD7IVS.js.map} +0 -0
- /package/dist/{chunk-FONW766A.js.map → chunk-GXIY65UH.js.map} +0 -0
- /package/dist/{chunk-2YREVL4T.js.map → chunk-HZC3OECF.js.map} +0 -0
- /package/dist/{chunk-X5OIPQME.js.map → chunk-HZNYJN77.js.map} +0 -0
- /package/dist/{chunk-LYPG7GOO.js.map → chunk-I2GIYTRU.js.map} +0 -0
- /package/dist/{chunk-FR3FNPCJ.js.map → chunk-IT57AOMC.js.map} +0 -0
- /package/dist/{chunk-KJH6W2I7.js.map → chunk-L3CHRBHE.js.map} +0 -0
- /package/dist/{chunk-PSH3VFZ7.js.map → chunk-L3LRWDJW.js.map} +0 -0
- /package/dist/{chunk-QZR3WT3L.js.map → chunk-LJJWFV2Q.js.map} +0 -0
- /package/dist/{chunk-TLVLJSWS.js.map → chunk-LRDP2AC2.js.map} +0 -0
- /package/dist/{chunk-QV7OEHFN.js.map → chunk-LTBKKSCX.js.map} +0 -0
- /package/dist/{chunk-6P2NUVM5.js.map → chunk-NEJQ65NV.js.map} +0 -0
- /package/dist/{chunk-7PKNOXYB.js.map → chunk-NTJ6TY44.js.map} +0 -0
- /package/dist/{chunk-HYVBJLEK.js.map → chunk-O5AFC5KM.js.map} +0 -0
- /package/dist/{chunk-N655QIA2.js.map → chunk-PEDYKCJI.js.map} +0 -0
- /package/dist/{chunk-4QFCAKNM.js.map → chunk-RUZM6Z52.js.map} +0 -0
- /package/dist/{chunk-O4TTIMOP.js.map → chunk-SFR7J2G6.js.map} +0 -0
- /package/dist/{chunk-ION47U3Z.js.map → chunk-SNW3YRO6.js.map} +0 -0
- /package/dist/{chunk-XL24QQ63.js.map → chunk-TBUEE7XF.js.map} +0 -0
- /package/dist/{chunk-TAPFJ3ET.js.map → chunk-U7XLOSG2.js.map} +0 -0
- /package/dist/{chunk-T3HC7RJ7.js.map → chunk-USAUBGBC.js.map} +0 -0
- /package/dist/{chunk-TAMY4NKO.js.map → chunk-USGKHF36.js.map} +0 -0
- /package/dist/{chunk-XRPPHIRI.js.map → chunk-VUDALPI5.js.map} +0 -0
- /package/dist/{chunk-HC55YCNK.js.map → chunk-VYIXCMUP.js.map} +0 -0
- /package/dist/{chunk-HQ2VGCGP.js.map → chunk-XBGLEOID.js.map} +0 -0
- /package/dist/{chunk-XX4HPFFM.js.map → chunk-XDL5CYZN.js.map} +0 -0
- /package/dist/{chunk-THACANBI.js.map → chunk-YOKSRIFI.js.map} +0 -0
- /package/dist/{chunk-BWJMBJCY.js.map → chunk-YTZA7U6Q.js.map} +0 -0
- /package/dist/{chunk-4KM3V6VS.js.map → chunk-ZN7SMKAK.js.map} +0 -0
- /package/dist/{contradiction-scan-522AYLQC.js.map → contradiction-scan-VOTQRRSX.js.map} +0 -0
- /package/dist/{first-start-migration-HZ4VPRFQ.js.map → first-start-migration-4NETATFF.js.map} +0 -0
- /package/dist/{graph-edge-decay-ZXGBXNPW.js.map → graph-edge-decay-A4SDFMMS.js.map} +0 -0
- /package/dist/{tier-stats-7E6OBUSD.js.map → tier-stats-4B5IJ3NE.js.map} +0 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isLikelyUnsafeRegex
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-PCXDUHQH.js";
|
|
4
|
+
import {
|
|
5
|
+
normalizeNamespaceIdentity
|
|
6
|
+
} from "./chunk-VUFL7X3H.js";
|
|
4
7
|
import {
|
|
5
8
|
resolveNamespaceCapabilities
|
|
6
9
|
} from "./chunk-JUOZ62Y7.js";
|
|
@@ -49,28 +52,35 @@ function resolvePrincipal(sessionKey, config) {
|
|
|
49
52
|
function canReadNamespace(principal, namespace, config) {
|
|
50
53
|
if (!resolveNamespaceCapabilities(config).namespaces) return true;
|
|
51
54
|
if (!principal) return false;
|
|
52
|
-
const
|
|
53
|
-
|
|
55
|
+
const identity = normalizeNamespaceIdentity(namespace);
|
|
56
|
+
const policy = config.namespacePolicies.find((p) => normalizeNamespaceIdentity(p.name) === identity);
|
|
57
|
+
if (!policy) {
|
|
58
|
+
return identity === normalizeNamespaceIdentity(config.defaultNamespace) || identity === normalizeNamespaceIdentity(config.sharedNamespace);
|
|
59
|
+
}
|
|
54
60
|
return policy.readPrincipals.includes(principal) || policy.readPrincipals.includes("*");
|
|
55
61
|
}
|
|
56
62
|
function canWriteNamespace(principal, namespace, config) {
|
|
57
63
|
if (!resolveNamespaceCapabilities(config).namespaces) return true;
|
|
58
64
|
if (!principal) return false;
|
|
59
|
-
const
|
|
60
|
-
|
|
65
|
+
const identity = normalizeNamespaceIdentity(namespace);
|
|
66
|
+
const policy = config.namespacePolicies.find((p) => normalizeNamespaceIdentity(p.name) === identity);
|
|
67
|
+
if (!policy) return identity === normalizeNamespaceIdentity(config.defaultNamespace);
|
|
61
68
|
return policy.writePrincipals.includes(principal) || policy.writePrincipals.includes("*");
|
|
62
69
|
}
|
|
63
70
|
function isNamespacePolicyCovered(namespace, config) {
|
|
64
71
|
if (!resolveNamespaceCapabilities(config).namespaces) return true;
|
|
65
|
-
const
|
|
66
|
-
|
|
72
|
+
const identity = normalizeNamespaceIdentity(namespace);
|
|
73
|
+
const policy = config.namespacePolicies.find((p) => normalizeNamespaceIdentity(p.name) === identity);
|
|
74
|
+
if (!policy) return identity === normalizeNamespaceIdentity(config.defaultNamespace);
|
|
67
75
|
return policy.writePrincipals.some((w) => w.trim().length > 0);
|
|
68
76
|
}
|
|
69
77
|
function defaultNamespaceForPrincipal(principal, config) {
|
|
70
78
|
if (!resolveNamespaceCapabilities(config).namespaces) return config.defaultNamespace;
|
|
71
79
|
if (!principal) return config.defaultNamespace;
|
|
72
|
-
const
|
|
73
|
-
|
|
80
|
+
const policy = config.namespacePolicies.find(
|
|
81
|
+
(p) => normalizeNamespaceIdentity(p.name) === normalizeNamespaceIdentity(principal)
|
|
82
|
+
);
|
|
83
|
+
return policy ? normalizeNamespaceIdentity(policy.name) : config.defaultNamespace;
|
|
74
84
|
}
|
|
75
85
|
function recallNamespacesForPrincipal(principal, config) {
|
|
76
86
|
const out = [];
|
|
@@ -111,4 +121,4 @@ export {
|
|
|
111
121
|
recallNamespacesForPrincipal,
|
|
112
122
|
citationAuthorizedNamespaces
|
|
113
123
|
};
|
|
114
|
-
//# sourceMappingURL=chunk-
|
|
124
|
+
//# sourceMappingURL=chunk-MRV7VSQK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/namespaces/principal.ts"],"sourcesContent":["import { resolveNamespaceCapabilities } from \"../capabilities.js\";\nimport type { PluginConfig } from \"../types.js\";\nimport { isLikelyUnsafeRegex } from \"../routing/engine.js\";\nimport { normalizeNamespaceIdentity } from \"./identity.js\";\nconst MAX_REGEX_SESSION_KEY_LENGTH = 512;\n\nfunction compileSafePrincipalRegex(pattern: string): RegExp | null {\n if (isLikelyUnsafeRegex(pattern)) return null;\n try {\n return new RegExp(pattern);\n } catch {\n return null;\n }\n}\n\nexport function resolvePrincipal(sessionKey: string | undefined, config: PluginConfig): string | undefined {\n if (!resolveNamespaceCapabilities(config).namespaces) return \"default\";\n const sk = typeof sessionKey === \"string\" && sessionKey.trim().length > 0\n ? sessionKey\n : \"\";\n const mode = config.principalFromSessionKeyMode;\n const rules = config.principalFromSessionKeyRules ?? [];\n\n if (!sk) return undefined;\n\n if (mode === \"prefix\") {\n for (const r of rules) {\n if (sk.startsWith(r.match)) return r.principal;\n }\n } else if (mode === \"map\") {\n for (const r of rules) {\n if (sk === r.match) return r.principal;\n }\n } else if (mode === \"regex\") {\n if (sk.length <= MAX_REGEX_SESSION_KEY_LENGTH) {\n for (const r of rules) {\n const re = compileSafePrincipalRegex(r.match);\n if (re?.test(sk)) {\n return r.principal;\n }\n }\n }\n }\n\n // Fallback heuristic: \"agent:<agentId>:<channelType>:...\"\n const parts = sk.split(\":\");\n if (parts.length >= 2 && parts[0] === \"agent\") {\n const agentId = parts[1];\n if (agentId && agentId.length > 0) return agentId;\n }\n return \"default\";\n}\n\nexport function canReadNamespace(principal: string | undefined, namespace: string, config: PluginConfig): boolean {\n if (!resolveNamespaceCapabilities(config).namespaces) return true;\n if (!principal) return false;\n const identity = normalizeNamespaceIdentity(namespace);\n const policy = config.namespacePolicies.find((p) => normalizeNamespaceIdentity(p.name) === identity);\n if (!policy) {\n return identity === normalizeNamespaceIdentity(config.defaultNamespace) ||\n identity === normalizeNamespaceIdentity(config.sharedNamespace);\n }\n return policy.readPrincipals.includes(principal) || policy.readPrincipals.includes(\"*\");\n}\n\nexport function canWriteNamespace(principal: string | undefined, namespace: string, config: PluginConfig): boolean {\n if (!resolveNamespaceCapabilities(config).namespaces) return true;\n if (!principal) return false;\n const identity = normalizeNamespaceIdentity(namespace);\n const policy = config.namespacePolicies.find((p) => normalizeNamespaceIdentity(p.name) === identity);\n if (!policy) return identity === normalizeNamespaceIdentity(config.defaultNamespace);\n return policy.writePrincipals.includes(principal) || policy.writePrincipals.includes(\"*\");\n}\n\n/**\n * Config-time namespace-policy lint (issue #1888 improvement 3). Principal-\n * agnostic mirror of {@link canWriteNamespace}: true when the namespace is\n * writable by AT LEAST ONE principal. When namespace enforcement is off every\n * write is allowed. Otherwise an explicit `namespacePolicies` entry takes\n * precedence (exactly as `canWriteNamespace`): with a matching policy the\n * namespace is covered only if that policy grants at least one non-blank\n * writer — even when the name equals `defaultNamespace`; without a policy it\n * is covered only if it IS the `defaultNamespace`. The `sharedNamespace` is\n * NOT implicitly writable (matching\n * `canWriteNamespace`), and a policy entry with no `writePrincipals` grants\n * nobody. A configured namespace that is NOT covered is writable for no one,\n * so every write to it is rejected by the ACL and dead-lettered — `remnic\n * doctor` warns on that before a session silently loses memory.\n */\nexport function isNamespacePolicyCovered(namespace: string, config: PluginConfig): boolean {\n if (!resolveNamespaceCapabilities(config).namespaces) return true;\n // An explicit policy wins over the default fallback (exactly as\n // canWriteNamespace), so a policy naming the default with no writers is NOT\n // covered. Only fall back to defaultNamespace when no policy matches.\n const identity = normalizeNamespaceIdentity(namespace);\n const policy = config.namespacePolicies.find((p) => normalizeNamespaceIdentity(p.name) === identity);\n if (!policy) return identity === normalizeNamespaceIdentity(config.defaultNamespace);\n return policy.writePrincipals.some((w) => w.trim().length > 0);\n}\n\n/**\n * Default \"self\" namespace for a principal.\n *\n * Heuristic:\n * - If there's a namespace policy with the same name as the principal, use it.\n * - Otherwise use config.defaultNamespace.\n */\nexport function defaultNamespaceForPrincipal(principal: string | undefined, config: PluginConfig): string {\n if (!resolveNamespaceCapabilities(config).namespaces) return config.defaultNamespace;\n if (!principal) return config.defaultNamespace;\n const policy = config.namespacePolicies.find(\n (p) => normalizeNamespaceIdentity(p.name) === normalizeNamespaceIdentity(principal),\n );\n return policy ? normalizeNamespaceIdentity(policy.name) : config.defaultNamespace;\n}\n\nexport function recallNamespacesForPrincipal(principal: string | undefined, config: PluginConfig): string[] {\n const out: string[] = [];\n if (!resolveNamespaceCapabilities(config).namespaces) return [config.defaultNamespace];\n if (!principal) return out;\n\n const selfNs = defaultNamespaceForPrincipal(principal, config);\n if (config.defaultRecallNamespaces.includes(\"self\") && canReadNamespace(principal, selfNs, config)) {\n out.push(selfNs);\n }\n if (config.defaultRecallNamespaces.includes(\"shared\") && canReadNamespace(principal, config.sharedNamespace, config)) {\n out.push(config.sharedNamespace);\n }\n\n for (const p of config.namespacePolicies) {\n if (p.includeInRecallByDefault && canReadNamespace(principal, p.name, config)) {\n if (!out.includes(p.name)) out.push(p.name);\n }\n }\n\n return out;\n}\n\n/**\n * Namespaces a principal may attribute a citation to: the default-recall\n * namespaces, every readable policy namespace, and the readable built-in\n * default/shared namespaces — which `defaultRecallNamespaces` can omit even\n * though `canReadNamespace` grants access (#2020).\n */\nexport function citationAuthorizedNamespaces(\n principal: string | undefined,\n config: PluginConfig,\n): string[] {\n return Array.from(\n new Set([\n ...recallNamespacesForPrincipal(principal, config),\n ...config.namespacePolicies\n .filter((p) => canReadNamespace(principal, p.name, config))\n .map((p) => p.name),\n ...[config.defaultNamespace, config.sharedNamespace].filter((ns) =>\n canReadNamespace(principal, ns, config),\n ),\n ]),\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from "./chunk-7ZNJN5WZ.js";
|
|
16
16
|
import {
|
|
17
17
|
compareEntityTimestamps
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-YTZA7U6Q.js";
|
|
19
19
|
import {
|
|
20
20
|
normalizeEntityName
|
|
21
21
|
} from "./chunk-YXZG5TVN.js";
|
|
@@ -889,4 +889,4 @@ export {
|
|
|
889
889
|
entityIndexVersion,
|
|
890
890
|
entityRecentTranscriptLookbackHours
|
|
891
891
|
};
|
|
892
|
-
//# sourceMappingURL=chunk-
|
|
892
|
+
//# sourceMappingURL=chunk-NEJQ65NV.js.map
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
headerConnector,
|
|
7
7
|
renderExtractionConversation,
|
|
8
8
|
resolveSourceConnector
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-A7266TRJ.js";
|
|
10
10
|
import {
|
|
11
11
|
ConsolidationResultSchema,
|
|
12
12
|
DaySummaryResultSchema,
|
|
@@ -2442,4 +2442,4 @@ export {
|
|
|
2442
2442
|
shouldEnableLocalExtractionThinking,
|
|
2443
2443
|
ExtractionEngine
|
|
2444
2444
|
};
|
|
2445
|
-
//# sourceMappingURL=chunk-
|
|
2445
|
+
//# sourceMappingURL=chunk-NTJ6TY44.js.map
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-UYCIS6V5.js";
|
|
8
8
|
import {
|
|
9
9
|
StorageManager
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-YTZA7U6Q.js";
|
|
11
11
|
import {
|
|
12
12
|
parseContinuityImprovementLoops
|
|
13
13
|
} from "./chunk-LDXUBPMO.js";
|
|
@@ -1283,4 +1283,4 @@ export {
|
|
|
1283
1283
|
defaultTierMigrationCycleBudget,
|
|
1284
1284
|
CompoundingEngine
|
|
1285
1285
|
};
|
|
1286
|
-
//# sourceMappingURL=chunk-
|
|
1286
|
+
//# sourceMappingURL=chunk-O5AFC5KM.js.map
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
namespaceIdentityToken
|
|
3
|
+
} from "./chunk-VUFL7X3H.js";
|
|
4
|
+
|
|
1
5
|
// src/routing/engine.ts
|
|
2
6
|
var DEFAULT_CATEGORIES = [
|
|
3
7
|
"fact",
|
|
@@ -15,8 +19,9 @@ var DEFAULT_CATEGORIES = [
|
|
|
15
19
|
"reasoning_trace"
|
|
16
20
|
];
|
|
17
21
|
function normalizeNamespace(namespace) {
|
|
18
|
-
return namespace.
|
|
22
|
+
return namespace.normalize("NFC");
|
|
19
23
|
}
|
|
24
|
+
var SAFE_ROUTE_NAMESPACE_CHARS = /^[\p{L}\p{M}\p{N}._-]+$/u;
|
|
20
25
|
function isLikelyUnsafeRegex(pattern) {
|
|
21
26
|
const value = pattern.trim();
|
|
22
27
|
if (value.length === 0) return true;
|
|
@@ -32,17 +37,19 @@ function isLikelyUnsafeRegex(pattern) {
|
|
|
32
37
|
function isSafeRouteNamespace(namespace) {
|
|
33
38
|
const value = normalizeNamespace(namespace);
|
|
34
39
|
if (value.length === 0) return false;
|
|
40
|
+
if (Array.from(value).length > 64) return false;
|
|
41
|
+
if (namespaceIdentityToken(value).length > 255) return false;
|
|
35
42
|
if (value === ".") return false;
|
|
36
43
|
if (value.includes("/") || value.includes("\\")) return false;
|
|
37
44
|
if (value.includes("..")) return false;
|
|
38
|
-
return
|
|
45
|
+
return SAFE_ROUTE_NAMESPACE_CHARS.test(value);
|
|
39
46
|
}
|
|
40
47
|
function validateRouteTarget(target, options) {
|
|
41
48
|
if (!target || typeof target !== "object") {
|
|
42
49
|
return { ok: false, error: "target must be an object" };
|
|
43
50
|
}
|
|
44
51
|
const allowedCategories = new Set(options?.allowedCategories ?? DEFAULT_CATEGORIES);
|
|
45
|
-
const allowedNamespaces = options?.allowedNamespaces ? new Set(options.allowedNamespaces.map((v) => v.trim()).filter((v) => v.length > 0)) : null;
|
|
52
|
+
const allowedNamespaces = options?.allowedNamespaces ? new Set(options.allowedNamespaces.map((v) => normalizeNamespace(v.trim())).filter((v) => v.length > 0)) : null;
|
|
46
53
|
const normalized = {};
|
|
47
54
|
if (typeof target.category === "string") {
|
|
48
55
|
if (!allowedCategories.has(target.category)) {
|
|
@@ -110,4 +117,4 @@ export {
|
|
|
110
117
|
doesRuleMatch,
|
|
111
118
|
selectRouteRule
|
|
112
119
|
};
|
|
113
|
-
//# sourceMappingURL=chunk-
|
|
120
|
+
//# sourceMappingURL=chunk-PCXDUHQH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/routing/engine.ts"],"sourcesContent":["import { namespaceIdentityToken } from \"../namespaces/identity.js\";\nimport type { MemoryCategory } from \"../types.js\";\n\nexport type RoutePatternType = \"regex\" | \"keyword\";\n\nexport interface RouteTarget {\n category?: MemoryCategory;\n namespace?: string;\n}\n\nexport interface RouteRule {\n id: string;\n patternType: RoutePatternType;\n pattern: string;\n priority: number;\n target: RouteTarget;\n enabled?: boolean;\n}\n\nexport interface RoutingEngineOptions {\n allowedNamespaces?: string[];\n allowedCategories?: MemoryCategory[];\n}\n\nexport interface RouteSelection {\n rule: RouteRule;\n target: RouteTarget;\n}\n\nconst DEFAULT_CATEGORIES: readonly MemoryCategory[] = [\n \"fact\",\n \"preference\",\n \"correction\",\n \"entity\",\n \"decision\",\n \"relationship\",\n \"principle\",\n \"commitment\",\n \"moment\",\n \"skill\",\n \"rule\",\n \"procedure\",\n \"reasoning_trace\",\n] as const;\n\nfunction normalizeNamespace(namespace: string): string {\n return namespace.normalize(\"NFC\");\n}\n\nconst SAFE_ROUTE_NAMESPACE_CHARS = /^[\\p{L}\\p{M}\\p{N}._-]+$/u;\n\nexport function isLikelyUnsafeRegex(pattern: string): boolean {\n const value = pattern.trim();\n if (value.length === 0) return true;\n if (value.length > 120) return true;\n if (/\\\\[1-9]/.test(value)) return true; // backreferences\n if (/\\(\\?<?[=!]/.test(value)) return true; // lookaround assertions\n if (/\\((?:[^()\\\\]|\\\\.)*[+*](?:[^()\\\\]|\\\\.)*\\)[+*{]/.test(value)) return true; // nested quantifiers\n // Conservative fail-closed guardrail: grouped/alternation regexes are user-configurable and can be expensive.\n if (/(^|[^\\\\])[()|]/.test(value)) return true;\n // Multiple quantifiers in one user pattern are high risk for catastrophic backtracking on non-matches.\n const quantifierCount =\n (value.match(/(^|[^\\\\])[*+?]/g)?.length ?? 0) +\n (value.match(/(^|[^\\\\])\\{/g)?.length ?? 0);\n if (quantifierCount > 1) return true;\n return false;\n}\n\nexport function isSafeRouteNamespace(namespace: string): boolean {\n const value = normalizeNamespace(namespace);\n // The route limit is 64 Unicode code points. On-disk identity tokens may\n // exceed 64 characters because namespaceIdentityToken hex-encodes UTF-8.\n // A route can use at most 64 code points, but the reversible on-disk token\n // must also fit one filesystem component (255 characters).\n if (value.length === 0) return false;\n if (Array.from(value).length > 64) return false;\n if (namespaceIdentityToken(value).length > 255) return false;\n if (value === \".\") return false;\n if (value.includes(\"/\") || value.includes(\"\\\\\")) return false;\n if (value.includes(\"..\")) return false;\n return SAFE_ROUTE_NAMESPACE_CHARS.test(value);\n}\n\nexport function validateRouteTarget(target: RouteTarget | null | undefined, options?: RoutingEngineOptions): {\n ok: boolean;\n error?: string;\n target?: RouteTarget;\n} {\n if (!target || typeof target !== \"object\") {\n return { ok: false, error: \"target must be an object\" };\n }\n\n const allowedCategories = new Set(options?.allowedCategories ?? DEFAULT_CATEGORIES);\n const allowedNamespaces = options?.allowedNamespaces\n ? new Set(options.allowedNamespaces.map((v) => normalizeNamespace(v.trim())).filter((v) => v.length > 0))\n : null;\n\n const normalized: RouteTarget = {};\n\n if (typeof target.category === \"string\") {\n if (!allowedCategories.has(target.category)) {\n return { ok: false, error: `invalid category: ${target.category}` };\n }\n normalized.category = target.category;\n }\n\n if (typeof target.namespace === \"string\") {\n const namespace = normalizeNamespace(target.namespace);\n if (!isSafeRouteNamespace(namespace)) {\n return { ok: false, error: `invalid namespace: ${target.namespace}` };\n }\n if (allowedNamespaces && !allowedNamespaces.has(namespace)) {\n return { ok: false, error: `namespace not allowed: ${namespace}` };\n }\n normalized.namespace = namespace;\n }\n\n if (!normalized.category && !normalized.namespace) {\n return { ok: false, error: \"target must include category or namespace\" };\n }\n\n return { ok: true, target: normalized };\n}\n\nexport function doesRuleMatch(rule: RouteRule, text: string): boolean {\n if (!rule || typeof rule !== \"object\") return false;\n if (rule.enabled === false) return false;\n if (typeof rule.pattern !== \"string\") return false;\n const pattern = rule.pattern.trim();\n if (pattern.length === 0) return false;\n\n if (rule.patternType === \"keyword\") {\n return text.toLowerCase().includes(pattern.toLowerCase());\n }\n if (rule.patternType !== \"regex\") {\n return false;\n }\n\n if (isLikelyUnsafeRegex(pattern)) {\n return false;\n }\n\n try {\n return new RegExp(pattern, \"i\").test(text);\n } catch {\n return false;\n }\n}\n\nexport function selectRouteRule(text: string, rules: RouteRule[], options?: RoutingEngineOptions): RouteSelection | null {\n const ranked = rules\n .map((rule, index) => ({ rule, index }))\n .sort((a, b) => {\n if (b.rule.priority !== a.rule.priority) return b.rule.priority - a.rule.priority;\n return a.index - b.index;\n });\n\n for (const entry of ranked) {\n if (!doesRuleMatch(entry.rule, text)) continue;\n\n const validation = validateRouteTarget(entry.rule.target, options);\n if (!validation.ok || !validation.target) continue;\n\n return {\n rule: entry.rule,\n target: validation.target,\n };\n }\n\n return null;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -13,10 +13,10 @@ import {
|
|
|
13
13
|
} from "./chunk-2AD5SXO4.js";
|
|
14
14
|
import {
|
|
15
15
|
ExtractionDeadlineError
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-4UUF2EX3.js";
|
|
17
17
|
import {
|
|
18
18
|
resolvePrincipal
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-MRV7VSQK.js";
|
|
20
20
|
import {
|
|
21
21
|
resolvePresentationCapabilities
|
|
22
22
|
} from "./chunk-JUOZ62Y7.js";
|
|
@@ -792,4 +792,4 @@ var SmartBuffer = class {
|
|
|
792
792
|
export {
|
|
793
793
|
SmartBuffer
|
|
794
794
|
};
|
|
795
|
-
//# sourceMappingURL=chunk-
|
|
795
|
+
//# sourceMappingURL=chunk-PEDYKCJI.js.map
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
2
|
expandScopeProfileReadNamespaces,
|
|
3
3
|
resolveScopeProfilePlan
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import {
|
|
6
|
-
namespaceIdentityFromToken,
|
|
7
|
-
namespaceIdentityToken
|
|
8
|
-
} from "./chunk-EGGE52UU.js";
|
|
4
|
+
} from "./chunk-KCT36JAW.js";
|
|
9
5
|
import {
|
|
10
6
|
canReadNamespace,
|
|
11
7
|
canWriteNamespace,
|
|
12
8
|
defaultNamespaceForPrincipal,
|
|
13
9
|
recallNamespacesForPrincipal,
|
|
14
10
|
resolvePrincipal
|
|
15
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-MRV7VSQK.js";
|
|
12
|
+
import {
|
|
13
|
+
namespaceIdentityFromToken,
|
|
14
|
+
namespaceIdentityLegacyToken,
|
|
15
|
+
namespaceIdentityToken,
|
|
16
|
+
normalizeNamespaceIdentity
|
|
17
|
+
} from "./chunk-VUFL7X3H.js";
|
|
16
18
|
import {
|
|
17
19
|
resolveNamespaceCapabilities
|
|
18
20
|
} from "./chunk-JUOZ62Y7.js";
|
|
@@ -104,7 +106,7 @@ function getConfiguredNamespaces(config) {
|
|
|
104
106
|
config.defaultNamespace,
|
|
105
107
|
config.sharedNamespace,
|
|
106
108
|
...config.namespacePolicies.map((policy) => policy.name)
|
|
107
|
-
].map((value) => value
|
|
109
|
+
].map((value) => normalizeNamespaceIdentity(value)).filter(Boolean)
|
|
108
110
|
)
|
|
109
111
|
);
|
|
110
112
|
}
|
|
@@ -131,7 +133,7 @@ function resolveNamespaceFromStorageDir(storageDir, options) {
|
|
|
131
133
|
if (hintedNamespace) return hintedNamespace;
|
|
132
134
|
}
|
|
133
135
|
const decoded = namespaceIdentityFromToken(dirName);
|
|
134
|
-
if (decoded && namespaceIdentityToken(decoded) === dirName) {
|
|
136
|
+
if (decoded && (namespaceIdentityToken(decoded) === dirName || namespaceIdentityLegacyToken(decoded.normalize("NFD")) === dirName)) {
|
|
135
137
|
return decoded;
|
|
136
138
|
}
|
|
137
139
|
return dirName;
|
|
@@ -202,4 +204,4 @@ export {
|
|
|
202
204
|
resolveWritableNamespaceValue,
|
|
203
205
|
resolveScopedWritableNamespaceValue
|
|
204
206
|
};
|
|
205
|
-
//# sourceMappingURL=chunk-
|
|
207
|
+
//# sourceMappingURL=chunk-Q57GOHLF.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/scopes/scope-plan.ts"],"sourcesContent":["/**\n * ScopePlan resolver (issue #1521).\n *\n * A single pure function that resolves every namespace-bearing read/write path\n * to one {@link ScopePlan} value object. Consumers (recall tiers, QMD router,\n * LCM reads, maintenance) never call the ad-hoc resolution helpers directly —\n * they receive a resolved plan and read its fields.\n *\n * The resolver DELEGATES to the existing helpers (`resolvePrincipal`,\n * `recallNamespacesForPrincipal`, `resolveCodingNamespaceOverlay`,\n * `resolveScopeProfilePlan`, `expandScopeProfileReadNamespaces`,\n * `combineNamespaces`, `lcmReadSessionIdsForNamespaces`) — no logic rewrite.\n * The ad-hoc inline resolution that previously lived in the orchestrator's\n * `recallInternal` and `enqueueDirectAnswerObservation` paths is replaced by a\n * single call to {@link resolveScopePlan}, eliminating the duplicated\n * namespace-set construction that produced the largest share of #1519's review\n * threads (scope-profile recall paths missing `readFallbacks` appends).\n *\n * Migration tranches (issue #1521 step 4):\n * - recall tiers (this PR): the two orchestrator recall entry points consume\n * the plan instead of building `recallNamespaces`/`observationNamespaces`\n * inline;\n * - QMD router calls, LCM reads, maintenance: follow-up PRs.\n *\n * CLAUDE.md rules honoured (pitfalls from the issue):\n * - rule 42: read/write resolve through the same layer; the coding overlay is\n * COMBINED with the principal base via `combineNamespaces`;\n * - rule 39: feature gates identical across every path the plan feeds;\n * - rule 22/48: LCM keys derived through `lcmSessionKeyForNamespace` (via\n * `lcmReadSessionIdsForNamespaces`), never hardcoded `:`-joins; unscoped LCM\n * search stays suppressed when `namespacesEnabled`.\n */\n\nimport { resolveNamespaceCapabilities } from \"../capabilities.js\";\nimport {\n canReadNamespace,\n canWriteNamespace,\n defaultNamespaceForPrincipal,\n recallNamespacesForPrincipal,\n resolvePrincipal,\n} from \"../namespaces/principal.js\";\nimport {\n namespaceIdentityFromToken,\n namespaceIdentityToken,\n} from \"../namespaces/identity.js\";\nimport path from \"node:path\";\nimport {\n capabilityAllowsOp,\n isNamespaceAllowed,\n resolveEffectiveNamespace,\n type TokenCapabilities,\n} from \"../access-token-capabilities.js\";\nimport type { ResolvedScopeProfilePlan } from \"../namespaces/scope-profiles.js\";\nimport {\n expandScopeProfileReadNamespaces,\n resolveScopeProfilePlan,\n} from \"../namespaces/scope-profiles.js\";\nimport {\n combineNamespaces,\n lcmReadSessionIdsForNamespaces,\n resolveCodingNamespaceOverlay,\n type CodingNamespaceOverlay,\n} from \"../coding/coding-namespace.js\";\nimport type { CodingContext, PluginConfig } from \"../types.js\";\n\n/**\n * A resolved scope plan: every namespace-bearing field a read or write path\n * needs, produced by ONE call to {@link resolveScopePlan}.\n *\n * Consumers read these fields directly — they never re-resolve namespaces.\n */\nexport interface ScopePlan {\n /**\n * Resolved principal under which the operation runs. `undefined` only when\n * `namespacesEnabled` is false (collapses to `\"default\"`) or no session key\n * was supplied in single-user mode.\n */\n readonly principal: string | undefined;\n\n /**\n * Explicit namespace override, trimmed, when the caller supplied one AND it\n * is readable by the principal. `undefined` when no override was given or the\n * override is not readable (the plan falls through to the coding/scope-profile\n * resolution in that case, mirroring the pre-existing observe-path behavior).\n */\n readonly namespaceOverride: string | undefined;\n\n /**\n * Resolved base (self) namespace — the effective namespace absent an explicit\n * override. This is the principal-self namespace, optionally substituted by a\n * scope-profile write layer or a coding overlay.\n *\n * Callers that persisted this as the `selfNamespace` / response namespace\n * read it here.\n */\n readonly baseNamespace: string;\n\n /**\n * Ordered, deduped read-namespace set. Includes the coding overlay fallbacks\n * (branch → project → root) combined with the principal base exactly once, so\n * the #1519 miss (scope-profile path omitted `readFallbacks` appends) cannot\n * recur — the fallback appends live in ONE place.\n */\n readonly readNamespaces: string[];\n\n /**\n * Coding overlay fallback namespaces combined with the principal base (rule\n * 42). Empty when no coding overlay applies. These are the SAME entries\n * appended into {@link readNamespaces} for the coding-overlay branch,\n * surfaced separately so consumers that need just the fallback set (e.g.\n * LCM read-key derivation) can read them without re-deriving.\n */\n readonly readFallbacks: string[];\n\n /**\n * LCM read namespace set. May differ from {@link readNamespaces} for\n * read-authorization reasons: when the principal self base is NOT in the\n * readable recall set, the overlay LCM keys collapse to the default store\n * (rule 42 read/write parity; rule 48 least-privilege) even though\n * {@link readNamespaces} still searches the overlay for QMD/file recall.\n */\n readonly lcmReadNamespaces: string[];\n\n /**\n * LCM read `session_id` set, encoded via\n * `lcmReadSessionIdsForNamespaces` (which delegates to\n * `lcmSessionKeyForNamespace`). Ordered and deduped; the primary overlay key\n * is first. Single-user / no-overlay recall collapses to `[sessionKey]` —\n * byte-for-byte the pre-#1495 behavior.\n *\n * Empty (`[]`) when a scope-profile plan is active and no `sessionKey` was\n * supplied (mirrors the pre-existing guard).\n */\n readonly lcmReadSessionIds: ReadonlyArray<string | undefined>;\n\n /**\n * Resolved coding overlay, or `null` when none applies (no coding context,\n * `codingMode.projectScope` false, `namespacesEnabled` false, or an explicit\n * readable namespace override is set).\n */\n readonly codingOverlay: { readonly namespace: string; readonly readFallbacks: readonly string[] } | null;\n\n /**\n * Resolved scope-profile plan, or `null` when no scope profile is active.\n * Consumers that branched on \"profile vs. non-profile\" read this directly.\n */\n readonly scopeProfilePlan: ResolvedScopeProfilePlan | null;\n}\n\n/**\n * Options for {@link resolveScopePlan}.\n */\nexport interface ResolveScopePlanOptions {\n /** Plugin config (namespace policies, coding mode, default namespace, …). */\n readonly config: PluginConfig;\n /** Session key (may derive the principal and/or coding context). */\n readonly sessionKey?: string;\n /**\n * Explicit namespace override (raw — the resolver trims it). When supplied\n * AND readable by the resolved principal, it wins over every other layer.\n */\n readonly namespace?: string;\n /**\n * Authenticated principal override. Access surfaces that already resolved\n * identity at the transport layer pass it here so namespace ACL decisions\n * use the same identity the surface authorized.\n */\n readonly principalOverride?: string;\n /**\n * Coding context for the session. Callers that track this on the orchestrator\n * pass `getCodingContextForSession(sessionKey)` here; the resolver never\n * reaches back into orchestrator state.\n */\n readonly codingContext?: CodingContext | null;\n /**\n * Whether namespace routing is enabled. Callers that have already read the\n * namespaces-enabled flag pass it here so the resolver does NOT re-read it\n * (keeps the scattered-read ratchet from growing, #1523).\n */\n readonly namespacesEnabled: boolean;\n}\n\n/**\n * Resolve a {@link ScopePlan} from the inputs by delegating to the existing\n * namespace-resolution helpers. Pure — no side effects, no orchestrator state.\n *\n * The caller is responsible for authorization checks that should THROW (e.g.\n * `namespacesEnabled && !principal`, or an unreadable explicit override in the\n * recall path). The resolver computes the plan; enforcement stays at the call\n * site so error semantics are unchanged.\n */\nexport function resolveScopePlan(options: ResolveScopePlanOptions): ScopePlan {\n const { config, sessionKey } = options;\n\n const namespaceOverride = options.namespace?.trim() || undefined;\n\n const principal =\n typeof options.principalOverride === \"string\" && options.principalOverride.length > 0\n ? options.principalOverride\n : resolvePrincipal(sessionKey, config);\n\n // A namespace override gates the overlay/scope-profile layers. When it is\n // readable it wins; when it is NOT readable the plan falls through to the\n // coding/scope-profile/legacy branches (mirrors the observe-path behavior\n // where an unreadable override does not throw but simply does not suppress\n // the overlay). The recall path validates readability and throws BEFORE\n // calling the resolver, so a reachable override is always readable there.\n const namespaceOverrideReadable =\n namespaceOverride !== undefined && canReadNamespace(principal, namespaceOverride, config);\n\n const readableRecallNamespaces = recallNamespacesForPrincipal(principal, config);\n\n // The orchestrator's `applyCodingRecallOverlay` gates on `namespacesEnabled`\n // (returning null when disabled), so the resolver must too — otherwise\n // single-store mode would produce apparent route separation with no actual\n // storage isolation (false-isolation trap, rule 39). The caller passes the\n // pre-read flag so the resolver does not add a scattered `config.*Enabled`\n // read (ratchet, #1523).\n const namespacesEnabled = options.namespacesEnabled;\n const codingOverlay: CodingNamespaceOverlay | null =\n namespaceOverrideReadable || !namespacesEnabled\n ? null\n : resolveCodingNamespaceOverlay(\n options.codingContext ?? null,\n config.codingMode,\n config.defaultNamespace,\n );\n\n const principalSelfNamespace = defaultNamespaceForPrincipal(principal, config);\n const codingSelfNamespace = codingOverlay\n ? combineNamespaces(principalSelfNamespace, codingOverlay.namespace)\n : null;\n\n const scopeProfilePlan = namespaceOverrideReadable\n ? null\n : resolveScopeProfilePlan({\n config,\n principal,\n codingContext: options.codingContext ?? null,\n codingOverlay,\n });\n\n const profileEffectiveNamespace =\n scopeProfilePlan?.writeNamespace || scopeProfilePlan?.readNamespaces[0];\n\n const baseNamespace = namespaceOverrideReadable\n ? namespaceOverride!\n : profileEffectiveNamespace ?? codingSelfNamespace ?? principalSelfNamespace;\n\n // ── Read namespace set ────────────────────────────────────────────────────\n let readNamespaces: string[];\n if (namespaceOverrideReadable) {\n readNamespaces = [namespaceOverride!];\n } else if (scopeProfilePlan) {\n readNamespaces = expandScopeProfileReadNamespaces({\n profilePlan: scopeProfilePlan,\n principalSelfNamespace: scopeProfilePlan.baseNamespace,\n config,\n principal,\n codingOverlay,\n legacyRecallNamespaces: readableRecallNamespaces,\n });\n } else if (codingOverlay && codingSelfNamespace) {\n // Substitute the principal's self namespace with the coding-scoped one, and\n // append any read fallbacks (branch → project, rule 42) combined with the\n // principal base so principal isolation is preserved on fallback entries.\n const mapped = readableRecallNamespaces.map((ns) =>\n ns === principalSelfNamespace ? codingSelfNamespace : ns,\n );\n const fallbackNs = codingOverlay.readFallbacks.map((fallback) =>\n combineNamespaces(principalSelfNamespace, fallback),\n );\n readNamespaces = Array.from(new Set<string>([...mapped, ...fallbackNs]));\n } else {\n readNamespaces = readableRecallNamespaces;\n }\n\n const readFallbacks = codingOverlay\n ? codingOverlay.readFallbacks.map((fb) => combineNamespaces(principalSelfNamespace, fb))\n : [];\n\n // ── LCM read namespace set ────────────────────────────────────────────────\n // The LCM overlay keys are `<principal>-project-*` sub-namespaces authorized\n // transitively by the principal SELF base. Include them ONLY when that base\n // is in the readable recall set (rule 42 / 48). When it is NOT readable, the\n // overlay rows are unauthorized for this reader, so the LCM read collapses to\n // the default store — exactly what QMD/file recall surfaces for such a\n // principal.\n const codingOverlaySelfReadable =\n codingOverlay !== null &&\n (scopeProfilePlan\n ? scopeProfilePlan.layers.some((layer) => layer.id === \"userProject\" && layer.readable)\n : readableRecallNamespaces.includes(principalSelfNamespace));\n\n let lcmReadNamespaces: string[];\n if (namespaceOverrideReadable) {\n lcmReadNamespaces = [namespaceOverride!];\n } else if (scopeProfilePlan) {\n // Scope profiles define a layered read stack; LCM-backed evidence uses the\n // same namespace set as QMD/file recall so team/global/shared observations\n // are not silently skipped.\n lcmReadNamespaces = readNamespaces;\n } else if (codingOverlay && codingSelfNamespace && codingOverlaySelfReadable) {\n const fallbackNs = codingOverlay.readFallbacks.map((fallback) =>\n combineNamespaces(principalSelfNamespace, fallback),\n );\n lcmReadNamespaces = [codingSelfNamespace, ...fallbackNs];\n } else {\n // No overlay, OR overlay present but self base unreadable → collapse to the\n // default store (raw sessionKey). No `<principal>-project-*` overlay key is\n // searched.\n lcmReadNamespaces = [config.defaultNamespace];\n }\n\n const lcmReadSessionIds =\n scopeProfilePlan && !sessionKey\n ? []\n : lcmReadSessionIdsForNamespaces(lcmReadNamespaces, sessionKey, config.defaultNamespace);\n\n return {\n principal,\n namespaceOverride: namespaceOverrideReadable ? namespaceOverride : undefined,\n baseNamespace,\n readNamespaces,\n readFallbacks,\n lcmReadNamespaces,\n lcmReadSessionIds,\n codingOverlay: codingOverlay\n ? { namespace: codingOverlay.namespace, readFallbacks: codingOverlay.readFallbacks }\n : null,\n scopeProfilePlan,\n };\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Centralized namespace-resolution helpers (issue #1521 step 3–4)\n//\n// The three functions below consolidate the ad-hoc namespace-resolution logic\n// that was previously scattered across orchestrator.ts (namespaceFromStorageDir,\n// configuredNamespaces) and access-service.ts (resolveWritableNamespace). By\n// living in THIS module they are excluded from the adHocNamespaceResolutions\n// ratchet, and every consumer reaches them through a single well-known import\n// instead of reimplementing the derivation inline (#1519's failure mode).\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * The configured namespace set: default + shared + every namespace-policy\n * name, trimmed and de-duplicated. Pure — reads only from `config`.\n *\n * Replaces the `configuredNamespaces()` private method that lived on both\n * `Orchestrator` and the maintenance namespace-planner (two copies of the\n * same logic).\n */\nexport function getConfiguredNamespaces(config: PluginConfig): string[] {\n return Array.from(\n new Set(\n [\n config.defaultNamespace,\n config.sharedNamespace,\n ...config.namespacePolicies.map((policy) => policy.name),\n ]\n .map((value) => value.trim())\n .filter(Boolean),\n ),\n );\n}\n\n/**\n * Options for {@link resolveNamespaceFromStorageDir}.\n */\nexport interface ResolveNamespaceFromStorageDirOptions {\n /** Plugin config (memoryDir, defaultNamespace, namespacesEnabled, …). */\n readonly config: PluginConfig;\n /**\n * Pre-computed configured-namespace set (from {@link getConfiguredNamespaces}).\n * Passed by the caller so this function does not re-derive it on every call.\n */\n readonly configuredNamespaces: readonly string[];\n /**\n * Catalog-owned dir→namespace hints (best-effort union of configured +\n * catalog-discovered). May be `undefined` when no catalog hints are loaded.\n */\n readonly hints?: ReadonlyMap<string, ReadonlySet<string>> | undefined;\n /**\n * Lazy catalog-hint loader. Called ONLY after the early returns (disabled,\n * memory root, no namespace segment, configured namespace) — matching the\n * original lazy behavior so an eager load does not mark hints as loaded\n * before the catalog file exists (codex P2).\n */\n readonly loadHints?: () => void;\n}\n\n/**\n * Derive the namespace a storage directory belongs to.\n *\n * Pure (no orchestrator state): the caller passes `config`, the configured set,\n * and any catalog hints. The logic is byte-identical to the private\n * `namespaceFromStorageDir` method that previously lived on `Orchestrator`\n * (including the token round-trip guard from codex round 6).\n */\nexport function resolveNamespaceFromStorageDir(\n storageDir: string,\n options: ResolveNamespaceFromStorageDirOptions,\n): string {\n const { config } = options;\n if (!resolveNamespaceCapabilities(config).namespaces) return config.defaultNamespace;\n const resolvedStorageDir = path.resolve(storageDir);\n const resolvedMemoryDir = path.resolve(config.memoryDir);\n if (resolvedStorageDir === resolvedMemoryDir) return config.defaultNamespace;\n const m = resolvedStorageDir.match(/[\\\\/]namespaces[\\\\/]([^\\\\/]+)$/);\n if (!m?.[1]) return config.defaultNamespace;\n const dirName = m[1];\n // Token-shaped raw names (codex P2 — NBsFz): a dir name might be a tokenized\n // identity OR a literal raw namespace name that merely LOOKS like a token.\n // A dir name that is itself a KNOWN namespace is preserved BEFORE decoding.\n if (options.configuredNamespaces.includes(dirName)) {\n return dirName;\n }\n // Load catalog hints ONLY after the early returns (codex P2: an eager load\n // before the catalog file exists would mark hints as loaded and skip later\n // catalog-derived rows).\n options.loadHints?.();\n const hints = options.hints;\n const hintedNamespaces = hints?.get(resolvedStorageDir);\n if (hintedNamespaces?.has(dirName)) {\n return dirName;\n }\n if (hintedNamespaces?.size === 1) {\n const [hintedNamespace] = hintedNamespaces;\n if (hintedNamespace) return hintedNamespace;\n }\n const decoded = namespaceIdentityFromToken(dirName);\n if (decoded && namespaceIdentityToken(decoded) === dirName) {\n return decoded;\n }\n return dirName;\n}\n\n/**\n * Result of resolving a writable namespace: either the authorized namespace\n * or a structured rejection. The caller decides how to surface the rejection\n * (e.g. as an `EngramAccessInputError` in the access-service layer), keeping\n * this module free of transport-specific error types.\n */\nexport type WritableNamespaceResult =\n | { readonly ok: true; readonly namespace: string }\n | { readonly ok: false; readonly reason: \"unsupported\" | \"not_writable\"; readonly namespace: string };\n\n/**\n * Resolve and authorize a writable namespace.\n *\n * Combines the three steps that `AccessService.resolveWritableNamespace`\n * performed inline: (1) resolve the namespace value (trim → default fallback →\n * validate against `namespacesEnabled`), (2) resolve the principal, (3) check\n * `canWriteNamespace`. Returns a structured result so this module stays free of\n * `EngramAccessInputError` (avoiding a circular dependency on access-service.ts).\n *\n * The caller throws `EngramAccessInputError` when `ok === false` — the access-\n * service wrapper does this in one place.\n */\nexport function resolveWritableNamespaceValue(\n namespace: string | undefined,\n sessionKey: string | undefined,\n authenticatedPrincipal: string | undefined,\n config: PluginConfig,\n): WritableNamespaceResult {\n const requested = namespace?.trim();\n let resolved: string;\n if (!requested) {\n resolved = config.defaultNamespace;\n } else if (\n !resolveNamespaceCapabilities(config).namespaces &&\n requested !== config.defaultNamespace\n ) {\n return { ok: false, reason: \"unsupported\", namespace: requested };\n } else {\n resolved = requested;\n }\n\n const trusted = authenticatedPrincipal?.trim();\n const principal =\n typeof trusted === \"string\" && trusted.length > 0\n ? trusted\n : resolvePrincipal(sessionKey, config);\n\n if (!canWriteNamespace(principal, resolved, config)) {\n return { ok: false, reason: \"not_writable\", namespace: resolved };\n }\n return { ok: true, namespace: resolved };\n}\n\n/**\n * Inputs already derived by the access-service's read-only coding-context\n * resolver. Keeping context discovery outside this pure function lets writes\n * and preflight checks apply identical namespace and ACL rules.\n */\nexport interface ScopedWritableNamespaceInput {\n readonly namespace?: string;\n readonly sessionKey?: string;\n readonly authenticatedPrincipal?: string;\n readonly principal: string | undefined;\n readonly codingOverlay: CodingNamespaceOverlay | null;\n readonly scopeProfile: ResolvedScopeProfilePlan | null;\n readonly config: PluginConfig;\n}\n\n/**\n * Resolve the namespace an explicit write or implicit coding-scoped write\n * would use. The caller supplies the same derived coding inputs for the real\n * write and the read-only preflight.\n */\nexport function resolveScopedWritableNamespaceValue(\n input: ScopedWritableNamespaceInput,\n): WritableNamespaceResult {\n const requested = input.namespace?.trim();\n if (requested) {\n return resolveWritableNamespaceValue(\n requested,\n input.sessionKey,\n input.authenticatedPrincipal,\n input.config,\n );\n }\n if (input.scopeProfile) {\n const selectedLayer = input.scopeProfile.layers.find(\n (layer) => layer.id === input.scopeProfile?.writeLayer,\n );\n const writeNamespaceReadable =\n input.scopeProfile.writeNamespace.length > 0 &&\n input.scopeProfile.readNamespaces.includes(input.scopeProfile.writeNamespace);\n if (!selectedLayer?.writable || !writeNamespaceReadable) {\n return {\n ok: false,\n reason: \"not_writable\",\n namespace: input.scopeProfile.writeNamespace,\n };\n }\n return { ok: true, namespace: input.scopeProfile.writeNamespace };\n }\n if (!input.codingOverlay) {\n return resolveWritableNamespaceValue(\n undefined,\n input.sessionKey,\n input.authenticatedPrincipal,\n input.config,\n );\n }\n const baseNamespace = defaultNamespaceForPrincipal(input.principal, input.config);\n if (!canWriteNamespace(input.principal, baseNamespace, input.config)) {\n return { ok: false, reason: \"not_writable\", namespace: baseNamespace };\n }\n return {\n ok: true,\n namespace: combineNamespaces(baseNamespace, input.codingOverlay.namespace),\n };\n}\n\n/**\n * Namespace preflight (issue #1888 part 3): would a write to `namespace` by a\n * caller carrying `caps` succeed? Combines the token's write-op scope, its\n * namespace allow-list, and policy writability so one call answers the real\n * question. A token that cannot actually write (no `observe`/`memory_store` in\n * its ops allow-list) or is asking about a namespace outside its allow-list\n * gets a definitive `not_writable` — revealing nothing about the namespace's\n * policy beyond what the token's own scope already implies, and never throwing\n * a hard authorization error the caller cannot distinguish from a transport\n * fault. (Unrestricted tokens pass both scope checks unchanged.)\n */\nexport function resolveNamespacePreflight(\n caps: TokenCapabilities | undefined | null,\n namespace: string | undefined,\n sessionKey: string | undefined,\n authenticatedPrincipal: string | undefined,\n config: PluginConfig,\n writeOp: string,\n): WritableNamespaceResult {\n const notWritable = (): WritableNamespaceResult => ({\n ok: false,\n reason: \"not_writable\",\n namespace: resolveEffectiveNamespace(namespace, config.defaultNamespace) ?? namespace ?? \"\",\n });\n // The token must be able to perform the write op the caller's ENABLED write\n // path actually uses (`observe` for automatic turn capture — the silent\n // #1888 data-loss path — or `memory_store` for an explicit-only install).\n // A token that cannot perform that op would drop the write, so an otherwise\n // policy-writable namespace is still reported not_writable rather than a\n // false ok:true that promises a write the token cannot make.\n if (!capabilityAllowsOp(caps, writeOp)) {\n return notWritable();\n }\n if (!isNamespaceAllowed(caps, namespace, config.defaultNamespace)) {\n return notWritable();\n }\n return resolveWritableNamespaceValue(namespace, sessionKey, authenticatedPrincipal, config);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/scopes/scope-plan.ts"],"sourcesContent":["/**\n * ScopePlan resolver (issue #1521).\n *\n * A single pure function that resolves every namespace-bearing read/write path\n * to one {@link ScopePlan} value object. Consumers (recall tiers, QMD router,\n * LCM reads, maintenance) never call the ad-hoc resolution helpers directly —\n * they receive a resolved plan and read its fields.\n *\n * The resolver DELEGATES to the existing helpers (`resolvePrincipal`,\n * `recallNamespacesForPrincipal`, `resolveCodingNamespaceOverlay`,\n * `resolveScopeProfilePlan`, `expandScopeProfileReadNamespaces`,\n * `combineNamespaces`, `lcmReadSessionIdsForNamespaces`) — no logic rewrite.\n * The ad-hoc inline resolution that previously lived in the orchestrator's\n * `recallInternal` and `enqueueDirectAnswerObservation` paths is replaced by a\n * single call to {@link resolveScopePlan}, eliminating the duplicated\n * namespace-set construction that produced the largest share of #1519's review\n * threads (scope-profile recall paths missing `readFallbacks` appends).\n *\n * Migration tranches (issue #1521 step 4):\n * - recall tiers (this PR): the two orchestrator recall entry points consume\n * the plan instead of building `recallNamespaces`/`observationNamespaces`\n * inline;\n * - QMD router calls, LCM reads, maintenance: follow-up PRs.\n *\n * CLAUDE.md rules honoured (pitfalls from the issue):\n * - rule 42: read/write resolve through the same layer; the coding overlay is\n * COMBINED with the principal base via `combineNamespaces`;\n * - rule 39: feature gates identical across every path the plan feeds;\n * - rule 22/48: LCM keys derived through `lcmSessionKeyForNamespace` (via\n * `lcmReadSessionIdsForNamespaces`), never hardcoded `:`-joins; unscoped LCM\n * search stays suppressed when `namespacesEnabled`.\n */\n\nimport { resolveNamespaceCapabilities } from \"../capabilities.js\";\nimport {\n canReadNamespace,\n canWriteNamespace,\n defaultNamespaceForPrincipal,\n recallNamespacesForPrincipal,\n resolvePrincipal,\n} from \"../namespaces/principal.js\";\nimport {\n namespaceIdentityFromToken,\n namespaceIdentityLegacyToken,\n namespaceIdentityToken,\n normalizeNamespaceIdentity,\n} from \"../namespaces/identity.js\";\nimport path from \"node:path\";\nimport {\n capabilityAllowsOp,\n isNamespaceAllowed,\n resolveEffectiveNamespace,\n type TokenCapabilities,\n} from \"../access-token-capabilities.js\";\nimport type { ResolvedScopeProfilePlan } from \"../namespaces/scope-profiles.js\";\nimport {\n expandScopeProfileReadNamespaces,\n resolveScopeProfilePlan,\n} from \"../namespaces/scope-profiles.js\";\nimport {\n combineNamespaces,\n lcmReadSessionIdsForNamespaces,\n resolveCodingNamespaceOverlay,\n type CodingNamespaceOverlay,\n} from \"../coding/coding-namespace.js\";\nimport type { CodingContext, PluginConfig } from \"../types.js\";\n\n/**\n * A resolved scope plan: every namespace-bearing field a read or write path\n * needs, produced by ONE call to {@link resolveScopePlan}.\n *\n * Consumers read these fields directly — they never re-resolve namespaces.\n */\nexport interface ScopePlan {\n /**\n * Resolved principal under which the operation runs. `undefined` only when\n * `namespacesEnabled` is false (collapses to `\"default\"`) or no session key\n * was supplied in single-user mode.\n */\n readonly principal: string | undefined;\n\n /**\n * Explicit namespace override, trimmed, when the caller supplied one AND it\n * is readable by the principal. `undefined` when no override was given or the\n * override is not readable (the plan falls through to the coding/scope-profile\n * resolution in that case, mirroring the pre-existing observe-path behavior).\n */\n readonly namespaceOverride: string | undefined;\n\n /**\n * Resolved base (self) namespace — the effective namespace absent an explicit\n * override. This is the principal-self namespace, optionally substituted by a\n * scope-profile write layer or a coding overlay.\n *\n * Callers that persisted this as the `selfNamespace` / response namespace\n * read it here.\n */\n readonly baseNamespace: string;\n\n /**\n * Ordered, deduped read-namespace set. Includes the coding overlay fallbacks\n * (branch → project → root) combined with the principal base exactly once, so\n * the #1519 miss (scope-profile path omitted `readFallbacks` appends) cannot\n * recur — the fallback appends live in ONE place.\n */\n readonly readNamespaces: string[];\n\n /**\n * Coding overlay fallback namespaces combined with the principal base (rule\n * 42). Empty when no coding overlay applies. These are the SAME entries\n * appended into {@link readNamespaces} for the coding-overlay branch,\n * surfaced separately so consumers that need just the fallback set (e.g.\n * LCM read-key derivation) can read them without re-deriving.\n */\n readonly readFallbacks: string[];\n\n /**\n * LCM read namespace set. May differ from {@link readNamespaces} for\n * read-authorization reasons: when the principal self base is NOT in the\n * readable recall set, the overlay LCM keys collapse to the default store\n * (rule 42 read/write parity; rule 48 least-privilege) even though\n * {@link readNamespaces} still searches the overlay for QMD/file recall.\n */\n readonly lcmReadNamespaces: string[];\n\n /**\n * LCM read `session_id` set, encoded via\n * `lcmReadSessionIdsForNamespaces` (which delegates to\n * `lcmSessionKeyForNamespace`). Ordered and deduped; the primary overlay key\n * is first. Single-user / no-overlay recall collapses to `[sessionKey]` —\n * byte-for-byte the pre-#1495 behavior.\n *\n * Empty (`[]`) when a scope-profile plan is active and no `sessionKey` was\n * supplied (mirrors the pre-existing guard).\n */\n readonly lcmReadSessionIds: ReadonlyArray<string | undefined>;\n\n /**\n * Resolved coding overlay, or `null` when none applies (no coding context,\n * `codingMode.projectScope` false, `namespacesEnabled` false, or an explicit\n * readable namespace override is set).\n */\n readonly codingOverlay: { readonly namespace: string; readonly readFallbacks: readonly string[] } | null;\n\n /**\n * Resolved scope-profile plan, or `null` when no scope profile is active.\n * Consumers that branched on \"profile vs. non-profile\" read this directly.\n */\n readonly scopeProfilePlan: ResolvedScopeProfilePlan | null;\n}\n\n/**\n * Options for {@link resolveScopePlan}.\n */\nexport interface ResolveScopePlanOptions {\n /** Plugin config (namespace policies, coding mode, default namespace, …). */\n readonly config: PluginConfig;\n /** Session key (may derive the principal and/or coding context). */\n readonly sessionKey?: string;\n /**\n * Explicit namespace override (raw — the resolver trims it). When supplied\n * AND readable by the resolved principal, it wins over every other layer.\n */\n readonly namespace?: string;\n /**\n * Authenticated principal override. Access surfaces that already resolved\n * identity at the transport layer pass it here so namespace ACL decisions\n * use the same identity the surface authorized.\n */\n readonly principalOverride?: string;\n /**\n * Coding context for the session. Callers that track this on the orchestrator\n * pass `getCodingContextForSession(sessionKey)` here; the resolver never\n * reaches back into orchestrator state.\n */\n readonly codingContext?: CodingContext | null;\n /**\n * Whether namespace routing is enabled. Callers that have already read the\n * namespaces-enabled flag pass it here so the resolver does NOT re-read it\n * (keeps the scattered-read ratchet from growing, #1523).\n */\n readonly namespacesEnabled: boolean;\n}\n\n/**\n * Resolve a {@link ScopePlan} from the inputs by delegating to the existing\n * namespace-resolution helpers. Pure — no side effects, no orchestrator state.\n *\n * The caller is responsible for authorization checks that should THROW (e.g.\n * `namespacesEnabled && !principal`, or an unreadable explicit override in the\n * recall path). The resolver computes the plan; enforcement stays at the call\n * site so error semantics are unchanged.\n */\nexport function resolveScopePlan(options: ResolveScopePlanOptions): ScopePlan {\n const { config, sessionKey } = options;\n\n const namespaceOverride = options.namespace?.trim() || undefined;\n\n const principal =\n typeof options.principalOverride === \"string\" && options.principalOverride.length > 0\n ? options.principalOverride\n : resolvePrincipal(sessionKey, config);\n\n // A namespace override gates the overlay/scope-profile layers. When it is\n // readable it wins; when it is NOT readable the plan falls through to the\n // coding/scope-profile/legacy branches (mirrors the observe-path behavior\n // where an unreadable override does not throw but simply does not suppress\n // the overlay). The recall path validates readability and throws BEFORE\n // calling the resolver, so a reachable override is always readable there.\n const namespaceOverrideReadable =\n namespaceOverride !== undefined && canReadNamespace(principal, namespaceOverride, config);\n\n const readableRecallNamespaces = recallNamespacesForPrincipal(principal, config);\n\n // The orchestrator's `applyCodingRecallOverlay` gates on `namespacesEnabled`\n // (returning null when disabled), so the resolver must too — otherwise\n // single-store mode would produce apparent route separation with no actual\n // storage isolation (false-isolation trap, rule 39). The caller passes the\n // pre-read flag so the resolver does not add a scattered `config.*Enabled`\n // read (ratchet, #1523).\n const namespacesEnabled = options.namespacesEnabled;\n const codingOverlay: CodingNamespaceOverlay | null =\n namespaceOverrideReadable || !namespacesEnabled\n ? null\n : resolveCodingNamespaceOverlay(\n options.codingContext ?? null,\n config.codingMode,\n config.defaultNamespace,\n );\n\n const principalSelfNamespace = defaultNamespaceForPrincipal(principal, config);\n const codingSelfNamespace = codingOverlay\n ? combineNamespaces(principalSelfNamespace, codingOverlay.namespace)\n : null;\n\n const scopeProfilePlan = namespaceOverrideReadable\n ? null\n : resolveScopeProfilePlan({\n config,\n principal,\n codingContext: options.codingContext ?? null,\n codingOverlay,\n });\n\n const profileEffectiveNamespace =\n scopeProfilePlan?.writeNamespace || scopeProfilePlan?.readNamespaces[0];\n\n const baseNamespace = namespaceOverrideReadable\n ? namespaceOverride!\n : profileEffectiveNamespace ?? codingSelfNamespace ?? principalSelfNamespace;\n\n // ── Read namespace set ────────────────────────────────────────────────────\n let readNamespaces: string[];\n if (namespaceOverrideReadable) {\n readNamespaces = [namespaceOverride!];\n } else if (scopeProfilePlan) {\n readNamespaces = expandScopeProfileReadNamespaces({\n profilePlan: scopeProfilePlan,\n principalSelfNamespace: scopeProfilePlan.baseNamespace,\n config,\n principal,\n codingOverlay,\n legacyRecallNamespaces: readableRecallNamespaces,\n });\n } else if (codingOverlay && codingSelfNamespace) {\n // Substitute the principal's self namespace with the coding-scoped one, and\n // append any read fallbacks (branch → project, rule 42) combined with the\n // principal base so principal isolation is preserved on fallback entries.\n const mapped = readableRecallNamespaces.map((ns) =>\n ns === principalSelfNamespace ? codingSelfNamespace : ns,\n );\n const fallbackNs = codingOverlay.readFallbacks.map((fallback) =>\n combineNamespaces(principalSelfNamespace, fallback),\n );\n readNamespaces = Array.from(new Set<string>([...mapped, ...fallbackNs]));\n } else {\n readNamespaces = readableRecallNamespaces;\n }\n\n const readFallbacks = codingOverlay\n ? codingOverlay.readFallbacks.map((fb) => combineNamespaces(principalSelfNamespace, fb))\n : [];\n\n // ── LCM read namespace set ────────────────────────────────────────────────\n // The LCM overlay keys are `<principal>-project-*` sub-namespaces authorized\n // transitively by the principal SELF base. Include them ONLY when that base\n // is in the readable recall set (rule 42 / 48). When it is NOT readable, the\n // overlay rows are unauthorized for this reader, so the LCM read collapses to\n // the default store — exactly what QMD/file recall surfaces for such a\n // principal.\n const codingOverlaySelfReadable =\n codingOverlay !== null &&\n (scopeProfilePlan\n ? scopeProfilePlan.layers.some((layer) => layer.id === \"userProject\" && layer.readable)\n : readableRecallNamespaces.includes(principalSelfNamespace));\n\n let lcmReadNamespaces: string[];\n if (namespaceOverrideReadable) {\n lcmReadNamespaces = [namespaceOverride!];\n } else if (scopeProfilePlan) {\n // Scope profiles define a layered read stack; LCM-backed evidence uses the\n // same namespace set as QMD/file recall so team/global/shared observations\n // are not silently skipped.\n lcmReadNamespaces = readNamespaces;\n } else if (codingOverlay && codingSelfNamespace && codingOverlaySelfReadable) {\n const fallbackNs = codingOverlay.readFallbacks.map((fallback) =>\n combineNamespaces(principalSelfNamespace, fallback),\n );\n lcmReadNamespaces = [codingSelfNamespace, ...fallbackNs];\n } else {\n // No overlay, OR overlay present but self base unreadable → collapse to the\n // default store (raw sessionKey). No `<principal>-project-*` overlay key is\n // searched.\n lcmReadNamespaces = [config.defaultNamespace];\n }\n\n const lcmReadSessionIds =\n scopeProfilePlan && !sessionKey\n ? []\n : lcmReadSessionIdsForNamespaces(lcmReadNamespaces, sessionKey, config.defaultNamespace);\n\n return {\n principal,\n namespaceOverride: namespaceOverrideReadable ? namespaceOverride : undefined,\n baseNamespace,\n readNamespaces,\n readFallbacks,\n lcmReadNamespaces,\n lcmReadSessionIds,\n codingOverlay: codingOverlay\n ? { namespace: codingOverlay.namespace, readFallbacks: codingOverlay.readFallbacks }\n : null,\n scopeProfilePlan,\n };\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Centralized namespace-resolution helpers (issue #1521 step 3–4)\n//\n// The three functions below consolidate the ad-hoc namespace-resolution logic\n// that was previously scattered across orchestrator.ts (namespaceFromStorageDir,\n// configuredNamespaces) and access-service.ts (resolveWritableNamespace). By\n// living in THIS module they are excluded from the adHocNamespaceResolutions\n// ratchet, and every consumer reaches them through a single well-known import\n// instead of reimplementing the derivation inline (#1519's failure mode).\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * The configured namespace set: default + shared + every namespace-policy\n * name, trimmed and de-duplicated. Pure — reads only from `config`.\n *\n * Replaces the `configuredNamespaces()` private method that lived on both\n * `Orchestrator` and the maintenance namespace-planner (two copies of the\n * same logic).\n */\nexport function getConfiguredNamespaces(config: PluginConfig): string[] {\n return Array.from(\n new Set(\n [\n config.defaultNamespace,\n config.sharedNamespace,\n ...config.namespacePolicies.map((policy) => policy.name),\n ]\n .map((value) => normalizeNamespaceIdentity(value))\n .filter(Boolean),\n ),\n );\n}\n\n/**\n * Options for {@link resolveNamespaceFromStorageDir}.\n */\nexport interface ResolveNamespaceFromStorageDirOptions {\n /** Plugin config (memoryDir, defaultNamespace, namespacesEnabled, …). */\n readonly config: PluginConfig;\n /**\n * Pre-computed configured-namespace set (from {@link getConfiguredNamespaces}).\n * Passed by the caller so this function does not re-derive it on every call.\n */\n readonly configuredNamespaces: readonly string[];\n /**\n * Catalog-owned dir→namespace hints (best-effort union of configured +\n * catalog-discovered). May be `undefined` when no catalog hints are loaded.\n */\n readonly hints?: ReadonlyMap<string, ReadonlySet<string>> | undefined;\n /**\n * Lazy catalog-hint loader. Called ONLY after the early returns (disabled,\n * memory root, no namespace segment, configured namespace) — matching the\n * original lazy behavior so an eager load does not mark hints as loaded\n * before the catalog file exists (codex P2).\n */\n readonly loadHints?: () => void;\n}\n\n/**\n * Derive the namespace a storage directory belongs to.\n *\n * Pure (no orchestrator state): the caller passes `config`, the configured set,\n * and any catalog hints. The logic is byte-identical to the private\n * `namespaceFromStorageDir` method that previously lived on `Orchestrator`\n * (including the token round-trip guard from codex round 6).\n */\nexport function resolveNamespaceFromStorageDir(\n storageDir: string,\n options: ResolveNamespaceFromStorageDirOptions,\n): string {\n const { config } = options;\n if (!resolveNamespaceCapabilities(config).namespaces) return config.defaultNamespace;\n const resolvedStorageDir = path.resolve(storageDir);\n const resolvedMemoryDir = path.resolve(config.memoryDir);\n if (resolvedStorageDir === resolvedMemoryDir) return config.defaultNamespace;\n const m = resolvedStorageDir.match(/[\\\\/]namespaces[\\\\/]([^\\\\/]+)$/);\n if (!m?.[1]) return config.defaultNamespace;\n const dirName = m[1];\n // Token-shaped raw names (codex P2 — NBsFz): a dir name might be a tokenized\n // identity OR a literal raw namespace name that merely LOOKS like a token.\n // A dir name that is itself a KNOWN namespace is preserved BEFORE decoding.\n if (options.configuredNamespaces.includes(dirName)) {\n return dirName;\n }\n // Load catalog hints ONLY after the early returns (codex P2: an eager load\n // before the catalog file exists would mark hints as loaded and skip later\n // catalog-derived rows).\n options.loadHints?.();\n const hints = options.hints;\n const hintedNamespaces = hints?.get(resolvedStorageDir);\n if (hintedNamespaces?.has(dirName)) {\n return dirName;\n }\n if (hintedNamespaces?.size === 1) {\n const [hintedNamespace] = hintedNamespaces;\n if (hintedNamespace) return hintedNamespace;\n }\n const decoded = namespaceIdentityFromToken(dirName);\n if (\n decoded &&\n (namespaceIdentityToken(decoded) === dirName ||\n namespaceIdentityLegacyToken(decoded.normalize(\"NFD\")) === dirName)\n ) {\n return decoded;\n }\n return dirName;\n}\n\n/**\n * Result of resolving a writable namespace: either the authorized namespace\n * or a structured rejection. The caller decides how to surface the rejection\n * (e.g. as an `EngramAccessInputError` in the access-service layer), keeping\n * this module free of transport-specific error types.\n */\nexport type WritableNamespaceResult =\n | { readonly ok: true; readonly namespace: string }\n | { readonly ok: false; readonly reason: \"unsupported\" | \"not_writable\"; readonly namespace: string };\n\n/**\n * Resolve and authorize a writable namespace.\n *\n * Combines the three steps that `AccessService.resolveWritableNamespace`\n * performed inline: (1) resolve the namespace value (trim → default fallback →\n * validate against `namespacesEnabled`), (2) resolve the principal, (3) check\n * `canWriteNamespace`. Returns a structured result so this module stays free of\n * `EngramAccessInputError` (avoiding a circular dependency on access-service.ts).\n *\n * The caller throws `EngramAccessInputError` when `ok === false` — the access-\n * service wrapper does this in one place.\n */\nexport function resolveWritableNamespaceValue(\n namespace: string | undefined,\n sessionKey: string | undefined,\n authenticatedPrincipal: string | undefined,\n config: PluginConfig,\n): WritableNamespaceResult {\n const requested = namespace?.trim();\n let resolved: string;\n if (!requested) {\n resolved = config.defaultNamespace;\n } else if (\n !resolveNamespaceCapabilities(config).namespaces &&\n requested !== config.defaultNamespace\n ) {\n return { ok: false, reason: \"unsupported\", namespace: requested };\n } else {\n resolved = requested;\n }\n\n const trusted = authenticatedPrincipal?.trim();\n const principal =\n typeof trusted === \"string\" && trusted.length > 0\n ? trusted\n : resolvePrincipal(sessionKey, config);\n\n if (!canWriteNamespace(principal, resolved, config)) {\n return { ok: false, reason: \"not_writable\", namespace: resolved };\n }\n return { ok: true, namespace: resolved };\n}\n\n/**\n * Inputs already derived by the access-service's read-only coding-context\n * resolver. Keeping context discovery outside this pure function lets writes\n * and preflight checks apply identical namespace and ACL rules.\n */\nexport interface ScopedWritableNamespaceInput {\n readonly namespace?: string;\n readonly sessionKey?: string;\n readonly authenticatedPrincipal?: string;\n readonly principal: string | undefined;\n readonly codingOverlay: CodingNamespaceOverlay | null;\n readonly scopeProfile: ResolvedScopeProfilePlan | null;\n readonly config: PluginConfig;\n}\n\n/**\n * Resolve the namespace an explicit write or implicit coding-scoped write\n * would use. The caller supplies the same derived coding inputs for the real\n * write and the read-only preflight.\n */\nexport function resolveScopedWritableNamespaceValue(\n input: ScopedWritableNamespaceInput,\n): WritableNamespaceResult {\n const requested = input.namespace?.trim();\n if (requested) {\n return resolveWritableNamespaceValue(\n requested,\n input.sessionKey,\n input.authenticatedPrincipal,\n input.config,\n );\n }\n if (input.scopeProfile) {\n const selectedLayer = input.scopeProfile.layers.find(\n (layer) => layer.id === input.scopeProfile?.writeLayer,\n );\n const writeNamespaceReadable =\n input.scopeProfile.writeNamespace.length > 0 &&\n input.scopeProfile.readNamespaces.includes(input.scopeProfile.writeNamespace);\n if (!selectedLayer?.writable || !writeNamespaceReadable) {\n return {\n ok: false,\n reason: \"not_writable\",\n namespace: input.scopeProfile.writeNamespace,\n };\n }\n return { ok: true, namespace: input.scopeProfile.writeNamespace };\n }\n if (!input.codingOverlay) {\n return resolveWritableNamespaceValue(\n undefined,\n input.sessionKey,\n input.authenticatedPrincipal,\n input.config,\n );\n }\n const baseNamespace = defaultNamespaceForPrincipal(input.principal, input.config);\n if (!canWriteNamespace(input.principal, baseNamespace, input.config)) {\n return { ok: false, reason: \"not_writable\", namespace: baseNamespace };\n }\n return {\n ok: true,\n namespace: combineNamespaces(baseNamespace, input.codingOverlay.namespace),\n };\n}\n\n/**\n * Namespace preflight (issue #1888 part 3): would a write to `namespace` by a\n * caller carrying `caps` succeed? Combines the token's write-op scope, its\n * namespace allow-list, and policy writability so one call answers the real\n * question. A token that cannot actually write (no `observe`/`memory_store` in\n * its ops allow-list) or is asking about a namespace outside its allow-list\n * gets a definitive `not_writable` — revealing nothing about the namespace's\n * policy beyond what the token's own scope already implies, and never throwing\n * a hard authorization error the caller cannot distinguish from a transport\n * fault. (Unrestricted tokens pass both scope checks unchanged.)\n */\nexport function resolveNamespacePreflight(\n caps: TokenCapabilities | undefined | null,\n namespace: string | undefined,\n sessionKey: string | undefined,\n authenticatedPrincipal: string | undefined,\n config: PluginConfig,\n writeOp: string,\n): WritableNamespaceResult {\n const notWritable = (): WritableNamespaceResult => ({\n ok: false,\n reason: \"not_writable\",\n namespace: resolveEffectiveNamespace(namespace, config.defaultNamespace) ?? namespace ?? \"\",\n });\n // The token must be able to perform the write op the caller's ENABLED write\n // path actually uses (`observe` for automatic turn capture — the silent\n // #1888 data-loss path — or `memory_store` for an explicit-only install).\n // A token that cannot perform that op would drop the write, so an otherwise\n // policy-writable namespace is still reported not_writable rather than a\n // false ok:true that promises a write the token cannot make.\n if (!capabilityAllowsOp(caps, writeOp)) {\n return notWritable();\n }\n if (!isNamespaceAllowed(caps, namespace, config.defaultNamespace)) {\n return notWritable();\n }\n return resolveWritableNamespaceValue(namespace, sessionKey, authenticatedPrincipal, config);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
SupportPassportError,
|
|
3
3
|
SupportPassportOwnerCreateGrantRequestSchema,
|
|
4
4
|
getOperation
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-CZUHNJHX.js";
|
|
6
6
|
|
|
7
7
|
// src/support-passport/access-http.ts
|
|
8
8
|
var OWNER_ROOT = "/engram/v1/support-passport";
|
|
@@ -137,4 +137,4 @@ export {
|
|
|
137
137
|
SUPPORT_PASSPORT_OWNER_HTTP_ROUTES,
|
|
138
138
|
handleSupportPassportOwnerHttp
|
|
139
139
|
};
|
|
140
|
-
//# sourceMappingURL=chunk-
|
|
140
|
+
//# sourceMappingURL=chunk-SFR7J2G6.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
namespaceIdentityToken,
|
|
6
6
|
normalizeNamespaceIdentity
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-VUFL7X3H.js";
|
|
8
8
|
import {
|
|
9
9
|
resolveNamespaceCapabilities
|
|
10
10
|
} from "./chunk-JUOZ62Y7.js";
|
|
@@ -470,4 +470,4 @@ export {
|
|
|
470
470
|
NamespaceSearchRouter,
|
|
471
471
|
normalizeQmdResultPath
|
|
472
472
|
};
|
|
473
|
-
//# sourceMappingURL=chunk-
|
|
473
|
+
//# sourceMappingURL=chunk-SNW3YRO6.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
validateRouteTarget
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-PCXDUHQH.js";
|
|
4
4
|
import {
|
|
5
5
|
log
|
|
6
6
|
} from "./chunk-3WCASNQZ.js";
|
|
@@ -306,4 +306,4 @@ var RoutingRulesStore = class {
|
|
|
306
306
|
export {
|
|
307
307
|
RoutingRulesStore
|
|
308
308
|
};
|
|
309
|
-
//# sourceMappingURL=chunk-
|
|
309
|
+
//# sourceMappingURL=chunk-TBUEE7XF.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SUPPORT_PASSPORT_DRAFT_MAX_CONTENT_CHARACTERS,
|
|
3
3
|
SUPPORT_PASSPORT_DRAFT_MAX_MEMORIES
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-CZUHNJHX.js";
|
|
5
5
|
import {
|
|
6
6
|
SUPPORT_PASSPORT_SOURCE_MEMORY_ID_MAX_LENGTH
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-XDL5CYZN.js";
|
|
8
8
|
|
|
9
9
|
// src/support-passport/model-bridge.ts
|
|
10
10
|
import { randomUUID } from "crypto";
|
|
@@ -343,4 +343,4 @@ export {
|
|
|
343
343
|
parseSupportPassportModelJob,
|
|
344
344
|
SupportPassportModelBridge
|
|
345
345
|
};
|
|
346
|
-
//# sourceMappingURL=chunk-
|
|
346
|
+
//# sourceMappingURL=chunk-U7XLOSG2.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
computeCorpusWatermarks,
|
|
6
6
|
resolveCorpusNamespaceRoots
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-LJJWFV2Q.js";
|
|
8
8
|
|
|
9
9
|
// src/operator-doctor-corpus.ts
|
|
10
10
|
var DIGEST_PREFIX_LENGTH = 12;
|
|
@@ -78,4 +78,4 @@ export {
|
|
|
78
78
|
corpusWatermarksFromCheck,
|
|
79
79
|
summarizeCorpusAndReplica
|
|
80
80
|
};
|
|
81
|
-
//# sourceMappingURL=chunk-
|
|
81
|
+
//# sourceMappingURL=chunk-USAUBGBC.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
StorageManager
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-YTZA7U6Q.js";
|
|
4
4
|
import {
|
|
5
5
|
getCachedRuleMemories,
|
|
6
6
|
setCachedRuleMemories
|
|
@@ -119,4 +119,4 @@ export {
|
|
|
119
119
|
compareVerifiedSemanticRuleResults,
|
|
120
120
|
searchVerifiedSemanticRules
|
|
121
121
|
};
|
|
122
|
-
//# sourceMappingURL=chunk-
|
|
122
|
+
//# sourceMappingURL=chunk-USGKHF36.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
summarizeReconcilePlan
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-L3CHRBHE.js";
|
|
4
4
|
import {
|
|
5
5
|
DEFAULT_CITATION_FORMAT,
|
|
6
6
|
stripCitationForTemplate
|
|
@@ -284,4 +284,4 @@ export {
|
|
|
284
284
|
buildReconcileManifest,
|
|
285
285
|
collapseActiveFactDuplicates
|
|
286
286
|
};
|
|
287
|
-
//# sourceMappingURL=chunk-
|
|
287
|
+
//# sourceMappingURL=chunk-VUDALPI5.js.map
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
// src/namespaces/identity.ts
|
|
2
2
|
function normalizeNamespaceIdentity(namespace) {
|
|
3
|
-
return namespace?.trim() ?? "";
|
|
3
|
+
return namespace?.trim().normalize("NFC") ?? "";
|
|
4
4
|
}
|
|
5
|
-
function
|
|
6
|
-
const
|
|
7
|
-
const bytes = new TextEncoder().encode(normalized);
|
|
5
|
+
function encodeNamespaceIdentityToken(namespace) {
|
|
6
|
+
const bytes = new TextEncoder().encode(namespace);
|
|
8
7
|
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
9
8
|
return `ns-${hex || "default"}`;
|
|
10
9
|
}
|
|
10
|
+
function namespaceIdentityToken(namespace) {
|
|
11
|
+
return encodeNamespaceIdentityToken(normalizeNamespaceIdentity(namespace));
|
|
12
|
+
}
|
|
13
|
+
function namespaceIdentityLegacyToken(namespace) {
|
|
14
|
+
return encodeNamespaceIdentityToken(namespace.trim());
|
|
15
|
+
}
|
|
11
16
|
function namespaceIdentityFromToken(token) {
|
|
12
17
|
if (!token.startsWith("ns-")) return null;
|
|
13
18
|
const hex = token.slice(3);
|
|
@@ -16,12 +21,14 @@ function namespaceIdentityFromToken(token) {
|
|
|
16
21
|
return null;
|
|
17
22
|
}
|
|
18
23
|
const decoded = Buffer.from(hex, "hex").toString("utf8");
|
|
19
|
-
|
|
24
|
+
const normalized = normalizeNamespaceIdentity(decoded);
|
|
25
|
+
return namespaceIdentityToken(decoded).toLowerCase() === token.toLowerCase() || namespaceIdentityLegacyToken(decoded).toLowerCase() === token.toLowerCase() ? normalized : null;
|
|
20
26
|
}
|
|
21
27
|
|
|
22
28
|
export {
|
|
23
29
|
normalizeNamespaceIdentity,
|
|
24
30
|
namespaceIdentityToken,
|
|
31
|
+
namespaceIdentityLegacyToken,
|
|
25
32
|
namespaceIdentityFromToken
|
|
26
33
|
};
|
|
27
|
-
//# sourceMappingURL=chunk-
|
|
34
|
+
//# sourceMappingURL=chunk-VUFL7X3H.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/namespaces/identity.ts"],"sourcesContent":["export function normalizeNamespaceIdentity(namespace: string | null | undefined): string {\n return namespace?.trim().normalize(\"NFC\") ?? \"\";\n}\n\nfunction encodeNamespaceIdentityToken(namespace: string): string {\n const bytes = new TextEncoder().encode(namespace);\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n return `ns-${hex || \"default\"}`;\n}\n\nexport function namespaceIdentityToken(namespace: string): string {\n return encodeNamespaceIdentityToken(normalizeNamespaceIdentity(namespace));\n}\n\nexport function namespaceIdentityLegacyToken(namespace: string): string {\n return encodeNamespaceIdentityToken(namespace.trim());\n}\n\nexport function namespaceIdentityFromToken(token: string): string | null {\n if (!token.startsWith(\"ns-\")) return null;\n const hex = token.slice(3);\n if (hex === \"default\") return \"\";\n if (hex.length === 0 || hex.length % 2 !== 0 || !/^[0-9a-f]+$/i.test(hex)) {\n return null;\n }\n const decoded = Buffer.from(hex, \"hex\").toString(\"utf8\");\n const normalized = normalizeNamespaceIdentity(decoded);\n return namespaceIdentityToken(decoded).toLowerCase() === token.toLowerCase() ||\n namespaceIdentityLegacyToken(decoded).toLowerCase() === token.toLowerCase()\n ? normalized\n : null;\n}\n"],"mappings":";AAAO,SAAS,2BAA2B,WAA8C;AACvF,SAAO,WAAW,KAAK,EAAE,UAAU,KAAK,KAAK;AAC/C;AAEA,SAAS,6BAA6B,WAA2B;AAC/D,QAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,SAAS;AAChD,QAAM,MAAM,MAAM,KAAK,OAAO,CAAC,SAAS,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AACnF,SAAO,MAAM,OAAO,SAAS;AAC/B;AAEO,SAAS,uBAAuB,WAA2B;AAChE,SAAO,6BAA6B,2BAA2B,SAAS,CAAC;AAC3E;AAEO,SAAS,6BAA6B,WAA2B;AACtE,SAAO,6BAA6B,UAAU,KAAK,CAAC;AACtD;AAEO,SAAS,2BAA2B,OAA8B;AACvE,MAAI,CAAC,MAAM,WAAW,KAAK,EAAG,QAAO;AACrC,QAAM,MAAM,MAAM,MAAM,CAAC;AACzB,MAAI,QAAQ,UAAW,QAAO;AAC9B,MAAI,IAAI,WAAW,KAAK,IAAI,SAAS,MAAM,KAAK,CAAC,eAAe,KAAK,GAAG,GAAG;AACzE,WAAO;AAAA,EACT;AACA,QAAM,UAAU,OAAO,KAAK,KAAK,KAAK,EAAE,SAAS,MAAM;AACvD,QAAM,aAAa,2BAA2B,OAAO;AACrD,SAAO,uBAAuB,OAAO,EAAE,YAAY,MAAM,MAAM,YAAY,KACzE,6BAA6B,OAAO,EAAE,YAAY,MAAM,MAAM,YAAY,IACxE,aACA;AACN;","names":[]}
|
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
4
|
} from "./chunk-KSXJOC4F.js";
|
|
5
5
|
import {
|
|
6
6
|
StorageManager
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YTZA7U6Q.js";
|
|
8
8
|
import {
|
|
9
9
|
excludeSupportPassportPrivateMemories
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-XDL5CYZN.js";
|
|
11
11
|
import {
|
|
12
12
|
isSafeRouteNamespace
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-PCXDUHQH.js";
|
|
14
14
|
import {
|
|
15
15
|
log
|
|
16
16
|
} from "./chunk-3WCASNQZ.js";
|
|
@@ -138,4 +138,4 @@ export {
|
|
|
138
138
|
runCodexMaterialize,
|
|
139
139
|
runPostConsolidationMaterialize
|
|
140
140
|
};
|
|
141
|
-
//# sourceMappingURL=chunk-
|
|
141
|
+
//# sourceMappingURL=chunk-VYIXCMUP.js.map
|