@skein-js/core
v0.16.0
Published
Framework-agnostic Agent Protocol engine for LangGraph.js — the heart of skein-js.
Downloads
2,456
Maintainers
Readme
@skein-js/core
The shared Agent Protocol contract for skein-js — wire types, the
SkeinStoreinterface, the queue / bus / auth seams, and the edge error type.
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 run engine, handler table, and SSE mapping that build on this contract live in @skein-js/agent-protocol.
Contents
What it does
Holds the Agent Protocol contract once, against normalized types, so behavior is identical across
every framework adapter and storage driver. It defines interfaces; it implements nothing — the
drivers (storage-memory, storage-postgres,
redis) implement these interfaces and the engine consumes
them. Everything downstream imports it:
- Wire types re-exported from
@langchain/langgraph-sdk(Assistant,Thread,Run,RunStatus,Config,Metadata,Item,StreamMode,Interrupt, …) — the single seam that pins the protocol version. skein-js never redefines them. SkeinStore— the persistence interface for protocol resources (assistants/threads/runs/store), including thehasActiveRunconcurrency guard andisTerminalRunStatus.RunQueue/RunEventBus/RunFrame— the background-run queue and streaming pub/sub seams.AuthEngine— the authentication + authorization contract (LangGraph-style custom auth), consulted per request by the engine when present.SkeinHttpError— the typed edge error carrying an HTTP status.RunError/toRunError— why a run failed, as one JSON-safe payload shared by theerrorSSE frame, the persistedRun.error, and a failedPOST /runs/wait.rootCause/rootCauseMessage— the deepest link of anError.causechain, for the places with room for one sentence rather than the whole chain.serializeWireJson— flattens LangChain messages to the Agent Protocol wire shape for output.
Install
pnpm add @skein-js/corePeer dependency (install once in your project):
@langchain/langgraph-sdk. core bundles nothing itself.
Usage
You rarely import core directly — you get it transitively. You reach for it when implementing a
driver or handling errors at the HTTP edge:
import { type SkeinStore, SkeinHttpError, isTerminalRunStatus } from "@skein-js/core";
// Storage drivers implement SkeinStore…
export class MyStore implements SkeinStore {
/* assistants / threads / runs / store repos */
}
// …and adapters throw/catch SkeinHttpError at the HTTP edge.
throw SkeinHttpError.notFound(`Thread "${id}" not found.`);API
Wire types (re-exported from
@langchain/langgraph-sdk):Assistant,AssistantBase,AssistantGraph,Checkpoint,Config,DefaultValues,GraphSchema,Interrupt,Item,Metadata,Run,SearchItem,StreamMode,Thread,ThreadState,ThreadStatus,ThreadTask; plusRunStatusandMultitaskStrategyderived fromRun.interface SkeinStore—{ assistants: AssistantRepo; threads: ThreadRepo; runs: RunRepo; crons: CronRepo; store: StoreRepo }, plus the read-onlydurable?andmaxPageSize?a driver declares about itself. Each repo exposes CRUD + list/search;RunRepo.hasActiveRun(threadId)is the concurrency guard (truewhile a run ispending/running). Input types:AssistantCreate,ThreadCreate,ThreadUpdate,RunCreate,RunKwargs,StoreSearchQuery,StoreTtlConfig,ThreadTtlConfig.Two contract points a driver author must honour.
ThreadRepo.createthrowsSkeinHttpError.conflict(409) whenthread_idis already taken — enforced atomically in the driver, never by a read-then-write, which is what lets the service turn it intoif_existshandling and two instances race the same id safely. AndThreadRepo.listExpired({ now, limit })returns the ids of threads whose TTL has passed — ids, not rows, because the sweeper deletes each through the thread service so an in-flight run is aborted first.TERMINAL_RUN_STATUSES/isTerminalRunStatus(status)—success/error/timeout/interrupted/cancelledare terminal (a resume arrives as a fresh run).interface RunQueue—enqueue(run, options?)+consume(process, options?)→RunConsumer.EnqueueOptions.delayMsholds a run back before any consumer may pick it up — the run-createafter_seconds.interface RunEventBus—publish/close/subscribe(runId, afterSeq?).RunFrame={ seq, event, data }(monotonicseqper run). PlusQueuedRun,RunProcessor,RunConsumer,RunConsumerOptions,EnqueueOptions.interface AuthEngine—authenticate(request)(→AuthContext, throws 401) +authorize({ resource, action, value, context })(→{ filters?, value }, throws 403) +matchesFilters(...). PlusAuthContext,AuthUser,AuthResource,AuthAction,AuthFilters,AuthFilterValue.class SkeinHttpError—new SkeinHttpError(status, message, options?)and the static helpersbadRequest(400) /unauthorized(401) /forbidden(403) /notFound(404) /conflict(409) /unprocessable(422);isSkeinHttpError(value)narrows it.serializeWireJson(value): string—JSON.stringifyreplacement that flattens LangChainBaseMessages to the wire shape the SDK /useStream/ Agent Chat UI expect.
Reuse
Reuses @langchain/langgraph-sdk TypeScript types as the wire contract rather than redefining them.
Graphs run through @langchain/langgraph (CompiledStateGraph.invoke/.stream, interrupts/resume)
in @skein-js/agent-protocol — never a reimplemented runtime.
