nexus-ai-pro 1.11.0 → 1.12.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/API_STABILITY.md CHANGED
@@ -313,6 +313,21 @@ Also since 1.11.0, all additive:
313
313
  - `context.taskId` and `context.attempt` are new. Step events carry `tasks` and `attempts` when there
314
314
  is something to report.
315
315
 
316
+ Also since 1.12.0, all additive:
317
+
318
+ - `Command` (with `Command.PARENT`) may be returned from a node; `NodeResult` names what a node may
319
+ return. `goto` routes are added to the node's outgoing edges, and a paused checkpoint records them in
320
+ the optional `gotos` field.
321
+ - `interruptBefore` and `interruptAfter` on compile and run options; a paused checkpoint and result
322
+ carry `breakpoint`, and step events may have type `breakpoint`.
323
+ - `NodeOptions.defer`, `GraphRunOptions.onEvent` with the `GraphEvent` union, and `context.emit()`.
324
+ New `GraphEvent` variants may be added in a minor release.
325
+ - `updateState()`, `fork()`, and `describe()` on a compiled graph, and the `nexus-ai-pro/graph/visualize`
326
+ subpath. The exact Mermaid text `toMermaid()` produces is not a guarantee — diagram layout may
327
+ improve in a minor release — but `GraphDescription` is a normalized shape.
328
+ - A subgraph node's thread id is `<parent thread>:<task id>`, which equals the previous
329
+ `<parent thread>:<node>` for any node not reached through `Send`.
330
+
316
331
  ## Deprecation process
317
332
 
318
333
  Deprecated APIs are marked with `@deprecated` in declarations and described in the changelog. Removals
package/CHANGELOG.md CHANGED
@@ -4,6 +4,45 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.12.0] - 2026-09-20
8
+
9
+ Graph control and introspection. A node can route itself, a run can pause at breakpoints and be
10
+ edited or forked, every task and custom event can be watched as it happens, and a graph can draw
11
+ itself.
12
+
13
+ ### Added
14
+
15
+ - **`Command`.** A node returns `new Command({ update, goto })` to write state and choose its
16
+ successors in one step, instead of splitting the decision into a separate router. `goto` takes node
17
+ names or `Send`s. A route chosen by a task that finished before its step paused is kept on the
18
+ checkpoint, so it is still followed when the step resumes.
19
+ - **`Command.PARENT`.** A node inside a subgraph hands control back to the graph that contains it. The
20
+ subgraph's own writes and the update it addressed to the parent are reduced separately, in order,
21
+ so neither overwrites the other.
22
+ - **Breakpoints.** `interruptBefore` and `interruptAfter`, per compile or per run, pause a run at
23
+ chosen nodes with status `interrupted` and a `breakpoint` describing where. `continue()` carries on,
24
+ and does not pause again at the breakpoint it resumes from.
25
+ - **Deferred nodes.** `addNode(name, fn, { defer: true })` holds a node until every other pending task
26
+ has finished, so an aggregator after branches of different lengths runs once.
27
+ - **`updateState()`.** Merges a correction into a thread's latest checkpoint in place, keeping any
28
+ pending question; with `asNode`, applies it as that node's output and continues along its edges.
29
+ - **`fork()`.** Copies a thread's history up to a step into a new thread, leaving the original
30
+ untouched and runnable, and records `metadata.forkedFrom`.
31
+ - **`onEvent` and `context.emit()`.** A run can observe each task starting, retrying, and finishing
32
+ with its update, every checkpoint written, and custom events a node emits, such as model tokens. A
33
+ listener that throws never fails the run.
34
+ - **`describe()` and `nexus-ai-pro/graph/visualize`.** `describe()` returns a graph's nodes, edges, and
35
+ run-time routes as plain JSON, including subgraphs. `toMermaid()` draws it, with optional
36
+ highlighting of where a thread is; `toGraphJSON()` returns the data. The visualizer imports no
37
+ runtime code.
38
+
39
+ ### Fixed
40
+
41
+ - **Parallel copies of a subgraph node no longer share a thread.** `asNode()` keyed the subgraph's
42
+ thread by node name, so two `Send` copies of one subgraph node in the same step wrote to the same
43
+ thread. It is now keyed by task; a plain node's task id is its name, so existing threads are
44
+ unaffected.
45
+
7
46
  ## [1.11.0] - 2026-09-18
8
47
 
9
48
  Parallel graphs. A fan-out that looks parallel now runs in parallel, a graph can fan out over data it
@@ -614,7 +653,8 @@ shared policy vocabulary is in place for a future release that revisits this.
614
653
  - URL fetching rejects unsafe private-network targets and limits response reads.
615
654
  - CLI and audit findings no longer disclose detected secret values.
616
655
 
617
- [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.11.0...HEAD
656
+ [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.12.0...HEAD
657
+ [1.12.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.11.0...v1.12.0
618
658
  [1.11.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.10.0...v1.11.0
619
659
  [1.10.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.9.0...v1.10.0
620
660
  [1.9.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.8.0...v1.9.0
package/README.md CHANGED
@@ -1,8 +1,12 @@
1
1
  # nexus-ai-pro
2
2
 
3
- The Universal AI Pipeline for Node.js: one typed API for routing, guardrails, caching, context-window management, tools, RAG, evals, jobs, and provider failover.
3
+ The typed AI framework for TypeScript. One API for provider routing and failover, agent graphs that
4
+ run branches in parallel and survive a restart, durable background operations, guardrails, cost
5
+ control, images, voice, and evals.
4
6
 
5
- Use the whole pipeline for production AI features, or turn pieces off when you only need a thin provider wrapper.
7
+ Import the whole runtime, or one piece: every capability has its own entry point with a size budget
8
+ CI enforces, and the table below publishes what each one costs. A graph-only application loads 60 KB
9
+ and installs no third-party package at all.
6
10
 
7
11
  - NPM: https://www.npmjs.com/package/nexus-ai-pro
8
12
  - GitHub: https://github.com/mkhitar-abrahamyan/nexus-ai
@@ -533,6 +537,98 @@ await graph.history(threadId); // newest first
533
537
  graph.resumeFrom(threadId, 3); // rewind and run forward
534
538
  ```
535
539
 
540
+ **Route from inside a node.** When a node already knows where to go — an agent that just picked a
541
+ tool, a triage step that classified a ticket — return a `Command` instead of splitting the decision
542
+ into a separate router:
543
+
544
+ ```ts
545
+ import { Command } from 'nexus-ai-pro/graph';
546
+
547
+ .addNode('triage', (ctx) =>
548
+ new Command({ update: { label: classify(ctx.state) }, goto: isUrgent(ctx.state) ? 'page-oncall' : 'queue' }),
549
+ { ends: ['page-oncall', 'queue'] },
550
+ )
551
+ ```
552
+
553
+ `goto` takes node names or `Send`s, and `ends` keeps compile-time checks and diagrams exact. A node
554
+ inside a subgraph can return `new Command({ graph: Command.PARENT, goto: 'human' })` to hand control
555
+ back to the graph that contains it — how a nested agent escalates.
556
+
557
+ **Aggregate after branches of different lengths.** `{ defer: true }` holds a node until every other
558
+ pending task has finished, so an aggregator after a two-step branch and a one-step branch runs once,
559
+ after both, rather than once per arrival.
560
+
561
+ **Pause for inspection.** Breakpoints stop a run before or after chosen nodes, checkpointed, without
562
+ changing the nodes themselves:
563
+
564
+ ```ts
565
+ const graph = builder.compile({ interruptBefore: ['publish'] });
566
+ const paused = await graph.invoke(input, { threadId }); // status: 'interrupted', breakpoint: { when: 'before', ... }
567
+ await graph.updateState(threadId, { draft: fixedDraft }); // correct something first, if needed
568
+ for await (const _ of graph.continue(threadId)) {} // then carry on
569
+ ```
570
+
571
+ Set them per compile or per run; a run's list replaces the compiled one.
572
+
573
+ **Edit state, or fork a thread.** `updateState(threadId, update)` merges a correction into the latest
574
+ checkpoint through the channel reducers, leaving any pending question in place.
575
+ `updateState(threadId, update, { asNode: 'research' })` applies it as if that node had produced it,
576
+ so the next step follows that node's edges. `fork(threadId, { step })` copies the history up to a step
577
+ into a new thread and leaves the original untouched, so two answers to the same question can be run
578
+ side by side.
579
+
580
+ **Watch it run.** `onEvent` receives every task starting, retrying, and finishing with its update,
581
+ every checkpoint written, and whatever a node passes to `context.emit()` — model tokens as they
582
+ stream, a status line, an intermediate result:
583
+
584
+ ```ts
585
+ .addNode('write', async (ctx) => {
586
+ for await (const token of streamAnswer(ctx.state)) ctx.emit({ token });
587
+ return { answer };
588
+ })
589
+
590
+ await graph.invoke(input, {
591
+ onEvent: (event) => event.type === 'custom' && process.stdout.write((event.data as { token: string }).token),
592
+ });
593
+ ```
594
+
595
+ A listener that throws never fails the run it is watching.
596
+
597
+ **See the shape.** `graph.describe()` returns nodes, edges, and which routes are decided at run
598
+ time, as plain JSON. `toMermaid()` from `nexus-ai-pro/graph/visualize` draws it — solid arrows are
599
+ always taken, dotted ones are chosen at run time, and subgraphs are drawn inside the node that runs
600
+ them:
601
+
602
+ ```ts
603
+ import { toMermaid } from 'nexus-ai-pro/graph/visualize';
604
+
605
+ console.log(toMermaid(graph, { highlight: checkpoint.next }));
606
+ ```
607
+
608
+ This diagram is that function's actual output for a research-and-review graph:
609
+
610
+ ```mermaid
611
+ flowchart TD
612
+ __start__([start])
613
+ __end__([end])
614
+ n_plan["plan"]
615
+ n_research["research<br/><small>retries</small>"]
616
+ n_summarize["summarize<br/><small>deferred</small>"]
617
+ n_review["review"]
618
+ n_publish["publish"]
619
+ __start__ --> n_plan
620
+ n_summarize --> n_review
621
+ n_publish --> __end__
622
+ n_plan -.-> n_research
623
+ n_research -.-> n_summarize
624
+ n_review -.-> n_publish
625
+ n_review -.-> n_plan
626
+ ```
627
+
628
+ The visualizer is its own subpath and imports no runtime code, so drawing a graph costs nothing to an
629
+ application that only runs one. No image renderer is bundled; any Mermaid renderer turns the output
630
+ into PNG or SVG.
631
+
536
632
  **Durable by construction.** The default checkpointer is in-process, holding up to 1,000 threads;
537
633
  pass `checkpointer: false` to turn checkpointing off. Point it at the operation store
538
634
  that already backs durable operations and a thread survives a restart — a different worker resumes
@@ -1832,13 +1928,13 @@ only, so it never appears in an import graph.
1832
1928
  | `nexus-ai-pro/providers/anthropic` | 70 KB | 11% | none |
1833
1929
  | `nexus-ai-pro/providers/google` | 66 KB | 11% | none |
1834
1930
  | `nexus-ai-pro/images` | 65 KB | 11% | none |
1931
+ | `nexus-ai-pro/graph` | 60 KB | 10% | none |
1835
1932
  | `nexus-ai-pro/providers/ollama` | 58 KB | 9% | none |
1836
1933
  | `nexus-ai-pro/batch/openai` | 52 KB | 8% | none |
1837
1934
  | `nexus-ai-pro/batch/anthropic` | 52 KB | 8% | none |
1838
1935
  | `nexus-ai-pro/operations` | 49 KB | 8% | none |
1839
1936
  | `nexus-ai-pro/batch/mock` | 47 KB | 8% | none |
1840
1937
  | `nexus-ai-pro/providers/cohere` | 46 KB | 8% | none |
1841
- | `nexus-ai-pro/graph` | 46 KB | 8% | none |
1842
1938
  | `nexus-ai-pro/realtime/openai-webrtc` | 46 KB | 8% | none |
1843
1939
  | `nexus-ai-pro/security` | 40 KB | 7% | +3.4 MB |
1844
1940
  | `nexus-ai-pro/models` | 35 KB | 6% | none |
@@ -1874,6 +1970,7 @@ only, so it never appears in an import graph.
1874
1970
  | `nexus-ai-pro/realtime/openai-server` | 8 KB | 1% | none |
1875
1971
  | `nexus-ai-pro/capabilities` | 8 KB | 1% | none |
1876
1972
  | `nexus-ai-pro/telephony/realtime-bridge` | 7 KB | 1% | none |
1973
+ | `nexus-ai-pro/graph/visualize` | 6 KB | 1.0% | none |
1877
1974
  | `nexus-ai-pro/providers/errors` | 5 KB | 0.8% | none |
1878
1975
  | `nexus-ai-pro/embeddings/mock` | 5 KB | 0.8% | none |
1879
1976
  | `nexus-ai-pro/cache/semantic-cache` | 5 KB | 0.8% | none |
@@ -1,4 +1,4 @@
1
- import type { ChannelSchema, CompileOptions, EdgeRouter, GraphCheckpoint, GraphProgress, GraphResult, GraphRunOptions, GraphStatus, GraphStepEvent, NodeFn, NodeOptions, StateUpdate } from '../types/graph.js';
1
+ import type { ChannelSchema, CompileOptions, EdgeRouter, GraphCheckpoint, GraphDescription, GraphProgress, GraphResult, GraphRunOptions, GraphStatus, GraphStepEvent, NodeFn, NodeOptions, StateUpdate } from '../types/graph.js';
2
2
  interface GraphNodeDefinition<S extends ChannelSchema> {
3
3
  fn: NodeFn<S>;
4
4
  options: NodeOptions;
@@ -91,6 +91,36 @@ export declare class CompiledGraph<S extends ChannelSchema> {
91
91
  * passes the answer into the subgraph, which continues where it stopped rather than starting over.
92
92
  */
93
93
  asNode<P extends ChannelSchema>(): NodeFn<P>;
94
+ /**
95
+ * The graph's shape as plain data: nodes, edges, and which routes are chosen at run time.
96
+ *
97
+ * What `nexus-ai-pro/graph/visualize` draws, and what a UI or a test can inspect without running
98
+ * anything. Subgraphs added through `asNode()` are described inside the node that runs them.
99
+ */
100
+ describe(): GraphDescription;
101
+ /**
102
+ * Edits a thread's state.
103
+ *
104
+ * Without `asNode`, the update is merged into the latest checkpoint in place, through the channel
105
+ * reducers, and the run continues exactly where it was — including a thread paused for input.
106
+ * With `asNode`, the update is applied as if that node had just produced it: a new checkpoint is
107
+ * written and the next step follows that node's edges. That is how an operator corrects a wrong
108
+ * intermediate result and lets the rest of the graph run on the fix.
109
+ */
110
+ updateState(threadId: string, update: StateUpdate<S>, options?: {
111
+ asNode?: string;
112
+ }): Promise<GraphCheckpoint<S>>;
113
+ /**
114
+ * Copies a thread's history up to `step` into a new thread, and returns the new thread's id.
115
+ *
116
+ * Rewinding with `resumeFrom()` replaces the original timeline. A fork keeps it: both threads stay
117
+ * readable and runnable, so two answers to the same question can be compared side by side. Every
118
+ * copied checkpoint records `metadata.forkedFrom`.
119
+ */
120
+ fork(threadId: string, options?: {
121
+ step?: number;
122
+ threadId?: string;
123
+ }): Promise<string>;
94
124
  private run;
95
125
  /** Runs one task to a verdict, applying its retry policy and timeout. */
96
126
  private runTask;