(ctx context.Context, in *ListConversationsInput)
| 109 | } |
| 110 | |
| 111 | func (s *Server) handleListConversations(ctx context.Context, in *ListConversationsInput) (*listConversationsOutput, error) { |
| 112 | ag, err := s.resolveOwnedAgent(ctx, in.Address) |
| 113 | if err != nil { |
| 114 | return nil, err |
| 115 | } |
| 116 | if s.deps.ListConversations == nil { |
| 117 | return nil, NewError(http.StatusInternalServerError, "internal_error", "conversation list unavailable") |
| 118 | } |
| 119 | since, err := parseRFC3339Filter(in.Since, "since") |
| 120 | if err != nil { |
| 121 | return nil, err |
| 122 | } |
| 123 | until, err := parseRFC3339Filter(in.Until, "until") |
| 124 | if err != nil { |
| 125 | return nil, err |
| 126 | } |
| 127 | if !since.IsZero() && !until.IsZero() && !since.Before(until) { |
| 128 | return nil, NewError(http.StatusBadRequest, "invalid_filter", "since must be earlier than until") |
| 129 | } |
| 130 | // Decode + validate the cursor against the current filter identity (CV-3). |
| 131 | var afterTime time.Time |
| 132 | var afterID string |
| 133 | if in.Cursor != "" { |
| 134 | var cur conversationsCursor |
| 135 | if err := DecodeCursor([]string{s.deps.CursorSecret}, in.Cursor, &cur); err != nil { |
| 136 | return nil, NewError(http.StatusBadRequest, "invalid_cursor", "invalid pagination cursor") |
| 137 | } |
| 138 | if cur.AgentID != ag.ID || cur.Since != rfc3339OrEmpty(since) || cur.Until != rfc3339OrEmpty(until) { |
| 139 | return nil, NewError(http.StatusBadRequest, "invalid_cursor", |
| 140 | "cursor was created with different filters — start a new query without a cursor") |
| 141 | } |
| 142 | afterTime = cur.LastMessageAt |
| 143 | afterID = cur.ConversationID |
| 144 | } |
| 145 | limit := in.Limit |
| 146 | if limit <= 0 { |
| 147 | limit = 100 |
| 148 | } |
| 149 | // Fetch limit+1 to detect a further page. |
| 150 | convos, err := s.deps.ListConversations(ctx, identity.ConversationListFilter{ |
| 151 | AgentID: ag.ID, |
| 152 | Limit: limit + 1, |
| 153 | Since: since, |
| 154 | Until: until, |
| 155 | AfterLastMessageAt: afterTime, |
| 156 | AfterConversationID: afterID, |
| 157 | }) |
| 158 | if err != nil { |
| 159 | return nil, NewError(http.StatusInternalServerError, "internal_error", "failed to fetch conversations") |
| 160 | } |
| 161 | hasMore := len(convos) > limit |
| 162 | if hasMore { |
| 163 | convos = convos[:limit] |
| 164 | } |
| 165 | items := make([]ConversationSummaryView, len(convos)) |
| 166 | for i, c := range convos { |
| 167 | items[i] = conversationSummaryView(c) |
| 168 | } |
nothing calls this directly
no test coverage detected