2026-09-15 05:20:44 +00:00
|
|
|
export type HumanQuestion = {
|
|
|
|
|
question?: string; reason?: string; options?: unknown[]; kind?: string;
|
|
|
|
|
task_id?: string; question_id?: string; site_url?: string; surface?: string;
|
|
|
|
|
handover?: boolean; submitted?: boolean;
|
|
|
|
|
};
|
|
|
|
|
|
2026-09-15 03:20:42 +00:00
|
|
|
export type AgentActivity = {
|
|
|
|
|
state: "idle" | "queued" | "running" | "waiting_input";
|
|
|
|
|
running: boolean;
|
|
|
|
|
queued: boolean;
|
|
|
|
|
observed_at_ms: number;
|
|
|
|
|
active_task_ids: string[];
|
2026-09-16 08:17:38 +00:00
|
|
|
queued_task_ids?: string[];
|
2026-09-15 05:20:44 +00:00
|
|
|
question?: HumanQuestion | null;
|
2026-09-15 03:20:42 +00:00
|
|
|
};
|
|
|
|
|
|
2026-09-14 09:08:20 +00:00
|
|
|
export type AgentRow = {
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
expertise?: string;
|
2026-09-15 09:07:16 +00:00
|
|
|
title?: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
tags?: string[];
|
|
|
|
|
avatar_color?: string;
|
|
|
|
|
avatar_shape?: string;
|
|
|
|
|
has_avatar?: boolean;
|
|
|
|
|
avatar_url?: string | null;
|
2026-09-14 09:08:20 +00:00
|
|
|
preview?: string;
|
2026-09-15 09:07:16 +00:00
|
|
|
last_at?: string | null;
|
2026-09-14 09:08:20 +00:00
|
|
|
running?: boolean;
|
2026-09-15 03:20:42 +00:00
|
|
|
activity?: AgentActivity;
|
2026-09-14 09:08:20 +00:00
|
|
|
};
|
|
|
|
|
|
2026-09-15 09:07:16 +00:00
|
|
|
export type AgentProfile = {
|
|
|
|
|
name: string;
|
|
|
|
|
title: string;
|
|
|
|
|
description: string;
|
|
|
|
|
tags: string[];
|
|
|
|
|
avatar_color: string;
|
|
|
|
|
avatar_shape: string;
|
|
|
|
|
};
|
|
|
|
|
|
2026-09-15 03:20:42 +00:00
|
|
|
export type TranscriptItem = { role: "user" | "assistant"; content: string; at?: string };
|
2026-09-14 09:08:20 +00:00
|
|
|
|
2026-09-15 09:07:16 +00:00
|
|
|
export type AgentDetail = AgentRow & {
|
2026-09-14 09:08:20 +00:00
|
|
|
expertise: string;
|
|
|
|
|
preview: string;
|
|
|
|
|
transcript: TranscriptItem[];
|
|
|
|
|
running: boolean;
|
2026-09-15 03:20:42 +00:00
|
|
|
activity: AgentActivity;
|
2026-09-14 09:08:20 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export type TeamEvent = {
|
|
|
|
|
id: number;
|
|
|
|
|
kind: string;
|
|
|
|
|
task_id?: string | null;
|
|
|
|
|
payload: Record<string, unknown>;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
async function request<T>(path: string, init?: RequestInit): Promise<T> {
|
|
|
|
|
const res = await fetch(path, {
|
|
|
|
|
...init,
|
2026-09-16 06:47:32 +00:00
|
|
|
signal: init?.signal ? AbortSignal.any([init.signal, AbortSignal.timeout(15000)]) : (!init?.method || init.method === "GET" ? AbortSignal.timeout(15000) : undefined),
|
2026-09-14 09:08:20 +00:00
|
|
|
headers: { "content-type": "application/json", ...(init?.headers || {}) },
|
|
|
|
|
});
|
|
|
|
|
const raw = await res.text();
|
|
|
|
|
let data: unknown = null;
|
|
|
|
|
try {
|
|
|
|
|
data = raw ? JSON.parse(raw) : null;
|
|
|
|
|
} catch {
|
|
|
|
|
data = raw;
|
|
|
|
|
}
|
|
|
|
|
if (!res.ok) {
|
|
|
|
|
const err = data as { error?: string } | null;
|
|
|
|
|
throw new Error(err?.error || raw || String(res.status));
|
|
|
|
|
}
|
|
|
|
|
return data as T;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const api = {
|
|
|
|
|
health: () => request<{ ok: boolean; service: boolean; model: string }>("/api/health"),
|
|
|
|
|
agents: async () => {
|
|
|
|
|
const data = await request<AgentRow[] | { agents?: AgentRow[] }>("/api/agents");
|
|
|
|
|
const agents = Array.isArray(data) ? data : data.agents || [];
|
|
|
|
|
return { agents };
|
|
|
|
|
},
|
2026-09-15 09:07:16 +00:00
|
|
|
createAgent: (profile: AgentProfile) =>
|
|
|
|
|
request<AgentRow>("/api/agents", { method: "POST", body: JSON.stringify(profile) }),
|
|
|
|
|
updateAgent: (id: string, profile: AgentProfile) =>
|
|
|
|
|
request<AgentRow>(`/api/agents/${id}`, { method: "PATCH", body: JSON.stringify(profile) }),
|
|
|
|
|
setAvatar: async (id: string, png: Blob) => {
|
|
|
|
|
const res = await fetch(`/api/agents/${encodeURIComponent(id)}/avatar`, {
|
|
|
|
|
method: "PUT",
|
|
|
|
|
headers: { "content-type": "image/png" },
|
|
|
|
|
body: png,
|
|
|
|
|
});
|
|
|
|
|
const data = await res.json().catch(() => ({}));
|
|
|
|
|
if (!res.ok) throw new Error((data as { error?: string }).error || String(res.status));
|
|
|
|
|
return data as AgentRow;
|
|
|
|
|
},
|
|
|
|
|
clearAvatar: (id: string) =>
|
|
|
|
|
request<AgentRow>(`/api/agents/${encodeURIComponent(id)}/avatar`, { method: "DELETE" }),
|
2026-09-15 03:20:42 +00:00
|
|
|
deleteAgent: (id: string) => request<{ ok: boolean; id: string; name: string }>(`/api/agents/${id}`, { method: "DELETE" }),
|
2026-09-16 06:47:32 +00:00
|
|
|
agent: (id: string, signal?: AbortSignal) => request<AgentDetail>(`/api/agents/${id}`, { signal }),
|
2026-09-15 03:20:42 +00:00
|
|
|
activity: (id: string) => request<AgentActivity>(`/api/agents/${id}/activity`, { signal: AbortSignal.timeout(8000) }),
|
2026-09-14 09:08:20 +00:00
|
|
|
send: (id: string, text: string) =>
|
|
|
|
|
request<{ queued?: string }>(`/api/agents/${id}/messages`, { method: "POST", body: JSON.stringify({ text }) }),
|
2026-09-15 05:20:44 +00:00
|
|
|
handoverDone: (id: string, taskId: string, questionId: string, action: "done" | "cancel" = "done") => request(`/api/agents/${id}/handover`, { method: "POST", body: JSON.stringify({ task_id: taskId, question_id: questionId, action }), signal: AbortSignal.timeout(8000) }),
|
2026-09-14 09:08:20 +00:00
|
|
|
stop: (id: string) => request(`/api/agents/${id}/stop`, { method: "POST", body: "{}" }),
|
2026-09-16 06:40:00 +00:00
|
|
|
computerStatus: (id: string) =>
|
2026-09-15 03:20:42 +00:00
|
|
|
request<{
|
|
|
|
|
ready: boolean;
|
2026-09-16 06:40:00 +00:00
|
|
|
state?: string;
|
|
|
|
|
viewer_url: string;
|
|
|
|
|
error?: string;
|
|
|
|
|
revision?: string;
|
|
|
|
|
crowded?: boolean;
|
|
|
|
|
}>(`/api/agents/${encodeURIComponent(id)}/computer`),
|
|
|
|
|
computer: (id: string, action: "start" | "restart" | "update" = "start") =>
|
|
|
|
|
request<{
|
|
|
|
|
ready: boolean;
|
|
|
|
|
state?: string;
|
2026-09-15 03:20:42 +00:00
|
|
|
viewer_url: string;
|
|
|
|
|
error?: string;
|
|
|
|
|
updated?: boolean;
|
|
|
|
|
revision?: string;
|
2026-09-16 06:40:00 +00:00
|
|
|
crowded?: boolean;
|
2026-09-16 08:17:38 +00:00
|
|
|
}>(id ? `/api/agents/${encodeURIComponent(id)}/computer` : "/api/computer", {
|
2026-09-14 09:08:20 +00:00
|
|
|
method: "POST",
|
2026-09-15 03:20:42 +00:00
|
|
|
body: JSON.stringify({ action }),
|
2026-09-14 09:08:20 +00:00
|
|
|
}),
|
|
|
|
|
};
|