I want you to integrate the SessionManager into the Harvey agent. All the relevant code is in this workspace. Here is exactly what needs to happen:
Add two fields to the Agent struct (around line 107):
Sessions *SessionManager
SessionID int64
Add a new method initSessions alongside initKnowledgeBase (around line 227):
func (a *Agent) initSessions(out io.Writer) {
sm, err := OpenSessionManager(a.Workspace)
if err != nil {
fmt.Fprintf(out, " ✗ Sessions unavailable: %v\n", err)
return
}
a.Sessions = sm
fmt.Fprintln(out, "✓ Sessions: agents/sessions/ (Fountain .spmd files)")
}
Call it in Run() immediately after a.initKnowledgeBase(out) (around line 84).
Also defer cleanup in Run() alongside the existing Recorder defer:
defer func() {
if a.Sessions != nil {
a.Sessions.Close()
}
}()
After the selectBackend call in Run() succeeds (around line 98), add this block. It loads the last session and offers to resume it; if the user declines (or no session exists), it creates a fresh one.
if a.Sessions != nil {
last, err := a.Sessions.LoadLast()
if err != nil {
fmt.Fprintf(out, yellow(" ✗")+" Session load error: %v\n", err)
} else if last != nil && len(last.History) > 0 {
turns := 0
for _, m := range last.History {
if m.Role == "user" {
turns++
}
}
label := last.Name
if label == "" {
label = "(unnamed)"
}
fmt.Fprintf(out, "\n Last session: %s — %d turn(s), model: %s, %s\n",
label, turns, last.Model, last.LastActive.Format("2006-01-02 15:04"))
if askYesNo(reader, out, " Resume last session? [Y/n] ", true) {
a.History = last.History
a.SessionID = last.ID
fmt.Fprintf(out, green("✓")+" Session %d resumed (%d messages)\n",
last.ID, len(last.History))
}
}
if a.SessionID == 0 {
model := ""
if a.Client != nil {
model = a.Client.Name()
}
id, err := a.Sessions.Create(a.Workspace.Root, model, nil)
if err != nil {
fmt.Fprintf(out, yellow(" ✗")+" Could not create session: %v\n", err)
} else {
a.SessionID = id
}
}
}
In the REPL loop, after a.AddMessage(“assistant”, buf.String()) (around line 200), add a checkpoint save:
if a.Sessions != nil && a.SessionID != 0 {
model := ""
if a.Client != nil {
model = a.Client.Name()
}
if err := a.Sessions.Save(a.SessionID, model, a.History); err != nil {
fmt.Fprintf(out, yellow(" ✗")+" Session save error: %v\n", err)
}
}
In cmdClear (around line 222), after a.ClearHistory(), add:
if a.Sessions != nil {
model := ""
if a.Client != nil {
model = a.Client.Name()
}
id, err := a.Sessions.Create(a.Workspace.Root, model, nil)
if err != nil {
fmt.Fprintf(out, " Session create error: %v\n", err)
} else {
a.SessionID = id
}
}
In cmdStatus (around line 199), add after the KB block:
if a.Sessions != nil && a.SessionID != 0 {
fmt.Fprintf(out, "Session: id=%d\n", a.SessionID)
} else {
fmt.Fprintln(out, "Session: none")
}
Register a new command in registerCommands():
"session": {
Usage: "/session <list|resume ID|new|name TEXT>",
Description: "Manage Harvey sessions",
Handler: cmdSession,
},
Implement cmdSession:
func cmdSession(a *Agent, args []string, out io.Writer) error {
if a.Sessions == nil {
fmt.Fprintln(out, "Sessions are not available.")
return nil
}
if len(args) == 0 || strings.ToLower(args[0]) == "list" {
sessions, err := a.Sessions.List()
if err != nil {
return err
}
if len(sessions) == 0 {
fmt.Fprintln(out, " (no sessions)")
return nil
}
for _, s := range sessions {
active := ""
if s.ID == a.SessionID {
active = " *"
}
label := s.Name
if label == "" {
label = "(unnamed)"
}
fmt.Fprintf(out, " [%d]%s %s — %s — %s\n",
s.ID, active, label, s.Model,
s.LastActive.Format("2006-01-02 15:04"))
}
return nil
}
switch strings.ToLower(args[0]) {
case "resume":
if len(args) < 2 {
fmt.Fprintln(out, "Usage: /session resume ID")
return nil
}
var id int64
if _, err := fmt.Sscanf(args[1], "%d", &id); err != nil {
fmt.Fprintf(out, "Invalid session ID: %s\n", args[1])
return nil
}
s, err := a.Sessions.Load(id)
if err != nil {
return err
}
if s == nil {
fmt.Fprintf(out, "No session with id=%d\n", id)
return nil
}
a.History = s.History
a.SessionID = s.ID
fmt.Fprintf(out, "Session %d resumed (%d messages, model: %s)\n",
s.ID, len(s.History), s.Model)
case "new":
model := ""
if a.Client != nil {
model = a.Client.Name()
}
id, err := a.Sessions.Create(a.Workspace.Root, model, nil)
if err != nil {
return err
}
a.ClearHistory()
a.SessionID = id
fmt.Fprintf(out, "New session started (id=%d)\n", id)
case "name":
if len(args) < 2 {
fmt.Fprintln(out, "Usage: /session name TEXT")
return nil
}
if a.SessionID == 0 {
fmt.Fprintln(out, "No active session.")
return nil
}
name := strings.Join(args[1:], " ")
if err := a.Sessions.Rename(a.SessionID, name); err != nil {
return err
}
fmt.Fprintf(out, "Session %d named %q\n", a.SessionID, name)
default:
fmt.Fprintf(out, "Unknown session subcommand: %s\n", args[0])
fmt.Fprintln(out, "Usage: /session <list|resume ID|new|name TEXT>")
}
return nil
}
After making all changes, run:
go build ./...
go test ./...
Both must pass before you report the task complete. Fix any compilation errors before moving on. Do not change sessions.go or sessions_test.go — those files are already correct.