107 lines
3.5 KiB
Go
107 lines
3.5 KiB
Go
package agents
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"g.fc.run/misc/byre/internal/api"
|
|
"g.fc.run/misc/byre/internal/mcp"
|
|
"g.fc.run/misc/byre/internal/store"
|
|
)
|
|
|
|
func (m *Manager) noteTools(tool func(name, description, schema string, run func(ctx context.Context, user api.User, raw json.RawMessage) (string, error)) mcp.Tool) []mcp.Tool {
|
|
return []mcp.Tool{
|
|
tool("remember",
|
|
"Save a note about how the human works: a priority, a preference, or reasoning behind a choice, worded so you can apply it later. "+
|
|
"Without id it adds a note; with id it replaces that note's text. byre gives you every note at the start of each new lead session.",
|
|
`{"type": "object", "properties": {
|
|
"text": {"type": "string"},
|
|
"id": {"type": "string", "description": "The note to replace."}
|
|
}, "required": ["text"]}`,
|
|
func(ctx context.Context, user api.User, raw json.RawMessage) (string, error) {
|
|
args := struct {
|
|
Text string `json:"text"`
|
|
ID string `json:"id"`
|
|
}{}
|
|
err := json.Unmarshal(raw, &args)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
text := strings.TrimSpace(args.Text)
|
|
if text == "" {
|
|
return "", errors.New("text is empty")
|
|
}
|
|
n, err := m.store.SaveLeadNote(ctx, user.Login, args.ID, text, leadActor(user.Login))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if args.ID != "" {
|
|
return fmt.Sprintf("Note %s updated.", n.Id), nil
|
|
}
|
|
return fmt.Sprintf("Saved as note %s.", n.Id), nil
|
|
}),
|
|
|
|
tool("forget",
|
|
"Delete a note that no longer holds, for example because the human contradicted it.",
|
|
`{"type": "object", "properties": {"id": {"type": "string"}}, "required": ["id"]}`,
|
|
func(ctx context.Context, user api.User, raw json.RawMessage) (string, error) {
|
|
args := struct {
|
|
ID string `json:"id"`
|
|
}{}
|
|
err := json.Unmarshal(raw, &args)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
_, err = m.store.DeleteLeadNote(ctx, user.Login, args.ID, leadActor(user.Login))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return fmt.Sprintf("Note %s deleted.", args.ID), nil
|
|
}),
|
|
|
|
tool("list_notes",
|
|
"List your notes about how the human works, with their ids, as they are now. The human can delete notes in byre's Settings tab.",
|
|
`{"type": "object", "properties": {}}`,
|
|
func(ctx context.Context, user api.User, raw json.RawMessage) (string, error) {
|
|
return m.notesText(ctx, user.Login)
|
|
}),
|
|
}
|
|
}
|
|
|
|
func (m *Manager) DeleteLeadNote(ctx context.Context, login, id string) error {
|
|
text, err := m.store.DeleteLeadNote(ctx, login, id, login)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, found, err := m.activeLead(ctx, login)
|
|
if err != nil || !found {
|
|
return err
|
|
}
|
|
signal := fmt.Sprintf("The human deleted your note %s: %q. Stop applying it from now on, in recommendations, answers and handoffs, and don't write it again unless they say so. If this needs nothing from you to the human, make your final message NO_REPLY.", id, text)
|
|
err = m.store.AddLeadInput(ctx, store.LeadInput{Owner: login, Kind: store.LeadMessage, Actor: "byre", Text: signal})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m.deliverLead(login)
|
|
return nil
|
|
}
|
|
|
|
func (m *Manager) notesText(ctx context.Context, owner string) (string, error) {
|
|
notes, err := m.store.LeadNotes(ctx, owner)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(notes) == 0 {
|
|
return fmt.Sprintf("You have no notes yet on how %s works.", owner), nil
|
|
}
|
|
b := &strings.Builder{}
|
|
fmt.Fprintf(b, "Your notes on how %s works (%d):", owner, len(notes))
|
|
for _, n := range notes {
|
|
fmt.Fprintf(b, "\n- [%s] %s", n.Id, n.Text)
|
|
}
|
|
return b.String(), nil
|
|
}
|