agent-relay 1.0.8 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/README.md +158 -0
  2. package/dist/bridge/config.d.ts +41 -0
  3. package/dist/bridge/config.d.ts.map +1 -0
  4. package/dist/bridge/config.js +143 -0
  5. package/dist/bridge/config.js.map +1 -0
  6. package/dist/bridge/index.d.ts +10 -0
  7. package/dist/bridge/index.d.ts.map +1 -0
  8. package/dist/bridge/index.js +10 -0
  9. package/dist/bridge/index.js.map +1 -0
  10. package/dist/bridge/multi-project-client.d.ts +99 -0
  11. package/dist/bridge/multi-project-client.d.ts.map +1 -0
  12. package/dist/bridge/multi-project-client.js +386 -0
  13. package/dist/bridge/multi-project-client.js.map +1 -0
  14. package/dist/bridge/spawner.d.ts +46 -0
  15. package/dist/bridge/spawner.d.ts.map +1 -0
  16. package/dist/bridge/spawner.js +223 -0
  17. package/dist/bridge/spawner.js.map +1 -0
  18. package/dist/bridge/types.d.ts +55 -0
  19. package/dist/bridge/types.d.ts.map +1 -0
  20. package/dist/bridge/types.js +6 -0
  21. package/dist/bridge/types.js.map +1 -0
  22. package/dist/bridge/utils.d.ts +30 -0
  23. package/dist/bridge/utils.d.ts.map +1 -0
  24. package/dist/bridge/utils.js +54 -0
  25. package/dist/bridge/utils.js.map +1 -0
  26. package/dist/cli/index.js +564 -5
  27. package/dist/cli/index.js.map +1 -1
  28. package/dist/daemon/agent-registry.d.ts.map +1 -1
  29. package/dist/daemon/agent-registry.js +6 -1
  30. package/dist/daemon/agent-registry.js.map +1 -1
  31. package/dist/daemon/connection.d.ts +22 -0
  32. package/dist/daemon/connection.d.ts.map +1 -1
  33. package/dist/daemon/connection.js +59 -13
  34. package/dist/daemon/connection.js.map +1 -1
  35. package/dist/daemon/router.d.ts +27 -0
  36. package/dist/daemon/router.d.ts.map +1 -1
  37. package/dist/daemon/router.js +108 -3
  38. package/dist/daemon/router.js.map +1 -1
  39. package/dist/daemon/server.d.ts +8 -0
  40. package/dist/daemon/server.d.ts.map +1 -1
  41. package/dist/daemon/server.js +95 -23
  42. package/dist/daemon/server.js.map +1 -1
  43. package/dist/dashboard/metrics.d.ts +105 -0
  44. package/dist/dashboard/metrics.d.ts.map +1 -0
  45. package/dist/dashboard/metrics.js +192 -0
  46. package/dist/dashboard/metrics.js.map +1 -0
  47. package/dist/dashboard/needs-attention.d.ts +24 -0
  48. package/dist/dashboard/needs-attention.d.ts.map +1 -0
  49. package/dist/dashboard/needs-attention.js +78 -0
  50. package/dist/dashboard/needs-attention.js.map +1 -0
  51. package/dist/dashboard/public/bridge.html +1272 -0
  52. package/dist/dashboard/public/index.html +2017 -879
  53. package/dist/dashboard/public/js/app.js +184 -0
  54. package/dist/dashboard/public/js/app.js.map +7 -0
  55. package/dist/dashboard/public/metrics.html +999 -0
  56. package/dist/dashboard/server.d.ts +13 -0
  57. package/dist/dashboard/server.d.ts.map +1 -1
  58. package/dist/dashboard/server.js +568 -13
  59. package/dist/dashboard/server.js.map +1 -1
  60. package/dist/dashboard/start.js +1 -1
  61. package/dist/dashboard/start.js.map +1 -1
  62. package/dist/dashboard-v2/index.d.ts +10 -0
  63. package/dist/dashboard-v2/index.d.ts.map +1 -0
  64. package/dist/dashboard-v2/index.js +54 -0
  65. package/dist/dashboard-v2/index.js.map +1 -0
  66. package/dist/dashboard-v2/lib/api.d.ts +95 -0
  67. package/dist/dashboard-v2/lib/api.d.ts.map +1 -0
  68. package/dist/dashboard-v2/lib/api.js +270 -0
  69. package/dist/dashboard-v2/lib/api.js.map +1 -0
  70. package/dist/dashboard-v2/lib/colors.d.ts +61 -0
  71. package/dist/dashboard-v2/lib/colors.d.ts.map +1 -0
  72. package/dist/dashboard-v2/lib/colors.js +198 -0
  73. package/dist/dashboard-v2/lib/colors.js.map +1 -0
  74. package/dist/dashboard-v2/lib/hierarchy.d.ts +74 -0
  75. package/dist/dashboard-v2/lib/hierarchy.d.ts.map +1 -0
  76. package/dist/dashboard-v2/lib/hierarchy.js +196 -0
  77. package/dist/dashboard-v2/lib/hierarchy.js.map +1 -0
  78. package/dist/dashboard-v2/types/index.d.ts +154 -0
  79. package/dist/dashboard-v2/types/index.d.ts.map +1 -0
  80. package/dist/dashboard-v2/types/index.js +6 -0
  81. package/dist/dashboard-v2/types/index.js.map +1 -0
  82. package/dist/storage/adapter.d.ts +21 -1
  83. package/dist/storage/adapter.d.ts.map +1 -1
  84. package/dist/storage/adapter.js +36 -0
  85. package/dist/storage/adapter.js.map +1 -1
  86. package/dist/storage/sqlite-adapter.d.ts +34 -0
  87. package/dist/storage/sqlite-adapter.d.ts.map +1 -1
  88. package/dist/storage/sqlite-adapter.js +253 -12
  89. package/dist/storage/sqlite-adapter.js.map +1 -1
  90. package/dist/utils/agent-config.d.ts +45 -0
  91. package/dist/utils/agent-config.d.ts.map +1 -0
  92. package/dist/utils/agent-config.js +118 -0
  93. package/dist/utils/agent-config.js.map +1 -0
  94. package/dist/wrapper/client.d.ts +8 -0
  95. package/dist/wrapper/client.d.ts.map +1 -1
  96. package/dist/wrapper/client.js +26 -0
  97. package/dist/wrapper/client.js.map +1 -1
  98. package/dist/wrapper/parser.d.ts +17 -0
  99. package/dist/wrapper/parser.d.ts.map +1 -1
  100. package/dist/wrapper/parser.js +334 -10
  101. package/dist/wrapper/parser.js.map +1 -1
  102. package/dist/wrapper/tmux-wrapper.d.ts +37 -2
  103. package/dist/wrapper/tmux-wrapper.d.ts.map +1 -1
  104. package/dist/wrapper/tmux-wrapper.js +178 -18
  105. package/dist/wrapper/tmux-wrapper.js.map +1 -1
  106. package/docs/AGENTS.md +105 -0
  107. package/docs/ARCHITECTURE_DECISIONS.md +175 -0
  108. package/docs/COMPETITIVE_ANALYSIS.md +897 -0
  109. package/docs/DESIGN_BRIDGE_STAFFING.md +878 -0
  110. package/docs/MONETIZATION.md +1679 -0
  111. package/docs/agent-relay-snippet.md +61 -0
  112. package/docs/dashboard-v2-plan.md +179 -0
  113. package/package.json +5 -2
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Metrics collection and computation for the Agent Relay dashboard.
3
+ * Provides real-time metrics for monitoring: throughput, agent health, error rates.
4
+ */
5
+ // Consider agents offline after 5 minutes of inactivity
6
+ const OFFLINE_THRESHOLD_MS = 5 * 60 * 1000;
7
+ /**
8
+ * Compute agent-level metrics from registry data
9
+ */
10
+ export function computeAgentMetrics(agents) {
11
+ const now = Date.now();
12
+ return agents.map((agent) => {
13
+ const lastSeenTime = new Date(agent.lastSeen).getTime();
14
+ const firstSeenTime = new Date(agent.firstSeen).getTime();
15
+ const isOnline = now - lastSeenTime < OFFLINE_THRESHOLD_MS;
16
+ const uptimeSeconds = Math.floor((now - firstSeenTime) / 1000);
17
+ return {
18
+ name: agent.name,
19
+ messagesSent: agent.messagesSent,
20
+ messagesReceived: agent.messagesReceived,
21
+ firstSeen: agent.firstSeen,
22
+ lastSeen: agent.lastSeen,
23
+ isOnline,
24
+ uptimeSeconds,
25
+ };
26
+ });
27
+ }
28
+ /**
29
+ * Compute throughput metrics from message history
30
+ */
31
+ export function computeThroughputMetrics(messages) {
32
+ const now = Date.now();
33
+ const oneMinuteAgo = now - 60 * 1000;
34
+ const oneHourAgo = now - 60 * 60 * 1000;
35
+ const oneDayAgo = now - 24 * 60 * 60 * 1000;
36
+ let lastMinute = 0;
37
+ let lastHour = 0;
38
+ let last24Hours = 0;
39
+ for (const msg of messages) {
40
+ const ts = new Date(msg.timestamp).getTime();
41
+ if (ts >= oneMinuteAgo)
42
+ lastMinute++;
43
+ if (ts >= oneHourAgo)
44
+ lastHour++;
45
+ if (ts >= oneDayAgo)
46
+ last24Hours++;
47
+ }
48
+ // Calculate average messages per minute over the last hour
49
+ const avgMessagesPerMinute = lastHour / 60;
50
+ return {
51
+ messagesLastMinute: lastMinute,
52
+ messagesLastHour: lastHour,
53
+ messagesLast24Hours: last24Hours,
54
+ avgMessagesPerMinute: Math.round(avgMessagesPerMinute * 100) / 100,
55
+ };
56
+ }
57
+ /**
58
+ * Compute session lifecycle metrics from session history
59
+ */
60
+ export function computeSessionMetrics(sessions) {
61
+ let activeSessions = 0;
62
+ let closedByAgent = 0;
63
+ let closedByDisconnect = 0;
64
+ let closedByError = 0;
65
+ for (const session of sessions) {
66
+ if (!session.endedAt) {
67
+ activeSessions++;
68
+ }
69
+ else {
70
+ switch (session.closedBy) {
71
+ case 'agent':
72
+ closedByAgent++;
73
+ break;
74
+ case 'disconnect':
75
+ closedByDisconnect++;
76
+ break;
77
+ case 'error':
78
+ closedByError++;
79
+ break;
80
+ default:
81
+ // Ended but no closedBy - treat as disconnect
82
+ closedByDisconnect++;
83
+ }
84
+ }
85
+ }
86
+ const closedSessions = closedByAgent + closedByDisconnect + closedByError;
87
+ const errorRate = closedSessions > 0 ? (closedByError / closedSessions) * 100 : 0;
88
+ // Format recent sessions for display (most recent 10)
89
+ const recentSessions = sessions
90
+ .slice(0, 10)
91
+ .map((s) => ({
92
+ id: s.id,
93
+ agentName: s.agentName,
94
+ startedAt: new Date(s.startedAt).toISOString(),
95
+ endedAt: s.endedAt ? new Date(s.endedAt).toISOString() : undefined,
96
+ closedBy: s.closedBy,
97
+ messageCount: s.messageCount,
98
+ }));
99
+ return {
100
+ totalSessions: sessions.length,
101
+ activeSessions,
102
+ closedByAgent,
103
+ closedByDisconnect,
104
+ closedByError,
105
+ errorRate: Math.round(errorRate * 100) / 100,
106
+ recentSessions,
107
+ };
108
+ }
109
+ /**
110
+ * Compute full system metrics
111
+ */
112
+ export function computeSystemMetrics(agents, messages, sessions = []) {
113
+ const agentMetrics = computeAgentMetrics(agents);
114
+ const throughput = computeThroughputMetrics(messages);
115
+ const sessionMetrics = computeSessionMetrics(sessions);
116
+ const onlineAgents = agentMetrics.filter((a) => a.isOnline).length;
117
+ const totalMessages = agents.reduce((sum, a) => sum + a.messagesSent + a.messagesReceived, 0) / 2; // Divide by 2 since each message is counted twice (sent + received)
118
+ return {
119
+ totalAgents: agents.length,
120
+ onlineAgents,
121
+ offlineAgents: agents.length - onlineAgents,
122
+ totalMessages: Math.round(totalMessages),
123
+ throughput,
124
+ sessions: sessionMetrics,
125
+ agents: agentMetrics,
126
+ timestamp: new Date().toISOString(),
127
+ };
128
+ }
129
+ /**
130
+ * Format metrics in Prometheus exposition format
131
+ */
132
+ export function formatPrometheusMetrics(metrics) {
133
+ const lines = [];
134
+ // Agent counts
135
+ lines.push('# HELP agent_relay_agents_total Total number of registered agents');
136
+ lines.push('# TYPE agent_relay_agents_total gauge');
137
+ lines.push(`agent_relay_agents_total ${metrics.totalAgents}`);
138
+ lines.push('# HELP agent_relay_agents_online Number of online agents');
139
+ lines.push('# TYPE agent_relay_agents_online gauge');
140
+ lines.push(`agent_relay_agents_online ${metrics.onlineAgents}`);
141
+ // Message throughput
142
+ lines.push('# HELP agent_relay_messages_total Total messages processed');
143
+ lines.push('# TYPE agent_relay_messages_total counter');
144
+ lines.push(`agent_relay_messages_total ${metrics.totalMessages}`);
145
+ lines.push('# HELP agent_relay_messages_last_minute Messages in last minute');
146
+ lines.push('# TYPE agent_relay_messages_last_minute gauge');
147
+ lines.push(`agent_relay_messages_last_minute ${metrics.throughput.messagesLastMinute}`);
148
+ lines.push('# HELP agent_relay_messages_last_hour Messages in last hour');
149
+ lines.push('# TYPE agent_relay_messages_last_hour gauge');
150
+ lines.push(`agent_relay_messages_last_hour ${metrics.throughput.messagesLastHour}`);
151
+ lines.push('# HELP agent_relay_messages_avg_per_minute Average messages per minute');
152
+ lines.push('# TYPE agent_relay_messages_avg_per_minute gauge');
153
+ lines.push(`agent_relay_messages_avg_per_minute ${metrics.throughput.avgMessagesPerMinute}`);
154
+ // Per-agent metrics
155
+ lines.push('# HELP agent_relay_agent_messages_sent Messages sent by agent');
156
+ lines.push('# TYPE agent_relay_agent_messages_sent counter');
157
+ for (const agent of metrics.agents) {
158
+ lines.push(`agent_relay_agent_messages_sent{agent="${agent.name}"} ${agent.messagesSent}`);
159
+ }
160
+ lines.push('# HELP agent_relay_agent_messages_received Messages received by agent');
161
+ lines.push('# TYPE agent_relay_agent_messages_received counter');
162
+ for (const agent of metrics.agents) {
163
+ lines.push(`agent_relay_agent_messages_received{agent="${agent.name}"} ${agent.messagesReceived}`);
164
+ }
165
+ lines.push('# HELP agent_relay_agent_online Agent online status (1=online, 0=offline)');
166
+ lines.push('# TYPE agent_relay_agent_online gauge');
167
+ for (const agent of metrics.agents) {
168
+ lines.push(`agent_relay_agent_online{agent="${agent.name}"} ${agent.isOnline ? 1 : 0}`);
169
+ }
170
+ lines.push('# HELP agent_relay_agent_uptime_seconds Agent uptime in seconds');
171
+ lines.push('# TYPE agent_relay_agent_uptime_seconds gauge');
172
+ for (const agent of metrics.agents) {
173
+ lines.push(`agent_relay_agent_uptime_seconds{agent="${agent.name}"} ${agent.uptimeSeconds}`);
174
+ }
175
+ // Session lifecycle metrics
176
+ lines.push('# HELP agent_relay_sessions_total Total number of sessions');
177
+ lines.push('# TYPE agent_relay_sessions_total counter');
178
+ lines.push(`agent_relay_sessions_total ${metrics.sessions.totalSessions}`);
179
+ lines.push('# HELP agent_relay_sessions_active Number of active sessions');
180
+ lines.push('# TYPE agent_relay_sessions_active gauge');
181
+ lines.push(`agent_relay_sessions_active ${metrics.sessions.activeSessions}`);
182
+ lines.push('# HELP agent_relay_sessions_closed_total Sessions closed by type');
183
+ lines.push('# TYPE agent_relay_sessions_closed_total counter');
184
+ lines.push(`agent_relay_sessions_closed_total{closed_by="agent"} ${metrics.sessions.closedByAgent}`);
185
+ lines.push(`agent_relay_sessions_closed_total{closed_by="disconnect"} ${metrics.sessions.closedByDisconnect}`);
186
+ lines.push(`agent_relay_sessions_closed_total{closed_by="error"} ${metrics.sessions.closedByError}`);
187
+ lines.push('# HELP agent_relay_sessions_error_rate Percentage of sessions closed by error');
188
+ lines.push('# TYPE agent_relay_sessions_error_rate gauge');
189
+ lines.push(`agent_relay_sessions_error_rate ${metrics.sessions.errorRate}`);
190
+ return lines.join('\n') + '\n';
191
+ }
192
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/dashboard/metrics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Derive which agents currently need attention based on message history.
3
+ *
4
+ * Heuristic:
5
+ * - Track the most recent inbound message to an agent for each conversation key.
6
+ * - Conversation key is thread ID if present, otherwise the counterparty agent.
7
+ * - Track the most recent outbound message from the agent for the same key.
8
+ * - An agent needs attention if their latest inbound message for any key is newer
9
+ * than their latest outbound message for that key.
10
+ */
11
+ export interface AttentionMessage {
12
+ from: string;
13
+ to: string;
14
+ timestamp: string;
15
+ thread?: string;
16
+ /** Whether the message was sent as a broadcast (to: '*') */
17
+ isBroadcast?: boolean;
18
+ }
19
+ /**
20
+ * Compute which agents have pending inbound messages they haven't answered.
21
+ * Only considers messages within the attention window (last 30 minutes).
22
+ */
23
+ export declare function computeNeedsAttention(messages: AttentionMessage[]): Set<string>;
24
+ //# sourceMappingURL=needs-attention.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"needs-attention.d.ts","sourceRoot":"","sources":["../../src/dashboard/needs-attention.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAgBD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CA6D/E"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Derive which agents currently need attention based on message history.
3
+ *
4
+ * Heuristic:
5
+ * - Track the most recent inbound message to an agent for each conversation key.
6
+ * - Conversation key is thread ID if present, otherwise the counterparty agent.
7
+ * - Track the most recent outbound message from the agent for the same key.
8
+ * - An agent needs attention if their latest inbound message for any key is newer
9
+ * than their latest outbound message for that key.
10
+ */
11
+ // Only consider messages from the last 30 minutes for "needs attention"
12
+ const ATTENTION_WINDOW_MS = 30 * 60 * 1000;
13
+ function updateLatest(map, agent, key, ts) {
14
+ const agentMap = map.get(agent) ?? new Map();
15
+ const prev = agentMap.get(key) ?? -Infinity;
16
+ if (ts > prev) {
17
+ agentMap.set(key, ts);
18
+ map.set(agent, agentMap);
19
+ }
20
+ }
21
+ /**
22
+ * Compute which agents have pending inbound messages they haven't answered.
23
+ * Only considers messages within the attention window (last 30 minutes).
24
+ */
25
+ export function computeNeedsAttention(messages) {
26
+ const latestInbound = new Map(); // agent -> (key -> ts)
27
+ const latestOutbound = new Map(); // agent -> (key -> ts)
28
+ const now = Date.now();
29
+ const cutoffTime = now - ATTENTION_WINDOW_MS;
30
+ for (const message of messages) {
31
+ const ts = Date.parse(message.timestamp);
32
+ if (Number.isNaN(ts))
33
+ continue;
34
+ // Detect broadcasts: either explicit isBroadcast flag or to === '*'
35
+ const isBroadcast = message.isBroadcast || message.to === '*';
36
+ // Inbound: messages directed to a specific agent (ignore broadcasts)
37
+ // Note: isBroadcast indicates the message was originally a broadcast, even though
38
+ // the 'to' field is set to the individual recipient for storage purposes
39
+ if (message.to && !isBroadcast) {
40
+ const inboundKey = message.thread ? `thread:${message.thread}` : `sender:${message.from}`;
41
+ updateLatest(latestInbound, message.to, inboundKey, ts);
42
+ }
43
+ // Outbound: track replies by thread (preferred) or by target agent
44
+ // Also treat broadcasts as clearing attention for all prior senders
45
+ if (message.from) {
46
+ const outboundKey = message.thread
47
+ ? `thread:${message.thread}`
48
+ : (message.to && !isBroadcast)
49
+ ? `sender:${message.to}`
50
+ : null;
51
+ if (outboundKey) {
52
+ updateLatest(latestOutbound, message.from, outboundKey, ts);
53
+ }
54
+ // Broadcasts clear attention: agent is actively participating
55
+ // Track as a "catch-all" outbound timestamp for this agent
56
+ if (isBroadcast) {
57
+ updateLatest(latestOutbound, message.from, '__broadcast__', ts);
58
+ }
59
+ }
60
+ }
61
+ const needsAttention = new Set();
62
+ latestInbound.forEach((keyMap, agent) => {
63
+ keyMap.forEach((inboundTs, key) => {
64
+ // Skip if inbound message is too old (outside attention window)
65
+ if (inboundTs < cutoffTime)
66
+ return;
67
+ const outboundTs = latestOutbound.get(agent)?.get(key) ?? -Infinity;
68
+ // Also check if agent sent a broadcast after the inbound message
69
+ const broadcastTs = latestOutbound.get(agent)?.get('__broadcast__') ?? -Infinity;
70
+ const latestReply = Math.max(outboundTs, broadcastTs);
71
+ if (inboundTs > latestReply) {
72
+ needsAttention.add(agent);
73
+ }
74
+ });
75
+ });
76
+ return needsAttention;
77
+ }
78
+ //# sourceMappingURL=needs-attention.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"needs-attention.js","sourceRoot":"","sources":["../../src/dashboard/needs-attention.ts"],"names":[],"mappings":"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"}