@radaros/core 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +265 -1
- package/dist/index.js +723 -33
- package/package.json +6 -2
- package/src/a2a/a2a-remote-agent.ts +270 -0
- package/src/a2a/types.ts +142 -0
- package/src/index.ts +27 -1
- package/src/mcp/mcp-client.ts +264 -0
- package/src/models/providers/vertex.ts +400 -0
- package/src/models/registry.ts +17 -0
- package/src/tools/tool-executor.ts +17 -9
- package/src/tools/types.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@radaros/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"mongodb": "^6.0.0",
|
|
34
34
|
"ollama": "^0.5.0",
|
|
35
35
|
"openai": "^4.0.0 || ^5.0.0 || ^6.0.0",
|
|
36
|
-
"pg": "^8.0.0"
|
|
36
|
+
"pg": "^8.0.0",
|
|
37
|
+
"@modelcontextprotocol/sdk": ">=1.0.0"
|
|
37
38
|
},
|
|
38
39
|
"peerDependenciesMeta": {
|
|
39
40
|
"openai": {
|
|
@@ -59,6 +60,9 @@
|
|
|
59
60
|
},
|
|
60
61
|
"@qdrant/js-client-rest": {
|
|
61
62
|
"optional": true
|
|
63
|
+
},
|
|
64
|
+
"@modelcontextprotocol/sdk": {
|
|
65
|
+
"optional": true
|
|
62
66
|
}
|
|
63
67
|
}
|
|
64
68
|
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
A2AAgentCard,
|
|
3
|
+
A2AJsonRpcRequest,
|
|
4
|
+
A2AJsonRpcResponse,
|
|
5
|
+
A2AMessage,
|
|
6
|
+
A2APart,
|
|
7
|
+
A2ATask,
|
|
8
|
+
} from "./types.js";
|
|
9
|
+
import type { RunOpts, RunOutput } from "../agent/types.js";
|
|
10
|
+
import type { ToolDef, ToolResult } from "../tools/types.js";
|
|
11
|
+
import type { StreamChunk } from "../models/types.js";
|
|
12
|
+
import type { RunContext } from "../agent/run-context.js";
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
|
|
15
|
+
export interface A2ARemoteAgentConfig {
|
|
16
|
+
url: string;
|
|
17
|
+
/** Custom headers for every request (e.g. auth tokens). */
|
|
18
|
+
headers?: Record<string, string>;
|
|
19
|
+
/** Override the discovered name. */
|
|
20
|
+
name?: string;
|
|
21
|
+
/** Request timeout in ms (default 60000). */
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A2ARemoteAgent wraps a remote A2A-compliant agent.
|
|
27
|
+
* It can be used as a tool, a Team member, or called directly.
|
|
28
|
+
*/
|
|
29
|
+
export class A2ARemoteAgent {
|
|
30
|
+
readonly url: string;
|
|
31
|
+
name: string;
|
|
32
|
+
instructions: string;
|
|
33
|
+
skills: Array<{ id: string; name: string; description?: string }> = [];
|
|
34
|
+
|
|
35
|
+
private headers: Record<string, string>;
|
|
36
|
+
private timeoutMs: number;
|
|
37
|
+
private card: A2AAgentCard | null = null;
|
|
38
|
+
private rpcId = 0;
|
|
39
|
+
|
|
40
|
+
get tools(): ToolDef[] {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get modelId(): string {
|
|
45
|
+
return "a2a-remote";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get providerId(): string {
|
|
49
|
+
return "a2a";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
get hasStructuredOutput(): boolean {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
constructor(config: A2ARemoteAgentConfig) {
|
|
57
|
+
this.url = config.url.replace(/\/$/, "");
|
|
58
|
+
this.name = config.name ?? "remote-agent";
|
|
59
|
+
this.instructions = "";
|
|
60
|
+
this.headers = config.headers ?? {};
|
|
61
|
+
this.timeoutMs = config.timeoutMs ?? 60_000;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Fetch the Agent Card from /.well-known/agent.json and populate metadata.
|
|
66
|
+
*/
|
|
67
|
+
async discover(): Promise<A2AAgentCard> {
|
|
68
|
+
const res = await fetch(`${this.url}/.well-known/agent.json`, {
|
|
69
|
+
headers: this.headers,
|
|
70
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (!res.ok) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`A2A discover failed: ${res.status} ${res.statusText} from ${this.url}`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
this.card = (await res.json()) as A2AAgentCard;
|
|
80
|
+
this.name = this.card.name;
|
|
81
|
+
this.instructions = this.card.description ?? "";
|
|
82
|
+
this.skills =
|
|
83
|
+
this.card.skills?.map((s) => ({
|
|
84
|
+
id: s.id,
|
|
85
|
+
name: s.name,
|
|
86
|
+
description: s.description,
|
|
87
|
+
})) ?? [];
|
|
88
|
+
|
|
89
|
+
return this.card;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Synchronous run: sends message/send and returns RunOutput.
|
|
94
|
+
*/
|
|
95
|
+
async run(
|
|
96
|
+
input: string,
|
|
97
|
+
opts?: RunOpts
|
|
98
|
+
): Promise<RunOutput> {
|
|
99
|
+
const message: A2AMessage = {
|
|
100
|
+
role: "user",
|
|
101
|
+
parts: [{ kind: "text", text: input }],
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const rpcReq: A2AJsonRpcRequest = {
|
|
105
|
+
jsonrpc: "2.0",
|
|
106
|
+
id: ++this.rpcId,
|
|
107
|
+
method: "message/send",
|
|
108
|
+
params: {
|
|
109
|
+
message,
|
|
110
|
+
...(opts?.sessionId ? { sessionId: opts.sessionId } : {}),
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const startMs = Date.now();
|
|
115
|
+
const res = await fetch(this.url, {
|
|
116
|
+
method: "POST",
|
|
117
|
+
headers: {
|
|
118
|
+
"Content-Type": "application/json",
|
|
119
|
+
...this.headers,
|
|
120
|
+
},
|
|
121
|
+
body: JSON.stringify(rpcReq),
|
|
122
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (!res.ok) {
|
|
126
|
+
throw new Error(`A2A message/send failed: ${res.status} ${res.statusText}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const rpcRes = (await res.json()) as A2AJsonRpcResponse;
|
|
130
|
+
if (rpcRes.error) {
|
|
131
|
+
throw new Error(`A2A error: ${rpcRes.error.message}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const task = rpcRes.result as A2ATask;
|
|
135
|
+
const agentMsg = task.status?.message;
|
|
136
|
+
const text = agentMsg
|
|
137
|
+
? this.partsToText(agentMsg.parts)
|
|
138
|
+
: "";
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
text,
|
|
142
|
+
toolCalls: [],
|
|
143
|
+
usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
144
|
+
durationMs: Date.now() - startMs,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Streaming run: sends message/stream and yields StreamChunks from SSE.
|
|
150
|
+
*/
|
|
151
|
+
async *stream(
|
|
152
|
+
input: string,
|
|
153
|
+
opts?: RunOpts
|
|
154
|
+
): AsyncGenerator<StreamChunk> {
|
|
155
|
+
const message: A2AMessage = {
|
|
156
|
+
role: "user",
|
|
157
|
+
parts: [{ kind: "text", text: input }],
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const rpcReq: A2AJsonRpcRequest = {
|
|
161
|
+
jsonrpc: "2.0",
|
|
162
|
+
id: ++this.rpcId,
|
|
163
|
+
method: "message/stream",
|
|
164
|
+
params: {
|
|
165
|
+
message,
|
|
166
|
+
...(opts?.sessionId ? { sessionId: opts.sessionId } : {}),
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const res = await fetch(this.url, {
|
|
171
|
+
method: "POST",
|
|
172
|
+
headers: {
|
|
173
|
+
"Content-Type": "application/json",
|
|
174
|
+
...this.headers,
|
|
175
|
+
},
|
|
176
|
+
body: JSON.stringify(rpcReq),
|
|
177
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
if (!res.ok) {
|
|
181
|
+
throw new Error(`A2A message/stream failed: ${res.status} ${res.statusText}`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (!res.body) {
|
|
185
|
+
throw new Error("A2A message/stream: no response body for SSE");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const reader = res.body.getReader();
|
|
189
|
+
const decoder = new TextDecoder();
|
|
190
|
+
let buffer = "";
|
|
191
|
+
|
|
192
|
+
while (true) {
|
|
193
|
+
const { done, value } = await reader.read();
|
|
194
|
+
if (done) break;
|
|
195
|
+
|
|
196
|
+
buffer += decoder.decode(value, { stream: true });
|
|
197
|
+
const lines = buffer.split("\n");
|
|
198
|
+
buffer = lines.pop()!;
|
|
199
|
+
|
|
200
|
+
for (const line of lines) {
|
|
201
|
+
if (!line.startsWith("data: ")) continue;
|
|
202
|
+
const jsonStr = line.slice(6).trim();
|
|
203
|
+
if (!jsonStr) continue;
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const event = JSON.parse(jsonStr) as A2AJsonRpcResponse;
|
|
207
|
+
const task = event.result as A2ATask | undefined;
|
|
208
|
+
if (!task) continue;
|
|
209
|
+
|
|
210
|
+
if (
|
|
211
|
+
task.status?.state === "working" &&
|
|
212
|
+
task.status.message?.parts?.length
|
|
213
|
+
) {
|
|
214
|
+
for (const part of task.status.message.parts) {
|
|
215
|
+
if (part.kind === "text") {
|
|
216
|
+
yield { type: "text", text: part.text };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (task.status?.state === "completed") {
|
|
222
|
+
yield {
|
|
223
|
+
type: "finish",
|
|
224
|
+
finishReason: "stop",
|
|
225
|
+
usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
} catch {
|
|
229
|
+
// skip unparseable lines
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Wrap this remote agent as a ToolDef so it can be used by an orchestrator agent.
|
|
237
|
+
*/
|
|
238
|
+
asTool(): ToolDef {
|
|
239
|
+
const self = this;
|
|
240
|
+
return {
|
|
241
|
+
name: `a2a_${this.name.replace(/[^a-zA-Z0-9_]/g, "_")}`,
|
|
242
|
+
description:
|
|
243
|
+
this.instructions ||
|
|
244
|
+
`Remote A2A agent: ${this.name}`,
|
|
245
|
+
parameters: z.object({
|
|
246
|
+
message: z.string().describe("The message to send to the remote agent"),
|
|
247
|
+
}),
|
|
248
|
+
async execute(
|
|
249
|
+
args: Record<string, unknown>,
|
|
250
|
+
_ctx: RunContext
|
|
251
|
+
): Promise<string | ToolResult> {
|
|
252
|
+
const result = await self.run(args.message as string);
|
|
253
|
+
return result.text;
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
getAgentCard(): A2AAgentCard | null {
|
|
259
|
+
return this.card;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private partsToText(parts: A2APart[]): string {
|
|
263
|
+
return parts
|
|
264
|
+
.filter(
|
|
265
|
+
(p): p is { kind: "text"; text: string } => p.kind === "text"
|
|
266
|
+
)
|
|
267
|
+
.map((p) => p.text)
|
|
268
|
+
.join("\n");
|
|
269
|
+
}
|
|
270
|
+
}
|
package/src/a2a/types.ts
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2A (Agent-to-Agent) Protocol types.
|
|
3
|
+
* Based on the A2A specification v0.2.
|
|
4
|
+
* https://google.github.io/A2A/specification/
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// ── Parts ─────────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
export interface A2ATextPart {
|
|
10
|
+
kind: "text";
|
|
11
|
+
text: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface A2AFilePart {
|
|
15
|
+
kind: "file";
|
|
16
|
+
file: {
|
|
17
|
+
name?: string;
|
|
18
|
+
mimeType?: string;
|
|
19
|
+
bytes?: string;
|
|
20
|
+
uri?: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface A2ADataPart {
|
|
25
|
+
kind: "data";
|
|
26
|
+
data: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type A2APart = A2ATextPart | A2AFilePart | A2ADataPart;
|
|
30
|
+
|
|
31
|
+
// ── Messages ──────────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
export interface A2AMessage {
|
|
34
|
+
role: "user" | "agent";
|
|
35
|
+
parts: A2APart[];
|
|
36
|
+
messageId?: string;
|
|
37
|
+
taskId?: string;
|
|
38
|
+
referenceTaskIds?: string[];
|
|
39
|
+
metadata?: Record<string, unknown>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ── Task ──────────────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
export type A2ATaskState =
|
|
45
|
+
| "submitted"
|
|
46
|
+
| "working"
|
|
47
|
+
| "input-required"
|
|
48
|
+
| "completed"
|
|
49
|
+
| "failed"
|
|
50
|
+
| "canceled";
|
|
51
|
+
|
|
52
|
+
export interface A2AArtifact {
|
|
53
|
+
artifactId: string;
|
|
54
|
+
name?: string;
|
|
55
|
+
description?: string;
|
|
56
|
+
parts: A2APart[];
|
|
57
|
+
metadata?: Record<string, unknown>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface A2ATask {
|
|
61
|
+
id: string;
|
|
62
|
+
sessionId?: string;
|
|
63
|
+
status: {
|
|
64
|
+
state: A2ATaskState;
|
|
65
|
+
message?: A2AMessage;
|
|
66
|
+
timestamp?: string;
|
|
67
|
+
};
|
|
68
|
+
artifacts?: A2AArtifact[];
|
|
69
|
+
history?: A2AMessage[];
|
|
70
|
+
metadata?: Record<string, unknown>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ── Agent Card ────────────────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
export interface A2ASkill {
|
|
76
|
+
id: string;
|
|
77
|
+
name: string;
|
|
78
|
+
description?: string;
|
|
79
|
+
tags?: string[];
|
|
80
|
+
examples?: string[];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface A2AAgentCard {
|
|
84
|
+
name: string;
|
|
85
|
+
description?: string;
|
|
86
|
+
url: string;
|
|
87
|
+
version?: string;
|
|
88
|
+
provider?: {
|
|
89
|
+
organization: string;
|
|
90
|
+
url?: string;
|
|
91
|
+
};
|
|
92
|
+
capabilities?: {
|
|
93
|
+
streaming?: boolean;
|
|
94
|
+
pushNotifications?: boolean;
|
|
95
|
+
stateTransitionHistory?: boolean;
|
|
96
|
+
};
|
|
97
|
+
authentication?: {
|
|
98
|
+
schemes?: string[];
|
|
99
|
+
credentials?: string;
|
|
100
|
+
};
|
|
101
|
+
skills?: A2ASkill[];
|
|
102
|
+
defaultInputModes?: string[];
|
|
103
|
+
defaultOutputModes?: string[];
|
|
104
|
+
supportedInputModes?: string[];
|
|
105
|
+
supportedOutputModes?: string[];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ── JSON-RPC ──────────────────────────────────────────────────────────────
|
|
109
|
+
|
|
110
|
+
export interface A2AJsonRpcRequest {
|
|
111
|
+
jsonrpc: "2.0";
|
|
112
|
+
id: string | number;
|
|
113
|
+
method: string;
|
|
114
|
+
params?: Record<string, unknown>;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface A2AJsonRpcResponse {
|
|
118
|
+
jsonrpc: "2.0";
|
|
119
|
+
id: string | number;
|
|
120
|
+
result?: unknown;
|
|
121
|
+
error?: {
|
|
122
|
+
code: number;
|
|
123
|
+
message: string;
|
|
124
|
+
data?: unknown;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ── Send/Stream params ────────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
export interface A2ASendParams {
|
|
131
|
+
message: A2AMessage;
|
|
132
|
+
configuration?: {
|
|
133
|
+
acceptedOutputModes?: string[];
|
|
134
|
+
blocking?: boolean;
|
|
135
|
+
};
|
|
136
|
+
metadata?: Record<string, unknown>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface A2ATaskQueryParams {
|
|
140
|
+
id: string;
|
|
141
|
+
historyLength?: number;
|
|
142
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -49,11 +49,13 @@ export type {
|
|
|
49
49
|
ModelConfig,
|
|
50
50
|
} from "./models/types.js";
|
|
51
51
|
export { getTextContent, isMultiModal } from "./models/types.js";
|
|
52
|
-
export { ModelRegistry, registry, openai, anthropic, google, ollama } from "./models/registry.js";
|
|
52
|
+
export { ModelRegistry, registry, openai, anthropic, google, ollama, vertex } from "./models/registry.js";
|
|
53
53
|
export { OpenAIProvider } from "./models/providers/openai.js";
|
|
54
54
|
export { AnthropicProvider } from "./models/providers/anthropic.js";
|
|
55
55
|
export { GoogleProvider } from "./models/providers/google.js";
|
|
56
56
|
export { OllamaProvider } from "./models/providers/ollama.js";
|
|
57
|
+
export { VertexAIProvider } from "./models/providers/vertex.js";
|
|
58
|
+
export type { VertexAIConfig } from "./models/providers/vertex.js";
|
|
57
59
|
|
|
58
60
|
// Tools
|
|
59
61
|
export { defineTool } from "./tools/define-tool.js";
|
|
@@ -109,3 +111,27 @@ export type { AgentEventMap } from "./events/types.js";
|
|
|
109
111
|
// Logger
|
|
110
112
|
export { Logger } from "./logger/logger.js";
|
|
111
113
|
export type { LogLevel, LoggerConfig } from "./logger/logger.js";
|
|
114
|
+
|
|
115
|
+
// MCP
|
|
116
|
+
export { MCPToolProvider } from "./mcp/mcp-client.js";
|
|
117
|
+
export type { MCPToolProviderConfig } from "./mcp/mcp-client.js";
|
|
118
|
+
|
|
119
|
+
// A2A
|
|
120
|
+
export { A2ARemoteAgent } from "./a2a/a2a-remote-agent.js";
|
|
121
|
+
export type { A2ARemoteAgentConfig } from "./a2a/a2a-remote-agent.js";
|
|
122
|
+
export type {
|
|
123
|
+
A2AAgentCard,
|
|
124
|
+
A2ASkill,
|
|
125
|
+
A2ATask,
|
|
126
|
+
A2ATaskState,
|
|
127
|
+
A2AMessage,
|
|
128
|
+
A2APart,
|
|
129
|
+
A2ATextPart,
|
|
130
|
+
A2AFilePart,
|
|
131
|
+
A2ADataPart,
|
|
132
|
+
A2AArtifact,
|
|
133
|
+
A2AJsonRpcRequest,
|
|
134
|
+
A2AJsonRpcResponse,
|
|
135
|
+
A2ASendParams,
|
|
136
|
+
A2ATaskQueryParams,
|
|
137
|
+
} from "./a2a/types.js";
|