import { type CanonMessage, type CanonConversation, type CanonUnifiedDiff, type CanonContact, type CanonContactRequest, type ContactRequestListOptions, type ContactRequestLifecyclePageOptions, type CanonContactRequestListPage, type CanonConversationsPage, type CanonConversationsPageOptions, type CanonMessagesPage, type CanonResolveAdmissionResult, type ResolveAdmissionTargetInput, type AppendMemoStreamChunkOptions, type CanonVoiceSession, type CanonVoiceSessionToken, type CreateMemoStreamOptions, type CreateMemoStreamResult, type CreateVoiceSessionOptions, type MemoStreamChunkAck, type AgentContext, type AddMemberResult, type CreateContactRequestResult, type CreateConversationResult, type MediaAttachment, type SendMessageOptions, type CreateConversationOptions, type JoinVoiceSessionOptions, type RegistrationStatus, type SessionSetupPolicy, type SetRuntimeTurnOptions, type SetStreamingOptions, type UpdateAgentProfileOptions } from './types.js'; import type { CanonGifSearchOptions, CanonGifSearchResponse } from './gifs.js'; import type { RuntimeInputKind } from './runtime-cards.js'; import type { ApprovalNativeRequestMetadata, ApprovalRequestCategory, ApprovalRequestDetail, ApprovalRisk, SessionRule } from './approval-types.js'; import type { RuntimeInputAnswers, RuntimeInputChoice, RuntimeInputNativeMetadata, RuntimeInputQuestion, RuntimeCardNativeMetadata, RuntimeCardV1 } from './runtime-cards.js'; import type { SendContextualMessageOptions, SendContextualMessageResult } from './self-context.js'; import type { InboundDisposition } from './turn-protocol.js'; import { type CanonVerbWireRequest, type CanonVerbWireResponse, type SendToInput, type SendToResult } from '@canonmsg/backend-contracts'; export interface ResumableMediaUploadSession { uploadId: string; /** Private bearer capability returned by Cloud Storage. Do not persist or log it. */ uploadUrl: string; expiresAt: string; sizeBytes: number; mimeType: string; } export interface ResumableMediaUploadResult { uploadId: string; url: string; status?: 'processing' | 'ready' | 'failed'; attachment: MediaAttachment; } /** * Thin REST client for Canon's agent API. * Uses native fetch — no runtime dependencies. */ export declare class CanonClient { private baseUrl; private apiKey; constructor(apiKey: string, baseUrl?: string); private authHeaders; getAuthToken(): Promise<{ token: string; expiresAt: string; agentId: string; }>; getAgentMe(): Promise; updateAgentProfile(options: UpdateAgentProfileOptions): Promise; rotateAgentKey(): Promise<{ apiKey: string; }>; /** * List conversations. Called with no options this is the historical request — * no query string, every conversation the agent belongs to, most recent * first. Passing `limit`/`before` opts into paging; use * `getConversationsPage` when you need the cursor back. */ getConversations(options?: CanonConversationsPageOptions): Promise; /** * One page of conversations plus the cursor to continue from. Pages are cut * in conversation-id order; keep calling with `before: page.nextBefore` until * `nextBefore` is null. Do not stop on an empty page and do not derive the * cursor from the returned array — the array is recency-sorted within the * page and hidden conversations are filtered after the page is read. */ getConversationsPage(options?: CanonConversationsPageOptions): Promise; private fetchConversationsPage; getMessages(conversationId: string, limit?: number, before?: string): Promise; getMessagesPage(conversationId: string, limit?: number, before?: string): Promise; sendMessage(conversationId: string, text: string, options?: SendMessageOptions): Promise<{ messageId: string; }>; /** * Execute a canonical verb over the wire contract (canon.verb-wire.v1). * Bindings build the request with projectVerbIntentToWire; the endpoint * validates the envelope, executes with the json codec, and answers 501 * for mls bodies until the codec lands (docs/design/e2ee-mls.md). */ executeVerbWire(request: CanonVerbWireRequest): Promise; /** * Execute Canon's canonical admission-aware send operation. * * `canonContactId` is a public contact-card address rather than a wire field, * so resolve it once before projecting the intent. All admission decisions, * contact-request creation, and deferred first-message delivery remain * server-owned behind `/agent/verbs/send_to`. */ sendTo(options: SendToInput): Promise; sendContextualMessage(options: SendContextualMessageOptions): Promise; createConversation(options: CreateConversationOptions): Promise; createMemoStream(options: CreateMemoStreamOptions): Promise; appendMemoStreamChunk(conversationId: string, streamId: string, options: AppendMemoStreamChunkOptions): Promise; finalizeMemoStream(conversationId: string, streamId: string, messageId: string): Promise; abortMemoStream(conversationId: string, streamId: string): Promise; createVoiceSession(options: CreateVoiceSessionOptions): Promise; getVoiceSessionState(conversationId: string, sessionId: string): Promise<{ session: CanonVoiceSession; }>; declineVoiceSession(conversationId: string, sessionId: string): Promise; joinVoiceSession(conversationId: string, sessionId: string, options?: Omit): Promise; endVoiceSession(conversationId: string, sessionId: string): Promise; createContactRequest(targetUserId: string, message?: string | null): Promise; private contactRequestListQuery; listContactRequestLifecyclePage(options: ContactRequestLifecyclePageOptions): Promise; listContactRequests(options?: ContactRequestListOptions): Promise; cancelContactRequest(requestId: string): Promise<{ status: 'cancelled'; requestId: string; }>; listContacts(): Promise; getContact(contactId: string): Promise; deleteContact(contactId: string): Promise; blockUser(userId: string): Promise; unblockUser(userId: string): Promise; resolveAdmission(target: string | ResolveAdmissionTargetInput): Promise; uploadMedia(conversationId: string, data: string, mimeType: string, fileName?: string): Promise<{ url: string; attachment: MediaAttachment; }>; /** * Start a private direct-to-Storage upload. The returned upload URL is a * short-lived bearer capability; send exactly `sizeBytes` raw bytes to it, * then call `finalizeResumableMediaUpload`. */ createResumableMediaUpload(conversationId: string, sizeBytes: number, mimeType: string, fileName?: string): Promise; /** Promote validated pending bytes into a canonical Canon attachment. */ finalizeResumableMediaUpload(uploadId: string): Promise; getFeaturedGifs(options?: Omit): Promise; searchGifs(query: string, options?: Omit): Promise; updateTopic(conversationId: string, topic: string): Promise; deleteMessage(conversationId: string, messageId: string): Promise; updateMessageDisposition(conversationId: string, messageId: string, inboundDisposition: InboundDisposition): Promise; markAsRead(conversationId: string): Promise; leaveConversation(conversationId: string): Promise; react(conversationId: string, messageId: string, emoji: string): Promise; updateConversationName(conversationId: string, name: string): Promise; addMember(conversationId: string, userId: string): Promise; removeMember(conversationId: string, userId: string): Promise; setStreaming(options: SetStreamingOptions): Promise; clearStreaming(conversationId: string): Promise; setRuntimeTurn(options: SetRuntimeTurnOptions): Promise; setTyping(conversationId: string, typing: boolean, status?: 'thinking' | 'typing'): Promise; createRuntimeInputRequest(options: { conversationId: string; inputId: string; kind: RuntimeInputKind; expiresAt: number; title?: string; prompt?: string; choices?: RuntimeInputChoice[]; questions?: RuntimeInputQuestion[]; secretName?: string; native?: RuntimeInputNativeMetadata; sensitive?: boolean; responseUserId?: string; turnId?: string; }): Promise<{ success: true; inputId: string; expiresAt: number; messageId?: string; }>; consumeRuntimeInputResponse(options: { conversationId: string; inputId: string; cancel?: boolean; }): Promise<{ status: 'pending'; inputId: string; expiresAt?: number; } | { status: 'submitted'; inputId: string; kind: RuntimeInputKind; value: string; answers?: RuntimeInputAnswers; } | { status: 'cancelled'; inputId: string; kind: RuntimeInputKind; } | { status: 'timeout'; inputId: string; kind: RuntimeInputKind; }>; createRuntimeApprovalRequest(options: { conversationId: string; approvalId?: string; toolName: string; toolSummary: string; expiresAt: number; responseUserId?: string; riskLevel?: 'normal' | 'destructive'; risk?: ApprovalRisk; category?: ApprovalRequestCategory; runtimeId?: string; turnId?: string; native?: ApprovalNativeRequestMetadata; details?: ApprovalRequestDetail[]; /** File-change preview rendered inline on the approval card. Truncate via truncateUnifiedDiff before sending. */ diff?: CanonUnifiedDiff; allowSessionRule?: boolean; }): Promise<{ success: true; approvalId: string; expiresAt: number; messageId?: string; responseUserId: string; allowSessionRule: boolean; }>; consumeRuntimeApprovalResponse(options: { conversationId: string; approvalId: string; cancel?: boolean; }): Promise<{ status: 'pending'; approvalId: string; expiresAt?: number; } | { status: 'allow'; approvalId: string; sessionRule?: SessionRule; respondedBy?: string; } | { status: 'deny'; approvalId: string; sessionRule?: SessionRule; respondedBy?: string; } | { status: 'timeout'; approvalId: string; }>; createRuntimeCardRequest(options: { conversationId: string; card: RuntimeCardV1; cardId?: string; expiresAt?: number; responseUserId?: string; runtimeId?: string; turnId?: string; native?: RuntimeCardNativeMetadata; }): Promise<{ success: true; cardId: string; messageId: string; /** True when the card has actions and a response is pending; false for display cards. */ interactive: boolean; /** Effective human responder selected by Canon. */ responseUserId?: string; /** Present only for interactive cards. */ expiresAt?: number; }>; consumeRuntimeCardResponse(options: { conversationId: string; cardId: string; cancel?: boolean; }): Promise<{ status: 'pending'; cardId: string; expiresAt?: number; } | { status: 'submitted'; cardId: string; actionId?: string; values?: Record; respondedBy?: string; } | { status: 'cancelled'; cardId: string; respondedBy?: string; } | { status: 'timeout'; cardId: string; }>; /** * Create a server-owned plan-approval interaction. * * Seeds the pending node + attention + runtime-card state and authors the * `plan_approval` card message, mirroring the other `createRuntime*Request` * spine methods. Hosts resolve it through `consumeRuntimePlanResponse()` so * the canonical pending state, rather than a timeline receipt, is authoritative. */ createRuntimePlanRequest(options: { conversationId: string; planId?: string; title?: string; summary?: string; body?: string; allowedPrompts?: ReadonlyArray<{ tool: string; prompt: string; }>; responseUserId?: string; turnId?: string; expiresAt?: number; }): Promise<{ success: true; planId: string; expiresAt: number; messageId?: string; interactive: boolean; }>; consumeRuntimePlanResponse(options: { conversationId: string; planId: string; cancel?: boolean; }): Promise<{ status: 'pending'; planId: string; expiresAt?: number; } | { status: 'approve' | 'revise' | 'reject'; planId: string; feedback?: string; grantedPrompts?: Array<{ tool: string; prompt: string; }>; receiptId?: string; } | { status: 'cancelled'; planId: string; } | { status: 'timeout'; planId: string; }>; updateRuntimeStatus(options: { runtime: string; hostMode?: boolean; runtimeDescriptor?: Record; }): Promise; static register(baseUrl: string | undefined, body: { name: string; description: string; ownerPhone: string; developerInfo: string; avatarUrl?: string; clientType?: string; sessionSetupPolicy?: SessionSetupPolicy; requestedAgentId?: string; localRegistrationId?: string; }): Promise<{ requestId: string; pollToken?: string; }>; static checkStatus(baseUrl: string | undefined, requestId: string, pollToken?: string): Promise; static ackRegistrationStatus(baseUrl: string | undefined, requestId: string, pollToken?: string): Promise; } export declare class CanonApiError extends Error { status: number; retryAfter?: string | null; code?: string; constructor(status: number, body: string, headers?: Headers); }