#region AgentChat.Research [C:3] [TYPE ADR] [SEMANTICS research,agent-chat,langgraph,gradio]
@BRIEF Research — LangGraph agent with interrupt/resume, PostgresSaver, structured metadata. Final architecture choices.

## 1. Agent Framework: LangGraph `create_react_agent()`

**Decision**: `create_react_agent(model, tools, checkpointer=PostgresSaver)` from `langgraph.prebuilt`. Uses `interrupt()` for HITL, `Command(resume=...)` for resume.
**Rationale**: Native HITL support, PostgresSaver checkpointer, `astream_events()` for structured streaming.
**Rejected**: LangChain AgentExecutor — no native interrupt/checkpointer, more boilerplate.

## 2. Tools: Native `@tool`

**Decision**: `@tool`-decorated functions with Pydantic `args_schema`. Each tool calls FastAPI REST via HTTP with user JWT.
**Rationale**: Single source of metadata. Auto OpenAI function schema. Old `@assistant_tool` → `@DEPRECATED`.
**Rejected**: Dual registration (`@assistant_tool` + `StructuredTool`) — redundant.

## 3. Confirmation: `interrupt_before=DANGEROUS_TOOLS`

**Decision**: LangGraph native `interrupt_before=DANGEROUS_TOOLS` + `Command(resume=...)`. No custom `HumanInTheLoopMiddleware` — LangGraph provides HITL natively via checkpointing and resume. Second `submit()` with `additional_inputs[1]="confirm"/"deny"` triggers resume.
**Rationale**: Zero REST endpoints. LangGraph checkpoint ensures safe resume.
**Rejected**: REST confirmation endpoints + polling — more code, more latency. Custom middleware — LangGraph has native interrupt support.

## 4. History: `RunnableWithMessageHistory`

**Decision**: LangChain auto-loads/saves message history. Svelte does NOT pass `history` — only `conversation_id`.
**Rationale**: Eliminates manual history management on frontend and backend.
**Rejected**: Manual `history` parameter in `submit()` — fragile.

## 5. Checkpoints: `PostgresSaver`

**Decision**: PostgreSQL via `langgraph-checkpoint-postgres`. Same instance as FastAPI. Survives all container restarts. Thread ID = conversation_id.
**Rationale**: Agent state persists across container restarts.
**Rejected**: In-memory only — lost on restart.

## 6. Gradio Native Features

**Decision**: `additional_inputs`, `gr.Request`, `concurrency_limit=1`, `examples`. All used.
**Rationale**: Eliminates custom code for conversation_id passing, JWT extraction, message queuing, welcome chips.
**Rejected**: Custom headers, manual queues — redundant.

## 7. Tool Execution: HTTP to FastAPI

**Decision**: All `@tool` functions call FastAPI REST with user JWT (not service JWT).
**Rationale**: RBAC enforced by FastAPI under user identity. Service JWT only for bootstrap.
**Rejected**: Direct import — Gradio has no DB connection.

## 8. Streaming: Structured JSON Metadata

**Decision**: LangGraph events yield `ChatMessage` objects with `metadata.type` (stream_token, tool_start, tool_end, tool_error, confirm_required, confirm_resolved, error). Frontend parses structured JSON from `event.data`, not emoji strings.
**Rationale**: Structured metadata is deterministic, typed, and localizable. Emoji string parsing is brittle and collides with model output.
**Rejected**: Plain-text prefix protocol — rejected because emoji prefix parsing is fragile, hard to localize, and error-prone.

#endregion AgentChat.Research
