package executor import ( "strings" "testing" "github.com/tidwall/gjson" ) func TestParseCodexAgentConfig(t *testing.T) { tests := []struct { name string body []byte want codexAgentMode disableAuto bool }{ { name: "cliproxy nested", body: []byte(`{"_cliproxy":{"agent_mode":"planner-reviewer"}}`), want: codexAgentModePlannerReviewer, }, { name: "top-level alias underscore", body: []byte(`{"agent_mode":"planner_reviewer"}`), want: codexAgentModePlannerReviewer, }, { name: "unknown mode", body: []byte(`{"_cliproxy":{"agent_mode":"foo"}}`), want: codexAgentModeNone, }, { name: "explicit off", body: []byte(`{"_cliproxy":{"agent_mode":"off"}}`), want: codexAgentModeNone, disableAuto: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := parseCodexAgentConfig(tt.body) if got.Mode != tt.want { t.Fatalf("mode = %q, want %q", got.Mode, tt.want) } if got.DisableAuto != tt.disableAuto { t.Fatalf("DisableAuto = %v, want %v", got.DisableAuto, tt.disableAuto) } }) } } func TestResolveCodexAgentConfigForNonStream_DefaultsToPlannerReviewer(t *testing.T) { got := resolveCodexAgentConfigForNonStream([]byte(`{"input":[{"type":"message","role":"user","content":[{"type":"input_text","text":"hi"}]}]}`)) if got.Mode != codexAgentModePlannerReviewer { t.Fatalf("mode = %q, want %q", got.Mode, codexAgentModePlannerReviewer) } if got.Explicit { t.Fatalf("expected default mode to be implicit") } } func TestResolveCodexAgentConfigForNonStream_HonorsExplicitDisable(t *testing.T) { got := resolveCodexAgentConfigForNonStream([]byte(`{"_cliproxy":{"agent_mode":"disabled"}}`)) if got.Enabled() { t.Fatalf("expected agent mode disabled, got %q", got.Mode) } if !got.DisableAuto { t.Fatalf("expected DisableAuto=true") } } func TestCodexAgentCompatibilityIssue(t *testing.T) { if issue := codexAgentCompatibilityIssue([]byte(`{"tools":[{"name":"x"}]}`)); !strings.Contains(issue, "tool") { t.Fatalf("expected tool compatibility issue, got %q", issue) } if issue := codexAgentCompatibilityIssue([]byte(`{"text":{"format":{"type":"json_schema"}}}`)); !strings.Contains(issue, "structured") { t.Fatalf("expected structured compatibility issue, got %q", issue) } if issue := codexAgentCompatibilityIssue([]byte(`{"input":[]}`)); issue != "" { t.Fatalf("unexpected issue for simple text request: %q", issue) } } func TestCodexBuildAgentPassBody(t *testing.T) { base := []byte(`{ "model":"gpt-5", "stream":true, "reasoning":{"effort":"high"}, "tools":[{"type":"function","name":"x"}], "_cliproxy":{"agent_mode":"planner-reviewer"}, "input":[{"type":"message","role":"user","content":[{"type":"input_text","text":"hi"}]}] }`) out, err := codexBuildAgentPassBody(base, "planner", "original task", "", "") if err != nil { t.Fatalf("codexBuildAgentPassBody error: %v", err) } if got := gjson.GetBytes(out, "tools"); got.Exists() { t.Fatalf("expected tools to be stripped, got %s", got.Raw) } if got := gjson.GetBytes(out, "_cliproxy"); got.Exists() { t.Fatalf("expected _cliproxy to be stripped, got %s", got.Raw) } if !gjson.GetBytes(out, "stream").Bool() { t.Fatalf("expected stream=true in internal pass body") } if got := gjson.GetBytes(out, "reasoning.effort").String(); got != "high" { t.Fatalf("reasoning.effort = %q, want %q", got, "high") } prompt := gjson.GetBytes(out, "input.0.content.0.text").String() if !strings.Contains(prompt, "original task") { t.Fatalf("expected planner prompt to include original task, got %q", prompt) } instructions := gjson.GetBytes(out, "instructions").String() if !strings.Contains(strings.ToLower(instructions), "planning agent") { t.Fatalf("expected planner instructions, got %q", instructions) } } func TestCodexExtractCompletedMessageAndReasoning(t *testing.T) { payload := []byte(`{ "type":"response.completed", "response":{ "output":[ {"type":"reasoning","summary":[{"type":"summary_text","text":"check edge cases"}]}, {"type":"message","content":[{"type":"output_text","text":"final answer"}]} ] } }`) msg, reasoning := codexExtractCompletedMessageAndReasoning(payload) if msg != "final answer" { t.Fatalf("message = %q, want %q", msg, "final answer") } if reasoning != "check edge cases" { t.Fatalf("reasoning = %q, want %q", reasoning, "check edge cases") } } func TestCodexAgentPhaseInstructions_IncludeDeepEngineeringStandards(t *testing.T) { planner := codexAgentPhaseInstructions("planner") if !strings.Contains(planner, "internal planning agent") { t.Fatalf("planner instructions missing role text: %q", planner) } if !strings.Contains(planner, "Override Brevity") || !strings.Contains(planner, "Library & Framework Discipline") { t.Fatalf("planner instructions missing shared engineering standards: %q", planner) } if strings.Contains(planner, "Response format (normal mode)") { t.Fatalf("planner instructions should not include final response format block: %q", planner) } final := codexAgentPhaseInstructions("final") if !strings.Contains(final, "final responder") { t.Fatalf("final instructions missing role text: %q", final) } if !strings.Contains(final, "Response format (normal mode)") { t.Fatalf("final instructions missing response format block: %q", final) } }