agent-relay 2.1.4 → 2.1.6

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 (132) hide show
  1. package/README.md +85 -236
  2. package/dist/index.cjs +281 -24
  3. package/package.json +19 -19
  4. package/packages/api-types/package.json +1 -1
  5. package/packages/benchmark/package.json +4 -4
  6. package/packages/bridge/dist/spawner.d.ts.map +1 -1
  7. package/packages/bridge/dist/spawner.js +39 -5
  8. package/packages/bridge/dist/spawner.js.map +1 -1
  9. package/packages/bridge/package.json +8 -8
  10. package/packages/bridge/src/spawner.ts +40 -5
  11. package/packages/cli-tester/package.json +1 -1
  12. package/packages/config/package.json +2 -2
  13. package/packages/continuity/package.json +2 -2
  14. package/packages/daemon/dist/server.d.ts +5 -0
  15. package/packages/daemon/dist/server.d.ts.map +1 -1
  16. package/packages/daemon/dist/server.js +31 -0
  17. package/packages/daemon/dist/server.js.map +1 -1
  18. package/packages/daemon/package.json +12 -12
  19. package/packages/daemon/src/server.ts +37 -0
  20. package/packages/hooks/package.json +4 -4
  21. package/packages/mcp/dist/cloud.d.ts +7 -114
  22. package/packages/mcp/dist/cloud.d.ts.map +1 -1
  23. package/packages/mcp/dist/cloud.js +21 -431
  24. package/packages/mcp/dist/cloud.js.map +1 -1
  25. package/packages/mcp/dist/errors.d.ts +4 -22
  26. package/packages/mcp/dist/errors.d.ts.map +1 -1
  27. package/packages/mcp/dist/errors.js +4 -43
  28. package/packages/mcp/dist/errors.js.map +1 -1
  29. package/packages/mcp/dist/hybrid-client.d.ts.map +1 -1
  30. package/packages/mcp/dist/hybrid-client.js +7 -1
  31. package/packages/mcp/dist/hybrid-client.js.map +1 -1
  32. package/packages/mcp/package.json +4 -3
  33. package/packages/mcp/src/cloud.ts +29 -511
  34. package/packages/mcp/src/errors.ts +12 -49
  35. package/packages/mcp/src/hybrid-client.ts +8 -1
  36. package/packages/mcp/tests/discover.test.ts +72 -11
  37. package/packages/memory/package.json +2 -2
  38. package/packages/policy/package.json +2 -2
  39. package/packages/protocol/dist/types.d.ts +17 -1
  40. package/packages/protocol/dist/types.d.ts.map +1 -1
  41. package/packages/protocol/package.json +1 -1
  42. package/packages/protocol/src/types.ts +23 -0
  43. package/packages/resiliency/package.json +1 -1
  44. package/packages/sdk/dist/browser-client.d.ts +212 -0
  45. package/packages/sdk/dist/browser-client.d.ts.map +1 -0
  46. package/packages/sdk/dist/browser-client.js +750 -0
  47. package/packages/sdk/dist/browser-client.js.map +1 -0
  48. package/packages/sdk/dist/browser-framing.d.ts +46 -0
  49. package/packages/sdk/dist/browser-framing.d.ts.map +1 -0
  50. package/packages/sdk/dist/browser-framing.js +122 -0
  51. package/packages/sdk/dist/browser-framing.js.map +1 -0
  52. package/packages/sdk/dist/client.d.ts +129 -2
  53. package/packages/sdk/dist/client.d.ts.map +1 -1
  54. package/packages/sdk/dist/client.js +312 -2
  55. package/packages/sdk/dist/client.js.map +1 -1
  56. package/packages/sdk/dist/discovery.d.ts +10 -0
  57. package/packages/sdk/dist/discovery.d.ts.map +1 -0
  58. package/packages/sdk/dist/discovery.js +22 -0
  59. package/packages/sdk/dist/discovery.js.map +1 -0
  60. package/packages/sdk/dist/errors.d.ts +9 -0
  61. package/packages/sdk/dist/errors.d.ts.map +1 -0
  62. package/packages/sdk/dist/errors.js +9 -0
  63. package/packages/sdk/dist/errors.js.map +1 -0
  64. package/packages/sdk/dist/index.d.ts +18 -2
  65. package/packages/sdk/dist/index.d.ts.map +1 -1
  66. package/packages/sdk/dist/index.js +27 -1
  67. package/packages/sdk/dist/index.js.map +1 -1
  68. package/packages/sdk/dist/transports/index.d.ts +92 -0
  69. package/packages/sdk/dist/transports/index.d.ts.map +1 -0
  70. package/packages/sdk/dist/transports/index.js +129 -0
  71. package/packages/sdk/dist/transports/index.js.map +1 -0
  72. package/packages/sdk/dist/transports/socket-transport.d.ts +30 -0
  73. package/packages/sdk/dist/transports/socket-transport.d.ts.map +1 -0
  74. package/packages/sdk/dist/transports/socket-transport.js +94 -0
  75. package/packages/sdk/dist/transports/socket-transport.js.map +1 -0
  76. package/packages/sdk/dist/transports/types.d.ts +69 -0
  77. package/packages/sdk/dist/transports/types.d.ts.map +1 -0
  78. package/packages/sdk/dist/transports/types.js +10 -0
  79. package/packages/sdk/dist/transports/types.js.map +1 -0
  80. package/packages/sdk/dist/transports/websocket-transport.d.ts +55 -0
  81. package/packages/sdk/dist/transports/websocket-transport.d.ts.map +1 -0
  82. package/packages/sdk/dist/transports/websocket-transport.js +180 -0
  83. package/packages/sdk/dist/transports/websocket-transport.js.map +1 -0
  84. package/packages/sdk/package.json +28 -4
  85. package/packages/sdk/src/browser-client.ts +985 -0
  86. package/packages/sdk/src/browser-framing.test.ts +115 -0
  87. package/packages/sdk/src/browser-framing.ts +150 -0
  88. package/packages/sdk/src/client.test.ts +425 -0
  89. package/packages/sdk/src/client.ts +397 -3
  90. package/packages/sdk/src/discovery.ts +38 -0
  91. package/packages/sdk/src/errors.ts +17 -0
  92. package/packages/sdk/src/index.ts +82 -1
  93. package/packages/sdk/src/transports/index.ts +197 -0
  94. package/packages/sdk/src/transports/socket-transport.ts +115 -0
  95. package/packages/sdk/src/transports/types.ts +77 -0
  96. package/packages/sdk/src/transports/websocket-transport.ts +245 -0
  97. package/packages/sdk/tsconfig.json +1 -1
  98. package/packages/spawner/package.json +1 -1
  99. package/packages/state/package.json +1 -1
  100. package/packages/storage/package.json +2 -2
  101. package/packages/storage/src/jsonl-adapter.test.ts +8 -3
  102. package/packages/telemetry/package.json +1 -1
  103. package/packages/trajectory/package.json +2 -2
  104. package/packages/user-directory/package.json +2 -2
  105. package/packages/utils/dist/cjs/discovery.js +328 -0
  106. package/packages/utils/dist/cjs/errors.js +81 -0
  107. package/packages/utils/dist/discovery.d.ts +123 -0
  108. package/packages/utils/dist/discovery.d.ts.map +1 -0
  109. package/packages/utils/dist/discovery.js +439 -0
  110. package/packages/utils/dist/discovery.js.map +1 -0
  111. package/packages/utils/dist/errors.d.ts +29 -0
  112. package/packages/utils/dist/errors.d.ts.map +1 -0
  113. package/packages/utils/dist/errors.js +50 -0
  114. package/packages/utils/dist/errors.js.map +1 -0
  115. package/packages/utils/package.json +15 -2
  116. package/packages/utils/src/consolidation.test.ts +125 -0
  117. package/packages/utils/src/discovery.test.ts +196 -0
  118. package/packages/utils/src/discovery.ts +524 -0
  119. package/packages/utils/src/errors.test.ts +83 -0
  120. package/packages/utils/src/errors.ts +56 -0
  121. package/packages/wrapper/dist/opencode-wrapper.d.ts +6 -2
  122. package/packages/wrapper/dist/opencode-wrapper.d.ts.map +1 -1
  123. package/packages/wrapper/dist/opencode-wrapper.js +34 -10
  124. package/packages/wrapper/dist/opencode-wrapper.js.map +1 -1
  125. package/packages/wrapper/dist/relay-pty-orchestrator.d.ts +22 -2
  126. package/packages/wrapper/dist/relay-pty-orchestrator.d.ts.map +1 -1
  127. package/packages/wrapper/dist/relay-pty-orchestrator.js +174 -4
  128. package/packages/wrapper/dist/relay-pty-orchestrator.js.map +1 -1
  129. package/packages/wrapper/package.json +6 -6
  130. package/packages/wrapper/src/opencode-wrapper.ts +37 -9
  131. package/packages/wrapper/src/relay-pty-orchestrator.ts +197 -4
  132. package/relay-snippets/agent-relay-snippet.md +17 -5
@@ -64,6 +64,61 @@ const MAX_SOCKET_PATH_LENGTH = 107;
64
64
  */
65
65
  const MAX_OUTPUT_BUFFER_SIZE = 10 * 1024 * 1024; // 10MB
66
66
 
67
+ // ============================================================================
68
+ // Activity Verification Constants
69
+ // ============================================================================
70
+
71
+ /**
72
+ * Activity patterns used to verify that a CLI has received and started processing
73
+ * an injected task. These patterns work across Claude Code, Codex, and Droid/Gemini.
74
+ *
75
+ * The problem we're solving: Rust confirms "delivered to PTY" but that doesn't mean
76
+ * the CLI processed it. The T-003 failure showed the CLI wasn't ready when input arrived.
77
+ *
78
+ * @see https://github.com/your-org/relay/issues/XXX for the original investigation
79
+ */
80
+ const ACTIVITY_VERIFICATION = {
81
+ /** Time to wait for activity patterns after injection (ms) */
82
+ TIMEOUT_MS: 5000,
83
+ /** How often to check for activity patterns (ms) */
84
+ POLL_INTERVAL_MS: 200,
85
+ /** Maximum retries when no activity is detected */
86
+ MAX_RETRIES: 3,
87
+ /** Delay between retries (ms) */
88
+ RETRY_DELAY_MS: 500,
89
+
90
+ /**
91
+ * Patterns indicating the task was received and displayed.
92
+ * These are the primary verification patterns.
93
+ */
94
+ TASK_RECEIVED_PATTERNS: [
95
+ /\[Pasted text #\d+/, // Claude Code shows "[Pasted text #1 +95 lines]"
96
+ /› Relay message from/, // Codex shows "› Relay message from"
97
+ /Relay message from \w+ \[[\w-]+\]/, // Droid/Gemini shows "Relay message from Agent [id]:"
98
+ ],
99
+
100
+ /**
101
+ * Patterns indicating the CLI is thinking/processing.
102
+ * Secondary verification - proves the CLI is active.
103
+ */
104
+ THINKING_PATTERNS: [
105
+ /\(.*esc to (?:interrupt|stop)\)/i, // All CLIs: "(esc to interrupt)" or "(Press ESC to stop)"
106
+ /[✻✶✳✢·✽⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]/, // Spinner characters (Claude + Droid)
107
+ /Thinking\.\.\./, // Droid: "Thinking..."
108
+ /Working/, // Codex: "Working"
109
+ /Forming|Noodling|Manifesting/i, // Claude Code thinking states
110
+ ],
111
+
112
+ /**
113
+ * Patterns indicating tool execution started.
114
+ * Tertiary verification - proves the CLI is working.
115
+ */
116
+ TOOL_EXECUTION_PATTERNS: [
117
+ /⏺\s*(Bash|Read|Write|Edit|Glob|Grep|Task|WebFetch)/, // Claude Code tool markers
118
+ /•\s*Running/, // Codex: "• Running: command"
119
+ ],
120
+ } as const;
121
+
67
122
  function hashWorkspaceId(workspaceId: string): string {
68
123
  return createHash('sha256').update(workspaceId).digest('hex').slice(0, 12);
69
124
  }
@@ -213,6 +268,7 @@ export class RelayPtyOrchestrator extends BaseWrapper {
213
268
  private outputBuffer = '';
214
269
  private rawBuffer = '';
215
270
  private lastParsedLength = 0;
271
+ private bufferTrimCount = 0;
216
272
 
217
273
  // Interactive mode (show output to terminal)
218
274
  private isInteractive = false;
@@ -965,15 +1021,21 @@ export class RelayPtyOrchestrator extends BaseWrapper {
965
1021
 
966
1022
  // Trim buffers if they exceed max size to prevent RangeError: Invalid string length
967
1023
  // Keep the most recent output (tail) as it's more relevant for pattern matching
1024
+ let buffersTrimmed = false;
968
1025
  if (this.rawBuffer.length > MAX_OUTPUT_BUFFER_SIZE) {
969
1026
  const trimAmount = this.rawBuffer.length - MAX_OUTPUT_BUFFER_SIZE;
970
1027
  this.rawBuffer = this.rawBuffer.slice(-MAX_OUTPUT_BUFFER_SIZE);
971
1028
  // Adjust lastParsedLength to stay in sync with the trimmed buffer
972
1029
  // This ensures parseRelayCommands() doesn't skip content or re-parse old content
973
1030
  this.lastParsedLength = Math.max(0, this.lastParsedLength - trimAmount);
1031
+ buffersTrimmed = true;
974
1032
  }
975
1033
  if (this.outputBuffer.length > MAX_OUTPUT_BUFFER_SIZE) {
976
1034
  this.outputBuffer = this.outputBuffer.slice(-MAX_OUTPUT_BUFFER_SIZE);
1035
+ buffersTrimmed = true;
1036
+ }
1037
+ if (buffersTrimmed) {
1038
+ this.bufferTrimCount += 1;
977
1039
  }
978
1040
 
979
1041
  // Feed to idle detector
@@ -2572,17 +2634,148 @@ Then output: \`->relay-file:spawn\`
2572
2634
  }
2573
2635
 
2574
2636
  /**
2575
- * Inject a task using the socket-based injection system with verification.
2637
+ * Verify that the CLI shows activity after task injection.
2638
+ * Checks output for patterns indicating the task was received and processing started.
2639
+ *
2640
+ * This catches the race condition where PTY write succeeds but CLI wasn't ready
2641
+ * (the T-003 failure scenario where CLI showed bell characters instead of processing).
2642
+ *
2643
+ * @param outputBefore The output buffer content before injection
2644
+ * @returns Promise resolving to true if activity detected, false otherwise
2645
+ */
2646
+ private async verifyActivityAfterInjection(outputBefore: string): Promise<boolean> {
2647
+ const startTime = Date.now();
2648
+ const { TIMEOUT_MS, POLL_INTERVAL_MS, TASK_RECEIVED_PATTERNS, THINKING_PATTERNS, TOOL_EXECUTION_PATTERNS } = ACTIVITY_VERIFICATION;
2649
+ const trimCountBefore = this.bufferTrimCount;
2650
+
2651
+ while (Date.now() - startTime < TIMEOUT_MS) {
2652
+ // If buffers were trimmed during verification, treat it as activity.
2653
+ // Large output growth triggers trimming, which would otherwise make slice() return
2654
+ // an empty string and falsely signal no activity.
2655
+ if (this.bufferTrimCount !== trimCountBefore) {
2656
+ this.log(` Activity verified: output buffer trimmed during verification (large output)`);
2657
+ return true;
2658
+ }
2659
+
2660
+ // Get new output since injection
2661
+ const currentOutput = this.outputBuffer;
2662
+ const newOutput = currentOutput.slice(outputBefore.length);
2663
+
2664
+ if (newOutput.length > 0) {
2665
+ // REJECTION CHECK: Multiple BEL characters (0x07) indicate CLI rejected input
2666
+ // The T-003 failure showed CLI producing 1000+ BELs when input was injected
2667
+ // while the CLI wasn't ready (still initializing or in wrong mode)
2668
+ const belCount = (newOutput.match(/\x07/g) || []).length;
2669
+ if (belCount > 10) {
2670
+ this.logError(` Input rejected: CLI produced ${belCount} BEL characters`);
2671
+ return false; // Don't wait - immediately fail so retry can attempt
2672
+ }
2673
+
2674
+ // Check for task received patterns (primary verification)
2675
+ for (const pattern of TASK_RECEIVED_PATTERNS) {
2676
+ if (pattern.test(newOutput)) {
2677
+ this.log(` Activity verified: task received pattern matched`);
2678
+ return true;
2679
+ }
2680
+ }
2681
+
2682
+ // Check for thinking/processing patterns (secondary verification)
2683
+ for (const pattern of THINKING_PATTERNS) {
2684
+ if (pattern.test(newOutput)) {
2685
+ this.log(` Activity verified: thinking pattern matched`);
2686
+ return true;
2687
+ }
2688
+ }
2689
+
2690
+ // Check for tool execution patterns (tertiary verification)
2691
+ for (const pattern of TOOL_EXECUTION_PATTERNS) {
2692
+ if (pattern.test(newOutput)) {
2693
+ this.log(` Activity verified: tool execution pattern matched`);
2694
+ return true;
2695
+ }
2696
+ }
2697
+
2698
+ // Fallback: If output grew significantly (>100 meaningful chars), assume activity
2699
+ // This catches CLIs with unusual output patterns
2700
+ // IMPORTANT: Strip out BEL (0x07) and other control characters - these are rejection
2701
+ // signals not activity. The T-003 failure showed CLI producing 1000+ BELs when input
2702
+ // was injected before it was ready.
2703
+ const meaningfulOutput = newOutput.replace(/[\x00-\x1f]/g, ''); // Strip control chars
2704
+ if (meaningfulOutput.length > 100) {
2705
+ this.log(` Activity verified: significant output growth (${meaningfulOutput.length} meaningful chars)`);
2706
+ return true;
2707
+ }
2708
+ }
2709
+
2710
+ await sleep(POLL_INTERVAL_MS);
2711
+ }
2712
+
2713
+ // No activity detected within timeout
2714
+ this.log(` No activity detected within ${TIMEOUT_MS}ms`);
2715
+ return false;
2716
+ }
2717
+
2718
+ /**
2719
+ * Inject a task using the socket-based injection system with activity verification.
2576
2720
  * This is the preferred method for spawned agent task delivery.
2577
2721
  *
2722
+ * After socket confirms delivery, verifies the CLI shows activity (task received,
2723
+ * thinking indicators, or tool execution). Retries if no activity is detected.
2724
+ *
2578
2725
  * @param task The task text to inject
2579
2726
  * @param from The sender name (default: "spawner")
2580
- * @returns Promise resolving to true if injection succeeded, false otherwise
2727
+ * @returns Promise resolving to true if task was delivered AND activity verified, false otherwise
2581
2728
  */
2582
2729
  async injectTask(task: string, from = 'spawner'): Promise<boolean> {
2730
+ const { MAX_RETRIES, RETRY_DELAY_MS } = ACTIVITY_VERIFICATION;
2731
+
2732
+ // Claude Code's TUI needs a stabilization delay after displaying the welcome screen
2733
+ // before it's ready to accept stdin input. Without this, input may be silently dropped
2734
+ // even though the CLI appears "idle" and doesn't reject with BELs.
2735
+ // 1500ms is based on observed behavior - Claude Code typically needs 1-1.5s after
2736
+ // its TUI renders before input handlers are fully initialized.
2737
+ const STABILIZATION_DELAY_MS = 1500;
2738
+ this.log(` Waiting ${STABILIZATION_DELAY_MS}ms for CLI stabilization before task injection`);
2739
+ await sleep(STABILIZATION_DELAY_MS);
2740
+
2741
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
2742
+ if (attempt > 0) {
2743
+ this.log(` Retry ${attempt}/${MAX_RETRIES} - waiting ${RETRY_DELAY_MS}ms before retry`);
2744
+ await sleep(RETRY_DELAY_MS);
2745
+ }
2746
+
2747
+ // Capture output buffer state before injection
2748
+ const outputBefore = this.outputBuffer;
2749
+
2750
+ // Attempt delivery via socket
2751
+ const delivered = await this.performTaskInjection(task, from);
2752
+ if (!delivered) {
2753
+ this.logError(` Task delivery failed on attempt ${attempt + 1}`);
2754
+ continue; // Retry
2755
+ }
2756
+
2757
+ // Verify CLI shows activity (task received, thinking, or tool execution)
2758
+ const activityVerified = await this.verifyActivityAfterInjection(outputBefore);
2759
+ if (activityVerified) {
2760
+ this.log(` Task delivered and activity verified successfully`);
2761
+ return true;
2762
+ }
2763
+
2764
+ // Delivery succeeded but no activity - CLI might not have been ready
2765
+ this.logError(` Task delivered but no activity detected (attempt ${attempt + 1})`);
2766
+ }
2767
+
2768
+ this.logError(` Task injection failed after ${MAX_RETRIES + 1} attempts - no CLI activity detected`);
2769
+ return false;
2770
+ }
2771
+
2772
+ /**
2773
+ * Perform a single task injection attempt (without retry logic).
2774
+ * @returns true if the injection was sent successfully, false otherwise
2775
+ */
2776
+ private async performTaskInjection(task: string, from: string): Promise<boolean> {
2583
2777
  if (!this.socket || !this.socketConnected) {
2584
2778
  this.log(` Socket not connected for task injection, falling back to stdin write`);
2585
- // Fallback to direct write if socket not available
2586
2779
  try {
2587
2780
  await this.write(task + '\n');
2588
2781
  return true;
@@ -2606,7 +2799,7 @@ Then output: \`->relay-file:spawn\`
2606
2799
  priority: 0, // High priority for initial task
2607
2800
  };
2608
2801
 
2609
- // Send with timeout and verification
2802
+ // Send with timeout and get socket confirmation
2610
2803
  return new Promise<boolean>((resolve) => {
2611
2804
  const timeout = setTimeout(() => {
2612
2805
  this.logError(` Task inject timeout for ${shortId} after 30s`);
@@ -24,7 +24,9 @@ Real-time agent-to-agent messaging via file-based protocol.
24
24
 
25
25
  ## Sending Messages
26
26
 
27
- Write a file to your outbox, then output the trigger:
27
+ **TWO STEPS REQUIRED:**
28
+
29
+ **Step 1:** EXECUTE this bash command (you MUST run it via your Bash/shell tool, not just display it):
28
30
 
29
31
  ```bash
30
32
  cat > $AGENT_RELAY_OUTBOX/msg << 'EOF'
@@ -34,7 +36,12 @@ Your message here.
34
36
  EOF
35
37
  ```
36
38
 
37
- IMPORTANT: Output the trigger `->relay-file:msg` directly in your response text (not via echo in bash). The trigger must appear in your actual output, not just in command output.
39
+ **Step 2:** Output the trigger `->relay-file:msg` directly in your response text (after the bash command succeeds).
40
+
41
+ ⚠️ **CRITICAL**: You must EXECUTE the bash command above - do NOT just display it as a code block. The file must actually be created on disk. Then output the trigger as plain text.
42
+
43
+ ✅ CORRECT: Run Bash tool → then output `->relay-file:msg`
44
+ ❌ WRONG: Display the bash code without executing it
38
45
 
39
46
  > **Note**: `$AGENT_RELAY_OUTBOX` is automatically set by agent-relay when spawning agents. Data is stored in `.agent-relay/` within your project directory.
40
47
 
@@ -105,6 +112,7 @@ EOF
105
112
 
106
113
  **IMPORTANT**: The filename is always `spawn` (not `spawn-agentname`) and the trigger is always `->relay-file:spawn`. Spawn agents one at a time sequentially.
107
114
 
115
+ **Step 1:** EXECUTE this bash command (run it, don't just display it):
108
116
  ```bash
109
117
  # Spawn
110
118
  cat > $AGENT_RELAY_OUTBOX/spawn << 'EOF'
@@ -115,8 +123,9 @@ CLI: claude
115
123
  Task description here.
116
124
  EOF
117
125
  ```
118
- Then: `->relay-file:spawn`
126
+ **Step 2:** Output: `->relay-file:spawn`
119
127
 
128
+ **Step 1:** EXECUTE this bash command (run it, don't just display it):
120
129
  ```bash
121
130
  # Release
122
131
  cat > $AGENT_RELAY_OUTBOX/release << 'EOF'
@@ -124,7 +133,7 @@ KIND: release
124
133
  NAME: WorkerName
125
134
  EOF
126
135
  ```
127
- Then: `->relay-file:release`
136
+ **Step 2:** Output: `->relay-file:release`
128
137
 
129
138
  ## When You Are Spawned
130
139
 
@@ -137,7 +146,10 @@ If you were spawned by another agent:
137
146
  ```bash
138
147
  # Check your spawner
139
148
  echo "I was spawned by: $AGENT_RELAY_SPAWNER"
149
+ ```
140
150
 
151
+ **Step 1:** EXECUTE this bash command:
152
+ ```bash
141
153
  # Reply to your spawner
142
154
  cat > $AGENT_RELAY_OUTBOX/msg << 'EOF'
143
155
  TO: $AGENT_RELAY_SPAWNER
@@ -145,7 +157,7 @@ TO: $AGENT_RELAY_SPAWNER
145
157
  ACK: Starting on the task.
146
158
  EOF
147
159
  ```
148
- Then: `->relay-file:msg`
160
+ **Step 2:** Output: `->relay-file:msg`
149
161
 
150
162
  ## Receiving Messages
151
163