Skip to content

Modular Dashboard: agent-composable Control UI workspaces (tracking) #101093

Description

@100yenadmin

Modular Dashboard: agent-composable Control UI workspaces (tracking issue)

The dashboard your agent builds with you. OpenClaw's Control UI becomes a modular, agent-composable workspace: your agents create tabs, drop in widgets, and wire them to live data through the same guarded commands a human uses — no code, no redeploy. The thing no fixed dashboard (Codex included) can do.

End-to-end: the Overview-as-data default workspace, an agent composing a Financials tab live with the dashboard.* RPCs firing, a pending custom widget approved and mounted in a sandbox, a live data update, then the operator dragging, collapsing, and using the cell menu — all against one shared document

▶ Full-quality recording (light / dark).

This issue tracks a 3-PR series. Each PR is independently reviewable, independently useful, and lands in order. Nothing here is a big-bang rewrite — it's a bundled plugin plus one bundled Control UI view, following the same shape #99930 (plugin tabs) just landed.

The gap

The Control UI is a fixed set of hand-coded views. Two audiences have outgrown that:

  • Small-business operators want a business control hub they arrange themselves — financial tabs, per-agent workspaces, drag/drop, hide/collapse — and will never touch code.
  • Daily-driver maintainers (a lot of them on Codex right now) want mission-control ergonomics: sessions, activity, approvals, and cron composed into one view, live, fast to dispatch from.

Both groups are asking the same thing in different words: let me arrange this myself.

The insight

Don't build them a settings panel. Give the agent the ability to compose the surface — via layout-as-data plus a guarded control plane — so humans and agents edit the same document, through the same validated commands. "Give me a financials tab" becomes a real capability: the agent creates the tab, adds widgets it authored, binds them to live data, and the operator keeps full drag/drop parity over the exact same layout.

How it works (architecture overview)

Four ideas carry the whole design. The per-PR bodies go deep on each; this is the shape.

1. Layout-as-data (workspace.json). The dashboard's entire state is one JSON document at <stateDir>/dashboard/workspace.json: tabs contain widgets, each widget carries a 12-column grid position, its data bindings, and provenance (createdBy: "user" | "system" | "agent:<id>"). Nothing about a workspace lives in code — so anything that can edit the document can compose the dashboard, and a human and an agent are editing the same bytes.

2. One control plane, three faces. Every mutation — from the Control UI, the openclaw dashboard … CLI, or an agent's dashboard_* tool — funnels through a single validated DashboardStore. One in-process async mutex serializes all callers; every write is schema-validated by hand-written guards (repo idiom, no zod at the RPC layer), applied whole-or-not-at-all, written atomically (temp + rename), and undoable (20-entry ring). Humans and agents literally cannot diverge, because there is exactly one code path that writes the file:

  CLI  ─┐
  agent tool  ─┼─▶  DashboardStore.mutate()  ──▶  workspace.json
  gateway RPC  ─┘     (one mutex · one validator · atomic write)
                          │
                          └──▶ plugin.dashboard.changed  ──▶  every operator UI live-updates

Each successful write broadcasts plugin.dashboard.changed, so an agent's edit appears in an already-open browser with no reload — and the operator can push back with drag/drop on the same document immediately.

3. Plugin-tab seam reuse (#99930). The whole surface is one bundled Control-UI tab, registered through the exact descriptor + BUNDLED_TAB_VIEWS mechanism logbook shipped in #99930. No new core UI mechanism is invented; this is the second consumer of that seam. Core ui/ changes are limited to five small additive edits — everything else is new plugin/UI files.

4. A hard trust boundary for custom widgets. Trusted builtins render as first-party Lit templates. Agent-authored widgets are untrusted code and are treated as hostile: they render in an <iframe sandbox="allow-scripts"> (opaque origin, never allow-same-origin), are served from a static-only, path-jailed, approval-gated route with a strict CSP (connect-src 'none' — no network, structurally), and reach data only through a versioned postMessage bridge where the trusted parent resolves the widget's declared bindings on its behalf. The widget never holds a credential and never talks to the gateway. An operator approval gate stands between "agent scaffolds a widget" and "an iframe ever mounts."

The proof

The series is backed by an end-to-end demo (the recording above): an agent creates a "Financials" tab live in an already-open browser (no reload), drops in built-in widgets bound to a data file, scaffolds a sandboxed custom widget, the operator approves it from a pending-card, it renders and stays live on data pushes — then the human drags things around, collapses/hides widgets, and the agent's own dashboard_workspace_get reflects the human's edits accurately. Same document, both directions.

The two ends of that story as stills — the default surface, and what the agent composes on top of it:

Overview-as-data — the default workspace, with nine live builtins (cost and token cards, instance health, sessions, cron ok/error, activity feed) reading live RPCs, no configuration

The agent-composed Financials tab: the approved sandboxed custom chart widget with AI-provenance chips and the Finance Activity lifecycle feed

The receipts

This is what's already been asked for, repeatedly:

Each PR below states exactly which of these it closes or partially advances — none of them are claimed as fully closed by a single PR except where stated.

The discipline

This builds on recent direction, not against it:

Core stays lean: everything except one bundled tab view lives in a new plugin, extensions/dashboard. The agent never touches ui/ source — it only ever calls the same validated gateway methods/CLI/tools a human can call.

Try the whole thing in one checkout

For reviewers who want to run it before reading three diffs: because we don't have upstream write access to set GitHub-native stacked bases, the three PRs each target main with cumulative diffs — which means #101098 (the top of the stack) already is the full feature in one branch. Just gh pr checkout #101098 (or git fetch <fork> feat/modular-dashboard), start the gateway, open the Control UI, and see the whole thing work end to end. CI runs against the full integration on #101098. The review-and-merge units are still the three PRs below, reviewed base-first (#101094#101097#101098); #101098 doubles as the "check it out and run everything" branch.

The 3-PR roadmap

PR Content Closes / anchors
#101094 — Backend: workspace document + control plane extensions/dashboard plugin: the workspace.json store, 14 gateway methods, CLI, 14 agent tools, change broadcasts. Zero core changes. Foundation for the series; references this issue
#101097 — Workspaces UI: shell + builtins + Overview-as-data + polish The bundled plugin-tab view: live tab strip, 12-column grid, drag/drop, resize, collapse/hide, per-cell error boundaries, live updates — plus 9 trusted built-in widgets (stat-card, markdown, table, iframe-embed, sessions, usage, cron, instances, activity) and the default workspace that replaces the old Overview with the same information as live data #66138; supersedes #77774 / #86460 as workspace content; partials #72545 / #76089
#101098 — Sandboxed custom widgets + approval flow Agent-authored HTML widgets rendered in a locked-down iframe, a versioned postMessage bridge, and an operator approval gate before anything untrusted ever mounts #66983 / #68497; partials #27574

Order: PR1 → PR2 → PR3. Each depends on the prior and is independently mergeable/valuable on its own — none of them require the ones after it to be useful. Each PR stays inside VISION.md's ~5K reviewable-line guideline (source LOC, excluding generated i18n bundles).

What this deliberately is not

This is not the contested big-bang dashboard PR (#95339). That PR's shape is exactly what this series avoids: one huge diff, no staging, core changes throughout. Here, two of the three PRs touch only a new plugin directory; the only core code PR2 touches at all is five existing files — a BUNDLED_TAB_VIEWS entry in plugin-page.ts, a styles.css import, and a breadcrumb prop threaded through app-host.ts, app-topbar.ts, and dashboard-header.ts — plus the new plugin page/component files it adds under ui/src/pages/plugin/ and ui/src/lib/dashboard/. Touches almost no core code.

Verification posture across the series

Every PR ships with its own test suite (unit + integration, and E2E where relevant) and its full test suite passes before it is proposed here (CI runs on each PR). PR3 additionally carries a written adversarial-security-review summary, because it's the highest-security-surface layer in the project (see PR3's body for specifics — independent reviewers tried to refute the sandbox/path-jail/CSP model and could not, and the one real hole they did find is fixed with a regression test before this reaches upstream).

Each increment is well inside VISION.md's ~5K reviewable-line guideline: #101094 ~3,300 lines of source (11 files); #101097 ~4,000 lines of source (23 files) — the raw diff looks larger (~14K) only because it includes 61 generated i18n locale bundles (regenerated, never hand-edited) plus tests; #101098 ~1,400 lines of source (9 files).

Because we have no upstream write access to set GitHub-native stacked bases, the three PRs each target main with cumulative diffs — review base-first (#101094#101097#101098), reading each PR's new files as the delta. #101098, at the top of the stack, is also the full-feature branch to check out and run.


Cross-links: #101094, #101097, #101098.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:needs-security-reviewClawSweeper marked this issue as needing security-sensitive review.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.impact:securitySecurity boundary, credential, authz, sandbox, or sensitive-data risk.impact:ux-frictionUser-facing flow adds avoidable confusion or support burden without fully blocking progress.issue-rating: 🌊 off-meta tidepoolIssue quality rating does not apply to this item.maturity:stableIssue affects a taxonomy feature currently scored M4/M5.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions