fix(agent): handle partial room timestamps without crashing

This commit is contained in:
2026-10-25 10:15:35 -07:00
parent 1b74acbc2b
commit d4f8710ee8
7 changed files with 213 additions and 25 deletions
+19
View File
@@ -605,6 +605,25 @@ durable entries and can advance without visible messages. Use returned cursors;
do not derive them from message IDs. `limit` bounds source entries, not displayed
messages.
Every room message, including protected tool rows and `activity.queuedMessages`,
has a string `id`, `role`, `content`, and ISO-8601 `createdAt`. Privacy filtering
retains this envelope and tool lifecycle metadata (`kind`, `toolCallId`,
`toolName`, `entryId`, `status`, and available `startedAt`/`completedAt`). Protected
tools use their public tool name as `content`; private arguments, previews, and
errors are withheld. Result-only pages may omit initiation metadata.
Historical entries without a known time use `1970-01-01T00:00:00.000Z` as an
unknown-time sentinel. For compatibility with older partial responses, clients
may recover missing/invalid `createdAt` from `metadata.startedAt`, then
`metadata.completedAt`, or use that sentinel; never substitute the current time.
Older redacted tool rows containing only `id`, `role`, and `name` can retain their
identity with a tool-name label until a complete upsert arrives. The web client
uses the same normalization for REST, WebSocket, and queued activity, retains
known times across partial upserts, and orders by time, numeric `entryId` (when
present, before absent), then stable `id`. Unknown times display as “Earlier”
without a clock time. Invalid identities or non-tool message bodies reject the
page before advancing its replay cursor; timestamps never replace cursors.
### `POST /v1/agent/messages`
Body: `{ "content": "Check the oven", "requestId": "client-generated-uuid" }`.