@skein-js/core 0.4.0 → 0.6.3

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
@@ -79,8 +79,8 @@ throw SkeinHttpError.notFound(`Thread "${id}" not found.`);
79
79
  `RunConsumerOptions`.
80
80
  - **`interface AuthEngine`** — `authenticate(request)` (→ `AuthContext`, throws 401) + `authorize({ resource, action, value, context })` (→ `{ filters?, value }`, throws 403) + `matchesFilters(...)`. Plus `AuthContext`, `AuthUser`, `AuthResource`, `AuthAction`, `AuthFilters`, `AuthFilterValue`.
81
81
  - **`class SkeinHttpError`** — `new SkeinHttpError(status, message, options?)` and the static helpers
82
- `badRequest` (400) / `unauthorized` (401) / `forbidden` (403) / `notFound` (404) / `conflict` (409);
83
- `isSkeinHttpError(value)` narrows it.
82
+ `badRequest` (400) / `unauthorized` (401) / `forbidden` (403) / `notFound` (404) / `conflict` (409) /
83
+ `unprocessable` (422); `isSkeinHttpError(value)` narrows it.
84
84
  - **`serializeWireJson(value): string`** — `JSON.stringify` replacement that flattens LangChain
85
85
  `BaseMessage`s to the wire shape the SDK / `useStream` / Agent Chat UI expect.
86
86
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { Run as Run$1, Config, Metadata, Assistant, StreamMode, Thread, DefaultValues, ThreadStatus, Interrupt, Item, SearchItem } from '@langchain/langgraph-sdk';
2
- export { Assistant, AssistantBase, AssistantGraph, Checkpoint, Config, DefaultValues, GraphSchema, Interrupt, Item, Metadata, SearchItem, StreamMode, Thread, ThreadState, ThreadStatus, ThreadTask } from '@langchain/langgraph-sdk';
1
+ import { Run as Run$1, Config, Metadata, Assistant, AssistantVersion, StreamMode, Thread, DefaultValues, ThreadStatus, Interrupt, Item, SearchItem } from '@langchain/langgraph-sdk';
2
+ export { Assistant, AssistantBase, AssistantGraph, AssistantVersion, Checkpoint, Config, DefaultValues, GraphSchema, Interrupt, Item, Metadata, SearchItem, StreamMode, Thread, ThreadState, ThreadStatus, ThreadTask } from '@langchain/langgraph-sdk';
3
3
 
4
4
  type RunStatus = Run$1["status"] | "cancelled";
5
5
  type Run = Omit<Run$1, "status"> & {
@@ -7,6 +7,66 @@ type Run = Omit<Run$1, "status"> & {
7
7
  };
8
8
  type MultitaskStrategy = NonNullable<Run$1["multitask_strategy"]>;
9
9
 
10
+ /**
11
+ * The authenticated caller, normalized to the shape LangGraph's `Auth` produces: a required
12
+ * `identity`, `display_name`, `is_authenticated`, and `permissions` scopes, plus open extra keys a
13
+ * handler may attach (e.g. `email`, `org_id`). Structurally identical to the SDK's internal
14
+ * `BaseUser`, so a graph written for LangGraph Platform reads the same `langgraph_auth_user`.
15
+ */
16
+ interface AuthUser {
17
+ identity: string;
18
+ display_name: string;
19
+ is_authenticated: boolean;
20
+ permissions: string[];
21
+ [key: string]: unknown;
22
+ }
23
+ /** The result of authenticating a request: the caller plus their permission scopes. */
24
+ interface AuthContext {
25
+ user: AuthUser;
26
+ scopes: string[];
27
+ }
28
+ /** A protocol resource an `@auth.on.*` handler can guard. Runs authorize through their thread. */
29
+ type AuthResource = "threads" | "assistants" | "store";
30
+ /** An action on a resource, mirroring LangGraph's `resource:action` event names. */
31
+ type AuthAction = "create" | "read" | "update" | "delete" | "search" | "create_run" | "put" | "get" | "list_namespaces";
32
+ /** A single metadata filter clause — an exact value or an `$eq`/`$contains` operator object. */
33
+ type AuthFilterValue = string | {
34
+ $eq?: string;
35
+ $contains?: string | string[];
36
+ };
37
+ /**
38
+ * Ownership filters returned by an `@auth.on.*` handler. Keys are metadata fields; a returned filter
39
+ * both scopes reads (a resource is visible only if its metadata satisfies every clause) and stamps
40
+ * writes (the clause values are merged into new resources' metadata). Shape-compatible with
41
+ * `@langchain/langgraph-api`'s `isAuthMatching`.
42
+ */
43
+ type AuthFilters = Record<string, AuthFilterValue>;
44
+ /**
45
+ * The injectable auth engine — one per runtime, no module-global state (unlike langgraph-api's
46
+ * `registerAuth`). Absent from `ProtocolDeps` means no auth is configured and every request is
47
+ * allowed (the default, so `skein dev` stays frictionless).
48
+ */
49
+ interface AuthEngine {
50
+ /** True when a `langgraph.json` `auth.path` was loaded; false disables the whole gate. */
51
+ readonly enabled: boolean;
52
+ /** When true, studio traffic must present real credentials like any other client. */
53
+ readonly studioAuthDisabled: boolean;
54
+ /** Run the user's authenticate handler over a request; throws 401 on rejection/missing identity. */
55
+ authenticate(request: Request): Promise<AuthContext | undefined>;
56
+ /** Consult the `@auth.on.*` handlers for a resource+action; throws 403 on deny, else returns filters. */
57
+ authorize(input: {
58
+ resource: AuthResource;
59
+ action: AuthAction;
60
+ value: unknown;
61
+ context: AuthContext | undefined;
62
+ }): Promise<{
63
+ filters?: AuthFilters;
64
+ value: unknown;
65
+ }>;
66
+ /** Whether a resource's metadata satisfies the ownership filters (`$eq`/`$contains` semantics). */
67
+ matchesFilters(metadata: Record<string, unknown> | undefined, filters?: AuthFilters): boolean;
68
+ }
69
+
10
70
  /** Fields accepted when registering an assistant (from a langgraph.json graph or the API). */
11
71
  interface AssistantCreate {
12
72
  graph_id: string;
@@ -18,11 +78,67 @@ interface AssistantCreate {
18
78
  context?: unknown;
19
79
  metadata?: Metadata;
20
80
  }
81
+ /**
82
+ * Partial update; omitted fields keep the current version's value. Every update mints a NEW
83
+ * immutable version (see {@link AssistantRepo.update}) — there is no in-place field mutation.
84
+ */
85
+ interface AssistantUpdate {
86
+ graph_id?: string;
87
+ name?: string;
88
+ description?: string;
89
+ config?: Config;
90
+ context?: unknown;
91
+ metadata?: Metadata;
92
+ }
93
+ /** Filter + pagination for `POST /assistants/search`. Omitted fields don't constrain the result. */
94
+ interface AssistantSearchQuery {
95
+ /** Restrict to assistants of this graph. */
96
+ graph_id?: string;
97
+ /** Restrict to assistants with this exact name. */
98
+ name?: string;
99
+ /** Match assistants whose metadata contains every one of these key/value pairs (subset match). */
100
+ metadata?: Metadata;
101
+ limit?: number;
102
+ offset?: number;
103
+ /** Sort key; defaults to `created_at`. */
104
+ sortBy?: "assistant_id" | "graph_id" | "name" | "created_at" | "updated_at";
105
+ /** Sort direction; defaults to `desc`. */
106
+ sortOrder?: "asc" | "desc";
107
+ }
108
+ /** Filter + pagination for `POST /assistants/{id}/versions`. */
109
+ interface AssistantVersionsQuery {
110
+ /** Match versions whose metadata contains every one of these key/value pairs (subset match). */
111
+ metadata?: Metadata;
112
+ limit?: number;
113
+ offset?: number;
114
+ }
21
115
  interface AssistantRepo {
22
116
  list(): Promise<Assistant[]>;
117
+ /** Filtered + paginated listing backing `POST /assistants/search`. */
118
+ search(query: AssistantSearchQuery): Promise<Assistant[]>;
119
+ /** Number of assistants matching `query` (ignores limit/offset), backing `POST /assistants/count`. */
120
+ count(query: AssistantSearchQuery): Promise<number>;
23
121
  get(assistantId: string): Promise<Assistant | null>;
24
- /** Create (or, for a graph-derived assistant, register) an assistant. */
122
+ /**
123
+ * Create an assistant, seeding version 1 (the live row and its first {@link AssistantVersion}
124
+ * snapshot are written together). Throws `SkeinHttpError.conflict` (409) when `assistant_id` is
125
+ * already taken — the service layer turns that into `if_exists` handling, and callers that want
126
+ * idempotent registration (e.g. graph auto-registration) get-before-create and tolerate the 409.
127
+ */
25
128
  create(input: AssistantCreate): Promise<Assistant>;
129
+ /**
130
+ * Apply a partial patch by minting a NEW version: snapshot the current fields with `patch` applied,
131
+ * bump the live row to those fields + the new version number. Throws `SkeinHttpError.notFound` when
132
+ * the assistant is unknown. Returns the (now-active) assistant.
133
+ */
134
+ update(assistantId: string, patch: AssistantUpdate): Promise<Assistant>;
135
+ /** Version history, newest-first, filtered + paginated. Empty when the assistant is unknown. */
136
+ listVersions(assistantId: string, query?: AssistantVersionsQuery): Promise<AssistantVersion[]>;
137
+ /**
138
+ * Roll the live row back to an existing version's snapshot (no new version is minted). Throws
139
+ * `SkeinHttpError.notFound` when the assistant or the target version is unknown.
140
+ */
141
+ setLatest(assistantId: string, version: number): Promise<Assistant>;
26
142
  delete(assistantId: string): Promise<void>;
27
143
  }
28
144
  interface ThreadCreate {
@@ -96,6 +212,24 @@ interface RunKwargs {
96
212
  stream_mode?: StreamMode | StreamMode[];
97
213
  interrupt_before?: string[] | "*";
98
214
  interrupt_after?: string[] | "*";
215
+ /**
216
+ * Optional run-completion webhook URL (absolute `http(s)`). When set, the run engine POSTs the
217
+ * settled run (status + final values) to it once the run reaches a terminal status — matching
218
+ * `@langchain/langgraph-api`. Persisted opaquely so a background/crash-recovered run still fires.
219
+ */
220
+ webhook?: string;
221
+ /**
222
+ * The authenticated caller, stamped by the server (never accepted from the client). Persisted
223
+ * opaquely with the run so a background/crash-recovered run on another instance reconstructs the
224
+ * principal via `getKwargs` and injects it into the graph's `configurable.langgraph_auth_user`.
225
+ */
226
+ auth_user?: AuthUser;
227
+ /**
228
+ * The caller's authenticated permission scopes (the `AuthContext.scopes`), stamped alongside
229
+ * {@link auth_user}. Injected as `configurable.langgraph_auth_permissions`, matching LangGraph
230
+ * (which sources permissions from the auth scopes, not from the user object's `permissions`).
231
+ */
232
+ auth_scopes?: string[];
99
233
  }
100
234
  interface RunCreate {
101
235
  thread_id: string;
@@ -124,6 +258,13 @@ interface RunRepo {
124
258
  * as a fresh run. The run engine uses this to reject/queue concurrent runs.
125
259
  */
126
260
  hasActiveRun(threadId: string): Promise<boolean>;
261
+ /**
262
+ * The thread's *inflight* runs — those still `pending` or `running` (non-terminal per
263
+ * {@link isTerminalRunStatus}), the same set {@link hasActiveRun} counts. The multitask engine
264
+ * reads these to `interrupt`/`rollback` them when a second run arrives mid-run. Order is
265
+ * unspecified.
266
+ */
267
+ listActiveRuns(threadId: string): Promise<Run[]>;
127
268
  }
128
269
  /**
129
270
  * Run statuses from which a run never transitions again. `"interrupted"` is terminal: resuming an
@@ -179,8 +320,9 @@ interface SkeinStore {
179
320
  * A driver-agnostic, JSON-serializable snapshot of every resource row — the unit of bulk
180
321
  * transfer for persistence and migration tooling (e.g. `skein dev`'s cross-restart snapshot, and
181
322
  * importing an existing LangGraph in-memory dev state). Each entry is an `[id, row]` tuple: the id
182
- * is the entity's own id, except `items` (keyed by `JSON.stringify([namespace, key])`) and
183
- * `runKwargs` (keyed by `run_id`, since {@link RunKwargs} has no id of its own).
323
+ * is the entity's own id, except `items` (keyed by `JSON.stringify([namespace, key])`),
324
+ * `runKwargs` (keyed by `run_id`, since {@link RunKwargs} has no id of its own), and
325
+ * `assistantVersions` (keyed by `JSON.stringify([assistant_id, version])`).
184
326
  *
185
327
  * A driver MAY expose `restore(snapshot)` to bulk-load one of these verbatim — ids and timestamps
186
328
  * preserved — which is what makes an import lossless. It is intentionally not part of
@@ -188,6 +330,7 @@ interface SkeinStore {
188
330
  */
189
331
  interface SkeinStoreSnapshot {
190
332
  assistants: [string, Assistant][];
333
+ assistantVersions: [string, AssistantVersion][];
191
334
  threads: [string, Thread][];
192
335
  runs: [string, Run][];
193
336
  runKwargs: [string, RunKwargs][];
@@ -289,66 +432,13 @@ declare class SkeinHttpError extends Error {
289
432
  static notFound(message: string, options?: SkeinHttpErrorOptions): SkeinHttpError;
290
433
  /** 409 — the request conflicts with current state (e.g. a thread already has an active run). */
291
434
  static conflict(message: string, options?: SkeinHttpErrorOptions): SkeinHttpError;
435
+ /**
436
+ * 422 — the request was well-formed but can't be processed in the current state. Used for a
437
+ * `reject` multitask strategy hitting a busy thread, matching `@langchain/langgraph-api`.
438
+ */
439
+ static unprocessable(message: string, options?: SkeinHttpErrorOptions): SkeinHttpError;
292
440
  }
293
441
  /** Narrow an unknown thrown value to a {@link SkeinHttpError}. */
294
442
  declare function isSkeinHttpError(value: unknown): value is SkeinHttpError;
295
443
 
296
- /**
297
- * The authenticated caller, normalized to the shape LangGraph produces: a required `identity`,
298
- * `permissions` scopes, and open extra keys a handler may attach (e.g. `email`, `org_id`).
299
- */
300
- interface AuthUser {
301
- identity: string;
302
- display_name: string;
303
- is_authenticated: boolean;
304
- permissions: string[];
305
- [key: string]: unknown;
306
- }
307
- /** The result of authenticating a request: the caller plus their permission scopes. */
308
- interface AuthContext {
309
- user: AuthUser;
310
- scopes: string[];
311
- }
312
- /** A protocol resource an `@auth.on.*` handler can guard. Runs authorize through their thread. */
313
- type AuthResource = "threads" | "assistants" | "store";
314
- /** An action on a resource, mirroring LangGraph's `resource:action` event names. */
315
- type AuthAction = "create" | "read" | "update" | "delete" | "search" | "create_run" | "put" | "get" | "list_namespaces";
316
- /** A single metadata filter clause — an exact value or an `$eq`/`$contains` operator object. */
317
- type AuthFilterValue = string | {
318
- $eq?: string;
319
- $contains?: string | string[];
320
- };
321
- /**
322
- * Ownership filters returned by an `@auth.on.*` handler. Keys are metadata fields; a returned filter
323
- * both scopes reads (a resource is visible only if its metadata satisfies every clause) and stamps
324
- * writes (the clause values are merged into new resources' metadata). Shape-compatible with
325
- * `@langchain/langgraph-api`'s `isAuthMatching`.
326
- */
327
- type AuthFilters = Record<string, AuthFilterValue>;
328
- /**
329
- * The injectable auth engine — one per runtime, no module-global state (unlike langgraph-api's
330
- * `registerAuth`). Absent from `ProtocolDeps` means no auth is configured and every request is
331
- * allowed (the default, so `skein dev` stays frictionless).
332
- */
333
- interface AuthEngine {
334
- /** True when a `langgraph.json` `auth.path` was loaded; false disables the whole gate. */
335
- readonly enabled: boolean;
336
- /** When true, studio traffic must present real credentials like any other client. */
337
- readonly studioAuthDisabled: boolean;
338
- /** Run the user's authenticate handler over a request; throws 401 on rejection/missing identity. */
339
- authenticate(request: Request): Promise<AuthContext | undefined>;
340
- /** Consult the `@auth.on.*` handlers for a resource+action; throws 403 on deny, else returns filters. */
341
- authorize(input: {
342
- resource: AuthResource;
343
- action: AuthAction;
344
- value: unknown;
345
- context: AuthContext | undefined;
346
- }): Promise<{
347
- filters?: AuthFilters;
348
- value: unknown;
349
- }>;
350
- /** Whether a resource's metadata satisfies the ownership filters (`$eq`/`$contains` semantics). */
351
- matchesFilters(metadata: Record<string, unknown> | undefined, filters?: AuthFilters): boolean;
352
- }
353
-
354
- export { type AssistantCreate, type AssistantRepo, type AuthAction, type AuthContext, type AuthEngine, type AuthFilterValue, type AuthFilters, type AuthResource, type AuthUser, type MultitaskStrategy, type QueuedRun, type Run, type RunConsumer, type RunConsumerOptions, type RunCreate, type RunEventBus, type RunFrame, type RunKwargs, type RunProcessor, type RunQueue, type RunRepo, type RunStatus, SkeinHttpError, type SkeinHttpErrorOptions, type SkeinStore, type SkeinStoreSnapshot, type StorePutOptions, type StoreRepo, type StoreSearchQuery, type StoreTtlConfig, TERMINAL_RUN_STATUSES, type ThreadCreate, type ThreadRepo, type ThreadSearchQuery, type ThreadUpdate, isMetadataSubset, isSkeinHttpError, isTerminalRunStatus, serializeWireJson };
444
+ export { type AssistantCreate, type AssistantRepo, type AssistantSearchQuery, type AssistantUpdate, type AssistantVersionsQuery, type AuthAction, type AuthContext, type AuthEngine, type AuthFilterValue, type AuthFilters, type AuthResource, type AuthUser, type MultitaskStrategy, type QueuedRun, type Run, type RunConsumer, type RunConsumerOptions, type RunCreate, type RunEventBus, type RunFrame, type RunKwargs, type RunProcessor, type RunQueue, type RunRepo, type RunStatus, SkeinHttpError, type SkeinHttpErrorOptions, type SkeinStore, type SkeinStoreSnapshot, type StorePutOptions, type StoreRepo, type StoreSearchQuery, type StoreTtlConfig, TERMINAL_RUN_STATUSES, type ThreadCreate, type ThreadRepo, type ThreadSearchQuery, type ThreadUpdate, isMetadataSubset, isSkeinHttpError, isTerminalRunStatus, serializeWireJson };
package/dist/index.js CHANGED
@@ -76,6 +76,13 @@ var SkeinHttpError = class _SkeinHttpError extends Error {
76
76
  static conflict(message, options) {
77
77
  return new _SkeinHttpError(409, message, options);
78
78
  }
79
+ /**
80
+ * 422 — the request was well-formed but can't be processed in the current state. Used for a
81
+ * `reject` multitask strategy hitting a busy thread, matching `@langchain/langgraph-api`.
82
+ */
83
+ static unprocessable(message, options) {
84
+ return new _SkeinHttpError(422, message, options);
85
+ }
79
86
  };
80
87
  function isSkeinHttpError(value) {
81
88
  return value instanceof SkeinHttpError;
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@skein-js/core",
3
- "version": "0.4.0",
3
+ "version": "0.6.3",
4
4
  "description": "Framework-agnostic Agent Protocol engine for LangGraph.js — the heart of skein-js.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Maina Wycliffe <wmmaina7@gmail.com>",
7
- "homepage": "https://github.com/mainawycliffe/skein-js/tree/main/packages/core#readme",
7
+ "homepage": "https://github.com/skein-js/skein-js/tree/main/packages/core#readme",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "git+https://github.com/mainawycliffe/skein-js.git",
10
+ "url": "git+https://github.com/skein-js/skein-js.git",
11
11
  "directory": "packages/core"
12
12
  },
13
13
  "bugs": {
14
- "url": "https://github.com/mainawycliffe/skein-js/issues"
14
+ "url": "https://github.com/skein-js/skein-js/issues"
15
15
  },
16
16
  "keywords": [
17
17
  "typescript",