From f97aa641425c843103cdcbd98a64a23bcd129696 Mon Sep 17 00:00:00 2001 From: Markov Date: Mon, 23 Feb 2026 13:57:32 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20align=20with=20AGENT-PROTOCOL=20v1.0=20?= =?UTF-8?q?=E2=80=94=20correct=20WS=20types,=20heartbeat,=20REST=20auth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router.ts | 8 ++++---- src/tracker/client.ts | 28 +++++++++------------------- src/transport/ws-client.ts | 28 +++++----------------------- 3 files changed, 18 insertions(+), 46 deletions(-) diff --git a/src/router.ts b/src/router.ts index cc91bfa..bb856a8 100644 --- a/src/router.ts +++ b/src/router.ts @@ -96,7 +96,7 @@ export class EventRouter { if (collectedText.trim()) { this.log.info('│ → Sending result comment (%d chars)...', collectedText.trim().length); this.log.info('│ Result preview: %s', collectedText.trim().slice(0, 300)); - await this.client.sendMessage({ task_id: task.id, content: collectedText.trim() }).catch((err) => { + await this.client.sendMessage({ task_id: task.id, content: collectedText.trim() }, this.config.slug).catch((err) => { this.log.error({ err, taskId: task.id }, 'Failed to add comment'); }); } @@ -114,7 +114,7 @@ export class EventRouter { await this.client.sendMessage({ task_id: task.id, content: `Agent error: ${err instanceof Error ? err.message : String(err)}`, - }).catch(() => {}); + }, this.config.slug).catch(() => {}); } finally { this.activeTasks--; this.taskTracker.removeTask(task.id); @@ -164,11 +164,11 @@ export class EventRouter { if (collectedText.trim()) { this.log.info('│ → Sending reply (%d chars): %s', collectedText.trim().length, collectedText.trim().slice(0, 200)); if (taskId) { - await this.client.sendMessage({ task_id: taskId, content: collectedText.trim() }).catch((err) => { + await this.client.sendMessage({ task_id: taskId, content: collectedText.trim() }, this.config.slug).catch((err) => { this.log.error({ err, taskId }, 'Failed to send task comment reply'); }); } else if (chatId) { - await this.client.sendMessage({ chat_id: chatId, content: collectedText.trim() }).catch((err) => { + await this.client.sendMessage({ chat_id: chatId, content: collectedText.trim() }, this.config.slug).catch((err) => { this.log.error({ err, chatId }, 'Failed to send chat reply'); }); } diff --git a/src/tracker/client.ts b/src/tracker/client.ts index 0ad17e7..9a52674 100644 --- a/src/tracker/client.ts +++ b/src/tracker/client.ts @@ -43,15 +43,8 @@ export class TrackerClient { } // --- Agent lifecycle --- - - async register(payload: RegistrationPayload): Promise { - this.log.info({ slug: payload.slug }, 'Registering agent'); - await this.request('POST', '/api/v1/agents/register', payload); - } - - async heartbeat(payload: HeartbeatPayload): Promise { - await this.request('POST', '/api/v1/agents/heartbeat', payload); - } + // Registration: done via UI (Settings → Agents) or POST /api/v1/members + // Heartbeat: done via WebSocket only ({"type": "heartbeat", "status": "online"}) // --- Tasks --- @@ -104,9 +97,13 @@ export class TrackerClient { // --- Messages (unified: chat + task comments) --- - async sendMessage(payload: { chat_id?: string; task_id?: string; content: string; mentions?: string[] }): Promise> { + async sendMessage(payload: { chat_id?: string; task_id?: string; content: string; mentions?: string[] }, agentSlug?: string): Promise> { this.log.info({ chatId: payload.chat_id, taskId: payload.task_id, contentLength: payload.content.length }, 'Sending message'); - return this.request('POST', '/api/v1/messages', payload); + return this.request('POST', '/api/v1/messages', { + ...payload, + author_type: 'agent', + author_slug: agentSlug || 'agent', + }); } async listMessages(params: Record): Promise[]> { @@ -115,14 +112,7 @@ export class TrackerClient { } // --- Files --- - - async uploadFile(taskId: string, filename: string, content: string): Promise { - await this.request('POST', `/api/v1/tasks/${taskId}/files`, { filename, content }); - } - - async listTaskFiles(taskId: string): Promise[]> { - return this.request('GET', `/api/v1/tasks/${taskId}/files`); - } + // TODO: file upload/download not implemented yet in Tracker // --- Projects --- diff --git a/src/transport/ws-client.ts b/src/transport/ws-client.ts index 23798c4..d1cab86 100644 --- a/src/transport/ws-client.ts +++ b/src/transport/ws-client.ts @@ -98,12 +98,9 @@ export class WsClientTransport implements TaskTracker { ws.on('open', () => { this.log.info('━━━ WS CONNECTED ━━━'); - // Send auth — use BOTH "event" and "type" for compatibility + // Send auth — Tracker expects "type" field only this.send('auth', { token: this.config.token, - name: this.config.name, - slug: this.config.slug, - capabilities: this.config.capabilities, }); }); @@ -139,11 +136,10 @@ export class WsClientTransport implements TaskTracker { } /** - * Send a JSON message with both "event" and "type" fields set, - * so the tracker picks up whichever field it uses. + * Send a JSON message with "type" field (Tracker protocol). */ private send(eventType: string, payload: Record = {}): void { - const msg = { event: eventType, type: eventType, ...payload }; + const msg = { type: eventType, ...payload }; const json = JSON.stringify(msg); this.log.info('→ SEND: %s', json.slice(0, 500)); if (this.ws?.readyState === WebSocket.OPEN) { @@ -219,24 +215,10 @@ export class WsClientTransport implements TaskTracker { this.reconnectDelay = 1000; this.startHeartbeat(); - // Subscribe to lobby chat - if (this.lobbyChatId) { - this.log.info('→ Subscribing to lobby chat: %s', this.lobbyChatId); - this.send('chat.subscribe', { chat_id: this.lobbyChatId }); - } - - // Subscribe to projects (try both protocols) + // Subscribe to all projects for (const project of this.projects) { this.log.info('→ Subscribing to project: %s (%s)', project.slug, project.id); - // WEBSOCKET-PROTOCOL.md style: subscribe with channels - this.send('subscribe', { channels: [`project:${project.slug}`] }); - // TRACKER-PROTOCOL.md style: project.subscribe this.send('project.subscribe', { project_id: project.id }); - // If project has a chat_id, subscribe to it - if (project.chat_id) { - this.log.info('→ Subscribing to project chat: %s', project.chat_id); - this.send('chat.subscribe', { chat_id: project.chat_id }); - } } if (this.resolveStart) { @@ -303,7 +285,7 @@ export class WsClientTransport implements TaskTracker { private sendHeartbeat(): void { const status = this.currentTasks.size > 0 ? 'busy' : 'online'; - this.send('agent.heartbeat', { status, current_tasks: [...this.currentTasks] }); + this.send('heartbeat', { status }); } private scheduleReconnect(): void {