handleListMessages ports the legacy list handler: same filter semantics and defaults, but the standardized cursor envelope. Validation failures return the machine-branchable error envelope.
(ctx context.Context, in *ListMessagesInput)
| 448 | // and defaults, but the standardized cursor envelope. Validation failures |
| 449 | // return the machine-branchable error envelope. |
| 450 | func (s *Server) handleListMessages(ctx context.Context, in *ListMessagesInput) (*listMessagesOutput, error) { |
| 451 | ag, err := s.resolveOwnedAgent(ctx, in.Address) |
| 452 | if err != nil { |
| 453 | return nil, err |
| 454 | } |
| 455 | if s.deps.ListMessages == nil { |
| 456 | return nil, NewError(http.StatusInternalServerError, "internal_error", "message list unavailable") |
| 457 | } |
| 458 | |
| 459 | // Direction (default inbound for SDK back-compat). |
| 460 | direction := in.Direction |
| 461 | if direction == "" { |
| 462 | direction = "inbound" |
| 463 | } |
| 464 | |
| 465 | // Status default depends on direction; only meaningful for inbound. |
| 466 | status := in.Status |
| 467 | if status == "" { |
| 468 | if direction == "inbound" { |
| 469 | status = "unread" |
| 470 | } else { |
| 471 | status = "all" |
| 472 | } |
| 473 | } |
| 474 | if direction == "outbound" && status != "all" { |
| 475 | return nil, NewError(http.StatusBadRequest, "invalid_filter", |
| 476 | "status filter only applies to inbound messages — pass status=all when direction=outbound") |
| 477 | } |
| 478 | |
| 479 | // Bounded substring filters. |
| 480 | if len(in.From) > maxFilterStr { |
| 481 | return nil, NewError(http.StatusBadRequest, "invalid_filter", "from filter too long (max 200 chars)") |
| 482 | } |
| 483 | if len(in.SubjectContains) > maxFilterStr { |
| 484 | return nil, NewError(http.StatusBadRequest, "invalid_filter", "subject_contains filter too long (max 200 chars)") |
| 485 | } |
| 486 | if len(in.ConversationID) > maxFilterStr { |
| 487 | return nil, NewError(http.StatusBadRequest, "invalid_filter", "conversation_id too long (max 200 chars)") |
| 488 | } |
| 489 | if err := validateConversationID(in.ConversationID); err != nil { |
| 490 | return nil, NewError(http.StatusBadRequest, "invalid_filter", err.Error()) |
| 491 | } |
| 492 | |
| 493 | // Labels filter: validate + dedup (read access allows the e2a: system |
| 494 | // namespace, matching legacy allowSystemPrefix=true). |
| 495 | labelsFilter, err := normalizeLabelFilter(in.Labels) |
| 496 | if err != nil { |
| 497 | return nil, NewError(http.StatusBadRequest, "invalid_filter", err.Error()) |
| 498 | } |
| 499 | |
| 500 | // Time range. |
| 501 | since, err := parseRFC3339Filter(in.Since, "since") |
| 502 | if err != nil { |
| 503 | return nil, err |
| 504 | } |
| 505 | until, err := parseRFC3339Filter(in.Until, "until") |
| 506 | if err != nil { |
| 507 | return nil, err |
nothing calls this directly
no test coverage detected