@sema-agent/server 7.116.1 → 7.118.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 +195 -0
- package/USAGE.md +65 -41
- package/dist/approval-card.d.ts +82 -18
- package/dist/approval-card.js +46 -4
- package/dist/approval.d.ts +4 -12
- package/dist/approval.js +4 -26
- package/dist/boot/budget-tracing.js +2 -3
- package/dist/boot/config-center.d.ts +2 -2
- package/dist/boot/config-center.js +5 -7
- package/dist/boot/parked-revive-gate.d.ts +1 -7
- package/dist/boot/parked-revive-gate.js +2 -12
- package/dist/boot/resolve-spec.js +18 -16
- package/dist/boot/runner-deps.d.ts +5 -3
- package/dist/boot/runner-deps.js +6 -4
- package/dist/boot/stage-07-capability-layer.js +2 -2
- package/dist/brain.d.ts +17 -8
- package/dist/brain.js +10 -7
- package/dist/capabilities/hands-lane.d.ts +25 -0
- package/dist/capabilities/hands-lane.js +1 -1
- package/dist/capabilities/mcp-probe.d.ts +2 -2
- package/dist/config-catalog.js +3 -2
- package/dist/config-center/apply-effective.d.ts +6 -16
- package/dist/config-center/apply-effective.js +0 -6
- package/dist/config-center/facade.d.ts +1 -1
- package/dist/config-center/facade.js +1 -1
- package/dist/config-center/restart-signal.d.ts +1 -1
- package/dist/config-types.d.ts +12 -16
- package/dist/config.d.ts +32 -29
- package/dist/config.js +20 -27
- package/dist/deployment-governance.d.ts +13 -19
- package/dist/deployment-governance.js +7 -14
- package/dist/governance-overrides.d.ts +20 -0
- package/dist/governance-overrides.js +8 -0
- package/dist/host-decision.d.ts +83 -25
- package/dist/host-decision.js +31 -1
- package/dist/http/admission.js +13 -7
- package/dist/http/approval-sweeps.js +16 -13
- package/dist/http/checkpoint-ctx.d.ts +83 -24
- package/dist/http/checkpoint-ctx.js +68 -8
- package/dist/http/mcp-dial-deadline.d.ts +35 -0
- package/dist/http/mcp-dial-deadline.js +21 -0
- package/dist/http/memory-compliance-failure-reply.d.ts +11 -5
- package/dist/http/memory-compliance-failure-reply.js +17 -8
- package/dist/http/memory-store-refusal-reply.d.ts +14 -11
- package/dist/http/memory-store-refusal-reply.js +16 -5
- package/dist/http/resume-legs.js +18 -18
- package/dist/http/retired-skill-manifest.d.ts +1 -1
- package/dist/http/route-ctx.d.ts +3 -3
- package/dist/http/routes/approvals-assistant.js +21 -24
- package/dist/http/routes/capabilities.js +5 -3
- package/dist/http/routes/mcp-reconnect.d.ts +17 -0
- package/dist/http/routes/mcp-reconnect.js +6 -1
- package/dist/http/routes/memory-bundle.js +1 -1
- package/dist/http/routes/memory-compliance.js +1 -1
- package/dist/http/routes/memory-origin.js +1 -1
- package/dist/http/routes/runs.js +53 -33
- package/dist/http/routes/sessions.d.ts +1 -1
- package/dist/http/routes/sessions.js +3 -6
- package/dist/http/routes/workflows.js +2 -0
- package/dist/http/wire-types.d.ts +6 -1
- package/dist/memory-store-refusal.d.ts +42 -17
- package/dist/memory-store-refusal.js +25 -6
- package/dist/model-select.d.ts +16 -5
- package/dist/model-select.js +7 -3
- package/dist/model-unreachable-notice.d.ts +1 -1
- package/dist/namespaced-rule-name.d.ts +45 -39
- package/dist/namespaced-rule-name.js +36 -22
- package/dist/observability/corrupt-read-seat.d.ts +4 -0
- package/dist/observability/corrupt-read-seat.js +1 -0
- package/dist/observability/fail-open.d.ts +15 -3
- package/dist/observability/fail-open.js +15 -3
- package/dist/observability/permission-rule-events.d.ts +3 -0
- package/dist/observability/permission-rule-events.js +4 -0
- package/dist/observability/run-terminal.js +21 -1
- package/dist/operator-ask.d.ts +4 -1
- package/dist/orchestration/workflow-agent-session-index.d.ts +82 -59
- package/dist/orchestration/workflow-agent-session-index.js +80 -14
- package/dist/orchestration/workflow-completion-inbox.d.ts +5 -0
- package/dist/orchestration/workflow-completion-inbox.js +39 -17
- package/dist/orchestration/workflow-notify-journal.d.ts +1 -1
- package/dist/orchestration/workflow-notify-journal.js +7 -2
- package/dist/outbound-dispatcher.d.ts +47 -20
- package/dist/outbound-dispatcher.js +198 -23
- package/dist/parked-decide.d.ts +43 -48
- package/dist/parked-decide.js +54 -16
- package/dist/permission-rule-list.d.ts +20 -31
- package/dist/permission-rule-vocab.d.ts +4 -3
- package/dist/permission-rule-vocab.js +1 -1
- package/dist/plugins/approval-ask-store-sql.d.ts +17 -20
- package/dist/plugins/approval-ask-store-sql.js +0 -3
- package/dist/plugins/one-time-migrations.d.ts +25 -0
- package/dist/plugins/one-time-migrations.js +192 -6
- package/dist/plugins/pg-pool.js +1 -1
- package/dist/plugins/tidb-pool.js +1 -1
- package/dist/plugins/web-search.d.ts +20 -1
- package/dist/plugins/web-search.js +39 -10
- package/dist/plugins/workflow-run-store-sql.d.ts +7 -6
- package/dist/plugins/workflow-run-store-sql.js +5 -7
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +1 -1
- package/dist/router/route-orchestration.d.ts +2 -23
- package/dist/rule-entry-list.d.ts +88 -0
- package/dist/rule-entry-list.js +102 -0
- package/dist/rules-consent.js +3 -0
- package/dist/run-local.d.ts +4 -2
- package/dist/run-local.js +4 -3
- package/dist/runs.d.ts +1 -1
- package/dist/runs.js +1 -0
- package/dist/runtime-governance.d.ts +5 -21
- package/dist/runtime-governance.js +4 -8
- package/dist/session-policy-wire.d.ts +13 -12
- package/dist/session-policy-wire.js +11 -6
- package/dist/spec-fields.d.ts +13 -24
- package/dist/spec-fields.js +13 -17
- package/dist/standing-answer-grant.d.ts +14 -2
- package/dist/standing-answer-grant.js +8 -1
- package/dist/task-settings.d.ts +18 -18
- package/dist/task-settings.js +63 -32
- package/dist/tool-approval.d.ts +77 -145
- package/dist/tool-approval.js +80 -62
- package/dist/trace/core-keyset-guard.d.ts +6 -6
- package/dist/trace/engine-notice-wire.d.ts +4 -6
- package/dist/trace/engine-notice-wire.js +14 -1
- package/dist/trace/self-minted-wire.d.ts +16 -2
- package/dist/trace/self-minted-wire.js +7 -0
- package/dist/user-input-ledger.d.ts +24 -12
- package/dist/user-input-ledger.js +33 -14
- package/package.json +2 -2
- package/scripts/drain-check-local.mjs +103 -7
- package/scripts/drain-check.mysql.sql +59 -8
- package/scripts/drain-check.pg.sql +66 -10
package/MIGRATION.md
CHANGED
|
@@ -7,6 +7,201 @@
|
|
|
7
7
|
> ⚠️ 完整清单在仓库根 `CHANGELOG.md`——它**不随 npm tarball 出包**(本文件随包)。看完整迁移窗的
|
|
8
8
|
> 权威姿势是源码 tag diff:`git diff v<旧>..v<新>`(每版都推 `v<版本>` tag);npm 包页也镜像 CHANGELOG。
|
|
9
9
|
|
|
10
|
+
## 7.118.0 —— 审批决定体的出处位 `decidedBy`(取代 `settledBy`)· 能力位 `approvalDecidedBy` · 有审批名单却无检查点店的装配,任务提交一律 501
|
|
11
|
+
|
|
12
|
+
**审批决定体用一只键说「谁做的这个决定」**:`decidedBy?: "person" | "policy"`(缺席 = `"person"`)。三条工具审批决定口 ——
|
|
13
|
+
`POST /v1/tool-approvals/:approvalId/respond`、`POST /v1/tasks/:taskId/asks/:askId/decision`、`POST /v1/approvals/:sessionId/decide` ——
|
|
14
|
+
同一只读法收它;`"policy"` 让引擎把这次决定记在策略名下(`tool_end.gate.settlement.kind` = `policy_allowed` / `policy_refused`,
|
|
15
|
+
`who.party:"policy"`),**放行与拒两向都可以**(此前一次规则代答的放行只能记成 `human_allowed`)。
|
|
16
|
+
|
|
17
|
+
**要动的(发过 `settledBy` 的调用方)**
|
|
18
|
+
|
|
19
|
+
1. **`settledBy` 退役,改发 `decidedBy`**。7.93.3–7.117.x 的决定体上有一格 `settledBy?: "policy"`(只配 deny);它与 `decidedBy` 是同一个
|
|
20
|
+
事实,本版删掉,零别名。送它:三条决定口都 `400 request.body_shape`,拒在裁决之前、卡不动(live `/respond` 的句子点名 `decidedBy`)。
|
|
21
|
+
迁移 = 一处装配:`settledBy:"policy"` → `decidedBy:"policy"`;放行一向现在也可以带它。
|
|
22
|
+
2. **见位才发**:`GET /v1/capabilities` 的 `approvalDecidedBy`(恒 `true` 的版本位)在场才发 `decidedBy`。老 server 上耐久两口与
|
|
23
|
+
`/v1/assistant/tasks/:id/plan_review` 对它 400,live `/respond` 静默丢 + 200 —— 位缺席就照老形发。
|
|
24
|
+
3. **直连门部署**:耐久两口上带 `decidedBy`(不论值)⇒ `400 decided_by_not_in_proof`(它不在决策证明的签名载荷里);错误码
|
|
25
|
+
`settled_by_not_in_proof` 随退役键一起删。live `/respond` 照收。
|
|
26
|
+
4. **回滚到 7.117.x**:本版 live `/respond` 与耐久 ask 账口上 `decidedBy:"policy"` 的放行,落成 ask 账里一条「`decision = approve` + 来源位 `settled_by = policy`」的行;
|
|
27
|
+
7.117.x 的读法对放行行不读来源位,同一只 ask 回滚后被重入(故障切换 / 重启后重放)时把它读成**人**放行 —— 审计归属错,不多放权(放行本来就生效)。
|
|
28
|
+
要审计准确:回滚前让在途的审批都结掉 —— 流内的卡清零、停驻的审批卡决掉或取消(做法:回滚前停收新任务、等在跑的 run 全部结束;流内卡数 = `SELECT COUNT(*) FROM approval_ask WHERE state = 'STREAM_PENDING'`,停驻的 workflow 子代卡照下面「7.118.0 线」第 10 条数),没有还会被重入的 ask,就没有被读错的行。
|
|
29
|
+
|
|
30
|
+
**新的拒(按需处置)**
|
|
31
|
+
|
|
32
|
+
- 闭集外(含 `"sla_timeout"` —— 「等待到点、没人答」只有服务端自己的截止扫说得出;含旧词 `human` / `timeout` / 结算词 / 空串 / 非串)⇒ 400,
|
|
33
|
+
句子点名闭集:live `/respond` = `request.body_shape`,耐久两口 = `request.field_invalid`。拒在裁决之前,卡不动。
|
|
34
|
+
- 一张**要人**的卡(帧顶层 `requiresRealApproval` / 卡面 `risk.requiresRealApproval`)上 `decidedBy:"policy"` 的放行:live `/respond` =
|
|
35
|
+
`400 request.body_shape`,ask 决定口 = `400 request.field_conflict`(拒在裁决之前,卡不动);`/decide` = 引擎在 CAS 之前拒,
|
|
36
|
+
`409 resume_outcome_invalid` + `field:"hostDecision"`(卡留 pending)。改成人答,或以策略的名义拒。
|
|
37
|
+
- `plan_review` 不收 `decidedBy`(引擎的计划审阅结局没有决定者位)⇒ `400 request.body_shape`,落 `unsupportedKeys`。
|
|
38
|
+
- **有审批名单(`APPROVAL_REQUIRE` / `APPROVAL_NEVER_AUTO` / `APPROVAL_DENY` 任一非空)或显式 `DURABLE_APPROVAL=true`、而装配里没有检查点店** ⇒ 每个任务提交(含续跑腿)`501 capability.checkpoint_store_required`,
|
|
39
|
+
拒在一切副作用之前;句子点名缺的是耐久席位、怎么配。**只有自己装配 `resolveSpec` 的嵌入方会撞到**:`sema-server` 起服对同一形早就拒启(`DURABLE_APPROVAL=false` / `DB_BACKEND=memory`),到不了任务提交。
|
|
40
|
+
7.117.0 上这一形的装配读到的是静默放行(审批基线铸成 `undefined`,请求的 `settings.permissions.allow` 清掉普通问后任务零卡落盘)。出路:给装配一只检查点店(`DURABLE_APPROVAL=true` + 带检查点面的后端),或去掉名单;
|
|
41
|
+
单用户 turnkey(没有名单、没有显式旗)的 allow-all 基线不变。
|
|
42
|
+
|
|
43
|
+
**已知边界(老版本,升级决策知悉)**:≤7.115.x 的 `DB_BACKEND=local` 后端没有文件历史店 —— 会话同步的文件历史 blob 两口(`PUT …/sync/blobs/:hash` 恒 501、`GET …/sync/history/blobs/:hash` 对清单引用的 blob 恒 404 `not_found.blob`),而那一代的能力位 `sessionSync` 仍为真。7.116.0 起拆成独立位 `sessionSyncFileHistory`(local 为假):消费方以这一位为唯一读法,位缺席 / 为假 ⇒ 按「这台不搬文件历史」处置,不要照旧尝试。SQL 后端(两只文件历史店在场)自 7.52 起 blob 面可用,不受此条影响。
|
|
44
|
+
|
|
45
|
+
**订正 7.117.0 节的一行配方**:排空判据 `skill-deny-inert` 逐行精确核的 `node -e` 一行此前写成裸子路径 `import("@sema-agent/server/dist/rule-entry-list.js")`,而本包不导出 `./dist/*` 子路径(`ERR_PACKAGE_PATH_NOT_EXPORTED`);已改成按文件路径 import(7.117.0 节与仓内部署前提文档同步改)。 正确的一行(7.117.0 节正文保持发布时原样,照这里的跑):
|
|
46
|
+
`node -e 'import(process.argv[1]).then(m => console.log(m.skillDenyInertEntryOf(JSON.parse(require("fs").readFileSync(process.argv[2], "utf8")).body) ?? "ok"))' "$(node -p 'require.resolve("@sema-agent/server/package.json").replace(/package\\.json$/, "dist/rule-entry-list.js")')" ctx.json`(包不导出 `./dist/*` 子路径,所以按文件路径 import,`ctx.json` 作第二个实参),
|
|
47
|
+
|
|
48
|
+
**部署前提(拉配置中心的部署,升级决策知悉)**:7.114.0 起引擎按 `@sema-agent/settings-schema` ≥ 7 的形读中心下发的 `workers` 配置;中心仍发 6.x 形时**整份中心配置被拒**,起服回落 env 并 `ready:false`,之后每次刷新照拒(响亮,不静默套用旧形;本仓不为 6.x 形留第二种读法)。**先把中心升到 settings-schema ≥ 7(web-admin SS8 / 8.0.0 + M-R2),再把拉它配置的引擎升到 ≥ 7.114.0**;顺序反了就是上面那一形。7.114.0 节当时没写这一句,在此补。
|
|
49
|
+
|
|
50
|
+
### 7.118.0 线 —— `@sema-agent/core` 7.42.1 → 7.44.0:规则名只认引擎判官 · allow 侧名字层交引擎 · 技能规则拼法 `Skill(<名>)` · 请求键 `approvalReach` · 崩溃告知按车道分族 · 重放拒体带 `detail.differs`
|
|
51
|
+
|
|
52
|
+
**升级前必读**
|
|
53
|
+
|
|
54
|
+
1. **嵌入方 import 了引擎的旧名**:`USER_STEER_UNDRAINED_CAUSES` / `UserSteerUndrainedCause` 已改名 `USER_INPUT_UNDRAINED_CAUSES` / `UserInputUndrainedCause`(引擎 7.44.0,零别名;TS 编译红、JS 读到 `undefined`)。换新名,成员不变。
|
|
55
|
+
2. **规则里的工具名只认引擎的判官**。协议名的服务器段引擎铸不出 / 为空(`mcp__github.com__*`、`mcp__*`、`mcp__`):
|
|
56
|
+
- 运维名表(`APPROVAL_REQUIRE` / `APPROVAL_DENY` / `APPROVAL_NEVER_AUTO`、中心下发)与请求 `settings.permissions.ask` / `deny` 照旧拒,**句子换成引擎的**(带它铸出的写法);按旧句子匹配的脚本要改。
|
|
57
|
+
- 会话规则写口(`PUT /v1/sessions/:id/policy`、同步导入的 `policy` 面)对这一族的码由 `400 request.field_invalid` 改为 `400 rules.legacy_tool_name`(体顶层 `verdict: "unmintable_segment"`、`currentName` = 该写的名字(带通配的协议名上它是摘要名,见本节末「已知边界」),与 `field` / `name` 同层,不在 `detail` 里;第 6 条 steer 重放拒的 `detail.differs` 才是嵌套的)。库里已存的这种死 deny,属主自己的写现在删得掉。
|
|
58
|
+
- 工具段为空的协议名(`mcp__srv__`)照旧在每张表上拒,会话写口仍是 `request.field_invalid`。
|
|
59
|
+
3. **allow 侧的名字交引擎**:请求 `settings.permissions.allow` 与请求自带技能的 `allowedTools` 里写退役名 / 无前缀短名 / 铸不出的协议名(`bash`、`figma__x`、`mcp__*`)不再 422 / 400,
|
|
60
|
+
而是受理、开跑时收通告 `permission.allowed_tool_not_granted`(`reason: "tool_name"`,带 `currentName`)—— 这些条目从来什么都不授予,现在只是不在提交时拦。依赖「写错名字当场被拒」的调用方改读那条通告。
|
|
61
|
+
技能 `disallowedTools` 里的同一族仍是 400(判据换成引擎的条目读法,原因词 `tool_name`);`APPROVAL_GRANT` 里的同一族仍拒启。
|
|
62
|
+
**存量停驻体**里技能 `disallowedTools` 写了这一族(7.116.x 受理过的)⇒ 续跑认领之前 `409 resume_blocked_by_policy`(`blockedBy: request.field_invalid`)——
|
|
63
|
+
引擎 7.43 起这一类拒侧条目同样让那只技能不激活;随包排空判据 `skill-deny-inert` 同数它们,升级前照 7.117.0 节的做法排空。
|
|
64
|
+
4. **技能规则只有一种拼法**:`settings.permissions.ask` / `deny` 里 `Skill(<名>)`(精确)/ `Skill(<前缀>:*)`(名字前缀,无词界)现在受理并生效(此前 422);参数面拼法 `Skill(skill:<名>)` 现在 422。先改写再升。
|
|
65
|
+
持久规则(`/v1/rules`、导入)的 `match` 多两个词 `name` / `name_prefix`;回滚到 7.117.x 的副本读到这种行会把它当坏行。
|
|
66
|
+
5. **崩溃丢掉的 `priority:"later"` steer 报在 follow-up 一族**:进程崩溃后下一条 run 首拍的告知,`later` 档那几条现在是 `task.user_followup_undrained{followUp, cause:"crashed"}`(其余仍是 `task.user_steer_undrained`);
|
|
67
|
+
`GET /v1/sessions/:id` 的 `userInputUndrained[].kind` 多一个词 `"follow_up"`。按 `code` 写死只认 steer 一族、或按 `kind === "steer"` 过滤的消费方两码同读。回滚到 7.117.x:那几条 `follow_up` 行旧版本读不懂、不告知。
|
|
68
|
+
6. **`steering.duplicate_input_id` 的 409 体多一格 `detail.differs`**(`text` / `trusted` / `priority` / `actor` 的非空子集):含 `"text"` ⇒ 换键;不含 ⇒ 别换键重发(会排两次),用原值重放。句子是引擎的,已改。
|
|
69
|
+
7. **新请求键 `approvalReach`**(唯一一词 `"none"`;能力位 `approvalReach`):声明这条 run 上没有人能答审批 —— 有停驻设施的部署上第一次门控调用就停驻(`paused`),要「拒了接着跑」用 `permissionMode:"dontAsk"`。不发它的调用方零变化。
|
|
70
|
+
|
|
71
|
+
8. **升级那一刻还停驻着的 workflow 子代审批卡**:决定口答 `409 decide.workflow_parks_unreadable`(句子说成因是更早的引擎版本),卡仍 pending、什么都不投递。
|
|
72
|
+
出路:续跑体里 `runId` 那条 workflow run,它走到那张卡时写下自己的持有者位,之后再决即可。
|
|
73
|
+
引擎 7.17 之前写下、记录上根本没有 `parks` 的那一代:没有条目、持有不了任何东西 —— 同一张卡上有新引擎写下的持有者时照常交给它;只有它点名这张卡时答
|
|
74
|
+
`409 conflict.resume_context_unavailable`(体里不带 `runId`;出路同上,续跑发起它的那条 workflow run)。想省掉这一步:**升级前在旧版本的数据上**跑随包排空判据 `workflow-park-holder`
|
|
75
|
+
(SQL:`node_modules/@sema-agent/server/scripts/drain-check.mysql.sql` / `drain-check.pg.sql` 的那一段,期望 `hit_rows = 0`;local:`node node_modules/@sema-agent/server/scripts/drain-check-local.mjs`,
|
|
76
|
+
期望 `WORKFLOW-PARK-HOLDER rows=0`;命中行点名 workflow run),把这些卡先决掉。
|
|
77
|
+
9. **新码 `409 decide.workflow_park_forked`**(体带 `runIds`):同一条 workflow run 先后被续跑两次、两支都停在同一张卡上(判得出的前提是 park 指针索引齐;一枚竞争指针没写上时决定投给指针在的那一支;指针**写失败**那一形运维计数 `server.workflow-park-index.sync-failed`,**写记录与写指针之间崩溃**那一形不计数 —— 计数为 0 不等于分叉门完好)。重试不会好:续跑 `runIds` 里的一条再决。把未知 409 一律当「稍后重试」的客户端要加这一支。
|
|
78
|
+
`GET /v1/workflows/:id` 的 `parks[]` 条目多两位 `parkedBy` / `generation`(在场才有);钉过条目精确键集的探针要放宽。
|
|
79
|
+
10. **SQL 后端**:`workflow_agent_session` 主键改成 `(session_id, run_id)`,启动期自动改形(暂存表复制 + 原子换名,任何一步被打断下一次起服从暂存表续上、旧行不丢;启动日志 `one_time_migrations_applied` 的 `workflowParkIndexKey: "reshaped"` 记一次)。
|
|
80
|
+
按旧主键写过外部查询的运维:同一个 `session_id` 现在可以有多行。
|
|
81
|
+
**SQL 多副本整队一次切,不混跑**:先停光 7.117.x,再起 7.118.0。改形之后 7.117.x 副本对这张表 ——
|
|
82
|
+
PostgreSQL:**每一次写都失败**(它的 upsert 冲突目标是 `(session_id)`,新主键下没有这只唯一约束;新键本来就允许一个子会话多行,这只约束保不住)⇒ 经它停驻的 workflow 子代卡没有指针,决定口决不掉,要人续跑那条 workflow run;
|
|
83
|
+
TiDB / MySQL:写照常落(`REPLACE INTO` 按新主键落行,不再覆盖同一子会话别的 run 的行),读一个子会话时多行里取哪一行不定 ⇒ 续跑链 / 分叉上的卡经它可能决不掉。
|
|
84
|
+
两方言都有一瞬:整表复制到换名之间旧副本写进旧表的指针随旧表删掉。
|
|
85
|
+
**回滚到 7.117.x**:先排空 workflow 子代的待批卡 —— 在 7.118.0 的数据上跑随包排空判据 `workflow-park-holder`(SQL 两份文件的那一段,期望 `hit_rows = 0`;此时数出的就是全部待批的 workflow 子代卡,逐张决掉或取消发起它的任务);
|
|
86
|
+
停掉全部 7.118.0 副本后再跑一次确认仍为 0;然后 `DROP TABLE workflow_agent_session`(派生投影,7.117.x 起服按它自己的建表语句重建旧形);再起 7.117.x。不删这张表,PostgreSQL 上 7.117.x 对它的写全部失败。
|
|
87
|
+
11. **出网代理拒隧道约 2 s 终局**:代理有时拒隧道、几十秒内又恢复的部署,以前靠长阶梯熬过的 run 现在失败、要重提;没有恢复长阶梯的旋钮,升级前稳住代理。终局句以
|
|
88
|
+
`proxy authentication required (HTTP 407)` / `proxy forbids this destination (HTTP 403)` / `proxy refused the tunnel (HTTP <n>)` 开头(按 `[network] fetch failed (` 开头匹配的脚本改成在句内搜)。
|
|
89
|
+
12. **记忆作用域家必须是真目录**:软链(哪怕指向真目录)或文件形的家在每条碰到它的记忆路由上答 `409 memory.scope_home_not_directory`(此前平铺布局是 500 `internal.memory_control_plane_corrupt`)。
|
|
90
|
+
升级前 `find <记忆目录> -maxdepth 1 -mindepth 1 ! -type d` 核一眼;要换盘就挂载整个记忆目录。记忆的 409 句子不再带宿主路径(`memory.target_outside_store` 同):路径在运维 warn `memory_store_form_refused { code, path, form? }`。
|
|
91
|
+
|
|
92
|
+
13. **`ask` 规则卡上的「本会话放行」不再落记录**:`APPROVAL_REQUIRE`、`APPROVAL_NEVER_AUTO`、请求的 `settings.permissions.ask` 或存下的 ask 行铸的卡带 `rememberRefused: true`;
|
|
93
|
+
`allow_session` / `remember: "session"` 只批这一次(`rememberApplied: false`)。**升级前已落的「本会话放行」对 `APPROVAL_REQUIRE` 的卡失效**:每次照问(旧行还在豁免列表里、按名撤得掉,
|
|
94
|
+
只是不再压住名单门的问)。工具不该被问就从名单里拿掉;要自动批用 `APPROVAL_AUTO_BUDGET`。
|
|
95
|
+
14. **没有类集的卡不记**:引擎 7.37 之前写的停驻卡(server ≤ 7.108)没有类集,`remember: "session"` 在它们上面只批这一次。
|
|
96
|
+
15. **技能卡**带 `boundGrantDigest` 时,live「本会话放行」按同技能同清单记住;耐久 `remember: "session"` 仍不记技能卡 —— 停驻的技能卡在运维队列行 / 推送帧上带 `rememberRefused: true`,`/decide` 回执 `rememberApplied: false`;
|
|
97
|
+
活卡停驻之后(`tool_approval_complete` 带 `parked: true`)对它的迟到应答同样不记。`rememberRefused` 按应答路读:活的面与停驻的面各说自己那条路(契约「缺席的读法」表)。
|
|
98
|
+
|
|
99
|
+
**已知边界(升级决策知悉)**
|
|
100
|
+
|
|
101
|
+
- workflow 子代的审批卡,只要点名它的记录里有一条的 `parks` **条目坏形**(或这一位在场却不是列表),决定口对这张卡恒答 `409 decide.workflow_parks_unreadable`,
|
|
102
|
+
直到运维把那条记录(体里的 `runId`)的 JSON 修好;续跑解不开 —— 那条记录的指针不随任何后继记录消失。体里另带 `readableHolderRunId` 时,工作可以从那条记录续上。
|
|
103
|
+
引擎从不写坏形,这一形只来自存储层改写或手改。
|
|
104
|
+
- 规则里写了带通配的协议名(`mcp__*`、`mcp__git*__x`)时,拒句里引擎的改法指引说「its tools are named mcp__<一串摘要>__<tool>」—— 那串摘要不是任何服务器的名字,照写不生效;
|
|
105
|
+
会话规则写口拒体顶层的 `currentName` 在这一形上也是那个摘要名(本服务的拒句不再在句尾追加它)。写具体的服务器名即可(`mcp__<server>__*` / `mcp__<server>`)。指引句在后续引擎版本修正。
|
|
106
|
+
|
|
107
|
+
## 7.117.0 —— 2026-10-06
|
|
108
|
+
|
|
109
|
+
### 7.117.0 线 —— `@sema-agent/core` 7.41.2 → 7.42.1:「这个调用不用问」只有一个去处(引擎的 allow 层)· 升级前排空带 `settings.permissions.allow` 的停驻行 · `APPROVAL_GRANT` 语义变宽 · 卡面披露键改名 `allowRuleShadowed` · 部署 hook 回 `allow` 现在清问 · 能力位 `settings.permissions.allowLayer` · 通告码改名 · 新错误码 `config.image_invalid`
|
|
110
|
+
|
|
111
|
+
**升级前必读(按顺序做)**
|
|
112
|
+
|
|
113
|
+
1. **排空带 `settings.permissions.allow` 的停驻行**。本版把请求的 allow 整份交给引擎的 allow 层(表里的调用免问,写 / shell 的普通问也清),读法记号换成新词。
|
|
114
|
+
升级时还停驻、提交体里带非空 `allow` 的任务 —— 不论是 7.115.0–7.116.x 写下的(当年 allow 只「表态」)还是更早的(当年是名单)—— 续跑一律
|
|
115
|
+
409 `checkpoint.unsupported_version`,行与卡一个字节不动(每个重放入口同一落点,含终态 run 的 steer 铸唤醒卡之前 —— 当场 409、不铸卡),出路是取消后作为新任务重提。升级前用随包排空判据数清零:
|
|
116
|
+
SQL 后端跑 `scripts/drain-check.mysql.sql` / `scripts/drain-check.pg.sql` 里 `permissions-allow-list` 那一段(期望 `hit_rows = 0`);
|
|
117
|
+
local 后端跑 `node scripts/drain-check-local.mjs`(末行 `PERMISSIONS-ALLOW-LIST rows=0`)。
|
|
118
|
+
2. **`APPROVAL_GRANT` 语义变宽(旋钮不改名)**。此前它只回答「没人表态」那一问、只收工具名形;本版起它是引擎 allow 层的部署成员
|
|
119
|
+
(每只 Runner 同一席,含 leader 与 run-local),清「普通问 + 没人表态」两类,**内置工具也在内**:`APPROVAL_GRANT=Bash` 此前什么也不放,本版起
|
|
120
|
+
整只 shell 不再问;`APPROVAL_GRANT=Write` 起文件写不再问。升级前逐条看一遍表里的内置工具名。`auto` 模式例外:引擎把 allow 层里的整只 shell 工具与
|
|
121
|
+
启动器前缀形(`python*` / `node` / `npx` 一类)剔出去交分类器 —— 请求 allow、技能清单、运维表三张同样。永远清不掉的:任何一层的 deny、
|
|
122
|
+
`APPROVAL_REQUIRE`、`APPROVAL_NEVER_AUTO`、`MANUAL_MODE_SHELL_GATE=always`、写保护路径、敏感写路径、治理、PreToolUse hook 的问、plan 模式。
|
|
123
|
+
同一工具既在 `APPROVAL_GRANT` 又在 `APPROVAL_REQUIRE` / `APPROVAL_DENY` ⇒ ask / deny 赢。
|
|
124
|
+
启动校验跟着引擎走:引擎读成空的条目(`*`、`mcp__*`、写路径形如 `Edit(src/**)`、域名形、带空白的名字)、协议名服务器段铸不出 ⇒ 拒启,
|
|
125
|
+
句子点名那一条、原因词与写法;内容形(`Bash(git:*)`、`Read(//srv/data/**)`)受理;退役名 / 短名照旧拒启(名字层四张表 —— 运维表、技能两只清单、请求三桶 —— 同一只判官、同一组句子);超过 1000 条拒启
|
|
126
|
+
(引擎每次准备都逐条播种这张表,平方级 —— 与请求 allow 桶、技能两只清单同一只帽)。
|
|
127
|
+
起服日志行 `approval_grant_in_force` 的 `note` 写着当前效果。
|
|
128
|
+
3. **卡面 / 帧披露键改名**:`tool_approval` 帧、`approval_request` 呈卡帧、`card_json`、重放帧上的 `persistedRuleShadowed`(字符串)与运维待批队列
|
|
129
|
+
行上的 `riskDescriptor.shadowedRule` 删除(无别名),换成 `allowRuleShadowed: { member, entries, omitted? }[]`(`member` ∈ `hook` / `deployment` /
|
|
130
|
+
`persisted` / `skill` / `request`;hook 的 `entries` 恒空)。卡上那句「命中但清不掉」的话在 `message` 里(引擎写的)。SQL 后端升级前停驻的行起服时
|
|
131
|
+
一次性改写成新键(`checkpoint.risk_descriptor` 那一列);local 后端的停驻行、两种后端已落盘的 run 结果(`checkpointGate`)上旧键原样留着
|
|
132
|
+
(只缺结构化披露,文本照旧脱敏显示)。
|
|
133
|
+
**滚动升级(SQL 多副本)前等在途的流内审批卡清零**:旧副本写下的流内卡(`approval_ask.card_json` 带旧键)新副本读不动 —— 断连重放看不到它、
|
|
134
|
+
同一只 ask 重入时停驻(声明无人值守即拒的部署上直接拒这次调用)、对它答「记住规则」得 `rule_material_absent`(裁决照常生效,规则不落)。
|
|
135
|
+
不写迁移(这些卡随流内审批窗短命)。怎么数:`SELECT COUNT(*) FROM approval_ask WHERE state = 'STREAM_PENDING' AND card_json LIKE '%"persistedRuleShadowed"%';`
|
|
136
|
+
期望 0(或接受窗内这些卡不重放)。local 后端是单进程,升级 = 重启,活的流内卡随进程结束(起服收敛把它们作废),无需此步。
|
|
137
|
+
4. **部署 PreToolUse hook 回 `allow` 现在清问**。此前 hook 的 allow 只回答「没人表态」;本版起它是 allow 层第一个成员:default 模式下写 / shell
|
|
138
|
+
不出卡,auto 不过分类器(plan 模式与运维的强制类照问)。只想旁观的 hook 不要回 `permissionDecision`;必须到人的问用上面的逐工具旋钮。
|
|
139
|
+
5. **排空技能拒侧清单里有读成空条目的停驻行**。本版的引擎逐字读规则条目:上一版引擎先去首尾空白,所以 `disallowedTools: ["Bash "]` 当年是一条有效的拒,
|
|
140
|
+
本版读成空、什么也不拒。本版对这种体在提交与续跑上同拒:升级时还停驻、提交体里技能(顶层 `skills[]` 或子代 `agents[].skills[]`)的 `disallowedTools`
|
|
141
|
+
带引擎读成空的条目(带空白 / 畸形的名字、`*`、域名形、协议名带通配)的任务,续跑 409 `resume_blocked_by_policy`(`blockedBy: request.field_invalid`,
|
|
142
|
+
句子点名那一条),行与卡不动;每个重放入口同一落点(decide / resume / plan_review / wake / SLA 到期拒扫跳过它 / 终态 run 的 steer 在铸唤醒卡之前当场 409、不铸卡);
|
|
143
|
+
出路是取消、改好条目重提。升级前数清零:SQL 后端跑两份 SQL 里 `skill-deny-inert` 那一段(期望 `hit_rows = 0`;SQL 不能照引擎逐条判读,数的是**真超集** =
|
|
144
|
+
技能〔顶层 / 子代〕`disallowedTools` 非空的停驻行;命中行逐条用服务那一只判精确核 —— 从**装好的新版包**里跑
|
|
145
|
+
`node -e 'import("@sema-agent/server/dist/rule-entry-list.js").then(m => console.log(m.skillDenyInertEntryOf(JSON.parse(process.argv[1]).body) ?? "ok"))' "$(<ctx.json)"`,
|
|
146
|
+
`ctx.json` = `checkpoint_ctx.ctx` 那一格导出的文本(workflow 子代的行用发起会话那一份);打出 `ok` 的行放过,打出句子的要排空;**不要**用「续跑一次看答复」核 ——
|
|
147
|
+
升级前续跑不会被拒,会真把决定执行掉);local 后端 `node scripts/drain-check-local.mjs` 末行之上的 `SKILL-DENY-INERT rows=0`(调服务同一只判,精确;
|
|
148
|
+
已知边界:它对 workflow 子代的停驻行读的是行自己会话的 ctx,而续跑腿读的是发起会话那一份 —— local 车道上有 workflow 子代停驻时,子代那几行把发起会话的
|
|
149
|
+
`checkpoint-ctx/<会话>.json` 信封里的 `ctx` 喂上面那条 `node -e` 再核一遍)。
|
|
150
|
+
**引擎侧同病的另一个持久面(本版不修,如实)**:7.116.x 上技能被激活时引擎把这类条目原串冻在停驻行的技能授予节里;本版引擎续跑时把整节判成读不出,续跑 fail-closed
|
|
151
|
+
答 `checkpoint.invalid_outcome`(reason `skill_grants_damaged` —— 原因词说的是「损坏」,实际是读法换了),后台子代的停驻赎回同样到这一句;本仓的续跑预判与 SQL 判据
|
|
152
|
+
都不覆盖这一面。升级前连同回滚判据 `skill-grants-rollback` 那一段一起数(它数的就是带技能授予节的停驻行),命中的行取消重提。
|
|
153
|
+
6. **技能清单的名字层与请求 allow 同判**。`skills[].allowedTools` / `disallowedTools`(顶层与子代)里写退役名 / 短名(例 `Task`)本版起提交 400(上一版受理,
|
|
154
|
+
引擎读作一个永不命中的名字);句子与请求 `settings.permissions.allow` 桶的同一组。
|
|
155
|
+
7. **未随本版修的既有边界(升级决策知悉)**:无耐久席位的部署上 `APPROVAL_REQUIRE` 整只不生效(既有);「总是允许」的结算判据漏 `ask_rule` 类(既有)。
|
|
156
|
+
|
|
157
|
+
**行为与面的变化**
|
|
158
|
+
|
|
159
|
+
- **运维日志事件改名(按事件名配告警 / 数行的要改配)**:存量停驻体「本版不重放」的拒现在只有一只判据(allow 读法记号 / 技能拒侧读成空两问),日志跟着收成一种字段形
|
|
160
|
+
(`kind` ∈ `allow_list_reading` / `skill_deny_inert`,allow 臂带 `era`、技能臂带 `layer`,`note` 一句出路):续跑入口 `resume_refused_allow_list_reading` → **`resume_refused_stored_body`**;
|
|
161
|
+
SLA 到期拒扫跳过臂 `deny_sweep_allow_list_reading_skipped`(字段 `sessionIds`)→ **`deny_sweep_stored_body_skipped`**(字段 `skipped: [{ sessionId, kind }]`);
|
|
162
|
+
终态 run 的 steer 铸唤醒卡前的拒新增 **`steer_wake_park_refused_stored_body`**(带 `taskId`)。
|
|
163
|
+
- **终态 run 的 `POST /v1/runs/:id/steer` 多一种答复**:这条会话的存量体本版不重放 ⇒ 铸唤醒卡之前当场拒、不铸卡 —— allow 读法记号那一问 409 `checkpoint.unsupported_version`,
|
|
164
|
+
技能拒侧读成空那一问 **409 `resume_blocked_by_policy`**(与 `/wake` 对同一体的答复逐字同;7.116.x 上是 202 `parked_for_wake` 再在 `/wake` 时拒)。
|
|
165
|
+
- 请求的 `settings.permissions.allow`:原样(顺序、空白、重复)交给引擎;内容形受理;条数上限仍是 1000(超了 400 `over_cap`,句子改说「合并成更宽的条目」);空串不再 400(引擎读成
|
|
166
|
+
`grammar` 并通告);写路径形 / 命令规则等内容形不再 422(7.116.x 的 allow 桶拒内容形);`*` / 带空白的名字上一版本就受理 —— 本版这类引擎读成空的条目收通告 `permission.allowed_tool_not_granted`(受众 = 用户),不拒。
|
|
167
|
+
退役名(`Task` / `bash` …)与无协议前缀的短名照旧 422,与 deny / ask 同一句(本仓只判名字,不判内容)。
|
|
168
|
+
协议名服务器段铸不出 / 缺段 / 写通配(`mcp__*`)仍 422(与技能清单同一判);`Read(~/…/**)` 受理(引擎的读目录形只有 `Read(//abs/**)`,`~/` 形读成空并通告)。
|
|
169
|
+
- 请求 `settings` 的 422(编不成生效规则 / 带永远不生效的条目):`error` 句首由 `settings are tighten-only and cannot loosen the deployment policy: ` 改为
|
|
170
|
+
`request settings cannot be applied as written: `(无别名);按句首字面匹配的客户端改读 `errorCode` 或新句首。
|
|
171
|
+
- 引擎过境的三条(本服务用户感受得到):auto 模式下持久的启动器前缀规则(`Bash(npm run:*)`)改交分类器(多问);被激活技能里的 shell 命令规则在 auto 下
|
|
172
|
+
命中即免问(少问);守卫 / 规则读不出 / 连拒回落三类问不再被持久规则与技能清单清掉(多问)。`permission_denied_total{deniedBy}`:被打断 ∧ 规则拒的调用
|
|
173
|
+
从 `ask_resolution` 桶挪到 `policy` 桶。
|
|
174
|
+
- 请求自带技能的 `allowedTools` 与 `settings.permissions.allow` 读法完全相同(此前前者更宽)。技能两只清单各自超过 1000 条 ⇒ 400(顶层与子代两处;
|
|
175
|
+
此前无帽 —— 引擎逐条播种是平方级,与请求 allow 桶同一只帽)。
|
|
176
|
+
- 技能的 `disallowedTools` 里引擎读成空的条目(`*`、域名形、协议名带通配、带空白 / 畸形的名字)提交即 400(顶层 `skills[]` 与 `agents[].skills[]`);
|
|
177
|
+
续跑重放同一份体 ⇒ 409 `resume_blocked_by_policy`(同一句,见升级前必读第 5 条)。
|
|
178
|
+
- 通告码 `skills.allowed_tool_not_granted` 改名 `permission.allowed_tool_not_granted`(受众从运维改为用户,上会话流;`detail.entries[].source` = `skill` /
|
|
179
|
+
`request` / `deployment`)。按码路由的消费方换词。
|
|
180
|
+
- 能力位:`GET /v1/capabilities` 的 `settings.permissions` 新增 `allowLayer: true`(恒真);`allowGrant` 仍为真。客户端据 `allowLayer === true` 判断
|
|
181
|
+
这台 server 把请求的 allow 交给引擎 allow 层(键缺席 = 7.115.0–7.116.x:allow 只表态,内容形 422)。
|
|
182
|
+
- 新错误码 `config.image_invalid`(同步腿 400):请求内联图片的类型不在 `image/png` / `jpeg` / `gif` / `webp`、字节与声明的类型签名不符、或超 5 MiB
|
|
183
|
+
⇒ 第一次模型调用之前拒。修前是模型供应方的报错。发送 API 自己的类型词与文件的原样字节。
|
|
184
|
+
- `tool_end.gate.disposition`:一只被打断的调用若同时被规则拒,`deniedBy` 由 `ask_resolution` 变 `policy`。
|
|
185
|
+
- 滚动升级窗 / 回滚(如实):新旧副本并存时,在新副本读到 `allowLayer` 后提交的 allow 若落到旧副本,被按「表态」读 —— 工具名形多问(方向安全),
|
|
186
|
+
**内容形 allow(`Bash(git:*)`、`Read(//dir/**)`、`Edit(src/**)`)在旧副本上整请求 422**(那一版的受理口不收内容形);并且排空判据清零之后,旧副本仍会写下
|
|
187
|
+
记号是 `"statement"` 的停驻行(它带 allow 的新提交),这些行到了新副本上续跑被拒 —— 滚动窗内对带 allow 的停驻行再跑一次排空判据,或整队一次切换。
|
|
188
|
+
回滚到 7.116.x 之前,本版写下的停驻行(记号 `allow_layer`)在旧版上被拒(旧版只认 `statement`)—— 回滚前同样排空。
|
|
189
|
+
|
|
190
|
+
### 7.117.0 线 —— 出网代理:http 目标改走绝对 URL 形请求(不再对代理发 `CONNECT host:80`)
|
|
191
|
+
|
|
192
|
+
- **行为变了(出网代理后面的 http 目标)**:配了 `HTTP_PROXY` / `HTTPS_PROXY`、目标是 `http://` 地址(典型:`http://` 模型网关)时,本服务现在向代理发**绝对 URL 形请求**
|
|
193
|
+
(`POST http://gateway/v1/chat/completions`),不再发 `CONNECT gateway:80`;https 目标照旧 `CONNECT host:443`。只放行 `:443` 隧道的代理(squid 缺省策略)原先拒掉 http 网关的 `CONNECT :80`、run 约 3 分钟后失败 —— 本版起可用。
|
|
194
|
+
范围:目标 `http:` 且代理 URL 也是 `http:`;`https://` 代理 + http 目标仍 `CONNECT`。`NO_PROXY`、回环恒直连、代理认证头(userinfo ⇒ `Proxy-Authorization`)不变。
|
|
195
|
+
- **① 谁受影响**:(a)代理只认 `CONNECT`、不转发绝对 URL 形请求的部署(罕见)—— http 网关会在代理处被拒(`407` / `403` / `400` 视代理而定)。出路:网关改 https,或把它写进 `NO_PROXY`。
|
|
196
|
+
(b)在代理上按 `CONNECT` 记账 / 鉴权 / 审计的部署 —— http 目标不再出现 `CONNECT` 记录,而是明文请求行。
|
|
197
|
+
- **② 🔴 凭据头会经过代理**:http 目标的请求行与头(含发给网关的 `Authorization` 等凭据头)以**明文**经过代理,代理看得到完整 URL 与头(隧道里则是密文)。**要对代理保密,网关用 https。**
|
|
198
|
+
(此前 http 网关走 `CONNECT :80` 隧道,代理看到的也是明文字节但不解析请求;现在代理按请求解析并可能记录 URL 与头。)
|
|
199
|
+
- **③ 代理拒绝的读法(仅 http 目标那一腿)**:绝对 URL 形里代理的回话是普通 HTTP 响应。`407`(要求代理认证)仍读作代理拒 —— `via outbound proxy (from HTTP_PROXY) ← proxy answered the request with HTTP 407`
|
|
200
|
+
(与 CONNECT 腿的 `… answered CONNECT with HTTP 407` 同一只错误、同一个包装句,只差根因句里的一个词);`403` 等其余状态**不再**是代理拒:它与网关自己回的 403 在 HTTP 层原理上分不开,
|
|
201
|
+
调用方读到一条普通 403 响应(「网关回了 403」)—— 代理的 ACL 拒绝请对着代理日志查(此前 http 网关被代理的 `CONNECT :80` 403 拒掉时读到 `… answered CONNECT with HTTP 403`)。
|
|
202
|
+
代理**拨号**失败(拒连 / 解析不了 / 超时 / 代理 TLS)两条腿同一个句式(`via outbound proxy (from HTTP_PROXY) ← …`)、同一个 `proxyInUse`。
|
|
203
|
+
- **④ 补偿**:没有旋钮 —— 出路就是上面的两条(网关改 https / 写进 `NO_PROXY`)。回滚到 7.116.x 即恢复 `CONNECT :80`。
|
|
204
|
+
|
|
10
205
|
## 7.116.0 —— `@sema-agent/core` 7.40.1 → 7.41.2:技能 `manifest` 退役(400)· 技能清单从围栏改成本次 run 的规则来源(也是一次放宽)· 引擎 `Skill` 卡上「本会话放行」只放行一次(新卡面位 `rememberRefused`)· 新请求键 `allowedTools` / `disallowedTools` / `userInvokedSkills` 与能力位 `userInvokedSkills` / `skillToolLists` · 停驻的 `Skill` 卡两种拒法 · 回滚到 7.115.x 前排空停驻行 · 排空配方进包 · 新询问类 `requires_user_interaction` · 停驻批准后的调用再过一次 PreToolUse · 文件店拒绝句 / 会话同步导出 / 「没人能答」拒句 / 旧工具名拒体的四处读面小改 · 服务器段铸不出的协议规则名拒 · local 重启后对上一世审批卡的作答 200 → 410 · run 死亡判决的机读码 `errorCode`(含启动期一次性迁移补码)· 两段覆盖形 `mcp__<server>` 在配置表受理 · steer 的 `priority` 交给引擎(能力位 `fleet.steerPriority` → `"engine"`)· 能力位与端点的不可用答法读同一判据(多族 403 / 404 → 501、`a2aServe` 在无后端部署上由真变假、新位 `sessionSyncFileHistory` / `workflowsJournal`)· 后台子代三张读面的文本变了 · run-local 无终端拒句按问的类说话
|
|
11
206
|
|
|
12
207
|
- **🔴 技能的 `manifest` 退役(硬破坏,零别名)**。`body.skills[].manifest` 任何值 ⇒ 400 `request.field_invalid`(句子 `skill.manifest was retired in core 7.41.0 — …`);
|
package/USAGE.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 调用方手册(OA / 智能体 / 你自己)
|
|
2
2
|
|
|
3
3
|
> 一句话心智模型:**你只对一个地址发普通 HTTP 请求——LB `http://<host>:8090`;请求体里只写"要做什么"。**
|
|
4
|
-
> 工具面与安全策略由服务端 `resolveSpec` 注入,请求体**替换不了**它们(`tools` 不是受理键,传了 400;按请求只能**减**:`excludeTools` / `settings.permissions` 的 deny · ask;7.
|
|
4
|
+
> 工具面与安全策略由服务端 `resolveSpec` 注入,请求体**替换不了**它们(`tools` 不是受理键,传了 400;按请求只能**减**:`excludeTools` / `settings.permissions` 的 deny · ask;7.117.0 起 `settings.permissions.allow` 整份交给引擎的 allow 层:表里的调用免问(普通问与没人表态两类,内置工具的写门 / shell 普通问也清),deny / ask 规则与运维的强制类照赢;能力位 `settings.permissions.allowLayer`)。
|
|
5
5
|
> `model` 与 `systemPrompt` / `appendSystemPrompt` 是受理键,但 `model` 只能点服务端已配置目录里的名字 / 档位词 / 模型 id(未知 ⇒ 400)。凭据永远不进模型 / 请求体。
|
|
6
6
|
> 你不"调度 worker",你只发任务;LB + TiDB 自己协调。每个字段都来自当前真实代码。
|
|
7
7
|
|
|
@@ -172,7 +172,7 @@ WEB_SEARCH_PROBE_ON_BOOT=true # boot 期一次性真出网探活,
|
|
|
172
172
|
| env 键 | 类型 | 默认 | 缺席行为 | 坏值行为 |
|
|
173
173
|
|---|---|---|---|---|
|
|
174
174
|
| `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` |
|
|
175
|
-
| `WEB_SEARCH_API_KEY` | secret string | 无 | brave/tavily:后端仍会装配(装配只看 `PROVIDER`),**首次真实工具调用**时抛 `WEB_SEARCH_API_KEY is required for the <provider> provider`(`braveSearch` / `tavilySearch` 的首行守卫);searxng:本键无消费点 | 空串 /
|
|
175
|
+
| `WEB_SEARCH_API_KEY` | secret string | 无 | brave/tavily:后端仍会装配(装配只看 `PROVIDER`),**首次真实工具调用**时抛 `WEB_SEARCH_API_KEY is required for the <provider> provider`(`braveSearch` / `tavilySearch` 的首行守卫);searxng:本键无消费点 | 空串 / 纯空白同缺席。内容只有一条形律:**能当 HTTP 请求头值**(brave 放 `X-Subscription-Token`,tavily 放 `Authorization: Bearer <key>`;判的是实际发出去的那一串)。含换行 / NUL / 其它控制字符 / U+007F / U+00FF 以上字符等头不能携带的字符 ⇒ **拒启**(`config.web_search_invalid`,句 `env WEB_SEARCH_API_KEY must be usable as an HTTP header value (it contains a character an HTTP header cannot carry, such as a line break)`,**不回显值** —— 7.118.0 起;改前采纳,首次搜索时运行时抛的错误句带着整串明文)。判据就是运行时构造并发出请求头的那条流水线(Node 自带的两步:`Headers` 构造的归一化 + 发送层的字符表;不是另一张字符表):首尾空白(含换行)归一化后不剩,所以从密钥文件带出的尾随换行照收;searxng 不发鉴权头,但这一键照判(按裸 key)。出路:修好值,或不用这一键时 unset 它 |
|
|
176
176
|
| `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`,**不回显值**(这一格可能带凭据) |
|
|
177
177
|
| `WEB_SEARCH_MAX_RESULTS` | number | `10` | 用默认 10 | 空串同缺席;**非数字或 < 1 ⇒ 拒启**(`config.web_search_invalid`;改前静默落默认 10);是数则向下取整,再夹到 `[1,20]`(设 999 被夹到 20 —— 夹紧不是形错) |
|
|
178
178
|
| `WEB_SEARCH_TIMEOUT_MS` | number(ms) | `10000` | 用默认 10000 | 空串同缺席;**非数字(`"30s"` 这类带单位的也算)或 < 1 ⇒ 拒启**(`config.web_search_invalid`;改前静默落默认 10000 —— 写了 30s 的运维实际拿到 10s);是数则向下取整,再抬到 ≥ 1000ms |
|
|
@@ -860,10 +860,10 @@ UNATTENDED_APPROVAL_POLICY=park
|
|
|
860
860
|
**「本会话不再询问」对引擎的问也生效(7.106.0 起)**
|
|
861
861
|
|
|
862
862
|
- 审批面 `POST /v1/approvals/:sessionId/decide {decision:"approve", remember:"session"}` 与活卡的 `allow_session` 记下的放行,两处兑现:
|
|
863
|
-
|
|
863
|
+
本服务自己的 `ordinary` 类问(`run_workflow` 问句、acceptEdits 的域外写)在它们自己的层上不再问;**引擎自己的问**(`default`
|
|
864
864
|
模式下的每一次写、证不了只读的 shell 命令)由服务的**审批席**(工具审批协调器)在下一次到达时代答。后一半要审批席在场:
|
|
865
865
|
`TOOL_APPROVAL_ENABLED` 开(多租户缺省关);不想要活卡(纯 park 部署)再配 `STREAM_ASK_WINDOW_MS=0`。
|
|
866
|
-
- 代答不到的:`APPROVAL_DENY` / 敏感路径守卫(是拒,不是问);`APPROVAL_NEVER_AUTO` 与耐久预算耗尽的问(「恒问人」,回执 `rememberApplied:false`);
|
|
866
|
+
- 代答不到的:`APPROVAL_DENY` / 敏感路径守卫(是拒,不是问);`APPROVAL_REQUIRE` 名单的问(`ask_rule` 类 —— 引擎的类表说 allow 层清不掉它,站着的答案同读;每次照问,回执 `rememberApplied:false`;要免问就把工具移出名单或开自动批额度);`APPROVAL_NEVER_AUTO` 与耐久预算耗尽的问(「恒问人」,回执 `rememberApplied:false`);
|
|
867
867
|
委派子代经父链继承到祖先席上的引擎问(引擎盖强制位,席依约不代答)。
|
|
868
868
|
|
|
869
869
|
**无人值守腿:`default` 模式 + 按类作答的审批席(7.109.0 起;S-934 / S-906)—— 没有旋钮**
|
|
@@ -881,34 +881,31 @@ UNATTENDED_APPROVAL_POLICY=park
|
|
|
881
881
|
(`config.env_key_retired`,任何值都拒);从 `.env` 里删掉它。要让机器多答某一类问,先立案,不开旋钮。
|
|
882
882
|
- 写面:`default` 模式不带写面 ⇒ 工作区根内(`roots`,与 7.105.0 同)。 `roots` 只约束 Write / Edit 这类结构化文件工具;shell 的根外写、Read 工具的根外读属 core `ordinary`,这些腿上由机器答(落地结果与 ≤7.108.0 的 bypass 放行同,门记录归属词见 MIGRATION 7.109.0「审计读者」)。
|
|
883
883
|
|
|
884
|
-
**可选 — 运维的 allow
|
|
884
|
+
**可选 — 运维的 allow 表 `APPROVAL_GRANT`(缺省关;7.117.0 起语义变宽)**
|
|
885
885
|
|
|
886
886
|
```bash
|
|
887
|
-
APPROVAL_GRANT='mcp__figma__*,a2a__planner__
|
|
887
|
+
APPROVAL_GRANT='mcp__figma__*,a2a__planner__*,Bash(git:*)' # 逗号分隔的 allow 规则:这些调用不用问
|
|
888
888
|
```
|
|
889
|
-
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
- **不是白名单**:它只给出表态,不收窄任何东西(「只许这些工具」是会话规则 `PUT /v1/sessions/:id/policy` 的 `toolAllow`)。
|
|
889
|
+
- **是什么**:引擎 allow 层的**部署成员**(`RunnerDeps.allowedTools`)。本进程造的**每一只** Runner 都带同一席(HTTP 提交 / durable 续跑 / 停驻后台 agent 赎回 /
|
|
890
|
+
`run-local` / leader 的首跑与修复腿,一条规则不随腿而异),Runner 构造时读一次;只走 env,改了要重启。命中 = 免问:清「普通问 + 没人表态」两类,
|
|
891
|
+
**内置工具也在内** —— `APPROVAL_GRANT=Bash` 起整只 shell 不再问,`Write` 起文件写不再问(7.115.0–7.116.x 只回答「没人表态」、只收工具名形)。
|
|
892
|
+
- **为什么要它**:询问类模式下没人表态的 MCP 服务器工具 / A2A 技能调用会出卡;请求腿可以在请求体里写 `settings.permissions.allow`,`run-local` /
|
|
893
|
+
leader 没有请求体 —— 运维为自己接的服务器、自家信得过的命令,在这里写一次。
|
|
894
|
+
- **值形(与引擎的文法相同)**:工具名、`mcp__<server>__*` 或两段形 `mcp__<server>`、`a2a__<peer>__*`、`Bash(<命令>)` / `Bash(<前缀>:*)` 命令规则、
|
|
895
|
+
`Read(//<dir>/**)` 读目录。`auto` 模式下交出任意代码的条目(整只 shell、解释器 / 启动器前缀)交分类器判(引擎的剔除表)。
|
|
896
|
+
坏值 ⇒ **启动拒**(句子点名那一条、引擎给的原因词、怎么写):引擎读成空的条目(`*`、`mcp__*`、写路径形 `Edit(src/**)`、域名形、带空白的名字;
|
|
897
|
+
7.118.0 起条目名也过引擎的判官 —— 退役名 / 无前缀短名 / 协议名服务器段铸不出读成 `tool_name`,句子带现名)、工具段为空的协议名(`mcp__srv__`)。
|
|
898
|
+
内容形里带逗号的规则写不进(这张表的分隔符就是逗号)。
|
|
899
|
+
- **永远清不掉的**:任何一层的 deny(`APPROVAL_DENY`、请求 / 会话规则的 deny、命令策略拒档、敏感写路径)、ask 规则(`APPROVAL_REQUIRE`、请求的 ask)、
|
|
900
|
+
`APPROVAL_NEVER_AUTO`、`MANUAL_MODE_SHELL_GATE=always`、写保护路径、治理、PreToolUse hook 的问、plan 模式。命中而清不掉时卡上带
|
|
901
|
+
`allowRuleShadowed: [{ member: "deployment", entries: [...] }]`。同一工具既在本键又在 `APPROVAL_REQUIRE` / `APPROVAL_DENY` ⇒ ask / deny 赢。
|
|
902
|
+
- **不是白名单**:它只放,不收窄任何东西(「只许这些工具」是会话规则 `PUT /v1/sessions/:id/policy` 的 `toolAllow`)。
|
|
904
903
|
- **射程如实**:规则对**每一个租户、每一条腿**生效;`mcp__<server>__*` 也覆盖那台服务器将来新增的工具。按需写窄;要收回某一只,写
|
|
905
|
-
`APPROVAL_DENY` / `APPROVAL_REQUIRE`(压过本键)
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
-
|
|
909
|
-
|
|
910
|
-
= 规则名原样(空表 = 未设);`GET /v1/config/catalog` 一行。客户端能力面(`/v1/capabilities`)不报它。
|
|
911
|
-
- **启动告警 `tool_policy_only_sensitive_baseline`**(既有事件,名字不改):没有审批席(`DURABLE_APPROVAL` 关)且不是单用户的部署上,若部署唯一的策略是「不是门」的那几层 —— 敏感路径守卫集(`SENSITIVE_WRITE_PATTERNS`),或 7.115.0 起只有本键 —— 起服时发一次:它们让 `spec.toolPolicy` 在场、引擎自己的 UNGATED 告警因此不再出现,而它们自己又不判写。`detail` 点名是哪一片,`guardedPatterns` / `grantRules` 两个计数。
|
|
904
|
+
`APPROVAL_DENY` / `APPROVAL_REQUIRE`(压过本键),或删掉规则重启。
|
|
905
|
+
- **A2A 如实**:运维声明的对端(配置中心 / `config.d`,刻意不收 `toolAxes`)上,每次技能调用都是引擎自己的外发标记问(`tool_marks_a2a`),allow 层清不掉它;
|
|
906
|
+
只有请求体自带的对端(单用户闸)能在 `toolAxes` 里把某个技能的 `egress` 降为 `false`,那之后才是普通问,本键才清得掉。
|
|
907
|
+
- **看得见**:起服日志一行 `approval_grant_in_force`(条数 + 规则 + 一句当前效果:清什么、清不掉什么);`GET /v1/diagnostics/wiring`(运维面)`serverGates.approvalGrant`
|
|
908
|
+
= 规则原样(空表 = 未设);`GET /v1/config/catalog` 一行。客户端能力面(`/v1/capabilities`)不报它。
|
|
912
909
|
- v1 只有 env 一个写入通道(配置中心与 settings 契约没有这个键)。
|
|
913
910
|
|
|
914
911
|
**可选 — 写保护名表(S-138;缺席=引擎缺省表在岗)**
|
|
@@ -2059,7 +2056,7 @@ TOC 本地形)时被**采纳**;`REQUIRE_PRINCIPAL=true`(多租户)上一段**形
|
|
|
2059
2056
|
| 键 | 类型 | 必填 | 默认 | 形错(⇒ 400) |
|
|
2060
2057
|
|---|---|---|---|---|
|
|
2061
2058
|
| `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 腿词表外是「不装配」(部署自己的缺席) |
|
|
2062
|
-
| `apiKey` | string(明文) | brave/tavily 建议带(缺了首次调用才报错,见下表);searxng 不读 | 无(不继承 env 的 `WEB_SEARCH_API_KEY`);空串同缺席 |
|
|
2059
|
+
| `apiKey` | string(明文) | brave/tavily 建议带(缺了首次调用才报错,见下表);searxng 不读 | 无(不继承 env 的 `WEB_SEARCH_API_KEY`);空串同缺席 | 不是字符串,或不能当 HTTP 请求头值(含换行 / NUL / 其它控制字符 / U+007F / U+00FF 以上字符等;判据 = 运行时构造并发出请求头的那条流水线,与 env `WEB_SEARCH_API_KEY` 同一只;7.118.0 起,句 `settings.webSearch.apiKey must be usable as an HTTP header value (it contains a character an HTTP header cannot carry, such as a line break)`,**不回显值**;改前采纳、首次搜索时运行时抛的错误句带着整串明文)。首尾空白(含换行)运行时会剥掉 ⇒ 照收 |
|
|
2063
2060
|
| `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)`,不回显值) |
|
|
2064
2061
|
| `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 会照常解析(源码头注自述:字段和消费点先落地,配置录入面尚未跟上,是一笔尚未还清的既有债务) |
|
|
2065
2062
|
| `maxResults` | number | 否 | 10(与 env 同一 clamp,`[1,20]`,小数向下取整;999 夹到 20 不是形错) | 不是数,或 < 1(`"10"` 这种字符串也算) |
|
|
@@ -2090,8 +2087,8 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
|
|
|
2090
2087
|
工具在场 ⇔ 调用方的 per-request 配置被采纳(单用户车道)或调用方根本没带(用部署默认)。
|
|
2091
2088
|
|
|
2092
2089
|
**`apiKey` 明文与 env 槽边界**:server 收到的 `settings.webSearch.apiKey` 是**明文字符串**,没有任何服务端
|
|
2093
|
-
密钥槽位/引用间接——收到什么字符串就直接进 backend 闭包(判官 `judgeRequestWebSearch`
|
|
2094
|
-
见 `src/plugins/web-search.ts` 的 `WebSearchBackendConfig.apiKey` 字段注)
|
|
2090
|
+
密钥槽位/引用间接——收到什么字符串就直接进 backend 闭包(判官 `judgeRequestWebSearch` 只判「是不是字符串」与「能不能当 HTTP 请求头值」——后者见上表 `apiKey` 行,与 `WEB_SEARCH_API_KEY` 同一只判官、同一条消费路径,
|
|
2091
|
+
见 `src/plugins/web-search.ts` 的 `WebSearchBackendConfig.apiKey` 字段注)。明文字段的形状维持原样(不是引用、不是槽位名)。调用方(壳)如何在
|
|
2095
2092
|
自己机器上管理这份明文是调用方的事——例如 cli 壳侧的约定是在**调用方自己的环境**里按
|
|
2096
2093
|
`SEMA_WEBSEARCH_KEY_<PROVIDER>` 这样的命名空间存放每个 provider 的 key,由壳在本地读出后把明文塞进请求体;
|
|
2097
2094
|
这纯粹是**客户端约定**,本仓不读取、不校验、也不感知任何这类客户端侧 env 命名(这条命名事实来自任务书,
|
|
@@ -2163,7 +2160,7 @@ docker 推送的鉴权走构建宿主上的 docker 凭据存储(`docker login`),
|
|
|
2163
2160
|
|
|
2164
2161
|
### 9.8 出网代理:`HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY`(7.103.0 起)
|
|
2165
2162
|
|
|
2166
|
-
server 进程(HTTP 服务与 `run-local`)**所有走 undici 的出网跳**都认这三只平台标准变量:引擎 → 模型网关(
|
|
2163
|
+
server 进程(HTTP 服务与 `run-local`)**所有走 undici 的出网跳**都认这三只平台标准变量:引擎 → 模型网关(http 网关发**绝对 URL 形请求**、https 网关经 `CONNECT` 隧道)、
|
|
2167
2164
|
配置中心拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP、hook 模型调用、core 的 MCP streamable-http / A2A / WebFetch 工具 …
|
|
2168
2165
|
实现是**一只**进程级 dispatcher,在启动时装一次;没有另外的开关 —— 这三只变量本身就是配置通道。
|
|
2169
2166
|
|
|
@@ -2174,6 +2171,14 @@ server 进程(HTTP 服务与 `run-local`)**所有走 undici 的出网跳**都认
|
|
|
2174
2171
|
带路径 / 查询的值 undici 用不了 ⇒ **拒启**,拒句带码 `[config.outbound_proxy_invalid]`(带 `scheme://` 却在 host:port 之后还有路径 / 点段 / 查询 /
|
|
2175
2172
|
片段的值,本仓先于 undici 拒 —— 这一道各运行时同,Bun 下也拒);回显只出 `scheme//host[:port]`,形不干净(有路径 /
|
|
2176
2173
|
查询 / 片段 —— 未编码的特殊字符会让密码落进这几段)整只占位 `«redacted:url»`。Bun 运行时不校验(见下)。
|
|
2174
|
+
- **http 目标与 https 目标走不同的代理形(7.117.0 起)**:目标是 `http://` ⇒ 向代理发**绝对 URL 形请求**(`POST http://gateway/v1/chat/completions`),不建 `CONNECT` 隧道;
|
|
2175
|
+
目标是 `https://` ⇒ `CONNECT host:443`,隧道里再握手。7.116.x 及以前 http 目标也被发 `CONNECT host:80` —— 只放行 `:443` 隧道的代理(squid 缺省策略)拒掉它,run 约 3 分钟后失败。
|
|
2176
|
+
范围按 undici 划:目标 `http:` **且**代理 URL 也是 `http:` 才走绝对 URL 形;`https://` 代理 + http 目标仍是 `CONNECT`。
|
|
2177
|
+
🔴 **要知道的三件**:① **明文过代理**:http 目标的请求行与头(含发给网关的凭据头)以明文经过代理,代理看得到完整 URL 与头;要对代理保密,网关用 https(隧道里是密文)。
|
|
2178
|
+
② 代理按 `CONNECT` 记账 / 鉴权、且不转发绝对 URL 形请求的部署(罕见):把该网关改成 https,或把它写进 `NO_PROXY`。
|
|
2179
|
+
③ **代理拒绝的读法**:`CONNECT` 腿上代理回非 200 ⇒ 下面「代理答了拒」那一条(短阶梯 + 按状态选的句子);绝对 URL 形里代理的回话是**普通 HTTP 响应**,分两种 ——
|
|
2180
|
+
**407(要求代理认证)同样读成代理答了拒**,根因 `proxy answered the request with HTTP 407`(407 按协议只可能来自代理,两条腿同一种终局;一个自己回 407 的源站是违约,也会被读作代理);
|
|
2181
|
+
**403 等其余状态不是代理拒**:它与网关自己回的 403 在 HTTP 层原理上分不开(没有通用判别式),调用方读到一条普通 403 响应(「网关回了 403」),代理的 ACL 拒绝请对着代理自己的日志查。网关自己回的 401 / 403 照原样交给调用方,不会被读成代理拒。
|
|
2177
2182
|
- **`NO_PROXY` 语法 = undici 的**:逗号或空白分隔;`host` 精确匹配**且含其子域**;`.host` / `*.host` 后缀;`host:port` 限端口;整串 `*` = 全直连;
|
|
2178
2183
|
IPv6 写方括号形 `[fd00::1]`。**不支持 CIDR / IP 段**(`10.0.0.0/8`、`10.*` 都不生效):按 IP 访问的目标逐个列(可带 `:port`)。
|
|
2179
2184
|
- 🔴 **回环恒直连,不看 `NO_PROXY`**:目标是 `localhost` / `127.0.0.0/8` / `::1` / `::ffff:127.x` / `0.0.0.0` / `::` 时一律直连 —— 本机网关、本机中心
|
|
@@ -2185,21 +2190,38 @@ server 进程(HTTP 服务与 `run-local`)**所有走 undici 的出网跳**都认
|
|
|
2185
2190
|
行为一致,只多回环直连。不设它也一样生效。
|
|
2186
2191
|
- **代理那一跳失败时怎么读**(Node 运行时):连代理的每一次拨号 —— 拒连、解析不了、超时、`https://` 代理自身的 TLS、收下连接就关、CONNECT 回
|
|
2187
2192
|
非 200 —— 失败时,引擎的终局句(与 `run_failed` 日志行)形如
|
|
2188
|
-
`fetch failed (via outbound proxy (from HTTPS_PROXY) ←
|
|
2189
|
-
租户可见的失败原因:**包装句**(`via outbound proxy (from <变量>)`)点明「经代理 +
|
|
2190
|
-
|
|
2193
|
+
`fetch failed (via outbound proxy (from HTTPS_PROXY) ← ECONNREFUSED)` / `… ← proxy answered CONNECT with HTTP 403)` —— 这句会进
|
|
2194
|
+
租户可见的失败原因:**包装句**(`via outbound proxy (from <变量>)`)点明「经代理 + 哪只变量」,`←` 之后**只有码**(`ECONNREFUSED` / `ENOTFOUND` /
|
|
2195
|
+
`UND_ERR_SOCKET` / `ERR_TLS_…` 等)或本服务自己写的无地址句子(`proxy answered … with HTTP <状态码>` / `proxy did not answer CONNECT within …`)—— **整句没有代理的
|
|
2196
|
+
host:port、没有 userinfo**:上游错误原文(`connect ECONNREFUSED 10.0.0.9:3128` 这类点名代理地址的句子)在代理那一跳上被收成码,不上 wire、不进 `String(err)`。
|
|
2197
|
+
**要地址去运维面**:运维日志 `outbound_connect_failed` 的 `proxyHop`(原句,含代理 host:port)与启动日志 `outbound_proxy` 行(display-safe 代理值);诊断面
|
|
2198
|
+
`GET /v1/diagnostics/wiring` 与用户通告仍只报变量名。直连失败不在此列(目标的地址本来就是失败原因的一部分)。代理拨号有一只
|
|
2191
2199
|
**从拨号起计的绝对期限** 10 s(= undici 连接阶段的缺省时限;连代理的 TCP / TLS 与 CONNECT 交换都算在内,代理中途吐字节也不续命)—— 到点
|
|
2192
2200
|
CONNECT 还没回完 ⇒ 根因 `proxy did not answer CONNECT within 10000 ms`、那条连接随之关掉;调用方先取消时请求当场结束,挂着的那次拨号同样
|
|
2193
|
-
|
|
2194
|
-
|
|
2201
|
+
在期限内收口、不留连接。**一次拨号**失败当场收口、dispatcher 不在原地重连(代理收下连接就关也一样)—— 这说的是传输层的那一次请求;
|
|
2202
|
+
**run 层**还会按引擎的重试阶梯重发这次模型调用,阶梯长短由失败的形决定(见下一条)。隧道建好之后的失败(目标的 TLS、
|
|
2203
|
+
目标的响应)不带这段前缀 —— 那是目标本身的事。**http 目标(绝对 URL 形)那一腿**:拨号失败(拒连 / 解析不了 / 超时 / 代理 TLS)同一个包装句、同一个 `proxyInUse`;
|
|
2204
|
+
10 s 拨号期限与「CONNECT 回非 200」只属于 `CONNECT` 腿 —— 绝对 URL 形的请求发出之后就是普通请求,代理收下连接就关、中途断流、回 4xx(407 除外,见上一条③)都与目标自己的失败同形,不冠这段前缀。
|
|
2205
|
+
- **代理答了拒 = 确定的答案,短阶梯(7.118.0 起;Node 运行时)**:代理**回了话**而且是拒绝 —— `CONNECT` 回非 200(403 / 407 / 502 …),或绝对 URL 形那一腿回 407 ——
|
|
2206
|
+
代理那一跳的错误带引擎的确定性连接码 `ERR_PROXY_TUNNEL_REFUSED` 与代理说的状态码 ⇒ 引擎不再走长阶梯:缺省重试 2 次(≤1 s 退避,约 2 s 内终局),
|
|
2207
|
+
等待帧写 `proxy refused the tunnel, retrying briefly`,终局句以按状态选的一句开头 —— 407 `proxy authentication required (HTTP 407)`、
|
|
2208
|
+
403 `proxy forbids this destination (HTTP 403)`、其余 `proxy refused the tunnel (HTTP <n>)` —— 之后 ` — ` 接原来的失败链,形如
|
|
2209
|
+
`[network] proxy forbids this destination (HTTP 403) — fetch failed (ERR_PROXY_TUNNEL_REFUSED: via outbound proxy (from HTTPS_PROXY) ← proxy answered CONNECT with HTTP 403)`
|
|
2210
|
+
(整句仍无代理地址与凭据)。7.117.x 及以前这一形读不出,走满长阶梯(缺省 10 次重试、约 3 分钟)才终局,等待帧一直说「connection lost, reconnecting」。
|
|
2211
|
+
代理的状态码**不**当作模型服务商的状态发布(`terminal.detail` / `apiFailure.status` 不带它 —— 那些位说的是服务商回了什么)。
|
|
2212
|
+
**拨号失败**(拒连 / 解析不了 / 收下就关 / 静默到 10 s 期限 / 代理 TLS)不带这枚码,阶梯照旧由下面那枚码决定:拒连 / 解析不了确定、收下就关 / 静默 / TLS 不确定(长阶梯)。
|
|
2213
|
+
🔴 **谁受伤**:代理**短暂**拒隧道、几十秒后恢复(凭据轮换、策略推送中)的部署,以前靠长阶梯熬过去,现在约 2 s 就失败、要重跑。
|
|
2214
|
+
**补偿**:本版没有恢复长阶梯的旋钮 —— 引擎恢复它的座位(`deterministicConnectRetries` ≥ 重试上限)本服务没有接出来,`GATEWAY_MAX_RETRIES` 调小只会更早终局;
|
|
2215
|
+
出路是修好代理的策略 / 凭据后重跑。按 407 / 403 分支的消费方今天读句子里的 `(HTTP n)`;结构化的状态位引擎本版没有。
|
|
2195
2216
|
- **启动日志**:恰一行 `outbound_proxy`,`{httpProxy, httpsProxy, noProxy, loopbackDirect, runtime}`(代理值 display-safe;`httpsProxy` 是 https
|
|
2196
2217
|
目标**实际**走的那只,含回落)。配置目录 `GET /v1/config/catalog` 有 `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` 三行(`limitsHttp` 组,回显同一份
|
|
2197
2218
|
display-safe 投影)。`/health` 与 fleet 公告**不**带代理信息。
|
|
2198
2219
|
- **建连失败时怎么读(7.108.0 / S-925;Node 运行时)**:任何一条出网请求在**建连时**就失败(码闭集:`UND_ERR_CONNECT_TIMEOUT` —— TCP + TLS 没在
|
|
2199
2220
|
传输层的建连时限内完成 / `ECONNREFUSED` / `ENOTFOUND` / `EAI_AGAIN` / `EHOSTUNREACH` / `ENETUNREACH`;可能出现在已建立连接上的
|
|
2200
2221
|
`ECONNRESET` / `ETIMEDOUT` 刻意不算)⇒ 三件,同一份事实:
|
|
2201
|
-
① **运维日志** 一行 `outbound_connect_failed { targetHost, code, proxyInUse, proxyReadAtMs, sinceBootMs, hint }` —— `proxyInUse` = 这一跳走的代理
|
|
2202
|
-
**变量名**(`HTTP_PROXY` / `HTTPS_PROXY`,不打值;直连 ⇒ `null`),`
|
|
2222
|
+
① **运维日志** 一行 `outbound_connect_failed { targetHost, code, proxyInUse, proxyHop?, proxyReadAtMs, sinceBootMs, hint }` —— `proxyInUse` = 这一跳走的代理
|
|
2223
|
+
**变量名**(`HTTP_PROXY` / `HTTPS_PROXY`,不打值;直连 ⇒ `null`),`proxyHop` = 只在这一跳走了代理时在场的**原句**(上游错误原文,含代理 host:port,凭据位已脱敏;
|
|
2224
|
+
这是代理地址在运行时失败里唯一的去处,wire 上没有),`proxyReadAtMs` = 代理变量被读进来的时刻(= 起服那一拍),`sinceBootMs` = 进程已跑多久
|
|
2203
2225
|
—— 两者一对照就知道「起服之后 VPN / 代理是不是换过」;全进程的出网都算(模型、中心、工具 …),每条失败的请求一行(重试各算一次)。
|
|
2204
2226
|
② **诊断面** `GET /v1/diagnostics/wiring` 的 `outbound` 段:`{ proxy: { http, https }, noProxy, loopbackDirect, proxyReadAtMs, lastConnectFailure }` ——
|
|
2205
2227
|
代理只报**名字**(哪只变量供的;`https` 在 `HTTPS_PROXY` 缺席时回落 `HTTP_PROXY`)、`NO_PROXY` 只报在不在,地址一个字节都不上本段;
|
|
@@ -2250,7 +2272,7 @@ server 进程(HTTP 服务与 `run-local`)**所有走 undici 的出网跳**都认
|
|
|
2250
2272
|
引用)。脚本不读 `~/.npmrc`:每条 npm 调用带临时 `--userconfig`,临时 npmrc 只写 `${NPM_TOKEN}` 引用、不写值;开头封口,`npm ci` 的安装脚本 /
|
|
2251
2273
|
构建 / pack-hygiene 测试拿不到任何凭据,只有 `npm publish` 那一个进程拿到 `NPM_TOKEN`(且带 `--ignore-scripts`)。令牌从不打印。
|
|
2252
2274
|
- **门 4 不再等 CI**(同一裁定):`scripts/post-publish-gates.sh` 的门 4 = 一个 45 min 的 npm 窗(公开 packument 上 `versions[<v>]` 在且
|
|
2253
|
-
`dist-tags.latest` 追平)。判决行 `GATES456 v=… rc4=… rc5=… rc6=… shasum=… elapsed=… ciTag=… ciMain=… tagRun=… mainRun=… why4
|
|
2275
|
+
`dist-tags.latest` 追平)。判决行 `GATES456 v=… rc4=… rc5=… rc6=… shasum=… elapsed=… ciTag=… ciMain=… tagRun=… mainRun=… why4=… chain=…`:既有十一键形状不变,末尾追加第十二键 `chain=<本轮标识>`(见下一条起链启动器);
|
|
2254
2276
|
CI 两腿(`ciTag` / `ciMain` / `tagRun` / `mainRun`)是**信息格**,不进 rc4、不进 why4;`why4` 只剩 `propagation_timeout`(rc4 非零)与 `-`(绿)。
|
|
2255
2277
|
自此门 4 常态首拍即绿,CI 两腿多半还在跑 ⇒ 常落 `unchecked:still-running`,如实。7.105.0 起(S-882 ③)CI 信息格见包后**读一次、不轮询**
|
|
2256
2278
|
(此前的有界轮询几乎恒白等 10 min 再落同一个 still-running):落了终态记 conclusion;还在跑 ⇒ `unchecked:still-running` + 一句 `gh run view <id>`
|
|
@@ -2348,7 +2370,9 @@ server 进程(HTTP 服务与 `run-local`)**所有走 undici 的出网跳**都认
|
|
|
2348
2370
|
**从表派生** —— 一门要一个新名字就登记进表,
|
|
2349
2371
|
启动器自动透传(7.104.0 手抄名单漏了 `DEEPSEEK_API_KEY` ⇒ 门 2.4 停链)。起链 shell 里表外的变量(含 core 名形黑名单不认的
|
|
2350
2372
|
`*_PASS` / `*_PWD` / `*_COOKIE`)一条都进不去;值经管道进 `env -i` 起的子 shell,不进任何 argv。远端三条 `GATE_*_TEST_URL` 由它显式传
|
|
2351
|
-
共享库(k3s8 NodePort)
|
|
2373
|
+
共享库(k3s8 NodePort)。链日志按版本追加:门 2 / 门 3.5 的行(`GATE2` / `PUBLISH`)只认起链之后新落的行;**判决行只认本轮标识** —— 启动器起链时铸 `<pid>-<epoch>`,经 `release.sh` 的第一个参数交给它起的 `post-publish-gates.sh`
|
|
2374
|
+
(第二个参数),判决行末键 `chain=` 带回,等待方只读 `chain=` 等于本轮标识的那一行。同版本重起链时,上一轮留下的 `post-publish-gates.sh` 迟到追加的判决行(落在起链偏移之后,按偏移认不出)、
|
|
2375
|
+
十一键老形、手跑的 `chain=-` 行都不是本轮的 —— 没等到本轮的行 ⇒ 退出码 1 并说没出现,不转述别轮的读数。手跑 `release.sh` / `post-publish-gates.sh` 不给标识 = `chain=-`。之后转述门 3.5 的 `PUBLISH` 行;release.sh 红而 tag 已在 ⇒ 立即收尾并指路 `publish-local.sh` + `post-publish-gates.sh`;
|
|
2352
2376
|
绿 ⇒ 等 `GATES456`(窗 = `GATE4_WINDOW_SEC` + 20 min)后转述 `GATE2` / `PUBLISH` / 判决行。退出码见脚本头注(0 = 全绿)。
|
|
2353
2377
|
- **wire 变更机器清单**(S-129 ③,7.105.0 起):`node scripts/wire-changes-server.mjs <version>` 生成 `wire-changes/server@<version>.json`
|
|
2354
2378
|
(形照 core 的 `wire-changes/core@<ver>.json`:`added` / `removed` / `changed` / `narrowed` + `closedSetMembers` + `breaking`)。源 = 契约附录
|