AI Agent Routes
Base URL: https://agent.insiders.bot
Env var: VITE_AGENT_API_URL
Frontend client: src/components/AiAgent/agentApi.ts
Server code: not available locally — deployed separately
The agent uses SSE (Server-Sent Events) for streaming responses — POST /api/agent/chat returns text/event-stream, not JSON. Auth is the same user JWT (Authorization: Bearer <token>) as the main API.
Chat · /api/agent/chat
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/agent/chat | 🔒 | Send a message, stream response via SSE |
Request body
{
conversation_id: string // UUID — generate with newConversationId()
message: string
byok_provider?: "claude" | "deepseek" | null
proposed_action?: ProposedAction | null // set when user confirms a recommendation
}ProposedAction shape
{
tool: "place_order" | "close_position" | "register_copy_trade"
condition_id?: string
side?: "YES" | "NO"
usd_amount?: number // for place_order
fraction?: number // for close_position (1.0 = full close)
target_wallet?: string // for register_copy_trade
market_title?: string
market_slug?: string
}SSE event stream
Each event is event: <type>\ndata: <json>\n\n. Events in order:
| Event | Data | Description |
|---|---|---|
conversation | { conversation_id } | Echoes the conversation ID |
thinking | { text } | Agent's internal reasoning (shown as thinking indicator) |
token | { text } | Streaming response token |
tool_call | { tool, args, call_id } | Agent called a tool |
tool_progress | { call_id, phase, text, tool?, stage_id? } | Tool running / success / error |
tool_result | { call_id, tool?, summary, elapsed_ms, error?, error_user_msg? } | Tool finished |
entities_update | Record<string, unknown> | Market/wallet entities extracted from context |
done | { recommendation?, inspector? } | Final event — contains action recommendation if any |
error | { message, error_code? } | Stream-level error |
done — recommendation types
The agent may emit a recommendation on the final done event. The frontend renders this as an action card.
trade — buy or close a position:
{
type: "trade"
trade: {
action?: "place_order" | "close_position"
condition_id: string
side: "YES" | "NO"
suggested_usd?: number
usd_amount?: number
fraction?: number // for close_position
market_title?: string
current_price?: number
}
}redirect — navigate to alternative markets/wallets:
{
type: "redirect"
redirect?: {
alternatives: Array<{
condition_id?: string
title?: string
slug?: string
kind?: "market" | "wallet"
label?: string
ref?: string
}>
}
}watch — add market to watchlist:
{ type: "watch"; market: { condition_id: string; title: string } }no_op — no action:
{ type: "no_op"; reasoning?: string }Cancel turn · /api/agent/conversations/:id/turns/:index/cancel
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/agent/conversations/:conversationId/turns/:turnIndex/cancel | 🔒 | Abort an in-progress agent turn |
Conversation history
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/agent/conversations | 🔒 | List conversations ({ conversations: ConversationSummary[] }) |
| GET | /api/agent/conversations/:id | 🔒 | Full conversation with messages + events |
| DELETE | /api/agent/conversations/:id | 🔒 | Delete a conversation |
ConversationSummary
{
conversation_id: string
last_at: string // ISO timestamp
msg_count: number
title: string
}Trade confirmation flow
When the user clicks Confirm on a recommendation card, the frontend:
- Calls
synthesizeConfirmationMessage(rec)to build a plain-English message - Sends a new
POST /api/agent/chatwith the message andproposed_action - The agent's
intent_completenessguard re-validates that the message names every disambiguator (side, amount/fraction, market reference)
This contract is mirrored in polyinsiders-backend-v1/Tgbot/services/bot.ts → synthesizeConfirmationMessage. If you change one, change both.