import { type AgentClientType, type AgentRuntime, type CanonConversation, type CanonGroupContext, type CanonGroupContextMode, type CanonMembershipChange, type CanonMessage, type CanonMessagesPage, type CanonRuntimeProvenance, type MessageCreatedPayload } from './types.js'; import { type CanonClient } from './client.js'; import { type SessionWorkspaceConfig } from './execution-environment.js'; import { type ResolvedAgentBehaviorPolicy } from './policy.js'; import type { RTDBClientHandle } from './rtdb-rest.js'; import type { CanonInboundPromptOptions } from './inbound-frame.js'; import type { HostAdmissionActionCapabilities } from './turn-protocol.js'; interface HostWorkspaceResolverOption { id: string; cwd: string; } export interface HostInboundParticipantContext { conversationType: CanonConversation['type'] | 'unknown'; memberCount: number | null; senderType: 'human' | 'ai_agent'; senderName: string; isOwner: boolean; mentionedAgent: boolean; groupContext?: CanonGroupContext; groupContextMode?: CanonGroupContextMode; recentSenderTypes: Array<'human' | 'ai_agent'>; recentHumanCount: number; recentAgentCount: number; consecutiveAgentTurns: number; currentAgentStreakStartedByHuman: boolean; } export interface AutoReplyDecision { allow: boolean; reason: string; } /** * Decide whether a coding host should auto-reply to an inbound message. * Thin adapter over {@link evaluateParticipationPolicy}; lives here (not in * @canonmsg/coding-agent-host) because it needs core's participation-policy * engine and the context type above, and coding-agent-host is deliberately * dependency-free. */ export declare function decideAutoReply(context: HostInboundParticipantContext, behavior?: ResolvedAgentBehaviorPolicy | null): AutoReplyDecision; type HostInboundMessage = { id?: string | null; senderId?: string | null; senderName?: string | null; text?: string | null; contentType?: CanonMessage['contentType'] | null; attachments?: CanonMessage['attachments']; senderType?: CanonMessage['senderType']; mentions?: string[] | null; contactCard?: CanonMessage['contactCard']; replyTo?: string | null; replyToPosition?: number | null; deleted?: boolean | null; }; export interface CanonReplyContext { messageId: string; senderId?: string | null; senderName?: string | null; senderType?: CanonMessage['senderType'] | null; text?: string | null; contentType?: CanonMessage['contentType'] | null; attachments?: CanonMessage['attachments']; contactCard?: CanonMessage['contactCard']; body: string; replyToPosition?: number | null; found: boolean; } export declare const CANON_TURN_CONTEXT_SCHEMA_V2: "canon.turn.v2"; export interface CanonTurnContextAttachmentV2 { index: number; kind: NonNullable[number]['kind']; placeholder: string; fileName: string | null; mimeType: string | null; sizeBytes: number | null; width: number | null; height: number | null; durationMs: number | null; } export interface CanonTurnContextContactCardV2 { userId: string; canonContactId: string | null; displayName: string; userType: NonNullable['userType']; ownerName: string | null; about: string | null; } export interface CanonTurnContextReplyContextV2 { messageId: string; found: boolean; senderId: string | null; senderName: string | null; senderType: CanonMessage['senderType'] | null; contentType: CanonMessage['contentType'] | null; text: string | null; body: string; replyToPosition: number | null; attachments: CanonTurnContextAttachmentV2[]; contactCard: CanonTurnContextContactCardV2 | null; } export interface CanonTurnContextProvenanceV2 { sender: { id: string | null; name: string | null; type: HostInboundParticipantContext['senderType']; isOwner: boolean; }; mentionedAgent: boolean; activeSelfContext: CanonRuntimeProvenance['activeSelfContext']; } export interface CanonTurnContextBehaviorV2 { participationStyle: ResolvedAgentBehaviorPolicy['participation']['style']; requireMentionForGroupReplies?: boolean; allowAgentToAgent?: boolean; allowLongRunningCollaboration?: boolean; maxConsecutiveAgentTurns?: number; instructions?: string[]; source?: ResolvedAgentBehaviorPolicy['source']; } export interface CanonTurnContextParticipationV2 { recentRoleSummary?: HostInboundParticipantContext['recentSenderTypes']; recentHumanCount?: number; recentAgentCount?: number; consecutiveAgentTurns?: number; currentAgentStreakStartedByHuman?: boolean; } export interface CanonTurnContextV2 { schema: typeof CANON_TURN_CONTEXT_SCHEMA_V2; conversation: { id: string; type: HostInboundParticipantContext['conversationType']; memberCount: number | null; }; provenance: CanonTurnContextProvenanceV2; behavior?: CanonTurnContextBehaviorV2; participation?: CanonTurnContextParticipationV2; group?: { context: CanonGroupContext; mode?: CanonGroupContextMode; }; selfContext?: { activeSelfContextId: string | null; contexts: NonNullable; }; replyContext?: CanonTurnContextReplyContextV2; session?: { lines: string[]; }; message: { id: string | null; contentType: CanonMessage['contentType'] | null; renderedContent: string; attachments?: CanonTurnContextAttachmentV2[]; contactCard?: CanonTurnContextContactCardV2; mentions?: string[]; replyTo?: string; replyToPosition?: number; deleted?: true; }; } export interface HostInboundContentRenderOptions { /** Descriptor-derived actions available to the model in this exact turn. */ admissionActions?: HostAdmissionActionCapabilities; /** Model-visible name for the reach-out projection, when one is registered. */ reachOutToolName?: string; } export declare function buildCanonTurnContextV2(input: { content: string; conversationId: string; participantContext: HostInboundParticipantContext; behavior?: ResolvedAgentBehaviorPolicy | null; selfContexts?: MessageCreatedPayload['selfContexts']; activeSelfContextId?: string | null; provenance: CanonRuntimeProvenance; replyContext?: CanonReplyContext | null; sessionContextLines?: string[]; message?: HostInboundMessage | null; }): CanonTurnContextV2; export declare function renderCanonTurnBriefPrompt(context: CanonTurnContextV2, options?: CanonInboundPromptOptions): string; export declare function buildCanonReplyContextLines(replyContext: CanonReplyContext | null): string[]; /** * Render the **text portion** of an inbound Canon message. Images are * referenced by short placeholders — their actual bytes are delivered to the * host as native vision/media inputs (Codex `-i `, Anthropic image * blocks). URLs are intentionally *not* inlined, since the harness never * needs to refetch and earlier `[Image: ]` inlining caused vision * models to see a string about an image instead of the image itself. * * `materialized` may be passed so non-image attachments can reference a * local path the agent can Read. Without it we fall back to an unadorned * placeholder; the vision path still works because image args carry the * file path directly. */ export declare function renderCanonHostInboundContent(message: HostInboundMessage, materialized?: ReadonlyArray<{ kind: 'image' | 'audio' | 'video' | 'file'; path: string; fileName?: string; durationMs?: number; index: number; }>, options?: HostInboundContentRenderOptions): string; export declare function resolveCanonReplyContext(input: { message: HostInboundMessage; messages?: ReadonlyArray | null; renderOptions?: HostInboundContentRenderOptions; }): CanonReplyContext | null; export declare function buildHydratedInboundContext(input: { agentId: string; conversationId: string; conversation: CanonConversation | null; page?: CanonMessagesPage | null; activeSelfContextId?: string | null; selfContexts?: MessageCreatedPayload['selfContexts']; provenance?: MessageCreatedPayload['provenance']; message: HostInboundMessage; senderName: string; isOwner: boolean; ownerId?: string | null; ownerName?: string | null; membershipChange?: CanonMembershipChange | null; groupContextMode?: CanonGroupContextMode; renderOptions?: HostInboundContentRenderOptions; }): { participantContext: HostInboundParticipantContext; behavior?: ResolvedAgentBehaviorPolicy | null; activeSelfContextId: string | null; selfContexts: NonNullable; provenance: CanonRuntimeProvenance; replyContext: CanonReplyContext | null; hydratedFromPage: boolean; }; export declare function publishHostAgentRuntime(agentId: string, clientType: AgentClientType, runtime: AgentRuntime, rtdb: RTDBClientHandle): Promise; export declare function publishHostSessionSnapshots(input: { conversationIds: string[]; agentId: string; rtdb: RTDBClientHandle; clientType: AgentClientType; runtime: AgentRuntime; workspaceOptions: HostWorkspaceResolverOption[]; defaultCwd: string; extraSessionConfigFields?: readonly string[]; liveSessionConfigByConversation?: ReadonlyMap; workspaceId?: string; executionMode?: SessionWorkspaceConfig['executionMode']; executionBranch?: string | null; }>; }): Promise; export declare function readHostSessionConfig(raw: unknown, extraStringFields?: readonly TExtra[]): (SessionWorkspaceConfig & Partial> & { runtimeControlValues?: Record; }) | null; export declare function loadHostSessionConfig(input: { conversationId: string; agentId: string; rtdb: RTDBClientHandle; extraStringFields?: readonly TExtra[]; /** Briefly bridge Firestore membership -> RTDB mirror propagation for new DMs. */ retryMissingMs?: number; }): Promise<(SessionWorkspaceConfig & Partial> & { runtimeControlValues?: Record; }) | null>; export declare function resolveHostWorkspaceCwd(input: { workspaceOptions: HostWorkspaceResolverOption[]; config: { workspaceId?: string; retiredWorkspaceConfig?: boolean; } | null; defaultCwd: string; }): string; export declare function createConversationMetadataLoader(input: { client: CanonClient; conversationCache: Map; cacheTtlMs?: number; }): { refreshConversationCache(force?: boolean): Promise; getConversationMeta(conversationId: string): Promise; }; export {};