agent-relay-server 0.104.3 → 0.104.5

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/docs/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Agent Relay API",
5
- "version": "0.104.0",
5
+ "version": "0.104.5",
6
6
  "description": "Real-time message bus for inter-agent communication. Agent-first: this spec is designed for machine consumption — agents can self-discover the full API surface via GET /api/spec.",
7
7
  "license": {
8
8
  "name": "MIT",
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
+ import { RelayHttpClient } from "agent-relay-sdk";
4
+
3
5
  type GitHubIssuePayload = {
4
6
  action?: string;
5
7
  issue?: {
@@ -20,6 +22,7 @@ if (!token) throw new Error("AGENT_RELAY_INTEGRATION_TOKEN is required");
20
22
 
21
23
  const target = process.env.AGENT_RELAY_TARGET || "cap:triage";
22
24
  const channel = process.env.AGENT_RELAY_CHANNEL || "support";
25
+ const relay = new RelayHttpClient({ baseUrl: relayUrl, token });
23
26
  const payload = JSON.parse(await Bun.stdin.text()) as GitHubIssuePayload;
24
27
  const issue = payload.issue;
25
28
  const repo = payload.repository?.full_name || "unknown/repo";
@@ -30,25 +33,15 @@ if (!issue || !["opened", "reopened", "labeled", "edited"].includes(action)) {
30
33
  }
31
34
 
32
35
  const labels = issue.labels?.map((label) => label.name) ?? [];
33
- const response = await fetch(new URL("/api/integrations/events", relayUrl), {
34
- method: "POST",
35
- headers: {
36
- Authorization: `Bearer ${token}`,
37
- "Content-Type": "application/json",
38
- },
39
- body: JSON.stringify({
40
- type: "github-issue",
41
- severity: labels.includes("P0") || labels.includes("critical") ? "critical" : labels.includes("P1") ? "warning" : "info",
42
- dedupeKey: `github:${repo}:issue:${issue.number}`,
43
- title: `${repo}#${issue.number}: ${issue.title}`,
44
- body: issue.body || "(no issue body)",
45
- target,
46
- channel,
47
- externalUrl: issue.html_url,
48
- metadata: { repo, issueNumber: issue.number, action, labels },
49
- }),
36
+ await relay.raiseAlert({
37
+ source: `github:${repo}`,
38
+ type: "issue",
39
+ severity: labels.includes("P0") || labels.includes("critical") ? "critical" : labels.includes("P1") ? "warning" : "info",
40
+ dedupeKey: `issue:${issue.number}`,
41
+ title: `${repo}#${issue.number}: ${issue.title}`,
42
+ body: issue.body || "(no issue body)",
43
+ payload: { repo, issueNumber: issue.number, action, labels },
44
+ target,
45
+ channel,
46
+ externalUrl: issue.html_url,
50
47
  });
51
-
52
- if (!response.ok) {
53
- throw new Error(`failed to send GitHub issue ${repo}#${issue.number}: ${response.status} ${await response.text()}`);
54
- }
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
+ import { RelayHttpClient } from "agent-relay-sdk";
4
+
3
5
  type Alert = {
4
6
  status?: string;
5
7
  labels?: Record<string, string>;
@@ -19,6 +21,7 @@ if (!token) throw new Error("AGENT_RELAY_INTEGRATION_TOKEN is required");
19
21
 
20
22
  const target = process.env.AGENT_RELAY_TARGET || "cap:ops";
21
23
  const channel = process.env.AGENT_RELAY_CHANNEL || "alerts";
24
+ const relay = new RelayHttpClient({ baseUrl: relayUrl, token });
22
25
  const payload = JSON.parse(await Bun.stdin.text()) as AlertmanagerPayload;
23
26
  const alerts = payload.alerts ?? [];
24
27
 
@@ -29,29 +32,19 @@ for (const alert of alerts) {
29
32
  const service = labels.service || labels.job || labels.instance || "unknown";
30
33
  const status = alert.status === "resolved" || payload.status === "resolved" ? "resolved" : undefined;
31
34
 
32
- const response = await fetch(new URL("/api/integrations/events", relayUrl), {
33
- method: "POST",
34
- headers: {
35
- Authorization: `Bearer ${token}`,
36
- "Content-Type": "application/json",
37
- },
38
- body: JSON.stringify({
39
- type: "alertmanager",
40
- severity: alertSeverity(labels.severity),
41
- status,
42
- dedupeKey: `alertmanager:${alert.fingerprint || name}:${service}`,
43
- title: `${name} (${service})`,
44
- body: annotations.description || annotations.summary || JSON.stringify(labels),
45
- target,
46
- channel,
47
- externalUrl: alert.generatorURL,
48
- metadata: { labels, annotations, alertStatus: alert.status },
49
- }),
35
+ await relay.raiseAlert({
36
+ source: `alertmanager:${service}`,
37
+ type: name,
38
+ severity: alertSeverity(labels.severity),
39
+ status,
40
+ dedupeKey: alert.fingerprint ? `alertmanager:${alert.fingerprint}` : undefined,
41
+ title: `${name} (${service})`,
42
+ body: annotations.description || annotations.summary || JSON.stringify(labels),
43
+ payload: { labels, annotations, alertStatus: alert.status },
44
+ target,
45
+ channel,
46
+ externalUrl: alert.generatorURL,
50
47
  });
51
-
52
- if (!response.ok) {
53
- throw new Error(`failed to send alert ${name}: ${response.status} ${await response.text()}`);
54
- }
55
48
  }
56
49
 
57
50
  function alertSeverity(value?: string): "info" | "warning" | "critical" {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-server",
3
- "version": "0.104.3",
3
+ "version": "0.104.5",
4
4
  "description": "Lightweight HTTP message relay for inter-agent communication across machines",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
@@ -37,7 +37,7 @@
37
37
  ],
38
38
  "dependencies": {
39
39
  "agent-relay-providers": "0.104.0",
40
- "agent-relay-sdk": "0.2.88",
40
+ "agent-relay-sdk": "0.2.90",
41
41
  "ajv": "^8.20.0"
42
42
  },
43
43
  "scripts": {
@@ -101,6 +101,7 @@
101
101
  "devDependencies": {
102
102
  "@types/bun": "latest",
103
103
  "@types/madge": "^5.0.3",
104
+ "callmux": "0.23.0",
104
105
  "madge": "^8.0.0",
105
106
  "typescript": "^5"
106
107
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-relay-runner",
3
3
  "description": "Thin Agent Relay runner bridge for Claude Code",
4
- "version": "0.104.0",
4
+ "version": "0.104.5",
5
5
  "agentRelayContracts": {
6
6
  "providerPluginProtocol": 1
7
7
  }
@@ -90,10 +90,11 @@ export function ingestIntegrationEvent(input: IntegrationEventInput, integration
90
90
  const targetStatus = input.status === "resolved" ? "done" : input.status;
91
91
  const attachmentRefs = input.attachments ?? [];
92
92
  validateAttachmentRefs(attachmentRefs);
93
- updateIntegrationObserved(source, now);
93
+ updateIntegrationObserved(integrationName, now);
94
94
 
95
95
  return getDb().transaction(() => {
96
96
  const existing = input.dedupeKey ? findOpenTaskByDedupe(source, input.dedupeKey) : null;
97
+ const metadata = integrationTaskMetadata(input.metadata, integrationName, existing?.metadata);
97
98
  let taskId: number;
98
99
  let created = false;
99
100
 
@@ -112,7 +113,7 @@ export function ingestIntegrationEvent(input: IntegrationEventInput, integration
112
113
  input.target,
113
114
  input.channel ?? null,
114
115
  input.externalUrl ?? null,
115
- JSON.stringify(input.metadata ?? existing.metadata),
116
+ JSON.stringify(metadata),
116
117
  now,
117
118
  now,
118
119
  targetStatus ?? null,
@@ -136,7 +137,7 @@ export function ingestIntegrationEvent(input: IntegrationEventInput, integration
136
137
  input.channel ?? null,
137
138
  input.dedupeKey ?? null,
138
139
  input.externalUrl ?? null,
139
- JSON.stringify(input.metadata ?? {}),
140
+ JSON.stringify(metadata),
140
141
  now,
141
142
  now,
142
143
  now,
@@ -153,7 +154,7 @@ export function ingestIntegrationEvent(input: IntegrationEventInput, integration
153
154
  severity,
154
155
  title: input.title,
155
156
  body: input.body,
156
- metadata: input.metadata ?? {},
157
+ metadata,
157
158
  }, now);
158
159
 
159
160
  let message: Message | undefined;
@@ -185,11 +186,23 @@ export function ingestIntegrationEvent(input: IntegrationEventInput, integration
185
186
  })();
186
187
  }
187
188
 
189
+ function integrationTaskMetadata(
190
+ metadata: Record<string, unknown> | undefined,
191
+ integrationName: string,
192
+ fallback: Record<string, unknown> | undefined,
193
+ ): Record<string, unknown> {
194
+ return {
195
+ ...(fallback ?? {}),
196
+ ...(metadata ?? {}),
197
+ relayIntegrationName: integrationName,
198
+ };
199
+ }
200
+
188
201
 
189
202
  export function listIntegrationTaskStats(): IntegrationTaskStats[] {
190
203
  const rows = getDb().query(`
191
204
  SELECT
192
- source,
205
+ coalesce(json_extract(metadata, '$.relayIntegrationName'), source) AS source,
193
206
  COUNT(*) AS tasks,
194
207
  SUM(CASE WHEN status NOT IN ('done', 'failed', 'canceled') THEN 1 ELSE 0 END) AS open_tasks,
195
208
  SUM(CASE WHEN status IN ('open', 'blocked') AND claimed_by IS NULL THEN 1 ELSE 0 END) AS waiting_tasks,
@@ -197,7 +210,7 @@ export function listIntegrationTaskStats(): IntegrationTaskStats[] {
197
210
  MAX(last_seen_at) AS last_seen_at,
198
211
  MAX(updated_at) AS last_updated_at
199
212
  FROM tasks
200
- GROUP BY source
213
+ GROUP BY coalesce(json_extract(metadata, '$.relayIntegrationName'), source)
201
214
  ORDER BY last_seen_at DESC, source ASC
202
215
  `).all() as any[];
203
216
 
@@ -337,4 +350,3 @@ export function isIntegrationRegistryEnabled(name: string): boolean {
337
350
  const registry = getIntegrationRegistry(name);
338
351
  return registry?.enabled !== false;
339
352
  }
340
-
@@ -606,13 +606,13 @@ export const postIntegrationEvent: Handler = async (req) => {
606
606
  const parsed = await parseBody<unknown>(req);
607
607
  if (!parsed.ok) return error(parsed.error, parsed.status);
608
608
  try {
609
- const normalized = { ...normalizeIntegrationEvent(parsed.body), source: integrationName };
609
+ const normalized = normalizeIntegrationEvent(parsed.body);
610
610
  const requestedChannelId = channelIdFromIntegrationTarget(normalized.target);
611
611
  const integrationScope = auth && !hasIntegrationScope(auth, "events:create") ? "task:write" : "integration:write";
612
612
  if (!isRequestAuthorizedFor(req, { scope: integrationScope, resource: { integrationName, target: normalized.target, channel: normalized.channel ?? requestedChannelId } })) {
613
613
  return error("integration token cannot target this task", 403);
614
614
  }
615
- const input = resolveIntegrationEventTarget(normalized);
615
+ const input = resolveIntegrationEventTarget({ ...normalized, source: normalized.source ?? integrationName });
616
616
  const result = ingestIntegrationEvent(input, integrationName);
617
617
  if (result.message) emitNewMessage(result.message);
618
618
  emitTaskChanged(result.task, result.created ? "task.created" : "task.updated");