agent-relay-server 0.61.0 → 0.61.1

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.61.0",
5
+ "version": "0.61.1",
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",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-server",
3
- "version": "0.61.0",
3
+ "version": "0.61.1",
4
4
  "description": "Lightweight HTTP message relay for inter-agent communication across machines",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
@@ -1,6 +1,7 @@
1
1
  // Auto-split from routes.ts (#299). Domain: orchestrator.
2
2
  import { APPROVAL_MODES, SPAWN_PROVIDERS, VALID_WORKSPACE_MODES, isRecord } from "agent-relay-sdk";
3
3
  import { CONTRACT_VERSIONS, parseRuntimeCapabilities, parseRuntimeContracts, parseRuntimePackage, type RuntimeCapabilities, type RuntimeContracts, type RuntimePackageMetadata } from "../contracts";
4
+ import { notifyAgentOffline } from "../agent-lifecycle-events";
4
5
  import { VERSION } from "../config";
5
6
  import { ValidationError, deleteOrchestrator, getOrchestrator, listOrchestrators, orchestratorHeartbeat, recordAgentExitDiagnostics, setOrchestratorUpgradeState, updateManagedAgents, upsertOrchestrator } from "../db";
6
7
  import { auditEvent, authAuditMetadata, authorizeRoute, cleanJsonArray, cleanSafeNumber, dashboardAttribution, emitCommand, error, isRootCredentialRequest, json, parseBody, spawnRequestId, type Handler } from "./_shared";
@@ -201,7 +202,15 @@ export const patchOrchestratorAgents: Handler = async (req, params) => {
201
202
  const updated = updateManagedAgents(params.id!, cleaned);
202
203
  for (const exited of exitedAgents) {
203
204
  const agent = recordAgentExitDiagnostics(exited.agentId, exited);
204
- if (agent) emitAgentStatus(agent.id);
205
+ if (agent) {
206
+ emitAgentStatus(agent.id);
207
+ // #466 — fast exit path must wake the spawn-parent. recordAgentExitDiagnostics already
208
+ // flipped the agent offline (above), so the stale-agent-reaper will structurally skip it
209
+ // (its WHERE excludes status='offline'), guaranteeing a single notifyAgentOffline emission.
210
+ // notifyAgentOffline no-ops for non-spawned agents. Only fire when we actually recorded the
211
+ // exit (agent !== null) so an already-offline row can't double-notify.
212
+ notifyAgentOffline(exited.agentId, exitReason(exited));
213
+ }
205
214
  auditEvent({
206
215
  clientId: [
207
216
  "managed-session-exit",
@@ -248,6 +257,17 @@ export const patchOrchestratorAgents: Handler = async (req, params) => {
248
257
  }
249
258
  };
250
259
 
260
+ // #466 — derive a human-meaningful offline cause for the spawn-parent notification from the exit
261
+ // diagnostics, in preference to the reaper's generic "heartbeat lost". Prefer the captured
262
+ // lastError, then the systemd result (e.g. "success", "signal"), else a plain fallback.
263
+ function exitReason(exited: ManagedSessionExitDiagnostics): string {
264
+ const lastError = exited.lastError?.trim();
265
+ if (lastError) return lastError;
266
+ const result = exited.systemd?.result?.trim();
267
+ if (result) return `exited (${result})`;
268
+ return "exited";
269
+ }
270
+
251
271
  function cleanManagedSessionExitDiagnostics(value: unknown, index: number): ManagedSessionExitDiagnostics {
252
272
  if (!isRecord(value)) throw new ValidationError(`exitedAgents[${index}] must be an object`);
253
273
  const provider = optionalEnum(value.provider, `exitedAgents[${index}].provider`, SPAWN_PROVIDERS);