{
  "interval": {
    "intervalStart": "2026-04-25T00:00:00.000Z",
    "intervalEnd": "2026-04-26T00:00:00.000Z",
    "intervalType": "day"
  },
  "repository": "elizaos/eliza",
  "overview": "From 2026-04-25 to 2026-04-26, elizaos/eliza had 3 new PRs (9 merged), 0 new issues, and 17 active contributors.",
  "topIssues": [],
  "topPRs": [
    {
      "id": "PR_kwDOMT5cIs7VipzR",
      "title": "feat(agent): x402 paid plugin routes and test harness hardening",
      "author": "odilitime",
      "number": 7100,
      "body": "- Add x402 seller middleware: payment gate on plugin routes, startup validation, replay/facilitator/standard payment flows, and Vitest coverage.\r\n- Extend Route and payment types in @elizaos/core; document x402 in Mintlify and link from webhooks-and-routes.\r\n- Re-export @elizaos/app-lifeops lifeops-browser-packaging; expose ./api/server-auth in package exports; onboarding-presets shim.\r\n- Harden @elizaos/app-core tests: Vitest workspace path aliases, repo-root detection for flat monorepos, fix test helper imports, skip drift checks when optional workflows are absent, ComputerUse approval overlay and local plugin inventory fixes.\r\n- @elizaos/plugin-computeruse: key alias helpers, validateInt edge cases, partial helper mocks, longer timeouts, gate full runtime e2E behind env, relax live permission assertions.\r\n- Add package README/CHANGELOG and x402 smoke script for @elizaos/agent.\r\n\r\nMade-with: Cursor\n\n<!-- CURSOR_SUMMARY -->\n---\n\n> [!NOTE]\n> **High Risk**\n> Introduces payment-gating and settlement logic on HTTP plugin routes (including replay protection and remote facilitator verification), which is security- and revenue-critical and could block routes or allow unintended access if misconfigured.\n> \n> **Overview**\n> Adds first-class **x402 paid plugin routes** to `@elizaos/agent`: routes can declare `x402`, and the new middleware enforces 402 responses plus payment verification (on-chain proofs, facilitator payment IDs, and standard `PAYMENT-SIGNATURE`/`X-Payment` flows) with **facilitator `POST /verify` then `POST /settle`** before running handlers, emitting `PAYMENT_REQUIRED`/`PAYMENT_VERIFIED` events and returning `PAYMENT-REQUIRED`/`PAYMENT-RESPONSE` headers.\n> \n> Integrates this into the runtime route dispatcher (auto-wraps `x402` routes unless already wrapped) and adds startup-time x402 validation in `server.ts` to fail fast on invalid configs. Also adds built-in payment config presets, durable (SQL-backed) replay protection, extensive Vitest coverage, plus new package docs (`README.md`, `CHANGELOG.md`) and an `x402` smoke-test script; updates package exports and dependencies accordingly.\n> \n> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 016ae6d387ccac74a281afc46ff6f8b5bac2b69a. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>\n<!-- /CURSOR_SUMMARY -->\n\n<!-- This is an auto-generated comment: release notes by coderabbit.ai -->\n\n## Summary by CodeRabbit\n\n* **New Features**\n  * Added X402 payment middleware for protecting plugin HTTP routes with micropayment requirements\n  * Introduced payment configuration system with support for BASE, SOLANA, and POLYGON networks\n  * Added startup validation for X402-protected routes with detailed error reporting\n\n* **Documentation**\n  * New guides for X402 paid plugin routes and implementation roadmap\n  * Updated webhook and routes documentation with X402 micropayment references\n\n* **Refactoring**\n  * Reorganized internal imports and code formatting across agent package\n\n<!-- end of auto-generated comment: release notes by coderabbit.ai -->",
      "repository": "elizaos/eliza",
      "createdAt": "2026-04-25T04:06:45Z",
      "mergedAt": "2026-04-25T23:39:49Z",
      "additions": 7917,
      "deletions": 1256
    },
    {
      "id": "PR_kwDOMT5cIs7VSiXK",
      "title": "feat: open app surfaces in managed desktop windows",
      "author": "Dexploarer",
      "number": 7092,
      "body": "## Plan\n- Open app catalog selections in managed Electrobun windows using the existing native toolbar-backed surfaces.\n- Keep detached windows connected to page-scoped chat/control context.\n- Add targeted coverage for always-on-top controls and the duplicate launch guard.\n\n## Patch summary\n- Electrobun: add managed app window RPCs, always-on-top toggles, and explicit webview load handling for detached windows.\n- Apps: add App Windows controls, hash-safe detached app routes, and prevent manual launches from being replayed by slug auto-launch.\n- Agent chat: add Connectors, Plugins, and Settings page scopes so detached surfaces retain scoped chat context.\n\n## Validation\n- `bunx @biomejs/biome check <touched files>`\n- `bunx vitest run eliza/packages/app-core/src/components/pages/AppsView.test.tsx eliza/packages/app-core/src/components/pages/page-scoped-conversations.test.ts eliza/packages/app-core/src/components/workspace/AppWorkspaceChrome.test.tsx eliza/packages/agent/src/api/conversation-metadata.test.ts --passWithNoTests`\n- `(cd packages/app-core/platforms/electrobun && bun run test -- src/surface-windows.test.ts src/native/canvas.test.ts)`\n- `(cd packages/app-core && bun run typecheck)`\n- `bun run dev:desktop -- --force-renderer`\n- Live smoke: clicked Apps -> Plugin Viewer; one elizaOS Plugins window opened, loaded the existing Plugins surface, and showed the Plugins chat panel.\n\n## Notes\n- Direct upstream push was denied for Dexploarer, so this PR is opened from the fork branch.\n- Desktop dev still reports existing whisper.cpp and n8n sidecar warnings; they are unrelated to this patch.\n\n<!-- greptile_comment -->\n\n<details><summary><h3>Greptile Summary</h3></summary>\n\nThis PR wires up app catalog selections to open in managed Electrobun desktop windows, adds always-on-top controls for both managed surface windows and canvas/game windows, introduces concurrent-open coalescing for detached surfaces, and extends chat scope to cover Connectors, Plugins, and Settings views in detached windows. The previously flagged issues — silent no-op on `setAlwaysOnTop`, stale pin state on canvas window failure, and the `shouldUseHashNavigation` duplication — are all resolved in this patch.\n</details>\n\n<h3>Confidence Score: 5/5</h3>\n\nSafe to merge — all previously flagged issues are addressed and no new P0/P1 defects found.\n\nThe patch resolves every issue from the prior review thread: setAlwaysOnTop now returns {success:boolean} instead of silently no-opping, the alwaysOnTop state on canvasListWindows failure is handled in a try/catch, shouldUseHashNavigation duplication is eliminated by extracting shared utilities to navigation/index.ts, and the duplicate-surface-window guard is implemented via openSurfaceWindowOnce. New tests cover the concurrent-open coalescing, singleton guard, explicit webview loadURL, and the always-on-top toggle contract. No P1 or P0 issues found in the new code paths.\n\nNo files require special attention.\n\n<details><summary><h3>Important Files Changed</h3></summary>\n\n| Filename | Overview |\n|----------|----------|\n| packages/app-core/platforms/electrobun/src/surface-windows.ts | Adds openAppWindow, concurrent-open coalescing via pendingSurfaceWindows, setWindowAlwaysOnTop, and explicit webview loadURL. Singleton guard for detached surfaces is now present; loadURL deferred via setTimeout(0) to allow webview to initialize. |\n| packages/app-core/src/components/pages/AppsView.tsx | Adds AppWindowRecord tracking, heartbeat loop, desktop window event subscriptions, and the App Windows control strip. slugAutoLaunchDone.current is now set before the slug lookup to prevent re-launch after a manual catalog open updates the URL — intentional guard. |\n| packages/app-core/platforms/electrobun/src/rpc-handlers.ts | Adds desktopOpenAppWindow, desktopSetManagedWindowAlwaysOnTop, and canvasSetAlwaysOnTop RPC handlers. normalizeRendererRoutePath validates the incoming path is a renderer route before it reaches the URL builder. |\n| packages/app-core/platforms/electrobun/src/native/canvas.ts | Adds setAlwaysOnTop (returns {success: boolean}), exposes alwaysOnTop in listWindows output, and accepts alwaysOnTop in openGameWindow. Addresses the previously flagged silent no-op issue. |\n| packages/app-core/platforms/electrobun/src/native/desktop.ts | Adds openAppWindowCallback and managedWindowAlwaysOnTopCallback wiring. openSurfaceWindow now returns a ManagedWindowSnapshot rather than void, enabling callers to receive the created window ID. |\n| packages/app-core/platforms/electrobun/src/rpc-schema.ts | Schema-level additions: DesktopManagedWindowSnapshot, desktopOpenAppWindow, desktopSetManagedWindowAlwaysOnTop, canvasSetAlwaysOnTop, desktopManagedWindowsChanged push event, and alwaysOnTop on canvas/game types. |\n| packages/app-core/src/navigation/index.ts | Extracts shouldUseHashNavigation, getWindowNavigationPath, and isAppWindowRoute into shared utilities, eliminating the previously flagged duplication across AppsView, AppsPageView, AppContext, and useNavigationState. |\n| packages/agent/src/api/conversation-metadata.ts | Adds page-connectors, page-plugins, and page-settings to VALID_SCOPES. page-settings was already in the ConversationScope type union but was missing from the runtime validation set — this is a correct sync fix. |\n| packages/app-core/src/shell/DetachedShellRoot.tsx | Wraps connectors, plugins, triggers, and settings detached views in DetachedWorkspaceView to provide scoped chat context. Settings section gains overflow-y-auto for scroll. |\n| packages/app-core/src/components/apps/GameView.tsx | Adds always-on-top toggle button for game windows with try/catch guard on canvasListWindows fetch (addressing previous stale-state comment) and success-response check before updating local state. |\n| packages/app-core/platforms/electrobun/src/surface-windows.test.ts | New test file covering surface URL building, singleton guard, concurrent-open coalescing, and explicit webview loadURL. Good coverage of the new deduplication logic. |\n| packages/app-core/platforms/electrobun/src/native/canvas.test.ts | New test file covering openGameWindow with alwaysOnTop, setAlwaysOnTop toggle, and failure path for missing window IDs. Validates the { success: boolean } return contract. |\n\n</details>\n\n</details>\n\n<details><summary><h3>Sequence Diagram</h3></summary>\n\n```mermaid\nsequenceDiagram\n    participant UI as AppsView (Renderer)\n    participant Bridge as Desktop Bridge (RPC)\n    participant SM as SurfaceWindowManager\n    participant DM as DesktopManager\n    participant CM as CanvasManager\n\n    Note over UI: User clicks catalog app\n\n    alt Internal tool app (plugins/connectors/browser/triggers)\n        UI->>Bridge: desktopOpenSurfaceWindow(surface, alwaysOnTop)\n        Bridge->>DM: openSurfaceWindow()\n        DM->>SM: openSurfaceWindow(surface, browse, alwaysOnTop)\n        SM->>SM: Check pendingSurfaceWindows (coalesce)\n        SM->>SM: Check existing window (singleton guard)\n        SM-->>UI: ManagedWindowSnapshot {id, alwaysOnTop}\n        SM->>UI: desktopManagedWindowsChanged push event\n    else External viewer app (game/canvas)\n        UI->>Bridge: gameOpenWindow(url, title, alwaysOnTop)\n        Bridge->>CM: openGameWindow(url, alwaysOnTop)\n        CM-->>UI: {id}\n        Note over UI: Heartbeat loop started\n    else Generic app route\n        UI->>Bridge: desktopOpenAppWindow(title, path, alwaysOnTop)\n        Bridge->>SM: openAppWindow(title, path, alwaysOnTop)\n        SM->>SM: buildAppWindowRendererUrl(rendererUrl, path)\n        Note over SM: URL: host/?appWindow=1#/route\n        SM-->>UI: ManagedWindowSnapshot {id, alwaysOnTop}\n        SM->>UI: desktopManagedWindowsChanged push event\n    end\n\n    Note over UI: Toggle pin on open window\n    alt Managed window\n        UI->>Bridge: desktopSetManagedWindowAlwaysOnTop(id, flag)\n        Bridge->>SM: setWindowAlwaysOnTop(id, flag)\n        SM-->>UI: {success: boolean}\n    else Canvas/game window\n        UI->>Bridge: canvasSetAlwaysOnTop(id, flag)\n        Bridge->>CM: setAlwaysOnTop(id, flag)\n        CM-->>UI: {success: boolean}\n    end\n```\n</details>\n\n<sub>Reviews (2): Last reviewed commit: [\"fix: address managed app window review c...\"](https://github.com/elizaos/eliza/commit/58831fa8e73bc48c4ab8586b9ac1d7780717b2fc) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=29583462)</sub>\n\n<!-- /greptile_comment -->",
      "repository": "elizaos/eliza",
      "createdAt": "2026-04-24T09:51:57Z",
      "mergedAt": "2026-04-25T05:31:04Z",
      "additions": 1545,
      "deletions": 114
    },
    {
      "id": "PR_kwDOMT5cIs7VWcWM",
      "title": "fix(character): tokenize name occurrences on save so renames propagate",
      "author": "dutchiono",
      "number": 7093,
      "body": "## Problem\n\nThe character editor already resolves `{{name}}` / `{{agentName}}` tokens on **load** (`replaceNameTokens` in `packages/app-core/src/utils/name-tokens.ts`, seeded by the onboarding presets in `packages/shared/src/onboarding-presets.characters.ts`).\n\nBut `prepareDraftForSave` in `packages/app-core/src/character/character-draft-helpers.ts` never ran the reverse pass. The moment a user saved via the editor, expanded literals got persisted and the tokens were lost forever. From that point on, renaming the agent in Identity settings stopped propagating — bio, system, message examples, post examples, style, and topics all kept the old name as dead text.\n\nUser-visible symptom: renaming the agent requires find-and-replacing the name across ~20 lines of bio, every time.\n\n## Fix\n\nMake save the mirror of load.\n\n- Add `tokenizeNameOccurrences(text, name)` next to `replaceNameTokens`. Whole-word, case-sensitive, idempotent; refuses single-character names to avoid eating prose.\n- `prepareDraftForSave(draft, previousName?)` now runs the tokenizer over every free-text field: `bio`, `system`, `topics`, `postExamples`, `style.{all,chat,post}`, and `messageExamples[].examples[].content.text`. Speaker-label tokenization via `normalizeSpeakerName` is unchanged.\n- Both the current draft name and (optionally) the previous saved name are tokenized so a rename within the same save pass also catches stale old-name literals that the user didn't hand-edit.\n- `useCharacterState.handleSaveCharacter` threads `characterData?.name` through as the previous name.\n\nRuntime prompt composition already resolves `{{name}}` / `{{agentName}}` tokens via `@elizaos/prompts`, so no backend or runtime changes are needed — the fix is purely at the UI persistence boundary.\n\n## Migration\n\nNo explicit one-shot migration needed. The first save after this change tokenizes the character in place, and the load path already renders tokens back to literals for display. Because tokenization is idempotent and case-sensitive/whole-word, re-running it is safe.\n\n## Tests\n\n15 new cases in `packages/app-core/src/utils/name-tokens.test.ts` covering:\n\n- Whole-word boundary behavior (e.g. name \"Kai\" does not match inside \"Kaizen\")\n- Case sensitivity (lowercase \"momo\" is not tokenized when name is \"Momo\")\n- Idempotency (`tokenize(tokenize(x)) === tokenize(x)`)\n- Guardrails (single-character names, empty inputs, regex special chars)\n- `prepareDraftForSave` field coverage: bio / system / topics / postExamples / style / messageExamples bodies\n- Previous-name tokenization during rename\n- Round-trip through `replaceNameTokens` after a rename\n\n```\n Test Files  1 passed (1)\n      Tests  15 passed (15)\n```\n\n## Verified\n\n- `bun run typecheck` — clean\n- `bunx @biomejs/biome check` on all four changed files — clean\n- Unit test suite — 15/15 passing\n\n## Not claimed\n\n- Knowledge / Experience / Relationships tabs may also store the name literally; they're on different API surfaces. If they do, a follow-up PR extends the tokenizer to those endpoints. No change to them here.\n- Nickname / alternate-form support (`{{nickname}}`) is not added. Schema leaves space for it; not needed for the core rename-propagation fix.\n\n<!-- greptile_comment -->\n\n<h3>Greptile Summary</h3>\n\nThis PR adds `tokenizeNameOccurrences` and wires it into `prepareDraftForSave` so that literal name text is re-tokenized to `{{name}}` on every save, making agent renames propagate without manual find-and-replace. The approach is sound, but there is a critical asymmetry between the save and load paths.\n\n- **Save path** now tokenizes `bio`, `system`, `topics`, `postExamples`, `style.all/chat/post`, and `messageExamples[].content.text`.\n- **Load path** (`loadCharacter` in `useCharacterState.ts`) applies `replaceNameTokens` only to `bio` and `system` — the four additional field groups are passed through raw. After the first save with this fix, users will see literal `{{name}}` tokens in the Topics, Style, Post Examples, and Message Examples editor fields.\n\n<h3>Confidence Score: 3/5</h3>\n\nNot safe to merge — the save/load asymmetry will surface raw `{{name}}` tokens in the editor UI immediately after first save.\n\nA single clear P1 defect: the save path tokenizes six field groups but the load path only un-tokenizes two, creating an immediate and reproducible UX regression for every user who saves after this change ships.\n\n`packages/app-core/src/state/useCharacterState.ts` — `loadCharacter` must apply `replaceNameTokens` to `topics`, `style.*`, `postExamples`, and `messageExamples[].content.text` to match the expanded save-side coverage.\n\n<h3>Important Files Changed</h3>\n\n\n\n\n| Filename | Overview |\n|----------|----------|\n| packages/app-core/src/state/useCharacterState.ts | Threads `characterData?.name` as `previousName` into `prepareDraftForSave`, but the `loadCharacter` callback only applies `replaceNameTokens` to `bio` and `system` — not to the six field groups now tokenized on save — causing raw `{{name}}` tokens to appear in the editor UI after first save. |\n| packages/app-core/src/character/character-draft-helpers.ts | Adds `previousName` param and applies tokenization across all free-text fields on save; logic is sound, but correctness depends on the load path being symmetrically updated (which it isn't for several field groups). |\n| packages/app-core/src/utils/name-tokens.ts | Adds `tokenizeNameOccurrences` — case-sensitive, whole-word, idempotent, with guardrails for short/empty names and regex-special chars. Word boundary behaviour is limited to ASCII names; non-ASCII names may not tokenize reliably. |\n| packages/app-core/src/utils/name-tokens.test.ts | Good unit coverage of `tokenizeNameOccurrences` and `prepareDraftForSave` field tokenization; missing round-trip tests verifying load-side expansion for `topics`, `style`, `postExamples`, and `messageExamples`. |\n\n</details>\n\n\n\n<h3>Sequence Diagram</h3>\n\n```mermaid\nsequenceDiagram\n    participant UI as Editor UI\n    participant State as useCharacterState\n    participant Helpers as character-draft-helpers\n    participant API as Backend API\n\n    Note over UI,API: Save flow (this PR)\n    UI->>State: handleSaveCharacter()\n    State->>Helpers: prepareDraftForSave(draft, previousName)\n    Note over Helpers: tokenizes bio, system, topics,<br/>postExamples, style.*, messageExamples\n    Helpers-->>State: tokenized draft\n    State->>API: updateCharacter(tokenizedDraft)\n    API-->>State: success\n    State->>State: loadCharacter()\n\n    Note over UI,API: Load flow (this PR — unchanged)\n    State->>API: getCharacter()\n    API-->>State: character data (with {{name}} tokens)\n    Note over State: clean() applied to bio + system only\n    Note over State: topics / style / postExamples /<br/>messageExamples NOT cleaned ⚠️\n    State->>UI: setCharacterDraft (raw {{name}} tokens visible in editor)\n```\n\n<!-- greptile_failed_comments -->\n<details><summary><h3>Comments Outside Diff (1)</h3></summary>\n\n1. `packages/app-core/src/state/useCharacterState.ts`, line 86-101 ([link](https://github.com/elizaos/eliza/blob/7cb0d48b06af3b7ab64803113994ac619151d18b/packages/app-core/src/state/useCharacterState.ts#L86-L101)) \n\n   <a href=\"#\"><img alt=\"P1\" src=\"https://greptile-static-assets.s3.amazonaws.com/badges/p1.svg?v=7\" align=\"top\"></a> **Load path doesn't un-tokenize `topics`, `style`, `postExamples`, `messageExamples`**\n\n   `loadCharacter` applies `clean` (i.e. `replaceNameTokens`) to `bio` and `system` only, but `prepareDraftForSave` now tokenizes six additional field groups — `topics`, `postExamples`, `style.all/chat/post`, and `messageExamples[].content.text`. After the first save with this fix, those fields will be persisted with `{{name}}` tokens. On reload, the draft will contain raw token strings such as `\"how {{name}} thinks\"`, which the editor will display verbatim to the user.\n\n   ```ts\n   // before setting the draft, add:\n   const cleanArr = (arr: string[]) => arr.map(clean);\n\n   setCharacterDraft({\n     name: savedName,\n     username: character.username ?? \"\",\n     bio: Array.isArray(character.bio)\n       ? character.bio.map(clean).join(\"\\n\")\n       : clean(character.bio ?? \"\"),\n     system: clean(character.system ?? \"\"),\n     adjectives: character.adjectives ?? [],\n     topics: cleanArr(character.topics ?? []),\n     style: {\n       all: cleanArr(character.style?.all ?? []),\n       chat: cleanArr(character.style?.chat ?? []),\n       post: cleanArr(character.style?.post ?? []),\n     },\n     messageExamples: (character.messageExamples ?? []).map((group) => ({\n       ...group,\n       examples: group.examples.map((ex) => ({\n         ...ex,\n         content: { ...ex.content, text: clean(ex.content.text) },\n       })),\n     })),\n     postExamples: cleanArr(character.postExamples ?? []),\n   });\n   ```\n\n</details>\n\n<!-- /greptile_failed_comments -->\n\n<sub>Reviews (1): Last reviewed commit: [\"fix(character): tokenize name occurrence...\"](https://github.com/elizaos/eliza/commit/7cb0d48b06af3b7ab64803113994ac619151d18b) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=29603728)</sub>\n\n> Greptile also left **1 inline comment** on this PR.\n\n<!-- /greptile_comment -->",
      "repository": "elizaos/eliza",
      "createdAt": "2026-04-24T13:37:34Z",
      "mergedAt": null,
      "additions": 389,
      "deletions": 27
    },
    {
      "id": "PR_kwDOMT5cIs7VjOuO",
      "title": "fix(character): tokenize name occurrences on save so renames propagate",
      "author": "lalalune",
      "number": 7101,
      "body": "Cherry-picked from #7093 (by @dutchiono) — only the character rename fix, without the unrelated submodule pointer bumps, Windows pty patch, and coding-agent chip changes that #7093 also carried.\n\n## Problem\n\nThe character editor resolves \\`{{name}}\\` / \\`{{agentName}}\\` tokens on **load** (\\`replaceNameTokens\\`), but \\`prepareDraftForSave\\` never ran the reverse pass. The moment a user saved via the editor, expanded literals got persisted and the tokens were lost. From that point on, renaming the agent in Identity settings stopped propagating — bio, system, message examples, post examples, style, and topics all kept the old name as dead text.\n\n## Fix\n\n- Add \\`tokenizeNameOccurrences(text, name)\\` next to \\`replaceNameTokens\\`. Whole-word, case-sensitive, idempotent; refuses single-character names to avoid eating prose.\n- \\`prepareDraftForSave(draft, previousName?)\\` runs the tokenizer over every free-text field: \\`bio\\`, \\`system\\`, \\`topics\\`, \\`postExamples\\`, \\`style.{all,chat,post}\\`, and \\`messageExamples[].examples[].content.text\\`.\n- Both the current draft name and (optionally) the previous saved name are tokenized so a rename within the same save pass also catches stale old-name literals.\n- \\`useCharacterState.handleSaveCharacter\\` threads \\`characterData?.name\\` through as the previous name.\n\nRuntime prompt composition already resolves \\`{{name}}\\` / \\`{{agentName}}\\` tokens via \\`@elizaos/prompts\\`, so no backend changes are needed.\n\n## Tests\n\n15 cases in \\`name-tokens.test.ts\\`: whole-word boundaries, case sensitivity, idempotency, single-char/empty/regex-special guardrails, full \\`prepareDraftForSave\\` field coverage, previous-name during rename, round-trip with \\`replaceNameTokens\\`.\n\nSupersedes #7093.\n\n<!-- greptile_comment -->\n\n<h3>Greptile Summary</h3>\n\nThis PR adds `tokenizeNameOccurrences` to reverse the load-side `replaceNameTokens` expansion on save, so character renames propagate through all free-text fields. The core logic and tests are solid, but the save-side tokenization is not symmetric with the load-side de-tokenization.\n\n- **P1 — Editor shows raw `{{name}}` after save**: `prepareDraftForSave` now tokenizes `topics`, `postExamples`, `style.*`, and `messageExamples[].content.text`, but `loadCharacter` in `useCharacterState.ts` only calls `replaceNameTokens` on `bio` and `system`. Because `handleSaveCharacter` calls `loadCharacter()` on success, these fields immediately reload with raw `{{name}}` template syntax visible in the editor UI.\n\n<h3>Confidence Score: 3/5</h3>\n\nNot safe to merge — the load/save asymmetry will cause raw {{name}} tokens to appear in the editor immediately after any save.\n\nOne P1 defect: loadCharacter in useCharacterState.ts does not de-tokenize the four field groups that prepareDraftForSave now tokenizes. Because handleSaveCharacter calls loadCharacter() on success, users will see {{name}} as literal text in the topics, post-examples, style, and message-example editors after every save.\n\npackages/app-core/src/state/useCharacterState.ts — the loadCharacter callback (lines 86–102) must be extended to apply replaceNameTokens to all newly-tokenized fields.\n\n<h3>Important Files Changed</h3>\n\n\n\n\n| Filename | Overview |\n|----------|----------|\n| packages/app-core/src/state/useCharacterState.ts | Adds characterData to the dependency array and passes it as previousName to prepareDraftForSave, but loadCharacter only de-tokenizes bio and system — leaving topics, postExamples, style.*, and messageExamples with raw {{name}} tokens visible in the editor after each save. |\n| packages/app-core/src/character/character-draft-helpers.ts | Correctly introduces a tokenize closure that covers all free-text fields on save; adjectives are intentionally excluded. Implementation is well-structured. |\n| packages/app-core/src/utils/name-tokens.ts | New tokenizeNameOccurrences is case-sensitive, whole-word, idempotent, and safely escapes regex metacharacters; single-char guard is correct. |\n| packages/app-core/src/utils/name-tokens.test.ts | Comprehensive 15-case test suite covering tokenization, idempotency, edge guards, and round-trip with replaceNameTokens; no issues found. |\n\n</details>\n\n\n\n<h3>Sequence Diagram</h3>\n\n```mermaid\nsequenceDiagram\n    participant Editor\n    participant useCharacterState\n    participant prepareDraftForSave\n    participant Server\n\n    Editor->>useCharacterState: handleSaveCharacter()\n    useCharacterState->>prepareDraftForSave: draft + previousName\n    prepareDraftForSave->>prepareDraftForSave: tokenize(bio, system, topics, postExamples, style.*, messageExamples)\n    prepareDraftForSave-->>useCharacterState: tokenized draft\n    useCharacterState->>Server: updateCharacter(draft)\n    Server-->>useCharacterState: success\n    useCharacterState->>useCharacterState: loadCharacter()\n    useCharacterState->>Server: getCharacter()\n    Server-->>useCharacterState: character with {{name}} in all fields\n    useCharacterState->>useCharacterState: clean(bio) ✅ clean(system) ✅ topics as-is ❌ postExamples as-is ❌ style.* as-is ❌ messageExamples as-is ❌\n    useCharacterState-->>Editor: draft with raw {{name}} visible in topics/style/etc\n```\n\n<!-- greptile_failed_comments -->\n<details><summary><h3>Comments Outside Diff (1)</h3></summary>\n\n1. `packages/app-core/src/state/useCharacterState.ts`, line 86-102 ([link](https://github.com/elizaos/eliza/blob/406f30fea4094536e09fce7c72935c8b668edf2b/packages/app-core/src/state/useCharacterState.ts#L86-L102)) \n\n   <a href=\"#\"><img alt=\"P1\" src=\"https://greptile-static-assets.s3.amazonaws.com/badges/p1.svg?v=7\" align=\"top\"></a> **Load-side de-tokenization is incomplete — users will see raw `{{name}}` in the editor**\n\n   `prepareDraftForSave` now tokenizes `topics`, `postExamples`, `style.*`, and `messageExamples[].content.text`, but `loadCharacter` (lines 86–102) only calls `clean` on `bio` and `system`. After a successful save, `handleSaveCharacter` calls `loadCharacter()` (line 137), which reloads the character without de-tokenizing those four field groups — so the user immediately sees `{{name}}` as raw text in the topics, style, post-examples, and message-example editors.\n\n   Concrete path: save character \"Momo\" with topic `\"how Momo thinks\"` → stored as `\"how {{name}} thinks\"` → `loadCharacter` puts it back into the draft untouched → editor renders `\"how {{name}} thinks\"`.\n\n   Fix: extend the `clean` pass in `loadCharacter` to cover all fields that `prepareDraftForSave` now tokenizes:\n\n   ```\n   topics: (character.topics ?? []).map(clean),\n   postExamples: (character.postExamples ?? []).map(clean),\n   style: {\n     all: (character.style?.all ?? []).map(clean),\n     chat: (character.style?.chat ?? []).map(clean),\n     post: (character.style?.post ?? []).map(clean),\n   },\n   messageExamples: (character.messageExamples ?? []).map((group) => ({\n     ...group,\n     examples: group.examples.map((ex) => ({\n       ...ex,\n       content: { ...ex.content, text: clean(ex.content.text) },\n     })),\n   })),\n   ```\n\n</details>\n\n<!-- /greptile_failed_comments -->\n\n<sub>Reviews (1): Last reviewed commit: [\"fix(character): tokenize name occurrence...\"](https://github.com/elizaos/eliza/commit/406f30fea4094536e09fce7c72935c8b668edf2b) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=29694663)</sub>\n\n> Greptile also left **2 inline comments** on this PR.\n\n<!-- /greptile_comment -->",
      "repository": "elizaos/eliza",
      "createdAt": "2026-04-25T05:32:13Z",
      "mergedAt": "2026-04-25T05:32:29Z",
      "additions": 251,
      "deletions": 12
    },
    {
      "id": "PR_kwDOMT5cIs7VOtvB",
      "title": "fix(agent): deliver swarm synthesis to the triggering room",
      "author": "NubsCarson",
      "number": 7090,
      "body": "## Summary\n\nThe swarm synthesis path was broken in three stacked ways. When a task agent finished its work (opens a PR, builds a page, fetches data, etc.), nothing was posted back to the triggering room — the agent silently dropped its final result on the floor.\n\n### 1. `setSwarmCompleteCallback` was a no-op\n\n`wireCodingAgentSwarmSynthesis` installed a deliberate no-op with a comment claiming *\"synthesis happens via the streamer instead.\"* In practice `handleSwarmSynthesis` (defined in the same file) is never called anywhere in the repo. Task agents completed, `swarm_complete` fired, the callback ran, returned — no user-facing message was ever emitted.\n\n**Fix:** wire the callback to `await handleSwarmSynthesis(st, payload)`.\n\n### 2. `routeSynthesisToConnector` gated on a field that's never assigned\n\nThe connector-routing path early-returns when `coordinator.sourceRoomId` is null. That field is declared on the coordinator interface but **never assigned anywhere in the orchestrator**, so the path was always dead.\n\n**Fix:** thread a `fallbackRoomId` parameter through `routeSynthesisToConnector`. Derive it from the most recently terminal task in the payload — the task whose completion fired `swarm_complete`, and whose room is waiting for an answer. Naively taking *\"first task with a roomId\"* leaked results into stale rooms when the coordinator carried tasks across rooms. `coordinator.sourceRoomId` is still preferred when set, so any future direct assignment still wins.\n\n### 3. Synthesis rendered a meta-summary instead of the actual result\n\n`buildTaskResultLine` used the coordinator's `completionSummary` as the user-facing output. That's a meta-judgment about *whether* the task finished (\"The agent delivered a complete evening news brief with 7 headlines...\"), not the content the agent produced. The real deliverable — the brief itself, the PR URL line, the built page URL — was sitting unread in the task agent's `claude-code` session transcript.\n\n**Fix:** read the newest `.jsonl` at `~/.claude/projects/<sanitized-workdir>/` and return the last assistant message as the primary synthesis content. Only fall through to `completionSummary` (and then to port-heuristic text) when the jsonl can't be read. The workdir sanitizer replaces both `/` and `.` with `-` so hidden paths like `/home/u/.milady/workspaces/<id>` resolve to their matching project directory.\n\n## Changes\n\nOne file: `packages/agent/src/api/server-helpers-swarm.ts` (+107, -6).\n\n- `wireCodingAgentSwarmSynthesis` now invokes `handleSwarmSynthesis`\n- `handleSwarmSynthesis` payload carries per-task `roomId` and `workdir`\n- `routeSynthesisToConnector` accepts an optional `fallbackRoomId`\n- New `readAgentFinalAssistantMessage(workdir)` helper reads the agent's final message from its claude-code transcript\n- `buildTaskResultLine` prefers the agent's real output over the coordinator's meta-summary\n\n## Test plan\n\n- [x] Verified end-to-end on a real Discord-triggered delegation: user prompt → planner picks SPAWN_AGENT → task agent completes → synthesis posts the actual deliverable into the originating Discord room. Log shows:\n  ```\n  [swarm-synthesis] Generating synthesis for 1 tasks (1 completed, 0 stopped, 0 errored)\n  [swarm-synthesis] Synthesis generated, routing to user\n  [swarm-synthesis] Routed result to discord room <uuid>\n  ```\n- [x] Multi-task swarm where some tasks span different rooms: synthesis lands in the correct (most recently terminal) room, not in a stale room from a sibling task.\n- [x] `tsc --noEmit` clean in `packages/agent`.\n\n## Compatibility notes\n\n- Behavior for turns where `coordinator.sourceRoomId` is actually set (any downstream consumer that populates it): unchanged — still preferred over the fallback.\n- Behavior when the jsonl isn't readable (non-claude task agents, race at early-kill time, etc.): falls back to existing `completionSummary` / port-heuristic path, so no regression vs. pre-patch rendering.\n- Net effect for users relying on the synthesis NOT running (none observed, but noting it): they now get a post-task message where they previously got silence. If that's desired behavior for some deployment, it should be guarded explicitly rather than left to a silent-drop bug.\n",
      "repository": "elizaos/eliza",
      "createdAt": "2026-04-24T06:14:55Z",
      "mergedAt": "2026-04-25T05:30:41Z",
      "additions": 107,
      "deletions": 6
    }
  ],
  "codeChanges": {
    "additions": 9287,
    "deletions": 1158,
    "files": 86,
    "commitCount": 69
  },
  "completedItems": [
    {
      "title": "fix(message): stop leaking internal mechanism words in transient-failure replies",
      "prNumber": 7098,
      "type": "bugfix",
      "body": "## Summary\n\n`buildStructuredFailureReply` previously instructed the recovery model to *\"Explain what failed and why using only the diagnostics below\"* and fed it raw structured-output diagnostics plus action-result summaries. The resulting ",
      "files": [
        "packages/typescript/src/services/message.ts"
      ]
    },
    {
      "title": "fix(message): drop passive bookkeeping actions when delegation owns the turn",
      "prNumber": 7097,
      "type": "bugfix",
      "body": "## Summary\n\n`stripReplyWhenActionOwnsTurn` previously only suppressed `REPLY` when another action with `suppressPostActionContinuation = true` (e.g. `SPAWN_AGENT`) was selected for the same turn. The planner commonly also picks `MANAGE_TASK",
      "files": [
        "packages/typescript/src/services/message.ts"
      ]
    },
    {
      "title": "fix(agent): stop workbench task list from leaking runtime scheduler tasks",
      "prNumber": 7095,
      "type": "bugfix",
      "body": "## Summary\n\nTwo stacked issues caused the agent to dump its internal task table into chat whenever the planner picked `MANAGE_TASKS` with `operation=list`. A prompt like *\"doomscroll the web for me\"* or any vague phrase could produce a Disc",
      "files": [
        "packages/agent/src/actions/manage-tasks.ts",
        "packages/agent/src/api/workbench-helpers.ts"
      ]
    },
    {
      "title": "fix(auth): distinguish Claude Code CLI credentials from in-app OAuth",
      "prNumber": 7094,
      "type": "bugfix",
      "body": "## Problem\r\nWhen the Claude Code CLI is installed and authenticated on the user's machine, the Anthropic subscription panel in Settings → AI Models incorrectly reports \"Connected to Claude Subscription\" with an active Disconnect button — ev",
      "files": [
        "packages/agent/src/auth/credentials.ts",
        "packages/app-core/src/components/settings/ProviderSwitcher.tsx",
        "packages/app-core/src/components/settings/SubscriptionStatus.tsx",
        "packages/app-core/src/i18n/locales/en.json",
        "packages/shared/src/contracts/onboarding.ts"
      ]
    },
    {
      "title": "feat: open app surfaces in managed desktop windows",
      "prNumber": 7092,
      "type": "feature",
      "body": "## Plan\n- Open app catalog selections in managed Electrobun windows using the existing native toolbar-backed surfaces.\n- Keep detached windows connected to page-scoped chat/control context.\n- Add targeted coverage for always-on-top controls",
      "files": [
        "packages/agent/src/api/conversation-metadata.test.ts",
        "packages/agent/src/api/conversation-metadata.ts",
        "packages/agent/src/api/server-types.ts",
        "packages/agent/src/providers/page-scoped-context.ts",
        "packages/app-core/platforms/electrobun/src/index.ts",
        "packages/app-core/platforms/electrobun/src/native/canvas.test.ts",
        "packages/app-core/platforms/electrobun/src/native/canvas.ts",
        "packages/app-core/platforms/electrobun/src/native/desktop.ts",
        "packages/app-core/platforms/electrobun/src/rpc-handlers.ts",
        "packages/app-core/platforms/electrobun/src/rpc-schema.ts",
        "packages/app-core/platforms/electrobun/src/surface-windows.test.ts",
        "packages/app-core/platforms/electrobun/src/surface-windows.ts",
        "packages/app-core/scripts/dev-platform.mjs",
        "packages/app-core/src/components/apps/GameView.tsx",
        "packages/app-core/src/components/pages/AppsPageView.tsx",
        "packages/app-core/src/components/pages/AppsView.test.tsx",
        "packages/app-core/src/components/pages/AppsView.tsx",
        "packages/app-core/src/components/pages/page-scoped-conversations.ts",
        "packages/app-core/src/navigation/index.ts",
        "packages/app-core/src/shell/DetachedShellRoot.tsx",
        "packages/app-core/src/state/AppContext.tsx",
        "packages/app-core/src/state/useNavigationState.ts"
      ]
    },
    {
      "title": "fix(agent): deliver swarm synthesis to the triggering room",
      "prNumber": 7090,
      "type": "bugfix",
      "body": "## Summary\n\nThe swarm synthesis path was broken in three stacked ways. When a task agent finished its work (opens a PR, builds a page, fetches data, etc.), nothing was posted back to the triggering room — the agent silently dropped its fina",
      "files": [
        "packages/agent/src/api/server-helpers-swarm.ts"
      ]
    },
    {
      "title": "fix(character): tokenize name occurrences on save so renames propagate",
      "prNumber": 7101,
      "type": "bugfix",
      "body": "Cherry-picked from #7093 (by @dutchiono) — only the character rename fix, without the unrelated submodule pointer bumps, Windows pty patch, and coding-agent chip changes that #7093 also carried.\n\n## Problem\n\nThe character editor resolves \\`",
      "files": [
        "packages/app-core/src/character/character-draft-helpers.ts",
        "packages/app-core/src/state/useCharacterState.ts",
        "packages/app-core/src/utils/name-tokens.test.ts",
        "packages/app-core/src/utils/name-tokens.ts"
      ]
    },
    {
      "title": "feat(agent): x402 paid plugin routes and test harness hardening",
      "prNumber": 7100,
      "type": "feature",
      "body": "- Add x402 seller middleware: payment gate on plugin routes, startup validation, replay/facilitator/standard payment flows, and Vitest coverage.\r\n- Extend Route and payment types in @elizaos/core; document x402 in Mintlify and link from web",
      "files": [
        "cloud",
        "packages/agent/CHANGELOG.md",
        "packages/agent/README.md",
        "packages/agent/package.json",
        "packages/agent/scripts/test-x402-plugin-route.ts",
        "packages/agent/src/actions/context-signal.ts",
        "packages/agent/src/actions/grounded-action-reply.ts",
        "packages/agent/src/actions/index.ts",
        "packages/agent/src/api/__tests__/runtime-plugin-routes-x402.test.ts",
        "packages/agent/src/api/index.ts",
        "packages/agent/src/api/lifeops-browser-packaging.ts",
        "packages/agent/src/api/runtime-plugin-routes.ts",
        "packages/agent/src/api/server-helpers-config.ts",
        "packages/agent/src/api/server.ts",
        "packages/agent/src/middleware/x402/__tests__/apply-payment-protection.test.ts",
        "packages/agent/src/middleware/x402/__tests__/payment-config.builtins.test.ts",
        "packages/agent/src/middleware/x402/__tests__/startup-validator.test.ts",
        "packages/agent/src/middleware/x402/__tests__/x402-facilitator-binding.test.ts",
        "packages/agent/src/middleware/x402/__tests__/x402-replay-durable.test.ts",
        "packages/agent/src/middleware/x402/__tests__/x402-replay-guard.test.ts",
        "packages/agent/src/middleware/x402/__tests__/x402-replay-keys.test.ts",
        "packages/agent/src/middleware/x402/__tests__/x402-resolve.test.ts",
        "packages/agent/src/middleware/x402/__tests__/x402-standard-payment.test.ts",
        "packages/agent/src/middleware/x402/index.ts",
        "packages/agent/src/middleware/x402/payment-config.ts",
        "packages/agent/src/middleware/x402/payment-wrapper.ts",
        "packages/agent/src/middleware/x402/startup-validator.ts",
        "packages/agent/src/middleware/x402/types.ts",
        "packages/agent/src/middleware/x402/x402-facilitator-binding.ts",
        "packages/agent/src/middleware/x402/x402-replay-durable.ts",
        "packages/agent/src/middleware/x402/x402-replay-guard.ts",
        "packages/agent/src/middleware/x402/x402-replay-keys.ts",
        "packages/agent/src/middleware/x402/x402-resolve.ts",
        "packages/agent/src/middleware/x402/x402-standard-payment.ts",
        "packages/agent/src/middleware/x402/x402-types.ts",
        "packages/agent/src/onboarding-presets.ts",
        "packages/agent/src/providers/index.ts",
        "packages/agent/src/runtime/task-heartbeat.ts",
        "packages/app-core/package.json",
        "packages/app-core/scripts/electrobun-release-workflow-drift.test.ts",
        "packages/app-core/scripts/lib/repo-root.mjs",
        "packages/app-core/scripts/run-mobile-build.test.ts",
        "packages/app-core/scripts/startup-integration-script-drift.test.ts",
        "packages/app-core/src/components/shell/ComputerUseApprovalOverlay.test.tsx",
        "packages/app-core/test/app/memory-relationships.real.e2e.test.ts",
        "packages/app-core/test/app/onboarding-companion.live.e2e.test.ts",
        "packages/app-core/test/app/qa-checklist.real.e2e.test.ts",
        "packages/app-core/test/live-agent/agent-runtime.live.e2e.test.ts",
        "packages/app-core/test/live-agent/api-auth-live.e2e.test.ts",
        "packages/app-core/test/live-agent/cloud-auth.live.e2e.test.ts"
      ]
    },
    {
      "title": "fix(message): tolerate malformed planner action XML",
      "prNumber": 7099,
      "type": "bugfix",
      "body": "## Summary\n\nThe planner LLM occasionally emits an unclosed `<action>` wrapper around a well-formed `<name>X</name>`, e.g.:\n\n```xml\n<actions><action><name>REPLY</name></actions>\n```\n\n(closing `</action>` is missing). The strict `<action>...<",
      "files": [
        "packages/typescript/src/services/message.test.ts",
        "packages/typescript/src/services/message.ts"
      ]
    }
  ],
  "topContributors": [
    {
      "username": "lalalune",
      "avatarUrl": "https://avatars.githubusercontent.com/u/18633264?u=e2e906c3712c2506ebfa98df01c2cfdc50050b30&v=4",
      "totalScore": 46.35189820629263,
      "prScore": 46.15189820629263,
      "issueScore": 0,
      "reviewScore": 0,
      "commentScore": 0.2,
      "summary": "lalalune: Focused on improving character data integrity by merging PR #7101 in elizaos/eliza, which implemented tokenization for name occurrences during save operations. Their work today involved extensive codebase modifications across 61 commits, primarily centered on general maintenance and bugfix efforts."
    },
    {
      "username": "odilitime",
      "avatarUrl": "https://avatars.githubusercontent.com/u/16395496?u=c9bac48e632aae594a0d85aaf9e9c9c69b674d8b&v=4",
      "totalScore": 42.9457738965761,
      "prScore": 42.9457738965761,
      "issueScore": 0,
      "reviewScore": 0,
      "commentScore": 0,
      "summary": "odilitime: Drove a significant architectural update to the agent framework by merging PR #7100 in elizaos/eliza, which implemented x402 paid plugin routes and hardened the test harness. This extensive contribution involved modifying 11,166 files, reflecting a balanced focus across feature development, bug fixes, testing, and general codebase maintenance."
    },
    {
      "username": "NubsCarson",
      "avatarUrl": "https://avatars.githubusercontent.com/u/192162056?u=d2be9082dbee60fcbad21d32bf6e662ab1af3674&v=4",
      "totalScore": 39.63472898822964,
      "prScore": 39.63472898822964,
      "issueScore": 0,
      "reviewScore": 0,
      "commentScore": 0,
      "summary": "NubsCarson: Focused on enhancing system reliability by resolving a critical bug in elizaos/eliza via PR #7099, which ensures the planner correctly handles malformed XML actions. Their work today involved 9 commits across 14 files, with a primary emphasis on bugfix efforts balanced equally between code and test implementation."
    },
    {
      "username": "re-bruce-wayne",
      "avatarUrl": "https://avatars.githubusercontent.com/u/196209605?u=0f106df5aeadfc126ee7ca9d05c34171fe5b6476&v=4",
      "totalScore": 14.346573590279972,
      "prScore": 14.346573590279972,
      "issueScore": 0,
      "reviewScore": 0,
      "commentScore": 0,
      "summary": "re-bruce-wayne: Focused on expanding ecosystem integrations by opening PR #350 in elizaos-plugins/registry to add the @traderouter/plugin-elizaos."
    },
    {
      "username": "sailorpepe",
      "avatarUrl": "https://avatars.githubusercontent.com/u/159828807?u=9298c48eb8b41b0a0b0968e987c029e307771855&v=4",
      "totalScore": 11.32071895621705,
      "prScore": 11.32071895621705,
      "issueScore": 0,
      "reviewScore": 0,
      "commentScore": 0,
      "summary": "sailorpepe: Focused on expanding the ecosystem by opening PR #351 in elizaos-plugins/registry to integrate new TCG and undesirable plugins. This work involved a balanced effort of feature implementation and bug fixing within configuration files."
    },
    {
      "username": "greptile-apps",
      "avatarUrl": "https://avatars.githubusercontent.com/in/867647?v=4",
      "totalScore": 9,
      "prScore": 0,
      "issueScore": 0,
      "reviewScore": 9,
      "commentScore": 0,
      "summary": "greptile-apps: No activity today."
    },
    {
      "username": "fel123",
      "avatarUrl": "https://avatars.githubusercontent.com/u/33407903?v=4",
      "totalScore": 0.2,
      "prScore": 0,
      "issueScore": 0,
      "reviewScore": 0,
      "commentScore": 0.2,
      "summary": null
    }
  ],
  "newPRs": 3,
  "mergedPRs": 9,
  "newIssues": 0,
  "closedIssues": 0,
  "activeContributors": 17
}