nexus-ai-pro 2.3.0 → 2.4.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 (178) hide show
  1. package/API_STABILITY.md +186 -0
  2. package/CHANGELOG.md +184 -1
  3. package/README.md +14 -5
  4. package/SECURITY.md +140 -0
  5. package/dist/adapter-kit/contracts.d.ts +87 -0
  6. package/dist/adapter-kit/contracts.js +237 -0
  7. package/dist/adapter-kit/errors.d.ts +39 -0
  8. package/dist/adapter-kit/errors.js +52 -0
  9. package/dist/adapter-kit/index.d.ts +144 -0
  10. package/dist/adapter-kit/index.js +178 -0
  11. package/dist/adapter-kit/semver.d.ts +12 -0
  12. package/dist/adapter-kit/semver.js +55 -0
  13. package/dist/agent/middleware/pii.js +2 -33
  14. package/dist/core/secure-stream.js +4 -3
  15. package/dist/embeddings/adapters.js +2 -1
  16. package/dist/evals/judge.js +2 -5
  17. package/dist/evaluate/cache.d.ts +0 -14
  18. package/dist/evaluate/cache.js +0 -30
  19. package/dist/evaluate/file-cache.d.ts +15 -0
  20. package/dist/evaluate/file-cache.js +30 -0
  21. package/dist/evaluate/index.d.ts +2 -1
  22. package/dist/evaluate/index.js +2 -1
  23. package/dist/evaluate/review.js +2 -2
  24. package/dist/evaluate/run.js +2 -2
  25. package/dist/evaluate/version.js +3 -3
  26. package/dist/graph/graph.js +2 -2
  27. package/dist/graph/node-cache.js +3 -3
  28. package/dist/hallucination/rag.d.ts +4 -1
  29. package/dist/hallucination/rag.js +6 -1
  30. package/dist/images/asset-stores.js +2 -3
  31. package/dist/images/asset-support.js +2 -2
  32. package/dist/images/assets.js +1 -2
  33. package/dist/images/evals.js +4 -4
  34. package/dist/insights/proposals.js +1 -2
  35. package/dist/insights/regressions.d.ts +26 -2
  36. package/dist/insights/regressions.js +129 -3
  37. package/dist/loaders/git.js +8 -1
  38. package/dist/loaders/web.js +7 -1
  39. package/dist/mcp/client.d.ts +14 -0
  40. package/dist/mcp/client.js +59 -1
  41. package/dist/mcp/registry.d.ts +6 -0
  42. package/dist/mcp/registry.js +7 -0
  43. package/dist/prompts/file.js +28 -11
  44. package/dist/protocols/a2a.d.ts +235 -0
  45. package/dist/protocols/a2a.js +386 -0
  46. package/dist/protocols/acp.d.ts +87 -0
  47. package/dist/protocols/acp.js +322 -0
  48. package/dist/protocols/ag-ui.d.ts +120 -0
  49. package/dist/protocols/ag-ui.js +180 -0
  50. package/dist/protocols/shared.d.ts +80 -0
  51. package/dist/protocols/shared.js +49 -0
  52. package/dist/providers/anthropic.js +1 -0
  53. package/dist/providers/cohere.js +2 -2
  54. package/dist/providers/google.js +2 -11
  55. package/dist/providers/openai.js +1 -0
  56. package/dist/rag/file-ingestion.js +15 -9
  57. package/dist/runtime/detect.d.ts +17 -0
  58. package/dist/runtime/detect.js +16 -0
  59. package/dist/runtime/index.d.ts +19 -0
  60. package/dist/runtime/index.js +7 -0
  61. package/dist/security/injection-detector.d.ts +2 -0
  62. package/dist/security/injection-detector.js +7 -1
  63. package/dist/security/input-guard.js +5 -2
  64. package/dist/security/output-guard.js +8 -4
  65. package/dist/security/pii-detector.js +22 -6
  66. package/dist/security/prompt-hardening.js +6 -2
  67. package/dist/security/semantic-injection-classifier.d.ts +2 -1
  68. package/dist/security/semantic-injection-classifier.js +31 -35
  69. package/dist/security/upload-scanner.d.ts +8 -0
  70. package/dist/security/upload-scanner.js +51 -9
  71. package/dist/server/deployments.d.ts +63 -1
  72. package/dist/server/deployments.js +75 -2
  73. package/dist/server/runs.d.ts +9 -0
  74. package/dist/server/runs.js +20 -0
  75. package/dist/server/server.js +5 -0
  76. package/dist/testing/record.js +2 -2
  77. package/dist/tracing/stores.js +2 -1
  78. package/dist/tracing/tracer.js +2 -2
  79. package/dist/types/config.d.ts +20 -0
  80. package/dist/types/server.d.ts +6 -0
  81. package/dist/utils/base64.d.ts +4 -0
  82. package/dist/utils/base64.js +21 -0
  83. package/dist/utils/checksums.d.ts +22 -0
  84. package/dist/utils/checksums.js +39 -0
  85. package/dist/utils/ids.d.ts +8 -0
  86. package/dist/utils/ids.js +26 -2
  87. package/dist/utils/sha256.d.ts +22 -0
  88. package/dist/utils/sha256.js +120 -0
  89. package/dist/version.d.ts +1 -1
  90. package/dist/version.js +1 -1
  91. package/dist-cjs/adapter-kit/contracts.d.ts +87 -0
  92. package/dist-cjs/adapter-kit/contracts.js +243 -0
  93. package/dist-cjs/adapter-kit/errors.d.ts +39 -0
  94. package/dist-cjs/adapter-kit/errors.js +57 -0
  95. package/dist-cjs/adapter-kit/index.d.ts +144 -0
  96. package/dist-cjs/adapter-kit/index.js +194 -0
  97. package/dist-cjs/adapter-kit/semver.d.ts +12 -0
  98. package/dist-cjs/adapter-kit/semver.js +59 -0
  99. package/dist-cjs/agent/middleware/pii.js +4 -35
  100. package/dist-cjs/core/secure-stream.js +4 -3
  101. package/dist-cjs/embeddings/adapters.js +2 -1
  102. package/dist-cjs/evals/judge.js +2 -5
  103. package/dist-cjs/evaluate/cache.d.ts +0 -14
  104. package/dist-cjs/evaluate/cache.js +1 -65
  105. package/dist-cjs/evaluate/file-cache.d.ts +15 -0
  106. package/dist-cjs/evaluate/file-cache.js +67 -0
  107. package/dist-cjs/evaluate/index.d.ts +2 -1
  108. package/dist-cjs/evaluate/index.js +3 -2
  109. package/dist-cjs/evaluate/review.js +2 -2
  110. package/dist-cjs/evaluate/run.js +2 -2
  111. package/dist-cjs/evaluate/version.js +3 -3
  112. package/dist-cjs/graph/graph.js +2 -2
  113. package/dist-cjs/graph/node-cache.js +3 -3
  114. package/dist-cjs/hallucination/rag.d.ts +4 -1
  115. package/dist-cjs/hallucination/rag.js +6 -1
  116. package/dist-cjs/images/asset-stores.js +2 -3
  117. package/dist-cjs/images/asset-support.js +2 -2
  118. package/dist-cjs/images/assets.js +1 -2
  119. package/dist-cjs/images/evals.js +4 -4
  120. package/dist-cjs/insights/proposals.js +1 -2
  121. package/dist-cjs/insights/regressions.d.ts +26 -2
  122. package/dist-cjs/insights/regressions.js +129 -3
  123. package/dist-cjs/loaders/git.js +8 -1
  124. package/dist-cjs/loaders/web.js +7 -1
  125. package/dist-cjs/mcp/client.d.ts +14 -0
  126. package/dist-cjs/mcp/client.js +60 -1
  127. package/dist-cjs/mcp/registry.d.ts +6 -0
  128. package/dist-cjs/mcp/registry.js +7 -0
  129. package/dist-cjs/prompts/file.js +28 -11
  130. package/dist-cjs/protocols/a2a.d.ts +235 -0
  131. package/dist-cjs/protocols/a2a.js +393 -0
  132. package/dist-cjs/protocols/acp.d.ts +87 -0
  133. package/dist-cjs/protocols/acp.js +326 -0
  134. package/dist-cjs/protocols/ag-ui.d.ts +120 -0
  135. package/dist-cjs/protocols/ag-ui.js +186 -0
  136. package/dist-cjs/protocols/shared.d.ts +80 -0
  137. package/dist-cjs/protocols/shared.js +59 -0
  138. package/dist-cjs/providers/anthropic.js +1 -0
  139. package/dist-cjs/providers/cohere.js +2 -2
  140. package/dist-cjs/providers/google.js +3 -12
  141. package/dist-cjs/providers/openai.js +1 -0
  142. package/dist-cjs/rag/file-ingestion.js +14 -8
  143. package/dist-cjs/runtime/detect.d.ts +17 -0
  144. package/dist-cjs/runtime/detect.js +19 -0
  145. package/dist-cjs/runtime/index.d.ts +19 -0
  146. package/dist-cjs/runtime/index.js +31 -0
  147. package/dist-cjs/security/injection-detector.d.ts +2 -0
  148. package/dist-cjs/security/injection-detector.js +7 -1
  149. package/dist-cjs/security/input-guard.js +5 -2
  150. package/dist-cjs/security/output-guard.js +8 -4
  151. package/dist-cjs/security/pii-detector.js +22 -6
  152. package/dist-cjs/security/prompt-hardening.js +6 -2
  153. package/dist-cjs/security/semantic-injection-classifier.d.ts +2 -1
  154. package/dist-cjs/security/semantic-injection-classifier.js +31 -35
  155. package/dist-cjs/security/upload-scanner.d.ts +8 -0
  156. package/dist-cjs/security/upload-scanner.js +53 -9
  157. package/dist-cjs/server/deployments.d.ts +63 -1
  158. package/dist-cjs/server/deployments.js +75 -2
  159. package/dist-cjs/server/runs.d.ts +9 -0
  160. package/dist-cjs/server/runs.js +20 -0
  161. package/dist-cjs/server/server.js +5 -0
  162. package/dist-cjs/testing/record.js +2 -2
  163. package/dist-cjs/tracing/stores.js +2 -1
  164. package/dist-cjs/tracing/tracer.js +2 -2
  165. package/dist-cjs/types/config.d.ts +20 -0
  166. package/dist-cjs/types/server.d.ts +6 -0
  167. package/dist-cjs/utils/base64.d.ts +4 -0
  168. package/dist-cjs/utils/base64.js +25 -0
  169. package/dist-cjs/utils/checksums.d.ts +22 -0
  170. package/dist-cjs/utils/checksums.js +46 -0
  171. package/dist-cjs/utils/ids.d.ts +8 -0
  172. package/dist-cjs/utils/ids.js +28 -2
  173. package/dist-cjs/utils/sha256.d.ts +22 -0
  174. package/dist-cjs/utils/sha256.js +125 -0
  175. package/dist-cjs/version.d.ts +1 -1
  176. package/dist-cjs/version.js +1 -1
  177. package/llms.txt +5 -2
  178. package/package.json +76 -6
package/API_STABILITY.md CHANGED
@@ -41,6 +41,178 @@ 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.4 stage (2.4.0)
45
+
46
+ Everything 2.4 adds is stable from the start and additive. These security fixes tighten behavior,
47
+ as the compatibility rules allow:
48
+ - the upload scanner checks the larger of the declared and actual sizes;
49
+ - an allowlist refuses a file with no MIME type;
50
+ - textual bytes are scanned;
51
+ - secret findings carry a preview instead of the value;
52
+ - card and phone matches must pass their checks.
53
+
54
+ New and stable:
55
+ - `confidence`, `minErrorRateIncrease`, `minFeedbackDrop`, and `resamples` on `watchCanaries()`;
56
+ the same options and `seed` on `compareRuns()`; `Regression.interval`;
57
+ - `Deployments.evaluate()` with `RunEvaluationOptions` and `RunEvaluationReport`;
58
+ - `RunRecord.feedback`, `RunManager.addFeedback()`, and `POST /runs/:id/feedback`;
59
+ - `nexus-ai-pro/adapter-kit`: the `define*()` builders, `Adapter`, `AdapterDefinition`,
60
+ `AdapterReport`, `AdapterError` and its codes, the contract runners, and the capability types.
61
+ Contract checks may be added in a minor release when they test something the contract already
62
+ promised. A new check for a new promise arrives behind a capability an adapter must declare.
63
+ - `nexus-ai-pro/protocols/ag-ui`, `nexus-ai-pro/protocols/a2a`, and `nexus-ai-pro/protocols/acp`:
64
+ the handlers, `a2aClient()`, `a2aTool()`, `serveAcp()`, and their types. Each speaks the
65
+ protocol version it names. A minor release may send an event or answer a method it did not
66
+ before, as a protocol adds them. An event or field already sent keeps its meaning. A new major
67
+ version of a protocol arrives beside the old one, not in its place.
68
+ - `nexus-ai-pro/runtime`, `runtimeInfo()`, `RuntimeInfo`, and `RuntimeName`, which may gain
69
+ names in a minor release; the `fetch` option on the OpenAI, Anthropic, Google, and Cohere
70
+ provider configurations.
71
+ - Portability, for the client, the kernel, the provider adapters, graphs, agents, and the agent
72
+ protocols: none of them starts needing Node.js in a minor release, and the build fails if one
73
+ does.
74
+
75
+ Without `confidence`, a canary is judged as before.
76
+
77
+ These security fixes tighten more behavior, each closing a finding of a graduation review in
78
+ [SECURITY.md](./SECURITY.md#threat-reviews):
79
+ - the Git loader never follows a symbolic link, nor a path that leaves the checkout;
80
+ - a gzipped sitemap may inflate to 50 MB at most, the sitemap protocol's maximum;
81
+ - a local MCP server inherits only what a process needs to run, not this process's environment.
82
+ `inheritEnv` restores the old behavior, by name or entirely;
83
+ - a canary is judged on runs, so each run counts once per feedback key, as the mean of its scores;
84
+ - the file prompt store's label compare-and-set holds within one process;
85
+ - changes to one deployment through one `Deployments` object apply in order.
86
+
87
+ ### Graduation
88
+
89
+ A surface left experimental status in 2.4 only when every requirement in
90
+ [How an item graduates](./ROADMAP.md#how-an-item-graduates) had evidence. That evidence is below.
91
+ "n/a" names a requirement that does not apply, and says why. Every test named here runs in CI.
92
+ Load and soak budgets are checked by `npm run bench:surfaces` and `npm run bench:runtime`, which are
93
+ part of `check:release`. Upgrade tests run against the published 2.3.0 package, installed as a
94
+ development alias.
95
+
96
+ Every graduated surface is covered by the 2.x compatibility rules, with no breaking change planned.
97
+ That is the "API stability" requirement, met by all of them.
98
+
99
+ **The SQLite adapters** (`nexus-ai-pro/sqlite` and its subpaths):
100
+
101
+ | Requirement | Evidence |
102
+ | --- | --- |
103
+ | Conformance | The operation store, store, and vector store contracts: `tests/sqlite.test.ts`, `tests/vector-stores.test.ts` |
104
+ | Crash recovery | A crash at every durable boundary resumes to the uninterrupted state: `tests/crash-injection.test.ts` |
105
+ | Concurrency | Ten workers race one thread, one idempotency key, one queue, and one budget: `tests/ten-workers.test.ts` |
106
+ | Load | `checkpoint.sqlite` and `operations.claim-10k-queued` in `runtime-budget.json`; `sqlite.vectors-query-2k` and `sqlite.store-put-get-search` in `surface-budget.json` |
107
+ | Tenant isolation | Every SQLite store: `tests/tenant-isolation.test.ts` |
108
+ | Security | [Review](./SECURITY.md#sqlite-adapters): no findings |
109
+ | Upgrade | A 2.3 file upgrades in place, and a 2.3 worker shares its queue: `tests/upgrade-previous-minor.test.ts`; likewise from 2.0: `tests/upgrade.test.ts` |
110
+ | Soak | 20,000 queue lifecycles and 2,000 open, migrate, and close cycles, with flat memory and no handle left open |
111
+
112
+ **The Postgres and SQLite vector stores** (`PostgresVectorStore`, `SqliteVectorStore`):
113
+
114
+ | Requirement | Evidence |
115
+ | --- | --- |
116
+ | Conformance | The vector store contract on pgvector and on SQLite: `tests/vector-stores.test.ts`. The retriever contract over each: `tests/graduation-conformance.test.ts` |
117
+ | Crash recovery | n/a: a store keeps no run state, and a reload upserts the same ids |
118
+ | Concurrency | Ten writers upserting the same ids at once leave one whole row per id: `tests/graduation-conformance.test.ts` |
119
+ | Load | `sqlite.vectors-query-2k` and `postgres.vectors-query-1k` in `surface-budget.json` |
120
+ | Tenant isolation | Both stores: `tests/tenant-isolation.test.ts` |
121
+ | Security | [Review](./SECURITY.md#the-postgres-and-sqlite-vector-stores): no findings |
122
+ | Upgrade | A table 2.3 wrote is searched and filtered here: `tests/upgrade-previous-minor.test.ts` |
123
+ | Soak | 1,000 write, search, and delete cycles on pgvector |
124
+
125
+ **The loaders** (`nexus-ai-pro/loaders/*`):
126
+
127
+ | Requirement | Evidence |
128
+ | --- | --- |
129
+ | Conformance | Every format, the web loader through the SSRF-safe fetch, and Git cloned and in place: `tests/loaders.test.ts` |
130
+ | Crash recovery | n/a: a loader keeps no state, and reloading replaces the same chunks |
131
+ | Concurrency | n/a: a loader shares no records |
132
+ | Load | `loaders.markdown-csv-html` in `surface-budget.json` |
133
+ | Tenant isolation | n/a: a loader stores nothing; the store it loads into is isolated |
134
+ | Security | [Review](./SECURITY.md#loaders): two findings, both fixed |
135
+ | Upgrade | The same files give the same ids and text as in 2.3: `tests/upgrade-previous-minor.test.ts` |
136
+ | Soak | 2,000 load cycles |
137
+
138
+ **The retrievers** (`nexus-ai-pro/rag/retrievers`):
139
+
140
+ | Requirement | Evidence |
141
+ | --- | --- |
142
+ | Conformance | The retriever contract on `KeywordIndex`, on a vector retriever over memory, SQLite, and pgvector, and on `hybridRetriever()`: `tests/graduation-conformance.test.ts` |
143
+ | Crash recovery | n/a: an index is held in memory, and rebuilt by loading |
144
+ | Concurrency | n/a: an index belongs to one process |
145
+ | Load | `retrievers.keyword-query-5k` and `retrievers.hybrid-query` in `surface-budget.json` |
146
+ | Tenant isolation | Filters narrow every retriever, by the contract; the stores behind them are isolated |
147
+ | Security | [Review](./SECURITY.md#retrievers): no findings |
148
+ | Upgrade | The same corpus ranks the same, score for score, as in 2.3: `tests/upgrade-previous-minor.test.ts` |
149
+ | Soak | 50,000 add and delete cycles |
150
+
151
+ **The MCP registry** (`nexus-ai-pro/mcp/registry`):
152
+
153
+ | Requirement | Evidence |
154
+ | --- | --- |
155
+ | Conformance | Servers over the protocol in process and as a real child process: `tests/mcp-registry.test.ts` |
156
+ | Crash recovery | A server that fails to start is reported, and the rest serve: `tests/mcp-registry.test.ts` |
157
+ | Concurrency | n/a: a registry shares no records |
158
+ | Load | `mcp.registry-tools-10-servers` in `surface-budget.json` |
159
+ | Tenant isolation | n/a: a registry stores nothing |
160
+ | Security | [Review](./SECURITY.md#mcp-registry): one finding, fixed |
161
+ | Upgrade | A configuration reads the same as in 2.3: `tests/upgrade-previous-minor.test.ts` |
162
+ | Soak | 1,000 connect, call, and close cycles, with no handle left open |
163
+
164
+ **The context hub** (`nexus-ai-pro/context-hub`):
165
+
166
+ | Requirement | Evidence |
167
+ | --- | --- |
168
+ | Conformance | The same behavior on memory, files, Redis, and Postgres: `tests/graduation-conformance.test.ts` |
169
+ | Crash recovery | A commit is content-addressed, so repeating an interrupted one writes the same version: `tests/context-hub.test.ts` |
170
+ | Concurrency | Two hubs racing for one label never both win, on every backend: `tests/graduation-conformance.test.ts` |
171
+ | Load | `context-hub.commit-resolve` in `surface-budget.json` |
172
+ | Tenant isolation | Context bundles: `tests/tenant-isolation.test.ts` |
173
+ | Security | [Review](./SECURITY.md#context-hub): one finding, fixed |
174
+ | Upgrade | A bundle has the same version as in 2.3, and a 2.3 export imports: `tests/upgrade-previous-minor.test.ts` |
175
+ | Soak | 2,000 commit, resolve, and render cycles |
176
+
177
+ **Insights** (`nexus-ai-pro/insights`):
178
+
179
+ | Requirement | Evidence |
180
+ | --- | --- |
181
+ | Conformance | The same issues from runs in memory and in Postgres: `tests/graduation-conformance.test.ts`; proposals in memory and in files: `tests/insights.test.ts` |
182
+ | Crash recovery | n/a: analysis is read-only, and a proposal is one write |
183
+ | Concurrency | n/a: a proposal is decided once, by a person |
184
+ | Load | `insights.find-issues-5k-runs` in `surface-budget.json` |
185
+ | Tenant isolation | Insights read through the trace store, which is isolated: `tests/tenant-isolation.test.ts` |
186
+ | Security | [Review](./SECURITY.md#insights): no findings |
187
+ | Upgrade | A 2.3 proposal reads here, and errors group under the same signatures: `tests/upgrade-previous-minor.test.ts` |
188
+ | Soak | 300 clustering cycles |
189
+
190
+ **Tenant limits and the server's worker queue** (`nexus-ai-pro/server/tenancy`, and the queue
191
+ `createAgentServer()` runs on its operation store):
192
+
193
+ | Requirement | Evidence |
194
+ | --- | --- |
195
+ | Conformance | Tenant usage in memory and in Redis; the queue on every operation store: `tests/deployments.test.ts`, `tests/operations.test.ts` |
196
+ | Crash recovery | A crash at every durable boundary resumes to the same state, and a draining worker hands a run to another: `tests/crash-injection.test.ts`, `tests/deployments.test.ts` |
197
+ | Concurrency | Ten workers drain one queue, and spend one tenant budget without over-granting a slot: `tests/ten-workers.test.ts` |
198
+ | Load | `tenancy.admit-release` in `surface-budget.json`; `operations.claim-10k-queued` and `server.sse-fanout` in `runtime-budget.json` |
199
+ | Tenant isolation | Operations: `tests/tenant-isolation.test.ts` |
200
+ | Security | [Review](./SECURITY.md#tenant-limits-and-the-worker-queue): no findings |
201
+ | Upgrade | A 2.3 worker and a 2.4 worker drain one queue: `tests/upgrade-previous-minor.test.ts` |
202
+ | Soak | 50,000 admit and release cycles, and 500 replica lifecycles with no timer left running |
203
+
204
+ **Still experimental**, each with what remains:
205
+ - **Deployments** (`nexus-ai-pro/server/deployments`). Two replicas changing one deployment in the
206
+ same instant can lose one change, because the state store has no atomic compare-and-set. Every
207
+ other requirement has evidence above or in its review.
208
+ - **Images.** Recorded live conformance must pass on all three backends. The scheduled live workflow
209
+ now records it, wherever a backend's credential is set.
210
+ - **The Qdrant, Redis, Pinecone, Weaviate, and Chroma vector stores.** The contract must pass against
211
+ the real servers, not only stubs. The scheduled live workflow now runs it against each one as a
212
+ service container, and Pinecone runs when its secret is set.
213
+ - **The studio and its accounts.** A threat review of its sign-in, sessions, and roles, and a soak
214
+ of its server, are still to do.
215
+
44
216
  ## 2.3 stage (2.3.0)
45
217
 
46
218
  Everything 2.3 adds is stable from the start and additive: no default changes meaning.
@@ -618,6 +790,9 @@ of the 1.x line.
618
790
 
619
791
  ## Deployment stage (1.24.0)
620
792
 
793
+ Tenant limits and the worker queue graduated in 2.4; see the 2.4 stage for their evidence.
794
+ Deployments remain experimental.
795
+
621
796
  Deployments (`nexus-ai-pro/server/deployments`), tenant limits (`nexus-ai-pro/server/tenancy`), and the
622
797
  server's worker queue are experimental in production readiness. Their contracts follow the 1.x rules.
623
798
 
@@ -632,6 +807,9 @@ server's worker queue are experimental in production readiness. Their contracts
632
807
 
633
808
  ## Team platform stage (1.23.0)
634
809
 
810
+ The context hub and insights graduated in 2.4; see the 2.4 stage for their evidence. The studio's
811
+ accounts remain experimental.
812
+
635
813
  The context hub (`nexus-ai-pro/context-hub`), insights (`nexus-ai-pro/insights`), and the studio's
636
814
  accounts are experimental in production readiness. Their contracts follow the 1.x rules.
637
815
 
@@ -644,6 +822,9 @@ accounts are experimental in production readiness. Their contracts follow the 1.
644
822
 
645
823
  ## Retrieval breadth stage (1.22.0)
646
824
 
825
+ The loaders, the SQLite vector store, the retrievers, and the MCP registry graduated in 2.4; see the
826
+ 2.4 stage for their evidence. The Redis, Pinecone, Weaviate, and Chroma stores remain experimental.
827
+
647
828
  The loaders (`nexus-ai-pro/loaders/*`), the Redis, Pinecone, Weaviate, Chroma, and SQLite vector
648
829
  stores, the retrievers (`nexus-ai-pro/rag/retrievers`), and the MCP registry
649
830
  (`nexus-ai-pro/mcp/registry`) are experimental in production readiness. Their contracts follow the
@@ -660,6 +841,8 @@ stores, the retrievers (`nexus-ai-pro/rag/retrievers`), and the MCP registry
660
841
 
661
842
  ## Durable execution stage (1.21.0)
662
843
 
844
+ The SQLite adapters graduated in 2.4; see the 2.4 stage for their evidence.
845
+
663
846
  Functional workflows (`nexus-ai-pro/graph/functional`) and the SQLite adapters (`nexus-ai-pro/sqlite`)
664
847
  are experimental in production readiness. Their contracts follow the 1.x rules.
665
848
 
@@ -672,6 +855,9 @@ are experimental in production readiness. Their contracts follow the 1.x rules.
672
855
 
673
856
  ## Retrieval stores stage (1.20.0)
674
857
 
858
+ `PostgresVectorStore` graduated in 2.4; see the 2.4 stage for its evidence. `QdrantVectorStore`
859
+ remains experimental.
860
+
675
861
  `PostgresVectorStore` (`nexus-ai-pro/postgres/vectors`) and `QdrantVectorStore` (`nexus-ai-pro/rag/qdrant`)
676
862
  are experimental in production readiness. The `VectorStore` contract they share with
677
863
  `MemoryVectorStore` follows the 1.x rules: a minor release may add optional methods and options, not
package/CHANGELOG.md CHANGED
@@ -4,6 +4,188 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [2.4.0] - 2026-10-08
8
+
9
+ Graduation and interoperability.
10
+
11
+ ### Added
12
+
13
+ - **Canaries judged with confidence, and on quality.**
14
+ - `watchCanaries({ confidence })` judges each metric the way an evaluation comparison judges a
15
+ score: a seeded bootstrap interval that must not reach zero, and a minimum effect —
16
+ `minErrorRateIncrease`, the latency and cost margins, and `minFeedbackDrop`. A real regression
17
+ rolls back; noise and a trivially small change do not. `compareRuns()` takes the same options,
18
+ and each `Regression` carries its `interval`.
19
+ - `Deployments.evaluate()` scores an assistant's finished runs with evaluators from
20
+ `nexus-ai-pro/evaluate`, and records the scores as run feedback. A canary that answers worse is
21
+ then rolled back like one that fails more. Sampling is stable by run id, and a run is scored
22
+ once per source.
23
+ - Runs carry `feedback`. `POST /runs/:id/feedback` and `RunManager.addFeedback()` record a
24
+ person's rating as well.
25
+ - **An adapter kit**, on the new `nexus-ai-pro/adapter-kit`, for building adapters outside this
26
+ repository:
27
+ - `defineProviderAdapter()`, `defineEmbeddingsAdapter()`, `defineVectorStoreAdapter()`,
28
+ `defineRetrieverAdapter()`, and `defineStoreAdapter()`. Each takes a capability declaration
29
+ and the range of releases the adapter supports, read as npm reads ranges.
30
+ - Every instance normalizes its failures into an `AdapterError` with a code and `retryable`, and
31
+ reports each call to an `onCall` telemetry hook.
32
+ - `verify()` runs the adapter's contract suite, skipping only what it declares it cannot do, with
33
+ a benchmark held to the p95 budgets it declares.
34
+ - The vector-store, retriever, and store contracts ship as `runVectorStoreContract()`,
35
+ `runRetrieverContract()`, and `runStoreContract()`, with no test framework needed.
36
+ - **Agent protocols**, each on its own entry point and with no dependency:
37
+ - `nexus-ai-pro/protocols/ag-ui`: `agUiHandler()` serves a graph to any AG-UI frontend as
38
+ Server-Sent Events: each step, text as it streams, tool calls with their results, and the final
39
+ state. A run that needs approval finishes with an interrupt, and a resume entry on the same
40
+ thread continues it. `agUiEvents()` yields the same events for any other transport.
41
+ - `nexus-ai-pro/protocols/a2a`: `a2aHandler()` serves a graph as an A2A 1.0 agent: its card,
42
+ `SendMessage`, `SendStreamingMessage`, `GetTask`, and `CancelTask`. A context is a
43
+ conversation, and a task that needs approval asks for input and continues when a message answers
44
+ it. `a2aClient()` calls any A2A agent, and `a2aTool()` makes one a tool that declares the
45
+ host it reaches, so a permission policy decides it.
46
+ - `nexus-ai-pro/protocols/acp`: `serveAcp()` puts a graph in a code editor over the Agent
47
+ Client Protocol. The editor sees streamed text and thought, each tool call with its kind, file,
48
+ and diff, and the plan from `write_todos`. It asks the person to approve calls, and keeps an
49
+ always-allow or always-reject choice for the session.
50
+ - **A portable kernel**, on the new `nexus-ai-pro/runtime`: messages, responses, the provider and
51
+ tool contracts, streaming, and capabilities, in one small import that reaches nothing Node-only.
52
+ `runtimeInfo()` names the runtime the code is on.
53
+ - **The client, the provider adapters, graphs, agents, and the agent protocols run on Deno, Bun,
54
+ edge runtimes, and browsers.**
55
+ - Ids come from Web Crypto.
56
+ - SHA-256 for node-cache keys and dataset versions is computed in plain JavaScript where the
57
+ runtime has no synchronous native digest. The digests are identical, so existing cache keys
58
+ still match.
59
+ - Base64 and UTF-8 byte counts no longer need `Buffer`.
60
+ - 125 of 157 entry points now run anywhere. The new runtimes guide lists the rest, each with
61
+ exactly what it needs.
62
+ - The OpenAI, Anthropic, Google, and Cohere providers take a `fetch` option: a platform's bound
63
+ fetch, a proxy, or a test stub.
64
+ - **Measured graduation.** These surfaces leave experimental status, each with evidence for every
65
+ requirement of the graduation checklist, published in `API_STABILITY.md`:
66
+ - the SQLite adapters;
67
+ - the Postgres and SQLite vector stores;
68
+ - the loaders and the retrievers;
69
+ - the MCP registry;
70
+ - the context hub and insights;
71
+ - tenant limits and the server's worker queue.
72
+
73
+ Deployments, images, the Qdrant, Redis, Pinecone, Weaviate, and Chroma vector stores, and the
74
+ studio stay experimental, each with the requirement that remains named.
75
+ - `inheritEnv` on a local MCP server, in `StdioClientOptions` and in the registry's server
76
+ configuration: which of this process's environment variables the server inherits.
77
+ - Load and soak budgets for every graduated surface, held in CI by `npm run bench:surfaces`. A soak
78
+ fails on heap growth, on a handle left open, or on a timer never cleared, unref'd ones included.
79
+ - The scheduled live workflow records image conformance on every backend with a credential, and
80
+ runs the vector store contract against real Qdrant, Chroma, Weaviate, and Redis servers.
81
+
82
+ ### Security
83
+
84
+ Testing the security code to depth found these gaps, and each is closed:
85
+
86
+ - **The upload scanner trusted the caller.**
87
+ - A declared `sizeBytes` could carry a larger file past `maxBytes`; the larger of the declared and
88
+ actual sizes is now checked.
89
+ - `run.exe.`, `run.exe ` (Windows drops the trailing dot or space), and a name with a control
90
+ character such as `run.exe\0.txt` passed the extension block; names are now read as the file
91
+ system reads them, and a control character is itself a critical finding.
92
+ - Content given as bytes, which is how upload middleware delivers it, was never scanned; textual
93
+ bytes are now decoded and scanned.
94
+ - A file with no MIME type passed an allowlist; an allowlist now fails closed. Types are compared
95
+ without parameters, so `text/plain; charset=utf-8` is `text/plain`.
96
+ - **File ingestion read what it never scanned.** Text an extractor returns from a PDF or an image is
97
+ now scanned like a text file. A text file given as bytes is ingested rather than skipped.
98
+ - **The input guard put secrets in its findings.** A finding now carries a short preview, such as
99
+ `ghp_… (40 characters)`, never the secret. The SSRF check also catches `[::1]`, all of
100
+ `127.0.0.0/8`, and the Google and Alibaba metadata hosts.
101
+ - **Prompt hardening could be escaped.** User text holding the delimiter closed the block early; each
102
+ occurrence inside the text is now broken apart. The text parts of multi-part messages are wrapped
103
+ too.
104
+ - **The injection detectors read text as typed, not as the model reads it.** Both now fold
105
+ full-width letters and drop zero-width characters first, so `IGNORE previous instructions` and
106
+ `ig​nore previous instructions` are found. `neutralize()` leaves clean text exactly as it was.
107
+ - **The PII detector and the output guard took any long number for a card or a phone.** A
108
+ millisecond timestamp or an order id was masked, and in the output guard a card's last digits
109
+ could survive because the phone pattern claimed the rest first. Card numbers must now pass Luhn,
110
+ and phone numbers need 10 to 15 digits with a separator or a leading `+`. Cards are claimed before
111
+ phones, and a span one kind has claimed is not reported again as another.
112
+ - **Retrieved context reads as data.** `withRagContext()` now tells the model that the context is
113
+ data, not instructions.
114
+
115
+ The graduation reviews, published in `SECURITY.md`, found these, and each is closed:
116
+
117
+ - **The Git loader followed symbolic links.** A repository with a tracked link to a file of the host,
118
+ such as `~/.ssh/id_rsa`, had that file loaded into a knowledge base. A link is now never followed,
119
+ nor a path that leaves the checkout.
120
+ - **A gzipped sitemap could be a decompression bomb.** Inflation now stops at 50 MB, the sitemap
121
+ protocol's maximum, and the loader reports the sitemap.
122
+ - **A local MCP server inherited this process's whole environment**, every API key and database URL
123
+ included. It now starts with its `env` and only what a process needs to run: the path, the home
124
+ and temporary directories, the locale, the Windows system variables, proxies, and certificates.
125
+ Name what else a server needs in `env`, or set `inheritEnv`.
126
+ - **One caller's ratings could decide a canary.** Feedback was counted per rating, so a hundred low
127
+ ratings of one run met the minimum sample alone, and could roll back a canary or keep a bad one
128
+ alive. Each run now counts once per feedback key, as the mean of its ratings.
129
+ - **The file prompt store could lose a label move.** Its compare-and-set was a read and then a write,
130
+ so two moves at once in one process could both succeed, the later erasing the earlier. Label
131
+ writes to one file are now serialized across the process.
132
+ - **Two changes at once to one deployment, on one replica, could lose one**, such as a guard's
133
+ rollback and an operator's promotion. They now apply in order. Changes from two replicas in the
134
+ same instant still can, which keeps deployments experimental.
135
+
136
+ ### Fixed
137
+
138
+ - The agent server's guide called the server experimental, though it has been stable since 2.0.
139
+ - Bundling the client for an edge runtime failed: it reached the file-based evaluation cache, which
140
+ loads `node:fs`. `FileEvaluationCache` now lives in a module of its own, still exported from
141
+ `nexus-ai-pro/evaluate`.
142
+ - `extractCitations()` and `validateCitations()` read a Markdown link `[text](url)` as a citation,
143
+ and `[doc-1, doc-2]` as one id; links are ignored and lists are split.
144
+ - The semantic injection classifier embeds a request in one call instead of one per message, and
145
+ `detectSync()` honours `enabled: false` as `detect()` did.
146
+
147
+ ### Proof
148
+
149
+ - Grounding and verification, file ingestion, the upload scanner, the PII detector, and both
150
+ injection detectors are tested to the package's own coverage:
151
+ - grounding, the PII detector, prompt hardening, and the upload scanner at 100% of lines;
152
+ - file ingestion, which no test loaded before, at 97%.
153
+ - Each security fix above is a test that names the attack it stops or the false positive it rules
154
+ out.
155
+ - A canary seeded with a quality regression is rolled back. The proof runs on a real server: 300
156
+ runs, online evaluation recording the scores, and the guard reading them. A canary whose quality
157
+ differs only within the noise keeps its share. A difference that 20,000 runs make significant, but
158
+ that is under the minimum effect, is not a regression either.
159
+ - A community-style adapter, built only from the kit, passes its contract suite. It is a vector store
160
+ in plain JavaScript inside the clean-install test's consumer project, importing nothing but
161
+ `nexus-ai-pro/adapter-kit` from the installed tarball. A deliberately broken store fails exactly the
162
+ checks it breaks.
163
+ - A Nexus agent and a remote agent complete tasks over A2A in both directions:
164
+ - A client that writes raw JSON-RPC, as the specification describes it, drives the served agent
165
+ through a conversation, an approval, streaming, cancellation, and each error code.
166
+ - The client and tool complete tasks against a stub agent written from the specification alone.
167
+ - One Nexus agent calls another through the tool.
168
+ - A frontend renders a run through AG-UI with a plain `fetch` and Server-Sent Events parser, and
169
+ no Nexus client code, approval and resume included.
170
+ - An editor's end of an ACP connection approves an edit, and sees the plan, the diff, and the
171
+ answer.
172
+ - The kernel's tests pass on Node.js, Deno, Bun, and Vercel's edge runtime, and CI runs all four.
173
+ The tests are written against web-standard APIs only, with no test framework and nothing of Node's,
174
+ and they cover a provider over `fetch`, the client, a cached graph, an agent, and A2A and AG-UI.
175
+ A check walks every entry point's imports, static and dynamic. It fails the build when the client,
176
+ the kernel, a provider adapter, graphs, agents, or a protocol reaches anything Node-only.
177
+ - Every graduated surface passes its contract on every backend it supports. For the context hub,
178
+ that is memory, files, Redis, and Postgres, with two hubs racing for one label and never both
179
+ winning. For the retrievers, it is a vector retriever over memory, SQLite, and pgvector, besides
180
+ the keyword and hybrid retrievers.
181
+ - What the published 2.3.0 package wrote, this release reads, and what it derived, this release
182
+ derives the same: a SQLite file shared by a 2.3 and a 2.4 worker, a pgvector table, loader ids,
183
+ retriever rankings, MCP configurations, context bundle versions and exports, insight proposals
184
+ and error signatures, and canary routing, thread by thread.
185
+ - Each security fix has a test that names the attack, and fails on the code before it. The Git test
186
+ runs on Linux, where links are tracked as links, and the race test holds both writers until both
187
+ arrive, so it does not depend on timing.
188
+
7
189
  ## [2.3.0] - 2026-10-06
8
190
 
9
191
  Agents that act safely, on production retrieval.
@@ -1777,7 +1959,8 @@ shared policy vocabulary is in place for a future release that revisits this.
1777
1959
  - URL fetching rejects unsafe private-network targets and limits response reads.
1778
1960
  - CLI and audit findings no longer disclose detected secret values.
1779
1961
 
1780
- [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.3.0...HEAD
1962
+ [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.4.0...HEAD
1963
+ [2.4.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.3.0...v2.4.0
1781
1964
  [2.3.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.2.0...v2.3.0
1782
1965
  [2.2.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.1.0...v2.2.0
1783
1966
  [2.1.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.0.0...v2.1.0
package/README.md CHANGED
@@ -8,7 +8,10 @@ team can share.
8
8
  Import only what you use. The root import is the client and nothing else, and every other capability
9
9
  has its own entry point with a size budget CI enforces; [the packaging guide](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/packaging.md)
10
10
  publishes what each one costs. The package has no required dependencies: a graph-only application
11
- loads 80 KB, and installs nothing but this package.
11
+ loads 81 KB, and installs nothing but this package.
12
+ The client, the provider adapters, graphs, agents, and the agent protocols run on Node.js, Deno,
13
+ Bun, edge runtimes, and browsers; [the runtimes guide](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/runtimes.md)
14
+ lists the entry points that need Node.js, and why.
12
15
 
13
16
  - NPM: https://www.npmjs.com/package/nexus-ai-pro
14
17
  - GitHub: https://github.com/mkhitar-abrahamyan/nexus-ai
@@ -136,6 +139,7 @@ from the doc comments. The guides live in the repository, so these links go to G
136
139
  | [The deep-agent preset](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/deep-agents.md) | Long, multi-step agents: a plan, a sandboxed workspace, helpers, skills, and a context that stays small |
137
140
  | [Long-term memory](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/memory.md) | Namespaced memory with semantic search, in memory or Redis |
138
141
  | [MCP](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/mcp.md) | Borrowing tools from MCP servers, a registry of many, and serving your own |
142
+ | [Agent protocols](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/protocols.md) | Serving an agent to user interfaces over AG-UI, to other agents over A2A, and to code editors over ACP, and calling remote agents as tools |
139
143
  | [Traces](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/tracing.md) | Run trees, queries, feedback, comparison, and alerts |
140
144
  | [Evaluation](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/evaluation.md) | Datasets, evaluators, experiments, comparisons with a verdict, review queues, LLM judges |
141
145
  | [Context hub](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/context-hub.md) | Prompts, instructions, tools, and skills versioned together, promoted through gates, and moved between projects |
@@ -156,6 +160,7 @@ from the doc comments. The guides live in the repository, so these links go to G
156
160
  | [Realtime voice](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/realtime.md) | Browser and server realtime sessions with barge-in, tools, and exports |
157
161
  | [Telephony](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/telephony.md) | Calls, webhooks, phone numbers, and phone agents on realtime sessions |
158
162
  | [Testing](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/testing.md) | Recording and replaying provider traffic, and conformance suites |
163
+ | [Adapter kit](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/adapter-kit.md) | Building a provider, store, or retriever outside this repository, and verifying it against the same contracts |
159
164
  | [Studio](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/studio.md) | A shared UI for traces, threads, approvals, experiments, prompts, deployments, costs, and health, as a separate package |
160
165
  | [Agent server](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/server.md) | Self-hosted HTTP server for assistants: threads, durable runs, resumable streams, cron, a worker queue, and metrics |
161
166
  | [Deployments](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/deployments.md) | Revisions and canaries with automatic rollback, autoscaling on Kubernetes and Helm, and per-tenant limits |
@@ -163,6 +168,7 @@ from the doc comments. The guides live in the repository, so these links go to G
163
168
  | [Workflows](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/workflows.md) | Ready-made chains and domain workflows |
164
169
  | [Command line](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/cli.md) | The `nexus` command |
165
170
  | [Packaging and install weight](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/packaging.md) | Every entry point and what it costs |
171
+ | [Runtimes](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/runtimes.md) | The portable kernel, and running the client, providers, graphs, and agents on Deno, Bun, edge runtimes, and browsers |
166
172
 
167
173
  ## Examples
168
174
 
@@ -256,13 +262,16 @@ Shipped so far:
256
262
  - 2.3 agents that act safely, on production retrieval: capabilities and permission policies, a
257
263
  sandbox contract, a middleware catalog, server authentication with one principal on every run,
258
264
  keyword search in Postgres and Elasticsearch with rerankers, durable ingestion, and a deep-agent
259
- preset.
265
+ preset;
266
+ - 2.4 graduation and interoperability: SQLite, loaders, retrievers, the MCP registry, the context
267
+ hub, insights, and tenant limits out of experimental with published evidence; canaries judged with
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.
260
270
 
261
271
  Upgrading from 1.x: `npx nexus migrate src --write` moves your imports, and
262
272
  [MIGRATING.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/MIGRATING.md) covers the rest.
263
- Next, 2.4 takes the experimental surfaces out of experimental with evidence, adds an adapter kit, and
264
- speaks the agent protocols beyond MCP from a portable kernel. The [roadmap](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/ROADMAP.md) lists the
265
- releases after it.
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.
266
275
 
267
276
  ## Before Production
268
277
 
package/SECURITY.md CHANGED
@@ -44,3 +44,143 @@ Nexus guardrails reduce common AI application risks, but they are not a complete
44
44
  - Browser WebRTC still requires a backend security boundary. A direct media path reduces latency; it
45
45
  does not remove the need for server-owned credentials, session policy, tool authorization, abuse
46
46
  monitoring, and provider-side safety controls.
47
+
48
+ ## Threat reviews
49
+
50
+ Every surface that leaves experimental status is reviewed for how untrusted input reaches it and
51
+ what it could do with that input. Each review is published here with its findings, which are closed
52
+ before the surface graduates. A finding is closed in one of two ways: by a fix with a test that
53
+ names the attack, or by a documented limitation that the surface's guide states.
54
+
55
+ These reviews were done for 2.4.
56
+
57
+ ### SQLite adapters
58
+
59
+ Trust boundary: an application's data, an operation's payload, and metadata filters that may come
60
+ from a request.
61
+ - Table names are checked against a strict pattern and quoted. Every value, including each metadata
62
+ filter key and its JSON path, is a bound parameter, so a filter cannot change a statement.
63
+ - A migration records its checksum, and a changed migration is refused rather than reapplied.
64
+ - Every change to an operation is one `UPDATE … WHERE sequence = expected`, so two workers sharing
65
+ a file never both claim it. The ten-worker race tests run on SQLite.
66
+ - Tenant data is scoped by key prefix, and the isolation suite runs on every SQLite store.
67
+
68
+ Findings: none.
69
+
70
+ ### The Postgres and SQLite vector stores
71
+
72
+ Trust boundary: metadata filters, which applications often build from a request.
73
+ - The pgvector store sends a filter as one bound `jsonb` containment parameter, and quotes its table
74
+ and index names.
75
+ - The SQLite store binds each key's JSON path and value, and matches a value's JSON type as well as
76
+ its value, so `"1"` never matches `1`.
77
+
78
+ Findings: none.
79
+
80
+ ### Loaders
81
+
82
+ Trust boundary: everything a loader reads is untrusted. A repository, a website, a sitemap, or an
83
+ uploaded file may be written by an attacker who wants it in a knowledge base, or who wants the
84
+ loader to read something else.
85
+ - Web pages and sitemaps go through the SSRF-safe fetch. Private, loopback, and cloud-metadata
86
+ addresses are refused, every redirect is checked again, and bodies are capped.
87
+ - The Git loader runs `git` without a shell. It disables the `ext::` transport, puts `--` before
88
+ the repository, and refuses a repository that starts with `-`.
89
+ - `loadDirectory()` does not follow symbolic links.
90
+
91
+ Findings, both fixed in 2.4:
92
+ - **The Git loader followed symbolic links.** A tracked link such as `notes.md` pointing to
93
+ `~/.ssh/id_rsa` was read and ingested, so a knowledge base could quote a file of the host. A
94
+ link is now never followed, nor a path that resolves outside the checkout. Proof: a repository
95
+ holding a tracked link to a file outside it loads without that file, cloned and in place. On
96
+ Linux, the test fails on the earlier loader.
97
+ - **A gzipped sitemap could be a decompression bomb.** A few megabytes of gzip inflated without a
98
+ limit. Inflation now stops at 50 MB, the sitemap protocol's own maximum. Proof: 60 MB of gzip
99
+ that inflates past it is refused.
100
+
101
+ ### Retrievers
102
+
103
+ Trust boundary: queries come from users, and documents may hold text written to steer a model.
104
+ - Tokenizing is one linear regular expression, with no backtracking on hostile input.
105
+ - Filters compare values; they never become code or a query language.
106
+ - A model reranker or query-variant generator reads documents and queries as data in its prompt. Its
107
+ output is parsed as JSON and used only to order or widen a search, never to call a tool.
108
+
109
+ Findings: none.
110
+
111
+ ### MCP registry
112
+
113
+ Trust boundary: the configuration file is trusted, since it names programs to run. What a server
114
+ says about itself is not: its tool names, its descriptions, and its results.
115
+ - A server's tools are prefixed with its name by default, so one server cannot shadow another's
116
+ tool.
117
+ - Allow and deny lists limit what agents see, and deny wins.
118
+ - A tool from a server declares no capabilities. A permission policy denies such a tool unless its
119
+ `undeclared` rule says otherwise, and `humanApproval()` asks about it by default.
120
+ - `${NAME}` placeholders keep credentials out of the file. A missing variable is named in the error,
121
+ never its value.
122
+
123
+ Finding, fixed in 2.4: **a local server inherited this process's whole environment.** Any
124
+ `npx`-started server, which is usually third-party code, saw every API key and database URL the
125
+ application held. A server now starts with `env` and only what a process needs to run: the path,
126
+ the home and temporary directories, the locale, proxies, and certificates. The new `inheritEnv`
127
+ option widens this by name, or entirely. Proof: a real child process started by the transport sees
128
+ the variables it was given, and not a secret set in the parent.
129
+
130
+ ### Context hub
131
+
132
+ Trust boundary: an imported bundle may come from another team or another organization. Who may
133
+ commit and promote is the application's access control, which the studio's roles enforce.
134
+ - An import recomputes each bundle's and each prompt's content version, and refuses one whose content
135
+ changed after export.
136
+ - A bundle names tools, but `bindTools()` binds only implementations the application supplies, so
137
+ a bundle cannot add code.
138
+ - Promotion runs the label's gates, an experiment over the exact version among them.
139
+ - Labels move by compare-and-set, so a promotion decided on a stale version is refused.
140
+
141
+ Finding, fixed in 2.4: **the file store's compare-and-set could lose a move.** Two label moves at
142
+ once in one process could both pass the check, and the later write erased the earlier one. Label
143
+ writes to one file are now serialized across the process. Proof: two hubs whose writes are held
144
+ until both arrive race for one label on every backend, and on the file store the earlier code
145
+ loses a move every time. Several processes promoting the same label at once still need the Redis or
146
+ Postgres store, as the prompts guide says.
147
+
148
+ ### Insights
149
+
150
+ Trust boundary: runs and their errors carry user data, and a model proposes fixes.
151
+ - An error signature replaces numbers, quoted values, URLs, ids, and long hexadecimal strings, so a
152
+ cluster's summary does not carry a customer's data.
153
+ - A model's proposed fix is parsed as data, evaluated on a dataset, and never applied without a
154
+ person. A promotion still runs the label's gates.
155
+ - A proposal id is checked against a strict pattern before it becomes a file name.
156
+
157
+ Findings: none.
158
+
159
+ ### Tenant limits and the worker queue
160
+
161
+ Trust boundary: a tenant is whoever the application's `authenticate()` says, never a header the
162
+ server trusts by itself.
163
+ - Concurrency, rate, and budget limits are kept in a shared store, so several replicas enforce one
164
+ limit. Admission and release are tested by ten racing workers.
165
+ - A run is released exactly once, however it ends.
166
+ - The queue claims an operation under a lease in one atomic step on every store. A lost lease
167
+ cancels the run's signal, and a crash at any durable boundary resumes to the same state.
168
+
169
+ Findings: none.
170
+
171
+ ### Deployments (reviewed; still experimental)
172
+
173
+ Findings:
174
+ - **Fixed in 2.4: one caller's ratings could decide a canary.** A canary was judged on feedback
175
+ entries, not on runs. A hundred low ratings of one run met the minimum sample alone, and could
176
+ roll back a canary for everyone, or keep a bad one alive. Each run now counts once per feedback
177
+ key, as the mean of its ratings.
178
+ - **Fixed in 2.4: two changes at once on one replica could lose one**, such as a guard's rollback
179
+ 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.