nexus-ai-pro 1.10.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
@@ -106,7 +106,7 @@ exactly-once provider execution. Those capabilities will use additive adapters a
106
106
  `MemoryAssetStore` is likewise process-local and does not promise cross-process durability or shared
107
107
  tenant state.
108
108
 
109
- ### Image portability (Unreleased)
109
+ ### Image portability (1.10.0)
110
110
 
111
111
  The following are public under the same 1.x rules. None is exported from the root or from
112
112
  `nexus-ai-pro/images`:
@@ -278,10 +278,10 @@ and threading the schema through it would force an annotation on every construct
278
278
  The graph casts at that boundary and hands typed state back through `state()` and `history()`.
279
279
 
280
280
  `compile()` without a checkpointer uses an in-process `MemoryGraphCheckpointer` capped at 1,000
281
- threads (Unreleased; earlier releases created none). `checkpointer: false` disables checkpointing. A
281
+ threads (since 1.10.0; earlier releases created none). `checkpointer: false` disables checkpointing. A
282
282
  run started without a `threadId` reports its generated id on the result.
283
283
 
284
- Also Unreleased:
284
+ Also since 1.10.0:
285
285
 
286
286
  - A paused or failed superstep records its already finished nodes in the optional `completed` field.
287
287
  Those nodes are not run again, and their edges still count when the step completes.
@@ -290,10 +290,43 @@ Also Unreleased:
290
290
  - A subgraph interrupt surfaces as an interrupt of the parent node.
291
291
  - `onProgress` receives `context.report()` calls.
292
292
 
293
- Scheduling within a superstep is not a guarantee. Nodes in a fan-out currently run in edge order and
294
- sequentially; running them concurrently is a valid future change, so a node must not depend on
295
- observing another node's write within the same superstep — that is what channels are for. The default
296
- `maxSteps` may change; pass it explicitly when a run depends on the bound.
293
+ Scheduling within a superstep is not a guarantee, and 1.11.0 makes use of that: tasks in a
294
+ superstep now run concurrently, bounded by `maxConcurrency` (default 16, settable per compile and per
295
+ run; `1` is strictly sequential). A node still must not depend on observing another node's write
296
+ within the same superstep — that is what channels are for. What is guaranteed is that writes are
297
+ reduced in task order rather than completion order, so a replay of the same decisions produces the
298
+ same state. The default `maxSteps` and `maxConcurrency` may change; pass them explicitly when a run
299
+ depends on the bound.
300
+
301
+ Also since 1.11.0, all additive:
302
+
303
+ - `Send` creates one task per value, with the task's input on `context.input`. Task ids are derived
304
+ from the step and the order produced, and appear in the checkpoint's optional `tasks` field, which
305
+ is written only when it says more than `next` does.
306
+ - `NodeOptions` on `addNode` carries `retry`, `timeoutMs`, and `ends`; `compile({ retry })` sets the
307
+ default policy. Retrying is off unless asked for. A timeout raises `GraphNodeTimeoutError`.
308
+ - `onNodeError` chooses `fail-fast` (default) or `settle` for the siblings of a failed task.
309
+ - A paused checkpoint and result carry `interrupts`, every question the step asked;
310
+ `interrupt` remains the first of them. `resumeInterrupts()` and `resumeInterruptsWith()` answer any
311
+ subset by interrupt id. Interrupt ids are keyed by task, so ids written by earlier releases for
312
+ ordinary nodes still resolve.
313
+ - `context.taskId` and `context.attempt` are new. Step events carry `tasks` and `attempts` when there
314
+ is something to report.
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`.
297
330
 
298
331
  ## Deprecation process
299
332
 
package/CHANGELOG.md CHANGED
@@ -4,6 +4,94 @@ 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
+
46
+ ## [1.11.0] - 2026-09-18
47
+
48
+ Parallel graphs. A fan-out that looks parallel now runs in parallel, a graph can fan out over data it
49
+ discovers at run time, and a node can retry or time out on its own.
50
+
51
+ ### Added
52
+
53
+ - **Concurrent supersteps.** Every task in a superstep starts together: four branches of 300 ms
54
+ finish in about 310 ms, where they used to take 1,253 ms. `npm run bench:graph` measures it and
55
+ fails if the gain disappears.
56
+ - `maxConcurrency` bounds how many run at once. It defaults to 16, is settable per compile and per
57
+ run, and `1` restores strictly sequential execution.
58
+ - Writes are reduced in task order, never completion order, so timing cannot change the state a
59
+ replay produces.
60
+ - A superstep with a single task skips the scheduler, so a linear graph pays nothing for this.
61
+ - **`Send`, for fan-out over run-time data.** A router can return `new Send(node, input)` values,
62
+ creating one task per value, each reading its own `context.input`. Fifty-seven URLs become
63
+ fifty-seven tasks of one node in one superstep.
64
+ - Each copy is checkpointed separately, so a resume re-runs only the copies that did not finish.
65
+ - Task ids come from the step and the order produced, so a replay rebuilds the same tasks.
66
+ - `addNode(name, fn, { ends })` declares `Send` targets, keeping reachability checks exact.
67
+ - **Per-node retries and timeouts.** `addNode(name, fn, { retry, timeoutMs })`, with
68
+ `compile({ retry })` as the default for every node.
69
+ - Retrying is off unless asked for, because only the application knows whether a node is
70
+ idempotent. Interrupts, aborts, and validation errors are never retried.
71
+ - `context.attempt` tells a node which try it is on, and step events report the attempts a task
72
+ needed.
73
+ - A timeout aborts the node's signal and fails that attempt with `GraphNodeTimeoutError`, even if
74
+ the node ignores its signal.
75
+ - **`onNodeError`.** When a task fails, its siblings are aborted (`fail-fast`, the default) or left to
76
+ finish (`settle`). Either way, what they already wrote is kept.
77
+ - **Several questions at once.** Parallel tasks can each interrupt. Paused checkpoints and results
78
+ carry `interrupts`, and `resumeInterrupts()` answers any subset by id. A single question still works
79
+ exactly as before through `resume()`.
80
+ - **Install weight is now measured honestly.** The size table only ever counted this package's own
81
+ files, so a small entry point could quietly force a large dependency on the consumer.
82
+ - `npm run size:check` follows bare imports too, and the README table gains a column for what each
83
+ entry point makes you install. Most entry points force nothing: `/graph`, `/operations`,
84
+ `/batch`, `/embeddings`, and every image subpath import no third-party package at all. The root
85
+ import, `/core`, `/config`, and `/security` pull in 3.4 to 4.7 MB, because JSON-schema and Zod
86
+ validation live there.
87
+ - The clean-install test now measures and bounds the whole production install: 10.0 MB of
88
+ `node_modules`, of which 3.0 MB is this package.
89
+
90
+ ### Fixed
91
+
92
+ - **A retry no longer lets the process exit while it waits.** The backoff timer was unref'd, so a run
93
+ waiting to retry could be abandoned by an otherwise idle process.
94
+
7
95
  ## [1.10.0] - 2026-09-16
8
96
 
9
97
  Image portability. Three image backends now sit behind one contract: OpenAI, Google Imagen, and a
@@ -565,7 +653,9 @@ shared policy vocabulary is in place for a future release that revisits this.
565
653
  - URL fetching rejects unsafe private-network targets and limits response reads.
566
654
  - CLI and audit findings no longer disclose detected secret values.
567
655
 
568
- [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.10.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
658
+ [1.11.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.10.0...v1.11.0
569
659
  [1.10.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.9.0...v1.10.0
570
660
  [1.9.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.8.0...v1.9.0
571
661
  [1.8.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.7.0...v1.8.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
@@ -452,6 +456,58 @@ whether that means overwrite, append, or sum. Assignment would silently drop one
452
456
  **Cycles are first-class**, so `maxSteps` is what stands between a mistaken router and an infinite
453
457
  loop. Exceeding it names the nodes still pending rather than hanging.
454
458
 
459
+ **Branches run in parallel.** Every task in a superstep starts together, so four branches of three
460
+ seconds each finish in about three seconds rather than twelve:
461
+
462
+ ```ts
463
+ .addConditionalEdges('plan', () => ['research-a', 'research-b', 'research-c', 'research-d'])
464
+ ```
465
+
466
+ `maxConcurrency` caps how many run at once — 16 by default, settable per compile and per run, and
467
+ `1` restores strictly sequential execution. Whatever the timing, writes are reduced in task order, so
468
+ a replay produces the same state. A superstep with one task skips the scheduler entirely.
469
+
470
+ **Fan out over data decided at run time.** Edges can only name nodes that already exist. `Send`
471
+ creates one task per value instead, each reading its own `context.input`:
472
+
473
+ ```ts
474
+ import { Send } from 'nexus-ai-pro/graph';
475
+
476
+ .addNode('research', async (ctx) => ({ findings: [await fetch(ctx.input as string)] }), { ends: [END] })
477
+ .addConditionalEdges('plan', (state) => state.urls.map((url) => new Send('research', url)))
478
+ ```
479
+
480
+ Fifty-seven URLs become fifty-seven tasks of one node in a single superstep, bounded by
481
+ `maxConcurrency` and checkpointed individually, so a resume re-runs only the copies that did not
482
+ finish. Declaring `ends` keeps compile-time reachability checks exact.
483
+
484
+ **Retries and timeouts per node.** A node that calls a flaky service can retry on its own, without
485
+ wrapping every node body in the same try/catch:
486
+
487
+ ```ts
488
+ .addNode('fetch', fetchNode, {
489
+ retry: { maxAttempts: 3, initialIntervalMs: 500, backoffFactor: 2, jitter: true },
490
+ timeoutMs: 10_000,
491
+ })
492
+ ```
493
+
494
+ Retrying defaults to off, because only you know whether a node is idempotent; `compile({ retry })`
495
+ sets a default for every node. Interrupts, aborts, and validation errors are never retried.
496
+ `context.attempt` tells a node which try it is on, and a timeout aborts the node's signal and fails
497
+ that attempt. When one task fails, its siblings are aborted by default; `onNodeError: 'settle'` lets
498
+ them finish first, and either way what they already wrote is kept.
499
+
500
+ **Several questions at once.** Parallel tasks can each interrupt. The paused result carries every
501
+ pending question, and they can be answered together or a few at a time:
502
+
503
+ ```ts
504
+ const run = await graph.invoke(input, { threadId });
505
+ await graph.resumeInterruptsWith(threadId, {
506
+ [run.interrupts[0].id]: true,
507
+ [run.interrupts[1].id]: 'use the second draft',
508
+ });
509
+ ```
510
+
455
511
  **Human in the loop.** A node calls `interrupt()`; the graph checkpoints and stops:
456
512
 
457
513
  ```ts
@@ -481,7 +537,100 @@ await graph.history(threadId); // newest first
481
537
  graph.resumeFrom(threadId, 3); // rewind and run forward
482
538
  ```
483
539
 
484
- **Durable by construction.** The default checkpointer is in-process. Point it at the operation store
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
+
632
+ **Durable by construction.** The default checkpointer is in-process, holding up to 1,000 threads;
633
+ pass `checkpointer: false` to turn checkpointing off. Point it at the operation store
485
634
  that already backs durable operations and a thread survives a restart — a different worker resumes
486
635
  what another suspended:
487
636
 
@@ -1750,85 +1899,94 @@ Importing one piece loads one piece. The figures below are the transitive import
1750
1899
  point, generated from the build by `npm run size:check`, which fails CI when an entry point grows
1751
1900
  past its budget — so the numbers stay true rather than aspirational.
1752
1901
 
1902
+ The last column is the part a file-size table usually hides: what an entry point forces you to
1903
+ install. Most of them force nothing at all — `/graph`, `/operations`, `/images/*`, `/batch`,
1904
+ `/embeddings` and the rest import no third-party package. The root import, `/core`, `/config`, and
1905
+ `/security` do, because JSON-schema and Zod validation live there. A production install of this
1906
+ package is about 10 MB of `node_modules`, of which 3 MB is this package; the clean-install test holds
1907
+ that total to a ceiling too. `@types/node` accounts for a further 2.4 MB at install time but is types
1908
+ only, so it never appears in an import graph.
1909
+
1753
1910
  <!-- size-table:start -->
1754
- | Import | Size | Share of root |
1755
- | --- | --- | --- |
1756
- | `nexus-ai-pro` | 612 KB | 100% |
1757
- | `nexus-ai-pro/config` | 468 KB | 76% |
1758
- | `nexus-ai-pro/core` | 462 KB | 75% |
1759
- | `nexus-ai-pro/realtime` | 157 KB | 26% |
1760
- | `nexus-ai-pro/batch` | 118 KB | 19% |
1761
- | `nexus-ai-pro/realtime/session` | 94 KB | 15% |
1762
- | `nexus-ai-pro/embeddings` | 91 KB | 15% |
1763
- | `nexus-ai-pro/providers/groq` | 79 KB | 13% |
1764
- | `nexus-ai-pro/providers/mistral` | 79 KB | 13% |
1765
- | `nexus-ai-pro/providers/azure-openai` | 78 KB | 13% |
1766
- | `nexus-ai-pro/providers/openrouter` | 78 KB | 13% |
1767
- | `nexus-ai-pro/providers/deepseek` | 77 KB | 13% |
1768
- | `nexus-ai-pro/providers/llamacpp` | 77 KB | 13% |
1769
- | `nexus-ai-pro/providers/lmstudio` | 77 KB | 13% |
1770
- | `nexus-ai-pro/providers/openai` | 77 KB | 13% |
1771
- | `nexus-ai-pro/providers/anthropic` | 70 KB | 11% |
1772
- | `nexus-ai-pro/providers/google` | 66 KB | 11% |
1773
- | `nexus-ai-pro/images` | 65 KB | 11% |
1774
- | `nexus-ai-pro/providers/ollama` | 58 KB | 9% |
1775
- | `nexus-ai-pro/batch/openai` | 52 KB | 8% |
1776
- | `nexus-ai-pro/batch/anthropic` | 52 KB | 8% |
1777
- | `nexus-ai-pro/operations` | 49 KB | 8% |
1778
- | `nexus-ai-pro/batch/mock` | 47 KB | 8% |
1779
- | `nexus-ai-pro/providers/cohere` | 46 KB | 8% |
1780
- | `nexus-ai-pro/realtime/openai-webrtc` | 46 KB | 8% |
1781
- | `nexus-ai-pro/security` | 40 KB | 7% |
1782
- | `nexus-ai-pro/models` | 35 KB | 6% |
1783
- | `nexus-ai-pro/graph` | 33 KB | 5% |
1784
- | `nexus-ai-pro/images/inputs` | 31 KB | 5% |
1785
- | `nexus-ai-pro/realtime/openai-websocket` | 29 KB | 5% |
1786
- | `nexus-ai-pro/evals` | 23 KB | 4% |
1787
- | `nexus-ai-pro/images/transform` | 22 KB | 4% |
1788
- | `nexus-ai-pro/images/stores` | 22 KB | 4% |
1789
- | `nexus-ai-pro/telephony/twilio` | 21 KB | 3% |
1790
- | `nexus-ai-pro/telephony` | 20 KB | 3% |
1791
- | `nexus-ai-pro/images/openai` | 19 KB | 3% |
1792
- | `nexus-ai-pro/realtime/mock` | 19 KB | 3% |
1793
- | `nexus-ai-pro/voice` | 18 KB | 3% |
1794
- | `nexus-ai-pro/images/comfyui` | 17 KB | 3% |
1795
- | `nexus-ai-pro/embeddings/adapters` | 17 KB | 3% |
1796
- | `nexus-ai-pro/realtime/conversation` | 16 KB | 3% |
1797
- | `nexus-ai-pro/images/google` | 15 KB | 2% |
1798
- | `nexus-ai-pro/images/evals` | 15 KB | 2% |
1799
- | `nexus-ai-pro/images/assets` | 14 KB | 2% |
1800
- | `nexus-ai-pro/context` | 13 KB | 2% |
1801
- | `nexus-ai-pro/operations/adapters` | 13 KB | 2% |
1802
- | `nexus-ai-pro/realtime/tools` | 13 KB | 2% |
1803
- | `nexus-ai-pro/workflows` | 13 KB | 2% |
1804
- | `nexus-ai-pro/images/mock` | 12 KB | 2% |
1805
- | `nexus-ai-pro/optimizer` | 11 KB | 2% |
1806
- | `nexus-ai-pro/voice/session` | 11 KB | 2% |
1807
- | `nexus-ai-pro/providers` | 10 KB | 2% |
1808
- | `nexus-ai-pro/providers/base` | 10 KB | 2% |
1809
- | `nexus-ai-pro/embeddings/models` | 10 KB | 2% |
1810
- | `nexus-ai-pro/voice/openai` | 9 KB | 1% |
1811
- | `nexus-ai-pro/images/moderation` | 9 KB | 1% |
1812
- | `nexus-ai-pro/ops/circuit-breaker` | 8 KB | 1% |
1813
- | `nexus-ai-pro/realtime/openai-server` | 8 KB | 1% |
1814
- | `nexus-ai-pro/capabilities` | 8 KB | 1% |
1815
- | `nexus-ai-pro/telephony/realtime-bridge` | 7 KB | 1% |
1816
- | `nexus-ai-pro/providers/errors` | 5 KB | 0.8% |
1817
- | `nexus-ai-pro/embeddings/mock` | 5 KB | 0.8% |
1818
- | `nexus-ai-pro/cache/semantic-cache` | 5 KB | 0.8% |
1819
- | `nexus-ai-pro/evals/judge` | 5 KB | 0.8% |
1820
- | `nexus-ai-pro/operations/webhooks` | 4 KB | 0.7% |
1821
- | `nexus-ai-pro/ops/rate-limit-adapters` | 3 KB | 0.5% |
1822
- | `nexus-ai-pro/cache/memory-cache` | 3 KB | 0.5% |
1823
- | `nexus-ai-pro/cache` | 2 KB | 0.3% |
1824
- | `nexus-ai-pro/cache/adapters` | 2 KB | 0.3% |
1825
- | `nexus-ai-pro/rag` | 2 KB | 0.3% |
1826
- | `nexus-ai-pro/jobs` | 2 KB | 0.3% |
1827
- | `nexus-ai-pro/jobs/durable-adapters` | 2 KB | 0.3% |
1828
- | `nexus-ai-pro/jobs/queue` | 2 KB | 0.3% |
1829
- | `nexus-ai-pro/streaming` | 1 KB | 0.2% |
1830
- | `nexus-ai-pro/providers/type-guards` | 1 KB | 0.2% |
1831
- | `nexus-ai-pro/jobs/batch` | 1 KB | 0.2% |
1911
+ | Import | Size | Share of root | Third-party install |
1912
+ | --- | --- | --- | --- |
1913
+ | `nexus-ai-pro` | 612 KB | 100% | +4.7 MB |
1914
+ | `nexus-ai-pro/config` | 468 KB | 76% | +4.7 MB |
1915
+ | `nexus-ai-pro/core` | 462 KB | 75% | +4.7 MB |
1916
+ | `nexus-ai-pro/realtime` | 157 KB | 26% | none |
1917
+ | `nexus-ai-pro/batch` | 118 KB | 19% | none |
1918
+ | `nexus-ai-pro/realtime/session` | 94 KB | 15% | none |
1919
+ | `nexus-ai-pro/embeddings` | 91 KB | 15% | none |
1920
+ | `nexus-ai-pro/providers/groq` | 79 KB | 13% | none |
1921
+ | `nexus-ai-pro/providers/mistral` | 79 KB | 13% | none |
1922
+ | `nexus-ai-pro/providers/azure-openai` | 78 KB | 13% | none |
1923
+ | `nexus-ai-pro/providers/openrouter` | 78 KB | 13% | none |
1924
+ | `nexus-ai-pro/providers/deepseek` | 77 KB | 13% | none |
1925
+ | `nexus-ai-pro/providers/llamacpp` | 77 KB | 13% | none |
1926
+ | `nexus-ai-pro/providers/lmstudio` | 77 KB | 13% | none |
1927
+ | `nexus-ai-pro/providers/openai` | 77 KB | 13% | none |
1928
+ | `nexus-ai-pro/providers/anthropic` | 70 KB | 11% | none |
1929
+ | `nexus-ai-pro/providers/google` | 66 KB | 11% | none |
1930
+ | `nexus-ai-pro/images` | 65 KB | 11% | none |
1931
+ | `nexus-ai-pro/graph` | 60 KB | 10% | none |
1932
+ | `nexus-ai-pro/providers/ollama` | 58 KB | 9% | none |
1933
+ | `nexus-ai-pro/batch/openai` | 52 KB | 8% | none |
1934
+ | `nexus-ai-pro/batch/anthropic` | 52 KB | 8% | none |
1935
+ | `nexus-ai-pro/operations` | 49 KB | 8% | none |
1936
+ | `nexus-ai-pro/batch/mock` | 47 KB | 8% | none |
1937
+ | `nexus-ai-pro/providers/cohere` | 46 KB | 8% | none |
1938
+ | `nexus-ai-pro/realtime/openai-webrtc` | 46 KB | 8% | none |
1939
+ | `nexus-ai-pro/security` | 40 KB | 7% | +3.4 MB |
1940
+ | `nexus-ai-pro/models` | 35 KB | 6% | none |
1941
+ | `nexus-ai-pro/images/inputs` | 31 KB | 5% | none |
1942
+ | `nexus-ai-pro/realtime/openai-websocket` | 29 KB | 5% | none |
1943
+ | `nexus-ai-pro/evals` | 23 KB | 4% | none |
1944
+ | `nexus-ai-pro/images/transform` | 22 KB | 4% | none |
1945
+ | `nexus-ai-pro/images/stores` | 22 KB | 4% | none |
1946
+ | `nexus-ai-pro/telephony/twilio` | 21 KB | 3% | none |
1947
+ | `nexus-ai-pro/telephony` | 20 KB | 3% | none |
1948
+ | `nexus-ai-pro/images/openai` | 19 KB | 3% | none |
1949
+ | `nexus-ai-pro/realtime/mock` | 19 KB | 3% | none |
1950
+ | `nexus-ai-pro/voice` | 18 KB | 3% | none |
1951
+ | `nexus-ai-pro/images/comfyui` | 17 KB | 3% | none |
1952
+ | `nexus-ai-pro/embeddings/adapters` | 17 KB | 3% | none |
1953
+ | `nexus-ai-pro/realtime/conversation` | 16 KB | 3% | none |
1954
+ | `nexus-ai-pro/images/google` | 15 KB | 2% | none |
1955
+ | `nexus-ai-pro/images/evals` | 15 KB | 2% | none |
1956
+ | `nexus-ai-pro/images/assets` | 14 KB | 2% | none |
1957
+ | `nexus-ai-pro/context` | 13 KB | 2% | none |
1958
+ | `nexus-ai-pro/operations/adapters` | 13 KB | 2% | none |
1959
+ | `nexus-ai-pro/realtime/tools` | 13 KB | 2% | none |
1960
+ | `nexus-ai-pro/workflows` | 13 KB | 2% | none |
1961
+ | `nexus-ai-pro/images/mock` | 12 KB | 2% | none |
1962
+ | `nexus-ai-pro/optimizer` | 11 KB | 2% | none |
1963
+ | `nexus-ai-pro/voice/session` | 11 KB | 2% | none |
1964
+ | `nexus-ai-pro/providers` | 10 KB | 2% | none |
1965
+ | `nexus-ai-pro/providers/base` | 10 KB | 2% | none |
1966
+ | `nexus-ai-pro/embeddings/models` | 10 KB | 2% | none |
1967
+ | `nexus-ai-pro/voice/openai` | 9 KB | 1% | none |
1968
+ | `nexus-ai-pro/images/moderation` | 9 KB | 1% | none |
1969
+ | `nexus-ai-pro/ops/circuit-breaker` | 8 KB | 1% | none |
1970
+ | `nexus-ai-pro/realtime/openai-server` | 8 KB | 1% | none |
1971
+ | `nexus-ai-pro/capabilities` | 8 KB | 1% | none |
1972
+ | `nexus-ai-pro/telephony/realtime-bridge` | 7 KB | 1% | none |
1973
+ | `nexus-ai-pro/graph/visualize` | 6 KB | 1.0% | none |
1974
+ | `nexus-ai-pro/providers/errors` | 5 KB | 0.8% | none |
1975
+ | `nexus-ai-pro/embeddings/mock` | 5 KB | 0.8% | none |
1976
+ | `nexus-ai-pro/cache/semantic-cache` | 5 KB | 0.8% | none |
1977
+ | `nexus-ai-pro/evals/judge` | 5 KB | 0.8% | none |
1978
+ | `nexus-ai-pro/operations/webhooks` | 4 KB | 0.7% | none |
1979
+ | `nexus-ai-pro/ops/rate-limit-adapters` | 3 KB | 0.5% | none |
1980
+ | `nexus-ai-pro/cache/memory-cache` | 3 KB | 0.5% | none |
1981
+ | `nexus-ai-pro/cache` | 2 KB | 0.3% | none |
1982
+ | `nexus-ai-pro/cache/adapters` | 2 KB | 0.3% | none |
1983
+ | `nexus-ai-pro/rag` | 2 KB | 0.3% | none |
1984
+ | `nexus-ai-pro/jobs` | 2 KB | 0.3% | none |
1985
+ | `nexus-ai-pro/jobs/durable-adapters` | 2 KB | 0.3% | none |
1986
+ | `nexus-ai-pro/jobs/queue` | 2 KB | 0.3% | none |
1987
+ | `nexus-ai-pro/streaming` | 1 KB | 0.2% | none |
1988
+ | `nexus-ai-pro/providers/type-guards` | 1 KB | 0.2% | none |
1989
+ | `nexus-ai-pro/jobs/batch` | 1 KB | 0.2% | none |
1832
1990
  <!-- size-table:end -->
1833
1991
 
1834
1992
  A capability that only works by importing the whole runtime is treated as a design problem, not an
@@ -19,7 +19,11 @@ export declare class GraphInterrupt extends GraphError {
19
19
  readonly node: string;
20
20
  readonly step: number;
21
21
  readonly index: number;
22
- constructor(request: InterruptRequest, node: string, step: number, index: number);
22
+ /** Task that asked. Equal to `node` unless the task came from a `Send`. */
23
+ readonly taskId: string;
24
+ constructor(request: InterruptRequest, node: string, step: number, index: number,
25
+ /** Task that asked. Equal to `node` unless the task came from a `Send`. */
26
+ taskId?: string);
23
27
  }
24
28
  /**
25
29
  * Raised when a run exceeds its superstep budget.
@@ -46,5 +50,24 @@ export declare class GraphNotInterruptedError extends GraphError {
46
50
  readonly status: string;
47
51
  constructor(threadId: string, status: string);
48
52
  }
49
- /** Stable key for one interrupt, so a replayed node picks up the answer it was given. */
50
- export declare function interruptKey(pending: Pick<PendingInterrupt, 'node' | 'step' | 'index'>): string;
53
+ /**
54
+ * Raised when a node outlives its `timeoutMs`.
55
+ *
56
+ * Separate from an ordinary failure because it is usually worth retrying, and because the node's
57
+ * signal was aborted underneath it rather than the node choosing to stop.
58
+ */
59
+ export declare class GraphNodeTimeoutError extends GraphError {
60
+ readonly node: string;
61
+ readonly timeoutMs: number;
62
+ constructor(node: string, timeoutMs: number);
63
+ }
64
+ /**
65
+ * Stable key for one interrupt, so a replayed node picks up the answer it was given.
66
+ *
67
+ * Keyed by task rather than node, so several `Send` tasks of one node each keep their own answer.
68
+ * For a node reached through an ordinary edge the task id is the node name, so keys written by
69
+ * earlier releases still resolve.
70
+ */
71
+ export declare function interruptKey(pending: Pick<PendingInterrupt, 'node' | 'step' | 'index'> & {
72
+ taskId?: string;
73
+ }): string;
@@ -26,12 +26,16 @@ export class GraphInterrupt extends GraphError {
26
26
  node;
27
27
  step;
28
28
  index;
29
- constructor(request, node, step, index) {
29
+ taskId;
30
+ constructor(request, node, step, index,
31
+ /** Task that asked. Equal to `node` unless the task came from a `Send`. */
32
+ taskId = node) {
30
33
  super(`Graph interrupted at "${node}": ${request.reason}`, 'GRAPH_INTERRUPT');
31
34
  this.request = request;
32
35
  this.node = node;
33
36
  this.step = step;
34
37
  this.index = index;
38
+ this.taskId = taskId;
35
39
  this.name = 'GraphInterrupt';
36
40
  }
37
41
  }
@@ -79,7 +83,29 @@ export class GraphNotInterruptedError extends GraphError {
79
83
  this.name = 'GraphNotInterruptedError';
80
84
  }
81
85
  }
82
- /** Stable key for one interrupt, so a replayed node picks up the answer it was given. */
86
+ /**
87
+ * Raised when a node outlives its `timeoutMs`.
88
+ *
89
+ * Separate from an ordinary failure because it is usually worth retrying, and because the node's
90
+ * signal was aborted underneath it rather than the node choosing to stop.
91
+ */
92
+ export class GraphNodeTimeoutError extends GraphError {
93
+ node;
94
+ timeoutMs;
95
+ constructor(node, timeoutMs) {
96
+ super(`Graph node "${node}" exceeded its ${timeoutMs}ms timeout`, 'GRAPH_NODE_TIMEOUT');
97
+ this.node = node;
98
+ this.timeoutMs = timeoutMs;
99
+ this.name = 'GraphNodeTimeoutError';
100
+ }
101
+ }
102
+ /**
103
+ * Stable key for one interrupt, so a replayed node picks up the answer it was given.
104
+ *
105
+ * Keyed by task rather than node, so several `Send` tasks of one node each keep their own answer.
106
+ * For a node reached through an ordinary edge the task id is the node name, so keys written by
107
+ * earlier releases still resolve.
108
+ */
83
109
  export function interruptKey(pending) {
84
- return `${pending.node}:${pending.step}:${pending.index}`;
110
+ return `${pending.taskId ?? pending.node}:${pending.step}:${pending.index}`;
85
111
  }