Let Compact continue on another model or vendor
This commit is contained in:
@@ -24,15 +24,38 @@ const leadHandoffPrompt = `Write a handoff for a fresh lead agent that will take
|
||||
`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) {
|
||||
func (m *Manager) Compact(ctx context.Context, sessionID, model 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, "")
|
||||
task, err := m.taskFor(ctx, sess)
|
||||
if err != nil {
|
||||
return api.AgentSession{}, err
|
||||
}
|
||||
return m.Send(ctx, sessionID, "byre", handoffPrompt, "")
|
||||
prompt := handoffPrompt
|
||||
if sess.Owner != "" {
|
||||
prompt = leadHandoffPrompt
|
||||
}
|
||||
err = transition(ctx, m.store, sess.Id, api.AgentSessionStateIdle, api.AgentSessionStateRunning, "byre")
|
||||
if err != nil {
|
||||
return api.AgentSession{}, err
|
||||
}
|
||||
m.mu.Lock()
|
||||
m.handoffModel[sessionID] = model
|
||||
m.mu.Unlock()
|
||||
return m.launch(ctx, sess, task, "byre", prompt, nil)
|
||||
}
|
||||
|
||||
func (m *Manager) continuationModel(sess api.AgentSession) string {
|
||||
m.mu.Lock()
|
||||
model := m.handoffModel[sess.Id]
|
||||
delete(m.handoffModel, sess.Id)
|
||||
m.mu.Unlock()
|
||||
if model == "" {
|
||||
return sess.Model
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
func isHandoffPrompt(actor, text string) bool {
|
||||
@@ -60,13 +83,14 @@ func (m *Manager) handoff(ctx context.Context, sessionID, handoff string) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
model := m.continuationModel(sess)
|
||||
|
||||
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)
|
||||
_, err = m.createLead(ctx, creator, leadHandoffPreamble+handoff, model, "byre", nil)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -88,7 +112,7 @@ func (m *Manager) handoff(ctx context.Context, sessionID, handoff string) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = m.create(ctx, task, creator, handoffPreamble+handoff, sess.Model, "byre", nil)
|
||||
_, err = m.create(ctx, task, creator, handoffPreamble+handoff, model, "byre", nil)
|
||||
if errors.Is(err, ErrConflict) {
|
||||
m.startDiscard(task.Id)
|
||||
return nil
|
||||
|
||||
@@ -293,7 +293,7 @@ func (m *Manager) leadTurnEnded(ctx context.Context, l *live, res endResult) err
|
||||
go m.continueFromHandoff(l.id, res.result)
|
||||
case wantCompact && finished && !compacting:
|
||||
go func() {
|
||||
_, err := m.Compact(context.Background(), l.id)
|
||||
_, err := m.Compact(context.Background(), l.id, "")
|
||||
if err != nil {
|
||||
slog.Error("compact lead", "session", l.id, "err", err)
|
||||
}
|
||||
|
||||
@@ -325,13 +325,24 @@ func (m *Manager) leadTools(l *live) []mcp.Tool {
|
||||
return "Interrupted.", nil
|
||||
}),
|
||||
|
||||
tool("compact_task", "Have a task's idle agent write a handoff and continue in a fresh session.", taskOnly,
|
||||
tool("compact_task", "Have a task's idle agent write a handoff and continue in a fresh session.",
|
||||
`{"type": "object", "properties": {
|
||||
"task_id": {"type": "string"},
|
||||
"model": {"type": "string", "description": "Start the fresh session on this model, on either vendor; the current agent still writes the handoff. Defaults to the current model."}
|
||||
}, "required": ["task_id"]}`,
|
||||
func(ctx context.Context, user api.User, raw json.RawMessage) (string, error) {
|
||||
args := struct {
|
||||
Model string `json:"model"`
|
||||
}{}
|
||||
err := json.Unmarshal(raw, &args)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sess, err := m.taskSessionArg(ctx, raw)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, err = m.Compact(ctx, sess.Id)
|
||||
_, err = m.Compact(ctx, sess.Id, args.Model)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ type Manager struct {
|
||||
startedTodo map[string]time.Time
|
||||
startRefused map[string]bool
|
||||
finishing map[string]bool
|
||||
handoffModel map[string]string
|
||||
|
||||
deliverMu sync.Mutex
|
||||
deliverLocks map[string]*sessionLock
|
||||
@@ -141,6 +142,7 @@ func New(st *store.Store, worktreeRoot, leadRoot, codexHome string, status func(
|
||||
startedTodo: map[string]time.Time{},
|
||||
startRefused: map[string]bool{},
|
||||
finishing: map[string]bool{},
|
||||
handoffModel: map[string]string{},
|
||||
signals: make(chan []api.Event, 1024),
|
||||
snaps: map[string]*snapState{},
|
||||
merges: map[string]*projectMerge{},
|
||||
|
||||
Reference in New Issue
Block a user