@skein-js/agent-protocol 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -102,7 +102,10 @@ Graph **state, history, and interrupt/resume are 100% LangGraph-native** via the
102
102
 
103
103
  - **Entry points:** `createProtocolRuntime(deps, options?)` → `{ service, handlers, worker }`;
104
104
  `createProtocolService` / `createProtocolServiceFromContext`; `createProtocolHandlers`; `createContext`;
105
- `createRunWorker(ctx, options?)` (`RunWorkerOptions`: `maxConcurrency`, `shutdownGraceMs`).
105
+ `createRunWorker(ctx, options?)` (`RunWorkerOptions`: `maxConcurrency` — queued runs at once,
106
+ default `DEFAULT_RUN_CONCURRENCY` (10, matching the LangGraph CLI) — and `shutdownGraceMs`). The
107
+ adapters surface this as `worker.maxConcurrency` and also read `SKEIN_RUN_CONCURRENCY` /
108
+ `N_JOBS_PER_WORKER`; see [runs-and-redis.md](../../docs/runs-and-redis.md#run-concurrency).
106
109
  - **Service surface** (`runtime.service`): `assistants` (`registerGraphAssistants`, `get`, `list`,
107
110
  `search`, `schemas`), `threads` (`create`/`get`/`list`/`patch`/`delete`/`history`/`getState`),
108
111
  `threadStream` (`stream` / `joinStream` / `command` — HIL resume, requires status `interrupted`),
package/dist/index.d.ts CHANGED
@@ -436,10 +436,33 @@ interface ProtocolHandlers {
436
436
  }
437
437
  declare function createProtocolHandlers(service: ProtocolService): ProtocolHandlers;
438
438
 
439
+ /**
440
+ * Queued runs one worker executes at once when nothing is configured. Matches the LangGraph CLI's
441
+ * `--n-jobs-per-worker` default, so a project moving off `langgraph dev` keeps its throughput.
442
+ */
443
+ declare const DEFAULT_RUN_CONCURRENCY = 10;
444
+ /**
445
+ * How long `stop()` lets in-flight runs finish before aborting them. Chosen to leave room inside the
446
+ * tightest common stop window — a container platform typically sends SIGTERM and SIGKILLs ~10s later
447
+ * (Cloud Run's default; `docker stop`'s too) — for this drain *plus* the abort-and-settle step that
448
+ * follows it. Raise it via `SKEIN_SHUTDOWN_GRACE_MS` where the platform allows a longer window
449
+ * (Railway, Kubernetes' `terminationGracePeriodSeconds`, ECS `stopTimeout`); the host must also wait
450
+ * longer than this before forcing exit, or the abort path never runs. See docs/deploy.md.
451
+ */
452
+ declare const DEFAULT_SHUTDOWN_GRACE_MS = 5000;
439
453
  interface RunWorkerOptions {
440
- /** Max runs executing at once. Default 1 — strict per-thread serialization in dev. */
454
+ /**
455
+ * Max queued runs this worker executes at once. Default {@link DEFAULT_RUN_CONCURRENCY}.
456
+ * Per-thread ordering does not depend on this: `startRunExecution` serializes by `thread_id` at
457
+ * every value. The trade-off is head-of-line blocking — a run waiting on a busy thread's lock
458
+ * still holds a slot, so a burst of `multitask_strategy: "enqueue"` runs on one thread can occupy
459
+ * the worker. See docs/runs-and-redis.md.
460
+ */
441
461
  maxConcurrency?: number;
442
- /** How long `stop()` waits for in-flight runs before aborting them (ms). Default 5000. */
462
+ /**
463
+ * How long `stop()` waits for in-flight runs before aborting them (ms). Default
464
+ * {@link DEFAULT_SHUTDOWN_GRACE_MS}.
465
+ */
443
466
  shutdownGraceMs?: number;
444
467
  }
445
468
  interface RunWorker {
@@ -452,7 +475,7 @@ declare function createRunWorker(ctx: ProtocolContext, options?: RunWorkerOption
452
475
 
453
476
  /** Options for {@link createProtocolRuntime}. */
454
477
  interface ProtocolRuntimeOptions {
455
- /** Tuning for the background run worker (concurrency, poll interval). */
478
+ /** Tuning for the background run worker (max concurrency, shutdown grace period). */
456
479
  worker?: RunWorkerOptions;
457
480
  }
458
481
  /** The wired engine: the service, the transport-neutral handler table, and the background worker. */
@@ -602,4 +625,4 @@ declare function toSseEvents(frames: AsyncIterable<RunFrame>, finalStatus: () =>
602
625
  */
603
626
  declare function parseAfterSeq(lastEventId: string | undefined): number;
604
627
 
605
- export { type AssistantService, type Clock, type CommandInput, type CompiledGraphFactory, type CreateAssistantInput, type CreateRunInput, type CreateThreadInput, DEFAULT_INVOKE_PREFIX, type DeleteAssistantOptions, type DrawGraphOptions, type GraphInvokeHandlerName, type GraphInvokeOptions, type GraphResolver, type GraphSchemas, type HistoryOptions, type HttpMethod, type Logger, type PatchThreadInput, type ProtocolContext, type ProtocolDeps, type ProtocolHandler, type ProtocolHandlers, type ProtocolRequest, type ProtocolResponse, type ProtocolRuntime, type ProtocolRuntimeOptions, type ProtocolService, type ResolvedGraph, type RouteBinding, type RouteMatch, type RouteMatcher, type RunService, type RunWorker, type RunWorkerOptions, SSE_HEADERS, SkeinBaseStore, type StartedStream, type StoreService, type SubgraphsOptions, type ThreadService, type ThreadStreamInput, type ThreadStreamService, buildProtocolService, copyThreadIdIntoBody, createContext, createGraphInvokeHandler, createProtocolHandlers, createProtocolRuntime, createProtocolService, createProtocolServiceFromContext, createRouteMatcher, createRunWorker, encodeFrame, encodeTerminal, foldThreadId, graphInvokeRoutes, matchSkeinRoute, parseAfterSeq, skeinRoutes, toSseEvents };
628
+ export { type AssistantService, type Clock, type CommandInput, type CompiledGraphFactory, type CreateAssistantInput, type CreateRunInput, type CreateThreadInput, DEFAULT_INVOKE_PREFIX, DEFAULT_RUN_CONCURRENCY, DEFAULT_SHUTDOWN_GRACE_MS, type DeleteAssistantOptions, type DrawGraphOptions, type GraphInvokeHandlerName, type GraphInvokeOptions, type GraphResolver, type GraphSchemas, type HistoryOptions, type HttpMethod, type Logger, type PatchThreadInput, type ProtocolContext, type ProtocolDeps, type ProtocolHandler, type ProtocolHandlers, type ProtocolRequest, type ProtocolResponse, type ProtocolRuntime, type ProtocolRuntimeOptions, type ProtocolService, type ResolvedGraph, type RouteBinding, type RouteMatch, type RouteMatcher, type RunService, type RunWorker, type RunWorkerOptions, SSE_HEADERS, SkeinBaseStore, type StartedStream, type StoreService, type SubgraphsOptions, type ThreadService, type ThreadStreamInput, type ThreadStreamService, buildProtocolService, copyThreadIdIntoBody, createContext, createGraphInvokeHandler, createProtocolHandlers, createProtocolRuntime, createProtocolService, createProtocolServiceFromContext, createRouteMatcher, createRunWorker, encodeFrame, encodeTerminal, foldThreadId, graphInvokeRoutes, matchSkeinRoute, parseAfterSeq, skeinRoutes, toSseEvents };
package/dist/index.js CHANGED
@@ -2014,7 +2014,15 @@ function createAuthorizingHandlers(context, engine) {
2014
2014
  import {
2015
2015
  isTerminalRunStatus as isTerminalRunStatus6
2016
2016
  } from "@skein-js/core";
2017
- var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2017
+ var DEFAULT_RUN_CONCURRENCY = 10;
2018
+ var DEFAULT_SHUTDOWN_GRACE_MS = 5e3;
2019
+ function cancellableDelay(ms) {
2020
+ let timer;
2021
+ const elapsed = new Promise((resolve) => {
2022
+ timer = setTimeout(resolve, ms);
2023
+ });
2024
+ return { elapsed, cancel: () => clearTimeout(timer) };
2025
+ }
2018
2026
  function logRunLifecycle(logger, run, status, startedAt, endedAt) {
2019
2027
  logger.info(status === "success" ? "Background run succeeded" : `Background run ${status}`, {
2020
2028
  run_id: run.run_id,
@@ -2028,10 +2036,16 @@ function logRunLifecycle(logger, run, status, startedAt, endedAt) {
2028
2036
  }
2029
2037
  function createRunWorker(ctx, options = {}) {
2030
2038
  const { deps, control } = ctx;
2031
- const maxConcurrency = options.maxConcurrency ?? 1;
2032
- const shutdownGraceMs = options.shutdownGraceMs ?? 5e3;
2039
+ const maxConcurrency = options.maxConcurrency ?? DEFAULT_RUN_CONCURRENCY;
2040
+ const shutdownGraceMs = options.shutdownGraceMs ?? DEFAULT_SHUTDOWN_GRACE_MS;
2033
2041
  const inFlight = /* @__PURE__ */ new Set();
2034
- const process = async (queued) => {
2042
+ const running = /* @__PURE__ */ new Set();
2043
+ const executeQueuedRun = (queued) => {
2044
+ const task = executeOne(queued);
2045
+ running.add(task);
2046
+ return task.finally(() => running.delete(task));
2047
+ };
2048
+ const executeOne = async (queued) => {
2035
2049
  const run = await deps.store.runs.get(queued.run_id);
2036
2050
  if (!run || isTerminalRunStatus6(run.status)) {
2037
2051
  ctx.rollbackPlans.delete(queued.run_id);
@@ -2052,20 +2066,37 @@ function createRunWorker(ctx, options = {}) {
2052
2066
  return {
2053
2067
  start() {
2054
2068
  if (consumer) return;
2055
- consumer = deps.queue.consume(process, { concurrency: maxConcurrency });
2069
+ consumer = deps.queue.consume(executeQueuedRun, { concurrency: maxConcurrency });
2056
2070
  },
2057
2071
  async stop() {
2058
2072
  if (!consumer) return;
2059
2073
  const active = consumer;
2060
2074
  consumer = void 0;
2061
2075
  let drained = false;
2062
- const graceful = active.close().then(() => {
2063
- drained = true;
2064
- });
2065
- await Promise.race([graceful, sleep(shutdownGraceMs)]);
2076
+ let drainFailure;
2077
+ const graceful = active.close().then(
2078
+ () => {
2079
+ drained = true;
2080
+ },
2081
+ (error) => {
2082
+ drainFailure = error;
2083
+ }
2084
+ );
2085
+ const deadline = cancellableDelay(shutdownGraceMs);
2086
+ try {
2087
+ await Promise.race([graceful, deadline.elapsed]);
2088
+ } finally {
2089
+ deadline.cancel();
2090
+ }
2066
2091
  if (!drained) {
2067
2092
  for (const runId of inFlight) control.abort(runId, "cancel");
2068
2093
  await graceful;
2094
+ await Promise.allSettled([...running]);
2095
+ }
2096
+ if (drainFailure !== void 0) {
2097
+ deps.logger.warn("Run queue did not close cleanly during shutdown", {
2098
+ error: drainFailure instanceof Error ? drainFailure.message : String(drainFailure)
2099
+ });
2069
2100
  }
2070
2101
  }
2071
2102
  };
@@ -2315,6 +2346,8 @@ function createGraphInvokeHandler(deps, options = {}) {
2315
2346
  }
2316
2347
  export {
2317
2348
  DEFAULT_INVOKE_PREFIX,
2349
+ DEFAULT_RUN_CONCURRENCY,
2350
+ DEFAULT_SHUTDOWN_GRACE_MS,
2318
2351
  SSE_HEADERS,
2319
2352
  SkeinBaseStore,
2320
2353
  buildProtocolService,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skein-js/agent-protocol",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Framework-agnostic Agent Protocol engine for LangGraph.js — run engine, handlers, and SSE, driven entirely by injected dependencies.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Maina Wycliffe <wmmaina7@gmail.com>",
@@ -30,7 +30,8 @@
30
30
  "exports": {
31
31
  ".": {
32
32
  "types": "./dist/index.d.ts",
33
- "import": "./dist/index.js"
33
+ "import": "./dist/index.js",
34
+ "default": "./dist/index.js"
34
35
  }
35
36
  },
36
37
  "files": [
@@ -44,14 +45,14 @@
44
45
  },
45
46
  "dependencies": {
46
47
  "zod": "^3.25.76",
47
- "@skein-js/core": "0.9.0"
48
+ "@skein-js/core": "0.10.0"
48
49
  },
49
50
  "peerDependencies": {
50
51
  "@langchain/langgraph": "^1.4.0",
51
52
  "@langchain/langgraph-sdk": "^1.9.0"
52
53
  },
53
54
  "devDependencies": {
54
- "@skein-js/storage-memory": "0.9.0"
55
+ "@skein-js/storage-memory": "0.10.0"
55
56
  },
56
57
  "publishConfig": {
57
58
  "access": "public"