/** * Human-in-the-loop approval gate (F4) — durable-only since 5.0.0 ([ref]/[ref] A-5:OA 轮询车道 * (createOaApprovalPolicy + ApprovalStore pending 行轮询)整体退役;唯一门形 = durable checkpoint * suspend/resume,见 createDurableAskPolicy 与 /v1/approvals 的 checkpointStore 腿)。 */ import { type ToolPolicy } from "@sema-agent/core"; import { type PostureSource } from "./posture-source.js"; /** * 运维**是否表达了门意图** —— boot 的单用户 allow-all 基线只在"零门意图"时才允许铺开, * 所以这个谓词漏一格 = 那一格的意图被 allow-all 静默吞掉。 * * 🔴 由来(2026-07-31 缝合审):这判据本来是 `main.ts` 里的一行内联表达式,只枚举了 * requireApproval / deny / durableApproval **三格**,漏了 `approvalNeverAuto`。漏的根因不是手滑, * 是 `config-types.ts` 当时把 neverAuto 描述成 "Subset of approvalRequire" —— 若真是子集, * 枚举 requireApproval 就覆盖了它。但本文件 inv#2 的执行面逐字相反(neverAuto ⊄ requireApproval), * 于是「单用户 + 只配 APPROVAL_NEVER_AUTO=Bash」这一**语义完全合理**的形被判成零门意图 ⇒ * 落进 `createAllowDenyPolicy({})` 全面放行 ⇒ 那张"永远问人"的表从未进入任何 policy,Bash 直接跑。 * * 判定挪到**与 inv#2 同一个文件**就是为了这个:两者再想漂开,得有人同时改这两段。 * 新增任何"门意图"配置项时,这里必须同步加一格(下面的表驱动测试会点名漏的那格)。 */ export declare function hasOperatorGateIntent(config: { approvalRequire: readonly string[]; approvalDeny: readonly string[]; approvalNeverAuto: readonly string[]; durableApproval: boolean; durableApprovalSource: PostureSource; }): boolean; /** * 门意图不可服务=boot 拒启(全窗复审 D3-F1,HIGH;与 env 墓碑族同谱系)。 * v4.3.0 的轮询门形(APPROVAL_REQUIRE + DB backend + DURABLE_APPROVAL 未设)在 5.0.0 只剩 core 每任务 * warn——**原先拦、现在放**(方向反转)。「留 undefined 让 core warn」对新配置是曝露误配,对升级存量是 * 门静默消失,后者必须响亮。放这文件与 hasOperatorGateIntent/inv#2 同源(意图判据再漂要同时改两段)。 */ export declare function assertGateIntentServiceable(config: { approvalRequire: readonly string[]; approvalDeny: readonly string[]; approvalNeverAuto: readonly string[]; durableApproval: boolean; durableApprovalSource: PostureSource; }, checkpointStorePresent: boolean): void; /** * [ref] 件⑩ —— **center 的 `forceDurableGate` 在无 checkpoint 店的部署上会静默 inert**,补一条 boot 披露。 * * 事实链(core 7.2.0 亲读):`RuntimeCaps.forceDurableGate:true` 让引擎**合成**一个 * `durableApproval{scope: principal}`(`dist/core/types.d.ts`),好让 policy 的 `ask` 挂到 * durable wire 上。但「挂得上去」的前提是这台 worker 真有 checkpoint 店 —— 而本仓的构造条件逐字是 * `backend?.checkpoint && config.durableApproval`(main.ts)。`DURABLE_APPROVAL` 关 ⇒ 店缺席 ⇒ 合成出来 * 的那把锁**没有锁孔**:core 侧退化成非 durable 的 ask 链,center 那条「fleet-wide interactive runs gate」 * 的授权在这台机器上**一个字节都没兑现**,且两侧都不出声(core 侧归 core 的 C-22)。 * * 为什么是 warn 不是拒启:`forceDurableGate` 是 **per-principal** 的授权,boot 期没有 principal,判不出 * 「今天真的会有人被下发」——能判的只有「这台机器的下发口开着(有 entitlement 源)而承接面关着」。 * 拿一个**可能**的下发去挡死一台本来跑得好好的 worker,是本条诊断带来的新故障,不是它治的病 * (与 `noticeAutoCompactAboveWindow` 的同一条取舍)。所以:响亮一行,不拒启。 * * 为什么在 **boot** 判而不是 resolve 时判:两个入参都是**部署级**的(entitlement 源在不在、durable 门开 * 不开),per-principal 的那一半改变不了结论;放 resolve 里就得自己造去重,而 boot 天然只跑一次。 * * 纯函数(返回一行或 undefined,不自己写日志)= 红先测得动;调用点在 main.ts 的 * {@link assertGateIntentServiceable} 之后(同一段门装配)。 */ export declare function buildForceDurableGateInertNotice(input: { /** center per-principal entitlement 源在不在(`centerEntitlementSourceWired` 的结果 —— 判据单源在那里)。 */ entitlementSourceWired: boolean; /** 本进程真建出 checkpoint 店了吗(= `backend?.checkpoint && DURABLE_APPROVAL`)。 */ checkpointStorePresent: boolean; durableApproval: boolean; }): { event: string; fields: Record; } | undefined; /** * Durable approval policy (core 1.67 / [ref]): returns three-state **`ask`** for gated tools instead of * polling. With `TaskSpec.durableApproval` set + a `checkpointStore` on the Runner, core turns that `ask` into * a **suspend** (persists a checkpoint, ends `status:"suspended"`) — no held connection, resumable on any * replica. (The former poll-lane * twin, createOaApprovalPolicy, was retired in 5.0.0 — durable is the only gate.) */ export interface DurableAskOptions { requireApproval: string[]; deny?: string[]; autoBudget?: number; neverAuto?: string[]; /** Per-session approval-exemption probe ("本会话不再询问"): consulted ONLY for require-listed * tools, AFTER deny/neverAuto (both always win) and BEFORE the budget path. Canonical-space toolName. * A rejected promise is treated as NOT exempt (fail-closed → ask) at the call site below. */ exempt?: (canonicalToolName: string) => Promise; /** Audit hook — fired when an exemption short-circuits the ask (the `approval_exempted` log line). */ onExempted?: (canonicalToolName: string, rawToolName: string) => void; } export declare function createDurableAskPolicy(opts: DurableAskOptions): ToolPolicy; //# sourceMappingURL=approval.d.ts.map