agent-relay 1.5.0 → 1.5.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.
Files changed (39) hide show
  1. package/README.md +1 -1
  2. package/dist/bridge/spawner.d.ts +1 -1
  3. package/dist/bridge/spawner.js +189 -70
  4. package/dist/cli/index.js +25 -8
  5. package/dist/dashboard/out/404.html +1 -1
  6. package/dist/dashboard/out/_next/static/chunks/64-2cf6a4c4286af350.js +1 -0
  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 +2 -2
  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 +2 -2
  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.html +1 -1
  30. package/dist/dashboard/out/providers.txt +1 -1
  31. package/dist/dashboard/out/signup.html +1 -1
  32. package/dist/dashboard/out/signup.txt +1 -1
  33. package/dist/dashboard-server/server.js +32 -7
  34. package/dist/dashboard-server/user-bridge.d.ts +6 -0
  35. package/dist/dashboard-server/user-bridge.js +23 -0
  36. package/package.json +1 -1
  37. package/dist/dashboard/out/_next/static/chunks/64-87ab9cd6bcf2f737.js +0 -1
  38. /package/dist/dashboard/out/_next/static/{IxxVRv94L1w3ReRGAiI-k → c6Ndf9KrCr5HE-vSoIyj6}/_buildManifest.js +0 -0
  39. /package/dist/dashboard/out/_next/static/{IxxVRv94L1w3ReRGAiI-k → c6Ndf9KrCr5HE-vSoIyj6}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -190,7 +190,7 @@ npx prpm install @agent-relay/agent-relay-snippet
190
190
  npx prpm install @agent-relay/agent-relay-snippet --location CLAUDE.md
191
191
  ```
192
192
 
193
- View the rest of our packages on on [prpm organization page](https://prpm.dev/orgs?name=Agent%20Relay)
193
+ View the rest of our packages on our [prpm organization page](https://prpm.dev/orgs?name=Agent%20Relay)
194
194
 
195
195
  Or manually add the relay patterns to your agent instructions.
196
196
 
@@ -3,7 +3,7 @@
3
3
  * Handles spawning and releasing worker agents via node-pty.
4
4
  * Workers run headlessly with output capture for logs.
5
5
  */
6
- import type { SummaryEvent, SessionEndEvent } from '../wrapper/pty-wrapper.js';
6
+ import { type SummaryEvent, type SessionEndEvent } from '../wrapper/pty-wrapper.js';
7
7
  import { AgentPolicyService, type CloudPolicyFetcher } from '../policy/agent-policy.js';
8
8
  import type { SpawnRequest, SpawnResult, WorkerInfo, SpawnWithShadowRequest, SpawnWithShadowResult } from './types.js';
9
9
  /**
@@ -5,11 +5,16 @@
5
5
  */
6
6
  import fs from 'node:fs';
7
7
  import path from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
8
9
  import { sleep } from './utils.js';
9
10
  import { getProjectPaths } from '../utils/project-namespace.js';
10
11
  import { resolveCommand } from '../utils/command-resolver.js';
11
12
  import { RelayPtyOrchestrator } from '../wrapper/relay-pty-orchestrator.js';
13
+ import { PtyWrapper } from '../wrapper/pty-wrapper.js';
12
14
  import { selectShadowCli } from './shadow-cli.js';
15
+ // Get the directory where this module is located (for binary path resolution)
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = path.dirname(__filename);
13
18
  import { AgentPolicyService } from '../policy/agent-policy.js';
14
19
  import { buildClaudeArgs } from '../utils/agent-config.js';
15
20
  import { getUserDirectoryService } from '../daemon/user-directory.js';
@@ -25,23 +30,108 @@ function getRelayInstructions(agentName) {
25
30
  '',
26
31
  '## Sending Messages',
27
32
  '',
28
- 'Use fenced format for all messages:',
29
- '->relay:TargetAgent <<<',
30
- 'Your message here.>>>',
33
+ 'Write a file to your outbox, then output the trigger:',
34
+ '',
35
+ '```bash',
36
+ `cat > /tmp/relay-outbox/${agentName}/msg << 'EOF'`,
37
+ 'TO: TargetAgent',
38
+ '',
39
+ 'Your message here.',
40
+ 'EOF',
41
+ '```',
42
+ '',
43
+ 'Then output: `->relay-file:msg`',
31
44
  '',
32
45
  '## Communication Rules',
33
46
  '',
34
47
  '1. **ACK immediately** - When you receive a task:',
35
- ' ->relay:Sender <<<',
36
- ' ACK: Brief description of task received>>>',
48
+ '```bash',
49
+ `cat > /tmp/relay-outbox/${agentName}/ack << 'EOF'`,
50
+ 'TO: Sender',
51
+ '',
52
+ 'ACK: Brief description of task received',
53
+ 'EOF',
54
+ '```',
55
+ 'Then: `->relay-file:ack`',
37
56
  '',
38
57
  '2. **Report completion** - When done:',
39
- ' ->relay:Sender <<<',
40
- ' DONE: Brief summary of what was completed>>>',
58
+ '```bash',
59
+ `cat > /tmp/relay-outbox/${agentName}/done << 'EOF'`,
60
+ 'TO: Sender',
61
+ '',
62
+ 'DONE: Brief summary of what was completed',
63
+ 'EOF',
64
+ '```',
65
+ 'Then: `->relay-file:done`',
66
+ '',
67
+ '## Message Format',
41
68
  '',
42
- '3. Close >>> must immediately follow content (no blank lines before it)',
69
+ '```',
70
+ 'TO: Target',
71
+ 'THREAD: optional-thread',
72
+ '',
73
+ 'Message body (everything after blank line)',
74
+ '```',
75
+ '',
76
+ '| TO Value | Behavior |',
77
+ '|----------|----------|',
78
+ '| `AgentName` | Direct message |',
79
+ '| `*` | Broadcast to all |',
80
+ '| `#channel` | Channel message |',
43
81
  ].join('\n');
44
82
  }
83
+ /**
84
+ * Check if the relay-pty binary is available.
85
+ * Returns the path to the binary if found, null otherwise.
86
+ *
87
+ * Search order:
88
+ * 1. bin/relay-pty in package root (installed by postinstall)
89
+ * 2. relay-pty/target/release/relay-pty (local Rust build)
90
+ * 3. /usr/local/bin/relay-pty (global install)
91
+ */
92
+ function findRelayPtyBinary() {
93
+ // Get the package root (three levels up from dist/bridge/)
94
+ const packageRoot = path.join(__dirname, '..', '..');
95
+ const candidates = [
96
+ // Primary: installed by postinstall from platform-specific binary
97
+ path.join(packageRoot, 'bin', 'relay-pty'),
98
+ // Development: local Rust build
99
+ path.join(packageRoot, 'relay-pty', 'target', 'release', 'relay-pty'),
100
+ path.join(packageRoot, 'relay-pty', 'target', 'debug', 'relay-pty'),
101
+ // Local build in cwd (for development)
102
+ path.join(process.cwd(), 'relay-pty', 'target', 'release', 'relay-pty'),
103
+ // Installed globally
104
+ '/usr/local/bin/relay-pty',
105
+ // In node_modules (when installed as dependency)
106
+ path.join(process.cwd(), 'node_modules', 'agent-relay', 'bin', 'relay-pty'),
107
+ ];
108
+ for (const candidate of candidates) {
109
+ if (fs.existsSync(candidate)) {
110
+ return candidate;
111
+ }
112
+ }
113
+ return null;
114
+ }
115
+ /** Cached result of relay-pty binary check */
116
+ let relayPtyBinaryPath;
117
+ let relayPtyBinaryChecked = false;
118
+ /**
119
+ * Check if relay-pty binary is available (cached).
120
+ * Returns true if the binary exists, false otherwise.
121
+ */
122
+ function hasRelayPtyBinary() {
123
+ if (!relayPtyBinaryChecked) {
124
+ relayPtyBinaryPath = findRelayPtyBinary();
125
+ relayPtyBinaryChecked = true;
126
+ if (relayPtyBinaryPath) {
127
+ console.log(`[spawner] relay-pty binary found: ${relayPtyBinaryPath}`);
128
+ }
129
+ else {
130
+ console.log('[spawner] relay-pty binary not found, will use PtyWrapper fallback');
131
+ }
132
+ }
133
+ return relayPtyBinaryPath !== null;
134
+ }
45
135
  export class AgentSpawner {
46
136
  activeWorkers = new Map();
47
137
  agentsPath;
@@ -262,70 +352,99 @@ export class AgentSpawner {
262
352
  }
263
353
  if (debug)
264
354
  console.log(`[spawner:debug] Socket path for ${name}: ${this.socketPath ?? 'undefined'}`);
265
- const ptyConfig = {
266
- name,
267
- command,
268
- args,
269
- socketPath: this.socketPath,
270
- cwd: agentCwd,
271
- dashboardPort: this.dashboardPort,
272
- env: userEnv,
273
- streamLogs: true,
274
- // Shadow agent configuration
275
- shadowOf: request.shadowOf,
276
- shadowSpeakOn: request.shadowSpeakOn,
277
- // Skip continuity for spawned agents - they're short-lived workers
278
- skipContinuity: true,
279
- // Only use callbacks if dashboardPort is not set (for backwards compatibility)
280
- onSpawn: this.dashboardPort ? undefined : async (workerName, workerCli, workerTask) => {
281
- // Handle nested spawn requests (legacy path, may fail in non-TTY)
282
- if (debug)
283
- console.log(`[spawner:debug] Nested spawn: ${workerName}`);
284
- await this.spawn({
285
- name: workerName,
286
- cli: workerCli,
287
- task: workerTask,
288
- // Nested spawns don't inherit team - they're flat by default
289
- userId,
355
+ // Check if relay-pty binary is available - use PtyWrapper as fallback
356
+ const useRelayPty = hasRelayPtyBinary();
357
+ // Common exit handler for both wrapper types
358
+ const onExitHandler = (code) => {
359
+ if (debug)
360
+ console.log(`[spawner:debug] Worker ${name} exited with code ${code}`);
361
+ // Get the agentId and clean up listeners before removing from active workers
362
+ const worker = this.activeWorkers.get(name);
363
+ const agentId = worker?.pty?.getAgentId?.();
364
+ if (worker?.listeners) {
365
+ this.unbindListeners(worker.pty, worker.listeners);
366
+ }
367
+ this.activeWorkers.delete(name);
368
+ try {
369
+ this.saveWorkersMetadata();
370
+ }
371
+ catch (err) {
372
+ console.error(`[spawner] Failed to save metadata on exit:`, err);
373
+ }
374
+ // Notify if agent died unexpectedly (non-zero exit)
375
+ if (code !== 0 && code !== null && this.onAgentDeath) {
376
+ this.onAgentDeath({
377
+ name,
378
+ exitCode: code,
379
+ agentId,
380
+ resumeInstructions: agentId
381
+ ? `To resume this agent's work, use: --resume ${agentId}`
382
+ : undefined,
290
383
  });
291
- },
292
- onRelease: this.dashboardPort ? undefined : async (workerName) => {
293
- // Handle release requests from workers (legacy path)
294
- if (debug)
295
- console.log(`[spawner:debug] Release request: ${workerName}`);
296
- await this.release(workerName);
297
- },
298
- onExit: (code) => {
299
- if (debug)
300
- console.log(`[spawner:debug] Worker ${name} exited with code ${code}`);
301
- // Get the agentId and clean up listeners before removing from active workers
302
- const worker = this.activeWorkers.get(name);
303
- const agentId = worker?.pty?.getAgentId?.();
304
- if (worker?.listeners) {
305
- this.unbindListeners(worker.pty, worker.listeners);
306
- }
307
- this.activeWorkers.delete(name);
308
- try {
309
- this.saveWorkersMetadata();
310
- }
311
- catch (err) {
312
- console.error(`[spawner] Failed to save metadata on exit:`, err);
313
- }
314
- // Notify if agent died unexpectedly (non-zero exit)
315
- if (code !== 0 && code !== null && this.onAgentDeath) {
316
- this.onAgentDeath({
317
- name,
318
- exitCode: code,
319
- agentId,
320
- resumeInstructions: agentId
321
- ? `To resume this agent's work, use: --resume ${agentId}`
322
- : undefined,
323
- });
324
- }
325
- },
384
+ }
385
+ };
386
+ // Common spawn/release handlers
387
+ const onSpawnHandler = this.dashboardPort ? undefined : async (workerName, workerCli, workerTask) => {
388
+ if (debug)
389
+ console.log(`[spawner:debug] Nested spawn: ${workerName}`);
390
+ await this.spawn({
391
+ name: workerName,
392
+ cli: workerCli,
393
+ task: workerTask,
394
+ userId,
395
+ });
326
396
  };
327
- // Create and start the relay-pty orchestrator
328
- const pty = new RelayPtyOrchestrator(ptyConfig);
397
+ const onReleaseHandler = this.dashboardPort ? undefined : async (workerName) => {
398
+ if (debug)
399
+ console.log(`[spawner:debug] Release request: ${workerName}`);
400
+ await this.release(workerName);
401
+ };
402
+ // Create the appropriate wrapper based on binary availability
403
+ let pty;
404
+ if (useRelayPty) {
405
+ const ptyConfig = {
406
+ name,
407
+ command,
408
+ args,
409
+ socketPath: this.socketPath,
410
+ cwd: agentCwd,
411
+ dashboardPort: this.dashboardPort,
412
+ env: userEnv,
413
+ streamLogs: true,
414
+ shadowOf: request.shadowOf,
415
+ shadowSpeakOn: request.shadowSpeakOn,
416
+ skipContinuity: true,
417
+ onSpawn: onSpawnHandler,
418
+ onRelease: onReleaseHandler,
419
+ onExit: onExitHandler,
420
+ };
421
+ pty = new RelayPtyOrchestrator(ptyConfig);
422
+ if (debug)
423
+ console.log(`[spawner:debug] Using RelayPtyOrchestrator for ${name}`);
424
+ }
425
+ else {
426
+ const ptyConfig = {
427
+ name,
428
+ command,
429
+ args,
430
+ socketPath: this.socketPath,
431
+ cwd: agentCwd,
432
+ dashboardPort: this.dashboardPort,
433
+ env: userEnv,
434
+ streamLogs: true,
435
+ shadowOf: request.shadowOf,
436
+ shadowSpeakOn: request.shadowSpeakOn,
437
+ skipContinuity: true,
438
+ onSpawn: onSpawnHandler,
439
+ onRelease: onReleaseHandler,
440
+ onExit: onExitHandler,
441
+ logsDir: this.logsDir,
442
+ allowSpawn: true,
443
+ };
444
+ pty = new PtyWrapper(ptyConfig);
445
+ if (debug)
446
+ console.log(`[spawner:debug] Using PtyWrapper fallback for ${name}`);
447
+ }
329
448
  // Track listener references for proper cleanup
330
449
  const listeners = {};
331
450
  // Hook up output events for live log streaming
package/dist/cli/index.js CHANGED
@@ -1117,20 +1117,37 @@ ${projectContext}
1117
1117
 
1118
1118
  ## Cross-Project Messaging
1119
1119
 
1120
- Use this syntax to message agents in specific projects:
1120
+ Write a file to your outbox, then output the trigger. Use project:AgentName syntax:
1121
1121
 
1122
+ \`\`\`bash
1123
+ # Message specific project lead
1124
+ cat > /tmp/relay-outbox/\$AGENT_RELAY_NAME/msg << 'EOF'
1125
+ TO: ${valid[0].id}:${valid[0].leadName}
1126
+
1127
+ Your message to this project's lead.
1128
+ EOF
1122
1129
  \`\`\`
1123
- ->relay:${valid[0].id}:${valid[0].leadName} <<<
1124
- Your message to this project's lead>>>
1130
+ Then output: \`->relay-file:msg\`
1125
1131
 
1126
- ->relay:${valid.length > 1 ? valid[1].id : valid[0].id}:* <<<
1127
- Broadcast to all agents in a project>>>
1132
+ \`\`\`bash
1133
+ # Broadcast to all agents in a project
1134
+ cat > /tmp/relay-outbox/\$AGENT_RELAY_NAME/broadcast << 'EOF'
1135
+ TO: ${valid.length > 1 ? valid[1].id : valid[0].id}:*
1128
1136
 
1129
- ->relay:*:* <<<
1130
- Broadcast to ALL agents in ALL projects>>>
1137
+ Broadcast to all agents in a project.
1138
+ EOF
1131
1139
  \`\`\`
1140
+ Then output: \`->relay-file:broadcast\`
1141
+
1142
+ \`\`\`bash
1143
+ # Broadcast to ALL agents in ALL projects
1144
+ cat > /tmp/relay-outbox/\$AGENT_RELAY_NAME/all << 'EOF'
1145
+ TO: *:*
1132
1146
 
1133
- Format: \`->relay:project-id:agent-name\`
1147
+ Broadcast to ALL agents in ALL projects.
1148
+ EOF
1149
+ \`\`\`
1150
+ Then output: \`->relay-file:all\`
1134
1151
 
1135
1152
  ## Getting Started
1136
1153
  1. Check in with each project lead to understand current status
@@ -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/8f9ed310f454e5a5.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-6e8e8d3ef4e0192a.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/8f9ed310f454e5a5.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\":\"IxxVRv94L1w3ReRGAiI-k\",\"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/8f9ed310f454e5a5.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/8f9ed310f454e5a5.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-6e8e8d3ef4e0192a.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/8f9ed310f454e5a5.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\":\"c6Ndf9KrCr5HE-vSoIyj6\",\"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/8f9ed310f454e5a5.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>