nexus-ai-pro 2.4.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/API_STABILITY.md +121 -0
  2. package/CHANGELOG.md +248 -1
  3. package/README.md +14 -3
  4. package/SECURITY.md +78 -9
  5. package/dist/agent/create-agent.d.ts +37 -2
  6. package/dist/agent/create-agent.js +62 -4
  7. package/dist/agent/index.d.ts +2 -1
  8. package/dist/agent/tool.d.ts +19 -4
  9. package/dist/agent/tool.js +27 -4
  10. package/dist/context-hub/hub.d.ts +53 -0
  11. package/dist/context-hub/hub.js +108 -3
  12. package/dist/context-hub/index.d.ts +2 -1
  13. package/dist/context-hub/index.js +2 -1
  14. package/dist/context-hub/signing.d.ts +95 -0
  15. package/dist/context-hub/signing.js +88 -0
  16. package/dist/doctor/index.d.ts +4 -2
  17. package/dist/doctor/index.js +29 -0
  18. package/dist/evaluate/threads.d.ts +187 -0
  19. package/dist/evaluate/threads.js +187 -0
  20. package/dist/graph/event-stream.js +2 -0
  21. package/dist/graph/graph.js +4 -1
  22. package/dist/mcp/client.d.ts +9 -1
  23. package/dist/mcp/client.js +38 -8
  24. package/dist/mcp/server.js +42 -2
  25. package/dist/operations/adapters.d.ts +2 -1
  26. package/dist/operations/runner.js +2 -0
  27. package/dist/operations/stats.js +11 -4
  28. package/dist/postgres/index.d.ts +2 -1
  29. package/dist/postgres/index.js +4 -0
  30. package/dist/postgres/operations.js +11 -4
  31. package/dist/postgres/store.d.ts +7 -0
  32. package/dist/postgres/store.js +21 -0
  33. package/dist/postgres/tenancy.d.ts +43 -0
  34. package/dist/postgres/tenancy.js +71 -0
  35. package/dist/protocols/a2a.d.ts +166 -1
  36. package/dist/protocols/a2a.js +499 -132
  37. package/dist/protocols/acp-editor.d.ts +43 -0
  38. package/dist/protocols/acp-editor.js +164 -0
  39. package/dist/protocols/acp.d.ts +76 -3
  40. package/dist/protocols/acp.js +180 -7
  41. package/dist/protocols/ag-ui.d.ts +23 -0
  42. package/dist/protocols/ag-ui.js +128 -8
  43. package/dist/server/deployments.d.ts +127 -12
  44. package/dist/server/deployments.js +402 -37
  45. package/dist/server/metrics.d.ts +2 -0
  46. package/dist/server/metrics.js +7 -0
  47. package/dist/server/runs.d.ts +9 -1
  48. package/dist/server/runs.js +46 -7
  49. package/dist/server/server.js +4 -1
  50. package/dist/server/state.d.ts +12 -2
  51. package/dist/server/state.js +15 -0
  52. package/dist/sqlite/operations.js +15 -4
  53. package/dist/sqlite/store.d.ts +7 -0
  54. package/dist/sqlite/store.js +28 -0
  55. package/dist/store/memory.d.ts +8 -0
  56. package/dist/store/memory.js +12 -1
  57. package/dist/store/redis.d.ts +13 -0
  58. package/dist/store/redis.js +38 -0
  59. package/dist/tracing/instrument.js +15 -3
  60. package/dist/tracing/tracer.d.ts +4 -0
  61. package/dist/tracing/tracer.js +6 -0
  62. package/dist/types/graph.d.ts +7 -0
  63. package/dist/types/messages.d.ts +12 -0
  64. package/dist/types/operations.d.ts +7 -0
  65. package/dist/types/server.d.ts +61 -2
  66. package/dist/types/store.d.ts +9 -0
  67. package/dist/version.d.ts +1 -1
  68. package/dist/version.js +1 -1
  69. package/dist-cjs/agent/create-agent.d.ts +37 -2
  70. package/dist-cjs/agent/create-agent.js +62 -4
  71. package/dist-cjs/agent/index.d.ts +2 -1
  72. package/dist-cjs/agent/tool.d.ts +19 -4
  73. package/dist-cjs/agent/tool.js +27 -4
  74. package/dist-cjs/context-hub/hub.d.ts +53 -0
  75. package/dist-cjs/context-hub/hub.js +110 -4
  76. package/dist-cjs/context-hub/index.d.ts +2 -1
  77. package/dist-cjs/context-hub/index.js +7 -1
  78. package/dist-cjs/context-hub/signing.d.ts +95 -0
  79. package/dist-cjs/context-hub/signing.js +96 -0
  80. package/dist-cjs/doctor/index.d.ts +4 -2
  81. package/dist-cjs/doctor/index.js +29 -0
  82. package/dist-cjs/evaluate/threads.d.ts +187 -0
  83. package/dist-cjs/evaluate/threads.js +198 -0
  84. package/dist-cjs/graph/event-stream.js +2 -0
  85. package/dist-cjs/graph/graph.js +4 -1
  86. package/dist-cjs/mcp/client.d.ts +9 -1
  87. package/dist-cjs/mcp/client.js +37 -7
  88. package/dist-cjs/mcp/server.js +42 -2
  89. package/dist-cjs/operations/adapters.d.ts +2 -1
  90. package/dist-cjs/operations/runner.js +2 -0
  91. package/dist-cjs/operations/stats.js +11 -4
  92. package/dist-cjs/postgres/index.d.ts +2 -1
  93. package/dist-cjs/postgres/index.js +8 -1
  94. package/dist-cjs/postgres/operations.js +11 -4
  95. package/dist-cjs/postgres/store.d.ts +7 -0
  96. package/dist-cjs/postgres/store.js +21 -0
  97. package/dist-cjs/postgres/tenancy.d.ts +43 -0
  98. package/dist-cjs/postgres/tenancy.js +110 -0
  99. package/dist-cjs/protocols/a2a.d.ts +166 -1
  100. package/dist-cjs/protocols/a2a.js +501 -132
  101. package/dist-cjs/protocols/acp-editor.d.ts +43 -0
  102. package/dist-cjs/protocols/acp-editor.js +167 -0
  103. package/dist-cjs/protocols/acp.d.ts +76 -3
  104. package/dist-cjs/protocols/acp.js +183 -8
  105. package/dist-cjs/protocols/ag-ui.d.ts +23 -0
  106. package/dist-cjs/protocols/ag-ui.js +127 -7
  107. package/dist-cjs/server/deployments.d.ts +127 -12
  108. package/dist-cjs/server/deployments.js +402 -37
  109. package/dist-cjs/server/metrics.d.ts +2 -0
  110. package/dist-cjs/server/metrics.js +8 -0
  111. package/dist-cjs/server/runs.d.ts +9 -1
  112. package/dist-cjs/server/runs.js +45 -6
  113. package/dist-cjs/server/server.js +4 -1
  114. package/dist-cjs/server/state.d.ts +12 -2
  115. package/dist-cjs/server/state.js +15 -0
  116. package/dist-cjs/sqlite/operations.js +15 -4
  117. package/dist-cjs/sqlite/store.d.ts +7 -0
  118. package/dist-cjs/sqlite/store.js +28 -0
  119. package/dist-cjs/store/memory.d.ts +8 -0
  120. package/dist-cjs/store/memory.js +12 -1
  121. package/dist-cjs/store/redis.d.ts +13 -0
  122. package/dist-cjs/store/redis.js +38 -0
  123. package/dist-cjs/tracing/instrument.js +15 -3
  124. package/dist-cjs/tracing/tracer.d.ts +4 -0
  125. package/dist-cjs/tracing/tracer.js +6 -0
  126. package/dist-cjs/types/graph.d.ts +7 -0
  127. package/dist-cjs/types/messages.d.ts +12 -0
  128. package/dist-cjs/types/operations.d.ts +7 -0
  129. package/dist-cjs/types/server.d.ts +61 -2
  130. package/dist-cjs/types/store.d.ts +9 -0
  131. package/dist-cjs/version.d.ts +1 -1
  132. package/dist-cjs/version.js +1 -1
  133. package/llms.txt +1 -1
  134. package/package.json +43 -2
package/API_STABILITY.md CHANGED
@@ -41,6 +41,127 @@ 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.6 stage (2.6.0)
45
+
46
+ Everything 2.6 adds is stable from the start and additive.
47
+
48
+ New and stable:
49
+ - `ToolDefinition.stream`, `ToolContext.progress`, and `ToolBody`; `tool()` with `stream`. The
50
+ AG-UI event name `tool_progress`, and the `progress` and `callId` fields of a tool run's metadata;
51
+ - `McpClient.callTool()`'s `progress` option, and `notifications/progress` from `McpServer`;
52
+ - `AgUiOptions.stateDeltas`, `messagesSnapshot`, and `frontendTools`, and `AgUiPatchOperation`;
53
+ - `CreateAgentOptions.toolSource` and `ToolSourceContext`; `ClientToolDefinition`, the `clientTools`
54
+ channel, and the `AgentStopReason` member `client_tools`;
55
+ - the `step` member of `GraphEvent`;
56
+ - on `nexus-ai-pro/protocols/a2a`: `A2aTaskStore`, `A2aTaskRecord`, `A2aTaskQuery`,
57
+ `MemoryA2aTaskStore`, `storeA2aTasks()`; `A2aServerOptions.store`, `pushNotifications`,
58
+ `extendedCard`, `pollMs`, and `onError`; `A2aPushOptions` and `A2aPushNotificationConfig`; the
59
+ methods `ListTasks`, `SubscribeToTask`, the four push notification config methods, and
60
+ `GetExtendedAgentCard`; the client's new methods, `A2aListTasksOptions`, and `A2aTaskPage`; and
61
+ the error codes `pushNotificationNotSupported` and `extendedCardNotConfigured`.
62
+
63
+ - on `nexus-ai-pro/protocols/acp`: `AcpSessionStore`, `AcpSessionRecord`, `MemoryAcpSessionStore`,
64
+ `storeAcpSessions()`, `AcpOptions.sessions` and `editor`, `AcpEditorBridge`, `AcpConnection`,
65
+ `AcpClientCapabilities`, `session/load`, image and audio blocks, and the fourth argument of
66
+ `AcpOptions.input`;
67
+ - `nexus-ai-pro/protocols/acp/editor`: `acpEditor()`, `AcpEditor`, `AcpEditorOptions`, and the tool
68
+ names `editor_read_file`, `editor_write_file`, and `editor_run_command`.
69
+
70
+ An AG-UI run reports a node's tasks running side by side as one step, and finishes every step it
71
+ started before `RUN_FINISHED` or `RUN_ERROR`. A2A `ListTasks` refuses a status that is not a task
72
+ state, and reads `TASK_STATE_UNSPECIFIED` and `UNRECOGNIZED` as no filter.
73
+
74
+ A streamed A2A task now reports its move to `TASK_STATE_WORKING` as a status update, before its
75
+ other events.
76
+
77
+ `GraphEvent` and `AgentStopReason` gain members, as the compatibility rules allow in a minor
78
+ release. Code that switches over either exhaustively should handle the new ones.
79
+
80
+ ## 2.5 stage (2.5.0)
81
+
82
+ Everything 2.5 adds is stable from the start and additive.
83
+
84
+ New and stable:
85
+ - `putIfVersion()`, optional on `Store` and `ServerStateStore`, and its implementations;
86
+ `Deployments.atomicChanges`; `ScalingSnapshot.atomicChanges`;
87
+ - `nexus-ai-pro/postgres/tenancy`: `PostgresTenantUsage`, `PostgresTenantUsageOptions`,
88
+ `tenantUsageMigration()`, and `tenantUsageMigrations()`; the `tenancy` member of
89
+ `PostgresAdapter`;
90
+ - `OperationStoreFilter.kinds`, `OperationStoreStats.queuedByKind`, `RevisionRequest.queued`,
91
+ `RevisionChoice.local`, `ScalingSnapshot.queuedByRevision`, and the
92
+ `nexus_server_runs_queued_by_revision` metric.
93
+
94
+ - signing on `nexus-ai-pro/context-hub`: `ContextHubOptions.signer`, `keyring`, and `requireSignature`;
95
+ `ContextHub.verify()` and `ContextVerification`; `ContextBundle.signatures`; `ContextSignatureError`;
96
+ `generateContextKey()`, `ed25519Signer()`, `ed25519Keyring()`, `contextDigest()`, and the signing
97
+ types. The signed digest's format is `nexus-context-signature`, format version 1, and a later release
98
+ verifies it;
99
+ - `metadata.context` on a traced model call;
100
+ - on `nexus-ai-pro/server/deployments`, experimental with the rest of it: `BucketStrategy`,
101
+ `DeploymentRecord.bucketStrategy`, `RevisionedAssistantOptions.bucketStrategy`, the `bucketing`
102
+ change and `Deployments.bucketing()`, the strategy argument of `bucket()`; `recordRun()`,
103
+ `rollups()`, `flushRollups()`, `stats({ deployment })`, `DeploymentRollup`, `RevisionRollup`, and
104
+ `DeploymentsOptions.rollupMs`; `CanaryGuardOptions.from`. `ServerDeployments` gains an optional
105
+ `recordRun()`, and `RunManagerOptions.deployments`. The positions `fnv1a-v1` and `hash-v2` give a key
106
+ never change;
107
+ - the doctor's `settings` check; `RedisOperationStore.indexed`; `Tracer.store` and
108
+ `Tracer.incremental`;
109
+ - `nexus-ai-pro/evaluate/threads`, experimental: `simulatedUser()`, `scriptedUser()`,
110
+ `conversationTarget()`, `graphThreadAgent()`, `formatThread()`, `goalReached()`,
111
+ `goalCompletion()`, `turnCount()`, `turnScores()`, and their types. The score keys `goal_reached`,
112
+ `goal_completion`, `turns`, and `turn_score` are stable once the entry point graduates.
113
+
114
+ The studio now refuses, as the compatibility rules allow for security: a change whose body is not
115
+ `application/json`, with `415`; a body that is not a JSON object, with `400`; a body past
116
+ `maxBodyBytes`, with `413`; and a list `limit`, `hours`, or `days` past its ceiling, with `400`. With
117
+ accounts, only a bearer `Authorization` header stands in for the page token. The agent server refuses
118
+ an assistant or revision named after a built-in property, such as `constructor`. Also stable from
119
+ 2.5: `StudioOptions.maxBodyBytes`, `Studio.routes` and `StudioRoute`, and
120
+ `MemoryStudioJournalOptions.maxComments`.
121
+
122
+ ### Graduation
123
+
124
+ Two more surfaces leave experimental status in 2.5, by the same checklist as in 2.4. Every test named
125
+ here runs in CI.
126
+
127
+ **Deployments** (`nexus-ai-pro/server/deployments`):
128
+
129
+ | Requirement | Evidence |
130
+ | --- | --- |
131
+ | Conformance | Atomic changes on the memory server store and the memory, Redis, Postgres, and SQLite stores: `tests/atomic-deployments.test.ts`; routing, canaries, and guards: `tests/deployments.test.ts`, `tests/fair-canaries.test.ts`; revision pools: `tests/revision-pools.test.ts` |
132
+ | Crash recovery | A change is one compare-and-set write, so a crash leaves the old record or the new one; a drained replica hands its runs to another: `tests/deployments.test.ts`. A replica that dies loses at most its last `rollupMs` of counts, which the guide states |
133
+ | Concurrency | Ten replicas making mixed changes at once land in one linear history on every store: `tests/atomic-deployments.test.ts` |
134
+ | Load | `deployments.route-canary` and `deployments.bucket` in `surface-budget.json` |
135
+ | Tenant isolation | n/a: a deployment is an assistant's configuration, not a tenant's data. The runs it routes are operations, which are isolated: `tests/tenant-isolation.test.ts` |
136
+ | Security | [Review](./SECURITY.md#deployments): four findings fixed, two documented limitations |
137
+ | Upgrade | A 2.3 replica's deployment routes the same here, and every thread keeps its bucket: `tests/upgrade-previous-minor.test.ts` |
138
+ | Soak | 50,000 route, admit, and release cycles, 500 replica lifecycles, and 20,000 runs counted and judged from rollups, with no timer left running |
139
+
140
+ **The studio and its accounts** (`nexus-ai-pro-studio`):
141
+
142
+ | Requirement | Evidence |
143
+ | --- | --- |
144
+ | Conformance | Every view over its sources, with and without accounts: `studio/test/studio.test.ts`, `studio/test/team.test.ts`, `studio/test/deployments.test.ts` |
145
+ | Crash recovery | n/a: the studio keeps no state but its journal, which appends one line per entry and skips a line a crash cut short: `studio/test/security.test.ts` |
146
+ | Concurrency | n/a: every change goes through its source's own API, where races are settled: prompt and bundle labels by compare-and-set, deployments by `putIfVersion()` |
147
+ | Load | 500 signed-in reads within a budget relative to the machine: `studio/test/security.test.ts` |
148
+ | Tenant isolation | A studio over `tenantScope()` views lists one tenant's runs, and another tenant's trace is not found: `studio/test/security.test.ts` |
149
+ | Security | [Review](./SECURITY.md#the-studio-and-its-accounts): ten findings, all fixed; every route fuzzed by every role and with hostile input: `studio/test/security.test.ts` |
150
+ | Upgrade | The journal reads what 2.4 wrote, and accounts and configuration load unchanged: `studio/test/security.test.ts`, `studio/test/team.test.ts` |
151
+ | Soak | 2,000 sign-in, read, and change cycles with flat memory, and 200 servers started and closed with no handle left open: `studio/test/security.test.ts` |
152
+
153
+ **Still experimental**, each with what remains:
154
+ - **Images.** Recorded live conformance must pass on all three backends. The scheduled live workflow
155
+ records it wherever a backend's credential is set, and none has run with all three yet.
156
+ - **The Qdrant, Redis, Pinecone, Weaviate, and Chroma vector stores.** The contract must pass against
157
+ the real servers. The scheduled live workflow runs it against each one, Pinecone when its secret is
158
+ set, and its evidence is published when it has passed against all five.
159
+
160
+ A queued run's kind now names its revision, as `assistant:<id>@<revision>`. Code that reads the
161
+ server's runs by the `assistant:` prefix, as the server itself does, is unaffected.
162
+
163
+ A state store without `putIfVersion()` keeps its earlier behavior.
164
+
44
165
  ## 2.4 stage (2.4.0)
45
166
 
46
167
  Everything 2.4 adds is stable from the start and additive. These security fixes tighten behavior,
package/CHANGELOG.md CHANGED
@@ -4,6 +4,251 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [2.6.0] - 2026-10-11
8
+
9
+ Protocols complete, and progress on every stream.
10
+
11
+ ### Added
12
+
13
+ - **Tool progress, end to end.**
14
+ - A `ToolDefinition` can have `stream`: an async generator that yields progress and returns the
15
+ result. `tool()` takes one instead of `execute`, and gives it an `execute` that runs the stream,
16
+ so it works wherever a tool is executed. `ToolExecutor` runs a stream itself.
17
+ - `ToolContext.progress` reports progress from any tool. An agent sets it, and turns each report
18
+ into a `progress` tool event while the tool runs.
19
+ - Progress reaches the event stream and the server's run events; AG-UI, as a `CUSTOM`
20
+ `tool_progress` event; A2A, as a working status update with the progress as data; ACP, as an
21
+ update of the tool call in progress; traces, in the tool run's `metadata.progress`; and the
22
+ studio's run view.
23
+ - MCP: `McpServer` runs `stream` tools and sends `notifications/progress` for a call that names a
24
+ `progressToken`. `McpClient.callTool()` takes a `progress` listener, and the tools from
25
+ `toNexusTools()` pass a remote tool's progress to the agent.
26
+
27
+ - **AG-UI, complete**, each part opt-in on `AgUiOptions`:
28
+ - `stateDeltas` reports state as it changes: a `STATE_SNAPSHOT` after the first step, then a
29
+ `STATE_DELTA` with an RFC 6902 JSON Patch after every step that changed it, appending to arrays
30
+ item by item. `AgUiPatchOperation` is its type.
31
+ - `messagesSnapshot` sends the whole transcript as `MESSAGES_SNAPSHOT` before the run finishes.
32
+ - `frontendTools` offers the tools the frontend sends to the model. The frontend runs a call to one,
33
+ and the next run's messages carry its result.
34
+ - **Tools the client runs.** An agent's input may carry `clientTools`, each a `ClientToolDefinition`.
35
+ The model is offered them; a call to one ends the turn with the new `stopReason: 'client_tools'`
36
+ once the agent's own calls of that step have run, and the client's result continues it. A client
37
+ tool never replaces a tool of the agent's own.
38
+ - **Tools that depend on the run.** `CreateAgentOptions.toolSource` supplies tools per run, from a
39
+ `ToolSourceContext`, offered and run like `tools`, under the same permission policy.
40
+ - **A2A, durable and complete.**
41
+ - Tasks and conversations live in an `A2aTaskStore`: `MemoryA2aTaskStore` by default, or
42
+ `storeA2aTasks()` over any long-term `Store`, so a task paused for an answer survives a restart
43
+ and another replica finishes it.
44
+ - `ListTasks` by context and state, a page at a time; `SubscribeToTask` on any replica; and a
45
+ cancel that stands against the replica running the task.
46
+ - Push notifications, with `pushNotifications`: each state change POSTed to a task's webhook,
47
+ with the caller's token and signed as the server's operation webhooks are. A webhook must be
48
+ `https` and not a private address, unless `allowPrivateNetworks` is set.
49
+ - `GetExtendedAgentCard`, with `extendedCard`, for authenticated callers.
50
+ - The client gains `extendedCard()`, `listTasks()`, `subscribe()`, `setPushNotification()`,
51
+ `listPushNotifications()`, `deletePushNotification()`, and `push` and `returnImmediately` when
52
+ sending.
53
+ - **ACP, durable and complete.**
54
+ - Sessions live in an `AcpSessionStore`: `MemoryAcpSessionStore` by default, or
55
+ `storeAcpSessions()` over any long-term `Store`. The agent offers `session/load`, replays the
56
+ conversation, and asks again a question it was waiting on.
57
+ - A prompt can carry images and audio, which reach the model as content parts.
58
+ - `acpEditor()`, on the new `nexus-ai-pro/protocols/acp/editor`, gives an agent the editor's files,
59
+ terminal, and MCP servers, as tools from its `toolSource`, each declaring what it touches.
60
+ `serveAcp()` takes it as `editor`, and detaches it when the editor closes its end.
61
+ - **Interoperability, proven.**
62
+ - A browser in CI: `npm run test:browser` serves the build to headless Chromium, which imports
63
+ every kernel entry point and runs a graph, an agent, and AG-UI. It runs in `npm test`.
64
+ - The previous minor on the other side of the wire: a 2.5 client against a 2.6 server, and the
65
+ other way round, on the server's HTTP API and A2A, and one harness across both releases for
66
+ AG-UI and ACP.
67
+ - A nightly `Interoperability` workflow drives each protocol with its reference client:
68
+ `@ag-ui/client`, `@a2a-js/sdk`, and `@agentclientprotocol/sdk`.
69
+ - **A `step` graph event.** `GraphRunOptions.onEvent` receives `step` after each superstep, with the
70
+ state after it.
71
+
72
+ ### Fixed
73
+
74
+ - AG-UI: a run that paused inside a node left that node's step open, so a strict client refused its
75
+ `RUN_FINISHED`; and a node's tasks running side by side each started the same step. A node's tasks
76
+ are now one step, and every step still open is finished before the run finishes or fails. Found by
77
+ the reference client.
78
+ - A2A: `ListTasks` without a status filter, as the reference SDK sends it (`UNRECOGNIZED`), matched
79
+ no task. An unset filter now filters nothing, and an unknown state is refused as invalid.
80
+ - MCP: the stdio transport's `close()` killed the server without waiting for it, so what the process
81
+ held, such as its working directory, could outlive the close. It now waits up to two seconds for
82
+ the process to exit.
83
+
84
+ ### Proof
85
+
86
+ - Headless Chromium imports every kernel entry point and runs a graph, an agent with a streaming
87
+ tool, and AG-UI. A 2.5 client and a 2.6 server pass the protocol suites in both directions, and the
88
+ reference clients of AG-UI, A2A, and ACP each drive this release's servers through a whole run.
89
+ - An ACP session paused on a question survives the agent's restart, on memory and SQLite; the
90
+ editor reopens it, sees the conversation replayed, and answers. An image and audio reach the model.
91
+ Through the bridge, an agent reads, writes (after the person allows it), and runs the tests in the
92
+ editor, and searches docs through an MCP server the editor offered, started as a real process.
93
+ - An A2A task paused for an approval survives a restart and is finished by another replica, sending
94
+ its email once, on the memory, SQLite, Postgres, and Redis stores. A running task is followed where
95
+ it runs and from another replica; a cancel on one replica stands against the other. Every state
96
+ change reaches the webhook, signed and with the caller's token, and nine webhook URLs that point
97
+ into the agent's own network or are not https are refused.
98
+ - An AG-UI client applying a run's snapshot and its deltas rebuilds the final state exactly, with
99
+ each new message one `add`. A frontend tool is offered to the model beside the agent's own; the
100
+ agent runs its own call, waits without calling the model again, and continues from the page's
101
+ result in the next run. A page cannot shadow the agent's tool, and without the options the events
102
+ are what they were.
103
+ - A tool that will not go on until a reader has seen its first report finishes on every stream: the
104
+ event stream, the server's events, AG-UI, A2A, ACP, and MCP. Its reports arrive in order, after the
105
+ call and before the result, and its traced run keeps them.
106
+
107
+ ## [2.5.0] - 2026-10-09
108
+
109
+ Graduation finished, and conversations evaluated.
110
+
111
+ ### Added
112
+
113
+ - **Deployment changes that are atomic across replicas.**
114
+ - The long-term `Store` contract and the server's `ServerStateStore` gain an optional
115
+ `putIfVersion()`. It writes a record only when the stored one's `version` is still the version
116
+ the change was decided on, in one atomic step. The memory, Postgres, and SQLite stores, the server's
117
+ memory store, and the Redis store (when its client has `eval`) have it, and `fromStore()` passes
118
+ it on.
119
+ - A deployment change uses it. A replica that loses the race decides again on the winner's record,
120
+ so two changes made at once both land, in order. A change made on an `expectedVersion` is
121
+ refused with `DEPLOYMENT_CONFLICT` instead. Two replicas starting at once seed one record.
122
+ - `Deployments.atomicChanges` and `/scaling`'s `atomicChanges` say whether the state store
123
+ offers this.
124
+ - **Tenant usage in Postgres**, on the new `nexus-ai-pro/postgres/tenancy`: `PostgresTenantUsage`, so
125
+ every replica enforces one tenant limit without Redis. The `tenancy` adapter joins
126
+ `postgresMigrations()` and `nexus db migrate`.
127
+ - **Revision worker pools.** A worker image carries some revisions of an assistant, and claims only
128
+ the runs routed to them, so a new revision ships in a new image beside the old one.
129
+ - A queued run's kind names its revision, as `assistant:<id>@<revision>`, and a worker claims with
130
+ the kinds of the revisions it registered.
131
+ - A run that will wait in the queue is routed over the revisions any fresh worker reports
132
+ (`RevisionRequest.queued`), so the API replicas send traffic to a revision only a new image
133
+ carries. `RevisionChoice.local` says whether the answering replica has it.
134
+ - `/scaling` reports `queuedByRevision`, and `/metrics` `nexus_server_runs_queued_by_revision`, so an
135
+ autoscaler starts the pool a waiting revision needs.
136
+ - `OperationStoreFilter.kinds` filters by exact kinds on every operation store, and
137
+ `OperationStoreStats.queuedByKind` counts queued records per kind. `claimQueued()` checks each
138
+ candidate against the filter itself, for a store that ignores one.
139
+ - **Signed context bundles.**
140
+ - A hub with a `signer` signs every new version with Ed25519, through Web Crypto, so on any
141
+ runtime. The signature covers the bundle's content and its pinned prompts' content, under the
142
+ full SHA-256 that `contextDigest()` computes, not the 12-digit version.
143
+ - A hub with a `keyring` checks signatures with `verify()`. With `requireSignature`, it refuses a
144
+ bundle without a trusted signature, with a `ContextSignatureError`, on `import()` and whenever
145
+ `resolve()` or `renderPrompt()` serves one.
146
+ - An import keeps the signatures that claim its content, and adds the importing hub's. That is how
147
+ keys rotate.
148
+ - `generateContextKey()`, `ed25519Signer()`, and `ed25519Keyring()` make and load keys, as JSON Web
149
+ Keys or Web Crypto keys.
150
+ - **Canaries that stay fair.**
151
+ - A deployment records a `bucketStrategy`. `hash-v2` mixes the thread id's hash with
152
+ MurmurHash3's finalizer, so sequential ids such as `user-1` to `user-500` split as evenly as random
153
+ ones. `fnv1a-v1` stays the default, and every deployment recorded before keeps it, so no thread
154
+ moves through an upgrade. `Deployments.assistant()` takes `bucketStrategy`; `bucketing()` and a
155
+ `bucketing` change switch a recorded deployment. `bucket()` takes the strategy.
156
+ - Every replica counts the runs it finishes, per revision, in a rollup under the deployment version
157
+ they were routed with: counts, latency in buckets 5% apart, and cost. `stats()` with `deployment`
158
+ and `rollups()` sum them in one read, and `GET /deployments/:assistant` reports them.
159
+ - `watchCanaries({ from: 'rollups' })` judges a canary on every run since the split last changed,
160
+ instead of the latest 2,000 run records.
161
+ - **A doctor that knows the opt-in settings.** `nexus doctor` and `diagnose()` warn, in a new
162
+ `settings` check, when a `RedisOperationStore` runs without `index: true`, and when a `Tracer`
163
+ writes to a persistent store without `incremental`. Each warning names the setting that fixes it.
164
+ Both stay opt-in through 2.x. `RedisOperationStore.indexed`, `Tracer.store`, and
165
+ `Tracer.incremental` report the settings.
166
+ - **Graduation.** Deployments and the studio leave experimental status, each with its checklist and
167
+ evidence in `API_STABILITY.md` and a threat review in `SECURITY.md`.
168
+ - **The studio's routes, listed.** `Studio.routes` gives every route's method, path, role, and audit
169
+ action. `StudioOptions.maxBodyBytes` bounds a request body, and
170
+ `MemoryStudioJournalOptions.maxComments` bounds the comments kept in memory.
171
+ - **Whole conversations, evaluated** (`nexus-ai-pro/evaluate/threads`).
172
+ - `simulatedUser()` plays a user with a goal and a persona, driven by a model, and says when the goal
173
+ was met or when it gives up. `scriptedUser()` follows a script, for deterministic tests.
174
+ - `conversationTarget()` is an `evaluate()` target that holds a conversation per example and returns
175
+ the `Thread`: every turn, how long each answer took, the messages the agent added, and how it
176
+ ended. `graphThreadAgent()` drives an agent from `createAgent()`, which keeps its own tool calls
177
+ from turn to turn.
178
+ - Thread evaluators: `goalReached()`, `goalCompletion()` with a judge, `turnCount()`, and
179
+ `turnScores()`, which scores every turn with an evaluator of single answers and names the turn
180
+ where the conversation went wrong.
181
+ - **Context on every trace.** A traced model call records `metadata.context` beside
182
+ `metadata.prompt`, so a trace query or an insight narrows by bundle version.
183
+
184
+ ### Security
185
+
186
+ - **The studio**, from its threat review:
187
+ - A request body is refused with `413` past `maxBodyBytes`, 1 MiB by default, as it arrives and
188
+ before anyone is signed in. Before, a body was read whole into memory first.
189
+ - With accounts, only a bearer `Authorization` header stands in for the page token. Before, any
190
+ `Authorization` header did, and a browser adds Basic credentials to another site's request by
191
+ itself, so a sign-in proxy that uses Basic authentication let another site make changes.
192
+ - A change's body must be `application/json` (`415` otherwise) and a JSON object (`400`
193
+ otherwise).
194
+ - A change refused for want of the page token is recorded in the audit log.
195
+ - Graphs and review queues are looked up among the application's own entries, so a name such as
196
+ `constructor` no longer reaches the prototype.
197
+ - Malformed percent-encoding in a path is a `400`, not a server error.
198
+ - `limit`, `hours`, and `days` have ceilings: 1,000 items, 10,000 audit entries, 90 days of
199
+ issues, and 366 days of costs.
200
+ - The memory journal keeps at most 10,000 comments.
201
+ - The server no longer sends an internal error's message, and logs it instead.
202
+ - Only `http` and `https` pull-request links are rendered.
203
+ - **The agent server** refuses an assistant or revision named after a built-in property, such as
204
+ `constructor`, with `400`. Before, such a run was accepted and failed when it ran.
205
+
206
+ ### Fixed
207
+
208
+ - A worker that lacked the revision a run was routed to ran the assistant's live revision instead,
209
+ and the run still recorded the revision it was routed to. A worker now claims only runs of revisions
210
+ it carries.
211
+
212
+ ### Proof
213
+
214
+ - The studio's threat review is a test: every route is called by every role and refuses the roles
215
+ below its own, recording each refusal; over 2,000 requests with hostile paths, queries, and bodies
216
+ all answer below `500`, and no prototype is polluted. 2,000 sign-in, read, and change cycles leave
217
+ the heap flat, and 200 servers start and close with no handle left open.
218
+ - The doctor's seeded deployment warns about a Redis operation store without its index and a tracer
219
+ writing to a persistent store at the end of each trace, and the same deployment with both settings
220
+ turned on passes.
221
+ - Ten replicas make mixed changes to one deployment at once (canaries, splits, promotions,
222
+ rollbacks, and a bucketing change), held until all ten arrive, on the memory server store and the
223
+ memory, Redis, Postgres, and SQLite stores. Every change lands, each with a version of its own, and
224
+ the history is one linear sequence.
225
+ - Over 48 sets of 500 sequential thread ids, `hash-v2` places a canary's share within 2.3 standard
226
+ deviations of its weight, as random placement does; `fnv1a-v1` strays by up to 9.9. Over 10,000
227
+ sequential ids, `hash-v2` is within half a point at every weight.
228
+ - A canary that fails 0.8% of the time against 0.5% is held by a guard over the latest 2,000 run
229
+ records, and rolled back by a guard over rollups, which counts all 100,000 runs from three replicas
230
+ in one read.
231
+ - A simulated user holds 50 conversations with each of two versions of a support agent built with
232
+ `createAgent()`. The thread evaluators score every conversation, and `compareExperiments()` finds
233
+ the second version better on goals met, the judge's verdict, turns taken, and per-turn scores, with
234
+ no regression; the reverse comparison regresses. A conversation that failed names the turn where it
235
+ went wrong.
236
+ - A signed bundle imports into a hub that requires signatures. It is refused there when it is unsigned,
237
+ or edited after export even with its version recomputed. One edited where it is stored is refused
238
+ when it is served. A re-import through a hub with a new key keeps both signatures, so the bundle
239
+ stays trusted once the old key is retired. Every traced model call rendered from it names its
240
+ context and prompt versions.
241
+ - Two worker images drain one queue, one carrying v1 and the other v1 and v2, with an API replica
242
+ that carries only v1. Under a 50% canary, every run executes on the revision it was routed to, and
243
+ only the image with v2 runs v2. A run for a pool scaled to zero waits, is counted per revision on
244
+ `/scaling` and `/metrics`, and runs when that pool returns.
245
+ - Two replicas changing one deployment in the same instant both land, on the memory, Redis,
246
+ Postgres, and SQLite stores. The race test holds both writers until both arrive, and the same race
247
+ on a store without `putIfVersion()` loses a change. Of two changes made on one expected version,
248
+ exactly one is refused.
249
+ - Ten workers spending one tenant budget lose nothing, and never over-grant a slot, on Postgres as
250
+ on memory and Redis.
251
+
7
252
  ## [2.4.0] - 2026-10-08
8
253
 
9
254
  Graduation and interoperability.
@@ -1959,7 +2204,9 @@ shared policy vocabulary is in place for a future release that revisits this.
1959
2204
  - URL fetching rejects unsafe private-network targets and limits response reads.
1960
2205
  - CLI and audit findings no longer disclose detected secret values.
1961
2206
 
1962
- [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.4.0...HEAD
2207
+ [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.6.0...HEAD
2208
+ [2.6.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.5.0...v2.6.0
2209
+ [2.5.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.4.0...v2.5.0
1963
2210
  [2.4.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.3.0...v2.4.0
1964
2211
  [2.3.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.2.0...v2.3.0
1965
2212
  [2.2.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.1.0...v2.2.0
package/README.md CHANGED
@@ -266,12 +266,23 @@ Shipped so far:
266
266
  - 2.4 graduation and interoperability: SQLite, loaders, retrievers, the MCP registry, the context
267
267
  hub, insights, and tenant limits out of experimental with published evidence; canaries judged with
268
268
  confidence and on quality; an adapter kit; AG-UI, A2A, and ACP; and a portable kernel, with the
269
- client, providers, graphs, and agents running on Deno, Bun, edge runtimes, and browsers.
269
+ client, providers, graphs, and agents running on Deno, Bun, edge runtimes, and browsers;
270
+ - 2.5 graduation finished, and conversations evaluated: deployments that change atomically across
271
+ replicas, revision worker pools, fair canaries judged on every run, and the studio out of
272
+ experimental after a threat review; Postgres tenant usage; signed context named on every trace;
273
+ simulated users and thread evaluators; and a doctor that knows the opt-in settings;
274
+ - 2.6 protocols complete, and progress on every stream: a tool's progress reaching the event stream,
275
+ the protocols, traces, and the studio; AG-UI state deltas and frontend tools; A2A tasks and ACP
276
+ sessions that survive a restart, with push notifications, `session/load`, and the editor's files,
277
+ terminal, and MCP servers; and every protocol proven in a browser, against the previous release,
278
+ and against its reference client.
270
279
 
271
280
  Upgrading from 1.x: `npx nexus migrate src --write` moves your imports, and
272
281
  [MIGRATING.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/MIGRATING.md) covers the rest.
273
- What comes next is on the [roadmap](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/ROADMAP.md):
274
- the gaps 2.4 left open, and the work applications ask for after them.
282
+ Next, 2.7 lets agents do more: provider tool search, an interpreter contract, workspace search,
283
+ helpers that run beside the agent, and a container sandbox. Evaluation grows its own datasets and
284
+ runs on workers, within a budget. The [roadmap](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/ROADMAP.md) lists the
285
+ releases after it.
275
286
 
276
287
  ## Before Production
277
288
 
package/SECURITY.md CHANGED
@@ -52,7 +52,7 @@ what it could do with that input. Each review is published here with its finding
52
52
  before the surface graduates. A finding is closed in one of two ways: by a fix with a test that
53
53
  names the attack, or by a documented limitation that the surface's guide states.
54
54
 
55
- These reviews were done for 2.4.
55
+ These reviews were done for 2.4, and for 2.5 where a review says so.
56
56
 
57
57
  ### SQLite adapters
58
58
 
@@ -168,7 +168,18 @@ server trusts by itself.
168
168
 
169
169
  Findings: none.
170
170
 
171
- ### Deployments (reviewed; still experimental)
171
+ ### Deployments
172
+
173
+ Reviewed for 2.4, and again for 2.5, when it graduated.
174
+
175
+ Trust boundary: a deployment is changed by an admin, a canary guard, or the studio's admins; a run
176
+ names its assistant, and optionally a revision, in a request from any caller with the write scope.
177
+ Replicas, guards, and the studio share the server's state store, which they trust.
178
+ - Every change is decided on the version it read and written by compare-and-set, on every included
179
+ state store, so a stale change never overwrites a newer one.
180
+ - A guard acts on the version it judged, so guards on every replica never undo each other.
181
+ - A replica writes rollups only to its own record. Counts are as trustworthy as the state store, as
182
+ run records already are.
172
183
 
173
184
  Findings:
174
185
  - **Fixed in 2.4: one caller's ratings could decide a canary.** A canary was judged on feedback
@@ -177,10 +188,68 @@ Findings:
177
188
  key, as the mean of its ratings.
178
189
  - **Fixed in 2.4: two changes at once on one replica could lose one**, such as a guard's rollback
179
190
  and an operator's promotion. Changes to one deployment now apply in order.
180
- - **Open: two replicas changing one deployment in the same instant can lose one change.** The
181
- server's state store has no atomic compare-and-set. Until it does, deployments stay experimental.
182
- The guide says to make changes from one place, or to pass `expectedVersion` and retry a conflict.
183
- - **Documented limitation: `bucket()` spreads sequential ids unevenly.** Ids that differ only in their
184
- last characters spread unevenly over a few hundred threads. Random ids, which the server gives by
185
- default, spread evenly. The function's output is part of the stability promise, so it is not
186
- changed in 2.x.
191
+ - **Fixed in 2.5: two replicas changing one deployment in the same instant could lose one change.**
192
+ The state store gained `putIfVersion()`, and a replica that loses the race decides again on the
193
+ winner's record. Proof: ten replicas held until all arrive make mixed changes on every store, and
194
+ every change lands in one linear history.
195
+ - **Fixed in 2.5: a name from a request could reach an object's prototype.** A run for the assistant
196
+ `constructor`, or naming the revision `constructor`, was accepted and failed when it ran. Only an
197
+ application's own assistants and revisions are looked up now, and both are refused with `400`.
198
+ - **Documented limitation: `fnv1a-v1` spreads sequential ids unevenly.** It stays the default, since
199
+ its positions are part of the stability promise. `hash-v2` spreads them evenly, opt-in per
200
+ deployment.
201
+ - **Documented limitation: a replica that dies loses the runs it counted in its last `rollupMs`.**
202
+ Rollups are statistics; the run records stay whole.
203
+
204
+ ### The studio and its accounts
205
+
206
+ Reviewed for 2.5, when it graduated.
207
+
208
+ Trust boundary: anyone who can reach the port, before signing in; a signed-in person, limited by
209
+ their role; a page on another site acting through a signed-in person's browser; and the data the
210
+ studio shows, which traces, models, and people wrote.
211
+ - The `Host` header must be a loopback name or one in `allowedHosts`, which closes DNS rebinding.
212
+ - Tokens are compared in constant time. A token in the URL is moved into an HTTP-only, `SameSite=Strict`
213
+ cookie, `Secure` over HTTPS, and taken out of the address bar. `headerAuth()` trusts identity
214
+ headers only with the proxy's secret or from its addresses.
215
+ - Every change needs a page token in a header, an HMAC of the user and their role under the server's
216
+ secret, which another site can neither read nor send. Every refusal is audited.
217
+ - Every response carries a content security policy without inline script, `nosniff`, `no-store`,
218
+ and no referrer. The page inserts every value as text.
219
+
220
+ Findings, each fixed in 2.5 with a test in `studio/test/security.test.ts`:
221
+ - **A request body was read whole before anyone signed in.** Anyone who could reach the port could
222
+ fill the server's memory. A body past `maxBodyBytes`, 1 MiB by default, is now refused with `413`
223
+ as it arrives.
224
+ - **Behind a sign-in proxy that uses Basic authentication, another site could make a change.** Any
225
+ `Authorization` header counted as proof that a script, not a browser, sent the change. But a
226
+ browser attaches Basic credentials to another site's request by itself. Only a bearer token counts
227
+ now, and a change's body must be `application/json`, which a form on another site cannot send.
228
+ - **A change refused for want of the page token was not audited.** It is now, under the person whose
229
+ browser sent it, so a forged change leaves a trace.
230
+ - **A name from the URL could reach an object's prototype.** A graph or review queue named
231
+ `constructor` failed with a server error. Only an application's own entries are looked up.
232
+ - **A body that was not a JSON object reached handlers that read its fields.** `null`, an array, or a
233
+ number failed with a server error. Such a body is now refused with `400`.
234
+ - **A path with malformed percent-encoding failed with a server error.** It is now a `400`.
235
+ - **A viewer could ask a store for everything it holds.** `limit`, `hours`, and `days` had no
236
+ ceiling. A list now returns at most 1,000 items, the audit log 10,000 entries, the issues view 90
237
+ days, and the costs view 366 days; past that, the request is refused with `400`.
238
+ - **The memory journal kept every comment.** It now keeps the latest 10,000, as it keeps audit entries.
239
+ - **The server answered an internal error with its message.** That reply could reach anyone who can
240
+ reach the port. It now says only that the request failed, and logs the error on the server.
241
+ - **A proposal's pull-request link was rendered as stored.** The content security policy already
242
+ blocked a `javascript:` address. Only `http` and `https` links are rendered now.
243
+
244
+ Documented limitations, which the studio guide states:
245
+ - The studio has no tenants of its own. Every account sees every source the studio is given, so a
246
+ tenant's people get a studio over that tenant's `tenantScope()` views.
247
+ - A page token stays valid while the secret, the user, and their role do. Rotate `secret` to revoke
248
+ every page at once.
249
+ - Someone without a session who opens another person's personal link is signed in as that person.
250
+ The page shows who is signed in.
251
+ - `FileStudioJournal` reads its whole file for each listing, so rotate a long-lived one.
252
+ - The playground spends model budget, which is why it needs the editor role.
253
+
254
+ The fuzzing behind these: every route called by every role, and over 2,000 requests with hostile
255
+ paths, queries, and bodies. Each must answer below `500`, and no prototype may be polluted.
@@ -163,12 +163,44 @@ export interface CreateAgentOptions {
163
163
  * event once the model returns. Middleware sees the assembled response either way.
164
164
  */
165
165
  streamTokens?: boolean;
166
+ /**
167
+ * Tools that depend on the run, added to `tools` on every model call and run like them: an editor's
168
+ * file system for one session, or the MCP servers one user connected. A tool in `tools` wins over a
169
+ * source's tool of the same name.
170
+ */
171
+ toolSource?: (context: ToolSourceContext) => ToolDefinition[] | Promise<ToolDefinition[]>;
172
+ }
173
+ /** What a tool source is told about the run it supplies tools for. */
174
+ export interface ToolSourceContext {
175
+ /** The run's thread. */
176
+ threadId: string;
177
+ /** The tenant the run is for. */
178
+ tenantId?: string;
179
+ /** Who the run is for. */
180
+ principal?: Readonly<Principal>;
181
+ /** Aborted when the run is cancelled. */
182
+ signal: AbortSignal;
183
+ }
184
+ /**
185
+ * A tool the client runs, such as one an AG-UI frontend offers: a name, a description, and a schema,
186
+ * with no implementation on the agent's side. Given in the input as `clientTools`, it is offered to
187
+ * the model like any tool. When the model calls one, the agent ends its turn with `stopReason:
188
+ * 'client_tools'`, and the client sends the result back in the next run's messages.
189
+ */
190
+ export interface ClientToolDefinition {
191
+ /** The name the model calls it by. */
192
+ name: string;
193
+ /** What it does, written for the model. */
194
+ description: string;
195
+ /** JSON Schema for the arguments. */
196
+ parameters: Record<string, unknown>;
166
197
  }
167
198
  declare const agentChannels: () => {
168
199
  messages: import("../types/graph.js").Channel<Message[]>;
169
200
  iterations: import("../types/graph.js").Channel<number>;
170
201
  answer: import("../types/graph.js").Channel<string>;
171
202
  stopReason: import("../types/graph.js").Channel<AgentStopReason | undefined>;
203
+ clientTools: import("../types/graph.js").Channel<ClientToolDefinition[] | undefined>;
172
204
  };
173
205
  /** The agent's state channels, as a graph schema. */
174
206
  export type AgentChannels = ReturnType<typeof agentChannels>;
@@ -182,12 +214,15 @@ export type AgentState = {
182
214
  answer: string;
183
215
  /** Why the agent stopped. */
184
216
  stopReason?: AgentStopReason;
217
+ /** Tools the client runs, given in the input; see `ClientToolDefinition`. */
218
+ clientTools?: ClientToolDefinition[];
185
219
  };
186
220
  /**
187
221
  * `completed` when the model answered, `max_iterations` when it ran out of model calls, `stopped`
188
- * when a middleware ended the run through `context.stop()`.
222
+ * when a middleware ended the run through `context.stop()`, and `client_tools` when the model called
223
+ * a tool the client runs, which waits for the client's result.
189
224
  */
190
- export type AgentStopReason = 'completed' | 'max_iterations' | 'stopped';
225
+ export type AgentStopReason = 'completed' | 'max_iterations' | 'stopped' | 'client_tools';
191
226
  /** An agent: a compiled graph over the agent's channels. */
192
227
  export type AgentGraph = CompiledGraph<AgentChannels>;
193
228
  /** Wraps a question into the state an agent starts from. */