Skip to content

fix: resolve agent by id across web UI, SDK types, and ACP#18803

Open
filipeandre wants to merge 6 commits intoanomalyco:devfrom
filipeandre:fix/agent-id-resolution
Open

fix: resolve agent by id across web UI, SDK types, and ACP#18803
filipeandre wants to merge 6 commits intoanomalyco:devfrom
filipeandre:fix/agent-id-resolution

Conversation

@filipeandre
Copy link

Issue for this PR

Closes #9296 (follow-up to #18163)

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Three related bugs where agent names were used as stable identifiers instead of agent ids, causing failures when agents are renamed via config (e.g. agent.build.name = "Forge" or default_agent: "Pulse").

1. Web UI dropdown does not switch after leaving plan mode (fix(local))

local.session.restore() bailed out as soon as any saved session state existed, so the plan_exit synthetic user message (which carries agent: "build") could never update the dropdown. Also, agent selection was keyed on display name rather than stable id.

  • Store and resolve agent selection by canonical id, falling back to name for legacy persisted values.
  • Extend saved session state with the last-restored message id so restore() re-seeds the dropdown whenever a newer backend user message arrives, instead of skipping all updates after the first.
  • New unit tests cover: id-first lookup, legacy name fallback, same-message skip, newer-message update.

2. SDK Agent type missing id field (fix(sdk))

The OpenAPI generator dropped id from the Agent schema even though the server always returns it. This forced the app to use an unsafe runtime cast to read agent ids.

  • Added id to components.schemas.Agent in openapi.json and to the generated v2/gen/types.gen.ts type.
  • Patched sdk/js/script/build.ts to inject Agent.id into the spec before @hey-api/openapi-ts runs, so future regenerations preserve the fix.

3. ACP init fails with default agent "Pulse" not found (fix(acp))

Two bugs in src/acp/agent.ts:

  • AgentModule.defaultAgent() was called without Instance.provide(), so config/agent resolution ran outside the project instance context. The custom agent only defined in the project config was never found.
  • loadAvailableModes built ModeOption with id: agent.name instead of id: agent.id, breaking mode selection and session history restore for any agent with a custom display name.

Fixed by wrapping both defaultAgent() call sites with Instance.provide({ directory, fn }) and switching mode construction to use canonical ids. New ACP tests cover default display-name resolution and prompt agent-id forwarding.

How did you verify your code works?

  • npm run typecheck passes in packages/opencode, packages/app, and packages/sdk/js.
  • Unit tests added for local.tsx (agent pick helpers, session state sync) and ACP mode resolution.
  • End-to-end path: enter plan mode → plan_exit → agent dropdown switches to build agent; IntelliJ ACP init with default_agent: "Pulse" no longer throws.

Screenshots / recordings

Not a visual change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

The OpenAPI generator omitted the id field from the Agent schema even
though the runtime response always included it. This made app-side agent
lookup rely on an unsafe cast to read the stable id at runtime.

- Add id to components.schemas.Agent properties and required array in
  openapi.json so generated types are correct going forward
- Add id: string to the generated Agent type in v2/gen/types.gen.ts
- Patch build.ts to inject Agent.id into the spec before @hey-api/openapi-ts
  runs, so future regenerations preserve the fix automatically
…message

- Store and resolve agent selection by canonical id (falling back to name
  for legacy persisted values) so renamed plan/build agents map correctly
- Add message id tracking to local session state so restore() re-seeds the
  dropdown whenever a newer backend user message arrives (e.g. plan_exit)
  instead of skipping updates once any saved selection exists
- Export pickAgentItem and syncSessionState as pure helpers for unit testing
- Add focused tests covering id-first lookup, legacy name fallback, same-
  message skip, and newer-message update
Two bugs in ACP caused the 'default agent not found' error when using
custom named agents (e.g. default_agent: "Pulse") from IDEs like IntelliJ:

1. AgentModule.defaultAgent() was called without Instance.provide(), so
   config/agent resolution ran outside the project instance context. Wrap
   both call sites (resolveModeState and prompt fallback) with
   Instance.provide({ directory, fn }).

2. loadAvailableModes built ModeOption with id: agent.name instead of
   id: agent.id. This broke mode selection and session history restore for
   any agent whose display name differs from its canonical id. Switch to
   id: agent.id and update the comparison in resolveModeState to match
   against mode.id.

Add ACP mode-resolution tests covering:
- default_agent display name resolves to canonical mode id
- prompt sends canonical agent id rather than display name
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Experimental plan mode handover -> build uses plan agent's model

1 participant