fix: align with AGENT-PROTOCOL v1.0 — correct WS types, heartbeat, REST auth
This commit is contained in:
parent
368b9abf69
commit
f97aa64142
@ -96,7 +96,7 @@ export class EventRouter {
|
|||||||
if (collectedText.trim()) {
|
if (collectedText.trim()) {
|
||||||
this.log.info('│ → Sending result comment (%d chars)...', collectedText.trim().length);
|
this.log.info('│ → Sending result comment (%d chars)...', collectedText.trim().length);
|
||||||
this.log.info('│ Result preview: %s', collectedText.trim().slice(0, 300));
|
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');
|
this.log.error({ err, taskId: task.id }, 'Failed to add comment');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -114,7 +114,7 @@ export class EventRouter {
|
|||||||
await this.client.sendMessage({
|
await this.client.sendMessage({
|
||||||
task_id: task.id,
|
task_id: task.id,
|
||||||
content: `Agent error: ${err instanceof Error ? err.message : String(err)}`,
|
content: `Agent error: ${err instanceof Error ? err.message : String(err)}`,
|
||||||
}).catch(() => {});
|
}, this.config.slug).catch(() => {});
|
||||||
} finally {
|
} finally {
|
||||||
this.activeTasks--;
|
this.activeTasks--;
|
||||||
this.taskTracker.removeTask(task.id);
|
this.taskTracker.removeTask(task.id);
|
||||||
@ -164,11 +164,11 @@ export class EventRouter {
|
|||||||
if (collectedText.trim()) {
|
if (collectedText.trim()) {
|
||||||
this.log.info('│ → Sending reply (%d chars): %s', collectedText.trim().length, collectedText.trim().slice(0, 200));
|
this.log.info('│ → Sending reply (%d chars): %s', collectedText.trim().length, collectedText.trim().slice(0, 200));
|
||||||
if (taskId) {
|
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');
|
this.log.error({ err, taskId }, 'Failed to send task comment reply');
|
||||||
});
|
});
|
||||||
} else if (chatId) {
|
} 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');
|
this.log.error({ err, chatId }, 'Failed to send chat reply');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,15 +43,8 @@ export class TrackerClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Agent lifecycle ---
|
// --- Agent lifecycle ---
|
||||||
|
// Registration: done via UI (Settings → Agents) or POST /api/v1/members
|
||||||
async register(payload: RegistrationPayload): Promise<void> {
|
// Heartbeat: done via WebSocket only ({"type": "heartbeat", "status": "online"})
|
||||||
this.log.info({ slug: payload.slug }, 'Registering agent');
|
|
||||||
await this.request('POST', '/api/v1/agents/register', payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
async heartbeat(payload: HeartbeatPayload): Promise<void> {
|
|
||||||
await this.request('POST', '/api/v1/agents/heartbeat', payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Tasks ---
|
// --- Tasks ---
|
||||||
|
|
||||||
@ -104,9 +97,13 @@ export class TrackerClient {
|
|||||||
|
|
||||||
// --- Messages (unified: chat + task comments) ---
|
// --- Messages (unified: chat + task comments) ---
|
||||||
|
|
||||||
async sendMessage(payload: { chat_id?: string; task_id?: string; content: string; mentions?: string[] }): Promise<Record<string, unknown>> {
|
async sendMessage(payload: { chat_id?: string; task_id?: string; content: string; mentions?: string[] }, agentSlug?: string): Promise<Record<string, unknown>> {
|
||||||
this.log.info({ chatId: payload.chat_id, taskId: payload.task_id, contentLength: payload.content.length }, 'Sending message');
|
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<string, string>): Promise<Record<string, unknown>[]> {
|
async listMessages(params: Record<string, string>): Promise<Record<string, unknown>[]> {
|
||||||
@ -115,14 +112,7 @@ export class TrackerClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Files ---
|
// --- Files ---
|
||||||
|
// TODO: file upload/download not implemented yet in Tracker
|
||||||
async uploadFile(taskId: string, filename: string, content: string): Promise<void> {
|
|
||||||
await this.request('POST', `/api/v1/tasks/${taskId}/files`, { filename, content });
|
|
||||||
}
|
|
||||||
|
|
||||||
async listTaskFiles(taskId: string): Promise<Record<string, unknown>[]> {
|
|
||||||
return this.request('GET', `/api/v1/tasks/${taskId}/files`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Projects ---
|
// --- Projects ---
|
||||||
|
|
||||||
|
|||||||
@ -98,12 +98,9 @@ export class WsClientTransport implements TaskTracker {
|
|||||||
|
|
||||||
ws.on('open', () => {
|
ws.on('open', () => {
|
||||||
this.log.info('━━━ WS CONNECTED ━━━');
|
this.log.info('━━━ WS CONNECTED ━━━');
|
||||||
// Send auth — use BOTH "event" and "type" for compatibility
|
// Send auth — Tracker expects "type" field only
|
||||||
this.send('auth', {
|
this.send('auth', {
|
||||||
token: this.config.token,
|
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,
|
* Send a JSON message with "type" field (Tracker protocol).
|
||||||
* so the tracker picks up whichever field it uses.
|
|
||||||
*/
|
*/
|
||||||
private send(eventType: string, payload: Record<string, unknown> = {}): void {
|
private send(eventType: string, payload: Record<string, unknown> = {}): void {
|
||||||
const msg = { event: eventType, type: eventType, ...payload };
|
const msg = { type: eventType, ...payload };
|
||||||
const json = JSON.stringify(msg);
|
const json = JSON.stringify(msg);
|
||||||
this.log.info('→ SEND: %s', json.slice(0, 500));
|
this.log.info('→ SEND: %s', json.slice(0, 500));
|
||||||
if (this.ws?.readyState === WebSocket.OPEN) {
|
if (this.ws?.readyState === WebSocket.OPEN) {
|
||||||
@ -219,24 +215,10 @@ export class WsClientTransport implements TaskTracker {
|
|||||||
this.reconnectDelay = 1000;
|
this.reconnectDelay = 1000;
|
||||||
this.startHeartbeat();
|
this.startHeartbeat();
|
||||||
|
|
||||||
// Subscribe to lobby chat
|
// Subscribe to all projects
|
||||||
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)
|
|
||||||
for (const project of this.projects) {
|
for (const project of this.projects) {
|
||||||
this.log.info('→ Subscribing to project: %s (%s)', project.slug, project.id);
|
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 });
|
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) {
|
if (this.resolveStart) {
|
||||||
@ -303,7 +285,7 @@ export class WsClientTransport implements TaskTracker {
|
|||||||
|
|
||||||
private sendHeartbeat(): void {
|
private sendHeartbeat(): void {
|
||||||
const status = this.currentTasks.size > 0 ? 'busy' : 'online';
|
const status = this.currentTasks.size > 0 ? 'busy' : 'online';
|
||||||
this.send('agent.heartbeat', { status, current_tasks: [...this.currentTasks] });
|
this.send('heartbeat', { status });
|
||||||
}
|
}
|
||||||
|
|
||||||
private scheduleReconnect(): void {
|
private scheduleReconnect(): void {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user