agent-relay 0.1.0

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/CHANGELOG.md +11 -0
  2. package/LICENSE +22 -0
  3. package/PROTOCOL.md +319 -0
  4. package/README.md +791 -0
  5. package/dist/cli/index.d.ts +7 -0
  6. package/dist/cli/index.d.ts.map +1 -0
  7. package/dist/cli/index.js +1591 -0
  8. package/dist/cli/index.js.map +1 -0
  9. package/dist/daemon/connection.d.ts +60 -0
  10. package/dist/daemon/connection.d.ts.map +1 -0
  11. package/dist/daemon/connection.js +245 -0
  12. package/dist/daemon/connection.js.map +1 -0
  13. package/dist/daemon/index.d.ts +4 -0
  14. package/dist/daemon/index.d.ts.map +1 -0
  15. package/dist/daemon/index.js +4 -0
  16. package/dist/daemon/index.js.map +1 -0
  17. package/dist/daemon/router.d.ts +72 -0
  18. package/dist/daemon/router.d.ts.map +1 -0
  19. package/dist/daemon/router.js +183 -0
  20. package/dist/daemon/router.js.map +1 -0
  21. package/dist/daemon/server.d.ts +52 -0
  22. package/dist/daemon/server.d.ts.map +1 -0
  23. package/dist/daemon/server.js +186 -0
  24. package/dist/daemon/server.js.map +1 -0
  25. package/dist/dashboard/public/index.html +690 -0
  26. package/dist/dashboard/server.d.ts +2 -0
  27. package/dist/dashboard/server.d.ts.map +1 -0
  28. package/dist/dashboard/server.js +220 -0
  29. package/dist/dashboard/server.js.map +1 -0
  30. package/dist/games/index.d.ts +2 -0
  31. package/dist/games/index.d.ts.map +1 -0
  32. package/dist/games/index.js +2 -0
  33. package/dist/games/index.js.map +1 -0
  34. package/dist/games/tictactoe.d.ts +24 -0
  35. package/dist/games/tictactoe.d.ts.map +1 -0
  36. package/dist/games/tictactoe.js +160 -0
  37. package/dist/games/tictactoe.js.map +1 -0
  38. package/dist/hooks/inbox-check/hook.d.ts +28 -0
  39. package/dist/hooks/inbox-check/hook.d.ts.map +1 -0
  40. package/dist/hooks/inbox-check/hook.js +97 -0
  41. package/dist/hooks/inbox-check/hook.js.map +1 -0
  42. package/dist/hooks/inbox-check/index.d.ts +8 -0
  43. package/dist/hooks/inbox-check/index.d.ts.map +1 -0
  44. package/dist/hooks/inbox-check/index.js +8 -0
  45. package/dist/hooks/inbox-check/index.js.map +1 -0
  46. package/dist/hooks/inbox-check/types.d.ts +31 -0
  47. package/dist/hooks/inbox-check/types.d.ts.map +1 -0
  48. package/dist/hooks/inbox-check/types.js +5 -0
  49. package/dist/hooks/inbox-check/types.js.map +1 -0
  50. package/dist/hooks/inbox-check/utils.d.ts +44 -0
  51. package/dist/hooks/inbox-check/utils.d.ts.map +1 -0
  52. package/dist/hooks/inbox-check/utils.js +107 -0
  53. package/dist/hooks/inbox-check/utils.js.map +1 -0
  54. package/dist/index.d.ts +10 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +10 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/protocol/framing.d.ts +32 -0
  59. package/dist/protocol/framing.d.ts.map +1 -0
  60. package/dist/protocol/framing.js +71 -0
  61. package/dist/protocol/framing.js.map +1 -0
  62. package/dist/protocol/index.d.ts +3 -0
  63. package/dist/protocol/index.d.ts.map +1 -0
  64. package/dist/protocol/index.js +3 -0
  65. package/dist/protocol/index.js.map +1 -0
  66. package/dist/protocol/types.d.ts +104 -0
  67. package/dist/protocol/types.d.ts.map +1 -0
  68. package/dist/protocol/types.js +6 -0
  69. package/dist/protocol/types.js.map +1 -0
  70. package/dist/state/agent-state.d.ts +40 -0
  71. package/dist/state/agent-state.d.ts.map +1 -0
  72. package/dist/state/agent-state.js +120 -0
  73. package/dist/state/agent-state.js.map +1 -0
  74. package/dist/storage/adapter.d.ts +29 -0
  75. package/dist/storage/adapter.d.ts.map +1 -0
  76. package/dist/storage/adapter.js +2 -0
  77. package/dist/storage/adapter.js.map +1 -0
  78. package/dist/storage/sqlite-adapter.d.ts +15 -0
  79. package/dist/storage/sqlite-adapter.d.ts.map +1 -0
  80. package/dist/storage/sqlite-adapter.js +116 -0
  81. package/dist/storage/sqlite-adapter.js.map +1 -0
  82. package/dist/supervisor/inbox.d.ts +38 -0
  83. package/dist/supervisor/inbox.d.ts.map +1 -0
  84. package/dist/supervisor/inbox.js +162 -0
  85. package/dist/supervisor/inbox.js.map +1 -0
  86. package/dist/supervisor/index.d.ts +10 -0
  87. package/dist/supervisor/index.d.ts.map +1 -0
  88. package/dist/supervisor/index.js +10 -0
  89. package/dist/supervisor/index.js.map +1 -0
  90. package/dist/supervisor/spawner.d.ts +54 -0
  91. package/dist/supervisor/spawner.d.ts.map +1 -0
  92. package/dist/supervisor/spawner.js +282 -0
  93. package/dist/supervisor/spawner.js.map +1 -0
  94. package/dist/supervisor/state.d.ts +132 -0
  95. package/dist/supervisor/state.d.ts.map +1 -0
  96. package/dist/supervisor/state.js +465 -0
  97. package/dist/supervisor/state.js.map +1 -0
  98. package/dist/supervisor/supervisor.d.ts +67 -0
  99. package/dist/supervisor/supervisor.d.ts.map +1 -0
  100. package/dist/supervisor/supervisor.js +263 -0
  101. package/dist/supervisor/supervisor.js.map +1 -0
  102. package/dist/supervisor/types.d.ts +139 -0
  103. package/dist/supervisor/types.d.ts.map +1 -0
  104. package/dist/supervisor/types.js +12 -0
  105. package/dist/supervisor/types.js.map +1 -0
  106. package/dist/utils/index.d.ts +2 -0
  107. package/dist/utils/index.d.ts.map +1 -0
  108. package/dist/utils/index.js +2 -0
  109. package/dist/utils/index.js.map +1 -0
  110. package/dist/utils/name-generator.d.ts +17 -0
  111. package/dist/utils/name-generator.d.ts.map +1 -0
  112. package/dist/utils/name-generator.js +52 -0
  113. package/dist/utils/name-generator.js.map +1 -0
  114. package/dist/webhook/spawner.d.ts +79 -0
  115. package/dist/webhook/spawner.d.ts.map +1 -0
  116. package/dist/webhook/spawner.js +288 -0
  117. package/dist/webhook/spawner.js.map +1 -0
  118. package/dist/wrapper/client.d.ts +72 -0
  119. package/dist/wrapper/client.d.ts.map +1 -0
  120. package/dist/wrapper/client.js +306 -0
  121. package/dist/wrapper/client.js.map +1 -0
  122. package/dist/wrapper/inbox.d.ts +37 -0
  123. package/dist/wrapper/inbox.d.ts.map +1 -0
  124. package/dist/wrapper/inbox.js +73 -0
  125. package/dist/wrapper/inbox.js.map +1 -0
  126. package/dist/wrapper/index.d.ts +4 -0
  127. package/dist/wrapper/index.d.ts.map +1 -0
  128. package/dist/wrapper/index.js +7 -0
  129. package/dist/wrapper/index.js.map +1 -0
  130. package/dist/wrapper/parser.d.ts +94 -0
  131. package/dist/wrapper/parser.d.ts.map +1 -0
  132. package/dist/wrapper/parser.js +360 -0
  133. package/dist/wrapper/parser.js.map +1 -0
  134. package/dist/wrapper/pty-wrapper.d.ts +125 -0
  135. package/dist/wrapper/pty-wrapper.d.ts.map +1 -0
  136. package/dist/wrapper/pty-wrapper.js +494 -0
  137. package/dist/wrapper/pty-wrapper.js.map +1 -0
  138. package/dist/wrapper/tmux-wrapper.d.ts +131 -0
  139. package/dist/wrapper/tmux-wrapper.d.ts.map +1 -0
  140. package/dist/wrapper/tmux-wrapper.js +427 -0
  141. package/dist/wrapper/tmux-wrapper.js.map +1 -0
  142. package/install.sh +69 -0
  143. package/package.json +82 -0
@@ -0,0 +1,220 @@
1
+ import express from 'express';
2
+ import { WebSocketServer, WebSocket } from 'ws';
3
+ import http from 'http';
4
+ import path from 'path';
5
+ import fs from 'fs';
6
+ import { fileURLToPath } from 'url';
7
+ import { SqliteStorageAdapter } from '../storage/sqlite-adapter.js';
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+ export async function startDashboard(port, dataDir, dbPath) {
11
+ console.log('Starting dashboard...');
12
+ console.log('__dirname:', __dirname);
13
+ const publicDir = path.join(__dirname, 'public');
14
+ console.log('Public dir:', publicDir);
15
+ const storage = dbPath
16
+ ? new SqliteStorageAdapter({ dbPath })
17
+ : undefined;
18
+ process.on('uncaughtException', (err) => {
19
+ console.error('Uncaught Exception:', err);
20
+ });
21
+ process.on('unhandledRejection', (reason, promise) => {
22
+ console.error('Unhandled Rejection at:', promise, 'reason:', reason);
23
+ });
24
+ const app = express();
25
+ const server = http.createServer(app);
26
+ const wss = new WebSocketServer({ server, path: '/ws' });
27
+ if (storage) {
28
+ await storage.init();
29
+ }
30
+ // Serve static files from public directory
31
+ app.use(express.static(publicDir));
32
+ app.use(express.json());
33
+ const getTeamData = () => {
34
+ const teamPath = path.join(dataDir, 'team.json');
35
+ if (!fs.existsSync(teamPath))
36
+ return null;
37
+ try {
38
+ return JSON.parse(fs.readFileSync(teamPath, 'utf-8'));
39
+ }
40
+ catch (e) {
41
+ console.error('Failed to read team.json', e);
42
+ return null;
43
+ }
44
+ };
45
+ const parseInbox = (agentName) => {
46
+ const inboxPath = path.join(dataDir, agentName, 'inbox.md');
47
+ if (!fs.existsSync(inboxPath))
48
+ return [];
49
+ try {
50
+ const content = fs.readFileSync(inboxPath, 'utf-8');
51
+ const messages = [];
52
+ // Split by "## Message from "
53
+ const parts = content.split('## Message from ');
54
+ parts.forEach((part, index) => {
55
+ if (!part.trim())
56
+ return;
57
+ const firstLineEnd = part.indexOf('\n');
58
+ if (firstLineEnd === -1)
59
+ return;
60
+ const header = part.substring(0, firstLineEnd).trim(); // "Sender | Timestamp" or just "Sender"
61
+ const body = part.substring(firstLineEnd).trim();
62
+ // Handle potential " | " in header
63
+ let sender = header;
64
+ let timestamp = new Date().toISOString();
65
+ if (header.includes('|')) {
66
+ const split = header.split('|');
67
+ sender = split[0].trim();
68
+ timestamp = split.slice(1).join('|').trim();
69
+ }
70
+ messages.push({
71
+ from: sender,
72
+ to: agentName,
73
+ content: body,
74
+ timestamp: timestamp,
75
+ id: `${agentName}-${index}-${Date.now()}`
76
+ });
77
+ });
78
+ return messages;
79
+ }
80
+ catch (e) {
81
+ console.error(`Failed to read inbox for ${agentName}`, e);
82
+ return [];
83
+ }
84
+ };
85
+ const mapStoredMessages = (rows) => rows
86
+ .map((row) => ({
87
+ from: row.from,
88
+ to: row.to,
89
+ content: row.body,
90
+ timestamp: new Date(row.ts).toISOString(),
91
+ id: row.id,
92
+ }));
93
+ const getMessages = async (agents) => {
94
+ if (storage) {
95
+ const rows = await storage.getMessages({ limit: 500, order: 'desc' });
96
+ // Dashboard expects oldest first
97
+ return mapStoredMessages(rows).reverse();
98
+ }
99
+ // Fallback to file-based inbox parsing
100
+ let allMessages = [];
101
+ agents.forEach((a) => {
102
+ const msgs = parseInbox(a.name);
103
+ allMessages = [...allMessages, ...msgs];
104
+ });
105
+ return allMessages;
106
+ };
107
+ const getAllData = async () => {
108
+ const team = getTeamData();
109
+ if (!team)
110
+ return { agents: [], messages: [], activity: [] };
111
+ const agentsMap = new Map();
112
+ const allMessages = await getMessages(team.agents);
113
+ // Initialize agents from config
114
+ team.agents.forEach((a) => {
115
+ agentsMap.set(a.name, {
116
+ name: a.name,
117
+ role: a.role,
118
+ cli: a.cli,
119
+ messageCount: 0,
120
+ status: 'Idle'
121
+ });
122
+ });
123
+ // Update inbox counts if fallback mode; if storage, count messages addressed to agent
124
+ if (storage) {
125
+ for (const msg of allMessages) {
126
+ const agent = agentsMap.get(msg.to);
127
+ if (agent) {
128
+ agent.messageCount = (agent.messageCount ?? 0) + 1;
129
+ }
130
+ }
131
+ }
132
+ else {
133
+ // Sort by timestamp
134
+ allMessages.sort((a, b) => {
135
+ return new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime();
136
+ });
137
+ }
138
+ // Derive status from messages sent BY agents
139
+ // We scan all messages; if M is from A, we check if it is a STATUS message
140
+ allMessages.forEach(m => {
141
+ const agent = agentsMap.get(m.from);
142
+ if (agent) {
143
+ agent.lastActive = m.timestamp;
144
+ if (m.content.startsWith('STATUS:')) {
145
+ agent.status = m.content.substring(7).trim(); // remove "STATUS:"
146
+ }
147
+ }
148
+ });
149
+ return {
150
+ agents: Array.from(agentsMap.values()),
151
+ messages: allMessages,
152
+ activity: allMessages // For now, activity log is just the message log
153
+ };
154
+ };
155
+ const broadcastData = async () => {
156
+ const data = await getAllData();
157
+ const payload = JSON.stringify(data);
158
+ wss.clients.forEach(client => {
159
+ if (client.readyState === WebSocket.OPEN) {
160
+ client.send(payload);
161
+ }
162
+ });
163
+ };
164
+ app.get('/api/data', (req, res) => {
165
+ getAllData().then((data) => res.json(data)).catch((err) => {
166
+ console.error('Failed to fetch dashboard data', err);
167
+ res.status(500).json({ error: 'Failed to load data' });
168
+ });
169
+ });
170
+ // Watch for changes
171
+ if (storage) {
172
+ setInterval(() => {
173
+ broadcastData().catch((err) => console.error('Broadcast failed', err));
174
+ }, 1000);
175
+ }
176
+ else {
177
+ let fsWait = null;
178
+ try {
179
+ if (fs.existsSync(dataDir)) {
180
+ console.log(`Watching ${dataDir} for changes...`);
181
+ fs.watch(dataDir, { recursive: true }, (eventType, filename) => {
182
+ if (filename && (filename.endsWith('inbox.md') || filename.endsWith('team.json'))) {
183
+ // Debounce
184
+ if (fsWait)
185
+ return;
186
+ fsWait = setTimeout(() => {
187
+ fsWait = null;
188
+ broadcastData();
189
+ }, 100);
190
+ }
191
+ });
192
+ }
193
+ else {
194
+ console.warn(`Data directory ${dataDir} does not exist yet.`);
195
+ }
196
+ }
197
+ catch (e) {
198
+ console.error('Watch failed:', e);
199
+ }
200
+ }
201
+ return new Promise((resolve, reject) => {
202
+ try {
203
+ server.listen(port, () => {
204
+ console.log(`Dashboard running at http://localhost:${port}`);
205
+ console.log(`Monitoring: ${dataDir}`);
206
+ // We do NOT resolve here to keep the process alive
207
+ // But we must resolve if the user sends SIGINT?
208
+ // The main process handles SIGINT.
209
+ });
210
+ server.on('error', (err) => {
211
+ console.error('Server error:', err);
212
+ reject(err);
213
+ });
214
+ }
215
+ catch (e) {
216
+ reject(e);
217
+ }
218
+ });
219
+ }
220
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from './tictactoe.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/games/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './tictactoe.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/games/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Tic-Tac-Toe Setup Helpers
3
+ *
4
+ * This is a lightweight “game module” focused on generating instructions and
5
+ * directory setup for manual or scripted agent runs.
6
+ *
7
+ * It intentionally does NOT spawn agent processes (that stays in CLI tooling),
8
+ * and it uses the inbox CLI commands (`inbox-poll`, `inbox-write`) so it works
9
+ * with any agent runtime.
10
+ */
11
+ export interface TicTacToeSetupOptions {
12
+ dataDir: string;
13
+ playerX?: string;
14
+ playerO?: string;
15
+ }
16
+ export interface TicTacToeSetupResult {
17
+ dataDir: string;
18
+ playerX: string;
19
+ playerO: string;
20
+ instructionsXPath: string;
21
+ instructionsOPath: string;
22
+ }
23
+ export declare function setupTicTacToe(options: TicTacToeSetupOptions): TicTacToeSetupResult;
24
+ //# sourceMappingURL=tictactoe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tictactoe.d.ts","sourceRoot":"","sources":["../../src/games/tictactoe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAwID,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,oBAAoB,CAmBnF"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Tic-Tac-Toe Setup Helpers
3
+ *
4
+ * This is a lightweight “game module” focused on generating instructions and
5
+ * directory setup for manual or scripted agent runs.
6
+ *
7
+ * It intentionally does NOT spawn agent processes (that stays in CLI tooling),
8
+ * and it uses the inbox CLI commands (`inbox-poll`, `inbox-write`) so it works
9
+ * with any agent runtime.
10
+ */
11
+ import fs from 'node:fs';
12
+ import path from 'node:path';
13
+ function instructionsForPlayerX(dataDir, playerX, playerO) {
14
+ return `# Tic-Tac-Toe Autonomous Game Protocol
15
+
16
+ You are **${playerX}** (X). You play FIRST. Your opponent is **${playerO}** (O).
17
+
18
+ ## Board Positions
19
+ \`\`\`
20
+ 1 | 2 | 3
21
+ -----------
22
+ 4 | 5 | 6
23
+ -----------
24
+ 7 | 8 | 9
25
+ \`\`\`
26
+
27
+ ## Commands Available
28
+ You have these \`agent-relay\` commands to communicate:
29
+
30
+ **Wait for opponent's message (blocking):**
31
+ \`\`\`bash
32
+ agent-relay inbox-poll -n ${playerX} -d ${dataDir} --clear
33
+ \`\`\`
34
+
35
+ **Send a move to opponent:**
36
+ \`\`\`bash
37
+ agent-relay inbox-write -t ${playerO} -f ${playerX} -m "MOVE: X at position N" -d ${dataDir}
38
+ \`\`\`
39
+
40
+ ## PROTOCOL (follow EXACTLY)
41
+
42
+ ### Since you're X, you go FIRST:
43
+ 1. Make your first move to position 5 (center)
44
+ 2. Send it to opponent:
45
+ \`\`\`bash
46
+ agent-relay inbox-write -t ${playerO} -f ${playerX} -m "MOVE: X at position 5" -d ${dataDir}
47
+ \`\`\`
48
+
49
+ ### Then enter the game loop:
50
+ 1. **WAIT** for opponent's response (this will block until they respond):
51
+ \`\`\`bash
52
+ agent-relay inbox-poll -n ${playerX} -d ${dataDir} --clear
53
+ \`\`\`
54
+
55
+ 2. **UPDATE** your mental board state with opponent's move
56
+
57
+ 3. **CHECK** for win/draw. If game over, send result and announce:
58
+ \`\`\`bash
59
+ agent-relay inbox-write -t ${playerO} -f ${playerX} -m "GAME OVER: X wins!" -d ${dataDir}
60
+ \`\`\`
61
+
62
+ 4. **MAKE** your next move and send it:
63
+ \`\`\`bash
64
+ agent-relay inbox-write -t ${playerO} -f ${playerX} -m "MOVE: X at position N" -d ${dataDir}
65
+ \`\`\`
66
+
67
+ 5. **REPEAT** from step 1 until game over
68
+
69
+ ## Rules
70
+ - Valid moves: positions 1-9 that are empty
71
+ - Win: 3 in a row (horizontal, vertical, or diagonal)
72
+ - Draw: all 9 positions filled with no winner
73
+
74
+ ## CRITICAL
75
+ - NEVER stop mid-game
76
+ - ALWAYS use the inbox-poll command to wait (it blocks until opponent responds)
77
+ - Keep track of the board state
78
+ - Announce result when game ends
79
+
80
+ ## START NOW
81
+ Make your FIRST MOVE to position 5, then wait for opponent's response.
82
+ `;
83
+ }
84
+ function instructionsForPlayerO(dataDir, playerX, playerO) {
85
+ return `# Tic-Tac-Toe Autonomous Game Protocol
86
+
87
+ You are **${playerO}** (O). ${playerX} plays first, so you WAIT for their move.
88
+
89
+ ## Board Positions
90
+ \`\`\`
91
+ 1 | 2 | 3
92
+ -----------
93
+ 4 | 5 | 6
94
+ -----------
95
+ 7 | 8 | 9
96
+ \`\`\`
97
+
98
+ ## Commands Available
99
+ You have these \`agent-relay\` commands to communicate:
100
+
101
+ **Wait for opponent's message (blocking):**
102
+ \`\`\`bash
103
+ agent-relay inbox-poll -n ${playerO} -d ${dataDir} --clear
104
+ \`\`\`
105
+
106
+ **Send a move to opponent:**
107
+ \`\`\`bash
108
+ agent-relay inbox-write -t ${playerX} -f ${playerO} -m "MOVE: O at position N" -d ${dataDir}
109
+ \`\`\`
110
+
111
+ ## PROTOCOL (follow EXACTLY)
112
+
113
+ ### Since you're O, you go SECOND. Start by waiting:
114
+ 1. **WAIT** for opponent's first move (this will block until they move):
115
+ \`\`\`bash
116
+ agent-relay inbox-poll -n ${playerO} -d ${dataDir} --clear
117
+ \`\`\`
118
+
119
+ 2. **UPDATE** your mental board state with opponent's move
120
+
121
+ 3. **CHECK** for win/draw. If game over, announce result.
122
+
123
+ 4. **MAKE** your response move and send it:
124
+ \`\`\`bash
125
+ agent-relay inbox-write -t ${playerX} -f ${playerO} -m "MOVE: O at position N" -d ${dataDir}
126
+ \`\`\`
127
+
128
+ 5. **WAIT** for opponent's next move (back to step 1)
129
+
130
+ ## Rules
131
+ - Valid moves: positions 1-9 that are empty
132
+ - Win: 3 in a row (horizontal, vertical, or diagonal)
133
+ - Draw: all 9 positions filled with no winner
134
+
135
+ ## CRITICAL
136
+ - NEVER stop mid-game
137
+ - ALWAYS use the inbox-poll command to wait (it blocks until opponent responds)
138
+ - Keep track of the board state
139
+ - Announce result when game ends
140
+
141
+ ## START NOW
142
+ Run the inbox-poll command to WAIT for ${playerX}'s first move.
143
+ `;
144
+ }
145
+ export function setupTicTacToe(options) {
146
+ const dataDir = options.dataDir;
147
+ const playerX = options.playerX ?? 'PlayerX';
148
+ const playerO = options.playerO ?? 'PlayerO';
149
+ fs.mkdirSync(path.join(dataDir, playerX), { recursive: true });
150
+ fs.mkdirSync(path.join(dataDir, playerO), { recursive: true });
151
+ // Clear inboxes
152
+ fs.writeFileSync(path.join(dataDir, playerX, 'inbox.md'), '', 'utf-8');
153
+ fs.writeFileSync(path.join(dataDir, playerO, 'inbox.md'), '', 'utf-8');
154
+ const instructionsXPath = path.join(dataDir, playerX, 'GAME_INSTRUCTIONS.md');
155
+ const instructionsOPath = path.join(dataDir, playerO, 'GAME_INSTRUCTIONS.md');
156
+ fs.writeFileSync(instructionsXPath, instructionsForPlayerX(dataDir, playerX, playerO), 'utf-8');
157
+ fs.writeFileSync(instructionsOPath, instructionsForPlayerO(dataDir, playerX, playerO), 'utf-8');
158
+ return { dataDir, playerX, playerO, instructionsXPath, instructionsOPath };
159
+ }
160
+ //# sourceMappingURL=tictactoe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tictactoe.js","sourceRoot":"","sources":["../../src/games/tictactoe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAgB7B,SAAS,sBAAsB,CAAC,OAAe,EAAE,OAAe,EAAE,OAAe;IAC/E,OAAO;;YAEG,OAAO,8CAA8C,OAAO;;;;;;;;;;;;;;;;4BAgB5C,OAAO,OAAO,OAAO;;;;;6BAKpB,OAAO,OAAO,OAAO,kCAAkC,OAAO;;;;;;;;;gCAS3D,OAAO,OAAO,OAAO,kCAAkC,OAAO;;;;;;+BAM/D,OAAO,OAAO,OAAO;;;;;;;gCAOpB,OAAO,OAAO,OAAO,+BAA+B,OAAO;;;;;gCAK3D,OAAO,OAAO,OAAO,kCAAkC,OAAO;;;;;;;;;;;;;;;;;;CAkB7F,CAAC;AACF,CAAC;AAED,SAAS,sBAAsB,CAAC,OAAe,EAAE,OAAe,EAAE,OAAe;IAC/E,OAAO;;YAEG,OAAO,WAAW,OAAO;;;;;;;;;;;;;;;;4BAgBT,OAAO,OAAO,OAAO;;;;;6BAKpB,OAAO,OAAO,OAAO,kCAAkC,OAAO;;;;;;;;+BAQ5D,OAAO,OAAO,OAAO;;;;;;;;;gCASpB,OAAO,OAAO,OAAO,kCAAkC,OAAO;;;;;;;;;;;;;;;;;yCAiBrD,OAAO;CAC/C,CAAC;AACF,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAA8B;IAC3D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAChC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,SAAS,CAAC;IAC7C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,SAAS,CAAC;IAE7C,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE/D,gBAAgB;IAChB,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;IACvE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;IAEvE,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,sBAAsB,CAAC,CAAC;IAC9E,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,sBAAsB,CAAC,CAAC;IAE9E,EAAE,CAAC,aAAa,CAAC,iBAAiB,EAAE,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;IAChG,EAAE,CAAC,aAAa,CAAC,iBAAiB,EAAE,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;IAEhG,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,CAAC;AAC7E,CAAC"}
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Agent Relay Inbox Check Hook
4
+ *
5
+ * A Claude Code Stop hook that checks for unread messages in the agent-relay inbox.
6
+ * When messages are present, it blocks Claude from stopping and instructs it to
7
+ * read and respond to the messages.
8
+ *
9
+ * This enables autonomous agent-to-agent communication without human intervention.
10
+ *
11
+ * Usage in .claude/settings.json:
12
+ * {
13
+ * "hooks": {
14
+ * "Stop": [{
15
+ * "hooks": [{
16
+ * "type": "command",
17
+ * "command": "node /path/to/agent-relay/dist/hooks/inbox-check/hook.js"
18
+ * }]
19
+ * }]
20
+ * }
21
+ * }
22
+ *
23
+ * Environment Variables:
24
+ * - AGENT_RELAY_NAME: The agent's name (set by agent-relay wrapper)
25
+ * - AGENT_RELAY_INBOX_DIR: Custom inbox directory (default: /tmp/agent-relay)
26
+ */
27
+ export {};
28
+ //# sourceMappingURL=hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../../src/hooks/inbox-check/hook.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG"}
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Agent Relay Inbox Check Hook
4
+ *
5
+ * A Claude Code Stop hook that checks for unread messages in the agent-relay inbox.
6
+ * When messages are present, it blocks Claude from stopping and instructs it to
7
+ * read and respond to the messages.
8
+ *
9
+ * This enables autonomous agent-to-agent communication without human intervention.
10
+ *
11
+ * Usage in .claude/settings.json:
12
+ * {
13
+ * "hooks": {
14
+ * "Stop": [{
15
+ * "hooks": [{
16
+ * "type": "command",
17
+ * "command": "node /path/to/agent-relay/dist/hooks/inbox-check/hook.js"
18
+ * }]
19
+ * }]
20
+ * }
21
+ * }
22
+ *
23
+ * Environment Variables:
24
+ * - AGENT_RELAY_NAME: The agent's name (set by agent-relay wrapper)
25
+ * - AGENT_RELAY_INBOX_DIR: Custom inbox directory (default: /tmp/agent-relay)
26
+ */
27
+ import { readFileSync } from 'node:fs';
28
+ import { DEFAULT_INBOX_DIR, getAgentName, getInboxPath, hasUnreadMessages, countMessages, buildBlockReason } from './utils.js';
29
+ /**
30
+ * Read hook input from stdin
31
+ */
32
+ function readStdin() {
33
+ try {
34
+ const input = readFileSync(0, 'utf-8');
35
+ return JSON.parse(input);
36
+ }
37
+ catch {
38
+ return {};
39
+ }
40
+ }
41
+ /**
42
+ * Output hook result as JSON
43
+ */
44
+ function outputResult(result) {
45
+ console.log(JSON.stringify(result));
46
+ }
47
+ /**
48
+ * Exit the hook process
49
+ */
50
+ function exitHook(code) {
51
+ process.exit(code);
52
+ }
53
+ /**
54
+ * Main hook execution
55
+ */
56
+ async function main() {
57
+ try {
58
+ // Read stdin (required by Claude Code hook protocol, but we only use env vars)
59
+ readStdin();
60
+ // Get agent name from env
61
+ const agentName = getAgentName();
62
+ // If no agent name configured, allow stop (not in relay mode)
63
+ if (!agentName) {
64
+ outputResult({ decision: 'approve' });
65
+ exitHook(0);
66
+ }
67
+ // Get inbox configuration
68
+ const inboxDir = process.env.AGENT_RELAY_INBOX_DIR || DEFAULT_INBOX_DIR;
69
+ const inboxPath = getInboxPath({ inboxDir, agentName });
70
+ // Check for unread messages
71
+ if (hasUnreadMessages(inboxPath)) {
72
+ const messageCount = countMessages(inboxPath);
73
+ const reason = buildBlockReason(inboxPath, messageCount);
74
+ // Log to stderr for visibility
75
+ console.error(`[agent-relay] Found ${messageCount} unread message(s), blocking stop`);
76
+ // Block stop and provide reason
77
+ outputResult({
78
+ decision: 'block',
79
+ reason
80
+ });
81
+ }
82
+ else {
83
+ // No messages, allow stop
84
+ outputResult({ decision: 'approve' });
85
+ }
86
+ exitHook(0);
87
+ }
88
+ catch (error) {
89
+ // On error, allow stop to avoid blocking user
90
+ console.error('[agent-relay] Hook error:', error);
91
+ outputResult({ decision: 'approve' });
92
+ exitHook(0);
93
+ }
94
+ }
95
+ // Run the hook
96
+ main();
97
+ //# sourceMappingURL=hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.js","sourceRoot":"","sources":["../../../src/hooks/inbox-check/hook.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,SAAS,SAAS;IAChB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QACvC,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,MAAkB;IACtC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AACtC,CAAC;AAED;;GAEG;AACH,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,+EAA+E;QAC/E,SAAS,EAAE,CAAC;QAEZ,0BAA0B;QAC1B,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;QAEjC,8DAA8D;QAC9D,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,YAAY,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;YACtC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACd,CAAC;QAED,0BAA0B;QAC1B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,iBAAiB,CAAC;QACxE,MAAM,SAAS,GAAG,YAAY,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;QAExD,4BAA4B;QAC5B,IAAI,iBAAiB,CAAC,SAAS,CAAC,EAAE,CAAC;YACjC,MAAM,YAAY,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;YAC9C,MAAM,MAAM,GAAG,gBAAgB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;YAEzD,+BAA+B;YAC/B,OAAO,CAAC,KAAK,CAAC,uBAAuB,YAAY,mCAAmC,CAAC,CAAC;YAEtF,gCAAgC;YAChC,YAAY,CAAC;gBACX,QAAQ,EAAE,OAAO;gBACjB,MAAM;aACP,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,0BAA0B;YAC1B,YAAY,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;QACxC,CAAC;QAED,QAAQ,CAAC,CAAC,CAAC,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,8CAA8C;QAC9C,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;QAClD,YAAY,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;QACtC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACd,CAAC;AACH,CAAC;AAED,eAAe;AACf,IAAI,EAAE,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Agent Relay Inbox Check Hook
3
+ *
4
+ * Exports for programmatic use of the hook utilities.
5
+ */
6
+ export * from './types.js';
7
+ export * from './utils.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/inbox-check/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Agent Relay Inbox Check Hook
3
+ *
4
+ * Exports for programmatic use of the hook utilities.
5
+ */
6
+ export * from './types.js';
7
+ export * from './utils.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/hooks/inbox-check/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Types for Agent Relay Inbox Check Hook
3
+ */
4
+ export interface HookInput {
5
+ /** The hook event type */
6
+ hook_event_name?: string;
7
+ /** Working directory */
8
+ workingDirectory?: string;
9
+ /** Session ID */
10
+ session_id?: string;
11
+ /** Stop reason from Claude */
12
+ stop_reason?: string;
13
+ }
14
+ export interface HookOutput {
15
+ /** Decision: "approve" to allow stop, "block" to continue */
16
+ decision: 'approve' | 'block';
17
+ /** Reason for the decision (shown to Claude if blocked) */
18
+ reason?: string;
19
+ }
20
+ export interface InboxMessage {
21
+ from: string;
22
+ timestamp: string;
23
+ body: string;
24
+ }
25
+ export interface InboxConfig {
26
+ /** Base directory for inbox files */
27
+ inboxDir: string;
28
+ /** Agent name (from env var or config) */
29
+ agentName?: string;
30
+ }
31
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/hooks/inbox-check/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,SAAS;IACxB,0BAA0B;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wBAAwB;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,6DAA6D;IAC7D,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC;IAC9B,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Types for Agent Relay Inbox Check Hook
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/hooks/inbox-check/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}