Skip to content

Commit 32ed0b1

Browse files
committed
fix(docs): clarify external tool lists are exact-match only
CHANGELOG recommended EXTERNAL_TOOL_REQUIRE_CONFIRMATION_FOR=delete_*,write_* but policy uses exact Set.has(), so wildcards silently ALLOW destructive tools. Document exact-match only with exact-name examples.
1 parent ca8eafe commit 32ed0b1

2 files changed

Lines changed: 2 additions & 2 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4040
- **Stream tool-error surface unification (Stage 2 + final)**: `/v1/chat/completions`, `/v1/messages`, and `/v1/responses` `stream:true` fail-closed tool errors (`parallel_external_tool_calls`, `external_tool_choice_*`, `invalid_external_tool_call`, `external_tool_policy_blocked`, `duplicate_external_tool_call_id`, `malformed_external_tool_call`) now return the same stable `code` as non-stream (chat: 502 JSON before headers / SSE `data: {error}` after; messages: 502 JSON / SSE `error` event, `api_error`; responses: 502 JSON / `response.failed` + `[DONE]`, `server_error`) instead of falling through to 500 `internal_error` via `transformUpstreamError`.
4141
- **Collector ID-less tool tracking (Stage 2, review fix)**: counter becomes a per-tool-name `Set` (`noid:<tool>` / `noid:unknown`) so two concurrent ID-less backend tool parts no longer clear each other, while repeated `message.part.updated` snapshots of the same logical tool no longer inflate and pin the idle exemption until `REQUEST_TIMEOUT_MS`. Replayed terminal snapshots are ignored via a settled set (they must not delete a still-running bucket); name-mismatch fuzzy clearing applies to the single-bucket case only, multi-bucket ambiguity waits for the real terminal (waiting beats truncating). Locked by `tests/stream-hardening.test.js` (repeat, distinct-parallel, replay, mismatch both directions, same-name limitation).
4242
- **Review confirmations (no behavior change)**: `stream:true` with any external tool contract intentionally buffers visible text until completion to avoid leaking `<function_calls>` markup incrementally (pure-text turns also batch — documented in route comments); non-stream `if (error != null)` intentionally fails even with partial content rather than returning partial as success on chat/messages/responses (retry still covers the no-output transient case; interactions is the deliberate exception — it only throws when there is no content at all, otherwise returns partial as `interaction.completed`); verified-empty tool discovery (`[]`) intentionally stays 503 fail-closed under `DISABLE_TOOLS=true` (a real backend always exposes tools; empty means unverifiable — locked by `tests/tool-alias.test.js`).
43-
- **Explicit external-tool policy semantics (Stage 2, breaking note)**: `REQUIRE_CONFIRMATION` now fires only for proxy-authored `requiresConfirmation===true` or `EXTERNAL_TOOL_REQUIRE_CONFIRMATION_FOR` hits (plus `report-only` bypass); the old implicit `WRITE/HIGH/CRITICAL`/`DELETE` inference is removed, so a `delete_*` tool with an empty allowlist now returns `ALLOW` instead of being blocked. Operators relying on default-deny for destructive tools should set `EXTERNAL_TOOL_REQUIRE_CONFIRMATION_FOR=delete_*,write_*` explicitly. Parser stays fail-closed by design (`valid plus malformed` still rejects the batch with `malformed_external_tool_call`, locked by `tests/app.test.js`).
43+
- **Explicit external-tool policy semantics (Stage 2, breaking note)**: `REQUIRE_CONFIRMATION` now fires only for proxy-authored `requiresConfirmation===true` or `EXTERNAL_TOOL_REQUIRE_CONFIRMATION_FOR` hits (plus `report-only` bypass); the old implicit `WRITE/HIGH/CRITICAL`/`DELETE` inference is removed, so a `delete_repo` tool with an empty allowlist now returns `ALLOW` instead of being blocked. Allowlist / denylist / confirmation lists are exact-match only (no wildcard/glob support; matching is checked against original / namespaced / declared names). Operators relying on default-deny for destructive tools should list exact tool names explicitly, e.g. `EXTERNAL_TOOL_REQUIRE_CONFIRMATION_FOR=delete_repo,write_file`. 白名单/黑名单/确认名单仅精确匹配,不支持 `*` 通配符(按原始名/命名空间名/声明名三者任一命中为准)。Parser stays fail-closed by design (`valid plus malformed` still rejects the batch with `malformed_external_tool_call`, locked by `tests/app.test.js`).
4444
- **Wire-compat confirmations (Stage 3)**: upstream 503 now passes through as 503 `server_error` (was coerced to 502; `tool_discovery_unavailable` stays proxy-originated only, locked by `tests/backend-error-surface.test.js`); `/v1/messages` bare `{type:tool}` without `name` is 400 via `validateAnthropicToolChoice` (direct converter calls degrade to auto, locked by `tests/messages-anthropic.test.js`); empty `tool_result` is preserved as `TOOL_RESULT content:""` to keep turn alignment (prompt breaking: previously dropped, now one extra prompt line); chat/messages/responses empty-stream stays 500 `Upstream returned no assistant data` (fail-closed, non-stream empty stays 200 — documented inconsistency, unify later); interactions `storeResponseState` intentionally omits tool calls (text+grounding only); native discovery 30s cooldown fail-closed is by design (no second `tool.ids()` probe inside the window).
4545

4646
## [1.6.0] - 2026-09-08

‎docs/configuration.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ OpenCode2API 现在支持把外部客户端传入的 OpenAI-compatible `tools`
219219
| 4 | `EXTERNAL_TOOL_POLICY_MODE` | `enforce`(默认,命中确认名单即拦截)/ `report-only`(只记 debug 日志并放行) |
220220
| 兜底 | `EXTERNAL_TOOL_DEFAULT_RISK_LEVEL` | 工具未声明风险等级时返回的 `effectiveRisk`(默认 `low`) |
221221

222-
> 名单按客户端声明名 / 命名空间名(`external__*`)/ 原始名任一命中即可;三个列表默认为空,即不改变现有行为。library 调用方也可直接传 `EXTERNAL_TOOL_ALLOWLIST: [...]` 等 options 覆盖。外部桥接工具由客户端执行,代理仅返回调用契约;名称推断的 sideEffect/risk 只做元数据,不自动触发 confirmation,operator 需用显式 confirmation 名单、deny/allowlist 控制。
222+
> 名单按客户端声明名 / 命名空间名(`external__<name>`)/ 原始名任一精确命中即可(exact-match only,不支持 `*` 通配符,例如用 `delete_repo,write_file` 而非 `delete_*,write_*`);三个列表默认为空,即不改变现有行为。library 调用方也可直接传 `EXTERNAL_TOOL_ALLOWLIST: [...]` 等 options 覆盖。外部桥接工具由客户端执行,代理仅返回调用契约;名称推断的 sideEffect/risk 只做元数据,不自动触发 confirmation,operator 需用显式 confirmation 名单、deny/allowlist 控制。
223223
224224
### 内置工具 allowlist
225225

0 commit comments

Comments
 (0)