feat(agents): add GPT-5.2 optimized prompt for sisyphus-junior

Restructure sisyphus-junior agent to use model-specific prompts:
- GPT models: GPT-5.2 prompting guide principles (verbosity constraints,
  scope discipline, tool usage rules, explicit decision criteria)
- Claude models: Original prompt with extended reasoning context

Directory structure now mirrors atlas/ pattern for consistency.
This commit is contained in:
YeonGyu-Kim 2026-02-03 14:12:22 +09:00
parent 1a0cc424b3
commit bf87bf473f
5 changed files with 454 additions and 136 deletions

View File

@ -1,134 +0,0 @@
import type { AgentConfig } from "@opencode-ai/sdk"
import type { AgentMode } from "./types"
import { isGptModel } from "./types"
import type { AgentOverrideConfig } from "../config/schema"
import {
createAgentToolRestrictions,
type PermissionValue,
} from "../shared/permission-compat"
const MODE: AgentMode = "subagent"
function buildTodoDisciplineSection(useTaskSystem: boolean): string {
if (useTaskSystem) {
return `<Task_Discipline>
TASK OBSESSION (NON-NEGOTIABLE):
- 2+ steps TaskCreate FIRST, atomic breakdown
- TaskUpdate(status="in_progress") before starting (ONE at a time)
- TaskUpdate(status="completed") IMMEDIATELY after each step
- NEVER batch completions
No tasks on multi-step work = INCOMPLETE WORK.
</Task_Discipline>`
}
return `<Todo_Discipline>
TODO OBSESSION (NON-NEGOTIABLE):
- 2+ steps todowrite FIRST, atomic breakdown
- Mark in_progress before starting (ONE at a time)
- Mark completed IMMEDIATELY after each step
- NEVER batch completions
No todos on multi-step work = INCOMPLETE WORK.
</Todo_Discipline>`
}
function buildSisyphusJuniorPrompt(useTaskSystem: boolean, promptAppend?: string): string {
const todoDiscipline = buildTodoDisciplineSection(useTaskSystem)
const verificationText = useTaskSystem ? "All tasks marked completed" : "All todos marked completed"
const prompt = `<Role>
Sisyphus-Junior - Focused executor from OhMyOpenCode.
Execute tasks directly. NEVER delegate or spawn other agents.
</Role>
<Critical_Constraints>
BLOCKED ACTIONS (will fail if attempted):
- task tool: BLOCKED
- delegate_task tool: BLOCKED
ALLOWED: call_omo_agent - You CAN spawn explore/librarian agents for research.
You work ALONE for implementation. No delegation of implementation tasks.
</Critical_Constraints>
${todoDiscipline}
<Verification>
Task NOT complete without:
- lsp_diagnostics clean on changed files
- Build passes (if applicable)
- ${verificationText}
</Verification>
<Style>
- Start immediately. No acknowledgments.
- Match user's communication style.
- Dense > verbose.
</Style>`
if (!promptAppend) return prompt
return prompt + "\n\n" + promptAppend
}
// Core tools that Sisyphus-Junior must NEVER have access to
// Note: call_omo_agent is ALLOWED so subagents can spawn explore/librarian
const BLOCKED_TOOLS = ["task", "delegate_task"]
export const SISYPHUS_JUNIOR_DEFAULTS = {
model: "anthropic/claude-sonnet-4-5",
temperature: 0.1,
} as const
export function createSisyphusJuniorAgentWithOverrides(
override: AgentOverrideConfig | undefined,
systemDefaultModel?: string,
useTaskSystem = false
): AgentConfig {
if (override?.disable) {
override = undefined
}
const model = override?.model ?? systemDefaultModel ?? SISYPHUS_JUNIOR_DEFAULTS.model
const temperature = override?.temperature ?? SISYPHUS_JUNIOR_DEFAULTS.temperature
const promptAppend = override?.prompt_append
const prompt = buildSisyphusJuniorPrompt(useTaskSystem, promptAppend)
const baseRestrictions = createAgentToolRestrictions(BLOCKED_TOOLS)
const userPermission = (override?.permission ?? {}) as Record<string, PermissionValue>
const basePermission = baseRestrictions.permission
const merged: Record<string, PermissionValue> = { ...userPermission }
for (const tool of BLOCKED_TOOLS) {
merged[tool] = "deny"
}
merged.call_omo_agent = "allow"
const toolsConfig = { permission: { ...merged, ...basePermission } }
const base: AgentConfig = {
description: override?.description ??
"Focused task executor. Same discipline, no delegation. (Sisyphus-Junior - OhMyOpenCode)",
mode: MODE,
model,
temperature,
maxTokens: 64000,
prompt,
color: override?.color ?? "#20B2AA",
...toolsConfig,
}
if (override?.top_p !== undefined) {
base.top_p = override.top_p
}
if (isGptModel(model)) {
return { ...base, reasoningEffort: "medium" } as AgentConfig
}
return {
...base,
thinking: { type: "enabled", budgetTokens: 32000 },
} as AgentConfig
}
createSisyphusJuniorAgentWithOverrides.mode = MODE

View File

@ -0,0 +1,74 @@
/**
* Default Sisyphus-Junior system prompt optimized for Claude series models.
*
* Key characteristics:
* - Optimized for Claude's tendency to be "helpful" by forcing explicit constraints
* - Strong emphasis on blocking delegation attempts
* - Extended reasoning context for complex tasks
*/
export function buildDefaultSisyphusJuniorPrompt(
useTaskSystem: boolean,
promptAppend?: string
): string {
const todoDiscipline = buildTodoDisciplineSection(useTaskSystem)
const verificationText = useTaskSystem
? "All tasks marked completed"
: "All todos marked completed"
const prompt = `<Role>
Sisyphus-Junior - Focused executor from OhMyOpenCode.
Execute tasks directly. NEVER delegate or spawn other agents.
</Role>
<Critical_Constraints>
BLOCKED ACTIONS (will fail if attempted):
- task tool: BLOCKED
- delegate_task tool: BLOCKED
ALLOWED: call_omo_agent - You CAN spawn explore/librarian agents for research.
You work ALONE for implementation. No delegation of implementation tasks.
</Critical_Constraints>
${todoDiscipline}
<Verification>
Task NOT complete without:
- lsp_diagnostics clean on changed files
- Build passes (if applicable)
- ${verificationText}
</Verification>
<Style>
- Start immediately. No acknowledgments.
- Match user's communication style.
- Dense > verbose.
</Style>`
if (!promptAppend) return prompt
return prompt + "\n\n" + promptAppend
}
function buildTodoDisciplineSection(useTaskSystem: boolean): string {
if (useTaskSystem) {
return `<Task_Discipline>
TASK OBSESSION (NON-NEGOTIABLE):
- 2+ steps TaskCreate FIRST, atomic breakdown
- TaskUpdate(status="in_progress") before starting (ONE at a time)
- TaskUpdate(status="completed") IMMEDIATELY after each step
- NEVER batch completions
No tasks on multi-step work = INCOMPLETE WORK.
</Task_Discipline>`
}
return `<Todo_Discipline>
TODO OBSESSION (NON-NEGOTIABLE):
- 2+ steps todowrite FIRST, atomic breakdown
- Mark in_progress before starting (ONE at a time)
- Mark completed IMMEDIATELY after each step
- NEVER batch completions
No todos on multi-step work = INCOMPLETE WORK.
</Todo_Discipline>`
}

View File

@ -0,0 +1,129 @@
/**
* GPT-5.2 Optimized Sisyphus-Junior System Prompt
*
* Restructured following OpenAI's GPT-5.2 Prompting Guide principles:
* - Explicit verbosity constraints (2-4 sentences for updates)
* - Scope discipline (no extra features, implement exactly what's specified)
* - 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 function buildGptSisyphusJuniorPrompt(
useTaskSystem: boolean,
promptAppend?: string
): string {
const taskDiscipline = buildGptTaskDisciplineSection(useTaskSystem)
const verificationText = useTaskSystem
? "All tasks marked completed"
: "All todos marked completed"
const prompt = `<identity>
You are Sisyphus-Junior - Focused task executor from OhMyOpenCode.
Role: Execute tasks directly. You work ALONE.
</identity>
<output_verbosity_spec>
- Default: 2-4 sentences for status updates.
- For progress: 1 sentence + current step.
- AVOID long explanations; prefer compact bullets.
- Do NOT rephrase the task unless semantics change.
</output_verbosity_spec>
<scope_and_design_constraints>
- Implement EXACTLY and ONLY what is requested.
- 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>
<blocked_actions>
BLOCKED (will fail if attempted):
| Tool | Status |
|------|--------|
| task | BLOCKED |
| delegate_task | BLOCKED |
ALLOWED:
| Tool | Usage |
|------|-------|
| call_omo_agent | Spawn explore/librarian for research ONLY |
You work ALONE for implementation. No delegation.
</blocked_actions>
<uncertainty_and_ambiguity>
- If a task is ambiguous or underspecified:
- Ask 1-2 precise clarifying questions, OR
- State your interpretation explicitly and proceed with the simplest approach.
- Never fabricate file paths, requirements, or behavior.
- Prefer language like "Based on the request..." instead of absolute claims.
</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.
</tool_usage_rules>
${taskDiscipline}
<verification_spec>
Task NOT complete without evidence:
| Check | Tool | Expected |
|-------|------|----------|
| Diagnostics | lsp_diagnostics | ZERO errors on changed files |
| Build | Bash | Exit code 0 (if applicable) |
| Tracking | ${useTaskSystem ? "TaskUpdate" : "todowrite"} | ${verificationText} |
**No evidence = not complete.**
</verification_spec>
<style_spec>
- Start immediately. No acknowledgments ("I'll...", "Let me...").
- Match user's communication style.
- Dense > verbose.
- Use structured output (bullets, tables) over prose.
</style_spec>`
if (!promptAppend) return prompt
return prompt + "\n\n" + promptAppend
}
function buildGptTaskDisciplineSection(useTaskSystem: boolean): string {
if (useTaskSystem) {
return `<task_discipline_spec>
TASK TRACKING (NON-NEGOTIABLE):
| Trigger | Action |
|---------|--------|
| 2+ steps | TaskCreate FIRST, atomic breakdown |
| Starting step | TaskUpdate(status="in_progress") - ONE at a time |
| Completing step | TaskUpdate(status="completed") IMMEDIATELY |
| Batching | NEVER batch completions |
No tasks on multi-step work = INCOMPLETE WORK.
</task_discipline_spec>`
}
return `<todo_discipline_spec>
TODO TRACKING (NON-NEGOTIABLE):
| Trigger | Action |
|---------|--------|
| 2+ steps | todowrite FIRST, atomic breakdown |
| Starting step | Mark in_progress - ONE at a time |
| Completing step | Mark completed IMMEDIATELY |
| Batching | NEVER batch completions |
No todos on multi-step work = INCOMPLETE WORK.
</todo_discipline_spec>`
}

View File

@ -1,5 +1,10 @@
import { describe, expect, test } from "bun:test"
import { createSisyphusJuniorAgentWithOverrides, SISYPHUS_JUNIOR_DEFAULTS } from "./sisyphus-junior"
import {
createSisyphusJuniorAgentWithOverrides,
SISYPHUS_JUNIOR_DEFAULTS,
getSisyphusJuniorPromptSource,
buildSisyphusJuniorPrompt,
} from "./index"
describe("createSisyphusJuniorAgentWithOverrides", () => {
describe("honored fields", () => {
@ -212,7 +217,31 @@ describe("createSisyphusJuniorAgentWithOverrides", () => {
// then
expect(result.prompt).toContain("Sisyphus-Junior")
expect(result.prompt).toContain("You work ALONE")
})
test("Claude model uses default prompt with BLOCKED ACTIONS section", () => {
// given
const override = { model: "anthropic/claude-sonnet-4-5" }
// when
const result = createSisyphusJuniorAgentWithOverrides(override)
// then
expect(result.prompt).toContain("BLOCKED ACTIONS")
expect(result.prompt).not.toContain("<blocked_actions>")
})
test("GPT model uses GPT-optimized prompt with blocked_actions section", () => {
// given
const override = { model: "openai/gpt-5.2" }
// when
const result = createSisyphusJuniorAgentWithOverrides(override)
// then
expect(result.prompt).toContain("<blocked_actions>")
expect(result.prompt).toContain("<output_verbosity_spec>")
expect(result.prompt).toContain("<scope_and_design_constraints>")
})
test("prompt_append is added after base prompt", () => {
@ -225,8 +254,107 @@ describe("createSisyphusJuniorAgentWithOverrides", () => {
// then
const baseEndIndex = result.prompt!.indexOf("Dense > verbose.")
const appendIndex = result.prompt!.indexOf("CUSTOM_MARKER_FOR_TEST")
expect(baseEndIndex).not.toBe(-1) // Guard: anchor text must exist in base prompt
expect(baseEndIndex).not.toBe(-1)
expect(appendIndex).toBeGreaterThan(baseEndIndex)
})
})
})
describe("getSisyphusJuniorPromptSource", () => {
test("returns 'gpt' for OpenAI models", () => {
// given
const model = "openai/gpt-5.2"
// when
const source = getSisyphusJuniorPromptSource(model)
// then
expect(source).toBe("gpt")
})
test("returns 'gpt' for GitHub Copilot GPT models", () => {
// given
const model = "github-copilot/gpt-4o"
// when
const source = getSisyphusJuniorPromptSource(model)
// then
expect(source).toBe("gpt")
})
test("returns 'default' for Claude models", () => {
// given
const model = "anthropic/claude-sonnet-4-5"
// when
const source = getSisyphusJuniorPromptSource(model)
// then
expect(source).toBe("default")
})
test("returns 'default' for undefined model", () => {
// given
const model = undefined
// when
const source = getSisyphusJuniorPromptSource(model)
// then
expect(source).toBe("default")
})
})
describe("buildSisyphusJuniorPrompt", () => {
test("GPT model prompt contains GPT-5.2 specific sections", () => {
// given
const model = "openai/gpt-5.2"
// when
const prompt = buildSisyphusJuniorPrompt(model, false)
// then
expect(prompt).toContain("<identity>")
expect(prompt).toContain("<output_verbosity_spec>")
expect(prompt).toContain("<scope_and_design_constraints>")
expect(prompt).toContain("<tool_usage_rules>")
})
test("Claude model prompt contains Claude-specific sections", () => {
// given
const model = "anthropic/claude-sonnet-4-5"
// when
const prompt = buildSisyphusJuniorPrompt(model, false)
// then
expect(prompt).toContain("<Role>")
expect(prompt).toContain("<Critical_Constraints>")
expect(prompt).toContain("BLOCKED ACTIONS")
})
test("useTaskSystem=true includes Task_Discipline for GPT", () => {
// given
const model = "openai/gpt-5.2"
// when
const prompt = buildSisyphusJuniorPrompt(model, true)
// then
expect(prompt).toContain("<task_discipline_spec>")
expect(prompt).toContain("TaskCreate")
})
test("useTaskSystem=false includes Todo_Discipline for Claude", () => {
// given
const model = "anthropic/claude-sonnet-4-5"
// when
const prompt = buildSisyphusJuniorPrompt(model, false)
// then
expect(prompt).toContain("<Todo_Discipline>")
expect(prompt).toContain("todowrite")
})
})

View File

@ -0,0 +1,121 @@
/**
* Sisyphus-Junior - Focused Task Executor
*
* Executes delegated tasks directly without spawning other agents.
* Category-spawned executor with domain-specific configurations.
*
* 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 } from "../types"
import { isGptModel } from "../types"
import type { AgentOverrideConfig } from "../../config/schema"
import {
createAgentToolRestrictions,
type PermissionValue,
} from "../../shared/permission-compat"
import { buildDefaultSisyphusJuniorPrompt } from "./default"
import { buildGptSisyphusJuniorPrompt } from "./gpt"
export { buildDefaultSisyphusJuniorPrompt } from "./default"
export { buildGptSisyphusJuniorPrompt } from "./gpt"
const MODE: AgentMode = "subagent"
// Core tools that Sisyphus-Junior must NEVER have access to
// Note: call_omo_agent is ALLOWED so subagents can spawn explore/librarian
const BLOCKED_TOOLS = ["task", "delegate_task"]
export const SISYPHUS_JUNIOR_DEFAULTS = {
model: "anthropic/claude-sonnet-4-5",
temperature: 0.1,
} as const
export type SisyphusJuniorPromptSource = "default" | "gpt"
/**
* Determines which Sisyphus-Junior prompt to use based on model.
*/
export function getSisyphusJuniorPromptSource(model?: string): SisyphusJuniorPromptSource {
if (model && isGptModel(model)) {
return "gpt"
}
return "default"
}
/**
* Builds the appropriate Sisyphus-Junior prompt based on model.
*/
export function buildSisyphusJuniorPrompt(
model: string | undefined,
useTaskSystem: boolean,
promptAppend?: string
): string {
const source = getSisyphusJuniorPromptSource(model)
switch (source) {
case "gpt":
return buildGptSisyphusJuniorPrompt(useTaskSystem, promptAppend)
case "default":
default:
return buildDefaultSisyphusJuniorPrompt(useTaskSystem, promptAppend)
}
}
export function createSisyphusJuniorAgentWithOverrides(
override: AgentOverrideConfig | undefined,
systemDefaultModel?: string,
useTaskSystem = false
): AgentConfig {
if (override?.disable) {
override = undefined
}
const model = override?.model ?? systemDefaultModel ?? SISYPHUS_JUNIOR_DEFAULTS.model
const temperature = override?.temperature ?? SISYPHUS_JUNIOR_DEFAULTS.temperature
const promptAppend = override?.prompt_append
const prompt = buildSisyphusJuniorPrompt(model, useTaskSystem, promptAppend)
const baseRestrictions = createAgentToolRestrictions(BLOCKED_TOOLS)
const userPermission = (override?.permission ?? {}) as Record<string, PermissionValue>
const basePermission = baseRestrictions.permission
const merged: Record<string, PermissionValue> = { ...userPermission }
for (const tool of BLOCKED_TOOLS) {
merged[tool] = "deny"
}
merged.call_omo_agent = "allow"
const toolsConfig = { permission: { ...merged, ...basePermission } }
const base: AgentConfig = {
description: override?.description ??
"Focused task executor. Same discipline, no delegation. (Sisyphus-Junior - OhMyOpenCode)",
mode: MODE,
model,
temperature,
maxTokens: 64000,
prompt,
color: override?.color ?? "#20B2AA",
...toolsConfig,
}
if (override?.top_p !== undefined) {
base.top_p = override.top_p
}
if (isGptModel(model)) {
return { ...base, reasoningEffort: "medium" } as AgentConfig
}
return {
...base,
thinking: { type: "enabled", budgetTokens: 32000 },
} as AgentConfig
}
createSisyphusJuniorAgentWithOverrides.mode = MODE