Files
openclaw/docs/web/urls.md
T
Peter Steinberger cca5b14785 feat(ui): path-based session and dashboard URLs (#113883)
* feat(ui): path-based session and dashboard URLs

* docs(ui): document control UI URLs

* fix(ui): finalize session path routing

* feat(ui): anchor session URLs on stable keys

* docs(ui): clarify stable session URL identity

* fix(ui): resolve session prefixes with full prefix and pagination

Query sessions.list with the full supplied prefix instead of an eight-char
truncation, and paginate up to a bounded page count so longer disambiguation
links resolve instead of being reported ambiguous. Zero strict-prefix matches
now fall through to literal-key resolution rather than rendering an empty
ambiguity view.

Also document the ~dot/~dotdot segment escape: peer ids reach session keys
trimmed and lowercased only (src/routing/session-key.ts), so a literal '.' or
'..' segment is reachable and browsers would normalize it away.

* fix(ui): synchronize committed session routes

* test(ui): split native shell host coverage

* fix(ui): thread configured mainKey through session URL builders

Reserved-set disambiguation needs the operator-configured mainKey at runtime, so
thread it from agentsList through every session path builder and the ClickClack
control URL. Unambiguous non-hex single-segment rests now resolve literally while
short-id-shaped rests still fail closed, which restores ClickClack channel
compatibility detection and control-link reconciliation.

Also bound prefix-resolution retries, preserve catalog thread identity, and keep
draft state on ambiguous candidate links.

* fix(ui): repair session URL CI integration

* perf(ui): lazy-load session route resolution

* perf(ui): isolate session prefix resolution

* perf(ui): defer session path parsing

* perf(ui): defer session navigation startup

* fix(ui): preserve first-run and literal session navigation

* fix(ui): satisfy session routing type and export gates

* fix(clickclack): preserve unscoped control link agent

* style(ui): satisfy chat page line limit

* refactor(ui): move chat page helpers to owning modules

* fix(ui): preserve destination session route identity

* fix(ui): preserve agent identity in session routes

* fix(ui): escape dots in literal session path segments

encodeURIComponent leaves periods intact, so a literal key segment like
channel:release.js produced /chat/main/channel/release.js. In-app navigation is
intercepted by the SPA, but a refresh, an external link, or a ClickClack link
would be served as a static asset request and never reach the app.

pathForWorkboardBoard already escapes dots for this reason; mirror it in both the
session URL contract and the ClickClack encoder, route the agent id through the
same segment encoder, and pin the case in both shared vector tables.

* fix(ui): stop bootstrap after teardown race

* refactor(ui): centralize session navigation targets

* fix(ui): consume bootstrap teardown abort

* fix(ui): canonicalize configured main session routes

* fix(ui): preserve distinct session references

* fix(ui): redirect released session query links

* fix(ui): make bootstrap teardown abort-safe
2026-07-26 12:46:04 -04:00

12 KiB

summary, read_when, title
summary read_when title
Control UI URL routes, stable session-link grammar, and connection handoff parameters
You need to bookmark or share a Control UI session
You are adding or changing a Control UI route
You need a terminal, approval, onboarding, or remote Gateway URL
Control UI URLs

The Control UI uses readable paths for pages and session links. A configured gateway.controlUi.basePath prefixes every path below. For example, /chat/main becomes /openclaw/chat/main when the base path is /openclaw.

Session and dashboard URLs

Chat and dashboard views are parallel route namespaces:

/chat/main/deploy-monitor-6db92d48
/dashboard/main/deploy-monitor-6db92d48
/chat/main/telegram/12345
/chat/main/cron/nightly/run/8821
/chat/main

The path grammar is:

/<namespace>/<agentId>
/<namespace>/<agentId>/<sessionRef>
/<namespace>/<agentId>/<restSegment>/<restSegment>...

<namespace> is either /chat or /dashboard. The first form opens that agent's main session. The other forms encode one immutable session key in one of two ways.

The short-id form applies when the session key's rest, everything after agent:<agentId>:, ends in a UUID. <sessionRef> is an optional display-name slug plus a short id, such as deploy-monitor-6db92d48. The short id is the authoritative part: at least eight lowercase hexadecimal characters from the start of the key's trailing UUID, with UUID dashes omitted. Longer prefixes up to all 32 hexadecimal characters are accepted. The row's rotating sessionId is not part of the URL identity.

Every other key uses the literal-key form. Each colon-delimited segment after agent:<agentId>: becomes one URL-encoded path segment. For example, agent:main:telegram:12345 becomes /chat/main/telegram/12345, and agent:main:cron:nightly:run:8821 becomes /chat/main/cron/nightly/run/8821.

Literal rest segments exactly equal to . or .. use ~dot and ~dotdot so browsers cannot collapse them as relative path segments. A literal segment that starts with ~ doubles that leading character to keep the encoding reversible. When an otherwise literal one-segment rest could be mistaken for a short id, the builder inserts ~key before it, for example agent:main:release-deadbeef becomes /chat/main/~key/release-deadbeef. The marker forces literal interpretation and appears only when the unescaped form would be ambiguous.

The reserved single-segment literal rest names are main, global, boot, and sessions. The configured session.mainKey joins that set at runtime. Exactly one segment after the agent id is literal when it is reserved or does not contain a valid short id; otherwise it is a short reference. Two or more segments after the agent id are always literal.

Only the configured session.mainKey collapses to the agent-only main-session path. With session.mainKey: "workspace", agent:research:workspace becomes /chat/research, while the distinct key agent:research:main remains the literal path /chat/research/main.

Stability contract

The following parts are stable URL contracts:

  • The /chat and /dashboard namespace words.
  • The key UUID short id in short-id URLs.
  • The arity and short-versus-literal parsing rules above.

In short-id form, the agent segment and slug are explicitly decorative. They may change without notice and are not used to identify or validate the session. After resolution, the Control UI replaces the address bar with the current agent id and current display-name slug without adding a browser-history entry.

In literal-key form, the agent segment is authoritative because it is part of the reconstructed session key. The remaining literal segments are authoritative too. A slug, when present, is always decorative; literal-key forms do not synthesize one.

If one short id matches more than one session, the UI does not guess. It shows a small disambiguation view with the matching display names, agents, and longer id prefixes. Use a longer prefix to make the URL unique. Resolution examines at most five pages of search results; if more remain, the view says that the search was incomplete instead of guessing.

Canonical links do not use ?session= or ?face=. Released links such as /chat?session=<sessionKey> are accepted only at the application boundary as a migration aid and immediately rewritten, without adding browser history, to the canonical path. The released ?face=dashboard companion selects the /dashboard namespace during that rewrite. Loaders and page code never read the query-form identity, and new links must not emit it. The Sessions list keeps its own ?session= parameter because that parameter expands a row; it is not a session deep link. The one-shot composer value ?draft= remains supported on chat and dashboard session paths.

Route table

This table lists every Control UI application route. A dash means the route has no route-specific URL parameters.

Page Canonical path Aliases Parameters or dynamic forms
Chat /chat - Key-backed session forms above; ?draft=<text>
Dashboard /dashboard - Key-backed session forms above; ?draft=<text>
Ask OpenClaw /custodian - ?intent=new-agent, ?onboarding=1
New session /new - ?agent=<agentId>, ?catalog=<catalogId>
Activity /activity - -
Apps /apps - -
Agents /settings/agents /agents ?agent=<agentId>
Channels /settings/channels /channels Shared settings parameters below
Connection /settings/connection - Shared settings parameters below
General settings /settings/general /config Shared settings parameters below
Profile /settings/profile /profile Shared settings parameters below
Communications /settings/communications /communications Shared settings parameters below
Appearance /settings/appearance /appearance Shared settings parameters below
Notifications /settings/notifications - Shared settings parameters below
Security /settings/security - Shared settings parameters below
Advanced /settings/advanced - Shared settings parameters below
Approvals /settings/approvals - Shared settings parameters below
Automation settings /settings/automation /automation Shared settings parameters below
MCP /settings/mcp /mcp Shared settings parameters below
Infrastructure /settings/infrastructure /infrastructure Shared settings parameters below
Labs /settings/labs - Shared settings parameters below
About /settings/about - Shared settings parameters below
AI and agents /settings/ai-agents /ai-agents Shared settings parameters below
Model setup /settings/model-setup /model-setup ?firstRun=1
Model providers /settings/model-providers /model-providers Shared settings parameters below
Import memory /memory-import /settings/memory-import -
Workboard /workboard - /workboard/<boardId>
Worktrees /worktrees /settings/worktrees -
Sessions /sessions /settings/sessions ?session=<sessionKey>, ?status=archived|all
Usage /usage - -
Debug /debug - -
Logs /logs - -
Skill Workshop /skills/workshop - -
Skills /skills - -
Plugins /settings/plugins - ?tab=discover|installed
Automations /cron - -
Tasks /tasks - -
Devices /settings/devices /nodes Shared settings parameters below
Plugin tab host /plugin - ?plugin=<pluginId>&id=<tabId>

Settings routes that use schema-backed deep links accept ?section=<section>, ?advanced=1, and #<setting-id>. These values select content within the page; they do not change the route identity.

Special documents and startup modes

These Gateway-served documents sit outside the application route table:

  • /?onboarding=1 opens the first-run onboarding presentation.
  • /?view=terminal opens the full-screen terminal-only document used by the mobile apps. Availability still requires gateway.terminal.enabled and operator.admin.
  • /approve/<approvalId> opens a standalone approval document. With a base path, use <basePath>/approve/<approvalId>. The id identifies an approval but never authorizes it; normal Gateway authentication still applies.

The approval namespace is reserved ahead of plugin HTTP routes for all HTTP methods. When Control UI serving is disabled, it returns 404 instead of falling through to a plugin route.

Remote Gateway handoff

The Vite development UI can connect to a different Gateway:

http://localhost:5173/?gatewayUrl=ws%3A%2F%2F<gateway-host>%3A18789
http://localhost:5173/?gatewayUrl=wss%3A%2F%2F<gateway-host>%3A18789#token=<gateway-token>

URL-encode a full ws:// or wss:// value. gatewayUrl is accepted only in a top-level window, stored after load, and removed from the address bar. Prefer #token= because fragments do not enter HTTP request logs or Referer headers. The legacy ?token= handoff remains a bootstrap-only credential fallback and is stripped immediately. Passwords stay in memory only.

When gatewayUrl selects another Gateway, the UI does not fall back to local configuration or environment credentials. Provide the remote Gateway's token or password explicitly, and use wss:// behind TLS.