feat(api-client): conversation + agent hooks + pure SSE streaming transport #345
No reviewers
Labels
No labels
area:auth
area:ci
area:db
area:infra
area:native
area:pwa
area:service
epic
feature
foundation
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
james/carol!345
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "344-api-client-agent-hooks"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The built-in agent's full API exists (conversations #338, streaming #340, proposal read #343, commit/reject #51) but
@carol/api-clienthad no hooks for it. This adds the data layer the chat UI (next ticket) consumes, keeping the package platform-neutral.What's in it
hooks/conversations.ts— TanStack Query hooks over the typed client, mirroringhooks/notes.ts:useConversations(+useConversationsInfinite),useConversation,useCreateConversation,useSendMessageJson(the non-streaming JSON turn — works on every platform),useResumeConversation,useProposal,useCommitProposal,useRejectProposal. Every DTO from the generated contract; cache invalidation on mutations.agent-stream.ts— a pure, React-free, platform-neutral SSE transport (streamConversationTurn) with an injectedfetchImpl/url/getAuthHeader. Noexpo/react-nativeimport — the app wiresexpo/fetch+ the runtime server URL in the chat-UI ticket. Parsesevent:/data:frames into a typedConversationStreamEventunion (mirrors the backend'sConversationEvent; can't import across the app boundary). Handles frames split across reads, malformeddata:(skip, never throw), comment/keepalive lines, CRLF framing, no-body, and non-OK HTTP (→onError, not a throw); in-banderrorevents flow throughonEvent.keys.ts—conversations+proposalskey namespaces.Verification (run locally on this branch)
typecheck✓ ·lint✓ (no disables, no non-null assertions) ·check✓ ("generated client is up to date" — no spec/codegen change)test✓ — 8 files / 40 passed, incl. 12 behavioral SSE-transport tests over a fakefetch+ReadableStream(ordered deltas, tool call/result, awaiting-confirmation, message, done, in-band error, split-frame reassembly, comment/blank-line skip, malformed-data skip, non-OK → onError)Note (potential small follow-up)
MessageDtoisn't a named component in the generated OpenAPI types —openapi-typescriptinlines it insideTurnResponseDto.messages[], so the stream event'smessagepayload is derived asTurnResponseDto["messages"][number](still contract-sourced, documented inline). NamingzMessageDtovia.openapi("MessageDto")in the backend spec would let consumers referencecomponents["schemas"]["MessageDto"]directly — out of scope here (client-only PR, no spec change), worth a tiny backend follow-up if the UI wants the named type.Out of scope
expo/fetch, runtime URL, streaming send-from-context) — chat-UI ticket.No
openapi.jsonchange. Part of epic #47; unblocks the chat UI.Closes #344
🤖 Generated with Claude Code
📊 Test coverage
Patch coverage: no testable lines changed.
Overall (
app/,lib/,db/, excluding UI per ADR-0019):Soft thresholds per ADR-0019. Coverage is informational and does not block merge.