From a5fe8e4297b56fbd59dafa4a9c9f2958f3214fae Mon Sep 17 00:00:00 2001 From: Khaliq Date: Sun, 28 Dec 2025 19:30:20 -0500 Subject: [PATCH 1/3] tmux resolution --- .beads/issues.jsonl | 72 +++++++----- .gitignore | 3 + bin/.gitkeep | 0 package.json | 6 +- scripts/postinstall.js | 225 ++++++++++++++++++++++++++++++++++++ src/bridge/spawner.ts | 25 ++-- src/cli/index.ts | 68 ++++++++--- src/dashboard/server.ts | 30 ++++- src/utils/tmux-resolver.ts | 207 +++++++++++++++++++++++++++++++++ src/wrapper/tmux-wrapper.ts | 41 ++++--- 10 files changed, 600 insertions(+), 77 deletions(-) create mode 100644 bin/.gitkeep create mode 100644 scripts/postinstall.js create mode 100644 src/utils/tmux-resolver.ts diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index fd81a5920..1bea4d5bf 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -7,21 +7,21 @@ {"id":"agent-relay-1t7","title":"Project name display in bridge header","description":"Show selected project name in the bridge interface header for clarity","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-24T11:47:39.531513+01:00","updated_at":"2025-12-24T11:59:16.98688+01:00","closed_at":"2025-12-24T11:59:16.98688+01:00"} {"id":"agent-relay-290","title":"Interface parity: bridge vs dashboard features","description":"Audit and synchronize features between bridge and dashboard interfaces to ensure feature parity","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-24T11:47:58.911802+01:00","updated_at":"2025-12-24T12:01:06.6157+01:00","closed_at":"2025-12-24T12:01:06.6157+01:00"} {"id":"agent-relay-2lw","title":"Add agent metadata tracking","description":"Track program, model, task description in agents.json. Better agent discovery.","status":"closed","priority":2,"issue_type":"feature","assignee":"Implementer","created_at":"2025-12-20T21:36:19.741328+01:00","updated_at":"2025-12-22T17:17:25.919228+01:00","closed_at":"2025-12-22T17:17:25.919228+01:00"} -{"id":"agent-relay-2sn","title":"Competitive Analysis: mcp_agent_mail vs agent-relay","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-20T21:34:44.49366+01:00","updated_at":"2025-12-20T21:36:26.362391+01:00","closed_at":"2025-12-20T21:36:26.362391+01:00"} +{"id":"agent-relay-2sn","title":"Competitive Analysis: mcp_agent_mail vs agent-relay","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-20T21:34:44.49366+01:00","updated_at":"2025-12-20T21:36:26.362391+01:00","closed_at":"2025-12-20T21:36:26.362391+01:00"} {"id":"agent-relay-2uf","title":"Add message threading support","description":"@relay:Bob [thread:feature-123] pattern. Group related messages for better context.","notes":"Current state: thread parsing + protocol/storage support implemented (ParsedCommand.thread, SendPayload.thread, DB messages.thread + filter). Remaining: wire cmd.thread through tmux-wrapper sendRelayCommand -\u003e RelayClient.sendMessage(..., thread) and include thread in injected display/Inbox. See src/wrapper/tmux-wrapper.ts sendRelayCommand + handleIncomingMessage.","status":"closed","priority":2,"issue_type":"feature","assignee":"SecondLead","created_at":"2025-12-20T21:36:19.631448+01:00","updated_at":"2025-12-22T17:17:42.876826+01:00","closed_at":"2025-12-22T17:17:42.876826+01:00"} {"id":"agent-relay-2z1","title":"ACK messages not used for reliability","description":"In connection.ts:114-116, ACK messages are accepted but not processed. The protocol supports reliable delivery with ACK/NACK but it's not implemented. Need to: (1) Track unACKed messages, (2) Implement retry logic, (3) Add configurable TTL for messages.","status":"closed","priority":2,"issue_type":"feature","assignee":"LeadDev","created_at":"2025-12-20T00:17:43.615251+01:00","updated_at":"2025-12-20T21:56:07.202292+01:00","closed_at":"2025-12-20T21:56:07.202292+01:00"} {"id":"agent-relay-37i","title":"Message deduplication uses in-memory Set without limits","description":"In tmux-wrapper.ts:65, sentMessageHashes is a Set that grows unbounded. For long-running sessions, this could cause memory issues. Add: (1) Max size with LRU eviction, (2) Time-based expiration, (3) Bloom filter alternative for memory efficiency.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-20T00:18:47.229988+01:00","updated_at":"2025-12-20T00:18:47.229988+01:00"} {"id":"agent-relay-3px","title":"Add playbook system for batch automation","description":"Implement playbook system (like Maestro's Auto Run) for batch-processing task lists through agents. Define workflows in YAML/markdown, execute automatically with context isolation. Enables reproducible multi-step automation.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-23T17:04:54.464749+01:00","updated_at":"2025-12-23T17:04:54.464749+01:00"} -{"id":"agent-relay-3tx","title":"PR-9 Review: Document configurable timeouts","description":"","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-22T21:54:15.789418+01:00","updated_at":"2025-12-22T21:54:15.789418+01:00"} -{"id":"agent-relay-3y1","title":"Fix dashboard auto-scroll interfering with manual scrolling","description":"","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-24T15:09:41.112636+01:00","updated_at":"2025-12-24T15:10:59.748796+01:00","closed_at":"2025-12-24T15:10:59.748796+01:00"} +{"id":"agent-relay-3tx","title":"PR-9 Review: Document configurable timeouts","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-22T21:54:15.789418+01:00","updated_at":"2025-12-22T21:54:15.789418+01:00"} +{"id":"agent-relay-3y1","title":"Fix dashboard auto-scroll interfering with manual scrolling","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-24T15:09:41.112636+01:00","updated_at":"2025-12-24T15:10:59.748796+01:00","closed_at":"2025-12-24T15:10:59.748796+01:00"} {"id":"agent-relay-41f","title":"BUG: better-sqlite3 bindings fail on Node 25","description":"agent-relay read command fails with 'Could not locate the bindings file' on Node v25.2.1. Need to rebuild bindings or use compatible Node version.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-20T21:46:24.882216+01:00","updated_at":"2025-12-20T21:49:56.89756+01:00","closed_at":"2025-12-20T21:49:56.89756+01:00"} {"id":"agent-relay-47z","title":"Express 5 may have breaking changes from Express 4 patterns","description":"package.json uses express@5.2.1 which is a major version with breaking changes from Express 4. Verify: (1) Error handling middleware patterns, (2) Router behavior, (3) Body parsing (express.json vs body-parser).","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-20T00:18:49.269841+01:00","updated_at":"2025-12-20T00:18:49.269841+01:00"} {"id":"agent-relay-4e0","title":"Fix message truncation - messages cut off at source","description":"Root cause found: parser.ts:40 inline regex only captures single line. Multi-line messages are split by parsePassThrough() at line 206. Fix options: (1) Allow continuation lines in inline format, (2) Use block format for multi-line, (3) Add heuristic to join lines until next @relay pattern.","status":"closed","priority":2,"issue_type":"bug","assignee":"MistyShelter","created_at":"2025-12-19T23:40:35.082717+01:00","updated_at":"2025-12-20T00:03:54.806087+01:00","closed_at":"2025-12-20T00:03:54.806087+01:00"} -{"id":"agent-relay-4ft","title":"Merge project info into status command","description":"","status":"closed","priority":2,"issue_type":"task","assignee":"Pruner","created_at":"2025-12-19T21:59:52.685495+01:00","updated_at":"2025-12-19T22:06:44.276187+01:00","closed_at":"2025-12-19T22:06:44.276187+01:00"} +{"id":"agent-relay-4ft","title":"Merge project info into status command","status":"closed","priority":2,"issue_type":"task","assignee":"Pruner","created_at":"2025-12-19T21:59:52.685495+01:00","updated_at":"2025-12-19T22:06:44.276187+01:00","closed_at":"2025-12-19T22:06:44.276187+01:00"} {"id":"agent-relay-4jv","title":"Add session discovery from existing Claude Code","description":"Auto-discover and import existing Claude Code sessions (like Maestro). Detect running claude processes, offer to wrap them with agent-relay messaging. Reduces friction for adoption - users don't have to restart their agents.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-23T17:05:01.003851+01:00","updated_at":"2025-12-23T17:05:01.003851+01:00"} {"id":"agent-relay-4oy","title":"Fix beads sync prefix mismatch errors","description":"During bd sync, getting 'prefix mismatch detected: database uses agent-relay- but found issues with prefixes: agent- (5 issues)'. Need to investigate and fix the prefix inconsistency or use --rename-on-import flag.","status":"open","priority":2,"issue_type":"bug","created_at":"2025-12-23T23:10:12.720183+01:00","updated_at":"2025-12-23T23:10:12.720183+01:00"} -{"id":"agent-relay-4vl","title":"PR-9 Review: Add unit tests for AgentSpawner","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-22T21:54:03.195295+01:00","updated_at":"2025-12-22T22:00:57.315234+01:00","closed_at":"2025-12-22T22:00:57.315234+01:00"} -{"id":"agent-relay-4xs","title":"Dashboard: Enter to send, Shift+Enter for newline (Slack-style)","description":"","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-12-24T15:25:01.248707+01:00","updated_at":"2025-12-24T15:26:24.619384+01:00","closed_at":"2025-12-24T15:26:24.619384+01:00"} +{"id":"agent-relay-4vl","title":"PR-9 Review: Add unit tests for AgentSpawner","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-22T21:54:03.195295+01:00","updated_at":"2025-12-22T22:00:57.315234+01:00","closed_at":"2025-12-22T22:00:57.315234+01:00"} +{"id":"agent-relay-4xs","title":"Dashboard: Enter to send, Shift+Enter for newline (Slack-style)","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-12-24T15:25:01.248707+01:00","updated_at":"2025-12-24T15:26:24.619384+01:00","closed_at":"2025-12-24T15:26:24.619384+01:00"} {"id":"agent-relay-51u","title":"Outgoing messages truncated before delivery","description":"Agent messages are being cut off mid-sentence. Examples: 'Updates for mcl/2z1:' and 'Signing off. Progress report:' - both end abruptly. May be related to capture-pane buffer limits, parser issues, or injection timing. Investigate: (1) capture-pane -S - scrollback limits, (2) parser line joining, (3) message storage truncation.","notes":"PROGRESS: Reproduced truncation in tmux wrapper/relay flow. Likely due to display truncation on injection length and terminal capture limitations. No code changes yet.","status":"closed","priority":1,"issue_type":"bug","assignee":"Implementer","created_at":"2025-12-20T21:50:11.411952+01:00","updated_at":"2025-12-22T13:48:45.264136+01:00","closed_at":"2025-12-22T13:48:45.264136+01:00"} {"id":"agent-relay-52d","title":"Add metrics/observability for daemon","description":"No way to monitor daemon health, message throughput, or agent activity. Add: (1) /metrics endpoint for Prometheus, (2) Message count/rate stats, (3) Connection lifecycle events, (4) Error rate tracking.","status":"closed","priority":2,"issue_type":"feature","assignee":"FEDev","created_at":"2025-12-20T00:18:48.378728+01:00","updated_at":"2025-12-25T13:59:43.376287+01:00","closed_at":"2025-12-25T13:59:43.376287+01:00"} {"id":"agent-relay-52e","title":"Unify bridge view with dashboard UI","description":"Make bridge view feel like the main dashboard with project tabs, same agent/messaging UI, spawn capability at bridge level, and ability to click into individual project dashboards","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-25T13:44:11.491787+01:00","updated_at":"2025-12-25T13:49:36.007645+01:00","closed_at":"2025-12-25T13:49:36.007645+01:00"} @@ -32,22 +32,23 @@ {"id":"agent-relay-6dl8","title":"Multi-line relay messages truncated in dashboard display","description":"Multi-line messages sent via -\u003erelay: are being cut off after first line when displayed in dashboard. This blocks communication between agents. Need to: (1) Verify daemon sends full message, (2) Fix dashboard rendering to handle newlines properly.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-26T12:28:39.746942+01:00","updated_at":"2025-12-26T12:38:36.844034+01:00","closed_at":"2025-12-26T12:38:36.844034+01:00"} {"id":"agent-relay-6mo","title":"Lead spawn capability not working (@relay:spawn pattern)","description":"The lead command advertises spawn capability via @relay:spawn pattern but it's not fully implemented. Agents started with 'agent-relay lead' should be able to spawn workers, but the parser extension mentioned in the code is not implemented.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-23T12:14:59.492237+01:00","updated_at":"2025-12-23T12:18:14.114911+01:00","closed_at":"2025-12-23T12:18:14.114911+01:00"} {"id":"agent-relay-6nx","title":"Add activity state tracking for better injection timing","description":"Track active/idle/disconnected state with timestamps. When session goes idle (30s no activity), trigger message injection opportunity. Improves injection timing vs current fixed 1.5s wait. See docs/TMUX_IMPROVEMENTS.md for implementation details.","status":"closed","priority":2,"issue_type":"feature","assignee":"LeadDev","created_at":"2025-12-20T21:28:46.993856+01:00","updated_at":"2025-12-20T21:33:42.223042+01:00","closed_at":"2025-12-20T21:33:42.223042+01:00"} -{"id":"agent-relay-6ny","title":"Fix message truncation: store full message in SQLite first, include ID in relay","description":"","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-19T22:04:36.168862+01:00","updated_at":"2025-12-19T22:08:28.207532+01:00","closed_at":"2025-12-19T22:08:28.207532+01:00"} +{"id":"agent-relay-6ny","title":"Fix message truncation: store full message in SQLite first, include ID in relay","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-19T22:04:36.168862+01:00","updated_at":"2025-12-19T22:08:28.207532+01:00","closed_at":"2025-12-19T22:08:28.207532+01:00"} {"id":"agent-relay-6rz","title":"Message injection timing can cause race conditions","description":"In tmux-wrapper.ts:564-569, injection waits for 'idle' (1.5s since last output) but this is fragile. If agent produces output during injection, messages could interleave. Consider: (1) Input buffer detection, (2) Bracketed paste mode, (3) Agent-specific injection strategies.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-20T00:18:17.76865+01:00","updated_at":"2025-12-25T14:31:59.858855+01:00","closed_at":"2025-12-25T14:31:59.858855+01:00"} {"id":"agent-relay-7b2","title":"CRITICAL: Agents cycling between offline/online status","description":"Dashboard reports agents cycling between offline/online. Root cause analysis: Heartbeat timeout in connection.ts line 230 - if PONG not received within 10 seconds (2x heartbeat interval), connection is killed. This is too aggressive for AI agents that may be processing. Fix: increase timeout tolerance or make configurable. See connection.ts:223-244.","status":"closed","priority":0,"issue_type":"bug","assignee":"Lead","created_at":"2025-12-23T22:57:53.052261+01:00","updated_at":"2025-12-23T23:02:41.925425+01:00","closed_at":"2025-12-23T23:02:41.925425+01:00"} {"id":"agent-relay-7bp","title":"Memory storage adapter has fixed 1000 message limit","description":"In storage/adapter.ts:60-63, MemoryStorageAdapter hard-codes 1000 message limit. This should be configurable and potentially use LRU eviction instead of slice.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-20T00:17:46.132735+01:00","updated_at":"2025-12-20T00:17:46.132735+01:00"} -{"id":"agent-relay-7fx","title":"PR-9 Review: Fix @relay vs -\u003erelay syntax inconsistency in docs","description":"","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-22T22:02:02.782958+01:00","updated_at":"2025-12-22T22:04:02.664058+01:00","closed_at":"2025-12-22T22:04:02.664058+01:00"} +{"id":"agent-relay-7fx","title":"PR-9 Review: Fix @relay vs -\u003erelay syntax inconsistency in docs","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-22T22:02:02.782958+01:00","updated_at":"2025-12-22T22:04:02.664058+01:00","closed_at":"2025-12-22T22:04:02.664058+01:00"} {"id":"agent-relay-7mm","title":"Add orphaned tmux session cleanup","description":"When agent-relay wrapper crashes or gets SIGKILL, the tmux session remains. Need: 1) CLI command to gc orphaned sessions, 2) Daemon-side cleanup on client disconnect","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-22T15:52:01.58781+01:00","updated_at":"2025-12-22T15:54:59.840769+01:00","closed_at":"2025-12-22T15:54:59.840769+01:00"} {"id":"agent-relay-7tk","title":"Dashboard truncates multi-line messages","description":"Messages containing line breaks only show the first line in the dashboard. Need to render newlines properly - either with CSS white-space or by converting newlines to \u003cbr\u003e tags.","status":"closed","priority":1,"issue_type":"bug","assignee":"FE-Dev","created_at":"2025-12-23T23:05:03.86361+01:00","updated_at":"2025-12-23T23:05:59.96346+01:00","closed_at":"2025-12-23T23:05:59.96346+01:00"} {"id":"agent-relay-7tu","title":"Fix storage portability: better-sqlite3 native binding failures","description":"The read command fails in some Node versions (e.g., Node 25) due to missing better-sqlite3 bindings. Decide path: pin supported Node LTS + ensure rebuild, or replace with a non-native option (e.g., Node sqlite, wasm, or a pure JS adapter).","design":"Implement driver selection/fallback: try better-sqlite3, fallback to node:sqlite DatabaseSync when bindings missing. Add env override AGENT_RELAY_SQLITE_DRIVER=node|better-sqlite3. Convert queries to positional params so both drivers share code.","acceptance_criteria":"- dist CLI read works on supported Node versions\\n- CI/build docs reflect supported runtime or replacement adapter","notes":"Implemented sqlite portability: dynamic better-sqlite3 import + fallback to node:sqlite (env override AGENT_RELAY_SQLITE_DRIVER). Updated sqlite adapter to use positional params for compatibility; added/updated tests; npx vitest run passes (216).","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-20T21:44:57.868091+01:00","updated_at":"2025-12-20T21:56:13.384399+01:00","closed_at":"2025-12-20T21:56:13.3844+01:00","labels":["build","storage"]} -{"id":"agent-relay-7yo","title":"Update CLAUDE.md with new CLI commands","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T22:00:02.661859+01:00","updated_at":"2025-12-19T22:07:27.766701+01:00","closed_at":"2025-12-19T22:07:27.766701+01:00","dependencies":[{"issue_id":"agent-relay-7yo","depends_on_id":"agent-relay-85z","type":"blocks","created_at":"2025-12-19T22:00:25.731921+01:00","created_by":"daemon"},{"issue_id":"agent-relay-7yo","depends_on_id":"agent-relay-4ft","type":"blocks","created_at":"2025-12-19T22:00:25.772241+01:00","created_by":"daemon"},{"issue_id":"agent-relay-7yo","depends_on_id":"agent-relay-bd0","type":"blocks","created_at":"2025-12-19T22:00:25.80776+01:00","created_by":"daemon"},{"issue_id":"agent-relay-7yo","depends_on_id":"agent-relay-f3q","type":"blocks","created_at":"2025-12-19T22:00:25.843131+01:00","created_by":"daemon"}]} -{"id":"agent-relay-85z","title":"Merge dashboard into start command","description":"","status":"closed","priority":2,"issue_type":"task","assignee":"InterfaceManager","created_at":"2025-12-19T21:59:51.61716+01:00","updated_at":"2025-12-19T22:06:44.27487+01:00","closed_at":"2025-12-19T22:06:44.27487+01:00"} +{"id":"agent-relay-7yo","title":"Update CLAUDE.md with new CLI commands","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T22:00:02.661859+01:00","updated_at":"2025-12-19T22:07:27.766701+01:00","closed_at":"2025-12-19T22:07:27.766701+01:00","dependencies":[{"issue_id":"agent-relay-7yo","depends_on_id":"agent-relay-85z","type":"blocks","created_at":"2025-12-19T22:00:25.731921+01:00","created_by":"daemon","metadata":"{}"},{"issue_id":"agent-relay-7yo","depends_on_id":"agent-relay-4ft","type":"blocks","created_at":"2025-12-19T22:00:25.772241+01:00","created_by":"daemon","metadata":"{}"},{"issue_id":"agent-relay-7yo","depends_on_id":"agent-relay-bd0","type":"blocks","created_at":"2025-12-19T22:00:25.80776+01:00","created_by":"daemon","metadata":"{}"},{"issue_id":"agent-relay-7yo","depends_on_id":"agent-relay-f3q","type":"blocks","created_at":"2025-12-19T22:00:25.843131+01:00","created_by":"daemon","metadata":"{}"}]} +{"id":"agent-relay-85z","title":"Merge dashboard into start command","status":"closed","priority":2,"issue_type":"task","assignee":"InterfaceManager","created_at":"2025-12-19T21:59:51.61716+01:00","updated_at":"2025-12-19T22:06:44.27487+01:00","closed_at":"2025-12-19T22:06:44.27487+01:00"} {"id":"agent-relay-8ap","title":"CRITICAL: send command triggers infinite reconnection loop","description":"When sending relay messages to Gem agent, the send command triggers an infinite reconnection loop. Client repeatedly connects with RESUME_TOO_OLD errors, never actually delivers the message. Each connection gets a new session ID and immediately reconnects (~90-110ms apart). This is a critical client/daemon issue, not just Gemini-specific.","notes":"CLI send command removed (keeping surface area small). Root cause still needs investigation - client reconnection loop with RESUME_TOO_OLD errors.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-22T12:18:13.272843+01:00","updated_at":"2025-12-22T14:37:19.435751+01:00","closed_at":"2025-12-22T14:37:19.435751+01:00"} {"id":"agent-relay-8fc","title":"Agent auto-complete menu is transparent/invisible","description":"The agent auto-complete dropdown menu in the dashboard appears to be transparent or has visibility issues, making it unusable. Need to debug CSS/z-index/display issues in bridge.html agent selector component.","status":"closed","priority":2,"issue_type":"bug","assignee":"FE-Dev","created_at":"2025-12-24T15:04:53.390674+01:00","updated_at":"2025-12-24T15:30:23.72431+01:00","closed_at":"2025-12-24T15:30:23.72431+01:00"} {"id":"agent-relay-8ff","title":"Add agent list command to CLI","description":"CLI has up/down/status/read but no way to list connected agents or see message history from command line. Add: (1) agent-relay agents - list connected agents, (2) agent-relay history - show recent messages, (3) agent-relay send \u003cagent\u003e \u003cmsg\u003e - send from CLI.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-20T00:18:34.400432+01:00","updated_at":"2025-12-22T15:35:44.695605+01:00","closed_at":"2025-12-22T15:35:44.695605+01:00"} {"id":"agent-relay-8nx","title":"SIGINT/SIGTERM handlers don't await cleanup","description":"In cli/index.ts:74-77 and daemon setup, SIGINT handlers call stop() but process.exit(0) runs immediately. The stop() is async but not awaited, potentially leaving socket files or incomplete shutdown.","status":"open","priority":2,"issue_type":"bug","created_at":"2025-12-20T00:18:19.189514+01:00","updated_at":"2025-12-20T00:18:19.189514+01:00"} -{"id":"agent-relay-8z1","title":"Add CLI tests for new command structure","description":"","status":"closed","priority":2,"issue_type":"task","assignee":"Lead","created_at":"2025-12-19T22:00:04.561793+01:00","updated_at":"2025-12-22T17:10:28.100961+01:00","closed_at":"2025-12-22T17:10:28.100961+01:00","dependencies":[{"issue_id":"agent-relay-8z1","depends_on_id":"agent-relay-85z","type":"blocks","created_at":"2025-12-19T22:00:27.632396+01:00","created_by":"daemon"},{"issue_id":"agent-relay-8z1","depends_on_id":"agent-relay-4ft","type":"blocks","created_at":"2025-12-19T22:00:27.671868+01:00","created_by":"daemon"},{"issue_id":"agent-relay-8z1","depends_on_id":"agent-relay-bd0","type":"blocks","created_at":"2025-12-19T22:00:27.713889+01:00","created_by":"daemon"},{"issue_id":"agent-relay-8z1","depends_on_id":"agent-relay-f3q","type":"blocks","created_at":"2025-12-19T22:00:27.752052+01:00","created_by":"daemon"}]} +{"id":"agent-relay-8z1","title":"Add CLI tests for new command structure","status":"closed","priority":2,"issue_type":"task","assignee":"Lead","created_at":"2025-12-19T22:00:04.561793+01:00","updated_at":"2025-12-22T17:10:28.100961+01:00","closed_at":"2025-12-22T17:10:28.100961+01:00","dependencies":[{"issue_id":"agent-relay-8z1","depends_on_id":"agent-relay-85z","type":"blocks","created_at":"2025-12-19T22:00:27.632396+01:00","created_by":"daemon","metadata":"{}"},{"issue_id":"agent-relay-8z1","depends_on_id":"agent-relay-4ft","type":"blocks","created_at":"2025-12-19T22:00:27.671868+01:00","created_by":"daemon","metadata":"{}"},{"issue_id":"agent-relay-8z1","depends_on_id":"agent-relay-bd0","type":"blocks","created_at":"2025-12-19T22:00:27.713889+01:00","created_by":"daemon","metadata":"{}"},{"issue_id":"agent-relay-8z1","depends_on_id":"agent-relay-f3q","type":"blocks","created_at":"2025-12-19T22:00:27.752052+01:00","created_by":"daemon","metadata":"{}"}]} {"id":"agent-relay-90h","title":"Show 'needs attention' indicator when agent is waiting for permissions","description":"Frontend complete: needsAttention field, pulsing indicator, badge. Backend piece still needed: detect when agents are waiting for permissions and set the flag.","status":"closed","priority":1,"issue_type":"feature","assignee":"FE-Dev","created_at":"2025-12-23T22:55:26.87753+01:00","updated_at":"2025-12-24T11:48:32.679452+01:00","closed_at":"2025-12-24T11:48:32.679452+01:00"} +{"id":"agent-relay-9igw","title":"Agents not closing multi-line relay messages with \u003e\u003e\u003e","description":"Agent Relay requires multi-line messages to be wrapped in \u003c\u003c\u003c \u003e\u003e\u003e format with \u003e\u003e\u003e on its own line. Agents are sending multi-line messages without proper closure. This breaks message parsing and relay communication quality.","status":"open","priority":1,"issue_type":"bug","created_at":"2025-12-28T19:29:43.950026-05:00","updated_at":"2025-12-28T19:29:43.950026-05:00"} {"id":"agent-relay-9kc","title":"Add skip-permissions flag for spawned Claude agents","description":"When spawning Claude agents via agent-relay, automatically use --dangerously-skip-permissions to avoid permission dialog delays. Update spawn command in CLI to pass this flag to spawned sessions.","status":"closed","priority":1,"issue_type":"task","assignee":"FEDev","created_at":"2025-12-25T14:09:00.07082+01:00","updated_at":"2025-12-25T14:15:45.925839+01:00","closed_at":"2025-12-25T14:15:45.925839+01:00"} {"id":"agent-relay-9kw","title":"Add multi-machine agent support","description":"Enable agents to run across multiple machines with centralized coordination. AI Maestro scales to 100+ agents across machines. Extend bridge mode to support remote daemon connections over TCP/WebSocket. Critical for scaling beyond single machine.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-23T17:04:35.521241+01:00","updated_at":"2025-12-23T17:04:35.521241+01:00"} {"id":"agent-relay-9qa","title":"Dashboard websocket 'Invalid frame header' error on localhost:3888","description":"Websocket connection to ws://localhost:3888/ws fails with 'Invalid frame header' error from browser. Blocking dashboard functionality.","notes":"WS handshake fixed: restored default WebSocket compression (removed perMessageDeflate:false), removed HTTP polling fallback. FE-Dev confirmed 101 upgrade on 127.0.0.1:3888 after rebuild/refresh.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-23T16:42:29.342087+01:00","updated_at":"2025-12-23T17:04:04.246054+01:00","closed_at":"2025-12-23T17:04:04.246054+01:00"} @@ -57,14 +58,15 @@ {"id":"agent-relay-a4k","title":"Add safety mechanisms for dangerous commands","description":"Implement safety layer for dangerous operations (like ACFS's two-person rule). Require confirmation from second agent or human for destructive commands (rm -rf, git push -f, DROP TABLE). Configurable blocklist and approval workflow.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-23T17:05:21.032206+01:00","updated_at":"2025-12-23T17:05:21.032206+01:00"} {"id":"agent-relay-ahe","title":"Session resume not implemented - RESUME_TOO_OLD always sent","description":"In connection.ts:140-143, session resume tokens are received but not persisted or validated. The server always responds with RESUME_TOO_OLD. This breaks the resume capability advertised in the protocol. Need to implement token persistence and session state recovery.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-20T00:17:30.605649+01:00","updated_at":"2025-12-22T15:31:05.795905+01:00","closed_at":"2025-12-22T15:31:05.795905+01:00"} {"id":"agent-relay-aq2","title":"Implement agent-to-agent spawning via relay","description":"Allow agents to spawn new agents through relay messaging. Enable -\u003erelay:spawn pattern so agents can dynamically create specialized sub-agents. This unblocks multi-agent workflows and team hierarchies.","status":"closed","priority":1,"issue_type":"feature","assignee":"BESpecialist","created_at":"2025-12-25T14:14:02.110506+01:00","updated_at":"2025-12-25T14:32:10.400627+01:00","closed_at":"2025-12-25T14:32:10.400627+01:00"} -{"id":"agent-relay-aqy","title":"Display cross-project messages with project badge in dashboard","description":"When messages use @project:agent format, display them in the dashboard with a project badge/indicator. Parse project:agent from the 'to' field and render as [ProjectBadge] @AgentName. Depends on agent-relay-cross-project-parser completing first.","status":"closed","priority":2,"issue_type":"feature","assignee":"FE-Dev","created_at":"2025-12-24T10:29:37.528325+01:00","updated_at":"2025-12-24T11:46:39.341486+01:00","closed_at":"2025-12-24T11:46:39.341486+01:00","dependencies":[{"issue_id":"agent-relay-aqy","depends_on_id":"agent-relay-ytl","type":"blocks","created_at":"2025-12-24T10:29:48.712573+01:00","created_by":"khaliqgant"}]} +{"id":"agent-relay-aqy","title":"Display cross-project messages with project badge in dashboard","description":"When messages use @project:agent format, display them in the dashboard with a project badge/indicator. Parse project:agent from the 'to' field and render as [ProjectBadge] @AgentName. Depends on agent-relay-cross-project-parser completing first.","status":"closed","priority":2,"issue_type":"feature","assignee":"FE-Dev","created_at":"2025-12-24T10:29:37.528325+01:00","updated_at":"2025-12-24T11:46:39.341486+01:00","closed_at":"2025-12-24T11:46:39.341486+01:00","dependencies":[{"issue_id":"agent-relay-aqy","depends_on_id":"agent-relay-ytl","type":"blocks","created_at":"2025-12-24T10:29:48.712573+01:00","created_by":"khaliqgant","metadata":"{}"}]} {"id":"agent-relay-b11","title":"Cannot scroll in agent tmux window","description":"User unable to scroll in the tmux window when running agent-relay wrapped agents. Need to fix mouse scroll or keyboard scroll behavior.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-23T15:01:55.791186+01:00","updated_at":"2025-12-23T15:04:51.896303+01:00","closed_at":"2025-12-23T15:04:51.896303+01:00"} -{"id":"agent-relay-bai","title":"Fix bridge reconnection loop caused by agent name conflict","description":"","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-25T13:26:46.025963+01:00","updated_at":"2025-12-25T13:27:04.323001+01:00","closed_at":"2025-12-25T13:27:04.323001+01:00"} -{"id":"agent-relay-bd0","title":"Consolidate team-* commands under team subcommand","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T21:59:54.102815+01:00","updated_at":"2025-12-19T22:06:16.220013+01:00","closed_at":"2025-12-19T22:06:16.220013+01:00"} +{"id":"agent-relay-bai","title":"Fix bridge reconnection loop caused by agent name conflict","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-25T13:26:46.025963+01:00","updated_at":"2025-12-25T13:27:04.323001+01:00","closed_at":"2025-12-25T13:27:04.323001+01:00"} +{"id":"agent-relay-bd0","title":"Consolidate team-* commands under team subcommand","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T21:59:54.102815+01:00","updated_at":"2025-12-19T22:06:16.220013+01:00","closed_at":"2025-12-19T22:06:16.220013+01:00"} {"id":"agent-relay-bei","title":"Gemini (CleverBeacon) bash syntax errors - unexpected EOF","description":"Gemini agent gets bash errors: 'unexpected EOF while looking for matching quote' and 'syntax error: unexpected end of file' when running shell commands. Possibly related to quote escaping in the relay wrapper.","status":"closed","priority":2,"issue_type":"bug","assignee":"GraniteElk","created_at":"2025-12-19T23:40:36.464079+01:00","updated_at":"2025-12-19T23:45:46.05609+01:00","closed_at":"2025-12-19T23:45:46.05609+01:00"} {"id":"agent-relay-blj","title":"Add metrics and observability endpoint","description":"Add metrics collection and observability for production monitoring. Include: message throughput, agent health, latency percentiles, error rates, queue depths. Expose via HTTP endpoint (Prometheus format) and/or dashboard integration. Currently flying blind in production.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-23T17:03:41.964359+01:00","updated_at":"2025-12-25T13:33:56.132695+01:00","closed_at":"2025-12-25T13:33:56.132695+01:00"} {"id":"agent-relay-bx0","title":"Filter stale/internal agents from agents list","description":"agent-relay agents shows __status__ and cli agents which are temporary internal connections. Should filter these out by default, with --all flag to show everything.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-22T15:44:51.944084+01:00","updated_at":"2025-12-22T15:45:44.262049+01:00","closed_at":"2025-12-22T15:45:44.262049+01:00"} {"id":"agent-relay-coh","title":"Add tests for heartbeat timeout behavior","description":"The heartbeat timeout fix (10s-\u003e30s) was made but no new tests were added. Add unit tests for: 1) configurable heartbeatTimeoutMultiplier, 2) connection survives slow pong responses, 3) connection dies after timeout exceeded.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-23T23:10:18.599194+01:00","updated_at":"2025-12-25T13:31:23.910414+01:00","closed_at":"2025-12-25T13:31:23.910414+01:00"} +{"id":"agent-relay-cuer","title":"Single agent flickering between active/inactive on dashboard","description":"When a single agent is spawned, the dashboard flickers rapidly between showing the agent as active and inactive. This blocks reliable agent monitoring. Need to investigate: (1) WebSocket connection stability, (2) Status update frequency, (3) State sync race conditions.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-26T16:53:20.385225+01:00","updated_at":"2025-12-26T17:20:58.613208+01:00","closed_at":"2025-12-26T17:20:58.613208+01:00"} {"id":"agent-relay-cw8","title":"Add procedural memory for learned patterns","description":"Implement procedural memory system (like ACFS's cass-memory) to store learned patterns and successful approaches. Agents can query 'how did we solve X before?' Enable cross-session learning and pattern reuse.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-23T17:05:14.667585+01:00","updated_at":"2025-12-23T17:05:14.667585+01:00"} {"id":"agent-relay-d07","title":"Message metadata: subject/thread/importance/replyTo","description":"Add first-class metadata fields to messages (subject, threadId, importance, ackRequired, replyTo/correlationId). Carry them via SendPayload.data and/or [[RELAY]] blocks; persist in storage; update injection formatting + dashboard filters.","acceptance_criteria":"- Can send/receive messages with threadId + importance\\n- Dashboard can filter/group by threadId\\n- Injection shows importance/thread hint without breaking TUIs","notes":"SecondLead: Implemented thread support end-to-end. SendMeta added to client.sendMessage(). tmux-wrapper converts ParsedMessageMetadata to SendMeta. Injection shows [thread:xxx] hint. Dashboard displays thread badge. REMAINING: importance field requires protocol update - DeliverEnvelope needs payload_meta to carry importance through to receivers.","status":"closed","priority":2,"issue_type":"task","assignee":"SecondLead","created_at":"2025-12-20T21:44:18.094598+01:00","updated_at":"2025-12-22T17:15:37.261368+01:00","closed_at":"2025-12-22T17:15:37.261368+01:00","labels":["protocol","ux"]} {"id":"agent-relay-d2f","title":"Add optional Git audit trail","description":"Commit messages to .agent-relay/messages/ directory. Recoverable history, compliance-friendly.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-20T21:36:19.689795+01:00","updated_at":"2025-12-20T21:36:19.689795+01:00"} @@ -72,40 +74,52 @@ {"id":"agent-relay-dkx","title":"Add metrics dashboard for system health monitoring","description":"Track agent metrics (throughput, uptime, online status), session lifecycle (active/closed/error rates), and per-agent message counts. Provide Prometheus-format export for monitoring integration. Includes metrics.ts utilities and metrics.html dashboard page.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-25T14:07:28.416696+01:00","updated_at":"2025-12-26T11:41:20.321264+01:00","closed_at":"2025-12-26T11:41:20.321264+01:00"} {"id":"agent-relay-dyr","title":"No authentication between agents","description":"Any process can connect to the daemon socket and impersonate any agent name. Consider: (1) Per-agent tokens/secrets, (2) Socket permission checks, (3) Optional TLS for non-localhost deployments.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-20T00:18:16.215889+01:00","updated_at":"2025-12-20T00:18:16.215889+01:00"} {"id":"agent-relay-eek","title":"Change relay prefix from @relay to \u003e\u003erelay","description":"Unify the relay protocol prefix to \u003e\u003erelay (instead of @relay) so it works for all agents including Gemini. Update parser, documentation, and any hardcoded references.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-22T12:20:35.35848+01:00","updated_at":"2025-12-22T12:30:21.696239+01:00","closed_at":"2025-12-22T12:30:21.696239+01:00"} +{"id":"agent-relay-ekk9","title":"Add @-mention autocomplete to v2 message composer","description":"V1 dashboard has @-mention autocomplete in the message composer. When typing @A, it shows a dropdown with matching agents (@Alice, @Admin, etc.). V2's MessageComposer lacks this feature. Need to port the autocomplete logic from v1's components.ts (showMentionAutocomplete, completeMention functions) to v2.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-27T05:36:28.283162+01:00","updated_at":"2025-12-27T05:44:05.597544+01:00","closed_at":"2025-12-27T05:44:05.597544+01:00","close_reason":"Added missing mention-autocomplete CSS styles to globals.css"} {"id":"agent-relay-ex7","title":"Competitive analysis: Agent-to-agent messaging solutions","description":"Research and compare 17 multi-agent orchestration tools against agent-relay. Analyze: agent communication patterns, pros/cons, server/mobile support, memory architecture.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-23T16:34:31.174109+01:00","updated_at":"2025-12-23T16:55:19.264056+01:00","closed_at":"2025-12-23T16:55:19.264056+01:00"} {"id":"agent-relay-ey9","title":"Dashboard message sending not working","description":"User reports sending messages from dashboard doesn't work. Need to investigate.","status":"closed","priority":2,"issue_type":"bug","assignee":"SecondLead","created_at":"2025-12-22T15:54:48.975146+01:00","updated_at":"2025-12-22T17:07:48.705786+01:00","closed_at":"2025-12-22T17:07:48.705786+01:00"} -{"id":"agent-relay-f3q","title":"Make msg-read a subcommand of send or message","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T21:59:55.123772+01:00","updated_at":"2025-12-19T22:04:51.112763+01:00","closed_at":"2025-12-19T22:04:51.112763+01:00"} +{"id":"agent-relay-f3q","title":"Make msg-read a subcommand of send or message","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T21:59:55.123772+01:00","updated_at":"2025-12-19T22:04:51.112763+01:00","closed_at":"2025-12-19T22:04:51.112763+01:00"} {"id":"agent-relay-f9k","title":"Fix WebSocket Invalid frame header error","description":"Dashboard WebSocket throws Invalid frame header with 415KB payload. Missing wss.on('connection') handler causes race condition.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-23T17:17:26.412812+01:00","updated_at":"2025-12-23T17:18:30.4994+01:00","closed_at":"2025-12-23T17:18:30.4994+01:00"} {"id":"agent-relay-fb3","title":"Add configurable relay prefix for Gemini compatibility","description":"Gemini CLI uses @ for file references, conflicting with @relay:. Add --prefix flag to CLI and relayPrefix config option. Auto-detect CLI type and use appropriate default (\u003e\u003e for Gemini, @relay: for Claude/Codex). Update parser to use dynamic prefix. See docs/TMUX_IMPROVEMENTS.md for full implementation plan.","status":"closed","priority":2,"issue_type":"feature","assignee":"Coordinator","created_at":"2025-12-20T21:28:52.685002+01:00","updated_at":"2025-12-20T21:40:13.066766+01:00","closed_at":"2025-12-20T21:40:13.066766+01:00"} +{"id":"agent-relay-fuob","title":"Fix spawner tests for resolved tmux path","description":"Update src/bridge/spawner.test.ts mocks to expect resolved tmux binary path (e.g., \"/opt/homebrew/bin/tmux\") instead of just \"tmux\". 9 tests failing due to exact command string assertions.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-28T19:28:46.381693-05:00","updated_at":"2025-12-28T19:28:46.381693-05:00"} {"id":"agent-relay-ghy","title":"Team config: auto-spawn agents or auto-assign names from teams.json","description":"When a project has a teams.json config file, agent-relay up should either:\n\n1. **Auto-spawn option**: Automatically kick off terminal sessions for each agent defined in teams.json\n2. **Auto-assign option**: When users manually start sessions with `agent-relay -n \u003cname\u003e claude`, validate the name against teams.json and auto-assign roles/permissions\n\n## teams.json format\n```json\n{\n \"team\": \"my-project\",\n \"agents\": [\n {\"name\": \"Coordinator\", \"cli\": \"claude\", \"role\": \"coordinator\"},\n {\"name\": \"LeadDev\", \"cli\": \"claude\", \"role\": \"developer\"},\n {\"name\": \"Reviewer\", \"cli\": \"claude\", \"role\": \"reviewer\"}\n ],\n \"autoSpawn\": false\n}\n```\n\n## Behavior\n- If `autoSpawn: true`, `agent-relay up` spawns tmux sessions for each agent\n- If `autoSpawn: false`, validate names against config when agents connect\n- Store teams.json in project root or .agent-relay/teams.json\n\n## Commands\n- `agent-relay up --spawn` - force spawn all agents\n- `agent-relay up --no-spawn` - just start daemon, manual agent starts","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-19T23:13:02.482971+01:00","updated_at":"2025-12-22T22:11:38.639588+01:00"} {"id":"agent-relay-go9","title":"PostgreSQL storage adapter not implemented","description":"In storage/adapter.ts:152-162, PostgreSQL is listed as a storage option but throws 'not yet implemented'. For production multi-node deployments, SQLite won't scale. Implement PostgreSQL adapter for distributed storage.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-20T00:17:45.065487+01:00","updated_at":"2025-12-20T00:17:45.065487+01:00"} {"id":"agent-relay-hgd","title":"Reliable delivery: resume/replay via cursor/checkpoint","description":"Make ACK/RESUME real: persist a per-agent delivery cursor (last delivered/acked seq) and on reconnect replay missed messages from storage. Use existing delivery.seq as stream position, similar to swarm-mail DurableCursor checkpointing.","acceptance_criteria":"- Client can reconnect and receive missed messages\\n- Duplicate suppression is deterministic (id/seq based)\\n- Cursor persists across daemon restarts","notes":"Persistent resume tokens with stored cursors; replay unacked messages on reconnect; ack tracking + dedup; tests passing.","status":"in_progress","priority":1,"issue_type":"task","assignee":"BE-Dev","created_at":"2025-12-20T21:44:02.016428+01:00","updated_at":"2025-12-26T12:13:54.547885+01:00","labels":["durability","protocol"]} {"id":"agent-relay-hgn","title":"Agent team hierarchies with specialized sub-agents","description":"Each main agent should be able to spawn and coordinate a team of specialized sub-agents:\n\n- **Lead** → Operations Consultant (monitors behavior, provides retrospective feedback on coordination)\n- **Implementer** → Code Reviewer (reviews code before commits, suggests improvements)\n- **Designer** → UX Reviewer (validates design decisions)\n- **Architect** → Security Auditor (checks for vulnerabilities)\n\nImplementation ideas:\n1. Define agent profiles with 'team' configurations in .claude/agents/\n2. When an agent starts, it can auto-spawn its team members\n3. Sub-agents monitor the parent agent's work and provide feedback\n4. At session end, sub-agents provide retrospective summaries\n5. Use -\u003erelay:spawn to create sub-agents with specific roles\n\nThis enables each agent to have a dedicated support team for quality assurance and improvement.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-23T15:03:56.608847+01:00","updated_at":"2025-12-23T15:03:56.608847+01:00"} -{"id":"agent-relay-hgw","title":"PR-9 Review: Add reconnection logic to MultiProjectClient","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-22T21:54:09.712003+01:00","updated_at":"2025-12-24T11:50:55.708361+01:00","closed_at":"2025-12-24T11:50:55.708361+01:00"} +{"id":"agent-relay-hgw","title":"PR-9 Review: Add reconnection logic to MultiProjectClient","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-22T21:54:09.712003+01:00","updated_at":"2025-12-24T11:50:55.708361+01:00","closed_at":"2025-12-24T11:50:55.708361+01:00"} {"id":"agent-relay-hks","title":"Increase test coverage for daemon/server.ts, dashboard, and CLI","description":"Current coverage is only 39% overall. Key files with 0% coverage: daemon/server.ts, dashboard/server.ts, cli/index.ts, wrapper/client.ts, wrapper/tmux-wrapper.ts, utils/project-namespace.ts. Need integration tests for the daemon startup/shutdown lifecycle and CLI commands.","status":"closed","priority":2,"issue_type":"task","assignee":"Lead","created_at":"2025-12-20T00:17:29.603137+01:00","updated_at":"2025-12-22T17:14:41.611717+01:00","closed_at":"2025-12-22T17:14:41.611717+01:00"} {"id":"agent-relay-hr1","title":"Show project reconnection status in dashboard","description":"Display project connection status (Connected/Reconnecting/Disconnected) in dashboard header. Use onProjectStateChange callbacks from MultiProjectClient to drive 'Connected/Disconnecting' indicators. Show per-project status and overall relay connection health. Color code: green (connected), yellow (reconnecting), red (disconnected).","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-24T11:51:32.74308+01:00","updated_at":"2025-12-24T11:52:22.778492+01:00","closed_at":"2025-12-24T11:52:22.778492+01:00"} {"id":"agent-relay-hvm","title":"Dashboard: Add ability to send messages to agents","description":"Add a message compose UI in the dashboard to send messages to any connected agent. Should include: (1) Agent selector dropdown, (2) Message input field, (3) Send button that dispatches via the relay protocol. This complements the existing message viewing capability.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-22T15:31:07.022117+01:00","updated_at":"2025-12-22T15:36:18.78037+01:00","closed_at":"2025-12-22T15:36:18.78037+01:00"} {"id":"agent-relay-i5f","title":"Fix rendering and display issues","description":"Relay messages display with garbled/overlapping text in terminal. Characters appear scattered and mixed with other terminal content. Example: message text like 'Ping me when you're ready' renders with letters displaced across the line. PR #10 added input detection (isInputClear, waitForClearInput, getCursorX) to prevent this, but it's not working effectively. Need to investigate why the input detection/wait logic isn't preventing message injection during active typing.","notes":"Parser improvements for continuation lines (bullets, numbered lists, box drawing chars for Gemini). Debug flag set to false.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-22T11:59:53.254169+01:00","updated_at":"2025-12-22T14:21:32.043374+01:00","closed_at":"2025-12-22T14:21:32.043374+01:00","labels":["review"]} +{"id":"agent-relay-i6gt","title":"Debug thinking block filter - tests fail despite correct patterns","notes":"Investigation notes:\n\nWhat was implemented:\n- Added THINKING_START and THINKING_END patterns to parser.ts (lines 69-71)\n- Added inThinkingBlock state variable (line 157)\n- Added detection logic in parsePassThrough (lines 445-465)\n- Added reset logic in flush() and reset() methods\n\nThe problem:\nTests show the thinking block content is NOT being filtered - it passes through to output.\nThe patterns match correctly when tested standalone in Node.\n\nWhat to investigate:\n1. The check is at lines 456-465 in parser.ts - why doesn't it match at runtime?\n2. Could be an issue with how the for loop flows or where the check is placed\n3. Try adding console.log in the detection code to trace execution\n4. The compiled code at dist/wrapper/parser.js lines 380-390 looks correct\n\nFiles modified:\n- src/wrapper/parser.ts - thinking block filter implementation\n- Tests were written but reverted (failing)","status":"open","priority":2,"issue_type":"bug","created_at":"2025-12-27T11:42:23.020797+01:00","updated_at":"2025-12-27T11:42:44.912268+01:00"} {"id":"agent-relay-i7s","title":"Add priority-based message queuing","description":"Implement message priority levels (urgent/high/normal/low) like AI Maestro. Urgent messages jump the queue and interrupt agents. Enables time-sensitive coordination in production scenarios.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-23T17:04:48.031237+01:00","updated_at":"2025-12-23T17:04:48.031237+01:00"} -{"id":"agent-relay-j7y","title":"Implement composer toolbar buttons (bold, emoji)","description":"","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-23T16:26:23.598781+01:00","updated_at":"2025-12-23T16:27:53.92751+01:00","closed_at":"2025-12-23T16:27:53.92751+01:00"} -{"id":"agent-relay-j9z","title":"Message injection corrupts human input in progress","description":"When a relay message arrives while the human is actively typing, the injection (Esc + Ctrl-U + message + Enter) destroys their partial input. Need to detect active input state before injecting. Options: (1) Check tmux pane input buffer, (2) Queue messages until prompt detected, (3) Use tmux display-message instead of send-keys for non-intrusive notification, (4) Add visual indicator that message is pending.","design":"Mitigation ideas: 1) Prefer tmux non-intrusive notify: tmux display-message (and always write to inbox/storage) instead of send-keys when user is typing. 2) Gate send-keys injection on 'safe' state: pane idle AND prompt detected AND no partial input; otherwise queue. 3) If queued, show pending count via display-message.","status":"open","priority":1,"issue_type":"bug","created_at":"2025-12-20T21:48:03.280298+01:00","updated_at":"2025-12-20T22:00:04.196161+01:00","dependencies":[{"issue_id":"agent-relay-j9z","depends_on_id":"agent-relay-6rz","type":"blocks","created_at":"2025-12-20T21:48:10.118963+01:00","created_by":"daemon"}]} +{"id":"agent-relay-iblw","title":"Allow agents to spawn other agents via agent-relay API","description":"Agents should be able to spawn new agent processes programmatically via agent-relay API. Enables dynamic team scaling and parallel agent coordination. Implement: (1) Agent spawn endpoint, (2) API wrapper for agent code, (3) Spawn coordination, (4) Resource limits, (5) Tests.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-26T17:08:22.05611+01:00","updated_at":"2025-12-26T17:08:22.05611+01:00"} +{"id":"agent-relay-iic5","title":"Multi-line relay messages truncated in dashboard UI (regression)","description":"Multi-line relay messages are only displaying the first line in the dashboard UI, even though the parser is capturing all lines. Root cause investigation needed: (1) Dashboard rendering (may be truncating display), (2) WebSocket message transmission, (3) Message state management. Previously marked as fixed but still broken.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-26T17:05:33.146646+01:00","updated_at":"2025-12-26T17:20:58.611383+01:00","closed_at":"2025-12-26T17:20:58.611383+01:00"} +{"id":"agent-relay-j7y","title":"Implement composer toolbar buttons (bold, emoji)","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-23T16:26:23.598781+01:00","updated_at":"2025-12-23T16:27:53.92751+01:00","closed_at":"2025-12-23T16:27:53.92751+01:00"} +{"id":"agent-relay-j9z","title":"Message injection corrupts human input in progress","description":"When a relay message arrives while the human is actively typing, the injection (Esc + Ctrl-U + message + Enter) destroys their partial input. Need to detect active input state before injecting. Options: (1) Check tmux pane input buffer, (2) Queue messages until prompt detected, (3) Use tmux display-message instead of send-keys for non-intrusive notification, (4) Add visual indicator that message is pending.","design":"Mitigation ideas: 1) Prefer tmux non-intrusive notify: tmux display-message (and always write to inbox/storage) instead of send-keys when user is typing. 2) Gate send-keys injection on 'safe' state: pane idle AND prompt detected AND no partial input; otherwise queue. 3) If queued, show pending count via display-message.","status":"open","priority":1,"issue_type":"bug","created_at":"2025-12-20T21:48:03.280298+01:00","updated_at":"2025-12-20T22:00:04.196161+01:00","dependencies":[{"issue_id":"agent-relay-j9z","depends_on_id":"agent-relay-6rz","type":"blocks","created_at":"2025-12-20T21:48:10.118963+01:00","created_by":"daemon","metadata":"{}"}]} +{"id":"agent-relay-jmwh","title":"Bundle tmux or provide fallback so users don't need to install separately","description":"User installation failed: tmux command not found. Currently agent-relay requires tmux to be pre-installed on the system. Need to bundle tmux or provide a fallback so installation works out-of-the-box.\n\nOptions to explore:\n1. NPM package that includes tmux binaries (npm tmux package or similar)\n2. Postinstall script that downloads/installs tmux\n3. Fallback to alternative shell mechanism if tmux unavailable\n4. Docker-based approach (out of scope but document)\n\nAcceptance: Installation should succeed and agent-relay should work without requiring manual tmux install","status":"in_progress","priority":0,"issue_type":"feature","assignee":"FullStack","created_at":"2025-12-28T18:51:04.556744-05:00","updated_at":"2025-12-28T18:53:01.401617-05:00"} {"id":"agent-relay-k8t","title":"Consider backend-focused agent for daemon work","description":"Session retrospective: only had FE-Dev specialist, so backend work (permission detection, daemon issues) fell to Lead. Consider spawning a dedicated backend agent for daemon/server work in future sessions.","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-23T23:10:24.756163+01:00","updated_at":"2025-12-23T23:10:24.756163+01:00"} {"id":"agent-relay-kaj","title":"Show project connection status in dashboard (Connected/Reconnecting/Disconnected)","description":"Use onProjectStateChange callbacks from MultiProjectClient to show connection status in dashboard. When onProjectStateChange(id, false) fires, show 'Disconnected' or 'Reconnecting...' indicator. When onProjectStateChange(id, true) fires, show 'Connected'. Could use a status badge in the sidebar or header.","status":"closed","priority":2,"issue_type":"feature","assignee":"FE-Dev","created_at":"2025-12-24T11:51:40.774105+01:00","updated_at":"2025-12-24T11:55:00.944949+01:00","closed_at":"2025-12-24T11:55:00.944949+01:00"} {"id":"agent-relay-kto","title":"Add topic-based pub/sub messaging","description":"Add topic subscription alongside direct addressing. Agents can subscribe to topics (e.g., 'errors', 'deploys', 'reviews') and receive all messages on those topics. Complements existing -\u003erelay:Name with -\u003erelay:#topic pattern. Pattern from Claude-Flow's message bus.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-23T17:05:27.213842+01:00","updated_at":"2025-12-23T17:05:27.213842+01:00"} +{"id":"agent-relay-kw80","title":"Plan and execute Dashboard v1 → v2 migration","description":"## Dashboard v1 → v2 Migration Plan\n\n### Feature Parity Analysis (COMPLETED)\n\n**V1 Features in V2:** ✅ ALL FEATURES PORTED\n- Agent list with online/offline status\n- Message list with date dividers \n- Channel selection (general + DMs)\n- Command palette (Cmd+K)\n- Spawn agent modal\n- Thread support\n- Fleet view (multi-server)\n- Release agent button\n- Needs attention badge\n- ✅ @-mention autocomplete (agent-relay-ekk9 - DONE)\n\n**V2-Only Features (Enhancements):**\n- Theme support (light/dark/system)\n- Settings panel\n- Trajectory viewer\n- Decision queue\n- Enhanced broadcast composer with templates\n- Notification toasts\n\n### Migration Steps\n\n1. **[agent-relay-lls5]** Configure parallel running (v1:4280, v2:4281) - READY\n2. **[agent-relay-s8hg]** Add --dashboard=v1|v2 CLI flag - blocked by lls5\n3. ~~**[agent-relay-ekk9]** Fix @-mention autocomplete~~ ✅ DONE\n4. **[agent-relay-qkey]** Switch default to v2 - blocked by s8hg\n\n### Rollback Procedure\n\n1. Use `agent-relay up --dashboard=v1` to revert instantly\n2. V1 code remains in place until v2 is stable\n3. No data migration needed - both share same backend/storage","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-26T17:01:58.36924+01:00","updated_at":"2025-12-27T05:49:48.797956+01:00","dependencies":[{"issue_id":"agent-relay-kw80","depends_on_id":"agent-relay-cuer","type":"blocks","created_at":"2025-12-26T17:02:04.103774+01:00","created_by":"daemon","metadata":"{}"},{"issue_id":"agent-relay-kw80","depends_on_id":"agent-relay-ekk9","type":"blocks","created_at":"2025-12-27T05:36:37.645846+01:00","created_by":"daemon"}]} {"id":"agent-relay-kzw","title":"Project namespace uses /tmp which can be cleared on reboot","description":"In utils/project-namespace.ts:13, BASE_DIR is /tmp/agent-relay. On macOS/Linux, /tmp is cleared on reboot, losing all message history. Consider: (1) XDG_DATA_HOME fallback, (2) ~/.agent-relay option, (3) Per-project .agent-relay folder.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-20T00:18:33.224965+01:00","updated_at":"2025-12-20T00:18:33.224965+01:00"} -{"id":"agent-relay-l99","title":"Add output filtering for cleaner logs","description":"Filter noisy patterns from logs: thinking indicators [1/418], empty ANSI-only lines, etc. Add config.filterLogs option. See docs/TMUX_IMPROVEMENTS.md for implementation details.","status":"open","priority":4,"issue_type":"feature","created_at":"2025-12-20T21:28:51.199736+01:00","updated_at":"2025-12-20T21:28:51.199736+01:00","dependencies":[{"issue_id":"agent-relay-l99","depends_on_id":"agent-relay-1ek","type":"blocks","created_at":"2025-12-20T21:29:26.987526+01:00","created_by":"daemon"}]} -{"id":"agent-relay-lto","title":"Command palette channel/chat selection","description":"","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-23T16:15:32.818842+01:00","updated_at":"2025-12-23T16:18:00.952354+01:00","closed_at":"2025-12-23T16:18:00.952354+01:00"} +{"id":"agent-relay-l99","title":"Add output filtering for cleaner logs","description":"Filter noisy patterns from logs: thinking indicators [1/418], empty ANSI-only lines, etc. Add config.filterLogs option. See docs/TMUX_IMPROVEMENTS.md for implementation details.","status":"open","priority":4,"issue_type":"feature","created_at":"2025-12-20T21:28:51.199736+01:00","updated_at":"2025-12-20T21:28:51.199736+01:00","dependencies":[{"issue_id":"agent-relay-l99","depends_on_id":"agent-relay-1ek","type":"blocks","created_at":"2025-12-20T21:29:26.987526+01:00","created_by":"daemon","metadata":"{}"}]} +{"id":"agent-relay-lfky","title":"Add 'agent thinking/processing' indicator to dashboard","description":"When an agent receives a message, the dashboard should show a visual indicator that the agent is processing/thinking. Similar to 'typing...' indicators in chat apps.\n\n**Requirements:**\n1. Backend: Track agent status (idle/processing/responding)\n2. Protocol: Agents report when they start/finish processing a message\n3. Dashboard UI: Show pulsing/animated indicator next to agent when processing\n4. Timeout: Auto-clear indicator if no response after X seconds\n\n**Implementation options:**\n- Agents send STATUS: PROCESSING / STATUS: IDLE messages\n- Or use heartbeat with embedded state\n- Or infer from message timestamps (less reliable)\n\nRequested by: Dashboard agent during v1→v2 migration planning","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-27T05:38:19.022332+01:00","updated_at":"2025-12-27T10:59:41.070383+01:00","closed_at":"2025-12-27T10:59:41.070383+01:00","close_reason":"Processing indicator implementation complete with router tracking and dashboard color support"} +{"id":"agent-relay-lls5","title":"Configure daemon to serve v2 dashboard on alt port","description":"Add daemon configuration to serve v2 dashboard (Next.js) on port 4281 while keeping v1 on 4280. This enables parallel running during migration period.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-27T05:36:49.475705+01:00","updated_at":"2025-12-27T05:36:49.475705+01:00"} +{"id":"agent-relay-lto","title":"Command palette channel/chat selection","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-23T16:15:32.818842+01:00","updated_at":"2025-12-23T16:18:00.952354+01:00","closed_at":"2025-12-23T16:18:00.952354+01:00"} {"id":"agent-relay-mcl","title":"Gemini agent cannot send messages properly","description":"Gemini agent cannot autonomously send relay messages. When Gemini outputs @relay: patterns, it enters shell mode instead of being intercepted by the relay wrapper. Manual typing works fine. This suggests a PTY parsing issue specific to how Gemini outputs the @relay: pattern - possibly timing, escaping, or output buffering differences compared to Claude.","status":"closed","priority":2,"issue_type":"bug","assignee":"LeadDev","created_at":"2025-12-20T00:29:15.797552+01:00","updated_at":"2025-12-20T21:56:07.20143+01:00","closed_at":"2025-12-20T21:56:07.20143+01:00"} {"id":"agent-relay-mkz","title":"Fix multi-line message display in dashboard","description":"Multi-line messages are not displaying properly in the dashboard UI. This is a critical UX issue affecting message readability. Need to fix the frontend rendering of message content to properly handle newlines, formatting, and multi-line text.","status":"closed","priority":1,"issue_type":"bug","assignee":"Frontend","created_at":"2025-12-25T14:11:25.663841+01:00","updated_at":"2025-12-26T11:51:07.14395+01:00","closed_at":"2025-12-26T11:51:07.14395+01:00"} -{"id":"agent-relay-n36","title":"Add metrics dashboard for system health monitoring","description":"","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-25T14:07:36.499133+01:00","updated_at":"2025-12-26T11:41:20.33149+01:00","closed_at":"2025-12-26T11:41:20.33149+01:00"} -{"id":"agent-relay-nox","title":"Dashboard not showing live agents or messages","description":"","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-23T12:11:48.614811+01:00","updated_at":"2025-12-23T12:26:51.322956+01:00","closed_at":"2025-12-23T12:26:51.322956+01:00"} +{"id":"agent-relay-mplo","title":"Parser: Fix overcapturing text in multi-line relay messages","description":"Recent parser improvements (agent-relay-6dl8 fix) are still overcapturing text in multi-line relay messages. Dashboard reports text is being included beyond message boundaries. Need to review parser.ts finishBlock() and handle parsing edge cases.","status":"closed","priority":1,"issue_type":"bug","assignee":"FullStack","created_at":"2025-12-26T21:06:42.006611+01:00","updated_at":"2025-12-27T05:43:25.432704+01:00","closed_at":"2025-12-27T05:43:25.432704+01:00","close_reason":"Fixed: Reverted parser to simpler logic - inline messages are single-line only, only indented lines continue (TUI wrapping). Multi-line messages should use [[RELAY]] block format."} +{"id":"agent-relay-n36","title":"Add metrics dashboard for system health monitoring","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-25T14:07:36.499133+01:00","updated_at":"2025-12-26T11:41:20.33149+01:00","closed_at":"2025-12-26T11:41:20.33149+01:00"} +{"id":"agent-relay-nox","title":"Dashboard not showing live agents or messages","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-23T12:11:48.614811+01:00","updated_at":"2025-12-23T12:26:51.322956+01:00","closed_at":"2025-12-23T12:26:51.322956+01:00"} {"id":"agent-relay-oiw","title":"BUG: No per-agent inbox files created","description":"Expected /tmp/agent-relay/\u003cproject\u003e/\u003cagent\u003e/inbox.md but only team/agents.json exists. Agents cannot read their inbox via file.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-20T21:46:24.826442+01:00","updated_at":"2025-12-22T14:29:46.830632+01:00","closed_at":"2025-12-22T14:29:46.830632+01:00"} {"id":"agent-relay-p0k","title":"Gemini agents cannot send relay messages","description":"Gem (Gemini CLI) can receive messages but cannot send them. Tried: -\u003erelay:, \u003e\u003erelay:, escaping with backticks, splitting lines. Messages don't get captured/sent. Possibly Gemini CLI output handling differs from Claude/Codex. Need to investigate how Gemini outputs text and whether the tmux capture-pane is picking it up correctly.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-22T13:52:43.407612+01:00","updated_at":"2025-12-22T13:55:11.068344+01:00","closed_at":"2025-12-22T13:55:11.068344+01:00"} -{"id":"agent-relay-pvx","title":"Dashboard agent detection debugging","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-23T17:14:36.376736+01:00","updated_at":"2025-12-23T17:16:13.115531+01:00","closed_at":"2025-12-23T17:16:13.115531+01:00"} +{"id":"agent-relay-pvx","title":"Dashboard agent detection debugging","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-23T17:14:36.376736+01:00","updated_at":"2025-12-23T17:16:13.115531+01:00","closed_at":"2025-12-23T17:16:13.115531+01:00"} +{"id":"agent-relay-qkey","title":"Switch default dashboard to v2","description":"After feature parity is verified and parallel running period is complete, switch the default dashboard from v1 to v2. Update agent-relay up command to serve v2 by default.","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-27T05:36:52.036031+01:00","updated_at":"2025-12-27T05:36:52.036031+01:00","dependencies":[{"issue_id":"agent-relay-qkey","depends_on_id":"agent-relay-s8hg","type":"blocks","created_at":"2025-12-27T05:37:00.746565+01:00","created_by":"daemon"},{"issue_id":"agent-relay-qkey","depends_on_id":"agent-relay-ekk9","type":"blocks","created_at":"2025-12-27T05:37:00.817799+01:00","created_by":"daemon"}]} {"id":"agent-relay-rm7","title":"Add scheduling strategies for task distribution","description":"Implement scheduling strategies for distributing tasks across agents. Start with round-robin, then add capability-based, least-loaded, and affinity strategies. Inspired by Claude-Flow's 4-strategy approach. Enables autonomous task assignment instead of manual coordination.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-23T17:03:22.014068+01:00","updated_at":"2025-12-23T17:03:22.014068+01:00"} {"id":"agent-relay-ruv","title":"Add work stealing for load balancing","description":"Implement work stealing so idle agents can claim tasks from overloaded peers. Key production feature for better resource utilization. Pattern from Claude-Flow's WorkStealingCoordinator. Requires task queue visibility and agent load metrics.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-23T17:03:28.153623+01:00","updated_at":"2025-12-23T17:03:28.153623+01:00"} +{"id":"agent-relay-s8hg","title":"Add dashboard version switch to CLI","description":"Add --dashboard=v1|v2 flag to agent-relay up command to choose which dashboard version to run. Default should remain v1 until migration is complete.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-27T05:36:51.0113+01:00","updated_at":"2025-12-27T05:36:51.0113+01:00","dependencies":[{"issue_id":"agent-relay-s8hg","depends_on_id":"agent-relay-lls5","type":"blocks","created_at":"2025-12-27T05:37:00.684918+01:00","created_by":"daemon"}]} {"id":"agent-relay-sio","title":"Add graceful degradation when relay daemon is unavailable","description":"In wrapper/tmux-wrapper.ts:195-197, daemon connection failures are silently caught. Consider: (1) Periodic reconnection attempts, (2) Queueing messages for later delivery, (3) Visual indicator in terminal showing connection status.","status":"closed","priority":2,"issue_type":"feature","assignee":"Implementer","created_at":"2025-12-20T00:17:32.600333+01:00","updated_at":"2025-12-22T17:12:29.892345+01:00","closed_at":"2025-12-22T17:12:29.892345+01:00"} -{"id":"agent-relay-t4b","title":"Add spawn agent UI to dashboard","description":"","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-25T13:20:54.452959+01:00","updated_at":"2025-12-25T13:25:05.019339+01:00","closed_at":"2025-12-25T13:25:05.019339+01:00"} +{"id":"agent-relay-t4b","title":"Add spawn agent UI to dashboard","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-25T13:20:54.452959+01:00","updated_at":"2025-12-25T13:25:05.019339+01:00","closed_at":"2025-12-25T13:25:05.019339+01:00"} {"id":"agent-relay-tun","title":"Debug output leaking to terminal during relay message send","description":"When sending relay messages (observed with relay:Gem), debug output is leaking to the terminal repeatedly. Output shows: 'relay:Gem] isInputClear: lastLine=\"\" ~/.../agent-relay (main*)' and 'ear=false' (truncated 'isInputClear=false'). Lines are split/corrupted and repeating in a loop. Likely issue in the tmux wrapper or message injection code where debug logging isn't being suppressed properly.","status":"closed","priority":1,"issue_type":"bug","assignee":"Lead","created_at":"2025-12-22T13:40:28.576361+01:00","updated_at":"2025-12-22T13:42:16.746166+01:00","closed_at":"2025-12-22T13:42:16.746166+01:00"} {"id":"agent-relay-tx9","title":"Add circuit breaker for unresponsive agents","description":"Implement circuit breaker pattern to handle unresponsive or failing agents. Auto-detect agents that stop responding, temporarily remove from routing, attempt recovery, alert on persistent failures. Prevents cascade failures in production. Pattern from Claude-Flow's CircuitBreakerManager.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-23T17:03:48.162055+01:00","updated_at":"2025-12-23T17:03:48.162055+01:00"} {"id":"agent-relay-u2z","title":"Add @-mention autocomplete with Tab in dashboard chat input","description":"When a user types '@' in the dashboard chat input, show a dropdown of available/connected agents. Tab key should autocomplete the selected agent name. This enables easy @-mentioning of agents for direct messages.","status":"closed","priority":1,"issue_type":"feature","assignee":"FE-Dev","created_at":"2025-12-23T22:52:34.749062+01:00","updated_at":"2025-12-23T22:57:11.634365+01:00","closed_at":"2025-12-23T22:57:11.634365+01:00"} @@ -115,17 +129,17 @@ {"id":"agent-relay-v57","title":"No message expiration/cleanup in SQLite storage","description":"SQLite adapter has no TTL or cleanup mechanism for old messages. Over time, the database will grow unbounded. Add: (1) Configurable message retention period, (2) Automatic cleanup job, (3) Index on ts column is there but no cleanup uses it.","status":"closed","priority":2,"issue_type":"task","assignee":"Lead","created_at":"2025-12-20T00:18:01.86766+01:00","updated_at":"2025-12-22T17:17:41.867951+01:00","closed_at":"2025-12-22T17:17:41.867951+01:00"} {"id":"agent-relay-v7f","title":"Add conflict detection and resolution","description":"Implement conflict detection when multiple agents work on same files/resources. Add resolution strategies: priority-based, timestamp-based, voting-based. Prevents race conditions in production. Pattern from Claude-Flow's ConflictResolver and OptimisticLockManager.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-23T17:03:34.498546+01:00","updated_at":"2025-12-23T17:03:34.498546+01:00"} {"id":"agent-relay-vxc","title":"Navigation consistency between bridge and project views","description":"Ensure navigation patterns are consistent when switching between bridge and project dashboard views","status":"closed","priority":2,"issue_type":"task","assignee":"FE-Dev","created_at":"2025-12-24T11:47:46.62539+01:00","updated_at":"2025-12-24T12:00:15.805473+01:00","closed_at":"2025-12-24T12:00:15.805473+01:00"} -{"id":"agent-relay-wsd","title":"URGENT: Gemini interprets relay messages as shell commands","description":"","status":"closed","priority":0,"issue_type":"bug","created_at":"2025-12-22T14:23:05.989732+01:00","updated_at":"2025-12-22T14:24:18.714626+01:00","closed_at":"2025-12-22T14:24:18.714626+01:00"} +{"id":"agent-relay-wsd","title":"URGENT: Gemini interprets relay messages as shell commands","status":"closed","priority":0,"issue_type":"bug","created_at":"2025-12-22T14:23:05.989732+01:00","updated_at":"2025-12-22T14:24:18.714626+01:00","closed_at":"2025-12-22T14:24:18.714626+01:00"} {"id":"agent-relay-xn1","title":"Add remote access via Cloudflare tunnels","description":"Enable remote/mobile access to agent-relay dashboard and control. Use Cloudflare tunnels (like Maestro) for secure access without port forwarding. Include QR code generation for easy mobile connection. Major gap vs competitors - AI Maestro and Maestro both have this.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-23T17:04:28.788302+01:00","updated_at":"2025-12-23T17:04:28.788302+01:00"} {"id":"agent-relay-ylf","title":"Add knowledge graph for persistent memory","description":"Implement knowledge graph storage (like Mimir's Neo4j approach) for persistent cross-session context. Store entities, relationships, and semantic embeddings. Enable agents to learn from past sessions and share knowledge. Could use SQLite with FTS5 or optional Neo4j adapter.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-23T17:05:08.367875+01:00","updated_at":"2025-12-23T17:05:08.367875+01:00"} {"id":"agent-relay-ytj","title":"Filter chat view to show only messages with selected agent","description":"When clicking on a particular agent in the dashboard sidebar, the chat view should filter to only show messages between Dashboard and that specific agent. This provides a focused conversation view instead of showing all messages.","status":"closed","priority":2,"issue_type":"feature","assignee":"FE-Dev","created_at":"2025-12-23T22:53:31.325805+01:00","updated_at":"2025-12-23T22:58:41.407339+01:00","closed_at":"2025-12-23T22:58:41.407339+01:00"} {"id":"agent-relay-ytk","title":"Simplify agent-relay CLI interface","description":"The CLI has too many commands. Consolidate into a clean, simple interface:\n\n1. `relay start` should also kick off the dashboard automatically\n2. Merge redundant team-* commands\n3. Remove rarely-used commands or make them subcommands\n4. Target: 5-7 top-level commands max\n\nCurrent commands to evaluate:\n- start, stop, status (keep)\n- wrap (keep)\n- project (keep or merge into status)\n- send (keep)\n- team-setup, team-status, team-send, team-check, team-listen, team-start (consolidate)\n- msg-read (make subcommand or integrate)\n- dashboard (merge into start)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-22T21:51:14.010849+01:00","updated_at":"2025-12-22T21:51:14.010849+01:00","closed_at":"2025-12-19T22:08:44.107992+01:00"} {"id":"agent-relay-ytl","title":"Add cross-project message syntax to parser","description":"Extend the parser (src/wrapper/parser.ts) to recognize @relay:project:agent syntax for cross-project messaging. Currently only @relay:agent is parsed. Need to add pattern matching for the project:agent format.","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-12-21T10:45:00Z","updated_at":"2025-12-24T11:46:51.622328+01:00","closed_at":"2025-12-24T11:46:51.622328+01:00"} {"id":"agent-relay-ytm","title":"Enhance dashboard for multi-project bridge view","description":"Add a bridge view to the dashboard that shows all connected projects, their leads, workers, and cross-project message flow. Should be accessible at /bridge?projects=... See docs/DESIGN_BRIDGE_STAFFING.md for mockups.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-21T10:45:00Z","updated_at":"2025-12-22T22:04:18.511591+01:00","closed_at":"2025-12-22T22:04:18.511591+01:00"} -{"id":"agent-relay-ytn","title":"Shadow agent pairing: CLI support","description":"Add --shadow and --shadow-role CLI flags to spawn paired agents. Example: `agent-relay -n Lead --shadow Auditor claude` spawns Lead with an Auditor shadow that observes and provides feedback. The shadow receives all messages the primary sends/receives.","status":"open","priority":1,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-25T08:00:00Z","labels":["shadow-agents"]} -{"id":"agent-relay-yto","title":"Shadow agent pairing: Config file support","description":"Support shadow agent configuration in .agent-relay.json. Define pairs (primary-\u003eshadow mappings) and roles (prompt templates like performance-auditor, integration-tester). Config format: { pairs: { Lead: { shadow: 'Auditor', shadowRole: 'performance-auditor' } }, roles: { 'performance-auditor': { prompt: '...', speakOn: ['SESSION_END'] } } }","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-25T08:00:00Z","labels":["shadow-agents"],"dependencies":[{"issue_id":"agent-relay-yto","depends_on_id":"agent-relay-ytn","type":"blocks","created_at":"2025-12-25T08:00:00Z","created_by":"daemon"}]} -{"id":"agent-relay-ytp","title":"Shadow agent pairing: spawnWithShadow() API","description":"Programmatic API for spawning paired agents. Example: `const { primary, shadow } = await spawnWithShadow({ primary: { name: 'Lead', command: 'claude' }, shadow: { name: 'Auditor', role: 'performance-auditor', speakOn: ['SESSION_END'] } })`. Returns handles to both agents for orchestration.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-25T08:00:00Z","labels":["shadow-agents"],"dependencies":[{"issue_id":"agent-relay-ytp","depends_on_id":"agent-relay-ytn","type":"blocks","created_at":"2025-12-25T08:00:00Z","created_by":"daemon"}]} -{"id":"agent-relay-ytq","title":"Shadow agent pairing: Built-in roles","description":"Ship built-in shadow roles: (1) performance-auditor - observes silently, provides feedback at session end; (2) integration-tester - writes tests as primary writes code; (3) code-reviewer - reviews changes before commit; (4) security-auditor - flags security issues in real-time. Roles define prompts and speakOn triggers.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-25T08:00:00Z","labels":["shadow-agents"],"dependencies":[{"issue_id":"agent-relay-ytq","depends_on_id":"agent-relay-yto","type":"blocks","created_at":"2025-12-25T08:00:00Z","created_by":"daemon"}]} +{"id":"agent-relay-ytn","title":"Shadow agent pairing: CLI support","description":"Add --shadow and --shadow-role CLI flags to spawn paired agents. Example: `agent-relay -n Lead --shadow Auditor claude` spawns Lead with an Auditor shadow that observes and provides feedback. The shadow receives all messages the primary sends/receives.","status":"in_progress","priority":1,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-26T20:27:51.517714+01:00","labels":["shadow-agents"]} +{"id":"agent-relay-yto","title":"Shadow agent pairing: Config file support","description":"Support shadow agent configuration in .agent-relay.json. Define pairs (primary-\u003eshadow mappings) and roles (prompt templates like performance-auditor, integration-tester). Config format: { pairs: { Lead: { shadow: 'Auditor', shadowRole: 'performance-auditor' } }, roles: { 'performance-auditor': { prompt: '...', speakOn: ['SESSION_END'] } } }","status":"open","priority":1,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-27T10:56:12.797952+01:00","labels":["shadow-agents"],"dependencies":[{"issue_id":"agent-relay-yto","depends_on_id":"agent-relay-ytn","type":"blocks","created_at":"2025-12-25T08:00:00Z","created_by":"daemon","metadata":"{}"}]} +{"id":"agent-relay-ytp","title":"Shadow agent pairing: spawnWithShadow() API","description":"Programmatic API for spawning paired agents. Example: `const { primary, shadow } = await spawnWithShadow({ primary: { name: 'Lead', command: 'claude' }, shadow: { name: 'Auditor', role: 'performance-auditor', speakOn: ['SESSION_END'] } })`. Returns handles to both agents for orchestration.","status":"open","priority":1,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-27T10:56:14.164476+01:00","labels":["shadow-agents"],"dependencies":[{"issue_id":"agent-relay-ytp","depends_on_id":"agent-relay-ytn","type":"blocks","created_at":"2025-12-25T08:00:00Z","created_by":"daemon","metadata":"{}"}]} +{"id":"agent-relay-ytq","title":"Shadow agent pairing: Built-in roles","description":"Ship built-in shadow roles: (1) performance-auditor - observes silently, provides feedback at session end; (2) integration-tester - writes tests as primary writes code; (3) code-reviewer - reviews changes before commit; (4) security-auditor - flags security issues in real-time. Roles define prompts and speakOn triggers.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-25T08:00:00Z","labels":["shadow-agents"],"dependencies":[{"issue_id":"agent-relay-ytq","depends_on_id":"agent-relay-yto","type":"blocks","created_at":"2025-12-25T08:00:00Z","created_by":"daemon","metadata":"{}"}]} {"id":"agent-relay-ytr","title":"Shadow agent pairing: Message routing","description":"Implement shadow message routing: shadows receive copies of all messages their primary sends/receives. Add 'shadow' subscription type in router. Shadows can be configured to speakOn specific triggers (SESSION_END, CODE_WRITTEN, REVIEW_REQUEST) or stay silent until asked.","status":"open","priority":1,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-25T08:00:00Z","labels":["shadow-agents"]} {"id":"agent-relay-yts","title":"Wire up spawn/release command handling in lead mode","description":"Lead agents can output @relay:spawn and @relay:release commands, but the handler needs to be wired up to actually call the AgentSpawner. The spawner exists in src/bridge/spawner.ts, need to intercept these commands in the TmuxWrapper or add a message handler.","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-12-21T10:45:00Z","updated_at":"2025-12-24T15:09:22.953849+01:00","closed_at":"2025-12-24T15:09:22.953849+01:00"} {"id":"agent-relay-ytt","title":"Implement threaded conversations (Slack-style)","description":"Add thread replies with parent_message_id tracking in storage, nested thread UI display in dashboard, thread notification badges, and expand/collapse thread views. Backend: add parent_message_id column, thread reply routing. Frontend: inline thread expansion, reply count badges, thread-specific compose.","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-12-23T12:00:00Z","updated_at":"2025-12-23T16:37:22.987643+01:00","closed_at":"2025-12-23T16:37:22.987643+01:00"} diff --git a/.gitignore b/.gitignore index 5f8d2935f..ebd36a280 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ node_modules/ # Build output dist/ +# Bundled tmux binary (downloaded at install time) +bin/tmux + # TypeScript build info (if enabled later) *.tsbuildinfo diff --git a/bin/.gitkeep b/bin/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/package.json b/package.json index 105174391..22ccb0ee6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agent-relay", - "version": "1.0.9", + "version": "1.0.10", "description": "Real-time agent-to-agent communication system", "type": "module", "main": "dist/index.js", @@ -10,6 +10,8 @@ }, "files": [ "dist/", + "bin/", + "scripts/", "install.sh", "README.md", "LICENSE", @@ -19,7 +21,7 @@ "access": "public" }, "scripts": { - "postinstall": "npm rebuild better-sqlite3", + "postinstall": "npm rebuild better-sqlite3 && node scripts/postinstall.js", "build": "npm run clean && tsc && npm run build:frontend", "build:frontend": "esbuild src/dashboard/frontend/app.ts --bundle --outfile=src/dashboard/public/js/app.js --format=esm --target=es2022 --minify --sourcemap", "postbuild": "cp -r src/dashboard/public dist/dashboard/ && chmod +x dist/cli/index.js", diff --git a/scripts/postinstall.js b/scripts/postinstall.js new file mode 100644 index 000000000..11e6f8a98 --- /dev/null +++ b/scripts/postinstall.js @@ -0,0 +1,225 @@ +#!/usr/bin/env node +/** + * Postinstall Script for agent-relay + * + * This script runs after npm install to: + * 1. Rebuild native modules (better-sqlite3) + * 2. Install tmux binary if not available on the system + * + * The tmux binary is installed within the package itself (bin/tmux), + * making it portable and not requiring global installation. + */ + +import { execSync } from 'node:child_process'; +import os from 'node:os'; +import path from 'node:path'; +import fs from 'node:fs'; +import https from 'node:https'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +/** Get package root directory (parent of scripts/) */ +function getPackageRoot() { + return path.resolve(__dirname, '..'); +} + +/** Installation directory (within the package) */ +function getInstallDir() { + return path.join(getPackageRoot(), 'bin'); +} + +// Colors for console output +const colors = { + reset: '\x1b[0m', + green: '\x1b[32m', + yellow: '\x1b[33m', + blue: '\x1b[34m', + red: '\x1b[31m', +}; + +function info(msg) { + console.log(`${colors.blue}[info]${colors.reset} ${msg}`); +} + +function success(msg) { + console.log(`${colors.green}[success]${colors.reset} ${msg}`); +} + +function warn(msg) { + console.log(`${colors.yellow}[warn]${colors.reset} ${msg}`); +} + +function error(msg) { + console.log(`${colors.red}[error]${colors.reset} ${msg}`); +} + +/** + * Check if tmux is available on the system + */ +function hasSystemTmux() { + try { + execSync('which tmux', { stdio: 'pipe' }); + return true; + } catch { + return false; + } +} + +/** + * Get platform identifier for tmux-builds + */ +function getPlatformId() { + const platform = os.platform(); + const arch = os.arch(); + + if (platform === 'darwin') { + return arch === 'arm64' ? 'macos-arm64' : 'macos-x86_64'; + } else if (platform === 'linux') { + return arch === 'arm64' ? 'linux-arm64' : 'linux-x86_64'; + } + + return null; +} + +/** + * Download file with redirect support + */ +function downloadFile(url, destPath) { + return new Promise((resolve, reject) => { + const file = fs.createWriteStream(destPath); + + const request = (currentUrl, redirectCount = 0) => { + if (redirectCount > 5) { + reject(new Error('Too many redirects')); + return; + } + + const urlObj = new URL(currentUrl); + const options = { + hostname: urlObj.hostname, + path: urlObj.pathname + urlObj.search, + headers: { + 'User-Agent': 'agent-relay-installer', + }, + }; + + https + .get(options, (response) => { + if (response.statusCode === 302 || response.statusCode === 301) { + const location = response.headers.location; + if (location) { + request(location, redirectCount + 1); + return; + } + } + + if (response.statusCode !== 200) { + reject(new Error(`HTTP ${response.statusCode}`)); + return; + } + + response.pipe(file); + file.on('finish', () => { + file.close(); + resolve(); + }); + }) + .on('error', reject); + }; + + request(url); + }); +} + +/** + * Install tmux binary + */ +async function installTmux() { + const TMUX_VERSION = '3.6a'; + const INSTALL_DIR = getInstallDir(); + const tmuxPath = path.join(INSTALL_DIR, 'tmux'); + + // Check if already installed + if (fs.existsSync(tmuxPath)) { + info('Bundled tmux already installed'); + return true; + } + + const platformId = getPlatformId(); + if (!platformId) { + const platform = os.platform(); + warn(`Unsupported platform: ${platform} ${os.arch()}`); + if (platform === 'win32') { + warn('tmux requires WSL (Windows Subsystem for Linux)'); + warn('Install WSL first, then run: sudo apt install tmux'); + } else { + warn('Please install tmux manually: https://github.com/tmux/tmux/wiki/Installing'); + } + return false; + } + + info(`Installing tmux ${TMUX_VERSION} for ${platformId}...`); + + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-relay-tmux-')); + const archiveName = `tmux-${TMUX_VERSION}-${platformId}.tar.gz`; + const archivePath = path.join(tmpDir, archiveName); + const downloadUrl = `https://github.com/tmux/tmux-builds/releases/download/v${TMUX_VERSION}/${archiveName}`; + + try { + info('Downloading tmux binary...'); + await downloadFile(downloadUrl, archivePath); + + info('Extracting...'); + execSync(`tar -xzf "${archivePath}" -C "${tmpDir}"`, { stdio: 'pipe' }); + + const extractedTmux = path.join(tmpDir, 'tmux'); + if (!fs.existsSync(extractedTmux)) { + throw new Error('tmux binary not found in archive'); + } + + fs.mkdirSync(INSTALL_DIR, { recursive: true }); + fs.copyFileSync(extractedTmux, tmuxPath); + fs.chmodSync(tmuxPath, 0o755); + + success(`Installed tmux to ${tmuxPath}`); + return true; + } catch (err) { + error(`Failed to install tmux: ${err.message}`); + warn('Please install tmux manually, then reinstall: npm install agent-relay'); + return false; + } finally { + try { + fs.rmSync(tmpDir, { recursive: true, force: true }); + } catch { + // Ignore + } + } +} + +/** + * Main postinstall routine + */ +async function main() { + // Skip in CI environments where tmux isn't needed + if (process.env.CI === 'true') { + info('Skipping tmux install in CI environment'); + return; + } + + // Check if system tmux is available + if (hasSystemTmux()) { + info('System tmux found'); + return; + } + + // Try to install bundled tmux + await installTmux(); +} + +main().catch((err) => { + // Don't fail the install if tmux installation fails + // User can still install tmux manually + warn(`Postinstall warning: ${err.message}`); +}); diff --git a/src/bridge/spawner.ts b/src/bridge/spawner.ts index 473469eb1..977e11b9a 100644 --- a/src/bridge/spawner.ts +++ b/src/bridge/spawner.ts @@ -7,12 +7,15 @@ import fs from 'node:fs'; import path from 'node:path'; import { execAsync, sleep, escapeForTmux } from './utils.js'; import { getProjectPaths } from '../utils/project-namespace.js'; +import { getTmuxPath } from '../utils/tmux-resolver.js'; import type { SpawnRequest, SpawnResult, WorkerInfo } from './types.js'; export class AgentSpawner { private activeWorkers: Map = new Map(); private tmuxSession: string; private agentsPath: string; + private projectRoot: string; + private tmuxPath: string; // Resolved path to tmux binary constructor( projectRoot: string, @@ -22,21 +25,23 @@ export class AgentSpawner { this.projectRoot = paths.projectRoot; this.agentsPath = path.join(paths.teamDir, 'agents.json'); + // Resolve tmux path (will throw TmuxNotFoundError if tmux unavailable) + this.tmuxPath = getTmuxPath(); + // Default session name based on project this.tmuxSession = tmuxSession || 'relay-workers'; } - private projectRoot: string; /** * Ensure the worker tmux session exists */ async ensureSession(): Promise { try { - await execAsync(`tmux has-session -t ${this.tmuxSession} 2>/dev/null`); + await execAsync(`"${this.tmuxPath}" has-session -t ${this.tmuxSession} 2>/dev/null`); } catch { // Session doesn't exist, create it await execAsync( - `tmux new-session -d -s ${this.tmuxSession} -c "${this.projectRoot}"` + `"${this.tmuxPath}" new-session -d -s ${this.tmuxSession} -c "${this.projectRoot}"` ); console.log(`[spawner] Created session ${this.tmuxSession}`); } @@ -64,7 +69,7 @@ export class AgentSpawner { // Create new window for worker const windowName = name; - const newWindowCmd = `tmux new-window -t ${this.tmuxSession} -n ${windowName} -c "${this.projectRoot}"`; + const newWindowCmd = `"${this.tmuxPath}" new-window -t ${this.tmuxSession} -n ${windowName} -c "${this.projectRoot}"`; if (debug) console.log(`[spawner:debug] Creating window: ${newWindowCmd}`); await execAsync(newWindowCmd); @@ -90,7 +95,7 @@ export class AgentSpawner { if (debug) console.log(`[spawner:debug] Agent command: ${cmd}`); // Send the command - const sendCmd = `tmux send-keys -t ${this.tmuxSession}:${windowName} '${cmd}' Enter`; + const sendCmd = `"${this.tmuxPath}" send-keys -t ${this.tmuxSession}:${windowName} '${cmd}' Enter`; if (debug) console.log(`[spawner:debug] Sending: ${sendCmd}`); await execAsync(sendCmd); @@ -100,7 +105,7 @@ export class AgentSpawner { const error = `Worker ${name} failed to register within 30s`; console.error(`[spawner] ${error}`); // Clean up the tmux window to avoid orphaned workers - await execAsync(`tmux kill-window -t ${this.tmuxSession}:${windowName}`).catch(() => {}); + await execAsync(`"${this.tmuxPath}" kill-window -t ${this.tmuxSession}:${windowName}`).catch(() => {}); return { success: false, name, @@ -113,11 +118,11 @@ export class AgentSpawner { const escapedTask = escapeForTmux(task); if (debug) console.log(`[spawner:debug] Injecting task: ${escapedTask.substring(0, 50)}...`); await execAsync( - `tmux send-keys -t ${this.tmuxSession}:${windowName} -l "${escapedTask}"` + `"${this.tmuxPath}" send-keys -t ${this.tmuxSession}:${windowName} -l "${escapedTask}"` ); await sleep(100); await execAsync( - `tmux send-keys -t ${this.tmuxSession}:${windowName} Enter` + `"${this.tmuxPath}" send-keys -t ${this.tmuxSession}:${windowName} Enter` ); } @@ -163,7 +168,7 @@ export class AgentSpawner { try { // Send exit command gracefully await execAsync( - `tmux send-keys -t ${worker.window} '/exit' Enter` + `"${this.tmuxPath}" send-keys -t ${worker.window} '/exit' Enter` ).catch(() => {}); // Wait a bit for graceful shutdown @@ -171,7 +176,7 @@ export class AgentSpawner { // Kill the window await execAsync( - `tmux kill-window -t ${worker.window}` + `"${this.tmuxPath}" kill-window -t ${worker.window}` ).catch(() => {}); this.activeWorkers.delete(name); diff --git a/src/cli/index.ts b/src/cli/index.ts index cff880324..a8573303b 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -16,6 +16,7 @@ import { config as dotenvConfig } from 'dotenv'; import { Daemon } from '../daemon/server.js'; import { RelayClient } from '../wrapper/client.js'; import { generateAgentName } from '../utils/name-generator.js'; +import { getTmuxPath } from '../utils/tmux-resolver.js'; import fs from 'node:fs'; import path from 'node:path'; import { promisify } from 'node:util'; @@ -540,6 +541,34 @@ program console.log(`agent-relay v${VERSION}`); }); +// check-tmux - Check tmux availability (hidden - for diagnostics) +program + .command('check-tmux', { hidden: true }) + .description('Check tmux availability and version') + .action(async () => { + const { resolveTmux, checkTmuxVersion } = await import('../utils/tmux-resolver.js'); + + const info = resolveTmux(); + if (!info) { + console.log('tmux: NOT FOUND'); + console.log(''); + console.log('Install tmux, then reinstall agent-relay:'); + console.log(' brew install tmux # macOS'); + console.log(' apt install tmux # Ubuntu/Debian'); + console.log(' npm install agent-relay # Reinstall to bundle tmux'); + process.exit(1); + } + + console.log(`tmux: ${info.path}`); + console.log(`Version: ${info.version}`); + console.log(`Source: ${info.isBundled ? 'bundled' : 'system'}`); + + const versionCheck = checkTmuxVersion(); + if (!versionCheck.ok) { + console.log(`Warning: tmux ${versionCheck.minimum}+ recommended`); + } + }); + // bridge - Multi-project orchestration program .command('bridge') @@ -839,9 +868,10 @@ program // Kill orphaned sessions let killed = 0; + const tmuxPath = getTmuxPath(); for (const session of orphaned) { try { - await execAsync(`tmux kill-session -t ${session.sessionName}`); + await execAsync(`"${tmuxPath}" kill-session -t ${session.sessionName}`); killed++; console.log(`Killed: ${session.sessionName}`); } catch (err) { @@ -860,7 +890,8 @@ interface RelaySessionInfo { async function discoverRelaySessions(): Promise { try { - const { stdout } = await execAsync('tmux list-sessions -F "#{session_name}"'); + const tmuxPath = getTmuxPath(); + const { stdout } = await execAsync(`"${tmuxPath}" list-sessions -F "#{session_name}"`); const sessionNames = stdout .split('\n') .map(s => s.trim()) @@ -879,7 +910,7 @@ async function discoverRelaySessions(): Promise { let cwd: string | undefined; try { const { stdout: cwdOut } = await execAsync( - `tmux display-message -t ${session.sessionName} -p '#{pane_current_path}'` + `"${tmuxPath}" display-message -t ${session.sessionName} -p '#{pane_current_path}'` ); cwd = cwdOut.trim() || undefined; } catch { @@ -1031,9 +1062,10 @@ program .option('--json', 'Output as JSON') .action(async (options: { json?: boolean }) => { try { + const tmuxPath = getTmuxPath(); // Check if worker session exists try { - await execAsync(`tmux has-session -t ${WORKER_SESSION} 2>/dev/null`); + await execAsync(`"${tmuxPath}" has-session -t ${WORKER_SESSION} 2>/dev/null`); } catch { if (options.json) { console.log(JSON.stringify({ workers: [], session: null })); @@ -1045,7 +1077,7 @@ program // List windows in the worker session const { stdout } = await execAsync( - `tmux list-windows -t ${WORKER_SESSION} -F "#{window_index}|#{window_name}|#{pane_current_command}|#{window_activity}"` + `"${tmuxPath}" list-windows -t ${WORKER_SESSION} -F "#{window_index}|#{window_name}|#{pane_current_command}|#{window_activity}"` ); const workers = stdout @@ -1103,11 +1135,12 @@ program .option('-n, --lines ', 'Number of lines to show', '50') .option('-f, --follow', 'Follow output (like tail -f)') .action(async (name: string, options: { lines?: string; follow?: boolean }) => { + const tmuxPath = getTmuxPath(); const window = `${WORKER_SESSION}:${name}`; try { // Check if window exists - await execAsync(`tmux has-session -t ${window} 2>/dev/null`); + await execAsync(`"${tmuxPath}" has-session -t ${window} 2>/dev/null`); } catch { console.error(`Worker "${name}" not found`); console.log(`Run 'agent-relay workers' to see available workers`); @@ -1122,7 +1155,7 @@ program let lastContent = ''; const poll = async () => { try { - const { stdout } = await execAsync(`tmux capture-pane -t ${window} -p -S -100`); + const { stdout } = await execAsync(`"${tmuxPath}" capture-pane -t ${window} -p -S -100`); if (stdout !== lastContent) { // Print only new lines const newContent = stdout.replace(lastContent, ''); @@ -1149,7 +1182,7 @@ program } else { try { const lines = parseInt(options.lines || '50', 10); - const { stdout } = await execAsync(`tmux capture-pane -t ${window} -p -S -${lines}`); + const { stdout } = await execAsync(`"${tmuxPath}" capture-pane -t ${window} -p -S -${lines}`); console.log(`Output from ${window} (last ${lines} lines):`); console.log('─'.repeat(50)); console.log(stdout || '(empty)'); @@ -1165,11 +1198,12 @@ program .description('Attach to a spawned worker tmux window') .argument('', 'Worker name') .action(async (name: string) => { + const tmuxPath = getTmuxPath(); const window = `${WORKER_SESSION}:${name}`; try { // Check if window exists - await execAsync(`tmux has-session -t ${window} 2>/dev/null`); + await execAsync(`"${tmuxPath}" has-session -t ${window} 2>/dev/null`); } catch { console.error(`Worker "${name}" not found`); console.log(`Run 'agent-relay workers' to see available workers`); @@ -1181,7 +1215,7 @@ program // Spawn tmux attach as a child process with stdio inherited const { spawn } = await import('child_process'); - const child = spawn('tmux', ['attach-session', '-t', window], { + const child = spawn(tmuxPath, ['attach-session', '-t', window], { stdio: 'inherit', }); @@ -1197,11 +1231,12 @@ program .argument('', 'Worker name') .option('--force', 'Skip graceful shutdown, kill immediately') .action(async (name: string, options: { force?: boolean }) => { + const tmuxPath = getTmuxPath(); const window = `${WORKER_SESSION}:${name}`; try { // Check if window exists - await execAsync(`tmux has-session -t ${window} 2>/dev/null`); + await execAsync(`"${tmuxPath}" has-session -t ${window} 2>/dev/null`); } catch { console.error(`Worker "${name}" not found`); console.log(`Run 'agent-relay workers' to see available workers`); @@ -1212,7 +1247,7 @@ program // Try graceful shutdown first console.log(`Sending /exit to ${name}...`); try { - await execAsync(`tmux send-keys -t ${window} '/exit' Enter`); + await execAsync(`"${tmuxPath}" send-keys -t ${window} '/exit' Enter`); // Wait for graceful shutdown await new Promise(r => setTimeout(r, 2000)); } catch { @@ -1222,7 +1257,7 @@ program // Kill the window try { - await execAsync(`tmux kill-window -t ${window}`); + await execAsync(`"${tmuxPath}" kill-window -t ${window}`); console.log(`Killed worker: ${name}`); } catch (err) { console.error(`Failed to kill ${name}:`, (err as Error).message); @@ -1236,9 +1271,10 @@ program .description('Show worker tmux session details') .action(async () => { try { + const tmuxPath = getTmuxPath(); // Check if session exists try { - await execAsync(`tmux has-session -t ${WORKER_SESSION} 2>/dev/null`); + await execAsync(`"${tmuxPath}" has-session -t ${WORKER_SESSION} 2>/dev/null`); } catch { console.log(`Session "${WORKER_SESSION}" does not exist`); console.log('Spawn a worker to create it.'); @@ -1250,14 +1286,14 @@ program // Get session info const { stdout: sessionInfo } = await execAsync( - `tmux display-message -t ${WORKER_SESSION} -p "Created: #{session_created_string}\\nWindows: #{session_windows}\\nAttached: #{?session_attached,yes,no}"` + `"${tmuxPath}" display-message -t ${WORKER_SESSION} -p "Created: #{session_created_string}\\nWindows: #{session_windows}\\nAttached: #{?session_attached,yes,no}"` ); console.log(sessionInfo); // List windows console.log('\nWindows:'); const { stdout: windows } = await execAsync( - `tmux list-windows -t ${WORKER_SESSION} -F " #{window_index}: #{window_name} (#{pane_current_command})"` + `"${tmuxPath}" list-windows -t ${WORKER_SESSION} -F " #{window_index}: #{window_name} (#{pane_current_command})"` ); console.log(windows || ' (none)'); diff --git a/src/dashboard/server.ts b/src/dashboard/server.ts index 19dd970f4..150bc5be6 100644 --- a/src/dashboard/server.ts +++ b/src/dashboard/server.ts @@ -99,6 +99,20 @@ export async function startDashboard( console.log('__dirname:', __dirname); const publicDir = path.join(__dirname, 'public'); console.log('Public dir:', publicDir); + + // Verify public directory exists and contains expected files + if (!fs.existsSync(publicDir)) { + console.error(`[dashboard] ERROR: Public directory not found: ${publicDir}`); + } else { + const files = fs.readdirSync(publicDir); + console.log('Public dir contents:', files.join(', ')); + if (!files.includes('metrics.html')) { + console.error('[dashboard] WARNING: metrics.html not found in public directory'); + } + if (!files.includes('bridge.html')) { + console.error('[dashboard] WARNING: bridge.html not found in public directory'); + } + } const storage: StorageAdapter | undefined = dbPath ? new SqliteStorageAdapter({ dbPath }) : undefined; @@ -889,12 +903,24 @@ export async function startDashboard( // Metrics view route - serves metrics.html app.get('/metrics', (req, res) => { - res.sendFile(path.join(publicDir, 'metrics.html')); + const filePath = path.join(publicDir, 'metrics.html'); + res.sendFile(filePath, (err) => { + if (err) { + console.error(`[dashboard] Failed to serve metrics.html from ${filePath}:`, err.message); + res.status(404).send('Metrics page not found'); + } + }); }); // Bridge view route - serves bridge.html app.get('/bridge', (req, res) => { - res.sendFile(path.join(publicDir, 'bridge.html')); + const filePath = path.join(publicDir, 'bridge.html'); + res.sendFile(filePath, (err) => { + if (err) { + console.error(`[dashboard] Failed to serve bridge.html from ${filePath}:`, err.message); + res.status(404).send('Bridge page not found'); + } + }); }); // Bridge API endpoint - returns multi-project data diff --git a/src/utils/tmux-resolver.ts b/src/utils/tmux-resolver.ts new file mode 100644 index 000000000..b648fd9ab --- /dev/null +++ b/src/utils/tmux-resolver.ts @@ -0,0 +1,207 @@ +/** + * Tmux Binary Resolver + * + * Locates tmux binary with fallback to bundled version. + * Priority: + * 1. System tmux (in PATH) + * 2. Bundled tmux within the agent-relay package (bin/tmux) + */ + +import { execSync } from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; +import os from 'node:os'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +/** + * Get the package root directory (where agent-relay is installed) + * This works whether we're in dist/utils/ or src/utils/ + */ +function getPackageRoot(): string { + // Navigate up from dist/utils or src/utils to package root + return path.resolve(__dirname, '..', '..'); +} + +/** Path where bundled tmux binary is installed (within the package) */ +export function getBundledTmuxDir(): string { + return path.join(getPackageRoot(), 'bin'); +} + +export function getBundledTmuxPath(): string { + return path.join(getBundledTmuxDir(), 'tmux'); +} + +// Legacy exports for backwards compatibility +export const BUNDLED_TMUX_DIR = getBundledTmuxDir(); +export const BUNDLED_TMUX_PATH = getBundledTmuxPath(); + +/** Minimum supported tmux version */ +export const MIN_TMUX_VERSION = '3.0'; + +export interface TmuxInfo { + /** Full path to tmux binary */ + path: string; + /** Version string (e.g., "3.6a") */ + version: string; + /** Whether this is the bundled version */ + isBundled: boolean; +} + +/** + * Check if tmux exists at a given path and get its version + */ +function getTmuxVersion(tmuxPath: string): string | null { + try { + const output = execSync(`"${tmuxPath}" -V`, { + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + }); + // Output format: "tmux 3.6a" or similar + const match = output.trim().match(/tmux\s+(\d+\.\d+\w?)/i); + return match ? match[1] : null; + } catch { + return null; + } +} + +/** + * Find tmux in system PATH + */ +function findSystemTmux(): string | null { + try { + const output = execSync('which tmux', { + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + }); + return output.trim() || null; + } catch { + return null; + } +} + +/** + * Resolve tmux binary path with fallback to bundled version. + * Returns null if tmux is not available. + */ +export function resolveTmux(): TmuxInfo | null { + // 1. Check system tmux first + const systemPath = findSystemTmux(); + if (systemPath) { + const version = getTmuxVersion(systemPath); + if (version) { + return { + path: systemPath, + version, + isBundled: false, + }; + } + } + + // 2. Check bundled tmux (within the package) + const bundledPath = getBundledTmuxPath(); + if (fs.existsSync(bundledPath)) { + const version = getTmuxVersion(bundledPath); + if (version) { + return { + path: bundledPath, + version, + isBundled: true, + }; + } + } + + return null; +} + +/** + * Get the tmux command to use. Throws if tmux is not available. + */ +export function getTmuxPath(): string { + const info = resolveTmux(); + if (!info) { + throw new TmuxNotFoundError(); + } + return info.path; +} + +/** + * Check if tmux is available (either system or bundled) + */ +export function isTmuxAvailable(): boolean { + return resolveTmux() !== null; +} + +/** + * Get platform identifier for downloading binaries + */ +export function getPlatformIdentifier(): string | null { + const platform = os.platform(); + const arch = os.arch(); + + if (platform === 'darwin') { + return arch === 'arm64' ? 'macos-arm64' : 'macos-x86_64'; + } else if (platform === 'linux') { + return arch === 'arm64' ? 'linux-arm64' : 'linux-x86_64'; + } + + // Unsupported platform + return null; +} + +/** + * Error thrown when tmux is not available + */ +export class TmuxNotFoundError extends Error { + constructor() { + const platformInstructions = (() => { + switch (os.platform()) { + case 'darwin': + return ' macOS: brew install tmux'; + case 'linux': + return ' Ubuntu/Debian: sudo apt install tmux\n Fedora: sudo dnf install tmux\n Arch: sudo pacman -S tmux'; + case 'win32': + return ' Windows: tmux requires WSL (Windows Subsystem for Linux)\n Install WSL, then: sudo apt install tmux'; + default: + return ' See: https://github.com/tmux/tmux/wiki/Installing'; + } + })(); + + super( + `tmux is required but not found.\n\nInstall tmux:\n${platformInstructions}\n\nThen reinstall agent-relay: npm install agent-relay` + ); + this.name = 'TmuxNotFoundError'; + } +} + +/** + * Parse version string to compare versions + */ +function parseVersion(version: string): { major: number; minor: number } { + const match = version.match(/(\d+)\.(\d+)/); + if (!match) { + return { major: 0, minor: 0 }; + } + return { major: parseInt(match[1], 10), minor: parseInt(match[2], 10) }; +} + +/** + * Check if installed tmux version meets minimum requirements + */ +export function checkTmuxVersion(): { ok: boolean; version: string | null; minimum: string } { + const info = resolveTmux(); + if (!info) { + return { ok: false, version: null, minimum: MIN_TMUX_VERSION }; + } + + const installed = parseVersion(info.version); + const required = parseVersion(MIN_TMUX_VERSION); + + const ok = + installed.major > required.major || + (installed.major === required.major && installed.minor >= required.minor); + + return { ok, version: info.version, minimum: MIN_TMUX_VERSION }; +} diff --git a/src/wrapper/tmux-wrapper.ts b/src/wrapper/tmux-wrapper.ts index 6a554d25d..79c4a01d2 100644 --- a/src/wrapper/tmux-wrapper.ts +++ b/src/wrapper/tmux-wrapper.ts @@ -21,6 +21,7 @@ import { InboxManager } from './inbox.js'; import type { SendPayload, SendMeta } from '../protocol/types.js'; import { SqliteStorageAdapter } from '../storage/sqlite-adapter.js'; import { getProjectPaths } from '../utils/project-namespace.js'; +import { getTmuxPath, TmuxNotFoundError } from '../utils/tmux-resolver.js'; const execAsync = promisify(exec); const escapeRegex = (str: string): string => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); @@ -130,6 +131,7 @@ export class TmuxWrapper { private receivedMessageIdSet: Set = new Set(); private receivedMessageIdOrder: string[] = []; private readonly MAX_RECEIVED_MESSAGES = 2000; + private tmuxPath: string; // Resolved path to tmux binary (system or bundled) constructor(config: TmuxWrapperConfig) { this.config = { @@ -169,6 +171,9 @@ export class TmuxWrapper { // Session name (one agent per name - starting a duplicate kills the existing one) this.sessionName = `relay-${config.name}`; + // Resolve tmux path early so we fail fast if tmux isn't available + this.tmuxPath = getTmuxPath(); + this.client = new RelayClient({ agentName: config.name, socketPath: config.socketPath, @@ -264,7 +269,7 @@ export class TmuxWrapper { */ private async sessionExists(): Promise { try { - await execAsync(`tmux has-session -t ${this.sessionName} 2>/dev/null`); + await execAsync(`"${this.tmuxPath}" has-session -t ${this.sessionName} 2>/dev/null`); return true; } catch { return false; @@ -290,7 +295,7 @@ export class TmuxWrapper { // Kill any existing session with this name try { - execSync(`tmux kill-session -t ${this.sessionName} 2>/dev/null`); + execSync(`"${this.tmuxPath}" kill-session -t ${this.sessionName} 2>/dev/null`); } catch { // Session doesn't exist, that's fine } @@ -302,7 +307,7 @@ export class TmuxWrapper { // Create tmux session try { - execSync(`tmux new-session -d -s ${this.sessionName} -x ${this.config.cols} -y ${this.config.rows}`, { + execSync(`"${this.tmuxPath}" new-session -d -s ${this.sessionName} -x ${this.config.cols} -y ${this.config.rows}`, { cwd: this.config.cwd ?? process.cwd(), stdio: 'pipe', }); @@ -327,7 +332,7 @@ export class TmuxWrapper { for (const setting of tmuxSettings) { try { - execSync(`tmux ${setting}`, { stdio: 'pipe' }); + execSync(`"${this.tmuxPath}" ${setting}`, { stdio: 'pipe' }); } catch { // Some settings may not be available in older tmux versions } @@ -346,7 +351,7 @@ export class TmuxWrapper { for (const setting of tmuxMouseBindings) { try { - execSync(`tmux ${setting}`, { stdio: 'pipe' }); + execSync(`"${this.tmuxPath}" ${setting}`, { stdio: 'pipe' }); } catch { // Ignore on older tmux versions lacking these key tables } @@ -359,7 +364,7 @@ export class TmuxWrapper { TERM: 'xterm-256color', })) { const escaped = value.replace(/"/g, '\\"'); - execSync(`tmux setenv -t ${this.sessionName} ${key} "${escaped}"`); + execSync(`"${this.tmuxPath}" setenv -t ${this.sessionName} ${key} "${escaped}"`); } // Wait for shell to be ready (look for prompt) @@ -447,7 +452,7 @@ export class TmuxWrapper { try { const { stdout } = await execAsync( // -J joins wrapped lines so long prompts/messages stay intact - `tmux capture-pane -t ${this.sessionName} -p -J 2>/dev/null` + `"${this.tmuxPath}" capture-pane -t ${this.sessionName} -p -J 2>/dev/null` ); // Check if the last non-empty line looks like a prompt @@ -476,7 +481,7 @@ export class TmuxWrapper { * This spawns tmux attach and lets it take over stdin/stdout */ private attachToSession(): void { - this.attachProcess = spawn('tmux', ['attach-session', '-t', this.sessionName], { + this.attachProcess = spawn(this.tmuxPath, ['attach-session', '-t', this.sessionName], { stdio: 'inherit', // User's terminal connects directly to tmux }); @@ -522,7 +527,7 @@ export class TmuxWrapper { // Capture scrollback const { stdout } = await execAsync( // -J joins wrapped lines to avoid truncating ->relay commands mid-line - `tmux capture-pane -t ${this.sessionName} -p -J -S - 2>/dev/null` + `"${this.tmuxPath}" capture-pane -t ${this.sessionName} -p -J -S - 2>/dev/null` ); // Always parse the FULL capture for ->relay commands @@ -1053,7 +1058,7 @@ export class TmuxWrapper { * Send special keys to tmux */ private async sendKeys(keys: string): Promise { - await execAsync(`tmux send-keys -t ${this.sessionName} ${keys}`); + await execAsync(`"${this.tmuxPath}" send-keys -t ${this.sessionName} ${keys}`); } /** @@ -1069,7 +1074,7 @@ export class TmuxWrapper { .replace(/\$/g, '\\$') .replace(/`/g, '\\`') .replace(/!/g, '\\!'); - await execAsync(`tmux send-keys -t ${this.sessionName} -l "${escaped}"`); + await execAsync(`"${this.tmuxPath}" send-keys -t ${this.sessionName} -l "${escaped}"`); } /** @@ -1088,12 +1093,12 @@ export class TmuxWrapper { // Set tmux buffer then paste // Skip bracketed paste (-p) for CLIs that don't handle it properly (droid, other) - await execAsync(`tmux set-buffer -- "${escaped}"`); + await execAsync(`"${this.tmuxPath}" set-buffer -- "${escaped}"`); const useBracketedPaste = this.cliType === 'claude' || this.cliType === 'codex' || this.cliType === 'gemini'; if (useBracketedPaste) { - await execAsync(`tmux paste-buffer -t ${this.sessionName} -p`); + await execAsync(`"${this.tmuxPath}" paste-buffer -t ${this.sessionName} -p`); } else { - await execAsync(`tmux paste-buffer -t ${this.sessionName}`); + await execAsync(`"${this.tmuxPath}" paste-buffer -t ${this.sessionName}`); } } @@ -1131,7 +1136,7 @@ export class TmuxWrapper { private async getLastLine(): Promise { try { const { stdout } = await execAsync( - `tmux capture-pane -t ${this.sessionName} -p -J 2>/dev/null` + `"${this.tmuxPath}" capture-pane -t ${this.sessionName} -p -J 2>/dev/null` ); const lines = stdout.split('\n').filter(l => l.length > 0); return lines[lines.length - 1] || ''; @@ -1179,7 +1184,7 @@ export class TmuxWrapper { private async getCursorX(): Promise { try { const { stdout } = await execAsync( - `tmux display-message -t ${this.sessionName} -p "#{cursor_x}" 2>/dev/null` + `"${this.tmuxPath}" display-message -t ${this.sessionName} -p "#{cursor_x}" 2>/dev/null` ); return parseInt(stdout.trim(), 10) || 0; } catch { @@ -1239,7 +1244,7 @@ export class TmuxWrapper { private async capturePaneSignature(): Promise { try { const { stdout } = await execAsync( - `tmux capture-pane -t ${this.sessionName} -p -J -S - 2>/dev/null` + `"${this.tmuxPath}" capture-pane -t ${this.sessionName} -p -J -S - 2>/dev/null` ); const hash = crypto.createHash('sha1').update(stdout).digest('hex'); return `${stdout.length}:${hash}`; @@ -1297,7 +1302,7 @@ export class TmuxWrapper { // Kill tmux session try { - execSync(`tmux kill-session -t ${this.sessionName} 2>/dev/null`); + execSync(`"${this.tmuxPath}" kill-session -t ${this.sessionName} 2>/dev/null`); } catch { // Ignore } From ca09a80ae2298c73d24db2593f605ed7af8c7426 Mon Sep 17 00:00:00 2001 From: Khaliq Date: Sun, 28 Dec 2025 19:32:10 -0500 Subject: [PATCH 2/3] fix: Update spawner tests to use mocked tmux resolver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mock getTmuxPath() to return 'tmux' and update all test expectations to use quoted tmux path format ("tmux" ...) to match the actual code. Fixes agent-relay-fuob 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/bridge/spawner.test.ts | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/bridge/spawner.test.ts b/src/bridge/spawner.test.ts index 928361a98..65074e6af 100644 --- a/src/bridge/spawner.test.ts +++ b/src/bridge/spawner.test.ts @@ -33,6 +33,12 @@ vi.mock('../utils/project-namespace.js', () => { }; }); +vi.mock('../utils/tmux-resolver.js', () => { + return { + getTmuxPath: vi.fn(() => 'tmux'), + }; +}); + const execAsyncMock = vi.mocked(execAsync); const sleepMock = vi.mocked(sleep); const escapeForTmuxMock = vi.mocked(escapeForTmux); @@ -66,8 +72,8 @@ describe('AgentSpawner', () => { await spawner.ensureSession(); expect(execAsyncMock).toHaveBeenCalledTimes(2); - expect(execAsyncMock.mock.calls[0][0]).toBe(`tmux has-session -t ${session} 2>/dev/null`); - expect(execAsyncMock.mock.calls[1][0]).toBe(`tmux new-session -d -s ${session} -c "${projectRoot}"`); + expect(execAsyncMock.mock.calls[0][0]).toBe(`"tmux" has-session -t ${session} 2>/dev/null`); + expect(execAsyncMock.mock.calls[1][0]).toBe(`"tmux" new-session -d -s ${session} -c "${projectRoot}"`); }); it('does nothing when tmux session already exists', async () => { @@ -77,7 +83,7 @@ describe('AgentSpawner', () => { await spawner.ensureSession(); expect(execAsyncMock).toHaveBeenCalledTimes(1); - expect(execAsyncMock).toHaveBeenCalledWith(`tmux has-session -t ${session} 2>/dev/null`); + expect(execAsyncMock).toHaveBeenCalledWith(`"tmux" has-session -t ${session} 2>/dev/null`); }); it('spawns a worker and tracks it', async () => { @@ -99,12 +105,12 @@ describe('AgentSpawner', () => { window: `${session}:Dev1`, }); expect(spawner.hasWorker('Dev1')).toBe(true); - expect(execAsyncMock).toHaveBeenNthCalledWith(1, `tmux has-session -t ${session} 2>/dev/null`); - expect(execAsyncMock).toHaveBeenNthCalledWith(2, `tmux new-window -t ${session} -n Dev1 -c "${projectRoot}"`); + expect(execAsyncMock).toHaveBeenNthCalledWith(1, `"tmux" has-session -t ${session} 2>/dev/null`); + expect(execAsyncMock).toHaveBeenNthCalledWith(2, `"tmux" new-window -t ${session} -n Dev1 -c "${projectRoot}"`); expect(execAsyncMock).toHaveBeenNthCalledWith(3, 'which agent-relay'); // Find full path - expect(execAsyncMock).toHaveBeenNthCalledWith(4, `tmux send-keys -t ${session}:Dev1 'unset TMUX && /usr/local/bin/agent-relay -n Dev1 -- claude --dangerously-skip-permissions' Enter`); - expect(execAsyncMock).toHaveBeenNthCalledWith(5, `tmux send-keys -t ${session}:Dev1 -l "escaped:Finish the report"`); - expect(execAsyncMock).toHaveBeenNthCalledWith(6, `tmux send-keys -t ${session}:Dev1 Enter`); + expect(execAsyncMock).toHaveBeenNthCalledWith(4, `"tmux" send-keys -t ${session}:Dev1 'unset TMUX && /usr/local/bin/agent-relay -n Dev1 -- claude --dangerously-skip-permissions' Enter`); + expect(execAsyncMock).toHaveBeenNthCalledWith(5, `"tmux" send-keys -t ${session}:Dev1 -l "escaped:Finish the report"`); + expect(execAsyncMock).toHaveBeenNthCalledWith(6, `"tmux" send-keys -t ${session}:Dev1 Enter`); expect(sleepMock).toHaveBeenCalledWith(100); }); @@ -124,7 +130,7 @@ describe('AgentSpawner', () => { // Check that the command includes --dangerously-skip-permissions for claude:opus expect(execAsyncMock).toHaveBeenNthCalledWith( 4, - `tmux send-keys -t ${session}:Opus1 'unset TMUX && /usr/local/bin/agent-relay -n Opus1 -- claude:opus --dangerously-skip-permissions' Enter` + `"tmux" send-keys -t ${session}:Opus1 'unset TMUX && /usr/local/bin/agent-relay -n Opus1 -- claude:opus --dangerously-skip-permissions' Enter` ); }); @@ -144,7 +150,7 @@ describe('AgentSpawner', () => { // Check that the command does NOT include --dangerously-skip-permissions for codex expect(execAsyncMock).toHaveBeenNthCalledWith( 4, - `tmux send-keys -t ${session}:Codex1 'unset TMUX && /usr/local/bin/agent-relay -n Codex1 -- codex' Enter` + `"tmux" send-keys -t ${session}:Codex1 'unset TMUX && /usr/local/bin/agent-relay -n Codex1 -- codex' Enter` ); }); @@ -201,7 +207,7 @@ describe('AgentSpawner', () => { expect(result.success).toBe(false); expect(result.error).toContain('failed to register'); - expect(execAsyncMock).toHaveBeenCalledWith(`tmux kill-window -t ${session}:Late`); + expect(execAsyncMock).toHaveBeenCalledWith(`"tmux" kill-window -t ${session}:Late`); expect(spawner.hasWorker('Late')).toBe(false); }); @@ -223,8 +229,8 @@ describe('AgentSpawner', () => { expect(result).toBe(true); expect(spawner.hasWorker('Worker')).toBe(false); - expect(execAsyncMock).toHaveBeenNthCalledWith(1, `tmux send-keys -t ${session}:Worker '/exit' Enter`); - expect(execAsyncMock).toHaveBeenNthCalledWith(2, `tmux kill-window -t ${session}:Worker`); + expect(execAsyncMock).toHaveBeenNthCalledWith(1, `"tmux" send-keys -t ${session}:Worker '/exit' Enter`); + expect(execAsyncMock).toHaveBeenNthCalledWith(2, `"tmux" kill-window -t ${session}:Worker`); expect(sleepMock).toHaveBeenCalledWith(2000); }); @@ -256,8 +262,8 @@ describe('AgentSpawner', () => { expect(result).toBe(true); expect(spawner.hasWorker('Failing')).toBe(false); expect(execAsyncMock).toHaveBeenCalledTimes(2); - expect(execAsyncMock.mock.calls[0][0]).toBe(`tmux send-keys -t ${session}:Failing '/exit' Enter`); - expect(execAsyncMock.mock.calls[1][0]).toBe(`tmux kill-window -t ${session}:Failing`); + expect(execAsyncMock.mock.calls[0][0]).toBe(`"tmux" send-keys -t ${session}:Failing '/exit' Enter`); + expect(execAsyncMock.mock.calls[1][0]).toBe(`"tmux" kill-window -t ${session}:Failing`); expect(sleepMock).toHaveBeenCalledWith(2000); }); From de3b332fc0347151a31234409555aaa00d5054e2 Mon Sep 17 00:00:00 2001 From: Khaliq Date: Sun, 28 Dec 2025 19:34:25 -0500 Subject: [PATCH 3/3] beads --- .beads/issues.jsonl | 72 ++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 43 deletions(-) diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 1bea4d5bf..fd81a5920 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -7,21 +7,21 @@ {"id":"agent-relay-1t7","title":"Project name display in bridge header","description":"Show selected project name in the bridge interface header for clarity","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-24T11:47:39.531513+01:00","updated_at":"2025-12-24T11:59:16.98688+01:00","closed_at":"2025-12-24T11:59:16.98688+01:00"} {"id":"agent-relay-290","title":"Interface parity: bridge vs dashboard features","description":"Audit and synchronize features between bridge and dashboard interfaces to ensure feature parity","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-24T11:47:58.911802+01:00","updated_at":"2025-12-24T12:01:06.6157+01:00","closed_at":"2025-12-24T12:01:06.6157+01:00"} {"id":"agent-relay-2lw","title":"Add agent metadata tracking","description":"Track program, model, task description in agents.json. Better agent discovery.","status":"closed","priority":2,"issue_type":"feature","assignee":"Implementer","created_at":"2025-12-20T21:36:19.741328+01:00","updated_at":"2025-12-22T17:17:25.919228+01:00","closed_at":"2025-12-22T17:17:25.919228+01:00"} -{"id":"agent-relay-2sn","title":"Competitive Analysis: mcp_agent_mail vs agent-relay","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-20T21:34:44.49366+01:00","updated_at":"2025-12-20T21:36:26.362391+01:00","closed_at":"2025-12-20T21:36:26.362391+01:00"} +{"id":"agent-relay-2sn","title":"Competitive Analysis: mcp_agent_mail vs agent-relay","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-20T21:34:44.49366+01:00","updated_at":"2025-12-20T21:36:26.362391+01:00","closed_at":"2025-12-20T21:36:26.362391+01:00"} {"id":"agent-relay-2uf","title":"Add message threading support","description":"@relay:Bob [thread:feature-123] pattern. Group related messages for better context.","notes":"Current state: thread parsing + protocol/storage support implemented (ParsedCommand.thread, SendPayload.thread, DB messages.thread + filter). Remaining: wire cmd.thread through tmux-wrapper sendRelayCommand -\u003e RelayClient.sendMessage(..., thread) and include thread in injected display/Inbox. See src/wrapper/tmux-wrapper.ts sendRelayCommand + handleIncomingMessage.","status":"closed","priority":2,"issue_type":"feature","assignee":"SecondLead","created_at":"2025-12-20T21:36:19.631448+01:00","updated_at":"2025-12-22T17:17:42.876826+01:00","closed_at":"2025-12-22T17:17:42.876826+01:00"} {"id":"agent-relay-2z1","title":"ACK messages not used for reliability","description":"In connection.ts:114-116, ACK messages are accepted but not processed. The protocol supports reliable delivery with ACK/NACK but it's not implemented. Need to: (1) Track unACKed messages, (2) Implement retry logic, (3) Add configurable TTL for messages.","status":"closed","priority":2,"issue_type":"feature","assignee":"LeadDev","created_at":"2025-12-20T00:17:43.615251+01:00","updated_at":"2025-12-20T21:56:07.202292+01:00","closed_at":"2025-12-20T21:56:07.202292+01:00"} {"id":"agent-relay-37i","title":"Message deduplication uses in-memory Set without limits","description":"In tmux-wrapper.ts:65, sentMessageHashes is a Set that grows unbounded. For long-running sessions, this could cause memory issues. Add: (1) Max size with LRU eviction, (2) Time-based expiration, (3) Bloom filter alternative for memory efficiency.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-20T00:18:47.229988+01:00","updated_at":"2025-12-20T00:18:47.229988+01:00"} {"id":"agent-relay-3px","title":"Add playbook system for batch automation","description":"Implement playbook system (like Maestro's Auto Run) for batch-processing task lists through agents. Define workflows in YAML/markdown, execute automatically with context isolation. Enables reproducible multi-step automation.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-23T17:04:54.464749+01:00","updated_at":"2025-12-23T17:04:54.464749+01:00"} -{"id":"agent-relay-3tx","title":"PR-9 Review: Document configurable timeouts","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-22T21:54:15.789418+01:00","updated_at":"2025-12-22T21:54:15.789418+01:00"} -{"id":"agent-relay-3y1","title":"Fix dashboard auto-scroll interfering with manual scrolling","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-24T15:09:41.112636+01:00","updated_at":"2025-12-24T15:10:59.748796+01:00","closed_at":"2025-12-24T15:10:59.748796+01:00"} +{"id":"agent-relay-3tx","title":"PR-9 Review: Document configurable timeouts","description":"","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-22T21:54:15.789418+01:00","updated_at":"2025-12-22T21:54:15.789418+01:00"} +{"id":"agent-relay-3y1","title":"Fix dashboard auto-scroll interfering with manual scrolling","description":"","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-24T15:09:41.112636+01:00","updated_at":"2025-12-24T15:10:59.748796+01:00","closed_at":"2025-12-24T15:10:59.748796+01:00"} {"id":"agent-relay-41f","title":"BUG: better-sqlite3 bindings fail on Node 25","description":"agent-relay read command fails with 'Could not locate the bindings file' on Node v25.2.1. Need to rebuild bindings or use compatible Node version.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-20T21:46:24.882216+01:00","updated_at":"2025-12-20T21:49:56.89756+01:00","closed_at":"2025-12-20T21:49:56.89756+01:00"} {"id":"agent-relay-47z","title":"Express 5 may have breaking changes from Express 4 patterns","description":"package.json uses express@5.2.1 which is a major version with breaking changes from Express 4. Verify: (1) Error handling middleware patterns, (2) Router behavior, (3) Body parsing (express.json vs body-parser).","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-20T00:18:49.269841+01:00","updated_at":"2025-12-20T00:18:49.269841+01:00"} {"id":"agent-relay-4e0","title":"Fix message truncation - messages cut off at source","description":"Root cause found: parser.ts:40 inline regex only captures single line. Multi-line messages are split by parsePassThrough() at line 206. Fix options: (1) Allow continuation lines in inline format, (2) Use block format for multi-line, (3) Add heuristic to join lines until next @relay pattern.","status":"closed","priority":2,"issue_type":"bug","assignee":"MistyShelter","created_at":"2025-12-19T23:40:35.082717+01:00","updated_at":"2025-12-20T00:03:54.806087+01:00","closed_at":"2025-12-20T00:03:54.806087+01:00"} -{"id":"agent-relay-4ft","title":"Merge project info into status command","status":"closed","priority":2,"issue_type":"task","assignee":"Pruner","created_at":"2025-12-19T21:59:52.685495+01:00","updated_at":"2025-12-19T22:06:44.276187+01:00","closed_at":"2025-12-19T22:06:44.276187+01:00"} +{"id":"agent-relay-4ft","title":"Merge project info into status command","description":"","status":"closed","priority":2,"issue_type":"task","assignee":"Pruner","created_at":"2025-12-19T21:59:52.685495+01:00","updated_at":"2025-12-19T22:06:44.276187+01:00","closed_at":"2025-12-19T22:06:44.276187+01:00"} {"id":"agent-relay-4jv","title":"Add session discovery from existing Claude Code","description":"Auto-discover and import existing Claude Code sessions (like Maestro). Detect running claude processes, offer to wrap them with agent-relay messaging. Reduces friction for adoption - users don't have to restart their agents.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-23T17:05:01.003851+01:00","updated_at":"2025-12-23T17:05:01.003851+01:00"} {"id":"agent-relay-4oy","title":"Fix beads sync prefix mismatch errors","description":"During bd sync, getting 'prefix mismatch detected: database uses agent-relay- but found issues with prefixes: agent- (5 issues)'. Need to investigate and fix the prefix inconsistency or use --rename-on-import flag.","status":"open","priority":2,"issue_type":"bug","created_at":"2025-12-23T23:10:12.720183+01:00","updated_at":"2025-12-23T23:10:12.720183+01:00"} -{"id":"agent-relay-4vl","title":"PR-9 Review: Add unit tests for AgentSpawner","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-22T21:54:03.195295+01:00","updated_at":"2025-12-22T22:00:57.315234+01:00","closed_at":"2025-12-22T22:00:57.315234+01:00"} -{"id":"agent-relay-4xs","title":"Dashboard: Enter to send, Shift+Enter for newline (Slack-style)","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-12-24T15:25:01.248707+01:00","updated_at":"2025-12-24T15:26:24.619384+01:00","closed_at":"2025-12-24T15:26:24.619384+01:00"} +{"id":"agent-relay-4vl","title":"PR-9 Review: Add unit tests for AgentSpawner","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-22T21:54:03.195295+01:00","updated_at":"2025-12-22T22:00:57.315234+01:00","closed_at":"2025-12-22T22:00:57.315234+01:00"} +{"id":"agent-relay-4xs","title":"Dashboard: Enter to send, Shift+Enter for newline (Slack-style)","description":"","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-12-24T15:25:01.248707+01:00","updated_at":"2025-12-24T15:26:24.619384+01:00","closed_at":"2025-12-24T15:26:24.619384+01:00"} {"id":"agent-relay-51u","title":"Outgoing messages truncated before delivery","description":"Agent messages are being cut off mid-sentence. Examples: 'Updates for mcl/2z1:' and 'Signing off. Progress report:' - both end abruptly. May be related to capture-pane buffer limits, parser issues, or injection timing. Investigate: (1) capture-pane -S - scrollback limits, (2) parser line joining, (3) message storage truncation.","notes":"PROGRESS: Reproduced truncation in tmux wrapper/relay flow. Likely due to display truncation on injection length and terminal capture limitations. No code changes yet.","status":"closed","priority":1,"issue_type":"bug","assignee":"Implementer","created_at":"2025-12-20T21:50:11.411952+01:00","updated_at":"2025-12-22T13:48:45.264136+01:00","closed_at":"2025-12-22T13:48:45.264136+01:00"} {"id":"agent-relay-52d","title":"Add metrics/observability for daemon","description":"No way to monitor daemon health, message throughput, or agent activity. Add: (1) /metrics endpoint for Prometheus, (2) Message count/rate stats, (3) Connection lifecycle events, (4) Error rate tracking.","status":"closed","priority":2,"issue_type":"feature","assignee":"FEDev","created_at":"2025-12-20T00:18:48.378728+01:00","updated_at":"2025-12-25T13:59:43.376287+01:00","closed_at":"2025-12-25T13:59:43.376287+01:00"} {"id":"agent-relay-52e","title":"Unify bridge view with dashboard UI","description":"Make bridge view feel like the main dashboard with project tabs, same agent/messaging UI, spawn capability at bridge level, and ability to click into individual project dashboards","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-25T13:44:11.491787+01:00","updated_at":"2025-12-25T13:49:36.007645+01:00","closed_at":"2025-12-25T13:49:36.007645+01:00"} @@ -32,23 +32,22 @@ {"id":"agent-relay-6dl8","title":"Multi-line relay messages truncated in dashboard display","description":"Multi-line messages sent via -\u003erelay: are being cut off after first line when displayed in dashboard. This blocks communication between agents. Need to: (1) Verify daemon sends full message, (2) Fix dashboard rendering to handle newlines properly.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-26T12:28:39.746942+01:00","updated_at":"2025-12-26T12:38:36.844034+01:00","closed_at":"2025-12-26T12:38:36.844034+01:00"} {"id":"agent-relay-6mo","title":"Lead spawn capability not working (@relay:spawn pattern)","description":"The lead command advertises spawn capability via @relay:spawn pattern but it's not fully implemented. Agents started with 'agent-relay lead' should be able to spawn workers, but the parser extension mentioned in the code is not implemented.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-23T12:14:59.492237+01:00","updated_at":"2025-12-23T12:18:14.114911+01:00","closed_at":"2025-12-23T12:18:14.114911+01:00"} {"id":"agent-relay-6nx","title":"Add activity state tracking for better injection timing","description":"Track active/idle/disconnected state with timestamps. When session goes idle (30s no activity), trigger message injection opportunity. Improves injection timing vs current fixed 1.5s wait. See docs/TMUX_IMPROVEMENTS.md for implementation details.","status":"closed","priority":2,"issue_type":"feature","assignee":"LeadDev","created_at":"2025-12-20T21:28:46.993856+01:00","updated_at":"2025-12-20T21:33:42.223042+01:00","closed_at":"2025-12-20T21:33:42.223042+01:00"} -{"id":"agent-relay-6ny","title":"Fix message truncation: store full message in SQLite first, include ID in relay","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-19T22:04:36.168862+01:00","updated_at":"2025-12-19T22:08:28.207532+01:00","closed_at":"2025-12-19T22:08:28.207532+01:00"} +{"id":"agent-relay-6ny","title":"Fix message truncation: store full message in SQLite first, include ID in relay","description":"","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-19T22:04:36.168862+01:00","updated_at":"2025-12-19T22:08:28.207532+01:00","closed_at":"2025-12-19T22:08:28.207532+01:00"} {"id":"agent-relay-6rz","title":"Message injection timing can cause race conditions","description":"In tmux-wrapper.ts:564-569, injection waits for 'idle' (1.5s since last output) but this is fragile. If agent produces output during injection, messages could interleave. Consider: (1) Input buffer detection, (2) Bracketed paste mode, (3) Agent-specific injection strategies.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-20T00:18:17.76865+01:00","updated_at":"2025-12-25T14:31:59.858855+01:00","closed_at":"2025-12-25T14:31:59.858855+01:00"} {"id":"agent-relay-7b2","title":"CRITICAL: Agents cycling between offline/online status","description":"Dashboard reports agents cycling between offline/online. Root cause analysis: Heartbeat timeout in connection.ts line 230 - if PONG not received within 10 seconds (2x heartbeat interval), connection is killed. This is too aggressive for AI agents that may be processing. Fix: increase timeout tolerance or make configurable. See connection.ts:223-244.","status":"closed","priority":0,"issue_type":"bug","assignee":"Lead","created_at":"2025-12-23T22:57:53.052261+01:00","updated_at":"2025-12-23T23:02:41.925425+01:00","closed_at":"2025-12-23T23:02:41.925425+01:00"} {"id":"agent-relay-7bp","title":"Memory storage adapter has fixed 1000 message limit","description":"In storage/adapter.ts:60-63, MemoryStorageAdapter hard-codes 1000 message limit. This should be configurable and potentially use LRU eviction instead of slice.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-20T00:17:46.132735+01:00","updated_at":"2025-12-20T00:17:46.132735+01:00"} -{"id":"agent-relay-7fx","title":"PR-9 Review: Fix @relay vs -\u003erelay syntax inconsistency in docs","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-22T22:02:02.782958+01:00","updated_at":"2025-12-22T22:04:02.664058+01:00","closed_at":"2025-12-22T22:04:02.664058+01:00"} +{"id":"agent-relay-7fx","title":"PR-9 Review: Fix @relay vs -\u003erelay syntax inconsistency in docs","description":"","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-22T22:02:02.782958+01:00","updated_at":"2025-12-22T22:04:02.664058+01:00","closed_at":"2025-12-22T22:04:02.664058+01:00"} {"id":"agent-relay-7mm","title":"Add orphaned tmux session cleanup","description":"When agent-relay wrapper crashes or gets SIGKILL, the tmux session remains. Need: 1) CLI command to gc orphaned sessions, 2) Daemon-side cleanup on client disconnect","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-22T15:52:01.58781+01:00","updated_at":"2025-12-22T15:54:59.840769+01:00","closed_at":"2025-12-22T15:54:59.840769+01:00"} {"id":"agent-relay-7tk","title":"Dashboard truncates multi-line messages","description":"Messages containing line breaks only show the first line in the dashboard. Need to render newlines properly - either with CSS white-space or by converting newlines to \u003cbr\u003e tags.","status":"closed","priority":1,"issue_type":"bug","assignee":"FE-Dev","created_at":"2025-12-23T23:05:03.86361+01:00","updated_at":"2025-12-23T23:05:59.96346+01:00","closed_at":"2025-12-23T23:05:59.96346+01:00"} {"id":"agent-relay-7tu","title":"Fix storage portability: better-sqlite3 native binding failures","description":"The read command fails in some Node versions (e.g., Node 25) due to missing better-sqlite3 bindings. Decide path: pin supported Node LTS + ensure rebuild, or replace with a non-native option (e.g., Node sqlite, wasm, or a pure JS adapter).","design":"Implement driver selection/fallback: try better-sqlite3, fallback to node:sqlite DatabaseSync when bindings missing. Add env override AGENT_RELAY_SQLITE_DRIVER=node|better-sqlite3. Convert queries to positional params so both drivers share code.","acceptance_criteria":"- dist CLI read works on supported Node versions\\n- CI/build docs reflect supported runtime or replacement adapter","notes":"Implemented sqlite portability: dynamic better-sqlite3 import + fallback to node:sqlite (env override AGENT_RELAY_SQLITE_DRIVER). Updated sqlite adapter to use positional params for compatibility; added/updated tests; npx vitest run passes (216).","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-20T21:44:57.868091+01:00","updated_at":"2025-12-20T21:56:13.384399+01:00","closed_at":"2025-12-20T21:56:13.3844+01:00","labels":["build","storage"]} -{"id":"agent-relay-7yo","title":"Update CLAUDE.md with new CLI commands","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T22:00:02.661859+01:00","updated_at":"2025-12-19T22:07:27.766701+01:00","closed_at":"2025-12-19T22:07:27.766701+01:00","dependencies":[{"issue_id":"agent-relay-7yo","depends_on_id":"agent-relay-85z","type":"blocks","created_at":"2025-12-19T22:00:25.731921+01:00","created_by":"daemon","metadata":"{}"},{"issue_id":"agent-relay-7yo","depends_on_id":"agent-relay-4ft","type":"blocks","created_at":"2025-12-19T22:00:25.772241+01:00","created_by":"daemon","metadata":"{}"},{"issue_id":"agent-relay-7yo","depends_on_id":"agent-relay-bd0","type":"blocks","created_at":"2025-12-19T22:00:25.80776+01:00","created_by":"daemon","metadata":"{}"},{"issue_id":"agent-relay-7yo","depends_on_id":"agent-relay-f3q","type":"blocks","created_at":"2025-12-19T22:00:25.843131+01:00","created_by":"daemon","metadata":"{}"}]} -{"id":"agent-relay-85z","title":"Merge dashboard into start command","status":"closed","priority":2,"issue_type":"task","assignee":"InterfaceManager","created_at":"2025-12-19T21:59:51.61716+01:00","updated_at":"2025-12-19T22:06:44.27487+01:00","closed_at":"2025-12-19T22:06:44.27487+01:00"} +{"id":"agent-relay-7yo","title":"Update CLAUDE.md with new CLI commands","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T22:00:02.661859+01:00","updated_at":"2025-12-19T22:07:27.766701+01:00","closed_at":"2025-12-19T22:07:27.766701+01:00","dependencies":[{"issue_id":"agent-relay-7yo","depends_on_id":"agent-relay-85z","type":"blocks","created_at":"2025-12-19T22:00:25.731921+01:00","created_by":"daemon"},{"issue_id":"agent-relay-7yo","depends_on_id":"agent-relay-4ft","type":"blocks","created_at":"2025-12-19T22:00:25.772241+01:00","created_by":"daemon"},{"issue_id":"agent-relay-7yo","depends_on_id":"agent-relay-bd0","type":"blocks","created_at":"2025-12-19T22:00:25.80776+01:00","created_by":"daemon"},{"issue_id":"agent-relay-7yo","depends_on_id":"agent-relay-f3q","type":"blocks","created_at":"2025-12-19T22:00:25.843131+01:00","created_by":"daemon"}]} +{"id":"agent-relay-85z","title":"Merge dashboard into start command","description":"","status":"closed","priority":2,"issue_type":"task","assignee":"InterfaceManager","created_at":"2025-12-19T21:59:51.61716+01:00","updated_at":"2025-12-19T22:06:44.27487+01:00","closed_at":"2025-12-19T22:06:44.27487+01:00"} {"id":"agent-relay-8ap","title":"CRITICAL: send command triggers infinite reconnection loop","description":"When sending relay messages to Gem agent, the send command triggers an infinite reconnection loop. Client repeatedly connects with RESUME_TOO_OLD errors, never actually delivers the message. Each connection gets a new session ID and immediately reconnects (~90-110ms apart). This is a critical client/daemon issue, not just Gemini-specific.","notes":"CLI send command removed (keeping surface area small). Root cause still needs investigation - client reconnection loop with RESUME_TOO_OLD errors.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-22T12:18:13.272843+01:00","updated_at":"2025-12-22T14:37:19.435751+01:00","closed_at":"2025-12-22T14:37:19.435751+01:00"} {"id":"agent-relay-8fc","title":"Agent auto-complete menu is transparent/invisible","description":"The agent auto-complete dropdown menu in the dashboard appears to be transparent or has visibility issues, making it unusable. Need to debug CSS/z-index/display issues in bridge.html agent selector component.","status":"closed","priority":2,"issue_type":"bug","assignee":"FE-Dev","created_at":"2025-12-24T15:04:53.390674+01:00","updated_at":"2025-12-24T15:30:23.72431+01:00","closed_at":"2025-12-24T15:30:23.72431+01:00"} {"id":"agent-relay-8ff","title":"Add agent list command to CLI","description":"CLI has up/down/status/read but no way to list connected agents or see message history from command line. Add: (1) agent-relay agents - list connected agents, (2) agent-relay history - show recent messages, (3) agent-relay send \u003cagent\u003e \u003cmsg\u003e - send from CLI.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-20T00:18:34.400432+01:00","updated_at":"2025-12-22T15:35:44.695605+01:00","closed_at":"2025-12-22T15:35:44.695605+01:00"} {"id":"agent-relay-8nx","title":"SIGINT/SIGTERM handlers don't await cleanup","description":"In cli/index.ts:74-77 and daemon setup, SIGINT handlers call stop() but process.exit(0) runs immediately. The stop() is async but not awaited, potentially leaving socket files or incomplete shutdown.","status":"open","priority":2,"issue_type":"bug","created_at":"2025-12-20T00:18:19.189514+01:00","updated_at":"2025-12-20T00:18:19.189514+01:00"} -{"id":"agent-relay-8z1","title":"Add CLI tests for new command structure","status":"closed","priority":2,"issue_type":"task","assignee":"Lead","created_at":"2025-12-19T22:00:04.561793+01:00","updated_at":"2025-12-22T17:10:28.100961+01:00","closed_at":"2025-12-22T17:10:28.100961+01:00","dependencies":[{"issue_id":"agent-relay-8z1","depends_on_id":"agent-relay-85z","type":"blocks","created_at":"2025-12-19T22:00:27.632396+01:00","created_by":"daemon","metadata":"{}"},{"issue_id":"agent-relay-8z1","depends_on_id":"agent-relay-4ft","type":"blocks","created_at":"2025-12-19T22:00:27.671868+01:00","created_by":"daemon","metadata":"{}"},{"issue_id":"agent-relay-8z1","depends_on_id":"agent-relay-bd0","type":"blocks","created_at":"2025-12-19T22:00:27.713889+01:00","created_by":"daemon","metadata":"{}"},{"issue_id":"agent-relay-8z1","depends_on_id":"agent-relay-f3q","type":"blocks","created_at":"2025-12-19T22:00:27.752052+01:00","created_by":"daemon","metadata":"{}"}]} +{"id":"agent-relay-8z1","title":"Add CLI tests for new command structure","description":"","status":"closed","priority":2,"issue_type":"task","assignee":"Lead","created_at":"2025-12-19T22:00:04.561793+01:00","updated_at":"2025-12-22T17:10:28.100961+01:00","closed_at":"2025-12-22T17:10:28.100961+01:00","dependencies":[{"issue_id":"agent-relay-8z1","depends_on_id":"agent-relay-85z","type":"blocks","created_at":"2025-12-19T22:00:27.632396+01:00","created_by":"daemon"},{"issue_id":"agent-relay-8z1","depends_on_id":"agent-relay-4ft","type":"blocks","created_at":"2025-12-19T22:00:27.671868+01:00","created_by":"daemon"},{"issue_id":"agent-relay-8z1","depends_on_id":"agent-relay-bd0","type":"blocks","created_at":"2025-12-19T22:00:27.713889+01:00","created_by":"daemon"},{"issue_id":"agent-relay-8z1","depends_on_id":"agent-relay-f3q","type":"blocks","created_at":"2025-12-19T22:00:27.752052+01:00","created_by":"daemon"}]} {"id":"agent-relay-90h","title":"Show 'needs attention' indicator when agent is waiting for permissions","description":"Frontend complete: needsAttention field, pulsing indicator, badge. Backend piece still needed: detect when agents are waiting for permissions and set the flag.","status":"closed","priority":1,"issue_type":"feature","assignee":"FE-Dev","created_at":"2025-12-23T22:55:26.87753+01:00","updated_at":"2025-12-24T11:48:32.679452+01:00","closed_at":"2025-12-24T11:48:32.679452+01:00"} -{"id":"agent-relay-9igw","title":"Agents not closing multi-line relay messages with \u003e\u003e\u003e","description":"Agent Relay requires multi-line messages to be wrapped in \u003c\u003c\u003c \u003e\u003e\u003e format with \u003e\u003e\u003e on its own line. Agents are sending multi-line messages without proper closure. This breaks message parsing and relay communication quality.","status":"open","priority":1,"issue_type":"bug","created_at":"2025-12-28T19:29:43.950026-05:00","updated_at":"2025-12-28T19:29:43.950026-05:00"} {"id":"agent-relay-9kc","title":"Add skip-permissions flag for spawned Claude agents","description":"When spawning Claude agents via agent-relay, automatically use --dangerously-skip-permissions to avoid permission dialog delays. Update spawn command in CLI to pass this flag to spawned sessions.","status":"closed","priority":1,"issue_type":"task","assignee":"FEDev","created_at":"2025-12-25T14:09:00.07082+01:00","updated_at":"2025-12-25T14:15:45.925839+01:00","closed_at":"2025-12-25T14:15:45.925839+01:00"} {"id":"agent-relay-9kw","title":"Add multi-machine agent support","description":"Enable agents to run across multiple machines with centralized coordination. AI Maestro scales to 100+ agents across machines. Extend bridge mode to support remote daemon connections over TCP/WebSocket. Critical for scaling beyond single machine.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-23T17:04:35.521241+01:00","updated_at":"2025-12-23T17:04:35.521241+01:00"} {"id":"agent-relay-9qa","title":"Dashboard websocket 'Invalid frame header' error on localhost:3888","description":"Websocket connection to ws://localhost:3888/ws fails with 'Invalid frame header' error from browser. Blocking dashboard functionality.","notes":"WS handshake fixed: restored default WebSocket compression (removed perMessageDeflate:false), removed HTTP polling fallback. FE-Dev confirmed 101 upgrade on 127.0.0.1:3888 after rebuild/refresh.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-23T16:42:29.342087+01:00","updated_at":"2025-12-23T17:04:04.246054+01:00","closed_at":"2025-12-23T17:04:04.246054+01:00"} @@ -58,15 +57,14 @@ {"id":"agent-relay-a4k","title":"Add safety mechanisms for dangerous commands","description":"Implement safety layer for dangerous operations (like ACFS's two-person rule). Require confirmation from second agent or human for destructive commands (rm -rf, git push -f, DROP TABLE). Configurable blocklist and approval workflow.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-23T17:05:21.032206+01:00","updated_at":"2025-12-23T17:05:21.032206+01:00"} {"id":"agent-relay-ahe","title":"Session resume not implemented - RESUME_TOO_OLD always sent","description":"In connection.ts:140-143, session resume tokens are received but not persisted or validated. The server always responds with RESUME_TOO_OLD. This breaks the resume capability advertised in the protocol. Need to implement token persistence and session state recovery.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-20T00:17:30.605649+01:00","updated_at":"2025-12-22T15:31:05.795905+01:00","closed_at":"2025-12-22T15:31:05.795905+01:00"} {"id":"agent-relay-aq2","title":"Implement agent-to-agent spawning via relay","description":"Allow agents to spawn new agents through relay messaging. Enable -\u003erelay:spawn pattern so agents can dynamically create specialized sub-agents. This unblocks multi-agent workflows and team hierarchies.","status":"closed","priority":1,"issue_type":"feature","assignee":"BESpecialist","created_at":"2025-12-25T14:14:02.110506+01:00","updated_at":"2025-12-25T14:32:10.400627+01:00","closed_at":"2025-12-25T14:32:10.400627+01:00"} -{"id":"agent-relay-aqy","title":"Display cross-project messages with project badge in dashboard","description":"When messages use @project:agent format, display them in the dashboard with a project badge/indicator. Parse project:agent from the 'to' field and render as [ProjectBadge] @AgentName. Depends on agent-relay-cross-project-parser completing first.","status":"closed","priority":2,"issue_type":"feature","assignee":"FE-Dev","created_at":"2025-12-24T10:29:37.528325+01:00","updated_at":"2025-12-24T11:46:39.341486+01:00","closed_at":"2025-12-24T11:46:39.341486+01:00","dependencies":[{"issue_id":"agent-relay-aqy","depends_on_id":"agent-relay-ytl","type":"blocks","created_at":"2025-12-24T10:29:48.712573+01:00","created_by":"khaliqgant","metadata":"{}"}]} +{"id":"agent-relay-aqy","title":"Display cross-project messages with project badge in dashboard","description":"When messages use @project:agent format, display them in the dashboard with a project badge/indicator. Parse project:agent from the 'to' field and render as [ProjectBadge] @AgentName. Depends on agent-relay-cross-project-parser completing first.","status":"closed","priority":2,"issue_type":"feature","assignee":"FE-Dev","created_at":"2025-12-24T10:29:37.528325+01:00","updated_at":"2025-12-24T11:46:39.341486+01:00","closed_at":"2025-12-24T11:46:39.341486+01:00","dependencies":[{"issue_id":"agent-relay-aqy","depends_on_id":"agent-relay-ytl","type":"blocks","created_at":"2025-12-24T10:29:48.712573+01:00","created_by":"khaliqgant"}]} {"id":"agent-relay-b11","title":"Cannot scroll in agent tmux window","description":"User unable to scroll in the tmux window when running agent-relay wrapped agents. Need to fix mouse scroll or keyboard scroll behavior.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-23T15:01:55.791186+01:00","updated_at":"2025-12-23T15:04:51.896303+01:00","closed_at":"2025-12-23T15:04:51.896303+01:00"} -{"id":"agent-relay-bai","title":"Fix bridge reconnection loop caused by agent name conflict","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-25T13:26:46.025963+01:00","updated_at":"2025-12-25T13:27:04.323001+01:00","closed_at":"2025-12-25T13:27:04.323001+01:00"} -{"id":"agent-relay-bd0","title":"Consolidate team-* commands under team subcommand","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T21:59:54.102815+01:00","updated_at":"2025-12-19T22:06:16.220013+01:00","closed_at":"2025-12-19T22:06:16.220013+01:00"} +{"id":"agent-relay-bai","title":"Fix bridge reconnection loop caused by agent name conflict","description":"","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-25T13:26:46.025963+01:00","updated_at":"2025-12-25T13:27:04.323001+01:00","closed_at":"2025-12-25T13:27:04.323001+01:00"} +{"id":"agent-relay-bd0","title":"Consolidate team-* commands under team subcommand","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T21:59:54.102815+01:00","updated_at":"2025-12-19T22:06:16.220013+01:00","closed_at":"2025-12-19T22:06:16.220013+01:00"} {"id":"agent-relay-bei","title":"Gemini (CleverBeacon) bash syntax errors - unexpected EOF","description":"Gemini agent gets bash errors: 'unexpected EOF while looking for matching quote' and 'syntax error: unexpected end of file' when running shell commands. Possibly related to quote escaping in the relay wrapper.","status":"closed","priority":2,"issue_type":"bug","assignee":"GraniteElk","created_at":"2025-12-19T23:40:36.464079+01:00","updated_at":"2025-12-19T23:45:46.05609+01:00","closed_at":"2025-12-19T23:45:46.05609+01:00"} {"id":"agent-relay-blj","title":"Add metrics and observability endpoint","description":"Add metrics collection and observability for production monitoring. Include: message throughput, agent health, latency percentiles, error rates, queue depths. Expose via HTTP endpoint (Prometheus format) and/or dashboard integration. Currently flying blind in production.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-23T17:03:41.964359+01:00","updated_at":"2025-12-25T13:33:56.132695+01:00","closed_at":"2025-12-25T13:33:56.132695+01:00"} {"id":"agent-relay-bx0","title":"Filter stale/internal agents from agents list","description":"agent-relay agents shows __status__ and cli agents which are temporary internal connections. Should filter these out by default, with --all flag to show everything.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-22T15:44:51.944084+01:00","updated_at":"2025-12-22T15:45:44.262049+01:00","closed_at":"2025-12-22T15:45:44.262049+01:00"} {"id":"agent-relay-coh","title":"Add tests for heartbeat timeout behavior","description":"The heartbeat timeout fix (10s-\u003e30s) was made but no new tests were added. Add unit tests for: 1) configurable heartbeatTimeoutMultiplier, 2) connection survives slow pong responses, 3) connection dies after timeout exceeded.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-23T23:10:18.599194+01:00","updated_at":"2025-12-25T13:31:23.910414+01:00","closed_at":"2025-12-25T13:31:23.910414+01:00"} -{"id":"agent-relay-cuer","title":"Single agent flickering between active/inactive on dashboard","description":"When a single agent is spawned, the dashboard flickers rapidly between showing the agent as active and inactive. This blocks reliable agent monitoring. Need to investigate: (1) WebSocket connection stability, (2) Status update frequency, (3) State sync race conditions.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-26T16:53:20.385225+01:00","updated_at":"2025-12-26T17:20:58.613208+01:00","closed_at":"2025-12-26T17:20:58.613208+01:00"} {"id":"agent-relay-cw8","title":"Add procedural memory for learned patterns","description":"Implement procedural memory system (like ACFS's cass-memory) to store learned patterns and successful approaches. Agents can query 'how did we solve X before?' Enable cross-session learning and pattern reuse.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-23T17:05:14.667585+01:00","updated_at":"2025-12-23T17:05:14.667585+01:00"} {"id":"agent-relay-d07","title":"Message metadata: subject/thread/importance/replyTo","description":"Add first-class metadata fields to messages (subject, threadId, importance, ackRequired, replyTo/correlationId). Carry them via SendPayload.data and/or [[RELAY]] blocks; persist in storage; update injection formatting + dashboard filters.","acceptance_criteria":"- Can send/receive messages with threadId + importance\\n- Dashboard can filter/group by threadId\\n- Injection shows importance/thread hint without breaking TUIs","notes":"SecondLead: Implemented thread support end-to-end. SendMeta added to client.sendMessage(). tmux-wrapper converts ParsedMessageMetadata to SendMeta. Injection shows [thread:xxx] hint. Dashboard displays thread badge. REMAINING: importance field requires protocol update - DeliverEnvelope needs payload_meta to carry importance through to receivers.","status":"closed","priority":2,"issue_type":"task","assignee":"SecondLead","created_at":"2025-12-20T21:44:18.094598+01:00","updated_at":"2025-12-22T17:15:37.261368+01:00","closed_at":"2025-12-22T17:15:37.261368+01:00","labels":["protocol","ux"]} {"id":"agent-relay-d2f","title":"Add optional Git audit trail","description":"Commit messages to .agent-relay/messages/ directory. Recoverable history, compliance-friendly.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-20T21:36:19.689795+01:00","updated_at":"2025-12-20T21:36:19.689795+01:00"} @@ -74,52 +72,40 @@ {"id":"agent-relay-dkx","title":"Add metrics dashboard for system health monitoring","description":"Track agent metrics (throughput, uptime, online status), session lifecycle (active/closed/error rates), and per-agent message counts. Provide Prometheus-format export for monitoring integration. Includes metrics.ts utilities and metrics.html dashboard page.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-25T14:07:28.416696+01:00","updated_at":"2025-12-26T11:41:20.321264+01:00","closed_at":"2025-12-26T11:41:20.321264+01:00"} {"id":"agent-relay-dyr","title":"No authentication between agents","description":"Any process can connect to the daemon socket and impersonate any agent name. Consider: (1) Per-agent tokens/secrets, (2) Socket permission checks, (3) Optional TLS for non-localhost deployments.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-20T00:18:16.215889+01:00","updated_at":"2025-12-20T00:18:16.215889+01:00"} {"id":"agent-relay-eek","title":"Change relay prefix from @relay to \u003e\u003erelay","description":"Unify the relay protocol prefix to \u003e\u003erelay (instead of @relay) so it works for all agents including Gemini. Update parser, documentation, and any hardcoded references.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-22T12:20:35.35848+01:00","updated_at":"2025-12-22T12:30:21.696239+01:00","closed_at":"2025-12-22T12:30:21.696239+01:00"} -{"id":"agent-relay-ekk9","title":"Add @-mention autocomplete to v2 message composer","description":"V1 dashboard has @-mention autocomplete in the message composer. When typing @A, it shows a dropdown with matching agents (@Alice, @Admin, etc.). V2's MessageComposer lacks this feature. Need to port the autocomplete logic from v1's components.ts (showMentionAutocomplete, completeMention functions) to v2.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-27T05:36:28.283162+01:00","updated_at":"2025-12-27T05:44:05.597544+01:00","closed_at":"2025-12-27T05:44:05.597544+01:00","close_reason":"Added missing mention-autocomplete CSS styles to globals.css"} {"id":"agent-relay-ex7","title":"Competitive analysis: Agent-to-agent messaging solutions","description":"Research and compare 17 multi-agent orchestration tools against agent-relay. Analyze: agent communication patterns, pros/cons, server/mobile support, memory architecture.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-23T16:34:31.174109+01:00","updated_at":"2025-12-23T16:55:19.264056+01:00","closed_at":"2025-12-23T16:55:19.264056+01:00"} {"id":"agent-relay-ey9","title":"Dashboard message sending not working","description":"User reports sending messages from dashboard doesn't work. Need to investigate.","status":"closed","priority":2,"issue_type":"bug","assignee":"SecondLead","created_at":"2025-12-22T15:54:48.975146+01:00","updated_at":"2025-12-22T17:07:48.705786+01:00","closed_at":"2025-12-22T17:07:48.705786+01:00"} -{"id":"agent-relay-f3q","title":"Make msg-read a subcommand of send or message","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T21:59:55.123772+01:00","updated_at":"2025-12-19T22:04:51.112763+01:00","closed_at":"2025-12-19T22:04:51.112763+01:00"} +{"id":"agent-relay-f3q","title":"Make msg-read a subcommand of send or message","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-19T21:59:55.123772+01:00","updated_at":"2025-12-19T22:04:51.112763+01:00","closed_at":"2025-12-19T22:04:51.112763+01:00"} {"id":"agent-relay-f9k","title":"Fix WebSocket Invalid frame header error","description":"Dashboard WebSocket throws Invalid frame header with 415KB payload. Missing wss.on('connection') handler causes race condition.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-23T17:17:26.412812+01:00","updated_at":"2025-12-23T17:18:30.4994+01:00","closed_at":"2025-12-23T17:18:30.4994+01:00"} {"id":"agent-relay-fb3","title":"Add configurable relay prefix for Gemini compatibility","description":"Gemini CLI uses @ for file references, conflicting with @relay:. Add --prefix flag to CLI and relayPrefix config option. Auto-detect CLI type and use appropriate default (\u003e\u003e for Gemini, @relay: for Claude/Codex). Update parser to use dynamic prefix. See docs/TMUX_IMPROVEMENTS.md for full implementation plan.","status":"closed","priority":2,"issue_type":"feature","assignee":"Coordinator","created_at":"2025-12-20T21:28:52.685002+01:00","updated_at":"2025-12-20T21:40:13.066766+01:00","closed_at":"2025-12-20T21:40:13.066766+01:00"} -{"id":"agent-relay-fuob","title":"Fix spawner tests for resolved tmux path","description":"Update src/bridge/spawner.test.ts mocks to expect resolved tmux binary path (e.g., \"/opt/homebrew/bin/tmux\") instead of just \"tmux\". 9 tests failing due to exact command string assertions.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-28T19:28:46.381693-05:00","updated_at":"2025-12-28T19:28:46.381693-05:00"} {"id":"agent-relay-ghy","title":"Team config: auto-spawn agents or auto-assign names from teams.json","description":"When a project has a teams.json config file, agent-relay up should either:\n\n1. **Auto-spawn option**: Automatically kick off terminal sessions for each agent defined in teams.json\n2. **Auto-assign option**: When users manually start sessions with `agent-relay -n \u003cname\u003e claude`, validate the name against teams.json and auto-assign roles/permissions\n\n## teams.json format\n```json\n{\n \"team\": \"my-project\",\n \"agents\": [\n {\"name\": \"Coordinator\", \"cli\": \"claude\", \"role\": \"coordinator\"},\n {\"name\": \"LeadDev\", \"cli\": \"claude\", \"role\": \"developer\"},\n {\"name\": \"Reviewer\", \"cli\": \"claude\", \"role\": \"reviewer\"}\n ],\n \"autoSpawn\": false\n}\n```\n\n## Behavior\n- If `autoSpawn: true`, `agent-relay up` spawns tmux sessions for each agent\n- If `autoSpawn: false`, validate names against config when agents connect\n- Store teams.json in project root or .agent-relay/teams.json\n\n## Commands\n- `agent-relay up --spawn` - force spawn all agents\n- `agent-relay up --no-spawn` - just start daemon, manual agent starts","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-19T23:13:02.482971+01:00","updated_at":"2025-12-22T22:11:38.639588+01:00"} {"id":"agent-relay-go9","title":"PostgreSQL storage adapter not implemented","description":"In storage/adapter.ts:152-162, PostgreSQL is listed as a storage option but throws 'not yet implemented'. For production multi-node deployments, SQLite won't scale. Implement PostgreSQL adapter for distributed storage.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-20T00:17:45.065487+01:00","updated_at":"2025-12-20T00:17:45.065487+01:00"} {"id":"agent-relay-hgd","title":"Reliable delivery: resume/replay via cursor/checkpoint","description":"Make ACK/RESUME real: persist a per-agent delivery cursor (last delivered/acked seq) and on reconnect replay missed messages from storage. Use existing delivery.seq as stream position, similar to swarm-mail DurableCursor checkpointing.","acceptance_criteria":"- Client can reconnect and receive missed messages\\n- Duplicate suppression is deterministic (id/seq based)\\n- Cursor persists across daemon restarts","notes":"Persistent resume tokens with stored cursors; replay unacked messages on reconnect; ack tracking + dedup; tests passing.","status":"in_progress","priority":1,"issue_type":"task","assignee":"BE-Dev","created_at":"2025-12-20T21:44:02.016428+01:00","updated_at":"2025-12-26T12:13:54.547885+01:00","labels":["durability","protocol"]} {"id":"agent-relay-hgn","title":"Agent team hierarchies with specialized sub-agents","description":"Each main agent should be able to spawn and coordinate a team of specialized sub-agents:\n\n- **Lead** → Operations Consultant (monitors behavior, provides retrospective feedback on coordination)\n- **Implementer** → Code Reviewer (reviews code before commits, suggests improvements)\n- **Designer** → UX Reviewer (validates design decisions)\n- **Architect** → Security Auditor (checks for vulnerabilities)\n\nImplementation ideas:\n1. Define agent profiles with 'team' configurations in .claude/agents/\n2. When an agent starts, it can auto-spawn its team members\n3. Sub-agents monitor the parent agent's work and provide feedback\n4. At session end, sub-agents provide retrospective summaries\n5. Use -\u003erelay:spawn to create sub-agents with specific roles\n\nThis enables each agent to have a dedicated support team for quality assurance and improvement.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-23T15:03:56.608847+01:00","updated_at":"2025-12-23T15:03:56.608847+01:00"} -{"id":"agent-relay-hgw","title":"PR-9 Review: Add reconnection logic to MultiProjectClient","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-22T21:54:09.712003+01:00","updated_at":"2025-12-24T11:50:55.708361+01:00","closed_at":"2025-12-24T11:50:55.708361+01:00"} +{"id":"agent-relay-hgw","title":"PR-9 Review: Add reconnection logic to MultiProjectClient","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-22T21:54:09.712003+01:00","updated_at":"2025-12-24T11:50:55.708361+01:00","closed_at":"2025-12-24T11:50:55.708361+01:00"} {"id":"agent-relay-hks","title":"Increase test coverage for daemon/server.ts, dashboard, and CLI","description":"Current coverage is only 39% overall. Key files with 0% coverage: daemon/server.ts, dashboard/server.ts, cli/index.ts, wrapper/client.ts, wrapper/tmux-wrapper.ts, utils/project-namespace.ts. Need integration tests for the daemon startup/shutdown lifecycle and CLI commands.","status":"closed","priority":2,"issue_type":"task","assignee":"Lead","created_at":"2025-12-20T00:17:29.603137+01:00","updated_at":"2025-12-22T17:14:41.611717+01:00","closed_at":"2025-12-22T17:14:41.611717+01:00"} {"id":"agent-relay-hr1","title":"Show project reconnection status in dashboard","description":"Display project connection status (Connected/Reconnecting/Disconnected) in dashboard header. Use onProjectStateChange callbacks from MultiProjectClient to drive 'Connected/Disconnecting' indicators. Show per-project status and overall relay connection health. Color code: green (connected), yellow (reconnecting), red (disconnected).","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-24T11:51:32.74308+01:00","updated_at":"2025-12-24T11:52:22.778492+01:00","closed_at":"2025-12-24T11:52:22.778492+01:00"} {"id":"agent-relay-hvm","title":"Dashboard: Add ability to send messages to agents","description":"Add a message compose UI in the dashboard to send messages to any connected agent. Should include: (1) Agent selector dropdown, (2) Message input field, (3) Send button that dispatches via the relay protocol. This complements the existing message viewing capability.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-22T15:31:07.022117+01:00","updated_at":"2025-12-22T15:36:18.78037+01:00","closed_at":"2025-12-22T15:36:18.78037+01:00"} {"id":"agent-relay-i5f","title":"Fix rendering and display issues","description":"Relay messages display with garbled/overlapping text in terminal. Characters appear scattered and mixed with other terminal content. Example: message text like 'Ping me when you're ready' renders with letters displaced across the line. PR #10 added input detection (isInputClear, waitForClearInput, getCursorX) to prevent this, but it's not working effectively. Need to investigate why the input detection/wait logic isn't preventing message injection during active typing.","notes":"Parser improvements for continuation lines (bullets, numbered lists, box drawing chars for Gemini). Debug flag set to false.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-22T11:59:53.254169+01:00","updated_at":"2025-12-22T14:21:32.043374+01:00","closed_at":"2025-12-22T14:21:32.043374+01:00","labels":["review"]} -{"id":"agent-relay-i6gt","title":"Debug thinking block filter - tests fail despite correct patterns","notes":"Investigation notes:\n\nWhat was implemented:\n- Added THINKING_START and THINKING_END patterns to parser.ts (lines 69-71)\n- Added inThinkingBlock state variable (line 157)\n- Added detection logic in parsePassThrough (lines 445-465)\n- Added reset logic in flush() and reset() methods\n\nThe problem:\nTests show the thinking block content is NOT being filtered - it passes through to output.\nThe patterns match correctly when tested standalone in Node.\n\nWhat to investigate:\n1. The check is at lines 456-465 in parser.ts - why doesn't it match at runtime?\n2. Could be an issue with how the for loop flows or where the check is placed\n3. Try adding console.log in the detection code to trace execution\n4. The compiled code at dist/wrapper/parser.js lines 380-390 looks correct\n\nFiles modified:\n- src/wrapper/parser.ts - thinking block filter implementation\n- Tests were written but reverted (failing)","status":"open","priority":2,"issue_type":"bug","created_at":"2025-12-27T11:42:23.020797+01:00","updated_at":"2025-12-27T11:42:44.912268+01:00"} {"id":"agent-relay-i7s","title":"Add priority-based message queuing","description":"Implement message priority levels (urgent/high/normal/low) like AI Maestro. Urgent messages jump the queue and interrupt agents. Enables time-sensitive coordination in production scenarios.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-23T17:04:48.031237+01:00","updated_at":"2025-12-23T17:04:48.031237+01:00"} -{"id":"agent-relay-iblw","title":"Allow agents to spawn other agents via agent-relay API","description":"Agents should be able to spawn new agent processes programmatically via agent-relay API. Enables dynamic team scaling and parallel agent coordination. Implement: (1) Agent spawn endpoint, (2) API wrapper for agent code, (3) Spawn coordination, (4) Resource limits, (5) Tests.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-26T17:08:22.05611+01:00","updated_at":"2025-12-26T17:08:22.05611+01:00"} -{"id":"agent-relay-iic5","title":"Multi-line relay messages truncated in dashboard UI (regression)","description":"Multi-line relay messages are only displaying the first line in the dashboard UI, even though the parser is capturing all lines. Root cause investigation needed: (1) Dashboard rendering (may be truncating display), (2) WebSocket message transmission, (3) Message state management. Previously marked as fixed but still broken.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-26T17:05:33.146646+01:00","updated_at":"2025-12-26T17:20:58.611383+01:00","closed_at":"2025-12-26T17:20:58.611383+01:00"} -{"id":"agent-relay-j7y","title":"Implement composer toolbar buttons (bold, emoji)","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-23T16:26:23.598781+01:00","updated_at":"2025-12-23T16:27:53.92751+01:00","closed_at":"2025-12-23T16:27:53.92751+01:00"} -{"id":"agent-relay-j9z","title":"Message injection corrupts human input in progress","description":"When a relay message arrives while the human is actively typing, the injection (Esc + Ctrl-U + message + Enter) destroys their partial input. Need to detect active input state before injecting. Options: (1) Check tmux pane input buffer, (2) Queue messages until prompt detected, (3) Use tmux display-message instead of send-keys for non-intrusive notification, (4) Add visual indicator that message is pending.","design":"Mitigation ideas: 1) Prefer tmux non-intrusive notify: tmux display-message (and always write to inbox/storage) instead of send-keys when user is typing. 2) Gate send-keys injection on 'safe' state: pane idle AND prompt detected AND no partial input; otherwise queue. 3) If queued, show pending count via display-message.","status":"open","priority":1,"issue_type":"bug","created_at":"2025-12-20T21:48:03.280298+01:00","updated_at":"2025-12-20T22:00:04.196161+01:00","dependencies":[{"issue_id":"agent-relay-j9z","depends_on_id":"agent-relay-6rz","type":"blocks","created_at":"2025-12-20T21:48:10.118963+01:00","created_by":"daemon","metadata":"{}"}]} -{"id":"agent-relay-jmwh","title":"Bundle tmux or provide fallback so users don't need to install separately","description":"User installation failed: tmux command not found. Currently agent-relay requires tmux to be pre-installed on the system. Need to bundle tmux or provide a fallback so installation works out-of-the-box.\n\nOptions to explore:\n1. NPM package that includes tmux binaries (npm tmux package or similar)\n2. Postinstall script that downloads/installs tmux\n3. Fallback to alternative shell mechanism if tmux unavailable\n4. Docker-based approach (out of scope but document)\n\nAcceptance: Installation should succeed and agent-relay should work without requiring manual tmux install","status":"in_progress","priority":0,"issue_type":"feature","assignee":"FullStack","created_at":"2025-12-28T18:51:04.556744-05:00","updated_at":"2025-12-28T18:53:01.401617-05:00"} +{"id":"agent-relay-j7y","title":"Implement composer toolbar buttons (bold, emoji)","description":"","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-23T16:26:23.598781+01:00","updated_at":"2025-12-23T16:27:53.92751+01:00","closed_at":"2025-12-23T16:27:53.92751+01:00"} +{"id":"agent-relay-j9z","title":"Message injection corrupts human input in progress","description":"When a relay message arrives while the human is actively typing, the injection (Esc + Ctrl-U + message + Enter) destroys their partial input. Need to detect active input state before injecting. Options: (1) Check tmux pane input buffer, (2) Queue messages until prompt detected, (3) Use tmux display-message instead of send-keys for non-intrusive notification, (4) Add visual indicator that message is pending.","design":"Mitigation ideas: 1) Prefer tmux non-intrusive notify: tmux display-message (and always write to inbox/storage) instead of send-keys when user is typing. 2) Gate send-keys injection on 'safe' state: pane idle AND prompt detected AND no partial input; otherwise queue. 3) If queued, show pending count via display-message.","status":"open","priority":1,"issue_type":"bug","created_at":"2025-12-20T21:48:03.280298+01:00","updated_at":"2025-12-20T22:00:04.196161+01:00","dependencies":[{"issue_id":"agent-relay-j9z","depends_on_id":"agent-relay-6rz","type":"blocks","created_at":"2025-12-20T21:48:10.118963+01:00","created_by":"daemon"}]} {"id":"agent-relay-k8t","title":"Consider backend-focused agent for daemon work","description":"Session retrospective: only had FE-Dev specialist, so backend work (permission detection, daemon issues) fell to Lead. Consider spawning a dedicated backend agent for daemon/server work in future sessions.","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-23T23:10:24.756163+01:00","updated_at":"2025-12-23T23:10:24.756163+01:00"} {"id":"agent-relay-kaj","title":"Show project connection status in dashboard (Connected/Reconnecting/Disconnected)","description":"Use onProjectStateChange callbacks from MultiProjectClient to show connection status in dashboard. When onProjectStateChange(id, false) fires, show 'Disconnected' or 'Reconnecting...' indicator. When onProjectStateChange(id, true) fires, show 'Connected'. Could use a status badge in the sidebar or header.","status":"closed","priority":2,"issue_type":"feature","assignee":"FE-Dev","created_at":"2025-12-24T11:51:40.774105+01:00","updated_at":"2025-12-24T11:55:00.944949+01:00","closed_at":"2025-12-24T11:55:00.944949+01:00"} {"id":"agent-relay-kto","title":"Add topic-based pub/sub messaging","description":"Add topic subscription alongside direct addressing. Agents can subscribe to topics (e.g., 'errors', 'deploys', 'reviews') and receive all messages on those topics. Complements existing -\u003erelay:Name with -\u003erelay:#topic pattern. Pattern from Claude-Flow's message bus.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-23T17:05:27.213842+01:00","updated_at":"2025-12-23T17:05:27.213842+01:00"} -{"id":"agent-relay-kw80","title":"Plan and execute Dashboard v1 → v2 migration","description":"## Dashboard v1 → v2 Migration Plan\n\n### Feature Parity Analysis (COMPLETED)\n\n**V1 Features in V2:** ✅ ALL FEATURES PORTED\n- Agent list with online/offline status\n- Message list with date dividers \n- Channel selection (general + DMs)\n- Command palette (Cmd+K)\n- Spawn agent modal\n- Thread support\n- Fleet view (multi-server)\n- Release agent button\n- Needs attention badge\n- ✅ @-mention autocomplete (agent-relay-ekk9 - DONE)\n\n**V2-Only Features (Enhancements):**\n- Theme support (light/dark/system)\n- Settings panel\n- Trajectory viewer\n- Decision queue\n- Enhanced broadcast composer with templates\n- Notification toasts\n\n### Migration Steps\n\n1. **[agent-relay-lls5]** Configure parallel running (v1:4280, v2:4281) - READY\n2. **[agent-relay-s8hg]** Add --dashboard=v1|v2 CLI flag - blocked by lls5\n3. ~~**[agent-relay-ekk9]** Fix @-mention autocomplete~~ ✅ DONE\n4. **[agent-relay-qkey]** Switch default to v2 - blocked by s8hg\n\n### Rollback Procedure\n\n1. Use `agent-relay up --dashboard=v1` to revert instantly\n2. V1 code remains in place until v2 is stable\n3. No data migration needed - both share same backend/storage","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-26T17:01:58.36924+01:00","updated_at":"2025-12-27T05:49:48.797956+01:00","dependencies":[{"issue_id":"agent-relay-kw80","depends_on_id":"agent-relay-cuer","type":"blocks","created_at":"2025-12-26T17:02:04.103774+01:00","created_by":"daemon","metadata":"{}"},{"issue_id":"agent-relay-kw80","depends_on_id":"agent-relay-ekk9","type":"blocks","created_at":"2025-12-27T05:36:37.645846+01:00","created_by":"daemon"}]} {"id":"agent-relay-kzw","title":"Project namespace uses /tmp which can be cleared on reboot","description":"In utils/project-namespace.ts:13, BASE_DIR is /tmp/agent-relay. On macOS/Linux, /tmp is cleared on reboot, losing all message history. Consider: (1) XDG_DATA_HOME fallback, (2) ~/.agent-relay option, (3) Per-project .agent-relay folder.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-20T00:18:33.224965+01:00","updated_at":"2025-12-20T00:18:33.224965+01:00"} -{"id":"agent-relay-l99","title":"Add output filtering for cleaner logs","description":"Filter noisy patterns from logs: thinking indicators [1/418], empty ANSI-only lines, etc. Add config.filterLogs option. See docs/TMUX_IMPROVEMENTS.md for implementation details.","status":"open","priority":4,"issue_type":"feature","created_at":"2025-12-20T21:28:51.199736+01:00","updated_at":"2025-12-20T21:28:51.199736+01:00","dependencies":[{"issue_id":"agent-relay-l99","depends_on_id":"agent-relay-1ek","type":"blocks","created_at":"2025-12-20T21:29:26.987526+01:00","created_by":"daemon","metadata":"{}"}]} -{"id":"agent-relay-lfky","title":"Add 'agent thinking/processing' indicator to dashboard","description":"When an agent receives a message, the dashboard should show a visual indicator that the agent is processing/thinking. Similar to 'typing...' indicators in chat apps.\n\n**Requirements:**\n1. Backend: Track agent status (idle/processing/responding)\n2. Protocol: Agents report when they start/finish processing a message\n3. Dashboard UI: Show pulsing/animated indicator next to agent when processing\n4. Timeout: Auto-clear indicator if no response after X seconds\n\n**Implementation options:**\n- Agents send STATUS: PROCESSING / STATUS: IDLE messages\n- Or use heartbeat with embedded state\n- Or infer from message timestamps (less reliable)\n\nRequested by: Dashboard agent during v1→v2 migration planning","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-27T05:38:19.022332+01:00","updated_at":"2025-12-27T10:59:41.070383+01:00","closed_at":"2025-12-27T10:59:41.070383+01:00","close_reason":"Processing indicator implementation complete with router tracking and dashboard color support"} -{"id":"agent-relay-lls5","title":"Configure daemon to serve v2 dashboard on alt port","description":"Add daemon configuration to serve v2 dashboard (Next.js) on port 4281 while keeping v1 on 4280. This enables parallel running during migration period.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-27T05:36:49.475705+01:00","updated_at":"2025-12-27T05:36:49.475705+01:00"} -{"id":"agent-relay-lto","title":"Command palette channel/chat selection","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-23T16:15:32.818842+01:00","updated_at":"2025-12-23T16:18:00.952354+01:00","closed_at":"2025-12-23T16:18:00.952354+01:00"} +{"id":"agent-relay-l99","title":"Add output filtering for cleaner logs","description":"Filter noisy patterns from logs: thinking indicators [1/418], empty ANSI-only lines, etc. Add config.filterLogs option. See docs/TMUX_IMPROVEMENTS.md for implementation details.","status":"open","priority":4,"issue_type":"feature","created_at":"2025-12-20T21:28:51.199736+01:00","updated_at":"2025-12-20T21:28:51.199736+01:00","dependencies":[{"issue_id":"agent-relay-l99","depends_on_id":"agent-relay-1ek","type":"blocks","created_at":"2025-12-20T21:29:26.987526+01:00","created_by":"daemon"}]} +{"id":"agent-relay-lto","title":"Command palette channel/chat selection","description":"","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-23T16:15:32.818842+01:00","updated_at":"2025-12-23T16:18:00.952354+01:00","closed_at":"2025-12-23T16:18:00.952354+01:00"} {"id":"agent-relay-mcl","title":"Gemini agent cannot send messages properly","description":"Gemini agent cannot autonomously send relay messages. When Gemini outputs @relay: patterns, it enters shell mode instead of being intercepted by the relay wrapper. Manual typing works fine. This suggests a PTY parsing issue specific to how Gemini outputs the @relay: pattern - possibly timing, escaping, or output buffering differences compared to Claude.","status":"closed","priority":2,"issue_type":"bug","assignee":"LeadDev","created_at":"2025-12-20T00:29:15.797552+01:00","updated_at":"2025-12-20T21:56:07.20143+01:00","closed_at":"2025-12-20T21:56:07.20143+01:00"} {"id":"agent-relay-mkz","title":"Fix multi-line message display in dashboard","description":"Multi-line messages are not displaying properly in the dashboard UI. This is a critical UX issue affecting message readability. Need to fix the frontend rendering of message content to properly handle newlines, formatting, and multi-line text.","status":"closed","priority":1,"issue_type":"bug","assignee":"Frontend","created_at":"2025-12-25T14:11:25.663841+01:00","updated_at":"2025-12-26T11:51:07.14395+01:00","closed_at":"2025-12-26T11:51:07.14395+01:00"} -{"id":"agent-relay-mplo","title":"Parser: Fix overcapturing text in multi-line relay messages","description":"Recent parser improvements (agent-relay-6dl8 fix) are still overcapturing text in multi-line relay messages. Dashboard reports text is being included beyond message boundaries. Need to review parser.ts finishBlock() and handle parsing edge cases.","status":"closed","priority":1,"issue_type":"bug","assignee":"FullStack","created_at":"2025-12-26T21:06:42.006611+01:00","updated_at":"2025-12-27T05:43:25.432704+01:00","closed_at":"2025-12-27T05:43:25.432704+01:00","close_reason":"Fixed: Reverted parser to simpler logic - inline messages are single-line only, only indented lines continue (TUI wrapping). Multi-line messages should use [[RELAY]] block format."} -{"id":"agent-relay-n36","title":"Add metrics dashboard for system health monitoring","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-25T14:07:36.499133+01:00","updated_at":"2025-12-26T11:41:20.33149+01:00","closed_at":"2025-12-26T11:41:20.33149+01:00"} -{"id":"agent-relay-nox","title":"Dashboard not showing live agents or messages","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-23T12:11:48.614811+01:00","updated_at":"2025-12-23T12:26:51.322956+01:00","closed_at":"2025-12-23T12:26:51.322956+01:00"} +{"id":"agent-relay-n36","title":"Add metrics dashboard for system health monitoring","description":"","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-25T14:07:36.499133+01:00","updated_at":"2025-12-26T11:41:20.33149+01:00","closed_at":"2025-12-26T11:41:20.33149+01:00"} +{"id":"agent-relay-nox","title":"Dashboard not showing live agents or messages","description":"","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-23T12:11:48.614811+01:00","updated_at":"2025-12-23T12:26:51.322956+01:00","closed_at":"2025-12-23T12:26:51.322956+01:00"} {"id":"agent-relay-oiw","title":"BUG: No per-agent inbox files created","description":"Expected /tmp/agent-relay/\u003cproject\u003e/\u003cagent\u003e/inbox.md but only team/agents.json exists. Agents cannot read their inbox via file.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-20T21:46:24.826442+01:00","updated_at":"2025-12-22T14:29:46.830632+01:00","closed_at":"2025-12-22T14:29:46.830632+01:00"} {"id":"agent-relay-p0k","title":"Gemini agents cannot send relay messages","description":"Gem (Gemini CLI) can receive messages but cannot send them. Tried: -\u003erelay:, \u003e\u003erelay:, escaping with backticks, splitting lines. Messages don't get captured/sent. Possibly Gemini CLI output handling differs from Claude/Codex. Need to investigate how Gemini outputs text and whether the tmux capture-pane is picking it up correctly.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-22T13:52:43.407612+01:00","updated_at":"2025-12-22T13:55:11.068344+01:00","closed_at":"2025-12-22T13:55:11.068344+01:00"} -{"id":"agent-relay-pvx","title":"Dashboard agent detection debugging","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-23T17:14:36.376736+01:00","updated_at":"2025-12-23T17:16:13.115531+01:00","closed_at":"2025-12-23T17:16:13.115531+01:00"} -{"id":"agent-relay-qkey","title":"Switch default dashboard to v2","description":"After feature parity is verified and parallel running period is complete, switch the default dashboard from v1 to v2. Update agent-relay up command to serve v2 by default.","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-27T05:36:52.036031+01:00","updated_at":"2025-12-27T05:36:52.036031+01:00","dependencies":[{"issue_id":"agent-relay-qkey","depends_on_id":"agent-relay-s8hg","type":"blocks","created_at":"2025-12-27T05:37:00.746565+01:00","created_by":"daemon"},{"issue_id":"agent-relay-qkey","depends_on_id":"agent-relay-ekk9","type":"blocks","created_at":"2025-12-27T05:37:00.817799+01:00","created_by":"daemon"}]} +{"id":"agent-relay-pvx","title":"Dashboard agent detection debugging","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-23T17:14:36.376736+01:00","updated_at":"2025-12-23T17:16:13.115531+01:00","closed_at":"2025-12-23T17:16:13.115531+01:00"} {"id":"agent-relay-rm7","title":"Add scheduling strategies for task distribution","description":"Implement scheduling strategies for distributing tasks across agents. Start with round-robin, then add capability-based, least-loaded, and affinity strategies. Inspired by Claude-Flow's 4-strategy approach. Enables autonomous task assignment instead of manual coordination.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-23T17:03:22.014068+01:00","updated_at":"2025-12-23T17:03:22.014068+01:00"} {"id":"agent-relay-ruv","title":"Add work stealing for load balancing","description":"Implement work stealing so idle agents can claim tasks from overloaded peers. Key production feature for better resource utilization. Pattern from Claude-Flow's WorkStealingCoordinator. Requires task queue visibility and agent load metrics.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-23T17:03:28.153623+01:00","updated_at":"2025-12-23T17:03:28.153623+01:00"} -{"id":"agent-relay-s8hg","title":"Add dashboard version switch to CLI","description":"Add --dashboard=v1|v2 flag to agent-relay up command to choose which dashboard version to run. Default should remain v1 until migration is complete.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-27T05:36:51.0113+01:00","updated_at":"2025-12-27T05:36:51.0113+01:00","dependencies":[{"issue_id":"agent-relay-s8hg","depends_on_id":"agent-relay-lls5","type":"blocks","created_at":"2025-12-27T05:37:00.684918+01:00","created_by":"daemon"}]} {"id":"agent-relay-sio","title":"Add graceful degradation when relay daemon is unavailable","description":"In wrapper/tmux-wrapper.ts:195-197, daemon connection failures are silently caught. Consider: (1) Periodic reconnection attempts, (2) Queueing messages for later delivery, (3) Visual indicator in terminal showing connection status.","status":"closed","priority":2,"issue_type":"feature","assignee":"Implementer","created_at":"2025-12-20T00:17:32.600333+01:00","updated_at":"2025-12-22T17:12:29.892345+01:00","closed_at":"2025-12-22T17:12:29.892345+01:00"} -{"id":"agent-relay-t4b","title":"Add spawn agent UI to dashboard","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-25T13:20:54.452959+01:00","updated_at":"2025-12-25T13:25:05.019339+01:00","closed_at":"2025-12-25T13:25:05.019339+01:00"} +{"id":"agent-relay-t4b","title":"Add spawn agent UI to dashboard","description":"","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-25T13:20:54.452959+01:00","updated_at":"2025-12-25T13:25:05.019339+01:00","closed_at":"2025-12-25T13:25:05.019339+01:00"} {"id":"agent-relay-tun","title":"Debug output leaking to terminal during relay message send","description":"When sending relay messages (observed with relay:Gem), debug output is leaking to the terminal repeatedly. Output shows: 'relay:Gem] isInputClear: lastLine=\"\" ~/.../agent-relay (main*)' and 'ear=false' (truncated 'isInputClear=false'). Lines are split/corrupted and repeating in a loop. Likely issue in the tmux wrapper or message injection code where debug logging isn't being suppressed properly.","status":"closed","priority":1,"issue_type":"bug","assignee":"Lead","created_at":"2025-12-22T13:40:28.576361+01:00","updated_at":"2025-12-22T13:42:16.746166+01:00","closed_at":"2025-12-22T13:42:16.746166+01:00"} {"id":"agent-relay-tx9","title":"Add circuit breaker for unresponsive agents","description":"Implement circuit breaker pattern to handle unresponsive or failing agents. Auto-detect agents that stop responding, temporarily remove from routing, attempt recovery, alert on persistent failures. Prevents cascade failures in production. Pattern from Claude-Flow's CircuitBreakerManager.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-23T17:03:48.162055+01:00","updated_at":"2025-12-23T17:03:48.162055+01:00"} {"id":"agent-relay-u2z","title":"Add @-mention autocomplete with Tab in dashboard chat input","description":"When a user types '@' in the dashboard chat input, show a dropdown of available/connected agents. Tab key should autocomplete the selected agent name. This enables easy @-mentioning of agents for direct messages.","status":"closed","priority":1,"issue_type":"feature","assignee":"FE-Dev","created_at":"2025-12-23T22:52:34.749062+01:00","updated_at":"2025-12-23T22:57:11.634365+01:00","closed_at":"2025-12-23T22:57:11.634365+01:00"} @@ -129,17 +115,17 @@ {"id":"agent-relay-v57","title":"No message expiration/cleanup in SQLite storage","description":"SQLite adapter has no TTL or cleanup mechanism for old messages. Over time, the database will grow unbounded. Add: (1) Configurable message retention period, (2) Automatic cleanup job, (3) Index on ts column is there but no cleanup uses it.","status":"closed","priority":2,"issue_type":"task","assignee":"Lead","created_at":"2025-12-20T00:18:01.86766+01:00","updated_at":"2025-12-22T17:17:41.867951+01:00","closed_at":"2025-12-22T17:17:41.867951+01:00"} {"id":"agent-relay-v7f","title":"Add conflict detection and resolution","description":"Implement conflict detection when multiple agents work on same files/resources. Add resolution strategies: priority-based, timestamp-based, voting-based. Prevents race conditions in production. Pattern from Claude-Flow's ConflictResolver and OptimisticLockManager.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-23T17:03:34.498546+01:00","updated_at":"2025-12-23T17:03:34.498546+01:00"} {"id":"agent-relay-vxc","title":"Navigation consistency between bridge and project views","description":"Ensure navigation patterns are consistent when switching between bridge and project dashboard views","status":"closed","priority":2,"issue_type":"task","assignee":"FE-Dev","created_at":"2025-12-24T11:47:46.62539+01:00","updated_at":"2025-12-24T12:00:15.805473+01:00","closed_at":"2025-12-24T12:00:15.805473+01:00"} -{"id":"agent-relay-wsd","title":"URGENT: Gemini interprets relay messages as shell commands","status":"closed","priority":0,"issue_type":"bug","created_at":"2025-12-22T14:23:05.989732+01:00","updated_at":"2025-12-22T14:24:18.714626+01:00","closed_at":"2025-12-22T14:24:18.714626+01:00"} +{"id":"agent-relay-wsd","title":"URGENT: Gemini interprets relay messages as shell commands","description":"","status":"closed","priority":0,"issue_type":"bug","created_at":"2025-12-22T14:23:05.989732+01:00","updated_at":"2025-12-22T14:24:18.714626+01:00","closed_at":"2025-12-22T14:24:18.714626+01:00"} {"id":"agent-relay-xn1","title":"Add remote access via Cloudflare tunnels","description":"Enable remote/mobile access to agent-relay dashboard and control. Use Cloudflare tunnels (like Maestro) for secure access without port forwarding. Include QR code generation for easy mobile connection. Major gap vs competitors - AI Maestro and Maestro both have this.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-23T17:04:28.788302+01:00","updated_at":"2025-12-23T17:04:28.788302+01:00"} {"id":"agent-relay-ylf","title":"Add knowledge graph for persistent memory","description":"Implement knowledge graph storage (like Mimir's Neo4j approach) for persistent cross-session context. Store entities, relationships, and semantic embeddings. Enable agents to learn from past sessions and share knowledge. Could use SQLite with FTS5 or optional Neo4j adapter.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-23T17:05:08.367875+01:00","updated_at":"2025-12-23T17:05:08.367875+01:00"} {"id":"agent-relay-ytj","title":"Filter chat view to show only messages with selected agent","description":"When clicking on a particular agent in the dashboard sidebar, the chat view should filter to only show messages between Dashboard and that specific agent. This provides a focused conversation view instead of showing all messages.","status":"closed","priority":2,"issue_type":"feature","assignee":"FE-Dev","created_at":"2025-12-23T22:53:31.325805+01:00","updated_at":"2025-12-23T22:58:41.407339+01:00","closed_at":"2025-12-23T22:58:41.407339+01:00"} {"id":"agent-relay-ytk","title":"Simplify agent-relay CLI interface","description":"The CLI has too many commands. Consolidate into a clean, simple interface:\n\n1. `relay start` should also kick off the dashboard automatically\n2. Merge redundant team-* commands\n3. Remove rarely-used commands or make them subcommands\n4. Target: 5-7 top-level commands max\n\nCurrent commands to evaluate:\n- start, stop, status (keep)\n- wrap (keep)\n- project (keep or merge into status)\n- send (keep)\n- team-setup, team-status, team-send, team-check, team-listen, team-start (consolidate)\n- msg-read (make subcommand or integrate)\n- dashboard (merge into start)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-22T21:51:14.010849+01:00","updated_at":"2025-12-22T21:51:14.010849+01:00","closed_at":"2025-12-19T22:08:44.107992+01:00"} {"id":"agent-relay-ytl","title":"Add cross-project message syntax to parser","description":"Extend the parser (src/wrapper/parser.ts) to recognize @relay:project:agent syntax for cross-project messaging. Currently only @relay:agent is parsed. Need to add pattern matching for the project:agent format.","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-12-21T10:45:00Z","updated_at":"2025-12-24T11:46:51.622328+01:00","closed_at":"2025-12-24T11:46:51.622328+01:00"} {"id":"agent-relay-ytm","title":"Enhance dashboard for multi-project bridge view","description":"Add a bridge view to the dashboard that shows all connected projects, their leads, workers, and cross-project message flow. Should be accessible at /bridge?projects=... See docs/DESIGN_BRIDGE_STAFFING.md for mockups.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-21T10:45:00Z","updated_at":"2025-12-22T22:04:18.511591+01:00","closed_at":"2025-12-22T22:04:18.511591+01:00"} -{"id":"agent-relay-ytn","title":"Shadow agent pairing: CLI support","description":"Add --shadow and --shadow-role CLI flags to spawn paired agents. Example: `agent-relay -n Lead --shadow Auditor claude` spawns Lead with an Auditor shadow that observes and provides feedback. The shadow receives all messages the primary sends/receives.","status":"in_progress","priority":1,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-26T20:27:51.517714+01:00","labels":["shadow-agents"]} -{"id":"agent-relay-yto","title":"Shadow agent pairing: Config file support","description":"Support shadow agent configuration in .agent-relay.json. Define pairs (primary-\u003eshadow mappings) and roles (prompt templates like performance-auditor, integration-tester). Config format: { pairs: { Lead: { shadow: 'Auditor', shadowRole: 'performance-auditor' } }, roles: { 'performance-auditor': { prompt: '...', speakOn: ['SESSION_END'] } } }","status":"open","priority":1,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-27T10:56:12.797952+01:00","labels":["shadow-agents"],"dependencies":[{"issue_id":"agent-relay-yto","depends_on_id":"agent-relay-ytn","type":"blocks","created_at":"2025-12-25T08:00:00Z","created_by":"daemon","metadata":"{}"}]} -{"id":"agent-relay-ytp","title":"Shadow agent pairing: spawnWithShadow() API","description":"Programmatic API for spawning paired agents. Example: `const { primary, shadow } = await spawnWithShadow({ primary: { name: 'Lead', command: 'claude' }, shadow: { name: 'Auditor', role: 'performance-auditor', speakOn: ['SESSION_END'] } })`. Returns handles to both agents for orchestration.","status":"open","priority":1,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-27T10:56:14.164476+01:00","labels":["shadow-agents"],"dependencies":[{"issue_id":"agent-relay-ytp","depends_on_id":"agent-relay-ytn","type":"blocks","created_at":"2025-12-25T08:00:00Z","created_by":"daemon","metadata":"{}"}]} -{"id":"agent-relay-ytq","title":"Shadow agent pairing: Built-in roles","description":"Ship built-in shadow roles: (1) performance-auditor - observes silently, provides feedback at session end; (2) integration-tester - writes tests as primary writes code; (3) code-reviewer - reviews changes before commit; (4) security-auditor - flags security issues in real-time. Roles define prompts and speakOn triggers.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-25T08:00:00Z","labels":["shadow-agents"],"dependencies":[{"issue_id":"agent-relay-ytq","depends_on_id":"agent-relay-yto","type":"blocks","created_at":"2025-12-25T08:00:00Z","created_by":"daemon","metadata":"{}"}]} +{"id":"agent-relay-ytn","title":"Shadow agent pairing: CLI support","description":"Add --shadow and --shadow-role CLI flags to spawn paired agents. Example: `agent-relay -n Lead --shadow Auditor claude` spawns Lead with an Auditor shadow that observes and provides feedback. The shadow receives all messages the primary sends/receives.","status":"open","priority":1,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-25T08:00:00Z","labels":["shadow-agents"]} +{"id":"agent-relay-yto","title":"Shadow agent pairing: Config file support","description":"Support shadow agent configuration in .agent-relay.json. Define pairs (primary-\u003eshadow mappings) and roles (prompt templates like performance-auditor, integration-tester). Config format: { pairs: { Lead: { shadow: 'Auditor', shadowRole: 'performance-auditor' } }, roles: { 'performance-auditor': { prompt: '...', speakOn: ['SESSION_END'] } } }","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-25T08:00:00Z","labels":["shadow-agents"],"dependencies":[{"issue_id":"agent-relay-yto","depends_on_id":"agent-relay-ytn","type":"blocks","created_at":"2025-12-25T08:00:00Z","created_by":"daemon"}]} +{"id":"agent-relay-ytp","title":"Shadow agent pairing: spawnWithShadow() API","description":"Programmatic API for spawning paired agents. Example: `const { primary, shadow } = await spawnWithShadow({ primary: { name: 'Lead', command: 'claude' }, shadow: { name: 'Auditor', role: 'performance-auditor', speakOn: ['SESSION_END'] } })`. Returns handles to both agents for orchestration.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-25T08:00:00Z","labels":["shadow-agents"],"dependencies":[{"issue_id":"agent-relay-ytp","depends_on_id":"agent-relay-ytn","type":"blocks","created_at":"2025-12-25T08:00:00Z","created_by":"daemon"}]} +{"id":"agent-relay-ytq","title":"Shadow agent pairing: Built-in roles","description":"Ship built-in shadow roles: (1) performance-auditor - observes silently, provides feedback at session end; (2) integration-tester - writes tests as primary writes code; (3) code-reviewer - reviews changes before commit; (4) security-auditor - flags security issues in real-time. Roles define prompts and speakOn triggers.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-25T08:00:00Z","labels":["shadow-agents"],"dependencies":[{"issue_id":"agent-relay-ytq","depends_on_id":"agent-relay-yto","type":"blocks","created_at":"2025-12-25T08:00:00Z","created_by":"daemon"}]} {"id":"agent-relay-ytr","title":"Shadow agent pairing: Message routing","description":"Implement shadow message routing: shadows receive copies of all messages their primary sends/receives. Add 'shadow' subscription type in router. Shadows can be configured to speakOn specific triggers (SESSION_END, CODE_WRITTEN, REVIEW_REQUEST) or stay silent until asked.","status":"open","priority":1,"issue_type":"feature","created_at":"2025-12-25T08:00:00Z","updated_at":"2025-12-25T08:00:00Z","labels":["shadow-agents"]} {"id":"agent-relay-yts","title":"Wire up spawn/release command handling in lead mode","description":"Lead agents can output @relay:spawn and @relay:release commands, but the handler needs to be wired up to actually call the AgentSpawner. The spawner exists in src/bridge/spawner.ts, need to intercept these commands in the TmuxWrapper or add a message handler.","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-12-21T10:45:00Z","updated_at":"2025-12-24T15:09:22.953849+01:00","closed_at":"2025-12-24T15:09:22.953849+01:00"} {"id":"agent-relay-ytt","title":"Implement threaded conversations (Slack-style)","description":"Add thread replies with parent_message_id tracking in storage, nested thread UI display in dashboard, thread notification badges, and expand/collapse thread views. Backend: add parent_message_id column, thread reply routing. Frontend: inline thread expansion, reply count badges, thread-specific compose.","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-12-23T12:00:00Z","updated_at":"2025-12-23T16:37:22.987643+01:00","closed_at":"2025-12-23T16:37:22.987643+01:00"}