agent-relay 2.0.13 → 2.0.14

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.
Files changed (143) hide show
  1. package/bin/relay-pty-darwin-arm64 +0 -0
  2. package/bin/relay-pty-darwin-x64 +0 -0
  3. package/bin/relay-pty-linux-x64 +0 -0
  4. package/deploy/workspace/codex.config.toml +5 -0
  5. package/deploy/workspace/entrypoint.sh +10 -2
  6. package/dist/dashboard/out/404.html +1 -1
  7. package/dist/dashboard/out/app/onboarding.html +1 -1
  8. package/dist/dashboard/out/app/onboarding.txt +1 -1
  9. package/dist/dashboard/out/app.html +1 -1
  10. package/dist/dashboard/out/app.txt +1 -1
  11. package/dist/dashboard/out/cloud/link.html +1 -1
  12. package/dist/dashboard/out/cloud/link.txt +1 -1
  13. package/dist/dashboard/out/connect-repos.html +1 -1
  14. package/dist/dashboard/out/connect-repos.txt +1 -1
  15. package/dist/dashboard/out/history.html +1 -1
  16. package/dist/dashboard/out/history.txt +1 -1
  17. package/dist/dashboard/out/index.html +1 -1
  18. package/dist/dashboard/out/index.txt +1 -1
  19. package/dist/dashboard/out/login.html +1 -1
  20. package/dist/dashboard/out/login.txt +1 -1
  21. package/dist/dashboard/out/metrics.html +1 -1
  22. package/dist/dashboard/out/metrics.txt +1 -1
  23. package/dist/dashboard/out/pricing.html +1 -1
  24. package/dist/dashboard/out/pricing.txt +1 -1
  25. package/dist/dashboard/out/providers/setup/claude.html +1 -1
  26. package/dist/dashboard/out/providers/setup/claude.txt +1 -1
  27. package/dist/dashboard/out/providers/setup/codex.html +1 -1
  28. package/dist/dashboard/out/providers/setup/codex.txt +1 -1
  29. package/dist/dashboard/out/providers/setup/cursor.html +1 -1
  30. package/dist/dashboard/out/providers/setup/cursor.txt +1 -1
  31. package/dist/dashboard/out/providers.html +1 -1
  32. package/dist/dashboard/out/providers.txt +1 -1
  33. package/dist/dashboard/out/signup.html +1 -1
  34. package/dist/dashboard/out/signup.txt +1 -1
  35. package/dist/src/cli/index.js +131 -21
  36. package/package.json +20 -19
  37. package/packages/api-types/package.json +1 -1
  38. package/packages/bridge/dist/index.d.ts +1 -1
  39. package/packages/bridge/dist/index.js +1 -1
  40. package/packages/bridge/dist/spawner.d.ts +18 -0
  41. package/packages/bridge/dist/spawner.js +122 -38
  42. package/packages/bridge/package.json +8 -7
  43. package/packages/cloud/package.json +6 -6
  44. package/packages/config/package.json +2 -2
  45. package/packages/continuity/package.json +1 -1
  46. package/packages/daemon/dist/connection.js +5 -1
  47. package/packages/daemon/dist/relay-ledger.d.ts +3 -1
  48. package/packages/daemon/dist/relay-ledger.js +8 -2
  49. package/packages/daemon/dist/router.js +13 -0
  50. package/packages/daemon/dist/server.d.ts +7 -0
  51. package/packages/daemon/dist/server.js +338 -4
  52. package/packages/daemon/package.json +12 -12
  53. package/packages/dashboard/dist/server.js +29 -5
  54. package/packages/dashboard/package.json +13 -12
  55. package/packages/dashboard/ui-dist/404.html +1 -1
  56. package/packages/dashboard/ui-dist/app/onboarding.html +1 -1
  57. package/packages/dashboard/ui-dist/app/onboarding.txt +1 -1
  58. package/packages/dashboard/ui-dist/app.html +1 -1
  59. package/packages/dashboard/ui-dist/app.txt +1 -1
  60. package/packages/dashboard/ui-dist/cloud/link.html +1 -1
  61. package/packages/dashboard/ui-dist/cloud/link.txt +1 -1
  62. package/packages/dashboard/ui-dist/connect-repos.html +1 -1
  63. package/packages/dashboard/ui-dist/connect-repos.txt +1 -1
  64. package/packages/dashboard/ui-dist/history.html +1 -1
  65. package/packages/dashboard/ui-dist/history.txt +1 -1
  66. package/packages/dashboard/ui-dist/index.html +1 -1
  67. package/packages/dashboard/ui-dist/index.txt +1 -1
  68. package/packages/dashboard/ui-dist/login.html +1 -1
  69. package/packages/dashboard/ui-dist/login.txt +1 -1
  70. package/packages/dashboard/ui-dist/metrics.html +1 -1
  71. package/packages/dashboard/ui-dist/metrics.txt +1 -1
  72. package/packages/dashboard/ui-dist/pricing.html +1 -1
  73. package/packages/dashboard/ui-dist/pricing.txt +1 -1
  74. package/packages/dashboard/ui-dist/providers/setup/claude.html +1 -1
  75. package/packages/dashboard/ui-dist/providers/setup/claude.txt +1 -1
  76. package/packages/dashboard/ui-dist/providers/setup/codex.html +1 -1
  77. package/packages/dashboard/ui-dist/providers/setup/codex.txt +1 -1
  78. package/packages/dashboard/ui-dist/providers/setup/cursor.html +1 -1
  79. package/packages/dashboard/ui-dist/providers/setup/cursor.txt +1 -1
  80. package/packages/dashboard/ui-dist/providers.html +1 -1
  81. package/packages/dashboard/ui-dist/providers.txt +1 -1
  82. package/packages/dashboard/ui-dist/signup.html +1 -1
  83. package/packages/dashboard/ui-dist/signup.txt +1 -1
  84. package/packages/dashboard-server/dist/server.js +29 -5
  85. package/packages/dashboard-server/package.json +12 -12
  86. package/packages/hooks/package.json +4 -4
  87. package/packages/mcp/README.md +24 -3
  88. package/packages/mcp/dist/bin.js +13 -5
  89. package/packages/mcp/dist/client.d.ts +54 -1
  90. package/packages/mcp/dist/client.js +132 -18
  91. package/packages/mcp/dist/cloud.d.ts +12 -0
  92. package/packages/mcp/dist/cloud.js +125 -1
  93. package/packages/mcp/dist/file-transport.d.ts +97 -0
  94. package/packages/mcp/dist/file-transport.js +197 -0
  95. package/packages/mcp/dist/hybrid-client.d.ts +28 -0
  96. package/packages/mcp/dist/hybrid-client.js +159 -0
  97. package/packages/mcp/dist/index.d.ts +4 -2
  98. package/packages/mcp/dist/index.js +6 -2
  99. package/packages/mcp/dist/install.d.ts +23 -1
  100. package/packages/mcp/dist/install.js +229 -31
  101. package/packages/mcp/dist/server.js +7 -1
  102. package/packages/mcp/dist/simple.d.ts +1 -1
  103. package/packages/mcp/dist/tools/index.d.ts +1 -0
  104. package/packages/mcp/dist/tools/index.js +1 -0
  105. package/packages/mcp/dist/tools/relay-continuity.d.ts +35 -0
  106. package/packages/mcp/dist/tools/relay-continuity.js +101 -0
  107. package/packages/mcp/dist/tools/relay-health.d.ts +1 -4
  108. package/packages/mcp/dist/tools/relay-health.js +7 -15
  109. package/packages/mcp/dist/tools/relay-logs.js +4 -2
  110. package/packages/mcp/dist/tools/relay-metrics.d.ts +1 -4
  111. package/packages/mcp/dist/tools/relay-metrics.js +4 -15
  112. package/packages/mcp/dist/tools/relay-send.d.ts +2 -2
  113. package/packages/mcp/package.json +3 -2
  114. package/packages/memory/package.json +2 -2
  115. package/packages/policy/package.json +2 -2
  116. package/packages/protocol/dist/relay-pty-schemas.d.ts +14 -0
  117. package/packages/protocol/dist/types.d.ts +152 -2
  118. package/packages/protocol/package.json +1 -1
  119. package/packages/resiliency/package.json +1 -1
  120. package/packages/sdk/dist/client.js +7 -0
  121. package/packages/sdk/package.json +2 -2
  122. package/packages/spawner/package.json +1 -1
  123. package/packages/state/package.json +1 -1
  124. package/packages/storage/package.json +2 -2
  125. package/packages/telemetry/package.json +1 -1
  126. package/packages/trajectory/package.json +2 -2
  127. package/packages/user-directory/package.json +2 -2
  128. package/packages/utils/dist/logger.js +3 -1
  129. package/packages/utils/package.json +1 -1
  130. package/packages/wrapper/dist/relay-pty-orchestrator.d.ts +28 -1
  131. package/packages/wrapper/dist/relay-pty-orchestrator.js +292 -37
  132. package/packages/wrapper/package.json +6 -6
  133. package/scripts/demos/README.md +79 -0
  134. package/scripts/demos/server-capacity.sh +69 -0
  135. package/scripts/demos/sprint-planning.sh +73 -0
  136. /package/dist/dashboard/out/_next/static/{Fa21F5wPUusvLUxj67gGm → RgEj_9Y-mWbLaxggzni-X}/_buildManifest.js +0 -0
  137. /package/dist/dashboard/out/_next/static/{Fa21F5wPUusvLUxj67gGm → RgEj_9Y-mWbLaxggzni-X}/_ssgManifest.js +0 -0
  138. /package/packages/dashboard/ui-dist/_next/static/{Fa21F5wPUusvLUxj67gGm → RgEj_9Y-mWbLaxggzni-X}/_buildManifest.js +0 -0
  139. /package/packages/dashboard/ui-dist/_next/static/{Fa21F5wPUusvLUxj67gGm → RgEj_9Y-mWbLaxggzni-X}/_ssgManifest.js +0 -0
  140. /package/packages/dashboard/ui-dist/_next/static/{coRjSO5QkUdvCiJRCqxfi → UkLmDJOkaPWU2PaNQnkx5}/_buildManifest.js +0 -0
  141. /package/packages/dashboard/ui-dist/_next/static/{coRjSO5QkUdvCiJRCqxfi → UkLmDJOkaPWU2PaNQnkx5}/_ssgManifest.js +0 -0
  142. /package/packages/dashboard/ui-dist/_next/static/{yKnlyfJx-NzAy4kPAGkMr → bv9xidgU2pXi7xxPoCAK-}/_buildManifest.js +0 -0
  143. /package/packages/dashboard/ui-dist/_next/static/{yKnlyfJx-NzAy4kPAGkMr → bv9xidgU2pXi7xxPoCAK-}/_ssgManifest.js +0 -0
@@ -20,6 +20,7 @@ import { getMemoryMonitor } from '@agent-relay/resiliency';
20
20
  import { generateId } from '@agent-relay/wrapper';
21
21
  import { createConsensusIntegration, } from './consensus-integration.js';
22
22
  import { initTelemetry, track, shutdown as shutdownTelemetry, } from '@agent-relay/telemetry';
23
+ import { RelayWatchdog } from './relay-watchdog.js';
23
24
  export const DEFAULT_SOCKET_PATH = '/tmp/agent-relay.sock';
24
25
  export const DEFAULT_DAEMON_CONFIG = {
25
26
  ...DEFAULT_CONFIG,
@@ -43,6 +44,8 @@ export class Daemon {
43
44
  consensus;
44
45
  cloudSyncDebounceTimer;
45
46
  spawnManager;
47
+ shuttingDown = false;
48
+ relayWatchdog;
46
49
  /** Telemetry tracking */
47
50
  startTime;
48
51
  agentSpawnCount = 0;
@@ -92,7 +95,12 @@ export class Daemon {
92
95
  // The registry persists on every update; this is a no-op helper for symmetry.
93
96
  const agents = this.registry.getAgents();
94
97
  try {
95
- const targetPath = path.join(this.config.teamDir ?? path.dirname(this.config.socketPath), 'agents.json');
98
+ const targetDir = this.config.teamDir ?? path.dirname(this.config.socketPath);
99
+ const targetPath = path.join(targetDir, 'agents.json');
100
+ // Ensure directory exists (defensive - may have been deleted)
101
+ if (!fs.existsSync(targetDir)) {
102
+ fs.mkdirSync(targetDir, { recursive: true });
103
+ }
96
104
  const data = JSON.stringify({ agents }, null, 2);
97
105
  // Write atomically: write to temp file first, then rename
98
106
  // This prevents race conditions where readers see partial/empty data
@@ -109,16 +117,27 @@ export class Daemon {
109
117
  * This file contains agents currently processing/thinking after receiving a message.
110
118
  */
111
119
  writeProcessingStateFile() {
120
+ // Skip writes during shutdown to avoid race conditions with directory cleanup
121
+ if (this.shuttingDown)
122
+ return;
112
123
  try {
113
124
  const processingAgents = this.router.getProcessingAgents();
114
- const targetPath = path.join(this.config.teamDir ?? path.dirname(this.config.socketPath), 'processing-state.json');
125
+ const targetDir = this.config.teamDir ?? path.dirname(this.config.socketPath);
126
+ const targetPath = path.join(targetDir, 'processing-state.json');
127
+ // Ensure directory exists (defensive - may have been deleted)
128
+ if (!fs.existsSync(targetDir)) {
129
+ fs.mkdirSync(targetDir, { recursive: true });
130
+ }
115
131
  const data = JSON.stringify({ processingAgents, updatedAt: Date.now() }, null, 2);
116
132
  const tempPath = `${targetPath}.tmp`;
117
133
  fs.writeFileSync(tempPath, data, 'utf-8');
118
134
  fs.renameSync(tempPath, targetPath);
119
135
  }
120
136
  catch (err) {
121
- log.error('Failed to write processing-state.json', { error: String(err) });
137
+ // Suppress ENOENT errors during shutdown race conditions
138
+ if (!this.shuttingDown) {
139
+ log.error('Failed to write processing-state.json', { error: String(err) });
140
+ }
122
141
  }
123
142
  }
124
143
  /**
@@ -129,7 +148,12 @@ export class Daemon {
129
148
  try {
130
149
  const connectedAgents = this.router.getAgents();
131
150
  const connectedUsers = this.router.getUsers();
132
- const targetPath = path.join(this.config.teamDir ?? path.dirname(this.config.socketPath), 'connected-agents.json');
151
+ const targetDir = this.config.teamDir ?? path.dirname(this.config.socketPath);
152
+ const targetPath = path.join(targetDir, 'connected-agents.json');
153
+ // Ensure directory exists (defensive - may have been deleted)
154
+ if (!fs.existsSync(targetDir)) {
155
+ fs.mkdirSync(targetDir, { recursive: true });
156
+ }
133
157
  const data = JSON.stringify({
134
158
  agents: connectedAgents,
135
159
  users: connectedUsers,
@@ -333,6 +357,12 @@ export class Daemon {
333
357
  this.processingStateInterval = setInterval(() => {
334
358
  this.writeProcessingStateFile();
335
359
  }, Daemon.PROCESSING_STATE_INTERVAL_MS);
360
+ // Start RelayWatchdog for MCP file-based messages (ledger-based for durability)
361
+ // Use parent of teamDir since teamDir is .agent-relay/team/ but outbox is at .agent-relay/outbox/
362
+ const relayRoot = path.dirname(teamDir);
363
+ this.initRelayWatchdog(relayRoot).catch(err => {
364
+ log.error('Failed to start RelayWatchdog', { error: String(err) });
365
+ });
336
366
  // Track daemon start
337
367
  track('daemon_start', {});
338
368
  log.info('Listening', { socketPath: this.config.socketPath });
@@ -340,6 +370,107 @@ export class Daemon {
340
370
  });
341
371
  });
342
372
  }
373
+ /**
374
+ * Initialize RelayWatchdog for MCP and file-based agents.
375
+ * Uses the ledger-based watchdog for durable file processing.
376
+ */
377
+ async initRelayWatchdog(relayDir) {
378
+ // Create project-local relay paths
379
+ const relayPaths = {
380
+ rootDir: relayDir,
381
+ outboxDir: path.join(relayDir, 'outbox'),
382
+ attachmentsDir: path.join(relayDir, 'attachments'),
383
+ metaDir: path.join(relayDir, 'meta'),
384
+ legacyOutboxDir: path.join(relayDir, 'outbox'), // Same as outboxDir for project-local
385
+ };
386
+ this.relayWatchdog = new RelayWatchdog({
387
+ relayPaths,
388
+ ledgerPath: path.join(relayDir, 'meta', 'file-ledger.sqlite'),
389
+ });
390
+ // Handle delivered files from file-based agents (MCP, etc.)
391
+ this.relayWatchdog.on('file:delivered', (file) => {
392
+ const { agentName, messageType, headers, body } = file;
393
+ log.debug('File delivered', { agentName, messageType, headers });
394
+ // Determine message type from headers or filename
395
+ const kind = headers['KIND']?.toLowerCase() || messageType;
396
+ if (kind === 'spawn') {
397
+ // Handle spawn request
398
+ if (this.spawnManager) {
399
+ const envelope = {
400
+ v: PROTOCOL_VERSION,
401
+ type: 'SPAWN',
402
+ id: generateId(),
403
+ ts: Date.now(),
404
+ payload: {
405
+ name: headers['NAME'] || '',
406
+ cli: headers['CLI'] || 'claude',
407
+ task: body,
408
+ cwd: headers['CWD'],
409
+ model: headers['MODEL'],
410
+ spawnerName: agentName,
411
+ },
412
+ };
413
+ const virtualConnection = { agentName, send: () => true };
414
+ this.spawnManager.handleSpawn(virtualConnection, envelope);
415
+ }
416
+ else {
417
+ log.warn('Spawn request ignored - SpawnManager not enabled');
418
+ }
419
+ }
420
+ else if (kind === 'release') {
421
+ // Handle release request
422
+ if (this.spawnManager) {
423
+ const envelope = {
424
+ v: PROTOCOL_VERSION,
425
+ type: 'RELEASE',
426
+ id: generateId(),
427
+ ts: Date.now(),
428
+ payload: {
429
+ name: headers['NAME'] || '',
430
+ reason: body || undefined,
431
+ },
432
+ };
433
+ const virtualConnection = { agentName, send: () => true };
434
+ this.spawnManager.handleRelease(virtualConnection, envelope);
435
+ }
436
+ else {
437
+ log.warn('Release request ignored - SpawnManager not enabled');
438
+ }
439
+ }
440
+ else {
441
+ // Default: treat as message
442
+ const to = headers['TO'];
443
+ if (!to) {
444
+ log.warn('Message missing TO header', { agentName, headers });
445
+ return;
446
+ }
447
+ const envelope = {
448
+ v: PROTOCOL_VERSION,
449
+ type: 'SEND',
450
+ id: generateId(),
451
+ ts: Date.now(),
452
+ from: agentName,
453
+ to,
454
+ payload: {
455
+ kind: 'message',
456
+ body,
457
+ thread: headers['THREAD'],
458
+ },
459
+ };
460
+ const virtualConnection = { agentName };
461
+ this.router.route(virtualConnection, envelope);
462
+ }
463
+ });
464
+ // Log errors
465
+ this.relayWatchdog.on('error', (error) => {
466
+ log.error('RelayWatchdog error', { error: error.message });
467
+ });
468
+ this.relayWatchdog.on('file:failed', (record, error) => {
469
+ log.error('File processing failed', { fileId: record.fileId, error: error.message });
470
+ });
471
+ await this.relayWatchdog.start();
472
+ log.info('RelayWatchdog started', { relayDir });
473
+ }
343
474
  /**
344
475
  * Initialize cloud sync service for cross-machine agent communication.
345
476
  */
@@ -615,6 +746,8 @@ export class Daemon {
615
746
  async stop() {
616
747
  if (!this.running)
617
748
  return;
749
+ // Mark as shutting down to prevent race conditions with state file writes
750
+ this.shuttingDown = true;
618
751
  // Track daemon stop
619
752
  const uptimeSeconds = this.startTime
620
753
  ? Math.floor((Date.now() - this.startTime) / 1000)
@@ -640,6 +773,11 @@ export class Daemon {
640
773
  clearInterval(this.processingStateInterval);
641
774
  this.processingStateInterval = undefined;
642
775
  }
776
+ // Stop RelayWatchdog
777
+ if (this.relayWatchdog) {
778
+ await this.relayWatchdog.stop();
779
+ this.relayWatchdog = undefined;
780
+ }
643
781
  // Close all active connections
644
782
  for (const connection of this.connections) {
645
783
  connection.close();
@@ -933,6 +1071,202 @@ export class Daemon {
933
1071
  this.spawnManager.handleRelease(connection, releaseEnvelope);
934
1072
  break;
935
1073
  }
1074
+ // Query handlers (MCP/client requests)
1075
+ case 'STATUS': {
1076
+ const uptimeMs = this.startTime ? Date.now() - this.startTime : 0;
1077
+ const response = {
1078
+ v: PROTOCOL_VERSION,
1079
+ type: 'STATUS_RESPONSE',
1080
+ id: envelope.id,
1081
+ ts: Date.now(),
1082
+ payload: {
1083
+ version: '2.0.13',
1084
+ uptime: uptimeMs,
1085
+ cloudConnected: this.cloudSync?.isConnected() ?? false,
1086
+ agentCount: this.router.connectionCount,
1087
+ },
1088
+ };
1089
+ connection.send(response);
1090
+ break;
1091
+ }
1092
+ case 'INBOX': {
1093
+ const inboxPayload = envelope.payload;
1094
+ const agentName = inboxPayload.agent || connection.agentName;
1095
+ // Get messages from storage
1096
+ const getInboxMessages = async () => {
1097
+ if (!this.storage?.getMessages) {
1098
+ return [];
1099
+ }
1100
+ try {
1101
+ const messages = await this.storage.getMessages({
1102
+ to: agentName,
1103
+ from: inboxPayload.from,
1104
+ limit: inboxPayload.limit || 50,
1105
+ unreadOnly: inboxPayload.unreadOnly,
1106
+ });
1107
+ return messages.map(m => ({
1108
+ id: m.id,
1109
+ from: m.from,
1110
+ body: m.body,
1111
+ channel: m.data?.channel,
1112
+ thread: m.thread,
1113
+ timestamp: m.ts,
1114
+ }));
1115
+ }
1116
+ catch {
1117
+ return [];
1118
+ }
1119
+ };
1120
+ getInboxMessages().then(messages => {
1121
+ const response = {
1122
+ v: PROTOCOL_VERSION,
1123
+ type: 'INBOX_RESPONSE',
1124
+ id: envelope.id,
1125
+ ts: Date.now(),
1126
+ payload: { messages },
1127
+ };
1128
+ connection.send(response);
1129
+ }).catch(err => {
1130
+ this.sendErrorEnvelope(connection, `Failed to get inbox: ${err.message}`);
1131
+ });
1132
+ break;
1133
+ }
1134
+ case 'LIST_AGENTS': {
1135
+ const listPayload = envelope.payload;
1136
+ // Get connected agents from router
1137
+ const connectedAgents = this.router.getAgents();
1138
+ // Get all agents from registry for metadata lookup
1139
+ const registryAgents = this.registry?.getAgents() ?? [];
1140
+ const registryMap = new Map(registryAgents.map(a => [a.name, a]));
1141
+ // Build agent list from connected agents
1142
+ const agents = connectedAgents
1143
+ .filter(name => !this.isInternalAgent(name))
1144
+ .map(name => {
1145
+ const registryAgent = registryMap.get(name);
1146
+ return {
1147
+ name,
1148
+ cli: registryAgent?.cli,
1149
+ idle: false, // Connected agents are not idle
1150
+ parent: registryAgent?.task?.includes('spawned by') ? 'parent' : undefined,
1151
+ };
1152
+ });
1153
+ // Optionally include idle agents from registry
1154
+ if (listPayload.includeIdle && this.registry) {
1155
+ for (const agent of registryAgents) {
1156
+ if (!connectedAgents.includes(agent.name) && !this.isInternalAgent(agent.name)) {
1157
+ agents.push({
1158
+ name: agent.name,
1159
+ cli: agent.cli,
1160
+ idle: true,
1161
+ parent: undefined,
1162
+ });
1163
+ }
1164
+ }
1165
+ }
1166
+ const response = {
1167
+ v: PROTOCOL_VERSION,
1168
+ type: 'LIST_AGENTS_RESPONSE',
1169
+ id: envelope.id,
1170
+ ts: Date.now(),
1171
+ payload: { agents },
1172
+ };
1173
+ connection.send(response);
1174
+ break;
1175
+ }
1176
+ case 'HEALTH': {
1177
+ const healthPayload = envelope.payload;
1178
+ // Compute health based on available data
1179
+ const connectedAgents = this.router.getAgents();
1180
+ const registryAgents = this.registry?.getAgents() ?? [];
1181
+ const agentCount = connectedAgents.filter(n => !this.isInternalAgent(n)).length;
1182
+ // Basic health computation
1183
+ const issues = [];
1184
+ const recommendations = [];
1185
+ let healthScore = 100;
1186
+ // Check for memory issues via memory monitor
1187
+ const memoryMonitor = getMemoryMonitor();
1188
+ const memoryMetrics = memoryMonitor.getAll();
1189
+ const criticalAgents = memoryMetrics.filter(m => m.alertLevel === 'critical');
1190
+ const warningAgents = memoryMetrics.filter(m => m.alertLevel === 'warning');
1191
+ if (criticalAgents.length > 0) {
1192
+ healthScore -= 30;
1193
+ for (const agent of criticalAgents) {
1194
+ issues.push({ severity: 'critical', message: `${agent.name} has critical memory usage` });
1195
+ }
1196
+ recommendations.push('Consider releasing some agents to free memory');
1197
+ }
1198
+ if (warningAgents.length > 0) {
1199
+ healthScore -= 10;
1200
+ for (const agent of warningAgents) {
1201
+ issues.push({ severity: 'warning', message: `${agent.name} has high memory usage` });
1202
+ }
1203
+ }
1204
+ // Check cloud sync status
1205
+ if (!this.cloudSync?.isConnected()) {
1206
+ issues.push({ severity: 'info', message: 'Cloud sync not connected' });
1207
+ }
1208
+ const summary = healthScore >= 80 ? 'System is healthy' :
1209
+ healthScore >= 50 ? 'System has some issues' :
1210
+ 'System needs attention';
1211
+ const healthResponse = {
1212
+ v: PROTOCOL_VERSION,
1213
+ type: 'HEALTH_RESPONSE',
1214
+ id: envelope.id,
1215
+ ts: Date.now(),
1216
+ payload: {
1217
+ healthScore: Math.max(0, healthScore),
1218
+ summary,
1219
+ issues,
1220
+ recommendations,
1221
+ crashes: [], // Would need crash tracking implementation
1222
+ alerts: [], // Would need alert tracking implementation
1223
+ stats: {
1224
+ totalCrashes24h: 0,
1225
+ totalAlerts24h: 0,
1226
+ agentCount,
1227
+ },
1228
+ },
1229
+ };
1230
+ connection.send(healthResponse);
1231
+ break;
1232
+ }
1233
+ case 'METRICS': {
1234
+ const metricsPayload = envelope.payload;
1235
+ // Get metrics from memory monitor
1236
+ const memoryMonitor = getMemoryMonitor();
1237
+ let metrics = memoryMonitor.getAll();
1238
+ // Filter to specific agent if requested
1239
+ if (metricsPayload.agent) {
1240
+ metrics = metrics.filter(m => m.name === metricsPayload.agent);
1241
+ }
1242
+ // Convert to response format
1243
+ const agents = metrics.map(m => ({
1244
+ name: m.name,
1245
+ pid: m.pid,
1246
+ status: m.alertLevel === 'normal' ? 'running' : m.alertLevel,
1247
+ rssBytes: m.current.rssBytes,
1248
+ cpuPercent: m.current.cpuPercent,
1249
+ trend: m.trend,
1250
+ alertLevel: m.alertLevel,
1251
+ highWatermark: m.highWatermark,
1252
+ uptimeMs: m.uptimeMs,
1253
+ }));
1254
+ // System metrics
1255
+ const system = {
1256
+ totalMemory: os.totalmem(),
1257
+ freeMemory: os.freemem(),
1258
+ heapUsed: process.memoryUsage().heapUsed,
1259
+ };
1260
+ const metricsResponse = {
1261
+ v: PROTOCOL_VERSION,
1262
+ type: 'METRICS_RESPONSE',
1263
+ id: envelope.id,
1264
+ ts: Date.now(),
1265
+ payload: { agents, system },
1266
+ };
1267
+ connection.send(metricsResponse);
1268
+ break;
1269
+ }
936
1270
  }
937
1271
  }
938
1272
  handleAck(connection, envelope) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-relay/daemon",
3
- "version": "2.0.13",
3
+ "version": "2.0.14",
4
4
  "description": "Relay daemon server - agent coordination and message routing",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -22,17 +22,17 @@
22
22
  "test:watch": "vitest"
23
23
  },
24
24
  "dependencies": {
25
- "@agent-relay/protocol": "2.0.13",
26
- "@agent-relay/config": "2.0.13",
27
- "@agent-relay/storage": "2.0.13",
28
- "@agent-relay/bridge": "2.0.13",
29
- "@agent-relay/utils": "2.0.13",
30
- "@agent-relay/policy": "2.0.13",
31
- "@agent-relay/memory": "2.0.13",
32
- "@agent-relay/resiliency": "2.0.13",
33
- "@agent-relay/user-directory": "2.0.13",
34
- "@agent-relay/wrapper": "2.0.13",
35
- "@agent-relay/telemetry": "2.0.13",
25
+ "@agent-relay/protocol": "2.0.14",
26
+ "@agent-relay/config": "2.0.14",
27
+ "@agent-relay/storage": "2.0.14",
28
+ "@agent-relay/bridge": "2.0.14",
29
+ "@agent-relay/utils": "2.0.14",
30
+ "@agent-relay/policy": "2.0.14",
31
+ "@agent-relay/memory": "2.0.14",
32
+ "@agent-relay/resiliency": "2.0.14",
33
+ "@agent-relay/user-directory": "2.0.14",
34
+ "@agent-relay/wrapper": "2.0.14",
35
+ "@agent-relay/telemetry": "2.0.14",
36
36
  "ws": "^8.18.3",
37
37
  "better-sqlite3": "^12.6.2",
38
38
  "pg": "^8.16.3",
@@ -1910,9 +1910,9 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
1910
1910
  return false;
1911
1911
  }
1912
1912
  };
1913
- // Helper to subscribe to an agent
1914
- const subscribeToAgent = (agentName) => {
1915
- const isSpawned = spawner?.hasWorker(agentName) ?? false;
1913
+ // Helper to subscribe to an agent (async to handle spawn timing)
1914
+ const subscribeToAgent = async (agentName) => {
1915
+ let isSpawned = spawner?.hasWorker(agentName) ?? false;
1916
1916
  const isDaemon = isDaemonConnected(agentName);
1917
1917
  // Check if agent exists (either spawned or daemon-connected)
1918
1918
  if (!isSpawned && !isDaemon) {
@@ -1925,6 +1925,26 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
1925
1925
  ws.close(4404, 'Agent not found');
1926
1926
  return false;
1927
1927
  }
1928
+ // If agent is daemon-connected but not yet in spawner's activeWorkers,
1929
+ // poll briefly to handle race condition between spawn API returning and
1930
+ // WebSocket connection. This is common for setup agents (__setup__*).
1931
+ if (!isSpawned && isDaemon && spawner) {
1932
+ const maxWaitMs = 3000; // Wait up to 3 seconds
1933
+ const pollIntervalMs = 100;
1934
+ const startTime = Date.now();
1935
+ while (Date.now() - startTime < maxWaitMs) {
1936
+ await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
1937
+ isSpawned = spawner.hasWorker(agentName);
1938
+ if (isSpawned) {
1939
+ console.log(`[dashboard] Agent ${agentName} appeared in spawner after ${Date.now() - startTime}ms`);
1940
+ break;
1941
+ }
1942
+ // Check if WebSocket was closed during wait
1943
+ if (ws.readyState !== WebSocket.OPEN) {
1944
+ return false;
1945
+ }
1946
+ }
1947
+ }
1928
1948
  // Add to subscriptions
1929
1949
  clientSubscriptions.add(agentName);
1930
1950
  if (!logSubscriptions.has(agentName)) {
@@ -1960,14 +1980,18 @@ export async function startDashboard(portOrOptions, dataDirArg, teamDirArg, dbPa
1960
1980
  const pathMatch = pathname.match(/^\/ws\/logs\/(.+)$/);
1961
1981
  if (pathMatch) {
1962
1982
  const agentName = decodeURIComponent(pathMatch[1]);
1963
- subscribeToAgent(agentName);
1983
+ subscribeToAgent(agentName).catch((err) => {
1984
+ console.error(`[dashboard] Error subscribing to ${agentName}:`, err);
1985
+ });
1964
1986
  }
1965
1987
  ws.on('message', (data) => {
1966
1988
  try {
1967
1989
  const msg = JSON.parse(data.toString());
1968
1990
  // Subscribe to agent logs
1969
1991
  if (msg.subscribe && typeof msg.subscribe === 'string') {
1970
- subscribeToAgent(msg.subscribe);
1992
+ subscribeToAgent(msg.subscribe).catch((err) => {
1993
+ console.error(`[dashboard] Error subscribing to ${msg.subscribe}:`, err);
1994
+ });
1971
1995
  }
1972
1996
  // Unsubscribe from agent logs
1973
1997
  if (msg.unsubscribe && typeof msg.unsubscribe === 'string') {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-relay/dashboard",
3
- "version": "2.0.13",
3
+ "version": "2.0.14",
4
4
  "description": "Web dashboard for Agent Relay - optional package for visual agent coordination",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -25,17 +25,18 @@
25
25
  "test:watch": "vitest"
26
26
  },
27
27
  "dependencies": {
28
- "@agent-relay/protocol": "2.0.13",
29
- "@agent-relay/config": "2.0.13",
30
- "@agent-relay/storage": "2.0.13",
31
- "@agent-relay/bridge": "2.0.13",
32
- "@agent-relay/utils": "2.0.13",
33
- "@agent-relay/resiliency": "2.0.13",
34
- "@agent-relay/trajectory": "2.0.13",
35
- "@agent-relay/cloud": "2.0.13",
36
- "@agent-relay/daemon": "2.0.13",
37
- "@agent-relay/user-directory": "2.0.13",
38
- "@agent-relay/wrapper": "2.0.13",
28
+ "@agent-relay/protocol": "2.0.14",
29
+ "@agent-relay/config": "2.0.14",
30
+ "@agent-relay/storage": "2.0.14",
31
+ "@agent-relay/bridge": "2.0.14",
32
+ "@agent-relay/utils": "2.0.14",
33
+ "@agent-relay/resiliency": "2.0.14",
34
+ "@agent-relay/trajectory": "2.0.14",
35
+ "@agent-relay/cloud": "2.0.14",
36
+ "@agent-relay/daemon": "2.0.14",
37
+ "@agent-relay/user-directory": "2.0.14",
38
+ "@agent-relay/wrapper": "2.0.14",
39
+ "@agent-relay/sdk": "2.0.14",
39
40
  "express": "^5.2.1",
40
41
  "ws": "^8.18.3"
41
42
  },
@@ -1 +1 @@
1
- <!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/605dd4e30c91986f.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-1cdd8ed57114d5e1.js"/><script src="/_next/static/chunks/fd9d1056-609918ca7b6280bb.js" async=""></script><script src="/_next/static/chunks/117-c8afed19e821a35d.js" async=""></script><script src="/_next/static/chunks/main-app-fdbeb09028f57c9f.js" async=""></script><meta name="robots" content="noindex"/><title>404: This page could not be found.</title><title>Agent Relay Dashboard</title><meta name="description" content="Fleet control dashboard for Agent Relay"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><script src="/_next/static/chunks/webpack-1cdd8ed57114d5e1.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/_next/static/css/605dd4e30c91986f.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"2:I[2846,[],\"\"]\n4:I[4707,[],\"\"]\n5:I[6423,[],\"\"]\nb:I[1060,[],\"\"]\n6:{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"}\n7:{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"}\n8:{\"display\":\"inline-block\"}\n9:{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0}\nc:[]\n"])</script><script>self.__next_f.push([1,"0:[\"$\",\"$L2\",null,{\"buildId\":\"Fa21F5wPUusvLUxj67gGm\",\"assetPrefix\":\"\",\"urlParts\":[\"\",\"_not-found\"],\"initialTree\":[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{},[[\"$L3\",[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null],null],null]},[null,[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"/_not-found\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]],null]},[[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/605dd4e30c91986f.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"children\":[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":\"$6\",\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":\"$7\",\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":\"$8\",\"children\":[\"$\",\"h2\",null,{\"style\":\"$9\",\"children\":\"This page could not be found.\"}]}]]}]}]],\"notFoundStyles\":[]}]}]}]],null],null],\"couldBeIntercepted\":false,\"initialHead\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],\"$La\"],\"globalErrorComponent\":\"$b\",\"missingSlots\":\"$Wc\"}]\n"])</script><script>self.__next_f.push([1,"a:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"1\",{\"charSet\":\"utf-8\"}],[\"$\",\"title\",\"2\",{\"children\":\"Agent Relay Dashboard\"}],[\"$\",\"meta\",\"3\",{\"name\":\"description\",\"content\":\"Fleet control dashboard for Agent Relay\"}]]\n3:null\n"])</script></body></html>
1
+ <!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/605dd4e30c91986f.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-1cdd8ed57114d5e1.js"/><script src="/_next/static/chunks/fd9d1056-609918ca7b6280bb.js" async=""></script><script src="/_next/static/chunks/117-c8afed19e821a35d.js" async=""></script><script src="/_next/static/chunks/main-app-fdbeb09028f57c9f.js" async=""></script><meta name="robots" content="noindex"/><title>404: This page could not be found.</title><title>Agent Relay Dashboard</title><meta name="description" content="Fleet control dashboard for Agent Relay"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><script src="/_next/static/chunks/webpack-1cdd8ed57114d5e1.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/_next/static/css/605dd4e30c91986f.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"2:I[2846,[],\"\"]\n4:I[4707,[],\"\"]\n5:I[6423,[],\"\"]\nb:I[1060,[],\"\"]\n6:{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"}\n7:{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"}\n8:{\"display\":\"inline-block\"}\n9:{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0}\nc:[]\n"])</script><script>self.__next_f.push([1,"0:[\"$\",\"$L2\",null,{\"buildId\":\"RgEj_9Y-mWbLaxggzni-X\",\"assetPrefix\":\"\",\"urlParts\":[\"\",\"_not-found\"],\"initialTree\":[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{},[[\"$L3\",[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null],null],null]},[null,[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"/_not-found\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]],null]},[[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/605dd4e30c91986f.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"children\":[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":\"$6\",\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":\"$7\",\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":\"$8\",\"children\":[\"$\",\"h2\",null,{\"style\":\"$9\",\"children\":\"This page could not be found.\"}]}]]}]}]],\"notFoundStyles\":[]}]}]}]],null],null],\"couldBeIntercepted\":false,\"initialHead\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],\"$La\"],\"globalErrorComponent\":\"$b\",\"missingSlots\":\"$Wc\"}]\n"])</script><script>self.__next_f.push([1,"a:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"1\",{\"charSet\":\"utf-8\"}],[\"$\",\"title\",\"2\",{\"children\":\"Agent Relay Dashboard\"}],[\"$\",\"meta\",\"3\",{\"name\":\"description\",\"content\":\"Fleet control dashboard for Agent Relay\"}]]\n3:null\n"])</script></body></html>
@@ -1 +1 @@
1
- <!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/605dd4e30c91986f.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-1cdd8ed57114d5e1.js"/><script src="/_next/static/chunks/fd9d1056-609918ca7b6280bb.js" async=""></script><script src="/_next/static/chunks/117-c8afed19e821a35d.js" async=""></script><script src="/_next/static/chunks/main-app-fdbeb09028f57c9f.js" async=""></script><script src="/_next/static/chunks/app/app/onboarding/page-9914652442f7e4fb.js" async=""></script><title>Agent Relay Dashboard</title><meta name="description" content="Fleet control dashboard for Agent Relay"/><link rel="apple-touch-icon" href="/apple-icon.png?9e7a840704165ca6" type="image/png" sizes="256x256"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><!--$!--><template data-dgst="BAILOUT_TO_CLIENT_SIDE_RENDERING"></template><div class="min-h-screen bg-gradient-to-br from-[#0a0a0f] via-[#0d1117] to-[#0a0a0f] flex items-center justify-center"><div class="text-center"><svg class="w-8 h-8 text-accent-cyan animate-spin mx-auto" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path></svg><p class="mt-4 text-text-muted">Loading...</p></div></div><!--/$--><script src="/_next/static/chunks/webpack-1cdd8ed57114d5e1.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/_next/static/css/605dd4e30c91986f.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"2:I[2846,[],\"\"]\n4:I[9107,[],\"ClientPageRoot\"]\n5:I[4011,[\"615\",\"static/chunks/app/app/onboarding/page-9914652442f7e4fb.js\"],\"default\",1]\n6:I[4707,[],\"\"]\n7:I[6423,[],\"\"]\n9:I[1060,[],\"\"]\na:[]\n"])</script><script>self.__next_f.push([1,"0:[\"$\",\"$L2\",null,{\"buildId\":\"Fa21F5wPUusvLUxj67gGm\",\"assetPrefix\":\"\",\"urlParts\":[\"\",\"app\",\"onboarding\"],\"initialTree\":[\"\",{\"children\":[\"app\",{\"children\":[\"onboarding\",{\"children\":[\"__PAGE__\",{}]}]}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"app\",{\"children\":[\"onboarding\",{\"children\":[\"__PAGE__\",{},[[\"$L3\",[\"$\",\"$L4\",null,{\"props\":{\"params\":{},\"searchParams\":{}},\"Component\":\"$5\"}],null],null],null]},[null,[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"app\",\"children\",\"onboarding\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]],null]},[null,[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"app\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]],null]},[[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/605dd4e30c91986f.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"children\":[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],\"notFoundStyles\":[]}]}]}]],null],null],\"couldBeIntercepted\":false,\"initialHead\":[null,\"$L8\"],\"globalErrorComponent\":\"$9\",\"missingSlots\":\"$Wa\"}]\n"])</script><script>self.__next_f.push([1,"8:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"1\",{\"charSet\":\"utf-8\"}],[\"$\",\"title\",\"2\",{\"children\":\"Agent Relay Dashboard\"}],[\"$\",\"meta\",\"3\",{\"name\":\"description\",\"content\":\"Fleet control dashboard for Agent Relay\"}],[\"$\",\"link\",\"4\",{\"rel\":\"apple-touch-icon\",\"href\":\"/apple-icon.png?9e7a840704165ca6\",\"type\":\"image/png\",\"sizes\":\"256x256\"}]]\n3:null\n"])</script></body></html>
1
+ <!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/605dd4e30c91986f.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-1cdd8ed57114d5e1.js"/><script src="/_next/static/chunks/fd9d1056-609918ca7b6280bb.js" async=""></script><script src="/_next/static/chunks/117-c8afed19e821a35d.js" async=""></script><script src="/_next/static/chunks/main-app-fdbeb09028f57c9f.js" async=""></script><script src="/_next/static/chunks/app/app/onboarding/page-9914652442f7e4fb.js" async=""></script><title>Agent Relay Dashboard</title><meta name="description" content="Fleet control dashboard for Agent Relay"/><link rel="apple-touch-icon" href="/apple-icon.png?9e7a840704165ca6" type="image/png" sizes="256x256"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><!--$!--><template data-dgst="BAILOUT_TO_CLIENT_SIDE_RENDERING"></template><div class="min-h-screen bg-gradient-to-br from-[#0a0a0f] via-[#0d1117] to-[#0a0a0f] flex items-center justify-center"><div class="text-center"><svg class="w-8 h-8 text-accent-cyan animate-spin mx-auto" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path></svg><p class="mt-4 text-text-muted">Loading...</p></div></div><!--/$--><script src="/_next/static/chunks/webpack-1cdd8ed57114d5e1.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/_next/static/css/605dd4e30c91986f.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"2:I[2846,[],\"\"]\n4:I[9107,[],\"ClientPageRoot\"]\n5:I[4011,[\"615\",\"static/chunks/app/app/onboarding/page-9914652442f7e4fb.js\"],\"default\",1]\n6:I[4707,[],\"\"]\n7:I[6423,[],\"\"]\n9:I[1060,[],\"\"]\na:[]\n"])</script><script>self.__next_f.push([1,"0:[\"$\",\"$L2\",null,{\"buildId\":\"RgEj_9Y-mWbLaxggzni-X\",\"assetPrefix\":\"\",\"urlParts\":[\"\",\"app\",\"onboarding\"],\"initialTree\":[\"\",{\"children\":[\"app\",{\"children\":[\"onboarding\",{\"children\":[\"__PAGE__\",{}]}]}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"app\",{\"children\":[\"onboarding\",{\"children\":[\"__PAGE__\",{},[[\"$L3\",[\"$\",\"$L4\",null,{\"props\":{\"params\":{},\"searchParams\":{}},\"Component\":\"$5\"}],null],null],null]},[null,[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"app\",\"children\",\"onboarding\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]],null]},[null,[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"app\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]],null]},[[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/605dd4e30c91986f.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"children\":[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],\"notFoundStyles\":[]}]}]}]],null],null],\"couldBeIntercepted\":false,\"initialHead\":[null,\"$L8\"],\"globalErrorComponent\":\"$9\",\"missingSlots\":\"$Wa\"}]\n"])</script><script>self.__next_f.push([1,"8:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"1\",{\"charSet\":\"utf-8\"}],[\"$\",\"title\",\"2\",{\"children\":\"Agent Relay Dashboard\"}],[\"$\",\"meta\",\"3\",{\"name\":\"description\",\"content\":\"Fleet control dashboard for Agent Relay\"}],[\"$\",\"link\",\"4\",{\"rel\":\"apple-touch-icon\",\"href\":\"/apple-icon.png?9e7a840704165ca6\",\"type\":\"image/png\",\"sizes\":\"256x256\"}]]\n3:null\n"])</script></body></html>