index.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import type { AgentConfig as SDKAgentConfig } from "@opencode-ai/sdk";
  2. import { DEFAULT_MODELS, SUBAGENT_NAMES, type PluginConfig, type AgentOverrideConfig } from "../config";
  3. import { createOrchestratorAgent, type AgentDefinition } from "./orchestrator";
  4. import { createOracleAgent } from "./oracle";
  5. import { createLibrarianAgent } from "./librarian";
  6. import { createExplorerAgent } from "./explorer";
  7. import { createDesignerAgent } from "./designer";
  8. import { createFixerAgent } from "./fixer";
  9. export type { AgentDefinition } from "./orchestrator";
  10. type AgentFactory = (model: string) => AgentDefinition;
  11. // Backward Compatibility
  12. /** Map old agent names to new names for backward compatibility */
  13. const AGENT_ALIASES: Record<string, string> = {
  14. "explore": "explorer",
  15. "frontend-ui-ux-engineer": "designer",
  16. };
  17. /**
  18. * Get agent override config by name, supporting backward-compatible aliases.
  19. * Checks both the current name and any legacy alias names.
  20. */
  21. function getOverride(overrides: Record<string, AgentOverrideConfig>, name: string): AgentOverrideConfig | undefined {
  22. return overrides[name] ?? overrides[Object.keys(AGENT_ALIASES).find(k => AGENT_ALIASES[k] === name) ?? ""];
  23. }
  24. // Agent Configuration Helpers
  25. /**
  26. * Apply user-provided overrides to an agent's configuration.
  27. * Supports overriding model and temperature.
  28. */
  29. function applyOverrides(agent: AgentDefinition, override: AgentOverrideConfig): void {
  30. if (override.model) agent.config.model = override.model;
  31. if (override.temperature !== undefined) agent.config.temperature = override.temperature;
  32. }
  33. /**
  34. * Apply default permissions to an agent.
  35. * Currently sets 'question' permission to 'allow' for all agents.
  36. */
  37. function applyDefaultPermissions(agent: AgentDefinition): void {
  38. const existing = (agent.config.permission ?? {}) as Record<string, "ask" | "allow" | "deny">;
  39. agent.config.permission = { ...existing, question: "allow" } as SDKAgentConfig["permission"];
  40. }
  41. // Agent Classification
  42. export type SubagentName = typeof SUBAGENT_NAMES[number];
  43. export function isSubagent(name: string): name is SubagentName {
  44. return (SUBAGENT_NAMES as readonly string[]).includes(name);
  45. }
  46. // Agent Factories
  47. const SUBAGENT_FACTORIES: Record<SubagentName, AgentFactory> = {
  48. explorer: createExplorerAgent,
  49. librarian: createLibrarianAgent,
  50. oracle: createOracleAgent,
  51. designer: createDesignerAgent,
  52. fixer: createFixerAgent,
  53. };
  54. // Public API
  55. /**
  56. * Create all agent definitions with optional configuration overrides.
  57. * Instantiates the orchestrator and all subagents, applying user config and defaults.
  58. *
  59. * @param config - Optional plugin configuration with agent overrides
  60. * @returns Array of agent definitions (orchestrator first, then subagents)
  61. */
  62. export function createAgents(config?: PluginConfig): AgentDefinition[] {
  63. const agentOverrides = config?.agents ?? {};
  64. // TEMP: If fixer has no config, inherit from librarian's model to avoid breaking
  65. // existing users who don't have fixer in their config yet
  66. const getModelForAgent = (name: SubagentName): string => {
  67. if (name === "fixer" && !getOverride(agentOverrides, "fixer")?.model) {
  68. return getOverride(agentOverrides, "librarian")?.model ?? DEFAULT_MODELS["librarian"];
  69. }
  70. return DEFAULT_MODELS[name];
  71. };
  72. // 1. Gather all sub-agent definitions
  73. const protoSubAgents = (Object.entries(SUBAGENT_FACTORIES) as [SubagentName, AgentFactory][]).map(
  74. ([name, factory]) => factory(getModelForAgent(name))
  75. );
  76. // 2. Apply overrides to each agent
  77. const allSubAgents = protoSubAgents.map((agent) => {
  78. const override = getOverride(agentOverrides, agent.name);
  79. if (override) {
  80. applyOverrides(agent, override);
  81. }
  82. return agent;
  83. });
  84. // 3. Create Orchestrator (with its own overrides)
  85. const orchestratorModel =
  86. getOverride(agentOverrides, "orchestrator")?.model ?? DEFAULT_MODELS["orchestrator"];
  87. const orchestrator = createOrchestratorAgent(orchestratorModel);
  88. applyDefaultPermissions(orchestrator);
  89. const oOverride = getOverride(agentOverrides, "orchestrator");
  90. if (oOverride) {
  91. applyOverrides(orchestrator, oOverride);
  92. }
  93. return [orchestrator, ...allSubAgents];
  94. }
  95. /**
  96. * Get agent configurations formatted for the OpenCode SDK.
  97. * Converts agent definitions to SDK config format and applies classification metadata.
  98. *
  99. * @param config - Optional plugin configuration with agent overrides
  100. * @returns Record mapping agent names to their SDK configurations
  101. */
  102. export function getAgentConfigs(config?: PluginConfig): Record<string, SDKAgentConfig> {
  103. const agents = createAgents(config);
  104. return Object.fromEntries(
  105. agents.map((a) => {
  106. const sdkConfig: SDKAgentConfig = { ...a.config, description: a.description };
  107. // Apply classification-based visibility and mode
  108. if (isSubagent(a.name)) {
  109. sdkConfig.mode = "subagent";
  110. } else if (a.name === "orchestrator") {
  111. sdkConfig.mode = "primary";
  112. }
  113. return [a.name, sdkConfig];
  114. })
  115. );
  116. }