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 +49 -1
- package/CHANGELOG.md +69 -1
- package/README.md +6 -3
- package/dist/agent/capabilities.d.ts +3 -0
- package/dist/agent/openapi.d.ts +139 -0
- package/dist/agent/openapi.js +455 -0
- package/dist/evaluate/emulate.d.ts +73 -6
- package/dist/evaluate/emulate.js +114 -12
- package/dist/optimizer/densifier.js +20 -11
- package/dist/protocols/a2a-grpc.js +1 -1
- package/dist/protocols/a2a-proto.d.ts +2 -2
- package/dist/protocols/a2a-proto.js +76 -1
- package/dist/protocols/a2a.d.ts +121 -0
- package/dist/protocols/a2a.js +88 -29
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist-cjs/agent/capabilities.d.ts +3 -0
- package/dist-cjs/agent/openapi.d.ts +139 -0
- package/dist-cjs/agent/openapi.js +461 -0
- package/dist-cjs/evaluate/emulate.d.ts +73 -6
- package/dist-cjs/evaluate/emulate.js +116 -13
- package/dist-cjs/optimizer/densifier.js +20 -11
- package/dist-cjs/protocols/a2a-grpc.js +1 -1
- package/dist-cjs/protocols/a2a-proto.d.ts +2 -2
- package/dist-cjs/protocols/a2a-proto.js +76 -1
- package/dist-cjs/protocols/a2a.d.ts +121 -0
- package/dist-cjs/protocols/a2a.js +88 -29
- package/dist-cjs/version.d.ts +1 -1
- package/dist-cjs/version.js +1 -1
- package/llms.txt +1 -1
- package/package.json +14 -1
|
@@ -16,6 +16,7 @@ exports.A2A_ERRORS = {
|
|
|
16
16
|
unsupportedOperation: -32004,
|
|
17
17
|
extendedCardNotConfigured: -32007,
|
|
18
18
|
versionNotSupported: -32009,
|
|
19
|
+
permissionDenied: -32099,
|
|
19
20
|
};
|
|
20
21
|
class A2aError extends Error {
|
|
21
22
|
code;
|
|
@@ -55,7 +56,8 @@ class MemoryA2aTaskStore {
|
|
|
55
56
|
list(query) {
|
|
56
57
|
const matching = [...this.tasks.values()]
|
|
57
58
|
.filter((record) => (query.contextId === undefined || record.task.contextId === query.contextId) &&
|
|
58
|
-
(query.state === undefined || record.task.status.state === query.state)
|
|
59
|
+
(query.state === undefined || record.task.status.state === query.state) &&
|
|
60
|
+
(query.tenant === undefined || record.tenant === query.tenant))
|
|
59
61
|
.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
60
62
|
return {
|
|
61
63
|
records: matching.slice(query.offset, query.offset + query.limit).map((record) => structuredClone(record)),
|
|
@@ -94,6 +96,7 @@ function storeA2aTasks(store, options = {}) {
|
|
|
94
96
|
const filter = {
|
|
95
97
|
...(query.contextId === undefined ? {} : { 'task.contextId': query.contextId }),
|
|
96
98
|
...(query.state === undefined ? {} : { 'task.status.state': query.state }),
|
|
99
|
+
...(query.tenant === undefined ? {} : { tenant: query.tenant }),
|
|
97
100
|
};
|
|
98
101
|
const items = await store.search(tasks, {
|
|
99
102
|
limit: query.limit,
|
|
@@ -140,6 +143,7 @@ const ERROR_STATUS = {
|
|
|
140
143
|
[-32007]: { httpStatus: 400, grpcStatus: 'FAILED_PRECONDITION', reason: 'EXTENDED_AGENT_CARD_NOT_CONFIGURED' },
|
|
141
144
|
[-32008]: { httpStatus: 400, grpcStatus: 'FAILED_PRECONDITION', reason: 'EXTENSION_SUPPORT_REQUIRED' },
|
|
142
145
|
[-32009]: { httpStatus: 400, grpcStatus: 'UNIMPLEMENTED', reason: 'VERSION_NOT_SUPPORTED' },
|
|
146
|
+
[-32099]: { httpStatus: 403, grpcStatus: 'PERMISSION_DENIED', reason: 'PERMISSION_DENIED' },
|
|
143
147
|
[-32700]: { httpStatus: 400, grpcStatus: 'INVALID_ARGUMENT', reason: 'INVALID_PARAMS' },
|
|
144
148
|
[-32600]: { httpStatus: 400, grpcStatus: 'INVALID_ARGUMENT', reason: 'INVALID_PARAMS' },
|
|
145
149
|
[-32601]: { httpStatus: 404, grpcStatus: 'UNIMPLEMENTED', reason: 'METHOD_NOT_FOUND' },
|
|
@@ -195,7 +199,12 @@ function a2aHandler(graph, options) {
|
|
|
195
199
|
defaultInputModes: options.card.defaultInputModes ?? ['text/plain', 'application/json'],
|
|
196
200
|
defaultOutputModes: options.card.defaultOutputModes ?? ['text/plain'],
|
|
197
201
|
skills: options.card.skills,
|
|
202
|
+
...(options.card.securitySchemes ? { securitySchemes: options.card.securitySchemes } : {}),
|
|
203
|
+
...(options.card.securityRequirements ? { securityRequirements: options.card.securityRequirements } : {}),
|
|
204
|
+
...(options.card.signatures ? { signatures: options.card.signatures } : {}),
|
|
198
205
|
};
|
|
206
|
+
const tenancy = options.tenants ? (options.tenants === true ? {} : options.tenants) : undefined;
|
|
207
|
+
const conversationKey = (record) => record.tenant === undefined ? record.task.contextId : `${record.tenant}\u0000${record.task.contextId}`;
|
|
199
208
|
const store = options.store ?? new MemoryA2aTaskStore({ maxTasks: options.maxTasks ?? 10_000 });
|
|
200
209
|
const owner = options.lease?.owner ?? (0, shared_js_1.protocolId)('replica');
|
|
201
210
|
const leaseMs = options.lease?.ms ?? 30_000;
|
|
@@ -396,8 +405,8 @@ function a2aHandler(graph, options) {
|
|
|
396
405
|
const text = (0, shared_js_1.answerOf)(result.state);
|
|
397
406
|
const reply = agentMessage(task, text);
|
|
398
407
|
record.history.push(reply);
|
|
399
|
-
const before = (await store.conversation(
|
|
400
|
-
await store.saveConversation(
|
|
408
|
+
const before = (await store.conversation(conversationKey(record))) ?? [];
|
|
409
|
+
await store.saveConversation(conversationKey(record), (0, shared_js_1.transcriptOf)(result.state, [...before, (0, shared_js_1.messageOf)('assistant', text)]));
|
|
401
410
|
task.artifacts = [{ artifactId: `${task.id}:answer`, name: 'answer', parts: [{ text }] }];
|
|
402
411
|
if (streamed) {
|
|
403
412
|
const last = {
|
|
@@ -437,10 +446,10 @@ function a2aHandler(graph, options) {
|
|
|
437
446
|
}
|
|
438
447
|
};
|
|
439
448
|
const startRun = async (record, message, runOptions) => {
|
|
440
|
-
const history = (await store.conversation(record
|
|
449
|
+
const history = (await store.conversation(conversationKey(record))) ?? [];
|
|
441
450
|
const said = (0, shared_js_1.messageOf)('user', textOfParts(message.parts));
|
|
442
451
|
const result = await graph.invoke(options.input ? options.input(message, history) : { messages: [...history, said] }, runOptions);
|
|
443
|
-
await store.saveConversation(record
|
|
452
|
+
await store.saveConversation(conversationKey(record), [...history, said]);
|
|
444
453
|
return result;
|
|
445
454
|
};
|
|
446
455
|
const carryOn = async (record, runOptions) => {
|
|
@@ -492,11 +501,12 @@ function a2aHandler(graph, options) {
|
|
|
492
501
|
void recover().catch((error) => options.onError?.(error));
|
|
493
502
|
}, options.recoverEveryMs);
|
|
494
503
|
timer?.unref?.();
|
|
495
|
-
const taskFor = async (message, principal) => {
|
|
504
|
+
const taskFor = async (message, principal, mine, tenant) => {
|
|
496
505
|
if (message.taskId) {
|
|
497
506
|
const existing = await store.get(message.taskId);
|
|
498
|
-
if (!existing)
|
|
507
|
+
if (!existing || !mine(existing)) {
|
|
499
508
|
return { error: exports.A2A_ERRORS.taskNotFound, message: `Task ${message.taskId} was not found` };
|
|
509
|
+
}
|
|
500
510
|
if (existing.task.status.state !== 'TASK_STATE_INPUT_REQUIRED') {
|
|
501
511
|
return { error: exports.A2A_ERRORS.unsupportedOperation, message: `Task ${message.taskId} is not waiting for input` };
|
|
502
512
|
}
|
|
@@ -515,6 +525,7 @@ function a2aHandler(graph, options) {
|
|
|
515
525
|
history: [said],
|
|
516
526
|
run: { kind: 'invoke', message: said, ...(principal ? { principal } : {}) },
|
|
517
527
|
updatedAt: new Date().toISOString(),
|
|
528
|
+
...(tenant !== undefined ? { tenant } : {}),
|
|
518
529
|
};
|
|
519
530
|
await store.put(record);
|
|
520
531
|
return record;
|
|
@@ -601,9 +612,29 @@ function a2aHandler(graph, options) {
|
|
|
601
612
|
const pushAnswer = (taskId, config, binding) => binding === 'JSONRPC' ? { taskId, ...config, pushNotificationConfig: config } : { taskId, ...config };
|
|
602
613
|
const fail = (code, message) => ({ error: { code, message } });
|
|
603
614
|
const call = async (method, params, context = {}) => {
|
|
604
|
-
|
|
615
|
+
let principal = context.principal;
|
|
605
616
|
const binding = context.binding ?? 'JSONRPC';
|
|
606
617
|
const pushOn = Boolean(options.pushNotifications);
|
|
618
|
+
const named = typeof params.tenant === 'string' && params.tenant !== '' ? params.tenant : undefined;
|
|
619
|
+
if (tenancy) {
|
|
620
|
+
if (named === undefined) {
|
|
621
|
+
if (tenancy.required !== false)
|
|
622
|
+
return fail(-32602, 'This agent serves several tenants: name one as tenant');
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
const allowed = tenancy.authorize ? await tenancy.authorize(named, principal) : principal?.tenantId === named;
|
|
626
|
+
if (!allowed)
|
|
627
|
+
return fail(exports.A2A_ERRORS.permissionDenied, `This caller may not act for tenant ${named}`);
|
|
628
|
+
if (principal?.tenantId !== named)
|
|
629
|
+
principal = { ...principal, tenantId: named };
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
const tenant = tenancy ? named : undefined;
|
|
633
|
+
const mine = (record) => !tenancy || record.tenant === tenant;
|
|
634
|
+
const own = async (id) => {
|
|
635
|
+
const record = await store.get(id);
|
|
636
|
+
return record && mine(record) ? record : undefined;
|
|
637
|
+
};
|
|
607
638
|
try {
|
|
608
639
|
switch (method) {
|
|
609
640
|
case 'SendMessage':
|
|
@@ -616,7 +647,7 @@ function a2aHandler(graph, options) {
|
|
|
616
647
|
if (pushConfig && !pushOn) {
|
|
617
648
|
return fail(exports.A2A_ERRORS.pushNotificationNotSupported, 'This agent does not push notifications');
|
|
618
649
|
}
|
|
619
|
-
const record = await taskFor(message, principal);
|
|
650
|
+
const record = await taskFor(message, principal, mine, tenant);
|
|
620
651
|
if ('error' in record)
|
|
621
652
|
return fail(record.error, record.message);
|
|
622
653
|
if (pushConfig) {
|
|
@@ -634,7 +665,7 @@ function a2aHandler(graph, options) {
|
|
|
634
665
|
return { events: follow(record, () => execute(record, message, principal), context.signal) };
|
|
635
666
|
}
|
|
636
667
|
case 'GetTask': {
|
|
637
|
-
const record = await
|
|
668
|
+
const record = await own(String(params.id ?? ''));
|
|
638
669
|
if (!record)
|
|
639
670
|
return fail(exports.A2A_ERRORS.taskNotFound, `Task ${String(params.id)} was not found`);
|
|
640
671
|
return { result: view(record, typeof params.historyLength === 'number' ? params.historyLength : undefined) };
|
|
@@ -650,6 +681,7 @@ function a2aHandler(graph, options) {
|
|
|
650
681
|
const listed = await store.list({
|
|
651
682
|
...(typeof params.contextId === 'string' && params.contextId !== '' ? { contextId: params.contextId } : {}),
|
|
652
683
|
...(filtered ? { state: status } : {}),
|
|
684
|
+
...(tenant !== undefined ? { tenant } : {}),
|
|
653
685
|
limit: pageSize,
|
|
654
686
|
offset,
|
|
655
687
|
});
|
|
@@ -659,6 +691,7 @@ function a2aHandler(graph, options) {
|
|
|
659
691
|
return {
|
|
660
692
|
result: {
|
|
661
693
|
tasks: listed.records
|
|
694
|
+
.filter(mine)
|
|
662
695
|
.filter((record) => !after || (record.task.status.timestamp ?? '') >= after)
|
|
663
696
|
.map((record) => view(record, historyLength))
|
|
664
697
|
.map(({ artifacts, ...task }) => params.includeArtifacts === false ? task : { ...task, ...(artifacts ? { artifacts } : {}) }),
|
|
@@ -669,7 +702,7 @@ function a2aHandler(graph, options) {
|
|
|
669
702
|
};
|
|
670
703
|
}
|
|
671
704
|
case 'SubscribeToTask': {
|
|
672
|
-
const record = await
|
|
705
|
+
const record = await own(String(params.id ?? ''));
|
|
673
706
|
if (!record)
|
|
674
707
|
return fail(exports.A2A_ERRORS.taskNotFound, `Task ${String(params.id)} was not found`);
|
|
675
708
|
if (TERMINAL.has(record.task.status.state)) {
|
|
@@ -681,7 +714,7 @@ function a2aHandler(graph, options) {
|
|
|
681
714
|
return { events: follow(record, undefined, context.signal) };
|
|
682
715
|
}
|
|
683
716
|
case 'CancelTask': {
|
|
684
|
-
const record = await
|
|
717
|
+
const record = await own(String(params.id ?? ''));
|
|
685
718
|
if (!record)
|
|
686
719
|
return fail(exports.A2A_ERRORS.taskNotFound, `Task ${String(params.id)} was not found`);
|
|
687
720
|
if (TERMINAL.has(record.task.status.state)) {
|
|
@@ -701,7 +734,7 @@ function a2aHandler(graph, options) {
|
|
|
701
734
|
if (!pushOn)
|
|
702
735
|
return fail(exports.A2A_ERRORS.pushNotificationNotSupported, 'This agent does not push notifications');
|
|
703
736
|
const taskId = String(params.taskId ?? params.id ?? '');
|
|
704
|
-
const record = await
|
|
737
|
+
const record = await own(taskId);
|
|
705
738
|
if (!record)
|
|
706
739
|
return fail(exports.A2A_ERRORS.taskNotFound, `Task ${taskId} was not found`);
|
|
707
740
|
if (method === 'CreateTaskPushNotificationConfig') {
|
|
@@ -782,8 +815,21 @@ function a2aHandler(graph, options) {
|
|
|
782
815
|
return rpcError(id, outcome.error.code, outcome.error.message);
|
|
783
816
|
return rpcResult(id, outcome.result);
|
|
784
817
|
};
|
|
785
|
-
const httpJson = async (request,
|
|
818
|
+
const httpJson = async (request, path) => {
|
|
786
819
|
const verb = request.method;
|
|
820
|
+
const known = /^\/(?:message:send$|message:stream$|tasks(?:$|\/)|extendedAgentCard$)/;
|
|
821
|
+
let route = path;
|
|
822
|
+
let routedTenant;
|
|
823
|
+
const prefixed = /^\/([^/]+)(\/.*)$/.exec(path);
|
|
824
|
+
if (!known.test(path) && prefixed && known.test(prefixed[2])) {
|
|
825
|
+
try {
|
|
826
|
+
routedTenant = decodeURIComponent(prefixed[1]);
|
|
827
|
+
}
|
|
828
|
+
catch {
|
|
829
|
+
return restError(-32602, 'The tenant in the path is not valid percent-encoding');
|
|
830
|
+
}
|
|
831
|
+
route = prefixed[2];
|
|
832
|
+
}
|
|
787
833
|
const query = new URL(request.url).searchParams;
|
|
788
834
|
const number = (name) => (query.has(name) ? Number(query.get(name)) : undefined);
|
|
789
835
|
const id = (value) => decodeURIComponent(value ?? '');
|
|
@@ -835,6 +881,8 @@ function a2aHandler(graph, options) {
|
|
|
835
881
|
const refused = versionRefused(request);
|
|
836
882
|
if (refused)
|
|
837
883
|
return restError(exports.A2A_ERRORS.versionNotSupported, refused);
|
|
884
|
+
if (routedTenant !== undefined)
|
|
885
|
+
params = { ...params, tenant: routedTenant };
|
|
838
886
|
if (body) {
|
|
839
887
|
const text = await request.text();
|
|
840
888
|
try {
|
|
@@ -1045,23 +1093,34 @@ function a2aClient(options) {
|
|
|
1045
1093
|
});
|
|
1046
1094
|
return cached;
|
|
1047
1095
|
};
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1050
|
-
|
|
1096
|
+
const target = async () => {
|
|
1097
|
+
if (!isCard)
|
|
1098
|
+
return { url: options.url, ...(options.tenant ? { tenant: options.tenant } : {}) };
|
|
1099
|
+
const chosen = (await card()).supportedInterfaces.find((item) => item.protocolBinding === binding);
|
|
1100
|
+
const tenant = options.tenant ?? chosen?.tenant;
|
|
1101
|
+
return { url: chosen?.url ?? options.url, ...(tenant ? { tenant } : {}) };
|
|
1102
|
+
};
|
|
1051
1103
|
const headers = { 'content-type': 'application/json', 'a2a-version': exports.A2A_PROTOCOL_VERSION, ...options.headers };
|
|
1052
1104
|
let ids = 0;
|
|
1053
|
-
const rpc = async (method, params, signal) =>
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1105
|
+
const rpc = async (method, params, signal) => {
|
|
1106
|
+
const { url, tenant } = await target();
|
|
1107
|
+
return fetcher(url, {
|
|
1108
|
+
method: 'POST',
|
|
1109
|
+
headers,
|
|
1110
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: ++ids, method, params: tenant ? { tenant, ...params } : params }),
|
|
1111
|
+
...(signal ? { signal } : {}),
|
|
1112
|
+
});
|
|
1113
|
+
};
|
|
1114
|
+
const route = async (verb, path, body, signal) => {
|
|
1115
|
+
const { url, tenant } = await target();
|
|
1116
|
+
const under = tenant ? `/${encodeURIComponent(tenant)}` : '';
|
|
1117
|
+
return fetcher(`${url.replace(/\/+$/, '')}${under}${path}`, {
|
|
1118
|
+
method: verb,
|
|
1119
|
+
headers: body === undefined ? { ...headers, 'content-type': 'application/json' } : headers,
|
|
1120
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
1121
|
+
...(signal ? { signal } : {}),
|
|
1122
|
+
});
|
|
1123
|
+
};
|
|
1065
1124
|
const restFailure = async (response) => {
|
|
1066
1125
|
const body = (await response.json().catch(() => ({})));
|
|
1067
1126
|
const reason = body.error?.details?.find((detail) => detail.reason)?.reason;
|
package/dist-cjs/version.d.ts
CHANGED
package/dist-cjs/version.js
CHANGED
package/llms.txt
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Import only what you use. The root import is the client and nothing else, and every other capability has its own entry point with a size budget CI enforces; [the packaging guide](https://raw.githubusercontent.com/mkhitar-abrahamyan/nexus-ai/main/docs/packaging.md) publishes what each one costs. The package has no required dependencies: a graph-only application loads 81 KB, and installs nothing but this package. The client, the provider adapters, graphs, agents, and the agent protocols run on Node.js, Deno, Bun, edge runtimes, and browsers; [the runtimes guide](https://raw.githubusercontent.com/mkhitar-abrahamyan/nexus-ai/main/docs/runtimes.md) lists the entry points that need Node.js, and why.
|
|
6
6
|
|
|
7
7
|
- Install with `npm install nexus-ai-pro`. It needs Node.js 22 or newer, and ships ESM and CommonJS builds with shared types. License: MIT.
|
|
8
|
-
- It has
|
|
8
|
+
- It has 199 entry points. `nexus-ai-pro` itself is the client, and every other capability has its own subpath, such as `nexus-ai-pro/graph`.
|
|
9
9
|
- Nothing is a required dependency. Install only the optional peers you use: `@anthropic-ai/sdk`, `@types/node`, `ajv`, `ajv-formats`, `ollama`, `openai`, `zod`.
|
|
10
10
|
- Each guide explains every export of its entry points, then lists them in a reference generated from the doc comments.
|
|
11
11
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexus-ai-pro",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.20.2",
|
|
4
4
|
"description": "Typed AI framework for TypeScript: provider routing and failover, durable agent graphs and workflows, retrieval over any vector store, MCP, evals, guardrails, and cost control",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist-cjs/index.js",
|
|
@@ -670,6 +670,16 @@
|
|
|
670
670
|
"default": "./dist-cjs/agent/middleware/index.js"
|
|
671
671
|
}
|
|
672
672
|
},
|
|
673
|
+
"./agent/openapi": {
|
|
674
|
+
"import": {
|
|
675
|
+
"types": "./dist/agent/openapi.d.ts",
|
|
676
|
+
"default": "./dist/agent/openapi.js"
|
|
677
|
+
},
|
|
678
|
+
"require": {
|
|
679
|
+
"types": "./dist-cjs/agent/openapi.d.ts",
|
|
680
|
+
"default": "./dist-cjs/agent/openapi.js"
|
|
681
|
+
}
|
|
682
|
+
},
|
|
673
683
|
"./deep-agent": {
|
|
674
684
|
"import": {
|
|
675
685
|
"types": "./dist/deep-agent/index.d.ts",
|
|
@@ -2412,6 +2422,9 @@
|
|
|
2412
2422
|
"agent/middleware": [
|
|
2413
2423
|
"dist-cjs/agent/middleware/index.d.ts"
|
|
2414
2424
|
],
|
|
2425
|
+
"agent/openapi": [
|
|
2426
|
+
"dist-cjs/agent/openapi.d.ts"
|
|
2427
|
+
],
|
|
2415
2428
|
"deep-agent": [
|
|
2416
2429
|
"dist-cjs/deep-agent/index.d.ts"
|
|
2417
2430
|
],
|