@ateam-ai-mobile/plugin-runtime 0.1.5 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-RXR6M623.mjs +3085 -0
- package/dist/chunk-RXR6M623.mjs.map +1 -0
- package/dist/index.d.mts +2083 -12
- package/dist/index.d.ts +2083 -12
- package/dist/index.js +2605 -1332
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +265 -2157
- package/dist/index.mjs.map +1 -1
- package/dist/loader-UUOPZQSI.mjs +9 -0
- package/dist/loader-UUOPZQSI.mjs.map +1 -0
- package/package.json +2 -2
- package/src/bundles/bundleManager.ts +496 -51
- package/src/bundles/integrity.ts +29 -0
- package/src/bundles/storage.ts +59 -0
- package/src/deviceStateHost.ts +222 -0
- package/src/hooks/usePlugin.ts +14 -4
- package/src/host.ts +82 -8
- package/src/index.ts +96 -2
- package/src/pluginRuntime.ts +108 -0
- package/src/sdk/CameraPreview.tsx +94 -0
- package/src/sdk/device-state.ts +1315 -0
- package/src/sdk/index.ts +42 -3
- package/src/sdk/registration.ts +40 -0
- package/src/sdk/types.ts +489 -8
- package/src/sdk/voice.ts +186 -0
- package/src/tools/loader.ts +185 -0
- package/src/tools/platformTools.ts +41 -0
- package/src/tools/registry.ts +190 -0
- package/src/tools/serverCalls.ts +78 -0
- package/src/tools/sessionSync.ts +107 -0
- package/src/voiceHost.ts +128 -0
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sdk/index.ts","../src/sdk/registration.ts","../src/sdk/primitives.ts","../src/sdk/components.ts","../src/sdk/usePluginTheme.ts","../src/sdk/useApi.ts","../src/host.ts","../src/bundles/bundleManager.ts","../src/surfaces/SurfaceDrawer.tsx","../src/surfaces/AdaOrb.tsx","../src/surfaces/SurfaceCard.tsx","../src/surfaces/SurfaceFullscreen.tsx","../src/registry/store.ts","../src/registry/SurfaceDispatcher.tsx","../src/utils/sha256.ts","../src/bundles/storage.ts","../src/bridge/native.ts","../src/hooks/usePlugin.ts","../src/hooks/useRemotePlugins.ts","../src/api/plugins.ts"],"sourcesContent":["/**\n * @adas/plugin-sdk namespace — everything a PLUGIN author imports.\n * Remote bundles resolve `require('@adas/plugin-sdk')` to exactly this\n * module via the bundle manager's require shim.\n */\n\n// Registration API\nexport { register, registerService } from './registration';\nimport { register, registerService } from './registration';\nexport const PluginSDK = { register, registerService } as const;\n\n// Types\nexport type {\n PluginBridge,\n PluginNative,\n PluginContext,\n PluginProps,\n ThemeTokens,\n HapticsAPI,\n CameraAPI,\n LocationAPI,\n StorageAPI,\n NotificationsAPI,\n ConnectivityAPI,\n FileSystemAPI,\n CookiesAPI,\n SharingAPI,\n ScannerAPI,\n NfcAPI,\n BiometricsAPI,\n BiometricsAPIv2,\n NativeCapabilities,\n PluginHooks,\n PluginManifest,\n RenderMode,\n RegisteredPlugin,\n RegisteredUIPlugin,\n RegisteredServicePlugin,\n} from './types';\n\n// RN primitives (plugins import from the SDK, not from react-native)\nexport {\n View,\n Text,\n ScrollView,\n FlatList,\n SectionList,\n Pressable,\n TouchableOpacity,\n TextInput,\n Image,\n ActivityIndicator,\n Modal,\n Switch,\n StyleSheet,\n Animated,\n Platform,\n RefreshControl,\n} from './primitives';\n\n// Pre-built components\nexport {\n Card,\n Badge,\n TabBar,\n ListItem,\n SearchBar,\n EmptyState,\n ErrorState,\n LoadingState,\n ActionButton,\n} from './components';\n\n// Theme + API hooks\nexport { usePluginTheme, setPluginThemeColors } from './usePluginTheme';\nexport { useApi } from './useApi';\n","/**\n * Plugin registration API.\n * Solution developers use these to register their plugins.\n */\nimport type {\n NativeCapabilities,\n PluginHooks,\n PluginProps,\n RegisteredUIPlugin,\n RegisteredServicePlugin,\n} from './types';\n\n/**\n * Register a UI or hybrid plugin (has a visual component).\n */\nexport function register(\n id: string,\n config: {\n type?: 'ui' | 'hybrid';\n capabilities?: NativeCapabilities;\n Component: React.FC<PluginProps>;\n },\n): RegisteredUIPlugin {\n return {\n id,\n type: config.type || 'ui',\n capabilities: config.capabilities,\n Component: config.Component,\n };\n}\n\n/**\n * Register a headless service plugin (no UI).\n */\nexport function registerService(\n id: string,\n config: {\n capabilities?: NativeCapabilities;\n hooks?: PluginHooks;\n onStart: (props: Omit<PluginProps, 'theme'>) => Promise<void> | void;\n onStop?: () => Promise<void> | void;\n },\n): RegisteredServicePlugin {\n return {\n id,\n type: 'service',\n capabilities: config.capabilities,\n hooks: config.hooks,\n onStart: config.onStart,\n onStop: config.onStop,\n };\n}\n","/**\n * Re-exported React Native primitives.\n * Plugins import these from '@adas/plugin-sdk' — never from 'react-native' directly.\n */\nexport {\n View,\n Text,\n ScrollView,\n FlatList,\n SectionList,\n Pressable,\n TouchableOpacity,\n TextInput,\n Image,\n ActivityIndicator,\n Modal,\n Switch,\n StyleSheet,\n Animated,\n Platform,\n RefreshControl,\n} from 'react-native';\n","/**\n * Pre-built UI components for native plugins.\n * These provide consistent styling using ThemeTokens.\n */\nimport React from 'react';\nimport {\n View,\n Text,\n Pressable,\n TextInput,\n ActivityIndicator,\n StyleSheet,\n} from 'react-native';\nimport type { ThemeTokens } from './types';\n\n// ── Card ────────────────────────────────────────────────────\n\ninterface CardProps {\n theme: ThemeTokens;\n children: React.ReactNode;\n style?: any;\n onPress?: () => void;\n}\n\nexport function Card({ theme, children, style, onPress }: CardProps) {\n const cardStyle = [\n styles.card,\n {\n backgroundColor: theme.colors.surface,\n borderColor: theme.colors.border,\n },\n style,\n ];\n\n if (onPress) {\n return React.createElement(Pressable, { style: cardStyle, onPress }, children);\n }\n return React.createElement(View, { style: cardStyle }, children);\n}\n\n// ── Badge ───────────────────────────────────────────────────\n\ninterface BadgeProps {\n theme: ThemeTokens;\n label: string;\n color?: 'accent' | 'success' | 'warning' | 'error' | 'purple';\n style?: any;\n}\n\nconst BADGE_COLORS = {\n accent: { bg: 'accentSoft', text: 'accent' },\n success: { bg: 'successSoft', text: 'success' },\n warning: { bg: 'warningSoft', text: 'warning' },\n error: { bg: 'errorSoft', text: 'error' },\n purple: { bg: 'purpleSoft', text: 'purple' },\n} as const;\n\nexport function Badge({ theme, label, color = 'accent', style }: BadgeProps) {\n const mapping = BADGE_COLORS[color];\n return React.createElement(\n View,\n {\n style: [\n styles.badge,\n {\n backgroundColor: theme.colors[mapping.bg],\n borderRadius: theme.borderRadius.sm,\n },\n style,\n ],\n },\n React.createElement(Text, {\n style: [styles.badgeText, { color: theme.colors[mapping.text] }],\n }, label),\n );\n}\n\n// ── TabBar ──────────────────────────────────────────────────\n\ninterface TabBarProps {\n theme: ThemeTokens;\n tabs: string[];\n activeTab: string;\n onTabChange: (tab: string) => void;\n style?: any;\n}\n\nexport function TabBar({ theme, tabs, activeTab, onTabChange, style }: TabBarProps) {\n return React.createElement(\n View,\n {\n style: [\n styles.tabBar,\n { borderBottomColor: theme.colors.border },\n style,\n ],\n },\n tabs.map((tab) =>\n React.createElement(\n Pressable,\n {\n key: tab,\n style: [\n styles.tab,\n {\n borderBottomColor: tab === activeTab ? theme.colors.accent : 'transparent',\n },\n ],\n onPress: () => onTabChange(tab),\n },\n React.createElement(Text, {\n style: {\n color: tab === activeTab ? theme.colors.accent : theme.colors.textSecondary,\n fontSize: theme.fontSize.sm,\n fontWeight: tab === activeTab ? '600' : '400',\n },\n }, tab),\n ),\n ),\n );\n}\n\n// ── ListItem ────────────────────────────────────────────────\n\ninterface ListItemProps {\n theme: ThemeTokens;\n title: string;\n subtitle?: string;\n right?: React.ReactNode;\n onPress?: () => void;\n style?: any;\n}\n\nexport function ListItem({ theme, title, subtitle, right, onPress, style }: ListItemProps) {\n const content = [\n React.createElement(\n View,\n { key: 'content', style: styles.listItemContent },\n React.createElement(Text, {\n style: { color: theme.colors.text, fontSize: theme.fontSize.md },\n numberOfLines: 1,\n }, title),\n subtitle\n ? React.createElement(Text, {\n style: { color: theme.colors.textSecondary, fontSize: theme.fontSize.xs, marginTop: 2 },\n numberOfLines: 1,\n }, subtitle)\n : null,\n ),\n right ? React.createElement(View, { key: 'right' }, right) : null,\n ];\n\n if (onPress) {\n return React.createElement(\n Pressable,\n { style: [styles.listItem, { borderBottomColor: theme.colors.border }, style], onPress },\n ...content,\n );\n }\n return React.createElement(\n View,\n { style: [styles.listItem, { borderBottomColor: theme.colors.border }, style] },\n ...content,\n );\n}\n\n// ── SearchBar ───────────────────────────────────────────────\n\ninterface SearchBarProps {\n theme: ThemeTokens;\n value: string;\n onChangeText: (text: string) => void;\n placeholder?: string;\n style?: any;\n}\n\nexport function SearchBar({ theme, value, onChangeText, placeholder = 'Search...', style }: SearchBarProps) {\n return React.createElement(TextInput, {\n style: [\n styles.searchBar,\n {\n backgroundColor: theme.colors.input,\n color: theme.colors.text,\n borderColor: theme.colors.border,\n borderRadius: theme.borderRadius.sm,\n fontSize: theme.fontSize.sm,\n },\n style,\n ],\n value,\n onChangeText,\n placeholder,\n placeholderTextColor: theme.colors.textMuted,\n autoCapitalize: 'none',\n autoCorrect: false,\n });\n}\n\n// ── EmptyState ──────────────────────────────────────────────\n\ninterface EmptyStateProps {\n theme: ThemeTokens;\n title: string;\n subtitle?: string;\n style?: any;\n}\n\nexport function EmptyState({ theme, title, subtitle, style }: EmptyStateProps) {\n return React.createElement(\n View,\n { style: [styles.emptyState, style] },\n React.createElement(Text, {\n style: { color: theme.colors.textSecondary, fontSize: theme.fontSize.md, textAlign: 'center' },\n }, title),\n subtitle\n ? React.createElement(Text, {\n style: { color: theme.colors.textMuted, fontSize: theme.fontSize.sm, textAlign: 'center', marginTop: 4 },\n }, subtitle)\n : null,\n );\n}\n\n// ── ErrorState ──────────────────────────────────────────────\n\ninterface ErrorStateProps {\n theme: ThemeTokens;\n message: string;\n onRetry?: () => void;\n style?: any;\n}\n\nexport function ErrorState({ theme, message, onRetry, style }: ErrorStateProps) {\n return React.createElement(\n View,\n { style: [styles.emptyState, style] },\n React.createElement(Text, {\n style: { color: theme.colors.error, fontSize: theme.fontSize.md, textAlign: 'center' },\n }, message),\n onRetry\n ? React.createElement(\n Pressable,\n {\n style: [styles.retryButton, { backgroundColor: theme.colors.accentSoft, borderRadius: theme.borderRadius.sm }],\n onPress: onRetry,\n },\n React.createElement(Text, {\n style: { color: theme.colors.accent, fontSize: theme.fontSize.sm, fontWeight: '600' },\n }, 'Retry'),\n )\n : null,\n );\n}\n\n// ── LoadingState ────────────────────────────────────────────\n\ninterface LoadingStateProps {\n theme: ThemeTokens;\n message?: string;\n style?: any;\n}\n\nexport function LoadingState({ theme, message = 'Loading...', style }: LoadingStateProps) {\n return React.createElement(\n View,\n { style: [styles.emptyState, style] },\n React.createElement(ActivityIndicator, { size: 'small', color: theme.colors.accent }),\n React.createElement(Text, {\n style: { color: theme.colors.textSecondary, fontSize: theme.fontSize.sm, marginTop: 8 },\n }, message),\n );\n}\n\n// ── ActionButton (FAB) ──────────────────────────────────────\n\ninterface ActionButtonProps {\n theme: ThemeTokens;\n label?: string;\n icon?: React.ReactNode;\n onPress: () => void;\n style?: any;\n}\n\nexport function ActionButton({ theme, label, icon, onPress, style }: ActionButtonProps) {\n return React.createElement(\n Pressable,\n {\n style: [\n styles.fab,\n { backgroundColor: theme.colors.accent, borderRadius: theme.borderRadius.full },\n style,\n ],\n onPress,\n },\n icon || React.createElement(Text, { style: styles.fabText }, label || '+'),\n );\n}\n\n// ── Styles ──────────────────────────────────────────────────\n\nconst styles = StyleSheet.create({\n card: {\n padding: 12,\n borderRadius: 12,\n borderWidth: 1,\n marginBottom: 8,\n },\n badge: {\n paddingHorizontal: 8,\n paddingVertical: 3,\n },\n badgeText: {\n fontSize: 11,\n fontWeight: '600',\n },\n tabBar: {\n flexDirection: 'row',\n borderBottomWidth: 1,\n },\n tab: {\n flex: 1,\n padding: 12,\n alignItems: 'center',\n borderBottomWidth: 2,\n },\n listItem: {\n flexDirection: 'row',\n alignItems: 'center',\n paddingVertical: 12,\n paddingHorizontal: 16,\n borderBottomWidth: StyleSheet.hairlineWidth,\n },\n listItemContent: {\n flex: 1,\n },\n searchBar: {\n height: 40,\n paddingHorizontal: 12,\n borderWidth: 1,\n },\n emptyState: {\n flex: 1,\n justifyContent: 'center',\n alignItems: 'center',\n padding: 32,\n },\n retryButton: {\n marginTop: 12,\n paddingHorizontal: 16,\n paddingVertical: 8,\n },\n fab: {\n position: 'absolute',\n bottom: 24,\n right: 24,\n width: 56,\n height: 56,\n justifyContent: 'center',\n alignItems: 'center',\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 2 },\n shadowOpacity: 0.25,\n shadowRadius: 4,\n elevation: 5,\n },\n fabText: {\n color: '#fff',\n fontSize: 24,\n fontWeight: '600',\n },\n});\n","/**\n * Theme delivered to plugins via `usePluginTheme()`.\n *\n * Theming flows from the HOST — plugin-side fallback hex values are a no-op\n * by design. The default is the Ada warm-paper palette (matching the\n * reference app); hosts with their own brand call `setPluginThemeColors()`\n * once at startup to override.\n */\nimport { useMemo, useSyncExternalStore } from 'react';\nimport { useColorScheme } from 'react-native';\nimport { ada, spacing, fontSize, borderRadius } from '@ateam-ai-mobile/core';\nimport type { ThemeTokens } from './types';\n\ntype ThemeColors = ThemeTokens['colors'];\n\n// Ada palette mapped onto the plugin theme contract — mirrors the reference\n// app's useTheme.ts. Everything renders against warm paper; dark mode is on\n// the roadmap.\nconst adaColors: ThemeColors = {\n bg: ada.paper,\n surface: ada.paperSoft,\n surfaceAlt: ada.paperDeep,\n input: ada.paper,\n border: ada.border,\n text: ada.ink,\n textSecondary: ada.inkSoft,\n textMuted: ada.inkMuted,\n accent: ada.orange,\n accentSoft: ada.orangeSoft,\n success: '#5A8A5C',\n successSoft: 'rgba(90,138,92,0.14)',\n warning: '#D49A4E',\n warningSoft: 'rgba(212,154,78,0.16)',\n error: '#C15545',\n errorSoft: 'rgba(193,85,69,0.14)',\n purple: ada.orangeDeep,\n purpleSoft: ada.orangeSoft,\n};\n\nlet _colors: ThemeColors = adaColors;\n// null = host hasn't pinned it → track the device color scheme live\n// (parity with the reference app, whose isDark followed useColorScheme()).\nlet _isDark: boolean | null = null;\nconst _subscribers = new Set<() => void>();\n\n/** Override the plugin theme (host brand). Call once at startup — mounted\n * plugins re-render with the new tokens. */\nexport function setPluginThemeColors(colors: Partial<ThemeColors>, opts?: { isDark?: boolean }): void {\n _colors = { ...adaColors, ...colors };\n if (opts?.isDark !== undefined) _isDark = opts.isDark;\n _subscribers.forEach(fn => fn());\n}\n\nfunction subscribe(fn: () => void): () => void {\n _subscribers.add(fn);\n return () => { _subscribers.delete(fn); };\n}\n\nexport function usePluginTheme(): ThemeTokens {\n const colors = useSyncExternalStore(subscribe, () => _colors);\n const hostIsDark = useSyncExternalStore(subscribe, () => _isDark);\n const scheme = useColorScheme();\n const isDark = hostIsDark ?? scheme === 'dark';\n\n // Memoize so plugins don't re-render on every host render.\n return useMemo(() => ({\n colors,\n spacing: {\n xs: spacing.xs,\n sm: spacing.sm,\n md: spacing.md,\n lg: spacing.lg,\n xl: spacing.xl,\n xxl: spacing.xxl,\n },\n fontSize: {\n xs: fontSize.xs,\n sm: fontSize.sm,\n md: fontSize.md,\n lg: fontSize.lg,\n xl: fontSize.xl,\n },\n borderRadius: {\n sm: borderRadius.sm,\n md: borderRadius.md,\n lg: borderRadius.lg,\n full: borderRadius.full,\n },\n isDark,\n }), [colors, isDark]);\n}\n","/**\n * Simple plugin API — call any connector tool.\n *\n * Supports THREE usage patterns:\n *\n * // Pattern 1: Direct function call\n * const api = useApi(bridge);\n * const tasks = await api('task-board-mcp', 'tasks.list', {});\n *\n * // Pattern 2: .call(connector, tool, args) — connector first\n * const api = useApi(bridge);\n * const tasks = await api.call('memory-mcp', 'rules.list', {});\n *\n * // Pattern 3: .call(tool, args, connector) — connector last (rn-src plugins use this!)\n * const api = useApi(bridge);\n * const tasks = await api.call('memory.list', { type: 'rule' }, 'memory-mcp');\n *\n * All three work. The .call method auto-detects argument order.\n */\nimport { useMemo } from 'react';\nimport type { PluginBridge } from './types';\n\ninterface PluginApi {\n (connectorId: string, tool: string, args?: Record<string, any>): Promise<any>;\n call(connectorOrTool: string, toolOrArgs?: string | Record<string, any>, argsOrConnector?: Record<string, any> | string): Promise<any>;\n}\n\n/**\n * Create a simple API function to call connector tools.\n */\nexport function useApi(bridge: PluginBridge): PluginApi {\n return useMemo(() => {\n // Core implementation: (connectorId, tool, args) => Promise\n const apiFn = async (connectorId: string, tool: string, args: Record<string, any> = {}): Promise<any> => {\n if (!bridge) {\n throw new Error('useApi: bridge is required. Pass it from plugin props.');\n }\n return bridge.mcpCall(tool, args, connectorId);\n };\n\n // Add .call method that supports ALL THREE patterns:\n // api.call(connector, tool, args) — 2nd arg is string → connector first\n // api.call(tool, args, connector) — 3rd arg is string → connector last (rn-src pattern)\n // api.call(tool, args) — 2 args only → use bridge default connector\n const callMethod = async (\n first: string,\n second?: string | Record<string, any>,\n third?: Record<string, any> | string,\n ): Promise<any> => {\n if (!bridge) {\n throw new Error('useApi: bridge is required. Pass it from plugin props.');\n }\n\n if (typeof second === 'string') {\n // api.call(connector, tool, args) — 2nd arg is tool name string\n return bridge.mcpCall(second, (third as Record<string, any>) || {}, first);\n }\n\n if (typeof third === 'string') {\n // api.call(tool, args, connector) — 3rd arg is connector ID string\n return bridge.mcpCall(first, (second as Record<string, any>) || {}, third);\n }\n\n // api.call(tool, args) — no connector, use bridge default\n return bridge.mcpCall(first, (second as Record<string, any>) || {});\n };\n\n // Override .call on the function to be our custom method\n // (shadows Function.prototype.call intentionally)\n Object.defineProperty(apiFn, 'call', {\n value: callMethod,\n writable: false,\n configurable: false,\n });\n\n return apiFn as PluginApi;\n }, [bridge]);\n}\n","/**\n * Host integration points — the DI seams that keep this package free of\n * Expo imports. The host app (or @ateam-ai-mobile/expo) plugs in concrete providers\n * once at startup; everything here has a safe no-op/in-memory default so\n * a bare host works with zero configuration.\n */\n\nimport type { HapticsAPI, NativeCapabilities, PluginNative } from './sdk/types';\nimport type { RegisteredPlugin } from './sdk/types';\n\n// ── Haptics ────────────────────────────────────────────────────────────────\n\nlet _haptics: HapticsAPI | null = null;\n\n/** Register a haptics implementation (e.g. expo-haptics via @ateam-ai-mobile/expo). */\nexport function setHapticsProvider(haptics: HapticsAPI | null): void {\n _haptics = haptics;\n}\n\n/** No-op safe accessor — surfaces call this for tap feedback. */\nexport function getHaptics(): HapticsAPI | null {\n return _haptics;\n}\n\n// ── PluginNative (device capabilities handed to plugins) ──────────────────\n// The full implementation lives in the HOST (it owns the native modules —\n// e.g. ateam-mobile's device-bridge). Without a provider, plugins get\n// clearly-erroring stubs instead of crashes.\n\nfunction unavailable(api: string): any {\n return new Proxy({}, {\n get: (_t, method) => () =>\n Promise.reject(new Error(`[adas] native.${api}.${String(method)} unavailable — host did not call setPluginNativeProvider()`)),\n });\n}\n\nconst noopNative: PluginNative = {\n camera: unavailable('camera'),\n location: unavailable('location'),\n storage: unavailable('storage'),\n biometrics: unavailable('biometrics'),\n biometricsV2: unavailable('biometricsV2'),\n notifications: unavailable('notifications'),\n haptics: { impact: () => {}, notification: () => {}, selection: () => {} },\n connectivity: { isOnline: () => true, onChange: () => () => {} },\n fileSystem: unavailable('fileSystem'),\n cookies: unavailable('cookies'),\n sharing: unavailable('sharing'),\n scanner: unavailable('scanner'),\n nfc: unavailable('nfc'),\n};\n\nlet _pluginNative: PluginNative = noopNative;\n\n/** Install the device-capabilities implementation plugins receive as\n * `props.native`. The host owns the native modules. */\nexport function setPluginNativeProvider(native: PluginNative): void {\n _pluginNative = native;\n}\n\nexport function getPluginNative(): PluginNative {\n return _pluginNative;\n}\n\n/** Blocked-capability proxy: any call warns and is ignored (no throw) —\n * matches the reference app's behaviour for undeclared capabilities. */\nfunction blocked(api: string): any {\n return new Proxy({}, {\n get: (_t, method) => {\n if (typeof method !== 'string') return undefined;\n return () => {\n console.warn(`[adas] native.${api}.${method} blocked — capability '${api}' not declared in the plugin manifest`);\n };\n },\n });\n}\n\n/**\n * Gate a PluginNative by the capabilities a plugin's manifest declares.\n * Undeclared capabilities become warn-and-ignore proxies, so a plugin can\n * only touch device APIs it asked for (per-plugin security model, preserved\n * from the reference app). Connectivity is always allowed.\n *\n * Flag mapping mirrors the manifest contract: `storage` is gated by\n * `offline` (offline data ⇒ local storage).\n */\nexport function gatePluginNative(\n native: PluginNative,\n declared?: NativeCapabilities,\n): PluginNative {\n const caps = declared || {};\n return {\n camera: caps.camera ? native.camera : blocked('camera'),\n location: caps.location ? native.location : blocked('location'),\n storage: caps.offline ? native.storage : blocked('storage'),\n biometrics: caps.biometrics ? native.biometrics : blocked('biometrics'),\n biometricsV2: caps.biometricsV2 ? native.biometricsV2 : blocked('biometricsV2'),\n notifications: caps.notifications ? native.notifications : blocked('notifications'),\n haptics: caps.haptics ? native.haptics : blocked('haptics'),\n connectivity: native.connectivity,\n fileSystem: caps.fileSystem ? native.fileSystem : blocked('fileSystem'),\n cookies: caps.cookies ? native.cookies : blocked('cookies'),\n sharing: caps.sharing ? native.sharing : blocked('sharing'),\n scanner: caps.scanner ? native.scanner : blocked('scanner'),\n nfc: caps.nfc ? native.nfc : blocked('nfc'),\n };\n}\n\n// ── Native (host-bundled) plugin registry ─────────────────────────────────\n// Plugins compiled into the host binary (dev plugins, first-party panels).\n// usePlugin checks this registry before hitting the remote bundle pipeline.\n// Values may be lazy loaders so heavy plugins don't cost app-start time.\n\ntype PluginResolver = RegisteredPlugin | (() => Promise<RegisteredPlugin | null>);\n\nconst _nativeRegistry = new Map<string, PluginResolver>();\n\nexport function registerNativePlugin(id: string, plugin: PluginResolver): void {\n _nativeRegistry.set(id, plugin);\n}\n\nexport function unregisterNativePlugin(id: string): void {\n _nativeRegistry.delete(id);\n}\n\nexport const nativePluginRegistry = {\n has(id: string): boolean {\n return _nativeRegistry.has(id);\n },\n async get(id: string): Promise<RegisteredPlugin | null> {\n const entry = _nativeRegistry.get(id);\n if (!entry) return null;\n if (typeof entry === 'function') {\n try {\n return await entry();\n } catch (err) {\n console.warn(`[adas-plugins] lazy native plugin '${id}' failed to load:`, err);\n return null;\n }\n }\n return entry;\n },\n};\n\n// ── Extra bundle modules ───────────────────────────────────────────────────\n// Remote bundles are built with externals resolved by the require shim in\n// bundleManager. react / react-native / @adas/plugin-sdk are always provided;\n// anything else (react-native-webview, expo-linking, …) must be registered\n// by the host, which actually has those modules installed.\n\nconst _bundleModules = new Map<string, any>();\n\nexport function registerBundleModules(modules: Record<string, any>): void {\n for (const [name, mod] of Object.entries(modules)) {\n _bundleModules.set(name, mod);\n }\n}\n\nexport function resolveBundleModule(name: string): any | undefined {\n return _bundleModules.get(name);\n}\n","/**\n * Global Plugin Bundle Manager — singleton.\n *\n * ONE download per bundleUrl (deduplicates multi-plugin bundles); all plugin\n * renderer instances share the same loaded modules. Persistence goes through\n * the BundleStorage adapter (in-memory by default; @ateam-ai-mobile/expo provides disk).\n *\n * Usage:\n * const component = await bundleManager.load(pluginId, bundleUrl, version, hash);\n * // component = { id, type, Component, ... } (RegisteredUIPlugin)\n */\nimport React from 'react';\nimport {\n useState, useEffect, useCallback, useMemo, useRef,\n useContext, useReducer, useLayoutEffect, useImperativeHandle, useDebugValue,\n} from 'react';\nimport * as ReactNative from 'react-native';\nimport { apiFetchRaw } from '@ateam-ai-mobile/core';\nimport * as PluginSDKModule from '../sdk';\nimport SurfaceDrawer from '../surfaces/SurfaceDrawer';\nimport SurfaceCard from '../surfaces/SurfaceCard';\nimport SurfaceFullscreen from '../surfaces/SurfaceFullscreen';\nimport { useSurfaceStore } from '../registry/store';\nimport SurfaceDispatcher from '../registry/SurfaceDispatcher';\nimport { resolveBundleModule } from '../host';\nimport { sha256Hex } from '../utils/sha256';\nimport { getBundleStorage, type BundleCacheEntry } from './storage';\nimport type { RegisteredUIPlugin } from '../sdk/types';\n\n// ── Require shim ─────────────────────────────────────────────\n// Bundles are built with external: ['react', 'react-native', '@adas/plugin-sdk']\n// so they emit require() calls that this shim resolves at runtime. Anything\n// beyond the built-ins (react-native-webview, expo-linking, …) must be\n// registered by the host via registerBundleModules() — the host is the one\n// that actually has those modules installed.\n\nconst ReactShim: any = {\n __esModule: true,\n default: React,\n ...React,\n // Explicitly include hooks (React 19 may not have them as own enumerable props)\n useState, useEffect, useCallback, useMemo, useRef, useContext,\n useReducer, useLayoutEffect, useImperativeHandle, useDebugValue,\n createElement: React.createElement,\n Fragment: React.Fragment,\n Component: React.Component,\n PureComponent: React.PureComponent,\n createContext: React.createContext,\n forwardRef: React.forwardRef,\n memo: React.memo,\n Children: React.Children,\n cloneElement: React.cloneElement,\n isValidElement: React.isValidElement,\n createRef: React.createRef,\n lazy: React.lazy,\n Suspense: React.Suspense,\n};\n\n// Freeze PluginSDK shim to prevent bundle monkey-patching of register().\n// Surface containers + registry ride along — the reference app's original\n// shim exposed the FULL plugin-sdk index, and bundles built against it may\n// import SurfaceDrawer/useSurfaceStore/etc. from '@adas/plugin-sdk'.\nconst PluginSDKShim = Object.freeze({\n ...PluginSDKModule,\n SurfaceDrawer,\n SurfaceCard,\n SurfaceFullscreen,\n useSurfaceStore,\n SurfaceDispatcher,\n PluginSDK: Object.freeze({\n ...PluginSDKModule.PluginSDK,\n register: PluginSDKModule.PluginSDK.register,\n }),\n register: PluginSDKModule.register,\n});\n\nconst requireShim = (name: string) => {\n if (name === 'react') return ReactShim;\n if (name === 'react-native') return ReactNative;\n if (name === '@adas/plugin-sdk') return PluginSDKShim;\n const hostModule = resolveBundleModule(name);\n if (hostModule !== undefined) return hostModule;\n console.warn(`[BundleManager] Unknown module: ${name} — register it via registerBundleModules()`);\n return {};\n};\n\n// ── Cache constants ──────────────────────────────────────────\n\nconst CACHE_TTL = 7 * 24 * 60 * 60 * 1000; // 7 days\n\n// ── BundleManager singleton ──────────────────────────────────\n\nclass BundleManager {\n /** In-memory module cache: bundleUrl → evaluated module.exports */\n private modules = new Map<string, any>();\n\n /** In-flight download deduplication: bundleUrl → Promise */\n private inflight = new Map<string, Promise<any>>();\n\n // ── Public API ──────────────────────────────────────────────\n\n /**\n * Load a plugin component. Returns the RegisteredUIPlugin object.\n * Handles: in-memory cache → disk cache → download → evaluate → extract.\n *\n * `expectedHash` (SHA-256 hex from cp.getContextPlugin manifest) MUST be\n * provided for any bundle the user has not previously verified — without\n * it, a tampered bundle is `eval`'d as JS with full RN runtime + every\n * native capability the manifest declares.\n */\n async load(\n pluginId: string,\n bundleUrl: string,\n version: string,\n expectedHash?: string,\n ): Promise<RegisteredUIPlugin> {\n // 1. In-memory cache — instant\n const cached = this.getComponentFromMemory(pluginId, bundleUrl);\n if (cached) return cached;\n\n // 2. Deduplicate in-flight downloads for same bundleUrl\n let modulePromise = this.inflight.get(bundleUrl);\n if (!modulePromise) {\n modulePromise = this.loadModule(bundleUrl, version, expectedHash);\n this.inflight.set(bundleUrl, modulePromise);\n // Swallow rejection on the cleanup chain — the original `await` below\n // still surfaces the error to the caller.\n modulePromise.catch(() => {}).finally(() => this.inflight.delete(bundleUrl));\n }\n\n await modulePromise;\n\n // 3. Extract component for this specific pluginId\n const component = this.getComponentFromMemory(pluginId, bundleUrl);\n if (!component) {\n throw new Error(\n `Bundle loaded from ${bundleUrl} but no component found for \"${pluginId}\". ` +\n `Available exports: ${this.describeModule(this.modules.get(bundleUrl))}`\n );\n }\n\n return component;\n }\n\n /**\n * Prewarm: load every plugin bundle in the background after app boot, so a\n * later open is instant (in-memory hit) instead of round-tripping the\n * network. Errors are swallowed per-plugin — one bad bundle shouldn't\n * block others. Concurrency capped at 3.\n */\n async prewarmAll(items: Array<{\n pluginId: string;\n bundleUrl: string;\n version: string;\n bundleHash?: string;\n }>): Promise<void> {\n const queue = [...items];\n const concurrency = Math.min(3, queue.length);\n const start = Date.now();\n let okCount = 0;\n let failCount = 0;\n\n const worker = async () => {\n while (queue.length > 0) {\n const it = queue.shift();\n if (!it) return;\n try {\n await this.load(it.pluginId, it.bundleUrl, it.version, it.bundleHash);\n okCount++;\n } catch (err) {\n failCount++;\n // Silent — preload is best-effort. Log for diagnostics only.\n console.log(`[prewarm] ${it.pluginId}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n };\n\n await Promise.all(Array.from({ length: concurrency }, worker));\n console.log(`[prewarm] ${okCount} loaded, ${failCount} failed in ${Date.now() - start}ms`);\n }\n\n /**\n * Clear all caches (in-memory + storage).\n */\n async clearAll(): Promise<void> {\n this.modules.clear();\n try {\n await getBundleStorage().clearAll();\n } catch (err) {\n console.warn('[BundleManager] clearAll error:', err);\n }\n }\n\n /**\n * Clear cache for a specific plugin/bundle.\n */\n async clearPlugin(pluginId: string): Promise<void> {\n const storage = getBundleStorage();\n const index = await storage.loadIndex();\n const entry = index[pluginId];\n if (entry) {\n this.modules.delete(entry.bundleUrl);\n delete index[pluginId];\n await storage.saveIndex(index);\n try {\n await storage.deleteBundle(entry.localPath);\n } catch { /* ignore */ }\n }\n }\n\n // ── Private: module loading pipeline ────────────────────────\n\n private async loadModule(bundleUrl: string, version: string, expectedHash?: string): Promise<void> {\n // Already in memory?\n if (this.modules.has(bundleUrl)) return;\n\n const storage = getBundleStorage();\n const index = await storage.loadIndex();\n const expectedLc = expectedHash ? expectedHash.toLowerCase() : null;\n\n // Check disk cache. A cache hit is only valid when the cached SHA-256\n // matches the manifest hash exactly — never trust a cached bundle\n // without proof.\n let content: string | null = null;\n for (const entry of Object.values(index)) {\n if (entry.bundleUrl === bundleUrl && entry.version === version) {\n const age = Date.now() - entry.timestamp;\n const hashOk = expectedLc && entry.bundleHash && entry.bundleHash.toLowerCase() === expectedLc;\n if (age < CACHE_TTL && hashOk) {\n content = await storage.readBundle(entry.localPath);\n if (content) break;\n }\n }\n }\n\n // Download if not cached\n if (!content) {\n const downloaded = await this.download(bundleUrl, expectedLc);\n content = downloaded.content;\n // Save with verified hash\n const safeName = bundleUrl.replace(/[^a-zA-Z0-9]/g, '_').slice(-80);\n try {\n const localPath = await storage.writeBundle(`${safeName}@${version}.js`, content);\n index[bundleUrl] = {\n localPath,\n version,\n timestamp: Date.now(),\n bundleUrl,\n bundleHash: downloaded.sha256,\n } satisfies BundleCacheEntry;\n await storage.saveIndex(index);\n } catch (err) {\n console.warn('[BundleManager] Cache write error:', err);\n }\n }\n\n // Evaluate\n const mod = this.evaluate(content, bundleUrl);\n this.modules.set(bundleUrl, mod);\n }\n\n /**\n * Download + integrity-verify a bundle. Refuses to return when:\n * - manifest hash and computed hash disagree\n * - server's `X-Bundle-SHA256` disagrees with manifest hash\n * - no expected hash AND no server hash header (cannot prove the bundle\n * wasn't tampered with — fail closed rather than trust the wire).\n */\n private async download(bundleUrl: string, expectedHash: string | null): Promise<{ content: string; sha256: string }> {\n const cacheBust = `${bundleUrl}${bundleUrl.includes('?') ? '&' : '?'}t=${Date.now()}`;\n const response = await apiFetchRaw(cacheBust);\n if (!response.ok) {\n const body = await response.text().catch(() => '');\n throw new Error(`Download failed: ${response.status} ${response.statusText} — ${body.substring(0, 200)}`);\n }\n\n const content = await response.text();\n if (content.length < 50 || content.trimStart().startsWith('<!')) {\n throw new Error('Bundle content is empty or HTML — server may have returned an error page');\n }\n\n const headerHash = (response.headers.get('x-bundle-sha256') || '').toLowerCase();\n const computed = sha256Hex(content);\n\n if (expectedHash) {\n if (computed !== expectedHash) {\n throw new Error(\n `Bundle integrity FAIL: expected sha256=${expectedHash.slice(0, 12)}... got ${computed.slice(0, 12)}.... Refusing to evaluate.`\n );\n }\n if (headerHash && headerHash !== expectedHash) {\n throw new Error(\n `Bundle hash mismatch: manifest=${expectedHash.slice(0, 12)}... server=${headerHash.slice(0, 12)}.... Refusing to evaluate.`\n );\n }\n } else if (headerHash) {\n if (computed !== headerHash) {\n throw new Error(\n `Bundle body sha256 (${computed.slice(0, 12)}...) does not match server's X-Bundle-SHA256 (${headerHash.slice(0, 12)}...).`\n );\n }\n console.warn(`[BundleManager] No manifest bundleHash — verified via X-Bundle-SHA256 header only`);\n } else {\n throw new Error(\n `No bundleHash in manifest and no X-Bundle-SHA256 header from server. Refusing to evaluate untrusted bundle.`\n );\n }\n\n return { content, sha256: computed };\n }\n\n private evaluate(content: string, bundleUrl: string): any {\n // Strip __toESM wrapping — our shim already provides correct ESM objects.\n // esbuild's __toESM + isNodeMode=1 double-wraps and breaks React 19 hooks.\n let cleaned = content.replace(\n /__toESM\\(require\\((\"[^\"]+\")\\)(?:,\\s*\\d+)?\\)/g,\n 'require($1)',\n );\n\n // Strip __toCommonJS wrapper — it creates a getter-based Proxy that\n // collapses all named exports to the same object in Hermes/JSC.\n cleaned = cleaned.replace(\n /module\\.exports\\s*=\\s*__toCommonJS\\((\\w+)\\)/g,\n 'module.exports = $1',\n );\n\n const mod: { exports: any } = { exports: {} };\n try {\n const factory = new Function(\n 'module', 'exports', 'require', '__filename', '__dirname',\n cleaned,\n );\n factory(mod, mod.exports, requireShim, bundleUrl, 'adas-plugin-bundles');\n } catch (err) {\n throw new Error(`Bundle evaluation failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n // Materialize getter-based exports into plain values.\n // esbuild's __export uses Object.defineProperty getters which can\n // misbehave in Hermes/JSC — force-resolve them now.\n const raw = mod.exports;\n const materialized: Record<string, any> = {};\n for (const key of Object.getOwnPropertyNames(raw)) {\n try {\n materialized[key] = raw[key];\n } catch { /* skip broken getters */ }\n }\n return materialized;\n }\n\n // ── Private: component extraction ───────────────────────────\n\n /**\n * Find a plugin component in a loaded module.\n * Handles: default export, direct key match, multi-plugin .id match.\n */\n private getComponentFromMemory(pluginId: string, bundleUrl: string): RegisteredUIPlugin | null {\n const mod = this.modules.get(bundleUrl);\n if (!mod) return null;\n\n const shortName = pluginId.includes(':') ? pluginId.split(':').pop()! : pluginId;\n\n // 1. Direct key match (full pluginId as key)\n if (mod[pluginId] && typeof mod[pluginId].Component === 'function') {\n return mod[pluginId] as RegisteredUIPlugin;\n }\n\n // 2. PascalCase match from shortName (most reliable for combined bundles)\n // e.g., \"memories-panel\" → \"MemoriesPanel\"\n const pascal = shortName.replace(/(^|-)([a-z])/g, (_: string, __: string, c: string) => c.toUpperCase());\n if (mod[pascal] && typeof mod[pascal] === 'object' && typeof mod[pascal].Component === 'function') {\n return mod[pascal] as RegisteredUIPlugin;\n }\n\n // 3. Default export (single-plugin bundles)\n if (mod.default && typeof mod.default === 'object' && typeof mod.default.Component === 'function') {\n return mod.default as RegisteredUIPlugin;\n }\n\n // 4. Match by .id field (last resort — can be unreliable in combined bundles)\n for (const key of Object.keys(mod)) {\n const val = mod[key];\n if (\n val &&\n typeof val === 'object' &&\n val.id === shortName &&\n typeof val.Component === 'function'\n ) {\n return val as RegisteredUIPlugin;\n }\n }\n\n return null;\n }\n\n private describeModule(mod: any): string {\n if (!mod) return '(empty)';\n return Object.keys(mod)\n .map(k => {\n const v = mod[k];\n if (v && typeof v === 'object' && v.id) return `${k}(id=\"${v.id}\")`;\n return k;\n })\n .join(', ');\n }\n}\n\n/** Singleton — shared across all plugin renderers */\nexport const bundleManager = new BundleManager();\n","/**\n * SurfaceDrawer — generic bottom-sheet container for plugins.\n *\n * Plugins render their content; SurfaceDrawer provides the Ada chrome:\n * drag handle, header (orb + title + subtitle + optional privacy pill),\n * close button, motion, backdrop dismiss.\n *\n * Usage:\n * <SurfaceDrawer\n * visible={open}\n * onClose={() => setOpen(false)}\n * title=\"What I remember about you\"\n * subtitle=\"Only you can see this\"\n * privacy\n * >\n * <MyPluginContent />\n * </SurfaceDrawer>\n */\nimport { useEffect, useRef, type ReactNode } from 'react';\nimport {\n View,\n Text,\n Pressable,\n StyleSheet,\n Animated,\n Dimensions,\n Platform,\n} from 'react-native';\nimport Svg, { Path } from 'react-native-svg';\nimport { ada } from '@ateam-ai-mobile/core';\nimport AdaOrb from './AdaOrb';\n\nconst SCREEN_H = Dimensions.get('window').height;\nconst fontFam = Platform.OS === 'ios' ? 'SF Pro Rounded' : undefined;\n\nexport interface SurfaceDrawerProps {\n visible: boolean;\n onClose: () => void;\n title: string;\n subtitle?: string;\n /** Show the lock + \"Only you can see this\" privacy pill under the title */\n privacy?: boolean;\n /** Show the small Ada orb in the header (default: true) */\n showOrb?: boolean;\n /** Sheet height ratio of screen (default: 0.85) */\n heightRatio?: number;\n children: ReactNode;\n}\n\nexport default function SurfaceDrawer({\n visible,\n onClose,\n title,\n subtitle,\n privacy,\n showOrb = true,\n heightRatio = 0.85,\n children,\n}: SurfaceDrawerProps) {\n const sheetH = Math.round(SCREEN_H * heightRatio);\n const fadeAnim = useRef(new Animated.Value(0)).current;\n const slideAnim = useRef(new Animated.Value(sheetH)).current;\n\n useEffect(() => {\n if (visible) {\n Animated.parallel([\n Animated.timing(fadeAnim, { toValue: 1, duration: 220, useNativeDriver: true }),\n Animated.spring(slideAnim, { toValue: 0, useNativeDriver: true, tension: 80, friction: 12 }),\n ]).start();\n } else {\n fadeAnim.setValue(0);\n slideAnim.setValue(sheetH);\n }\n }, [visible]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const close = () => {\n Animated.parallel([\n Animated.timing(fadeAnim, { toValue: 0, duration: 160, useNativeDriver: true }),\n Animated.timing(slideAnim, { toValue: sheetH, duration: 200, useNativeDriver: true }),\n ]).start(() => onClose());\n };\n\n if (!visible) return null;\n\n return (\n <Animated.View style={[styles.overlay, { opacity: fadeAnim }]} pointerEvents=\"auto\">\n <Pressable style={StyleSheet.absoluteFill} onPress={close} />\n <Animated.View\n style={[\n styles.sheet,\n { height: sheetH, transform: [{ translateY: slideAnim }] },\n ]}\n >\n {/* Drag handle */}\n <View style={styles.handle} />\n\n {/* Header */}\n <View style={styles.header}>\n {showOrb ? (\n <View style={styles.headerOrb}>\n <AdaOrb size={36} state=\"idle\" />\n </View>\n ) : null}\n <View style={{ flex: 1 }}>\n <Text style={styles.title}>{title}</Text>\n {subtitle ? <Text style={styles.subtitle}>{subtitle}</Text> : null}\n {privacy ? (\n <View style={styles.privacyPill}>\n <Svg width={11} height={11} viewBox=\"0 0 24 24\" fill=\"none\" stroke={ada.inkSoft} strokeWidth={2.5} strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <Path d=\"M19 11H5a2 2 0 00-2 2v7a2 2 0 002 2h14a2 2 0 002-2v-7a2 2 0 00-2-2z\" />\n <Path d=\"M7 11V7a5 5 0 0110 0v4\" />\n </Svg>\n <Text style={styles.privacyText}>Only you can see this</Text>\n </View>\n ) : null}\n </View>\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel=\"Close\"\n style={styles.closeBtn}\n onPress={close}\n hitSlop={12}\n >\n <Svg width={18} height={18} viewBox=\"0 0 24 24\" fill=\"none\" stroke={ada.inkSoft} strokeWidth={2.2} strokeLinecap=\"round\">\n <Path d=\"M18 6L6 18M6 6l12 12\" />\n </Svg>\n </Pressable>\n </View>\n\n {/* Body — caller renders content */}\n <View style={styles.body}>{children}</View>\n </Animated.View>\n </Animated.View>\n );\n}\n\nconst styles = StyleSheet.create({\n overlay: {\n position: 'absolute',\n top: 0, left: 0, right: 0, bottom: 0,\n backgroundColor: 'rgba(26, 20, 16, 0.35)',\n justifyContent: 'flex-end',\n zIndex: 999,\n elevation: 999,\n },\n sheet: {\n backgroundColor: ada.paper,\n borderTopLeftRadius: 28,\n borderTopRightRadius: 28,\n overflow: 'hidden',\n },\n handle: {\n width: 40,\n height: 4,\n borderRadius: 2,\n backgroundColor: ada.inkMuted,\n opacity: 0.3,\n alignSelf: 'center',\n marginTop: 10,\n marginBottom: 6,\n },\n header: {\n flexDirection: 'row',\n alignItems: 'center',\n paddingHorizontal: 22,\n paddingVertical: 16,\n gap: 12,\n borderBottomWidth: 1,\n borderBottomColor: ada.border,\n },\n headerOrb: {\n width: 36,\n height: 36,\n alignItems: 'center',\n justifyContent: 'center',\n },\n title: {\n fontSize: 17,\n fontWeight: '600',\n color: ada.ink,\n letterSpacing: -0.2,\n fontFamily: fontFam,\n },\n subtitle: {\n fontSize: 13,\n fontWeight: '400',\n color: ada.inkSoft,\n marginTop: 2,\n letterSpacing: -0.1,\n fontFamily: fontFam,\n },\n privacyPill: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 5,\n marginTop: 4,\n },\n privacyText: {\n fontSize: 11,\n fontWeight: '500',\n color: ada.inkSoft,\n letterSpacing: 0.2,\n fontFamily: fontFam,\n },\n closeBtn: {\n width: 32,\n height: 32,\n borderRadius: 16,\n backgroundColor: ada.paperSoft,\n justifyContent: 'center',\n alignItems: 'center',\n },\n body: {\n flex: 1,\n },\n});\n","import { useEffect } from 'react';\nimport { View, StyleSheet } from 'react-native';\nimport Animated, {\n useSharedValue,\n useAnimatedProps,\n withRepeat,\n withTiming,\n withSequence,\n withDelay,\n Easing,\n cancelAnimation,\n} from 'react-native-reanimated';\nimport Svg, { Circle, Defs, RadialGradient, Stop, Text as SvgText } from 'react-native-svg';\n\nconst AnimatedCircle = Animated.createAnimatedComponent(Circle);\n\nexport type AdaOrbState = 'idle' | 'listening' | 'thinking' | 'speaking';\n\ninterface AdaOrbProps {\n size?: number;\n state?: AdaOrbState;\n /** Show the \"iA\" wordmark inside the orb. Suppressed below 60px because\n * glyphs become unreadable at avatar/chevron sizes — render as a plain\n * orb there. Default true. */\n inscription?: boolean;\n}\n\nexport default function AdaOrb({ size = 120, state = 'idle', inscription = true }: AdaOrbProps) {\n const showInscription = inscription && size >= 60;\n const center = size / 2;\n const coreR = size * 0.32;\n const innerHaloR = size * 0.42;\n const outerHaloR = size * 0.5;\n\n // Base breath layers\n const coreScale = useSharedValue(1);\n const innerScale = useSharedValue(1);\n const outerScale = useSharedValue(1);\n const outerOpacity = useSharedValue(0.55);\n\n // Listening: continuous ripple cycle (one shared value, phase-shifted reads)\n const rippleCycle = useSharedValue(0);\n\n // Thinking: 3 orbiting sparks at different speeds + directions\n const orbit1 = useSharedValue(0); // fast inner\n const orbit2 = useSharedValue(0); // medium mid\n const orbit3 = useSharedValue(0); // slow outer (counter)\n\n // Speaking: secondary fast double-beat overlay\n const beat = useSharedValue(1);\n\n useEffect(() => {\n cancelAnimation(coreScale);\n cancelAnimation(innerScale);\n cancelAnimation(outerScale);\n cancelAnimation(outerOpacity);\n cancelAnimation(rippleCycle);\n cancelAnimation(orbit1);\n cancelAnimation(orbit2);\n cancelAnimation(orbit3);\n cancelAnimation(beat);\n\n // Reset state-specific drivers\n rippleCycle.value = 0;\n orbit1.value = 0;\n orbit2.value = 0;\n orbit3.value = 0;\n beat.value = 1;\n\n let breathMs = 3600;\n let breathAmount = 0.025;\n\n if (state === 'listening') {\n breathMs = 2000;\n breathAmount = 0.05;\n // Continuous ripple loop — one cycle = one ripple lifetime\n rippleCycle.value = withRepeat(\n withTiming(1, { duration: 2000, easing: Easing.out(Easing.quad) }),\n -1,\n false,\n );\n } else if (state === 'thinking') {\n breathMs = 2400;\n breathAmount = 0.04;\n // Three sparks orbiting at different speeds + directions — feels like processing\n orbit1.value = withRepeat(\n withTiming(1, { duration: 1100, easing: Easing.linear }),\n -1,\n false,\n );\n orbit2.value = withRepeat(\n withTiming(1, { duration: 1900, easing: Easing.linear }),\n -1,\n false,\n );\n orbit3.value = withRepeat(\n withTiming(-1, { duration: 2700, easing: Easing.linear }),\n -1,\n false,\n );\n } else if (state === 'speaking') {\n breathMs = 700;\n breathAmount = 0.13;\n // Double-beat overlay — quick punch + secondary kick\n beat.value = withRepeat(\n withSequence(\n withTiming(1.08, { duration: 90, easing: Easing.out(Easing.quad) }),\n withTiming(1.0, { duration: 130, easing: Easing.in(Easing.quad) }),\n withTiming(1.05, { duration: 80, easing: Easing.out(Easing.quad) }),\n withTiming(1.0, { duration: 200, easing: Easing.in(Easing.quad) }),\n withTiming(1.0, { duration: 280 }),\n ),\n -1,\n false,\n );\n }\n\n // Base breath — different easing per state\n const breathEasing =\n state === 'speaking' ? Easing.out(Easing.cubic) : Easing.inOut(Easing.sin);\n\n coreScale.value = withRepeat(\n withSequence(\n withTiming(1 + breathAmount, { duration: breathMs, easing: breathEasing }),\n withTiming(1, { duration: breathMs, easing: breathEasing }),\n ),\n -1,\n false,\n );\n innerScale.value = withRepeat(\n withSequence(\n withTiming(1 + breathAmount * 1.4, { duration: breathMs * 1.15, easing: breathEasing }),\n withTiming(1, { duration: breathMs * 1.15, easing: breathEasing }),\n ),\n -1,\n false,\n );\n outerScale.value = withRepeat(\n withSequence(\n withTiming(1 + breathAmount * 1.8, { duration: breathMs * 1.3, easing: breathEasing }),\n withTiming(1, { duration: breathMs * 1.3, easing: breathEasing }),\n ),\n -1,\n false,\n );\n outerOpacity.value = withRepeat(\n withSequence(\n withTiming(state === 'idle' ? 0.55 : 0.8, { duration: breathMs * 1.3, easing: breathEasing }),\n withTiming(0.35, { duration: breathMs * 1.3, easing: breathEasing }),\n ),\n -1,\n false,\n );\n }, [state]);\n\n // Animated props for base layers — multiplied by `beat` so speaking double-pulse stacks\n const coreProps = useAnimatedProps(() => ({\n r: coreR * coreScale.value * beat.value,\n }));\n const innerProps = useAnimatedProps(() => ({\n r: innerHaloR * innerScale.value * beat.value,\n }));\n const outerProps = useAnimatedProps(() => ({\n r: outerHaloR * outerScale.value,\n opacity: outerOpacity.value,\n }));\n\n // Listening ripples — three phase-shifted reads of rippleCycle\n const ripple1Props = useAnimatedProps(() => {\n const t = rippleCycle.value;\n return {\n r: outerHaloR * (1 + t * 0.55),\n opacity: state === 'listening' ? 0.55 * (1 - t) : 0,\n };\n });\n const ripple2Props = useAnimatedProps(() => {\n const t = (rippleCycle.value + 0.34) % 1;\n return {\n r: outerHaloR * (1 + t * 0.55),\n opacity: state === 'listening' ? 0.45 * (1 - t) : 0,\n };\n });\n const ripple3Props = useAnimatedProps(() => {\n const t = (rippleCycle.value + 0.67) % 1;\n return {\n r: outerHaloR * (1 + t * 0.55),\n opacity: state === 'listening' ? 0.35 * (1 - t) : 0,\n };\n });\n\n // Thinking sparks — three at different radii and angular velocities\n const spark1Props = useAnimatedProps(() => {\n const angle = orbit1.value * Math.PI * 2 - Math.PI / 2;\n const r = coreR * 0.55;\n return {\n cx: center + Math.cos(angle) * r,\n cy: center + Math.sin(angle) * r,\n opacity: state === 'thinking' ? 1 : 0,\n };\n });\n const spark2Props = useAnimatedProps(() => {\n const angle = orbit2.value * Math.PI * 2 + Math.PI * 0.6;\n const r = coreR * 0.85;\n return {\n cx: center + Math.cos(angle) * r,\n cy: center + Math.sin(angle) * r,\n opacity: state === 'thinking' ? 0.9 : 0,\n };\n });\n const spark3Props = useAnimatedProps(() => {\n const angle = orbit3.value * Math.PI * 2 + Math.PI * 1.3;\n const r = coreR * 1.05;\n return {\n cx: center + Math.cos(angle) * r,\n cy: center + Math.sin(angle) * r,\n opacity: state === 'thinking' ? 0.7 : 0,\n };\n });\n\n return (\n <View style={[styles.wrap, { width: size, height: size }]}>\n <Svg width={size} height={size}>\n <Defs>\n <RadialGradient id=\"adaCore\" cx=\"36%\" cy=\"30%\" r=\"70%\">\n <Stop offset=\"0%\" stopColor=\"#FFE0B0\" stopOpacity=\"1\" />\n <Stop offset=\"28%\" stopColor=\"#FFA85A\" stopOpacity=\"1\" />\n <Stop offset=\"65%\" stopColor=\"#FF7A28\" stopOpacity=\"1\" />\n <Stop offset=\"100%\" stopColor=\"#B8410A\" stopOpacity=\"1\" />\n </RadialGradient>\n <RadialGradient id=\"adaInner\" cx=\"50%\" cy=\"50%\" r=\"50%\">\n <Stop offset=\"0%\" stopColor=\"#FF8A3C\" stopOpacity=\"0.55\" />\n <Stop offset=\"100%\" stopColor=\"#FF7028\" stopOpacity=\"0\" />\n </RadialGradient>\n <RadialGradient id=\"adaOuter\" cx=\"50%\" cy=\"50%\" r=\"50%\">\n <Stop offset=\"0%\" stopColor=\"#FF7A28\" stopOpacity=\"0.4\" />\n <Stop offset=\"100%\" stopColor=\"#FF6018\" stopOpacity=\"0\" />\n </RadialGradient>\n <RadialGradient id=\"adaSpark\" cx=\"50%\" cy=\"50%\" r=\"50%\">\n <Stop offset=\"0%\" stopColor=\"#FFFAEB\" stopOpacity=\"1\" />\n <Stop offset=\"60%\" stopColor=\"#FFD095\" stopOpacity=\"0.9\" />\n <Stop offset=\"100%\" stopColor=\"#FFA85A\" stopOpacity=\"0\" />\n </RadialGradient>\n </Defs>\n\n {/* Listening ripples — drawn behind orb body */}\n <AnimatedCircle\n cx={center}\n cy={center}\n stroke=\"#FF7A28\"\n strokeWidth={1.5}\n fill=\"none\"\n animatedProps={ripple1Props}\n />\n <AnimatedCircle\n cx={center}\n cy={center}\n stroke=\"#FF7A28\"\n strokeWidth={1.5}\n fill=\"none\"\n animatedProps={ripple2Props}\n />\n <AnimatedCircle\n cx={center}\n cy={center}\n stroke=\"#FF7A28\"\n strokeWidth={1.5}\n fill=\"none\"\n animatedProps={ripple3Props}\n />\n\n {/* Base orb body */}\n <AnimatedCircle cx={center} cy={center} fill=\"url(#adaOuter)\" animatedProps={outerProps} />\n <AnimatedCircle cx={center} cy={center} fill=\"url(#adaInner)\" animatedProps={innerProps} />\n <AnimatedCircle cx={center} cy={center} fill=\"url(#adaCore)\" animatedProps={coreProps} />\n\n {/* Thinking sparks — three orbiting particles drawn on top */}\n <AnimatedCircle\n r={Math.max(3, size * 0.05)}\n fill=\"url(#adaSpark)\"\n animatedProps={spark1Props}\n />\n <AnimatedCircle\n r={Math.max(2.5, size * 0.04)}\n fill=\"url(#adaSpark)\"\n animatedProps={spark2Props}\n />\n <AnimatedCircle\n r={Math.max(2, size * 0.032)}\n fill=\"url(#adaSpark)\"\n animatedProps={spark3Props}\n />\n\n {/* \"iA\" wordmark — matches the app icon. Suppressed at small sizes\n where the glyphs would be unreadable (see showInscription). */}\n {showInscription && (\n <SvgText\n x={center}\n y={center + size * 0.11}\n fontSize={size * 0.34}\n fontWeight=\"600\"\n fill=\"#FFF5E0\"\n textAnchor=\"middle\"\n letterSpacing={-size * 0.005}\n >\n iA\n </SvgText>\n )}\n </Svg>\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n wrap: {\n alignItems: 'center',\n justifyContent: 'center',\n },\n});\n","/**\n * SurfaceCard — a tappable tile shape for plugins that surface as cards\n * (empty-state suggestions, dashboards, daily-brief items).\n *\n * Plugins declare icon + title + subtitle + onPress; the host provides\n * the warm Ada chrome (paper-soft bg, soft border, arrow chevron, haptic).\n *\n * Optional `children` render below the title block for richer previews\n * (e.g. small ring widget, last-meal photo, current weather).\n *\n * Usage:\n * <SurfaceCard\n * icon=\"🥗\"\n * title=\"Today's plate\"\n * subtitle=\"2,100 kcal · 140g protein\"\n * onPress={() => sendPrompt('Tell me about my nutrition today')}\n * />\n */\nimport { type ReactNode } from 'react';\nimport {\n View,\n Text,\n Pressable,\n StyleSheet,\n Platform,\n} from 'react-native';\nimport { getHaptics } from '../host';\nimport { ada, borderRadius } from '@ateam-ai-mobile/core';\n\nconst fontFam = Platform.OS === 'ios' ? 'SF Pro Rounded' : undefined;\n\nexport interface SurfaceCardProps {\n /** Emoji or short string icon shown on the left */\n icon?: string;\n /** Card headline (e.g. \"Today's plate\") */\n title: string;\n /** Optional secondary line below title */\n subtitle?: string;\n /** Tap handler. Fires haptic before invoking. */\n onPress?: () => void;\n /** Long-press handler. Fires a heavier haptic. Used by user widgets for\n * the Delete / (later) Hide / Rename context menu. */\n onLongPress?: () => void;\n /** Whether to show the ›-style trailing arrow (default: true if onPress) */\n showArrow?: boolean;\n /** Optional preview content rendered below the title block */\n children?: ReactNode;\n}\n\nexport default function SurfaceCard({\n icon,\n title,\n subtitle,\n onPress,\n onLongPress,\n showArrow,\n children,\n}: SurfaceCardProps) {\n const arrow = showArrow ?? !!onPress;\n const handlePress = () => {\n if (!onPress) return;\n getHaptics()?.impact('light');\n onPress();\n };\n const handleLongPress = () => {\n if (!onLongPress) return;\n getHaptics()?.impact('medium');\n onLongPress();\n };\n\n return (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={[icon, title, subtitle].filter(Boolean).join(', ')}\n accessibilityHint={onLongPress ? 'Long-press for more options' : undefined}\n style={styles.card}\n onPress={onPress ? handlePress : undefined}\n onLongPress={onLongPress ? handleLongPress : undefined}\n disabled={!onPress && !onLongPress}\n >\n <View style={styles.row}>\n {icon ? <Text style={styles.icon}>{icon}</Text> : null}\n <View style={{ flex: 1 }}>\n <Text style={styles.title}>{title}</Text>\n {subtitle ? <Text style={styles.subtitle}>{subtitle}</Text> : null}\n </View>\n {arrow ? <Text style={styles.arrow}>{'\\u2192'}</Text> : null}\n </View>\n {children ? <View style={styles.body}>{children}</View> : null}\n </Pressable>\n );\n}\n\nconst styles = StyleSheet.create({\n card: {\n backgroundColor: ada.paperSoft,\n borderWidth: 1,\n borderColor: ada.border,\n borderRadius: borderRadius.xl,\n paddingHorizontal: 16,\n paddingVertical: 14,\n },\n row: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 14,\n },\n icon: {\n fontSize: 22,\n },\n title: {\n fontSize: 15,\n fontWeight: '600',\n color: ada.ink,\n letterSpacing: -0.2,\n fontFamily: fontFam,\n },\n subtitle: {\n fontSize: 13,\n fontWeight: '400',\n color: ada.inkMuted,\n marginTop: 2,\n letterSpacing: -0.1,\n fontFamily: fontFam,\n },\n arrow: {\n fontSize: 18,\n color: ada.inkMuted,\n fontWeight: '400',\n },\n body: {\n marginTop: 10,\n paddingTop: 10,\n borderTopWidth: 1,\n borderTopColor: ada.border,\n },\n});\n","/**\n * SurfaceFullscreen — edge-to-edge takeover container for plugins\n * (voice mode, focus modes, photo capture, etc.).\n *\n * Plugins render their content; SurfaceFullscreen provides:\n * - Paper background, fade-in motion\n * - Top status line (\"ada\" wordmark + status text)\n * - Optional hero element above body (e.g. AdaOrb)\n * - Bottom close button with label\n * - Spring entry on the hero (scale 0.6 → 1)\n *\n * Usage:\n * <SurfaceFullscreen\n * visible={open}\n * onClose={hangUp}\n * status=\"Ada is speaking\"\n * hero={<AdaOrb size={180} state=\"speaking\" />}\n * closeIcon=\"hangup\"\n * closeLabel=\"End\"\n * >\n * <Transcript ... />\n * </SurfaceFullscreen>\n */\nimport { useEffect, useRef, type ReactNode } from 'react';\nimport {\n View,\n Text,\n Pressable,\n StyleSheet,\n Animated,\n Platform,\n} from 'react-native';\nimport Svg, { Path } from 'react-native-svg';\nimport { ada } from '@ateam-ai-mobile/core';\n\nconst fontFam = Platform.OS === 'ios' ? 'SF Pro Rounded' : undefined;\n\nexport interface SurfaceFullscreenProps {\n visible: boolean;\n onClose: () => void;\n /** Status text shown under the wordmark */\n status?: string;\n /** Optional hero element above body (typically an AdaOrb) */\n hero?: ReactNode;\n /** Body content */\n children?: ReactNode;\n /** Whether to show the small \"ada\" wordmark (default: true) */\n showWordmark?: boolean;\n /** Built-in close icon flavor or a custom node. Default: 'x' */\n closeIcon?: 'x' | 'hangup' | ReactNode;\n /** Label under the close button (default: \"Close\") */\n closeLabel?: string;\n}\n\nexport default function SurfaceFullscreen({\n visible,\n onClose,\n status,\n hero,\n children,\n showWordmark = true,\n closeIcon = 'x',\n closeLabel = 'Close',\n}: SurfaceFullscreenProps) {\n const fadeAnim = useRef(new Animated.Value(0)).current;\n const heroScale = useRef(new Animated.Value(0.6)).current;\n\n useEffect(() => {\n if (visible) {\n Animated.parallel([\n Animated.timing(fadeAnim, { toValue: 1, duration: 280, useNativeDriver: true }),\n Animated.spring(heroScale, { toValue: 1, useNativeDriver: true, tension: 60, friction: 11 }),\n ]).start();\n } else {\n fadeAnim.setValue(0);\n heroScale.setValue(0.6);\n }\n }, [visible]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const close = () => {\n Animated.parallel([\n Animated.timing(fadeAnim, { toValue: 0, duration: 180, useNativeDriver: true }),\n Animated.timing(heroScale, { toValue: 0.6, duration: 180, useNativeDriver: true }),\n ]).start(() => onClose());\n };\n\n if (!visible) return null;\n\n const renderCloseIcon = () => {\n if (closeIcon === 'hangup') {\n return (\n <Svg width={22} height={22} viewBox=\"0 0 24 24\" fill={ada.paper}>\n <Path d=\"M3.68 16.07l3.92-3.11c.28-.23.68-.28 1.01-.14l2.77 1.15c.36.15.61.5.61.89v2.82c4.34-.76 7.93-4.35 8.69-8.69h-2.82c-.39 0-.74-.25-.89-.61L16.12 5.6c-.13-.33-.09-.73.14-1.01l3.11-3.92c.29-.37.81-.41 1.15-.08l3.24 3.24c.2.2.31.47.31.76C24 14.44 14.44 24 4.59 24c-.29 0-.56-.11-.76-.31L.59 20.45c-.33-.34-.29-.86.09-1.15z\" />\n </Svg>\n );\n }\n if (closeIcon === 'x') {\n return (\n <Svg width={20} height={20} viewBox=\"0 0 24 24\" fill=\"none\" stroke={ada.paper} strokeWidth={2.5} strokeLinecap=\"round\">\n <Path d=\"M18 6L6 18M6 6l12 12\" />\n </Svg>\n );\n }\n return closeIcon;\n };\n\n return (\n <Animated.View style={[styles.overlay, { opacity: fadeAnim }]} pointerEvents=\"auto\">\n {/* Top status */}\n <View style={styles.topBar}>\n {showWordmark ? <Text style={styles.wordmark}>ada</Text> : null}\n {status ? <Text style={styles.status}>{status}</Text> : null}\n </View>\n\n {/* Optional hero (e.g. AdaOrb) — springs in */}\n {hero ? (\n <View style={styles.heroWrap}>\n <Animated.View style={{ transform: [{ scale: heroScale }] }}>\n {hero}\n </Animated.View>\n </View>\n ) : null}\n\n {/* Body — caller content */}\n <View style={styles.body}>{children}</View>\n\n {/* Bottom close */}\n <View style={styles.bottomBar}>\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel={closeLabel}\n style={styles.closeBtn}\n onPress={close}\n hitSlop={12}\n >\n {renderCloseIcon()}\n </Pressable>\n {closeLabel ? <Text style={styles.closeLabel}>{closeLabel}</Text> : null}\n </View>\n </Animated.View>\n );\n}\n\nconst styles = StyleSheet.create({\n overlay: {\n position: 'absolute',\n top: 0, left: 0, right: 0, bottom: 0,\n backgroundColor: ada.paper,\n zIndex: 1000,\n elevation: 1000,\n },\n topBar: {\n paddingTop: Platform.OS === 'ios' ? 64 : 36,\n paddingHorizontal: 24,\n alignItems: 'center',\n gap: 6,\n },\n wordmark: {\n fontSize: 14,\n fontFamily: fontFam,\n fontWeight: '500',\n color: ada.inkMuted,\n letterSpacing: 4,\n },\n status: {\n fontSize: 17,\n fontFamily: fontFam,\n fontWeight: '500',\n color: ada.ink,\n letterSpacing: -0.2,\n },\n heroWrap: {\n paddingTop: 24,\n paddingBottom: 12,\n alignItems: 'center',\n justifyContent: 'center',\n },\n body: {\n flex: 1,\n },\n bottomBar: {\n paddingBottom: Platform.OS === 'ios' ? 48 : 32,\n paddingTop: 12,\n alignItems: 'center',\n gap: 8,\n },\n closeBtn: {\n width: 64,\n height: 64,\n borderRadius: 32,\n backgroundColor: ada.ink,\n justifyContent: 'center',\n alignItems: 'center',\n shadowColor: ada.ink,\n shadowOffset: { width: 0, height: 8 },\n shadowOpacity: 0.18,\n shadowRadius: 14,\n elevation: 8,\n },\n closeLabel: {\n fontSize: 13,\n fontFamily: fontFam,\n fontWeight: '500',\n color: ada.inkMuted,\n letterSpacing: 0.3,\n },\n});\n","/**\n * Surface registry + active-state store.\n *\n * - `manifests`: map of registered surface plugins\n * - `activeDrawer` / `activeFullscreen`: which surface (if any) is currently mounted\n * (only one of each at a time)\n *\n * Plugins are registered by calling `register(...)` from bootstrap or by an\n * \"engine\" event. The dispatcher subscribes to active state and mounts.\n */\nimport { create } from 'zustand';\nimport type {\n SurfaceManifest,\n SurfaceType,\n SurfaceVisibility,\n SurfacePlacement,\n InlineSurfaceManifest,\n MountSurfaceManifest,\n} from './types';\n\ninterface SurfaceState {\n manifests: Record<string, SurfaceManifest>;\n activeDrawer: string | null;\n activeFullscreen: string | null;\n /** Props passed by the engine when summoning a surface via sys.focusUiPlugin.\n * Keyed by surface id; cleared on hide. Plugins read these on first mount. */\n activeProps: Record<string, any>;\n /** Increments on every `show()` call. Used as a React `key` so the Surface\n * Component remounts even if the same surface is shown again — guarantees\n * the plugin's data-fetching effects re-run, so the user always sees fresh\n * data when they open a drawer. */\n mountKey: number;\n\n register: (manifest: SurfaceManifest) => void;\n unregister: (id: string) => void;\n\n /** Open a surface in its declared container. Props (optional) are forwarded\n * to the mounted plugin on first render. */\n show: (id: string, props?: any) => void;\n /** Close the surface with the given id (or whatever is active for the matching type) */\n hide: (id?: string) => void;\n\n /** All registered manifests, optionally filtered.\n * `placement: 'unset'` selects only plugins WITHOUT a placement (default list). */\n list: (filter?: {\n type?: SurfaceType;\n visibility?: SurfaceVisibility[];\n placement?: SurfacePlacement | 'unset';\n }) => SurfaceManifest[];\n}\n\nexport const useSurfaceStore = create<SurfaceState>((set, get) => ({\n manifests: {},\n activeDrawer: null,\n activeFullscreen: null,\n activeProps: {},\n mountKey: 0,\n\n register: (manifest) =>\n set(s => ({ manifests: { ...s.manifests, [manifest.id]: manifest } })),\n\n unregister: (id) =>\n set(s => {\n const next = { ...s.manifests };\n delete next[id];\n const nextProps = { ...s.activeProps };\n delete nextProps[id];\n return { manifests: next, activeProps: nextProps };\n }),\n\n show: (id, props) => {\n const m = get().manifests[id];\n if (!m) {\n console.warn('[SurfaceStore] show: unknown surface id', id);\n return;\n }\n set(s => {\n const nextProps = props !== undefined\n ? { ...s.activeProps, [id]: props }\n : s.activeProps;\n // Bump mountKey on every show so the dispatcher remounts the Component,\n // forcing the plugin's data fetch to re-run with fresh results.\n const mountKey = s.mountKey + 1;\n if (m.type === 'drawer') return { activeDrawer: id, activeProps: nextProps, mountKey };\n if (m.type === 'fullscreen') return { activeFullscreen: id, activeProps: nextProps, mountKey };\n return { mountKey };\n });\n },\n\n hide: (id) => {\n const s = get();\n if (!id) {\n // Hide whichever is active and clear their props\n const cleanup = { ...s.activeProps };\n if (s.activeDrawer) delete cleanup[s.activeDrawer];\n if (s.activeFullscreen) delete cleanup[s.activeFullscreen];\n set({ activeDrawer: null, activeFullscreen: null, activeProps: cleanup });\n return;\n }\n const cleanup = { ...s.activeProps };\n delete cleanup[id];\n const patch: Partial<SurfaceState> = { activeProps: cleanup };\n if (s.activeDrawer === id) patch.activeDrawer = null;\n if (s.activeFullscreen === id) patch.activeFullscreen = null;\n set(patch);\n },\n\n list: (filter) => {\n const all = Object.values(get().manifests);\n return all.filter(m => {\n if (filter?.type && m.type !== filter.type) return false;\n if (filter?.visibility && !filter.visibility.includes(m.visibility)) return false;\n if (filter?.placement) {\n if (filter.placement === 'unset') {\n if (m.placement) return false;\n } else if (m.placement !== filter.placement) {\n return false;\n }\n }\n return true;\n });\n },\n}));\n\n// Convenience guards\nexport function isMountSurface(m: SurfaceManifest): m is MountSurfaceManifest {\n return m.type === 'drawer' || m.type === 'fullscreen';\n}\nexport function isInlineSurface(m: SurfaceManifest): m is InlineSurfaceManifest {\n return m.type === 'card' || m.type === 'header' || m.type === 'ambient' || m.type === 'nudge';\n}\n","/**\n * SurfaceDispatcher — mounted once at the app root.\n *\n * Watches the surface store and:\n * - Mounts the active drawer surface (max one at a time)\n * - Mounts the active fullscreen surface (max one at a time)\n *\n * The mounted Component receives `dismiss` to close itself; the dispatcher\n * clears active-state after dismiss.\n *\n * Inline surfaces (card / header / ambient / nudge) are NOT rendered by the\n * dispatcher — they're queried via `useSurfaceStore().list({ type: 'card' })`\n * and rendered inline by the host screen that owns that placement.\n */\nimport { useEffect } from 'react';\nimport { useSurfaceStore } from './store';\nimport { pluginCommandEmitter } from '@ateam-ai-mobile/core';\n\nexport default function SurfaceDispatcher() {\n const manifests = useSurfaceStore(s => s.manifests);\n const activeDrawer = useSurfaceStore(s => s.activeDrawer);\n const activeFullscreen = useSurfaceStore(s => s.activeFullscreen);\n const activeProps = useSurfaceStore(s => s.activeProps);\n const mountKey = useSurfaceStore(s => s.mountKey);\n const hide = useSurfaceStore(s => s.hide);\n\n // bridge.close() inside a plugin emits a global `plugin-close` event.\n // The legacy plugins overlay listens for this; we mirror that here so\n // surface-mounted plugins (auth-webview, etc.) actually dismiss themselves.\n useEffect(() => {\n const unsub = pluginCommandEmitter.on('plugin-close', ({ pluginId }: { pluginId?: string } = {}) => {\n if (!pluginId) return;\n const s = useSurfaceStore.getState();\n if (s.activeDrawer === pluginId || s.activeFullscreen === pluginId) {\n s.hide(pluginId);\n }\n });\n return unsub;\n }, []);\n\n const drawer = activeDrawer ? manifests[activeDrawer] : null;\n const fullscreen = activeFullscreen ? manifests[activeFullscreen] : null;\n const drawerProps = activeDrawer ? activeProps[activeDrawer] : undefined;\n const fullscreenProps = activeFullscreen ? activeProps[activeFullscreen] : undefined;\n\n // mountKey changes on every show() so React remounts the Component → the\n // plugin's data-fetching useEffects re-run → user sees fresh data each open.\n return (\n <>\n {drawer && drawer.type === 'drawer' && drawer.Component ? (\n <drawer.Component\n key={`drawer-${drawer.id}-${mountKey}`}\n dismiss={() => hide(drawer.id)}\n props={drawerProps}\n />\n ) : null}\n {fullscreen && fullscreen.type === 'fullscreen' && fullscreen.Component ? (\n <fullscreen.Component\n key={`fullscreen-${fullscreen.id}-${mountKey}`}\n dismiss={() => hide(fullscreen.id)}\n props={fullscreenProps}\n />\n ) : null}\n </>\n );\n}\n","/**\n * Pure-JS SHA-256 — used to verify integrity of downloaded plugin bundles.\n * Avoids pulling expo-crypto / a native module into the dependency graph for\n * what amounts to a one-shot post-download check.\n *\n * Hot path: hash a ~100KB-1MB bundle once after download. Performance is\n * acceptable on every supported device (sub-50ms).\n *\n * Algorithm reference: FIPS 180-4. Implementation derived from the public-\n * domain reference code; see https://en.wikipedia.org/wiki/SHA-2#Pseudocode.\n */\n\nconst K = new Uint32Array([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n]);\n\nconst rotr = (n: number, x: number) => (x >>> n) | (x << (32 - n));\n\n/** SHA-256 of a UTF-8 string. Returns the lowercase hex digest. */\nexport function sha256Hex(message: string): string {\n // 1. Encode to UTF-8 bytes\n const utf8 = unescape(encodeURIComponent(message));\n const bytes = new Uint8Array(utf8.length);\n for (let i = 0; i < utf8.length; i++) bytes[i] = utf8.charCodeAt(i);\n\n // 2. Pre-processing: append 0x80, pad with zeros, then append 64-bit big-endian length\n const bitLen = bytes.length * 8;\n const padLen = (56 - (bytes.length + 1) % 64 + 64) % 64;\n const buf = new Uint8Array(bytes.length + 1 + padLen + 8);\n buf.set(bytes);\n buf[bytes.length] = 0x80;\n // 64-bit big-endian length (high 32 bits zeroed — bundles are well under 2^32 bits)\n const len = buf.length;\n buf[len - 4] = (bitLen >>> 24) & 0xff;\n buf[len - 3] = (bitLen >>> 16) & 0xff;\n buf[len - 2] = (bitLen >>> 8) & 0xff;\n buf[len - 1] = bitLen & 0xff;\n\n const h = new Uint32Array([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,\n 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n ]);\n const w = new Uint32Array(64);\n\n // 3. Process each 512-bit chunk\n for (let i = 0; i < buf.length; i += 64) {\n for (let t = 0; t < 16; t++) {\n const j = i + t * 4;\n w[t] = (buf[j] << 24) | (buf[j + 1] << 16) | (buf[j + 2] << 8) | buf[j + 3];\n }\n for (let t = 16; t < 64; t++) {\n const s0 = rotr(7, w[t - 15]) ^ rotr(18, w[t - 15]) ^ (w[t - 15] >>> 3);\n const s1 = rotr(17, w[t - 2]) ^ rotr(19, w[t - 2]) ^ (w[t - 2] >>> 10);\n w[t] = (w[t - 16] + s0 + w[t - 7] + s1) | 0;\n }\n\n let a = h[0], b = h[1], c = h[2], d = h[3], e = h[4], f = h[5], g = h[6], hh = h[7];\n for (let t = 0; t < 64; t++) {\n const S1 = rotr(6, e) ^ rotr(11, e) ^ rotr(25, e);\n const ch = (e & f) ^ (~e & g);\n const temp1 = (hh + S1 + ch + K[t] + w[t]) | 0;\n const S0 = rotr(2, a) ^ rotr(13, a) ^ rotr(22, a);\n const maj = (a & b) ^ (a & c) ^ (b & c);\n const temp2 = (S0 + maj) | 0;\n hh = g; g = f; f = e; e = (d + temp1) | 0; d = c; c = b; b = a; a = (temp1 + temp2) | 0;\n }\n\n h[0] = (h[0] + a) | 0; h[1] = (h[1] + b) | 0; h[2] = (h[2] + c) | 0; h[3] = (h[3] + d) | 0;\n h[4] = (h[4] + e) | 0; h[5] = (h[5] + f) | 0; h[6] = (h[6] + g) | 0; h[7] = (h[7] + hh) | 0;\n }\n\n // 4. Produce hex\n let out = '';\n for (let i = 0; i < 8; i++) {\n out += h[i].toString(16).padStart(8, '0');\n }\n return out;\n}\n","/**\n * Bundle disk-cache adapter. The bundle manager persists downloaded plugin\n * bundles through this interface so the package stays free of Expo/RN\n * storage imports. @ateam-ai-mobile/expo ships a file-system implementation; the\n * default is in-memory (bundles re-download per app session — correct,\n * just slower on cold start).\n */\n\nexport interface BundleCacheEntry {\n localPath: string;\n version: string;\n timestamp: number;\n bundleUrl: string;\n /** SHA-256 (hex, lowercase) of the cached bundle bytes — integrity proof */\n bundleHash?: string;\n}\n\nexport interface BundleStorage {\n loadIndex(): Promise<Record<string, BundleCacheEntry>>;\n saveIndex(index: Record<string, BundleCacheEntry>): Promise<void>;\n /** Read a cached bundle. Returns null when missing/unreadable. */\n readBundle(localPath: string): Promise<string | null>;\n /** Persist a bundle; returns the localPath to record in the index.\n * `key` is a filesystem-safe name derived from the bundle URL+version. */\n writeBundle(key: string, content: string): Promise<string>;\n deleteBundle(localPath: string): Promise<void>;\n /** Wipe everything (index + bundles). */\n clearAll(): Promise<void>;\n}\n\n/** Default storage: keeps bundles for the lifetime of the JS context only. */\nexport class MemoryBundleStorage implements BundleStorage {\n private index: Record<string, BundleCacheEntry> = {};\n private files = new Map<string, string>();\n\n async loadIndex() {\n return this.index;\n }\n async saveIndex(index: Record<string, BundleCacheEntry>) {\n this.index = index;\n }\n async readBundle(localPath: string) {\n return this.files.get(localPath) ?? null;\n }\n async writeBundle(key: string, content: string) {\n const localPath = `memory://${key}`;\n this.files.set(localPath, content);\n return localPath;\n }\n async deleteBundle(localPath: string) {\n this.files.delete(localPath);\n }\n async clearAll() {\n this.index = {};\n this.files.clear();\n }\n}\n\nlet _storage: BundleStorage = new MemoryBundleStorage();\n\n/** Install a persistent storage implementation (call before first plugin load). */\nexport function setBundleStorage(storage: BundleStorage): void {\n _storage = storage;\n}\n\nexport function getBundleStorage(): BundleStorage {\n return _storage;\n}\n","/**\n * Native bridge for React Native plugins.\n * Direct function calls — no postMessage, no serialization.\n *\n * Identity comes from @ateam-ai-mobile/core config (host-owned auth); tool calls go\n * through the same MCP layer as everything else.\n */\nimport { useCallback, useMemo, useRef, useEffect } from 'react';\nimport type { MutableRefObject } from 'react';\nimport {\n connectorCall,\n mcpFetch as mcpFetchApi,\n unwrapMcpResult,\n postCommandResult,\n getTenant,\n getUser,\n pluginCommandEmitter,\n useChatStore,\n} from '@ateam-ai-mobile/core';\nimport type { PluginBridge, PluginContext } from '../sdk/types';\n\ntype CommandListener = (type: string, payload: any) => void;\n\nfunction buildContext(pluginId: string, connectorId?: string): PluginContext {\n const user = getUser();\n return {\n tenant: getTenant(),\n pluginId,\n connectorId,\n user: user ? { id: user.actorId || '', name: user.name || '', roles: user.roles || [] } : undefined,\n };\n}\n\nexport function useNativeBridge(pluginId: string, connectorId?: string, commandBufferRef?: MutableRefObject<any[]>): PluginBridge {\n const initCallbackRef = useRef<((ctx: PluginContext) => void) | null>(null);\n const commandListenersRef = useRef<Set<CommandListener>>(new Set());\n const initializedRef = useRef(false);\n\n const fireInit = useCallback(() => {\n if (initializedRef.current || !initCallbackRef.current) return;\n initCallbackRef.current(buildContext(pluginId, connectorId));\n initializedRef.current = true;\n }, [pluginId, connectorId]);\n\n const onInit = useCallback((cb: (ctx: PluginContext) => void) => {\n initCallbackRef.current = cb;\n setTimeout(fireInit, 0);\n }, [fireInit]);\n\n const mcpCall = useCallback(async (toolOrConnector: string, argsOrTool: any, cIdOrArgs?: any): Promise<any> => {\n // Support BOTH argument orders:\n // (tool, args, connectorId?) — bridge standard\n // (connectorId, tool, args?) — plugin convenience (matches useApi pattern)\n // Detect: if 2nd arg is a string, caller used (connector, tool, args) order\n let tool: string;\n let args: any;\n let cId: string | undefined;\n\n if (typeof argsOrTool === 'string') {\n // (connectorId, tool, args) order\n cId = toolOrConnector;\n tool = argsOrTool;\n args = cIdOrArgs || {};\n } else {\n // Standard (tool, args, connectorId?) order\n tool = toolOrConnector;\n args = argsOrTool;\n cId = typeof cIdOrArgs === 'string' ? cIdOrArgs : undefined;\n }\n\n const targetConnector = cId || connectorId;\n if (!targetConnector) {\n console.warn(`[NativeBridge:${pluginId}] mcpCall failed — no connectorId. tool=${tool}`);\n throw new Error(`[NativeBridge:${pluginId}] mcpCall requires a connectorId`);\n }\n\n try {\n const data = await connectorCall(targetConnector, tool, args);\n\n if (!data.ok && data.error) {\n throw new Error(data.error);\n }\n return unwrapMcpResult(data.result);\n } catch (err) {\n console.warn(`[NativeBridge:${pluginId}] mcpCall failed → connector=${targetConnector} tool=${tool}:`, (err as any)?.message || err);\n throw err;\n }\n }, [pluginId, connectorId]);\n\n const mcpFetch = useCallback(async (body: any): Promise<any> => {\n return mcpFetchApi(body);\n }, []);\n\n const sendCommand = useCallback((type: string, payload: any) => {\n // Handle command results — mirror web PluginHost pattern\n if (type === 'plugin.command.result' && payload?.correlationId) {\n postCommandResult(payload.correlationId, payload.result, payload.error);\n return;\n }\n }, [pluginId]);\n\n const onCommand = useCallback((cb: CommandListener): (() => void) => {\n commandListenersRef.current.add(cb);\n // Flush buffered commands immediately when a listener registers —\n // the buffer may have commands that arrived before the plugin mounted.\n if (commandBufferRef) {\n const buf = commandBufferRef.current;\n const remaining: any[] = [];\n for (const cmd of buf) {\n if (cmd.pluginId === pluginId && cmd.command !== '__focus') {\n try { cb(cmd.command, { ...cmd.args, correlationId: cmd.correlationId }); } catch {}\n } else {\n remaining.push(cmd);\n }\n }\n commandBufferRef.current = remaining;\n }\n // Also drain the emitter's own recent-commands buffer (covers the\n // surface-dispatcher path which has no commandBufferRef).\n for (const cmd of pluginCommandEmitter.drainPluginCommands(pluginId)) {\n try { cb(cmd.command, { ...cmd.args, correlationId: cmd.correlationId }); } catch {}\n }\n return () => {\n commandListenersRef.current.delete(cb);\n };\n }, [pluginId, commandBufferRef]);\n\n // Dispatch live plugin commands to registered listeners\n useEffect(() => {\n const unsub = pluginCommandEmitter.on('plugin-command', (cmd: any) => {\n if (cmd.pluginId !== pluginId) return;\n if (cmd.command === '__focus') return; // Focus-only, no dispatch\n commandListenersRef.current.forEach(fn => {\n try { fn(cmd.command, { ...cmd.args, correlationId: cmd.correlationId }); } catch {}\n });\n });\n return unsub;\n }, [pluginId]);\n\n // Flush buffered commands on mount (commands that arrived before plugin\n // was ready). Two sources, both safe to drain:\n // 1. The parent-passed `commandBufferRef` (legacy overlay path).\n // 2. The emitter's own recent-commands buffer (surface-dispatcher path).\n useEffect(() => {\n const timer = setTimeout(() => {\n const dispatch = (cmd: any) => {\n commandListenersRef.current.forEach(fn => {\n try { fn(cmd.command, { ...cmd.args, correlationId: cmd.correlationId }); } catch {}\n });\n };\n\n if (commandBufferRef) {\n const remaining: any[] = [];\n for (const cmd of commandBufferRef.current) {\n if (cmd.pluginId === pluginId && cmd.command !== '__focus') dispatch(cmd);\n else remaining.push(cmd);\n }\n commandBufferRef.current = remaining;\n }\n\n // Always drain the emitter buffer too — the dispatcher path has no\n // parent buffer, and the legacy path may still have lost commands here\n // if they arrived before the parent's listener registered.\n for (const cmd of pluginCommandEmitter.drainPluginCommands(pluginId)) {\n dispatch(cmd);\n }\n }, 500); // Delay to let plugin register its onCommand listener\n return () => clearTimeout(timer);\n }, [pluginId, commandBufferRef]);\n\n useEffect(() => {\n return () => {\n commandListenersRef.current.clear();\n initializedRef.current = false;\n };\n }, []);\n\n const close = useCallback(() => {\n pluginCommandEmitter.emit('plugin-close', { pluginId });\n }, [pluginId]);\n\n /** Send a chat message into the user's thread (e.g. confirmed \"connect my\n * Gmail\"). Pair with bridge.close() to land the user on chat. */\n const sendMessage = useCallback((text: string) => {\n const t = (text || '').trim();\n if (!t) return;\n try {\n useChatStore.getState().sendMessage(t);\n } catch (err) {\n console.warn('[bridge.sendMessage] failed:', err instanceof Error ? err.message : String(err));\n }\n }, []);\n\n /** Prepare (pre-fill) the chat composer without sending. The host chat\n * screen listens for `prepare-chat-message` and stuffs `text` into its\n * input state. Pair with bridge.close(). */\n const prepareMessage = useCallback((text: string) => {\n const t = (text || '').trim();\n if (!t) return;\n pluginCommandEmitter.emit('prepare-chat-message', { text: t });\n }, []);\n\n // Memoize the bridge object to prevent child re-renders — a new object\n // every render → useApi recreates → fetch effects refire → flicker loop.\n return useMemo(\n () => ({ onInit, mcpCall, mcpFetch, sendCommand, onCommand, close, sendMessage, prepareMessage }),\n [onInit, mcpCall, mcpFetch, sendCommand, onCommand, close, sendMessage, prepareMessage],\n );\n}\n\n/**\n * Non-hook version for service plugins (no React lifecycle).\n */\nexport function createNativeBridge(pluginId: string, connectorId?: string): PluginBridge {\n const commandListeners = new Set<CommandListener>();\n\n return {\n onInit(cb) {\n setTimeout(() => {\n cb(buildContext(pluginId, connectorId));\n }, 0);\n },\n\n async mcpCall(tool: string, args: any, cId?: string) {\n const targetConnector = cId || connectorId;\n if (!targetConnector) {\n throw new Error(`[NativeBridge:${pluginId}] mcpCall requires a connectorId`);\n }\n const data = await connectorCall(targetConnector, tool, args);\n if (!data.ok && data.error) throw new Error(data.error);\n return unwrapMcpResult(data.result);\n },\n\n async mcpFetch(body: any) {\n return mcpFetchApi(body);\n },\n\n sendCommand(_type: string, _payload: any) {\n },\n\n onCommand(cb: CommandListener) {\n commandListeners.add(cb);\n return () => { commandListeners.delete(cb); };\n },\n\n close() {\n pluginCommandEmitter.emit('plugin-close', { pluginId });\n },\n\n sendMessage(text: string) {\n const t = (text || '').trim();\n if (!t) return;\n try {\n useChatStore.getState().sendMessage(t);\n } catch (err) {\n console.warn('[bridge.sendMessage] failed:', err instanceof Error ? err.message : String(err));\n }\n },\n\n prepareMessage(text: string) {\n const t = (text || '').trim();\n if (!t) return;\n pluginCommandEmitter.emit('prepare-chat-message', { text: t });\n },\n } as PluginBridge;\n}\n","/**\n * usePlugin — single hook for loading any plugin.\n *\n * Simple async load → done pattern over the global BundleManager singleton\n * (deduplication + caching) and the host's native plugin registry.\n *\n * Usage:\n * const { plugin, loading, error } = usePlugin(pluginId, manifest);\n * if (loading) return <Spinner />;\n * if (error) return <Error message={error} />;\n * return <plugin.Component bridge={bridge} native={native} theme={theme} context={context} />;\n */\nimport { useState, useEffect } from 'react';\nimport { bundleManager } from '../bundles/bundleManager';\nimport { nativePluginRegistry } from '../host';\nimport type { RegisteredUIPlugin } from '../sdk/types';\n\ninterface PluginManifestLike {\n version?: string;\n name?: string;\n render?: any;\n _connectorId?: string;\n}\n\ninterface UsePluginResult {\n plugin: RegisteredUIPlugin | null;\n loading: boolean;\n error: string | null;\n bundleMissing: boolean;\n}\n\n/**\n * Compute the bundle URL.\n *\n * **Policy:** bundle URLs are ALWAYS derived from the pluginId via the\n * canonical platform route /api/ui-plugins/{pluginId}/bundle.js.\n * Connector-supplied bundleUrl fields are IGNORED — the Builder strips them\n * at introspection time, but we treat any survivors as bugs and ignore them\n * here too (defense in depth vs \"hand-coded URL with bad encoding causes\n * silent iframe fallback\" bugs).\n */\ntype BundleUrlSource = 'derived-default' | 'ignored-explicit' | 'none';\nfunction extractBundleUrl(pluginId: string, manifest: PluginManifestLike): { url: string | null; source: BundleUrlSource; ignored?: string } {\n const render = manifest.render;\n if (!render || typeof render !== 'object') return { url: null, source: 'none' };\n\n // Detect (and refuse) any explicit bundle URL the connector tried to inject.\n const injected =\n (manifest as any).bundleUrl ||\n (render as any).bundleUrl ||\n render.reactNative?.bundleUrl ||\n null;\n\n // Mobile decides native vs web INDEPENDENT of render.mode. The server's\n // cp.getContextPlugin flattens mode to \"iframe\" whenever an iframeUrl is\n // available (for web-frontend compatibility) — even when a reactNative\n // component is also declared. So we derive the bundle URL whenever a\n // reactNative component is present, regardless of what `mode` says.\n const hasNativeComponent = !!(render.reactNative?.component || render.component);\n if (hasNativeComponent) {\n return {\n url: `/api/ui-plugins/${encodeURIComponent(pluginId)}/bundle.js`,\n source: injected ? 'ignored-explicit' : 'derived-default',\n ...(injected && { ignored: injected }),\n };\n }\n\n return { url: null, source: 'none' };\n}\n\n/**\n * Extract the SHA-256 integrity hash from the manifest (server-provided).\n * BundleManager refuses to evaluate a downloaded bundle whose bytes don't\n * hash to this value — defense vs MITM/CDN tampering.\n */\nfunction extractBundleHash(manifest: PluginManifestLike): string | undefined {\n if ((manifest as any).bundleHash) return (manifest as any).bundleHash;\n const render = manifest.render;\n if (!render || typeof render !== 'object') return undefined;\n if (render.bundleHash) return render.bundleHash;\n if (render.reactNative?.bundleHash) return render.reactNative.bundleHash;\n return undefined;\n}\n\n/**\n * Extract component name from manifest render field.\n */\nfunction extractComponentId(manifest: PluginManifestLike): string | null {\n const render = manifest.render;\n if (!render || typeof render !== 'object') return null;\n return render.component || render.reactNative?.component || null;\n}\n\nexport function usePlugin(pluginId: string, manifest: PluginManifestLike): UsePluginResult {\n const [plugin, setPlugin] = useState<RegisteredUIPlugin | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [bundleMissing, setBundleMissing] = useState(false);\n\n const version = manifest.version || '1.0.0';\n const { url: bundleUrl, ignored: ignoredUrl } = extractBundleUrl(pluginId, manifest);\n const bundleHash = extractBundleHash(manifest);\n const componentId = extractComponentId(manifest);\n const lookupId = componentId || pluginId;\n\n if (ignoredUrl) {\n console.warn(`[usePlugin] ${pluginId}: IGNORED connector-injected bundleUrl=\"${ignoredUrl}\". The host derives bundle URLs from pluginId via the canonical route.`);\n }\n\n useEffect(() => {\n let cancelled = false;\n\n (async () => {\n try {\n // Path 1: Local registry (host-bundled / dev plugins)\n if (nativePluginRegistry.has(lookupId)) {\n const registered = await nativePluginRegistry.get(lookupId);\n if (cancelled) return;\n\n if (!registered) {\n throw new Error(`Local plugin not found: ${lookupId}`);\n }\n if (registered.type === 'service') {\n throw new Error(`Plugin ${lookupId} is a service plugin and cannot be rendered`);\n }\n\n setPlugin(registered as RegisteredUIPlugin);\n setLoading(false);\n return;\n }\n\n // Path 2: Remote bundle (via global BundleManager)\n if (bundleUrl) {\n try {\n const component = await bundleManager.load(pluginId, bundleUrl, version, bundleHash);\n if (cancelled) return;\n setPlugin(component);\n setLoading(false);\n return;\n } catch (bundleErr) {\n const msg = bundleErr instanceof Error ? bundleErr.message : String(bundleErr);\n // warn (not error) — this is a handled path: we fall back to WebView.\n console.warn(`[usePlugin] ${pluginId}: Bundle load failed, falling back to WebView:`, msg);\n // RN bundle failed — connector declared a component but didn't\n // ship a usable bundle (404, eval error, integrity fail, etc).\n // Mark \"bundle missing\" so the parent renderer can fall back to\n // the WebView/iframe when the manifest also has an iframeUrl.\n if (!cancelled) {\n setBundleMissing(true);\n setLoading(false);\n }\n return;\n }\n }\n\n // No native source available. If the manifest also has an iframeUrl,\n // mark \"bundle missing\" so the parent falls back to the WebView.\n // Only throw when there's truly nothing renderable.\n const iframeUrl = manifest?.render?.iframeUrl\n || (manifest?.render?.mode === 'adaptive' && manifest?.render?.iframe?.iframeUrl);\n if (iframeUrl) {\n if (!cancelled) {\n setBundleMissing(true);\n setLoading(false);\n }\n return;\n }\n throw new Error(\n `Plugin \"${manifest.name || pluginId}\" has no bundle URL and is not in the local registry. ` +\n `Check that the solution definition includes a valid render.reactNative.bundleUrl.`\n );\n } catch (err) {\n if (cancelled) return;\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[usePlugin] Failed to load ${pluginId}:`, msg);\n setError(msg);\n setLoading(false);\n }\n })();\n\n return () => { cancelled = true; };\n }, [pluginId, lookupId, bundleUrl, bundleHash, version]);\n\n return { plugin, loading, error, bundleMissing };\n}\n","import { useEffect, useState, useCallback } from 'react';\nimport { apiFetch } from '@ateam-ai-mobile/core';\nimport type { PluginManifest } from '../sdk/types';\n\nexport interface RemotePlugin {\n id: string;\n name: string;\n version: string;\n manifest: PluginManifest;\n source: 'remote' | 'local'; // Where this plugin came from\n}\n\ninterface UseRemotePluginsResult {\n plugins: RemotePlugin[];\n loading: boolean;\n error: string | null;\n refreshing: boolean;\n refresh: () => Promise<void>;\n}\n\n/**\n * Fetches available UI plugins from A-Team Core for the current solution.\n * Uses apiFetch which auto-injects JWT + tenant headers.\n */\nexport function useRemotePlugins(solutionId?: string): UseRemotePluginsResult {\n const [plugins, setPlugins] = useState<RemotePlugin[]>([]);\n const [loading, setLoading] = useState(true);\n const [refreshing, setRefreshing] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const fetchRemotePlugins = useCallback(async () => {\n try {\n setError(null);\n\n const targetSolutionId = solutionId || 'current';\n\n let data: any;\n try {\n data = await apiFetch<any>(`/api/solutions/${targetSolutionId}/ui-plugins`);\n } catch (err: any) {\n if (err?.message?.includes('404')) {\n console.warn(`[useRemotePlugins] Solution ${targetSolutionId} not found or has no plugins`);\n setPlugins([]);\n return;\n }\n throw err;\n }\n const uiPlugins: RemotePlugin[] = (data.plugins || []).map((plugin: any) => ({\n id: plugin.id,\n name: plugin.name,\n version: plugin.version || '1.0.0',\n source: 'remote' as const,\n manifest: {\n id: plugin.id,\n name: plugin.name,\n version: plugin.version || '1.0.0',\n type: 'ui',\n description: plugin.description || '',\n capabilities: plugin.capabilities || {},\n channels: plugin.channels || ['default'],\n render: (plugin.render && typeof plugin.render === 'object') ? plugin.render : { mode: 'react-native' as const, component: plugin.name || plugin.id },\n entry_point: plugin.entry_point || plugin.name,\n sandbox: plugin.sandbox !== false,\n requires_auth: plugin.requires_auth !== false,\n } as PluginManifest,\n }));\n\n setPlugins(uiPlugins);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error('[useRemotePlugins] Failed to fetch plugins:', message);\n setError(message);\n setPlugins([]); // Clear plugins on error\n } finally {\n setLoading(false);\n setRefreshing(false);\n }\n }, [solutionId]);\n\n const refresh = useCallback(async () => {\n setRefreshing(true);\n await fetchRemotePlugins();\n }, [fetchRemotePlugins]);\n\n useEffect(() => {\n setLoading(true);\n fetchRemotePlugins();\n }, [fetchRemotePlugins]);\n\n return {\n plugins,\n loading,\n error,\n refreshing,\n refresh,\n };\n}\n","import { apiFetch } from '@ateam-ai-mobile/core';\nimport { getCoreApiUrl } from '@ateam-ai-mobile/core';\n\n/**\n * Summary returned by cp.listContextPlugins — no `render` field.\n * Used for the plugin grid (name, icon, description).\n */\nexport interface PluginSurfaceMeta {\n type: 'drawer' | 'card' | 'fullscreen' | 'header' | 'ambient' | 'nudge';\n visibility?: 'always' | 'user' | 'engine';\n trigger?: { from?: string; gesture?: string };\n icon?: string;\n title?: string;\n subtitle?: string;\n privacy?: boolean;\n prompt?: string;\n}\n\nexport interface PluginSummary {\n id: string;\n name: string;\n version: string;\n description?: string;\n _source?: string; // \"mcp\" for connector plugins\n _connectorId?: string;\n /** Declarative surface placement — added by the surface-SDK migration */\n surface?: PluginSurfaceMeta;\n}\n\n/**\n * Native capability flags (declared in manifest).\n */\nexport interface NativeCapabilities {\n camera?: boolean;\n location?: boolean;\n biometrics?: boolean;\n offline?: boolean;\n backgroundTasks?: boolean;\n fileSystem?: boolean;\n notifications?: boolean;\n bluetooth?: boolean;\n haptics?: boolean;\n}\n\n/**\n * Lifecycle hooks for service/hybrid plugins.\n */\nexport interface PluginHooks {\n onAppStart?: boolean;\n onPushReceived?: boolean;\n onConnectivity?: boolean;\n onForeground?: boolean;\n onBackground?: boolean;\n}\n\n/**\n * Render mode — polymorphic.\n * - 'iframe': existing WebView (backward compat)\n * - 'react-native': native RN component only\n * - 'adaptive': both web and native, host picks best\n */\nexport type PluginRenderMode =\n | { mode: 'iframe'; iframeUrl: string; bundleUrl?: string; bundleHash?: string; external?: boolean }\n | { mode: 'react-native'; component: string; bundleUrl?: string; bundleHash?: string; bundleId?: string }\n | {\n mode: 'adaptive';\n iframe: { iframeUrl: string; external?: boolean };\n reactNative: { component: string; bundleUrl?: string; bundleHash?: string; bundleId?: string };\n };\n\n/**\n * Full manifest returned by cp.getContextPlugin.\n * Extended to support native mobile plugins alongside existing web plugins.\n */\nexport interface PluginManifest extends PluginSummary {\n type?: 'ui' | 'service' | 'hybrid';\n render: PluginRenderMode;\n native?: NativeCapabilities;\n hooks?: PluginHooks;\n channels?: string[] | {\n events: string[];\n actions: string[];\n };\n capabilities?: Record<string, unknown>;\n _mcpEndpoint?: string;\n}\n\n/**\n * Determine if a manifest supports native rendering.\n */\nexport function supportsNative(manifest: PluginManifest): boolean {\n const mode = manifest.render?.mode;\n return mode === 'react-native' || mode === 'adaptive';\n}\n\n/**\n * Get the native component ID from a manifest (if it has native support).\n */\nexport function getNativeComponentId(manifest: PluginManifest): string | null {\n if (manifest.render?.mode === 'react-native') return manifest.render.component;\n if (manifest.render?.mode === 'adaptive') return manifest.render.reactNative?.component;\n return null;\n}\n\n/**\n * Get the native bundle URL from a manifest (if it has native support).\n */\nexport function getNativeBundleUrl(manifest: PluginManifest): string | null {\n // Check direct bundleUrl on manifest or within render structure\n const direct = (manifest as any).bundleUrl;\n if (direct) return direct;\n\n const render = manifest.render;\n if (render.mode === 'react-native') return render.bundleUrl || null;\n if (render.mode === 'adaptive') return render.reactNative?.bundleUrl || null;\n\n return null;\n}\n\n/**\n * Get the SHA-256 integrity hash for a native bundle, if the server provided one.\n * Used post-download to verify the bundle bytes weren't tampered in transit\n * — a MITM-replaced bundle gets `eval`'d as RN code with full capability access.\n */\nexport function getNativeBundleHash(manifest: PluginManifest): string | null {\n const direct = (manifest as any).bundleHash;\n if (direct) return direct;\n const render = manifest.render;\n if (render.mode === 'react-native') return (render as any).bundleHash || null;\n if (render.mode === 'adaptive') return render.reactNative?.bundleHash || null;\n return null;\n}\n\n/**\n * Check if a manifest can be rendered as a WebView (iframe or adaptive with iframe).\n */\nexport function supportsWebView(manifest: PluginManifest): boolean {\n return manifest.render?.mode === 'iframe' || manifest.render?.mode === 'adaptive';\n}\n\n/**\n * List all available UI plugins via MCP JSON-RPC.\n * Returns summary data only (no render field).\n * Protocol: cp.listContextPlugins → core plugins + MCP connector plugins.\n */\nexport async function listPlugins(): Promise<PluginSummary[]> {\n const res = await apiFetch<{\n result: {\n content: Array<{ type: string; text: string }>;\n structuredContent?: { plugins?: PluginSummary[] };\n };\n }>('/mcp', {\n method: 'POST',\n body: JSON.stringify({\n jsonrpc: '2.0',\n id: Date.now(),\n method: 'tools/call',\n params: {\n name: 'cp.listContextPlugins',\n arguments: {},\n },\n }),\n });\n\n // Accept multiple response formats (matches web mcpClient.js fallback chain)\n const structured = res.result?.structuredContent?.plugins;\n if (structured) return structured;\n\n const text = res.result?.content?.[0]?.text;\n if (!text) return [];\n try {\n const parsed = JSON.parse(text);\n return parsed.plugins || parsed || [];\n } catch {\n return [];\n }\n}\n\n/**\n * Get a specific plugin's FULL manifest (with render.iframeUrl).\n * Protocol: cp.getContextPlugin({ id }) → resolves iframeUrl for core/MCP/external.\n *\n * IMPORTANT: The backend parameter is `id`, not `pluginId`.\n */\nexport async function getPlugin(pluginId: string): Promise<PluginManifest | null> {\n const res = await apiFetch<{\n result: {\n content: Array<{ type: string; text: string }>;\n structuredContent?: PluginManifest;\n };\n }>('/mcp', {\n method: 'POST',\n body: JSON.stringify({\n jsonrpc: '2.0',\n id: Date.now(),\n method: 'tools/call',\n params: {\n name: 'cp.getContextPlugin',\n arguments: { id: pluginId }, // Backend expects { id }, NOT { pluginId }\n },\n }),\n });\n\n // Accept structured or text response\n if (res.result?.structuredContent) return res.result.structuredContent;\n\n const text = res.result?.content?.[0]?.text;\n if (!text) return null;\n try {\n const parsed = JSON.parse(text);\n // Backend may return error object\n if (parsed.error) {\n console.warn('[getPlugin] Backend error:', parsed.error);\n return null;\n }\n return parsed;\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve plugin iframe URL to a full URL for WebView.\n * Matches the web frontend's PluginHost.jsx logic:\n * - External (absolute http): append ?tenant=\n * - MCP plugin (/mcp-ui/...): inject tenant into path → /mcp-ui/{tenant}/{connectorId}/...\n * - Core plugin (/assets/...): prepend getCoreApiUrl()\n */\nexport function resolvePluginUrl(manifest: PluginManifest, tenant?: string): string {\n // Extract iframeUrl from different render modes.\n // Adaptive manifests in the wild use a flat shape — `render.iframeUrl` —\n // not the nested `render.iframe.iframeUrl` from the type. Accept both.\n const render = manifest.render as any;\n let url: string | undefined;\n if (render?.mode === 'iframe') {\n url = render.iframeUrl;\n } else if (render?.mode === 'adaptive') {\n url = render.iframeUrl || render.iframe?.iframeUrl;\n }\n // react-native only → no URL\n if (!url) return '';\n\n // Already absolute URL (external plugins like Voice)\n if (url.startsWith('http')) {\n const sep = url.includes('?') ? '&' : '?';\n return tenant ? `${url}${sep}tenant=${encodeURIComponent(tenant)}` : url;\n }\n\n // MCP plugin — inject tenant into path: /mcp-ui/{connectorId}/... → /mcp-ui/{tenant}/{connectorId}/...\n if (tenant && url.startsWith('/mcp-ui/')) {\n const tenantUrl = url.replace('/mcp-ui/', `/mcp-ui/${encodeURIComponent(tenant)}/`);\n return `${getCoreApiUrl()}${tenantUrl}`;\n }\n\n // Core plugin — prepend core API base\n return `${getCoreApiUrl()}${url}`;\n}\n"],"mappings":"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","names":["View","Text","Pressable","TextInput","ActivityIndicator","StyleSheet","useMemo","React","useEffect","useMemo","useRef","useEffect","View","Text","Pressable","StyleSheet","Animated","Platform","Svg","ada","View","StyleSheet","Animated","styles","jsx","jsxs","Platform","Animated","useEffect","styles","Pressable","StyleSheet","View","Text","Svg","ada","View","Text","Pressable","StyleSheet","Platform","ada","borderRadius","jsx","jsxs","fontFam","Platform","Pressable","styles","View","Text","StyleSheet","useEffect","useRef","View","Text","Pressable","StyleSheet","Animated","Platform","Svg","Path","ada","jsx","jsxs","fontFam","styles","cleanup","useEffect","jsx","jsxs","useEffect","React","useEffect","useMemo","useRef","useCallback","useMemo","useRef","useEffect","pluginCommandEmitter","useState","useEffect","useState","useEffect","useEffect","useState","useCallback","apiFetch"]}
|
|
1
|
+
{"version":3,"sources":["../src/sdk/device-state.ts","../src/tools/platformTools.ts","../src/tools/sessionSync.ts","../src/tools/serverCalls.ts","../src/hooks/usePlugin.ts","../src/hooks/useRemotePlugins.ts"],"sourcesContent":["/**\n * @adas/device-state — Device Bridge v2, the SEMANTIC layer.\n *\n * Intended repo path:\n * packages/plugin-runtime/src/sdk/device-state.ts\n * Intended module specifier for plugin bundles:\n * '@adas/device-state' (NOT '@adas/plugin-sdk')\n *\n * ─────────────────────────────────────────────────────────────────────────\n * WHICH LAYER AM I IN?\n *\n * props.device.* MECHANICAL. \"Do this to the hardware, now.\"\n * VERB names: takePhoto, getCurrent, scanBarcode,\n * readOnce, authenticate, pickImage.\n * Returns a BARE value. THROWS when it cannot.\n * Declared in the manifest as `capabilities: { camera: true }`\n * (the legacy `native: {}` field is still honoured).\n * Lives in '@adas/plugin-sdk'.\n *\n * props.deviceState.* SEMANTIC. \"What is true right now, and how well do\n * we know it?\"\n * STATE names: getSnapshot, subscribe, start, stop.\n * Returns a SNAPSHOT carrying freshness + confidence,\n * and able to say `unknown` in every field.\n * A read NEVER throws for absence.\n * Declared in the manifest as `stateDomains: ['visual']`.\n * Lives in '@adas/device-state' (this file).\n *\n * The two containers are the discriminator. Every semantic call in a plugin\n * begins with the eight characters `deviceState.`; every mechanical call\n * begins with `native.`. There is no line of code where the layer is\n * ambiguous, and no object on which both appear.\n *\n * This is a deliberate departure from\n * Docs/device-bridge-v2/DEVICE_BRIDGE_V2_IMPLEMENTATION_GROUNDING_2026-08-07.md\n * §\"Migration strategy\", which proposed adding `visual`, `spatial`,\n * `connectivityState`… as fields ON `PluginNative`. See the design note at the\n * bottom of this file for why that layout is rejected and what it costs.\n * ─────────────────────────────────────────────────────────────────────────\n *\n * PROVENANCE MARKERS used throughout. They are not decoration — the freeze\n * really does fix some of this and really does leave the rest open, and an\n * implementor needs to know which is which before changing a line:\n *\n * FROZEN — API_FREEZE 2026-08-07 §15 lists it as a frozen candidate.\n * V0.1 — \"v0.1 freeze candidate; may change only when implementation\n * grounding proves a field impossible/non-portable.\"\n * OPEN — the docs explicitly leave it undecided. Do not quietly close it.\n * DECISION — not in the docs at all; decided here, and reversible.\n */\n\n/* eslint-disable @typescript-eslint/no-empty-interface */\n\n// ═════════════════════════════════════════════════════════════════════════\n// 1. Common metadata model\n// FROZEN (API_FREEZE §1, §15 \"Common metadata model: frozen candidate\").\n// Shared verbatim by all eight domains. Do not fork a per-domain variant.\n// ═════════════════════════════════════════════════════════════════════════\n\n/** FROZEN (API_FREEZE §1). The eight domain keys. */\nexport type DeviceBridgeDomain =\n | 'spatial'\n | 'visual'\n | 'activity'\n | 'deviceContext'\n | 'connectivity'\n | 'capability'\n | 'environment'\n | 'interaction';\n\nexport type FreshnessState = 'fresh' | 'aging' | 'stale' | 'unknown';\n\n/**\n * FROZEN. Both fields are nullable on purpose: \"we have no observation at all\"\n * has to be expressible without inventing a timestamp.\n *\n * `aging` is a real third state, not a rounding of fresh/stale — a consumer\n * that only branches on fresh-vs-stale is misreading the contract.\n *\n * API_FREEZE §1 \"Why observedAt + ageMs\": the host may compute `ageMs` at read\n * time, so it can be more accurate than `Date.now() - observedAt` measured on\n * the JS side after a bridge hop. Read `ageMs` when you have it.\n */\nexport interface Freshness {\n state: FreshnessState;\n /** Epoch ms of the observation itself. `null` = never observed. */\n observedAt: number | null;\n /** Age at read time. `null` = never observed. */\n ageMs: number | null;\n}\n\nexport type ConfidenceLevel = 'high' | 'medium' | 'low' | 'unknown';\n\n/**\n * FROZEN. `level` is mandatory and ordinal; `score` is optional and exists\n * only \"when a stable numeric estimate exists\" (API_FREEZE §1).\n *\n * The asymmetry is the point: native must not manufacture 0.5 to fill a field.\n * A missing `score` is information — it says the estimator has no calibrated\n * number, only a bucket.\n */\nexport interface Confidence {\n level: ConfidenceLevel;\n /** 0..1. Omitted when no stable numeric estimate exists. */\n score?: number;\n}\n\n/**\n * FROZEN. Every snapshot in v2 carries exactly one of these.\n *\n * `freshness` required, `confidence` optional — because some domains report\n * exact OS facts (battery level, audio route) where a confidence is\n * meaningless, and others report inference (activity, indoor/outdoor) where it\n * is mandatory. Cross-domain review: \"Freshness is mandatory for dynamic\n * state. Confidence is mandatory for inferred/estimated state; optional for\n * exact OS facts.\"\n *\n * NOTE (OPEN): freshness is per-SNAPSHOT, never per-field. So a snapshot\n * cannot today say \"lighting is fresh but acoustics is stale\". The\n * architecture doc raised this for Spatial and Environment and never resolved\n * it. Do not work around it by inventing a second freshness field.\n */\nexport interface StateQuality {\n freshness: Freshness;\n confidence?: Confidence;\n}\n\n// ── Support / permission / availability / restriction ──────────────────────\n// FROZEN (API_FREEZE §1). Four INDEPENDENT axes. The capability doc is blunt:\n// \"Do not collapse all failure modes into a single boolean.\" A camera can be\n// supported + granted + busy, and each of those three answers leads the caller\n// somewhere different.\n\nexport type SupportState = 'supported' | 'unsupported' | 'unknown';\n\nexport type PermissionState =\n | 'granted'\n | 'denied'\n | 'restricted'\n | 'notDetermined'\n | 'limited'\n | 'unavailable'\n | 'unknown';\n\nexport type AvailabilityState =\n | 'available'\n | 'busy'\n | 'temporarilyUnavailable'\n | 'unavailable'\n | 'unknown';\n\nexport type RestrictionState =\n | 'none'\n | 'backgroundRestricted'\n | 'systemRestricted'\n | 'policyRestricted'\n | 'lowPowerRestricted'\n | 'unknown';\n\n// ── Events ────────────────────────────────────────────────────────────────\n\n/**\n * FROZEN (API_FREEZE core rule 4): \"Events are meaningful semantic changes,\n * never raw sensor streams.\" If you find yourself wanting an event per frame\n * or per GPS fix, you are in the wrong layer.\n */\nexport interface StateEvent<TName extends string = string, TPayload = unknown> {\n domain: DeviceBridgeDomain;\n name: TName;\n /** Epoch ms. */\n timestamp: number;\n /**\n * OPEN: per-event payload schemas are not specified anywhere in the three\n * design docs. Defaults to `unknown` rather than `any` so a consumer is forced\n * to narrow it rather than read a field that may not exist. A domain that\n * decides its schemas (spatial — see `SpatialStateEvent`) passes `TPayload`,\n * which is the freeze's own second parameter, dropped here until one did.\n */\n payload?: TPayload;\n}\n\nexport interface Subscription {\n unsubscribe(): void;\n}\n\nexport type StateListener<TName extends string = string> = (\n event: StateEvent<TName>,\n) => void;\n\n/**\n * VOCABULARY §\"Event Naming Rule\", expressed as a type.\n *\n * Four domains (visual, activity, spatial, deviceContext) have NO frozen event\n * vocabulary — the vocabulary doc publishes \"Initial … Events\" lists for\n * connectivity, capability, interaction and environment only. Rather than mint\n * names those docs deliberately did not mint, their events are typed against\n * the naming RULE. A wrong-shaped name is a compile error; the specific\n * vocabulary stays open, as the docs left it — except where a DECISION closes\n * it (spatial, see `SpatialEvent`), and that list is checked against the rule\n * through `AssertConforming`.\n */\nexport type ConformingEventName =\n | `${string}Changed`\n | `${string}Started`\n | `${string}Stopped`\n | `${string}Available`\n | `${string}Unavailable`\n | `${string}StateStale`;\n\n/**\n * A closed list of names, checked against the naming rule at compile time.\n * Wrap a decided vocabulary in this and a name like `turnCompleted` stops\n * compiling instead of reaching a plugin.\n */\nexport type AssertConforming<T extends ConformingEventName> = T;\n\n// ── Errors ────────────────────────────────────────────────────────────────\n\n/** FROZEN (API_FREEZE §12). */\nexport type DeviceBridgeErrorCode =\n | 'UNSUPPORTED'\n | 'PERMISSION_DENIED'\n | 'RESTRICTED'\n | 'BUSY'\n | 'TEMPORARILY_UNAVAILABLE'\n | 'STALE'\n | 'TIMEOUT'\n | 'CANCELLED'\n | 'INTERNAL';\n\n/**\n * FROZEN shape (API_FREEZE §12).\n *\n * READ THE SCOPE LINE CAREFULLY. This is for FAILED OPERATIONS —\n * `visual.capture()`, `visual.start()`. It is NOT how the surface reports that\n * a camera is denied or a sensor is missing: \"Expected state should still be\n * exposed in snapshots.\"\n *\n * So: `getSnapshot()` does not throw this. `capture()` and `start()` do.\n * That split is the whole no-fabrication contract in one sentence — a read\n * always returns something honest, an action either happens or explains\n * itself in a typed code.\n */\nexport class DeviceBridgeError extends Error {\n readonly code: DeviceBridgeErrorCode;\n readonly domain?: DeviceBridgeDomain;\n /** Whether retrying could plausibly succeed (BUSY yes, UNSUPPORTED no). */\n readonly recoverable?: boolean;\n\n constructor(\n code: DeviceBridgeErrorCode,\n message: string,\n opts?: { domain?: DeviceBridgeDomain; recoverable?: boolean },\n ) {\n super(message);\n this.name = 'DeviceBridgeError';\n this.code = code;\n this.domain = opts?.domain;\n this.recoverable = opts?.recoverable;\n }\n}\n\n// ── Sessions and the lifecycle pattern ────────────────────────────────────\n\nexport interface StateSession {\n id: string;\n /** The intent this session was started with. */\n intent: string;\n startedAt: number;\n}\n\nexport interface StartOptions<TIntent extends string> {\n /**\n * MANDATORY, and that is the single most important line in this file.\n *\n * You cannot open a v2 session without naming what it is FOR. Native then\n * owns sampling rate, resolution, buffering, fusion and power — API_FREEZE\n * core rule 6. `start()` means \"maintain state according to this intent\",\n * not \"open a particular native API\" (§2).\n *\n * Contrast the mechanical layer, where `camera.takePhoto()` is a legal,\n * complete, intent-free call. That difference is visible at every call site.\n */\n intent: TIntent;\n /**\n * DECISION (not frozen). §13 requires bounded timeout/cancellation for\n * session starts and says chain STOP must propagate to native, but leaves\n * the plumbing \"implementation-specific\". `AbortSignal` is what\n * `DeviceToolContext.signal` already carries in this SDK, so reuse it rather\n * than invent a second cancellation idiom.\n */\n signal?: AbortSignal;\n}\n\n/**\n * FROZEN pattern (API_FREEZE §2), reproduced as documentation. The eight\n * concrete domains below do NOT extend this — each spells out exactly the\n * methods it has, because §11 warns \"domains should not be forced to implement\n * meaningless lifecycle methods\" and three domains genuinely have no intent\n * vocabulary at all.\n *\n * Two invariants apply to every domain:\n * - `getSnapshot()` reads CACHED state. It must not trigger expensive raw\n * sensing (no camera open, no network probe, no GPS cold start).\n * - \"Multiple plugins may eventually share one native engine; session\n * ownership must not imply duplicate sensor pipelines.\" (OPEN: the\n * arbitration mechanism is undefined. See DESIGN NOTE 3.)\n */\nexport interface StatefulDomain<\n TSnapshot,\n TIntent extends string = never,\n TEvent extends string = string,\n> {\n getSnapshot(): Promise<TSnapshot>;\n start?(options: StartOptions<TIntent>): Promise<StateSession>;\n stop?(sessionId?: string): Promise<void>;\n subscribe?(listener: StateListener<TEvent>): Subscription;\n}\n\n/**\n * Why every read is `Promise<T>` even though it is a cached read.\n *\n * Not because it samples — it must not. Because it crosses the RN bridge, and\n * this SDK has no synchronous JSI module today (verified: everything in\n * PluginNative except haptics/connectivity.isOnline is Promise-based). The\n * grounding doc's own target for a warm cached snapshot is p50 < 10 ms /\n * p95 < 30 ms, which is an async-bridge budget, not a sampling budget.\n */\n\n// ═════════════════════════════════════════════════════════════════════════\n// 2. Domain 1 — Spatial State\n// ═════════════════════════════════════════════════════════════════════════\n\n/**\n * FROZEN (API_FREEZE §3). Copied verbatim, not designed here.\n *\n * This landed as a placeholder first, on the belief that §3's field schema had\n * never been written. It had — the placeholder was a gap in what got read, not\n * a gap in the freeze. Worth recording, because \"the docs leave this open\" and\n * \"I did not find it\" produce identical-looking comments and only one of them\n * is a reason to invent a shape.\n */\nexport type SpatialIntent =\n | 'navigation'\n | 'fitness'\n | 'geofence'\n | 'lowPowerTracking'\n | 'preciseLocate';\n\n/** FROZEN (API_FREEZE §3). */\nexport interface SpatialPoint {\n latitude: number;\n longitude: number;\n altitude?: number | null;\n /** Metres. `null` when the platform gives no estimate — never 0, which\n * would read as a perfect fix. */\n accuracyM?: number | null;\n timestamp: number;\n}\n\n/** FROZEN (API_FREEZE §3). */\nexport interface SpatialSnapshot {\n /** `null` when there is no usable fix. Not a zero coordinate — (0,0) is a\n * real place in the Gulf of Guinea. */\n position: SpatialPoint | null;\n movement: {\n speedMps: number | null;\n bearingDeg: number | null;\n distanceRecentM: number | null;\n state?: 'stationary' | 'moving' | 'unknown';\n };\n orientation: {\n headingDeg: number | null;\n headingAccuracyDeg?: number | null;\n pitchDeg?: number | null;\n rollDeg?: number | null;\n };\n /** Newest last. Empty when nothing has been recorded — the ring is a native\n * concern, and its window is chosen by intent. */\n recentPath: SpatialPoint[];\n changes?: {\n lastMeaningfulTurnDeg?: number | null;\n timestamp?: number | null;\n };\n quality: StateQuality;\n}\n\n/**\n * DECISION (not frozen) — the spatial event vocabulary. Six names, all ending\n * in a suffix the naming rule allows, which `AssertConforming` enforces.\n *\n * Drawn from the architecture doc's spatial candidates (meaningful movement,\n * meaningful turn, location quality degraded/recovered, state became stale)\n * and named after the SNAPSHOT fields they report, so an event and a read use\n * one vocabulary: `lastMeaningfulTurnChanged` is `changes.lastMeaningfulTurnDeg`\n * changing. The names an earlier note floated (`turnCompleted`, `arrived`,\n * `headingLost`) are rejected — the first two fail the rule, and \"arrived\" is a\n * destination, which the vocabulary's Governance puts above Device Bridge.\n */\nexport type SpatialEvent = AssertConforming<\n | 'movementStarted'\n | 'movementStopped'\n | 'lastMeaningfulTurnChanged'\n | 'positionConfidenceChanged'\n | 'positionChanged'\n | 'spatialStateStale'\n>;\n\n/** A spatial event: the frozen envelope, with its payload always present. */\ntype SpatialEventOf<TName extends SpatialEvent, TPayload> =\n StateEvent<TName, TPayload> & { domain: 'spatial'; payload: TPayload };\n\n/**\n * DECISION — what each spatial event carries. Only the facts of the\n * transition, in the snapshot's own field names; everything else is one\n * `getSnapshot()` away, which keeps two shapes of the same fact from drifting.\n *\n * `timestamp` on the envelope is when the transition was RECOGNISED. Where a\n * hold delays that on purpose, `since` carries the true onset.\n */\nexport type SpatialStateEvent =\n /** `movement.state` became `moving` and held (navigation: 3 s). */\n | SpatialEventOf<'movementStarted', {\n previous: 'stationary' | 'unknown';\n speedMps: number | null;\n bearingDeg: number | null;\n since: number;\n }>\n /** `moving` → `stationary`, held (navigation: 5 s). Never fired on moving →\n * `unknown`: losing the speed is not evidence of stopping. */\n | SpatialEventOf<'movementStopped', { previous: 'moving'; since: number }>\n /** A new completed turn. Signed: positive = clockwise = right. */\n | SpatialEventOf<'lastMeaningfulTurnChanged', { lastMeaningfulTurnDeg: number; timestamp: number }>\n /** The GPS fix band (high ≤10 m, medium ≤50 m, else low) changed and held.\n * The fix alone — a compass calibration change is not a position event. */\n | SpatialEventOf<'positionConfidenceChanged', {\n level: ConfidenceLevel;\n previous: ConfidenceLevel;\n accuracyM: number | null;\n direction: 'dropped' | 'recovered';\n }>\n /** Moved at least max(profile floor, 2 × accuracyM) from the last reported\n * position, so GPS jitter never fires it. The session's first fix fires\n * with `previous: null`. */\n | SpatialEventOf<'positionChanged', {\n position: SpatialPoint;\n previous: SpatialPoint | null;\n distanceM: number | null;\n }>\n /** `quality.freshness.state` became `stale`. Once; re-armed by a fresh read. */\n | SpatialEventOf<'spatialStateStale', { observedAt: number | null; ageMs: number | null }>;\n\nexport interface SpatialDomain {\n getSnapshot(): Promise<SpatialSnapshot>;\n start(options: StartOptions<SpatialIntent>): Promise<StateSession>;\n stop(sessionId?: string): Promise<void>;\n /**\n * DECISION: the six `SpatialEvent`s, typed per name — narrow on `e.name` and\n * `e.payload` is that event's shape.\n *\n * Events flow only while a spatial session is open; subscribing opens no\n * sensor. They are derived from the same snapshot `getSnapshot()` returns: a\n * listener that re-reads inside the callback sees the snapshot its event came\n * from. A state change must hold, on at least two fixes, before it fires.\n *\n * From a device tool's `ctx.deviceState` this refuses (RESTRICTED): a call\n * has no lifetime to own the listener.\n */\n subscribe(listener: (event: SpatialStateEvent) => void): Subscription;\n}\n\n// ═════════════════════════════════════════════════════════════════════════\n// 3. Domain 2 — Visual State\n//\n// Visual is NOT \"the camera API\". It is a native-maintained visual timeline\n// plus camera/capture state. Everything interpretive — what is in the scene,\n// what the sign says, which corridor to take — sits ABOVE Device Bridge and is\n// not modelled here.\n// ═════════════════════════════════════════════════════════════════════════\n\n/** FROZEN (API_FREEZE §4) + VOCABULARY §\"Visual Intents\". Nine values. */\nexport type VisualIntent =\n | 'navigation'\n | 'document'\n | 'ocr'\n | 'shopping'\n | 'inspection'\n | 'accessibility'\n | 'stream'\n | 'highQuality'\n | 'lowPower';\n\n/**\n * Media crosses by REFERENCE, never as inline bytes (core rule 3).\n *\n * AN ID AND NOTHING ELSE. The `uri` field is gone (2026-08-16), and its\n * removal answers the question this comment used to record as OPEN — \"how does\n * a plugin turn a handle into pixels?\" It doesn't. Ever.\n *\n * Frames now live in a native ring keyed by id and are never written to disk.\n * `native.vision.readText/describe/assessQuality` take this id and resolve it\n * INSIDE the trusted layer; the pixels do not cross into plugin code, so a\n * solution's bundle cannot copy, upload or retain them. That is what makes\n * \"frames never leave the phone\" enforceable rather than a promise.\n *\n * Do not add `uri` or `bytes` back. A plugin that believes it needs the pixels\n * needs a new method on `native.vision` instead — one whose implementation\n * stays on this side of the boundary.\n */\nexport interface MediaHandle {\n id: string;\n mimeType?: string;\n /**\n * Epoch ms after which this id no longer resolves — the frame's real\n * eviction time from the native ring, not a guess.\n *\n * Past it, `native.vision` throws rather than returning an empty result:\n * \"I cannot look at that frame\" and \"I looked and found nothing\" are\n * different answers and must never be confused by a caller about to speak.\n * In practice a caller rarely hits this, because the timeline is clamped to\n * native retention — see `visualEngine.effectiveWindowMs()`.\n */\n expiresAt?: number | null;\n}\n\n/**\n * V0.1. Per-frame usability is CATEGORICAL, not numeric — 'degraded' rather\n * than a blur score. Coarse honest classes beat false precision; the same\n * principle the connectivity doc states outright for connection quality.\n *\n * Every member carries `'unknown'` so a platform that cannot assess blur says\n * so instead of defaulting to 'good'.\n */\nexport interface VisualFrameQuality {\n blur?: 'good' | 'degraded' | 'unknown';\n exposure?: 'good' | 'dark' | 'bright' | 'unknown';\n /** `null` = cannot tell. Distinct from `false` = checked, not obstructed. */\n obstructed?: boolean | null;\n}\n\nexport interface VisualFrameRef {\n handle: MediaHandle;\n timestamp: number;\n orientation?: { headingDeg?: number; pitchDeg?: number; rollDeg?: number };\n quality?: VisualFrameQuality;\n}\n\nexport interface VisualSnapshot {\n camera: {\n available: boolean;\n /**\n * Independent of `available` on purpose. A camera can be present and\n * permitted (`available: true`) while no session is running\n * (`active: false`) — that is a different situation from a camera the app\n * cannot use at all, and Guide has to say different things about them.\n *\n * DRIFT (flagged, not resolved): the architecture doc's conceptual block\n * also carried `camera.orientation`; the frozen §4 block does not. Not\n * added here.\n */\n active: boolean;\n facing?: 'front' | 'back' | 'unknown';\n };\n /**\n * `null` when there is no usable current frame.\n *\n * This is the anti-fabrication field. The alternative implementation —\n * returning the last frame you happen to still hold — is precisely how a\n * guidance loop ends up describing a corridor the user left thirty seconds\n * ago. If it is not current, it is `null`, and the timeline below is where\n * you go looking for history, knowingly.\n */\n latest: VisualFrameRef | null;\n /**\n * TIME-based, not frame-count-based. Default window ~5s; documented bounded\n * range roughly 1–15s, selected by profile/intent. Native decides how\n * densely to retain frames inside the window and evicts everything outside\n * it automatically.\n *\n * `windowMs` is exposed; FPS knobs are NOT, and must never be added\n * (API_FREEZE §4). If a plugin wants \"more frames\", it declares a different\n * intent — it does not turn a dial.\n */\n recentTimeline: { windowMs: number; frames: VisualFrameRef[] };\n quality: StateQuality;\n}\n\nexport interface VisualCaptureResult {\n frame: VisualFrameRef;\n}\n\n/**\n * OPEN, worth knowing before you use it: whether `capture({intent})` may pass\n * an intent DIFFERENT from the running session's intent is not addressed\n * anywhere in the three docs.\n */\nexport interface VisualCaptureOptions {\n intent: VisualIntent;\n /** §13: capture must support bounded timeout/cancellation. */\n signal?: AbortSignal;\n timeoutMs?: number;\n}\n\nexport interface VisualDomain {\n /**\n * Cached read. Does NOT open the camera and does NOT take a picture.\n *\n * This is the call a guidance loop makes on every turn. The hot path is:\n * native camera pipeline → rolling native timeline → cached visual state →\n * one snapshot read from RN. \"Avoid repeatedly starting the camera or\n * capturing a new image for every plugin-tool call during an interactive\n * session.\"\n */\n getSnapshot(): Promise<VisualSnapshot>;\n\n /** Maintain visual state according to this intent. Not \"open the camera\". */\n start(options: StartOptions<VisualIntent>): Promise<StateSession>;\n\n /**\n * An intentional, one-shot, high-quality-ish acquisition. THROWS\n * DeviceBridgeError on failure — it is an operation, not a read.\n *\n * If you are about to call this once per conversational turn, stop: that is\n * `getSnapshot()`'s job, and doing it here is the frame-timer pattern this\n * whole layer exists to replace.\n */\n capture(options: VisualCaptureOptions): Promise<VisualCaptureResult>;\n\n /**\n * OPEN (doc conflict): the architecture doc shows `visual.subscribe(events)`\n * — a filter — while API_FREEZE shows `visual.subscribe(listener)`. The\n * freeze is followed here. Unresolved.\n *\n * OPEN: there is no \"Initial Visual Events\" list anywhere. The candidate set\n * in prose (camera became available/unavailable, frame quality degraded/\n * recovered, camera appears obstructed, significant scene change, visual\n * session suspended/resumed, visual state became stale) is NOT a minted\n * vocabulary. Typed against the naming rule only.\n */\n subscribe(listener: StateListener<ConformingEventName>): Subscription;\n\n stop(sessionId?: string): Promise<void>;\n}\n\n// ═════════════════════════════════════════════════════════════════════════\n// 4. Domain 3 — Human Activity State\n// ═════════════════════════════════════════════════════════════════════════\n\n/** FROZEN (API_FREEZE §5, VOCABULARY §\"Human Activity Values\"). */\nexport type HumanActivity =\n | 'stationary'\n | 'walking'\n | 'running'\n | 'cycling'\n | 'driving'\n | 'unknown';\n\n/** FROZEN. Independent of HumanActivity — walking+pocket, driving+mounted. */\nexport type DevicePlacement =\n | 'handheld'\n | 'pocket'\n | 'table'\n | 'mounted'\n | 'unknown';\n\n/**\n * FROZEN. Note `driving` is BOTH an intent here and a HumanActivity value\n * above. The docs define no relationship between the two and do not flag the\n * collision: declaring intent `driving` does not assert the user is driving.\n */\nexport type ActivityIntent =\n | 'navigation'\n | 'fitness'\n | 'driving'\n | 'safety'\n | 'accessibility'\n | 'lowPower';\n\nexport interface ActivitySnapshot {\n human: {\n state: HumanActivity;\n /** `null` when the start of the current activity is not known. */\n since: number | null;\n /** REQUIRED, unlike StateQuality.confidence — an inferred activity may\n * never be handed over without saying how sure the inference is. */\n confidence: Confidence;\n };\n devicePose: {\n placement: DevicePlacement;\n /** V0.1. These four values appear only in the freeze TS, not in the\n * vocabulary doc — unlike the placement values. Mild drift, flagged. */\n orientation?: 'portrait' | 'landscape' | 'flat' | 'unknown';\n confidence: Confidence;\n };\n /**\n * Advisory only. `likelyNext` is a hint and never a fact.\n * OPEN: transition names are bare `string` — no controlled vocabulary exists\n * anywhere, which sits awkwardly beside \"Intents are not arbitrary strings\".\n */\n transition?: {\n current?: string | null;\n likelyNext?: string | null;\n confidence?: Confidence;\n };\n /** OPEN: no window length or cap is specified for Activity (Spatial gets\n * ~10–20s, Visual ~5s). Required array — an empty one is a real answer. */\n recentTransitions: Array<{ name: string; timestamp: number }>;\n quality: StateQuality;\n}\n\nexport interface ActivityDomain {\n getSnapshot(): Promise<ActivitySnapshot>;\n /**\n * OPEN: §11's naming list shows only `activity.getSnapshot()`, and §2 makes\n * start/stop optional. Whether Activity is session-based or a passive\n * always-on domain is genuinely unstated. Included here because the six\n * frozen ActivityIntent values would otherwise have no seam to arrive\n * through — but this is a DECISION, not a reading of the freeze.\n */\n start(options: StartOptions<ActivityIntent>): Promise<StateSession>;\n stop(sessionId?: string): Promise<void>;\n /** OPEN: no \"Initial Activity Events\" list exists. */\n subscribe(listener: StateListener<ConformingEventName>): Subscription;\n}\n\n// ═════════════════════════════════════════════════════════════════════════\n// 5. Domain 4 — Device Context\n//\n// SCOPE WARNING. The early architecture draft put lifecycle, audio route and\n// hardware availability here. The freeze REMOVED all three: app foreground/\n// background and active channels are canonicalized under Interaction; hardware\n// support/permission moved to Capability. Frozen Device Context is power +\n// thermal + resources, and nothing else.\n// ═════════════════════════════════════════════════════════════════════════\n\nexport interface DeviceContextSnapshot {\n power: {\n /** 0..1 FRACTION, not a percentage. `null` = not known. */\n batteryLevel: number | null;\n charging: boolean | null;\n lowPowerMode: boolean | null;\n };\n thermal: {\n state: 'nominal' | 'fair' | 'serious' | 'critical' | 'unknown';\n };\n resources: {\n memoryPressure?: 'normal' | 'warning' | 'critical' | 'unknown';\n storagePressure?: 'normal' | 'warning' | 'critical' | 'unknown';\n };\n /** Confidence is legitimately omitted here — these are exact OS facts, not\n * estimates. Freshness still matters: a populated batteryLevel says nothing\n * about whether it was sampled a second or a minute ago. */\n quality: StateQuality;\n}\n\n/**\n * NO INTENT TYPE, and that is deliberate — not an oversight, and not a gap to\n * fill in later. There is no `DeviceContextIntent` in the freeze and no\n * \"Device Context Intents\" section in the vocabulary, because\n * (cross-domain review) \"A domain may expose an intent only if it changes\n * native implementation strategy in a meaningful way.\" Reading battery and\n * thermal does not.\n *\n * The direction of flow is the other way round: Device Context is an INPUT to\n * how OTHER domains satisfy their intents. Visual intent `navigation` +\n * device context `battery 5% + low-power mode` → native picks a lower-power\n * camera strategy while still honouring the Visual contract. The plugin does\n * not retune anything, and must not be asked to.\n *\n * Do not add `deviceContext.start({intent:'lowPower'})` because five other\n * domains have a lowPower intent.\n */\nexport interface DeviceContextDomain {\n getSnapshot(): Promise<DeviceContextSnapshot>;\n /**\n * OPEN: `DeviceContextDomain`'s interface body is never written in the\n * freeze, and no Device Context event names are frozen (the vocabulary has\n * \"Initial … Events\" for connectivity/capability/interaction/environment\n * only). DECISION: expose subscribe, because the candidate events —\n * low-power mode changed, charging changed, thermal pressure changed — are\n * exactly what an adaptive session needs and polling for them is wasteful.\n *\n * OPEN: \"battery crossed significant threshold\" and \"changed materially\"\n * have no numbers anywhere, and Device Context has no profiles to\n * parameterize them.\n */\n subscribe(listener: StateListener<ConformingEventName>): Subscription;\n}\n\n// ═════════════════════════════════════════════════════════════════════════\n// 6. Domain 5 — Connectivity State\n// ═════════════════════════════════════════════════════════════════════════\n\n/** FROZEN (API_FREEZE §7, VOCABULARY §\"Connectivity Intents\"). */\nexport type ConnectivityIntent =\n | 'lowLatencyConversation'\n | 'interactive'\n | 'bulkTransfer'\n | 'backgroundSync'\n | 'offlineResilient'\n | 'lowPower';\n\n/**\n * V0.1, and note it is FLAT — the connectivity design doc sketched a nested\n * shape (internet{…}, quality{…}, recent_history{…}) and the freeze flattened\n * it. Two unreconciled deltas, carried here as-is rather than merged:\n * - `bandwidthClass` appears in the freeze but in neither the design doc nor\n * the vocabulary's Connectivity Values.\n * - there is NO recent-history field, although the design doc requires native\n * to maintain recent transitions and leaves the default window OPEN.\n */\nexport interface ConnectivitySnapshot {\n /** NOTE: the design doc says \"online / offline / uncertain\"; the frozen\n * third value is `unknown`. The two words are never reconciled. */\n reachability: 'online' | 'offline' | 'unknown';\n transport: 'wifi' | 'cellular' | 'ethernet' | 'other' | 'none' | 'unknown';\n qualityClass:\n | 'excellent'\n | 'good'\n | 'degraded'\n | 'poor'\n | 'unusable'\n | 'unknown';\n stability: 'stable' | 'changing' | 'flapping' | 'unknown';\n /** `null` when no recent measurement exists. Explicitly preferred over\n * reporting a stale number: \"return unknown rather than stale precision.\" */\n latencyMs?: number | null;\n bandwidthClass?: 'high' | 'medium' | 'low' | 'unknown';\n metered?: boolean | null;\n constrained?: boolean | null;\n quality: StateQuality;\n}\n\n/** FROZEN v0.1 (VOCABULARY §\"Initial Connectivity Events\"). All ten. */\nexport type ConnectivityEvent =\n | 'connectivityOnline'\n | 'connectivityOffline'\n | 'transportChanged'\n | 'connectivityDegraded'\n | 'connectivityRecovered'\n | 'connectivityUnstable'\n | 'connectivityStable'\n | 'meteredStateChanged'\n | 'constrainedStateChanged'\n | 'connectivityStateStale';\n\nexport interface ConnectivityDomain {\n /**\n * A fast local cached read. It must NOT ping, and must NOT run a bandwidth\n * or speed test. Latency is sampled on native's own schedule, chosen by\n * intent, and cached with a timestamp.\n */\n getSnapshot(): Promise<ConnectivitySnapshot>;\n /**\n * OPEN: `ConnectivityDomain` is named in §11 but never defined, so whether\n * connectivity is session-based is unstated. DECISION: yes — six frozen\n * intents with documented behavioural differences (lowLatencyConversation\n * \"prioritize fresh latency knowledge\"; lowPower \"rely primarily on passive\n * OS signals and minimize active probes\") only mean something if there is a\n * way to declare one.\n */\n start(options: StartOptions<ConnectivityIntent>): Promise<StateSession>;\n stop(sessionId?: string): Promise<void>;\n subscribe(listener: StateListener<ConnectivityEvent>): Subscription;\n}\n\n// ═════════════════════════════════════════════════════════════════════════\n// 7. Domain 6 — Capability / Permission State\n//\n// The \"why\" domain. When another snapshot says `unknown`, this is where the\n// reason lives. Descriptive only — it never triggers a permission dialog.\n// ═════════════════════════════════════════════════════════════════════════\n\n/**\n * FROZEN. Four independent axes, deliberately not collapsed. The canonical\n * worked example from the capability doc: supported + granted + busy + none —\n * a camera you own, are allowed to use, and cannot have right now.\n */\nexport interface CapabilityStatus {\n support: SupportState;\n permission?: PermissionState;\n availability: AvailabilityState;\n restriction?: RestrictionState;\n}\n\nexport interface CapabilitySnapshot {\n camera: CapabilityStatus;\n microphone: CapabilityStatus;\n /** The only capability the freeze gives scoped detail to. Health and\n * photos/media do NOT get one — see the gap note below. */\n location: CapabilityStatus & {\n precision?: 'precise' | 'approximate' | 'unknown';\n backgroundPermission?: PermissionState;\n };\n motion: CapabilityStatus;\n bluetooth: CapabilityStatus;\n uwb: CapabilityStatus;\n nfc: CapabilityStatus;\n notifications: CapabilityStatus;\n /**\n * GAP (flagged): the capability doc notes health \"authorization is often\n * record-type specific rather than a single global permission\" and its\n * candidate shape had a `permission_summary`. The frozen §9 gives health a\n * plain CapabilityStatus, so scoped health authorization is NOT\n * representable in v0.1. Its own exit criterion (\"scoped permissions are\n * representable\") is therefore only partly met.\n */\n health: CapabilityStatus;\n backgroundExecution: CapabilityStatus;\n /** GAP (flagged): photos/media appears in the design doc's native inputs and\n * its full-vs-limited scope discussion, but there is NO photos key in the\n * frozen CapabilitySnapshot. Not added here. */\n quality: StateQuality;\n}\n\n/** FROZEN v0.1 (VOCABULARY §\"Initial Capability Events\"). All ten. */\nexport type CapabilityEvent =\n | 'capabilityChanged'\n | 'permissionChanged'\n | 'capabilityAvailable'\n | 'capabilityUnavailable'\n /** NOTE: this pair does not match the documented naming patterns. The docs\n * neither flag nor reconcile it; carried verbatim rather than \"fixed\". */\n | 'capabilityBusy'\n | 'capabilityFree'\n | 'restrictionChanged'\n | 'locationPrecisionChanged'\n | 'backgroundPermissionChanged'\n | 'capabilityStateStale';\n\n/**\n * NO INTENT TYPE — the one domain that is intent-less by explicit reasoning\n * rather than by omission: \"Capability State itself should generally be cheap\n * and mostly event-driven, so it does not need many sensing profiles.\"\n * A candidate `interactive/background/minimal` vocabulary is floated in the\n * design doc and explicitly conditional (\"only be kept if implementation\n * proves they add value\"). Nothing frozen. Not added.\n *\n * Also NOT here: any `requestPermission()`. Permission-request UX is outside\n * this state engine by design, and a floated generic primitive is future work.\n */\nexport interface CapabilityDomain {\n getSnapshot(): Promise<CapabilitySnapshot>;\n subscribe(listener: StateListener<CapabilityEvent>): Subscription;\n}\n\n// ═════════════════════════════════════════════════════════════════════════\n// 8. Domain 7 — Environment State\n// ═════════════════════════════════════════════════════════════════════════\n\n/** FROZEN (VOCABULARY §\"Environment Intents\"). */\nexport type EnvironmentIntent =\n | 'ambient'\n | 'navigation'\n | 'accessibility'\n | 'proximity'\n | 'safety'\n | 'lowPower';\n\nexport interface EnvironmentSnapshot {\n /** Privacy is part of the SHAPE, not advice: this says `dim`, never a camera\n * frame; `loud`, never microphone audio; a beacon count, never a BLE\n * payload. Raw audio/images/BLE payloads are not part of this domain. */\n lighting: {\n level: 'dark' | 'dim' | 'normal' | 'bright' | 'veryBright' | 'unknown';\n lux?: number | null;\n confidence?: Confidence;\n };\n acoustics: {\n level: 'quiet' | 'moderate' | 'loud' | 'veryLoud' | 'unknown';\n approximateDb?: number | null;\n confidence?: Confidence;\n };\n /** `uncertain` and `unknown` are different: uncertain = we looked and cannot\n * decide; unknown = we have no basis to decide. */\n enclosure: {\n state: 'indoor' | 'outdoor' | 'uncertain' | 'unknown';\n confidence?: Confidence;\n };\n proximity?: {\n distanceClass?: 'immediate' | 'near' | 'far' | 'unknown';\n confidence?: Confidence;\n };\n /** The only sub-block with no confidence — these are presence booleans, not\n * estimates. Note they cannot distinguish unsupported / denied / switched\n * off; that distinction lives in `capability`. */\n nearbySignals?: {\n bleAvailable?: boolean;\n beaconCount?: number | null;\n uwbAvailable?: boolean;\n nfcAvailable?: boolean;\n };\n quality: StateQuality;\n}\n\n/** FROZEN v0.1 (VOCABULARY §\"Initial Environment Events\"). All ten. */\nexport type EnvironmentEvent =\n | 'lightingChanged'\n | 'environmentBecameDark'\n | 'environmentBecameBright'\n | 'noiseLevelChanged'\n | 'environmentBecameLoud'\n | 'indoorOutdoorChanged'\n | 'proximityChanged'\n | 'nearbySignalAppeared'\n | 'nearbySignalDisappeared'\n | 'environmentStateStale';\n\nexport interface EnvironmentDomain {\n getSnapshot(): Promise<EnvironmentSnapshot>;\n start(options: StartOptions<EnvironmentIntent>): Promise<StateSession>;\n stop(sessionId?: string): Promise<void>;\n subscribe(listener: StateListener<EnvironmentEvent>): Subscription;\n}\n\n// ═════════════════════════════════════════════════════════════════════════\n// 9. Domain 8 — Interaction State\n//\n// Reports interaction FACTS. It never decides \"pause the guide\" or \"speak now\"\n// — that is orchestration and belongs to the Plugin Tool / Skill / AI.\n// ═════════════════════════════════════════════════════════════════════════\n\nexport interface InteractionSnapshot {\n lifecycle: {\n appState: 'foreground' | 'background' | 'inactive' | 'unknown';\n screenOn: boolean | null;\n userActive?: boolean | null;\n idleDurationMs?: number | null;\n };\n audio: {\n captureActive: boolean | null;\n playbackActive: boolean | null;\n route:\n | 'speaker'\n | 'earpiece'\n | 'wiredHeadset'\n | 'bluetooth'\n | 'carAudio'\n | 'other'\n | 'unknown';\n /** `none` (checked, nothing interrupting) and `unknown` (no knowledge) are\n * deliberately different values. */\n interruption: 'none' | 'temporary' | 'system' | 'unknown';\n };\n /** Optional GROUP: a platform that cannot report voice session state omits\n * the whole block rather than guessing `false` for each field. */\n voice?: {\n sessionActive: boolean | null;\n userSpeaking?: boolean | null;\n assistantSpeaking?: boolean | null;\n };\n /** May report that a camera session is running; must NOT duplicate frame\n * state. Frames belong to `visual`. */\n visual?: {\n cameraSessionActive: boolean | null;\n };\n quality: StateQuality;\n}\n\n/** FROZEN v0.1 (VOCABULARY §\"Initial Interaction Events\"). All twelve. */\nexport type InteractionEvent =\n | 'appStateChanged'\n | 'screenStateChanged'\n | 'audioRouteChanged'\n | 'audioInterruptionStarted'\n | 'audioInterruptionEnded'\n | 'voiceSessionStarted'\n | 'voiceSessionStopped'\n | 'cameraSessionStarted'\n | 'cameraSessionStopped'\n | 'userBecameActive'\n | 'userBecameIdle'\n | 'interactionStateStale';\n\n/**\n * NO INTENT TYPE. §10 declares only `InteractionSnapshot`, and the vocabulary\n * — which does publish intents for spatial, visual, activity, connectivity and\n * environment — has no \"Interaction Intents\" section. Five profiles are\n * floated in the design doc, hedged twice (\"Likely profiles, if needed\") with\n * no obligation attached to any of them. Not added.\n */\nexport interface InteractionDomain {\n getSnapshot(): Promise<InteractionSnapshot>;\n subscribe(listener: StateListener<InteractionEvent>): Subscription;\n}\n\n// ═════════════════════════════════════════════════════════════════════════\n// 10. Root surface\n// ═════════════════════════════════════════════════════════════════════════\n\n/**\n * FROZEN name (API_FREEZE §11) and FROZEN member names.\n *\n * §11's \"Recommended naming rule\" says to call it `visual.getSnapshot()`, not\n * `device.visual.*`. Read here as a rule about the WIRE/tool-name convention\n * (`device.*` stays a human/backend routing convention for callable plugin\n * tools) rather than a ban on a plugin-prop container. That reading is a\n * judgement call and is flagged as such — if it is wrong, the fix is to rename\n * the PROP, never to merge these eight members into `PluginNative`.\n */\nexport interface DeviceBridgeV2 {\n readonly spatial: SpatialDomain;\n readonly visual: VisualDomain;\n readonly activity: ActivityDomain;\n readonly deviceContext: DeviceContextDomain;\n readonly connectivity: ConnectivityDomain;\n readonly capability: CapabilityDomain;\n readonly environment: EnvironmentDomain;\n readonly interaction: InteractionDomain;\n}\n\n/** The name plugin authors see. See DESIGN NOTE 1 for why not `deviceBridge`. */\nexport type DeviceState = DeviceBridgeV2;\n\n// ═════════════════════════════════════════════════════════════════════════\n// 11. Manifest gate — a SEPARATE declaration from `native`\n// ═════════════════════════════════════════════════════════════════════════\n\n/**\n * DECISION (not in the docs). A second, differently-named manifest field, so a\n * plugin cannot half-declare its way across the boundary:\n *\n * native: { camera: true } → props.device.camera.takePhoto()\n * stateDomains: ['visual', 'spatial'] → props.deviceState.visual.getSnapshot()\n *\n * Declaring `native.camera` grants NOTHING in the semantic layer, and vice\n * versa. There is no field whose meaning spans both.\n *\n * How an UNDECLARED domain must behave — and this is the load-bearing part:\n * it returns a normal snapshot in which every enum is `unknown`, every\n * nullable is `null`, and `quality.freshness` is\n * `{ state:'unknown', observedAt:null, ageMs:null }`. It does NOT throw and it\n * does NOT return undefined.\n *\n * Why that matters here specifically: today's `gatePluginNative` gives an\n * undeclared capability a proxy whose methods `console.warn` and return\n * `undefined` (packages/plugin-runtime/src/host.ts:67-76). So\n * `await native.location.getCurrent()` on an undeclared plugin resolves to\n * `undefined`, and `pos?.latitude` is `undefined` — which then flows into an\n * LLM answer as a confident nothing. A silent undefined is a fabrication\n * vector; a snapshot of explicit unknowns is not.\n *\n * The \"why\" is readable from `capability.getSnapshot()` using an existing\n * frozen enum member: a manifest-gate denial maps to\n * `restriction: 'policyRestricted'` — the plugin manifest IS a policy. No new\n * vocabulary needed. (The docs never consider a manifest gate, so this\n * mapping is a DECISION, but it uses only frozen values.)\n */\nexport type StateDomainDeclaration = readonly DeviceBridgeDomain[];\n\n// ═════════════════════════════════════════════════════════════════════════\n// 12. Small helpers — the \"present does not mean fresh\" guard\n// DECISION. Not frozen, not in any doc. Included because the single most\n// likely misuse of this API is reading a value and ignoring `quality`, and a\n// one-line guard is easier to reach for than a correct hand-rolled check.\n// ═════════════════════════════════════════════════════════════════════════\n\nconst CONFIDENCE_ORDER: Record<ConfidenceLevel, number> = {\n unknown: 0,\n low: 1,\n medium: 2,\n high: 3,\n};\n\n/**\n * Is this snapshot good enough to act on?\n *\n * Deliberately requires the caller to state a budget: there is no universal\n * \"fresh enough\", and the docs give no numeric thresholds for any domain\n * (native chooses sampling behaviour). A guidance loop wants ~1500 ms; a\n * battery-aware background job is happy with 60 s.\n *\n * `unknown` freshness fails: it means nothing was ever observed, which is not\n * the same as an old-but-real reading and must not be treated as one.\n */\nexport function isActionable(\n quality: StateQuality,\n budget: { maxAgeMs: number; minConfidence?: ConfidenceLevel },\n): boolean {\n const { freshness, confidence } = quality;\n if (freshness.state === 'unknown' || freshness.state === 'stale') return false;\n if (freshness.ageMs === null || freshness.ageMs > budget.maxAgeMs) return false;\n if (budget.minConfidence) {\n const have = CONFIDENCE_ORDER[confidence?.level ?? 'unknown'];\n if (have < CONFIDENCE_ORDER[budget.minConfidence]) return false;\n }\n return true;\n}\n\n/** The honest \"we have never observed this\" value. Use it instead of\n * inventing a timestamp when constructing a degraded snapshot. This is the\n * canonical source of the `unknown` freshness shape — the engine's\n * `qualityUnknown()` derives from it so the two never drift. */\nexport function neverObserved(): Freshness {\n return { state: 'unknown', observedAt: null, ageMs: null };\n}\n\n/**\n * One human-readable sentence for why a capability cannot answer, built ONLY\n * from what the four axes actually say. Returns null when the capability is\n * fine — so a caller cannot accidentally print a reason that does not exist.\n */\nexport function explainCapability(status: CapabilityStatus): string | null {\n if (status.support === 'unsupported') return 'this device does not have it';\n if (status.permission === 'denied') return 'permission was denied';\n if (status.permission === 'restricted') return 'permission is restricted';\n if (status.permission === 'notDetermined') return 'permission has not been asked for yet';\n if (status.permission === 'limited') return 'only limited access was granted';\n if (status.restriction === 'policyRestricted') return 'this plugin did not declare it';\n if (status.restriction === 'backgroundRestricted') return 'the app is in the background';\n if (status.restriction === 'lowPowerRestricted') return 'the device is in low-power mode';\n if (status.availability === 'busy') return 'it is in use by something else';\n if (status.availability === 'temporarilyUnavailable') return 'it is temporarily unavailable';\n if (status.availability === 'unavailable') return 'it is unavailable';\n if (status.support === 'unknown' || status.availability === 'unknown') {\n return 'its state could not be determined';\n }\n return null;\n}\n\n// ═════════════════════════════════════════════════════════════════════════\n// 13. Integration seams into the existing SDK\n// ═════════════════════════════════════════════════════════════════════════\n\n/**\n * Additive extension of `PluginProps` (sdk/types.ts:259).\n *\n * `deviceState` is OPTIONAL because a host that has not installed a v2\n * provider must not pretend to have one. `undefined` means \"this host has no\n * semantic layer\", which a plugin can check once; it does NOT mean \"the\n * sensors said nothing\". Those are different, and collapsing them would\n * reintroduce exactly the ambiguity this file exists to remove.\n */\nexport interface PluginPropsV2Extension {\n readonly deviceState?: DeviceState;\n}\n\n/**\n * Additive extension of `DeviceToolContext` (sdk/types.ts:320-327).\n *\n * This is the seam that matters most: device tools are what the AI calls, and\n * device tools are where a fabricated value reaches a user. The lint rule\n * proposed alongside this file (`no-mechanical-sensing-in-device-tools`) bans\n * `ctx.native.camera`, `ctx.native.location` and `ctx.native.connectivity`\n * inside a `DeviceToolDefinition.run` body — a device tool must reach sensing\n * through `ctx.deviceState`. UI code is unaffected.\n */\nexport interface DeviceToolContextV2Extension {\n readonly deviceState?: DeviceState;\n}\n\n// ═════════════════════════════════════════════════════════════════════════\n// DESIGN NOTES\n// ═════════════════════════════════════════════════════════════════════════\n\n/**\n * DESIGN NOTE 1 — why the prop is `deviceState`, not `deviceBridge`.\n *\n * \"Device Bridge\" already names three different things in this codebase:\n * - `DeviceBridge`, the telemetry sync class that polls collectors and\n * pushes to the relay (packages/expo/src/device-bridge/index.ts:60);\n * - `PluginNative`, which sdk/types.ts:323 documents verbatim as\n * \"Device Bridge — camera, location, sensors, storage\";\n * - the v2 semantic layer in this file.\n * A fourth use of the word would be the opposite of unmistakable. `deviceState`\n * says what the object is (state, not actions) and collides with nothing.\n *\n *\n * DESIGN NOTE 2 — why NOT `PluginNative.visual` / `PluginNative.connectivityState`.\n *\n * The implementation-grounding doc proposes adding the eight domains as fields\n * on `PluginNative`, with `connectivityState` to dodge the collision with the\n * existing `connectivity`. Rejected, for three reasons:\n *\n * (a) It puts `native.camera.takePhoto()` and `native.visual.getSnapshot()`\n * on one object, one keystroke apart in autocomplete. The layer becomes\n * invisible at the call site, which is the failure being designed out.\n * (b) `native.connectivity` vs `native.connectivityState` is a confusable\n * pair by construction — two fields, one object, near-identical names,\n * different layers, different return shapes. `deviceState.connectivity`\n * vs `native.connectivity` is disambiguated by the first token instead.\n * (c) The gate model would have to span both meanings of one manifest field.\n *\n * The grounding doc's actual constraint is §14's \"Do not create a second\n * plugin runtime\" — and this design does not. Same runtime, same registration,\n * same bundle pipeline, same props object; one additional namespace on it.\n * Everything else in that doc's file-by-file plan (resident engines, warm\n * sessions, reference counting, no pipeline per tool call, the p50<10ms /\n * p95<30ms snapshot budget) stands unchanged.\n *\n *\n * DESIGN NOTE 3 — what this file does NOT decide, because the docs did not.\n *\n * - Per-intent behavioural contracts. The architecture doc MANDATES that each\n * intent document latency goal, quality target, continuous-vs-one-shot,\n * rolling-history behaviour, power posture, background behaviour, required\n * permissions and output shape. No document supplies these for ANY of the\n * nine visual intents, and there is no equivalent table for activity. The\n * vocabulary is minted; the semantics behind each name are not. This is the\n * largest hole in the whole design and no amount of TypeScript closes it.\n * - Multi-consumer session ownership / reference counting.\n * - MediaHandle → bytes resolution, and expiry behaviour.\n * - Whether `visual.capture()` may use a different intent from the session.\n * - Event vocabularies for visual, activity, deviceContext. (Spatial's is a\n * DECISION, recorded at `SpatialEvent`, not a reading of the docs.)\n * - Per-event payload schemas for every domain but spatial\n * (`SpatialStateEvent`, likewise a DECISION).\n * - Background/foreground semantics for activity and connectivity (specified\n * only for visual).\n * - Numeric thresholds for fresh/aging/stale, and for \"materially changed\".\n * - Cross-domain conflict resolution (activity says walking, spatial speed\n * says vehicle) — explicitly deferred as future State Engine Fusion and\n * out of v2 scope. A v2 snapshot must be internally coherent; it is NOT\n * obliged to reconcile itself with another domain.\n */\n","/**\n * Platform device tools.\n *\n * These are not a solution's plugin tools — they ship with the SDK and every\n * host has them, which is what makes a device tool testable without deploying\n * anything to a tenant. Their names and contracts are declared once on the\n * server (connectors/platform/device-tools-mcp.json); this is the device half.\n *\n * Registered at import, so a client answers them even before any plugin bundle\n * has loaded — that is the point: proving the device path works should not\n * depend on the thing being proved.\n */\nimport { registerDeviceTools } from './registry';\n\nconst PLATFORM_PLUGIN_ID = 'platform:device-tools';\n\nexport function registerPlatformDeviceTools(): void {\n registerDeviceTools(PLATFORM_PLUGIN_ID, [\n {\n name: 'device.echo',\n description: \"Echo a string back from the user's own device.\",\n input_schema: {\n type: 'object',\n properties: { text: { type: 'string' } },\n required: ['text'],\n },\n run: (args: any) => ({\n ok: true,\n echoed: (args && args.text) || '',\n // The proof: a server-side implementation could not say this truthfully.\n ran_on: 'device',\n }),\n },\n {\n name: 'device.ping',\n description: 'Check that the device is reachable.',\n input_schema: { type: 'object', properties: {} },\n run: () => ({ ok: true, ran_on: 'device', at: new Date().toISOString() }),\n },\n ]);\n}\n","/**\n * Ambient device-tool registration, driven by the auth session (R14).\n *\n * The LOCAL reasoning path must be live BEFORE voice opens — otherwise the only\n * proof of a loadable external skill is a voice-triggered one. So the host wires\n * this to its EXISTING auth-session subscription (no new transport, no plugin\n * special-case): a tenant becoming authenticated loads that tenant's external\n * device-tool bundles (and re-ensures the static platform tools); a tenant\n * SWITCH is a change of tenant while still authenticated, so it clears the old\n * tenant's tools and reloads; sign-out resets the loader latch (the registry\n * itself is cleared by the host's logout teardown, which already calls\n * clearDeviceTools()).\n *\n * The decision is a PURE function so the lifecycle is testable without a network\n * or a device; the effects are injected (same DI shape as the host's logout\n * teardown) so a test drives them with fakes.\n */\nimport { registerPlatformDeviceTools } from './platformTools';\nimport { clearDeviceTools } from './registry';\n// NOTE: `./loader` pulls in the bundle manager (react-native, core), so it is\n// imported LAZILY inside the real effects below — keeping this module itself\n// node-testable (a test injects fake effects and never touches that graph).\n\nexport interface SessionSnapshot {\n authenticated: boolean;\n /** The tenant the session is scoped to (derive it; never a stored copy). */\n tenant: string | null;\n}\n\nexport type DeviceToolsAction = 'load' | 'reset' | 'none';\n\n/**\n * PURE: what should the device-tool registry do for this session transition?\n * - a new authenticated tenant (initial sign-in OR a switch) → 'load'\n * - the same authenticated tenant again (e.g. a silent token refresh) → 'none'\n * - leaving authenticated (sign-out) → 'reset'\n */\nexport function deviceToolsActionForSession(\n next: SessionSnapshot,\n prevTenant: string | null,\n): { action: DeviceToolsAction; tenant: string | null } {\n const tenant = next.authenticated ? (next.tenant ?? null) : null;\n if (tenant && tenant !== prevTenant) return { action: 'load', tenant };\n if (!tenant && prevTenant) return { action: 'reset', tenant: null };\n return { action: 'none', tenant: prevTenant };\n}\n\n/** The effects a session transition drives — injected so a test can fake them. */\nexport interface DeviceToolsSessionDeps {\n /** Drop the previous tenant's device tools (a switch does not run logout teardown). */\n clear: () => void;\n /** Ensure the static, host-owned platform device tools are present. */\n registerPlatform: () => void;\n /** Forget the \"already loaded\" latch so a later (non-forced) load runs — sign-out. */\n resetLoader: () => void;\n /** Fire-and-forget reload of the current tenant's external device-tool bundles. */\n load: () => void;\n}\n\nconst REAL_DEPS: DeviceToolsSessionDeps = {\n clear: clearDeviceTools,\n registerPlatform: registerPlatformDeviceTools,\n // `./loader` reaches react-native via the bundle manager; import it only when\n // an effect actually fires (real app), never at module load (tests).\n resetLoader: () => {\n void import('./loader').then((m) => m.resetDeviceToolLoader());\n },\n load: () => {\n void import('./loader').then((m) => {\n m.resetDeviceToolLoader();\n return m.ensureDeviceToolsLoaded({ force: true });\n });\n },\n};\n\nlet prevTenant: string | null = null;\n\n/**\n * Apply a session transition to the device-tool registry. Call from the host's\n * existing auth-session subscription with a derived snapshot. Idempotent per\n * tenant: repeated snapshots for the same authenticated tenant do nothing, so a\n * silent token refresh does not churn the registry. Returns the action taken.\n */\nexport function syncDeviceToolsForSession(\n next: SessionSnapshot,\n deps: DeviceToolsSessionDeps = REAL_DEPS,\n): DeviceToolsAction {\n const { action, tenant } = deviceToolsActionForSession(next, prevTenant);\n prevTenant = tenant;\n if (action === 'load') {\n // A new authenticated tenant: drop the prior tenant's tools, re-seat the\n // static platform tools, then reload this tenant's external bundles (forced).\n deps.clear();\n deps.registerPlatform();\n deps.load();\n } else if (action === 'reset') {\n // Sign-out: the host's logout teardown clears the registry; we only forget\n // the loader latch so a later sign-in reloads.\n deps.resetLoader();\n }\n return action;\n}\n\n/** Test seam: forget the last-seen tenant so a test starts from signed-out. */\nexport function __resetSessionSyncForTest(): void {\n prevTenant = null;\n}\n","/**\n * Device tools called BY THE SERVER.\n *\n * Two callers reach the same registered function on this phone:\n *\n * - the voice loop, which already runs here (no server hop at all), and\n * - the server reasoning loop, which cannot execute a device tool itself and\n * sends it down the client's stream instead.\n *\n * This is the second one. It rides the transport the `ui.*` plugin commands\n * already use — a command arrives on the job/actor stream, we run it, and post\n * the outcome back keyed by `correlationId`. Nothing new was invented; the\n * payload just carries a tool call instead of a UI command.\n *\n * The host wires this once at startup:\n *\n * pluginCommandEmitter.on('plugin-command', handleServerDeviceToolCall);\n */\nimport { callDeviceTool } from './registry';\nimport { ensureDeviceToolsLoaded } from './loader';\n\ntype PostResult = (correlationId: string, result: any, error?: string) => Promise<void>;\n\nlet postResult: PostResult | null = null;\n\n/**\n * Host DI: how to answer the server. `@ateam-ai-mobile/core` supplies\n * postCommandResult; kept injectable so this package stays free of the REST\n * client and so tests can observe the answer.\n */\nexport function setDeviceToolResultPoster(fn: PostResult): void {\n postResult = fn;\n}\n\n/** Is this stream command a device tool call? */\nexport function isDeviceToolCommand(cmd: any): boolean {\n return !!cmd && cmd.kind === 'device_tool' && typeof cmd.tool === 'string' && !!cmd.correlationId;\n}\n\n/**\n * Run a server-dispatched device tool and answer.\n *\n * Never throws — the server is blocked on the correlationId, so the one thing\n * we must not do is stay silent. Every path posts something back.\n */\nexport async function handleServerDeviceToolCall(cmd: any): Promise<void> {\n if (!isDeviceToolCommand(cmd)) return;\n\n const { tool, args, correlationId } = cmd;\n\n if (!postResult) {\n console.warn('[deviceTools] server called a tool but no result poster is installed');\n return;\n }\n\n try {\n // The server may reach us before anything opened a voice call, so the\n // plugins may not be loaded yet. Cheap and idempotent once warm.\n await ensureDeviceToolsLoaded();\n\n const outcome = await callDeviceTool(tool, args || {});\n console.log(`[deviceTools] server-called ${tool} → ${outcome.ok ? 'ok' : outcome.reason}`);\n\n await postResult(\n correlationId,\n outcome.ok ? { ok: true, ...unwrap(outcome.result) } : undefined,\n outcome.ok ? undefined : outcome.error,\n );\n } catch (err: any) {\n await postResult(correlationId, undefined, err?.message || String(err)).catch(() => {});\n }\n}\n\n/** Keep an object result flat for the reasoning loop; wrap anything else. */\nfunction unwrap(result: any): Record<string, any> {\n if (result && typeof result === 'object' && !Array.isArray(result)) return result;\n return { result };\n}\n","/**\n * usePlugin — single hook for loading any plugin.\n *\n * Simple async load → done pattern over the global BundleManager singleton\n * (deduplication + caching) and the host's native plugin registry.\n *\n * Usage:\n * const { plugin, loading, error } = usePlugin(pluginId, manifest);\n * if (loading) return <Spinner />;\n * if (error) return <Error message={error} />;\n * return <plugin.Component bridge={bridge} device={device} theme={theme} context={context} />;\n */\nimport { useState, useEffect } from 'react';\nimport { bundleManager } from '../bundles/bundleManager';\nimport { nativePluginRegistry } from '../host';\nimport { resolvePluginGrants } from '../pluginRuntime';\nimport type { NativeCapabilities, RegisteredUIPlugin } from '../sdk/types';\nimport type { DeviceBridgeDomain } from '../sdk/device-state';\n\ninterface PluginManifestLike {\n version?: string;\n name?: string;\n render?: any;\n _connectorId?: string;\n capabilities?: Record<string, unknown>;\n native?: NativeCapabilities;\n stateDomains?: DeviceBridgeDomain[];\n}\n\ninterface UsePluginResult {\n plugin: RegisteredUIPlugin | null;\n loading: boolean;\n error: string | null;\n bundleMissing: boolean;\n}\n\n/**\n * Compute the bundle URL.\n *\n * **Policy:** bundle URLs are ALWAYS derived from the pluginId via the\n * canonical platform route /api/ui-plugins/{pluginId}/bundle.js.\n * Connector-supplied bundleUrl fields are IGNORED — the Builder strips them\n * at introspection time, but we treat any survivors as bugs and ignore them\n * here too (defense in depth vs \"hand-coded URL with bad encoding causes\n * silent iframe fallback\" bugs).\n */\ntype BundleUrlSource = 'derived-default' | 'ignored-explicit' | 'none';\nfunction extractBundleUrl(pluginId: string, manifest: PluginManifestLike): { url: string | null; source: BundleUrlSource; ignored?: string } {\n const render = manifest.render;\n if (!render || typeof render !== 'object') return { url: null, source: 'none' };\n\n // Detect (and refuse) any explicit bundle URL the connector tried to inject.\n const injected =\n (manifest as any).bundleUrl ||\n (render as any).bundleUrl ||\n render.reactNative?.bundleUrl ||\n null;\n\n // Mobile decides native vs web INDEPENDENT of render.mode. The server's\n // cp.getContextPlugin flattens mode to \"iframe\" whenever an iframeUrl is\n // available (for web-frontend compatibility) — even when a reactNative\n // component is also declared. So we derive the bundle URL whenever a\n // reactNative component is present, regardless of what `mode` says.\n const hasNativeComponent = !!(render.reactNative?.component || render.component);\n if (hasNativeComponent) {\n return {\n url: `/api/ui-plugins/${encodeURIComponent(pluginId)}/bundle.js`,\n source: injected ? 'ignored-explicit' : 'derived-default',\n ...(injected && { ignored: injected }),\n };\n }\n\n return { url: null, source: 'none' };\n}\n\n/**\n * Extract the SHA-256 integrity hash from the manifest (server-provided).\n * BundleManager refuses to evaluate a downloaded bundle whose bytes don't\n * hash to this value — defense vs MITM/CDN tampering.\n */\nfunction extractBundleHash(manifest: PluginManifestLike): string | undefined {\n if ((manifest as any).bundleHash) return (manifest as any).bundleHash;\n const render = manifest.render;\n if (!render || typeof render !== 'object') return undefined;\n if (render.bundleHash) return render.bundleHash;\n if (render.reactNative?.bundleHash) return render.reactNative.bundleHash;\n return undefined;\n}\n\n/**\n * Extract component name from manifest render field.\n */\nfunction extractComponentId(manifest: PluginManifestLike): string | null {\n const render = manifest.render;\n if (!render || typeof render !== 'object') return null;\n return render.component || render.reactNative?.component || null;\n}\n\nexport function usePlugin(pluginId: string, manifest: PluginManifestLike): UsePluginResult {\n const [plugin, setPlugin] = useState<RegisteredUIPlugin | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [bundleMissing, setBundleMissing] = useState(false);\n\n const version = manifest.version || '1.0.0';\n const { url: bundleUrl, ignored: ignoredUrl } = extractBundleUrl(pluginId, manifest);\n const bundleHash = extractBundleHash(manifest);\n const componentId = extractComponentId(manifest);\n const lookupId = componentId || pluginId;\n // The manifest's grants — the authority for the bundle's device tools, the\n // same one the renderer gates the component with. Keyed by value so a fresh\n // manifest object with the same grants does not reload the plugin.\n const grants = resolvePluginGrants({ pluginId, manifest });\n const grantsKey = JSON.stringify(grants);\n\n if (ignoredUrl) {\n console.warn(`[usePlugin] ${pluginId}: IGNORED connector-injected bundleUrl=\"${ignoredUrl}\". The host derives bundle URLs from pluginId via the canonical route.`);\n }\n\n useEffect(() => {\n let cancelled = false;\n\n (async () => {\n try {\n // Path 1: Local registry (host-bundled / dev plugins)\n if (nativePluginRegistry.has(lookupId)) {\n const registered = await nativePluginRegistry.get(lookupId);\n if (cancelled) return;\n\n if (!registered) {\n throw new Error(`Local plugin not found: ${lookupId}`);\n }\n if (registered.type === 'service') {\n throw new Error(`Plugin ${lookupId} is a service plugin and cannot be rendered`);\n }\n\n setPlugin(registered as RegisteredUIPlugin);\n setLoading(false);\n return;\n }\n\n // Path 2: Remote bundle (via global BundleManager)\n if (bundleUrl) {\n try {\n const component = await bundleManager.load(pluginId, bundleUrl, version, bundleHash, grants);\n if (cancelled) return;\n setPlugin(component);\n setLoading(false);\n return;\n } catch (bundleErr) {\n const msg = bundleErr instanceof Error ? bundleErr.message : String(bundleErr);\n // warn (not error) — this is a handled path: we fall back to WebView.\n console.warn(`[usePlugin] ${pluginId}: Bundle load failed, falling back to WebView:`, msg);\n // RN bundle failed — connector declared a component but didn't\n // ship a usable bundle (404, eval error, integrity fail, etc).\n // Mark \"bundle missing\" so the parent renderer can fall back to\n // the WebView/iframe when the manifest also has an iframeUrl.\n if (!cancelled) {\n setBundleMissing(true);\n setLoading(false);\n }\n return;\n }\n }\n\n // No native source available. If the manifest also has an iframeUrl,\n // mark \"bundle missing\" so the parent falls back to the WebView.\n // Only throw when there's truly nothing renderable.\n const iframeUrl = manifest?.render?.iframeUrl\n || (manifest?.render?.mode === 'adaptive' && manifest?.render?.iframe?.iframeUrl);\n if (iframeUrl) {\n if (!cancelled) {\n setBundleMissing(true);\n setLoading(false);\n }\n return;\n }\n throw new Error(\n `Plugin \"${manifest.name || pluginId}\" has no bundle URL and is not in the local registry. ` +\n `Check that the solution definition includes a valid render.reactNative.bundleUrl.`\n );\n } catch (err) {\n if (cancelled) return;\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[usePlugin] Failed to load ${pluginId}:`, msg);\n setError(msg);\n setLoading(false);\n }\n })();\n\n return () => { cancelled = true; };\n }, [pluginId, lookupId, bundleUrl, bundleHash, version, grantsKey]);\n\n return { plugin, loading, error, bundleMissing };\n}\n","import { useEffect, useState, useCallback } from 'react';\nimport { apiFetch } from '@ateam-ai-mobile/core';\nimport type { PluginManifest } from '../sdk/types';\n\nexport interface RemotePlugin {\n id: string;\n name: string;\n version: string;\n manifest: PluginManifest;\n source: 'remote' | 'local'; // Where this plugin came from\n}\n\ninterface UseRemotePluginsResult {\n plugins: RemotePlugin[];\n loading: boolean;\n error: string | null;\n refreshing: boolean;\n refresh: () => Promise<void>;\n}\n\n/**\n * Fetches available UI plugins from A-Team Core for the current solution.\n * Uses apiFetch which auto-injects JWT + tenant headers.\n */\nexport function useRemotePlugins(solutionId?: string): UseRemotePluginsResult {\n const [plugins, setPlugins] = useState<RemotePlugin[]>([]);\n const [loading, setLoading] = useState(true);\n const [refreshing, setRefreshing] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const fetchRemotePlugins = useCallback(async () => {\n try {\n setError(null);\n\n const targetSolutionId = solutionId || 'current';\n\n let data: any;\n try {\n data = await apiFetch<any>(`/api/solutions/${targetSolutionId}/ui-plugins`);\n } catch (err: any) {\n if (err?.message?.includes('404')) {\n console.warn(`[useRemotePlugins] Solution ${targetSolutionId} not found or has no plugins`);\n setPlugins([]);\n return;\n }\n throw err;\n }\n const uiPlugins: RemotePlugin[] = (data.plugins || []).map((plugin: any) => ({\n id: plugin.id,\n name: plugin.name,\n version: plugin.version || '1.0.0',\n source: 'remote' as const,\n manifest: {\n id: plugin.id,\n name: plugin.name,\n version: plugin.version || '1.0.0',\n type: 'ui',\n description: plugin.description || '',\n capabilities: plugin.capabilities || {},\n channels: plugin.channels || ['default'],\n render: (plugin.render && typeof plugin.render === 'object') ? plugin.render : { mode: 'react-native' as const, component: plugin.name || plugin.id },\n entry_point: plugin.entry_point || plugin.name,\n sandbox: plugin.sandbox !== false,\n requires_auth: plugin.requires_auth !== false,\n } as PluginManifest,\n }));\n\n setPlugins(uiPlugins);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error('[useRemotePlugins] Failed to fetch plugins:', message);\n setError(message);\n setPlugins([]); // Clear plugins on error\n } finally {\n setLoading(false);\n setRefreshing(false);\n }\n }, [solutionId]);\n\n const refresh = useCallback(async () => {\n setRefreshing(true);\n await fetchRemotePlugins();\n }, [fetchRemotePlugins]);\n\n useEffect(() => {\n setLoading(true);\n fetchRemotePlugins();\n }, [fetchRemotePlugins]);\n\n return {\n plugins,\n loading,\n error,\n refreshing,\n refresh,\n };\n}\n"],"mappings":"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","names":["prevTenant","useEffect","useState"]}
|