Compare commits

...

4 Commits

Author SHA1 Message Date
cexll
f7aeaa5c7e fix(codeagent-wrapper): add sleep in fake script to prevent CI race condition
Add 50ms sleep in createFakeCodexScript to ensure parser goroutine has
time to read stdout before the process exits. Fixes TestRun_ExplicitStdinSuccess
flaky failure on Linux CI where fast shell execution closes pipe prematurely.

Generated with SWE-Agent.ai

Co-Authored-By: SWE-Agent.ai <noreply@swe-agent.ai>
2026-01-13 22:56:05 +08:00
cexll
c8f75faf84 fix gemini env load 2026-01-13 22:40:49 +08:00
cexll
b8b06257ff feat(codeagent-wrapper): add reasoning effort config for codex backend
- Add --reasoning-effort CLI flag for codex model thinking intensity
- Support reasoning config in ~/.codeagent/models.json per agent
- CLI flag takes precedence over config file
- Only effective for codex backend

Closes #117

Generated with SWE-Agent.ai

Co-Authored-By: SWE-Agent.ai <noreply@swe-agent.ai>
2026-01-13 22:38:38 +08:00
cexll
369a3319f9 fix omo 2026-01-13 19:28:37 +08:00
8 changed files with 285 additions and 55 deletions

View File

@@ -13,6 +13,7 @@ type AgentModelConfig struct {
PromptFile string `json:"prompt_file,omitempty"` PromptFile string `json:"prompt_file,omitempty"`
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
Yolo bool `json:"yolo,omitempty"` Yolo bool `json:"yolo,omitempty"`
Reasoning string `json:"reasoning,omitempty"`
} }
type ModelsConfig struct { type ModelsConfig struct {
@@ -25,15 +26,15 @@ var defaultModelsConfig = ModelsConfig{
DefaultBackend: "opencode", DefaultBackend: "opencode",
DefaultModel: "opencode/grok-code", DefaultModel: "opencode/grok-code",
Agents: map[string]AgentModelConfig{ Agents: map[string]AgentModelConfig{
"sisyphus": {Backend: "claude", Model: "claude-sonnet-4-20250514", PromptFile: "~/.claude/skills/omo/references/sisyphus.md", Description: "Primary orchestrator"}, "sisyphus": {Backend: "claude", Model: "claude-sonnet-4-20250514", PromptFile: "~/.claude/skills/omo/references/sisyphus.md", Description: "Primary orchestrator"},
"oracle": {Backend: "claude", Model: "claude-sonnet-4-20250514", PromptFile: "~/.claude/skills/omo/references/oracle.md", Description: "Technical advisor"}, "oracle": {Backend: "claude", Model: "claude-sonnet-4-20250514", PromptFile: "~/.claude/skills/omo/references/oracle.md", Description: "Technical advisor"},
"librarian": {Backend: "claude", Model: "claude-sonnet-4-5-20250514", PromptFile: "~/.claude/skills/omo/references/librarian.md", Description: "Researcher"}, "librarian": {Backend: "claude", Model: "claude-sonnet-4-5-20250514", PromptFile: "~/.claude/skills/omo/references/librarian.md", Description: "Researcher"},
"explore": {Backend: "opencode", Model: "opencode/grok-code", PromptFile: "~/.claude/skills/omo/references/explore.md", Description: "Code search"}, "explore": {Backend: "opencode", Model: "opencode/grok-code", PromptFile: "~/.claude/skills/omo/references/explore.md", Description: "Code search"},
"develop": {Backend: "codex", Model: "", PromptFile: "~/.claude/skills/omo/references/develop.md", Description: "Code development"}, "develop": {Backend: "codex", Model: "", PromptFile: "~/.claude/skills/omo/references/develop.md", Description: "Code development"},
"frontend-ui-ux-engineer": {Backend: "gemini", Model: "gemini-3-pro-preview", PromptFile: "~/.claude/skills/omo/references/frontend-ui-ux-engineer.md", Description: "Frontend engineer"}, "frontend-ui-ux-engineer": {Backend: "gemini", Model: "", PromptFile: "~/.claude/skills/omo/references/frontend-ui-ux-engineer.md", Description: "Frontend engineer"},
"document-writer": {Backend: "gemini", Model: "gemini-3-flash-preview", PromptFile: "~/.claude/skills/omo/references/document-writer.md", Description: "Documentation"}, "document-writer": {Backend: "gemini", Model: "", PromptFile: "~/.claude/skills/omo/references/document-writer.md", Description: "Documentation"},
}, },
} }
func loadModelsConfig() *ModelsConfig { func loadModelsConfig() *ModelsConfig {
home, err := os.UserHomeDir() home, err := os.UserHomeDir()
@@ -70,10 +71,10 @@ func loadModelsConfig() *ModelsConfig {
return &cfg return &cfg
} }
func resolveAgentConfig(agentName string) (backend, model, promptFile string, yolo bool) { func resolveAgentConfig(agentName string) (backend, model, promptFile, reasoning string, yolo bool) {
cfg := loadModelsConfig() cfg := loadModelsConfig()
if agent, ok := cfg.Agents[agentName]; ok { if agent, ok := cfg.Agents[agentName]; ok {
return agent.Backend, agent.Model, agent.PromptFile, agent.Yolo return agent.Backend, agent.Model, agent.PromptFile, agent.Reasoning, agent.Yolo
} }
return cfg.DefaultBackend, cfg.DefaultModel, "", false return cfg.DefaultBackend, cfg.DefaultModel, "", "", false
} }

View File

@@ -19,17 +19,17 @@ func TestResolveAgentConfig_Defaults(t *testing.T) {
wantModel string wantModel string
wantPromptFile string wantPromptFile string
}{ }{
{"sisyphus", "claude", "claude-sonnet-4-20250514", "~/.claude/skills/omo/references/sisyphus.md"}, {"sisyphus", "claude", "claude-sonnet-4-20250514", "~/.claude/skills/omo/references/sisyphus.md"},
{"oracle", "claude", "claude-sonnet-4-20250514", "~/.claude/skills/omo/references/oracle.md"}, {"oracle", "claude", "claude-sonnet-4-20250514", "~/.claude/skills/omo/references/oracle.md"},
{"librarian", "claude", "claude-sonnet-4-5-20250514", "~/.claude/skills/omo/references/librarian.md"}, {"librarian", "claude", "claude-sonnet-4-5-20250514", "~/.claude/skills/omo/references/librarian.md"},
{"explore", "opencode", "opencode/grok-code", "~/.claude/skills/omo/references/explore.md"}, {"explore", "opencode", "opencode/grok-code", "~/.claude/skills/omo/references/explore.md"},
{"frontend-ui-ux-engineer", "gemini", "gemini-3-pro-preview", "~/.claude/skills/omo/references/frontend-ui-ux-engineer.md"}, {"frontend-ui-ux-engineer", "gemini", "", "~/.claude/skills/omo/references/frontend-ui-ux-engineer.md"},
{"document-writer", "gemini", "gemini-3-flash-preview", "~/.claude/skills/omo/references/document-writer.md"}, {"document-writer", "gemini", "", "~/.claude/skills/omo/references/document-writer.md"},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.agent, func(t *testing.T) { t.Run(tt.agent, func(t *testing.T) {
backend, model, promptFile, _ := resolveAgentConfig(tt.agent) backend, model, promptFile, _, _ := resolveAgentConfig(tt.agent)
if backend != tt.wantBackend { if backend != tt.wantBackend {
t.Errorf("backend = %q, want %q", backend, tt.wantBackend) t.Errorf("backend = %q, want %q", backend, tt.wantBackend)
} }
@@ -48,7 +48,7 @@ func TestResolveAgentConfig_UnknownAgent(t *testing.T) {
t.Setenv("HOME", home) t.Setenv("HOME", home)
t.Setenv("USERPROFILE", home) t.Setenv("USERPROFILE", home)
backend, model, promptFile, _ := resolveAgentConfig("unknown-agent") backend, model, promptFile, _, _ := resolveAgentConfig("unknown-agent")
if backend != "opencode" { if backend != "opencode" {
t.Errorf("unknown agent backend = %q, want %q", backend, "opencode") t.Errorf("unknown agent backend = %q, want %q", backend, "opencode")
} }

View File

@@ -106,6 +106,51 @@ func loadMinimalEnvSettings() map[string]string {
return settings.Env return settings.Env
} }
// loadGeminiEnv loads environment variables from ~/.gemini/.env
// Supports GEMINI_API_KEY, GEMINI_MODEL, GOOGLE_GEMINI_BASE_URL
// Also sets GEMINI_API_KEY_AUTH_MECHANISM=bearer for third-party API compatibility
func loadGeminiEnv() map[string]string {
home, err := os.UserHomeDir()
if err != nil || home == "" {
return nil
}
envPath := filepath.Join(home, ".gemini", ".env")
data, err := os.ReadFile(envPath)
if err != nil {
return nil
}
env := make(map[string]string)
for _, line := range strings.Split(string(data), "\n") {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
idx := strings.IndexByte(line, '=')
if idx <= 0 {
continue
}
key := strings.TrimSpace(line[:idx])
value := strings.TrimSpace(line[idx+1:])
if key != "" && value != "" {
env[key] = value
}
}
// Set bearer auth mechanism for third-party API compatibility
if _, ok := env["GEMINI_API_KEY"]; ok {
if _, hasAuth := env["GEMINI_API_KEY_AUTH_MECHANISM"]; !hasAuth {
env["GEMINI_API_KEY_AUTH_MECHANISM"] = "bearer"
}
}
if len(env) == 0 {
return nil
}
return env
}
func buildClaudeArgs(cfg *Config, targetArg string) []string { func buildClaudeArgs(cfg *Config, targetArg string) []string {
if cfg == nil { if cfg == nil {
return nil return nil

View File

@@ -16,6 +16,7 @@ type Config struct {
SessionID string SessionID string
WorkDir string WorkDir string
Model string Model string
ReasoningEffort string
ExplicitStdin bool ExplicitStdin bool
Timeout int Timeout int
Backend string Backend string
@@ -35,18 +36,19 @@ type ParallelConfig struct {
// TaskSpec describes an individual task entry in the parallel config // TaskSpec describes an individual task entry in the parallel config
type TaskSpec struct { type TaskSpec struct {
ID string `json:"id"` ID string `json:"id"`
Task string `json:"task"` Task string `json:"task"`
WorkDir string `json:"workdir,omitempty"` WorkDir string `json:"workdir,omitempty"`
Dependencies []string `json:"dependencies,omitempty"` Dependencies []string `json:"dependencies,omitempty"`
SessionID string `json:"session_id,omitempty"` SessionID string `json:"session_id,omitempty"`
Backend string `json:"backend,omitempty"` Backend string `json:"backend,omitempty"`
Model string `json:"model,omitempty"` Model string `json:"model,omitempty"`
Agent string `json:"agent,omitempty"` ReasoningEffort string `json:"reasoning_effort,omitempty"`
PromptFile string `json:"prompt_file,omitempty"` Agent string `json:"agent,omitempty"`
Mode string `json:"-"` PromptFile string `json:"prompt_file,omitempty"`
UseStdin bool `json:"-"` Mode string `json:"-"`
Context context.Context `json:"-"` UseStdin bool `json:"-"`
Context context.Context `json:"-"`
} }
// TaskResult captures the execution outcome of a task // TaskResult captures the execution outcome of a task
@@ -190,6 +192,8 @@ func parseParallelConfig(data []byte) (*ParallelConfig, error) {
task.Backend = value task.Backend = value
case "model": case "model":
task.Model = value task.Model = value
case "reasoning_effort":
task.ReasoningEffort = value
case "agent": case "agent":
agentSpecified = true agentSpecified = true
task.Agent = value task.Agent = value
@@ -214,13 +218,16 @@ func parseParallelConfig(data []byte) (*ParallelConfig, error) {
if err := validateAgentName(task.Agent); err != nil { if err := validateAgentName(task.Agent); err != nil {
return nil, fmt.Errorf("task block #%d invalid agent name: %w", taskIndex, err) return nil, fmt.Errorf("task block #%d invalid agent name: %w", taskIndex, err)
} }
backend, model, promptFile, _ := resolveAgentConfig(task.Agent) backend, model, promptFile, reasoning, _ := resolveAgentConfig(task.Agent)
if task.Backend == "" { if task.Backend == "" {
task.Backend = backend task.Backend = backend
} }
if task.Model == "" { if task.Model == "" {
task.Model = model task.Model = model
} }
if task.ReasoningEffort == "" {
task.ReasoningEffort = reasoning
}
task.PromptFile = promptFile task.PromptFile = promptFile
} }
@@ -257,6 +264,7 @@ func parseArgs() (*Config, error) {
backendName := defaultBackendName backendName := defaultBackendName
model := "" model := ""
reasoningEffort := ""
agentName := "" agentName := ""
promptFile := "" promptFile := ""
promptFileExplicit := false promptFileExplicit := false
@@ -277,12 +285,15 @@ func parseArgs() (*Config, error) {
if err := validateAgentName(value); err != nil { if err := validateAgentName(value); err != nil {
return nil, fmt.Errorf("--agent flag invalid value: %w", err) return nil, fmt.Errorf("--agent flag invalid value: %w", err)
} }
resolvedBackend, resolvedModel, resolvedPromptFile, resolvedYolo := resolveAgentConfig(value) resolvedBackend, resolvedModel, resolvedPromptFile, resolvedReasoning, resolvedYolo := resolveAgentConfig(value)
backendName = resolvedBackend backendName = resolvedBackend
model = resolvedModel model = resolvedModel
if !promptFileExplicit { if !promptFileExplicit {
promptFile = resolvedPromptFile promptFile = resolvedPromptFile
} }
if reasoningEffort == "" {
reasoningEffort = resolvedReasoning
}
yolo = resolvedYolo yolo = resolvedYolo
agentName = value agentName = value
i++ i++
@@ -295,12 +306,15 @@ func parseArgs() (*Config, error) {
if err := validateAgentName(value); err != nil { if err := validateAgentName(value); err != nil {
return nil, fmt.Errorf("--agent flag invalid value: %w", err) return nil, fmt.Errorf("--agent flag invalid value: %w", err)
} }
resolvedBackend, resolvedModel, resolvedPromptFile, resolvedYolo := resolveAgentConfig(value) resolvedBackend, resolvedModel, resolvedPromptFile, resolvedReasoning, resolvedYolo := resolveAgentConfig(value)
backendName = resolvedBackend backendName = resolvedBackend
model = resolvedModel model = resolvedModel
if !promptFileExplicit { if !promptFileExplicit {
promptFile = resolvedPromptFile promptFile = resolvedPromptFile
} }
if reasoningEffort == "" {
reasoningEffort = resolvedReasoning
}
yolo = resolvedYolo yolo = resolvedYolo
agentName = value agentName = value
continue continue
@@ -355,6 +369,24 @@ func parseArgs() (*Config, error) {
} }
model = value model = value
continue continue
case arg == "--reasoning-effort":
if i+1 >= len(args) {
return nil, fmt.Errorf("--reasoning-effort flag requires a value")
}
value := strings.TrimSpace(args[i+1])
if value == "" {
return nil, fmt.Errorf("--reasoning-effort flag requires a value")
}
reasoningEffort = value
i++
continue
case strings.HasPrefix(arg, "--reasoning-effort="):
value := strings.TrimSpace(strings.TrimPrefix(arg, "--reasoning-effort="))
if value == "" {
return nil, fmt.Errorf("--reasoning-effort flag requires a value")
}
reasoningEffort = value
continue
case strings.HasPrefix(arg, "--skip-permissions="): case strings.HasPrefix(arg, "--skip-permissions="):
skipPermissions = parseBoolFlag(strings.TrimPrefix(arg, "--skip-permissions="), skipPermissions) skipPermissions = parseBoolFlag(strings.TrimPrefix(arg, "--skip-permissions="), skipPermissions)
continue continue
@@ -370,7 +402,7 @@ func parseArgs() (*Config, error) {
} }
args = filtered args = filtered
cfg := &Config{WorkDir: defaultWorkdir, Backend: backendName, Agent: agentName, PromptFile: promptFile, PromptFileExplicit: promptFileExplicit, SkipPermissions: skipPermissions, Yolo: yolo, Model: strings.TrimSpace(model)} cfg := &Config{WorkDir: defaultWorkdir, Backend: backendName, Agent: agentName, PromptFile: promptFile, PromptFileExplicit: promptFileExplicit, SkipPermissions: skipPermissions, Yolo: yolo, Model: strings.TrimSpace(model), ReasoningEffort: strings.TrimSpace(reasoningEffort)}
cfg.MaxParallelWorkers = resolveMaxParallelWorkers() cfg.MaxParallelWorkers = resolveMaxParallelWorkers()
if args[0] == "resume" { if args[0] == "resume" {

View File

@@ -764,6 +764,10 @@ func buildCodexArgs(cfg *Config, targetArg string) []string {
args = append(args, "--model", model) args = append(args, "--model", model)
} }
if reasoningEffort := strings.TrimSpace(cfg.ReasoningEffort); reasoningEffort != "" {
args = append(args, "--reasoning-effort", reasoningEffort)
}
args = append(args, "--skip-git-repo-check") args = append(args, "--skip-git-repo-check")
if isResume { if isResume {
@@ -804,12 +808,13 @@ func runCodexTaskWithContext(parentCtx context.Context, taskSpec TaskSpec, backe
logger := injectedLogger logger := injectedLogger
cfg := &Config{ cfg := &Config{
Mode: taskSpec.Mode, Mode: taskSpec.Mode,
Task: taskSpec.Task, Task: taskSpec.Task,
SessionID: taskSpec.SessionID, SessionID: taskSpec.SessionID,
WorkDir: taskSpec.WorkDir, WorkDir: taskSpec.WorkDir,
Model: taskSpec.Model, Model: taskSpec.Model,
Backend: defaultBackendName, ReasoningEffort: taskSpec.ReasoningEffort,
Backend: defaultBackendName,
} }
commandName := codexCommand commandName := codexCommand
@@ -846,6 +851,12 @@ func runCodexTaskWithContext(parentCtx context.Context, taskSpec TaskSpec, backe
} }
} }
// Load gemini env from ~/.gemini/.env if exists
var geminiEnv map[string]string
if cfg.Backend == "gemini" {
geminiEnv = loadGeminiEnv()
}
useStdin := taskSpec.UseStdin useStdin := taskSpec.UseStdin
targetArg := taskSpec.Task targetArg := taskSpec.Task
if useStdin { if useStdin {
@@ -948,6 +959,9 @@ func runCodexTaskWithContext(parentCtx context.Context, taskSpec TaskSpec, backe
if cfg.Backend == "claude" && len(claudeEnv) > 0 { if cfg.Backend == "claude" && len(claudeEnv) > 0 {
cmd.SetEnv(claudeEnv) cmd.SetEnv(claudeEnv)
} }
if cfg.Backend == "gemini" && len(geminiEnv) > 0 {
cmd.SetEnv(geminiEnv)
}
// For backends that don't support -C flag (claude, gemini), set working directory via cmd.Dir // For backends that don't support -C flag (claude, gemini), set working directory via cmd.Dir
// Codex passes workdir via -C flag, so we skip setting Dir for it to avoid conflicts // Codex passes workdir via -C flag, so we skip setting Dir for it to avoid conflicts

View File

@@ -434,12 +434,13 @@ func run() (exitCode int) {
logInfo(fmt.Sprintf("%s running...", cfg.Backend)) logInfo(fmt.Sprintf("%s running...", cfg.Backend))
taskSpec := TaskSpec{ taskSpec := TaskSpec{
Task: taskText, Task: taskText,
WorkDir: cfg.WorkDir, WorkDir: cfg.WorkDir,
Mode: cfg.Mode, Mode: cfg.Mode,
SessionID: cfg.SessionID, SessionID: cfg.SessionID,
Model: cfg.Model, Model: cfg.Model,
UseStdin: useStdin, ReasoningEffort: cfg.ReasoningEffort,
UseStdin: useStdin,
} }
result := runTaskFn(taskSpec, false, cfg.Timeout) result := runTaskFn(taskSpec, false, cfg.Timeout)

View File

@@ -637,9 +637,13 @@ func (f *fakeCmd) StdinContents() string {
func createFakeCodexScript(t *testing.T, threadID, message string) string { func createFakeCodexScript(t *testing.T, threadID, message string) string {
t.Helper() t.Helper()
scriptPath := filepath.Join(t.TempDir(), "codex.sh") scriptPath := filepath.Join(t.TempDir(), "codex.sh")
// Add small sleep to ensure parser goroutine has time to read stdout before
// the process exits and closes the pipe. This prevents race conditions in CI
// where fast shell script execution can close stdout before parsing completes.
script := fmt.Sprintf(`#!/bin/sh script := fmt.Sprintf(`#!/bin/sh
printf '%%s\n' '{"type":"thread.started","thread_id":"%s"}' printf '%%s\n' '{"type":"thread.started","thread_id":"%s"}'
printf '%%s\n' '{"type":"item.completed","item":{"type":"agent_message","text":"%s"}}' printf '%%s\n' '{"type":"item.completed","item":{"type":"agent_message","text":"%s"}}'
sleep 0.05
`, threadID, message) `, threadID, message)
if err := os.WriteFile(scriptPath, []byte(script), 0o755); err != nil { if err := os.WriteFile(scriptPath, []byte(script), 0o755); err != nil {
t.Fatalf("failed to create fake codex script: %v", err) t.Fatalf("failed to create fake codex script: %v", err)
@@ -1290,6 +1294,65 @@ func TestBackendParseArgs_ModelFlag(t *testing.T) {
} }
} }
func TestBackendParseArgs_ReasoningEffortFlag(t *testing.T) {
tests := []struct {
name string
args []string
want string
wantErr bool
}{
{
name: "reasoning-effort flag",
args: []string{"codeagent-wrapper", "--reasoning-effort", "low", "task"},
want: "low",
},
{
name: "reasoning-effort equals syntax",
args: []string{"codeagent-wrapper", "--reasoning-effort=medium", "task"},
want: "medium",
},
{
name: "reasoning-effort trimmed",
args: []string{"codeagent-wrapper", "--reasoning-effort", " high ", "task"},
want: "high",
},
{
name: "reasoning-effort with resume mode",
args: []string{"codeagent-wrapper", "--reasoning-effort", "low", "resume", "sid", "task"},
want: "low",
},
{
name: "missing reasoning-effort value",
args: []string{"codeagent-wrapper", "--reasoning-effort"},
wantErr: true,
},
{
name: "reasoning-effort equals missing value",
args: []string{"codeagent-wrapper", "--reasoning-effort=", "task"},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
os.Args = tt.args
cfg, err := parseArgs()
if tt.wantErr {
if err == nil {
t.Fatalf("expected error, got nil")
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if cfg.ReasoningEffort != tt.want {
t.Fatalf("ReasoningEffort = %q, want %q", cfg.ReasoningEffort, tt.want)
}
})
}
}
func TestBackendParseArgs_PromptFileFlag(t *testing.T) { func TestBackendParseArgs_PromptFileFlag(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
@@ -1829,6 +1892,28 @@ func TestRun_PromptFilePrefixesTask(t *testing.T) {
}) })
} }
func TestRun_PassesReasoningEffortToTaskSpec(t *testing.T) {
defer resetTestHooks()
cleanupLogsFn = func() (CleanupStats, error) { return CleanupStats{}, nil }
stdinReader = strings.NewReader("")
isTerminalFn = func() bool { return true }
var got TaskSpec
runTaskFn = func(task TaskSpec, silent bool, timeout int) TaskResult {
got = task
return TaskResult{ExitCode: 0, Message: "ok"}
}
os.Args = []string{"codeagent-wrapper", "--reasoning-effort", "high", "task"}
if code := run(); code != 0 {
t.Fatalf("run exit = %d, want 0", code)
}
if got.ReasoningEffort != "high" {
t.Fatalf("ReasoningEffort = %q, want %q", got.ReasoningEffort, "high")
}
}
func TestRunBuildCodexArgs_NewMode(t *testing.T) { func TestRunBuildCodexArgs_NewMode(t *testing.T) {
const key = "CODEX_BYPASS_SANDBOX" const key = "CODEX_BYPASS_SANDBOX"
t.Setenv(key, "false") t.Setenv(key, "false")
@@ -1852,6 +1937,64 @@ func TestRunBuildCodexArgs_NewMode(t *testing.T) {
} }
} }
func TestRunBuildCodexArgs_NewMode_WithReasoningEffort(t *testing.T) {
const key = "CODEX_BYPASS_SANDBOX"
t.Setenv(key, "false")
cfg := &Config{Mode: "new", WorkDir: "/test/dir", ReasoningEffort: "high"}
args := buildCodexArgs(cfg, "my task")
expected := []string{
"e",
"--reasoning-effort", "high",
"--skip-git-repo-check",
"-C", "/test/dir",
"--json",
"my task",
}
if len(args) != len(expected) {
t.Fatalf("len mismatch")
}
for i := range args {
if args[i] != expected[i] {
t.Fatalf("args[%d]=%s, want %s", i, args[i], expected[i])
}
}
}
func TestRunCodexTaskWithContext_CodexReasoningEffort(t *testing.T) {
defer resetTestHooks()
t.Setenv("CODEX_BYPASS_SANDBOX", "false")
var gotArgs []string
origRunner := newCommandRunner
newCommandRunner = func(ctx context.Context, name string, args ...string) commandRunner {
gotArgs = append([]string(nil), args...)
return newFakeCmd(fakeCmdConfig{
PID: 123,
StdoutPlan: []fakeStdoutEvent{
{Data: "{\"type\":\"result\",\"session_id\":\"sid\",\"result\":\"ok\"}\n"},
},
})
}
t.Cleanup(func() { newCommandRunner = origRunner })
res := runCodexTaskWithContext(context.Background(), TaskSpec{Task: "hi", Mode: "new", WorkDir: defaultWorkdir, ReasoningEffort: "high"}, nil, nil, false, true, 5)
if res.ExitCode != 0 || res.Message != "ok" {
t.Fatalf("unexpected result: %+v", res)
}
found := false
for i := 0; i+1 < len(gotArgs); i++ {
if gotArgs[i] == "--reasoning-effort" && gotArgs[i+1] == "high" {
found = true
break
}
}
if !found {
t.Fatalf("expected --reasoning-effort high in args, got %v", gotArgs)
}
}
func TestRunBuildCodexArgs_ResumeMode(t *testing.T) { func TestRunBuildCodexArgs_ResumeMode(t *testing.T) {
const key = "CODEX_BYPASS_SANDBOX" const key = "CODEX_BYPASS_SANDBOX"
t.Setenv(key, "false") t.Setenv(key, "false")

View File

@@ -119,12 +119,6 @@
"target": "skills/omo/SKILL.md", "target": "skills/omo/SKILL.md",
"description": "Install omo skill" "description": "Install omo skill"
}, },
{
"type": "copy_file",
"source": "skills/omo/references/sisyphus.md",
"target": "skills/omo/references/sisyphus.md",
"description": "Install sisyphus agent prompt"
},
{ {
"type": "copy_file", "type": "copy_file",
"source": "skills/omo/references/oracle.md", "source": "skills/omo/references/oracle.md",