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
package/dist/protocols/a2a.js
CHANGED
|
@@ -7,6 +7,7 @@ export const A2A_ERRORS = {
|
|
|
7
7
|
unsupportedOperation: -32004,
|
|
8
8
|
extendedCardNotConfigured: -32007,
|
|
9
9
|
versionNotSupported: -32009,
|
|
10
|
+
permissionDenied: -32099,
|
|
10
11
|
};
|
|
11
12
|
export class A2aError extends Error {
|
|
12
13
|
code;
|
|
@@ -45,7 +46,8 @@ export class MemoryA2aTaskStore {
|
|
|
45
46
|
list(query) {
|
|
46
47
|
const matching = [...this.tasks.values()]
|
|
47
48
|
.filter((record) => (query.contextId === undefined || record.task.contextId === query.contextId) &&
|
|
48
|
-
(query.state === undefined || record.task.status.state === query.state)
|
|
49
|
+
(query.state === undefined || record.task.status.state === query.state) &&
|
|
50
|
+
(query.tenant === undefined || record.tenant === query.tenant))
|
|
49
51
|
.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
50
52
|
return {
|
|
51
53
|
records: matching.slice(query.offset, query.offset + query.limit).map((record) => structuredClone(record)),
|
|
@@ -83,6 +85,7 @@ export function storeA2aTasks(store, options = {}) {
|
|
|
83
85
|
const filter = {
|
|
84
86
|
...(query.contextId === undefined ? {} : { 'task.contextId': query.contextId }),
|
|
85
87
|
...(query.state === undefined ? {} : { 'task.status.state': query.state }),
|
|
88
|
+
...(query.tenant === undefined ? {} : { tenant: query.tenant }),
|
|
86
89
|
};
|
|
87
90
|
const items = await store.search(tasks, {
|
|
88
91
|
limit: query.limit,
|
|
@@ -129,6 +132,7 @@ const ERROR_STATUS = {
|
|
|
129
132
|
[-32007]: { httpStatus: 400, grpcStatus: 'FAILED_PRECONDITION', reason: 'EXTENDED_AGENT_CARD_NOT_CONFIGURED' },
|
|
130
133
|
[-32008]: { httpStatus: 400, grpcStatus: 'FAILED_PRECONDITION', reason: 'EXTENSION_SUPPORT_REQUIRED' },
|
|
131
134
|
[-32009]: { httpStatus: 400, grpcStatus: 'UNIMPLEMENTED', reason: 'VERSION_NOT_SUPPORTED' },
|
|
135
|
+
[-32099]: { httpStatus: 403, grpcStatus: 'PERMISSION_DENIED', reason: 'PERMISSION_DENIED' },
|
|
132
136
|
[-32700]: { httpStatus: 400, grpcStatus: 'INVALID_ARGUMENT', reason: 'INVALID_PARAMS' },
|
|
133
137
|
[-32600]: { httpStatus: 400, grpcStatus: 'INVALID_ARGUMENT', reason: 'INVALID_PARAMS' },
|
|
134
138
|
[-32601]: { httpStatus: 404, grpcStatus: 'UNIMPLEMENTED', reason: 'METHOD_NOT_FOUND' },
|
|
@@ -184,7 +188,12 @@ export function a2aHandler(graph, options) {
|
|
|
184
188
|
defaultInputModes: options.card.defaultInputModes ?? ['text/plain', 'application/json'],
|
|
185
189
|
defaultOutputModes: options.card.defaultOutputModes ?? ['text/plain'],
|
|
186
190
|
skills: options.card.skills,
|
|
191
|
+
...(options.card.securitySchemes ? { securitySchemes: options.card.securitySchemes } : {}),
|
|
192
|
+
...(options.card.securityRequirements ? { securityRequirements: options.card.securityRequirements } : {}),
|
|
193
|
+
...(options.card.signatures ? { signatures: options.card.signatures } : {}),
|
|
187
194
|
};
|
|
195
|
+
const tenancy = options.tenants ? (options.tenants === true ? {} : options.tenants) : undefined;
|
|
196
|
+
const conversationKey = (record) => record.tenant === undefined ? record.task.contextId : `${record.tenant}\u0000${record.task.contextId}`;
|
|
188
197
|
const store = options.store ?? new MemoryA2aTaskStore({ maxTasks: options.maxTasks ?? 10_000 });
|
|
189
198
|
const owner = options.lease?.owner ?? protocolId('replica');
|
|
190
199
|
const leaseMs = options.lease?.ms ?? 30_000;
|
|
@@ -385,8 +394,8 @@ export function a2aHandler(graph, options) {
|
|
|
385
394
|
const text = answerOf(result.state);
|
|
386
395
|
const reply = agentMessage(task, text);
|
|
387
396
|
record.history.push(reply);
|
|
388
|
-
const before = (await store.conversation(
|
|
389
|
-
await store.saveConversation(
|
|
397
|
+
const before = (await store.conversation(conversationKey(record))) ?? [];
|
|
398
|
+
await store.saveConversation(conversationKey(record), transcriptOf(result.state, [...before, messageOf('assistant', text)]));
|
|
390
399
|
task.artifacts = [{ artifactId: `${task.id}:answer`, name: 'answer', parts: [{ text }] }];
|
|
391
400
|
if (streamed) {
|
|
392
401
|
const last = {
|
|
@@ -426,10 +435,10 @@ export function a2aHandler(graph, options) {
|
|
|
426
435
|
}
|
|
427
436
|
};
|
|
428
437
|
const startRun = async (record, message, runOptions) => {
|
|
429
|
-
const history = (await store.conversation(record
|
|
438
|
+
const history = (await store.conversation(conversationKey(record))) ?? [];
|
|
430
439
|
const said = messageOf('user', textOfParts(message.parts));
|
|
431
440
|
const result = await graph.invoke(options.input ? options.input(message, history) : { messages: [...history, said] }, runOptions);
|
|
432
|
-
await store.saveConversation(record
|
|
441
|
+
await store.saveConversation(conversationKey(record), [...history, said]);
|
|
433
442
|
return result;
|
|
434
443
|
};
|
|
435
444
|
const carryOn = async (record, runOptions) => {
|
|
@@ -481,11 +490,12 @@ export function a2aHandler(graph, options) {
|
|
|
481
490
|
void recover().catch((error) => options.onError?.(error));
|
|
482
491
|
}, options.recoverEveryMs);
|
|
483
492
|
timer?.unref?.();
|
|
484
|
-
const taskFor = async (message, principal) => {
|
|
493
|
+
const taskFor = async (message, principal, mine, tenant) => {
|
|
485
494
|
if (message.taskId) {
|
|
486
495
|
const existing = await store.get(message.taskId);
|
|
487
|
-
if (!existing)
|
|
496
|
+
if (!existing || !mine(existing)) {
|
|
488
497
|
return { error: A2A_ERRORS.taskNotFound, message: `Task ${message.taskId} was not found` };
|
|
498
|
+
}
|
|
489
499
|
if (existing.task.status.state !== 'TASK_STATE_INPUT_REQUIRED') {
|
|
490
500
|
return { error: A2A_ERRORS.unsupportedOperation, message: `Task ${message.taskId} is not waiting for input` };
|
|
491
501
|
}
|
|
@@ -504,6 +514,7 @@ export function a2aHandler(graph, options) {
|
|
|
504
514
|
history: [said],
|
|
505
515
|
run: { kind: 'invoke', message: said, ...(principal ? { principal } : {}) },
|
|
506
516
|
updatedAt: new Date().toISOString(),
|
|
517
|
+
...(tenant !== undefined ? { tenant } : {}),
|
|
507
518
|
};
|
|
508
519
|
await store.put(record);
|
|
509
520
|
return record;
|
|
@@ -590,9 +601,29 @@ export function a2aHandler(graph, options) {
|
|
|
590
601
|
const pushAnswer = (taskId, config, binding) => binding === 'JSONRPC' ? { taskId, ...config, pushNotificationConfig: config } : { taskId, ...config };
|
|
591
602
|
const fail = (code, message) => ({ error: { code, message } });
|
|
592
603
|
const call = async (method, params, context = {}) => {
|
|
593
|
-
|
|
604
|
+
let principal = context.principal;
|
|
594
605
|
const binding = context.binding ?? 'JSONRPC';
|
|
595
606
|
const pushOn = Boolean(options.pushNotifications);
|
|
607
|
+
const named = typeof params.tenant === 'string' && params.tenant !== '' ? params.tenant : undefined;
|
|
608
|
+
if (tenancy) {
|
|
609
|
+
if (named === undefined) {
|
|
610
|
+
if (tenancy.required !== false)
|
|
611
|
+
return fail(-32602, 'This agent serves several tenants: name one as tenant');
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
const allowed = tenancy.authorize ? await tenancy.authorize(named, principal) : principal?.tenantId === named;
|
|
615
|
+
if (!allowed)
|
|
616
|
+
return fail(A2A_ERRORS.permissionDenied, `This caller may not act for tenant ${named}`);
|
|
617
|
+
if (principal?.tenantId !== named)
|
|
618
|
+
principal = { ...principal, tenantId: named };
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
const tenant = tenancy ? named : undefined;
|
|
622
|
+
const mine = (record) => !tenancy || record.tenant === tenant;
|
|
623
|
+
const own = async (id) => {
|
|
624
|
+
const record = await store.get(id);
|
|
625
|
+
return record && mine(record) ? record : undefined;
|
|
626
|
+
};
|
|
596
627
|
try {
|
|
597
628
|
switch (method) {
|
|
598
629
|
case 'SendMessage':
|
|
@@ -605,7 +636,7 @@ export function a2aHandler(graph, options) {
|
|
|
605
636
|
if (pushConfig && !pushOn) {
|
|
606
637
|
return fail(A2A_ERRORS.pushNotificationNotSupported, 'This agent does not push notifications');
|
|
607
638
|
}
|
|
608
|
-
const record = await taskFor(message, principal);
|
|
639
|
+
const record = await taskFor(message, principal, mine, tenant);
|
|
609
640
|
if ('error' in record)
|
|
610
641
|
return fail(record.error, record.message);
|
|
611
642
|
if (pushConfig) {
|
|
@@ -623,7 +654,7 @@ export function a2aHandler(graph, options) {
|
|
|
623
654
|
return { events: follow(record, () => execute(record, message, principal), context.signal) };
|
|
624
655
|
}
|
|
625
656
|
case 'GetTask': {
|
|
626
|
-
const record = await
|
|
657
|
+
const record = await own(String(params.id ?? ''));
|
|
627
658
|
if (!record)
|
|
628
659
|
return fail(A2A_ERRORS.taskNotFound, `Task ${String(params.id)} was not found`);
|
|
629
660
|
return { result: view(record, typeof params.historyLength === 'number' ? params.historyLength : undefined) };
|
|
@@ -639,6 +670,7 @@ export function a2aHandler(graph, options) {
|
|
|
639
670
|
const listed = await store.list({
|
|
640
671
|
...(typeof params.contextId === 'string' && params.contextId !== '' ? { contextId: params.contextId } : {}),
|
|
641
672
|
...(filtered ? { state: status } : {}),
|
|
673
|
+
...(tenant !== undefined ? { tenant } : {}),
|
|
642
674
|
limit: pageSize,
|
|
643
675
|
offset,
|
|
644
676
|
});
|
|
@@ -648,6 +680,7 @@ export function a2aHandler(graph, options) {
|
|
|
648
680
|
return {
|
|
649
681
|
result: {
|
|
650
682
|
tasks: listed.records
|
|
683
|
+
.filter(mine)
|
|
651
684
|
.filter((record) => !after || (record.task.status.timestamp ?? '') >= after)
|
|
652
685
|
.map((record) => view(record, historyLength))
|
|
653
686
|
.map(({ artifacts, ...task }) => params.includeArtifacts === false ? task : { ...task, ...(artifacts ? { artifacts } : {}) }),
|
|
@@ -658,7 +691,7 @@ export function a2aHandler(graph, options) {
|
|
|
658
691
|
};
|
|
659
692
|
}
|
|
660
693
|
case 'SubscribeToTask': {
|
|
661
|
-
const record = await
|
|
694
|
+
const record = await own(String(params.id ?? ''));
|
|
662
695
|
if (!record)
|
|
663
696
|
return fail(A2A_ERRORS.taskNotFound, `Task ${String(params.id)} was not found`);
|
|
664
697
|
if (TERMINAL.has(record.task.status.state)) {
|
|
@@ -670,7 +703,7 @@ export function a2aHandler(graph, options) {
|
|
|
670
703
|
return { events: follow(record, undefined, context.signal) };
|
|
671
704
|
}
|
|
672
705
|
case 'CancelTask': {
|
|
673
|
-
const record = await
|
|
706
|
+
const record = await own(String(params.id ?? ''));
|
|
674
707
|
if (!record)
|
|
675
708
|
return fail(A2A_ERRORS.taskNotFound, `Task ${String(params.id)} was not found`);
|
|
676
709
|
if (TERMINAL.has(record.task.status.state)) {
|
|
@@ -690,7 +723,7 @@ export function a2aHandler(graph, options) {
|
|
|
690
723
|
if (!pushOn)
|
|
691
724
|
return fail(A2A_ERRORS.pushNotificationNotSupported, 'This agent does not push notifications');
|
|
692
725
|
const taskId = String(params.taskId ?? params.id ?? '');
|
|
693
|
-
const record = await
|
|
726
|
+
const record = await own(taskId);
|
|
694
727
|
if (!record)
|
|
695
728
|
return fail(A2A_ERRORS.taskNotFound, `Task ${taskId} was not found`);
|
|
696
729
|
if (method === 'CreateTaskPushNotificationConfig') {
|
|
@@ -771,8 +804,21 @@ export function a2aHandler(graph, options) {
|
|
|
771
804
|
return rpcError(id, outcome.error.code, outcome.error.message);
|
|
772
805
|
return rpcResult(id, outcome.result);
|
|
773
806
|
};
|
|
774
|
-
const httpJson = async (request,
|
|
807
|
+
const httpJson = async (request, path) => {
|
|
775
808
|
const verb = request.method;
|
|
809
|
+
const known = /^\/(?:message:send$|message:stream$|tasks(?:$|\/)|extendedAgentCard$)/;
|
|
810
|
+
let route = path;
|
|
811
|
+
let routedTenant;
|
|
812
|
+
const prefixed = /^\/([^/]+)(\/.*)$/.exec(path);
|
|
813
|
+
if (!known.test(path) && prefixed && known.test(prefixed[2])) {
|
|
814
|
+
try {
|
|
815
|
+
routedTenant = decodeURIComponent(prefixed[1]);
|
|
816
|
+
}
|
|
817
|
+
catch {
|
|
818
|
+
return restError(-32602, 'The tenant in the path is not valid percent-encoding');
|
|
819
|
+
}
|
|
820
|
+
route = prefixed[2];
|
|
821
|
+
}
|
|
776
822
|
const query = new URL(request.url).searchParams;
|
|
777
823
|
const number = (name) => (query.has(name) ? Number(query.get(name)) : undefined);
|
|
778
824
|
const id = (value) => decodeURIComponent(value ?? '');
|
|
@@ -824,6 +870,8 @@ export function a2aHandler(graph, options) {
|
|
|
824
870
|
const refused = versionRefused(request);
|
|
825
871
|
if (refused)
|
|
826
872
|
return restError(A2A_ERRORS.versionNotSupported, refused);
|
|
873
|
+
if (routedTenant !== undefined)
|
|
874
|
+
params = { ...params, tenant: routedTenant };
|
|
827
875
|
if (body) {
|
|
828
876
|
const text = await request.text();
|
|
829
877
|
try {
|
|
@@ -1034,23 +1082,34 @@ export function a2aClient(options) {
|
|
|
1034
1082
|
});
|
|
1035
1083
|
return cached;
|
|
1036
1084
|
};
|
|
1037
|
-
const
|
|
1038
|
-
|
|
1039
|
-
|
|
1085
|
+
const target = async () => {
|
|
1086
|
+
if (!isCard)
|
|
1087
|
+
return { url: options.url, ...(options.tenant ? { tenant: options.tenant } : {}) };
|
|
1088
|
+
const chosen = (await card()).supportedInterfaces.find((item) => item.protocolBinding === binding);
|
|
1089
|
+
const tenant = options.tenant ?? chosen?.tenant;
|
|
1090
|
+
return { url: chosen?.url ?? options.url, ...(tenant ? { tenant } : {}) };
|
|
1091
|
+
};
|
|
1040
1092
|
const headers = { 'content-type': 'application/json', 'a2a-version': A2A_PROTOCOL_VERSION, ...options.headers };
|
|
1041
1093
|
let ids = 0;
|
|
1042
|
-
const rpc = async (method, params, signal) =>
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1094
|
+
const rpc = async (method, params, signal) => {
|
|
1095
|
+
const { url, tenant } = await target();
|
|
1096
|
+
return fetcher(url, {
|
|
1097
|
+
method: 'POST',
|
|
1098
|
+
headers,
|
|
1099
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: ++ids, method, params: tenant ? { tenant, ...params } : params }),
|
|
1100
|
+
...(signal ? { signal } : {}),
|
|
1101
|
+
});
|
|
1102
|
+
};
|
|
1103
|
+
const route = async (verb, path, body, signal) => {
|
|
1104
|
+
const { url, tenant } = await target();
|
|
1105
|
+
const under = tenant ? `/${encodeURIComponent(tenant)}` : '';
|
|
1106
|
+
return fetcher(`${url.replace(/\/+$/, '')}${under}${path}`, {
|
|
1107
|
+
method: verb,
|
|
1108
|
+
headers: body === undefined ? { ...headers, 'content-type': 'application/json' } : headers,
|
|
1109
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
1110
|
+
...(signal ? { signal } : {}),
|
|
1111
|
+
});
|
|
1112
|
+
};
|
|
1054
1113
|
const restFailure = async (response) => {
|
|
1055
1114
|
const body = (await response.json().catch(() => ({})));
|
|
1056
1115
|
const reason = body.error?.details?.find((detail) => detail.reason)?.reason;
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const NEXUS_VERSION = '2.
|
|
1
|
+
export const NEXUS_VERSION = '2.20.2';
|
|
@@ -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>;
|