nexus-ai-pro 2.19.0 → 2.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/API_STABILITY.md CHANGED
@@ -41,6 +41,54 @@ 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
+ ## How an item graduates
45
+
46
+ A new surface starts this way:
47
+
48
+ 1. Provider-neutral types and a deterministic mock land first.
49
+ 2. One real adapter proves the contract; conformance fixtures cover it.
50
+ 3. A second adapter on a different wire protocol proves portability.
51
+ 4. Packed-package, clean-install, and consumer type-resolution tests pass on every supported Node
52
+ line.
53
+ 5. Evaluation appropriate to the modality covers the supported capability matrix.
54
+
55
+ It leaves experimental status here only when every item below has evidence, published with the
56
+ graduation:
57
+
58
+ | Requirement | Evidence |
59
+ | --- | --- |
60
+ | API stability | Covered by the 2.x compatibility rules, with no planned breaking change |
61
+ | Conformance | Passes its contract suite on every supported backend |
62
+ | Crash recovery | Crash-injection tests at each durable boundary, where it has one |
63
+ | Concurrency | Race tests with several processes on the same records |
64
+ | Load | A published benchmark with a budget in CI |
65
+ | Tenant isolation | The isolation suite, where it stores tenant data |
66
+ | Security | A written threat review, with its findings closed |
67
+ | Upgrade | A migration test from the previous minor |
68
+ | Soak | A long-running test with flat memory and no leaked handles |
69
+
70
+ ## 2.20 stage (2.20.0)
71
+
72
+ Everything 2.20 adds is stable from the start and additive, unless this section says otherwise.
73
+
74
+ New and stable:
75
+ - `nexus-ai-pro/agent/openapi`: `openApiTools()`, `loadOpenApiTools()`, `OpenApiError`,
76
+ `OpenApiDocument`, `OpenApiOperation`, `OpenApiTools`, `OpenApiToolsOptions`, and
77
+ `OpenApiHeadersRequest`;
78
+ - the capabilities `api.read`, `api.write`, and `api.destructive`;
79
+ - in `nexus-ai-pro/evaluate/emulate`: `EmulatedWorld`, `EmulatedWorldCall`, `EmulatedWorldSnapshot`,
80
+ `EmulatedToolsOptions.world` and `reads`, and `emulationKey()`'s `world`. A key without a world is
81
+ the key of every earlier release;
82
+ - in `nexus-ai-pro/protocols/a2a`: `A2aServerOptions.tenants`, `A2aTenancyOptions`,
83
+ `A2aClientOptions.tenant`, `A2aTaskRecord.tenant`, `A2aTaskQuery.tenant`, `A2A_ERRORS.permissionDenied`,
84
+ the card's `securitySchemes`, `securityRequirements`, and `signatures`, `A2aSkill.securityRequirements`,
85
+ `A2aSecurityScheme`, `A2aOAuthFlows`, `A2aSecurityRequirement`, and `A2aCardSignature`.
86
+
87
+ Changed on purpose: the gRPC binding writes and reads a card's security schemes, requirements, and
88
+ signatures, which it read past; without `tenants`, a request's tenant is still not read. A custom
89
+ `A2aTaskStore` serving several tenants should honour `A2aTaskQuery.tenant`, for full pages; the handler
90
+ filters by tenant either way.
91
+
44
92
  ## 2.19 stage (2.19.0)
45
93
 
46
94
  Everything 2.19 adds is stable from the start and additive, unless this section says otherwise.
@@ -655,7 +703,7 @@ These security fixes tighten more behavior, each closing a finding of a graduati
655
703
  ### Graduation
656
704
 
657
705
  A surface left experimental status in 2.4 only when every requirement in
658
- [How an item graduates](./ROADMAP.md#how-an-item-graduates) had evidence. That evidence is below.
706
+ [How an item graduates](#how-an-item-graduates) had evidence. That evidence is below.
659
707
  "n/a" names a requirement that does not apply, and says why. Every test named here runs in CI.
660
708
  Load and soak budgets are checked by `npm run bench:surfaces` and `npm run bench:runtime`, which are
661
709
  part of `check:release`. Upgrade tests run against the published 2.3.0 package, installed as a
package/CHANGELOG.md CHANGED
@@ -4,6 +4,71 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [2.20.2] - 2026-10-24
8
+
9
+ The first published release of 2.20. The package is 2.20.0's: the publishes of 2.20.0 and 2.20.1
10
+ stopped at a studio test that failed by chance, so neither reached npm.
11
+
12
+ ### Fixed
13
+
14
+ - The studio's load budget is judged by the fastest of five passes, after three warm-up passes
15
+ rather than one. The median of five failed the 2.20.1 publish at 3.61 calibration loops: its first
16
+ three passes took 91 to 102 ms and its last two 37 and 48 ms. A busy runner only ever adds time,
17
+ while a real slowdown slows every pass, so a request made 0.1 ms slower still fails it, at 4.3 loops.
18
+
19
+ ## [2.20.1] - 2026-10-24 (not published)
20
+
21
+ The package is 2.20.0's. Its publish stopped at the same studio test, measured another way.
22
+
23
+ ### Fixed
24
+
25
+ - The studio's load budget is measured as the median of five passes, as the surface budgets are,
26
+ rather than one: a single slow pass on a busy runner failed the 2.20.0 publish at 2.56 calibration
27
+ loops against a budget of 2.55, where the test measures 0.77. A real slowdown still fails it.
28
+
29
+ ## [2.20.0] - 2026-10-24 (not published)
30
+
31
+ Tools read from a service's OpenAPI document, an emulator that remembers the evaluation, and one A2A
32
+ handler serving many tenants.
33
+
34
+ ### Added
35
+
36
+ - **Tools from an OpenAPI document.** `openApiTools()` and `loadOpenApiTools()`, on
37
+ `nexus-ai-pro/agent/openapi`, read an OpenAPI 3.0 or 3.1 document into one tool per operation: its
38
+ name, description, and parameters the API's, its body's fields flattened, references followed, and
39
+ each declaring `api.read`, `api.write`, or `api.destructive` as its method says, with `network` for
40
+ the API's host. Given where the API lives, each tool calls it, the path encoded, the application's
41
+ headers sent, no redirect followed, and an answer read only up to a limit; a refusal is an
42
+ `OpenApiError` in the API's own words. `results` holds the schema of what each returns.
43
+ - **An emulator that remembers the evaluation.** An `EmulatedWorld`, given to `emulatedTools()` as
44
+ `world`, holds the scenario and every call across the runs of an evaluation, so a change one example
45
+ makes is there for the next; the emulator is told every change. `snapshot()` writes it as JSON,
46
+ `fork()` and `new EmulatedWorld(snapshot)` start runs from it, and the key of each answer kept carries
47
+ the world's `digest()`, so a recorded evaluation replays only after the same changes. `reads` names
48
+ tools that change nothing, beside those whose capabilities only read.
49
+ - **Capabilities for an API's operations:** `api.read:<api>/<operation>`, `api.write`, and
50
+ `api.destructive`, decided by a permission policy as MCP's are.
51
+ - **A2A for several tenants.** `a2aHandler({ tenants })` routes each request by the `tenant` it names,
52
+ in its params, its path (`/{tenant}/message:send`), or its gRPC message. Each tenant's tasks and
53
+ conversations are its own, its runs carry the tenant on their principal, and a caller that may not
54
+ act for the tenant it names is refused with `A2A_ERRORS.permissionDenied`, HTTP 403 and
55
+ `PERMISSION_DENIED` over gRPC. `A2aTenancyOptions` sets who may, and whether a tenant is required.
56
+ Task stores keep and list by tenant. A client names its interface's tenant, or its `tenant` option,
57
+ on every request, as protocol 1.0 requires.
58
+ - **A card's security.** `securitySchemes` (`A2aSecurityScheme`, with `A2aOAuthFlows`),
59
+ `securityRequirements` (`A2aSecurityRequirement`, on the card and on a skill), and `signatures`
60
+ (`A2aCardSignature`) are part of the card, and the gRPC binding writes and reads them, which it read
61
+ past before.
62
+
63
+ ### Fixed
64
+
65
+ - The prompt densifier joined a code block's fences to the text around them, so a block it meant to
66
+ keep stopped being one; it shortened phrases inside other words, turning "in order today" into
67
+ "today"; and it lowercased a phrase that began a sentence.
68
+ - A test of a call that never answers, and the unit, coverage, and studio runs, no longer let a hung
69
+ test hold the release gate: every synchronous child the tests start has a timeout, and a test that
70
+ runs past five minutes fails.
71
+
7
72
  ## [2.19.0] - 2026-10-24
8
73
 
9
74
  Typed decisions: a yes or no, a choice, or a score, answered with probabilities and a confidence read
@@ -3222,7 +3287,10 @@ shared policy vocabulary is in place for a future release that revisits this.
3222
3287
  - URL fetching rejects unsafe private-network targets and limits response reads.
3223
3288
  - CLI and audit findings no longer disclose detected secret values.
3224
3289
 
3225
- [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.19.0...HEAD
3290
+ [Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.20.2...HEAD
3291
+ [2.20.2]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.20.1...v2.20.2
3292
+ [2.20.1]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.20.0...v2.20.1
3293
+ [2.20.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.19.0...v2.20.0
3226
3294
  [2.19.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.18.0...v2.19.0
3227
3295
  [2.18.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.17.0...v2.18.0
3228
3296
  [2.17.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.16.0...v2.17.0
package/README.md CHANGED
@@ -335,12 +335,15 @@ Shipped so far:
335
335
  - 2.19 typed decisions: a yes or no, a choice, or a score, answered with probabilities and a
336
336
  confidence, from TypeSafe AI's Jev or from any model's log probabilities, gated by thresholds a
337
337
  calibration chooses, so a graph's branch, an approval, or a guardrail acts when it is sure and asks
338
- a person when it is not.
338
+ a person when it is not;
339
+ - 2.20 tools read from a service's own OpenAPI document, which an agent calls or an emulator plays,
340
+ an emulator that holds what a whole evaluation did so examples that build on each other are
341
+ evaluated with nothing sent, and one A2A handler serving many tenants, each seeing only its own.
339
342
 
340
343
  Upgrading from 1.x: `npx nexus migrate src --write` moves your imports, and
341
344
  [MIGRATING.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/MIGRATING.md) covers the rest.
342
- Next, 2.20 reads a service's own API description into emulated tools, and lets an emulator hold
343
- what a whole evaluation did, so examples that build on each other are evaluated with nothing sent.
345
+ Next, 2.21 asks typed decisions about pictures, and gathers community adapters' compatibility
346
+ records from their own CI.
344
347
  Pinecone, images, the Gemini
345
348
  embedding batch tier, media understanding, video, Gemini Live, SIP, and both phone providers
346
349
  graduate once the live workflow passes against each. The
@@ -12,6 +12,9 @@ import type { ToolDefinition } from '../types/messages.js';
12
12
  * | `mcp.read:<server>/<tool>` | An MCP tool its server says only reads |
13
13
  * | `mcp.write:<server>/<tool>` | An MCP tool its server says changes things but destroys nothing |
14
14
  * | `mcp.destructive:<server>/<tool>` | An MCP tool its server says may destroy or overwrite |
15
+ * | `api.read:<api>/<operation>` | An operation of an OpenAPI document whose method only reads |
16
+ * | `api.write:<api>/<operation>` | One whose method changes things |
17
+ * | `api.destructive:<api>/<operation>` | One whose method deletes |
15
18
  * | `<name>`, `<name>:<detail>` | Anything the application names, such as `payments:refund` |
16
19
  *
17
20
  * A tool that declares `capabilities: []` does nothing to the world; one that declares none at all
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Tools read from a service's own API description, an OpenAPI document: one tool per operation, its
3
+ * name, description, and parameters the service's, the capabilities it declares read from its method,
4
+ * and what it returns kept as a schema. Given where the API lives, each tool calls it; without, the
5
+ * tools describe the API and run nowhere, as `emulatedTools()` wants them.
6
+ */
7
+ import type { ToolContext, ToolDefinition } from '../types/messages.js';
8
+ /** An OpenAPI 3 document, as parsed JSON. Only what tools are read from is typed. */
9
+ export interface OpenApiDocument {
10
+ /** The OpenAPI version, `3.0.x` or `3.1.x`. */
11
+ openapi: string;
12
+ /** The API's title and version. */
13
+ info?: {
14
+ title?: string;
15
+ version?: string;
16
+ description?: string;
17
+ };
18
+ /** Where the API is served. */
19
+ servers?: Array<{
20
+ url: string;
21
+ variables?: Record<string, {
22
+ default: string;
23
+ }>;
24
+ }>;
25
+ /** Its operations, by path and method. */
26
+ paths?: Record<string, Record<string, unknown>>;
27
+ /** Schemas, parameters, request bodies, and responses its operations refer to. */
28
+ components?: Record<string, unknown>;
29
+ [key: string]: unknown;
30
+ }
31
+ /** One operation of the API, as a tool reads it. */
32
+ export interface OpenApiOperation {
33
+ /** The tool's name: the operation's id, or its method and path. */
34
+ name: string;
35
+ /** The HTTP method, in capitals. */
36
+ method: string;
37
+ /** The path, with its `{parameters}`. */
38
+ path: string;
39
+ /** The operation's id, when it has one. */
40
+ operationId?: string;
41
+ /** Its summary, when it has one. */
42
+ summary?: string;
43
+ /** Its tags. */
44
+ tags: string[];
45
+ /** What it does to the service, read from its method: `read` for GET, HEAD, OPTIONS, and TRACE, `destructive` for DELETE, and `write` for the rest. */
46
+ access: 'read' | 'write' | 'destructive';
47
+ }
48
+ /** What a tool's headers are chosen from: the operation, and the call's context. */
49
+ export interface OpenApiHeadersRequest {
50
+ /** The operation being called. */
51
+ operation: OpenApiOperation;
52
+ /** The call's context: its tenant, principal, and thread. */
53
+ context?: ToolContext;
54
+ }
55
+ /** Options for `openApiTools()`. */
56
+ export interface OpenApiToolsOptions {
57
+ /** The API's name, in each tool's capabilities. Defaults to its title, as a slug, or `api`. */
58
+ name?: string;
59
+ /**
60
+ * Where the API lives, its path prefix included, such as `https://shop.example.com/api`. Defaults
61
+ * to the document's first server, resolved against `documentUrl` when relative. Without one, the
62
+ * tools describe the API and run nowhere.
63
+ */
64
+ baseUrl?: string;
65
+ /** Where the document came from, to resolve a relative server URL against. */
66
+ documentUrl?: string;
67
+ /**
68
+ * Headers every call sends, such as authorization, or a function choosing them per operation and
69
+ * call, such as by its tenant. They win over a header parameter the model sets.
70
+ */
71
+ headers?: Readonly<Record<string, string>> | ((request: OpenApiHeadersRequest) => Record<string, string> | Promise<Record<string, string>>);
72
+ /** Which operations become tools. Defaults to every one. */
73
+ include?: (operation: OpenApiOperation) => boolean;
74
+ /** Put before every tool's name, with an underscore, so two APIs' operations stay apart. */
75
+ prefix?: string;
76
+ /** How long a call may take, in milliseconds. Defaults to 30 seconds. */
77
+ timeoutMs?: number;
78
+ /** The largest answer a call reads, in bytes; a larger one fails the call. Defaults to 1 MB. */
79
+ maxResponseBytes?: number;
80
+ /** Replaces the global `fetch`. */
81
+ fetch?: typeof globalThis.fetch;
82
+ }
83
+ /** An API's operations as tools, with what each returns. */
84
+ export interface OpenApiTools {
85
+ /** The API's name, as its tools' capabilities carry it. */
86
+ name: string;
87
+ /** Where the tools send their calls, when they run. */
88
+ baseUrl?: string;
89
+ /** One tool per operation. */
90
+ tools: ToolDefinition[];
91
+ /** The operations, in the tools' order. */
92
+ operations: OpenApiOperation[];
93
+ /** The JSON Schema of what each tool returns, by name: its first success response's. Pass it to `emulatedTools()` as `results`. */
94
+ results: Record<string, Record<string, unknown>>;
95
+ /** Operations that could not become tools, and why: a request body of a type the tools do not send. */
96
+ skipped: Array<{
97
+ method: string;
98
+ path: string;
99
+ reason: string;
100
+ }>;
101
+ }
102
+ /** Why an API could not be read into tools, or a call to it failed. */
103
+ export declare class OpenApiError extends Error {
104
+ /**
105
+ * `OPENAPI_INVALID` for a malformed document or call, `OPENAPI_UNSUPPORTED` for one these tools
106
+ * cannot read, such as Swagger 2.0 or a reference outside the document, and `OPENAPI_REQUEST`
107
+ * for a call the API refused or did not answer.
108
+ */
109
+ readonly code: 'OPENAPI_INVALID' | 'OPENAPI_UNSUPPORTED' | 'OPENAPI_REQUEST';
110
+ /** The HTTP status the API answered with, when it did. */
111
+ readonly status?: number | undefined;
112
+ constructor(message: string,
113
+ /**
114
+ * `OPENAPI_INVALID` for a malformed document or call, `OPENAPI_UNSUPPORTED` for one these tools
115
+ * cannot read, such as Swagger 2.0 or a reference outside the document, and `OPENAPI_REQUEST`
116
+ * for a call the API refused or did not answer.
117
+ */
118
+ code: 'OPENAPI_INVALID' | 'OPENAPI_UNSUPPORTED' | 'OPENAPI_REQUEST',
119
+ /** The HTTP status the API answered with, when it did. */
120
+ status?: number | undefined);
121
+ }
122
+ /**
123
+ * Reads an OpenAPI 3 document into tools, one per operation: named by its `operationId`, or its
124
+ * method and path; described by its summary; its path, query, and header parameters, and the fields
125
+ * of a JSON body, as the tool's parameters. Each declares `api.read`, `api.write`, or
126
+ * `api.destructive` with the API's name and its own, as its method says, and `network` for the
127
+ * API's host when it runs. References within the document are followed; one outside it is refused.
128
+ *
129
+ * ```ts
130
+ * const shop = openApiTools(JSON.parse(await readFile('shop.openapi.json', 'utf8')), {
131
+ * baseUrl: 'https://shop.example.com/api',
132
+ * headers: { authorization: `Bearer ${process.env.SHOP_TOKEN}` },
133
+ * });
134
+ * const agent = createAgent({ client: ai, tools: shop.tools });
135
+ * ```
136
+ */
137
+ export declare function openApiTools(document: OpenApiDocument | string, options?: OpenApiToolsOptions): OpenApiTools;
138
+ /** Fetches an OpenAPI document, as JSON, and reads it into tools, resolving its servers against where it came from. */
139
+ export declare function loadOpenApiTools(url: string, options?: OpenApiToolsOptions): Promise<OpenApiTools>;