@sema-agent/server 7.100.0 → 7.101.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 +22 -1
- package/README.md +12 -1
- package/README.zh-CN.md +9 -1
- package/USAGE.md +80 -9
- package/dist/approval-card.d.ts +24 -12
- package/dist/approval-card.js +4 -3
- package/dist/approval-policy-settlement.d.ts +31 -0
- package/dist/approval-policy-settlement.js +16 -0
- package/dist/approval-reconciler.d.ts +8 -1
- package/dist/approval-reconciler.js +5 -4
- package/dist/boot/config-center.js +86 -20
- package/dist/boot/execution-env.js +20 -2
- package/dist/boot/parked-revive-gate.js +1 -0
- package/dist/boot/reapers.js +1 -0
- package/dist/boot/resolve-spec.js +25 -8
- package/dist/boot/runner-deps.js +9 -3
- package/dist/boot/stage-02-registry-pull.js +9 -0
- package/dist/boot/stores.js +4 -0
- package/dist/config-catalog.js +5 -3
- package/dist/config-center/apply-effective.d.ts +25 -0
- package/dist/config-center/apply-effective.js +144 -69
- package/dist/config-center/apply-ledger.d.ts +12 -2
- package/dist/config-center/apply-ledger.js +4 -1
- package/dist/config-center/facade.d.ts +1 -1
- package/dist/config-center/facade.js +1 -1
- package/dist/config-center/http-client.d.ts +4 -1
- package/dist/config-center/http-client.js +15 -10
- package/dist/config-center/read-warnings.d.ts +115 -6
- package/dist/config-center/read-warnings.js +115 -5
- package/dist/config-center/types.d.ts +6 -0
- package/dist/config-invariants.d.ts +2 -2
- package/dist/config-invariants.js +15 -0
- package/dist/config-provider.d.ts +4 -0
- package/dist/config-provider.js +64 -9
- package/dist/config-types.d.ts +37 -8
- package/dist/config.js +87 -22
- package/dist/deployment-governance.d.ts +45 -12
- package/dist/deployment-governance.js +37 -9
- package/dist/env-name-allowlist-knobs.d.ts +37 -0
- package/dist/env-name-allowlist-knobs.js +24 -0
- package/dist/fleet-client.d.ts +2 -1
- package/dist/hooks/hook-llm.js +13 -4
- package/dist/host-lsp-manager.d.ts +14 -2
- package/dist/host-lsp-manager.js +7 -6
- package/dist/http/admission.js +22 -7
- package/dist/http/memory-store-refusal-reply.d.ts +24 -0
- package/dist/http/memory-store-refusal-reply.js +10 -0
- package/dist/http/routes/approvals-assistant.js +1 -0
- package/dist/http/routes/capabilities.js +3 -3
- package/dist/http/routes/memory-bundle.js +3 -0
- package/dist/http/routes/memory-compliance.js +3 -5
- package/dist/http/routes/memory-origin.js +3 -5
- package/dist/http/routes/sessions.js +7 -6
- package/dist/http/routes/side-query.js +10 -4
- package/dist/instruction-source-notice.d.ts +97 -0
- package/dist/instruction-source-notice.js +78 -0
- package/dist/memory-layer-legacy-lock.d.ts +14 -0
- package/dist/memory-layer-legacy-lock.js +74 -0
- package/dist/memory-operator-faces.js +1 -17
- package/dist/memory-store-refusal.d.ts +37 -0
- package/dist/memory-store-refusal.js +14 -0
- package/dist/model-entry-refusal.d.ts +130 -0
- package/dist/model-entry-refusal.js +157 -0
- package/dist/model-provider.d.ts +9 -3
- package/dist/model-route-endpoint.d.ts +1 -1
- package/dist/model-select.d.ts +49 -4
- package/dist/model-select.js +48 -17
- package/dist/observability/fail-open.d.ts +9 -1
- package/dist/observability/fail-open.js +9 -1
- package/dist/observability/metrics.js +1 -0
- package/dist/observability/secret-env-scrub.d.ts +27 -4
- package/dist/observability/secret-env-scrub.js +22 -7
- package/dist/plugins/caching-session-store.d.ts +6 -0
- package/dist/plugins/caching-session-store.js +6 -0
- package/dist/plugins/local-session-store.d.ts +20 -25
- package/dist/plugins/local-session-store.js +111 -64
- package/dist/plugins/pg-session-storage.d.ts +7 -0
- package/dist/plugins/pg-session-storage.js +19 -0
- package/dist/plugins/remote-env-host.d.ts +3 -1
- package/dist/plugins/remote-env-host.js +3 -6
- package/dist/plugins/tidb-pool.js +5 -0
- package/dist/plugins/tidb-session-store.d.ts +13 -0
- package/dist/plugins/tidb-session-store.js +14 -0
- package/dist/plugins/web-search.d.ts +2 -1
- package/dist/plugins/web-search.js +43 -24
- package/dist/project-memory.d.ts +10 -0
- package/dist/project-memory.js +28 -14
- package/dist/run-local.d.ts +17 -15
- package/dist/run-local.js +115 -15
- package/dist/sealed-key.d.ts +5 -2
- package/dist/server-secret-env.d.ts +23 -1
- package/dist/server-secret-env.js +9 -0
- package/dist/tool-approval.d.ts +65 -31
- package/dist/tool-approval.js +96 -49
- package/dist/trace/core-keyset-guard.d.ts +27 -5
- package/dist/trace/engine-notice-wire.d.ts +27 -1
- package/dist/trace/engine-notice-wire.js +8 -0
- package/dist/trace/wire-projection-faces.d.ts +2 -2
- package/dist/trace/wire-projection-faces.js +2 -2
- package/package.json +2 -2
- package/dist/memory-layer-preflight.d.ts +0 -46
- package/dist/memory-layer-preflight.js +0 -121
package/MIGRATION.md
CHANGED
|
@@ -7,6 +7,24 @@
|
|
|
7
7
|
> ⚠️ 完整清单在仓库根 `CHANGELOG.md`——它**不随 npm tarball 出包**(本文件随包)。看完整迁移窗的
|
|
8
8
|
> 权威姿势是源码 tag diff:`git diff v<旧>..v<新>`(每版都推 `v<版本>` tag);npm 包页也镜像 CHANGELOG。
|
|
9
9
|
|
|
10
|
+
## 7.101.0 —— 十四条(`run-local` 执法三张审批名单 / 人停下的 run 退 1 / 模型目录 fail-closed 三条 + 类型面 `ServiceConfig.modelRefusals` · `ServiceConfig.lspEnvAllow` / 记忆抹除面 409 撤 + 存量只读位一次性 `chmod`(滚动窗内会被旧副本再锁,起服 warn 点名)/ settings 层 `crossSessionInbound: accept` 改答 hold / 子进程剥名收窄 / SQL `session_meta` 新列 / 沙箱名单形错拒启 + 空串值照注入 / `WEB_SEARCH_ENDPOINT` 带 userinfo 拒启 / 配置目录两行 `csv` → `secret` / 被拒名撞档位别名 ⇒ 模型面组拒;S-690 / S-694 / S-699 / S-704 / S-705 / S-712 / S-713 / S-716 / S-723)
|
|
11
|
+
|
|
12
|
+
- **`run-local` 上 `APPROVAL_REQUIRE` / `APPROVAL_DENY` / `APPROVAL_NEVER_AUTO` 生效**(改前:这条一次性 CLI 腿不读三张名单,名单工具照跑,只有启动日志一条 `tool_policy_only_sensitive_baseline` warn)。改后与 HTTP 腿同一只策略:`APPROVAL_DENY` 命中 ⇒ 当场拒;`APPROVAL_REQUIRE` / `APPROVAL_NEVER_AUTO` 命中 ⇒ stdin 是 TTY 就 `y/N` 问人(问句多一行这次调用的参数),否则当场拒并在 stderr 点名三张名单键(`APPROVAL_NEVER_AUTO` 不吃 `APPROVAL_AUTO_BUDGET` 预算)。**谁受伤**:无 TTY(CI)跑 `run-local`、而 `.env` 与服务共用并带着名单的脚本 —— 名单工具从「照跑」变「当场拒」。**迁移**:给 CLI 环境单独一份 `.env`(不带名单),或设 `APPROVAL_AUTO_BUDGET=<N>` 让 require 名单前 N 次自动批,或在终端里跑(TTY 问人)。`tool_policy_only_sensitive_baseline` 在 `run-local` 上不再出现(名单已执法)。
|
|
13
|
+
- **`run-local`:人拒掉一次工具调用而停下的 run 退 1**(改前退 0)。这类 run 的终局是 completed + `haltedOnUserRejection`(无 TTY 当场拒、TTY 答 N 都是这一形),意思是「停在等人指示」而不是「干完了活」;改后退 1(`--json` 形同样退 1),stdout 仍是模型停下前产出的文字(常为空);非 `--json` 形 stderr 多一行 `task stopped (haltedOnUserRejection): … continue it with --session <id>`。**谁受伤**:靠「`AUTONOMY=ask` / 名单被拒后仍退 0」继续往下跑的脚本。**迁移**:要把「被拒」当成功的脚本改看 stderr 停因行或 `--json` 输出的 `haltedOnUserRejection`。
|
|
14
|
+
- **本部署服务不了的目录条目不可用(S-704 ①,行为面)**:`provider:"anthropic"` 的目录条目在没配 `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN` 的部署上不进可选目录(`GET /v1/models` 不列、不当缺省),显式选中 ⇒ 400 `request.model_unavailable`(拒因码 `config.model_no_brain`);此前它落网关 brain、带着条目自己的钥匙。不合 settings-schema `ModelEntry` 形的条目同律(`config.model_entry_invalid`;坏 `baseUrl` 此前被退回部署网关)。目录里每条都不可用 ⇒ 那一代模型面整拒(上一代继续服务)。**迁移**:配 anthropic 凭据,或把条目改成 `gateway` / `vllm` 并给它自己的 `baseUrl`;按 `sema_registry_model_no_brain` / `sema_registry_model_entry_invalid` 日志修条目。
|
|
15
|
+
- **一条坏条目只丢它自己(S-704 ②,行为面,所有目录型域)**:schema 逐条丢掉的条目不再让 refresh / 迟到 boot 整拒候选;其余条目照用,坏条目每条一行 `config_entry_dropped`(坐标 + 字段诊断)。**谁受伤**:依赖「一条坏条目冻结整份候选、上一代继续服务」的部署 —— 现在新一代(少那一条)生效。**迁移**:按日志坐标修那一条。
|
|
16
|
+
- **被拒条目的名字撞档位词 / CC 别名 ⇒ 这份候选的模型面整组被拒(S-712,行为面)**:目录里一条不可用(本机无 brain / 形坏被读腿丢掉)的条目恰好叫 `sonnet` / `opus` / `haiku` / `pro` / `flash` / `best` 之类,而档位表会铸出这个词 ⇒ 模型面停在上一代(码 `config.model_refused_name_minted`,warn `sema_registry_model_plane_refused` 点名「被拒名 → 目标条目」);此前(7.101.0 开发期)这个词被静默铸到另一条条目上,显式点它 / 绑它的角色跑在别人的端点与账号上。**迁移**:改名、修好那条条目,或改绑铸出这个词的档位绑定;多台 worker 共用一份中心目录、只有部分 worker 配了 anthropic 凭据(目录里 anthropic 条目叫 `opus` / `sonnet` / `haiku` 且激活了档位组)时,无凭据的 worker 上这一形**每一代**都会出现 —— 在中心按 worker 的 roster(`SEMA_REGISTRY_WORKER`)里把这些条目排出那些 worker,或给它们配凭据,或至少设 `MODEL_ID` 让它们起得来(DEBTS S-749)。没有字符串名的被丢行不触发本条(没有可保留的名字,与停用同处置)。同批:模型面三条不变量(缺省模型 / 路由端点 / 本条)不成立时只拒**模型面这一组**(目录,以及带模型引用、随它同代的 collab 模板 —— boot 按组回落盘上 LKG 时同样整组取 LKG 那一代),同候选的审批名单 / 限额照落(此前整份候选不落,boot 腿日志还写成 `config_source_unreachable_using_env`;拉取成功而应用失败现在是 `config_apply_refused`)。
|
|
17
|
+
- **`MODEL_ID` 不再是 loadConfig 期的硬要求(S-705)**:目录有可用条目即可省;目录应用之后仍没有缺省模型 ⇒ 拒启(`config.default_model_missing`,句首保留 `MODEL_ID is required`)。中心超出 `CONFIG_BOOT_FETCH_BUDGET_MS` 且无 LKG 时 boot 那一刻没有目录:没写 `MODEL_ID` 就拒启 —— 写一个 `MODEL_ID` 兜底或调大预算。
|
|
18
|
+
- **类型面**:`ServiceConfig.modelRefusals`(新必填席;手铸 `ServiceConfig` 的消费方 tsc 即红 —— env 形写 `{}`)与 `ServiceConfig.lspEnvAllow`(S-699 新必填席 `readonly string[]`,同样 tsc 即红 —— 缺省形写 `[]`);`SealedKeyPoisonReason` 删 `unsupported_alg`(形错的 sealed 密文现在让整条不可用,到不了毒丸臂);`FoldedReadWarning` 带 `scope` 判别位、`FetchEffectiveResult` 新键 `droppedEntries`。
|
|
19
|
+
- **记忆抹除面的 409 `memory.layer_unwritable` 撤销(S-697,随 core 7.31.0 #1076 提货;wire 码删除 + 行为面)**:`POST /v1/memory/erase`、`POST /v1/memory/origin/entries/:entryId/clear`、`POST /v1/sessions/:id/memory/erase` 不再在引擎之前做盘上可写预检,这个码不再出现(7.100.0 那条「按 409 体 `chmod u+w <dir>` 后重试」的指路随之撤)—— core 7.31.0 的挂载**一个模式位都不改**(只读层是会话策略:往会话不写的层里写在写点被拒,绕过工具写进去的字节永不被采纳)。7.100.0 上被 409 盖住的三种答复回到 core 自己的读数(非法选择子 400 `config.memory_erasure_request`、清标空理由 422 `memory.origin_clear_invalid`、已提交抹除同 `requestId` 重放的 200 收执),锁住的是别的层时抹除可写层条目照常 200。**升级(一次性)**:存量已锁目录在**最后一个 ≤7.30.x 会话结束后**(= 7.100.0 及更早的 server 副本全部停掉之后)执行 `chmod -R u+w <memoryDir>`(file 记忆引擎的 `<数据根>/memory`)—— 混跑期旧引擎每次只读挂载都会再锁一次,7.31 不替你解。**谁受伤**:没做这一步、又去删一条住在锁住目录里的条目的部署 ⇒ `500`(日志带 core 的同一条 `chmod -R u+w` 补救句),journal 留盘、下次起服失败,照补救句做完即自愈(`docs/DEPLOY-PREREQS.md` 同名段)。**滚动窗内这一形会反复出现**:同一数据根上还有 7.100.0 及更早的副本在跑时,它每次只读挂载一层都会把那一层再锁一次 —— 窗内做过的 `chmod` 会被撤回,7.101.0 副本在那一层上删条目照样 `500` + journal 卡盘。7.101.0 起服时对记忆挂载面探一次:有锁住的层 ⇒ 一条 warn(`engine_notice`,码 `memory.layer_locked_legacy`,`detail.layers` = 层名、`detail.remedy` = 同一句 `chmod -R u+w '<memoryDir>'`),**照常起服**(不拒启:窗内每次 boot 都可能刚被旧副本锁过);看到这一行就先别在那几层上抹除 / 清标,等旧副本全停后执行那一句。按码分支 `memory.layer_unwritable` 的消费方(sdk 错误码目录)删掉那一臂。
|
|
20
|
+
- **settings 层里残留的 `crossSessionInbound: "accept"` ⇒ 跨会话消息 hold(core 7.31.0 #1078,行为面)—— 本服务不受影响(只 env 层接线)**:本服务三层来源里只有 env 管理层 `CROSS_SESSION_INBOUND` 接线(user / repo 两层不读),而 env 写 `accept` 自 7.100.0 起就拒启 ⇒ 下面这一形只波及**自持 settings 层的嵌入方**。7.30.x 把这个退役词读成 `next-turn` 并每腿通告一次(7.100.0 本节那句「由引擎自己读成 `next-turn`」随之过期);7.31.0 起它与任何未知词同路 —— 该层进 `invalidLayers`、fail-closed 落 `hold`(每条跨会话消息以 `invalid-setting` 因由扣住,`peer.inbound_disposition` 披露 `held`)。env `CROSS_SESSION_INBOUND=accept` 自 7.100.0 起就拒启,不受影响;`@sema-agent/settings-schema` 6.0.0 的文件层本就拒这个词,所以只有绕过 schema 写进去的 settings 文件会撞上。**迁移**(仅嵌入方):把每一层的 `accept` 改写成 `next-turn`。
|
|
21
|
+
- **子进程剥名收窄(core 7.31.0 #1081,运行面;7.100.0 本节「子进程按名剥凭据形变量」一条的修正)**:十个预算名(`MAX_NEW_TOKENS` `MAX_COMPLETION_TOKENS` `AIDER_MAP_TOKENS` `AIDER_MAX_CHAT_HISTORY_TOKENS` `MAX_BATCH_PREFILL_TOKENS` `MAX_WAITING_TOKENS` `MAX_TOP_N_TOKENS` `RATE_LIMIT_TOKENS` `PROMPT_TOKENS` `CONTEXT_TOKENS`)与两个开关(`DB_FOREIGN_KEYS` `JSON_SORT_KEYS`)按**精确名**豁免,重新到得了工具子 shell、git 子进程与本地语言服务器;装饰过的变体(`USERS_MAX_NEW_TOKENS`、`MAX_NEW_TOKENS_2`)仍剥。为这五名改过名或用 `inheritEnv:[names]` 点名放行的部署可以撤回那一手(留着也无害)。
|
|
22
|
+
- **SQL 后端:`session_meta` 新列 `instruction_source`(S-690,存储面)**:存量 MySQL 协议 / PostgreSQL 库升级后**拒启**(`config.schema_mismatch`,拒因里逐字给出 `ALTER TABLE session_meta ADD COLUMN instruction_source VARCHAR(64) NULL;`,PG 为 `… VARCHAR(64) COLLATE "C";`),照它加列或删库重建;滚动序恒为「**先升库、再升副本**」(加列之后,旧副本重启会把它看成幽灵列而拒启)。`DB_BACKEND=local` 与内存形零动作。见上方「SQL 存储面 BREAKING」表。
|
|
23
|
+
- **沙箱名单形错拒启 + 空串值照注入(S-713,行为面)**:`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` 写了空段、尾逗号(`A,`)、带 `-` / `.` 的键名(`MY-TOKEN`)或点名名单型旋钮(自己 / 另一只沙箱名单 / `LSP_ENV_ALLOW`)⇒ **拒启**(拒句点名键与第几段,不回显原值;修前静默吞或照转发)。点到的键值为空串(compose `X=${X:-}` 在宿主没设时的常态)⇒ **照注入空串**(修前不注入):local-docker 上 `-e X=` 盖掉镜像里的同名缺省值,e2b 上盖掉 `SANDBOX_PKG_SOURCE` 推导出的同名包源变量。**迁移**:按拒句修名单;不想盖缺省值就把那个名从名单里删掉。
|
|
24
|
+
- **`WEB_SEARCH_ENDPOINT` 带 userinfo ⇒ 拒启(S-716,行为面)**:`WEB_SEARCH_PROVIDER` 点名了后端、`WEB_SEARCH_ENDPOINT` 形如 `https://user:pw@host/…`(或只带 `user@`)的部署升级后拒启(`config.web_search_invalid`);每请求 `settings.webSearch.endpoint` 带这一形从 202 变 `400 request.field_invalid`(修前这一形本来就用不了:fetch 每次都抛,错误文本还带出口令)。**迁移**:brave / tavily 的凭据走 `WEB_SEARCH_API_KEY`(或 `settings.webSearch.apiKey`);带 basic-auth 的 SearXNG 在前面放一层注入鉴权的反代。
|
|
25
|
+
- **配置目录两行 `csv` → `secret`(S-713,wire 形变)**:`GET /v1/config/catalog` 的 `E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` 两行 `type` `csv` → `secret`、`valueClass` `plain` → `secret`、`effectiveValue` 从名字数组变 `{ present }`,`summary.secret` 计数 +2。读这两行名单内容的消费方改读起服行 `remote_exec_enabled.sandboxEnvKeys`(只键名);这两只旋钮的原文从此也不进 host shell / git 子进程 / 语言服务器(在这些子进程里读它们的脚本收不到了)。
|
|
26
|
+
|
|
27
|
+
|
|
10
28
|
## 7.100.0 —— 十条 BREAKING(部署面 `CROSS_SESSION_INBOUND=accept` 拒启 / 类型面 `MailboxStore.ack` / WebSearch 配置形错响亮 / 请求面 `settings.webSearch` / WebSearch 报错句换形 / deny 部署的「无人可答」记部署政策 / 记忆抹除面新 409 / 审批名单热应用 / 子进程剥名 / local 车道回滚前收敛 ask 账本)
|
|
11
29
|
|
|
12
30
|
- **`CROSS_SESSION_INBOUND=accept` ⇒ 拒启**(core 7.30.0 把 `accept` 从 `crossSessionInbound` 闭集删掉,拆成 `wake`(立刻唤醒收件方)/ `next-turn`(排到收件方下一轮 = 旧 `accept` 的那一档);本仓词表直接取 core 常量,零别名 —— 拒启句列出新词表)。**迁移**:env 写 `next-turn`(原义)或 `wake`;`settings.json` 里的 `accept` 由引擎自己读成 `next-turn` 并通告一次,但 `@sema-agent/settings-schema` 6.0.0 的文件层对 `accept` 响亮拒 ⇒ 一并改写。本版精确钉 core 7.30.1 + settings-schema 6.0.0(锁步序:core → settings-schema → server → 宿主才可写 `wake`)。
|
|
@@ -26,7 +44,7 @@
|
|
|
26
44
|
- **`ServiceConfig.configCenter.token` 删除,换成 `credential`(7.99.0,S-651 波 A-1,类型面)**:凭证改为一只每次请求取值的活对象;新旋钮 `SEMA_REGISTRY_TOKEN_FILE`(与 `SEMA_REGISTRY_TOKEN` 二选一,都设 ⇒ 拒启)。手铸 `ServiceConfig` 的消费方 tsc 即红。**盘上数据**:旧 LKG 文件(`config-lkg-<worker>.json`,formatVersion 1)按凭证身份分箱后一次性迁移 —— 此刻凭证是该 worker 的拉取令牌且新箱无文件 ⇒ 迁进新箱、旧文件改名 `.migrated-v1`;其它形响亮拒一行(点名旧路径与出路);迁移代码到期删除(S-683,10-31)。旧 prompt 活动状态目录暂不迁移(S-685)。
|
|
27
45
|
- **Node 运行时下限改成实测值 `^20.19.0 || >=22.12.0`(7.99.0)**:此前 `>=20` 是假话 —— 7.98.0 在 Node 20.0–20.18 与 22.0–22.11 上本就起不来(依赖 e2b 的 CJS 入口撞 chalk@5 纯 ESM,S-682);开了 engine-strict 的安装在范围外会失败。**迁移**:Node 升到 20.19+ 或 22.12+。
|
|
28
46
|
|
|
29
|
-
## SQL 存储面 BREAKING(3.0.0
|
|
47
|
+
## SQL 存储面 BREAKING(3.0.0 之后;列增批自 7.101.0 起入表)
|
|
30
48
|
|
|
31
49
|
**常设口径**:本仓**不出 `ALTER TABLE` 增量迁移**(成文裁定:预生产期零存量用户窗口,schema 变更
|
|
32
50
|
一律删表/删库重建);boot 对旧形 schema **拒启并带恢复动作文案**,不会静默跑在错形表上。只用
|
|
@@ -37,6 +55,9 @@ file/local 存储形(未配 SQL 后端)的部署不受本节任何条目影响
|
|
|
37
55
|
| **7.6.0**(2026-08-08) | SQL 双端归一化第一刀:隔离键排序规则钉死(MySQL 腿表级 `COLLATE utf8mb4_bin`/PG 腿逐列 `COLLATE "C"`)+索引名 36 条+列名/宽度收窄 | **删库重建**(两方言) |
|
|
38
56
|
| **7.8.0**(2026-08-09) | SQL 命名三轴归一化第二刀:9 张表名单数化、epoch 毫秒列补 `_ms` 后缀 5 列、approval 两表 `version→rev`;wire 面零变化 | **删库重建**(两方言) |
|
|
39
57
|
| **7.14.0**(2026-08-12) | `tool_result` 换代(随 core 5.26.0 的 ref 格式换代):ref 四段单射形、主键 `VARCHAR(190)→518`、新增出处两列 `owner_session_id`/`owner_task_id` | 升级前两方言 **`DROP TABLE tool_result`**(不删=boot 拒启;offload 产物是可恢复窗缓存,转录内联预览不受影响) |
|
|
58
|
+
| **7.101.0** | `session_meta` 新列 `instruction_source VARCHAR(64) NULL`(PG `COLLATE "C"`;S-690 项目指令换源通告的基线) | 两方言按拒启句里给出的 `ALTER TABLE session_meta ADD COLUMN …` 加列,或删库重建;**先升库、再升副本**(加列后旧副本重启见幽灵列拒启) |
|
|
59
|
+
|
|
60
|
+
> 列增批此前只记在 CHANGELOG / `docs/DEPLOY-PREREQS.md`(7.92.0 `session_policy`、7.93.4 `approval_ask.settled_by` 各加过一列),自 7.101.0 起同样入本表。7.101.0 这一列的拒启句自带可粘贴的 `ALTER`,本仓仍不替你执行。
|
|
40
61
|
|
|
41
62
|
## server 3.0.0 —— BREAKING 四条(2026-07-31)
|
|
42
63
|
|
package/README.md
CHANGED
|
@@ -140,6 +140,17 @@ curl -s localhost:8090/v1/tasks -H "Authorization: Bearer <SERVICE_AUTH_TOKEN>"
|
|
|
140
140
|
resume from. A gated `ask` (e.g. `AUTONOMY=ask`, which routes every shell command through approval)
|
|
141
141
|
is answered inline: a `y/N` prompt when stdin is a TTY, otherwise a fail-closed **deny** with a
|
|
142
142
|
stderr line naming the knob that produced the gate.
|
|
143
|
+
- **The three approval lists are enforced here too** (since 7.101.0 — before that they were silently
|
|
144
|
+
inert on this leg): `APPROVAL_DENY` refuses outright; `APPROVAL_REQUIRE` / `APPROVAL_NEVER_AUTO` ask
|
|
145
|
+
through that same inline answer (TTY `y/N`, otherwise deny with the list keys named on stderr);
|
|
146
|
+
`APPROVAL_AUTO_BUDGET` auto-approves the first N `APPROVAL_REQUIRE` calls (never-auto tools always ask).
|
|
147
|
+
A CI script that shares a server's `.env` should get its own `.env`, or run in a terminal.
|
|
148
|
+
- **The `y/N` prompt shows the call's full arguments** (JSON, terminal-escaped; a call whose arguments cannot
|
|
149
|
+
be displayed is refused instead of asked), and one terminal answers one question at a time — concurrent asks
|
|
150
|
+
from the main task and its sub-agents queue behind each other.
|
|
151
|
+
- **A run stopped by a refusal exits 1** (since 7.101.0; it used to exit 0), `--json` included: whatever the
|
|
152
|
+
model wrote before the refusal still goes to stdout, and without `--json` stderr names the stop
|
|
153
|
+
(`task stopped (haltedOnUserRejection) …`).
|
|
143
154
|
- **Full-stack, one command** (DB + object store + registry web + sandbox pool; Docker and k8s
|
|
144
155
|
paths): [`sema-agent/sema-deploy`](https://github.com/sema-agent/sema-deploy).
|
|
145
156
|
- **Sandbox package sources**: default = official upstreams (pypi/npmjs/crates.io/…). For
|
|
@@ -156,7 +167,7 @@ The server is configured entirely through environment variables. The most import
|
|
|
156
167
|
| `PORT` | `8090` | HTTP listen port |
|
|
157
168
|
| `BIND_HOST` (alias `HOST`) | see note | Listen address. An explicit `BIND_HOST` **always wins**. Default: `127.0.0.1` when the write face is unauthenticated (`ALLOW_UNAUTHED_WRITES=true` **and** no service token configured), otherwise all interfaces — deployments with a token are unaffected. Since 3.15.0 the `HOST` alias is **not** fully equivalent: shells commonly set `HOST` to the machine name without the operator knowing, so when the narrowing condition above holds it wins over an inherited `HOST` (logged as `bind_host_from_HOST_env_overridden`). Set `BIND_HOST` explicitly, or configure a service token, to expose the write face. |
|
|
158
169
|
| `MODEL_GATEWAY_BASEURL` | **no default** | OpenAI-compatible gateway base URL (without `/chat/completions`). **No factory default** (S-623): every route that rides the openai-compatible lane must resolve to an explicit endpoint — the model's own catalog `baseUrl`, or this knob — else the server refuses to boot, naming the models and printing the explicit local spelling (`MODEL_GATEWAY_BASEURL=http://127.0.0.1:8000/v1`). An Anthropic-only deployment may leave it unset. Setting `MODEL_GATEWAY_FALLBACK_URLS` without it is refused too |
|
|
159
|
-
| `MODEL_ID` | **required** | Default model id — **no factory default since 3.0.0**.
|
|
170
|
+
| `MODEL_ID` | **required unless a catalog supplies the default** | Default model id — **no factory default since 3.0.0**. Since 7.101.0 (S-705) the check runs **after** the catalog is applied: `MODEL_ID` **or** a usable catalog default (`config.d/models.json` / the config center) is enough; with neither the server refuses to boot (`config.default_model_missing`, naming both ways out). The old baked-in default was an internal-only model name, so every external deployment failed later and further from the cause: a gateway `400` plus a cascade of title-hook warnings |
|
|
160
171
|
| `MODEL_API_KEY` | — | Gateway API key (optional). Pairs only with `MODEL_GATEWAY_BASEURL`: with no gateway it is not loaded and sent nowhere |
|
|
161
172
|
| `SERVICE_AUTH_TOKEN` | — | Callers must send `Authorization: Bearer <token>` |
|
|
162
173
|
| `DB_BACKEND` | `local`* | `mysql` (any MySQL-protocol DB: MySQL/TiDB/MariaDB) / `pg` (PostgreSQL) / `local` (file-backed, no DB) / `memory` (explicit in-memory: nothing survives a restart, durable-runs faces 501). *Bare boot (no DB env at all) defaults to `local` so a single-user machine keeps its runs across restarts; any SQL signal (`SESSION_BACKEND` or `MYSQL_/PG_HOST`) keeps the `mysql` engine default, and `REQUIRE_PRINCIPAL=true` bare boots stay `memory`. (`TIDB_HOST`/`_PORT`/`_USER`/`_PASSWORD`/`_DATABASE`/`_POOL_SIZE` are retired names, not an alias — since 5.0.0 setting any of them refuses to boot, pointing at the `MYSQL_*` replacement; TiDB itself connects fine through `MYSQL_*`, since it speaks the MySQL protocol.) (the local file store has no tenant isolation — a warning says so). A DEFAULT-derived `local` that cannot create its data root degrades to memory with a warning + the `store_backend_degraded` gauge; an EXPLICIT `DB_BACKEND=local` fails loud instead. Setting `mysql`/`pg` explicitly also switches sessions to durable |
|
package/README.zh-CN.md
CHANGED
|
@@ -121,6 +121,14 @@ curl -s localhost:8090/v1/tasks -H "Authorization: Bearer <SERVICE_AUTH_TOKEN>"
|
|
|
121
121
|
- **这条腿没有 durable 审批 park**:一次性 CLI 没有 `/v1/approvals/:id/decide` 可赎回。被门住的 `ask`
|
|
122
122
|
(例如 `AUTONOMY=ask`,它把每条 shell 命令都送进审批链)当场结算:stdin 是 TTY 就 `y/N` 问人,
|
|
123
123
|
否则 fail-closed **拒绝**并在 stderr 点名是哪个旋钮产的这道门。
|
|
124
|
+
- **三张审批名单在这条腿上同样执法**(7.101.0 起;此前在本腿静默不生效):`APPROVAL_DENY` 当场拒;
|
|
125
|
+
`APPROVAL_REQUIRE` / `APPROVAL_NEVER_AUTO` 走上面那条当场结算(TTY `y/N`,否则拒并在 stderr 点名名单键);
|
|
126
|
+
`APPROVAL_AUTO_BUDGET` 让前 N 次 `APPROVAL_REQUIRE` 自动批(never-auto 工具恒问人)。与服务共用 `.env`
|
|
127
|
+
的 CI 脚本请给它单独一份 `.env`,或在终端里跑。
|
|
128
|
+
- **`y/N` 问句带上这次调用的完整参数**(JSON、终端安全化;参数展示不出的调用不问、当场拒);一个终端一次只答
|
|
129
|
+
一问 —— 主任务与子代的并发询问依次排队。
|
|
130
|
+
- **因拒答而停下的 run 退 1**(7.101.0 起,`--json` 也一样;此前退 0):模型在拒答前写的文字照打 stdout,
|
|
131
|
+
非 `--json` 形 stderr 一行点名停因(`task stopped (haltedOnUserRejection) …`)。
|
|
124
132
|
- **一键全栈部署**(DB + 对象存储 + registry 网站 + 沙箱池,docker/k8s 双路径):
|
|
125
133
|
[`sema-agent/sema-deploy`](https://github.com/sema-agent/sema-deploy)。
|
|
126
134
|
- **沙箱装包源**:缺省 = 官方源(pypi/npmjs/crates.io/…)。中国大陆部署配
|
|
@@ -136,7 +144,7 @@ curl -s localhost:8090/v1/tasks -H "Authorization: Bearer <SERVICE_AUTH_TOKEN>"
|
|
|
136
144
|
| `PORT` | `8090` | HTTP 监听端口 |
|
|
137
145
|
| `BIND_HOST`(别名 `HOST`) | 见说明 | 监听地址。显式 `BIND_HOST` **恒生效**。缺省:写面无鉴权时(`ALLOW_UNAUTHED_WRITES=true` **且**未配任何 service token)= `127.0.0.1`,否则全接口——配了 token 的部署不受影响。自 3.15.0 起 `HOST` 别名**不再完全等效**:shell 常把 `HOST` 设成机器名而 operator 并不知情,于是上述收窄条件成立时,收窄压过继承来的 `HOST`(告警事件 `bind_host_from_HOST_env_overridden`)。要暴露写面,显式设 `BIND_HOST`,或配一个 service token。 |
|
|
138
146
|
| `MODEL_GATEWAY_BASEURL` | **无缺省** | OpenAI 兼容网关地址(不带 `/chat/completions`)。**无出厂缺省**(S-623):每一条落 openai 兼容腿的路由都必须解析到显式端点 —— 目录条目自己的 `baseUrl`,或本键 —— 否则拒启,拒因点名模型并给出本机写法(`MODEL_GATEWAY_BASEURL=http://127.0.0.1:8000/v1`)。anthropic 单路由部署可不设。只设 `MODEL_GATEWAY_FALLBACK_URLS` 不设本键同拒 |
|
|
139
|
-
| `MODEL_ID` |
|
|
147
|
+
| `MODEL_ID` | **必填(目录给出缺省时可省)** | 缺省模型 id ——**3.0.0 起无出厂缺省**。7.101.0 起(S-705)判据挪到目录应用**之后**:`MODEL_ID` **或**目录里一条可用的缺省(`config.d/models.json` / 配置控制面)任一在场即可;两者皆无 = 拒启(码 `config.default_model_missing`,指路二选一)。旧的烤死缺省是内网模型名,外部部署必炸且炸在离根因最远处:网关 `400` + 标题 hook 连环告警 |
|
|
140
148
|
| `MODEL_API_KEY` | — | 网关 key(可选)。只与 `MODEL_GATEWAY_BASEURL` 配对:没设网关时不装载、不发往任何路由 |
|
|
141
149
|
| `SERVICE_AUTH_TOKEN` | — | 调用方需带 `Authorization: Bearer <token>` |
|
|
142
150
|
| `DB_BACKEND` | `local`* | SQL 引擎:`mysql`(任何 MySQL 协议库:MySQL/TiDB/MariaDB —— TiDB 走这个值,**没有** `tidb` 别名,写它启动即拒)/ `pg`(PostgreSQL)/ `local`(免 DB 文件持久化)/ `memory`(显式纯内存)。显式设置 `mysql`/`pg` 时 session 自动转 durable。*单租户裸 boot 缺省 `local`;`REQUIRE_PRINCIPAL=true` 的多租户裸 boot 缺省 `memory`(local 与多租户互斥) |
|
package/USAGE.md
CHANGED
|
@@ -61,7 +61,16 @@ ANTHROPIC_API_KEY=sk-ant-… # 可选:ANTHROPIC_BASE_URL / ANTHROPIC_VERSION /
|
|
|
61
61
|
(码 `config.model_route_endpoint_missing`)点名解析不到的模型并给出本机写法 `MODEL_GATEWAY_BASEURL=http://127.0.0.1:8000/v1`。
|
|
62
62
|
只设 `MODEL_GATEWAY_FALLBACK_URLS` 不设主网关同拒(`config.gateway_fallback_without_primary`)。只走 Anthropic 路由的部署
|
|
63
63
|
不设网关合法;此时 `MODEL_API_KEY`(网关的凭据)不装载、不发往任何路由(启动通知 `model_gateway_key_unpaired`)。
|
|
64
|
-
中心 / config.d 下发的候选目录里有解析不到的条目 ⇒
|
|
64
|
+
中心 / config.d 下发的候选目录里有解析不到的条目 ⇒ 候选的模型面这一组被拒、上一代模型面继续服务(同候选其余组照落)。
|
|
65
|
+
- 🔴 **目录里的 anthropic 条目要本机真有 anthropic 路由**(S-704,7.101.0 起):`provider:"anthropic"` 的目录条目只在本服务配了
|
|
66
|
+
`ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN` 时可用;没配 ⇒ 该条目**不进可选目录**(`GET /v1/models` 不列、不当缺省,缺省顺延到下一条
|
|
67
|
+
可用条目),启动 / 刷新日志一行 `sema_registry_model_no_brain`(带码 `config.model_no_brain` 与出路),显式点它(`body.model` /
|
|
68
|
+
`@名` / `settings.model` / `compactionModel` / side-query)⇒ 400 `request.model_unavailable`。**7.100.0 及以前**这种条目会落到网关
|
|
69
|
+
brain、带着**它自己的** `apiKeyEnv` 钥匙打到网关。不合 settings-schema 形的条目同律(码 `config.model_entry_invalid`,如非 http(s)
|
|
70
|
+
的 `baseUrl` —— 此前被「退回部署网关」;读腿逐条丢掉的条目同样留名字,点它照样 400);目录里**每条**都不可用 ⇒ 那一代模型面这一组被拒。
|
|
71
|
+
🔴 **被拒条目的名字不许被档位 / 别名铸到别的条目上**(S-712):不可用条目恰好叫 `sonnet` / `opus` / `pro` / `best` 这类档位词或 CC 别名、
|
|
72
|
+
而档位表会铸出这个词时,这份候选的模型面整组被拒(码 `config.model_refused_name_minted`,warn `sema_registry_model_plane_refused` 点名
|
|
73
|
+
「被拒名 → 目标条目」)—— 否则显式点它、或绑它的角色,会静默跑在另一条条目的端点与账号上;出路:改名 / 修好条目,或改绑那条档位绑定。
|
|
65
74
|
- **failover ≠ Anthropic↔vLLM**:failover 给所有 brain 发**同一个 model**(同协议同 id 的冗余);云↔本地是**按 `model.provider` 路由**的选择,不是故障转移(两者 model id/参数不同,不能透明互切)。设 `MODEL_PROVIDER=anthropic` + `MODEL_ID=claude-…` 让整个服务走 Anthropic。
|
|
66
75
|
- **缺省推断**:`MODEL_PROVIDER` **未设**、但显式配了 Anthropic 协议 base URL(`ANTHROPIC_BASE_URL`)**和**对应凭证(`ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN`)时,缺省自动判 `anthropic` 并打一行启动 warn(`model_provider_inferred`)——纯净机直连 Anthropic 兼容上游不再需要显式第七个键。显式 `MODEL_PROVIDER` 恒赢;只给 base URL 或只给凭证不推断;两者皆无 = `gateway`,与以前逐字节一致。
|
|
67
76
|
- 启动日志的 `brain` 字段会回显当前组合(`failover` / `anthropicRoute` / `anthropicCacheBreakpoints`)。
|
|
@@ -110,10 +119,10 @@ WEB_SEARCH_PROBE_ON_BOOT=true # boot 期一次性真出网探活,
|
|
|
110
119
|
|---|---|---|---|---|
|
|
111
120
|
| `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` |
|
|
112
121
|
| `WEB_SEARCH_API_KEY` | secret string | 无 | brave/tavily:后端仍会装配(装配只看 `PROVIDER`),**首次真实工具调用**时抛 `WEB_SEARCH_API_KEY is required for the <provider> provider`(`braveSearch` / `tavilySearch` 的首行守卫);searxng:本键无消费点 | 空串 / 纯空白同缺席;内容不设形(它是凭据) |
|
|
113
|
-
| `WEB_SEARCH_ENDPOINT` | url string(brave 下 = 完整搜索 URL,可指向 Brave 兼容网关如 Nólë) | brave/tavily → 各自官方 API;searxng → 无默认 | brave/tavily:落官方 endpoint;searxng:**首次真实工具调用**时抛 `WEB_SEARCH_ENDPOINT (the SearXNG instance URL) is required for the searxng provider`(`searxngSearch` 的首行守卫) | 空串同缺席(compose 模板 `${WEB_SEARCH_ENDPOINT:-}` 铸的就是空串)。**不是 `scheme://host[:port][/path]` 形的绝对 URL ⇒ 拒启**(`searx.internal`、`localhost:8888` 这类少了 `scheme://` 的形都算;判据与模型路由 base URL 拒启同一只)。拒启句带键名与机读码 `config.web_search_invalid`,**不回显值**(这一格可能带凭据) |
|
|
122
|
+
| `WEB_SEARCH_ENDPOINT` | url string(brave 下 = 完整搜索 URL,可指向 Brave 兼容网关如 Nólë) | brave/tavily → 各自官方 API;searxng → 无默认 | brave/tavily:落官方 endpoint;searxng:**首次真实工具调用**时抛 `WEB_SEARCH_ENDPOINT (the SearXNG instance URL) is required for the searxng provider`(`searxngSearch` 的首行守卫) | 空串同缺席(compose 模板 `${WEB_SEARCH_ENDPOINT:-}` 铸的就是空串)。**不是 `scheme://host[:port][/path]` 形的绝对 URL ⇒ 拒启**(`searx.internal`、`localhost:8888` 这类少了 `scheme://` 的形都算;判据与模型路由 base URL 拒启同一只)。**带 userinfo ⇒ 拒启**(7.101.0 起,S-716:`https://user:pw@host/…`、只带用户名的 `https://user@host/…` 都算;判据 = WHATWG 解析后用户名或口令任一非空,与运行时 fetch 拒收含凭据 URL 的约束同一条,所以修前这一形起得来、每次搜索必抛;brave / tavily 的凭据走 `WEB_SEARCH_API_KEY`,searxng 腿没有凭据通道,带鉴权的实例需在前面放一层注入鉴权的反代)。拒启句带键名与机读码 `config.web_search_invalid`,**不回显值**(这一格可能带凭据) |
|
|
114
123
|
| `WEB_SEARCH_MAX_RESULTS` | number | `10` | 用默认 10 | 空串同缺席;**非数字或 < 1 ⇒ 拒启**(`config.web_search_invalid`;改前静默落默认 10);是数则向下取整,再夹到 `[1,20]`(设 999 被夹到 20 —— 夹紧不是形错) |
|
|
115
124
|
| `WEB_SEARCH_TIMEOUT_MS` | number(ms) | `10000` | 用默认 10000 | 空串同缺席;**非数字(`"30s"` 这类带单位的也算)或 < 1 ⇒ 拒启**(`config.web_search_invalid`;改前静默落默认 10000 —— 写了 30s 的运维实际拿到 10s);是数则向下取整,再抬到 ≥ 1000ms |
|
|
116
|
-
| `WEB_SEARCH_SEARXNG_PARAMS` | string(`k=v;k=v`) | 无(不铸键 = 不传 `extraParams`,交给 core adapter 自己的缺省) | 键整个不铸 | 空串 / 纯空白 / 只有分号 = 缺席。文法与每请求 `settings.webSearch.searxngParams` 同一套:`name=value` 用 `;` 分隔(尾分号可有可无),名是参数名(字母 / 数字 / `_` / `-` / `.`),值非空,同名只许一次。**任一段不成形 ⇒ 拒启**(`config.web_search_invalid
|
|
125
|
+
| `WEB_SEARCH_SEARXNG_PARAMS` | string(`k=v;k=v`) | 无(不铸键 = 不传 `extraParams`,交给 core adapter 自己的缺省) | 键整个不铸 | 空串 / 纯空白 / 只有分号 = 缺席。文法与每请求 `settings.webSearch.searxngParams` 同一套:`name=value` 用 `;` 分隔(尾分号可有可无),名是参数名(字母 / 数字 / `_` / `-` / `.`),值非空,同名只许一次。**任一段不成形 ⇒ 拒启**(`config.web_search_invalid`,句里只报第几项 `entry #N`,**不回显名或值** —— 7.101.0 起,S-716;改前句里回显参数名):把数组或对象的 JSON 文本(`["engines=bing"]`、`{"engines":"bing"}`)填进来、一段没有 `=`、名为空、同名两次。改前这些被静默跳过或吸成垃圾参数发给实例 |
|
|
117
126
|
| `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`) |
|
|
118
127
|
|
|
119
128
|
**结论:缺席与形错分两件事。** 缺席(未设 / 空串,以及 `WEB_SEARCH_PROVIDER` 不是三词之一)一律走「不装配 / 用默认」,
|
|
@@ -281,7 +290,7 @@ OTEL_EXPORTER_OTLP_HEADERS=authorization=Bearer xxx # 逗号分隔的 k=v
|
|
|
281
290
|
# 便宜模型分层(同网关、换 id):council 的 6 个 lens / 上下文压缩走便宜档,主任务与仲裁走主模型。
|
|
282
291
|
MODEL_ID=<your-model-name> MODEL_CHEAP_ID=<your-cheap-model-name>
|
|
283
292
|
```
|
|
284
|
-
- 调用方可在 `objective` 里 `@<模型名>` 选模型,**只认已配置的名单**(`MODEL_ID` / `MODEL_CHEAP_ID
|
|
293
|
+
- 调用方可在 `objective` 里 `@<模型名>` 选模型,**只认已配置的名单**(`MODEL_ID` / `MODEL_CHEAP_ID`,或目录条目)——注入不了 `baseUrl`/`apiKey`;无 `@` → 默认模型;`@` 点中一条已发布但本部署不可用的目录条目 ⇒ 400 `request.model_unavailable`(S-704)。
|
|
285
294
|
- **`GET /v1/models`**(带 Bearer)→ `{models:[{name,id,provider,reasoning,vision}],default}`,只含名字/能力、**不含密钥** —— 消费方(如 OA)拿它做 `@` 自动补全下拉的数据源。
|
|
286
295
|
- 不设 `MODEL_CHEAP_ID` → 所有角色 = 主模型(行为不变)。
|
|
287
296
|
- **1M 双窗**(`autoCompactTokens`):`967000` 只发给 **claude-sonnet-5 系 id ∧ 窗 ≥1M**(CC 的 per-model 值,不是窗宽几何)。其他 ≥1M 模型要双窗触发点请显式配 —— 主槽 `MODEL_AUTO_COMPACT_TOKENS=<tokens>`、cheap 槽 `MODEL_CHEAP_AUTO_COMPACT_TOKENS=<tokens>`(**两槽各配各的**:主槽的值按主模型 id/窗声明,不会外溢到 cheap)。不配 → core 按整窗的 `W-33000` 平几何触发,boot 期发 `auto_compact_window_not_derived_1m` warn 点名槽位(`slot=main|cheap`)。⚠️ cheap 槽不设 `MODEL_CHEAP_CONTEXT_WINDOW` 时**继承主模型的窗**,主模型是 1M 则 cheap 也按 1M 计。取值范围:**必须低于该槽的物理窗**(CC 的形是 `窗-33000`)—— 高于物理窗时 **core 在取数处把触发窗夹回物理窗**(`resolveTriggerWindow`,自 core 5.60.0 起),即**这根旋钮变成 no-op**、几何回落成没配它的样子,并由引擎逐任务铸一条 `config.autocompact_window_clamped` 通告(受众 operator)。server 侧**不夹也不拒**(夹紧归 core 的取数处,一处一次),但会在 **boot** 就发 `auto_compact_tokens_above_context_window` warn 点名槽位、越窗值与 **`clampedTo`(core 会夹到的那个数)**—— 不必先派一条任务才知道旋钮白配了。⚠️ 7.57.0 之前这一段写的是「autocompact 事实上关掉、改由破坏性 guard 砍历史」,那是 core 5.60.0 之前的行为,已订正。
|
|
@@ -514,6 +523,13 @@ FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启 flee
|
|
|
514
523
|
# (此前静默每拍 announce 400,worker 永不注册)。
|
|
515
524
|
```
|
|
516
525
|
- 中心**空/未发布** → `applyEffective` 回落 env + 内建 teams 并 warn `config_center_unpublished`,**不影响在跑的服务**(接了也安全)。
|
|
526
|
+
- **逐条容错**(S-704 ②,7.101.0 起):目录里一条条目不合形(如 `contextWindow:"abc"`)⇒ **只丢那一条**(warn `config_entry_dropped`,
|
|
527
|
+
点名坐标 `models.<下标>` 与字段诊断),其余条目照用;refresh 拍 / 迟到的 boot 拉取**不再因此整拒候选**(此前一条坏条目让整份目录停在
|
|
528
|
+
上一代 —— 上一代若还是 env,env 模型就一直服务)。颗粒更粗的失效(整个域读不懂、拼错键、坏文件)仍整拒候选、保 LKG。
|
|
529
|
+
- **没有 `MODEL_ID` 也能起**(S-705,7.101.0 起):目录(`config.d/models.json` 或中心)有可用条目时 env 可以不写 `MODEL_ID` —— 缺省模型取
|
|
530
|
+
目录缺省。判据在目录应用**之后**:env `MODEL_ID` 或目录缺省任一在场即过;两者皆无 ⇒ 拒启(码 `config.default_model_missing`,指路二选一)。
|
|
531
|
+
中心拉取超出 `CONFIG_BOOT_FETCH_BUDGET_MS` 又没有 LKG 时,boot 那一刻没有目录 ⇒ 没有 `MODEL_ID` 兜底就拒启(调大预算让 boot 等目录,
|
|
532
|
+
或写一个 `MODEL_ID` 兜底)。
|
|
517
533
|
- **灰度姿势**(配置控制面 AI 建议):先 `SEMA_REGISTRY_DRY_RUN=true` 起一轮,看日志 `sema_registry_dry_run`(中心给的 models/roles/teams + 会否覆盖 default、per-model apiKeyEnv)对得上 env 再去掉该 flag 真正 apply。
|
|
518
534
|
- 拉取**只读、只取逻辑配置**(模型名册/角色/团队);密钥/网关仍在本服务 env(中心只发 env-**名** 引用,不发密钥值)。回滚=去掉 `SEMA_REGISTRY_URL` 即纯 env。
|
|
519
535
|
- **配置热更新真表(7.38+ 现行)**——refresh 拍(60s 轮询,或 `POST /v1/admin/config/refresh` 手动触发,见 API 表)对各域的生效方式:
|
|
@@ -529,6 +545,7 @@ FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启 flee
|
|
|
529
545
|
| 记忆整理驱动席(仅 `MEMORY_CONSOLIDATION_DRIVER=on`) | 重启生效 | 整理跑用的模型在启动期解析一次;中心改了 roles / 档位 / 该条目录后 `/health` 报 `restartRequired`,`restart.reasons` 含 `memory-consolidation`(7.100.0 新理由;此前不报) |
|
|
530
546
|
|
|
531
547
|
生效与否的观测口=`/health` 世代账键(`configTargetVersion`/`configAppliedVersion`+两 ordinal+`configApplyStaleMs`,见 API 表 `/health` 行)——target≠applied 持续=「改了没生效」的机读信号。
|
|
548
|
+
- **`run-local`(一次性本机 CLI)上三张审批名单同样执法**(7.101.0 起,TTY 问 / 无 TTY 拒):`APPROVAL_DENY` 当场拒;`APPROVAL_REQUIRE` / `APPROVAL_NEVER_AUTO` 命中 ⇒ stdin 是 TTY 就 `y/N` 问人,否则当场拒并在 stderr 点名三张名单键;`APPROVAL_AUTO_BUDGET` 照常生效(never-auto 不吃预算);TTY 问句带上这次调用的参数,拒答而停下的 run 退 1。这条腿没有 checkpoint 店也**不**拒启 —— 它有一只当场作答的席,名单的 ask 不需要 park(上表「无 checkpoint 店 ⇒ 整组拒」只说服务腿)。此前三张名单在这条腿上无声不生效。
|
|
532
549
|
- **仅 `/v1/tasks`(同步)+ `/v1/runs`(异步)**——`/v1/tasks/stream` 不支持(多次尝试非单流,400)。与 `verify` **互斥**(同时给 → 400)。
|
|
533
550
|
- 成本上界 = 任务的 `maxCostUsd`(防冷重跑税)。**三条 core 警示**:① 每档**冷重跑**重付输入成本(便宜档常过才划算);② 门收到**未脱敏**输出(自定义门转发外部 verifier 要脱敏);③ **写工具会跑 N 次**——**只用于只读/幂等任务**(每次升级整任务重跑)。开放式任务(找全 bug/文笔)没有可判定 oracle、级联会空转,那种用 `discuss`(广度交叉)而非级联(深度阶梯)。
|
|
534
551
|
|
|
@@ -680,6 +697,9 @@ UNATTENDED_APPROVAL_POLICY=park
|
|
|
680
697
|
「用户不想继续,停下来等他」,run 以 `haltedOnUserRejection` 收束 —— 而这台部署自己声明了没人会来。
|
|
681
698
|
少数情形记的是 `approval_window_expired`,同样不在人名下:活卡已送达、本服务自己的窗走完没人点,**且**没有持久 ask 店(或 ask 店
|
|
682
699
|
当时报错、结束等待的只能说是本服务的窗)。有 ask 店时窗走完、终局 claim 赢下的那一支记的是 `policy_refused`(行上同落 `settled_by`)。
|
|
700
|
+
店抖动兜底(持久行不可用 / 落行身份不定)与崩溃恢复(收敛器代打崩溃遗留的过期 ask)同判(7.101.0 起):同样记
|
|
701
|
+
`policy_refused`,行上同落政策结算(证不出是本次插下的行不当场动,由收敛器在窗过 + 宽限后落),同一只 ask 重入
|
|
702
|
+
回放同一个政策拒(此前这三形分别是 durable park、durable park、重入记成 `human_refused`)。
|
|
683
703
|
- **7.34.0 的行为变化(park 侧)**:此前「窗走完没人答」是**当场拒绝 + 一条 error 回模型**(模型往往就
|
|
684
704
|
绕开了那次治理);现在它与其余「无人可答」的情形一样走 park。要恢复旧结局请显式配 `deny`。
|
|
685
705
|
- 纯部署级:**不看**客户端的任何表态/权限模式。协调器整体关掉(`TOOL_APPROVAL_ENABLED` 关)时这个旋钮
|
|
@@ -1057,6 +1077,56 @@ invalid_using_default` 警告 + 退回缺省值」这条臂随词表放宽一并
|
|
|
1057
1077
|
🪦 `LSP_ENABLED=false`(拆分前唯一的 host 腿逃生舱)自 3.0.0 起是墓碑:拒启并指路 `LSP_HOST_ENABLED`。
|
|
1058
1078
|
`LSP_ENABLED=true` 不受影响——那是沙箱腿自己的 opt-in,语义没变。
|
|
1059
1079
|
|
|
1080
|
+
**host 腿语言服务器拿到的环境 + 点名放行 `LSP_ENV_ALLOW`(7.101.0 起)**:host 腿的语言服务器是本机子进程,它拿到的环境
|
|
1081
|
+
= 服务进程环境**剥掉凭据形的键**(与模型可驱动的 host shell 同一只剥除:配置目录 `secret` 型键按名剥 ∘ 引擎按键名形状剥
|
|
1082
|
+
`*_KEY` / `*_TOKEN` / `*_SECRET` / `*_PASSWORD` …),`PATH` / `HOME` / locale 这类照常在。代价是语言服务器自己要用的令牌也被
|
|
1083
|
+
剥:`.npmrc` 里 `${NPM_TOKEN}` 插值的私有 registry(TS 自动装类型)、rust-analyzer `cargo metadata` 走私有 registry 的
|
|
1084
|
+
`CARGO_REGISTRIES_<名>_TOKEN` —— 症状是补全 / 诊断静默变差。剥了哪些键,起服时一行 `warn` 告诉你(见下),出路是点名放行:
|
|
1085
|
+
|
|
1086
|
+
| 键 | 缺省 | 写法 | 行为 |
|
|
1087
|
+
|---|---|---|---|
|
|
1088
|
+
| `LSP_ENV_ALLOW` | 未设 = 空名单 = 全剥(7.100.0 行为) | 逗号分隔的**裸变量名**,如 `LSP_ENV_ALLOW=NPM_TOKEN,CARGO_REGISTRIES_MY_REG_TOKEN` | 点到的键从服务进程环境**原样**交给语言服务器(密钥形也照交 —— 点名即你显式承担把这个值交给仓里代码的后果);没点到的照旧剥,`SERVICE_AUTH_TOKENS` 这类不会因为别的键被放行而跟着进去。空串 = 未设。**形错拒启**:空段(`A,,B`、尾逗号)、非变量名形(`1BAD`、带空格、`NAME=value` 整段贴进来)⇒ 起服失败,拒句点名键与第几段,**不回显原值**。形对但本进程环境里**没有**这个名的段 ⇒ 不放行,起服一行 `config_env_entry_rejected` 只报第几段(常见成因是 `LSP_ENV_ALLOW=$NPM_TOKEN` 这种把**值**展开进来的手滑 —— `npm_` 令牌恰好是合法变量名形,所以名单原文在任何日志 / 配置目录回显里都不出现)。旋钮**原文**按凭据类对待:配置目录只回在场位,`LSP_ENV_ALLOW` 这个键本身不进语言服务器 / host shell / git 子进程(点名它自己,或点名两只沙箱名单 `E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` ⇒ 拒启);生效名单看起服那行的 `allowed`。`REQUIRE_PRINCIPAL=true`(多租户 / 托管)⇒ 忽略名单、仍全剥,起服一行 `config_env_set_but_ineffective`(host 腿语言服务器在这一姿态下本来就不起)。只作用于 host 腿;沙箱腿的语言服务器跑在沙箱里,环境由沙箱车道自己的旋钮决定 |
|
|
1089
|
+
|
|
1090
|
+
**「剥了什么」这一行**:起服(host 腿语言服务器管理器构造时)一行 `warn` `secret_env_scrubbed`,`site:"server.lsp.spawn-env"`,
|
|
1091
|
+
字段 `keys`(这次剥掉的键名)· `allowed`(当前放行名单)· `hint`(出路句,点名 `LSP_ENV_ALLOW=NAME,NAME`)· `summary`;**值永不进日志**。
|
|
1092
|
+
放回的键不算剥除(不在 `keys` 里、不进计数 `secret_env_scrubbed_total`);同一个键每进程只报一次;没有剥除 ⇒ 不发。
|
|
1093
|
+
另两条剥除现场(模型可驱动的 host shell、project-memory 的 git 子进程)没有部署级放行旋钮,它们的同名行照旧是 `info`、不带 `allowed` / `hint`。
|
|
1094
|
+
|
|
1095
|
+
**形状规则的精确名豁免(7.101.0 起,core 7.31.0 #1081)**:十个预算名(`MAX_NEW_TOKENS` `MAX_COMPLETION_TOKENS` `AIDER_MAP_TOKENS`
|
|
1096
|
+
`AIDER_MAX_CHAT_HISTORY_TOKENS` `MAX_BATCH_PREFILL_TOKENS` `MAX_WAITING_TOKENS` `MAX_TOP_N_TOKENS` `RATE_LIMIT_TOKENS` `PROMPT_TOKENS`
|
|
1097
|
+
`CONTEXT_TOKENS`)与两个开关(`DB_FOREIGN_KEYS` `JSON_SORT_KEYS`)不再被剥,三条子进程腿同时生效(7.100.0 上这五个 7.30.1 误剥的名
|
|
1098
|
+
`MAX_NEW_TOKENS` / `MAX_COMPLETION_TOKENS` / `MAX_BATCH_PREFILL_TOKENS` / `DB_FOREIGN_KEYS` / `JSON_SORT_KEYS` 需要点名放行,现在不用)。
|
|
1099
|
+
**精确名,不是形**:`USERS_MAX_NEW_TOKENS`、`MAX_NEW_TOKENS_2` 这类装饰过的变体照剥。引擎自己的「剥了什么」通告码
|
|
1100
|
+
`config.secret_env_scrubbed` 只在引擎**自己**剥的子进程上发;本服务三条腿都交本服务剥好的环境,所以运维面看的是上面这行
|
|
1101
|
+
`secret_env_scrubbed`,不是那只通告码。
|
|
1102
|
+
|
|
1103
|
+
**「点名放行」只有一种意思**:本服务里凡是「一张名单点到哪些环境变量键」的地方(这里的 `LSP_ENV_ALLOW`、沙箱车道的
|
|
1104
|
+
`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV`(见下表),与嵌入方构造 host shell 时的 `inheritEnv:[names]`)用的是同一只谓词 ——
|
|
1105
|
+
点到的键从服务进程环境原样交出去(空串值照交),密钥形也照交;源里没有的名什么也不产生。三只名单型旋钮(`LSP_ENV_ALLOW` /
|
|
1106
|
+
`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV`)另走同一只名单解析:形错拒启点名第几段、不回显原值,点名任一只名单型旋钮拒启,
|
|
1107
|
+
本进程环境里没有的名按序号一行 `config_env_entry_rejected`,旋钮原文按凭据类对待(配置目录只回在场位、不进任何子进程)。
|
|
1108
|
+
几处只在**没点到的键**上不同:语言服务器在缺省剥除的结果之上放回点名键(没点到的照剥,非密钥键照常在);host shell 的
|
|
1109
|
+
`inheritEnv:[names]` 与两只沙箱名单是 hermetic 的(没点到的一律不交,与引擎 `NodeExecutionEnv` 同名选项同义)。
|
|
1110
|
+
两处都不向对方看齐:host shell 改成「剥后放回」等于在模型可驱动的 shell 上放宽,语言服务器改成 hermetic 会让它当场找不到自己的工具链。
|
|
1111
|
+
|
|
1112
|
+
**沙箱带外 env 名单(`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV`,7.101.0 / S-713 起同上一段的解析与谓词)**:沙箱里的工具要用本服务
|
|
1113
|
+
进程环境里的凭据(如 git 走 `$GIT_TOKEN`),又不想让它进提示词、命令串或事件日志,就把**名字**写进名单,值只在服务进程环境里:
|
|
1114
|
+
|
|
1115
|
+
| 键 | 车道 | 写法 | 行为 |
|
|
1116
|
+
|---|---|---|---|
|
|
1117
|
+
| `E2B_SANDBOX_ENV` | `REMOTE_EXEC=e2b` | 逗号分隔的**裸变量名**,如 `E2B_SANDBOX_ENV=GIT_TOKEN,NPM_TOKEN` | 点到的键从服务进程环境**原样**注入沙箱(e2b 沙箱级 env;与 `SANDBOX_PKG_SOURCE` 推导出的包源变量同名时名单赢) |
|
|
1118
|
+
| `DOCKER_SANDBOX_ENV` | `REMOTE_EXEC=local-docker` | 同上 | 点到的键以 `docker exec -e K=V` 注入每条命令(与单条命令自带的 env 同名时,命令自带的赢) |
|
|
1119
|
+
|
|
1120
|
+
两只旋钮只在**自己的车道被选中时**才求值(别的车道上它们是无关配置,不校验、不告警)。未设 / 空串 = 不注入。**形错拒启**:空段
|
|
1121
|
+
(`A,,B`、尾逗号)、非变量名形(`1BAD`、带空格、`NAME=value` 整段贴进来)、点名名单型旋钮(`E2B_SANDBOX_ENV=E2B_SANDBOX_ENV`,
|
|
1122
|
+
或 `LSP_ENV_ALLOW` / 另一只沙箱名单)⇒ 起服失败,拒句点名键与第几段,**不回显原值**。形对但服务进程环境里**没有**这个名 ⇒ 不注入,
|
|
1123
|
+
起服一行 `config_env_entry_rejected` 只报第几段。点到的键值为空串 ⇒ 照样注入一个空串(7.101.0 前跳过,见 CHANGELOG)。
|
|
1124
|
+
`REQUIRE_PRINCIPAL=true` 下照常注入(与 `LSP_ENV_ALLOW` 不同:沙箱 env 是部署方给隔离沙箱显式配的)。
|
|
1125
|
+
**起服可见面(7.101.0 / S-715)**:两条车道都在 `remote_exec_enabled` 行带 `sandboxEnvKeys` —— 名单点到、且服务进程环境里真有(因而真被转发)的键名,字典序,只键名不带值
|
|
1126
|
+
(名单没配或点到的名全不在 ⇒ 不带该字段;e2b 腿不含 `SANDBOX_PKG_SOURCE` 推导出的包源变量);`REQUIRE_PRINCIPAL=true` 下另有一行 warn
|
|
1127
|
+
`config_env_forwarded_multi_tenant`(`env` = 旋钮名、`entries` = 名单段数、`note` = 姿态句:这张 worker 级名单进每个租户的沙箱),
|
|
1128
|
+
不回显名单原文;单用户部署不出这一行。
|
|
1129
|
+
|
|
1060
1130
|
**`CONFIG_REQUIRE_ROSTER=true`(3.1.0,可选,缺省关)**:远程 registry 非 dryRun 车道的 fleet worker
|
|
1061
1131
|
可布防「roster 落地前拒接计费提交(503)」——3.0.0 起 MODEL_ID 必填,每个 worker 都带 boot 模型起服,
|
|
1062
1132
|
旧的「无 env 模型即等 roster」推断失效;要那个保护语义现在需显式声明。
|
|
@@ -1079,7 +1149,7 @@ invalid_using_default` 警告 + 退回缺省值」这条臂随词表放宽一并
|
|
|
1079
1149
|
|
|
1080
1150
|
> ⚠️ **Bearer 是"每个请求",不只是 POST。** 配了 `SERVICE_AUTH_TOKEN` 后,**`GET /v1/runs/:id`、`GET /v1/runs/:id/events`(SSE)、`/metrics`** 等所有非 `/health` 路由都要带 `Authorization: Bearer`——漏带一律 `401`。下面示例为简洁**省略了 Bearer**,真实调用请逐个补上。
|
|
1081
1151
|
|
|
1082
|
-
**请求体字段(
|
|
1152
|
+
**请求体字段(常用;受理**闭集**以 `docs/ASSISTANT-WIRE-CONTRACT.md` §1 为准 —— 本表不再重抄一份,S-710):**
|
|
1083
1153
|
| 字段 | 必填 | 说明 |
|
|
1084
1154
|
|---|---|---|
|
|
1085
1155
|
| `objective` | ✅ | 要做什么(自然语言)。**唯一必填。** |
|
|
@@ -1088,7 +1158,8 @@ invalid_using_default` 警告 + 退回缺省值」这条臂随词表放宽一并
|
|
|
1088
1158
|
| `attachmentIds` | — | D-1 通用文件上传(1.289+):先 `POST /v1/attachments?name=…`(raw body,content-type=mime)拿句柄,提交时引用 ≤16 个;文件物化到执行环境工作目录 `attachments/` 下,objective 尾部自动追加文件清单(内容不进会话流)。单文件缺省 ≤32 MiB(`ATTACHMENT_MAX_BYTES`);可配 mime 白名单(`ATTACHMENT_MIME_ALLOWLIST` CSV,缺省不限);上传后未引用的按 `ATTACHMENT_UNBOUND_TTL_MS`(缺省 24h)回收。**云形态(tidb/pg)字节本体存对象存储——MinIO 必配**(`MINIO_ENDPOINT/MINIO_ACCESS_KEY/MINIO_SECRET_KEY`,与快照 lane 同一组变量),未配则附件面 501;local 形走本地文件店。 |
|
|
1089
1159
|
| `scenario` | — | 省略时取本部署的 `DEFAULT_SCENARIO`(工厂缺省场景名 = **`code`**——CC 编码 persona 蒸馏版,default 全量工具面,终验旋钮独立;`default` 场景**已非出厂缺省**,要用它须显式传 `scenario:"default"`,或部署方显式设 `DEFAULT_SCENARIO=default`)。可选内建名:`default`(通用/助理场景,域中性 persona,CC agent loop 全量工具面)/ `code-review`(见 §5)/ `scan`(同 §5 的 repo 只读工具但**中性无框架提示词**——objective+中心下发 skill 全权主导输出,OA 扫描类用;与 `code-review`/`discuss` 同属 clone-free 无执行环境场景,见 §5 末)/ **配置控制面可声明任意新场景**(`{name, toolset: none\|repo-readonly, prompt?}`,组合即配置、能力写死在部署;restart-to-apply;center 可覆盖内建名,boot 日志 `config_center_scenarios.shadowsBuiltin` 可审计) |
|
|
1090
1160
|
| `repo` / `council` / `debate` | — | `repo` 为 `code-review`/`scan` 必填;`council`/`debate` 仅 `code-review`,见 §5 |
|
|
1091
|
-
|
|
|
1161
|
+
| `systemPrompt` / `appendSystemPrompt` | — | 调用方自带的系统提示 / 追加段(各 ≤16384 字符,超限 400 `request.field_invalid`;`appendSystemPrompt` 必须非空串)—— 任务自带时优先于角色 / 全局缺省(见 §模型角色) |
|
|
1162
|
+
| ~~model / tools~~ | 🚫 | **不接受**——服务端注入(模型走目录 / 角色,工具走场景装配;按请求只能**减**:`excludeTools` / `settings.permissions`) |
|
|
1092
1163
|
|
|
1093
1164
|
**`TaskResult`(同步 / `done` 事件里拿到的;7.64.0 起终局是**一条** `terminal` 因由,不再有 `status`/`errorCode`/`errorMessage`/`blockedReason` 四个平面键):**
|
|
1094
1165
|
```json
|
|
@@ -1716,10 +1787,10 @@ TOC 本地形)时被**采纳**;`REQUIRE_PRINCIPAL=true`(多租户)上一段**形
|
|
|
1716
1787
|
|
|
1717
1788
|
| 键 | 类型 | 必填 | 默认 | 形错(⇒ 400) |
|
|
1718
1789
|
|---|---|---|---|---|
|
|
1719
|
-
| `provider` | `"brave"|"tavily"|"searxng"` | **是** | 无 | 缺席、不是字符串、或不是三词之一(大小写 / 首尾空白照旧归一)——一段 `settings.webSearch` 就是在点名目的地,认不出就 400 点名 `settings.webSearch.provider
|
|
1790
|
+
| `provider` | `"brave"|"tavily"|"searxng"` | **是** | 无 | 缺席、不是字符串、或不是三词之一(大小写 / 首尾空白照旧归一)——一段 `settings.webSearch` 就是在点名目的地,认不出就 400 点名 `settings.webSearch.provider`(括号里只说哪一形:`it is missing` / `it is not a string` / `it is not one of them`,**不回显**收到的串 —— 7.101.0 起,S-716),**不**静默换成部署 env 的 backend(改前:整段丢弃、回落 env backend、不报错)。词表守卫与 env 腿**同一只** `isWebSearchProvider`;env 腿词表外是「不装配」(部署自己的缺席) |
|
|
1720
1791
|
| `apiKey` | string(明文) | brave/tavily 建议带(缺了首次调用才报错,见下表);searxng 不读 | 无(不继承 env 的 `WEB_SEARCH_API_KEY`);空串同缺席 | 不是字符串 |
|
|
1721
|
-
| `endpoint` | string | searxng 必需(缺了首次调用才报错);brave/tavily 可选覆盖 | 无(不继承 env 的 `WEB_SEARCH_ENDPOINT`);空串同缺席 | 不是字符串,或不是 `scheme://host[:port][/path]` 形的绝对 URL(`localhost:8888` 这类少了 `scheme://` 的也算) |
|
|
1722
|
-
| `searxngParams` | `Record<string,string>`(对象)或 `"name=value;name=value"`(字符串) | 否 | 无;空串 / 空对象同缺席 | 与 env `WEB_SEARCH_SEARXNG_PARAMS` 同一套文法:数组、数字、一段不是 `name=value`、名不是参数名(字母 / 数字 / `_` / `-` / `.`)
|
|
1792
|
+
| `endpoint` | string | searxng 必需(缺了首次调用才报错);brave/tavily 可选覆盖 | 无(不继承 env 的 `WEB_SEARCH_ENDPOINT`);空串同缺席 | 不是字符串,或不是 `scheme://host[:port][/path]` 形的绝对 URL(`localhost:8888` 这类少了 `scheme://` 的也算),或带 userinfo(`user:pw@` / 只带 `user@`;7.101.0 起,S-716,句 `settings.webSearch.endpoint must not carry userinfo (user or user:password before the host)`,不回显值) |
|
|
1793
|
+
| `searxngParams` | `Record<string,string>`(对象)或 `"name=value;name=value"`(字符串) | 否 | 无;空串 / 空对象同缺席 | 与 env `WEB_SEARCH_SEARXNG_PARAMS` 同一套文法:数组、数字、一段不是 `name=value`、名不是参数名(字母 / 数字 / `_` / `-` / `.`)、值为空或不是字符串、同名两次(句里只报第几项 `entry #N`,不回显名或值 —— 7.101.0 起,S-716)。⚠️ **未列入已发布的 `@sema-agent/sdk` 8.8.0 `SettingsWebSearch` 类型**(该接口只有 `provider`/`apiKey`/`endpoint`/`maxResults` 四键、无开放下标)——server 侧代码认这个键,但当前发布的 TS 类型接不到它;手写 JSON 请求体仍可以发,server 会照常解析(源码头注自述:字段和消费点先落地,配置录入面尚未跟上,是一笔尚未还清的既有债务) |
|
|
1723
1794
|
| `maxResults` | number | 否 | 10(与 env 同一 clamp,`[1,20]`,小数向下取整;999 夹到 20 不是形错) | 不是数,或 < 1(`"10"` 这种字符串也算) |
|
|
1724
1795
|
| *(无)* `timeoutMs` | — | — | — | **per-request 车道没有这个字段**——即便部署用 `WEB_SEARCH_TIMEOUT_MS` 配了非默认超时,per-request 生效那次调用永远退回 backend 自己的默认(10000ms,下限 1000ms),因为"整段整取"意味着 env 的 `timeoutMs` 根本不在 `reqWebSearch` 那个新对象里 |
|
|
1725
1796
|
| *(无)* `fetchImpl` | — | — | — | 同上,仅测试注入用,per-request 车道不可达 |
|
package/dist/approval-card.d.ts
CHANGED
|
@@ -380,15 +380,24 @@ export declare function readRuleOffersAbsenceSeat(x: unknown): RuleOffersAbsence
|
|
|
380
380
|
*/
|
|
381
381
|
export declare function readRuleOffersAbsence(req: unknown): RuleOffersAbsence | undefined;
|
|
382
382
|
/**
|
|
383
|
-
* park
|
|
384
|
-
* `
|
|
383
|
+
* park 行上两只 **`?: true` 孪生座**的名(core d.ts:「present (`true`) ⇔ … Absent otherwise (never `false`)」):
|
|
384
|
+
* · `requiresRealApproval`([ref])—— 这一问只有**真人**清得掉(连一揽子席也不得答;蕴含下一位);
|
|
385
|
+
* · `mandated`(#1079,core 7.31.0;S-663 二段 / S-723)—— 没有任何持久规则、也没有任何站着的答案退得掉这一问
|
|
386
|
+
* (`AskRequest.mandated` 的 park 孪生,core 从行的规则报价工厂读的**同一份**清障事实铸)。
|
|
387
|
+
* 两位同形同律 ⇒ 同一只读法 {@link readParkRowTrueSeat}(一处解析,名字是参数,不是两只各写一遍的函数)。
|
|
388
|
+
* 型从 core 寄存行上取键(`keyof ToolApprovalPark` 约束):core 改名 / 退役任一位 ⇒ 这里 tsc 红。
|
|
389
|
+
*/
|
|
390
|
+
export type ParkRowTrueSeat = Extract<keyof ToolApprovalPark, "requiresRealApproval" | "mandated">;
|
|
391
|
+
/**
|
|
392
|
+
* park 行 `?: true` 座的读数(NP-1 起;S-663 二段起名字是参数)—— **`=== true` 严判**,`false` 与其它值读作缺席
|
|
393
|
+
* (core d.ts「Absent otherwise (never `false`)」;与 core `summarizeCheckpoint` 同一条判法:它把两位都筛成字面 `true`)。
|
|
385
394
|
* `kind` 由调用方先判(本函数只读这一位)。两个读者共用这一只,解析单源、处置各归读者(与 {@link readRuleOffersAbsenceSeat}
|
|
386
395
|
* 同一条分工):展示投影 {@link parkRowFacts}(队列行顶层展开)与耐久 `/decide` 腿的 remember 判据(`tool-approval.ts`
|
|
387
|
-
* 的 `parkRowStandingAnswerFacts`)
|
|
388
|
-
*
|
|
389
|
-
* fail-closed 的另一种处置(S-663 codex r1 钉「权限面不计展示面 tag」)。
|
|
396
|
+
* 的 `parkRowStandingAnswerFacts`)。这两位在两处方向相同(在场才算、读不出 = 缺席)⇒ 不需要三态、不计数(inbox 面对同一形
|
|
397
|
+
* 同样静默缺席,两面同律);也因此权限面读它们**不经** `parkRowFacts` —— 那只展示投影顺手读 `ruleOffersAbsence` 并对集外词计
|
|
398
|
+
* 展示面的 F 类 tag,而权限面对同一格是 fail-closed 的另一种处置(S-663 codex r1 钉「权限面不计展示面 tag」)。
|
|
390
399
|
*/
|
|
391
|
-
export declare function
|
|
400
|
+
export declare function readParkRowTrueSeat(toolApproval: object, seat: ParkRowTrueSeat): boolean;
|
|
392
401
|
/** core 寄存行的 `tool_approval` 臂(park 行事实全部住在这一臂上;其余臂没有工具字段)。 */
|
|
393
402
|
type ToolApprovalPark = Extract<CorePendingAction, {
|
|
394
403
|
kind: "tool_approval";
|
|
@@ -399,12 +408,15 @@ type ToolApprovalPark = Extract<CorePendingAction, {
|
|
|
399
408
|
* 纪律):core 改名 / 退役任一位 ⇒ 这里 tsc 红;这一位与 {@link RuleOffersAbsence}(从 `AskRequest` 派生)两个
|
|
400
409
|
* 联合漂开 ⇒ {@link parkRowFacts} 的返回处 tsc 红(core 说两座是同一个 factory 铸的同一闭集,本仓照此钉)。
|
|
401
410
|
*
|
|
402
|
-
* 两只店的 `PendingCheckpoint` **直接继承本型**(`extends`),于是 core
|
|
403
|
-
*
|
|
404
|
-
* `
|
|
405
|
-
*
|
|
411
|
+
* 两只店的 `PendingCheckpoint` **直接继承本型**(`extends`),于是 core 再给这一臂加一位同类事实时,两店与路由零改 ——
|
|
412
|
+
* S-663 二段(core #1079,7.31.0)的 `mandated` 就是这么到货的。要改的三处,编译器逐一点名:这张 Pick 表加一个名 +
|
|
413
|
+
* {@link parkRowFacts} 函数体一行 + `trace/core-keyset-guard.ts` ⑧b 的 EXCLUDED 簿删掉这个名(⑧b 的事实段**从本型派生**,
|
|
414
|
+
* 8D-1 / 车OC 起单写者 —— 名字进了本表却还躺在 EXCLUDED 里 ⇒ `_NoDoubleBookParkArmQueue` 当拍红);`test/park-row-facts.test.ts`
|
|
415
|
+
* 的 `FACT_KEYS` 对本型双向围栏,少列一名同样编译红。
|
|
416
|
+
* core d.ts 说三位彼此独立:`requiresRealApproval`(只有人能清 —— 蕴含 `mandated`)、`mandated`(没有规则能清)、
|
|
417
|
+
* `ruleOffersAbsence`(报价车道有什么可给;「never a substitute for this one」)—— 投影逐位照投,不互推。
|
|
406
418
|
*/
|
|
407
|
-
export type ParkRowFacts = Pick<ToolApprovalPark, "requiresRealApproval" | "ruleOffersAbsence">;
|
|
419
|
+
export type ParkRowFacts = Pick<ToolApprovalPark, "requiresRealApproval" | "ruleOffersAbsence" | "mandated">;
|
|
408
420
|
/**
|
|
409
421
|
* S-700 —— park 行事实的**唯一投影**(`pendingAction` 是寄存行上 core 铸的那一只,读自持久层 ⇒ `unknown`)。
|
|
410
422
|
*
|
|
@@ -417,7 +429,7 @@ export type ParkRowFacts = Pick<ToolApprovalPark, "requiresRealApproval" | "rule
|
|
|
417
429
|
* 判据(每一条都是 core d.ts 的消费纪律,不是本仓自定):
|
|
418
430
|
* · **kind 先判**(「Every consumer that reads the tool fields MUST branch on `kind` first」)—— 非对象 / 缺席 /
|
|
419
431
|
* 非 `tool_approval` 臂 ⇒ `{}`,那些臂上出现的同名键不是这两位;
|
|
420
|
-
* · `requiresRealApproval` —— 经 {@link
|
|
432
|
+
* · `requiresRealApproval` / `mandated` —— 经 {@link readParkRowTrueSeat}(`=== true` 严判,真才带;`false` 与其它值读作
|
|
421
433
|
* 缺席,「Absent otherwise (never `false`)」)。与 core `summarizeCheckpoint` 逐字同一条判法 ⇒ inbox 行与队列行对同一张卡
|
|
422
434
|
* 同答(`test/park-row-facts.test.ts` 钉)。不计数:inbox 那一面对同一形同样静默缺席,两面同律;
|
|
423
435
|
* · `ruleOffersAbsence` —— 经 {@link readRuleOffersAbsence}(活卡帧 / `card_json` 同一只 schema、同一个集外 tag):
|
package/dist/approval-card.js
CHANGED
|
@@ -266,8 +266,8 @@ function outOfSetDetail(raw) {
|
|
|
266
266
|
return `<${typeof raw}>`;
|
|
267
267
|
return String(raw).slice(0, 40);
|
|
268
268
|
}
|
|
269
|
-
export function
|
|
270
|
-
const v =
|
|
269
|
+
export function readParkRowTrueSeat(toolApproval, seat) {
|
|
270
|
+
const v = seat in toolApproval ? toolApproval[seat] : undefined;
|
|
271
271
|
return v === true;
|
|
272
272
|
}
|
|
273
273
|
export function parkRowFacts(pendingAction) {
|
|
@@ -275,7 +275,8 @@ export function parkRowFacts(pendingAction) {
|
|
|
275
275
|
return {};
|
|
276
276
|
const ruleOffersAbsence = readRuleOffersAbsence(pendingAction);
|
|
277
277
|
return {
|
|
278
|
-
...(
|
|
278
|
+
...(readParkRowTrueSeat(pendingAction, "requiresRealApproval") ? { requiresRealApproval: true } : {}),
|
|
279
|
+
...(readParkRowTrueSeat(pendingAction, "mandated") ? { mandated: true } : {}),
|
|
279
280
|
...(ruleOffersAbsence !== undefined ? { ruleOffersAbsence } : {}),
|
|
280
281
|
};
|
|
281
282
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S-702 / S-706 —— 「无人可答」的**政策感知终态 claim**:全仓唯一一只(NP-2 的政策结算抽出来,四处调用,不写第二份)。
|
|
3
|
+
*
|
|
4
|
+
* 一句话:一只没人答的 ask 在持久 ask 行上**落什么终局**,由部署政策(`UNATTENDED_APPROVAL_POLICY`)一处决定 ——
|
|
5
|
+
* · `park` ⇒ `expire` 意图:STREAM_PENDING→PARKING、批转投递面、同批流内兄弟连坐 VOID(park 路由的单位是整批,core 随后
|
|
6
|
+
* durable park,对账收敛器再把行绑到 checkpoint 上);
|
|
7
|
+
* · `deny` ⇒ `refuse` 意图:**按 ask 作用域**把这一只行 CAS 成 DECIDED(deny) + `settled_by="policy"`,不碰批、不连坐兄弟
|
|
8
|
+
* (deny 政策下没有 park 路由这个单位,7.99.0 铸的整腿批存量行同判)。落行的来源词是店侧常量 `POLICY_REFUSAL_DECISION`,
|
|
9
|
+
* 协调器交给 core 的部署政策拒对象读的是**同一个**常量 ⇒ 同 ask 重入 / 收敛后重入由 `outcomeOfDecidedRow` 读回同一个对象,
|
|
10
|
+
* 不退化成裸 `false`(core 的 onAsk 契约里那是「一个人拒了」,可停批)。
|
|
11
|
+
*
|
|
12
|
+
* 四个调用点(都调这一只,谁也不自己挑意图):
|
|
13
|
+
* 1. live 腿 —— 窗到期臂的终局 claim(`tool-approval.ts` `windowExpired`,经 claim 阶梯 `resolveLocalOutcome`);
|
|
14
|
+
* 2. live 腿 —— emit 全灭臂的终局 claim(同上,`emitFailed` 支);
|
|
15
|
+
* 3. 两兜底臂 —— `ensureAsk` 行不可用 / ensure 身份不定(`tool-approval.ts` `landUnattendedOutcome`,只在交给 core 的是**结算**时
|
|
16
|
+
* 调:park 路由不是结算,行留给投递面的既有机器,park 行为逐字不变),以及后者迟到落盘那条行的收尾;
|
|
17
|
+
* 4. 崩溃恢复腿 —— 对账收敛器段二的孤儿 STREAM_PENDING 代打(`approval-reconciler.ts`):它就是窗到期竞争者在崩溃后的补位,
|
|
18
|
+
* 同一只 claim、同一个转移(S-706 之前它无条件 `expireAsk`,deny 部署上崩溃后行进 PARKING,run 完成后成不带来源的 DENIED)。
|
|
19
|
+
*
|
|
20
|
+
* 形:纯委托 —— 店的原子终局 claim(`ApprovalAskStore.claimTerminal`,三 twin 一台 CAS 机器,律 2)+ 政策→意图的穷举映射。
|
|
21
|
+
* 答案自足(律 1):赢 ⇒ `{claimed:true, voidedSiblings}`(refuse 恒空名单);输 ⇒ 携行真相;抛错 ⇒ 什么也不证明。
|
|
22
|
+
* 各调用点对这三种答案的处置归各自(阶梯 / 兜底 / 收敛器),不在这里。
|
|
23
|
+
*/
|
|
24
|
+
import type { ApprovalAskStore, AskRow, AskTerminalClaimOutcome } from "./plugins/approval-ask-store-sql.js";
|
|
25
|
+
import type { UnattendedApprovalPolicy } from "./tool-approval.js";
|
|
26
|
+
/**
|
|
27
|
+
* 按部署政策给这一只 ask 行打原子终局 claim(见文件头:四个调用点、两个意图、答案自足)。`row` 只取 scope 两维 ——
|
|
28
|
+
* `batchId` 必须是**行上**的批(幂等命中的存量行可能是别的政策 / 旧版本铸的整腿批,错 scope 的 claim 如实 `absent`)。
|
|
29
|
+
*/
|
|
30
|
+
export declare function settleByPolicy(store: Pick<ApprovalAskStore, "claimTerminal">, row: Pick<AskRow, "askId" | "batchId">, policy: UnattendedApprovalPolicy): Promise<AskTerminalClaimOutcome>;
|
|
31
|
+
//# sourceMappingURL=approval-policy-settlement.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function unattendedClaimIntent(policy) {
|
|
2
|
+
switch (policy) {
|
|
3
|
+
case "park":
|
|
4
|
+
return "expire";
|
|
5
|
+
case "deny":
|
|
6
|
+
return "refuse";
|
|
7
|
+
default: {
|
|
8
|
+
const unreachable = policy;
|
|
9
|
+
throw new Error(`unattendedClaimIntent: unknown UNATTENDED_APPROVAL_POLICY word ${String(unreachable)}`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function settleByPolicy(store, row, policy) {
|
|
14
|
+
return store.claimTerminal(row.askId, row.batchId, unattendedClaimIntent(policy));
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=approval-policy-settlement.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AskRow, ApprovalAskStore } from "./plugins/approval-ask-store-sql.js";
|
|
2
|
+
import type { UnattendedApprovalPolicy } from "./tool-approval.js";
|
|
2
3
|
import type { BatchState } from "./approval-ask-machine.js";
|
|
3
4
|
import type { CheckpointAskCandidate } from "./plugins/checkpoint-store-sql.js";
|
|
4
5
|
import { type RunRecord } from "./plugins/store-contracts.js";
|
|
@@ -189,7 +190,8 @@ export interface ReconcileStats {
|
|
|
189
190
|
/** 段一/段二因**墙钟预算**用尽而提前收工的段数(0/1/2)。>0 ⇒ 依赖在退化,剩余行留给下一轮
|
|
190
191
|
* (见 {@link RECONCILE_SEGMENT_BUDGET_MS})。 */
|
|
191
192
|
budgetExhausted: number;
|
|
192
|
-
/**
|
|
193
|
+
/** 段二:被代打**窗到期终局 claim** 的孤儿 `STREAM_PENDING` 行数(赢 CAS 的)。「expired」指触发(窗过了没人收),
|
|
194
|
+
* 不指意图 —— 与 live 腿 `window_expired` 同一个读法:park 部署 ⇒ expire(进投递面);deny 部署 ⇒ refuse(当场落政策结算,S-706)。 */
|
|
193
195
|
orphansExpired: number;
|
|
194
196
|
}
|
|
195
197
|
/** 撤卡帧的投递面(逐 ctx 分发:durable 腿落账本、sync 腿 live,见 `ApprovalRevokeFrame` 顶注)。
|
|
@@ -222,6 +224,11 @@ export interface ApprovalReconcilerDeps {
|
|
|
222
224
|
pendingGraceMs: number;
|
|
223
225
|
adhocGraceMs: number;
|
|
224
226
|
orphanTtlMs: number;
|
|
227
|
+
/**
|
|
228
|
+
* S-706:部署的无人值守政策(`UNATTENDED_APPROVAL_POLICY`)—— 段二孤儿代打打的是哪个终局 claim。**必填**、恒传(与协调器
|
|
229
|
+
* 读同一个部署值;没有缺省 —— 缺省 park 会让 deny 部署在崩溃恢复这一形上静默退回「进投递面 → 不带来源的 DENIED」)。
|
|
230
|
+
*/
|
|
231
|
+
unattendedPolicy: UnattendedApprovalPolicy;
|
|
225
232
|
emitRevoke?: ApprovalRevokeEmitter | undefined;
|
|
226
233
|
}
|
|
227
234
|
/** {@link createApprovalReconciler} 返回的活对象。 */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CANCELLED_CODE } from "./run-cancel-context.js";
|
|
2
|
+
import { settleByPolicy } from "./approval-policy-settlement.js";
|
|
2
3
|
import { isTerminalRunStatus } from "./plugins/store-contracts.js";
|
|
3
4
|
import { DENY_REASONS, VOID_REASONS } from "./approval-deny-reasons.js";
|
|
4
5
|
import { buildRevokeFrame } from "./approval-card.js";
|
|
@@ -78,7 +79,7 @@ export function decideReconcileAction(input) {
|
|
|
78
79
|
return { kind: "hold" };
|
|
79
80
|
}
|
|
80
81
|
export function createApprovalReconciler(deps) {
|
|
81
|
-
const { askStore, checkpoints, runs, logger, metrics, batchLimit, pendingGraceMs, adhocGraceMs, orphanTtlMs, emitRevoke } = deps;
|
|
82
|
+
const { askStore, checkpoints, runs, logger, metrics, batchLimit, pendingGraceMs, adhocGraceMs, orphanTtlMs, unattendedPolicy, emitRevoke } = deps;
|
|
82
83
|
const revoke = (row, askIds, reason, nowMs) => {
|
|
83
84
|
if (askIds.length === 0 || !emitRevoke)
|
|
84
85
|
return;
|
|
@@ -245,8 +246,8 @@ export function createApprovalReconciler(deps) {
|
|
|
245
246
|
if (row.expiresAtMs + pendingGraceMs >= nowMs)
|
|
246
247
|
continue;
|
|
247
248
|
try {
|
|
248
|
-
const res = await withDeadline(askStore
|
|
249
|
-
if (!res.
|
|
249
|
+
const res = await withDeadline(settleByPolicy(askStore, row, unattendedPolicy), "claimTerminal(orphan)");
|
|
250
|
+
if (!res.claimed)
|
|
250
251
|
continue;
|
|
251
252
|
stats.orphansExpired += 1;
|
|
252
253
|
revoke(row, res.voidedSiblings, "superseded_by_park", nowMs);
|
|
@@ -254,7 +255,7 @@ export function createApprovalReconciler(deps) {
|
|
|
254
255
|
catch (err) {
|
|
255
256
|
stats.failed += 1;
|
|
256
257
|
try {
|
|
257
|
-
logger.warn("approval_orphan_expire_failed", { askId: row.askId, taskId: row.taskId, err: err instanceof Error ? err.message : String(err) });
|
|
258
|
+
logger.warn("approval_orphan_expire_failed", { askId: row.askId, taskId: row.taskId, policy: unattendedPolicy, err: err instanceof Error ? err.message : String(err) });
|
|
258
259
|
}
|
|
259
260
|
catch {
|
|
260
261
|
}
|