Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ab59020
docs(webui): the engine layer has six files, not five (webui-parity 107)
fengzhi09 Oct 1, 2026
1dc559a
test(webui): M2 capability-declaration snapshot vs the real host (eng…
fengzhi09 Oct 1, 2026
8c085fa
test(webui): point the capability snapshot at the engine layer's real…
fengzhi09 Oct 1, 2026
aa5ab47
fix(webui): stop the shell from carrying one session's state into ano…
fengzhi09 Oct 1, 2026
af01e0e
refactor(webui): the plugins and turn-diff routes take the host from …
fengzhi09 Oct 1, 2026
f1842ba
test(webui): make the run-mirror, first-turn-guard and mavis-usage su…
fengzhi09 Oct 1, 2026
726d286
refactor(webui): the plugins and turn-diff routes take the host from …
fengzhi09 Oct 1, 2026
a4fad96
test(webui): make the run-mirror, first-turn-guard and mavis-usage su…
fengzhi09 Oct 1, 2026
e7c0ce9
feat(webui): the five read endpoints ask the engine facade, not the t…
fengzhi09 Oct 1, 2026
e053ae7
feat(webui): the session-tree and export endpoints ask the engine fac…
fengzhi09 Oct 1, 2026
4fb8267
feat(webui): the usage endpoints ask the engine facade, and the deriv…
fengzhi09 Oct 1, 2026
88b9a48
fix(webui): rebase M3-B3 onto M3-B2, register B2's two tmp prefixes, …
fengzhi09 Oct 1, 2026
6bc24bd
feat(webui): the account, model and capability reads ask the engine f…
fengzhi09 Oct 2, 2026
eb2a429
feat(webui): #73 swaps the ACP wire table for the 14-key engine-capab…
fengzhi09 Oct 2, 2026
2baf051
fix(webui): stop two B4 comments describing behaviour the code no lon…
fengzhi09 Oct 2, 2026
edf2b1e
feat(webui): move the session write family behind the engine facade
fengzhi09 Oct 2, 2026
1506cc2
fix(webui): drop whitespace text nodes in markdown tables and dedupe …
fengzhi09 Oct 2, 2026
8cca235
fix(webui): sweep the non-flipping inverted text token off primary su…
fengzhi09 Oct 2, 2026
eecd8c0
feat(webui): move session switch behind the engine facade
fengzhi09 Oct 2, 2026
0cfd51f
Merge main into dev-lhl
fengzhi09 Oct 2, 2026
e4cf052
chore: allowlist the leak-tripwire fixture in model-reads tests
fengzhi09 Oct 2, 2026
3f5b8d2
test(webui): pin session-writes cleanup-orphans test to isolated paths
fengzhi09 Oct 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,10 @@ paths = ['''(^|/)dist/webui/server\.js$''']
regexTarget = "match"
regexes = ['''^[A-Za-z_$][A-Za-z0-9_$]*\.setRsaPrivateKey = [A-Za-z_$][A-Za-z0-9_$]*\.rsa\.setPrivateKey ?$''', '''^[A-Za-z_$][A-Za-z0-9_$]*\.privateKeyToAsn1 = [A-Za-z_$][A-Za-z0-9_$]*\.privateKeyToRSAPrivateKey ?$''', '''^[A-Za-z_$][A-Za-z0-9_$]*\.generateKey = [A-Za-z_$][A-Za-z0-9_$]*\.pbe\.generatePkcs12Key;?$''']

[[rules.allowlists]]
description = "Leak-prevention tripwire fixture: asserts the facade never serializes this fake key"
condition = "AND"
paths = ['''(^|/)packages/webui/test/lib/engine/model-reads\.test\.js$''']
regexTarget = "match"
regexes = ['''apiKey: "sk-secret-should-never-leak"''']

16 changes: 14 additions & 2 deletions docs/tui-capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,11 @@ Status legend: ✅ wired · ⚠ partial / path differs · ❌ no path · 🚧 re

The webui does not yet expose `/fork`, `/resume`, or a "rewind last turn"
action — those acp methods (`fork`, `resume`) are reported by
`MCODE_ACP_CAPABILITIES` but no webui route wraps them.
`MCODE_ACP_CAPABILITIES` but no webui route wraps them. (Since M3-B4 that
table is no longer what `GET /api/protocol/capabilities` returns; the
endpoint serves the engine's declared 14-key capability object instead.
The table is still exported and still pinned by
`test/lib/mcode-rpc.check.mjs`.)

## ACP Skill commands

Expand Down Expand Up @@ -252,7 +256,15 @@ entries by default.
mcodeVersion,
mcodeName?,
mcodeTitle?,
capabilities: MCODE_ACP_CAPABILITIES, // see packages/webui/server/lib/mcode-rpc.js
// The engine's DECLARED 14-key capability object, served by
// packages/webui/server/engine/capability-reads.js. Before M3-B4 this
// field carried MCODE_ACP_CAPABILITIES (the ACP wire table in
// packages/webui/server/lib/mcode-rpc.js), which is still exported
// there and still a true statement about the ENGINE's ACP surface.
capabilities: <14-key declaration>,
capabilitiesProvider, // which provider's declaration answered
capabilitiesProviderFor, // "transport" | "default" (see API.md)
capabilitiesUnavailable, // the degradation roll-up
notes: {
set_mode, set_config_option, cancel, activate, fork,
load, list, close, new, prompt,
Expand Down
2 changes: 1 addition & 1 deletion docs/webui.md
Original file line number Diff line number Diff line change
Expand Up @@ -2411,7 +2411,7 @@ marker), not by tool name.
| `POST` | `/api/protocol/load-session` | `routes/protocol.js#handleLoadSession` | `?cwd=`, fallback to current |
| `POST` | `/api/protocol/activate-session` | `routes/protocol.js#handleActivateSession` | one acp client tracks one active session |
| `GET` | `/api/protocol/list-sessions` | `routes/protocol.js#handleListSessions` | `?cwd=` filtered |
| `GET` | `/api/protocol/capabilities` | `routes/protocol.js#handleCapabilities` | `{mcodeVersion, mcodeName?, mcodeTitle?, capabilities: MCODE_ACP_CAPABILITIES, notes}` |
| `GET` | `/api/protocol/capabilities` | `routes/protocol.js#handleCapabilities` | `{mcodeVersion, mcodeName?, mcodeTitle?, capabilities, capabilitiesProvider, capabilitiesProviderFor, capabilitiesUnavailable, notes}` — `capabilities` is the engine's declared 14-key capability object (it was the ACP wire table `MCODE_ACP_CAPABILITIES` before M3-B4) |

### Legacy dispatcher (`server/router.js`)

Expand Down
2 changes: 1 addition & 1 deletion docs/webui.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -1797,7 +1797,7 @@ createdAtMs, updatedAtMs}`)下发,按 `toolCallId` 幂等、上限 32 条、
| `POST` | `/api/protocol/load-session` | `routes/protocol.js#handleLoadSession` | `?cwd=`,缺省取当前 |
| `POST` | `/api/protocol/activate-session` | `routes/protocol.js#handleActivateSession` | 一个 acp 客户端跟踪一个活动会话 |
| `GET` | `/api/protocol/list-sessions` | `routes/protocol.js#handleListSessions` | `?cwd=` 过滤 |
| `GET` | `/api/protocol/capabilities` | `routes/protocol.js#handleCapabilities` | `{mcodeVersion, mcodeName?, mcodeTitle?, capabilities: MCODE_ACP_CAPABILITIES, notes}` |
| `GET` | `/api/protocol/capabilities` | `routes/protocol.js#handleCapabilities` | `{mcodeVersion, mcodeName?, mcodeTitle?, capabilities, capabilitiesProvider, capabilitiesProviderFor, capabilitiesUnavailable, notes}`——`capabilities` 是引擎声明的 14 键能力对象(M3-B4 之前是 ACP wire 表 `MCODE_ACP_CAPABILITIES`) |

### 旧派发器(`server/router.js`)

Expand Down
91 changes: 75 additions & 16 deletions packages/webui/docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -2416,10 +2416,25 @@ insensitive, trailing slash-insensitive, `\` and `/` interchangeable).

### `GET /api/protocol/capabilities`

Returns the engine's `agentInfo` (from the `initialize` reply) plus the
capability table webui knows about (`MCODE_ACP_CAPABILITIES` in
`server/lib/mcode-rpc.js`). Used by the webui to decide which UI
controls to enable.
Returns the engine's `agentInfo` (from the `initialize` reply) and the
**engine-capabilities view**: the declared 14-key capability surface of the
active engine provider, the same declaration `GET /api/engine-capabilities`
serves. Used by the webui to decide which UI controls to enable.

**This field's contract changed in M3 batch B4.** `capabilities` used to
carry `MCODE_ACP_CAPABILITIES`, a hand-maintained flat `{method: boolean}`
table of the ACP JSON-RPC surface (`set_mode`, `set_config_option`,
`cancel`, `activate`, `fork`, `resume`, `delete`, `load`, `close`, `list`,
`new`, `prompt`). Those twelve keys are **gone**: a consumer reading
`capabilities.set_mode` now gets `undefined` and must fail loudly. What
replaced them answers a different question — **"does the engine have this
capability at all"** — with the 14 matrix keys, each
`{level, missing?, reason?}`. The ACP wire table is still exported from
`server/lib/mcode-rpc.js` and is still a true statement about the
engine's ACP surface; it simply no longer travels on this endpoint.

The declaration appears exactly once, under `capabilities`, and three
sibling keys say where it came from and what to do about its gaps.

**Response 200**
```json
Expand All @@ -2429,18 +2444,45 @@ controls to enable.
"mcodeName": "mcode",
"mcodeTitle": "mcode",
"capabilities": {
"set_mode": true,
"set_config_option": true,
"cancel": true,
"activate": true,
"fork": true,
"resume": true,
"delete": false,
"load": true,
"close": true,
"list": true,
"new": true,
"prompt": true
"sessionCrud": { "level": "full" },
"streamingSend": { "level": "full" },
"interrupt": { "level": "full" },
"toolSkillInvocation": { "level": "full" },
"turnDiff": { "level": "full" },
"turnRewindRedo": { "level": "full" },
"plugins": { "level": "full" },
"mcp": { "level": "full" },
"subagents": {
"level": "partial",
"missing": ["getDelegationSnapshot", "stopDelegation"],
"reason": "delegation snapshot/stop live on the TuiRuntimeAdapter access-context, not on the v2 CliService surface (design §1.3 v2)"
},
"usageStats": { "level": "full" },
"authCredentials": { "level": "full" },
"updateCheck": {
"level": "none",
"reason": "interface-absent: no update-check method anywhere in local-runtime-v2 (design §1.3 v2)"
},
"fileReadWrite": {
"level": "partial",
"missing": ["file-write"],
"reason": "workspace read browsing only; no write API — writes go through in-turn tools (design §1.3 v2)"
},
"gitOperations": {
"level": "partial",
"missing": ["git-diff", "git-commit", "git-branch"],
"reason": "read-only metadata + review link; change mutation is outside this package (same discipline as v1's read-only Git facade)"
}
},
"capabilitiesProvider": "local-runtime-v2",
"capabilitiesProviderFor": "transport",
"capabilitiesUnavailable": {
"none": ["updateCheck"],
"partial": [
{ "key": "subagents", "missing": ["getDelegationSnapshot", "stopDelegation"] },
{ "key": "fileReadWrite", "missing": ["file-write"] },
{ "key": "gitOperations", "missing": ["git-diff", "git-commit", "git-branch"] }
]
},
"notes": {
"set_mode": "Takes a modeId from the session's availableModes.",
Expand All @@ -2455,6 +2497,23 @@ controls to enable.
`mcodeVersion` is `"unknown"` before a client has attached (no `initialize`
reply yet); the endpoint does not invent a version.

`capabilitiesProvider` is the provider whose declaration answered, and
`capabilitiesProviderFor` says HOW it was chosen. A consumer should
branch on the second one:

- `"transport"` — the active `MCODE_WEBUI_TRANSPORT`'s own registered
provider answered.
- `"default"` — no provider claims that transport yet (arrives with M4), so
the default provider's declaration is standing in. The view is still a
real, reviewed declaration, but it is not necessarily the connected
engine's, and reporting it as such would be a lie.

`capabilitiesUnavailable` is the degradation summary the capability-driven
UI renders from: a `none` key means hide the entry point, a `partial` key
means hide or disable exactly the listed sub-actions. It is the one field
that is not the declaration itself, and a consumer should not have to
re-derive it from a taxonomy with three levels and two optional fields.

---

## Authorize decisions
Expand Down
87 changes: 72 additions & 15 deletions packages/webui/docs/API.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -2230,9 +2230,24 @@ code, killEndpoint: "/api/stop" }`。温和版→SIGKILL 的级联

### `GET /api/protocol/capabilities`

返回引擎的 `agentInfo`(取自 `initialize` 应答)以及 webui
已知的 capability 表(`server/lib/mcode-rpc.js` 里的
`MCODE_ACP_CAPABILITIES`)。webui 用它来决定启用哪些 UI 控件。
返回引擎的 `agentInfo`(取自 `initialize` 应答)与
**engine-capabilities 视图**:当前引擎 provider 声明的 14 键能力面,
也就是 `GET /api/engine-capabilities` 所服务的同一份声明。webui 用它来
决定启用哪些 UI 控件。

**这个字段的契约在 M3 批次 B4 变更过。** `capabilities` 过去承载
`MCODE_ACP_CAPABILITIES`——一张手工维护的扁平 `{方法: 布尔}` 表,描述
ACP JSON-RPC 面(`set_mode`、`set_config_option`、`cancel`、`activate`、
`fork`、`resume`、`delete`、`load`、`close`、`list`、`new`、
`prompt`)。这 12 个键**已经没有了**:读 `capabilities.set_mode` 的消费方
现在拿到 `undefined`,会响亮地失败。顶替它们回答的是另一个问题
——**「引擎到底有没有这项能力」**——用 14 个矩阵键,每项形如
`{level, missing?, reason?}`。ACP wire 表仍从
`server/lib/mcode-rpc.js` 导出,且仍是对**引擎** ACP 面的真实陈述;
它只是不再随这个端点返回。

声明在响应里只出现一次,就在 `capabilities` 下;三个兄弟键说明它从哪来
以及该拿它的缺口怎么办。

**响应 200**
```json
Expand All @@ -2242,18 +2257,45 @@ code, killEndpoint: "/api/stop" }`。温和版→SIGKILL 的级联
"mcodeName": "mcode",
"mcodeTitle": "mcode",
"capabilities": {
"set_mode": true,
"set_config_option": true,
"cancel": true,
"activate": true,
"fork": true,
"resume": true,
"delete": false,
"load": true,
"close": true,
"list": true,
"new": true,
"prompt": true
"sessionCrud": { "level": "full" },
"streamingSend": { "level": "full" },
"interrupt": { "level": "full" },
"toolSkillInvocation": { "level": "full" },
"turnDiff": { "level": "full" },
"turnRewindRedo": { "level": "full" },
"plugins": { "level": "full" },
"mcp": { "level": "full" },
"subagents": {
"level": "partial",
"missing": ["getDelegationSnapshot", "stopDelegation"],
"reason": "delegation snapshot/stop live on the TuiRuntimeAdapter access-context, not on the v2 CliService surface (design §1.3 v2)"
},
"usageStats": { "level": "full" },
"authCredentials": { "level": "full" },
"updateCheck": {
"level": "none",
"reason": "interface-absent: no update-check method anywhere in local-runtime-v2 (design §1.3 v2)"
},
"fileReadWrite": {
"level": "partial",
"missing": ["file-write"],
"reason": "workspace read browsing only; no write API — writes go through in-turn tools (design §1.3 v2)"
},
"gitOperations": {
"level": "partial",
"missing": ["git-diff", "git-commit", "git-branch"],
"reason": "read-only metadata + review link; change mutation is outside this package (same discipline as v1's read-only Git facade)"
}
},
"capabilitiesProvider": "local-runtime-v2",
"capabilitiesProviderFor": "transport",
"capabilitiesUnavailable": {
"none": ["updateCheck"],
"partial": [
{ "key": "subagents", "missing": ["getDelegationSnapshot", "stopDelegation"] },
{ "key": "fileReadWrite", "missing": ["file-write"] },
{ "key": "gitOperations", "missing": ["git-diff", "git-commit", "git-branch"] }
]
},
"notes": {
"set_mode": "Takes a modeId from the session's availableModes.",
Expand All @@ -2268,6 +2310,21 @@ code, killEndpoint: "/api/stop" }`。温和版→SIGKILL 的级联
`mcodeVersion` 在尚无客户端挂接(还没收到 `initialize` 应答)
时为 `"unknown"`;本端点不会臆造一个版本号。

`capabilitiesProvider` 是应答了的那份声明所属的 provider,
`capabilitiesProviderFor` 说明它是**怎么**被选中的。消费方应当对后者
分支:

- `"transport"`——当前 `MCODE_WEBUI_TRANSPORT` 自己的已注册 provider
应答的。
- `"default"`——尚无任何 provider 声明该传输(M4 引入),由默认
provider 的声明顶替。这份视图仍是一份真实且经评审的声明,但它未必
是已连接引擎的那份;把它当成后者报出去就是撒谎。

`capabilitiesUnavailable` 是能力驱动型 UI 据以渲染的降级摘要:`none`
的键意味着隐藏整个入口,`partial` 的键意味着恰好隐藏或禁用列出的那些
子动作。它是唯一一个并非声明本身的字段,消费方不该被迫从一个有三级
两可选字段的分类法里重新推导它。

---

## 授权决策
Expand Down
Loading
Loading