Fix diagram layout and clicks; tell agents to yield to image messages

This commit is contained in:
Ian Gulliver
2026-10-23 10:58:01 -07:00
parent eba85a6a1a
commit 4e20fe8a12
5 changed files with 168 additions and 49 deletions
+28 -8
View File
@@ -387,19 +387,27 @@ type midTurn struct {
taskInputs []store.TaskInput
leadInputs []store.LeadInput
responses []string
hinted int64
}
func (m *Manager) deliverMidTurn(ctx context.Context, l *live) midTurn {
if l.owner != "" {
inputs := m.takeLeadQueuedMidTurn(ctx, l)
if len(inputs) == 0 {
return midTurn{}
inputs, held := m.takeLeadQueuedMidTurn(ctx, l)
parts := []string{}
if len(inputs) > 0 {
msgs := []queued{}
for _, in := range inputs {
msgs = append(msgs, queued{actor: in.Actor, text: in.Text, sendID: in.SendID, client: in.Client})
}
parts = append(parts, m.recordQueued(ctx, l, msgs))
}
msgs := []queued{}
for _, in := range inputs {
msgs = append(msgs, queued{actor: in.Actor, text: in.Text, sendID: in.SendID, client: in.Client})
mt := midTurn{leadInputs: inputs}
if hint := m.imagesHint(l, held); hint != "" {
parts = append(parts, hint)
mt.hinted = held.id
}
return midTurn{text: m.recordQueued(ctx, l, msgs), leadInputs: inputs}
mt.text = strings.Join(parts, "\n\n")
return mt
}
unlock := m.lockDelivery(l.id)
@@ -419,7 +427,8 @@ func (m *Manager) deliverMidTurn(ctx context.Context, l *live) midTurn {
mt := midTurn{}
parts := []string{}
mt.taskInputs = m.takeQueuedMidTurn(ctx, l)
inputs, held := m.takeQueuedMidTurn(ctx, l)
mt.taskInputs = inputs
if len(mt.taskInputs) > 0 {
msgs := []queued{}
for _, in := range mt.taskInputs {
@@ -427,6 +436,10 @@ func (m *Manager) deliverMidTurn(ctx context.Context, l *live) midTurn {
}
parts = append(parts, m.recordQueued(ctx, l, msgs))
}
if hint := m.imagesHint(l, held); hint != "" {
parts = append(parts, hint)
mt.hinted = held.id
}
responses, ids := m.responsesMidTurn(ctx, l)
if responses != "" {
parts = append(parts, responses)
@@ -472,6 +485,13 @@ func (m *Manager) restoreMidTurn(ctx context.Context, l *live, mt midTurn) {
slog.Error("mark responses undelivered", "session", l.id, "err", err, "responses", mt.responses)
}
}
if mt.hinted != 0 {
m.mu.Lock()
if l.hintedImages == mt.hinted {
l.hintedImages = 0
}
m.mu.Unlock()
}
slog.Info("mid-turn delivery restored", "session", l.id, "inputs", len(mt.taskInputs)+len(mt.leadInputs), "responses", mt.responses)
}
+2
View File
@@ -122,6 +122,8 @@ type live struct {
lastText string
subagents map[string]bool
hintedImages int64
contextModel string
contextUsed int
contextWindow int
+34 -12
View File
@@ -176,12 +176,17 @@ type queued struct {
client string
}
func (m *Manager) takeQueuedMidTurn(ctx context.Context, l *live) []store.TaskInput {
type heldImages struct {
id int64
actor string
}
func (m *Manager) takeQueuedMidTurn(ctx context.Context, l *live) ([]store.TaskInput, heldImages) {
m.mu.Lock()
compacting := l.compacting
m.mu.Unlock()
if compacting {
return nil
return nil, heldImages{}
}
unlock := m.lockDelivery(queueLockKey(l.taskID))
@@ -190,34 +195,36 @@ func (m *Manager) takeQueuedMidTurn(ctx context.Context, l *live) []store.TaskIn
inputs, err := m.store.TaskInputs(ctx, l.taskID, l.member)
if err != nil {
slog.Error("list queued messages", "session", l.id, "err", err)
return nil
return nil, heldImages{}
}
out := []store.TaskInput{}
ids := []int64{}
held := heldImages{}
for _, in := range inputs {
if len(in.Images) > 0 {
held = heldImages{in.ID, in.Actor}
break
}
out = append(out, in)
ids = append(ids, in.ID)
}
if len(ids) == 0 {
return nil
return nil, held
}
err = m.store.DeleteTaskInputs(ctx, ids)
if err != nil {
slog.Error("delete queued messages", "session", l.id, "err", err)
return nil
return nil, held
}
return out
return out, held
}
func (m *Manager) takeLeadQueuedMidTurn(ctx context.Context, l *live) []store.LeadInput {
func (m *Manager) takeLeadQueuedMidTurn(ctx context.Context, l *live) ([]store.LeadInput, heldImages) {
m.mu.Lock()
compacting := l.compacting
m.mu.Unlock()
if compacting {
return nil
return nil, heldImages{}
}
m.leadMu.Lock()
@@ -226,29 +233,44 @@ func (m *Manager) takeLeadQueuedMidTurn(ctx context.Context, l *live) []store.Le
inputs, err := m.store.LeadInputs(ctx, l.owner)
if err != nil {
slog.Error("list held lead messages", "session", l.id, "err", err)
return nil
return nil, heldImages{}
}
out := []store.LeadInput{}
ids := []int64{}
held := heldImages{}
for _, in := range inputs {
if in.Kind != store.LeadMessage {
continue
}
if len(in.Images) > 0 {
held = heldImages{in.ID, in.Actor}
break
}
out = append(out, in)
ids = append(ids, in.ID)
}
if len(ids) == 0 {
return nil
return nil, held
}
err = m.store.DeleteLeadInputs(ctx, ids)
if err != nil {
slog.Error("delete held lead messages", "session", l.id, "err", err)
return nil
return nil, held
}
return out
return out, held
}
func (m *Manager) imagesHint(l *live, held heldImages) string {
if held.id == 0 {
return ""
}
m.mu.Lock()
defer m.mu.Unlock()
if l.hintedImages == held.id {
return ""
}
l.hintedImages = held.id
return fmt.Sprintf("%s sent you a message with images, which byre can only hand you as a new turn. Finish the step you're on, end this turn with a short summary, and don't start more work: they're waiting for you to read it.", held.actor)
}
func (m *Manager) recordQueued(ctx context.Context, l *live, msgs []queued) string {