* fix(gateway): stop device pairing approval alert floods from retrying devices
A device retrying with a broken token minted a new pending requestId (and
approval alert broadcast) every 5-minute TTL window because refresh preserved
ts and expiry keyed on ts. Pending requests now stay alive via an internal
refreshedAtMs keepalive (ts still owns ordering/--latest), superseded device
requests broadcast device.pair.resolved like node pairing already did, and the
Mac app keeps one alert per device, closes superseded visible alerts, and
resyncs its queue when approving a stale request.
Closes#100974
* chore: satisfy swiftformat and refresh native i18n inventory for pairing prompter changes
* feat(crestodian): AI-only conversation with model-judged approvals
Every chat message now routes to the AI (agent loop, then planner); the
typed grammar is a single anchored command language used only as the
no-model deterministic fallback and for rescue/one-shot, so natural
language can no longer misparse into operations. Approval of pending
mutations is judged from the user's own words by a host-run classifier
(closed-list fast path, single-shot model judgment, fail-closed), never
by the conversation model. New connect_channel/open_agent ring-zero tool
directives let the AI start the hosted channel wizard and agent handoff.
Chat turns are serialized, stale host proposals are cleared once the
agent loop owns the conversation, sensitive config-set values are
redacted from AI-visible history, and Gemini CLI joins the overview,
models output, prompts, and local backend ladder.
Fixes#100604
* test(crestodian): cover AI-only turns, approval intents, directives, and anchored grammar
* docs(crestodian): AI-only conversation, natural approvals, Gemini fallback
* test(crestodian): classify decline via text classifier in agent-loop seam
* fix(crestodian): correct textResult arity and directive handoff type
* fix(crestodian): keep exact sensitive config-set commands off every model path
* feat(ui): declutter the sidebar — pinned/chats groups, stable ordering, palette session search, titlebar brand in macOS app
- Sessions split into Pinned and Chats groups; pins are exempt from the
nine-row recency cap and the open session highlights in place instead of
hoisting to the top, so clicking a row no longer reshuffles the list.
- The command palette (Cmd+K) is the single search: it queries sessions
across agents server-side and lists matching chats next to nav commands;
the sidebar session-picker popover and its magnifier are removed, and the
topbar search pill shrinks to an icon button.
- The sidebar brand row is gone: web keeps the breadcrumb wordmark, and the
macOS app shows a compact brand mark in the native titlebar strip next to
the traffic lights (CSS-gated on the injected openclaw-native-macos class).
- The agent filter dropdown becomes a compact scope chip in the Chats group
header; the welcome badge drops its duplicate claw logo.
- Footer collapses to one icon row (status dot, settings, docs, pairing,
mobile theme switch); default pinned nav routes are now Overview-only and
the Recent-collapse preference is retired.
* test(ui): keep sidebar hover-reveal proof before the stability click; refresh locale metadata
Clicking the row leaves :focus-within on it, which keeps the management
actions visible; the hidden-actions assertion must run first.
* test(ui): archive an idle session in the sidebar e2e; assert running rows stay archive-disabled
* fix(ui): drop stale and hidden rows from palette session search
Invalidate the in-flight search and clear results on every query change so a
late-resolving previous-query request cannot leave stale rows selectable, and
run results through getVisibleSessionRows so archived/global/unknown/cron/
subagent rows stay hidden like the sidebar list. Codex review findings.
* fix(ui): over-fetch palette session search so hidden rows cannot starve visible matches
Exclude global/unknown rows server-side and fetch a full 50-row page before
applying the sidebar's hidden-row filter, keeping the 10-row display cap.
Codex review finding.
* chore(i18n): refresh sidebar locale metadata after rebase
* fix(ui): bound command palette session search
* docs: defer release notes to release generation
Second sweep: 13 cards from the openclaw.ai X showcase and Discord
maintainer channel — car negotiation, flight check-in, insurance
claim, Idealista skill, tokenomics cost tracker, Excalidraw
diagrams, GA4 skill, X post pipeline, lab results to Notion,
Pebble Ring voice, creator media studio, HomePod skill, holo cube,
macOS menu bar app — plus a Related link to the full X showcase.
oxfmt 0.57.0 --check passed; hook bypassed (no node_modules in
worktree).
Adds 12 community cards sourced from Discord (#skills, #general,
shell-society) and the web: Dropage deploy, anti-scam URL checker,
product-design skill trio, miab-broker, lite-mode, ClawEval,
Music Craft, gardening back office, Obsidian second brain,
family history bot, Action Button walkie-talkie, and Clinch.
All links verified live; oxfmt 0.57.0 --check passed (worktree
lacks node_modules, hook bypassed after proof).
Gateway (additive, no protocol version bump): SessionEntry gains
lastReadAt/markedUnreadAt/lastActivityAt; session rows expose a derived
unread flag (explicit mark, or last read before latest activity; never-read
sessions stay read so upgrades do not light up). lastActivityAt is stamped
in the canonical post-run store update - user, channel, and cron runs count
as activity; heartbeat, internal-event, and preserved-state runs do not.
sessions.patch gains unread; sessions.create gains fork (transcript fork
from parentSessionKey under the parent lifecycle lock, refusing active,
concurrently-changed, and oversized parents, cross-agent aware).
Web sidebar: Pinned/custom-group/Ungrouped sections, unread dots, kebab and
right-click context menu (pin, mark unread/read, rename, fork, move to group,
archive, delete guarded for agent main sessions and active runs), mark-read
on view with loop-safe re-acknowledgement and failure retry; sessions page
gets unread + fork actions and shared custom-group helpers.
iOS Command Center: grouped sections, unread/pin indicators, Show Archived
gated on per-entry state, full context menu with rename/new-group alerts and
delete confirmation, current-session preview guarantee, read-episode
re-acknowledgement; new patch/delete/fork transport calls; Swift protocol
models regenerated.
Android SessionsScreen: grouped headers, unread/pin indicators, Archived
filter gated on per-entry state, long-press menu with the full control set,
agent-scoped forks, explicit label/category clears from session events,
main-session fallback when archiving/deleting the open chat, read-episode
re-acknowledgement with failure retry.
Closes#100739
* feat: correlate native search outcomes in audit history
Metadata-only audit ledger for agent runs and tool actions in the shared
state DB: stable event identity, closed action/status/error vocabularies,
one-way-hashed tool-call ids, never-inferred terminal outcomes for native
web-search (explicit completed/failed only; otherwise unknown), bounded
retention, audit.list gateway RPC and openclaw audit CLI. Squashed from
the 82-commit audit stack for replay onto current main.
* feat(audit): add audit.enabled config gate (default on)
The metadata-only audit ledger records by default: an audit trail enabled
only after an incident cannot explain the incident, and the rows are
strictly less sensitive than the transcripts every install already
stores. audit.enabled=false stops new writes at the gateway subscription
seam; audit.list and openclaw audit keep serving existing records until
they expire. Documented in the configuration reference, protocol page,
and CLI reference.
* fix: repair full-matrix CI findings after rebase
- break the dynamic-tools/dynamic-tool-execution import cycle by
extracting resolveCodexToolAbortTerminalReason into a leaf module
- restore main's session-worktree protocol exports lost in the
index.ts auto-merge
- register the audit event writer worker as a knip entry point
- docs table formatting; subagent wait-cancellation test scoped to its
audit intent (outcome + timing) and advanced past main's new
lifecycle-timeout retry grace
Adds an additive worktree flag to sessions.create so any new chat can run in an
isolated managed worktree of the agent's git workspace, with the branch checked
out and .worktreeinclude provisioning applied. The session's spawnedCwd points at
the matching subdirectory inside the worktree so chat runs, CLI, and the file
browser execute there. agents.list gains workspaceGit (workspace or an ancestor
is a git checkout) to gate the affordance; web sidebar, iOS, and Android expose a
New-Chat-in-worktree action. Uses the method's operator.write scope, but the
.openclaw/worktree-setup.sh step runs only for operator.admin callers since it
executes repo code. Deleting the session, or leaving via a plain New Chat, clears
the cwd and lossless-removes the worktree; idle GC treats recent session activity
as worktree activity so an active session's checkout is never swept.
Live-verified end-to-end on a real gateway; follow-up to #100535 (issue #100534).
* feat(browser): restore driver "extension" via loopback Chrome extension relay
Reintroduces browser profile driver "extension" (removed in 2026.3.22) as a
loopback relay that drives the user's signed-in Chrome through an MV3 extension
instead of the remote-debugging port. This avoids Chrome's blocking "Allow
remote debugging?" prompt, which cannot be clicked when the operator drives
OpenClaw from a phone. Automated tabs live in an "OpenClaw" tab group (the
consent boundary), mirroring the Codex/Claude-in-Chrome model.
- relay bridge synthesizes the CDP browser target surface for Playwright
connectOverCDP and forwards session-scoped commands to chrome.debugger
- relay server binds loopback only; both sides authenticate with a token
derived (HMAC-SHA256) from gateway auth, so the raw credential never reaches
Chrome; extension origin + loopback Host checks guard the upgrade
- built-in "chrome" profile; distinct relay ports per extension profile;
relay reconciles on auth rotation / cdpPort change and prunes removed profiles
- doctor + status surface the extension transport; doctor keeps repairing the
retired relay endpoint URL on legacy "extension" profiles
Refs #53599
* feat(browser): bundle the OpenClaw MV3 Chrome extension
Thin MV3 extension (chrome-extension/): a WebSocket client to the loopback
relay plus chrome.debugger forwarding and OpenClaw tab-group management. All
CDP target synthesis lives server-side in the relay bridge, so the extension
stays a dumb transport (the removed 2026.3 extension put that logic in a
1000-line untestable service worker). Popup handles pairing and per-tab share
toggle; `openclaw browser extension path|pair` load and pair it. A build copy
hook stages it into dist so the load path is stable.
Refs #53599
* docs(browser): document the Chrome extension profile
Adds docs/tools/chrome-extension for the restored extension driver (install,
pair, tab-group consent model, security posture) and wires it into the browser
docs profile section and nav.
Refs #53599
* feat(browser): make the extension relay work on remote browser nodes
Derives the relay auth token from a host-local secret in the credentials dir
(created on first use) instead of gateway auth. Each machine that runs a
browser — the gateway host and every browser node host — owns its own token, so
the extension pairs with whichever machine hosts its Chrome and no gateway
credential travels to a node. The node host already runs the shared browser
control bootstrap, so this is all that was missing for cross-machine control.
Also removes the "relay needs gateway auth before it can start" failure mode:
startup and `openclaw browser extension pair` ensure the secret exists.
Refs #53599
* fix(browser): harden relay secret creation and satisfy CI lint/typecheck
- Make the host-local relay secret creation atomic (O_CREAT|O_EXCL + adopt the
winner on EEXIST) so the gateway service and `extension pair` CLI cannot mint
divergent tokens on a fresh host (would 401 until restart); credentials dir
created mode 0700. (adversarial review finding)
- Resolve type-aware oxlint findings across the relay + extension: unknown catch
vars, addEventListener over ws.on* in the MV3 worker, void async listeners,
drop useless returns/spreads, Object.assign over map-spread, safe ws frame
decode (Buffer[]/ArrayBuffer), toSorted.
- Add extensionRelayDefaultPort/extensionRelayPorts to remaining test config
literals; type the extension relay-core module (.d.ts, excluded from dist);
regenerate docs_map.
* fix(browser): satisfy OpenGrep security policy on the relay
- Hash both operands before timingSafeEqual so token comparison has no
length short-circuit (GHSA-JJ6Q-RRRF-H66H).
- Bound the relay WebSocketServer with maxPayload (64 MiB, headroom for CDP
screenshots/bodies) against oversized frames (GHSA-VW3H-Q6XQ-JJM5).
- Rewrite the config test env helper to avoid the skill-env-host-injection
shape (GHSA-82G8-464F-2MV7); it is a test-only env swap.
Reactive corrections ("that's not what I asked", "stop doing X") now
count as durable signals: expanded extraction patterns, vocabulary
routing to existing workspace skills, per-skill grouping. Both capture
modes share one invariant: a bounded signal-fingerprint ring on the
session entry prevents replaying applied/rejected corrections, pending
autocapture-owned proposals are revised instead of skipped, /learn-style
turns suppress duplicate agent-end capture, and extraction runs before
any skill discovery. Autonomy off keeps the suggest-tier offer; autonomy
on files/revises proposals directly.
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* improve(cron): show consecutive failure count and last error in cron list/show
* improve(cron): show consecutive failure count and last error in cron CLI output
* fix(clownfish): address review for live-pr-inventory-20260706T074223-002 (1)
---------
Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
* fix(hooks): flag hook event names that no trigger site emits
* docs(hooks): clarify bare family subscriptions in unknown-event note
* fix(hooks): word unknown-event diagnostics around core-emitted keys
* fix(hooks): apply unknown-event advisory to legacy config handlers too