import type { ConfigManager } from '../config/manager.js'; import type { SecretsManager } from '../config/secrets.js'; import type { ServiceRegistry } from '../config/service-registry.js'; import type { AgentRecord } from '../tools/agent/index.js'; import type { AgentManager } from '../tools/agent/index.js'; import type { ControlPlaneGateway, SharedSessionBroker } from '../control-plane/index.js'; import type { RouteBindingManager, ChannelPolicyManager } from '../channels/index.js'; import type { GenericWebhookAdapterContext, SurfaceAdapterContext } from '../adapters/index.js'; import type { AutomationManager } from '../automation/index.js'; import type { ChannelPolicyDecision, ChannelIngressPolicyInput } from '../channels/index.js'; import type { RuntimeEventBus } from '../runtime/events/index.js'; import type { CompanionChatManager } from '../companion/companion-chat-manager.js'; import { type ApprovalReplyBroker } from './approval-reply.js'; import type { WorkProposalStore } from '../agents/work-proposal-store.js'; interface DaemonSurfaceActionContext { readonly serviceRegistry: ServiceRegistry; readonly secretsManager: Pick; readonly configManager: ConfigManager; readonly routeBindings: RouteBindingManager; readonly sessionBroker: SharedSessionBroker; readonly channelPolicy: ChannelPolicyManager; readonly controlPlaneGateway: ControlPlaneGateway; readonly runtimeBus: RuntimeEventBus; readonly companionChatManager: CompanionChatManager | null; readonly automationManager: AutomationManager; readonly agentManager: AgentManager; readonly trySpawnAgent: (input: Parameters[0], logLabel?: string, sessionId?: string) => AgentRecord | Response; readonly queueSurfaceReplyFromBinding: (binding: import('../automation/routes.js').AutomationRouteBinding | undefined, input: { readonly agentId: string; readonly task: string; readonly agentTask?: string | undefined; readonly workflowChainId?: string | undefined; readonly sessionId?: string | undefined; }) => void; readonly queueWebhookReply: (input: { readonly agentId: string; readonly task: string; readonly sessionId?: string | undefined; readonly routeId?: string | undefined; readonly callbackUrl?: string | undefined; readonly callbackCorrelationId?: string | undefined; readonly callbackSignature?: import('./types.js').PendingSurfaceReply['callbackSignature'] | undefined; }) => void; readonly surfaceDeliveryEnabled: (surface: 'slack' | 'discord' | 'ntfy' | 'webhook' | 'homeassistant' | 'telegram' | 'google-chat' | 'signal' | 'whatsapp' | 'telephony' | 'imessage' | 'msteams' | 'bluebubbles' | 'mattermost' | 'matrix') => boolean; readonly signWebhookPayload: (body: string, secret: string) => string; readonly handleApprovalAction: (approvalId: string, action: 'claim' | 'approve' | 'deny' | 'cancel', req: Request) => Promise; /** * Shared pending-approval broker, the same machinery the TUI and webui * resolve asks through. Lets a paired channel owner approve, deny, or * steer a pending ask by replying in the channel. */ readonly approvalBroker?: ApprovalReplyBroker | undefined; readonly resolveDefaultProviderModel?: (() => { provider: string; model: string; } | null) | undefined; /** * Pending work proposals for the conversation-first gate. Absent = the gate * is not installed and inbound messages spawn as they always did, which is * what isolated contexts and older embedders get. */ readonly workProposals?: WorkProposalStore | undefined; /** * Put one short line on the channel a binding points at, and say whether it * got there. The outcome is discriminated rather than boolean so a caller * can log WHICH guard refused, see SurfaceNoticeRefusal. */ readonly deliverSurfaceNotice?: ((binding: import('../automation/routes.js').AutomationRouteBinding | undefined, text: string) => Promise) | undefined; /** * The per-surface ingress alarm, handed to adapters so the two DETACHED * inbound paths (Slack and Discord slash commands, which answer the provider * before doing the work) can report a failure the shared webhook seam in * `ChannelPluginRegistry` cannot see. */ readonly ingressAlarm?: import('../channels/ingress-alarm.js').ChannelIngressAlarm | undefined; } export declare class DaemonSurfaceActionHelper { private readonly context; private static readonly NTFY_CHAT_REPLY_TTL_MS; private readonly pendingNtfyChatReplies; private ntfyChatReplyUnsubscribers; private ntfyRemoteSessionId; constructor(context: DaemonSurfaceActionContext); buildSurfaceAdapterContext(): SurfaceAdapterContext; buildGenericWebhookAdapterContext(): GenericWebhookAdapterContext; /** The slice of this helper's context the conversation gate consults. Public * so the shared-session continuation runner gates through the SAME deps. */ conversationGateDeps(): { configManager: ConfigManager; routeBindings: RouteBindingManager; sessionBroker: SharedSessionBroker; trySpawnAgent: (input: Parameters[0], logLabel?: string, sessionId?: string) => AgentRecord | Response; queueSurfaceReplyFromBinding: (binding: import("../automation/routes.js").AutomationRouteBinding | undefined, input: { readonly agentId: string; readonly task: string; readonly agentTask?: string | undefined; readonly workflowChainId?: string | undefined; readonly sessionId?: string | undefined; }) => void; workProposals: WorkProposalStore | undefined; deliverSurfaceNotice: ((binding: import("../automation/routes.js").AutomationRouteBinding | undefined, text: string) => Promise) | undefined; }; authorizeSurfaceIngress(input: ChannelIngressPolicyInput): Promise; parseSurfaceControlCommand(text: string): { readonly action: 'status' | 'cancel' | 'retry'; readonly id: string; } | null; performSurfaceControlCommand(command: { readonly action: 'status' | 'cancel' | 'retry'; readonly id: string; }): Promise; performInteractiveSurfaceAction(actionId: string, surface: 'slack' | 'discord', req: Request): Promise; private publishConversationFollowup; private queueNtfyChatReply; private ensureNtfyChatReplyListeners; private matchNtfyChatReplyTurn; private readNtfyOriginMessageId; private deliverNtfyChatReply; private findPendingNtfyChatReplyForPrompt; private findPendingNtfyChatReplyForMessageId; private takeNtfyChatReply; private cleanupExpiredNtfyChatReplies; private postNtfyRemoteChatMessage; private runNtfyRemoteChatTurn; /** * Run an inbound Home Assistant message as a chat turn and publish the reply. * The work lives in surface-homeassistant-reply.ts; this stays so the adapter * context keeps the same shape. */ private postHomeAssistantChatMessage; private publishNtfyReply; private resolveNtfyToken; } export {}; //# sourceMappingURL=surface-actions.d.ts.map