Server and API#
Neoism Agent is an HTTP server. Every client — the desktop pane, the browser, the SDK, external tools — talks to the same versioned API, so anything the UI can do, your scripts can do.
One surface: /v2#
The public API lives entirely under /v2/. There are no legacy aliases; a request to an unversioned path returns 404. The full contract is machine-readable:
GET /v2/openapi.jsonserves the canonical OpenAPI document.- The same document is committed in the Neoism repository at
neoism-agent/openapi/v2.json, so API changes are visible in review diffs. GET /v2/metareportsapiVersion,serverVersion,pluginApiVersion, and the active plugin generation.
Errors use one envelope everywhere: { "code", "message", "retryable", "details" }.
Route map#
| Domain | Routes |
|---|---|
| System | /v2/health, /v2/meta, /v2/openapi.json, /v2/capabilities, /v2/audit |
| Sessions | /v2/sessions CRUD, /status, /fork, /children, /runtime, /todos, /diff, import/export |
| Generation | /v2/sessions/{id}/prompt, /prompt-async, /abort, /compact, /wait, /summarize, /context |
| Messages | /v2/sessions/{id}/messages, message and part deletion/patching |
| Queue | /v2/sessions/{id}/queue (list, clear, pop) |
| History | /v2/sessions/{id}/undo, /redo, /revert, /unrevert, /undo-tree |
| Interactions | /v2/interactions/permissions, /v2/interactions/questions with reply/reject |
| Artifacts | /v2/artifacts upload/download/list |
| Events | /v2/events (SSE) |
| Plugins | /v2/plugins, plus every plugin-contributed route (config, providers, agents, skills, commands, LSP, MCP, PTY, VCS, workflows, goals, subagents, semantic search) |
Plugin-contributed routes register through the plugin runtime and are held to the same OpenAPI parity tests as the core routes.
Prompting#
POST /v2/sessions/{id}/prompt accepts text or structured parts, an optional client-generated messageId (idempotent — retries are deduplicated), a model/agent override, and a delivery of steer (join the active run) or queue (wait for it). The server owns the run: closing the client does not stop generation.
The event stream#
GET /v2/events?sessionId=<id>&tail=true is a Server-Sent Events stream carrying everything that happens: token deltas, part snapshots, session status, permissions, questions, queue changes, execution activity, and subagent lifecycle.
- Events arrive in publish order from a single ordered bus. A part snapshot can never overtake or lag the deltas around it.
- Every record's
datais a typed,type-discriminated union — the OpenAPIEventschema enumerates all event types with their exact payloads, and the SDK exposes it as a TypeScript discriminated union. - Passing
since=<sequence>(or aLast-Event-IDheader) instead oftailreplays the durable event log from that cursor before going live. - Live token deltas are transient; durable events are committed transactionally with the state change they describe.
Subscribing with a sessionId follows the whole session family: child subagent sessions discovered later join the stream automatically.
Authentication and hosted mode#
With no token configured, the server is open on loopback for local use. Three credential forms enable gating:
| Mechanism | Purpose |
|---|---|
NEOISM_AGENT_TOKEN |
A single bearer token for the local server. |
| Daemon-signed credentials | The workspace daemon signs per-client claims (workspace, tenant, directory prefixes). |
NEOISM_AGENT_AUTH_CONFIG |
Local/trusted token file: per-token tenants, directory scopes, rate and concurrency quotas. |
Authenticated callers get application-level admission checks: session ownership checks on session-scoped routes (validated against route descriptors, never guessed from the path), directory-prefix enforcement, restricted config and credential access, and authenticated-request audit entries (/v2/audit). Token tenants and directory prefixes are not VM isolation or an OS filesystem jail. They do not make a native shared-process deployment safe for mutually untrusted tenants.
For strict shared hosting, embed the server with authoritative tenant resolution, scoped credential/blob stores and for_hosted_control_plane(). That shared process is execution-disabled. The application separately admits a whole Agent worker in one isolated VM per logical workspace, using the host manager/broker and production infrastructure provider. Worker startup pairs immutable bootstrap with a 32-byte public verifier; private signing seeds remain controller-only. Authenticated GET /v2/runtime reports worker version, root and runtime identity/generation for broker verification. Worker scopes authorize API operations, not OS isolation among agents sharing the VM. See The Neoism Agent, SDK, and the authoritative hosted architecture/status guide.
Run it standalone#
The agent server also runs without the desktop:
neoism-agent serve # HTTP server
neoism-agent openapi # print the OpenAPI document
Point any SDK or HTTP client at it. The desktop's embedded server is the same binary surface.
Embedding the agent in a product#
A backend that drives the agent (a SaaS assistant, a bot, a pipeline) runs one loop per conversation. For local/trusted integrations, NEOISM_AGENT_AUTH_CONFIG can restrict token access to tenant directories. For mutually untrusted hosted execution, use the strict shared control plane and separately admitted whole workspace workers described above; directory metadata alone is not execution isolation.
- Connect with a bearer token. Locally, a configured token can carry
directoryPrefixesand rate/concurrency quotas. In cloud mode, use the application's host broker to obtain a short-lived credential for the current authorized worker. - Create or reuse a session rooted in an admitted directory (
sessions.create({ directory })). Instructions and configuration are discovered upward from that directory; multiple project folders and Git worktrees remain on the same workspace VM, not separate machines. - Subscribe to
/v2/eventswith the session id andtail: truebefore prompting, so nothing slips between the two. The SDK subscription reconnects automatically and resumes from its sequence cursor. - Prompt with a caller-generated
messageId— retrying the same prompt after a network failure is idempotent, never a duplicate turn. - Stream tokens from
message.part.delta, watch typed tool and step-finish parts (token counts and cost for billing), and finish whensession.statusreports idle for the session.
The complete, runnable version of this loop is
sdk/typescript/examples/headless.ts in the repository.
Headless runs should preconfigure Permissions rules (allow and deny
patterns) so a turn never blocks on an interactive approval; the
permission.asked event plus interactions.permissions.reply is the
interactive fallback, not the plan.
API stability#
The committed OpenAPI document is the contract. Within a major
apiVersion: existing routes, fields, event types, and part types are not
removed or repurposed; new ones may be added at any time. Part and event
schemas tolerate additive fields — validating clients must ignore unknown
properties. Event sequence values are monotone per connection and safe
to use as a resume cursor. Breaking changes get a new version prefix and a
deprecation window, never a silent change under /v2.
See SDK for typed clients, Plugins for extending the server, and Sessions and Sharing for the session model.