Let the lead keep notes on how its user works

This commit is contained in:
Ian Gulliver
2026-10-16 10:50:29 -07:00
parent 919e1f8002
commit d6f73a1560
18 changed files with 759 additions and 208 deletions
+8 -3
View File
@@ -25,7 +25,8 @@ const leadGuidance = `You are the human's lead agent in byre, shown in the left
- The changes tab only when the human asks to see the diff; they rarely want it.
- Show, don't tell. What the right pane shows, the human reads there: never repeat, quote or paraphrase a task agent's question, choices, context or final message in chat. Add only what the pane doesn't say, such as your recommendation and why, or how it connects to other work.
- Don't move the pane for things that don't need the human, and don't pull it away from what they are looking at unless the new thing is more urgent. While the human is working in the right pane, show is refused unless it is on the project tab or what it shows is finished. It is also refused on the project tab for 10 seconds after the human starts a TODO item there, since they are likely to start more. When show is refused, say in chat what you would have shown, and carry on. "The start hold is over" means those 10 seconds passed with the pane still on the project tab: show what you held back, if it still matters, or make your final message NO_REPLY.
- Questions from task agents: answer one yourself with answer_question only when the answer is clear, for example the agent's recommendation is plainly right given what the human has said, or the human already decided it. Otherwise show it to the human with your recommendation and wait. When the human tells you an answer, give it with answer_question. Say which questions you answered and how.
- Questions from task agents: answer one yourself with answer_question only when the answer is clear, for example the agent's recommendation is plainly right given what the human has said, the human already decided it, or one of your notes settles it outright. Otherwise show it to the human with your recommendation and wait. When the human tells you an answer, give it with answer_question. Say which questions you answered and how, naming the note by its id when one decided it.
- Notes: you learn how the human works and keep it with remember, forget and list_notes. byre adds every note to your instructions at the start of each lead session, so they outlast compaction; the human can read and delete them in the Settings tab. Write a note when the human shows a priority, a preference or the reasoning behind a choice that will recur: an answer that departs from the agent's recommendation (the answered signal shows both), a correction or overrule of you or a task agent, or a statement of how they want things done. Record the choice and the why, in their terms, general enough to apply to the next case and specific enough to act on, for example "Prefers replacing a state label over adding a tag beside it: keeps the picker uncluttered." Don't note one-off facts about a single task, and don't duplicate: update an existing note with remember and its id instead. When the human contradicts a note, update it or forget it. Before recommending anything, check your notes, and when one shaped the recommendation, cite its id.
- Finished turns: read them and show the human only what matters: work finished and ready to merge, problems, decisions made. Stay quiet about routine progress.
- Reviews: after each new snapshot of a task, a reviewer agent from the other vendor reviews the change. The results are on the task's reviews tab, read_transcript shows each review's findings, and a question shows the review of the turn that asked it. They are for the human to look at when they want; mention one only when its findings matter to what the human is deciding.
- Dig into context with list_tasks, read_transcript and read_inbox, and with your own Read, Grep and Bash on a task's worktree path (from list_tasks). Don't edit files in task worktrees; ask the task's agent with send_message.
@@ -146,7 +147,7 @@ func (m *Manager) DeliverLead(ctx context.Context, owner string) error {
}
batch, actor, text, images := nextLeadTurn(inputs)
if actor == "byre" {
if batch[0].Kind == store.LeadSignal {
state, err := m.leadStateText(ctx, owner)
if err != nil {
slog.Error("lead state", "owner", owner, "err", err)
@@ -555,7 +556,11 @@ func (m *Manager) answeredText(ctx context.Context, item api.InboxItem) string {
if r.Text != nil && strings.TrimSpace(*r.Text) != "" {
answer = strings.TrimSpace(answer + " " + *r.Text)
}
return fmt.Sprintf("%s: %s answered %s %s: %s", m.taskLabel(ctx, item.TaskId), r.User, item.Kind, item.Id, clip(answer, 600))
if item.Kind != api.Question {
return fmt.Sprintf("%s: %s answered %s %s: %s", m.taskLabel(ctx, item.TaskId), r.User, item.Kind, item.Id, clip(answer, 600))
}
recommended := strings.Join(anyStrings(item.Payload["recommended"]), "; ")
return fmt.Sprintf("%s: %s answered question %s %q: %s\nThe agent recommended: %s", m.taskLabel(ctx, item.TaskId), r.User, item.Id, clip(fmt.Sprint(item.Payload["question"]), 300), clip(answer, 600), clip(recommended, 600))
}
func remarshal(from any, to any) error {
+106
View File
@@ -0,0 +1,106 @@
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
}
+1 -1
View File
@@ -430,7 +430,7 @@ func (m *Manager) leadTools(l *live) []mcp.Tool {
m.mu.Unlock()
return "byre will ask for your handoff once this turn ends.", nil
}),
}, m.workflowTools(tool)...)
}, append(m.workflowTools(tool), m.noteTools(tool)...)...)
}
func (m *Manager) SetFocus(ctx context.Context, login string, f api.Focus, actor string) error {
+6
View File
@@ -78,6 +78,7 @@ type live struct {
taskID string
projectID string
owner string
notes string
reviewID string
stage string
stopping bool
@@ -599,6 +600,11 @@ func (m *Manager) ensureLive(ctx context.Context, sess api.AgentSession, task ap
return nil, err
}
g = leadGuidance
notes, err := m.notesText(ctx, l.owner)
if err != nil {
return nil, err
}
l.notes = "\n\n" + notes
tools = m.leadTools(l)
env = nil
}
+2 -2
View File
@@ -70,7 +70,7 @@ func (m *Manager) startClaude(ctx context.Context, sess api.AgentSession, l *liv
GitName: l.gitName,
GitEmail: l.gitEmail,
Env: env,
Guidance: g,
Guidance: g + l.notes,
MCPServers: servers,
StdioMCP: stdio,
Hooks: hooks,
@@ -157,7 +157,7 @@ func (m *Manager) startCodex(ctx context.Context, sess api.AgentSession, l *live
GitName: l.gitName,
GitEmail: l.gitEmail,
Env: append(env, "CODEX_HOME="+m.codexHome),
Guidance: g,
Guidance: g + l.notes,
Reminder: g,
MCPServers: servers,
StdioMCP: stdio,
+245 -187
View File
@@ -684,6 +684,14 @@ type InboxResponseType string
// InboxState defines model for InboxState.
type InboxState string
// LeadNote defines model for LeadNote.
type LeadNote struct {
CreatedAt time.Time `json:"created_at"`
Id string `json:"id"`
Text string `json:"text"`
UpdatedAt time.Time `json:"updated_at"`
}
// MessageCreate defines model for MessageCreate.
type MessageCreate struct {
// Images Blob ids from uploadBlob, sent to the agent as images alongside the text.
@@ -1237,6 +1245,12 @@ type ServerInterface interface {
// (POST /api/me/lead/messages)
MessageLead(w http.ResponseWriter, r *http.Request)
// (GET /api/me/lead/notes)
ListLeadNotes(w http.ResponseWriter, r *http.Request)
// (DELETE /api/me/lead/notes/{noteId})
DeleteLeadNote(w http.ResponseWriter, r *http.Request, noteId string)
// (GET /api/me/lead/sessions)
ListLeadSessions(w http.ResponseWriter, r *http.Request)
@@ -1667,6 +1681,46 @@ func (siw *ServerInterfaceWrapper) MessageLead(w http.ResponseWriter, r *http.Re
handler.ServeHTTP(w, r)
}
// ListLeadNotes operation middleware
func (siw *ServerInterfaceWrapper) ListLeadNotes(w http.ResponseWriter, r *http.Request) {
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
siw.Handler.ListLeadNotes(w, r)
}))
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler.ServeHTTP(w, r)
}
// DeleteLeadNote operation middleware
func (siw *ServerInterfaceWrapper) DeleteLeadNote(w http.ResponseWriter, r *http.Request) {
var err error
_ = err
// ------------- Path parameter "noteId" -------------
var noteId string
err = runtime.BindStyledParameterWithOptions("simple", "noteId", r.PathValue("noteId"), &noteId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: "", ValueIsUnescaped: true})
if err != nil {
siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "noteId", Err: err})
return
}
handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
siw.Handler.DeleteLeadNote(w, r, noteId)
}))
for _, middleware := range siw.HandlerMiddlewares {
handler = middleware(handler)
}
handler.ServeHTTP(w, r)
}
// ListLeadSessions operation middleware
func (siw *ServerInterfaceWrapper) ListLeadSessions(w http.ResponseWriter, r *http.Request) {
@@ -2955,6 +3009,8 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H
m.HandleFunc(http.MethodPut+" "+options.BaseURL+"/api/me/notifications", wrapper.UpdateNotificationPrefs)
m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/me/lead/sessions", wrapper.ListLeadSessions)
m.HandleFunc(http.MethodPost+" "+options.BaseURL+"/api/me/lead/messages", wrapper.MessageLead)
m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/me/lead/notes", wrapper.ListLeadNotes)
m.HandleFunc(http.MethodDelete+" "+options.BaseURL+"/api/me/lead/notes/{noteId}", wrapper.DeleteLeadNote)
m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/api/me/focus", wrapper.GetFocus)
m.HandleFunc(http.MethodPut+" "+options.BaseURL+"/api/me/focus", wrapper.SetFocus)
m.HandleFunc(http.MethodPut+" "+options.BaseURL+"/api/me/pane-focus", wrapper.SetPaneFocus)
@@ -3010,193 +3066,195 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H
// const string: with thousands of chunks the chained `+` fold is several
// times slower for the Go compiler than parsing a slice literal.
var swaggerSpec = []string{
"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",
"GXubU/QY8p2KG3/69H8DAAD//w==",
"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",
}
// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes,
+18
View File
@@ -52,6 +52,24 @@ func (s *Server) MessageLead(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusAccepted, sess)
}
func (s *Server) ListLeadNotes(w http.ResponseWriter, r *http.Request) {
notes, err := s.store.LeadNotes(r.Context(), actor(r))
if err != nil {
writeStoreError(w, err)
return
}
writeJSON(w, http.StatusOK, notes)
}
func (s *Server) DeleteLeadNote(w http.ResponseWriter, r *http.Request, noteId string) {
err := s.agents.DeleteLeadNote(r.Context(), actor(r), noteId)
if err != nil {
writeAgentError(w, err)
return
}
w.WriteHeader(http.StatusNoContent)
}
func (s *Server) GetFocus(w http.ResponseWriter, r *http.Request) {
f, err := s.store.GetFocus(r.Context(), actor(r))
if err != nil {
+86
View File
@@ -0,0 +1,86 @@
package store
import (
"context"
"database/sql"
"errors"
"fmt"
"time"
"g.fc.run/misc/byre/internal/api"
)
func (s *Store) LeadNotes(ctx context.Context, owner string) ([]api.LeadNote, error) {
rows, err := s.db.QueryContext(ctx, `SELECT id, text, created_at, updated_at FROM lead_notes WHERE owner = ? ORDER BY created_at, id`, owner)
if err != nil {
return nil, err
}
defer rows.Close()
notes := []api.LeadNote{}
for rows.Next() {
n := api.LeadNote{}
createdAt := ""
updatedAt := ""
err = rows.Scan(&n.Id, &n.Text, &createdAt, &updatedAt)
if err != nil {
return nil, err
}
n.CreatedAt, err = parseTime(createdAt)
if err != nil {
return nil, err
}
n.UpdatedAt, err = parseTime(updatedAt)
if err != nil {
return nil, err
}
notes = append(notes, n)
}
return notes, rows.Err()
}
func (s *Store) SaveLeadNote(ctx context.Context, owner, id, text, actor string) (api.LeadNote, error) {
at := time.Now().UTC()
n := api.LeadNote{Id: id, Text: text, CreatedAt: at, UpdatedAt: at}
err := s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
if id == "" {
n.Id = NewID()
_, err := tx.ExecContext(ctx, `INSERT INTO lead_notes (id, owner, text, created_at, updated_at) VALUES (?, ?, ?, ?, ?)`,
n.Id, owner, text, formatTime(at), formatTime(at))
if err != nil {
return err
}
} else {
createdAt := ""
err := tx.QueryRowContext(ctx, `UPDATE lead_notes SET text = ?, updated_at = ? WHERE id = ? AND owner = ? RETURNING created_at`,
text, formatTime(at), id, owner).Scan(&createdAt)
if errors.Is(err, sql.ErrNoRows) {
return fmt.Errorf("note %s: %w", id, ErrNotFound)
}
if err != nil {
return err
}
n.CreatedAt, err = parseTime(createdAt)
if err != nil {
return err
}
}
return emit(api.Event{Kind: "lead.note_saved", Owner: &owner, Actor: actor, Payload: ToPayload(n)})
})
return n, err
}
func (s *Store) DeleteLeadNote(ctx context.Context, owner, id, actor string) (string, error) {
text := ""
err := s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
err := tx.QueryRowContext(ctx, `DELETE FROM lead_notes WHERE id = ? AND owner = ? RETURNING text`, id, owner).Scan(&text)
if errors.Is(err, sql.ErrNoRows) {
return fmt.Errorf("note %s: %w", id, ErrNotFound)
}
if err != nil {
return err
}
return emit(api.Event{Kind: "lead.note_deleted", Owner: &owner, Actor: actor, Payload: map[string]any{"id": id, "text": text}})
})
return text, err
}
+9
View File
@@ -429,6 +429,15 @@ CREATE INDEX tasks_parent ON tasks(parent_task_id) WHERE parent_task_id != '';
ALTER TABLE reviews ADD COLUMN finding_states TEXT NOT NULL DEFAULT '{}';
UPDATE events SET payload = json_set(payload, '$.finding_states', json('{}'))
WHERE kind IN ('review.started', 'review.finished') AND json_type(payload, '$.finding_states') IS NULL;
`}, {"add-lead-notes", `
CREATE TABLE lead_notes (
id TEXT PRIMARY KEY,
owner TEXT NOT NULL REFERENCES users(login),
text TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE INDEX lead_notes_owner ON lead_notes(owner, created_at);
`}}
const (