From bf87bf473f167c15970f472558465515035cd663 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Tue, 3 Feb 2026 14:12:22 +0900 Subject: [PATCH] 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. --- src/agents/sisyphus-junior.ts | 134 ------------------ src/agents/sisyphus-junior/default.ts | 74 ++++++++++ src/agents/sisyphus-junior/gpt.ts | 129 +++++++++++++++++ .../index.test.ts} | 132 ++++++++++++++++- src/agents/sisyphus-junior/index.ts | 121 ++++++++++++++++ 5 files changed, 454 insertions(+), 136 deletions(-) delete mode 100644 src/agents/sisyphus-junior.ts create mode 100644 src/agents/sisyphus-junior/default.ts create mode 100644 src/agents/sisyphus-junior/gpt.ts rename src/agents/{sisyphus-junior.test.ts => sisyphus-junior/index.test.ts} (66%) create mode 100644 src/agents/sisyphus-junior/index.ts diff --git a/src/agents/sisyphus-junior.ts b/src/agents/sisyphus-junior.ts deleted file mode 100644 index 4bb419df..00000000 --- a/src/agents/sisyphus-junior.ts +++ /dev/null @@ -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 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. -` - } - - return ` -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. -` -} - -function buildSisyphusJuniorPrompt(useTaskSystem: boolean, promptAppend?: string): string { - const todoDiscipline = buildTodoDisciplineSection(useTaskSystem) - const verificationText = useTaskSystem ? "All tasks marked completed" : "All todos marked completed" - - const prompt = ` -Sisyphus-Junior - Focused executor from OhMyOpenCode. -Execute tasks directly. NEVER delegate or spawn other agents. - - - -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. - - -${todoDiscipline} - - -Task NOT complete without: -- lsp_diagnostics clean on changed files -- Build passes (if applicable) -- ${verificationText} - - -` - - 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 - const basePermission = baseRestrictions.permission - const merged: Record = { ...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 diff --git a/src/agents/sisyphus-junior/default.ts b/src/agents/sisyphus-junior/default.ts new file mode 100644 index 00000000..a234a021 --- /dev/null +++ b/src/agents/sisyphus-junior/default.ts @@ -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 = ` +Sisyphus-Junior - Focused executor from OhMyOpenCode. +Execute tasks directly. NEVER delegate or spawn other agents. + + + +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. + + +${todoDiscipline} + + +Task NOT complete without: +- lsp_diagnostics clean on changed files +- Build passes (if applicable) +- ${verificationText} + + +` + + if (!promptAppend) return prompt + return prompt + "\n\n" + promptAppend +} + +function buildTodoDisciplineSection(useTaskSystem: boolean): string { + if (useTaskSystem) { + return ` +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. +` + } + + return ` +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. +` +} diff --git a/src/agents/sisyphus-junior/gpt.ts b/src/agents/sisyphus-junior/gpt.ts new file mode 100644 index 00000000..0a26b222 --- /dev/null +++ b/src/agents/sisyphus-junior/gpt.ts @@ -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 = ` +You are Sisyphus-Junior - Focused task executor from OhMyOpenCode. +Role: Execute tasks directly. You work ALONE. + + + +- 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. + + + +- 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. + + + +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. + + + +- 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. + + + +- 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. + + +${taskDiscipline} + + +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.** + + + +- Start immediately. No acknowledgments ("I'll...", "Let me..."). +- Match user's communication style. +- Dense > verbose. +- Use structured output (bullets, tables) over prose. +` + + if (!promptAppend) return prompt + return prompt + "\n\n" + promptAppend +} + +function buildGptTaskDisciplineSection(useTaskSystem: boolean): string { + if (useTaskSystem) { + return ` +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. +` + } + + return ` +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. +` +} diff --git a/src/agents/sisyphus-junior.test.ts b/src/agents/sisyphus-junior/index.test.ts similarity index 66% rename from src/agents/sisyphus-junior.test.ts rename to src/agents/sisyphus-junior/index.test.ts index 49f0ea08..7b9128a5 100644 --- a/src/agents/sisyphus-junior.test.ts +++ b/src/agents/sisyphus-junior/index.test.ts @@ -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("") + }) + + 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("") + expect(result.prompt).toContain("") + expect(result.prompt).toContain("") }) 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("") + expect(prompt).toContain("") + expect(prompt).toContain("") + expect(prompt).toContain("") + }) + + 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("") + expect(prompt).toContain("") + 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("") + 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("") + expect(prompt).toContain("todowrite") + }) +}) diff --git a/src/agents/sisyphus-junior/index.ts b/src/agents/sisyphus-junior/index.ts new file mode 100644 index 00000000..26c3f753 --- /dev/null +++ b/src/agents/sisyphus-junior/index.ts @@ -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 + const basePermission = baseRestrictions.permission + const merged: Record = { ...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