fix: clear scoped cloud-pair key on explicit disconnect - #1
Open
sharkwon wants to merge 600 commits into
Open
Conversation
(cherry picked from commit 0dcaf1409bfcf1a9b5acf49d4847bbb75af7c765)
Extends elizaOS#17715 with the typed J4 source-state boundary found during live preview QA. AI provider/model: OpenAI / gpt-5.6-sol Client / agent tooling: Codex desktop Contribution skill revision: 04ff898:packages/skills/skills/contribute-to-eliza Attribution status: self-reported — [codex/restack-demo-0804] eliza-computer-attribution:v1 {"agent":"codex","provider":"openai","model":"gpt-5.6-sol","client":"codex-desktop","skill_revision":"04ff8982861e7aa5045c0a065fb7e1d8750e6e6b:packages/skills/skills/contribute-to-eliza","status":"self-reported","marker":"codex/restack-demo-0804"}
Closes elizaOS#17726 AI provider/model: OpenAI / gpt-5.6-sol Client / agent tooling: Codex desktop Contribution skill revision: 04ff898:packages/skills/skills/contribute-to-eliza Attribution status: self-reported — [codex/restack-demo-0804] eliza-computer-attribution:v1 {"provider":"openai","model":"gpt-5.6-sol","tool":"codex-desktop","skill":"04ff8982861e7aa5045c0a065fb7e1d8750e6e6b:packages/skills/skills/contribute-to-eliza","status":"self-reported","lane":"codex/restack-demo-0804"}
…OS#17686) * fix(scripts): replace rg with git ls-files in packageJsonPaths Removes undeclared ripgrep dependency. Uses git ls-files which is a hard requirement everywhere. Adds ENOENT error handling to report missing git binary instead of generic failure message. Closes elizaOS#17681 * fix(scripts): keep packageJsonPaths matching only real package.json files git's `*package.json` pathspec is a suffix match, so it would also return a stray `foo-package.json` that `rg --files -g package.json` never matched. It happens to return the same 153 files today, which is exactly the kind of equivalence that stops being true silently. Filter on the basename so the replacement is behaviour-preserving by construction rather than by coincidence. --------- Co-authored-by: lalalune <shawmakesmagic@gmail.com>
* fix: repair Windows verification contracts # Conflicts: # packages/app-core/scripts/ensure-shared-i18n-data.mjs # packages/import-conversations/package.json * docs: sync native gateway lint guidance --------- Co-authored-by: Shaw <shawmakesmagic@gmail.com>
…efore React boots (elizaOS#17893) * fix(agent): seed elizaos:active-server accessToken in injected HTML before React boots Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(agent): keep window.__ELIZA_API_TOKEN__ alongside the boot-config seed Seeding boot-config apiToken is the right fix — getElizaApiToken() reads only the boot-config store, so the bare window global never reached it. But the global is not dead: plugin-native-agent and plugin-native-websiteblocker read `window.__ELIZA_API_TOKEN__` directly (documented in both plugins' README/CLAUDE/AGENTS) and the Android WebView hydrates it. They fall back only to sessionStorage, which nothing seeds, so dropping it sent their API calls unauthenticated. Emit all three sinks and document why each exists, including the first-run-complete write, which only applies to already-provisioned deployments. Replaces the substring assertions with tests that execute the injected script against a stubbed browser global, so a syntactically broken seed can no longer pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: elizaOS Developer <developer@elizalabs.ai> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: lalalune <autonomousresearcher@gmail.com> Co-authored-by: Shaw <shawmakesmagic@gmail.com>
…ugin (elizaOS#17871) Co-authored-by: Jeremy <jeremy@webtop-dev.0mod.com> Co-authored-by: Shaw <shawmakesmagic@gmail.com>
* fix(ci): harden snap-publish workflow inputs
Move workflow_dispatch version/channel into env: and expand via shell
variables instead of interpolating ${{ }} into run: script text.
Co-authored-by: Cursor <cursoragent@cursor.com>
* ci: re-trigger PR gates after snap-publish hygiene
Empty commit to re-run Develop/Security Advisory gates that timed out.
* ci(snap-publish): validate version/channel before sed and upload
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
… and workflows (elizaOS#17599) * fix(ci): re-land the review-hardened Bun contract on the refactored tree develop adopted this PR's v1 skeleton (ci-bun-version.json + the early line-based contract) via the refactor sweeps, then the same sweeps regressed most of the site pins the contract exists to hold: bare bun.sh/install in the deploy workflows and training cloud scripts, presence-only install guards, a 1.3.10 default in docker-ci-smoke, a 1.3.13 Hetzner cloud-init download, a canary cell and mutable @v2 in the computeruse CI template, and the contract execution dropped from BOTH required lanes. Live demonstration of the drift class elizaOS#17044 names. This commit is the review-hardened v2 contract (job-scoped structured YAML resolution per maintainer review, Docker stage scoping, shell default proving, job-scoped allowlist, 55-test suite) re-anchored to the refactored tree: the gate list drops the two lanes the refactor deleted (benchmark-tests, feed-env-audit), every regressed site is re-pinned, and both required lanes execute the contract with the exact-head inventory artifact again. Contract green: 333 sites, 9 gate lanes, zero drift classifications. * fix(ci): scan the surfaces the Bun contract was selecting itself out of Addresses @lalalune's four findings on elizaOS#17599, rebased onto develop. 1. The scan precondition excluded exactly what invariant 2 targets. A non-.github YAML entered the inventory only if it already contained a `bun-version:` key — but a `oven-sh/setup-bun` step that wires no version has no such key, so the defective files selected themselves out and the gate reported a clean sweep of a set chosen to exclude its own counterexamples. 16 plugin workflows were running the action's floating "latest". The scan now also admits any file referencing oven-sh/setup-bun, which surfaced 30 violations on the first run, and all 15 files are pinned to the reviewed SHA plus bun-version 1.3.14. 2. test.yml had two contract invocations. Removed the bare one in the `changes` job; the `test-runner-vacuous-green-guard` copy is a strict superset (same check plus the inventory artifact), needs `changes`, and is deliberately outside the path gates, so coverage is unchanged. 3. A tracked path that will not open now fails with a named violation instead of a raw ENOENT. git ls-files can list a file the working tree lacks — sparse checkout, uninitialised submodule — and the crash named neither the contract nor the surface. 4. The snapcraft hardcode is reverted and the real gap is fixed. develop already pins BUN_VERSION="1.3.14" at line 86; inlining the literal at the use site made two places to bump. The reason the PR did that is that the shell-defaults scan was gated on `.sh`, so it never read the assignment inside a YAML block scalar and called the use unproven. That gate now accepts YAML, so develop's single pin proves its own use. Separately, deploy-railway.sh wrote `FROM oven/bun:canary-alpine` into a heredoc — scanDockerfile only inspects files NAMED Dockerfile, so a floating canary shipped past a contract whose headline promise is that none survive. Added an embedded-image scan (variant suffixes like -alpine allowed, versions not) and pinned it. Verification on the rebased, develop-merged tree: contract exits 0 (368 sites, up from 363 before the widened scan), 60/60 in ci-bun-version-contract.test.ts. Five new tests cover each finding, and the headline one is mutation-checked — restoring the old precondition fails "scans a non-.github workflow whose setup-bun wires no version". The unreadable-path test builds a real git checkout, since a synthetic tree uses the directory-walk fallback and cannot express a tracked-but- absent file. Biome clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gZy3feiGVKs61PDUzx1Cd --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* fix(cloud): scope durable onboarding admissions * fix(cloud): harden onboarding durable replay cleanup * fix(cloud): bound onboarding replay cleanup * test(cloud): prove onboarding tenant isolation
Co-authored-by: lalalune <18633264+lalalune@users.noreply.github.com>
* fix(agent): bound what a snapshot capture materializes in the agent's own heap Addresses review point 1 on elizaOS#17180 (elizaOS#17172 section 1). The Cloud restorable-size check is strictly downstream of this process having already assembled AND serialized the whole payload, so it bounds what Cloud retains, never what the agent's heap burns getting there. createAgentSnapshot ran five captures concurrently with no size awareness at all: file capture base64-encodes whole files (~2.33x the file size transiently) and the media set has no include predicate, so a large agent could exhaust its container mid-capture. The memory watchdog then force-restarts it, and the lifecycle sites that snapshot before upgrade/shutdown/sleep degrade to a stale or missing backup. Adds a produce-side budget charged as bytes are produced, so an over-budget capture is refused at the first byte past the line instead of after everything is resident. File entries are refused from the declared stat size BEFORE the read, since charging afterwards is charging after the allocation. An optional AbortSignal is threaded through the directory walk so a cancelled capture stops descending. The default ceiling is the shared canonical restorable limit, so the producer refuses exactly what the consumer would. Deliberately NOT claimed as complete: charging the Postgres rows bounds the assembled snapshot, not the peak of the single query that produced it — pool.query buffers the whole result set and node-postgres cannot interrupt an in-flight SELECT. Bounding that peak needs batched keyset reads or a cursor dependency in the agent image; that is a maintainer call, called out in the code and the PR rather than papered over. Stacked on elizaOS#17180, which introduces the shared limits module this imports. Tests: 9/9 in packages/agent, including a red-control that the oversized file is never read. Refs elizaOS#17172 section 1. * fix(agent): read agent-scoped Postgres tables in keyset batches Closes the limitation the previous commit documented instead of solving: charging the serialized rows bounded the assembled snapshot, but pool.query buffers whatever a statement returns, so an unbounded SELECT * WHERE agent_id = $1 still put an entire table in this process's heap before the budget could see a byte. On a chat-heavy agent the memories/embeddings/logs tables are the gigabytes. The two unbounded shapes (the memories-joined embeddings read and the generic owner-column read) now walk in keyset batches ordered by primary key, charging the budget per batch, so the peak is one batch and an oversized table is refused mid-walk. Keyset rather than OFFSET: OFFSET re-scans from the start each page, and a snapshot taken while the agent is live would skip or duplicate rows as they shift. The agent row is already bounded by construction (WHERE id = $1), and a table with no primary key keeps its single read with the post-read charge — documented where it happens. No new dependency: this is the keyset option, not the pg-cursor one that would have needed a call about the agent container image. Tests: 12/12, including that the walk covers a 1200-row table with no duplicates or gaps, that every statement is ORDER BY + LIMIT and resumes from the last id, and that an over-budget table stops early instead of reading to the end. Refs elizaOS#17172 section 1. * fix(agent): make the snapshot budget reserve capacity and cover every capture path Two holes from review. First, reserve() observed the projected total without holding anything, so concurrent captures could each pass the same check, read and encode in parallel, and only collide at charge time - after every payload was already resident. reserve() now returns a settle-once token that claims capacity other reserves must count; the actual encoded size is charged on commit, and a failed read releases the hold. Second, the PGlite dump path materialized the whole dump (Blob, ArrayBuffer, Buffer, base64) without consulting the budget at all, and the pglite-files fallback walk plus the character config read were equally unbudgeted. The dump is now refused from Blob.size before arrayBuffer(), and every component of a capture draws from the one budget. A refusal in one component now also aborts the others: the capture owns an internal AbortController wired into the shared budget signal, so siblings stop at their next check instead of reading at full speed toward a result that is already doomed. The keyset-capture doc no longer implies transactional consistency; it bounds memory, and says exactly that. * fix(backups): close snapshot safety gaps --------- Co-authored-by: standujar <s.andujar@proton.me> Co-authored-by: moon <autonomousresearcher@gmail.com>
…lizaOS#17541) * registry: add 4 x402 third-party plugins (concordance, trading-strategy-data, trustfetch, chainscope) * registry: bump 4 x402 third-party plugins to 0.2.0 (real x402 payment fix) * registry: bump 4 x402 third-party plugins to 0.3.0 (client-side payment policy fix) Addresses the review on the prior PR: all 4 clients signed whatever amount/network/asset/payTo a 402 challenge specified, with no local ceiling, expected-recipient check, or asset/network pin. 0.3.0 adds a local policy check before createPaymentHeader is ever called (exact scheme/network/asset/payTo match plus a configurable per-request max amount), with adversarial tests proving no signature is produced when any of those fields is tampered with. * registry: bump 4 x402 third-party plugins to 0.3.1 (fix source/npm mismatch + dependency audit) 0.3.0 was published to npm from local uncommitted changes that were never pushed to the linked repos -- the review correctly couldn't reproduce the payment-policy code or the adversarial test file from the recorded gitHead. 0.3.1 is published from a commit that's actually in each repo's history (verified: fresh clone + npm install + npm test passes clean for all 4 -- 55/55 tests total). Also resolves the high-severity axios/ws transitive advisories via package.json overrides in each repo (both pulled in through x402's own dependency tree, not direct deps of the plugins) -- 0 high remaining per npm audit. --------- Co-authored-by: Vohlsyr <vohlsyr@users.noreply.github.com> Co-authored-by: Shaw <shawmakesmagic@gmail.com>
elizaOS#17933) * fix(skills): keep the gh 2.45 --jq pagination form inside the retry loop Rebased onto develop. Develop replaced gh's --slurp with `--jq .[]` because Ubuntu 24.04 ships gh 2.45, which has no --slurp; this branch's retry loop was built around the --slurp form. The loop now wraps develop's argument vector and returns parsePaginatedJson(result.stdout, endpoint) so type errors name the endpoint. The retry tests were adapted to the line-delimited output --jq .[] produces rather than slurped nested page arrays, the argv assertion now expects --jq/.[], the spawn-failure case expects develop's endpoint-context wrapper (which preserves `cause`), and the malformed-output case feeds a genuinely non-JSON line, since a bare object is valid under the new form. Develop's endpoint-context test is kept alongside the new retry coverage. * fix(skills): repair retry validation and policy * fix(skills): repair the spliced retry test block Running the suite caught two defects introduced while adapting the retry tests onto develop's `--jq .[]` output form: - develop's "fails command and spawn errors with endpoint context" lost its closing `});`, so the retry tests nested inside it and the file ended with an unbalanced block (Unexpected end of file). - three adapted `stdout` fixtures carried real newlines instead of escapes, so their single-quoted literals spanned lines (Unterminated string literal). Both fixed; the suite runs green at 23 pass / 0 fail. --------- Co-authored-by: lalalune <18633264+lalalune@users.noreply.github.com>
…am is real in CI (elizaOS#17956) The two elizaOS#17768 gate-hint tests added by elizaOS#17770 require a live cache seam, but the Cloud Tests workflow exports CACHE_ENABLED=false, which disables the cache before MOCK_REDIS=1 is honored — every hint write returns 'unavailable' and both tests fail deterministically on every PR lane. Pin CACHE_ENABLED=true at module top, matching inference-billing-fast-path and inference-auth-cache. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
elizaOS#17545) (elizaOS#17930) * docs(cloud): say what the release suite proves, and justify the fence asymmetry krutftw's review of elizaOS#17545. Four documentation corrections, no behaviour change. The 'concurrent releases' test cannot be concurrent on its harness: PGlite is a single WASM backend behind one connection, so the two transactions queue rather than interleave. It is renamed to what it actually pins — a second release of an already-spent generation is a no-op — and the comment now states what is NOT covered: real row-lock contention needs two independent PostgreSQL connections, and under REPEATABLE READ the loser raises a serialization failure rather than returning not-owned. Also: record why dropping organization_id on the reap path is not a weaker fence (agent_sandboxes.id is the primary key, so it selects the same row or none), why the schema ensure is cheap on the teardown path, and why committing the ownership flip is correct when the docker_nodes row is gone. Refs elizaOS#17185 Rebased onto develop and renumbered the migration: develop landed 0189_agent_sandbox_lifecycle_revision_scope through 0192_oidc_request_binding while this branch was open, so 0189_agent_sandbox_deletion_allocation_ownership collided on both the ordinal and the journal idx. It is now 0193 at idx 192 with when=1786392000000. The when value has to exceed the running maximum rather than merely be unique, because the migrator computes pending work as entry.when > lastAppliedCreatedAt. * fix(cloud): restore deletion-allocation-ownership migration SQL dropped in rebase The rebase renumbered the journal entry to tag 0193_agent_sandbox_deletion_allocation_ownership but lost the .sql file, leaving the journal pointing at a migration that did not exist. Recover the ALTER TABLE from the original PR elizaOS#17545 head as 0193, and add the deletion_allocation_counted column to the agent_sandboxes fixture in the app-container store recovery integration test so its inline schema matches the query under test. Merges origin/develop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Zorba-the-buddhah <zorba.the.buddhah@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(core): record each model request once * fix(openai): preserve cache usage in trajectories * fix(core): surface trajectory fallback write failures * test(core): align failed-provider dedupe expectation with recorded billed-but-failed attempts Develop's elizaOS#17690 intentionally records a rejected provider attempt as a finishReason="error" trajectory entry before the successful fallback's generic record, so the wrapped-provider-failure case now sees two entries. 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>
…catalog checker into CI (elizaOS#17606) * fix(i18n): restore 703 catalog entries from call-site defaults and purge 469 dead keys en.json is the source catalog every other locale keys off, but 705 keys used in source were absent from it — inline defaultValue masked the gap for English users while making those strings untranslatable in all seven other locales. 703 entries are restored with values extracted verbatim from their call sites' defaultValue (zero conflicting defaults across duplicate sites; additions-only diff). The remaining two keys pass runtime-conditional defaults and are handled in the checker commit. The reverse rot is purged in the same pass: 469 en.json keys referenced by no literal, no source template prefix, and no allowlist entry — plus one key translated in all seven locales but absent from en and used nowhere — are deleted from every locale (3,741 lines, pure removals, each file re-parsed after edit). Refs elizaOS#17605 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MgpHguHoGxK4pVEtZ6gY43 * fix(i18n): make the catalog checker wireable and wire it into the test gate check-i18n.mjs existed to prevent exactly the drift the sibling commit repairs, but its all-errors contract (any locale missing any key) made it permanently red and it ended up invoked from no lane at all. The contract now matches what CI can hold on every PR: source-locale completeness, unused source keys, orphaned translations, and stale allowlist entries are errors; non-source translation gaps print as warning summaries, upgraded to errors under --strict-translations. runI18nCheck is exported pure (no console/exit) with fixture tests per failure mode, following the ci-bun-version-contract pattern. The dynamic-keys allowlist gains an "uncatalogued" field for the two keys whose call sites pass runtime-conditional defaultValues — cataloging those would freeze one ternary branch and change rendered English, so they stay deliberately uncataloged (with reasons) until the sites are split into per-branch keys; a stale entry whose call site disappears fails the contract. test.yml now runs the checker with the other contracts. Closes elizaOS#17605 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MgpHguHoGxK4pVEtZ6gY43 * fix(i18n): teach the scanner tRef.current() and restore the key it wrongly purged Review finding on elizaOS#17606 (@sharkwon): the dead-key purge deleted documentsview.FailedToLoadDocumentsData from all eight locales while DocumentsView.tsx:470 still calls it. Root cause is in this checker: LITERAL_KEY_RE matched only `t("key")`, so the stable-ref indirection `const tRef = useRef(t)` → `tRef.current("key")` — used in 12 places across 9 files — was invisible, and keys reached only that way looked unreferenced. Two consequences, both real: seven locales lost a translated string and now fall back to English, and English itself silently changed, because the catalog value ("Failed to load knowledge data") outranks the call site's defaultValue ("Failed to load Knowledge data") — the exact drift class elizaOS#17605 exists to remove. The regex now accepts the ref form, and the key plus all seven translations are restored from origin/develop with their original values, not from the defaultValue. Audited the whole purge against this pattern: this key was the only casualty. Two fixture tests added — a tRef.current key that IS catalogued must not be reported unused, and one that is NOT catalogued must be reported missing. Deliberately NOT adding `documentsview.` to the prefix allowlist as suggested: with the scanner fixed the key is seen literally, and a blanket prefix would suppress detection of genuinely dead documentsview keys — weaker than the defect it would paper over. Refs elizaOS#17605 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MgpHguHoGxK4pVEtZ6gY43 * fix(i18n): re-land the catalog checker at its post-refactor home The refactor relocated check-i18n.mjs to packages/app-core/scripts/ (rename detection was skipped — elizaOS#17695 changed 25k files — so it presented as deleted; the factory's own cycle forensics caught the move via git ls-tree). This re-lands the advisory-hardened checker AT THAT PATH with two fixes the move surfaced: the repo-root resolve gains the extra hop, and the direct-run predicate adopts pathToFileURL (the same defect class elizaOS#17677 fixed — the old `file://${argv[1]}` form breaks on spaced paths everywhere and on Windows always). test.yml wiring repointed; the test suite moves alongside and re-roots. Catalogs are restored as PURE ADDITIONS over develop's live copies (+116 keys per locale, en +847; the one true orphan removed from every locale). The checker then caught 26 real gaps on live develop, all fixed from their own sources: 24 missing keys filled from call-site defaultValue / metadata defaultLabel+defaultTitle, and the two state-dependent ternary call sites (cloudOverview localMode) split into four distinct keys — one key can never carry two strings. Checker: OK — 4434 literal keys, 8185 source keys × 8 locales. Suite: 12/12 at the new path. * test(i18n): budget the real-repo case for the tree it actually walks The re-landed checker test's real-repo case scans packages/ui/src and packages/app-core/src synchronously — 5.5s and 7.6s on two consecutive local runs — against bun's 5s default per-test timeout, so it fails on duration alone. Measured at 2402df0: 11 pass, 1 fail, the failure being 'this test timed out after 5000ms'. That is the wrong signal from the one case whose job is to prove the checker is wireable into CI, and this PR's whole subject is wiring it into CI. A CI runner on a cold filesystem is not faster than this machine. Explicit 60s budget on that case only; every fixture case keeps the default. * test(app): add a settings-surface walkthrough spec for PR evidence The evidence gate now derives "UI surface PR" from changed files, so any PR touching packages/ui owes a walkthrough recording rather than an N/A. This spec records one: it renders and scrolls the settings surface with video enabled per-context, so the rest of the smoke suite keeps its cost. Deliberately does not drive section navigation — the shell overlay intercepts pointer events in this harness, and a recording of navigation would claim coverage this suite does not have. It captures the surface rendering, which is what a catalog change owes. * fix(i18n): fail closed on a missing allowlist and catalog the oidcContinue keys Addresses @lalalune's three prioritised findings on elizaOS#17606, rebased onto current develop. 1. The default allowlist path was wrong and the miss was silent. It pointed at packages/scripts/i18n-dynamic-keys.json; the file lives at packages/app-core/scripts/i18n-dynamic-keys.json. loadAllowlist answered the missing file with an empty allowlist, so the dynamic-key and uncatalogued rules did not run in the exact CI invocation and the checker still exited 0 — a green gate with two of its rules disabled. Path corrected and loadAllowlist now throws. "No allowlist" and "an empty allowlist" are different states; only the second is a configuration. The error text told operators to pass --allowlist, which did not exist, so that flag is now real, and the CLI translates the throw into a one-line message and exit 1 (error-policy:J1) instead of a stack trace in the CI log. 2. Rebased and catalogued the four cloud.oidcContinue.* keys, which oidc-continue-page.tsx uses and en.json lacked — the gate would have gone red on the merged tree. Values are each call site's own defaultValue verbatim, so no rendered English changes, and the {{envVar}} placeholder is preserved. Inserted textually to keep the catalog additive-only: `git diff --numstat` on en.json is 4/0. 3. Dropped the two stale uncatalogued entries (settings.cloudOverview.localModeLabel / localModeDescription) that this PR's own refactor obsoleted. With the path fixed the checker names them itself. Verification: 14/14 in check-i18n.test.ts, up from a baseline of 11 pass / 1 fail — the fixed path also resolves the pre-existing failure. Two new tests pin both halves of the contract, and the fail-closed one is mutation-checked: restoring the silent-empty return fails it. Checker against the rebased tree exits 0 with no en.json gaps and no uncatalogued warnings; a bogus --allowlist exits 1 with the message. Biome clean. The test fixture now always writes an allowlist, since under the new contract a fixture that wants an empty one has to say so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011gZy3feiGVKs61PDUzx1Cd * fix(i18n): dedupe en.json keys reintroduced by the develop merge 22 keys existed twice with identical values: keys this branch restored that develop later added independently (documentsview.Scope*, triggerform.event.*, elizaosapps.phone.tab.dialer, desktop.tray.openEliza, cloud.accountDetails.email) plus branch-internal duplicates (cloud.account.securityLink*, cloud.admin.gate.signIn*, cloud.privacyPanel.*Unavailable, cloud.redemptions.copyUserId, cloud.security.*). Keep the first occurrence of each; biome noDuplicateObjectKeys on the locales dir is clean and the catalog checker passes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(agent): authenticate protected self-API callers * fix(agent): resolve one self-API credential on both sides of the boundary The caller normalized its credential (stripping a `Bearer ` prefix, falling back to `ELIZA_API_AUTH_TOKEN`) while the server compared its own raw configured value, so two documented configurations 401'd every protected loopback caller: a `Bearer `-prefixed `ELIZA_API_TOKEN`, and a legacy-only deployment the server never read. `resolveSelfApiCredential` is now the single resolver. It owns canonical-over- legacy precedence and Bearer normalization, and both the caller building `Authorization` and the server resolving what it expects derive their bytes from it, so the two cannot drift. The integration test authorized with a local string compare against the token it had just handed the client, which defined the server to agree with the caller and could not observe drift in the code under test. It now runs the production `isAuthorized` path, with the server's configured credential pinned independently of the caller's so a mismatch is a real disagreement. Reported by @0xSolace on elizaOS#17633; both reproductions confirmed against source. * fix(agent): normalize the Bearer prefix at its source, not at each consumer Takes the review's suggested shape for both blocking findings. resolveApiSecurityConfig now strips an optional leading `Bearer ` from the configured token, so every consumer agrees by construction. Previously ELIZA_API_TOKEN="Bearer x" worked only because the server compared a raw `Bearer x` against the client's `Authorization: Bearer x` header - moving the strip to only some readers turned that working config into a 401 at static-file-server.ts's injected dashboard token and at app-core's resolveBoundaryRole. resolveSelfApiCredential shares the same helper, so caller and server derive an identical credential. getConfiguredApiToken goes back to resolveApiToken in both server-helpers-auth.ts and auth-routes.ts. That helper is not self-API-only: it backs isAuthorized, isWebSocketAuthorized, resolveWebSocketUpgradeRejection, pairingEnabled, ensureApiTokenForBindHost and resolveTerminalRunRejection. Accepting the plugin-level compat key ELIZA_API_AUTH_TOKEN there promoted it to the inbound credential for every remote client, and under a legacy-only config it flipped apiTokenEnabled true - 403-ing loopback terminal runs - while satisfying ensureApiTokenForBindHost's early return so the elizaOS#12228 wildcard-bind hardening never minted its 32-byte token. resolveSelfApiCredential stays what it was introduced to be: the caller-side resolver for requests this process makes back to its own API. * fix(shared): align stripBearerPrefix with the null-based env resolver contract develop's firstNonEmpty and ResolvedApiSecurityConfig.token use string | null, so the undefined-based helper no longer typechecks at the token assignment. Accept null | undefined and return string | null so resolveSelfApiCredential returns the stripped credential directly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: lalalune <shawmakesmagic@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…e) (elizaOS#17890) * feat(plugins): add plugin-zerollama (Ollama-compatible local inference) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(plugin-zerollama): finish provider integration and validation --------- Co-authored-by: elizaOS Developer <developer@elizalabs.ai> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Shaw <shawgotbags@gmail.com>
* fix(cloud): fence concurrent schema migrations * fix(cloud): scope migration hash identity forward * fix(cloud): resolve preflight migrations from module * fix(cloud): validate mixed historical migration orders * fix(cloud): harden migration activation safety * fix(cloud): abort wedged migration preflights * test(cloud): bound migration database cleanup --------- Co-authored-by: Shaw <shawgotbags@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
… and steward TRADE orders (elizaOS#17775) * fix(plugin-wallet): close GHSA-gh63 guard gap for gov votes and steward TRADE orders The prompt-injection financial-write block fires only for transfer, swap, bridge, and pump_fun_buy. Two sibling financial writes skipped it: EVM governance votes/delegations routed through the wallet router (gov), and steward TRADE order submission, which dispatches through trade-action.ts rather than the router. Add gov and trade to the blocked subaction set and assert the guard in the trade action before order submission. Signed-off-by: SashaMIT <sash@ela.city> Co-authored-by: Cursor <cursoragent@cursor.com> * style: wrap the injection-blocked callback to satisfy Biome The new catch emitted a single-line callback that exceeded the configured line width, failing the lint lane on a formatter diagnostic. No behavior change. * fix(plugin-wallet): accurate block message, header, lint, error-policy tag The injection-guard message named only transfer/swap/bridge while the guard now also blocks governance votes and steward TRADE orders; state the full set. File header updated to match. Biome line-width fix at trade-action.ts:614 and // error-policy:J1 on the new catch. Signed-off-by: Sasha Mitchell <sash@ela.city> --------- Signed-off-by: SashaMIT <sash@ela.city> Signed-off-by: Sasha Mitchell <sash@ela.city> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: lalalune <shawmakesmagic@gmail.com>
…S#17750) assertMacArtifactStagerLooksCorrect transcribed the staple-failure block as independent substrings checked with script.includes in isolation. Three of them are bare shell punctuation that recur throughout the file (exit 1 nine times, fi fifty-eight), so the gate proved only that each line existed somewhere, in any order, at any distance. Deleting the exit 1 that fails a require-staple release left the gate green while the release shipped an unstapled DMG. Assert that block as one contiguous ordered run instead, comparing trimmed lines so reformatting does not turn the gate red while order and adjacency — which carry the contract — are pinned. Closes elizaOS#17680
sharkwon
force-pushed
the
fix/clear-cloud-pair-token-on-explicit-disconnect
branch
from
August 7, 2026 13:39
deb1d1b to
95f525e
Compare
…refactor (elizaOS#17572) * feat: interactive Cloud login with type‑level contract (fix elizaOS#17129) - Added handleInteractiveCloudLogin() entry point that pre‑opens the named popup window, preventing forgotten login popups. - Updated AppContext, useCloudState, and 5 component call‑sites to use the new method. - Refactored related test files to use the new method name. - Bumped watchdog PR link and added PR footer attribution. * fix(ui): restore interactive Cloud login tests broken by elizaOS#17129 refactor - Re-add steward_session_token removal in the client-auth test: the stored bearer short-circuits login (the agents list is the connectivity probe), so the interactive entry point is only reachable when login is needed. - Adapt the popup-close test to the elizaOS#17129 contract: the interactive entry point pre-opens the named window and closes it once auth lands. - Drop dead preOpenWindow/closePreOpenedAuthWindow plumbing from startCloudProvisionFlow — popup lifecycle now lives entirely in the store's handleInteractiveCloudLogin. Verified: 58/58 conductor tests, 346/346 first-run suite, 19/19 ChatOverlay.firstrun tests, biome clean. * chore: drop unrelated action-search keywords regeneration from elizaOS#17129 branch The interactive Cloud login contract change does not touch the action-search keyword catalog; the 53k-line regeneration (reformat + reorder + stale entries) was an unintended artifact of running the keywords generator. Revert the generated file to the develop state so the PR diff stays scoped to the Cloud login call-site contract and its tests. * fix(ui): make the Cloud login recovery path separately named and type-scoped (elizaOS#17129) - Rename the raw null-window entry point to handleCloudLoginRecovery: non-interactive by construction, takes no window, so a missed interactive caller cannot compile against it. handleInteractiveCloudLogin remains the only interactive entry point (pre-opens the named popup itself). - Remove handleCloudLogin from the public AppActions surface (types.ts) and from ConfigPageView / ElizaCloudDashboard selectors; recovery is reachable only through the separately named handleCloudLoginRecovery wrapper. - Add rationale for the boot-recovery conductor's then(releaseOverride) continuation and route App.tsx native re-auth through the recovery API. - Add cloud-login-callsite-contract.test.ts: scans the real interactive surfaces (ConfigPageView, ElizaCloudDashboard, first-run-finish, CloudOverviewSection, CloudConnectorsUpsell) and fails if any of them invokes the raw path or the recovery API; recovery calls are allowed only at App.tsx and use-boot-recovery-conductor.ts. Mutation-proven. - Update component tests to assert the new contract: interactive surfaces call handleInteractiveCloudLogin and do not pre-open the window themselves (pre-opening is the entry point's job, elizaOS#17129). * test(ui): remove globals: true from vitest config The globals: true setting was added without demonstrated need. The contract test and all other tests explicitly import from vitest. Removing this unrelated package-wide configuration change per review feedback (0xSolace). * test(ui): make cloud-login-callsite-contract exhaustive over all sources The previous contract pinned two fixed allowlists (INTERACTIVE_SURFACES and RECOVERY_SITES) — a new production file elsewhere under packages/ui/src could call handleCloudLoginRecovery() and the test stays green. This revision walks the whole tree at test time, excludes tests/specs/generated/stories plus the definition file (useCloudState.ts), and asserts: 1. No source invokes the raw null-window path. 2. Sanctioned interactive surfaces use handleInteractiveCloudLogin. 3. handleCloudLoginRecovery is only called at the two sanctioned sites (App.tsx, use-boot-recovery-conductor.ts) — a future file elsewhere in the tree that reaches the recovery API fails this assertion. 4. types.ts does not expose the raw handleCloudLogin. 5. types.ts does expose handleInteractiveCloudLogin. 164/164 focused tests pass (contract + first-run + connectors + settings + App navigate-view); typecheck clean. AI provider/model: deepseek/deepseek-v4-pro Client / agent tooling: Hermes Contribution skill revision: elizaOS/eliza@e379248:packages/skills/skills/contribute-to-eliza Attribution status: self-reported — [sharkwon] * test(ui): add rendered popup evidence spec for interactive Cloud login (elizaOS#17129) Proves the rendered Settings → Cloud connect CTA drives the interactive entry point: clicking it opens a real popup window named eliza-cloud-auth (window.open via preOpenCloudLoginWindow) which navigates to the elizacloud.ai device-login URL, while the main page stays on /settings (no same-tab fallback — the elizaOS#17064 defect class is unrepresentable). Captures before/after desktop (1280x800) + mobile (390x844) screenshots, a popup device-login capture, raw console/network logs, and an MP4 walkthrough (context closed to flush the recording before transcoding). Cloud endpoints stubbed at the network boundary (established ui-smoke pattern); renderer, store, and call-site wiring are the real modules. 164/164 focused tests + this spec (2/2) pass at head d357fa5. AI provider/model: deepseek / deepseek-v4-pro Client / agent tooling: Hermes Agent Contribution skill revision: elizaOS/eliza@e379248:packages/skills/skills/contribute-to-eliza Attribution status: self-reported — [sharkwon] * fix(ui): claim the Cloud-login popup synchronously inside the click gesture (elizaOS#17064) The interactive login entry point pre-opened its popup via window.open only AFTER the network awaits that precede it (first-run provisioning awaits listAgents/getCloudStatusIfSupported; the Settings/dashboard CTAs were the exception). Transient user activation does not survive those round-trips, so the browser blocks the popup and the flow falls back to same-tab /login — re-opening the exact elizaOS#17064 defect this PR closes, reached through a delayed open instead of a missing one (krutktfw, wtfsayo). Fix: interactive surfaces now call claimCloudLoginWindow() synchronously IN the click handler while activation is live; the entry point consumes that stashed handle (takeClaimedCloudLoginWindow()). The type-level contract is unchanged — handleCloudLoginRecovery stays the only null-window route, and handleInteractiveCloudLogin still won't compile against a raw window argument. Call sites updated (Settings Cloud CTA, dashboard, ConfigPageView, connectors upsell, first-run conductor flow launchers). Contract test now (a) matches identifiers not just calls (alias `const x = s.handleCloudLogin` caught), (b) requires EACH sanctioned recovery site individually, (c) documents UI package-root path naming. Docs that named the raw handleCloudLogin reworded. 190/190 focused tests, typecheck + biome clean. AI provider/model: deepseek / deepseek-v4-pro Client / agent tooling: Hermes Agent Contribution skill revision: elizaOS/eliza@e379248:packages/skills/skills/contribute-to-eliza Attribution status: self-reported — [sharkwon] * chore: trigger CI re-run * chore: trigger CI re-run * chore: trigger CI on fork with Actions enabled * chore: trigger develop-pr workflow on fork * fix(cloud-login): remove preOpenCloudLoginWindow fallback in handleInteractiveCloudLogin The fallback to preOpenCloudLoginWindow() ran after awaits in listOrAutoProvisionCloudAgent / runFirstRunFinish, when transient user activation had lapsed. This caused the popup to be blocked and falling back to same-tab navigation — the exact elizaOS#17064 regression this PR was meant to close. All interactive callers (ConfigPageView, ElizaCloudDashboard, CloudOverviewSection, CloudConnectorsUpsell, use-first-run-conductor) already call claimCloudLoginWindow() synchronously in their click handlers. The entry point now strictly consumes the stashed handle with no fallback. * fix(ui): release never-consumed gesture-claimed cloud login popups Flows that claim the popup inside the user gesture but never reach interactive login (local-runtime finish, already-authenticated cloud provision) left the about:blank window open forever. The flow launchers now release the stash in their finally, and a re-claim closes any prior unconsumed handle before overwriting it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Ubuntu <ubuntu@localhost.localdomain> Co-authored-by: sharkwon <sharkwon@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…nsfers (elizaOS#17773) * fix(plugin-wallet): apply recipient-authorization guard to Solana transfers The GHSA-7qxr-x6cg-r9cc recipient guard in runWalletRouter only fired when params.recipient matched /^0x[0-9a-fA-F]{40}$/, so a Solana base58 recipient skipped assertEvmTransferRecipientAuthorized entirely. A transfer recipient inferred from prior session context, token metadata, or embedded addresses — the exact class the EVM guard closed — was unchallenged on the Solana path. Add messageAuthorizesSolanaRecipient/assertSolanaTransferRecipientAuthorized (case-sensitive base58 presence in the current user message or structured action parameters, mirroring the EVM logic) and dispatch on address form in the router gate. Structured action parameters remain authoritative, so programmatic callers are unaffected. Signed-off-by: SashaMIT <sash@ela.city> Co-authored-by: Cursor <cursoragent@cursor.com> * test(plugin-wallet): pin Solana recipient case-sensitivity A message containing the recipient with mangled case must not authorize the transfer; base58 is case-sensitive, so the comparison deliberately does not lowercase like the EVM path. Signed-off-by: SashaMIT <sash@ela.city> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(plugin-wallet): sanitize Solana addresses in display labels, fix lint sanitizeWalletDisplayLabel stripped only EVM addresses, so a poisoned token label carrying an embedded base58 address was rendered verbatim; wire the exported SOLANA_ADDRESS_PATTERN in. Also: biome line-width fix at wallet-context-safety.ts:86, and the file header now states the Solana guard and dual-family label sanitization. Signed-off-by: Sasha Mitchell <sash@ela.city> --------- Signed-off-by: SashaMIT <sash@ela.city> Signed-off-by: Sasha Mitchell <sash@ela.city> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…, visible purge failures - CloudPairRelay: when no owning agent resolves, install the exchanged bearer for the live session only (in-memory installs, no storage write) and show a distinct 'signed in for this session only' state with an explicit continue — never a plain success that drops the spent one-time token's credential. - clearCloudPairApiToken: the agent-scoped branch no longer removes the legacy global key — on a pre-migration install that key may hold a different agent's only bearer; only the global (no-agentId) clear purges it. - cloud-pair-token: storage-enumeration failures during the global purge are tagged error-policy:J6 and logged at warn so a vacated purge is visible. - clearStalePairCredentialsForAgent: JSDoc now matches the unconditional per-agent clear instead of the retired active-server guard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…#17929) * resolve rebase conflicts: update Dockerfile + test for gateway-discord * style(cloud): wrap long expect lines in dockerfile guard test Biome format fix for the gateway guard-test loop; no behavior change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Shaw <shaw.nicola.walters@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…onnect Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…bal clear, session-only fallback capture - Phase 3 now proves the agent-scoped clear leaves the legacy global key (unknown owner) and the other agent's key untouched; new phase 4 proves the global clear purges every scoped key plus the legacy key. - New browser-real test renders the actual CloudPairRelay (real modules through the Vite graph, real styles) on a non-dedicated origin and captures the pairing before-state and the new session-only fallback state, asserting the in-memory install, empty storages, and the explicit continue instead of a silent success redirect. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts: # packages/app/src/main.tsx # packages/app/test/cloud-pair-session-token.test.ts # packages/ui/src/state/useCloudState.backend-sign-out.test.tsx # packages/ui/src/state/useCloudState.ts
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Test PR to trigger fork CI