@sema-agent/server 7.116.1 → 7.117.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.
- package/MIGRATION.md +98 -0
- package/USAGE.md +32 -27
- package/dist/approval-card.d.ts +43 -9
- package/dist/approval-card.js +36 -4
- package/dist/boot/budget-tracing.js +2 -3
- package/dist/boot/config-center.d.ts +2 -2
- package/dist/boot/config-center.js +5 -7
- package/dist/boot/resolve-spec.js +10 -3
- package/dist/boot/runner-deps.d.ts +5 -3
- package/dist/boot/runner-deps.js +4 -2
- package/dist/brain.d.ts +17 -8
- package/dist/brain.js +10 -7
- package/dist/config-catalog.js +3 -2
- package/dist/config-center/apply-effective.d.ts +6 -16
- package/dist/config-center/apply-effective.js +0 -6
- package/dist/config-center/facade.d.ts +1 -1
- package/dist/config-center/facade.js +1 -1
- package/dist/config-center/restart-signal.d.ts +1 -1
- package/dist/config-types.d.ts +12 -16
- package/dist/config.d.ts +34 -27
- package/dist/config.js +23 -19
- package/dist/deployment-governance.d.ts +11 -12
- package/dist/deployment-governance.js +4 -9
- package/dist/governance-overrides.d.ts +20 -0
- package/dist/governance-overrides.js +8 -0
- package/dist/http/admission.js +7 -7
- package/dist/http/approval-sweeps.js +15 -12
- package/dist/http/checkpoint-ctx.d.ts +83 -24
- package/dist/http/checkpoint-ctx.js +68 -8
- package/dist/http/resume-legs.js +11 -15
- package/dist/http/retired-skill-manifest.d.ts +1 -1
- package/dist/http/route-ctx.d.ts +3 -3
- package/dist/http/routes/capabilities.js +3 -3
- package/dist/http/routes/runs.js +12 -0
- package/dist/model-select.d.ts +16 -5
- package/dist/model-select.js +7 -3
- package/dist/namespaced-rule-name.d.ts +7 -5
- package/dist/observability/fail-open.d.ts +1 -1
- package/dist/observability/fail-open.js +1 -1
- package/dist/observability/permission-rule-events.d.ts +3 -0
- package/dist/observability/permission-rule-events.js +4 -0
- package/dist/observability/run-terminal.js +21 -1
- package/dist/outbound-dispatcher.d.ts +18 -0
- package/dist/outbound-dispatcher.js +96 -10
- package/dist/permission-rule-list.d.ts +20 -31
- package/dist/plugins/one-time-migrations.d.ts +20 -0
- package/dist/plugins/one-time-migrations.js +84 -4
- package/dist/router/route-orchestration.d.ts +2 -23
- package/dist/rule-entry-list.d.ts +99 -0
- package/dist/rule-entry-list.js +110 -0
- package/dist/run-local.d.ts +4 -2
- package/dist/run-local.js +3 -3
- package/dist/runs.js +1 -0
- package/dist/runtime-governance.d.ts +5 -21
- package/dist/runtime-governance.js +4 -8
- package/dist/spec-fields.d.ts +15 -24
- package/dist/spec-fields.js +16 -17
- package/dist/task-settings.d.ts +18 -18
- package/dist/task-settings.js +57 -30
- package/dist/tool-approval.d.ts +24 -33
- package/dist/tool-approval.js +5 -7
- package/dist/trace/core-keyset-guard.d.ts +3 -3
- package/dist/trace/engine-notice-wire.d.ts +4 -6
- package/dist/trace/engine-notice-wire.js +14 -1
- package/package.json +2 -2
- package/scripts/drain-check-local.mjs +31 -6
- package/scripts/drain-check.mysql.sql +31 -6
- package/scripts/drain-check.pg.sql +38 -8
package/MIGRATION.md
CHANGED
|
@@ -7,6 +7,104 @@
|
|
|
7
7
|
> ⚠️ 完整清单在仓库根 `CHANGELOG.md`——它**不随 npm tarball 出包**(本文件随包)。看完整迁移窗的
|
|
8
8
|
> 权威姿势是源码 tag diff:`git diff v<旧>..v<新>`(每版都推 `v<版本>` tag);npm 包页也镜像 CHANGELOG。
|
|
9
9
|
|
|
10
|
+
## 7.117.0 —— 2026-10-06
|
|
11
|
+
|
|
12
|
+
### 7.117.0 线 —— `@sema-agent/core` 7.41.2 → 7.42.1:「这个调用不用问」只有一个去处(引擎的 allow 层)· 升级前排空带 `settings.permissions.allow` 的停驻行 · `APPROVAL_GRANT` 语义变宽 · 卡面披露键改名 `allowRuleShadowed` · 部署 hook 回 `allow` 现在清问 · 能力位 `settings.permissions.allowLayer` · 通告码改名 · 新错误码 `config.image_invalid`
|
|
13
|
+
|
|
14
|
+
**升级前必读(按顺序做)**
|
|
15
|
+
|
|
16
|
+
1. **排空带 `settings.permissions.allow` 的停驻行**。本版把请求的 allow 整份交给引擎的 allow 层(表里的调用免问,写 / shell 的普通问也清),读法记号换成新词。
|
|
17
|
+
升级时还停驻、提交体里带非空 `allow` 的任务 —— 不论是 7.115.0–7.116.x 写下的(当年 allow 只「表态」)还是更早的(当年是名单)—— 续跑一律
|
|
18
|
+
409 `checkpoint.unsupported_version`,行与卡一个字节不动(每个重放入口同一落点,含终态 run 的 steer 铸唤醒卡之前 —— 当场 409、不铸卡),出路是取消后作为新任务重提。升级前用随包排空判据数清零:
|
|
19
|
+
SQL 后端跑 `scripts/drain-check.mysql.sql` / `scripts/drain-check.pg.sql` 里 `permissions-allow-list` 那一段(期望 `hit_rows = 0`);
|
|
20
|
+
local 后端跑 `node scripts/drain-check-local.mjs`(末行 `PERMISSIONS-ALLOW-LIST rows=0`)。
|
|
21
|
+
2. **`APPROVAL_GRANT` 语义变宽(旋钮不改名)**。此前它只回答「没人表态」那一问、只收工具名形;本版起它是引擎 allow 层的部署成员
|
|
22
|
+
(每只 Runner 同一席,含 leader 与 run-local),清「普通问 + 没人表态」两类,**内置工具也在内**:`APPROVAL_GRANT=Bash` 此前什么也不放,本版起
|
|
23
|
+
整只 shell 不再问;`APPROVAL_GRANT=Write` 起文件写不再问。升级前逐条看一遍表里的内置工具名。`auto` 模式例外:引擎把 allow 层里的整只 shell 工具与
|
|
24
|
+
启动器前缀形(`python*` / `node` / `npx` 一类)剔出去交分类器 —— 请求 allow、技能清单、运维表三张同样。永远清不掉的:任何一层的 deny、
|
|
25
|
+
`APPROVAL_REQUIRE`、`APPROVAL_NEVER_AUTO`、`MANUAL_MODE_SHELL_GATE=always`、写保护路径、敏感写路径、治理、PreToolUse hook 的问、plan 模式。
|
|
26
|
+
同一工具既在 `APPROVAL_GRANT` 又在 `APPROVAL_REQUIRE` / `APPROVAL_DENY` ⇒ ask / deny 赢。
|
|
27
|
+
启动校验跟着引擎走:引擎读成空的条目(`*`、`mcp__*`、写路径形如 `Edit(src/**)`、域名形、带空白的名字)、协议名服务器段铸不出 ⇒ 拒启,
|
|
28
|
+
句子点名那一条、原因词与写法;内容形(`Bash(git:*)`、`Read(//srv/data/**)`)受理;退役名 / 短名照旧拒启(名字层四张表 —— 运维表、技能两只清单、请求三桶 —— 同一只判官、同一组句子);超过 1000 条拒启
|
|
29
|
+
(引擎每次准备都逐条播种这张表,平方级 —— 与请求 allow 桶、技能两只清单同一只帽)。
|
|
30
|
+
起服日志行 `approval_grant_in_force` 的 `note` 写着当前效果。
|
|
31
|
+
3. **卡面 / 帧披露键改名**:`tool_approval` 帧、`approval_request` 呈卡帧、`card_json`、重放帧上的 `persistedRuleShadowed`(字符串)与运维待批队列
|
|
32
|
+
行上的 `riskDescriptor.shadowedRule` 删除(无别名),换成 `allowRuleShadowed: { member, entries, omitted? }[]`(`member` ∈ `hook` / `deployment` /
|
|
33
|
+
`persisted` / `skill` / `request`;hook 的 `entries` 恒空)。卡上那句「命中但清不掉」的话在 `message` 里(引擎写的)。SQL 后端升级前停驻的行起服时
|
|
34
|
+
一次性改写成新键(`checkpoint.risk_descriptor` 那一列);local 后端的停驻行、两种后端已落盘的 run 结果(`checkpointGate`)上旧键原样留着
|
|
35
|
+
(只缺结构化披露,文本照旧脱敏显示)。
|
|
36
|
+
**滚动升级(SQL 多副本)前等在途的流内审批卡清零**:旧副本写下的流内卡(`approval_ask.card_json` 带旧键)新副本读不动 —— 断连重放看不到它、
|
|
37
|
+
同一只 ask 重入时停驻(声明无人值守即拒的部署上直接拒这次调用)、对它答「记住规则」得 `rule_material_absent`(裁决照常生效,规则不落)。
|
|
38
|
+
不写迁移(这些卡随流内审批窗短命)。怎么数:`SELECT COUNT(*) FROM approval_ask WHERE state = 'STREAM_PENDING' AND card_json LIKE '%"persistedRuleShadowed"%';`
|
|
39
|
+
期望 0(或接受窗内这些卡不重放)。local 后端是单进程,升级 = 重启,活的流内卡随进程结束(起服收敛把它们作废),无需此步。
|
|
40
|
+
4. **部署 PreToolUse hook 回 `allow` 现在清问**。此前 hook 的 allow 只回答「没人表态」;本版起它是 allow 层第一个成员:default 模式下写 / shell
|
|
41
|
+
不出卡,auto 不过分类器(plan 模式与运维的强制类照问)。只想旁观的 hook 不要回 `permissionDecision`;必须到人的问用上面的逐工具旋钮。
|
|
42
|
+
5. **排空技能拒侧清单里有读成空条目的停驻行**。本版的引擎逐字读规则条目:上一版引擎先去首尾空白,所以 `disallowedTools: ["Bash "]` 当年是一条有效的拒,
|
|
43
|
+
本版读成空、什么也不拒。本版对这种体在提交与续跑上同拒:升级时还停驻、提交体里技能(顶层 `skills[]` 或子代 `agents[].skills[]`)的 `disallowedTools`
|
|
44
|
+
带引擎读成空的条目(带空白 / 畸形的名字、`*`、域名形、协议名带通配)的任务,续跑 409 `resume_blocked_by_policy`(`blockedBy: request.field_invalid`,
|
|
45
|
+
句子点名那一条),行与卡不动;每个重放入口同一落点(decide / resume / plan_review / wake / SLA 到期拒扫跳过它 / 终态 run 的 steer 在铸唤醒卡之前当场 409、不铸卡);
|
|
46
|
+
出路是取消、改好条目重提。升级前数清零:SQL 后端跑两份 SQL 里 `skill-deny-inert` 那一段(期望 `hit_rows = 0`;SQL 不能照引擎逐条判读,数的是**真超集** =
|
|
47
|
+
技能〔顶层 / 子代〕`disallowedTools` 非空的停驻行;命中行逐条用服务那一只判精确核 —— 从**装好的新版包**里跑
|
|
48
|
+
`node -e 'import("@sema-agent/server/dist/rule-entry-list.js").then(m => console.log(m.skillDenyInertEntryOf(JSON.parse(process.argv[1]).body) ?? "ok"))' "$(<ctx.json)"`,
|
|
49
|
+
`ctx.json` = `checkpoint_ctx.ctx` 那一格导出的文本(workflow 子代的行用发起会话那一份);打出 `ok` 的行放过,打出句子的要排空;**不要**用「续跑一次看答复」核 ——
|
|
50
|
+
升级前续跑不会被拒,会真把决定执行掉);local 后端 `node scripts/drain-check-local.mjs` 末行之上的 `SKILL-DENY-INERT rows=0`(调服务同一只判,精确;
|
|
51
|
+
已知边界:它对 workflow 子代的停驻行读的是行自己会话的 ctx,而续跑腿读的是发起会话那一份 —— local 车道上有 workflow 子代停驻时,子代那几行把发起会话的
|
|
52
|
+
`checkpoint-ctx/<会话>.json` 信封里的 `ctx` 喂上面那条 `node -e` 再核一遍)。
|
|
53
|
+
**引擎侧同病的另一个持久面(本版不修,如实)**:7.116.x 上技能被激活时引擎把这类条目原串冻在停驻行的技能授予节里;本版引擎续跑时把整节判成读不出,续跑 fail-closed
|
|
54
|
+
答 `checkpoint.invalid_outcome`(reason `skill_grants_damaged` —— 原因词说的是「损坏」,实际是读法换了),后台子代的停驻赎回同样到这一句;本仓的续跑预判与 SQL 判据
|
|
55
|
+
都不覆盖这一面。升级前连同回滚判据 `skill-grants-rollback` 那一段一起数(它数的就是带技能授予节的停驻行),命中的行取消重提。
|
|
56
|
+
6. **技能清单的名字层与请求 allow 同判**。`skills[].allowedTools` / `disallowedTools`(顶层与子代)里写退役名 / 短名(例 `Task`)本版起提交 400(上一版受理,
|
|
57
|
+
引擎读作一个永不命中的名字);句子与请求 `settings.permissions.allow` 桶的同一组。
|
|
58
|
+
7. **未随本版修的既有边界(升级决策知悉)**:无耐久席位的部署上 `APPROVAL_REQUIRE` 整只不生效(既有);「总是允许」的结算判据漏 `ask_rule` 类(既有)。
|
|
59
|
+
|
|
60
|
+
**行为与面的变化**
|
|
61
|
+
|
|
62
|
+
- **运维日志事件改名(按事件名配告警 / 数行的要改配)**:存量停驻体「本版不重放」的拒现在只有一只判据(allow 读法记号 / 技能拒侧读成空两问),日志跟着收成一种字段形
|
|
63
|
+
(`kind` ∈ `allow_list_reading` / `skill_deny_inert`,allow 臂带 `era`、技能臂带 `layer`,`note` 一句出路):续跑入口 `resume_refused_allow_list_reading` → **`resume_refused_stored_body`**;
|
|
64
|
+
SLA 到期拒扫跳过臂 `deny_sweep_allow_list_reading_skipped`(字段 `sessionIds`)→ **`deny_sweep_stored_body_skipped`**(字段 `skipped: [{ sessionId, kind }]`);
|
|
65
|
+
终态 run 的 steer 铸唤醒卡前的拒新增 **`steer_wake_park_refused_stored_body`**(带 `taskId`)。
|
|
66
|
+
- **终态 run 的 `POST /v1/runs/:id/steer` 多一种答复**:这条会话的存量体本版不重放 ⇒ 铸唤醒卡之前当场拒、不铸卡 —— allow 读法记号那一问 409 `checkpoint.unsupported_version`,
|
|
67
|
+
技能拒侧读成空那一问 **409 `resume_blocked_by_policy`**(与 `/wake` 对同一体的答复逐字同;7.116.x 上是 202 `parked_for_wake` 再在 `/wake` 时拒)。
|
|
68
|
+
- 请求的 `settings.permissions.allow`:原样(顺序、空白、重复)交给引擎;内容形受理;条数上限仍是 1000(超了 400 `over_cap`,句子改说「合并成更宽的条目」);空串不再 400(引擎读成
|
|
69
|
+
`grammar` 并通告);写路径形 / 命令规则等内容形不再 422(7.116.x 的 allow 桶拒内容形);`*` / 带空白的名字上一版本就受理 —— 本版这类引擎读成空的条目收通告 `permission.allowed_tool_not_granted`(受众 = 用户),不拒。
|
|
70
|
+
退役名(`Task` / `bash` …)与无协议前缀的短名照旧 422,与 deny / ask 同一句(本仓只判名字,不判内容)。
|
|
71
|
+
协议名服务器段铸不出 / 缺段 / 写通配(`mcp__*`)仍 422(与技能清单同一判);`Read(~/…/**)` 受理(引擎的读目录形只有 `Read(//abs/**)`,`~/` 形读成空并通告)。
|
|
72
|
+
- 请求 `settings` 的 422(编不成生效规则 / 带永远不生效的条目):`error` 句首由 `settings are tighten-only and cannot loosen the deployment policy: ` 改为
|
|
73
|
+
`request settings cannot be applied as written: `(无别名);按句首字面匹配的客户端改读 `errorCode` 或新句首。
|
|
74
|
+
- 引擎过境的三条(本服务用户感受得到):auto 模式下持久的启动器前缀规则(`Bash(npm run:*)`)改交分类器(多问);被激活技能里的 shell 命令规则在 auto 下
|
|
75
|
+
命中即免问(少问);守卫 / 规则读不出 / 连拒回落三类问不再被持久规则与技能清单清掉(多问)。`permission_denied_total{deniedBy}`:被打断 ∧ 规则拒的调用
|
|
76
|
+
从 `ask_resolution` 桶挪到 `policy` 桶。
|
|
77
|
+
- 请求自带技能的 `allowedTools` 与 `settings.permissions.allow` 读法完全相同(此前前者更宽)。技能两只清单各自超过 1000 条 ⇒ 400(顶层与子代两处;
|
|
78
|
+
此前无帽 —— 引擎逐条播种是平方级,与请求 allow 桶同一只帽)。
|
|
79
|
+
- 技能的 `disallowedTools` 里引擎读成空的条目(`*`、域名形、协议名带通配、带空白 / 畸形的名字)提交即 400(顶层 `skills[]` 与 `agents[].skills[]`);
|
|
80
|
+
续跑重放同一份体 ⇒ 409 `resume_blocked_by_policy`(同一句,见升级前必读第 5 条)。
|
|
81
|
+
- 通告码 `skills.allowed_tool_not_granted` 改名 `permission.allowed_tool_not_granted`(受众从运维改为用户,上会话流;`detail.entries[].source` = `skill` /
|
|
82
|
+
`request` / `deployment`)。按码路由的消费方换词。
|
|
83
|
+
- 能力位:`GET /v1/capabilities` 的 `settings.permissions` 新增 `allowLayer: true`(恒真);`allowGrant` 仍为真。客户端据 `allowLayer === true` 判断
|
|
84
|
+
这台 server 把请求的 allow 交给引擎 allow 层(键缺席 = 7.115.0–7.116.x:allow 只表态,内容形 422)。
|
|
85
|
+
- 新错误码 `config.image_invalid`(同步腿 400):请求内联图片的类型不在 `image/png` / `jpeg` / `gif` / `webp`、字节与声明的类型签名不符、或超 5 MiB
|
|
86
|
+
⇒ 第一次模型调用之前拒。修前是模型供应方的报错。发送 API 自己的类型词与文件的原样字节。
|
|
87
|
+
- `tool_end.gate.disposition`:一只被打断的调用若同时被规则拒,`deniedBy` 由 `ask_resolution` 变 `policy`。
|
|
88
|
+
- 滚动升级窗 / 回滚(如实):新旧副本并存时,在新副本读到 `allowLayer` 后提交的 allow 若落到旧副本,被按「表态」读 —— 工具名形多问(方向安全),
|
|
89
|
+
**内容形 allow(`Bash(git:*)`、`Read(//dir/**)`、`Edit(src/**)`)在旧副本上整请求 422**(那一版的受理口不收内容形);并且排空判据清零之后,旧副本仍会写下
|
|
90
|
+
记号是 `"statement"` 的停驻行(它带 allow 的新提交),这些行到了新副本上续跑被拒 —— 滚动窗内对带 allow 的停驻行再跑一次排空判据,或整队一次切换。
|
|
91
|
+
回滚到 7.116.x 之前,本版写下的停驻行(记号 `allow_layer`)在旧版上被拒(旧版只认 `statement`)—— 回滚前同样排空。
|
|
92
|
+
|
|
93
|
+
### 7.117.0 线 —— 出网代理:http 目标改走绝对 URL 形请求(不再对代理发 `CONNECT host:80`)
|
|
94
|
+
|
|
95
|
+
- **行为变了(出网代理后面的 http 目标)**:配了 `HTTP_PROXY` / `HTTPS_PROXY`、目标是 `http://` 地址(典型:`http://` 模型网关)时,本服务现在向代理发**绝对 URL 形请求**
|
|
96
|
+
(`POST http://gateway/v1/chat/completions`),不再发 `CONNECT gateway:80`;https 目标照旧 `CONNECT host:443`。只放行 `:443` 隧道的代理(squid 缺省策略)原先拒掉 http 网关的 `CONNECT :80`、run 约 3 分钟后失败 —— 本版起可用。
|
|
97
|
+
范围:目标 `http:` 且代理 URL 也是 `http:`;`https://` 代理 + http 目标仍 `CONNECT`。`NO_PROXY`、回环恒直连、代理认证头(userinfo ⇒ `Proxy-Authorization`)不变。
|
|
98
|
+
- **① 谁受影响**:(a)代理只认 `CONNECT`、不转发绝对 URL 形请求的部署(罕见)—— http 网关会在代理处被拒(`407` / `403` / `400` 视代理而定)。出路:网关改 https,或把它写进 `NO_PROXY`。
|
|
99
|
+
(b)在代理上按 `CONNECT` 记账 / 鉴权 / 审计的部署 —— http 目标不再出现 `CONNECT` 记录,而是明文请求行。
|
|
100
|
+
- **② 🔴 凭据头会经过代理**:http 目标的请求行与头(含发给网关的 `Authorization` 等凭据头)以**明文**经过代理,代理看得到完整 URL 与头(隧道里则是密文)。**要对代理保密,网关用 https。**
|
|
101
|
+
(此前 http 网关走 `CONNECT :80` 隧道,代理看到的也是明文字节但不解析请求;现在代理按请求解析并可能记录 URL 与头。)
|
|
102
|
+
- **③ 代理拒绝的读法(仅 http 目标那一腿)**:绝对 URL 形里代理的回话是普通 HTTP 响应。`407`(要求代理认证)仍读作代理拒 —— `via outbound proxy (from HTTP_PROXY) ← proxy answered the request with HTTP 407`
|
|
103
|
+
(与 CONNECT 腿的 `… answered CONNECT with HTTP 407` 同一只错误、同一个包装句,只差根因句里的一个词);`403` 等其余状态**不再**是代理拒:它与网关自己回的 403 在 HTTP 层原理上分不开,
|
|
104
|
+
调用方读到一条普通 403 响应(「网关回了 403」)—— 代理的 ACL 拒绝请对着代理日志查(此前 http 网关被代理的 `CONNECT :80` 403 拒掉时读到 `… answered CONNECT with HTTP 403`)。
|
|
105
|
+
代理**拨号**失败(拒连 / 解析不了 / 超时 / 代理 TLS)两条腿同一个句式(`via outbound proxy (from HTTP_PROXY) ← …`)、同一个 `proxyInUse`。
|
|
106
|
+
- **④ 补偿**:没有旋钮 —— 出路就是上面的两条(网关改 https / 写进 `NO_PROXY`)。回滚到 7.116.x 即恢复 `CONNECT :80`。
|
|
107
|
+
|
|
10
108
|
## 7.116.0 —— `@sema-agent/core` 7.40.1 → 7.41.2:技能 `manifest` 退役(400)· 技能清单从围栏改成本次 run 的规则来源(也是一次放宽)· 引擎 `Skill` 卡上「本会话放行」只放行一次(新卡面位 `rememberRefused`)· 新请求键 `allowedTools` / `disallowedTools` / `userInvokedSkills` 与能力位 `userInvokedSkills` / `skillToolLists` · 停驻的 `Skill` 卡两种拒法 · 回滚到 7.115.x 前排空停驻行 · 排空配方进包 · 新询问类 `requires_user_interaction` · 停驻批准后的调用再过一次 PreToolUse · 文件店拒绝句 / 会话同步导出 / 「没人能答」拒句 / 旧工具名拒体的四处读面小改 · 服务器段铸不出的协议规则名拒 · local 重启后对上一世审批卡的作答 200 → 410 · run 死亡判决的机读码 `errorCode`(含启动期一次性迁移补码)· 两段覆盖形 `mcp__<server>` 在配置表受理 · steer 的 `priority` 交给引擎(能力位 `fleet.steerPriority` → `"engine"`)· 能力位与端点的不可用答法读同一判据(多族 403 / 404 → 501、`a2aServe` 在无后端部署上由真变假、新位 `sessionSyncFileHistory` / `workflowsJournal`)· 后台子代三张读面的文本变了 · run-local 无终端拒句按问的类说话
|
|
11
109
|
|
|
12
110
|
- **🔴 技能的 `manifest` 退役(硬破坏,零别名)**。`body.skills[].manifest` 任何值 ⇒ 400 `request.field_invalid`(句子 `skill.manifest was retired in core 7.41.0 — …`);
|
package/USAGE.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 调用方手册(OA / 智能体 / 你自己)
|
|
2
2
|
|
|
3
3
|
> 一句话心智模型:**你只对一个地址发普通 HTTP 请求——LB `http://<host>:8090`;请求体里只写"要做什么"。**
|
|
4
|
-
> 工具面与安全策略由服务端 `resolveSpec` 注入,请求体**替换不了**它们(`tools` 不是受理键,传了 400;按请求只能**减**:`excludeTools` / `settings.permissions` 的 deny · ask;7.
|
|
4
|
+
> 工具面与安全策略由服务端 `resolveSpec` 注入,请求体**替换不了**它们(`tools` 不是受理键,传了 400;按请求只能**减**:`excludeTools` / `settings.permissions` 的 deny · ask;7.117.0 起 `settings.permissions.allow` 整份交给引擎的 allow 层:表里的调用免问(普通问与没人表态两类,内置工具的写门 / shell 普通问也清),deny / ask 规则与运维的强制类照赢;能力位 `settings.permissions.allowLayer`)。
|
|
5
5
|
> `model` 与 `systemPrompt` / `appendSystemPrompt` 是受理键,但 `model` 只能点服务端已配置目录里的名字 / 档位词 / 模型 id(未知 ⇒ 400)。凭据永远不进模型 / 请求体。
|
|
6
6
|
> 你不"调度 worker",你只发任务;LB + TiDB 自己协调。每个字段都来自当前真实代码。
|
|
7
7
|
|
|
@@ -881,34 +881,30 @@ UNATTENDED_APPROVAL_POLICY=park
|
|
|
881
881
|
(`config.env_key_retired`,任何值都拒);从 `.env` 里删掉它。要让机器多答某一类问,先立案,不开旋钮。
|
|
882
882
|
- 写面:`default` 模式不带写面 ⇒ 工作区根内(`roots`,与 7.105.0 同)。 `roots` 只约束 Write / Edit 这类结构化文件工具;shell 的根外写、Read 工具的根外读属 core `ordinary`,这些腿上由机器答(落地结果与 ≤7.108.0 的 bypass 放行同,门记录归属词见 MIGRATION 7.109.0「审计读者」)。
|
|
883
883
|
|
|
884
|
-
**可选 — 运维的 allow
|
|
884
|
+
**可选 — 运维的 allow 表 `APPROVAL_GRANT`(缺省关;7.117.0 起语义变宽)**
|
|
885
885
|
|
|
886
886
|
```bash
|
|
887
|
-
APPROVAL_GRANT='mcp__figma__*,a2a__planner__
|
|
887
|
+
APPROVAL_GRANT='mcp__figma__*,a2a__planner__*,Bash(git:*)' # 逗号分隔的 allow 规则:这些调用不用问
|
|
888
888
|
```
|
|
889
|
-
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
读边界)照旧。无人值守腿的按类作答席不因本键多答任何一类 —— 本键只让运维**点名过的**调用不再出卡。
|
|
903
|
-
- **不是白名单**:它只给出表态,不收窄任何东西(「只许这些工具」是会话规则 `PUT /v1/sessions/:id/policy` 的 `toolAllow`)。
|
|
889
|
+
- **是什么**:引擎 allow 层的**部署成员**(`RunnerDeps.allowedTools`)。本进程造的**每一只** Runner 都带同一席(HTTP 提交 / durable 续跑 / 停驻后台 agent 赎回 /
|
|
890
|
+
`run-local` / leader 的首跑与修复腿,一条规则不随腿而异),Runner 构造时读一次;只走 env,改了要重启。命中 = 免问:清「普通问 + 没人表态」两类,
|
|
891
|
+
**内置工具也在内** —— `APPROVAL_GRANT=Bash` 起整只 shell 不再问,`Write` 起文件写不再问(7.115.0–7.116.x 只回答「没人表态」、只收工具名形)。
|
|
892
|
+
- **为什么要它**:询问类模式下没人表态的 MCP 服务器工具 / A2A 技能调用会出卡;请求腿可以在请求体里写 `settings.permissions.allow`,`run-local` /
|
|
893
|
+
leader 没有请求体 —— 运维为自己接的服务器、自家信得过的命令,在这里写一次。
|
|
894
|
+
- **值形(与引擎的文法相同)**:工具名、`mcp__<server>__*` 或两段形 `mcp__<server>`、`a2a__<peer>__*`、`Bash(<命令>)` / `Bash(<前缀>:*)` 命令规则、
|
|
895
|
+
`Read(//<dir>/**)` 读目录。`auto` 模式下交出任意代码的条目(整只 shell、解释器 / 启动器前缀)交分类器判(引擎的剔除表)。
|
|
896
|
+
坏值 ⇒ **启动拒**(句子点名那一条、引擎给的原因词、怎么写):引擎读成空的条目(`*`、`mcp__*`、写路径形 `Edit(src/**)`、域名形、带空白的名字)、
|
|
897
|
+
协议名服务器段铸不出 / 缺段 / 写通配。内容形里带逗号的规则写不进(这张表的分隔符就是逗号)。
|
|
898
|
+
- **永远清不掉的**:任何一层的 deny(`APPROVAL_DENY`、请求 / 会话规则的 deny、命令策略拒档、敏感写路径)、ask 规则(`APPROVAL_REQUIRE`、请求的 ask)、
|
|
899
|
+
`APPROVAL_NEVER_AUTO`、`MANUAL_MODE_SHELL_GATE=always`、写保护路径、治理、PreToolUse hook 的问、plan 模式。命中而清不掉时卡上带
|
|
900
|
+
`allowRuleShadowed: [{ member: "deployment", entries: [...] }]`。同一工具既在本键又在 `APPROVAL_REQUIRE` / `APPROVAL_DENY` ⇒ ask / deny 赢。
|
|
901
|
+
- **不是白名单**:它只放,不收窄任何东西(「只许这些工具」是会话规则 `PUT /v1/sessions/:id/policy` 的 `toolAllow`)。
|
|
904
902
|
- **射程如实**:规则对**每一个租户、每一条腿**生效;`mcp__<server>__*` 也覆盖那台服务器将来新增的工具。按需写窄;要收回某一只,写
|
|
905
|
-
`APPROVAL_DENY` / `APPROVAL_REQUIRE`(压过本键)
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
-
|
|
909
|
-
|
|
910
|
-
= 规则名原样(空表 = 未设);`GET /v1/config/catalog` 一行。客户端能力面(`/v1/capabilities`)不报它。
|
|
911
|
-
- **启动告警 `tool_policy_only_sensitive_baseline`**(既有事件,名字不改):没有审批席(`DURABLE_APPROVAL` 关)且不是单用户的部署上,若部署唯一的策略是「不是门」的那几层 —— 敏感路径守卫集(`SENSITIVE_WRITE_PATTERNS`),或 7.115.0 起只有本键 —— 起服时发一次:它们让 `spec.toolPolicy` 在场、引擎自己的 UNGATED 告警因此不再出现,而它们自己又不判写。`detail` 点名是哪一片,`guardedPatterns` / `grantRules` 两个计数。
|
|
903
|
+
`APPROVAL_DENY` / `APPROVAL_REQUIRE`(压过本键),或删掉规则重启。
|
|
904
|
+
- **A2A 如实**:运维声明的对端(配置中心 / `config.d`,刻意不收 `toolAxes`)上,每次技能调用都是引擎自己的外发标记问(`tool_marks_a2a`),allow 层清不掉它;
|
|
905
|
+
只有请求体自带的对端(单用户闸)能在 `toolAxes` 里把某个技能的 `egress` 降为 `false`,那之后才是普通问,本键才清得掉。
|
|
906
|
+
- **看得见**:起服日志一行 `approval_grant_in_force`(条数 + 规则 + 一句当前效果:清什么、清不掉什么);`GET /v1/diagnostics/wiring`(运维面)`serverGates.approvalGrant`
|
|
907
|
+
= 规则原样(空表 = 未设);`GET /v1/config/catalog` 一行。客户端能力面(`/v1/capabilities`)不报它。
|
|
912
908
|
- v1 只有 env 一个写入通道(配置中心与 settings 契约没有这个键)。
|
|
913
909
|
|
|
914
910
|
**可选 — 写保护名表(S-138;缺席=引擎缺省表在岗)**
|
|
@@ -2163,7 +2159,7 @@ docker 推送的鉴权走构建宿主上的 docker 凭据存储(`docker login`),
|
|
|
2163
2159
|
|
|
2164
2160
|
### 9.8 出网代理:`HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY`(7.103.0 起)
|
|
2165
2161
|
|
|
2166
|
-
server 进程(HTTP 服务与 `run-local`)**所有走 undici 的出网跳**都认这三只平台标准变量:引擎 → 模型网关(
|
|
2162
|
+
server 进程(HTTP 服务与 `run-local`)**所有走 undici 的出网跳**都认这三只平台标准变量:引擎 → 模型网关(http 网关发**绝对 URL 形请求**、https 网关经 `CONNECT` 隧道)、
|
|
2167
2163
|
配置中心拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP、hook 模型调用、core 的 MCP streamable-http / A2A / WebFetch 工具 …
|
|
2168
2164
|
实现是**一只**进程级 dispatcher,在启动时装一次;没有另外的开关 —— 这三只变量本身就是配置通道。
|
|
2169
2165
|
|
|
@@ -2174,6 +2170,14 @@ server 进程(HTTP 服务与 `run-local`)**所有走 undici 的出网跳**都认
|
|
|
2174
2170
|
带路径 / 查询的值 undici 用不了 ⇒ **拒启**,拒句带码 `[config.outbound_proxy_invalid]`(带 `scheme://` 却在 host:port 之后还有路径 / 点段 / 查询 /
|
|
2175
2171
|
片段的值,本仓先于 undici 拒 —— 这一道各运行时同,Bun 下也拒);回显只出 `scheme//host[:port]`,形不干净(有路径 /
|
|
2176
2172
|
查询 / 片段 —— 未编码的特殊字符会让密码落进这几段)整只占位 `«redacted:url»`。Bun 运行时不校验(见下)。
|
|
2173
|
+
- **http 目标与 https 目标走不同的代理形(7.117.0 起)**:目标是 `http://` ⇒ 向代理发**绝对 URL 形请求**(`POST http://gateway/v1/chat/completions`),不建 `CONNECT` 隧道;
|
|
2174
|
+
目标是 `https://` ⇒ `CONNECT host:443`,隧道里再握手。7.116.x 及以前 http 目标也被发 `CONNECT host:80` —— 只放行 `:443` 隧道的代理(squid 缺省策略)拒掉它,run 约 3 分钟后失败。
|
|
2175
|
+
范围按 undici 划:目标 `http:` **且**代理 URL 也是 `http:` 才走绝对 URL 形;`https://` 代理 + http 目标仍是 `CONNECT`。
|
|
2176
|
+
🔴 **要知道的三件**:① **明文过代理**:http 目标的请求行与头(含发给网关的凭据头)以明文经过代理,代理看得到完整 URL 与头;要对代理保密,网关用 https(隧道里是密文)。
|
|
2177
|
+
② 代理按 `CONNECT` 记账 / 鉴权、且不转发绝对 URL 形请求的部署(罕见):把该网关改成 https,或把它写进 `NO_PROXY`。
|
|
2178
|
+
③ **代理拒绝的读法**:`CONNECT` 腿上代理回非 200 ⇒ 下面「代理那一跳失败时怎么读」的 `via outbound proxy` 句;绝对 URL 形里代理的回话是**普通 HTTP 响应**,分两种 ——
|
|
2179
|
+
**407(要求代理认证)同样读成 `via outbound proxy (from <变量>) ← proxy answered the request with HTTP 407`**(407 按协议只可能来自代理,两条腿同一种终局;一个自己回 407 的源站是违约,也会被读作代理);
|
|
2180
|
+
**403 等其余状态不是代理拒**:它与网关自己回的 403 在 HTTP 层原理上分不开(没有通用判别式),调用方读到一条普通 403 响应(「网关回了 403」),代理的 ACL 拒绝请对着代理自己的日志查。网关自己回的 401 / 403 照原样交给调用方,不会被读成代理拒。
|
|
2177
2181
|
- **`NO_PROXY` 语法 = undici 的**:逗号或空白分隔;`host` 精确匹配**且含其子域**;`.host` / `*.host` 后缀;`host:port` 限端口;整串 `*` = 全直连;
|
|
2178
2182
|
IPv6 写方括号形 `[fd00::1]`。**不支持 CIDR / IP 段**(`10.0.0.0/8`、`10.*` 都不生效):按 IP 访问的目标逐个列(可带 `:port`)。
|
|
2179
2183
|
- 🔴 **回环恒直连,不看 `NO_PROXY`**:目标是 `localhost` / `127.0.0.0/8` / `::1` / `::ffff:127.x` / `0.0.0.0` / `::` 时一律直连 —— 本机网关、本机中心
|
|
@@ -2191,7 +2195,8 @@ server 进程(HTTP 服务与 `run-local`)**所有走 undici 的出网跳**都认
|
|
|
2191
2195
|
**从拨号起计的绝对期限** 10 s(= undici 连接阶段的缺省时限;连代理的 TCP / TLS 与 CONNECT 交换都算在内,代理中途吐字节也不续命)—— 到点
|
|
2192
2196
|
CONNECT 还没回完 ⇒ 根因 `proxy did not answer CONNECT within 10000 ms`、那条连接随之关掉;调用方先取消时请求当场结束,挂着的那次拨号同样
|
|
2193
2197
|
在期限内收口、不留连接。请求当场失败,不重连(代理收下连接就关也一样)。隧道建好之后的失败(目标的 TLS、
|
|
2194
|
-
目标的响应)不带这段前缀 ——
|
|
2198
|
+
目标的响应)不带这段前缀 —— 那是目标本身的事。**http 目标(绝对 URL 形)那一腿**:拨号失败(拒连 / 解析不了 / 超时 / 代理 TLS)同一个包装句、同一个 `proxyInUse`;
|
|
2199
|
+
10 s 拨号期限与「CONNECT 回非 200」只属于 `CONNECT` 腿 —— 绝对 URL 形的请求发出之后就是普通请求,代理收下连接就关、中途断流、回 4xx(407 除外,见上一条③)都与目标自己的失败同形,不冠这段前缀。
|
|
2195
2200
|
- **启动日志**:恰一行 `outbound_proxy`,`{httpProxy, httpsProxy, noProxy, loopbackDirect, runtime}`(代理值 display-safe;`httpsProxy` 是 https
|
|
2196
2201
|
目标**实际**走的那只,含回落)。配置目录 `GET /v1/config/catalog` 有 `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` 三行(`limitsHttp` 组,回显同一份
|
|
2197
2202
|
display-safe 投影)。`/health` 与 fleet 公告**不**带代理信息。
|
package/dist/approval-card.d.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* 没有方法、没有捕获的行为。
|
|
22
22
|
*/
|
|
23
23
|
import { z } from "zod";
|
|
24
|
-
import { READ_ROOT_CANDIDATE_DIR_MAX, type AskClass, type AskEvidenceAbsence, type AskRequest as CoreAskRequest, type PendingAction as CorePendingAction, type ReadRootGrantCandidate, type RuleOffer } from "@sema-agent/core";
|
|
24
|
+
import { ALLOW_RULE_MEMBERS, READ_ROOT_CANDIDATE_DIR_MAX, type AskClass, type AskEvidenceAbsence, type AskRequest as CoreAskRequest, type PendingAction as CorePendingAction, type ReadRootGrantCandidate, type RuleOffer } from "@sema-agent/core";
|
|
25
25
|
import type { AskRow } from "./plugins/approval-ask-store-sql.js";
|
|
26
26
|
/** 模型自由文本(`sourceAgentName` / `delegation.agentName`)的限长(设计稿 §6.2)。设计 §3.1 把
|
|
27
27
|
* 「限长 + 脱敏」写成 **server 新增责任**(引擎无此层):spawning model 挑的名字是自由文本,
|
|
@@ -126,7 +126,7 @@ export declare const RuleOfferSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
126
126
|
* `password=…` 换成更长的遮蔽标记)。拿带 `.max()` 的形去校验**脱敏前**的原文,再脱敏,产出的就可能是
|
|
127
127
|
* 一条超限的文本;而任何**回读**路径若再跑一遍同一个函数,那一条会在第二次校验时被整条丢掉 —— 于是
|
|
128
128
|
* 「落库时在、读回时不在」,并且**只发生在有回读的那条腿上**(SQL 有列回读、LOCAL 直接从 blob 投),
|
|
129
|
-
* 两个后端对同一份素材给出不同的卡面。同族先例逐字在 `tool-approval.ts` 的 `persistedRuleShadowed`
|
|
129
|
+
* 两个后端对同一份素材给出不同的卡面。同族先例逐字在 `tool-approval.ts` 的 `allowRuleShadowed`(7.41.x 时代叫 `persistedRuleShadowed`)
|
|
130
130
|
* 发帧点(「先脱敏再截,一次算定」)。
|
|
131
131
|
*
|
|
132
132
|
* ⇒ 正确顺序:**结构判(本 schema)→ 脱敏 → 截长 → 结果按 {@link RuleOfferSchema} 必然合形**。
|
|
@@ -282,7 +282,7 @@ export declare function readProbeCause(req: unknown): ProbeCauseProjection | und
|
|
|
282
282
|
* · 长度:超过 {@link MAX_PROBE_REASON_CODEPOINTS} ⇒ **整只丢,不截**(判据与 `probeCause.code` 超限同形);
|
|
283
283
|
* 先判长度再脱敏 ⇒ 喂给脱敏器的永远是个短串(脱敏器的 RangeError 面是超大输入)。
|
|
284
284
|
* · 内容族(部署自写的探针散文,人眼信任边界上的文本)⇒ `redactSecrets`。脱敏后**不再截**:耐久面同样不截,
|
|
285
|
-
* 两面逐字节同值比「帧上短一截」重要(
|
|
285
|
+
* 两面逐字节同值比「帧上短一截」重要(allow 层披露那一格 R2-[medium] 的反面:那里有卡面要对齐,这里没有)。
|
|
286
286
|
* · 缺席 ≠「没有原因」:只有 maybe 档收紧且探针真给了 reason 的 ask 才有它。
|
|
287
287
|
*/
|
|
288
288
|
export declare function readProbeReason(req: unknown): string | undefined;
|
|
@@ -337,6 +337,23 @@ export declare const RuleEvidenceSchema: z.ZodObject<{
|
|
|
337
337
|
personalRuleDotsAbsent: z.ZodOptional<z.ZodCustom<AskEvidenceAbsence, AskEvidenceAbsence>>;
|
|
338
338
|
}, z.core.$strict>;
|
|
339
339
|
export type RuleEvidenceProjection = z.infer<typeof RuleEvidenceSchema>;
|
|
340
|
+
/** 一条披露记录列出的条目上限 —— 防御值(引擎自己的上限更小,超出部分它计进 `omitted`);本层超出同样计进 `omitted`,不丢、不截条目个数以外的东西。 */
|
|
341
|
+
export declare const MAX_ALLOW_RULE_SHADOW_ENTRIES = 32;
|
|
342
|
+
/** 一条 allow 层披露记录的投影形(core `AllowRuleShadow` 的值形,条目已脱敏截长)。 */
|
|
343
|
+
export interface AllowRuleShadowProjection {
|
|
344
|
+
member: (typeof ALLOW_RULE_MEMBERS)[number];
|
|
345
|
+
entries: string[];
|
|
346
|
+
omitted?: number;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* `AskRequest.allowRuleShadowed`(core 7.42.0 #1327)的**边界窄读** —— 活卡帧(`ToolApprovalFrame.allowRuleShadowed`)与 `card_json`
|
|
350
|
+
* (`buildApprovalCard`)的**唯一**铸造点,两面结构性同值(与 {@link readRuleEvidence} 同款分工)。姿势:
|
|
351
|
+
* · 整只形不合(不是数组 / 某条成员不在 core 闭集 / 条目不是字符串表 / `omitted` 不是正整数)⇒ 按缺席处置(半解上卡比缺席更坏);空数组 = 缺席;
|
|
352
|
+
* · 条目 = 内容族 ⇒ `redactSecrets` 后截 `MAX_RULE_TEXT_CHARS`,**先脱敏再截一次算定**(redact 会变长,两面各截会漂);超过
|
|
353
|
+
* {@link MAX_ALLOW_RULE_SHADOW_ENTRIES} 的条目计进 `omitted`(与引擎的计数相加);
|
|
354
|
+
* · 不包 try / catch:值来自引擎(已过引擎自己的 `readAllowRuleShadows` 判形),是纯数据;一只抛错的 getter 在这里抛 = 响亮失败,不是静默缺席。
|
|
355
|
+
*/
|
|
356
|
+
export declare function readAllowRuleShadowed(req: unknown): AllowRuleShadowProjection[] | undefined;
|
|
340
357
|
/**
|
|
341
358
|
* `AskRequest.ruleEvidence`(core 5.35.0 [ref] G-2)的**边界窄读**——活卡帧(`ToolApprovalFrame.
|
|
342
359
|
* ruleEvidence`)与 `card_json`(`buildApprovalCard`)的**唯一**铸造点,两面结构性同值(与
|
|
@@ -345,7 +362,7 @@ export type RuleEvidenceProjection = z.infer<typeof RuleEvidenceSchema>;
|
|
|
345
362
|
* · **恰一不变量**:一对成员(值 / 缺席词)**双在场** ⇒ 整只丢——那是矛盾证据(引擎盖章的恰一,
|
|
346
363
|
* 双在场只可能是第三方 producer 的坏行),半解上卡比缺席更坏。双缺席**容忍**(老 producer 行)。
|
|
347
364
|
* · `orgRule` 内容族(管理员写的规则文本)⇒ `redactSecrets` 后截 `MAX_RULE_TEXT_CHARS`,**先脱敏
|
|
348
|
-
* 再截一次算定**(
|
|
365
|
+
* 再截一次算定**(allow 层披露那一格 R2-[medium] 教训:redact 会变长,两面各截会漂);空串丢整只。
|
|
349
366
|
* · `dots.actor` 身份串 ⇒ `redactSecrets`(对正常 id 恒等);超限**整只丢**(见上限顶注,身份不可截)。
|
|
350
367
|
* · `orgRevision` / 缺席词 verbatim(数值 / 闭集词,非内容)。
|
|
351
368
|
*/
|
|
@@ -568,7 +585,17 @@ export declare const ApprovalCardSchema: z.ZodObject<{
|
|
|
568
585
|
governanceForced: z.ZodOptional<z.ZodLiteral<true>>;
|
|
569
586
|
rememberRefused: z.ZodOptional<z.ZodLiteral<true>>;
|
|
570
587
|
inputHasBidi: z.ZodOptional<z.ZodLiteral<true>>;
|
|
571
|
-
|
|
588
|
+
allowRuleShadowed: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
589
|
+
member: z.ZodEnum<{
|
|
590
|
+
hook: "hook";
|
|
591
|
+
skill: "skill";
|
|
592
|
+
deployment: "deployment";
|
|
593
|
+
request: "request";
|
|
594
|
+
persisted: "persisted";
|
|
595
|
+
}>;
|
|
596
|
+
entries: z.ZodArray<z.ZodString>;
|
|
597
|
+
omitted: z.ZodOptional<z.ZodNumber>;
|
|
598
|
+
}, z.core.$strict>>>;
|
|
572
599
|
ruleOffers: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
573
600
|
kind: z.ZodLiteral<"single">;
|
|
574
601
|
rule: z.ZodString;
|
|
@@ -684,7 +711,17 @@ export declare const ApprovalCardEnvelopeSchema: z.ZodObject<{
|
|
|
684
711
|
governanceForced: z.ZodOptional<z.ZodLiteral<true>>;
|
|
685
712
|
rememberRefused: z.ZodOptional<z.ZodLiteral<true>>;
|
|
686
713
|
inputHasBidi: z.ZodOptional<z.ZodLiteral<true>>;
|
|
687
|
-
|
|
714
|
+
allowRuleShadowed: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
715
|
+
member: z.ZodEnum<{
|
|
716
|
+
hook: "hook";
|
|
717
|
+
skill: "skill";
|
|
718
|
+
deployment: "deployment";
|
|
719
|
+
request: "request";
|
|
720
|
+
persisted: "persisted";
|
|
721
|
+
}>;
|
|
722
|
+
entries: z.ZodArray<z.ZodString>;
|
|
723
|
+
omitted: z.ZodOptional<z.ZodNumber>;
|
|
724
|
+
}, z.core.$strict>>>;
|
|
688
725
|
ruleOffers: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
689
726
|
kind: z.ZodLiteral<"single">;
|
|
690
727
|
rule: z.ZodString;
|
|
@@ -807,9 +844,6 @@ export interface ApprovalCardSource {
|
|
|
807
844
|
/** E-14:bidi 在场位 —— 与 wire 帧**同一份素材**(`ToolApprovalFrame` 结构上满足本接口)⇒ live 帧 /
|
|
808
845
|
* `approval_request` / `card_json` / 重放帧四面同源,不是四处各扫一遍。 */
|
|
809
846
|
inputHasBidi?: true;
|
|
810
|
-
/** [ref](core 5.25.0):被越级的持久规则原文 —— **已 redactSecrets**(发帧点做,本模块只 clip)。
|
|
811
|
-
* 与 wire 帧**同一份素材**(`ToolApprovalFrame` 结构上满足本接口)⇒ live 帧 / `card_json` / 重放帧三面同源。 */
|
|
812
|
-
persistedRuleShadowed?: string;
|
|
813
847
|
/** [ref](core 5.58.0):引擎铸的「不再询问」OFFER(**只在规则店装配时**由发帧点填;语义与在场性契约见
|
|
814
848
|
* `ApprovalCardSchema.ruleOffers`)。与 wire 帧**同一份素材** ⇒ live 帧 / `card_json` / 重放帧
|
|
815
849
|
* 三面同源。 */
|
package/dist/approval-card.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { ASK_EVIDENCE_ABSENCE_VALUES, MAX_RULE_TEXT_CHARS, READ_ROOT_CANDIDATE_DIR_MAX, isAskClass, isAskOrigin, isPersistedRuleMandate, screenAskClassMembers } from "@sema-agent/core";
|
|
2
|
+
import { ALLOW_RULE_MEMBERS, ASK_EVIDENCE_ABSENCE_VALUES, MAX_RULE_TEXT_CHARS, READ_ROOT_CANDIDATE_DIR_MAX, isAskClass, isAskOrigin, isPersistedRuleMandate, screenAskClassMembers } from "@sema-agent/core";
|
|
3
3
|
import { redactSecrets } from "./trace/redact.js";
|
|
4
4
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
5
5
|
import { PERSISTED_RULE_MATCHES, UNCOVERED_SEGMENT_REASONS } from "./permission-rule-vocab.js";
|
|
@@ -211,6 +211,28 @@ const RuleEvidenceEnvelopeSchema = z.object({
|
|
|
211
211
|
})
|
|
212
212
|
.optional(),
|
|
213
213
|
});
|
|
214
|
+
export const MAX_ALLOW_RULE_SHADOW_ENTRIES = 32;
|
|
215
|
+
const ALLOW_RULE_MEMBER_SET = new Set(ALLOW_RULE_MEMBERS);
|
|
216
|
+
export function readAllowRuleShadowed(req) {
|
|
217
|
+
const raw = req !== null && typeof req === "object" ? req.allowRuleShadowed : undefined;
|
|
218
|
+
if (!Array.isArray(raw) || raw.length === 0 || raw.length > ALLOW_RULE_MEMBERS.length)
|
|
219
|
+
return undefined;
|
|
220
|
+
const list = raw;
|
|
221
|
+
const out = [];
|
|
222
|
+
for (const r of list) {
|
|
223
|
+
if (r === null || typeof r !== "object")
|
|
224
|
+
return undefined;
|
|
225
|
+
const { member, entries, omitted } = r;
|
|
226
|
+
if (!ALLOW_RULE_MEMBER_SET.has(member) || !Array.isArray(entries) || !entries.every((e) => typeof e === "string"))
|
|
227
|
+
return undefined;
|
|
228
|
+
if (omitted !== undefined && !(typeof omitted === "number" && Number.isInteger(omitted) && omitted > 0))
|
|
229
|
+
return undefined;
|
|
230
|
+
const shown = entries.slice(0, MAX_ALLOW_RULE_SHADOW_ENTRIES).map((e) => redactSecrets(e).slice(0, MAX_RULE_TEXT_CHARS));
|
|
231
|
+
const extra = entries.length - shown.length + (omitted ?? 0);
|
|
232
|
+
out.push({ member: member, entries: shown, ...(extra > 0 ? { omitted: extra } : {}) });
|
|
233
|
+
}
|
|
234
|
+
return out;
|
|
235
|
+
}
|
|
214
236
|
export function readRuleEvidence(req) {
|
|
215
237
|
const parsed = RuleEvidenceEnvelopeSchema.safeParse(req);
|
|
216
238
|
const raw = parsed.success ? parsed.data.ruleEvidence : undefined;
|
|
@@ -356,7 +378,17 @@ export const ApprovalCardSchema = z
|
|
|
356
378
|
governanceForced: z.literal(true).optional(),
|
|
357
379
|
rememberRefused: z.literal(true).optional(),
|
|
358
380
|
inputHasBidi: z.literal(true).optional(),
|
|
359
|
-
|
|
381
|
+
allowRuleShadowed: z
|
|
382
|
+
.array(z
|
|
383
|
+
.object({
|
|
384
|
+
member: z.enum(ALLOW_RULE_MEMBERS),
|
|
385
|
+
entries: z.array(z.string().max(MAX_RULE_TEXT_CHARS)).max(MAX_ALLOW_RULE_SHADOW_ENTRIES),
|
|
386
|
+
omitted: z.number().int().positive().optional(),
|
|
387
|
+
})
|
|
388
|
+
.strict())
|
|
389
|
+
.min(1)
|
|
390
|
+
.max(ALLOW_RULE_MEMBERS.length)
|
|
391
|
+
.optional(),
|
|
360
392
|
ruleOffers: z.array(RuleOfferSchema).max(MAX_RULE_OFFERS).optional(),
|
|
361
393
|
ruleOffersAbsence: RuleOffersAbsenceSchema.optional(),
|
|
362
394
|
denialLimitFallback: DenialLimitFallbackSchema.optional(),
|
|
@@ -416,7 +448,7 @@ export function buildApprovalCard(source, req, requiresRealApproval) {
|
|
|
416
448
|
const toolCallId = clip(source.toolCallId, MAX_IDENT);
|
|
417
449
|
const sourceTaskId = clip(source.sourceTaskId, MAX_IDENT);
|
|
418
450
|
const sourceAgentName = clip(source.sourceAgentName, MAX_AGENT_NAME);
|
|
419
|
-
const
|
|
451
|
+
const allowRuleShadowed = readAllowRuleShadowed(req);
|
|
420
452
|
return {
|
|
421
453
|
toolName: clip(source.toolName, MAX_IDENT) ?? "",
|
|
422
454
|
message: clip(source.message, MAX_MESSAGE) ?? "",
|
|
@@ -430,7 +462,7 @@ export function buildApprovalCard(source, req, requiresRealApproval) {
|
|
|
430
462
|
...(source.governanceForced === true ? { governanceForced: true } : {}),
|
|
431
463
|
...(source.rememberRefused === true ? { rememberRefused: true } : {}),
|
|
432
464
|
...(source.inputHasBidi === true ? { inputHasBidi: true } : {}),
|
|
433
|
-
...(
|
|
465
|
+
...(allowRuleShadowed !== undefined ? { allowRuleShadowed } : {}),
|
|
434
466
|
...(probeCause !== undefined ? { probeCause } : {}),
|
|
435
467
|
...(ruleEvidence !== undefined ? { ruleEvidence } : {}),
|
|
436
468
|
...(source.ruleOffers !== undefined && source.ruleOffers.length > 0 ? { ruleOffers: source.ruleOffers.map(copyRuleOffer) } : {}),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createBrainAssembly } from "../brain.js";
|
|
2
2
|
import { ModelUsageTracker, PromptManifestTracker, buildPricing, createSideQueryAccountant, createTracer } from "../budget.js";
|
|
3
3
|
import { FleetUsageAccumulator } from "../fleet-client.js";
|
|
4
4
|
import { createFleetLeaseFromEnv } from "../fleet-lease.js";
|
|
@@ -8,8 +8,7 @@ import { buildQuotaBounds } from "./limit-sync.js";
|
|
|
8
8
|
import { assembleFileHistory } from "../file-history-capture.js";
|
|
9
9
|
export function createBudgetAndTracing(ctx) {
|
|
10
10
|
const { config, logger, metrics, backend, breakerState } = ctx;
|
|
11
|
-
const brain =
|
|
12
|
-
const degradeRouteWatch = createDegradeRouteWatch(config);
|
|
11
|
+
const { brain, degradeRoute: degradeRouteWatch } = createBrainAssembly(config, { breakerState });
|
|
13
12
|
const pricing = buildPricing(config.models);
|
|
14
13
|
const counterDegradeHook = (info) => {
|
|
15
14
|
metrics.setGauge("counter_flush_fail_streak", info.streak, { table: info.table, kind: info.kind });
|
|
@@ -63,8 +63,8 @@ export interface ConfigCenterRuntime {
|
|
|
63
63
|
* CLI 之后不再有刷新拍,它继续用纯判据判一次)。
|
|
64
64
|
* `consolidationSeat`(S-668 波 0,过渡)= 整理驱动席的两侧读数口,喂 `memory-consolidation` 重启片;
|
|
65
65
|
* 缺席 = 阀门关着(没有 boot 烤死的席)。
|
|
66
|
-
* `degradeRoute`(S-794,**必填**)= 反应式降级壳的两侧读数口(brain.ts `
|
|
67
|
-
*
|
|
66
|
+
* `degradeRoute`(S-794,**必填**)= 反应式降级壳的两侧读数口(brain.ts `createBrainAssembly` 的产物 `degradeRoute`,`baked` 在焊壳处
|
|
67
|
+
* 算好随装配交出),喂 `degrade-route` 重启片。必填而不是可选:车道没开 / 没焊壳由席自己答「两侧同为 null」,缺席没有合法含义 ——
|
|
68
68
|
* 可选就是一条「接线漏了 ⇒ 片永远不签」的静默路([ref])。
|
|
69
69
|
* `checkpointStorePresent`(S-668 波 2,**必填**)= 部署事实「本进程建没建出 checkpoint 店」(stage-06 的产物,
|
|
70
70
|
* 不在 ServiceConfig 里)。审批名单转热之后,热腿要用它判 ④「门意图不可服务」(与 boot 拒启同一只判据);
|
|
@@ -19,7 +19,7 @@ import { centerManagedConfigKeys } from "../config-center/apply-effective.js";
|
|
|
19
19
|
import { createConfigProvider, raceBootFetch, serviceConfigSourceKind, BOOT_FETCH_DEFERRED } from "../config-provider.js";
|
|
20
20
|
import { createKeyResolver } from "../key-resolver.js";
|
|
21
21
|
import { buildSealedKeyPublicFace, ensureSealedKeyStore, reportExecutionPublicKey } from "../sealed-key.js";
|
|
22
|
-
import { applyEffective, applyCenterReadFace, mutateInPlace, logEffectiveDiff, applyCenterSkills, resolveMcpServers, resolveA2aPeers, restartReasons, modelPlaneChanged, withModelPlaneOf,
|
|
22
|
+
import { applyEffective, applyCenterReadFace, mutateInPlace, logEffectiveDiff, applyCenterSkills, resolveMcpServers, resolveA2aPeers, restartReasons, modelPlaneChanged, withModelPlaneOf, fullyLandedForLkg, fetchPromptArtifact } from "../config-center/facade.js";
|
|
23
23
|
import { createMcpRevocationLedger, enabledMcpEntries } from "../config-center/mcp-revocation.js";
|
|
24
24
|
import { SHA256_HEX_RE } from "../digest-form.js";
|
|
25
25
|
import { BOOT_REJUDGE_OF_REFUSED_GROUP, createConfigApplyLedger, modelPlaneCommitted, refusedGroups } from "../config-center/apply-ledger.js";
|
|
@@ -422,7 +422,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
422
422
|
const lkgPlane = modelPlaneCommitted(lkgReport);
|
|
423
423
|
if (lkgPlane)
|
|
424
424
|
markRosterLanded(lkgR.effective);
|
|
425
|
-
effective =
|
|
425
|
+
effective = lkgR.effective;
|
|
426
426
|
if (lkgPlane)
|
|
427
427
|
appliedPlaneEff = lkgR.effective;
|
|
428
428
|
if (fullyLandedForLkg(lkgR, lkgReport)) {
|
|
@@ -504,7 +504,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
504
504
|
}
|
|
505
505
|
if (bootPlaneEff !== undefined)
|
|
506
506
|
markRosterLanded(bootPlaneEff);
|
|
507
|
-
effective =
|
|
507
|
+
effective = r.effective;
|
|
508
508
|
if (bootPlaneEff !== undefined)
|
|
509
509
|
appliedPlaneEff = bootPlaneEff;
|
|
510
510
|
if (bootClean)
|
|
@@ -858,8 +858,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
858
858
|
await adoptCenterPrompts(r.effective, "refresh");
|
|
859
859
|
if (tickPlane)
|
|
860
860
|
markRosterLanded(r.effective);
|
|
861
|
-
|
|
862
|
-
if (!fullyApplied) {
|
|
861
|
+
if (!fullyLandedForLkg(r, applyReport)) {
|
|
863
862
|
ccEtag = undefined;
|
|
864
863
|
const refused = applyReport ? refusedGroups(applyReport) : [];
|
|
865
864
|
logger.warn("config_candidate_partially_applied", {
|
|
@@ -972,8 +971,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
972
971
|
await adoptCenterPrompts(r.effective, "boot-deferred");
|
|
973
972
|
if (latePlane)
|
|
974
973
|
markRosterLanded(r.effective);
|
|
975
|
-
|
|
976
|
-
if (!fullyAppliedLate) {
|
|
974
|
+
if (!fullyLandedForLkg(r, applyReportLate)) {
|
|
977
975
|
ccEtag = undefined;
|
|
978
976
|
const refusedLate = applyReportLate ? refusedGroups(applyReportLate) : [];
|
|
979
977
|
logger.warn("config_candidate_partially_applied", {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { realpathSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { NodeExecutionEnv, QUESTION_AWAITS_RESUME, WRITE_EXEMPT_DIR_COVERS_ROOT_CODE, deriveAskEffective, expandTiers, modePolicyOf, resolveAskSeamForm
|
|
3
|
+
import { NodeExecutionEnv, QUESTION_AWAITS_RESUME, WRITE_EXEMPT_DIR_COVERS_ROOT_CODE, deriveAskEffective, expandTiers, modePolicyOf, resolveAskSeamForm } from "@sema-agent/core";
|
|
4
4
|
import { cappedCeiling } from "../budget.js";
|
|
5
5
|
import { centerPromptProvider, centerIdentityAssembled } from "../capabilities/center-prompts.js";
|
|
6
6
|
import { SessionEnvironmentSelection, selectEnvironmentTool } from "../capabilities/select-environment-tool.js";
|
|
@@ -24,6 +24,7 @@ import { isRemoteScratchpadLane, remoteScratchpadDirFor } from "../plugins/remot
|
|
|
24
24
|
import { bindAttachmentsForTask } from "../plugins/task-attachment-store.js";
|
|
25
25
|
import { resourceSuspendOptIn } from "../resource-suspend.js";
|
|
26
26
|
import { isIsolatedExecEnv, routeServiceTask, supPostureOverrides } from "../router/route-orchestration.js";
|
|
27
|
+
import { tightenGoverned } from "../governance-overrides.js";
|
|
27
28
|
import { gateExecutionLane } from "../runtime-caps-resolver.js";
|
|
28
29
|
import { applyRuntimeGovernance, stripDelegationTools } from "../runtime-governance.js";
|
|
29
30
|
import { HttpError, encodeCheckpointScope } from "../security.js";
|
|
@@ -35,6 +36,7 @@ import { cwdHonored, deviceCwdHonored, effectiveHostWorkspace, inProcessSingleUs
|
|
|
35
36
|
import { assertRequestA2aUnlocked, resolveRequestA2a } from "../task-a2a.js";
|
|
36
37
|
import { assertRequestMcpContentOrigin, assertRequestMcpUnlocked, resolveRequestMcp } from "../task-mcp.js";
|
|
37
38
|
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, acceptAppendSystemPrompt, applyTaskSettings, effectivePermissionMode, effectiveThinking, hasConstitutionAnchors, parseTaskSettings, taskHooksHonored, providerDropsAppend, settingsPermissionBucketIssue, withPermissionMode } from "../task-settings.js";
|
|
39
|
+
import { skillDenyInertEntryOf } from "../rule-entry-list.js";
|
|
38
40
|
import { enableForkFromBody, normalizeRetainSubagentSessions, selfOrchestrationFromBody } from "../task-workflow.js";
|
|
39
41
|
import { redactSecrets } from "../trace/redact.js";
|
|
40
42
|
import { DeferredSandboxPathEnv, isSandboxPathAdjudicationLane, sandboxPathEnvSlots } from "./deferred-sandbox-path-env.js";
|
|
@@ -104,6 +106,11 @@ export function createResolveSpec(ctx) {
|
|
|
104
106
|
if (bucketIssue)
|
|
105
107
|
throw new HttpError(400, bucketIssue.message, { code: "request.field_invalid", extra: bucketIssue.fields });
|
|
106
108
|
}
|
|
109
|
+
{
|
|
110
|
+
const skillDeny = skillDenyInertEntryOf(body);
|
|
111
|
+
if (skillDeny !== undefined)
|
|
112
|
+
throw new HttpError(400, skillDeny.message, { code: "request.field_invalid" });
|
|
113
|
+
}
|
|
107
114
|
assertRequestA2aUnlocked(body.a2aPeers, lockedKeys);
|
|
108
115
|
const gated = await gateScenarioAndAppend(body, auth, opts);
|
|
109
116
|
const effMode = effectivePermissionMode(body, gated.parsedSettings.settings);
|
|
@@ -590,7 +597,7 @@ export function createResolveSpec(ctx) {
|
|
|
590
597
|
...(e.detail !== undefined ? { extra: { writeExemptDirCoversRoot: e.detail } } : {}),
|
|
591
598
|
});
|
|
592
599
|
}
|
|
593
|
-
throw new HttpError(422, `settings
|
|
600
|
+
throw new HttpError(422, `request settings cannot be applied as written: ${e.message}`);
|
|
594
601
|
}
|
|
595
602
|
}
|
|
596
603
|
if (governed.handsReadOnly === true && Array.isArray(governed.tools)) {
|
|
@@ -667,7 +674,7 @@ export function createResolveSpec(ctx) {
|
|
|
667
674
|
});
|
|
668
675
|
logger.info("orchestration_routed", { mode: decision.mode, reason: decision.reason, sessionId: auth?.sessionId ?? null });
|
|
669
676
|
if (decision.mode === "supervisor") {
|
|
670
|
-
return
|
|
677
|
+
return tightenGoverned(governed, supPostureOverrides());
|
|
671
678
|
}
|
|
672
679
|
}
|
|
673
680
|
return governed;
|
|
@@ -236,7 +236,7 @@ export interface RunnerDepsCtx {
|
|
|
236
236
|
}
|
|
237
237
|
/** [ref] A10 留档发现②:main runner `RunnerDeps` 与 main.ts subRunner 字面量之间此前手工重复
|
|
238
238
|
* 的 ~15 个键,类型标注见 {@link createSharedRunnerDeps} 头注。 */
|
|
239
|
-
export type SharedRunnerDeps = Pick<RunnerDeps, "brain" | "readFace" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude" | "memoryDelegationEvidence" | "memoryProvenance" | "memoryCapturePolicy" | "delegationEntryCaps" | "crossSessionInbound" | "crossSessionDialogExpiry" | "writeProtectedPaths" | "models" | "roles" | "tiers" | "pricing" | "tracer" | "promptSource" | "executionEnvFactory" | "lspManager" | "backgroundAgentStore" | "mailboxStore" | "rosterStore" | "hooks" | "toolResultStore" | "checkpointStore" | "hands" | "sessionPolicyStore" | "usageWindows" | "usageWindowStore" | "memoryScopeAdmission" | "deploymentMemoryScopes" | "sharedMemoryStores" | "compliancePostureResolver" | "lockedConfig" | "retentionPolicy" | "onNotice" | "mcpRevocations">;
|
|
239
|
+
export type SharedRunnerDeps = Pick<RunnerDeps, "brain" | "readFace" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude" | "memoryDelegationEvidence" | "memoryProvenance" | "memoryCapturePolicy" | "delegationEntryCaps" | "crossSessionInbound" | "crossSessionDialogExpiry" | "writeProtectedPaths" | "allowedTools" | "models" | "roles" | "tiers" | "pricing" | "tracer" | "promptSource" | "executionEnvFactory" | "lspManager" | "backgroundAgentStore" | "mailboxStore" | "rosterStore" | "hooks" | "toolResultStore" | "checkpointStore" | "hands" | "sessionPolicyStore" | "usageWindows" | "usageWindowStore" | "memoryScopeAdmission" | "deploymentMemoryScopes" | "sharedMemoryStores" | "compliancePostureResolver" | "lockedConfig" | "retentionPolicy" | "onNotice" | "mcpRevocations">;
|
|
240
240
|
/**
|
|
241
241
|
* [ref] A10 留档发现②(review 2026-07-29,[ref]§三族A 同源修补的延续):main runner 的
|
|
242
242
|
* `RunnerDeps` 字面量(下方 `createRunnerDeps`)与 `main.ts` 里 subRunner 的 `new Runner({...})`
|
|
@@ -311,8 +311,10 @@ export declare function createEngineNoticeSeat(logger: {
|
|
|
311
311
|
* 「键为 undefined」与「键缺席」对引擎逐字等价)。**不复制引擎缺省**(`roots`/`static-face`/`carry`/`open`/
|
|
312
312
|
* CC parity 20/200 一个都不抄):上游改缺省之日起本仓不会成为第二份真源。
|
|
313
313
|
*/
|
|
314
|
-
export type DeploymentPostureSeats = Pick<RunnerDeps, "readFace" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude" | "memoryDelegationEvidence" | "memoryProvenance" | "memoryCapturePolicy" | "delegationEntryCaps" | "crossSessionInbound" | "crossSessionDialogExpiry" | "writeProtectedPaths">;
|
|
315
|
-
|
|
314
|
+
export type DeploymentPostureSeats = Pick<RunnerDeps, "readFace" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude" | "memoryDelegationEvidence" | "memoryProvenance" | "memoryCapturePolicy" | "delegationEntryCaps" | "crossSessionInbound" | "crossSessionDialogExpiry" | "writeProtectedPaths" | "allowedTools">;
|
|
315
|
+
/** 本族读的配置切面:席名与 config 键同名的那几席 + 运维 allow 表(席 `allowedTools` ← config `approvalGrant`,env 名 `APPROVAL_GRANT` 不改)。 */
|
|
316
|
+
export type DeploymentPostureConfig = Pick<ServiceConfig, Exclude<keyof DeploymentPostureSeats, "allowedTools"> | "approvalGrant">;
|
|
317
|
+
export declare function buildDeploymentPostureSeats(config: DeploymentPostureConfig): DeploymentPostureSeats;
|
|
316
318
|
export declare function createSharedRunnerDeps(ctx: SharedRunnerDepsCtx): SharedRunnerDeps;
|
|
317
319
|
export declare function createRunnerDeps(ctx: RunnerDepsCtx): RunnerDeps;
|
|
318
320
|
//# sourceMappingURL=runner-deps.d.ts.map
|
package/dist/boot/runner-deps.js
CHANGED
|
@@ -9,7 +9,7 @@ import { createHardenedVmRunner } from "../orchestration/hardened-vm-runner.js";
|
|
|
9
9
|
import { createWorkerHardenedVmRunner } from "../orchestration/hardened-vm-worker-runner.js";
|
|
10
10
|
import { WorkflowAgentRegistry } from "../orchestration/workflow-agent-steer.js";
|
|
11
11
|
import { resolveServedSession } from "../orchestration/workflow-completion-inbox.js";
|
|
12
|
-
import { defaultEngineNoticeRouter } from "../trace/engine-notice-wire.js";
|
|
12
|
+
import { defaultEngineNoticeRouter, presentNoticeDetail } from "../trace/engine-notice-wire.js";
|
|
13
13
|
import { WorkflowNotifyGate } from "../orchestration/workflow-notify-journal.js";
|
|
14
14
|
import { makeLoadProjectMemory, makeProbeInstructionSources } from "../project-memory.js";
|
|
15
15
|
import { createInstructionSourceSeat } from "../instruction-source-notice.js";
|
|
@@ -41,7 +41,8 @@ const PROMPT_CACHE_BREAK_LEVELS = new Map(Object.entries(PROMPT_CACHE_BREAK_CAUS
|
|
|
41
41
|
const CEILING_ORIGINS = new Set(["walltime", "turns"]);
|
|
42
42
|
export function createEngineNoticeForwarder(logger, router = defaultEngineNoticeRouter, metrics) {
|
|
43
43
|
return (notice) => {
|
|
44
|
-
|
|
44
|
+
const detail = presentNoticeDetail(notice.code, notice.detail);
|
|
45
|
+
logger.warn("engine_notice", { code: notice.code, message: notice.message, ...(detail !== undefined ? { detail } : {}) });
|
|
45
46
|
if (metrics !== undefined && notice.code === "task.interrupt_unconsumed") {
|
|
46
47
|
const origin = notice.detail?.origin;
|
|
47
48
|
if (typeof origin === "string" && CEILING_ORIGINS.has(origin))
|
|
@@ -68,6 +69,7 @@ export function buildDeploymentPostureSeats(config) {
|
|
|
68
69
|
crossSessionInbound: Object.keys(crossSessionLayers).length > 0 ? crossSessionLayers : undefined,
|
|
69
70
|
crossSessionDialogExpiry: crossSessionDialogExpiryOf(config),
|
|
70
71
|
writeProtectedPaths: config.writeProtectedPaths?.entries,
|
|
72
|
+
allowedTools: config.approvalGrant.length > 0 ? config.approvalGrant : undefined,
|
|
71
73
|
};
|
|
72
74
|
return seats;
|
|
73
75
|
}
|