@skein-js/agent-protocol
v0.6.3
Published
Framework-agnostic Agent Protocol engine for LangGraph.js — run engine, handlers, and SSE, driven entirely by injected dependencies.
Maintainers
Readme
@skein-js/agent-protocol
The framework-agnostic Agent Protocol engine — run engine, handler table, and SSE mapping. The heart of skein-js.
Part of skein-js — a TypeScript Agent Protocol server for LangGraph.js, and a drop-in replacement for the LangGraph CLI.
Status: 🚧 Pre-alpha — implemented. Depends only on the @skein-js/core contracts and is designed to be consumed on its own.
This is the engine at the heart of skein-js: a complete, framework-agnostic implementation of the
Agent Protocol for LangGraph.js. Build your own server on it — any HTTP framework, any storage/queue —
by injecting a ProtocolDeps.
Contents
What it does
The run engine + protocol handler table + SSE mapping, and nothing else. It has no opinion about
your HTTP framework, your database, your queue, or your CLI — every collaborator is injected.
Give it a store, a queue, an event bus, a checkpointer, and a way to resolve graphs, and it serves
assistants, threads, the three run modes (wait / stream / background), the store, and
human-in-the-loop interrupt/resume — wire-compatible with the official @langchain/langgraph-sdk
client.
Install
pnpm add @skein-js/agent-protocol @skein-js/core@langchain/langgraph and @langchain/langgraph-sdk are peer dependencies — install them too if
your project doesn't already depend on them:
pnpm add @langchain/langgraph @langchain/langgraph-sdkUsage
import { createProtocolRuntime } from "@skein-js/agent-protocol";
const runtime = createProtocolRuntime({
store, // a SkeinStore (e.g. @skein-js/storage-memory, @skein-js/storage-postgres)
graphs, // a GraphResolver — ids + load(id) + schemas(id) (e.g. @skein-js/config's registry)
queue, // a RunQueue for background runs
bus, // a RunEventBus for streaming fan-out
checkpointer, // a LangGraph BaseCheckpointSaver (MemorySaver in dev)
// optional: auth, logger, clock, logRunActivity, runTimeoutMs
});
// One-time startup: register an assistant per graph, then start processing background runs.
await runtime.service.assistants.registerGraphAssistants();
runtime.worker.start();
// `runtime.handlers` is a transport-neutral table an adapter (e.g. @skein-js/express) mounts.
// `runtime.service` is the typed engine you can also drive directly.Two layers
service(createProtocolService(deps)/runtime.service) — framework-agnostic logic over already-validated, typed inputs. Returns plain values or anAsyncIterable<RunFrame>; throwsSkeinHttpError. Use this to embed the engine directly.handlers(runtime.handlers) — a thin table of(ProtocolRequest) => ProtocolResponsehandlers that validate raw input with Zod and delegate to the service. Framework adapters map their request/response objects ontoProtocolRequest/ProtocolResponse.
createProtocolRuntime builds the service, handlers, and background worker over one shared
context, so cancelling a run through the service actually aborts it in the worker. Use the
individual createProtocolService / createProtocolHandlers / createRunWorker factories only when
you don't run a worker in the same process.
The injected contract (ProtocolDeps)
| Dependency | Type | Responsibility |
| ----------------- | ---------------------------------------------- | ------------------------------------------------------------------------ |
| store | SkeinStore (core) | Protocol resource rows (assistants/threads/runs/store) |
| graphs | GraphResolver (this package) | Resolve a graph_id to a compiled graph + schemas |
| queue | RunQueue (core) | Hand background runs to a worker |
| bus | RunEventBus (core) | Fan run frames out to streaming clients |
| checkpointer | BaseCheckpointSaver (@langchain/langgraph) | Graph state, history, and interrupt/resume |
| auth? | AuthEngine (core) | Per-request 401/403 + ownership filtering; absent = all allowed |
| logger? | Logger (this package) | Structured logging; default no-op |
| clock? | Clock | Time source; default () => new Date() |
| logRunActivity? | boolean | Log per-run start/finish, tool calls, interrupts (skein dev --verbose) |
| runTimeoutMs? | number | Optional per-run wall-clock timeout → "timeout" |
Graph state, history, and interrupt/resume are 100% LangGraph-native via the checkpointer. The
SkeinStore owns only the protocol resource rows — it is deliberately not the checkpointer.
API
- Entry points:
createProtocolRuntime(deps, options?)→{ service, handlers, worker };createProtocolService/buildProtocolService;createProtocolHandlers;createContext;createRunWorker(ctx, options?)(RunWorkerOptions:maxConcurrency,shutdownGraceMs). - Service surface (
runtime.service):assistants(registerGraphAssistants,get,list,search,schemas),threads(create/get/list/patch/delete/history/getState),threadStream(stream/joinStream/command— HIL resume, requires statusinterrupted),runs(createWait/createStream/createBackground/get/listByThread/cancel/delete/join/finalStatus),store(put/get/delete/search/listNamespaces). - Transport types:
ProtocolRequest,ProtocolResponse(json|empty|sse),ProtocolHandler,ProtocolHandlers. SkeinBaseStore— bridges aStoreRepointo a LangGraphBaseStore, so graph nodes reach long-term memory viagetStore(). The engine attaches one to every run.- SSE helpers (for adapters writing the stream themselves):
SSE_HEADERS,encodeFrame,encodeTerminal,toSseEvents,parseAfterSeq.
Note on duplicate type names.
GraphResolver,CompiledGraphFactory,ResolvedGraph, andGraphSchemasare exported here and (structurally compatible copies) by@skein-js/config.config'sGraphRegistrysatisfies this package'sGraphResolverat wire-up time.
Reuse
Runs graphs through @langchain/langgraph (invoke/stream, interrupts/resume) and uses the
injected BaseCheckpointSaver for state/history — never a reimplemented runtime. Wire types come
from @langchain/langgraph-sdk via @skein-js/core.
Learn more
- Agent Protocol surface · Streaming (SSE) · Runs & Redis
- Building your own adapter — mount this engine on any HTTP framework
- skein-js overview · Reuse-first architecture · Root README
