feat(conversation-chip): brand mark + model name per conversation#708
feat(conversation-chip): brand mark + model name per conversation#708simonrosenberg wants to merge 4 commits into
Conversation
For each conversation card, show a single inline chip: [brand mark] <model text> Mirrors OpenHands' main web UI conversation chip (PR #14510) so the two products converge on the same identity affordance. How the text is sourced: - OpenHands native conversations: ``agent.llm.model`` (unchanged), gated behind the existing ``showLlmProfiles`` preference, rendered next to the OpenHands logo. - ACP conversations: ``ConversationInfo.current_model_name`` lifted by the agent-server from software-agent-sdk PR #3347 (``ACPAgent.current_model_id`` resolved through ``available_models`` to a human-readable name — ``"Default (recommended)"``, ``"GPT-5.5 (xhigh)"``, ``"Opus 4.1"``, …). Falls through to ``current_model_id`` then to the provider brand display name when older SDK builds don't lift either field. Hover tooltip always includes the harness identity for unambiguous attribution. Pinning to the unreleased SDK PR: - ``config/defaults.json`` gains an ``agentServerGitRef`` field set to the PR's latest commit (235fed007). ``scripts/dev-safe.mjs`` reads it and threads the SHA into the existing uvx git-ref install path so the dev stack picks up the new ``ConversationInfo.current_model_*`` fields without waiting for a PyPI release. Env vars ``OH_AGENT_SERVER_LOCAL_PATH`` / ``OH_AGENT_SERVER_GIT_REF`` / ``OH_AGENT_SERVER_VERSION`` still override; the in-repo default just promotes git-ref above the PyPI fallback. Clear the field once the SDK PR merges and a release ships. Shape: - New ``src/components/shared/agent-brand-icon.tsx`` renders the brand glyph per kind (``openhands`` | ``claude-code`` | ``codex`` | ``gemini`` | ``cli-generic``) reusing the path data already inlined for the onboarding tile (extracted into ``src/constants/acp-brand-marks.ts``). - ``resolveAcpProviderIcon(key)`` added alongside the existing display-name resolver. - ``ConversationCardFooter`` now renders one chip via ``AgentBrandIcon`` + text + tooltip — replaces the standalone ACP pill and the separate LLM-model row. - ``DirectConversationInfo`` typed for ``current_model_id`` / ``current_model_name``; the adapter chains ``current_model_name → current_model_id → null`` for ACP conversations and leaves OpenHands conversations on ``agent.llm.model``. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The previous pin (235fed007) populated ``current_model_*`` only while
the ACP subprocess was live; idle / cold-listed conversations came back
with both fields null and the chip fell back to the provider brand
("Claude Code") instead of the model name. The new SDK commit persists
the resolved id + name into ``agent_state`` and reads them back on
cold conversation reads in ``_compose_conversation_info``, so the chip
shows the model on every ACP conversation in the sidebar.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
all-hands-bot
left a comment
There was a problem hiding this comment.
🟢 Good taste - Clean implementation that solves the problem elegantly.
Summary
This PR successfully adds conversation chip functionality showing both agent harness brand marks and model names. The implementation is straightforward, well-tested, and maintains backward compatibility.
Key strengths:
- Clean data structure with proper fallback chain:
current_model_name → current_model_id → null - Comprehensive test coverage (127 tests passing)
- Simple, maintainable component design
- Good backward compatibility (optional fields, graceful degradation)
- Dev workflow properly handles SDK git ref precedence
Notes:
- ✅ Draft status is intentional (waiting for SDK PR #3347 to merge)
- ✅ Breaking testid change (
conversation-card-acp-badge→conversation-card-agent-chip) is documented - ✅ All tests pass, linting clean
[RISK ASSESSMENT]
- [Overall PR]
⚠️ Risk Assessment: 🟢 LOW
This is a frontend UI enhancement with no security implications, no breaking API changes (only a testid rename that's documented), and comprehensive test coverage. The SDK pin to an unmerged PR is intentional for development and will be removed once the SDK PR ships.
VERDICT:
✅ Worth merging once the SDK dependency (PR #3347) is merged and released. The code itself is ready.
KEY INSIGHT:
The adapter's field-chaining pattern (current_model_name ?? current_model_id ?? null) elegantly handles backward compatibility across three SDK generations without adding complexity.
Was this automated review useful? React with 👍 or 👎 to this review to help us measure review quality.
Workflow run: https://github.com/OpenHands/agent-canvas/actions/runs/26234359911
…name)
Previous pin (e6e9771) surfaced ``ModelInfo.name`` which for
claude-agent-acp's generic aliases is still an alias
("Default (recommended)") — not what's actually running. The new SDK
commit extracts the resolved model identity from ``ModelInfo.description``
for known aliases, so the chip now reads "Opus 4.7 with 1M context" /
"Sonnet 4.6" / "Haiku 4.5" instead.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #3347 alone tripped a pre-existing deadlock in \`EventService._setup_stats_streaming\` (introduced in #3308) where the stats_callback re-acquires the conversation state lock the caller already holds, hanging every ACP turn before the assistant's FinishAction is emitted. The new pin merges PR #3349 — which drops the redundant \`with state:\` — into our PR branch so both the chip data and the response flow work together. Drop both pins once #3347 and #3349 merge to main and a new SDK release ships.
📸 Snapshot Test ReportWarning Snapshot comparison step crashed (timeout, OOM, or runner error) — diff results below may be incomplete or absent. ❌ 11 snapshots differ from the main branch baselines. Add the
🔴 Changed snapshots (11)
|
| Expected (main) | Actual (PR) | Diff |
|---|---|---|
![]() |
![]() |
![]() |
mcp-custom-server-editor
| Expected (main) | Actual (PR) | Diff |
|---|---|---|
![]() |
![]() |
![]() |
mcp-empty-installed
| Expected (main) | Actual (PR) | Diff |
|---|---|---|
![]() |
![]() |
![]() |
mcp-search-filtered
| Expected (main) | Actual (PR) | Diff |
|---|---|---|
![]() |
![]() |
![]() |
mcp-slack-install-1-marketplace
| Expected (main) | Actual (PR) | Diff |
|---|---|---|
![]() |
![]() |
![]() |
onboarding
onboarding-step-2-setup-llm
| Expected (main) | Actual (PR) | Diff |
|---|---|---|
![]() |
![]() |
![]() |
skills-page — 5 snapshots
skills-empty
| Expected (main) | Actual (PR) | Diff |
|---|---|---|
![]() |
![]() |
![]() |
skills-loaded
| Expected (main) | Actual (PR) | Diff |
|---|---|---|
![]() |
![]() |
![]() |
skills-no-match
| Expected (main) | Actual (PR) | Diff |
|---|---|---|
![]() |
![]() |
![]() |
skills-search-filtered
| Expected (main) | Actual (PR) | Diff |
|---|---|---|
![]() |
![]() |
![]() |
skills-type-filter
| Expected (main) | Actual (PR) | Diff |
|---|---|---|
![]() |
![]() |
![]() |
✅ Unchanged snapshots (62)
archived-conversation
- conversation-panel-with-archived-badges
- conversation-view-archived
- conversation-view-sandbox-error
automations
- automations-delete-modal
- automations-list-active-inactive
- automations-no-automations
- automations-search-no-results
backends-extended
- backend-add-blank-disabled
- backend-add-cloud-advanced-open
- backend-add-cloud-no-key-disabled
- backend-add-cloud-with-key-enabled
- backend-add-form-partially-filled
- backend-add-invalid-url-disabled
- backend-add-local-ready
- backend-add-name-only-disabled
- backend-add-two-column-layout
- backend-add-whitespace-host-disabled
- backend-after-switch
- backend-cancel-nothing-saved
- backend-dropdown-two-backends
- backend-edit-prefilled
- backend-manage-after-removal
- backend-manage-two-listed
- backend-remove-cancelled
- backend-remove-confirmation
- backend-switch-overlay
backends
- backend-add-modal
- backend-manage-modal
- backend-selector-open
changes-tab
- changes-deleted-file
- changes-diff-viewer
- changes-empty
collapsible-thinking
- reasoning-content-collapsed
- reasoning-content-expanded
- think-action-collapsed
- think-action-expanded
mcp-page
- mcp-custom-server-2-url-filled
- mcp-custom-server-3-all-filled
- mcp-custom-server-4-installed
- mcp-slack-install-2-modal
- mcp-slack-install-3-filled
- mcp-slack-install-4-installed
onboarding
- onboarding-step-0-choose-agent
- onboarding-step-1-check-backend
- onboarding-step-3-say-hello
projects-workspace-browser
- projects-workspace-browser
settings-page
- add-backend-modal
- analytics-consent-modal
- home-screen
- settings-app-page
- settings-page
settings-secrets
- secrets-add-form-filled
- secrets-add-form
- secrets-after-save
- secrets-delete-confirm
- secrets-list
settings-verification
- condenser-settings
- verification-settings-off
- verification-settings-on
sidebar
- sidebar-collapsed
- sidebar-conversation-panel
- sidebar-filter-menu
Generated by the Snapshot Tests workflow. This comment was created by an AI agent (OpenHands) on behalf of the repo maintainers.

































Why
Each conversation card should communicate which agent harness is running it and which model that harness is using. Today the ACP card shows a small "Claude Code" / "Codex" pill (harness only — no model), and OpenHands cards optionally show a plain text model name (model only — no logo). Mirror the chip we just shipped in the OpenHands web UI (OpenHands#14510) so both products converge: brand-mark icon + model text on a single inline row.
For ACP, the model name was previously unavailable to the frontend. The companion SDK PR software-agent-sdk#3347 adds
ConversationInfo.current_model_name(a human-readable name resolved via the ACPavailable_modelslookup —"Default (recommended)"/"GPT-5.5 (xhigh)"/"Opus 4.1"…) andConversationInfo.current_model_id. This PR pins the dev stack to that SDK PR's SHA so the new fields are actually populated, and renders them in the chip.Summary
AgentBrandIconcomponent renders the right glyph per harness (openhands|claude-code|codex|gemini|cli-genericfallback). Path data extracted from the existing onboarding tile intosrc/constants/acp-brand-marks.tsfor reuse.ConversationCardFooterswitches from the standalone pill + separate model row to a single inline chip:[brand mark] {model text}. Tooltip always carries the full harness + model so the chip is unambiguous on hover.DirectConversationInfotyped for the newcurrent_model_name/current_model_idfields. Adapter chainscurrent_model_name → current_model_id → nullfor ACP conversations; OpenHands conversations continue to source fromagent.llm.model(unchanged).config/defaults.jsongainsagentServerGitRef: "235fed007c3c49841d34a92fb4b16d8877d6d82e".scripts/dev-safe.mjsreads it and routes the existinguvxgit-ref install path so the dev stack picks up the SDK PR. Env vars still override; clear the field once the SDK ships.Issue Number
None — follow-up to OpenHands#14510 and depends on software-agent-sdk#3347.
How to Test
config/defaults.jsonalready pins the SDK to PR #3347's commit, so the dev stack will pull that SDK build viauvxautomatically — watch foragent-server source: git (235fed007c3c49841d34a92fb4b16d8877d6d82e)in the startup log to confirm the pin is in effect.Then:
"Default (recommended)"for default config;"Opus 4.1"if you setacp_model="claude-opus-4-1")."GPT-5.5 (xhigh)"(or whatever the activegpt-X.Y/<tier>is).showLlmProfilesfor a native OpenHands conversation. Chip should show the OpenHands logo + the rawagent.llm.modelstring.<provider> · <model>for ACP conversations (e.g."Claude Code · Default (recommended)").Video/Screenshots
To attach after a reviewer eyeballs the dev stack — the visible change is the chip rendering on the conversation list.
Type
Notes
versions.agentServerinconfig/defaults.jsonto the new release and remove theagentServerGitReffield. The chip render path needs no further change once the SDK pin moves.conversation-card-acp-badgetestid is gone; replaced byconversation-card-agent-chip. If any downstream automation greps for the old testid, this is a breaking rename — flagging in case.current_model_*ontoConversationInfo. The adapter chain still resolves tonull, and the chip falls back to the provider brand text — same behavior as today, just rendered with the new icon-first layout.AgentBrandIcon) are nominative-use copies of the same path data the onboarding tile already ships underchoose-agent-step.tsx. No new asset/licensing decisions.🤖 Generated with Claude Code
🐳 Docker images for this PR
• GHCR package: https://github.com/OpenHands/agent-canvas/pkgs/container/agent-canvas
ghcr.io/openhands/agent-canvasghcr.io/openhands/agent-server:1.23.0-pythonopenhands-automation==1.0.0a362a115a9e355fa02aac3602c7c00e0c00951fcbdPull (multi-arch manifest)
# Multi-arch manifest — Docker automatically pulls the correct architecture docker pull ghcr.io/openhands/agent-canvas:sha-62a115aRun
All tags pushed for this build
About Multi-Architecture Support
sha-62a115a) is a multi-arch manifest supporting both amd64 and arm64sha-62a115a-amd64) are also available if needed