Skip to content

Commit 645f7fe

Browse files
skyxiaobaiTangMeng12
authored andcommitted
feat: add A2A protocol handler (Server + Client)
Add A2A (Agent-to-Agent) protocol support, enabling phone-side agents to discover and invoke device capabilities via HTTP. Server role (shared port with ws_server): - GET /.well-known/agent.json — AgentCard from tool_registry - POST /a2a/invoke — routes through agent_loop (LLM + tools) - GET /a2a/health — health check Client role (for calling remote agents): - a2a_client_discover() — fetch remote AgentCard - a2a_client_invoke() — invoke remote agent skill - a2a_client_health() — check remote agent health Key design decisions: - Invoke goes through message_bus + mbus_tap for full agent processing (LLM reasoning + tool calls), not direct tool exec - Per-request tap key (fd-based) supports concurrent requests - Heap-allocated ctx with done-flag prevents use-after-free on timeout Also fix: skip mallinfo() when CONFIG_DEBUG_MM is enabled to avoid assert in mm_foreach on QEMU tmpfs-only configurations. Verified on QEMU: health, agent_card, invoke (agent_loop path) all pass end-to-end. Signed-off-by: zhouwenjie1 <zhouwenjie1@xiaomi.com>
1 parent 09699d3 commit 645f7fe

5 files changed

Lines changed: 634 additions & 21 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ if(CONFIG_EXAMPLES_AI_AGENT_VELA)
3434
src/infra/config_store.c
3535
src/infra/network_manager.c
3636
src/infra/url_parse.c
37+
src/infra/a2a_handler.c
3738
src/infra/cron_service.c
3839
src/infra/heartbeat.c
3940
src/channels/nsh_commands.c

src/channels/ws_server.c

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include "channels/ws_server.h"
2424
#include "core/message_bus.h"
25+
#include "infra/a2a_handler.h"
2526
#ifdef CONFIG_AI_AGENT_NODE
2627
#include "node/node_manager.h"
2728
#endif
@@ -145,30 +146,39 @@ static int make_accept_key(const char* key, char* out, size_t out_size)
145146
* Read full HTTP upgrade request, extract Sec-WebSocket-Key.
146147
* Returns 0 on success; sends 101 response.
147148
*/
148-
static int do_ws_handshake(int fd, char* chat_id_out, size_t chat_id_size)
149+
/**
150+
* WebSocket handshake. If pre_buf is non-NULL, use it as already-read
151+
* HTTP headers; otherwise read from fd.
152+
*/
153+
static int do_ws_handshake_ex(int fd, const char *pre_buf, int pre_len,
154+
char *chat_id_out, size_t chat_id_size)
149155
{
150-
char buf[1024];
151-
int total = 0;
156+
char local_buf[1024];
157+
const char *buf;
152158

153-
/* Read until we see the end of headers */
154-
while (total < (int)sizeof(buf) - 1) {
155-
int n = recv(fd, buf + total, sizeof(buf) - 1 - total, 0);
156-
if (n <= 0)
157-
return -1;
158-
total += n;
159-
buf[total] = '\0';
160-
if (strstr(buf, "\r\n\r\n"))
161-
break;
159+
if (pre_buf) {
160+
buf = pre_buf;
161+
} else {
162+
int total = 0;
163+
while (total < (int)sizeof(local_buf) - 1) {
164+
int n = recv(fd, local_buf + total,
165+
sizeof(local_buf) - 1 - total, 0);
166+
if (n <= 0) return -1;
167+
total += n;
168+
local_buf[total] = '\0';
169+
if (strstr(local_buf, "\r\n\r\n")) break;
170+
}
171+
buf = local_buf;
162172
}
163173

164174
/* Extract Sec-WebSocket-Key */
165-
char* key_hdr = strcasestr(buf, "\r\nSec-WebSocket-Key: ");
175+
const char *key_hdr = strcasestr(buf, "\r\nSec-WebSocket-Key: ");
166176
if (!key_hdr) {
167177
syslog(LOG_WARNING, "[%s] No Sec-WebSocket-Key\n", TAG);
168178
return -1;
169179
}
170180
key_hdr += 21;
171-
char* eol = strstr(key_hdr, "\r\n");
181+
const char *eol = strstr(key_hdr, "\r\n");
172182
if (!eol)
173183
return -1;
174184

@@ -195,7 +205,6 @@ static int do_ws_handshake(int fd, char* chat_id_out, size_t chat_id_size)
195205
if (send(fd, resp, rlen, 0) != rlen)
196206
return -1;
197207

198-
/* Default chat_id from fd (may be overridden by first message) */
199208
snprintf(chat_id_out, chat_id_size, "ws_%d", fd);
200209
return 0;
201210
}
@@ -356,9 +365,28 @@ static void* client_thread(void* arg)
356365
free(arg);
357366
int fd = ca.fd;
358367

359-
/* Handshake */
368+
/* Peek HTTP headers to decide: A2A HTTP or WebSocket upgrade */
369+
char peek_buf[2048];
370+
int peek_total = 0;
371+
while (peek_total < (int)sizeof(peek_buf) - 1) {
372+
int n = recv(fd, peek_buf + peek_total,
373+
sizeof(peek_buf) - 1 - peek_total, 0);
374+
if (n <= 0) { close(fd); return NULL; }
375+
peek_total += n;
376+
peek_buf[peek_total] = '\0';
377+
if (strstr(peek_buf, "\r\n\r\n")) break;
378+
}
379+
380+
/* Try A2A HTTP routes first */
381+
if (a2a_try_handle(fd, peek_buf, peek_total)) {
382+
close(fd);
383+
return NULL;
384+
}
385+
386+
/* Not A2A — proceed with WebSocket handshake */
360387
char chat_id[32];
361-
if (do_ws_handshake(fd, chat_id, sizeof(chat_id)) != 0) {
388+
if (do_ws_handshake_ex(fd, peek_buf, peek_total,
389+
chat_id, sizeof(chat_id)) != 0) {
362390
syslog(LOG_WARNING, "[%s] Handshake failed for fd=%d\n", TAG, fd);
363391
close(fd);
364392
return NULL;

src/core/agent_mem.h

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,28 @@ typedef struct {
4545
} agent_mem_status_t;
4646

4747
/**
48-
* Query current heap memory status via mallinfo().
48+
* Query current heap memory status.
49+
* Uses mallinfo() when available and safe. On QEMU builds where
50+
* heap validation may assert (CONFIG_DEBUG_MM + tmpfs fallback),
51+
* returns conservative defaults that don't constrain allocations.
4952
*/
5053
static inline void agent_mem_get_status(agent_mem_status_t* st)
5154
{
55+
st->total_heap = 128 * 1024 * 1024;
56+
st->free_heap = 128 * 1024 * 1024;
57+
st->largest_block = 64 * 1024 * 1024;
58+
59+
#if !defined(CONFIG_DEBUG_MM)
60+
/* Only call mallinfo when heap debug assertions are disabled,
61+
* because CONFIG_DEBUG_MM enables strict node validation in
62+
* mm_foreach that can assert on edge cases (e.g. tmpfs-only boot) */
5263
struct mallinfo mi = mallinfo();
53-
st->total_heap = mi.arena;
54-
st->free_heap = mi.fordblks;
55-
st->largest_block = mi.fordblks; /* conservative estimate */
64+
if (mi.arena > 0) {
65+
st->total_heap = mi.arena;
66+
st->free_heap = mi.fordblks;
67+
st->largest_block = mi.fordblks;
68+
}
69+
#endif
5670
}
5771

5872
/**

0 commit comments

Comments
 (0)