@perkos/perkos-a2a 0.9.0 → 0.9.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/dist/agentic-actions.js +472 -0
- package/dist/agentic-actions.js.map +1 -0
- package/dist/bridge-agent.d.ts +14 -0
- package/dist/bridge-agent.d.ts.map +1 -0
- package/dist/bridge-agent.js +319 -0
- package/dist/bridge-agent.js.map +1 -0
- package/dist/chat-client.d.ts +77 -0
- package/dist/chat-client.d.ts.map +1 -0
- package/dist/chat-client.js +364 -0
- package/dist/chat-client.js.map +1 -0
- package/dist/chat-store.d.ts +87 -0
- package/dist/chat-store.d.ts.map +1 -0
- package/dist/chat-store.js +233 -0
- package/dist/chat-store.js.map +1 -0
- package/dist/chat-types.d.ts +166 -0
- package/dist/chat-types.d.ts.map +1 -0
- package/dist/chat-types.js +13 -0
- package/dist/chat-types.js.map +1 -0
- package/dist/pairing.js +66 -0
- package/dist/pairing.js.map +1 -0
- package/dist/relay-client.js +222 -0
- package/dist/relay-client.js.map +1 -0
- package/dist/relay.js +274 -0
- package/dist/relay.js.map +1 -0
- package/dist/runtime-reply.js +137 -0
- package/dist/runtime-reply.js.map +1 -0
- package/dist/server.js +611 -0
- package/dist/server.js.map +1 -0
- package/package.json +7 -34
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2A Relay Client
|
|
3
|
+
*
|
|
4
|
+
* WebSocket client that connects to an A2A Relay Hub for NAT-friendly
|
|
5
|
+
* bidirectional communication. Handles registration, auto-reconnect
|
|
6
|
+
* with exponential backoff, heartbeats, and message routing.
|
|
7
|
+
*/
|
|
8
|
+
import WebSocket from "ws";
|
|
9
|
+
import { randomUUID } from "crypto";
|
|
10
|
+
const MIN_RECONNECT_MS = 1_000;
|
|
11
|
+
const MAX_RECONNECT_MS = 60_000;
|
|
12
|
+
const HEARTBEAT_INTERVAL_MS = 25_000;
|
|
13
|
+
export class RelayClient {
|
|
14
|
+
ws = null;
|
|
15
|
+
options;
|
|
16
|
+
logger;
|
|
17
|
+
reconnectMs = MIN_RECONNECT_MS;
|
|
18
|
+
reconnectTimer = null;
|
|
19
|
+
heartbeatTimer = null;
|
|
20
|
+
connected = false;
|
|
21
|
+
stopped = false;
|
|
22
|
+
pendingCallbacks = new Map();
|
|
23
|
+
constructor(options) {
|
|
24
|
+
this.options = options;
|
|
25
|
+
this.logger = options.logger || { info: console.log, error: console.error };
|
|
26
|
+
}
|
|
27
|
+
isConnected() {
|
|
28
|
+
return this.connected;
|
|
29
|
+
}
|
|
30
|
+
start() {
|
|
31
|
+
this.stopped = false;
|
|
32
|
+
this.connect();
|
|
33
|
+
}
|
|
34
|
+
stop() {
|
|
35
|
+
this.stopped = true;
|
|
36
|
+
this.clearTimers();
|
|
37
|
+
if (this.ws) {
|
|
38
|
+
this.ws.close(1000, "Client shutting down");
|
|
39
|
+
this.ws = null;
|
|
40
|
+
}
|
|
41
|
+
this.connected = false;
|
|
42
|
+
}
|
|
43
|
+
/** Send a task to another agent via the relay */
|
|
44
|
+
async sendTask(targetAgent, payload) {
|
|
45
|
+
return this.sendAndWait({
|
|
46
|
+
type: "task",
|
|
47
|
+
to: targetAgent,
|
|
48
|
+
id: randomUUID(),
|
|
49
|
+
from: this.options.agentName,
|
|
50
|
+
payload,
|
|
51
|
+
timestamp: new Date().toISOString(),
|
|
52
|
+
}, "task_response", 60_000);
|
|
53
|
+
}
|
|
54
|
+
/** Discover agents connected to the relay */
|
|
55
|
+
async discover() {
|
|
56
|
+
const response = await this.sendAndWait({
|
|
57
|
+
type: "discover",
|
|
58
|
+
id: randomUUID(),
|
|
59
|
+
from: this.options.agentName,
|
|
60
|
+
payload: {},
|
|
61
|
+
timestamp: new Date().toISOString(),
|
|
62
|
+
}, "discover_response", 10_000);
|
|
63
|
+
return response.payload.agents || [];
|
|
64
|
+
}
|
|
65
|
+
/** Send a task response back through the relay */
|
|
66
|
+
sendTaskResponse(originalMsg, result) {
|
|
67
|
+
const response = {
|
|
68
|
+
type: "task_response",
|
|
69
|
+
id: originalMsg.id,
|
|
70
|
+
from: this.options.agentName,
|
|
71
|
+
to: originalMsg.from,
|
|
72
|
+
payload: result,
|
|
73
|
+
timestamp: new Date().toISOString(),
|
|
74
|
+
};
|
|
75
|
+
this.send(response);
|
|
76
|
+
}
|
|
77
|
+
connect() {
|
|
78
|
+
if (this.stopped)
|
|
79
|
+
return;
|
|
80
|
+
const url = this.options.relay.url;
|
|
81
|
+
this.logger.info(`[perkos-a2a] Connecting to relay: ${url}`);
|
|
82
|
+
try {
|
|
83
|
+
this.ws = new WebSocket(url);
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
87
|
+
this.logger.error(`[perkos-a2a] Failed to create WebSocket: ${msg}`);
|
|
88
|
+
this.scheduleReconnect();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
this.ws.on("open", () => {
|
|
92
|
+
this.connected = true;
|
|
93
|
+
this.reconnectMs = MIN_RECONNECT_MS;
|
|
94
|
+
this.logger.info("[perkos-a2a] Connected to relay hub");
|
|
95
|
+
this.register();
|
|
96
|
+
this.startHeartbeat();
|
|
97
|
+
});
|
|
98
|
+
this.ws.on("message", (data) => {
|
|
99
|
+
let msg;
|
|
100
|
+
try {
|
|
101
|
+
msg = JSON.parse(data.toString());
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
this.logger.error("[perkos-a2a] Failed to parse relay message");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
this.handleMessage(msg);
|
|
108
|
+
});
|
|
109
|
+
this.ws.on("close", (code, reason) => {
|
|
110
|
+
this.connected = false;
|
|
111
|
+
this.clearTimers();
|
|
112
|
+
if (!this.stopped) {
|
|
113
|
+
this.logger.info(`[perkos-a2a] Relay connection closed (${code}: ${reason || "no reason"}). Reconnecting...`);
|
|
114
|
+
this.scheduleReconnect();
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
this.ws.on("error", (err) => {
|
|
118
|
+
this.logger.error(`[perkos-a2a] Relay WebSocket error: ${err.message}`);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
register() {
|
|
122
|
+
const msg = {
|
|
123
|
+
type: "register",
|
|
124
|
+
id: randomUUID(),
|
|
125
|
+
from: this.options.agentName,
|
|
126
|
+
payload: {
|
|
127
|
+
agentName: this.options.agentName,
|
|
128
|
+
apiKey: this.options.relay.apiKey,
|
|
129
|
+
card: this.options.card,
|
|
130
|
+
},
|
|
131
|
+
timestamp: new Date().toISOString(),
|
|
132
|
+
};
|
|
133
|
+
this.send(msg);
|
|
134
|
+
}
|
|
135
|
+
handleMessage(msg) {
|
|
136
|
+
// Check for pending request/response callbacks
|
|
137
|
+
const callback = this.pendingCallbacks.get(msg.id);
|
|
138
|
+
if (callback) {
|
|
139
|
+
this.pendingCallbacks.delete(msg.id);
|
|
140
|
+
callback(msg);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
switch (msg.type) {
|
|
144
|
+
case "register_ack":
|
|
145
|
+
this.logger.info("[perkos-a2a] Registered with relay hub");
|
|
146
|
+
break;
|
|
147
|
+
case "task":
|
|
148
|
+
this.options.onTask(msg);
|
|
149
|
+
break;
|
|
150
|
+
case "task_response":
|
|
151
|
+
// Late response with no pending callback; log and discard
|
|
152
|
+
this.logger.info(`[perkos-a2a] Received unmatched task response: ${msg.id}`);
|
|
153
|
+
break;
|
|
154
|
+
case "discover_response":
|
|
155
|
+
if (this.options.onDiscoverResponse) {
|
|
156
|
+
this.options.onDiscoverResponse(msg.payload.agents);
|
|
157
|
+
}
|
|
158
|
+
break;
|
|
159
|
+
case "heartbeat_ack":
|
|
160
|
+
break;
|
|
161
|
+
case "error":
|
|
162
|
+
this.logger.error(`[perkos-a2a] Relay error: ${msg.payload.code} - ${msg.payload.message}`);
|
|
163
|
+
break;
|
|
164
|
+
default:
|
|
165
|
+
this.logger.info(`[perkos-a2a] Unknown relay message type: ${msg.type}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
send(msg) {
|
|
169
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
170
|
+
this.ws.send(JSON.stringify(msg));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async sendAndWait(msg, expectedType, timeoutMs) {
|
|
174
|
+
return new Promise((resolve, reject) => {
|
|
175
|
+
const timer = setTimeout(() => {
|
|
176
|
+
this.pendingCallbacks.delete(msg.id);
|
|
177
|
+
reject(new Error(`Relay request timed out after ${timeoutMs}ms`));
|
|
178
|
+
}, timeoutMs);
|
|
179
|
+
this.pendingCallbacks.set(msg.id, (response) => {
|
|
180
|
+
clearTimeout(timer);
|
|
181
|
+
if (response.type === "error") {
|
|
182
|
+
reject(new Error(`Relay error: ${response.payload.message}`));
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
resolve(response);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
this.send(msg);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
startHeartbeat() {
|
|
192
|
+
this.heartbeatTimer = setInterval(() => {
|
|
193
|
+
const msg = {
|
|
194
|
+
type: "heartbeat",
|
|
195
|
+
id: randomUUID(),
|
|
196
|
+
from: this.options.agentName,
|
|
197
|
+
payload: {},
|
|
198
|
+
timestamp: new Date().toISOString(),
|
|
199
|
+
};
|
|
200
|
+
this.send(msg);
|
|
201
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
202
|
+
}
|
|
203
|
+
scheduleReconnect() {
|
|
204
|
+
if (this.stopped)
|
|
205
|
+
return;
|
|
206
|
+
this.reconnectTimer = setTimeout(() => {
|
|
207
|
+
this.connect();
|
|
208
|
+
}, this.reconnectMs);
|
|
209
|
+
this.reconnectMs = Math.min(this.reconnectMs * 2, MAX_RECONNECT_MS);
|
|
210
|
+
}
|
|
211
|
+
clearTimers() {
|
|
212
|
+
if (this.heartbeatTimer) {
|
|
213
|
+
clearInterval(this.heartbeatTimer);
|
|
214
|
+
this.heartbeatTimer = null;
|
|
215
|
+
}
|
|
216
|
+
if (this.reconnectTimer) {
|
|
217
|
+
clearTimeout(this.reconnectTimer);
|
|
218
|
+
this.reconnectTimer = null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=relay-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay-client.js","sourceRoot":"","sources":["../src/relay-client.ts"],"names":[],"mappings":"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"}
|
package/dist/relay.js
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2A Relay Hub
|
|
3
|
+
*
|
|
4
|
+
* Lightweight WebSocket broker for NAT traversal. Agents connect outbound
|
|
5
|
+
* to the relay, which routes messages between them. Supports API key auth,
|
|
6
|
+
* rate limiting, agent presence registry, and offline message queuing.
|
|
7
|
+
*
|
|
8
|
+
* Can run standalone via bin/relay.ts or embedded in full mode.
|
|
9
|
+
*/
|
|
10
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
11
|
+
import { randomUUID } from "crypto";
|
|
12
|
+
const DEFAULT_CONFIG = {
|
|
13
|
+
port: 6060,
|
|
14
|
+
apiKeys: [],
|
|
15
|
+
registeredAgents: {},
|
|
16
|
+
maxQueuePerAgent: 200,
|
|
17
|
+
rateLimitPerMinute: 60,
|
|
18
|
+
heartbeatIntervalMs: 30_000,
|
|
19
|
+
heartbeatTimeoutMs: 90_000,
|
|
20
|
+
};
|
|
21
|
+
export class RelayHub {
|
|
22
|
+
wss = null;
|
|
23
|
+
agents = new Map();
|
|
24
|
+
offlineQueue = new Map();
|
|
25
|
+
rateCounts = new Map();
|
|
26
|
+
config;
|
|
27
|
+
heartbeatTimer = null;
|
|
28
|
+
logger;
|
|
29
|
+
constructor(config, logger) {
|
|
30
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
31
|
+
this.logger = logger || { info: console.log, error: console.error };
|
|
32
|
+
}
|
|
33
|
+
start() {
|
|
34
|
+
this.wss = new WebSocketServer({ port: this.config.port });
|
|
35
|
+
this.logger.info(`[perkos-a2a] Relay hub listening on port ${this.config.port}`);
|
|
36
|
+
this.wss.on("connection", (ws, req) => {
|
|
37
|
+
const remoteAddr = req.socket.remoteAddress || "unknown";
|
|
38
|
+
this.logger.info(`[perkos-a2a] Relay connection from ${remoteAddr}`);
|
|
39
|
+
this.handleConnection(ws);
|
|
40
|
+
});
|
|
41
|
+
this.heartbeatTimer = setInterval(() => this.checkHeartbeats(), this.config.heartbeatIntervalMs);
|
|
42
|
+
}
|
|
43
|
+
stop() {
|
|
44
|
+
if (this.heartbeatTimer) {
|
|
45
|
+
clearInterval(this.heartbeatTimer);
|
|
46
|
+
this.heartbeatTimer = null;
|
|
47
|
+
}
|
|
48
|
+
if (this.wss) {
|
|
49
|
+
for (const agent of this.agents.values()) {
|
|
50
|
+
agent.ws.close(1001, "Relay shutting down");
|
|
51
|
+
}
|
|
52
|
+
this.wss.close();
|
|
53
|
+
this.wss = null;
|
|
54
|
+
this.logger.info("[perkos-a2a] Relay hub stopped");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
getRegistry() {
|
|
58
|
+
const entries = [];
|
|
59
|
+
for (const agent of this.agents.values()) {
|
|
60
|
+
entries.push({
|
|
61
|
+
name: agent.name,
|
|
62
|
+
connectedAt: agent.connectedAt,
|
|
63
|
+
lastHeartbeat: agent.lastHeartbeat,
|
|
64
|
+
card: agent.card,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return entries;
|
|
68
|
+
}
|
|
69
|
+
handleConnection(ws) {
|
|
70
|
+
let agentName = null;
|
|
71
|
+
ws.on("message", (data) => {
|
|
72
|
+
let msg;
|
|
73
|
+
try {
|
|
74
|
+
msg = JSON.parse(data.toString());
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
this.sendError(ws, "invalid_json", "Failed to parse message");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (msg.type === "register") {
|
|
81
|
+
agentName = this.handleRegister(ws, msg);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (!agentName) {
|
|
85
|
+
this.sendError(ws, "not_registered", "Must register before sending messages");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (!this.checkRateLimit(agentName)) {
|
|
89
|
+
this.sendError(ws, "rate_limited", "Rate limit exceeded");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
switch (msg.type) {
|
|
93
|
+
case "task":
|
|
94
|
+
case "task_response":
|
|
95
|
+
this.routeMessage(agentName, msg);
|
|
96
|
+
break;
|
|
97
|
+
case "discover":
|
|
98
|
+
this.handleDiscover(ws, msg);
|
|
99
|
+
break;
|
|
100
|
+
case "heartbeat":
|
|
101
|
+
this.handleHeartbeat(agentName, msg);
|
|
102
|
+
break;
|
|
103
|
+
default:
|
|
104
|
+
this.sendError(ws, "unknown_type", `Unknown message type: ${msg.type}`);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
ws.on("close", () => {
|
|
108
|
+
if (agentName) {
|
|
109
|
+
this.agents.delete(agentName);
|
|
110
|
+
this.logger.info(`[perkos-a2a] Agent disconnected: ${agentName}`);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
ws.on("error", (err) => {
|
|
114
|
+
this.logger.error(`[perkos-a2a] WebSocket error for ${agentName || "unknown"}: ${err.message}`);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
handleRegister(ws, msg) {
|
|
118
|
+
const name = msg.payload.agentName;
|
|
119
|
+
const apiKey = msg.payload.apiKey;
|
|
120
|
+
if (!name) {
|
|
121
|
+
this.sendError(ws, "missing_name", "agentName is required for registration");
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
const approvedKey = this.config.registeredAgents[name];
|
|
125
|
+
if (Object.keys(this.config.registeredAgents).length > 0) {
|
|
126
|
+
if (!approvedKey || approvedKey !== apiKey) {
|
|
127
|
+
this.sendError(ws, "not_approved", "Agent is not approved for this relay or API key does not match");
|
|
128
|
+
ws.close(4001, "Agent not approved");
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else if (this.config.apiKeys.length > 0 && !this.config.apiKeys.includes(apiKey)) {
|
|
133
|
+
this.sendError(ws, "auth_failed", "Invalid API key");
|
|
134
|
+
ws.close(4001, "Authentication failed");
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
// Disconnect existing connection for same agent name
|
|
138
|
+
const existing = this.agents.get(name);
|
|
139
|
+
if (existing) {
|
|
140
|
+
existing.ws.close(4002, "Replaced by new connection");
|
|
141
|
+
}
|
|
142
|
+
const now = new Date().toISOString();
|
|
143
|
+
this.agents.set(name, {
|
|
144
|
+
name,
|
|
145
|
+
ws,
|
|
146
|
+
apiKey: apiKey || "",
|
|
147
|
+
card: msg.payload.card,
|
|
148
|
+
connectedAt: now,
|
|
149
|
+
lastHeartbeat: now,
|
|
150
|
+
});
|
|
151
|
+
this.logger.info(`[perkos-a2a] Agent registered: ${name}`);
|
|
152
|
+
const ack = {
|
|
153
|
+
type: "register_ack",
|
|
154
|
+
id: msg.id,
|
|
155
|
+
from: "relay",
|
|
156
|
+
payload: { status: "ok", agentName: name },
|
|
157
|
+
timestamp: now,
|
|
158
|
+
};
|
|
159
|
+
ws.send(JSON.stringify(ack));
|
|
160
|
+
// Deliver queued messages
|
|
161
|
+
this.drainQueue(name, ws);
|
|
162
|
+
return name;
|
|
163
|
+
}
|
|
164
|
+
routeMessage(fromAgent, msg) {
|
|
165
|
+
const target = msg.to;
|
|
166
|
+
if (!target) {
|
|
167
|
+
this.sendError(this.agents.get(fromAgent).ws, "missing_target", "Message requires 'to' field");
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
msg.from = fromAgent;
|
|
171
|
+
msg.timestamp = new Date().toISOString();
|
|
172
|
+
if (Object.keys(this.config.registeredAgents).length > 0 && !(target in this.config.registeredAgents)) {
|
|
173
|
+
this.sendError(this.agents.get(fromAgent).ws, "target_not_approved", `Target agent ${target} is not approved on this relay`);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const targetAgent = this.agents.get(target);
|
|
177
|
+
if (targetAgent && targetAgent.ws.readyState === WebSocket.OPEN) {
|
|
178
|
+
targetAgent.ws.send(JSON.stringify(msg));
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
// Queue for offline delivery
|
|
182
|
+
if (!this.offlineQueue.has(target)) {
|
|
183
|
+
this.offlineQueue.set(target, []);
|
|
184
|
+
}
|
|
185
|
+
const queue = this.offlineQueue.get(target);
|
|
186
|
+
if (queue.length < this.config.maxQueuePerAgent) {
|
|
187
|
+
queue.push({ message: msg, queuedAt: new Date().toISOString() });
|
|
188
|
+
this.logger.info(`[perkos-a2a] Queued message for offline agent: ${target} (${queue.length} pending)`);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
this.logger.info(`[perkos-a2a] Queue full for agent: ${target}, dropping message`);
|
|
192
|
+
const sender = this.agents.get(fromAgent);
|
|
193
|
+
if (sender) {
|
|
194
|
+
this.sendError(sender.ws, "queue_full", `Target agent ${target} queue is full`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
drainQueue(agentName, ws) {
|
|
200
|
+
const queue = this.offlineQueue.get(agentName);
|
|
201
|
+
if (!queue || queue.length === 0)
|
|
202
|
+
return;
|
|
203
|
+
this.logger.info(`[perkos-a2a] Delivering ${queue.length} queued messages to ${agentName}`);
|
|
204
|
+
for (const item of queue) {
|
|
205
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
206
|
+
ws.send(JSON.stringify(item.message));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
this.offlineQueue.delete(agentName);
|
|
210
|
+
}
|
|
211
|
+
handleDiscover(ws, msg) {
|
|
212
|
+
const agents = this.getRegistry();
|
|
213
|
+
const response = {
|
|
214
|
+
type: "discover_response",
|
|
215
|
+
id: msg.id,
|
|
216
|
+
from: "relay",
|
|
217
|
+
payload: { agents },
|
|
218
|
+
timestamp: new Date().toISOString(),
|
|
219
|
+
};
|
|
220
|
+
ws.send(JSON.stringify(response));
|
|
221
|
+
}
|
|
222
|
+
handleHeartbeat(agentName, msg) {
|
|
223
|
+
const agent = this.agents.get(agentName);
|
|
224
|
+
if (agent) {
|
|
225
|
+
agent.lastHeartbeat = new Date().toISOString();
|
|
226
|
+
}
|
|
227
|
+
const ack = {
|
|
228
|
+
type: "heartbeat_ack",
|
|
229
|
+
id: msg.id,
|
|
230
|
+
from: "relay",
|
|
231
|
+
payload: {},
|
|
232
|
+
timestamp: new Date().toISOString(),
|
|
233
|
+
};
|
|
234
|
+
const ws = agent?.ws;
|
|
235
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
236
|
+
ws.send(JSON.stringify(ack));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
checkHeartbeats() {
|
|
240
|
+
const now = Date.now();
|
|
241
|
+
for (const [name, agent] of this.agents) {
|
|
242
|
+
const lastBeat = new Date(agent.lastHeartbeat).getTime();
|
|
243
|
+
if (now - lastBeat > this.config.heartbeatTimeoutMs) {
|
|
244
|
+
this.logger.info(`[perkos-a2a] Agent ${name} timed out (no heartbeat)`);
|
|
245
|
+
agent.ws.close(4003, "Heartbeat timeout");
|
|
246
|
+
this.agents.delete(name);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
checkRateLimit(agentName) {
|
|
251
|
+
const now = Date.now();
|
|
252
|
+
const window = 60_000;
|
|
253
|
+
let entry = this.rateCounts.get(agentName);
|
|
254
|
+
if (!entry || now - entry.windowStart > window) {
|
|
255
|
+
entry = { count: 0, windowStart: now };
|
|
256
|
+
this.rateCounts.set(agentName, entry);
|
|
257
|
+
}
|
|
258
|
+
entry.count++;
|
|
259
|
+
return entry.count <= this.config.rateLimitPerMinute;
|
|
260
|
+
}
|
|
261
|
+
sendError(ws, code, message) {
|
|
262
|
+
const errorMsg = {
|
|
263
|
+
type: "error",
|
|
264
|
+
id: randomUUID(),
|
|
265
|
+
from: "relay",
|
|
266
|
+
payload: { code, message },
|
|
267
|
+
timestamp: new Date().toISOString(),
|
|
268
|
+
};
|
|
269
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
270
|
+
ws.send(JSON.stringify(errorMsg));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=relay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay.js","sourceRoot":"","sources":["../src/relay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
function stripTaskMarker(text, marker) {
|
|
3
|
+
const trimmed = text.trim();
|
|
4
|
+
if (!trimmed.includes(marker))
|
|
5
|
+
return trimmed;
|
|
6
|
+
return trimmed.slice(trimmed.indexOf(marker) + marker.length).trim();
|
|
7
|
+
}
|
|
8
|
+
function requestedFinalMarker(text) {
|
|
9
|
+
const patterns = [
|
|
10
|
+
/termina(?:r)?\s+exactamente\s+con\s+([A-Z0-9_\-]+)/i,
|
|
11
|
+
/end\s+(?:exactly\s+)?with\s+([A-Z0-9_\-]+)/i,
|
|
12
|
+
];
|
|
13
|
+
for (const pattern of patterns) {
|
|
14
|
+
const match = text.match(pattern);
|
|
15
|
+
if (match?.[1])
|
|
16
|
+
return match[1];
|
|
17
|
+
}
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
function withRequestedMarker(reply, text) {
|
|
21
|
+
const marker = requestedFinalMarker(text);
|
|
22
|
+
if (!marker || reply.trim().endsWith(marker))
|
|
23
|
+
return reply;
|
|
24
|
+
return `${reply.trim()} ${marker}`;
|
|
25
|
+
}
|
|
26
|
+
function runtimeOption(config, key) {
|
|
27
|
+
const value = config.runtime?.[key];
|
|
28
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
29
|
+
}
|
|
30
|
+
function agentIdFor(config) {
|
|
31
|
+
return runtimeOption(config, "agentId") || config.agentName || "perkos-a2a-agent";
|
|
32
|
+
}
|
|
33
|
+
function llmSystemPrompt(config) {
|
|
34
|
+
const agentName = config.agentName || config.runtime?.kind || "PerkOS A2A agent";
|
|
35
|
+
return [
|
|
36
|
+
`You are ${agentName}, a PerkOS A2A agent.`,
|
|
37
|
+
"Reply concisely and directly.",
|
|
38
|
+
"If asked to confirm receipt, confirm that you received the task via PerkOS A2A and are using PerkOS LLM.",
|
|
39
|
+
"Do not mention implementation details unless asked.",
|
|
40
|
+
].join(" ");
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Generate a final task reply through PerkOS LLM when the local runtime accepts
|
|
44
|
+
* a task but does not expose a synchronous final answer API.
|
|
45
|
+
*
|
|
46
|
+
* This keeps A2A task state machine semantics intact: delivery/wake still
|
|
47
|
+
* happens, while the A2A caller receives a terminal task with a useful artifact
|
|
48
|
+
* instead of being left in `working` indefinitely.
|
|
49
|
+
*/
|
|
50
|
+
export async function generateRuntimeReply(config, text, marker) {
|
|
51
|
+
const runtimeKind = config.runtime?.kind || "openclaw";
|
|
52
|
+
const apiKey = runtimeOption(config, "llmApiKey");
|
|
53
|
+
if (!apiKey) {
|
|
54
|
+
const agentName = config.agentName || config.runtime?.kind || "PerkOS A2A agent";
|
|
55
|
+
const lower = text.toLowerCase();
|
|
56
|
+
if (lower.includes("confirma") || lower.includes("confirm") || lower.includes("ping")) {
|
|
57
|
+
return withRequestedMarker(`${agentName} recibió este mensaje vía PerkOS A2A.`, text);
|
|
58
|
+
}
|
|
59
|
+
return withRequestedMarker([
|
|
60
|
+
`${agentName} recibió la tarea vía PerkOS A2A.`,
|
|
61
|
+
"El runtime/LLM local no está configurado para generar una respuesta completa en este momento, pero el transporte A2A y el estado terminal funcionan.",
|
|
62
|
+
].join(" "), text);
|
|
63
|
+
}
|
|
64
|
+
const headers = {
|
|
65
|
+
"content-type": "application/json",
|
|
66
|
+
authorization: `Bearer ${apiKey}`,
|
|
67
|
+
"x-agent-id": agentIdFor(config),
|
|
68
|
+
};
|
|
69
|
+
const system = llmSystemPrompt(config);
|
|
70
|
+
const user = [
|
|
71
|
+
text,
|
|
72
|
+
"",
|
|
73
|
+
"Return only the useful final answer.",
|
|
74
|
+
`Do not include ${marker}; the A2A bridge will attach the marker/task result.`,
|
|
75
|
+
].join("\n");
|
|
76
|
+
if (runtimeKind === "hermes") {
|
|
77
|
+
const base = (runtimeOption(config, "openAiBaseUrl") || "https://api.llm.perkos.xyz/v1").replace(/\/+$/, "");
|
|
78
|
+
const model = runtimeOption(config, "model") || "kimi-k2.6:cloud";
|
|
79
|
+
const response = await fetch(`${base}/chat/completions`, {
|
|
80
|
+
method: "POST",
|
|
81
|
+
headers,
|
|
82
|
+
body: JSON.stringify({
|
|
83
|
+
model,
|
|
84
|
+
messages: [
|
|
85
|
+
{ role: "system", content: system },
|
|
86
|
+
{ role: "user", content: user },
|
|
87
|
+
],
|
|
88
|
+
temperature: 0.2,
|
|
89
|
+
}),
|
|
90
|
+
});
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
const body = await response.text().catch(() => "");
|
|
93
|
+
throw new Error(`PerkOS LLM OpenAI-compatible call failed (${response.status}): ${body || response.statusText}`);
|
|
94
|
+
}
|
|
95
|
+
const data = await response.json();
|
|
96
|
+
return stripTaskMarker(data.choices?.[0]?.message?.content || "Task completed.", marker);
|
|
97
|
+
}
|
|
98
|
+
const base = (runtimeOption(config, "ollamaBaseUrl") || "https://api.llm.perkos.xyz").replace(/\/+$/, "");
|
|
99
|
+
const model = runtimeOption(config, "model") || "kimi-k2.6:cloud";
|
|
100
|
+
const response = await fetch(`${base}/api/chat`, {
|
|
101
|
+
method: "POST",
|
|
102
|
+
headers,
|
|
103
|
+
body: JSON.stringify({
|
|
104
|
+
model,
|
|
105
|
+
stream: false,
|
|
106
|
+
messages: [
|
|
107
|
+
{ role: "system", content: system },
|
|
108
|
+
{ role: "user", content: user },
|
|
109
|
+
],
|
|
110
|
+
}),
|
|
111
|
+
});
|
|
112
|
+
if (!response.ok) {
|
|
113
|
+
const body = await response.text().catch(() => "");
|
|
114
|
+
throw new Error(`PerkOS LLM Ollama-compatible call failed (${response.status}): ${body || response.statusText}`);
|
|
115
|
+
}
|
|
116
|
+
const data = await response.json();
|
|
117
|
+
return stripTaskMarker(data.message?.content || data.response || "Task completed.", marker);
|
|
118
|
+
}
|
|
119
|
+
export function completeTaskWithReply(task, finalText) {
|
|
120
|
+
task.artifacts.push({
|
|
121
|
+
kind: "artifact",
|
|
122
|
+
artifactId: randomUUID(),
|
|
123
|
+
parts: [{ kind: "text", text: finalText }],
|
|
124
|
+
});
|
|
125
|
+
task.status = {
|
|
126
|
+
state: "completed",
|
|
127
|
+
timestamp: new Date().toISOString(),
|
|
128
|
+
message: {
|
|
129
|
+
role: "agent",
|
|
130
|
+
parts: [{ kind: "text", text: finalText }],
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
export function isTerminalTaskState(state) {
|
|
135
|
+
return state === "completed" || state === "failed" || state === "canceled";
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=runtime-reply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-reply.js","sourceRoot":"","sources":["../src/runtime-reply.ts"],"names":[],"mappings":"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"}
|