nexus-ai-pro 2.8.0 → 2.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/API_STABILITY.md +77 -0
- package/CHANGELOG.md +245 -1
- package/README.md +20 -4
- package/dist/batch/poll.d.ts +8 -4
- package/dist/batch/poll.js +2 -1
- package/dist/core/lifecycle.d.ts +11 -1
- package/dist/core/lifecycle.js +9 -0
- package/dist/core/nexus.js +4 -0
- package/dist/deep-agent/helpers.d.ts +36 -0
- package/dist/deep-agent/helpers.js +118 -5
- package/dist/deep-agent/index.d.ts +10 -2
- package/dist/evaluate/online.d.ts +5 -0
- package/dist/evaluate/online.js +74 -5
- package/dist/graph/graph.d.ts +5 -0
- package/dist/graph/graph.js +6 -0
- package/dist/lifecycle/budget.d.ts +60 -3
- package/dist/lifecycle/budget.js +147 -3
- package/dist/lifecycle/index.d.ts +2 -2
- package/dist/lifecycle/reservations.d.ts +46 -0
- package/dist/lifecycle/reservations.js +67 -0
- package/dist/media/rag.d.ts +76 -1
- package/dist/media/rag.js +195 -53
- package/dist/postgres/tenancy.d.ts +26 -1
- package/dist/postgres/tenancy.js +116 -1
- package/dist/postgres/traces.d.ts +2 -1
- package/dist/postgres/traces.js +13 -1
- package/dist/protocols/a2a.d.ts +84 -1
- package/dist/protocols/a2a.js +178 -14
- package/dist/protocols/acp.d.ts +16 -1
- package/dist/protocols/acp.js +78 -17
- package/dist/protocols/shared.d.ts +15 -0
- package/dist/realtime/conversation.d.ts +28 -1
- package/dist/realtime/conversation.js +62 -1
- package/dist/realtime/gemini-live.transport.d.ts +134 -0
- package/dist/realtime/gemini-live.transport.js +769 -0
- package/dist/realtime/index.d.ts +8 -4
- package/dist/realtime/index.js +5 -3
- package/dist/realtime/openai-server.d.ts +49 -0
- package/dist/realtime/openai-server.js +103 -0
- package/dist/realtime/openai-sip.d.ts +114 -0
- package/dist/realtime/openai-sip.js +111 -0
- package/dist/realtime/openai-webrtc.transport.d.ts +7 -0
- package/dist/realtime/openai-webrtc.transport.js +4 -0
- package/dist/realtime/openai-websocket.transport.d.ts +5 -0
- package/dist/realtime/openai-websocket.transport.js +3 -0
- package/dist/realtime/provider.d.ts +20 -0
- package/dist/realtime/provider.js +17 -0
- package/dist/realtime/session.d.ts +51 -2
- package/dist/realtime/session.js +249 -20
- package/dist/realtime/types.d.ts +73 -2
- package/dist/server/tenancy.d.ts +33 -2
- package/dist/server/tenancy.js +162 -0
- package/dist/tracing/index.d.ts +2 -2
- package/dist/tracing/index.js +1 -1
- package/dist/tracing/query.d.ts +5 -1
- package/dist/tracing/query.js +14 -1
- package/dist/tracing/stores.d.ts +2 -2
- package/dist/tracing/stores.js +2 -2
- package/dist/types/lifecycle.d.ts +105 -1
- package/dist/types/tracing.d.ts +20 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/video/errors.d.ts +37 -0
- package/dist/video/errors.js +22 -0
- package/dist/video/google.d.ts +52 -0
- package/dist/video/google.js +248 -0
- package/dist/video/index.d.ts +426 -0
- package/dist/video/index.js +666 -0
- package/dist/video/mock.d.ts +62 -0
- package/dist/video/mock.js +97 -0
- package/dist/video/runway.d.ts +47 -0
- package/dist/video/runway.js +241 -0
- package/dist-cjs/batch/poll.d.ts +8 -4
- package/dist-cjs/batch/poll.js +2 -1
- package/dist-cjs/core/lifecycle.d.ts +11 -1
- package/dist-cjs/core/lifecycle.js +9 -0
- package/dist-cjs/core/nexus.js +4 -0
- package/dist-cjs/deep-agent/helpers.d.ts +36 -0
- package/dist-cjs/deep-agent/helpers.js +118 -5
- package/dist-cjs/deep-agent/index.d.ts +10 -2
- package/dist-cjs/evaluate/online.d.ts +5 -0
- package/dist-cjs/evaluate/online.js +74 -5
- package/dist-cjs/graph/graph.d.ts +5 -0
- package/dist-cjs/graph/graph.js +6 -0
- package/dist-cjs/lifecycle/budget.d.ts +60 -3
- package/dist-cjs/lifecycle/budget.js +147 -3
- package/dist-cjs/lifecycle/index.d.ts +2 -2
- package/dist-cjs/lifecycle/reservations.d.ts +46 -0
- package/dist-cjs/lifecycle/reservations.js +71 -0
- package/dist-cjs/media/rag.d.ts +76 -1
- package/dist-cjs/media/rag.js +195 -53
- package/dist-cjs/postgres/tenancy.d.ts +26 -1
- package/dist-cjs/postgres/tenancy.js +115 -0
- package/dist-cjs/postgres/traces.d.ts +2 -1
- package/dist-cjs/postgres/traces.js +13 -1
- package/dist-cjs/protocols/a2a.d.ts +84 -1
- package/dist-cjs/protocols/a2a.js +178 -14
- package/dist-cjs/protocols/acp.d.ts +16 -1
- package/dist-cjs/protocols/acp.js +78 -17
- package/dist-cjs/protocols/shared.d.ts +15 -0
- package/dist-cjs/realtime/conversation.d.ts +28 -1
- package/dist-cjs/realtime/conversation.js +66 -1
- package/dist-cjs/realtime/gemini-live.transport.d.ts +134 -0
- package/dist-cjs/realtime/gemini-live.transport.js +773 -0
- package/dist-cjs/realtime/index.d.ts +8 -4
- package/dist-cjs/realtime/index.js +15 -1
- package/dist-cjs/realtime/openai-server.d.ts +49 -0
- package/dist-cjs/realtime/openai-server.js +108 -0
- package/dist-cjs/realtime/openai-sip.d.ts +114 -0
- package/dist-cjs/realtime/openai-sip.js +114 -0
- package/dist-cjs/realtime/openai-webrtc.transport.d.ts +7 -0
- package/dist-cjs/realtime/openai-webrtc.transport.js +4 -0
- package/dist-cjs/realtime/openai-websocket.transport.d.ts +5 -0
- package/dist-cjs/realtime/openai-websocket.transport.js +3 -0
- package/dist-cjs/realtime/provider.d.ts +20 -0
- package/dist-cjs/realtime/provider.js +19 -1
- package/dist-cjs/realtime/session.d.ts +51 -2
- package/dist-cjs/realtime/session.js +248 -19
- package/dist-cjs/realtime/types.d.ts +73 -2
- package/dist-cjs/server/tenancy.d.ts +33 -2
- package/dist-cjs/server/tenancy.js +162 -0
- package/dist-cjs/tracing/index.d.ts +2 -2
- package/dist-cjs/tracing/index.js +3 -1
- package/dist-cjs/tracing/query.d.ts +5 -1
- package/dist-cjs/tracing/query.js +16 -1
- package/dist-cjs/tracing/stores.d.ts +2 -2
- package/dist-cjs/tracing/stores.js +3 -1
- package/dist-cjs/types/lifecycle.d.ts +105 -1
- package/dist-cjs/types/tracing.d.ts +20 -1
- package/dist-cjs/version.d.ts +1 -1
- package/dist-cjs/version.js +1 -1
- package/dist-cjs/video/errors.d.ts +37 -0
- package/dist-cjs/video/errors.js +27 -0
- package/dist-cjs/video/google.d.ts +52 -0
- package/dist-cjs/video/google.js +252 -0
- package/dist-cjs/video/index.d.ts +426 -0
- package/dist-cjs/video/index.js +708 -0
- package/dist-cjs/video/mock.d.ts +62 -0
- package/dist-cjs/video/mock.js +101 -0
- package/dist-cjs/video/runway.d.ts +47 -0
- package/dist-cjs/video/runway.js +245 -0
- package/llms.txt +2 -1
- package/package.json +86 -5
package/API_STABILITY.md
CHANGED
|
@@ -41,6 +41,83 @@ Both are covered by this policy:
|
|
|
41
41
|
Deep imports into `dist`, `dist-cjs`, or `src` are not supported. Import only from `nexus-ai-pro` or
|
|
42
42
|
one of its explicit subpaths.
|
|
43
43
|
|
|
44
|
+
## 2.10 stage (2.10.0)
|
|
45
|
+
|
|
46
|
+
Everything 2.10 adds is stable from the start and additive, unless this section says otherwise.
|
|
47
|
+
|
|
48
|
+
New and stable:
|
|
49
|
+
- on `nexus-ai-pro/protocols/a2a`: `A2aHandler`, which `a2aHandler()` now returns, with `owner`,
|
|
50
|
+
`recover()`, and `close()`; `A2aRecoveryReport`; `A2aTaskLease` and `A2aTaskRun`, and
|
|
51
|
+
`A2aTaskRecord.lease`, `run`, and `version`; `A2aTaskStore.claim()`, optional;
|
|
52
|
+
`A2aServerOptions.lease` and `recoverEveryMs`. The handler is still the request function it was;
|
|
53
|
+
- on `nexus-ai-pro/protocols/acp`: `AcpTurnRecord` and `AcpSessionRecord.turn`;
|
|
54
|
+
- on `nexus-ai-pro/deep-agent`: `DeepSubagent.remote`, `DeepRemoteHelper`, and `DeepRemoteRunResult`;
|
|
55
|
+
- `CompiledGraph.continueWith()`, and `ProtocolGraph.continueWith()` and `state()`, both optional;
|
|
56
|
+
- on `nexus-ai-pro/lifecycle`: `ProviderHealthOutcome`, `OperationLifecycle.reportHealth()`,
|
|
57
|
+
`OperationLifecycleLike.reportHealth()`, optional, and `LifecycleRuntime.providerHealth`;
|
|
58
|
+
- on `nexus-ai-pro/realtime`: `RealtimeSessionConfig.tenantId`, `userId`, `principal`,
|
|
59
|
+
`traceContext`, `conversationStore`, and `resume`; `RealtimeConversationOptions.replayItems`;
|
|
60
|
+
`RealtimeConversationStore`, `MemoryRealtimeConversationStore`, `storeRealtimeConversations()`,
|
|
61
|
+
`replayRealtimeConversation()`, and `REALTIME_REPLAY_PREFIX`; `GeminiLiveProvider` and
|
|
62
|
+
`GeminiLiveProviderOptions`; `RealtimeSession.sendImage()` and `sendVideoFrame()`,
|
|
63
|
+
`RealtimeSessionConfig.video`, `RealtimeImageInput`, `RealtimeImageOptions`, and
|
|
64
|
+
`RealtimeVideoOptions`; `RealtimeTransport.sendVideo()`, optional;
|
|
65
|
+
- `RealtimeConversationOptions.retainRawAudio`, `RealtimeConversationReducerOptions.inPlace`, and
|
|
66
|
+
`RealtimePeerConnectionLike.addTransceiver()`, optional;
|
|
67
|
+
- `nexus-ai-pro/realtime/gemini-live`, with `GeminiLiveTransport` and `GeminiLiveTransportOptions`;
|
|
68
|
+
- `nexus-ai-pro/realtime/openai-sip`, with `answerOpenAISipCalls()` and its types, also on
|
|
69
|
+
`nexus-ai-pro/realtime`;
|
|
70
|
+
- on `nexus-ai-pro/realtime/openai-server` and `nexus-ai-pro/realtime`: `acceptOpenAIRealtimeCall()`,
|
|
71
|
+
`rejectOpenAIRealtimeCall()`, `referOpenAIRealtimeCall()`, `hangupOpenAIRealtimeCall()`,
|
|
72
|
+
`verifyOpenAIWebhook()`, `OpenAIRealtimeCallOptions`, `OpenAIWebhookEvent`, `OpenAIWebhookHeaders`,
|
|
73
|
+
and `OpenAIWebhookVerifyOptions`; `OpenAIWebSocketTransportOptions.callId`.
|
|
74
|
+
|
|
75
|
+
The realtime family stays experimental, and all of these with it.
|
|
76
|
+
|
|
77
|
+
Changed, each to keep a promise the surface already made:
|
|
78
|
+
- A realtime session's raw events keep an audio event's size, `delta_bytes`, in place of its audio,
|
|
79
|
+
unless `retainRawAudio`: a session held every frame it played, about 13 MB a 3-minute call. The
|
|
80
|
+
`openai-events` export follows.
|
|
81
|
+
- A realtime session announces `conversation.updated` only to a listener, as before, but no longer
|
|
82
|
+
copies its record for each event when none is listening.
|
|
83
|
+
- A realtime session given a lifecycle runs each turn as a `realtime.turn` operation and charges it
|
|
84
|
+
as it finishes; its `realtime.session` operation reports the total and charges nothing more. Its
|
|
85
|
+
usage and cost are the sum of its turns', where each turn's replaced the last before.
|
|
86
|
+
|
|
87
|
+
## 2.9 stage (2.9.0)
|
|
88
|
+
|
|
89
|
+
Everything 2.9 adds is stable from the start and additive, unless this section says otherwise.
|
|
90
|
+
|
|
91
|
+
New and stable:
|
|
92
|
+
- `OperationFamily` gains `video`;
|
|
93
|
+
- `nexus-ai-pro/video`, `/video/google`, `/video/runway`, and `/video/mock` are new and experimental:
|
|
94
|
+
video generation graduates by the checklist once Veo and Runway pass its conformance suite live;
|
|
95
|
+
- `RunQuery.after` with `RunCursor`, and `runCursor()` and `isAfterCursor()` on
|
|
96
|
+
`nexus-ai-pro/tracing`. A store of your own that does not honour `after` still works with the
|
|
97
|
+
online-evaluation worker, which falls back to offsets;
|
|
98
|
+
- `OnlineSchedulePass.paging`;
|
|
99
|
+
- on `nexus-ai-pro/lifecycle`: `BudgetReservation.id`; `BudgetReservationStatus`,
|
|
100
|
+
`BudgetReservationRecord`, `BudgetReservationOutcome`, `BudgetRecoveryReport`, and
|
|
101
|
+
`BudgetRecoverOptions`; `BudgetLedger.renew()`, `settle()`, and `recover()`, all optional;
|
|
102
|
+
`BudgetLedgerOptions.leaseMs` and `uncertain`; `BudgetLedgerWithReport.reservation()`; and the five
|
|
103
|
+
optional reservation methods of `BudgetUsageStore` with `BudgetReservationChange`. The statuses,
|
|
104
|
+
and which outcome moves a reservation from which status, are part of the promise;
|
|
105
|
+
- `hold()`, `settle()`, `renew()`, `lapsed()`, and `reservation()` on `MemoryTenantUsage`,
|
|
106
|
+
`RedisTenantUsage`, and `PostgresTenantUsage`, and the tenancy migration version 2 with its
|
|
107
|
+
reservations table;
|
|
108
|
+
- on `nexus-ai-pro/media/rag`, experimental with the media family: `MediaIndexOptions.manifest` and
|
|
109
|
+
`name`, `MediaIndexEntry`, `MediaIndexReader`, `MediaIndexReport.unchanged`, and
|
|
110
|
+
`MediaIndex.delete()`, `reindex()`, `describe()`, and `list()`.
|
|
111
|
+
|
|
112
|
+
Changed, each to keep a promise the surface already made:
|
|
113
|
+
- `TraceStore.query()` orders runs that share a start time by id, from last to first, as code units,
|
|
114
|
+
so a page means the same on every store. Before, that order was not defined.
|
|
115
|
+
- `MemoryBudgetUsage`, the ledger's default store, records reservations, so `reserve()` returns one
|
|
116
|
+
with an `id`. An online-evaluation worker reserves every run of its evaluators, a run after a crash
|
|
117
|
+
included, where before it reserved only a first.
|
|
118
|
+
- A deep agent with a checkpointer writes the helpers it creates to `<thread>/helpers-created`, so
|
|
119
|
+
`createHelpers.max` holds for a thread across processes, as it says.
|
|
120
|
+
|
|
44
121
|
## 2.8 stage (2.8.0)
|
|
45
122
|
|
|
46
123
|
Everything 2.8 adds is stable from the start and additive, unless this section says otherwise.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,248 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [2.10.0] - 2026-10-16
|
|
8
|
+
|
|
9
|
+
Durable conversations, and a second realtime provider.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **A2A tasks that outlive their replica.** A working task's record now holds an `A2aTaskLease` and
|
|
14
|
+
an `A2aTaskRun`. The lease names the replica running the task, which renews it every third of
|
|
15
|
+
`lease.ms` (30 seconds); the run says what the task was asked to do. `a2aHandler()` returns an
|
|
16
|
+
`A2aHandler`: the request handler, plus `recover()` and `close()`. `recover()` takes over working
|
|
17
|
+
tasks whose lease lapsed, and carries each run on in one of three ways:
|
|
18
|
+
- from the graph's last checkpoint;
|
|
19
|
+
- by answering its questions again, when a resume never reached the graph;
|
|
20
|
+
- from the start, when the run left no checkpoint.
|
|
21
|
+
|
|
22
|
+
`recoverEveryMs` runs it on a timer. `A2aTaskStore.claim()` takes a lease in one step, so two
|
|
23
|
+
replicas recovering at once take a task once. `MemoryA2aTaskStore` has it, and `storeA2aTasks()`
|
|
24
|
+
builds it on `putIfVersion()`. A replica that finds its lease taken stops its run, and never
|
|
25
|
+
records its result over the other's.
|
|
26
|
+
- **ACP turns that outlive the agent.** While a prompt runs, its session record holds an
|
|
27
|
+
`AcpTurnRecord`. When the editor reopens the session with `session/load`, the agent carries the
|
|
28
|
+
turn on, streaming its updates: from the graph's last checkpoint, by answering again, or from the
|
|
29
|
+
start. A turn stopped while the editor was deciding an approval asks for it again.
|
|
30
|
+
- **Realtime on the client's lifecycle.** A session given `ai.lifecycle` runs as `realtime.session`,
|
|
31
|
+
and each model turn as `realtime.turn`, carrying the session's `tenantId`, `userId` (the
|
|
32
|
+
`principal`'s by default), and `traceContext`. A turn is admitted as it starts, rate-limited,
|
|
33
|
+
authorized, and holding the average turn cost against the budget, and charged its cost when it
|
|
34
|
+
finishes. A turn the lifecycle refuses is cancelled before the model runs it, and the session ends
|
|
35
|
+
with `lifecycle_refused`. Connections are reported to the client's provider health and circuit
|
|
36
|
+
breaker through the new `reportHealth()`, on `OperationLifecycleLike` and `OperationLifecycle`,
|
|
37
|
+
with `ProviderHealthOutcome` and `LifecycleRuntime.providerHealth`.
|
|
38
|
+
- **Realtime conversations that outlive their process.** `conversationStore`, a
|
|
39
|
+
`RealtimeConversationStore`, keeps the record after each committed turn:
|
|
40
|
+
`MemoryRealtimeConversationStore`, or `storeRealtimeConversations()` on any `Store`. `resume`
|
|
41
|
+
carries a conversation on, from its record or its id: same id, items, metrics, and metadata. On
|
|
42
|
+
connecting, the provider is given what was said and what the tools returned before anything new,
|
|
43
|
+
through `replayRealtimeConversation()`. Echoes carry `REALTIME_REPLAY_PREFIX` and are not recorded
|
|
44
|
+
twice. `conversation.replayItems` caps the replay.
|
|
45
|
+
- **Gemini Live, behind the same realtime session.** `GeminiLiveTransport`, on the new
|
|
46
|
+
`nexus-ai-pro/realtime/gemini-live`, speaks Gemini's Live API, and `GeminiLiveProvider`, on
|
|
47
|
+
`nexus-ai-pro/realtime`, creates sessions on it for `createRealtimeAgent()`. The session's
|
|
48
|
+
configuration becomes Gemini's setup; audio, messages, and tool results become its realtime input,
|
|
49
|
+
turns, and tool responses; and its audio, transcripts, tool calls, interruptions, and usage become
|
|
50
|
+
the events the session already reads. It takes an API key on a server or an ephemeral token
|
|
51
|
+
anywhere, and reconnects a dropped socket with Gemini's latest resumption handle.
|
|
52
|
+
- **Helpers on another server, whose runs outlive their worker.** A `DeepSubagent` with `remote`, a
|
|
53
|
+
`DeepRemoteHelper` such as `createRemoteGraph()` returns, runs on another agent server, as a run
|
|
54
|
+
leased to the worker that runs it: when that worker stops, another takes it over. The agent starts
|
|
55
|
+
the run under a key made from the call that started it, so delegating again or collecting it, in
|
|
56
|
+
this process or another, finds the same run; a background task is marked on its thread first, so a
|
|
57
|
+
run that never reached the server is started by the process that collects it. Its answer comes from
|
|
58
|
+
a `DeepRemoteRunResult`.
|
|
59
|
+
- **Phone calls answered by a realtime agent, over SIP.** `answerOpenAISipCalls()`, on the new
|
|
60
|
+
`nexus-ai-pro/realtime/openai-sip`, handles OpenAI's `realtime.call.incoming` webhook: it verifies
|
|
61
|
+
each delivery, claims it so a retry answers its call once, and answers the call with the session
|
|
62
|
+
`answer()` chooses from the caller, which attaches to the call and runs it with its tools,
|
|
63
|
+
record, and lifecycle; or declines it with a SIP status. `OpenAISipCall` transfers and hangs up.
|
|
64
|
+
The call control is on `nexus-ai-pro/realtime/openai-server` too: `acceptOpenAIRealtimeCall()`,
|
|
65
|
+
`rejectOpenAIRealtimeCall()`, `referOpenAIRealtimeCall()`, `hangupOpenAIRealtimeCall()`, and
|
|
66
|
+
`verifyOpenAIWebhook()`, on Web Crypto. `OpenAIWebSocketTransportOptions.callId` attaches a
|
|
67
|
+
transport to a call.
|
|
68
|
+
- **A published method and budget for realtime turn latency and session load.** `npm run
|
|
69
|
+
bench:realtime`, in the release gate, holds what a session adds to a message (under 5 ms at p99 for
|
|
70
|
+
one call, under 20 ms with 250 calls at real time), that a turn late in a long call costs what an
|
|
71
|
+
early one does, that 250 calls at real time keep up, the heap each session holds, and that ended
|
|
72
|
+
sessions leave nothing. Budgets are in `realtime-budget.json`.
|
|
73
|
+
- **Realtime over real WebRTC in a browser.** `npm run test:browser:realtime`, in `npm test`, drives a
|
|
74
|
+
session in headless Chromium against a peer connection in the page that plays the provider: real
|
|
75
|
+
SDP, ICE, DTLS, and data channel, the microphone up, the model's voice down, a tool call, and a
|
|
76
|
+
session with no microphone.
|
|
77
|
+
- `retainRawAudio` on a realtime conversation's options, `inPlace` on
|
|
78
|
+
`RealtimeConversationReducerOptions`, and `addTransceiver()`, optional, on
|
|
79
|
+
`RealtimePeerConnectionLike`.
|
|
80
|
+
- **Video and images in a realtime session.** `sendVideoFrame()` streams camera or screen frames:
|
|
81
|
+
as realtime video on Gemini Live, as images in the conversation elsewhere, spaced by
|
|
82
|
+
`RealtimeVideoOptions.minIntervalMs`. `sendImage()` adds an image, with text, to the conversation.
|
|
83
|
+
`RealtimeTransport.sendVideo()`, optional, is what a transport with video input implements.
|
|
84
|
+
- `continueWith()` on a compiled graph: `continue()`, returning the final result.
|
|
85
|
+
`ProtocolGraph.continueWith()` and `state()`, both optional, are what A2A and ACP carry a run on
|
|
86
|
+
with.
|
|
87
|
+
|
|
88
|
+
### Fixed
|
|
89
|
+
|
|
90
|
+
- The WebRTC transport trimmed the SDP answer's final line break, and Chromium refuses a description
|
|
91
|
+
whose last line has none, so a session in Chrome or Edge never connected.
|
|
92
|
+
- A WebRTC session that sent no microphone offered no audio to receive, so it never heard the model.
|
|
93
|
+
- A realtime session copied its whole conversation record twice for every event, so each event cost
|
|
94
|
+
more as the call went on: a turn took 39 ms by the 500th, and 250 calls took half a core. It now
|
|
95
|
+
applies events in place and copies the record only for a `conversation.updated` listener.
|
|
96
|
+
- An A2A `SendMessage` with `returnImmediately`, whose store failed while the task ran, raised an
|
|
97
|
+
unhandled rejection, which ends a Node process by default. A store that cannot record a task's
|
|
98
|
+
failure now leaves the task working, with its lease, for the replica that takes it over.
|
|
99
|
+
- A realtime session's usage and estimated cost are the sum of its responses'. Before, each
|
|
100
|
+
response's usage replaced the last, so a session recorded, and settled, only its last turn.
|
|
101
|
+
|
|
102
|
+
### Proof
|
|
103
|
+
|
|
104
|
+
- `tests/conversation-crash.test.ts`: an A2A task, an ACP turn, and a realtime conversation are each
|
|
105
|
+
killed at every write a clean run makes, and taken over elsewhere. Each ends as the clean run did,
|
|
106
|
+
or its caller was told it never started; nothing is left half done, and nothing is recorded twice.
|
|
107
|
+
The suite found three defects before they shipped, each fixed: an unhandled rejection from a
|
|
108
|
+
failing store; a task submitted to a replica that stopped before starting it, which no replica
|
|
109
|
+
would ever start; and a reopened session's save that dropped the turn it was about to carry on.
|
|
110
|
+
- `tests/deep-agent-remote-helpers.test.ts`: against two replicas of a real agent server, the agent's
|
|
111
|
+
process and the worker running its remote helper both die; the second replica takes the run over,
|
|
112
|
+
and the agent, resumed in a new process, collects that same run: one run, its answer, and the
|
|
113
|
+
agent's note not written again. Without the key, the resumed agent starts a second run, and the
|
|
114
|
+
proof fails.
|
|
115
|
+
- `tests/realtime-sip.test.ts`: a signed incoming-call delivery is accepted with the agent's session,
|
|
116
|
+
chosen from the caller's number; the session attaches by the call's id, books a table through its
|
|
117
|
+
tool when the caller asks, and hangs up. Forged, altered, and stale deliveries answer nothing; a
|
|
118
|
+
retry after a failed accept answers the call, and a duplicate, on one replica or two, answers it
|
|
119
|
+
once. Without the signature check, the claim, or the release, a test fails.
|
|
120
|
+
- `tests/realtime-gemini.test.ts`: one agent definition (instructions, a tool, conversation settings)
|
|
121
|
+
runs on OpenAI's realtime API and on Gemini Live, each over its own scripted wire protocol. The two
|
|
122
|
+
records match item for item: what the person asked, the tool call and its result, and what the model
|
|
123
|
+
answered, with the same usage, and the tool runs once on each. Without the tool response, or with
|
|
124
|
+
the request for a response not dropped after it, the proof fails.
|
|
125
|
+
- `tests/realtime-lifecycle.test.ts`: through a real client with a five-cent budget, two
|
|
126
|
+
two-cent turns run, and the third is cancelled as it starts and ends the session; the budget holds
|
|
127
|
+
four cents, and every operation names the tenant and user. Connection failures open the client's
|
|
128
|
+
circuit. Without the admission at the turn's start, the proof fails.
|
|
129
|
+
- `tests/realtime-resume.test.ts`: a session's process is killed mid-conversation on the memory and
|
|
130
|
+
SQLite stores. Another resumes it by id on a new transport, and gives the provider what was said
|
|
131
|
+
before anything new. The conversation goes on under its own id with nothing recorded twice.
|
|
132
|
+
Without the replay, or without skipping the echoes, the proof fails.
|
|
133
|
+
- `tests/a2a-takeover.test.ts`: a task whose replica dies halfway through a tool is taken over, on
|
|
134
|
+
the memory, SQLite, Postgres, and Redis stores. The model call the dead replica finished is not
|
|
135
|
+
made again, and the email it never sent is sent once. Two replicas recovering at the same moment
|
|
136
|
+
take the task over once, and a lease renewed while a slow task works is never taken. Without the
|
|
137
|
+
lease check, or carried on from the start instead of the checkpoint, the proof fails.
|
|
138
|
+
- `tests/acp-turns.test.ts`: an agent stopped halfway through a tool is started again and its
|
|
139
|
+
session reopened; the turn goes on without repeating the finished model call, and sends the email
|
|
140
|
+
once. A turn stopped during an approval asks again. Without the recovery on load, the proof fails.
|
|
141
|
+
|
|
142
|
+
## [2.9.0] - 2026-10-15
|
|
143
|
+
|
|
144
|
+
Video, and everything shipped, proven.
|
|
145
|
+
|
|
146
|
+
### Added
|
|
147
|
+
|
|
148
|
+
- **Video generation.** `nexus-ai-pro/video`: `VideoManager` makes videos from a prompt, a first or
|
|
149
|
+
last frame, or reference images, through a provider's asynchronous job. `generate()` is a durable
|
|
150
|
+
operation that records the job the moment it exists and each video as it is stored, so
|
|
151
|
+
`recover()` in another worker collects a job whose process died, never submitting it twice, and
|
|
152
|
+
streams each video into an asset store. Each generation runs in the lifecycle's new `video`
|
|
153
|
+
family, and is priced per second by `priceVideo()` from `VIDEO_PRICES`, the Veo 3.1 list prices,
|
|
154
|
+
or the account's own. `start()`, `resume()`, `status()`, `download()`, and `cancel()` work from a
|
|
155
|
+
JSON `VideoJobRef` in any process. A request is checked against the model's capabilities before
|
|
156
|
+
any job, and a failed job is submitted again only when the provider says that may help.
|
|
157
|
+
- `GoogleVideoProvider` (`/video/google`): Veo through its long-running predictions, on the
|
|
158
|
+
Gemini API, the video read from Google's URI with the key, or on Vertex AI, read from Cloud
|
|
159
|
+
Storage or inline.
|
|
160
|
+
- `RunwayVideoProvider` (`/video/runway`): Runway's task API, for its own models and the hosted
|
|
161
|
+
ones it serves, Veo, Gemini Omni Flash, and Seedance among them; each video read from its signed
|
|
162
|
+
URL without the secret.
|
|
163
|
+
- `MockVideoProvider` (`/video/mock`), and `runVideoConformance()`, which all three pass.
|
|
164
|
+
|
|
165
|
+
New guide `docs/video.md`. The family is experimental until Veo and Runway pass the conformance
|
|
166
|
+
suite live. OpenAI's Videos API was removed on 2026-09-24, so there is no OpenAI adapter.
|
|
167
|
+
- **The experimental families, live.** `npm run test:families:live`, and a weekly *Family
|
|
168
|
+
conformance* job beside the vector stores: media understanding's conformance suite against an
|
|
169
|
+
OpenAI or Gemini vision model and Mistral's OCR, on a PDF and a PNG the script builds; a two-input
|
|
170
|
+
batch through the Gemini embedding batch tier, waited on for 20 minutes and cancelled past that;
|
|
171
|
+
and, when the repository variable `NEXUS_LIVE_VIDEO` is set, the video conformance suite on Veo 3.1
|
|
172
|
+
Lite and Runway. Each check runs where its key is set, and what it measured is kept. These are the
|
|
173
|
+
runs media, the Gemini batch tier, and video graduate on.
|
|
174
|
+
- **Paging by cursor.** `RunQuery.after` takes a `RunCursor`, the start time and id of a page's last
|
|
175
|
+
run, and returns only the runs after it; `runCursor()` takes the cursor from a run, and
|
|
176
|
+
`isAfterCursor()` is the test on its own, for a trace store of your own. Every bundled store
|
|
177
|
+
honours it, and `applyQuery()` does for a store that filters in memory. A new Postgres trace store
|
|
178
|
+
migration, version 3, indexes runs in query order.
|
|
179
|
+
- **Budget reservations that outlive their process.** Over a store that records reservations, a
|
|
180
|
+
`budgetLedger()` holds each one as a `BudgetReservationRecord` under a lease (`leaseMs`, 10
|
|
181
|
+
minutes), and moves it with its total in one step: `reserved`, then `settled`, `released`,
|
|
182
|
+
`expired`, or `uncertain`. `recover(resolve)` settles reservations whose lease lapsed by what
|
|
183
|
+
became of each operation, a `BudgetReservationOutcome`; `settle(id, outcome)` does it for one from
|
|
184
|
+
any process; `renew()` extends a lease; and `uncertain: 'charge' | 'release'` says what an unknown
|
|
185
|
+
outcome costs. `MemoryBudgetUsage`, `MemoryTenantUsage`, `RedisTenantUsage`, and
|
|
186
|
+
`PostgresTenantUsage` all record them: Redis keeps a reservation in its total's Cluster slot, so one
|
|
187
|
+
script moves both, and Postgres does it in one statement, in the reservations table of a new
|
|
188
|
+
tenancy migration, version 2. A store of your own records them through five optional methods on
|
|
189
|
+
`BudgetUsageStore`.
|
|
190
|
+
- **A media index that remembers what it holds.** `MediaIndexOptions.manifest`, any `Store`, with a
|
|
191
|
+
`name`, keeps one `MediaIndexEntry` per medium: a hash of its content, source, metadata, and
|
|
192
|
+
indexing options, where it is kept, its pages, its chunk ids, the embedding model, and the
|
|
193
|
+
`MediaIndexReader` that read and described it. `add()` then skips a medium at the same version and
|
|
194
|
+
replaces exactly the chunks and asset of one that changed, and `MediaIndex` gains `delete()`,
|
|
195
|
+
`reindex()`, `describe()`, and `list()`. `MediaIndexReport.unchanged` counts what was skipped. A
|
|
196
|
+
change is recorded as pending first, so a crash leaves nothing the next call cannot finish.
|
|
197
|
+
|
|
198
|
+
### Changed
|
|
199
|
+
|
|
200
|
+
- Runs that started in the same millisecond come by id, from last to first, compared as code units,
|
|
201
|
+
on every trace store. The order of such runs was not defined before, and the memory and Postgres
|
|
202
|
+
stores disagreed on it.
|
|
203
|
+
|
|
204
|
+
### Fixed
|
|
205
|
+
|
|
206
|
+
- An online-evaluation worker reads a backlog of runs that started in the same millisecond through,
|
|
207
|
+
however many pages it spans. Before, it resumed each page at the last run's start time, so a page
|
|
208
|
+
that ended among such runs was read again on every pass, and `drain()` never finished. On a trace
|
|
209
|
+
store of your own that does not honour `RunQuery.after`, the worker pages by offset instead, and
|
|
210
|
+
its `OnlineSchedulePass` says `paging: 'offset'`.
|
|
211
|
+
- A budget reservation held by an online-evaluation worker that dies no longer stays charged for the
|
|
212
|
+
period. The worker records the reservation just before it calls its evaluators, and whoever takes
|
|
213
|
+
over settles it: charged at what the scores cost if they were written, given back if the
|
|
214
|
+
evaluators never ran, and by the ledger's `uncertain` policy if they ran and wrote nothing.
|
|
215
|
+
- An evaluation run again after a crash is reserved and charged. Its evaluators run again to find
|
|
216
|
+
the scores still missing, and before, only a first run drew on the budget.
|
|
217
|
+
- A deep agent resumed after a crash counts the helpers it created before. With a checkpointer, each
|
|
218
|
+
created helper is recorded, with its instructions, on `<thread>/helpers-created` before the call
|
|
219
|
+
that created it returns, so the resumed agent cannot pass `createHelpers.max` and delegates to them
|
|
220
|
+
again by name. Before, the count was kept by the process, and a resumed agent could create `max`
|
|
221
|
+
more and no longer found the ones it had made.
|
|
222
|
+
|
|
223
|
+
### Proof
|
|
224
|
+
|
|
225
|
+
- `tests/video.test.ts`: the mock, Veo on the Gemini API and Vertex AI, and Runway pass the video
|
|
226
|
+
conformance suite against stand-ins for their APIs. A request a model cannot make is refused
|
|
227
|
+
before any job. A generation runs through the lifecycle, streams three megabytes into a
|
|
228
|
+
filesystem store, and is priced per second. A generation whose process dies after submitting, or
|
|
229
|
+
after storing its video, is collected by another worker with one submission and one stored video.
|
|
230
|
+
If the recorded job is ignored, the job is submitted twice.
|
|
231
|
+
- `tests/online-evaluation-paging.test.ts`: the memory, JSONL, and Postgres trace stores order 5,000
|
|
232
|
+
runs, 4,000 of them in one millisecond, identically, and page through them by cursor without a
|
|
233
|
+
repeat or a gap. Ten workers reading 1,000 a page schedule each of them exactly once on every
|
|
234
|
+
store, and on a store that ignores the cursor. The previous worker never reads past the burst.
|
|
235
|
+
- `tests/budget-reservations.test.ts`: on the memory, tenant memory, Redis, and Postgres stores a
|
|
236
|
+
reservation moves with its total, is settled from another process by its operation's outcome, and
|
|
237
|
+
of two processes settling it, one changes the total. An online-evaluation worker killed while
|
|
238
|
+
holding a reservation, before its evaluator, during it, or after its scores, leaves the budget at
|
|
239
|
+
what the judge billed once another worker takes over. With recovery switched off, the same test
|
|
240
|
+
finds the dead worker's estimate still charged.
|
|
241
|
+
- `tests/media-rag.test.ts`: with a manifest, an unchanged medium is neither read nor written, a
|
|
242
|
+
changed one keeps exactly its chunks and one asset, and a deleted one leaves none, with a crash at
|
|
243
|
+
each write of a first index, a change, and a delete. Without the pending record, the crash proof
|
|
244
|
+
fails.
|
|
245
|
+
- `tests/deep-agent-helpers.test.ts`: an agent that created its last allowed helper, killed and
|
|
246
|
+
continued in another process, is refused a second and delegates to the first again by name. Without
|
|
247
|
+
the record read back, the second is created.
|
|
248
|
+
|
|
7
249
|
## [2.8.0] - 2026-10-14
|
|
8
250
|
|
|
9
251
|
Retrieval at production scale, and media that is understood.
|
|
@@ -2452,7 +2694,9 @@ shared policy vocabulary is in place for a future release that revisits this.
|
|
|
2452
2694
|
- URL fetching rejects unsafe private-network targets and limits response reads.
|
|
2453
2695
|
- CLI and audit findings no longer disclose detected secret values.
|
|
2454
2696
|
|
|
2455
|
-
[Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.
|
|
2697
|
+
[Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.10.0...HEAD
|
|
2698
|
+
[2.10.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.9.0...v2.10.0
|
|
2699
|
+
[2.9.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.8.0...v2.9.0
|
|
2456
2700
|
[2.8.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.7.0...v2.8.0
|
|
2457
2701
|
[2.7.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.6.0...v2.7.0
|
|
2458
2702
|
[2.6.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.5.0...v2.6.0
|
package/README.md
CHANGED
|
@@ -156,6 +156,7 @@ from the doc comments. The guides live in the repository, so these links go to G
|
|
|
156
156
|
| [Retrieval](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/retrieval.md) | Hybrid search, reranking, MMR, parent documents, and Redis, Pinecone, Weaviate, and Chroma stores |
|
|
157
157
|
| [Embeddings and retrieval](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/embeddings.md) | Embeddings as a routed operation family, and RAG helpers |
|
|
158
158
|
| [Media understanding](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/media.md) | Reading documents and images page by page, describing images, and answering questions about them, through any vision model or OCR |
|
|
159
|
+
| [Video generation](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/video.md) | Videos from a prompt or a first frame, on Google Veo and Runway, as durable jobs streamed into an asset store and priced per second |
|
|
159
160
|
| [Images (experimental)](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/images.md) | Generation and masked edits over three backends, input safety, moderation, asset stores |
|
|
160
161
|
| [Voice](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/voice.md) | Transcription, speech, voice turns, and voice sessions |
|
|
161
162
|
| [Realtime voice](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/realtime.md) | Browser and server realtime sessions with barge-in, tools, and exports |
|
|
@@ -217,11 +218,13 @@ interface, or enum, has no doc comment, and `npm run docs:guides` fails when a g
|
|
|
217
218
|
every export of the entry points it covers, or its generated reference is stale. Both hold at 100%;
|
|
218
219
|
`--list` names what is missing, and `npm run docs:update` regenerates the references.
|
|
219
220
|
|
|
220
|
-
Real provider conformance,
|
|
221
|
+
Real provider conformance, the vector store contract against real servers, and the experimental
|
|
222
|
+
families against their live services are opt-in:
|
|
221
223
|
|
|
222
224
|
```bash
|
|
223
225
|
npm run test:conformance:real
|
|
224
226
|
npm run test:vectors:live
|
|
227
|
+
npm run test:families:live
|
|
225
228
|
```
|
|
226
229
|
|
|
227
230
|
## Roadmap
|
|
@@ -285,12 +288,25 @@ Shipped so far:
|
|
|
285
288
|
Chroma stores out of experimental after their checklist ran against the real servers; a shared
|
|
286
289
|
embedding cache; images, audio, and documents as embedding input; a half-price embedding batch tier
|
|
287
290
|
that ingestion survives a crash through; streaming asset stores; registry data you supply and
|
|
288
|
-
refresh; and reading, describing, and searching media, with answers given the media themselves
|
|
291
|
+
refresh; and reading, describing, and searching media, with answers given the media themselves;
|
|
292
|
+
- 2.9 video, and everything shipped, proven: videos from a prompt or a first frame on Google Veo and
|
|
293
|
+
Runway, as durable jobs another worker collects after a crash, streamed into an asset store and
|
|
294
|
+
priced per second; online evaluation that reads every run however many share a millisecond, and
|
|
295
|
+
budget reservations a dead worker leaves settled by what its call did; a deep agent's helper limit
|
|
296
|
+
across processes; a media index that remembers what it holds; and the experimental families run
|
|
297
|
+
against their live services every week;
|
|
298
|
+
- 2.10 durable conversations, and a second realtime provider: A2A tasks, ACP turns, and realtime
|
|
299
|
+
sessions taken over by another worker when theirs dies, each proven killed at every write; realtime
|
|
300
|
+
on the client's lifecycle; Gemini Live behind the same session, with video and images; phone calls
|
|
301
|
+
answered over SIP; helpers on another server whose runs outlive their worker; and realtime over
|
|
302
|
+
real WebRTC in a browser, with published budgets for turn latency and session load.
|
|
289
303
|
|
|
290
304
|
Upgrading from 1.x: `npx nexus migrate src --write` moves your imports, and
|
|
291
305
|
[MIGRATING.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/MIGRATING.md) covers the rest.
|
|
292
|
-
Next,
|
|
293
|
-
|
|
306
|
+
Next, 2.11 separates where an agent's files live from where its code runs, with workspaces, and
|
|
307
|
+
completes telephony: a second provider, messaging, recording, conference, and transfer. Pinecone,
|
|
308
|
+
images, the Gemini embedding batch tier, media understanding, and video graduate once the live
|
|
309
|
+
workflow passes against each. The
|
|
294
310
|
[roadmap](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/ROADMAP.md) lists what comes after, in the order
|
|
295
311
|
applications ask for it.
|
|
296
312
|
|
package/dist/batch/poll.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type BatchJobState } from '../types/batch.js';
|
|
2
|
-
/** How a batch is waited on. */
|
|
3
|
-
export interface BatchPollOptions {
|
|
2
|
+
/** How a batch, or any job with a status, is waited on. */
|
|
3
|
+
export interface BatchPollOptions<State = BatchJobState> {
|
|
4
4
|
/** The first wait between polls, in milliseconds. */
|
|
5
5
|
intervalMs: number;
|
|
6
6
|
/** The longest wait, once backoff has grown it. */
|
|
@@ -8,11 +8,15 @@ export interface BatchPollOptions {
|
|
|
8
8
|
/** Stops waiting. The provider-side batch keeps running. */
|
|
9
9
|
signal: AbortSignal;
|
|
10
10
|
/** Called with each state that is not yet final. */
|
|
11
|
-
onState?: (state:
|
|
11
|
+
onState?: (state: State) => void;
|
|
12
|
+
/** Whether a state is final. Defaults to a batch's terminal statuses. */
|
|
13
|
+
settled?: (state: State) => boolean;
|
|
12
14
|
}
|
|
13
15
|
/**
|
|
14
16
|
* Polls a batch until it reaches a status it never leaves. A batch measured in hours does not deserve
|
|
15
17
|
* a fixed short interval: the wait grows by half each time up to the maximum, which keeps the poll
|
|
16
18
|
* count sane without delaying a fast batch, which the first few short polls still catch.
|
|
17
19
|
*/
|
|
18
|
-
export declare function pollUntilSettled
|
|
20
|
+
export declare function pollUntilSettled<State extends {
|
|
21
|
+
status: string;
|
|
22
|
+
} = BatchJobState>(poll: () => Promise<State>, options: BatchPollOptions<State>): Promise<State>;
|
package/dist/batch/poll.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { isTerminalBatchStatus } from '../types/batch.js';
|
|
2
2
|
export async function pollUntilSettled(poll, options) {
|
|
3
|
+
const settled = options.settled ?? ((state) => isTerminalBatchStatus(state.status));
|
|
3
4
|
let interval = options.intervalMs;
|
|
4
5
|
let state = await poll();
|
|
5
|
-
while (!
|
|
6
|
+
while (!settled(state)) {
|
|
6
7
|
if (options.signal.aborted)
|
|
7
8
|
throw options.signal.reason ?? new Error('aborted');
|
|
8
9
|
options.onState?.(state);
|
package/dist/core/lifecycle.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RateLimitConfig } from '../types/config.js';
|
|
2
|
-
import type { LifecycleConfig, LifecycleStage, OperationDescriptor, OperationLifecycleLike, OperationResultInfo, OperationStartOptions, OperationTicket, ProviderCallContext } from '../types/lifecycle.js';
|
|
2
|
+
import type { ProviderHealthOutcome, LifecycleConfig, LifecycleStage, OperationDescriptor, OperationLifecycleLike, OperationResultInfo, OperationStartOptions, OperationTicket, ProviderCallContext } from '../types/lifecycle.js';
|
|
3
3
|
import type { AuditLogger } from '../ops/audit-logger.js';
|
|
4
4
|
import type { MetricsCollector } from '../ops/metrics.js';
|
|
5
5
|
import { type RateLimiter } from '../ops/rate-limiter.js';
|
|
@@ -45,6 +45,11 @@ export interface LifecycleRuntime {
|
|
|
45
45
|
config?: LifecycleConfig;
|
|
46
46
|
/** Default milliseconds an operation may run before its signal aborts. */
|
|
47
47
|
timeoutMs?: number;
|
|
48
|
+
/** Where `reportHealth()` goes: the client's provider health and circuit breaker. */
|
|
49
|
+
providerHealth?: {
|
|
50
|
+
recordSuccess(provider: string, latencyMs: number): void;
|
|
51
|
+
recordFailure(provider: string, error: unknown): void;
|
|
52
|
+
};
|
|
48
53
|
}
|
|
49
54
|
/** The family's part of one operation: what each stage does for it. Every stage but `execute` is optional. */
|
|
50
55
|
export interface OperationPlan<T> {
|
|
@@ -99,6 +104,11 @@ export declare class OperationLifecycle implements OperationLifecycleLike {
|
|
|
99
104
|
}, plan: OperationPlan<T>, options?: OperationStartOptions & {
|
|
100
105
|
audit?: Record<string, unknown>;
|
|
101
106
|
}): Promise<T>;
|
|
107
|
+
/**
|
|
108
|
+
* Reports how a provider answered a call a family made itself, such as a realtime connection, to the
|
|
109
|
+
* client's provider health and circuit breaker.
|
|
110
|
+
*/
|
|
111
|
+
reportHealth(provider: string, outcome: ProviderHealthOutcome): void;
|
|
102
112
|
/** True when nothing is configured, so a caller may skip work that only feeds the lifecycle. */
|
|
103
113
|
get inert(): boolean;
|
|
104
114
|
private checkRateLimit;
|
package/dist/core/lifecycle.js
CHANGED
|
@@ -91,6 +91,15 @@ export class OperationLifecycle {
|
|
|
91
91
|
throw error;
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
+
reportHealth(provider, outcome) {
|
|
95
|
+
const health = this.runtime.providerHealth;
|
|
96
|
+
if (!health || !provider)
|
|
97
|
+
return;
|
|
98
|
+
if (outcome.ok)
|
|
99
|
+
health.recordSuccess(provider, outcome.latencyMs);
|
|
100
|
+
else
|
|
101
|
+
health.recordFailure(provider, outcome.error);
|
|
102
|
+
}
|
|
94
103
|
get inert() {
|
|
95
104
|
const config = this.runtime.config;
|
|
96
105
|
return (!this.runtime.metrics?.enabled &&
|
package/dist/core/nexus.js
CHANGED
|
@@ -75,6 +75,10 @@ export class NexusAI {
|
|
|
75
75
|
rateLimiter: this.rateLimiter,
|
|
76
76
|
rateLimit: this.config.rateLimit,
|
|
77
77
|
config: this.config.lifecycle,
|
|
78
|
+
providerHealth: {
|
|
79
|
+
recordSuccess: (provider, latencyMs) => this.attemptHooks.onAttemptSuccess(provider, latencyMs),
|
|
80
|
+
recordFailure: (provider, error) => this.attemptHooks.onAttemptFailure(provider, error),
|
|
81
|
+
},
|
|
78
82
|
});
|
|
79
83
|
const familyRuntime = {
|
|
80
84
|
metrics: this.metrics,
|
|
@@ -6,6 +6,14 @@
|
|
|
6
6
|
* its checkpoints to the deep agent's checkpointer. A crash leaves the helper's finished steps behind
|
|
7
7
|
* it: collecting the task in a new process carries the helper on from its last checkpoint, and a call
|
|
8
8
|
* the agent repeats after the crash finds the same thread instead of starting the helper again.
|
|
9
|
+
*
|
|
10
|
+
* The helpers an agent creates are recorded on a thread of their own beside its own, before the call
|
|
11
|
+
* that creates one returns, so an agent resumed in another process counts them against its `max` and
|
|
12
|
+
* delegates to them again by name.
|
|
13
|
+
*
|
|
14
|
+
* A remote helper runs on another agent server, as a run that server's workers lease: when the worker
|
|
15
|
+
* running it stops, another takes it over. The agent starts it under a key made from the call that
|
|
16
|
+
* started it, so delegating again or collecting it, in this process or another, finds that same run.
|
|
9
17
|
*/
|
|
10
18
|
import { type CreateAgentOptions } from '../agent/create-agent.js';
|
|
11
19
|
import type { PermissionPolicyLike } from '../agent/permissions.js';
|
|
@@ -25,6 +33,33 @@ export interface DeepHelperLimits {
|
|
|
25
33
|
/** How many helpers the agent may create in one thread. Defaults to 5. */
|
|
26
34
|
max?: number;
|
|
27
35
|
}
|
|
36
|
+
/** How a run on another agent server ended, as `createRemoteGraph()` reports it. */
|
|
37
|
+
export interface DeepRemoteRunResult {
|
|
38
|
+
/** The run's id on that server. */
|
|
39
|
+
runId: string;
|
|
40
|
+
/** Where it ended: `succeeded`, `failed`, `cancelled`, `expired`, or `awaiting_input`. */
|
|
41
|
+
status: string;
|
|
42
|
+
/** What it produced. */
|
|
43
|
+
output?: unknown;
|
|
44
|
+
/** Why it failed. */
|
|
45
|
+
error?: {
|
|
46
|
+
message?: string;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A helper on another agent server: what `createRemoteGraph()` returns, or anything that runs an input
|
|
51
|
+
* there and waits for it.
|
|
52
|
+
*/
|
|
53
|
+
export interface DeepRemoteHelper {
|
|
54
|
+
/**
|
|
55
|
+
* Runs an input there and waits for it to end. A second call with the same `idempotencyKey` waits
|
|
56
|
+
* for the run the first started, as the agent server does, rather than starting another.
|
|
57
|
+
*/
|
|
58
|
+
invoke(input: unknown, options: {
|
|
59
|
+
idempotencyKey: string;
|
|
60
|
+
signal?: AbortSignal;
|
|
61
|
+
}): Promise<DeepRemoteRunResult>;
|
|
62
|
+
}
|
|
28
63
|
/** What the deep agent hands the helper tools. */
|
|
29
64
|
export interface HelperSetup {
|
|
30
65
|
/** The helpers the application declared. */
|
|
@@ -35,6 +70,7 @@ export interface HelperSetup {
|
|
|
35
70
|
tools?: readonly string[];
|
|
36
71
|
model?: string;
|
|
37
72
|
maxIterations?: number;
|
|
73
|
+
remote?: DeepRemoteHelper;
|
|
38
74
|
}>;
|
|
39
75
|
/** Limits on created helpers, when the agent may create them. */
|
|
40
76
|
create?: DeepHelperLimits;
|