PR: #724: feat(agents): HTTP launch for all four rail builders [Story-2842] by @ankitmaheshwari-com
View PR →

Files changed

49 file(s), +3702 / -602 lines

Path Role + -
openapi/app-api.jsonConfig+511-27
openapi/permission-map.jsonConfig+28-1
src/app/api/internal/agents/[agentKey]/route.tsApp route+88-0
src/gen/client/agentsLaunchAgent.tsGenerated contract+0-70
src/gen/client/agentsLaunchAgentUnknown.tsGenerated contract+61-0
src/gen/client/agentsLaunchAtlasDockChat.tsGenerated contract+67-0
src/gen/client/agentsLaunchAtlasIntakeReview.tsGenerated contract+70-0
src/gen/client/agentsLaunchMira.tsGenerated contract+66-0
src/gen/client/index.tsGenerated contract+4-1
src/gen/hooks/index.tsGenerated contract+16-4
src/gen/hooks/useAgentsLaunchAgent.tsGenerated contract+0-156
src/gen/hooks/useAgentsLaunchAgentUnknown.tsGenerated contract+147-0
src/gen/hooks/useAgentsLaunchAtlasDockChat.tsGenerated contract+150-0
src/gen/hooks/useAgentsLaunchAtlasIntakeReview.tsGenerated contract+152-0
src/gen/hooks/useAgentsLaunchMira.tsGenerated contract+150-0
src/gen/index.tsGenerated contract+118-29
src/gen/schemas/agentLaunchResponse.jsonGenerated contract+3-5
src/gen/schemas/atlasDockChatLaunchRequest.jsonGenerated contract+225-0
src/gen/schemas/atlasIntakeReviewLaunchRequest.jsonGenerated contract+83-0
src/gen/schemas/miraAgentLaunchRequest.jsonGenerated contract+33-9
src/gen/types/AgentLaunchResponse.tsGenerated contract+2-3
src/gen/types/AgentsLaunchAgentUnknown.tsGenerated contract+70-0
src/gen/types/AgentsLaunchAtlasDockChat.tsGenerated contract+70-0
src/gen/types/AgentsLaunchAtlasIntakeReview.tsGenerated contract+70-0
src/gen/types/{AgentsLaunchAgent.ts => AgentsLaunchMira.ts}Generated contract+18-26
src/gen/types/AtlasDockChatLaunchRequest.tsGenerated contract+32-0
src/gen/types/AtlasIntakeReviewLaunchRequest.tsGenerated contract+51-0
src/gen/types/MiraAgentLaunchRequest.tsGenerated contract+21-8
src/gen/types/index.tsGenerated contract+53-13
src/gen/zod/agentLaunchResponseSchema.tsGenerated contract+7-1
src/gen/zod/{agentsLaunchAgentSchema.ts => agentsLaunchAgentUnknownSchema.ts}Generated contract+29-37
src/gen/zod/agentsLaunchAtlasDockChatSchema.tsGenerated contract+91-0
src/gen/zod/agentsLaunchAtlasIntakeReviewSchema.tsGenerated contract+91-0
src/gen/zod/agentsLaunchMiraSchema.tsGenerated contract+91-0
src/gen/zod/atlasDockChatLaunchRequestSchema.tsGenerated contract+43-0
src/gen/zod/atlasIntakeReviewLaunchRequestSchema.tsGenerated contract+58-0
src/gen/zod/index.tsGenerated contract+45-11
src/gen/zod/miraAgentLaunchRequestSchema.tsGenerated contract+31-5
src/lib/agent/gateway.tsLibrary+35-0
src/lib/agents/__tests__/atlas-rail-store.test.tsLibrary+338-60
src/lib/agents/__tests__/atlas-rail.test.tsLibrary+11-10
src/lib/agents/atlas-rail-store.test.tsLibrary+34-5
src/lib/agents/atlas-rail-store.tsLibrary+99-44
src/lib/agents/atlas-rail.tsLibrary+19-12
src/lib/agents/mira-rail.test.tsLibrary+42-29
src/lib/agents/mira-rail.tsLibrary+45-36
src/lib/agents/rail-launch.tsLibrary+46-0
src/lib/kubb-client.tsLibrary+10-0
tests/unit/agent-launch-proxy.test.tsTest+178-0

Story-2842: App rail rewire — HTTP launch for all 4 builders; WS bidirectional for query/replay

Every agent run in the app used to LAUNCH over the WebSocket rail: the chat message rode the socket itself. RFD-AGT-013 (T1b) moves the launch onto the channel-neutral HTTP endpoint POST /app/agents/{agent_key} (pramaan-functions Story-2840/2895, merged as #777/#785) so the app and future channels share one guarded launch. This story rewires all four app launch builders — add-matter wizard intake, Agent Dock chat, Mira converse, Mira command — through the ONE shared rail-store path, while the already-open WebSocket stays bidirectional for streaming and query/replay.

Open the socket (as today) → launch via HTTP through the BFF → deltas and terminal frames keep arriving on the same socket exactly as before.

What changed, layer by layer

LayerBeforeAfter
Launch transport (src/lib/agents/atlas-rail-store.ts) socket.send(JSON.stringify(buildLaunch(…))) — the launch rode the WS await this.connect() (socket opens FIRST so the server-derived attach resolves it), then postAgentLaunch(agentKey, body) — an HTTP POST through the generated client
The four builders Built camelCase WS client messages (type:"message", agentKey, …) Build HTTP request bodies: buildIntakeReviewLaunchRequest, buildDockChatLaunchRequest (atlas-rail.ts); buildMiraConverseLaunchRequest, buildMiraCommandLaunchRequest (mira-rail.ts)
Acceptance The broker pushed a run.accepted frame Acceptance arrives on the 202 response; the store applies the SAME state transition via reduceFrame, so every downstream behaviour is unchanged
Blocked launch (consent / rate limit / breaker) run.failed frame over the socket accepted:false in the response IS the terminal truth — the store fails the rail immediately with userVisibleMessage + retryable, keeping the run id as the support reference
Streaming + replay Unchanged. The socket stays connected; the server attaches the caller's newest live socket to the run (functions Story-2896, RFD-AGT-013 D4 — the browser never learns its API-Gateway connectionId). query/replay still rides the socket (buildQueryMessage / buildDockChatQuery / buildMiraQuery untouched).

The BFF proxy (browser never talks to the gateway)

Contract notes a reviewer should know

Test evidence

pnpm vitest run src/lib/agents/__tests__/atlas-rail-store.test.ts \
  src/lib/agents/atlas-rail-store.test.ts \
  src/lib/agents/mira-rail.test.ts \
  src/lib/agents/__tests__/atlas-rail.test.ts \
  tests/unit/agent-launch-proxy.test.ts

CC-54 (refresh mid-answer loses nothing)

CC-54 is covered by the replay path, which this story deliberately does not touch: a refresh drops the socket, the store's resume(runId)/query reconnect-refetch replays the run's persisted truth (turn.completed + run.done, or run.accepted while still running), and thread hydration re-seeds committed turns from agents.chat_turn (Story-2223). The rewire changes only WHERE the launch goes; the run's truth is still persisted server-side and the same replay frames flow through the same reduceFrame. The replay tests above pin this. Manual UAT script for the human check: open a matter, ask Mira a long question, refresh mid-stream — the panel reopens, the thread hydrates, and the answer lands complete.