@sema-agent/server 7.128.0 → 7.129.0

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.
Files changed (96) hide show
  1. package/MIGRATION.md +118 -0
  2. package/USAGE.md +89 -18
  3. package/dist/approval-card.d.ts +53 -26
  4. package/dist/approval-card.js +40 -11
  5. package/dist/approval-crash-convergence.js +4 -4
  6. package/dist/bake-runner/main.js +2 -1
  7. package/dist/boot/memory-consolidation.d.ts +46 -16
  8. package/dist/boot/memory-consolidation.js +15 -8
  9. package/dist/boot/side-query-lane.d.ts +7 -20
  10. package/dist/boot/side-query-lane.js +0 -24
  11. package/dist/boot/stage-01-config.js +3 -0
  12. package/dist/boot/stage-06-runners.js +1 -1
  13. package/dist/boot/stores.js +2 -2
  14. package/dist/boot/web-admin.js +17 -10
  15. package/dist/boot/webfetch-summarize-lane.d.ts +10 -7
  16. package/dist/boot/webfetch-summarize-lane.js +2 -3
  17. package/dist/brain.js +6 -9
  18. package/dist/capabilities/builtin-tools.d.ts +2 -2
  19. package/dist/capabilities/engine-capabilities.d.ts +14 -0
  20. package/dist/capabilities/engine-capabilities.js +12 -0
  21. package/dist/capability-facts.d.ts +3 -1
  22. package/dist/capability-facts.js +1 -0
  23. package/dist/config-types.d.ts +4 -3
  24. package/dist/egress-redirect.d.ts +12 -0
  25. package/dist/egress-redirect.js +7 -0
  26. package/dist/entry-mode-clamp-notice.d.ts +30 -0
  27. package/dist/entry-mode-clamp-notice.js +13 -0
  28. package/dist/fleet/fleet-bus.d.ts +7 -0
  29. package/dist/fleet/fleet-bus.js +1 -0
  30. package/dist/hooks/hook-llm.js +6 -2
  31. package/dist/host-lsp-manager.d.ts +3 -3
  32. package/dist/http/admission.js +5 -2
  33. package/dist/http/resume-legs.js +46 -27
  34. package/dist/http/route-ctx.d.ts +1 -1
  35. package/dist/http/routes/approvals-assistant.js +6 -4
  36. package/dist/http/routes/capabilities.js +1 -0
  37. package/dist/http/routes/session-sync.js +3 -4
  38. package/dist/http/routes/sessions.js +4 -17
  39. package/dist/http/send.d.ts +1 -1
  40. package/dist/http/send.js +4 -3
  41. package/dist/http/wire-types.d.ts +3 -2
  42. package/dist/index.d.ts +1 -1
  43. package/dist/index.js +1 -1
  44. package/dist/observability/fail-open.d.ts +7 -3
  45. package/dist/observability/fail-open.js +7 -3
  46. package/dist/observability/secret-env-scrub.d.ts +37 -3
  47. package/dist/observability/secret-env-scrub.js +2 -2
  48. package/dist/parked-decide.js +5 -2
  49. package/dist/plugins/memory-embedder.js +5 -0
  50. package/dist/plugins/one-time-migrations.d.ts +180 -9
  51. package/dist/plugins/one-time-migrations.js +315 -3
  52. package/dist/plugins/pg-pool.js +3 -1
  53. package/dist/plugins/remote-env-host.d.ts +17 -20
  54. package/dist/plugins/remote-env-host.js +13 -25
  55. package/dist/plugins/session-policy-store-sql.d.ts +16 -4
  56. package/dist/plugins/session-policy-store-sql.js +17 -20
  57. package/dist/plugins/session-rules-column.d.ts +16 -0
  58. package/dist/plugins/session-rules-column.js +6 -0
  59. package/dist/plugins/tidb-pool.js +4 -1
  60. package/dist/read-face-posture.d.ts +2 -1
  61. package/dist/request-value-closure.js +1 -1
  62. package/dist/retired-card-words.d.ts +17 -0
  63. package/dist/retired-card-words.js +49 -0
  64. package/dist/rule-entry-list.js +2 -0
  65. package/dist/run-local.js +10 -3
  66. package/dist/server-secret-env.d.ts +77 -85
  67. package/dist/server-secret-env.js +40 -26
  68. package/dist/session-policy-wire.d.ts +61 -124
  69. package/dist/session-policy-wire.js +59 -121
  70. package/dist/session-sync.d.ts +8 -6
  71. package/dist/session-sync.js +6 -5
  72. package/dist/spec-fields.d.ts +3 -2
  73. package/dist/standing-answer-grant.d.ts +17 -9
  74. package/dist/standing-answer-grant.js +11 -8
  75. package/dist/task-settings.d.ts +6 -1
  76. package/dist/task-settings.js +1 -1
  77. package/dist/tool-approval.d.ts +14 -7
  78. package/dist/tool-approval.js +23 -18
  79. package/dist/trace/core-keyset-guard.d.ts +3 -3
  80. package/dist/trace/engine-notice-wire.d.ts +1 -1
  81. package/dist/trace/engine-notice-wire.js +1 -0
  82. package/dist/trace/project.d.ts +7 -4
  83. package/dist/trace/project.js +6 -75
  84. package/dist/trace/tool-output-media.d.ts +2 -1
  85. package/dist/trace/tool-output-media.js +3 -25
  86. package/dist/web-admin-client/apply-effective.js +28 -11
  87. package/dist/web-admin-client/facade.d.ts +1 -1
  88. package/dist/web-admin-client/facade.js +1 -1
  89. package/dist/web-admin-client/read-face.d.ts +73 -20
  90. package/dist/web-admin-client/read-face.js +76 -36
  91. package/dist/web-admin-client/skills-mcp.d.ts +1 -1
  92. package/dist/web-admin-client/skills-mcp.js +1 -1
  93. package/dist/web-admin-client/types.d.ts +3 -3
  94. package/package.json +3 -3
  95. package/dist/mcp-tool-axes.d.ts +0 -62
  96. package/dist/mcp-tool-axes.js +0 -47
@@ -1,120 +1,45 @@
1
1
  /**
2
- * `SessionPermissionRules` 的 **wire 形读者** —— 两条写入腿(`PUT /v1/sessions/:id/policy` 与
3
- * `POST /v1/sessions/:id/sync/import` 的 `policy` 面)**唯一**的形判据。
2
+ * 会话规则(`SessionPermissionRules`)的 **wire 面单一属主** —— 两条写入腿(`PUT /v1/sessions/:id/policy` 与
3
+ * `POST /v1/sessions/:id/sync/import` 的 `policy` 面)的写口读者、`SessionPolicyError` 闭集 → HTTP 的映射、拒句。
4
4
  *
5
- * ── 为什么是一只共享读者(S-496 / [ref],取证 P4 + S-498)───────────────────────────────────────────
6
- * 改前两条腿各自为政:
7
- * · PUT 腿在 `http/routes/sessions.ts` 里**手抄**五桶键名(`["toolAllow","toolDeny","allowDirs",
8
- * "commandAllow","commandDeny"] as const`),core 的 `SessionPermissionRules` 加第六桶 ⇒ 那一桶在
9
- * PUT 上被**静默丢**、200 回执却说「已生效」;
10
- * · sync/import 腿(S-498)只判 `Array.isArray(body.policy)` **外层**,逐条记录的 `principal` / `rules`
11
- * 一个字节都不校验就裸交 `replayPolicyRecords` → `putRules`。坏形走的是**放宽**方向,亲读 core
12
- * `session-policy-store.js` 两处实现:
13
- * · `normalizeRules` 对每桶做 `[...rules.toolDeny]` ⇒ 一个**字符串** `"rm"` 被展开成 `["r","m"]`,
14
- * 本来该拒 `rm` 的 deny 规则变成拒两个不存在的命令名 = 门整条失效;
15
- * · `allowDirs` 走 `.map(...)` ⇒ 字符串上没有 `.map` ⇒ **TypeError → 500**(路由头注 `:300-302`
16
- * 自陈「Malformed shape → 400(never a downstream TypeError)」,这一半没守);
17
- * · `loosenReasons` 的每一臂都从 `prior` 出发,**prior 为空时恒回空集** ⇒ tighten-only 门对一条
18
- * 全新的会话放行任何内容,坏形因此连「收紧门」这一层都碰不到。
19
- * ⇒ 类修:一只读者,键集由 `keyof SessionPermissionRules` **派生**(下方 `RULE_FIELDS` 的
20
- * `satisfies Record<keyof SessionPermissionRules, true>`:core 加桶 ⇒ 缺键**编译红**;core 删桶 ⇒
21
- * 多键**编译红**),两条腿同调。
5
+ * ── 规则(一句;引擎 7.51.0 #1480 / #1497 起)────────────────────────────────────────────────────────────────────────
6
+ * 会话规则在每一站只有一种文法、一只判官 —— 引擎的:写口判官 `judgeSessionRulesWrite`(形 `readSessionRulesShape` → 每条条目的读法
7
+ * `readRuleEntry`),读回是同一只形读法(SQL 店的 `readSessionRulesBody`)。core 7.51.2 起一行条目的意思由行自带的文法戳 `grammar` 说(判官盖、店整只存、
8
+ * 读回原样交出);写口的值带 `rev` 而无戳 ⇒ 引擎按存量行的文法读它(`earlier_grammar`,见 {@link describeInertEntry})。引擎的两只**内容**拒码各对应一个本服务码,名字随引擎的词:
9
+ * · `invalid_shape` ⇒ `400 rules.invalid_shape` —— 规则体不是「一只对象、键都是桶、每只桶是一列字符串」(含退役的 `commandAllow` / `commandDeny`、
10
+ * 纯空白的目录、`rules` 缺席或不是对象);拒体顶层 = 引擎的形读数 `{ field, got }`(值的**类型**,不回显值),句子 = 引擎的句子原样。
11
+ * · `inert_entry` ⇒ `400 rules.inert_entry` —— 桶里有一条引擎条目读者读成「什么都不是」的条目(退役名、无前缀的 `__` 名、铸不出的协议段、
12
+ * `*`、`mcp__*`、文法不成的 `Bash(`、工具没有那种规则形的 `Monitor(…)` …);拒体顶层 = 引擎的条目读数 `{ field, entry, reason, verdict?, guidance?,
13
+ * currentName? }`,句子是本服务的({@link describeInertEntry}:引擎那一句的退役名出路指向嵌入方的构造选项 `mountedToolNames`,HTTP 调用方没有这个旋钮)。
14
+ * 两条写入腿与店拒中继({@link sessionPolicyErrorHttp} / `http/send.ts` 的 `sendSessionPolicyError`)走**同一张映射、同一句、同一份拒体** ——
15
+ * 写口读者先问一遍只是为了 sync/import 腿在建 staging **之前**整批拒(店拒发生在逐条写的中途),判官仍是引擎那一只。
22
16
  *
23
- * ── S-800(7.105.0):同一只读者多判一问 —— 写得进却跑不了的工具名 ─────────────────────────────────
24
- * 两条写入腿是 server 往会话规则店写的**全部**写口,所以「写口 fail-closed」收成一只**写口读者**
25
- * ({@link readWritableSessionPermissionRules} = 形读者 + 名字判官),两腿同调,将来第三条写入腿
26
- * 调同一只也同得。判词多一种(`legacy-tool-name`),两腿各把它映射成 `400 rules.legacy_tool_name`(映射点仍在各自
27
- * 站点:码位只放字面量)。形读者本身不变 —— SQL 店的读回也用它,读回不许判名字(理由见写口读者头注)。
28
- * S-1047(7.116.0 / core 7.41.0 #1156):名字判官 = core 导出的 `assertSessionRuleNamesCurrent`(两只 bundled 店与本仓 SQL 双生在 `putRules`
29
- * 里同点同调它;准备阶段的名审计与本仓配置表读的是同一只 `judgeRuleToolName`)—— 本仓手写的那一只(`legacyToolNameOf`)删除,零双读;
30
- * 「哪几桶装工具名」归 core(它的 `HOLDS_TOOL_NAMES`)。
31
- *
32
- * ── 7.123.0(core 7.48.0):两问都只剩引擎的判 ──────────────────────────────────────────────────────────────────────
33
- * ① **桶**能不能写:core 7.48.0 #1471 起 `normalizeRules` 对「不是一列非空字符串」的桶抛 `TypeError`(裸字符串、`null`、混入非字符串、`""` /
34
- * 纯空白项),句子点名桶与值类型。写口读者逐桶问它({@link engineRuleListOf})⇒ 两条写口是唯一拒点(`400 request.field_invalid`,句子原样),
35
- * 店里的 `normalizeRules` 对这两条写口的输入不再可能抛。修前本仓的形读者只判「字符串数组」,`[""]` / `[" "]` 过它、在店里抛 ⇒ 500。
36
- * 本仓自己的「字符串数组」判只留在**读回**({@link readSessionPermissionRules}:SQL 店读出的行,读不出形 ⇒ `corrupt`)—— 读回与写口是两个问题,
37
- * 读回不许判「引擎还肯不肯写」:7.47.x 及以前写下的空串项存量行必须读得出来(运营方清它、准备阶段照常执行它都要先读到它)。
38
- * ② **名字**够不够得着工具:core 7.48.0 #1400 起工具段为空的协议名(`mcp__srv__`)也是引擎判官的判词 `unmintable_segment` ⇒ 本仓 7.116.0 起
39
- * 自答的那一问(「引擎判官放行、却一只工具都够不着」)整只退役,写口拒码随之由 `request.field_invalid` 变 `rules.legacy_tool_name`。
40
- *
41
- * ── 文案也在这里(codex 一轮追加:两条腿各铸一份句子 = 第二份手抄)───────────────────────────────
42
- * 初版把 wire 文案留在两个路由各自铸,理由是错误文案冻结门(`test/api-error-text-freeze.test.ts`)锚的是
43
- * `src/http/` 桶里的 `sendError` 站点。codex 一轮指出那等于**把同一句话写了两遍**(`sessions.ts` 与
44
- * `session-sync.ts` 各一份),这正是本批要消灭的形。改法照本仓既有成案(S-405 / [ref] / [ref] 三处
45
- * 「常量锚」):句子收进本文件**单一属主**,冻结职责搬到冻结门的**常量锚格** —— 那一格直接断言这两只
46
- * 渲染器对每一种判词的输出字节,且对判词闭集穷尽(加一种判词 ⇒ 那一格编译红)。文案一个字节未改。
17
+ * ── 退役(硬破坏,零别名)────────────────────────────────────────────────────────────────────────────────────────────
18
+ * · 7.128.x 及以前的那一只 `rules.` 码(名字说「旧工具名」,引擎 7.51.0 起装的是条目读者的全集 —— `*` / `Monitor(…)` 与工具名无关)⇒ 拆回引擎的两个词;
19
+ * · 写口的三问三码(容器形 `request.body_shape` / 桶形 `request.field_invalid` / 条目那一只 `rules.` 码)⇒ 引擎的两问两码;
20
+ * · 本仓自己的形读者、桶表与逐键问引擎的那一圈(同一问的第二个读者,[ref])⇒ 读回直接读引擎的形读法。
47
21
  */
48
22
  import { SessionPolicyError } from "@sema-agent/core";
49
- import type { SessionPermissionRules, SessionRuleNameRefusal, SessionRulesRecord } from "@sema-agent/core";
50
- /** core `SessionPermissionRules` 的桶名全集(派生,不是手抄)。 */
51
- export declare const SESSION_PERMISSION_RULE_FIELDS: readonly (keyof SessionPermissionRules)[];
52
- /** **读回**的形问题(SQL 店读出的行;判词而非文案,见文件头注的「文案属主」一段)。 */
53
- export type SessionPermissionRulesShapeProblem =
54
- /** 整体不是一只普通对象(`null` / 数组 / 标量 / 缺席)。 */
55
- {
56
- kind: "not-an-object";
57
- }
58
- /** 某一桶在场但不是 `string[]`(字符串 / 数字 / 混杂数组都在此)。 */
59
- | {
60
- kind: "field-not-string-array";
61
- field: keyof SessionPermissionRules;
62
- };
63
- /** 一条**要写进店**的规则体的问题 —— 三问都只有一个判官:容器形(本仓)、桶(引擎 `normalizeRules`)、名字(引擎 `assertSessionRuleNamesCurrent`)。 */
64
- export type SessionPermissionRulesWriteProblem = {
65
- kind: "not-an-object";
66
- }
67
- /** 引擎不肯把这一桶写成规则表(core 7.48.0 #1471);`refusal` = 引擎的句子(点名桶与值类型),原样上 wire。 */
68
- | {
69
- kind: "field-not-rule-list";
70
- field: keyof SessionPermissionRules;
71
- refusal: string;
72
- }
73
- /** S-800 / S-1047:桶形对,但工具名桶里有一条引擎判官判非现役的名字;`refusal` = core 判官交回的 detail(原样上 wire)。 */
74
- | {
75
- kind: "legacy-tool-name";
76
- refusal: SessionRuleNameRefusal;
23
+ import type { SessionPermissionRules, SessionRuleEntryRefusal, SessionRuleShapeRefusal, SessionRulesRecord } from "@sema-agent/core";
24
+ /** 引擎的两只**内容**拒(规则体自己的问题;`conflict` / `loosen_forbidden` / `corrupt` 是店与这一次写的关系,不在此列)。 */
25
+ export type SessionRulesRefusal = SessionPolicyError & {
26
+ readonly code: "invalid_shape" | "inert_entry";
77
27
  };
78
- /** 两只读者的判词并集(句子铸点 {@link describeSessionPermissionRulesProblem} 读它)。 */
79
- export type SessionPermissionRulesProblem = SessionPermissionRulesShapeProblem | SessionPermissionRulesWriteProblem;
80
- /** 两只读者的读数:有问题给判词,没问题给**已收窄**的规则体(调用方零 cast)。`P` = 这只读者会给的判词集。 */
81
- export type SessionPermissionRulesRead<P extends SessionPermissionRulesProblem = SessionPermissionRulesProblem> = {
82
- problem: P;
83
- rules?: undefined;
84
- } | {
85
- problem?: undefined;
86
- rules: SessionPermissionRules;
87
- };
88
- /**
89
- * **读回**的形读者:读一只 `SessionPermissionRules`,形不对给判词,形对给**只含五桶**的规则体。消费者 = SQL 店的读回
90
- * (`session-policy-store-sql.ts` 的 `readSessionRulesBody`:读不出形的行判 `corrupt`)。
91
- *
92
- * 认识面窄得刻意:**只判「五桶各是字符串数组」**,不判引擎还肯不肯写(空串 / 纯空白项)、不判名字 —— 那是写口的问题
93
- * ({@link readWritableSessionPermissionRules})。7.47.x 及以前写下的空串项存量行必须读得出:读回把它判坏 ⇒ 运营方清它的 PUT 在写前快照上 500、
94
- * 准备阶段也读不到它(同 S-800 的「判官只在写口」)。未知键**照旧忽略**:core 的 `normalizeRules` 本来就只搬这五桶,本读者与它同口径。
95
- */
96
- export declare function readSessionPermissionRules(raw: unknown): SessionPermissionRulesRead<SessionPermissionRulesShapeProblem>;
97
28
  /**
98
- * 一份规则体(写口的入参,或店里读出来的一行)里**引擎不肯写**的第一只桶 —— 没有 ⇒ `undefined`。判官同 {@link engineRuleListOf}。
99
- * 两个读者:写口读者(拒)与 2c 重放的同内容判据(`session-sync.ts` 的 `sameRulesContent`:目的端存量行是这一形 ⇒ 答「不同内容」,写交给店的收紧门)。
29
+ * **写口读者** —— 两条写入腿往会话规则店写之前的**唯一**判据 = 引擎的写口判官 `judgeSessionRulesWrite`(`prior = null`:不比 rev、不走收紧门;
30
+ * 不声明 `mountedToolNames` —— 本服务没有挂在退役名下的工具,与本仓 SQL 店、两只 bundled 店的缺省同判)。好体 ⇒ 引擎的规范形
31
+ * (`normalizeRules`:三只桶、目录 `..`-free);内容拒 ⇒ 那只 `SessionPolicyError` 原样交回(调用点经 `sendSessionPolicyError` 发)。
32
+ * 判官抛别的(非内容码、非 `SessionPolicyError`)⇒ 原样上抛(fail-loud,不吞)。
33
+ * 拿到规则体 = 店里同一只判官对它的形与条目两问都不会再拒;只剩 CAS 与收紧门。
100
34
  */
101
- export declare function unwritableRuleListOf(rules: Readonly<Partial<Record<keyof SessionPermissionRules, unknown>>>): {
102
- field: keyof SessionPermissionRules;
103
- refusal: string;
104
- } | undefined;
105
- /**
106
- * **写口读者**(S-800)—— 两条写入腿(`PUT /v1/sessions/:id/policy`、sync/import 的 `policy` 面经 {@link readSessionRulesRecords})往会话规则店
107
- * 写之前的**唯一**判据,三问按序、每问一个判官:
108
- * ① 容器形 —— 一只普通对象(本仓);
109
- * ② 每一只桶 —— 引擎肯不肯把它写成规则表({@link engineRuleListOf},core 7.48.0 #1471;与引擎店同序:先 `normalizeRules` 再判名);
110
- * ③ 工具名桶里的每一条 —— 引擎判官({@link legacyToolNameRefusalOf}:退役名 / 无前缀 `__` 名 / 对端段或工具段铸不出、为空)。
111
- * 拿到规则体 = 店里的 `normalizeRules` 与 `assertSessionRuleNamesCurrent` 对它都不会再抛(同一只判官先问过)。
112
- * 🔴 **只在写口,不在读回**:SQL 店读回同一行时只判形({@link readSessionPermissionRules})。早先写进去的退役名行 / 空串项行必须**读得出来** ——
113
- * 那是运营方清掉它的唯一路径(operator 的 PUT 先读写前快照),也是 core 准备阶段报 `config.legacy_tool_name` 而不是一只 500 `corrupt` 的前提
114
- * (7.105.0 车QQ 双库套件实抓,修在同批)。
115
- */
116
- export declare function readWritableSessionPermissionRules(raw: unknown): SessionPermissionRulesRead<SessionPermissionRulesWriteProblem>;
117
- /** 一条 `SessionRulesRecord` 的形问题(`policy[]` 的元素级;桶级问题原样转载在 `rules` 位)。 */
35
+ export declare function readWritableSessionPermissionRules(raw: unknown): {
36
+ rules: SessionPermissionRules;
37
+ refusal?: undefined;
38
+ } | {
39
+ refusal: SessionRulesRefusal;
40
+ rules?: undefined;
41
+ };
42
+ /** 一条 `SessionRulesRecord` 的问题(`policy[]` 的元素级;规则体的问题原样转载引擎的内容拒)。 */
118
43
  export type SessionRulesRecordProblem =
119
44
  /** `policy` 本身不是数组。 */
120
45
  {
@@ -131,11 +56,11 @@ export type SessionRulesRecordProblem =
131
56
  kind: "principal-not-a-string";
132
57
  index: number;
133
58
  }
134
- /** 第 `index` 条的 `rules` 写不进(转载写口读者的判词)。 */
59
+ /** 第 `index` 条的 `rules` 写不进(引擎的内容拒,由写口读者交回)。 */
135
60
  | {
136
61
  kind: "rules";
137
62
  index: number;
138
- problem: SessionPermissionRulesWriteProblem;
63
+ refusal: SessionRulesRefusal;
139
64
  };
140
65
  /** {@link readSessionRulesRecords} 的读数。 */
141
66
  export type SessionRulesRecordsRead = {
@@ -150,21 +75,31 @@ export type SessionRulesRecordsRead = {
150
75
  * 它只服务那一条**写入**腿(导入的记录随后 `putRules` 进店),所以每条记录的规则体走**写口读者**
151
76
  * {@link readWritableSessionPermissionRules}(与 PUT 腿同一只判官)。
152
77
  *
153
- * `rev`:记录里带的 `rev` 是**导出端的**乐观锁读数,`putRules` 侧一进门就 `stripRev` 掉(`sameRulesContent`
78
+ * `rev`:记录里带的 `rev` 是**导出端的**乐观锁读数,`putRules` 侧的形读法一进门就略过它(店的戳;`sameRulesContent`
154
79
  * 也比的是去 rev 的字节形),所以它在本读者里只是过境位 —— 非数字即当缺席(不是拒因:一个坏 rev 改变不了
155
80
  * 任何一次写入的结果,拒它只会把合法同步砖死)。
156
81
  */
157
82
  export declare function readSessionRulesRecords(raw: unknown): SessionRulesRecordsRead;
158
- /** 桶级判词 → 人话。闭集穷尽:{@link SessionPermissionRulesProblem} 加一种判词 ⇒ 本 switch 编译红。 */
159
- export declare function describeSessionPermissionRulesProblem(p: SessionPermissionRulesProblem): string;
83
+ /** 记录级判词 → 人话(只剩不是规则体的那三形;规则体的问题由 `sendSessionPolicyError` 带前缀 `policy: [i].rules: ` 发)。闭集穷尽:加一种 ⇒ 编译红。 */
84
+ export declare function describeSessionRulesRecordProblem(p: Exclude<SessionRulesRecordProblem, {
85
+ kind: "rules";
86
+ }>): string;
87
+ /**
88
+ * `inert_entry` 的句子(本服务的 wire 文案;拒体顶层带引擎的条目读数原样)。理由闭集 = core `ALLOW_ENTRY_INERT_REASONS`,名字判词闭集 = core
89
+ * `RULE_TOOL_NAME_VERDICTS` 去掉 `current`(core 加一员 ⇒ 本 switch 编译红)。不转录 core 店的那一句:它对退役名的出路是「在店选项
90
+ * `mountedToolNames` 里声明」—— 那是嵌入方的构造选项,本服务不暴露它,照抄等于给 HTTP 调用方指一个不存在的旋钮。
91
+ * 改法指引只读拒自己带的 `guidance`(判官对同一条的句子,逐字节;`retired` 与 `unmintable_segment` 带,`unprefixed_namespace` 没有 ⇒ 无括注),
92
+ * 本仓不二次调判官、不铸回落句。
93
+ */
94
+ export declare function describeInertEntry(r: SessionRuleEntryRefusal): string;
160
95
  /**
161
- * S-800:一只 `policy[]` 判词**是不是**「写得进却跑不了的名字」那一类 —— 是就交回那一格(桶 + 名字),
162
- * 调用点据此发 `rules.legacy_tool_name`,否则发形的码。闭集穷尽两层:记录级判词或桶级判词加一种 ⇒ 编译红,
163
- * 新判词不会静默落进某一个码。
96
+ * 一只 `SessionPolicyError` 上 wire 的句子(单一属主;`http/send.ts` 的 `sendSessionPolicyError` 在它前面接调用点的前缀、过脱敏器)。
97
+ * `inert_entry` ⇒ 本服务的句子(读引擎的条目读数);其余码 ⇒ 引擎 / 本仓店铸的原句(`invalid_shape` 的句子点名键与类型、不带值)。
98
+ * 引擎契约破了(`inert_entry` 不带条目读数)⇒ 抛,不编一句。
164
99
  */
165
- export declare function legacyToolNameProblemOf(p: SessionRulesRecordProblem): SessionRuleNameRefusal | undefined;
166
- /** `policy[]` 判词 → 人话(sync/import 腿在它前面拼 `policy: `)。闭集穷尽同上。 */
167
- export declare function describeSessionRulesRecordProblem(p: SessionRulesRecordProblem): string;
100
+ export declare function sessionPolicyErrorSentence(e: SessionPolicyError): string;
101
+ /** 一只 `SessionPolicyError` 上 wire 的拒体格(引擎的 `detail` 原样摊在体顶层;只在两只内容码上有)。 */
102
+ export declare function sessionPolicyErrorFacts(e: SessionPolicyError): Readonly<SessionRuleShapeRefusal | SessionRuleEntryRefusal> | undefined;
168
103
  /**
169
104
  * 🔴 **`SessionPolicyError` 的闭集 → HTTP 的**唯一**映射(core 7.25.0 提货批新建)。**
170
105
  *
@@ -175,7 +110,7 @@ export declare function describeSessionRulesRecordProblem(p: SessionRulesRecordP
175
110
  * **数据完整性故障**。这正是 [ref] 词表纪律说的那一形:闭集必须穷尽 `switch`,未知词是**编译**错,
176
111
  * 不是运行期落进某个恰好存在的臂。
177
112
  *
178
- * ⇒ 映射搬到这里(本文件已是 policy wire 形的单一读者/属主,S-496 先例),两条路由同调它。
113
+ * ⇒ 映射搬到这里(本文件是会话规则 wire 面的单一属主,S-496 先例),两条写入腿、读腿与 2c 重放腿同调它。
179
114
  * 新增一个词 ⇒ 本 switch 编译红,由人裁定它的状态码与码位,而不是让它静默继承邻居的。
180
115
  *
181
116
  * 逐词:
@@ -184,9 +119,11 @@ export declare function describeSessionRulesRecordProblem(p: SessionRulesRecordP
184
119
  * 如实登记、未改):PG 后端的 SQL 双生对含 NUL / 孤代理字节的规则拒收时借用了它
185
120
  * (`plugins/session-policy-store-sql.ts` 的 `pgHasUnstorable` 臂)—— 那一形**既非放宽也非越权**,operator 写同样撞上;
186
121
  * core 的闭集里没有「输入不可存」这个词,改码位要先裁定新码与 PUT / 2c 导入两条入口同判(DEBTS S-546 B5)。
187
- * · `corrupt`(**500**)—— 店读不出它自己该读的那份字节(记号/行被列外篡改或撕裂)。
188
- * 不是 4xx:调用方没做错任何事,它也改不了这件事;不是 503:重试改不了判(core 成文的修复路径是
189
- * **人**去改回或删掉那一行,而删记号要交出它握着的那个号)。
122
+ * · `corrupt`(**500**)—— 店读不出它自己该读的那份字节(记号/行被列外篡改或撕裂,或存着引擎今天的形读法读不出的规则体)。
123
+ * 不是 4xx:调用方没做错任何事,它也改不了这件事;不是 503:重试改不了判(修复路径是**人**去改回或删掉那一行,
124
+ * 而删记号要交出它握着的那个号)。
125
+ * · `invalid_shape` / `inert_entry`(**400**)—— 引擎的两只内容拒,码名随引擎的词(文件头注)。调用方写错了规则体,改写即成;
126
+ * 不是数据故障(500)也不是越权(403)。operator 写同样拒(一条什么都不是的条目,谁写都护不住任何东西)。
190
127
  */
191
128
  export declare function sessionPolicyErrorHttp(code: SessionPolicyError["code"]): {
192
129
  status: number;
@@ -1,80 +1,17 @@
1
- import { PROTOCOL_TABLE, SessionPolicyError, assertSessionRuleNamesCurrent, normalizeRules } from "@sema-agent/core";
1
+ import { PROTOCOL_TABLE, SessionPolicyError, judgeSessionRulesWrite } from "@sema-agent/core";
2
2
  import { displayListEntry } from "./list-entry-display.js";
3
- const RULE_FIELDS = {
4
- toolAllow: true,
5
- toolDeny: true,
6
- allowDirs: true,
7
- commandAllow: true,
8
- commandDeny: true,
9
- };
10
- export const SESSION_PERMISSION_RULE_FIELDS = Object.keys(RULE_FIELDS);
11
- function legacyToolNameRefusalOf(rules) {
12
- try {
13
- assertSessionRuleNamesCurrent(rules);
14
- return undefined;
15
- }
16
- catch (e) {
17
- if (e instanceof SessionPolicyError && e.code === "legacy_tool_name" && e.detail !== undefined)
18
- return e.detail;
19
- throw e;
20
- }
21
- }
22
- function protocolToolNameShapes() {
23
- return PROTOCOL_TABLE.map((ns) => `${ns.prefix}<peer>__<tool>`).join(" or ");
24
- }
25
- function isRulesObject(raw) {
26
- return raw !== null && typeof raw === "object" && !Array.isArray(raw);
3
+ function isSessionRulesRefusal(e) {
4
+ return e instanceof SessionPolicyError && (e.code === "invalid_shape" || e.code === "inert_entry");
27
5
  }
28
- export function readSessionPermissionRules(raw) {
29
- if (!isRulesObject(raw))
30
- return { problem: { kind: "not-an-object" } };
31
- const rules = {};
32
- for (const field of SESSION_PERMISSION_RULE_FIELDS) {
33
- const v = raw[field];
34
- if (v === undefined)
35
- continue;
36
- if (!Array.isArray(v) || !v.every((x) => typeof x === "string"))
37
- return { problem: { kind: "field-not-string-array", field } };
38
- rules[field] = v;
39
- }
40
- return { rules };
41
- }
42
- function engineRuleListOf(field, value) {
43
- let normalized;
6
+ export function readWritableSessionPermissionRules(raw) {
44
7
  try {
45
- normalized = normalizeRules({ [field]: value });
8
+ return { rules: judgeSessionRulesWrite(null, raw, undefined) };
46
9
  }
47
10
  catch (e) {
48
- if (e instanceof TypeError)
49
- return { refusal: e.message };
11
+ if (isSessionRulesRefusal(e))
12
+ return { refusal: e };
50
13
  throw e;
51
14
  }
52
- const list = normalized[field];
53
- return list === undefined ? {} : { list };
54
- }
55
- export function unwritableRuleListOf(rules) {
56
- for (const field of SESSION_PERMISSION_RULE_FIELDS) {
57
- const judged = engineRuleListOf(field, rules[field]);
58
- if ("refusal" in judged)
59
- return { field, refusal: judged.refusal };
60
- }
61
- return undefined;
62
- }
63
- export function readWritableSessionPermissionRules(raw) {
64
- if (!isRulesObject(raw))
65
- return { problem: { kind: "not-an-object" } };
66
- const rules = {};
67
- for (const field of SESSION_PERMISSION_RULE_FIELDS) {
68
- const judged = engineRuleListOf(field, raw[field]);
69
- if ("refusal" in judged)
70
- return { problem: { kind: "field-not-rule-list", field, refusal: judged.refusal } };
71
- if (judged.list !== undefined)
72
- rules[field] = judged.list;
73
- }
74
- const refusal = legacyToolNameRefusalOf(rules);
75
- if (refusal !== undefined)
76
- return { problem: { kind: "legacy-tool-name", refusal } };
77
- return { rules };
78
15
  }
79
16
  export function readSessionRulesRecords(raw) {
80
17
  if (!Array.isArray(raw))
@@ -88,83 +25,84 @@ export function readSessionRulesRecords(raw) {
88
25
  if (principal !== undefined && typeof principal !== "string")
89
26
  return { problem: { kind: "principal-not-a-string", index } };
90
27
  const read = readWritableSessionPermissionRules(rules);
91
- if (read.problem !== undefined)
92
- return { problem: { kind: "rules", index, problem: read.problem } };
28
+ if (read.refusal !== undefined)
29
+ return { problem: { kind: "rules", index, refusal: read.refusal } };
93
30
  const innerRev = rules?.rev;
94
31
  const carried = typeof innerRev === "number" ? innerRev : typeof rev === "number" ? rev : 0;
95
32
  records.push({ principal, rules: { ...read.rules, rev: carried } });
96
33
  }
97
34
  return { records };
98
35
  }
99
- export function describeSessionPermissionRulesProblem(p) {
36
+ export function describeSessionRulesRecordProblem(p) {
100
37
  switch (p.kind) {
101
- case "not-an-object": return "rules must be a SessionPermissionRules object";
102
- case "field-not-string-array": return `${p.field} must be an array of strings`;
103
- case "field-not-rule-list": return p.refusal;
104
- case "legacy-tool-name": return describeLegacyToolName(p.refusal);
38
+ case "not-an-array": return "must be an array of {principal, rules} records";
39
+ case "record-not-an-object": return `[${p.index}] must be a {principal, rules} record`;
40
+ case "principal-not-a-string": return `[${p.index}].principal must be a string when present (omit it for the session-wide row)`;
105
41
  default: {
106
42
  const unhandled = p;
107
- throw new Error(`unhandled SessionPermissionRules problem: ${JSON.stringify(unhandled)}`);
43
+ throw new Error(`unhandled SessionRulesRecord problem: ${JSON.stringify(unhandled)}`);
108
44
  }
109
45
  }
110
46
  }
111
- function describeLegacyToolName(r) {
112
- const why = r.guidance !== undefined ? ` (${r.guidance})` : "";
113
- switch (r.verdict) {
114
- case "retired":
115
- return `${r.field} names ${displayListEntry(r.name)}, a retired tool name${why}; with that rule in place this session could not start another run, so nothing was written — use the current tool name${r.currentName !== undefined ? ` (${displayListEntry(r.currentName)})` : ""}`;
116
- case "unprefixed_namespace":
117
- return `${r.field} names ${displayListEntry(r.name)}, which contains "__" but no protocol prefix; with that rule in place this session could not start another run, so nothing was written — a protocol tool is named ${protocolToolNameShapes()}`;
118
- case "unmintable_segment":
119
- return `${r.field} names ${displayListEntry(r.name)}, which can never match a tool${why}, so nothing was written`;
120
- default: {
121
- const unhandled = r.verdict;
122
- throw new Error(`unhandled legacy tool name verdict: ${JSON.stringify(unhandled)}`);
123
- }
124
- }
47
+ function protocolToolNameShapes() {
48
+ return PROTOCOL_TABLE.map((ns) => `${ns.prefix}<peer>__<tool>`).join(" or ");
125
49
  }
126
- export function legacyToolNameProblemOf(p) {
127
- switch (p.kind) {
128
- case "not-an-array":
129
- case "record-not-an-object":
130
- case "principal-not-a-string":
131
- return undefined;
132
- case "rules":
133
- switch (p.problem.kind) {
134
- case "not-an-object":
135
- case "field-not-rule-list":
136
- return undefined;
137
- case "legacy-tool-name":
138
- return p.problem.refusal;
50
+ function entryShapes() {
51
+ return `an entry is a tool name (a protocol tool is named ${protocolToolNameShapes()}), a protocol namespace (${PROTOCOL_TABLE.map((ns) => `${ns.prefix}<peer>`).join(" or ")}), or a rule in its tool's own grammar (Bash(<command>), Edit(<path>), Skill(<name>), WebFetch(domain:<host>))`;
52
+ }
53
+ export function describeInertEntry(r) {
54
+ const why = r.guidance !== undefined ? ` (${r.guidance})` : "";
55
+ switch (r.reason) {
56
+ case "tool_name":
57
+ switch (r.verdict) {
58
+ case "retired":
59
+ return `${r.field} names ${displayListEntry(r.entry)}, a retired tool name${why}; with that rule in place this session could not start another run, so nothing was written — use the current tool name${r.currentName !== undefined ? ` (${displayListEntry(r.currentName)})` : ""}`;
60
+ case "unprefixed_namespace":
61
+ return `${r.field} names ${displayListEntry(r.entry)}, which contains "__" but no protocol prefix; with that rule in place this session could not start another run, so nothing was written — a protocol tool is named ${protocolToolNameShapes()}`;
62
+ case "unmintable_segment":
63
+ return `${r.field} names ${displayListEntry(r.entry)}, which can never match a tool${why}, so nothing was written`;
64
+ case undefined:
65
+ return inertEntrySentence(r, why);
139
66
  default: {
140
- const unhandled = p.problem;
141
- throw new Error(`unhandled SessionPermissionRules problem: ${JSON.stringify(unhandled)}`);
67
+ const unhandled = r.verdict;
68
+ throw new Error(`unhandled rule tool name verdict: ${JSON.stringify(unhandled)}`);
142
69
  }
143
70
  }
71
+ case "wildcard_all":
72
+ case "namespace_wildcard":
73
+ case "content_unsupported":
74
+ case "grammar":
75
+ case "near_miss":
76
+ return inertEntrySentence(r, why);
77
+ case "earlier_grammar":
78
+ return `${r.field} holds ${displayListEntry(r.entry)}, which admits nothing as written (${r.reason}): the value carries a stored row's rev and no grammar stamp, so its entries are read in the grammar the row was stored in, where an entry was a tool name as written and this one named no tool; nothing was written — write the rules afresh (the buckets, without the row's rev; the write stamps the current grammar), or leave the entry out`;
144
79
  default: {
145
- const unhandled = p;
146
- throw new Error(`unhandled SessionRulesRecord problem: ${JSON.stringify(unhandled)}`);
80
+ const unhandled = r.reason;
81
+ throw new Error(`unhandled inert entry reason: ${JSON.stringify(unhandled)}`);
147
82
  }
148
83
  }
149
84
  }
150
- export function describeSessionRulesRecordProblem(p) {
151
- switch (p.kind) {
152
- case "not-an-array": return "must be an array of {principal, rules} records";
153
- case "record-not-an-object": return `[${p.index}] must be a {principal, rules} record`;
154
- case "principal-not-a-string": return `[${p.index}].principal must be a string when present (omit it for the session-wide row)`;
155
- case "rules": return `[${p.index}].rules: ${describeSessionPermissionRulesProblem(p.problem)}`;
156
- default: {
157
- const unhandled = p;
158
- throw new Error(`unhandled SessionRulesRecord problem: ${JSON.stringify(unhandled)}`);
159
- }
160
- }
85
+ function inertEntrySentence(r, why) {
86
+ const way = r.currentName !== undefined ? `write ${displayListEntry(r.currentName)}` : entryShapes();
87
+ return `${r.field} holds ${displayListEntry(r.entry)}, which ${r.field === "toolDeny" ? "refuses" : "admits"} nothing as written (${r.reason})${why}, so nothing was written — ${way}`;
88
+ }
89
+ export function sessionPolicyErrorSentence(e) {
90
+ if (e.code !== "inert_entry")
91
+ return e.message;
92
+ if (e.detail === undefined || !("entry" in e.detail))
93
+ throw new Error(`SessionPolicyError inert_entry without the entry reader's detail: ${e.message}`);
94
+ return describeInertEntry(e.detail);
95
+ }
96
+ export function sessionPolicyErrorFacts(e) {
97
+ return isSessionRulesRefusal(e) ? e.detail : undefined;
161
98
  }
162
99
  export function sessionPolicyErrorHttp(code) {
163
100
  switch (code) {
164
101
  case "conflict": return { status: 409, errorCode: "conflict" };
165
102
  case "loosen_forbidden": return { status: 403, errorCode: "loosen_forbidden" };
166
103
  case "corrupt": return { status: 500, errorCode: "corrupt" };
167
- case "legacy_tool_name": return { status: 400, errorCode: "rules.legacy_tool_name" };
104
+ case "invalid_shape": return { status: 400, errorCode: "rules.invalid_shape" };
105
+ case "inert_entry": return { status: 400, errorCode: "rules.inert_entry" };
168
106
  default: {
169
107
  const unhandled = code;
170
108
  throw new Error(`unhandled SessionPolicyError code: ${JSON.stringify(unhandled)}`);
@@ -164,12 +164,14 @@ export declare function planSync(sessionId: string, srcBackend: StoreBackend, ds
164
164
  /** [ref] ①(死码 A6 修的移植):同内容判据 —— 比较「归一化、去 rev」后的字节形。putRules 每次
165
165
  * 无条件写都 rev+1,而 rev 是 operator 乐观锁的 CAS 令牌(`PutRulesOptions.expectedRev`),
166
166
  * 幂等重试/无害重复同步不该把在途 CAS 打失败。归一化用 core 的 `normalizeRules`(store 落盘
167
- * 前走同一函数 ⇒ 两侧同坐标);`stripRev` 去掉记录内嵌的 rev(`listBySession` 的 rules 含 rev,
168
- * 它不是内容)。
169
- * 🔴 core 7.48.0 起 `normalizeRules` 对「不是一列非空字符串」的桶抛 `TypeError`。`incoming` 由写口读者保证是引擎肯写的
170
- * (`session-policy-wire.ts` `readSessionRulesRecords`);`stored` 可能是 7.47.x 及以前写下的空串项存量行 —— 读得出、引擎却不肯再写。
171
- * 这种行与任何肯写的 `incoming` 都**不同内容**(它多一个空项),所以答 `false`(判官 = 写口读者那一只 `unwritableRuleListOf`,不拿它跑 normalize):
172
- * 写照常交给店 —— 店的收紧门对它照常判(operator 照写,是清旧行的出路;非 operator 删空串 deny 项被读成放宽 ⇒ 403,与 7.122.0 同答)。 */
167
+ * 前走同一函数 ⇒ 两侧同坐标);它略过店的戳 `rev` / `gen`(`listBySession` 的 rules 含 rev,
168
+ * 它不是内容;core 7.51.0 #1474 起 `normalizeRules` 自己略过,修前的 `stripRev` 包裹随之退役)。
169
+ * 两侧过的是同一只形读法(core 7.51.0 `readSessionRulesShape`):`incoming` 由写口读者(引擎的写口判官)交出,`stored` 由店的读回交出 ——
170
+ * 店读回读不出形的行答 `corrupt`、根本到不了这里,所以 `normalizeRules` 对两侧都不抛(修前本仓的读回比引擎宽,另有一只判官先挡一次)。
171
+ * 🔴 字节同 ≠ 意思同(core 7.51.2 行自带文法戳;codex r1 [high] 验真):`normalizeRules` 把店的戳 `rev` / `gen` / `grammar` 都略过,而同一串桶字节在两种文法里是两句话 ——
172
+ * 目的行无戳(文法 0:`toolAllow` 里的 `Bash(rm:*)` 什么都不放行)、导入记录带 `grammar: 1`(放行 rm),只比字节会判「同内容」、零次 putRules、同步答 200 而目的行仍是旧意思。
173
+ * 所以字节同之外还问引擎的收紧比对 `loosenReasons` **双向**(两侧各在自己的文法里读:`stored` 带 `rev`,`incoming` 带 `grammar`):两个方向都「没放宽」才是同一句话;
174
+ * 意思不同就交给 `putRules` 的判官(非 operator ⇒ `loosen_forbidden`;operator ⇒ 改写成当前文法、盖戳)。本仓仍零处读 / 比戳值;意思由引擎说。 */
173
175
  export declare function sameRulesContent(stored: StoredSessionRules | null, incoming: SessionPermissionRules): boolean;
174
176
  /** [ref] ① 的重放腿共享实现:PUSH 路由的两条 policy 重放腿(Phase A identical 短路支 / Phase B
175
177
  * commit 后支)都从这里走 —— 等内容跳过(rev 零漂移),真变更照常落地(E6 tighten-only 门在
@@ -1,6 +1,5 @@
1
1
  import { classifySyncRelationship } from "./session-sync-kernel.js";
2
- import { normalizeRules, stripRev } from "@sema-agent/core";
3
- import { unwritableRuleListOf } from "./session-policy-wire.js";
2
+ import { loosenReasons, normalizeRules } from "@sema-agent/core";
4
3
  export function fileHistoryImportFace(fs) {
5
4
  const candidate = fs?.importHistory;
6
5
  if (typeof candidate !== "function")
@@ -59,10 +58,12 @@ export async function planSync(sessionId, srcBackend, dstBackend) {
59
58
  return classifySyncRelationship(srcEntries, dstEntries);
60
59
  }
61
60
  export function sameRulesContent(stored, incoming) {
62
- if (stored === null || unwritableRuleListOf(stored) !== undefined)
61
+ if (stored === null)
63
62
  return false;
64
- const norm = (r) => JSON.stringify(normalizeRules(stripRev(r)));
65
- return norm(stored) === norm(incoming);
63
+ const norm = (r) => JSON.stringify(normalizeRules(r));
64
+ if (norm(stored) !== norm(incoming))
65
+ return false;
66
+ return loosenReasons(stored, incoming).length === 0 && loosenReasons(incoming, stored).length === 0;
66
67
  }
67
68
  export async function replayPolicyRecords(store, sessionId, records, operatorOk) {
68
69
  for (const rec of records) {
@@ -66,8 +66,9 @@ export declare function skillToolListRefusal(skill: Record<string, unknown>): st
66
66
  /** [ref] 单条 agent 定义的形状裁决:null=合法;否则一句可 400 的英文错误(不带索引,调用方自己加)。
67
67
  * 白名单外键 fail-loud —— 与 resilience/limits 的「容忍未知键」相反:agents 是能力定义,静默丢字段 =
68
68
  * 定义被悄悄削(一个丢掉的 isolation 就是放宽),比 400 更危险,故整条拒。`allowTools` / `denyTools` 本门只判形与条数(`toolList`),
69
- * **名字归引擎一只判**(core 7.49.0 #1623 / #1433 第二步起与根 run 的 `allowTools` / `excludeTools` 是同一只选择器,开跑前,三来源同判):没有任何工具答应的名字
70
- * (无人挂的名、大小写孪生 / 退役名 `bash` / `BASH`、坏拼法、非字符串条目)⇒ 整 run 拒 `config.agent.unknown_tool` —— 主车道的委派工具带 extraTools 工厂,本仓在同一处
69
+ * **名字归引擎一只判**(core 7.49.0 #1623 / #1433 第二步起与根 run 的 `allowTools` / `excludeTools` 是同一只选择器,开跑前,三来源同判):core 7.51.0 #1736 起
70
+ * 没有任何工具答应的名字(无人挂的名、大小写孪生 / 退役名 `bash` / `BASH`)**不拒 run** —— 条目原样保留、经 onError(config)告知一次(带该写的拼法);
71
+ * 只有任何工具名都不可能有的拼法(`grammar`)与非字符串条目 ⇒ 整 run 拒 `config.agent.unknown_tool` —— 主车道的委派工具带 extraTools 工厂,本仓在同一处
71
72
  * 声明它的名册(core 7.46.0 `extraToolNames`;工厂与名册读同一批工具对象,`capabilities/builtin-tools.ts` `createSubagentExtraTools`),引擎因此列得出子代工具池。
72
73
  * 两张表管子代可挂载的**全部**工具:委派池、hands 内建、引擎自己的工具(`Monitor` / `Artifact` / `Skill` / 记忆工具 …)与父的 MCP 挂载 ——
73
74
  * `denyTools:["Monitor"]` ⇒ 子代没有 `Monitor`;`allowTools` 只点了调用方工具 ⇒ 子代只剩那几只与它这条腿的协议工具。`denyTools` 的 `Tool(content)` ⇒ 整只工具。
@@ -1,9 +1,13 @@
1
- /** 读口的入参:任何一种载着「这一问」的形(活的 `AskRequest`、`ToolCallRequest`、停驻行的 `tool_approval` 臂)都结构上满足。 */
2
- export interface StandingAnswerGrantCarrier {
3
- readonly toolName?: unknown;
1
+ /** 卡载体:引擎铸的问 / 停驻行 / 它们的投影行(活的 `AskRequest`、停驻行的 `tool_approval` 臂、运维队列行都结构上满足)。 */
2
+ export interface StandingAnswerCard {
4
3
  readonly boundGrantDigest?: unknown;
4
+ readonly boundGrantAbsence?: unknown;
5
+ }
6
+ /** 调用载体:策略层的 `ToolCallRequest`、豁免店的行(只有工具名)。 */
7
+ export interface StandingAnswerCall {
8
+ readonly toolName?: unknown;
5
9
  }
6
- /** 一问的授予绑定(闭集):`none` = 应答只覆盖这一次调用;`bound` = 授予、绑定可读;`unreadable` = 授予、绑定读不出。 */
10
+ /** 一问的授予绑定(闭集):`none` = 应答只覆盖这一次调用;`bound` = 授予、绑定可读;`unreadable` = 授予或说不清、绑定读不出。 */
7
11
  export type GrantBinding = {
8
12
  readonly kind: "none";
9
13
  } | {
@@ -12,9 +16,13 @@ export type GrantBinding = {
12
16
  } | {
13
17
  readonly kind: "unreadable";
14
18
  };
15
- /** 这一问的授予绑定(读法见文件头注)。 */
16
- export declare function grantBindingOf(carrier: StandingAnswerGrantCarrier): GrantBinding;
17
- /** 这一问的应答是否授予这一次调用之外的东西(= 绑定不是 `none`)。**按工具名记**的读者(耐久豁免店的读写口、豁免列表)据它不碰这类问:
18
- * 它们的键没有绑定这一维,记下 / 代答都会越过绑定。 */
19
- export declare function answerGrantsBeyondCall(carrier: StandingAnswerGrantCarrier): boolean;
19
+ /** 卡上的授予绑定 —— 只读引擎的词(读法见文件头注):摘要是串 ⇒ bound;摘要不在且词是 `not_bindable` ⇒ none;其余 ⇒ unreadable。 */
20
+ export declare function grantBindingOf(card: StandingAnswerCard): GrantBinding;
21
+ /** 调用上的授予绑定 —— 调用上没有引擎的词,读引擎绑定工厂的那一条规则(见文件头注;🚧 引擎在调用上盖词之后退役)。 */
22
+ export declare function callGrantBindingOf(call: StandingAnswerCall): GrantBinding;
23
+ /**
24
+ * 一问的授予绑定的**唯一折点**:读哪个载体由**问的属主**定 —— 名单自己的问(`deploymentListQuestion`,即 `standingAnswerHome` 的 `server_store` 家)读调用口,
25
+ * 引擎铸的问读卡口(载体与理由见文件头注)。所有把「一张卡」折成 {@link StandingAnswerFacts} 的读者(活的问、待决条目、停驻行、运维队列行)经它取绑定,不各自选口。
26
+ */
27
+ export declare function questionGrantBindingOf(carrier: StandingAnswerCard & StandingAnswerCall, deploymentListQuestion: boolean): GrantBinding;
20
28
  //# sourceMappingURL=standing-answer-grant.d.ts.map
@@ -1,12 +1,15 @@
1
1
  import { SKILL_TOOL_NAME } from "@sema-agent/core";
2
- export function grantBindingOf(carrier) {
3
- if (Object.hasOwn(carrier, "boundGrantDigest")) {
4
- const d = carrier.boundGrantDigest;
5
- return typeof d === "string" && d.length > 0 ? { kind: "bound", digest: d } : { kind: "unreadable" };
6
- }
7
- return carrier.toolName === SKILL_TOOL_NAME ? { kind: "unreadable" } : { kind: "none" };
2
+ const NOT_BINDABLE = "not_bindable";
3
+ export function grantBindingOf(card) {
4
+ const digest = card.boundGrantDigest;
5
+ if (typeof digest === "string")
6
+ return { kind: "bound", digest };
7
+ return digest === undefined && card.boundGrantAbsence === NOT_BINDABLE ? { kind: "none" } : { kind: "unreadable" };
8
8
  }
9
- export function answerGrantsBeyondCall(carrier) {
10
- return grantBindingOf(carrier).kind !== "none";
9
+ export function callGrantBindingOf(call) {
10
+ return call.toolName === SKILL_TOOL_NAME ? { kind: "unreadable" } : { kind: "none" };
11
+ }
12
+ export function questionGrantBindingOf(carrier, deploymentListQuestion) {
13
+ return deploymentListQuestion ? callGrantBindingOf(carrier) : grantBindingOf(carrier);
11
14
  }
12
15
  //# sourceMappingURL=standing-answer-grant.js.map