feat(api): server-side agent loop + conversations/messages schema #339
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!339
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "338-agent-loop"
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?
Implements the server-side agent loop (#338, ADR-0029 §2/§4/§6), non-streaming — the piece that makes the built-in agent actually run, wiring the #51 registry + commit path and the #337 provider adapters over normalized persistence.
What's in it
025_conversations_messages(normalized per ADR-0029 §4) + entities + repositories.provider/modelsnapshotted onto the conversation from the user's #334 config at create time.runTurn/resumeTurn(lib/agent/loop.ts) with an injectableLlmClient(defaults togetLlmClientForUser; tests inject a fake — no live calls). Each round builds the request frombuildSystemPrompt()+ history +llmToolDefsFromRegistry()and callsclient.generate. Read tools execute inline →toolresult message; write tools propose-then-pause (conversation →awaiting_confirmation, proposal persisted, DB unmutated);MAX_TOOL_ROUNDS = 8guard.POST /api/agent/proposals/{id}/{commit,reject};resumeTurnthen feeds the proposal's terminal outcome back as the write tool's result and continues. The provider never sees the internalproposalId.lib/agent/tool-dispatch.tsandlib/mcp/server.tsis refactored to call it, so the MCP and built-in surfaces dispatch identically (the 14-test MCP suite is unchanged).POST /api/conversations,.../{id}/messages,.../{id}/resume, list, get) + zod DTOs + OpenAPI.409 llm_not_configuredif the user hasn't set a provider (#334). Session or PAT → 401; cross-user conversation → 404.Safety
ctx.userIdcomes only from the resolved identity, never the body/model; tools carry nouser_id; cross-user conversations 404 (don't leak).LlmError/AgentError→ clean RFC 7807 problems; the provider key never leaks.Verification (full pre-ship set)
openapi:checkup to date · coverage 116 pairs;@carol/api-clientregen + typecheck clean.Streaming is the next ticket — SSE needs an adapter
stream()that #337 deferred; this loop is non-streaming (JSON request/response). The chat UI follows that.Closes #338
🤖 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.