Skip to content

[Bug]: LLM facade hardcodes responses wire protocol for codex / opencode openai/*, ignoring LLM_API_PROTOCOL=chat_completions #305

Description

@wanliqin

Summary

When the daemon is configured with LLM_API_PROTOCOL=chat_completions (e.g. for DeepSeek / vLLM / Ollama-style OpenAI-compatible upstreams that only implement /chat/completions), sandboxes created for the codex provider and for opencode with an openai/* model still get an LLM facade token issued for the responses wire protocol only. Guest-side calls to /chat/completions then fail with 403/405 (and retries surface as 502).

Observed behavior

  • Daemon env: LLM_API_PROTOCOL=chat_completions, LLM_API_ENDPOINT=<openai-compatible upstream>, LLM_MODEL=deepseek-v4-flash.
  • Agent: provider: opencode, model: openai/deepseek-v4-flash (also reproducible with provider: codex).
  • Inside the sandbox: echo $LLM_API_PROTOCOL prints responses (expected chat_completions).
  • Daemon log: POST /api/runtime/sandboxes/.../llm/openai/v1/responses 403, then retries fail; guest calls to /chat/completions return 405.

Root cause (current main)

The facade wire API is hardcoded to llms.APIProtocolResponses in three places:

  • pkg/llms/runtimefacade/config.goensureSessionCodexConfig: facadeWireAPI := llms.APIProtocolResponses
  • pkg/llms/runtimefacade/config.goensureOpenCodeOpenAIConfig: token + LLM_API_PROTOCOL env hardcoded to llms.APIProtocolResponses
  • pkg/runs/controller.go (~line 1048-1063) — prompt-attach path: token, WriteCodexRuntimeConfig and injected env all hardcoded to llms.APIProtocolResponses

Note that ensureOpenCodeCustomProviderConfig (used for openai-compatible/* models) already resolves the wire API from the provider config via llms.NormalizeWireAPI(target.WireAPI) — the three paths above should follow the same resolution (daemon LLM_API_PROTOCOL env / provider config) instead of hardcoding.

Workaround

Use a custom provider model id (e.g. openai-compatible/<model>) whose wire API resolves to chat_completions, so the facade token matches the protocol the agent actually speaks.

Environment

  • ghcr.io/chaitin/agent-compose:latest (2026-07), Linux x86_64, Docker bridge network
  • Upstream: OpenAI-compatible gateway implementing only /v1/chat/completions

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions