Commit Graph

70 Commits

Author SHA1 Message Date
Peter Steinberger ea06d72e85 feat(secrets): manage team secrets in Control UI (#121724)
* feat(secrets): add gateway store settings

* perf(control-ui): trim secrets startup copy

* perf(control-ui): reduce secrets startup payload

* fix(secrets): harden store mutation refresh

* perf(control-ui): meet secrets startup budget

* test(control-ui): update secrets navigation copy

* fix(ui): pluralize secret-detection count and drop duplicated dialog hint

* chore(protocol): regenerate gateway clients and SDK baseline after rebase

* fix(gateway): merge secrets store methods after project RPCs in advertised order

* chore: leave changelog to release generation

* test(gateway): retain desktop launch train coverage
2026-08-11 07:20:38 +00:00
Peter Steinberger 8b0735e89f refactor(memory)!: remove the QMD backend; builtin is the only memory engine (#120936)
* refactor(memory): remove qmd backend

Make builtin the sole memory-core engine, rename the retained session helper barrel, retire QMD config with doctor migrations, and remove QMD runtime/UI/policy surfaces.

* docs(memory): remove qmd backend guidance

Delete the QMD concept page, rewrite memory documentation for builtin retrieval, and remove QMD from navigation and taxonomy source.

* refactor(memory): remove qmd-only leftovers

* refactor(memory): finish qmd integration cleanup

* build(deps): align root string-width types

* build(deps): model root string-width tooling

* refactor(memory): align qmd removal ui and docs

* fix(memory): preserve qmd external paths in doctor

* test(memory): remove obsolete backend probe case

* test(plugin-sdk): refresh private type baseline
2026-08-09 03:05:47 -07:00
Peter Steinberger e40e352fe7 refactor(session-url): centralize normalization and contract tests (#120945)
* refactor(session-url): centralize contract normalization

* fix(ui): resolve normalization agent-id alias

* test(session-url): make grammar tables type-safe
2026-08-09 02:56:07 -07:00
Peter Steinberger f15ce3941f refactor(ui): devices is the user-facing word for paired hardware (#120689)
Implements docs/plan/runners.md milestone 1b.
2026-08-08 13:47:17 -07:00
Peter Steinberger 1889764369 revert(ui): session is the product noun — undo thread copy rename (#120667)
Implements docs/plan/runners.md milestone 1a.
2026-08-08 12:43:05 -07:00
Peter Steinberger e3de98a451 feat(update): scheduled update campaigns with countdown, dev-channel auto-update, and Updates settings page (#120506)
* feat(update): scheduled update campaigns with countdown, dev-channel auto-update, and Updates settings page

* fix(ui): satisfy update campaign deadcode checks

* feat(update): extend scheduled update campaigns

* test(ui): include Updates page in settings sidebar search expectations

* fix(update): end campaigns when apply fails

* fix(update): pin dev campaign applies to the announced commit

* test(ui): align update e2e fixtures

* fix(update): pin adopted dev updates to the announced commit

* docs(update): document automatic dev update campaigns

* fix(ui): gate automatic dev updates on git installs

* fix(gateway): scope detailed update metadata to read-capable clients

* fix(gateway): break post-attach madge cycle with a leaf client-lookup type

* chore(protocol): regenerate clients after rebase

* test(gateway): update method tail after rebase

* fix(update): preserve campaign target and hold state

* test(update): align adoption mocks and split handoff suite

* test(update): route split handoff suites on Windows
2026-08-08 11:19:53 -07:00
Omar Shahine 5f4471c8d3 feat(ui): finish cron -> Automations rename in visible strings (#114853)
* feat(ui): finish cron -> Automations rename in visible strings

* fix(ui): strip persisted pre-rename Cron labels before applying the Automation prefix

Codex review finding: gateway rows persist labels as 'Cron: ...' (and keep
writing them until the strings PR lands), so the renamed typed prefix
double-rendered as 'Automation: Cron: ...'. Legacy label prefixes are now
stripped before prefixing; regression tests cover both stored shapes.

* fix(ui): catalog typed-session prefixes and refresh locale catalogs for the rename

Review follow-ups: typed-session prefixes come from the i18n catalog and
display branching uses machine identity (session kind), not translated text;
remaining English feature labels (config group, settings nav, dreaming intro)
renamed; all 20 locale catalogs refreshed through the owner sync (translated,
strict ui:i18n:check green) with a non-English regression protecting the
renamed keys.

* chore(ui): reconcile locale catalogs after rebase onto main locale refresh

* fix(ui): keep locale bundles workflow-owned; pin the rename at the English source

CI isolates generated locale artifacts from source PRs (preflight changed-scope
gate); the post-merge control-ui-locale-refresh workflow owns retranslation.
This PR ships the English source, catalog prefix keys, and machine-identity
display branching; the regression pins the renamed keys at the source of truth.

* fix(ui): move rename catalog test out of workflow-owned locales dir

---------

Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
2026-07-30 07:18:19 -07:00
Peter Steinberger af70829cdf refactor(ui): move Agent Defaults entry from the sidebar into the Agents page (#115836)
* refactor(ui): move Agent Defaults entry from the sidebar into the Agents page

* fix(ui): keep Agent Defaults searchable
2026-07-29 06:30:40 -04:00
Peter Steinberger f318df7a55 refactor(ui): dissolve the General settings page into Appearance (#115798)
* refactor(ui): dissolve the General settings page into Appearance

* test(ui): drop dissolved config route expectation

* test(ui): cover legacy General model redirect

* style(ui): format legacy redirect test
2026-07-29 05:49:20 -04:00
Peter Steinberger 7a1aa4eb4f feat(ui): Talk settings page with catalog-driven realtime pickers (#115409)
* feat(talk): emit realtime models and voices in talk.catalog and mirror create-time readiness

* feat(ui): add curated Talk settings page with catalog-driven pickers

* docs(talk): correct stale claims and add one-page GPT-Live setup path

* fix(ui): refresh Talk catalog on config-hash advance and neutralize GPT-Live badge

* fix(ui): provider-aware Talk selection, atomic provider switch, focus refresh

* fix(ui): resolve Talk provider fallbacks via catalog and make Default a true reset

* fix(ui): provider-compatible transports and race-free Talk catalog loads

* fix(ui): never resolve an unknown explicit Talk provider to the active one

* docs(talk): note Android relay readiness caveat for browser-only models

* fix(ui): keep the relay transport when switching Talk provider to Auto

* fix(ui): align section-ownership test and drop unused export after rebase
2026-07-28 17:38:15 -04:00
Peter Steinberger 13f20055f0 refactor(ui): split Profile (identity) from Usage (statistics) (#115245)
* refactor(ui): profile owns identity; usage owns statistics

* fix(ui): rerender profile on connection transitions

* chore(ui): drop usage exports orphaned by the profile split

* chore(ui): localize usage TTL constant in gateway source replacement test
2026-07-28 12:09:50 -04:00
Peter Steinberger 58a3679c49 fix(ui): keep reconnects, browser tabs, and localized search consistent (#115221)
* fix(ui): fence stale gateway and browser lifecycles

* test(ui): restore history after agent tool navigation
2026-07-28 11:24:02 -04:00
Peter Steinberger 455e728533 refactor(ui): consolidate model configuration onto the Models page (#115108)
* refactor(ui): consolidate model defaults onto the Models page

* docs(ui): note busy-gate equivalence on the Models page controls

* fix(ui): render model behavior controls while providers load

* fix(ui): redirect the legacy General model scroll target to Models

* fix(ui): surface config autosave and apply workflow on the Models page
2026-07-28 09:31:09 -04:00
Peter Steinberger 4eab19a25d fix(ui): stop route aliases from hijacking base-path inference on settings deep links (#115186) 2026-07-28 08:51:26 -04:00
Peter Steinberger b6f8641c28 refactor(ui): consolidate gateway host/status onto the Gateway settings page (#114971)
* refactor(ui): move gateway host panel and connection status to the Gateway page

* test(ui): update e2e expectations for the Gateway settings page move
2026-07-28 04:24:29 -04:00
Peter Steinberger 5a8939d88e refactor(ui): restructure Communications settings (Messages/Talk/Voice) (#114948)
* refactor(ui): communications settings own messages/talk/voice; channels config lives on Channels page

* test(ui): update route subtitle expectations for communications/infrastructure
2026-07-28 03:48:42 -04:00
Peter Steinberger 54309a832a feat(ui): expand the Lobsterdex with 14 palettes, lore tooltips, and a collection page (#114959)
* feat(ui): expand the Lobsterdex with 14 palettes, lore tooltips, and a collection page

* fix(ui): keep Lobsterdex view types internal
2026-07-28 02:22:00 -04:00
Peter Steinberger b8e7f27ff2 feat(ui): let a link's slug settle a shared short-id prefix (#114486)
* feat(ui): let a link's slug settle a shared short-id prefix

Two sessions can share the first block of their uuid, which sent an otherwise
exact link to the disambiguation view even though the slug in that link already
said which session was meant. Capture the slug on short references and use it to
break the tie.

It can only narrow: a slug matching none or several of the tied sessions leaves
the chooser alone, and a reference that already resolves uniquely is untouched.
Generated links stay at their normal length instead of growing a full uuid.

* fix(ui): never settle a slug tie from an incomplete search

A truncated candidate set is an unfinished search, not a tie: an unexamined page
could hold the same short-id prefix under the same slug. Settling there would be
the guess the bounded search exists to avoid, so keep the chooser.
2026-07-27 06:08:51 -04:00
Peter Steinberger 8b66fc103d feat(ui): durable session board face and dashboards index (#114262)
* feat(ui): durable session board face and dashboards index

Board face lived only in client-side boardSessionViews, capped at 50 entries,
so the preference never followed the user to another device, evicted as
sessions accumulated, and could not be seen as a set.

Persist it as SessionEntry.boardFace, which rides the existing entry_json blob
and so needs no SQLite schema change or version bump. Expose it on the session
list row and add it to the sessions.patch write-scope allowlist alongside label,
pinned, and archived: setting your own view preference is user-level chat
organization, not policy. Unknown patch fields still fail closed to
operator.admin.

Generic navigation now reads the stored face, so the sidebar and session list
open a thread on the face you left it on. boardSessionViews keeps only
activeTabId and reopenDockByTab, which are genuinely per-device.

Add /dashboards listing threads whose preferred face is dashboard. Filtering
runs server-side in filterSessionEntries before pagination, because the client
holds only a capped page and a client-side filter would silently omit
dashboards.

* test(protocol): assert the pre-rename face param is rejected

The gateway-protocol validator test still passed the pre-rename 'face' key,
which the closed schema rejects. Use boardFace, and pin the old name as a
negative case so it cannot silently return.

* chore(protocol): regenerate Swift bindings and docs map for boardFace

Adding boardFace to the sessions schema changes two committed generated
artifacts: the Swift gateway models (pnpm protocol:gen:swift) and the docs map
(pnpm docs:map:gen), which now lists the dashboards index section.
2026-07-27 00:35:34 -04:00
Peter Steinberger c91a37aeed feat(ui): add a Memory settings page (#114037)
* feat(ui): add a Memory settings page with Dreaming as a tab

Memory config was scattered across five surfaces: the memory.* schema section
lived on AI & Agents with 43 of 51 keys behind the Advanced tier, the memory
slot owner was only visible on Plugins, dreaming's knobs were JSON-only, its
status UI sat under Agents, and Memory Import was a separate route.

/settings/memory now owns that surface, following the MCP page shape (curated
rows above an embedded schema editor):

- Overview: the exclusive memory slot rendered as a segmented control over
  installed memory-kind plugins, memory.backend promoted out of Advanced with
  the qmd sub-config revealed only when qmd is selected, additive add-on rows,
  and a Memory Import link.
- Search: the memory.search surface via the embedded editor.
- Dreaming: the global frequency/model/timezone/storage/phase knobs, which
  previously required hand-editing openclaw.json, plus an agent picker feeding
  the existing dream scene/diary/advanced panel for the agent-scoped reads.

Engine selection calls plugins.setEnabled so the gateway's exclusive slot
policy stays the single owner instead of being duplicated in the UI.

* fix(ui): redirect stale ai-agents memory deep links to the memory page

* fix(ui): report memory runtime defaults on the Memory page

The Dreaming tab rendered its own defaults instead of the ones
resolveMemoryDreamingConfig applies, so a config carrying only
dreaming.enabled showed all three phases off while they were running, and
an unset storage mode read as inline instead of separate. Toggle specs now
carry the runtime fallback and the storage default is stated once, both
pointing at src/memory-host-sdk/dreaming.ts.

Three more surfaces asserted things the runtime does not do:

- plugins.slots.memory "none" is the explicit-off sentinel, not an engine
  id, so the segmented control selected nothing. The slot now resolves to a
  closed auto/off/pinned selection with its own hint.
- memory.backend is resolved by the memory runtime the slot owner
  registers, which only memory-core ships, so the row is hidden for any
  other engine instead of saving a value nothing reads.
- The Dreaming tab wrote config.dreaming for whichever plugin owns the
  slot even when that plugin's schema cannot hold it. It now reuses the
  enablement flow's schema check (resolveDreamingConfigPathSupport, shared
  with updateDreamingEnabled) and renders an unsupported state instead.

Also key the plugin-catalog sync on the connected phase: the connecting ->
connected transition keeps the same client object, so a page mounted
during the handshake never loaded the catalog and never showed the engine
picker.

The tab keeps the autosave status line and restart banner the embedded
editor renders on the other tabs; these knobs autosave, but nothing
reported it. The pure view moved to memory-dreaming.ts with the element in
memory-dreaming-page.ts, matching memory.ts/memory-page.ts.

* fix(ui): resolve the memory slot through the canonical policy

The Memory page re-derived plugins.slots.memory instead of using the rule the
runtime applies, which broke both directions of the engine control:

- An unset slot was reported as "the first enabled memory-kind plugin in the
  catalog". The runtime resolves it to the slot's default owner
  (DEFAULT_SLOT_BY_KEY.memory), so the page could show one engine as active
  while another was loaded, reveal or hide the backend row for the wrong
  plugin, and target the wrong plugin when switching memory off.
- Off called plugins.setEnabled(false), which writes enablement only. The slot
  stayed pinned, so the choice did not survive a refresh and re-enabling that
  plugin from the Plugins page silently switched memory back on.

resolveSlotSelection now lives next to defaultSlotIdForKey in
src/plugins/slots.ts and owns the rule once; config normalization consumes it
and the page imports it instead of restating it. Off writes the explicit "none"
sentinel through the config form, so it round-trips; picking an engine still
goes through plugins.setEnabled, which is where the exclusive slot policy
lives. The dreaming controller's own copy of the rule is gone too.

Four smaller fixes on the same surface:

- A failed engine change is reported next to the control instead of being
  swallowed, so the selector no longer just snaps back.
- Dreaming's numeric inputs carry the memory-core manifest's integer/min/max
  bounds and refuse out-of-range edits at the field, rather than patching a
  value autosave then fails to write.
- Settings search destinations carry the Memory tab that renders the matched
  child, so a memory.search hit no longer lands on Overview, whose narrowed
  editor omits it.
- The Dreaming tab caches only a definitive schema-capability answer. An
  offline or failed lookup now reports "unknown" and is retried on reconnect
  instead of permanently suppressing the recheck.

* fix(ui): model unknown memory state instead of collapsing it

The Memory page reported unknowns as decided values. An empty catalog meant
loading, disconnected, or a failed plugins.list, yet add-on rows rendered
"Disabled"; catalog completions were keyed on client identity, which survives a
phase flip, so a stale load could repopulate a disconnected page or overwrite a
newer read; and `?tab=` was adopted once per distinct value, so a repeat
navigation to a tab the user had left was ignored.

Replace the ad-hoc nullable fields with closed shapes. MemoryCatalog is a
loading/unavailable/ready union, so absence of an entry only decides anything
inside `ready`, and MemoryAddonRow carries a four-state enablement the view
renders without ever inventing an "off". CatalogConnection is one object per
(client, connected) transition and doubles as the request generation an
in-flight load carries, so obsolete completions are dropped by identity. The tab
is no longer page state at all: the URL owns it, tab clicks navigate, and every
arrival is honored.

Settings search now resolves the engine/backend through the same
resolveMemoryBackend the page uses and matches only the `memory.*` children the
page can surface, so a `memory.qmd` hit under the built-in backend no longer
routes to an Overview whose editor omits it.

* fix(ui): surface a disabled memory owner and anchor curated backend search

The slot and plugin enablement are independent config surfaces, so
`plugins.slots.memory` can name a plugin the catalog reports as disabled.
The engine control showed that plugin as selected, and because re-picking an
already-selected radio fires no change event, there was no way back on. Add an
explicit enable row for that state and let the same-id write through when the
owner is not running; picking Off stays a no-op.

`memory.backend` is curated out of the schema editor, so the generic
`#config-section-memory` anchor scrolled past it. Fold the memory tab and hash
choice into one `memoryDestination` owner that routes a curated-only match to
the new anchor above the editor.

* fix(ui): scope the dreaming capability probe to its connection

The probe was deduplicated by plugin id alone, which cannot tell a current
answer from a stale one. A disconnect and reconnect on the same slot owner left
the token armed, so the reconnect read as "already in flight" and swallowed the
retry that an `unknown` answer requires — leaving an unsupported engine's knobs
editable until some unrelated config notification arrived. An A -> B -> A switch
had the mirror problem: the old A response was accepted for the new A probe.

Make the in-flight probe an object whose identity is the generation, drop it
whenever the owner or the connection changes, and accept only the completion
that still owns the slot. Same shape as the catalog guard on the Memory page.

* fix(ui): satisfy the lint and dead-export gates on the memory page

Exhaustive switches need a terminal `default:` to satisfy
typescript/consistent-return, matching the existing view-status.ts shape.

Seven symbols were exported with no production consumer outside their own
module, which the hard-zero Knip production scan rejects. Tests alone do not
make internals contracts, so drop the exports and reach the behavior through
each module's public surface instead: the view props type comes from
`Parameters<typeof renderMemory>`, the tab panel is found by its ARIA role, and
the dreaming number/storage helpers are proven through `renderDreamingSettings`.

Folding those helper unit tests into the render path also corrected one of them:
a `type="number"` input coerces unparseable text to empty, so the "reject
garbage" case was unreachable through the real control. Replaced with the
inclusive-bound and clear-the-field cases, which are reachable.

* refactor(ui): keep the memory schema facts out of the startup bundle

Settings pages are already lazy — the config route is `import("./config-page.ts")`
— but settings search runs from app-host at startup, and it needed the same
answers about which `memory.*` children are reachable and where a match lives.
Importing those from the view module dragged lit, hub-tabs, and settings-ui into
the startup chunk with it, blowing the Control UI startup budget.

Move the rendering-free facts (slot/backend resolution, tab and curated key
lists, schema narrowing, the anchor id) into memory-schema.ts, which imports
only record-coerce and the shared slot policy. The view keeps the templates and
now consumes the same module, so there is still one owner per fact.

* chore(ui): record the memory settings surface in the startup budget baseline

Routing settings search through memory-schema.ts instead of the view module
recovered 10,872 B of the startup chunk (334,992 -> 324,120 B), which is back
under the 324,608 B ceiling. The remaining 2,795 B over the old baseline is the
honest cost of the new surface: its i18n strings, plus the slot/backend facts
the startup search index has to read.

Measured by hosted CI (run 30189972795); this worktree cannot build locally
because pnpm wants to purge a node_modules shared with other running agents.
2026-07-26 21:05:03 -04:00
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
Peter Steinberger e069b9dbdd fix(ui): repair settings titles, scope form-unsafe banner, consolidate advanced settings (#113160)
* feat(ui): repair settings titles, scope form-unsafe banner, consolidate advanced tier

- Advanced/Notifications settings headers rendered raw i18n keys
  (tabs.advanced/tabs.notifications); configPageTitle now resolves through
  the nav registry titleForRoute, deleting the drifting key map.
- Form-unsafe banner is value-aware (only when the user's config has a value
  at a form-unsupported path in the active scope), names the paths, offers an
  Open Raw editor action, and sits in the content column instead of full-bleed.
- Advanced tier collapses to one synced toggle: hidden advanced fields render
  a ghost row that enables the toggle; the Advanced page always reveals and
  hides the toggle; per-section details state and the controlled-open dance
  are deleted; toggle visibility mirrors the renderer's tier split.
- schema.tags no longer tags facet-less paths as "advanced" (common fields
  like update.channel wore a lying chip); the chip is no longer rendered in
  form rows while tag:advanced search keeps working.

* fix(config): dev bootstrap writes canonical agents.entries; finish stale agents.list sweep

- openclaw gateway run --dev failed on a fresh state dir: the bootstrap wrote
  the retired agents.list array and validation rejected its own config
  (Unrecognized key: list). It now writes the keyed agents.entries record;
  new dev.test.ts validates the written shape against the zod schema.
- Revive the dead gateway hot-reload rule: prefix agents.list never matched
  canonical config diffs, so per-agent heartbeat edits fell through to the
  agents:none tail rule; the rule now matches agents.entries.
- config set replacement protection moves from the dead agents.list array
  to the agents.entries map (joins plugins.entries/auth.profiles family).
- Sweep remaining stale agents.list config-path strings in hints, fix-it
  messages, elevated gates, doctor hints, audit text, and type docs to
  agents.entries.*; RPC ids, the internal list projection, and doctor
  legacy-migration references intentionally keep the old name.

* test(ui): derive nav i18n audit from route registry; fix stale bootstrap hint assertion

- Knip flagged navigationCopyEntries (test-only export); the audit now walks
  ALL_ROUTES through prod titleForRoute/subtitleForRoute and rejects raw
  dotted-key output, so no export exists solely for the test.
- commands-context-report expected the retired agents.list[] hint wording.

* fix(config): align rebase resolution with main's roster-aware diagnostic

- tool-policy-diagnostic: keep main's test expectations (dotted
  agents.entries.<id> paths from the roster-aware implementation); our
  pre-rebase bracket-style assertions no longer match any code.
- Reapply the placeholder sweep the conflict resolution dropped:
  generic agents.list[] fallbacks in tool-policy-diagnostic and the audit
  sandbox-mode hint now say agents.entries.*.
2026-07-25 00:51:57 -07:00
Peter Steinberger 856f534880 feat(ui): mirror active destination and connection state in the dashboard title (#112929)
* feat(ui): mirror active destination and connection state in the dashboard title

* feat(ui): title agent main chats with the agent name

* test(ui): split document-title shell tests to satisfy max-lines
2026-07-23 06:10:54 -07:00
Peter Steinberger b83af8157b test(ui): reuse shared non-isolated workers (#112493)
* test: isolate OpenClaw home in shared setup

* test(ui): reuse shared workers for pure suites

* test(ui): align scoped runner contract
2026-07-21 21:08:12 -07:00
Peter Steinberger 440539b538 feat(workboard): routable boards, sidebar pinning, icon and color (#112302)
* feat(ui): add Workboard board routes and pins

* fix(ui): reject malformed Workboard board catalogs

* perf(ui): lazy-load Workboard sidebar runtime

* test(ui): align sidebar board fixtures with render contract

* fix(ui): clear Workboard landing gates
2026-07-21 15:16:38 -07:00
Peter Steinberger 2220e9f841 fix(ui): give settings sidebar rows unique icons (#111974) 2026-07-20 16:34:05 -07:00
Peter Steinberger bb7b2bca68 feat(ui): finish the chat session → thread rename across the Control UI (#110973)
* feat(ui): finish the chat session → thread rename across the Control UI

Completes the #110933 creation-surface rename mechanically: Threads page
(title, filters, archive/delete/rename/pin menus, transcript search),
sidebar labels and menus, welcome suggestion, palette category, tools
availability copy, workboard lifecycle/card strings, dreaming history,
board/diff/workspace-rail chrome, and the untitled main/channel fallback
names. Technical identifiers stay: Session ID, session keys, terminal
sessions, protocol wording. Living docs updated (control-ui.md);
historical release notes left as shipped.

* style(ui): format session-management e2e

* fix(ui): finish thread rename in navigation labels and stale test literals

Codex review caught the palette Threads entry and the Worktrees owner
column still saying Session; CI caught test literals asserting the old
copy (Threads page overview tile, filter tooltips, group counts, catalog
terminal notice, create-rejection error, workboard lifecycle hint). Also
completes the usage-stat cluster (Threads + hints) and the tool-catalog
group label. Native app strings (iOS/Android/macOS) keep session wording
and reconcile through their own i18n pipelines as a follow-up.
2026-07-18 23:15:13 +01:00
Peter Steinberger ef7f4f2a61 feat(ui): Labs settings page for experimental feature toggles (#110359)
* feat(ui): add Labs settings page

* chore(ui): drop changelog entry — release generation owns CHANGELOG

* fix(ui): drop lint suppression, sync Apple i18n catalogs for Labs keys

* ci: retrigger CI on current head

* docs(ui): clarify swarm labs-entry gating comment
2026-07-18 21:08:52 +01:00
Peter Steinberger 36cba351e5 feat(ui): distinct Apps icon and pairing shortcut on the Apps page (#110909) 2026-07-18 20:17:25 +01:00
Peter Steinberger 3b2797f09b improve(ui): settings cleanup batch — universal config.changed emitter, synced chat prefs, dedupe and copy fixes (#110581)
* fix(gateway): emit config.changed from the reload committer for every accepted write

Agent config_set, CLI, and doctor writes reach the gateway through the file
watcher's reload path, which never broadcast config.changed - only direct RPC
writes did. Move the broadcast to a single onConfigCandidateCommitted hook in
the reload committer so all writers notify connected UIs, including
runtime-skipped commits (writer-intent echo suppression, reload mode off),
and delete the duplicate RPC-side broadcasts.

* feat(ui): sync chat follow-up and commentary prefs across devices

Add chatPersistCommentary and chatFollowUpMode to the ui.prefs sync surface
(schema, types, docs) with clearable null removal for the follow-up override.
Rewrite server-prefs around a descriptor table so a synced pref is one entry
instead of five hand-written code sites. Caption synced rows/sections
(chat prefs, theme, language) and refresh the stale Settings subtitle.

* refactor(ui): dedupe settings constants, drop dead composer branch, refresh copy and docs

Derive the Security tool-profile options from PROFILE_OPTIONS and General's
thinking levels from BASE_THINKING_LEVELS instead of parallel literals.
Remove the unused composer mode:"model" branch, the orphaned
.config-view-toggle CSS family, and stale quick-settings wording. Add static
settings-search entries for the Sessions and Managed Worktrees pages plus a
provenance-row test, and update docs/cli/openclaw.md to describe the current
system-agent config-write policy (denylist roots, route-backing plugin guard,
per-agent routing escalations).

* chore: refresh plugin SDK API baseline after rebase onto current main
2026-07-18 12:10:20 +01:00
Peter Steinberger 654560f289 feat(ui): add Apps & extensions page to the Control UI (#110563)
* feat(ui): add Apps & extensions page

* feat(ui): add per-theme app-art card illustrations

* docs: describe the Apps and extensions page in control-ui

* fix(ui): keep icons.ts under max-lines, drop unused export, regen docs map
2026-07-18 09:52:34 +01:00
Peter Steinberger 64aebb996a improve(ui): settings follow-ups — live pref sync, model provenance, Agent Defaults, Memory Import restyle (#110494)
- gateway broadcasts a hash-only config.changed event after every
  persisted config write (operator.read scoped); the Control UI refreshes
  its snapshot on the event so agent-approved ui.prefs changes apply live
  on every connected client (skipped while a local draft is dirty)
- the chat composer's model picker shows provenance — 'Using default
  from Settings' vs 'Session override' with an icon reset back to the
  default — mirroring the existing reasoning-row anatomy
- the AI & Agents settings page is renamed Agent Defaults to separate it
  from the per-agent Agents manager
- Memory Import renders through the settings design language (sections,
  rows, toggle, status badges); only the collection review list, apply
  report, skeleton, and confirm dialog keep custom markup, and the dead
  bespoke CSS is pruned
2026-07-18 08:14:11 +01:00
Peter Steinberger 723f5ba4fc improve(ui): restructure Settings around user-facing pages with full agent parity (#110445)
* refactor(ui): restructure Settings IA around user-facing pages

Settings sidebar shrinks to settings-only destinations grouped by attention:
General/Appearance/Notifications up top, Connections, Agents & Tools,
Privacy & Security, System. Sessions, Worktrees, Activity, and Memory
Import leave the takeover: Sessions/Activity return to the workspace
sidebar, Worktrees becomes a Sessions hub tab, Memory Import is reached
from the Agents memory panel. Model Setup becomes a Models subpage.

New config pages: Notifications (web push), Privacy & Security (curated
policy rows + security/approvals schema sections), Advanced (uncurated
schema sections + raw editor, replacing the General quick/advanced
toggle). General slims to model defaults, language, and gateway host;
identity moves to Profile, lobster appearance rows to Appearance.

* feat(agents): widen system-agent config writes to operator parity

Every config surface the Control UI can edit is now agent-writable behind
the exact-operation human approval gate, except a small documented
denylist ($include, auth, env, models, secrets). tools.* and
plugins.entries.* writes are allowed; plugin_uninstall works for plugins
that provably do not back the active default inference route (fail closed
otherwise); set_default_model takes an optional agentId and live-tests a
per-agent model with the same rigor as the default route. Per-agent
routing fields on non-default agents are direct-writable; the default
agent's route keeps the verified set_default_model/onboard paths. A new
config-write-parity contract test forces explicit classification of every
top-level config key.

* feat(ui): sync operator display prefs through config ui.prefs

Theme, theme mode, text scale, locale, and chat display prefs get a
canonical server-side home in config ui.prefs. The Control UI mirrors
them into localStorage for instant boot, writes local changes through to
the gateway (best-effort; viewer-scope or offline clients stay
device-local), and applies server-side deltas on connect and on every
config snapshot reload — so an agent changing the theme through the
approval gate takes effect in the UI, and prefs follow the operator
across devices.

* chore: prune quick.ts from max-lines baseline after slim-down

* style: oxfmt changed files

* chore: regenerate plugin-sdk api baseline for ui.prefs config surface

* fix: dedupe runPluginUninstall dep type and narrow locale extraction

* docs: update Control UI settings structure and ui.prefs sync

* fix: address autoreview findings on parity guards and prefs sync

- plugins.entries.* config writes get the same active-route ownership
  check as plugin_uninstall (fail closed when unprovable)
- system-agent prompt now describes the guarded write policy instead of
  the old blanket refusals
- per-agent set_default_model logs the agent-scoped label
- legacy /config?section=... deep links redirect to the Advanced page
- prefs sync: pushes coalesce and drain serially (no CAS races dropping
  updates), replaced config hashes mark pre-patch snapshots as stale, and
  the last-seen server value persists per gateway scope so reloads cannot
  revert unpushable viewer-scope edits
- lint: theme membership checks use Sets

* fix: harden uninstall route guard and scope prefs queue per gateway

- plugin uninstall ownership now covers execution owners (runtime policy,
  embedded harness override) in addition to the provider, and re-verifies
  inside the commit boundary so a concurrent route switch cannot slip a
  destructive uninstall past the pre-approval check
- prefs push queue binds to one gateway client; switching gateways drops
  the old queue (device-local fallback) instead of cross-writing prefs

* style: top-level type import in config-page test

* fix: close review races in config-write guards and prefs reconciliation

- config_set/config_set_ref re-verify the inference-route guard inside
  the commit boundary so conditional verdicts (per-agent routing, plugin
  entries) cannot be flipped by a concurrent edit after approval
- server prefs apply per changed field, so a server change to one pref
  no longer reverts unpushable local edits of the others
- replaced-hash staleness retires once post-patch state is observed, so
  a genuine restore of the pre-patch config becomes authoritative again
- moved-section deep links (communications notifications, automation
  approvals) redirect to their new pages

* style: satisfy no-useless-assignment and loop-condition lints in prefs sync

* fix(ci): resolve unhandled profile-test rejections and knip unused exports

The profile page's new identity section reads context.config; the test
context mock now provides it, eliminating the unhandled rejections that
failed checks-ui and the compact-large shard. Exports flagged by the
dependency gate (extract/patch helpers, section-key and prop types) go
module-private; extraction coverage moves behind applyServerUiPrefs.

* refactor(agents): move config-write policy to its own module

The static path classifier and documented denylist move to
config-write-policy.ts, shared by the execution guard and the parity
contract test — no test-only exports left for the dependency gate to
flag. Blocked-root refusals now cite the denylist's documented
escalation for that root.
2026-07-18 07:03:31 +01:00
Peter Steinberger 6a0f5541c6 feat(ui): permanent OpenClaw presence — pinned sidebar entry and Settings dock (#110269)
* feat(ui): add permanent OpenClaw navigation

* refactor(ui): drive Exit setup visibility with a typed onboarding property

The route view previously hid the button with an injected style tag (the page
file was owned by a parallel lane); render it conditionally instead.

* test(ui): use the navigation route-load cause literal

* fix(ui): request the caretaker greeting for permanent custodian visits

The onboarding welcome variant seeds the first-run setup proposal; the pinned
sidebar and Settings entries now omit it so post-setup visits get the normal
caretaker greeting, driven by the same route onboarding flag as the chrome.

* fix(ui): restart custodian session on mode change
2026-07-18 03:29:25 +01:00
Peter Steinberger e32292e534 feat(ui): custodian onboarding surface with option cards and first-run handoff (#110141)
* feat(ui): add custodian onboarding surface

* fix(ui): reset custodian session when credentials change on the same gateway

* fix(ui): include bootstrap token in custodian session ownership key

* fix(ui): no replay for ambiguous user turns; send sensitive input verbatim

* fix(ui): harden custodian session and route state

* fix(ui): keep device-token session scope stable across transient drops

* fix(ui): detect gateway scope errors structurally, not by class identity

Under isolate:false a custom element registered by an earlier test file keeps
its own module registry, so instanceof GatewayRequestError diverges while the
error shape stays stable; the branch's new custodian test files reshuffled
worker packing and exposed this in usage-page.test.ts.
2026-07-17 23:20:20 +01:00
Peter Steinberger cb84dc76fc feat(control-ui): approval attention chip + approval history page (#108776)
Adds the sidebar approval attention chip and a dedicated approval history
page backed by the new approval.history gateway RPC (30-day retention
window). Extracts buildSidebarAttentionItems into its own module so the Lit
component consumes it as a real cross-module dependency, and wires the strict
i18n catalog for the new strings (fallbacks=0).
2026-07-16 10:32:05 -07:00
Peter Steinberger 4b904e6ac8 feat(ui): guided Model Setup page — web/Linux equivalent of the macOS Connect-your-AI flow (#108868)
* feat(ui): add guided Model Setup page for inference onboarding

* fix(ui): regenerate i18n locale bundles and narrow model-setup exports for CI gates

* chore(ui): regenerate i18n baselines after rebase

* chore(ui): translate model-setup strings for all locales
2026-07-16 04:16:27 -07:00
Peter Steinberger 4319ddbe8c feat(control-ui): import Codex and Claude Code memory (#106406)
* feat(control-ui): import coding assistant memory

* test(migrate): clean memory import temp dirs

* chore: move memory import note to PR body

* build: refresh memory import generated artifacts

* fix(control-ui): complete memory import checks

* build(control-ui): localize memory recovery labels

* fix(control-ui): harden memory import recovery

* refactor(migrate): keep memory import surfaces bounded

* fix(control-ui): use canonical agent row type

* fix(codex): drop dead migration type exports

* fix(migrate): restrict imported memory permissions

* fix(control-ui): preserve memory import recovery state

* fix(control-ui): retain memory import results

* build(control-ui): preserve translation memory history

* fix(control-ui): bind memory import state to agent

* fix(control-ui): unlock memory import after refresh

* test(gateway): preserve memory method suffix

* fix(migrate): bind reviewed memory imports

* fix(migrate): make memory imports retry-safe

* perf(ui): keep memory import lazy

* build: refresh generated artifacts after rebase

* chore: keep migration runtime within LOC guard

* build: refresh Swift protocol model
2026-07-13 15:30:06 -07:00
Peter Steinberger bfe2786957 refactor(ui): remove dead exports (#105979)
* refactor(ui): remove dead exports

* chore(deadcode): refresh export baseline

* refactor(gateway): privatize chat media fields
2026-07-13 11:32:00 -07:00
Peter Steinberger 9086389527 feat(control-ui): agent identity editor, Settings-homed Agents page, scalable chip switcher (#106058)
* feat(control-ui): agent identity editor, settings-homed agents page, chip switcher pinning

* test(control-ui): pinned-agent persistence coverage; persist pins only when set

* refactor(control-ui): split agent menu, identity actions, model staging out of oversized files

* chore(i18n): sync control-ui locale bundles for agent identity/switcher keys

* fix(control-ui): stabilize new sidebar switcher tests; ratchet LOC + export baselines

* chore(review): document identity clear limitation; drop unused export

* feat(control-ui): scope pages to selected agent

* fix(control-ui): keep scoped page state consistent

* docs(control-ui): explain shared agent scope

* fix(control-ui): reconcile agent scope with current ui

* fix(control-ui): preserve complete agent filtering

* fix(control-ui): bound uploaded agent avatars

* test(control-ui): keep pinned-route fixture valid

* test(control-ui): keep pinned-route fixture valid

* chore(i18n): sync locale bundles for agent scoping keys
2026-07-13 04:03:26 -07:00
Peter Steinberger 62e53919fd improve(ui): move Devices into Settings and redesign the device inventory (#106055)
* feat(ui): move Devices page into settings and redesign device inventory

- Devices now lives at /settings/devices (System group); /nodes stays as alias
- per-device form-factor icons (phone/browser/terminal/machine) with status dot
- equal-height header actions; stale cleanup and pairing only
- drop manual Refresh button; page already auto-refreshes via presence events + 30s poll
- dynamic connected/pending summary replaces static card subtitle

* style(ui): oxfmt pass + loading empty state for devices inventory

* chore(ui): sync locale bundles for devices settings copy; docs point at Settings → Devices

* refactor(ui): split device tile + pending rows out of view-inventory; drop unused icons

Keeps the TypeScript LOC ratchet green: view-inventory.ts returns below its
baseline and icons.ts sheds unused folderOpen/micOff/volumeOff glyphs.

* fix(ui): ratchet LOC baseline, drop unused DeviceIconSource export, settle devices settings copy

- baseline: icons.ts 693, view-inventory.ts back under the 500 ceiling
- subtitles.nodes now describes the settings page; nav tests updated
- locale bundles resynced (fallbacks=0)

* refactor(ui): rename token row param for reviewer-tool clarity

* fix(ui): collapse device rows in narrow containers and classify the TUI as a terminal client

Review findings: the nodes-entry grid override beat the shared 560px
list-item collapse, overflowing phone-width rows; openclaw-tui connects
with mode ui so only its client id marks it as a terminal.

* chore(ui): translate pending locale keys after rebase (fallbacks=0)
2026-07-13 03:18:17 -07:00
Peter Steinberger 73da94f06f improve(ui): move Sessions from the sidebar More menu into Settings (#106032)
Sessions is session management, not a day-to-day workspace destination.
It now lives in Settings under Agents & Tools at /settings/sessions,
with the old /sessions path kept as a route alias; stale pinned-route
prefs drop automatically via normalizeSidebarPinnedRoutes. Also repairs
the sidebar-customization e2e that #105777 broke on main by opening
Settings through the agent-chip menu.
2026-07-13 00:30:20 -07:00
Shakker b640718204 fix: segment localized settings queries (#104675) 2026-07-12 21:46:50 +01:00
Peter Steinberger cfe95266af feat(ui): redesign cron page as sidebar-first Automations master-detail (#104251)
Rebuilds the Control UI cron page as a sidebar-first Automations master-detail workspace: a task list pane (All/Active/Paused tabs, search, filter popover, curated emoji suggestion cards, scheduler footer) beside a detail pane that edits the selected task inline (prompt, row-style Details/Frequency groups, collapsible Advanced, per-task run history) and shows all-task recent activity when nothing is selected. Create & run now kicks a new task immediately after creation. The modal editor and quick-create wizard are retired; the automation-ideas catalog from #104761 is ported into the new suggestions list. Nav item renamed to Automations and pinned by default; route (/cron) and cron.* RPC contracts unchanged.

Closes #104217

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-12 07:12:26 +01:00
Peter Steinberger dd5928711a feat(ui): fold Skills and Skill Workshop into a Plugins hub (#104834)
* feat(ui): fold Skills and Skill Workshop into a Plugins hub

One sidebar entry (Plugins) now covers plugins, skills, and skill-workshop
routes via a shared hub tab strip (Installed / Discover / Skills / Workshop).
Routes and URLs are unchanged; /settings/plugins gains ?tab= deep links and
the sidebar highlights Plugins for all hub routes.

Also latches Skill Workshop proposal loading so updated() no longer re-kicks
loads while one is pending or after one fails - the early-return finally
used to spin the page at 100% CPU whenever skills.proposals.list stalled.

* feat(mock): plugin catalog and skill-workshop fixtures for the mock harness

* docs(web): describe the Plugins hub tabs in the Control UI guide

* test(ui): backfill initialTab in plugins route-data fixtures

* fix(ui): review fixes for the plugins hub

- plugins route declares loaderDeps on the ?tab= param so query-only
  navigation and back/forward re-run the loader instead of reusing the
  cached match with the previous tab
- catalog tab clicks navigate so the URL and history stay in sync with
  the documented ?tab=discover deep link (local switch stays instant)
- skill-workshop error banner gains a Try again button so a transient
  proposals-list failure is recoverable without remounting the route

* fix(ui): restore the Installed tab on bare plugins URLs in history navigation

* fix(ui): manual-activation hub tablist with cross-route focus hand-off

Arrow keys only move focus now; activation stays on click/Enter so arrowing
can never unmount the strip under the user's focus. Keyboard activation of a
cross-route tab hands focus to the destination strip's active tab, and the
hub-route set uses a Set per lint.

* fix(ui): defer hub-tab focus reclaim until the strip is connected

* fix(ui): skip focus recovery for same-tab hub activation

* docs(css): note the hub-panel flex ownership for the workshop board

* fix(ui): adapt hub test to retired Overview route

* chore(i18n): sync locale bundles for the plugins hub keys

* docs(map): regenerate for the plugins hub section
2026-07-11 22:03:38 -07:00
Peter Steinberger 2829fe107c improve(ui): retire the Overview page for a chat-first start screen (#104819)
* feat(ui): replace Overview page with Connection settings and sidebar attention chips

* feat(ui): open new-session drafts on the chat start screen hero

* refactor(ui): drop old overview-hints paths after rename

* chore(i18n): sync control-ui locale bundles for connection/palette/attention keys

* test(ui): expect attention slot above the sidebar update card

* chore(i18n): re-sync locale bundles after rebase onto main

* test(ui): stub sidebar-attention RPCs in app-sidebar unit tests

* test(ui): use a non-secret-shaped token fixture in connection view test

* refactor(ui): destructure gateway connection in connection settings draft

* refactor(ui): keep connection settings code out of secret-scanner shapes

* test(ui): expect Connection in the settings Connections group

* improve(ui): idle-refresh sidebar attention chips for always-visible windows

* chore(i18n): refresh raw-copy baseline after rebase

* docs(ui): note the unknown-route chat fallback covers retired paths
2026-07-11 18:11:14 -07:00
Peter Steinberger 3255b3218a feat: unify external sessions with native catalog pagination (#104717)
* feat(gateway): add generic sessions.catalog surface with plugin SDK registration seam

* feat(agents): support one-shot forked CLI session resume with successor rebinding

* feat(anthropic): adopt local Claude CLI sessions into native chats via catalog continue

* feat(codex): register the session catalog provider independently of supervision

* refactor(ui): delete the retired custom Claude/Codex session tab views

* feat(ui): render external session catalogs as native sidebar sessions and chat panes

* docs: describe the unified native session catalog

* fix: harden forked CLI resume and catalog transcript mapping after review

* fix: satisfy strict typecheck for catalog source guard and imported message cast

* chore(i18n): regenerate session catalog locales

* fix(codex): simplify session source guard type

* fix(ui): paginate sidebar session catalogs

* chore(i18n): refresh catalog metadata after main merge

* fix(ui): harden session catalog pagination refresh

* test(agents): use sqlite session accessor in fork tests

* fix(ci): refresh session catalog generated surfaces

* fix(ui): align session catalog locales

* fix: address session catalog review findings

* fix(sessions): roll back failed plugin catalog adoption

* test(sessions): preserve CLI binding literals

* fix(ui): restore native session pagination label

* docs: note external session catalogs

* Revert "docs: note external session catalogs"

This reverts commit cfb503f160.
2026-07-11 17:26:26 -07:00
Peter Steinberger 1b1a41d87f refactor(ui): sidebar IA cleanup: Activity to Settings, Dreams into Agents Memory tab, single MCP mutation owner (#104604)
* refactor(ui): move dreams page modules to agents/memory (verbatim)

* refactor(ui): sidebar IA cleanup: Activity to Settings, Dreams into Agents Memory tab, single MCP mutation owner, Usage icon

Folds the per-agent Dreams page into the Agents page as a Memory panel and
deletes the dreams route; moves the session-scoped Activity page out of the
sidebar into Settings > System next to Logs; removes the MCP enable/disable
toggle from Settings > MCP so the Plugins page is the single mutation surface
for config.mcp.servers; gives Usage a coins icon so it no longer collides
with Overview.

Closes #104590

* docs: update Control UI docs for Activity/Memory/MCP sidebar changes

* fix(scripts): fail control-ui i18n sync closed on new untranslated keys without provider auth

An unauthenticated ui:i18n:sync silently recorded English fallbacks, which the
shipped-fallback CI gate then rejects; post-merge translation is owned by the
control-ui-locale-refresh workflow. Sync now errors on new untranslated keys
without a provider (OPENCLAW_CONTROL_UI_I18N_AUTH_OPTIONAL=1 opts back in),
and the ui AGENTS guide documents the commit-en.ts-only contributor flow.

* chore(ui): translate new sidebar keys, refresh i18n baseline, localize Memory tab expectation
2026-07-11 14:24:39 -07:00
Peter Steinberger c48ed95533 feat(ui): merge Instances into a unified Devices page (#104561)
* chore(i18n): regenerate locale bundles for Devices rename

* chore(i18n): regenerate locale bundles for Devices rename

* chore(i18n): sync locale metadata for Devices rename

* fix(gateway): stop tracking ephemeral control-plane clients in presence

* feat(ui): merge Instances into a unified Devices page

* docs: point Instances references at the unified Devices page

* chore(i18n): reconcile locale metadata after rebase

* fix(gateway): keep test-mode clients tracked in presence; refresh docs map
2026-07-11 13:36:07 -07:00
Peter Steinberger 05aecc1d6f feat: show Codex transcripts in the sidebar (#104437)
* feat: show Codex transcripts in sidebar

* fix: clear Codex sidebar on Gateway switch

* chore: leave release notes to release workflow

* chore: refresh native i18n inventory

* fix: satisfy macOS Codex node lint

* fix: refresh native localization inventory

* docs: refresh documentation map

* chore: refresh UI locale metadata
2026-07-11 05:39:29 -07:00