@sema-agent/server 7.81.0 → 7.82.1
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/USAGE.md +44 -14
- package/dist/boot/memory-consolidation.d.ts +38 -0
- package/dist/boot/memory-consolidation.js +14 -0
- package/dist/boot/runner-deps.d.ts +11 -0
- package/dist/boot/runner-deps.js +15 -0
- package/dist/boot/stage-06-runners.js +13 -8
- package/dist/boot/stage-07-capability-layer.d.ts +1 -0
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-08-reapers.d.ts +1 -0
- package/dist/boot/stage-09-leader.d.ts +1 -0
- package/dist/boot/stage-10-http-server.d.ts +1 -0
- package/dist/boot/stage-10-http-server.js +2 -1
- package/dist/config-catalog.js +3 -2
- package/dist/config-types.d.ts +26 -0
- package/dist/config.js +9 -1
- package/dist/http/routes/capabilities.js +2 -0
- package/dist/http/server.d.ts +8 -0
- package/dist/observability/metrics.js +1 -0
- package/dist/plugins/posix-shell-fs.d.ts +2 -1
- package/dist/plugins/posix-shell-fs.js +4 -1
- package/dist/plugins/remote-env-adb.d.ts +67 -4
- package/dist/plugins/remote-env-adb.js +30 -15
- package/dist/plugins/remote-env-e2b.d.ts +8 -1
- package/dist/plugins/remote-env-e2b.js +4 -1
- package/dist/plugins/remote-env-host.d.ts +15 -1
- package/dist/plugins/remote-env-host.js +37 -1
- package/dist/plugins/remote-env-k8s.d.ts +5 -1
- package/dist/plugins/remote-env-k8s.js +3 -0
- package/dist/plugins/remote-env-local-docker.d.ts +5 -1
- package/dist/plugins/remote-env-local-docker.js +3 -0
- package/dist/plugins/remote-env-ssh.d.ts +5 -1
- package/dist/plugins/remote-env-ssh.js +3 -0
- package/dist/plugins/remote-shell.d.ts +74 -1
- package/dist/plugins/remote-shell.js +35 -0
- package/dist/plugins/web-search.d.ts +50 -2
- package/dist/plugins/web-search.js +9 -2
- package/dist/trace/core-keyset-guard.d.ts +6 -1
- package/dist/trace/project.d.ts +18 -0
- package/dist/trace/project.js +11 -0
- package/package.json +3 -3
package/USAGE.md
CHANGED
|
@@ -93,12 +93,12 @@ WEB_SEARCH_PROBE_ON_BOOT=true # boot 期一次性真出网探活,
|
|
|
93
93
|
|
|
94
94
|
| env 键 | 类型 | 默认 | 缺席行为 | 坏值行为 |
|
|
95
95
|
|---|---|---|---|---|
|
|
96
|
-
| `WEB_SEARCH_PROVIDER` | 闭集(`brave`\|`tavily`\|`searxng`) | 无 | WebSearch 工具整体不装配(功能缺席,无 warn) | 三词之外的任何值 = 同缺席处理,不装配、不 warn(
|
|
97
|
-
| `WEB_SEARCH_API_KEY` | secret string | 无 | brave/tavily:后端仍会装配(装配只看 `PROVIDER`),**首次真实工具调用**时抛 `WEB_SEARCH_API_KEY is required for the <provider> provider`(
|
|
98
|
-
| `WEB_SEARCH_ENDPOINT` | url string | brave/tavily → 各自官方 API;searxng → 无默认 | brave/tavily:落官方 endpoint;searxng:**首次真实工具调用**时抛 `WEB_SEARCH_ENDPOINT (the SearXNG instance URL) is required for the searxng provider`(
|
|
99
|
-
| `WEB_SEARCH_MAX_RESULTS` | number | `10` | 用默认 10 | 非数字/≤0 → 回落默认 10;是数字则向下取整;最终值再夹在 `[1,20]`(设 999 也被 clamp 到 20
|
|
100
|
-
| `WEB_SEARCH_TIMEOUT_MS` | number(ms) | `10000` | 用默认 10000 | 非数字/≤0 → 回落默认;最终值下限夹到 1000ms(`Math.max(1000
|
|
101
|
-
| `WEB_SEARCH_SEARXNG_PARAMS` | string(`k=v;k=v`) | 无(不铸键 = 不传 `extraParams`,交给 core adapter 自己的缺省) | 键整个不铸 | 一个 `k=v` 对都解析不出(没有 `=`,或 `k`/`v` 任一为空)→ 同缺席,整串忽略,不拒启不 warn(
|
|
96
|
+
| `WEB_SEARCH_PROVIDER` | 闭集(`brave`\|`tavily`\|`searxng`) | 无 | WebSearch 工具整体不装配(功能缺席,无 warn) | 三词之外的任何值 = 同缺席处理,不装配、不 warn(`webSearchConfigFromEnv` 的词表守卫 `isWebSearchProvider`)。**读面(S-382 起)**:`GET /v1/capabilities` 的 `webSearch.backend` 把这一格的生效值广告成闭集词(缺席/坏值都报 `"none"` —— 两者行为本就相同);端点/密钥/配额**不上 wire**,那些仍只在 operator 面 `GET /v1/config/catalog` |
|
|
97
|
+
| `WEB_SEARCH_API_KEY` | secret string | 无 | brave/tavily:后端仍会装配(装配只看 `PROVIDER`),**首次真实工具调用**时抛 `WEB_SEARCH_API_KEY is required for the <provider> provider`(`braveSearch` / `tavilySearch` 的首行守卫);searxng:本键无消费点 | 空字符串同缺席(`env.WEB_SEARCH_API_KEY ?` 只认真值,`webSearchConfigFromEnv` 的条件展开) |
|
|
98
|
+
| `WEB_SEARCH_ENDPOINT` | url string | brave/tavily → 各自官方 API;searxng → 无默认 | brave/tavily:落官方 endpoint;searxng:**首次真实工具调用**时抛 `WEB_SEARCH_ENDPOINT (the SearXNG instance URL) is required for the searxng provider`(`searxngSearch` 的首行守卫) | 不做 URL 形校验——写不成 URL 由 `new URL()` 抛出,同样落到"首次调用才现形"那条路径,不拒启 |
|
|
99
|
+
| `WEB_SEARCH_MAX_RESULTS` | number | `10` | 用默认 10 | 非数字/≤0 → 回落默认 10;是数字则向下取整;最终值再夹在 `[1,20]`(设 999 也被 clamp 到 20 —— `createWebSearchBackend` 里的 `maxResults` clamp) |
|
|
100
|
+
| `WEB_SEARCH_TIMEOUT_MS` | number(ms) | `10000` | 用默认 10000 | 非数字/≤0 → 回落默认;最终值下限夹到 1000ms(`createWebSearchBackend` 里的 `Math.max(1000, …)`) |
|
|
101
|
+
| `WEB_SEARCH_SEARXNG_PARAMS` | string(`k=v;k=v`) | 无(不铸键 = 不传 `extraParams`,交给 core adapter 自己的缺省) | 键整个不铸 | 一个 `k=v` 对都解析不出(没有 `=`,或 `k`/`v` 任一为空)→ 同缺席,整串忽略,不拒启不 warn(`parseSearxngParams`);env 侧值恒为字符串,不会触发下面 per-request 那个"数组被误当对象吸收"的边角(见 §9.5) |
|
|
102
102
|
| `WEB_SEARCH_PROBE_ON_BOOT` | boolean(仅认 `"true"`/`"1"`) | `false`(OFF) | 不探活 | trim+小写后不等于 `"true"`/`"1"` 的任何拼法(含 `"yes"`/`"on"`)一律当 `false`;探活失败(网络不通/后端拒绝)只 `warn`(`web_search_probe_failed`),**不拒启**——功能型能力缺席走降级,不是保护型旋钮(`main.ts:1028-1034`) |
|
|
103
103
|
|
|
104
104
|
**结论:七键无一在 boot 期拒启。** 与本仓其它安全轴旋钮(`MODEL_DEGRADE_ON`、思考档三键等,拼错即拒启)
|
|
@@ -1137,6 +1137,33 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
1137
1137
|
> ④**模型座位解析不出来**——既没有显式 chat 席,也没有 `roles.consolidate` / `roles.summarize`(含档位表的
|
|
1138
1138
|
> `flash` 绑定)。core **刻意不回落主模型**(整库蒸馏不该悄悄骑最贵的席位),本服务照抄那条判断,不在下游
|
|
1139
1139
|
> 补一个兜底席。
|
|
1140
|
+
> - `MEMORY_AUTO_CONSOLIDATION=on|off`(**未设 = 读引擎缺省**,见下):记忆**自动整理**的部署席
|
|
1141
|
+
> (core 7.21.0 的 `RunnerDeps.autoRunOnRecommendation` 席)。武装 ⇒ **每个产生了整理建议的任务
|
|
1142
|
+
> 之后**,为该 scope 起**一次**整库整理跑 —— 🔴 **记忆内容自动外流到配置的整理模型**。
|
|
1143
|
+
> **三态,别读成布尔**:
|
|
1144
|
+
> - **未设** = 不写引擎那个键 ⇒ 读引擎的具名缺省,而**本版装的引擎那个缺省是「开」**。
|
|
1145
|
+
> ⇒ 🔴 **不配这根旋钮 = 武装**(在下面那条前置满足时)。缺省的属主在引擎侧、会随提货漂,所以
|
|
1146
|
+
> 「什么都不配」这一态的含义**不是稳定的**;要一个稳定的答案就显式写一个词。
|
|
1147
|
+
> - `off` = **显式关,胜过缺省** —— 这是**关闭口**。不想让记忆自动外流的部署,靠它在 core 把缺省翻开
|
|
1148
|
+
> 之后仍然保持关闭。**这是本旋钮存在的理由。**
|
|
1149
|
+
> - `on` = 武装。**设之前请先读**「这是出口决定不是性能旋钮」那一句:一轮 consolidation 读**全库**
|
|
1150
|
+
> (~1e5 prompt tokens)并把内容交给整理模型,既是钱更是**数据出境**。
|
|
1151
|
+
> ⚠️ **闭集两词,别的一律拒启**(与上一根同一条纪律,方向在这根上更要紧:一个被读成「未设」的拼错关词
|
|
1152
|
+
> 会让那台部署跟着 core 的新缺省**静默武装**,而运维手里握着一份自以为关掉了的配置)。
|
|
1153
|
+
> 🔴 **前置 = 上一根旋钮**:引擎面的整理两席(协议席 + 模型席)在场 **iff** `MEMORY_CONSOLIDATION_DRIVER=on`
|
|
1154
|
+
> 且记忆引擎接了线 —— 两席与那两个 operator 口**共用同一只已解析座**(不做第二份装配)。
|
|
1155
|
+
> · 阀门关着 ⇒ 两席都不写键,引擎那半场**逐字节不变**;此时 `MEMORY_AUTO_CONSOLIDATION=on`
|
|
1156
|
+
> **启动期响亮拒**(core 对「武装但没接整理席」是**每一次 prepare 都抛** `config.auto_consolidation`,
|
|
1157
|
+
> 留到运行期就是一台**启动成功、每个任务都死**的机器)。
|
|
1158
|
+
> · 阀门开着 ⇒ 两席齐 ⇒ 缺省(开)当场生效。
|
|
1159
|
+
> 🔴🔴 **升级影响**:**升级前就配了 `MEMORY_CONSOLIDATION_DRIVER=on` 的部署,升到本版之后每个产生了
|
|
1160
|
+
> 整理建议的任务之后会自动跑一轮整库整理**(升级前那是只能由人按的手动阀门)。不想要就**升级前**显式写
|
|
1161
|
+
> `MEMORY_AUTO_CONSOLIDATION=off` —— **不配不等于关**。启动日志 `memory_consolidation_driver_armed`
|
|
1162
|
+
> 带一位 `autoRunOnRecommendation`(三态原样),用它核这台机器是哪一态。
|
|
1163
|
+
> **两根的分工**:上一根决定**有没有**整理能力,这一根决定那个能力**要不要自动触发**;关掉自动整理,
|
|
1164
|
+
> 手动阀门照常可用。
|
|
1165
|
+
> 未武装的部署,`wiring_manifest` 与它的 `configFingerprint` 对 7.20.1 **逐字节相同**;武装的部署,新段
|
|
1166
|
+
> `autoConsolidation: { onRecommendation: true }` **只出现在 operator 帧上**(契约附录 G.13)。
|
|
1140
1167
|
> - `MEMORY_CONSOLIDATION_SCOPES=user:local[,org:acme]`:阀门的 scope 表 = 这台 worker **声明**它会折叠哪些库。
|
|
1141
1168
|
> **恰好一条**时它是两个口省略 `scope` 的唯一缺省;零条或多条时省略 `scope` 一律 400(在两个库之间替人挑
|
|
1142
1169
|
> 一个去花全库模型钱是本设计要消灭的形)。**表非空时显式 scope 必须在表里**,否则 400 —— 一个陈旧或敲错
|
|
@@ -1515,6 +1542,9 @@ const webSearch = reqWebSearch ? createWebSearchBackend(reqWebSearch) : deps.web
|
|
|
1515
1542
|
TOC 本地形)时被读取;`REQUIRE_PRINCIPAL=true`(多租户)上这个键**结构性够不着**——不是被拒、也不留任何
|
|
1516
1543
|
`warn`/`capabilities` 位说"你发的 webSearch 被忽略了"(对比 `mcpServers` 有 `capabilities.mcpInjection` +
|
|
1517
1544
|
`mcp_injection_dropped` 日志可读;`settings.webSearch` 没有对应的能力探测位,`src/http/wire-types.ts:320-323`)。
|
|
1545
|
+
🔴 **别把 S-382 的新位读成这个探测位**:`GET /v1/capabilities` 的 `webSearch.backend`(S-382)说的是
|
|
1546
|
+
**部署默认后端是哪一只**,**不是**「我发的 per-request `settings.webSearch` 会不会被采纳」——后者在多租户上
|
|
1547
|
+
依旧是结构性够不着、无 warn、无位,这一段的结论一字未变。
|
|
1518
1548
|
原因是安全边界(与多租户能力配置隔离规则同源):per-request `endpoint`/`apiKey` 是能力配置,多租户下一个租户把 `searxng`
|
|
1519
1549
|
指向内网地址就是 SSRF,所以多租户上只认部署 env 配的 backend。
|
|
1520
1550
|
|
|
@@ -1523,11 +1553,11 @@ TOC 本地形)时被读取;`REQUIRE_PRINCIPAL=true`(多租户)上这个键**结
|
|
|
1523
1553
|
|
|
1524
1554
|
| 键 | 类型 | 必填 | 默认 | 消费点 |
|
|
1525
1555
|
|---|---|---|---|---|
|
|
1526
|
-
| `provider` | `"brave"|"tavily"|"searxng"` | 是(缺席/非三词之一 ⇒ 整个 `settings.webSearch` 被丢,回落到 env 的 backend,**不报错**) | 无 |
|
|
1527
|
-
| `apiKey` | string(明文) | brave/tavily 建议带(缺了首次调用才报错,见下表);searxng 不读 | 无(不继承 env 的 `WEB_SEARCH_API_KEY`) |
|
|
1528
|
-
| `endpoint` | string | searxng 必需(缺了首次调用才报错);brave/tavily 可选覆盖 | 无(不继承 env 的 `WEB_SEARCH_ENDPOINT`) |
|
|
1529
|
-
| `searxngParams` | `Record<string,string>`(对象)或 `"k=v;k=v"`(字符串) | 否 | 无 |
|
|
1530
|
-
| `maxResults` | number | 否 | 10(与 env 同一 clamp,`[1,20]`,小数向下取整) |
|
|
1556
|
+
| `provider` | `"brave"|"tavily"|"searxng"` | 是(缺席/非三词之一 ⇒ 整个 `settings.webSearch` 被丢,回落到 env 的 backend,**不报错**) | 无 | `webSearchConfigFromSettings` 的词表守卫(与 env 腿**同一只** `isWebSearchProvider`,词表属主 = `WEB_SEARCH_PROVIDERS`) |
|
|
1557
|
+
| `apiKey` | string(明文) | brave/tavily 建议带(缺了首次调用才报错,见下表);searxng 不读 | 无(不继承 env 的 `WEB_SEARCH_API_KEY`) | `webSearchConfigFromSettings` 的 `apiKey` 条件展开 |
|
|
1558
|
+
| `endpoint` | string | searxng 必需(缺了首次调用才报错);brave/tavily 可选覆盖 | 无(不继承 env 的 `WEB_SEARCH_ENDPOINT`) | `webSearchConfigFromSettings` 的 `endpoint` 条件展开 |
|
|
1559
|
+
| `searxngParams` | `Record<string,string>`(对象)或 `"k=v;k=v"`(字符串) | 否 | 无 | `webSearchConfigFromSettings` 的 `searxngParams` 臂,解析逻辑与 env 腿共用 `parseSearxngParams`。⚠️ **未列入已发布的 `@sema-agent/sdk` 8.8.0 `SettingsWebSearch` 类型**(该接口只有 `provider`/`apiKey`/`endpoint`/`maxResults` 四键、无开放下标)——server 侧代码认这个键,但当前发布的 TS 类型接不到它;手写 JSON 请求体仍可以发,server 会照常解析(源码头注自述:字段和消费点先落地,配置录入面尚未跟上,是一笔尚未还清的既有债务) |
|
|
1560
|
+
| `maxResults` | number | 否 | 10(与 env 同一 clamp,`[1,20]`,小数向下取整) | `webSearchConfigFromSettings` 的 `maxResults` 归一 |
|
|
1531
1561
|
| *(无)* `timeoutMs` | — | — | — | **per-request 车道没有这个字段**——即便部署用 `WEB_SEARCH_TIMEOUT_MS` 配了非默认超时,per-request 生效那次调用永远退回 backend 自己的默认(10000ms,下限 1000ms),因为"整段整取"意味着 env 的 `timeoutMs` 根本不在 `reqWebSearch` 那个新对象里 |
|
|
1532
1562
|
| *(无)* `fetchImpl` | — | — | — | 同上,仅测试注入用,per-request 车道不可达 |
|
|
1533
1563
|
|
|
@@ -1548,7 +1578,7 @@ TOC 本地形)时被读取;`REQUIRE_PRINCIPAL=true`(多租户)上这个键**结
|
|
|
1548
1578
|
| 缺 `apiKey`(brave/tavily,env 或 per-request 均未给) | 首次调用抛 `WEB_SEARCH_API_KEY is required for the <provider> provider`,core 接住转成 `Error (WebSearch): the search backend failed. …` 文本回模型 | `"unknown"`(消息里没有 HTTP 状态码模式,`classifySearchFailure` 落最后一条默认分支) |
|
|
1549
1579
|
| `searxngParams` 非对象/非字符串(如数字、布尔、`null`) | 静默丢弃——`parseSearxngParams` 落到"非 object 且非 string"分支,返回 `undefined`,键整个不铸,**不报错、不 warn** | 不适用 |
|
|
1550
1580
|
| ⚠️ `searxngParams` 是**数组**(如 `["engines=bing"]`) | `typeof [] === "object"` 让它落进对象分支:`Object.entries` 按数组下标产出 `{"0":"engines=bing"}`——**不被拒绝**,但产出的键是数字字符串、值是未拆分的原始 `"k=v"` 串,传给 core adapter 的 `extraParams` 后是一组没有意义的查询参数(不是解析出 `engines=bing`)。这条边角只在 per-request 车道可达(env 值恒为字符串,不会触发);已用与源码逐字一致的独立复现脚本核验(见收车档),未改代码 | 不适用(不是异常路径) |
|
|
1551
|
-
| 搜索后端返回**非 2xx HTTP 响应**(鉴权失败/限流/服务端 5xx 等) | 三个 provider 各自的 `!res.ok` 分支抛错,消息形固定为 `"<provider> search failed (<status>): <body首 200 字符>"`(brave/tavily
|
|
1581
|
+
| 搜索后端返回**非 2xx HTTP 响应**(鉴权失败/限流/服务端 5xx 等) | 三个 provider 各自的 `!res.ok` 分支抛错,消息形固定为 `"<provider> search failed (<status>): <body首 200 字符>"`(brave/tavily,`braveSearch` / `tavilySearch` 的 `!res.ok` 分支)或 `"SearXNG <status> <statusText> from <url>"`(searxng,core adapter) | ⚠️ **实测几乎恒为 `"unknown"`,不是按状态码分档**:`classifySearchFailure` 的状态码分支要求 `http`/`status`/`code`/`error` 四词之一紧邻数字前(`\D{0,12}`内),但本仓三个 adapter 的消息把状态码写在 `failed (…)`/`SearXNG …` 之后,不触发该分支;独立复现脚本核验(见收车档)brave/tavily/searxng 的 429/500/403/403 全部落到最后一条默认分支 `retryable:"unknown"`。**真正被分类对的只有两条兜底正则**:上游错误体文本里若真含 `"rate limit"` 才判 429 类 `true`,含 `"timed out"`/`"timeout"` 才判 408 类 `true`——都取决于上游返回的具体措辞,不是本仓能保证的 |
|
|
1552
1582
|
| `endpoint` **网络层**不可达(连接被拒/DNS 解析失败/fetch 自身抛错,尚未拿到任何 HTTP 响应) | `fetch` 抛出的传输层错误(`ECONNREFUSED`/`ENOTFOUND`/`fetch failed` 等 Node/undici 标准措辞)被同一 `catch` 接住 | `true`——这条路径的错误文本天然含 `econnrefused`/`enotfound`/`fetch failed`/`dns` 等词,`classifySearchFailure` 的网络故障正则能命中(与上一行"已拿到 HTTP 响应但非 2xx"是两条不同的失败路径,别混淆) |
|
|
1553
1583
|
|
|
1554
1584
|
⚠️ **"坏 provider 静默回落"这条对调用方是否可观察,取决于部署 env 有没有配 backend**:上表第一行说
|
|
@@ -1561,8 +1591,8 @@ TOC 本地形)时被读取;`REQUIRE_PRINCIPAL=true`(多租户)上这个键**结
|
|
|
1561
1591
|
见收车档)。
|
|
1562
1592
|
|
|
1563
1593
|
**`apiKey` 明文与 env 槽边界**:server 收到的 `settings.webSearch.apiKey` 是**明文字符串**,没有任何服务端
|
|
1564
|
-
密钥槽位/引用间接——收到什么字符串就直接进 backend 闭包(
|
|
1565
|
-
`src/plugins/web-search.ts
|
|
1594
|
+
密钥槽位/引用间接——收到什么字符串就直接进 backend 闭包(`webSearchConfigFromSettings` 的 `apiKey` 条件展开,与 `WEB_SEARCH_API_KEY` 同一条消费路径,
|
|
1595
|
+
见 `src/plugins/web-search.ts` 的 `WebSearchBackendConfig.apiKey` 字段注)。**server 本批不改受理面**:明文字段的形状维持原样。调用方(壳)如何在
|
|
1566
1596
|
自己机器上管理这份明文是调用方的事——例如 cli 壳侧的约定是在**调用方自己的环境**里按
|
|
1567
1597
|
`SEMA_WEBSEARCH_KEY_<PROVIDER>` 这样的命名空间存放每个 provider 的 key,由壳在本地读出后把明文塞进请求体;
|
|
1568
1598
|
这纯粹是**客户端约定**,本仓不读取、不校验、也不感知任何这类客户端侧 env 命名(这条命名事实来自任务书,
|
|
@@ -138,6 +138,44 @@ export declare function buildStopDetailAudit(input: {
|
|
|
138
138
|
outcome: string;
|
|
139
139
|
untrustedDetail: string;
|
|
140
140
|
};
|
|
141
|
+
/**
|
|
142
|
+
* S-362 / S-399 —— **`RunnerDeps.autoRunOnRecommendation` 武装席的装配不变量**(D1 的姊妹门)。
|
|
143
|
+
*
|
|
144
|
+
* 与 {@link assertConsolidationDriverWirable} 刻意**分家**而不是加第四臂:那道门问「阀门这根旋钮
|
|
145
|
+
* (`MEMORY_CONSOLIDATION_DRIVER`)开着而这台机器结构上跑不了」,本门问「武装这根旋钮
|
|
146
|
+
* (`MEMORY_AUTO_CONSOLIDATION`)开着而引擎那半场没有整理席」。两句诊断指向的动作不同,合成一道门就会
|
|
147
|
+
* 指错方向。
|
|
148
|
+
*
|
|
149
|
+
* ## 为什么是**拒启**而不是让 core 在 prepare 抛
|
|
150
|
+
*
|
|
151
|
+
* core 对「`autoRunOnRecommendation: true` 但没接整理席」的处置是**每一次 prepare 都抛**
|
|
152
|
+
* `config.auto_consolidation`(带码错,不是通知;逐字见 core `RunnerDeps.autoRunOnRecommendation` 的 d.ts)。
|
|
153
|
+
* 留到运行期买到的是一台**启动成功、每一个任务都死**的 worker —— 比阀门那三条(「启动成功、什么都没
|
|
154
|
+
* 折叠」)更坏。所以按 D1 的同一条(**安全/成本控件不得半开**)提前到启动期,并把**该去拧哪根**写进句子。
|
|
155
|
+
*
|
|
156
|
+
* ## 判据取的是**结构事实**,不是一个手抄的布尔
|
|
157
|
+
*
|
|
158
|
+
* `memoryConsolidationWired` 由调用点从**它刚刚铸出来的那只 `RunnerDeps`** 上读(`deps.memoryConsolidation
|
|
159
|
+
* !== undefined`),不是在这里写一个常量 —— S-399 把两席接上之后,本门**自动**从「恒拒」变成「阀门开着
|
|
160
|
+
* 就放行」,没有人需要记得回来改一个数。
|
|
161
|
+
*
|
|
162
|
+
* 🔴 **core 的拒绝谓词是两个析取项,本门只覆盖第一个**(对抗复审 F5 [low],验真后如实登记):
|
|
163
|
+
* core d.ts 逐字「`true` with nothing to run (**no `RunnerDeps.memoryConsolidation`, or no resolvable
|
|
164
|
+
* driver model seat**), or a non-boolean, is refused at prepare」。第二项(模型席解不出)本门不判,
|
|
165
|
+
* 而这在本仓**结构上不可达**:两席是**同一只已解析座**一起写上去的(`boot/runner-deps.ts` 的
|
|
166
|
+
* `ctx.consolidationSeat` 那一段)——座解析不出来时 {@link resolveConsolidationDriverSeat} 早在
|
|
167
|
+
* D1 那道门上就**拒启**了,轮不到这里。哪天有人把两席拆开各走各的路,这条「不可达」就失效,那时
|
|
168
|
+
* 本门要补第二项(core 侧的判据是 `consolidationSeatResolvable`,**不从包根导出**,届时要么向 core
|
|
169
|
+
* 要导出,要么在本层复现 —— 这一句留在这里就是为了那一天不必重新推一遍)。
|
|
170
|
+
*
|
|
171
|
+
* 未武装(`undefined` / `false`)⇒ 整条门不判。
|
|
172
|
+
*/
|
|
173
|
+
export declare function assertAutoConsolidationWirable(input: {
|
|
174
|
+
/** 部署席的三态读数(`config.memoryConsolidationDriver.autoRunOnRecommendation`)。 */
|
|
175
|
+
armed: boolean | undefined;
|
|
176
|
+
/** 铸好的 `RunnerDeps` 上**真的**有整理席吗(`deps.memoryConsolidation !== undefined`)。 */
|
|
177
|
+
memoryConsolidationWired: boolean;
|
|
178
|
+
}): void;
|
|
141
179
|
/** 两个 admin 口消费的窄能力面(`ServiceDeps.memoryConsolidation` 的实参来源)。 */
|
|
142
180
|
export interface MemoryConsolidationFaces {
|
|
143
181
|
/** D3 的座位自证(投影用;boot 期解析一次,restart-to-apply)。 */
|
|
@@ -107,6 +107,20 @@ export function buildStopDetailAudit(input) {
|
|
|
107
107
|
untrustedDetail: redactSecrets(input.stopDetail).slice(0, STOP_DETAIL_LOG_MAX),
|
|
108
108
|
};
|
|
109
109
|
}
|
|
110
|
+
export function assertAutoConsolidationWirable(input) {
|
|
111
|
+
if (input.armed !== true)
|
|
112
|
+
return;
|
|
113
|
+
if (input.memoryConsolidationWired)
|
|
114
|
+
return;
|
|
115
|
+
throw new Error(`MEMORY_AUTO_CONSOLIDATION=on arms automatic memory consolidation (a whole-library fold after every task that ` +
|
|
116
|
+
`recommended one — memory content EGRESSES to the configured consolidation model), but this deployment wires NO ` +
|
|
117
|
+
`engine-side consolidation seat (RunnerDeps.memoryConsolidation). The engine refuses that combination at EVERY ` +
|
|
118
|
+
`prepare (config.auto_consolidation), so this worker would boot fine and then fail every single task. ` +
|
|
119
|
+
`The engine-side seats are wired IFF MEMORY_CONSOLIDATION_DRIVER=on and a memory engine is wired — so either ` +
|
|
120
|
+
`set MEMORY_CONSOLIDATION_DRIVER=on (which also mounts the manual valve, and note that BOTH knobs then bill ` +
|
|
121
|
+
`whole-library model calls), or set MEMORY_AUTO_CONSOLIDATION=off (unsetting it is NOT the same: the engine's ` +
|
|
122
|
+
`named default is ON as of core 7.21.1).`);
|
|
123
|
+
}
|
|
110
124
|
export function createMemoryConsolidationFaces(store, input) {
|
|
111
125
|
const engine = new MemoryEngine({
|
|
112
126
|
backend: store.backend,
|
|
@@ -17,6 +17,7 @@ import type { ElicitationCoordinator } from "../elicitation.js";
|
|
|
17
17
|
import { FleetEventBus } from "../fleet/fleet-bus.js";
|
|
18
18
|
import type { Logger } from "../observability/logger.js";
|
|
19
19
|
import type { Metrics } from "../observability/metrics.js";
|
|
20
|
+
import { type ConsolidationDriverSeat } from "./memory-consolidation.js";
|
|
20
21
|
import { WorkflowAgentRegistry } from "../orchestration/workflow-agent-steer.js";
|
|
21
22
|
import { type WorkflowCompletionInbox } from "../orchestration/workflow-completion-inbox.js";
|
|
22
23
|
import { type EngineNoticeRouter } from "../trace/engine-notice-wire.js";
|
|
@@ -148,6 +149,16 @@ export interface RunnerDepsCtx {
|
|
|
148
149
|
backend: MemoryBackend;
|
|
149
150
|
root: string;
|
|
150
151
|
} | undefined;
|
|
152
|
+
/**
|
|
153
|
+
* S-399(core 7.21.1)—— 记忆整理的**已解析座**(`boot/memory-consolidation.ts` 的
|
|
154
|
+
* `resolveConsolidationDriverSeat` 产物),在场 **iff** `MEMORY_CONSOLIDATION_DRIVER=on` 且记忆引擎接了线。
|
|
155
|
+
*
|
|
156
|
+
* 🔴 **一座两用,不是两次装配**:同一只座同时喂 admin 阀门的操作面引擎与这里的引擎面两席
|
|
157
|
+
* (`RunnerDeps.memoryConsolidation` + `memoryConsolidationDriver`)。第二次解析会让「谁在花钱」
|
|
158
|
+
* 在两条腿上各答一遍,而座是 boot 期定的(restart-to-apply,理由在该模块顶注)。
|
|
159
|
+
* 缺席 ⇒ 两席都**不写键** ⇒ 引擎那半场对 7.20.x 逐字节不变。
|
|
160
|
+
*/
|
|
161
|
+
consolidationSeat: ConsolidationDriverSeat | undefined;
|
|
151
162
|
/** [ref]②([ref] §2.1b,core 7.0.2 [ref] 件2)/ S-215:会话 capture opt-out 记录的**按平面取用源**
|
|
152
163
|
* (`backend.sessionCaptureRecords()`,main.ts 取**一次**、与四只 operator 面引擎同一只源 —— 全站同批
|
|
153
164
|
* 注入律,机器钉 = test/memory-capture-optout-lane.test.ts ②)。在场 ⇒ `capture:"off"` 声明与中途翻转
|
package/dist/boot/runner-deps.js
CHANGED
|
@@ -3,6 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
import { listCollabWorkflows, resolveCollabWorkflow } from "../capabilities/collab-workflows.js";
|
|
4
4
|
import { fleetBackgroundChildPublisher, FleetEventBus } from "../fleet/fleet-bus.js";
|
|
5
5
|
import { createMemoryEngineIncidentSink } from "../memory-incident-sink.js";
|
|
6
|
+
import { assertAutoConsolidationWirable } from "./memory-consolidation.js";
|
|
6
7
|
import { createHardenedVmRunner } from "../orchestration/hardened-vm-runner.js";
|
|
7
8
|
import { createWorkerHardenedVmRunner } from "../orchestration/hardened-vm-worker-runner.js";
|
|
8
9
|
import { WorkflowAgentRegistry } from "../orchestration/workflow-agent-steer.js";
|
|
@@ -274,9 +275,23 @@ export function createRunnerDeps(ctx) {
|
|
|
274
275
|
});
|
|
275
276
|
return;
|
|
276
277
|
}
|
|
278
|
+
metrics.inc("runner_error_total", { phase: ctx.phase });
|
|
277
279
|
logger.error("runner_error", { phase: ctx.phase, sessionId: ctx.sessionId, err: String(err) });
|
|
278
280
|
},
|
|
281
|
+
...(ctx.consolidationSeat !== undefined
|
|
282
|
+
? {
|
|
283
|
+
memoryConsolidation: {},
|
|
284
|
+
memoryConsolidationDriver: { chat: ctx.consolidationSeat.options.chat, model: ctx.consolidationSeat.options.model },
|
|
285
|
+
}
|
|
286
|
+
: {}),
|
|
287
|
+
...(config.memoryConsolidationDriver.autoRunOnRecommendation !== undefined
|
|
288
|
+
? { autoRunOnRecommendation: config.memoryConsolidationDriver.autoRunOnRecommendation }
|
|
289
|
+
: {}),
|
|
279
290
|
};
|
|
291
|
+
assertAutoConsolidationWirable({
|
|
292
|
+
armed: config.memoryConsolidationDriver.autoRunOnRecommendation,
|
|
293
|
+
memoryConsolidationWired: runnerDeps.memoryConsolidation !== undefined,
|
|
294
|
+
});
|
|
280
295
|
return runnerDeps;
|
|
281
296
|
}
|
|
282
297
|
//# sourceMappingURL=runner-deps.js.map
|
|
@@ -62,15 +62,18 @@ export function runStage06(ctx) {
|
|
|
62
62
|
memoryEngineBackend: config.memoryEngineBackend,
|
|
63
63
|
...(config.memoryProvenance !== undefined ? { provenance: config.memoryProvenance } : {}),
|
|
64
64
|
});
|
|
65
|
-
const
|
|
65
|
+
const consolidationSeat = config.memoryConsolidationDriver.enabled && memoryEngine
|
|
66
|
+
? resolveConsolidationDriverSeat({
|
|
67
|
+
brain,
|
|
68
|
+
models: config.models,
|
|
69
|
+
tiers: config.tiers,
|
|
70
|
+
roles: config.roles,
|
|
71
|
+
getApiKeyAndHeaders: createPerModelAuthSeat(() => configCenter.getKeyResolver()),
|
|
72
|
+
})
|
|
73
|
+
: undefined;
|
|
74
|
+
const memoryConsolidationFaces = consolidationSeat && memoryEngine
|
|
66
75
|
? createMemoryConsolidationFaces(memoryEngine, {
|
|
67
|
-
seat:
|
|
68
|
-
brain,
|
|
69
|
-
models: config.models,
|
|
70
|
-
tiers: config.tiers,
|
|
71
|
-
roles: config.roles,
|
|
72
|
-
getApiKeyAndHeaders: createPerModelAuthSeat(() => configCenter.getKeyResolver()),
|
|
73
|
-
}),
|
|
76
|
+
seat: consolidationSeat,
|
|
74
77
|
scopes: config.memoryConsolidationDriver.scopes,
|
|
75
78
|
...(config.memoryProvenance !== undefined ? { provenance: config.memoryProvenance } : {}),
|
|
76
79
|
onIncident: (err) => logger.warn("memory_consolidation_incident", { code: err.code ?? null, detail: err.message }),
|
|
@@ -92,6 +95,7 @@ export function runStage06(ctx) {
|
|
|
92
95
|
model: memoryConsolidationFaces.model,
|
|
93
96
|
scopes: memoryConsolidationFaces.scopes,
|
|
94
97
|
periodic: false,
|
|
98
|
+
autoRunOnRecommendation: config.memoryConsolidationDriver.autoRunOnRecommendation ?? "engine-default(on)",
|
|
95
99
|
});
|
|
96
100
|
}
|
|
97
101
|
const governanceSeams = createGovernanceSeams({
|
|
@@ -108,6 +112,7 @@ export function runStage06(ctx) {
|
|
|
108
112
|
const workflowModelAllowlistSnapshot = workflowModelAllowlistFor(config) ?? [];
|
|
109
113
|
const sessionShellGateRegistry = createSessionShellGateRegistry();
|
|
110
114
|
const runnerDeps = createRunnerDeps({
|
|
115
|
+
consolidationSeat,
|
|
111
116
|
hands: commitHands,
|
|
112
117
|
mcpRevocations,
|
|
113
118
|
sessionShellGateExplicitlyOff: sessionShellGateRegistry.explicitlyOff,
|
|
@@ -24,6 +24,7 @@ export declare function runStage07(ctx: Awaited<ReturnType<typeof runStage06>>):
|
|
|
24
24
|
maxTurns: number | undefined;
|
|
25
25
|
};
|
|
26
26
|
taskTimeoutSec: number;
|
|
27
|
+
webSearchProvider: "brave" | "tavily" | "searxng" | undefined;
|
|
27
28
|
approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
|
|
28
29
|
centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
|
|
29
30
|
checkpointStore: import("../plugins/store-backend.js").CheckpointStoreFull | undefined;
|
|
@@ -158,6 +158,6 @@ export async function runStage07(ctx) {
|
|
|
158
158
|
const builtinScenarioNames = Object.keys(scenarioDetails);
|
|
159
159
|
configCenter.applyCenterScenarios({ scenarioDeps, scenarios, scenarioDetails, builtinScenarioNames });
|
|
160
160
|
logger.info("capabilities", { skills: skills.length, scenarios: Object.keys(scenarios), repoTools: Boolean(repoClient) });
|
|
161
|
-
return { ...ctx, approvalExemptionStore, approvalNonceStore, authorize, imageBakes, imageIndex, parkedReviveInheritedGate, parkedReviveTool, rateLimiter, resumeAnchorStore, runStore, scenarioDetails, scenarios, selectEnvTool, singleUserAutoAcceptBaseline, taskLimitCaps, taskTimeoutSec };
|
|
161
|
+
return { ...ctx, approvalExemptionStore, approvalNonceStore, authorize, imageBakes, imageIndex, parkedReviveInheritedGate, parkedReviveTool, rateLimiter, resumeAnchorStore, runStore, scenarioDetails, scenarios, selectEnvTool, singleUserAutoAcceptBaseline, taskLimitCaps, taskTimeoutSec, webSearchProvider: webSearchCfg?.provider };
|
|
162
162
|
}
|
|
163
163
|
//# sourceMappingURL=stage-07-capability-layer.js.map
|
|
@@ -37,6 +37,7 @@ export declare function runStage08(ctx: Awaited<ReturnType<typeof runStage07>>):
|
|
|
37
37
|
maxTurns: number | undefined;
|
|
38
38
|
};
|
|
39
39
|
taskTimeoutSec: number;
|
|
40
|
+
webSearchProvider: "brave" | "tavily" | "searxng" | undefined;
|
|
40
41
|
approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
|
|
41
42
|
centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
|
|
42
43
|
checkpointStore: import("../plugins/store-backend.js").CheckpointStoreFull | undefined;
|
|
@@ -71,6 +71,7 @@ export declare function runStage09(ctx: Awaited<ReturnType<typeof runStage08>>):
|
|
|
71
71
|
maxTurns: number | undefined;
|
|
72
72
|
};
|
|
73
73
|
taskTimeoutSec: number;
|
|
74
|
+
webSearchProvider: "brave" | "tavily" | "searxng" | undefined;
|
|
74
75
|
approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
|
|
75
76
|
centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
|
|
76
77
|
checkpointStore: import("../plugins/store-backend.js").CheckpointStoreFull | undefined;
|
|
@@ -71,6 +71,7 @@ export declare function runStage10(ctx: Awaited<ReturnType<typeof runStage09>>):
|
|
|
71
71
|
maxTurns: number | undefined;
|
|
72
72
|
};
|
|
73
73
|
taskTimeoutSec: number;
|
|
74
|
+
webSearchProvider: "brave" | "tavily" | "searxng" | undefined;
|
|
74
75
|
approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
|
|
75
76
|
centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
|
|
76
77
|
checkpointStore: import("../plugins/store-backend.js").CheckpointStoreFull | undefined;
|
|
@@ -22,7 +22,7 @@ import { installShutdownHandlers } from "./shutdown.js";
|
|
|
22
22
|
import { bindServerLateSeats } from "./late-binds.js";
|
|
23
23
|
import { serviceVersion } from "../service-version.js";
|
|
24
24
|
export async function runStage10(ctx) {
|
|
25
|
-
const { approvalAskAudit, approvalExemptionStore, approvalNonceStore, authorize, backend, backgroundAgentStore, brain, breakerState, centerRuntimeCapsResolver, checkpointStore, config, configCenter, costQuota, deploymentHooks, deviceLane, drainState, durableEnabled, elicitation, fenceSessionOwner, fileHistoryStore, fleetBus, fleetLease, fleetUsage, governanceSeams, handslessRunner, hookAgent, hookLlm, hookWakeBus, imageBakes, imageIndex, instanceId, instrumentDegenerate, leaderEndpoint, localRoot, logger, lspManager, mcpRevocations, memoryBundleFaces, memoryComplianceFaces, memoryConsolidationFaces, memoryEngine, memoryExportBackend, memoryOptOutGrantStore, memoryOriginFaces, memoryPosture, memorySyncCursors, metrics, modelUsageTracker, orgMemoryAdmission, otelExporter, outcomeSink, ownerAware, parkedReviveInheritedGate, parkedReviveTool, perSessionCwd, perTaskImage, planCacheProbe, pricing, principalCaps, promptManifestTracker, purgeSession, question, rateLimiter, reaper, registryJwtVerifier, resumeAnchorStore, retentionLaneStarted, retentionLaneStore, ruleConsent, runStore, runner, runnerDeps, runtimeCapsPeekResolver, scenarioDetails, scenarios, selectEnvTool, sendFileLedger, sendUserFileEmitter, sendUserFileToolSpec, sessionAudit, sessionEnvSelection, sessionMemoryStatusFace, sessionPolicyStore, sessionShellGateRegistry, sessionTitler, sessionWatchRegistry, setSessionCwd, setSessionShellEnv, sharedMemoryStore, sideQueryAccounting, singleUserAutoAcceptBaseline, sqlEngineFacts, sqlWorkflowRunStore, staticWiring, storeBackendDegraded, subRunner, subagentSteerRegistry, taskAttachmentStore, taskLimitCaps, taskTimeoutSec, toolApproval, toolResultStore, usageWindowStore, workflowAgentRegistry, workflowAgentSessionIndex, workflowCompletionInbox, workflowJournalStore, workflowModelAllowlistSnapshot, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, workflowRunStore, workflowsCapable } = ctx;
|
|
25
|
+
const { approvalAskAudit, approvalExemptionStore, approvalNonceStore, authorize, backend, backgroundAgentStore, brain, breakerState, centerRuntimeCapsResolver, checkpointStore, config, configCenter, costQuota, deploymentHooks, deviceLane, drainState, durableEnabled, elicitation, fenceSessionOwner, fileHistoryStore, fleetBus, fleetLease, fleetUsage, governanceSeams, handslessRunner, hookAgent, hookLlm, hookWakeBus, imageBakes, imageIndex, instanceId, instrumentDegenerate, leaderEndpoint, localRoot, logger, lspManager, mcpRevocations, memoryBundleFaces, memoryComplianceFaces, memoryConsolidationFaces, memoryEngine, memoryExportBackend, memoryOptOutGrantStore, memoryOriginFaces, memoryPosture, memorySyncCursors, metrics, modelUsageTracker, orgMemoryAdmission, otelExporter, outcomeSink, ownerAware, parkedReviveInheritedGate, parkedReviveTool, perSessionCwd, perTaskImage, planCacheProbe, pricing, principalCaps, promptManifestTracker, purgeSession, question, rateLimiter, reaper, registryJwtVerifier, resumeAnchorStore, retentionLaneStarted, retentionLaneStore, ruleConsent, runStore, runner, runnerDeps, runtimeCapsPeekResolver, scenarioDetails, scenarios, selectEnvTool, sendFileLedger, sendUserFileEmitter, sendUserFileToolSpec, sessionAudit, sessionEnvSelection, sessionMemoryStatusFace, sessionPolicyStore, sessionShellGateRegistry, sessionTitler, sessionWatchRegistry, setSessionCwd, setSessionShellEnv, sharedMemoryStore, sideQueryAccounting, singleUserAutoAcceptBaseline, sqlEngineFacts, sqlWorkflowRunStore, staticWiring, storeBackendDegraded, subRunner, subagentSteerRegistry, taskAttachmentStore, taskLimitCaps, taskTimeoutSec, toolApproval, toolResultStore, usageWindowStore, webSearchProvider, workflowAgentRegistry, workflowAgentSessionIndex, workflowCompletionInbox, workflowJournalStore, workflowModelAllowlistSnapshot, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, workflowRunStore, workflowsCapable } = ctx;
|
|
26
26
|
const stores = {
|
|
27
27
|
runStore,
|
|
28
28
|
resumeAnchorStore: resumeAnchorStore ? resumeAnchorStore : undefined,
|
|
@@ -133,6 +133,7 @@ export async function runStage10(ctx) {
|
|
|
133
133
|
memoryPosture,
|
|
134
134
|
sqlEngineFacts,
|
|
135
135
|
writeProtection: buildWriteProtectionPosture(config),
|
|
136
|
+
webSearchProvider,
|
|
136
137
|
instanceId,
|
|
137
138
|
capabilities: {
|
|
138
139
|
version: serviceVersion(),
|
package/dist/config-catalog.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES } from "@sema-agent/core";
|
|
2
2
|
import { THINKING_FORMATS } from "./model-compat.js";
|
|
3
3
|
import { RESILIENCE_DEFAULTS } from "./config-types.js";
|
|
4
|
-
import { webSearchConfigFromEnv } from "./plugins/web-search.js";
|
|
4
|
+
import { webSearchConfigFromEnv, WEB_SEARCH_PROVIDERS } from "./plugins/web-search.js";
|
|
5
5
|
import { CENTER_READ_FACE_LIVE_KEYS } from "./config-center/read-face.js";
|
|
6
6
|
export const CATALOG_VALUE_TYPES = ["boolean", "number", "string", "enum", "csv", "json", "path", "url", "secret", "opaque"];
|
|
7
7
|
export const CATALOG_TIMINGS = ["hot", "restart", "deploy", "inert"];
|
|
@@ -213,6 +213,7 @@ export const CONFIG_CATALOG = [
|
|
|
213
213
|
r("MCP_ELICITATION_MAX_TOTAL", "approval", "number", "elicitation 每 run 总量帽", { derivedDefaultNote: "缺席 ⇒ core 出厂窗" }),
|
|
214
214
|
r("MCP_ELICITATION_MIN_INTERVAL_MS", "approval", "number", "elicitation per-server 最小间隔(ms)", { derivedDefaultNote: "缺席 ⇒ core 出厂窗" }),
|
|
215
215
|
r("MCP_ELICITATION_TTL_MS", "approval", "number", "elicitation 无人应答释放窗(ms)", { derivedDefaultNote: "缺席 ⇒ core 出厂窗" }),
|
|
216
|
+
r("MEMORY_AUTO_CONSOLIDATION", "memory", "enum", "记忆自动整理部署席(on=每个建议任务后整库整理跑,记忆内容自动外流到整理模型;off=显式关,胜过引擎缺省;未设=读引擎具名缺省)", { enumValues: ["on", "off"], derivedDefaultNote: "缺席 ⇒ 引擎具名缺省(**本版装的引擎是开**)——不配不等于关;前置=MEMORY_CONSOLIDATION_DRIVER=on", danger: SEC, resolve: (c) => c.memoryConsolidationDriver.autoRunOnRecommendation, booleanEnum: { true: "on", false: "off" } }),
|
|
216
217
|
r("MEMORY_CAPTURE_POLICY", "memory", "enum", "记忆 capture 姿态(open=声明按面值;governed=按 per-principal verdict;capture-required=恒采)", { enumValues: ["open", "governed", "capture-required"], staticDefault: "open", danger: SEC, configKey: "memoryCapturePolicy" }),
|
|
217
218
|
r("MEMORY_CONSOLIDATION_DRIVER", "memory", "enum", "记忆折叠阀门(off=零装配零探针,两 admin 口 404 族)", { enumValues: ["on", "off"], staticDefault: "off", danger: BILL, resolve: (c) => c.memoryConsolidationDriver.enabled, booleanEnum: { true: "on", false: "off" } }),
|
|
218
219
|
r("MEMORY_CONSOLIDATION_INTERVAL_SEC", "memory", "number", "折叠周期腿节律(秒;本 build 任何正值拒启 —— 周期腿未落地,恒 0)", { staticDefault: "0" }),
|
|
@@ -446,7 +447,7 @@ export const CONFIG_CATALOG = [
|
|
|
446
447
|
r("WEB_SEARCH_ENDPOINT", "orchestration", "url", "web search 后端 endpoint 覆写(searxng 自建面)"),
|
|
447
448
|
r("WEB_SEARCH_MAX_RESULTS", "orchestration", "number", "web search 单次结果数上限", { derivedDefaultNote: "缺席/非正 ⇒ backend 默认" }),
|
|
448
449
|
r("WEB_SEARCH_PROBE_ON_BOOT", "orchestration", "boolean", "boot 期 web search 连通性探测(只认 true/1;含糊值当没开——猜错方向是凭空出网)", { staticDefault: "false" }),
|
|
449
|
-
r("WEB_SEARCH_PROVIDER", "orchestration", "enum", "web search 工具后端(设合法词=装配 WebSearch;其它/缺席=不装配)", { enumValues:
|
|
450
|
+
r("WEB_SEARCH_PROVIDER", "orchestration", "enum", "web search 工具后端(设合法词=装配 WebSearch;其它/缺席=不装配)", { enumValues: WEB_SEARCH_PROVIDERS, danger: SEC, resolve: (_c, env) => webSearchConfigFromEnv(env)?.provider ?? null }),
|
|
450
451
|
r("WEB_SEARCH_SEARXNG_PARAMS", "orchestration", "string", "searxng 额外查询参数(k=v 对;解析不出任何一对 ⇒ 键不铸)"),
|
|
451
452
|
r("WEB_SEARCH_TIMEOUT_MS", "orchestration", "number", "web search 单次超时(ms)", { derivedDefaultNote: "缺席/非正 ⇒ backend 默认" }),
|
|
452
453
|
r("WORKFLOW_AGENTS_READONLY", "orchestration", "boolean", "workflow 子 agent 只读钳(TOB 保守旋钮;默认同权 CC parity)", { staticDefault: "false", danger: SEC, configKey: "workflowAgentsReadOnly" }),
|
package/dist/config-types.d.ts
CHANGED
|
@@ -140,6 +140,32 @@ export interface MemoryConsolidationDriverConfig {
|
|
|
140
140
|
* `src/memory-operator-faces.ts` 的 `erasureEnvelopeRefusal` 头注),在本层拿它当白名单就是**发明
|
|
141
141
|
* 第二套授权语义**。显式传的 scope 照跑,表只管缺省。 */
|
|
142
142
|
scopes: readonly string[];
|
|
143
|
+
/**
|
|
144
|
+
* S-362 / core 7.21.0 [ref] —— **`RunnerDeps.autoRunOnRecommendation` 的部署席**
|
|
145
|
+
* (env `MEMORY_AUTO_CONSOLIDATION=on|off`;**缺席 = 不写那个键**,由 core 的具名缺省
|
|
146
|
+
* `AUTO_RUN_ON_RECOMMENDATION_DEFAULT` 作答)。
|
|
147
|
+
*
|
|
148
|
+
* 🔴 **三态,不是布尔**,而且三态各有各的意思 —— 折成布尔会当场丢掉本旋钮存在的**唯一**理由:
|
|
149
|
+
* · `undefined`(env 未设)= 不写 `RunnerDeps` 那个键 ⇒ 读 core 的具名缺省。core 7.21.0 发的是
|
|
150
|
+
* `false`,而 clay [ref] 已裁**缺省开**、core 7.21.1 翻常量 —— 所以这一格的取值**会随提货漂**,
|
|
151
|
+
* 这正是它该有的样子(缺省的属主在 core)。
|
|
152
|
+
* · `false`(`MEMORY_AUTO_CONSOLIDATION=off`)= **显式关,胜过缺省**。这是本旋钮的**关闭口**:
|
|
153
|
+
* 一台不愿意让记忆内容自动外流的部署,靠它在 core 把缺省翻开之后仍然保持关闭。
|
|
154
|
+
* · `true`(`=on`)= 武装。**武装是一条出口决定,不是性能旋钮**:每个建议任务之后会有一次整库整理跑,
|
|
155
|
+
* 记忆内容自动外流到配置的整理模型 —— 设它之前请读 `boot/memory-consolidation.ts` 的 EGRESS 段。
|
|
156
|
+
*
|
|
157
|
+
* ⚠️ **前置 = `MEMORY_CONSOLIDATION_DRIVER=on`**(且记忆引擎接了线)。阀门开着 ⇒ 引擎那半场的整理两席
|
|
158
|
+
* (`RunnerDeps.memoryConsolidation` + `memoryConsolidationDriver`)由**同一只已解析座**接上
|
|
159
|
+
* (S-399,`boot/runner-deps.ts` 的 `ctx.consolidationSeat` 那一段)⇒ 本旋钮真的生效;阀门关着 ⇒ 两席
|
|
160
|
+
* 一个键都不写,而 core 对「武装但没接整理席」的处置是**每一次 prepare 都抛** `config.auto_consolidation`
|
|
161
|
+
* —— 那会买到一台**启动成功、每个任务都死**的机器,所以本仓把那一拒提前到启动期响亮拒
|
|
162
|
+
* (`assertAutoConsolidationWirable`,读的是刚铸出来的那只 deps 而不是一个手抄的布尔),与
|
|
163
|
+
* `MEMORY_CONSOLIDATION_DRIVER` 的 D1「安全/成本控件不得半开」同一条。
|
|
164
|
+
* 🔴 **本段自 S-399 改判**:S-362 那一批这里写的是「本 build 上 `on` 一定拒启(本仓没接整理席)」——
|
|
165
|
+
* 同一批的后半场把两席接上之后,那句话成了谎。旧世界断言留在树上比欠账更危险(7.81.0 整批的教训),
|
|
166
|
+
* 所以真相变了就地改,不留「将来某一版」的口气。
|
|
167
|
+
*/
|
|
168
|
+
autoRunOnRecommendation?: boolean;
|
|
143
169
|
}
|
|
144
170
|
/** A sema-registry MCP server resolved to a core spec (env-NAME refs already → real values) plus the
|
|
145
171
|
* scenarios it applies to (empty = all). `resolveSpec` filters by scenario and passes `spec` to core. */
|
package/dist/config.js
CHANGED
|
@@ -1349,6 +1349,14 @@ function parseMemoryConsolidationDriver() {
|
|
|
1349
1349
|
`spellings to preserve, unlike MEMORY_ENGINE's two-family word table.`);
|
|
1350
1350
|
}
|
|
1351
1351
|
const enabled = word === "on";
|
|
1352
|
+
const autoWord = requireNonBlank("MEMORY_AUTO_CONSOLIDATION", process.env.MEMORY_AUTO_CONSOLIDATION);
|
|
1353
|
+
if (autoWord !== undefined && autoWord !== "on" && autoWord !== "off") {
|
|
1354
|
+
throw new Error(`MEMORY_AUTO_CONSOLIDATION must be exactly "on" or "off", got ${JSON.stringify(autoWord)} — this knob decides whether memory content ` +
|
|
1355
|
+
`EGRESSES to the consolidation model automatically after every recommending task. A misspelled word read as "unset" would hand the ` +
|
|
1356
|
+
`decision back to the engine's named default (which core is flipping to ON), so an operator who believes they turned it off would be ` +
|
|
1357
|
+
`exporting their users' memory. UNSET it to take the engine default, or spell it exactly.`);
|
|
1358
|
+
}
|
|
1359
|
+
const autoRunOnRecommendation = autoWord === undefined ? undefined : autoWord === "on";
|
|
1352
1360
|
let intervalSec = 0;
|
|
1353
1361
|
const intervalRaw = requireNonBlank("MEMORY_CONSOLIDATION_INTERVAL_SEC", process.env.MEMORY_CONSOLIDATION_INTERVAL_SEC);
|
|
1354
1362
|
if (intervalRaw !== undefined) {
|
|
@@ -1377,7 +1385,7 @@ function parseMemoryConsolidationDriver() {
|
|
|
1377
1385
|
`the consolidation valve is OFF, so neither knob reaches anything (the two admin endpoints are not even mounted). ` +
|
|
1378
1386
|
`Set MEMORY_CONSOLIDATION_DRIVER=on, or unset the other two.`);
|
|
1379
1387
|
}
|
|
1380
|
-
return { enabled, intervalSec, scopes };
|
|
1388
|
+
return { enabled, intervalSec, scopes, ...(autoRunOnRecommendation !== undefined ? { autoRunOnRecommendation } : {}) };
|
|
1381
1389
|
}
|
|
1382
1390
|
function parseMemoryDomain(ctx) {
|
|
1383
1391
|
const { requirePrincipal } = ctx;
|
|
@@ -12,6 +12,7 @@ import { resolveStreamApprovalGate } from "../../tool-approval.js";
|
|
|
12
12
|
import { projectMemoryEngineCapability } from "../../memory-posture.js";
|
|
13
13
|
import { projectWriteProtectionCapability } from "../../write-protection.js";
|
|
14
14
|
import { projectSqlEngineCapability } from "../../sql-engine-posture.js";
|
|
15
|
+
import { projectWebSearchCapability } from "../../plugins/web-search.js";
|
|
15
16
|
import { workflowModelAllowlistFor, selfOrchestrationDenial } from "../../task-workflow.js";
|
|
16
17
|
import { centerEntitlementSourceWired } from "../../runtime-caps-resolver.js";
|
|
17
18
|
import { judgeAutoModeArming } from "../../auto-mode-face.js";
|
|
@@ -76,6 +77,7 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
76
77
|
memoryWrite: false,
|
|
77
78
|
memoryEngine: projectMemoryEngineCapability(deps.memoryPosture),
|
|
78
79
|
sql: projectSqlEngineCapability(deps.sqlEngineFacts?.()),
|
|
80
|
+
webSearch: projectWebSearchCapability(deps.webSearchProvider),
|
|
79
81
|
sessionBackgroundable: Boolean(deps.runStore),
|
|
80
82
|
runMemoryCaptureOptOut: Boolean(deps.runStore),
|
|
81
83
|
sharedMemory: Boolean(deps.sharedMemoryStore && deps.orgMemoryDirectory),
|
package/dist/http/server.d.ts
CHANGED
|
@@ -689,6 +689,14 @@ export interface ServiceDeploymentDeps {
|
|
|
689
689
|
* boot 期算一次的**存活对象**(纯 config 派生,运行期不变;core 座没有热改腿)。
|
|
690
690
|
* 缺席 = 这个进程不是由 composition root 装起来的(测试夹具形)⇒ 诊断面报 null、能力位报 null。 */
|
|
691
691
|
writeProtection?: import("../write-protection.js").WriteProtectionPosture;
|
|
692
|
+
/** S-382:**部署侧 WebSearch 后端**(`WEB_SEARCH_PROVIDER`)—— `GET /v1/capabilities` 的 `webSearch` 位的
|
|
693
|
+
* 唯一席位。值**逐字**取自 boot 期真正拿去造后端的那份 `webSearchConfigFromEnv()` 产物
|
|
694
|
+
* (`boot/stage-07-capability-layer.ts`),所以「能力位说 brave、装配里装的是 tavily」结构上不可能。
|
|
695
|
+
* 缺席 = 本部署没有搜索后端(未设 / 拼错 / 装配没喂席位)⇒ 窄投影报 `{backend:"none"}`;
|
|
696
|
+
* 折词的属主与全部判据在 `plugins/web-search.ts` 的 `projectWebSearchCapability` 头注。
|
|
697
|
+
* ⚠️ 席位只承载**部署默认**:单用户车道上每请求的 `settings.webSearch` 压过它,但那条腿**不经过本席**
|
|
698
|
+
* (它在场景装配里每请求现算),所以本位不会随调用方漂。 */
|
|
699
|
+
webSearchProvider?: import("../plugins/web-search.js").WebSearchProvider;
|
|
692
700
|
/** ① core ruling — SPLIT: `capabilities.workflows` = the ENGINE-CAN axis, boot-computed from core's own
|
|
693
701
|
* `workflowsCapability(deps)` (hardened script runner ∧ governance), NOT the `Boolean(workflowRunStore)` store
|
|
694
702
|
* proxy. Orthogonal to `workflowsList` (the durable-list axis = `workflowRunStore`). Falls back to the store
|
|
@@ -229,6 +229,7 @@ export function createMetrics() {
|
|
|
229
229
|
m.histogram("plan_cache_qsig", "Significant-token count of task objectives (qSig length)", [1, 2, 3, 5, 10, 20]);
|
|
230
230
|
m.counter("memory_consolidation_ops_total", "Memory consolidation store ops (1.62 B-full), by op (update/delete)");
|
|
231
231
|
m.counter("mcp_server_unavailable_total", "MCP servers skipped fail-open at task start (1.68), unreachable/misconfigured");
|
|
232
|
+
m.counter("runner_error_total", "RunnerDeps.onError frames that no phase arm claimed, by core phase (hook / compaction / interrupt-reconcile / suggestions)");
|
|
232
233
|
m.counter("a2a_peer_unavailable_total", "A2A peers skipped at task start (DESIGN-269 client leg): card unreachable / spec unusable");
|
|
233
234
|
m.counter("a2a_serve_rpc_total", "A2A server-as-peer JSON-RPC calls by method and outcome (DESIGN-269 车2)");
|
|
234
235
|
m.counter("a2a_serve_rejected_total", "A2A server-as-peer requests refused before reaching a method handler, by reason (DESIGN-269 车2)");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FileError, type ExecutionError, type RemoteExecutionError, type FileInfo, type Result } from "@sema-agent/core";
|
|
1
|
+
import { FileError, type ExecutionError, type RemoteExecutionError, type FileInfo, type Result, type SymlinkChain } from "@sema-agent/core";
|
|
2
2
|
export interface ShellRunResult {
|
|
3
3
|
stdout: string;
|
|
4
4
|
stderr: string;
|
|
@@ -14,6 +14,7 @@ export interface PosixShellFsDeps {
|
|
|
14
14
|
}
|
|
15
15
|
export interface PosixShellFs {
|
|
16
16
|
readLink(p: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
|
|
17
|
+
canonicalChain(p: string, abortSignal?: AbortSignal): Promise<Result<SymlinkChain, FileError>>;
|
|
17
18
|
canonicalPath(p: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
|
|
18
19
|
statFileInfo(p: string, abortSignal?: AbortSignal): Promise<Result<FileInfo, FileError>>;
|
|
19
20
|
listDir(p: string, abortSignal?: AbortSignal): Promise<Result<FileInfo[], FileError>>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { FileError, } from "@sema-agent/core";
|
|
3
|
-
import { shellQuote, kindFromMode } from "./remote-shell.js";
|
|
3
|
+
import { runSymlinkChain, shellQuote, kindFromMode } from "./remote-shell.js";
|
|
4
4
|
import { classifyFsStderr, fileErrorFromExec } from "./remote-env-file-error.js";
|
|
5
5
|
const ok = (value) => ({ ok: true, value });
|
|
6
6
|
export function createPosixShellFs(deps) {
|
|
@@ -22,6 +22,9 @@ export function createPosixShellFs(deps) {
|
|
|
22
22
|
return { ok: false, error: classifyFsStderr(r.value.stderr, abs, "not_found") };
|
|
23
23
|
return ok(r.value.stdout.trim());
|
|
24
24
|
},
|
|
25
|
+
async canonicalChain(p, abortSignal) {
|
|
26
|
+
return runSymlinkChain(resolve(p), (cmd) => run(cmd, abortSignal));
|
|
27
|
+
},
|
|
25
28
|
async canonicalPath(p, abortSignal) {
|
|
26
29
|
const abs = resolve(p);
|
|
27
30
|
const r = await run(`realpath -- ${shellQuote(abs)}`, abortSignal);
|