agent-relay 0.1.0 → 1.0.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.
- package/README.md +56 -730
- package/dist/cli/index.d.ts +6 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +109 -1521
- package/dist/cli/index.js.map +1 -1
- package/dist/daemon/server.d.ts +8 -1
- package/dist/daemon/server.d.ts.map +1 -1
- package/dist/daemon/server.js +23 -7
- package/dist/daemon/server.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/storage/adapter.d.ts +43 -0
- package/dist/storage/adapter.d.ts.map +1 -1
- package/dist/storage/adapter.js +105 -1
- package/dist/storage/adapter.js.map +1 -1
- package/dist/storage/sqlite-adapter.d.ts +1 -0
- package/dist/storage/sqlite-adapter.d.ts.map +1 -1
- package/dist/storage/sqlite-adapter.js +26 -0
- package/dist/storage/sqlite-adapter.js.map +1 -1
- package/dist/utils/project-namespace.d.ts +46 -0
- package/dist/utils/project-namespace.d.ts.map +1 -0
- package/dist/utils/project-namespace.js +111 -0
- package/dist/utils/project-namespace.js.map +1 -0
- package/dist/wrapper/client.d.ts +1 -1
- package/dist/wrapper/client.d.ts.map +1 -1
- package/dist/wrapper/client.js +1 -1
- package/dist/wrapper/client.js.map +1 -1
- package/dist/wrapper/index.d.ts +0 -1
- package/dist/wrapper/index.d.ts.map +1 -1
- package/dist/wrapper/index.js +2 -3
- package/dist/wrapper/index.js.map +1 -1
- package/dist/wrapper/tmux-wrapper.d.ts +5 -0
- package/dist/wrapper/tmux-wrapper.d.ts.map +1 -1
- package/dist/wrapper/tmux-wrapper.js +94 -19
- package/dist/wrapper/tmux-wrapper.js.map +1 -1
- package/docs/AGENTS.md +386 -0
- package/docs/CLI-SIMPLIFICATION-COMPLETE.md +48 -0
- package/docs/CONTRIBUTING.md +151 -0
- package/docs/TMUX_IMPLEMENTATION_NOTES.md +364 -0
- package/docs/removable-code-analysis.md +24 -0
- package/install.sh +10 -2
- package/package.json +3 -4
- package/dist/games/index.d.ts +0 -2
- package/dist/games/index.d.ts.map +0 -1
- package/dist/games/index.js +0 -2
- package/dist/games/index.js.map +0 -1
- package/dist/games/tictactoe.d.ts +0 -24
- package/dist/games/tictactoe.d.ts.map +0 -1
- package/dist/games/tictactoe.js +0 -160
- package/dist/games/tictactoe.js.map +0 -1
- package/dist/supervisor/inbox.d.ts +0 -38
- package/dist/supervisor/inbox.d.ts.map +0 -1
- package/dist/supervisor/inbox.js +0 -162
- package/dist/supervisor/inbox.js.map +0 -1
- package/dist/supervisor/index.d.ts +0 -10
- package/dist/supervisor/index.d.ts.map +0 -1
- package/dist/supervisor/index.js +0 -10
- package/dist/supervisor/index.js.map +0 -1
- package/dist/supervisor/spawner.d.ts +0 -54
- package/dist/supervisor/spawner.d.ts.map +0 -1
- package/dist/supervisor/spawner.js +0 -282
- package/dist/supervisor/spawner.js.map +0 -1
- package/dist/supervisor/state.d.ts +0 -132
- package/dist/supervisor/state.d.ts.map +0 -1
- package/dist/supervisor/state.js +0 -465
- package/dist/supervisor/state.js.map +0 -1
- package/dist/supervisor/supervisor.d.ts +0 -67
- package/dist/supervisor/supervisor.d.ts.map +0 -1
- package/dist/supervisor/supervisor.js +0 -263
- package/dist/supervisor/supervisor.js.map +0 -1
- package/dist/supervisor/types.d.ts +0 -139
- package/dist/supervisor/types.d.ts.map +0 -1
- package/dist/supervisor/types.js +0 -12
- package/dist/supervisor/types.js.map +0 -1
- package/dist/webhook/spawner.d.ts +0 -79
- package/dist/webhook/spawner.d.ts.map +0 -1
- package/dist/webhook/spawner.js +0 -288
- package/dist/webhook/spawner.js.map +0 -1
- package/dist/wrapper/pty-wrapper.d.ts +0 -125
- package/dist/wrapper/pty-wrapper.d.ts.map +0 -1
- package/dist/wrapper/pty-wrapper.js +0 -494
- package/dist/wrapper/pty-wrapper.js.map +0 -1
- /package/{CHANGELOG.md → docs/CHANGELOG.md} +0 -0
- /package/{PROTOCOL.md → docs/PROTOCOL.md} +0 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
# tmux Implementation Notes
|
|
2
|
+
|
|
3
|
+
## Previous Failure Analysis
|
|
4
|
+
|
|
5
|
+
### What Failed: "Game never even started"
|
|
6
|
+
|
|
7
|
+
The previous tmux implementation had these issues:
|
|
8
|
+
|
|
9
|
+
1. **Nested PTY Attachment**: Created tmux session, then attached via node-pty
|
|
10
|
+
```typescript
|
|
11
|
+
// Created tmux session
|
|
12
|
+
execSync(`tmux new-session -d -s ${session} '${command}'`);
|
|
13
|
+
// Then attached via PTY
|
|
14
|
+
this.ptyProcess = pty.spawn('tmux', ['attach-session', '-t', session]);
|
|
15
|
+
```
|
|
16
|
+
This creates: Agent CLI → tmux → tmux attach → node-pty
|
|
17
|
+
Double terminal layer causes escape sequence issues.
|
|
18
|
+
|
|
19
|
+
2. **Command Quoting Problem**: Single quotes break complex commands
|
|
20
|
+
```bash
|
|
21
|
+
tmux new-session -d -s session 'claude -p "You're playing..."'
|
|
22
|
+
# The apostrophe in "You're" breaks the quoting
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
3. **Environment Variables Not Passed**: ENV vars set on attach, not on session
|
|
26
|
+
- The agent process inside tmux doesn't see AGENT_RELAY_NAME
|
|
27
|
+
|
|
28
|
+
4. **No Session Ready Wait**: Attached immediately after creating session
|
|
29
|
+
- Session might not be ready, causing race conditions
|
|
30
|
+
|
|
31
|
+
## New Architecture: No Attachment
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
┌──────────────────────────────────┐
|
|
35
|
+
│ tmux session "relay-AgentX" │
|
|
36
|
+
│ (detached, running in background)│
|
|
37
|
+
│ ┌────────────────────────────┐ │
|
|
38
|
+
│ │ agent CLI process │ │
|
|
39
|
+
│ │ (claude, codex, etc) │ │
|
|
40
|
+
│ └────────────────────────────┘ │
|
|
41
|
+
└──────────────────────────────────┘
|
|
42
|
+
↑ ↓
|
|
43
|
+
send-keys capture-pane
|
|
44
|
+
(inject input) (read output)
|
|
45
|
+
│ │
|
|
46
|
+
┌──────────────────────────────────┐
|
|
47
|
+
│ TmuxWrapper (this process) │
|
|
48
|
+
│ - Polls capture-pane @ 100ms │
|
|
49
|
+
│ - Detects new output (diff) │
|
|
50
|
+
│ - Parses @relay: commands │
|
|
51
|
+
│ - Writes to stdout for user │
|
|
52
|
+
│ - Injects messages via send-keys│
|
|
53
|
+
│ - Forwards user stdin to tmux │
|
|
54
|
+
└──────────────────────────────────┘
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Key Differences from Previous Implementation
|
|
58
|
+
|
|
59
|
+
1. **No PTY attachment** - Eliminates double-terminal problem
|
|
60
|
+
2. **capture-pane polling** - Direct access to terminal content
|
|
61
|
+
3. **Proper command escaping** - Use shell escaping, not simple quotes
|
|
62
|
+
4. **Environment via tmux** - Set ENV in tmux session directly
|
|
63
|
+
5. **Session ready wait** - Wait for session to be active before proceeding
|
|
64
|
+
|
|
65
|
+
## Implementation Details
|
|
66
|
+
|
|
67
|
+
### Starting a Session
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Set environment variables IN the tmux session
|
|
71
|
+
tmux new-session -d -s relay-AgentX \
|
|
72
|
+
-x 120 -y 40 \
|
|
73
|
+
"export AGENT_RELAY_NAME=AgentX; exec claude"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Capturing Output
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Capture entire scrollback (not just visible pane)
|
|
80
|
+
tmux capture-pane -t relay-AgentX -p -S -
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Sending Input
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# Use -l for literal string (no escaping needed)
|
|
87
|
+
tmux send-keys -t relay-AgentX -l "Your message here"
|
|
88
|
+
tmux send-keys -t relay-AgentX Enter
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Handling User Input
|
|
92
|
+
|
|
93
|
+
Forward process stdin to tmux session:
|
|
94
|
+
```typescript
|
|
95
|
+
process.stdin.on('data', (data) => {
|
|
96
|
+
execSync(`tmux send-keys -t ${session} -l "${escape(data)}"`);
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Edge Cases to Handle
|
|
101
|
+
|
|
102
|
+
1. **Fast output**: capture-pane might miss fast scrolling
|
|
103
|
+
- Solution: Use -S - to get full scrollback, diff against last capture
|
|
104
|
+
|
|
105
|
+
2. **Binary/escape sequences**: Output might contain control characters
|
|
106
|
+
- Solution: Strip ANSI codes before parsing @relay:
|
|
107
|
+
|
|
108
|
+
3. **Session death**: tmux session might exit
|
|
109
|
+
- Solution: Monitor with `tmux has-session -t session`
|
|
110
|
+
|
|
111
|
+
4. **Multiple messages**: Several @relay: in one capture
|
|
112
|
+
- Solution: Track last processed line/position
|
|
113
|
+
|
|
114
|
+
5. **Stdin race conditions**: User types while we inject
|
|
115
|
+
- Solution: Queue injections, use mutex/lock
|
|
116
|
+
|
|
117
|
+
## Testing Plan
|
|
118
|
+
|
|
119
|
+
1. **Basic session start**: Does claude actually launch in tmux?
|
|
120
|
+
2. **Output capture**: Can we see claude's output?
|
|
121
|
+
3. **Input injection**: Can we send text and get response?
|
|
122
|
+
4. **@relay detection**: Does parser find @relay: commands?
|
|
123
|
+
5. **Full game**: Two agents playing tic-tac-toe
|
|
124
|
+
|
|
125
|
+
## Rollback Plan
|
|
126
|
+
|
|
127
|
+
If this doesn't work, fall back to:
|
|
128
|
+
1. File-based inbox (already implemented)
|
|
129
|
+
2. Hook-based polling (Claude only)
|
|
130
|
+
3. Spawn-per-message (loses context)
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Implementation Complete - Testing Instructions
|
|
135
|
+
|
|
136
|
+
### Build Status: SUCCESS
|
|
137
|
+
|
|
138
|
+
The new TmuxWrapper has been implemented at:
|
|
139
|
+
- `src/wrapper/tmux-wrapper.ts`
|
|
140
|
+
|
|
141
|
+
CLI updated to support `--tmux2` flag.
|
|
142
|
+
|
|
143
|
+
### Quick Test: Basic Session Start
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# Test 1: Does bash work in tmux?
|
|
147
|
+
node dist/cli/index.js wrap --tmux2 -n TestAgent -- bash
|
|
148
|
+
|
|
149
|
+
# You should see:
|
|
150
|
+
# - "Mode: tmux2 (new simplified tmux wrapper)"
|
|
151
|
+
# - Bash prompt in your terminal
|
|
152
|
+
# - Type commands, they should work
|
|
153
|
+
# - Ctrl+C to exit
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Test 2: Simple Echo Command
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Start with echo (should immediately show output and exit)
|
|
160
|
+
node dist/cli/index.js wrap --tmux2 -n TestAgent -- echo "Hello World"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Test 3: Claude CLI (No Relay)
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# Test Claude starts correctly
|
|
167
|
+
node dist/cli/index.js wrap --tmux2 -n PlayerX -- claude
|
|
168
|
+
|
|
169
|
+
# Expected: Claude CLI starts, you can interact with it
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Test 4: With Relay Daemon
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# Terminal 1: Start daemon
|
|
176
|
+
node dist/cli/index.js start -f
|
|
177
|
+
|
|
178
|
+
# Terminal 2: Start PlayerX
|
|
179
|
+
node dist/cli/index.js wrap --tmux2 -n PlayerX -- claude
|
|
180
|
+
|
|
181
|
+
# Terminal 3: Start PlayerO
|
|
182
|
+
node dist/cli/index.js wrap --tmux2 -n PlayerO -- claude
|
|
183
|
+
|
|
184
|
+
# Terminal 4: Send a test message
|
|
185
|
+
node dist/cli/index.js send -f Coordinator -t PlayerX -m "Hello from Coordinator"
|
|
186
|
+
|
|
187
|
+
# Expected: PlayerX terminal should show the message and inject it
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Test 5: Tic-Tac-Toe
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
# Terminal 1: Daemon
|
|
194
|
+
node dist/cli/index.js start -f
|
|
195
|
+
|
|
196
|
+
# Terminal 2: PlayerX
|
|
197
|
+
node dist/cli/index.js wrap --tmux2 -n PlayerX -- claude -p "You are PlayerX playing tic-tac-toe. Use @relay:PlayerO to send moves. Start with your first move."
|
|
198
|
+
|
|
199
|
+
# Terminal 3: PlayerO
|
|
200
|
+
node dist/cli/index.js wrap --tmux2 -n PlayerO -- claude -p "You are PlayerO playing tic-tac-toe. Use @relay:PlayerX to send moves. Wait for PlayerX to start."
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Debugging
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
# Check if tmux session exists
|
|
207
|
+
tmux list-sessions
|
|
208
|
+
|
|
209
|
+
# Attach to session manually to see what's happening
|
|
210
|
+
tmux attach -t relay-PlayerX-<pid>
|
|
211
|
+
|
|
212
|
+
# Kill stuck sessions
|
|
213
|
+
tmux kill-server
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Key Differences from Old tmux Implementation
|
|
217
|
+
|
|
218
|
+
1. **No PTY attachment** - We don't `tmux attach` via node-pty
|
|
219
|
+
2. **capture-pane polling** - Read output by polling, not events
|
|
220
|
+
3. **send-keys -l** - Use literal mode for text injection
|
|
221
|
+
4. **Environment via tmux setenv** - Set ENV vars in tmux session directly
|
|
222
|
+
5. **Simpler architecture** - Less layers = fewer failure points
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Test Results - December 19, 2025
|
|
227
|
+
|
|
228
|
+
### Basic Test: PASSED
|
|
229
|
+
|
|
230
|
+
Message injection into bash session works:
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
[daemon] Agent registered: TestAgent
|
|
234
|
+
[router] Coordinator -> TestAgent: Hello TestAgent!...
|
|
235
|
+
[router] Delivered to TestAgent: success
|
|
236
|
+
[tmux:TestAgent] ← Coordinator: Hello TestAgent!...
|
|
237
|
+
[tmux:TestAgent] Injecting message from Coordinator
|
|
238
|
+
[tmux:TestAgent] Message injected successfully
|
|
239
|
+
bash-3.2$ Relay message from Coordinator: Hello TestAgent!
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
The message was successfully typed into the bash terminal via tmux send-keys.
|
|
243
|
+
|
|
244
|
+
### What Works
|
|
245
|
+
|
|
246
|
+
1. **Session creation** - tmux session starts correctly
|
|
247
|
+
2. **Output capture** - capture-pane polling detects new output
|
|
248
|
+
3. **Relay connection** - Agent connects to daemon
|
|
249
|
+
4. **Message routing** - Messages route between agents
|
|
250
|
+
5. **Message injection** - Text is typed into the terminal via send-keys
|
|
251
|
+
|
|
252
|
+
### Next Steps
|
|
253
|
+
|
|
254
|
+
1. Test with Claude CLI (replace bash with claude)
|
|
255
|
+
2. Test full tic-tac-toe game between two agents
|
|
256
|
+
3. Verify @relay: command parsing works in both directions
|
|
257
|
+
4. Check if multi-round injection remains stable (previous implementations failed after 2-3 rounds)
|
|
258
|
+
|
|
259
|
+
### Critical Question
|
|
260
|
+
|
|
261
|
+
Does this implementation avoid the "fails after a few rounds" problem? The key difference:
|
|
262
|
+
|
|
263
|
+
- **Old approach**: PTY stdin write (`ptyProcess.write()`) - corrupts over time
|
|
264
|
+
- **New approach**: tmux send-keys (`tmux send-keys -l "..."`) - should be more stable
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## December 19, 2025 - Attach-Based Implementation: SUCCESS
|
|
269
|
+
|
|
270
|
+
### Problem with First tmux Attempt
|
|
271
|
+
|
|
272
|
+
The first tmux wrapper tried to:
|
|
273
|
+
1. Poll `capture-pane` for output
|
|
274
|
+
2. Write output to stdout
|
|
275
|
+
3. This caused display corruption after message injection
|
|
276
|
+
|
|
277
|
+
### Solution: Direct Attach
|
|
278
|
+
|
|
279
|
+
New architecture:
|
|
280
|
+
1. Start agent in detached tmux session
|
|
281
|
+
2. **Attach user directly** via `spawn('tmux', ['attach'], { stdio: 'inherit' })`
|
|
282
|
+
3. Background polling is **completely silent** (no stdout writes)
|
|
283
|
+
4. Injection via `send-keys` works on attached session
|
|
284
|
+
5. Logs only to stderr
|
|
285
|
+
|
|
286
|
+
### Test Result: PASSED
|
|
287
|
+
|
|
288
|
+
Message injection works without display corruption:
|
|
289
|
+
```bash
|
|
290
|
+
# Terminal 1: Daemon
|
|
291
|
+
node dist/cli/index.js start -f
|
|
292
|
+
|
|
293
|
+
# Terminal 2: Agent
|
|
294
|
+
node dist/cli/index.js wrap --tmux2 -n PlayerX -- claude
|
|
295
|
+
|
|
296
|
+
# Terminal 3: Send message
|
|
297
|
+
node dist/cli/index.js send -f Test -t PlayerX -m "Hello!"
|
|
298
|
+
# → Message injected successfully, display intact
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Next: Multi-Round Tic-Tac-Toe Test
|
|
302
|
+
|
|
303
|
+
The critical test is whether injection remains stable over multiple rounds:
|
|
304
|
+
```bash
|
|
305
|
+
# Terminal 1: Daemon
|
|
306
|
+
node dist/cli/index.js start -f
|
|
307
|
+
|
|
308
|
+
# Terminal 2: PlayerX
|
|
309
|
+
node dist/cli/index.js wrap --tmux2 -n PlayerX -- claude -p "Play tic-tac-toe. Use @relay:PlayerO to send moves."
|
|
310
|
+
|
|
311
|
+
# Terminal 3: PlayerO
|
|
312
|
+
node dist/cli/index.js wrap --tmux2 -n PlayerO -- claude -p "Play tic-tac-toe. Use @relay:PlayerX to send moves."
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
If this works for a full game (5-9 moves), we've solved the problem.
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## December 19, 2025 - Command Quoting Fix
|
|
320
|
+
|
|
321
|
+
### Problem
|
|
322
|
+
|
|
323
|
+
Running with `-p` flag failed:
|
|
324
|
+
```bash
|
|
325
|
+
node dist/cli/index.js wrap --tmux2 -n PlayerX -- claude -p "You are PlayerX..."
|
|
326
|
+
# Result: Claude terminal didn't open
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Root Cause
|
|
330
|
+
|
|
331
|
+
The CLI was joining all command parts into a single string at line 229:
|
|
332
|
+
```typescript
|
|
333
|
+
const command = commandParts.join(' ');
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
And passing only this joined string to TmuxWrapper. But by the time args reach Node.js,
|
|
337
|
+
the shell has already stripped the outer quotes, so `commandParts` = `['claude', '-p', 'You are PlayerX...']`.
|
|
338
|
+
|
|
339
|
+
When joined back together without re-quoting: `claude -p You are PlayerX...` - the prompt is split.
|
|
340
|
+
|
|
341
|
+
### Fix
|
|
342
|
+
|
|
343
|
+
1. **CLI (index.ts)**: Split command and args separately:
|
|
344
|
+
```typescript
|
|
345
|
+
const [mainCommand, ...commandArgs] = commandParts;
|
|
346
|
+
// Pass to TmuxWrapper as:
|
|
347
|
+
// command: mainCommand ('claude')
|
|
348
|
+
// args: commandArgs (['-p', 'You are PlayerX...'])
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
2. **TmuxWrapper**: `buildCommand()` re-quotes args containing spaces:
|
|
352
|
+
```typescript
|
|
353
|
+
private buildCommand(): string {
|
|
354
|
+
const quotedArgs = this.config.args.map(arg => {
|
|
355
|
+
if (arg.includes(' ') || arg.includes('"') || arg.includes("'")) {
|
|
356
|
+
return `"${arg.replace(/"/g, '\\"')}"`;
|
|
357
|
+
}
|
|
358
|
+
return arg;
|
|
359
|
+
});
|
|
360
|
+
return `${this.config.command} ${quotedArgs.join(' ')}`;
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Result: `claude -p "You are PlayerX..."` is correctly sent to tmux.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Removable Code Analysis
|
|
2
|
+
|
|
3
|
+
Requested: identify code/assets likely safe to remove (unused, legacy, or duplicates).
|
|
4
|
+
|
|
5
|
+
## Candidates
|
|
6
|
+
1) `run-dashboard.js`
|
|
7
|
+
- Purpose: helper to start dashboard from built `dist/dashboard/server.js`.
|
|
8
|
+
- Evidence: Dashboard now exposed via CLI subcommand `dashboard` in `src/cli/index.ts`. No `rg` references to `run-dashboard.js`. Redundant helper.
|
|
9
|
+
|
|
10
|
+
2) `scripts/games/*` and `scripts/tictactoe-setup.sh`
|
|
11
|
+
- Purpose: old demo/game scripts (hearts/tictactoe).
|
|
12
|
+
- Evidence: Not referenced by CLI commands or docs (checked with `rg`). Pure examples; removable if demos not needed.
|
|
13
|
+
|
|
14
|
+
3) `src/hooks/check-inbox.sh`
|
|
15
|
+
- Purpose: legacy hook example for Claude stop-event inbox check.
|
|
16
|
+
- Evidence: Replaced by compiled hook in `dist/hooks/inbox-check/*` and TypeScript sources in `src/hooks/inbox-check/*`. No other references via `rg`, so redundant.
|
|
17
|
+
|
|
18
|
+
4) `coverage/` directory
|
|
19
|
+
- Purpose: test coverage artifacts.
|
|
20
|
+
- Evidence: Generated output; not source. Safe to delete/regenerate.
|
|
21
|
+
|
|
22
|
+
5) `TOMORROW.md`
|
|
23
|
+
- Purpose: session notes / planning doc.
|
|
24
|
+
- Evidence: Not used by runtime or tests; optional to keep for historical notes only.
|
package/install.sh
CHANGED
|
@@ -42,8 +42,16 @@ install_source() {
|
|
|
42
42
|
curl -fsSL "https://github.com/$REPO/archive/main.tar.gz" | tar -xz -C "$INSTALL_DIR" --strip-components=1
|
|
43
43
|
fi
|
|
44
44
|
|
|
45
|
-
cd "$INSTALL_DIR" && npm ci && npm run build
|
|
46
|
-
|
|
45
|
+
cd "$INSTALL_DIR" && npm ci && npm rebuild better-sqlite3 && npm run build
|
|
46
|
+
|
|
47
|
+
# Remove any existing symlink or file (old installs used symlinks which cause issues)
|
|
48
|
+
rm -f "$BIN_DIR/agent-relay"
|
|
49
|
+
|
|
50
|
+
# Create wrapper script that runs from install dir (for node_modules resolution)
|
|
51
|
+
cat > "$BIN_DIR/agent-relay" << WRAPPER
|
|
52
|
+
#!/usr/bin/env bash
|
|
53
|
+
cd "$INSTALL_DIR" && exec node dist/cli/index.js "\$@"
|
|
54
|
+
WRAPPER
|
|
47
55
|
chmod +x "$BIN_DIR/agent-relay"
|
|
48
56
|
|
|
49
57
|
[[ ":$PATH:" != *":$BIN_DIR:"* ]] && warn "Add to PATH: export PATH=\"\$PATH:$BIN_DIR\""
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-relay",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Real-time agent-to-agent communication system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -12,9 +12,8 @@
|
|
|
12
12
|
"dist/",
|
|
13
13
|
"install.sh",
|
|
14
14
|
"README.md",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"LICENSE"
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"docs/"
|
|
18
17
|
],
|
|
19
18
|
"publishConfig": {
|
|
20
19
|
"access": "public"
|
package/dist/games/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/games/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
package/dist/games/index.js
DELETED
package/dist/games/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/games/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
|
|
@@ -1,24 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
package/dist/games/tictactoe.js
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Supervisor Inbox Utilities
|
|
3
|
-
*
|
|
4
|
-
* Provides an atomic "claim" for an agent inbox file to avoid losing messages
|
|
5
|
-
* that arrive while the supervisor is processing.
|
|
6
|
-
*/
|
|
7
|
-
export interface InboxMessage {
|
|
8
|
-
from: string;
|
|
9
|
-
timestamp: string;
|
|
10
|
-
body: string;
|
|
11
|
-
}
|
|
12
|
-
export interface ClaimedInbox {
|
|
13
|
-
inboxPath: string;
|
|
14
|
-
processingPath: string;
|
|
15
|
-
content: string;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Parse inbox markdown into structured messages.
|
|
19
|
-
*
|
|
20
|
-
* Expected format (repeated blocks):
|
|
21
|
-
* ## Message from <sender> | <timestamp>
|
|
22
|
-
* <body>
|
|
23
|
-
*/
|
|
24
|
-
export declare function parseInboxMarkdown(content: string): InboxMessage[];
|
|
25
|
-
/**
|
|
26
|
-
* Atomically claim an inbox by renaming it to a processing file.
|
|
27
|
-
* New incoming messages will be written to a new inbox.md.
|
|
28
|
-
*/
|
|
29
|
-
export declare function claimInbox(inboxPath: string): ClaimedInbox | null;
|
|
30
|
-
/**
|
|
31
|
-
* Finalize a claimed inbox.
|
|
32
|
-
*
|
|
33
|
-
* - On success: delete processing file.
|
|
34
|
-
* - On failure: re-queue the claimed content back into inbox.md without
|
|
35
|
-
* overwriting any messages that arrived while processing.
|
|
36
|
-
*/
|
|
37
|
-
export declare function finalizeClaim(claim: ClaimedInbox, success: boolean): void;
|
|
38
|
-
//# sourceMappingURL=inbox.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"inbox.d.ts","sourceRoot":"","sources":["../../src/supervisor/inbox.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAeD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,CA4DlE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAyBjE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CA6CzE"}
|