import React, { createContext, useContext, type ReactNode } from 'react'; import type { useSuperagentRuntime } from './useSuperagentRuntime'; import type { SuperagentAgent, SuperagentCreateAgentInput, SuperagentMediaPicker, SuperagentMessage, SuperagentRoute, } from '../types'; /** * Runtime state + handlers shared across the Superagent screen tree via React * context, so components read what they need through domain hooks * (`useSuperagentChannels`, `useSuperagentConnectors`, …) instead of receiving * dozens of drilled props. Mirrors the web agent-editor's store-hook consumption * model; we use a context-backed value rather than a module singleton so the two * mounted screens (home tab + pushed conversation) each keep their own isolated * runtime. Genuine identity/UI props (`agent`, `isVisible`, refs) stay props — * only shared runtime state flows through here, matching the web boundary. */ type RuntimeReturn = ReturnType; /** * The full context value: the runtime hook's return, minus the navigation/route * fields the shell overrides with host-aware versions, plus the shell-owned * extras (host navigation, media pickers, plans CTA, debug flag, insets). */ export type SuperagentRuntimeContextValue = Omit< RuntimeReturn, | 'onOpenAgent' | 'onRouteChange' | 'initialRoute' | 'currentRoute' | 'latestMessages' | 'messagesByAgentId' | 'onCreateAgent' | 'onRenameAgent' | 'onCloneAgent' | 'onDeleteAgent' | 'onShareAgentLink' > & { // Overridden with real element types — the runtime returns empty `[]` / `{}` // literals, which infer as `never[]` / `{}` and can't be indexed/mapped. latestMessages: SuperagentMessage[]; messagesByAgentId: Record; // The runtime impl ignores its arg; widen to the seeded-create contract callers use. onCreateAgent: (input?: SuperagentCreateAgentInput) => Promise | SuperagentAgent | void; // Optional so overflow-menu / settings actions can gate their affordance on // handler presence (the runtime always provides them, but consumers must not assume it). onRenameAgent?: RuntimeReturn['onRenameAgent']; onCloneAgent?: RuntimeReturn['onCloneAgent']; onDeleteAgent?: RuntimeReturn['onDeleteAgent']; onShareAgentLink?: RuntimeReturn['onShareAgentLink']; navigationMode?: 'internal' | 'external'; initialRoute?: SuperagentRoute; onOpenAgent?: (agentId: string) => void; onAgentBack?: () => void; onRouteChange?: (route: SuperagentRoute) => void; onViewPlans?: () => void; showDebugPayloads?: boolean; isActive?: boolean; contentTopInset?: number; hideConversationHeader?: boolean; onPickFiles?: SuperagentMediaPicker; onPickPhotos?: SuperagentMediaPicker; onTakePhoto?: SuperagentMediaPicker; }; const SuperagentRuntimeContext = createContext(null); export function SuperagentRuntimeProvider({ value, children, }: { value: SuperagentRuntimeContextValue; children: ReactNode; }) { return {children}; } /** * Base accessor for the whole runtime value. Throws when used outside the * provider (i.e. outside a `SuperagentHomeScreen`) so a missing provider surfaces * immediately rather than as confusing `undefined` reads. Prefer the domain hooks * below; reach for this only when a component genuinely spans several domains. */ export function useSuperagentRuntimeContext(): SuperagentRuntimeContextValue { const value = useContext(SuperagentRuntimeContext); if (!value) { throw new Error('Superagent runtime hooks must be used within a .'); } return value; } /** Agents list + agent-lifecycle actions (home list, overflow menu). */ export function useSuperagentAgents() { const ctx = useSuperagentRuntimeContext(); return { agents: ctx.agents, activeAgentId: ctx.activeAgentId, activeAgent: ctx.activeAgent, isLoading: ctx.isLoading, isRefreshingAgents: ctx.isRefreshingAgents, loadError: ctx.loadError, onRefreshAgents: ctx.onRefreshAgents, onCreateAgent: ctx.onCreateAgent, onRenameAgent: ctx.onRenameAgent, onCloneAgent: ctx.onCloneAgent, onDeleteAgent: ctx.onDeleteAgent, onShareAgentLink: ctx.onShareAgentLink, }; } /** App-shell navigation wiring (routing between home and agent screens). */ export function useSuperagentNavigation() { const ctx = useSuperagentRuntimeContext(); return { navigationMode: ctx.navigationMode, initialRoute: ctx.initialRoute, onOpenAgent: ctx.onOpenAgent, onAgentBack: ctx.onAgentBack, onRouteChange: ctx.onRouteChange, }; } /** Conversation-runtime dependencies (clients, current user, message seeds). */ export function useSuperagentConversationRuntime() { const ctx = useSuperagentRuntimeContext(); return { apiClient: ctx.apiClient, realtimeClient: ctx.realtimeClient, currentUserId: ctx.currentUserId, currentUserAvatarUrl: ctx.currentUserAvatarUrl, currentUserName: ctx.currentUserName, latestMessages: ctx.latestMessages, messagesByAgentId: ctx.messagesByAgentId, onAgentMessageDone: ctx.onAgentMessageDone, copyToClipboard: ctx.copyToClipboard, }; } /** OAuth connectors: catalog, connection state, and connect/disconnect actions. */ export function useSuperagentConnectors() { const ctx = useSuperagentRuntimeContext(); return { availableConnectors: ctx.availableConnectors, connectedConnectors: ctx.connectedConnectors, connectingConnectorId: ctx.connectingConnectorId, isLoadingConnectors: ctx.isLoadingConnectors, onConnectConnector: ctx.onConnectConnector, onDisconnectConnector: ctx.onDisconnectConnector, onRemoveConnector: ctx.onRemoveConnector, onCancelConnectorConnection: ctx.onCancelConnectorConnection, }; } /** Messaging channels (WhatsApp, Telegram, LINE, iMessage, Slack). */ export function useSuperagentChannels() { const ctx = useSuperagentRuntimeContext(); return { channelStatus: ctx.channelStatus, connectingChannelId: ctx.connectingChannelId, disconnectingChannelId: ctx.disconnectingChannelId, isLoadingChannels: ctx.isLoadingChannels, onConnectSlack: ctx.onConnectSlack, onDisconnectSlack: ctx.onDisconnectSlack, onDisconnectTelegram: ctx.onDisconnectTelegram, onDisconnectIMessage: ctx.onDisconnectIMessage, onDisconnectWhatsApp: ctx.onDisconnectWhatsApp, onGenerateLineCode: ctx.onGenerateLineCode, onGenerateIMessageCode: ctx.onGenerateIMessageCode, onOpenIMessage: ctx.onOpenIMessage, onOpenLine: ctx.onOpenLine, onOpenTelegram: ctx.onOpenTelegram, onOpenWhatsApp: ctx.onOpenWhatsApp, onSetupTelegram: ctx.onSetupTelegram, onShareIMessageCode: ctx.onShareIMessageCode, onShareLineCode: ctx.onShareLineCode, onRefreshChannels: ctx.onRefreshChannels, }; } /** Scheduled/triggered automations ("Tasks") + their credit usage. */ export function useSuperagentAutomations() { const ctx = useSuperagentRuntimeContext(); return { automations: ctx.automations, automationCredits: ctx.automationCredits, automationLoadError: ctx.automationLoadError, isLoadingAutomations: ctx.isLoadingAutomations, onToggleAutomation: ctx.onToggleAutomation, onArchiveAutomation: ctx.onArchiveAutomation, onRestoreAutomation: ctx.onRestoreAutomation, onDeleteAutomation: ctx.onDeleteAutomation, onRunAutomationNow: ctx.onRunAutomationNow, onEditAutomation: ctx.onEditAutomation, onRefreshAutomations: ctx.onRefreshAutomations, }; } /** * Workflows ("Tasks") for a workflows-enabled agent + their lifecycle actions. * An agent runs EITHER workflows OR automations — the Tasks panel picks the * surface via `agent.workflowsEnabled` and reads the matching domain hook. */ export function useSuperagentWorkflows() { const ctx = useSuperagentRuntimeContext(); return { workflows: ctx.workflows, workflowLoadError: ctx.workflowLoadError, isLoadingWorkflows: ctx.isLoadingWorkflows, onToggleWorkflow: ctx.onToggleWorkflow, onArchiveWorkflow: ctx.onArchiveWorkflow, onRestoreWorkflow: ctx.onRestoreWorkflow, onRunWorkflowNow: ctx.onRunWorkflowNow, onRefreshWorkflows: ctx.onRefreshWorkflows, }; } /** Sandbox files browser. */ export function useSuperagentFiles() { const ctx = useSuperagentRuntimeContext(); return { filePaths: ctx.filePaths, fileLoadError: ctx.fileLoadError, fileLoadFailed: ctx.fileLoadFailed, isLoadingFiles: ctx.isLoadingFiles, onOpenSandboxFile: ctx.onOpenSandboxFile, onSaveSandboxFile: ctx.onSaveSandboxFile, onUploadSandboxFiles: ctx.onUploadSandboxFiles, onRefreshFiles: ctx.onRefreshFiles, }; } /** Agent secrets (env vars) + the agent-settings load flag. */ export function useSuperagentSecrets() { const ctx = useSuperagentRuntimeContext(); return { secrets: ctx.secrets, isLoadingAgentSettings: ctx.isLoadingAgentSettings, onSaveSecret: ctx.onSaveSecret, onDeleteSecret: ctx.onDeleteSecret, onRefreshAgentSettings: ctx.onRefreshAgentSettings, }; } /** Collaborators / sharing (invite, refresh, open workspace members). */ export function useSuperagentCollaborators() { const ctx = useSuperagentRuntimeContext(); return { collaborators: ctx.collaborators, isLoadingCollaborators: ctx.isLoadingCollaborators, onShareAgent: ctx.onShareAgent, onRefreshCollaborators: ctx.onRefreshCollaborators, onOpenWorkspaceMembers: ctx.onOpenWorkspaceMembers, }; } /** Model + tool-permission mutations (chat model, automation model, guards). */ export function useSuperagentModelActions() { const ctx = useSuperagentRuntimeContext(); return { onUpdateAgentModel: ctx.onUpdateAgentModel, onUpdateAgentAutomationModel: ctx.onUpdateAgentAutomationModel, onUpdateToolPermissions: ctx.onUpdateToolPermissions, }; } /** Native media capture/pick + live voice for the composer. */ export function useSuperagentMedia() { const ctx = useSuperagentRuntimeContext(); return { onPickFiles: ctx.onPickFiles, onPickPhotos: ctx.onPickPhotos, onTakePhoto: ctx.onTakePhoto, onStartLiveVoice: ctx.onStartLiveVoice, speechToText: ctx.speechToText, }; } /** Cross-cutting shell options (plans CTA, debug payloads, content inset). */ export function useSuperagentShellOptions() { const ctx = useSuperagentRuntimeContext(); return { onViewPlans: ctx.onViewPlans, showDebugPayloads: ctx.showDebugPayloads, isActive: ctx.isActive, contentTopInset: ctx.contentTopInset, hideConversationHeader: ctx.hideConversationHeader, }; }