import type { SlackSetupOptions, SlackManagedProvisionInput, SlackManagedResult } from "@paperclipai/shared"; import { api } from "./client"; import type { SlackAppConfiguration, SlackRegistrationInput, SlackRegistrationState, SlackAvatarState, SlackAccountState, SlackInstallAuthorization, UpdateChatEndpointInput, PhotonProjectInspection, PhotonChannelConfiguration, ChatPublicationBatchStatus, ChatPublicationState, ChatPublicationSummary, ChatActivityItem, ChatFileTransferResolutionPrecondition, } from "@paperclipai/shared"; export type { ChatPublicationSummary, ChatActivityItem, } from "@paperclipai/shared"; export type ChatProvider = "slack" | "github" | "discord" | "microsoft-teams" | "telegram" | "agentmail" | "imessage-photon" | "speko"; export type ChatEndpointStatus = | "draft" | "verifying" | "active" | "paused" | "attention" | "revoked" | "archived"; export type ChatEndpointSetupAction = "configure" | "verify" | "pause" | "resume" | "reconnect" | "remove"; export interface ChatEndpointResource { executionDefaults?: import("@paperclipai/shared").ChatExecutionDefaults | null; metadata?: Record; id: string; type: string; providerResourceId: string; label: string; availability: "available" | "unavailable" | "removed"; enabled: boolean; detail?: string | null; participants?: string[]; } export interface ChatIdentityLink { githubUserId?: string; githubLogin?: string | null; id: string; principalId: string; /** Latest discovery-only connect command received by this endpoint. */ lastConnectAt?: string | null; externalLabel: string; externalDetail?: string | null; paperclipUserId?: string | null; paperclipUserLabel?: string | null; paperclipUserImage?: string | null; status: "linked" | "pending" | "revoked"; } export interface ChatConversation { id: string; externalLabel: string; externalUrl?: string | null; issueId?: string | null; issueIdentifier?: string | null; issueTitle?: string | null; state: "active" | "waiting" | "completed" | "unavailable" | "endpoint_removed"; updatedAt: string; lastPublicationStatus?: ChatPublicationState | null; } export interface ExternalChannelBindingSummary { endpointId: string; provider: ChatProvider; botLabel?: string | null; externalLabel: string; externalUrl?: string | null; conversationId: string; publicationState?: string | null; assignedAgentLocked: true; } export interface ChatIdentityLinkPreview { selfService?: boolean; canConfirm?: boolean; endpointId: string; companyId: string; companyName: string; companyPrefix: string; provider: ChatProvider; providerAccountLabel?: string | null; botLabel?: string | null; externalLabel: string; externalDetail?: string | null; expiresAt: string; } export interface ChatEndpoint { executionDefaults?: import("@paperclipai/shared").ChatExecutionDefaults | null; communicationInstructions?: string; publicationMode?: "automatic" | "explicit"; externalExecutionPolicy?: "restricted" | "agent"; id: string; companyId: string; provider: ChatProvider; status: ChatEndpointStatus; assignedAgentId: string; assignedAgentName: string; connectionId?: string | null; sponsorUserId?: string | null; providerAccountId?: string | null; providerAccountLabel?: string | null; botLabel?: string | null; botUsername?: string | null; botExternalId?: string | null; photonAllocation?: "dedicated" | "shared"; allowDirectMessages?: boolean; requireAtMention?: boolean; allowGroupChats?: boolean; allowUnlinkedPeople: boolean; replyMode?: "subscribed" | "mention_each_reply" | null; healthMessage?: string | null; lastError?: string | null; lastActivityAt?: string | null; resources?: ChatEndpointResource[]; identityLinks?: ChatIdentityLink[]; conversations?: ChatConversation[]; activity?: ChatActivityItem[]; setup?: { github?: import("@paperclipai/shared").ChatEndpointSetupState["github"]; step: string; testStartedAt?: string | null; testSkipped?: boolean; authorizationUrl?: string | null; providerUrl?: string | null; webhookUrl?: string | null; messagingEndpoint?: string | null; command?: string | null; slackApp?: SlackAppConfiguration; slackSetupMethod?: "managed" | "automatic" | "manual" | "existing"; slackManagerError?: string; slackRegistration?: SlackRegistrationState; slackAvatar?: SlackAvatarState; slackAccount?: SlackAccountState; slackOAuthCallbackUri?: string | null; webhookVerifiedAt?: string | null; webhookSecretConfigured?: boolean; callbackSurfaces?: { events: ChatCallbackSurfaceState; interactivity: ChatCallbackSurfaceState; slashCommands: ChatCallbackSurfaceState; }; callbacksNeedUpdate?: boolean; }; } export interface ChatCallbackSurfaceState { status: "current" | "stale" | "unverified"; observedAt?: string | null; } export interface ChatEndpointSetupSecret { webhookSecret: string; } type ListResponse = | T[] | { items?: T[]; endpoints?: T[]; resources?: T[]; principals?: T[]; conversations?: T[]; deliveries?: T[]; publications?: T[]; }; function rows(response: ListResponse): T[] { if (Array.isArray(response)) return response; return ( response.items ?? response.endpoints ?? response.resources ?? response.principals ?? response.conversations ?? response.deliveries ?? response.publications ?? [] ); } export const chatEndpointsApi = { list: async (companyId: string) => rows( await api.get>( `/companies/${companyId}/chat-endpoints`, ), ), get: (endpointId: string) => api.get(`/chat-endpoints/${endpointId}`), create: ( companyId: string, input: { provider: ChatProvider; assignedAgentId: string; slackApp?: SlackAppConfiguration; slackSetupMethod?: "managed" | "automatic" | "manual" | "existing" }, ) => api.post(`/companies/${companyId}/chat-endpoints`, input), update: ( endpointId: string, input: UpdateChatEndpointInput, ) => api.patch(`/chat-endpoints/${endpointId}`, input), setup: ( endpointId: string, input: { action: ChatEndpointSetupAction; credentials?: Record; photon?: PhotonChannelConfiguration; }, ) => api.post(`/chat-endpoints/${endpointId}/setup`, input), sendSlackTestMessage: (endpointId: string, input: import("@paperclipai/shared").SlackTestMessageInput) => api.post(`/chat-endpoints/${endpointId}/slack/test-message`, input), slackSetupOptions: (companyId: string) => api.get(`/companies/${companyId}/chat-slack/setup-options`), authorizeSlackManager: (endpointId: string) => api.post(`/chat-endpoints/${endpointId}/slack/managed/authorize`, {}), provisionManagedSlack: (endpointId: string, input: SlackManagedProvisionInput) => api.post(`/chat-endpoints/${endpointId}/slack/managed/provision`, input), createSlackApp: (endpointId: string, input: SlackRegistrationInput) => api.post(`/chat-endpoints/${endpointId}/slack/registration`, input), installSlackApp: (endpointId: string) => api.post(`/chat-endpoints/${endpointId}/slack/install`, {}), resumeSlackInstallation: (endpointId: string) => api.post(`/chat-endpoints/${endpointId}/slack/resume`, {}), inspectPhoton: (endpointId: string, input: { projectId: string; projectSecret: string }) => api.post(`/chat-endpoints/${endpointId}/photon/inspect`, input), generateSetupSecret: (endpointId: string) => api.post( `/chat-endpoints/${endpointId}/setup-secret`, {}, ), test: (endpointId: string) => api.post(`/chat-endpoints/${endpointId}/test`, {}), finishSlackSetup: (endpointId: string) => api.post(`/chat-endpoints/${endpointId}/finish`, {}), setupTestStatus: (endpointId: string) => api.get<{ messageReceivedAt: string | null }>(`/chat-endpoints/${endpointId}/test-status`), requestIdentityAccess: (token: string) => api.post<{ status: "member" | "pending_approval" }>("/chat-identity-links/request-access", { token }), listResources: async (endpointId: string) => rows( await api.get>( `/chat-endpoints/${endpointId}/resources`, ), ), updateResources: ( endpointId: string, resources: Array<{ id: string; enabled?: boolean; executionDefaults?: import("@paperclipai/shared").ChatExecutionDefaults | null }>, ) => api.put(`/chat-endpoints/${endpointId}/resources`, { resources, }), listPrincipals: async (endpointId: string) => rows( await api.get>( `/chat-endpoints/${endpointId}/principals`, ), ), createLinkIntent: (endpointId: string, principalId: string) => api.post<{ confirmationUrl: string }>( `/chat-endpoints/${endpointId}/principals/${principalId}/link-intent`, {}, ), revokeLink: (endpointId: string, principalId: string) => api.delete( `/chat-endpoints/${endpointId}/principals/${principalId}/link`, ), previewIdentityLink: (token: string) => api.get( `/chat-identity-links/preview?token=${encodeURIComponent(token)}`, ), confirmIdentityLink: (token: string) => api.post<{ ok: true; endpointId: string }>("/chat-identity-links/confirm", { token, }), listConversations: async (endpointId: string) => rows( await api.get>( `/chat-endpoints/${endpointId}/conversations`, ), ), listActivity: async (endpointId: string) => rows( await api.get>( `/chat-endpoints/${endpointId}/activity`, ), ), listActivityPage: (endpointId: string, cursor?: string) => api.get<{ items: ChatActivityItem[]; nextCursor: string | null }>( `/chat-endpoints/${endpointId}/activity?limit=25${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}`, ), getIssueBinding: (issueId: string) => api.get( `/issues/${issueId}/chat-binding`, ), replayDelivery: (endpointId: string, deliveryId: string) => api.post( `/chat-endpoints/${endpointId}/deliveries/${deliveryId}/replay`, {}, ), replayPublication: (endpointId: string, publicationId: string) => api.post( `/chat-endpoints/${endpointId}/publications/${publicationId}/replay`, {}, ), resolvePublication: ( endpointId: string, publicationId: string, action: "mark_delivered" | "retry_anyway" | "cancel", fileTransfer?: ChatFileTransferResolutionPrecondition, ) => api.post( `/chat-endpoints/${endpointId}/publications/${publicationId}/resolve`, { action, ...(fileTransfer ? { fileTransfer: { phase: fileTransfer.phase, version: fileTransfer.version, }, } : {}), }, ), resolveAction: ( endpointId: string, actionId: string, action: "mark_delivered" | "retry_anyway" | "cancel", ) => api.post( `/chat-endpoints/${endpointId}/actions/${actionId}/resolve`, { action }, ), getPublicationBatchStatus: ( endpointId: string, conversationId: string, publicationId: string, ) => api.get( `/chat-endpoints/${endpointId}/conversations/${conversationId}/publications/${publicationId}/status`, ), publishComment: ( endpointId: string, conversationId: string, commentId: string, ) => api.post( `/chat-endpoints/${endpointId}/conversations/${conversationId}/publications`, { commentId }, ), publishBoardMessage: ( endpointId: string, conversationId: string, body: string, idempotencyKey: string, attachmentIds: string[] = [], ) => api.post( `/chat-endpoints/${endpointId}/conversations/${conversationId}/publications`, { body, idempotencyKey, ...(attachmentIds.length ? { attachmentIds } : {}), }, ), };