/** * `SessionPermissionRules` 的 **wire 形读者** —— 两条写入腿(`PUT /v1/sessions/:id/policy` 与 * `POST /v1/sessions/:id/sync/import` 的 `policy` 面)**唯一**的形判据。 * * ── 为什么是一只共享读者(S-496 / [ref],取证 P4 + S-498)─────────────────────────────────────────── * 改前两条腿各自为政: * · PUT 腿在 `http/routes/sessions.ts` 里**手抄**五桶键名(`["toolAllow","toolDeny","allowDirs", * "commandAllow","commandDeny"] as const`),core 的 `SessionPermissionRules` 加第六桶 ⇒ 那一桶在 * PUT 上被**静默丢**、200 回执却说「已生效」; * · sync/import 腿(S-498)只判 `Array.isArray(body.policy)` **外层**,逐条记录的 `principal` / `rules` * 一个字节都不校验就裸交 `replayPolicyRecords` → `putRules`。坏形走的是**放宽**方向,亲读 core * `session-policy-store.js` 两处实现: * · `normalizeRules` 对每桶做 `[...rules.toolDeny]` ⇒ 一个**字符串** `"rm"` 被展开成 `["r","m"]`, * 本来该拒 `rm` 的 deny 规则变成拒两个不存在的命令名 = 门整条失效; * · `allowDirs` 走 `.map(...)` ⇒ 字符串上没有 `.map` ⇒ **TypeError → 500**(路由头注 `:300-302` * 自陈「Malformed shape → 400(never a downstream TypeError)」,这一半没守); * · `loosenReasons` 的每一臂都从 `prior` 出发,**prior 为空时恒回空集** ⇒ tighten-only 门对一条 * 全新的会话放行任何内容,坏形因此连「收紧门」这一层都碰不到。 * ⇒ 类修:一只读者,键集由 `keyof SessionPermissionRules` **派生**(下方 `RULE_FIELD_PRESENCE` 的 * `satisfies Record`:core 加桶 ⇒ 缺键**编译红**;core 删桶 ⇒ * 多键**编译红**),两条腿同调。 * * ── 文案也在这里(codex 一轮追加:两条腿各铸一份句子 = 第二份手抄)─────────────────────────────── * 初版把 wire 文案留在两个路由各自铸,理由是错误文案冻结门(`test/api-error-text-freeze.test.ts`)锚的是 * `src/http/` 桶里的 `sendError` 站点。codex 一轮指出那等于**把同一句话写了两遍**(`sessions.ts` 与 * `session-sync.ts` 各一份),这正是本批要消灭的形。改法照本仓既有成案(S-405 / [ref] / [ref] 三处 * 「常量锚」):句子收进本文件**单一属主**,冻结职责搬到冻结门的**常量锚格** —— 那一格直接断言这两只 * 渲染器对每一种判词的输出字节,且对判词闭集穷尽(加一种判词 ⇒ 那一格编译红)。文案一个字节未改。 */ import type { SessionPermissionRules, SessionPolicyError, SessionRulesRecord } from "@sema-agent/core"; /** core `SessionPermissionRules` 的桶名全集(派生,不是手抄)。 */ export declare const SESSION_PERMISSION_RULE_FIELDS: readonly (keyof SessionPermissionRules)[]; /** 一条规则体的形问题。判词而非文案(见文件头注的「文案属主」一段)。 */ export type SessionPermissionRulesProblem = /** 整体不是一只普通对象(`null` / 数组 / 标量 / 缺席)。 */ { kind: "not-an-object"; } /** 某一桶在场但不是 `string[]`(字符串 / 数字 / 混杂数组都在此)。 */ | { kind: "field-not-string-array"; field: keyof SessionPermissionRules; }; /** {@link readSessionPermissionRules} 的读数:坏形给判词,好形给**已收窄**的规则体(调用方零 cast)。 */ export type SessionPermissionRulesRead = { problem: SessionPermissionRulesProblem; rules?: undefined; } | { problem?: undefined; rules: SessionPermissionRules; }; /** * 读一只 wire 上的 `SessionPermissionRules`:形不对给判词,形对给**只含五桶**的规则体。 * * 认识面窄得刻意:**只判形**,不判内容(桶内字符串的 pattern 文法、路径是否可达、tighten-only 是否成立 * 都不在这里 —— 前两者归 core 的 rule 文法与 `normalizeRules`,后者归 `putRules` 的收紧门)。 * 未知键**照旧忽略**(不是新的拒因):core 的 `normalizeRules` 本来就只搬这五桶,本读者与它同口径。 */ export declare function readSessionPermissionRules(raw: unknown): SessionPermissionRulesRead; /** 一条 `SessionRulesRecord` 的形问题(`policy[]` 的元素级;桶级问题原样转载在 `rules` 位)。 */ export type SessionRulesRecordProblem = /** `policy` 本身不是数组。 */ { kind: "not-an-array"; } /** 第 `index` 条记录不是一只普通对象。 */ | { kind: "record-not-an-object"; index: number; } /** 第 `index` 条的 `principal` 在场但不是字符串(**缺席**才是「会话级默认行」的合法写法 —— 两个店的 * `listBySession` 对无 principal 的行都产 `undefined`,JSON 序列化后即缺席,所以 `null` 不在受理面)。 */ | { kind: "principal-not-a-string"; index: number; } /** 第 `index` 条的 `rules` 形不对(转载桶级判词)。 */ | { kind: "rules"; index: number; problem: SessionPermissionRulesProblem; }; /** {@link readSessionRulesRecords} 的读数。 */ export type SessionRulesRecordsRead = { problem: SessionRulesRecordProblem; records?: undefined; } | { problem?: undefined; records: SessionRulesRecord[]; }; /** * 读 sync/import 的 `policy` 面(`SessionRulesRecord[]`)。好形返回**已收窄**的记录数组,坏形给判词。 * * `rev`:记录里带的 `rev` 是**导出端的**乐观锁读数,`putRules` 侧一进门就 `stripRev` 掉(`sameRulesContent` * 也比的是去 rev 的字节形),所以它在本读者里只是过境位 —— 非数字即当缺席(不是拒因:一个坏 rev 改变不了 * 任何一次写入的结果,拒它只会把合法同步砖死)。 */ export declare function readSessionRulesRecords(raw: unknown): SessionRulesRecordsRead; /** 桶级判词 → 人话。闭集穷尽:{@link SessionPermissionRulesProblem} 加一种判词 ⇒ 本 switch 编译红。 */ export declare function describeSessionPermissionRulesProblem(p: SessionPermissionRulesProblem): string; /** `policy[]` 判词 → 人话(sync/import 腿在它前面拼 `policy: `)。闭集穷尽同上。 */ export declare function describeSessionRulesRecordProblem(p: SessionRulesRecordProblem): string; /** * 🔴 **`SessionPolicyError` 的闭集 → HTTP 的**唯一**映射(core 7.25.0 提货批新建)。** * * 此前这张映射在两条路由上各写了一个**二元三元表达式**:`sessions.ts` 写 `conflict ? 409 : 403`、 * `session-sync.ts` 写 `loosen_forbidden ? 403 : 409`。两句话在两个词的世界里都对,而 core 这一版给闭集 * 加了第三个词 `corrupt`(店读不出自己写的记号/行 ⇒ 拒写,而不是按 0 续号)—— 于是同一个新词在两条路由上 * 拿到**两个不同的、都不对的**答案:一边 403「你没权限」,一边 409「有人先改了」,而它其实是一次 * **数据完整性故障**。这正是 [ref] 词表纪律说的那一形:闭集必须穷尽 `switch`,未知词是**编译**错, * 不是运行期落进某个恰好存在的臂。 * * ⇒ 映射搬到这里(本文件已是 policy wire 形的单一读者/属主,S-496 先例),两条路由同调它。 * 新增一个词 ⇒ 本 switch 编译红,由人裁定它的状态码与码位,而不是让它静默继承邻居的。 * * 逐词: * · `conflict`(409)—— CAS 输了,重读 `rev` 再来。**码位与状态逐字不变**。 * · `loosen_forbidden`(403)—— 非 operator 想放松。**码位与状态逐字不变**。 * · `corrupt`(**500**)—— 店读不出它自己该读的那份字节(记号/行被列外篡改或撕裂)。 * 不是 4xx:调用方没做错任何事,它也改不了这件事;不是 503:重试改不了判(core 成文的修复路径是 * **人**去改回或删掉那一行,而删记号要交出它握着的那个号)。 */ export declare function sessionPolicyErrorHttp(code: SessionPolicyError["code"]): { status: number; errorCode: string; }; //# sourceMappingURL=session-policy-wire.d.ts.map