diff --git a/src/agents/atlas/agent.ts b/src/agents/atlas/agent.ts index 5e8801ebb..8c837457e 100644 --- a/src/agents/atlas/agent.ts +++ b/src/agents/atlas/agent.ts @@ -3,27 +3,27 @@ * * Orchestrates work via task() to complete ALL tasks in a todo list until fully done. * - * Prompt routing (`getAtlasPromptSource`, evaluated in this order): - * 1. GPT family → gpt.ts (calibrated for GPT-5.5) - * 2. Gemini family → gemini.ts - * 3. Kimi K2.x family → kimi.ts (Claude-family base + K2.6 thinking-mode calibration) - * 4. Claude Opus 4.7 → opus-4-7.ts (literal-following + explicit fan-out push) - * 5. Default (Claude 4.6 family: opus-4-6, sonnet-4-6, haiku-4-5, etc.) → default.ts + * Prompt routing (`getAtlasPromptSource`, evaluated by prompts-core variant order): + * 1. Claude Opus 4.7 → opus-4-7.md (literal-following + explicit fan-out push) + * 2. GPT family → gpt.md (calibrated for GPT-5.5) + * 3. Gemini family → gemini.md + * 4. Kimi K2.x family → kimi.md (Claude-family base + K2.6 thinking-mode calibration) + * 5. Default (Claude 4.6 family: opus-4-6, sonnet-4-6, haiku-4-5, etc.) → default.md */ import type { AgentConfig } from "@opencode-ai/sdk" +import { + atlasPromptVariants, + loadPromptSync, + resolveVariant, + type SyncRuntimeInjection, +} from "@oh-my-opencode/prompts-core" import type { AgentMode, AgentPromptMetadata } from "../types" -import { isClaudeOpus47Model, isGeminiModel, isGptModel, isKimiK2Model } from "../types" import type { AvailableAgent, AvailableSkill, AvailableCategory } from "../dynamic-agent-prompt-builder" import { buildAgentIdentitySection, buildCategorySkillsDelegationGuide } from "../dynamic-agent-prompt-builder" import type { CategoryConfig } from "../../config/schema" import { mergeCategories } from "../../shared/merge-categories" -import { getDefaultAtlasPrompt } from "./default" -import { getGptAtlasPrompt } from "./gpt" -import { getGeminiAtlasPrompt } from "./gemini" -import { getKimiAtlasPrompt } from "./kimi" -import { getOpus47AtlasPrompt } from "./opus-4-7" import { getCategoryDescription, buildAgentSelectionSection, @@ -36,20 +36,22 @@ const MODE: AgentMode = "primary" export type AtlasPromptSource = "default" | "gpt" | "gemini" | "kimi" | "opus-4-7" +class AtlasPromptVariantError extends Error { + readonly name = "AtlasPromptVariantError" + + constructor(readonly variant: string) { + super(`Unknown Atlas prompt variant: ${variant}`) + } +} + export function getAtlasPromptSource(model?: string): AtlasPromptSource { - if (model && isGptModel(model)) { - return "gpt" - } - if (model && isGeminiModel(model)) { - return "gemini" - } - if (model && isKimiK2Model(model)) { - return "kimi" - } - if (model && isClaudeOpus47Model(model)) { - return "opus-4-7" - } - return "default" + const variant = resolveVariant({ + agentName: "atlas", + modelID: model, + variants: atlasPromptVariants, + }) + if (isAtlasPromptSource(variant)) return variant + throw new AtlasPromptVariantError(variant) } export interface OrchestratorContext { @@ -61,20 +63,15 @@ export interface OrchestratorContext { export function getAtlasPrompt(model?: string): string { const source = getAtlasPromptSource(model) + return loadPromptSync({ + source: atlasPromptVariants[source], + name: "atlas", + variant: source, + }).body +} - switch (source) { - case "gpt": - return getGptAtlasPrompt() - case "gemini": - return getGeminiAtlasPrompt() - case "kimi": - return getKimiAtlasPrompt() - case "opus-4-7": - return getOpus47AtlasPrompt() - case "default": - default: - return getDefaultAtlasPrompt() - } +function isAtlasPromptSource(variant: string): variant is AtlasPromptSource { + return Object.prototype.hasOwnProperty.call(atlasPromptVariants, variant) } function buildDynamicOrchestratorPrompt(ctx?: OrchestratorContext): string { @@ -94,23 +91,31 @@ function buildDynamicOrchestratorPrompt(ctx?: OrchestratorContext): string { const decisionMatrix = buildDecisionMatrix(agents, userCategories) const skillsSection = buildSkillsSection(skills) const categorySkillsGuide = buildCategorySkillsDelegationGuide(availableCategories, skills) + const source = getAtlasPromptSource(model) + const runtimeInjections = [ + { placeholder: "{CATEGORY_SECTION}", resolver: () => categorySection }, + { placeholder: "{AGENT_SECTION}", resolver: () => agentSection }, + { placeholder: "{DECISION_MATRIX}", resolver: () => decisionMatrix }, + { placeholder: "{SKILLS_SECTION}", resolver: () => skillsSection }, + { placeholder: "{{CATEGORY_SKILLS_DELEGATION_GUIDE}}", resolver: () => categorySkillsGuide }, + ] satisfies readonly SyncRuntimeInjection[] const agentIdentity = buildAgentIdentitySection( "Atlas", "Master Orchestrator agent from OhMyOpenCode that coordinates specialized agents to complete todo lists", ) - const basePrompt = getAtlasPrompt(model) + const basePrompt = loadPromptSync({ + source: atlasPromptVariants[source], + name: "atlas", + variant: source, + inject: runtimeInjections, + }).body return agentIdentity + "\n" + basePrompt - .replace("{CATEGORY_SECTION}", categorySection) - .replace("{AGENT_SECTION}", agentSection) - .replace("{DECISION_MATRIX}", decisionMatrix) - .replace("{SKILLS_SECTION}", skillsSection) - .replace("{{CATEGORY_SKILLS_DELEGATION_GUIDE}}", categorySkillsGuide) } export function createAtlasAgent(ctx: OrchestratorContext): AgentConfig { - const baseConfig = { + const baseConfig: AgentConfig = { description: "Orchestrates work via task() to complete ALL tasks in a todo list until fully done. (Atlas - OhMyOpenCode)", mode: MODE, @@ -120,7 +125,7 @@ export function createAtlasAgent(ctx: OrchestratorContext): AgentConfig { color: "#10B981", } - return baseConfig as AgentConfig + return baseConfig } createAtlasAgent.mode = MODE diff --git a/src/agents/atlas/atlas-prompt.test.ts b/src/agents/atlas/atlas-prompt.test.ts index 351e3a0dd..1b36ff07b 100644 --- a/src/agents/atlas/atlas-prompt.test.ts +++ b/src/agents/atlas/atlas-prompt.test.ts @@ -1,16 +1,12 @@ import { describe, test, expect } from "bun:test" -import { ATLAS_SYSTEM_PROMPT } from "./default" -import { ATLAS_GPT_SYSTEM_PROMPT } from "./gpt" -import { ATLAS_GEMINI_SYSTEM_PROMPT } from "./gemini" -import { ATLAS_KIMI_SYSTEM_PROMPT } from "./kimi" -import { ATLAS_OPUS_47_SYSTEM_PROMPT } from "./opus-4-7" +import { getAtlasPrompt } from "./agent" const ALL_VARIANTS: Array<[string, string]> = [ - ["default", ATLAS_SYSTEM_PROMPT], - ["gpt", ATLAS_GPT_SYSTEM_PROMPT], - ["gemini", ATLAS_GEMINI_SYSTEM_PROMPT], - ["kimi", ATLAS_KIMI_SYSTEM_PROMPT], - ["opus-4-7", ATLAS_OPUS_47_SYSTEM_PROMPT], + ["default", getAtlasPrompt("anthropic/claude-sonnet-4-6")], + ["gpt", getAtlasPrompt("openai/gpt-5.5")], + ["gemini", getAtlasPrompt("google/gemini-3.1-pro")], + ["kimi", getAtlasPrompt("moonshotai/kimi-k2.6")], + ["opus-4-7", getAtlasPrompt("anthropic/claude-opus-4-7")], ] describe("Atlas prompts auto-continue policy", () => { diff --git a/src/agents/atlas/prompt-checkbox-enforcement.test.ts b/src/agents/atlas/prompt-checkbox-enforcement.test.ts index 60007552c..5cc1e1b69 100644 --- a/src/agents/atlas/prompt-checkbox-enforcement.test.ts +++ b/src/agents/atlas/prompt-checkbox-enforcement.test.ts @@ -1,16 +1,12 @@ import { describe, test, expect } from "bun:test" -import { ATLAS_SYSTEM_PROMPT } from "./default" -import { ATLAS_GPT_SYSTEM_PROMPT } from "./gpt" -import { ATLAS_GEMINI_SYSTEM_PROMPT } from "./gemini" -import { ATLAS_KIMI_SYSTEM_PROMPT } from "./kimi" -import { ATLAS_OPUS_47_SYSTEM_PROMPT } from "./opus-4-7" +import { getAtlasPrompt } from "./agent" const ALL_VARIANTS: Array<[string, string]> = [ - ["default", ATLAS_SYSTEM_PROMPT], - ["gpt", ATLAS_GPT_SYSTEM_PROMPT], - ["gemini", ATLAS_GEMINI_SYSTEM_PROMPT], - ["kimi", ATLAS_KIMI_SYSTEM_PROMPT], - ["opus-4-7", ATLAS_OPUS_47_SYSTEM_PROMPT], + ["default", getAtlasPrompt("anthropic/claude-sonnet-4-6")], + ["gpt", getAtlasPrompt("openai/gpt-5.5")], + ["gemini", getAtlasPrompt("google/gemini-3.1-pro")], + ["kimi", getAtlasPrompt("moonshotai/kimi-k2.6")], + ["opus-4-7", getAtlasPrompt("anthropic/claude-opus-4-7")], ] describe("ATLAS prompt checkbox enforcement", () => {