From 259ec5a8bfa5dd666bd5aa676fa9a481d0a4e59f Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Wed, 21 Oct 2026 12:55:55 -0700 Subject: [PATCH] Make answering own the inbox state transition --- internal/agents/fork.go | 2 +- internal/agents/respond.go | 31 ++++++++++++++----------------- internal/store/inbox.go | 12 +++--------- 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/internal/agents/fork.go b/internal/agents/fork.go index 769be2d..99ec7e4 100644 --- a/internal/agents/fork.go +++ b/internal/agents/fork.go @@ -103,7 +103,7 @@ func (m *Manager) AnswerAgain(ctx context.Context, creator api.User, create api. } open := item open.State = api.Open - _, err = CheckResponse(open, create) + err = checkResponse(open, create) if err != nil { return api.Task{}, err } diff --git a/internal/agents/respond.go b/internal/agents/respond.go index cf32e55..e204083 100644 --- a/internal/agents/respond.go +++ b/internal/agents/respond.go @@ -19,7 +19,7 @@ func (m *Manager) Respond(ctx context.Context, user string, creates []api.InboxR return nil, fmt.Errorf("%w: responses is empty", ErrInvalid) } - resolutions := []store.Resolution{} + responses := []api.InboxResponse{} sessions := []string{} seen := map[string]bool{} @@ -34,21 +34,18 @@ func (m *Manager) Respond(ctx context.Context, user string, creates []api.InboxR return nil, err } - state, err := CheckResponse(item, create) + err = checkResponse(item, create) if err != nil { return nil, err } - resolutions = append(resolutions, store.Resolution{ - State: state, - Response: newResponse(item.Id, user, create), - }) + responses = append(responses, newResponse(item.Id, user, create)) if !slices.Contains(sessions, item.AgentSessionId) { sessions = append(sessions, item.AgentSessionId) } } - err := m.store.Respond(ctx, user, resolutions) + err := m.store.Respond(ctx, user, responses) if errors.Is(err, store.ErrNotOpen) { return nil, fmt.Errorf("%w: %w", ErrConflict, err) } @@ -56,8 +53,8 @@ func (m *Manager) Respond(ctx context.Context, user string, creates []api.InboxR slog.Error("record inbox responses", "user", user, "err", err) return nil, err } - for _, res := range resolutions { - slog.Info("inbox response recorded", "item", res.Response.ItemId, "response", res.Response.Id, "type", res.Response.Type, "user", user) + for _, resp := range responses { + slog.Info("inbox response recorded", "item", resp.ItemId, "response", resp.Id, "type", resp.Type, "user", user) } for _, id := range sessions { @@ -70,8 +67,8 @@ func (m *Manager) Respond(ctx context.Context, user string, creates []api.InboxR } items := []api.InboxItem{} - for _, res := range resolutions { - item, err := m.store.GetInboxItem(ctx, res.Response.ItemId) + for _, resp := range responses { + item, err := m.store.GetInboxItem(ctx, resp.ItemId) if err != nil { return nil, err } @@ -126,9 +123,9 @@ func (m *Manager) Dismiss(ctx context.Context, user, itemID string) (api.InboxIt return m.store.GetInboxItem(ctx, itemID) } -func CheckResponse(item api.InboxItem, c api.InboxResponseCreate) (api.InboxState, error) { +func checkResponse(item api.InboxItem, c api.InboxResponseCreate) error { if item.State != api.Open { - return "", fmt.Errorf("%w: item %s is %s", ErrConflict, item.Id, item.State) + return fmt.Errorf("%w: item %s is %s", ErrConflict, item.Id, item.State) } text := "" @@ -136,8 +133,8 @@ func CheckResponse(item api.InboxItem, c api.InboxResponseCreate) (api.InboxStat text = strings.TrimSpace(*c.Text) } images := c.Images != nil && len(*c.Images) > 0 - bad := func(format string, args ...any) (api.InboxState, error) { - return "", fmt.Errorf("%w: item %s: "+format, append([]any{ErrInvalid, item.Id}, args...)...) + bad := func(format string, args ...any) error { + return fmt.Errorf("%w: item %s: "+format, append([]any{ErrInvalid, item.Id}, args...)...) } switch c.Type { @@ -158,13 +155,13 @@ func CheckResponse(item api.InboxItem, c api.InboxResponseCreate) (api.InboxStat } seen[o] = true } - return api.Answered, nil + return nil case api.Other: if text == "" && !images { return bad("other needs text or images") } - return api.Answered, nil + return nil } return bad("unknown response type %q", c.Type) diff --git a/internal/store/inbox.go b/internal/store/inbox.go index 891b188..8cee512 100644 --- a/internal/store/inbox.go +++ b/internal/store/inbox.go @@ -111,17 +111,11 @@ func (s *Store) ListInbox(ctx context.Context, f InboxFilter) ([]api.InboxItem, return items, nil } -type Resolution struct { - Response api.InboxResponse - State api.InboxState -} - -func (s *Store) Respond(ctx context.Context, actor string, resolutions []Resolution) error { +func (s *Store) Respond(ctx context.Context, actor string, responses []api.InboxResponse) error { return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error { - for _, r := range resolutions { - resp := r.Response + for _, resp := range responses { res, err := tx.ExecContext(ctx, `UPDATE inbox_items SET state = ?, resolved_by = ?, resolved_at = ? WHERE id = ? AND state = ?`, - string(r.State), actor, now(), resp.ItemId, string(api.Open)) + string(api.Answered), actor, now(), resp.ItemId, string(api.Open)) if err != nil { return err }