@remnic/core 9.3.753 → 9.3.754
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 +26 -26
- package/dist/access-boundary.d.ts +1 -1
- package/dist/access-boundary.js +27 -27
- package/dist/access-cli.js +75 -75
- package/dist/access-http.d.ts +1 -1
- package/dist/access-http.js +34 -34
- package/dist/access-lcm-surface.d.ts +188 -0
- package/dist/access-lcm-surface.js +128 -0
- package/dist/access-lcm-surface.js.map +1 -0
- package/dist/access-mcp.d.ts +1 -1
- package/dist/access-mcp.js +31 -31
- package/dist/access-operations-batch.js +28 -28
- package/dist/access-operations.d.ts +5 -5
- package/dist/access-operations.js +30 -30
- package/dist/access-recall-surface.d.ts +1 -1
- package/dist/access-recall-surface.js +26 -26
- package/dist/access-schema.d.ts +74 -74
- package/dist/access-schema.js +6 -6
- package/dist/{access-service-MitduesD.d.ts → access-service-DKRV1zxh.d.ts} +4 -64
- package/dist/access-service.d.ts +1 -1
- package/dist/access-service.js +26 -26
- package/dist/access-surface-catalog.d.ts +1 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/{auto-sync-IEBKPGAM.js → auto-sync-G4YKBPOU.js} +5 -5
- package/dist/briefing.js +7 -7
- package/dist/calibration.js +2 -2
- package/dist/causal-consolidation.js +8 -8
- package/dist/{chunk-HAZVVOYT.js → chunk-24463PIV.js} +2 -2
- package/dist/{chunk-C3IHRWIC.js → chunk-2MHCWXX6.js} +2 -2
- package/dist/{chunk-GWGWBK5Y.js → chunk-2MR3MFQB.js} +7 -7
- package/dist/{chunk-YTU7WKYN.js → chunk-2OZ2JEUP.js} +2 -2
- package/dist/{chunk-AU7Q3LSC.js → chunk-2QSZNTDO.js} +4 -4
- package/dist/{chunk-EHYB325U.js → chunk-3DOD6ICJ.js} +2 -2
- package/dist/{chunk-KY4WOZ3P.js → chunk-4DVYRPLL.js} +42 -42
- package/dist/{chunk-VUA4S7P6.js → chunk-5J4OL7TJ.js} +4 -4
- package/dist/{chunk-EEO4YWQ7.js → chunk-7AL5WNPW.js} +2 -2
- package/dist/{chunk-BWOEYBRB.js → chunk-7CAJLUNW.js} +2 -2
- package/dist/{chunk-JES2O5Q6.js → chunk-BERWPOXU.js} +4 -4
- package/dist/{chunk-3HGZX26P.js → chunk-BQ6J6P3K.js} +8 -8
- package/dist/{chunk-XOPLMSVV.js → chunk-CJ276MDI.js} +2 -2
- package/dist/{chunk-TVISHYL5.js → chunk-COZEVVXZ.js} +2 -2
- package/dist/{chunk-ZIFPFKB2.js → chunk-CXCZ4LAI.js} +2 -2
- package/dist/{chunk-OUXPVWTZ.js → chunk-FLVL4L3L.js} +5 -5
- package/dist/{chunk-LZ73Y56P.js → chunk-G7LCG56O.js} +4 -4
- package/dist/{chunk-E3MRYDUQ.js → chunk-GAH5755F.js} +2 -2
- package/dist/{chunk-JGPURIEH.js → chunk-GPFXPC2V.js} +2 -2
- package/dist/{chunk-3ZIKDH6C.js → chunk-JBO6UBIE.js} +4 -4
- package/dist/{chunk-YNBEHUZH.js → chunk-KURUULFF.js} +2 -2
- package/dist/{chunk-ZECLLIMX.js → chunk-L4AS7TIG.js} +4998 -4950
- package/dist/chunk-L4AS7TIG.js.map +1 -0
- package/dist/{chunk-LQ6JI4VH.js → chunk-LD53WPMU.js} +4 -4
- package/dist/{chunk-QIRZI3C3.js → chunk-OH66EBCS.js} +2 -2
- package/dist/{chunk-OZMBT7N3.js → chunk-PJQMISPD.js} +2 -2
- package/dist/{chunk-PN5AMCK2.js → chunk-PYVZKEZZ.js} +53 -53
- package/dist/{chunk-DCWIQFNA.js → chunk-T422SYM6.js} +5 -5
- package/dist/{chunk-5CEJH5ZN.js → chunk-TAPSNIMA.js} +4 -4
- package/dist/{chunk-3NN73CM7.js → chunk-TH7WMHVK.js} +2 -2
- package/dist/{chunk-34THARMH.js → chunk-THZ4WR5L.js} +10 -10
- package/dist/{chunk-E6LENGYV.js → chunk-TIFF32QK.js} +2 -2
- package/dist/{chunk-RNWWAS5V.js → chunk-TJATYB3F.js} +11 -11
- package/dist/{chunk-KYHGLDFU.js → chunk-TXALJRYO.js} +6 -6
- package/dist/{chunk-DKIZ4BUQ.js → chunk-U2SMR57B.js} +7 -7
- package/dist/{chunk-VVSV2QVY.js → chunk-V4C4SFZB.js} +3 -3
- package/dist/{chunk-AXBRVBT6.js → chunk-VSU2NZGC.js} +2 -2
- package/dist/{chunk-N3VE7OCY.js → chunk-WS6GLJ7P.js} +2 -2
- package/dist/{chunk-ITLJATQI.js → chunk-WUVUTOUV.js} +2 -2
- package/dist/{chunk-WRFKZEO6.js → chunk-YQGHW5ML.js} +4 -4
- package/dist/{chunk-243P7YYV.js → chunk-YRBXBHU7.js} +2 -2
- package/dist/{chunk-ZMHDSIEX.js → chunk-ZJMSTFOR.js} +4 -4
- package/dist/{cli-DqonYBZ0.d.ts → cli-D-xw8Sp1.d.ts} +1 -1
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +55 -55
- package/dist/compounding/engine.js +7 -7
- package/dist/connectors/codex-materialize-runner.js +7 -7
- package/dist/connectors/index.js +7 -7
- package/dist/conversation-index/backend.js +2 -2
- package/dist/entity-retrieval.js +7 -7
- package/dist/extraction-faithfulness.js +3 -3
- package/dist/extraction-judge.js +3 -3
- package/dist/extraction.js +3 -3
- package/dist/fallback-llm.js +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +149 -149
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.js +4 -4
- package/dist/maintenance/memory-governance.js +7 -7
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +7 -7
- package/dist/maintenance/rebuild-memory-projection.js +8 -8
- package/dist/mcp-memory-inspector-app.d.ts +1 -1
- package/dist/namespaces/migrate.js +17 -17
- package/dist/namespaces/search.js +9 -9
- package/dist/namespaces/storage.js +7 -7
- package/dist/operator-toolkit.js +21 -21
- package/dist/orchestration/maintenance.js +9 -9
- package/dist/orchestrator.js +75 -75
- package/dist/recall-planner-llm.js +2 -2
- package/dist/schemas.d.ts +90 -90
- 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 +8 -8
- package/dist/semantic-rule-promotion.js +7 -7
- package/dist/semantic-rule-verifier.js +7 -7
- package/dist/shared-context/manager.d.ts +8 -8
- package/dist/storage.js +6 -6
- package/dist/summarizer.js +3 -3
- package/dist/transfer/autodetect.js +1 -1
- package/dist/transfer/backup.js +1 -1
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/types.d.ts +66 -66
- package/dist/verified-recall.js +7 -7
- package/package.json +2 -2
- package/src/access-lcm-surface.ts +517 -0
- package/src/access-service.ts +21 -417
- package/dist/chunk-ZECLLIMX.js.map +0 -1
- /package/dist/{auto-sync-IEBKPGAM.js.map → auto-sync-G4YKBPOU.js.map} +0 -0
- /package/dist/{chunk-HAZVVOYT.js.map → chunk-24463PIV.js.map} +0 -0
- /package/dist/{chunk-C3IHRWIC.js.map → chunk-2MHCWXX6.js.map} +0 -0
- /package/dist/{chunk-GWGWBK5Y.js.map → chunk-2MR3MFQB.js.map} +0 -0
- /package/dist/{chunk-YTU7WKYN.js.map → chunk-2OZ2JEUP.js.map} +0 -0
- /package/dist/{chunk-AU7Q3LSC.js.map → chunk-2QSZNTDO.js.map} +0 -0
- /package/dist/{chunk-EHYB325U.js.map → chunk-3DOD6ICJ.js.map} +0 -0
- /package/dist/{chunk-KY4WOZ3P.js.map → chunk-4DVYRPLL.js.map} +0 -0
- /package/dist/{chunk-VUA4S7P6.js.map → chunk-5J4OL7TJ.js.map} +0 -0
- /package/dist/{chunk-EEO4YWQ7.js.map → chunk-7AL5WNPW.js.map} +0 -0
- /package/dist/{chunk-BWOEYBRB.js.map → chunk-7CAJLUNW.js.map} +0 -0
- /package/dist/{chunk-JES2O5Q6.js.map → chunk-BERWPOXU.js.map} +0 -0
- /package/dist/{chunk-3HGZX26P.js.map → chunk-BQ6J6P3K.js.map} +0 -0
- /package/dist/{chunk-XOPLMSVV.js.map → chunk-CJ276MDI.js.map} +0 -0
- /package/dist/{chunk-TVISHYL5.js.map → chunk-COZEVVXZ.js.map} +0 -0
- /package/dist/{chunk-ZIFPFKB2.js.map → chunk-CXCZ4LAI.js.map} +0 -0
- /package/dist/{chunk-OUXPVWTZ.js.map → chunk-FLVL4L3L.js.map} +0 -0
- /package/dist/{chunk-LZ73Y56P.js.map → chunk-G7LCG56O.js.map} +0 -0
- /package/dist/{chunk-E3MRYDUQ.js.map → chunk-GAH5755F.js.map} +0 -0
- /package/dist/{chunk-JGPURIEH.js.map → chunk-GPFXPC2V.js.map} +0 -0
- /package/dist/{chunk-3ZIKDH6C.js.map → chunk-JBO6UBIE.js.map} +0 -0
- /package/dist/{chunk-YNBEHUZH.js.map → chunk-KURUULFF.js.map} +0 -0
- /package/dist/{chunk-LQ6JI4VH.js.map → chunk-LD53WPMU.js.map} +0 -0
- /package/dist/{chunk-QIRZI3C3.js.map → chunk-OH66EBCS.js.map} +0 -0
- /package/dist/{chunk-OZMBT7N3.js.map → chunk-PJQMISPD.js.map} +0 -0
- /package/dist/{chunk-PN5AMCK2.js.map → chunk-PYVZKEZZ.js.map} +0 -0
- /package/dist/{chunk-DCWIQFNA.js.map → chunk-T422SYM6.js.map} +0 -0
- /package/dist/{chunk-5CEJH5ZN.js.map → chunk-TAPSNIMA.js.map} +0 -0
- /package/dist/{chunk-3NN73CM7.js.map → chunk-TH7WMHVK.js.map} +0 -0
- /package/dist/{chunk-34THARMH.js.map → chunk-THZ4WR5L.js.map} +0 -0
- /package/dist/{chunk-E6LENGYV.js.map → chunk-TIFF32QK.js.map} +0 -0
- /package/dist/{chunk-RNWWAS5V.js.map → chunk-TJATYB3F.js.map} +0 -0
- /package/dist/{chunk-KYHGLDFU.js.map → chunk-TXALJRYO.js.map} +0 -0
- /package/dist/{chunk-DKIZ4BUQ.js.map → chunk-U2SMR57B.js.map} +0 -0
- /package/dist/{chunk-VVSV2QVY.js.map → chunk-V4C4SFZB.js.map} +0 -0
- /package/dist/{chunk-AXBRVBT6.js.map → chunk-VSU2NZGC.js.map} +0 -0
- /package/dist/{chunk-N3VE7OCY.js.map → chunk-WS6GLJ7P.js.map} +0 -0
- /package/dist/{chunk-ITLJATQI.js.map → chunk-WUVUTOUV.js.map} +0 -0
- /package/dist/{chunk-WRFKZEO6.js.map → chunk-YQGHW5ML.js.map} +0 -0
- /package/dist/{chunk-243P7YYV.js.map → chunk-YRBXBHU7.js.map} +0 -0
- /package/dist/{chunk-ZMHDSIEX.js.map → chunk-ZJMSTFOR.js.map} +0 -0
package/src/access-service.ts
CHANGED
|
@@ -251,6 +251,7 @@ import {
|
|
|
251
251
|
import { formatProfileTraceAscii } from "./profiling.js";
|
|
252
252
|
import { resolveAccessSetupCapabilities, resolveGraphConstructionCapabilities, resolveIndexingCapabilities } from "./capabilities.js";
|
|
253
253
|
import { resolveRecallEnhancementCapabilities } from "./capabilities.js";
|
|
254
|
+
import { AccessLcmSurface } from "./access-lcm-surface.js";
|
|
254
255
|
import { AccessAdminOpsSurface } from "./access-admin-ops-surface.js";
|
|
255
256
|
import { AccessRecallSurface } from "./access-recall-surface.js";
|
|
256
257
|
import { selfDeps } from "./orchestration/self-deps.js";
|
|
@@ -1308,6 +1309,18 @@ export class EngramAccessService {
|
|
|
1308
1309
|
private readonly budget: CrossNamespaceBudget;
|
|
1309
1310
|
private readonly auditAdapter: AccessAuditAdapter | null;
|
|
1310
1311
|
|
|
1312
|
+
/** AccessLcmSurface (access-service decomposition). Lazy; selfDeps live wiring. */
|
|
1313
|
+
private _accessLcmSurface: AccessLcmSurface | undefined;
|
|
1314
|
+
|
|
1315
|
+
private get accessLcmSurface(): AccessLcmSurface {
|
|
1316
|
+
if (!this._accessLcmSurface) {
|
|
1317
|
+
this._accessLcmSurface = new AccessLcmSurface(
|
|
1318
|
+
selfDeps<ConstructorParameters<typeof AccessLcmSurface>[0]>(this),
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
return this._accessLcmSurface;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1311
1324
|
/** AccessAdminOpsSurface (access-service decomposition). Lazy; selfDeps live wiring. */
|
|
1312
1325
|
private _accessAdminOpsSurface: AccessAdminOpsSurface | undefined;
|
|
1313
1326
|
|
|
@@ -4603,212 +4616,9 @@ export class EngramAccessService {
|
|
|
4603
4616
|
}
|
|
4604
4617
|
|
|
4605
4618
|
async lcmSearch(request: EngramAccessLcmSearchRequest): Promise<EngramAccessLcmSearchResponse> {
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
}
|
|
4609
|
-
|
|
4610
|
-
const principal = this.resolveRequestPrincipal(request.sessionKey, request.authenticatedPrincipal);
|
|
4611
|
-
const hasExplicitNamespace =
|
|
4612
|
-
typeof request.namespace === "string" &&
|
|
4613
|
-
request.namespace.trim().length > 0;
|
|
4614
|
-
// Resolve the readable base namespace WITHOUT pre-authorizing `default`
|
|
4615
|
-
// (#1505 thread NBHWz). An EXPLICIT namespace is still authorized strictly
|
|
4616
|
-
// via `resolveReadableNamespace` (explicit reads must pass the ACL — throws
|
|
4617
|
-
// on an unreadable explicit namespace). For an IMPLICIT read, derive the
|
|
4618
|
-
// fallback from the ALREADY read-authorized recall namespace set instead of
|
|
4619
|
-
// read-authorizing `config.defaultNamespace`: under a restrictive `default`
|
|
4620
|
-
// READ policy where the principal's self namespace is readable, normal recall
|
|
4621
|
-
// still succeeds via `recallNamespacesForPrincipal`, so `lcmSearch` must too
|
|
4622
|
-
// (the same defect class the raw-excerpt path fixes). `undefined` ⇒ no
|
|
4623
|
-
// readable LCM namespace exists, so return NO rows rather than throwing.
|
|
4624
|
-
const profileLcmReadNamespaces = hasExplicitNamespace
|
|
4625
|
-
? null
|
|
4626
|
-
: this.resolveScopeProfileLcmReadNamespaces(
|
|
4627
|
-
request.sessionKey,
|
|
4628
|
-
request.authenticatedPrincipal,
|
|
4629
|
-
);
|
|
4630
|
-
const namespace = hasExplicitNamespace
|
|
4631
|
-
? this.resolveReadableNamespace(request.namespace, principal)
|
|
4632
|
-
: profileLcmReadNamespaces !== null
|
|
4633
|
-
? profileLcmReadNamespaces[0]
|
|
4634
|
-
: this.resolveImplicitLcmReadFallbackNamespace(principal);
|
|
4635
|
-
|
|
4636
|
-
if (!this.orchestrator.lcmEngine || !this.orchestrator.lcmEngine.enabled) {
|
|
4637
|
-
return {
|
|
4638
|
-
query: request.query,
|
|
4639
|
-
namespace: namespace ?? this.orchestrator.config.defaultNamespace,
|
|
4640
|
-
results: [],
|
|
4641
|
-
count: 0,
|
|
4642
|
-
lcmEnabled: false,
|
|
4643
|
-
};
|
|
4644
|
-
}
|
|
4645
|
-
|
|
4646
|
-
// An active scope profile with no readable layers is authoritative: search
|
|
4647
|
-
// no legacy/default LCM keys instead of falling back around the profile.
|
|
4648
|
-
if (profileLcmReadNamespaces !== null && profileLcmReadNamespaces.length === 0) {
|
|
4649
|
-
return {
|
|
4650
|
-
query: request.query,
|
|
4651
|
-
namespace: this.orchestrator.config.defaultNamespace,
|
|
4652
|
-
results: [],
|
|
4653
|
-
count: 0,
|
|
4654
|
-
lcmEnabled: true,
|
|
4655
|
-
};
|
|
4656
|
-
}
|
|
4657
|
-
|
|
4658
|
-
// No readable LCM namespace for an IMPLICIT read (restrictive `default` READ
|
|
4659
|
-
// policy, no readable overlay/self) ⇒ return NO rows instead of pre-
|
|
4660
|
-
// authorizing the denied default (#1505 thread NBHWz). Normal recall still
|
|
4661
|
-
// succeeds through the readable self namespace; LCM search degrades to empty.
|
|
4662
|
-
if (namespace === undefined) {
|
|
4663
|
-
return {
|
|
4664
|
-
query: request.query,
|
|
4665
|
-
namespace: this.orchestrator.config.defaultNamespace,
|
|
4666
|
-
results: [],
|
|
4667
|
-
count: 0,
|
|
4668
|
-
lcmEnabled: true,
|
|
4669
|
-
};
|
|
4670
|
-
}
|
|
4671
|
-
|
|
4672
|
-
const limit = Math.max(1, Math.min(request.limit ?? 10, 100));
|
|
4673
|
-
// Route the LCM read session_id AND prefix through the SAME overlay-aware
|
|
4674
|
-
// namespace `observe`'s write key and compaction use (#1505 round 3). A
|
|
4675
|
-
// project-scoped `observe` with no explicit namespace archived under the
|
|
4676
|
-
// coding-overlay namespace; deriving the prefix only from the readable
|
|
4677
|
-
// `namespace` (as before) would search the raw key and miss those turns.
|
|
4678
|
-
// The effective namespace is resolved ONCE from the real session
|
|
4679
|
-
// (`request.sessionKey`) — the prefix is a search fragment with no bound
|
|
4680
|
-
// coding context, so it inherits the same namespace. Collapses to the raw key
|
|
4681
|
-
// for single-store / no-overlay / explicit-default flows (existing behavior).
|
|
4682
|
-
const lcmReadNamespace = profileLcmReadNamespaces !== null
|
|
4683
|
-
? profileLcmReadNamespaces[0] ?? this.orchestrator.config.defaultNamespace
|
|
4684
|
-
: this.resolveLcmReadNamespace(
|
|
4685
|
-
request.namespace,
|
|
4686
|
-
namespace,
|
|
4687
|
-
request.sessionKey,
|
|
4688
|
-
request.authenticatedPrincipal,
|
|
4689
|
-
);
|
|
4690
|
-
// Ordered, read-authorized LCM read key SET for a concrete `sessionKey`
|
|
4691
|
-
// (#1505 fallback unification + #1501 scope profiles). A branch-scoped
|
|
4692
|
-
// session whose rows were archived at project/root scope is found by querying
|
|
4693
|
-
// the primary overlay key first, then each fallback. When a scope profile is
|
|
4694
|
-
// active, the profile's expanded read namespace set is authoritative,
|
|
4695
|
-
// including the empty set.
|
|
4696
|
-
const lcmSessionKeyIds = request.sessionKey
|
|
4697
|
-
? profileLcmReadNamespaces !== null
|
|
4698
|
-
? this.lcmSessionIdsForNamespaces(
|
|
4699
|
-
profileLcmReadNamespaces,
|
|
4700
|
-
request.sessionKey,
|
|
4701
|
-
)
|
|
4702
|
-
: this.resolveLcmReadSessionIds(
|
|
4703
|
-
request.namespace,
|
|
4704
|
-
namespace,
|
|
4705
|
-
request.sessionKey,
|
|
4706
|
-
request.authenticatedPrincipal,
|
|
4707
|
-
)
|
|
4708
|
-
: [undefined];
|
|
4709
|
-
const lcmSessionPrefixes = request.sessionPrefix
|
|
4710
|
-
? profileLcmReadNamespaces !== null && !request.sessionKey
|
|
4711
|
-
? this.lcmSessionIdsForNamespaces(
|
|
4712
|
-
profileLcmReadNamespaces,
|
|
4713
|
-
request.sessionPrefix,
|
|
4714
|
-
)
|
|
4715
|
-
: [
|
|
4716
|
-
lcmSessionKeyForNamespace(
|
|
4717
|
-
lcmReadNamespace,
|
|
4718
|
-
request.sessionPrefix,
|
|
4719
|
-
this.orchestrator.config.defaultNamespace,
|
|
4720
|
-
) ?? request.sessionPrefix,
|
|
4721
|
-
]
|
|
4722
|
-
: [undefined];
|
|
4723
|
-
// SECURITY (#1495 P1 + codex P1 r2 "Require a scoped LCM filter before
|
|
4724
|
-
// archive searches"): a sessionless, prefixless `lcmSearch` issues
|
|
4725
|
-
// `searchContextFull(query, limit, undefined, undefined)`, an archive-wide
|
|
4726
|
-
// FTS scan over EVERY `session_id`, including the sentinel-framed
|
|
4727
|
-
// `<ns>`-scoped overlay/tenant rows. The LCM archive is keyed by the
|
|
4728
|
-
// `session_id` STRING and is NOT partitioned by namespace, so an unscoped
|
|
4729
|
-
// scan CANNOT be constrained to the caller's authorized namespace — neither
|
|
4730
|
-
// an explicit `namespace` nor a readable `default` confines its results to
|
|
4731
|
-
// rows the caller may read. The scan is therefore safe ONLY in single-store
|
|
4732
|
-
// mode (namespaces disabled, one shared archive owned by the caller). When
|
|
4733
|
-
// namespaces are ENABLED, an unscoped `lcmSearch` (no `sessionKey` AND no
|
|
4734
|
-
// `sessionPrefix`) must be SUPPRESSED — return EMPTY — regardless of an
|
|
4735
|
-
// explicit namespace or default-readability, so a caller authorized for
|
|
4736
|
-
// `default` (or for one explicit namespace) cannot read other namespaces'
|
|
4737
|
-
// transcript rows via the archive-wide scan (cross-tenant read leak). A
|
|
4738
|
-
// SCOPED call (sessionKey or sessionPrefix present) is unaffected: it carries
|
|
4739
|
-
// a namespace-framed `session_id` / prefix filter that already constrains the
|
|
4740
|
-
// search to the caller's authorized, read-gated namespace.
|
|
4741
|
-
const hasScopedSession =
|
|
4742
|
-
(typeof request.sessionKey === "string" &&
|
|
4743
|
-
request.sessionKey.length > 0) ||
|
|
4744
|
-
lcmSessionPrefixes.some((prefix) => typeof prefix === "string" && prefix.length > 0);
|
|
4745
|
-
if (!hasScopedSession && resolveNamespaceCapabilities(this.orchestrator.config).namespaces === true) {
|
|
4746
|
-
return {
|
|
4747
|
-
query: request.query,
|
|
4748
|
-
namespace,
|
|
4749
|
-
results: [],
|
|
4750
|
-
count: 0,
|
|
4751
|
-
lcmEnabled: true,
|
|
4752
|
-
};
|
|
4753
|
-
}
|
|
4754
|
-
// Query each LCM read key in order, merging + deduping rows (by
|
|
4755
|
-
// sessionId+turnIndex) and preserving first-seen order, capped at `limit`.
|
|
4756
|
-
// Use allSettled so one corrupt/failed namespace key cannot discard sibling
|
|
4757
|
-
// results from other authorized profile namespaces.
|
|
4758
|
-
const seenRows = new Set<string>();
|
|
4759
|
-
const results: Array<{ sessionId: string; content: string; turnIndex: number }> = [];
|
|
4760
|
-
const lcmSearches: Array<{
|
|
4761
|
-
key: string | undefined;
|
|
4762
|
-
prefix: string | undefined;
|
|
4763
|
-
promise: Promise<Array<{ session_id: string; content: string; turn_index: number }>>;
|
|
4764
|
-
}> = [];
|
|
4765
|
-
for (const lcmSessionKey of lcmSessionKeyIds) {
|
|
4766
|
-
for (const lcmSessionPrefix of lcmSessionPrefixes) {
|
|
4767
|
-
lcmSearches.push({
|
|
4768
|
-
key: lcmSessionKey,
|
|
4769
|
-
prefix: lcmSessionPrefix,
|
|
4770
|
-
promise: this.orchestrator.lcmEngine.searchContextFull(
|
|
4771
|
-
request.query,
|
|
4772
|
-
limit,
|
|
4773
|
-
lcmSessionKey,
|
|
4774
|
-
lcmSessionPrefix,
|
|
4775
|
-
) as Promise<Array<{ session_id: string; content: string; turn_index: number }>>,
|
|
4776
|
-
});
|
|
4777
|
-
}
|
|
4778
|
-
}
|
|
4779
|
-
const settledSearches = await Promise.allSettled(
|
|
4780
|
-
lcmSearches.map((search) => search.promise),
|
|
4619
|
+
return this.accessLcmSurface.lcmSearch(
|
|
4620
|
+
request,
|
|
4781
4621
|
);
|
|
4782
|
-
for (let i = 0; i < settledSearches.length; i += 1) {
|
|
4783
|
-
if (results.length >= limit) break;
|
|
4784
|
-
const settled = settledSearches[i];
|
|
4785
|
-
if (!settled || settled.status === "rejected") {
|
|
4786
|
-
const failed = lcmSearches[i];
|
|
4787
|
-
log.warn(
|
|
4788
|
-
`lcmSearch: failed for key=${failed?.key ?? "<none>"} prefix=${failed?.prefix ?? "<none>"}: ${settled?.status === "rejected" ? settled.reason : "missing result"}`,
|
|
4789
|
-
);
|
|
4790
|
-
continue;
|
|
4791
|
-
}
|
|
4792
|
-
for (const r of settled.value) {
|
|
4793
|
-
const dedupeKey = `${r.session_id}\0${r.turn_index}`;
|
|
4794
|
-
if (seenRows.has(dedupeKey)) continue;
|
|
4795
|
-
seenRows.add(dedupeKey);
|
|
4796
|
-
results.push({
|
|
4797
|
-
sessionId: r.session_id,
|
|
4798
|
-
content: r.content,
|
|
4799
|
-
turnIndex: r.turn_index,
|
|
4800
|
-
});
|
|
4801
|
-
if (results.length >= limit) break;
|
|
4802
|
-
}
|
|
4803
|
-
}
|
|
4804
|
-
|
|
4805
|
-
return {
|
|
4806
|
-
query: request.query,
|
|
4807
|
-
namespace,
|
|
4808
|
-
results,
|
|
4809
|
-
count: results.length,
|
|
4810
|
-
lcmEnabled: true,
|
|
4811
|
-
};
|
|
4812
4622
|
}
|
|
4813
4623
|
|
|
4814
4624
|
/**
|
|
@@ -4833,38 +4643,6 @@ export class EngramAccessService {
|
|
|
4833
4643
|
* so the read key is byte-for-byte what the LCM write and the recall readers
|
|
4834
4644
|
* use.
|
|
4835
4645
|
*/
|
|
4836
|
-
/**
|
|
4837
|
-
* Resolve the effective LCM NAMESPACE a same-session operation must prefix
|
|
4838
|
-
* with (the namespace half of {@link resolveLcmReadSessionKey}). Split out so
|
|
4839
|
-
* `lcmSearch` can apply ONE namespace to BOTH its `sessionKey` and its
|
|
4840
|
-
* `sessionPrefix` — the prefix is a search fragment, not a real session, so its
|
|
4841
|
-
* own coding context can't be looked up; it must inherit the namespace resolved
|
|
4842
|
-
* from the real session (`sessionKeyForOverlay`).
|
|
4843
|
-
*
|
|
4844
|
-
* `purpose` selects the AUTHORIZATION gate applied before honouring the
|
|
4845
|
-
* coding overlay (#1505 round 3 + round 4, codex P2):
|
|
4846
|
-
*
|
|
4847
|
-
* - `"read"` (`lcmSearch` / raw-excerpt recall): the overlay rows are only
|
|
4848
|
-
* visible when the principal SELF base is in the READABLE RECALL SET — the
|
|
4849
|
-
* same gate the orchestrator's `lcmReadNamespaceForSession` and the recall
|
|
4850
|
-
* namespace set use (`recallNamespacesForPrincipal`, gated by both
|
|
4851
|
-
* `defaultRecallNamespaces.includes("self")` AND `canReadNamespace`). A
|
|
4852
|
-
* caller that passed the default read check must NOT receive
|
|
4853
|
-
* `<principal>-project-*` rows the policy never granted (cross-tenant read
|
|
4854
|
-
* leak). When the self base is not readable, keep the just-authorized
|
|
4855
|
-
* namespace (collapses to the raw key on the default store).
|
|
4856
|
-
*
|
|
4857
|
-
* - `"write"` (`lcmCompactionFlush` / `lcmCompactionRecord`): these are
|
|
4858
|
-
* write/maintenance operations on the SAME queue `observe` just wrote, so
|
|
4859
|
-
* the gate must mirror observe's WRITE authorization (`canWriteNamespace`
|
|
4860
|
-
* on the self base), NOT readability. A principal that can WRITE but not
|
|
4861
|
-
* READ its self namespace (or whose `defaultRecallNamespaces` omits `self`)
|
|
4862
|
-
* archived under the overlay key via `observe`; gating compaction by
|
|
4863
|
-
* readability would fall back to the default/raw key and leave that queue
|
|
4864
|
-
* never flushed/recorded (round-4 codex P2). Write-authorized ⇒ overlay
|
|
4865
|
-
* key, matching the observe write key (rule 42 read/write parity; rule 39
|
|
4866
|
-
* identical gates across paths).
|
|
4867
|
-
*/
|
|
4868
4646
|
private resolveLcmReadNamespace(
|
|
4869
4647
|
explicitNamespace: string | undefined,
|
|
4870
4648
|
resolvedNamespace: string,
|
|
@@ -4872,134 +4650,24 @@ export class EngramAccessService {
|
|
|
4872
4650
|
authenticatedPrincipal: string | undefined,
|
|
4873
4651
|
purpose: "read" | "write" = "read",
|
|
4874
4652
|
): string {
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
// Mirror observe's write resolution: use the coding-overlay namespace when
|
|
4879
|
-
// one applies, else the default store. NOT the principal self base — an
|
|
4880
|
-
// unqualified observe archives under the default store, so a self-base prefix
|
|
4881
|
-
// here would target a queue observe never wrote to (#1495).
|
|
4882
|
-
const principal = this.resolveRequestPrincipal(
|
|
4653
|
+
return this.accessLcmSurface.resolveLcmReadNamespace(
|
|
4654
|
+
explicitNamespace,
|
|
4655
|
+
resolvedNamespace,
|
|
4883
4656
|
sessionKeyForOverlay,
|
|
4884
4657
|
authenticatedPrincipal,
|
|
4658
|
+
purpose,
|
|
4885
4659
|
);
|
|
4886
|
-
const base = defaultNamespaceForPrincipal(principal, this.orchestrator.config);
|
|
4887
|
-
const overlaid = this.orchestrator.applyCodingNamespaceOverlay(
|
|
4888
|
-
sessionKeyForOverlay,
|
|
4889
|
-
base,
|
|
4890
|
-
);
|
|
4891
|
-
// No overlay → the default store (raw sessionKey), as before.
|
|
4892
|
-
if (overlaid === base) return this.orchestrator.config.defaultNamespace;
|
|
4893
|
-
// Overlay applied. Authorize access to the principal's `<principal>-project-*`
|
|
4894
|
-
// overlay base before switching the LCM key to it. The gate differs by
|
|
4895
|
-
// operation purpose (see the doc comment): reads use the readable-recall-set
|
|
4896
|
-
// gate (no cross-tenant read leak), writes use observe's write authorization
|
|
4897
|
-
// (so compaction targets the same overlay queue observe wrote to).
|
|
4898
|
-
const authorized =
|
|
4899
|
-
purpose === "write"
|
|
4900
|
-
? canWriteNamespace(principal, base, this.orchestrator.config)
|
|
4901
|
-
: recallNamespacesForPrincipal(
|
|
4902
|
-
principal,
|
|
4903
|
-
this.orchestrator.config,
|
|
4904
|
-
).includes(base);
|
|
4905
|
-
if (authorized) return overlaid;
|
|
4906
|
-
// Unauthorized overlay base. For READS, collapse to the DEFAULT STORE (the
|
|
4907
|
-
// raw sessionKey) EXACTLY like the orchestrator's `lcmReadNamespaceForSession`
|
|
4908
|
-
// (rule 39 / 42) — NOT the caller's `resolvedNamespace`, which for an implicit
|
|
4909
|
-
// read can be a readable recall namespace (e.g. `shared`). Returning that
|
|
4910
|
-
// would prefix LCM reads with `shared:sessionKey` while in-prompt recall uses
|
|
4911
|
-
// the raw `sessionKey`, diverging `lcmSearch`/raw disclosure from orchestrator
|
|
4912
|
-
// LCM reads (cursor "LCM read gate wrong fallback"). For an explicit read the
|
|
4913
|
-
// method already returned at the top, so this only affects implicit reads.
|
|
4914
|
-
// The (currently unused) write purpose preserves its prior `resolvedNamespace`
|
|
4915
|
-
// fallback for backward compatibility.
|
|
4916
|
-
if (purpose === "read") return this.orchestrator.config.defaultNamespace;
|
|
4917
|
-
return resolvedNamespace;
|
|
4918
4660
|
}
|
|
4919
4661
|
|
|
4920
|
-
/**
|
|
4921
|
-
* Resolve the namespace the raw-disclosure excerpt lookup
|
|
4922
|
-
* ({@link fetchRawExcerpts}) must prefix its LCM `session_id` with (#1505
|
|
4923
|
-
* thread 2f7). Raw disclosure reads the SAME LCM archive `lcmSearch` and the
|
|
4924
|
-
* in-prompt LCM sections read, so it MUST pass through the identical
|
|
4925
|
-
* read-authorization gate — NOT `snapshot.namespace`, which records the
|
|
4926
|
-
* effective WRITE/overlay namespace (`<principal>-project-*`) even when the
|
|
4927
|
-
* principal can WRITE but not READ its self base (or `defaultRecallNamespaces`
|
|
4928
|
-
* omits `self`). Routing through `resolveLcmReadNamespace(..., "read")` makes
|
|
4929
|
-
* raw disclosure fall back to the default store exactly like normal recall +
|
|
4930
|
-
* `lcmSearch`, so it never attaches overlay transcript rows the read gate
|
|
4931
|
-
* excludes (cross-tenant read leak). Collapses to the default store / raw
|
|
4932
|
-
* sessionKey for single-store / no-overlay / explicit-default flows, so
|
|
4933
|
-
* single-user recall is byte-for-byte unchanged.
|
|
4934
|
-
*
|
|
4935
|
-
* Returns `undefined` when NO readable LCM namespace exists for an IMPLICIT
|
|
4936
|
-
* (no explicit `namespace`) raw recall — i.e. a restrictive `default` READ
|
|
4937
|
-
* policy denies the principal `default` AND no overlay/self namespace is
|
|
4938
|
-
* readable. In that case the caller emits NO excerpts rather than throwing
|
|
4939
|
-
* `namespace is not readable: default` (#1505 thread NBHWz): normal recall
|
|
4940
|
-
* still succeeds via `recallNamespacesForPrincipal`, so `disclosure: "raw"`
|
|
4941
|
-
* must degrade gracefully (empty excerpts), never pre-authorize `default`.
|
|
4942
|
-
*
|
|
4943
|
-
* IMPLICIT-namespace fallback selection derives from the ALREADY
|
|
4944
|
-
* read-authorized recall namespace set (`recallNamespacesForPrincipal` +
|
|
4945
|
-
* `canReadNamespace`) — the principal's self base when it is in the readable
|
|
4946
|
-
* recall set, else `config.defaultNamespace` ONLY when the principal may read
|
|
4947
|
-
* it. It NEVER pre-authorizes `default`. An EXPLICIT `namespace` is still
|
|
4948
|
-
* authorized strictly via `resolveReadableNamespace` (explicit reads must pass
|
|
4949
|
-
* the ACL — no behavior change).
|
|
4950
|
-
*/
|
|
4951
4662
|
private resolveRawExcerptReadNamespace(
|
|
4952
4663
|
explicitNamespace: string | undefined,
|
|
4953
4664
|
sessionKey: string | undefined,
|
|
4954
4665
|
authenticatedPrincipal: string | undefined,
|
|
4955
4666
|
): string | undefined {
|
|
4956
|
-
|
|
4957
|
-
sessionKey,
|
|
4958
|
-
authenticatedPrincipal,
|
|
4959
|
-
);
|
|
4960
|
-
const hasExplicitNamespace =
|
|
4961
|
-
typeof explicitNamespace === "string" &&
|
|
4962
|
-
explicitNamespace.trim().length > 0;
|
|
4963
|
-
if (hasExplicitNamespace) {
|
|
4964
|
-
// Explicit reads must pass the ACL — authorize strictly, exactly as
|
|
4965
|
-
// `lcmSearch` does (throws on an unreadable explicit namespace).
|
|
4966
|
-
const resolvedNamespace = this.resolveReadableNamespace(
|
|
4967
|
-
explicitNamespace,
|
|
4968
|
-
principal,
|
|
4969
|
-
);
|
|
4970
|
-
return this.resolveLcmReadNamespace(
|
|
4971
|
-
explicitNamespace,
|
|
4972
|
-
resolvedNamespace,
|
|
4973
|
-
sessionKey,
|
|
4974
|
-
authenticatedPrincipal,
|
|
4975
|
-
"read",
|
|
4976
|
-
);
|
|
4977
|
-
}
|
|
4978
|
-
// IMPLICIT raw recall: an active scope profile owns the same LCM read
|
|
4979
|
-
// namespace set used by recall and lcmSearch. Return the first profile
|
|
4980
|
-
// namespace as the legacy raw-excerpt namespace hint; the concrete ordered
|
|
4981
|
-
// key set is still produced by resolveLcmReadSessionIds(), which also treats
|
|
4982
|
-
// an empty profile read set as authoritative.
|
|
4983
|
-
const profileReadNamespaces = this.resolveScopeProfileLcmReadNamespaces(
|
|
4984
|
-
sessionKey,
|
|
4985
|
-
authenticatedPrincipal,
|
|
4986
|
-
);
|
|
4987
|
-
if (profileReadNamespaces !== null) return profileReadNamespaces[0];
|
|
4988
|
-
|
|
4989
|
-
// Otherwise derive the read fallback from the ALREADY read-authorized recall
|
|
4990
|
-
// namespace set — NEVER pre-authorize `default` (#1505 thread NBHWz). When
|
|
4991
|
-
// namespaces are disabled the default store is the only namespace and is
|
|
4992
|
-
// always readable (byte-for-byte single-user path).
|
|
4993
|
-
const fallbackNamespace =
|
|
4994
|
-
this.resolveImplicitLcmReadFallbackNamespace(principal);
|
|
4995
|
-
// No readable LCM namespace at all ⇒ no excerpts (caller short-circuits).
|
|
4996
|
-
if (fallbackNamespace === undefined) return undefined;
|
|
4997
|
-
return this.resolveLcmReadNamespace(
|
|
4667
|
+
return this.accessLcmSurface.resolveRawExcerptReadNamespace(
|
|
4998
4668
|
explicitNamespace,
|
|
4999
|
-
fallbackNamespace,
|
|
5000
4669
|
sessionKey,
|
|
5001
4670
|
authenticatedPrincipal,
|
|
5002
|
-
"read",
|
|
5003
4671
|
);
|
|
5004
4672
|
}
|
|
5005
4673
|
|
|
@@ -5175,76 +4843,12 @@ export class EngramAccessService {
|
|
|
5175
4843
|
sessionKey: string,
|
|
5176
4844
|
authenticatedPrincipal: string | undefined,
|
|
5177
4845
|
): string[] {
|
|
5178
|
-
|
|
4846
|
+
return this.accessLcmSurface.resolveLcmReadSessionIds(
|
|
5179
4847
|
explicitNamespace,
|
|
5180
4848
|
resolvedNamespace,
|
|
5181
4849
|
sessionKey,
|
|
5182
4850
|
authenticatedPrincipal,
|
|
5183
|
-
"read",
|
|
5184
|
-
);
|
|
5185
|
-
const hasExplicitNamespace =
|
|
5186
|
-
typeof explicitNamespace === "string" &&
|
|
5187
|
-
explicitNamespace.trim().length > 0;
|
|
5188
|
-
// Explicit namespace → no overlay fallbacks (the overlay never applies to an
|
|
5189
|
-
// explicit read). Single key, unchanged.
|
|
5190
|
-
if (hasExplicitNamespace) return [primary];
|
|
5191
|
-
|
|
5192
|
-
const profileReadNamespaces = this.resolveScopeProfileLcmReadNamespaces(
|
|
5193
|
-
sessionKey,
|
|
5194
|
-
authenticatedPrincipal,
|
|
5195
|
-
);
|
|
5196
|
-
if (profileReadNamespaces !== null) {
|
|
5197
|
-
return this.lcmSessionIdsForNamespaces(profileReadNamespaces, sessionKey);
|
|
5198
|
-
}
|
|
5199
|
-
|
|
5200
|
-
const principal = this.resolveRequestPrincipal(
|
|
5201
|
-
sessionKey,
|
|
5202
|
-
authenticatedPrincipal,
|
|
5203
|
-
);
|
|
5204
|
-
const base = defaultNamespaceForPrincipal(
|
|
5205
|
-
principal,
|
|
5206
|
-
this.orchestrator.config,
|
|
5207
|
-
);
|
|
5208
|
-
const overlaid = this.orchestrator.applyCodingNamespaceOverlay(
|
|
5209
|
-
sessionKey,
|
|
5210
|
-
base,
|
|
5211
|
-
);
|
|
5212
|
-
// No overlay → single default-store key, unchanged.
|
|
5213
|
-
if (overlaid === base) return [primary];
|
|
5214
|
-
// Overlay present but self base unreadable → the "read" gate already
|
|
5215
|
-
// collapsed `primary` to the default store; do NOT add overlay fallbacks
|
|
5216
|
-
// (they would be unauthorized `<principal>-project-*` keys). Single key.
|
|
5217
|
-
const selfReadableInRecall = recallNamespacesForPrincipal(
|
|
5218
|
-
principal,
|
|
5219
|
-
this.orchestrator.config,
|
|
5220
|
-
).includes(base);
|
|
5221
|
-
if (!selfReadableInRecall) return [primary];
|
|
5222
|
-
// Self base readable → overlay rows authorized. Append one LCM key per coding
|
|
5223
|
-
// read fallback (project → root), combined with the principal base for
|
|
5224
|
-
// isolation — the SAME ordered set the orchestrator recall path searches.
|
|
5225
|
-
const overlay = resolveCodingNamespaceOverlay(
|
|
5226
|
-
this.orchestrator.getCodingContextForSession(sessionKey),
|
|
5227
|
-
this.orchestrator.config.codingMode,
|
|
5228
|
-
this.orchestrator.config.defaultNamespace,
|
|
5229
4851
|
);
|
|
5230
|
-
const fallbackNamespaces = (overlay?.readFallbacks ?? []).map((fallback) =>
|
|
5231
|
-
combineNamespaces(base, fallback),
|
|
5232
|
-
);
|
|
5233
|
-
const out = [primary];
|
|
5234
|
-
const seen = new Set<string>([primary]);
|
|
5235
|
-
for (const ns of fallbackNamespaces) {
|
|
5236
|
-
const key =
|
|
5237
|
-
lcmSessionKeyForNamespace(
|
|
5238
|
-
ns,
|
|
5239
|
-
sessionKey,
|
|
5240
|
-
this.orchestrator.config.defaultNamespace,
|
|
5241
|
-
) ?? sessionKey;
|
|
5242
|
-
if (!seen.has(key)) {
|
|
5243
|
-
seen.add(key);
|
|
5244
|
-
out.push(key);
|
|
5245
|
-
}
|
|
5246
|
-
}
|
|
5247
|
-
return out;
|
|
5248
4852
|
}
|
|
5249
4853
|
|
|
5250
4854
|
async lcmCompactionFlush(
|