frdel commited on
Commit
5cb4b4d
·
1 Parent(s): fd50946

connectivity and other docs

Browse files
README.md CHANGED
@@ -12,6 +12,7 @@
12
  [Installation](./docs/installation.md) •
13
  [Development](./docs/development.md) •
14
  [Extensibility](./docs/extensibility.md) •
 
15
  [How to update](./docs/installation.md#how-to-update-agent-zero) •
16
  [Documentation](./docs/README.md) •
17
  [Usage](./docs/usage.md)
@@ -158,6 +159,9 @@ docker run -p 50001:80 agent0ai/agent-zero
158
  |-------|-------------|
159
  | [Installation](./docs/installation.md) | Installation, setup and configuration |
160
  | [Usage](./docs/usage.md) | Basic and advanced usage |
 
 
 
161
  | [Architecture](./docs/architecture.md) | System design and components |
162
  | [Contributing](./docs/contribution.md) | How to contribute |
163
  | [Troubleshooting](./docs/troubleshooting.md) | Common issues and their solutions |
@@ -165,6 +169,34 @@ docker run -p 50001:80 agent0ai/agent-zero
165
 
166
  ## 🎯 Changelog
167
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
 
169
  ### v0.9.2 - Kokoro TTS, Attachments
170
  [Release video](https://www.youtube.com/watch?v=sPot_CAX62I)
@@ -174,7 +206,6 @@ docker run -p 50001:80 agent0ai/agent-zero
174
  - Minor updates: log truncation, hyperlink targets, component examples, api cleanup
175
 
176
 
177
-
178
  ### v0.9.1 - LiteLLM, UI improvements
179
  [Release video](https://youtu.be/crwr0M4Spcg)
180
  - Langchain replaced with LiteLLM
 
12
  [Installation](./docs/installation.md) •
13
  [Development](./docs/development.md) •
14
  [Extensibility](./docs/extensibility.md) •
15
+ [Connectivity](./docs/connectivity.md) •
16
  [How to update](./docs/installation.md#how-to-update-agent-zero) •
17
  [Documentation](./docs/README.md) •
18
  [Usage](./docs/usage.md)
 
159
  |-------|-------------|
160
  | [Installation](./docs/installation.md) | Installation, setup and configuration |
161
  | [Usage](./docs/usage.md) | Basic and advanced usage |
162
+ | [Development](./docs/development.md) | Development and customization |
163
+ | [Extensibility](./docs/extensibility.md) | Extending Agent Zero |
164
+ | [Connectivity](./docs/connectivity.md) | External API endpoints, MCP server connections, A2A protocol |
165
  | [Architecture](./docs/architecture.md) | System design and components |
166
  | [Contributing](./docs/contribution.md) | How to contribute |
167
  | [Troubleshooting](./docs/troubleshooting.md) | Common issues and their solutions |
 
169
 
170
  ## 🎯 Changelog
171
 
172
+ ### v0.9.4 - Connectivity, UI
173
+ - External API endpoints
174
+ - Streamable HTTP MCP A0 server
175
+ - A2A (Agent to Agent) protocol - server+client
176
+ - New notifications system
177
+ - New local terminal interface for stability
178
+ - Rate limiter integration to models
179
+ - Smarter autoscrolling in UI
180
+ - Action buttons in messages
181
+ - Multiple API keys support
182
+ - Download streaming
183
+ - Tunnel URL QR code
184
+ - Internal fixes and optimizations
185
+
186
+ ### v0.9.3 - Subordinates, memory, providers Latest
187
+ [Release video](https://www.youtube.com/watch?v=-LfejFWL34k)
188
+ - Faster startup/restart
189
+ - Subordinate agents can have dedicated prompts, tools and system extensions
190
+ - Streamable HTTP MCP server support
191
+ - Memory loading enhanced by AI filter
192
+ - Memory AI consolidation when saving memories
193
+ - Auto memory system configuration in settings
194
+ - LLM providers available are set by providers.yaml configuration file
195
+ - Venice.ai LLM provider supported
196
+ - Initial agent message for user + as example for LLM
197
+ - Docker build support for local images
198
+ - File browser fix
199
+
200
 
201
  ### v0.9.2 - Kokoro TTS, Attachments
202
  [Release video](https://www.youtube.com/watch?v=sPot_CAX62I)
 
206
  - Minor updates: log truncation, hyperlink targets, component examples, api cleanup
207
 
208
 
 
209
  ### v0.9.1 - LiteLLM, UI improvements
210
  [Release video](https://youtu.be/crwr0M4Spcg)
211
  - Langchain replaced with LiteLLM
docs/README.md CHANGED
@@ -6,6 +6,7 @@ To begin with Agent Zero, follow the links below for detailed guides on various
6
  - **[Usage Guide](usage.md):** Explore GUI features and usage scenarios.
7
  - **[Development](development.md):** Set up a development environment for Agent Zero.
8
  - **[Extensibility](extensibility.md):** Learn how to create custom extensions for Agent Zero.
 
9
  - **[Architecture Overview](architecture.md):** Understand the internal workings of the framework.
10
  - **[Contributing](contribution.md):** Learn how to contribute to the Agent Zero project.
11
  - **[Troubleshooting and FAQ](troubleshooting.md):** Find answers to common issues and questions.
 
6
  - **[Usage Guide](usage.md):** Explore GUI features and usage scenarios.
7
  - **[Development](development.md):** Set up a development environment for Agent Zero.
8
  - **[Extensibility](extensibility.md):** Learn how to create custom extensions for Agent Zero.
9
+ - **[Connectivity](connectivity.md):** Learn how to connect to Agent Zero from other applications.
10
  - **[Architecture Overview](architecture.md):** Understand the internal workings of the framework.
11
  - **[Contributing](contribution.md):** Learn how to contribute to the Agent Zero project.
12
  - **[Troubleshooting and FAQ](troubleshooting.md):** Find answers to common issues and questions.
docs/connectivity.md ADDED
@@ -0,0 +1,585 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Agent Zero Connectivity Guide
2
+
3
+ This guide covers the different ways to connect to Agent Zero from external applications, including using the External API, connecting as an MCP client, and enabling agent-to-agent communication.
4
+
5
+ **Note:** You can find your specific URLs and API tokens in your Agent Zero instance under `Settings > External Services`.
6
+
7
+ ### API Token Information
8
+
9
+ The API token is automatically generated from your username and password. This same token is used for External API endpoints, MCP server connections, and A2A communication. The token will change if you update your credentials.
10
+
11
+ ---
12
+
13
+ ## External API Endpoints
14
+
15
+ Agent Zero provides external API endpoints for integration with other applications. These endpoints use API key authentication and support text messages and file attachments.
16
+
17
+ ### `POST /api_message`
18
+
19
+ Send messages to Agent Zero and receive responses. Supports text messages, file attachments, and conversation continuity.
20
+
21
+ ### API Reference
22
+
23
+ **Parameters:**
24
+ * `context_id` (string, optional): Existing chat context ID
25
+ * `message` (string, required): The message to send
26
+ * `attachments` (array, optional): Array of `{filename, base64}` objects
27
+ * `lifetime_hours` (number, optional): Chat lifetime in hours (default: 24)
28
+
29
+ **Headers:**
30
+ * `X-API-KEY` (required)
31
+ * `Content-Type: application/json`
32
+
33
+ ### JavaScript Examples
34
+
35
+ #### Basic Usage Example
36
+
37
+ ```javascript
38
+ // Basic message example
39
+ async function sendMessage() {
40
+ try {
41
+ const response = await fetch('YOUR_AGENT_ZERO_URL/api_message', {
42
+ method: 'POST',
43
+ headers: {
44
+ 'Content-Type': 'application/json',
45
+ 'X-API-KEY': 'YOUR_API_KEY'
46
+ },
47
+ body: JSON.stringify({
48
+ message: "Hello, how can you help me?",
49
+ lifetime_hours: 24
50
+ })
51
+ });
52
+
53
+ const data = await response.json();
54
+
55
+ if (response.ok) {
56
+ console.log('✅ Success!');
57
+ console.log('Response:', data.response);
58
+ console.log('Context ID:', data.context_id);
59
+ return data;
60
+ } else {
61
+ console.error('❌ Error:', data.error);
62
+ return null;
63
+ }
64
+ } catch (error) {
65
+ console.error('❌ Request failed:', error);
66
+ return null;
67
+ }
68
+ }
69
+
70
+ // Call the function
71
+ sendMessage().then(result => {
72
+ if (result) {
73
+ console.log('Message sent successfully!');
74
+ }
75
+ });
76
+ ```
77
+
78
+ #### Conversation Continuation Example
79
+
80
+ ```javascript
81
+ // Continue conversation example
82
+ async function continueConversation(contextId) {
83
+ try {
84
+ const response = await fetch('YOUR_AGENT_ZERO_URL/api_message', {
85
+ method: 'POST',
86
+ headers: {
87
+ 'Content-Type': 'application/json',
88
+ 'X-API-KEY': 'YOUR_API_KEY'
89
+ },
90
+ body: JSON.stringify({
91
+ context_id: contextId,
92
+ message: "Can you tell me more about that?",
93
+ lifetime_hours: 24
94
+ })
95
+ });
96
+
97
+ const data = await response.json();
98
+
99
+ if (response.ok) {
100
+ console.log('✅ Continuation Success!');
101
+ console.log('Response:', data.response);
102
+ return data;
103
+ } else {
104
+ console.error('❌ Error:', data.error);
105
+ return null;
106
+ }
107
+ } catch (error) {
108
+ console.error('❌ Request failed:', error);
109
+ return null;
110
+ }
111
+ }
112
+
113
+ // Example: First send a message, then continue the conversation
114
+ async function fullConversationExample() {
115
+ const firstResult = await sendMessage();
116
+ if (firstResult && firstResult.context_id) {
117
+ await continueConversation(firstResult.context_id);
118
+ }
119
+ }
120
+
121
+ fullConversationExample();
122
+ ```
123
+
124
+ #### File Attachment Example
125
+
126
+ ```javascript
127
+ // File attachment example
128
+ async function sendWithAttachment() {
129
+ try {
130
+ // Example with text content (convert to base64)
131
+ const textContent = "Hello World from attachment!";
132
+ const base64Content = btoa(textContent);
133
+
134
+ const response = await fetch('YOUR_AGENT_ZERO_URL/api_message', {
135
+ method: 'POST',
136
+ headers: {
137
+ 'Content-Type': 'application/json',
138
+ 'X-API-KEY': 'YOUR_API_KEY'
139
+ },
140
+ body: JSON.stringify({
141
+ message: "Please analyze this file:",
142
+ attachments: [
143
+ {
144
+ filename: "document.txt",
145
+ base64: base64Content
146
+ }
147
+ ],
148
+ lifetime_hours: 12
149
+ })
150
+ });
151
+
152
+ const data = await response.json();
153
+
154
+ if (response.ok) {
155
+ console.log('✅ File sent successfully!');
156
+ console.log('Response:', data.response);
157
+ return data;
158
+ } else {
159
+ console.error('❌ Error:', data.error);
160
+ return null;
161
+ }
162
+ } catch (error) {
163
+ console.error('❌ Request failed:', error);
164
+ return null;
165
+ }
166
+ }
167
+
168
+ // Call the function
169
+ sendWithAttachment();
170
+ ```
171
+
172
+ ---
173
+
174
+ ## `GET/POST /api_log_get`
175
+
176
+ Retrieve log data by context ID, limited to a specified number of entries from the newest.
177
+
178
+ ### API Reference
179
+
180
+ **Parameters:**
181
+ * `context_id` (string, required): Context ID to get logs from
182
+ * `length` (integer, optional): Number of log items to return from newest (default: 100)
183
+
184
+ **Headers:**
185
+ * `X-API-KEY` (required)
186
+ * `Content-Type: application/json` (for POST)
187
+
188
+ ### JavaScript Examples
189
+
190
+ #### GET Request Example
191
+
192
+ ```javascript
193
+ // Get logs using GET request
194
+ async function getLogsGET(contextId, length = 50) {
195
+ try {
196
+ const params = new URLSearchParams({
197
+ context_id: contextId,
198
+ length: length.toString()
199
+ });
200
+
201
+ const response = await fetch('YOUR_AGENT_ZERO_URL/api_log_get?' + params, {
202
+ method: 'GET',
203
+ headers: {
204
+ 'X-API-KEY': 'YOUR_API_KEY'
205
+ }
206
+ });
207
+
208
+ const data = await response.json();
209
+
210
+ if (response.ok) {
211
+ console.log('✅ Logs retrieved successfully!');
212
+ console.log('Total items:', data.log.total_items);
213
+ console.log('Returned items:', data.log.returned_items);
214
+ console.log('Log items:', data.log.items);
215
+ return data;
216
+ } else {
217
+ console.error('❌ Error:', data.error);
218
+ return null;
219
+ }
220
+ } catch (error) {
221
+ console.error('❌ Request failed:', error);
222
+ return null;
223
+ }
224
+ }
225
+
226
+ // Example usage
227
+ getLogsGET('ctx_abc123', 20);
228
+ ```
229
+
230
+ #### POST Request Example
231
+
232
+ ```javascript
233
+ // Get logs using POST request
234
+ async function getLogsPOST(contextId, length = 50) {
235
+ try {
236
+ const response = await fetch('YOUR_AGENT_ZERO_URL/api_log_get', {
237
+ method: 'POST',
238
+ headers: {
239
+ 'Content-Type': 'application/json',
240
+ 'X-API-KEY': 'YOUR_API_KEY'
241
+ },
242
+ body: JSON.stringify({
243
+ context_id: contextId,
244
+ length: length
245
+ })
246
+ });
247
+
248
+ const data = await response.json();
249
+
250
+ if (response.ok) {
251
+ console.log('✅ Logs retrieved successfully!');
252
+ console.log('Context ID:', data.context_id);
253
+ console.log('Log GUID:', data.log.guid);
254
+ console.log('Total items:', data.log.total_items);
255
+ console.log('Returned items:', data.log.returned_items);
256
+ console.log('Start position:', data.log.start_position);
257
+ console.log('Progress:', data.log.progress);
258
+ console.log('Log items:', data.log.items);
259
+ return data;
260
+ } else {
261
+ console.error('❌ Error:', data.error);
262
+ return null;
263
+ }
264
+ } catch (error) {
265
+ console.error('❌ Request failed:', error);
266
+ return null;
267
+ }
268
+ }
269
+
270
+ // Example usage - get latest 10 log entries
271
+ getLogsPOST('ctx_abc123', 10);
272
+ ```
273
+
274
+ ---
275
+
276
+ ## `POST /api_terminate_chat`
277
+
278
+ Terminate and remove a chat context to free up resources. Similar to the MCP `finish_chat` function.
279
+
280
+ ### API Reference
281
+
282
+ **Parameters:**
283
+ * `context_id` (string, required): Context ID of the chat to terminate
284
+
285
+ **Headers:**
286
+ * `X-API-KEY` (required)
287
+ * `Content-Type: application/json`
288
+
289
+ ### JavaScript Examples
290
+
291
+ #### Basic Termination Examples
292
+
293
+ ```javascript
294
+ // Basic terminate chat function
295
+ async function terminateChat(contextId) {
296
+ try {
297
+ const response = await fetch('YOUR_AGENT_ZERO_URL/api_terminate_chat', {
298
+ method: 'POST',
299
+ headers: {
300
+ 'Content-Type': 'application/json',
301
+ 'X-API-KEY': 'YOUR_API_KEY'
302
+ },
303
+ body: JSON.stringify({
304
+ context_id: contextId
305
+ })
306
+ });
307
+
308
+ const data = await response.json();
309
+
310
+ if (response.ok) {
311
+ console.log('✅ Chat deleted successfully!');
312
+ console.log('Message:', data.message);
313
+ return data;
314
+ } else {
315
+ console.error('❌ Error:', data.error);
316
+ return null;
317
+ }
318
+ } catch (error) {
319
+ console.error('❌ Request failed:', error);
320
+ return null;
321
+ }
322
+ }
323
+
324
+ // Example 1: Terminate a specific chat
325
+ terminateChat('ctx_abc123');
326
+
327
+ // Example 2: Complete workflow - send message, then terminate
328
+ async function simpleWorkflow() {
329
+ // Send a message
330
+ const result = await sendMessage();
331
+
332
+ if (result && result.context_id) {
333
+ console.log('Chat created:', result.context_id);
334
+
335
+ // Do some work with the chat...
336
+ // await continueConversation(result.context_id);
337
+
338
+ // Clean up when done
339
+ await terminateChat(result.context_id);
340
+ console.log('Chat cleaned up');
341
+ }
342
+ }
343
+
344
+ // Run the workflow
345
+ simpleWorkflow();
346
+ ```
347
+
348
+ ---
349
+
350
+ ## `POST /api_reset_chat`
351
+
352
+ Reset a chat context to clear conversation history while keeping the `context_id` alive for continued use.
353
+
354
+ ### API Reference
355
+
356
+ **Parameters:**
357
+ * `context_id` (string, required): Context ID of the chat to reset
358
+
359
+ **Headers:**
360
+ * `X-API-KEY` (required)
361
+ * `Content-Type: application/json`
362
+
363
+ ### JavaScript Examples
364
+
365
+ #### Basic Reset Examples
366
+
367
+ ```javascript
368
+ // Basic reset chat function
369
+ async function resetChat(contextId) {
370
+ try {
371
+ const response = await fetch('YOUR_AGENT_ZERO_URL/api_reset_chat', {
372
+ method: 'POST',
373
+ headers: {
374
+ 'Content-Type': 'application/json',
375
+ 'X-API-KEY': 'YOUR_API_KEY'
376
+ },
377
+ body: JSON.stringify({
378
+ context_id: contextId
379
+ })
380
+ });
381
+
382
+ const data = await response.json();
383
+
384
+ if (response.ok) {
385
+ console.log('✅ Chat reset successfully!');
386
+ console.log('Message:', data.message);
387
+ console.log('Context ID:', data.context_id);
388
+ return data;
389
+ } else {
390
+ console.error('❌ Error:', data.error);
391
+ return null;
392
+ }
393
+ } catch (error) {
394
+ console.error('❌ Request failed:', error);
395
+ return null;
396
+ }
397
+ }
398
+
399
+ // Example 1: Reset a specific chat
400
+ resetChat('ctx_abc123');
401
+
402
+ // Example 2: Reset and continue conversation
403
+ async function resetAndContinue() {
404
+ const contextId = 'ctx_abc123';
405
+
406
+ // Reset the chat to clear history
407
+ const resetResult = await resetChat(contextId);
408
+
409
+ if (resetResult) {
410
+ console.log('Chat reset, starting fresh conversation...');
411
+
412
+ // Continue with same context_id but fresh history
413
+ const response = await fetch('YOUR_AGENT_ZERO_URL/api_message', {
414
+ method: 'POST',
415
+ headers: {
416
+ 'Content-Type': 'application/json',
417
+ 'X-API-KEY': 'YOUR_API_KEY'
418
+ },
419
+ body: JSON.stringify({
420
+ context_id: contextId, // Same context ID
421
+ message: "Hello, this is a fresh start!",
422
+ lifetime_hours: 24
423
+ })
424
+ });
425
+
426
+ const data = await response.json();
427
+ console.log('New conversation started:', data.response);
428
+ }
429
+ }
430
+
431
+ // Run the example
432
+ resetAndContinue();
433
+ ```
434
+
435
+ ---
436
+
437
+ ## `POST /api_files_get`
438
+
439
+ Retrieve file contents by paths, returning files as base64 encoded data. Useful for retrieving uploaded attachments.
440
+
441
+ ### API Reference
442
+
443
+ **Parameters:**
444
+ * `paths` (array, required): Array of file paths to retrieve (e.g., `["/a0/tmp/uploads/file.txt"]`)
445
+
446
+ **Headers:**
447
+ * `X-API-KEY` (required)
448
+ * `Content-Type: application/json`
449
+
450
+ ### JavaScript Examples
451
+
452
+ #### File Retrieval Examples
453
+
454
+ ```javascript
455
+ // Basic file retrieval
456
+ async function getFiles(filePaths) {
457
+ try {
458
+ const response = await fetch('YOUR_AGENT_ZERO_URL/api_files_get', {
459
+ method: 'POST',
460
+ headers: {
461
+ 'Content-Type': 'application/json',
462
+ 'X-API-KEY': 'YOUR_API_KEY'
463
+ },
464
+ body: JSON.stringify({
465
+ paths: filePaths
466
+ })
467
+ });
468
+
469
+ const data = await response.json();
470
+
471
+ if (response.ok) {
472
+ console.log('✅ Files retrieved successfully!');
473
+ console.log('Retrieved files:', Object.keys(data));
474
+
475
+ // Convert base64 back to text for display
476
+ for (const [filename, base64Content] of Object.entries(data)) {
477
+ try {
478
+ const textContent = atob(base64Content);
479
+ console.log(`${filename}: ${textContent.substring(0, 100)}...`);
480
+ } catch (e) {
481
+ console.log(`${filename}: Binary file (${base64Content.length} chars)`);
482
+ }
483
+ }
484
+
485
+ return data;
486
+ } else {
487
+ console.error('❌ Error:', data.error);
488
+ return null;
489
+ }
490
+ } catch (error) {
491
+ console.error('❌ Request failed:', error);
492
+ return null;
493
+ }
494
+ }
495
+
496
+ // Example 1: Get specific files
497
+ const filePaths = [
498
+ "/a0/tmp/uploads/document.txt",
499
+ "/a0/tmp/uploads/data.json"
500
+ ];
501
+ getFiles(filePaths);
502
+
503
+ // Example 2: Complete attachment workflow
504
+ async function attachmentWorkflow() {
505
+ // Step 1: Send message with attachments
506
+ const messageResponse = await fetch('YOUR_AGENT_ZERO_URL/api_message', {
507
+ method: 'POST',
508
+ headers: {
509
+ 'Content-Type': 'application/json',
510
+ 'X-API-KEY': 'YOUR_API_KEY'
511
+ },
512
+ body: JSON.stringify({
513
+ message: "Please analyze this file",
514
+ attachments: [{
515
+ filename: "test.txt",
516
+ base64: btoa("Hello, this is test content!")
517
+ }],
518
+ lifetime_hours: 1
519
+ })
520
+ });
521
+
522
+ if (messageResponse.ok) {
523
+ console.log('Message sent with attachment');
524
+
525
+ // Step 2: Retrieve the uploaded file
526
+ const retrievedFiles = await getFiles(["/a0/tmp/uploads/test.txt"]);
527
+
528
+ if (retrievedFiles && retrievedFiles["test.txt"]) {
529
+ const originalContent = atob(retrievedFiles["test.txt"]);
530
+ console.log('Retrieved content:', originalContent);
531
+ }
532
+ }
533
+ }
534
+
535
+ // Run the complete workflow
536
+ attachmentWorkflow();
537
+ ```
538
+
539
+ ---
540
+
541
+ ## MCP Server Connectivity
542
+
543
+ Agent Zero includes an MCP Server that allows other MCP-compatible clients to connect to it. The server runs on the same URL and port as the Web UI.
544
+
545
+ It provides two endpoint types:
546
+ - **SSE (`/mcp/sse`):** For clients that support Server-Sent Events.
547
+ - **Streamable HTTP (`/mcp/http/`):** For clients that use streamable HTTP requests.
548
+
549
+ ### Example MCP Server Configuration
550
+
551
+ Below is an example of a `mcp.json` configuration file that a client could use to connect to the Agent Zero MCP server.
552
+
553
+ **Note:** You can find your personalized connection URLs under `Settings > MCP Server > MCP Server`.
554
+
555
+ ```json
556
+ {
557
+ "mcpServers":
558
+ {
559
+ "agent-zero": {
560
+ "type": "sse",
561
+ "url": "YOUR_AGENT_ZERO_URL/mcp/t-YOUR_API_TOKEN/sse"
562
+ },
563
+ "agent-zero-http": {
564
+ "type": "streamable-http",
565
+ "url": "YOUR_AGENT_ZERO_URL/mcp/t-YOUR_API_TOKEN/http/"
566
+ }
567
+ }
568
+ }
569
+ ```
570
+
571
+ ---
572
+
573
+ ## A2A (Agent-to-Agent) Connectivity
574
+
575
+ Agent Zero's A2A Server enables communication with other agents using the FastA2A protocol. Other agents can connect to your instance using the connection URL.
576
+
577
+ ### A2A Connection URL
578
+
579
+ To connect another agent to your Agent Zero instance, use the following URL format.
580
+
581
+ **Note:** You can find your specific A2A connection URL under `Settings > External Services > A2A Connection`.
582
+
583
+ ```
584
+ YOUR_AGENT_ZERO_URL/a2a/t-YOUR_API_TOKEN
585
+ ```
docs/development.md CHANGED
@@ -1,6 +1,11 @@
1
  # Development manual for Agent Zero
2
  This guide will show you how to setup a local development environment for Agent Zero in a VS Code compatible IDE, including proper debugger.
3
 
 
 
 
 
 
4
  > [!WARNING]
5
  > This guide is for developers and contributors. It assumes you have a basic understanding of how to use Git/GitHub, Docker, IDEs and Python.
6
 
 
1
  # Development manual for Agent Zero
2
  This guide will show you how to setup a local development environment for Agent Zero in a VS Code compatible IDE, including proper debugger.
3
 
4
+
5
+ [![Tutorial video](./res/devguide_vid.png)](https://www.youtube.com/watch?v=KE39P4qBjDk)
6
+
7
+
8
+
9
  > [!WARNING]
10
  > This guide is for developers and contributors. It assumes you have a basic understanding of how to use Git/GitHub, Docker, IDEs and Python.
11
 
docs/res/devguide_vid.png ADDED

Git LFS Details

  • SHA256: fc4766f5cba65e486acd1726c7f2d48bd6a493b66388f258d9313f3c53736bdb
  • Pointer size: 132 Bytes
  • Size of remote file: 1.16 MB
tests/tty_test.py DELETED
@@ -1,266 +0,0 @@
1
- """
2
- tty_spawn.py – drive any console program through a real TTY
3
- cross-platform (Windows via pywinpty, POSIX via pty).
4
-
5
- API (async):
6
- term = TTYSpawn(cmd, *, cwd=None, env=None, encoding="utf-8")
7
- await term.start() # launch child
8
- chunk = await term.read(timeout=1.0) # None on timeout
9
- await term.send("data") # raw bytes
10
- await term.sendline("text") # adds '\n'
11
- await term.wait() # exit code
12
- term.kill() # abort
13
- """
14
- # ────────────────── NO ORIGINAL LINES REMOVED – ONLY APPENDED CODE ──────────────────
15
-
16
- import asyncio, os, sys, platform, errno
17
-
18
- _IS_WIN = platform.system() == "Windows"
19
- if _IS_WIN:
20
- import winpty # pip install pywinpty
21
- import msvcrt
22
-
23
-
24
- # Make stdin / stdout tolerant to broken UTF-8 so input() never aborts
25
- try:
26
- # Python 3.7+ has reconfigure()
27
- sys.stdin .reconfigure(errors="replace")
28
- sys.stdout.reconfigure(errors="replace")
29
- except AttributeError:
30
- # Older Pythons: wrap them manually
31
- import io
32
- sys.stdin = io.TextIOWrapper(sys.stdin.buffer,
33
- encoding=sys.stdin.encoding or "utf-8",
34
- errors="replace",
35
- line_buffering=True)
36
- sys.stdout = io.TextIOWrapper(sys.stdout.buffer,
37
- encoding=sys.stdout.encoding or "utf-8",
38
- errors="replace",
39
- line_buffering=True)
40
- # ─────────────────────────────────────────────────────────────────────
41
-
42
-
43
- # ──────────────────────────── PUBLIC CLASS ────────────────────────────
44
-
45
- class TTYSpawn:
46
- def __init__(self, cmd, *, cwd=None, env=None,
47
- encoding="utf-8", echo=True): # ← NEW kw-arg `echo`
48
- self.cmd = cmd if isinstance(cmd, str) else " ".join(cmd)
49
- self.cwd = cwd
50
- self.env = env or os.environ.copy()
51
- self.encoding = encoding
52
- self.echo = echo # ← store preference
53
- self._proc = None
54
- self._buf = asyncio.Queue()
55
-
56
- # ── user-facing coroutines ────────────────────────────────────────
57
- async def start(self):
58
- if _IS_WIN:
59
- self._proc = await _spawn_winpty(
60
- self.cmd, self.cwd, self.env, self.echo) # ← pass echo
61
- else:
62
- self._proc = await _spawn_posix_pty(
63
- self.cmd, self.cwd, self.env, self.echo) # ← pass echo
64
- asyncio.create_task(self._pump_stdout())
65
-
66
- async def read(self, timeout=None):
67
- # Return any decoded text the child produced, or None on timeout
68
- try:
69
- return await asyncio.wait_for(self._buf.get(), timeout)
70
- except asyncio.TimeoutError:
71
- return None
72
-
73
- # backward-compat alias:
74
- readline = read
75
-
76
- async def send(self, data: str | bytes):
77
- if isinstance(data, str):
78
- data = data.encode(self.encoding)
79
- self._proc.stdin.write(data)
80
- await self._proc.stdin.drain()
81
-
82
- async def sendline(self, line: str):
83
- await self.send(line + "\n")
84
-
85
- async def wait(self):
86
- return await self._proc.wait()
87
-
88
- def kill(self):
89
- self._proc.kill()
90
-
91
- async def read_until_idle(self, idle_timeout, total_timeout):
92
- # Collect child output using iter_until_idle to avoid duplicate logic
93
- return "".join([chunk async for chunk in self.iter_until_idle(idle_timeout, total_timeout)])
94
-
95
- async def iter_until_idle(self, idle_timeout, total_timeout):
96
- # Yield each chunk as soon as it arrives until idle or total timeout
97
- import time
98
- start = time.monotonic()
99
- while True:
100
- if time.monotonic() - start > total_timeout:
101
- break
102
- chunk = await self.read(timeout=idle_timeout)
103
- if chunk is None:
104
- break
105
- yield chunk
106
-
107
- # ── internal: stream raw output into the queue ────────────────────
108
- async def _pump_stdout(self):
109
- reader = self._proc.stdout
110
- while True:
111
- chunk = await reader.read(4096) # grab whatever is ready
112
- if not chunk:
113
- break
114
- self._buf.put_nowait(chunk.decode(self.encoding, "replace"))
115
-
116
- # ──────────────────────────── POSIX IMPLEMENTATION ────────────────────
117
-
118
- async def _spawn_posix_pty(cmd, cwd, env, echo):
119
- import pty, asyncio, os, termios
120
- master, slave = pty.openpty()
121
-
122
- # ── Disable ECHO on the slave side if requested ──
123
- if not echo:
124
- attrs = termios.tcgetattr(slave)
125
- attrs[3] &= ~termios.ECHO # lflag
126
- termios.tcsetattr(slave, termios.TCSANOW, attrs)
127
-
128
- proc = await asyncio.create_subprocess_shell(
129
- cmd,
130
- stdin=slave,
131
- stdout=slave,
132
- stderr=slave,
133
- cwd=cwd,
134
- env=env,
135
- close_fds=True,
136
- )
137
- os.close(slave)
138
-
139
- loop = asyncio.get_running_loop()
140
- reader = asyncio.StreamReader()
141
-
142
- def _on_data():
143
- try:
144
- data = os.read(master, 1 << 16)
145
- except OSError as e:
146
- if e.errno != errno.EIO: # EIO == EOF on some systems
147
- raise
148
- data = b""
149
- if data:
150
- reader.feed_data(data)
151
- else:
152
- reader.feed_eof()
153
- loop.remove_reader(master)
154
-
155
- loop.add_reader(master, _on_data)
156
-
157
- class _Stdin:
158
- def write(self, d): os.write(master, d)
159
- async def drain(self): await asyncio.sleep(0)
160
-
161
- proc.stdin = _Stdin()
162
- proc.stdout = reader
163
- return proc
164
-
165
- # ──────────────────────────── WINDOWS IMPLEMENTATION ──────────────────
166
-
167
- async def _spawn_winpty(cmd, cwd, env, echo):
168
- # A quick way to silence command echo in cmd.exe is /Q (quiet)
169
- if not echo and cmd.strip().lower().startswith("cmd") and "/q" not in cmd.lower():
170
- cmd = cmd.replace("cmd.exe", "cmd.exe /Q")
171
-
172
- cols, rows = 80, 25
173
- pty = winpty.PTY(cols, rows)
174
- child = pty.spawn(cmd, cwd=cwd or os.getcwd(), env=env)
175
-
176
- master_r_fd = msvcrt.open_osfhandle(child.conout_pipe, os.O_RDONLY)
177
- master_w_fd = msvcrt.open_osfhandle(child.conin_pipe, 0)
178
-
179
- loop = asyncio.get_running_loop()
180
- reader = asyncio.StreamReader()
181
-
182
- def _on_data():
183
- try:
184
- data = os.read(master_r_fd, 1 << 16)
185
- except OSError:
186
- data = b""
187
- if data:
188
- reader.feed_data(data)
189
- else:
190
- reader.feed_eof()
191
- loop.remove_reader(master_r_fd)
192
-
193
- loop.add_reader(master_r_fd, _on_data)
194
-
195
- class _Stdin:
196
- def write(self, d): os.write(master_w_fd, d)
197
- async def drain(self): await asyncio.sleep(0)
198
-
199
- class _Proc(asyncio.subprocess.Process):
200
- def __init__(self):
201
- self.stdin = _Stdin()
202
- self.stdout = reader
203
- self.pid = child.pid
204
- async def wait(self):
205
- while child.isalive():
206
- await asyncio.sleep(0.2)
207
- return 0
208
- def kill(self):
209
- child.kill()
210
-
211
- return _Proc()
212
-
213
- # ───────────────────────── INTERACTIVE DRIVER ─────────────────────────
214
-
215
- async def interactive_shell():
216
- shell_cmd, prompt_hint = (
217
- ("cmd.exe", "$") if _IS_WIN else ("/bin/bash", "$")
218
- )
219
-
220
- # echo=False → suppress the shell’s own echo of commands
221
- term = TTYSpawn(shell_cmd, echo=False)
222
- await term.start()
223
-
224
- timeout = 1.0
225
-
226
- print(f"Connected to {shell_cmd}.")
227
- print("Type commands for the shell.")
228
- print("• t=<seconds> → change idle timeout")
229
- print("• exit → quit helper\n")
230
-
231
- await term.sendline(" ")
232
- print(await term.read_until_idle(timeout, timeout), end="", flush=True)
233
-
234
- while True:
235
- try:
236
- user = input(f"(timeout={timeout}) {prompt_hint} ")
237
- except (EOFError, KeyboardInterrupt):
238
- print("\nLeaving…")
239
- break
240
-
241
- if user.lower() == "/exit":
242
- break
243
- if user.startswith("/t="):
244
- try:
245
- timeout = float(user.split("=", 1)[1])
246
- print(f"[helper] idle timeout set to {timeout}s")
247
- except ValueError:
248
- print("[helper] invalid number")
249
- continue
250
-
251
- idle_timeout = timeout
252
- total_timeout = 10 * idle_timeout
253
- if user == "":
254
- # Just read output, do not send empty line
255
- async for chunk in term.iter_until_idle(idle_timeout, total_timeout):
256
- print(chunk, end="", flush=True)
257
- else:
258
- await term.sendline(user)
259
- async for chunk in term.iter_until_idle(idle_timeout, total_timeout):
260
- print(chunk, end="", flush=True)
261
-
262
- await term.sendline("exit")
263
- await term.wait()
264
-
265
- if __name__ == "__main__":
266
- asyncio.run(interactive_shell())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
webui/index.html CHANGED
@@ -619,7 +619,7 @@
619
  </template>
620
 
621
 
622
- <!-- Password field -->
623
  <template x-if="field.type === 'password'">
624
  <input type="password" :class="field.classes" :value="field.value"
625
  :readonly="field.readonly === true"
 
619
  </template>
620
 
621
 
622
+ <!-- Password field -->
623
  <template x-if="field.type === 'password'">
624
  <input type="password" :class="field.classes" :value="field.value"
625
  :readonly="field.readonly === true"
webui/vendor/ace-min/ext-beautify [conflicted].js ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ define("ace/ext/beautify",["require","exports","module","ace/token_iterator"],function(e,t,n){"use strict";function i(e,t){return e.type.lastIndexOf(t+".xml")>-1}var r=e("../token_iterator").TokenIterator;t.singletonTags=["area","base","br","col","command","embed","hr","html","img","input","keygen","link","meta","param","source","track","wbr"],t.blockTags=["article","aside","blockquote","body","div","dl","fieldset","footer","form","head","header","html","nav","ol","p","script","section","style","table","tbody","tfoot","thead","ul"],t.formatOptions={lineBreaksAfterCommasInCurlyBlock:!0},t.beautify=function(e){var n=new r(e,0,0),s=n.getCurrentToken(),o=e.getTabString(),u=t.singletonTags,a=t.blockTags,f=t.formatOptions||{},l,c=!1,h=!1,p=!1,d="",v="",m="",g=0,y=0,b=0,w=0,E=0,S=0,x=0,T,N=0,C=0,k=[],L=!1,A,O=!1,M=!1,_=!1,D=!1,P={0:0},H=[],B=!1,j=function(){l&&l.value&&l.type!=="string.regexp"&&(l.value=l.value.replace(/^\s*/,""))},F=function(){var e=d.length-1;for(;;){if(e==0)break;if(d[e]!==" ")break;e-=1}d=d.slice(0,e+1)},I=function(){d=d.trimRight(),c=!1};while(s!==null){N=n.getCurrentTokenRow(),k=n.$rowTokens,l=n.stepForward();if(typeof s!="undefined"){v=s.value,E=0,_=m==="style"||e.$modeId==="ace/mode/css",i(s,"tag-open")?(M=!0,l&&(D=a.indexOf(l.value)!==-1),v==="</"&&(D&&!c&&C<1&&C++,_&&(C=1),E=1,D=!1)):i(s,"tag-close")?M=!1:i(s,"comment.start")?D=!0:i(s,"comment.end")&&(D=!1),!M&&!C&&s.type==="paren.rparen"&&s.value.substr(0,1)==="}"&&C++,N!==T&&(C=N,T&&(C-=T));if(C){I();for(;C>0;C--)d+="\n";c=!0,!i(s,"comment")&&!s.type.match(/^(comment|string)$/)&&(v=v.trimLeft())}if(v){s.type==="keyword"&&v.match(/^(if|else|elseif|for|foreach|while|switch)$/)?(H[g]=v,j(),p=!0,v.match(/^(else|elseif)$/)&&d.match(/\}[\s]*$/)&&(I(),h=!0)):s.type==="paren.lparen"?(j(),v.substr(-1)==="{"&&(p=!0,O=!1,M||(C=1)),v.substr(0,1)==="{"&&(h=!0,d.substr(-1)!=="["&&d.trimRight().substr(-1)==="["?(I(),h=!1):d.trimRight().substr(-1)===")"?I():F())):s.type==="paren.rparen"?(E=1,v.substr(0,1)==="}"&&(H[g-1]==="case"&&E++,d.trimRight().substr(-1)==="{"?I():(h=!0,_&&(C+=2))),v.substr(0,1)==="]"&&d.substr(-1)!=="}"&&d.trimRight().substr(-1)==="}"&&(h=!1,w++,I()),v.substr(0,1)===")"&&d.substr(-1)!=="("&&d.trimRight().substr(-1)==="("&&(h=!1,w++,I()),F()):s.type!=="keyword.operator"&&s.type!=="keyword"||!v.match(/^(=|==|===|!=|!==|&&|\|\||and|or|xor|\+=|.=|>|>=|<|<=|=>)$/)?s.type==="punctuation.operator"&&v===";"?(I(),j(),p=!0,_&&C++):s.type==="punctuation.operator"&&v.match(/^(:|,)$/)?(I(),j(),v.match(/^(,)$/)&&x>0&&S===0&&f.lineBreaksAfterCommasInCurlyBlock?C++:(p=!0,c=!1)):s.type==="support.php_tag"&&v==="?>"&&!c?(I(),h=!0):i(s,"attribute-name")&&d.substr(-1).match(/^\s$/)?h=!0:i(s,"attribute-equals")?(F(),j()):i(s,"tag-close")?(F(),v==="/>"&&(h=!0)):s.type==="keyword"&&v.match(/^(case|default)$/)&&B&&(E=1):(I(),j(),h=!0,p=!0);if(c&&(!s.type.match(/^(comment)$/)||!!v.substr(0,1).match(/^[/#]$/))&&(!s.type.match(/^(string)$/)||!!v.substr(0,1).match(/^['"@]$/))){w=b;if(g>y){w++;for(A=g;A>y;A--)P[A]=w}else g<y&&(w=P[g]);y=g,b=w,E&&(w-=E),O&&!S&&(w++,O=!1);for(A=0;A<w;A++)d+=o}s.type==="keyword"&&v.match(/^(case|default)$/)?B===!1&&(H[g]=v,g++,B=!0):s.type==="keyword"&&v.match(/^(break)$/)&&H[g-1]&&H[g-1].match(/^(case|default)$/)&&(g--,B=!1),s.type==="paren.lparen"&&(S+=(v.match(/\(/g)||[]).length,x+=(v.match(/\{/g)||[]).length,g+=v.length),s.type==="keyword"&&v.match(/^(if|else|elseif|for|while)$/)?(O=!0,S=0):!S&&v.trim()&&s.type!=="comment"&&(O=!1);if(s.type==="paren.rparen"){S-=(v.match(/\)/g)||[]).length,x-=(v.match(/\}/g)||[]).length;for(A=0;A<v.length;A++)g--,v.substr(A,1)==="}"&&H[g]==="case"&&g--}s.type=="text"&&(v=v.replace(/\s+$/," ")),h&&!c&&(F(),d.substr(-1)!=="\n"&&(d+=" ")),d+=v,p&&(d+=" "),c=!1,h=!1,p=!1;if(i(s,"tag-close")&&(D||a.indexOf(m)!==-1)||i(s,"doctype")&&v===">")D&&l&&l.value==="</"?C=-1:C=1;l&&u.indexOf(l.value)===-1&&(i(s,"tag-open")&&v==="</"?g--:i(s,"tag-open")&&v==="<"?g++:i(s,"tag-close")&&v==="/>"&&g--),i(s,"tag-name")&&(m=v),T=N}}s=l}d=d.trim(),e.doc.setValue(d)},t.commands=[{name:"beautify",description:"Format selection (Beautify)",exec:function(e){t.beautify(e.session)},bindKey:"Ctrl-Shift-B"}]}); (function() {
2
+ window.require(["ace/ext/beautify"], function(m) {
3
+ if (typeof module == "object" && typeof exports == "object" && module) {
4
+ module.exports = m;
5
+ }
6
+ });
7
+ })();
8
+