@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.
@@ -141,6 +141,7 @@ import { apiFetchRaw, getTenant as getTenant2 } from "@ateam-ai-mobile/core";
141
141
  // src/sdk/index.ts
142
142
  var sdk_exports = {};
143
143
  __export(sdk_exports, {
144
+ AI_LIVE_IDLE: () => AI_LIVE_IDLE,
144
145
  ActionButton: () => ActionButton,
145
146
  ActivityIndicator: () => ActivityIndicator,
146
147
  Animated: () => Animated,
@@ -213,6 +214,16 @@ function registerService(id, config) {
213
214
  // src/sdk/voice.ts
214
215
  var PLATFORM_VOICE_CALLER = /* @__PURE__ */ Symbol("adas.voice.platform");
215
216
 
217
+ // src/sdk/aiLive.ts
218
+ var AI_LIVE_IDLE = Object.freeze({
219
+ state: "idle",
220
+ camera: null,
221
+ sent: 0,
222
+ skipped: 0,
223
+ lastEpisode: null,
224
+ lastError: null
225
+ });
226
+
216
227
  // src/sdk/device-state.ts
217
228
  var device_state_exports = {};
218
229
  __export(device_state_exports, {
@@ -647,6 +658,35 @@ function setCameraPreviewProvider(Component) {
647
658
  function getCameraPreviewProvider() {
648
659
  return _cameraPreview;
649
660
  }
661
+ var _picturesToAiConsent = null;
662
+ var _consentListeners = /* @__PURE__ */ new Set();
663
+ function setPicturesToAiConsentProvider(provider) {
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
+ } };
680
+ }
681
+ function mayPicturesLeavePhone(declared) {
682
+ if (declared?.picturesToAi !== true) return false;
683
+ if (!_picturesToAiConsent) return false;
684
+ try {
685
+ return _picturesToAiConsent() === true;
686
+ } catch {
687
+ return false;
688
+ }
689
+ }
650
690
  var _nativeRegistry = /* @__PURE__ */ new Map();
651
691
  function registerNativePlugin(id, plugin) {
652
692
  _nativeRegistry.set(id, plugin);
@@ -3048,6 +3088,164 @@ function gateVoice(declared, pluginId) {
3048
3088
  );
3049
3089
  }
3050
3090
 
3091
+ // src/aiLiveHost.ts
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.";
3117
+ function setAiLiveProvider(provider) {
3118
+ _provider3 = provider || null;
3119
+ }
3120
+ function denial2(why) {
3121
+ return Object.assign(new Error(`aiLive: ${why}`), { code: "RESTRICTED" });
3122
+ }
3123
+ function deniedAiLive(why) {
3124
+ const fail = () => {
3125
+ throw denial2(why);
3126
+ };
3127
+ return {
3128
+ async start() {
3129
+ return fail();
3130
+ },
3131
+ async stop() {
3132
+ },
3133
+ getSnapshot() {
3134
+ return { state: "error", camera: null, sent: 0, skipped: 0, lastEpisode: null, lastError: why };
3135
+ },
3136
+ onEpisode() {
3137
+ return fail();
3138
+ },
3139
+ subscribe() {
3140
+ return fail();
3141
+ }
3142
+ };
3143
+ }
3144
+ var aiLiveCameraHolder = (holder) => `${holder}#aiLive`;
3145
+ function gateAiLive(capabilities, pluginId, holder, camera) {
3146
+ if (capabilities?.picturesToAi !== true) {
3147
+ return deniedAiLive("not declared in this plugin's manifest. Add `native: { picturesToAi: true }` to send camera pictures to the workspace's chosen AI.");
3148
+ }
3149
+ if (!pluginId || !holder) return deniedAiLive("no plugin identity \u2014 it fails closed without a screen to bind the session to.");
3150
+ if (!_provider3) return deniedAiLive("no live AI in this build \u2014 the app has not called setAiLiveProvider.");
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
+ };
3175
+ return {
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
+ });
3202
+ },
3203
+ stop: () => stopNowAndInTurn(),
3204
+ getSnapshot: () => provider.getSnapshot(holder),
3205
+ onEpisode: (cb) => provider.onEpisode(cb, holder),
3206
+ subscribe: (cb) => provider.subscribe(cb, holder)
3207
+ };
3208
+ }
3209
+ async function releaseAiLive(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);
3241
+ }
3242
+ var NOTHING = { unsubscribe() {
3243
+ } };
3244
+ var aiLiveIndicator = {
3245
+ isSending: () => !!_provider3?.isSending(),
3246
+ subscribe: (cb) => _provider3?.subscribeSending(cb) ?? NOTHING
3247
+ };
3248
+
3051
3249
  // src/pluginRuntime.ts
3052
3250
  function mergeCaps(decl) {
3053
3251
  if (!decl) return void 0;
@@ -3063,7 +3261,8 @@ function resolvePluginRuntimeProps(src, holder = src.pluginId) {
3063
3261
  const native = gatePluginNative(getPluginNative(), capabilities, src.pluginId);
3064
3262
  const deviceState = gateDeviceState(stateDomains, holder);
3065
3263
  const voice = gateVoice(!!capabilities?.voice, src.pluginId);
3066
- return { device: native, native, deviceState, voice };
3264
+ const aiLive = gateAiLive(capabilities, src.pluginId, holder, () => gateDeviceState(["visual"], aiLiveCameraHolder(holder)).visual);
3265
+ return { device: native, native, deviceState, voice, aiLive };
3067
3266
  }
3068
3267
 
3069
3268
  // src/tools/loader.ts
@@ -3167,6 +3366,7 @@ export {
3167
3366
  registerTools,
3168
3367
  registerService,
3169
3368
  PLATFORM_VOICE_CALLER,
3369
+ AI_LIVE_IDLE,
3170
3370
  isActionable,
3171
3371
  neverObserved,
3172
3372
  explainCapability,
@@ -3203,6 +3403,10 @@ export {
3203
3403
  gatePluginNative,
3204
3404
  setCameraPreviewProvider,
3205
3405
  getCameraPreviewProvider,
3406
+ setPicturesToAiConsentProvider,
3407
+ notifyPicturesToAiConsentChanged,
3408
+ onPicturesToAiConsentChange,
3409
+ mayPicturesLeavePhone,
3206
3410
  registerNativePlugin,
3207
3411
  unregisterNativePlugin,
3208
3412
  nativePluginRegistry,
@@ -3221,6 +3425,11 @@ export {
3221
3425
  setVoiceProvider,
3222
3426
  implementedVoice,
3223
3427
  gateVoice,
3428
+ CONSENT_TAKEN_BACK,
3429
+ setAiLiveProvider,
3430
+ gateAiLive,
3431
+ releaseAiLive,
3432
+ aiLiveIndicator,
3224
3433
  resolvePluginGrants,
3225
3434
  resolvePluginRuntimeProps,
3226
3435
  useNativeBridge,
@@ -3255,4 +3464,4 @@ export {
3255
3464
  ensureDeviceToolsLoaded,
3256
3465
  resetDeviceToolLoader
3257
3466
  };
3258
- //# sourceMappingURL=chunk-56Z2X5ZB.mjs.map
3467
+ //# sourceMappingURL=chunk-RNA6X65P.mjs.map