Skip to content

feat: Support custom default agent configuration #110

@shuv1337

Description

@shuv1337

Context

Port of upstream PR sst/opencode#5313 which addresses sst/opencode#4443.

This feature allows users to configure a custom default agent instead of always defaulting to "build". Users with custom agents or different workflows can now set their preferred agent as the default.

Problem

Currently, when no agent is specified, the application always defaults to "build". This is hardcoded in multiple places across the codebase, making it inflexible for users who:

  • Have custom agents they prefer to use as their primary agent
  • Want to use a different built-in agent (e.g., "plan") as their default
  • Have disabled the "build" agent and need a fallback

Proposed Solution

Implement the upstream PR with modifications based on reviewer feedback from @rekram1-node:

I don't like the "!" assertion, I feel like this should behave the same way say "model" does, where we don't force the default at the config level for consistency. So this would be:
session.modeId ?? globalCfg.default_agent ?? "build"

This means instead of forcing default_agent to always be set in config (using ! assertions), we should use the nullish coalescing pattern throughout: value ?? config.default_agent ?? "build".

Acceptance Criteria

  • Users can set default_agent in opencode.json config
  • The default_agent field properly falls back to "build" when not specified
  • If configured default_agent doesn't exist or is disabled, fallback to first available primary agent
  • Agent list command shows which agent is the default (e.g., build (primary, default))
  • TUI initializes with the default agent selected
  • All hardcoded "build" references use the fallback chain: specificValue ?? config.default_agent ?? "build"
  • Tests verify custom default agent, disabled agent fallback, and nonexistent agent fallback

Implementation Details

Files to Modify

  1. packages/opencode/src/config/config.ts

    • Add default_agent field to config schema (line ~594)
    • Add firstPrimaryAgent() helper function to find fallback agent
    • Add validation: if default_agent is missing, doesn't exist, or is disabled → select first primary agent
  2. packages/opencode/src/agent/agent.ts

    • Add default: z.boolean().optional() to Agent.Info schema (line ~15)
    • Set result[key].default = true when agent matches cfg.default_agent (after line ~227)
  3. packages/opencode/src/acp/agent.ts

    • Update currentModeId calculation (line ~472):
      const availableAgents = agents.filter((agent) => agent.mode !== "subagent")
      const currentModeId = availableAgents.find((agent) => agent.default)?.name ?? availableAgents[0]?.name
    • In prompt() method (line ~574), use fallback pattern:
      const agent = session.modeId ?? globalCfg.default_agent ?? "build"
  4. packages/opencode/src/cli/cmd/agent.ts (line ~237)

    • Update agent list output to show default:
      process.stdout.write(`${agent.name} (${agent.mode}${agent.default ? ", default" : ""})${EOL}`)
  5. packages/opencode/src/cli/cmd/run.ts (lines ~227, ~236)

    • Import Config and use fallback pattern:
      agent: args.agent || cfg.default_agent ?? "build"
  6. packages/opencode/src/cli/cmd/tui/context/local.tsx (line ~59)

    • Use default agent for initial selection:
      current: agents().find((x) => x.default)?.name ?? agents()[0].name
  7. packages/opencode/src/server/server.ts (lines ~1003, ~1007)

    • Use fallback pattern for agent in compaction:
      let currentAgent = cfg.default_agent ?? "build"
      // ...
      currentAgent = info.agent ?? cfg.default_agent ?? "build"
  8. packages/opencode/src/session/prompt.ts (line ~864)

    • Use fallback pattern in createUserMessage:
      const cfg = await Config.get()
      const agent = await Agent.get(input.agent ?? cfg.default_agent ?? "build")
  9. packages/sdk/js/src/v2/gen/types.gen.ts

    • Add default_agent?: string to Config type (after line ~1287)
    • Add default?: boolean to Agent type (after line ~1606)
  10. packages/opencode/test/config/config.test.ts

    • Add tests for:
      • Custom default_agent being used
      • Fallback when default_agent is disabled
      • Fallback when default_agent doesn't exist

Key Modification from Upstream PR

Replace all globalCfg.default_agent! (non-null assertion) patterns with globalCfg.default_agent ?? "build" to maintain consistency with how other config defaults (like model) are handled.

Example Config

{
  "$schema": "https://opencode.ai/config.json",
  "default_agent": "custom",
  "agent": {
    "custom": {
      "model": "anthropic/claude-sonnet-4-20250514",
      "description": "My custom default agent"
    }
  }
}

Related Links

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions