Codex mid-turn delivery deletes input before the steer is accepted

This commit is contained in:
Ian Gulliver
2026-10-16 14:18:40 -07:00
parent a0e6a3953f
commit 8c7c3bee2e
9 changed files with 118 additions and 33 deletions
+58 -18
View File
@@ -350,13 +350,24 @@ func (m *Manager) Deliver(ctx context.Context, sessionID string) error {
return m.store.MarkDelivered(ctx, ids)
}
func (m *Manager) deliverMidTurn(ctx context.Context, l *live) string {
type midTurn struct {
text string
taskInputs []store.TaskInput
leadInputs []store.LeadInput
responses []string
}
func (m *Manager) deliverMidTurn(ctx context.Context, l *live) midTurn {
if l.owner != "" {
msgs := m.takeLeadQueuedMidTurn(ctx, l)
if len(msgs) == 0 {
return ""
inputs := m.takeLeadQueuedMidTurn(ctx, l)
if len(inputs) == 0 {
return midTurn{}
}
return m.recordQueued(ctx, l, msgs)
msgs := []queued{}
for _, in := range inputs {
msgs = append(msgs, queued{actor: in.Actor, text: in.Text})
}
return midTurn{text: m.recordQueued(ctx, l, msgs), leadInputs: inputs}
}
unlock := m.lockDelivery(l.id)
@@ -365,49 +376,78 @@ func (m *Manager) deliverMidTurn(ctx context.Context, l *live) string {
discard, err := m.store.GetDiscard(ctx, l.taskID)
if err != nil {
slog.Error("read discard state", "task", l.taskID, "err", err)
return ""
return midTurn{}
}
if discard.State != store.DiscardNone {
return ""
return midTurn{}
}
mt := midTurn{}
parts := []string{}
msgs := m.takeQueuedMidTurn(ctx, l)
if len(msgs) > 0 {
mt.taskInputs = m.takeQueuedMidTurn(ctx, l)
if len(mt.taskInputs) > 0 {
msgs := []queued{}
for _, in := range mt.taskInputs {
msgs = append(msgs, queued{actor: in.Actor, text: in.Text})
}
parts = append(parts, m.recordQueued(ctx, l, msgs))
}
responses := m.responsesMidTurn(ctx, l)
responses, ids := m.responsesMidTurn(ctx, l)
if responses != "" {
parts = append(parts, responses)
mt.responses = ids
}
return strings.Join(parts, "\n\n")
mt.text = strings.Join(parts, "\n\n")
return mt
}
func (m *Manager) responsesMidTurn(ctx context.Context, l *live) string {
func (m *Manager) restoreMidTurn(ctx context.Context, l *live, mt midTurn) {
if len(mt.taskInputs) > 0 {
err := m.store.RestoreTaskInputs(ctx, mt.taskInputs)
if err != nil {
slog.Error("restore queued messages", "session", l.id, "err", err)
}
}
if len(mt.leadInputs) > 0 {
err := m.store.RestoreLeadInputs(ctx, mt.leadInputs)
if err != nil {
slog.Error("restore held lead messages", "session", l.id, "err", err)
}
}
if len(mt.responses) > 0 {
err := m.store.MarkUndelivered(ctx, mt.responses)
if err != nil {
slog.Error("mark responses undelivered", "session", l.id, "err", err, "responses", mt.responses)
}
}
slog.Info("mid-turn delivery restored", "session", l.id, "inputs", len(mt.taskInputs)+len(mt.leadInputs), "responses", mt.responses)
}
func (m *Manager) responsesMidTurn(ctx context.Context, l *live) (string, []string) {
pending, err := m.store.Undelivered(ctx, l.id)
if err != nil {
slog.Error("list undelivered responses", "session", l.id, "err", err)
return ""
return "", nil
}
if len(pending) == 0 {
return ""
return "", nil
}
ids := responseIDs(pending)
waiting, err := m.store.AwaitingHuman(ctx, l.id)
if err != nil {
slog.Error("count waiting questions", "session", l.id, "err", err)
return ""
return "", nil
}
if waiting > 0 {
slog.Info("deliver held mid-turn: question waiting", "session", l.id, "waiting", waiting, "responses", ids)
return ""
return "", nil
}
err = m.store.MarkDelivered(ctx, ids)
if err != nil {
slog.Error("mark responses delivered", "session", l.id, "err", err, "responses", ids)
return ""
return "", nil
}
text := deliveryMessage(pending)
@@ -416,7 +456,7 @@ func (m *Manager) responsesMidTurn(ctx context.Context, l *live) string {
slog.Error("record mid-turn delivery", "session", l.id, "err", err)
}
slog.Info("deliver sent mid-turn", "session", l.id, "responses", ids)
return text
return text, ids
}
func responseIDs(pending []store.Pending) []string {