@ateam-ai-mobile/plugin-runtime 0.1.5 → 0.1.7

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/index.mjs CHANGED
@@ -1,2071 +1,338 @@
1
- var __defProp = Object.defineProperty;
2
- var __export = (target, all) => {
3
- for (var name in all)
4
- __defProp(target, name, { get: all[name], enumerable: true });
5
- };
6
-
7
- // src/sdk/index.ts
8
- var sdk_exports = {};
9
- __export(sdk_exports, {
10
- ActionButton: () => ActionButton,
11
- ActivityIndicator: () => ActivityIndicator,
12
- Animated: () => Animated,
13
- Badge: () => Badge,
14
- Card: () => Card,
15
- EmptyState: () => EmptyState,
16
- ErrorState: () => ErrorState,
17
- FlatList: () => FlatList,
18
- Image: () => Image,
19
- ListItem: () => ListItem,
20
- LoadingState: () => LoadingState,
21
- Modal: () => Modal,
22
- Platform: () => Platform,
23
- PluginSDK: () => PluginSDK,
24
- Pressable: () => Pressable,
25
- RefreshControl: () => RefreshControl,
26
- ScrollView: () => ScrollView,
27
- SearchBar: () => SearchBar,
28
- SectionList: () => SectionList,
29
- StyleSheet: () => StyleSheet,
30
- Switch: () => Switch,
31
- TabBar: () => TabBar,
32
- Text: () => Text,
33
- TextInput: () => TextInput,
34
- TouchableOpacity: () => TouchableOpacity,
35
- View: () => View,
36
- register: () => register,
37
- registerService: () => registerService,
38
- setPluginThemeColors: () => setPluginThemeColors,
39
- useApi: () => useApi,
40
- usePluginTheme: () => usePluginTheme
41
- });
42
-
43
- // src/sdk/registration.ts
44
- function register(id, config) {
45
- return {
46
- id,
47
- type: config.type || "ui",
48
- capabilities: config.capabilities,
49
- Component: config.Component
50
- };
51
- }
52
- function registerService(id, config) {
53
- return {
54
- id,
55
- type: "service",
56
- capabilities: config.capabilities,
57
- hooks: config.hooks,
58
- onStart: config.onStart,
59
- onStop: config.onStop
60
- };
61
- }
62
-
63
- // src/sdk/primitives.ts
64
1
  import {
65
- View,
66
- Text,
67
- ScrollView,
2
+ ActionButton,
3
+ ActivityIndicator,
4
+ AdaOrb,
5
+ Animated,
6
+ Badge,
7
+ CameraPreview,
8
+ Card,
9
+ EmptyState,
10
+ ErrorState,
68
11
  FlatList,
69
- SectionList,
70
- Pressable,
71
- TouchableOpacity,
72
- TextInput,
73
12
  Image,
74
- ActivityIndicator,
13
+ ListItem,
14
+ LoadingState,
15
+ MemoryBundleStorage,
75
16
  Modal,
76
- Switch,
77
- StyleSheet,
78
- Animated,
17
+ PLATFORM_VOICE_CALLER,
79
18
  Platform,
80
- RefreshControl
81
- } from "react-native";
19
+ PluginSDK,
20
+ Pressable,
21
+ RefreshControl,
22
+ ScrollView,
23
+ SearchBar,
24
+ SectionList,
25
+ StyleSheet,
26
+ SurfaceCard,
27
+ SurfaceDispatcher,
28
+ SurfaceDrawer,
29
+ SurfaceFullscreen,
30
+ Switch,
31
+ TabBar,
32
+ Text,
33
+ TextInput,
34
+ TouchableOpacity,
35
+ View,
36
+ __export,
37
+ bundleManager,
38
+ callDeviceTool,
39
+ clearDeviceTools,
40
+ createNativeBridge,
41
+ ensureDeviceToolsLoaded,
42
+ gateDeviceState,
43
+ gatePluginNative,
44
+ getCameraPreviewProvider,
45
+ getHaptics,
46
+ getNativeBundleHash,
47
+ getNativeBundleUrl,
48
+ getNativeComponentId,
49
+ getPlugin,
50
+ getPluginNative,
51
+ hasDeviceTool,
52
+ implementedStateDomains,
53
+ isInlineSurface,
54
+ isMountSurface,
55
+ listDeviceTools,
56
+ listPlugins,
57
+ nativePluginRegistry,
58
+ register,
59
+ registerBundleModules,
60
+ registerDeviceTools,
61
+ registerNativePlugin,
62
+ registerService,
63
+ registerTools,
64
+ resetDeviceToolLoader,
65
+ resolvePluginUrl,
66
+ setBundleStorage,
67
+ setCameraPreviewProvider,
68
+ setDeviceStateProvider,
69
+ setDeviceToolContextProvider,
70
+ setHapticsProvider,
71
+ setPluginNativeProvider,
72
+ setPluginThemeColors,
73
+ supportsNative,
74
+ supportsWebView,
75
+ unregisterDeviceTools,
76
+ unregisterNativePlugin,
77
+ useApi,
78
+ useNativeBridge,
79
+ usePluginTheme,
80
+ useSurfaceStore
81
+ } from "./chunk-PKDQLNBL.mjs";
82
82
 
83
- // src/sdk/components.ts
84
- import React from "react";
85
- import {
86
- View as View2,
87
- Text as Text2,
88
- Pressable as Pressable2,
89
- TextInput as TextInput2,
90
- ActivityIndicator as ActivityIndicator2,
91
- StyleSheet as StyleSheet2
92
- } from "react-native";
93
- function Card({ theme, children, style, onPress }) {
94
- const cardStyle = [
95
- styles.card,
96
- {
97
- backgroundColor: theme.colors.surface,
98
- borderColor: theme.colors.border
99
- },
100
- style
101
- ];
102
- if (onPress) {
103
- return React.createElement(Pressable2, { style: cardStyle, onPress }, children);
83
+ // src/sdk/device-state.ts
84
+ var device_state_exports = {};
85
+ __export(device_state_exports, {
86
+ DeviceBridgeError: () => DeviceBridgeError,
87
+ explainCapability: () => explainCapability,
88
+ isActionable: () => isActionable,
89
+ neverObserved: () => neverObserved
90
+ });
91
+ var DeviceBridgeError = class extends Error {
92
+ constructor(code, message, opts) {
93
+ super(message);
94
+ this.name = "DeviceBridgeError";
95
+ this.code = code;
96
+ this.domain = opts?.domain;
97
+ this.recoverable = opts?.recoverable;
104
98
  }
105
- return React.createElement(View2, { style: cardStyle }, children);
106
- }
107
- var BADGE_COLORS = {
108
- accent: { bg: "accentSoft", text: "accent" },
109
- success: { bg: "successSoft", text: "success" },
110
- warning: { bg: "warningSoft", text: "warning" },
111
- error: { bg: "errorSoft", text: "error" },
112
- purple: { bg: "purpleSoft", text: "purple" }
113
99
  };
114
- function Badge({ theme, label, color = "accent", style }) {
115
- const mapping = BADGE_COLORS[color];
116
- return React.createElement(
117
- View2,
118
- {
119
- style: [
120
- styles.badge,
121
- {
122
- backgroundColor: theme.colors[mapping.bg],
123
- borderRadius: theme.borderRadius.sm
124
- },
125
- style
126
- ]
127
- },
128
- React.createElement(Text2, {
129
- style: [styles.badgeText, { color: theme.colors[mapping.text] }]
130
- }, label)
131
- );
132
- }
133
- function TabBar({ theme, tabs, activeTab, onTabChange, style }) {
134
- return React.createElement(
135
- View2,
136
- {
137
- style: [
138
- styles.tabBar,
139
- { borderBottomColor: theme.colors.border },
140
- style
141
- ]
142
- },
143
- tabs.map(
144
- (tab) => React.createElement(
145
- Pressable2,
146
- {
147
- key: tab,
148
- style: [
149
- styles.tab,
150
- {
151
- borderBottomColor: tab === activeTab ? theme.colors.accent : "transparent"
152
- }
153
- ],
154
- onPress: () => onTabChange(tab)
155
- },
156
- React.createElement(Text2, {
157
- style: {
158
- color: tab === activeTab ? theme.colors.accent : theme.colors.textSecondary,
159
- fontSize: theme.fontSize.sm,
160
- fontWeight: tab === activeTab ? "600" : "400"
161
- }
162
- }, tab)
163
- )
164
- )
165
- );
166
- }
167
- function ListItem({ theme, title, subtitle, right, onPress, style }) {
168
- const content = [
169
- React.createElement(
170
- View2,
171
- { key: "content", style: styles.listItemContent },
172
- React.createElement(Text2, {
173
- style: { color: theme.colors.text, fontSize: theme.fontSize.md },
174
- numberOfLines: 1
175
- }, title),
176
- subtitle ? React.createElement(Text2, {
177
- style: { color: theme.colors.textSecondary, fontSize: theme.fontSize.xs, marginTop: 2 },
178
- numberOfLines: 1
179
- }, subtitle) : null
180
- ),
181
- right ? React.createElement(View2, { key: "right" }, right) : null
182
- ];
183
- if (onPress) {
184
- return React.createElement(
185
- Pressable2,
186
- { style: [styles.listItem, { borderBottomColor: theme.colors.border }, style], onPress },
187
- ...content
188
- );
100
+ var CONFIDENCE_ORDER = {
101
+ unknown: 0,
102
+ low: 1,
103
+ medium: 2,
104
+ high: 3
105
+ };
106
+ function isActionable(quality, budget) {
107
+ const { freshness, confidence } = quality;
108
+ if (freshness.state === "unknown" || freshness.state === "stale") return false;
109
+ if (freshness.ageMs === null || freshness.ageMs > budget.maxAgeMs) return false;
110
+ if (budget.minConfidence) {
111
+ const have = CONFIDENCE_ORDER[confidence?.level ?? "unknown"];
112
+ if (have < CONFIDENCE_ORDER[budget.minConfidence]) return false;
113
+ }
114
+ return true;
115
+ }
116
+ function neverObserved() {
117
+ return { state: "unknown", observedAt: null, ageMs: null };
118
+ }
119
+ function explainCapability(status) {
120
+ if (status.support === "unsupported") return "this device does not have it";
121
+ if (status.permission === "denied") return "permission was denied";
122
+ if (status.permission === "restricted") return "permission is restricted";
123
+ if (status.permission === "notDetermined") return "permission has not been asked for yet";
124
+ if (status.permission === "limited") return "only limited access was granted";
125
+ if (status.restriction === "policyRestricted") return "this plugin did not declare it";
126
+ if (status.restriction === "backgroundRestricted") return "the app is in the background";
127
+ if (status.restriction === "lowPowerRestricted") return "the device is in low-power mode";
128
+ if (status.availability === "busy") return "it is in use by something else";
129
+ if (status.availability === "temporarilyUnavailable") return "it is temporarily unavailable";
130
+ if (status.availability === "unavailable") return "it is unavailable";
131
+ if (status.support === "unknown" || status.availability === "unknown") {
132
+ return "its state could not be determined";
189
133
  }
190
- return React.createElement(
191
- View2,
192
- { style: [styles.listItem, { borderBottomColor: theme.colors.border }, style] },
193
- ...content
194
- );
195
- }
196
- function SearchBar({ theme, value, onChangeText, placeholder = "Search...", style }) {
197
- return React.createElement(TextInput2, {
198
- style: [
199
- styles.searchBar,
200
- {
201
- backgroundColor: theme.colors.input,
202
- color: theme.colors.text,
203
- borderColor: theme.colors.border,
204
- borderRadius: theme.borderRadius.sm,
205
- fontSize: theme.fontSize.sm
206
- },
207
- style
208
- ],
209
- value,
210
- onChangeText,
211
- placeholder,
212
- placeholderTextColor: theme.colors.textMuted,
213
- autoCapitalize: "none",
214
- autoCorrect: false
215
- });
216
- }
217
- function EmptyState({ theme, title, subtitle, style }) {
218
- return React.createElement(
219
- View2,
220
- { style: [styles.emptyState, style] },
221
- React.createElement(Text2, {
222
- style: { color: theme.colors.textSecondary, fontSize: theme.fontSize.md, textAlign: "center" }
223
- }, title),
224
- subtitle ? React.createElement(Text2, {
225
- style: { color: theme.colors.textMuted, fontSize: theme.fontSize.sm, textAlign: "center", marginTop: 4 }
226
- }, subtitle) : null
227
- );
228
- }
229
- function ErrorState({ theme, message, onRetry, style }) {
230
- return React.createElement(
231
- View2,
232
- { style: [styles.emptyState, style] },
233
- React.createElement(Text2, {
234
- style: { color: theme.colors.error, fontSize: theme.fontSize.md, textAlign: "center" }
235
- }, message),
236
- onRetry ? React.createElement(
237
- Pressable2,
238
- {
239
- style: [styles.retryButton, { backgroundColor: theme.colors.accentSoft, borderRadius: theme.borderRadius.sm }],
240
- onPress: onRetry
241
- },
242
- React.createElement(Text2, {
243
- style: { color: theme.colors.accent, fontSize: theme.fontSize.sm, fontWeight: "600" }
244
- }, "Retry")
245
- ) : null
246
- );
247
- }
248
- function LoadingState({ theme, message = "Loading...", style }) {
249
- return React.createElement(
250
- View2,
251
- { style: [styles.emptyState, style] },
252
- React.createElement(ActivityIndicator2, { size: "small", color: theme.colors.accent }),
253
- React.createElement(Text2, {
254
- style: { color: theme.colors.textSecondary, fontSize: theme.fontSize.sm, marginTop: 8 }
255
- }, message)
256
- );
257
- }
258
- function ActionButton({ theme, label, icon, onPress, style }) {
259
- return React.createElement(
260
- Pressable2,
261
- {
262
- style: [
263
- styles.fab,
264
- { backgroundColor: theme.colors.accent, borderRadius: theme.borderRadius.full },
265
- style
266
- ],
267
- onPress
268
- },
269
- icon || React.createElement(Text2, { style: styles.fabText }, label || "+")
270
- );
134
+ return null;
271
135
  }
272
- var styles = StyleSheet2.create({
273
- card: {
274
- padding: 12,
275
- borderRadius: 12,
276
- borderWidth: 1,
277
- marginBottom: 8
278
- },
279
- badge: {
280
- paddingHorizontal: 8,
281
- paddingVertical: 3
282
- },
283
- badgeText: {
284
- fontSize: 11,
285
- fontWeight: "600"
286
- },
287
- tabBar: {
288
- flexDirection: "row",
289
- borderBottomWidth: 1
290
- },
291
- tab: {
292
- flex: 1,
293
- padding: 12,
294
- alignItems: "center",
295
- borderBottomWidth: 2
296
- },
297
- listItem: {
298
- flexDirection: "row",
299
- alignItems: "center",
300
- paddingVertical: 12,
301
- paddingHorizontal: 16,
302
- borderBottomWidth: StyleSheet2.hairlineWidth
303
- },
304
- listItemContent: {
305
- flex: 1
306
- },
307
- searchBar: {
308
- height: 40,
309
- paddingHorizontal: 12,
310
- borderWidth: 1
311
- },
312
- emptyState: {
313
- flex: 1,
314
- justifyContent: "center",
315
- alignItems: "center",
316
- padding: 32
317
- },
318
- retryButton: {
319
- marginTop: 12,
320
- paddingHorizontal: 16,
321
- paddingVertical: 8
322
- },
323
- fab: {
324
- position: "absolute",
325
- bottom: 24,
326
- right: 24,
327
- width: 56,
328
- height: 56,
329
- justifyContent: "center",
330
- alignItems: "center",
331
- shadowColor: "#000",
332
- shadowOffset: { width: 0, height: 2 },
333
- shadowOpacity: 0.25,
334
- shadowRadius: 4,
335
- elevation: 5
336
- },
337
- fabText: {
338
- color: "#fff",
339
- fontSize: 24,
340
- fontWeight: "600"
341
- }
342
- });
343
136
 
344
- // src/sdk/usePluginTheme.ts
345
- import { useMemo, useSyncExternalStore } from "react";
346
- import { useColorScheme } from "react-native";
347
- import { ada, spacing, fontSize, borderRadius } from "@ateam-ai-mobile/core";
348
- var adaColors = {
349
- bg: ada.paper,
350
- surface: ada.paperSoft,
351
- surfaceAlt: ada.paperDeep,
352
- input: ada.paper,
353
- border: ada.border,
354
- text: ada.ink,
355
- textSecondary: ada.inkSoft,
356
- textMuted: ada.inkMuted,
357
- accent: ada.orange,
358
- accentSoft: ada.orangeSoft,
359
- success: "#5A8A5C",
360
- successSoft: "rgba(90,138,92,0.14)",
361
- warning: "#D49A4E",
362
- warningSoft: "rgba(212,154,78,0.16)",
363
- error: "#C15545",
364
- errorSoft: "rgba(193,85,69,0.14)",
365
- purple: ada.orangeDeep,
366
- purpleSoft: ada.orangeSoft
367
- };
368
- var _colors = adaColors;
369
- var _isDark = null;
370
- var _subscribers = /* @__PURE__ */ new Set();
371
- function setPluginThemeColors(colors, opts) {
372
- _colors = { ...adaColors, ...colors };
373
- if (opts?.isDark !== void 0) _isDark = opts.isDark;
374
- _subscribers.forEach((fn) => fn());
375
- }
376
- function subscribe(fn) {
377
- _subscribers.add(fn);
378
- return () => {
379
- _subscribers.delete(fn);
137
+ // src/voiceHost.ts
138
+ var _provider = null;
139
+ function setVoiceProvider(provider) {
140
+ _provider = provider || null;
141
+ }
142
+ function implementedVoice() {
143
+ return !!_provider;
144
+ }
145
+ function voiceDenial(why) {
146
+ const err = new Error(`voice: ${why}`);
147
+ err.code = "RESTRICTED";
148
+ return err;
149
+ }
150
+ function deniedVoice(why) {
151
+ const fail = () => {
152
+ throw voiceDenial(why);
380
153
  };
381
- }
382
- function usePluginTheme() {
383
- const colors = useSyncExternalStore(subscribe, () => _colors);
384
- const hostIsDark = useSyncExternalStore(subscribe, () => _isDark);
385
- const scheme = useColorScheme();
386
- const isDark = hostIsDark ?? scheme === "dark";
387
- return useMemo(() => ({
388
- colors,
389
- spacing: {
390
- xs: spacing.xs,
391
- sm: spacing.sm,
392
- md: spacing.md,
393
- lg: spacing.lg,
394
- xl: spacing.xl,
395
- xxl: spacing.xxl
154
+ return {
155
+ async start() {
156
+ return fail();
396
157
  },
397
- fontSize: {
398
- xs: fontSize.xs,
399
- sm: fontSize.sm,
400
- md: fontSize.md,
401
- lg: fontSize.lg,
402
- xl: fontSize.xl
158
+ stop() {
159
+ fail();
403
160
  },
404
- borderRadius: {
405
- sm: borderRadius.sm,
406
- md: borderRadius.md,
407
- lg: borderRadius.lg,
408
- full: borderRadius.full
161
+ setMuted() {
162
+ fail();
409
163
  },
410
- isDark
411
- }), [colors, isDark]);
412
- }
413
-
414
- // src/sdk/useApi.ts
415
- import { useMemo as useMemo2 } from "react";
416
- function useApi(bridge) {
417
- return useMemo2(() => {
418
- const apiFn = async (connectorId, tool, args = {}) => {
419
- if (!bridge) {
420
- throw new Error("useApi: bridge is required. Pass it from plugin props.");
421
- }
422
- return bridge.mcpCall(tool, args, connectorId);
423
- };
424
- const callMethod = async (first, second, third) => {
425
- if (!bridge) {
426
- throw new Error("useApi: bridge is required. Pass it from plugin props.");
427
- }
428
- if (typeof second === "string") {
429
- return bridge.mcpCall(second, third || {}, first);
430
- }
431
- if (typeof third === "string") {
432
- return bridge.mcpCall(first, second || {}, third);
433
- }
434
- return bridge.mcpCall(first, second || {});
435
- };
436
- Object.defineProperty(apiFn, "call", {
437
- value: callMethod,
438
- writable: false,
439
- configurable: false
440
- });
441
- return apiFn;
442
- }, [bridge]);
443
- }
444
-
445
- // src/sdk/index.ts
446
- var PluginSDK = { register, registerService };
447
-
448
- // src/host.ts
449
- var _haptics = null;
450
- function setHapticsProvider(haptics) {
451
- _haptics = haptics;
452
- }
453
- function getHaptics() {
454
- return _haptics;
455
- }
456
- function unavailable(api) {
457
- return new Proxy({}, {
458
- get: (_t, method) => () => Promise.reject(new Error(`[adas] native.${api}.${String(method)} unavailable \u2014 host did not call setPluginNativeProvider()`))
459
- });
460
- }
461
- var noopNative = {
462
- camera: unavailable("camera"),
463
- location: unavailable("location"),
464
- storage: unavailable("storage"),
465
- biometrics: unavailable("biometrics"),
466
- biometricsV2: unavailable("biometricsV2"),
467
- notifications: unavailable("notifications"),
468
- haptics: { impact: () => {
469
- }, notification: () => {
470
- }, selection: () => {
471
- } },
472
- connectivity: { isOnline: () => true, onChange: () => () => {
473
- } },
474
- fileSystem: unavailable("fileSystem"),
475
- cookies: unavailable("cookies"),
476
- sharing: unavailable("sharing"),
477
- scanner: unavailable("scanner"),
478
- nfc: unavailable("nfc")
479
- };
480
- var _pluginNative = noopNative;
481
- function setPluginNativeProvider(native) {
482
- _pluginNative = native;
483
- }
484
- function getPluginNative() {
485
- return _pluginNative;
486
- }
487
- function blocked(api) {
488
- return new Proxy({}, {
489
- get: (_t, method) => {
490
- if (typeof method !== "string") return void 0;
491
- return () => {
492
- console.warn(`[adas] native.${api}.${method} blocked \u2014 capability '${api}' not declared in the plugin manifest`);
164
+ prompt() {
165
+ fail();
166
+ },
167
+ observe() {
168
+ fail();
169
+ },
170
+ getSnapshot() {
171
+ return {
172
+ state: "error",
173
+ sessionId: null,
174
+ muted: false,
175
+ speaking: false,
176
+ listening: false,
177
+ userSpeaking: false,
178
+ lastUserSpeechEndedAt: null,
179
+ lastError: why
493
180
  };
181
+ },
182
+ subscribe() {
183
+ return fail();
184
+ },
185
+ transcript() {
186
+ return [];
494
187
  }
495
- });
188
+ };
496
189
  }
497
- function gatePluginNative(native, declared) {
498
- const caps = declared || {};
190
+ function scopedVoice(provider, pluginId) {
499
191
  return {
500
- camera: caps.camera ? native.camera : blocked("camera"),
501
- location: caps.location ? native.location : blocked("location"),
502
- storage: caps.offline ? native.storage : blocked("storage"),
503
- biometrics: caps.biometrics ? native.biometrics : blocked("biometrics"),
504
- biometricsV2: caps.biometricsV2 ? native.biometricsV2 : blocked("biometricsV2"),
505
- notifications: caps.notifications ? native.notifications : blocked("notifications"),
506
- haptics: caps.haptics ? native.haptics : blocked("haptics"),
507
- connectivity: native.connectivity,
508
- fileSystem: caps.fileSystem ? native.fileSystem : blocked("fileSystem"),
509
- cookies: caps.cookies ? native.cookies : blocked("cookies"),
510
- sharing: caps.sharing ? native.sharing : blocked("sharing"),
511
- scanner: caps.scanner ? native.scanner : blocked("scanner"),
512
- nfc: caps.nfc ? native.nfc : blocked("nfc")
192
+ start: (opts) => provider.start(opts, pluginId),
193
+ stop: () => provider.stop(pluginId),
194
+ setMuted: (muted) => provider.setMuted(muted, pluginId),
195
+ prompt: (input) => provider.prompt(input, pluginId),
196
+ observe: (text) => provider.observe(text, pluginId),
197
+ getSnapshot: () => provider.getSnapshot(pluginId),
198
+ subscribe: (cb) => provider.subscribe(cb, pluginId),
199
+ transcript: () => provider.transcript(pluginId)
513
200
  };
514
201
  }
515
- var _nativeRegistry = /* @__PURE__ */ new Map();
516
- function registerNativePlugin(id, plugin) {
517
- _nativeRegistry.set(id, plugin);
518
- }
519
- function unregisterNativePlugin(id) {
520
- _nativeRegistry.delete(id);
521
- }
522
- var nativePluginRegistry = {
523
- has(id) {
524
- return _nativeRegistry.has(id);
525
- },
526
- async get(id) {
527
- const entry = _nativeRegistry.get(id);
528
- if (!entry) return null;
529
- if (typeof entry === "function") {
530
- try {
531
- return await entry();
532
- } catch (err) {
533
- console.warn(`[adas-plugins] lazy native plugin '${id}' failed to load:`, err);
534
- return null;
535
- }
536
- }
537
- return entry;
538
- }
539
- };
540
- var _bundleModules = /* @__PURE__ */ new Map();
541
- function registerBundleModules(modules) {
542
- for (const [name, mod] of Object.entries(modules)) {
543
- _bundleModules.set(name, mod);
544
- }
545
- }
546
- function resolveBundleModule(name) {
547
- return _bundleModules.get(name);
548
- }
549
-
550
- // src/bundles/bundleManager.ts
551
- import React2 from "react";
552
- import {
553
- useState,
554
- useEffect as useEffect5,
555
- useCallback,
556
- useMemo as useMemo3,
557
- useRef as useRef3,
558
- useContext,
559
- useReducer,
560
- useLayoutEffect,
561
- useImperativeHandle,
562
- useDebugValue
563
- } from "react";
564
- import * as ReactNative from "react-native";
565
- import { apiFetchRaw } from "@ateam-ai-mobile/core";
566
-
567
- // src/surfaces/SurfaceDrawer.tsx
568
- import { useEffect as useEffect2, useRef } from "react";
569
- import {
570
- View as View4,
571
- Text as Text3,
572
- Pressable as Pressable3,
573
- StyleSheet as StyleSheet4,
574
- Animated as Animated3,
575
- Dimensions,
576
- Platform as Platform2
577
- } from "react-native";
578
- import Svg2, { Path } from "react-native-svg";
579
- import { ada as ada2 } from "@ateam-ai-mobile/core";
580
-
581
- // src/surfaces/AdaOrb.tsx
582
- import { useEffect } from "react";
583
- import { View as View3, StyleSheet as StyleSheet3 } from "react-native";
584
- import Animated2, {
585
- useSharedValue,
586
- useAnimatedProps,
587
- withRepeat,
588
- withTiming,
589
- withSequence,
590
- Easing,
591
- cancelAnimation
592
- } from "react-native-reanimated";
593
- import Svg, { Circle, Defs, RadialGradient, Stop, Text as SvgText } from "react-native-svg";
594
- import { jsx, jsxs } from "react/jsx-runtime";
595
- var AnimatedCircle = Animated2.createAnimatedComponent(Circle);
596
- function AdaOrb({ size = 120, state = "idle", inscription = true }) {
597
- const showInscription = inscription && size >= 60;
598
- const center = size / 2;
599
- const coreR = size * 0.32;
600
- const innerHaloR = size * 0.42;
601
- const outerHaloR = size * 0.5;
602
- const coreScale = useSharedValue(1);
603
- const innerScale = useSharedValue(1);
604
- const outerScale = useSharedValue(1);
605
- const outerOpacity = useSharedValue(0.55);
606
- const rippleCycle = useSharedValue(0);
607
- const orbit1 = useSharedValue(0);
608
- const orbit2 = useSharedValue(0);
609
- const orbit3 = useSharedValue(0);
610
- const beat = useSharedValue(1);
611
- useEffect(() => {
612
- cancelAnimation(coreScale);
613
- cancelAnimation(innerScale);
614
- cancelAnimation(outerScale);
615
- cancelAnimation(outerOpacity);
616
- cancelAnimation(rippleCycle);
617
- cancelAnimation(orbit1);
618
- cancelAnimation(orbit2);
619
- cancelAnimation(orbit3);
620
- cancelAnimation(beat);
621
- rippleCycle.value = 0;
622
- orbit1.value = 0;
623
- orbit2.value = 0;
624
- orbit3.value = 0;
625
- beat.value = 1;
626
- let breathMs = 3600;
627
- let breathAmount = 0.025;
628
- if (state === "listening") {
629
- breathMs = 2e3;
630
- breathAmount = 0.05;
631
- rippleCycle.value = withRepeat(
632
- withTiming(1, { duration: 2e3, easing: Easing.out(Easing.quad) }),
633
- -1,
634
- false
635
- );
636
- } else if (state === "thinking") {
637
- breathMs = 2400;
638
- breathAmount = 0.04;
639
- orbit1.value = withRepeat(
640
- withTiming(1, { duration: 1100, easing: Easing.linear }),
641
- -1,
642
- false
643
- );
644
- orbit2.value = withRepeat(
645
- withTiming(1, { duration: 1900, easing: Easing.linear }),
646
- -1,
647
- false
648
- );
649
- orbit3.value = withRepeat(
650
- withTiming(-1, { duration: 2700, easing: Easing.linear }),
651
- -1,
652
- false
653
- );
654
- } else if (state === "speaking") {
655
- breathMs = 700;
656
- breathAmount = 0.13;
657
- beat.value = withRepeat(
658
- withSequence(
659
- withTiming(1.08, { duration: 90, easing: Easing.out(Easing.quad) }),
660
- withTiming(1, { duration: 130, easing: Easing.in(Easing.quad) }),
661
- withTiming(1.05, { duration: 80, easing: Easing.out(Easing.quad) }),
662
- withTiming(1, { duration: 200, easing: Easing.in(Easing.quad) }),
663
- withTiming(1, { duration: 280 })
664
- ),
665
- -1,
666
- false
667
- );
668
- }
669
- const breathEasing = state === "speaking" ? Easing.out(Easing.cubic) : Easing.inOut(Easing.sin);
670
- coreScale.value = withRepeat(
671
- withSequence(
672
- withTiming(1 + breathAmount, { duration: breathMs, easing: breathEasing }),
673
- withTiming(1, { duration: breathMs, easing: breathEasing })
674
- ),
675
- -1,
676
- false
677
- );
678
- innerScale.value = withRepeat(
679
- withSequence(
680
- withTiming(1 + breathAmount * 1.4, { duration: breathMs * 1.15, easing: breathEasing }),
681
- withTiming(1, { duration: breathMs * 1.15, easing: breathEasing })
682
- ),
683
- -1,
684
- false
202
+ function gateVoice(declared, pluginId) {
203
+ if (!declared) {
204
+ return deniedVoice(
205
+ "not declared in this plugin's capabilities. Add `capabilities: { voice: true }` to use it."
685
206
  );
686
- outerScale.value = withRepeat(
687
- withSequence(
688
- withTiming(1 + breathAmount * 1.8, { duration: breathMs * 1.3, easing: breathEasing }),
689
- withTiming(1, { duration: breathMs * 1.3, easing: breathEasing })
690
- ),
691
- -1,
692
- false
693
- );
694
- outerOpacity.value = withRepeat(
695
- withSequence(
696
- withTiming(state === "idle" ? 0.55 : 0.8, { duration: breathMs * 1.3, easing: breathEasing }),
697
- withTiming(0.35, { duration: breathMs * 1.3, easing: breathEasing })
698
- ),
699
- -1,
700
- false
701
- );
702
- }, [state]);
703
- const coreProps = useAnimatedProps(() => ({
704
- r: coreR * coreScale.value * beat.value
705
- }));
706
- const innerProps = useAnimatedProps(() => ({
707
- r: innerHaloR * innerScale.value * beat.value
708
- }));
709
- const outerProps = useAnimatedProps(() => ({
710
- r: outerHaloR * outerScale.value,
711
- opacity: outerOpacity.value
712
- }));
713
- const ripple1Props = useAnimatedProps(() => {
714
- const t = rippleCycle.value;
715
- return {
716
- r: outerHaloR * (1 + t * 0.55),
717
- opacity: state === "listening" ? 0.55 * (1 - t) : 0
718
- };
719
- });
720
- const ripple2Props = useAnimatedProps(() => {
721
- const t = (rippleCycle.value + 0.34) % 1;
722
- return {
723
- r: outerHaloR * (1 + t * 0.55),
724
- opacity: state === "listening" ? 0.45 * (1 - t) : 0
725
- };
726
- });
727
- const ripple3Props = useAnimatedProps(() => {
728
- const t = (rippleCycle.value + 0.67) % 1;
729
- return {
730
- r: outerHaloR * (1 + t * 0.55),
731
- opacity: state === "listening" ? 0.35 * (1 - t) : 0
732
- };
733
- });
734
- const spark1Props = useAnimatedProps(() => {
735
- const angle = orbit1.value * Math.PI * 2 - Math.PI / 2;
736
- const r = coreR * 0.55;
737
- return {
738
- cx: center + Math.cos(angle) * r,
739
- cy: center + Math.sin(angle) * r,
740
- opacity: state === "thinking" ? 1 : 0
741
- };
742
- });
743
- const spark2Props = useAnimatedProps(() => {
744
- const angle = orbit2.value * Math.PI * 2 + Math.PI * 0.6;
745
- const r = coreR * 0.85;
746
- return {
747
- cx: center + Math.cos(angle) * r,
748
- cy: center + Math.sin(angle) * r,
749
- opacity: state === "thinking" ? 0.9 : 0
750
- };
751
- });
752
- const spark3Props = useAnimatedProps(() => {
753
- const angle = orbit3.value * Math.PI * 2 + Math.PI * 1.3;
754
- const r = coreR * 1.05;
755
- return {
756
- cx: center + Math.cos(angle) * r,
757
- cy: center + Math.sin(angle) * r,
758
- opacity: state === "thinking" ? 0.7 : 0
759
- };
760
- });
761
- return /* @__PURE__ */ jsx(View3, { style: [styles2.wrap, { width: size, height: size }], children: /* @__PURE__ */ jsxs(Svg, { width: size, height: size, children: [
762
- /* @__PURE__ */ jsxs(Defs, { children: [
763
- /* @__PURE__ */ jsxs(RadialGradient, { id: "adaCore", cx: "36%", cy: "30%", r: "70%", children: [
764
- /* @__PURE__ */ jsx(Stop, { offset: "0%", stopColor: "#FFE0B0", stopOpacity: "1" }),
765
- /* @__PURE__ */ jsx(Stop, { offset: "28%", stopColor: "#FFA85A", stopOpacity: "1" }),
766
- /* @__PURE__ */ jsx(Stop, { offset: "65%", stopColor: "#FF7A28", stopOpacity: "1" }),
767
- /* @__PURE__ */ jsx(Stop, { offset: "100%", stopColor: "#B8410A", stopOpacity: "1" })
768
- ] }),
769
- /* @__PURE__ */ jsxs(RadialGradient, { id: "adaInner", cx: "50%", cy: "50%", r: "50%", children: [
770
- /* @__PURE__ */ jsx(Stop, { offset: "0%", stopColor: "#FF8A3C", stopOpacity: "0.55" }),
771
- /* @__PURE__ */ jsx(Stop, { offset: "100%", stopColor: "#FF7028", stopOpacity: "0" })
772
- ] }),
773
- /* @__PURE__ */ jsxs(RadialGradient, { id: "adaOuter", cx: "50%", cy: "50%", r: "50%", children: [
774
- /* @__PURE__ */ jsx(Stop, { offset: "0%", stopColor: "#FF7A28", stopOpacity: "0.4" }),
775
- /* @__PURE__ */ jsx(Stop, { offset: "100%", stopColor: "#FF6018", stopOpacity: "0" })
776
- ] }),
777
- /* @__PURE__ */ jsxs(RadialGradient, { id: "adaSpark", cx: "50%", cy: "50%", r: "50%", children: [
778
- /* @__PURE__ */ jsx(Stop, { offset: "0%", stopColor: "#FFFAEB", stopOpacity: "1" }),
779
- /* @__PURE__ */ jsx(Stop, { offset: "60%", stopColor: "#FFD095", stopOpacity: "0.9" }),
780
- /* @__PURE__ */ jsx(Stop, { offset: "100%", stopColor: "#FFA85A", stopOpacity: "0" })
781
- ] })
782
- ] }),
783
- /* @__PURE__ */ jsx(
784
- AnimatedCircle,
785
- {
786
- cx: center,
787
- cy: center,
788
- stroke: "#FF7A28",
789
- strokeWidth: 1.5,
790
- fill: "none",
791
- animatedProps: ripple1Props
792
- }
793
- ),
794
- /* @__PURE__ */ jsx(
795
- AnimatedCircle,
796
- {
797
- cx: center,
798
- cy: center,
799
- stroke: "#FF7A28",
800
- strokeWidth: 1.5,
801
- fill: "none",
802
- animatedProps: ripple2Props
803
- }
804
- ),
805
- /* @__PURE__ */ jsx(
806
- AnimatedCircle,
807
- {
808
- cx: center,
809
- cy: center,
810
- stroke: "#FF7A28",
811
- strokeWidth: 1.5,
812
- fill: "none",
813
- animatedProps: ripple3Props
814
- }
815
- ),
816
- /* @__PURE__ */ jsx(AnimatedCircle, { cx: center, cy: center, fill: "url(#adaOuter)", animatedProps: outerProps }),
817
- /* @__PURE__ */ jsx(AnimatedCircle, { cx: center, cy: center, fill: "url(#adaInner)", animatedProps: innerProps }),
818
- /* @__PURE__ */ jsx(AnimatedCircle, { cx: center, cy: center, fill: "url(#adaCore)", animatedProps: coreProps }),
819
- /* @__PURE__ */ jsx(
820
- AnimatedCircle,
821
- {
822
- r: Math.max(3, size * 0.05),
823
- fill: "url(#adaSpark)",
824
- animatedProps: spark1Props
825
- }
826
- ),
827
- /* @__PURE__ */ jsx(
828
- AnimatedCircle,
829
- {
830
- r: Math.max(2.5, size * 0.04),
831
- fill: "url(#adaSpark)",
832
- animatedProps: spark2Props
833
- }
834
- ),
835
- /* @__PURE__ */ jsx(
836
- AnimatedCircle,
837
- {
838
- r: Math.max(2, size * 0.032),
839
- fill: "url(#adaSpark)",
840
- animatedProps: spark3Props
841
- }
842
- ),
843
- showInscription && /* @__PURE__ */ jsx(
844
- SvgText,
845
- {
846
- x: center,
847
- y: center + size * 0.11,
848
- fontSize: size * 0.34,
849
- fontWeight: "600",
850
- fill: "#FFF5E0",
851
- textAnchor: "middle",
852
- letterSpacing: -size * 5e-3,
853
- children: "iA"
854
- }
855
- )
856
- ] }) });
857
- }
858
- var styles2 = StyleSheet3.create({
859
- wrap: {
860
- alignItems: "center",
861
- justifyContent: "center"
862
207
  }
863
- });
864
-
865
- // src/surfaces/SurfaceDrawer.tsx
866
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
867
- var SCREEN_H = Dimensions.get("window").height;
868
- var fontFam = Platform2.OS === "ios" ? "SF Pro Rounded" : void 0;
869
- function SurfaceDrawer({
870
- visible,
871
- onClose,
872
- title,
873
- subtitle,
874
- privacy,
875
- showOrb = true,
876
- heightRatio = 0.85,
877
- children
878
- }) {
879
- const sheetH = Math.round(SCREEN_H * heightRatio);
880
- const fadeAnim = useRef(new Animated3.Value(0)).current;
881
- const slideAnim = useRef(new Animated3.Value(sheetH)).current;
882
- useEffect2(() => {
883
- if (visible) {
884
- Animated3.parallel([
885
- Animated3.timing(fadeAnim, { toValue: 1, duration: 220, useNativeDriver: true }),
886
- Animated3.spring(slideAnim, { toValue: 0, useNativeDriver: true, tension: 80, friction: 12 })
887
- ]).start();
888
- } else {
889
- fadeAnim.setValue(0);
890
- slideAnim.setValue(sheetH);
891
- }
892
- }, [visible]);
893
- const close = () => {
894
- Animated3.parallel([
895
- Animated3.timing(fadeAnim, { toValue: 0, duration: 160, useNativeDriver: true }),
896
- Animated3.timing(slideAnim, { toValue: sheetH, duration: 200, useNativeDriver: true })
897
- ]).start(() => onClose());
898
- };
899
- if (!visible) return null;
900
- return /* @__PURE__ */ jsxs2(Animated3.View, { style: [styles3.overlay, { opacity: fadeAnim }], pointerEvents: "auto", children: [
901
- /* @__PURE__ */ jsx2(Pressable3, { style: StyleSheet4.absoluteFill, onPress: close }),
902
- /* @__PURE__ */ jsxs2(
903
- Animated3.View,
904
- {
905
- style: [
906
- styles3.sheet,
907
- { height: sheetH, transform: [{ translateY: slideAnim }] }
908
- ],
909
- children: [
910
- /* @__PURE__ */ jsx2(View4, { style: styles3.handle }),
911
- /* @__PURE__ */ jsxs2(View4, { style: styles3.header, children: [
912
- showOrb ? /* @__PURE__ */ jsx2(View4, { style: styles3.headerOrb, children: /* @__PURE__ */ jsx2(AdaOrb, { size: 36, state: "idle" }) }) : null,
913
- /* @__PURE__ */ jsxs2(View4, { style: { flex: 1 }, children: [
914
- /* @__PURE__ */ jsx2(Text3, { style: styles3.title, children: title }),
915
- subtitle ? /* @__PURE__ */ jsx2(Text3, { style: styles3.subtitle, children: subtitle }) : null,
916
- privacy ? /* @__PURE__ */ jsxs2(View4, { style: styles3.privacyPill, children: [
917
- /* @__PURE__ */ jsxs2(Svg2, { width: 11, height: 11, viewBox: "0 0 24 24", fill: "none", stroke: ada2.inkSoft, strokeWidth: 2.5, strokeLinecap: "round", strokeLinejoin: "round", children: [
918
- /* @__PURE__ */ jsx2(Path, { d: "M19 11H5a2 2 0 00-2 2v7a2 2 0 002 2h14a2 2 0 002-2v-7a2 2 0 00-2-2z" }),
919
- /* @__PURE__ */ jsx2(Path, { d: "M7 11V7a5 5 0 0110 0v4" })
920
- ] }),
921
- /* @__PURE__ */ jsx2(Text3, { style: styles3.privacyText, children: "Only you can see this" })
922
- ] }) : null
923
- ] }),
924
- /* @__PURE__ */ jsx2(
925
- Pressable3,
926
- {
927
- accessibilityRole: "button",
928
- accessibilityLabel: "Close",
929
- style: styles3.closeBtn,
930
- onPress: close,
931
- hitSlop: 12,
932
- children: /* @__PURE__ */ jsx2(Svg2, { width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", stroke: ada2.inkSoft, strokeWidth: 2.2, strokeLinecap: "round", children: /* @__PURE__ */ jsx2(Path, { d: "M18 6L6 18M6 6l12 12" }) })
933
- }
934
- )
935
- ] }),
936
- /* @__PURE__ */ jsx2(View4, { style: styles3.body, children })
937
- ]
938
- }
939
- )
940
- ] });
941
- }
942
- var styles3 = StyleSheet4.create({
943
- overlay: {
944
- position: "absolute",
945
- top: 0,
946
- left: 0,
947
- right: 0,
948
- bottom: 0,
949
- backgroundColor: "rgba(26, 20, 16, 0.35)",
950
- justifyContent: "flex-end",
951
- zIndex: 999,
952
- elevation: 999
953
- },
954
- sheet: {
955
- backgroundColor: ada2.paper,
956
- borderTopLeftRadius: 28,
957
- borderTopRightRadius: 28,
958
- overflow: "hidden"
959
- },
960
- handle: {
961
- width: 40,
962
- height: 4,
963
- borderRadius: 2,
964
- backgroundColor: ada2.inkMuted,
965
- opacity: 0.3,
966
- alignSelf: "center",
967
- marginTop: 10,
968
- marginBottom: 6
969
- },
970
- header: {
971
- flexDirection: "row",
972
- alignItems: "center",
973
- paddingHorizontal: 22,
974
- paddingVertical: 16,
975
- gap: 12,
976
- borderBottomWidth: 1,
977
- borderBottomColor: ada2.border
978
- },
979
- headerOrb: {
980
- width: 36,
981
- height: 36,
982
- alignItems: "center",
983
- justifyContent: "center"
984
- },
985
- title: {
986
- fontSize: 17,
987
- fontWeight: "600",
988
- color: ada2.ink,
989
- letterSpacing: -0.2,
990
- fontFamily: fontFam
991
- },
992
- subtitle: {
993
- fontSize: 13,
994
- fontWeight: "400",
995
- color: ada2.inkSoft,
996
- marginTop: 2,
997
- letterSpacing: -0.1,
998
- fontFamily: fontFam
999
- },
1000
- privacyPill: {
1001
- flexDirection: "row",
1002
- alignItems: "center",
1003
- gap: 5,
1004
- marginTop: 4
1005
- },
1006
- privacyText: {
1007
- fontSize: 11,
1008
- fontWeight: "500",
1009
- color: ada2.inkSoft,
1010
- letterSpacing: 0.2,
1011
- fontFamily: fontFam
1012
- },
1013
- closeBtn: {
1014
- width: 32,
1015
- height: 32,
1016
- borderRadius: 16,
1017
- backgroundColor: ada2.paperSoft,
1018
- justifyContent: "center",
1019
- alignItems: "center"
1020
- },
1021
- body: {
1022
- flex: 1
208
+ if (!pluginId) {
209
+ return deniedVoice(
210
+ "no plugin identity \u2014 voice fails closed without a caller to scope the session to."
211
+ );
1023
212
  }
1024
- });
1025
-
1026
- // src/surfaces/SurfaceCard.tsx
1027
- import {
1028
- View as View5,
1029
- Text as Text4,
1030
- Pressable as Pressable4,
1031
- StyleSheet as StyleSheet5,
1032
- Platform as Platform3
1033
- } from "react-native";
1034
- import { ada as ada3, borderRadius as borderRadius2 } from "@ateam-ai-mobile/core";
1035
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1036
- var fontFam2 = Platform3.OS === "ios" ? "SF Pro Rounded" : void 0;
1037
- function SurfaceCard({
1038
- icon,
1039
- title,
1040
- subtitle,
1041
- onPress,
1042
- onLongPress,
1043
- showArrow,
1044
- children
1045
- }) {
1046
- const arrow = showArrow ?? !!onPress;
1047
- const handlePress = () => {
1048
- if (!onPress) return;
1049
- getHaptics()?.impact("light");
1050
- onPress();
1051
- };
1052
- const handleLongPress = () => {
1053
- if (!onLongPress) return;
1054
- getHaptics()?.impact("medium");
1055
- onLongPress();
1056
- };
1057
- return /* @__PURE__ */ jsxs3(
1058
- Pressable4,
1059
- {
1060
- accessibilityRole: "button",
1061
- accessibilityLabel: [icon, title, subtitle].filter(Boolean).join(", "),
1062
- accessibilityHint: onLongPress ? "Long-press for more options" : void 0,
1063
- style: styles4.card,
1064
- onPress: onPress ? handlePress : void 0,
1065
- onLongPress: onLongPress ? handleLongPress : void 0,
1066
- disabled: !onPress && !onLongPress,
1067
- children: [
1068
- /* @__PURE__ */ jsxs3(View5, { style: styles4.row, children: [
1069
- icon ? /* @__PURE__ */ jsx3(Text4, { style: styles4.icon, children: icon }) : null,
1070
- /* @__PURE__ */ jsxs3(View5, { style: { flex: 1 }, children: [
1071
- /* @__PURE__ */ jsx3(Text4, { style: styles4.title, children: title }),
1072
- subtitle ? /* @__PURE__ */ jsx3(Text4, { style: styles4.subtitle, children: subtitle }) : null
1073
- ] }),
1074
- arrow ? /* @__PURE__ */ jsx3(Text4, { style: styles4.arrow, children: "\u2192" }) : null
1075
- ] }),
1076
- children ? /* @__PURE__ */ jsx3(View5, { style: styles4.body, children }) : null
1077
- ]
1078
- }
213
+ return _provider ? scopedVoice(_provider, pluginId) : deniedVoice(
214
+ "no voice session in this build \u2014 the app has not mounted a VoiceSessionHost / called setVoiceProvider."
1079
215
  );
1080
216
  }
1081
- var styles4 = StyleSheet5.create({
1082
- card: {
1083
- backgroundColor: ada3.paperSoft,
1084
- borderWidth: 1,
1085
- borderColor: ada3.border,
1086
- borderRadius: borderRadius2.xl,
1087
- paddingHorizontal: 16,
1088
- paddingVertical: 14
1089
- },
1090
- row: {
1091
- flexDirection: "row",
1092
- alignItems: "center",
1093
- gap: 14
1094
- },
1095
- icon: {
1096
- fontSize: 22
1097
- },
1098
- title: {
1099
- fontSize: 15,
1100
- fontWeight: "600",
1101
- color: ada3.ink,
1102
- letterSpacing: -0.2,
1103
- fontFamily: fontFam2
1104
- },
1105
- subtitle: {
1106
- fontSize: 13,
1107
- fontWeight: "400",
1108
- color: ada3.inkMuted,
1109
- marginTop: 2,
1110
- letterSpacing: -0.1,
1111
- fontFamily: fontFam2
1112
- },
1113
- arrow: {
1114
- fontSize: 18,
1115
- color: ada3.inkMuted,
1116
- fontWeight: "400"
1117
- },
1118
- body: {
1119
- marginTop: 10,
1120
- paddingTop: 10,
1121
- borderTopWidth: 1,
1122
- borderTopColor: ada3.border
1123
- }
1124
- });
1125
217
 
1126
- // src/surfaces/SurfaceFullscreen.tsx
1127
- import { useEffect as useEffect3, useRef as useRef2 } from "react";
1128
- import {
1129
- View as View6,
1130
- Text as Text5,
1131
- Pressable as Pressable5,
1132
- StyleSheet as StyleSheet6,
1133
- Animated as Animated4,
1134
- Platform as Platform4
1135
- } from "react-native";
1136
- import Svg3, { Path as Path2 } from "react-native-svg";
1137
- import { ada as ada4 } from "@ateam-ai-mobile/core";
1138
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1139
- var fontFam3 = Platform4.OS === "ios" ? "SF Pro Rounded" : void 0;
1140
- function SurfaceFullscreen({
1141
- visible,
1142
- onClose,
1143
- status,
1144
- hero,
1145
- children,
1146
- showWordmark = true,
1147
- closeIcon = "x",
1148
- closeLabel = "Close"
1149
- }) {
1150
- const fadeAnim = useRef2(new Animated4.Value(0)).current;
1151
- const heroScale = useRef2(new Animated4.Value(0.6)).current;
1152
- useEffect3(() => {
1153
- if (visible) {
1154
- Animated4.parallel([
1155
- Animated4.timing(fadeAnim, { toValue: 1, duration: 280, useNativeDriver: true }),
1156
- Animated4.spring(heroScale, { toValue: 1, useNativeDriver: true, tension: 60, friction: 11 })
1157
- ]).start();
1158
- } else {
1159
- fadeAnim.setValue(0);
1160
- heroScale.setValue(0.6);
1161
- }
1162
- }, [visible]);
1163
- const close = () => {
1164
- Animated4.parallel([
1165
- Animated4.timing(fadeAnim, { toValue: 0, duration: 180, useNativeDriver: true }),
1166
- Animated4.timing(heroScale, { toValue: 0.6, duration: 180, useNativeDriver: true })
1167
- ]).start(() => onClose());
1168
- };
1169
- if (!visible) return null;
1170
- const renderCloseIcon = () => {
1171
- if (closeIcon === "hangup") {
1172
- return /* @__PURE__ */ jsx4(Svg3, { width: 22, height: 22, viewBox: "0 0 24 24", fill: ada4.paper, children: /* @__PURE__ */ jsx4(Path2, { 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" }) });
1173
- }
1174
- if (closeIcon === "x") {
1175
- return /* @__PURE__ */ jsx4(Svg3, { width: 20, height: 20, viewBox: "0 0 24 24", fill: "none", stroke: ada4.paper, strokeWidth: 2.5, strokeLinecap: "round", children: /* @__PURE__ */ jsx4(Path2, { d: "M18 6L6 18M6 6l12 12" }) });
1176
- }
1177
- return closeIcon;
1178
- };
1179
- return /* @__PURE__ */ jsxs4(Animated4.View, { style: [styles5.overlay, { opacity: fadeAnim }], pointerEvents: "auto", children: [
1180
- /* @__PURE__ */ jsxs4(View6, { style: styles5.topBar, children: [
1181
- showWordmark ? /* @__PURE__ */ jsx4(Text5, { style: styles5.wordmark, children: "ada" }) : null,
1182
- status ? /* @__PURE__ */ jsx4(Text5, { style: styles5.status, children: status }) : null
1183
- ] }),
1184
- hero ? /* @__PURE__ */ jsx4(View6, { style: styles5.heroWrap, children: /* @__PURE__ */ jsx4(Animated4.View, { style: { transform: [{ scale: heroScale }] }, children: hero }) }) : null,
1185
- /* @__PURE__ */ jsx4(View6, { style: styles5.body, children }),
1186
- /* @__PURE__ */ jsxs4(View6, { style: styles5.bottomBar, children: [
1187
- /* @__PURE__ */ jsx4(
1188
- Pressable5,
1189
- {
1190
- accessibilityRole: "button",
1191
- accessibilityLabel: closeLabel,
1192
- style: styles5.closeBtn,
1193
- onPress: close,
1194
- hitSlop: 12,
1195
- children: renderCloseIcon()
1196
- }
1197
- ),
1198
- closeLabel ? /* @__PURE__ */ jsx4(Text5, { style: styles5.closeLabel, children: closeLabel }) : null
1199
- ] })
1200
- ] });
218
+ // src/pluginRuntime.ts
219
+ function mergeCaps(decl) {
220
+ if (!decl) return void 0;
221
+ return { ...decl.native, ...decl.capabilities };
222
+ }
223
+ function resolvePluginRuntimeProps(src) {
224
+ const trusted = src.manifest ?? src.plugin ?? null;
225
+ const capabilities = mergeCaps(trusted);
226
+ const stateDomains = trusted?.stateDomains;
227
+ const native = gatePluginNative(getPluginNative(), capabilities, src.pluginId);
228
+ const deviceState = gateDeviceState(stateDomains);
229
+ const voice = gateVoice(!!capabilities?.voice, src.pluginId);
230
+ return { device: native, native, deviceState, voice };
1201
231
  }
1202
- var styles5 = StyleSheet6.create({
1203
- overlay: {
1204
- position: "absolute",
1205
- top: 0,
1206
- left: 0,
1207
- right: 0,
1208
- bottom: 0,
1209
- backgroundColor: ada4.paper,
1210
- zIndex: 1e3,
1211
- elevation: 1e3
1212
- },
1213
- topBar: {
1214
- paddingTop: Platform4.OS === "ios" ? 64 : 36,
1215
- paddingHorizontal: 24,
1216
- alignItems: "center",
1217
- gap: 6
1218
- },
1219
- wordmark: {
1220
- fontSize: 14,
1221
- fontFamily: fontFam3,
1222
- fontWeight: "500",
1223
- color: ada4.inkMuted,
1224
- letterSpacing: 4
1225
- },
1226
- status: {
1227
- fontSize: 17,
1228
- fontFamily: fontFam3,
1229
- fontWeight: "500",
1230
- color: ada4.ink,
1231
- letterSpacing: -0.2
1232
- },
1233
- heroWrap: {
1234
- paddingTop: 24,
1235
- paddingBottom: 12,
1236
- alignItems: "center",
1237
- justifyContent: "center"
1238
- },
1239
- body: {
1240
- flex: 1
1241
- },
1242
- bottomBar: {
1243
- paddingBottom: Platform4.OS === "ios" ? 48 : 32,
1244
- paddingTop: 12,
1245
- alignItems: "center",
1246
- gap: 8
1247
- },
1248
- closeBtn: {
1249
- width: 64,
1250
- height: 64,
1251
- borderRadius: 32,
1252
- backgroundColor: ada4.ink,
1253
- justifyContent: "center",
1254
- alignItems: "center",
1255
- shadowColor: ada4.ink,
1256
- shadowOffset: { width: 0, height: 8 },
1257
- shadowOpacity: 0.18,
1258
- shadowRadius: 14,
1259
- elevation: 8
1260
- },
1261
- closeLabel: {
1262
- fontSize: 13,
1263
- fontFamily: fontFam3,
1264
- fontWeight: "500",
1265
- color: ada4.inkMuted,
1266
- letterSpacing: 0.3
1267
- }
1268
- });
1269
232
 
1270
- // src/registry/store.ts
1271
- import { create } from "zustand";
1272
- var useSurfaceStore = create((set, get) => ({
1273
- manifests: {},
1274
- activeDrawer: null,
1275
- activeFullscreen: null,
1276
- activeProps: {},
1277
- mountKey: 0,
1278
- register: (manifest) => set((s) => ({ manifests: { ...s.manifests, [manifest.id]: manifest } })),
1279
- unregister: (id) => set((s) => {
1280
- const next = { ...s.manifests };
1281
- delete next[id];
1282
- const nextProps = { ...s.activeProps };
1283
- delete nextProps[id];
1284
- return { manifests: next, activeProps: nextProps };
1285
- }),
1286
- show: (id, props) => {
1287
- const m = get().manifests[id];
1288
- if (!m) {
1289
- console.warn("[SurfaceStore] show: unknown surface id", id);
1290
- return;
1291
- }
1292
- set((s) => {
1293
- const nextProps = props !== void 0 ? { ...s.activeProps, [id]: props } : s.activeProps;
1294
- const mountKey = s.mountKey + 1;
1295
- if (m.type === "drawer") return { activeDrawer: id, activeProps: nextProps, mountKey };
1296
- if (m.type === "fullscreen") return { activeFullscreen: id, activeProps: nextProps, mountKey };
1297
- return { mountKey };
1298
- });
1299
- },
1300
- hide: (id) => {
1301
- const s = get();
1302
- if (!id) {
1303
- const cleanup2 = { ...s.activeProps };
1304
- if (s.activeDrawer) delete cleanup2[s.activeDrawer];
1305
- if (s.activeFullscreen) delete cleanup2[s.activeFullscreen];
1306
- set({ activeDrawer: null, activeFullscreen: null, activeProps: cleanup2 });
1307
- return;
233
+ // src/tools/platformTools.ts
234
+ var PLATFORM_PLUGIN_ID = "platform:device-tools";
235
+ function registerPlatformDeviceTools() {
236
+ registerDeviceTools(PLATFORM_PLUGIN_ID, [
237
+ {
238
+ name: "device.echo",
239
+ description: "Echo a string back from the user's own device.",
240
+ input_schema: {
241
+ type: "object",
242
+ properties: { text: { type: "string" } },
243
+ required: ["text"]
244
+ },
245
+ run: (args) => ({
246
+ ok: true,
247
+ echoed: args && args.text || "",
248
+ // The proof: a server-side implementation could not say this truthfully.
249
+ ran_on: "device"
250
+ })
251
+ },
252
+ {
253
+ name: "device.ping",
254
+ description: "Check that the device is reachable.",
255
+ input_schema: { type: "object", properties: {} },
256
+ run: () => ({ ok: true, ran_on: "device", at: (/* @__PURE__ */ new Date()).toISOString() })
1308
257
  }
1309
- const cleanup = { ...s.activeProps };
1310
- delete cleanup[id];
1311
- const patch = { activeProps: cleanup };
1312
- if (s.activeDrawer === id) patch.activeDrawer = null;
1313
- if (s.activeFullscreen === id) patch.activeFullscreen = null;
1314
- set(patch);
1315
- },
1316
- list: (filter) => {
1317
- const all = Object.values(get().manifests);
1318
- return all.filter((m) => {
1319
- if (filter?.type && m.type !== filter.type) return false;
1320
- if (filter?.visibility && !filter.visibility.includes(m.visibility)) return false;
1321
- if (filter?.placement) {
1322
- if (filter.placement === "unset") {
1323
- if (m.placement) return false;
1324
- } else if (m.placement !== filter.placement) {
1325
- return false;
1326
- }
1327
- }
1328
- return true;
1329
- });
1330
- }
1331
- }));
1332
- function isMountSurface(m) {
1333
- return m.type === "drawer" || m.type === "fullscreen";
1334
- }
1335
- function isInlineSurface(m) {
1336
- return m.type === "card" || m.type === "header" || m.type === "ambient" || m.type === "nudge";
258
+ ]);
1337
259
  }
1338
260
 
1339
- // src/registry/SurfaceDispatcher.tsx
1340
- import { useEffect as useEffect4 } from "react";
1341
- import { pluginCommandEmitter } from "@ateam-ai-mobile/core";
1342
- import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1343
- function SurfaceDispatcher() {
1344
- const manifests = useSurfaceStore((s) => s.manifests);
1345
- const activeDrawer = useSurfaceStore((s) => s.activeDrawer);
1346
- const activeFullscreen = useSurfaceStore((s) => s.activeFullscreen);
1347
- const activeProps = useSurfaceStore((s) => s.activeProps);
1348
- const mountKey = useSurfaceStore((s) => s.mountKey);
1349
- const hide = useSurfaceStore((s) => s.hide);
1350
- useEffect4(() => {
1351
- const unsub = pluginCommandEmitter.on("plugin-close", ({ pluginId } = {}) => {
1352
- if (!pluginId) return;
1353
- const s = useSurfaceStore.getState();
1354
- if (s.activeDrawer === pluginId || s.activeFullscreen === pluginId) {
1355
- s.hide(pluginId);
1356
- }
261
+ // src/tools/sessionSync.ts
262
+ function deviceToolsActionForSession(next, prevTenant2) {
263
+ const tenant = next.authenticated ? next.tenant ?? null : null;
264
+ if (tenant && tenant !== prevTenant2) return { action: "load", tenant };
265
+ if (!tenant && prevTenant2) return { action: "reset", tenant: null };
266
+ return { action: "none", tenant: prevTenant2 };
267
+ }
268
+ var REAL_DEPS = {
269
+ clear: clearDeviceTools,
270
+ registerPlatform: registerPlatformDeviceTools,
271
+ // `./loader` reaches react-native via the bundle manager; import it only when
272
+ // an effect actually fires (real app), never at module load (tests).
273
+ resetLoader: () => {
274
+ void import("./loader-5LUVXUY2.mjs").then((m) => m.resetDeviceToolLoader());
275
+ },
276
+ load: () => {
277
+ void import("./loader-5LUVXUY2.mjs").then((m) => {
278
+ m.resetDeviceToolLoader();
279
+ return m.ensureDeviceToolsLoaded({ force: true });
1357
280
  });
1358
- return unsub;
1359
- }, []);
1360
- const drawer = activeDrawer ? manifests[activeDrawer] : null;
1361
- const fullscreen = activeFullscreen ? manifests[activeFullscreen] : null;
1362
- const drawerProps = activeDrawer ? activeProps[activeDrawer] : void 0;
1363
- const fullscreenProps = activeFullscreen ? activeProps[activeFullscreen] : void 0;
1364
- return /* @__PURE__ */ jsxs5(Fragment, { children: [
1365
- drawer && drawer.type === "drawer" && drawer.Component ? /* @__PURE__ */ jsx5(
1366
- drawer.Component,
1367
- {
1368
- dismiss: () => hide(drawer.id),
1369
- props: drawerProps
1370
- },
1371
- `drawer-${drawer.id}-${mountKey}`
1372
- ) : null,
1373
- fullscreen && fullscreen.type === "fullscreen" && fullscreen.Component ? /* @__PURE__ */ jsx5(
1374
- fullscreen.Component,
1375
- {
1376
- dismiss: () => hide(fullscreen.id),
1377
- props: fullscreenProps
1378
- },
1379
- `fullscreen-${fullscreen.id}-${mountKey}`
1380
- ) : null
1381
- ] });
1382
- }
1383
-
1384
- // src/utils/sha256.ts
1385
- var K = new Uint32Array([
1386
- 1116352408,
1387
- 1899447441,
1388
- 3049323471,
1389
- 3921009573,
1390
- 961987163,
1391
- 1508970993,
1392
- 2453635748,
1393
- 2870763221,
1394
- 3624381080,
1395
- 310598401,
1396
- 607225278,
1397
- 1426881987,
1398
- 1925078388,
1399
- 2162078206,
1400
- 2614888103,
1401
- 3248222580,
1402
- 3835390401,
1403
- 4022224774,
1404
- 264347078,
1405
- 604807628,
1406
- 770255983,
1407
- 1249150122,
1408
- 1555081692,
1409
- 1996064986,
1410
- 2554220882,
1411
- 2821834349,
1412
- 2952996808,
1413
- 3210313671,
1414
- 3336571891,
1415
- 3584528711,
1416
- 113926993,
1417
- 338241895,
1418
- 666307205,
1419
- 773529912,
1420
- 1294757372,
1421
- 1396182291,
1422
- 1695183700,
1423
- 1986661051,
1424
- 2177026350,
1425
- 2456956037,
1426
- 2730485921,
1427
- 2820302411,
1428
- 3259730800,
1429
- 3345764771,
1430
- 3516065817,
1431
- 3600352804,
1432
- 4094571909,
1433
- 275423344,
1434
- 430227734,
1435
- 506948616,
1436
- 659060556,
1437
- 883997877,
1438
- 958139571,
1439
- 1322822218,
1440
- 1537002063,
1441
- 1747873779,
1442
- 1955562222,
1443
- 2024104815,
1444
- 2227730452,
1445
- 2361852424,
1446
- 2428436474,
1447
- 2756734187,
1448
- 3204031479,
1449
- 3329325298
1450
- ]);
1451
- var rotr = (n, x) => x >>> n | x << 32 - n;
1452
- function sha256Hex(message) {
1453
- const utf8 = unescape(encodeURIComponent(message));
1454
- const bytes = new Uint8Array(utf8.length);
1455
- for (let i = 0; i < utf8.length; i++) bytes[i] = utf8.charCodeAt(i);
1456
- const bitLen = bytes.length * 8;
1457
- const padLen = (56 - (bytes.length + 1) % 64 + 64) % 64;
1458
- const buf = new Uint8Array(bytes.length + 1 + padLen + 8);
1459
- buf.set(bytes);
1460
- buf[bytes.length] = 128;
1461
- const len = buf.length;
1462
- buf[len - 4] = bitLen >>> 24 & 255;
1463
- buf[len - 3] = bitLen >>> 16 & 255;
1464
- buf[len - 2] = bitLen >>> 8 & 255;
1465
- buf[len - 1] = bitLen & 255;
1466
- const h = new Uint32Array([
1467
- 1779033703,
1468
- 3144134277,
1469
- 1013904242,
1470
- 2773480762,
1471
- 1359893119,
1472
- 2600822924,
1473
- 528734635,
1474
- 1541459225
1475
- ]);
1476
- const w = new Uint32Array(64);
1477
- for (let i = 0; i < buf.length; i += 64) {
1478
- for (let t = 0; t < 16; t++) {
1479
- const j = i + t * 4;
1480
- w[t] = buf[j] << 24 | buf[j + 1] << 16 | buf[j + 2] << 8 | buf[j + 3];
1481
- }
1482
- for (let t = 16; t < 64; t++) {
1483
- const s0 = rotr(7, w[t - 15]) ^ rotr(18, w[t - 15]) ^ w[t - 15] >>> 3;
1484
- const s1 = rotr(17, w[t - 2]) ^ rotr(19, w[t - 2]) ^ w[t - 2] >>> 10;
1485
- w[t] = w[t - 16] + s0 + w[t - 7] + s1 | 0;
1486
- }
1487
- 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];
1488
- for (let t = 0; t < 64; t++) {
1489
- const S1 = rotr(6, e) ^ rotr(11, e) ^ rotr(25, e);
1490
- const ch = e & f ^ ~e & g;
1491
- const temp1 = hh + S1 + ch + K[t] + w[t] | 0;
1492
- const S0 = rotr(2, a) ^ rotr(13, a) ^ rotr(22, a);
1493
- const maj = a & b ^ a & c ^ b & c;
1494
- const temp2 = S0 + maj | 0;
1495
- hh = g;
1496
- g = f;
1497
- f = e;
1498
- e = d + temp1 | 0;
1499
- d = c;
1500
- c = b;
1501
- b = a;
1502
- a = temp1 + temp2 | 0;
1503
- }
1504
- h[0] = h[0] + a | 0;
1505
- h[1] = h[1] + b | 0;
1506
- h[2] = h[2] + c | 0;
1507
- h[3] = h[3] + d | 0;
1508
- h[4] = h[4] + e | 0;
1509
- h[5] = h[5] + f | 0;
1510
- h[6] = h[6] + g | 0;
1511
- h[7] = h[7] + hh | 0;
1512
- }
1513
- let out = "";
1514
- for (let i = 0; i < 8; i++) {
1515
- out += h[i].toString(16).padStart(8, "0");
1516
281
  }
1517
- return out;
282
+ };
283
+ var prevTenant = null;
284
+ function syncDeviceToolsForSession(next, deps = REAL_DEPS) {
285
+ const { action, tenant } = deviceToolsActionForSession(next, prevTenant);
286
+ prevTenant = tenant;
287
+ if (action === "load") {
288
+ deps.clear();
289
+ deps.registerPlatform();
290
+ deps.load();
291
+ } else if (action === "reset") {
292
+ deps.resetLoader();
293
+ }
294
+ return action;
295
+ }
296
+ function __resetSessionSyncForTest() {
297
+ prevTenant = null;
1518
298
  }
1519
299
 
1520
- // src/bundles/storage.ts
1521
- var MemoryBundleStorage = class {
1522
- constructor() {
1523
- this.index = {};
1524
- this.files = /* @__PURE__ */ new Map();
1525
- }
1526
- async loadIndex() {
1527
- return this.index;
1528
- }
1529
- async saveIndex(index) {
1530
- this.index = index;
1531
- }
1532
- async readBundle(localPath) {
1533
- return this.files.get(localPath) ?? null;
1534
- }
1535
- async writeBundle(key, content) {
1536
- const localPath = `memory://${key}`;
1537
- this.files.set(localPath, content);
1538
- return localPath;
1539
- }
1540
- async deleteBundle(localPath) {
1541
- this.files.delete(localPath);
1542
- }
1543
- async clearAll() {
1544
- this.index = {};
1545
- this.files.clear();
1546
- }
1547
- };
1548
- var _storage = new MemoryBundleStorage();
1549
- function setBundleStorage(storage) {
1550
- _storage = storage;
300
+ // src/tools/serverCalls.ts
301
+ var postResult = null;
302
+ function setDeviceToolResultPoster(fn) {
303
+ postResult = fn;
1551
304
  }
1552
- function getBundleStorage() {
1553
- return _storage;
305
+ function isDeviceToolCommand(cmd) {
306
+ return !!cmd && cmd.kind === "device_tool" && typeof cmd.tool === "string" && !!cmd.correlationId;
1554
307
  }
1555
-
1556
- // src/bundles/bundleManager.ts
1557
- var ReactShim = {
1558
- __esModule: true,
1559
- default: React2,
1560
- ...React2,
1561
- // Explicitly include hooks (React 19 may not have them as own enumerable props)
1562
- useState,
1563
- useEffect: useEffect5,
1564
- useCallback,
1565
- useMemo: useMemo3,
1566
- useRef: useRef3,
1567
- useContext,
1568
- useReducer,
1569
- useLayoutEffect,
1570
- useImperativeHandle,
1571
- useDebugValue,
1572
- createElement: React2.createElement,
1573
- Fragment: React2.Fragment,
1574
- Component: React2.Component,
1575
- PureComponent: React2.PureComponent,
1576
- createContext: React2.createContext,
1577
- forwardRef: React2.forwardRef,
1578
- memo: React2.memo,
1579
- Children: React2.Children,
1580
- cloneElement: React2.cloneElement,
1581
- isValidElement: React2.isValidElement,
1582
- createRef: React2.createRef,
1583
- lazy: React2.lazy,
1584
- Suspense: React2.Suspense
1585
- };
1586
- var PluginSDKShim = Object.freeze({
1587
- ...sdk_exports,
1588
- SurfaceDrawer,
1589
- SurfaceCard,
1590
- SurfaceFullscreen,
1591
- useSurfaceStore,
1592
- SurfaceDispatcher,
1593
- PluginSDK: Object.freeze({
1594
- ...PluginSDK,
1595
- register: PluginSDK.register
1596
- }),
1597
- register
1598
- });
1599
- var requireShim = (name) => {
1600
- if (name === "react") return ReactShim;
1601
- if (name === "react-native") return ReactNative;
1602
- if (name === "@adas/plugin-sdk") return PluginSDKShim;
1603
- const hostModule = resolveBundleModule(name);
1604
- if (hostModule !== void 0) return hostModule;
1605
- console.warn(`[BundleManager] Unknown module: ${name} \u2014 register it via registerBundleModules()`);
1606
- return {};
1607
- };
1608
- var CACHE_TTL = 7 * 24 * 60 * 60 * 1e3;
1609
- var BundleManager = class {
1610
- constructor() {
1611
- /** In-memory module cache: bundleUrl → evaluated module.exports */
1612
- this.modules = /* @__PURE__ */ new Map();
1613
- /** In-flight download deduplication: bundleUrl → Promise */
1614
- this.inflight = /* @__PURE__ */ new Map();
1615
- }
1616
- // ── Public API ──────────────────────────────────────────────
1617
- /**
1618
- * Load a plugin component. Returns the RegisteredUIPlugin object.
1619
- * Handles: in-memory cache → disk cache → download → evaluate → extract.
1620
- *
1621
- * `expectedHash` (SHA-256 hex from cp.getContextPlugin manifest) MUST be
1622
- * provided for any bundle the user has not previously verified — without
1623
- * it, a tampered bundle is `eval`'d as JS with full RN runtime + every
1624
- * native capability the manifest declares.
1625
- */
1626
- async load(pluginId, bundleUrl, version, expectedHash) {
1627
- const cached = this.getComponentFromMemory(pluginId, bundleUrl);
1628
- if (cached) return cached;
1629
- let modulePromise = this.inflight.get(bundleUrl);
1630
- if (!modulePromise) {
1631
- modulePromise = this.loadModule(bundleUrl, version, expectedHash);
1632
- this.inflight.set(bundleUrl, modulePromise);
1633
- modulePromise.catch(() => {
1634
- }).finally(() => this.inflight.delete(bundleUrl));
1635
- }
1636
- await modulePromise;
1637
- const component = this.getComponentFromMemory(pluginId, bundleUrl);
1638
- if (!component) {
1639
- throw new Error(
1640
- `Bundle loaded from ${bundleUrl} but no component found for "${pluginId}". Available exports: ${this.describeModule(this.modules.get(bundleUrl))}`
1641
- );
1642
- }
1643
- return component;
1644
- }
1645
- /**
1646
- * Prewarm: load every plugin bundle in the background after app boot, so a
1647
- * later open is instant (in-memory hit) instead of round-tripping the
1648
- * network. Errors are swallowed per-plugin — one bad bundle shouldn't
1649
- * block others. Concurrency capped at 3.
1650
- */
1651
- async prewarmAll(items) {
1652
- const queue = [...items];
1653
- const concurrency = Math.min(3, queue.length);
1654
- const start = Date.now();
1655
- let okCount = 0;
1656
- let failCount = 0;
1657
- const worker = async () => {
1658
- while (queue.length > 0) {
1659
- const it = queue.shift();
1660
- if (!it) return;
1661
- try {
1662
- await this.load(it.pluginId, it.bundleUrl, it.version, it.bundleHash);
1663
- okCount++;
1664
- } catch (err) {
1665
- failCount++;
1666
- console.log(`[prewarm] ${it.pluginId}: ${err instanceof Error ? err.message : String(err)}`);
1667
- }
1668
- }
1669
- };
1670
- await Promise.all(Array.from({ length: concurrency }, worker));
1671
- console.log(`[prewarm] ${okCount} loaded, ${failCount} failed in ${Date.now() - start}ms`);
1672
- }
1673
- /**
1674
- * Clear all caches (in-memory + storage).
1675
- */
1676
- async clearAll() {
1677
- this.modules.clear();
1678
- try {
1679
- await getBundleStorage().clearAll();
1680
- } catch (err) {
1681
- console.warn("[BundleManager] clearAll error:", err);
1682
- }
1683
- }
1684
- /**
1685
- * Clear cache for a specific plugin/bundle.
1686
- */
1687
- async clearPlugin(pluginId) {
1688
- const storage = getBundleStorage();
1689
- const index = await storage.loadIndex();
1690
- const entry = index[pluginId];
1691
- if (entry) {
1692
- this.modules.delete(entry.bundleUrl);
1693
- delete index[pluginId];
1694
- await storage.saveIndex(index);
1695
- try {
1696
- await storage.deleteBundle(entry.localPath);
1697
- } catch {
1698
- }
1699
- }
1700
- }
1701
- // ── Private: module loading pipeline ────────────────────────
1702
- async loadModule(bundleUrl, version, expectedHash) {
1703
- if (this.modules.has(bundleUrl)) return;
1704
- const storage = getBundleStorage();
1705
- const index = await storage.loadIndex();
1706
- const expectedLc = expectedHash ? expectedHash.toLowerCase() : null;
1707
- let content = null;
1708
- for (const entry of Object.values(index)) {
1709
- if (entry.bundleUrl === bundleUrl && entry.version === version) {
1710
- const age = Date.now() - entry.timestamp;
1711
- const hashOk = expectedLc && entry.bundleHash && entry.bundleHash.toLowerCase() === expectedLc;
1712
- if (age < CACHE_TTL && hashOk) {
1713
- content = await storage.readBundle(entry.localPath);
1714
- if (content) break;
1715
- }
1716
- }
1717
- }
1718
- if (!content) {
1719
- const downloaded = await this.download(bundleUrl, expectedLc);
1720
- content = downloaded.content;
1721
- const safeName = bundleUrl.replace(/[^a-zA-Z0-9]/g, "_").slice(-80);
1722
- try {
1723
- const localPath = await storage.writeBundle(`${safeName}@${version}.js`, content);
1724
- index[bundleUrl] = {
1725
- localPath,
1726
- version,
1727
- timestamp: Date.now(),
1728
- bundleUrl,
1729
- bundleHash: downloaded.sha256
1730
- };
1731
- await storage.saveIndex(index);
1732
- } catch (err) {
1733
- console.warn("[BundleManager] Cache write error:", err);
1734
- }
1735
- }
1736
- const mod = this.evaluate(content, bundleUrl);
1737
- this.modules.set(bundleUrl, mod);
308
+ async function handleServerDeviceToolCall(cmd) {
309
+ if (!isDeviceToolCommand(cmd)) return;
310
+ const { tool, args, correlationId } = cmd;
311
+ if (!postResult) {
312
+ console.warn("[deviceTools] server called a tool but no result poster is installed");
313
+ return;
1738
314
  }
1739
- /**
1740
- * Download + integrity-verify a bundle. Refuses to return when:
1741
- * - manifest hash and computed hash disagree
1742
- * - server's `X-Bundle-SHA256` disagrees with manifest hash
1743
- * - no expected hash AND no server hash header (cannot prove the bundle
1744
- * wasn't tampered with — fail closed rather than trust the wire).
1745
- */
1746
- async download(bundleUrl, expectedHash) {
1747
- const cacheBust = `${bundleUrl}${bundleUrl.includes("?") ? "&" : "?"}t=${Date.now()}`;
1748
- const response = await apiFetchRaw(cacheBust);
1749
- if (!response.ok) {
1750
- const body = await response.text().catch(() => "");
1751
- throw new Error(`Download failed: ${response.status} ${response.statusText} \u2014 ${body.substring(0, 200)}`);
1752
- }
1753
- const content = await response.text();
1754
- if (content.length < 50 || content.trimStart().startsWith("<!")) {
1755
- throw new Error("Bundle content is empty or HTML \u2014 server may have returned an error page");
1756
- }
1757
- const headerHash = (response.headers.get("x-bundle-sha256") || "").toLowerCase();
1758
- const computed = sha256Hex(content);
1759
- if (expectedHash) {
1760
- if (computed !== expectedHash) {
1761
- throw new Error(
1762
- `Bundle integrity FAIL: expected sha256=${expectedHash.slice(0, 12)}... got ${computed.slice(0, 12)}.... Refusing to evaluate.`
1763
- );
1764
- }
1765
- if (headerHash && headerHash !== expectedHash) {
1766
- throw new Error(
1767
- `Bundle hash mismatch: manifest=${expectedHash.slice(0, 12)}... server=${headerHash.slice(0, 12)}.... Refusing to evaluate.`
1768
- );
1769
- }
1770
- } else if (headerHash) {
1771
- if (computed !== headerHash) {
1772
- throw new Error(
1773
- `Bundle body sha256 (${computed.slice(0, 12)}...) does not match server's X-Bundle-SHA256 (${headerHash.slice(0, 12)}...).`
1774
- );
1775
- }
1776
- console.warn(`[BundleManager] No manifest bundleHash \u2014 verified via X-Bundle-SHA256 header only`);
1777
- } else {
1778
- throw new Error(
1779
- `No bundleHash in manifest and no X-Bundle-SHA256 header from server. Refusing to evaluate untrusted bundle.`
1780
- );
1781
- }
1782
- return { content, sha256: computed };
1783
- }
1784
- evaluate(content, bundleUrl) {
1785
- let cleaned = content.replace(
1786
- /__toESM\(require\(("[^"]+")\)(?:,\s*\d+)?\)/g,
1787
- "require($1)"
1788
- );
1789
- cleaned = cleaned.replace(
1790
- /module\.exports\s*=\s*__toCommonJS\((\w+)\)/g,
1791
- "module.exports = $1"
315
+ try {
316
+ await ensureDeviceToolsLoaded();
317
+ const outcome = await callDeviceTool(tool, args || {});
318
+ console.log(`[deviceTools] server-called ${tool} \u2192 ${outcome.ok ? "ok" : outcome.reason}`);
319
+ await postResult(
320
+ correlationId,
321
+ outcome.ok ? { ok: true, ...unwrap(outcome.result) } : void 0,
322
+ outcome.ok ? void 0 : outcome.error
1792
323
  );
1793
- const mod = { exports: {} };
1794
- try {
1795
- const factory = new Function(
1796
- "module",
1797
- "exports",
1798
- "require",
1799
- "__filename",
1800
- "__dirname",
1801
- cleaned
1802
- );
1803
- factory(mod, mod.exports, requireShim, bundleUrl, "adas-plugin-bundles");
1804
- } catch (err) {
1805
- throw new Error(`Bundle evaluation failed: ${err instanceof Error ? err.message : String(err)}`);
1806
- }
1807
- const raw = mod.exports;
1808
- const materialized = {};
1809
- for (const key of Object.getOwnPropertyNames(raw)) {
1810
- try {
1811
- materialized[key] = raw[key];
1812
- } catch {
1813
- }
1814
- }
1815
- return materialized;
1816
- }
1817
- // ── Private: component extraction ───────────────────────────
1818
- /**
1819
- * Find a plugin component in a loaded module.
1820
- * Handles: default export, direct key match, multi-plugin .id match.
1821
- */
1822
- getComponentFromMemory(pluginId, bundleUrl) {
1823
- const mod = this.modules.get(bundleUrl);
1824
- if (!mod) return null;
1825
- const shortName = pluginId.includes(":") ? pluginId.split(":").pop() : pluginId;
1826
- if (mod[pluginId] && typeof mod[pluginId].Component === "function") {
1827
- return mod[pluginId];
1828
- }
1829
- const pascal = shortName.replace(/(^|-)([a-z])/g, (_, __, c) => c.toUpperCase());
1830
- if (mod[pascal] && typeof mod[pascal] === "object" && typeof mod[pascal].Component === "function") {
1831
- return mod[pascal];
1832
- }
1833
- if (mod.default && typeof mod.default === "object" && typeof mod.default.Component === "function") {
1834
- return mod.default;
1835
- }
1836
- for (const key of Object.keys(mod)) {
1837
- const val = mod[key];
1838
- if (val && typeof val === "object" && val.id === shortName && typeof val.Component === "function") {
1839
- return val;
1840
- }
1841
- }
1842
- return null;
1843
- }
1844
- describeModule(mod) {
1845
- if (!mod) return "(empty)";
1846
- return Object.keys(mod).map((k) => {
1847
- const v = mod[k];
1848
- if (v && typeof v === "object" && v.id) return `${k}(id="${v.id}")`;
1849
- return k;
1850
- }).join(", ");
1851
- }
1852
- };
1853
- var bundleManager = new BundleManager();
1854
-
1855
- // src/bridge/native.ts
1856
- import { useCallback as useCallback2, useMemo as useMemo4, useRef as useRef4, useEffect as useEffect6 } from "react";
1857
- import {
1858
- connectorCall,
1859
- mcpFetch as mcpFetchApi,
1860
- unwrapMcpResult,
1861
- postCommandResult,
1862
- getTenant,
1863
- getUser,
1864
- pluginCommandEmitter as pluginCommandEmitter2,
1865
- useChatStore
1866
- } from "@ateam-ai-mobile/core";
1867
- function buildContext(pluginId, connectorId) {
1868
- const user = getUser();
1869
- return {
1870
- tenant: getTenant(),
1871
- pluginId,
1872
- connectorId,
1873
- user: user ? { id: user.actorId || "", name: user.name || "", roles: user.roles || [] } : void 0
1874
- };
1875
- }
1876
- function useNativeBridge(pluginId, connectorId, commandBufferRef) {
1877
- const initCallbackRef = useRef4(null);
1878
- const commandListenersRef = useRef4(/* @__PURE__ */ new Set());
1879
- const initializedRef = useRef4(false);
1880
- const fireInit = useCallback2(() => {
1881
- if (initializedRef.current || !initCallbackRef.current) return;
1882
- initCallbackRef.current(buildContext(pluginId, connectorId));
1883
- initializedRef.current = true;
1884
- }, [pluginId, connectorId]);
1885
- const onInit = useCallback2((cb) => {
1886
- initCallbackRef.current = cb;
1887
- setTimeout(fireInit, 0);
1888
- }, [fireInit]);
1889
- const mcpCall = useCallback2(async (toolOrConnector, argsOrTool, cIdOrArgs) => {
1890
- let tool;
1891
- let args;
1892
- let cId;
1893
- if (typeof argsOrTool === "string") {
1894
- cId = toolOrConnector;
1895
- tool = argsOrTool;
1896
- args = cIdOrArgs || {};
1897
- } else {
1898
- tool = toolOrConnector;
1899
- args = argsOrTool;
1900
- cId = typeof cIdOrArgs === "string" ? cIdOrArgs : void 0;
1901
- }
1902
- const targetConnector = cId || connectorId;
1903
- if (!targetConnector) {
1904
- console.warn(`[NativeBridge:${pluginId}] mcpCall failed \u2014 no connectorId. tool=${tool}`);
1905
- throw new Error(`[NativeBridge:${pluginId}] mcpCall requires a connectorId`);
1906
- }
1907
- try {
1908
- const data = await connectorCall(targetConnector, tool, args);
1909
- if (!data.ok && data.error) {
1910
- throw new Error(data.error);
1911
- }
1912
- return unwrapMcpResult(data.result);
1913
- } catch (err) {
1914
- console.warn(`[NativeBridge:${pluginId}] mcpCall failed \u2192 connector=${targetConnector} tool=${tool}:`, err?.message || err);
1915
- throw err;
1916
- }
1917
- }, [pluginId, connectorId]);
1918
- const mcpFetch = useCallback2(async (body) => {
1919
- return mcpFetchApi(body);
1920
- }, []);
1921
- const sendCommand = useCallback2((type, payload) => {
1922
- if (type === "plugin.command.result" && payload?.correlationId) {
1923
- postCommandResult(payload.correlationId, payload.result, payload.error);
1924
- return;
1925
- }
1926
- }, [pluginId]);
1927
- const onCommand = useCallback2((cb) => {
1928
- commandListenersRef.current.add(cb);
1929
- if (commandBufferRef) {
1930
- const buf = commandBufferRef.current;
1931
- const remaining = [];
1932
- for (const cmd of buf) {
1933
- if (cmd.pluginId === pluginId && cmd.command !== "__focus") {
1934
- try {
1935
- cb(cmd.command, { ...cmd.args, correlationId: cmd.correlationId });
1936
- } catch {
1937
- }
1938
- } else {
1939
- remaining.push(cmd);
1940
- }
1941
- }
1942
- commandBufferRef.current = remaining;
1943
- }
1944
- for (const cmd of pluginCommandEmitter2.drainPluginCommands(pluginId)) {
1945
- try {
1946
- cb(cmd.command, { ...cmd.args, correlationId: cmd.correlationId });
1947
- } catch {
1948
- }
1949
- }
1950
- return () => {
1951
- commandListenersRef.current.delete(cb);
1952
- };
1953
- }, [pluginId, commandBufferRef]);
1954
- useEffect6(() => {
1955
- const unsub = pluginCommandEmitter2.on("plugin-command", (cmd) => {
1956
- if (cmd.pluginId !== pluginId) return;
1957
- if (cmd.command === "__focus") return;
1958
- commandListenersRef.current.forEach((fn) => {
1959
- try {
1960
- fn(cmd.command, { ...cmd.args, correlationId: cmd.correlationId });
1961
- } catch {
1962
- }
1963
- });
324
+ } catch (err) {
325
+ await postResult(correlationId, void 0, err?.message || String(err)).catch(() => {
1964
326
  });
1965
- return unsub;
1966
- }, [pluginId]);
1967
- useEffect6(() => {
1968
- const timer = setTimeout(() => {
1969
- const dispatch = (cmd) => {
1970
- commandListenersRef.current.forEach((fn) => {
1971
- try {
1972
- fn(cmd.command, { ...cmd.args, correlationId: cmd.correlationId });
1973
- } catch {
1974
- }
1975
- });
1976
- };
1977
- if (commandBufferRef) {
1978
- const remaining = [];
1979
- for (const cmd of commandBufferRef.current) {
1980
- if (cmd.pluginId === pluginId && cmd.command !== "__focus") dispatch(cmd);
1981
- else remaining.push(cmd);
1982
- }
1983
- commandBufferRef.current = remaining;
1984
- }
1985
- for (const cmd of pluginCommandEmitter2.drainPluginCommands(pluginId)) {
1986
- dispatch(cmd);
1987
- }
1988
- }, 500);
1989
- return () => clearTimeout(timer);
1990
- }, [pluginId, commandBufferRef]);
1991
- useEffect6(() => {
1992
- return () => {
1993
- commandListenersRef.current.clear();
1994
- initializedRef.current = false;
1995
- };
1996
- }, []);
1997
- const close = useCallback2(() => {
1998
- pluginCommandEmitter2.emit("plugin-close", { pluginId });
1999
- }, [pluginId]);
2000
- const sendMessage = useCallback2((text) => {
2001
- const t = (text || "").trim();
2002
- if (!t) return;
2003
- try {
2004
- useChatStore.getState().sendMessage(t);
2005
- } catch (err) {
2006
- console.warn("[bridge.sendMessage] failed:", err instanceof Error ? err.message : String(err));
2007
- }
2008
- }, []);
2009
- const prepareMessage = useCallback2((text) => {
2010
- const t = (text || "").trim();
2011
- if (!t) return;
2012
- pluginCommandEmitter2.emit("prepare-chat-message", { text: t });
2013
- }, []);
2014
- return useMemo4(
2015
- () => ({ onInit, mcpCall, mcpFetch, sendCommand, onCommand, close, sendMessage, prepareMessage }),
2016
- [onInit, mcpCall, mcpFetch, sendCommand, onCommand, close, sendMessage, prepareMessage]
2017
- );
327
+ }
2018
328
  }
2019
- function createNativeBridge(pluginId, connectorId) {
2020
- const commandListeners = /* @__PURE__ */ new Set();
2021
- return {
2022
- onInit(cb) {
2023
- setTimeout(() => {
2024
- cb(buildContext(pluginId, connectorId));
2025
- }, 0);
2026
- },
2027
- async mcpCall(tool, args, cId) {
2028
- const targetConnector = cId || connectorId;
2029
- if (!targetConnector) {
2030
- throw new Error(`[NativeBridge:${pluginId}] mcpCall requires a connectorId`);
2031
- }
2032
- const data = await connectorCall(targetConnector, tool, args);
2033
- if (!data.ok && data.error) throw new Error(data.error);
2034
- return unwrapMcpResult(data.result);
2035
- },
2036
- async mcpFetch(body) {
2037
- return mcpFetchApi(body);
2038
- },
2039
- sendCommand(_type, _payload) {
2040
- },
2041
- onCommand(cb) {
2042
- commandListeners.add(cb);
2043
- return () => {
2044
- commandListeners.delete(cb);
2045
- };
2046
- },
2047
- close() {
2048
- pluginCommandEmitter2.emit("plugin-close", { pluginId });
2049
- },
2050
- sendMessage(text) {
2051
- const t = (text || "").trim();
2052
- if (!t) return;
2053
- try {
2054
- useChatStore.getState().sendMessage(t);
2055
- } catch (err) {
2056
- console.warn("[bridge.sendMessage] failed:", err instanceof Error ? err.message : String(err));
2057
- }
2058
- },
2059
- prepareMessage(text) {
2060
- const t = (text || "").trim();
2061
- if (!t) return;
2062
- pluginCommandEmitter2.emit("prepare-chat-message", { text: t });
2063
- }
2064
- };
329
+ function unwrap(result) {
330
+ if (result && typeof result === "object" && !Array.isArray(result)) return result;
331
+ return { result };
2065
332
  }
2066
333
 
2067
334
  // src/hooks/usePlugin.ts
2068
- import { useState as useState2, useEffect as useEffect7 } from "react";
335
+ import { useState, useEffect } from "react";
2069
336
  function extractBundleUrl(pluginId, manifest) {
2070
337
  const render = manifest.render;
2071
338
  if (!render || typeof render !== "object") return { url: null, source: "none" };
@@ -2094,10 +361,10 @@ function extractComponentId(manifest) {
2094
361
  return render.component || render.reactNative?.component || null;
2095
362
  }
2096
363
  function usePlugin(pluginId, manifest) {
2097
- const [plugin, setPlugin] = useState2(null);
2098
- const [loading, setLoading] = useState2(true);
2099
- const [error, setError] = useState2(null);
2100
- const [bundleMissing, setBundleMissing] = useState2(false);
364
+ const [plugin, setPlugin] = useState(null);
365
+ const [loading, setLoading] = useState(true);
366
+ const [error, setError] = useState(null);
367
+ const [bundleMissing, setBundleMissing] = useState(false);
2101
368
  const version = manifest.version || "1.0.0";
2102
369
  const { url: bundleUrl, ignored: ignoredUrl } = extractBundleUrl(pluginId, manifest);
2103
370
  const bundleHash = extractBundleHash(manifest);
@@ -2106,7 +373,7 @@ function usePlugin(pluginId, manifest) {
2106
373
  if (ignoredUrl) {
2107
374
  console.warn(`[usePlugin] ${pluginId}: IGNORED connector-injected bundleUrl="${ignoredUrl}". The host derives bundle URLs from pluginId via the canonical route.`);
2108
375
  }
2109
- useEffect7(() => {
376
+ useEffect(() => {
2110
377
  let cancelled = false;
2111
378
  (async () => {
2112
379
  try {
@@ -2167,14 +434,14 @@ function usePlugin(pluginId, manifest) {
2167
434
  }
2168
435
 
2169
436
  // src/hooks/useRemotePlugins.ts
2170
- import { useEffect as useEffect8, useState as useState3, useCallback as useCallback3 } from "react";
437
+ import { useEffect as useEffect2, useState as useState2, useCallback } from "react";
2171
438
  import { apiFetch } from "@ateam-ai-mobile/core";
2172
439
  function useRemotePlugins(solutionId) {
2173
- const [plugins, setPlugins] = useState3([]);
2174
- const [loading, setLoading] = useState3(true);
2175
- const [refreshing, setRefreshing] = useState3(false);
2176
- const [error, setError] = useState3(null);
2177
- const fetchRemotePlugins = useCallback3(async () => {
440
+ const [plugins, setPlugins] = useState2([]);
441
+ const [loading, setLoading] = useState2(true);
442
+ const [refreshing, setRefreshing] = useState2(false);
443
+ const [error, setError] = useState2(null);
444
+ const fetchRemotePlugins = useCallback(async () => {
2178
445
  try {
2179
446
  setError(null);
2180
447
  const targetSolutionId = solutionId || "current";
@@ -2219,11 +486,11 @@ function useRemotePlugins(solutionId) {
2219
486
  setRefreshing(false);
2220
487
  }
2221
488
  }, [solutionId]);
2222
- const refresh = useCallback3(async () => {
489
+ const refresh = useCallback(async () => {
2223
490
  setRefreshing(true);
2224
491
  await fetchRemotePlugins();
2225
492
  }, [fetchRemotePlugins]);
2226
- useEffect8(() => {
493
+ useEffect2(() => {
2227
494
  setLoading(true);
2228
495
  fetchRemotePlugins();
2229
496
  }, [fetchRemotePlugins]);
@@ -2235,115 +502,13 @@ function useRemotePlugins(solutionId) {
2235
502
  refresh
2236
503
  };
2237
504
  }
2238
-
2239
- // src/api/plugins.ts
2240
- import { apiFetch as apiFetch2 } from "@ateam-ai-mobile/core";
2241
- import { getCoreApiUrl } from "@ateam-ai-mobile/core";
2242
- function supportsNative(manifest) {
2243
- const mode = manifest.render?.mode;
2244
- return mode === "react-native" || mode === "adaptive";
2245
- }
2246
- function getNativeComponentId(manifest) {
2247
- if (manifest.render?.mode === "react-native") return manifest.render.component;
2248
- if (manifest.render?.mode === "adaptive") return manifest.render.reactNative?.component;
2249
- return null;
2250
- }
2251
- function getNativeBundleUrl(manifest) {
2252
- const direct = manifest.bundleUrl;
2253
- if (direct) return direct;
2254
- const render = manifest.render;
2255
- if (render.mode === "react-native") return render.bundleUrl || null;
2256
- if (render.mode === "adaptive") return render.reactNative?.bundleUrl || null;
2257
- return null;
2258
- }
2259
- function getNativeBundleHash(manifest) {
2260
- const direct = manifest.bundleHash;
2261
- if (direct) return direct;
2262
- const render = manifest.render;
2263
- if (render.mode === "react-native") return render.bundleHash || null;
2264
- if (render.mode === "adaptive") return render.reactNative?.bundleHash || null;
2265
- return null;
2266
- }
2267
- function supportsWebView(manifest) {
2268
- return manifest.render?.mode === "iframe" || manifest.render?.mode === "adaptive";
2269
- }
2270
- async function listPlugins() {
2271
- const res = await apiFetch2("/mcp", {
2272
- method: "POST",
2273
- body: JSON.stringify({
2274
- jsonrpc: "2.0",
2275
- id: Date.now(),
2276
- method: "tools/call",
2277
- params: {
2278
- name: "cp.listContextPlugins",
2279
- arguments: {}
2280
- }
2281
- })
2282
- });
2283
- const structured = res.result?.structuredContent?.plugins;
2284
- if (structured) return structured;
2285
- const text = res.result?.content?.[0]?.text;
2286
- if (!text) return [];
2287
- try {
2288
- const parsed = JSON.parse(text);
2289
- return parsed.plugins || parsed || [];
2290
- } catch {
2291
- return [];
2292
- }
2293
- }
2294
- async function getPlugin(pluginId) {
2295
- const res = await apiFetch2("/mcp", {
2296
- method: "POST",
2297
- body: JSON.stringify({
2298
- jsonrpc: "2.0",
2299
- id: Date.now(),
2300
- method: "tools/call",
2301
- params: {
2302
- name: "cp.getContextPlugin",
2303
- arguments: { id: pluginId }
2304
- // Backend expects { id }, NOT { pluginId }
2305
- }
2306
- })
2307
- });
2308
- if (res.result?.structuredContent) return res.result.structuredContent;
2309
- const text = res.result?.content?.[0]?.text;
2310
- if (!text) return null;
2311
- try {
2312
- const parsed = JSON.parse(text);
2313
- if (parsed.error) {
2314
- console.warn("[getPlugin] Backend error:", parsed.error);
2315
- return null;
2316
- }
2317
- return parsed;
2318
- } catch {
2319
- return null;
2320
- }
2321
- }
2322
- function resolvePluginUrl(manifest, tenant) {
2323
- const render = manifest.render;
2324
- let url;
2325
- if (render?.mode === "iframe") {
2326
- url = render.iframeUrl;
2327
- } else if (render?.mode === "adaptive") {
2328
- url = render.iframeUrl || render.iframe?.iframeUrl;
2329
- }
2330
- if (!url) return "";
2331
- if (url.startsWith("http")) {
2332
- const sep = url.includes("?") ? "&" : "?";
2333
- return tenant ? `${url}${sep}tenant=${encodeURIComponent(tenant)}` : url;
2334
- }
2335
- if (tenant && url.startsWith("/mcp-ui/")) {
2336
- const tenantUrl = url.replace("/mcp-ui/", `/mcp-ui/${encodeURIComponent(tenant)}/`);
2337
- return `${getCoreApiUrl()}${tenantUrl}`;
2338
- }
2339
- return `${getCoreApiUrl()}${url}`;
2340
- }
2341
505
  export {
2342
506
  ActionButton,
2343
507
  ActivityIndicator,
2344
508
  AdaOrb,
2345
509
  Animated,
2346
510
  Badge,
511
+ CameraPreview,
2347
512
  Card,
2348
513
  EmptyState,
2349
514
  ErrorState,
@@ -2353,6 +518,7 @@ export {
2353
518
  LoadingState,
2354
519
  MemoryBundleStorage,
2355
520
  Modal,
521
+ PLATFORM_VOICE_CALLER,
2356
522
  Platform,
2357
523
  PluginSDK,
2358
524
  Pressable,
@@ -2371,30 +537,60 @@ export {
2371
537
  TextInput,
2372
538
  TouchableOpacity,
2373
539
  View,
540
+ __resetSessionSyncForTest,
2374
541
  bundleManager,
542
+ callDeviceTool,
543
+ clearDeviceTools,
2375
544
  createNativeBridge,
545
+ device_state_exports as deviceStateV2,
546
+ deviceToolsActionForSession,
547
+ ensureDeviceToolsLoaded,
548
+ explainCapability,
549
+ gateDeviceState,
2376
550
  gatePluginNative,
551
+ gateVoice,
552
+ getCameraPreviewProvider,
2377
553
  getHaptics,
2378
554
  getNativeBundleHash,
2379
555
  getNativeBundleUrl,
2380
556
  getNativeComponentId,
2381
557
  getPlugin,
2382
558
  getPluginNative,
559
+ handleServerDeviceToolCall,
560
+ hasDeviceTool,
561
+ implementedStateDomains,
562
+ implementedVoice,
563
+ isActionable,
564
+ isDeviceToolCommand,
2383
565
  isInlineSurface,
2384
566
  isMountSurface,
567
+ listDeviceTools,
2385
568
  listPlugins,
2386
569
  nativePluginRegistry,
570
+ neverObserved,
2387
571
  register,
2388
572
  registerBundleModules,
573
+ registerDeviceTools,
2389
574
  registerNativePlugin,
575
+ registerPlatformDeviceTools,
2390
576
  registerService,
577
+ registerTools,
578
+ resetDeviceToolLoader,
579
+ resolvePluginRuntimeProps,
2391
580
  resolvePluginUrl,
2392
581
  setBundleStorage,
582
+ setCameraPreviewProvider,
583
+ setDeviceStateProvider,
584
+ setDeviceToolContextProvider,
585
+ setDeviceToolResultPoster,
2393
586
  setHapticsProvider,
2394
587
  setPluginNativeProvider,
2395
588
  setPluginThemeColors,
589
+ setVoiceProvider,
2396
590
  supportsNative,
2397
591
  supportsWebView,
592
+ syncDeviceToolsForSession,
593
+ unregisterDeviceTools,
2398
594
  unregisterNativePlugin,
2399
595
  useApi,
2400
596
  useNativeBridge,