@remnic/core 9.25.7 → 9.26.0
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 +41 -41
- package/dist/access-audit.js +2 -2
- package/dist/access-authorization-probe.d.ts +1 -1
- package/dist/access-authorization-probe.js +42 -42
- package/dist/access-boundary.d.ts +1 -1
- package/dist/access-boundary.js +41 -41
- package/dist/access-cli.js +98 -97
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http-lcm-compaction.d.ts +130 -0
- package/dist/access-http-lcm-compaction.js +10 -0
- package/dist/access-http.d.ts +1 -1
- package/dist/access-http.js +53 -52
- package/dist/access-identity-continuity-surface.js +41 -41
- package/dist/access-lcm-surface.d.ts +1 -1
- package/dist/access-lcm-surface.js +41 -41
- package/dist/access-mcp.d.ts +1 -1
- package/dist/access-mcp.js +47 -47
- package/dist/access-namespace-preflight.js +41 -41
- package/dist/access-observe-write-surface.d.ts +1 -1
- package/dist/access-observe-write-surface.js +41 -41
- package/dist/access-operations-batch.js +43 -43
- package/dist/access-operations.d.ts +4 -4
- package/dist/access-operations.js +45 -45
- package/dist/access-recall-concurrency.d.ts +1 -1
- package/dist/access-recall-concurrency.js +41 -41
- package/dist/access-recall-response.d.ts +1 -1
- package/dist/access-recall-response.js +41 -41
- package/dist/access-recall-surface.d.ts +1 -1
- package/dist/access-recall-surface.js +41 -41
- package/dist/access-schema.d.ts +92 -77
- package/dist/access-schema.js +8 -8
- package/dist/{access-service-CIdWkP6J.d.ts → access-service-DL8FJnTj.d.ts} +1 -1
- package/dist/access-service.d.ts +1 -1
- package/dist/access-service.js +41 -41
- package/dist/access-surface-catalog.d.ts +1 -1
- package/dist/adapters/index.js +7 -7
- package/dist/adapters/registry.js +3 -3
- package/dist/{auto-sync-5OE5QOZB.js → auto-sync-UPTUVE4X.js} +3 -3
- package/dist/briefing.js +5 -5
- package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-YO5QJ6L3.js} +2 -2
- package/dist/capsule-crypto-YO5QJ6L3.js.map +1 -0
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.js +11 -11
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-JBPKEARU.js → chunk-2QSZNTDO.js} +7 -7
- package/dist/{chunk-BL4JHZSO.js → chunk-2XP3PWL3.js} +40 -40
- package/dist/{chunk-AQ2ZQYDI.js → chunk-4KKV7W62.js} +6 -6
- package/dist/chunk-4Z4XNEWZ.js +86 -0
- package/dist/chunk-4Z4XNEWZ.js.map +1 -0
- package/dist/{chunk-32XTXKIA.js → chunk-67DSDRUB.js} +22 -14
- package/dist/chunk-67DSDRUB.js.map +1 -0
- package/dist/{chunk-2PHX3I5C.js → chunk-6NNNURLK.js} +8 -8
- package/dist/{chunk-BLAKS3CP.js → chunk-77CNV7DY.js} +4 -4
- package/dist/{chunk-TVVEYCNW.js → chunk-7K5Q6COX.js} +4 -4
- package/dist/{chunk-227UFSEQ.js → chunk-7RZJWZ46.js} +5 -5
- package/dist/{chunk-WIGBTWXA.js → chunk-7XXCIO6X.js} +2 -2
- package/dist/{chunk-7CPOZH6Y.js → chunk-AJ7QCSIS.js} +2 -2
- package/dist/{chunk-2NLLXCJG.js → chunk-BXLOS5AJ.js} +2 -2
- package/dist/{chunk-X7Y7WX73.js → chunk-DQEMWVMT.js} +1 -1
- package/dist/{chunk-UMFLAACL.js → chunk-E6EAB4J6.js} +9 -9
- package/dist/{chunk-WSEA52RI.js → chunk-EXM2Q555.js} +2 -2
- package/dist/{chunk-G6MGAE36.js → chunk-F6LSNPPB.js} +5 -5
- package/dist/{chunk-7KXQ44PA.js → chunk-FE5FKTCI.js} +5 -5
- package/dist/{chunk-5J7HJ7RT.js → chunk-H2PBM562.js} +2 -2
- package/dist/{chunk-OIE242WM.js → chunk-HBVOJNV4.js} +3 -3
- package/dist/{chunk-PS5OG75R.js → chunk-IHMXPG4P.js} +2 -2
- package/dist/{chunk-3UOSVTFR.js → chunk-JCVQV6QX.js} +11 -11
- package/dist/{chunk-P25IPRXP.js → chunk-KGTD2KZJ.js} +28 -8
- package/dist/chunk-KGTD2KZJ.js.map +1 -0
- package/dist/{chunk-NY6W6CJW.js → chunk-KPWLZBXZ.js} +2 -2
- package/dist/{chunk-W33DEUKR.js → chunk-LIIQPDFN.js} +2 -2
- package/dist/{chunk-D7MAWMMK.js → chunk-M2KGXXTL.js} +2 -2
- package/dist/{chunk-UOURUMBN.js → chunk-MIFF37NN.js} +2 -2
- package/dist/{chunk-37VPMR4E.js → chunk-NKDVXEFU.js} +6 -6
- package/dist/{chunk-ZPUFQGUF.js → chunk-PQPSZICJ.js} +5 -5
- package/dist/{chunk-ES36HTSC.js → chunk-PTN6UAUA.js} +4 -4
- package/dist/{chunk-KZVPTJNZ.js → chunk-PX7VDW7R.js} +75 -75
- package/dist/{chunk-S2BBISHU.js → chunk-RYHWZ52A.js} +4 -4
- package/dist/{chunk-ZN4UZWMT.js → chunk-SK3EYPKC.js} +2 -2
- package/dist/{chunk-TPYZ727G.js → chunk-SN3ZESWQ.js} +6 -6
- package/dist/{chunk-BUK2FXOL.js → chunk-TKBDJTXG.js} +4 -4
- package/dist/{chunk-4B77XSG5.js → chunk-UV72DEZD.js} +5 -5
- package/dist/{chunk-K4DP5KZA.js → chunk-UZK7VVPD.js} +14 -14
- package/dist/{chunk-KQAFEZQX.js → chunk-VDX2J7OX.js} +2 -2
- package/dist/{chunk-SSYBI43M.js → chunk-VGEH3STZ.js} +6 -6
- package/dist/{chunk-NRV4Y3PM.js → chunk-W4FBGYGK.js} +1 -1
- package/dist/{chunk-IR4LGIH6.js → chunk-WMDRLTNA.js} +2 -2
- package/dist/{chunk-MU7ZHSZE.js → chunk-WZFAPHIK.js} +2 -2
- package/dist/{chunk-JA4HEXV7.js → chunk-XAAIUXJO.js} +2 -2
- package/dist/{chunk-VPSAVXHM.js → chunk-XNUBF6RF.js} +7 -7
- package/dist/{chunk-3CHQZVAL.js → chunk-Y6E37WRG.js} +5 -5
- package/dist/{chunk-E5A6C4A5.js → chunk-YN4AUGNF.js} +48 -48
- package/dist/{chunk-EZ25VE3G.js → chunk-YNDLCWXS.js} +4 -4
- package/dist/{chunk-7IGWPXVC.js → chunk-ZHP4SUPB.js} +2 -2
- package/dist/{cli-Da5kesWC.d.ts → cli-BoouX6LX.d.ts} +1 -1
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +75 -74
- package/dist/compounding/engine.js +5 -5
- package/dist/connectors/codex-materialize-runner.js +5 -5
- package/dist/connectors/index.js +5 -5
- package/dist/conversation-index/backend.js +2 -2
- package/dist/corpus-watermark.js +16 -16
- package/dist/dashboard-runtime.js +2 -2
- package/dist/entity-retrieval.js +5 -5
- package/dist/extraction.js +3 -3
- package/dist/{first-start-migration-BKNGOFED.js → first-start-migration-LEXL3XYL.js} +4 -4
- package/dist/index.d.ts +391 -391
- package/dist/index.js +177 -176
- package/dist/lcm/engine.js +3 -3
- package/dist/lcm/index.js +9 -9
- package/dist/maintenance/memory-governance.js +6 -6
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +5 -5
- package/dist/maintenance/rebuild-memory-projection.js +7 -7
- package/dist/mcp-memory-inspector-app.d.ts +1 -1
- package/dist/namespaces/migrate.js +15 -15
- package/dist/namespaces/search.js +9 -9
- package/dist/namespaces/storage.js +5 -5
- package/dist/operator-doctor-corpus.js +17 -17
- package/dist/operator-toolkit.js +28 -28
- package/dist/orchestration/maintenance.js +11 -11
- package/dist/orchestrator.js +98 -97
- package/dist/resume-bundles.js +1 -1
- package/dist/schemas.d.ts +76 -76
- package/dist/search/factory.js +8 -8
- package/dist/search/index.js +13 -13
- package/dist/search/lancedb-backend.js +2 -2
- package/dist/search/meilisearch-backend.js +2 -2
- package/dist/search/orama-backend.js +2 -2
- package/dist/semantic-consolidation.js +7 -7
- package/dist/semantic-rule-promotion.js +5 -5
- package/dist/semantic-rule-verifier.js +6 -6
- package/dist/session-namespace-bindings.d.ts +27 -0
- package/dist/session-namespace-bindings.js +243 -0
- package/dist/session-namespace-bindings.js.map +1 -0
- package/dist/shared-context/manager.d.ts +8 -8
- package/dist/storage.js +4 -4
- package/dist/testing/lifecycle-matrix.d.ts +100 -0
- package/dist/testing/lifecycle-matrix.js +149 -0
- package/dist/testing/lifecycle-matrix.js.map +1 -0
- package/dist/transfer/backup.js +4 -4
- package/dist/transfer/capsule-export.js +4 -4
- package/dist/transfer/capsule-import.js +3 -3
- package/dist/transfer/types.d.ts +66 -66
- package/dist/verified-recall.js +6 -6
- package/package.json +22 -2
- package/src/access-http-lcm-compaction.ts +116 -0
- package/src/access-http.test.ts +218 -0
- package/src/access-http.ts +8 -8
- package/src/access-schema-pi.test.ts +9 -0
- package/src/access-schema.ts +29 -4
- package/src/access-surface-catalog.test.ts +1 -0
- package/src/session-namespace-bindings.test.ts +358 -0
- package/src/session-namespace-bindings.ts +301 -0
- package/src/testing/lifecycle-matrix.ts +17 -7
- package/dist/chunk-32XTXKIA.js.map +0 -1
- package/dist/chunk-P25IPRXP.js.map +0 -1
- /package/dist/{capsule-crypto-7FJQINUR.js.map → access-http-lcm-compaction.js.map} +0 -0
- /package/dist/{auto-sync-5OE5QOZB.js.map → auto-sync-UPTUVE4X.js.map} +0 -0
- /package/dist/{chunk-JBPKEARU.js.map → chunk-2QSZNTDO.js.map} +0 -0
- /package/dist/{chunk-BL4JHZSO.js.map → chunk-2XP3PWL3.js.map} +0 -0
- /package/dist/{chunk-AQ2ZQYDI.js.map → chunk-4KKV7W62.js.map} +0 -0
- /package/dist/{chunk-2PHX3I5C.js.map → chunk-6NNNURLK.js.map} +0 -0
- /package/dist/{chunk-BLAKS3CP.js.map → chunk-77CNV7DY.js.map} +0 -0
- /package/dist/{chunk-TVVEYCNW.js.map → chunk-7K5Q6COX.js.map} +0 -0
- /package/dist/{chunk-227UFSEQ.js.map → chunk-7RZJWZ46.js.map} +0 -0
- /package/dist/{chunk-WIGBTWXA.js.map → chunk-7XXCIO6X.js.map} +0 -0
- /package/dist/{chunk-7CPOZH6Y.js.map → chunk-AJ7QCSIS.js.map} +0 -0
- /package/dist/{chunk-2NLLXCJG.js.map → chunk-BXLOS5AJ.js.map} +0 -0
- /package/dist/{chunk-X7Y7WX73.js.map → chunk-DQEMWVMT.js.map} +0 -0
- /package/dist/{chunk-UMFLAACL.js.map → chunk-E6EAB4J6.js.map} +0 -0
- /package/dist/{chunk-WSEA52RI.js.map → chunk-EXM2Q555.js.map} +0 -0
- /package/dist/{chunk-G6MGAE36.js.map → chunk-F6LSNPPB.js.map} +0 -0
- /package/dist/{chunk-7KXQ44PA.js.map → chunk-FE5FKTCI.js.map} +0 -0
- /package/dist/{chunk-5J7HJ7RT.js.map → chunk-H2PBM562.js.map} +0 -0
- /package/dist/{chunk-OIE242WM.js.map → chunk-HBVOJNV4.js.map} +0 -0
- /package/dist/{chunk-PS5OG75R.js.map → chunk-IHMXPG4P.js.map} +0 -0
- /package/dist/{chunk-3UOSVTFR.js.map → chunk-JCVQV6QX.js.map} +0 -0
- /package/dist/{chunk-NY6W6CJW.js.map → chunk-KPWLZBXZ.js.map} +0 -0
- /package/dist/{chunk-W33DEUKR.js.map → chunk-LIIQPDFN.js.map} +0 -0
- /package/dist/{chunk-D7MAWMMK.js.map → chunk-M2KGXXTL.js.map} +0 -0
- /package/dist/{chunk-UOURUMBN.js.map → chunk-MIFF37NN.js.map} +0 -0
- /package/dist/{chunk-37VPMR4E.js.map → chunk-NKDVXEFU.js.map} +0 -0
- /package/dist/{chunk-ZPUFQGUF.js.map → chunk-PQPSZICJ.js.map} +0 -0
- /package/dist/{chunk-ES36HTSC.js.map → chunk-PTN6UAUA.js.map} +0 -0
- /package/dist/{chunk-KZVPTJNZ.js.map → chunk-PX7VDW7R.js.map} +0 -0
- /package/dist/{chunk-S2BBISHU.js.map → chunk-RYHWZ52A.js.map} +0 -0
- /package/dist/{chunk-ZN4UZWMT.js.map → chunk-SK3EYPKC.js.map} +0 -0
- /package/dist/{chunk-TPYZ727G.js.map → chunk-SN3ZESWQ.js.map} +0 -0
- /package/dist/{chunk-BUK2FXOL.js.map → chunk-TKBDJTXG.js.map} +0 -0
- /package/dist/{chunk-4B77XSG5.js.map → chunk-UV72DEZD.js.map} +0 -0
- /package/dist/{chunk-K4DP5KZA.js.map → chunk-UZK7VVPD.js.map} +0 -0
- /package/dist/{chunk-KQAFEZQX.js.map → chunk-VDX2J7OX.js.map} +0 -0
- /package/dist/{chunk-SSYBI43M.js.map → chunk-VGEH3STZ.js.map} +0 -0
- /package/dist/{chunk-NRV4Y3PM.js.map → chunk-W4FBGYGK.js.map} +0 -0
- /package/dist/{chunk-IR4LGIH6.js.map → chunk-WMDRLTNA.js.map} +0 -0
- /package/dist/{chunk-MU7ZHSZE.js.map → chunk-WZFAPHIK.js.map} +0 -0
- /package/dist/{chunk-JA4HEXV7.js.map → chunk-XAAIUXJO.js.map} +0 -0
- /package/dist/{chunk-VPSAVXHM.js.map → chunk-XNUBF6RF.js.map} +0 -0
- /package/dist/{chunk-3CHQZVAL.js.map → chunk-Y6E37WRG.js.map} +0 -0
- /package/dist/{chunk-E5A6C4A5.js.map → chunk-YN4AUGNF.js.map} +0 -0
- /package/dist/{chunk-EZ25VE3G.js.map → chunk-YNDLCWXS.js.map} +0 -0
- /package/dist/{chunk-7IGWPXVC.js.map → chunk-ZHP4SUPB.js.map} +0 -0
- /package/dist/{first-start-migration-BKNGOFED.js.map → first-start-migration-LEXL3XYL.js.map} +0 -0
package/dist/verified-recall.js
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
compareVerifiedEpisodeResults,
|
|
3
3
|
searchVerifiedEpisodes
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-VGEH3STZ.js";
|
|
5
5
|
import "./chunk-B7RWBZXW.js";
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-K4DP5KZA.js";
|
|
8
|
-
import "./chunk-2QMKMVHH.js";
|
|
6
|
+
import "./chunk-UZK7VVPD.js";
|
|
9
7
|
import "./chunk-UHVXE24Y.js";
|
|
8
|
+
import "./chunk-2QMKMVHH.js";
|
|
10
9
|
import "./chunk-DEBHXHJR.js";
|
|
11
10
|
import "./chunk-DNHVPZOD.js";
|
|
12
11
|
import "./chunk-6CIAGZ6X.js";
|
|
13
|
-
import "./chunk-J6A3CX5N.js";
|
|
14
12
|
import "./chunk-HKF5VASQ.js";
|
|
13
|
+
import "./chunk-J6A3CX5N.js";
|
|
15
14
|
import "./chunk-V25OWXAC.js";
|
|
16
15
|
import "./chunk-XKGPIWGI.js";
|
|
17
16
|
import "./chunk-EGGE52UU.js";
|
|
@@ -28,12 +27,12 @@ import "./chunk-R3DJVOSM.js";
|
|
|
28
27
|
import "./chunk-BT7NVCML.js";
|
|
29
28
|
import "./chunk-JYIJNNN3.js";
|
|
30
29
|
import "./chunk-AAD2ZS6P.js";
|
|
31
|
-
import "./chunk-W5H6ZRPT.js";
|
|
32
30
|
import "./chunk-5VQJBH2G.js";
|
|
33
31
|
import "./chunk-AITCS2RC.js";
|
|
34
32
|
import "./chunk-XMMESCPD.js";
|
|
35
33
|
import "./chunk-42JKGUFJ.js";
|
|
36
34
|
import "./chunk-PDJTHRAW.js";
|
|
35
|
+
import "./chunk-W5H6ZRPT.js";
|
|
37
36
|
import "./chunk-G7D6GZ5J.js";
|
|
38
37
|
import "./chunk-C23BYTJV.js";
|
|
39
38
|
import "./chunk-QIEUU4UK.js";
|
|
@@ -49,6 +48,7 @@ import "./chunk-EYIEWJNI.js";
|
|
|
49
48
|
import "./chunk-JUC24CTX.js";
|
|
50
49
|
import "./chunk-6MKAMLQL.js";
|
|
51
50
|
import "./chunk-JAKIDQB2.js";
|
|
51
|
+
import "./chunk-ZBJMUXZH.js";
|
|
52
52
|
import "./chunk-BJMBJZ2Y.js";
|
|
53
53
|
import "./chunk-UKJAGEXH.js";
|
|
54
54
|
import "./chunk-FP2373TW.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remnic/core",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.26.0",
|
|
4
4
|
"description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -2420,6 +2420,26 @@
|
|
|
2420
2420
|
"remnic-source": "./src/session-integrity.ts",
|
|
2421
2421
|
"import": "./dist/session-integrity.js"
|
|
2422
2422
|
},
|
|
2423
|
+
"./session-namespace-bindings": {
|
|
2424
|
+
"types": "./dist/session-namespace-bindings.d.ts",
|
|
2425
|
+
"remnic-source": "./src/session-namespace-bindings.ts",
|
|
2426
|
+
"import": "./dist/session-namespace-bindings.js"
|
|
2427
|
+
},
|
|
2428
|
+
"./session-namespace-bindings.js": {
|
|
2429
|
+
"types": "./dist/session-namespace-bindings.d.ts",
|
|
2430
|
+
"remnic-source": "./src/session-namespace-bindings.ts",
|
|
2431
|
+
"import": "./dist/session-namespace-bindings.js"
|
|
2432
|
+
},
|
|
2433
|
+
"./testing/lifecycle-matrix": {
|
|
2434
|
+
"types": "./dist/testing/lifecycle-matrix.d.ts",
|
|
2435
|
+
"remnic-source": "./src/testing/lifecycle-matrix.ts",
|
|
2436
|
+
"import": "./dist/testing/lifecycle-matrix.js"
|
|
2437
|
+
},
|
|
2438
|
+
"./testing/lifecycle-matrix.js": {
|
|
2439
|
+
"types": "./dist/testing/lifecycle-matrix.d.ts",
|
|
2440
|
+
"remnic-source": "./src/testing/lifecycle-matrix.ts",
|
|
2441
|
+
"import": "./dist/testing/lifecycle-matrix.js"
|
|
2442
|
+
},
|
|
2423
2443
|
"./session-observer-bands": {
|
|
2424
2444
|
"types": "./dist/session-observer-bands.d.ts",
|
|
2425
2445
|
"remnic-source": "./src/session-observer-bands.ts",
|
|
@@ -3056,7 +3076,7 @@
|
|
|
3056
3076
|
"core"
|
|
3057
3077
|
],
|
|
3058
3078
|
"peerDependencies": {
|
|
3059
|
-
"@remnic/coding-graph": "^9.
|
|
3079
|
+
"@remnic/coding-graph": "^9.26.0"
|
|
3060
3080
|
},
|
|
3061
3081
|
"peerDependenciesMeta": {
|
|
3062
3082
|
"@remnic/coding-graph": {
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { ServerResponse } from "node:http";
|
|
2
|
+
|
|
3
|
+
import type { LcmCompactionFlushRequest } from "./access-schema.js";
|
|
4
|
+
import type { EngramAccessLcmCompactionFlushResponse, EngramAccessService } from "./access-service.js";
|
|
5
|
+
|
|
6
|
+
type LcmCompactionService = Pick<EngramAccessService, "lcmCompactionFlush">;
|
|
7
|
+
type JsonResponder = (res: ServerResponse, status: number, payload: unknown) => void;
|
|
8
|
+
|
|
9
|
+
export interface LcmCompactionFlushHttpOptions {
|
|
10
|
+
body: LcmCompactionFlushRequest;
|
|
11
|
+
service: LcmCompactionService;
|
|
12
|
+
response: ServerResponse;
|
|
13
|
+
ensureWriteRateLimitAvailable: () => void;
|
|
14
|
+
recordWriteRateLimitHit: () => void;
|
|
15
|
+
resolveNamespace: (namespace?: string) => string | undefined;
|
|
16
|
+
defaultNamespace?: string;
|
|
17
|
+
resolveRequestPrincipal: () => string | undefined;
|
|
18
|
+
respondJson: JsonResponder;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function respondLcmCompactionCapabilitiesHttp(response: ServerResponse): void {
|
|
22
|
+
response.statusCode = 200;
|
|
23
|
+
response.setHeader("content-type", "application/json; charset=utf-8");
|
|
24
|
+
response.end(JSON.stringify({ lcmCompactionFlushBatch: true }, null, 2));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function handleLcmCompactionFlushHttp({
|
|
28
|
+
body,
|
|
29
|
+
service,
|
|
30
|
+
response,
|
|
31
|
+
ensureWriteRateLimitAvailable,
|
|
32
|
+
recordWriteRateLimitHit,
|
|
33
|
+
resolveNamespace,
|
|
34
|
+
defaultNamespace,
|
|
35
|
+
resolveRequestPrincipal,
|
|
36
|
+
respondJson,
|
|
37
|
+
}: LcmCompactionFlushHttpOptions): Promise<void> {
|
|
38
|
+
ensureWriteRateLimitAvailable();
|
|
39
|
+
const requestedNamespaces = body.namespaces;
|
|
40
|
+
if (requestedNamespaces === undefined) {
|
|
41
|
+
const result = await service.lcmCompactionFlush({
|
|
42
|
+
sessionKey: body.sessionKey,
|
|
43
|
+
namespace: resolveNamespace(body.namespace),
|
|
44
|
+
authenticatedPrincipal: resolveRequestPrincipal(),
|
|
45
|
+
});
|
|
46
|
+
recordWriteRateLimitHit();
|
|
47
|
+
respondJson(response, 200, result);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const resolutionOutcomes = await Promise.allSettled(
|
|
51
|
+
requestedNamespaces.map(async (requestedNamespace) => {
|
|
52
|
+
const namespace = resolveNamespace(requestedNamespace);
|
|
53
|
+
return {
|
|
54
|
+
requestedNamespace,
|
|
55
|
+
namespace,
|
|
56
|
+
effectiveNamespace: namespace ?? defaultNamespace ?? "",
|
|
57
|
+
};
|
|
58
|
+
})
|
|
59
|
+
);
|
|
60
|
+
const uniqueResolvedNamespaces: Array<{
|
|
61
|
+
requestedNamespace: string;
|
|
62
|
+
namespace: string | undefined;
|
|
63
|
+
effectiveNamespace: string;
|
|
64
|
+
}> = [];
|
|
65
|
+
const queuedEffectiveNamespaces = new Set<string>();
|
|
66
|
+
for (const outcome of resolutionOutcomes) {
|
|
67
|
+
if (outcome.status === "fulfilled" && !queuedEffectiveNamespaces.has(outcome.value.effectiveNamespace)) {
|
|
68
|
+
queuedEffectiveNamespaces.add(outcome.value.effectiveNamespace);
|
|
69
|
+
uniqueResolvedNamespaces.push(outcome.value);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const serviceOutcomes = await Promise.allSettled(
|
|
73
|
+
uniqueResolvedNamespaces.map(({ namespace }) =>
|
|
74
|
+
service.lcmCompactionFlush({
|
|
75
|
+
sessionKey: body.sessionKey,
|
|
76
|
+
namespace,
|
|
77
|
+
authenticatedPrincipal: resolveRequestPrincipal(),
|
|
78
|
+
})
|
|
79
|
+
)
|
|
80
|
+
);
|
|
81
|
+
const serviceOutcomesByEffectiveNamespace = new Map<
|
|
82
|
+
string,
|
|
83
|
+
PromiseSettledResult<EngramAccessLcmCompactionFlushResponse>
|
|
84
|
+
>();
|
|
85
|
+
for (const [index, resolved] of uniqueResolvedNamespaces.entries()) {
|
|
86
|
+
const serviceOutcome = serviceOutcomes[index];
|
|
87
|
+
if (serviceOutcome !== undefined) {
|
|
88
|
+
serviceOutcomesByEffectiveNamespace.set(resolved.effectiveNamespace, serviceOutcome);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
type BatchResult =
|
|
92
|
+
| {
|
|
93
|
+
requestedNamespace: string;
|
|
94
|
+
status: "fulfilled";
|
|
95
|
+
result: EngramAccessLcmCompactionFlushResponse;
|
|
96
|
+
}
|
|
97
|
+
| { requestedNamespace: string; status: "rejected" };
|
|
98
|
+
const results: BatchResult[] = resolutionOutcomes.map((outcome, index) => {
|
|
99
|
+
const requestedNamespace = requestedNamespaces[index] ?? "";
|
|
100
|
+
if (outcome.status === "rejected") {
|
|
101
|
+
return { status: "rejected", requestedNamespace };
|
|
102
|
+
}
|
|
103
|
+
const serviceOutcome = serviceOutcomesByEffectiveNamespace.get(outcome.value.effectiveNamespace);
|
|
104
|
+
return serviceOutcome?.status === "fulfilled"
|
|
105
|
+
? { status: "fulfilled", requestedNamespace, result: serviceOutcome.value }
|
|
106
|
+
: { status: "rejected", requestedNamespace };
|
|
107
|
+
});
|
|
108
|
+
recordWriteRateLimitHit();
|
|
109
|
+
respondJson(response, 200, {
|
|
110
|
+
enabled: results.every((result) => result.status === "fulfilled" && result.result.enabled !== false),
|
|
111
|
+
flushed: results.every((result) => result.status === "fulfilled" && result.result.flushed !== false),
|
|
112
|
+
sessionKey: body.sessionKey,
|
|
113
|
+
namespaces: requestedNamespaces,
|
|
114
|
+
results: results.map(({ requestedNamespace, ...result }) => ({ ...result, namespace: requestedNamespace })),
|
|
115
|
+
});
|
|
116
|
+
}
|
package/src/access-http.test.ts
CHANGED
|
@@ -108,6 +108,224 @@ test("HTTP memory browse rejects malformed pagination and sort query values", as
|
|
|
108
108
|
}
|
|
109
109
|
});
|
|
110
110
|
|
|
111
|
+
test("HTTP batch LCM flush isolates namespace failures and charges one write quota", async () => {
|
|
112
|
+
const calls: Array<{ namespace?: string; sessionKey: string; authenticatedPrincipal?: string }> = [];
|
|
113
|
+
const service = {
|
|
114
|
+
lcmCompactionFlush: async (request: {
|
|
115
|
+
namespace?: string;
|
|
116
|
+
sessionKey: string;
|
|
117
|
+
authenticatedPrincipal?: string;
|
|
118
|
+
}) => {
|
|
119
|
+
calls.push(request);
|
|
120
|
+
if (request.namespace === "team-b") throw new Error("team-b flush failed");
|
|
121
|
+
return { enabled: true, flushed: true };
|
|
122
|
+
},
|
|
123
|
+
} as unknown as EngramAccessService;
|
|
124
|
+
const server = new EngramAccessHttpServer({
|
|
125
|
+
service,
|
|
126
|
+
port: 0,
|
|
127
|
+
authToken: "test-token",
|
|
128
|
+
adminConsoleEnabled: false,
|
|
129
|
+
writeRateLimitMaxRequests: 1,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const status = await server.start();
|
|
133
|
+
try {
|
|
134
|
+
const response = await fetch(`http://127.0.0.1:${status.port}/engram/v1/lcm/compaction/flush`, {
|
|
135
|
+
method: "POST",
|
|
136
|
+
headers: {
|
|
137
|
+
authorization: "Bearer test-token",
|
|
138
|
+
"content-type": "application/json",
|
|
139
|
+
},
|
|
140
|
+
body: JSON.stringify({
|
|
141
|
+
sessionKey: "batch-session",
|
|
142
|
+
namespaces: ["team-a", "team-b"],
|
|
143
|
+
}),
|
|
144
|
+
});
|
|
145
|
+
assert.equal(response.status, 200);
|
|
146
|
+
assert.deepEqual(await response.json(), {
|
|
147
|
+
enabled: false,
|
|
148
|
+
flushed: false,
|
|
149
|
+
sessionKey: "batch-session",
|
|
150
|
+
namespaces: ["team-a", "team-b"],
|
|
151
|
+
results: [
|
|
152
|
+
{
|
|
153
|
+
status: "fulfilled",
|
|
154
|
+
namespace: "team-a",
|
|
155
|
+
result: { enabled: true, flushed: true },
|
|
156
|
+
},
|
|
157
|
+
{ status: "rejected", namespace: "team-b" },
|
|
158
|
+
],
|
|
159
|
+
});
|
|
160
|
+
assert.deepEqual(calls, [
|
|
161
|
+
{
|
|
162
|
+
sessionKey: "batch-session",
|
|
163
|
+
namespace: "team-a",
|
|
164
|
+
authenticatedPrincipal: undefined,
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
sessionKey: "batch-session",
|
|
168
|
+
namespace: "team-b",
|
|
169
|
+
authenticatedPrincipal: undefined,
|
|
170
|
+
},
|
|
171
|
+
]);
|
|
172
|
+
|
|
173
|
+
const conflicting = await fetch(`http://127.0.0.1:${status.port}/engram/v1/lcm/compaction/flush`, {
|
|
174
|
+
method: "POST",
|
|
175
|
+
headers: {
|
|
176
|
+
authorization: "Bearer test-token",
|
|
177
|
+
"content-type": "application/json",
|
|
178
|
+
},
|
|
179
|
+
body: JSON.stringify({
|
|
180
|
+
sessionKey: "batch-session",
|
|
181
|
+
namespace: "team-a",
|
|
182
|
+
namespaces: ["team-b"],
|
|
183
|
+
}),
|
|
184
|
+
});
|
|
185
|
+
assert.equal(conflicting.status, 400);
|
|
186
|
+
assert.equal(calls.length, 2, "conflicting namespace forms must fail before dispatch");
|
|
187
|
+
|
|
188
|
+
const rateLimited = await fetch(`http://127.0.0.1:${status.port}/engram/v1/lcm/compaction/flush`, {
|
|
189
|
+
method: "POST",
|
|
190
|
+
headers: {
|
|
191
|
+
authorization: "Bearer test-token",
|
|
192
|
+
"content-type": "application/json",
|
|
193
|
+
},
|
|
194
|
+
body: JSON.stringify({
|
|
195
|
+
sessionKey: "batch-session",
|
|
196
|
+
namespaces: ["team-a"],
|
|
197
|
+
}),
|
|
198
|
+
});
|
|
199
|
+
assert.equal(rateLimited.status, 429);
|
|
200
|
+
assert.equal(calls.length, 2, "a batch consumes one write quota unit");
|
|
201
|
+
} finally {
|
|
202
|
+
await server.stop();
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test("HTTP batch LCM flush deduplicates aliases after effective namespace resolution", async () => {
|
|
207
|
+
const calls: Array<{ namespace?: string; sessionKey: string; authenticatedPrincipal?: string }> = [];
|
|
208
|
+
const service = {
|
|
209
|
+
configRef: parseConfig({ namespacesEnabled: true, defaultNamespace: "default" }),
|
|
210
|
+
lcmCompactionFlush: async (request: {
|
|
211
|
+
namespace?: string;
|
|
212
|
+
sessionKey: string;
|
|
213
|
+
authenticatedPrincipal?: string;
|
|
214
|
+
}) => {
|
|
215
|
+
calls.push(request);
|
|
216
|
+
return { enabled: true, flushed: true };
|
|
217
|
+
},
|
|
218
|
+
} as unknown as EngramAccessService;
|
|
219
|
+
const server = new EngramAccessHttpServer({
|
|
220
|
+
service,
|
|
221
|
+
port: 0,
|
|
222
|
+
authToken: "test-token",
|
|
223
|
+
adminConsoleEnabled: false,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const status = await server.start();
|
|
227
|
+
try {
|
|
228
|
+
const response = await fetch(`http://127.0.0.1:${status.port}/engram/v1/lcm/compaction/flush`, {
|
|
229
|
+
method: "POST",
|
|
230
|
+
headers: {
|
|
231
|
+
authorization: "Bearer test-token",
|
|
232
|
+
"content-type": "application/json",
|
|
233
|
+
},
|
|
234
|
+
body: JSON.stringify({
|
|
235
|
+
sessionKey: "effective-namespace-session",
|
|
236
|
+
namespaces: ["", "default"],
|
|
237
|
+
}),
|
|
238
|
+
});
|
|
239
|
+
assert.equal(response.status, 200);
|
|
240
|
+
assert.deepEqual(await response.json(), {
|
|
241
|
+
enabled: true,
|
|
242
|
+
flushed: true,
|
|
243
|
+
sessionKey: "effective-namespace-session",
|
|
244
|
+
namespaces: ["", "default"],
|
|
245
|
+
results: [
|
|
246
|
+
{
|
|
247
|
+
status: "fulfilled",
|
|
248
|
+
namespace: "",
|
|
249
|
+
result: { enabled: true, flushed: true },
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
status: "fulfilled",
|
|
253
|
+
namespace: "default",
|
|
254
|
+
result: { enabled: true, flushed: true },
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
});
|
|
258
|
+
assert.deepEqual(calls, [
|
|
259
|
+
{
|
|
260
|
+
sessionKey: "effective-namespace-session",
|
|
261
|
+
namespace: undefined,
|
|
262
|
+
authenticatedPrincipal: undefined,
|
|
263
|
+
},
|
|
264
|
+
]);
|
|
265
|
+
} finally {
|
|
266
|
+
await server.stop();
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
test("HTTP batch LCM flush isolates per-namespace authorization failures", async () => {
|
|
272
|
+
const calls: string[] = [];
|
|
273
|
+
const service = {
|
|
274
|
+
lcmCompactionFlush: async (request: { namespace?: string }) => {
|
|
275
|
+
calls.push(request.namespace ?? "");
|
|
276
|
+
return { enabled: true, flushed: true };
|
|
277
|
+
},
|
|
278
|
+
} as unknown as EngramAccessService;
|
|
279
|
+
const server = new EngramAccessHttpServer({
|
|
280
|
+
service,
|
|
281
|
+
port: 0,
|
|
282
|
+
authToken: "unused-token",
|
|
283
|
+
authTokenEntriesGetter: () => [
|
|
284
|
+
{
|
|
285
|
+
token: "scoped-token",
|
|
286
|
+
capabilities: {
|
|
287
|
+
version: 1,
|
|
288
|
+
ops: ["lcm_compaction_flush"],
|
|
289
|
+
namespaces: ["team-a"],
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
adminConsoleEnabled: false,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const status = await server.start();
|
|
297
|
+
try {
|
|
298
|
+
const response = await fetch(`http://127.0.0.1:${status.port}/engram/v1/lcm/compaction/flush`, {
|
|
299
|
+
method: "POST",
|
|
300
|
+
headers: {
|
|
301
|
+
authorization: "Bearer scoped-token",
|
|
302
|
+
"content-type": "application/json",
|
|
303
|
+
},
|
|
304
|
+
body: JSON.stringify({
|
|
305
|
+
sessionKey: "authorized-batch-session",
|
|
306
|
+
namespaces: ["team-a", "team-b"],
|
|
307
|
+
}),
|
|
308
|
+
});
|
|
309
|
+
assert.equal(response.status, 200);
|
|
310
|
+
assert.deepEqual(await response.json(), {
|
|
311
|
+
enabled: false,
|
|
312
|
+
flushed: false,
|
|
313
|
+
sessionKey: "authorized-batch-session",
|
|
314
|
+
namespaces: ["team-a", "team-b"],
|
|
315
|
+
results: [
|
|
316
|
+
{
|
|
317
|
+
status: "fulfilled",
|
|
318
|
+
namespace: "team-a",
|
|
319
|
+
result: { enabled: true, flushed: true },
|
|
320
|
+
},
|
|
321
|
+
{ status: "rejected", namespace: "team-b" },
|
|
322
|
+
],
|
|
323
|
+
});
|
|
324
|
+
assert.deepEqual(calls, ["team-a"]);
|
|
325
|
+
} finally {
|
|
326
|
+
await server.stop();
|
|
327
|
+
}
|
|
328
|
+
});
|
|
111
329
|
test("HTTP admin console assets are public but API routes require bearer authentication", async () => {
|
|
112
330
|
const service = {} as EngramAccessService;
|
|
113
331
|
const server = new EngramAccessHttpServer({
|
package/src/access-http.ts
CHANGED
|
@@ -40,6 +40,7 @@ import { projectTagProjectId } from "./coding/coding-namespace.js";
|
|
|
40
40
|
import { getOperation, type OperationName } from "./access-boundary.js";
|
|
41
41
|
import { authorizationProbeNamespaces, probeOperationAuthorization } from "./access-authorization-probe.js";
|
|
42
42
|
import { resolveQueryNamespaceWritablePreflight } from "./access-namespace-preflight.js";
|
|
43
|
+
import * as lcm from "./access-http-lcm-compaction.js";
|
|
43
44
|
import {
|
|
44
45
|
assertOperationAllowed,
|
|
45
46
|
capabilityAllowsOp,
|
|
@@ -889,7 +890,7 @@ export class EngramAccessHttpServer {
|
|
|
889
890
|
this.respondJson(res, 200, await this.service.health());
|
|
890
891
|
return;
|
|
891
892
|
}
|
|
892
|
-
|
|
893
|
+
if (req.method === "GET" && pathname === "/engram/v1/capabilities") return lcm.respondLcmCompactionCapabilitiesHttp(res);
|
|
893
894
|
if (req.method === "GET" && pathname === "/engram/v1/authorization") {
|
|
894
895
|
res.setHeader("cache-control", "no-store");
|
|
895
896
|
const probe = probeOperationAuthorization(tokenCapabilityStore.getStore(), parsed.searchParams.getAll("op"));
|
|
@@ -1674,14 +1675,13 @@ export class EngramAccessHttpServer {
|
|
|
1674
1675
|
) {
|
|
1675
1676
|
this.enforceTokenOp("lcm_compaction_flush"); // boundary dispatch (issue #1525)
|
|
1676
1677
|
const body = await this.readValidatedBody(req, "lcmCompactionFlush");
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1678
|
+
await lcm.handleLcmCompactionFlushHttp({
|
|
1679
|
+
body, service: this.service,
|
|
1680
|
+
response: res, ensureWriteRateLimitAvailable: () => this.ensureWriteRateLimitAvailable(req),
|
|
1681
|
+
recordWriteRateLimitHit: () => this.recordWriteRateLimitHit(req),
|
|
1682
|
+
resolveNamespace: (namespace) => this.resolveNamespace(req, namespace), defaultNamespace: this.service.configRef?.defaultNamespace,
|
|
1683
|
+
resolveRequestPrincipal: () => this.resolveRequestPrincipal(req), respondJson: this.respondJson.bind(this),
|
|
1682
1684
|
});
|
|
1683
|
-
this.recordWriteRateLimitHit(req);
|
|
1684
|
-
this.respondJson(res, 200, response);
|
|
1685
1685
|
return;
|
|
1686
1686
|
}
|
|
1687
1687
|
|
|
@@ -49,6 +49,15 @@ test("LCM compaction schemas validate Pi extension requests", () => {
|
|
|
49
49
|
assert.equal(record.success, true);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
+
test("LCM compaction flush rejects duplicate namespace batches", () => {
|
|
53
|
+
const result = validateRequest("lcmCompactionFlush", {
|
|
54
|
+
sessionKey: "pi:session",
|
|
55
|
+
namespaces: ["work", "work"],
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
assert.equal(result.success, false);
|
|
59
|
+
});
|
|
60
|
+
|
|
52
61
|
test("LCM compaction record rejects invalid token counts", () => {
|
|
53
62
|
const result = validateRequest("lcmCompactionRecord", {
|
|
54
63
|
sessionKey: "pi:session",
|
package/src/access-schema.ts
CHANGED
|
@@ -331,10 +331,35 @@ export const lcmSearchRequestSchema = z.object({
|
|
|
331
331
|
limit: z.number().int().min(1).max(100).optional(),
|
|
332
332
|
});
|
|
333
333
|
|
|
334
|
-
export const lcmCompactionFlushRequestSchema = z
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
334
|
+
export const lcmCompactionFlushRequestSchema = z
|
|
335
|
+
.object({
|
|
336
|
+
sessionKey: z.string().trim().min(1, "sessionKey is required").max(512),
|
|
337
|
+
namespace: namespaceSchema,
|
|
338
|
+
/**
|
|
339
|
+
* Optional multi-namespace flush used by delegate lifecycle replay. The
|
|
340
|
+
* server handles the whole list as one quota-counted write.
|
|
341
|
+
*/
|
|
342
|
+
namespaces: z.array(z.string().trim().max(256)).min(1).max(64).optional(),
|
|
343
|
+
})
|
|
344
|
+
.superRefine((value, ctx) => {
|
|
345
|
+
if (value.namespace !== undefined && value.namespaces !== undefined) {
|
|
346
|
+
ctx.addIssue({
|
|
347
|
+
code: z.ZodIssueCode.custom,
|
|
348
|
+
message: "namespace and namespaces are mutually exclusive",
|
|
349
|
+
path: ["namespaces"],
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
if (
|
|
353
|
+
value.namespaces !== undefined &&
|
|
354
|
+
new Set(value.namespaces).size !== value.namespaces.length
|
|
355
|
+
) {
|
|
356
|
+
ctx.addIssue({
|
|
357
|
+
code: z.ZodIssueCode.custom,
|
|
358
|
+
message: "namespaces must not contain duplicates",
|
|
359
|
+
path: ["namespaces"],
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
});
|
|
338
363
|
|
|
339
364
|
export const lcmCompactionRecordRequestSchema = z.object({
|
|
340
365
|
sessionKey: z.string().trim().min(1, "sessionKey is required").max(512),
|
|
@@ -534,6 +534,7 @@ test("HTTP handler source routes match the catalog (static completeness)", () =>
|
|
|
534
534
|
|
|
535
535
|
const INFRA = [
|
|
536
536
|
/^\/engram\/v1\/health$/,
|
|
537
|
+
/^\/engram\/v1\/capabilities$/,
|
|
537
538
|
/^\/engram\/v1\/authorization$/,
|
|
538
539
|
/^\/engram\/v1\/admin\//,
|
|
539
540
|
/^\/engram\/ui/,
|