1package agent
2
3import (
4 "context"
5 "encoding/json"
6 "net/http"
7 "net/http/httptest"
8 "strings"
9 "testing"
10
11 "github.com/bnema/zut/packages/agent/subagents"
12 "github.com/bnema/zut/packages/core"
13)
14
15func TestOrchestratedWaitDeliversReportOnlyThroughContinuation(t *testing.T) {
16 t.Setenv("ZUT_HOME", t.TempDir())
17 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
18 w.Header().Set("content-type", "text/event-stream")
19 writeOpenAIChunk(t, w, map[string]any{
20 "choices": []any{map[string]any{"index": 0, "delta": map[string]string{"content": "synthetic child report"}, "finish_reason": "stop"}},
21 })
22 }))
23 defer server.Close()
24 tracker := subagents.NewCompletionTracker()
25 args := Args{CWD: t.TempDir(), Provider: "openai", Model: "gpt-4o", APIKey: "synthetic", BaseURL: server.URL, NoContextFiles: true, NoSkill: true, NoLSP: true}
26 runtime := newOrchestratedRuntime(t.Context(), args, Resolved{CWD: args.CWD, Provider: args.Provider, Model: args.Model, BaseURL: args.BaseURL}, Config{}, tracker)
27 t.Cleanup(func() { _ = runtime.ResidentManager().Close(context.Background()) })
28 facade := runtime.InjectTools(core.Registry{})["subagent"]
29 if facade == nil {
30 t.Fatal("subagent tool unavailable")
31 }
32 result, err := facade.Execute(t.Context(), json.RawMessage(`{"action":"spawn","task":"return a report","wait":5}`), nil)
33 if err != nil || result.IsError {
34 t.Fatalf("spawn = (%#v, %v)", result, err)
35 }
36 encoded, err := json.Marshal(result.Content)
37 if err != nil {
38 t.Fatal(err)
39 }
40 if strings.Contains(string(encoded), "synthetic child report") || !strings.Contains(string(encoded), "host_update") {
41 t.Fatalf("tool report = %s", encoded)
42 }
43 calls := 0
44 got, err := runHeadlessContinuation(t.Context(), tracker, "initial", func(_ context.Context, prompt string) (string, error) {
45 calls++
46 if strings.Count(prompt, "[auto-subagents update]") != 1 || strings.Count(prompt, "synthetic child report") != 1 {
47 t.Errorf("continuation report = %q", prompt)
48 }
49 return "synthesis", nil
50 })
51 if err != nil || calls != 1 || got != "synthesis" {
52 t.Fatalf("continuation = (%q, %v), calls=%d", got, err, calls)
53 }
54}