package agents import ( "context" "errors" "log/slog" "strings" "g.fc.run/misc/byre/internal/api" "g.fc.run/misc/byre/internal/store" ) const handoffPrompt = `Write a handoff for a fresh agent that will continue this task with none of your context. ` + `Cover: the goal; decisions made and why; the current state (what is done, what is in progress, what is verified); ` + `dead ends tried; open questions; and the files that matter. Be complete: the next agent sees nothing else. ` + `Your final message is the handoff, and nothing else.` const handoffPreamble = "You are continuing this task from a previous agent session, which ended so the work could go on with a fresh context. Its handoff follows.\n\n" const leadHandoffPreamble = "You are continuing as the human's lead agent from a previous session, which ended so you could go on with a fresh context. Its handoff follows. Signals that arrived meanwhile come next.\n\n" const leadHandoffPrompt = `Write a handoff for a fresh lead agent that will take over from you with none of your context. ` + `Cover: the human's standing instructions and preferences; what each task you are tracking is doing and what you are waiting on from it; ` + `questions you have surfaced or answered and why; what you last pointed the human's attention at; and anything you promised to do or check. ` + `Be complete: the next agent sees nothing else. Your final message is the handoff, and nothing else.` func (m *Manager) Compact(ctx context.Context, sessionID string) (api.AgentSession, error) { sess, err := m.store.GetSession(ctx, sessionID) if err != nil { return api.AgentSession{}, err } if sess.Owner != "" { return m.Send(ctx, sessionID, "byre", leadHandoffPrompt, "") } return m.Send(ctx, sessionID, "byre", handoffPrompt, "") } func isHandoffPrompt(actor, text string) bool { return actor == "byre" && (text == handoffPrompt || text == leadHandoffPrompt) } func isContinuation(actor, text string) bool { return actor == "byre" && (strings.HasPrefix(text, handoffPreamble) || strings.HasPrefix(text, leadHandoffPreamble)) } func (m *Manager) continueFromHandoff(sessionID, handoff string) { ctx := context.Background() err := m.handoff(ctx, sessionID, handoff) if err != nil { slog.Error("continue from handoff", "session", sessionID, "err", err) } } func (m *Manager) handoff(ctx context.Context, sessionID, handoff string) error { sess, err := m.store.GetSession(ctx, sessionID) if err != nil { return err } creator, err := m.store.GetUser(ctx, sess.CreatedBy) if err != nil { return err } if sess.Owner != "" { err = m.retire(ctx, sess, "byre") if err != nil { return err } _, err = m.createLead(ctx, creator, leadHandoffPreamble+handoff, sess.Model, "byre", nil) return err } discard, err := m.store.GetDiscard(ctx, sess.TaskId) if err != nil { return err } if discard.State != store.DiscardNone { m.startDiscard(sess.TaskId) return nil } err = transition(ctx, m.store, sess.Id, api.AgentSessionStateIdle, api.AgentSessionStateCompleted, "byre") if err != nil { return err } m.stopLive(sess.Id) task, err := m.store.GetTask(ctx, sess.TaskId) if err != nil { return err } _, err = m.create(ctx, task, creator, handoffPreamble+handoff, sess.Model, "byre", nil) if errors.Is(err, ErrConflict) { m.startDiscard(task.Id) return nil } return err }