blob: e711d47eb231e1c05bdc9ef118a5d150be2a3298 [file] [log] [blame]
Sketch🕴️305f8172026-02-27 13:58:43 +04001package loop
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "strings"
8 "time"
9
10 "dodo.cloud/neo/tools"
11
12 "github.com/anthropics/anthropic-sdk-go"
13)
14
15type AnthropicAgent struct {
Sketch🕴️00202652026-02-28 21:10:00 +040016 pr PromptReader
Sketch🕴️305f8172026-02-27 13:58:43 +040017 reg tools.Registry
18 client anthropic.Client
19}
20
21func (a *AnthropicAgent) Run(todo *ToDo) error {
22 sp := fmt.Sprintf(systemPrompt, ToDoJSONSchema())
23 var toolParams []anthropic.ToolParam
24 for _, t := range a.reg.All() {
25 schema, err := GetToolSchema(t.InputSchema())
26 if err != nil {
27 return err
28 }
29 toolParams = append(toolParams, anthropic.ToolParam{
30 Name: t.Name(),
31 Description: anthropic.String(t.Description()),
32 InputSchema: schema,
33 })
34 }
35 tools := make([]anthropic.ToolUnionParam, len(toolParams))
36 for i, toolParam := range toolParams {
37 tools[i] = anthropic.ToolUnionParam{OfTool: &toolParam}
38 }
Sketch🕴️305f8172026-02-27 13:58:43 +040039 for {
Sketch🕴️00202652026-02-28 21:10:00 +040040 todo.Lock()
Sketch🕴️305f8172026-02-27 13:58:43 +040041 items := findActionableItems(todo, "assistant")
42 if len(items) == 0 {
Sketch🕴️00202652026-02-28 21:10:00 +040043 fmt.Println("## AGENT NO ITEMS")
44 todo.Unlock()
Sketch🕴️305f8172026-02-27 13:58:43 +040045 time.Sleep(30 * time.Second)
46 continue
47 }
48 var itemIds []string
49 var messages []anthropic.MessageParam
50 for _, i := range items {
51 itemIds = append(itemIds, i.ID)
52 }
Sketch🕴️00202652026-02-28 21:10:00 +040053 fmt.Println(todo.String())
54 fmt.Printf("-- AGENT START WORKING %s\n", strings.Join(itemIds, ", "))
55 b, err := json.MarshalIndent(todo, "", "\t")
56 if err != nil {
57 todo.Unlock()
58 break
59 }
60 messages = append(messages, anthropic.NewUserMessage(anthropic.NewTextBlock(string(b))))
Sketch🕴️305f8172026-02-27 13:58:43 +040061 messages = append(messages, anthropic.NewUserMessage(anthropic.NewTextBlock(
62 fmt.Sprintf("Work on TODO item: %s", strings.Join(itemIds, ", ")))))
63 for {
64 resp, err := a.client.Messages.New(context.TODO(), anthropic.MessageNewParams{
65 MaxTokens: 10240,
66 System: []anthropic.TextBlockParam{
67 {Text: sp},
68 },
69 Messages: messages,
70 Model: anthropic.ModelClaudeOpus4_6,
71 Tools: tools,
72 })
73 if err != nil {
Sketch🕴️00202652026-02-28 21:10:00 +040074 todo.Unlock()
Sketch🕴️305f8172026-02-27 13:58:43 +040075 return err
76 }
77 fmt.Printf("--- STOP_REASON: %s\n", resp.StopReason)
78 messages = append(messages, resp.ToParam())
79
80 var toolResults []anthropic.ContentBlockParamUnion
81 for _, block := range resp.Content {
82 switch v := block.AsAny().(type) {
83 case anthropic.TextBlock:
84 fmt.Printf("AI: %s\n", v.Text)
85 case anthropic.ToolUseBlock:
Sketch🕴️00202652026-02-28 21:10:00 +040086 args := v.JSON.Input.Raw()
87 if v.Name == "bash_command" {
88 fmt.Printf("!!!!! %s: ", args)
89 p, err := a.pr.Read()
90 if err != nil {
91 todo.Unlock()
92 return err
93 }
94 if p != "OK" {
95 toolResults = append(toolResults, anthropic.NewToolResultBlock(v.ID, p, true))
96 continue
97 }
98 }
Sketch🕴️305f8172026-02-27 13:58:43 +040099 t := a.reg.Get(v.Name)
100 if t == nil {
101 toolResults = append(toolResults, anthropic.NewToolResultBlock(v.ID, fmt.Sprintf("unknown tool %q", v.Name), true))
102 continue
103 }
Sketch🕴️00202652026-02-28 21:10:00 +0400104
Sketch🕴️305f8172026-02-27 13:58:43 +0400105 fmt.Printf("CALLING TOOL: %s %s\n", v.Name, args)
106 out, err := t.Call(string(args))
107 if err != nil {
108 fmt.Printf("ERR: %s\n", err.Error())
109 toolResults = append(toolResults, anthropic.NewToolResultBlock(v.ID, err.Error(), true))
110 } else {
111 toolResults = append(toolResults, anthropic.NewToolResultBlock(v.ID, out, false))
112 }
113 }
114 }
115 if len(toolResults) == 0 {
116 break
117 }
118 messages = append(messages, anthropic.NewUserMessage(toolResults...))
119 }
120 for _, i := range items {
121 i.AssignedTo = "user"
122 }
Sketch🕴️00202652026-02-28 21:10:00 +0400123 todo.Unlock()
Sketch🕴️305f8172026-02-27 13:58:43 +0400124 }
Sketch🕴️00202652026-02-28 21:10:00 +0400125 return nil
Sketch🕴️305f8172026-02-27 13:58:43 +0400126}