nexus-ai-pro 2.1.0 → 2.3.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 (300) hide show
  1. package/API_STABILITY.md +77 -0
  2. package/CHANGELOG.md +286 -1
  3. package/README.md +19 -9
  4. package/dist/agent/capabilities.d.ts +39 -0
  5. package/dist/agent/capabilities.js +36 -0
  6. package/dist/agent/create-agent.d.ts +61 -11
  7. package/dist/agent/create-agent.js +136 -22
  8. package/dist/agent/index.d.ts +3 -2
  9. package/dist/agent/index.js +1 -1
  10. package/dist/agent/middleware/approval.d.ts +26 -0
  11. package/dist/agent/middleware/approval.js +23 -0
  12. package/dist/agent/{middleware.d.ts → middleware/basic.d.ts} +5 -4
  13. package/dist/agent/{middleware.js → middleware/basic.js} +3 -5
  14. package/dist/agent/middleware/context-editor.d.ts +52 -0
  15. package/dist/agent/middleware/context-editor.js +66 -0
  16. package/dist/agent/middleware/filesystem.d.ts +44 -0
  17. package/dist/agent/middleware/filesystem.js +83 -0
  18. package/dist/agent/middleware/index.d.ts +19 -0
  19. package/dist/agent/middleware/index.js +9 -0
  20. package/dist/agent/middleware/limits.d.ts +42 -0
  21. package/dist/agent/middleware/limits.js +52 -0
  22. package/dist/agent/middleware/models.d.ts +48 -0
  23. package/dist/agent/middleware/models.js +44 -0
  24. package/dist/agent/middleware/pii.d.ts +67 -0
  25. package/dist/agent/middleware/pii.js +173 -0
  26. package/dist/agent/middleware/retry.d.ts +66 -0
  27. package/dist/agent/middleware/retry.js +59 -0
  28. package/dist/agent/middleware/shared.d.ts +41 -0
  29. package/dist/agent/middleware/shared.js +73 -0
  30. package/dist/agent/middleware/tool-selector.d.ts +64 -0
  31. package/dist/agent/middleware/tool-selector.js +106 -0
  32. package/dist/agent/permissions.d.ts +113 -0
  33. package/dist/agent/permissions.js +180 -0
  34. package/dist/agent/sandbox.d.ts +151 -0
  35. package/dist/agent/sandbox.js +257 -0
  36. package/dist/agent/tool.d.ts +8 -3
  37. package/dist/agent/tool.js +14 -3
  38. package/dist/cache/memory-cache.d.ts +8 -0
  39. package/dist/cache/memory-cache.js +9 -0
  40. package/dist/cache/semantic-cache.d.ts +6 -3
  41. package/dist/cache/semantic-cache.js +5 -2
  42. package/dist/cli/args.js +3 -0
  43. package/dist/cli/db.d.ts +16 -4
  44. package/dist/cli/db.js +169 -22
  45. package/dist/cli/doctor.d.ts +17 -0
  46. package/dist/cli/doctor.js +53 -0
  47. package/dist/cli.js +12 -1
  48. package/dist/core/lifecycle.js +11 -6
  49. package/dist/core/nexus.d.ts +6 -0
  50. package/dist/core/nexus.js +103 -22
  51. package/dist/deep-agent/index.d.ts +109 -0
  52. package/dist/deep-agent/index.js +216 -0
  53. package/dist/doctor/index.d.ts +90 -0
  54. package/dist/doctor/index.js +237 -0
  55. package/dist/graph/functional.d.ts +89 -7
  56. package/dist/graph/functional.js +156 -36
  57. package/dist/graph/graph.d.ts +15 -5
  58. package/dist/graph/graph.js +95 -22
  59. package/dist/graph/lint.d.ts +4 -1
  60. package/dist/graph/lint.js +35 -0
  61. package/dist/images/evals.js +4 -2
  62. package/dist/operations/adapters.d.ts +52 -4
  63. package/dist/operations/adapters.js +194 -7
  64. package/dist/operations/runner.js +1 -0
  65. package/dist/operations/stats.js +8 -1
  66. package/dist/operations/store.d.ts +5 -1
  67. package/dist/operations/store.js +7 -0
  68. package/dist/ops/circuit-breaker.d.ts +52 -0
  69. package/dist/ops/circuit-breaker.js +4 -0
  70. package/dist/ops/circuit-store.d.ts +13 -2
  71. package/dist/ops/circuit-store.js +68 -0
  72. package/dist/ops/circuit-sync.d.ts +17 -0
  73. package/dist/ops/circuit-sync.js +86 -0
  74. package/dist/ops/rate-limiter.d.ts +2 -0
  75. package/dist/ops/rate-limiter.js +2 -0
  76. package/dist/postgres/circuits.d.ts +21 -4
  77. package/dist/postgres/circuits.js +65 -4
  78. package/dist/postgres/client.d.ts +0 -2
  79. package/dist/postgres/client.js +0 -4
  80. package/dist/postgres/evaluate.d.ts +16 -4
  81. package/dist/postgres/evaluate.js +17 -5
  82. package/dist/postgres/fulltext.d.ts +67 -0
  83. package/dist/postgres/fulltext.js +91 -0
  84. package/dist/postgres/index.d.ts +32 -11
  85. package/dist/postgres/index.js +65 -31
  86. package/dist/postgres/migrations.d.ts +60 -0
  87. package/dist/postgres/migrations.js +121 -0
  88. package/dist/postgres/operations.d.ts +14 -3
  89. package/dist/postgres/operations.js +45 -15
  90. package/dist/postgres/prompts.d.ts +11 -2
  91. package/dist/postgres/prompts.js +14 -3
  92. package/dist/postgres/rollups.d.ts +35 -0
  93. package/dist/postgres/rollups.js +129 -0
  94. package/dist/postgres/store.d.ts +11 -2
  95. package/dist/postgres/store.js +14 -3
  96. package/dist/postgres/traces.d.ts +13 -3
  97. package/dist/postgres/traces.js +25 -3
  98. package/dist/postgres/vectors.d.ts +11 -2
  99. package/dist/postgres/vectors.js +14 -3
  100. package/dist/rag/elasticsearch.d.ts +71 -0
  101. package/dist/rag/elasticsearch.js +130 -0
  102. package/dist/rag/pipeline.d.ts +140 -0
  103. package/dist/rag/pipeline.js +187 -0
  104. package/dist/rag/rerankers.d.ts +92 -0
  105. package/dist/rag/rerankers.js +97 -0
  106. package/dist/rag/retrievers.d.ts +12 -1
  107. package/dist/router/auto-router.d.ts +34 -7
  108. package/dist/router/auto-router.js +201 -68
  109. package/dist/server/assistant.d.ts +9 -1
  110. package/dist/server/assistant.js +10 -0
  111. package/dist/server/auth.d.ts +250 -0
  112. package/dist/server/auth.js +392 -0
  113. package/dist/server/errors.d.ts +12 -0
  114. package/dist/server/errors.js +7 -0
  115. package/dist/server/index.d.ts +1 -1
  116. package/dist/server/index.js +1 -1
  117. package/dist/server/runs.d.ts +24 -1
  118. package/dist/server/runs.js +90 -14
  119. package/dist/sqlite/index.d.ts +16 -3
  120. package/dist/sqlite/index.js +16 -3
  121. package/dist/sqlite/migrations.d.ts +26 -0
  122. package/dist/sqlite/migrations.js +63 -0
  123. package/dist/sqlite/operations.d.ts +14 -3
  124. package/dist/sqlite/operations.js +42 -15
  125. package/dist/sqlite/store.d.ts +9 -2
  126. package/dist/sqlite/store.js +13 -3
  127. package/dist/sqlite/vectors.d.ts +11 -2
  128. package/dist/sqlite/vectors.js +13 -2
  129. package/dist/store/memory.d.ts +10 -0
  130. package/dist/store/memory.js +10 -0
  131. package/dist/store/tenant.d.ts +12 -0
  132. package/dist/store/tenant.js +29 -0
  133. package/dist/tenancy/index.d.ts +113 -0
  134. package/dist/tenancy/index.js +233 -0
  135. package/dist/tracing/instrument.js +2 -0
  136. package/dist/types/config.d.ts +24 -2
  137. package/dist/types/graph.d.ts +49 -1
  138. package/dist/types/messages.d.ts +41 -2
  139. package/dist/types/operations.d.ts +6 -0
  140. package/dist/types/principal.d.ts +21 -0
  141. package/dist/types/principal.js +1 -0
  142. package/dist/types/response.d.ts +20 -0
  143. package/dist/types/server.d.ts +12 -10
  144. package/dist/utils/schema-migrations.d.ts +97 -0
  145. package/dist/utils/schema-migrations.js +91 -0
  146. package/dist/utils/signals.d.ts +17 -0
  147. package/dist/utils/signals.js +26 -0
  148. package/dist/utils/tenant.d.ts +25 -0
  149. package/dist/utils/tenant.js +30 -0
  150. package/dist/version.d.ts +1 -1
  151. package/dist/version.js +1 -1
  152. package/dist-cjs/agent/capabilities.d.ts +39 -0
  153. package/dist-cjs/agent/capabilities.js +41 -0
  154. package/dist-cjs/agent/create-agent.d.ts +61 -11
  155. package/dist-cjs/agent/create-agent.js +136 -22
  156. package/dist-cjs/agent/index.d.ts +3 -2
  157. package/dist-cjs/agent/index.js +4 -4
  158. package/dist-cjs/agent/middleware/approval.d.ts +26 -0
  159. package/dist-cjs/agent/middleware/approval.js +26 -0
  160. package/dist-cjs/agent/{middleware.d.ts → middleware/basic.d.ts} +5 -4
  161. package/dist-cjs/agent/{middleware.js → middleware/basic.js} +3 -5
  162. package/dist-cjs/agent/middleware/context-editor.d.ts +52 -0
  163. package/dist-cjs/agent/middleware/context-editor.js +70 -0
  164. package/dist-cjs/agent/middleware/filesystem.d.ts +44 -0
  165. package/dist-cjs/agent/middleware/filesystem.js +86 -0
  166. package/dist-cjs/agent/middleware/index.d.ts +19 -0
  167. package/dist-cjs/agent/middleware/index.js +28 -0
  168. package/dist-cjs/agent/middleware/limits.d.ts +42 -0
  169. package/dist-cjs/agent/middleware/limits.js +57 -0
  170. package/dist-cjs/agent/middleware/models.d.ts +48 -0
  171. package/dist-cjs/agent/middleware/models.js +48 -0
  172. package/dist-cjs/agent/middleware/pii.d.ts +67 -0
  173. package/dist-cjs/agent/middleware/pii.js +178 -0
  174. package/dist-cjs/agent/middleware/retry.d.ts +66 -0
  175. package/dist-cjs/agent/middleware/retry.js +63 -0
  176. package/dist-cjs/agent/middleware/shared.d.ts +41 -0
  177. package/dist-cjs/agent/middleware/shared.js +83 -0
  178. package/dist-cjs/agent/middleware/tool-selector.d.ts +64 -0
  179. package/dist-cjs/agent/middleware/tool-selector.js +109 -0
  180. package/dist-cjs/agent/permissions.d.ts +113 -0
  181. package/dist-cjs/agent/permissions.js +189 -0
  182. package/dist-cjs/agent/sandbox.d.ts +151 -0
  183. package/dist-cjs/agent/sandbox.js +297 -0
  184. package/dist-cjs/agent/tool.d.ts +8 -3
  185. package/dist-cjs/agent/tool.js +14 -3
  186. package/dist-cjs/cache/memory-cache.d.ts +8 -0
  187. package/dist-cjs/cache/memory-cache.js +9 -0
  188. package/dist-cjs/cache/semantic-cache.d.ts +6 -3
  189. package/dist-cjs/cache/semantic-cache.js +5 -2
  190. package/dist-cjs/cli/args.js +3 -0
  191. package/dist-cjs/cli/db.d.ts +16 -4
  192. package/dist-cjs/cli/db.js +200 -20
  193. package/dist-cjs/cli/doctor.d.ts +17 -0
  194. package/dist-cjs/cli/doctor.js +56 -0
  195. package/dist-cjs/core/lifecycle.js +11 -6
  196. package/dist-cjs/core/nexus.d.ts +6 -0
  197. package/dist-cjs/core/nexus.js +103 -22
  198. package/dist-cjs/deep-agent/index.d.ts +109 -0
  199. package/dist-cjs/deep-agent/index.js +219 -0
  200. package/dist-cjs/doctor/index.d.ts +90 -0
  201. package/dist-cjs/doctor/index.js +276 -0
  202. package/dist-cjs/graph/functional.d.ts +89 -7
  203. package/dist-cjs/graph/functional.js +155 -35
  204. package/dist-cjs/graph/graph.d.ts +15 -5
  205. package/dist-cjs/graph/graph.js +95 -22
  206. package/dist-cjs/graph/lint.d.ts +4 -1
  207. package/dist-cjs/graph/lint.js +35 -0
  208. package/dist-cjs/images/evals.js +4 -2
  209. package/dist-cjs/operations/adapters.d.ts +52 -4
  210. package/dist-cjs/operations/adapters.js +193 -6
  211. package/dist-cjs/operations/runner.js +1 -0
  212. package/dist-cjs/operations/stats.js +8 -1
  213. package/dist-cjs/operations/store.d.ts +5 -1
  214. package/dist-cjs/operations/store.js +7 -0
  215. package/dist-cjs/ops/circuit-breaker.d.ts +52 -0
  216. package/dist-cjs/ops/circuit-breaker.js +4 -0
  217. package/dist-cjs/ops/circuit-store.d.ts +13 -2
  218. package/dist-cjs/ops/circuit-store.js +68 -0
  219. package/dist-cjs/ops/circuit-sync.d.ts +17 -0
  220. package/dist-cjs/ops/circuit-sync.js +87 -0
  221. package/dist-cjs/ops/rate-limiter.d.ts +2 -0
  222. package/dist-cjs/ops/rate-limiter.js +2 -0
  223. package/dist-cjs/postgres/circuits.d.ts +21 -4
  224. package/dist-cjs/postgres/circuits.js +98 -3
  225. package/dist-cjs/postgres/client.d.ts +0 -2
  226. package/dist-cjs/postgres/client.js +0 -5
  227. package/dist-cjs/postgres/evaluate.d.ts +16 -4
  228. package/dist-cjs/postgres/evaluate.js +50 -4
  229. package/dist-cjs/postgres/fulltext.d.ts +67 -0
  230. package/dist-cjs/postgres/fulltext.js +129 -0
  231. package/dist-cjs/postgres/index.d.ts +32 -11
  232. package/dist-cjs/postgres/index.js +69 -20
  233. package/dist-cjs/postgres/migrations.d.ts +60 -0
  234. package/dist-cjs/postgres/migrations.js +129 -0
  235. package/dist-cjs/postgres/operations.d.ts +14 -3
  236. package/dist-cjs/postgres/operations.js +78 -14
  237. package/dist-cjs/postgres/prompts.d.ts +11 -2
  238. package/dist-cjs/postgres/prompts.js +47 -2
  239. package/dist-cjs/postgres/rollups.d.ts +35 -0
  240. package/dist-cjs/postgres/rollups.js +168 -0
  241. package/dist-cjs/postgres/store.d.ts +11 -2
  242. package/dist-cjs/postgres/store.js +47 -2
  243. package/dist-cjs/postgres/traces.d.ts +13 -3
  244. package/dist-cjs/postgres/traces.js +58 -2
  245. package/dist-cjs/postgres/vectors.d.ts +11 -2
  246. package/dist-cjs/postgres/vectors.js +47 -2
  247. package/dist-cjs/rag/elasticsearch.d.ts +71 -0
  248. package/dist-cjs/rag/elasticsearch.js +135 -0
  249. package/dist-cjs/rag/pipeline.d.ts +140 -0
  250. package/dist-cjs/rag/pipeline.js +191 -0
  251. package/dist-cjs/rag/rerankers.d.ts +92 -0
  252. package/dist-cjs/rag/rerankers.js +107 -0
  253. package/dist-cjs/rag/retrievers.d.ts +12 -1
  254. package/dist-cjs/router/auto-router.d.ts +34 -7
  255. package/dist-cjs/router/auto-router.js +201 -68
  256. package/dist-cjs/server/assistant.d.ts +9 -1
  257. package/dist-cjs/server/assistant.js +10 -0
  258. package/dist-cjs/server/auth.d.ts +250 -0
  259. package/dist-cjs/server/auth.js +402 -0
  260. package/dist-cjs/server/errors.d.ts +12 -0
  261. package/dist-cjs/server/errors.js +9 -1
  262. package/dist-cjs/server/index.d.ts +1 -1
  263. package/dist-cjs/server/index.js +2 -1
  264. package/dist-cjs/server/runs.d.ts +24 -1
  265. package/dist-cjs/server/runs.js +89 -13
  266. package/dist-cjs/sqlite/index.d.ts +16 -3
  267. package/dist-cjs/sqlite/index.js +31 -10
  268. package/dist-cjs/sqlite/migrations.d.ts +26 -0
  269. package/dist-cjs/sqlite/migrations.js +71 -0
  270. package/dist-cjs/sqlite/operations.d.ts +14 -3
  271. package/dist-cjs/sqlite/operations.js +76 -15
  272. package/dist-cjs/sqlite/store.d.ts +9 -2
  273. package/dist-cjs/sqlite/store.js +47 -3
  274. package/dist-cjs/sqlite/vectors.d.ts +11 -2
  275. package/dist-cjs/sqlite/vectors.js +47 -2
  276. package/dist-cjs/store/memory.d.ts +10 -0
  277. package/dist-cjs/store/memory.js +10 -0
  278. package/dist-cjs/store/tenant.d.ts +12 -0
  279. package/dist-cjs/store/tenant.js +33 -0
  280. package/dist-cjs/tenancy/index.d.ts +113 -0
  281. package/dist-cjs/tenancy/index.js +250 -0
  282. package/dist-cjs/tracing/instrument.js +2 -0
  283. package/dist-cjs/types/config.d.ts +24 -2
  284. package/dist-cjs/types/graph.d.ts +49 -1
  285. package/dist-cjs/types/messages.d.ts +41 -2
  286. package/dist-cjs/types/operations.d.ts +6 -0
  287. package/dist-cjs/types/principal.d.ts +21 -0
  288. package/dist-cjs/types/principal.js +2 -0
  289. package/dist-cjs/types/response.d.ts +20 -0
  290. package/dist-cjs/types/server.d.ts +12 -10
  291. package/dist-cjs/utils/schema-migrations.d.ts +97 -0
  292. package/dist-cjs/utils/schema-migrations.js +102 -0
  293. package/dist-cjs/utils/signals.d.ts +17 -0
  294. package/dist-cjs/utils/signals.js +29 -0
  295. package/dist-cjs/utils/tenant.d.ts +25 -0
  296. package/dist-cjs/utils/tenant.js +37 -0
  297. package/dist-cjs/version.d.ts +1 -1
  298. package/dist-cjs/version.js +1 -1
  299. package/llms.txt +6 -4
  300. package/package.json +188 -2
package/API_STABILITY.md CHANGED
@@ -41,6 +41,83 @@ Both are covered by this policy:
41
41
  Deep imports into `dist`, `dist-cjs`, or `src` are not supported. Import only from `nexus-ai-pro` or
42
42
  one of its explicit subpaths.
43
43
 
44
+ ## 2.3 stage (2.3.0)
45
+
46
+ Everything 2.3 adds is stable from the start and additive: no default changes meaning.
47
+
48
+ - `ToolDefinition.capabilities` and the capability grammar; `permissionPolicy()`, `PermissionPolicy`,
49
+ `PermissionRules`, `PermissionRequest`, `PermissionVerdict`, `PermissionDecision`,
50
+ `PermissionPolicyLike`, `parseCapability()`, `capabilitiesOf()`, `isSensitiveCapability()`, and
51
+ `normalizePath()` (`nexus-ai-pro/agent/permissions`); `CreateAgentOptions.permissions`;
52
+ - the sandbox contract: `Sandbox`, its option, result, and isolation types, `sandboxTools()`,
53
+ `runSandboxConformance()` and its report types, `SandboxPathError` (`nexus-ai-pro/agent/sandbox`).
54
+ `processSandbox()` is a development reference and may change; conformance checks may be added;
55
+ - `NodeOptions.effects` and `interrupts`, `CompileOptions.tools`, `GraphToolDescription`, and the lint
56
+ codes `SENSITIVE_TOOL_WITHOUT_APPROVAL`, `UNDECLARED_TOOL_CAPABILITIES`, and
57
+ `SIDE_EFFECT_BEFORE_INTERRUPT`;
58
+ - `nexus-ai-pro/agent/middleware` and every middleware and option type on it;
59
+ `AgentMiddleware.wrapModelCall`, `AgentMiddlewareContext`, `AgentModelCallContext`, the third
60
+ argument of `wrapToolCall`, `next(call)`, and `AgentStopReason` `'stopped'`. The PII patterns
61
+ may be tightened in a minor release to cut false positives, and new `PiiKind` members may be
62
+ added;
63
+ - `nexus-ai-pro/server/auth`: `jwtAuth()`, `apiKeyAuth()`, `trustedProxyAuth()`, `anyAuth()`,
64
+ `createJwtVerifier()`, `hashApiKey()`, `JwtError` and its codes, and every option type. New
65
+ `JwtAlgorithm` and `JwtErrorCode` members may be added;
66
+ - `Principal.roles`, `method`, and `claims`; `GraphRunOptions.principal`, `NodeContext.principal`,
67
+ `WorkflowRunOptions.principal`, and `WorkflowContext.principal`; `ToolContext` and the second
68
+ argument of `ToolDefinition.execute`; `AgentMiddlewareContext.principal` and
69
+ `PermissionRequest.principal`. A checkpoint's `metadata.userId` is recorded when a run has a
70
+ principal with a subject;
71
+ - `SparseRetriever`; `PostgresKeywordIndex` and `keywordIndexMigrations()`
72
+ (`nexus-ai-pro/postgres/fulltext`); `ElasticsearchKeywordIndex` and `ElasticsearchError`
73
+ (`nexus-ai-pro/rag/elasticsearch`); and every reranker and option type on
74
+ `nexus-ai-pro/rag/rerankers`. The default endpoints of hosted rerankers follow their providers'
75
+ current API versions;
76
+ - `nexus-ai-pro/rag/pipeline`: `createIngestionPipeline()`, `ingestionExecutor()`, and their types.
77
+ The manifest's layout under `['nexus', 'ingestion', <name>]` is stable: a later release reads a
78
+ manifest an earlier one wrote;
79
+ - `nexus-ai-pro/deep-agent`: `createDeepAgent()`, `DeepAgentOptions`, `DeepSubagent`, and
80
+ `DeepSkill`, and the names and arguments of the tools it gives the model (`write_todos`,
81
+ `delegate`, `load_skill`, `edit_file`). The wording of its system prompt may change in any release.
82
+
83
+ ## 2.2 stage (2.2.0)
84
+
85
+ Everything 2.2 adds is stable from the start and additive: no default changes meaning.
86
+
87
+ - versioned migrations: `SchemaMigration`, `AppliedMigration`, `MigrationStatus`, `MigrationResult`,
88
+ `SchemaMigrationError`, `migrationChecksum()`, `applyPostgresMigrations()`,
89
+ `postgresMigrationStatus()`, `postgresMigrations()`, `PostgresMigrateOptions`, every adapter's
90
+ `…Migrations()`, and the SQLite twins (`nexus-ai-pro/postgres`, `nexus-ai-pro/postgres/migrations`,
91
+ `nexus-ai-pro/sqlite`, `nexus-ai-pro/sqlite/migrations`). A migration, once released, never
92
+ changes; later releases add versions. `migrate()` now resolves to a `MigrationResult` instead of
93
+ nothing;
94
+ - `nexus db status` and `nexus db migrate`, and `nexus db sql --record`;
95
+ - `RedisOperationStoreOptions.index` and `reindex()`;
96
+ - `CircuitBreakerConfig.shareObservations`, `CircuitStateStore.observe()`, `CircuitObservation`,
97
+ `CircuitWindow`, and `CircuitSnapshot.shared`;
98
+ - `PostgresRollupStore` (`nexus-ai-pro/postgres/rollups`);
99
+ - the tenant model (`nexus-ai-pro/tenancy`): every `tenant…()` view, `tenantScope()`,
100
+ `TenantAssetStore`, `TenantStores`, `assertTenantId()`, `TenantIdError`, and `TENANT_NAMESPACE`.
101
+ The prefix a view stores names under (`tenant/<id>/`) is part of the contract and does not change
102
+ within 2.x; `GraphRunOptions.tenantId`, `NodeContext.tenantId`, the `tenantId` options of `state()`,
103
+ `history()`, `fork()`, and `updateState()`, `OperationRecord.tenantId`,
104
+ `OperationSubmitOptions.tenantId`, `OperationStoreFilter.tenantId`, `RateLimitedRequest.tenantId`,
105
+ and `rateLimit.key: 'tenantId'`;
106
+ - `CacheConfig.namespace`, `CacheNamespace`, `CacheOutcome` and `ResponseMeta.cache`,
107
+ `CompletionRequest.responseCache`, `MemoryCache.lookup()`, and the `scope` of `SemanticCache`'s
108
+ `get()` and `set()`;
109
+ - `diagnose()` and its types (`nexus-ai-pro/doctor`), and `nexus doctor`. Checks may be added in a
110
+ minor release; a check's id and what makes it fail do not change within 2.x;
111
+ - workflow steps' `timeout`, `onError`, `StepTimeout`, `StepFailure`, `StepRecovery`,
112
+ `StepContext.heartbeat()`, `WorkflowOptions.stepDefaults`, `WorkflowRunOptions.control` and
113
+ `tenantId`, `WorkflowContext.tenantId`, and `WorkflowStepTimeoutError.kind`;
114
+ - `RunHandOffError`, `AssistantRunContext.saveProgress()` and `progress`.
115
+
116
+ Three races are closed rather than added: a busy thread is claimed atomically, and the in-memory and
117
+ Redis operation stores refuse a second record under a held idempotency key, as the SQL stores
118
+ already did. The semantic cache no longer matches across tenants, and a failing response cache no
119
+ longer fails a request.
120
+
44
121
  ## 2.1 stage (2.1.0)
45
122
 
46
123
  Everything 2.1 adds is stable from the start and additive: no default changes meaning.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,289 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [2.3.0] - 2026-10-06
8
+
9
+ Agents that act safely, on production retrieval.
10
+
11
+ ### Added
12
+
13
+ - **Capabilities and permission policies.** A tool declares what each call does —
14
+ `filesystem:write:<path>`, `network:<host>`, `shell:<command>`, `code`, or an effect of the
15
+ application's own — as a list or a function of its arguments. `permissionPolicy()`, on the new
16
+ `nexus-ai-pro/agent/permissions`, grants filesystem globs, network hosts, and shell command
17
+ prefixes, and decides each call: allow, deny, or ask. Paths are normalized before any grant sees
18
+ them, and a command that chains, pipes, or substitutes is never granted by a prefix.
19
+ `createAgent({ permissions })` enforces it before a tool runs: a denied call never runs, an `ask`
20
+ interrupts as `interruptOn` does, and arguments an approver edits are decided again.
21
+ - **A sandbox contract**, on the new `nexus-ai-pro/agent/sandbox`: the `Sandbox` interface a
22
+ container, VM, or hosted interpreter fills; `sandboxTools()`, whose calls declare exactly the
23
+ command or path they use; `runSandboxConformance()`, which proves any sandbox against the
24
+ contract; and `processSandbox()`, a reference for development that is never a security boundary
25
+ and says so.
26
+ - **The linter reads capabilities.** `SENSITIVE_TOOL_WITHOUT_APPROVAL` reports a tool that writes, runs
27
+ commands or code, or reaches the network with neither approval nor a policy;
28
+ `UNDECLARED_TOOL_CAPABILITIES` a tool whose effects cannot be judged; and
29
+ `SIDE_EFFECT_BEFORE_INTERRUPT` a node that declares `effects` and `interrupts`. An agent's graph
30
+ describes its tools for it.
31
+ - **A middleware catalog**, on the new `nexus-ai-pro/agent/middleware`, each a separate import:
32
+ `modelRetry()`, `modelFallback()` (to another model or another client), `dynamicModel()`,
33
+ `toolRetry()` (which by default repeats only a call that cannot have changed anything),
34
+ `toolSelector()` (by rule, embedding similarity, or both), `contextEditor()` (clears old tool
35
+ results in batches, so a prompt cache keeps working), `piiMiddleware()` (validated card numbers
36
+ and IBANs; redact, mask, hash, or block), `humanApproval()` (per call, in band or out of band),
37
+ `modelCallLimit()` (per run and per thread, the thread count shared through the store), and
38
+ `filesystemContext()` (instruction files in, large results out to a file). The three existing
39
+ middleware are there too.
40
+ - **A wider middleware seam.** `wrapModelCall` wraps the model call itself, and may call `next()`
41
+ again to retry or fall back. Every hook receives an `AgentMiddlewareContext`: the state, the
42
+ store, the thread and tenant, the run's signal, and `interrupt()`. `context.stop()` ends a run
43
+ with an answer and the new `stopReason: 'stopped'`. A `wrapToolCall` can pass `next()` changed
44
+ arguments, which the permission policy decides again.
45
+ - **Server authentication**, on the new `nexus-ai-pro/server/auth`. It is built on Web Crypto, with no
46
+ dependency:
47
+ - `jwtAuth()` takes an identity provider's key set (cached, and refetched when a token names a new
48
+ key), a public key, or a shared secret, and checks issuer, audience, expiry, and age. It refuses
49
+ `alg: none` and key confusion, and requires an audience with a key set.
50
+ - `apiKeyAuth()` compares keys by their SHA-256 hash, from `hashApiKey()`.
51
+ - `trustedProxyAuth()` refuses to exist without proof that the proxy sent the request.
52
+ - `anyAuth()` combines hooks.
53
+ - `createJwtVerifier()` verifies tokens outside the server.
54
+ - **One Principal, everywhere a run goes.** It gains `roles`, `method`, and `claims`.
55
+ - Graph and workflow runs take a `principal`, which nodes read as `context.principal`. Its tenant
56
+ becomes the run's, and its subject is recorded on every checkpoint and in the run's trace.
57
+ - `graphAssistant()` passes the authenticated caller on.
58
+ - Tools receive a `ToolContext` as the second argument of `execute`: the principal, the thread
59
+ and tenant, the run's signal, and the tenant's store.
60
+ - Middleware and permission policies see the principal too.
61
+ - **Sparse retrieval as a contract.** A `SparseRetriever` is keyword search that takes writes, so it
62
+ fuses with vector search and stays in step with a vector store. `KeywordIndex` is one. Two more
63
+ ship:
64
+ - `PostgresKeywordIndex` (`nexus-ai-pro/postgres/fulltext`) uses Postgres full-text search, with a
65
+ GIN index and ranking in the database. With `shared`, it searches a pgvector table through a
66
+ generated column, so hybrid search keeps one copy of the data.
67
+ - `ElasticsearchKeywordIndex` (`nexus-ai-pro/rag/elasticsearch`) works with Elasticsearch or
68
+ OpenSearch over the REST API, with no client library.
69
+ - **Rerankers**, on the new `nexus-ai-pro/rag/rerankers`, with no SDK:
70
+ - `cohereReranker()`, `voyageReranker()`, and `jinaReranker()` for hosted APIs;
71
+ - `teiReranker()` for a self-hosted cross-encoder;
72
+ - `httpReranker()` for any other API;
73
+ - `crossEncoderReranker()` for a model run in process.
74
+ - **Durable ingestion**, on the new `nexus-ai-pro/rag/pipeline`. `createIngestionPipeline()` keeps a
75
+ manifest in any `Store`: each document's content version, its chunking, the embedding model, and
76
+ each chunk's version. A run:
77
+ - embeds only new and changed chunks; chunks are named by their text, so one that only moved keeps
78
+ its embedding;
79
+ - deletes the chunks and documents that are gone;
80
+ - keeps keyword indexes in step;
81
+ - writes to the stores before the manifest.
82
+
83
+ `ingestionExecutor()` runs it as a durable operation, and a retry starts at the document the
84
+ failed attempt stopped on.
85
+ - **A deep-agent preset**, on the new `nexus-ai-pro/deep-agent`. `createDeepAgent()` is
86
+ `createAgent()` composed with:
87
+ - a plan, kept in the transcript;
88
+ - a sandboxed workspace, with `edit_file` for precise changes and commands granted by name;
89
+ - helpers behind one `delegate` tool, which never get a tool that needs approval;
90
+ - skills loaded on demand;
91
+ - instruction files and context offloading;
92
+ - context editing, optional tool selection, and a default permission policy.
93
+
94
+ It returns the same compiled graph as any agent; it is not a new engine.
95
+ - `processSandbox()` loads Node's modules when first used, so `nexus-ai-pro/agent/sandbox`, and a
96
+ deep agent over a container sandbox, import on any runtime.
97
+
98
+ ### Fixed
99
+
100
+ - `MemoryStore` swept every item for expiry on every write, so writing n items took time
101
+ proportional to n². It now sweeps only once an item has a time to live, and at most once a second.
102
+ Expired items were already hidden from reads.
103
+
104
+ - `limitToolCalls()` counted calls in memory shared by every run of an agent, so one thread's calls
105
+ used up another's limit, and the count never reset. It now counts per run from the transcript.
106
+ - A subgraph run through `asNode()` now runs for its parent's tenant, so its store is the tenant's
107
+ view, as the parent's is.
108
+ - `sandboxTools()` file tools accept a path that names the mount, such as `/workspace/notes.txt`,
109
+ as well as one relative to it, and say in their descriptions which to use.
110
+
111
+ ### Proof
112
+
113
+ - In a real agent tool loop, a write outside the workspace and a request to an unlisted host are
114
+ refused before their tools run, and a granted command that needs approval waits for a person.
115
+ - The process sandbox passes the conformance suite, and a sandbox that leaks the host's environment
116
+ and writes outside its root fails exactly those two checks.
117
+ - An agent over 150 tools completes 40 tasks, a third of them needing two tools, with
118
+ `toolSelector()` at equal quality: every task succeeds either way. Input tokens per task fall from
119
+ about 64,000 to about 5,200, and latency by about 70%: the model's time computed from
120
+ each request's size, plus the agent's own measured time. Two experiments, reported by
121
+ `compareExperiments()`, show it.
122
+ - Each middleware is proved in a real agent loop. For example:
123
+ - a charge that timed out is never retried;
124
+ - an approver who raises a refund past what the policy allows has the edit refused;
125
+ - two agents sharing a store share one thread budget;
126
+ - a 45,000-character tool result reaches the transcript as a preview and a file.
127
+ - A JWT from a test identity provider is verified against its key set and reaches an agent's tool
128
+ call as a `Principal` with its subject, tenant, roles, and scopes.
129
+ - The tool's writes land in that tenant's view of the store.
130
+ - The checkpoint records who ran the step.
131
+ - Another tenant's token gets 404 for the thread.
132
+ - Expired, wrong-audience, wrong-issuer, unsigned, and forged tokens are refused.
133
+ - The provider rotates its key with one refetch.
134
+ - On the stored support dataset, on real Postgres with full-text search and pgvector, hybrid retrieval
135
+ beats vector-only: recall@5 0.71 → 0.97 and mean reciprocal rank 0.51 → 0.71, both with a 95%
136
+ interval above zero. A hosted reranker and a local cross-encoder, compared in the same experiment,
137
+ each lift mean reciprocal rank to 0.97. One contract test runs against all four keyword indexes.
138
+ - An ingestion of 100,000 documents dies halfway inside a durable operation. Its retry starts at the
139
+ document the first attempt stopped on, and embeds no chunk that had finished. A second run after
140
+ ten documents are edited embeds exactly ten chunks and deletes their old versions.
141
+ - A deep agent completes a multi-step task on a fixture repository inside the reference sandbox. It
142
+ plans, runs the failing test, reads and edits the bug, verifies the fix, has a helper review it,
143
+ loads a skill, and records the change; the test then passes outside the agent. Along the way, a
144
+ 30,000-character log is offloaded to a file. The default policy refuses an unlisted command, a
145
+ chained command, a write outside the workspace, and an ungranted tool.
146
+
147
+ ## [2.2.0] - 2026-10-05
148
+
149
+ Production scale and safe upgrades. Every database schema is versioned, so an upgrade applies only
150
+ what changed, and a worker of the previous release keeps running beside the new one. A claim costs
151
+ the same however long the queue is, routing costs the same however many models are configured, a
152
+ circuit breaker counts every worker's failures, and work that several replicas race for is taken by
153
+ exactly one. One tenant model reaches every store, and a deployment can check itself. Nothing
154
+ changes until it is opted into, except races and leaks that are now closed.
155
+
156
+ ### Added
157
+
158
+ - **Versioned migrations.** Every Postgres and SQLite adapter's schema is a list of numbered
159
+ `SchemaMigration`s, recorded in `nexus_schema_migrations` with a checksum. `migrate()` applies what
160
+ is pending, under a lock that holds through a connection pool, and refuses a migration whose
161
+ statements changed after it ran (`SchemaMigrationError`). Version 1 is the schema 2.0 created, so an
162
+ existing database is recognized as it is. `applyPostgresMigrations()`, `postgresMigrationStatus()`,
163
+ `postgresMigrations()`, and their SQLite twins work on any list, from `nexus-ai-pro/postgres`,
164
+ `nexus-ai-pro/sqlite`, and the new `nexus-ai-pro/postgres/migrations` and
165
+ `nexus-ai-pro/sqlite/migrations`. `PostgresMigrateOptions.transaction` runs each migration in one
166
+ transaction on one connection, and `postgresMigration({ record: true })` records what the script
167
+ creates.
168
+ - **`nexus db status` and `nexus db migrate`**, through a module that exports your own client:
169
+ applied, pending, changed, and newer migrations, `--check` for a deploy step, and `--dry-run`.
170
+ - **Indexed dispatch.** Version 2 of the Postgres and SQLite operation schemas indexes queued work by
171
+ age, so a claim reads the oldest few rows instead of sorting the queue, and version 3 by tenant.
172
+ `RedisOperationStore` takes `index: true`: queued work in a sorted set by age and leased work in one
173
+ by lease expiry, kept in the same Lua call as the record, with `reindex()` to index what is already
174
+ stored. A claim reads the same handful of records with 100,000 operations queued as with 1,000.
175
+ - **A circuit breaker that counts across workers.** `shareObservations: true` adds each worker's
176
+ calls to a rolling window in the shared store, so nine failures over three workers count as nine
177
+ and the circuit opens on all of them. Checks stay synchronous; a worker learns the shared count on
178
+ its next sync. `CircuitStateStore.observe()` is implemented by the memory, Redis, and Postgres
179
+ stores, and `CircuitSnapshot.shared` reports the window.
180
+ - **`PostgresRollupStore`**, on the new `nexus-ai-pro/postgres/rollups`: hourly rollups beside the
181
+ traces, summed in one statement so two workers adding to one hour both count.
182
+ - **A routing index.** The auto-router plans once per configuration — candidates, allow and deny
183
+ lists, capability requirements as bitmasks, and strategy scores — and a request pays only for open
184
+ circuits and health. Over 1,000 models a warm route takes about 15 µs at p50 and 23 µs at p99.
185
+ - **One tenant model**, on the new `nexus-ai-pro/tenancy`: a tenant's view of every store —
186
+ `tenantStore()`, `tenantTraceStore()`, `tenantOperationStore()`, `tenantPromptStore()` (and with it
187
+ the context hub), `tenantDatasetStore()`, `tenantExperimentStore()`, `tenantVectorStore()`,
188
+ `tenantCache()`, `tenantAssetStore()`, `tenantRollupStore()` — each implementing the store's own
189
+ contract over any backend, and `tenantScope()` for all of them at once. Graph runs take a
190
+ `tenantId`: recorded on every checkpoint, read as `context.tenantId`, scoping `context.store`, and
191
+ refusing another tenant's resume, continue, fork, or edit. `graphAssistant()` passes the
192
+ principal's tenant. Operations carry `tenantId`, and `OperationStoreFilter.tenantId` narrows queue
193
+ reads. Model calls are traced with their `tenantId`, so rollups count model spend per tenant, and
194
+ `rateLimit.key: 'tenantId'` gives each tenant its own share of a client's rate limit.
195
+ - **Cache namespaces and outcomes.** `cache.namespace` keeps responses apart by tenant, environment,
196
+ model revision, and context version. Every response reports `meta.cache` — `hit`, `miss`, `stale`,
197
+ `bypass`, or `error`, with its layer and reason — and the request's trace records it.
198
+ `CompletionRequest.responseCache` skips the cache (`bypass`) or refreshes it (`refresh`).
199
+ - **`nexus doctor`**, and `diagnose()` on the new `nexus-ai-pro/doctor`: the Node.js version,
200
+ optional packages, provider credentials (by name only), database connectivity and pending or
201
+ changed migrations, Redis, lapsed leases and a stalled queue, the registry's age, stale provider
202
+ health, and process-local stores behind several replicas. It exits 1 on a failure, or with
203
+ `--strict` on a warning.
204
+ - **Runtime budgets.** `npm run bench:runtime`, part of the release gate, times fourteen paths — graph
205
+ creation, a 1,000-node compile, `Send`, checkpoints, routing at three registry sizes, a
206
+ 20-middleware agent, traces, the event stream, server event fan-out, claims, and cache lookups —
207
+ as ratios to a calibration loop, so a budget holds across machines, and checks that 100,000
208
+ checkpoints and 10,000 runs leave the heap where it was.
209
+ - **Workflow steps have what graph nodes have.** `timeout: { runMs, idleMs }` with
210
+ `context.heartbeat()`, an `onError` that decides once retries run out — recorded as `recovered`,
211
+ so a resume repeats neither the step nor its recovery — `stepDefaults`, a `control` that drains
212
+ between steps and ends in `GraphDrainedError`, and `tenantId`. A workflow the agent server serves
213
+ is handed off on a drain like a graph.
214
+ - **A function assistant hands its run off on a drain**: it checks `context.control?.draining`,
215
+ saves how far it got with `context.saveProgress()`, and throws `RunHandOffError`; the next worker
216
+ reads `context.progress` and carries on.
217
+
218
+ ### Changed
219
+
220
+ - `migrate()` on every Postgres and SQLite adapter resolves to a `MigrationResult` instead of
221
+ nothing, records what it applied, and loads the migration runner only when called.
222
+ - `nexus db sql` and `postgresMigration()` include the rollups table and the new indexes by
223
+ default; every statement stays idempotent.
224
+ - The root import grew by 11 KB, to 336 KB, with the routing plan, cache outcomes, and linked
225
+ signals; `nexus-ai-pro/graph` by 5 KB, to 78 KB, with tenant runs; and
226
+ `nexus-ai-pro/graph/functional` by 6 KB, to 32 KB, with step timeouts, recovery, drain, and
227
+ tenants.
228
+
229
+ ### Fixed
230
+
231
+ - **Two replicas could both start a run on one busy thread.** The busy check read and wrote
232
+ separately. A thread is now claimed through the operation store under a unique key with
233
+ compare-and-set, so exactly one run proceeds and the rest meet the busy policy. An `enqueue` start
234
+ still busy when `queueTimeoutMs` runs out is refused instead of running beside the other.
235
+ - **Two submissions under one idempotency key could both run** on the in-memory and Redis operation
236
+ stores, which overwrote the key's owner. Both now refuse a second record under a key another record
237
+ holds, as Postgres and SQLite do, and Redis checks and writes in one Lua call.
238
+ - A circuit breaker's calls recorded before its shared-state module finished loading were not
239
+ shared.
240
+ - **Security: the semantic response cache answered one tenant with another's response** when their
241
+ questions were alike, because it compared every entry regardless of tenant. A semantic match is
242
+ now kept to the request's tenant and cache namespace.
243
+ - A response cache that threw failed the request, and one that failed to store an answer failed it
244
+ after the provider had answered. Both are now reported in `meta.cache`, and the request goes on.
245
+ - The auto-router rebuilt the model registry for every candidate on every request: over 1,000 models
246
+ a request took most of a second.
247
+ - **Memory grew with every run that shared a signal.** `AbortSignal.any()`, which composed each task
248
+ attempt's signal, keeps a reference per call for as long as any source lives in Node 22, so a
249
+ server's or a worker's long-lived signal held about 5 KB per graph run, and every client operation
250
+ with a deadline held one too. Signals are now linked and released when their work ends; 10,000
251
+ runs leave the heap within 0.3 MB of where it started, down from 52 MB.
252
+
253
+ ### Proof
254
+
255
+ - A database created by the published 2.0.0 package upgrades with 2.2's migrations, on Postgres and
256
+ SQLite, and a published 2.1.0 worker and a 2.2 worker drain one queue together, nothing run twice.
257
+ - Ten workers race one thread, one idempotency key, one queue, one tenant's slots and budget, and one
258
+ circuit, on every shared store, and every invariant holds.
259
+ - A Redis claim reads the same number of records at 1,000 and 100,000 queued; Postgres and SQLite
260
+ plan a claim through the age index with no sort.
261
+ - The Redis stores' Lua runs for real in the tests, in a Lua VM over an in-memory Redis.
262
+ - The routing index decides exactly as the published 2.1.0 router on 300 random configurations, and
263
+ a warm route over 1,000 models stays under 100 µs at p50 and 1 ms at p99.
264
+ - A tenant-isolation suite runs against every storage adapter — memory, Redis, Postgres, SQLite, and
265
+ files — and fails on any cross-tenant get, list, search, delete, fork, resume, trace query, vector
266
+ search, or cache hit.
267
+ - `nexus doctor` reports each misconfiguration a fixture deployment is seeded with — an old Node.js,
268
+ no credentials, an old registry, pending migrations, an unreachable Redis, a lapsed lease and a
269
+ stalled queue, stale health, and memory stores behind three replicas — and passes it once fixed.
270
+ - A workflow and a function assistant are each drained on one worker and finished on the next, with
271
+ every step and every item run exactly once.
272
+
273
+ ### Not in this release, and why
274
+
275
+ - **The migration lock is a row with a lease, not a session advisory lock.** A session lock does not
276
+ hold through a connection pool or a serverless driver, where each statement may reach another
277
+ connection; a lease row does, everywhere. Without a `transaction` function, statements run one at a
278
+ time, which is safe because every bundled statement is idempotent.
279
+ - **The Redis dispatch index is opt-in**, since a 2.1 worker writes records without index entries;
280
+ `stats()` on Redis still reads every record.
281
+ - **Consecutive failures across workers are counted as failures since any worker's last success.**
282
+ Calls are reported in batches, so two workers' failures and successes in the same second may be
283
+ ordered differently than they happened.
284
+ - **Tenant views scope by prefix and stamp, not by a column**, so they work over every backend
285
+ unchanged; data written before a view was used is not the tenant's until it is copied under its
286
+ prefix.
287
+ - **Runtime budgets compare ratios to a calibration loop**, which catches a cliff, not a 10% drift;
288
+ the tolerance is three times the recorded ratio.
289
+
7
290
  ## [2.1.0] - 2026-10-04
8
291
 
9
292
  Fault-tolerant graphs and crash-safe observability. A graph now chooses when its checkpoints are
@@ -1494,7 +1777,9 @@ shared policy vocabulary is in place for a future release that revisits this.
1494
1777
  - URL fetching rejects unsafe private-network targets and limits response reads.
1495
1778
  - CLI and audit findings no longer disclose detected secret values.
1496
1779
 
1497
- [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.1.0...HEAD
1780
+ [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.3.0...HEAD
1781
+ [2.3.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.2.0...v2.3.0
1782
+ [2.2.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.1.0...v2.2.0
1498
1783
  [2.1.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.0.0...v2.1.0
1499
1784
  [2.0.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.25.0...v2.0.0
1500
1785
  [1.25.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.24.0...v1.25.0
package/README.md CHANGED
@@ -8,12 +8,12 @@ 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 73 KB, and installs nothing but this package.
11
+ loads 80 KB, and installs nothing but this package.
12
12
 
13
13
  - NPM: https://www.npmjs.com/package/nexus-ai-pro
14
14
  - GitHub: https://github.com/mkhitar-abrahamyan/nexus-ai
15
15
  - Contributing, testing, and release procedure: [CONTRIBUTING.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/CONTRIBUTING.md)
16
- - Delivered work and what is planned next: [ROADMAP.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/ROADMAP.md)
16
+ - What is planned next: [ROADMAP.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/ROADMAP.md)
17
17
  - For AI assistants: [llms.txt](https://raw.githubusercontent.com/mkhitar-abrahamyan/nexus-ai/main/llms.txt) indexes every guide, and [llms-full.txt](https://raw.githubusercontent.com/mkhitar-abrahamyan/nexus-ai/main/llms-full.txt) holds them all in one file
18
18
 
19
19
  ## Install
@@ -132,7 +132,8 @@ from the doc comments. The guides live in the repository, so these links go to G
132
132
  | [Providers and routing](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/providers.md) | The twelve completion providers, routing and failover, the model registry |
133
133
  | [Guardrails](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/security.md) | Injection, PII, secrets, schema validation, output redaction |
134
134
  | [Graphs](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/graphs.md) | Parallel branches, interrupts, checkpoints, time travel, subgraphs, caching, diagrams |
135
- | [Agents and tools](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/agents.md) | Graph-based agents with approvals and middleware, and the simple tool loop |
135
+ | [Agents and tools](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/agents.md) | Graph-based agents with approvals, capabilities and permission policies, a sandbox contract, the middleware catalog, and the simple tool loop |
136
+ | [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 |
136
137
  | [Long-term memory](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/memory.md) | Namespaced memory with semantic search, in memory or Redis |
137
138
  | [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 |
138
139
  | [Traces](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/tracing.md) | Run trees, queries, feedback, comparison, and alerts |
@@ -158,6 +159,7 @@ from the doc comments. The guides live in the repository, so these links go to G
158
159
  | [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 |
159
160
  | [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 |
160
161
  | [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 |
162
+ | [Tenancy](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/tenancy.md) | One tenant model: a tenant's view of every store, tenant graph runs, and the isolation suite |
161
163
  | [Workflows](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/workflows.md) | Ready-made chains and domain workflows |
162
164
  | [Command line](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/cli.md) | The `nexus` command |
163
165
  | [Packaging and install weight](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/docs/packaging.md) | Every entry point and what it costs |
@@ -200,7 +202,8 @@ npm run check:release
200
202
 
201
203
  `check` runs formatting and lint gates, source/test/example type checks, the build, unit and mock
202
204
  conformance tests, coverage thresholds, package import checks, and an external type-consumer test.
203
- `check:release` additionally verifies the dry-run tarball and a clean packed-package install.
205
+ `check:release` additionally holds the scheduling and runtime budgets — speed against a calibration
206
+ loop, and memory by retention — and verifies the dry-run tarball and a clean packed-package install.
204
207
 
205
208
  `npm run docs:check` fails when any public export, or any public member of an exported class,
206
209
  interface, or enum, has no doc comment, and `npm run docs:guides` fails when a guide does not name
@@ -216,8 +219,8 @@ npm run test:vectors:live
216
219
 
217
220
  ## Roadmap
218
221
 
219
- See [ROADMAP.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/ROADMAP.md) for what has
220
- shipped and what is planned. It is a design proposal, not a compatibility promise; the guarantees
222
+ See [ROADMAP.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/ROADMAP.md) for what is
223
+ planned, and [CHANGELOG.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/CHANGELOG.md) for what has shipped. It is a design proposal, not a compatibility promise; the guarantees
221
224
  live in [API_STABILITY.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/API_STABILITY.md).
222
225
 
223
226
  Shipped so far:
@@ -246,12 +249,19 @@ Shipped so far:
246
249
  every provider;
247
250
  - 2.1 fault tolerance and crash-safe observability: checkpoints written on your terms, recovery after
248
251
  retries, idle timeouts and a clean drain, traces that survive a crash and reach any OpenTelemetry
249
- backend, one event stream, and dashboard rollups.
252
+ backend, one event stream, and dashboard rollups;
253
+ - 2.2 production scale and safe upgrades: versioned database migrations, queue claims and routing
254
+ that cost the same at any size, a circuit breaker that counts across workers, one tenant model
255
+ across every store, and `nexus doctor` to check a deployment before it serves traffic;
256
+ - 2.3 agents that act safely, on production retrieval: capabilities and permission policies, a
257
+ sandbox contract, a middleware catalog, server authentication with one principal on every run,
258
+ keyword search in Postgres and Elasticsearch with rerankers, durable ingestion, and a deep-agent
259
+ preset.
250
260
 
251
261
  Upgrading from 1.x: `npx nexus migrate src --write` moves your imports, and
252
262
  [MIGRATING.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/MIGRATING.md) covers the rest.
253
- Next, 2.2 removes the scaling cliffs the guides document: versioned database migrations, indexed
254
- queue dispatch, a circuit breaker that counts across workers, a routing index, and one tenant model. The [roadmap](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/ROADMAP.md) lists the
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
255
265
  releases after it.
256
266
 
257
267
  ## Before Production
@@ -0,0 +1,39 @@
1
+ import type { ToolDefinition } from '../types/messages.js';
2
+ /**
3
+ * Capabilities: what a tool does to the world, written as strings a policy can decide on.
4
+ *
5
+ * | Capability | Means |
6
+ * | --- | --- |
7
+ * | `filesystem:read`, `filesystem:read:<path>` | Reads files, or one path |
8
+ * | `filesystem:write`, `filesystem:write:<path>` | Writes, creates, or deletes files |
9
+ * | `network`, `network:<host or URL>` | Opens a network connection, to a host when given |
10
+ * | `shell`, `shell:<command line>` | Runs a shell command |
11
+ * | `code` | Runs code it was given, as an interpreter does |
12
+ * | `<name>`, `<name>:<detail>` | Anything the application names, such as `payments:refund` |
13
+ *
14
+ * A tool that declares `capabilities: []` does nothing to the world; one that declares none at all
15
+ * is undeclared, and a policy decides on it as such.
16
+ */
17
+ /** One capability, split into its parts. */
18
+ export interface ParsedCapability {
19
+ /** `filesystem`, `network`, `shell`, `code`, or the application's own name. */
20
+ kind: string;
21
+ /** `read` or `write`, for the filesystem. */
22
+ access?: 'read' | 'write';
23
+ /** The path, host, command, or detail, when the capability names one. */
24
+ target?: string;
25
+ }
26
+ /** Splits a capability string into its kind, access, and target. */
27
+ export declare function parseCapability(capability: string): ParsedCapability;
28
+ /**
29
+ * The capabilities one call of a tool declares: its static list, or what its function computes from
30
+ * the call's arguments. `undefined` means undeclared — no list, or a function that threw — which is
31
+ * not the same as `[]`, a tool that declares it does nothing to the world.
32
+ */
33
+ export declare function capabilitiesOf(tool: Pick<ToolDefinition, 'capabilities'> | undefined, args: Record<string, unknown>): string[] | undefined;
34
+ /**
35
+ * Whether a capability is sensitive: anything but reading files. A pure tool declares
36
+ * `capabilities: []`; whatever it does declare, a write, a command, code, the network, or an
37
+ * effect of the application's own such as `payments`, changes the world or leaves the process.
38
+ */
39
+ export declare function isSensitiveCapability(capability: string): boolean;
@@ -0,0 +1,36 @@
1
+ export function parseCapability(capability) {
2
+ const text = capability.trim();
3
+ const colon = text.indexOf(':');
4
+ const kind = (colon < 0 ? text : text.slice(0, colon)).toLowerCase();
5
+ const rest = colon < 0 ? undefined : text.slice(colon + 1);
6
+ if (kind === 'filesystem') {
7
+ if (!rest)
8
+ return { kind };
9
+ const [access, ...path] = rest.split(':');
10
+ const parsed = { kind };
11
+ if (access === 'read' || access === 'write')
12
+ parsed.access = access;
13
+ const target = parsed.access ? path.join(':') : rest;
14
+ if (target)
15
+ parsed.target = target;
16
+ return parsed;
17
+ }
18
+ return rest === undefined || rest === '' ? { kind } : { kind, target: rest };
19
+ }
20
+ export function capabilitiesOf(tool, args) {
21
+ const declared = tool?.capabilities;
22
+ if (!declared)
23
+ return undefined;
24
+ if (typeof declared !== 'function')
25
+ return [...declared];
26
+ try {
27
+ return [...declared(args)];
28
+ }
29
+ catch {
30
+ return undefined;
31
+ }
32
+ }
33
+ export function isSensitiveCapability(capability) {
34
+ const parsed = parseCapability(capability);
35
+ return !(parsed.kind === 'filesystem' && parsed.access === 'read');
36
+ }