Skip to content
Robotadocs

HTTP Transport

Serve an InteractiveSession over REST endpoints with @robota-sdk/agent-transport/http. Prompts stream back as Server-Sent Events. The routes are a Hono app, so you can serve them with any Hono adapter.

Basic setup

import { InteractiveSession } from '@robota-sdk/agent-framework';
import { createNodeFrameworkHost } from '@robota-sdk/agent-framework/node';
import { createDefaultTools } from '@robota-sdk/agent-tools/defaults';
import { AnthropicProvider } from '@robota-sdk/agent-provider/anthropic';
import { createHttpTransport } from '@robota-sdk/agent-transport/http';
import { serve } from '@hono/node-server';
 
const provider = new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY });
const session = new InteractiveSession({
  cwd: process.cwd(),
  host: createNodeFrameworkHost({ createDefaultTools }),
  provider,
});
 
const transport = createHttpTransport();
session.attachTransport(transport);
await transport.start();
 
// With no `admission` option the transport mints a credential, and every request must present it
// — including your own. Print it, or hand it to the client you start.
console.log('token:', transport.getAdmissionToken());
 
serve({ fetch: transport.getApp().fetch, port: 3000 });

A request without the token gets 401. The transport reaches session.submit and session.executeCommand, so it is closed unless you open it: pass admission: { token: '…' } to choose the credential, or admission: { open: true, openReason: '…' } when something in front of it already decides who may connect. getAdmissionToken() returns null for an open transport.

/submit waits for the session to finish initializing, so the first request to a fresh InteractiveSession is served, and answers 500 when initialization fails.

A client that can answer permission and ask prompts sends receivePrompts: true with /submit (see Answering prompts). Without it, a tool call that would ask and that no other surface answers is denied, so a client that never answers relies on the session's permissionMode.

Endpoints

MethodPathDescription
POST/submitSubmit prompt, stream events via SSE
POST/commandExecute a session command
POST/abortAbort current execution
POST/cancel-queueCancel queued prompt
GET/messagesGet message history
GET/contextGet context window state
GET/executingCheck if executing
GET/pendingGet pending queued prompt
GET/promptsList open permission/ask prompts
POST/prompts/:idAnswer an open prompt

Submitting a prompt

# $TOKEN is what transport.getAdmissionToken() printed at startup.
curl -X POST http://localhost:3000/submit \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Explain this project"}'

The response is an SSE stream with these events: text_delta, tool_start, tool_end, thinking, complete, interrupted, error.

Answering prompts

With "receivePrompts": true the stream also carries permission_request and ask_request, each with an id, and prompt_resolved once a prompt is settled. Answer while the stream stays open:

curl -N -X POST http://localhost:3000/submit \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "List the files here", "receivePrompts": true}'
 
# From another request, using the id from the permission_request event:
curl -X POST http://localhost:3000/prompts/p1 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"result": true}'

A permission takes result: true, false, "allow-session" or "allow-project". An ask takes response: { "type": "answer", "values": [...], "text"?: "..." } or { "type": "cancelled" }. GET /prompts lists the prompts still open, for a client that connects after one was asked. An id that is not open gets 404, and 200 means the prompt settled. Closing the stream aborts the turn and denies what it left open.

Session per request

createAgentRoutes() gives you the same routes with a session chosen per request — for example one session per signed-in user. Mount them on your own Hono app.

import { createAgentRoutes, type TSessionFactory } from '@robota-sdk/agent-transport/http';
 
// Receives each request's Hono context and returns the session that should handle it.
declare const resolveSession: TSessionFactory;
 
const routes = createAgentRoutes({
  sessionFactory: resolveSession,
  // Required. Every request must present this token before the session is reached.
  admission: { token: requiredEnv('AGENT_HTTP_TOKEN') },
});
 
// An empty string is not a token: it would make each process mint its own random one.
function requiredEnv(name: string): string {
  const value = process.env[name];
  if (!value)
    throw new Error(`${name} is not set — the HTTP routes have no credential to require.`);
  return value;
}

examples/capabilities/multi-surface-deploy serves one session over HTTP and WebSocket at the same time.