Add ask_human and the inbox with batched responses and delivery to agents

This commit is contained in:
Ian Gulliver
2026-10-14 07:02:56 -07:00
parent 09951e64e7
commit befd476598
16 changed files with 1962 additions and 129 deletions
+80
View File
@@ -0,0 +1,80 @@
package agents
import (
"context"
"strings"
"testing"
"g.fc.run/misc/byre/internal/api"
"g.fc.run/misc/byre/internal/store"
)
func TestValidateAsk(t *testing.T) {
m := &Manager{}
l := &live{id: "s1"}
yes := true
cases := []struct {
name string
args askArgs
want []string
}{
{"valid", askArgs{Question: "q", WhyBlocked: "w", Options: []string{"a", "b"}, RecommendedOption: "a", Blocking: &yes}, nil},
{"empty", askArgs{}, []string{"question is empty", "why_blocked is empty", "at least two", "recommended_option", "blocking is required"}},
{"one option", askArgs{Question: "q", WhyBlocked: "w", Options: []string{"a"}, RecommendedOption: "a", Blocking: &yes}, []string{"at least two"}},
{"bad recommendation", askArgs{Question: "q", WhyBlocked: "w", Options: []string{"a", "b"}, RecommendedOption: "c", Blocking: &yes}, []string{"recommended_option"}},
{"bad attachment", askArgs{Question: "q", WhyBlocked: "w", Options: []string{"a", "b"}, RecommendedOption: "a", Blocking: &yes,
Attachments: []api.Attachment{{Kind: "video"}}}, []string{`attachments[0].kind "video"`}},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
err := m.validateAsk(context.Background(), l, c.args)
if len(c.want) == 0 {
if err != nil {
t.Fatalf("unexpected error %v", err)
}
return
}
if err == nil {
t.Fatalf("want rejection mentioning %v", c.want)
}
if !strings.Contains(err.Error(), "nothing was filed") {
t.Fatalf("rejection doesn't say nothing was filed: %v", err)
}
for _, w := range c.want {
if !strings.Contains(err.Error(), w) {
t.Fatalf("rejection %q doesn't mention %q", err, w)
}
}
})
}
}
func TestDeliveryMessage(t *testing.T) {
str := func(s string) *string { return &s }
question := api.InboxItem{Id: "q1", ThreadId: "q1", Kind: api.InboxItemKindQuestion, Payload: map[string]any{"question": "Which color?"}}
report := api.InboxItem{Id: "r1", ThreadId: "r1", Kind: api.InboxItemKindReport, Payload: map[string]any{}}
msg := deliveryMessage([]store.Pending{
{Item: question, Response: api.InboxResponse{User: "alice", Type: api.InboxResponseTypeOption, Option: str("blue"), Text: str("navy if possible")}},
{Item: question, Response: api.InboxResponse{User: "bob", Type: api.InboxResponseTypeQuestion, Text: str("what is it for?"),
Attachments: []api.Attachment{{Kind: api.AttachmentKindUrl, Url: str("https://example.com")}}}},
{Item: report, Response: api.InboxResponse{User: "alice", Type: api.InboxResponseTypeRedirect, Text: str("stop and write tests")}},
})
for _, want := range []string{
"Your question q1: Which color?",
"alice chose: blue",
"Note: navy if possible",
"bob asked a follow-up question: what is it for?",
`calling ask_human with thread_id "q1"`,
`"url":"https://example.com"`,
"Your report r1",
"alice redirected you. These instructions replace your current direction: stop and write tests",
} {
if !strings.Contains(msg, want) {
t.Fatalf("message missing %q:\n%s", want, msg)
}
}
}