Skip to content

fix(ui): scope local assistant avatar override to agent ID (fixes #90890)#90932

Closed
xydigit-sj wants to merge 4 commits into
openclaw:mainfrom
xydigit-sj:fix/per-agent-avatar-override
Closed

fix(ui): scope local assistant avatar override to agent ID (fixes #90890)#90932
xydigit-sj wants to merge 4 commits into
openclaw:mainfrom
xydigit-sj:fix/per-agent-avatar-override

Conversation

@xydigit-sj

@xydigit-sj xydigit-sj commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Local assistant avatar overrides were stored under a single global localStorage key (openclaw.control.assistant.v1), so setting an avatar for one agent changed it for all agents. This PR scopes the override to the current agent ID and preserves legacy flat-key overrides created before this change.

Changes

  • ui/src/ui/storage.ts
    • loadLocalAssistantIdentity / saveLocalAssistantIdentity now accept an optional agentId parameter. When provided, the storage key becomes openclaw.control.assistant.v1:<agentId>.
    • loadLocalAssistantIdentity falls back to the legacy global key when no agent-scoped value exists, so existing avatar overrides are not silently lost after upgrade.
  • ui/src/ui/controllers/assistant-identity.ts: Threads state.assistantAgentId through loadAssistantIdentity and setAssistantAvatarOverride.
  • ui/src/ui/app-render.ts: Passes state.assistantAgentId when loading / saving the local avatar override.
  • ui/src/ui/controllers/control-ui-bootstrap.ts: Applies the agent-scoped local override during bootstrap.
  • ui/src/ui/controllers/assistant-identity.test.ts: Adds a regression test for the legacy flat-key fallback.

Backward compatibility

When no agentId is provided, the functions still fall back to the legacy global key, preserving existing behavior for unscoped callers. When an agentId is provided and no scoped override exists, the legacy global key is read so previously set avatars remain visible.

Verification

  • pnpm test ui/src/ui/controllers/assistant-identity.test.ts --run — 7/7 pass.
  • pnpm test ui/src/ui/app-render.assistant-avatar.test.ts --run — 16/16 pass.
  • pnpm test ui/src/ui/views/config-quick.test.ts --run — 14/14 pass.
  • pnpm test ui/src/ui/storage.node.test.ts --run — 20/20 pass.
  • pnpm test ui/src/ui/controllers/control-ui-bootstrap.test.ts --run — 10/10 pass.

Fixes #90890

Real behavior proof

Behavior addressed: Setting a local assistant avatar override for one agent overwrote the avatar for every agent because the override was stored under a single global localStorage key. Additionally, legacy overrides saved before per-agent scoping must not disappear after this change.

Real environment tested: Local Linux checkout of this branch, Node v22.22.0, pnpm 11.2.2, jsdom (Control UI test environment).

Exact steps or command run after this patch:

cd /home/0668000780/开源社区提交2/openclaw
git checkout fix/per-agent-avatar-override
NODE_LLAMA_CPP_POSTINSTALL=ignoreFailedBuild pnpm test ui/src/ui/controllers/assistant-identity.test.ts --run --reporter=verbose

The test suite exercises per-agent scoping, clearing overrides, and the legacy flat-key fallback using a real in-memory localStorage implementation.

Evidence after fix:

✓ |ui| ui/src/ui/controllers/assistant-identity.test.ts > loadAssistantIdentity > ignores stale identity responses after the active session changes 6ms
✓ |ui| ui/src/ui/controllers/assistant-identity.test.ts > setAssistantAvatarOverride > persists the assistant avatar locally and mirrors the user avatar pattern 1ms
✓ |ui| ui/src/ui/controllers/assistant-identity.test.ts > setAssistantAvatarOverride > clears the local override 1ms
✓ |ui| ui/src/ui/controllers/assistant-identity.test.ts > setAssistantAvatarOverride > scopes the local override to the given agentId 1ms
✓ |ui| ui/src/ui/controllers/assistant-identity.test.ts > setAssistantAvatarOverride > clears the agent-scoped local override 0ms
✓ |ui| ui/src/ui/controllers/assistant-identity.test.ts > setAssistantAvatarOverride > falls back to the legacy flat key when no agent-scoped override exists 0ms
✓ |ui| ui/src/ui/controllers/assistant-identity.test.ts > setAssistantAvatarOverride > applies the agent-scoped local override when loading identity 1ms

 Test Files  1 passed (1)
      Tests  7 passed (7)
   Start at  21:38:19
   Duration  544ms

Observed result after fix: Each agent gets its own localStorage key (openclaw.control.assistant.v1:<agentId>), changing the avatar for one agent leaves others unchanged, and a pre-existing legacy flat-key override is still honored when no scoped override exists.

What was not tested: A live browser screenshot of the Control UI avatar settings panel; the scoping and fallback behavior is exercised directly against the real storage functions.

Local assistant avatar overrides were stored under a single global
localStorage key, so setting an avatar for one agent changed it for
all agents.

- Make loadLocalAssistantIdentity / saveLocalAssistantIdentity accept
  an optional agentId and scope the storage key to the agent.
- Thread state.assistantAgentId through all callers:
  app-render, assistant-identity controller, control-ui-bootstrap.
- Preserve backward compatibility: when no agentId is provided the
  functions still use the legacy global key.

Fixes openclaw#90890
@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui size: S triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 6, 2026
@clawsweeper

clawsweeper Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 15, 2026, 9:45 AM ET / 13:45 UTC.

Summary
The PR scopes Control UI local assistant avatar localStorage reads and writes by agent ID, threads the ID through render/bootstrap/identity callers, adds legacy flat-key fallback, and updates focused tests.

PR surface: Source +15, Tests +83. Total +98 across 6 files.

Reproducibility: yes. source inspection gives a high-confidence reproduction path. Current main and v2026.6.6 read and write every local assistant avatar override through one unscoped localStorage key, matching the linked multi-agent overwrite report.

Review metrics: 2 noteworthy metrics.

  • Browser-local avatar storage semantics: 1 key family scoped, 1 legacy fallback added, 0 scoped clear sentinels. The PR changes a persisted browser preference, so upgrade and explicit clear behavior need maintainer attention before merge.
  • Duplicate avatar-fix branches: 4 open PRs. Multiple branches target the same linked bug, so maintainers should avoid divergent localStorage migration semantics.

Stored data model
Persistent data-model change detected: durable storage schema: ui/src/ui/storage.ts, serialized state: ui/src/ui/controllers/assistant-identity.test.ts, serialized state: ui/src/ui/storage.ts. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🧂 unranked krab
Result: blocked until real behavior proof from a real setup is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Store an explicit scoped null or clear marker so legacy fallback does not reappear after a user clears one agent.
  • [P1] Add redacted real Control UI browser proof covering two agents, avatar upload, switch, clear, and reload/bootstrap.
  • [P1] Coordinate with maintainers to choose one canonical branch among the open avatar-fix PRs.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR body only supplies Vitest/jsdom localStorage output; add redacted real browser proof for upload, agent switch, clear, and reload/bootstrap, then update the PR body to trigger re-review.

Mantis proof suggestion
A visible browser proof would materially reduce review risk for the Control UI avatar upload, switch, clear, and reload/bootstrap flow. A maintainer can ask Mantis to capture proof by posting a new PR comment that starts with the OpenClaw Mantis account mention, followed by:

visual task: verify Control UI assistant avatars stay independent for two agents after upload, switch, clear one, and reload/bootstrap.

Risk before merge

  • [P1] Existing users with a shipped flat openclaw.control.assistant.v1 avatar override can be unable to clear one agent's avatar, because clearing removes the scoped key and the loader falls back to the flat key.
  • [P1] The PR changes persisted browser-local storage semantics, so upgrade behavior needs explicit coverage before merge.
  • [P1] The supplied proof is Vitest/jsdom localStorage output, not a real Control UI browser flow with upload, agent switch, clear, and reload/bootstrap.
  • [P1] Several open PRs target the same linked avatar bug, so landing more than one branch could create divergent migration and clear semantics.

Maintainer options:

  1. Store A Scoped Clear Marker (recommended)
    Before merge, change scoped clears to persist an explicit null marker or equivalent so a cleared agent does not fall back to the legacy flat avatar.
  2. Require Browser Flow Proof
    Ask for redacted browser proof showing two agents through avatar upload, switching, clearing one override, and reload/bootstrap.
  3. Choose One Avatar Branch
    Pause duplicate avatar-fix branches until maintainers choose the canonical migration and proof story, then close the remaining branches after that lands.

Next step before merge

  • [P1] Human follow-up is needed for scoped-clear semantics, contributor-supplied browser proof, and canonical branch selection among duplicate open fixes.

Security
Cleared: No concrete security or supply-chain regression was found; the net diff only changes Control UI localStorage handling and adjacent tests.

Review findings

  • [P1] Block legacy fallback after scoped avatar clears — ui/src/ui/storage.ts:384
Review details

Best possible solution:

Land one canonical per-agent assistant-avatar storage fix that preserves legacy flat values on upgrade while blocking fallback after intentional scoped clears, with live Control UI proof for upload, switch, clear, and reload/bootstrap.

Do we have a high-confidence way to reproduce the issue?

Yes, source inspection gives a high-confidence reproduction path. Current main and v2026.6.6 read and write every local assistant avatar override through one unscoped localStorage key, matching the linked multi-agent overwrite report.

Is this the best way to solve the issue?

No, not as currently patched. Agent-scoped storage is the right layer, but this branch needs explicit scoped-clear semantics, live browser proof, and canonical-branch selection before it is the best merge path.

Full review comments:

  • [P1] Block legacy fallback after scoped avatar clears — ui/src/ui/storage.ts:384
    After this patch, a scoped load falls back to the legacy flat key whenever the scoped key is absent, but clearing a scoped override removes that scoped key. On upgraded profiles that still have the flat avatar value, pressing Clear for one agent will immediately redisplay the legacy avatar instead of leaving that agent cleared; store an explicit scoped null marker or otherwise block fallback after an intentional scoped clear.
    Confidence: 0.92

Overall correctness: patch is incorrect
Overall confidence: 0.91

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 66079161d72a.

Label changes

Label changes:

  • add merge-risk: 🚨 session-state: The changed localStorage value controls which assistant avatar appears for the active agent across switching and reload/bootstrap.

Label justifications:

  • P2: This is a normal-priority Control UI state bugfix with limited blast radius, but it affects persisted visible avatar state for multi-agent users.
  • merge-risk: 🚨 compatibility: The PR changes a shipped browser-local preference key and fallback behavior for existing users who already have a flat assistant avatar override.
  • merge-risk: 🚨 session-state: The changed localStorage value controls which assistant avatar appears for the active agent across switching and reload/bootstrap.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR body only supplies Vitest/jsdom localStorage output; add redacted real browser proof for upload, agent switch, clear, and reload/bootstrap, then update the PR body to trigger re-review.
Evidence reviewed

PR surface:

Source +15, Tests +83. Total +98 across 6 files.

View PR surface stats
Area Files Added Removed Net
Source 4 26 11 +15
Tests 2 84 1 +83
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 6 110 12 +98

What I checked:

  • Repository policy read: Root AGENTS.md and ui/AGENTS.md were read fully; persisted browser preference compatibility, whole-path review, and real behavior proof requirements affected this review. (AGENTS.md:13, 66079161d72a)
  • Current main stores one flat assistant avatar key: Current main load/save helpers read and write only openclaw.control.assistant.v1, so the local assistant avatar override is global rather than agent-scoped. (ui/src/ui/storage.ts:353, 66079161d72a)
  • Current main caller omits the agent id: The assistant identity controller applies the local avatar override with loadLocalAssistantIdentity() after resolving assistantAgentId, matching the cross-agent overwrite report. (ui/src/ui/controllers/assistant-identity.ts:71, 66079161d72a)
  • Latest release has the same shipped behavior: v2026.6.6 still reads and writes the same unscoped assistant avatar localStorage key, so the reported behavior is not already fixed in the latest release. (ui/src/ui/storage.ts:353, 8c802aa68351)
  • PR head reintroduces legacy avatars after scoped clears: At PR head, scoped loads fall back to the flat key when the scoped key is absent, while scoped clears remove the scoped key; an upgraded user's flat avatar can immediately reappear after clearing one agent. (ui/src/ui/storage.ts:384, 11bfcdb169a8)
  • PR tests miss the legacy-clear interaction: The added tests cover scoped clear and legacy fallback separately, but do not cover legacy flat key -> scoped override -> scoped clear, which is the upgrade path that still fails. (ui/src/ui/controllers/assistant-identity.test.ts:110, 11bfcdb169a8)

Likely related people:

  • BunsDev: PR metadata for fix(control-ui): persist assistant avatar override locally #71639 shows this account authored the browser-local assistant avatar persistence feature and touched the storage, controller, render, bootstrap, and Quick Settings surfaces involved here. (role: feature introducer and recent area contributor; confidence: high; commits: c65aa1d2a6e5, 6e6f7fcc3c6a; files: ui/src/ui/storage.ts, ui/src/ui/controllers/assistant-identity.ts, ui/src/ui/app-render.ts)
  • steipete: History shows nearby Control UI assistant identity and agent-avatar work before the local override feature, which is relevant to the active-agent boundary this PR threads through. (role: adjacent assistant identity contributor; confidence: medium; commits: 8544df36b87d, a59ac5cf6f94; files: ui/src/ui/app-render.ts, ui/src/ui/controllers/assistant-identity.ts)
  • Galin Iliev: Current-line blame in this shallow checkout points the UI storage/controller/render/bootstrap files at a broad recent import/update commit, which is a weak but useful routing signal. (role: recent area contributor; confidence: low; commits: 50c82b302006; files: ui/src/ui/storage.ts, ui/src/ui/controllers/assistant-identity.ts, ui/src/ui/app-render.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jun 6, 2026
@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 15, 2026
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 other 🚨 Merging this PR has meaningful risk outside the owned taxonomy. label Jun 15, 2026
… messages (backend-delegated)"

This reverts commit 151b6f4.
@clawsweeper clawsweeper Bot removed the merge-risk: 🚨 other 🚨 Merging this PR has meaningful risk outside the owned taxonomy. label Jun 15, 2026
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. label Jun 15, 2026
@openclaw-clownfish

Copy link
Copy Markdown
Contributor

Thanks @xydigit-sj for the focused fix work here. I am closing this PR as superseded by #91533 because both PRs track the same root cause from #90890: assistant avatar overrides were stored under one global localStorage key, so changing one agent's avatar affected other agents.

#91533 is the canonical path now: #91533. This source PR remains part of the audit trail at #90932, and the contribution context will stay visible for attribution as maintainers finish validation there. If this branch has a different reproduction path or still shows a gap after #91533 lands, please reply and we can reopen or split it back out.

@openclaw-clownfish openclaw-clownfish Bot added the clownfish Tracked by Clownfish automation label Jun 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui clownfish Tracked by Clownfish automation merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P2 Normal backlog priority with limited blast radius. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: S status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: You cannot give an avatar to a specific agent in settings

1 participant