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/evaluate/emulate.js
CHANGED
|
@@ -1,4 +1,53 @@
|
|
|
1
|
+
import { capabilitiesOf, parseCapability } from '../agent/capabilities.js';
|
|
1
2
|
import { sha256Hex } from '../utils/sha256.js';
|
|
3
|
+
export class EmulatedWorld {
|
|
4
|
+
scenario;
|
|
5
|
+
log;
|
|
6
|
+
constructor(start = {}) {
|
|
7
|
+
if (start.scenario !== undefined)
|
|
8
|
+
this.scenario = start.scenario;
|
|
9
|
+
this.log = JSON.parse(JSON.stringify(start.calls ?? []));
|
|
10
|
+
}
|
|
11
|
+
get calls() {
|
|
12
|
+
return JSON.parse(JSON.stringify(this.log));
|
|
13
|
+
}
|
|
14
|
+
get changes() {
|
|
15
|
+
return JSON.parse(JSON.stringify(this.log.filter((call) => call.changed)));
|
|
16
|
+
}
|
|
17
|
+
record(call, changed) {
|
|
18
|
+
this.log.push({ ...JSON.parse(JSON.stringify(call)), changed });
|
|
19
|
+
}
|
|
20
|
+
digest() {
|
|
21
|
+
return sha256Hex(canonical({
|
|
22
|
+
scenario: this.scenario ?? '',
|
|
23
|
+
changes: this.changes.map((call) => ({ tool: call.tool, args: call.args, result: call.result ?? null })),
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
ordinal(tool, args) {
|
|
27
|
+
const identity = canonical(args);
|
|
28
|
+
let count = 0;
|
|
29
|
+
for (let index = this.log.length - 1; index >= 0; index -= 1) {
|
|
30
|
+
const call = this.log[index];
|
|
31
|
+
if (call.changed)
|
|
32
|
+
break;
|
|
33
|
+
if (call.tool === tool && canonical(call.args) === identity)
|
|
34
|
+
count += 1;
|
|
35
|
+
}
|
|
36
|
+
return count;
|
|
37
|
+
}
|
|
38
|
+
snapshot() {
|
|
39
|
+
return {
|
|
40
|
+
...(this.scenario !== undefined ? { scenario: this.scenario } : {}),
|
|
41
|
+
calls: JSON.parse(JSON.stringify(this.log)),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
fork() {
|
|
45
|
+
return new EmulatedWorld(this.snapshot());
|
|
46
|
+
}
|
|
47
|
+
toJSON() {
|
|
48
|
+
return this.snapshot();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
2
51
|
export class EmulationError extends Error {
|
|
3
52
|
code;
|
|
4
53
|
tool;
|
|
@@ -30,7 +79,23 @@ export class MemoryEmulationStore {
|
|
|
30
79
|
}
|
|
31
80
|
}
|
|
32
81
|
export function emulationKey(call) {
|
|
33
|
-
return sha256Hex(canonical({
|
|
82
|
+
return sha256Hex(canonical({
|
|
83
|
+
tool: call.tool,
|
|
84
|
+
args: call.args,
|
|
85
|
+
scenario: call.scenario ?? '',
|
|
86
|
+
ordinal: call.ordinal ?? 0,
|
|
87
|
+
...(call.world !== undefined ? { world: call.world } : {}),
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
function onlyReads(definition, args, reads) {
|
|
91
|
+
if (reads.has(definition.name))
|
|
92
|
+
return true;
|
|
93
|
+
const declared = capabilitiesOf(definition, args);
|
|
94
|
+
if (declared === undefined)
|
|
95
|
+
return false;
|
|
96
|
+
const parsed = declared.map((capability) => parseCapability(capability));
|
|
97
|
+
const isRead = (item) => (item.kind === 'filesystem' && item.access === 'read') || item.kind === 'mcp.read' || item.kind === 'api.read';
|
|
98
|
+
return (parsed.every((item) => isRead(item) || item.kind === 'network') && (parsed.length === 0 || parsed.some(isRead)));
|
|
34
99
|
}
|
|
35
100
|
export function emulatedTools(tools, options) {
|
|
36
101
|
const mode = options.mode ?? (options.store ? 'auto' : 'emulate');
|
|
@@ -40,6 +105,10 @@ export function emulatedTools(tools, options) {
|
|
|
40
105
|
const limit = Math.max(0, options.history ?? 20);
|
|
41
106
|
const history = [];
|
|
42
107
|
const ordinals = new Map();
|
|
108
|
+
const world = options.world;
|
|
109
|
+
const reads = new Set(options.reads ?? []);
|
|
110
|
+
const scenario = options.scenario ?? world?.scenario;
|
|
111
|
+
const told = scenario === options.scenario ? options : { ...options, scenario };
|
|
43
112
|
const report = (call) => {
|
|
44
113
|
try {
|
|
45
114
|
options.onCall?.(call);
|
|
@@ -47,6 +116,18 @@ export function emulatedTools(tools, options) {
|
|
|
47
116
|
catch {
|
|
48
117
|
}
|
|
49
118
|
};
|
|
119
|
+
const earlier = () => {
|
|
120
|
+
if (!world)
|
|
121
|
+
return limit === 0 ? [] : history.slice(-limit);
|
|
122
|
+
const calls = world.calls;
|
|
123
|
+
return calls
|
|
124
|
+
.filter((call, index) => call.changed || index >= calls.length - limit)
|
|
125
|
+
.map(({ changed: _changed, ...call }) => call);
|
|
126
|
+
};
|
|
127
|
+
const remember = (definition, answer) => {
|
|
128
|
+
history.push(answer);
|
|
129
|
+
world?.record(answer, answer.error === undefined && !onlyReads(definition, answer.args, reads));
|
|
130
|
+
};
|
|
50
131
|
return tools.map((definition) => {
|
|
51
132
|
const { execute: run, stream, ...shape } = definition;
|
|
52
133
|
if (passthrough.has(definition.name)) {
|
|
@@ -55,13 +136,20 @@ export function emulatedTools(tools, options) {
|
|
|
55
136
|
return {
|
|
56
137
|
...shape,
|
|
57
138
|
execute: async (args, context) => {
|
|
58
|
-
const key = emulationKey({
|
|
139
|
+
const key = emulationKey({
|
|
140
|
+
tool: definition.name,
|
|
141
|
+
args,
|
|
142
|
+
scenario,
|
|
143
|
+
...(world ? { world: world.digest() } : {}),
|
|
144
|
+
});
|
|
59
145
|
try {
|
|
60
146
|
const result = run ? await run(args, context) : await drain(definition, args, context);
|
|
147
|
+
remember(definition, { tool: definition.name, args, result });
|
|
61
148
|
report({ tool: definition.name, args, result, source: 'real', key });
|
|
62
149
|
return result;
|
|
63
150
|
}
|
|
64
151
|
catch (error) {
|
|
152
|
+
remember(definition, { tool: definition.name, args, error: messageOf(error) });
|
|
65
153
|
report({ tool: definition.name, args, error: messageOf(error), source: 'real', key });
|
|
66
154
|
throw error;
|
|
67
155
|
}
|
|
@@ -71,10 +159,22 @@ export function emulatedTools(tools, options) {
|
|
|
71
159
|
return {
|
|
72
160
|
...shape,
|
|
73
161
|
execute: async (args, context) => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
162
|
+
let ordinal;
|
|
163
|
+
if (world) {
|
|
164
|
+
ordinal = world.ordinal(definition.name, args);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const identity = `${definition.name}\u0000${canonical(args)}`;
|
|
168
|
+
ordinal = ordinals.get(identity) ?? 0;
|
|
169
|
+
ordinals.set(identity, ordinal + 1);
|
|
170
|
+
}
|
|
171
|
+
const key = emulationKey({
|
|
172
|
+
tool: definition.name,
|
|
173
|
+
args,
|
|
174
|
+
scenario,
|
|
175
|
+
ordinal,
|
|
176
|
+
...(world ? { world: world.digest() } : {}),
|
|
177
|
+
});
|
|
78
178
|
let answer;
|
|
79
179
|
let source = 'store';
|
|
80
180
|
if (mode === 'auto' || mode === 'replay')
|
|
@@ -83,12 +183,12 @@ export function emulatedTools(tools, options) {
|
|
|
83
183
|
if (mode === 'replay') {
|
|
84
184
|
throw new EmulationError('EMULATION_MISS', definition.name, args, `No kept answer for ${definition.name}(${canonical(args)}), call ${ordinal + 1}: record it first`);
|
|
85
185
|
}
|
|
86
|
-
answer = await emulate(definition, args, context,
|
|
186
|
+
answer = await emulate(definition, args, context, earlier(), told, world !== undefined);
|
|
87
187
|
source = 'model';
|
|
88
188
|
if (mode !== 'emulate')
|
|
89
189
|
await options.store?.set(key, answer);
|
|
90
190
|
}
|
|
91
|
-
|
|
191
|
+
remember(definition, answer);
|
|
92
192
|
report({
|
|
93
193
|
tool: definition.name,
|
|
94
194
|
args,
|
|
@@ -103,9 +203,9 @@ export function emulatedTools(tools, options) {
|
|
|
103
203
|
};
|
|
104
204
|
});
|
|
105
205
|
}
|
|
106
|
-
async function emulate(definition, args, context, earlier, options) {
|
|
206
|
+
async function emulate(definition, args, context, earlier, options, inWorld) {
|
|
107
207
|
const messages = [
|
|
108
|
-
{ role: 'system', content: brief(definition, earlier, options) },
|
|
208
|
+
{ role: 'system', content: brief(definition, earlier, options, inWorld) },
|
|
109
209
|
{ role: 'user', content: `Arguments: ${JSON.stringify(args)}` },
|
|
110
210
|
];
|
|
111
211
|
let problem = '';
|
|
@@ -136,7 +236,7 @@ async function emulate(definition, args, context, earlier, options) {
|
|
|
136
236
|
}
|
|
137
237
|
throw new EmulationError('EMULATION_INVALID', definition.name, args, `The emulator gave no usable answer for ${definition.name}: ${problem}`);
|
|
138
238
|
}
|
|
139
|
-
function brief(definition, earlier, options) {
|
|
239
|
+
function brief(definition, earlier, options, inWorld) {
|
|
140
240
|
const lines = [
|
|
141
241
|
`You are emulating the tool "${definition.name}" for a test, in place of the real one.`,
|
|
142
242
|
'Reply with exactly what the real tool would return for the arguments you are given, as JSON:',
|
|
@@ -163,7 +263,9 @@ function brief(definition, earlier, options) {
|
|
|
163
263
|
}
|
|
164
264
|
}
|
|
165
265
|
if (earlier.length > 0) {
|
|
166
|
-
lines.push('',
|
|
266
|
+
lines.push('', inWorld
|
|
267
|
+
? 'What has happened in this world, oldest first: every change made in it, and the latest calls. Answer as the world now is.'
|
|
268
|
+
: 'Earlier calls in this run, oldest first:');
|
|
167
269
|
for (const call of earlier) {
|
|
168
270
|
const outcome = call.error === undefined ? `returned ${JSON.stringify(call.result)}` : `failed: ${call.error}`;
|
|
169
271
|
lines.push(`- ${call.tool} ${JSON.stringify(call.args)} ${outcome}`);
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
const PHRASES = [
|
|
2
|
+
[/\bplease make sure that\b/gi, 'ensure'],
|
|
3
|
+
[/\byou should\b/gi, 'do'],
|
|
4
|
+
[/\bit is important to\b/gi, 'must'],
|
|
5
|
+
[/\bin order to\b/gi, 'to'],
|
|
6
|
+
[/\bdue to the fact that\b/gi, 'because'],
|
|
7
|
+
[/\bat this point in time\b/gi, 'now'],
|
|
8
|
+
[/\bas soon as possible\b/gi, 'ASAP'],
|
|
9
|
+
];
|
|
1
10
|
export class PromptDensifier {
|
|
2
11
|
densifyRequest(request, config = {}) {
|
|
3
12
|
if (config.enabled === false)
|
|
@@ -19,7 +28,7 @@ export class PromptDensifier {
|
|
|
19
28
|
}
|
|
20
29
|
densifyText(text, techniques, applied, config) {
|
|
21
30
|
const segments = this.splitCodeBlocks(text, config.preserveCodeBlocks !== false);
|
|
22
|
-
|
|
31
|
+
const joined = segments
|
|
23
32
|
.map((segment) => {
|
|
24
33
|
if (segment.type === 'code')
|
|
25
34
|
return segment.value;
|
|
@@ -29,21 +38,15 @@ export class PromptDensifier {
|
|
|
29
38
|
output = output
|
|
30
39
|
.replace(/[ \t]+/g, ' ')
|
|
31
40
|
.replace(/\n{3,}/g, '\n\n')
|
|
32
|
-
.replace(/[ \t]+\n/g, '\n')
|
|
33
|
-
.trim();
|
|
41
|
+
.replace(/[ \t]+\n/g, '\n');
|
|
34
42
|
if (output !== before)
|
|
35
43
|
applied.add('whitespace-cleanup');
|
|
36
44
|
}
|
|
37
45
|
if (techniques.includes('phrase-compression')) {
|
|
38
46
|
const before = output;
|
|
39
|
-
|
|
40
|
-
.replace(
|
|
41
|
-
|
|
42
|
-
.replace(/it is important to/gi, 'must')
|
|
43
|
-
.replace(/in order to/gi, 'to')
|
|
44
|
-
.replace(/due to the fact that/gi, 'because')
|
|
45
|
-
.replace(/at this point in time/gi, 'now')
|
|
46
|
-
.replace(/as soon as possible/gi, 'ASAP');
|
|
47
|
+
for (const [phrase, shorter] of PHRASES) {
|
|
48
|
+
output = output.replace(phrase, (match) => /^[A-Z]/.test(match) ? `${shorter.charAt(0).toUpperCase()}${shorter.slice(1)}` : shorter);
|
|
49
|
+
}
|
|
47
50
|
if (output !== before)
|
|
48
51
|
applied.add('phrase-compression');
|
|
49
52
|
}
|
|
@@ -56,6 +59,12 @@ export class PromptDensifier {
|
|
|
56
59
|
return output;
|
|
57
60
|
})
|
|
58
61
|
.join('');
|
|
62
|
+
if (!techniques.includes('whitespace-cleanup'))
|
|
63
|
+
return joined;
|
|
64
|
+
const trimmed = joined.trim();
|
|
65
|
+
if (trimmed !== joined)
|
|
66
|
+
applied.add('whitespace-cleanup');
|
|
67
|
+
return trimmed;
|
|
59
68
|
}
|
|
60
69
|
splitCodeBlocks(text, preserveCodeBlocks) {
|
|
61
70
|
if (!preserveCodeBlocks)
|
|
@@ -258,7 +258,7 @@ export function a2aGrpcClient(options) {
|
|
|
258
258
|
});
|
|
259
259
|
const abort = () => request.close(http2.constants.NGHTTP2_CANCEL);
|
|
260
260
|
signal?.addEventListener('abort', abort, { once: true });
|
|
261
|
-
request.end(frame(encodeA2a(shape.request, params)));
|
|
261
|
+
request.end(frame(encodeA2a(shape.request, options.tenant ? { tenant: options.tenant, ...params } : params)));
|
|
262
262
|
let status = {};
|
|
263
263
|
request.on('response', (headers) => {
|
|
264
264
|
if (headers['grpc-status'] !== undefined)
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* A2A's messages in Protocol Buffers, as `specification/a2a.proto` of protocol 1.0 numbers them
|
|
3
3
|
* (package `lf.a2a.v1`): each turned into, and read from, the JSON the other bindings carry, so the
|
|
4
4
|
* gRPC binding calls the same methods with the same params. With `google.protobuf.Struct`, `Value`,
|
|
5
|
-
* `Timestamp`, and `google.rpc.Status`.
|
|
6
|
-
*
|
|
5
|
+
* `Timestamp`, and `google.rpc.Status`. A card's security schemes, requirements, and signatures are
|
|
6
|
+
* written and read as the card's JSON has them.
|
|
7
7
|
*/
|
|
8
8
|
/** Each method of `lf.a2a.v1.A2AService`: what it takes, what it answers, and whether it streams. */
|
|
9
9
|
export declare const A2A_GRPC_METHODS: Readonly<Record<string, {
|
|
@@ -95,11 +95,56 @@ const MESSAGES = {
|
|
|
95
95
|
f('version', 5, 'string'),
|
|
96
96
|
f('documentationUrl', 6, 'string', present),
|
|
97
97
|
f('capabilities', 7, { message: 'AgentCapabilities' }),
|
|
98
|
+
f('securitySchemes', 8, { map: 'SecurityScheme' }),
|
|
99
|
+
f('securityRequirements', 9, { message: 'SecurityRequirement' }, many),
|
|
98
100
|
f('defaultInputModes', 10, 'string', all),
|
|
99
101
|
f('defaultOutputModes', 11, 'string', all),
|
|
100
102
|
f('skills', 12, { message: 'AgentSkill' }, all),
|
|
103
|
+
f('signatures', 13, { message: 'AgentCardSignature' }, many),
|
|
101
104
|
f('iconUrl', 14, 'string', present),
|
|
102
105
|
],
|
|
106
|
+
AgentCardSignature: [f('protected', 1, 'string'), f('signature', 2, 'string'), f('header', 3, 'struct')],
|
|
107
|
+
StringList: [f('list', 1, 'string', all)],
|
|
108
|
+
SecurityRequirement: [f('schemes', 1, { map: 'StringList' })],
|
|
109
|
+
SecurityScheme: [
|
|
110
|
+
f('apiKeySecurityScheme', 1, { message: 'APIKeySecurityScheme' }),
|
|
111
|
+
f('httpAuthSecurityScheme', 2, { message: 'HTTPAuthSecurityScheme' }),
|
|
112
|
+
f('oauth2SecurityScheme', 3, { message: 'OAuth2SecurityScheme' }),
|
|
113
|
+
f('openIdConnectSecurityScheme', 4, { message: 'OpenIdConnectSecurityScheme' }),
|
|
114
|
+
f('mtlsSecurityScheme', 5, { message: 'MutualTlsSecurityScheme' }),
|
|
115
|
+
],
|
|
116
|
+
APIKeySecurityScheme: [f('description', 1, 'string'), f('location', 2, 'string'), f('name', 3, 'string')],
|
|
117
|
+
HTTPAuthSecurityScheme: [f('description', 1, 'string'), f('scheme', 2, 'string'), f('bearerFormat', 3, 'string')],
|
|
118
|
+
OAuth2SecurityScheme: [
|
|
119
|
+
f('description', 1, 'string'),
|
|
120
|
+
f('flows', 2, { message: 'OAuthFlows' }),
|
|
121
|
+
f('oauth2MetadataUrl', 3, 'string'),
|
|
122
|
+
],
|
|
123
|
+
OpenIdConnectSecurityScheme: [f('description', 1, 'string'), f('openIdConnectUrl', 2, 'string')],
|
|
124
|
+
MutualTlsSecurityScheme: [f('description', 1, 'string')],
|
|
125
|
+
OAuthFlows: [
|
|
126
|
+
f('authorizationCode', 1, { message: 'AuthorizationCodeOAuthFlow' }),
|
|
127
|
+
f('clientCredentials', 2, { message: 'ClientCredentialsOAuthFlow' }),
|
|
128
|
+
f('implicit', 3, { message: 'ImplicitOAuthFlow' }),
|
|
129
|
+
f('password', 4, { message: 'PasswordOAuthFlow' }),
|
|
130
|
+
f('deviceCode', 5, { message: 'DeviceCodeOAuthFlow' }),
|
|
131
|
+
],
|
|
132
|
+
AuthorizationCodeOAuthFlow: [
|
|
133
|
+
f('authorizationUrl', 1, 'string'),
|
|
134
|
+
f('tokenUrl', 2, 'string'),
|
|
135
|
+
f('refreshUrl', 3, 'string'),
|
|
136
|
+
f('scopes', 4, 'stringMap'),
|
|
137
|
+
f('pkceRequired', 5, 'bool'),
|
|
138
|
+
],
|
|
139
|
+
ClientCredentialsOAuthFlow: [f('tokenUrl', 1, 'string'), f('refreshUrl', 2, 'string'), f('scopes', 3, 'stringMap')],
|
|
140
|
+
ImplicitOAuthFlow: [f('authorizationUrl', 1, 'string'), f('refreshUrl', 2, 'string'), f('scopes', 3, 'stringMap')],
|
|
141
|
+
PasswordOAuthFlow: [f('tokenUrl', 1, 'string'), f('refreshUrl', 2, 'string'), f('scopes', 3, 'stringMap')],
|
|
142
|
+
DeviceCodeOAuthFlow: [
|
|
143
|
+
f('deviceAuthorizationUrl', 1, 'string'),
|
|
144
|
+
f('tokenUrl', 2, 'string'),
|
|
145
|
+
f('refreshUrl', 3, 'string'),
|
|
146
|
+
f('scopes', 4, 'stringMap'),
|
|
147
|
+
],
|
|
103
148
|
AgentProvider: [f('url', 1, 'string'), f('organization', 2, 'string')],
|
|
104
149
|
AgentCapabilities: [
|
|
105
150
|
f('streaming', 1, 'bool', present),
|
|
@@ -121,6 +166,7 @@ const MESSAGES = {
|
|
|
121
166
|
f('examples', 5, 'string', many),
|
|
122
167
|
f('inputModes', 6, 'string', many),
|
|
123
168
|
f('outputModes', 7, 'string', many),
|
|
169
|
+
f('securityRequirements', 8, { message: 'SecurityRequirement' }, many),
|
|
124
170
|
],
|
|
125
171
|
TaskPushNotificationConfig: [
|
|
126
172
|
f('tenant', 1, 'string'),
|
|
@@ -337,6 +383,21 @@ function writeField(writer, field, item, always) {
|
|
|
337
383
|
writer.bytes(inner.finish());
|
|
338
384
|
}
|
|
339
385
|
}
|
|
386
|
+
else if ('map' in type) {
|
|
387
|
+
if (typeof item !== 'object' || item === null || Array.isArray(item))
|
|
388
|
+
return;
|
|
389
|
+
for (const [key, entry] of Object.entries(item)) {
|
|
390
|
+
const inner = new Writer();
|
|
391
|
+
inner.tag(1, 2);
|
|
392
|
+
inner.bytes(utf8.encode(key));
|
|
393
|
+
const value = new Writer();
|
|
394
|
+
writeMessage(value, type.map, (entry ?? {}));
|
|
395
|
+
inner.tag(2, 2);
|
|
396
|
+
inner.bytes(value.finish());
|
|
397
|
+
writer.tag(field.number, 2);
|
|
398
|
+
writer.bytes(inner.finish());
|
|
399
|
+
}
|
|
400
|
+
}
|
|
340
401
|
else if ('enum' in type) {
|
|
341
402
|
const index = typeof item === 'number' ? item : type.enum.indexOf(String(item));
|
|
342
403
|
if (index < 0 || (!always && index === 0))
|
|
@@ -456,8 +517,9 @@ export function decodeA2a(type, bytes) {
|
|
|
456
517
|
const value = readField(field, raw);
|
|
457
518
|
if (value === undefined)
|
|
458
519
|
continue;
|
|
459
|
-
if (field.type === 'stringMap')
|
|
520
|
+
if (field.type === 'stringMap' || (typeof field.type === 'object' && 'map' in field.type)) {
|
|
460
521
|
out[field.json] = { ...out[field.json], ...value };
|
|
522
|
+
}
|
|
461
523
|
else if (field.repeated) {
|
|
462
524
|
const list = out[field.json] ?? [];
|
|
463
525
|
list.push(value);
|
|
@@ -511,6 +573,19 @@ function readField(field, raw) {
|
|
|
511
573
|
}
|
|
512
574
|
return { [key]: value };
|
|
513
575
|
}
|
|
576
|
+
if ('map' in type) {
|
|
577
|
+
if (!raw.bytes)
|
|
578
|
+
return undefined;
|
|
579
|
+
let key = '';
|
|
580
|
+
let value = new Uint8Array();
|
|
581
|
+
for (const inner of rawFields(raw.bytes)) {
|
|
582
|
+
if (inner.number === 1 && inner.bytes)
|
|
583
|
+
key = fromUtf8.decode(inner.bytes);
|
|
584
|
+
if (inner.number === 2 && inner.bytes)
|
|
585
|
+
value = inner.bytes;
|
|
586
|
+
}
|
|
587
|
+
return { [key]: decodeA2a(type.map, value) };
|
|
588
|
+
}
|
|
514
589
|
if ('enum' in type) {
|
|
515
590
|
if (raw.varint === undefined)
|
|
516
591
|
return undefined;
|
package/dist/protocols/a2a.d.ts
CHANGED
|
@@ -69,6 +69,86 @@ export interface A2aTask {
|
|
|
69
69
|
/** Anything else. */
|
|
70
70
|
metadata?: Record<string, unknown>;
|
|
71
71
|
}
|
|
72
|
+
/** One of the OAuth 2.0 flows an `oauth2SecurityScheme` offers: exactly one is set. */
|
|
73
|
+
export type A2aOAuthFlows = {
|
|
74
|
+
authorizationCode: {
|
|
75
|
+
authorizationUrl: string;
|
|
76
|
+
tokenUrl: string;
|
|
77
|
+
refreshUrl?: string;
|
|
78
|
+
scopes: Record<string, string>;
|
|
79
|
+
pkceRequired?: boolean;
|
|
80
|
+
};
|
|
81
|
+
} | {
|
|
82
|
+
clientCredentials: {
|
|
83
|
+
tokenUrl: string;
|
|
84
|
+
refreshUrl?: string;
|
|
85
|
+
scopes: Record<string, string>;
|
|
86
|
+
};
|
|
87
|
+
} | {
|
|
88
|
+
deviceCode: {
|
|
89
|
+
deviceAuthorizationUrl: string;
|
|
90
|
+
tokenUrl: string;
|
|
91
|
+
refreshUrl?: string;
|
|
92
|
+
scopes: Record<string, string>;
|
|
93
|
+
};
|
|
94
|
+
} | {
|
|
95
|
+
implicit: {
|
|
96
|
+
authorizationUrl?: string;
|
|
97
|
+
refreshUrl?: string;
|
|
98
|
+
scopes?: Record<string, string>;
|
|
99
|
+
};
|
|
100
|
+
} | {
|
|
101
|
+
password: {
|
|
102
|
+
tokenUrl?: string;
|
|
103
|
+
refreshUrl?: string;
|
|
104
|
+
scopes?: Record<string, string>;
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
/** How a caller authenticates with an agent, as its card names it: exactly one scheme is set. */
|
|
108
|
+
export type A2aSecurityScheme = {
|
|
109
|
+
apiKeySecurityScheme: {
|
|
110
|
+
description?: string;
|
|
111
|
+
location: 'query' | 'header' | 'cookie';
|
|
112
|
+
name: string;
|
|
113
|
+
};
|
|
114
|
+
} | {
|
|
115
|
+
httpAuthSecurityScheme: {
|
|
116
|
+
description?: string;
|
|
117
|
+
scheme: string;
|
|
118
|
+
bearerFormat?: string;
|
|
119
|
+
};
|
|
120
|
+
} | {
|
|
121
|
+
oauth2SecurityScheme: {
|
|
122
|
+
description?: string;
|
|
123
|
+
flows: A2aOAuthFlows;
|
|
124
|
+
oauth2MetadataUrl?: string;
|
|
125
|
+
};
|
|
126
|
+
} | {
|
|
127
|
+
openIdConnectSecurityScheme: {
|
|
128
|
+
description?: string;
|
|
129
|
+
openIdConnectUrl: string;
|
|
130
|
+
};
|
|
131
|
+
} | {
|
|
132
|
+
mtlsSecurityScheme: {
|
|
133
|
+
description?: string;
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
/** The schemes a call needs, by the names the card gives them, each with the scopes it needs. */
|
|
137
|
+
export interface A2aSecurityRequirement {
|
|
138
|
+
/** Each scheme's name, with its scopes. */
|
|
139
|
+
schemes: Record<string, {
|
|
140
|
+
list: string[];
|
|
141
|
+
}>;
|
|
142
|
+
}
|
|
143
|
+
/** A JSON Web Signature over an agent card, as the card carries it. */
|
|
144
|
+
export interface A2aCardSignature {
|
|
145
|
+
/** The protected header: base64url-encoded JSON. */
|
|
146
|
+
protected: string;
|
|
147
|
+
/** The signature, base64url-encoded. */
|
|
148
|
+
signature: string;
|
|
149
|
+
/** The unprotected header's values. */
|
|
150
|
+
header?: Record<string, unknown>;
|
|
151
|
+
}
|
|
72
152
|
/** A skill an agent card advertises. */
|
|
73
153
|
export interface A2aSkill {
|
|
74
154
|
/** Its id. */
|
|
@@ -81,6 +161,8 @@ export interface A2aSkill {
|
|
|
81
161
|
tags: string[];
|
|
82
162
|
/** Example requests. */
|
|
83
163
|
examples?: string[];
|
|
164
|
+
/** The schemes a call of this skill needs, beyond the card's. */
|
|
165
|
+
securityRequirements?: A2aSecurityRequirement[];
|
|
84
166
|
}
|
|
85
167
|
/** What an agent publishes about itself at `/.well-known/agent-card.json`. */
|
|
86
168
|
export interface A2aAgentCard {
|
|
@@ -114,6 +196,12 @@ export interface A2aAgentCard {
|
|
|
114
196
|
defaultOutputModes: string[];
|
|
115
197
|
/** What it can do. */
|
|
116
198
|
skills: A2aSkill[];
|
|
199
|
+
/** How a caller authenticates, each scheme by a name `securityRequirements` use. */
|
|
200
|
+
securitySchemes?: Record<string, A2aSecurityScheme>;
|
|
201
|
+
/** The schemes a call needs. */
|
|
202
|
+
securityRequirements?: A2aSecurityRequirement[];
|
|
203
|
+
/** Signatures over the card, as given: a signature must be over the card as it is served. */
|
|
204
|
+
signatures?: A2aCardSignature[];
|
|
117
205
|
}
|
|
118
206
|
/** A streamed event: a task, a status change, or a piece of an artifact. */
|
|
119
207
|
export type A2aStreamEvent = {
|
|
@@ -143,6 +231,11 @@ export declare const A2A_ERRORS: {
|
|
|
143
231
|
readonly unsupportedOperation: -32004;
|
|
144
232
|
readonly extendedCardNotConfigured: -32007;
|
|
145
233
|
readonly versionNotSupported: -32009;
|
|
234
|
+
/**
|
|
235
|
+
* This package's own, from the range JSON-RPC leaves to servers: the caller may not act for the
|
|
236
|
+
* tenant it named. HTTP 403, and `PERMISSION_DENIED` over gRPC.
|
|
237
|
+
*/
|
|
238
|
+
readonly permissionDenied: -32099;
|
|
146
239
|
};
|
|
147
240
|
/** Raised by a client when an agent answers with an error, on any binding, as A2A's code. */
|
|
148
241
|
export declare class A2aError extends Error {
|
|
@@ -191,6 +284,8 @@ export interface A2aTaskRecord {
|
|
|
191
284
|
run?: A2aTaskRun;
|
|
192
285
|
/** Bumped by every claim of the lease, so two replicas claiming one task cannot both win. */
|
|
193
286
|
version?: number;
|
|
287
|
+
/** The tenant it belongs to, when the handler serves several: no other tenant's request sees it. */
|
|
288
|
+
tenant?: string;
|
|
194
289
|
}
|
|
195
290
|
/** A replica's hold on a working task. */
|
|
196
291
|
export interface A2aTaskLease {
|
|
@@ -216,6 +311,8 @@ export interface A2aTaskQuery {
|
|
|
216
311
|
contextId?: string;
|
|
217
312
|
/** Only those in this state. */
|
|
218
313
|
state?: A2aTaskState;
|
|
314
|
+
/** Only this tenant's. A store that serves several tenants must honour it. */
|
|
315
|
+
tenant?: string;
|
|
219
316
|
/** Most returned. */
|
|
220
317
|
limit: number;
|
|
221
318
|
/** Skipped first, for paging. */
|
|
@@ -314,6 +411,16 @@ export interface A2aPushOptions {
|
|
|
314
411
|
everyMs?: number;
|
|
315
412
|
};
|
|
316
413
|
}
|
|
414
|
+
/** How one handler serves several tenants, each request routed by the `tenant` it names. */
|
|
415
|
+
export interface A2aTenancyOptions {
|
|
416
|
+
/**
|
|
417
|
+
* Whether a caller may act for the tenant its request names. Defaults to its principal's `tenantId`
|
|
418
|
+
* being that tenant, so a caller nobody authenticated may act for none.
|
|
419
|
+
*/
|
|
420
|
+
authorize?: (tenant: string, principal: Principal | undefined) => boolean | Promise<boolean>;
|
|
421
|
+
/** Whether every request must name a tenant. Defaults to true. */
|
|
422
|
+
required?: boolean;
|
|
423
|
+
}
|
|
317
424
|
/** Options for `a2aHandler()`. */
|
|
318
425
|
export interface A2aServerOptions {
|
|
319
426
|
/** The card's content: the endpoint `url` is where this handler is mounted. */
|
|
@@ -381,6 +488,15 @@ export interface A2aServerOptions {
|
|
|
381
488
|
protocolVersion?: string;
|
|
382
489
|
tenant?: string;
|
|
383
490
|
}>;
|
|
491
|
+
/**
|
|
492
|
+
* Serves several tenants from this one handler, each request routed by the tenant it names: in its
|
|
493
|
+
* params over JSON-RPC, as its path's first segment over HTTP+JSON (`/{tenant}/message:send`), and in
|
|
494
|
+
* its message over gRPC. Each tenant's tasks and conversations are its own, and its runs carry the
|
|
495
|
+
* tenant on their principal, for its credentials and budget. A caller that may not act for the tenant
|
|
496
|
+
* it names is refused with `A2A_ERRORS.permissionDenied`. `true` takes the defaults. Off by default:
|
|
497
|
+
* a request's tenant is not read.
|
|
498
|
+
*/
|
|
499
|
+
tenants?: A2aTenancyOptions | boolean;
|
|
384
500
|
}
|
|
385
501
|
/** The binding a call arrived on. */
|
|
386
502
|
export type A2aBinding = 'JSONRPC' | 'HTTP+JSON' | 'GRPC' | (string & {});
|
|
@@ -483,6 +599,11 @@ export interface A2aClientOptions {
|
|
|
483
599
|
* on `nexus-ai-pro/protocols/a2a/grpc`.
|
|
484
600
|
*/
|
|
485
601
|
binding?: 'JSONRPC' | 'HTTP+JSON';
|
|
602
|
+
/**
|
|
603
|
+
* The tenant every request names. Defaults to the `tenant` of the interface the card lists for the
|
|
604
|
+
* binding, as protocol 1.0 requires a client to send it.
|
|
605
|
+
*/
|
|
606
|
+
tenant?: string;
|
|
486
607
|
}
|
|
487
608
|
/** What to send: text, and optionally data, a conversation, or the paused task it answers. */
|
|
488
609
|
export interface A2aSendOptions {
|