| 1 | package agent |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "os" |
| 6 | "path/filepath" |
| 7 | "testing" |
| 8 | |
| 9 | "reasonix/internal/event" |
| 10 | "reasonix/internal/provider" |
| 11 | "reasonix/internal/sandbox" |
| 12 | "reasonix/internal/tool" |
| 13 | "reasonix/internal/tool/builtin" |
| 14 | ) |
| 15 | |
| 16 | func TestRebuiltAgentKeepsLiveFileObservationsAndRealShellWorks(t *testing.T) { |
| 17 | dir := t.TempDir() |
| 18 | if err := os.WriteFile(filepath.Join(dir, "file"), []byte("old"), 0o600); err != nil { |
| 19 | t.Fatal(err) |
| 20 | } |
| 21 | reg := tool.NewRegistry() |
| 22 | for _, target := range (builtin.Workspace{Dir: dir, Bash: sandbox.Spec{Mode: "off"}}).Tools("read_file", "edit_file", "bash") { |
| 23 | reg.Add(target) |
| 24 | } |
| 25 | a := New(nil, reg, NewSession(""), Options{}, event.Discard) |
| 26 | // This test exercises observation transfer and call ordering. Test binaries |
| 27 | // do not register the production Windows sandbox helper entry point, so run |
| 28 | // the diagnostic shell call under an explicit unconfined test preset. |
| 29 | a.SetPermissionPresetProvider(func() string { return "danger-full-access" }) |
| 30 | run := func(a *Agent, calls []provider.ToolCall) { |
| 31 | t.Helper() |
| 32 | a.Session().Add(provider.Message{Role: provider.RoleAssistant, ToolCalls: calls}) |
| 33 | batch := a.executeBatch(context.Background(), &a.turn, calls) |
| 34 | if batch.err != nil { |
| 35 | t.Fatal(batch.err) |
| 36 | } |
| 37 | for _, out := range batch.outcomes { |
| 38 | if out.errMsg != "" || out.blocked { |
| 39 | t.Fatalf("tool failed: %+v", out) |
| 40 | } |
| 41 | } |
| 42 | } |
| 43 | run(a, []provider.ToolCall{{ID: "read", Name: "read_file", Arguments: `{"path":"file","limit":1}`}}) |
| 44 | b := New(nil, reg, NewSession(""), Options{}, event.Discard) |
| 45 | b.SetPermissionPresetProvider(func() string { return "danger-full-access" }) |
| 46 | b.InheritFileObservationsFrom(a) |
| 47 | run(b, []provider.ToolCall{ |
| 48 | {ID: "edit", Name: "edit_file", Arguments: `{"path":"file","old_string":"old","new_string":"new"}`}, |
| 49 | {ID: "shell", Name: "bash", Arguments: `{"command":"git --version"}`}, |
| 50 | {ID: "edit-again", Name: "edit_file", Arguments: `{"path":"file","old_string":"new","new_string":"final"}`}, |
| 51 | }) |
| 52 | } |
| 53 |