import type { Agent, AdapterEnvironmentTestResult, AgentKeyCreated, AgentRuntimeState, AgentTaskSession, HeartbeatRun, Approval, AgentConfigRevision, } from "@paperclipai/shared"; import { isUuidLike, normalizeAgentUrlKey } from "@paperclipai/shared"; import { ApiError, api } from "./client"; export interface AgentKey { id: string; name: string; createdAt: Date; revokedAt: Date | null; } export interface AdapterModel { id: string; label: string; } export interface ClaudeLoginResult { exitCode: number | null; signal: string | null; timedOut: boolean; loginUrl: string | null; stdout: string; stderr: string; } export interface OrgNode { id: string; name: string; role: string; status: string; reports: OrgNode[]; } export interface AgentHireResponse { agent: Agent; approval: Approval | null; } function withCompanyScope(path: string, companyId?: string) { if (!companyId) return path; const separator = path.includes("?") ? "&" : "?"; return `${path}${separator}companyId=${encodeURIComponent(companyId)}`; } function agentPath(id: string, companyId?: string, suffix = "") { return withCompanyScope(`/agents/${encodeURIComponent(id)}${suffix}`, companyId); } export const agentsApi = { list: (companyId: string) => api.get(`/companies/${companyId}/agents`), org: (companyId: string) => api.get(`/companies/${companyId}/org`), listConfigurations: (companyId: string) => api.get[]>(`/companies/${companyId}/agent-configurations`), get: async (id: string, companyId?: string) => { try { return await api.get(agentPath(id, companyId)); } catch (error) { // Backward-compat fallback: if backend shortname lookup reports ambiguity, // resolve using company agent list while ignoring terminated agents. if ( !(error instanceof ApiError) || error.status !== 409 || !companyId || isUuidLike(id) ) { throw error; } const urlKey = normalizeAgentUrlKey(id); if (!urlKey) throw error; const agents = await api.get(`/companies/${companyId}/agents`); const matches = agents.filter( (agent) => agent.status !== "terminated" && normalizeAgentUrlKey(agent.urlKey) === urlKey, ); if (matches.length !== 1) throw error; return api.get(agentPath(matches[0]!.id, companyId)); } }, getConfiguration: (id: string, companyId?: string) => api.get>(agentPath(id, companyId, "/configuration")), listConfigRevisions: (id: string, companyId?: string) => api.get(agentPath(id, companyId, "/config-revisions")), getConfigRevision: (id: string, revisionId: string, companyId?: string) => api.get(agentPath(id, companyId, `/config-revisions/${revisionId}`)), rollbackConfigRevision: (id: string, revisionId: string, companyId?: string) => api.post(agentPath(id, companyId, `/config-revisions/${revisionId}/rollback`), {}), create: (companyId: string, data: Record) => api.post(`/companies/${companyId}/agents`, data), hire: (companyId: string, data: Record) => api.post(`/companies/${companyId}/agent-hires`, data), update: (id: string, data: Record, companyId?: string) => api.patch(agentPath(id, companyId), data), updatePermissions: (id: string, data: { canCreateAgents: boolean }, companyId?: string) => api.patch(agentPath(id, companyId, "/permissions"), data), pause: (id: string, companyId?: string) => api.post(agentPath(id, companyId, "/pause"), {}), resume: (id: string, companyId?: string) => api.post(agentPath(id, companyId, "/resume"), {}), terminate: (id: string, companyId?: string) => api.post(agentPath(id, companyId, "/terminate"), {}), remove: (id: string, companyId?: string) => api.delete<{ ok: true }>(agentPath(id, companyId)), listKeys: (id: string, companyId?: string) => api.get(agentPath(id, companyId, "/keys")), createKey: (id: string, name: string, companyId?: string) => api.post(agentPath(id, companyId, "/keys"), { name }), revokeKey: (agentId: string, keyId: string, companyId?: string) => api.delete<{ ok: true }>(agentPath(agentId, companyId, `/keys/${encodeURIComponent(keyId)}`)), runtimeState: (id: string, companyId?: string) => api.get(agentPath(id, companyId, "/runtime-state")), taskSessions: (id: string, companyId?: string) => api.get(agentPath(id, companyId, "/task-sessions")), resetSession: (id: string, taskKey?: string | null, companyId?: string) => api.post(agentPath(id, companyId, "/runtime-state/reset-session"), { taskKey: taskKey ?? null }), adapterModels: (companyId: string, type: string) => api.get( `/companies/${encodeURIComponent(companyId)}/adapters/${encodeURIComponent(type)}/models`, ), testEnvironment: ( companyId: string, type: string, data: { adapterConfig: Record }, ) => api.post( `/companies/${companyId}/adapters/${type}/test-environment`, data, ), invoke: (id: string, companyId?: string) => api.post(agentPath(id, companyId, "/heartbeat/invoke"), {}), wakeup: ( id: string, data: { source?: "timer" | "assignment" | "on_demand" | "automation"; triggerDetail?: "manual" | "ping" | "callback" | "system"; reason?: string | null; payload?: Record | null; idempotencyKey?: string | null; }, companyId?: string, ) => api.post(agentPath(id, companyId, "/wakeup"), data), loginWithClaude: (id: string, companyId?: string) => api.post(agentPath(id, companyId, "/claude-login"), {}), };