Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 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
e7df93d
chore: ignore gitleaks fingerprints of deliberate test fixtures
fengzhi09 Oct 2, 2026
62814ff
chore: make the gitleaks fixture allowlists path-only
fengzhi09 Oct 2, 2026
3074010
feat(webui): move interrupt and load endpoints behind the engine facade
fengzhi09 Oct 3, 2026
063a43a
fix(webui): take the plan's 5s abort force-kill bound by product call
fengzhi09 Oct 3, 2026
90cf85e
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
a9af820
docs(webui): add session-switch, interrupt and session-load to the ar…
fengzhi09 Oct 3, 2026
4d904c3
docs(webui): add the missing zh-CN section for the B5 write family
fengzhi09 Oct 3, 2026
fdc3ff2
fix(webui): make webui-only session delete return promptly instead of…
fengzhi09 Oct 3, 2026
dab453d
fix(webui): retire lossy streaming mirrors when the engine transcript…
fengzhi09 Oct 3, 2026
7138b5b
feat(webui): add the streaming-send capability gate and pure stream b…
fengzhi09 Oct 3, 2026
a2223f4
feat(webui): run send on the runtime transport behind the engine facade
fengzhi09 Oct 3, 2026
8b51fdd
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
964c0cf
feat(webui): answer set-mode and set-config-option with structured 50…
fengzhi09 Oct 3, 2026
bdde1eb
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
a112e45
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
245a101
docs(webui): add the streaming-send architecture section, bilingual
fengzhi09 Oct 3, 2026
d9e181d
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
679d0fe
docs(webui): add the streaming-send architecture section, bilingual
fengzhi09 Oct 3, 2026
a078ee6
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
591ccff
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
b529543
fix(local-runtime): make an abandoned migration lease recoverable at …
fengzhi09 Oct 3, 2026
a8e56dc
feat(webui): move model and permission writes behind the engine facade
fengzhi09 Oct 3, 2026
48199c5
Reset dev-lhl to the full local integration line (B9+B10+docs+P13+P14…
fengzhi09 Oct 3, 2026
5661bb9
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
4b5e8d2
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
9fdd8d1
fix(webui): surface truncated acp stderr in failure alerts
fengzhi09 Oct 3, 2026
5427f23
feat(webui): move the provider family behind the engine facade with s…
fengzhi09 Oct 3, 2026
afa995e
fix(webui): acknowledge in-flight messages explicitly instead of echo…
fengzhi09 Oct 3, 2026
6c30484
fix(webui): normalise the expected side of the provider cwd path asse…
fengzhi09 Oct 3, 2026
e68a8df
feat(webui): bridge thinkingEffort as the third config id and gate th…
fengzhi09 Oct 3, 2026
1473183
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
15a3f42
feat(webui): register the acp transport as the first engine capabilit…
fengzhi09 Oct 3, 2026
313286a
fix(webui): keep over-tall code blocks inside their scroll container
fengzhi09 Oct 3, 2026
506c0a9
feat(webui): replace the flat provider form with the desktop-style di…
fengzhi09 Oct 3, 2026
25da27a
docs(webui): document the provider dialog interaction, bilingual
fengzhi09 Oct 3, 2026
38befac
feat(webui): route session deletion through the engine deleteSession …
fengzhi09 Oct 3, 2026
06a0e8e
feat(webui): open the host services window for capability exposure ba…
fengzhi09 Oct 3, 2026
60e5361
feat(webui): register the exec transport in the engine capability reg…
fengzhi09 Oct 3, 2026
64914d7
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
382c10c
fix(webui): escape raw svg tags in markdown output instead of mountin…
fengzhi09 Oct 3, 2026
bc49315
fix(webui): consume the real stream-json events on the exec transport
fengzhi09 Oct 3, 2026
35f1e0c
feat(webui): unlock the session context menu actions backed by the en…
fengzhi09 Oct 3, 2026
2b3a9e4
test(webui): register the PB-1 real-host tmp prefix
fengzhi09 Oct 3, 2026
f9829b3
chore(release-tools): register the mcode-exec-stream- tmp prefix
fengzhi09 Oct 3, 2026
b5bad19
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
33629d7
feat(webui): wire the context-window usage switch to the composer rea…
fengzhi09 Oct 3, 2026
c10736d
feat(webui): unlock the project menu's reveal-in-folder (SB-6)
fengzhi09 Oct 3, 2026
92abe74
feat(webui): make the Shortcuts page state what the browser can do
fengzhi09 Oct 3, 2026
595c06d
feat(webui): plan card reads the account tier, honest cloud placeholders
fengzhi09 Oct 3, 2026
d087f28
feat(webui): wire the usage-and-models model source to the engine (SB-1)
fengzhi09 Oct 3, 2026
43d0b2a
dev-lhl: SB-3/6/2/7/1 + SB-5 + P19 + SB-4 (settings waves, delete-han…
fengzhi09 Oct 3, 2026
c96e7a2
fix(webui): re-read the account after a source switch, and stop the k…
fengzhi09 Oct 3, 2026
c290440
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
2250209
dev-lhl: SB-10 (DOM harness) + PB-3 (worktree page) + docs stale-pare…
fengzhi09 Oct 3, 2026
8fbb8c3
dev-lhl: P21 locale-independent worktree discovery + docs stale fix
fengzhi09 Oct 3, 2026
9806700
fix(webui): stop the built distribution from tripping the credential …
fengzhi09 Oct 3, 2026
0755124
Merge main into dev-lhl
fengzhi09 Oct 3, 2026
5442e4f
dev-lhl: carry P22's model-source.js (the blob the incremental push m…
fengzhi09 Oct 3, 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
98 changes: 96 additions & 2 deletions docs/webui.md
Original file line number Diff line number Diff line change
Expand Up @@ -1204,12 +1204,15 @@ one, Worktree, reads the engine since PB-3.
| Preferences | General (通用) | implemented |
| Preferences | Voice | implemented, placeholder controls — the microphone dropdown is disabled with a single 「本地版不适用」 option, and both dictation rows show 未设置 (no device enumeration, no dictation input in a browser) |
| Preferences | Shortcuts | implemented — 10 desktop rows, each stating what the browser can do with it: 3 rebindable and live, 1 live on macOS only, 6 blocked with the specific reason (see **Shortcuts — what the browser can intercept**) |
| Preferences | Personalization | implemented — 自定义指令 and 关于你 persist to `localStorage`; both memory switches render off and disabled with the not-applicable marker, and 管理 opens the 记忆摘要 dialog in its permanent empty state |
| Preferences | Personalization | implemented, and honest about what it is for — 自定义指令 and 关于你 persist to `localStorage` and each field states 「已保存于本浏览器,不会注入引擎会话」, because the engine has no channel that reads them (SB-8 / D-2; the storage is kept, the injection claim is not); both memory switches render off and disabled with the not-applicable marker, and 管理 opens the 记忆摘要 dialog in its permanent empty state |
| Management | Usage & models | implemented; since SB-1 the two engine sources are real (Token Plan / MiniMax API switch the engine's credential, the 「使用中」 badge reads the engine back, and the MiniMax API key can be saved and probed) — the third pill, Custom models, stays a VIEW onto the provider catalogue |
| Management | Connection | implemented |
| Management | Account | implemented as a read — the section reads `GET /api/account` on mount and renders the account name, the current plan name, the quota overview (plan-quota state plus the 5-hour and weekly remaining figures) and the account status; sign-out stays disabled (no engine method acts on it) |
| Coding | Code review | implemented — 自定义审查准则 persists to `localStorage`; 审查方式 is a disabled single-option dropdown showing 子会话 |
| Coding | Worktree | implemented as a CLEANUP page since PB-3 — see **Worktree — what the page can and cannot do** |

| Coding | Code review | implemented — 自定义审查准则 persists to `localStorage` and carries the same 「不会注入引擎会话」 note as the two Personalization texts (SB-8 / D-2); 审查方式 is a disabled single-option dropdown showing 子会话 |
| Coding | Worktree | **not implemented** — the tab is a one-line panel reading 「本地版暂不支持工作树管理」 |
| Archived | Archived tasks | the tab renders its empty state 「暂无已归档任务」; the list and its actions need an archived-session contract that does not exist |

**Worktree — what the page can and cannot do.** The Worktree tab is a
Expand Down Expand Up @@ -1326,7 +1329,7 @@ between adjacent rows:
| Session management | enabled | one switch, persisted; gates the composer's context-window readout (see below) |
| Agent control | disabled furniture | the 「自动打开浏览器面板」 switch renders off and disabled (no capability behind it) |
| Preference settings | enabled | follow-up behaviour (disabled / queue / send now); since SB-4 the composer reads it, and a send into a running turn reaches the engine's queue or steers the running turn. Watermark and data opt-in render disabled |
| About | mixed | upload logs and check-for-update are disabled buttons; the local URL and LAN URL are live read-only rows from `/api/settings` |
| About | mixed | **export logs** is a live download of this server's own diagnostic trail (`GET /api/logs/export`, see below); check-for-update is still a disabled button — self-hosted update is `git pull`, and the desktop updater's semantics do not apply; the local URL and LAN URL are live read-only rows from `/api/settings` |
| dataDir footer | not implemented | the reference prints the app data directory at the bottom of the General page; `/api/settings` has no such field and the server routes are read-only this round, so no value exists to print |

Appearance and language behave as before: immediate effect on click; the
Expand Down Expand Up @@ -1864,6 +1867,96 @@ scope, and until it lands a queued follow-up is invisible until the
running turn ends. A steered message reports admission, not whether the
running agent read the text before its next step.

### Export logs: a download, because there is nowhere to upload to (SB-8 / D-3)

**What the user sees.** The About section's first row is 导出日志 / **Export
logs**, and its button works: the browser saves one text file named
`mcode-webui-logs-<timestamp>.txt`. The row's description says what is in
it — this server's error log and recent activity — and adds that nothing is
uploaded anywhere.

**Why the rename.** The row used to be a permanently disabled button
labelled 上传日志 / **Upload logs**. There is no upload service in this
edition: no telemetry sink, no ticket intake, nothing that leaves the
machine. A label that names a destination the product does not have is a
promise, and a disabled control cannot keep it — the tooltip only
contradicted the title. The alternative considered and rejected was
keeping 上传日志 and pointing it at the download, on the argument that the
desktop reference uses the word; a reference's word does not make its
destination real, and the button would then say "upload" while writing to
the user's disk.

**What the file contains.** Two sources, both read from the module that
writes them, so a relocated data directory cannot make the export silently
empty:

| Section | Source | Bounded by |
| --- | --- | --- |
| Server error log | `WEBUI_DATA_DIR/.server.err` (`config.js#installGlobalErrorHandlers`) | 2000 lines / 2 MiB |
| Event log | `events.path()` (honours `MCODE_WEBUI_EVENTS_PATH`) | 2000 lines / 2 MiB |

The tail, not the head: the failure being investigated happened most
recently, and the event log is tens of megabytes on a long-lived install.
Every bound is printed in the file itself (`[truncated: showing the last N
of M lines]`), so a reader can tell a bounded file from a complete one
without trusting the tool that produced it.

**What the file deliberately does not contain.** `sessions.json`
(conversation transcripts), `settings.json` (provider credentials) and
`uploads/`. A diagnostics file users attach to a bug report must not be
the one file on the machine carrying their API keys and their
conversations. The source list is a closed two for that reason, and a test
asserts the markers never appear in a bundle built next to decoy files.

**Why there is no failure status.** The endpoint answers `200` in every
case, including a log file that does not exist. The client is a browser
anchor with a `download` attribute, so a `404` would be saved into the
user's downloads folder as `mcode-webui-logs-<timestamp>.txt` containing a
JSON error body — a file that looks like logs and is not. Absence is
therefore reported where the reader is: inside the body, per section.

**What it costs.** One read of two local files per click, synchronously on
the server's event loop. Both are capped, so the worst case is a few MiB
of already-warm page cache.

**Known debt.** The bundle is plain text with no redaction: an engine error
line can quote a prompt fragment. Redaction is not attempted because there
is no reliable rule for what is secret in an arbitrary log line, and a
partial redaction would be worse than none. Until the engine's own logging
grows a redaction hook, the operator is the one deciding what to share
from the downloaded file.

### The three stored texts are storage, not instructions (SB-8 / D-2)

**What the user sees.** 自定义指令, 关于你 and 自定义审查准则 each keep
their textarea and their 保存 action, and each now prints one line under
the field: **「已保存于本浏览器,不会注入引擎会话。」** / "Saved in this
browser only — it is not injected into engine sessions."

**Why.** The texts have always persisted — that part is real and stays
real. What is not real is any claim that they reach the engine. A grep of
the runtime source for `setConfigOption` — the option the plan had assumed
would carry them — found nothing: the method does not exist anywhere in
`local-runtime-v2`, so there is no config write channel to hang them on,
and no session-creation parameter that takes them either. With no consumer,
a saved 「自定义指令」 read as an instruction the agent follows. The field
was making a capability claim its backend had already disproved.

**The decision tree this took.** The three options were: extend the engine
contract, drop the fields, or keep the storage and stop claiming the
effect. Extending the contract is engine work with no local caller to size
it against; dropping the fields removes a place users keep text they own
and can read back at any time. What remains is honest and cheap: the
storage is a user's own local text, the field says plainly that it is not
an instruction, and the moment the engine grows a channel, deleting one
sentence and one `note` prop is the whole change.

**What this does not do.** It does not make the texts work, and it does not
hide that they do not work — that is what the note is for. Nothing reads
the three `localStorage` keys: a future change that wires one of them must
remove the note in the same commit, or the field will be describing an
effect it no longer lacks.

## Main-surface elements: user menu / project context menu / home capsules (ticket 55c)

The user asked for every desktop main-surface screenshot to be copied
Expand Down Expand Up @@ -3079,6 +3172,7 @@ marker), not by tool name.
| `PUT` | `/api/model-source/api-key` | `routes/model-source.js#handlePutModelSourceApiKey` | `{apiKey, saveAndUse?}`; an absent/empty/whitespace `apiKey` is the KEEP sentinel → `200 {changed:false}` with no engine write; `400 {code:"BAD_FIELD_TYPE"\|"INVALID_API_KEY"}`; `500 {code:"engine_error"}` never carries the thrown message |
| `POST` | `/api/model-source/test` | `routes/model-source.js#handleTestModelSource` | `{modelId?}`; always 200 for a COMPLETED probe (`{ok, success, providerId:"minimax_api", tested:"stored_key", status}`) including `success:false`; non-200 only when the probe is refused (`503`/`501`, or the engine's `400 NO_API_KEY`) |
| `POST` | `/api/follow-up` | `routes/follow-up.js#handleFollowUp` | `{behavior:"queue"\|"steer", content, attachments?, requestId?}` — the engine session id comes from the server's own conversation state, never the body; `400 {code:"invalid_follow_up_behavior"\|"follow_up_empty"\|"no_active_conversation"\|"BAD_FIELD_TYPE"}`; `409 {code:"no_active_turn"\|"turn_not_owned"}` when this process does not own the running turn, and nothing is queued; `501` when the host lacks the method, `503` when no runtime is booted; 200 `{ok, behavior, itemId, position, status}` (queue) or `{ok, behavior, turnId, mode}` (steer) — the engine's own answer |
| `GET` | `/api/logs/export` | `routes/logs.js#handleExportLogs` | always `200 text/plain` with `Content-Disposition: attachment; filename="mcode-webui-logs-<YYYYMMDDTHHMMSS>.txt"` — the crash trail (`WEBUI_DATA_DIR/.server.err`) plus the last 2000 lines / 2 MiB of the event log, each section stating its own truncation or absence. No status code for a missing or unreadable log file: that is reported inside the body, because the client is a browser anchor and a 4xx would be saved as a `.txt` file containing JSON |
| `POST` | `/api/debug/inject` | `routes/debug.js#handleDebugInject` | `DEBUG_INJECT=1` gate |
| `GET` | `/api/debug/state` | `routes/debug.js#handleDebugState` | same gate |
| `POST` | `/api/protocol/set-mode` | `routes/protocol.js#handleSetMode` | mid-session mode change |
Expand Down
Loading
Loading