refactor(views): remove the companion (VRM avatar) feature + dead steward wallet-policy UI - #10434
Conversation
|
Too many files changed for review. ( |
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
In the editable tree this surface was already dead — PolicyControlsView was mounted nowhere (no route, no settings section, no app-shell page), and the steward/injected slot wrappers always rendered null because nothing in the real app assigns the boot-config steward slots. The only consumers were the view's own Storybook stories, a stringly-typed agent coverage entry, and a feature-surface showcase. The live steward components ship from the external @elizaos/plugin-steward-app (not a declared dependency here). Removed: - PolicyControlsView.tsx (+ story) and the whole policy-controls/ module (AutoApprove/SpendingLimit/RateLimit/ApprovedAddresses/TimeWindow sections, PolicyToggle, helpers, constants, types, stories, smoke tests). - components/steward/ (injected slot wrappers + stories + smoke test). - AppBootConfig steward slots (stewardLogo/stewardApprovalQueue/ stewardTransactionHistory) and the StewardLogoProps / StewardApprovalQueueProps / StewardTransactionHistoryProps / AppBootStewardTx* / AppBootStewardPending* / AppBootStewardApprovalActionResponse types in boot-config-store. - The matching barrel re-exports (components/index, ui index), the agent external-modules.d.ts prop shims, the "settings/PolicyControlsView" coverage entry, the feature-surfaces story showcase, the orphaned template stub exports, and a stale design-doc listing. Kept (real wallet/auth plumbing): client-wallet approveStewardTx + /api/wallet/steward-approve-tx, client-types-steward (StewardApprovalInfo / StewardApprovalActionResponse), state approveStewardTx, contracts StewardApprovalInfo, the shared StewardPolicyResult type, and the CloudWalletProvider "steward" value. The cloud dashboard + all cloud Settings sections are untouched. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Deletes plugin-companion and every reference to it across the runtime,
app, UI, registry, build config, tests, and docs. The VRM avatar chat
overlay, the /companion view, the emote system (PLAY_EMOTE), the
companion ShellView state, and all wiring are gone; the shell state
machine is now {character, desktop}.
- Delete plugins/plugin-companion (199 files: plugin, views, actions,
assets, tests) and drop its workspace dep (bun.lock, package.json)
- Strip companion from agent core-plugins, plugin-resolver,
external-modules.d.ts, and every view-coverage / view-parity /
tui-view / bundle-lifecycle test; remove the qa-checklist companion
flow (live-e2e)
- scenario-runner: rewrite the companion-emote scenario to
GENERATE_MEDIA-only (deterministic-media-actions), preserving media
coverage, and drop PLAY_EMOTE from the action-coverage ratchet
- Regenerate the prompts action spec and core action-docs (PLAY_EMOTE
removed); drop PLAY_EMOTE from the training context-catalog
- Remove the dead `lucide-react -> ../plugin-companion/node_modules`
tsconfig path mapping from 15 plugins (lucide-react is root-hoisted)
- Remove companion stubs from the scaffold optional-app shim and the
unused LockScreen.companionBar slot (+ its DEFERRED.md note)
- xr-functional-parity: drop facewear from the GUI===XR parity registry
(its config moved to a Settings section, so it has no standalone GUI
view); registry is now 14 plugins, counts reconciled
- Clean stale companion references from knip.json, README, the root
CLAUDE.md/AGENTS.md, architecture.mdx, action-catalog.md, the
spatial-convert workflow, real.config, and plugin vitest/comment refs
Drift fixups re-derived against current develop (the original commit had
drifted ~207 commits):
- Retarget ensure-avatars.mjs off the deleted
eliza/plugins/app-companion/public dir to apps/app/public — the same
surviving VRM target the sibling process-vrms.mjs writes and the
static file server serves /vrms and /animations from
- Remove the now-dead playEmote client method (client-agent.ts) and the
/api/emote PostEmoteRequestSchema contract + test (shared/contracts)
- Drop the companion storybook glob/sort, the design-review template
companion ViewSpec, the EMOTE stub exports in the optional-app shim,
and the dead VITE_ENABLE_COMPANION_MODE flag from app/.env.example
- Fix the view-interaction-coverage visualCases count (58 -> 56) after
dropping the companion GUI_INTERACTION_OWNERS block
Note: packages/shared/src/i18n/keywords/action-search.generated.keywords.json
keeps its action.playEmote.request retrieval entry. It is generated
(retrieval-only ranking metadata, not an availability check) and a full
regen pulls in ~26k lines of unrelated keyword drift, so it is left at
develop's state, matching the original removal commit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6cc0193 to
7abbac4
Compare
The companion (VRM avatar) view (`viewId: "companion"`) is no longer registered after the plugin-companion removal, so every reference that routed to it — deterministic regexes, view-id token lists, command-matcher keyword maps, evaluator candidate sets, and their test expectations — was a dead route. Removed them: - plugin-app-control: the `/my companion|the companion|my avatar/ -> companion` deterministic rule (views-show.ts), the `companion`/`avatar` view-name tokens (views.ts), the `companion` keyword block + view-id entry (view-command-matcher.ts), the `companion` evaluator candidate (view-context.ts), and the matching test expectations (view-command-matcher.test.ts, view-context.test.ts, view-command-shortcut.test.ts — including the companion-emote cases). - plugin-app-control app/app-launch examples: swapped the removed `companion` example app for the still-existing `feed`. - plugin-training: dropped `companion` from the view enums in gepa-view-switching / multishot-view-switching / verify-view-switching (and the verify direct-prompt case). - plugin-facewear app-xr e2e route-server: dropped the `companion` view id. Kept (legitimate non-view uses of "companion"): the desktop companion-BAR UI variant (`"native" | "companion" | "titlebar"`), the electrobun companion WINDOW mode, mobile/desktop-companion remote-capability providers, phone-companion, browser-bridge companion, and the CSRF companion cookie. Verify: plugin-app-control view-command-matcher/view-context/ view-command-shortcut tests 236/236 pass; plugin-app-control typecheck clean; plugin-training scripts typecheck clean; grep shows no remaining `viewId: "companion"` / avatar-view routing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Trivial residual to sweep during the audit:app pass (non-blocking, non-functional):
All functional companion-view routing (plugin-app-control deterministic rules + matcher + evaluator, plugin-training view enums, facewear XR e2e route-server) is removed and verified clean. |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
LifeOps Benchmark —
|
LifeOps Multi-Tier BenchmarkSuite:
|
|
Post-merge review (adversarially verified): the core removal is clean and non-breaking — plugin-companion is fully gone, zero live-source references remain, and tsconfig/knip/registry/core-plugins/xr-parity-counts are all consistent. No over-deletion (the 2D avatar-preview path — There's a non-breaking dead-weight tail worth a follow-up cleanup (none of it breaks build/typecheck/tests, since every ref is inert dead config or tree-shaken dead code):
Filing as a follow-up rather than reopening, since none of it is a regression. |
LifeOps Benchmark —
|
LifeOps Multi-Tier BenchmarkSuite:
|
…in set #10434 (99cab28) removed the `companion` view from the builtin navigation set (BuiltinTab / TAB_PATHS) — and `voice` is likewise no longer a builtin tab — but the view-lifecycle matrix test still classified both, so "covers exactly the builtin tab set" failed on develop tip (classified 36 vs TAB_PATHS 34). TAB_PATHS/BuiltinTab is the authoritative builtin set and is correct; the test was stale. Drop the two stale entries; assertion stays an exact deep-equal against TAB_PATHS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ity, dock upgrade trap, chat-first sidebar, credential-preserving saves (#10994) * fix(ui): deep-QA batch — transcript data loss, notification reachability, dock upgrade trap, chat-first sidebar, credential-preserving saves Confirmed by an adversarially-verified QA sweep of the chat/launcher/home surfaces on develop (20 confirmed findings; this lands the highest-value, lowest-risk nine): - shell-state: keep attachment-only and secret-request-only turns in the transcript window — an image-only send (or a generated-image reply) used to silently vanish when the turn settled (CRITICAL: perceived data loss). New unit tests cover both roles, every phase, and the empty-array case. - HomeScreen pull-zone: stop double-counting safe-area-top (the strip deadened ~70px of home content on notched iPhones — taps dead, scroll refused); the strip now spans only the residual tucked band + 30px. It is also a real button now: click/tap and Enter/Space open the notification center, so desktop fine-pointer and keyboard/AT users have a path to notifications (was pointer-drag-only, home-only). - launcher-layout: re-seed the default dock for pre-#10800 payloads (favorites [] without intent marker) — those users were locked out of the dock forever with no in-product recovery. An explicitly committed empty dock now stamps dockCleared and stays respected. Corrupt payloads fall back to the seeded default. Migration + marker regression tests added. - ConversationsSidebar: 'New chat' clears the active terminal session (the fresh conversation was created but never shown); the collapsed-rail '+' is now chat-first (it always spawned a TERMINAL via an unreachable-branch bug; New terminal stays in the expanded terminal section). - conversation guards: stamp scope 'general' at creation and reject reserved legacy page-chat titles on rename — renaming a chat to 'wallet'/'settings'/… made it vanish from every list with no recovery. - plugin/connector config saves: handlePluginConfigSave now reports success; ConnectorsSection + PluginsView keep the user's typed draft when a save fails (pasted tokens/keys were silently wiped on network errors). - e2e harness de-flake (verified root causes, not product bugs): chat-sheet keyboard block uses structural flicks instead of a knife's-edge velocity race; launcher fixture overflows the seeded dock (27 specs) with a loud bundle-time guard, restoring the Page-2/page-swipe assertions. Focused suites green: shell-state (13), launcher-layout (35 incl. property), HomeScreen (7), conversations (21), settings+plugins state (202); run-launcher-e2e 15/15; run-chat-sheet-e2e PASSED (51 screenshots). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(evidence): post-fix captures for the deep UI/UX QA sweep (#10722) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ui): notification dismiss visibility, safe deep-link on home tile, reachable background redo Three more adversarially-confirmed QA findings: - NotificationCenter row dismiss (X) was opacity-0 with only a hover reveal — invisible on touch (the pull-down sheet is the primary mobile surface) yet still hit-testable, so near-edge taps silently deleted notifications; keyboard focus landed on an invisible button. Now dimmed visible at rest, full opacity on hover and focus-visible. - The home Notifications tile fed producer/LLM-influenceable deepLink strings straight into the navigate:view rail, bypassing the scheme-checked navigateDeepLink single source of truth: https deep links were dead taps (cross-origin pushState throw swallowed), unvetted schemes were forwarded, and the tile never marked the notification read (surfaces diverged). Now mirrors NotificationRow.handleOpen exactly. - Background redo (#10771) was unreachable by any user path: the UI channel handles op 'redo' but the BACKGROUND action's op union/resolver/schema had no redo and no UI control existed. Added the redo op end-to-end (regex intent, explicit op, handler broadcast, similes/examples/schema) and a Redo2 button next to Undo gated on canRedoBackground. 4 new tests; plugin suite 21/21, UI suites 15/15. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ui): overlays stacked above the chat win taps and Escape; palette opens visible Two adversarially-confirmed z-order findings: - The chat's document-level outside-tap swallower ate the FIRST tap on any surface painted above the chat glass (notification pull-down sheet at z-9501, tutorial card at Z_TUTORIAL, any Radix dialog) and collapsed the chat under it. The swallower now cedes taps whose target sits inside [data-above-shell-overlay] or [role=dialog]; the notification sheet, its backdrop, and the tutorial spotlight are tagged. - /commands opened the CommandPalette dialog (Z_DIALOG=170) BEHIND the open chat sheet (Z_SHELL_OVERLAY=9000) — an invisible focus-trapped dialog; Escape then closed the palette AND collapsed the chat at once. The composer's palette route now collapses the sheet first so the palette opens visible over the pill (raising all dialogs above the shell overlay was rejected: tooltips at Z_TOOLTIP=300 inside dialogs would stack under them), and the chat's Escape handler defers to an open dialog (scoped to [data-state=open] dialogs + the notification sheet — broad role=dialog would match always-mounted shell surfaces and permanently disable Escape-collapse). Regression tests: taps inside a role=dialog layer no longer collapse the sheet; Escape with an open dialog leaves the chat open and collapses it once the dialog is gone. 4/4 targeted + existing collapse tests green; run-chatux-gesture-e2e ALL PASSED post-change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ui): one-shot terminal problem-focus + tap-vs-scroll discrimination on the chat action rail Two adversarially-confirmed QA findings: - A blocked/errored coding-agent session permanently hijacked the chat surface: ChatView auto-focused any error/blocked PTY session whenever activeTerminalSessionId was null, and 'blocked' is a routine long-lived waiting-for-input state — closing the panel or selecting a conversation bounced straight back to the terminal, locking the desktop chat window out of every conversation. Auto-focus now fires at most once per transition into a problem state (pickProblemSessionToAutoFocus + a handled-set ref; viewing counts as handled; recovery evicts so a new transition focuses again). 7 new tests reproduce the loop through React's real effect scheduling. - Flick-scrolling the ChatView transcript on touch toggled the action rail (copy/edit/play/delete) on whichever message the finger started on: handleTapReveal had no move-slop or selection guard, unlike the shell ThreadLine. Touchstart position is now recorded and >10px travel (either axis) or a non-collapsed selection suppresses the toggle, mirroring ThreadLine's COPY_MOVE_CANCEL_PX pattern. 3 new tests (clean tap toggles; vertical/horizontal scrolls don't; selection suppresses). Focused suites: 17 files / 85 tests green; packages/ui typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ui): reachable wallpaper picker (Background settings section) + credential form for mode-less connectors Two adversarially-confirmed QA findings: - BackgroundSettingsSection was orphaned dead code: the only surface with BackgroundSettingsControls is the 'background' view (viewKind: preview — hidden on every default build) and the section was never registered, so a default user could not change, undo, or preview the wallpaper from any visible UI (chat-only via the BACKGROUND action). Registered as a real system-group Settings section (Wallpaper icon, en label; other locales fall back to the default label). - Settings → Connectors dead-ended every connector without a hardcoded mode list (farcaster, bluesky, matrix, nostr, msteams, mattermost, twitch, instagram, …) with the factually-wrong '{name} uses its own setup surface.' — no link, no form, even with declared parameters. Mirrors the canonical /connectors fallback: no declared modes + declared parameters → the credential form IS the setup surface (setupPluginId ?? plugin.id). local-setup/plugin-managed/cloud-managed modes keep their dedicated panels (regression suite still green). Settings sweep: 18 files / 208 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(evidence): audit:app 349/349 post-fix captures (broken=0 needs-work=0) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ui): single-owner gesture arbitration for nested pagers + fine-pointer paging control at any width Two adversarially-confirmed QA findings (one root cause): - The home↔launcher rail and the launcher grid pager both attach pointer handlers on the same bubble path: one horizontal drag was tracked — and painted — by BOTH pagers (touch: ~1.35x finger motion, background gap at the screen edge, spring-back jerk on release), and for mouse/pen the outer handler's later setPointerCapture stole the pointer mid-drag, freezing the inner pager (dragRef never saw pointerup, ResizeObserver resync skipped) — mouse drag-paging on launcher page 0 could never reach the Developer page. Violated the shell-surface store invariant that a swipe claimed by two machines must be structurally impossible. useHorizontalPager now runs a shared pointer-claim registry: trackers register in bubble order (innermost first); the first pager that commits a horizontal axis AND canMove in the drag direction claims the pointer exclusively and synchronously EVICTS the rest (push, not poll — losers may never see another event once the winner holds capture). Eviction abandons the drag immediately and settles to the resting page. An unowned dead-end drag rubber-bands on the innermost pager only. finish() claims before advancing so two pagers can never both commit one pointerup. Also fixes a latent scheduleOffset bug where a synchronous rAF left the frame permanently pending, swallowing later drag paints. - PagerEdgeButtons dropped its min-width:1024px gate (hover+fine-pointer kept): with page dots off in production, a fine-pointer window under 1024px had NO control that reached launcher page 2. Also updates the springboard-migration component tests to the #10800-QA dock re-seed semantics (dock-less migrated layouts dock chat+settings; manual page order still preserved for non-dock ids) — the old expectation was invalidated by the launcher-layout upgrade-path fix in this PR. Tests: 4 new nested-harness arbitration tests + 3 composed tests on the real bubbling path (tile-origin drags) + real-browser regression in run-home-screen-e2e (mouse drags page the inner grid both directions; the rail never hijacks) — HOME-SCREEN and LAUNCHER e2e PASSED; focused suites 88/88; packages/ui typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(ui): drop stale companion+voice from view-lifecycle matrix builtin set #10434 (99cab28) removed the `companion` view from the builtin navigation set (BuiltinTab / TAB_PATHS) — and `voice` is likewise no longer a builtin tab — but the view-lifecycle matrix test still classified both, so "covers exactly the builtin tab set" failed on develop tip (classified 36 vs TAB_PATHS 34). TAB_PATHS/BuiltinTab is the authoritative builtin set and is correct; the test was stale. Drop the two stale entries; assertion stays an exact deep-equal against TAB_PATHS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: moon <stupidlybadadvice@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Shaw <shawgotbags@gmail.com>
Six quality-gate tests were red on develop, protecting nothing while every
branch inherited the noise. Each fixed with an evidence-based intent decision:
1-3. no-focus-ring-gate — the design system owns focus affordances globally:
- MessageSearchPanel: drop focus:bg-muted/60 + focus:outline-none (hover keeps
the pointer affordance).
- agent-orchestrator-room-view: the status-dot cutout ring-1 ring-bg becomes
border border-bg (same 1px bg-colored cutout, no ring utility).
- ContinuousChatOverlay edit-textarea (#10713, my surface): drop
focus:border-white/40; the base border-white/20 stays.
4. will-change-gate — xr-scene declared willChange on LAYOUT properties
(left/top/width/height), the exact anti-pattern the gate's doctrine calls
out (only transform/opacity benefit from compositor promotion; layout props
still reflow). Removed the ineffective hint.
5. view-lifecycle-matrix — the MATRIX carried stale rows: companion (feature
removed by #10434) and voice are no longer in BUILTIN_VIEW_IDS. Removed the
stale classifications; the per-view policy loop (74 cases) stays green.
6. widget-coverage — ratcheted the app-manifest floor 28 → 26 with receipts:
plugin-companion removed by #10434, plugin-elizamaker by c394453 — both
intentional deletions that shrank the set without the floor moving.
7. overlay-app-registry ×2 — the expected arrays listed feed before contacts
while the assertion sorts the received list; the 4-member set is identical,
only the expectation ordering was wrong. Alphabetized.
All 5 gate files green (93 tests); packages/ui typecheck exit 0; touched-
component suites (ContinuousChatOverlay + message-search, 109 tests) green.
Co-authored-by: lalalune <shaw.nicola.walters@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…load routes (#10434 follow-up) The 3D companion (VRM avatar + its scene background) was removed in #10434, but the plumbing that fed it was left behind: the POST/GET `/api/avatar/vrm` + `/api/avatar/background` routes, the client's uploadCustomVrm/hasCustomVrm/uploadCustomBackground/hasCustomBackground methods (upload* had no callers at all), and the startup existence probes. Nothing renders an uploaded model or scene background anymore, so those HEAD probes only produced 404 noise on every first-run/startup. - avatar-routes.ts: drop the four vrm/background handlers (keep the live Discord avatar cache route); drop the now-unused readRequestBodyBuffer + resolveStateDir imports and the unused req/json ctx destructure. - client-agent.ts: delete the four dead ElizaClient methods + their type declarations. - startup-phase-hydrate.ts: drop the hasCustomVrm/hasCustomBackground probes (and the resolveApiUrl import + two Date.now() render-time nondeterminism sources they carried), keeping the live `resolvedIdx === 0 -> setSelectedVrmIndex(1)` default-avatar fallback. Live built-in avatar selection (selectedVrmIndex / avatarIndex / getVrmPreviewUrl) and the content-pack asset path are untouched. Agent + UI typecheck clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…y probe (#9950, #10712) - #9950: the all-views aesthetic audit caught a crashed density probe with a zero-density placeholder ({borderDividerCount:0, whitespaceRatio:1}), which SILENTLY satisfied both the minimalism budget and the ratchet — a probe crash read as "perfectly minimal → pass". Now the failure is recorded as a densityProbeFailures finding (surfaced in the summary log + per-view markdown, same posture as hoverFailures) and the budget/ratchet are skipped when the probe failed, so a crash can never manufacture a pass. - #10712: force-add the four gitignored live-run streaming logs the evidence README cites (git add -f) so a fresh clone keeps the real-Cerebras run proof. - Strict-config typecheck fixes for the #10694/#10434 work: widen makeGlslConfig's uniforms param to the unknown-valued partial it already normalizes, and drop the two now-removed avatar setters (setCustomVrmUrl/ setCustomBackgroundUrl) from the useStartupCoordinator deps literal in AppContext (they left HydratingDeps with the dead-route removal). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ckground + interaction/QA de-larp (#9952, #10694, #10722, #10434) (#11083) * feat(app-control/ui): give background redo its real triggers — agent op + settings button (#10694) The #10771 slice landed redo in the store and the background:apply receiver, but nothing could emit it: the BACKGROUND action op union stopped at set|undo|reset and the settings controls only rendered Undo. - BACKGROUND action: add "redo" to the op union/param enum, REDO plan inference (checked before UNDO so "restore the change I undid" reads as redo), handler branch broadcasting {op:"redo"}, REDO_BACKGROUND simile, examples + docs. - Settings: Redo2 button paired with Undo, disabled without redo history, registered as agent-surface element background-redo. - Tests: 5 new action tests (21/21 green; plugin suite 1262 green), new BackgroundSettingsControls.test.tsx, BackgroundView redo case, fuzz/store seeds carry the redo fields. - background e2e: fixture mirrors useDisplayPreferences redo semantics; runner gains set→set→undo→redo round-trip (agent redo + UI redo) with refreshed screenshots/video evidence (11/11 assertions green). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ui): de-larp gesture e2e to real CDP touch — and fix the two real bugs it exposed (#10722) CI gated the synthetic-event gesture runner while the real-touch runner was excluded — the gated test was the larp. Converting to real input immediately surfaced two real production bugs the synthetic events masked: - TopicGroup: pointerup→onTap toggles, React swaps header↔pill, and the browser's synthesized click re-hit-tests onto the replacement element and toggles BACK — a tap/click on the header/pill was a visible no-op on phones and desktop. Fixed with the suppressClickRef + onClickCapture idiom (onClick kept for keyboard activation). - HomeScreen: the home scroller (overflow-y-auto ⇒ touch-action:auto) consumed horizontal touch pans itself, so home→launcher swipe could never fire from the widget area on real touch. Added touch-pan-y. Test/CI changes: - run-chatux-gesture-e2e: synthetic PointerEvent dispatch → shared touchTap/touchSwipe (real CDP touch) + real mouse flicks for the fine-pointer profile; assertions now match real behavior (vertical touch flick on a pan-y header scrolls, it does not collapse). - run-home-screen-e2e: the 3 mobile outer-rail swipes converted to real touch (+touchSwipeRight); desktop rail paging deliberately stays mouse (fine-pointer affordance, documented). - real-touch-gestures: settleMainThread (rAF round-trip) before every gesture — root-causes and fixes a pre-existing develop red where CDP touch raced a busy renderer main thread in conversation-swipe; deleted the zero-consumer touchPinch export (touchTap now consumed). - ui-e2e-gate.yml: gate test:home-screen-e2e (the honest runner is now in CI), fix the mislabeled chatux step, add output-home artifacts + src/testing path filters. Verified locally: chatux 7/7, home-screen full pass (~70 asserts), conversation-swipe ALL PASSED (5 failed on develop baseline before the helper fix), chat-sheet pass, 40/40 unit tests, Biome clean, workflow YAML parsed. Evidence re-captured in output-home/ and output/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(agent/app-core): de-larp #10712 — honest SSE contract + live streaming/thinking assertions with real Cerebras evidence The "local-inference vs cloud-resolved provider path" parity matrix was a labeled mock: the providerPath string only round-tripped through the test's own fixtures, and no code under test reads a provider branch (route → generateChatResponse → messageService → useModel is provider-agnostic; selection lives in core's model registry, which the contract test mocks by design). Collapsed to one honestly-named conversation-stream-sse-contract.test.ts pinning the full frame contract: thinking→streaming status ordering, ordered cumulative tokens, terminal done with fullText+thought, done-is-terminal, thought-never-in-visible-text. The real provider path is now covered where it belongs — the live e2e: - streaming-visible-text.live.e2e.test.ts records every SSE payload and adds a live frame-contract test (status ordering, monotonic fullText, done schema, conditional-but-contractual thought that must never leak into visible frames). - Fixed a real warm-boot race it exposed: /api/health flips ready before the deferred provider plugin registers — gate on /api/status.canRespond instead. - Hardened the lane: persistent models cache, 300s ready timeout, hang-proof teardown, ELIZA_STREAM_FRAME_DUMP evidence hook. Live evidence (hand-read, key-free logs) in .github/issue-evidence/10712-live-streaming-thinking/: 3 consecutive green runs against Cerebras gpt-oss-120b, 19 token frames growing 43→909 chars, real usage (2737 prompt / 229-234 completion tokens). Honest findings recorded: live reply arrives via running_action (REPLY callback), and thought was absent on this path in both dumped runs — the thought-carrying branch stays pinned deterministically. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(app): give the Her-minimal aesthetic gate teeth — ratchet with committed baseline (#9950) The minimalism metrics were computed but could never block: computeVerdict mapped every breach to needs-eyeball, and the signal was actually DEAD in real runs — the audit spec never passed viewportArea, so exceedsMinimalismBudget always returned false. A cramped view shipped green. Ratchet design (same idiom as ui-determinism-baseline): - aesthetic-minimalism-baseline.json records breaching views' metrics (83 entries / 33 slugs from a real full audit:app run on this tree). - Breaching view NOT in baseline → needs-work (BLOCKING). - Baselined view → blocks only when a metric regresses past baseline ±5% relative tolerance (live-DOM jitter absorber; real regressions move density far past 5% — recorded values cluster at 89-304 vs the 45 ceiling). Within tolerance stays soft. - Spec afterAll now throws unconditionally on ratchet failures (verified live: the empty-baseline run correctly failed on all 83). Corrupt baseline fails loudly via parseMinimalismBaseline. - Refresh: bun run --cwd packages/app audit:app:minimalism:update (regenerates from the latest report.json; prunes healed views so a re-breach counts as new). Also: collectHoverViolations no longer swallows hover failures with .catch(()=>{}) — failed probes are recorded as hoverFailures findings. First real run surfaced 8 previously-invisible probe failures. Verified: full audit:app run (348 passed, gate replay against committed baseline → ratchet-blocking=0, good=136/needs-eyeball=212); 55/55 audit unit tests incl. 12 new ratchet cases; Biome clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(ui): lock the floating chat to onboarding until an option is chosen, auto-collapse on completion (#9952 follow-up) During in-chat first-run the overlay was fully interactive: free text went to the real server before a runtime existed, and Escape/outside tap/pull-down could dismiss the onboarding sheet entirely (the e2e helpers relied on it). Now, while first-run is pending: - Composer locked: textarea disabled with "Choose an option to continue"; attach/mic/send (and push-to-talk) disabled; submitText hard-guarded against the prefill/dictation/slash side doors. The transcript choice widgets and OAuth/secret blocks stay interactive. - Send seam hard-blocked: sendActionMessage drops any non-__first_run__: value while firstRunComplete is false — nothing reaches rawSendActionMessage. - Collapse locked at one choke point plus drag gates: document/thread/ composer Escape, grabber tap + keyboard close, outside-tap detector, pull-down/drag/settle-free, header clear + launcher buttons, and conversation swipe are all no-ops; the sheet stays pinned at full. - Auto-collapse exactly once on the completion falling edge (wasFirstRunOpenRef), revealing home; ordinary sessions never collapse. Desktop ?shellMode=chat-overlay verified unaffected — that shell never mounts the overlay/conductor. - Dead code: finishRemote + normalizeRemoteTarget deleted; runFirstRunFinish narrowed to FirstRunFinishDraft (cloud|local); dead switchAgentProfile port unwired (live remote path is adopt-remote-first-run, untouched). Tests: new ContinuousChatOverlay.firstrun.test.tsx (8 — lock, no-collapse paths, choice interactivity, one-shot auto-collapse + unlock); conductor state-machine tests now drive the real handleFirstRunAction seam (local end-to-end + POST-once + re-tap idempotency, BYO-keys banner, cloud OAuth→agent-pick→bind→tutorial, cloud error→local retry, unmount unregistration); e2e asserts the lock + negative Escape mid- onboarding and auto-collapse + unlock in all five completion flows (14 passed incl. Pixel-7 touch lane); cloud-live spec picks tutorial:skip before its real turn since completion now gates the composer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(agent,ui): remove the dead custom-VRM / custom-background upload routes (#10434 follow-up) The 3D companion (VRM avatar + its scene background) was removed in #10434, but the plumbing that fed it was left behind: the POST/GET `/api/avatar/vrm` + `/api/avatar/background` routes, the client's uploadCustomVrm/hasCustomVrm/uploadCustomBackground/hasCustomBackground methods (upload* had no callers at all), and the startup existence probes. Nothing renders an uploaded model or scene background anymore, so those HEAD probes only produced 404 noise on every first-run/startup. - avatar-routes.ts: drop the four vrm/background handlers (keep the live Discord avatar cache route); drop the now-unused readRequestBodyBuffer + resolveStateDir imports and the unused req/json ctx destructure. - client-agent.ts: delete the four dead ElizaClient methods + their type declarations. - startup-phase-hydrate.ts: drop the hasCustomVrm/hasCustomBackground probes (and the resolveApiUrl import + two Date.now() render-time nondeterminism sources they carried), keeping the live `resolvedIdx === 0 -> setSelectedVrmIndex(1)` default-avatar fallback. Live built-in avatar selection (selectedVrmIndex / avatarIndex / getVrmPreviewUrl) and the content-pack asset path are untouched. Agent + UI typecheck clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ui,app-control): arbitrary programmable GLSL shader background (#10694) develop already shipped the versioned undo/redo half of #10694 (#10771 + #10960). This adds the headline feature it was missing: a real, arbitrary GLSL fragment-shader background mode. Renderer (@elizaos/ui/backgrounds): - shader-schema.ts — typed, clamped uniform contract (u_speed/scale/intensity/ seed), hexToRgb, and a static fragment-source safety gate (size bound + reject unbounded while/do loops). three.js-free so it unit-tests without WebGL. - shader-presets.ts — 5 self-contained GLSL ES 1.00 presets (aurora, lava, plasma, waves, nebula), bounded loops only. - ProgrammableShaderBackground.tsx — renders the fragment via raw three.js (already a dep; no new deps) with the full safety sandbox required for untrusted GPU code: compile-validate BEFORE showing, uniform clamping, a frame-time watchdog that falls back on a GPU stall, WebGL context-loss recovery, and a single static frame under prefers-reduced-motion. Any failure → onFallback and the color field paints instead — a bad shader can never white-screen or hang the app. - BackgroundConfig gains a `glsl` mode (source + presetId + uniforms); normalizeBackgroundConfig validates/clamps it and collapses a missing/hostile/oversized source to the color field on load. - AppBackground switches to the shader (keyed by source for a fresh attempt on replacement) with the color-field fallback; useBackgroundApplyChannel handles glsl payloads: preset set, explicit source, and live-shader uniform tweaks. Agent (plugin-app-control BACKGROUND action): - NL → shader plan: named presets (+ synonyms: molten→lava, ocean→waves, cosmic→nebula), a generic "animated shader" → default preset, and relative tweaks (slower/faster/brighter/dimmer/bigger/more-detailed) → uniform patch. Precedence: named preset > concrete color > tweak > generic shader, so "red brighter" stays red and "molten" beats a bare color. The action names a preset id + uniforms only; GLSL source stays in the renderer. Tests (all green): shader-schema (clamp/NaN/hostile/while-loop/hex), preset library integrity, ProgrammableShaderBackground fallback + no-crash-on-garbage, persistence glsl round-trip + hostile-source collapse, AppBackground glsl render + fallback, a 400-step set/undo/redo storm fuzz across shader/image/glsl asserting clamped-finite uniforms + history-cap + flag invariants, and BACKGROUND action preset/tweak inference + emit. Stories added for story-gate. ui + plugin-app-control typecheck + biome clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ui): gate the last onboarding-lock collapse seam + document the contract (#9952) Cluster-A audit follow-up. Two gaps on the onboarding lock: - The TUTORIAL_CHAT_CONTROL_EVENT handler (rest/reset → collapse, prefill → un-pill) was the one collapse seam outside the gated funnel. It is unreachable in the real flow (the tour starts only after completeFirstRun), but a stray or adversarial window event during onboarding could have collapsed the pinned sheet. Now the handler no-ops while firstRunOpen. New firstrun test drives rest/reset/pill/prefill during onboarding and asserts the sheet stays pinned FULL and the composer stays locked. - The lock contract had zero documentation. Documented the full contract (pinned FULL, composer + side-door lock, every gated collapse path, one-shot auto-collapse) in first-run/IN_CHAT_ONBOARDING_DESIGN.md + a README pointer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ui,app): strict-typecheck cleanups + de-larp the aesthetic density probe (#9950, #10712) - #9950: the all-views aesthetic audit caught a crashed density probe with a zero-density placeholder ({borderDividerCount:0, whitespaceRatio:1}), which SILENTLY satisfied both the minimalism budget and the ratchet — a probe crash read as "perfectly minimal → pass". Now the failure is recorded as a densityProbeFailures finding (surfaced in the summary log + per-view markdown, same posture as hoverFailures) and the budget/ratchet are skipped when the probe failed, so a crash can never manufacture a pass. - #10712: force-add the four gitignored live-run streaming logs the evidence README cites (git add -f) so a fresh clone keeps the real-Cerebras run proof. - Strict-config typecheck fixes for the #10694/#10434 work: widen makeGlslConfig's uniforms param to the unknown-valued partial it already normalizes, and drop the two now-removed avatar setters (setCustomVrmUrl/ setCustomBackgroundUrl) from the useStartupCoordinator deps literal in AppContext (they left HydratingDeps with the dead-route removal). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(spatial): de-larp the WEBXR "validated end-to-end" overclaim (#10722) Cluster-E audit: WEBXR_PLATFORMS.md claimed the production enterImmersiveScene() was "validated end-to-end against the IWER emulator … read back two textured quads from the session framebuffer", but no committed test opens an immersive session or reads pixels — the only test exercises the mocked navigator.xr availability contract — and enterImmersiveScene/enterImmersiveFromSpecs have no production caller. Removed the unbacked claim and added an explicit "NOT yet validated" section stating the immersive render path + hand-pose interaction are uncovered and the entry points are unused, so the doc no longer advertises a validation that doesn't exist. (The sibling tui-pty "vacuous skip" audit finding was a false negative: the real-PTY suite IS wired into CI via test.yml's "Real-PTY TUI smoke" step — `test:tui-pty` sets RUN_TUI_PTY=1 internally — and passes locally; left as-is.) Also bundles the four force-added #10712 live-run streaming logs the evidence README cites (were gitignored). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ui): drop the duplicate background redo button left by the rebase (#10694) The rebase onto develop merged both develop's original standalone redo button (#10960) and this branch's paired undo+redo layout into BackgroundSettingsControls — two "Redo background change" buttons sharing the same background-redo agent-element ref. Kept the paired-disabled layout (undo + redo shown together, redo disabled without redo history) and removed the superseded standalone. BackgroundSettingsControls.test.tsx 4/4 green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Shaw <shawgotbags@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
What
Removes two dead UI surfaces — the companion (VRM avatar) feature and the
dead steward wallet-policy UI — re-derived on top of current
develop. Theoriginal work lived on
chore/review-dirty-worktree-cleanup, but develop moved~207 commits since it diverged, so the companion commit was applied via
git cherry-pickand every conflict + every new companion reference thatlanded on develop in the interim was resolved/removed against develop's current
code (the branch does not cherry-pick cleanly on its own).
1. Remove the dead steward wallet-policy UI surface (
b69f5e7)PolicyControlsViewwas mounted nowhere (no route, no settings section, noapp-shell page), and the
steward/injectedslot wrappers always rendered nullbecause nothing assigns the boot-config steward slots. Only Storybook stories, a
stringly coverage entry, and a feature-surface showcase consumed it.
Removed:
PolicyControlsView(+ story), the wholepolicy-controls/module,components/steward/, theAppBootConfigsteward slots +Steward*Props/AppBootSteward*types inboot-config-store, the barrel re-exports, the agentexternal-modules.d.tsprop shims, the"settings/PolicyControlsView"coverageentry, the feature-surfaces showcase, the orphaned template stub exports, and a
stale design-doc listing.
Kept (real wallet/auth plumbing):
approveStewardTx+/api/wallet/steward-approve-tx,client-types-steward(
StewardApprovalInfo), the sharedStewardPolicyResulttype, theCloudWalletProvider"steward"value, and all cloud Settings sections.2. Remove the companion (VRM avatar) feature entirely (
6cc0193)Deletes
plugins/plugin-companion/and every reference across the runtime, app,UI, registry, build config, tests, and docs. The VRM avatar chat overlay, the
/companionview, the emote system (PLAY_EMOTE//api/emote), the companionShellView state, and all wiring are gone.
plugins/plugin-companion; drop its workspace dep + bundled-package /knip / docker-runtime / registry entries.
core-plugins,plugin-resolver,external-modules.d.ts,prompt-compaction,view-action-affinity,misc-routes, and every view-coverage / view-parity / tui-view /bundle-lifecycle test.
companionBuiltinTab+ its TAB_PATHS / redirect wiring, theShellView"companion"member,COMPANION_ENABLED,CompanionVrmPowerMode/
CompanionHalfFramerateMode, and theCompleteFirstRunOptions/launchCompanionOverlayfirst-run plumbing (re-derived against develop'scurrent
useFirstRunCallbacks/first-run-finish/use-first-run-conductor— these were added after the branch diverged).
lucide-react -> ../plugin-companion/node_modulestsconfigpath mappings, the companion route from
dev-route-catalog, theVITE_ENABLE_COMPANION_MODEgate oncharacter-select(now unconditional),and the
/api/emotePostEmoteRequestSchemacontract + test.PLAY_EMOTEfrom the action-coverage ratchet, rewrite the companion-emotescenario to GENERATE_MEDIA-only (
deterministic-media-actions.scenario.ts),and remove it from the prompts action spec + core action-docs.
mobile-plugin-views.ts(its only registration was the companionapp-shell page) + its
main.tsximport; drop companion from the optional-appshim, the storybook glob/order, and the
client-agentplayEmotemethod.view-interaction-coveragevisualCases count reconciled to 56.packages/app-core/scripts/ensure-avatars.mjsoff the now-deletedplugins/plugin-companion/public/eliza/plugins/app-companion/publicpathto
apps/app/public— the exact dir the surviving siblingprocess-vrms.mjswrites and the static server serves
/vrms+/animationsfrom. The survivingVRM content-pack /
BundledVrmAssetavatar subsystem is unaffected.Why it's safe — no dangling references
Final repo-wide grep over
packages/ plugins/ scripts/ skills/(source + config,excluding
dist/andnode_modules):plugin-companion/@elizaos/plugin-companion→ no source/config hits.Surviving matches are docs + generated artifacts only: historical analysis
.mdfiles (perf report, VIEW_E2E_FINDINGS, loadperf research,minimal-redesign catalog), checked-in e2e HTML snapshots
(
__e2e__/output/*.html), and generated*-report.json— none are live code.PLAY_EMOTE/playEmote→ no source/config hits except one generatedretrieval-ranking entry,
packages/shared/src/i18n/keywords/action-search.generated.keywords.json: "action.playEmote.request". Kept at develop's state on purpose: a fullregen of that file produces a 49,508-line unrelated drift (locale
reformatting + 200+ other actions' keyword drift over 207 commits); it is
retrieval ranking metadata only (the header says it is not an availability
check) and is regenerated by the legitimate
generate:action-search-keywordspipeline. This matches the original removal commit's handling. (The downstream
validation-keyword-data.{ts,js}files that also listplayEmotearegitignored — regenerated at build, not in this PR; they regenerate clean.)
companionBuiltinTabinpackages/ui/src/navigation+state→ CLEAN.PolicyControlsView/policy-controls/ steward dead-slot props → nosource hits (one stale storybook-README changelog line + generated report JSON
remain — historical/generated, not code).
False positives deliberately KEPT (unrelated uses of "companion"): the
browser-bridge companion (
plugin-browser/BrowserWorkspaceView), theplugin-phonePhone Companion page (id: "phone-companion",@elizaos/plugin-phone),mobile-companion/desktop-companionremote-capability providers, the electrobun companion window, the CSRF companion
cookie, and the AOSP-local-inference "companion plugin" Android FFI help string.
Verification
Scoped
tsgotypecheck of every affected package (missing type packagessymlinked from the parent monorepo's
node_modules— gitignored, not in thecommit; this is the documented light-install worktree):
Unit tests (vitest):
scenario-runner/deterministic-action-coverage: the PLAY_EMOTE ratchet andcompanion-emote → GENERATE_MEDIA scenario rewrite are correct (the test file has
zero companion refs; the rewritten scenario has 0 PLAY_EMOTE / 10 GENERATE_MEDIA),
but the suite can't run green in this worktree due to an environment install
gap —
Cannot find package 'jimp'(transitively imported via plugin-vision;absent from both worktree and parent node_modules). All 8 failures share that one
import error and are unrelated to companion; CI's full install runs it.
plugin-xr/xr-bundle-coverage: 2 size assertions fail because three unrelatedplugins' view
bundle.jsentries are 148-byte re-export shims in this worktree'svite build (real content is in hashed chunks) — an env/build-config artifact, not
a companion regression. The companion-relevant
xr-functional-paritypasses.audit:app status (the one remaining gate)
ATTEMPTED (during the paired PR A run on this same worktree). The visual-audit
pipeline builds every plugin view bundle,
@elizaos/core+@elizaos/shared,and transforms the app renderer (4632 modules), but the live-stack cannot boot
in this isolated worktree because its
install:lightprovisioning is missing along tail of renderer/build deps (
@elizaos/vitest-vite,lucide-react,webxr-polyfill,@date-fns/tz, …). This is the documented "worktree sharesparent node_modules" constraint. The screenshot + manual-review loop is the one
remaining gate and should run on a full-install checkout (CI) for the affected
surfaces — most relevant here: the launcher view-manager (which loses the
companion tile) and first-run / onboarding (companion-overlay launch removed).
No fabricated screenshots or verdicts are attached.
🤖 Generated with Claude Code