Files
multica/apps
Jiayuan Zhang a67e533742 feat(agents): add per-agent model field with provider-aware dropdown
Adds a first-class `model` field to agents so users can pick the LLM model
from the create / settings UI instead of editing custom_env / custom_args.
The previous "set MULTICA_<PROVIDER>_MODEL env var on the daemon" approach
forced one model per provider per machine and was easy to misconfigure
(e.g. -m as a custom_arg breaks codex app-server initialization).

Backend (server/pkg/agent):
- New `agent.ListModels(provider, path)` returns the models supported by a
  provider. Static catalogs for claude, codex, gemini, cursor, copilot;
  dynamic discovery for opencode (`opencode models`), pi (`pi --list-models`),
  openclaw (`openclaw agents list`); 60s TTL cache + empty-list fallback on
  failure. Hermes returns an empty list and `ModelSelectionSupported=false`
  because its model is configured out-of-band.
- `agent.DefaultModel(provider)` returns the recommended default per
  provider (Sonnet 4.6 for claude, GPT-5.4 for codex, Gemini 2.5 Pro for
  gemini, composer-1.5 for cursor); copilot/openclaw/hermes deliberately
  have no default. The static catalog tags one entry per provider with
  `Default: true` so the UI can render a badge.
- For openclaw, opts.Model is mapped to `--agent <name>` since the CLI
  rejects `--model` outright; custom_args `--agent` still wins for
  back-compat.

Daemon protocol (server/internal/daemon):
- Heartbeat response carries an optional `pending_model_list` request
  (same pattern as PingStore / UpdateStore). The daemon resolves models
  via `agent.ListModels`, including the `supported` flag, and reports
  back via /api/daemon/runtimes/{id}/models/{requestId}/result.
- Task dispatch uses a three-tier fallback for the runtime model:
  agent.model → MULTICA_<PROVIDER>_MODEL env → agent.DefaultModel(provider).

Server API (server/internal/handler):
- `agent.model` is a new column (migration 050) and surfaces in
  Agent / CreateAgent / UpdateAgent payloads.
- New endpoints under /api/runtimes/{id}/models: POST to initiate
  discovery, GET to poll the request, plus the daemon-side report
  endpoint above.

CLI (server/cmd/multica):
- `multica agent create / update --model <id>`. Help copy steers users
  away from passing --model via --custom-args, which fails on codex
  (app-server mode) and openclaw.

Frontend (packages/core, packages/views):
- `Agent.model`, `RuntimeModel`, `RuntimeModelListRequest`,
  `RuntimeModelsResult` types.
- `runtimes/models.ts` exports `runtimeModelsOptions(runtimeId)` which
  initiates discovery and polls the request to completion (500ms
  cadence, 30s ceiling).
- New `ModelDropdown` (packages/views/agents) — searchable popover,
  provider grouping, creatable manual entry, "default" badge on the
  shipped recommendation, disabled state when the provider reports
  `supported=false` (Hermes), and clears any stale model value in that
  case to avoid persisting a ghost configuration.
- Wired into create-agent-dialog and the agent settings tab.

Verification:
- gofmt clean on touched files
- `go build ./... && go test ./...` (server) green; new openclaw and
  models_test cases included
- `pnpm typecheck` green across all 6 packages

Closes the immediate UX gap behind MUL-1151. DeepSeek V4 (or any new
model) becomes a zero-code addition: add it to the relevant static
catalog, or rely on the creatable input for one-off use.
2026-04-20 22:45:56 +08:00
..