agentgate 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -135,6 +135,108 @@ agentgate can notify your agent when queue items are completed, failed, or rejec
135
135
 
136
136
  Compatible with OpenClaw's `/hooks/wake` endpoint. See [OpenClaw webhook docs](https://docs.openclaw.ai/automation/webhook).
137
137
 
138
+ ## Service Access Control
139
+
140
+ Control which agents can access specific services. Each service/account can be configured with one of three access modes:
141
+
142
+ - **All** (default) - All agents can access
143
+ - **Allowlist** - Only listed agents can access
144
+ - **Denylist** - All agents except listed ones can access
145
+
146
+ **Setup in Admin UI:**
147
+ 1. Go to **Services** in the admin navigation
148
+ 2. Click on a service to configure access
149
+ 3. Set access mode and add/remove agents from the list
150
+
151
+ **API Endpoint:**
152
+ ```bash
153
+ # Check your access to services
154
+ GET /api/services
155
+ Authorization: Bearer rms_your_key
156
+
157
+ # Response shows only services you can access
158
+ { "services": [{ "service": "github", "account_name": "monteslu", "access_mode": "all" }, ...] }
159
+ ```
160
+
161
+ When an agent lacks access to a service, API calls return `403 Forbidden` with a clear error message.
162
+
163
+ ## Auth Bypass (Trusted Agents)
164
+
165
+ For highly trusted agents, you can bypass the write queue entirely. Agents with `bypass_auth` enabled execute write operations immediately without human approval.
166
+
167
+ > ⚠️ **Use with extreme caution.** Only enable for agents you completely trust with unsupervised write access.
168
+
169
+ **Setup in Admin UI:**
170
+ 1. Go to **API Keys**
171
+ 2. Click **Configure** next to the agent
172
+ 3. Enable **Auth Bypass**
173
+
174
+ **Behavior:**
175
+ - All write operations (POST/PUT/DELETE) execute immediately
176
+ - No queue entries are created
177
+ - The agent is effectively operating unsupervised
178
+ - Reads work the same as before
179
+
180
+ This is useful for automation agents that need to perform routine operations without human bottlenecks.
181
+
182
+ ## GitHub Webhooks
183
+
184
+ agentgate can receive GitHub webhook events and forward them to agent webhooks, enabling reactive workflows.
185
+
186
+ **Setup:**
187
+ 1. In GitHub repo settings, add a webhook:
188
+ - URL: `https://your-agentgate.com/webhooks/github`
189
+ - Content type: `application/json`
190
+ - Secret: (configure in agentgate settings)
191
+ - Events: Choose which events to receive
192
+
193
+ 2. Configure webhook forwarding in agentgate Admin UI under **Settings**
194
+
195
+ **Supported Events:**
196
+ - Push events
197
+ - Pull request events (opened, closed, merged, review requested)
198
+ - Issue events
199
+ - Workflow run events
200
+
201
+ Agents receive webhooks in the same format as other notifications, enabling them to react to repository changes in real-time.
202
+
203
+ ## Agent Avatars
204
+
205
+ Agents can have custom avatars displayed in the admin UI for easy identification.
206
+
207
+ **Setup in Admin UI:**
208
+ 1. Go to **API Keys**
209
+ 2. Click the avatar placeholder next to an agent name
210
+ 3. Upload an image (PNG, JPG, GIF, WebP supported)
211
+
212
+ Avatars are stored locally and displayed throughout the UI wherever the agent is referenced.
213
+
214
+ ## Memento (Agent Memory)
215
+
216
+ Durable memory storage for agents. Store and retrieve memory snapshots tagged with keywords for long-term context preservation.
217
+
218
+ 📖 **[See full documentation](docs/memento.md)** (coming soon)
219
+
220
+ **Key Features:**
221
+ - **Append-only** - Mementos are immutable once stored
222
+ - **Keyword tagging** - Each memento has 1-10 keywords (stemmed for matching)
223
+ - **Two-step retrieval** - Search returns metadata, fetch returns full content
224
+
225
+ **Quick API Overview:**
226
+ ```bash
227
+ # Store a memento
228
+ POST /api/agents/memento
229
+ { "content": "Your memory...", "keywords": ["project", "decision"] }
230
+
231
+ # Search by keyword
232
+ GET /api/agents/memento/search?keywords=project&limit=10
233
+
234
+ # Fetch full content
235
+ GET /api/agents/memento/42,38,15
236
+ ```
237
+
238
+ Each agent sees only their own mementos. Maximum 12KB per memento.
239
+
138
240
  ## Inter-Agent Messaging
139
241
 
140
242
  Agents can communicate with each other through agentgate, with optional human oversight.
@@ -146,14 +248,30 @@ Agents can communicate with each other through agentgate, with optional human ov
146
248
  - Configure agent webhooks in Admin UI under **API Keys > Configure**
147
249
  - Endpoints: `/api/agents/messageable`, `/api/agents/message`, `/api/agents/messages`, `/api/agents/status`
148
250
 
251
+ ### Broadcasts
252
+
253
+ Send messages to all agents at once:
254
+
255
+ ```bash
256
+ POST /api/agents/broadcast
257
+ { "message": "Team announcement: deployment starting" }
258
+
259
+ # Response
260
+ { "delivered": ["Agent1", "Agent2"], "failed": [], "total": 2 }
261
+ ```
262
+
263
+ Broadcasts are stored in the database and appear in each agent's message history. View broadcast history in the Admin UI under **Messages → Broadcast**.
264
+
149
265
  ## Agent Registry
150
266
 
151
267
  Manage your agents in the admin UI at `/ui/keys`. Each agent has:
152
268
 
153
269
  - **Name** - Unique identifier (case-insensitive)
154
270
  - **API Key** - Bearer token for agent → agentgate authentication (shown once at creation)
271
+ - **Avatar** - Optional image for visual identification
155
272
  - **Webhook URL** (optional) - Endpoint for agentgate → agent notifications
156
273
  - **Webhook Token** (optional) - Bearer token for webhook authentication
274
+ - **Auth Bypass** (optional) - Skip write queue for trusted agents
157
275
 
158
276
  When an agent's webhook is configured, agentgate will POST notifications for:
159
277
  - Queue item status changes (approved/rejected/completed/failed)
@@ -291,12 +409,8 @@ BASE_URL=https://agentgate.yourdomain.com npm start
291
409
 
292
410
  ## TODO
293
411
 
294
- - [ ] Per-agent service access control - different agents can access different services/accounts
295
412
  - [ ] Fine-grained endpoint control per service - whitelist/blacklist individual endpoints (even for read operations)
296
413
 
297
414
  ## License
298
415
 
299
416
  ISC
300
-
301
-
302
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgate",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "API gateway for AI agents with human-in-the-loop write approval",
6
6
  "main": "src/index.js",
@@ -52,7 +52,8 @@
52
52
  "express": "^4.18.2",
53
53
  "hsync": "^0.33.0",
54
54
  "nanoid": "^5.0.0",
55
- "socket.io": "^4.8.3"
55
+ "socket.io": "^4.8.3",
56
+ "stemmer": "^2.0.1"
56
57
  },
57
58
  "devDependencies": {
58
59
  "eslint": "^9.0.0",
@@ -0,0 +1,178 @@
1
+ /* Mobile Responsive Styles for agentgate */
2
+ /* Issue #39 */
3
+
4
+ /* Tablet breakpoint */
5
+ @media (max-width: 768px) {
6
+ body {
7
+ padding: 20px;
8
+ }
9
+
10
+ h1 {
11
+ font-size: 1.75rem;
12
+ }
13
+
14
+ .card {
15
+ padding: 20px;
16
+ margin: 16px 0;
17
+ }
18
+
19
+ /* Nav buttons wrap */
20
+ [style*="display: flex"][style*="gap: 12px"] {
21
+ flex-wrap: wrap;
22
+ }
23
+
24
+ .nav-btn {
25
+ padding: 12px 16px;
26
+ font-size: 14px;
27
+ }
28
+
29
+ /* Tables scroll horizontally */
30
+ table {
31
+ display: block;
32
+ overflow-x: auto;
33
+ white-space: nowrap;
34
+ }
35
+
36
+ /* Full width inputs */
37
+ input[type="text"],
38
+ input[type="password"],
39
+ input[type="url"],
40
+ textarea {
41
+ width: 100% !important;
42
+ min-width: 0;
43
+ }
44
+
45
+ /* Stack filter bar */
46
+ .filter-bar {
47
+ flex-direction: column;
48
+ align-items: stretch;
49
+ gap: 12px;
50
+ }
51
+
52
+ .filter-bar .clear-section {
53
+ margin-left: 0;
54
+ margin-top: 8px;
55
+ }
56
+ }
57
+
58
+ /* Mobile breakpoint */
59
+ @media (max-width: 480px) {
60
+ body {
61
+ padding: 12px;
62
+ }
63
+
64
+ h1 {
65
+ font-size: 1.4rem;
66
+ }
67
+
68
+ h2 {
69
+ font-size: 1.1rem;
70
+ }
71
+
72
+ .card {
73
+ padding: 16px;
74
+ margin: 12px 0;
75
+ border-radius: 12px;
76
+ }
77
+
78
+ /* Header stacks vertically */
79
+ [style*="display: flex"][style*="justify-content: space-between"] {
80
+ flex-direction: column;
81
+ gap: 16px;
82
+ align-items: stretch;
83
+ }
84
+
85
+ /* Nav buttons full width */
86
+ .nav-btn {
87
+ width: 100%;
88
+ justify-content: center;
89
+ }
90
+
91
+ /* Touch-friendly buttons */
92
+ button,
93
+ .btn-primary,
94
+ .btn-secondary,
95
+ .btn-danger,
96
+ input[type="submit"] {
97
+ min-height: 44px;
98
+ padding: 12px 20px;
99
+ }
100
+
101
+ .btn-sm {
102
+ min-height: 40px;
103
+ padding: 10px 16px;
104
+ }
105
+
106
+ /* Form actions stack */
107
+ .queue-actions,
108
+ .message-actions {
109
+ flex-direction: column;
110
+ gap: 12px;
111
+ }
112
+
113
+ .queue-actions input,
114
+ .message-actions input {
115
+ width: 100% !important;
116
+ }
117
+
118
+ .queue-actions button,
119
+ .message-actions button {
120
+ width: 100%;
121
+ }
122
+
123
+ /* Copyable text wraps */
124
+ .copyable {
125
+ flex-wrap: wrap;
126
+ word-break: break-all;
127
+ }
128
+
129
+ pre {
130
+ padding: 16px;
131
+ font-size: 12px;
132
+ }
133
+
134
+ code {
135
+ font-size: 12px;
136
+ word-break: break-all;
137
+ }
138
+
139
+ /* Hide nav divider on mobile */
140
+ .nav-divider {
141
+ display: none;
142
+ }
143
+
144
+ /* Badge position adjustment */
145
+ .badge {
146
+ top: -6px;
147
+ right: -6px;
148
+ width: 20px;
149
+ height: 20px;
150
+ font-size: 10px;
151
+ }
152
+
153
+ /* Account items stack */
154
+ .account-item {
155
+ flex-direction: column;
156
+ align-items: flex-start;
157
+ gap: 8px;
158
+ }
159
+
160
+ /* Login card */
161
+ .login-card {
162
+ margin: 40px auto;
163
+ }
164
+ }
165
+
166
+ /* Prevent horizontal scroll */
167
+ html, body {
168
+ overflow-x: hidden;
169
+ }
170
+
171
+ /* Safe area padding for notched devices */
172
+ @media (max-width: 480px) {
173
+ body {
174
+ padding-left: env(safe-area-inset-left, 12px);
175
+ padding-right: env(safe-area-inset-right, 12px);
176
+ padding-bottom: env(safe-area-inset-bottom, 12px);
177
+ }
178
+ }
package/public/style.css CHANGED
@@ -258,6 +258,7 @@ button, input[type="submit"] {
258
258
  display: inline-flex;
259
259
  align-items: center;
260
260
  gap: 8px;
261
+ white-space: nowrap;
261
262
  }
262
263
 
263
264
  .nav-btn-default {
@@ -285,6 +286,19 @@ button, input[type="submit"] {
285
286
  transform: translateY(-2px);
286
287
  }
287
288
 
289
+ .nav-btn-danger {
290
+ background: rgba(239, 68, 68, 0.1);
291
+ color: #f87171;
292
+ border: 1px solid rgba(239, 68, 68, 0.3);
293
+ }
294
+
295
+ .nav-btn-danger:hover {
296
+ background: rgba(239, 68, 68, 0.2);
297
+ color: #fca5a5;
298
+ border-color: rgba(239, 68, 68, 0.5);
299
+ transform: translateY(-2px);
300
+ }
301
+
288
302
  .account-item {
289
303
  display: flex;
290
304
  justify-content: space-between;
@@ -557,6 +571,40 @@ body > p:first-of-type {
557
571
  margin: 0 8px;
558
572
  }
559
573
 
574
+ /* Status badges for account auth state */
575
+ .badge-success {
576
+ display: inline-block;
577
+ padding: 2px 8px;
578
+ border-radius: 4px;
579
+ font-size: 12px;
580
+ font-weight: 500;
581
+ background: rgba(16, 185, 129, 0.15);
582
+ color: #34d399;
583
+ border: 1px solid rgba(16, 185, 129, 0.3);
584
+ }
585
+
586
+ .badge-error {
587
+ display: inline-block;
588
+ padding: 2px 8px;
589
+ border-radius: 4px;
590
+ font-size: 12px;
591
+ font-weight: 500;
592
+ background: rgba(239, 68, 68, 0.15);
593
+ color: #f87171;
594
+ border: 1px solid rgba(239, 68, 68, 0.3);
595
+ }
596
+
597
+ .badge-warning {
598
+ display: inline-block;
599
+ padding: 2px 8px;
600
+ border-radius: 4px;
601
+ font-size: 12px;
602
+ font-weight: 500;
603
+ background: rgba(245, 158, 11, 0.15);
604
+ color: #fbbf24;
605
+ border: 1px solid rgba(245, 158, 11, 0.3);
606
+ }
607
+
560
608
  /* Scrollbar styling */
561
609
  ::-webkit-scrollbar {
562
610
  width: 8px;
@@ -591,3 +639,84 @@ body > p:first-of-type {
591
639
  border-radius: 8px;
592
640
  border: 1px solid rgba(16, 185, 129, 0.3);
593
641
  }
642
+
643
+ /* Avatar component */
644
+ .avatar {
645
+ display: inline-flex;
646
+ align-items: center;
647
+ justify-content: center;
648
+ border-radius: 50%;
649
+ overflow: hidden;
650
+ flex-shrink: 0;
651
+ position: relative;
652
+ }
653
+
654
+ .avatar img {
655
+ width: 100%;
656
+ height: 100%;
657
+ object-fit: cover;
658
+ }
659
+
660
+ .avatar-initials {
661
+ color: white;
662
+ font-weight: 600;
663
+ font-size: 0.875em;
664
+ text-transform: uppercase;
665
+ display: flex;
666
+ align-items: center;
667
+ justify-content: center;
668
+ width: 100%;
669
+ height: 100%;
670
+ }
671
+
672
+ .avatar-sm {
673
+ width: 24px;
674
+ height: 24px;
675
+ }
676
+
677
+ .avatar-md {
678
+ width: 32px;
679
+ height: 32px;
680
+ }
681
+
682
+ .avatar-lg {
683
+ width: 48px;
684
+ height: 48px;
685
+ }
686
+
687
+ /* Agent name with avatar */
688
+ .agent-with-avatar {
689
+ display: inline-flex;
690
+ align-items: center;
691
+ gap: 8px;
692
+ }
693
+
694
+ /* Small and extra-small buttons */
695
+ .btn-xs {
696
+ padding: 2px 8px;
697
+ font-size: 11px;
698
+ border-radius: 4px;
699
+ background: rgba(99, 102, 241, 0.15);
700
+ color: #818cf8;
701
+ border: 1px solid rgba(99, 102, 241, 0.3);
702
+ cursor: pointer;
703
+ transition: all 0.2s;
704
+ }
705
+
706
+ .btn-xs:hover {
707
+ background: rgba(99, 102, 241, 0.25);
708
+ }
709
+
710
+ .btn-danger {
711
+ background: rgba(239, 68, 68, 0.15);
712
+ color: #f87171;
713
+ border: 1px solid rgba(239, 68, 68, 0.3);
714
+ padding: 8px 16px;
715
+ border-radius: 6px;
716
+ cursor: pointer;
717
+ transition: all 0.2s;
718
+ }
719
+
720
+ .btn-danger:hover {
721
+ background: rgba(239, 68, 68, 0.25);
722
+ }