1package agent
2
3import (
4 "context"
5 "testing"
6
7 "github.com/bnema/zut/packages/agent/tools"
8 "github.com/bnema/zut/packages/core"
9)
10
11func TestExpandWebCapabilityTools(t *testing.T) {
12 catalogue := append([]string{"read"}, tools.WebCapabilityNames...)
13 got := expandWebCapabilityTools([]string{"read", "web_search"}, catalogue, func(string) bool { return true })
14 for _, name := range append([]string{"read"}, tools.WebCapabilityNames...) {
15 found := false
16 for _, candidate := range got {
17 if candidate == name {
18 found = true
19 break
20 }
21 }
22 if !found {
23 t.Fatalf("expanded child tools = %v, missing %s", got, name)
24 }
25 }
26}
27
28func TestExpandWebCapabilityToolsPreservesPolicyAndNonWebSelections(t *testing.T) {
29 catalogue := append([]string{"read"}, tools.WebCapabilityNames...)
30 partial := expandWebCapabilityTools([]string{"web_open"}, catalogue, func(string) bool { return true })
31 for _, name := range tools.WebCapabilityNames {
32 found := false
33 for _, candidate := range partial {
34 if candidate == name {
35 found = true
36 break
37 }
38 }
39 if !found {
40 t.Fatalf("partial web selection did not expand: %v", partial)
41 }
42 }
43 denied := expandWebCapabilityTools([]string{"read", "web_search"}, catalogue, func(name string) bool { return name == "read" })
44 if len(denied) != 1 || denied[0] != "read" {
45 t.Fatalf("denied web capability = %v, want [read]", denied)
46 }
47 nonWeb := expandWebCapabilityTools([]string{"read"}, catalogue, func(string) bool { return true })
48 if len(nonWeb) != 1 || nonWeb[0] != "read" {
49 t.Fatalf("non-web selection changed to %v", nonWeb)
50 }
51}
52
53func TestResidentChildSpecSnapshotsCurrentProviderTransportSettings(t *testing.T) {
54 runtime := newSubagentRuntime(subagentRuntimeConfig{
55 Args: Args{}, Root: t.TempDir(), RepoRoot: t.TempDir(),
56 Provider: "openai", Model: "gpt-5.6-sol", BaseURL: "https://old.example/v1", InsecureTLS: false, ContextWindow: 500_000,
57 })
58 t.Cleanup(func() { _ = runtime.Close(context.Background()) })
59 runtime.SetProviderSettings("https://current.example/v1", true)
60 runtime.SetModel("gpt-current")
61 spec, err := runtime.buildResidentChildSpec(context.Background(), tools.ResidentSpawnRequest{Task: "review"}, core.Registry{"read": nil})
62 if err != nil {
63 t.Fatal(err)
64 }
65 if spec.BaseURL != "https://current.example/v1" || !spec.InsecureTLS || spec.Model != "gpt-current" {
66 t.Fatalf("resident spec transport/model = %q insecure=%t model=%q", spec.BaseURL, spec.InsecureTLS, spec.Model)
67 }
68}
69
70func TestResidentChildSpecTracksFastMode(t *testing.T) {
71 runtime := newSubagentRuntime(subagentRuntimeConfig{
72 Args: Args{}, Root: t.TempDir(), RepoRoot: t.TempDir(),
73 Provider: "openai", Model: "gpt-5.6-sol", FastMode: false,
74 })
75 t.Cleanup(func() { _ = runtime.Close(context.Background()) })
76
77 spec, err := runtime.buildResidentChildSpec(context.Background(), tools.ResidentSpawnRequest{Task: "review"}, core.Registry{"read": nil})
78 if err != nil {
79 t.Fatal(err)
80 }
81 if spec.FastMode {
82 t.Fatal("initial resident fast mode = true, want false")
83 }
84 runtime.SetFastMode(true)
85 spec, err = runtime.buildResidentChildSpec(context.Background(), tools.ResidentSpawnRequest{Task: "review"}, core.Registry{"read": nil})
86 if err != nil {
87 t.Fatal(err)
88 }
89 if !spec.FastMode {
90 t.Fatal("resident fast mode = false after update, want true")
91 }
92}
93
94func TestResidentChildSpecUsesSelectedModelContextForBudget(t *testing.T) {
95 runtime := newSubagentRuntime(subagentRuntimeConfig{
96 Args: Args{}, Root: t.TempDir(), RepoRoot: t.TempDir(),
97 Provider: "openai", Model: "gpt-5.6-sol", ContextWindow: 42_000,
98 })
99 t.Cleanup(func() { _ = runtime.Close(context.Background()) })
100
101 spec, err := runtime.buildResidentChildSpec(context.Background(), tools.ResidentSpawnRequest{Task: "review"}, core.Registry{"read": nil})
102 if err != nil {
103 t.Fatal(err)
104 }
105 if spec.BudgetLimit != 500_000 || spec.BudgetSource != "model_context" {
106 t.Fatalf("child model budget = %#v", spec)
107 }
108}
109
110func TestResidentChildSpecDoesNotCarryTransportAcrossProviderOverride(t *testing.T) {
111 runtime := newSubagentRuntime(subagentRuntimeConfig{
112 Args: Args{}, Root: t.TempDir(), RepoRoot: t.TempDir(),
113 Provider: "openai", Model: "gpt-5.6-sol", BaseURL: "https://parent.example/v1", InsecureTLS: true, ContextWindow: 500_000,
114 })
115 t.Cleanup(func() { _ = runtime.Close(context.Background()) })
116 spec, err := runtime.buildResidentChildSpec(context.Background(), tools.ResidentSpawnRequest{
117 Task: "review", Provider: "anthropic", Model: "claude-sonnet-4-5",
118 }, core.Registry{"read": nil})
119 if err != nil {
120 t.Fatal(err)
121 }
122 if spec.BaseURL != "" || spec.InsecureTLS {
123 t.Fatalf("cross-provider spec inherited transport: %#v", spec)
124 }
125}