Skip to content
Robotadocs

@robota-sdk/agent-transport/http

HTTP transport for the Robota SDK, built on Hono. It exposes a running agent session as a small set of HTTP routes, so a browser, another service, or any HTTP client can submit prompts, stream the agent's response as Server-Sent Events, and read the session's state. The result is a Hono app rather than a listening server: you mount it in your own Hono application or serve it yourself.

Installation

npm install @robota-sdk/agent-transport hono

This subpath reaches no Node built-in or Node global, so it runs wherever Hono does; on Node it needs 22.12 or later. hono is an optional peer dependency of the package, installed for this subpath only; your application serves or mounts the routes with it.

Usage

createHttpTransport wraps the routes as a transport with the standard attach / start / stop lifecycle. After start(), getApp() returns the Hono app to serve.

import { createHttpTransport } from '@robota-sdk/agent-transport/http';
import type { IHttpTransportSession } from '@robota-sdk/agent-transport/http';
 
declare const session: IHttpTransportSession; // e.g. an InteractiveSession from @robota-sdk/agent-framework
 
const transport = createHttpTransport({ basePath: '/agent' });
transport.attach(session);
await transport.start();
 
const app = transport.getApp(); // a Hono app; serve it, or mount it in your own app
const token = transport.getAdmissionToken(); // hand this to your client

Every request must carry Authorization: Bearer <token>; anything else is answered 401 before it reaches a route. When you omit admission, a random per-launch token is minted and getAdmissionToken() returns it. Pass admission: { token } to use a token you chose, or admission: { open: true, openReason: '…' } to run without a credential when something in front of the routes (a gateway, your own auth middleware) already decides who may reach them. openReason is required, and a token together with open: true is rejected.

Mounting the routes in your own app

createAgentRoutes returns the same routes as a Hono router. It resolves the session per request through sessionFactory, which receives the Hono request context, so one app can serve several sessions (for example, looked up from a request header). Here admission is required. A token minted inside createAgentRoutes could never be read back, so pass one you know: your own, or the result of resolveAdmission() from @robota-sdk/agent-transport.

import { createAgentRoutes } from '@robota-sdk/agent-transport/http';
import type { IHttpTransportSession } from '@robota-sdk/agent-transport/http';
import { Hono } from 'hono';
 
declare const session: IHttpTransportSession;
declare const agentToken: string; // shared with your clients out of band
 
const app = new Hono();
app.route(
  '/agent',
  createAgentRoutes({
    sessionFactory: () => session, // may also look the session up from the request context
    admission: { token: agentToken },
  }),
);

sessionFactory may return a new wrapper object on each call. /submit keys its one-turn-at-a-time check on getSession().getSessionId(), not on object identity.

Routes

MethodPathPurpose
POST/submitBody { "prompt": string, "receivePrompts"?: boolean }. Runs a turn and streams text_delta, tool_start, tool_end, thinking, then complete, interrupted or error as SSE. With receivePrompts: true the stream also carries permission_request, ask_request and prompt_resolved. 409 while a turn is already running.
POST/commandBody { "name": string, "args"?: string }. Runs a session command on behalf of a remote caller.
POST/abortAborts the running turn.
POST/cancel-queueDrops the queued prompt.
GET/messagesThe conversation history.
GET/contextThe context-window state.
GET/executing{ "executing": boolean }, using the same rule /submit uses to answer 409.
GET/pending{ "pending": … }, the queued prompt, if any.
GET/prompts{ "prompts": [{ "type", "event" }] }, the prompts a prompt-receiving /submit stream forwarded that are still open, oldest first.
POST/prompts/:idAnswers an open prompt: { "result": true | false | "allow-session" | "allow-project" } for a permission, { "response": { "type": "answer", "values": string[], "text"?: string } | { "type": "cancelled" } } for an ask. 404 when the id is not open, 400 for an answer of the wrong shape, 409 when the session did not take the answer.

Permission and ask prompts

A turn can stop to ask whether a tool may run (permission_request) or to ask the user a question (ask_request). Only a client that says it can answer receives them: send receivePrompts: true with /submit, and answer each prompt by its event.id through POST /prompts/:id. prompt_resolved follows when a prompt is settled, by this client or by another surface on the same session. A client that comes later reads the open prompts from GET /prompts and answers the same way.

Without receivePrompts, nothing changes: a turn whose prompt no surface can answer is denied (or the question cancelled) at once rather than left waiting. Closing the stream aborts the turn, which settles its open prompts the same way. The answer is recorded as the host's attribution.driverId, never as anything the request claims.

Paths are relative to where the routes are mounted (basePath, or the prefix you pass to app.route).

Exports

SymbolKindDescription
createHttpTransportfunction(options?: IHttpTransportOptions) => IHttpTransport
createAgentRoutesfunction(options: IAgentRoutesOptions) => Hono
IHttpTransportOptionsinterface{ basePath?, admission?, onStreamFailure?, attribution? }
IHttpTransportinterfaceThe transport; adds getApp() and getAdmissionToken() to the standard transport lifecycle
IAgentRoutesOptionsinterface{ sessionFactory, admission, onStreamFailure?, attribution? }
TSessionFactorytype(c: Context) => IHttpTransportSession | Promise<IHttpTransportSession>
IHttpTransportSessioninterfaceThe session capabilities the routes use; a full interactive session satisfies it
TTurnAttributiontypeHost-assigned driverId/surface for turns and prompt answers; never read from the request
TStreamFailureListenertypeReceives the details of a stream that fails after headers were sent (the client sees a generic error)