Skip to content

Promote develop → main: overnight UI/demo polish + money-gate + deploy fixes - #11918

Merged
NubsCarson merged 69 commits into
mainfrom
develop
Jul 3, 2026
Merged

Promote develop → main: overnight UI/demo polish + money-gate + deploy fixes#11918
NubsCarson merged 69 commits into
mainfrom
develop

Conversation

@NubsCarson

Copy link
Copy Markdown
Member

Promote develop → main (overnight UI/demo polish + fixes)

develop is 69 commits ahead of main (a544b23037). Promoting to get the overnight UI/demo polish and fixes to prod.

Notable payload

UI / demo polish

Money / security

Deploy / promote safety

Other

Pre-promote verification

Merge as a merge commit (promote convention). [cloud-frontdoor] approves the migrate gate + monitors the prod deploy (churn-proof via #11653).

[cloud-frontdoor]

NubsCarson and others added 30 commits July 3, 2026 04:10
…t 'Active' copy for coding-plan subscriptions

Two app-demo blockers in @elizaos/ui:

1) Sign-in first-click dead-end: with a stored-but-expired Steward JWT and no
   mounted Steward launcher (registerStewardLoginLauncher has zero production
   callers), handleCloudLogin entered the Steward branch, launchStewardLogin
   drained the stale token and threw 'the Steward login surface is not
   mounted' — first click errored, second click worked. Now the branch is only
   taken when it can complete on this click (usable stored token or mounted
   launcher); otherwise the stale token is drained and the same click falls
   through to the working device-code flow. New seam helper:
   hasUsableStoredStewardToken(). Launcher registration API untouched.

2) Misleading model-switch UI: selecting 'Claude Subscription' under
   Settings -> AI Model does NOT move main chat inference (by design:
   applySubscriptionProviderConfig only records it for the task-agent
   orchestrator; model.primary is set only for openai-codex), but the
   Intelligence summary row showed a bare 'Active'. Coding-plan subscription
   entries now read 'Active for coding agents' with a clarifier that chat
   replies keep using the Intelligence provider; the Codex plan (which can
   drive runtime inference) and Cloud/local keep the plain 'Active'.

Tests: useCloudState.steward-stale-login.test.tsx (fall-through, short-circuit
and launcher paths), cloud-steward-login.test.ts (usable-token rules),
ProviderSwitcher.active-summary.test.tsx (copy per entry category). All
mutation-checked (reverting each fix reds its test).
… + WS split/tile layout fields dropped

1. Help deep-links to a Settings section always landed on the generic hub.
   HelpView.navigate wrote the section into window.location.hash and then
   called setTab("settings"), whose pushState replaces the URL with the bare
   /settings path — clearing the fragment BEFORE SettingsView mounts and reads
   it (hash-nav mode clobbered it the same way with "#/settings"). Route the
   section through the sanctioned eliza:navigate:view `subview` channel
   instead (App.tsx maps it into SettingsView's initialSection — the same path
   the agent + slash-command flows use). "Open AI Model settings" now opens
   the ai-model section, not the hub.

2. The WS shell:navigate:view → DOM eliza:navigate:view bridge
   (startup-phase-hydrate) dropped the server's `views`/`layout`/`placement`
   fields, so an agent-driven split-view/tile-views action degraded to a
   single view: createNavigateViewHandler saw only viewId and laid out one
   pane. Forward the three fields with the same untrusted-input sanitization
   as the rest of the frame.

Tests: HelpView.test.tsx (4) pins the subview dispatch, the untouched
plain-tab + tutorial paths, and that the fragment stays clean;
startup-phase-hydrate.navigate-frame.test.ts (+3) pins layout-field
forwarding, sanitization, and single-view omission. Both mutation-checked
(reverting each fix reds its tests).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… salvaged from rate-limited lane (#11866)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
[codex] fix cloud api core stub duplicate export
Releases only carried GitHub's auto-generated source archives: release.yaml
created them with no `files:`, the npm tarballs it already builds were
discarded, and desktop installers only build for stable tags.

- Compile standalone `elizaos` CLI executables for macOS/Linux/Windows ×
  arch via `bun build --compile` (packages/elizaos/build-standalone.ts).
- Make the CLI compiled-aware: inside a bun binary `import.meta.url` points
  into the unreadable `/$bunfs` root, so getPackageRoot() now resolves
  templates/manifest/package.json next to the executable (isStandaloneBinary).
- Emit the real npm `.tgz` tarballs for the headline packages + checksums
  (packages/scripts/pack-release-artifacts.mjs), matching what npm publishes.
- release.yaml stages both + a combined SHA256SUMS and attaches them to beta
  releases and to stable production releases.
- Regression coverage: src/package-info.test.ts (unit) and
  scripts/standalone-smoke.mjs (compiles the host binary and drives
  version/info/create end-to-end).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(release): attach downloadable binaries to GitHub releases
feat: add elizaos-plugin-true402 to registry
…unctuation

ebd5203 (#11573) fixed the dead e.g/i.e entries by matching [\w.]+ but
anchored the match with (?:^|\s), which rejects abbreviations preceded by
quotes/parens/asterisks ('"Dr' / '(Mr') that the original \b handled — the
tts first-sentence early-emit path chopped mid-name ('He cited "Dr.').
drop the anchor: leftmost matching already captures the maximal trailing
[\w.] run and any other char or start-of-string delimits it.
…background (#11874)

AppBackground mounts at the shell root and is statically imported by App.tsx,
so its static import of ProgrammableShaderBackground pulled the whole vendor-three
chunk (~1.5 MB raw / ~320 KB brotli) onto the first-paint eager graph — even
though three is only needed for the opt-in GLSL programmable-background mode that
most sessions never use. Lazy-load ProgrammableShaderBackground so three is
code-split off the boot path; the plain ShaderBackground (same color) paints as
the Suspense fallback, so the swap is seamless when a user does select a GLSL
shader.

Verified: ProgrammableShaderBackground is the sole three consumer and AppBackground
was its only eager production importer (other importers are stories/tests, not in
the app bundle), so the chunk genuinely leaves the first-paint graph. Background
suite 54/54 green; biome + typecheck clean.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…fuse the next promote deleting BOTH copies (#11875)

#11845/#11847 raced the same missing-export fix into two spots, duplicating
the export. The dedup then ALSO raced: develop's #11857 deleted the first
copy while main's #11865 hotfix deleted the second. Relative to the promote
merge-base both sides now carry a different single deletion, so the next
develop->main promote auto-merges BOTH deletions cleanly and ships a stub
with ZERO runWithTrajectoryPurpose exports — re-breaking every Worker
deploy with the original "No matching export" build failure (verified by
simulating the merge: surviving export count = 0).

Fix: make develop's file byte-identical to main's (keep the first copy).
Identical content on both sides makes the promote merge trivially correct.
`bun run --cwd packages/cloud/api typecheck` passes with this tree.

Co-authored-by: NubsCarson <carson@nubs.site>
fix(core): extractFirstSentence abbreviation check when preceded by punctuation
…transcript

follow-up to 35e6a66 (#11712): looksLikeRawFieldTranscript fired on any
replyText: line anywhere in a reply, so a legitimate diagnosis that QUOTES a
leaked shouldRespond:/replyText: transcript (this repo's own daily debugging
workflow) was silently replaced at the send boundary by the QUOTED replyText
tail — the whole answer dropped with only a logger.warn. same hijack on the
text-mode path: parseMessageHandlerFieldTranscript claimed any prose with a
replyText: line and discarded every preamble line.

structural rule instead of contains-check: a raw envelope echo IS the message —
its first substantive line outside code fences is a known field line, with a
shouldRespond:/replyText: hallmark at top level. prose preamble or fenced field
lines mean the reply QUOTES a transcript and ships intact. parseFieldTranscript
now treats fenced field lines as value content, so a real leak whose replyText
quotes an envelope in a fence is no longer split at the quoted lines. the
text-mode claim is gated on the same detector, and the comment/code mismatch
(comment said routing AND reply field, code was OR) is resolved in favor of the
documented-correct OR (a lone shouldRespond: IGNORE echo must stay claimable).

genuine leak shape from #11712 (leading skeleton) is still detected, blocked,
and recovered — existing regression tests unchanged and green.
fix(core): stop transcript guard from rewriting replies that quote a transcript
The shell rendered a floating circular back-arrow button pinned to the
top-left corner of every routed and full-bleed view (ShellBackButton in
App.tsx). Because it was `fixed` at z-60 it floated over page content and
overlapped it: it covered the "GAMES & ENTERTAINMENT" heading on the Apps
gallery and sat on top of the "Character / Knowledge" breadcrumb — which is
itself the in-context back control.

Remove the global button and the now-dead code it required:
- delete ShellBackButton and its two mount sites (RoutedShellContent,
  FullBleedShellContent) plus the onNavigateBack prop and handleShellBack
  handler;
- drop the --shell-backnav-clearance CSS-var seam (set-side in App.tsx,
  consume-side padding in spatial/dom.tsx) that only existed to reserve top
  space for the fixed button;
- remove the now-orphaned ArrowLeft, CSSProperties, and goHome imports.

Pages that need a back affordance already render their own in-context control
(the preferred pattern, e.g. the Character subpages' "Back to Character hub"
button); everyone can also use browser/OS back. No page relied solely on the
global button for an in-app back path.

Tests: App.navigate-view-wiring asserts no global corner button mounts on app
routes; a new CharacterHubView.back-button test proves the Knowledge subpage
still renders its own back control. The obsolete clearance-seam guard is
deleted and the app-shell tap-target/occlusion specs are updated.

Closes #11876

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
the manual redirect loop rebuilt each hop init from params.init, re-sending
method+body on every hop. 624325a (#11693) stripped credential headers
cross-origin but left the body: a POST with a secret body that a compromised
server 302s to an attacker origin re-POSTed the full body there. it also
deviated from standard fetch, which the header fix cites: 301/302-POST and any
303 rewrite to a bodyless GET.

reproduce that rewrite (whatwg http-redirect fetch): 301/302 on POST and 303 on
any non-GET/HEAD method switch to GET, drop the body, and delete the
body-describing headers (content-type/length/...); 307/308 still preserve
method+body. fixes both the cross-origin body exfil and the GET-after-303
functional deviation.
fix(core): rewrite POST redirects to bodyless GET in ssrf fetch-guard
…nt files

the unborn-HEAD change-set scoop (3781312, #11605) unions
'ls-files --others --exclude-standard' into changedFiles, but a fresh
scaffold that runs npm install BEFORE writing .gitignore has thousands
of untracked node_modules paths (--exclude-standard has nothing to
honor yet). those flooded the 60-file cap, and because agent-written
tool paths were spread LAST (Set dedupe keeps first occurrence), the
flood evicted the agent's real files: 'what did you change' answered
with node_modules noise and diffs rendered junk.

- filter vendor/build dirs (node_modules, .venv, dist, ...) from the
  unborn-HEAD untracked scoop only; born-HEAD never scoops untracked
  and explicit tool-written paths are always kept
- spread agentWritten first so explicit edit/write tool calls survive
  the MAX_CHANGED_FILES cap
- drop the truncated garbage tail line when the ls-files listing was
  cut at maxBuffer (ENOBUFS)
fix(orchestrator): keep unborn-HEAD untracked scoop from evicting agent files
…successful retry as origin best result

35a9e81 (#11514) records completions on the task_complete early
returns so the spawn cap can relay them instead of re-spawning. but the
verify-retry handoff records a completion the router itself just judged
a failed build: the dead-url verification annotation makes that text
systematically longer than a clean success, so longest-wins
recordOriginResult keeps the failure and the successful retry's shorter
deliverable can never displace it. at the per-origin spawn cap tasks.ts
then relays the dead-url completion — planner-only verification
directive included — verbatim to the user as the final answer.

gate verify-failed completions (deadUrls > 0) out of origin-result
capture at every site: a known-failed build is not a relayable
deliverable, and the cap's honest "attempted N times" fallback covers
the nothing-clean case. the main-path record now routes through the
same helper so the key contract (#8875) and the gate live in one place.
… configured

When the user sends a message with no model provider configured, the chat
showed a forever "Waking Eliza…" spinner (and "Ask Eliza — waking up…"
placeholder) that never resolved.

Root cause: the server's computeCanRespond requires a registered TEXT handler,
so with no provider wired it reports canRespond:false FOREVER (not a transient
warm-up). The shell's readiness keys off canRespond, so phase stays "booting"
indefinitely — the boot spinner never clears even though the send came back
with the actionable no_provider gate.

Fix (client, packages/ui): derive an authoritative noProviderConfigured signal
from the latest assistant turn's failureKind === "no_provider" (server truth).
When set, useShellController auto-navigates to Settings once (setTab), and the
ContinuousChatOverlay suppresses the boot banner and swaps the "waking up…"
placeholder for a Settings hint — reusing the EXISTING in-transcript no_provider
error gate ("Connect a provider → Open Settings") as the error surface. Clears
automatically once a real reply lands (provider wired) and re-arms on a fresh
miss.

Tests: co-located vitest (jsdom) cover detection, single navigation,
idempotency, clear-on-recovery, re-arm, boot-banner suppression, and placeholder
swap — plus a Playwright before/after evidence capture over the real overlay.

Closes #11879

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(orchestrator): stop verify-failed completions from shadowing the successful retry as origin best result
…ub-agents via OPENCODE_CONFIG_CONTENT

gateway mode (86bc107, #11651) promised a child env dump contains no
raw provider key, but an opencode spawn broke the invariant twice over:
buildOpencodeSpawnConfig embedded the raw cerebras/opencode/cloud key as
provider options.apiKey inside the JSON assigned to
env.OPENCODE_CONFIG_CONTENT, and applyModelGatewayEnv — which runs after
and deletes only the seven named env keys — never touched it. the child
env carried the raw key AND the opencode child pointed its baseURL
straight at api.cerebras.ai / eliza cloud, bypassing the gateway.

- buildOpencodeSpawnConfig checks resolveModelGatewayConfig() first,
  before any credential read (env, runtime settings, or config-env —
  setting() falls back to all three, so env-key deletion alone could
  never fix this). in gateway mode it returns a gateway-pointed
  openai-compatible provider config: nothing raw to leak, and the child
  routes through the gateway instead of bypassing it.
- applyModelGatewayEnv enforces the stated invariant literally: raw
  values captured from the named keys are swept out of every remaining
  env value, so a composite carrier (a JSON blob like
  OPENCODE_CONFIG_CONTENT) that still embeds a raw key is dropped whole.
  fail-closed backstop for future merge steps.

off-mode behavior is byte-identical; a new off-mode test pins the legacy
direct-cerebras wiring. model-gateway-env.test.ts now exercises
agentType opencode — the untested gap that hid this.
fix(orchestrator): gateway mode leaked raw provider key to opencode sub-agents via OPENCODE_CONFIG_CONTENT
…pps earning inference markup (#11870)

A re-review BAN set review_status='rejected' but left monetization_enabled
true, and the creator-earnings path (deductCredits/reconcileCredits/
processPurchase) gates on that flag alone — only NEW paid charges checked
isAppMonetizationApproved. A rejected (prohibited-category) app therefore
kept collecting inference markup on every chat/generate-image/messages call
and stayed publicly usable, contradicting the invariant documented at
api/v1/apps/[id]/route.ts ("a rejected re-review DOES cut everything off").

- runAppReview: a rejection now flips monetization_enabled=false in the same
  transaction (pricing preserved; re-enable requires fresh approval via
  PUT /apps/:id/monetization). Composes with the create-time gate (#11828)
  and the restore gate (#11834/#11843).
- Earnings math derives its effective flag from isAppMonetizationActive
  (enabled AND not rejected) so rows persisted rejected+enabled before this
  fix earn nothing either; the draft re-gate deliberately keeps accruing per
  the documented grandfather DECISION.
- Real-PGlite ledger proof: approved+enabled earns 25% markup; a re-review
  ban (real runAppReview, deterministic pre-filter) revokes the flag and
  later calls earn ZERO; legacy rejected+enabled rows earn nothing (markup
  + purchase share). Red against pre-fix source, green with the fix.

Refs #11834 #11843. Closes #11870.

[cloud-security]
…ched boots

On a warm/cached load the app boots almost instantly, so the view transitions
loading -> none within a few milliseconds. StartupShell returned the full-screen
orange StartupLoading splash immediately for the "loading" kind, so those few ms
painted the splash and then ripped it away — a jarring flash.

Gate the loading splash behind STARTUP_SPLASH_DELAY_MS (220ms): render it only
once the loading state has persisted past the threshold. A boot that becomes
ready first never paints it. Error / pairing / bootstrap views stay immediate.

The delay uses an effect-based timer (useState + useEffect + setTimeout, cleared
on unmount / view-kind change) so no render-time clock/timer is introduced and
the audit:ui-determinism gate stays green. The startup-shell:first-paint mark now
fires only when visible startup UI actually paints (immediate views, or the
splash once its gate opens) — never on a null-rendering mount — preserving the
dedupe-by-name behavior. StartupLoading markup/roles/test ids are unchanged.

Closes #11883

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-reject

fix(cloud): review rejection revokes app monetization — stop banned apps earning inference markup (#11870)
NubsCarson and others added 13 commits July 3, 2026 07:33
…r gate, 5 coverage-ratchet drifts, deterministic #11030 deadlock guard (#11898)

packages/ui no-backdrop-blur-gate (#9141 battery): #11829's new
NotificationCenter reintroduced backdrop-blur-2xl/backdrop-saturate-150 on the
sheet/panel shell. Drop the GPU backdrop filter and carry readability with a
near-opaque base (bg-neutral-950/[0.87]) instead; refresh the committed
home-screen e2e artifacts (sheet + panel verified readable over the live
launcher in headless chromium, 10 screenshots + walkthrough video).

packages/app coverage/ratchet gates (5 reds, all real drift, no baselines
loosened):
- core-view-action-surface + view-switching matrices: track the automations
  core view (added by b07f3d9 without a surface owner) and the background
  settings subsection (#10994 registered it in SETTINGS_SECTION_META only) —
  AutomationsFeed + BackgroundSettings* are real agent-surface owners.
- route-coverage: plugin-birdclaw (#11385) declares a production collapsed
  gui/xr/tui view that was never added to the manifest ratchet. Wire it fully:
  manifest list, XR ratchet, runtime-plugin boot classification,
  gui+tui visual-matrix cases, tracked visual-review rows, manager-visible
  tile case, HMR lockstep probe, GUI interaction owners (its real
  plugin.test.ts + BirdclawView.test.tsx), and the ui-smoke stub
  (view registration + honest zero-key /api/birdclaw/status "not installed"
  state). Both birdclaw Playwright visual cases pass live (real BirdclawView
  bundle renders the setup card; audit JSON + screenshots reviewed).
- ui-smoke-coverage: scenario-pr.yml still hand-named the 10 pre-#11442
  voice-workbench spec filenames; point the slice at the renamed specs.
- view-interaction-coverage: the launcher owner declaration still claimed
  edit-mode/drag-to-reorder coverage that #11523's read-only launcher removed;
  declare what run-launcher-e2e.mjs actually proves now (no-edit long-press,
  tap-launch telemetry).

ios-local-agent-transport #11030 deadlock guard (both ui + app-core copies,
~1-in-4 flaky under a loaded suite): the 10s wall-clock Promise.race lost to a
CPU-starved event loop. Replace it with a deterministic detector — promise
assimilation invoking the hostile proxy's fabricated `then` proves the raw
Capacitor proxy crossed an await (the exact #11030 regression) and rejects
instantly with the descriptive error. Verified: reintroducing the raw-proxy
bug fails the test immediately with the #11030 message; fixed transport never
touches `then` (asserted).

Suites: packages/ui 544 files / 5532 pass; packages/app 37 files / 316 pass;
app-core transport suite 30 pass; typecheck green (ui, app, app-core); biome
clean on touched files.
…ch-action (#11853 sibling) (#11904)

The existing *-gate.test.ts files are all STYLE gates. Two production UI
bugs slipped past every one of them plus manual QA because they were
MECHANICS bugs (the pixels looked right, the interaction was dead):

  (a) drawer-not-scrollable: a clamped-height (max-h-[..vh]) + flex-col +
      overflow-hidden shell with no inner overflow-y-auto scroll body —
      taller content is dead-clipped and unreachable.
  (b) broken-swipe: a useHorizontalPager surface with no explicit
      touch-action — the browser default (auto) steals the horizontal pan
      and fires pointercancel, so the flick never commits on touch.

Adds a static (no-browser) vitest gate scoped to the real drawer/pager
surfaces (McpDetailDrawer, Launcher, HomeLauncherSurface). Comments are
stripped before scanning and the drawer scroll-body triad is checked
per-element (not per-file) so a comment mention or a decoy scroller can't
mask a regression. Self-tests plant each bug shape (incl. the exact
false-negatives codex flagged) and assert the detector fires.

Green against develop; fails on both planted real-bug shapes. This is the
MECHANICS gap not covered by #11868 (44px tap-target), #11877 (focus
order), or #11898 (test-red clearing). Enforcement is #11853's job.

— [sol-orch]

Co-authored-by: wakesync <shadow@shad0w.xyz>
fix(ui): dynamic view/overlay load failures show the recoverable card, never a blank screen
…a finish error

The in-chat first-run wizard could loop forever with no escape. On any
finish/provision error, `seedError` re-appended the runtime CHOICE
(`${message}\n\n${RUNTIME_CHOICE}`), so a persistent failure — e.g. the
"Not found" 404 from `POST /api/first-run` — re-offered the same runtime
question indefinitely with no distinct error surface and no way out. The
"Other / configure in Settings" provider pick made it worse: it ran a local
finish that hit the same 404 and re-looped instead of ever opening Settings.

This is the UX/navigation fix (the underlying 404 is a separate backend/env
issue, out of scope):

- Finish errors now seed a DISTINCT, non-looping recovery turn
  (`first-run:error:*`) with a human message and a dedicated
  `[CHOICE:first-run id=error]`: Try again (`error:retry`, re-runs the last
  runtime's finish), Choose a different way to run (`error:restart`, re-offers a
  fresh unlocked runtime CHOICE), and Configure in Settings (`error:settings`).
- "Other / configure in Settings" (`provider:other`) now opens the Settings tab
  (`setTab("settings")`) and exits first-run (`completeFirstRun("settings")`)
  via a shared `exitToSettings` helper, latched by `completedRef` so a
  double-tap can't flip the gate twice.
- Raw terse errors ("Not found", "Failed to fetch", …) are wrapped in a clear,
  human sentence via `finishErrorMessage`.

Local-success, cloud, and needs-cloud-login paths are unchanged.

Tests: updated the affected conductor tests and added coverage for the
persistent-404 no-loop + retry + Settings escape, the cloud `error:retry`
re-run, and `provider:other` -> Settings exit. Also hardened `beforeEach` to
restore default mock implementations (clearAllMocks keeps implementations, so a
leaked mockRejectedValue would poison later tests). Full src/first-run suite:
191 passed.

Closes #11882

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ebased

fix(ui): recover first-run finish errors without looping
…calibration-ceiling fix (#11373) (#11905)

* feat(voice): expose AEC delivery counters in window.__jniVoice.status() (#11373)

The JNI ambient pipeline (#11562) counts far-end playbackFrame deliveries and
tracks the last echo-cancelled batch ERLE, but the on-device harness status()
did not surface them — so on-device verification could not read the truthful
delivery counters the same way /api/voice/audio-frames/status exposes them for
the WebView pump path. Add playbackFramesReceived + lastEchoErleDb to
JniVoiceStatus.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(voice): raise echo-delay self-calibration ceiling to 500ms + reject cap-edge locks (#11373)

The Pixel 6a physical-device AEC capture (#11373 evidence) measured the real
WebView pump-path playback->mic delay at ~380-410 ms — beyond the previous
300 ms ECHO_CAL_MAX_LAG_SAMPLES search ceiling. The one-shot self-calibration
therefore locked a cap-edge lag (4765/4782 = ~298 ms at confidence ~0.32,
barely over the 0.3 gate) and pinned an ~85 ms misalignment forever; with 256
filter taps (16 ms) the NLMS then diverges instead of cancelling (echo-only
replay at the locked delay: converged-half -12 dB vs +3.3...+5.5 dB at the
true delay).

- ECHO_CAL_MAX_LAG_SAMPLES 4800 -> 8000 (300 ms -> 500 ms)
- reject locks within one frame of the ceiling (a cap-edge peak means the
  true delay is likely beyond the searched range; keep observing instead)
- calibration window 12000 -> 16000 samples for enough correlated overlap at
  large lags
- annotate the android platform seed with the measured device evidence
- tests: calibrates a 400 ms delay; refuses a cap-edge lock

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(voice): near-end (double-talk) seam in the AEC loop harness + physical-Android driver/analysis tools (#11373)

- aec-loop-harness: nearEndAudioUrl option (+ nearUrl deep-link param) — the
  double-talk near-end plays through the SAME AudioContext connected straight
  to destination, never through the playback tap: acoustically present at the
  mic, absent from the far-end reference. On-device findings that forced this
  shape: Android WebView HTMLMediaElement rejects data: URLs
  (NotSupportedError) and a second CDP-created AudioContext renders silently
  while the harness context holds the output stream (verified on Pixel 6a —
  near-end corr 0.014 in the mic, i.e. inaudible).
- android-physical-driver.mjs: physical-device variant of the emulator driver
  (wake/keyguard/pm-grant prep; volume via KEYCODE_VOLUME_* with read-verify
  because 'cmd media_session volume --set' silently does not apply on
  Pixel/Android 16; near-end via the harness seam instead of macOS 'say').
- measure-device-erle.ts: optional --delay override so a capture can be
  replayed at a sweep-found optimum, not only the device lock.
- sweep-delay-erle.ts / lag-trajectory.ts / near-end-preservation.ts: ERLE-vs-
  delay sweep, windowed lag-drift trajectory, and matched-filter near-end
  preservation over a device capture — all through the production classes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(evidence): Pixel 6a physical-device AEC acoustic-loop bundle (#11373)

Android target-device leg of #11373, captured autonomously over adb/CDP (no
human at the device — pm grant + volume keys + CDP cover every gate iOS could
not clear). Real speaker->air->mic loop through the production /api/voice/*
transport on a physical Pixel 6a (Android 16):

- transport: echoReferenceWired=true, playbackSamplesReceived=354880,
  lastPlaybackFrameAt advancing; mic capture is genuinely acoustic (0.021 RMS
  room floor -> 0.074 during playback)
- measured playback->mic delay: ~380-410 ms across five runs, wandering ~21 ms
  within a run (lag trajectory) — the finding that exposed the 300 ms
  calibration ceiling bug fixed in this branch
- echo-only ERLE: 0 dB AEC-off baseline; +3.3...+5.5 dB converged-half at the
  true delay (sweep), -12 dB at the pre-fix cap-edge lock (bug evidence kept
  as *-cal300ms-bug-*)
- double-talk: real overlapping second talker; near-end preservation +7.2 dB
  mean (not crushed; DTD holds) with the misalignment noise documented
- synthetic control at the measured delay converges to its noise-floor
  ceiling (+9.9 dB) — the measurement chain is sound
- screen recording, screenshot, logcat, mic characterization, full READMEs
  with honest limitations (WAV far-end because the fused lib fails to
  relocate on this build; same-loudspeaker near-end; uncontrolled room)

Refs #11373 (iOS rows remain open).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…11734, refs #11760) (#11908)

Hardware-lab acceptance row for the inference memory policy merged in
PR #11822, on real Pixel 6a hardware (serial 27051JEGR10034, MemTotal
5589MB -> CONSTRAINED, nCtx=4096, idleUnloadMs=300000, no debug
overrides), build 35f61d1 md5-verified on device after evicting a
foreign APK a sibling session had installed.

- 12.1 min foregrounded soak, 10/10 real local-inference turns
  (mobile-local-direct-reply, 52-58s warm, 95s cold first load)
- 4 pressure releases observed: PSS 2.16->0.28GB, GL mtrack
  1.60->0.05GB per release, MemAvailable ~0.7->2.0GB; released state
  held for the entire 7-min idle leg
- transparent reload after each release: 58s turn (~+4s vs warm)
- lmkd killed 48 processes across 27 other packages in the window;
  ai.elizaos.app killed 0 times, pid stable, exit-info before/after
  byte-identical (no new LOW_MEMORY of the main process)
- key device-exact finding: the CONSTRAINED pressure lever
  (availMem < lmkThreshold+512MB = 728MB) structurally preempts the
  300s idle lever on this device profile; idle firing remains covered
  by the emulator soak + JVM unit tests

Verdict: PASS.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Exclude the global chat overlay from per-view occupancy metrics and compact the Character overview Experience summary.
Supersedes #11893. Closes #11892.\n\nValidation:\n- bun run --cwd packages/ui test -- --run src/state/ src/first-run/\n- bun run --cwd packages/ui typecheck\n- bunx @biomejs/biome check changed files\n- git diff --check origin/develop...HEAD\n- bun run --cwd packages/app audit:app
Supersedes #11867. Closes #11864.\n\nValidation:\n- bun run --cwd packages/core prebuild\n- bun run --cwd packages/ui test src/components/shell/ConnectionFailedBanner.test.tsx\n- bun run --cwd packages/ui typecheck\n- bunx @biomejs/biome check changed files\n- git diff --check origin/develop...HEAD && git diff --check\n- node packages/ui/scripts/reconnecting-shift-proof.mjs\n- bun run --cwd packages/app audit:app
…#11912)

Captures the four adb-measurable rows of #11734 on real Pixel 6a hardware
(policy build 35f61d1, eliza-1-2b Q4 via the bionic Vulkan host), leaving
only battery/power-meter + iOS rows genuinely lab-gated:

- TTFT distribution: 12 identical-length turns — p50 54.3 s / p90 57.7 s
  (warm-only p50 54.3 / p90 54.5; 3/12 transparent post-release reloads at
  57.3-58.8 s). Client TTFT == full-turn latency: the fast path emits the
  whole reply as one SSE chunk and the native stream decodes the full
  256-token buffer per turn regardless of maxTokens.
- Isolated prefill: 8-rung exact-token ladder regressed on the device-logged
  in-lock window — marginal ~5.1 tok/s, batch-quantized by
  ELIZA_LLM_N_BATCH=128 (flat <=127 tok, +~23 s per extra batch); effective
  decode <= 7.9 tok/s. Cross-check llama-bench pp128 3.88 / pp512 8.86 /
  tg128 6.78 tok/s. The historical "4.8 warm decode" is a combined-window rate.
- Thermal timeline: 91.4 min at 15 s cadence — Thermal Status 0 throughout
  (no OS throttling), peak skin 42.9 C / TPU 68 C; battery fell 76->70%
  while USB-powered (bounds, does not replace, the power-meter row).
- eliza-1-4b tier: the predicted finding — lowmemorykiller kills the
  foreground app DURING the 2.95 GB Q4_K_M load (ApplicationExitInfo
  reason=3 LOW_MEMORY, RSS 3.4 GB, GL mtrack 3.22 GB, MemAvailable
  1670->206 MB in 22 s). The pressure-release policy cannot fire mid-load;
  minRamGb: 6 is confirmed real on this 5.59 GB device. 2b restored and
  verified afterward.

Also documented: the shipped device-tier eliza-1 GGUFs (2b/4b "-256k") are
Qwen3.5-architecture, not Gemma-4 (only the 8 GB "-128k" artifacts carry the
cutover), and an autonomous background agent job can hold the resident-model
lock longer than its own period, starving chat turns.

budgets.json measuredBaselines + BASELINE.md updated (non-gating; gate
budgets stay null pending multi-run stability).

Refs #11734

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Supersedes #11887. Closes #11886.\n\nValidation:\n- bun run --cwd packages/core prebuild\n- bun run --cwd packages/ui test src/styles/overscroll-behavior.test.ts\n- bun run --cwd packages/ui typecheck\n- bunx @biomejs/biome check changed files\n- git diff --check origin/develop...HEAD && git diff --check\n- bun run --cwd packages/app audit:app\n\nNote: physical macOS two-finger trackpad verification requires a rebuilt macOS Electrobun app and could not be exercised from this Linux/headless environment.
@greptile-apps

greptile-apps Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Too many files changed for review. (459 files found, 100 file limit)

Bypass the limit by tagging @greptile-apps to review.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c20b1736-46c7-48fe-ae9a-2edf140fa5c3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

LifeOps Benchmark — eliza

Run ID: lifeops-eliza-28668359773

LifeOps Benchmark

Model: gemma-4-31b
Judge: claude-opus-4-7
Scenarios: 25
pass@1: 0.000
pass@k: 0.000
Total cost: $0.0000

Full artifacts: see the lifeops-run-eliza-28668359773 upload on this run.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

❌ PR title does not match the required pattern. Please use one of these formats:

  • 'type: description' (e.g., 'feat: add new feature')
  • 'type(scope): description' (e.g., 'chore(core): update dependencies')
    Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert, release

@claude

claude Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Claude encountered an error —— View job


I'll analyze this and get back to you.

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

LifeOps Benchmark — hermes

Run ID: lifeops-hermes-28668359773

LifeOps Benchmark

Model: gemma-4-31b
Judge: claude-opus-4-7
Scenarios: 25
pass@1: 0.360
pass@k: 0.360
Total cost: $0.0000

Full artifacts: see the lifeops-run-hermes-28668359773 upload on this run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants