@ateam-ai-mobile/plugin-runtime 0.1.20 → 0.1.22

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.js CHANGED
@@ -75,6 +75,22 @@ var init_voice = __esm({
75
75
  }
76
76
  });
77
77
 
78
+ // src/sdk/aiLive.ts
79
+ var AI_LIVE_IDLE;
80
+ var init_aiLive = __esm({
81
+ "src/sdk/aiLive.ts"() {
82
+ "use strict";
83
+ AI_LIVE_IDLE = Object.freeze({
84
+ state: "idle",
85
+ camera: null,
86
+ sent: 0,
87
+ skipped: 0,
88
+ lastEpisode: null,
89
+ lastError: null
90
+ });
91
+ }
92
+ });
93
+
78
94
  // src/sdk/device-state.ts
79
95
  var device_state_exports = {};
80
96
  __export(device_state_exports, {
@@ -477,6 +493,33 @@ function setCameraPreviewProvider(Component) {
477
493
  function getCameraPreviewProvider() {
478
494
  return _cameraPreview;
479
495
  }
496
+ function setPicturesToAiConsentProvider(provider) {
497
+ _picturesToAiConsent = provider;
498
+ notifyPicturesToAiConsentChanged();
499
+ }
500
+ function notifyPicturesToAiConsentChanged() {
501
+ for (const cb of [..._consentListeners]) {
502
+ try {
503
+ cb();
504
+ } catch {
505
+ }
506
+ }
507
+ }
508
+ function onPicturesToAiConsentChange(cb) {
509
+ _consentListeners.add(cb);
510
+ return { unsubscribe: () => {
511
+ _consentListeners.delete(cb);
512
+ } };
513
+ }
514
+ function mayPicturesLeavePhone(declared) {
515
+ if (declared?.picturesToAi !== true) return false;
516
+ if (!_picturesToAiConsent) return false;
517
+ try {
518
+ return _picturesToAiConsent() === true;
519
+ } catch {
520
+ return false;
521
+ }
522
+ }
480
523
  function registerNativePlugin(id, plugin) {
481
524
  _nativeRegistry.set(id, plugin);
482
525
  }
@@ -498,7 +541,7 @@ function registerBundleModules(modules) {
498
541
  function resolveBundleModule(name) {
499
542
  return _bundleModules.get(name);
500
543
  }
501
- var _haptics, noopNative, _pluginNative, _cameraPreview, _nativeRegistry, nativePluginRegistry, _bundleModules;
544
+ var _haptics, noopNative, _pluginNative, _cameraPreview, _picturesToAiConsent, _consentListeners, _nativeRegistry, nativePluginRegistry, _bundleModules;
502
545
  var init_host = __esm({
503
546
  "src/host.ts"() {
504
547
  "use strict";
@@ -528,6 +571,8 @@ var init_host = __esm({
528
571
  };
529
572
  _pluginNative = noopNative;
530
573
  _cameraPreview = null;
574
+ _picturesToAiConsent = null;
575
+ _consentListeners = /* @__PURE__ */ new Set();
531
576
  _nativeRegistry = /* @__PURE__ */ new Map();
532
577
  nativePluginRegistry = {
533
578
  has(id) {
@@ -705,6 +750,7 @@ var init_useApi = __esm({
705
750
  // src/sdk/index.ts
706
751
  var sdk_exports = {};
707
752
  __export(sdk_exports, {
753
+ AI_LIVE_IDLE: () => AI_LIVE_IDLE,
708
754
  ActionButton: () => ActionButton,
709
755
  ActivityIndicator: () => import_react_native.ActivityIndicator,
710
756
  Animated: () => import_react_native.Animated,
@@ -749,6 +795,7 @@ var init_sdk = __esm({
749
795
  init_registration();
750
796
  init_registration();
751
797
  init_voice();
798
+ init_aiLive();
752
799
  init_device_state();
753
800
  init_primitives();
754
801
  init_components();
@@ -1064,6 +1111,172 @@ var init_voiceHost = __esm({
1064
1111
  }
1065
1112
  });
1066
1113
 
1114
+ // src/aiLiveHost.ts
1115
+ function screenOf(holder) {
1116
+ let sc = _screens.get(holder);
1117
+ if (!sc) {
1118
+ sc = { closed: false, stops: 0, queue: Promise.resolve(), watch: null };
1119
+ _screens.set(holder, sc);
1120
+ }
1121
+ return sc;
1122
+ }
1123
+ function inTurn(sc, step) {
1124
+ const run = sc.queue.then(step, step);
1125
+ sc.queue = run.then(() => {
1126
+ }, () => {
1127
+ });
1128
+ return run;
1129
+ }
1130
+ function setAiLiveProvider(provider) {
1131
+ _provider3 = provider || null;
1132
+ }
1133
+ function denial2(why) {
1134
+ return Object.assign(new Error(`aiLive: ${why}`), { code: "RESTRICTED" });
1135
+ }
1136
+ function deniedAiLive(why) {
1137
+ const fail = () => {
1138
+ throw denial2(why);
1139
+ };
1140
+ return {
1141
+ async start() {
1142
+ return fail();
1143
+ },
1144
+ async stop() {
1145
+ },
1146
+ getSnapshot() {
1147
+ return { state: "error", camera: null, sent: 0, skipped: 0, lastEpisode: null, lastError: why };
1148
+ },
1149
+ onEpisode() {
1150
+ return fail();
1151
+ },
1152
+ subscribe() {
1153
+ return fail();
1154
+ }
1155
+ };
1156
+ }
1157
+ function gateAiLive(capabilities, pluginId, holder, camera) {
1158
+ if (capabilities?.picturesToAi !== true) {
1159
+ return deniedAiLive("not declared in this plugin's manifest. Add `native: { picturesToAi: true }` to send camera pictures to the workspace's chosen AI.");
1160
+ }
1161
+ if (!pluginId || !holder) return deniedAiLive("no plugin identity \u2014 it fails closed without a screen to bind the session to.");
1162
+ if (!_provider3) return deniedAiLive("no live AI in this build \u2014 the app has not called setAiLiveProvider.");
1163
+ const provider = _provider3;
1164
+ const sc = screenOf(holder);
1165
+ const allowed = () => mayPicturesLeavePhone(capabilities);
1166
+ const releaseCamera = () => releaseDeviceStateHolds(aiLiveCameraHolder(holder));
1167
+ const tidy = async (why) => {
1168
+ unwatch(sc);
1169
+ try {
1170
+ await provider.stop(holder, why);
1171
+ } finally {
1172
+ await releaseCamera();
1173
+ }
1174
+ };
1175
+ const stopNowAndInTurn = (why) => {
1176
+ sc.stops++;
1177
+ const now = provider.stop(holder, why).catch(() => {
1178
+ });
1179
+ return inTurn(sc, async () => {
1180
+ await now;
1181
+ await tidy(why);
1182
+ });
1183
+ };
1184
+ const onConsentChange = () => {
1185
+ if (!allowed()) void stopNowAndInTurn(CONSENT_TAKEN_BACK);
1186
+ };
1187
+ return {
1188
+ start(opts) {
1189
+ if (sc.closed) return Promise.reject(stopped(SCREEN_CLOSED));
1190
+ const stopsWhenAsked = sc.stops;
1191
+ return inTurn(sc, async () => {
1192
+ if (sc.closed) throw stopped(SCREEN_CLOSED);
1193
+ if (sc.stops !== stopsWhenAsked) throw stopped(STOPPED_BEFORE_START);
1194
+ if (!allowed()) {
1195
+ throw denial2('the person on this phone has not allowed camera pictures to go to AI. They can turn on Settings > Camera > "Camera pictures to AI" in the app.');
1196
+ }
1197
+ unwatch(sc);
1198
+ sc.watch = onPicturesToAiConsentChange(onConsentChange);
1199
+ let snap;
1200
+ try {
1201
+ snap = await provider.start(opts || {}, holder, { visual: camera(), allowed });
1202
+ } catch (e) {
1203
+ unwatch(sc);
1204
+ await releaseCamera();
1205
+ if (!allowed()) throw denial2(CONSENT_TAKEN_BACK);
1206
+ throw e;
1207
+ }
1208
+ if (!allowed()) {
1209
+ await tidy(CONSENT_TAKEN_BACK);
1210
+ throw denial2(CONSENT_TAKEN_BACK);
1211
+ }
1212
+ return snap;
1213
+ });
1214
+ },
1215
+ stop: () => stopNowAndInTurn(),
1216
+ getSnapshot: () => provider.getSnapshot(holder),
1217
+ onEpisode: (cb) => provider.onEpisode(cb, holder),
1218
+ subscribe: (cb) => provider.subscribe(cb, holder)
1219
+ };
1220
+ }
1221
+ async function releaseAiLive(holder) {
1222
+ const sc = _screens.get(holder);
1223
+ if (!sc) {
1224
+ try {
1225
+ await _provider3?.stop(holder);
1226
+ } catch {
1227
+ }
1228
+ try {
1229
+ _provider3?.forget(holder);
1230
+ } catch {
1231
+ }
1232
+ await releaseDeviceStateHolds(aiLiveCameraHolder(holder));
1233
+ return;
1234
+ }
1235
+ sc.closed = true;
1236
+ sc.stops++;
1237
+ const now = _provider3 ? _provider3.stop(holder).catch(() => {
1238
+ }) : Promise.resolve();
1239
+ await inTurn(sc, async () => {
1240
+ await now;
1241
+ unwatch(sc);
1242
+ try {
1243
+ await _provider3?.stop(holder);
1244
+ } catch {
1245
+ }
1246
+ try {
1247
+ _provider3?.forget(holder);
1248
+ } catch {
1249
+ }
1250
+ await releaseDeviceStateHolds(aiLiveCameraHolder(holder));
1251
+ });
1252
+ if (_screens.get(holder) === sc) _screens.delete(holder);
1253
+ }
1254
+ var _provider3, _screens, unwatch, STOPPED_BEFORE_START, SCREEN_CLOSED, stopped, CONSENT_TAKEN_BACK, aiLiveCameraHolder, NOTHING, aiLiveIndicator;
1255
+ var init_aiLiveHost = __esm({
1256
+ "src/aiLiveHost.ts"() {
1257
+ "use strict";
1258
+ init_host();
1259
+ init_deviceStateHost();
1260
+ _provider3 = null;
1261
+ _screens = /* @__PURE__ */ new Map();
1262
+ unwatch = (sc) => {
1263
+ sc.watch?.unsubscribe();
1264
+ sc.watch = null;
1265
+ };
1266
+ STOPPED_BEFORE_START = "aiLive: stopped before it started: the screen stopped or closed while its start was waiting.";
1267
+ SCREEN_CLOSED = "aiLive: this screen is closed; it cannot start sending.";
1268
+ stopped = (msg) => Object.assign(new Error(msg), { code: "STOPPED" });
1269
+ CONSENT_TAKEN_BACK = "Camera pictures to AI was turned off on this phone, so sending stopped.";
1270
+ aiLiveCameraHolder = (holder) => `${holder}#aiLive`;
1271
+ NOTHING = { unsubscribe() {
1272
+ } };
1273
+ aiLiveIndicator = {
1274
+ isSending: () => !!_provider3?.isSending(),
1275
+ subscribe: (cb) => _provider3?.subscribeSending(cb) ?? NOTHING
1276
+ };
1277
+ }
1278
+ });
1279
+
1067
1280
  // src/pluginRuntime.ts
1068
1281
  function mergeCaps(decl) {
1069
1282
  if (!decl) return void 0;
@@ -1079,7 +1292,8 @@ function resolvePluginRuntimeProps(src, holder = src.pluginId) {
1079
1292
  const native = gatePluginNative(getPluginNative(), capabilities, src.pluginId);
1080
1293
  const deviceState = gateDeviceState(stateDomains, holder);
1081
1294
  const voice = gateVoice(!!capabilities?.voice, src.pluginId);
1082
- return { device: native, native, deviceState, voice };
1295
+ const aiLive = gateAiLive(capabilities, src.pluginId, holder, () => gateDeviceState(["visual"], aiLiveCameraHolder(holder)).visual);
1296
+ return { device: native, native, deviceState, voice, aiLive };
1083
1297
  }
1084
1298
  var init_pluginRuntime = __esm({
1085
1299
  "src/pluginRuntime.ts"() {
@@ -1087,6 +1301,7 @@ var init_pluginRuntime = __esm({
1087
1301
  init_host();
1088
1302
  init_deviceStateHost();
1089
1303
  init_voiceHost();
1304
+ init_aiLiveHost();
1090
1305
  }
1091
1306
  });
1092
1307
 
@@ -3326,11 +3541,13 @@ var init_loader = __esm({
3326
3541
  // src/index.ts
3327
3542
  var index_exports = {};
3328
3543
  __export(index_exports, {
3544
+ AI_LIVE_IDLE: () => AI_LIVE_IDLE,
3329
3545
  ActionButton: () => ActionButton,
3330
3546
  ActivityIndicator: () => import_react_native.ActivityIndicator,
3331
3547
  AdaOrb: () => AdaOrb,
3332
3548
  Animated: () => import_react_native.Animated,
3333
3549
  Badge: () => Badge,
3550
+ CONSENT_TAKEN_BACK: () => CONSENT_TAKEN_BACK,
3334
3551
  CameraPreview: () => CameraPreview,
3335
3552
  Card: () => Card,
3336
3553
  EmptyState: () => EmptyState,
@@ -3361,6 +3578,7 @@ __export(index_exports, {
3361
3578
  TouchableOpacity: () => import_react_native.TouchableOpacity,
3362
3579
  View: () => import_react_native.View,
3363
3580
  __resetSessionSyncForTest: () => __resetSessionSyncForTest,
3581
+ aiLiveIndicator: () => aiLiveIndicator,
3364
3582
  bundleManager: () => bundleManager,
3365
3583
  callDeviceTool: () => callDeviceTool,
3366
3584
  clearDeviceTools: () => clearDeviceTools,
@@ -3370,6 +3588,7 @@ __export(index_exports, {
3370
3588
  deviceToolsActionForSession: () => deviceToolsActionForSession,
3371
3589
  ensureDeviceToolsLoaded: () => ensureDeviceToolsLoaded,
3372
3590
  explainCapability: () => explainCapability,
3591
+ gateAiLive: () => gateAiLive,
3373
3592
  gateDeviceState: () => gateDeviceState,
3374
3593
  gatePluginNative: () => gatePluginNative,
3375
3594
  gateVoice: () => gateVoice,
@@ -3390,8 +3609,11 @@ __export(index_exports, {
3390
3609
  isMountSurface: () => isMountSurface,
3391
3610
  listDeviceTools: () => listDeviceTools,
3392
3611
  listPlugins: () => listPlugins,
3612
+ mayPicturesLeavePhone: () => mayPicturesLeavePhone,
3393
3613
  nativePluginRegistry: () => nativePluginRegistry,
3394
3614
  neverObserved: () => neverObserved,
3615
+ notifyPicturesToAiConsentChanged: () => notifyPicturesToAiConsentChanged,
3616
+ onPicturesToAiConsentChange: () => onPicturesToAiConsentChange,
3395
3617
  pluginBundleUrl: () => pluginBundleUrl,
3396
3618
  register: () => register,
3397
3619
  registerBundleModules: () => registerBundleModules,
@@ -3400,18 +3622,21 @@ __export(index_exports, {
3400
3622
  registerPlatformDeviceTools: () => registerPlatformDeviceTools,
3401
3623
  registerService: () => registerService,
3402
3624
  registerTools: () => registerTools,
3625
+ releaseAiLive: () => releaseAiLive,
3403
3626
  releaseDeviceStateHolds: () => releaseDeviceStateHolds,
3404
3627
  rendersNative: () => rendersNative,
3405
3628
  resetDeviceToolLoader: () => resetDeviceToolLoader,
3406
3629
  resolvePluginGrants: () => resolvePluginGrants,
3407
3630
  resolvePluginRuntimeProps: () => resolvePluginRuntimeProps,
3408
3631
  resolvePluginUrl: () => resolvePluginUrl,
3632
+ setAiLiveProvider: () => setAiLiveProvider,
3409
3633
  setBundleStorage: () => setBundleStorage,
3410
3634
  setCameraPreviewProvider: () => setCameraPreviewProvider,
3411
3635
  setDeviceStateProvider: () => setDeviceStateProvider,
3412
3636
  setDeviceToolContextProvider: () => setDeviceToolContextProvider,
3413
3637
  setDeviceToolResultPoster: () => setDeviceToolResultPoster,
3414
3638
  setHapticsProvider: () => setHapticsProvider,
3639
+ setPicturesToAiConsentProvider: () => setPicturesToAiConsentProvider,
3415
3640
  setPluginNativeProvider: () => setPluginNativeProvider,
3416
3641
  setPluginThemeColors: () => setPluginThemeColors,
3417
3642
  setVoiceProvider: () => setVoiceProvider,
@@ -3434,6 +3659,7 @@ init_device_state();
3434
3659
  init_device_state();
3435
3660
  init_deviceStateHost();
3436
3661
  init_voiceHost();
3662
+ init_aiLiveHost();
3437
3663
  init_host();
3438
3664
  init_pluginRuntime();
3439
3665
  init_registry();
@@ -3661,6 +3887,7 @@ function usePlugin(pluginId, manifest) {
3661
3887
  var import_react12 = require("react");
3662
3888
  init_pluginRuntime();
3663
3889
  init_deviceStateHost();
3890
+ init_aiLiveHost();
3664
3891
  var mounts = 0;
3665
3892
  function usePluginRuntimeProps(src, deps) {
3666
3893
  const holderRef = (0, import_react12.useRef)(null);
@@ -3673,6 +3900,9 @@ function usePluginRuntimeProps(src, deps) {
3673
3900
  (0, import_react12.useEffect)(() => () => {
3674
3901
  void releaseDeviceStateHolds(holder);
3675
3902
  }, [holder]);
3903
+ (0, import_react12.useEffect)(() => () => {
3904
+ void releaseAiLive(holder);
3905
+ }, [holder]);
3676
3906
  const grantedKey = granted.join(",");
3677
3907
  (0, import_react12.useEffect)(() => {
3678
3908
  void releaseDeviceStateHolds(holder, granted);
@@ -3761,11 +3991,13 @@ init_store();
3761
3991
  init_SurfaceDispatcher();
3762
3992
  // Annotate the CommonJS export names for ESM import in node:
3763
3993
  0 && (module.exports = {
3994
+ AI_LIVE_IDLE,
3764
3995
  ActionButton,
3765
3996
  ActivityIndicator,
3766
3997
  AdaOrb,
3767
3998
  Animated,
3768
3999
  Badge,
4000
+ CONSENT_TAKEN_BACK,
3769
4001
  CameraPreview,
3770
4002
  Card,
3771
4003
  EmptyState,
@@ -3796,6 +4028,7 @@ init_SurfaceDispatcher();
3796
4028
  TouchableOpacity,
3797
4029
  View,
3798
4030
  __resetSessionSyncForTest,
4031
+ aiLiveIndicator,
3799
4032
  bundleManager,
3800
4033
  callDeviceTool,
3801
4034
  clearDeviceTools,
@@ -3805,6 +4038,7 @@ init_SurfaceDispatcher();
3805
4038
  deviceToolsActionForSession,
3806
4039
  ensureDeviceToolsLoaded,
3807
4040
  explainCapability,
4041
+ gateAiLive,
3808
4042
  gateDeviceState,
3809
4043
  gatePluginNative,
3810
4044
  gateVoice,
@@ -3825,8 +4059,11 @@ init_SurfaceDispatcher();
3825
4059
  isMountSurface,
3826
4060
  listDeviceTools,
3827
4061
  listPlugins,
4062
+ mayPicturesLeavePhone,
3828
4063
  nativePluginRegistry,
3829
4064
  neverObserved,
4065
+ notifyPicturesToAiConsentChanged,
4066
+ onPicturesToAiConsentChange,
3830
4067
  pluginBundleUrl,
3831
4068
  register,
3832
4069
  registerBundleModules,
@@ -3835,18 +4072,21 @@ init_SurfaceDispatcher();
3835
4072
  registerPlatformDeviceTools,
3836
4073
  registerService,
3837
4074
  registerTools,
4075
+ releaseAiLive,
3838
4076
  releaseDeviceStateHolds,
3839
4077
  rendersNative,
3840
4078
  resetDeviceToolLoader,
3841
4079
  resolvePluginGrants,
3842
4080
  resolvePluginRuntimeProps,
3843
4081
  resolvePluginUrl,
4082
+ setAiLiveProvider,
3844
4083
  setBundleStorage,
3845
4084
  setCameraPreviewProvider,
3846
4085
  setDeviceStateProvider,
3847
4086
  setDeviceToolContextProvider,
3848
4087
  setDeviceToolResultPoster,
3849
4088
  setHapticsProvider,
4089
+ setPicturesToAiConsentProvider,
3850
4090
  setPluginNativeProvider,
3851
4091
  setPluginThemeColors,
3852
4092
  setVoiceProvider,