Skip to content

release: promote verified staging a3b14c9 to prod - #17113

Merged
lalalune merged 104 commits into
mainfrom
develop
Jul 23, 2026
Merged

release: promote verified staging a3b14c9 to prod#17113
lalalune merged 104 commits into
mainfrom
develop

Conversation

@lalalune

Copy link
Copy Markdown
Member

Promotion: staging-verified a3b14c9 → prod

Authorization: Shaw directed this promotion on 2026-07-23 (session goal): "make a PR from develop to main, merge, fix any issues and make sure prod is updated with latest."

Commit: a3b14c9433eb4d542b5a9343240ae09f339e6884 (exact develop tip at PR creation; staging deploys track every develop push).

Content: 101 commits / 299 changed files on top of the prior promote anchor 9f4dbdb (#17042, promoted to prod earlier today). Carries: the chat duplicate-delivery cluster (#17037, #17049, #17057), warm-pool key-push hardening lineage (#17066#17078#17083#17086), nav/not-found/loopback set (#17021, #17038, #17054, #17055, #17062), lifecycle-safe renderer services + LifeOps activity capture (#17056), unsafe YieldSignal registry removal (#17103), agent vmForks mock-leak fix (#17099), adm-zip lockfile/audit fixes (#17085, #17087, #17089), account-pool ratchet fix (#16514), and the develop-green lockstep fixes (#17069).

Migrations: none — zero migration/drizzle files in the promotion diff; schema-safe revert.

Rollback anchor: main@c97a73bfe84b0c191249e9397f87432812d091f5 (current prod tip; no cloud-DB migrations ride along).

Merge method: MERGE COMMIT per promotion pattern (not squash).

Guardrail: no wrangler.toml changes in the diff — prod billing/financial vars untouched; platform-only promotion.


Evidence

Promotion PR: each constituent PR carried its own reviewed evidence at merge time (per Definition of Done). Rows below link representative real artifacts from the promoted content.

  • Develop-push CI on the exact promoted head (a3b14c9): Windows CI · Dev Smoke · gitleaks — and this PR's own required lanes (test / build / dev-startup / lint-and-format) execute the full backend suite on the merge ref.

lalalune and others added 30 commits July 16, 2026 23:02
The type-safety ratchet was retired repo-wide in #16930 (script and
baseline deleted on develop). Deleting the branch's copy resolves the
modify/delete conflict so the branch can merge cleanly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resolves the type-safety-ratchet-baseline.json modify/delete conflict
(the ratchet was retired in #16930; the branch dropped its copy in the
previous commit). All four code files merge cleanly.

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

The develop-pr-gate per-file changed-coverage floor (50%) reports
packages/agent/src/api/accounts-routes.ts at 49.00% on this PR. Extend
the changed test file with real-branch cases: ensureSubscriptionCli
install/cooldown/not-on-PATH via its injected deps, the OAuth
unsupported-provider + status/submit/cancel session guards, the
coding-plan probe on test and refresh-usage (401 -> needs-reauth), and
the pool-driven + inline-fallback usage refresh paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix(accounts): remove broker fallback regressions
* feat(os): typed intent vocabulary for chat/voice/transcription routing

The one structural intent contract (eliza.os-intent/v1) unifying how chat,
voice, and transcription are launched across iOS/Android/desktop entry points.
Discriminated-union intents with declared prerequisites, targets, auto-start
gating, and typed outcomes. Behavior derives from structural fields only, never
prompt/transcript text.

Refs #16441

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(os): routing authority, dedupe store, decoder, executor

- router.ts: structural routing authority (stale/duplicate/degraded/blocked/
  consent-required/routed), consent-gated auto-start, prerequisite checks.
- dedupe.ts: clock-injected stable-id idempotency store with TTL + snapshot/seed
  for restored-session/crash recovery.
- decode.ts: J3 boundary decoder + deep-link and legacy-launch adapters mapping
  the free-form native action string to typed intents once.
- apply-command.ts: exhaustive executor over the narrowed one-controller surface.

Refs #16441

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(os): full intent-routing case matrix + biome formatting

76 deterministic vitest cases across contract/dedupe/decode/router/apply-command:
invalid/stale intents, locked device, missing permissions, auth expiry,
background/foreground, consent gating + reversibility, concurrency over a shared
store, duplicate-start prevention across redelivery paths, and crash recovery via
snapshot rehydrate. Exhaustive command executor now fail-fast throws on an
unhandled kind.

Refs #16441

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(os): end-to-end pipeline test + ./os-intent subpath export

Proves a real native elizaos:// launch link flows decode → route → drive the ONE
controller, and a redelivered link drives it exactly once. Exposes the module via
the ./os-intent package subpath (mirroring sibling #16440's ./native-transcript).

Refs #16441

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Shaw <shawgotbags@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
fix(release): include simple views in npm cohort
…top new unpriced positions swallowing deltas (#17039)

Two P1s in the balance-delta producer merged via #16956 (issue #16943):

1. The sample fingerprint folded per-position price coverage into a single
   opaque list, so ANY new unpriced position (spam airdrop) changed the
   fingerprint and silently re-baselined — permanently swallowing a
   concurrent material move in priced holdings. Coverage is now tracked
   per position and only priced<->unpriced flips on positions BOTH samples
   hold re-baseline; positions unique to one side fall through to the
   normal delta comparison.

2. The baseline carried no wallet address, so importing a different wallet
   cross-compared the old wallet's total against the new one's and
   fabricated a material-delta notification. The baseline now records a
   wallet identity key (sorted per-family addresses, EVM lowercased) and an
   address change on a retained family resets the baseline cleanly.

Legacy baseline rows (pre-walletKey schema) fail the shape check and are
discarded — one designed silent re-baseline on upgrade, never a comparison
against a row whose wallet identity is unknown.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(ui): resolve view-id navigation from registry paths

* chore: refresh PR checks

* chore: refresh evidence rows

---------

Co-authored-by: shadow <shadow@shad0w.xyz>
* ci: per-SHA staging concurrency groups so a gate-parked run cannot squat newer develop deploys

Since the 2026-07-16 required-reviewers rule on the staging environment,
push-event staging runs park in status=waiting at the env gate and a
waiting run holds the shared cloud-cf-deploy-v4-staging group. One
unapproved run wedged every newer develop push into pending/zero-jobs
(Jul 22 R10/R11, Jul 23 recurrence; misdiagnosed as runner starvation).

Fix: group staging push runs per head SHA. Waiting runs can no longer
block group admission for newer pushes; nothing in-flight is ever
cancelled (cancel-in-progress remains PR-only). Deploy-step
serialization is unchanged via the job-level per-env groups
(cancel-in-progress: false) and the #14083 freshness guard still skips
stale-SHA clobbers. Production keeps the shared queue-never-cancel
group untouched.

needs-human-review [sol-orch]

* ci: factor staging admission and Pages artifacts

---------

Co-authored-by: Shaw <shawgotbags@gmail.com>
…17041)

Native function-calling envelopes carry no top-level completed field, so
tryGateEvaluator's "planner declared the turn incomplete" veto was
structurally inert on the native lane — a sequential multi-op request could
be truncated after its first turnComplete-returning action (#17034).

Every native tool schema now accepts a reserved optional eliza_turn_scope
enum ("final" | "more_work_pending"). parsePlannerOutput folds the
declarations into a first-class completed signal (JSON-lane top-level
completed still outranks it), strips the argument before dispatch so no
action handler ever sees it, and the loop consumes the folded signal.
Absence preserves pre-#17034 behavior exactly; only an explicit
more_work_pending vetoes the gate, mirroring the JSON lane.

Fixes #17034

Co-authored-by: Shaw <shawgotbags@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Shaw <shawgotbags@gmail.com>
Co-authored-by: Shaw <shawgotbags@gmail.com>
…te WS echo duplicates

The active web chat received the same assistant reply twice: once streamed
into the optimistic temp-resp-* bubble via /messages/stream, then again as a
proactive-message WS broadcast of the persisted memory under a different
server UUID, which the id-only dedupe in the proactive handler appended as a
second bubble.

Protocol fix (root cause, replaces the rejected text-match suppression from
#17009):
- server: the terminal SSE done frame now carries messageId, the durable
  persisted assistant memory id. The streaming route pre-mints the id before
  emitting done and hands it to the deferred persist; action-callback turns
  that already persisted mid-turn reuse that memory's id.
- client: on done, the streamed temp-resp-* bubble is swapped to the
  persisted id in place (useStreamingText complete mod), collapsing any
  already-appended echo bubble with the same id.
- proactive-message handler now reconciles by id (update in place or no-op)
  instead of append-unless-same-id; genuinely new proactive messages
  (different id) still append.

No text matching anywhere; identity flows through the protocol.
…16970 #16951) (#17047)

* docs(evidence): retire remaining live .github/issue-evidence instructions (#16948 residuals)

plugin-meetings README gets the inline-PR artifact wording plus the managed
evidence-and-e2e-mandate markers (mirroring PR #16948's plugin-trajectory-logger
fix); the accounts-ui e2e README now documents the path the harness actually
writes (test-results/evidence/10722-accounts-ui-e2e/). The repo-wide sweep also
repoints the remaining live run instructions (voice matrix, startup trace,
mobile-resource lab normalization, voice workbench PR bundle, benchmark
review-package, device lifecycle matrix) at their real gitignored output dirs,
and stamps the coding-capability MASTER_PLAN with an evidence-location note so
its pre-retirement work items cannot re-instruct committed evidence. Fixture
paths in check-pr-evidence.test.mjs and history/research docs describing the
retirement are intentionally untouched.

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

* fix(shared): never let ELIZA_TTS_DEBUG go silently dead under strict LOG_LEVEL (#16958)

The server ttsDebug sink emitted at a fixed info level, so LOG_LEVEL=warn or
error filtered the diagnostic out even though the operator explicitly opted in
via ELIZA_TTS_DEBUG — the exact silently-dead-diagnostic defect #16347 existed
to kill. The sink now emits at info normally and escalates to the logger's
active threshold (warn/error/fatal) when LOG_LEVEL is stricter, so emission is
guaranteed at any level. New suite pins LOG_LEVEL=error at logger init and
asserts delivery through the real listener stream (which only fires for
entries that passed the level gate).

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

* fix(scenario-runner): pin the deterministic twin's non-owner tier to ADMIN (#16963)

The catalog README described the mutation-wall refusal fixture as a
USER-granted non-owner, but the twin actually whitelists the guest as a
connector-admin (ADMIN) — and nothing pinned that resolution, so a silently
broken whitelist stamp would degrade the guest to GUEST and the refusal would
still pass for the wrong reason (any non-owner is refused). The scenario now
probes the real roles.ts resolution (checkSenderRole) both at seed time and in
a finalCheck that runs before cleanup clears the whitelist, failing hard on
any tier other than ADMIN; the README states the real tier. Negative-tested:
emptying the whitelist fails the seed with 'expected the whitelisted guest to
resolve as ADMIN, saw GUEST'.

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

* fix(ci): per-cell FFI skip accounting; drop stale ABI v12 JSDoc (#16970)

bootFusedFfi's one-line JSDoc still said '(ABI v12)' while the function pins
the loaded library to ELIZA_INFERENCE_ABI_VERSION (currently 14) — the comment
now names the constant instead of a hardcoded version that drifts. The fused
FFI-lane guard in voice-live-e2e only tripped on '0 pass' or '12 skip', so a
partial skip (some matrix cells skipped, the rest passing) sailed through the
'nothing skipped' claim; it now fails on ANY nonzero skip count. Guard grep
verified against all four bun-test summary shapes (all-pass, partial-skip,
full-skip, explicit '0 skip' line).

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

* fix(ui): fail the builtin-view ratchet when observed sites drop below the pin (#16951)

maxMutationSites only ratcheted upward: when a refactor removed local mutation
sites the freed headroom silently accrued, worst on multi-file aggregates like
automations (71), where one slimmed file could absorb dozens of future
local-only mutations unnoticed. The validator now emits a stale-baseline
finding whenever observed < maxMutationSites (suppressed while a source file
is unreadable — the partial count is meaningless next to the missing-source
finding already emitted), so every drop force-pins the count down. All 25
current baseline entries already sit at their exact observed counts, verified
by recount. #17016's my-apps entry arrived via rebase onto develop.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Shaw <shawgotbags@gmail.com>
Follow-up to #17019, which covered agent-switch, model-switch, view
uninstall, and rollback re-register. A caller sweep of plugin-app-control
found four more Node-side loopback fetches that still crossed the
token-protected local API boundary with only Content-Type:

- settings defaultRouteFetch: only /api/views/* paths got the bearer;
  /api/config, /api/permissions/*, /api/wallet/*, /api/backups,
  /api/update/*, /api/training/* did not. Attach it unconditionally.
- background defaultGenerateImage (/api/background/generate-image).
- client/api.ts AppControlClient (/api/apps/installed, runs, launch, stop).
- verification-room-bridge live-load POSTs
  (/api/plugins/load-from-directory, /api/apps/load-from-directory).

All reuse the same createViewsRequestHeaders() seam from #16836: canonical
ELIZA_API_TOKEN with the legacy ELIZA_API_AUTH_TOKEN fallback, header omitted
entirely when no token is configured, so open local dev is unchanged.

Integration coverage extends the real-TCP bearer-protected test to the
non-views settings route, generate-image, and the app-control client, and
keeps the no-token-in-URL/body leak assertions over every captured request.

Co-authored-by: 0xSolace <shadow@shad0w.xyz>
…nts fetch

a stage-1 compose fans providers out concurrently and pays the same db
round-trips several times over: getRoom 4x (recent_messages / character /
platform_* / world) and 3 overlapping newest-first room messages-scans
(recent_messages at conversationLength, facts at 10, attachments at <=50).
on a single-threaded store (pglite wasm) those duplicates serialize — their
latencies sum instead of overlapping — and set the composeState wall.

three structural changes, no prompt-content change:

- runtime.getRoom now goes through a short-ttl (1s), in-flight-shared
  promise memo (same shipped pattern as identity-clusters.ts); every room
  mutation wrapper invalidates the key, so correctness never leans on the
  ttl in-process.
- runtime.getMemories coalesces the exact compose-shape messages-scan
  (newest-first, room-scoped, no filters) into one superset fetch sliced
  per caller; slicing is provably identical to a direct limit/start-bounded
  query because a start bound is a pure suffix predicate on the newest-first
  order. createMemory/createMemories bust the room key, making the
  intake-then-compose sequence self-enforcing: a stale window can never
  drop the message being answered. any other query shape passes through
  untouched.
- the attachments provider evaluates the message-side half of its render
  gate before fetching conversation history; a text-only turn with no
  attachment reference skips the history scan + access-context resolution
  entirely (it was the largest single provider wall on simple dms).

measured on a repeatable harness (real AgentRuntime + in-memory adapter
mirroring plugin-sql ordering, real recent_messages/attachments/facts
providers, serialized 20ms per db query modeling pglite): simple-dm compose
drops from 7-8 serialized round-trips / ~165ms to 3-4 / ~71ms.
- ensureConnection writes rooms via adapter.upsertRooms directly, bypassing the
  room-read memo invalidation; invalidate params.roomId after the standalone call
  so a just-created/updated room is never served as a memoized null or stale Room.
- coalesceRoomMessagesScan now bypasses the memo whenever includeEmbedding is set
  (previously only rejected === false), since the coalesced superset fetch omits
  the flag and would return embedding-less/embedding-bearing rows to a caller that
  pinned it either way.
- test: ensureConnection invalidates the room memo (exercises the upsertRooms
  wrapper-bypass path the review flagged).
Fixes #16997. Validates and reconstructs provider tool-call SSE incrementally, rejects divergent consolidated payloads, preserves protocol-valid usage frames, and adds exact-head live Cerebras evidence.
gpt-oss-120b remains an available option everywhere it is served (model
catalogs, Groq-hosted defaults, id-handling tests, pricing tables, quirk
branches) but no longer poses as the go-to default on lanes where the
product already standardized on gemma-4-31b:

- plugin-elizacloud package.json pluginParameters metadata claimed
  'Default: gpt-oss-120b' for nano/small/medium while the runtime resolvers
  (getSmallModel/getNanoModel/getMediumModel) actually default to
  DEFAULT_ELIZA_CLOUD_TEXT_MODEL = gemma-4-31b; align the declarations.
- Cerebras-lane defaults still pinning gpt-oss: proactive-greeting live
  script, SETTINGS live-e2e acceptance model, multitask-bench openclaw
  factory fallback, app-eval cerebras fixtures, openclaw-adapter cerebras
  config primary.
- Stale docs/workflow comments claiming gpt-oss is the Cerebras default
  (lifeops bench workflows, HyperliquidBench docs) — the lifeops large tier
  and HyperliquidBench cerebras path have resolved to gemma-4-31b since the
  tier registry landed.

Groq lanes keep openai/gpt-oss-120b: no gemma-4 is served on Groq, so it is
the available option there, not a stale default.

Co-authored-by: Shaw <shawgotbags@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…7055)

* fix(ui): designed not-found state for unknown /apps/<slug> routes

Navigating to /apps/<slug> for a slug nothing serves used to fall through
every router layer (registered pages, remote views, app runs) and silently
render the healthy launcher grid — a UI three-state violation (a failure
rendered as healthy) that is exactly how #17020 shipped invisible: the dead
deep link looked like a working page.

AppsPageView now receives the routed slug and, once the view registry has
settled with no claimant (no routable view at /apps/<slug>, no app run whose
slug matches), renders a designed AppRouteNotFound state: the literal dead
path in monospace, a plain statement that nothing is mounted there, a
"Browse apps" recovery action, and — when a routable view's id matches the
slug but lives at another canonical path (stale bookmark) — an
"Open <label>" action to that path. While the registry fetch is in flight
the grid still renders, so a cold deep link never flashes not-found before
the upstream registry claim can land. A structured logger.warn fires once
per unknown slug as the observable signal.

Ratchet baseline covers the two navigation-only controls (no domain
mutation to twin); tests cover the claim-resolution matrix, the component's
real history-seam navigation, and the mounted-App navigate-view path.

Closes #17033

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

* fix(ui): harden /apps/<slug> not-found gate — error state, claim grace, broken entries, i18n, hash nav

Review fixes on the #17033 not-found state:
- a failed registry load renders the grid and never warns (asserting
  "nothing mounted here" requires a settled successful registry read)
- a ~1.5s grace window absorbs the idle-deferred registerAppShellPage /
  async plugin-claim race before a settled-unclaimed slug is asserted dead
- available:false registry entries never claim the slug and are never
  offered as the recovery CTA, so broken installs surface as not-found
- appRouteNotFound.* strings translated across all eight locale catalogs
- recovery buttons follow the hash-navigation idiom on file:/appWindow
  surfaces where pushState is never observed by the router
- test headers converted to the prose-block doctrine

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

---------

Co-authored-by: Shaw <shawgotbags@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Shaw <shawgotbags@gmail.com>
…nt (#17054)

* fix(cloud): emit client-resolvable view ids from shared-tier nav intent

The Tier-0 shared-runtime nav fast path kept its own matcher-id -> label
table and emitted the raw matcher id as values.viewId in the VIEWS
handoff. Two ids drifted from the client's routable registry: "wallet"
(the builtin tab is "inventory"; TAB_PATHS inventory -> /wallet) and
"help" (no Help surface exists anywhere). The client resolves viewId
against its registry (PR #17021) and an unresolvable id lands in the
not-found fallback, so "open my wallet" produced a confident "Opening
Wallet for you." reply followed by a silent launcher grid.

The vocabulary now lives in @elizaos/shared/views/shared-nav-targets as
matcher id -> { viewId, label }, translating wallet -> inventory and
omitting help entirely so that utterance falls through to the normal LLM
turn. shared-nav-intent consumes the table (pure data, worker bundle
stays lean), and a cross-package contract test in packages/ui pins every
emitted id to either a builtin shell view at its canonical path or a
known plugin-declared view, failing by name on any future drift.

Closes #17032

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

* fix(shared-nav): drop AOSP-only camera target, fs-pin plugin views, correct #17021/#17033 claims

Adversarial-review fixes for the shared-tier nav vocabulary:

- Remove "camera" from SHARED_NAV_TARGETS: the camera view is an
  AOSP-fork-only native surface; on the clients a Tier-0 shared agent
  serves (web/desktop/iOS) /camera renders the ViewUnavailableFallback
  launcher grid. Camera utterances now fall through to the LLM turn
  (AOSP devices run dedicated local runtimes whose real VIEWS action
  handles camera). Tests updated on both sides.
- Correct overclaiming comments: PR #17021 resolves emitted ids against
  the client registry, but its unresolved fallback is still a blind
  /apps/<id> navigation; the designed not-found render for unclaimed
  /apps/<slug> routes is #17033.
- Replace the self-referential PLUGIN_VIEW_TARGETS map with an fs-backed
  pin: the contract test reads each declaring plugin source from the
  monorepo and asserts it still declares both the literal view id and
  canonical path, so a plugin rename fails the test instead of silently
  reintroducing the drift class.
- Rename the unroutable-id sweep to what it actually asserts and extend
  it to "camera".

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

---------

Co-authored-by: Shaw <shawgotbags@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Shaw and others added 18 commits July 23, 2026 14:08
fix(ci): restore develop merge protections removed by #17077
#17084)

86400s — far above any plausible leg runtime, per no-artificial-budgets
campaign policy; liveness detection stays with the orchestrator.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…t-resort guard

Follow-up to #17084, which merged with two stacked, contradictory comment
paragraphs on TIMEOUT_BENCHMARK_RUN_S (the retained text still justified the
old 900s bound under the new 86400 value) and a cap 97x the slowest observed
leg. State the current design once: hang detection is owned by the
orchestrator's ProcessDeadlinePolicy one layer below, and this constant only
bounds a child that wedges before that machinery arms — 4h (~16x the slowest
observed sanity leg) is far above any plausible runtime without letting a
dead process hold the gate for a day.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
x402-paid buyer-side yield signal action. Follows up #16685, closed with guidance to add as a third-party plugin.

Co-authored-by: Stakemate369 <299733642+Stakemate369@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Classify model output once through a shared typed boundary before Stage 1, planner, or failure-reply text reaches a user channel. Preserve ordinary JSON, recursively unwrap reply scaffolds, and reject action, planner, evaluator, spawn, malformed, and nested control records with observable errors at runtime boundaries.

Add deterministic adversarial coverage for extra keys, nesting, malformed records, fenced output, genuine domain JSON, and the Stage-1 direct-reply path.

Co-authored-by: wakesync <shadow@shad0w.xyz>
…herence

chore(benchmarks): coherent 4h last-resort acceptance-gate leg cap
…ivery

fix(agent): give dashboard replies one transport owner
…ct (#17063)

* fix(views): 'open knowledge' resolves the Knowledge (documents) view

The documents view's user-facing label is 'Knowledge' (tab name, view
manager card, tags), but the deterministic view-command matcher had no
'knowledge' noun — only documents/docs/files. So the on-screen name is the
one phrase that did NOT deterministically navigate: 'open knowledge' fell
through the rigid matcher, missed the narrow NL shortcut, and rode the full
planner path (where it is at the mercy of provider availability; live QA
surfaced it as a 'something glitched' fallback when the planner call
failed).

Add knowledge / knowledge base / knowledge hub to the documents nouns. The
view already carries the 'knowledge' tag, scoreView matches it for fuzzy
search, and DOCUMENT_SURFACE_WORDS in views-show already treats 'knowledge'
as a documents-surface word — the matcher was the only place the label was
missing.

Tests: view-command-matcher explicit cases gain the three phrasings (199
pass); full view-matrix suite 600 pass.

Co-authored-by: wakesync <shadow@shad0w.xyz>

* fix: unify localized Knowledge navigation

---------

Co-authored-by: wakesync <shadow@shad0w.xyz>
Co-authored-by: Shaw <shawgotbags@gmail.com>
…ks vi.mock across files

The vmForks pool shares one worker process whose VM-context module
interception races across test files: vi.mock factories nondeterministically
leak into (or vanish from) a neighboring file's module graph when several
conversation-route suites run in one invocation. Observed both directions on
pristine develop (~3/5 group runs): conversation-failurekind-roundtrip loses
its chat-routes mock (real readChatRequestPayload rejects with "text is
required"), and conversation-greeting-idempotency inherits a foreign no-op
persist mock (zero greeting rows, regenerated divergent greetings).

packages/agent was the only vmForks user; the root suite already runs forks.
8/8 grouped runs green under forks vs 3/5 failing under vmForks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
perf(core): coalesce turn-scoped room + message reads on the compose hot path
…leak

fix(test): move packages/agent vitest to the forks pool — vmForks leaks vi.mock across files
…registry

fix(registry): remove unsafe YieldSignal listing
…tivity capture (#17056)

* fix(app): lifecycle-safe renderer services + self-starting LifeOps activity capture

Adds a renderer-service lifecycle registry (@elizaos/ui/platform/
renderer-services): plugin appRegister entries declare scoped services
(registerRendererService) instead of starting work at import time; the app
shell installs a per-window host that starts only shell-eligible services,
retains every returned disposer, and tears down on pagehide, host
replacement, and same-id re-registration (HMR). Side-effect loader
identities are role-qualified (<name>#<mode>) so a register entry can never
share a cached import promise with the package-root facade.

Personal Assistant's LifeOps activity capture moves from the never-reliable
React mount to an imperative controller registered as a main-shell-only
service: idempotent starts, stop() racing any awaited native operation
removes late handles/monitors/intervals, native monitoring never starts
without granted permission (no capture before consent, re-checked on
resume), and unexpected failures surface as capture_error + console.error
instead of collapsing into runtime-not-ready.

Closes #16504

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

* fix(ui): survive bfcache round trips in the renderer-service host

A pagehide with persisted=true (iOS Safari back-nav entering the
back/forward cache) used to dispose the host outright, permanently
killing every renderer service until a hard reload. Only a real page
teardown (persisted=false) disposes now; bfcache freezes and resumes
running instances safely. Adds the bfcache round-trip test and the
requested rationale comment for the Symbol.for globalThis store
(#12091 doctrine).

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

* fix(lifeops): close the client-lifeops prototype race; leaf ui subpaths; surface 5xx status-probe failures

Ported from the adjudicated #17053 branch (fix/lifeops-connector-deps):

- The register-path import graph never evaluated client-lifeops.js, so
  client.captureLifeOpsActivitySignal existed only if the PA root facade
  happened to load first (lost boot-batch signals + spurious
  capture_error until the idle load landed). The capture module now
  side-effect-imports the extension itself and re-types its /api-subpath
  client view via the exported LifeOpsElizaClientMethods interface. A
  new test imports the capture module with the real client-lifeops
  module (no vi.mock of any @elizaos/ui specifier) and fails if the
  side-effect import is removed.
- Leaf @elizaos/ui subpath imports (/api, /bridge, /events,
  components/ui/button, components/ui/input) replace root-barrel
  imports in the capture path and blocker settings cards, keeping the
  router/component tree out of the register chunk.
- The status probe previously quieted every ApiError (persistent 500s
  included) as "not ready" forever, contradicting its own header
  comment; only network/timeout/503 count as the designed stand-down
  now, anything else surfaces as capture_error.

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

* test(lifeops): real-PGlite activity-signals route e2e; de-larp PA agent test stubs

Ported from the adjudicated #17053 branch: an HTTP-shaped POST through
the real route dispatcher -> LifeOpsService -> repository insert against
a real PGlite-backed AgentRuntime, read back via GET and raw SQL
(telemetry mirror included), plus the 400-with-no-row and
runtime-unavailable error paths. The PA agent test stub now re-exports
the real createIntegrationTelemetrySpan and checkRateLimit instead of
shape-drifted fakes, so route tests run the genuine dispatch path.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Shaw <shawgotbags@gmail.com>
…tion

fix(agent): preserve authoritative streamed text
@lalalune
lalalune merged commit d3e10cd into main Jul 23, 2026
126 of 156 checks passed
@lalalune

Copy link
Copy Markdown
Member Author

Merged to main; prod rollout is now waiting on production environment approval (prevent-self-review blocks lalalune, who triggered the push). @standujar @NubsCarson @0xSolace — pending approvals:

Rollback anchor: main@c97a73bfe84. No migrations in the diff; platform-only promotion.

@github-actions

Copy link
Copy Markdown
Contributor

LifeOps Multi-Tier Benchmark

Suite: smoke — Tiers requested: large,frontier

large

LifeOps Multi-Tier Benchmark

Tier: large
Suite: smoke

frontier

LifeOps Multi-Tier Benchmark

Tier: frontier
Suite: smoke

Artifacts: lifeops-multi-tier-large-30039706044, lifeops-multi-tier-frontier-30039706044

@lalalune

Copy link
Copy Markdown
Member Author

Status: the main-push Cloud CF Deploy run was manually cancelled at 21:17Z (no rejection recorded; prod groups are queue-never-cancel, so it wasn't automation). Re-queued it — it is back in waiting on the production approval gate alongside Apps Worker and Provisioning Worker. prevent-self-review blocks lalalune (push actor); any of @standujar / @NubsCarson / @0xSolace can approve. If the cancel was deliberate (holding prod), reject the gate instead and note it here.

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.

6 participants