nexus-ai-pro 1.20.0 → 1.21.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.
Files changed (48) hide show
  1. package/API_STABILITY.md +15 -2
  2. package/CHANGELOG.md +44 -1
  3. package/README.md +1 -0
  4. package/dist/graph/functional.d.ts +217 -0
  5. package/dist/graph/functional.js +431 -0
  6. package/dist/graph/layout.d.ts +55 -0
  7. package/dist/graph/layout.js +68 -0
  8. package/dist/graph/visualize.d.ts +32 -0
  9. package/dist/graph/visualize.js +59 -0
  10. package/dist/security/index.d.ts +6 -4
  11. package/dist/security/index.js +8 -6
  12. package/dist/server/assistant.d.ts +11 -0
  13. package/dist/server/assistant.js +14 -0
  14. package/dist/server/runs.js +9 -4
  15. package/dist/sqlite/client.d.ts +63 -0
  16. package/dist/sqlite/client.js +52 -0
  17. package/dist/sqlite/index.d.ts +3 -0
  18. package/dist/sqlite/index.js +3 -0
  19. package/dist/sqlite/operations.d.ts +52 -0
  20. package/dist/sqlite/operations.js +111 -0
  21. package/dist/sqlite/store.d.ts +49 -0
  22. package/dist/sqlite/store.js +131 -0
  23. package/dist/types/server.d.ts +9 -0
  24. package/dist/workflow/chains.d.ts +3 -2
  25. package/dist/workflow/chains.js +1 -0
  26. package/dist-cjs/graph/functional.d.ts +217 -0
  27. package/dist-cjs/graph/functional.js +437 -0
  28. package/dist-cjs/graph/layout.d.ts +55 -0
  29. package/dist-cjs/graph/layout.js +71 -0
  30. package/dist-cjs/graph/visualize.d.ts +32 -0
  31. package/dist-cjs/graph/visualize.js +62 -0
  32. package/dist-cjs/security/index.d.ts +6 -4
  33. package/dist-cjs/security/index.js +16 -10
  34. package/dist-cjs/server/assistant.d.ts +11 -0
  35. package/dist-cjs/server/assistant.js +14 -0
  36. package/dist-cjs/server/runs.js +9 -4
  37. package/dist-cjs/sqlite/client.d.ts +63 -0
  38. package/dist-cjs/sqlite/client.js +59 -0
  39. package/dist-cjs/sqlite/index.d.ts +3 -0
  40. package/dist-cjs/sqlite/index.js +12 -0
  41. package/dist-cjs/sqlite/operations.d.ts +52 -0
  42. package/dist-cjs/sqlite/operations.js +116 -0
  43. package/dist-cjs/sqlite/store.d.ts +49 -0
  44. package/dist-cjs/sqlite/store.js +136 -0
  45. package/dist-cjs/types/server.d.ts +9 -0
  46. package/dist-cjs/workflow/chains.d.ts +3 -2
  47. package/dist-cjs/workflow/chains.js +6 -0
  48. package/package.json +54 -2
package/API_STABILITY.md CHANGED
@@ -439,8 +439,9 @@ from the root.
439
439
  that reconnects with an id never receives it again.
440
440
  - The server is not a security boundary on its own: authentication, authorization beyond tenancy and
441
441
  scopes, and rate limiting are the application's, through the hooks.
442
- - Recovery re-runs a whole run rather than the step it died on, so an assistant with side effects
443
- should be idempotent or be left at a single attempt.
442
+ - Recovery repeats the step a run died in for graphs and workflows served by `graphAssistant()`, and
443
+ the whole run for any other assistant, so the repeated part should be idempotent or the run left at a
444
+ single attempt.
444
445
 
445
446
  ## Studio stage (1.19.0)
446
447
 
@@ -455,6 +456,18 @@ with nexus-ai-pro and declares the core versions it works with as a peer depende
455
456
  - The access rules are a guarantee: loopback binding by default, a token on every request, the token
456
457
  in a header for every change, and loopback hosts only unless others are allowed.
457
458
 
459
+ ## Durable execution stage (1.21.0)
460
+
461
+ Functional workflows (`nexus-ai-pro/graph/functional`) and the SQLite adapters (`nexus-ai-pro/sqlite`)
462
+ are experimental in production readiness. Their contracts follow the 1.x rules.
463
+
464
+ - A workflow checkpoint is a graph checkpoint whose state holds the input, the recorded step results
465
+ keyed by step name and call order, and the output. That layout is stable across 1.x, so a thread
466
+ started by one release resumes on the next.
467
+ - The SQLite table layouts may gain columns in a minor release; `migrate()` adds what is missing.
468
+ - `ServerAssistant.recover()` and `AssistantRunContext.attempt` are optional additions: an assistant
469
+ without `recover()` behaves as before.
470
+
458
471
  ## Retrieval stores stage (1.20.0)
459
472
 
460
473
  `PostgresVectorStore` (`nexus-ai-pro/postgres/vectors`) and `QdrantVectorStore` (`nexus-ai-pro/rag/qdrant`)
package/CHANGELOG.md CHANGED
@@ -4,6 +4,48 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.21.0] - 2026-09-29
8
+
9
+ Durable execution everywhere. Durability no longer requires drawing a graph, and a crash costs a
10
+ step rather than a run.
11
+
12
+ ### Added
13
+
14
+ - **Functional workflows**, experimental, on `nexus-ai-pro/graph/functional`. `workflow()` makes an
15
+ ordinary async function durable: each `step()` records its result, so a workflow that is resumed,
16
+ continued, or recovered on another worker returns finished steps from its checkpoint and carries on
17
+ from the first that did not finish. Steps run in parallel through `Promise.all`, bounded by
18
+ `maxConcurrency`, with per-step retries and timeouts; `interrupt()` pauses for a person exactly as in
19
+ a graph node; and the checkpoints are ordinary graph checkpoints, so every checkpointer stores
20
+ them, `traceGraph()` records them, and `graphAssistant()` serves a workflow on the agent server.
21
+ - **Step-level recovery in the agent server.** A run recovered after its worker died continues from
22
+ the last checkpoint that run wrote — for graphs and workflows served by `graphAssistant()` — so
23
+ only the step in flight runs again. `AssistantRunContext.attempt` reports the attempt, and the new
24
+ optional `ServerAssistant.recover()` hook is how an assistant continues.
25
+ - **SQLite persistence** on `nexus-ai-pro/sqlite`: `SqliteOperationStore` for durable operations and,
26
+ through `OperationStoreCheckpointer`, graph and workflow checkpoints; and `SqliteStore` for
27
+ long-term memory. Both take the database you open — `node:sqlite` and `better-sqlite3` directly,
28
+ libSQL through `fromLibsql()` — so no driver is a dependency, and both pass the same contract tests as
29
+ the memory, Redis, and Postgres stores.
30
+ - **Diagrams as images.** `toSvg()` on `nexus-ai-pro/graph/visualize` draws a graph as a standalone,
31
+ escaped SVG document with no dependency. `layoutGraph()`, the layered layout underneath, moved from
32
+ the studio into the core visualizer, so both draw the same diagram.
33
+
34
+ ### Changed
35
+
36
+ - The server reads a streamed event's `output` before its `state` when it records a run's output, so a
37
+ workflow's return value is the run's output.
38
+ - `nexus-ai-pro-studio` requires nexus-ai-pro 1.21 or newer, and re-exports `layoutGraph()` from the
39
+ core visualizer.
40
+
41
+ ### Fixed
42
+
43
+ - `guardrailPolicy()`, `GUARDRAIL_POLICIES`, `calibrateSemanticInjectionClassifier()`, and
44
+ `SEMANTIC_INJECTION_CALIBRATION_SET` are exported from `nexus-ai-pro/security`, and the four domain
45
+ workflows such as `supportTriageWorkflow()` from `nexus-ai-pro/workflows`, as their guides already
46
+ showed. They were reachable only from the package root. A new docs check fails when a guide's
47
+ example imports a name from an entry point that does not export it.
48
+
7
49
  ## [1.20.0] - 2026-09-28
8
50
 
9
51
  Retrieval that scales past one process. Deprecated options now say so at run time, and every guide
@@ -1043,7 +1085,8 @@ shared policy vocabulary is in place for a future release that revisits this.
1043
1085
  - URL fetching rejects unsafe private-network targets and limits response reads.
1044
1086
  - CLI and audit findings no longer disclose detected secret values.
1045
1087
 
1046
- [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.20.0...HEAD
1088
+ [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.21.0...HEAD
1089
+ [1.21.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.20.0...v1.21.0
1047
1090
  [1.20.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.19.0...v1.20.0
1048
1091
  [1.19.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.18.0...v1.19.0
1049
1092
  [1.18.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.17.0...v1.18.0
package/README.md CHANGED
@@ -114,6 +114,7 @@ from the doc comments. The guides live in the repository, so these links go to G
114
114
  | [Evaluation](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/evaluation.md) | Datasets, evaluators, experiments, comparisons with a verdict, review queues, LLM judges |
115
115
  | [Prompts](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/prompts.md) | Typed templates, content versions, gated promotion, rollback, A/B splits, serving through outages |
116
116
  | [Postgres](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/postgres.md) | One adapter family for operations, memory, traces, evaluation, circuits, and prompts |
117
+ | [SQLite](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/sqlite.md) | Durable operations, checkpoints, and memory on one machine, over any SQLite driver |
117
118
  | [Durable operations and jobs](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/operations.md) | Operations that survive a restart, webhooks, and in-process job helpers |
118
119
  | [Resilience and observability](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/resilience.md) | Circuit breaking, distributed rate limits, metrics, logs, and audit |
119
120
  | [Batch tiers](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/batch.md) | The providers' discounted batch APIs, resumable from any process |
@@ -0,0 +1,217 @@
1
+ import { GraphError } from './errors.js';
2
+ import type { GraphCheckpoint, GraphCheckpointer, GraphDescription, GraphEvent, GraphStatus, InterruptRequest, PendingInterrupt, RetryPolicy } from '../types/graph.js';
3
+ import type { Store } from '../types/store.js';
4
+ /** What a workflow checkpoint holds: the input, every completed step's result, and the output. */
5
+ export interface WorkflowState<I = unknown, O = unknown> {
6
+ /** The input the workflow was started with. */
7
+ input: I;
8
+ /** Each completed step's result, keyed by step name — `name#2` for the second call with a name. */
9
+ steps: Record<string, {
10
+ value?: unknown;
11
+ }>;
12
+ /** The workflow's return value, once it has completed. */
13
+ output?: O;
14
+ }
15
+ /** What a step's function receives. */
16
+ export interface StepContext {
17
+ /** The step's name. */
18
+ readonly name: string;
19
+ /** The key its result is recorded under: the name, or `name#n` for a repeated name. */
20
+ readonly key: string;
21
+ /** Attempt number, starting at 1. Above 1 only under a retry policy. */
22
+ readonly attempt: number;
23
+ /** Aborted when the run is cancelled or the step exceeds its timeout. */
24
+ readonly signal: AbortSignal;
25
+ }
26
+ /** How one step runs. */
27
+ export interface StepOptions {
28
+ /** Retries a failing attempt, overriding the workflow's default policy. */
29
+ retry?: RetryPolicy;
30
+ /** Aborts the step's signal and fails the attempt when it runs longer than this, in milliseconds. */
31
+ timeoutMs?: number;
32
+ }
33
+ /** What a workflow function receives besides its input. */
34
+ export interface WorkflowContext {
35
+ /** The thread this run belongs to. */
36
+ readonly threadId: string;
37
+ /** Aborted when the run is cancelled. */
38
+ readonly signal: AbortSignal;
39
+ /** Long-term memory, when the workflow was created with a store. */
40
+ readonly store?: Store;
41
+ /**
42
+ * Runs a piece of work once per thread and records its result.
43
+ *
44
+ * When the workflow runs again, a step that already finished returns its recorded result without
45
+ * running. Name steps so the same call has the same name every time; a name used more than once
46
+ * is numbered in call order. The result must survive JSON serialization.
47
+ */
48
+ step<T>(name: string, run: (context: StepContext) => Promise<T> | T, options?: StepOptions): Promise<T>;
49
+ /**
50
+ * Suspends the workflow until a person answers.
51
+ *
52
+ * The first time, the run stops with status `awaiting_input` and this question pending. After
53
+ * `resume()` supplies a value, the workflow runs again from the top — completed steps return their
54
+ * recorded results — and this call returns the value instead of stopping.
55
+ */
56
+ interrupt<T = unknown>(request: InterruptRequest): T;
57
+ /** Sends anything to the run's `onEvent` listener as a `custom` event. Nothing is recorded. */
58
+ emit(data: unknown): void;
59
+ }
60
+ /** A workflow function: its input and context in, its output out. */
61
+ export type WorkflowFn<I, O> = (input: I, context: WorkflowContext) => Promise<O> | O;
62
+ /** Options for `workflow()`. */
63
+ export interface WorkflowOptions {
64
+ /** Names the workflow in checkpoints, events, and diagrams. Defaults to `workflow`. */
65
+ name?: string;
66
+ /**
67
+ * Where checkpoints go. Defaults to an in-process `MemoryGraphCheckpointer`; pass a persistent one
68
+ * to survive restarts, or `false` to record nothing, which also disables interrupts and resume.
69
+ */
70
+ checkpointer?: GraphCheckpointer | false;
71
+ /** Long-term memory handed to the function as `context.store`. */
72
+ store?: Store;
73
+ /** Default retry policy for every step. A step's own policy wins. Defaults to one attempt. */
74
+ retry?: RetryPolicy;
75
+ /** Steps that may run at once, for a workflow that starts many with `Promise.all`. Defaults to 16. */
76
+ maxConcurrency?: number;
77
+ /** Replaces the system clock, for tests. */
78
+ now?: () => Date;
79
+ }
80
+ /** Options for one run of a workflow. */
81
+ export interface WorkflowRunOptions {
82
+ /** Identifies the run. Reusing one continues that thread. Defaults to a generated id. */
83
+ threadId?: string;
84
+ /** Stops the run. Steps in flight see the signal. */
85
+ signal?: AbortSignal;
86
+ /** Application data recorded on every checkpoint of the run. */
87
+ metadata?: Record<string, unknown>;
88
+ /** Receives each step starting, retrying, and finishing, each checkpoint, and custom events. */
89
+ onEvent?: (event: GraphEvent) => void;
90
+ }
91
+ /** One event of a streamed workflow run. */
92
+ export type WorkflowEvent<O = unknown> = {
93
+ /** A step finished, or returned its recorded result. */
94
+ type: 'step';
95
+ /** Checkpoints written so far in the thread. */
96
+ step: number;
97
+ /** The step's name. */
98
+ name: string;
99
+ /** The key its result is recorded under. */
100
+ key: string;
101
+ /** True when the result came from a checkpoint and the step did not run. */
102
+ cached: boolean;
103
+ /** The workflow state after the step. */
104
+ state: WorkflowState<unknown, O>;
105
+ /** Always `running`. */
106
+ status: 'running';
107
+ } | {
108
+ /** The workflow returned. */
109
+ type: 'done';
110
+ /** Checkpoints written in the thread. */
111
+ step: number;
112
+ /** The final state. */
113
+ state: WorkflowState<unknown, O>;
114
+ /** Always `completed`. */
115
+ status: 'completed';
116
+ /** What the workflow returned. */
117
+ output: O;
118
+ } | {
119
+ /** The workflow is waiting for a person, or was stopped by its signal. */
120
+ type: 'interrupt';
121
+ /** Checkpoints written in the thread. */
122
+ step: number;
123
+ /** The state when it stopped. */
124
+ state: WorkflowState<unknown, O>;
125
+ /** `awaiting_input` for a question, `interrupted` for a cancelled run. */
126
+ status: 'awaiting_input' | 'interrupted';
127
+ /** The first pending question. */
128
+ interrupt?: PendingInterrupt;
129
+ /** Every pending question. */
130
+ interrupts?: PendingInterrupt[];
131
+ };
132
+ /** The outcome of a workflow run. */
133
+ export interface WorkflowResult<O = unknown> {
134
+ /** The thread the run belongs to. */
135
+ threadId: string;
136
+ /** `completed`, `awaiting_input`, or `interrupted`. A failed run throws instead. */
137
+ status: GraphStatus;
138
+ /** What the workflow returned, when it completed. */
139
+ output?: O;
140
+ /** The workflow state: input, step results, and output. */
141
+ state: WorkflowState<unknown, O>;
142
+ /** Steps whose results are recorded. */
143
+ steps: number;
144
+ /** The first pending question, when it is waiting. */
145
+ interrupt?: PendingInterrupt;
146
+ /** Every pending question. */
147
+ interrupts?: PendingInterrupt[];
148
+ }
149
+ /** Raised when a step outlives its `timeoutMs`. */
150
+ export declare class WorkflowStepTimeoutError extends GraphError {
151
+ /** The step. */
152
+ readonly stepName: string;
153
+ /** The limit it exceeded, in milliseconds. */
154
+ readonly timeoutMs: number;
155
+ constructor(
156
+ /** The step. */
157
+ stepName: string,
158
+ /** The limit it exceeded, in milliseconds. */
159
+ timeoutMs: number);
160
+ }
161
+ /**
162
+ * A durable workflow: a function whose steps are checkpointed, so it survives interrupts, restarts,
163
+ * and a move to another worker. Create one with `workflow()`.
164
+ */
165
+ export declare class Workflow<I = unknown, O = unknown> {
166
+ private readonly fn;
167
+ private readonly options;
168
+ /** The workflow's name. */
169
+ readonly name: string;
170
+ private readonly defaultCheckpointer;
171
+ private readonly now;
172
+ constructor(fn: WorkflowFn<I, O>, options?: WorkflowOptions);
173
+ /** Runs to the end, or to the first question, and returns the result. */
174
+ invoke(input: I, runOptions?: WorkflowRunOptions): Promise<WorkflowResult<O>>;
175
+ /** Runs, yielding an event as each step finishes. */
176
+ stream(input: I, runOptions?: WorkflowRunOptions): AsyncIterable<WorkflowEvent<O>>;
177
+ /** Answers the first pending question and runs on. */
178
+ resume(threadId: string, value: unknown, runOptions?: WorkflowRunOptions): AsyncIterable<WorkflowEvent<O>>;
179
+ /** Answers the first pending question, runs on, and returns the result. */
180
+ resumeWith(threadId: string, value: unknown, runOptions?: WorkflowRunOptions): Promise<WorkflowResult<O>>;
181
+ /** Answers pending questions by id and runs on. */
182
+ resumeInterrupts(threadId: string, answers: Record<string, unknown>, runOptions?: WorkflowRunOptions): AsyncIterable<WorkflowEvent<O>>;
183
+ /**
184
+ * Runs a thread on from its latest checkpoint: after a crash, a failure, or a cancelled run.
185
+ * Completed steps return their recorded results, so only unfinished work runs again.
186
+ */
187
+ continue(threadId: string, runOptions?: WorkflowRunOptions): AsyncIterable<WorkflowEvent<O>>;
188
+ /** The latest checkpoint, or the one at `step`. */
189
+ state(threadId: string, step?: number): Promise<GraphCheckpoint | undefined>;
190
+ /** Every checkpoint of a thread, newest first. */
191
+ history(threadId: string, limit?: number): Promise<GraphCheckpoint[]>;
192
+ /** The workflow as a one-node graph, so diagrams and the studio can show it. */
193
+ describe(): GraphDescription;
194
+ private checkpointer;
195
+ private latest;
196
+ private fresh;
197
+ private waiting;
198
+ private answered;
199
+ private collect;
200
+ /** Runs the function against a starting checkpoint, streaming events through a queue. */
201
+ private execute;
202
+ private run;
203
+ private runStep;
204
+ }
205
+ /**
206
+ * Creates a durable workflow from a function.
207
+ *
208
+ * ```ts
209
+ * const refund = workflow(async (input: { orderId: string }, { step, interrupt }) => {
210
+ * const order = await step('load', () => orders.get(input.orderId));
211
+ * if (!interrupt<boolean>({ reason: `Refund ${order.total}?` })) return { refunded: false };
212
+ * await step('refund', () => payments.refund(order.id));
213
+ * return { refunded: true };
214
+ * }, { checkpointer });
215
+ * ```
216
+ */
217
+ export declare function workflow<I, O>(fn: WorkflowFn<I, O>, options?: WorkflowOptions): Workflow<I, O>;