nexus-ai-pro 2.6.0 → 2.8.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 (257) hide show
  1. package/API_STABILITY.md +137 -3
  2. package/CHANGELOG.md +251 -1
  3. package/README.md +15 -5
  4. package/SECURITY.md +41 -1
  5. package/dist/agent/create-agent.js +16 -3
  6. package/dist/agent/glob.d.ts +24 -0
  7. package/dist/agent/glob.js +72 -0
  8. package/dist/agent/index.d.ts +1 -0
  9. package/dist/agent/index.js +1 -0
  10. package/dist/agent/interpreter.d.ts +125 -0
  11. package/dist/agent/interpreter.js +270 -0
  12. package/dist/agent/sandbox-container.d.ts +48 -0
  13. package/dist/agent/sandbox-container.js +291 -0
  14. package/dist/agent/sandbox.d.ts +50 -1
  15. package/dist/agent/sandbox.js +155 -2
  16. package/dist/agent/tool-search.d.ts +39 -0
  17. package/dist/agent/tool-search.js +113 -0
  18. package/dist/agent/tool.d.ts +2 -0
  19. package/dist/agent/tool.js +1 -0
  20. package/dist/batch/manager.js +16 -33
  21. package/dist/batch/openai-client.d.ts +83 -0
  22. package/dist/batch/openai-client.js +120 -0
  23. package/dist/batch/openai.d.ts +12 -21
  24. package/dist/batch/openai.js +33 -125
  25. package/dist/batch/poll.d.ts +18 -0
  26. package/dist/batch/poll.js +29 -0
  27. package/dist/cli/models.d.ts +36 -0
  28. package/dist/cli/models.js +170 -0
  29. package/dist/cli.js +11 -1
  30. package/dist/context/window.js +4 -0
  31. package/dist/deep-agent/helpers.d.ts +57 -0
  32. package/dist/deep-agent/helpers.js +235 -0
  33. package/dist/deep-agent/index.d.ts +20 -1
  34. package/dist/deep-agent/index.js +69 -51
  35. package/dist/doctor/index.d.ts +16 -1
  36. package/dist/doctor/index.js +10 -5
  37. package/dist/embeddings/adapters.d.ts +2 -2
  38. package/dist/embeddings/adapters.js +24 -13
  39. package/dist/embeddings/batch-google.d.ts +40 -0
  40. package/dist/embeddings/batch-google.js +128 -0
  41. package/dist/embeddings/batch-mock.d.ts +45 -0
  42. package/dist/embeddings/batch-mock.js +71 -0
  43. package/dist/embeddings/batch-openai.d.ts +23 -0
  44. package/dist/embeddings/batch-openai.js +55 -0
  45. package/dist/embeddings/batch.d.ts +230 -0
  46. package/dist/embeddings/batch.js +282 -0
  47. package/dist/embeddings/cache.d.ts +81 -0
  48. package/dist/embeddings/cache.js +116 -0
  49. package/dist/embeddings/engine.d.ts +7 -0
  50. package/dist/embeddings/engine.js +110 -68
  51. package/dist/embeddings/google-wire.d.ts +32 -0
  52. package/dist/embeddings/google-wire.js +33 -0
  53. package/dist/embeddings/index.d.ts +1 -1
  54. package/dist/embeddings/inputs.d.ts +30 -0
  55. package/dist/embeddings/inputs.js +103 -0
  56. package/dist/embeddings/mock.d.ts +7 -1
  57. package/dist/embeddings/mock.js +8 -2
  58. package/dist/embeddings/models.js +21 -0
  59. package/dist/evaluate/generate.d.ts +171 -0
  60. package/dist/evaluate/generate.js +266 -0
  61. package/dist/evaluate/index.d.ts +1 -1
  62. package/dist/evaluate/index.js +1 -1
  63. package/dist/evaluate/online.d.ts +114 -0
  64. package/dist/evaluate/online.js +261 -0
  65. package/dist/evaluate/run.d.ts +28 -0
  66. package/dist/evaluate/run.js +56 -5
  67. package/dist/graph/graph.d.ts +7 -1
  68. package/dist/graph/graph.js +12 -1
  69. package/dist/images/asset-stores.d.ts +47 -1
  70. package/dist/images/asset-stores.js +135 -2
  71. package/dist/images/asset-support.d.ts +94 -1
  72. package/dist/images/asset-support.js +159 -3
  73. package/dist/images/assets.d.ts +10 -3
  74. package/dist/images/assets.js +15 -2
  75. package/dist/media/index.d.ts +244 -0
  76. package/dist/media/index.js +262 -0
  77. package/dist/media/mistral.d.ts +34 -0
  78. package/dist/media/mistral.js +90 -0
  79. package/dist/media/mock.d.ts +36 -0
  80. package/dist/media/mock.js +45 -0
  81. package/dist/media/rag.d.ts +141 -0
  82. package/dist/media/rag.js +198 -0
  83. package/dist/models/data.d.ts +87 -0
  84. package/dist/models/data.js +235 -0
  85. package/dist/models/registry.d.ts +2 -1
  86. package/dist/models/registry.js +3 -2
  87. package/dist/operations/runner.js +1 -1
  88. package/dist/postgres/embedding-cache.d.ts +29 -0
  89. package/dist/postgres/embedding-cache.js +58 -0
  90. package/dist/providers/anthropic.d.ts +6 -0
  91. package/dist/providers/anthropic.js +36 -9
  92. package/dist/providers/content.d.ts +33 -1
  93. package/dist/providers/content.js +62 -0
  94. package/dist/providers/google.js +14 -5
  95. package/dist/providers/openai.d.ts +8 -4
  96. package/dist/providers/openai.js +75 -34
  97. package/dist/rag/chroma.d.ts +13 -1
  98. package/dist/rag/chroma.js +36 -13
  99. package/dist/rag/pinecone.js +2 -2
  100. package/dist/rag/pipeline.d.ts +17 -2
  101. package/dist/rag/pipeline.js +95 -14
  102. package/dist/rag/qdrant.js +2 -2
  103. package/dist/rag/redis.d.ts +2 -1
  104. package/dist/rag/redis.js +56 -18
  105. package/dist/rag/vector-helpers.d.ts +5 -0
  106. package/dist/rag/vector-helpers.js +7 -0
  107. package/dist/rag/weaviate.d.ts +15 -1
  108. package/dist/rag/weaviate.js +75 -12
  109. package/dist/runtime/index.d.ts +1 -1
  110. package/dist/security/index.d.ts +1 -1
  111. package/dist/security/index.js +1 -1
  112. package/dist/security/injection-detector.d.ts +18 -0
  113. package/dist/security/injection-detector.js +39 -4
  114. package/dist/security/schema-validator.js +11 -2
  115. package/dist/sqlite/embedding-cache.d.ts +34 -0
  116. package/dist/sqlite/embedding-cache.js +61 -0
  117. package/dist/types/config.d.ts +5 -0
  118. package/dist/types/embeddings.d.ts +87 -5
  119. package/dist/types/images.d.ts +2 -2
  120. package/dist/types/lifecycle.d.ts +1 -1
  121. package/dist/types/messages.d.ts +57 -3
  122. package/dist/utils/redis-command.d.ts +15 -0
  123. package/dist/utils/redis-command.js +5 -0
  124. package/dist/utils/sha256.d.ts +9 -0
  125. package/dist/utils/sha256.js +6 -0
  126. package/dist/utils/tokenizer.js +4 -0
  127. package/dist/utils/vector-bytes.d.ts +11 -0
  128. package/dist/utils/vector-bytes.js +18 -0
  129. package/dist/version.d.ts +1 -1
  130. package/dist/version.js +1 -1
  131. package/dist-cjs/agent/create-agent.js +15 -2
  132. package/dist-cjs/agent/glob.d.ts +24 -0
  133. package/dist-cjs/agent/glob.js +77 -0
  134. package/dist-cjs/agent/index.d.ts +1 -0
  135. package/dist-cjs/agent/index.js +5 -1
  136. package/dist-cjs/agent/interpreter.d.ts +125 -0
  137. package/dist-cjs/agent/interpreter.js +308 -0
  138. package/dist-cjs/agent/sandbox-container.d.ts +48 -0
  139. package/dist-cjs/agent/sandbox-container.js +327 -0
  140. package/dist-cjs/agent/sandbox.d.ts +50 -1
  141. package/dist-cjs/agent/sandbox.js +155 -2
  142. package/dist-cjs/agent/tool-search.d.ts +39 -0
  143. package/dist-cjs/agent/tool-search.js +118 -0
  144. package/dist-cjs/agent/tool.d.ts +2 -0
  145. package/dist-cjs/agent/tool.js +1 -0
  146. package/dist-cjs/batch/manager.js +16 -33
  147. package/dist-cjs/batch/openai-client.d.ts +83 -0
  148. package/dist-cjs/batch/openai-client.js +125 -0
  149. package/dist-cjs/batch/openai.d.ts +12 -21
  150. package/dist-cjs/batch/openai.js +32 -124
  151. package/dist-cjs/batch/poll.d.ts +18 -0
  152. package/dist-cjs/batch/poll.js +32 -0
  153. package/dist-cjs/cli/models.d.ts +36 -0
  154. package/dist-cjs/cli/models.js +175 -0
  155. package/dist-cjs/context/window.js +4 -0
  156. package/dist-cjs/deep-agent/helpers.d.ts +57 -0
  157. package/dist-cjs/deep-agent/helpers.js +238 -0
  158. package/dist-cjs/deep-agent/index.d.ts +20 -1
  159. package/dist-cjs/deep-agent/index.js +68 -50
  160. package/dist-cjs/doctor/index.d.ts +16 -1
  161. package/dist-cjs/doctor/index.js +10 -5
  162. package/dist-cjs/embeddings/adapters.d.ts +2 -2
  163. package/dist-cjs/embeddings/adapters.js +23 -12
  164. package/dist-cjs/embeddings/batch-google.d.ts +40 -0
  165. package/dist-cjs/embeddings/batch-google.js +132 -0
  166. package/dist-cjs/embeddings/batch-mock.d.ts +45 -0
  167. package/dist-cjs/embeddings/batch-mock.js +75 -0
  168. package/dist-cjs/embeddings/batch-openai.d.ts +23 -0
  169. package/dist-cjs/embeddings/batch-openai.js +59 -0
  170. package/dist-cjs/embeddings/batch.d.ts +230 -0
  171. package/dist-cjs/embeddings/batch.js +320 -0
  172. package/dist-cjs/embeddings/cache.d.ts +81 -0
  173. package/dist-cjs/embeddings/cache.js +125 -0
  174. package/dist-cjs/embeddings/engine.d.ts +7 -0
  175. package/dist-cjs/embeddings/engine.js +143 -68
  176. package/dist-cjs/embeddings/google-wire.d.ts +32 -0
  177. package/dist-cjs/embeddings/google-wire.js +38 -0
  178. package/dist-cjs/embeddings/index.d.ts +1 -1
  179. package/dist-cjs/embeddings/inputs.d.ts +30 -0
  180. package/dist-cjs/embeddings/inputs.js +141 -0
  181. package/dist-cjs/embeddings/mock.d.ts +7 -1
  182. package/dist-cjs/embeddings/mock.js +8 -2
  183. package/dist-cjs/embeddings/models.js +21 -0
  184. package/dist-cjs/evaluate/generate.d.ts +171 -0
  185. package/dist-cjs/evaluate/generate.js +272 -0
  186. package/dist-cjs/evaluate/index.d.ts +1 -1
  187. package/dist-cjs/evaluate/index.js +2 -1
  188. package/dist-cjs/evaluate/online.d.ts +114 -0
  189. package/dist-cjs/evaluate/online.js +264 -0
  190. package/dist-cjs/evaluate/run.d.ts +28 -0
  191. package/dist-cjs/evaluate/run.js +58 -5
  192. package/dist-cjs/graph/graph.d.ts +7 -1
  193. package/dist-cjs/graph/graph.js +13 -1
  194. package/dist-cjs/images/asset-stores.d.ts +47 -1
  195. package/dist-cjs/images/asset-stores.js +133 -0
  196. package/dist-cjs/images/asset-support.d.ts +94 -1
  197. package/dist-cjs/images/asset-support.js +169 -3
  198. package/dist-cjs/images/assets.d.ts +10 -3
  199. package/dist-cjs/images/assets.js +17 -1
  200. package/dist-cjs/media/index.d.ts +244 -0
  201. package/dist-cjs/media/index.js +269 -0
  202. package/dist-cjs/media/mistral.d.ts +34 -0
  203. package/dist-cjs/media/mistral.js +94 -0
  204. package/dist-cjs/media/mock.d.ts +36 -0
  205. package/dist-cjs/media/mock.js +49 -0
  206. package/dist-cjs/media/rag.d.ts +141 -0
  207. package/dist-cjs/media/rag.js +202 -0
  208. package/dist-cjs/models/data.d.ts +87 -0
  209. package/dist-cjs/models/data.js +245 -0
  210. package/dist-cjs/models/registry.d.ts +2 -1
  211. package/dist-cjs/models/registry.js +3 -2
  212. package/dist-cjs/operations/runner.js +1 -1
  213. package/dist-cjs/postgres/embedding-cache.d.ts +29 -0
  214. package/dist-cjs/postgres/embedding-cache.js +96 -0
  215. package/dist-cjs/providers/anthropic.d.ts +6 -0
  216. package/dist-cjs/providers/anthropic.js +35 -8
  217. package/dist-cjs/providers/content.d.ts +33 -1
  218. package/dist-cjs/providers/content.js +69 -0
  219. package/dist-cjs/providers/google.js +13 -4
  220. package/dist-cjs/providers/openai.d.ts +8 -4
  221. package/dist-cjs/providers/openai.js +74 -33
  222. package/dist-cjs/rag/chroma.d.ts +13 -1
  223. package/dist-cjs/rag/chroma.js +35 -12
  224. package/dist-cjs/rag/pinecone.js +1 -1
  225. package/dist-cjs/rag/pipeline.d.ts +17 -2
  226. package/dist-cjs/rag/pipeline.js +95 -14
  227. package/dist-cjs/rag/qdrant.js +1 -1
  228. package/dist-cjs/rag/redis.d.ts +2 -1
  229. package/dist-cjs/rag/redis.js +55 -17
  230. package/dist-cjs/rag/vector-helpers.d.ts +5 -0
  231. package/dist-cjs/rag/vector-helpers.js +8 -0
  232. package/dist-cjs/rag/weaviate.d.ts +15 -1
  233. package/dist-cjs/rag/weaviate.js +74 -11
  234. package/dist-cjs/runtime/index.d.ts +1 -1
  235. package/dist-cjs/security/index.d.ts +1 -1
  236. package/dist-cjs/security/index.js +2 -1
  237. package/dist-cjs/security/injection-detector.d.ts +18 -0
  238. package/dist-cjs/security/injection-detector.js +40 -4
  239. package/dist-cjs/security/schema-validator.js +11 -2
  240. package/dist-cjs/sqlite/embedding-cache.d.ts +34 -0
  241. package/dist-cjs/sqlite/embedding-cache.js +99 -0
  242. package/dist-cjs/types/config.d.ts +5 -0
  243. package/dist-cjs/types/embeddings.d.ts +87 -5
  244. package/dist-cjs/types/images.d.ts +2 -2
  245. package/dist-cjs/types/lifecycle.d.ts +1 -1
  246. package/dist-cjs/types/messages.d.ts +57 -3
  247. package/dist-cjs/utils/redis-command.d.ts +15 -0
  248. package/dist-cjs/utils/redis-command.js +8 -0
  249. package/dist-cjs/utils/sha256.d.ts +9 -0
  250. package/dist-cjs/utils/sha256.js +7 -0
  251. package/dist-cjs/utils/tokenizer.js +4 -0
  252. package/dist-cjs/utils/vector-bytes.d.ts +11 -0
  253. package/dist-cjs/utils/vector-bytes.js +22 -0
  254. package/dist-cjs/version.d.ts +1 -1
  255. package/dist-cjs/version.js +1 -1
  256. package/llms.txt +2 -1
  257. package/package.json +214 -4
package/API_STABILITY.md CHANGED
@@ -41,6 +41,139 @@ 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.8 stage (2.8.0)
45
+
46
+ Everything 2.8 adds is stable from the start and additive, unless this section says otherwise.
47
+
48
+ New and stable:
49
+ - `ChromaVectorStoreOptions.filterFields`;
50
+ - `EmbeddingCacheStore`, `EmbeddingCacheEntry`, and `EmbeddingCacheConfig` with its `store`;
51
+ `nexus-ai-pro/embeddings/cache`: `embeddingCacheKey()` and `EmbeddingCacheContext`, `packVector()`
52
+ and `unpackVector()`, `MemoryEmbeddingCache` and its options, `RedisEmbeddingCache` with its client
53
+ and options, and `tieredEmbeddingCache()`. A key's derivation and a vector's packed layout are part
54
+ of the promise, so a cache one release wrote is read by the next;
55
+ - `EmbeddingModality`, `EmbeddingPart`, and `EmbeddingParts` as `EmbeddingInput`;
56
+ `EmbeddingProviderRequest.contents` with `EmbeddingProviderPart`, `EmbeddingProviderTextPart`, and
57
+ `EmbeddingProviderMediaPart`; `inputModalities` on `EmbeddingProviderCapabilities` and
58
+ `EmbeddingModelCapabilities`, and `costPerImage`; `MockEmbeddingProviderOptions.describeMedia`. An
59
+ adapter that declares no `inputModalities` is sent text only, as before;
60
+ - `nexus-ai-pro/embeddings/batch`: `EmbeddingBatchManager` and its config and runtime, its request,
61
+ item, result, and output types, the `EmbeddingBatchProvider` contract with its info, capabilities,
62
+ request, and item, `batchEmbedder()` with `BatchEmbedder` and its options; the OpenAI and mock
63
+ adapters on `/openai` and `/mock`. `GoogleEmbeddingBatchProvider` on `/google` is experimental
64
+ until a live run is recorded;
65
+ - on `nexus-ai-pro/images/assets`: `AssetStore.putStream()` and `getStream()`, both optional;
66
+ `putAssetStream()` and `readAssetStream()`, `DEFAULT_BUFFERED_ASSET_BYTES`, `AssetByteSource`,
67
+ `StreamAssetInput`, `AssetByteRange`, `AssetStreamOptions`, `StreamAssetDescriptor`, and
68
+ `PutAssetStreamOptions`; `S3LikeClient.putObjectStream()` and `getObjectStream()`, both optional.
69
+ The images family stays experimental, and these with it;
70
+ - `nexus-ai-pro/models/data`: `RegistryData`, `REGISTRY_DATA_FORMAT`, `checkRegistryData()`,
71
+ `parseRegistryData()` with `RegistryDataError`, `bundledRegistryData()`, `withRegistryData()`,
72
+ `diffRegistryData()` with `RegistryDiff` and `RegistryFieldChange`, and `registryDataAgeDays()`;
73
+ `ModelRegistryConfig.verifiedAt` and `DoctorOptions.models`; the commands `nexus models export`,
74
+ `check`, and `refresh`. The `nexus-registry/1` format is read by every 2.x release;
75
+ - `nexus-ai-pro/media`, `/media/mistral`, `/media/mock`, and `/media/rag` are new and experimental: media
76
+ understanding graduates by the checklist, once a vision model and the OCR service pass its
77
+ conformance suite live. `OperationFamily` gains `media`, and `AssetProvenance.operation` gains
78
+ `upload`, both stable;
79
+ - `IngestionPipelineOptions.batchEmbed` and `batchesInFlight`, and `IngestionReport.batches` and
80
+ `batchesRecovered`. `IngestionPipelineOptions.embed` is optional when `batchEmbed` is given;
81
+ - `nexus-ai-pro/postgres/embedding-cache`: `PostgresEmbeddingCache`, its options, and
82
+ `embeddingCacheMigrations()`; `nexus-ai-pro/sqlite/embedding-cache`: `SqliteEmbeddingCache`, its
83
+ options, and `sqliteEmbeddingCacheMigrations()`.
84
+
85
+ Changed, each to keep a promise the surface already made:
86
+ - `OpenAIBatchProvider` refuses `endpoint: '/v1/embeddings'` with a `BatchCapabilityError`; it sent
87
+ chat bodies there, which never embedded anything.
88
+ - The Qdrant, Redis, Pinecone, Weaviate, and Chroma stores refuse a `topK` that is not a positive
89
+ integer with a `RangeError`. An absent `topK`, or 0, still means 5.
90
+ - A replacement in Chroma removes the metadata fields the new version lacks; before, Chroma merged
91
+ them. A Weaviate filter is typed by the collection's schema, so a value of another type than its
92
+ property matches nothing instead of failing the search.
93
+
94
+ ### Graduation
95
+
96
+ The Qdrant, Redis, Weaviate, and Chroma vector stores leave experimental status, by the same
97
+ checklist as in 2.4. The live evidence comes from the scheduled live workflow, which runs every
98
+ check against each server; the stand-in evidence runs in CI on every change.
99
+
100
+ **The remote vector stores** (`QdrantVectorStore`, `RedisVectorStore`, `WeaviateVectorStore`,
101
+ `ChromaVectorStore`):
102
+
103
+ | Requirement | Evidence |
104
+ | --- | --- |
105
+ | API stability | Covered by the 2.x compatibility rules, with no planned breaking change |
106
+ | Conformance | The vector store contract and typed filters, against each real server, Redis over RESP3 and RESP2: `tests/vector-stores.live.ts`; against stand-ins that fail as the servers do: `tests/vector-stores.test.ts` |
107
+ | Crash recovery | n/a: a store keeps no run state, and a reload upserts the same ids |
108
+ | Concurrency | Ten writers replacing the same twenty ids at once leave each id as one writer's version, found only by that writer's filters, live and against stand-ins that interleave as a network does |
109
+ | Load | 2,000 chunks written, then 200 searches within a p95 budget of 200 ms on each server: `tests/vector-stores.live.ts` |
110
+ | Tenant isolation | Each store under `tenantVectorStore()`, live and in `tests/tenant-isolation.test.ts` |
111
+ | Security | [Review](./SECURITY.md#the-qdrant-redis-weaviate-and-chroma-vector-stores): four findings fixed, two documented limitations |
112
+ | Upgrade | What the published 2.7.0 wrote is read, filtered, replaced, and deleted by this release, live and in `tests/upgrade-remote-stores.test.ts` |
113
+ | Soak | 1,000 write, search, and delete cycles on each server, with the heap within 8 MB |
114
+
115
+ Chroma graduates in both modes. Without `filterFields`, two writers replacing one id at once can
116
+ leave a stale filter field, which its guide states; with `filterFields`, a replacement stays whole
117
+ under any race.
118
+
119
+ **Still experimental**, each with what remains:
120
+ - **The Pinecone store.** It passes every check against a stand-in, and the live workflow runs the
121
+ same checks against Pinecone once the repository's Pinecone secret is set.
122
+ - **Images.** Recorded live conformance must pass on all three backends. The live workflow records it
123
+ for each backend whose credential is set.
124
+
125
+ ## 2.7 stage (2.7.0)
126
+
127
+ Everything 2.7 adds is stable from the start and additive.
128
+
129
+ New and stable:
130
+ - on `nexus-ai-pro/agent/sandbox`: `Sandbox.glob()`, `grep()`, and `readBytes()`, all optional;
131
+ `SandboxGlobOptions`, `SandboxGrepOptions`, and `SandboxMatch`; `SandboxToolsOptions.maxMediaBytes`;
132
+ and the tool names `glob_files`, `grep_files`, and `read_media`;
133
+ - `DocumentContent` and the `document` member of `ContentPart`, exported from
134
+ `nexus-ai-pro/runtime`; the `base64` source of `AudioContent`;
135
+ - `nexus-ai-pro/agent/sandbox/container`: `containerSandbox()` and `ContainerSandboxOptions`, with
136
+ their defaults: no network, every capability dropped, no new privileges, 512 MB of memory and swap,
137
+ one CPU, and 256 processes;
138
+ - `nexus-ai-pro/agent/interpreter`: `Interpreter`, `InterpreterFunction`, `InterpreterRunOptions`,
139
+ `InterpreterResult`, `InterpreterCall`, `workerInterpreter()` and `WorkerInterpreterOptions`,
140
+ `codeTool()` and `CodeToolOptions` with the tool name `run_code`, and `runInterpreterConformance()`
141
+ with `InterpreterConformanceReport`; `DeepAgentOptions.interpreter`;
142
+ - `DeepAgentOptions.createHelpers` and `maxBackgroundHelpers`, `DeepHelperLimits`, the `background`
143
+ and `instructions` arguments of `delegate`, the tool name `collect_helper`, the helper thread id
144
+ `<thread>/helper/<name>@<call id>`, and the helper namespace `<name>@<call id>`;
145
+ - `ToolContext.forward`;
146
+ - `ToolDefinition.deferred` and `tool({ deferred })`; `ToolReferenceContent` and the `tool_reference`
147
+ member of `ContentPart`, exported from `nexus-ai-pro/runtime`; on `nexus-ai-pro/agent`,
148
+ `toolSearchTool()`, `ToolSearchOptions`, `searchTools()`, `ToolSearchHit`, and `TOOL_SEARCH_NAME`
149
+ with the tool name `search_tools`.
150
+
151
+ - `nexus-ai-pro/evaluate/generate`: `syntheticDataset()` with `SyntheticDatasetOptions`,
152
+ `SyntheticDocument`, `SyntheticPassage`, `SyntheticDraft`, `SyntheticCitation`, `SyntheticModelClient`,
153
+ and `SyntheticDatasetResult`; `adversarialDataset()` with `AdversarialDatasetOptions`,
154
+ `AdversarialInputs`, `AdversarialExpected`, `AttackCarrier`, and `AttackDisguise`; `resistsAttack()`;
155
+ `promoteIssue()` with `PromoteIssueOptions`;
156
+ - on `nexus-ai-pro/security`: `attackSeeds()`, `AttackSeed`, and `AttackCategory`;
157
+ - `nexus-ai-pro/evaluate/online`: `onlineEvaluationWorker()`, `OnlineEvaluationWorker`,
158
+ `OnlineEvaluationWorkerOptions`, `OnlineEvaluationWorkerReport`, and `OnlineSchedulePass`; the
159
+ operation kind `online-evaluation:<source>` and the score mark `value.evaluation`;
160
+ - `EvaluateOptions.budget` and `EvaluationBudgetError` on `nexus-ai-pro/evaluate`.
161
+
162
+ An agent with no deferred tool offers exactly the tools it did before. Adversarial example ids and
163
+ canaries are stable across runs, so experiments over the same options compare.
164
+
165
+ A graph run's first checkpoint carries the run's `metadata`, as every later checkpoint already did.
166
+
167
+ `sandboxTools()` offers the new tools only for a sandbox that implements the methods behind them, so
168
+ a sandbox of the application's own offers the same tools as before. A deep agent over
169
+ `processSandbox()` gets the three new tools and a line of its system prompt naming them.
170
+
171
+ On OpenAI, a tool message whose media is not only images is followed by a user message that starts
172
+ `Files the tools returned:`; one with only images keeps `Images the tools returned:`.
173
+
174
+ `ContentPart` and `AudioContent['source']` gain members, as the compatibility rules allow in a minor
175
+ release. Code that switches over either exhaustively should handle the new ones.
176
+
44
177
  ## 2.6 stage (2.6.0)
45
178
 
46
179
  Everything 2.6 adds is stable from the start and additive.
@@ -944,7 +1077,8 @@ accounts are experimental in production readiness. Their contracts follow the 1.
944
1077
  ## Retrieval breadth stage (1.22.0)
945
1078
 
946
1079
  The loaders, the SQLite vector store, the retrievers, and the MCP registry graduated in 2.4; see the
947
- 2.4 stage for their evidence. The Redis, Pinecone, Weaviate, and Chroma stores remain experimental.
1080
+ 2.4 stage for their evidence. The Redis, Weaviate, and Chroma stores graduated in 2.8; see the 2.8
1081
+ stage. The Pinecone store remains experimental.
948
1082
 
949
1083
  The loaders (`nexus-ai-pro/loaders/*`), the Redis, Pinecone, Weaviate, Chroma, and SQLite vector
950
1084
  stores, the retrievers (`nexus-ai-pro/rag/retrievers`), and the MCP registry
@@ -976,8 +1110,8 @@ are experimental in production readiness. Their contracts follow the 1.x rules.
976
1110
 
977
1111
  ## Retrieval stores stage (1.20.0)
978
1112
 
979
- `PostgresVectorStore` graduated in 2.4; see the 2.4 stage for its evidence. `QdrantVectorStore`
980
- remains experimental.
1113
+ `PostgresVectorStore` graduated in 2.4, and `QdrantVectorStore` in 2.8; see those stages for their
1114
+ evidence.
981
1115
 
982
1116
  `PostgresVectorStore` (`nexus-ai-pro/postgres/vectors`) and `QdrantVectorStore` (`nexus-ai-pro/rag/qdrant`)
983
1117
  are experimental in production readiness. The `VectorStore` contract they share with
package/CHANGELOG.md CHANGED
@@ -4,6 +4,254 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [2.8.0] - 2026-10-14
8
+
9
+ Retrieval at production scale, and media that is understood.
10
+
11
+ ### Added
12
+
13
+ - **Graduation.** The Qdrant, Redis, Weaviate, and Chroma vector stores leave experimental status.
14
+ Each passes its contract and the graduation checklist against the real server in the scheduled
15
+ live workflow: ten writers racing on the same ids, what 2.7.0 wrote read by this release, tenant
16
+ isolation, a load budget, and a soak. The evidence is in `API_STABILITY.md`, and the threat review
17
+ in `SECURITY.md`. The Pinecone store passes the same checks against a stand-in, and graduates once
18
+ the live workflow runs them against Pinecone.
19
+ - **A shared embedding cache.** `embeddings.cache.store` takes an `EmbeddingCacheStore`, which
20
+ reads every input of a request in one `getMany()` and writes what was embedded in one `setMany()`.
21
+ Keys are SHA-256 hashes from `embeddingCacheKey()`, so no text is a key, and vectors are packed
22
+ float32. On `nexus-ai-pro/embeddings/cache`: `MemoryEmbeddingCache`, `RedisEmbeddingCache` (one
23
+ `MGET` and one script a batch, or a command a key on Redis Cluster), `tieredEmbeddingCache()`, and
24
+ `packVector()` with `unpackVector()`. `PostgresEmbeddingCache` is on
25
+ `nexus-ai-pro/postgres/embedding-cache`, and `SqliteEmbeddingCache` on
26
+ `nexus-ai-pro/sqlite/embedding-cache`, each with its versioned migrations and `purgeExpired()`.
27
+ - **Images, audio, and documents as embedding input.** An `EmbeddingInput` may hold `EmbeddingParts`:
28
+ the text, image, audio, and document parts a message takes, embedded as one vector in the same
29
+ space as text. A model takes the `EmbeddingModality` kinds its registry entry or adapter declares in
30
+ `inputModalities`, and anything else is refused before any call; a fallback that cannot take the
31
+ media is skipped. An adapter receives each input's parts in `EmbeddingProviderRequest.contents`,
32
+ media already resolved to base64 or a URL. Google sends media inline or by a Files API URI, and
33
+ writes `gemini-embedding-2`'s task as the text prefix that model reads instead of a task type; Cohere
34
+ sends text and data-URI images as `inputs`. `gemini-embedding-2` joins the registry, with
35
+ `costPerImage`, and `embed-v4.0` takes images. `MockEmbeddingProviderOptions.describeMedia` puts a
36
+ medium and its caption in one space for tests.
37
+ - **A batch tier for embeddings**, on the new `nexus-ai-pro/embeddings/batch`: `EmbeddingBatchManager`
38
+ submits, polls, collects, and prices at the provider's batch rate, usually half, with `submit()`,
39
+ `start()`, `resume()`, `status()`, and `cancel()`. Adapters: `OpenAIEmbeddingBatchProvider` on
40
+ `/openai`, `GoogleEmbeddingBatchProvider` (`asyncBatchEmbedContent`, media included) on `/google`,
41
+ and `MockEmbeddingBatchProvider` on `/mock`. `batchEmbedder()` gives an ingestion pipeline a batch
42
+ tier.
43
+ - **Ingestion through a batch tier.** `IngestionPipelineOptions.batchEmbed` takes a batch tier in
44
+ place of `embed`. Chunks are gathered into windows, each one provider batch, with `batchesInFlight`
45
+ (4) outstanding, and written in order. A durable run records each batch the moment it is
46
+ submitted, and a retry collects it rather than submitting it again. `IngestionReport` counts
47
+ `batches` and `batchesRecovered`.
48
+ - **Streaming asset stores.** `AssetStore` gains the optional `putStream()` and `getStream()`, and
49
+ `putAssetStream()` with `readAssetStream()` stream through any store, falling back to `put()` and
50
+ `get()`. The filesystem and S3 stores write a stream a chunk at a time, checking its size limit and
51
+ checksum as it flows and leaving nothing behind when either fails, and read it back whole or by an
52
+ `AssetByteRange`. `S3LikeClient` takes an optional `putObjectStream()` and `getObjectStream()`.
53
+ - **Registry data on its own**, on the new `nexus-ai-pro/models/data`: the model and embedding
54
+ registries as a `RegistryData` file (`nexus-registry/1`) the application supplies.
55
+ `checkRegistryData()` and `parseRegistryData()` name every problem with its path,
56
+ `withRegistryData()` layers a file under the application's own entries and over the bundled ones,
57
+ `bundledRegistryData()` exports the bundled data, and `diffRegistryData()` compares two files.
58
+ `models.verifiedAt` measures freshness from the file, in `checkRegistryFreshness()` and `nexus
59
+ doctor`, which takes the configuration as `models`. `nexus models export`, `check`, and `refresh`
60
+ work with the file; `refresh` compares it with each provider's own model list.
61
+ - **Media understanding**, on the new `nexus-ai-pro/media`: `MediaManager` reads a document's or an
62
+ image's text page by page, describes an image, and answers a question about media, routing each
63
+ task to the provider named, its default, or the first that can, and running each call as one
64
+ `media` operation of the client's lifecycle. `modelMediaProvider()` does all three through any
65
+ vision model and the client's own `complete()`; `MistralOcrProvider`, on `/mistral`, reads PDFs,
66
+ slides, and images into Markdown pages with their sizes and picture boxes; `MockMediaProvider`, on
67
+ `/mock`, stands in for tests. `runMediaConformance()` checks any provider. `OperationFamily` gains
68
+ `media`.
69
+ - **Media search and RAG**, on the new `nexus-ai-pro/media/rag`: `createMediaIndex()` keeps media in
70
+ any vector store as their pages' text, captions, and, on a model that takes them, their own
71
+ vectors, and keeps each medium in an asset store, or by its URL, so a hit can hand it back.
72
+ `search()` returns each medium once at its best chunk, `resolve()` returns the medium, and
73
+ `answerFromMedia()` hands the model the retrieved media themselves. `AssetProvenance.operation`
74
+ takes `upload`, for media an application brings in.
75
+ - `ChromaVectorStoreOptions.filterFields`: the fields a search can filter on. Every write then sets
76
+ each declared field, or removes it, so a replacement needs no read and stays whole under any race;
77
+ a filter on another field is refused.
78
+
79
+ ### Changed
80
+
81
+ - The Qdrant, Redis, Pinecone, Weaviate, and Chroma stores refuse a `topK` that is not a positive
82
+ integer, before any request. An absent `topK`, or 0, still means 5.
83
+ - `RedisVectorStore` replaces a chunk with one atomic script instead of a delete and a write.
84
+ - An embedding `cache.adapter` is asked for every input at once, where it was asked for one input
85
+ after another: a 2,000-text request made 2,000 sequential reads. Its keys are unchanged, so
86
+ processes of this release and the last share entries during a rollout.
87
+
88
+ ### Fixed
89
+
90
+ - `RedisVectorStore` failed on every command with an ioredis client, which also has a
91
+ `sendCommand()` that takes its own command object; it now uses `call()` whenever a client has it.
92
+ It also reads a search reply in RESP3, which ioredis 6 speaks by default, as well as RESP2.
93
+ - `WeaviateVectorStore` failed a search whose filter value had another type than its property, such
94
+ as `'3'` on a number. It now chooses the operand from the collection's schema, read once, so such a
95
+ filter matches nothing, as in every other store, and an `int` property gets `valueInt`.
96
+ - A `topK` from a request could change a Weaviate or Redis query, since both placed it in the query
97
+ text.
98
+ - A replacement could keep a filter field the new version lacks, so a filter, such as a tenant's,
99
+ still found the chunk by its old value. In Chroma, which merges metadata, it happened on every
100
+ replacement, and kept a replaced `source` too; in Redis, when two writers raced.
101
+ - `BatchManager` submitted a batch again when a worker died after submitting it and before collecting
102
+ it, so the provider ran, and billed, both. A submission now records its batch the moment it exists,
103
+ and a later attempt collects that one.
104
+ - `OpenAIBatchProvider` read only the output file, so every request that failed was missing from the
105
+ results; it now reads the error file too. It also refuses the `/v1/embeddings` endpoint its options
106
+ offered, since it sent chat bodies there: embeddings are batched by `OpenAIEmbeddingBatchProvider`.
107
+ - After two writers replaced one object, Weaviate's filter index could return it for a value it no
108
+ longer had. Every filtered hit is now checked against the object's own fields, and the store asks
109
+ for more when a stale entry took a place.
110
+
111
+ ### Proof
112
+
113
+ - `npm run test:vectors:live` runs the contract and the graduation checklist against each server,
114
+ Redis over RESP3 and RESP2, and passes 35 of 35 checks locally against Qdrant, Weaviate 1.40.1,
115
+ Chroma 1.0, and Redis Stack with ioredis 6.0.0. The scheduled workflow keeps its measurements.
116
+ - The stand-ins now fail as the servers do: an ioredis-shaped client, RESP3 replies, Weaviate's typed
117
+ filter errors and stale filter entries, Chroma's merged metadata, and a seeded network delay that
118
+ interleaves racing writers. Each failure above reproduces against them on the previous code.
119
+ - `tests/upgrade-remote-stores.test.ts` reads, with this release, what the published 2.7.0 wrote into
120
+ each store.
121
+ - `tests/media-rag.test.ts`: with stand-in models, answers given the retrieved charts are right on
122
+ every question, and answers from the same hits' text on none.
123
+ - `tests/media.test.ts`: the vision-model, Mistral OCR, and mock providers pass the media conformance
124
+ suite, and the wire shapes each sends are asserted.
125
+ - `tests/registry-data.test.ts`: a price raised tenfold in a supplied file makes the next call cost
126
+ ten times as much, for completions and embeddings alike.
127
+ - `npm run bench:streams`, a step of the release gate: a gigabyte goes through the filesystem and S3
128
+ stores and back while memory grows by less than 128 MB.
129
+ - `tests/embedding-batch.test.ts`: an ingestion of 100,000 chunks through a batch tier dies while it
130
+ collects its third window, and across both attempts each of its ten windows is submitted once;
131
+ without the recorded batches, two are submitted twice.
132
+ - `tests/embeddings-media.test.ts`: a text query finds the image it describes through one vector
133
+ space, and media never reaches a model that takes text only.
134
+ - `tests/embedding-cache.test.ts`: a second process embeds a 2,000-text batch with no provider call
135
+ and one cache read, and every store keeps one contract, which the live suite also runs against
136
+ Redis over RESP3 and RESP2.
137
+
138
+ ## [2.7.0] - 2026-10-12
139
+
140
+ Agents that do more, and evaluation that runs on its own.
141
+
142
+ ### Added
143
+
144
+ - **Workspace search.** A `Sandbox` may implement `glob()`, `grep()`, and `readBytes()`, with
145
+ `SandboxGlobOptions`, `SandboxGrepOptions`, and `SandboxMatch`. `sandboxTools()` then offers
146
+ `glob_files` and `grep_files`, bounded at 500 paths and 200 lines, each declaring the directory it
147
+ reads. `processSandbox()` implements all three: its search follows no links, skips `.git` and
148
+ `node_modules`, walks at most 20,000 entries, and skips files over 1 MB and binary files.
149
+ - **A multimodal workspace.** `read_media` reads an image, audio, or a PDF in the sandbox as content
150
+ the model sees, as base64 so it survives a checkpoint, up to `SandboxToolsOptions.maxMediaBytes`
151
+ (10 MB by default). A deep agent gets all three tools when its sandbox supports them.
152
+ - **Documents and audio reach the model.** The new `DocumentContent` part carries a document, such
153
+ as a PDF, and `AudioContent` takes base64 text. Anthropic receives a document as a document block,
154
+ inside a tool result too; OpenAI chat as a file, OpenAI Responses as an input file, and Google as
155
+ inline data. Audio reaches Google, and OpenAI chat's audio models as `input_audio`. A tool's media
156
+ follows its results on OpenAI and Google, as its images already did. Any other provider, or a
157
+ source it cannot read, receives one line naming the part.
158
+ - **A container sandbox**, on the new `nexus-ai-pro/agent/sandbox/container`: `containerSandbox()`
159
+ drives the `docker` or `podman` binary, with no dependency, one container per sandbox. By default
160
+ the container has no network, drops every capability, cannot gain privileges, and is held to
161
+ 512 MB, one CPU, and 256 processes; nothing of the host is mounted. A command past its timeout is
162
+ killed inside the container with everything it started. Files, glob, grep, and media work inside
163
+ it. `ContainerSandboxOptions` sets the image, binary, working directory, network, and limits.
164
+ - **An interpreter contract**, on the new `nexus-ai-pro/agent/interpreter`. An `Interpreter` runs a
165
+ snippet that calls the agent's tools as `await tools.<name>(args)`, so many calls take one step.
166
+ `codeTool()` offers it to a model as `run_code`: it declares `code`, and the policy decides each
167
+ call the code makes as it decides the model's own, refusing one it would ask a person about.
168
+ `workerInterpreter()` is the JavaScript reference: a fresh worker per run, a 64 MB heap, a
169
+ 10-second timeout, and caps on calls and logs. It is not a security boundary.
170
+ `runInterpreterConformance()` checks any interpreter. `DeepAgentOptions.interpreter` adds
171
+ `run_code` over the deep agent's own tools, and its default policy then grants code.
172
+ - **Helpers beside the deep agent.**
173
+ - `delegate` takes `background`: it returns a task id at once, and the new `collect_helper` tool
174
+ returns the answer later, waiting up to `wait_seconds`. `maxBackgroundHelpers` (4) bounds how
175
+ many work at once.
176
+ - A background helper keeps checkpoints in the deep agent's checkpointer, on a thread named after
177
+ the call that started it. After a crash, collecting the task, or repeating the call, carries the
178
+ same helper on from its last checkpoint.
179
+ - `createHelpers` lets the agent create a helper from instructions it writes, within
180
+ `DeepHelperLimits`: the tools, model, model calls, and number per thread.
181
+ - Streamed with `subgraphs: true`, a helper's events reach the agent's stream under its namespace,
182
+ such as `tools/researcher@call-7`. `ToolContext.forward` lets any tool do the same for a run it
183
+ starts.
184
+ - **Tool search.** `ToolDefinition.deferred` (and `tool({ deferred })`) loads a tool only once the
185
+ model finds it. An agent with a deferred tool offers `search_tools`, which ranks the deferred tools
186
+ by BM25 over their names, descriptions, and arguments, and answers with `ToolReferenceContent`
187
+ parts. On Claude 4.5 and later every tool is sent, a deferred one with `defer_loading`, and a
188
+ found tool loads through a `tool_reference` block, so the tool list never changes; every other
189
+ model, OpenAI's and Google's included, is sent a deferred tool once it has been found.
190
+ `toolSearchTool()`, `searchTools()`, and `TOOL_SEARCH_NAME` are on `nexus-ai-pro/agent`.
191
+ - **Datasets that grow**, on the new `nexus-ai-pro/evaluate/generate`:
192
+ - `syntheticDataset()` writes examples from documents, from your function or a model. Each keeps
193
+ a citation of the quote that supports it, checked against its passage; unsupported drafts and
194
+ near-duplicate questions are dropped and counted.
195
+ - `adversarialDataset()` grows attacks from the guardrails' own patterns, in the user's message, a
196
+ document, or a tool's result, plain or disguised, each after a canary in the system prompt.
197
+ `resistsAttack()` scores whether the canary stayed in.
198
+ - `promoteIssue()` turns an insights issue's runs into examples that keep their run and the issue.
199
+ - `attackSeeds()` on `nexus-ai-pro/security` returns each injection pattern's example attack.
200
+ - **Online evaluation on workers**, on the new `nexus-ai-pro/evaluate/online`.
201
+ `onlineEvaluationWorker()` samples runs by a stable hash, queues one operation per run and source
202
+ on a shared operation store, stops queuing at `maxQueued`, and scores what it claims within a
203
+ budget ledger. Each score carries a mark naming its operation, so a retry after a crash writes
204
+ only what is missing, and each run is scored exactly once per source.
205
+ - **A spend cap for experiments.** `evaluate()` takes `budget`. Each example run is reserved and
206
+ reconciled, and once the budget runs out no further example starts; `evaluate()` rejects with the
207
+ new `EvaluationBudgetError`, which carries the results it paid for.
208
+
209
+ ### Fixed
210
+
211
+ - An operation whose store failed under it, such as a database going away while it recorded its
212
+ result, left its handle pending forever, with an unhandled rejection; the handle now fails with
213
+ the store's error, and the record's lease lapses for recovery.
214
+
215
+ ### Changed
216
+
217
+ - A graph run's first checkpoint now carries the run's `metadata`, as every later checkpoint did.
218
+ - A deep agent with helpers offers `collect_helper` beside `delegate`, and its prompt says how to use
219
+ both; its default policy allows `collect_helper`.
220
+
221
+ ### Proof
222
+
223
+ - Glob and grep on the reference sandbox: alternatives, `**/` matching no directory, results relative
224
+ to the root, a limit, a path outside the root refused, a link out of the root not followed, and
225
+ binary, oversized, vendored, and past-the-line-limit matches skipped.
226
+ - An image, a PDF, and audio from a tool, after a JSON round trip, on the wire of Anthropic, OpenAI
227
+ chat (with and without an audio model), OpenAI Responses, and Google.
228
+ - A deep agent that greps its workspace, finds a PDF, and reads it as a document.
229
+ - The container sandbox passes the sandbox conformance suite against a real Docker in CI, which now
230
+ requires one. A command cannot find a host file anywhere in the container, cannot reach the host or
231
+ the internet, while the same probe from a container with a network can; a command over a 64 MB
232
+ limit is stopped; and a timeout leaves nothing it started running.
233
+ - The worker interpreter passes the interpreter conformance suite. An agent totals three orders and
234
+ describes one from code in one model round trip, while the refund in the same code, which the
235
+ policy asks about, never runs; a deep agent searches and reads its workspace from code, and a
236
+ command its policy denies is denied from code too.
237
+ - A background helper answers after the agent has moved on, with its events in the agent's stream. A
238
+ crash while the agent waits for it, and a crash before the agent saved the call that started it,
239
+ each end with one lookup, one note, and one start of the helper.
240
+ - An agent with 500 deferred tools finishes all 40 tasks on Anthropic, OpenAI, and Google, with a
241
+ model that calls a tool only once that provider's request has loaded it. Anthropic is sent all 501
242
+ tools on every call with 500 deferred; OpenAI and Google never more than 11. The search ranks each
243
+ of the 500 tools first for the step that describes it.
244
+ - A synthetic dataset written from all 39 guides cites, for every example, words that stand in its
245
+ guide where the citation says; an invented quote and a repeated question are dropped every time.
246
+ An adversarial dataset of 75 examples gets its canary out of an unguarded agent every time and
247
+ never out of a guarded one, which still answers the benign examples. Each attack the guardrails
248
+ block at the input is critical or high, and each medium one is redacted on the way out.
249
+ - Ten online-evaluation workers score 10,000 runs, each score exactly once, while one dies right
250
+ after writing a run's scores and before recording them; that run is taken back and found already
251
+ scored, and the queue stays near its limit. Workers on a budget stop at it, leave the rest queued,
252
+ and carry on the next period without scoring any run twice. Two sources sharing one store never
253
+ cross, and sampling picks the same runs on every worker. An experiment stops at its budget.
254
+
7
255
  ## [2.6.0] - 2026-10-11
8
256
 
9
257
  Protocols complete, and progress on every stream.
@@ -2204,7 +2452,9 @@ shared policy vocabulary is in place for a future release that revisits this.
2204
2452
  - URL fetching rejects unsafe private-network targets and limits response reads.
2205
2453
  - CLI and audit findings no longer disclose detected secret values.
2206
2454
 
2207
- [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.6.0...HEAD
2455
+ [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.8.0...HEAD
2456
+ [2.8.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.7.0...v2.8.0
2457
+ [2.7.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.6.0...v2.7.0
2208
2458
  [2.6.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.5.0...v2.6.0
2209
2459
  [2.5.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.4.0...v2.5.0
2210
2460
  [2.4.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.3.0...v2.4.0
package/README.md CHANGED
@@ -155,6 +155,7 @@ from the doc comments. The guides live in the repository, so these links go to G
155
155
  | [Loaders](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/loaders.md) | Files, Markdown, HTML, CSV, JSON, PDF, web pages, sitemaps, and Git repositories into retrieval |
156
156
  | [Retrieval](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/retrieval.md) | Hybrid search, reranking, MMR, parent documents, and Redis, Pinecone, Weaviate, and Chroma stores |
157
157
  | [Embeddings and retrieval](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/embeddings.md) | Embeddings as a routed operation family, and RAG helpers |
158
+ | [Media understanding](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/media.md) | Reading documents and images page by page, describing images, and answering questions about them, through any vision model or OCR |
158
159
  | [Images (experimental)](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/images.md) | Generation and masked edits over three backends, input safety, moderation, asset stores |
159
160
  | [Voice](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/voice.md) | Transcription, speech, voice turns, and voice sessions |
160
161
  | [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 |
@@ -275,14 +276,23 @@ Shipped so far:
275
276
  the protocols, traces, and the studio; AG-UI state deltas and frontend tools; A2A tasks and ACP
276
277
  sessions that survive a restart, with push notifications, `session/load`, and the editor's files,
277
278
  terminal, and MCP servers; and every protocol proven in a browser, against the previous release,
278
- and against its reference client.
279
+ and against its reference client;
280
+ - 2.7 agents that do more, and evaluation that runs on its own: workspace search and a multimodal
281
+ workspace, a container sandbox, an interpreter, helpers that run in the background and survive a
282
+ crash, and tool search on every provider; datasets generated from documents and from the
283
+ guardrails' attacks; and online evaluation on workers, within a budget;
284
+ - 2.8 retrieval at production scale, and media that is understood: the Qdrant, Redis, Weaviate, and
285
+ Chroma stores out of experimental after their checklist ran against the real servers; a shared
286
+ embedding cache; images, audio, and documents as embedding input; a half-price embedding batch tier
287
+ that ingestion survives a crash through; streaming asset stores; registry data you supply and
288
+ refresh; and reading, describing, and searching media, with answers given the media themselves.
279
289
 
280
290
  Upgrading from 1.x: `npx nexus migrate src --write` moves your imports, and
281
291
  [MIGRATING.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/MIGRATING.md) covers the rest.
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.
292
+ Next, Pinecone, images, the Gemini embedding batch tier, and media understanding graduate once the
293
+ live workflow passes against each. The
294
+ [roadmap](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/ROADMAP.md) lists what comes after, in the order
295
+ applications ask for it.
286
296
 
287
297
  ## Before Production
288
298
 
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, and for 2.5 where a review says so.
55
+ These reviews were done for 2.4, and for 2.5 and 2.8 where a review says so.
56
56
 
57
57
  ### SQLite adapters
58
58
 
@@ -77,6 +77,46 @@ Trust boundary: metadata filters, which applications often build from a request.
77
77
 
78
78
  Findings: none.
79
79
 
80
+ ### The Qdrant, Redis, Weaviate, and Chroma vector stores
81
+
82
+ Reviewed for 2.8, when they graduated. Pinecone's adapter was reviewed with them.
83
+
84
+ Trust boundary: a search's `topK` and metadata filter, which applications often build from a
85
+ request; and the server, which the store trusts as it trusts its own database. Credentials travel
86
+ only in headers, never in a URL or an error message.
87
+ - Qdrant, Pinecone, and Chroma receive a filter as JSON. Qdrant's keys are placed under `metadata.`,
88
+ so a filter cannot reach a chunk's content or vector.
89
+ - Redis filters only on the fields the index declares. A filter value is tagged with its type, and
90
+ every character that is not a letter or a digit is escaped.
91
+ - Weaviate's query is GraphQL. The collection and every filtered property must be a valid name, and
92
+ each value is a quoted literal.
93
+
94
+ Findings:
95
+ - **Fixed in 2.8: a `topK` from a request could change a Weaviate or Redis query.** Both stores
96
+ placed it in the query text, so `5) { … }` became part of the query. Every remote store now refuses
97
+ a `topK` that is not a positive integer before anything is sent. Proof: that value, a fraction, a
98
+ negative number, and `NaN` are refused by all five stores.
99
+ - **Fixed in 2.8: a replacement could keep a filter field the new version lacks.** A filter could
100
+ then still find the chunk by its old value, and that includes the `tenantId` that
101
+ `tenantVectorStore()` filters on.
102
+ - Chroma merges metadata on upsert, so it happened on every replacement.
103
+ - Redis deleted and rewrote a chunk in two commands, so it happened when two writers raced.
104
+
105
+ Chroma now removes each field the new version lacks, and Redis replaces a chunk in one atomic
106
+ script. Proof: ten writers race on the same twenty ids, live and against stand-ins that interleave
107
+ as a network does, and each id ends as exactly one writer's version.
108
+ - **Fixed in 2.8: Weaviate's filter index can keep an entry for a value an object no longer has**,
109
+ after two writers replace it at once (seen on Weaviate 1.40.1). The store now checks every filtered
110
+ hit against the object's own fields, and asks for more when a stale entry took a place. A result
111
+ always matches its filter.
112
+ - **Documented limitation: Chroma without `filterFields` reads before it replaces**, so two writers
113
+ replacing one id at once can leave a stale field. With `filterFields`, every write sets each
114
+ declared field, and a replacement stays whole under any race.
115
+ - **Documented limitation: the Redis store has no timeout of its own.** It sends through the client
116
+ you pass, so the client's command timeout applies.
117
+ - `tenantVectorStore()` checks each result's id and tenant stamp as well as filtering, so a stale
118
+ filter entry in any server cannot hand one tenant another's chunk.
119
+
80
120
  ### Loaders
81
121
 
82
122
  Trust boundary: everything a loader reads is untrusted. A repository, a website, a sitemap, or an
@@ -1,8 +1,9 @@
1
1
  import { appendList, counter, lastValue } from '../graph/channels.js';
2
- import { createGraph } from '../graph/graph.js';
2
+ import { createGraph, forwardedEvents } from '../graph/graph.js';
3
3
  import { Command, END, Send } from '../types/graph.js';
4
4
  import { capabilitiesOf } from './capabilities.js';
5
5
  import { ToolExecutor, toolMessageContent } from './tool.js';
6
+ import { TOOL_SEARCH_NAME, toolSearchTool } from './tool-search.js';
6
7
  const agentChannels = () => ({
7
8
  messages: appendList(),
8
9
  iterations: counter(),
@@ -18,7 +19,13 @@ export function createAgent(options) {
18
19
  throw new TypeError('createAgent needs a client with a complete() method');
19
20
  }
20
21
  const maxIterations = Math.max(1, options.maxIterations ?? 8);
21
- const executor = new ToolExecutor(options.tools ?? []);
22
+ const searchable = (tools) => {
23
+ const deferred = tools.list().filter((definition) => definition.deferred);
24
+ if (deferred.length === 0 || tools.has(TOOL_SEARCH_NAME))
25
+ return tools;
26
+ return new ToolExecutor([...tools.list(), toolSearchTool(deferred)]);
27
+ };
28
+ const executor = searchable(new ToolExecutor(options.tools ?? []));
22
29
  const middleware = options.middleware ?? [];
23
30
  const approvals = options.interruptOn ?? {};
24
31
  const toolsFor = async (context) => {
@@ -30,7 +37,7 @@ export function createAgent(options) {
30
37
  ...(context.principal ? { principal: context.principal } : {}),
31
38
  signal: context.signal,
32
39
  });
33
- return supplied.length ? new ToolExecutor([...supplied, ...executor.list()]) : executor;
40
+ return supplied.length ? searchable(new ToolExecutor([...supplied, ...executor.list()])) : executor;
34
41
  };
35
42
  const graph = createGraph({ channels: agentChannels() })
36
43
  .addNode('model', async (context) => {
@@ -187,6 +194,7 @@ export function createAgent(options) {
187
194
  }
188
195
  }
189
196
  }
197
+ const forward = forwardedEvents(context);
190
198
  const run = async (current) => {
191
199
  if (options.permissions && current.args !== args) {
192
200
  const recheck = await options.permissions.decide({
@@ -205,6 +213,11 @@ export function createAgent(options) {
205
213
  signal: context.signal,
206
214
  ...(context.store ? { store: context.store } : {}),
207
215
  progress: (progress) => context.tool({ phase: 'progress', id: call.id, name: description.name, progress }),
216
+ ...(forward
217
+ ? {
218
+ forward: (event, namespace) => forward({ ...event, namespace: [...namespace, ...(event.namespace ?? [])] }),
219
+ }
220
+ : {}),
208
221
  });
209
222
  };
210
223
  context.tool({ phase: 'start', id: call.id, name: description.name, args });
@@ -0,0 +1,24 @@
1
+ /**
2
+ * File globs for workspace search, shared by the sandboxes: `*` matches within a path segment, `**`
3
+ * across segments (`**\/` also matches none), `?` one character, and `{a,b}` either alternative.
4
+ * Paths use `/`, are relative to the search root, and match case-sensitively.
5
+ */
6
+ export declare function globMatcher(pattern: string): (path: string) => boolean;
7
+ /** Directories workspace search never enters. */
8
+ export declare const SKIPPED_DIRECTORIES: ReadonlySet<string>;
9
+ /** The most directory entries, or files, one search walks. */
10
+ export declare const WALK_LIMIT = 20000;
11
+ /** Files larger than this are not searched. */
12
+ export declare const GREP_FILE_BYTES = 1048576;
13
+ /** One search reads no more than this, in all. */
14
+ export declare const GREP_TOTAL_BYTES: number;
15
+ /**
16
+ * The lines of a file that match, numbered from 1 and cut to 300 characters, at most `limit`; none
17
+ * for a binary file, one with a NUL byte in its first 8,000 bytes.
18
+ */
19
+ export declare function matchingLines(bytes: Uint8Array, expression: RegExp, limit: number): Array<{
20
+ line: number;
21
+ text: string;
22
+ }>;
23
+ /** MIME types for the media a workspace tool reads as content the model sees, by file extension. */
24
+ export declare const MEDIA_TYPES: Readonly<Record<string, string>>;