agent-relay-server 0.7.3 → 0.8.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/package.json +1 -1
- package/src/cli.ts +42 -0
- package/src/routes.ts +24 -1
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -38,6 +38,7 @@ Usage:
|
|
|
38
38
|
agent-relay message <target> <body> [options]
|
|
39
39
|
agent-relay /pair <target|accept|reject|send|status> [...]
|
|
40
40
|
agent-relay /message <target> <body>
|
|
41
|
+
agent-relay /reply <messageId> <body>
|
|
41
42
|
agent-relay /send-claimable <target> <body>
|
|
42
43
|
agent-relay /disconnect [PAIR_ID]
|
|
43
44
|
agent-relay /status
|
|
@@ -52,6 +53,7 @@ Pair examples:
|
|
|
52
53
|
agent-relay pair accept PAIR_ID --agent AGENT_ID
|
|
53
54
|
agent-relay pair send PAIR_ID --from AGENT_ID --body "What do you see?"
|
|
54
55
|
agent-relay /message codex "Can you look at that failing action?"
|
|
56
|
+
agent-relay /reply 206 "Sounds good, I'll take a look"
|
|
55
57
|
agent-relay /send-claimable tag:backend "Please claim and fix the failing API test"
|
|
56
58
|
agent-relay /disconnect
|
|
57
59
|
agent-relay /status
|
|
@@ -123,6 +125,10 @@ export async function handleCli(args: string[]): Promise<"start" | "handled"> {
|
|
|
123
125
|
await handleMessageCommand(args.slice(1), { claimable: command === "/send-claimable" });
|
|
124
126
|
return "handled";
|
|
125
127
|
}
|
|
128
|
+
if (command === "reply" || command === "/reply") {
|
|
129
|
+
await handleReplyCommand(args.slice(1));
|
|
130
|
+
return "handled";
|
|
131
|
+
}
|
|
126
132
|
if (command === "/status" || command === "status") {
|
|
127
133
|
await handleStatusCommand(args.slice(1));
|
|
128
134
|
return "handled";
|
|
@@ -499,6 +505,42 @@ async function handleMessageCommand(args: string[], defaults: { claimable?: bool
|
|
|
499
505
|
else console.log(`${claimable ? "Claimable message" : "Message"} sent: ${(message as any).id} -> ${target}`);
|
|
500
506
|
}
|
|
501
507
|
|
|
508
|
+
async function handleReplyCommand(args: string[]): Promise<void> {
|
|
509
|
+
const msgIdRaw = args[0];
|
|
510
|
+
if (!msgIdRaw || msgIdRaw.startsWith("--")) {
|
|
511
|
+
throw new Error("Usage: agent-relay /reply <messageId> <body> [--from AGENT_ID] [--subject TEXT] [--json]");
|
|
512
|
+
}
|
|
513
|
+
const replyTo = Number.parseInt(msgIdRaw, 10);
|
|
514
|
+
if (!Number.isFinite(replyTo) || replyTo <= 0) throw new Error("messageId must be a positive integer");
|
|
515
|
+
|
|
516
|
+
let from = await detectAgentId();
|
|
517
|
+
let subject: string | undefined;
|
|
518
|
+
let json = false;
|
|
519
|
+
const bodyParts: string[] = [];
|
|
520
|
+
|
|
521
|
+
for (let i = 1; i < args.length; i++) {
|
|
522
|
+
const arg = args[i];
|
|
523
|
+
if (arg === "--from" && i + 1 < args.length) from = args[++i];
|
|
524
|
+
else if (arg === "--subject" && i + 1 < args.length) subject = args[++i];
|
|
525
|
+
else if (arg === "--json") json = true;
|
|
526
|
+
else bodyParts.push(arg!);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const body = bodyParts.join(" ").trim();
|
|
530
|
+
if (!from) throw new Error("Could not detect current Agent Relay ID. Pass --from AGENT_ID or set AGENT_RELAY_ID.");
|
|
531
|
+
if (!body) throw new Error("Reply body required.");
|
|
532
|
+
|
|
533
|
+
const message = await apiRequest("POST", "/api/messages", {
|
|
534
|
+
from,
|
|
535
|
+
body,
|
|
536
|
+
subject,
|
|
537
|
+
replyTo,
|
|
538
|
+
});
|
|
539
|
+
const msg = message as any;
|
|
540
|
+
if (json) console.log(JSON.stringify(msg, null, 2));
|
|
541
|
+
else console.log(`Reply sent: ${msg.id} -> ${msg.to} (reply to #${replyTo})`);
|
|
542
|
+
}
|
|
543
|
+
|
|
502
544
|
async function handleStatusCommand(args: string[]): Promise<void> {
|
|
503
545
|
let agentId = await detectAgentId();
|
|
504
546
|
let json = false;
|
package/src/routes.ts
CHANGED
|
@@ -335,7 +335,7 @@ function normalizeMessageInput(body: unknown): SendMessageInput {
|
|
|
335
335
|
|
|
336
336
|
const input: SendMessageInput = {
|
|
337
337
|
from: cleanString(body.from, "from", { required: true, max: 200 })!,
|
|
338
|
-
to: cleanString(body.to, "to", {
|
|
338
|
+
to: cleanString(body.to, "to", { max: 200 }) ?? "",
|
|
339
339
|
body: cleanString(body.body, "body", { required: true, max: MAX_BODY_BYTES })!,
|
|
340
340
|
kind: kind as SendMessageInput["kind"] | undefined,
|
|
341
341
|
replyTo: cleanPositiveId(body.replyTo, "replyTo"),
|
|
@@ -355,6 +355,27 @@ function normalizeMessageInput(body: unknown): SendMessageInput {
|
|
|
355
355
|
return input;
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
+
function applyReplyRouting(input: SendMessageInput): void {
|
|
359
|
+
if (input.to || !input.replyTo) return;
|
|
360
|
+
const parent = getMessage(input.replyTo);
|
|
361
|
+
if (!parent) return;
|
|
362
|
+
input.to = parent.from;
|
|
363
|
+
if (!input.channel && parent.channel) input.channel = parent.channel;
|
|
364
|
+
const parentPayload = parent.payload ?? {};
|
|
365
|
+
if (parentPayload.schema === "agent-relay.channel.v1" || parentPayload.conversation) {
|
|
366
|
+
const replyContext: Record<string, unknown> = {};
|
|
367
|
+
if (parent.channel) replyContext.channelId = parent.channel;
|
|
368
|
+
if (parentPayload.conversation && typeof parentPayload.conversation === "object") {
|
|
369
|
+
replyContext.conversationId = (parentPayload.conversation as Record<string, unknown>).id;
|
|
370
|
+
}
|
|
371
|
+
if (parentPayload.event && typeof parentPayload.event === "object") {
|
|
372
|
+
replyContext.parentEventId = (parentPayload.event as Record<string, unknown>).id;
|
|
373
|
+
}
|
|
374
|
+
if (parentPayload.source) replyContext.source = parentPayload.source;
|
|
375
|
+
input.payload = { ...input.payload, replyContext };
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
358
379
|
function normalizeChannelBindingInput(body: unknown): {
|
|
359
380
|
channelId: string;
|
|
360
381
|
conversationId?: string;
|
|
@@ -1326,6 +1347,8 @@ const postMessage: Handler = async (req) => {
|
|
|
1326
1347
|
if (!input.idempotencyKey) {
|
|
1327
1348
|
input.idempotencyKey = cleanString(req.headers.get("Idempotency-Key") ?? undefined, "idempotencyKey", { max: 240 });
|
|
1328
1349
|
}
|
|
1350
|
+
applyReplyRouting(input);
|
|
1351
|
+
if (!input.to) return error("to is required (or provide replyTo to auto-route)");
|
|
1329
1352
|
const result = sendMessageWithResult(input);
|
|
1330
1353
|
if (result.created) {
|
|
1331
1354
|
emitNewMessage(result.message);
|