@ateam-ai-mobile/plugin-runtime 0.1.21 → 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.
@@ -659,8 +659,24 @@ function getCameraPreviewProvider() {
659
659
  return _cameraPreview;
660
660
  }
661
661
  var _picturesToAiConsent = null;
662
+ var _consentListeners = /* @__PURE__ */ new Set();
662
663
  function setPicturesToAiConsentProvider(provider) {
663
664
  _picturesToAiConsent = provider;
665
+ notifyPicturesToAiConsentChanged();
666
+ }
667
+ function notifyPicturesToAiConsentChanged() {
668
+ for (const cb of [..._consentListeners]) {
669
+ try {
670
+ cb();
671
+ } catch {
672
+ }
673
+ }
674
+ }
675
+ function onPicturesToAiConsentChange(cb) {
676
+ _consentListeners.add(cb);
677
+ return { unsubscribe: () => {
678
+ _consentListeners.delete(cb);
679
+ } };
664
680
  }
665
681
  function mayPicturesLeavePhone(declared) {
666
682
  if (declared?.picturesToAi !== true) return false;
@@ -3074,6 +3090,30 @@ function gateVoice(declared, pluginId) {
3074
3090
 
3075
3091
  // src/aiLiveHost.ts
3076
3092
  var _provider3 = null;
3093
+ var _screens = /* @__PURE__ */ new Map();
3094
+ function screenOf(holder) {
3095
+ let sc = _screens.get(holder);
3096
+ if (!sc) {
3097
+ sc = { closed: false, stops: 0, queue: Promise.resolve(), watch: null };
3098
+ _screens.set(holder, sc);
3099
+ }
3100
+ return sc;
3101
+ }
3102
+ function inTurn(sc, step) {
3103
+ const run = sc.queue.then(step, step);
3104
+ sc.queue = run.then(() => {
3105
+ }, () => {
3106
+ });
3107
+ return run;
3108
+ }
3109
+ var unwatch = (sc) => {
3110
+ sc.watch?.unsubscribe();
3111
+ sc.watch = null;
3112
+ };
3113
+ var STOPPED_BEFORE_START = "aiLive: stopped before it started: the screen stopped or closed while its start was waiting.";
3114
+ var SCREEN_CLOSED = "aiLive: this screen is closed; it cannot start sending.";
3115
+ var stopped = (msg) => Object.assign(new Error(msg), { code: "STOPPED" });
3116
+ var CONSENT_TAKEN_BACK = "Camera pictures to AI was turned off on this phone, so sending stopped.";
3077
3117
  function setAiLiveProvider(provider) {
3078
3118
  _provider3 = provider || null;
3079
3119
  }
@@ -3104,45 +3144,100 @@ function deniedAiLive(why) {
3104
3144
  var aiLiveCameraHolder = (holder) => `${holder}#aiLive`;
3105
3145
  function gateAiLive(capabilities, pluginId, holder, camera) {
3106
3146
  if (capabilities?.picturesToAi !== true) {
3107
- return deniedAiLive("not declared in this plugin's capabilities. Add `capabilities: { picturesToAi: true }` to send camera pictures to the workspace's chosen AI.");
3147
+ return deniedAiLive("not declared in this plugin's manifest. Add `native: { picturesToAi: true }` to send camera pictures to the workspace's chosen AI.");
3108
3148
  }
3109
3149
  if (!pluginId || !holder) return deniedAiLive("no plugin identity \u2014 it fails closed without a screen to bind the session to.");
3110
3150
  if (!_provider3) return deniedAiLive("no live AI in this build \u2014 the app has not called setAiLiveProvider.");
3111
3151
  const provider = _provider3;
3152
+ const sc = screenOf(holder);
3153
+ const allowed = () => mayPicturesLeavePhone(capabilities);
3154
+ const releaseCamera = () => releaseDeviceStateHolds(aiLiveCameraHolder(holder));
3155
+ const tidy = async (why) => {
3156
+ unwatch(sc);
3157
+ try {
3158
+ await provider.stop(holder, why);
3159
+ } finally {
3160
+ await releaseCamera();
3161
+ }
3162
+ };
3163
+ const stopNowAndInTurn = (why) => {
3164
+ sc.stops++;
3165
+ const now = provider.stop(holder, why).catch(() => {
3166
+ });
3167
+ return inTurn(sc, async () => {
3168
+ await now;
3169
+ await tidy(why);
3170
+ });
3171
+ };
3172
+ const onConsentChange = () => {
3173
+ if (!allowed()) void stopNowAndInTurn(CONSENT_TAKEN_BACK);
3174
+ };
3112
3175
  return {
3113
- async start(opts) {
3114
- if (!mayPicturesLeavePhone(capabilities)) {
3115
- throw denial2(`the person on this phone has not allowed camera pictures to go to AI. They can turn on "Pictures to AI" in the app's settings (What Ada can access).`);
3116
- }
3117
- try {
3118
- return await provider.start(opts || {}, holder, { visual: camera() });
3119
- } catch (e) {
3120
- await releaseDeviceStateHolds(aiLiveCameraHolder(holder));
3121
- throw e;
3122
- }
3123
- },
3124
- async stop() {
3125
- try {
3126
- await provider.stop(holder);
3127
- } finally {
3128
- await releaseDeviceStateHolds(aiLiveCameraHolder(holder));
3129
- }
3176
+ start(opts) {
3177
+ if (sc.closed) return Promise.reject(stopped(SCREEN_CLOSED));
3178
+ const stopsWhenAsked = sc.stops;
3179
+ return inTurn(sc, async () => {
3180
+ if (sc.closed) throw stopped(SCREEN_CLOSED);
3181
+ if (sc.stops !== stopsWhenAsked) throw stopped(STOPPED_BEFORE_START);
3182
+ if (!allowed()) {
3183
+ 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.');
3184
+ }
3185
+ unwatch(sc);
3186
+ sc.watch = onPicturesToAiConsentChange(onConsentChange);
3187
+ let snap;
3188
+ try {
3189
+ snap = await provider.start(opts || {}, holder, { visual: camera(), allowed });
3190
+ } catch (e) {
3191
+ unwatch(sc);
3192
+ await releaseCamera();
3193
+ if (!allowed()) throw denial2(CONSENT_TAKEN_BACK);
3194
+ throw e;
3195
+ }
3196
+ if (!allowed()) {
3197
+ await tidy(CONSENT_TAKEN_BACK);
3198
+ throw denial2(CONSENT_TAKEN_BACK);
3199
+ }
3200
+ return snap;
3201
+ });
3130
3202
  },
3203
+ stop: () => stopNowAndInTurn(),
3131
3204
  getSnapshot: () => provider.getSnapshot(holder),
3132
3205
  onEpisode: (cb) => provider.onEpisode(cb, holder),
3133
3206
  subscribe: (cb) => provider.subscribe(cb, holder)
3134
3207
  };
3135
3208
  }
3136
3209
  async function releaseAiLive(holder) {
3137
- try {
3138
- await _provider3?.stop(holder);
3139
- } catch {
3140
- }
3141
- try {
3142
- _provider3?.forget(holder);
3143
- } catch {
3144
- }
3145
- await releaseDeviceStateHolds(aiLiveCameraHolder(holder));
3210
+ const sc = _screens.get(holder);
3211
+ if (!sc) {
3212
+ try {
3213
+ await _provider3?.stop(holder);
3214
+ } catch {
3215
+ }
3216
+ try {
3217
+ _provider3?.forget(holder);
3218
+ } catch {
3219
+ }
3220
+ await releaseDeviceStateHolds(aiLiveCameraHolder(holder));
3221
+ return;
3222
+ }
3223
+ sc.closed = true;
3224
+ sc.stops++;
3225
+ const now = _provider3 ? _provider3.stop(holder).catch(() => {
3226
+ }) : Promise.resolve();
3227
+ await inTurn(sc, async () => {
3228
+ await now;
3229
+ unwatch(sc);
3230
+ try {
3231
+ await _provider3?.stop(holder);
3232
+ } catch {
3233
+ }
3234
+ try {
3235
+ _provider3?.forget(holder);
3236
+ } catch {
3237
+ }
3238
+ await releaseDeviceStateHolds(aiLiveCameraHolder(holder));
3239
+ });
3240
+ if (_screens.get(holder) === sc) _screens.delete(holder);
3146
3241
  }
3147
3242
  var NOTHING = { unsubscribe() {
3148
3243
  } };
@@ -3309,6 +3404,8 @@ export {
3309
3404
  setCameraPreviewProvider,
3310
3405
  getCameraPreviewProvider,
3311
3406
  setPicturesToAiConsentProvider,
3407
+ notifyPicturesToAiConsentChanged,
3408
+ onPicturesToAiConsentChange,
3312
3409
  mayPicturesLeavePhone,
3313
3410
  registerNativePlugin,
3314
3411
  unregisterNativePlugin,
@@ -3328,6 +3425,7 @@ export {
3328
3425
  setVoiceProvider,
3329
3426
  implementedVoice,
3330
3427
  gateVoice,
3428
+ CONSENT_TAKEN_BACK,
3331
3429
  setAiLiveProvider,
3332
3430
  gateAiLive,
3333
3431
  releaseAiLive,
@@ -3366,4 +3464,4 @@ export {
3366
3464
  ensureDeviceToolsLoaded,
3367
3465
  resetDeviceToolLoader
3368
3466
  };
3369
- //# sourceMappingURL=chunk-32IN24XO.mjs.map
3467
+ //# sourceMappingURL=chunk-RNA6X65P.mjs.map