@ateam-ai-mobile/plugin-runtime 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-PKDQLNBL.mjs → chunk-SICFDA5K.mjs} +388 -70
- package/dist/chunk-SICFDA5K.mjs.map +1 -0
- package/dist/index.d.mts +462 -46
- package/dist/index.d.ts +462 -46
- package/dist/index.js +429 -230
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +20 -157
- package/dist/index.mjs.map +1 -1
- package/dist/{loader-5LUVXUY2.mjs → loader-MSZ4PVTO.mjs} +2 -2
- package/package.json +2 -2
- package/src/api/plugins.ts +15 -9
- package/src/bundles/bundleManager.ts +187 -64
- package/src/bundles/bundleModuleNames.ts +52 -0
- package/src/bundles/bundleUrl.ts +41 -0
- package/src/bundles/storage.ts +71 -0
- package/src/deviceStateHost.ts +25 -0
- package/src/hooks/usePlugin.ts +15 -4
- package/src/host.ts +2 -0
- package/src/index.ts +16 -37
- package/src/pluginRuntime.ts +22 -11
- package/src/sdk/CameraPreview.tsx +7 -0
- package/src/sdk/device-state.ts +120 -17
- package/src/sdk/index.ts +41 -0
- package/src/sdk/registration.ts +2 -1
- package/src/sdk/types.ts +162 -5
- package/src/sdk/voice.ts +82 -0
- package/src/tools/loader.ts +28 -8
- package/src/tools/registry.ts +14 -7
- package/src/voiceHost.ts +2 -0
- package/dist/chunk-PKDQLNBL.mjs.map +0 -1
- /package/dist/{loader-5LUVXUY2.mjs.map → loader-MSZ4PVTO.mjs.map} +0 -0
package/dist/index.d.mts
CHANGED
|
@@ -6,12 +6,15 @@ import { View, TextInput } from 'react-native';
|
|
|
6
6
|
export { ActivityIndicator, Animated, FlatList, Image, Modal, Platform, Pressable, RefreshControl, ScrollView, SectionList, StyleSheet, Switch, Text, TextInput, TouchableOpacity, View } from 'react-native';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* Device state — Device Bridge v2, the SEMANTIC layer.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* Repo path:
|
|
12
12
|
* packages/plugin-runtime/src/sdk/device-state.ts
|
|
13
|
-
*
|
|
14
|
-
* '@adas/
|
|
13
|
+
* Module specifier for plugin bundles:
|
|
14
|
+
* '@adas/plugin-sdk' — the only SDK module a bundle can require. This file
|
|
15
|
+
* once named '@adas/device-state' here (4110943); the require shim never
|
|
16
|
+
* resolved it, so a bundle that imported it got `{}` and failed later. It is
|
|
17
|
+
* now refused by name at evaluation (bundles/bundleModuleNames.ts).
|
|
15
18
|
*
|
|
16
19
|
* ─────────────────────────────────────────────────────────────────────────
|
|
17
20
|
* WHICH LAYER AM I IN?
|
|
@@ -20,8 +23,9 @@ export { ActivityIndicator, Animated, FlatList, Image, Modal, Platform, Pressabl
|
|
|
20
23
|
* VERB names: takePhoto, getCurrent, scanBarcode,
|
|
21
24
|
* readOnce, authenticate, pickImage.
|
|
22
25
|
* Returns a BARE value. THROWS when it cannot.
|
|
23
|
-
* Declared in the manifest as `
|
|
24
|
-
*
|
|
26
|
+
* Declared in the manifest as `capabilities: { camera: true }`
|
|
27
|
+
* (the legacy `native: {}` field is still honoured).
|
|
28
|
+
* Its types are imported from '@adas/plugin-sdk'.
|
|
25
29
|
*
|
|
26
30
|
* props.deviceState.* SEMANTIC. "What is true right now, and how well do
|
|
27
31
|
* we know it?"
|
|
@@ -30,7 +34,8 @@ export { ActivityIndicator, Animated, FlatList, Image, Modal, Platform, Pressabl
|
|
|
30
34
|
* and able to say `unknown` in every field.
|
|
31
35
|
* A read NEVER throws for absence.
|
|
32
36
|
* Declared in the manifest as `stateDomains: ['visual']`.
|
|
33
|
-
*
|
|
37
|
+
* Defined in this file; its types and helpers are
|
|
38
|
+
* imported from '@adas/plugin-sdk' too.
|
|
34
39
|
*
|
|
35
40
|
* The two containers are the discriminator. Every semantic call in a plugin
|
|
36
41
|
* begins with the eight characters `deviceState.`; every mechanical call
|
|
@@ -117,17 +122,19 @@ type RestrictionState = 'none' | 'backgroundRestricted' | 'systemRestricted' | '
|
|
|
117
122
|
* never raw sensor streams." If you find yourself wanting an event per frame
|
|
118
123
|
* or per GPS fix, you are in the wrong layer.
|
|
119
124
|
*/
|
|
120
|
-
interface StateEvent<TName extends string = string> {
|
|
125
|
+
interface StateEvent<TName extends string = string, TPayload = unknown> {
|
|
121
126
|
domain: DeviceBridgeDomain;
|
|
122
127
|
name: TName;
|
|
123
128
|
/** Epoch ms. */
|
|
124
129
|
timestamp: number;
|
|
125
130
|
/**
|
|
126
131
|
* OPEN: per-event payload schemas are not specified anywhere in the three
|
|
127
|
-
* design docs.
|
|
128
|
-
* narrow it rather than read a field that may not exist.
|
|
132
|
+
* design docs. Defaults to `unknown` rather than `any` so a consumer is forced
|
|
133
|
+
* to narrow it rather than read a field that may not exist. A domain that
|
|
134
|
+
* decides its schemas (spatial — see `SpatialStateEvent`) passes `TPayload`,
|
|
135
|
+
* which is the freeze's own second parameter, dropped here until one did.
|
|
129
136
|
*/
|
|
130
|
-
payload?:
|
|
137
|
+
payload?: TPayload;
|
|
131
138
|
}
|
|
132
139
|
interface Subscription {
|
|
133
140
|
unsubscribe(): void;
|
|
@@ -141,9 +148,17 @@ type StateListener<TName extends string = string> = (event: StateEvent<TName>) =
|
|
|
141
148
|
* connectivity, capability, interaction and environment only. Rather than mint
|
|
142
149
|
* names those docs deliberately did not mint, their events are typed against
|
|
143
150
|
* the naming RULE. A wrong-shaped name is a compile error; the specific
|
|
144
|
-
* vocabulary stays open, as the docs left it
|
|
151
|
+
* vocabulary stays open, as the docs left it — except where a DECISION closes
|
|
152
|
+
* it (spatial, see `SpatialEvent`), and that list is checked against the rule
|
|
153
|
+
* through `AssertConforming`.
|
|
145
154
|
*/
|
|
146
155
|
type ConformingEventName = `${string}Changed` | `${string}Started` | `${string}Stopped` | `${string}Available` | `${string}Unavailable` | `${string}StateStale`;
|
|
156
|
+
/**
|
|
157
|
+
* A closed list of names, checked against the naming rule at compile time.
|
|
158
|
+
* Wrap a decided vocabulary in this and a name like `turnCompleted` stops
|
|
159
|
+
* compiling instead of reaching a plugin.
|
|
160
|
+
*/
|
|
161
|
+
type AssertConforming<T extends ConformingEventName> = T;
|
|
147
162
|
/** FROZEN (API_FREEZE §12). */
|
|
148
163
|
type DeviceBridgeErrorCode = 'UNSUPPORTED' | 'PERMISSION_DENIED' | 'RESTRICTED' | 'BUSY' | 'TEMPORARILY_UNAVAILABLE' | 'STALE' | 'TIMEOUT' | 'CANCELLED' | 'INTERNAL';
|
|
149
164
|
/**
|
|
@@ -272,12 +287,89 @@ interface SpatialSnapshot {
|
|
|
272
287
|
};
|
|
273
288
|
quality: StateQuality;
|
|
274
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* DECISION (not frozen) — the spatial event vocabulary. Six names, all ending
|
|
292
|
+
* in a suffix the naming rule allows, which `AssertConforming` enforces.
|
|
293
|
+
*
|
|
294
|
+
* Drawn from the architecture doc's spatial candidates (meaningful movement,
|
|
295
|
+
* meaningful turn, location quality degraded/recovered, state became stale)
|
|
296
|
+
* and named after the SNAPSHOT fields they report, so an event and a read use
|
|
297
|
+
* one vocabulary: `lastMeaningfulTurnChanged` is `changes.lastMeaningfulTurnDeg`
|
|
298
|
+
* changing. The names an earlier note floated (`turnCompleted`, `arrived`,
|
|
299
|
+
* `headingLost`) are rejected — the first two fail the rule, and "arrived" is a
|
|
300
|
+
* destination, which the vocabulary's Governance puts above Device Bridge.
|
|
301
|
+
*/
|
|
302
|
+
type SpatialEvent = AssertConforming<'movementStarted' | 'movementStopped' | 'lastMeaningfulTurnChanged' | 'positionConfidenceChanged' | 'positionChanged' | 'spatialStateStale'>;
|
|
303
|
+
/** A spatial event: the frozen envelope, with its payload always present. */
|
|
304
|
+
type SpatialEventOf<TName extends SpatialEvent, TPayload> = StateEvent<TName, TPayload> & {
|
|
305
|
+
domain: 'spatial';
|
|
306
|
+
payload: TPayload;
|
|
307
|
+
};
|
|
308
|
+
/**
|
|
309
|
+
* DECISION — what each spatial event carries. Only the facts of the
|
|
310
|
+
* transition, in the snapshot's own field names; everything else is one
|
|
311
|
+
* `getSnapshot()` away, which keeps two shapes of the same fact from drifting.
|
|
312
|
+
*
|
|
313
|
+
* `timestamp` on the envelope is when the transition was RECOGNISED. Where a
|
|
314
|
+
* hold delays that on purpose, `since` carries the true onset.
|
|
315
|
+
*/
|
|
316
|
+
type SpatialStateEvent =
|
|
317
|
+
/** `movement.state` became `moving` and held (navigation: 3 s). */
|
|
318
|
+
SpatialEventOf<'movementStarted', {
|
|
319
|
+
previous: 'stationary' | 'unknown';
|
|
320
|
+
speedMps: number | null;
|
|
321
|
+
bearingDeg: number | null;
|
|
322
|
+
since: number;
|
|
323
|
+
}>
|
|
324
|
+
/** `moving` → `stationary`, held (navigation: 5 s). Never fired on moving →
|
|
325
|
+
* `unknown`: losing the speed is not evidence of stopping. */
|
|
326
|
+
| SpatialEventOf<'movementStopped', {
|
|
327
|
+
previous: 'moving';
|
|
328
|
+
since: number;
|
|
329
|
+
}>
|
|
330
|
+
/** A new completed turn. Signed: positive = clockwise = right. */
|
|
331
|
+
| SpatialEventOf<'lastMeaningfulTurnChanged', {
|
|
332
|
+
lastMeaningfulTurnDeg: number;
|
|
333
|
+
timestamp: number;
|
|
334
|
+
}>
|
|
335
|
+
/** The GPS fix band (high ≤10 m, medium ≤50 m, else low) changed and held.
|
|
336
|
+
* The fix alone — a compass calibration change is not a position event. */
|
|
337
|
+
| SpatialEventOf<'positionConfidenceChanged', {
|
|
338
|
+
level: ConfidenceLevel;
|
|
339
|
+
previous: ConfidenceLevel;
|
|
340
|
+
accuracyM: number | null;
|
|
341
|
+
direction: 'dropped' | 'recovered';
|
|
342
|
+
}>
|
|
343
|
+
/** Moved at least max(profile floor, 2 × accuracyM) from the last reported
|
|
344
|
+
* position, so GPS jitter never fires it. The session's first fix fires
|
|
345
|
+
* with `previous: null`. */
|
|
346
|
+
| SpatialEventOf<'positionChanged', {
|
|
347
|
+
position: SpatialPoint;
|
|
348
|
+
previous: SpatialPoint | null;
|
|
349
|
+
distanceM: number | null;
|
|
350
|
+
}>
|
|
351
|
+
/** `quality.freshness.state` became `stale`. Once; re-armed by a fresh read. */
|
|
352
|
+
| SpatialEventOf<'spatialStateStale', {
|
|
353
|
+
observedAt: number | null;
|
|
354
|
+
ageMs: number | null;
|
|
355
|
+
}>;
|
|
275
356
|
interface SpatialDomain {
|
|
276
357
|
getSnapshot(): Promise<SpatialSnapshot>;
|
|
277
358
|
start(options: StartOptions<SpatialIntent>): Promise<StateSession>;
|
|
278
359
|
stop(sessionId?: string): Promise<void>;
|
|
279
|
-
/**
|
|
280
|
-
|
|
360
|
+
/**
|
|
361
|
+
* DECISION: the six `SpatialEvent`s, typed per name — narrow on `e.name` and
|
|
362
|
+
* `e.payload` is that event's shape.
|
|
363
|
+
*
|
|
364
|
+
* Events flow only while a spatial session is open; subscribing opens no
|
|
365
|
+
* sensor. They are derived from the same snapshot `getSnapshot()` returns: a
|
|
366
|
+
* listener that re-reads inside the callback sees the snapshot its event came
|
|
367
|
+
* from. A state change must hold, on at least two fixes, before it fires.
|
|
368
|
+
*
|
|
369
|
+
* From a device tool's `ctx.deviceState` this refuses (RESTRICTED): a call
|
|
370
|
+
* has no lifetime to own the listener.
|
|
371
|
+
*/
|
|
372
|
+
subscribe(listener: (event: SpatialStateEvent) => void): Subscription;
|
|
281
373
|
}
|
|
282
374
|
/** FROZEN (API_FREEZE §4) + VOCABULARY §"Visual Intents". Nine values. */
|
|
283
375
|
type VisualIntent = 'navigation' | 'document' | 'ocr' | 'shopping' | 'inspection' | 'accessibility' | 'stream' | 'highQuality' | 'lowPower';
|
|
@@ -431,8 +523,19 @@ interface VisualDomain {
|
|
|
431
523
|
}
|
|
432
524
|
/** FROZEN (API_FREEZE §5, VOCABULARY §"Human Activity Values"). */
|
|
433
525
|
type HumanActivity = 'stationary' | 'walking' | 'running' | 'cycling' | 'driving' | 'unknown';
|
|
434
|
-
/**
|
|
435
|
-
|
|
526
|
+
/**
|
|
527
|
+
* FROZEN. Independent of HumanActivity — walking+pocket, driving+mounted.
|
|
528
|
+
*
|
|
529
|
+
* What is returned TODAY: `handheld`, `table` and `unknown` only. Do not write
|
|
530
|
+
* a branch that waits for `pocket` or `mounted`; it will never run. A pocketed
|
|
531
|
+
* phone shows up in the Visual domain's frame quality (`obstructed: true` /
|
|
532
|
+
* `exposure: 'dark'`), not here.
|
|
533
|
+
*/
|
|
534
|
+
type DevicePlacement = 'handheld'
|
|
535
|
+
/** RESERVED — never returned until the proximity/light sensor is wired. */
|
|
536
|
+
| 'pocket' | 'table'
|
|
537
|
+
/** RESERVED — never returned until Bluetooth/CarPlay mount context is wired. */
|
|
538
|
+
| 'mounted' | 'unknown';
|
|
436
539
|
/**
|
|
437
540
|
* FROZEN. Note `driving` is BOTH an intent here and a HumanActivity value
|
|
438
541
|
* above. The docs define no relationship between the two and do not flag the
|
|
@@ -841,6 +944,7 @@ interface DeviceToolContextV2Extension {
|
|
|
841
944
|
type deviceState_ActivityDomain = ActivityDomain;
|
|
842
945
|
type deviceState_ActivityIntent = ActivityIntent;
|
|
843
946
|
type deviceState_ActivitySnapshot = ActivitySnapshot;
|
|
947
|
+
type deviceState_AssertConforming<T extends ConformingEventName> = AssertConforming<T>;
|
|
844
948
|
type deviceState_AvailabilityState = AvailabilityState;
|
|
845
949
|
type deviceState_CapabilityDomain = CapabilityDomain;
|
|
846
950
|
type deviceState_CapabilityEvent = CapabilityEvent;
|
|
@@ -877,12 +981,14 @@ type deviceState_MediaHandle = MediaHandle;
|
|
|
877
981
|
type deviceState_PluginPropsV2Extension = PluginPropsV2Extension;
|
|
878
982
|
type deviceState_RestrictionState = RestrictionState;
|
|
879
983
|
type deviceState_SpatialDomain = SpatialDomain;
|
|
984
|
+
type deviceState_SpatialEvent = SpatialEvent;
|
|
880
985
|
type deviceState_SpatialIntent = SpatialIntent;
|
|
881
986
|
type deviceState_SpatialPoint = SpatialPoint;
|
|
882
987
|
type deviceState_SpatialSnapshot = SpatialSnapshot;
|
|
988
|
+
type deviceState_SpatialStateEvent = SpatialStateEvent;
|
|
883
989
|
type deviceState_StartOptions<TIntent extends string> = StartOptions<TIntent>;
|
|
884
990
|
type deviceState_StateDomainDeclaration = StateDomainDeclaration;
|
|
885
|
-
type deviceState_StateEvent<TName extends string = string> = StateEvent<TName>;
|
|
991
|
+
type deviceState_StateEvent<TName extends string = string, TPayload = unknown> = StateEvent<TName, TPayload>;
|
|
886
992
|
type deviceState_StateListener<TName extends string = string> = StateListener<TName>;
|
|
887
993
|
type deviceState_StateQuality = StateQuality;
|
|
888
994
|
type deviceState_StateSession = StateSession;
|
|
@@ -900,7 +1006,7 @@ declare const deviceState_explainCapability: typeof explainCapability;
|
|
|
900
1006
|
declare const deviceState_isActionable: typeof isActionable;
|
|
901
1007
|
declare const deviceState_neverObserved: typeof neverObserved;
|
|
902
1008
|
declare namespace deviceState {
|
|
903
|
-
export { type deviceState_ActivityDomain as ActivityDomain, type deviceState_ActivityIntent as ActivityIntent, type deviceState_ActivitySnapshot as ActivitySnapshot, type deviceState_AvailabilityState as AvailabilityState, type deviceState_CapabilityDomain as CapabilityDomain, type deviceState_CapabilityEvent as CapabilityEvent, type deviceState_CapabilitySnapshot as CapabilitySnapshot, type deviceState_CapabilityStatus as CapabilityStatus, type deviceState_Confidence as Confidence, type deviceState_ConfidenceLevel as ConfidenceLevel, type deviceState_ConformingEventName as ConformingEventName, type deviceState_ConnectivityDomain as ConnectivityDomain, type deviceState_ConnectivityEvent as ConnectivityEvent, type deviceState_ConnectivityIntent as ConnectivityIntent, type deviceState_ConnectivitySnapshot as ConnectivitySnapshot, type deviceState_DeviceBridgeDomain as DeviceBridgeDomain, deviceState_DeviceBridgeError as DeviceBridgeError, type deviceState_DeviceBridgeErrorCode as DeviceBridgeErrorCode, type deviceState_DeviceBridgeV2 as DeviceBridgeV2, type deviceState_DeviceContextDomain as DeviceContextDomain, type deviceState_DeviceContextSnapshot as DeviceContextSnapshot, type deviceState_DevicePlacement as DevicePlacement, type deviceState_DeviceState as DeviceState, type deviceState_DeviceToolContextV2Extension as DeviceToolContextV2Extension, type deviceState_EnvironmentDomain as EnvironmentDomain, type deviceState_EnvironmentEvent as EnvironmentEvent, type deviceState_EnvironmentIntent as EnvironmentIntent, type deviceState_EnvironmentSnapshot as EnvironmentSnapshot, type deviceState_Freshness as Freshness, type deviceState_FreshnessState as FreshnessState, type deviceState_HumanActivity as HumanActivity, type deviceState_InteractionDomain as InteractionDomain, type deviceState_InteractionEvent as InteractionEvent, type deviceState_InteractionSnapshot as InteractionSnapshot, type deviceState_MediaHandle as MediaHandle, type PermissionState$1 as PermissionState, type deviceState_PluginPropsV2Extension as PluginPropsV2Extension, type deviceState_RestrictionState as RestrictionState, type deviceState_SpatialDomain as SpatialDomain, type deviceState_SpatialIntent as SpatialIntent, type deviceState_SpatialPoint as SpatialPoint, type deviceState_SpatialSnapshot as SpatialSnapshot, type deviceState_StartOptions as StartOptions, type deviceState_StateDomainDeclaration as StateDomainDeclaration, type deviceState_StateEvent as StateEvent, type deviceState_StateListener as StateListener, type deviceState_StateQuality as StateQuality, type deviceState_StateSession as StateSession, type deviceState_StatefulDomain as StatefulDomain, type deviceState_Subscription as Subscription, type deviceState_SupportState as SupportState, type deviceState_VisualCaptureOptions as VisualCaptureOptions, type deviceState_VisualCaptureResult as VisualCaptureResult, type deviceState_VisualDomain as VisualDomain, type deviceState_VisualFrameQuality as VisualFrameQuality, type deviceState_VisualFrameRef as VisualFrameRef, type deviceState_VisualIntent as VisualIntent, type deviceState_VisualSnapshot as VisualSnapshot, deviceState_explainCapability as explainCapability, deviceState_isActionable as isActionable, deviceState_neverObserved as neverObserved };
|
|
1009
|
+
export { type deviceState_ActivityDomain as ActivityDomain, type deviceState_ActivityIntent as ActivityIntent, type deviceState_ActivitySnapshot as ActivitySnapshot, type deviceState_AssertConforming as AssertConforming, type deviceState_AvailabilityState as AvailabilityState, type deviceState_CapabilityDomain as CapabilityDomain, type deviceState_CapabilityEvent as CapabilityEvent, type deviceState_CapabilitySnapshot as CapabilitySnapshot, type deviceState_CapabilityStatus as CapabilityStatus, type deviceState_Confidence as Confidence, type deviceState_ConfidenceLevel as ConfidenceLevel, type deviceState_ConformingEventName as ConformingEventName, type deviceState_ConnectivityDomain as ConnectivityDomain, type deviceState_ConnectivityEvent as ConnectivityEvent, type deviceState_ConnectivityIntent as ConnectivityIntent, type deviceState_ConnectivitySnapshot as ConnectivitySnapshot, type deviceState_DeviceBridgeDomain as DeviceBridgeDomain, deviceState_DeviceBridgeError as DeviceBridgeError, type deviceState_DeviceBridgeErrorCode as DeviceBridgeErrorCode, type deviceState_DeviceBridgeV2 as DeviceBridgeV2, type deviceState_DeviceContextDomain as DeviceContextDomain, type deviceState_DeviceContextSnapshot as DeviceContextSnapshot, type deviceState_DevicePlacement as DevicePlacement, type deviceState_DeviceState as DeviceState, type deviceState_DeviceToolContextV2Extension as DeviceToolContextV2Extension, type deviceState_EnvironmentDomain as EnvironmentDomain, type deviceState_EnvironmentEvent as EnvironmentEvent, type deviceState_EnvironmentIntent as EnvironmentIntent, type deviceState_EnvironmentSnapshot as EnvironmentSnapshot, type deviceState_Freshness as Freshness, type deviceState_FreshnessState as FreshnessState, type deviceState_HumanActivity as HumanActivity, type deviceState_InteractionDomain as InteractionDomain, type deviceState_InteractionEvent as InteractionEvent, type deviceState_InteractionSnapshot as InteractionSnapshot, type deviceState_MediaHandle as MediaHandle, type PermissionState$1 as PermissionState, type deviceState_PluginPropsV2Extension as PluginPropsV2Extension, type deviceState_RestrictionState as RestrictionState, type deviceState_SpatialDomain as SpatialDomain, type deviceState_SpatialEvent as SpatialEvent, type deviceState_SpatialIntent as SpatialIntent, type deviceState_SpatialPoint as SpatialPoint, type deviceState_SpatialSnapshot as SpatialSnapshot, type deviceState_SpatialStateEvent as SpatialStateEvent, type deviceState_StartOptions as StartOptions, type deviceState_StateDomainDeclaration as StateDomainDeclaration, type deviceState_StateEvent as StateEvent, type deviceState_StateListener as StateListener, type deviceState_StateQuality as StateQuality, type deviceState_StateSession as StateSession, type deviceState_StatefulDomain as StatefulDomain, type deviceState_Subscription as Subscription, type deviceState_SupportState as SupportState, type deviceState_VisualCaptureOptions as VisualCaptureOptions, type deviceState_VisualCaptureResult as VisualCaptureResult, type deviceState_VisualDomain as VisualDomain, type deviceState_VisualFrameQuality as VisualFrameQuality, type deviceState_VisualFrameRef as VisualFrameRef, type deviceState_VisualIntent as VisualIntent, type deviceState_VisualSnapshot as VisualSnapshot, deviceState_explainCapability as explainCapability, deviceState_isActionable as isActionable, deviceState_neverObserved as neverObserved };
|
|
904
1010
|
}
|
|
905
1011
|
|
|
906
1012
|
/**
|
|
@@ -923,6 +1029,7 @@ declare namespace deviceState {
|
|
|
923
1029
|
*
|
|
924
1030
|
* See docs/handoff/SPEC_voice_session_without_the_voice_screen_2026-09-12.md.
|
|
925
1031
|
*/
|
|
1032
|
+
|
|
926
1033
|
/** The session lifecycle — identical vocabulary to the built-in `CallState`. */
|
|
927
1034
|
type VoiceState = 'idle' | 'connecting' | 'connected' | 'error';
|
|
928
1035
|
/** One line of the conversation. `tool` entries are device/skill tool activity. */
|
|
@@ -931,6 +1038,40 @@ interface VoiceTranscriptEntry {
|
|
|
931
1038
|
text: string;
|
|
932
1039
|
ts: number;
|
|
933
1040
|
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Why a device-tool call the session received did not produce a result.
|
|
1043
|
+
* - the tool RAN and did not return: `not_found` | `no_context` | `timeout` |
|
|
1044
|
+
* `failed` (it threw) — see DeviceToolFailureReason;
|
|
1045
|
+
* - the phone REFUSED it before running: `not_ready` (Core's tool allowset for
|
|
1046
|
+
* this session had not arrived yet — transient, a retry can succeed) or
|
|
1047
|
+
* `not_authorized` (not allowed in this session — final);
|
|
1048
|
+
* - `invalid`: the call itself was malformed (it named no tool, or carried no
|
|
1049
|
+
* correlationId to answer on) — not run.
|
|
1050
|
+
*/
|
|
1051
|
+
type VoiceDeviceToolReason = DeviceToolFailureReason | 'not_ready' | 'not_authorized' | 'invalid';
|
|
1052
|
+
/**
|
|
1053
|
+
* One finished device-tool call, as `VoiceSnapshot.lastDeviceTool` reports it.
|
|
1054
|
+
* `name` and `at` keep their original meaning, so a reader of just those two is
|
|
1055
|
+
* unaffected.
|
|
1056
|
+
*/
|
|
1057
|
+
type VoiceDeviceToolCall = {
|
|
1058
|
+
/** The tool the model called ('' if the call named none — see `invalid`). */
|
|
1059
|
+
name: string;
|
|
1060
|
+
/** Epoch ms the call REACHED the phone (not when it finished — that is at + ms). */
|
|
1061
|
+
at: number;
|
|
1062
|
+
/** Milliseconds from arrival to outcome (≈0 for a refusal). */
|
|
1063
|
+
ms: number;
|
|
1064
|
+
} & ({
|
|
1065
|
+
ok: true;
|
|
1066
|
+
reason: null;
|
|
1067
|
+
error: null;
|
|
1068
|
+
} | {
|
|
1069
|
+
ok: false;
|
|
1070
|
+
reason: VoiceDeviceToolReason;
|
|
1071
|
+
/** What went wrong, clipped to 200 characters: tool errors can echo the
|
|
1072
|
+
* call's arguments, which may carry what the user said. */
|
|
1073
|
+
error: string;
|
|
1074
|
+
});
|
|
934
1075
|
/** A point-in-time read of the session — what a reasoning loop polls. */
|
|
935
1076
|
interface VoiceSnapshot {
|
|
936
1077
|
state: VoiceState;
|
|
@@ -954,11 +1095,56 @@ interface VoiceSnapshot {
|
|
|
954
1095
|
/** The last error, if `state === 'error'`; also carries a refusal reason when
|
|
955
1096
|
* this surface was handed to a plugin that did not declare `voice`. */
|
|
956
1097
|
lastError: string | null;
|
|
1098
|
+
/**
|
|
1099
|
+
* The most recently FINISHED device-tool call the model made through this
|
|
1100
|
+
* session, with its outcome — or null if none has finished yet. Set once the
|
|
1101
|
+
* outcome is known (ran, refused, malformed, threw, timed out), never on
|
|
1102
|
+
* arrival, so a refused call can no longer look like one that ran.
|
|
1103
|
+
*
|
|
1104
|
+
* SCOPED to the caller: a plugin sees only calls to tools IT registered (never
|
|
1105
|
+
* another solution's tool names or errors, even on its own call); the platform
|
|
1106
|
+
* sees every call. Cleared when the session ends or a new one starts.
|
|
1107
|
+
* Diagnostic — every solution's debug view gets "why did that call fail" free.
|
|
1108
|
+
*/
|
|
1109
|
+
lastDeviceTool: VoiceDeviceToolCall | null;
|
|
1110
|
+
/** The sessionId the Core allowset is bound to. Diagnostic: authorization
|
|
1111
|
+
* requires it to equal `sessionId`; a mismatch is why tools refuse. */
|
|
1112
|
+
authorizedSessionId: string | null;
|
|
957
1113
|
}
|
|
958
1114
|
interface VoiceStartOptions {
|
|
959
1115
|
/** Hand the mic to a specific skill on connect (voice /session → takeMic),
|
|
960
1116
|
* exactly as `sys.focusUiPlugin("platform:voice", { skillSlug })` does. */
|
|
961
1117
|
skillSlug?: string;
|
|
1118
|
+
/**
|
|
1119
|
+
* A PER-CALL persona override, same shape as the authored
|
|
1120
|
+
* `solution.voice.persona` ({ name, style }). Declared on the voice
|
|
1121
|
+
* session-create call (POST /session `persona`); the server sanitizes it
|
|
1122
|
+
* (name/style only, length-capped) and layers it OVER the solution's authored
|
|
1123
|
+
* persona for THIS call only — prompt-layer, so it can never touch caller
|
|
1124
|
+
* verification or tool allow-lists. Set once at session start and constant
|
|
1125
|
+
* for the call, so it adds no per-turn prompt pushes.
|
|
1126
|
+
*
|
|
1127
|
+
* Use it when a plugin needs its own voice persona without owning a skill
|
|
1128
|
+
* (e.g. a guide HUD). A solution-wide persona belongs in solution.voice.
|
|
1129
|
+
*/
|
|
1130
|
+
persona?: VoicePersona;
|
|
1131
|
+
/**
|
|
1132
|
+
* `'none'` = a CLEAN call: the voice gets none of the workspace's skills — no
|
|
1133
|
+
* skill manifest, no skill tools, no platform welcome line, no mid-call
|
|
1134
|
+
* skill-state prompt refresh — only the voice basics, this call's persona and
|
|
1135
|
+
* the CALLING plugin's own device tools (other solutions' tools registered on
|
|
1136
|
+
* the same phone are neither offered nor run). For a plugin that owns its conversation (e.g.
|
|
1137
|
+
* a guide HUD). Reduce-only: it can remove what the voice gets, never add.
|
|
1138
|
+
* Absent = the workspace's skills, as today. Not combinable with `skillSlug`.
|
|
1139
|
+
*/
|
|
1140
|
+
skills?: 'none';
|
|
1141
|
+
}
|
|
1142
|
+
/** The authored persona shape (solution.voice.persona). */
|
|
1143
|
+
interface VoicePersona {
|
|
1144
|
+
/** The name the voice uses for itself ("You are <name> …"). */
|
|
1145
|
+
name?: string;
|
|
1146
|
+
/** Free-text style/behaviour guidance rendered under the identity line. */
|
|
1147
|
+
style?: string;
|
|
962
1148
|
}
|
|
963
1149
|
/**
|
|
964
1150
|
* What changed. Coarse on purpose: `state` covers connect/mute/session/partials,
|
|
@@ -1411,6 +1597,108 @@ interface VisionAPI {
|
|
|
1411
1597
|
/** Measure whether a frame is usable. Feeds VisualFrameRef.quality. */
|
|
1412
1598
|
assessQuality(frameId: string): Promise<VisionFrameQuality>;
|
|
1413
1599
|
}
|
|
1600
|
+
/** The same `{ latitude, longitude }` `location.getCurrent` returns, so a fix
|
|
1601
|
+
* passes straight in. */
|
|
1602
|
+
interface MapsCoordinate {
|
|
1603
|
+
latitude: number;
|
|
1604
|
+
longitude: number;
|
|
1605
|
+
}
|
|
1606
|
+
/** A postal address, field by field. `null` = the map data has no value — never
|
|
1607
|
+
* an empty string, which would read as a street with no name. */
|
|
1608
|
+
interface MapsAddress {
|
|
1609
|
+
/** The placemark's own name — a building, or often number + street. */
|
|
1610
|
+
name: string | null;
|
|
1611
|
+
street: string | null;
|
|
1612
|
+
streetNumber: string | null;
|
|
1613
|
+
/** Neighbourhood / sub-locality. */
|
|
1614
|
+
district: string | null;
|
|
1615
|
+
city: string | null;
|
|
1616
|
+
/** State / province / administrative area. */
|
|
1617
|
+
region: string | null;
|
|
1618
|
+
postalCode: string | null;
|
|
1619
|
+
country: string | null;
|
|
1620
|
+
/** ISO 3166-1 alpha-2, e.g. "GB". */
|
|
1621
|
+
isoCountryCode: string | null;
|
|
1622
|
+
}
|
|
1623
|
+
interface MapsPlace {
|
|
1624
|
+
/** What the map calls it ("Boots", "10 Downing St"). Null when unnamed. */
|
|
1625
|
+
name: string | null;
|
|
1626
|
+
coordinate: MapsCoordinate;
|
|
1627
|
+
address: MapsAddress;
|
|
1628
|
+
/** Apple's point-of-interest category as a vocabulary term ("Pharmacy",
|
|
1629
|
+
* "Cafe", "PublicTransport") — NOT free text. Null for a plain address, or
|
|
1630
|
+
* when the service gives none. */
|
|
1631
|
+
category: string | null;
|
|
1632
|
+
/** Straight-line metres from `opts.near`, when you passed one — NOT walking
|
|
1633
|
+
* distance (ask `route` for that). Null without `near`. */
|
|
1634
|
+
distanceM: number | null;
|
|
1635
|
+
}
|
|
1636
|
+
interface MapsRouteStep {
|
|
1637
|
+
/** Apple's instruction ("Turn right onto Baker St"). The FIRST step is usually
|
|
1638
|
+
* an origin step with an empty instruction — kept, not dropped, so step
|
|
1639
|
+
* indexes line up with Apple's. */
|
|
1640
|
+
instructions: string;
|
|
1641
|
+
distanceM: number;
|
|
1642
|
+
/** An advisory Apple attaches to this step. Null when there is none. */
|
|
1643
|
+
notice: string | null;
|
|
1644
|
+
polyline: MapsCoordinate[];
|
|
1645
|
+
}
|
|
1646
|
+
interface MapsRoute {
|
|
1647
|
+
mode: 'walking';
|
|
1648
|
+
distanceM: number;
|
|
1649
|
+
/** Apple's estimate for the whole route. */
|
|
1650
|
+
expectedTravelTimeMs: number;
|
|
1651
|
+
steps: MapsRouteStep[];
|
|
1652
|
+
/** The whole route, start to end. */
|
|
1653
|
+
polyline: MapsCoordinate[];
|
|
1654
|
+
}
|
|
1655
|
+
interface MapsAPI {
|
|
1656
|
+
/** Can this build search and route? True when the AdaMaps module is in the
|
|
1657
|
+
* binary (iOS). False on Android and on an older build — `reverseGeocode`
|
|
1658
|
+
* does not need the module and may still work there. */
|
|
1659
|
+
isSupported(): Promise<boolean>;
|
|
1660
|
+
/**
|
|
1661
|
+
* Find places by free text — "pharmacy", "10 Downing Street".
|
|
1662
|
+
*
|
|
1663
|
+
* `near` centres the search on a coordinate YOU supply, within `radiusM`
|
|
1664
|
+
* (default 3000, above 0 and at most 100 000). On iOS 18+ results are
|
|
1665
|
+
* confined to that area; before 18 it is a strong hint and a farther result
|
|
1666
|
+
* can come back — each result's `distanceM` says how far. At most `limit`
|
|
1667
|
+
* results (default 8, from 1; more than 25 is capped at 25).
|
|
1668
|
+
*
|
|
1669
|
+
* Network, Apple's servers. `[]` = Apple looked and found nothing. iOS only:
|
|
1670
|
+
* throws UNAVAILABLE on Android or a build without AdaMaps.
|
|
1671
|
+
*/
|
|
1672
|
+
search(query: string, opts?: {
|
|
1673
|
+
near?: MapsCoordinate;
|
|
1674
|
+
radiusM?: number;
|
|
1675
|
+
limit?: number;
|
|
1676
|
+
}): Promise<MapsPlace[]>;
|
|
1677
|
+
/**
|
|
1678
|
+
* What is at a coordinate — its address and name.
|
|
1679
|
+
*
|
|
1680
|
+
* iOS AND Android (the platform geocoder); needs no AdaMaps module. The
|
|
1681
|
+
* result's `coordinate` is the point you asked about, and its
|
|
1682
|
+
* `category` / `distanceM` are null. `null` = nothing there.
|
|
1683
|
+
*
|
|
1684
|
+
* Network, not on-device. iOS reports a rate limit and a lost connection as
|
|
1685
|
+
* the SAME error, so OFFLINE here can also mean "too many requests". Android's
|
|
1686
|
+
* geocoder refuses without location permission (UNAVAILABLE) — that is the
|
|
1687
|
+
* platform's rule, not a location read.
|
|
1688
|
+
*/
|
|
1689
|
+
reverseGeocode(at: MapsCoordinate): Promise<MapsPlace | null>;
|
|
1690
|
+
/**
|
|
1691
|
+
* A walking route between two coordinates YOU supply.
|
|
1692
|
+
*
|
|
1693
|
+
* `null` = Apple found no route — never a fake 0 m route, which would read as
|
|
1694
|
+
* "you have arrived". Walking is the only mode; any other refuses by name.
|
|
1695
|
+
* Network, Apple's servers. iOS only: throws UNAVAILABLE on Android or a build
|
|
1696
|
+
* without AdaMaps.
|
|
1697
|
+
*/
|
|
1698
|
+
route(from: MapsCoordinate, to: MapsCoordinate, opts?: {
|
|
1699
|
+
mode?: 'walking';
|
|
1700
|
+
}): Promise<MapsRoute | null>;
|
|
1701
|
+
}
|
|
1414
1702
|
interface BluetoothAPI {
|
|
1415
1703
|
/** Is BLE hardware present and permitted? */
|
|
1416
1704
|
isSupported(): Promise<boolean>;
|
|
@@ -1481,6 +1769,9 @@ interface PluginNative {
|
|
|
1481
1769
|
bluetooth: BluetoothAPI;
|
|
1482
1770
|
/** SHAPE ONLY in this build — see VisionAPI. On-device image understanding. */
|
|
1483
1771
|
vision: VisionAPI;
|
|
1772
|
+
/** Place search, reverse geocoding, walking routes — see MapsAPI. Sends
|
|
1773
|
+
* queries to Apple's map servers; NOT on-device. */
|
|
1774
|
+
maps: MapsAPI;
|
|
1484
1775
|
}
|
|
1485
1776
|
interface NativeCapabilities$1 {
|
|
1486
1777
|
camera?: boolean;
|
|
@@ -1496,6 +1787,9 @@ interface NativeCapabilities$1 {
|
|
|
1496
1787
|
/** On-device image understanding (OCR, labels, frame quality). Refuses by
|
|
1497
1788
|
* name until the native module lands — see VisionAPI. */
|
|
1498
1789
|
vision?: boolean;
|
|
1790
|
+
/** Place search, reverse geocoding, walking routes. Means "may send place
|
|
1791
|
+
* queries to Apple Maps" — network, not on-device. See MapsAPI. */
|
|
1792
|
+
maps?: boolean;
|
|
1499
1793
|
/** The live voice SESSION (props.voice). A privacy surface — a declaring
|
|
1500
1794
|
* plugin can read the conversation transcript — so it is gated like any
|
|
1501
1795
|
* other capability, and coupled to the platform's unsuppressable mic
|
|
@@ -1649,8 +1943,9 @@ interface PluginManifest$1 {
|
|
|
1649
1943
|
};
|
|
1650
1944
|
capabilities?: Record<string, unknown>;
|
|
1651
1945
|
/** L3 DeviceState domains this plugin reads (props.deviceState), as Core passes
|
|
1652
|
-
* them through from the
|
|
1653
|
-
* bundle's own `stateDomains
|
|
1946
|
+
* them through from the solution's declaration. Absent → NO domains (fail
|
|
1947
|
+
* closed): the loaded bundle's own `stateDomains` is never consulted when a
|
|
1948
|
+
* manifest exists. Read via resolvePluginRuntimeProps. */
|
|
1654
1949
|
stateDomains?: DeviceBridgeDomain[];
|
|
1655
1950
|
_source?: string;
|
|
1656
1951
|
_connectorId?: string;
|
|
@@ -1688,12 +1983,27 @@ interface DeviceToolContext {
|
|
|
1688
1983
|
* the same gating a UI plugin's `props.deviceState` gets. This is what lets an
|
|
1689
1984
|
* EXTERNAL tool (e.g. Ada Guide's `guide.observe`) read `ctx.deviceState.visual`
|
|
1690
1985
|
* over the public surface, with no SDK-internal access.
|
|
1986
|
+
*
|
|
1987
|
+
* `subscribe` refuses (RESTRICTED) on every domain: a call has no lifetime to
|
|
1988
|
+
* own a subscription, and the engines outlive it. Read instead.
|
|
1691
1989
|
*/
|
|
1692
1990
|
deviceState: DeviceState;
|
|
1693
1991
|
/** Cooperative cancellation: caller cancelled, or the session went away. */
|
|
1694
1992
|
signal?: AbortSignal;
|
|
1695
1993
|
}
|
|
1696
1994
|
type DeviceToolFn = (args: any, ctx: DeviceToolContext) => Promise<any> | any;
|
|
1995
|
+
/**
|
|
1996
|
+
* Why running a device tool produced no result (`callDeviceTool`'s
|
|
1997
|
+
* `{ ok: false, reason }`). The one list: the registry returns these, and the
|
|
1998
|
+
* voice snapshot's `lastDeviceTool.reason` carries them unchanged.
|
|
1999
|
+
* - `not_found` — no tool is registered under that name.
|
|
2000
|
+
* - `no_context` — the tool's device context could not be built.
|
|
2001
|
+
* - `timeout` — the tool did not finish within the call's timeout.
|
|
2002
|
+
* - `failed` — the tool threw; the error is its message.
|
|
2003
|
+
* A tool that RETURNS a value has succeeded as far as the platform is concerned,
|
|
2004
|
+
* even if the value describes a failure — throw to report one.
|
|
2005
|
+
*/
|
|
2006
|
+
type DeviceToolFailureReason = 'not_found' | 'no_context' | 'timeout' | 'failed';
|
|
1697
2007
|
interface DeviceToolDefinition {
|
|
1698
2008
|
/** Domain name, as the catalog and the caller see it. e.g. "guide.observe" */
|
|
1699
2009
|
name: string;
|
|
@@ -1708,9 +2018,10 @@ interface RegisteredToolsPlugin {
|
|
|
1708
2018
|
type: 'tools';
|
|
1709
2019
|
capabilities?: NativeCapabilities$1;
|
|
1710
2020
|
/**
|
|
1711
|
-
* L3 DeviceState domains this plugin's tools
|
|
1712
|
-
*
|
|
1713
|
-
*
|
|
2021
|
+
* L3 DeviceState domains this plugin's tools read via `ctx.deviceState` (e.g.
|
|
2022
|
+
* `['visual']`). For a remote bundle this is the author's statement, not a
|
|
2023
|
+
* grant: the tools get the manifest's `stateDomains` (resolvePluginGrants),
|
|
2024
|
+
* and a domain declared here but not there is denied with a warning.
|
|
1714
2025
|
*/
|
|
1715
2026
|
stateDomains?: DeviceBridgeDomain[];
|
|
1716
2027
|
tools: DeviceToolDefinition[];
|
|
@@ -1748,7 +2059,8 @@ declare function register(id: string, config: {
|
|
|
1748
2059
|
*/
|
|
1749
2060
|
declare function registerTools(id: string, config: {
|
|
1750
2061
|
capabilities?: NativeCapabilities$1;
|
|
1751
|
-
/** L3 DeviceState domains the tools
|
|
2062
|
+
/** L3 DeviceState domains the tools read via ctx.deviceState (e.g. ['visual']).
|
|
2063
|
+
* For a remote bundle the MANIFEST grants them — see RegisteredToolsPlugin. */
|
|
1752
2064
|
stateDomains?: DeviceBridgeDomain[];
|
|
1753
2065
|
tools: DeviceToolDefinition[];
|
|
1754
2066
|
}): RegisteredToolsPlugin;
|
|
@@ -2006,11 +2318,6 @@ interface PluginRuntimeProps {
|
|
|
2006
2318
|
deviceState: DeviceState;
|
|
2007
2319
|
voice: VoiceAPI;
|
|
2008
2320
|
}
|
|
2009
|
-
/**
|
|
2010
|
-
* Where a plugin's declarations come from. TRUSTED source resolution lives here,
|
|
2011
|
-
* not in each caller: the server `manifest` wins, the loaded bundle's own default
|
|
2012
|
-
* export is the fallback.
|
|
2013
|
-
*/
|
|
2014
2321
|
/** One declaration source — either the server manifest or the registry entry. */
|
|
2015
2322
|
interface PluginDeclaration {
|
|
2016
2323
|
capabilities?: NativeCapabilities$1 | Record<string, unknown>;
|
|
@@ -2033,9 +2340,17 @@ interface PluginRuntimeSource {
|
|
|
2033
2340
|
*/
|
|
2034
2341
|
plugin?: PluginDeclaration | null;
|
|
2035
2342
|
}
|
|
2343
|
+
/** What a plugin is GRANTED — resolved from its one trusted source. The same
|
|
2344
|
+
* value gates its rendered surface and its device tools (the tool registry
|
|
2345
|
+
* receives it through bundleManager.load/loadTools). */
|
|
2346
|
+
interface PluginGrants {
|
|
2347
|
+
capabilities?: NativeCapabilities$1;
|
|
2348
|
+
stateDomains?: DeviceBridgeDomain[];
|
|
2349
|
+
}
|
|
2036
2350
|
/**
|
|
2037
|
-
*
|
|
2038
|
-
*
|
|
2351
|
+
* Resolve a plugin's grants — the SINGLE place this happens, for every rendered
|
|
2352
|
+
* surface AND for the device tools its bundle registers, so a plugin gets
|
|
2353
|
+
* identical access through either.
|
|
2039
2354
|
*
|
|
2040
2355
|
* TRUST BOUNDARY. Exactly ONE source is authoritative: the server `manifest` for
|
|
2041
2356
|
* a remote plugin, the registry `plugin` for a host-owned one. They are NEVER
|
|
@@ -2045,6 +2360,9 @@ interface PluginRuntimeSource {
|
|
|
2045
2360
|
* not carry `stateDomains` for a remote plugin, the fix is Core passing it, not
|
|
2046
2361
|
* trusting the bundle: Core already holds the solution's declaration.)
|
|
2047
2362
|
*/
|
|
2363
|
+
declare function resolvePluginGrants(src: PluginRuntimeSource): PluginGrants;
|
|
2364
|
+
/** Turn a plugin's grants into the gated { device, native, deviceState, voice } it
|
|
2365
|
+
* receives on every rendered surface. */
|
|
2048
2366
|
declare function resolvePluginRuntimeProps(src: PluginRuntimeSource): PluginRuntimeProps;
|
|
2049
2367
|
|
|
2050
2368
|
interface DeviceToolSummary {
|
|
@@ -2059,7 +2377,7 @@ type DeviceToolOutcome = {
|
|
|
2059
2377
|
} | {
|
|
2060
2378
|
ok: false;
|
|
2061
2379
|
error: string;
|
|
2062
|
-
reason:
|
|
2380
|
+
reason: DeviceToolFailureReason;
|
|
2063
2381
|
};
|
|
2064
2382
|
/**
|
|
2065
2383
|
* Override how a device tool reaches the Device Bridge. Rarely needed — the
|
|
@@ -2077,7 +2395,9 @@ declare function setDeviceToolContextProvider(fn: (pluginId: string, capabilitie
|
|
|
2077
2395
|
declare function registerDeviceTools(pluginId: string, tools: DeviceToolDefinition[], capabilities?: NativeCapabilities$1, stateDomains?: DeviceBridgeDomain[]): string[];
|
|
2078
2396
|
/** Drop every tool owned by a plugin (bundle eviction / plugin revoked). */
|
|
2079
2397
|
declare function unregisterDeviceTools(pluginId: string): void;
|
|
2080
|
-
|
|
2398
|
+
/** Is `name` registered — and, given `pluginId`, registered BY that plugin?
|
|
2399
|
+
* (A clean voice call only lets its owner's own tools run.) */
|
|
2400
|
+
declare function hasDeviceTool(name: string, pluginId?: string): boolean;
|
|
2081
2401
|
declare function listDeviceTools(): DeviceToolSummary[];
|
|
2082
2402
|
/**
|
|
2083
2403
|
* Invoke a device tool. Never throws — every failure comes back as a typed
|
|
@@ -2210,6 +2530,9 @@ declare class BundleManager {
|
|
|
2210
2530
|
* of the previous tenant's tools.
|
|
2211
2531
|
*/
|
|
2212
2532
|
private registeredToolPluginIds;
|
|
2533
|
+
/** bundleUrl → the trusted declaration last recorded on its disk-cache entry
|
|
2534
|
+
* this session, so a memory hit with unchanged grants skips the index I/O. */
|
|
2535
|
+
private recordedDeclarations;
|
|
2213
2536
|
/**
|
|
2214
2537
|
* Enforce the in-memory tenant boundary. Call at the top of every public load
|
|
2215
2538
|
* path, BEFORE any cache read.
|
|
@@ -2237,9 +2560,11 @@ declare class BundleManager {
|
|
|
2237
2560
|
* tenant switch (or wipe) happened while this load was in flight. Its results
|
|
2238
2561
|
* belong to a tenant that is no longer current and must be discarded. */
|
|
2239
2562
|
private isStale;
|
|
2240
|
-
/**
|
|
2241
|
-
*
|
|
2242
|
-
*
|
|
2563
|
+
/** Record a memory serve: the running bytes are the already-evaluated module,
|
|
2564
|
+
* and `expected` is refreshed from the manifest hash the caller JUST passed —
|
|
2565
|
+
* so after a redeploy the record reads hash !== expected (STALE) instead of
|
|
2566
|
+
* the old in-sync pair. The ONE place both memory paths (load()'s early return
|
|
2567
|
+
* and loadModule's) record reuse. No-op if nothing was recorded yet. */
|
|
2243
2568
|
private markMemoryHit;
|
|
2244
2569
|
/**
|
|
2245
2570
|
* Load a plugin component. Returns the RegisteredUIPlugin object.
|
|
@@ -2249,16 +2574,26 @@ declare class BundleManager {
|
|
|
2249
2574
|
* provided for any bundle the user has not previously verified — without
|
|
2250
2575
|
* it, a tampered bundle is `eval`'d as JS with full RN runtime + every
|
|
2251
2576
|
* native capability the manifest declares.
|
|
2577
|
+
*
|
|
2578
|
+
* `grants` is what the MANIFEST grants this plugin (resolvePluginGrants) — the
|
|
2579
|
+
* only authority its device tools are registered under. Omitted (e.g. a
|
|
2580
|
+
* prewarm from the slim plugin list) → the bundle is loaded but its tools are
|
|
2581
|
+
* NOT registered: without the manifest there is nothing trusted to gate them
|
|
2582
|
+
* on, and registering with nothing would downgrade a registration the
|
|
2583
|
+
* manifest-driven tool loader already made.
|
|
2252
2584
|
*/
|
|
2253
|
-
load(pluginId: string, bundleUrl: string, version: string, expectedHash?: string): Promise<RegisteredUIPlugin>;
|
|
2585
|
+
load(pluginId: string, bundleUrl: string, version: string, expectedHash?: string, grants?: PluginGrants): Promise<RegisteredUIPlugin>;
|
|
2254
2586
|
/**
|
|
2255
2587
|
* Load a bundle for its `tools/` section only — no UI required.
|
|
2256
2588
|
*
|
|
2257
2589
|
* Same download, same integrity check, same evaluation as `load()`; the only
|
|
2258
2590
|
* difference is that a bundle exporting tools and no `Component` is a success
|
|
2259
2591
|
* here rather than "no component found". Returns the tool names registered.
|
|
2592
|
+
*
|
|
2593
|
+
* `grants` is REQUIRED: the tools are registered under `pluginId` with exactly
|
|
2594
|
+
* what the manifest grants (see load()), never what the bundle claims.
|
|
2260
2595
|
*/
|
|
2261
|
-
loadTools(pluginId: string, bundleUrl: string, version: string, expectedHash
|
|
2596
|
+
loadTools(pluginId: string, bundleUrl: string, version: string, expectedHash: string | undefined, grants: PluginGrants): Promise<string[]>;
|
|
2262
2597
|
/**
|
|
2263
2598
|
* Prewarm: load every plugin bundle in the background after app boot, so a
|
|
2264
2599
|
* later open is instant (in-memory hit) instead of round-tripping the
|
|
@@ -2270,6 +2605,8 @@ declare class BundleManager {
|
|
|
2270
2605
|
bundleUrl: string;
|
|
2271
2606
|
version: string;
|
|
2272
2607
|
bundleHash?: string;
|
|
2608
|
+
/** The manifest's grants, when the caller has the manifest (see load()). */
|
|
2609
|
+
grants?: PluginGrants;
|
|
2273
2610
|
}>): Promise<void>;
|
|
2274
2611
|
/**
|
|
2275
2612
|
* Clear all caches (in-memory + storage).
|
|
@@ -2290,11 +2627,36 @@ declare class BundleManager {
|
|
|
2290
2627
|
getLoadedBundles(): LoadedBundleInfo[];
|
|
2291
2628
|
private loadModule;
|
|
2292
2629
|
/**
|
|
2293
|
-
*
|
|
2294
|
-
*
|
|
2295
|
-
*
|
|
2630
|
+
* Register THIS plugin's device tools under the TRUSTED declaration, then
|
|
2631
|
+
* record that declaration on the disk-cache entry. Runs on every trusted load
|
|
2632
|
+
* — memory hit included — so the grants in force are always the latest the
|
|
2633
|
+
* manifest gave (a revoked grant stops applying on the next load, not the next
|
|
2634
|
+
* app start). Callers run it only after their stale check.
|
|
2635
|
+
*/
|
|
2636
|
+
private commitTools;
|
|
2637
|
+
/**
|
|
2638
|
+
* Put the plugin's own `tools` section in the device tool registry — under the
|
|
2639
|
+
* pluginId and grants the MANIFEST resolved, never the bundle's own `id`,
|
|
2640
|
+
* `capabilities` or `stateDomains`. A bundle is code the manifest authorised
|
|
2641
|
+
* by hash; what it may touch is the manifest's to say, same as its rendered
|
|
2642
|
+
* surface (resolvePluginRuntimeProps). Its own `id` would also let it register
|
|
2643
|
+
* as another plugin — including the host's platform tools, which a same-id
|
|
2644
|
+
* re-registration replaces.
|
|
2645
|
+
*
|
|
2646
|
+
* Only this plugin's export (the same lookup the component uses) is
|
|
2647
|
+
* registered. Another export's tools in the same bundle belong to another
|
|
2648
|
+
* plugin and register when THAT plugin's manifest loads it.
|
|
2296
2649
|
*/
|
|
2297
2650
|
private registerToolsFromModule;
|
|
2651
|
+
/**
|
|
2652
|
+
* Record the trusted declaration on this tenant's disk-cache entry for the
|
|
2653
|
+
* bytes RUNNING — the recovery path (tools/loader loadFromDiskCache) has no
|
|
2654
|
+
* manifest and re-registers with exactly this. Only onto an entry this tenant
|
|
2655
|
+
* owns whose hash is the running hash: the grant was issued for these bytes,
|
|
2656
|
+
* and those are the bytes recovery will evaluate. Best-effort — a lost write
|
|
2657
|
+
* leaves no `trusted`, which recovery treats as fail closed.
|
|
2658
|
+
*/
|
|
2659
|
+
private recordTrustedDeclaration;
|
|
2298
2660
|
/**
|
|
2299
2661
|
* Download + integrity-verify a bundle. Refuses to return when:
|
|
2300
2662
|
* - manifest hash and computed hash disagree
|
|
@@ -2304,11 +2666,15 @@ declare class BundleManager {
|
|
|
2304
2666
|
*/
|
|
2305
2667
|
private download;
|
|
2306
2668
|
private evaluate;
|
|
2669
|
+
/** Find a plugin component in a loaded module. */
|
|
2670
|
+
private getComponentFromMemory;
|
|
2307
2671
|
/**
|
|
2308
|
-
* Find
|
|
2309
|
-
*
|
|
2672
|
+
* Find THIS plugin's export in a loaded module — the one lookup both the
|
|
2673
|
+
* component and the device tools use, so a plugin's UI and its tools always
|
|
2674
|
+
* come from the same export. `accept` says what the caller needs from it.
|
|
2675
|
+
* Handles: direct key match, PascalCase key, default export, multi-plugin .id match.
|
|
2310
2676
|
*/
|
|
2311
|
-
private
|
|
2677
|
+
private findPluginExport;
|
|
2312
2678
|
private describeModule;
|
|
2313
2679
|
}
|
|
2314
2680
|
/** Singleton — shared across all plugin renderers */
|
|
@@ -2321,6 +2687,7 @@ declare const bundleManager: BundleManager;
|
|
|
2321
2687
|
* default is in-memory (bundles re-download per app session — correct,
|
|
2322
2688
|
* just slower on cold start).
|
|
2323
2689
|
*/
|
|
2690
|
+
|
|
2324
2691
|
interface BundleCacheEntry {
|
|
2325
2692
|
localPath: string;
|
|
2326
2693
|
version: string;
|
|
@@ -2342,6 +2709,23 @@ interface BundleCacheEntry {
|
|
|
2342
2709
|
* untrusted. Never read a missing tenant as "mine".
|
|
2343
2710
|
*/
|
|
2344
2711
|
tenant?: string;
|
|
2712
|
+
/**
|
|
2713
|
+
* The trusted declaration these bytes' device tools were last registered
|
|
2714
|
+
* under: the pluginId and grants resolved from the server manifest that named
|
|
2715
|
+
* this bundle (resolvePluginGrants). Recorded so the disk-cache recovery path —
|
|
2716
|
+
* which runs exactly when there is NO manifest — re-registers with the same
|
|
2717
|
+
* authority instead of the bundle's own claim.
|
|
2718
|
+
*
|
|
2719
|
+
* Absent → no trusted declaration is known for these bytes, so recovery skips
|
|
2720
|
+
* the entry (fail closed) and the catalog path registers it once the owning
|
|
2721
|
+
* connector answers again.
|
|
2722
|
+
*/
|
|
2723
|
+
trusted?: TrustedToolDeclaration;
|
|
2724
|
+
}
|
|
2725
|
+
/** Who a bundle's device tools belong to and what they are granted — always
|
|
2726
|
+
* from the manifest, never from the bundle. */
|
|
2727
|
+
interface TrustedToolDeclaration extends PluginGrants {
|
|
2728
|
+
pluginId: string;
|
|
2345
2729
|
}
|
|
2346
2730
|
interface BundleStorage {
|
|
2347
2731
|
loadIndex(): Promise<Record<string, BundleCacheEntry>>;
|
|
@@ -2572,6 +2956,9 @@ interface PluginManifestLike {
|
|
|
2572
2956
|
name?: string;
|
|
2573
2957
|
render?: any;
|
|
2574
2958
|
_connectorId?: string;
|
|
2959
|
+
capabilities?: Record<string, unknown>;
|
|
2960
|
+
native?: NativeCapabilities$1;
|
|
2961
|
+
stateDomains?: DeviceBridgeDomain[];
|
|
2575
2962
|
}
|
|
2576
2963
|
interface UsePluginResult {
|
|
2577
2964
|
plugin: RegisteredUIPlugin | null;
|
|
@@ -2708,7 +3095,15 @@ declare function supportsNative(manifest: PluginManifest): boolean;
|
|
|
2708
3095
|
*/
|
|
2709
3096
|
declare function getNativeComponentId(manifest: PluginManifest): string | null;
|
|
2710
3097
|
/**
|
|
2711
|
-
*
|
|
3098
|
+
* The native bundle URL for a manifest that has a bundle on the server, or null.
|
|
3099
|
+
*
|
|
3100
|
+
* Core writes `bundleUrl` into the manifest only when the connector's bundle
|
|
3101
|
+
* exists, so its PRESENCE says "there is a bundle". Its SPELLING is not used:
|
|
3102
|
+
* Core writes the id unencoded, and a connector could inject any URL. The URL
|
|
3103
|
+
* returned is always the one derivation, pluginBundleUrl(manifest.id), the same
|
|
3104
|
+
* one the rendered surface uses. Returning the manifest's own string (6294cc3)
|
|
3105
|
+
* gave the device-tool loader a second spelling, so one plugin downloaded and
|
|
3106
|
+
* evaluated twice, as two module instances (MGAP-C8).
|
|
2712
3107
|
*/
|
|
2713
3108
|
declare function getNativeBundleUrl(manifest: PluginManifest): string | null;
|
|
2714
3109
|
/**
|
|
@@ -2743,6 +3138,27 @@ declare function getPlugin(pluginId: string): Promise<PluginManifest | null>;
|
|
|
2743
3138
|
*/
|
|
2744
3139
|
declare function resolvePluginUrl(manifest: PluginManifest, tenant?: string): string;
|
|
2745
3140
|
|
|
3141
|
+
/**
|
|
3142
|
+
* THE bundle URL of a plugin: the one place it is spelled.
|
|
3143
|
+
*
|
|
3144
|
+
* Core serves a plugin's React Native bundle at
|
|
3145
|
+
* `/api/ui-plugins/<pluginId>/bundle.js`, where the id is URL-encoded because
|
|
3146
|
+
* plugin ids contain ':' (`mcp:<connector>:<plugin>`). The bundle manager keys
|
|
3147
|
+
* everything it holds by this string: evaluated modules, in-flight downloads and
|
|
3148
|
+
* the disk index. To it, two spellings of one plugin's URL are two plugins, which
|
|
3149
|
+
* means two downloads, two evaluations and two separate copies of module state.
|
|
3150
|
+
*
|
|
3151
|
+
* That happened (MGAP-C8). The rendered surface derived the encoded route, while
|
|
3152
|
+
* the device-tool loader (6294cc3) used the manifest's `bundleUrl`, which Core
|
|
3153
|
+
* fills in UNENCODED. A plugin that was on screen and also had device tools ran
|
|
3154
|
+
* as two module instances, and its tools never saw its UI's state.
|
|
3155
|
+
*
|
|
3156
|
+
* So the URL is DERIVED from the plugin id and never read from a manifest (a
|
|
3157
|
+
* connector-supplied URL is ignored, as in ea435149). Every path that builds a
|
|
3158
|
+
* plugin's bundle URL, or looks one up, calls this function.
|
|
3159
|
+
*/
|
|
3160
|
+
declare function pluginBundleUrl(pluginId: string): string;
|
|
3161
|
+
|
|
2746
3162
|
interface SurfaceDrawerProps {
|
|
2747
3163
|
visible: boolean;
|
|
2748
3164
|
onClose: () => void;
|
|
@@ -2890,4 +3306,4 @@ declare function isInlineSurface(m: SurfaceManifest): m is InlineSurfaceManifest
|
|
|
2890
3306
|
|
|
2891
3307
|
declare function SurfaceDispatcher(): react_jsx_runtime.JSX.Element;
|
|
2892
3308
|
|
|
2893
|
-
export { ActionButton, type ActivitySnapshot, AdaOrb, type AdaOrbState, Badge, type BiometricsAPI, type BiometricsAPIv2, type BluetoothAPI, type BluetoothDevice, type Bridge, type BundleCacheEntry, type BundleStorage, type CameraAPI, CameraPreview, type CameraPreviewProps, type CapabilitySnapshot, type CapabilityStatus, Card, type Confidence, type ConnectivityAPI, type ConnectivitySnapshot, type CookiesAPI, type DeviceBridgeDomain, type DeviceContextSnapshot, type DeviceState, type DeviceToolContext, type DeviceToolDefinition, type DeviceToolFn, type DeviceToolOutcome, type DeviceToolSummary, type DeviceToolsAction, type DeviceToolsSessionDeps, EmptyState, type EnvironmentSnapshot, ErrorState, type FileSystemAPI, type Freshness, type HapticsAPI, type InlineSurfaceManifest, type InteractionSnapshot, ListItem, type LoadedBundleInfo, type LoadedBundleSource, LoadingState, type LocationAPI, MemoryBundleStorage, type MountSurfaceManifest, type NativeCapabilities$1 as NativeCapabilities, type NfcAPI, type NotificationsAPI, PLATFORM_VOICE_CALLER, type PluginManifest as PlatformPluginManifest, type PluginBridge, type PluginContext, type PluginHooks$1 as PluginHooks, type PluginManifest$1 as PluginManifest, type PluginNative, type PluginProps, type PluginRuntimeProps, type PluginRuntimeSource, PluginSDK, type PluginSummary, type PluginSurfaceMeta, type RegisteredPlugin, type RegisteredServicePlugin, type RegisteredToolsPlugin, type RegisteredUIPlugin, type RemotePlugin, type RenderMode, type ScannerAPI, SearchBar, type SessionSnapshot, type SharingAPI, type SpatialIntent, type SpatialPoint, type SpatialSnapshot, type StateQuality, type StorageAPI, SurfaceCard, type SurfaceCardProps, SurfaceDispatcher, SurfaceDrawer, type SurfaceDrawerProps, SurfaceFullscreen, type SurfaceFullscreenProps, type SurfaceManifest, type SurfacePlacement, type SurfaceType, type SurfaceVisibility, TabBar, type ThemeTokens, type VisionAPI, type VisionFrameQuality, type VisionLabel, type VisionObject, type VisionText, type VisualSnapshot, type VoiceAPI, type VoiceCaller, type VoiceEvent, type VoiceSessionProvider, type VoiceSnapshot, type VoiceStartOptions, type VoiceState, type VoiceSubscription, type VoiceTranscriptEntry, type WebViewCookie, __resetSessionSyncForTest, bundleManager, callDeviceTool, clearDeviceTools, createNativeBridge, deviceState as deviceStateV2, deviceToolsActionForSession, ensureDeviceToolsLoaded, explainCapability, gateDeviceState, gatePluginNative, gateVoice, getCameraPreviewProvider, getHaptics, getNativeBundleHash, getNativeBundleUrl, getNativeComponentId, getPlugin, getPluginNative, handleServerDeviceToolCall, hasDeviceTool, implementedStateDomains, implementedVoice, isActionable, isDeviceToolCommand, isInlineSurface, isMountSurface, listDeviceTools, listPlugins, nativePluginRegistry, neverObserved, register, registerBundleModules, registerDeviceTools, registerNativePlugin, registerPlatformDeviceTools, registerService, registerTools, resetDeviceToolLoader, resolvePluginRuntimeProps, resolvePluginUrl, setBundleStorage, setCameraPreviewProvider, setDeviceStateProvider, setDeviceToolContextProvider, setDeviceToolResultPoster, setHapticsProvider, setPluginNativeProvider, setPluginThemeColors, setVoiceProvider, supportsNative, supportsWebView, syncDeviceToolsForSession, unregisterDeviceTools, unregisterNativePlugin, useApi, useNativeBridge, usePlugin, usePluginTheme, useRemotePlugins, useSurfaceStore };
|
|
3309
|
+
export { ActionButton, type ActivitySnapshot, AdaOrb, type AdaOrbState, Badge, type BiometricsAPI, type BiometricsAPIv2, type BluetoothAPI, type BluetoothDevice, type Bridge, type BundleCacheEntry, type BundleStorage, type CameraAPI, CameraPreview, type CameraPreviewProps, type CapabilitySnapshot, type CapabilityStatus, Card, type Confidence, type ConnectivityAPI, type ConnectivitySnapshot, type CookiesAPI, type DeviceBridgeDomain, type DeviceContextSnapshot, type DeviceState, type DeviceToolContext, type DeviceToolDefinition, type DeviceToolFailureReason, type DeviceToolFn, type DeviceToolOutcome, type DeviceToolSummary, type DeviceToolsAction, type DeviceToolsSessionDeps, EmptyState, type EnvironmentSnapshot, ErrorState, type FileSystemAPI, type Freshness, type HapticsAPI, type InlineSurfaceManifest, type InteractionSnapshot, ListItem, type LoadedBundleInfo, type LoadedBundleSource, LoadingState, type LocationAPI, type MapsAPI, type MapsAddress, type MapsCoordinate, type MapsPlace, type MapsRoute, type MapsRouteStep, MemoryBundleStorage, type MountSurfaceManifest, type NativeCapabilities$1 as NativeCapabilities, type NfcAPI, type NotificationsAPI, PLATFORM_VOICE_CALLER, type PluginManifest as PlatformPluginManifest, type PluginBridge, type PluginContext, type PluginGrants, type PluginHooks$1 as PluginHooks, type PluginManifest$1 as PluginManifest, type PluginNative, type PluginProps, type PluginRuntimeProps, type PluginRuntimeSource, PluginSDK, type PluginSummary, type PluginSurfaceMeta, type RegisteredPlugin, type RegisteredServicePlugin, type RegisteredToolsPlugin, type RegisteredUIPlugin, type RemotePlugin, type RenderMode, type ScannerAPI, SearchBar, type SessionSnapshot, type SharingAPI, type SpatialEvent, type SpatialIntent, type SpatialPoint, type SpatialSnapshot, type SpatialStateEvent, type StateEvent, type StateListener, type StateQuality, type StorageAPI, type Subscription, SurfaceCard, type SurfaceCardProps, SurfaceDispatcher, SurfaceDrawer, type SurfaceDrawerProps, SurfaceFullscreen, type SurfaceFullscreenProps, type SurfaceManifest, type SurfacePlacement, type SurfaceType, type SurfaceVisibility, TabBar, type ThemeTokens, type TrustedToolDeclaration, type VisionAPI, type VisionFrameQuality, type VisionLabel, type VisionObject, type VisionText, type VisualSnapshot, type VoiceAPI, type VoiceCaller, type VoiceDeviceToolCall, type VoiceDeviceToolReason, type VoiceEvent, type VoicePersona, type VoiceSessionProvider, type VoiceSnapshot, type VoiceStartOptions, type VoiceState, type VoiceSubscription, type VoiceTranscriptEntry, type WebViewCookie, __resetSessionSyncForTest, bundleManager, callDeviceTool, clearDeviceTools, createNativeBridge, deviceState as deviceStateV2, deviceToolsActionForSession, ensureDeviceToolsLoaded, explainCapability, gateDeviceState, gatePluginNative, gateVoice, getCameraPreviewProvider, getHaptics, getNativeBundleHash, getNativeBundleUrl, getNativeComponentId, getPlugin, getPluginNative, handleServerDeviceToolCall, hasDeviceTool, implementedStateDomains, implementedVoice, isActionable, isDeviceToolCommand, isInlineSurface, isMountSurface, listDeviceTools, listPlugins, nativePluginRegistry, neverObserved, pluginBundleUrl, register, registerBundleModules, registerDeviceTools, registerNativePlugin, registerPlatformDeviceTools, registerService, registerTools, resetDeviceToolLoader, resolvePluginGrants, resolvePluginRuntimeProps, resolvePluginUrl, setBundleStorage, setCameraPreviewProvider, setDeviceStateProvider, setDeviceToolContextProvider, setDeviceToolResultPoster, setHapticsProvider, setPluginNativeProvider, setPluginThemeColors, setVoiceProvider, supportsNative, supportsWebView, syncDeviceToolsForSession, unregisterDeviceTools, unregisterNativePlugin, useApi, useNativeBridge, usePlugin, usePluginTheme, useRemotePlugins, useSurfaceStore };
|