Skip to content

fix(config): handle 'local-agent' provider in getLLMConfig - #124

Closed
StizDev wants to merge 1 commit into
elder-plinius:mainfrom
StizDev:fix/local-agent-provider-unknown
Closed

fix(config): handle 'local-agent' provider in getLLMConfig#124
StizDev wants to merge 1 commit into
elder-plinius:mainfrom
StizDev:fix/local-agent-provider-unknown

Conversation

@StizDev

@StizDev StizDev commented Jul 25, 2026

Copy link
Copy Markdown

What changed

ConfigManager.getLLMConfig (src/config/index.ts) had a switch over
provider names with a default: that threw Unknown provider: ${actualProvider}. The LLMProvider type union includes 'local-agent'
(Claude Code / Codex / Hermes CLI backends), and the UI correctly sends
provider='local-agent', but the switch had no case 'local-agent':
so any mission dispatched with that provider fell through to the throw.
The UI showed the agent as "connected" (the health-check uses a separate
in-memory registry, connectedLocalAgents), but the mission was refused
with:

Refused: the live-tools backend failed: Unknown provider: local-agent

A prior workaround at server.ts:6168 swallowed the throw inside
/api/models (comment: "getLLMConfig throws for...the legitimate
'local-agent'"), but the root cause remained — so other call sites that
resolve LLM config without the resolveGeneralLLMConfig short-circuit
(recon/whitebox.ts, the default-config resolver, the mission path when
a base URL is supplied) still crashed.

Why it changed

Selecting a connected local agent (e.g. Hermes) as the LLM backend is a
first-class, keyless path in the UI and the LocalAgentAdapter
(src/llm/index.ts:1294), but the config resolver rejected it. The fix
makes 'local-agent' a first-class case in getLLMConfig and
setDefaultProvider, matching how codex (the other keyless local-CLI
provider) is already handled — minus the stored provider block, since
local-agent has no apiKey/baseUrl.

What the change does

  • getLLMConfig: case 'local-agent': sets actualModel = model || 'codex'.
    The agent id (codex|claude|hermes) travels in model; default 'codex'
    matches LocalAgentAdapter.chat (src/llm/index.ts:1317). No apiKey or
    baseUrl is set — keyless, each CLI uses its own login.
  • setDefaultProvider: case 'local-agent': sets defaultModel to
    'codex' so selecting it as the default is not a silent no-op.

No change to LocalAgentAdapter, requireLiveLocalAgent, the connected-
agent registry, or the resolveGeneralLLMConfig short-circuit at
server.ts:6823 — they were already correct; they just couldn't be
reached reliably because the config resolver threw first.

Commands run

  • npm run typecheck -> pass
  • npm test -> pass (30/30: vitest src, ops preflight 11, model-matrix 19)
  • npm run doctor -> pass (offline API health endpoint is pre-existing, unrelated)
  • npm run verify-claims -> pass (27/27)
  • npm run lint -> pass (0 errors, 128 pre-existing warnings unchanged)
  • npm run build -> pass

Scope audit

git diff --name-status main...HEAD:
M src/config/index.ts

Single file, 10 insertions. No benchmark, provenance, provider-config,
redaction, or safety-test paths touched.

Contribution Receipt

  • Change: Add case 'local-agent': to ConfigManager.getLLMConfig and
    setDefaultProvider so the keyless local-agent CLI backend (Claude
    Code / Codex / Hermes) resolves like other providers instead of
    throwing Unknown provider: local-agent.
  • Scope class: local_lab
  • Target authority: not_applicable (config resolver fix; no target touched)
  • Network use: none
  • Run mode labels: local_agent, planning_only (no execution path changed)
  • Model/harness labels: model=not_applicable, provider=local-agent,
    agent_runtime=Claude Code/Codex/Hermes, harness=getLLMConfig,
    tool_access=none, target_class=static_fixture, run_mode=planning_only,
    attempts=0, successes=0, failures=0, abstentions=0
  • Commands run:
    • npm run typecheck -> pass
    • npm test -> pass (30/30)
    • npm run doctor -> pass
    • npm run verify-claims -> pass (27/27)
  • Artifacts: none (config-only fix; no benchmark artifacts introduced)
  • Redaction: none needed (no secrets, tokens, flags, or target data in diff)
  • Claims changed: none
  • Abstentions/refusals: 0 (the bug caused a refusal-like error; the fix removes it)
  • Residual risk: setDefaultModel('local-agent', ...) still falls through
    the second switch as a no-op — intentional, since local-agent has no
    stored provider block to merge a defaultModel into, and the agent id is
    always passed directly in model at call time. No behavior change vs.
    pre-fix for that path.

…nius#113)

The LLMProvider type union includes 'local-agent' (Claude Code / Codex /
Hermes CLI backends), but ConfigManager.getLLMConfig had no case for it,
so any mission dispatched with provider='local-agent' fell through to
`default:` and threw `Unknown provider: local-agent`. The UI showed the
agent as "connected" (the health-check uses a separate registry), but
the mission was refused with "the live-tools backend failed:
Unknown provider: local-agent".

The team had already worked around the throw at server.ts:6168 with a
try/catch comment ("getLLMConfig throws for...the legitimate
'local-agent'"), but left the root cause in place — so other call sites
(recon/whitebox, the default-config resolver) still crashed.

Fix: add `case 'local-agent':` to getLLMConfig and setDefaultProvider.
Local-agent is keyless (each CLI uses its own login); the agent id
(codex|claude|hermes) travels in `model`, defaulting to 'codex' to
match LocalAgentAdapter.chat (src/llm/index.ts:1317).

Verification:
- npm run typecheck -> pass
- npm test -> pass (30/30)
- npm run doctor -> pass
- npm run verify-claims -> pass (27/27)
- npm run lint -> pass (0 errors)
- npm run build -> pass
@StizDev

StizDev commented Jul 25, 2026

Copy link
Copy Markdown
Author

Closing as a duplicate of #118 (hoptoad-off), which fixes the same problem: getLLMConfig's switch had no case 'local-agent', so keyless missions fell through to default: and threw Unknown provider: local-agent.

@StizDev StizDev closed this Jul 25, 2026
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.

1 participant