@sema-agent/server 7.103.0 → 7.103.2
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 +55 -10
- package/USAGE.md +20 -6
- package/dist/boot/budget-tracing.d.ts +1 -0
- package/dist/boot/budget-tracing.js +3 -2
- package/dist/boot/config-center.d.ts +5 -0
- package/dist/boot/config-center.js +15 -17
- package/dist/boot/stage-04-stores.d.ts +1 -0
- package/dist/boot/stage-04-stores.js +2 -2
- package/dist/boot/stage-05-execution-env.d.ts +1 -0
- package/dist/boot/stage-06-runners.d.ts +1 -0
- package/dist/boot/stage-07-capability-layer.d.ts +1 -0
- package/dist/boot/stage-08-reapers.d.ts +1 -0
- package/dist/boot/stage-08-reapers.js +2 -2
- package/dist/boot/stage-09-leader.d.ts +1 -0
- package/dist/boot/stage-10-http-server.d.ts +1 -0
- package/dist/brain.d.ts +32 -1
- package/dist/brain.js +24 -4
- package/dist/config-center/apply-effective.d.ts +24 -2
- package/dist/config-center/apply-effective.js +9 -1
- 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 +40 -3
- package/dist/config-center/restart-signal.js +11 -12
- package/dist/host-lsp-manager.d.ts +1 -1
- package/dist/model-entry-refusal.d.ts +2 -1
- package/dist/observability/secret-env-scrub.d.ts +1 -1
- package/dist/observability/secret-env-scrub.js +3 -2
- package/dist/plugins/remote-env-adb.d.ts +10 -3
- package/dist/plugins/remote-env-adb.js +24 -20
- package/dist/plugins/remote-env-k8s.d.ts +8 -1
- package/dist/plugins/remote-env-k8s.js +31 -24
- package/dist/plugins/remote-env-ssh.d.ts +8 -1
- package/dist/plugins/remote-env-ssh.js +24 -18
- package/dist/plugins/remote-shell.d.ts +109 -9
- package/dist/plugins/remote-shell.js +83 -0
- package/dist/server-secret-env.d.ts +23 -31
- package/dist/server-secret-env.js +3 -19
- package/package.json +3 -3
- package/skills/update-config.md +23 -5
package/MIGRATION.md
CHANGED
|
@@ -7,6 +7,19 @@
|
|
|
7
7
|
> ⚠️ 完整清单在仓库根 `CHANGELOG.md`——它**不随 npm tarball 出包**(本文件随包)。看完整迁移窗的
|
|
8
8
|
> 权威姿势是源码 tag diff:`git diff v<旧>..v<新>`(每版都推 `v<版本>` tag);npm 包页也镜像 CHANGELOG。
|
|
9
9
|
|
|
10
|
+
## 7.103.2 —— core 7.33.8 提货:`GIT_CONFIG_PARAMETERS` 里的嵌套命令凭据助手不论第几条都整只扣;OTLP 头变量一族不进宿主子进程(S-845)
|
|
11
|
+
|
|
12
|
+
- 宿主子进程(host shell / 三条 git 腿 / 语言服务器 / bake-runner)自本版起:`GIT_CONFIG_PARAMETERS` 里要往里读一层命令的内联凭据助手(`!sh -c '…口令…'`)不论第几条
|
|
13
|
+
⇒ 整只不交(7.103.1 上第 4 条起放行;鉴权头 / URL userinfo / 口令旗等其它凭据形任何位置早就整只扣),同一只里无害的 `-c` 一起没了。
|
|
14
|
+
**迁移**:凭据别写进 `GIT_CONFIG_PARAMETERS`;无害设置按腿分 —— project-memory / center-plugins 两条 git 腿隔离宿主全局配置(`GIT_CONFIG_GLOBAL` / `GIT_CONFIG_SYSTEM`
|
|
15
|
+
= `/dev/null`),只能留在 env 里:从 `GIT_CONFIG_PARAMETERS` 删掉凭据条目只留无害设置,或改用 `GIT_CONFIG_COUNT` 组(一条一判、被扣的一条原位占位、其余照到);
|
|
16
|
+
host shell / leader 推送 / 语言服务器 / bake-runner 四条腿另可写进宿主 `~/.gitconfig` / `GIT_CONFIG_GLOBAL`(`docs/DEPLOY-PREREQS.md` 同段)。
|
|
17
|
+
- `OTEL_EXPORTER_OTLP_{TRACES,METRICS,LOGS,PROFILES}_HEADERS` 与旧拼法 `_SPAN_` / `_METRIC_HEADERS` 不再交给宿主子进程(`OTEL_EXPORTER_OTLP_HEADERS` 此前已由本服务
|
|
18
|
+
按名剥)。**迁移**:语言服务器 / 构建里要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。**观测面**:`OTEL_EXPORTER_OTLP_HEADERS` 被剥时计数改记
|
|
19
|
+
`secret_env_scrubbed_total{kind="exact-name"}`(core 的桶),不再记 `kind="config-catalog"`(本服务目录臂的桶)—— 按 `config-catalog` 桶数它的看板改看 `exact-name`;
|
|
20
|
+
另外六名新出现在 `exact-name` 桶。
|
|
21
|
+
- 零 wire 键、零 DDL。
|
|
22
|
+
|
|
10
23
|
## 7.103.0(车OY 部分)—— server 进程出网认 `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY`(S-776)
|
|
11
24
|
|
|
12
25
|
- **出网跟着代理变量走**:server 进程(HTTP 服务与 `run-local`)的出网 —— 引擎 → 网关、中心拉取 / 公告 / 凭证续签、WebSearch、对象存储、OTLP … ——
|
|
@@ -46,27 +59,59 @@
|
|
|
46
59
|
`mounted: true` ≠ shell 可达。operator 面的 `configFingerprint` **一次性变值**
|
|
47
60
|
(同装配各腿仍相等)—— 持久化 / 钉了指纹的读者会见一次跳变。
|
|
48
61
|
- **`GIT_CONFIG_KEY_<i>` 不再被剥**(core 7.33.1 #1129):宿主子进程(host shell / git 腿 / 语言服务器 / bake-runner)里,普通的 git env
|
|
49
|
-
配置组原样到达;只有某个 KEY 的值带 `<scheme>://<userinfo>@`(令牌 insteadOf)
|
|
62
|
+
配置组原样到达;只有某个 KEY 的值带 `<scheme>://<userinfo>@`(令牌 insteadOf)时这一条扣下(7.103.0 是本仓整组扣;7.103.1 起判定全在 core 7.33.6,见本段末条)。🔴 **安全轴披露(core
|
|
50
63
|
KL-1129a)**:`GIT_CONFIG_VALUE_<i>` **不在** core 的凭据形状判据里 —— 组里 KEY 是 `http.extraHeader`、VALUE 是 `Authorization: Bearer …`
|
|
51
64
|
这一形会**原样**进宿主子进程(含模型能驱动的 host shell),并在 `isolateConfig` 的 git 腿上作为命令级配置**生效**(`GIT_CONFIG_GLOBAL /
|
|
52
65
|
SYSTEM=/dev/null` 挡不住 env 这一层)。**迁移**:不要把凭据放进服务进程 env 的 git 配置组;要给 git 带凭据,走 credential helper / ssh key /
|
|
53
66
|
写在 `durableRemote` 里(见 DEPLOY-PREREQS)。⤷ **7.103.0 已随 core 7.33.2 修掉**(车PA 提货):core 给凭据侧加了**值族表**,
|
|
54
67
|
`GIT_CONFIG_VALUE_<i>` 的值是 `Authorization:` / `Proxy-Authorization:` / `Cookie:` 头(任一行、含折行续写)、带 userinfo 的 URL
|
|
55
68
|
(`<scheme>:/+<user>[:<secret>]@…`),或 proxy 地址键(`http.proxy` / `http.<url>.proxy` / `remote.<name>.proxy`)下的 `user[:secret]@host`
|
|
56
|
-
⇒ 按凭据剥(遥测 `secret_env_scrubbed_total{kind="family-rule"}`)
|
|
57
|
-
|
|
69
|
+
⇒ 按凭据剥(遥测 `secret_env_scrubbed_total{kind="family-rule"}`),被剥的那一条不到子进程(7.103.0 是本仓**整组**扣、该组里别的配置
|
|
70
|
+
一并不到;7.103.1 起其余条目照到,见本段末条)。值族表之外的形(裸令牌写在不叫头 / URL / proxy 的配置值里)仍会原样到达 —— 迁移句不变:凭据别放进服务进程 env 的 git 配置组。
|
|
58
71
|
⤷ **core 7.33.4(7.103.0 提货;经 7.33.3,车PB / 车PC)**:KEY 与 VALUE 共用的那只 URL 判据(7.33.2 起共用)不再解析 authority —— 值里有 `:/`、
|
|
59
72
|
其后任意位置有 `@` 或 `%40` 即剥;proxy 地址键下值里有 `@` / `%40` 即剥(密码含未转义 `/`、括号形、单斜杠 `https:/u:t@h`、`ssh://t%40h/…` 等此前
|
|
60
|
-
漏过的写法一并剥 ⇒
|
|
61
|
-
scp 绝对路径 `host:/repo@v2.git`、`http.extraHeader = Referer: https://…/@scope`、`includeIf.gitdir:/home/a@corp/.path` 同)
|
|
62
|
-
(
|
|
73
|
+
漏过的写法一并剥 ⇒ 这一对扣下)。**谁受伤**:git env 配置组的键或值里 `:/` 之后带 `@` / `%40` 的(含 `@scope` 包路径写法、`%40` 编码形;不限 URL ——
|
|
74
|
+
scp 绝对路径 `host:/repo@v2.git`、`http.extraHeader = Referer: https://…/@scope`、`includeIf.gitdir:/home/a@corp/.path` 同)那一对不到宿主子进程
|
|
75
|
+
(多剥,core KL-1129b;7.103.0 是整组不到,7.103.1 起同组其余条目照到)。**出路**(只对不带凭据的这类配置;凭据照旧走 credential helper / ssh key / `durableRemote`,`~/.gitconfig`
|
|
63
76
|
模型可读):不隔离全局配置的腿(host shell / 语言服务器 / bake-runner / leader 推送)读宿主全局 git 配置,写进 `~/.gitconfig` 即可;center-plugins
|
|
64
77
|
克隆腿每次在新目录克隆、且隔离全局 / 系统配置,这类配置在该腿上**没有替代通道**,只能改写成 `:/` 之后不带 `@` / `%40` 的形,或候 core 收窄
|
|
65
78
|
KL-1129b;project-memory 只跑 `git log` / `git status`,不读这类配置。不受影响:值里没有 `:/` 的 scp 相对路径(`git@host:org/repo.git`)与 `:/`
|
|
66
|
-
之后没有 `@` / `%40`
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
79
|
+
之后没有 `@` / `%40` 的值;7.103.1(core 7.33.6)起 `ssh://` / `git+ssh://` / `git://` 的 userinfo 只是登录名(`ssh://git@github.com/…`,键的子节或整条值)
|
|
80
|
+
也不再扣。
|
|
81
|
+
⤷ **7.103.1(core 7.33.6 精确提货;车PG 退役本层、车PK 提 7.33.6):git env 配置组的判定全在 core,本仓那一层退役**。7.103.0 的「整组扣」让同组的
|
|
82
|
+
`protocol.file.allow=never` 这类安全策略随被剥的那一条静默消失(D-7);7.103.1 起:core 一条配置(KEY + VALUE)一次判定;COUNT 以内被扣的一条**原位**
|
|
83
|
+
换占位(`GIT_CONFIG_KEY_<i>=sema.withheld` / `GIT_CONFIG_VALUE_<i>=` 空串 —— git 读成一条没人用的配置),其余条目与 `GIT_CONFIG_COUNT` 原样(不重排、
|
|
84
|
+
不改写);COUNT 之外或没有 COUNT 的被扣条目整条不交。COUNT 按 git 自己的读法读(` 2` / `+2` / `02` / 空串照认;git 拒的写法原样交出,子进程里的 git 与宿主
|
|
85
|
+
一样拒);POSIX 上只有大写成员名是 git 读的记录,别的拼写(`Git_Config_Key_0`、`git_config_count`)按普通变量另判(Windows 宿主照旧大小写折叠)。core 同时
|
|
86
|
+
多扣:`http.extraHeader` / `http.<url>.extraHeader` 下**非空**的条目不论头名(`X-Api-Key:` / `Private-Token:` …;空的 `Authorization:` 抑制头也扣 —— 整条空值
|
|
87
|
+
`http.extraHeader=` 是 git 的「清掉继承头」,原样交出)、口令 / `token` / `secret` 键(`imap.pass`、`github.token`、任意节下 `pass` / `password` …)、值能被 core
|
|
88
|
+
秘密扫描器认出的条目(厂商前缀令牌、JWT、私钥块、`Bearer <token>`、`token=` / `password=` 赋值)、写死口令的命令行(`core.sshCommand` / `core.gitProxy` 里的
|
|
89
|
+
`sshpass -p <口令>` / `plink -pw <口令>`)、选项名带 token / pass / secret / auth 且给了字面值的凭据助手(`mytool --token=<值>`)、非规范后缀(`_00` / `_x` / 空)
|
|
90
|
+
的散名对(一半被名字规则剥 ⇒ 另一半随之扣)。凭据助手只按 shell 读出的字面判:引用外部口令的写法(`!f() { echo "password=$(pass show …)"; }; f`、
|
|
91
|
+
`!gh auth git-credential`、`store --file …`、Windows 路径)原样交出。**反向变化**(对 7.103.0):① 被扣条目在子进程里留一行 `sema.withheld=`(`git config
|
|
92
|
+
--list` 看得见;对 `status` / `ls-remote` / `fetch` / `includeIf` 无影响 —— core 在 git 2.50 / 2.54 上核过);② 源组本就残缺(某 `i < COUNT` 的成员缺席)且另有
|
|
93
|
+
凭据条目 ⇒ 凭据条目原位占位、残缺照旧 ⇒ git 与源 env 一样报残缺(7.103.0:整组扣);③ 观测:本仓 `secret_env_scrubbed_total{kind="git-config-group"}`
|
|
94
|
+
标签值不再出现,被扣的成员改在 core 的 `family-rule` 桶(读该标签的看板改看 `family-rule`),披露行只点名子进程没按原样拿到的名字;7.103.1 开发中间态的
|
|
95
|
+
`secret_env_git_config_pairs_withheld_total` / warn `git_config_pair_withheld` 从未发布、随层删除。
|
|
96
|
+
**点名放回**(`LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`)按名叠在剥除结果之上:一条被扣的配置把 `GIT_CONFIG_KEY_<i>` 与 `GIT_CONFIG_VALUE_<i>` **两只一并点名** ⇒
|
|
97
|
+
这一条原样交出(COUNT 本就原样,点不点同;v7.103.0 文档写的出路 —— 点名全部 KEY / VALUE —— 照旧成立);只点一只 = 与另一只的占位半条配成一对。
|
|
98
|
+
**迁移**:凭据照旧不进 env 配置组。KL-1129b 多剥的无凭据配置,core 7.33.5 Pickup 段(git 2.54 真验)给的可用写法:`includeIf.gitdir:` 路径里的 `@` 写成
|
|
99
|
+
glob `?`;`safe.directory` 写进宿主自有的全局配置文件(`GIT_CONFIG_GLOBAL`;仓库级配置对它无效);`@scope` 包 URL 写进仓库级配置。(把 `@` 写成 `%40`
|
|
100
|
+
同样触发判据 —— 不可用。)**已知边界**(core 7.33.6):① 子进程看得见占位行 `sema.withheld=`(见上);② 运行时才拼出来的口令(`$(…)` / `$VAR` / `read` 取值、
|
|
101
|
+
由展开给出的程序名)core 读不到、照原样交出;③ `http.extraHeader` 下的**非凭据**头(trace / 路由头)照样按键扣,要它到达写进 git 配置文件。
|
|
102
|
+
**带口令的代理(core 7.33.5 #1150 / 7.33.6 #1165)**:`http_proxy` / `https_proxy` / `all_proxy` 与 `npm_config_https_proxy` / `npm_config_proxy` /
|
|
103
|
+
`ftp_proxy`(大小写两形)值里带 userinfo / `%40` ⇒ 宿主子进程拿不到它(`family-rule`);只有地址的代理与 `NO_PROXY` 照到;写死口令的 `GIT_SSH_COMMAND` /
|
|
104
|
+
`GIT_PROXY_COMMAND`(`sshpass -p <口令>` …)同样不到。出路见 `docs/DEPLOY-PREREQS.md`(服务进程自己的出网不受影响)。
|
|
105
|
+
⤷ **`GIT_CONFIG_PARAMETERS`(S-783)随 core 7.33.5 #1141 收口**:git 的命令级配置 env(`git -c` 传给子进程的那一份)按 git 自己的引号语法读,
|
|
106
|
+
任一条带凭据(或 git 会拒的写法)⇒ 整只不到宿主子进程;干净的列表逐字节原样。7.103.0 此处「不在剥密族表里」的披露降级为「core 扣它」。
|
|
107
|
+
⤷ 7.103.2(core 7.33.8 #1183)起按判、不论第几条(7.103.1 / core 7.33.6 上第 4 条起的嵌套命令凭据助手 `!sh -c '…'` 读不到、整只原样到 —— S-845;其它凭据形任何位置
|
|
108
|
+
都整只扣);任一条带凭据 ⇒ 整只变量不交,同一只里无害的 `-c` 一起没了。凭据别写进它;无害设置按腿分(隔离配置的 project-memory / center-plugins 两腿只能留在 env:
|
|
109
|
+
删掉凭据条目或改用 `GIT_CONFIG_COUNT` 组;其余四腿另可写进宿主 git 配置文件)—— 出路见 `docs/DEPLOY-PREREQS.md` 同段。
|
|
110
|
+
|
|
111
|
+
## 7.103.0(车OW 部分)—— `/health.modelRefusals` 是**免鉴权面**上的新键(S-768;7.103.0 验收 G2 格补披露)
|
|
112
|
+
|
|
113
|
+
- **披露**:`GET /health` 不鉴权(既有),7.103.0 起它多带 `modelRefusals`(与 fleet 心跳同一对象):条目名与拒因码经 settings-schema 的 `isReportableRefusalName` 出口筛过(7.103.1 换钉 6.3.0 后 = 白名单闭形 ∧ 无密钥形 ∧ 无 URL userinfo),但**合规的条目名本身会出现在免鉴权面上**。不想让匿名读到目录条目名的部署:把 `/health` 放在反代 / 网关的鉴权后面,或按 USAGE「健康面」节改用带鉴权的 `/v1/capabilities` 读同一对象。
|
|
114
|
+
- 无操作项;零 wire 键增删之外的变化。
|
|
70
115
|
|
|
71
116
|
## 7.102.0(车OI 部分)—— core 7.32.0 提货带来的四条(后台 shell 退出硬收 / Esc 不收后台 / 读根授权与 artifact store / parked 继承工具新失败形)
|
|
72
117
|
|
package/USAGE.md
CHANGED
|
@@ -1158,8 +1158,14 @@ invalid_using_default` 警告 + 退回缺省值」这条臂随词表放宽一并
|
|
|
1158
1158
|
`CONTEXT_TOKENS`)与两个开关(`DB_FOREIGN_KEYS` `JSON_SORT_KEYS`)不再被剥,三条子进程腿同时生效(7.100.0 上这五个 7.30.1 误剥的名
|
|
1159
1159
|
`MAX_NEW_TOKENS` / `MAX_COMPLETION_TOKENS` / `MAX_BATCH_PREFILL_TOKENS` / `DB_FOREIGN_KEYS` / `JSON_SORT_KEYS` 需要点名放行,现在不用)。
|
|
1160
1160
|
**精确名,不是形**:`USERS_MAX_NEW_TOKENS`、`MAX_NEW_TOKENS_2` 这类装饰过的变体照剥。引擎自己的「剥了什么」通告码
|
|
1161
|
-
`config.secret_env_scrubbed`
|
|
1162
|
-
|
|
1161
|
+
`config.secret_env_scrubbed` 只在引擎**自己**剥的子进程上发;本服务六条宿主腿(host shell / project-memory / center-plugins / leader 三条 git 腿 / 语言服务器 /
|
|
1162
|
+
bake-runner)都交本服务剥好的环境,所以运维面看的是上面这行
|
|
1163
|
+
`secret_env_scrubbed`,不是那只通告码。这只通告码**每进程一次**、引擎里几处剥除共用一闩(工具 shell / 语言服务器 / 7.103.2 起还有 `runSpec` 的
|
|
1164
|
+
oracle 门子进程,core 7.33.8 #1181):oracle 门若先于工具 shell 剥,通告只进 oracle 门那一处(`RunSpecOptions.onNotice`,没接就进 console),接在执行环境上的
|
|
1165
|
+
结构化通告收不到。本服务不调 `runSpec`,不受影响;嵌入引擎、自接 `runSpec` 的宿主给它交同一只 `onNotice`。
|
|
1166
|
+
**OTLP 头变量一族(7.103.2 起,core 7.33.7 #1180)**:`OTEL_EXPORTER_OTLP_HEADERS` 与按信号分的 `_TRACES_` / `_METRICS_` / `_LOGS_` / `_PROFILES_HEADERS`、
|
|
1167
|
+
旧拼法 `_SPAN_` / `_METRIC_HEADERS` 按名剥(值盲);`OTEL_EXPORTER_OTLP_HEADERS` 此前已由本服务的配置目录按名剥,另外六名此前原样交出。语言服务器 /
|
|
1168
|
+
bake-runner 要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。
|
|
1163
1169
|
|
|
1164
1170
|
**「点名放行」只有一种意思**:本服务里凡是「一张名单点到哪些环境变量键」的地方(这里的 `LSP_ENV_ALLOW`、沙箱车道的
|
|
1165
1171
|
`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV`(见下表),与嵌入方构造 host shell 时的 `inheritEnv:[names]`)用的是同一只谓词 ——
|
|
@@ -1939,10 +1945,18 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
|
|
|
1939
1945
|
`secret_env_scrubbed`(warn,site = `server.bake-runner.spawn-env`,带 `allowed` 与出路句;只键名,不带值)。
|
|
1940
1946
|
`PATH` / `HOME` / `NO_PROXY` / `DOCKER_CONFIG` / `BAKE_IMAGE_REGISTRY` / `RECIPE_GITSHA` 这类非密钥键照常传。
|
|
1941
1947
|
docker 推送的鉴权走构建宿主上的 docker 凭据存储(`docker login`),不走 env,不受影响。
|
|
1942
|
-
git 的 env 配置组(`GIT_CONFIG_COUNT` + `GIT_CONFIG_KEY_<i>` / `GIT_CONFIG_VALUE_<i>`)
|
|
1943
|
-
(
|
|
1944
|
-
`
|
|
1945
|
-
`
|
|
1948
|
+
git 的 env 配置组(`GIT_CONFIG_COUNT` + `GIT_CONFIG_KEY_<i>` / `GIT_CONFIG_VALUE_<i>`)的判定全在 core(7.103.1 起,core 7.33.6;7.103.0 是本仓
|
|
1949
|
+
整组扣):带凭据的一条配置(令牌 insteadOf、鉴权头 / 任意非空 `http.extraHeader`、`:/` 之后带 `@` / `%40` —— `ssh://` / `git+ssh://` / `git://` 只带登录名的
|
|
1950
|
+
`ssh://git@…` 除外、口令 / `token` / `secret` 键、秘密扫描器认得出的值、写死口令的命令行(`sshpass -p <口令>` …)、选项带字面令牌的凭据助手)不交;扣在
|
|
1951
|
+
COUNT 以内 ⇒ 那一条**原位**换占位(`GIT_CONFIG_KEY_<i>=sema.withheld` / `GIT_CONFIG_VALUE_<i>=` 空串 —— git 读成一条没人用的配置,子进程里
|
|
1952
|
+
`git config --list` 看得见一行 `sema.withheld=`),其余条目与 `GIT_CONFIG_COUNT` 原样(不重排、不改写)。被扣的成员进同一行 `secret_env_scrubbed`
|
|
1953
|
+
(`kind` = `family-rule`,只点名子进程没按原样拿到的名字)。COUNT 按 git 自己的读法读(` 2` / `+2` / `02` 照认;git 拒的写法原样交出,子进程里的 git 照样拒);
|
|
1954
|
+
POSIX 上只有大写成员名是 git 读的记录,别的拼写按普通变量另判。点名放回(`BAKE_ENV_ALLOW`)按名叠在剥除结果之上:一条被扣的配置把
|
|
1955
|
+
`GIT_CONFIG_KEY_<i>` 与 `GIT_CONFIG_VALUE_<i>` **两只一并点名** ⇒ 这一条原样交出(只点一只 = 与另一只的占位半条配成一对)。要被扣的那类配置稳定到达,
|
|
1956
|
+
改用文件型 git 配置(`~/.gitconfig`,不放凭据)。带口令的 `http_proxy` / `https_proxy` / `all_proxy` 与 `npm_config_https_proxy` / `npm_config_proxy` /
|
|
1957
|
+
`ftp_proxy`(大小写两形)同样不交(core 7.33.5 #1150 / 7.33.6 #1165),写死口令的 `GIT_SSH_COMMAND` / `GIT_PROXY_COMMAND` 也不交;配方真要带口令的代理 ⇒
|
|
1958
|
+
`BAKE_ENV_ALLOW` 点名放回 env 里**实际的拼写**(`HTTPS_PROXY` / `https_proxy`;走 `http://` 目标的工具只读小写 `http_proxy` —— curl(1)),或让代理
|
|
1959
|
+
按来源免密。运行时才拼出来的口令(`$(…)` / `$VAR` 取值)core 读不到、照原样交出 —— 凭据别放进 env。
|
|
1946
1960
|
|
|
1947
1961
|
**`BAKE_ENV_ALLOW=NAME,NAME`。** 配方真要一枚密钥形 env(例如 build.sh 里自己做 registry 登录要 `REGISTRY_PASSWORD`,或
|
|
1948
1962
|
`git fetch` 的 askpass 读 `GIT_TOKEN`)⇒ 点名它,runner 把该键从本进程 env **原样**放回给每一只子进程组(部署方显式承担)。
|
|
@@ -14,6 +14,7 @@ export interface BudgetTracingCtx {
|
|
|
14
14
|
}
|
|
15
15
|
export declare function createBudgetAndTracing(ctx: BudgetTracingCtx): {
|
|
16
16
|
brain: import("@sema-agent/core").Brain;
|
|
17
|
+
degradeRouteWatch: import("../brain.js").DegradeRouteWatch;
|
|
17
18
|
pricing: Record<string, import("@sema-agent/core").ModelPricing>;
|
|
18
19
|
counterDegradeHook: (info: {
|
|
19
20
|
table: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createBrain } from "../brain.js";
|
|
1
|
+
import { createBrain, createDegradeRouteWatch } 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,6 +8,7 @@ import { assembleFileHistory } from "../file-history-capture.js";
|
|
|
8
8
|
export function createBudgetAndTracing(ctx) {
|
|
9
9
|
const { config, logger, metrics, backend, breakerState } = ctx;
|
|
10
10
|
const brain = createBrain(config, { breakerState });
|
|
11
|
+
const degradeRouteWatch = createDegradeRouteWatch(config);
|
|
11
12
|
const pricing = buildPricing(config.models);
|
|
12
13
|
const counterDegradeHook = (info) => {
|
|
13
14
|
metrics.setGauge("counter_flush_fail_streak", info.streak, { table: info.table, kind: info.kind });
|
|
@@ -32,7 +33,7 @@ export function createBudgetAndTracing(ctx) {
|
|
|
32
33
|
const sessionPolicyStore = backend ? backend.sessionPolicy() : undefined;
|
|
33
34
|
const { store: fileHistoryStore, erasure: fileHistoryErasure } = assembleFileHistory(backend, config.fileHistoryCapture);
|
|
34
35
|
return {
|
|
35
|
-
brain, pricing, counterDegradeHook, costQuota, modelUsageTracker, promptManifestTracker,
|
|
36
|
+
brain, degradeRouteWatch, pricing, counterDegradeHook, costQuota, modelUsageTracker, promptManifestTracker,
|
|
36
37
|
fleetUsage, fleetLease, tracer, sideQueryAccounting, toolResultStore, sessionPolicyStore, fileHistoryStore, fileHistoryErasure,
|
|
37
38
|
};
|
|
38
39
|
}
|
|
@@ -11,6 +11,7 @@ import type { Logger } from "../observability/logger.js";
|
|
|
11
11
|
import type { Metrics } from "../observability/metrics.js";
|
|
12
12
|
import type { UnpricedCeilingVerdict } from "./unpriced-ceiling-verdict.js";
|
|
13
13
|
import type { ConsolidationSeatWatch } from "./memory-consolidation.js";
|
|
14
|
+
import type { DegradeRouteWatch } from "../brain.js";
|
|
14
15
|
import { type EngineRestartSignal } from "../config-center/facade.js";
|
|
15
16
|
import { type ConfigApplyLedgerView } from "../config-center/apply-ledger.js";
|
|
16
17
|
import type { LimitSync } from "./limit-sync.js";
|
|
@@ -57,6 +58,9 @@ export interface ConfigCenterRuntime {
|
|
|
57
58
|
* CLI 之后不再有刷新拍,它继续用纯判据判一次)。
|
|
58
59
|
* `consolidationSeat`(S-668 波 0,过渡)= 整理驱动席的两侧读数口,喂 `memory-consolidation` 重启片;
|
|
59
60
|
* 缺席 = 阀门关着(没有 boot 烤死的席)。
|
|
61
|
+
* `degradeRoute`(S-794,**必填**)= 反应式降级壳的两侧读数口(brain.ts `createDegradeRouteWatch`,与 createBrain 同一时刻
|
|
62
|
+
* 捕获),喂 `degrade-route` 重启片。必填而不是可选:车道没开 / 没焊壳由席自己答「两侧同为 null」,缺席没有合法含义 ——
|
|
63
|
+
* 可选就是一条「接线漏了 ⇒ 片永远不签」的静默路([ref])。
|
|
60
64
|
* `checkpointStorePresent`(S-668 波 2,**必填**)= 部署事实「本进程建没建出 checkpoint 店」(stage-06 的产物,
|
|
61
65
|
* 不在 ServiceConfig 里)。审批名单转热之后,热腿要用它判 ④「门意图不可服务」(与 boot 拒启同一只判据);
|
|
62
66
|
* 必填 = 刷新环不可能在不知道这件事的情况下热应用审批名单。 */
|
|
@@ -66,6 +70,7 @@ export interface ConfigCenterRuntime {
|
|
|
66
70
|
swapRunnerDeps: (next: SwappableDeps) => void;
|
|
67
71
|
ceilingVerdict?: UnpricedCeilingVerdict;
|
|
68
72
|
consolidationSeat?: ConsolidationSeatWatch;
|
|
73
|
+
degradeRoute: DegradeRouteWatch;
|
|
69
74
|
checkpointStorePresent: boolean;
|
|
70
75
|
}): void;
|
|
71
76
|
/** [ref]-2:停刷新环(hardShutdown 收尾链;幂等,未起环时 no-op)。S-698:中心凭证续签器的定时器同停(同一条收尾链)。 */
|
|
@@ -21,7 +21,7 @@ import { createConfigProvider, raceBootFetch, BOOT_FETCH_DEFERRED } from "../con
|
|
|
21
21
|
import { executionLaneCaps, executionLaneOf } from "../execution-lane-caps.js";
|
|
22
22
|
import { createKeyResolver } from "../key-resolver.js";
|
|
23
23
|
import { ensureSealedKeyStore, reportExecutionPublicKey } from "../sealed-key.js";
|
|
24
|
-
import { applyEffective, applyCenterReadFace, mutateInPlace, logEffectiveDiff, applyCenterSkills, resolveMcpServers, resolveA2aPeers, restartReasons, modelPlaneChanged, withModelPlaneOf,
|
|
24
|
+
import { applyEffective, applyCenterReadFace, mutateInPlace, logEffectiveDiff, applyCenterSkills, resolveMcpServers, resolveA2aPeers, restartReasons, modelPlaneChanged, withModelPlaneOf, bootSnapshotOf, fullyLandedForLkg, fetchPromptArtifact } from "../config-center/facade.js";
|
|
25
25
|
import { createMcpRevocationLedger, enabledMcpEntries } from "../config-center/mcp-revocation.js";
|
|
26
26
|
import { SHA256_HEX_RE } from "../digest-form.js";
|
|
27
27
|
import { createConfigApplyLedger, modelPlaneCommitted, refusedGroups } from "../config-center/apply-ledger.js";
|
|
@@ -423,12 +423,14 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
423
423
|
const lkgPlane = modelPlaneCommitted(lkgReport);
|
|
424
424
|
if (lkgPlane)
|
|
425
425
|
markRosterLanded(lkgR.effective);
|
|
426
|
-
effective = lkgR.effective;
|
|
426
|
+
effective = bootSnapshotOf(lkgR.effective, lkgPlane ? lkgR.effective : undefined);
|
|
427
427
|
if (lkgPlane)
|
|
428
428
|
appliedPlaneEff = lkgR.effective;
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
429
|
+
if (fullyLandedForLkg(lkgR, lkgReport)) {
|
|
430
|
+
latestEffective = lkgR.effective;
|
|
431
|
+
if (lkgR.etag !== undefined)
|
|
432
|
+
ccEtag = lkgR.etag;
|
|
433
|
+
}
|
|
432
434
|
const cfgModels = (lkgR.effective.models?.models ?? []).filter((m) => m.enabled !== false).length;
|
|
433
435
|
logger.info("config_loaded", { source: "lkg", version: lkgR.effective.version, worker: cc?.worker ?? "(global)", models: cfgModels });
|
|
434
436
|
};
|
|
@@ -463,15 +465,12 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
463
465
|
const r = raced === BOOT_FETCH_DEFERRED ? null : raced;
|
|
464
466
|
if (r) {
|
|
465
467
|
bootApplyPhase = true;
|
|
466
|
-
const bootPromptsRaw = r.effective.prompts;
|
|
467
|
-
const bootPromptsOk = bootPromptsRaw === undefined || validatePromptsDomain(bootPromptsRaw).ok;
|
|
468
|
-
let bootClean = (r.domainErrors ?? []).length === 0 && bootPromptsOk;
|
|
469
468
|
for (const de of r.domainErrors ?? [])
|
|
470
469
|
logger.warn("config_domain_invalid", { domain: de.domain, error: de.error, note: "tolerated at BOOT (corrupt file → schema defaults; dangling ref → reported as-is); the refresh path rejects such a candidate whole and keeps the LKG (§9.5-5)" });
|
|
471
470
|
warnDroppedEntries(r, "boot");
|
|
472
471
|
if (dryRun) {
|
|
473
472
|
logEffectiveDiff(config, r.effective, logger);
|
|
474
|
-
if (
|
|
473
|
+
if (fullyLandedForLkg(r, undefined))
|
|
475
474
|
ccEtag = r.etag;
|
|
476
475
|
}
|
|
477
476
|
else {
|
|
@@ -488,8 +487,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
488
487
|
},
|
|
489
488
|
});
|
|
490
489
|
applyLedger.applied(bootGen, r.effective.version);
|
|
491
|
-
|
|
492
|
-
bootClean = false;
|
|
490
|
+
const bootClean = fullyLandedForLkg(r, bootReport);
|
|
493
491
|
if (bootClean)
|
|
494
492
|
ccEtag = r.etag;
|
|
495
493
|
await adoptCenterPrompts(r.effective, "boot");
|
|
@@ -507,7 +505,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
507
505
|
}
|
|
508
506
|
if (bootPlaneEff !== undefined)
|
|
509
507
|
markRosterLanded(bootPlaneEff);
|
|
510
|
-
effective =
|
|
508
|
+
effective = bootSnapshotOf(r.effective, bootPlaneEff);
|
|
511
509
|
if (bootPlaneEff !== undefined)
|
|
512
510
|
appliedPlaneEff = bootPlaneEff;
|
|
513
511
|
if (bootClean)
|
|
@@ -566,8 +564,8 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
566
564
|
let keyResolver;
|
|
567
565
|
const mcpRevocations = createMcpRevocationLedger((ev) => logger.info("mcp_revocation_ledger", { ...(ev.revoked.length > 0 ? { revoked: ev.revoked } : {}), ...(ev.restored.length > 0 ? { restored: ev.restored } : {}) }));
|
|
568
566
|
let mcpRevocationWired = false;
|
|
569
|
-
const restartCtx = (consolidationSeat) => ({
|
|
570
|
-
|
|
567
|
+
const restartCtx = (consolidationSeat, degradeRoute) => ({
|
|
568
|
+
degradeRoute,
|
|
571
569
|
pluginsAllowHosts: config.pluginsAllowHosts,
|
|
572
570
|
...(consolidationSeat !== undefined ? { consolidationSeat } : {}),
|
|
573
571
|
...(readFaceEnvHeld.length > 0 ? { readFaceEnvHeld } : {}),
|
|
@@ -738,7 +736,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
738
736
|
}
|
|
739
737
|
},
|
|
740
738
|
startRefreshLoop(a) {
|
|
741
|
-
const { pricing, limitSync, swapRunnerDeps, ceilingVerdict, consolidationSeat, checkpointStorePresent } = a;
|
|
739
|
+
const { pricing, limitSync, swapRunnerDeps, ceilingVerdict, consolidationSeat, degradeRoute, checkpointStorePresent } = a;
|
|
742
740
|
if (configProvider) {
|
|
743
741
|
const ccRef = config.configCenter;
|
|
744
742
|
let refreshInFlight = false;
|
|
@@ -847,7 +845,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
847
845
|
return "ok";
|
|
848
846
|
}
|
|
849
847
|
const lkgPersisted = await persistLkgDurable(r.effective, r.etag);
|
|
850
|
-
const reasons = restartReasons(effective, r.effective, restartCtx(consolidationSeat?.read()));
|
|
848
|
+
const reasons = restartReasons(effective, r.effective, restartCtx(consolidationSeat?.read(), degradeRoute.read()));
|
|
851
849
|
const skillsChangeDetected = reasons.includes("skills");
|
|
852
850
|
if (bootSkillStale && !skillsChangeDetected)
|
|
853
851
|
reasons.push("skills");
|
|
@@ -981,7 +979,7 @@ export async function createConfigCenterRuntime(ctx) {
|
|
|
981
979
|
latestEffective = r.effective;
|
|
982
980
|
ccEtag = r.etag;
|
|
983
981
|
const lkgPersistedLate = await persistLkgDurable(r.effective, r.etag);
|
|
984
|
-
const reasons = restartReasons(undefined, r.effective, restartCtx(consolidationSeat?.read()));
|
|
982
|
+
const reasons = restartReasons(undefined, r.effective, restartCtx(consolidationSeat?.read(), degradeRoute.read()));
|
|
985
983
|
if (lkgSurvivesRestart && !lkgPersistedLate) {
|
|
986
984
|
const blocked = reasons.splice(0, reasons.length);
|
|
987
985
|
planeDeferredNoHandoff = { version: r.effective.version, since: Date.now(), blocked, candidate: r.effective, ...(r.etag !== undefined ? { candidateEtag: r.etag } : {}) };
|
|
@@ -2,6 +2,7 @@ import type { runStage03 } from "./stage-03-store-backend.js";
|
|
|
2
2
|
export declare function runStage04(ctx: Awaited<ReturnType<typeof runStage03>>): Promise<{
|
|
3
3
|
brain: import("@sema-agent/core").Brain;
|
|
4
4
|
costQuota: import("../observability/cost-quota.js").CostQuota | import("../plugins/pg-cost-quota.js").PgCostQuota | import("../plugins/tidb-cost-quota.js").TiDBCostQuota;
|
|
5
|
+
degradeRouteWatch: import("../brain.js").DegradeRouteWatch;
|
|
5
6
|
counterDegradeHook: (info: {
|
|
6
7
|
table: string;
|
|
7
8
|
kind: string;
|
|
@@ -27,7 +27,7 @@ export async function runStage04(ctx) {
|
|
|
27
27
|
if (adoptionBoot.scanned > 0 || adoptionBoot.error !== undefined) {
|
|
28
28
|
logger.info("adoption_boot_scan", { ...adoptionBoot });
|
|
29
29
|
}
|
|
30
|
-
const { brain, pricing, counterDegradeHook, costQuota, modelUsageTracker, promptManifestTracker, fleetUsage, fleetLease, tracer, sideQueryAccounting, toolResultStore, sessionPolicyStore, fileHistoryStore, fileHistoryErasure, } = createBudgetAndTracing({ config, logger, metrics, backend, breakerState });
|
|
30
|
+
const { brain, degradeRouteWatch, pricing, counterDegradeHook, costQuota, modelUsageTracker, promptManifestTracker, fleetUsage, fleetLease, tracer, sideQueryAccounting, toolResultStore, sessionPolicyStore, fileHistoryStore, fileHistoryErasure, } = createBudgetAndTracing({ config, logger, metrics, backend, breakerState });
|
|
31
31
|
const permissionRuleStores = config.permissionRulesEnabled && backend ? backend.permissionRule() : undefined;
|
|
32
32
|
const permissionRuleProvider = permissionRuleStores
|
|
33
33
|
? createPermissionRuleStoreProvider({ durable: permissionRuleStores.durable })
|
|
@@ -44,6 +44,6 @@ export async function runStage04(ctx) {
|
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
await auditDormantPermissionRules({ stores: permissionRuleStores, logger, explicit: config.permissionRulesEnabledExplicit });
|
|
47
|
-
return { ...ctx, brain, costQuota, counterDegradeHook, fileHistoryErasure, fileHistoryStore, fleetLease, fleetUsage, memoryBundleFaces, memoryComplianceFaces, memoryOriginFaces, modelUsageTracker, permissionRuleProvider, permissionRuleStores, pricing, promptManifestTracker, ruleConsent, sessionMemoryStatusFace, sessionPolicyStore, sideQueryAccounting, toolResultStore, tracer };
|
|
47
|
+
return { ...ctx, brain, costQuota, degradeRouteWatch, counterDegradeHook, fileHistoryErasure, fileHistoryStore, fleetLease, fleetUsage, memoryBundleFaces, memoryComplianceFaces, memoryOriginFaces, modelUsageTracker, permissionRuleProvider, permissionRuleStores, pricing, promptManifestTracker, ruleConsent, sessionMemoryStatusFace, sessionPolicyStore, sideQueryAccounting, toolResultStore, tracer };
|
|
48
48
|
}
|
|
49
49
|
//# sourceMappingURL=stage-04-stores.js.map
|
|
@@ -15,6 +15,7 @@ export declare function runStage05(ctx: Awaited<ReturnType<typeof runStage04>>):
|
|
|
15
15
|
worktreeReap: (() => Promise<void>) | undefined;
|
|
16
16
|
brain: import("@sema-agent/core").Brain;
|
|
17
17
|
costQuota: import("../observability/cost-quota.js").CostQuota | import("../plugins/pg-cost-quota.js").PgCostQuota | import("../plugins/tidb-cost-quota.js").TiDBCostQuota;
|
|
18
|
+
degradeRouteWatch: import("../brain.js").DegradeRouteWatch;
|
|
18
19
|
counterDegradeHook: (info: {
|
|
19
20
|
table: string;
|
|
20
21
|
kind: string;
|
|
@@ -76,6 +76,7 @@ export declare function runStage06(ctx: Awaited<ReturnType<typeof runStage05>>):
|
|
|
76
76
|
worktreeReap: (() => Promise<void>) | undefined;
|
|
77
77
|
brain: import("@sema-agent/core").Brain;
|
|
78
78
|
costQuota: import("../observability/cost-quota.js").CostQuota | import("../plugins/pg-cost-quota.js").PgCostQuota | import("../plugins/tidb-cost-quota.js").TiDBCostQuota;
|
|
79
|
+
degradeRouteWatch: import("../brain.js").DegradeRouteWatch;
|
|
79
80
|
counterDegradeHook: (info: {
|
|
80
81
|
table: string;
|
|
81
82
|
kind: string;
|
|
@@ -90,6 +90,7 @@ export declare function runStage07(ctx: Awaited<ReturnType<typeof runStage06>>):
|
|
|
90
90
|
worktreeReap: (() => Promise<void>) | undefined;
|
|
91
91
|
brain: import("@sema-agent/core").Brain;
|
|
92
92
|
costQuota: import("../observability/cost-quota.js").CostQuota | import("../plugins/pg-cost-quota.js").PgCostQuota | import("../plugins/tidb-cost-quota.js").TiDBCostQuota;
|
|
93
|
+
degradeRouteWatch: import("../brain.js").DegradeRouteWatch;
|
|
93
94
|
counterDegradeHook: (info: {
|
|
94
95
|
table: string;
|
|
95
96
|
kind: string;
|
|
@@ -103,6 +103,7 @@ export declare function runStage08(ctx: Awaited<ReturnType<typeof runStage07>>):
|
|
|
103
103
|
worktreeReap: (() => Promise<void>) | undefined;
|
|
104
104
|
brain: import("@sema-agent/core").Brain;
|
|
105
105
|
costQuota: import("../observability/cost-quota.js").CostQuota | import("../plugins/pg-cost-quota.js").PgCostQuota | import("../plugins/tidb-cost-quota.js").TiDBCostQuota;
|
|
106
|
+
degradeRouteWatch: import("../brain.js").DegradeRouteWatch;
|
|
106
107
|
counterDegradeHook: (info: {
|
|
107
108
|
table: string;
|
|
108
109
|
kind: string;
|
|
@@ -5,7 +5,7 @@ import { startRetentionLane, RETENTION_LEASE_TTL_FACTOR } from "./retention-lane
|
|
|
5
5
|
import { createLimitSync } from "./limit-sync.js";
|
|
6
6
|
import { runDenySweep } from "./late-binds.js";
|
|
7
7
|
export function runStage08(ctx) {
|
|
8
|
-
const { approvalNonceStore, backend, backgroundAgentStore, ceilingVerdict, checkpointStore, config, configCenter, consolidationSeatWatch, costQuota, fileHistoryErasure, handslessRunner, handslessSubRunner, hookAgentRunner, imageBakes, instanceId, localRoot, logger, mailboxStore, metrics, permissionRuleStores, pricing, rateLimiter, retentionExecutor, retentionLaneStore, rosterStore, runStore, runner, sqlWorkflowRunStore, subRunner, taskAttachmentStore, toolApproval, toolResultStore, workflowJournalStore, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, worktreeReap } = ctx;
|
|
8
|
+
const { approvalNonceStore, backend, backgroundAgentStore, ceilingVerdict, checkpointStore, config, configCenter, consolidationSeatWatch, costQuota, degradeRouteWatch, fileHistoryErasure, handslessRunner, handslessSubRunner, hookAgentRunner, imageBakes, instanceId, localRoot, logger, mailboxStore, metrics, permissionRuleStores, pricing, rateLimiter, retentionExecutor, retentionLaneStore, rosterStore, runStore, runner, sqlWorkflowRunStore, subRunner, taskAttachmentStore, toolApproval, toolResultStore, workflowJournalStore, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, worktreeReap } = ctx;
|
|
9
9
|
const reaper = startReapers({
|
|
10
10
|
config, logger, metrics, localRoot, backend, subRunner, runStore, checkpointStore,
|
|
11
11
|
rateLimiter, costQuota, toolResultStore, fileHistoryErasure, taskAttachmentStore, imageBakes, worktreeReap,
|
|
@@ -71,7 +71,7 @@ export function runStage08(ctx) {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
};
|
|
74
|
-
configCenter.startRefreshLoop({ pricing, limitSync, swapRunnerDeps, ceilingVerdict, checkpointStorePresent: checkpointStore !== undefined, ...(consolidationSeatWatch !== undefined ? { consolidationSeat: consolidationSeatWatch } : {}) });
|
|
74
|
+
configCenter.startRefreshLoop({ pricing, limitSync, swapRunnerDeps, ceilingVerdict, checkpointStorePresent: checkpointStore !== undefined, degradeRoute: degradeRouteWatch, ...(consolidationSeatWatch !== undefined ? { consolidationSeat: consolidationSeatWatch } : {}) });
|
|
75
75
|
configCenter.initKeyResolver();
|
|
76
76
|
return { ...ctx, otelExporter, reaper, retentionLaneStarted };
|
|
77
77
|
}
|
|
@@ -137,6 +137,7 @@ export declare function runStage09(ctx: Awaited<ReturnType<typeof runStage08>>):
|
|
|
137
137
|
worktreeReap: (() => Promise<void>) | undefined;
|
|
138
138
|
brain: import("@sema-agent/core").Brain;
|
|
139
139
|
costQuota: import("../observability/cost-quota.js").CostQuota | import("../plugins/pg-cost-quota.js").PgCostQuota | import("../plugins/tidb-cost-quota.js").TiDBCostQuota;
|
|
140
|
+
degradeRouteWatch: import("../brain.js").DegradeRouteWatch;
|
|
140
141
|
counterDegradeHook: (info: {
|
|
141
142
|
table: string;
|
|
142
143
|
kind: string;
|
|
@@ -137,6 +137,7 @@ export declare function runStage10(ctx: Awaited<ReturnType<typeof runStage09>>):
|
|
|
137
137
|
worktreeReap: (() => Promise<void>) | undefined;
|
|
138
138
|
brain: import("@sema-agent/core").Brain;
|
|
139
139
|
costQuota: import("../observability/cost-quota.js").CostQuota | import("../plugins/pg-cost-quota.js").PgCostQuota | import("../plugins/tidb-cost-quota.js").TiDBCostQuota;
|
|
140
|
+
degradeRouteWatch: import("../brain.js").DegradeRouteWatch;
|
|
140
141
|
counterDegradeHook: (info: {
|
|
141
142
|
table: string;
|
|
142
143
|
kind: string;
|
package/dist/brain.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { type Brain, type BreakerState } from "@sema-agent/core";
|
|
1
|
+
import { type Brain, type BreakerState, type Model } from "@sema-agent/core";
|
|
2
2
|
import type { ServiceConfig } from "./config-types.js";
|
|
3
|
+
import { type RestartSliceCtx } from "./config-center/restart-signal.js";
|
|
3
4
|
/**
|
|
4
5
|
* 配了快切层(failover ≥2 路 / 断路器)时,**每路**的重试上限。
|
|
5
6
|
*
|
|
@@ -41,6 +42,36 @@ export declare function createBrain(config: ServiceConfig, deps?: {
|
|
|
41
42
|
fetchImpl?: typeof fetch;
|
|
42
43
|
breakerState?: BreakerState;
|
|
43
44
|
}): Brain;
|
|
45
|
+
/** {@link degradeRouteFingerprint} 读的那几张表(全是活引用:refresh 就地换代,读数每次现取)。 */
|
|
46
|
+
export type DegradeRoutePlane = Pick<ServiceConfig, "degrade" | "models" | "gatewayBaseUrl" | "modelApiKeyEnv" | "modelApiKeys">;
|
|
47
|
+
/**
|
|
48
|
+
* S-794:反应式降级壳焊的是哪一只目标 —— **唯一判据**(createBrain 组壳、启动摘要、`degrade-route` 片的两侧读数同读这一只)。
|
|
49
|
+
* 车道没开(非 reactive)或表里没有叫 `degrade.to` 的条目 ⇒ `undefined`(createBrain 不装降级壳)。
|
|
50
|
+
*/
|
|
51
|
+
export declare function degradeTargetOf(config: Pick<ServiceConfig, "degrade" | "models">): Model | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* S-794:降级壳的**路由指纹**(纯数据)—— `degrade-route` 重启片 `baked` / `current` 两侧读数的唯一读法。
|
|
54
|
+
*
|
|
55
|
+
* 壳在 createBrain 那一刻焊死目标那只 `Model` 对象(env 派生或目录派生):请求打 `model.baseUrl`(缺席回网关根)、body 带
|
|
56
|
+
* `model.id` / `maxTokens` / `compat` / …,走哪条腿看 `provider`;refresh 就地换的是表里的**值**,够不到那只对象。于是指纹 =
|
|
57
|
+
* · 那只 Model 经 `catalogEntryFingerprint`(与 swap 门同一张热字段减法:单价**数值**热、在场性不热;其余字段一律算 —— deny-list,
|
|
58
|
+
* 漂移的失败形是多签一次,绝不漏签);
|
|
59
|
+
* · 有效端点根(S-623 同一只解析器 `openaiRouteBaseUrl(baseUrl, gatewayBaseUrl)`);
|
|
60
|
+
* · 它配的凭据**身份**:`apiKeyEnv`(变量名,不是值)与托管密钥**在场位**(不取值:值是热的,且是秘密)。两次查表的键与
|
|
61
|
+
* `fbOwnApiKey` 同(目标的真名,再 `degrade.to`)。
|
|
62
|
+
* 没有焊壳(车道没开 / 没有目标)⇒ `null`。在场 ↔ 缺席的翻转因此自然是一次变化。
|
|
63
|
+
*/
|
|
64
|
+
export declare function degradeRouteFingerprint(config: DegradeRoutePlane): unknown;
|
|
65
|
+
/** `degrade-route` 片的席:boot 焊进去的那一只 vs 活配置此刻的同一只目标(`restartReasons` 的 `degradeRoute` 两侧读数)。 */
|
|
66
|
+
export interface DegradeRouteWatch {
|
|
67
|
+
read(): NonNullable<RestartSliceCtx["degradeRoute"]>;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* `create*`:返回带捕获状态(组壳那一刻的指纹)的活对象。**须在 {@link createBrain} 的同一时刻、同一份 config 上调**
|
|
71
|
+
* (生产:boot/budget-tracing.ts 紧随 createBrain)—— 否则 `baked` 就不是壳真焊进去的那一只。与 boot/memory-consolidation.ts
|
|
72
|
+
* `createConsolidationSeatWatch` 同形。
|
|
73
|
+
*/
|
|
74
|
+
export declare function createDegradeRouteWatch(config: DegradeRoutePlane): DegradeRouteWatch;
|
|
44
75
|
/**
|
|
45
76
|
* S-753 复审(opus F2 + codex C1)—— **failover 拓扑 × 首字节段没有部署窗**的启动期披露(纯数据,`build*`;告警由 boot 出)。
|
|
46
77
|
*
|
package/dist/brain.js
CHANGED
|
@@ -4,6 +4,7 @@ import { buildDefaultResilience } from "./config-types.js";
|
|
|
4
4
|
import { resolveModelApiKey } from "./key-resolver.js";
|
|
5
5
|
import { dispatchBrainLane } from "./model-provider.js";
|
|
6
6
|
import { openaiRouteBaseUrl } from "./model-route-endpoint.js";
|
|
7
|
+
import { catalogEntryFingerprint } from "./config-center/restart-signal.js";
|
|
7
8
|
import { buildDisplaySafeRefusal, buildDisplaySafeUrl } from "./trace/redact.js";
|
|
8
9
|
function isOffDeploymentRoot(baseUrl, config) {
|
|
9
10
|
if (!baseUrl)
|
|
@@ -93,7 +94,7 @@ export function createBrain(config, deps = {}) {
|
|
|
93
94
|
: gatewayBrain;
|
|
94
95
|
if (config.degrade?.reactive) {
|
|
95
96
|
const degradeTo = config.degrade.to;
|
|
96
|
-
const fallbackModel = config
|
|
97
|
+
const fallbackModel = degradeTargetOf(config);
|
|
97
98
|
if (fallbackModel) {
|
|
98
99
|
const fbOwnApiKey = () => resolveModelApiKey(fallbackModel.name, config.modelApiKeyEnv ?? {}, process.env, config.modelApiKeys ?? {}) ??
|
|
99
100
|
resolveModelApiKey(degradeTo, config.modelApiKeyEnv ?? {}, process.env, config.modelApiKeys ?? {});
|
|
@@ -135,6 +136,25 @@ export function createBrain(config, deps = {}) {
|
|
|
135
136
|
}
|
|
136
137
|
return brain;
|
|
137
138
|
}
|
|
139
|
+
export function degradeTargetOf(config) {
|
|
140
|
+
return config.degrade?.reactive ? config.models?.[config.degrade.to] : undefined;
|
|
141
|
+
}
|
|
142
|
+
export function degradeRouteFingerprint(config) {
|
|
143
|
+
const target = degradeTargetOf(config);
|
|
144
|
+
if (target === undefined || config.degrade === undefined)
|
|
145
|
+
return null;
|
|
146
|
+
const to = config.degrade.to;
|
|
147
|
+
return {
|
|
148
|
+
model: catalogEntryFingerprint(target),
|
|
149
|
+
endpoint: openaiRouteBaseUrl(target.baseUrl, config.gatewayBaseUrl) ?? null,
|
|
150
|
+
apiKeyEnv: config.modelApiKeyEnv?.[target.name] ?? config.modelApiKeyEnv?.[to] ?? null,
|
|
151
|
+
sealedKey: config.modelApiKeys?.[target.name] !== undefined || config.modelApiKeys?.[to] !== undefined,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
export function createDegradeRouteWatch(config) {
|
|
155
|
+
const baked = degradeRouteFingerprint(config);
|
|
156
|
+
return { read: () => ({ baked, current: degradeRouteFingerprint(config) }) };
|
|
157
|
+
}
|
|
138
158
|
export function buildFailoverFirstByteNotice(config) {
|
|
139
159
|
const r = config.resilience ?? DEFAULT_RESILIENCE;
|
|
140
160
|
const fallbacks = config.gatewayFallbackUrls.length;
|
|
@@ -155,7 +175,7 @@ export function brainSummary(config) {
|
|
|
155
175
|
const r = config.resilience ?? DEFAULT_RESILIENCE;
|
|
156
176
|
const hasFailover = config.gatewayFallbackUrls.length > 0;
|
|
157
177
|
const breakerActive = r.circuitBreaker && hasFailover;
|
|
158
|
-
const degradeTarget = config
|
|
178
|
+
const degradeTarget = degradeTargetOf(config);
|
|
159
179
|
const degradeEndpoint = degradeTarget ? openaiRouteBaseUrl(degradeTarget.baseUrl, config.gatewayBaseUrl) : undefined;
|
|
160
180
|
return {
|
|
161
181
|
gateway: buildDisplaySafeUrl(config.gatewayBaseUrl ?? "") || null,
|
|
@@ -174,10 +194,10 @@ export function brainSummary(config) {
|
|
|
174
194
|
? `fast-fail:${FAST_FAIL_MAX_RETRIES}`
|
|
175
195
|
: "engine-default",
|
|
176
196
|
circuitBreakerNoop: r.circuitBreaker && !hasFailover ? "needs ≥2 gateway routes" : null,
|
|
177
|
-
reactiveDegradeTo:
|
|
197
|
+
reactiveDegradeTo: degradeTarget !== undefined ? (config.degrade?.to ?? null) : null,
|
|
178
198
|
reactiveDegradeBaseUrl: buildDisplaySafeUrl(degradeEndpoint ?? "") || null,
|
|
179
199
|
...((() => {
|
|
180
|
-
const target =
|
|
200
|
+
const target = degradeTarget;
|
|
181
201
|
if (!target)
|
|
182
202
|
return { reactiveDegradeRoutePairing: null, reactiveDegradeRouteRefusal: null };
|
|
183
203
|
if (dispatchBrainLane(target.provider, config.anthropic !== undefined) === "anthropic") {
|
|
@@ -95,8 +95,30 @@ export type ModelPlaneOf = Pick<EffectiveConfig, ModelPlaneDomain>;
|
|
|
95
95
|
* 手写 `{ ...x, models, collab }`(漏一个域正是 V1 的病形)。纯数据:不改两个入参。
|
|
96
96
|
*/
|
|
97
97
|
export declare function withModelPlaneOf<E extends EffectiveConfig>(target: E, source: ModelPlaneOf): E;
|
|
98
|
-
/**
|
|
99
|
-
|
|
98
|
+
/**
|
|
99
|
+
* **boot 快照**(restart 片的对照基线,`boot/config-center.ts` 的 `effective`)= 本次 boot 读到的那份(`read`:拉到的候选,或读回的盘上 LKG)
|
|
100
|
+
* 换上**真落地**的模型面 —— `appliedPlane` 是模型面组真落地的那一代(`read` 自己 / 按组回落的 LKG),`undefined` = 组被拒且没有可用的上一代
|
|
101
|
+
* (进程服务的是 env 表 ⇒「没有中心模型面」)。其余域恒是 `read` 的。
|
|
102
|
+
*
|
|
103
|
+
* boot 的两条落地路径(候选拉取、整份 LKG 读回)都经这一只,禁在调用点各写一份:S-789 的病形正是这句只在候选路径上成立(2d09c8e6),
|
|
104
|
+
* LKG 读回路径把被拒的 LKG 目录记成基线 ⇒ 修好的目录换掉降级目标时零 `degrade-route`,boot 焊死的降级壳永远指着 env 端点。
|
|
105
|
+
* S-794 起 `degrade-route` 片不再读快照的目录(改读降级壳 boot 焊的那只目标 vs 活配置,brain.ts `createDegradeRouteWatch`)——今天没有
|
|
106
|
+
* 重启片读快照的模型面;本函数保证的是快照本身说真话(进程真在服务的那一代),不让下一个读它的片重演 S-789。
|
|
107
|
+
* 纯数据:不改入参;模型面就是 `read` 自己那一代 ⇒ 原样交回(同一引用)。
|
|
108
|
+
*/
|
|
109
|
+
export declare function bootSnapshotOf<E extends EffectiveConfig>(read: E, appliedPlane: ModelPlaneOf | undefined): E;
|
|
110
|
+
/**
|
|
111
|
+
* E-1(7.103.1 修复轮,opus 腿):boot 读到的这一代**整套落地**了吗 —— 只有整套落地的那一代才可以成为 LKG(`latestEffective` /
|
|
112
|
+
* 盘上 LKG 候选)、才让它的 ETag 做条件请求(§9.5-2 + codex R2-F1)。三件同判,缺一不算:读腿没报坏域(`domainErrors`)、
|
|
113
|
+
* prompts 面过候选门同一只判形、apply 报告没有被整拒的组(`fullyApplied`;报告缺席 = 没 apply,dry-run 只判前两件)。
|
|
114
|
+
* boot 的两条落地路径(候选拉取、整份 LKG 读回)都调这一只,禁在调用点各写一份(此前候选路径三件、LKG 路径只抄了一件)。
|
|
115
|
+
* LKG 读回路径上前两件在上游已有门(`loadLkg` 的载荷复判拒坏 prompts;读回不产 `domainErrors`),这里同判是同一条规则的同一只谓词,
|
|
116
|
+
* 不是第二道门。refresh / 迟到 boot 两腿的候选门在 apply **之前**整拒坏域与坏 prompts(拒的是整份候选,不只是 LKG 推进),不走本谓词。
|
|
117
|
+
*/
|
|
118
|
+
export declare function fullyLandedForLkg(read: {
|
|
119
|
+
readonly effective: EffectiveConfig;
|
|
120
|
+
readonly domainErrors?: readonly unknown[];
|
|
121
|
+
}, report: ApplyReport | undefined): boolean;
|
|
100
122
|
/**
|
|
101
123
|
* S-768:组拒码 → 铸它的那条模型面不变量 —— **以码为键**(组拒码闭集的属主是 settings-schema `MODEL_PLANE_REFUSAL_CODES`;那边加一个码
|
|
102
124
|
* ⇒ 本表缺键 ⇒ 本仓 tsc 红)。模型面 stage 的组拒按**码**铸(码是判决那刻的数据),不变量 id 由本表派生 —— 一个 id 两个码
|
|
@@ -12,6 +12,7 @@ import { DEFAULT_MODEL_MISSING_CODE, REFUSED_NAME_MINTED_CODE, judgeCatalogEntri
|
|
|
12
12
|
import { judgeModelRouteEndpoint } from "../model-route-endpoint.js";
|
|
13
13
|
import { gateIntentUnserviceableReason } from "../approval.js";
|
|
14
14
|
import { refusedGroups } from "./apply-ledger.js";
|
|
15
|
+
import { validatePromptsDomain } from "../prompts-domain-validate.js";
|
|
15
16
|
const charsPerTokenOf = (v) => (typeof v === "number" && Number.isFinite(v) && v > 0 ? v : undefined);
|
|
16
17
|
function envDerivedModel(config) {
|
|
17
18
|
const m = config.envModel;
|
|
@@ -204,9 +205,16 @@ function takeModelPlaneDomain(out, source, d) {
|
|
|
204
205
|
else
|
|
205
206
|
out[d] = v;
|
|
206
207
|
}
|
|
207
|
-
|
|
208
|
+
function buildEnvOnlyModelPlane() {
|
|
208
209
|
return { models: { models: [], roles: {} } };
|
|
209
210
|
}
|
|
211
|
+
export function bootSnapshotOf(read, appliedPlane) {
|
|
212
|
+
return appliedPlane === read ? read : withModelPlaneOf(read, appliedPlane ?? buildEnvOnlyModelPlane());
|
|
213
|
+
}
|
|
214
|
+
export function fullyLandedForLkg(read, report) {
|
|
215
|
+
const prompts = read.effective.prompts;
|
|
216
|
+
return (read.domainErrors ?? []).length === 0 && (prompts === undefined || validatePromptsDomain(prompts).ok) && report?.fullyApplied !== false;
|
|
217
|
+
}
|
|
210
218
|
function modelRefusalVerdictOf(group) {
|
|
211
219
|
const entries = group.refused ?? {};
|
|
212
220
|
return group.rejected !== undefined ? { entries, plane: group.rejected.code } : { entries };
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
* importers to update, which was done in the same commit.
|
|
49
49
|
*/
|
|
50
50
|
export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory, ConfigCenterHttpError, fetchSkillContent, fetchPromptArtifact, fetchPromptBlob, } from "./http-client.js";
|
|
51
|
-
export { mutateInPlace, applyEffective, type ApplyReport, applyRuntimeGates, applyRuntimeHot, resolveDefaultModelName, logEffectiveDiff, MODEL_PLANE_DOMAINS, type ModelPlaneDomain, type ModelPlaneOf, withModelPlaneOf,
|
|
51
|
+
export { mutateInPlace, applyEffective, type ApplyReport, applyRuntimeGates, applyRuntimeHot, resolveDefaultModelName, logEffectiveDiff, MODEL_PLANE_DOMAINS, type ModelPlaneDomain, type ModelPlaneOf, withModelPlaneOf, bootSnapshotOf, fullyLandedForLkg, } from "./apply-effective.js";
|
|
52
52
|
export { restartReasons, modelPlaneChanged, type RestartSlice, type RestartSliceCtx, type EngineRestartSignal, } from "./restart-signal.js";
|
|
53
53
|
export { applyCenterSkills, resolveMcpServers, mcpForScenario, resolveA2aPeers, a2aForScenario } from "./skills-mcp.js";
|
|
54
54
|
export { applyCenterReadFace, CENTER_READ_FACE_LIVE_KEYS, type CenterReadFaceApply, type CenterReadFaceReport, type ReadFaceBaseline } from "./read-face.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory, ConfigCenterHttpError, fetchSkillContent, fetchPromptArtifact, fetchPromptBlob, } from "./http-client.js";
|
|
2
|
-
export { mutateInPlace, applyEffective, applyRuntimeGates, applyRuntimeHot, resolveDefaultModelName, logEffectiveDiff, MODEL_PLANE_DOMAINS, withModelPlaneOf,
|
|
2
|
+
export { mutateInPlace, applyEffective, applyRuntimeGates, applyRuntimeHot, resolveDefaultModelName, logEffectiveDiff, MODEL_PLANE_DOMAINS, withModelPlaneOf, bootSnapshotOf, fullyLandedForLkg, } from "./apply-effective.js";
|
|
3
3
|
export { restartReasons, modelPlaneChanged, } from "./restart-signal.js";
|
|
4
4
|
export { applyCenterSkills, resolveMcpServers, mcpForScenario, resolveA2aPeers, a2aForScenario } from "./skills-mcp.js";
|
|
5
5
|
export { applyCenterReadFace, CENTER_READ_FACE_LIVE_KEYS } from "./read-face.js";
|