Lead messages mid-turn

This commit is contained in:
Ian Gulliver
2026-10-18 00:12:42 -07:00
parent 1e3cc2d0c0
commit 2ed0adae90
6 changed files with 21 additions and 16 deletions
+2 -2
View File
File diff suppressed because one or more lines are too long
+7 -3
View File
@@ -139,8 +139,9 @@ func (m *Manager) leadTools(l *live) []mcp.Tool {
}),
tool("send_message",
"Send a message to a task's agent; it starts a turn. Starts the agent when the task has none. The agent must be idle: "+
"if it is mid-turn, wait for it to finish the turn or interrupt it first.",
"Send a message to a task's agent; it starts a turn. Starts the agent when the task has none. "+
"If the agent is mid-turn, the message is queued like the human's: the agent reads it with its next tool call's result, "+
"or as its next turn if it makes no further tool call, and gets it only once.",
`{"type": "object", "properties": {
"task_id": {"type": "string"},
"text": {"type": "string"},
@@ -174,10 +175,13 @@ func (m *Manager) leadTools(l *live) []mcp.Tool {
}
return fmt.Sprintf("Started an agent on %s with your message.", task.Id), nil
}
_, err = m.Send(ctx, sess.Id, leadActor(user.Login), args.Text, args.Model)
_, queued, err := m.Message(ctx, sess.Id, leadActor(user.Login), args.Text, args.Model)
if err != nil {
return "", err
}
if queued {
return fmt.Sprintf("Queued for %s's agent, which is mid-turn: it reads the message with its next tool call's result, or as its next turn.", task.Id), nil
}
return fmt.Sprintf("Sent to %s's agent.", task.Id), nil
}),
+9 -8
View File
@@ -39,40 +39,41 @@ func (m *Manager) lockDelivery(sessionID string) func() {
}
}
func (m *Manager) Message(ctx context.Context, sessionID, actor, text, model string, images ...string) (api.AgentSession, error) {
func (m *Manager) Message(ctx context.Context, sessionID, actor, text, model string, images ...string) (api.AgentSession, bool, error) {
unlock := m.lockDelivery(sessionID)
defer unlock()
sess, err := m.store.GetSession(ctx, sessionID)
if err != nil {
return api.AgentSession{}, err
return api.AgentSession{}, false, err
}
if sess.State != api.AgentSessionStateRunning || sess.Role != api.Implementer {
return m.Send(ctx, sessionID, actor, text, model, images...)
sess, err = m.Send(ctx, sessionID, actor, text, model, images...)
return sess, false, err
}
if model != "" && model != sess.Model {
err = checkModel(sess, model)
if err != nil {
return api.AgentSession{}, err
return api.AgentSession{}, false, err
}
err = m.store.SetSessionModel(ctx, sess.Id, model, actor)
if err != nil {
return api.AgentSession{}, err
return api.AgentSession{}, false, err
}
sess.Model = model
}
task, err := m.store.GetTask(ctx, sess.TaskId)
if err != nil {
return api.AgentSession{}, err
return api.AgentSession{}, false, err
}
err = m.store.AddTaskInput(ctx, store.TaskInput{TaskID: sess.TaskId, Actor: actor, Text: text, Images: images}, task.ProjectId, sess.Id)
if err != nil {
return api.AgentSession{}, err
return api.AgentSession{}, false, err
}
slog.Info("message queued", "session", sess.Id, "task", sess.TaskId, "actor", actor)
return sess, nil
return sess, true, nil
}
func (m *Manager) Withdraw(ctx context.Context, taskID string, inputID int64, actor string) error {
+1 -1
View File
@@ -334,7 +334,7 @@ func (m *Manager) forwardFindings(taskID, reviewID string) {
if s.State == api.AgentSessionStateCompleted {
continue
}
_, err = m.Message(ctx, s.Id, "byre", findingsMessage(r), "")
_, _, err = m.Message(ctx, s.Id, "byre", findingsMessage(r), "")
if err != nil {
slog.Error("forward review findings", "review", reviewID, "session", s.Id, "err", err)
return
+1 -1
View File
@@ -297,7 +297,7 @@ func (m *Manager) messageTask(ctx context.Context, task api.Task, user api.User,
_, err = m.create(ctx, task, user, text, "", actor, nil)
return err
}
_, err = m.Message(ctx, sess.Id, actor, text, "")
_, _, err = m.Message(ctx, sess.Id, actor, text, "")
return err
}
+1 -1
View File
@@ -792,7 +792,7 @@ func (s *Server) SendMessage(w http.ResponseWriter, r *http.Request, sessionID s
model = *req.Model
}
sess, err := s.agents.Message(r.Context(), sessionID, actor(r), req.Text, model, images...)
sess, _, err := s.agents.Message(r.Context(), sessionID, actor(r), req.Text, model, images...)
if err != nil {
writeAgentError(w, err)
return