Files
OpenHands/__tests__/constants
3d724dd8fd feat(acp): versioned ACP model picker + per-conversation agent chip (#730)
* feat(acp): save concrete model defaults in canvas

* test(acp): verify provider models from subprocesses

* feat(acp): per-conversation agent chip + versioned Claude model picker

User-visible changes:

- Conversation cards now show a single inline chip `[brand mark] {model}`
  on every conversation. ACP cards always render (identity info); OpenHands
  native cards render whenever `agent.llm.model` is present. New
  `AgentBrandIcon` covers Claude / Codex / Gemini brand marks with a
  terminal-glyph fallback; the OpenHands logo is recolored via
  `[&_path]:fill-current` so it inherits the muted-grey chip color
  (the shipped SVG hardcodes `fill="white"`).

- Settings → Agent dropdown for Claude Code lists 10 versioned options
  (Opus 4.7, 4.6, 4.6/1M, 4.5, 4.1; Sonnet 4.6, 4.6/1M, 4.5; Haiku 4.5;
  opusplan). Default switched from `sonnet` to `claude-opus-4-7`.
  Canonical IDs were verified against the bundled `claude` CLI binary's
  model registry — the static SDK `.mjs` shims don't carry the full
  registry. `[1m]` aliases used for 1M-context variants (no canonical
  `claude-*-1m` IDs ship in the SDK).

- `ConversationCardFooter` chip resolves the ACP model string from
  `current_model_name → current_model_id → agent.acp_model → agent.llm.model`
  (the `"acp-managed"` sentinel is filtered out), so the chip works
  whether or not the agent-server populates the SDK runtime fields.

- Removed dead `showLlmProfiles` plumbing on `ConversationCard` /
  `CompactConversationRow` / `ConversationPanel` pass-throughs that
  used to gate the OpenHands model line behind a metadata-menu toggle.
  Chip is now always shown when a model exists.

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

* refactor(acp): adapter cleanup + drop unauthenticated model-check script

- Extract `resolveAcpDisplayModel(info)` from the triple-nested ternary
  in `toAppConversation`. Same precedence chain (runtime name → runtime
  id → configured model → llm.model minus sentinel), just expressed once
  with a loop and named conditions.

- Name the `"acp-managed"` literal as `ACP_MANAGED_SENTINEL` (exported)
  and the `"default" / "default (recommended)"` strings as
  `ACP_DEFAULT_PLACEHOLDERS`. The sentinel was referenced from 7 places
  across source and tests.

- Trim `DirectConversationInfo.agent.kind` JSDoc to 3 lines (was 7).

- Delete `scripts/check-acp-provider-models.mjs` and revert the CI step +
  triggers in `.github/workflows/acp-providers-sync.yml`. The script
  spawned each ACP wrapper unauthenticated and validated Canvas's lists
  against the wrapper's fallback model set — which is only 3 models for
  Claude Code (sonnet / sonnet[1m] / haiku) regardless of what the
  wrapper actually accepts. The check flagged every legitimately-added
  Opus entry as drift. Removing it; followup tracked in #740 for moving
  the lists into `@openhands/typescript-client`.

- Remove `test:acp-models` npm script (only invoked the deleted file).

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

* fix(acp): plug review-flagged gaps in model resolution + chip path

Five behavioral fixes + two refactors from review feedback on PR #730:

- F1: ``normalizeAgent`` and ``requireDirectConversationInfo`` were
  dropping ``agent.acp_model``, ``current_model_id``, and
  ``current_model_name`` on the way in from the wire — so the chip
  resolver only worked in unit tests (which build ``DirectConversationInfo``
  in-process) and silently fell back to provider-name labels in production.
  Normalizer now preserves all three.

- F2: ChatInputActions gated rendering ChatInputModel on
  ``isCloud || conversation?.agent_kind === "acp"``, so on a local home
  screen with no active conversation it always picked SwitchProfileButton.
  SwitchProfileButton then hid itself for ACP — net result: the home-ACP
  model label never appeared. Added ``isHomeAcp`` derivation from
  ``settings.agent_settings.agent_kind`` so the chat input picks
  ChatInputModel in that case too.

- F3: Switching the preset dropdown to Custom set ``isCustomAcpModel``
  but left ``acpModel`` untouched, so a user moving Claude Code → Custom
  + typing a custom command could save ``acp_model: "claude-opus-4-7"``
  on an unrelated wrapper. Now clears ``acpModel`` on Custom selection.

- F4: ``buildConfiguredAcpAgentSettings`` was stripping null / empty
  ``acp_model`` and not falling back to ``provider.default_model``, so
  existing users with ``acp_model: null`` saw the new registered default
  in Settings → Agent but their next conversation still started with the
  agent-server's own default (UI/runtime mismatch). Conversation creation
  now substitutes the provider default for empty values, matching what
  the form displays.

- F5: ``isAcpDefaultPlaceholder`` was applied only to runtime fields, not
  to the ``configured`` (``agent.acp_model``) or ``sdkLlm``
  (``agent.llm.model``) fallback rungs of the precedence chain. Older
  settings that persisted the literal ``"Default (recommended)"`` could
  surface it on chips. Filter now applies uniformly.

- R1: All five surfaces (Settings form, conversation creation,
  ChatInputModel, conversation adapter, chip) now route through one
  helper, ``resolveEffectiveAcpModel({ runtimeName, runtimeId,
  configured, sdkLlm, providerDefault })`` in ``acp-providers.ts``.
  Placeholder + sentinel filtering live in one place. ``providerDefault``
  is opt-in — chip omits it (don't lie about what's running), settings
  / creation / chat-input pass it (silently substitute the registry
  default).

- R2: ``ACPProviderIcon`` no longer includes ``"openhands"`` — it's
  ACP-only again. Reintroduced ``AgentBrandIconKind = "openhands" |
  ACPProviderIcon`` for surfaces that can render either harness's mark.

Tests: ``acp-server-conversation-service.test.ts`` now exercises the wire
path with the new fields. ``agent-server-adapter.test.ts`` adds two cases
covering placeholder filtering on configured + sdkLlm rungs.
``agent-settings.test.tsx`` adds a Custom-preset-clears-default
regression. ``chat-input-model.test.tsx`` swaps the
"home + ACP renders nothing" assertion for "home + ACP shows the provider
default" — that's the new correct behavior.

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

* fix(acp): translate new agent-settings keys + harmonize Gemini labels

Addresses the latest review pass:

- Translates ``SETTINGS$AGENT_CUSTOM_MODEL`` and ``SETTINGS$AGENT_MODEL_HINT``
  into all 14 non-English locales (ja, zh-CN, zh-TW, ko-KR, no, it, pt, es,
  ar, fr, tr, de, uk, ca). Both keys previously fell back to English text
  on every non-English client, blocking the model dropdown's hint copy and
  Custom-model label from being legible.

- Harmonizes the Gemini model labels with the Claude / Codex pattern —
  raw IDs like ``gemini-3.1-pro-preview`` become ``Gemini 3.1 Pro
  (preview)`` so the three providers read consistently in the dropdown.

- Adds provenance notes to ``CODEX_MODELS`` and ``GEMINI_MODELS`` mirroring
  the Claude one — naming the upstream source the list was extracted from
  and pointing at agent-canvas#740 (the long-term "move ACP model lists to
  ``@openhands/typescript-client``" plan).

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

* fix(acp): address review — chip labels, dead toggle, icon de-dup, shared hook

- Chip shows the provider's picker label (e.g. "Claude Opus 4.7") instead of
  the raw acp_model ID via new labelForAcpModel(); falls back to the raw ID
  for custom overrides and the provider name when no model. (#1)
- Drop the stale version from the 1M labels (opus[1m]/sonnet[1m] →
  "Claude Opus (1M)" / "Claude Sonnet (1M)") so the version-agnostic alias
  and its label can't disagree. (#2)
- Remove the now no-op "Show LLM profiles" filter-menu row + its wiring
  (chip is unconditional now; the preference no longer affects cards). (#4)
- Onboarding AgentOptionIcon delegates to AgentBrandIcon; delete the
  duplicated brand-mark path constants and per-kind SVG markup. (#5)
- Name the OpenHands logo aspect ratio (3:2) so the chip and onboarding
  tile render identically. (#7)
- Extract useAcpModelContext() shared by chat-input-actions and
  chat-input-model to kill the duplicated isHomeAcp / destination-path /
  label logic. (#6)

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

* fix(acp): gate the agent chip behind the restored "LLM model" toggle

The chip was unconditional, which (a) silently flipped OpenHands cards from
main's model-hidden-by-default to always-shown and (b) left the panel's
"LLM model" toggle a no-op (then deleted). Restore one toggle that gates the
unified chip for BOTH ACP and OpenHands cards (default OFF), matching the
sibling "Repo and branch" metadata toggle.

- Footer: chip (ACP brand mark + model, OpenHands logo + model) now renders
  only when showAgentChip is set.
- Restore showLlmProfiles plumbing: filter-menu row + ConversationPanel/
  ConversationCard/CompactConversationRow pass-throughs (panel + filter-menu
  are now byte-identical to main).
- ConversationCard.shouldRenderFooter gates ACP under the toggle too.

Net effect vs main for OpenHands: identical by default (hidden); when the
toggle is on, the only change is the OpenHands logo added next to the model
name. ACP identity chip becomes opt-in via the same toggle.

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

* fix(acp): reconcile model on command edit, unify chip labels, dedupe provider lookup

Review follow-ups for the versioned ACP model picker + agent chip:

- agent-settings: editing the command textarea into a different provider
  (or a custom command) now reconciles the model selector, so Save can no
  longer silently persist e.g. claude-opus-4-7 against a Codex/custom
  wrapper. The preset dropdown already did this; the textarea is the other
  way a user switches providers. Also always show the custom-model input
  when the dropdown is on "Custom" so a mismatched value is visible/editable
  rather than hidden.
- chat input: surface the provider's human label (e.g. "Claude Opus 4.7")
  for ACP conversations, matching the conversation-list chip, instead of the
  raw acp_model id.
- relabel the conversation-panel "LLM model" toggle to "Agent / model" — it
  now governs the ACP brand chip too.
- extract getAcpProvider() and replace the repeated ACP_PROVIDERS.find()
  lookups across the adapter, settings, and the constants module.

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

* fix(acp): stop filling the OpenHands logo's transparent hand paths

AgentBrandIcon recolored the OpenHands logo with a blanket
``[&_path]:fill-current``, which also overrode the two ``fill="transparent"``
hand shapes — turning the mark into a solid white blob on the onboarding tile
(and a filled blob on the conversation chip). The logo asset has 5
``fill="white"`` wordmark paths and 2 ``fill="transparent"`` hands; only the
former should inherit ``currentColor``.

Scope the override to ``[&_path:not([fill=transparent])]:fill-current`` so the
hands stay transparent (negative space), restoring the original two-tone logo.

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

---------

Co-authored-by: Debug Agent <debug@example.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 17:54:54 +02:00
..