@remnic/core 9.3.755 → 9.3.756
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 +34 -34
- package/dist/access-boundary.d.ts +1 -1
- package/dist/access-boundary.js +35 -35
- package/dist/access-cli.js +83 -83
- package/dist/access-http.d.ts +1 -1
- package/dist/access-http.js +44 -44
- package/dist/access-lcm-surface.d.ts +1 -1
- package/dist/access-lcm-surface.js +34 -34
- package/dist/access-mcp.d.ts +1 -1
- package/dist/access-mcp.js +41 -41
- package/dist/access-observe-write-surface.d.ts +225 -0
- package/dist/access-observe-write-surface.js +128 -0
- package/dist/access-operations-batch.js +38 -38
- package/dist/access-operations.d.ts +1 -1
- package/dist/access-operations.js +40 -40
- package/dist/access-recall-surface.d.ts +1 -1
- package/dist/access-recall-surface.js +34 -34
- package/dist/access-schema.d.ts +2 -2
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-C3jkLi2U.d.ts → access-service-CJwcDoQp.d.ts} +4 -5
- package/dist/access-service.d.ts +1 -1
- package/dist/access-service.js +34 -34
- package/dist/access-surface-catalog.d.ts +1 -1
- package/dist/active-recall.js +1 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/{auto-sync-G4YKBPOU.js → auto-sync-RZO5ZPHR.js} +3 -3
- package/dist/briefing.js +7 -7
- package/dist/{capsule-crypto-CZJSLEFG.js → capsule-crypto-FFWJ4X4A.js} +2 -2
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.js +15 -15
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-M5QKGHCR.js → chunk-3I2IASNK.js} +2 -2
- package/dist/{chunk-OCFITZRV.js → chunk-4AC57RCM.js} +4 -4
- package/dist/{chunk-3DOD6ICJ.js → chunk-4I45UZ4V.js} +5 -5
- package/dist/{chunk-TAPSNIMA.js → chunk-5CEJH5ZN.js} +4 -4
- package/dist/{chunk-I6IAJCJS.js → chunk-5VWRYFQE.js} +7 -7
- package/dist/{chunk-4LQUYBZM.js → chunk-5ZKT7WTR.js} +7 -7
- package/dist/{chunk-VL5QHTJI.js → chunk-6DA5BBLS.js} +117 -117
- package/dist/{chunk-LFKEFIJA.js → chunk-6DXXIXED.js} +4 -4
- package/dist/{chunk-XBM5DG4O.js → chunk-7APVFQ34.js} +680 -595
- package/dist/chunk-7APVFQ34.js.map +1 -0
- package/dist/{chunk-NCNOYM6F.js → chunk-7CFGXWYX.js} +2 -2
- package/dist/{chunk-TJATYB3F.js → chunk-A6XCHI53.js} +5 -5
- package/dist/{chunk-UQJ6YTKR.js → chunk-BTH6MQJT.js} +7 -7
- package/dist/{chunk-G7LCG56O.js → chunk-CXW6Y7SI.js} +4 -4
- package/dist/{chunk-GCEV6SXP.js → chunk-DWDDQLSZ.js} +2298 -2218
- package/dist/chunk-DWDDQLSZ.js.map +1 -0
- package/dist/{chunk-DS4QM6I7.js → chunk-EEKDRAWX.js} +4 -4
- package/dist/{chunk-I3UWEZTK.js → chunk-EF3VFQC4.js} +2 -2
- package/dist/{chunk-437S3G37.js → chunk-EJJQDP3H.js} +1 -1
- package/dist/{chunk-FQLM6ZUP.js → chunk-EVVLGYHR.js} +3 -3
- package/dist/{chunk-GGYUR4M2.js → chunk-GBXP62CL.js} +10 -10
- package/dist/{chunk-N4AGSRVA.js → chunk-GG6G2RYR.js} +9 -9
- package/dist/{chunk-JITAVROY.js → chunk-H2T4OJNI.js} +4 -4
- package/dist/{chunk-2MR3MFQB.js → chunk-JTYM4HPA.js} +2 -2
- package/dist/{chunk-LD53WPMU.js → chunk-LQ6JI4VH.js} +4 -4
- package/dist/{chunk-GPFXPC2V.js → chunk-NG2NHB4C.js} +7 -7
- package/dist/{chunk-CIDGUFAL.js → chunk-NLXKSM46.js} +2 -2
- package/dist/{chunk-T6WHI3JW.js → chunk-OLAVQS6S.js} +6 -6
- package/dist/{chunk-ER3CCWK2.js → chunk-ORAK3LNZ.js} +2 -2
- package/dist/{chunk-PJ7DIEFC.js → chunk-PBZQVVLG.js} +5 -5
- package/dist/{chunk-PJQMISPD.js → chunk-PPAEJXVF.js} +5 -5
- package/dist/{chunk-GMWKWA6N.js → chunk-PRX77NPC.js} +5 -5
- package/dist/{chunk-24463PIV.js → chunk-PS572XYO.js} +2 -2
- package/dist/{chunk-KKOCFUI5.js → chunk-REZ4TUD2.js} +10 -10
- package/dist/{chunk-2QSZNTDO.js → chunk-RKNJBZ55.js} +4 -4
- package/dist/{chunk-V5UV7BOA.js → chunk-SQNED75R.js} +1 -1
- package/dist/{chunk-FLVL4L3L.js → chunk-TCKP4EHY.js} +2 -2
- package/dist/{chunk-SP42M3XP.js → chunk-TXV3I2O6.js} +7 -7
- package/dist/{chunk-S6UCPBPL.js → chunk-UNV6ZGTS.js} +2 -2
- package/dist/{chunk-2BFLDH5F.js → chunk-WEPWY4HB.js} +2 -2
- package/dist/{chunk-YQGHW5ML.js → chunk-WRFKZEO6.js} +4 -4
- package/dist/{chunk-XCXN3D2U.js → chunk-Y4TAFMUE.js} +6 -6
- package/dist/{chunk-H6S3DJVE.js → chunk-YMGQXBPS.js} +2 -2
- package/dist/{chunk-T422SYM6.js → chunk-ZUDM75KG.js} +5 -5
- package/dist/{chunk-XD6JVK5P.js → chunk-ZUHD6XZZ.js} +43 -43
- package/dist/{chunk-FOJY75BL.js → chunk-ZW5SZ3TH.js} +12 -12
- package/dist/{cli-BJQ2ahrh.d.ts → cli-cZyANcmY.d.ts} +1 -1
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +59 -59
- package/dist/compounding/engine.js +8 -8
- package/dist/config.js +1 -1
- package/dist/connectors/codex-materialize-runner.js +8 -8
- package/dist/connectors/index.js +9 -9
- package/dist/contradiction/index.js +2 -2
- package/dist/{contradiction-scan-2SODRNUF.js → contradiction-scan-3FJYWS3G.js} +3 -3
- package/dist/contradiction-scan-3FJYWS3G.js.map +1 -0
- package/dist/conversation-index/backend.js +2 -2
- package/dist/entity-retrieval.js +8 -8
- package/dist/extraction.js +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +126 -126
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.js +2 -2
- package/dist/maintenance/memory-governance.js +8 -8
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +7 -7
- package/dist/maintenance/rebuild-memory-projection.js +9 -9
- package/dist/mcp-memory-inspector-app.d.ts +1 -1
- package/dist/namespaces/migrate.js +18 -18
- package/dist/namespaces/search.js +9 -9
- package/dist/namespaces/storage.js +9 -9
- package/dist/operator-toolkit.js +24 -24
- package/dist/orchestration/maintenance.js +12 -12
- package/dist/orchestrator.js +82 -82
- package/dist/resume-bundles.js +2 -2
- package/dist/schemas.d.ts +10 -10
- package/dist/search/factory.js +8 -8
- package/dist/search/index.js +8 -8
- 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 +11 -11
- package/dist/semantic-rule-promotion.js +7 -7
- package/dist/semantic-rule-verifier.js +8 -8
- package/dist/shared-context/manager.d.ts +2 -2
- package/dist/storage.d.ts +60 -78
- package/dist/storage.js +14 -6
- package/dist/summarizer.js +2 -2
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +2 -2
- package/dist/transfer/types.d.ts +12 -12
- package/dist/verified-recall.js +8 -8
- package/package.json +2 -2
- package/src/access-observe-write-surface.ts +1007 -0
- package/src/access-service.ts +48 -794
- package/src/orchestration/self-deps.ts +9 -1
- package/src/storage/entity-store.ts +4 -2
- package/src/storage/memory-read-store.ts +756 -0
- package/src/storage.ts +67 -627
- package/dist/chunk-GCEV6SXP.js.map +0 -1
- package/dist/chunk-XBM5DG4O.js.map +0 -1
- /package/dist/{capsule-crypto-CZJSLEFG.js.map → access-observe-write-surface.js.map} +0 -0
- /package/dist/{auto-sync-G4YKBPOU.js.map → auto-sync-RZO5ZPHR.js.map} +0 -0
- /package/dist/{contradiction-scan-2SODRNUF.js.map → capsule-crypto-FFWJ4X4A.js.map} +0 -0
- /package/dist/{chunk-M5QKGHCR.js.map → chunk-3I2IASNK.js.map} +0 -0
- /package/dist/{chunk-OCFITZRV.js.map → chunk-4AC57RCM.js.map} +0 -0
- /package/dist/{chunk-3DOD6ICJ.js.map → chunk-4I45UZ4V.js.map} +0 -0
- /package/dist/{chunk-TAPSNIMA.js.map → chunk-5CEJH5ZN.js.map} +0 -0
- /package/dist/{chunk-I6IAJCJS.js.map → chunk-5VWRYFQE.js.map} +0 -0
- /package/dist/{chunk-4LQUYBZM.js.map → chunk-5ZKT7WTR.js.map} +0 -0
- /package/dist/{chunk-VL5QHTJI.js.map → chunk-6DA5BBLS.js.map} +0 -0
- /package/dist/{chunk-LFKEFIJA.js.map → chunk-6DXXIXED.js.map} +0 -0
- /package/dist/{chunk-NCNOYM6F.js.map → chunk-7CFGXWYX.js.map} +0 -0
- /package/dist/{chunk-TJATYB3F.js.map → chunk-A6XCHI53.js.map} +0 -0
- /package/dist/{chunk-UQJ6YTKR.js.map → chunk-BTH6MQJT.js.map} +0 -0
- /package/dist/{chunk-G7LCG56O.js.map → chunk-CXW6Y7SI.js.map} +0 -0
- /package/dist/{chunk-DS4QM6I7.js.map → chunk-EEKDRAWX.js.map} +0 -0
- /package/dist/{chunk-I3UWEZTK.js.map → chunk-EF3VFQC4.js.map} +0 -0
- /package/dist/{chunk-437S3G37.js.map → chunk-EJJQDP3H.js.map} +0 -0
- /package/dist/{chunk-FQLM6ZUP.js.map → chunk-EVVLGYHR.js.map} +0 -0
- /package/dist/{chunk-GGYUR4M2.js.map → chunk-GBXP62CL.js.map} +0 -0
- /package/dist/{chunk-N4AGSRVA.js.map → chunk-GG6G2RYR.js.map} +0 -0
- /package/dist/{chunk-JITAVROY.js.map → chunk-H2T4OJNI.js.map} +0 -0
- /package/dist/{chunk-2MR3MFQB.js.map → chunk-JTYM4HPA.js.map} +0 -0
- /package/dist/{chunk-LD53WPMU.js.map → chunk-LQ6JI4VH.js.map} +0 -0
- /package/dist/{chunk-GPFXPC2V.js.map → chunk-NG2NHB4C.js.map} +0 -0
- /package/dist/{chunk-CIDGUFAL.js.map → chunk-NLXKSM46.js.map} +0 -0
- /package/dist/{chunk-T6WHI3JW.js.map → chunk-OLAVQS6S.js.map} +0 -0
- /package/dist/{chunk-ER3CCWK2.js.map → chunk-ORAK3LNZ.js.map} +0 -0
- /package/dist/{chunk-PJ7DIEFC.js.map → chunk-PBZQVVLG.js.map} +0 -0
- /package/dist/{chunk-PJQMISPD.js.map → chunk-PPAEJXVF.js.map} +0 -0
- /package/dist/{chunk-GMWKWA6N.js.map → chunk-PRX77NPC.js.map} +0 -0
- /package/dist/{chunk-24463PIV.js.map → chunk-PS572XYO.js.map} +0 -0
- /package/dist/{chunk-KKOCFUI5.js.map → chunk-REZ4TUD2.js.map} +0 -0
- /package/dist/{chunk-2QSZNTDO.js.map → chunk-RKNJBZ55.js.map} +0 -0
- /package/dist/{chunk-V5UV7BOA.js.map → chunk-SQNED75R.js.map} +0 -0
- /package/dist/{chunk-FLVL4L3L.js.map → chunk-TCKP4EHY.js.map} +0 -0
- /package/dist/{chunk-SP42M3XP.js.map → chunk-TXV3I2O6.js.map} +0 -0
- /package/dist/{chunk-S6UCPBPL.js.map → chunk-UNV6ZGTS.js.map} +0 -0
- /package/dist/{chunk-2BFLDH5F.js.map → chunk-WEPWY4HB.js.map} +0 -0
- /package/dist/{chunk-YQGHW5ML.js.map → chunk-WRFKZEO6.js.map} +0 -0
- /package/dist/{chunk-XCXN3D2U.js.map → chunk-Y4TAFMUE.js.map} +0 -0
- /package/dist/{chunk-H6S3DJVE.js.map → chunk-YMGQXBPS.js.map} +0 -0
- /package/dist/{chunk-T422SYM6.js.map → chunk-ZUDM75KG.js.map} +0 -0
- /package/dist/{chunk-XD6JVK5P.js.map → chunk-ZUHD6XZZ.js.map} +0 -0
- /package/dist/{chunk-FOJY75BL.js.map → chunk-ZW5SZ3TH.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 { AccessObserveWriteSurface } from "./access-observe-write-surface.js";
|
|
254
255
|
import { AccessLcmSurface } from "./access-lcm-surface.js";
|
|
255
256
|
import { AccessAdminOpsSurface } from "./access-admin-ops-surface.js";
|
|
256
257
|
import { AccessRecallSurface } from "./access-recall-surface.js";
|
|
@@ -1309,6 +1310,18 @@ export class EngramAccessService {
|
|
|
1309
1310
|
private readonly budget: CrossNamespaceBudget;
|
|
1310
1311
|
private readonly auditAdapter: AccessAuditAdapter | null;
|
|
1311
1312
|
|
|
1313
|
+
/** AccessObserveWriteSurface (access-service decomposition). Lazy; selfDeps live wiring. */
|
|
1314
|
+
private _accessObserveWriteSurface: AccessObserveWriteSurface | undefined;
|
|
1315
|
+
|
|
1316
|
+
private get accessObserveWriteSurface(): AccessObserveWriteSurface {
|
|
1317
|
+
if (!this._accessObserveWriteSurface) {
|
|
1318
|
+
this._accessObserveWriteSurface = new AccessObserveWriteSurface(
|
|
1319
|
+
selfDeps<ConstructorParameters<typeof AccessObserveWriteSurface>[0]>(this),
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
return this._accessObserveWriteSurface;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1312
1325
|
/** AccessLcmSurface (access-service decomposition). Lazy; selfDeps live wiring. */
|
|
1313
1326
|
private _accessLcmSurface: AccessLcmSurface | undefined;
|
|
1314
1327
|
|
|
@@ -2468,89 +2481,12 @@ export class EngramAccessService {
|
|
|
2468
2481
|
namespace: string,
|
|
2469
2482
|
collection: string,
|
|
2470
2483
|
): Promise<EngramAccessQmdHealthResponse> {
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
degraded: false,
|
|
2476
|
-
mode: searchBackend !== "qmd" ? "not-selected" : "disabled",
|
|
2477
|
-
collection,
|
|
2478
|
-
collectionState: "skipped",
|
|
2479
|
-
installedVersion: null,
|
|
2480
|
-
supportedVersion: null,
|
|
2481
|
-
supported: null,
|
|
2482
|
-
upgradeAvailable: null,
|
|
2483
|
-
doctorAvailable: null,
|
|
2484
|
-
debugStatus: searchBackend !== "qmd" ? `backend=${searchBackend}` : "backend=disabled",
|
|
2485
|
-
};
|
|
2486
|
-
}
|
|
2487
|
-
|
|
2488
|
-
if (resolveNamespaceCapabilities(this.orchestrator.config).namespaces === true) {
|
|
2489
|
-
const namespaceHealth = await this.namespaceQmdHealth(searchBackend, qmdEnabled, namespace, collection);
|
|
2490
|
-
if (namespaceHealth) return namespaceHealth;
|
|
2491
|
-
}
|
|
2492
|
-
|
|
2493
|
-
const qmd = this.orchestrator.qmd;
|
|
2494
|
-
if (!qmd) {
|
|
2495
|
-
return {
|
|
2496
|
-
enabled: true,
|
|
2497
|
-
active: false,
|
|
2498
|
-
degraded: true,
|
|
2499
|
-
mode: "fallback",
|
|
2500
|
-
collection,
|
|
2501
|
-
collectionState: "unknown",
|
|
2502
|
-
installedVersion: null,
|
|
2503
|
-
supportedVersion: null,
|
|
2504
|
-
supported: null,
|
|
2505
|
-
upgradeAvailable: null,
|
|
2506
|
-
doctorAvailable: null,
|
|
2507
|
-
debugStatus: "backend=unavailable",
|
|
2508
|
-
};
|
|
2509
|
-
}
|
|
2510
|
-
const diagnosticAvailable = await this.qmdProbeAvailable(searchBackend, qmdEnabled);
|
|
2511
|
-
const operationalAvailable = diagnosticAvailable || qmd.isAvailable();
|
|
2512
|
-
const collectionState = diagnosticAvailable
|
|
2513
|
-
? await this.qmdCollectionState(searchBackend, qmdEnabled, collection)
|
|
2514
|
-
: "unknown";
|
|
2515
|
-
const active = operationalAvailable && collectionState !== "missing";
|
|
2516
|
-
const degraded =
|
|
2517
|
-
searchBackend === "qmd" &&
|
|
2518
|
-
qmdEnabled &&
|
|
2519
|
-
(!active || !diagnosticAvailable || collectionState === "unknown");
|
|
2520
|
-
const debugStatus = qmd.debugStatus();
|
|
2521
|
-
const versionStatus =
|
|
2522
|
-
"getVersionStatus" in qmd && typeof qmd.getVersionStatus === "function"
|
|
2523
|
-
? qmd.getVersionStatus()
|
|
2524
|
-
: null;
|
|
2525
|
-
const daemonMode =
|
|
2526
|
-
"isDaemonMode" in qmd && typeof qmd.isDaemonMode === "function"
|
|
2527
|
-
? qmd.isDaemonMode() === true
|
|
2528
|
-
: false;
|
|
2529
|
-
const mode =
|
|
2530
|
-
searchBackend !== "qmd"
|
|
2531
|
-
? "not-selected"
|
|
2532
|
-
: !qmdEnabled
|
|
2533
|
-
? "disabled"
|
|
2534
|
-
: !active
|
|
2535
|
-
? "fallback"
|
|
2536
|
-
: daemonMode
|
|
2537
|
-
? "daemon"
|
|
2538
|
-
: "cli";
|
|
2539
|
-
|
|
2540
|
-
return {
|
|
2541
|
-
enabled: qmdEnabled,
|
|
2542
|
-
active,
|
|
2543
|
-
degraded,
|
|
2544
|
-
mode,
|
|
2484
|
+
return this.accessObserveWriteSurface.qmdHealth(
|
|
2485
|
+
searchBackend,
|
|
2486
|
+
qmdEnabled,
|
|
2487
|
+
namespace,
|
|
2545
2488
|
collection,
|
|
2546
|
-
|
|
2547
|
-
installedVersion: versionStatus?.installedVersion ?? null,
|
|
2548
|
-
supportedVersion: versionStatus?.supportedVersion ?? null,
|
|
2549
|
-
supported: versionStatus?.supported ?? null,
|
|
2550
|
-
upgradeAvailable: versionStatus?.upgradeAvailable ?? null,
|
|
2551
|
-
doctorAvailable: versionStatus?.capabilities?.doctor ?? null,
|
|
2552
|
-
debugStatus,
|
|
2553
|
-
};
|
|
2489
|
+
);
|
|
2554
2490
|
}
|
|
2555
2491
|
|
|
2556
2492
|
private async namespaceQmdHealth(
|
|
@@ -2559,77 +2495,12 @@ export class EngramAccessService {
|
|
|
2559
2495
|
namespace: string,
|
|
2560
2496
|
fallbackCollection: string,
|
|
2561
2497
|
): Promise<EngramAccessQmdHealthResponse | null> {
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
) => Promise<{
|
|
2569
|
-
collection: string;
|
|
2570
|
-
available: boolean;
|
|
2571
|
-
collectionState: EngramAccessQmdCollectionState;
|
|
2572
|
-
debugStatus: string;
|
|
2573
|
-
installedVersion: string | null;
|
|
2574
|
-
supportedVersion: string | null;
|
|
2575
|
-
supported: boolean | null;
|
|
2576
|
-
upgradeAvailable: boolean | null;
|
|
2577
|
-
doctorAvailable: boolean | null;
|
|
2578
|
-
daemonMode: boolean | null;
|
|
2579
|
-
}>;
|
|
2580
|
-
}
|
|
2581
|
-
).searchHealthForNamespace;
|
|
2582
|
-
if (typeof searchHealthForNamespace !== "function") return null;
|
|
2583
|
-
|
|
2584
|
-
const controller = new AbortController();
|
|
2585
|
-
const timer = setTimeout(() => controller.abort(), 2_000);
|
|
2586
|
-
timer.unref?.();
|
|
2587
|
-
try {
|
|
2588
|
-
const health = await searchHealthForNamespace.call(this.orchestrator, namespace, {
|
|
2589
|
-
signal: controller.signal,
|
|
2590
|
-
});
|
|
2591
|
-
const active = health.available && health.collectionState !== "missing";
|
|
2592
|
-
const degraded = !active || health.collectionState === "unknown";
|
|
2593
|
-
const mode =
|
|
2594
|
-
!active
|
|
2595
|
-
? "fallback"
|
|
2596
|
-
: health.daemonMode === true
|
|
2597
|
-
? "daemon"
|
|
2598
|
-
: "cli";
|
|
2599
|
-
|
|
2600
|
-
return {
|
|
2601
|
-
enabled: true,
|
|
2602
|
-
active,
|
|
2603
|
-
degraded,
|
|
2604
|
-
mode,
|
|
2605
|
-
collection: health.collection || fallbackCollection,
|
|
2606
|
-
collectionState: health.collectionState,
|
|
2607
|
-
installedVersion: health.installedVersion,
|
|
2608
|
-
supportedVersion: health.supportedVersion,
|
|
2609
|
-
supported: health.supported,
|
|
2610
|
-
upgradeAvailable: health.upgradeAvailable,
|
|
2611
|
-
doctorAvailable: health.doctorAvailable,
|
|
2612
|
-
debugStatus: health.debugStatus,
|
|
2613
|
-
};
|
|
2614
|
-
} catch (error) {
|
|
2615
|
-
const detail = displayErrorDetail(error) || "unknown";
|
|
2616
|
-
return {
|
|
2617
|
-
enabled: true,
|
|
2618
|
-
active: false,
|
|
2619
|
-
degraded: true,
|
|
2620
|
-
mode: "fallback",
|
|
2621
|
-
collection: fallbackCollection,
|
|
2622
|
-
collectionState: "unknown",
|
|
2623
|
-
installedVersion: null,
|
|
2624
|
-
supportedVersion: null,
|
|
2625
|
-
supported: null,
|
|
2626
|
-
upgradeAvailable: null,
|
|
2627
|
-
doctorAvailable: null,
|
|
2628
|
-
debugStatus: `backend=namespace-unavailable error=${detail}`,
|
|
2629
|
-
};
|
|
2630
|
-
} finally {
|
|
2631
|
-
clearTimeout(timer);
|
|
2632
|
-
}
|
|
2498
|
+
return this.accessObserveWriteSurface.namespaceQmdHealth(
|
|
2499
|
+
searchBackend,
|
|
2500
|
+
qmdEnabled,
|
|
2501
|
+
namespace,
|
|
2502
|
+
fallbackCollection,
|
|
2503
|
+
);
|
|
2633
2504
|
}
|
|
2634
2505
|
|
|
2635
2506
|
private async qmdCollectionState(
|
|
@@ -2714,100 +2585,12 @@ export class EngramAccessService {
|
|
|
2714
2585
|
return this.orchestrator.generateDaySummary(memories);
|
|
2715
2586
|
}
|
|
2716
2587
|
|
|
2717
|
-
/**
|
|
2718
|
-
* Build a daily context briefing. Gracefully degrades when the OpenAI key
|
|
2719
|
-
* or Responses API is unavailable — never throws for LLM-related problems.
|
|
2720
|
-
*/
|
|
2721
2588
|
async briefing(
|
|
2722
2589
|
request: EngramAccessBriefingRequest,
|
|
2723
2590
|
): Promise<EngramAccessBriefingResponse> {
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
}
|
|
2728
|
-
|
|
2729
|
-
const namespace = this.resolveReadableNamespace(request.namespace, request.principal);
|
|
2730
|
-
const storage = await this.orchestrator.getStorage(namespace);
|
|
2731
|
-
|
|
2732
|
-
const token = typeof request.since === "string" && request.since.trim().length > 0
|
|
2733
|
-
? request.since.trim()
|
|
2734
|
-
: config.briefing.defaultWindow;
|
|
2735
|
-
const window = parseBriefingWindow(token);
|
|
2736
|
-
if (!window) {
|
|
2737
|
-
throw new EngramAccessInputError(`invalid briefing window: ${token}`);
|
|
2738
|
-
}
|
|
2739
|
-
|
|
2740
|
-
// Validate focus: only treat undefined / empty strings as "no filter".
|
|
2741
|
-
// Anything else that parses to null (e.g. "project:", "topic:") is malformed
|
|
2742
|
-
// and must be rejected so a templating miss never silently broadens the
|
|
2743
|
-
// briefing from a targeted project view to all memories.
|
|
2744
|
-
const rawFocus = typeof request.focus === "string" ? request.focus.trim() : "";
|
|
2745
|
-
let focus = null;
|
|
2746
|
-
if (rawFocus.length > 0) {
|
|
2747
|
-
focus = parseBriefingFocus(rawFocus);
|
|
2748
|
-
if (!focus) {
|
|
2749
|
-
throw new EngramAccessInputError(
|
|
2750
|
-
`invalid briefing focus filter: ${request.focus}`,
|
|
2751
|
-
);
|
|
2752
|
-
}
|
|
2753
|
-
}
|
|
2754
|
-
|
|
2755
|
-
// Reject unsupported format values explicitly. Programmatic callers that
|
|
2756
|
-
// bypass CLI/MCP pre-validation (which already use validateBriefingFormat)
|
|
2757
|
-
// could otherwise send a typo like "jsno" and silently receive a response
|
|
2758
|
-
// in the default format, masking the client bug and breaking format-dependent
|
|
2759
|
-
// automation. Only undefined / absent format falls through to the default.
|
|
2760
|
-
const SUPPORTED_FORMATS = ["markdown", "json"] as const;
|
|
2761
|
-
if (
|
|
2762
|
-
typeof request.format === "string" &&
|
|
2763
|
-
!(SUPPORTED_FORMATS as readonly string[]).includes(request.format)
|
|
2764
|
-
) {
|
|
2765
|
-
throw new EngramAccessInputError(
|
|
2766
|
-
`unsupported briefing format: "${request.format}". Accepted: ${SUPPORTED_FORMATS.join(", ")}.`,
|
|
2767
|
-
);
|
|
2768
|
-
}
|
|
2769
|
-
const format: "markdown" | "json" = request.format === "json"
|
|
2770
|
-
? "json"
|
|
2771
|
-
: request.format === "markdown"
|
|
2772
|
-
? "markdown"
|
|
2773
|
-
: config.briefing.defaultFormat;
|
|
2774
|
-
|
|
2775
|
-
const maxFollowups = typeof request.maxFollowups === "number" && Number.isFinite(request.maxFollowups)
|
|
2776
|
-
? Math.max(0, Math.min(10, Math.floor(request.maxFollowups)))
|
|
2777
|
-
: config.briefing.maxFollowups;
|
|
2778
|
-
|
|
2779
|
-
const calendarSource = config.briefing.calendarSource
|
|
2780
|
-
? new FileCalendarSource(config.briefing.calendarSource)
|
|
2781
|
-
: undefined;
|
|
2782
|
-
|
|
2783
|
-
const result = await buildBriefing({
|
|
2784
|
-
storage,
|
|
2785
|
-
namespace,
|
|
2786
|
-
window,
|
|
2787
|
-
focus,
|
|
2788
|
-
calendarSource,
|
|
2789
|
-
maxFollowups,
|
|
2790
|
-
allowLlm: config.briefing.llmFollowups,
|
|
2791
|
-
openaiApiKey: config.openaiApiKey,
|
|
2792
|
-
openaiBaseUrl: config.openaiBaseUrl,
|
|
2793
|
-
model: config.model,
|
|
2794
|
-
// Without a direct OpenAI key, route follow-ups through the configured
|
|
2795
|
-
// LLM chain (gateway model source or local LLM) — same fallback every
|
|
2796
|
-
// other LLM feature uses. A configured key keeps its precedence so
|
|
2797
|
-
// existing deployments are unchanged.
|
|
2798
|
-
followupGenerator: config.openaiApiKey
|
|
2799
|
-
? undefined
|
|
2800
|
-
: this.orchestrator.briefingChainFollowupGenerator,
|
|
2801
|
-
});
|
|
2802
|
-
|
|
2803
|
-
return {
|
|
2804
|
-
format,
|
|
2805
|
-
window: result.window,
|
|
2806
|
-
namespace,
|
|
2807
|
-
markdown: result.markdown,
|
|
2808
|
-
json: result.json,
|
|
2809
|
-
followupsUnavailableReason: result.followupsUnavailableReason,
|
|
2810
|
-
};
|
|
2591
|
+
return this.accessObserveWriteSurface.briefing(
|
|
2592
|
+
request,
|
|
2593
|
+
);
|
|
2811
2594
|
}
|
|
2812
2595
|
|
|
2813
2596
|
/**
|
|
@@ -3141,73 +2924,10 @@ export class EngramAccessService {
|
|
|
3141
2924
|
request: EngramAccessMemoryStoreRequest,
|
|
3142
2925
|
hooks?: { enforceWriteQuota?: () => void | Promise<void> },
|
|
3143
2926
|
): Promise<EngramAccessWriteResponse> {
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
}
|
|
3149
|
-
const execute = async (): Promise<EngramAccessWriteResponse> => {
|
|
3150
|
-
const candidate = this.validateWriteCandidate(request, namespace);
|
|
3151
|
-
if (request.dryRun === true) {
|
|
3152
|
-
return {
|
|
3153
|
-
schemaVersion: ENGRAM_ACCESS_WRITE_SCHEMA_VERSION,
|
|
3154
|
-
operation: "memory_store",
|
|
3155
|
-
namespace,
|
|
3156
|
-
dryRun: true,
|
|
3157
|
-
accepted: true,
|
|
3158
|
-
queued: false,
|
|
3159
|
-
status: "validated",
|
|
3160
|
-
idempotencyKey: request.idempotencyKey?.trim() || undefined,
|
|
3161
|
-
};
|
|
3162
|
-
}
|
|
3163
|
-
const result = await persistExplicitCapture(this.orchestrator, candidate, "memory_store");
|
|
3164
|
-
// Seed the session's coding binding ONLY after a real write commits, and
|
|
3165
|
-
// only when the namespace came from project scoping (no explicit
|
|
3166
|
-
// namespace). This mirrors recall's maybeAttachCodingContext so a LATER
|
|
3167
|
-
// bare recall/write on the same session is scoped to the same project —
|
|
3168
|
-
// but never binds the session on a dryRun, replay/conflict, quota
|
|
3169
|
-
// rejection, or an explicit-namespace write (which bypasses the overlay),
|
|
3170
|
-
// since those don't reach this point or aren't project-scoped (Codex review).
|
|
3171
|
-
await this.attachCodingContextAfterScopedWrite(request);
|
|
3172
|
-
// #1645 (review thread yG-): a tombstone-blocked capture is pending_review
|
|
3173
|
-
// (no active copy) — report it as queued_for_review so the HTTP/MCP caller
|
|
3174
|
-
// doesn't read it as a successfully stored active memory.
|
|
3175
|
-
const blocked = result.tombstoneBlocked === true;
|
|
3176
|
-
const response: EngramAccessWriteResponse = {
|
|
3177
|
-
schemaVersion: ENGRAM_ACCESS_WRITE_SCHEMA_VERSION,
|
|
3178
|
-
operation: "memory_store",
|
|
3179
|
-
namespace,
|
|
3180
|
-
dryRun: false,
|
|
3181
|
-
accepted: true,
|
|
3182
|
-
queued: blocked,
|
|
3183
|
-
status: blocked ? "queued_for_review" : result.duplicateOf ? "duplicate" : "stored",
|
|
3184
|
-
memoryId: result.id,
|
|
3185
|
-
duplicateOf: result.duplicateOf,
|
|
3186
|
-
idempotencyKey: request.idempotencyKey?.trim() || undefined,
|
|
3187
|
-
};
|
|
3188
|
-
log.info(
|
|
3189
|
-
`access-write op=memory_store namespace=${namespace} dryRun=false status=${response.status} memoryId=${response.memoryId ?? "-"} idempotency=${response.idempotencyKey ? "yes" : "no"}`,
|
|
3190
|
-
);
|
|
3191
|
-
return response;
|
|
3192
|
-
};
|
|
3193
|
-
return this.handleIdempotentWrite({
|
|
3194
|
-
operation: "memory_store",
|
|
3195
|
-
idempotencyKey: request.idempotencyKey,
|
|
3196
|
-
requestFingerprint: {
|
|
3197
|
-
schemaVersion,
|
|
3198
|
-
content: request.content,
|
|
3199
|
-
category: request.category,
|
|
3200
|
-
confidence: request.confidence,
|
|
3201
|
-
namespace,
|
|
3202
|
-
tags: request.tags,
|
|
3203
|
-
entityRef: request.entityRef,
|
|
3204
|
-
ttl: request.ttl,
|
|
3205
|
-
sourceReason: request.sourceReason,
|
|
3206
|
-
},
|
|
3207
|
-
skip: request.dryRun === true,
|
|
3208
|
-
beforeExecute: hooks?.enforceWriteQuota,
|
|
3209
|
-
execute,
|
|
3210
|
-
});
|
|
2927
|
+
return this.accessObserveWriteSurface.memoryStore(
|
|
2928
|
+
request,
|
|
2929
|
+
hooks,
|
|
2930
|
+
);
|
|
3211
2931
|
}
|
|
3212
2932
|
|
|
3213
2933
|
async peekMemoryStoreIdempotency(request: EngramAccessMemoryStoreRequest): Promise<EngramAccessIdempotencyStatus> {
|
|
@@ -3238,70 +2958,10 @@ export class EngramAccessService {
|
|
|
3238
2958
|
request: EngramAccessSuggestionSubmitRequest,
|
|
3239
2959
|
hooks?: { enforceWriteQuota?: () => void | Promise<void> },
|
|
3240
2960
|
): Promise<EngramAccessWriteResponse> {
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
}
|
|
3246
|
-
const execute = async (): Promise<EngramAccessWriteResponse> => {
|
|
3247
|
-
const candidate = this.validateWriteCandidate(request, namespace);
|
|
3248
|
-
if (request.dryRun === true) {
|
|
3249
|
-
return {
|
|
3250
|
-
schemaVersion: ENGRAM_ACCESS_WRITE_SCHEMA_VERSION,
|
|
3251
|
-
operation: "suggestion_submit",
|
|
3252
|
-
namespace,
|
|
3253
|
-
dryRun: true,
|
|
3254
|
-
accepted: true,
|
|
3255
|
-
queued: true,
|
|
3256
|
-
status: "validated",
|
|
3257
|
-
idempotencyKey: request.idempotencyKey?.trim() || undefined,
|
|
3258
|
-
};
|
|
3259
|
-
}
|
|
3260
|
-
const result = await queueExplicitCaptureForReview(
|
|
3261
|
-
this.orchestrator,
|
|
3262
|
-
candidate,
|
|
3263
|
-
"suggestion_submit",
|
|
3264
|
-
new Error(request.sourceReason?.trim() || "submitted via engram suggestion_submit"),
|
|
3265
|
-
);
|
|
3266
|
-
// Seed the session binding only after a real, project-scoped submit commits
|
|
3267
|
-
// (mirrors memory_store / recall; skips dryRun, replay, quota-reject, and
|
|
3268
|
-
// explicit-namespace writes — Codex review).
|
|
3269
|
-
await this.attachCodingContextAfterScopedWrite(request);
|
|
3270
|
-
const response: EngramAccessWriteResponse = {
|
|
3271
|
-
schemaVersion: ENGRAM_ACCESS_WRITE_SCHEMA_VERSION,
|
|
3272
|
-
operation: "suggestion_submit",
|
|
3273
|
-
namespace,
|
|
3274
|
-
dryRun: false,
|
|
3275
|
-
accepted: true,
|
|
3276
|
-
queued: true,
|
|
3277
|
-
status: "queued_for_review",
|
|
3278
|
-
memoryId: result.id,
|
|
3279
|
-
duplicateOf: result.duplicateOf,
|
|
3280
|
-
idempotencyKey: request.idempotencyKey?.trim() || undefined,
|
|
3281
|
-
};
|
|
3282
|
-
log.info(
|
|
3283
|
-
`access-write op=suggestion_submit namespace=${namespace} dryRun=false status=${response.status} memoryId=${response.memoryId ?? "-"} idempotency=${response.idempotencyKey ? "yes" : "no"}`,
|
|
3284
|
-
);
|
|
3285
|
-
return response;
|
|
3286
|
-
};
|
|
3287
|
-
return this.handleIdempotentWrite({
|
|
3288
|
-
operation: "suggestion_submit",
|
|
3289
|
-
idempotencyKey: request.idempotencyKey,
|
|
3290
|
-
requestFingerprint: {
|
|
3291
|
-
schemaVersion,
|
|
3292
|
-
content: request.content,
|
|
3293
|
-
category: request.category,
|
|
3294
|
-
confidence: request.confidence,
|
|
3295
|
-
namespace,
|
|
3296
|
-
tags: request.tags,
|
|
3297
|
-
entityRef: request.entityRef,
|
|
3298
|
-
ttl: request.ttl,
|
|
3299
|
-
sourceReason: request.sourceReason,
|
|
3300
|
-
},
|
|
3301
|
-
skip: request.dryRun === true,
|
|
3302
|
-
beforeExecute: hooks?.enforceWriteQuota,
|
|
3303
|
-
execute,
|
|
3304
|
-
});
|
|
2961
|
+
return this.accessObserveWriteSurface.suggestionSubmit(
|
|
2962
|
+
request,
|
|
2963
|
+
hooks,
|
|
2964
|
+
);
|
|
3305
2965
|
}
|
|
3306
2966
|
|
|
3307
2967
|
async peekSuggestionSubmitIdempotency(
|
|
@@ -4391,228 +4051,9 @@ export class EngramAccessService {
|
|
|
4391
4051
|
}
|
|
4392
4052
|
|
|
4393
4053
|
private async runObserve(request: EngramAccessObserveRequest): Promise<EngramAccessObserveResponse> {
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
}
|
|
4397
|
-
if (!Array.isArray(request.messages) || request.messages.length === 0) {
|
|
4398
|
-
throw new EngramAccessInputError("messages is required and must be a non-empty array");
|
|
4399
|
-
}
|
|
4400
|
-
for (const msg of request.messages) {
|
|
4401
|
-
if (!msg || typeof msg !== "object" || typeof msg.role !== "string" || typeof msg.content !== "string") {
|
|
4402
|
-
throw new EngramAccessInputError("each message must have a string 'role' and 'content'");
|
|
4403
|
-
}
|
|
4404
|
-
if (msg.role !== "user" && msg.role !== "assistant") {
|
|
4405
|
-
throw new EngramAccessInputError(`invalid message role: ${msg.role} (expected 'user' or 'assistant')`);
|
|
4406
|
-
}
|
|
4407
|
-
}
|
|
4408
|
-
|
|
4409
|
-
// 1. Resolve the FULL effective scope plan BEFORE any session mutation
|
|
4410
|
-
// (Codex P2 / Cursor "orphan context after overlay auth"). The plan is
|
|
4411
|
-
// read-only and re-runs the SAME authorization as
|
|
4412
|
-
// memory_store/suggestion_submit (rule 39): the explicit-namespace check
|
|
4413
|
-
// AND the coding-overlay self-base `canWriteNamespace` check both run
|
|
4414
|
-
// here. Because `maybeAttachCodingContext` has NOT run yet, the plan's
|
|
4415
|
-
// overlay resolves from the per-call `cwd`/`projectTag` fallback
|
|
4416
|
-
// (`resolveCodingContextFromOptions`) — identical to the context that
|
|
4417
|
-
// would be attached — so the scope is the same either way. Running the
|
|
4418
|
-
// plan first means an `observe` that ultimately throws on a non-writable
|
|
4419
|
-
// self base leaves NO coding context bound to the session, matching how
|
|
4420
|
-
// `memory_store` resolves its full scoped write namespace before any
|
|
4421
|
-
// session mutation.
|
|
4422
|
-
const scope = await this.resolveMemoryScopePlan(request);
|
|
4423
|
-
const writeNamespace = scope.writeNamespace;
|
|
4424
|
-
|
|
4425
|
-
// Backward-compatible BASE writable namespace (pre-#1495 response semantics)
|
|
4426
|
-
// for the legacy `namespace` response field. DERIVED from the already-resolved
|
|
4427
|
-
// scope plan — NOT a second writable-namespace resolution call
|
|
4428
|
-
// call (#1505 thread jvO). The fresh call re-authorized `undefined ⇒
|
|
4429
|
-
// config.defaultNamespace` a SECOND time; under a restrictive default-namespace
|
|
4430
|
-
// write policy that re-auth could REJECT an otherwise valid project-scoped
|
|
4431
|
-
// observe whose effective self/project write target the scope plan already
|
|
4432
|
-
// authorized (the same target memory_store/suggestion_submit accept). Worse,
|
|
4433
|
-
// that post-plan rejection fired AFTER `resolveMemoryScopePlan` may have seeded
|
|
4434
|
-
// the coding context, leaving an orphaned session binding behind. The plan is
|
|
4435
|
-
// the single authorization point (rule 22 / 39); the legacy field must reuse it
|
|
4436
|
-
// and never re-authorize. Pre-#1495 semantics were exactly
|
|
4437
|
-
// the writable-namespace resolver (overlay-agnostic): the explicit
|
|
4438
|
-
// namespace when supplied, else `config.defaultNamespace` for user-project
|
|
4439
|
-
// coding overlays. Hosted scope-profile layers such as `teamProject` report
|
|
4440
|
-
// their effective profile write namespace because there is no legacy
|
|
4441
|
-
// overlay-compatible base namespace for those writes.
|
|
4442
|
-
const namespace = this.legacyResponseNamespaceForScope(scope);
|
|
4443
|
-
const shouldWriteObjectiveState =
|
|
4444
|
-
resolveObjectiveStateCapabilities(this.orchestrator.config).objectiveStateMemory === true &&
|
|
4445
|
-
resolveObjectiveStateCapabilities(this.orchestrator.config).objectiveStateSnapshotWrites === true;
|
|
4446
|
-
|
|
4447
|
-
// 2. Auto-resolve coding context from cwd/projectTag so a LATER bare recall
|
|
4448
|
-
// on the same session is project-scoped (rule 42: same namespace layer as
|
|
4449
|
-
// recall). Done AFTER the scope plan authorized the write, so a rejected
|
|
4450
|
-
// request never leaves orphaned context on the session.
|
|
4451
|
-
await this.maybeAttachCodingContext(request.sessionKey, {
|
|
4452
|
-
cwd: request.cwd,
|
|
4453
|
-
projectTag: request.projectTag,
|
|
4454
|
-
});
|
|
4455
|
-
|
|
4456
|
-
// Prefix sessionKey with the EFFECTIVE write namespace for LCM archival so
|
|
4457
|
-
// observed turns are scoped to the same namespace project-scoped recall
|
|
4458
|
-
// reads. The SAME `lcmSessionKeyForNamespace` helper is used by the
|
|
4459
|
-
// orchestrator recall readers and by compaction flush/record, so the LCM
|
|
4460
|
-
// write key and every read/flush key agree (#1495, rule 42). Only prefixes
|
|
4461
|
-
// when the namespace diverges from the default store; a single-store
|
|
4462
|
-
// deployment keeps the raw sessionKey unchanged.
|
|
4463
|
-
const lcmSessionKey =
|
|
4464
|
-
lcmSessionKeyForNamespace(
|
|
4465
|
-
writeNamespace,
|
|
4466
|
-
request.sessionKey,
|
|
4467
|
-
this.orchestrator.config.defaultNamespace,
|
|
4468
|
-
) ?? request.sessionKey;
|
|
4469
|
-
|
|
4470
|
-
// 4. Objective-state snapshots → the scope plan's objective-state namespace.
|
|
4471
|
-
// For explicit-namespace and coding-overlay writes this equals
|
|
4472
|
-
// writeNamespace; for an IMPLICIT write it is the principal SELF base
|
|
4473
|
-
// (#928 contract, already auth-checked inside the scope plan), not the
|
|
4474
|
-
// general default-store write namespace.
|
|
4475
|
-
if (shouldWriteObjectiveState) {
|
|
4476
|
-
try {
|
|
4477
|
-
const objectiveStateLocation =
|
|
4478
|
-
await this.objectiveStateStoreLocationForNamespace(
|
|
4479
|
-
scope.objectiveStateNamespace,
|
|
4480
|
-
);
|
|
4481
|
-
await recordObjectiveStateSnapshotsFromObservedMessages({
|
|
4482
|
-
memoryDir: objectiveStateLocation.memoryDir,
|
|
4483
|
-
objectiveStateStoreDir: objectiveStateLocation.objectiveStateStoreDir,
|
|
4484
|
-
objectiveStateMemoryEnabled: resolveObjectiveStateCapabilities(this.orchestrator.config).objectiveStateMemory,
|
|
4485
|
-
objectiveStateSnapshotWritesEnabled:
|
|
4486
|
-
resolveObjectiveStateCapabilities(this.orchestrator.config).objectiveStateSnapshotWrites,
|
|
4487
|
-
sessionKey: request.sessionKey,
|
|
4488
|
-
recordedAt: new Date().toISOString(),
|
|
4489
|
-
messages: request.messages,
|
|
4490
|
-
});
|
|
4491
|
-
} catch (err) {
|
|
4492
|
-
log.error(`access-observe objective-state snapshot write failed: ${err}`);
|
|
4493
|
-
}
|
|
4494
|
-
}
|
|
4495
|
-
|
|
4496
|
-
// 5. LCM archival → effective write namespace.
|
|
4497
|
-
// lcmArchived in the response means "LCM archival was queued" (not
|
|
4498
|
-
// "completed"), matching extractionQueued semantics. Both run async.
|
|
4499
|
-
let lcmArchived = false;
|
|
4500
|
-
if (this.orchestrator.lcmEngine && this.orchestrator.lcmEngine.enabled) {
|
|
4501
|
-
// Fire-and-forget: LCM archival writes to SQLite and builds summary
|
|
4502
|
-
// DAGs, which can take tens of seconds for large sessions. Don't
|
|
4503
|
-
// block the HTTP response — the caller only needs acknowledgment.
|
|
4504
|
-
try {
|
|
4505
|
-
this.orchestrator.lcmEngine.enqueueObserveMessages(lcmSessionKey, request.messages);
|
|
4506
|
-
lcmArchived = true;
|
|
4507
|
-
} catch (err) {
|
|
4508
|
-
log.error(`access-observe LCM enqueue failed: ${err}`);
|
|
4509
|
-
}
|
|
4510
|
-
}
|
|
4511
|
-
|
|
4512
|
-
// 6. Extraction/replay → effective write namespace for STORAGE, ORIGINAL
|
|
4513
|
-
// sessionKey for IDENTITY (provenance + threading).
|
|
4514
|
-
let extractionQueued = false;
|
|
4515
|
-
if (request.skipExtraction !== true) {
|
|
4516
|
-
const turns = request.messages.map((m) => ({
|
|
4517
|
-
source: "openclaw" as const,
|
|
4518
|
-
// Identity-vs-routing separation (#1505 thread 1, cursor): extraction
|
|
4519
|
-
// derives the provenance principal via `resolvePrincipal(turn.sessionKey)`
|
|
4520
|
-
// and threads `turn.sessionKey` into conversation threading. Feeding the
|
|
4521
|
-
// namespace-PREFIXED `lcmSessionKey` here mis-derived the principal to
|
|
4522
|
-
// `default` (a `<ns>:<key>` string matches no prefix/map rule and fails
|
|
4523
|
-
// the `agent:` heuristic). Pass the ORIGINAL sessionKey so identity is
|
|
4524
|
-
// correct; storage routing is pinned separately via
|
|
4525
|
-
// writeNamespaceOverride below, and the authenticated principal is pinned
|
|
4526
|
-
// via principalOverride.
|
|
4527
|
-
sessionKey: request.sessionKey,
|
|
4528
|
-
role: m.role,
|
|
4529
|
-
content: m.content,
|
|
4530
|
-
parts: m.parts,
|
|
4531
|
-
rawContent: m.rawContent,
|
|
4532
|
-
sourceFormat: m.sourceFormat,
|
|
4533
|
-
timestamp: new Date().toISOString(),
|
|
4534
|
-
}));
|
|
4535
|
-
// Pin extraction STORAGE to the effective namespace rather than letting the
|
|
4536
|
-
// orchestrator re-derive one from the session key + coding overlay — that
|
|
4537
|
-
// re-derivation would have to reparse identity and could miss the overlay
|
|
4538
|
-
// (the #1495 drift). Passing writeNamespaceOverride makes the extraction
|
|
4539
|
-
// target deterministic and identical to LCM/objective-state (rule 39).
|
|
4540
|
-
//
|
|
4541
|
-
// Pin WHENEVER namespaces are enabled, not only when writeNamespace differs
|
|
4542
|
-
// from the default store (#1505 round 3, codex "Pin default-store extraction
|
|
4543
|
-
// writes too"). For an unqualified/no-overlay observe by a principal that
|
|
4544
|
-
// HAS a self namespace, writeNamespace is `config.defaultNamespace` but an
|
|
4545
|
-
// unpinned `runExtraction` would fall back to
|
|
4546
|
-
// `defaultNamespaceForPrincipal(principal)` = the SELF namespace — diverging
|
|
4547
|
-
// from where LCM/objective-state/response wrote (`default`). Pinning the
|
|
4548
|
-
// resolved writeNamespace forces all side effects onto the one scope-plan
|
|
4549
|
-
// namespace. When namespaces are DISABLED the router collapses every
|
|
4550
|
-
// namespace to one store, so leaving the override undefined preserves the
|
|
4551
|
-
// existing single-store routing byte-for-byte.
|
|
4552
|
-
const writeNamespaceOverride =
|
|
4553
|
-
resolveNamespaceCapabilities(this.orchestrator.config).namespaces === true
|
|
4554
|
-
? writeNamespace
|
|
4555
|
-
: undefined;
|
|
4556
|
-
// Pin provenance PRINCIPAL to the scope plan's resolved principal (#1505
|
|
4557
|
-
// thread 1). The scope plan already applied auth precedence
|
|
4558
|
-
// (authenticatedPrincipal/principalOverride > resolvePrincipal(original
|
|
4559
|
-
// sessionKey)), so this is the same identity the surface authorized — never
|
|
4560
|
-
// a `default` fallback parsed from a prefixed key. Omitted when no principal
|
|
4561
|
-
// resolved (namespaces-disabled / unauthenticated single-store), preserving
|
|
4562
|
-
// existing behavior.
|
|
4563
|
-
const principalOverride =
|
|
4564
|
-
typeof scope.principal === "string" && scope.principal.length > 0
|
|
4565
|
-
? scope.principal
|
|
4566
|
-
: undefined;
|
|
4567
|
-
// Fire-and-forget: queue extraction in the background so the HTTP
|
|
4568
|
-
// response returns immediately. LCM archival (above) is also
|
|
4569
|
-
// enqueue-only; extraction involves LLM calls that can take
|
|
4570
|
-
// minutes under load and should not block the caller.
|
|
4571
|
-
//
|
|
4572
|
-
// Backpressure: the orchestrator's own extraction queue already
|
|
4573
|
-
// limits concurrency (one extraction at a time per session via
|
|
4574
|
-
// queueBufferedExtraction). Fire-and-forget here just decouples
|
|
4575
|
-
// the HTTP response from the queue drain.
|
|
4576
|
-
try {
|
|
4577
|
-
const extractionPromise = this.orchestrator.ingestReplayBatch(turns, {
|
|
4578
|
-
archiveLcm: false,
|
|
4579
|
-
writeNamespaceOverride,
|
|
4580
|
-
principalOverride,
|
|
4581
|
-
});
|
|
4582
|
-
extractionPromise.catch((err) => {
|
|
4583
|
-
log.error(`access-observe background extraction failed: ${err}`);
|
|
4584
|
-
});
|
|
4585
|
-
extractionQueued = true;
|
|
4586
|
-
} catch (err) {
|
|
4587
|
-
// Synchronous enqueue failure (e.g. orchestrator disposed)
|
|
4588
|
-
log.error(`access-observe extraction enqueue failed: ${err}`);
|
|
4589
|
-
}
|
|
4590
|
-
}
|
|
4591
|
-
|
|
4592
|
-
log.info(
|
|
4593
|
-
`access-observe namespace=${namespace} effectiveNamespace=${writeNamespace} sessionKey=${request.sessionKey} messages=${request.messages.length} lcm=${lcmArchived} extraction=${extractionQueued}`,
|
|
4054
|
+
return this.accessObserveWriteSurface.runObserve(
|
|
4055
|
+
request,
|
|
4594
4056
|
);
|
|
4595
|
-
|
|
4596
|
-
return {
|
|
4597
|
-
accepted: request.messages.length,
|
|
4598
|
-
sessionKey: request.sessionKey,
|
|
4599
|
-
namespace,
|
|
4600
|
-
effectiveNamespace: writeNamespace,
|
|
4601
|
-
scopeDebug: {
|
|
4602
|
-
principal: scope.principal,
|
|
4603
|
-
explicitNamespace: scope.explicitNamespace,
|
|
4604
|
-
baseNamespace: scope.baseNamespace,
|
|
4605
|
-
writeNamespace: scope.writeNamespace,
|
|
4606
|
-
codingOverlayApplied: scope.codingOverlayApplied,
|
|
4607
|
-
readNamespaces: scope.readNamespaces,
|
|
4608
|
-
scopeProfile: scope.scopeProfile,
|
|
4609
|
-
writeLayer: scope.writeLayer,
|
|
4610
|
-
layers: scope.layers,
|
|
4611
|
-
promotionTargets: scope.promotionTargets,
|
|
4612
|
-
},
|
|
4613
|
-
lcmArchived,
|
|
4614
|
-
extractionQueued,
|
|
4615
|
-
};
|
|
4616
4057
|
}
|
|
4617
4058
|
|
|
4618
4059
|
async lcmSearch(request: EngramAccessLcmSearchRequest): Promise<EngramAccessLcmSearchResponse> {
|
|
@@ -5205,68 +4646,9 @@ export class EngramAccessService {
|
|
|
5205
4646
|
tags?: string[];
|
|
5206
4647
|
dueAt?: string;
|
|
5207
4648
|
}): Promise<unknown> {
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
const asPriority = (v?: string) => (v && PRIORITIES.has(v) ? v as "low" | "medium" | "high" : undefined);
|
|
5212
|
-
|
|
5213
|
-
const storage = new WorkStorage(this.orchestrator.config.memoryDir);
|
|
5214
|
-
await storage.ensureDirectories();
|
|
5215
|
-
const action = request.action;
|
|
5216
|
-
|
|
5217
|
-
if (action === "create") {
|
|
5218
|
-
if (!request.title?.trim()) throw new EngramAccessInputError("title is required for create");
|
|
5219
|
-
const task = await storage.createTask({
|
|
5220
|
-
title: request.title,
|
|
5221
|
-
description: request.description,
|
|
5222
|
-
status: asStatus(request.status),
|
|
5223
|
-
priority: asPriority(request.priority),
|
|
5224
|
-
owner: request.owner?.trim() || undefined,
|
|
5225
|
-
assignee: request.assignee?.trim() || undefined,
|
|
5226
|
-
projectId: request.projectId?.trim() || undefined,
|
|
5227
|
-
tags: request.tags,
|
|
5228
|
-
dueAt: request.dueAt?.trim() || undefined,
|
|
5229
|
-
});
|
|
5230
|
-
return { action, task };
|
|
5231
|
-
}
|
|
5232
|
-
if (action === "get") {
|
|
5233
|
-
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for get");
|
|
5234
|
-
return { action, task: await storage.getTask(request.id) };
|
|
5235
|
-
}
|
|
5236
|
-
if (action === "list") {
|
|
5237
|
-
const tasks = await storage.listTasks({
|
|
5238
|
-
status: asStatus(request.status),
|
|
5239
|
-
owner: request.owner?.trim() || undefined,
|
|
5240
|
-
assignee: request.assignee?.trim() || undefined,
|
|
5241
|
-
projectId: request.projectId?.trim() || undefined,
|
|
5242
|
-
});
|
|
5243
|
-
return { action, count: tasks.length, tasks };
|
|
5244
|
-
}
|
|
5245
|
-
if (action === "update") {
|
|
5246
|
-
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for update");
|
|
5247
|
-
const patch: Record<string, unknown> = {};
|
|
5248
|
-
if (request.title !== undefined) patch.title = request.title;
|
|
5249
|
-
if (request.description !== undefined) patch.description = request.description;
|
|
5250
|
-
const st = asStatus(request.status); if (st) patch.status = st;
|
|
5251
|
-
const pr = asPriority(request.priority); if (pr) patch.priority = pr;
|
|
5252
|
-
if (request.owner !== undefined) patch.owner = request.owner || null;
|
|
5253
|
-
if (request.assignee !== undefined) patch.assignee = request.assignee || null;
|
|
5254
|
-
if (request.projectId !== undefined) patch.projectId = request.projectId || null;
|
|
5255
|
-
if (request.tags) patch.tags = request.tags;
|
|
5256
|
-
if (request.dueAt !== undefined) patch.dueAt = request.dueAt || null;
|
|
5257
|
-
return { action, task: await storage.updateTask(request.id, patch as any) };
|
|
5258
|
-
}
|
|
5259
|
-
if (action === "transition") {
|
|
5260
|
-
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for transition");
|
|
5261
|
-
const st = asStatus(request.status);
|
|
5262
|
-
if (!st) throw new EngramAccessInputError("valid status is required for transition");
|
|
5263
|
-
return { action, task: await storage.transitionTask(request.id, st) };
|
|
5264
|
-
}
|
|
5265
|
-
if (action === "delete") {
|
|
5266
|
-
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for delete");
|
|
5267
|
-
return { action, deleted: await storage.deleteTask(request.id) };
|
|
5268
|
-
}
|
|
5269
|
-
throw new EngramAccessInputError(`Unsupported work_task action: ${action}`);
|
|
4649
|
+
return this.accessObserveWriteSurface.workTask(
|
|
4650
|
+
request,
|
|
4651
|
+
);
|
|
5270
4652
|
}
|
|
5271
4653
|
|
|
5272
4654
|
async workProject(request: {
|
|
@@ -5280,53 +4662,9 @@ export class EngramAccessService {
|
|
|
5280
4662
|
taskId?: string;
|
|
5281
4663
|
projectId?: string;
|
|
5282
4664
|
}): Promise<unknown> {
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
const storage = new WorkStorage(this.orchestrator.config.memoryDir);
|
|
5287
|
-
await storage.ensureDirectories();
|
|
5288
|
-
const action = request.action;
|
|
5289
|
-
|
|
5290
|
-
if (action === "create") {
|
|
5291
|
-
if (!request.name?.trim()) throw new EngramAccessInputError("name is required for create");
|
|
5292
|
-
const project = await storage.createProject({
|
|
5293
|
-
name: request.name,
|
|
5294
|
-
description: request.description,
|
|
5295
|
-
status: asStatus(request.status),
|
|
5296
|
-
owner: request.owner?.trim() || undefined,
|
|
5297
|
-
tags: request.tags,
|
|
5298
|
-
});
|
|
5299
|
-
return { action, project };
|
|
5300
|
-
}
|
|
5301
|
-
if (action === "get") {
|
|
5302
|
-
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for get");
|
|
5303
|
-
return { action, project: await storage.getProject(request.id) };
|
|
5304
|
-
}
|
|
5305
|
-
if (action === "list") {
|
|
5306
|
-
const projects = await storage.listProjects();
|
|
5307
|
-
return { action, count: projects.length, projects };
|
|
5308
|
-
}
|
|
5309
|
-
if (action === "update") {
|
|
5310
|
-
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for update");
|
|
5311
|
-
const patch: Record<string, unknown> = {};
|
|
5312
|
-
if (request.name !== undefined) patch.name = request.name;
|
|
5313
|
-
if (request.description !== undefined) patch.description = request.description;
|
|
5314
|
-
const st = asStatus(request.status); if (st) patch.status = st;
|
|
5315
|
-
if (request.owner !== undefined) patch.owner = request.owner || null;
|
|
5316
|
-
if (request.tags) patch.tags = request.tags;
|
|
5317
|
-
return { action, project: await storage.updateProject(request.id, patch as any) };
|
|
5318
|
-
}
|
|
5319
|
-
if (action === "delete") {
|
|
5320
|
-
if (!request.id?.trim()) throw new EngramAccessInputError("id is required for delete");
|
|
5321
|
-
return { action, deleted: await storage.deleteProject(request.id) };
|
|
5322
|
-
}
|
|
5323
|
-
if (action === "link_task") {
|
|
5324
|
-
if (!request.taskId?.trim() || !request.projectId?.trim()) {
|
|
5325
|
-
throw new EngramAccessInputError("taskId and projectId are required for link_task");
|
|
5326
|
-
}
|
|
5327
|
-
return { action, linked: await storage.linkTaskToProject(request.taskId, request.projectId) };
|
|
5328
|
-
}
|
|
5329
|
-
throw new EngramAccessInputError(`Unsupported work_project action: ${action}`);
|
|
4665
|
+
return this.accessObserveWriteSurface.workProject(
|
|
4666
|
+
request,
|
|
4667
|
+
);
|
|
5330
4668
|
}
|
|
5331
4669
|
|
|
5332
4670
|
async workBoard(request: {
|
|
@@ -5335,26 +4673,9 @@ export class EngramAccessService {
|
|
|
5335
4673
|
snapshotJson?: string;
|
|
5336
4674
|
linkToMemory?: boolean;
|
|
5337
4675
|
}): Promise<unknown> {
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
const projectId = request.projectId?.trim() || undefined;
|
|
5342
|
-
|
|
5343
|
-
if (action === "export_markdown") {
|
|
5344
|
-
const markdown = await exportWorkBoardMarkdown({ memoryDir, projectId });
|
|
5345
|
-
return { action, markdown: wrapWorkLayerContext(markdown, { linkToMemory: request.linkToMemory === true }) };
|
|
5346
|
-
}
|
|
5347
|
-
if (action === "export_snapshot") {
|
|
5348
|
-
const snapshot = await exportWorkBoardSnapshot({ memoryDir, projectId });
|
|
5349
|
-
return { action, snapshot };
|
|
5350
|
-
}
|
|
5351
|
-
if (action === "import_snapshot") {
|
|
5352
|
-
if (!request.snapshotJson?.trim()) throw new EngramAccessInputError("snapshotJson is required for import_snapshot");
|
|
5353
|
-
const snapshot = JSON.parse(request.snapshotJson);
|
|
5354
|
-
const result = await importWorkBoardSnapshot({ memoryDir, snapshot, projectId });
|
|
5355
|
-
return { action, result };
|
|
5356
|
-
}
|
|
5357
|
-
throw new EngramAccessInputError(`Unsupported work_board action: ${action}`);
|
|
4676
|
+
return this.accessObserveWriteSurface.workBoard(
|
|
4677
|
+
request,
|
|
4678
|
+
);
|
|
5358
4679
|
}
|
|
5359
4680
|
|
|
5360
4681
|
// ── Shared Context / Compounding ────────────────────────────────────────
|
|
@@ -5767,76 +5088,9 @@ export class EngramAccessService {
|
|
|
5767
5088
|
sourcePrompt?: string;
|
|
5768
5089
|
dryRun?: boolean;
|
|
5769
5090
|
}): Promise<unknown> {
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
"store_note",
|
|
5773
|
-
"update_note",
|
|
5774
|
-
"create_artifact",
|
|
5775
|
-
"summarize_node",
|
|
5776
|
-
"discard",
|
|
5777
|
-
"link_graph",
|
|
5778
|
-
]);
|
|
5779
|
-
if (!actionTypes.has(request.action as MemoryActionType)) {
|
|
5780
|
-
throw new EngramAccessInputError(
|
|
5781
|
-
`memory_action_apply: invalid action ${JSON.stringify(request.action)}`,
|
|
5782
|
-
);
|
|
5783
|
-
}
|
|
5784
|
-
|
|
5785
|
-
if (resolveCompressionCapabilities(this.orchestrator.config).contextCompressionActions !== true) {
|
|
5786
|
-
throw new EngramAccessInputError(
|
|
5787
|
-
"memory_action_apply is disabled; enable contextCompressionActionsEnabled to use this tool",
|
|
5788
|
-
);
|
|
5789
|
-
}
|
|
5790
|
-
|
|
5791
|
-
const outcome = request.outcome ?? "skipped";
|
|
5792
|
-
if (outcome !== "applied" && outcome !== "skipped" && outcome !== "failed") {
|
|
5793
|
-
throw new EngramAccessInputError(
|
|
5794
|
-
`memory_action_apply: outcome must be "applied", "skipped", or "failed"; got ${JSON.stringify(outcome)}`,
|
|
5795
|
-
);
|
|
5796
|
-
}
|
|
5797
|
-
|
|
5798
|
-
const resolvedNs = this.writableNamespaceFor(
|
|
5799
|
-
request.namespace,
|
|
5800
|
-
request.sessionKey,
|
|
5801
|
-
request.principal,
|
|
5091
|
+
return this.accessObserveWriteSurface.memoryActionApply(
|
|
5092
|
+
request,
|
|
5802
5093
|
);
|
|
5803
|
-
const inputSummaryParts = [
|
|
5804
|
-
request.content,
|
|
5805
|
-
request.category ? `category=${request.category}` : undefined,
|
|
5806
|
-
request.linkTargetId ? `linkTargetId=${request.linkTargetId}` : undefined,
|
|
5807
|
-
request.linkType ? `linkType=${request.linkType}` : undefined,
|
|
5808
|
-
typeof request.linkStrength === "number"
|
|
5809
|
-
? `linkStrength=${request.linkStrength}`
|
|
5810
|
-
: undefined,
|
|
5811
|
-
request.artifactType ? `artifactType=${request.artifactType}` : undefined,
|
|
5812
|
-
typeof request.execute === "boolean" ? `execute=${request.execute}` : undefined,
|
|
5813
|
-
].filter((part): part is string => typeof part === "string" && part.length > 0);
|
|
5814
|
-
|
|
5815
|
-
const event = {
|
|
5816
|
-
action: request.action as MemoryActionType,
|
|
5817
|
-
outcome: outcome as MemoryActionOutcome,
|
|
5818
|
-
namespace: resolvedNs,
|
|
5819
|
-
actor: "access.memory_action_apply",
|
|
5820
|
-
subsystem: "access.memory_action_apply",
|
|
5821
|
-
reason: request.reason,
|
|
5822
|
-
memoryId: request.memoryId,
|
|
5823
|
-
sourceSessionKey: request.sessionKey,
|
|
5824
|
-
inputSummary: inputSummaryParts.length > 0
|
|
5825
|
-
? inputSummaryParts.join(" | ").slice(0, 500)
|
|
5826
|
-
: undefined,
|
|
5827
|
-
dryRun: request.dryRun === true,
|
|
5828
|
-
promptHash:
|
|
5829
|
-
typeof request.sourcePrompt === "string" && request.sourcePrompt.length > 0
|
|
5830
|
-
? createHash("sha256").update(request.sourcePrompt).digest("hex")
|
|
5831
|
-
: undefined,
|
|
5832
|
-
};
|
|
5833
|
-
const preview = this.orchestrator.previewMemoryActionEvent(event);
|
|
5834
|
-
if (request.dryRun === true) {
|
|
5835
|
-
return { recorded: false, dryRun: true, event: preview };
|
|
5836
|
-
}
|
|
5837
|
-
|
|
5838
|
-
const recorded = await this.orchestrator.appendMemoryActionEvent(event);
|
|
5839
|
-
return { recorded, event: preview };
|
|
5840
5094
|
}
|
|
5841
5095
|
|
|
5842
5096
|
async contextCheckpoint(request: {
|