@mobileaidev/ai-app-bridge 0.2.15 → 0.3.0-rc.2

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.
Files changed (156) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +7 -0
  3. package/README.md +306 -53
  4. package/bin/ai-app-bridge.js +51 -4504
  5. package/bin/android-permissions.js +152 -0
  6. package/bin/android-uia-xml.js +74 -0
  7. package/bin/artifact-paths.js +127 -1
  8. package/bin/bridge-forward.js +56 -0
  9. package/bin/command-discovery.js +86 -0
  10. package/bin/command-errors.js +60 -0
  11. package/bin/command-registry.js +504 -0
  12. package/bin/command-request.js +14 -0
  13. package/bin/command-router.js +80 -0
  14. package/bin/connection-cache.js +119 -0
  15. package/bin/device-provider.js +2934 -0
  16. package/bin/execution-host.js +596 -0
  17. package/bin/execution-runtime.js +101 -0
  18. package/bin/fact-codec.js +321 -0
  19. package/bin/fact-recorder.js +691 -0
  20. package/bin/fact-store.js +226 -0
  21. package/bin/feedback-probe.js +285 -0
  22. package/bin/intent/install-intent.js +267 -0
  23. package/bin/intent/intent-action-executor.js +129 -0
  24. package/bin/intent/intent-autonomous-adapter.js +46 -0
  25. package/bin/intent/intent-capture-port.js +64 -0
  26. package/bin/intent/intent-entry.js +226 -0
  27. package/bin/intent/intent-errors.js +28 -0
  28. package/bin/intent/intent-evidence-store.js +121 -0
  29. package/bin/intent/intent-lifetime.js +62 -0
  30. package/bin/intent/intent-observation-target.js +33 -0
  31. package/bin/intent/intent-observer.js +194 -0
  32. package/bin/intent/intent-production-adapter.js +334 -0
  33. package/bin/intent/intent-provider.js +27 -0
  34. package/bin/intent/intent-runtime.js +63 -0
  35. package/bin/intent/intent-worker.js +432 -0
  36. package/bin/intent/ios-intent-adapter.js +90 -0
  37. package/bin/intent/permission-intent.js +239 -0
  38. package/bin/intent/web-intent-adapter.js +31 -0
  39. package/bin/ios-device-outcome.js +47 -0
  40. package/bin/ios-execution.js +108 -0
  41. package/bin/ios-provider.js +497 -583
  42. package/bin/ios-runtime-binding.js +54 -0
  43. package/bin/ios-wda-execution.js +70 -0
  44. package/bin/ios-wda-port.js +98 -0
  45. package/bin/ios-wda-project.js +79 -0
  46. package/bin/mcp-server.js +83 -1040
  47. package/bin/mmap-scan-index.js +329 -0
  48. package/bin/observation-collector.js +862 -0
  49. package/bin/runtime-client.js +154 -0
  50. package/bin/runtime-directory.js +107 -0
  51. package/bin/runtime-protocol.js +36 -0
  52. package/bin/script/bounded-script-registry.js +103 -0
  53. package/bin/script/node-runtime-adapter.js +233 -0
  54. package/bin/script/progress-projector.js +63 -0
  55. package/bin/script/python-runtime-adapter.js +111 -0
  56. package/bin/script/rolling-summary.js +134 -0
  57. package/bin/script/script-agent-port.js +40 -0
  58. package/bin/script/script-assert.js +95 -0
  59. package/bin/script/script-capture-port.js +76 -0
  60. package/bin/script/script-catalog.js +85 -0
  61. package/bin/script/script-durable-restore.js +195 -0
  62. package/bin/script/script-entry-code.js +26 -0
  63. package/bin/script/script-entry-route.js +38 -0
  64. package/bin/script/script-entry.js +3 -0
  65. package/bin/script/script-errors.js +29 -0
  66. package/bin/script/script-evidence-store.js +22 -0
  67. package/bin/script/script-format-removed.js +26 -0
  68. package/bin/script/script-host-port.js +397 -0
  69. package/bin/script/script-ledger.js +64 -0
  70. package/bin/script/script-result.js +57 -0
  71. package/bin/script/script-sdk.js +152 -0
  72. package/bin/script/script-sdk.py +153 -0
  73. package/bin/script/script-session-channel.js +127 -0
  74. package/bin/script/script-spec.js +86 -0
  75. package/bin/script/script-supervisor.js +919 -0
  76. package/bin/script/templates/checkpoint-reentry.js +13 -0
  77. package/bin/segment-index.js +481 -0
  78. package/bin/segmented-fact-store.js +1571 -0
  79. package/bin/shared-kernel/android-h5-target.js +10 -0
  80. package/bin/shared-kernel/android-install-execution.js +176 -0
  81. package/bin/shared-kernel/android-sdk-endpoint.js +42 -0
  82. package/bin/shared-kernel/android-shell-execution.js +195 -0
  83. package/bin/shared-kernel/argument-schema.js +117 -0
  84. package/bin/shared-kernel/canonical-path.js +17 -0
  85. package/bin/shared-kernel/device-acknowledgements.js +53 -0
  86. package/bin/shared-kernel/device-completion-history.js +52 -0
  87. package/bin/shared-kernel/device-mutation-lease.js +219 -0
  88. package/bin/shared-kernel/device-ownership-recovery.js +95 -0
  89. package/bin/shared-kernel/device-ownership-store.js +94 -0
  90. package/bin/shared-kernel/evidence-adapters.js +251 -0
  91. package/bin/shared-kernel/evidence-archive.js +329 -0
  92. package/bin/shared-kernel/evidence-recording.js +131 -0
  93. package/bin/shared-kernel/evidence-schema.js +194 -0
  94. package/bin/shared-kernel/evidence-store.js +190 -0
  95. package/bin/shared-kernel/execution-admission.js +22 -0
  96. package/bin/shared-kernel/execution-contracts.js +171 -0
  97. package/bin/shared-kernel/execution-io.js +106 -0
  98. package/bin/shared-kernel/execution-ledger.js +125 -0
  99. package/bin/shared-kernel/execution-scope.js +87 -0
  100. package/bin/shared-kernel/execution-target.js +115 -0
  101. package/bin/shared-kernel/flutter-execution.js +13 -0
  102. package/bin/shared-kernel/flutter-h5-port.js +60 -0
  103. package/bin/shared-kernel/flutter-h5-target.js +9 -0
  104. package/bin/shared-kernel/flutter-target.js +75 -0
  105. package/bin/shared-kernel/h5-execution.js +11 -0
  106. package/bin/shared-kernel/h5-target.js +31 -0
  107. package/bin/shared-kernel/host-fact-store.js +49 -0
  108. package/bin/shared-kernel/ios-h5-target.js +9 -0
  109. package/bin/shared-kernel/ios-native-target.js +71 -0
  110. package/bin/shared-kernel/live-capture-query.js +115 -0
  111. package/bin/shared-kernel/managed-sdk-execution.js +78 -0
  112. package/bin/shared-kernel/native-execution.js +13 -0
  113. package/bin/shared-kernel/native-target.js +156 -0
  114. package/bin/shared-kernel/provider-command-contracts.js +55 -0
  115. package/bin/shared-kernel/recorded-payload-archive.js +195 -0
  116. package/bin/shared-kernel/request-context.js +35 -0
  117. package/bin/shared-kernel/semantic-node.js +55 -0
  118. package/bin/shared-kernel/summary-transformer.js +352 -0
  119. package/bin/shared-kernel/target-lease-protocol.js +47 -0
  120. package/bin/shared-kernel/text-wait.js +111 -0
  121. package/bin/shared-kernel/uia-execution.js +96 -0
  122. package/bin/shared-kernel/uia-protocol.js +214 -0
  123. package/bin/shared-kernel/uia-runtime-port.js +377 -0
  124. package/bin/shared-kernel/uia-target.js +39 -0
  125. package/bin/shared-kernel/web-dom-target.js +44 -0
  126. package/bin/shared-kernel/xml-attributes.js +25 -0
  127. package/bin/target-execution.js +275 -0
  128. package/bin/web/command-schema.js +60 -0
  129. package/bin/web/session-store.js +157 -0
  130. package/bin/web-provider.js +334 -553
  131. package/docs/COMMAND_CONTRACT.md +1563 -0
  132. package/docs/EVIDENCE_ARCHIVE.md +214 -0
  133. package/docs/INTENT_FOREGROUND.md +71 -0
  134. package/docs/INTENT_NATIVE_EDITING.md +79 -0
  135. package/docs/RELEASE.md +59 -0
  136. package/docs/SCRIPT_AUTHORING.md +489 -0
  137. package/node_modules/@mobileaidev/segmented-fact-store-native/LICENSE +201 -0
  138. package/node_modules/@mobileaidev/segmented-fact-store-native/NOTICE +7 -0
  139. package/node_modules/@mobileaidev/segmented-fact-store-native/binding.gyp +36 -0
  140. package/node_modules/@mobileaidev/segmented-fact-store-native/bindings/node/sfs_node.c +597 -0
  141. package/node_modules/@mobileaidev/segmented-fact-store-native/include/sfs.h +178 -0
  142. package/node_modules/@mobileaidev/segmented-fact-store-native/index.js +5 -0
  143. package/node_modules/@mobileaidev/segmented-fact-store-native/package.json +24 -0
  144. package/node_modules/@mobileaidev/segmented-fact-store-native/src/sfs.c +2349 -0
  145. package/package.json +60 -5
  146. package/runtime/ios-wda/AABWDABinding.h +19 -0
  147. package/runtime/ios-wda/AABWDABinding.m +97 -0
  148. package/runtime/ios-wda/AABWDAExecution.h +26 -0
  149. package/runtime/ios-wda/AABWDAExecution.m +172 -0
  150. package/runtime/ios-wda/AABWDAIntegration.h +71 -0
  151. package/runtime/ios-wda/AABWDAManagedRoutes.h +392 -0
  152. package/runtime/ios-wda/AABWDAReceiptStore.h +10 -0
  153. package/runtime/ios-wda/AABWDAReceiptStore.m +116 -0
  154. package/runtime/uia/ai-app-bridge-uia.jar +0 -0
  155. package/runtime/uia/manifest.json +22 -0
  156. package/skills/ai-app-bridge-use/SKILL.md +23 -360
@@ -0,0 +1,329 @@
1
+ 'use strict';
2
+
3
+ const PARTITION_ALL = 0xffff_ffff;
4
+
5
+ /**
6
+ * Bounded-memory correctness projection over the authoritative mmap engine.
7
+ *
8
+ * When `base` is present it remains a sealed SQLite prefix and only records
9
+ * after `baseHighWater` are scanned from mmap. Without a base, every query is
10
+ * served directly from live mmap records. No fact payload is copied into a
11
+ * long-lived JavaScript collection.
12
+ */
13
+ class MmapScanIndex {
14
+ constructor({
15
+ engine,
16
+ decodeFact,
17
+ base = null,
18
+ baseHighWater = 0,
19
+ reason = null,
20
+ budgetBytes = 1024 * 1024 * 1024,
21
+ }) {
22
+ if (!engine || typeof engine.scan !== 'function' || typeof engine.status !== 'function') {
23
+ throw new TypeError('MmapScanIndex requires an mmap engine');
24
+ }
25
+ if (typeof decodeFact !== 'function') throw new TypeError('MmapScanIndex requires decodeFact');
26
+ this.engine = engine;
27
+ this.decodeFact = decodeFact;
28
+ this.base = base;
29
+ this.baseHighWater = nonNegativeInteger(baseHighWater, 'baseHighWater');
30
+ this.reason = reason;
31
+ this.budgetBytes = positiveInteger(budgetBytes, 'budgetBytes');
32
+ this.metadataValues = new Map();
33
+ this.closed = false;
34
+ }
35
+
36
+ insert(fact, location) {
37
+ this.assertOpen();
38
+ return indexedLocation(fact, location);
39
+ }
40
+
41
+ insertBatch(records) {
42
+ this.assertOpen();
43
+ if (!Array.isArray(records)) throw new TypeError('records must be an array');
44
+ }
45
+
46
+ query(query = {}) {
47
+ this.assertOpen();
48
+ const normalized = normalizeQuery(query);
49
+ const baseResult = this.base
50
+ ? this.base.query(indexQuery(normalized))
51
+ : { items: [], hasMore: false };
52
+ const tail = this.scanMatching({
53
+ ...normalized,
54
+ afterSequence: Math.max(normalized.afterSequence, this.baseHighWater),
55
+ });
56
+ const merged = new Map();
57
+ for (const item of [...baseResult.items, ...tail.items]) merged.set(item.sequence, item);
58
+ const ordered = [...merged.values()].sort((left, right) => left.sequence - right.sequence);
59
+ return {
60
+ items: ordered.slice(0, normalized.limit),
61
+ hasMore: baseResult.hasMore || tail.hasMore || ordered.length > normalized.limit,
62
+ };
63
+ }
64
+
65
+ findDedupe(dedupeKey) {
66
+ this.assertOpen();
67
+ if (dedupeKey === undefined || dedupeKey === null) return null;
68
+ const normalized = String(dedupeKey);
69
+ const fromBase = this.base?.findDedupe(normalized);
70
+ if (fromBase) return fromBase;
71
+ let cursor = { partitionId: PARTITION_ALL, afterSequence: this.baseHighWater };
72
+ while (true) {
73
+ const scanned = this.engine.scan(cursor);
74
+ cursor = scanned.cursor;
75
+ if (scanned.done) return null;
76
+ const fact = this.decodeFact(scanned.payload, scanned.record.sequence);
77
+ if (!fact) continue;
78
+ if (fact.dedupeKey === normalized) return locationFrom(fact, scanned.record);
79
+ }
80
+ }
81
+
82
+ locationForSequence(sequence) {
83
+ this.assertOpen();
84
+ const normalized = positiveInteger(sequence, 'sequence');
85
+ if (this.base && normalized <= this.baseHighWater) {
86
+ const fromBase = this.base.locationForSequence(normalized);
87
+ if (fromBase) return fromBase;
88
+ }
89
+ let cursor = { partitionId: PARTITION_ALL, afterSequence: normalized - 1 };
90
+ while (true) {
91
+ const scanned = this.engine.scan(cursor);
92
+ cursor = scanned.cursor;
93
+ if (scanned.done || scanned.record.sequence > normalized) return null;
94
+ if (scanned.record.sequence === normalized) {
95
+ const fact = this.decodeFact(scanned.payload, scanned.record.sequence);
96
+ return fact ? locationFrom(fact, scanned.record) : null;
97
+ }
98
+ }
99
+ }
100
+
101
+ maxSequence() {
102
+ this.assertOpen();
103
+ return this.logicalStatus().maxSequence;
104
+ }
105
+
106
+ metadata(key, createValue) {
107
+ this.assertOpen();
108
+ const normalized = String(key);
109
+ if (this.metadataValues.has(normalized)) return this.metadataValues.get(normalized);
110
+ const fromBase = this.base?.metadata(normalized);
111
+ if (fromBase !== undefined && fromBase !== null) {
112
+ this.metadataValues.set(normalized, fromBase);
113
+ return fromBase;
114
+ }
115
+ if (typeof createValue !== 'function') return null;
116
+ const value = String(createValue());
117
+ this.metadataValues.set(normalized, value);
118
+ return value;
119
+ }
120
+
121
+ setMetadata(key, value) {
122
+ this.assertOpen();
123
+ this.metadataValues.set(String(key), String(value));
124
+ }
125
+
126
+ prunePartitionBefore(partition, firstAvailableSequence) {
127
+ this.assertOpen();
128
+ return this.base?.prunePartitionBefore(partition, firstAvailableSequence) || 0;
129
+ }
130
+
131
+ clear() {
132
+ this.assertOpen();
133
+ this.base?.clear();
134
+ this.baseHighWater = 0;
135
+ }
136
+
137
+ reclaim(options = {}) {
138
+ this.assertOpen();
139
+ return this.base?.reclaim(options)
140
+ || { beforeBytes: 0, afterBytes: 0, reclaimedBytes: 0, autoVacuum: null };
141
+ }
142
+
143
+ storageBytes() {
144
+ this.assertOpen();
145
+ return this.base?.storageBytes() || 0;
146
+ }
147
+
148
+ status() {
149
+ if (this.closed) return { ...this.lastStatus, closed: true };
150
+ const logical = this.logicalStatus();
151
+ const base = this.base?.status() || null;
152
+ return {
153
+ ok: true,
154
+ adapter: this.base ? 'sqlite-projection+mmap-tail' : 'mmap-scan-projection',
155
+ authoritative: false,
156
+ rebuildable: true,
157
+ persistence: Boolean(this.base),
158
+ degraded: true,
159
+ boundedMemory: true,
160
+ reason: this.reason,
161
+ count: logical.count,
162
+ maxSequence: logical.maxSequence,
163
+ baseHighWater: this.baseHighWater,
164
+ tailFirstSequence: this.baseHighWater + 1,
165
+ bytes: Number(base?.bytes || 0),
166
+ totalBytes: Number(base?.totalBytes || 0),
167
+ budgetBytes: this.budgetBytes,
168
+ overQuota: Number(base?.totalBytes || 0) > this.budgetBytes,
169
+ quotaScope: 'host-sqlite-projection',
170
+ base,
171
+ closed: false,
172
+ };
173
+ }
174
+
175
+ close() {
176
+ if (this.closed) return;
177
+ let firstError = null;
178
+ try {
179
+ this.lastStatus = this.status();
180
+ } catch (error) {
181
+ firstError = error;
182
+ }
183
+ try {
184
+ this.base?.close();
185
+ } catch (error) {
186
+ if (!firstError) firstError = error;
187
+ }
188
+ this.closed = true;
189
+ if (firstError) throw firstError;
190
+ }
191
+
192
+ scanMatching(normalized) {
193
+ const items = [];
194
+ let cursor = { partitionId: PARTITION_ALL, afterSequence: normalized.afterSequence };
195
+ while (true) {
196
+ const scanned = this.engine.scan(cursor);
197
+ cursor = scanned.cursor;
198
+ if (scanned.done) break;
199
+ const fact = this.decodeFact(scanned.payload, scanned.record.sequence);
200
+ if (!fact) continue;
201
+ if (!matches(fact, normalized)) continue;
202
+ items.push(locationFrom(fact, scanned.record));
203
+ if (items.length > normalized.limit) break;
204
+ }
205
+ return {
206
+ items: items.slice(0, normalized.limit),
207
+ hasMore: items.length > normalized.limit,
208
+ };
209
+ }
210
+
211
+ logicalStatus() {
212
+ let count = 0;
213
+ let maxSequence = 0;
214
+ let cursor = { partitionId: PARTITION_ALL, afterSequence: 0 };
215
+ while (true) {
216
+ const scanned = this.engine.scan(cursor);
217
+ cursor = scanned.cursor;
218
+ if (scanned.done) break;
219
+ const fact = this.decodeFact(scanned.payload, scanned.record.sequence);
220
+ if (!fact) continue;
221
+ count += 1;
222
+ maxSequence = scanned.record.sequence;
223
+ }
224
+ return { count, maxSequence };
225
+ }
226
+
227
+ assertOpen() {
228
+ if (this.closed) throw new Error('MmapScanIndex is closed');
229
+ }
230
+ }
231
+
232
+ function normalizeQuery(query) {
233
+ if (!query || typeof query !== 'object' || Array.isArray(query)) throw new TypeError('query must be an object');
234
+ const limit = query.limit === undefined ? 100 : positiveInteger(query.limit, 'limit');
235
+ if (limit > 1000) throw new TypeError('limit must not exceed 1000');
236
+ return {
237
+ afterSequence: query.afterSequence === undefined
238
+ ? 0
239
+ : nonNegativeInteger(query.afterSequence, 'afterSequence'),
240
+ limit,
241
+ targetKey: nullableString(query.targetKey),
242
+ partitions: query.partitions === undefined
243
+ ? []
244
+ : [...new Set((Array.isArray(query.partitions) ? query.partitions : [query.partitions]).map(String))],
245
+ runtimeEpoch: nullableString(query.runtimeEpoch),
246
+ hasActionId: Object.prototype.hasOwnProperty.call(query, 'actionId'),
247
+ actionId: nullableString(query.actionId),
248
+ fromObservedAtMs: nullableInteger(query.fromObservedAtMs, 'fromObservedAtMs'),
249
+ toObservedAtMs: nullableInteger(query.toObservedAtMs, 'toObservedAtMs'),
250
+ };
251
+ }
252
+
253
+ function indexQuery(query) {
254
+ return {
255
+ afterSequence: query.afterSequence,
256
+ limit: query.limit,
257
+ targetKey: query.targetKey,
258
+ partitions: query.partitions,
259
+ runtimeEpoch: query.runtimeEpoch,
260
+ ...(query.hasActionId ? { actionId: query.actionId } : {}),
261
+ fromObservedAtMs: query.fromObservedAtMs,
262
+ toObservedAtMs: query.toObservedAtMs,
263
+ };
264
+ }
265
+
266
+ function matches(fact, query) {
267
+ if (query.targetKey !== null && fact.targetKey !== query.targetKey) return false;
268
+ if (query.partitions.length > 0 && !query.partitions.includes(fact.partition)) return false;
269
+ if (query.runtimeEpoch !== null && fact.runtimeEpoch !== query.runtimeEpoch) return false;
270
+ if (query.hasActionId && (fact.actionId ?? null) !== query.actionId) return false;
271
+ const observedAtMs = Number(fact.timestamps?.observedAtMs);
272
+ if (query.fromObservedAtMs !== null && observedAtMs < query.fromObservedAtMs) return false;
273
+ if (query.toObservedAtMs !== null && observedAtMs > query.toObservedAtMs) return false;
274
+ return true;
275
+ }
276
+
277
+ function indexedLocation(fact, location) {
278
+ return {
279
+ sequence: Number(location.sequence),
280
+ partition: String(fact.partition),
281
+ targetKey: String(fact.targetKey),
282
+ runtimeEpoch: String(fact.runtimeEpoch),
283
+ actionId: fact.actionId ?? null,
284
+ dedupeKey: fact.dedupeKey ?? null,
285
+ timestamps: { ...fact.timestamps },
286
+ segmentId: Number(location.segmentId),
287
+ frameOffset: Number(location.frameOffset),
288
+ payloadLength: logicalPayloadLength(fact, location.payloadLength),
289
+ };
290
+ }
291
+
292
+ function logicalPayloadLength(fact, fallback) {
293
+ const value = Number(fact?.__aiAppBridgeLogicalPayloadBytes);
294
+ return Number.isSafeInteger(value) && value > 0 ? value : Number(fallback);
295
+ }
296
+
297
+ function locationFrom(fact, record) {
298
+ return indexedLocation(fact, {
299
+ sequence: record.sequence,
300
+ segmentId: record.segmentId,
301
+ frameOffset: record.frameOffset,
302
+ payloadLength: record.payloadLength,
303
+ });
304
+ }
305
+
306
+ function positiveInteger(value, name) {
307
+ const number = Number(value);
308
+ if (!Number.isSafeInteger(number) || number <= 0) throw new TypeError(`${name} must be a positive safe integer`);
309
+ return number;
310
+ }
311
+
312
+ function nonNegativeInteger(value, name) {
313
+ const number = Number(value);
314
+ if (!Number.isSafeInteger(number) || number < 0) throw new TypeError(`${name} must be a non-negative safe integer`);
315
+ return number;
316
+ }
317
+
318
+ function nullableString(value) {
319
+ return value === undefined || value === null ? null : String(value);
320
+ }
321
+
322
+ function nullableInteger(value, name) {
323
+ if (value === undefined || value === null) return null;
324
+ const number = Number(value);
325
+ if (!Number.isSafeInteger(number)) throw new TypeError(`${name} must be a safe integer`);
326
+ return number;
327
+ }
328
+
329
+ module.exports = { MmapScanIndex };