@radaros/transport 0.3.5 → 0.3.6
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 +116 -0
- package/dist/index.js +1127 -0
- package/package.json +7 -3
- package/src/a2a/a2a-server.ts +0 -391
- package/src/a2a/agent-card.ts +0 -95
- package/src/a2a/types.ts +0 -11
- package/src/express/file-upload.ts +0 -88
- package/src/express/middleware.ts +0 -15
- package/src/express/router-factory.ts +0 -208
- package/src/express/swagger.ts +0 -476
- package/src/express/types.ts +0 -32
- package/src/index.ts +0 -14
- package/src/socketio/gateway.ts +0 -75
- package/src/socketio/handlers.ts +0 -1
- package/src/socketio/types.ts +0 -9
- package/tsconfig.json +0 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@radaros/transport",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -10,9 +10,13 @@
|
|
|
10
10
|
"types": "./dist/index.d.ts"
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
13
16
|
"scripts": {
|
|
14
17
|
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
15
|
-
"dev": "tsup src/index.ts --format esm --dts --watch"
|
|
18
|
+
"dev": "tsup src/index.ts --format esm --dts --watch",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
16
20
|
},
|
|
17
21
|
"devDependencies": {
|
|
18
22
|
"@types/node": "^25.3.1",
|
|
@@ -20,7 +24,7 @@
|
|
|
20
24
|
"typescript": "^5.6.0"
|
|
21
25
|
},
|
|
22
26
|
"peerDependencies": {
|
|
23
|
-
"@radaros/core": "^0.3.
|
|
27
|
+
"@radaros/core": "^0.3.6",
|
|
24
28
|
"@types/express": "^4.0.0 || ^5.0.0",
|
|
25
29
|
"express": "^4.0.0 || ^5.0.0",
|
|
26
30
|
"multer": ">=1.4.0",
|
package/src/a2a/a2a-server.ts
DELETED
|
@@ -1,391 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
import { randomUUID } from "node:crypto";
|
|
3
|
-
import type { Agent } from "@radaros/core";
|
|
4
|
-
import type {
|
|
5
|
-
A2ATask,
|
|
6
|
-
A2AMessage,
|
|
7
|
-
A2APart,
|
|
8
|
-
A2AJsonRpcRequest,
|
|
9
|
-
A2AJsonRpcResponse,
|
|
10
|
-
A2ATaskState,
|
|
11
|
-
} from "@radaros/core";
|
|
12
|
-
import { generateMultiAgentCard } from "./agent-card.js";
|
|
13
|
-
import type { A2AServerOptions } from "./types.js";
|
|
14
|
-
|
|
15
|
-
const _require = createRequire(import.meta.url);
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* In-memory task store. Can be replaced with a persistent implementation.
|
|
19
|
-
*/
|
|
20
|
-
class TaskStore {
|
|
21
|
-
private tasks = new Map<string, A2ATask>();
|
|
22
|
-
|
|
23
|
-
get(id: string): A2ATask | undefined {
|
|
24
|
-
return this.tasks.get(id);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
set(task: A2ATask): void {
|
|
28
|
-
this.tasks.set(task.id, task);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
updateState(id: string, state: A2ATaskState, message?: A2AMessage): void {
|
|
32
|
-
const task = this.tasks.get(id);
|
|
33
|
-
if (!task) return;
|
|
34
|
-
task.status = {
|
|
35
|
-
state,
|
|
36
|
-
message,
|
|
37
|
-
timestamp: new Date().toISOString(),
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function a2aPartsToText(parts: A2APart[]): string {
|
|
43
|
-
return parts
|
|
44
|
-
.filter((p): p is { kind: "text"; text: string } => p.kind === "text")
|
|
45
|
-
.map((p) => p.text)
|
|
46
|
-
.join("\n");
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function textToA2AParts(text: string): A2APart[] {
|
|
50
|
-
return [{ kind: "text", text }];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function jsonRpcError(
|
|
54
|
-
id: string | number,
|
|
55
|
-
code: number,
|
|
56
|
-
message: string
|
|
57
|
-
): A2AJsonRpcResponse {
|
|
58
|
-
return { jsonrpc: "2.0", id, error: { code, message } };
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function resolveAgent(
|
|
62
|
-
agents: Record<string, Agent>,
|
|
63
|
-
message: A2AMessage
|
|
64
|
-
): Agent | null {
|
|
65
|
-
const meta = message.metadata as Record<string, unknown> | undefined;
|
|
66
|
-
const agentName = meta?.agentName as string | undefined;
|
|
67
|
-
|
|
68
|
-
if (agentName && agents[agentName]) {
|
|
69
|
-
return agents[agentName];
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const names = Object.keys(agents);
|
|
73
|
-
if (names.length === 1) {
|
|
74
|
-
return agents[names[0]];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const textContent = a2aPartsToText(message.parts).toLowerCase();
|
|
78
|
-
for (const [name, agent] of Object.entries(agents)) {
|
|
79
|
-
if (textContent.includes(name.toLowerCase())) {
|
|
80
|
-
return agent;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return agents[names[0]] ?? null;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Mount an A2A-compliant server on an Express app.
|
|
89
|
-
*
|
|
90
|
-
* - Serves `/.well-known/agent.json` with the Agent Card
|
|
91
|
-
* - Handles JSON-RPC 2.0 requests at the basePath for message/send, message/stream, tasks/get, tasks/cancel
|
|
92
|
-
*/
|
|
93
|
-
export function createA2AServer(app: any, opts: A2AServerOptions): void {
|
|
94
|
-
const express = _require("express");
|
|
95
|
-
const basePath = opts.basePath ?? "/";
|
|
96
|
-
const taskStore = new TaskStore();
|
|
97
|
-
|
|
98
|
-
const serverUrl =
|
|
99
|
-
basePath === "/" ? "" : basePath;
|
|
100
|
-
|
|
101
|
-
const agentCard = generateMultiAgentCard(
|
|
102
|
-
opts.agents,
|
|
103
|
-
serverUrl || "/",
|
|
104
|
-
opts.provider,
|
|
105
|
-
opts.version
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
app.get("/.well-known/agent.json", (_req: any, res: any) => {
|
|
109
|
-
res.json(agentCard);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
app.use(basePath, express.json());
|
|
113
|
-
|
|
114
|
-
app.post(basePath, async (req: any, res: any) => {
|
|
115
|
-
const body: A2AJsonRpcRequest = req.body;
|
|
116
|
-
|
|
117
|
-
if (!body || body.jsonrpc !== "2.0" || !body.method) {
|
|
118
|
-
return res.status(400).json(
|
|
119
|
-
jsonRpcError(body?.id ?? 0, -32600, "Invalid JSON-RPC request")
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
switch (body.method) {
|
|
125
|
-
case "message/send":
|
|
126
|
-
return await handleMessageSend(req, res, body, opts.agents, taskStore);
|
|
127
|
-
case "message/stream":
|
|
128
|
-
return await handleMessageStream(req, res, body, opts.agents, taskStore);
|
|
129
|
-
case "tasks/get":
|
|
130
|
-
return handleTasksGet(res, body, taskStore);
|
|
131
|
-
case "tasks/cancel":
|
|
132
|
-
return handleTasksCancel(res, body, taskStore);
|
|
133
|
-
default:
|
|
134
|
-
return res.json(
|
|
135
|
-
jsonRpcError(body.id, -32601, `Method '${body.method}' not found`)
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
} catch (err: any) {
|
|
139
|
-
return res.json(
|
|
140
|
-
jsonRpcError(body.id, -32000, err.message ?? "Internal error")
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
async function handleMessageSend(
|
|
147
|
-
_req: any,
|
|
148
|
-
res: any,
|
|
149
|
-
body: A2AJsonRpcRequest,
|
|
150
|
-
agents: Record<string, Agent>,
|
|
151
|
-
store: TaskStore
|
|
152
|
-
): Promise<void> {
|
|
153
|
-
const params = body.params as any;
|
|
154
|
-
const message: A2AMessage = params?.message;
|
|
155
|
-
|
|
156
|
-
if (!message?.parts?.length) {
|
|
157
|
-
return res.json(jsonRpcError(body.id, -32602, "Missing message.parts"));
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const agent = resolveAgent(agents, message);
|
|
161
|
-
if (!agent) {
|
|
162
|
-
return res.json(jsonRpcError(body.id, -32602, "No matching agent found"));
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const taskId = randomUUID();
|
|
166
|
-
const input = a2aPartsToText(message.parts);
|
|
167
|
-
|
|
168
|
-
const task: A2ATask = {
|
|
169
|
-
id: taskId,
|
|
170
|
-
sessionId: (params?.sessionId as string) ?? undefined,
|
|
171
|
-
status: { state: "submitted", timestamp: new Date().toISOString() },
|
|
172
|
-
history: [message],
|
|
173
|
-
metadata: { agentName: agent.name },
|
|
174
|
-
};
|
|
175
|
-
store.set(task);
|
|
176
|
-
store.updateState(taskId, "working");
|
|
177
|
-
|
|
178
|
-
try {
|
|
179
|
-
const result = await agent.run(input, {
|
|
180
|
-
sessionId: task.sessionId,
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
const responseParts: A2APart[] = textToA2AParts(result.text);
|
|
184
|
-
|
|
185
|
-
if (result.structured) {
|
|
186
|
-
responseParts.push({
|
|
187
|
-
kind: "data",
|
|
188
|
-
data: result.structured as Record<string, unknown>,
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const agentMessage: A2AMessage = {
|
|
193
|
-
role: "agent",
|
|
194
|
-
parts: responseParts,
|
|
195
|
-
messageId: randomUUID(),
|
|
196
|
-
taskId,
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
task.history!.push(agentMessage);
|
|
200
|
-
|
|
201
|
-
if (result.toolCalls?.length) {
|
|
202
|
-
task.artifacts = result.toolCalls.map((tc) => ({
|
|
203
|
-
artifactId: tc.toolCallId,
|
|
204
|
-
name: tc.toolName,
|
|
205
|
-
parts: [
|
|
206
|
-
{
|
|
207
|
-
kind: "text" as const,
|
|
208
|
-
text: typeof tc.result === "string" ? tc.result : tc.result.content,
|
|
209
|
-
},
|
|
210
|
-
],
|
|
211
|
-
}));
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
store.updateState(taskId, "completed", agentMessage);
|
|
215
|
-
|
|
216
|
-
const response: A2AJsonRpcResponse = {
|
|
217
|
-
jsonrpc: "2.0",
|
|
218
|
-
id: body.id,
|
|
219
|
-
result: store.get(taskId),
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
res.json(response);
|
|
223
|
-
} catch (err: any) {
|
|
224
|
-
const errorMessage: A2AMessage = {
|
|
225
|
-
role: "agent",
|
|
226
|
-
parts: [{ kind: "text", text: `Error: ${err.message}` }],
|
|
227
|
-
taskId,
|
|
228
|
-
};
|
|
229
|
-
store.updateState(taskId, "failed", errorMessage);
|
|
230
|
-
|
|
231
|
-
res.json(jsonRpcError(body.id, -32000, err.message));
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
async function handleMessageStream(
|
|
236
|
-
_req: any,
|
|
237
|
-
res: any,
|
|
238
|
-
body: A2AJsonRpcRequest,
|
|
239
|
-
agents: Record<string, Agent>,
|
|
240
|
-
store: TaskStore
|
|
241
|
-
): Promise<void> {
|
|
242
|
-
const params = body.params as any;
|
|
243
|
-
const message: A2AMessage = params?.message;
|
|
244
|
-
|
|
245
|
-
if (!message?.parts?.length) {
|
|
246
|
-
return res.json(jsonRpcError(body.id, -32602, "Missing message.parts"));
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const agent = resolveAgent(agents, message);
|
|
250
|
-
if (!agent) {
|
|
251
|
-
return res.json(jsonRpcError(body.id, -32602, "No matching agent found"));
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const taskId = randomUUID();
|
|
255
|
-
const input = a2aPartsToText(message.parts);
|
|
256
|
-
|
|
257
|
-
const task: A2ATask = {
|
|
258
|
-
id: taskId,
|
|
259
|
-
sessionId: (params?.sessionId as string) ?? undefined,
|
|
260
|
-
status: { state: "submitted", timestamp: new Date().toISOString() },
|
|
261
|
-
history: [message],
|
|
262
|
-
metadata: { agentName: agent.name },
|
|
263
|
-
};
|
|
264
|
-
store.set(task);
|
|
265
|
-
|
|
266
|
-
res.writeHead(200, {
|
|
267
|
-
"Content-Type": "text/event-stream",
|
|
268
|
-
"Cache-Control": "no-cache",
|
|
269
|
-
Connection: "keep-alive",
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
const sendEvent = (data: any) => {
|
|
273
|
-
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
store.updateState(taskId, "working");
|
|
277
|
-
sendEvent({
|
|
278
|
-
jsonrpc: "2.0",
|
|
279
|
-
id: body.id,
|
|
280
|
-
result: store.get(taskId),
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
try {
|
|
284
|
-
let fullText = "";
|
|
285
|
-
for await (const chunk of agent.stream(input, {
|
|
286
|
-
sessionId: task.sessionId,
|
|
287
|
-
})) {
|
|
288
|
-
if (chunk.type === "text") {
|
|
289
|
-
fullText += chunk.text;
|
|
290
|
-
sendEvent({
|
|
291
|
-
jsonrpc: "2.0",
|
|
292
|
-
id: body.id,
|
|
293
|
-
result: {
|
|
294
|
-
id: taskId,
|
|
295
|
-
status: {
|
|
296
|
-
state: "working",
|
|
297
|
-
message: {
|
|
298
|
-
role: "agent",
|
|
299
|
-
parts: [{ kind: "text", text: chunk.text }],
|
|
300
|
-
},
|
|
301
|
-
timestamp: new Date().toISOString(),
|
|
302
|
-
},
|
|
303
|
-
},
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const agentMessage: A2AMessage = {
|
|
309
|
-
role: "agent",
|
|
310
|
-
parts: textToA2AParts(fullText),
|
|
311
|
-
messageId: randomUUID(),
|
|
312
|
-
taskId,
|
|
313
|
-
};
|
|
314
|
-
task.history!.push(agentMessage);
|
|
315
|
-
store.updateState(taskId, "completed", agentMessage);
|
|
316
|
-
|
|
317
|
-
sendEvent({
|
|
318
|
-
jsonrpc: "2.0",
|
|
319
|
-
id: body.id,
|
|
320
|
-
result: store.get(taskId),
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
res.end();
|
|
324
|
-
} catch (err: any) {
|
|
325
|
-
const errorMessage: A2AMessage = {
|
|
326
|
-
role: "agent",
|
|
327
|
-
parts: [{ kind: "text", text: `Error: ${err.message}` }],
|
|
328
|
-
taskId,
|
|
329
|
-
};
|
|
330
|
-
store.updateState(taskId, "failed", errorMessage);
|
|
331
|
-
|
|
332
|
-
sendEvent({
|
|
333
|
-
jsonrpc: "2.0",
|
|
334
|
-
id: body.id,
|
|
335
|
-
result: store.get(taskId),
|
|
336
|
-
});
|
|
337
|
-
res.end();
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
function handleTasksGet(
|
|
342
|
-
res: any,
|
|
343
|
-
body: A2AJsonRpcRequest,
|
|
344
|
-
store: TaskStore
|
|
345
|
-
): void {
|
|
346
|
-
const params = body.params as any;
|
|
347
|
-
const taskId = params?.id;
|
|
348
|
-
|
|
349
|
-
if (!taskId) {
|
|
350
|
-
return res.json(jsonRpcError(body.id, -32602, "Missing task id"));
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
const task = store.get(taskId);
|
|
354
|
-
if (!task) {
|
|
355
|
-
return res.json(jsonRpcError(body.id, -32602, `Task '${taskId}' not found`));
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
const historyLength = params?.historyLength as number | undefined;
|
|
359
|
-
const result = { ...task };
|
|
360
|
-
if (historyLength && result.history) {
|
|
361
|
-
result.history = result.history.slice(-historyLength);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
res.json({ jsonrpc: "2.0", id: body.id, result } as A2AJsonRpcResponse);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
function handleTasksCancel(
|
|
368
|
-
res: any,
|
|
369
|
-
body: A2AJsonRpcRequest,
|
|
370
|
-
store: TaskStore
|
|
371
|
-
): void {
|
|
372
|
-
const params = body.params as any;
|
|
373
|
-
const taskId = params?.id;
|
|
374
|
-
|
|
375
|
-
if (!taskId) {
|
|
376
|
-
return res.json(jsonRpcError(body.id, -32602, "Missing task id"));
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
const task = store.get(taskId);
|
|
380
|
-
if (!task) {
|
|
381
|
-
return res.json(jsonRpcError(body.id, -32602, `Task '${taskId}' not found`));
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
store.updateState(taskId, "canceled");
|
|
385
|
-
|
|
386
|
-
res.json({
|
|
387
|
-
jsonrpc: "2.0",
|
|
388
|
-
id: body.id,
|
|
389
|
-
result: store.get(taskId),
|
|
390
|
-
} as A2AJsonRpcResponse);
|
|
391
|
-
}
|
package/src/a2a/agent-card.ts
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import type { Agent } from "@radaros/core";
|
|
2
|
-
import type {
|
|
3
|
-
A2AAgentCard,
|
|
4
|
-
A2ASkill,
|
|
5
|
-
} from "@radaros/core";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Generate an A2A Agent Card from a RadarOS Agent.
|
|
9
|
-
* The card is served at /.well-known/agent.json per the A2A spec.
|
|
10
|
-
*/
|
|
11
|
-
export function generateAgentCard(
|
|
12
|
-
agent: Agent,
|
|
13
|
-
serverUrl: string,
|
|
14
|
-
provider?: { organization: string; url?: string },
|
|
15
|
-
version?: string
|
|
16
|
-
): A2AAgentCard {
|
|
17
|
-
const skills: A2ASkill[] = agent.tools.map((tool) => ({
|
|
18
|
-
id: tool.name,
|
|
19
|
-
name: tool.name,
|
|
20
|
-
description: tool.description,
|
|
21
|
-
}));
|
|
22
|
-
|
|
23
|
-
if (skills.length === 0) {
|
|
24
|
-
skills.push({
|
|
25
|
-
id: "general",
|
|
26
|
-
name: "General",
|
|
27
|
-
description:
|
|
28
|
-
typeof agent.instructions === "string"
|
|
29
|
-
? agent.instructions.slice(0, 200)
|
|
30
|
-
: "General-purpose agent",
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const description =
|
|
35
|
-
typeof agent.instructions === "string"
|
|
36
|
-
? agent.instructions
|
|
37
|
-
: `RadarOS agent: ${agent.name}`;
|
|
38
|
-
|
|
39
|
-
return {
|
|
40
|
-
name: agent.name,
|
|
41
|
-
description,
|
|
42
|
-
url: serverUrl,
|
|
43
|
-
version: version ?? "1.0.0",
|
|
44
|
-
provider,
|
|
45
|
-
capabilities: {
|
|
46
|
-
streaming: true,
|
|
47
|
-
pushNotifications: false,
|
|
48
|
-
stateTransitionHistory: true,
|
|
49
|
-
},
|
|
50
|
-
skills,
|
|
51
|
-
defaultInputModes: ["text/plain"],
|
|
52
|
-
defaultOutputModes: ["text/plain"],
|
|
53
|
-
supportedInputModes: ["text/plain", "application/json"],
|
|
54
|
-
supportedOutputModes: ["text/plain", "application/json"],
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Generate a combined Agent Card that lists multiple agents as skills.
|
|
60
|
-
*/
|
|
61
|
-
export function generateMultiAgentCard(
|
|
62
|
-
agents: Record<string, Agent>,
|
|
63
|
-
serverUrl: string,
|
|
64
|
-
provider?: { organization: string; url?: string },
|
|
65
|
-
version?: string
|
|
66
|
-
): A2AAgentCard {
|
|
67
|
-
const skills: A2ASkill[] = Object.entries(agents).map(
|
|
68
|
-
([name, agent]) => ({
|
|
69
|
-
id: name,
|
|
70
|
-
name: agent.name,
|
|
71
|
-
description:
|
|
72
|
-
typeof agent.instructions === "string"
|
|
73
|
-
? agent.instructions.slice(0, 200)
|
|
74
|
-
: `Agent: ${name}`,
|
|
75
|
-
})
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
return {
|
|
79
|
-
name: "RadarOS Agent Server",
|
|
80
|
-
description: `Multi-agent server with ${Object.keys(agents).length} agents: ${Object.keys(agents).join(", ")}`,
|
|
81
|
-
url: serverUrl,
|
|
82
|
-
version: version ?? "1.0.0",
|
|
83
|
-
provider,
|
|
84
|
-
capabilities: {
|
|
85
|
-
streaming: true,
|
|
86
|
-
pushNotifications: false,
|
|
87
|
-
stateTransitionHistory: true,
|
|
88
|
-
},
|
|
89
|
-
skills,
|
|
90
|
-
defaultInputModes: ["text/plain"],
|
|
91
|
-
defaultOutputModes: ["text/plain"],
|
|
92
|
-
supportedInputModes: ["text/plain", "application/json"],
|
|
93
|
-
supportedOutputModes: ["text/plain", "application/json"],
|
|
94
|
-
};
|
|
95
|
-
}
|
package/src/a2a/types.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
|
|
3
|
-
const _require = createRequire(import.meta.url);
|
|
4
|
-
|
|
5
|
-
const MIME_TO_PART_TYPE: Record<string, "image" | "audio" | "file"> = {
|
|
6
|
-
"image/png": "image",
|
|
7
|
-
"image/jpeg": "image",
|
|
8
|
-
"image/jpg": "image",
|
|
9
|
-
"image/gif": "image",
|
|
10
|
-
"image/webp": "image",
|
|
11
|
-
"audio/mpeg": "audio",
|
|
12
|
-
"audio/mp3": "audio",
|
|
13
|
-
"audio/wav": "audio",
|
|
14
|
-
"audio/ogg": "audio",
|
|
15
|
-
"audio/webm": "audio",
|
|
16
|
-
"audio/flac": "audio",
|
|
17
|
-
"audio/aac": "audio",
|
|
18
|
-
"audio/mp4": "audio",
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
function getPartType(mimeType: string): "image" | "audio" | "file" {
|
|
22
|
-
return MIME_TO_PART_TYPE[mimeType] ?? "file";
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface FileUploadOptions {
|
|
26
|
-
maxFileSize?: number;
|
|
27
|
-
maxFiles?: number;
|
|
28
|
-
allowedMimeTypes?: string[];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function createFileUploadMiddleware(opts: FileUploadOptions = {}) {
|
|
32
|
-
let multer: any;
|
|
33
|
-
try {
|
|
34
|
-
multer = _require("multer");
|
|
35
|
-
} catch {
|
|
36
|
-
throw new Error(
|
|
37
|
-
"multer is required for file uploads. Install it: npm install multer"
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const storage = multer.memoryStorage();
|
|
42
|
-
const upload = multer({
|
|
43
|
-
storage,
|
|
44
|
-
limits: {
|
|
45
|
-
fileSize: opts.maxFileSize ?? 50 * 1024 * 1024,
|
|
46
|
-
files: opts.maxFiles ?? 10,
|
|
47
|
-
},
|
|
48
|
-
fileFilter: opts.allowedMimeTypes
|
|
49
|
-
? (_req: any, file: any, cb: any) => {
|
|
50
|
-
if (opts.allowedMimeTypes!.includes(file.mimetype)) {
|
|
51
|
-
cb(null, true);
|
|
52
|
-
} else {
|
|
53
|
-
cb(new Error(`File type ${file.mimetype} is not allowed`));
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
: undefined,
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
return upload.array("files", opts.maxFiles ?? 10);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function filesToContentParts(files: any[]): any[] {
|
|
63
|
-
return files.map((file) => {
|
|
64
|
-
const base64 = file.buffer.toString("base64");
|
|
65
|
-
const partType = getPartType(file.mimetype);
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
type: partType,
|
|
69
|
-
data: base64,
|
|
70
|
-
mimeType: file.mimetype,
|
|
71
|
-
...(partType === "file" ? { fileName: file.originalname } : {}),
|
|
72
|
-
};
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function buildMultiModalInput(body: any, files?: any[]): string | any[] {
|
|
77
|
-
const textInput = body?.input;
|
|
78
|
-
if (!files || files.length === 0) {
|
|
79
|
-
return textInput;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const parts: any[] = [];
|
|
83
|
-
if (textInput) {
|
|
84
|
-
parts.push({ type: "text", text: textInput });
|
|
85
|
-
}
|
|
86
|
-
parts.push(...filesToContentParts(files));
|
|
87
|
-
return parts;
|
|
88
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export function errorHandler() {
|
|
2
|
-
return (err: any, _req: any, res: any, _next: any) => {
|
|
3
|
-
console.error("[radaros:transport] Error:", err.message);
|
|
4
|
-
res.status(err.statusCode ?? 500).json({
|
|
5
|
-
error: err.message ?? "Internal server error",
|
|
6
|
-
});
|
|
7
|
-
};
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function requestLogger() {
|
|
11
|
-
return (req: any, _res: any, next: any) => {
|
|
12
|
-
console.log(`[radaros:transport] ${req.method} ${req.path}`);
|
|
13
|
-
next();
|
|
14
|
-
};
|
|
15
|
-
}
|