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.
- package/API_STABILITY.md +121 -0
- package/CHANGELOG.md +248 -1
- package/README.md +14 -3
- package/SECURITY.md +78 -9
- package/dist/agent/create-agent.d.ts +37 -2
- package/dist/agent/create-agent.js +62 -4
- package/dist/agent/index.d.ts +2 -1
- package/dist/agent/tool.d.ts +19 -4
- package/dist/agent/tool.js +27 -4
- package/dist/context-hub/hub.d.ts +53 -0
- package/dist/context-hub/hub.js +108 -3
- package/dist/context-hub/index.d.ts +2 -1
- package/dist/context-hub/index.js +2 -1
- package/dist/context-hub/signing.d.ts +95 -0
- package/dist/context-hub/signing.js +88 -0
- package/dist/doctor/index.d.ts +4 -2
- package/dist/doctor/index.js +29 -0
- package/dist/evaluate/threads.d.ts +187 -0
- package/dist/evaluate/threads.js +187 -0
- package/dist/graph/event-stream.js +2 -0
- package/dist/graph/graph.js +4 -1
- package/dist/mcp/client.d.ts +9 -1
- package/dist/mcp/client.js +38 -8
- package/dist/mcp/server.js +42 -2
- package/dist/operations/adapters.d.ts +2 -1
- package/dist/operations/runner.js +2 -0
- package/dist/operations/stats.js +11 -4
- package/dist/postgres/index.d.ts +2 -1
- package/dist/postgres/index.js +4 -0
- package/dist/postgres/operations.js +11 -4
- package/dist/postgres/store.d.ts +7 -0
- package/dist/postgres/store.js +21 -0
- package/dist/postgres/tenancy.d.ts +43 -0
- package/dist/postgres/tenancy.js +71 -0
- package/dist/protocols/a2a.d.ts +166 -1
- package/dist/protocols/a2a.js +499 -132
- package/dist/protocols/acp-editor.d.ts +43 -0
- package/dist/protocols/acp-editor.js +164 -0
- package/dist/protocols/acp.d.ts +76 -3
- package/dist/protocols/acp.js +180 -7
- package/dist/protocols/ag-ui.d.ts +23 -0
- package/dist/protocols/ag-ui.js +128 -8
- package/dist/server/deployments.d.ts +127 -12
- package/dist/server/deployments.js +402 -37
- package/dist/server/metrics.d.ts +2 -0
- package/dist/server/metrics.js +7 -0
- package/dist/server/runs.d.ts +9 -1
- package/dist/server/runs.js +46 -7
- package/dist/server/server.js +4 -1
- package/dist/server/state.d.ts +12 -2
- package/dist/server/state.js +15 -0
- package/dist/sqlite/operations.js +15 -4
- package/dist/sqlite/store.d.ts +7 -0
- package/dist/sqlite/store.js +28 -0
- package/dist/store/memory.d.ts +8 -0
- package/dist/store/memory.js +12 -1
- package/dist/store/redis.d.ts +13 -0
- package/dist/store/redis.js +38 -0
- package/dist/tracing/instrument.js +15 -3
- package/dist/tracing/tracer.d.ts +4 -0
- package/dist/tracing/tracer.js +6 -0
- package/dist/types/graph.d.ts +7 -0
- package/dist/types/messages.d.ts +12 -0
- package/dist/types/operations.d.ts +7 -0
- package/dist/types/server.d.ts +61 -2
- package/dist/types/store.d.ts +9 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist-cjs/agent/create-agent.d.ts +37 -2
- package/dist-cjs/agent/create-agent.js +62 -4
- package/dist-cjs/agent/index.d.ts +2 -1
- package/dist-cjs/agent/tool.d.ts +19 -4
- package/dist-cjs/agent/tool.js +27 -4
- package/dist-cjs/context-hub/hub.d.ts +53 -0
- package/dist-cjs/context-hub/hub.js +110 -4
- package/dist-cjs/context-hub/index.d.ts +2 -1
- package/dist-cjs/context-hub/index.js +7 -1
- package/dist-cjs/context-hub/signing.d.ts +95 -0
- package/dist-cjs/context-hub/signing.js +96 -0
- package/dist-cjs/doctor/index.d.ts +4 -2
- package/dist-cjs/doctor/index.js +29 -0
- package/dist-cjs/evaluate/threads.d.ts +187 -0
- package/dist-cjs/evaluate/threads.js +198 -0
- package/dist-cjs/graph/event-stream.js +2 -0
- package/dist-cjs/graph/graph.js +4 -1
- package/dist-cjs/mcp/client.d.ts +9 -1
- package/dist-cjs/mcp/client.js +37 -7
- package/dist-cjs/mcp/server.js +42 -2
- package/dist-cjs/operations/adapters.d.ts +2 -1
- package/dist-cjs/operations/runner.js +2 -0
- package/dist-cjs/operations/stats.js +11 -4
- package/dist-cjs/postgres/index.d.ts +2 -1
- package/dist-cjs/postgres/index.js +8 -1
- package/dist-cjs/postgres/operations.js +11 -4
- package/dist-cjs/postgres/store.d.ts +7 -0
- package/dist-cjs/postgres/store.js +21 -0
- package/dist-cjs/postgres/tenancy.d.ts +43 -0
- package/dist-cjs/postgres/tenancy.js +110 -0
- package/dist-cjs/protocols/a2a.d.ts +166 -1
- package/dist-cjs/protocols/a2a.js +501 -132
- package/dist-cjs/protocols/acp-editor.d.ts +43 -0
- package/dist-cjs/protocols/acp-editor.js +167 -0
- package/dist-cjs/protocols/acp.d.ts +76 -3
- package/dist-cjs/protocols/acp.js +183 -8
- package/dist-cjs/protocols/ag-ui.d.ts +23 -0
- package/dist-cjs/protocols/ag-ui.js +127 -7
- package/dist-cjs/server/deployments.d.ts +127 -12
- package/dist-cjs/server/deployments.js +402 -37
- package/dist-cjs/server/metrics.d.ts +2 -0
- package/dist-cjs/server/metrics.js +8 -0
- package/dist-cjs/server/runs.d.ts +9 -1
- package/dist-cjs/server/runs.js +45 -6
- package/dist-cjs/server/server.js +4 -1
- package/dist-cjs/server/state.d.ts +12 -2
- package/dist-cjs/server/state.js +15 -0
- package/dist-cjs/sqlite/operations.js +15 -4
- package/dist-cjs/sqlite/store.d.ts +7 -0
- package/dist-cjs/sqlite/store.js +28 -0
- package/dist-cjs/store/memory.d.ts +8 -0
- package/dist-cjs/store/memory.js +12 -1
- package/dist-cjs/store/redis.d.ts +13 -0
- package/dist-cjs/store/redis.js +38 -0
- package/dist-cjs/tracing/instrument.js +15 -3
- package/dist-cjs/tracing/tracer.d.ts +4 -0
- package/dist-cjs/tracing/tracer.js +6 -0
- package/dist-cjs/types/graph.d.ts +7 -0
- package/dist-cjs/types/messages.d.ts +12 -0
- package/dist-cjs/types/operations.d.ts +7 -0
- package/dist-cjs/types/server.d.ts +61 -2
- package/dist-cjs/types/store.d.ts +9 -0
- package/dist-cjs/version.d.ts +1 -1
- package/dist-cjs/version.js +1 -1
- package/llms.txt +1 -1
- 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.
|
|
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
|
-
|
|
274
|
-
|
|
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
|
|
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
|
-
- **
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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. */
|