agentgate 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -2
- package/package.json +1 -1
- package/src/index.js +64 -2
- package/src/lib/agentNotifier.js +150 -0
- package/src/lib/db.js +185 -8
- package/src/lib/queueExecutor.js +4 -0
- package/src/routes/agents.js +185 -0
- package/src/routes/ui.js +667 -15
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import {
|
|
3
|
+
getMessagingMode,
|
|
4
|
+
createAgentMessage,
|
|
5
|
+
getAgentMessage,
|
|
6
|
+
getMessagesForAgent,
|
|
7
|
+
markMessageRead,
|
|
8
|
+
listApiKeys,
|
|
9
|
+
getApiKeyByName
|
|
10
|
+
} from '../lib/db.js';
|
|
11
|
+
import { notifyAgentMessage } from '../lib/agentNotifier.js';
|
|
12
|
+
|
|
13
|
+
const router = Router();
|
|
14
|
+
|
|
15
|
+
const MAX_MESSAGE_LENGTH = 10 * 1024; // 10KB limit
|
|
16
|
+
|
|
17
|
+
// POST /api/agents/message - Send a message to another agent
|
|
18
|
+
router.post('/message', async (req, res) => {
|
|
19
|
+
const { to, message } = req.body;
|
|
20
|
+
const fromAgent = req.apiKeyName; // Set by apiKeyAuth middleware
|
|
21
|
+
|
|
22
|
+
if (!to) {
|
|
23
|
+
return res.status(400).json({ error: 'Missing "to" field (recipient agent name)' });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!message) {
|
|
27
|
+
return res.status(400).json({ error: 'Missing "message" field' });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Check message length
|
|
31
|
+
if (message.length > MAX_MESSAGE_LENGTH) {
|
|
32
|
+
return res.status(400).json({
|
|
33
|
+
error: `Message too long. Maximum ${MAX_MESSAGE_LENGTH} bytes allowed.`,
|
|
34
|
+
length: message.length,
|
|
35
|
+
max: MAX_MESSAGE_LENGTH
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const mode = getMessagingMode();
|
|
40
|
+
|
|
41
|
+
if (mode === 'off') {
|
|
42
|
+
return res.status(403).json({
|
|
43
|
+
error: 'Agent messaging is disabled',
|
|
44
|
+
hint: 'Admin can enable messaging in the agentgate UI'
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Validate recipient exists (case-insensitive lookup)
|
|
49
|
+
const recipient = getApiKeyByName(to);
|
|
50
|
+
if (!recipient) {
|
|
51
|
+
return res.status(404).json({ error: `Agent "${to}" not found` });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Use canonical name from database
|
|
55
|
+
const recipientName = recipient.name;
|
|
56
|
+
|
|
57
|
+
// Can't message yourself (case-insensitive)
|
|
58
|
+
if (recipientName.toLowerCase() === fromAgent.toLowerCase()) {
|
|
59
|
+
return res.status(400).json({ error: 'Cannot send message to yourself' });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// Use canonical recipient name from database
|
|
64
|
+
const result = createAgentMessage(fromAgent, recipientName, message);
|
|
65
|
+
|
|
66
|
+
if (mode === 'supervised') {
|
|
67
|
+
return res.json({
|
|
68
|
+
id: result.id,
|
|
69
|
+
status: 'pending',
|
|
70
|
+
message: 'Message queued for human approval'
|
|
71
|
+
});
|
|
72
|
+
} else {
|
|
73
|
+
// open mode - notify recipient immediately
|
|
74
|
+
const fullMessage = getAgentMessage(result.id);
|
|
75
|
+
notifyAgentMessage(fullMessage);
|
|
76
|
+
|
|
77
|
+
return res.json({
|
|
78
|
+
id: result.id,
|
|
79
|
+
status: 'delivered',
|
|
80
|
+
message: 'Message delivered'
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
} catch (err) {
|
|
84
|
+
return res.status(500).json({ error: err.message });
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// GET /api/agents/messages - Get messages for the current agent
|
|
89
|
+
router.get('/messages', async (req, res) => {
|
|
90
|
+
const agentName = req.apiKeyName;
|
|
91
|
+
const unreadOnly = req.query.unread === 'true';
|
|
92
|
+
|
|
93
|
+
const mode = getMessagingMode();
|
|
94
|
+
|
|
95
|
+
if (mode === 'off') {
|
|
96
|
+
return res.status(403).json({
|
|
97
|
+
error: 'Agent messaging is disabled',
|
|
98
|
+
hint: 'Admin can enable messaging in the agentgate UI'
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const messages = getMessagesForAgent(agentName, unreadOnly);
|
|
103
|
+
|
|
104
|
+
return res.json({
|
|
105
|
+
mode,
|
|
106
|
+
messages: messages.map(m => ({
|
|
107
|
+
id: m.id,
|
|
108
|
+
from: m.from_agent,
|
|
109
|
+
message: m.message,
|
|
110
|
+
created_at: m.created_at,
|
|
111
|
+
read: m.read_at !== null
|
|
112
|
+
}))
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// POST /api/agents/messages/:id/read - Mark a message as read
|
|
117
|
+
router.post('/messages/:id/read', async (req, res) => {
|
|
118
|
+
const { id } = req.params;
|
|
119
|
+
const agentName = req.apiKeyName;
|
|
120
|
+
|
|
121
|
+
const mode = getMessagingMode();
|
|
122
|
+
|
|
123
|
+
if (mode === 'off') {
|
|
124
|
+
return res.status(403).json({ error: 'Agent messaging is disabled' });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const result = markMessageRead(id, agentName);
|
|
128
|
+
|
|
129
|
+
if (result.changes === 0) {
|
|
130
|
+
return res.status(404).json({ error: 'Message not found or already read' });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return res.json({ success: true });
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// GET /api/agents/status - Get messaging status and mode
|
|
137
|
+
router.get('/status', async (req, res) => {
|
|
138
|
+
const mode = getMessagingMode();
|
|
139
|
+
const agentName = req.apiKeyName;
|
|
140
|
+
|
|
141
|
+
if (mode === 'off') {
|
|
142
|
+
return res.json({
|
|
143
|
+
mode: 'off',
|
|
144
|
+
enabled: false,
|
|
145
|
+
message: 'Agent messaging is disabled'
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const messages = getMessagesForAgent(agentName, true);
|
|
150
|
+
|
|
151
|
+
return res.json({
|
|
152
|
+
mode,
|
|
153
|
+
enabled: true,
|
|
154
|
+
unread_count: messages.length
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// GET /api/agents/messageable - Discover which agents can be messaged
|
|
159
|
+
router.get('/messageable', async (req, res) => {
|
|
160
|
+
const mode = getMessagingMode();
|
|
161
|
+
const callerName = req.apiKeyName;
|
|
162
|
+
|
|
163
|
+
if (mode === 'off') {
|
|
164
|
+
return res.status(403).json({
|
|
165
|
+
error: 'Agent messaging is disabled',
|
|
166
|
+
agents: []
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const apiKeys = listApiKeys();
|
|
171
|
+
|
|
172
|
+
// Return all agents except self
|
|
173
|
+
const agents = apiKeys
|
|
174
|
+
.filter(k => k.name.toLowerCase() !== callerName.toLowerCase())
|
|
175
|
+
.map(k => ({
|
|
176
|
+
name: k.name
|
|
177
|
+
}));
|
|
178
|
+
|
|
179
|
+
return res.json({
|
|
180
|
+
mode,
|
|
181
|
+
agents
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
export default router;
|