100 Commits
Author SHA1 Message Date
Peter SteinbergerandGitHub d4766068b8 feat(ui): preview lobster sound when enabled (#111996)
* feat(ui): preview lobster sounds on enable

* perf(ui): keep sound preview within startup budget

* perf(ui): add sound preview bundle margin
2026-07-20 18:46:59 -07:00
Peter SteinbergerandGitHub 926c603006 fix(ui): dismiss new-session model picker on outside click (#111988)
* fix(ui): dismiss new-session picker on outside click

* perf(ui): keep picker dismissal within startup budget

* perf(ui): lazy-load new-session catalog helpers
2026-07-20 18:44:44 -07:00
Peter SteinbergerandGitHub 7c896d7859 test(ui): serialize full-app responsive browser cases (#111997) 2026-07-20 18:26:59 -07:00
Peter SteinbergerandGitHub 3d841472bd fix(cli): align configure and channel wizard behavior (#111720) 2026-07-20 18:22:57 -07:00
Peter SteinbergerandGitHub f06b6be182 feat(macos): manage Gateway profiles and multiple windows (#111986)
* feat(macos): manage gateway profiles and windows

* fix(macos): preserve gateway settings localization

* fix(deps): update axios past security advisory
2026-07-20 18:10:02 -07:00
Peter SteinbergerandGitHub 49ff6a4497 fix(protocol): classify system agents in rosters (#111920)
* fix(protocol): classify system agents in rosters

* fix(ci): align agent kind client gates

* fix(ci): keep roster filtering in startup bundle

* chore(ui): remove superseded roster module

* style(android): satisfy agent kind test formatting

* style(linux): format merged agent capability assertion

* fix(config): reject reserved system agent ids

* chore(i18n): refresh native source inventory

* style(android): format agent kind surfaces

* fix(gateway): preserve configured agent ownership
2026-07-20 16:57:04 -07:00
Peter SteinbergerandGitHub 2220e9f841 fix(ui): give settings sidebar rows unique icons (#111974) 2026-07-20 16:34:05 -07:00
Peter SteinbergerandGitHub b41263339b fix(ui): use layout-dashboard glyph for session dashboard indicator (#111975) 2026-07-20 16:32:25 -07:00
Peter SteinbergerandGitHub d5a8233e76 feat(gateway,ui): agent-scoped model provider credentials (#111796)
* feat: scope model provider credentials by agent

* fix: reject unknown model auth agent ids

* fix: discard stale model provider preload

* fix(gateway): reject whitespace-only explicit agent ids

* fix: scope model provider probes by agent

* fix: complete model provider agent scoping

* fix(ui): always clear the providers refresh flag on completion

* fix(ui): keep probe epochs monotonic across agent switches

* fix: derive agent scope for provider aborts

* fix: satisfy model provider CI checks
2026-07-20 16:22:09 -07:00
Peter SteinbergerandGitHub 7cafe35bd5 fix(onboard): never silently remap an existing fleet's workspaces (#111787)
* fix(onboard): preserve agent workspaces on rerun

* fix(onboard): preserve first-run workspace setup
2026-07-20 16:16:04 -07:00
Peter SteinbergerandGitHub a52eb2134b feat(apps): connect multiple gateways simultaneously (#111932)
* feat(apps): support simultaneous gateways

* fix(apps): satisfy multi-gateway checks

* chore(apps): refresh multi-gateway baselines

* style(android): format gateway fleet code
2026-07-20 13:06:29 -07:00
Peter SteinbergerandGitHub ba3711bb82 feat(linux): render widgets in Quick Chat (#111933) 2026-07-20 11:56:03 -07:00
Peter SteinbergerandGitHub 4aaea7b072 fix(ui): persist synchronized sidebar preferences (#111929) 2026-07-20 11:11:45 -07:00
Peter SteinbergerandGitHub 8195cf8707 feat(dashboard): reveal widget card chrome on hover for fine-pointer devices (#111914)
* feat(dashboard): reveal widget card chrome on hover for fine-pointer devices

* test(dashboard): deterministic focus sink for widget chrome hide assertions

* test(dashboard): finish animations before asserting widget chrome hides
2026-07-20 11:05:13 -07:00
Peter SteinbergerandGitHub ea148e728d fix(ui): restore startup JS budget headroom for sandbox diagnostics (#111928) 2026-07-20 10:52:51 -07:00
Peter SteinbergerandGitHub bf6961d33e fix(ui): actionable diagnostics for missing widget sandbox origin behind proxies (#111909)
* fix(ui): actionable error and doctor warning for missing widget sandbox origin

* fix(ui): phrase sandbox-origin diagnostics as conditional guidance

* test(ui): satisfy deadcode and mock-factory gates for sandbox diagnostics
2026-07-20 10:35:47 -07:00
Peter SteinbergerandGitHub 271e6b7391 fix(ui): tolerate absent chat-event messages in text extractors (#111918) 2026-07-20 10:33:20 -07:00
Peter SteinbergerandGitHub febeb6ac12 fix(dashboard): review follow-ups — provider lifecycle, scope-gated controls, pin robustness (#111907)
* fix(canvas): validate dashboard pins before materializing

* fix(ui): bound dashboard provider lifecycles

* fix(gateway): harden board widget descendant frames

* test(ui): clean up dashboard provider tests

* fix(ui): harden dashboard availability lifecycle

* fix(ui): invalidate stale board availability

* perf(ui): keep dashboard bundle within budget
2026-07-20 10:27:52 -07:00
Peter SteinbergerandGitHub 59bc78a5b2 fix(gateway): auto-approve trusted-proxy same-key device scope upgrades (#111916) 2026-07-20 10:25:25 -07:00
Peter SteinbergerandGitHub c715d89de8 fix(ui): show newly created PRs in session sidebar (#111783)
* fix(ui): surface newly created session PRs

* test(ui): split sidebar PR state coverage
2026-07-20 10:04:41 -07:00
Peter SteinbergerandGitHub e0206b4967 fix(dashboard): make the board face scrollable (#111911) 2026-07-20 09:53:27 -07:00
Peter SteinbergerandGitHub 38df399ed8 fix(macos): show local ClickClack session discussions (#111883)
* fix(macos): show local ClickClack session discussions

* fix(macos): support loopback aliases in discussion embeds

* fix(macos): allow trusted hosted ClickClack embeds

* fix(macos): trust only dashboard-initiated discussion frames
2026-07-20 09:33:27 -07:00
Peter SteinbergerandGitHub 324fd4fe6e refactor(channels): delete all media-placeholder consumer awareness (#111891)
Final sweep of the media-placeholder program: with all eleven producers
migrated, the consumer regexes are dead input and are hard-deleted —
media-understanding token un-mixing (bodies are pure user text now),
the persisted-user-turn placeholder drop, the session-memory strip, and
the inbound-event mediaPlaceholder field with its replacement branch
(an intentional removal of shipped SDK surface per the program's
hard-delete decision: plugins still minting placeholder bodies render
them as literal text until updated). formatMediaPlaceholderText remains
the single owner of the <media:kind> vocabulary, used only for
text-only carriers. Legacy tokens in pre-migration history remain
ordinary text.
2026-07-20 09:31:51 -07:00
Peter SteinbergerandGitHub 5ca5c5c1e0 fix(ui): render sidebar view-archived icon as outline (#111867)
The .sidebar-view-archived svg rule set stroke: currentColor but never
fill: none, so the archive icon's lid <rect> rendered with SVG's default
black fill — a solid black bar instead of the outlined Lucide archive
box. Align the rule with sibling sidebar icon styles (fill: none,
1.7px stroke, round caps/joins).
2026-07-20 08:47:35 -07:00
Peter SteinbergerandGitHub 336b01a2e3 fix(dashboard): inset board widgets from pane edges (#111876) 2026-07-20 08:24:15 -07:00
Peter SteinbergerandGitHub 96ffdf15c7 docs(web): user guide for session dashboards (#111834)
* docs(web): user guide for session dashboards

* docs: format dashboards page
2026-07-20 08:23:14 -07:00
Peter SteinbergerandGitHub f6f484fb1c fix(ui): auto-show the discussion sidebar when a session has an open discussion (#111871)
* fix(ui): auto-show the discussion sidebar when a session has an open discussion

* docs(ui): document why probe resolution is the only auto-show hook
2026-07-20 08:22:04 -07:00
Peter SteinbergerandGitHub 5197428add feat(channels): batch 4 Telegram drops media placeholder bodies (#111855)
Final producer batch of the media-placeholder program: Telegram primary
bodies are caption-only with one aligned structured fact per native
media; the message-cache kind-parsing regex is deleted (native kind
stored directly); reply-chain, debounce/forward, group-history, and
ambient transcript lines render structured facts via the shared
formatter (removing the plugin-local duplicate of the caption-less
literal); audio-transcript and sticker-description replacements gate on
structured facts instead of exact placeholder strings. Also restores
unconditional failed-retryable recording when media resolution is
aborted for live updates, so shutdown cannot silently settle an
undispatched update.
2026-07-20 07:31:10 -07:00
Peter SteinbergerandGitHub 154a49a78b feat(dashboard): widget capabilities — net CSP, host tools, hardened shared sandbox (#111687)
* feat(boards): enforce widget capability grants

* feat(ui): expose dashboard widget capabilities

* test(dashboard): align rebased gateway contracts

* refactor(dashboard): finish capability alignment

* chore: internalize unused board sandbox exports

* test(dashboard): bind grants to widget instances

* fix(dashboard): close widget authority lifetime gaps

* refactor(ui): preserve modular widget frame ownership

* test(ui): preserve explicit MCP App lease refresh

* test(ui): use shared dashboard sandbox server

* fix(ui): settle widget requests on reconnect

* fix(ui): retry transient widget document loads

* refactor(dashboard): document WebRTC egress residual

* fix(dashboard): clear landing checks
2026-07-20 06:54:44 -07:00
Peter SteinbergerandGitHub 0ac7b24d56 feat(channels): batch 3 producers drop media placeholder bodies (#111800)
* feat(channels): batch 3 producers drop media placeholder bodies

Media-placeholder program batch 3: iMessage, Signal, and WhatsApp stop
minting <media:kind> placeholder bodies. Primary bodies carry captions
only with one aligned structured fact per native attachment (type-only
for missing/rejected/oversized). Echo, quote, and pending-history
records gain structured media facts so dedupe and previews work without
rendered text. Signal media-only outbound sends drop the placeholder
body entirely (signal-cli accepts attachment-only sends; recipients now
see just the media). WhatsApp audio preflight de-stringed; qa-lab
sticker recognizer moved to structured facts; WhatsApp gif maps to
video (presentation mode, no behavioral consumer).

* test(whatsapp): keep media classifier private
2026-07-20 04:22:44 -07:00
Peter SteinbergerandGitHub 8bec61bab1 fix(cli): reject unknown models in config set (#111571)
* fix(cli): validate configured model references

* fix(cli): cover dependent model references

* fix(cli): reject malformed model references

* test(cli): satisfy model validation gates

* fix(cli): harden model validation drafts

* fix(cli): preserve model runtime ownership

* fix(cli): validate expanded model references

* fix(cli): close model validation gaps

* fix(cli): preserve model alias ownership

* fix(cli): secure model validation context

* fix(cli): redact expanded model references

* fix(cli): validate inherited agent catalogs

* fix(cli): align canonical model resolution

* fix(cli): scope inherited catalog validation

* fix(cli): compare resolved model snapshots

* fix(cli): cover model dependency transitions

* fix(cli): isolate model env validation

* fix(cli): redact dependent model checks

* fix(cli): compare agent model identity

* fix(cli): validate expanded model removal

* fix(cli): defer unresolved fallback validation

* fix(cli): preserve authored model changes
2026-07-20 03:31:03 -07:00
Peter SteinbergerandGitHub 7fe1d70a50 fix(mcp): prevent custom server config races and parsing errors (#111761)
* fix(mcp): harden custom server management

* fix(ui): keep config patch types private
2026-07-20 02:34:13 -07:00
Peter SteinbergerandGitHub a1f43b218a feat(ui): offer page refresh on protocol-mismatch failure (#111772) 2026-07-20 02:31:40 -07:00
Peter SteinbergerandGitHub 7defd4a7b4 refactor(android): split offline client databases (#111710)
* refactor(android): split offline client databases

* chore(android): refresh native i18n inventory

* test(gateway): close databases before transcript cleanup
2026-07-20 02:28:02 -07:00
Peter SteinbergerandGitHub 473f2aca33 fix(gateway): honor session title model routing (#111757)
Route automatic dashboard titles through the effective session model and auth profile while preserving explicit cross-provider utility ownership and the existing generic title fallback.
2026-07-20 02:19:57 -07:00
Peter SteinbergerandGitHub 9056c43368 docs(gateway): add client-building and embedding guides (#111726)
* docs(gateway): add client-building and embedding guides

* docs(gateway): harden package rollout guidance
2026-07-20 02:12:52 -07:00
Peter SteinbergerandGitHub fd081d6521 fix(ui): keep Codex commentary visible after runs (#111648)
* fix(ui): keep Codex commentary visible

* fix(codex): preserve commentary transcript ordering

* fix(ui): preserve commentary retention opt-out
2026-07-20 02:10:56 -07:00
Peter SteinbergerandGitHub 922240568a fix(doctor): check every configured agent (#111758) 2026-07-20 02:05:53 -07:00
Peter SteinbergerandGitHub 5d80b62d21 fix(ui): nudge stale tabs after gateway updates (#111619)
* fix(ui): nudge stale tabs after gateway updates

* fix(ui): resolve external packages in worktrees
2026-07-20 01:51:22 -07:00
Peter SteinbergerandGitHub 6f2fa019bd fix: honor configured default agent in consults (#111749) 2026-07-20 01:43:00 -07:00
Peter SteinbergerandGitHub 0e62293104 fix(ui): keep sidebar session titles aligned (#111698)
* fix(ui): stabilize sidebar session indicators

* test(ui): update sidebar leading-slot assertion
2026-07-20 01:27:25 -07:00
Peter SteinbergerandGitHub 343cfba638 fix(clickclack): private API base for gated hostnames and single discussion-provider registration (#111724)
* fix(clickclack): split API base and refresh discussion provider

* chore: regenerate bundled channel config metadata and docs map for the apiBaseUrl key
2026-07-20 01:27:13 -07:00
Peter SteinbergerandGitHub 629de998f9 feat(system-agent): agentic cached caretaker greeting with quick actions (#111615)
* feat(system-agent): agentic cached caretaker greeting with quick actions

* test(gateway): pin caretaker greeting in session-ownership tests

* fix(system-agent): offer agent handoff chip only when a model is available

* fix(ci): typecheck narrowing, lint, and knip gates for greeting module

* fix(system-agent): reject structured greeting output on every retained line

* test(gateway): split caretaker welcome tests under the max-lines gate
2026-07-20 01:21:09 -07:00
Peter SteinbergerandGitHub 6eee1dccfc fix(cloud-workers): reclaim sessions after workspace conflicts (#111714)
* fix(cloud-workers): make reclaim failures retryable

* chore: leave changelog to release flow

* chore: fix cloud reclaim lint

* fix(cloud-workers): preserve committed reclaim recovery
2026-07-20 01:16:07 -07:00
Peter SteinbergerandGitHub 83e78d8681 fix(ui): quiet the reconnect-wait queue row and hide its raw transport error (#111703) 2026-07-20 01:11:27 -07:00
Peter SteinbergerandGitHub 248726fafd feat(anthropic): import Claude Desktop custom groups (#111644)
* feat(anthropic): import Claude Desktop custom groups

* fix(anthropic): resolve Claude Desktop groups from LevelDB entries

Group discovery regexed whole decompressed blocks, so matches were not
attributable to a Local Storage value and byte order decided the winner.
On a real 5.4MB store that mislabelled 59 of 159 sessions, surfacing a
mojibake label spliced out of Snappy copy-record bytes.

Parse SSTable entries properly instead: prefix-delta keys bounded by the
restart array, newest internal sequence per user key (honoring deletions),
and record scanning confined to a single value. Values are normalized so
Chromium's UTF-16 form scans like Latin-1, and unflushed WAL writes keep
precedence over SSTables.

Prod LOC grows ~77; it buys structural correctness in place of ordering
luck, and folds the old index-only walk into one shared entry decoder.

Verified against the live store: 159 assignments, 0 mislabelled versus an
independent entry-level ground truth, 0 control-character labels.

* fix(ui): sort custom session groups ahead of project groups

Custom groups were pushed into the section list as encountered, so their
position depended on roster order rather than the documented behavior;
the existing test only passed because its fixture happened to be ordered
that way. Collect custom and project groups separately and concatenate,
and assert the guarantee with a reversed-input case.

* fix(anthropic): widen LevelDB fixture key type for test typecheck

The prefix-delta helper assigned a Buffer into a Buffer-typed accumulator
whose generic argument differed, which tsgo rejects in the test lane.

* chore(anthropic): drop release-owned changelog edit from the PR

CHANGELOG.md is generated at release time, so a normal PR must not carry
an entry. The release-note context lives in the feature commit message and
the PR body instead.
2026-07-20 00:30:37 -07:00
Peter SteinbergerandGitHub a971188a6d build(client): make @openclaw/gateway-client publishable to npm (#111707) 2026-07-20 00:23:36 -07:00
Peter SteinbergerandGitHub 3e14caa2bd fix(ui): give markdown list items more breathing room (#111693) 2026-07-20 00:15:04 -07:00
Peter SteinbergerandGitHub f5940ee92d fix(ui): make stale-chunk Reload survive the gateway restart (#111670)
* fix(ui): make stale-chunk Reload survive the gateway restart

The 'A new version is available' banner appears precisely because the
gateway was just updated — so when the user clicks Reload, that gateway
is usually still restarting. The handler probed once and, on failure,
silently fell back to revalidating the same replaced chunk, so the
button looked dead and a manual hard reload was the only way out.

Poll the document probe until the gateway answers (bounded, so an
unreachable gateway still degrades to the recoverable panel error
rather than a fatal navigation), and show a disabled 'Reloading…' state
while waiting.

* fix(ui): enforce the reload wait deadline locally

The bound was only checked after each probe settled, so it relied on the
probe timing itself out. The default probe does self-abort, but a
caller-supplied one need not, and a probe that never settles would leave
the Reload button disabled forever. Race each probe against the
remaining deadline instead.

* fix(ui): satisfy lint and deadcode gates for the reload retry

CI caught two things local oxlint does not: the wait default returned
the timer id from a Promise executor (eslint no-promise-executor-return),
and retryStaleChunkReload became an unused export once the lazy-route
button moved to the polling variant.

Wrap the executor body, and collapse the two near-identical retry paths
into one: the polling helper with timeoutMs: 0 is exactly the old
single-shot behavior, which the stylesheet-recovery banner keeps.

* fix(ui): keep the reload wait bounded across interval waits

The deadline was checked before the interval wait, so a wait that
carried past it left the next iteration taking the unbounded
single-shot branch — a probe that never settles would then strand the
disabled Reload button past its own bound. Only the first attempt may
probe from outside the window.
2026-07-20 00:11:01 -07:00
Peter SteinbergerandGitHub 873c2a0c32 fix(ui): keep completed work expanded outside dashboard sessions (#111640) 2026-07-20 00:08:53 -07:00
Peter SteinbergerandGitHub 46ceb21b89 fix(control-ui): keep OpenClaw in settings (#111686) 2026-07-20 00:00:23 -07:00
Peter SteinbergerandGitHub 8a5b4af660 fix(gateway): make agent deletion transactional (#111678)
* fix: make agent deletion transactional

* fix: align deletion proof with config IO

* test: align deletion mocks with strict types

* test: remove stale cron helper

* chore: drop changelog entry and unshipped-schema compat branches

* fix: preserve reclaimed agent state

* fix: make agent deletion recovery durable

* fix: make agent cleanup symlink-safe

* fix: retain failed agent database ownership

* fix: harden agent deletion path ownership

* fix: persist agent cleanup completion

* docs: name the local trust-boundary bound on deletion fencing

* fix: satisfy agent deletion CI guards
2026-07-19 23:58:02 -07:00
Peter SteinbergerandGitHub 34f942e89d fix(agents): tolerate auth mutation before first prepared-model-runtime owner (#111627) (#111701) 2026-07-19 23:53:53 -07:00
Peter SteinbergerandGitHub e2e394e1ab fix(ui): give roster rows their avatar-text gap and add presence mock fixtures (#111689)
* fix(ui): add avatar-text gap in roster menu and presence mock fixtures

* refactor(ui): keep presence snapshot helper out of the connect auth hunk
2026-07-19 23:38:40 -07:00
Peter SteinbergerandGitHub 6b80b01369 chore(pr): warn when ci-dispatch targets a remote head behind local work (#111694) 2026-07-19 23:24:29 -07:00
Peter SteinbergerandGitHub fef846a3f0 build(protocol): make @openclaw/gateway-protocol publishable to npm (#111664)
* build(protocol): make @openclaw/gateway-protocol publishable to npm

* chore(mxc): align package version with release train

* fix(release): support gateway-only core packages
2026-07-19 23:18:24 -07:00
Peter SteinbergerandGitHub 401ef9770e fix(ui): restyle chat pane error banner as a compact centered alert (#111681) 2026-07-19 23:12:03 -07:00
Peter SteinbergerandGitHub e67fda8ec8 fix(clickclack): accept omitted nullable managed-channel fields and auto-open the discussion panel (#111685)
* fix(clickclack): accept omitted nullable managed-channel fields and auto-open the discussion panel

* fix(ui): open the discussion when write access arrives after it resolved
2026-07-19 23:10:29 -07:00
Peter SteinbergerandGitHub f67e55ac00 refactor: unify the embedded-agent fallback run-entry across all four pipelines (#111608)
* refactor(agents): unify fallback run-entry across channel, command, memory, and follow-up paths

* refactor(agents): keep run-entry contracts module-local

* test(reply): derive run-entry params type locally
2026-07-19 23:08:55 -07:00
Peter SteinbergerandGitHub 250e636ffd fix(onboard): reject invalid setup options before writes (#111680)
* fix(onboard): validate setup options before mutation

* fix(onboard): keep flow helper type local
2026-07-19 23:05:23 -07:00
Peter SteinbergerandGitHub 104128dd32 chore(pr): dev-wrapper opt-in — advisory subcommands may dogfood modified wrappers (#111656)
* chore(pr): dev-wrapper opt-in — advisory subcommands may dogfood modified wrappers

* fix(pr): hermetic dev-wrapper test fixture and type narrowing
2026-07-19 22:58:15 -07:00
Peter SteinbergerandGitHub 6e19c36d41 feat(channels): batch 2 producers drop media placeholder bodies (#111665)
* feat(channels): batch 2 producers drop media placeholder bodies

Media-placeholder program batch 2: Discord, Feishu, and MSTeams stop
minting <media:kind> placeholder bodies. Primary bodies carry captions
only; native resources produce ordered structured facts with type-only
facts for failures/rejections (batch-1 alignment contract). Discord
stickers travel as native kind "sticker" (not MIME-derivable); text
carriers (forwarded snapshots, room history, thread starters, pending
text) render via formatMediaPlaceholderText. Feishu audio preflight and
MSTeams unavailable selection move from body-string equality to
structured facts; dead placeholder payload builders deleted.

* test(discord): make allowlist policy explicit
2026-07-19 22:48:32 -07:00
Peter SteinbergerandGitHub 71513436f9 fix(ui): hide system agent from agent roster (#111677) 2026-07-19 22:46:02 -07:00
Peter SteinbergerandGitHub a22ede72e4 fix(ui): redesign the who's-online roster menu (#111668) 2026-07-19 22:24:53 -07:00
Peter SteinbergerandGitHub bdf71d7d98 refactor(apple): unify offline client databases (#111598)
* refactor(apple): unify offline client databases

* refactor(apple): remove dead cache APIs

* fix(apple): satisfy native storage gates

* fix(apple): sync generated localization catalog

* Revert "fix(apple): sync generated localization catalog"

This reverts commit d0f5e7b74932e98ccc5f1984840e8f9d545edc2d.

* fix(i18n): keep source validation pre-merge

* test(macos): isolate flaky suite state

* test(macos): isolate log locator on main actor
2026-07-19 22:14:12 -07:00
Peter SteinbergerandGitHub 69ee7127c0 fix(ui): prevent chat jump when the working claw appears (#111642)
* fix(ui): smooth working claw transition

* test(ui): satisfy chat transition checks
2026-07-19 22:13:42 -07:00
Peter SteinbergerandGitHub 8f9e42f2a9 fix(cli): report honest SQLite store labels and consistent build SHA in help (#111659) 2026-07-19 22:07:44 -07:00
Peter SteinbergerandGitHub 1ee42427ea fix(cli): surface gateway→embedded fallback session divergence in --json output (#111645) 2026-07-19 21:26:58 -07:00
Peter SteinbergerandGitHub d18a7cff8e refactor(ui): extract the steered-message lifecycle into a typed state machine (#111610)
* refactor(ui): extract the steered-message lifecycle into a typed state machine

The steer flow was an implicit state machine spread across five chat modules,
with chip states encoded in loosely coupled optional fields and duplicated
guard predicates. This extracts it behind structural types:

- steered-chip.ts: leaf chip model — SteeredChip union (in-flight requires
  sendState "steering"; acknowledged requires pendingRunId and forbids
  sendState), constructors, and narrowing guards; impossible states are
  unrepresentable and every call site uses the guards
- steer-lifecycle.ts: owns the steer send flow, single terminal retirement
  pipeline (remember -> materialize acked chips -> clear), and history-proven
  retirement; absorbs queued-user-turn.ts
- terminal handling in chat-gateway/chat-state and the stored-outbox history
  verification in chat-send consolidate onto the shared helpers; chat-send.ts
  shrinks by ~300 lines; net prod LOC is flat
- behavior fixes riding the pipeline: acknowledged chips retire once
  authoritative history contains their user turn (no more brief chip/history
  double display) and lingering chips keyed to a filtered run id now retire on
  the next history load
- isTerminalFailureChatSendAck moves to chat-send-contract.ts so ack
  narrowing stays type-safe at every formatter call site

* fix(ui): satisfy type, lint, and dead-export gates for the steer-lifecycle split

- restore the snapshotChatAttachments helper (oxc no-map-spread fires on the
  inlined form)
- select the stored-outbox drain head with an explicit loop; the find-predicate
  form made TS narrow away the failed state and flagged the guard (TS2367)
- drop the unused chip re-exports from steer-lifecycle and the consumer-less
  isSteeredChip guard; chip types stay module-local, guard consumers import
  the steered-chip leaf directly

* style(ui): oxfmt line joins in chat-send and chat-state
2026-07-19 21:14:56 -07:00
Peter SteinbergerandGitHub bc76bb8933 feat(agents): agent-controlled session status, attention, and TTL in the sessions tool (#111583)
* feat(agents): agent-controlled session status, attention, and TTL in the sessions tool

* feat(protocol): sessions.patch agent status fields and curated attention icon ids

* chore(protocol): regenerate bindings for agent-status patch fields, unexport internal cap

* chore(sessions): unexport internal agent-status TTL default
2026-07-19 21:01:09 -07:00
Peter SteinbergerandGitHub 792f5b7b74 fix(cloud-workers): reconcile workspace results across turns (#111481)
* fix(cloud-workers): preserve accepted workspace manifests

* fix(cloud-workers): keep manifest helpers private

* fix(cloud-workers): harden accepted workspace publication

* fix(cloud-workers): preserve ancestor modes during publication

* fix(cloud-workers): pass manifest path filter to verifier

* refactor(cloud-workers): extract manifest stability check
2026-07-19 20:36:50 -07:00
Peter SteinbergerandGitHub 200653bd60 fix(gateway): generate dashboard titles despite sender metadata (#111613)
* fix(gateway): keep sender identity out of session titles

* docs(changelog): note dashboard session title fix

* test(gateway): type dashboard sender fixture

* docs: keep session title release note in PR
2026-07-19 20:26:28 -07:00
Peter SteinbergerandGitHub 6d39d3cf0b fix(onboard): preserve gateway settings on rerun (#111569)
* fix(onboard): preserve gateway config on rerun

* fix(onboard): scope remote secrets to endpoint

* fix(onboard): honor rerun override boundaries

* fix(onboard): secure inherited tailscale auth

* fix(onboard): honor explicit token auth

* fix(onboard): enforce funnel auth on rerun

* fix(onboard): preserve env password on rerun
2026-07-19 20:18:34 -07:00
Peter SteinbergerandGitHub 7d4dccf959 improve(swarm): close out wait, phase, and worker follow-ups (#111605)
* fix(codex): support full swarm wait budgets

* feat(ui): bucket swarm dots by phase

* docs(swarm): document flat worker patterns

* fix(codex): configured per-tool timeout still wins over swarm wait default

* fix(codex): outer swarm-wait watchdog outlives the 600s budget (cap + grace)

* fix(ui): order swarm phase buckets by observation rank

* fix(ui): repair rank-order widget test — proper lit render container, untangle sibling assertions

* fix(ui): implicit swarm phase assignment only on child creation events

* test(ui): split swarm activity integration coverage
2026-07-19 20:11:54 -07:00
Peter SteinbergerandGitHub 3564c1c795 fix(test): bound Matrix test process memory (#111607)
* test: bound Matrix test process memory

* test: align Matrix runner mock contract
2026-07-19 20:04:14 -07:00
Peter SteinbergerandGitHub eb3a600571 fix(ui): preserve native context menu for chat links (#111618) 2026-07-19 19:58:37 -07:00
Peter SteinbergerandGitHub da10827439 feat(ui): accept drag-and-drop attachments in the new-session composer (#111530)
* feat(ui): accept drag-and-drop attachments in new-session composer

The new-thread composer only accepted attachments via paste and the +
menu; dragging a file onto it was silently ignored. Wire the shared
chat attachment drop handling onto the composer shell with the same
balanced drag affordance as the chat pane, and gate both composers'
drop/dragover cancellation on file drags so text/URL drops keep the
textarea's native behavior.

* fix(ui): cancel non-file drops outside editable composer targets

A URL dropped on the transcript, header, or composer chrome would hit
the browser default and navigate the app away, discarding drafts. Keep
native text/URL drops only when the drop target is an editable control;
cancel them everywhere else in both composers.

* fix(ui): treat disabled and readonly inputs as non-editable drop targets

A URL dropped precisely on a disabled composer textarea would still hit
the browser default and could navigate away. Check actual editability
(disabled/readOnly/isContentEditable) instead of selector shape.

* fix(ui): limit native drops to text-entry inputs

Enabled non-text inputs (checkbox, range) counted as editable drop
targets, so a URL dropped on one skipped cancellation and could
navigate the browser away. Restrict the native-drop exception to
text-capable input types, textareas, and contenteditable content.
2026-07-19 19:55:29 -07:00
Peter SteinbergerandGitHub 64c82812a5 feat(dashboard): pinned MCP apps — board rendering, lease re-mint, durable tool grants (#111524)
* feat(boards): mint pinned MCP app views

* feat(ui): render pinned MCP apps on dashboards

* fix(dashboard): reconcile MCP app rebase

* fix(dashboard): align generated CI contracts

* test(gateway): record board.widget.appView release train

* fix(dashboard): revalidate pinned MCP app views
2026-07-19 19:51:26 -07:00
Peter SteinbergerandGitHub ddf57c083f fix(onboard): keep invalid wizard retries unpersisted (#111558)
* fix(onboard): validate wizard retries before persist

* fix(onboard): stage retry auth profiles until verified

* test(onboard): clarify staged credential fixtures

* refactor(onboard): preserve immediate provider auth path

* fix(onboard): retain staged auth across retries

* fix(onboard): isolate rejected auth repairs

* fix(onboard): retain verified credential refreshes

* test(onboard): mark credential fixtures synthetic

* test(onboard): use canonical secret placeholders

* refactor(onboard): destructure staged profile stores

* fix(onboard): retain rotated staged credentials

* test(onboard): use valid timeout status

* refactor(onboard): keep staged auth helper private
2026-07-20 02:47:21 +00:00
Peter SteinbergerandGitHub b07985ef45 feat(ui): show waiting-for-approval run status in chat (#111445)
* feat(ui): show waiting approval run status

* fix(ui): hydrate waiting approval status

* fix(ui): correlate approval status by run

* fix(ui): harden approval status reconciliation
2026-07-19 19:44:25 -07:00
Peter SteinbergerandGitHub 3c0f55b58f fix(gateway): prevent cross-client system-agent session takeover (#111565)
* fix(gateway): bind system-agent sessions to callers

* test(gateway): type system-agent response helper
2026-07-19 16:05:18 -07:00
Peter SteinbergerandGitHub c5f9addb52 fix(ui): keep custodian transcript aligned after inference errors (#111562)
* fix(ui): reconcile invalidated custodian sessions

* test(ui): preserve custodian page harness type
2026-07-19 16:00:08 -07:00
Peter SteinbergerandGitHub e0b1a39d2a improve(i18n): generate native locales after merge (#111557)
* ci(i18n): move native locale generation post-merge

* fix(i18n): allow generated Android companions
2026-07-19 15:42:41 -07:00
Peter SteinbergerandGitHub 3247a56d15 fix(ui): prevent cross-file mock leakage in Control UI tests (#111554)
* test(ui): stop shared mock registry leakage

* ci: add three-worker UI leakage canary
2026-07-19 15:38:50 -07:00
Peter SteinbergerandGitHub 7116ad6e28 fix(agents): preserve fresh workspace hatch (#111553) 2026-07-19 15:35:54 -07:00
Peter SteinbergerandGitHub 473962b7de fix(onboarding): scope recommendations by workspace (#111560) 2026-07-19 15:34:56 -07:00
Peter SteinbergerandGitHub 5fcf7e75de test: clear plugin runtimes between shared files (#111556) 2026-07-19 15:28:46 -07:00
Peter SteinbergerandGitHub a1e5b6ef0e fix(doctor): honor active migration targets (#111555) 2026-07-19 15:27:03 -07:00
Peter SteinbergerandGitHub 6c25f5ae33 fix(ui): keep steered composer messages visible until the transcript owns them (#111540)
* fix(ui): keep steered composer messages visible until the transcript owns them

Typing while the agent was busy showed a brief steering state and then the
message vanished until the next full history reload. The steer path removed
the queued row at ack time and only restored the steered chip when the tab
had adopted the active run id, which never happens for runs started by
automations, other clients, or tabs opened mid-run.

- steer sends reuse the durable row's sendRunId as the wire idempotencyKey so
  delivered-turn and history-proof reconciliation can correlate steered rows
- the steered chip is restored after every accepted ack, keyed to the active
  run when the tab still tracks it, else to the steer's own gateway lifecycle
- terminal events materialize acknowledged steered chips into the transcript
  before clearing them, and stale history reloads keep idempotency-marked
  local turns until authoritative history catches up
- in-flight (unacknowledged) steers are never materialized, so a rejected
  send cannot leave a phantom user turn behind

Fix authored with Codex; reviewed and hardened via structured review.

* fix(ui): use bracket access for __openclaw test marker (no-underscore-dangle)

* fix(ui): resolve steered attachment payloads through the store before materializing

Queue rows carry attachment metadata only; composer upload bytes live in the
payload store. Resolving through getChatAttachmentDataUrl keeps attachment-only
steers visible when their chip retires (Codex connector review finding).

* fix(ui): only user-role entries satisfy queued-turn presence checks

An assistant entry can carry the same run's idempotency key; matching it let
the chip re-add and terminal materialization skip the user's copy
(ClawSweeper rank-up move).
2026-07-19 15:17:08 -07:00
Peter SteinbergerandGitHub bcbbf28093 docs(plugin-sdk): migrate retained SDK contracts (#111535)
* refactor(plugin-sdk): finish retained public demotions

* test(plugin-sdk): tighten surface budgets

* fix(plugin-sdk): retain static tool metadata contract

* fix(plugin-sdk): keep retained exports pending

* fix(plugin-sdk): satisfy registry lint

* docs(plugin-sdk): document retained consumer seams
2026-07-19 15:00:45 -07:00
Peter SteinbergerandGitHub c6f7ddff1a fix(ui): show attributed sender identity properly in chat (#111537)
* fix(ui): show attributed sender identity properly in chat

Three defects around multi-user sender attribution:

- Sender labels rendered as 'name (<profile uuid>)': resolveSenderLabel
  appended the id part even when it is an opaque UUID. Suppress UUID
  suffixes server-side and strip already-baked ones in the UI normalizer
  so historical transcripts read clean too.
- Attributed messages showed initials instead of the author's avatar:
  chat sender metadata carries only the profile id, so resolveAvatar now
  derives the canonical /api/users/<id>/avatar route (upload -> gateway
  Gravatar proxy -> initials) from UUID-shaped sender ids.
- Author identity was hover-only: identity-resolving gateways keep the
  always-visible avatar gutter even in 1:1-shaped sessions, and the
  gutter renders the message author's avatar (with identity-colored
  initials fallback) instead of the local viewer's.

* fix(ui): preserve legacy author identity and 404-safe sender avatars

Review findings: stripping the baked UUID suffix discarded the only
author key on legacy rows (the gutter then showed the local viewer's
face under another person's label) — the UUID now survives as
structured non-display sender identity. And the derived avatar route
can 404 (no upload, no Gravatar), so the gutter image swaps to
identity-colored initials on error instead of a broken image.

* fix(ui): attribute bare-UUID legacy sender labels to their profile

A nameless legacy sender labels as the bare profile UUID; recognize that
whole-label shape as the sender id (keeping it as last-resort display)
so the row resolves the author's avatar instead of misattributing to the
local viewer.
2026-07-19 14:59:33 -07:00
Peter SteinbergerandGitHub 0fff74c800 feat(ios): replace the tab bar with a black overlay sidebar (web parity + dashboard) (#111339)
* feat(ios): unify navigation on a black overlay sidebar and drop the phone tab bar

* test(ios): rework navigation tests and snapshot UITests for the sidebar shell

* chore(i18n): sync derived native catalogs for sidebar strings

* feat(ios): reveal the sidebar beneath a push-away rounded content card

* feat(ios): web-parity sidebar — agent switcher, pinned pages, sessions vocabulary, uniform content card

* chore(i18n): translate sidebar and session strings across native catalogs

* fix(ios): sidebar footer shows connection dot only when degraded, like the web

* feat(ios): sidebar toggle top-left with edge-swipe open; card surface owns the drawer chrome

* fix(ios): single sidebar refresh owner, alias-aware Home badges, RM tap-close, user pin order, drop vestigial idiom shims

* feat(ios): align sidebar with the mobile prototype — agent roster, theme-following palette, web session subtitles, row badges

* chore(i18n): translate agent roster and Recent strings across native catalogs

* feat(ios): frosted-glass agent picker with uniform drawer insets

* fix(ios): address sidebar review findings — settings toggle edge, per-agent scoping, alias-aware selection, cron paging, refresh cadence

* fix(ios): clear landing CI blockers
2026-07-19 14:50:39 -07:00
Peter SteinbergerandGitHub 5420c5c409 feat(clickclack): session discussions — bound channels, side agent, lifecycle sync (#111503)
* feat(clickclack): add session discussions

* chore: remove release-owned changelog entry

* refactor(clickclack): split discussion service workflows

* fix(clickclack): satisfy plugin surface gates

* fix: narrow routing peer in inbound test and refresh docs map
2026-07-19 14:47:14 -07:00
Peter SteinbergerandGitHub c45e3dd315 fix(gateway): avoid macOS startup freeze during worker reconciliation (#111533)
* fix(gateway): warm system CA before worker reconciliation

* fix(gateway): tolerate denied worker warmup
2026-07-19 14:39:25 -07:00
Peter SteinbergerandGitHub 6a408983d0 fix(onboard): make first-run hatch reliable (#111465)
* fix(onboard): harden first-run handoff

* style(ui): avoid onboarding comparator shadowing

* fix(agents): preserve mixed refresh failures
2026-07-19 14:38:58 -07:00
Peter SteinbergerandGitHub 77378cb0f3 fix(ui): keep stray file drops from leaving Control UI (#111531)
* fix(ui): guard unhandled file drops

* test(ui): exercise accepted file drop target

* test(ui): prove file drop guard in browser
2026-07-19 14:38:30 -07:00
Peter SteinbergerandGitHub 171a3852ba fix: install exact app recommendations and retry failures (#111518)
* fix(onboard): preserve recommendation install retries

* fix(onboard): preserve bootstrap recommendation retries

* fix(onboard): commit recommendation outcomes after config

* fix(onboard): guard recommendation state transitions

* fix(onboard): reconcile durable skill installs

* docs(clawhub): clarify search publisher contract
2026-07-19 14:10:50 -07:00
Peter SteinbergerandGitHub 13ed8b5aa1 perf(state): cap the per-agent SQLite handle cache with LRU eviction (#111411)
* perf(state): cap the per-agent SQLite handle cache with LRU eviction

Multi-tenant hosts open one WAL database per agent (~3 file descriptors
each); the process-local handle cache was unbounded, so large fleets
exhausted descriptors. Cache hits now refresh LRU recency, and cache-miss
opens evict the oldest non-transactional handle before constructing the
new one, capped at 64 open handles. Eviction closes the process-local
handle only; registry rows and durable data are untouched and evicted
databases reopen transparently on next access.

* fix(state): fleet-wide introspection reads stop opening writable agent databases

Live testing an 81-agent gateway surfaced three full-fleet sweeps that
opened every agent's SQLite database writable (schema ensure + registry
write transaction per open): the 60s health snapshot's per-agent session
summaries, usage-cost cache reads behind the gateway usage endpoints, and
the zalouser doctor detector that ran from every CLI startup. Each sweep
churned the new bounded handle cache and saturated the event loop.

Session listing gains a readonly non-registering variant (SDK exposes it
as an additive readOnly flag on listSessionEntries); health and usage
reads use it, with transient-lock tolerance owned by the health caller.
The zalouser detector gates on the channel/credentials actually existing,
collects once per pass, and detection reads go readonly while migration
apply stays writable. The usage refresh queue's flat 50ms busy retry
becomes exponential backoff capped at 5s, and refreshing summaries retain
their cache timestamp so the 30s TTL gates fleet rescans.

* perf(state): make per-agent handle eviction and reopen cheap

Eviction only works if reopen is cheap. Reopens after eviction previously
repeated full first-open work; ensureOpenClawAgentSchema takes BEGIN
IMMEDIATE and the registry upsert writes the shared state DB, so reopens
blocked in synchronous busy waits (observed 14-22s event-loop stalls with
reconcile workers holding write transactions). Owner/schema validation and
registration now run once per path per process with invalidation on
quarantine and disposal; the read-only integrity guard still runs on every
physical open. Eviction closes with a PASSIVE WAL checkpoint instead of
TRUNCATE, which waits on readers; orderly dispose keeps TRUNCATE so
sidecars are flushed before unlink.

* ci: regenerate plugin-SDK API baseline, drop prod test-seam exports, mock readonly accessor

The additive readOnly flag on the SDK listSessionEntries changed the
declaration surface, so the generated API baseline is regenerated via the
sanctioned script. Knip forbids production exports consumed only by tests:
the usage-cache testApi export is removed outright and the refresh-queue
seam moves behind an env-gated global exposed by a test-support module.
health.plugins.test's full session-accessor mock gains the newly imported
readonly listing.
2026-07-19 13:58:01 -07:00
Peter SteinbergerandGitHub f6a3f22629 refactor(channels): consolidate message action read gate (#111529) 2026-07-19 13:57:39 -07:00
Peter SteinbergerandGitHub 1a574923cb fix(onboard): honor remote flags in interactive setup (#111517)
* fix(onboard): honor interactive remote flags

* fix(onboard): clear credentials for changed gateway

* fix(onboard): validate remote URL before probe

* test(onboard): mark synthetic remote tokens

* test(onboard): avoid literal credential fixtures

* refactor(onboard): name remote probe auth role

* refactor(onboard): keep remote probe seed canonical

* test(onboard): type remote URL validator mock
2026-07-19 13:25:46 -07:00