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 TestResidentChildSpecUsesSelectedModelContextForBudget(t *testing.T) {
71 runtime := newSubagentRuntime(subagentRuntimeConfig{
72 Args: Args{}, Root: t.TempDir(), RepoRoot: t.TempDir(),
73 Provider: "openai", Model: "gpt-5.6-sol", ContextWindow: 42_000,
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.BudgetLimit != 500_000 || spec.BudgetSource != "model_context" {
82 t.Fatalf("child model budget = %#v", spec)
83 }
84}
85
86func TestResidentChildSpecDoesNotCarryTransportAcrossProviderOverride(t *testing.T) {
87 runtime := newSubagentRuntime(subagentRuntimeConfig{
88 Args: Args{}, Root: t.TempDir(), RepoRoot: t.TempDir(),
89 Provider: "openai", Model: "gpt-5.6-sol", BaseURL: "https://parent.example/v1", InsecureTLS: true, ContextWindow: 500_000,
90 })
91 t.Cleanup(func() { _ = runtime.Close(context.Background()) })
92 spec, err := runtime.buildResidentChildSpec(context.Background(), tools.ResidentSpawnRequest{
93 Task: "review", Provider: "anthropic", Model: "claude-sonnet-4-5",
94 }, core.Registry{"read": nil})
95 if err != nil {
96 t.Fatal(err)
97 }
98 if spec.BaseURL != "" || spec.InsecureTLS {
99 t.Fatalf("cross-provider spec inherited transport: %#v", spec)
100 }
101}