/** * [ref] 件B/C/D([ref] 件1)—— 三个**部署治理座席**的装配点:把 core 5.13.0 起就存在、本仓一直 * 零接线的三个 `RunnerDeps` seam 接上。 * * · 件B `compliancePostureResolver` —— per-principal 合规档位否决(闭集 profile × 闭集 capability; * 生效 deny 集 = `BUILTIN_COMPLIANCE_DENIES[profile] ∪ additionalDenies`,**供数只能收紧**)。 * · 件C `lockedConfig` —— 管理员锁定层的**声明**(闭集注册表;spec 占了锁着的字段 ⇒ core 整拒 prepare)。 * · 件D `retentionPolicy` —— 托管留存期。core **只**拿它做启动期能力校验,引擎在任务路径上从不删数据。 * * ## 真源:为什么三件都只接 env * * [ref] §1 的三层分工表里,center 那一列对 B/C/D 写的都是「**新建**」——中心侧的档位真源 / 策略 * 真源 / 保留期真源**至今不存在**。接一条 center 腿等于造一个恒缺席的假面(件A 的 org 目录能接 center, * 是因为那一面真被建出来了)。所以本装配点的供数只有部署配置面,并且这**恰好**是 [ref] §4.3 对 * 「谁能解锁」的裁定:锁是**配置期物**,铸锁真源只有 center governance 域与部署 env,二者都是 operator * 控制的配置面,自带各自既有鉴权 —— 「谁能解锁」≡「谁能改部署配置」,不引入新鉴权面。 * * ## 件B 的窄化:档位是**部署级**,不是 per-principal * * core 的座席签名按 principal 取值,本仓的 resolver 对每个 principal 返回同一份部署档位。这是诚实的窄 * 实现:per-principal 档位需要 center 的下发面(§3.2 R5「与 caps 同一次 fetch」),那一面尚未建。 * 座席在场即恒生效,不存在「某些 principal 悄悄没档位」的分岔。 * * ## 件B 的**不采纳**:多租户缺解析器不拒启(显式定界) * * §3.4 建议「部署自称多租户却未配 B 的解析器 ⇒ 拒启动」。**本仓不采纳**,理由是三问里的第二问: * 谁被伤到 —— 今天每一个 `REQUIRE_PRINCIPAL=true` 的部署都没有这一格,采纳即是让它们**全部**在下一次 * 升级时起不来,而它们并没有任何东西变得更不安全(B 缺席 = 无合规限制 = 与今天逐字相同的行为)。 * 补偿是这条 env 旋钮本身:要档位的部署显式写下它。将来 center 真有了档位下发面,「多租户必须能解析出 * 档位」才是一条有对象可指的判据,那时再立。 * * ## 件C 的两条**装配相容性**判据(本仓特有,见 {@link assertLockAssemblable}) * * core 的锁按「spec 是否占了这个字段」判,而本仓是**部署自己**在铸那两个 spec 字段: * · `spec.toolPolicy` —— 治理拍(`applyRuntimeGovernance`)在**任何**客户端表态下都会铸出它(敏感路径 * 守卫集默认非空);于是 `toolPolicy` 锁会让 core 的 preflight **整拒每一条腿**。 * · `spec.mcp` —— 部署自带的 MCP 基线(场景/center 下发)同样经 `spec.mcp` 进 core;部署配了 MCP 又锁 * `mcp`,同样是每条腿必拒。 * 两者都在**启动期**拒(安全控件不得半开 §3.4 C 行),而不是让运维在每条任务腿上各撞一次 * `config.locked_key` 去猜。 * * ## 件D 的边界(成文,别读成「数据会被删」) * * 本仓**没有任何** store 声明 `retention:"managed"`(亲验:session/checkpoint/tool-result 三族 SQL 双生 * 与 local 形都没有这一位),托管留存的执行面(调度/重试/副本协调/逐行审计)按 §5.2 是「server 半场四件 * 皆新建」,不在本批。所以本装配点对件D 只做两件事:①把策略递给 core 的启动期校验 * (`assertRetentionCapability` —— 锁着 + 店不能删 ⇒ 拒启,「策略锁着、数据永存」不许发生);②策略在场 * 而没有任何 managed 店时打一条响亮 warn。**不**假装有执行面。 */ import { type LockedKey, type RunnerDeps } from "@sema-agent/core"; import type { ServiceConfig } from "../config.js"; import type { Logger } from "../observability/logger.js"; /** 装配产物:三个 core 座席 + 已校验的锁集(同步拒面 `assertRequestMcpUnlocked` 读它)。 */ export interface GovernanceSeams { compliancePostureResolver: RunnerDeps["compliancePostureResolver"]; lockedConfig: RunnerDeps["lockedConfig"]; retentionPolicy: RunnerDeps["retentionPolicy"]; /** 校验过的锁集(空集 = 无锁)。**不是** `lockedConfig` 的复述:core 那一格是给引擎的声明,这一格是 * server 自己的同步拒面判据(§4.3「请求根本不该进 core」),两者同源于一次解析。 */ lockedKeys: ReadonlySet; } /** 依赖收窄到真实消费面(接口隔离 —— 测试用真形字面量,零铸形)。 */ export interface GovernanceSeamsCtx { config: Pick; logger: Pick; /** * core 留存能力校验的对象集(名字进拒启文案)。喂的是**装配现场的真店**,不是「我以为装了什么」—— * 这条校验的全部价值就在于读的是真身上的声明位。 */ retentionStores: ReadonlyArray<{ name: string; store: object | undefined; }>; /** * [ref] 车1 —— **本 build 是否真的接了留存执行器**(sweep lane;车2 的 `startRetentionLane()`)。 * 缺席 = `false`。 * * 🔴 为什么这一位必须存在(codex 交叉复审 R2-[critical],验真后修):core 的 * `assertRetentionCapability` 只读**店的声明位**。[ref] 车1 让三只 SQL 店如实声明了 `"managed"` * (它们的行确实由托管留存删),于是那道门从此**放行** locked policy —— 可执行器还在车2 手里。 * 两者之间的窗口如果什么都不做,后果正是本仓最忌的那种:部署自述「策略已受管」、审计与能力面 * 也这么说,而**没有任何东西在删数据**;车1 之前那条 `retention_policy_not_executed` 的响亮 warn * 还会因为"有 managed 店在场"而**静音**。 * ⇒ 判据改成问「有没有东西真会删」,而不是「有没有店说自己被管」。车2 接上 lane 时把这一位传 true。 */ retentionExecutorWired?: boolean; } /** * 件C 的装配相容性判据(纯,可单测)——见文件头「两条判据」。 * * 🔴 失败方向朝**拒启**:一把在本部署上必然把每条腿都拒掉的锁,不是「配得比较严」,是配错了;继续启动 * 只会让服务对每一个请求说 `config.locked_key`,而运维手里没有任何线索指向那把锁。 */ export declare function assertLockAssemblable(input: { lockedKeys: ReadonlySet; deploymentMcpConfigured: boolean; }): void; /** * 治理面的**装配相容性**总判据(codex 交叉复审 R2-F2 起扩面)——锁与合规档位在这条轴上是同一件事: * 两者都可能把「本部署自己的装配」判成 core 门口的违规,而那不是「配得更严」,是**这台从此每条腿必失败**。 * * 合规半场的判据来自亲核 core 的 prepare 门(`prepare-task` 的 compliance 段):被否决的能力若**已在 * spec 上**,core 是**整拒 prepare**(`config.compliance_denied`),不是「不挂载」。于是: * · `mcp_servers` 被禁 ∧ 部署自带 MCP 基线 ⇒ 每条腿的 `spec.mcp` 都在 ⇒ 必拒; * · `web_fetch` 被禁 ∧ 花名册挂了 WebFetch ⇒ 必拒。⚠️ `hipaa` 的**内建 floor 就含 `web_fetch`**,而本仓 * 的单用户花名册**恒挂** WebFetch(`capabilities/scenarios.ts` 的 roster 过滤只在多租户下摘它)—— * 也就是说「单用户 + hipaa」在本仓是一个每条腿必失败的组合。这条判据的全部价值就在这儿:让它在 * 启动期说出来,而不是让运维看着一台"启动成功、任务全失败"的 worker 猜。 * · `workflows` 被禁只在 `spec.selfOrchestration === true` 的腿上拒(per-task,不是部署恒真)⇒ 不在本 * 判据内,由那条腿自己响亮拒;`org_memory_mount` 走件A 的准入面。**成文定界,不留白。** */ export declare function assertGovernanceAssemblable(input: { lockedKeys: ReadonlySet; /** 已解析的合规否决集(空集 = 无档位)。 */ complianceDenies: ReadonlySet; deploymentMcpConfigured: boolean; /** 本部署的花名册是否会挂 WebFetch(= 单用户形;多租户下 roster 过滤掉它)。 */ webFetchMounted: boolean; }): void; /** * 装配三座席。**拒启口即装配口**:相容性判据与 core 的留存能力校验都在这里跑,调用方不必记得再调一次 * (件A 的 C12 探测同姿势)。 */ export declare function createGovernanceSeams(ctx: GovernanceSeamsCtx): GovernanceSeams; //# sourceMappingURL=governance-seams.d.ts.map