Ephemeral User Agents — Frontend Integration Handoff¶
Feature: FEAT-149
Date: 2026-05-07
Audience: navigator-frontend-next team
Purpose: Input document for /sdd-proposal — paste as the Problem Statement and Constraints & Requirements body.
1. Context¶
What Is an Ephemeral Agent?¶
An ephemeral user agent is a personal AI assistant that lives entirely in memory for the duration of a user session. Unlike the regular bot-creation flow — which writes a database row before the bot is usable — an ephemeral agent is created instantly in memory, warmed up in the background, and only persisted to the database if the user explicitly decides to "save" it.
This means a user can try out a custom AI assistant with specific tools, MCP servers, and uploaded documents without committing to a permanent bot. If they like it, one click saves it. If not, it simply disappears (or expires after 24 hours).
The Memory-First Lifecycle¶
The lifecycle has four named phases:
| Phase | Meaning |
|---|---|
creating |
The server received the request and scheduled the warm-up. |
warming |
Background setup is running: tool sync, MCP handshake, RAG index build. |
ready |
The bot is fully operational and can receive chat messages. |
error |
Setup failed. The error field contains a human-readable description. |
The POST that creates an ephemeral agent returns 201 immediately with a
chatbot_id and status: "creating". The client then polls the status
endpoint until status reaches "ready" (or "error").
Why Memory-First?¶
Building a RAG index, connecting to external MCP servers, and loading tool configurations can take several seconds. Blocking the user on all of this during creation would feel slow. The memory-first approach lets the frontend show a progress indicator ("Setting up your agent…") while warm-up runs in the background.
How It Differs from the Existing Bot Flow¶
Existing flow (PUT /api/v1/user_agents) |
New ephemeral flow (POST /api/v1/agents/user/) |
|
|---|---|---|
| DB row created? | Immediately on PUT | Only when the user promotes via PUT |
| Bot ready? | Lazily on first chat | After polling status == "ready" |
| Can be discarded? | Only via DELETE | Yes, before and after promotion |
| TTL | No automatic expiry | 24 h (configurable server-side) |
| RAG store | pgvector collection | FAISS in-process (S3 on promote) |
Once an ephemeral agent is promoted via PUT /api/v1/agents/user/{chatbot_id},
it becomes a regular persisted bot accessible at the same chatbot_id through
the existing chat endpoint (POST /api/v1/agents/chat/{chatbot_id}). No
client-side identity change is needed.
2. End-to-End UI Flow¶
The following numbered steps describe the screens and state transitions the user goes through. This is a product-level description, not a wireframe spec.
-
Open the "New Agent" dialog — User taps "Create Agent". The UI shows a form with tabs: General, Tools, MCP Servers, Documents, RAG Mode.
-
Fill in General settings — Name, description, system prompt, LLM provider (e.g.
google), model name (e.g.gemini-2.0-flash), operation mode (adaptive/tool_only/ etc.). -
Pick tools — The UI fetches
GET /api/v1/tools/catalogand displays a searchable list of available tools. User checks the ones they want. -
Configure MCP servers (optional) — User adds HTTP MCP server URLs with optional auth. Each entry is a
{type, url, ...}JSON object. -
Upload documents (optional) — User drags in PDFs or text files. Up to 50 MB per file (default; env-configurable). The UI selects a RAG mode:
"vector"— FAISS vector store (default for uploaded PDFs).-
"pageindex"— Page-level index for large structured documents. -
Submit — User clicks Create. The UI posts to
POST /api/v1/agents/user/as multipart with aconfigJSON part and optionalfiles[]parts. The server responds201with{chatbot_id, status: "creating"}. -
Show warm-up progress — The UI polls
GET /api/v1/agents/user/{chatbot_id}/statusat decreasing intervals (see §4). It renders a per-subsystem progress bar: tools— "Syncing tools…" / "Ready"mcp— "Validating MCP servers…" / "Ready" / "Skipped"-
rag— "Building index…" / "Ready" / "Skipped" -
Ready to chat — When
phase == "ready", unlock the chat input. The user sends messages toPOST /api/v1/agents/chat/{chatbot_id}exactly as they would with any other agent (no API change there). -
Save (optional) — A persistent "Save Agent" button is available while the agent is ephemeral. Clicking it calls
PUT /api/v1/agents/user/{chatbot_id}to promote it to the database. After promotion, the agent continues to exist at the samechatbot_id. -
Discard (optional) — A "Discard" button (or closing the session) calls
DELETE /api/v1/agents/user/{chatbot_id}. Uploaded documents are deleted from S3. If the user does nothing, the agent expires after 24 h.
3. HTTP Endpoints¶
Auth¶
All five FEAT-149 endpoints require an authenticated session (session cookie).
The user_id is resolved server-side from the session — the client never
sends it explicitly.
Error envelope (used for all 4xx/5xx responses):
POST /api/v1/agents/user/ — Create ephemeral agent¶
Auth: session required
Content-Type: multipart/form-data (recommended when uploading files) or application/json
Request — multipart/form-data¶
POST /api/v1/agents/user/
Content-Type: multipart/form-data; boundary=---boundary
-----boundary
Content-Disposition: form-data; name="config"
Content-Type: application/json
{
"name": "trial-bot",
"description": "My experimental assistant",
"llm": "google",
"model_config": {"model": "gemini-2.0-flash"},
"system_prompt_template": "You are a helpful assistant.",
"tools_config_plain": ["weather", "ddgo"],
"mcp_config_plain": [
{
"type": "http",
"url": "https://my-mcp-server.example.com",
"name": "my-tools"
}
],
"use_vector": true,
"vector_config": {"rag_mode": "vector"}
}
-----boundary
Content-Disposition: form-data; name="files[]"; filename="manual.pdf"
Content-Type: application/pdf
<binary PDF data>
-----boundary--
Request — application/json (no files)¶
{
"name": "trial-bot",
"description": "My experimental assistant",
"llm": "google",
"model_config": {"model": "gemini-2.0-flash"},
"system_prompt_template": "You are a helpful assistant.",
"tools_config_plain": ["weather"],
"mcp_config_plain": [],
"use_vector": false,
"vector_config": {}
}
Config field reference¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | yes | — | Display name for the agent. |
description |
string | no | "" |
Short description. |
llm |
string | no | "google" |
LLM provider slug (google, openai, anthropic, etc.). |
model_config |
object | no | {} |
Provider-specific model params (e.g. {"model": "gemini-2.0-flash"}). |
system_prompt_template |
string | no | "" |
System prompt. Supports {variable} placeholders. |
human_prompt_template |
string | no | null |
Optional human-turn template. |
tools_config_plain |
array of strings | no | [] |
Tool slugs from the catalog (e.g. ["weather", "ddgo"]). |
mcp_config_plain |
array of objects | no | [] |
MCP server configs (see §7). |
use_vector |
bool | no | false |
Whether to build a RAG index over uploaded files. |
vector_config |
object | no | {} |
RAG config. Must include rag_mode when use_vector is true. |
vector_config.rag_mode |
"vector" | "pageindex" |
when use_vector |
"vector" |
RAG strategy. |
operation_mode |
string | no | "adaptive" |
Agent reasoning mode. |
language |
string | no | "en" |
Response language hint. |
max_context_turns |
int | no | 5 |
Number of prior turns to include in context. |
Responses¶
201 Created — Agent created, warm-up scheduled:
400 Bad Request — Required field missing:
401 Unauthorized — No valid session:
503 Service Unavailable — BotManager not ready:
500 Internal Server Error:
Idempotency: NOT idempotent. Each call creates a new chatbot_id.
GET /api/v1/agents/user/{chatbot_id}/status — Warm-up polling¶
Auth: session required
Content-Type: none
Path parameters¶
| Parameter | Type | Description |
|---|---|---|
chatbot_id |
UUID string | The chatbot_id returned by POST. |
Responses¶
200 OK — Status snapshot:
{
"chatbot_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"phase": "warming",
"progress": {
"tools": "ready",
"mcp": "validating",
"rag": "building"
},
"error": null
}
When phase == "error":
{
"chatbot_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"phase": "error",
"progress": {
"tools": "ready",
"mcp": "ready",
"rag": "building"
},
"error": "FAISS index build failed: out of memory"
}
401 Unauthorized:
404 Not Found — Not in registry (wrong user, expired, or not yet created):
Idempotency: Safe to call repeatedly. Has no side effects.
PUT /api/v1/agents/user/{chatbot_id} — Promote to persistent¶
Auth: session required
Content-Type: none (no request body)
Promotes the ephemeral agent to a persistent database row. The agent keeps its
chatbot_id and becomes accessible through the normal user-bot resolution
path. The ephemeral registry entry is removed.
Path parameters¶
| Parameter | Type | Description |
|---|---|---|
chatbot_id |
UUID string | The ephemeral agent to promote. |
Responses¶
200 OK — Promoted successfully. Returns the persisted bot model:
{
"chatbot_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"user_id": 42,
"name": "trial-bot",
"description": "My experimental assistant",
"llm": "google",
"enabled": true,
"use_vector": true,
"vector_config": {
"rag_mode": "vector",
"faiss_persist_path": "faiss/3fa85f64-5717-4562-b3fc-2c963f66afa6.faiss"
},
"documents": [
{
"name": "manual.pdf",
"path": "users_bots/42/3fa85f64.../manual.pdf",
"url": "https://...",
"size": 204800
}
],
"created_at": "2026-05-07T12:34:56",
"updated_at": "2026-05-07T12:35:10"
}
400 Bad Request — Missing path parameter:
401 Unauthorized:
404 Not Found — Not in ephemeral registry:
409 Conflict — Agent is not ready yet, or already promoted:
500 Internal Server Error:
Idempotency: NOT idempotent. Second call after successful promotion
returns 409 (agent no longer in the ephemeral registry).
DELETE /api/v1/agents/user/{chatbot_id} — Discard ephemeral¶
Auth: session required
Content-Type: none
Discards an ephemeral agent. Removes it from the in-memory registry and deletes any uploaded documents from S3 (best-effort).
If the agent has already been promoted (it is no longer ephemeral), this
endpoint returns 404. Use the existing DELETE /api/v1/user_agents/{chatbot_id}
to delete promoted bots.
Path parameters¶
| Parameter | Type | Description |
|---|---|---|
chatbot_id |
UUID string | The ephemeral agent to discard. |
Responses¶
204 No Content — Discarded successfully (no body).
400 Bad Request:
401 Unauthorized:
404 Not Found — Not in ephemeral registry (may already be promoted):
Idempotency: Safe to call once; a second call returns 404.
GET /api/v1/tools/catalog — Tool catalog¶
Auth: session required
Content-Type: none
Returns the list of all available tools. The frontend uses this to populate the tool-picker when creating an ephemeral agent.
Responses¶
200 OK — Tool catalog array sorted by slug:
[
{
"slug": "ddgo",
"dotted_path": "parrot_tools.ddgo.DuckDuckGoToolkit",
"description": "DuckDuckGo search toolkit.",
"category": "search"
},
{
"slug": "weather",
"dotted_path": "parrot_tools.weather.WeatherTool",
"description": "Get the current weather for a location."
},
{
"slug": "zipcode",
"dotted_path": "parrot_tools.zipcode.ZipcodeAPIToolkit"
}
]
Fields:
| Field | Type | Always present | Description |
|---|---|---|---|
slug |
string | yes | Identifier used in tools_config_plain. Stable contract. |
dotted_path |
string | yes | Python import path. Stable contract. |
description |
string | no | First line of the tool class docstring (if available). May be enriched in future. |
category |
string | no | Category string from the tool class, if defined. |
Note: description and category are best-effort; they may be absent
for some entries. slug and dotted_path are guaranteed stable.
401 Unauthorized:
Idempotency: Safe to call repeatedly. Response is cached server-side for the process lifetime after the first request.
Cross-reference: Existing routes (do NOT re-document here)¶
These routes continue to work for persisted bots and are unchanged:
| Method | Path | Description |
|---|---|---|
PUT |
/api/v1/user_agents |
Create a persisted bot (synchronous DB-first flow). |
PATCH |
/api/v1/user_agents/{chatbot_id} |
Partially update a persisted bot. |
GET |
/api/v1/user_agents |
List all persisted bots for the current user. |
GET |
/api/v1/user_agents/{chatbot_id} |
Fetch one persisted bot. |
DELETE |
/api/v1/user_agents/{chatbot_id} |
Delete a persisted bot (removes S3 docs). |
POST |
/api/v1/agents/chat/{chatbot_id} |
Send a chat message (works for both ephemeral-ready and persisted bots). |
4. Polling Guidance¶
Recommended Polling Strategy¶
| Time since POST | Interval |
|---|---|
| 0–10 s | Every 1 s |
| 10–60 s | Every 3 s |
| 60 s–5 min | Every 5 s |
| > 5 min | Stop and show timeout error to the user |
The 5-minute ceiling is a client-side safety valve. A healthy warm-up completes within 10–30 seconds for most configurations. Only very large RAG indexes (multi-hundred MB) may take longer.
Phase Meanings in UX Terms¶
phase |
UX Label | Description |
|---|---|---|
creating |
"Starting up…" | Server confirmed the request. Warm-up is about to begin. |
warming |
"Getting ready…" | Tools, MCP, and RAG setup are in progress. |
ready |
"Ready to chat!" | All subsystems initialized. Unlock the chat input. |
error |
"Setup failed" | Something went wrong. Show error field. Let user retry or discard. |
Progress Keys for a Per-Subsystem Progress Bar¶
The progress object in the status response contains per-subsystem keys.
Each value cycles through the states below:
| Key | Possible values | UX label |
|---|---|---|
tools |
"syncing" → "ready" |
Tool sync |
mcp |
"validating" → "ready" | "skipped" |
MCP server connection |
rag |
"building" → "ready" | "skipped" |
Document index build |
"skipped" means the subsystem had nothing to do (e.g. no MCP servers
configured, no documents uploaded). Show "Skipped" or simply hide that row.
5. File Upload Protocol¶
Multipart Layout¶
POST /api/v1/agents/user/
Content-Type: multipart/form-data; boundary=<boundary>
Part 1 — name="config", Content-Type="application/json" → config JSON (required)
Part 2+ — name="files[]", Content-Type="<mime-type>",
filename="<original-filename>" → file binary (0 or more)
- The
configpart MUST appear first. - Each file part uses the field name
files[]. - There is no enforced limit on the number of files, but per-file size is
capped at 50 MB by default (configurable via
MAX_UPLOAD_BYTESenv var on the server).
MIME Types by RAG Mode¶
rag_mode |
Accepted MIME types |
|---|---|
"vector" |
application/pdf, text/plain, text/markdown, text/html, any text-based type |
"pageindex" |
application/pdf (requires a document with a table of contents) |
No-File Shortcut¶
If there are no files, use application/json directly with the config
object as the request body. The server auto-detects content type.
6. Tool Catalog Payload¶
The GET /api/v1/tools/catalog response is a JSON array sorted by slug.
Full shape described in §3.
Stable contract fields (guaranteed across versions):
- slug — use this in tools_config_plain when creating an agent.
- dotted_path — internal import path; do not call directly.
Enrichment fields (may evolve):
- description — sourced from docstrings, subject to wording changes.
- category — may be added to more tools in future releases.
Open question: The catalog currently exposes all installed tools with no filtering by user permissions. A future update may scope the catalog to tools the user's plan allows. See §9 Open Questions.
7. MCP Server Config Payload¶
An MCP server entry (one item in mcp_config_plain) has this shape:
{
"type": "http",
"name": "my-tools",
"url": "https://mcp-server.example.com",
"api_key": "sk-...",
"timeout": 30
}
| Field | Type | Required | Description |
|---|---|---|---|
type |
"http" |
yes | Transport type. Only "http" is supported for ephemeral agents. |
name |
string | yes | Display name for the MCP server connection. |
url |
string | yes | Base URL of the HTTP MCP server. |
api_key |
string | no | API key for the MCP server (stored encrypted). |
timeout |
int | no | Connection timeout in seconds (default: 30). |
Handshake Validation During Warm-up¶
During warm-up, the server connects to each MCP server and lists tools once.
If the connection is refused, times out, or returns an unexpected response,
warm-up sets phase = "error" with an error message like:
A failing MCP handshake prevents promotion. The user should check the server URL and API key, then discard and recreate the agent.
HTTP-only: stdio-transport and local MCP servers are NOT supported for
ephemeral agents in FEAT-149.
8. Saving / Promoting¶
When the user promotes an ephemeral agent via PUT /api/v1/agents/user/{chatbot_id}:
-
The agent's in-memory state is written to the
navigator.users_botsdatabase table with the samechatbot_id. No new ID is generated. -
If the agent uses FAISS vector RAG, the index is serialized and uploaded to S3. The path is stored in
vector_config.faiss_persist_pathin the DB row. On future restarts, the index is reloaded from S3 automatically. -
The agent is removed from the ephemeral registry. From this point it is a regular persisted bot.
-
The frontend should refresh its "My Bots" list view after a successful promote (re-fetch from
GET /api/v1/user_agents). -
The promoted agent remains chateable at exactly the same endpoint:
POST /api/v1/agents/chat/{chatbot_id}. No URL change.
What the frontend should re-fetch after promote:
- The "My Bots" list: GET /api/v1/user_agents
- The individual bot details: GET /api/v1/user_agents/{chatbot_id}
What stays the same: chatbot_id, active chat session, chat history
stored in the session.
9. Open Questions for the Frontend Team¶
These are product decisions the backend cannot make unilaterally:
-
Discard button: Should the UI show a "Discard" button next to "Save" while the agent is ephemeral and ready? Discarding removes S3 docs. What confirmation UX is appropriate?
-
Error recovery: When
phase == "error", should the UI offer a "Retry" button (which would require a new POST + fresh chatbot_id) or only "Discard"? -
Promote during warm-up: The backend returns 409 if the user tries to promote while
phase != "ready". Should the UI disable the "Save" button until ready, or show a tooltip explaining the wait? -
Permissions field:
UserBotModelhas apermissionsdict for future team-sharing features. The create form currently does not expose it. Should v1 let advanced users set permissions at creation time? -
Tool catalog filtering: Should tools be filtered by some criteria (e.g. user plan, installed plugins) before being shown to the user? Today the catalog returns everything installed on the server.
-
Share-key flow (FEAT-149 §8, deferred): A future feature will allow sharing an agent with another user via a permission entry on
users_bots.permissions. Do not implement share UI in v1 — leave a placeholder for FEAT-XXX. -
Warm-up ceiling: If the user closes the "create" dialog while warm-up is still in progress, the bot continues warming in the background. Should the UI show a notification when warm-up completes (e.g. "Your bot is ready" badge)?
-
Multiple ephemeral agents: Can a user have more than one ephemeral agent running simultaneously? The backend supports it (each gets its own
chatbot_id). Should the UI cap this at 1, or allow N?
10. Out-of-Scope Reminders¶
The following capabilities are explicitly NOT part of FEAT-149 and should not be assumed in the frontend spec:
-
Agent sharing — per-user permission lists on
users_bots.permissionsare a post-FEAT-149 deliverable. No sharing endpoint exists yet. -
stdio MCP servers — only HTTP-transport MCP is supported for ephemeral agents. Local/stdio MCP is a different feature.
-
Runtime tool authoring — users cannot define new tools through the UI in FEAT-149. They can only select from
TOOL_REGISTRY. -
Streaming warm-up status — the status endpoint is a poll-only snapshot. A WebSocket or SSE stream for live progress is a possible follow-up.
-
Per-agent quotas or rate limits — no per-ephemeral resource accounting is implemented in FEAT-149.
-
Multi-tenant agent pools — ephemeral agents are strictly per-user. There is no concept of a "shared" ephemeral pool in this feature.