import type { Task, Handoff, Decision, Memory, Message, Ask, ActivityItem } from '../core/schema.js'; import type { IndexEntry } from '../core/index.js'; import type { InboxMessage } from '../core/services/messageService.js'; import type { TaskLogEntry } from '../core/services/taskLogService.js'; import type { HealthReport } from '../core/services/presenceService.js'; export class RemoteError extends Error { constructor(public status: number, message: string) { super(message); } } async function request(baseUrl: string, method: string, path: string, body?: unknown): Promise { const url = `${baseUrl}${path}`; let res: Response; try { res = await fetch(url, { method, headers: body ? { 'Content-Type': 'application/json' } : undefined, body: body ? JSON.stringify(body) : undefined, }); } catch (err) { const message = err instanceof Error ? err.message : String(err); throw new RemoteError(0, `Cannot reach AgentHub server at ${baseUrl}: ${message}`); } const text = await res.text(); if (!res.ok) { throw new RemoteError(res.status, text || `HTTP ${res.status}`); } if (!text) { throw new RemoteError(res.status, 'Empty response from server'); } return JSON.parse(text) as T; } export const remoteClient = { async getStatus(baseUrl: string): Promise { return request<{ body: string }>(baseUrl, 'GET', '/status').then((r) => r.body); }, async getHealth(baseUrl: string): Promise { return request(baseUrl, 'GET', '/health'); }, async getAgentIdentity(baseUrl: string, agent: string): Promise<{ canonical: string; names: string[]; workTimeoutSec?: number }> { return request<{ canonical: string; names: string[]; workTimeoutSec?: number }>( baseUrl, 'GET', `/agents/${encodeURIComponent(agent)}/identity`, ); }, async announce(baseUrl: string, agent: string, role?: string, action: 'joined' | 'left' = 'joined'): Promise { await request<{ ok: boolean }>(baseUrl, 'POST', '/announce', { agent, role, action }); }, async setLoop(baseUrl: string, agent: string, active: boolean, reason?: string): Promise { await request<{ ok: boolean }>( baseUrl, 'POST', `/agents/${encodeURIComponent(agent)}/loop`, { active, reason }, ); }, async updateStatus(baseUrl: string): Promise { return request<{ body: string }>(baseUrl, 'POST', '/status/update').then((r) => r.body); }, async createTask(baseUrl: string, options: Partial): Promise { return request(baseUrl, 'POST', '/tasks', options); }, async recordExternalTask(baseUrl: string, options: Partial & { title: string; doneBy: string }): Promise { return request(baseUrl, 'POST', '/tasks/record', options); }, async listTasks(baseUrl: string, filters?: { status?: string; role?: string }): Promise { const params = new URLSearchParams((filters ?? {}) as Record); const qs = params.toString(); return request(baseUrl, 'GET', `/tasks${qs ? '?' + qs : ''}`); }, async getTask(baseUrl: string, id: string): Promise<{ task: Task; body: string }> { return request<{ task: Task; body: string }>(baseUrl, 'GET', `/tasks/${id}`); }, async claimTask(baseUrl: string, id: string, agentName: string): Promise { return request(baseUrl, 'PATCH', `/tasks/${id}`, { status: 'in_progress', assignedTo: agentName }); }, async dispatchTask(baseUrl: string, id: string, agent: string): Promise { return request(baseUrl, 'POST', `/tasks/${id}/dispatch`, { agent }); }, async doneTask( baseUrl: string, id: string, meta?: { tokens?: number; duration?: number; by?: string }, ): Promise { return request(baseUrl, 'PATCH', `/tasks/${id}`, { status: 'done', doneTokens: meta?.tokens, doneDuration: meta?.duration, doneBy: meta?.by, }); }, async assignTask(baseUrl: string, id: string, agentName: string): Promise { return request(baseUrl, 'PATCH', `/tasks/${id}`, { assignedTo: agentName }); }, async reviewTask(baseUrl: string, id: string): Promise { return request(baseUrl, 'PATCH', `/tasks/${id}`, { status: 'review' }); }, async reopenTask(baseUrl: string, id: string): Promise { return request(baseUrl, 'PATCH', `/tasks/${id}`, { status: 'open' }); }, async getTaskActivity(baseUrl: string, id: string): Promise { return request(baseUrl, 'GET', `/tasks/${id}/activity`); }, async appendTaskLog( baseUrl: string, id: string, entry: { text: string; agent?: string; level?: string }, ): Promise { return request(baseUrl, 'POST', `/tasks/${id}/log`, entry); }, /** TSK-0274: was wartet auf diesen Agenten (Check-in-Kanal für Arbeitende). */ async getPending(baseUrl: string, agent: string, taskId?: string): Promise { const query = taskId ? `?taskId=${encodeURIComponent(taskId)}` : ''; return request(baseUrl, 'GET', `/agents/${encodeURIComponent(agent)}/pending${query}`); }, async createHandoff(baseUrl: string, options: Partial): Promise { return request(baseUrl, 'POST', '/handoffs', options); }, async listHandoffs(baseUrl: string): Promise { return request(baseUrl, 'GET', '/handoffs'); }, async getHandoff(baseUrl: string, id: string): Promise<{ handoff: Handoff; body: string }> { return request<{ handoff: Handoff; body: string }>(baseUrl, 'GET', `/handoffs/${id}`); }, async createDecision(baseUrl: string, options: Partial): Promise { return request(baseUrl, 'POST', '/decisions', options); }, async listDecisions(baseUrl: string): Promise { return request(baseUrl, 'GET', '/decisions'); }, async addMemory(baseUrl: string, options: Partial): Promise { return request(baseUrl, 'POST', '/memory', options); }, async searchMemory(baseUrl: string, query: string): Promise { return request(baseUrl, 'GET', `/memory/search?q=${encodeURIComponent(query)}`); }, async listMemory(baseUrl: string): Promise { return request(baseUrl, 'GET', '/memory'); }, async sendMessage(baseUrl: string, options: Partial): Promise { return request(baseUrl, 'POST', '/messages', options); }, async getInbox(baseUrl: string, agent: string, unreadOnly = false): Promise { const q = `?agent=${encodeURIComponent(agent)}${unreadOnly ? '&unread=1' : ''}`; return request(baseUrl, 'GET', `/messages${q}`); }, async markMessageRead(baseUrl: string, id: string): Promise { return request(baseUrl, 'POST', `/messages/${id}/read`); }, async ackMessage(baseUrl: string, id: string, by?: string): Promise { return request(baseUrl, 'POST', `/messages/${id}/ack`, by ? { by } : undefined); }, async getMessage(baseUrl: string, id: string): Promise<{ message: Message; body: string }> { return request<{ message: Message; body: string }>(baseUrl, 'GET', `/messages/${id}`); }, async createAsk(baseUrl: string, options: Partial): Promise { return request(baseUrl, 'POST', '/asks', options); }, async listAsks(baseUrl: string, filters?: { to?: string; status?: string }): Promise { const params = new URLSearchParams((filters ?? {}) as Record); const qs = params.toString(); return request(baseUrl, 'GET', `/asks${qs ? '?' + qs : ''}`); }, async getAsk(baseUrl: string, id: string): Promise<{ ask: Ask; body: string }> { return request<{ ask: Ask; body: string }>(baseUrl, 'GET', `/asks/${id}`); }, async answerAsk(baseUrl: string, id: string, text: string, by?: string): Promise { return request(baseUrl, 'POST', `/asks/${id}/answer`, { text, by }); }, async escalateAsk(baseUrl: string, id: string, note?: string): Promise { return request(baseUrl, 'POST', `/asks/${id}/escalate`, note ? { note } : undefined); }, async delegate(baseUrl: string, auto: boolean): Promise<{ suggestion?: { task: IndexEntry; role: string; preferredAgent: string }; handoff?: Handoff }> { return request<{ suggestion?: { task: IndexEntry; role: string; preferredAgent: string }; handoff?: Handoff }>(baseUrl, 'POST', `/delegate?auto=${auto}`); }, };