feat(api): GET /api/agent/proposals/{id} — read a proposed agent write #342

Closed
opened 2026-06-29 02:02:31 +00:00 by james · 0 comments
Owner

The built-in agent's write tools never mutate — they persist a ProposedChange (summary + structured diff + safe/destructive classification) as an agent_proposals row, and the conversation's awaiting_confirmation pause (and the persisted assistant message) carry only the proposalId. There is currently no way to read that proposal back, so a client can't render what's about to change before committing/rejecting it. The commit/reject endpoints (#51) exist; the read side is the missing piece.

This blocks the chat-UI confirmation card (epic #47: "the PWA renders a diff and applies on confirm") and is also what an MCP client would use to show a confirmation prompt. API-first: land the endpoint before the @carol/api-client hooks + chat UI consume it.

Scope

  • GET /api/agent/proposals/{id} — returns a ProposalDto: proposalId, action (safe_create|safe_update|destructive_update|destructive_delete), tool, entity{type,id}, summary, diff, status, createdAt, expiresAt. The original tool input stays server-side (apply-time replay detail).
  • Per-user scoped: a proposal owned by another user, or missing, is a 404 (don't leak existence) — same discipline as commit/reject.
  • A still-pending proposal past its expiry reads as expired, computed read-only (a GET must not mutate; the commit path keeps its lazy expired persist).
  • zod DTO, RFC 7807 errors, registered in the generated OpenAPI spec (committed openapi.json).

Out of scope

  • The @carol/api-client hook for this endpoint (lands with the conversation hooks).
  • The chat UI.

Part of epic #47; unblocks the chat-UI confirmation flow.

The built-in agent's write tools never mutate — they persist a `ProposedChange` (summary + structured diff + safe/destructive classification) as an `agent_proposals` row, and the conversation's `awaiting_confirmation` pause (and the persisted assistant message) carry only the `proposalId`. There is currently **no way to read that proposal back**, so a client can't render *what's about to change* before committing/rejecting it. The commit/reject endpoints (#51) exist; the read side is the missing piece. This blocks the chat-UI confirmation card (epic #47: "the PWA renders a diff and applies on confirm") and is also what an MCP client would use to show a confirmation prompt. API-first: land the endpoint before the `@carol/api-client` hooks + chat UI consume it. ## Scope - `GET /api/agent/proposals/{id}` — returns a `ProposalDto`: `proposalId`, `action` (`safe_create|safe_update|destructive_update|destructive_delete`), `tool`, `entity{type,id}`, `summary`, `diff`, `status`, `createdAt`, `expiresAt`. The original tool `input` stays server-side (apply-time replay detail). - Per-user scoped: a proposal owned by another user, or missing, is a **404** (don't leak existence) — same discipline as commit/reject. - A still-`pending` proposal past its expiry reads as `expired`, computed read-only (a GET must not mutate; the commit path keeps its lazy `expired` persist). - zod DTO, RFC 7807 errors, registered in the generated OpenAPI spec (committed `openapi.json`). ## Out of scope - The `@carol/api-client` hook for this endpoint (lands with the conversation hooks). - The chat UI. Part of epic #47; unblocks the chat-UI confirmation flow.
james closed this issue 2026-06-29 02:17:54 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
james/carol#342
No description provided.