/** * [ref] 件2/件3 —— 记忆面**姿态**的单一推导点。 * * ## 病 * * 「这台 worker 的记忆点亮没有 / 跑在哪个后端 / 检索在哪一档」这三件事此前只活在启动日志 * (`memory_engine_enabled`)里。日志不是接口:不可查询、跨副本不可聚合、也没有任何门保证它还在。 * 于是消费端(cli 的记忆 UX)与运维只能读日志、或者按 env 自己推 —— 而 env 推不出来:`MEMORY_ENGINE=on` * 的部署在多租户 file 底座上是**暗的**,`MEMORY_ENGINE_BACKEND=pg` 的部署检索档还要看 embedder 在不在。 * * ## 药 * * 一次推导,两个读面:operator 面(`GET /v1/diagnostics/wiring` 的 `memoryPosture` 段,全量)与消费端面 * (`GET /v1/capabilities` 的 `memoryEngine` 位,窄投影)。两面同源是本件的全部意义 —— 「诊断页说点亮、 * 能力位说没有」这种自相矛盾在结构上不可能发生。 * * ## 两条诚实纪律 * * ① **档位取实例真值,不按后端推断**([ref] 裁①/[ref]①的延续):`vectorMode` 是「后端上限 × embedder * 在场」的推断结果,两支 SQL 后端各自的实例 getter 才是真值;file 引擎**根本没有**这个指示面 * (core 的 `MemoryBackend` 契约里没有 `vectorMode`,亲验),所以它报 `null` —— 「没有这个指示面」与 * 「档位是词面」是两件事,折成一件就是编一个数出来。 * ② **dark 成因是闭集词**:散文归日志(它的读者是人),机器读面要词(它的读者是 cli 的分支)。 */ import type { ServiceConfig } from "./config-types.js"; /** 检索档位(两支 SQL 后端的实例 getter 值域;file 腿无此面)。 */ export type MemoryVectorRung = "native" | "portable" | "lexical"; /** 记忆面**暗**的成因(闭集;三条各对应装配期的一道门)。 */ export type MemoryDarkReason = /** operator 显式关停(`MEMORY_ENGINE=off`)。 */ "engine_off" /** 多租户 + file 底座:file 基座无租户隔离,fail-closed 不点亮(改 `MEMORY_ENGINE_BACKEND=pg|tidb`)。 */ | "multi_tenant_file_backend" /** 平面分裂车道:模型的文件工具在沙箱 fs、引擎在 worker fs,harvest 看不到沙箱写入 ⇒ fail-closed。 */ | "remote_lane_split"; /** 部署的记忆面姿态(operator 面全量下发;能力面取其窄投影)。 */ export interface MemoryPosture { /** 引擎**真的**接上了吗(= `RunnerDeps.memoryBackend` 在场),不是「配置里写了什么」。 */ enabled: boolean; /** 声明的后端方言。与点亮态**正交**:配了 pg 而引擎暗时这一位仍是 pg(配置事实与装配事实分开报)。 */ backend: ServiceConfig["memoryEngineBackend"]; /** 检索档位;`null` = 这条腿没有档位指示面(file 引擎),**不是**「档位是 lexical」。 */ vectorMode: MemoryVectorRung | null; /** 暗的成因;点亮时恒 `null`(两者同时在场即自相矛盾)。 */ darkReason: MemoryDarkReason | null; /** 多租户姿态(file 底座恒暗那条判据的输入,也是消费端判「这台的记忆是不是按租户分区」的依据)。 */ multiTenant: boolean; /** embedder 身份(pg 向量档才可能在场)。**只报 model/dimensions**:端点与 key 是凭证面,不进读面。 */ embedder: { model: string; dimensions: number; } | null; /** * [ref](core 5.45.0 [ref]):本部署给委派臂设的**证据标准**(`MEMORY_DELEGATION_EVIDENCE`)。 * * 🔴 `null` = **本部署没设这个键** ⇒ 引擎按它自己的缺省跑。**不是**「等于 static-face」—— 那句话是 * 关于 core 缺省的知识声明,本读面不替 core 宣布它(`vectorMode: null` 同一条诚实纪律: * 「没有这个指示面」与「档位是词面」是两件事)。运维要知道「实际生效的是哪一档」,读这一位 + * 实装 core 的契约,两件事分开摆。 * * 受众 = **operator 面独占**:与 `darkReason`/`multiTenant`/embedder 身份同判([ref] 件3 的分界), * 刻意**不**进 `GET /v1/capabilities` 的窄投影 —— 证据标准回答的是「这台机器怎么判污染」,是运维 * 诊断料,不是任何持凭证调用方该读的位。 */ delegationEvidence: "static-face" | "attested-only" | null; /** * [ref] 件4(core 5.46.0 [ref] §13-3):本部署的 **provenance 姿态**(`MEMORY_PROVENANCE`)—— * 一个会话被判为已暴露之后,它的记忆写是**带标收录**(`carry`)还是**整体隔离**(`off`)。 * * 🔴 `null` = **本部署没设这个键** ⇒ 引擎按它自己的缺省跑。**不是**「等于 carry」—— * 与 `delegationEvidence` / `vectorMode` 同一条诚实纪律:本读面不替 core 宣布它的缺省。 * * 受众 = **operator 面独占**(与 `delegationEvidence` 同判):它回答的是「这台机器对暴露会话的写 * 怎么处置」,是运维诊断料,刻意不进 `GET /v1/capabilities` 的窄投影。 */ provenance: "off" | "carry" | null; /** * [ref] §3.1(core 7.0.0 片3,[ref] S-1b):本部署的 **capture 姿态**(`MEMORY_CAPTURE_POLICY`)—— * per-principal `allowMemoryOptOut` verdict 被读进的那个框(`open` fail-safe / `governed` fail-closed / * `capture-required` 全拒)。 * * 🔴 `null` = **本部署没设这个键** ⇒ 引擎按它自己的缺省跑。**不是**「等于 open」—— * 与 `provenance` / `delegationEvidence` 同一条诚实纪律:本读面不替 core 宣布它的缺省。 * * 受众 = **operator 面独占**(同上两位):它回答的是「这台机器怎么裁 opt-out 声明」,是运维诊断料, * 刻意不进 `GET /v1/capabilities` 的窄投影。 */ capturePolicy: "open" | "governed" | "capture-required" | null; } /** 推导入参 = 装配现场的真事实(不在本函数里重算任何一件)。 */ export interface MemoryPostureFacts { config: Pick; /** 引擎这一腿真的接上了吗(`memoryEngine !== undefined`)—— 装配结果。 */ engineWired: boolean; /** 两支 SQL 后端从**实例 getter** 读到的档位;file 腿传 `undefined`(无此面)。 */ vectorMode: MemoryVectorRung | undefined; /** 车道门判 dark 了吗(`memoryEngineRemoteLanePosture(config)?.posture === "dark"`)。 */ remoteLaneDark: boolean; } /** * 纯推导(`build*`:产纯数据)。成因的判序是**显式关停 > 租户隔离 > 车道分裂**: * operator 自己关的那一形必须报成 `engine_off`,报成隔离/车道会把人指向一个改了也没用的旋钮。 */ export declare function buildMemoryPosture(facts: MemoryPostureFacts): MemoryPosture; /** * 能力面(`GET /v1/capabilities` 的 `memoryEngine` 位)的**窄投影**:点亮 ⇒ 后端形 + 档位; * 暗 ⇒ `false`(位在场且假 = 「这台认识这一位、本部署没点亮」,与位整个缺席 = 老 server 两回事)。 * * 刻意**不**外泄 `darkReason` / `multiTenant` / embedder 身份:能力面是任何持凭证调用方都能读的面, * 它只需要回答「我能不能用记忆、用的是哪一档」;成因与身份是运维诊断料,归 operator-only 那一面。 */ export declare function projectMemoryEngineCapability(posture: MemoryPosture | undefined): false | { backend: MemoryPosture["backend"]; vectorMode: MemoryVectorRung | null; }; //# sourceMappingURL=memory-posture.d.ts.map