refactor(agents): restructure atlas agent into modular directory with model-based routing
Split monolithic atlas.ts into modular structure: index.ts (routing), default.ts (Claude-optimized), gpt.ts (GPT-optimized), utils.ts (shared utilities). Atlas now routes to appropriate prompt based on model type instead of overriding model settings. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
parent
5c68ae3bee
commit
2e0d0c989b
@ -1,127 +1,13 @@
|
|||||||
import type { AgentConfig } from "@opencode-ai/sdk"
|
|
||||||
import type { AgentMode, AgentPromptMetadata } from "./types"
|
|
||||||
|
|
||||||
const MODE: AgentMode = "primary"
|
|
||||||
import type { AvailableAgent, AvailableSkill, AvailableCategory } from "./dynamic-agent-prompt-builder"
|
|
||||||
import { buildCategorySkillsDelegationGuide } from "./dynamic-agent-prompt-builder"
|
|
||||||
import type { CategoryConfig } from "../config/schema"
|
|
||||||
import { DEFAULT_CATEGORIES, CATEGORY_DESCRIPTIONS } from "../tools/delegate-task/constants"
|
|
||||||
import { createAgentToolRestrictions } from "../shared/permission-compat"
|
|
||||||
|
|
||||||
const getCategoryDescription = (name: string, userCategories?: Record<string, CategoryConfig>) =>
|
|
||||||
userCategories?.[name]?.description ?? CATEGORY_DESCRIPTIONS[name] ?? "General tasks"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Atlas - Master Orchestrator Agent
|
* Default Atlas system prompt optimized for Claude series models.
|
||||||
*
|
*
|
||||||
* Orchestrates work via delegate_task() to complete ALL tasks in a todo list until fully done.
|
* Key characteristics:
|
||||||
* You are the conductor of a symphony of specialized agents.
|
* - Optimized for Claude's tendency to be "helpful" by forcing explicit delegation
|
||||||
|
* - Strong emphasis on verification and QA protocols
|
||||||
|
* - Detailed workflow steps with narrative context
|
||||||
|
* - Extended reasoning sections
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export interface OrchestratorContext {
|
|
||||||
model?: string
|
|
||||||
availableAgents?: AvailableAgent[]
|
|
||||||
availableSkills?: AvailableSkill[]
|
|
||||||
userCategories?: Record<string, CategoryConfig>
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildAgentSelectionSection(agents: AvailableAgent[]): string {
|
|
||||||
if (agents.length === 0) {
|
|
||||||
return `##### Option B: Use AGENT directly (for specialized experts)
|
|
||||||
|
|
||||||
No agents available.`
|
|
||||||
}
|
|
||||||
|
|
||||||
const rows = agents.map((a) => {
|
|
||||||
const shortDesc = a.description.split(".")[0] || a.description
|
|
||||||
return `| \`${a.name}\` | ${shortDesc} |`
|
|
||||||
})
|
|
||||||
|
|
||||||
return `##### Option B: Use AGENT directly (for specialized experts)
|
|
||||||
|
|
||||||
| Agent | Best For |
|
|
||||||
|-------|----------|
|
|
||||||
${rows.join("\n")}`
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildCategorySection(userCategories?: Record<string, CategoryConfig>): string {
|
|
||||||
const allCategories = { ...DEFAULT_CATEGORIES, ...userCategories }
|
|
||||||
const categoryRows = Object.entries(allCategories).map(([name, config]) => {
|
|
||||||
const temp = config.temperature ?? 0.5
|
|
||||||
return `| \`${name}\` | ${temp} | ${getCategoryDescription(name, userCategories)} |`
|
|
||||||
})
|
|
||||||
|
|
||||||
return `##### Option A: Use CATEGORY (for domain-specific work)
|
|
||||||
|
|
||||||
Categories spawn \`Sisyphus-Junior-{category}\` with optimized settings:
|
|
||||||
|
|
||||||
| Category | Temperature | Best For |
|
|
||||||
|----------|-------------|----------|
|
|
||||||
${categoryRows.join("\n")}
|
|
||||||
|
|
||||||
\`\`\`typescript
|
|
||||||
delegate_task(category="[category-name]", load_skills=[...], prompt="...")
|
|
||||||
\`\`\``
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildSkillsSection(skills: AvailableSkill[]): string {
|
|
||||||
if (skills.length === 0) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
const skillRows = skills.map((s) => {
|
|
||||||
const shortDesc = s.description.split(".")[0] || s.description
|
|
||||||
return `| \`${s.name}\` | ${shortDesc} |`
|
|
||||||
})
|
|
||||||
|
|
||||||
return `
|
|
||||||
#### 3.2.2: Skill Selection (PREPEND TO PROMPT)
|
|
||||||
|
|
||||||
**Skills are specialized instructions that guide subagent behavior. Consider them alongside category selection.**
|
|
||||||
|
|
||||||
| Skill | When to Use |
|
|
||||||
|-------|-------------|
|
|
||||||
${skillRows.join("\n")}
|
|
||||||
|
|
||||||
**MANDATORY: Evaluate ALL skills for relevance to your task.**
|
|
||||||
|
|
||||||
Read each skill's description and ask: "Does this skill's domain overlap with my task?"
|
|
||||||
- If YES: INCLUDE in load_skills=[...]
|
|
||||||
- If NO: You MUST justify why in your pre-delegation declaration
|
|
||||||
|
|
||||||
**Usage:**
|
|
||||||
\`\`\`typescript
|
|
||||||
delegate_task(category="[category]", load_skills=["skill-1", "skill-2"], prompt="...")
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
**IMPORTANT:**
|
|
||||||
- Skills get prepended to the subagent's prompt, providing domain-specific instructions
|
|
||||||
- Subagents are STATELESS - they don't know what skills exist unless you include them
|
|
||||||
- Missing a relevant skill = suboptimal output quality`
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildDecisionMatrix(agents: AvailableAgent[], userCategories?: Record<string, CategoryConfig>): string {
|
|
||||||
const allCategories = { ...DEFAULT_CATEGORIES, ...userCategories }
|
|
||||||
|
|
||||||
const categoryRows = Object.entries(allCategories).map(([name]) =>
|
|
||||||
`| ${getCategoryDescription(name, userCategories)} | \`category="${name}", load_skills=[...]\` |`
|
|
||||||
)
|
|
||||||
|
|
||||||
const agentRows = agents.map((a) => {
|
|
||||||
const shortDesc = a.description.split(".")[0] || a.description
|
|
||||||
return `| ${shortDesc} | \`agent="${a.name}"\` |`
|
|
||||||
})
|
|
||||||
|
|
||||||
return `##### Decision Matrix
|
|
||||||
|
|
||||||
| Task Domain | Use |
|
|
||||||
|-------------|-----|
|
|
||||||
${categoryRows.join("\n")}
|
|
||||||
${agentRows.join("\n")}
|
|
||||||
|
|
||||||
**NEVER provide both category AND agent - they are mutually exclusive.**`
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ATLAS_SYSTEM_PROMPT = `
|
export const ATLAS_SYSTEM_PROMPT = `
|
||||||
<identity>
|
<identity>
|
||||||
You are Atlas - the Master Orchestrator from OhMyOpenCode.
|
You are Atlas - the Master Orchestrator from OhMyOpenCode.
|
||||||
@ -499,74 +385,6 @@ You are the QA gate. Subagents lie. Verify EVERYTHING.
|
|||||||
</critical_overrides>
|
</critical_overrides>
|
||||||
`
|
`
|
||||||
|
|
||||||
function buildDynamicOrchestratorPrompt(ctx?: OrchestratorContext): string {
|
export function getDefaultAtlasPrompt(): string {
|
||||||
const agents = ctx?.availableAgents ?? []
|
|
||||||
const skills = ctx?.availableSkills ?? []
|
|
||||||
const userCategories = ctx?.userCategories
|
|
||||||
|
|
||||||
const allCategories = { ...DEFAULT_CATEGORIES, ...userCategories }
|
|
||||||
const availableCategories: AvailableCategory[] = Object.entries(allCategories).map(([name]) => ({
|
|
||||||
name,
|
|
||||||
description: getCategoryDescription(name, userCategories),
|
|
||||||
}))
|
|
||||||
|
|
||||||
const categorySection = buildCategorySection(userCategories)
|
|
||||||
const agentSection = buildAgentSelectionSection(agents)
|
|
||||||
const decisionMatrix = buildDecisionMatrix(agents, userCategories)
|
|
||||||
const skillsSection = buildSkillsSection(skills)
|
|
||||||
const categorySkillsGuide = buildCategorySkillsDelegationGuide(availableCategories, skills)
|
|
||||||
|
|
||||||
return ATLAS_SYSTEM_PROMPT
|
return ATLAS_SYSTEM_PROMPT
|
||||||
.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 restrictions = createAgentToolRestrictions([
|
|
||||||
"task",
|
|
||||||
"call_omo_agent",
|
|
||||||
])
|
|
||||||
return {
|
|
||||||
description:
|
|
||||||
"Orchestrates work via delegate_task() to complete ALL tasks in a todo list until fully done. (Atlas - OhMyOpenCode)",
|
|
||||||
mode: MODE,
|
|
||||||
...(ctx.model ? { model: ctx.model } : {}),
|
|
||||||
temperature: 0.1,
|
|
||||||
prompt: buildDynamicOrchestratorPrompt(ctx),
|
|
||||||
thinking: { type: "enabled", budgetTokens: 32000 },
|
|
||||||
color: "#10B981",
|
|
||||||
...restrictions,
|
|
||||||
} as AgentConfig
|
|
||||||
}
|
|
||||||
createAtlasAgent.mode = MODE
|
|
||||||
|
|
||||||
export const atlasPromptMetadata: AgentPromptMetadata = {
|
|
||||||
category: "advisor",
|
|
||||||
cost: "EXPENSIVE",
|
|
||||||
promptAlias: "Atlas",
|
|
||||||
triggers: [
|
|
||||||
{
|
|
||||||
domain: "Todo list orchestration",
|
|
||||||
trigger: "Complete ALL tasks in a todo list with verification",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
domain: "Multi-agent coordination",
|
|
||||||
trigger: "Parallel task execution across specialized agents",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
useWhen: [
|
|
||||||
"User provides a todo list path (.sisyphus/plans/{name}.md)",
|
|
||||||
"Multiple tasks need to be completed in sequence or parallel",
|
|
||||||
"Work requires coordination across multiple specialized agents",
|
|
||||||
],
|
|
||||||
avoidWhen: [
|
|
||||||
"Single simple task that doesn't require orchestration",
|
|
||||||
"Tasks that can be handled directly by one agent",
|
|
||||||
"When user wants to execute tasks manually",
|
|
||||||
],
|
|
||||||
keyTrigger:
|
|
||||||
"Todo list path provided OR multiple tasks requiring multi-agent orchestration",
|
|
||||||
}
|
}
|
||||||
330
src/agents/atlas/gpt.ts
Normal file
330
src/agents/atlas/gpt.ts
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
/**
|
||||||
|
* GPT-5.2 Optimized Atlas System Prompt
|
||||||
|
*
|
||||||
|
* Restructured following OpenAI's GPT-5.2 Prompting Guide principles:
|
||||||
|
* - Explicit verbosity constraints
|
||||||
|
* - Scope discipline (no extra features)
|
||||||
|
* - Tool usage rules (prefer tools over internal knowledge)
|
||||||
|
* - Uncertainty handling (ask clarifying questions)
|
||||||
|
* - Compact, direct instructions
|
||||||
|
* - XML-style section tags for clear structure
|
||||||
|
*
|
||||||
|
* Key characteristics (from GPT 5.2 Prompting Guide):
|
||||||
|
* - "Stronger instruction adherence" - follows instructions more literally
|
||||||
|
* - "Conservative grounding bias" - prefers correctness over speed
|
||||||
|
* - "More deliberate scaffolding" - builds clearer plans by default
|
||||||
|
* - Explicit decision criteria needed (model won't infer)
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const ATLAS_GPT_SYSTEM_PROMPT = `
|
||||||
|
<identity>
|
||||||
|
You are Atlas - Master Orchestrator from OhMyOpenCode.
|
||||||
|
Role: Conductor, not musician. General, not soldier.
|
||||||
|
You DELEGATE, COORDINATE, and VERIFY. You NEVER write code yourself.
|
||||||
|
</identity>
|
||||||
|
|
||||||
|
<mission>
|
||||||
|
Complete ALL tasks in a work plan via \`delegate_task()\` until fully done.
|
||||||
|
- One task per delegation
|
||||||
|
- Parallel when independent
|
||||||
|
- Verify everything
|
||||||
|
</mission>
|
||||||
|
|
||||||
|
<output_verbosity_spec>
|
||||||
|
- Default: 2-4 sentences for status updates.
|
||||||
|
- For task analysis: 1 overview sentence + ≤5 bullets (Total, Remaining, Parallel groups, Dependencies).
|
||||||
|
- For delegation prompts: Use the 6-section structure (detailed below).
|
||||||
|
- For final reports: Structured summary with bullets.
|
||||||
|
- AVOID long narrative paragraphs; prefer compact bullets and tables.
|
||||||
|
- Do NOT rephrase the task unless semantics change.
|
||||||
|
</output_verbosity_spec>
|
||||||
|
|
||||||
|
<scope_and_design_constraints>
|
||||||
|
- Implement EXACTLY and ONLY what the plan specifies.
|
||||||
|
- No extra features, no UX embellishments, no scope creep.
|
||||||
|
- If any instruction is ambiguous, choose the simplest valid interpretation OR ask.
|
||||||
|
- Do NOT invent new requirements.
|
||||||
|
- Do NOT expand task boundaries beyond what's written.
|
||||||
|
</scope_and_design_constraints>
|
||||||
|
|
||||||
|
<uncertainty_and_ambiguity>
|
||||||
|
- If a task is ambiguous or underspecified:
|
||||||
|
- Ask 1-3 precise clarifying questions, OR
|
||||||
|
- State your interpretation explicitly and proceed with the simplest approach.
|
||||||
|
- Never fabricate task details, file paths, or requirements.
|
||||||
|
- Prefer language like "Based on the plan..." instead of absolute claims.
|
||||||
|
- When unsure about parallelization, default to sequential execution.
|
||||||
|
</uncertainty_and_ambiguity>
|
||||||
|
|
||||||
|
<tool_usage_rules>
|
||||||
|
- ALWAYS use tools over internal knowledge for:
|
||||||
|
- File contents (use Read, not memory)
|
||||||
|
- Current project state (use lsp_diagnostics, glob)
|
||||||
|
- Verification (use Bash for tests/build)
|
||||||
|
- Parallelize independent tool calls when possible.
|
||||||
|
- After ANY delegation, verify with your own tool calls:
|
||||||
|
1. \`lsp_diagnostics\` at project level
|
||||||
|
2. \`Bash\` for build/test commands
|
||||||
|
3. \`Read\` for changed files
|
||||||
|
</tool_usage_rules>
|
||||||
|
|
||||||
|
<delegation_system>
|
||||||
|
## Delegation API
|
||||||
|
|
||||||
|
Use \`delegate_task()\` with EITHER category OR agent (mutually exclusive):
|
||||||
|
|
||||||
|
\`\`\`typescript
|
||||||
|
// Category + Skills (spawns Sisyphus-Junior)
|
||||||
|
delegate_task(category="[name]", load_skills=["skill-1"], run_in_background=false, prompt="...")
|
||||||
|
|
||||||
|
// Specialized Agent
|
||||||
|
delegate_task(subagent_type="[agent]", load_skills=[], run_in_background=false, prompt="...")
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
{CATEGORY_SECTION}
|
||||||
|
|
||||||
|
{AGENT_SECTION}
|
||||||
|
|
||||||
|
{DECISION_MATRIX}
|
||||||
|
|
||||||
|
{SKILLS_SECTION}
|
||||||
|
|
||||||
|
{{CATEGORY_SKILLS_DELEGATION_GUIDE}}
|
||||||
|
|
||||||
|
## 6-Section Prompt Structure (MANDATORY)
|
||||||
|
|
||||||
|
Every \`delegate_task()\` prompt MUST include ALL 6 sections:
|
||||||
|
|
||||||
|
\`\`\`markdown
|
||||||
|
## 1. TASK
|
||||||
|
[Quote EXACT checkbox item. Be obsessively specific.]
|
||||||
|
|
||||||
|
## 2. EXPECTED OUTCOME
|
||||||
|
- [ ] Files created/modified: [exact paths]
|
||||||
|
- [ ] Functionality: [exact behavior]
|
||||||
|
- [ ] Verification: \`[command]\` passes
|
||||||
|
|
||||||
|
## 3. REQUIRED TOOLS
|
||||||
|
- [tool]: [what to search/check]
|
||||||
|
- context7: Look up [library] docs
|
||||||
|
- ast-grep: \`sg --pattern '[pattern]' --lang [lang]\`
|
||||||
|
|
||||||
|
## 4. MUST DO
|
||||||
|
- Follow pattern in [reference file:lines]
|
||||||
|
- Write tests for [specific cases]
|
||||||
|
- Append findings to notepad (never overwrite)
|
||||||
|
|
||||||
|
## 5. MUST NOT DO
|
||||||
|
- Do NOT modify files outside [scope]
|
||||||
|
- Do NOT add dependencies
|
||||||
|
- Do NOT skip verification
|
||||||
|
|
||||||
|
## 6. CONTEXT
|
||||||
|
### Notepad Paths
|
||||||
|
- READ: .sisyphus/notepads/{plan-name}/*.md
|
||||||
|
- WRITE: Append to appropriate category
|
||||||
|
|
||||||
|
### Inherited Wisdom
|
||||||
|
[From notepad - conventions, gotchas, decisions]
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
[What previous tasks built]
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**Minimum 30 lines per delegation prompt.**
|
||||||
|
</delegation_system>
|
||||||
|
|
||||||
|
<workflow>
|
||||||
|
## Step 0: Register Tracking
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
TodoWrite([{ id: "orchestrate-plan", content: "Complete ALL tasks in work plan", status: "in_progress", priority: "high" }])
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## Step 1: Analyze Plan
|
||||||
|
|
||||||
|
1. Read the todo list file
|
||||||
|
2. Parse incomplete checkboxes \`- [ ]\`
|
||||||
|
3. Build parallelization map
|
||||||
|
|
||||||
|
Output format:
|
||||||
|
\`\`\`
|
||||||
|
TASK ANALYSIS:
|
||||||
|
- Total: [N], Remaining: [M]
|
||||||
|
- Parallel Groups: [list]
|
||||||
|
- Sequential: [list]
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## Step 2: Initialize Notepad
|
||||||
|
|
||||||
|
\`\`\`bash
|
||||||
|
mkdir -p .sisyphus/notepads/{plan-name}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Structure: learnings.md, decisions.md, issues.md, problems.md
|
||||||
|
|
||||||
|
## Step 3: Execute Tasks
|
||||||
|
|
||||||
|
### 3.1 Parallelization Check
|
||||||
|
- Parallel tasks → invoke multiple \`delegate_task()\` in ONE message
|
||||||
|
- Sequential → process one at a time
|
||||||
|
|
||||||
|
### 3.2 Pre-Delegation (MANDATORY)
|
||||||
|
\`\`\`
|
||||||
|
Read(".sisyphus/notepads/{plan-name}/learnings.md")
|
||||||
|
Read(".sisyphus/notepads/{plan-name}/issues.md")
|
||||||
|
\`\`\`
|
||||||
|
Extract wisdom → include in prompt.
|
||||||
|
|
||||||
|
### 3.3 Invoke delegate_task()
|
||||||
|
|
||||||
|
\`\`\`typescript
|
||||||
|
delegate_task(category="[cat]", load_skills=["[skills]"], run_in_background=false, prompt=\`[6-SECTION PROMPT]\`)
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### 3.4 Verify (PROJECT-LEVEL QA)
|
||||||
|
|
||||||
|
After EVERY delegation:
|
||||||
|
1. \`lsp_diagnostics(filePath=".")\` → ZERO errors
|
||||||
|
2. \`Bash("bun run build")\` → exit 0
|
||||||
|
3. \`Bash("bun test")\` → all pass
|
||||||
|
4. \`Read\` changed files → confirm requirements met
|
||||||
|
|
||||||
|
Checklist:
|
||||||
|
- [ ] lsp_diagnostics clean
|
||||||
|
- [ ] Build passes
|
||||||
|
- [ ] Tests pass
|
||||||
|
- [ ] Files match requirements
|
||||||
|
|
||||||
|
### 3.5 Handle Failures
|
||||||
|
|
||||||
|
**CRITICAL: Use \`session_id\` for retries.**
|
||||||
|
|
||||||
|
\`\`\`typescript
|
||||||
|
delegate_task(session_id="ses_xyz789", load_skills=[...], prompt="FAILED: {error}. Fix by: {instruction}")
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
- Maximum 3 retries per task
|
||||||
|
- If blocked: document and continue to next independent task
|
||||||
|
|
||||||
|
### 3.6 Loop Until Done
|
||||||
|
|
||||||
|
Repeat Step 3 until all tasks complete.
|
||||||
|
|
||||||
|
## Step 4: Final Report
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
ORCHESTRATION COMPLETE
|
||||||
|
TODO LIST: [path]
|
||||||
|
COMPLETED: [N/N]
|
||||||
|
FAILED: [count]
|
||||||
|
|
||||||
|
EXECUTION SUMMARY:
|
||||||
|
- Task 1: SUCCESS (category)
|
||||||
|
- Task 2: SUCCESS (agent)
|
||||||
|
|
||||||
|
FILES MODIFIED: [list]
|
||||||
|
ACCUMULATED WISDOM: [from notepad]
|
||||||
|
\`\`\`
|
||||||
|
</workflow>
|
||||||
|
|
||||||
|
<parallel_execution>
|
||||||
|
**Exploration (explore/librarian)**: ALWAYS background
|
||||||
|
\`\`\`typescript
|
||||||
|
delegate_task(subagent_type="explore", run_in_background=true, ...)
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**Task execution**: NEVER background
|
||||||
|
\`\`\`typescript
|
||||||
|
delegate_task(category="...", run_in_background=false, ...)
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**Parallel task groups**: Invoke multiple in ONE message
|
||||||
|
\`\`\`typescript
|
||||||
|
delegate_task(category="quick", prompt="Task 2...")
|
||||||
|
delegate_task(category="quick", prompt="Task 3...")
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**Background management**:
|
||||||
|
- Collect: \`background_output(task_id="...")\`
|
||||||
|
- Cleanup: \`background_cancel(all=true)\`
|
||||||
|
</parallel_execution>
|
||||||
|
|
||||||
|
<notepad_protocol>
|
||||||
|
**Purpose**: Cumulative intelligence for STATELESS subagents.
|
||||||
|
|
||||||
|
**Before EVERY delegation**:
|
||||||
|
1. Read notepad files
|
||||||
|
2. Extract relevant wisdom
|
||||||
|
3. Include as "Inherited Wisdom" in prompt
|
||||||
|
|
||||||
|
**After EVERY completion**:
|
||||||
|
- Instruct subagent to append findings (never overwrite)
|
||||||
|
|
||||||
|
**Paths**:
|
||||||
|
- Plan: \`.sisyphus/plans/{name}.md\` (READ ONLY)
|
||||||
|
- Notepad: \`.sisyphus/notepads/{name}/\` (READ/APPEND)
|
||||||
|
</notepad_protocol>
|
||||||
|
|
||||||
|
<verification_rules>
|
||||||
|
You are the QA gate. Subagents lie. Verify EVERYTHING.
|
||||||
|
|
||||||
|
**After each delegation**:
|
||||||
|
| Step | Tool | Expected |
|
||||||
|
|------|------|----------|
|
||||||
|
| 1 | \`lsp_diagnostics(".")\` | ZERO errors |
|
||||||
|
| 2 | \`Bash("bun run build")\` | exit 0 |
|
||||||
|
| 3 | \`Bash("bun test")\` | all pass |
|
||||||
|
| 4 | \`Read\` changed files | matches requirements |
|
||||||
|
|
||||||
|
**No evidence = not complete.**
|
||||||
|
</verification_rules>
|
||||||
|
|
||||||
|
<boundaries>
|
||||||
|
**YOU DO**:
|
||||||
|
- Read files (context, verification)
|
||||||
|
- Run commands (verification)
|
||||||
|
- Use lsp_diagnostics, grep, glob
|
||||||
|
- Manage todos
|
||||||
|
- Coordinate and verify
|
||||||
|
|
||||||
|
**YOU DELEGATE**:
|
||||||
|
- All code writing/editing
|
||||||
|
- All bug fixes
|
||||||
|
- All test creation
|
||||||
|
- All documentation
|
||||||
|
- All git operations
|
||||||
|
</boundaries>
|
||||||
|
|
||||||
|
<critical_rules>
|
||||||
|
**NEVER**:
|
||||||
|
- Write/edit code yourself
|
||||||
|
- Trust subagent claims without verification
|
||||||
|
- Use run_in_background=true for task execution
|
||||||
|
- Send prompts under 30 lines
|
||||||
|
- Skip project-level lsp_diagnostics
|
||||||
|
- Batch multiple tasks in one delegation
|
||||||
|
- Start fresh session for failures (use session_id)
|
||||||
|
|
||||||
|
**ALWAYS**:
|
||||||
|
- Include ALL 6 sections in delegation prompts
|
||||||
|
- Read notepad before every delegation
|
||||||
|
- Run project-level QA after every delegation
|
||||||
|
- Pass inherited wisdom to every subagent
|
||||||
|
- Parallelize independent tasks
|
||||||
|
- Store and reuse session_id for retries
|
||||||
|
</critical_rules>
|
||||||
|
|
||||||
|
<user_updates_spec>
|
||||||
|
- Send brief updates (1-2 sentences) only when:
|
||||||
|
- Starting a new major phase
|
||||||
|
- Discovering something that changes the plan
|
||||||
|
- Avoid narrating routine tool calls
|
||||||
|
- Each update must include a concrete outcome ("Found X", "Verified Y", "Delegated Z")
|
||||||
|
- Do NOT expand task scope; if you notice new work, call it out as optional
|
||||||
|
</user_updates_spec>
|
||||||
|
`
|
||||||
|
|
||||||
|
export function getGptAtlasPrompt(): string {
|
||||||
|
return ATLAS_GPT_SYSTEM_PROMPT
|
||||||
|
}
|
||||||
153
src/agents/atlas/index.ts
Normal file
153
src/agents/atlas/index.ts
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/**
|
||||||
|
* Atlas - Master Orchestrator Agent
|
||||||
|
*
|
||||||
|
* Orchestrates work via delegate_task() to complete ALL tasks in a todo list until fully done.
|
||||||
|
* You are the conductor of a symphony of specialized agents.
|
||||||
|
*
|
||||||
|
* Routing:
|
||||||
|
* 1. GPT models (openai/*, github-copilot/gpt-*) → gpt.ts (GPT-5.2 optimized)
|
||||||
|
* 2. Default (Claude, etc.) → default.ts (Claude-optimized)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { AgentConfig } from "@opencode-ai/sdk"
|
||||||
|
import type { AgentMode, AgentPromptMetadata } from "../types"
|
||||||
|
import { isGptModel } from "../types"
|
||||||
|
import type { AvailableAgent, AvailableSkill, AvailableCategory } from "../dynamic-agent-prompt-builder"
|
||||||
|
import { buildCategorySkillsDelegationGuide } from "../dynamic-agent-prompt-builder"
|
||||||
|
import type { CategoryConfig } from "../../config/schema"
|
||||||
|
import { DEFAULT_CATEGORIES } from "../../tools/delegate-task/constants"
|
||||||
|
import { createAgentToolRestrictions } from "../../shared/permission-compat"
|
||||||
|
|
||||||
|
import { ATLAS_SYSTEM_PROMPT, getDefaultAtlasPrompt } from "./default"
|
||||||
|
import { ATLAS_GPT_SYSTEM_PROMPT, getGptAtlasPrompt } from "./gpt"
|
||||||
|
import {
|
||||||
|
getCategoryDescription,
|
||||||
|
buildAgentSelectionSection,
|
||||||
|
buildCategorySection,
|
||||||
|
buildSkillsSection,
|
||||||
|
buildDecisionMatrix,
|
||||||
|
} from "./utils"
|
||||||
|
|
||||||
|
export { ATLAS_SYSTEM_PROMPT, getDefaultAtlasPrompt } from "./default"
|
||||||
|
export { ATLAS_GPT_SYSTEM_PROMPT, getGptAtlasPrompt } from "./gpt"
|
||||||
|
export {
|
||||||
|
getCategoryDescription,
|
||||||
|
buildAgentSelectionSection,
|
||||||
|
buildCategorySection,
|
||||||
|
buildSkillsSection,
|
||||||
|
buildDecisionMatrix,
|
||||||
|
} from "./utils"
|
||||||
|
export { isGptModel }
|
||||||
|
|
||||||
|
const MODE: AgentMode = "primary"
|
||||||
|
|
||||||
|
export type AtlasPromptSource = "default" | "gpt"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines which Atlas prompt to use based on model.
|
||||||
|
*/
|
||||||
|
export function getAtlasPromptSource(model?: string): AtlasPromptSource {
|
||||||
|
if (model && isGptModel(model)) {
|
||||||
|
return "gpt"
|
||||||
|
}
|
||||||
|
return "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OrchestratorContext {
|
||||||
|
model?: string
|
||||||
|
availableAgents?: AvailableAgent[]
|
||||||
|
availableSkills?: AvailableSkill[]
|
||||||
|
userCategories?: Record<string, CategoryConfig>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the appropriate Atlas prompt based on model.
|
||||||
|
*/
|
||||||
|
export function getAtlasPrompt(model?: string): string {
|
||||||
|
const source = getAtlasPromptSource(model)
|
||||||
|
|
||||||
|
switch (source) {
|
||||||
|
case "gpt":
|
||||||
|
return getGptAtlasPrompt()
|
||||||
|
case "default":
|
||||||
|
default:
|
||||||
|
return getDefaultAtlasPrompt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildDynamicOrchestratorPrompt(ctx?: OrchestratorContext): string {
|
||||||
|
const agents = ctx?.availableAgents ?? []
|
||||||
|
const skills = ctx?.availableSkills ?? []
|
||||||
|
const userCategories = ctx?.userCategories
|
||||||
|
const model = ctx?.model
|
||||||
|
|
||||||
|
const allCategories = { ...DEFAULT_CATEGORIES, ...userCategories }
|
||||||
|
const availableCategories: AvailableCategory[] = Object.entries(allCategories).map(([name]) => ({
|
||||||
|
name,
|
||||||
|
description: getCategoryDescription(name, userCategories),
|
||||||
|
}))
|
||||||
|
|
||||||
|
const categorySection = buildCategorySection(userCategories)
|
||||||
|
const agentSection = buildAgentSelectionSection(agents)
|
||||||
|
const decisionMatrix = buildDecisionMatrix(agents, userCategories)
|
||||||
|
const skillsSection = buildSkillsSection(skills)
|
||||||
|
const categorySkillsGuide = buildCategorySkillsDelegationGuide(availableCategories, skills)
|
||||||
|
|
||||||
|
const basePrompt = getAtlasPrompt(model)
|
||||||
|
|
||||||
|
return 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 restrictions = createAgentToolRestrictions([
|
||||||
|
"task",
|
||||||
|
"call_omo_agent",
|
||||||
|
])
|
||||||
|
|
||||||
|
const baseConfig = {
|
||||||
|
description:
|
||||||
|
"Orchestrates work via delegate_task() to complete ALL tasks in a todo list until fully done. (Atlas - OhMyOpenCode)",
|
||||||
|
mode: MODE,
|
||||||
|
...(ctx.model ? { model: ctx.model } : {}),
|
||||||
|
temperature: 0.1,
|
||||||
|
prompt: buildDynamicOrchestratorPrompt(ctx),
|
||||||
|
color: "#10B981",
|
||||||
|
...restrictions,
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseConfig as AgentConfig
|
||||||
|
}
|
||||||
|
createAtlasAgent.mode = MODE
|
||||||
|
|
||||||
|
export const atlasPromptMetadata: AgentPromptMetadata = {
|
||||||
|
category: "advisor",
|
||||||
|
cost: "EXPENSIVE",
|
||||||
|
promptAlias: "Atlas",
|
||||||
|
triggers: [
|
||||||
|
{
|
||||||
|
domain: "Todo list orchestration",
|
||||||
|
trigger: "Complete ALL tasks in a todo list with verification",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domain: "Multi-agent coordination",
|
||||||
|
trigger: "Parallel task execution across specialized agents",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
useWhen: [
|
||||||
|
"User provides a todo list path (.sisyphus/plans/{name}.md)",
|
||||||
|
"Multiple tasks need to be completed in sequence or parallel",
|
||||||
|
"Work requires coordination across multiple specialized agents",
|
||||||
|
],
|
||||||
|
avoidWhen: [
|
||||||
|
"Single simple task that doesn't require orchestration",
|
||||||
|
"Tasks that can be handled directly by one agent",
|
||||||
|
"When user wants to execute tasks manually",
|
||||||
|
],
|
||||||
|
keyTrigger:
|
||||||
|
"Todo list path provided OR multiple tasks requiring multi-agent orchestration",
|
||||||
|
}
|
||||||
110
src/agents/atlas/utils.ts
Normal file
110
src/agents/atlas/utils.ts
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* Atlas Orchestrator - Shared Utilities
|
||||||
|
*
|
||||||
|
* Common functions for building dynamic prompt sections used by both
|
||||||
|
* default (Claude-optimized) and GPT-optimized prompts.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { CategoryConfig } from "../../config/schema"
|
||||||
|
import type { AvailableAgent, AvailableSkill } from "../dynamic-agent-prompt-builder"
|
||||||
|
import { DEFAULT_CATEGORIES, CATEGORY_DESCRIPTIONS } from "../../tools/delegate-task/constants"
|
||||||
|
|
||||||
|
export const getCategoryDescription = (name: string, userCategories?: Record<string, CategoryConfig>) =>
|
||||||
|
userCategories?.[name]?.description ?? CATEGORY_DESCRIPTIONS[name] ?? "General tasks"
|
||||||
|
|
||||||
|
export function buildAgentSelectionSection(agents: AvailableAgent[]): string {
|
||||||
|
if (agents.length === 0) {
|
||||||
|
return `##### Option B: Use AGENT directly (for specialized experts)
|
||||||
|
|
||||||
|
No agents available.`
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows = agents.map((a) => {
|
||||||
|
const shortDesc = a.description.split(".")[0] || a.description
|
||||||
|
return `| \`${a.name}\` | ${shortDesc} |`
|
||||||
|
})
|
||||||
|
|
||||||
|
return `##### Option B: Use AGENT directly (for specialized experts)
|
||||||
|
|
||||||
|
| Agent | Best For |
|
||||||
|
|-------|----------|
|
||||||
|
${rows.join("\n")}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildCategorySection(userCategories?: Record<string, CategoryConfig>): string {
|
||||||
|
const allCategories = { ...DEFAULT_CATEGORIES, ...userCategories }
|
||||||
|
const categoryRows = Object.entries(allCategories).map(([name, config]) => {
|
||||||
|
const temp = config.temperature ?? 0.5
|
||||||
|
return `| \`${name}\` | ${temp} | ${getCategoryDescription(name, userCategories)} |`
|
||||||
|
})
|
||||||
|
|
||||||
|
return `##### Option A: Use CATEGORY (for domain-specific work)
|
||||||
|
|
||||||
|
Categories spawn \`Sisyphus-Junior-{category}\` with optimized settings:
|
||||||
|
|
||||||
|
| Category | Temperature | Best For |
|
||||||
|
|----------|-------------|----------|
|
||||||
|
${categoryRows.join("\n")}
|
||||||
|
|
||||||
|
\`\`\`typescript
|
||||||
|
delegate_task(category="[category-name]", load_skills=[...], prompt="...")
|
||||||
|
\`\`\``
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildSkillsSection(skills: AvailableSkill[]): string {
|
||||||
|
if (skills.length === 0) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
const skillRows = skills.map((s) => {
|
||||||
|
const shortDesc = s.description.split(".")[0] || s.description
|
||||||
|
return `| \`${s.name}\` | ${shortDesc} |`
|
||||||
|
})
|
||||||
|
|
||||||
|
return `
|
||||||
|
#### 3.2.2: Skill Selection (PREPEND TO PROMPT)
|
||||||
|
|
||||||
|
**Skills are specialized instructions that guide subagent behavior. Consider them alongside category selection.**
|
||||||
|
|
||||||
|
| Skill | When to Use |
|
||||||
|
|-------|-------------|
|
||||||
|
${skillRows.join("\n")}
|
||||||
|
|
||||||
|
**MANDATORY: Evaluate ALL skills for relevance to your task.**
|
||||||
|
|
||||||
|
Read each skill's description and ask: "Does this skill's domain overlap with my task?"
|
||||||
|
- If YES: INCLUDE in load_skills=[...]
|
||||||
|
- If NO: You MUST justify why in your pre-delegation declaration
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
\`\`\`typescript
|
||||||
|
delegate_task(category="[category]", load_skills=["skill-1", "skill-2"], prompt="...")
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**IMPORTANT:**
|
||||||
|
- Skills get prepended to the subagent's prompt, providing domain-specific instructions
|
||||||
|
- Subagents are STATELESS - they don't know what skills exist unless you include them
|
||||||
|
- Missing a relevant skill = suboptimal output quality`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildDecisionMatrix(agents: AvailableAgent[], userCategories?: Record<string, CategoryConfig>): string {
|
||||||
|
const allCategories = { ...DEFAULT_CATEGORIES, ...userCategories }
|
||||||
|
|
||||||
|
const categoryRows = Object.entries(allCategories).map(([name]) =>
|
||||||
|
`| ${getCategoryDescription(name, userCategories)} | \`category="${name}", load_skills=[...]\` |`
|
||||||
|
)
|
||||||
|
|
||||||
|
const agentRows = agents.map((a) => {
|
||||||
|
const shortDesc = a.description.split(".")[0] || a.description
|
||||||
|
return `| ${shortDesc} | \`agent="${a.name}"\` |`
|
||||||
|
})
|
||||||
|
|
||||||
|
return `##### Decision Matrix
|
||||||
|
|
||||||
|
| Task Domain | Use |
|
||||||
|
|-------------|-----|
|
||||||
|
${categoryRows.join("\n")}
|
||||||
|
${agentRows.join("\n")}
|
||||||
|
|
||||||
|
**NEVER provide both category AND agent - they are mutually exclusive.**`
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user