Skip to content

feat: render inline web chat widgets via capability-gated show_widget tool#101840

Merged
steipete merged 1 commit into
mainfrom
feat/webchat-widget-tool-101790
Jul 9, 2026
Merged

feat: render inline web chat widgets via capability-gated show_widget tool#101840
steipete merged 1 commit into
mainfrom
feat/webchat-widget-tool-101790

Conversation

@steipete

@steipete steipete commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Closes #101790

What Problem This Solves

Agents in the web chat can only answer in text and markdown. For content that is inherently visual — flow diagrams, small dashboards, comparisons, interactive explainers — a rendered inline widget communicates far better, and OpenClaw already ships the rendering substrate (the Canvas plugin's sandboxed embed pipeline). What was missing is a first-class tool for "render this markup inline in the chat transcript," and the underlying capability such a tool needs: a way to offer a tool only where the user's client can actually render its output. Today every gateway client (web UI, iOS, Android, macOS, CLI) collapses to the same internal message channel at tool-assembly time, so a web-only tool would have appeared — and dead-ended — on phones and terminals too.

Why This Change Was Made

Two parts, deliberately layered:

Client-capability-gated tool availability (generic core seam). Gateway clients already declare capability flags at connect (GATEWAY_CLIENT_CAPS, previously only tool-events). This adds an inline-widgets capability that the browser Control UI declares, stamps the connected client's caps into the per-run message context at chat.send dispatch (mirroring GatewayClientScopes), and threads them through the run/queue/compaction paths into tool assembly. Tools — core or plugin — can declare requiredClientCaps; createOpenClawTools drops any tool whose required caps the originating client did not declare. Channel runs (Telegram, WhatsApp, …) have no gateway client and therefore no caps, so gated tools are simply absent there, and tools.allow cannot resurrect them (capability is a hard availability fact, not policy). Queue collect-mode batching keys on caps so runs from different surfaces don't merge. Additive protocol only: one new capability string, no version bump, no new config keys.

show_widget tool (Canvas plugin). The agent passes a title plus widget_code (<svg …> or an HTML fragment, ≤256 KiB). The plugin wraps it in a minimal HTML shell and persists it through the existing Canvas document store — bounded to 32 documents per session scope, oldest evicted — and returns the canvas preview handle the web chat already knows how to render, so the widget appears inline from the tool call itself and survives history reload with zero new UI rendering machinery.

Security model (hardened through two structured-review cycles):

  • Widget iframes never get allow-same-origin: the preview carries a sandbox ceiling that caps the operator's global embedSandbox mode, enforced on both the inline preview and the sidebar open path.
  • The Canvas host serves widget documents with a Content-Security-Policy: sandbox allow-scripts response header, so navigating to the hosted URL directly also runs the widget in an opaque origin rather than the Control UI origin.
  • The widget document's own CSP (default-src 'none'; inline style/script; data: images) blocks external fetches and resource loads; the Control UI page CSP (default-src 'self', no frame-src) additionally restricts where embedded frames can navigate. Scripts remain allowed by design — widgets are interactive — with the residual self-navigation limitation documented on the docs page.

User Impact

Agents talking to the web Control UI gain a show_widget tool and can answer with rendered, optionally interactive, self-contained widgets inline in the chat. Users on Telegram, WhatsApp, the CLI, or the native apps see no change: the tool does not exist for those runs, so agents cannot call it into a void. Plugin authors get a documented, generic way to ship surface-conditional tools (requiredClientCaps), and native apps can adopt the widget surface later by declaring the capability. No configuration changes are required; the existing gateway.controlUi.embedSandbox tiers keep applying, with widgets capped below trusted.

Evidence

  • Capability gating: src/agents/openclaw-tools.client-caps.test.ts (absent caps exclude, superset includes, channel runs exclude, tools.allow cannot resurrect), src/auto-reply/reply/queue/drain.client-caps.test.ts (collect-mode batching keys on caps), src/gateway/server-methods/chat.directive-tags.test.ts (ctx stamping; 270 tests), src/plugins/tool-descriptor-cache.test.ts (caps survive descriptor caching).
  • Widget tool: extensions/canvas/src/widget-tool.test.ts (validation, SVG/HTML wrapping with CSP meta, opaque ids, per-scope eviction, manifest sandbox marker), extensions/canvas/src/host/server.test.ts (sandbox-marked documents get the CSP sandbox header and skip live reload; unmarked documents unchanged).
  • Web UI: ui/src/api/gateway.node.test.ts (Control UI declares inline-widgets), ui/src/lib/chat/tool-display.node.test.ts (sandbox ceiling math), ui/src/pages/chat/components/chat-tool-cards.node.test.ts (canvas handle extraction incl. sandbox; sidebar conversion preserves the ceiling), ui/src/lib/chat/message-normalizer.test.ts.
  • pnpm build green with no [INEFFECTIVE_DYNAMIC_IMPORT]; docs map/MDX/link checks green; oxfmt/oxlint clean on touched files; git diff --check clean.
  • Structured autoreview ran until clean: two P1 findings surfaced and were fixed with regression tests (sidebar path dropping the sandbox ceiling under trusted mode; hosted widget documents executing as the app origin when opened directly), final review reported no actionable findings.
  • Focused proof ran locally (hosted CI verifies the full gates at the exact head); remote box warmups were queued at the time, noted per validation policy.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation app: web-ui App: web-ui gateway Gateway runtime agents Agent runtime and tooling size: L maintainer Maintainer-authored PR labels Jul 7, 2026
@clawsweeper

clawsweeper Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs changes before merge. Reviewed July 9, 2026, 5:18 AM ET / 09:18 UTC.

Summary
The branch adds a Gateway inline-widgets client capability, plugin requiredClientCaps filtering, Canvas show_widget inline web-chat widgets, UI rendering support, docs, and regression tests.

PR surface: Source +446, Tests +474, Docs +66. Total +986 across 59 files.

Reproducibility: not applicable. this is a feature PR rather than a broken existing behavior report. Source inspection confirms current main lacks the requested capability and the PR head adds it.

Review metrics: 3 noteworthy metrics.

  • Plugin API surface: 1 field added. requiredClientCaps becomes visible through plugin tool registration, so maintainers should review it as SDK/API direction.
  • Gateway capabilities: 1 capability added. inline-widgets changes Gateway handshake semantics and gates prompt tool availability by client surface.
  • Canvas manifest metadata: 2 fields added. retentionScope and cspSandbox persist in Canvas document manifests and affect cleanup plus sandbox serving behavior.

Stored data model
Persistent data-model change detected: serialized state: extensions/canvas/src/documents.ts, serialized state: extensions/canvas/src/host/server.test.ts, serialized state: extensions/canvas/src/host/server.ts, serialized state: extensions/canvas/src/widget-tool.test.ts, serialized state: src/auto-reply/reply/queue/drain.ts. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #101790
Summary: This PR is the active implementation candidate for the open inline-widget/client-capability feature request; the CLI-backed backend gap is a follow-up limitation, while older rich-widget and dashboard items overlap but do not own the same remaining work.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🐚 platinum hermit ✨ media proof bonus
Patch quality: 🦪 silver shellfish
Result: blocked by patch quality or review findings.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Fix the retention pruning bug so the just-created widget cannot be deleted before show_widget returns.
  • [P2] Repair the widget test type narrowing and remove the extension-to-core deep import.
  • After fixes, rerun the focused widget test plus exact-head type and boundary checks.

Mantis proof suggestion
A web UI chat proof after the mechanical fixes would materially show the new inline widget tool rendering in the transcript and surviving history reload; Mantis is not the right tool for CSP header diagnostics. A maintainer can ask Mantis to capture proof by posting this exact PR comment:

@openclaw-mantis web ui chat proof: verify that an agent in Control UI web chat can call show_widget, renders the inline widget, and still renders it after history reload.

Risk before merge

  • [P1] The PR adds a plugin-visible requiredClientCaps API and new Gateway capability semantics, so maintainers need to accept the long-term SDK/protocol direction before merge.
  • [P1] The PR intentionally renders model-authored HTML/SVG with script support inside hosted Canvas documents, so the CSP, iframe sandbox ceiling, direct-open behavior, and persistence model are security-boundary decisions.
  • [P1] Exact-head CI still has supported-surface failures in test types and extension-boundary checks, and the source-visible retention pruning bug can return a missing widget URL.

Maintainer options:

  1. Repair Blockers, Then Accept The Contract (recommended)
    Fix the pruning, type, and extension-boundary defects, then require maintainer acceptance of the plugin API and scripted widget sandbox before landing.
  2. Accept The Security Boundary After Green CI
    Maintainers may choose to land once the mechanical blockers are fixed if they are comfortable owning the scripted widget CSP and iframe sandbox model.
  3. Pause For API Redesign
    If requiredClientCaps is not the desired SDK contract, pause this branch and design a narrower capability seam first.
Copy recommended automerge instruction
@clawsweeper automerge

Special instructions:
Fix this PR's mechanical blockers without changing the feature direction: ensure `createCanvasDocument` cannot prune the document it just created, remove the extension test's core `src/**` deep import, return or assert a narrowed preview type in `extensions/canvas/src/widget-tool.test.ts`, add focused regression coverage, and validate `check-test-types` plus the extension-boundary lane or their repo wrappers.

Next step before merge

  • [P2] A narrow automated repair can address the retention, type, and extension-boundary blockers before maintainer API/security acceptance.

Maintainer decision needed

  • Question: Should OpenClaw accept requiredClientCaps as the plugin-visible tool API and ship Canvas show_widget as a bundled scripted web-chat widget surface after the mechanical blockers are fixed?
  • Rationale: The patch creates a durable plugin/gateway API contract and a model-authored scripted HTML sandbox surface; source review can identify defects, but the permanent API and security boundary need maintainer intent.
  • Likely owner: steipete — He owns the current candidate implementation and has the strongest recent history signal across the affected Canvas, Control UI, Gateway, and Codex surfaces.
  • Options:
    • Accept After Repair (recommended): Fix the retention, type, and extension-boundary blockers, then land the capability seam and Canvas widget surface with explicit maintainer acceptance.
    • Narrow The API First: Keep the widget direction but hide or redesign the plugin-visible capability contract before exposing it as SDK surface.
    • Pause Bundled Widgets: Defer this PR if maintainers want inline widgets to stay external or ClawHub-only until more API and security evidence exists.

Security
Cleared: No concrete security defect was found in the inspected CSP, iframe sandbox ceiling, direct-open sandbox header, or capability-gating code, but the scripted widget surface remains a maintainer-owned security-boundary decision.

Review findings

  • [P2] Exclude the returned widget from retention pruning — extensions/canvas/src/documents.ts:171-174
  • [P2] Return a narrowed preview from the helper — extensions/canvas/src/widget-tool.test.ts:50
  • [P2] Keep the extension test on public seams — extensions/canvas/src/widget-tool.test.ts:6
Review details

Best possible solution:

Land only after the mechanical blockers are repaired, exact-head CI is green, real web-chat proof remains attached, and maintainers explicitly accept the plugin capability API plus scripted widget sandbox boundary.

Do we have a high-confidence way to reproduce the issue?

Not applicable: this is a feature PR rather than a broken existing behavior report. Source inspection confirms current main lacks the requested capability and the PR head adds it.

Is this the best way to solve the issue?

Mostly yes, but not merge-ready: a generic client-capability seam plus Canvas-owned rendering is a cleaner fit than channel gating or raw DOM rendering. The current branch still needs the retention, type, and extension-boundary fixes plus maintainer acceptance of the API/security direction.

Full review comments:

  • [P2] Exclude the returned widget from retention pruning — extensions/canvas/src/documents.ts:171-174
    createCanvasDocument writes the new manifest and then prunes the scope by timestamp/id. If more than 32 same-scope widgets are created in the same millisecond, the just-created random id can sort into the deletion slice and show_widget returns a preview URL whose index.html was already removed; exclude the current id or prune before writing it.
    Confidence: 0.87
  • [P2] Return a narrowed preview from the helper — extensions/canvas/src/widget-tool.test.ts:50
    executeWidget throws when preview.viewId or preview.url is missing, but returns the original optional preview type. The exact-head check-test-types lane still reports these call sites as string | undefined; return a narrowed preview type or assert through a typed helper.
    Confidence: 0.93
  • [P2] Keep the extension test on public seams — extensions/canvas/src/widget-tool.test.ts:6
    The Canvas extension test imports ../../../src/chat/canvas-render.js, crossing from extensions/ into private core source. The scoped extension guide and the exact-head boundary lane require extension tests to stay on plugin/public seams; assert the handle shape locally or move parser coverage to the core/UI tests that own it.
    Confidence: 0.91

Overall correctness: patch is incorrect
Overall confidence: 0.91

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 0de5d37f92fc.

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR comment includes a live Testbox E2E description and an inspected screenshot showing a real Control UI chat turn rendering the show_widget output inline; contributors should continue redacting private endpoints, keys, phone numbers, and other private details in proof artifacts.
  • add proof: 📸 screenshot: Contributor real behavior proof includes screenshot evidence. The PR comment includes a live Testbox E2E description and an inspected screenshot showing a real Control UI chat turn rendering the show_widget output inline; contributors should continue redacting private endpoints, keys, phone numbers, and other private details in proof artifacts.
  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🐚 platinum hermit and patch quality is 🦪 silver shellfish.
  • remove rating: 🦐 gold shrimp: Current PR rating is rating: 🦪 silver shellfish, so this older rating label is no longer current.

Label justifications:

  • P2: This is a normal-priority feature/API PR with limited blast radius, concrete merge blockers, and maintainer-owned API/security review needs.
  • merge-risk: 🚨 compatibility: The PR adds a plugin-visible tool capability contract and Gateway capability semantics that can affect future plugin tool availability and upgrade expectations.
  • merge-risk: 🚨 security-boundary: The PR renders model-authored HTML/SVG with script support inside hosted Canvas documents, so isolation must be intentionally accepted before merge.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🐚 platinum hermit and patch quality is 🦪 silver shellfish.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (screenshot): The PR comment includes a live Testbox E2E description and an inspected screenshot showing a real Control UI chat turn rendering the show_widget output inline; contributors should continue redacting private endpoints, keys, phone numbers, and other private details in proof artifacts.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR comment includes a live Testbox E2E description and an inspected screenshot showing a real Control UI chat turn rendering the show_widget output inline; contributors should continue redacting private endpoints, keys, phone numbers, and other private details in proof artifacts.
  • proof: 📸 screenshot: Contributor real behavior proof includes screenshot evidence. The PR comment includes a live Testbox E2E description and an inspected screenshot showing a real Control UI chat turn rendering the show_widget output inline; contributors should continue redacting private endpoints, keys, phone numbers, and other private details in proof artifacts.
Evidence reviewed

PR surface:

Source +446, Tests +474, Docs +66. Total +986 across 59 files.

View PR surface stats
Area Files Added Removed Net
Source 39 460 14 +446
Tests 14 475 1 +474
Docs 6 66 0 +66
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 59 1001 15 +986

Acceptance criteria:

  • [P1] pnpm test extensions/canvas/src/widget-tool.test.ts.
  • [P1] pnpm check:test-types.
  • [P1] pnpm run lint:plugins:no-extension-test-core-imports.
  • [P1] pnpm check:changed -- extensions/canvas/src/documents.ts extensions/canvas/src/widget-tool.test.ts.

What I checked:

  • Repository policy read: Root and scoped AGENTS.md files for docs, extensions, agents, agent tools, embedded runner, plugins, gateway, gateway server methods, and UI were read; the plugin SDK, extension-boundary, gateway protocol, and sandbox/security guidance affected the review. (AGENTS.md:1, 0de5d37f92fc)
  • Current main lacks the requested capability: Current main defines only the tool-events Gateway client capability and the Control UI connects with only that cap; no inline-widgets, requiredClientCaps, or show_widget implementation is present. (packages/gateway-protocol/src/client-info.ts:75, 0de5d37f92fc)
  • PR adds the generic capability gate: The PR head adds filterToolsByClientCaps() and applies it in normal and plugin-only tool assembly paths, making requiredClientCaps a hard availability filter rather than a policy allowlist. (src/agents/openclaw-tools.ts:94, 2ab460cee0f7)
  • PR adds Canvas widget storage and sandbox markers: show_widget creates hosted Canvas HTML documents with retentionScope, cspSandbox: "scripts", and a preview sandbox ceiling, while the Canvas host serves sandbox-marked HTML with Content-Security-Policy: sandbox allow-scripts. (extensions/canvas/src/widget-tool.ts:89, 2ab460cee0f7)
  • Returned-widget pruning blocker remains: The PR still writes the new document and then prunes by timestamp/id without excluding the current id, so same-millisecond entries can delete the just-created document before its preview URL is returned. (extensions/canvas/src/documents.ts:171, d7a9fc81f209)
  • Current CI confirms test type and boundary blockers: The exact-head CI logs show check-test-types failing on preview.viewId as string | undefined in extensions/canvas/src/widget-tool.test.ts, and check-additional-boundaries-bcd failing because that extension test imports ../../../src/chat/canvas-render.js. (extensions/canvas/src/widget-tool.test.ts:6, 2ab460cee0f7)

Likely related people:

  • steipete: He authored the linked feature issue and this PR, and current history shows recent work on the Control UI chat/sidebar surface while the PR head adds the Canvas, gateway, and Codex app-server changes under review. (role: implementation candidate owner and recent area contributor; confidence: high; commits: ecc2cffad8aa, d7a9fc81f209, 01ae9ed8ec6f; files: ui/src/pages/chat/components/chat-tool-cards.ts, extensions/canvas/src/documents.ts, src/agents/openclaw-tools.ts)
  • Takhoffman: He authored the merged hosted-embed/chat directive foundation that this PR extends for inline Canvas previews and sandboxed web-chat rendering. (role: adjacent rich-rendering contributor; confidence: high; commits: cc5c691f0069; files: docs/web/control-ui.md, src/chat/canvas-render.ts, ui/src/pages/chat/components/chat-tool-cards.ts)
  • lsr911: The current main tree blames recent broad touches of the gateway/client-info and Canvas/UI docs paths to this contributor, making them a weak but visible recent-history routing signal for the affected files. (role: recent adjacent contributor in current main history; confidence: low; commits: d0c49a7ad422; files: packages/gateway-protocol/src/client-info.ts, ui/src/api/gateway.ts, docs/web/control-ui.md)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (3 earlier review cycles)
  • reviewed 2026-07-07T18:07:55.732Z sha af2a54f :: needs real behavior proof before merge. :: [P2] Return a narrowed preview from the widget test helper | [P2] Remove the extension test's core deep import
  • reviewed 2026-07-07T18:18:45.243Z sha af2a54f :: needs real behavior proof before merge. :: [P2] Exclude the new widget from retention pruning | [P2] Return a narrowed preview from the widget test helper | [P2] Remove the extension test's core deep import
  • reviewed 2026-07-07T20:27:29.228Z sha af2a54f :: needs real behavior proof before merge. :: [P2] Exclude the returned widget from retention pruning | [P2] Return a narrowed preview from the helper | [P2] Keep the extension test on public seams

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: af2a54f8ef

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +167 to +171
.toSorted(
(left, right) =>
left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id),
)
.slice(0, deleteCount);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude the new widget from retention pruning

When a session creates widgets fast enough that the new manifest gets the same millisecond createdAt as existing widgets after the 32-document cap is exceeded, this sort can select the just-created random id as one of the “oldest” entries and the fs.rm below deletes it before createCanvasDocument returns. That leaves show_widget returning a preview URL whose index.html was already removed, so the inline widget renders as missing/404; exclude the current id from pruning or prune before writing the new document.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. labels Jul 7, 2026
@steipete
steipete force-pushed the feat/webchat-widget-tool-101790 branch from af2a54f to 2ab460c Compare July 9, 2026 08:55
steipete added a commit to steipete/openclaw-pr-assets that referenced this pull request Jul 9, 2026
steipete added a commit to steipete/openclaw-pr-assets that referenced this pull request Jul 9, 2026
@steipete

steipete commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Live end-to-end proof (Blacksmith Testbox, real gpt-5.5)

Ran the full user path on a Testbox lease: built this branch, started a real gateway with a live OpenAI key, drove the Control UI with headless Chromium (connect → prompt → tool call → render), and screenshot-verified the result. The model was asked to render a "Deploy status" dashboard; it called show_widget directly and the widget rendered inline in the transcript, fitted to content, sandboxed.

Widget rendered inline in web chat

Verified live, not just in unit seams:

  • openclaw-control-ui connects declaring inline-widgets; chat.send stamps the caps; the run assembles show_widget; a channel-less/capless run does not get it.
  • The tool result renders through the existing canvas preview path (sandboxed iframe, sandbox="allow-scripts", capability-ticket URL) and survives history reload.
  • Direct fetch of the hosted widget document returns Content-Security-Policy: sandbox allow-scripts through the capability host, so opening the URL outside the iframe still lands in an opaque origin.
  • The widget document reports its content height and the chat fits the iframe (the stylesheet previously floored frames at 420px), stable across lit re-renders.

The E2E loop surfaced and fixed four real gaps that unit tests missed (now covered by regression tests in this PR):

  1. A plugin registering a second tool was blocked when one tool shares the plugin id — the id-conflict guard treated the plugin's own canvas tool as a shadowing core name, dropping show_widget entirely.
  2. Queued followup runs rebuilt runEmbeddedAgent params inline and dropped clientCaps.
  3. The Codex app-server harness (buildDynamicTools) never forwarded clientCaps into tool assembly — gpt-5.5 runs had no gated tools.
  4. Plugin-only tool construction plans bypassed createOpenClawTools and its capability filter.

Known limitation (documented on the docs page, follow-up filed): CLI-backed model backends do not yet transport client capabilities, so gated tools stay unavailable there (fail closed) — #102577.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Jul 9, 2026
… tool

Adds client-capability-gated tool availability: gateway clients declare
capabilities at connect (new inline-widgets cap), chat.send stamps them into
the run context, and every tool assembly path (embedded runner, queued
followups, Codex app-server harness, plugin-only construction plans) drops
tools whose requiredClientCaps the originating client did not declare. The
Canvas plugin ships the first such tool, show_widget: agents pass SVG or an
HTML fragment plus a title; the plugin hosts it as a bounded, retention-scoped
Canvas document and returns the existing canvas preview handle, which web chat
renders as a sandboxed iframe fitted to the widget's reported content height.
Widget frames never get allow-same-origin (per-preview sandbox ceiling,
including the sidebar path) and the Canvas host serves widget documents with a
CSP sandbox header so direct navigation runs in an opaque origin. Verified
live end-to-end on a Testbox with gpt-5.5 (screenshots on the PR). CLI-backed
model backends do not carry client caps yet and stay fail-closed (#102577).

Closes #101790
@steipete
steipete force-pushed the feat/webchat-widget-tool-101790 branch from 2ab460c to b793955 Compare July 9, 2026 11:17

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b7939558ab

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +106 to +110
return jsonResult({
kind: "canvas",
presentation: { target: "assistant_message", title, sandbox: "scripts" },
view: { id: document.id, url: document.entryUrl },
text: `Widget hosted at ${document.entryUrl}`,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Render show_widget previews during live tool events

When show_widget runs in the live Control UI, this result is delivered as a tool-stream/tool-result JSON text payload; the live renderer extracts card.preview from that text, but renderToolPreview(..., "chat_tool") deliberately returns nothing, and only chat.history later lifts the canvas preview onto an assistant message. That means the new widget does not render inline during the run as documented, and users only see raw details/sidebar until a history reload; emit or project a live assistant canvas block for this result path.

Useful? React with 👍 / 👎.

@steipete
steipete merged commit 3ccf2a0 into main Jul 9, 2026
108 of 110 checks passed
@steipete
steipete deleted the feat/webchat-widget-tool-101790 branch July 9, 2026 11:29
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 10, 2026
… tool (openclaw#101840)

Adds client-capability-gated tool availability: gateway clients declare
capabilities at connect (new inline-widgets cap), chat.send stamps them into
the run context, and every tool assembly path (embedded runner, queued
followups, Codex app-server harness, plugin-only construction plans) drops
tools whose requiredClientCaps the originating client did not declare. The
Canvas plugin ships the first such tool, show_widget: agents pass SVG or an
HTML fragment plus a title; the plugin hosts it as a bounded, retention-scoped
Canvas document and returns the existing canvas preview handle, which web chat
renders as a sandboxed iframe fitted to the widget's reported content height.
Widget frames never get allow-same-origin (per-preview sandbox ceiling,
including the sidebar path) and the Canvas host serves widget documents with a
CSP sandbox header so direct navigation runs in an opaque origin. Verified
live end-to-end on a Testbox with gpt-5.5 (screenshots on the PR). CLI-backed
model backends do not carry client caps yet and stay fail-closed (openclaw#102577).

Closes openclaw#101790
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling app: web-ui App: web-ui docs Improvements or additions to documentation extensions: codex gateway Gateway runtime maintainer Maintainer-authored PR merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. P2 Normal backlog priority with limited blast radius. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: XL status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Inline agent widgets in web chat via client-capability-gated tools

1 participant