new task lag

This commit is contained in:
Ian Gulliver
2026-10-20 11:49:59 -07:00
parent cb327fa15d
commit 2431a174a7
6 changed files with 170 additions and 46 deletions
+100 -36
View File
@@ -380,6 +380,20 @@ func (m *Manager) Recover(ctx context.Context) error {
continue
}
pending, err := m.store.SessionPending(ctx, sess.Id)
if err != nil {
return err
}
if pending {
found, err := m.deliverRecorded(ctx, sess, task)
if err != nil {
slog.Error("resume session: deliver its recorded message", "session", sess.Id, "task", task.Id, "err", err)
}
if found {
continue
}
}
l, err := m.ensureLive(ctx, sess, task)
if err != nil {
ferr := m.failResume(ctx, sess, task, err)
@@ -399,7 +413,7 @@ func (m *Manager) Recover(ctx context.Context) error {
notice = discardRestartPrompt
}
}
err = m.message(ctx, l, "byre", notice, nil)
err = m.message(ctx, sess, task, l, "byre", notice, nil)
if err != nil {
ferr := m.failResume(ctx, sess, task, err)
if ferr != nil {
@@ -566,28 +580,67 @@ func (m *Manager) startRunning(ctx context.Context, sess api.AgentSession, taskI
}
func (m *Manager) launch(ctx context.Context, sess api.AgentSession, task api.Task, actor, text string, images []string) (api.AgentSession, error) {
imgs, compacting, err := m.recordMessage(ctx, sess, task, actor, text, images)
if err != nil {
m.failStart(ctx, sess.Id, actor, err)
return api.AgentSession{}, err
}
err = m.deliver(ctx, sess, task, actor, text, imgs, compacting)
if err != nil {
return api.AgentSession{}, err
}
return m.store.GetSession(ctx, sess.Id)
}
func (m *Manager) deliver(ctx context.Context, sess api.AgentSession, task api.Task, actor, text string, imgs []image, compacting bool) error {
l, err := m.ensureLive(ctx, sess, task)
if err == nil {
err = m.syncModel(ctx, l, sess.Model)
}
if err != nil {
m.failStart(ctx, sess.Id, actor, err)
return api.AgentSession{}, err
return err
}
imgs, err := m.prepareMessage(ctx, l, actor, text, images)
if err != nil {
m.failStart(ctx, sess.Id, actor, err)
return api.AgentSession{}, err
}
err = l.sess.send(ctx, text, imgs)
m.beginTurn(l, compacting)
err = m.send(ctx, l, text, imgs)
if err != nil {
m.endMergeTurn(sess.Id, false)
return api.AgentSession{}, err
return err
}
return nil
}
return m.store.GetSession(ctx, sess.Id)
func (m *Manager) deliverRecorded(ctx context.Context, sess api.AgentSession, task api.Task) (bool, error) {
ev, found, err := m.store.LastMessage(ctx, sess.Id)
if err != nil || !found {
return false, err
}
text, _ := ev.Payload["text"].(string)
ids := []string{}
raw, _ := ev.Payload["images"].([]any)
for _, id := range raw {
s, _ := id.(string)
ids = append(ids, s)
}
imgs, err := m.readImages(ctx, ids)
if err != nil {
m.failStart(ctx, sess.Id, ev.Actor, err)
return true, err
}
return true, m.deliver(ctx, sess, task, ev.Actor, text, imgs, isHandoffPrompt(ev.Actor, text))
}
func (m *Manager) readImages(ctx context.Context, ids []string) ([]image, error) {
imgs := []image{}
for _, id := range ids {
b, data, err := m.store.ReadBlob(ctx, id)
if err != nil {
return nil, err
}
imgs = append(imgs, image{mediaType: b.ContentType, data: data})
}
return imgs, nil
}
func (m *Manager) failStart(ctx context.Context, sessionID, actor string, err error) {
@@ -625,32 +678,38 @@ func transition(ctx context.Context, st *store.Store, id string, from, to api.Ag
return err
}
func (m *Manager) message(ctx context.Context, l *live, actor, text string, images []string) error {
imgs, err := m.prepareMessage(ctx, l, actor, text, images)
func (m *Manager) message(ctx context.Context, sess api.AgentSession, task api.Task, l *live, actor, text string, images []string) error {
imgs, compacting, err := m.recordMessage(ctx, sess, task, actor, text, images)
if err != nil {
return err
}
return l.sess.send(ctx, text, imgs)
m.beginTurn(l, compacting)
return m.send(ctx, l, text, imgs)
}
func (m *Manager) prepareMessage(ctx context.Context, l *live, actor, text string, images []string) ([]image, error) {
if l.implementer() && isMergeInPrompt(actor, text) {
m.takeMergeTurn(l)
func (m *Manager) send(ctx context.Context, l *live, text string, imgs []image) error {
err := l.sess.send(ctx, text, imgs)
if err != nil {
return err
}
imgs := []image{}
for _, id := range images {
b, data, err := m.store.ReadBlob(ctx, id)
if err != nil {
return nil, err
}
imgs = append(imgs, image{mediaType: b.ContentType, data: data})
return m.store.MarkSessionDelivered(ctx, l.id)
}
func (m *Manager) recordMessage(ctx context.Context, sess api.AgentSession, task api.Task, actor, text string, images []string) ([]image, bool, error) {
implementer := sess.Role == api.Implementer
if implementer && isMergeInPrompt(actor, text) {
m.ownMerge(sess.Id, task.Id, task.WorktreePath)
}
imgs, err := m.readImages(ctx, images)
if err != nil {
return nil, false, err
}
payload := map[string]any{"text": text}
if l.implementer() {
head, err := git.Head(l.dir)
if implementer {
head, err := git.Head(task.WorktreePath)
if err != nil {
return nil, err
return nil, false, err
}
payload["head"] = head
}
@@ -664,11 +723,14 @@ func (m *Manager) prepareMessage(ctx context.Context, l *live, actor, text strin
if isContinuation(actor, text) {
payload["continuation"] = true
}
err := m.emit(ctx, l, actor, "user.message", payload)
err = m.store.RecordTurnMessage(ctx, sess.Id, newEvent(sess.Id, sess.Owner, task.ProjectId, task.Id, actor, "user.message", payload))
if err != nil {
return nil, err
return nil, false, err
}
return imgs, compacting, nil
}
func (m *Manager) beginTurn(l *live, compacting bool) {
m.mu.Lock()
l.interrupted = false
l.asked = false
@@ -678,8 +740,6 @@ func (m *Manager) prepareMessage(ctx context.Context, l *live, actor, text strin
l.rateLimited = nil
l.lastText = ""
m.mu.Unlock()
return imgs, nil
}
func (m *Manager) ensureLive(ctx context.Context, sess api.AgentSession, task api.Task) (*live, error) {
@@ -1357,18 +1417,22 @@ func (m *Manager) emit(ctx context.Context, l *live, actor, kind string, payload
}
func (m *Manager) event(l *live, actor, kind string, payload map[string]any) api.Event {
return newEvent(l.id, l.owner, l.projectID, l.taskID, actor, kind, payload)
}
func newEvent(sessionID, owner, projectID, taskID, actor, kind string, payload map[string]any) api.Event {
ev := api.Event{
Kind: kind,
AgentSessionId: &l.id,
AgentSessionId: &sessionID,
Actor: actor,
Payload: payload,
}
if l.owner != "" {
ev.Owner = &l.owner
if owner != "" {
ev.Owner = &owner
return ev
}
ev.ProjectId = &l.projectID
ev.TaskId = &l.taskID
ev.ProjectId = &projectID
ev.TaskId = &taskID
return ev
}