Skip to content

Shaw/pr 2067 eliza fix#7175

Merged
lalalune merged 100 commits into
developfrom
shaw/pr-2067-eliza-fix
Apr 29, 2026
Merged

Shaw/pr 2067 eliza fix#7175
lalalune merged 100 commits into
developfrom
shaw/pr-2067-eliza-fix

Conversation

@lalalune
Copy link
Copy Markdown
Member

Relates to

Risks

Background

What does this PR do?

What kind of change is this?

Documentation changes needed?

Testing

Where should a reviewer start?

Detailed testing steps

Dexploarer and others added 30 commits April 26, 2026 18:14
agent/api/index.ts
- Re-export `matchPluginRoutePath` and `tryHandleRuntimePluginRoute`
  from `./runtime-plugin-routes.js`. Plugin authors and their tests
  (apps/app-vincent/src/vincent-plugin-dispatch.test.ts is the most
  visible) reach for the matcher via `@elizaos/agent` — without the
  re-export the tests fail with `TypeError: matchPluginRoutePath is
  not a function`.

steward-sidecar.ts + steward-sidecar/process-management.ts +
steward-sidecar/wallet-setup.ts
- Replace `await import("node:fs|node:path|node:child_process")`
  with static namespace imports (`import * as fs from "node:fs"`).
  These files only run inside the bun process — they manage the
  Steward API child process and were never meant to load in the
  renderer. The dynamic loading wasn't preventing browser bundling
  (other steward modules already use static node:*) and was the
  source of the Vite "dynamically imported but also statically
  imported" warnings for `node:fs`, `node:path`, and
  `node:child_process` in the renderer build.

Net build: 0 warnings (was 11+ across circular-chunk, empty-chunk,
and dynamic↔static collision categories).
Rollup warning: `widgets/index.ts` re-exports `WidgetHost` from
`widgets/WidgetHost.tsx`, but the WidgetHost module also pulls other
widgets/* code that depends back through the barrel. When two consumers
end up in different chunks (e.g. the lazy AutomationsView chunk vs the
main shell), the cycle blocks proper splitting:

  Export "WidgetHost" of module ".../widgets/WidgetHost.tsx" was
  reexported through module ".../widgets/index.ts" while both modules
  are dependencies of each other and will end up in different chunks
  by current Rollup settings. This scenario is not well supported at
  the moment as it will produce a circular dependency between chunks
  and will likely lead to broken execution order.

Fix: switch the four non-widget callers (AutomationsView, HeartbeatsView,
CharacterHubView, TasksEventsPanel) from barrel imports to a direct
sub-path import of `widgets/WidgetHost`. The barrel itself stays for
non-cyclic exports (resolveWidgetsForSlot, declarations registry, etc.).

Build: still 0 warnings.
…ycle-and-greenup

# Conflicts:
#	packages/app-core/src/components/pages/AppDetailsView.tsx
#	packages/app-core/src/components/pages/AppsView.tsx
@elizaos/plugin-todo@2.0.0-alpha.14 (resolved via the 'alpha' dist-tag in
packages/autonomous/package.json) declares a hard pin to
@elizaos/plugin-rolodex@2.0.0-alpha.114 — but rolodex was never
published past alpha.10. As a result, every `bun install` against
develop fails with:

  No version matching '2.0.0-alpha.114' found for specifier
  '@elizaos/plugin-rolodex' (but package exists)

This was masked because ci.yaml (the workflow that actually runs
`bun install` and tests) hasn't run on develop in months — the
`Code Quality: Push on develop` workflow that does run does not
install dependencies.

Add an override to force the plugin-rolodex transitive resolution to
its highest published version (2.0.0-alpha.10), restoring `bun install`
on develop. Real fix is to coordinate sibling-package release
versioning so plugin-todo and plugin-rolodex stay in sync.
rimraf v6 drops Node <18 support and reorganizes ESM entry points; v5
introduced new glob handling. The breaking changes are largely about
Node version requirements — we're already on Node 24 (this stack also
includes a Node 22/23 -> 24 bump), so v6 is safe.

lerna@9.0.3 declares rimraf@^4.4.1, but uses rimraf only via its top-
level callback API, which is stable across v4-v6. A smoke test of
`bunx lerna --version` succeeds with the override applied.

Closes the rimraf -> v6 row of #79.
Snapshot of the in-flight work before pulling origin/develop. Covers:
- electrobun-webview tab kit (cursor overlay, realistic events, wallet shims)
- LAUNCHPAD_LAUNCH action + four.meme/flap.sh profile engine
- Solana tx signing through wallet-browser-compat-routes
- cloud balance string|number coercion across three readers
- dev-ui.mjs API supervisor restart-on-clean-exit

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
flap.sh launches tokens via Portal.newTokenV6(NewTokenV6Params) on BNB
Chain (mainnet chain 56, testnet chain 97). Per
https://docs.flap.sh/flap/developers/token-launcher-developers — the
website UI wraps that contract call; the resulting eth_sendTransaction
flows through our existing browser-wallet bridge and steward approval
path. No Solana plumbing was needed.

Renames flap-sh:devnet -> flap-sh:testnet across the action / tests /
profile, and adds Portal contract addresses + docs reference to the
profile header so future selector tuning has the on-chain context.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… health, hot reload

New `packages/agent/src/runtime/operations/` module:
- `manager.ts` — RuntimeOperationManager, the single-flight gate for
  provider switches, restarts, and reloads. Replaces the ad-hoc
  `providerSwitchInProgress` boolean.
- `repository.ts` — operation-state store, surfaces pending/active/done.
- `classifier.ts` + `classifier.test.ts` — decides whether an inbound
  request is a duplicate of an in-flight op via the idempotency key.
- `health.ts` + `health.test.ts` + `health-checks.ts` — runtime health
  predicates used by the reload/cold strategies.
- `cold-strategy.ts`, `reload-hot.ts` — strategy implementations for
  full restart vs hot reload.
- `index.ts` + `types.ts` — module barrel and shared types.

Wires the manager through:
- `api/provider-switch-routes.ts` — reads Idempotency-Key header,
  routes through the manager rather than the legacy boolean gate.
- `api/server.ts`, `runtime/restart.ts` — refactored against the new
  manager; the old single-flight scaffolding is gone.
- `app-core/scripts/dev-platform.mjs`, `app-core/src/api/client-base.ts`,
  `client-types-core.ts`, `cli/run-main.ts`, `runtime/error-handlers.ts`,
  `shared/scripts/generate-keywords.mjs` — companion changes the
  manager required (dev-platform integration, error surfacing,
  client-side typing for the new op-status responses).

WIP — preserving in a commit so it doesn't sit as uncommitted state on
the feature branch. Squash / split as needed in the PR.
Introduces @elizaos/confidant — the single seam at which credentials
can be observed inside an Eliza agent. Skills never read process.env
for credentials; they request from a per-skill ScopedConfidant that
goes through policy + audit. Confidant stores values literally
(AES-256-GCM at rest with secret id bound as AAD) or as references
into external password managers.

Phase 0 closes seven concrete failure modes in the existing
credential pipeline (skill exfiltration via process.env, dual-writer
bug between provider-switch-config and plugins-compat-routes, the
Object.values(config).find(non-empty) heuristic that lets a model
slug overwrite an API key, etc.). The runtime does not yet call into
Confidant — that is phase 1. This commit ships the contract.

Public API
- createConfidant, ScopedConfidant
- defineSecretSchema(...) — single source of truth for "this id is
  a secret" decisions; replaces the catalog-as-authoritative pattern
- parseReference / buildReference — URI scheme dispatch (op://,
  pass://, keyring://, file://, env://, cloud://)
- AES-256-GCM envelope helpers (encrypt/decrypt with AAD binding)
- Identifier validation + glob-pattern matching with most-specific
  selection

Backends
- KeyringBackend — cross-platform via @napi-rs/keyring (macOS
  Keychain, Windows Credential Manager, Linux Secret Service /
  libsecret). Prebuilt binaries for darwin/win/linux/freebsd.
- EnvLegacyBackend — read-only env://VAR migration scaffolding,
  removed in phase 6.
- 1Password / Proton Pass / Cloud are deferred to phase 4+.

Storage
- ~/.milady/confidant.json mode 0600, atomic-rename writes.
- Master key sourced from OS keychain by default; pluggable via
  inMemoryMasterKey (tests + headless deployments).
- The secret id is bound as AAD so a swapped ciphertext fails closed.

Policy
- Deny-by-default.
- Implicit grant: the plugin that registered a secret id via
  defineSecretSchema gets always-access to that id.
- Explicit grants persisted per-skill with glob patterns. Most-
  specific-wins; deny is absolute.
- prompt mode requires a PromptHandler; without one, prompt-mode
  grants resolve to deny.

Audit log
- ~/.milady/audit/confidant.jsonl, append-only JSONL.
- Records ids, never values. Explicit test verifies the secret value
  never appears in the log even after a successful resolve.

Tests
- 70 vitest cases across envelope, identifiers, references, store,
  policy, end-to-end Confidant flows, and a real cross-platform
  keyring round-trip. The keyring round-trip skips cleanly on hosts
  without a usable Secret Service agent.
Adds a dedicated workflow that runs the package's vitest suite on
ubuntu / macos / windows. The package's headline claim is that the
OS-keyring credential mediation layer works the same on all three
platforms; this workflow makes that claim verifiable in every PR that
touches packages/confidant/**.

Notes
- Confidant installs standalone (`cd packages/confidant && bun install`)
  rather than going through the root workspace install. This keeps the
  CI run independent of which submodules are initialized in the
  checkout, and finishes in seconds rather than minutes.
- On Linux, libsecret + gnome-keyring + dbus are installed and a
  session DBus + unlocked secret-service daemon are started so the
  real keyring round-trip tests run. Without those, the tests are
  designed to skip cleanly via a module-import-time probe — fail-open
  on hosts without a usable Secret Service.
- The workflow is path-filtered to packages/confidant/** + the
  workflow file itself, so unrelated changes don't trigger it.
- Both `develop` and `main` are listed as branches so the workflow
  runs for the standard elizaOS PR target.
Adds six end-to-end integration tests that prove Confidant's contract is
sufficient to close the failure modes documented in §2 of the design
doc, without requiring runtime wiring. Each test names a specific bug
from the existing elizaOS credential pipeline and demonstrates that
the bug is structurally impossible against the new architecture.

Tests added (test/integration.test.ts)
- bug #3 model-slug-overwrites-API-key: schema-driven save makes the
  legacy `Object.values(config).find(non-empty)` heuristic structurally
  unnecessary; reproducing the user's actual incident produces correct
  persistence regardless of input order.
- bug #1 skill exfiltration: non-owning skills cannot resolve another
  plugin's credentials. Implicit owner grant is exact-id, not
  pattern-spanning.
- bug #6 no-reveal: saved values can be round-tripped programmatically
  via `resolveDetailed`, foundation for a Settings "Reveal" button.
- schema-driven save: arbitrary input ordering produces correct
  persistence (input map iteration order doesn't affect outcome).
- storage opacity: the same id can be a literal one moment and a
  reference the next; consumer call signature is unchanged.
- audit trail: granted + denied resolves are both recorded; the secret
  value never appears in the log.

Test count: 70 → 76. All passing on macOS; cross-platform CI matrix
(added in the previous commit) runs the full suite on Linux / Windows
once a maintainer approves the workflow run.

CHANGELOG.md added per elizaOS package conventions.
…cy env bridge

Adds two integration modules so an elizaOS host app can adopt Confidant
in two lines of bootstrap rather than 14 individual schema declarations.
Both are pure data-shape, non-invasive to existing runtime code:

- `registerElizaProviderSchemas()` — registers the canonical schema for
  every AI provider in the elizaOS catalog (anthropic, openai,
  openrouter, google, groq, xai, deepseek, mistral, together, zai,
  elizacloud, ollama) plus the two subscription credential types
  (anthropic-subscription, openai-codex). Each entry carries the
  human label, format-prefix hint (sk-or-v1-..., sk-ant-..., sk-...),
  sensitivity flag, and ownership attribution to the matching plugin
  id (so the implicit-grant rule fires automatically once those
  plugins migrate to Confidant reads).

- `mirrorLegacyEnvCredentials(confidant, credentials)` — bulk-registers
  `env://VAR_NAME` references in Confidant given a list of resolved
  credentials in the shape `@elizaos/app-core`'s `credential-resolver`
  already produces. Returns a structured report of migrated vs.
  skipped entries (unknown-provider / missing-env-var), so callers
  can log adoption progress at boot.

Also exports `isSubscriptionProviderId(providerId)` (used by the future
CloudBackend to refuse syncing device-bound tokens) and
`providerIdForSecretId(id)` (inverse lookup for telemetry).

Why not in `@elizaos/agent` or `@elizaos/typescript`
- The bridge is data-shape-only — `ResolvedCredentialLike` mirrors the
  existing `credential-resolver` shape but the import is structural,
  not a runtime dependency. This avoids forcing `@elizaos/confidant`
  into a dependency cycle with the runtime packages and keeps the
  Confidant package consumable by any host app, not just the elizaOS
  reference runtime.

Tests added (test/integrations.test.ts)
- Coverage of the canonical map, the schema registration, the bridge
  migration paths (success / unknown / missing-env), the subscription
  flag, and the inverse lookup.
- An end-to-end "real-world bootstrap" test that wires schema +
  bridge + scoped resolve through env-legacy and verifies the audit
  log records the granted resolve correctly with `source: env-legacy`.

Test count: 76 → 86. All passing.
Adds MIGRATION.md showing step-by-step how an existing Eliza host app
adopts Confidant without breaking the running app at any point. Mirrors
the 7-phase plan from §9 of the architecture design doc but
operationalized for someone reading this package's docs:

Phase 1 — initialize Confidant at app boot (mirror legacy env)
Phase 2 — single canonical writer (Settings saves through confidant.set)
Phase 3 — built-in AI providers migrate to Confidant reads
Phase 4 — password manager backends (1Password, Proton Pass)
Phase 5 — third-party plugins migrate
Phase 6 — close the boundary (process.env.*_API_KEY undefined)
Phase 7 (optional) — E2E-encrypted Cloud sync

Each phase has steps, an exit criterion, and a one-liner test the
migrator can run to verify they actually completed it.

Also documents migration anti-patterns (reading process.env after
phase 6, `*` grants, caching resolved values) and a "When something
breaks" section pointing at the audit log.
… catalog

Following review feedback that the LLM-only `ELIZA_PROVIDER_SECRET_IDS`
map didn't reflect Confidant's full scope, replaced it with a
comprehensive `ELIZA_ENV_TO_SECRET_ID` keyed by env-var name and
covering every secret field across the 31 plugins in the elizaOS
catalog.

Domains added (and their canonical SecretId namespaces):
- llm.*           — anthropic, openai, openrouter, google, groq, xai,
                    deepseek, mistral, together, zai, vercelAiGateway
                    (formerly already supported, kept)
- subscription.*  — anthropic, openai (device-bound OAuth tokens)
- tts.*           — elevenlabs
- connector.*     — github, linear, twilio, roblox, x (X/Twitter — NOT
                    to be confused with xai the LLM provider)
- tool.*          — n8n, capsolver, browserbase
- storage.*       — s3 (AWS access key id + secret access key)
- wallet.*        — default (chain-agnostic), evm, solana, hedera,
                    polymarket — every entry flagged device-bound
- rpc.*           — alchemy, infura, ankr, helius, birdeye, jupiter,
                    moralis, coingecko, dexscreener, zeroex
- trading.*       — polymarket (CLOB credential triplet)
- music.*         — lastfm, genius, theaudiodb, spotify
- service.*       — elizacloud, acp, blooio, moltbook

API surface
- `ELIZA_ENV_TO_SECRET_ID` — env-var-name → SecretId (primary key,
  matches credential-resolver's `envVar` field).
- `ELIZA_PROVIDER_TO_SECRET_ID` — provider-id → SecretId (legacy index
  for callers working in the catalog's providerId space).
- `mirrorLegacyEnvCredentials` — bulk env-reference registration,
  works across every domain. Returns structured `MirrorResult` with
  per-credential `unknown-env-var` / `missing-env-var` skip reasons.
- `isSubscriptionProviderId` — flags Anthropic/Codex subscriptions.
- `isDeviceBoundSecretId` (NEW) — flags subscription tokens AND wallet
  private-key material; future CloudBackend (phase 7) refuses to sync
  any id flagged here.
- `providerIdForSecretId` / `envVarForSecretId` (NEW) — inverse
  lookups for telemetry / migration tooling.

Schema generator (`registerElizaSecretSchemas`) now produces
human-readable labels across every domain (with Eliza-aware acronym
casing: S3, EVM, RPC, ACP, OpenAI, OpenRouter, GitHub, ElevenLabs,
DeepSeek, Z.AI, Last.fm, TheAudioDB, DEXScreener, 0x, etc.) and
plugin-id ownership for every catalog credential.

Backwards-compatibility
- `registerElizaProviderSchemas` is kept as a deprecated alias for
  `registerElizaSecretSchemas`. Existing callers continue to work.

Tests
- Reworked test/integrations.test.ts to spot-check across all 11
  domains. Asserts every catalog env-var appears in the map, every
  provider-id has a SecretId, label and ownership generation work
  across LLM / connector / wallet / storage / TTS / RPC.
- New end-to-end test exercises a multi-domain credential set
  (LLM + connector + wallet) through the bridge with cross-plugin
  permission denial enforced.
- Test count: 86 → 91. All passing.

MIGRATION.md updated with multi-domain examples (LLM, connector,
wallet, RPC, storage) and a TL;DR that names the full catalog scope
up front instead of focusing on the original OpenRouter incident.
…into develop

# Conflicts:
#	bun.lock
#	package.json
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

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: 2cf3adf5-efba-4629-9f8d-23f36d1a0f6a

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 shaw/pr-2067-eliza-fix

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 and usage tips.

@lalalune lalalune merged commit 5168d47 into develop Apr 29, 2026
11 of 34 checks passed
@lalalune lalalune deleted the shaw/pr-2067-eliza-fix branch April 29, 2026 02:39
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 29, 2026

Claude encountered an error —— View job


I'll analyze this and get back to you.

lalalune added a commit that referenced this pull request May 3, 2026
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.

3 participants