nexus-ai-pro 1.3.0 → 1.5.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 (105) hide show
  1. package/API_STABILITY.md +67 -1
  2. package/CHANGELOG.md +153 -1
  3. package/README.md +213 -6
  4. package/ROADMAP.md +385 -209
  5. package/dist/capabilities/negotiate.d.ts +38 -0
  6. package/dist/capabilities/negotiate.js +190 -0
  7. package/dist/core/nexus.d.ts +26 -0
  8. package/dist/core/nexus.js +81 -3
  9. package/dist/core/usage.d.ts +58 -0
  10. package/dist/core/usage.js +97 -0
  11. package/dist/embeddings/adapters.d.ts +66 -0
  12. package/dist/embeddings/adapters.js +246 -0
  13. package/dist/embeddings/errors.d.ts +36 -0
  14. package/dist/embeddings/errors.js +76 -0
  15. package/dist/embeddings/index.d.ts +8 -0
  16. package/dist/embeddings/index.js +7 -0
  17. package/dist/embeddings/manager.d.ts +62 -0
  18. package/dist/embeddings/manager.js +618 -0
  19. package/dist/embeddings/mock.d.ts +36 -0
  20. package/dist/embeddings/mock.js +64 -0
  21. package/dist/embeddings/models.d.ts +74 -0
  22. package/dist/embeddings/models.js +252 -0
  23. package/dist/embeddings/providers.d.ts +15 -0
  24. package/dist/embeddings/providers.js +10 -0
  25. package/dist/embeddings/register.d.ts +10 -0
  26. package/dist/embeddings/register.js +39 -0
  27. package/dist/index.d.ts +16 -8
  28. package/dist/index.js +10 -4
  29. package/dist/models/registry.d.ts +60 -1
  30. package/dist/models/registry.js +82 -5
  31. package/dist/ops/rate-limiter.d.ts +11 -2
  32. package/dist/optimizer/cost.d.ts +10 -0
  33. package/dist/optimizer/cost.js +54 -2
  34. package/dist/providers/anthropic.js +169 -32
  35. package/dist/providers/cohere.js +4 -0
  36. package/dist/providers/google.d.ts +9 -0
  37. package/dist/providers/google.js +81 -37
  38. package/dist/providers/ollama.d.ts +2 -0
  39. package/dist/providers/ollama.js +27 -29
  40. package/dist/providers/openai.d.ts +8 -1
  41. package/dist/providers/openai.js +121 -32
  42. package/dist/security/schema-validator.d.ts +4 -4
  43. package/dist/testing/embedding-provider-conformance.d.ts +33 -0
  44. package/dist/testing/embedding-provider-conformance.js +117 -0
  45. package/dist/types/capabilities.d.ts +34 -0
  46. package/dist/types/capabilities.js +1 -0
  47. package/dist/types/config.d.ts +29 -1
  48. package/dist/types/embeddings.d.ts +230 -0
  49. package/dist/types/embeddings.js +1 -0
  50. package/dist/types/messages.d.ts +75 -0
  51. package/dist/types/planning.d.ts +7 -0
  52. package/dist/types/providers.d.ts +75 -0
  53. package/dist/types/providers.js +90 -1
  54. package/dist/types/response.d.ts +48 -1
  55. package/dist-cjs/capabilities/negotiate.d.ts +38 -0
  56. package/dist-cjs/capabilities/negotiate.js +195 -0
  57. package/dist-cjs/core/nexus.d.ts +26 -0
  58. package/dist-cjs/core/nexus.js +81 -3
  59. package/dist-cjs/core/usage.d.ts +58 -0
  60. package/dist-cjs/core/usage.js +104 -0
  61. package/dist-cjs/embeddings/adapters.d.ts +66 -0
  62. package/dist-cjs/embeddings/adapters.js +254 -0
  63. package/dist-cjs/embeddings/errors.d.ts +36 -0
  64. package/dist-cjs/embeddings/errors.js +86 -0
  65. package/dist-cjs/embeddings/index.d.ts +8 -0
  66. package/dist-cjs/embeddings/index.js +42 -0
  67. package/dist-cjs/embeddings/manager.d.ts +62 -0
  68. package/dist-cjs/embeddings/manager.js +622 -0
  69. package/dist-cjs/embeddings/mock.d.ts +36 -0
  70. package/dist-cjs/embeddings/mock.js +68 -0
  71. package/dist-cjs/embeddings/models.d.ts +74 -0
  72. package/dist-cjs/embeddings/models.js +265 -0
  73. package/dist-cjs/embeddings/providers.d.ts +15 -0
  74. package/dist-cjs/embeddings/providers.js +11 -0
  75. package/dist-cjs/embeddings/register.d.ts +10 -0
  76. package/dist-cjs/embeddings/register.js +42 -0
  77. package/dist-cjs/index.d.ts +16 -8
  78. package/dist-cjs/index.js +48 -4
  79. package/dist-cjs/models/registry.d.ts +60 -1
  80. package/dist-cjs/models/registry.js +85 -4
  81. package/dist-cjs/ops/rate-limiter.d.ts +11 -2
  82. package/dist-cjs/optimizer/cost.d.ts +10 -0
  83. package/dist-cjs/optimizer/cost.js +56 -3
  84. package/dist-cjs/providers/anthropic.js +168 -31
  85. package/dist-cjs/providers/cohere.js +4 -0
  86. package/dist-cjs/providers/google.d.ts +9 -0
  87. package/dist-cjs/providers/google.js +81 -37
  88. package/dist-cjs/providers/ollama.d.ts +2 -0
  89. package/dist-cjs/providers/ollama.js +27 -29
  90. package/dist-cjs/providers/openai.d.ts +8 -1
  91. package/dist-cjs/providers/openai.js +121 -32
  92. package/dist-cjs/security/schema-validator.d.ts +4 -4
  93. package/dist-cjs/testing/embedding-provider-conformance.d.ts +33 -0
  94. package/dist-cjs/testing/embedding-provider-conformance.js +121 -0
  95. package/dist-cjs/types/capabilities.d.ts +34 -0
  96. package/dist-cjs/types/capabilities.js +2 -0
  97. package/dist-cjs/types/config.d.ts +29 -1
  98. package/dist-cjs/types/embeddings.d.ts +230 -0
  99. package/dist-cjs/types/embeddings.js +2 -0
  100. package/dist-cjs/types/messages.d.ts +75 -0
  101. package/dist-cjs/types/planning.d.ts +7 -0
  102. package/dist-cjs/types/providers.d.ts +75 -0
  103. package/dist-cjs/types/providers.js +91 -2
  104. package/dist-cjs/types/response.d.ts +48 -1
  105. package/package.json +67 -2
package/API_STABILITY.md CHANGED
@@ -26,7 +26,48 @@ Source files, `dist` file paths, examples, test helpers that are not exported, a
26
26
  - Security fixes may tighten validation or blocking behavior in a patch release when preserving the old behavior would leave users exposed.
27
27
  - Provider behavior can change when an upstream API changes; Nexus will preserve its normalized request and response contracts where possible.
28
28
 
29
- The package is ESM-only and supports Node.js 22 or newer. CommonJS `require()` and deep imports into `dist` or `src` are not supported.
29
+ ## Module formats
30
+
31
+ Since 1.2.0 the package ships parallel ESM and CommonJS builds and supports Node.js 22 or newer.
32
+ Both are covered by this policy:
33
+
34
+ - every export subpath carries `import` and `require` conditions, each resolving to its own build and
35
+ its own declarations, so a CommonJS consumer never lands on ESM declarations;
36
+ - `typesVersions` maps each subpath to its CommonJS declarations, so subpath types resolve under
37
+ `moduleResolution: "node10"` without a consumer migrating its `tsconfig.json`;
38
+ - dropping either build, or removing a subpath from either condition, requires a major release.
39
+
40
+ Deep imports into `dist`, `dist-cjs`, or `src` are not supported. Import only from `nexus-ai-pro` or
41
+ one of its explicit subpaths.
42
+
43
+ ## Capability negotiation stage
44
+
45
+ `negotiateCompletionRequest()`, `NexusCapabilityError`, `CapabilityPolicy`, `CapabilityWarning`, and
46
+ the `nexus-ai-pro/capabilities` subpath are public and follow the 1.x rules.
47
+
48
+ Which options a given model accepts is registry data, not a compatibility guarantee. A model's
49
+ declared capabilities, cache lifetimes, reasoning effort levels, and prices can change in a minor
50
+ release when a provider changes its lineup, and new normalized warning entries may be added. What is
51
+ guaranteed is the behavior of each policy: `strict` throws before the provider call, `warn` records a
52
+ warning and continues, `off` sends the request unchanged, and an option a model does not mention is
53
+ always passed through rather than refused.
54
+
55
+ `ResponseMeta.usage` and `ResponseMeta.cost` are public normalized shapes. Reported token counts and
56
+ prices originate with the provider or with bundled defaults and are not compatibility guarantees;
57
+ `cost.basis` distinguishes the two. `ResponseMeta.estimatedCost` is deprecated in favor of
58
+ `cost.amount` and will be removed in the next major release.
59
+
60
+ ## Telephony API stage
61
+
62
+ The `nexus-ai-pro/telephony`, `telephony/twilio`, and `telephony/realtime-bridge` subpaths are public
63
+ and follow the 1.x rules. This covers `TelephonyManager`, the `TelephonyProvider` contract, the
64
+ normalized call, media-stream, status-callback, and phone-number types, the TwiML helpers, and the
65
+ realtime bridge.
66
+
67
+ Provider payloads under `raw`, TwiML markup details beyond the documented helper options, webhook
68
+ signature formats, upstream call-status vocabularies, and carrier behavior are controlled by the
69
+ provider and are not normalized guarantees. `TelephonyProvider` methods are optional by design, so a
70
+ provider adapter may implement any subset; adding a new optional method is a minor change.
30
71
 
31
72
  ## Realtime API stage
32
73
 
@@ -65,6 +106,31 @@ exactly-once provider execution. Those capabilities will use additive adapters a
65
106
  `MemoryAssetStore` is likewise process-local and does not promise cross-process durability or shared
66
107
  tenant state.
67
108
 
109
+ ## Embeddings API stage
110
+
111
+ The `nexus-ai-pro/embeddings` family is public and follows the 1.x rules, including the explicit
112
+ `embeddings/adapters`, `embeddings/mock`, and `embeddings/models` subpaths. This covers
113
+ `EmbeddingManager`, `ai.embed()` and `ai.embedOne()`, the `EmbeddingsProvider` contract, the
114
+ normalized request, embedding, and meta shapes, the error subclasses, and `toEmbeddingFunction()`.
115
+
116
+ `EmbeddingsProvider` is a distinct contract from the pre-existing `EmbeddingProvider` function type
117
+ used by `MemoryVectorStore`, the semantic cache, and RAG ingestion. That function type is unchanged
118
+ and remains supported; `toEmbeddingFunction()` converts one into the other.
119
+
120
+ Two behaviors are guaranteed and differ deliberately from completions. Vectors are returned in input
121
+ order regardless of how the request was split, deduplicated, or served from cache. And an option the
122
+ target model or adapter declares it cannot honor is **refused**, not dropped, because a vector built
123
+ with different dimensions or a different input type is silently incompatible with vectors already in
124
+ a store. An option nothing in the registry describes is still passed through, on the same
125
+ absence-means-unknown rule the completion path uses.
126
+
127
+ Embedding model entries are registry data, not compatibility guarantees. Bundled dimensions, input
128
+ limits, batch limits, and prices can change in a minor release when a provider changes its lineup,
129
+ and are overridable through `embeddings.models.registry`. Which concrete model an alias such as
130
+ `embed-quality` resolves to can also change in a minor release: the vectors two targets produce are
131
+ not interchangeable, so pin a concrete model whenever a stored index must stay valid across
132
+ upgrades. Provider payloads under `raw` and reported token counts are controlled by the provider.
133
+
68
134
  ## Deprecation process
69
135
 
70
136
  Deprecated APIs are marked with `@deprecated` in declarations and described in the changelog. Removals
package/CHANGELOG.md CHANGED
@@ -4,6 +4,152 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.5.0] - 2026-08-31
8
+
9
+ Embeddings become a first-class operation. `ai.embed()` gets the routing, caching, batching, budget,
10
+ retry, audit, and metrics that until now only completions had, so embedding spend is visible next to
11
+ completion spend instead of leaving the platform entirely. Everything here is additive.
12
+
13
+ ### Added
14
+
15
+ - **`ai.embed()` and `ai.embedOne()`.** A provider-neutral embeddings operation family behind
16
+ `EmbeddingManager`, reached through `ai.embeddings` or the new `nexus-ai-pro/embeddings` subpath.
17
+ The manager is built on first access, so a runtime that never embeds pays nothing for it.
18
+ - **Adapters for OpenAI, Google, Cohere, Mistral, and Ollama**, plus any OpenAI-compatible
19
+ `/embeddings` server through `OpenAIEmbeddingProvider`'s `baseUrl` and `providerName`. They are
20
+ registered automatically from the provider credentials already in `providers`, so a configured
21
+ chat provider makes `ai.embedOne('text')` work with no embedding-specific configuration. Explicit
22
+ registration through `registerEmbeddingProvider()` always wins, and auto-registration can be
23
+ turned off with `embeddings.autoRegisterProviders: false`.
24
+ - **Batching, deduplication, and per-input caching.** A batch larger than the model's limit is split
25
+ and run with bounded concurrency, and the vectors still come back in input order. Repeated texts
26
+ within one request are answered by a single provider call, and caching is keyed per input, so a
27
+ partially repeated batch only sends the texts it has not seen. `meta.batches`, `meta.cachedInputs`,
28
+ and `meta.deduplicatedInputs` report what actually happened.
29
+ - **Structured usage and numeric cost for embeddings.** `meta.usage` and `meta.cost` reuse the same
30
+ `TokenUsage` and `ResponseCost` shapes as completions, priced from a new embedding model registry.
31
+ When a provider reports no token counts — Google's `batchEmbedContents` does not — tokens are
32
+ estimated locally rather than reported as zero, so a call is never priced at nothing.
33
+ - **An embedding model registry** with dimensions, supported truncation sizes, input limits, batch
34
+ limits, and prices for eleven models across five providers, with aliases (`auto`, `embed-fast`,
35
+ `embed-quality`, `embed-multilingual`, `embed-local`) and full override through
36
+ `embeddings.models.registry`. It is deliberately separate from `KNOWN_MODELS`, because completion
37
+ routing scores models on context window, output price, and tool support, none of which an
38
+ embedding model has.
39
+ - **Capability refusal.** An unsupported `dimensions`, `inputType`, `encodingFormat`, or `truncate`
40
+ value throws `EmbeddingCapabilityError` before the provider call. Embeddings refuse rather than
41
+ drop: a vector built with different dimensions is silently incompatible with the vectors already
42
+ in a store, and the mismatch only surfaces later as unexplained retrieval quality loss. An option
43
+ the registry says nothing about is still passed through, and `providerOptions` reaches the
44
+ provider body untouched.
45
+ - **`toEmbeddingFunction()`**, which adapts the family to the plain `EmbeddingProvider` function that
46
+ `MemoryVectorStore`, the semantic cache, and RAG ingestion already accept. An existing vector store
47
+ gains routing, caching, retry, and metrics without changing its own contract.
48
+ - **`normalize`** for unit-length vectors, applied locally when the provider does not already return
49
+ them, and reapplied after a locally truncated vector loses its length.
50
+ - **A deterministic `MockEmbeddingProvider`** and `runEmbeddingProviderConformance()`, which checks
51
+ one vector per input, input order, consistent width, optional determinism, and an honored abort.
52
+ - New subpaths `nexus-ai-pro/embeddings`, `embeddings/adapters`, `embeddings/mock`, and
53
+ `embeddings/models`.
54
+
55
+ ### Changed
56
+
57
+ - `RateLimiter.check()` accepts the new structural `RateLimitedRequest` instead of a
58
+ `CompletionRequest`, so one limiter instance and one budget cover every operation family.
59
+ `CompletionRequest` still satisfies it, so no call site changes.
60
+ - Coverage rose from 82.6% lines / 68.9% branches / 78.2% functions to 85.7% / 70.9% / 80.8%, with
61
+ 65 new tests. `embeddings/providers.ts`, previously the weakest file in the package at 37% lines,
62
+ is now at 99%.
63
+
64
+ ### Notes
65
+
66
+ - Bundled embedding dimensions and prices are defaults, not financial truth; override them through
67
+ `embeddings.models.registry` when exact numbers matter.
68
+ - Which concrete model an embedding alias resolves to can change in a minor release. Two targets do
69
+ not produce interchangeable vectors, so pin a concrete model when a stored index must stay valid
70
+ across upgrades.
71
+
72
+ ## [1.4.0] - 2026-08-25
73
+
74
+ Request parity and cost truth: the completion request can now express what current models actually
75
+ do, and the response reports what the call really cost. Every addition is an optional field or a new
76
+ union member, so existing code is unaffected.
77
+
78
+ ### Added
79
+
80
+ - **Provider prompt caching.** `CompletionRequest.cache` selects `off`, `auto`, or `explicit` mode
81
+ with a `5m` or `1h` lifetime, and `Message.cache` / `ToolDefinition.cache` mark the end of a
82
+ cacheable prefix. The Anthropic adapter translates marks into `cache_control` breakpoints,
83
+ respecting the four-breakpoint limit by keeping the deepest marks, and every adapter reports what
84
+ the provider actually reused.
85
+ - **Reasoning controls.** `CompletionRequest.reasoning` carries `effort`, `maxTokens`, and `summary`.
86
+ These map to OpenAI `reasoning_effort` and the Responses `reasoning` field, Anthropic extended
87
+ thinking with a token budget, and Gemini `thinkingConfig`. A new `'reasoning'` `StreamChunk`
88
+ variant carries reasoning summaries, kept separate from `'text'` so existing consumers that switch
89
+ on chunk type see no change.
90
+ - **Tool and sampling controls.** `toolChoice`, `parallelToolCalls`, `seed`, `topK`,
91
+ `frequencyPenalty`, and `presencePenalty` on `CompletionRequest`, mapped per provider.
92
+ - **Structured usage and numeric cost.** `ResponseMeta.usage` reports input, output, cached-read,
93
+ cached-write, and reasoning tokens; `ResponseMeta.cost` reports a numeric amount with a currency
94
+ and an `estimated` or `reported` basis. Cached reads and cache writes are priced at their own
95
+ rates, with a long-lived write costing more than a short-lived one. `models.cachePricing`
96
+ overrides the bundled multipliers.
97
+ - **Capability negotiation.** `negotiateCompletionRequest()` reconciles a request against the routed
98
+ model under a `strict`, `warn`, or `off` policy, set through `capabilities.policy` or per request
99
+ through `capabilityPolicy`. Refused options are reported on `ResponseMeta.capabilityWarnings` and
100
+ in `plan()` warnings. An option the registry does not mention is passed through: absence means
101
+ unknown, not unsupported, so an application-registered model is never restricted by fields it does
102
+ not declare, and `off` guarantees a newer provider feature is never blocked by stale metadata.
103
+ - **Registry provenance.** Model entries accept `verifiedAt` and `source`; `MODEL_ALIAS_METADATA`
104
+ reports each alias's stage and whether its target floats between releases. `describeModel()`,
105
+ `checkRegistryFreshness()`, and `assertRegistryFreshness()` make drift visible instead of silent.
106
+ - **Streamed usage on OpenAI.** `stream_options.include_usage` is requested for OpenAI and Azure, so
107
+ a streamed response reports real tokens and cost rather than zeros. Other OpenAI-compatible
108
+ servers opt in with the new `streamUsage` provider option.
109
+ - A `nexus-ai-pro/capabilities` subpath for the negotiation API.
110
+
111
+ ### Fixed
112
+
113
+ - Claude 5 models were declared as non-reasoning. The registry decided extended-thinking support by
114
+ looking for a `4` in the family name, which is absent from `claude-sonnet-5.0`, `claude-haiku-5.0`,
115
+ and `claude-fable-5.0`. Support is now derived from the family version, so every release from 3.7
116
+ onward is reported correctly, together with the thinking budget each model can accept.
117
+ - The Anthropic adapter priced every completion at a hardcoded $0.003/$0.015 per 1k tokens instead of
118
+ using the model registry, so cost was wrong for every Claude model except one. All adapters now
119
+ share one pricing path.
120
+ - Streamed Anthropic and Google responses reported zero tokens and `$0.00`. Both now carry the
121
+ provider's own usage totals on the final chunk.
122
+ - Google reasoning summaries could be concatenated into visible output. Parts marked `thought` are
123
+ excluded from content and emitted as `'reasoning'` chunks instead.
124
+ - A chat stream that ended without a finish reason produced no `done` chunk, leaving consumers
125
+ without terminal metadata.
126
+
127
+ ### Changed
128
+
129
+ - `ResponseMeta.estimatedCost` is deprecated in favor of the numeric `cost` object. It is still
130
+ populated and stays until the next major release. The metrics path no longer parses money out of
131
+ its own display string.
132
+ - `ResponseMeta.tokensInput` keeps its original meaning of every prompt token; the uncached share
133
+ billed at the standard rate is `usage.inputTokens`. Adapters whose provider folds cached tokens
134
+ into one prompt total subtract them so no token is priced twice.
135
+ - `resolveModel()` no longer merges the bundled and application registries into a new object on each
136
+ call. It reads both maps directly, which removes a per-request allocation from the hot path;
137
+ `getModelRegistry()` and `getModelAliases()` are unchanged as the merged views.
138
+ - Coverage gates raised from 70/60/60 to 82% lines, 67% branches, and 73% functions, with new tests
139
+ for the agent loop, the rules router, the evaluation metric library, and the Google, Ollama, and
140
+ Cohere adapters. The gates sit a couple of points below the lowest figure the supported Node
141
+ versions report, because Node 22 and Node 24 do not count functions identically: the same suite
142
+ measures 78.7% on Node 22 and 75.9% on Node 24. A gate set from one version alone fails on the
143
+ other.
144
+
145
+ ### Notes
146
+
147
+ Image operations keep their existing strict capability behavior. An unsupported image option changes
148
+ the artifact that comes back, so silently dropping one is worse than refusing the request; the
149
+ shared policy vocabulary is in place for a future release that revisits this.
150
+
151
+ ## [1.3.0] - 2026-08-17
152
+
7
153
  ### Added
8
154
 
9
155
  - SMS routing on `updatePhoneNumber`. `UpdatePhoneNumberRequest` now carries `smsUrl`, `smsMethod`,
@@ -120,6 +266,12 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
120
266
  - URL fetching rejects unsafe private-network targets and limits response reads.
121
267
  - CLI and audit findings no longer disclose detected secret values.
122
268
 
123
- [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.0.0...HEAD
269
+ [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.5.0...HEAD
270
+ [1.5.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.4.0...v1.5.0
271
+ [1.4.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.3.0...v1.4.0
272
+ [1.3.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.2.1...v1.3.0
273
+ [1.2.1]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.2.0...v1.2.1
274
+ [1.2.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.1.0...v1.2.0
275
+ [1.1.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v1.0.0...v1.1.0
124
276
  [1.0.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/releases/tag/v1.0.0
125
277
  [0.9.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/releases/tag/v0.9.0
package/README.md CHANGED
@@ -83,6 +83,7 @@ You can still pass a plain `NexusAIConfig` to `new NexusAI(...)` when you want f
83
83
  - estimate tokens and cost before provider calls
84
84
  - cache exact or semantically similar prompts
85
85
  - add tools, agents, RAG context, evals, batch jobs, and queues
86
+ - embed text through the same routing, caching, batching, budget, retry, and metrics as a completion
86
87
  - build persistent realtime voice agents with interruption, live tools, and normalized conversation state
87
88
  - keep TypeScript types around every request and response
88
89
 
@@ -239,7 +240,112 @@ console.log(plan.fitsContext);
239
240
  console.log(plan.warnings);
240
241
  ```
241
242
 
242
- `plan()` does not call a provider. It estimates route, token use, context fit, cost, guardrail findings, and warnings.
243
+ `plan()` does not call a provider. It estimates route, token use, context fit, cost, guardrail findings, and warnings, including any option the routed model cannot honor.
244
+
245
+ ### Reasoning
246
+
247
+ ```ts
248
+ const response = await ai.complete({
249
+ model: 'auto',
250
+ messages: [{ role: 'user', content: 'Find the bug in this migration plan.' }],
251
+ reasoning: { effort: 'high', summary: 'auto' },
252
+ });
253
+
254
+ console.log(response.meta.usage?.reasoningTokens);
255
+ ```
256
+
257
+ `effort` is the portable control. It maps to OpenAI `reasoning_effort` and the Responses `reasoning`
258
+ field, Anthropic extended thinking, and Gemini `thinkingConfig`. Use `reasoning.maxTokens` when you
259
+ want to set a thinking budget directly instead of by level.
260
+
261
+ Requesting a summary emits `reasoning` stream chunks, which stay separate from visible output:
262
+
263
+ ```ts
264
+ for await (const chunk of ai.stream({ model: 'auto', messages, reasoning: { summary: 'auto' } })) {
265
+ if (chunk.type === 'reasoning') process.stderr.write(chunk.content ?? '');
266
+ if (chunk.type === 'text') process.stdout.write(chunk.content ?? '');
267
+ }
268
+ ```
269
+
270
+ ### Prompt Caching
271
+
272
+ Providers charge far less for a prompt prefix they have already processed. `mode: 'auto'` is the
273
+ default and reports whatever the provider reused on its own. `mode: 'explicit'` sends caller-placed
274
+ breakpoints to providers that accept them, such as Anthropic:
275
+
276
+ ```ts
277
+ const response = await ai.complete({
278
+ model: 'anthropic/best',
279
+ cache: { mode: 'explicit', ttl: '1h' },
280
+ messages: [
281
+ { role: 'system', content: longPolicyDocument, cache: true },
282
+ { role: 'user', content: 'Does clause 14 apply here?' },
283
+ ],
284
+ });
285
+
286
+ console.log(response.meta.usage?.cachedReadTokens);
287
+ console.log(response.meta.cost?.amount);
288
+ ```
289
+
290
+ Mark the end of the stable prefix, not every message. A provider caps how many breakpoints it
291
+ accepts (Anthropic allows four); when there are more marks than slots, the deepest ones are kept,
292
+ because a deeper breakpoint caches strictly more of the prompt.
293
+
294
+ ### Usage and Cost
295
+
296
+ Every response carries a numeric cost and a full token breakdown:
297
+
298
+ ```ts
299
+ const { usage, cost } = response.meta;
300
+
301
+ console.log(usage?.inputTokens); // billed at the standard input rate
302
+ console.log(usage?.cachedReadTokens); // served from the provider cache
303
+ console.log(usage?.cachedWriteTokens); // written into the provider cache
304
+ console.log(usage?.reasoningTokens); // share of output spent on reasoning
305
+ console.log(cost?.amount, cost?.currency, cost?.basis);
306
+ ```
307
+
308
+ `meta.tokensInput` keeps its original meaning of every prompt token, cached or not.
309
+ `meta.estimatedCost` is deprecated in favor of `cost.amount`. Bundled prices are defaults, not
310
+ financial truth; override them with `models.registry`, or adjust cache rates with
311
+ `models.cachePricing`.
312
+
313
+ ### Capability Negotiation
314
+
315
+ Requests are reconciled against the routed model before the provider is called:
316
+
317
+ ```ts
318
+ const ai = new NexusAI({
319
+ providers: { openai: { apiKey: process.env.OPENAI_API_KEY! } },
320
+ capabilities: { policy: 'warn' }, // 'strict' | 'warn' (default) | 'off'
321
+ });
322
+
323
+ const response = await ai.complete({ model: 'auto', messages, seed: 7 });
324
+ console.log(response.meta.capabilityWarnings);
325
+ ```
326
+
327
+ - `strict` throws a `NexusCapabilityError` before spending anything.
328
+ - `warn` drops or clamps the option and records it on `meta.capabilityWarnings`.
329
+ - `off` sends the request exactly as written.
330
+
331
+ An option a model does not declare is always passed through — absence means the registry does not
332
+ know, not that the provider refuses — so a model you register yourself is never restricted by fields
333
+ it omits. Set `capabilityPolicy: 'off'` on a single request to reach a provider feature that is
334
+ newer than the bundled registry.
335
+
336
+ Registry provenance is inspectable, and a release check can fail on stale data:
337
+
338
+ ```ts
339
+ import { describeModel, assertRegistryFreshness } from 'nexus-ai-pro';
340
+
341
+ console.log(describeModel('anthropic/best'));
342
+ // { model: 'claude-opus-4-8', verifiedAt: '…', alias: { stage: 'stable', floating: true }, … }
343
+
344
+ assertRegistryFreshness({ maxAgeDays: 120 });
345
+ ```
346
+
347
+ A `floating` alias resolves by intent rather than to a pinned model, so its target can change between
348
+ releases. Pin the resolved model when a run has to be reproducible.
243
349
 
244
350
  ### Streaming
245
351
 
@@ -254,7 +360,9 @@ for await (const chunk of stream) {
254
360
  }
255
361
  ```
256
362
 
257
- All providers normalize streaming chunks to `text`, `tool_call`, `done`, or `error`.
363
+ All providers normalize streaming chunks to `text`, `reasoning`, `tool_call`, `done`, or `error`.
364
+ Reasoning summaries only arrive when the request asks for them, so a consumer that switches on
365
+ `chunk.type` keeps receiving visible output only.
258
366
 
259
367
  When security is enabled, output streams are correctness-first: Nexus buffers and validates the
260
368
  complete output before yielding any chunk, up to 1 MiB or 100,000 chunks. This prevents secrets,
@@ -309,6 +417,98 @@ const response = await ai.completeVerified(
309
417
  );
310
418
  ```
311
419
 
420
+ ## Embeddings
421
+
422
+ `ai.embed()` is a first-class operation, not a helper: it gets the same routing, caching, batching,
423
+ budget, retry, audit, and metrics as a completion, so embedding spend shows up next to completion
424
+ spend instead of being invisible.
425
+
426
+ The smallest call is one line, and needs no embedding-specific configuration — adapters are derived
427
+ from the provider credentials already in `providers`:
428
+
429
+ ```ts
430
+ import { NexusAI } from 'nexus-ai-pro';
431
+
432
+ const ai = new NexusAI({ providers: { openai: { apiKey: process.env.OPENAI_API_KEY! } } });
433
+
434
+ const vector = await ai.embedOne('Provider-neutral embeddings.');
435
+ ```
436
+
437
+ A batch returns vectors in input order regardless of how many provider calls the model's batch
438
+ limit required, and reports what the call cost:
439
+
440
+ ```ts
441
+ const response = await ai.embed({
442
+ input: documents.map((document) => document.text),
443
+ model: 'embed-quality',
444
+ inputType: 'document',
445
+ normalize: true,
446
+ });
447
+
448
+ response.vectors; // number[][], in input order
449
+ response.meta.batches; // provider calls the split required
450
+ response.meta.usage.inputTokens; // reported by the provider, or estimated when it reports none
451
+ response.meta.cost.amount; // numeric, priced from the embedding registry
452
+ response.meta.cachedInputs; // inputs answered from cache
453
+ response.meta.deduplicatedInputs; // repeated inputs answered from one call
454
+ ```
455
+
456
+ Caching is per input rather than per request, so a partially repeated batch only sends the texts it
457
+ has not seen. Repeated texts inside one batch are collapsed into a single provider call:
458
+
459
+ ```ts
460
+ const ai = new NexusAI({
461
+ providers: { openai: { apiKey: process.env.OPENAI_API_KEY! } },
462
+ embeddings: {
463
+ cache: { enabled: true, ttlSeconds: 86_400 },
464
+ costBudget: { enabled: true, maxEstimatedCost: 5 },
465
+ retry: { enabled: true, maxRetries: 2 },
466
+ fallback: [{ provider: 'cohere', model: 'embed-v4.0' }],
467
+ },
468
+ });
469
+ ```
470
+
471
+ Unlike a completion, an unsupported embedding option is **refused rather than dropped**. A vector
472
+ built with different dimensions or a different input type is silently incompatible with the vectors
473
+ already in a store, and the mismatch only surfaces later as unexplained retrieval quality loss:
474
+
475
+ ```ts
476
+ await ai.embed({ input: 'x', model: 'embed-english-v3.0', dimensions: 512 });
477
+ // EmbeddingCapabilityError: model has a fixed size of 1024 dimensions
478
+ ```
479
+
480
+ An option the registry says nothing about is still passed through, because an undeclared capability
481
+ means unknown rather than unsupported, and `providerOptions` reaches the provider body untouched for
482
+ anything the neutral contract does not model yet.
483
+
484
+ Existing vector stores keep their contract. `toEmbeddingFunction()` adapts the family to the plain
485
+ function `MemoryVectorStore`, the semantic cache, and RAG ingestion already accept:
486
+
487
+ ```ts
488
+ import { MemoryVectorStore, toEmbeddingFunction } from 'nexus-ai-pro';
489
+
490
+ const store = new MemoryVectorStore(toEmbeddingFunction(ai, { inputType: 'document' }));
491
+ ```
492
+
493
+ Bundled adapters cover OpenAI (and any OpenAI-compatible `/embeddings` server), Google, Cohere,
494
+ Mistral, and Ollama. A custom adapter implements `EmbeddingsProvider` and is checked against the
495
+ neutral contract by `runEmbeddingProviderConformance()`:
496
+
497
+ ```ts
498
+ import { OpenAIEmbeddingProvider } from 'nexus-ai-pro/embeddings/adapters';
499
+ import { MockEmbeddingProvider } from 'nexus-ai-pro/embeddings/mock';
500
+
501
+ ai.registerEmbeddingProvider('gateway', new OpenAIEmbeddingProvider({
502
+ apiKey: process.env.GATEWAY_KEY!,
503
+ baseUrl: 'https://gateway.internal/v1',
504
+ providerName: 'gateway',
505
+ }));
506
+ ai.registerEmbeddingProvider('mock', new MockEmbeddingProvider());
507
+ ```
508
+
509
+ Bundled embedding dimensions and prices are defaults, not financial truth. Override them through
510
+ `embeddings.models.registry` when exact numbers matter.
511
+
312
512
  ## Image Generation and Editing (Experimental)
313
513
 
314
514
  Image operations use a separate provider-neutral manager because generated assets have different
@@ -1022,9 +1222,14 @@ import { NexusAI } from 'nexus-ai-pro/core';
1022
1222
  import { createNexusConfig } from 'nexus-ai-pro/config';
1023
1223
  import { TokenOptimizer } from 'nexus-ai-pro/optimizer';
1024
1224
  import { ContextWindowManager } from 'nexus-ai-pro/context';
1225
+ import { negotiateCompletionRequest } from 'nexus-ai-pro/capabilities';
1025
1226
  import { guardrailPolicy } from 'nexus-ai-pro/security';
1026
1227
  import { RedisCacheAdapter } from 'nexus-ai-pro/cache';
1027
1228
  import { DeepSeekProvider } from 'nexus-ai-pro/providers/deepseek';
1229
+ import { EmbeddingManager } from 'nexus-ai-pro/embeddings';
1230
+ import { OpenAIEmbeddingProvider } from 'nexus-ai-pro/embeddings/adapters';
1231
+ import { MockEmbeddingProvider } from 'nexus-ai-pro/embeddings/mock';
1232
+ import { KNOWN_EMBEDDING_MODELS } from 'nexus-ai-pro/embeddings/models';
1028
1233
  import { ImageManager } from 'nexus-ai-pro/images';
1029
1234
  import { MemoryAssetStore } from 'nexus-ai-pro/images/assets';
1030
1235
  import { MockImageProvider } from 'nexus-ai-pro/images/mock';
@@ -1086,10 +1291,12 @@ npm run test:conformance:real
1086
1291
 
1087
1292
  ## Roadmap
1088
1293
 
1089
- The experimental image foundation now includes portable asset types, `ImageManager`, provider
1090
- capability negotiation, local operation handles, visual-safety hooks, deterministic conformance, and an
1091
- opt-in OpenAI adapter. Durable operations, production asset storage, additional providers, and
1092
- media-specific evaluations remain planned in [ROADMAP.md](./ROADMAP.md).
1294
+ 1.4.0 closed the completion-request gap: prompt caching, reasoning controls, tool and sampling
1295
+ controls, structured usage with numeric cost, and capability negotiation with registry provenance.
1296
+
1297
+ Next is durable execution — an operation state machine, provider batch APIs, distributed rate
1298
+ limiting and circuit breaking, first-class embeddings, and filesystem/S3 asset stores — followed by
1299
+ image portability and promotion out of experimental. See [ROADMAP.md](./ROADMAP.md).
1093
1300
 
1094
1301
  ## Production Notes
1095
1302