Skip to content

Web control center, plan limits, Q&A mode, Chinese UI and OpenAI-compatible API agents - #12

Open
ZackLiuzeyu wants to merge 30 commits into
zetbrush:mainfrom
ZackLiuzeyu:main
Open

ZackLiuzeyu wants to merge 30 commits into
zetbrush:mainfrom
ZackLiuzeyu:main

Conversation

@ZackLiuzeyu

Copy link
Copy Markdown

Summary

This turns the web dashboard from a read-only monitor into a control center for running a team of agents from the browser. It also adds a fourth agent type that runs on any OpenAI-compatible API. Everything the dashboard does goes through the same orchestrator code the MCP tools use, so the MCP / CLI workflow keeps working as before.

54 files, about +13.3k / −3.0k lines. The changes are grouped by area below. Each area is one or more self-contained commits, so it can be reviewed commit by commit, or I can split this into smaller PRs if you prefer.

What's in it

1. Web control center

  • Actions from the browser:
    • message and broadcast;
    • pause/resume a session or an agent; release all agents, or only approved ones;
    • add, remove, rename and change the role of an agent;
    • create a new team; create, relaunch, archive, end, export and delete a session;
    • edit the plan and the knowledge store; manage file locks and ownership zones; guardrail steppers.
  • One orchestrator for both hosts:
    • The orchestrator's tools moved to orchestrator/runtime.ts, which both orchestrator-server.ts (MCP) and dashboard/server.ts host. The web runs the same create_team / add_agent / release_agent / end_session code instead of a copy.
    • dashboard/control.ts is the API (POST /api/<action>). Every mutation names its session and checks that the slot belongs to it.
  • Human gate: session config human_gate (also a create_team option) keeps a fully approved session waiting for a person instead of ending itself.
  • Broker: plan item add/edit/delete, plus a merging config_patch on session update. Messages from the browser use the sender id human.

2. Workspace layout

  • Three panes:
    • left: sessions and a file explorer;
    • middle: the team chat;
    • right: file preview and the team roster.
    • Side panes collapse and resize, and become drawers on narrow screens.
  • Chat:
    • Markdown is rendered safely.
    • File names an agent mentions become links.
    • Enter sends without breaking IME input.
  • Preview:
    • Formats: Markdown, code, images, PDF, .docx, CSV, sandboxed HTML, audio and video.
    • It follows the open file as agents edit it.
  • File serving: dashboard/files.ts serves the project read-only (GET /api/fs/*). Every path goes through realpath, so neither .. nor a symlink can leave the project. HTML and SVG get a sandbox CSP, and .docx is rendered server-side with a small zip reader, with no new dependency.
  • Themes: light, dark or follow the system.

3. Activity, approvals, models and limits

  • Activity timeline: one live timeline per agent, covering thinking, tool calls, diffs, command output and turn costs. Claude stream-json, Codex app-server items and Gemini stream-json map onto one shape (orchestrator/activity.ts, broker /activity/*).
  • Approvals:
    • Per-agent modes: bypass, auto, accept_edits and ask.
    • Claude now runs with --permission-prompt-tool stdio. Codex approval requests are no longer auto-accepted when a handler is set.
    • Requests are stored in the broker (/approvals/*), so any dashboard can answer them: Allow, Always allow, or Deny with a note.
    • Team-protocol tools never ask.
  • Models and effort: set per slot and synced to running drivers. Claude switches live (set_model / set_permission_mode); Codex picks the change up on its next turn.
  • Usage limits: session config.limits, in tokens or USD, per agent or per session. An agent that reaches its limit is paused and interrupted.

4. Plan limits, talking to agents, live thinking

  • Plan limits: read without spending a model turn and cached for 2 minutes.
    • Claude: 5-hour and weekly windows, via a get_usage control request.
    • Codex: 5-hour and weekly windows, via account/rateLimits/read.
    • Gemini: daily quota per model, via Code Assist retrieveUserQuota with the stored Google login.
  • Talking to agents:
    • Agents can message human and are told the id to use.
    • Interrupt stops one agent's turn so it answers first.
    • Q&A mode holds the whole team: running turns stop and agents only hear the person. Only read-only tools are allowed, teammates' messages wait, and nudges, respawns and auto-end pause.
  • Live view: Claude runs with --include-partial-messages. A LiveTracker turns Claude, Codex and Gemini deltas into a typing indicator, the current step and the thinking as it forms.

5. Chinese / English UI and customization

  • dashboard/i18n.js: tr(english, vars) with a Chinese dictionary, pattern rules for server-generated text, and in-place translation of the static HTML. Chinese is the default for zh browsers. tests/web-i18n.test.ts checks that every UI string has an entry.
  • Avatars: default to each CLI's icon and can be changed to initials, a color or an image.
  • Page background: a background image with adjustable opacity and blur, kept per browser.

6. "Other" agents on any OpenAI-compatible API

  • Agent type api: DeepSeek, Qwen, Kimi, GLM, OpenRouter, a local Ollama, or anything else that speaks the chat-completions API.
  • Providers: defined in a dialog with presets and model fetching. Fields: name, base URL, key, default model, optional prices, context window and extra request fields.
  • Keys:
    • Stored in ~/.multiagents/providers.json (mode 0600); the browser only gets a masked hint.
    • A key written as $ENV_NAME is read from that environment variable.
    • Slots keep only the provider id.
  • orchestrator/api-driver.ts:
    • Runs the agent in-process. It spawns the team-tools MCP server in driver mode, adds local tools with Claude's names (Read, Write, Edit, LS, Glob, Grep, Bash, WebFetch), all confined to the project, and streams completions (reasoning_content becomes thinking).
    • It emits Claude stream-json events and implements a new ClaudeSession interface, so the following all reuse the Claude paths: delivery, approvals, activity, the live view, cost accounting, Q&A, interrupts and live model switches.

7. Fixes

  • Non-Latin session names: slugify kept only a-z0-9, so a session named in Chinese got the id "" and could not be opened or ended. Slugs now keep letters of any script; the broker refuses an empty id and migrates rows stored under "".
  • Outdated broker: the broker daemon kept its old code across updates, so new features silently did nothing.
    • /health now lists BROKER_FEATURES. The dashboard restarts an idle outdated broker at startup, and otherwise shows a restart banner.
    • A restart no longer kills the agents: current brokers exit on SIGUSR2 without the SIGTERM peer cleanup, and older ones get SIGKILL (WAL keeps the data).
  • Finished agents deleted: the dead-slot loop deleted every slot that had been disconnected for 5 minutes, so agents that exit when done (e.g. Gemini after approval) vanished. deadSlotVerdict now deletes only orphan slots; finished agents stay listed as offline.
  • Codex rate limits: older app-servers reject params on account/rateLimits/read; it now sends null.
  • CLI detection: detectAgent now honors MULTIAGENTS_CLAUDE_COMMAND / MULTIAGENTS_CODEX_COMMAND, so CI without the real CLIs can launch teams.
  • Dashboard stuck on "connecting…": this happened when an older server served the new files. It now falls back to English and asks for a restart.
  • CI: workflow_dispatch lets CI be run by hand from the Actions tab.

Behavior changes to be aware of

  • Claude flags: driver-launched Claude agents now get --permission-prompt-tool stdio and --include-partial-messages.
  • Broker restarts: the broker handles SIGUSR2 as "exit without touching peers". SIGTERM behaves as before.
  • Background cleanup: it no longer deletes finished agents; they stay until a person removes them.
  • Browser-started teams: these default to human_gate: true.

Testing

  • HOME=$(mktemp -d) bun test: 352 pass, 2 skip (live tests, gated by MULTIAGENTS_LIVE_TESTS), 0 fail.
  • tsc --noEmit reports no errors.
  • New suites:
    • web control, files and i18n: tests/web-control.test.ts, tests/web-files.test.ts, tests/web-i18n.test.ts;
    • activity, approvals and settings;
    • plan usage, Q&A mode and the live view;
    • supervision verdicts;
    • tests/api-agent.test.ts, which runs a scripted fake OpenAI-compatible server through a real broker and the dashboard. It covers masking, thinking, tools, replying to the person, cost accounting, approvals, model switching and the symlink boundary.
  • Driver tests still run against tests/fakes/fake-claude.ts and tests/fakes/fake-codex-app-server.ts.
  • README and CLAUDE.md are updated for all of the above.

claude and others added 21 commits October 22, 2026 00:36
The web dashboard could watch a team but not act on it. It now covers the
CLI and orchestrator operations used day to day: messaging and broadcast,
session and agent pause/resume, release (all, or approved only), add,
remove, rename and change role of agents, New Team, session create, relaunch,
archive, end, export and delete, plan and knowledge editing, file locks and
ownership zones, and guardrail steppers.

Agent processes live in the memory of the process that launched them, so the
orchestrator's tools move to orchestrator/runtime.ts. The MCP server and the
web dashboard both host it, and the web runs the same create_team, add_agent,
release_agent, end_session and resume_session code instead of a copy.
dashboard/control.ts is the API: every mutation names its session, checks
that the slot belongs to it, and returns the broker's answer or an error.

A human gate (session config human_gate, on for teams started from the
browser, create_team's human_gate for MCP) keeps a fully approved session
waiting instead of ending itself. From there the person releases the agents
or sends one back with actionable feedback.

The broker gains plan item add/edit/delete and a merging config_patch on
session update. Messages from the browser use sender id "human".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SP5EAuSkQZm3jj3PTTWDfN
Turn the web dashboard into a control center
slugify kept only a-z and 0-9, so a session named "食物过敏论文" got the id
"". The dashboard then could not open or end it ("session_id is required",
HTTP 400 on switch). Slugs now keep letters and digits of any script and
fall back to "session"; the broker refuses an empty id and, on startup,
renames a session stored under "" in every table. A failed session switch
shows the server's reason instead of the status code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SP5EAuSkQZm3jj3PTTWDfN
The dashboard becomes a three-pane workspace: sessions and a file
explorer of the project on the left, the team chat in the middle, and a
file preview plus team roster on the right. Side panes collapse and
resize, and turn into drawers on narrow screens.

- Chat: your messages on the right, agents on the left with avatar,
  role and message kind; system notices fold into centered lines;
  one-to-many sends show once. Enter sends without cutting off IME input.
  Markdown renders safely, and file names agents mention become links
  once the project is found to contain them.
- Preview: Markdown, code with line numbers, images, PDF, .docx, CSV,
  sandboxed HTML, audio and video; it follows the open file as agents
  edit it.
- Themes: light, dark, or follow the system.

dashboard/files.ts serves the project read-only under GET /api/fs/*,
resolving every path with realpath so neither ".." nor a symlink leaves
the project. HTML and SVG get a sandbox CSP, and .docx is rendered
server-side with a small zip reader. The UI moves to index.html,
app.js and styles.css.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SP5EAuSkQZm3jj3PTTWDfN
Web dashboard: team chat, file explorer and preview; fix non-ASCII session names
Activity: a live timeline of every agent's work in the dashboard —
thinking, tool calls with their inputs, edits as diffs, commands with
output, turn costs. orchestrator/activity.ts maps Claude stream-json,
Codex app-server items and Gemini stream-json onto one shape, written to
the broker (/activity/*) by the host that runs the agent.

Approvals: each agent has an approval mode (bypass, auto, accept_edits,
ask). Claude now runs with --permission-prompt-tool stdio, so its
can_use_tool control requests reach the driver; Codex's approval requests
are no longer auto-accepted when a handler is set. Both are stored in the
broker (/approvals/*) and wait for a person, so any dashboard can answer:
Allow, Always allow (the tool, or the shell program, for the rest of the
agent's session) or Deny with a note. Team-protocol tools never ask.

Models and effort: settings live in the slot snapshot, and the
runtime syncs running drivers with them each delivery tick. Claude
switches live (set_model, set_permission_mode); Codex picks them up at
its next turn; Gemini and Claude's effort at the next start. Pausing a
slot now also interrupts its running turn.

Usage and limits: Claude's reported cost is metered per slot (cost_usd),
Gemini's token stats are counted, and session config.limits (per agent
or per session, tokens or USD) pause and interrupt an agent that reaches
them. The dashboard gains the Activity tab, approval cards and banner, a
Settings dialog per agent, and a Usage tab with limit locks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SP5EAuSkQZm3jj3PTTWDfN
- dashboard/i18n.js: tr(english, vars) with a Chinese dictionary, pattern
  rules for server-generated text (activity titles, toasts), and in-place
  translation of the static HTML and modal bodies. Language button in the
  top bar; Chinese is the default for zh browsers; dates use the locale.
- Avatars default to the agent CLI's icon (Claude, Gemini from Simple Icons;
  a neutral terminal glyph for Codex). Click an avatar to use initials, a
  color or an uploaded image instead; your own avatar too.
- Appearance dialog: background image under the whole page, image and
  panel opacity, optional blur. All per-browser localStorage.
- tests/web-i18n.test.ts checks every UI string has a Chinese entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SP5EAuSkQZm3jj3PTTWDfN
Web dashboard: agent activity and step approvals, model switching, usage limits, i18n, avatars and background
A dashboard server started before an update serves the new index.html and
app.js from disk but 404s i18n.js, which it doesn't know. app.js then threw
at boot and the page sat on "connecting…". It now falls back to English
and says the server needs a restart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SP5EAuSkQZm3jj3PTTWDfN
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SP5EAuSkQZm3jj3PTTWDfN
…m thinking

Plan limits: the Usage tab (and a top-bar meter) shows each CLI's
subscription windows, read without a model turn — Claude's 5-hour and
weekly windows (get_usage control request on a throwaway claude -p),
Codex's 5-hour and weekly windows (account/rateLimits/read), Gemini's
daily per-model quota (Code Assist retrieveUserQuota with the stored
Google login, never refreshed here). Cached two minutes.

Talking to agents: agents can now reply to "human" (the broker refused it)
and are told the id to use. "Interrupt" on the composer stops one agent's
turn so it answers first. Q&A mode holds the team: running turns stop,
agents hear only the person, edits and commands are refused (reads stay
allowed; Codex turns run read-only), teammates' messages wait, and nudges,
respawns and auto-end pause until it ends.

Live view: Claude runs with --include-partial-messages; a LiveTracker per
agent turns Claude, Codex and Gemini deltas into the block being written
and the current step, kept in memory on the broker. The chat shows typing
dots, the step and the thinking as it forms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SP5EAuSkQZm3jj3PTTWDfN
detectAgent looked for the real claude / codex binaries even when
MULTIAGENTS_CLAUDE_COMMAND / MULTIAGENTS_CODEX_COMMAND replace them, so on
a machine without the CLI (CI) every team launch was refused although the
override would have run. The override's program is what has to exist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SP5EAuSkQZm3jj3PTTWDfN
Web dashboard: plan limits, talking to agents (interrupt, Q&A mode), live thinking
…nswered

The broker is a daemon that keeps its old code across updates, so replies
to the person, Q&A mode and the live thinking view silently did nothing
until it was restarted. /health now lists the broker's features; the
dashboard restarts an idle outdated broker at startup and otherwise shows
a banner with a Restart button. A restart leaves agents running: current
brokers exit on SIGUSR2 without the SIGTERM handler that kills every
registered peer, older ones are ended with SIGKILL (WAL keeps the data).

The chat now also shows an agent that was asked something and has not
answered yet, from the messages alone, so every agent gets a "working"
row even when no live report reaches the dashboard.

Codex plan limits: older app-servers take no params on
account/rateLimits/read ("invalid type: map, expected unit"); send null,
which every version accepts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SP5EAuSkQZm3jj3PTTWDfN
Driver-run Codex agents have a connected slot but no peer row, so the
zero-peer check could restart an outdated broker under them at dashboard
startup. brokerIsIdle also requires no connected slot in any open session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SP5EAuSkQZm3jj3PTTWDfN
The dead-slot loop deleted every slot disconnected for 5 minutes. Agents
that exit when done (Gemini after approval, exit 0) vanished from the
team list — also while the session was paused or waiting on a person.

deadSlotVerdict now decides: a slot is deleted only if its agent
connected, never worked (task_state idle), never sent a message or held a
plan item, and the session is not paused, in Q&A mode or at the human
gate. Everything else stays listed as offline until a person removes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SP5EAuSkQZm3jj3PTTWDfN
Restart an outdated broker without killing agents; keep finished agents; show who has not answered; fix Codex plan limits
A new agent type, api, runs an agent on any chat-completions API
configured under API providers (name, base URL, key, default model,
optional prices, context window and extra request fields). Keys live in
~/.multiagents/providers.json (0600) and never reach the page; slots keep
the provider id.

ApiDriver runs the agent in-process: it spawns the team-tools MCP server
in driver mode and speaks MCP to it, adds local tools with Claude's names
(Read, Write, Edit, LS, Glob, Grep, Bash, WebFetch) confined to the
project, streams completions (reasoning_content becomes thinking) and
emits Claude's stream-json events. It implements ClaudeSession, so
delivery, approvals per permission mode, the activity timeline, the live
view, token and cost accounting, Q&A mode, interrupts and live model
switches all reuse the Claude paths.

The dashboard gets an API providers dialog with presets and model
fetching, the "Other (OpenAI-compatible API)" type in Add Agent and New
Team, provider names on cards and a plug avatar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SP5EAuSkQZm3jj3PTTWDfN
…how to sign in

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SP5EAuSkQZm3jj3PTTWDfN
- A new file whose parent directories do not exist yet is resolved
  through its nearest existing ancestor, so a symlinked directory in the
  project cannot carry a Write outside it.
- The team-tools adapter of an API agent registers as "api" (not
  "claude"), so peer lists and type filters report it correctly.
- Clearing an API agent's model is refused when the provider has no
  default model, since a restarted agent could not launch without one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SP5EAuSkQZm3jj3PTTWDfN
Add "Other" agents on any OpenAI-compatible API (DeepSeek, Qwen, Kimi…)
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-22T11:30:18.100185Z
We couldn’t complete this review. Comment @codex review to try again.
a6e7226 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants