@skein-js/runtime
v0.16.0
Published
Assembles skein-js ProtocolDeps from langgraph.json + selected drivers (memory/Postgres/Redis).
Maintainers
Readme
@skein-js/runtime
Assembles a production
ProtocolDeps(memory / Postgres / Redis) from alanggraph.json.
Part of skein-js — the open-source alternative to LangGraph Platform for TypeScript: a self-hosted Agent Protocol server for LangGraph.js, and a drop-in replacement for the LangGraph CLI.
Status: 🚧 Pre-alpha — implemented; the assembler behind skein dev and skein up.
What it does
buildRuntime() assembles a ProtocolDeps from a langgraph.json plus a chosen
store/queue driver, and hands it to any framework adapter through the injectable { deps } seam.
This is the one place a production driver combination is selected — so skein dev and skein up run
the same engine against either the zero-setup in-memory drivers or production-shaped
Postgres + Redis. The engine itself stays driver-agnostic.
import { buildRuntime } from "@skein-js/runtime";
import { createExpressServer } from "@skein-js/express";
const runtime = await buildRuntime({
configPath: "/abs/path/to/langgraph.json",
store: "postgres", // "memory" | "postgres" (postgres reads POSTGRES_URI)
queue: "redis", // "memory" | "redis" (redis reads REDIS_URI)
});
const server = await createExpressServer({ deps: runtime.deps, cors: runtime.cors });
await server.listen(2024);
// …on shutdown:
await runtime.dispose();store and queue are required (no defaults — the CLI supplies its own flag defaults). The
driver branches:
store: "postgres"connectsPostgresSkeinStore(fromPOSTGRES_URI), runs its migrations, and usesPostgresSaveras the LangGraph checkpointer.queue: "redis"uses the BullMQ run queue + Redis Streams/pub-sub event bus (fromREDIS_URI).store: "memory"+queue: "memory"delegates to@skein-js/express's reloadable in-memory runtime, soskein dev's hot-reload and cross-restart state persistence work.
A missing POSTGRES_URI / REDIS_URI throws RuntimeConfigError; if assembly fails part-way, any
resources already created are disposed before rethrowing, so a failed build leaks nothing.
This is the same environment contract the skein build image uses, so anything you learn here
applies when you deploy it — see deploy anywhere for the full variable table,
pool sizing, and per-platform guides.
Graph hot-reload (reloadGraphs()) works in every mode; snapshotState/hydrateState are present
only in all-memory mode (durable stores keep their own state).
createExpressServeris imported from@skein-js/express, not from here. The shippedexamples/callcreateExpressServer({ config })directly (the config-path form, which uses the in-memory runtime under the hood);buildRuntimeis the path the CLI uses to add Postgres/Redis.
In-code embedding (Postgres + Redis)
buildRuntime assembles durable deps from a langgraph.json. embedPostgresGraphs assembles the
same durable deps from a graph you already hold in code — the persistent counterpart to
embedInMemoryGraphs (which wires only in-memory drivers). Because it owns
pools/connections, it's async and returns a dispose():
import { createExpressServer } from "@skein-js/express";
import { embedPostgresGraphs } from "@skein-js/runtime";
import { graph } from "./my-graph.js";
const { deps, dispose } = await embedPostgresGraphs({ agent: graph }); // reads POSTGRES_URI / REDIS_URI
const server = await createExpressServer({ deps });
await server.listen(2024);
// …on shutdown:
await dispose();Postgres is required (POSTGRES_URI or postgresUri). Redis is optional — with no redisUri /
REDIS_URI, the run queue + event bus fall back to in-memory: state survives a restart, but you're
limited to a single instance (the queue is process-local; streaming isn't fanned across instances).
Options mirror the low-level knobs: index (pgvector), ttl (store items), threadTtl (threads),
poolMax, sslNoVerify, and overrides
for non-driver deps (auth/logger/…). See docs/embedding.md for the full
walkthrough.
Install
pnpm add @skein-js/runtimePeer dependencies: @langchain/langgraph and @langchain/langgraph-checkpoint-postgres. Loading
TypeScript graphs/embedders requires passing an importModule (the CLI injects a vite loader).
API
buildRuntime(options): Promise<SkeinRuntime>—options:{ configPath, store, queue, importModule? }. Durable deps from alanggraph.json.interface SkeinRuntime—{ deps, cors?, reloadGraphs(), dispose(), snapshotState?(), hydrateState?() }(the last two only in all-memory mode).type StoreDriver="memory" | "postgres"·type QueueDriver="memory" | "redis".embedPostgresGraphs(graphs, options?): Promise<EmbeddedPostgresRuntime>— durable deps from graphs in code (Postgres +PostgresSaver, Redis when configured). Returns{ deps, dispose() }.options:{ postgresUri?, redisUri?, index?, ttl?, threadTtl?, poolMax?, sslNoVerify?, connectionTimeoutMs?, idleTimeoutMs?, statementTimeoutMs?, maxPageSize?, overrides? }.ttlis store-item expiry;threadTtlis thread expiry — the in-codecheckpointer.ttl.interface EmbedPostgresGraphsOptions·interface EmbeddedPostgresRuntime· re-exportedtype EmbeddableGraph(from@skein-js/server-kit).class RuntimeConfigError— thrown when a driver's env var orstore.index.embedcan't be resolved.resolveEmbed(embed, { configDir, importModule? })— resolves alanggraph.jsonstore.index.embedto anEmbedFunction(see below); exported for reuse/testing.
Semantic search (store.index.embed)
When store: "postgres" and langgraph.json declares a store.index, buildRuntime resolves the
embed value into an embedder and enables pgvector semantic search — honoring both forms the
LangGraph CLI documents:
"provider:model"— e.g."openai:text-embedding-3-small". Mirrors Pythoninit_embeddings: the provider prefix selects a@langchain/<provider>package (dynamically imported — install it in your project, e.g.@langchain/openai, and set its API key). Supported prefixes:openai,azure_openai,cohere,google_genai,mistralai,bedrock,ollama.- Custom-function path — e.g.
"./embeddings.ts:embed". The export is either a raw(texts: string[]) => number[][](the shape LangGraph documents) or a LangChainEmbeddingsinstance. Resolved through the samepath:exportloader used for graphs — no extra dependency.
store.index.dims is required whenever embed is set. Without a store.index, Postgres search falls
back to naive text matching (identical to the memory driver).
Learn more
- Storage · Runs & Redis · LangGraph CLI compatibility
- skein-js overview · Reuse-first architecture · Root README
