426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
234 lines
10 KiB
Markdown
234 lines
10 KiB
Markdown
# iOS Computer-Use — Honest Scope
|
|
|
|
Apple does not let third-party apps drive other apps. There is no equivalent
|
|
to Android's `MediaProjection` for full-screen capture without a user prompt,
|
|
no `ScreenCaptureKit` (that is macOS only), and no system-wide accessibility
|
|
API that a sandboxed iOS app can use to inject events into other apps.
|
|
|
|
This document spells out what *is* possible on iOS, exactly how each surface
|
|
behaves, and the manual validation checklist that has to be run on a real
|
|
device before any of this ships.
|
|
|
|
## What works
|
|
|
|
### 1. ReplayKit foreground capture (own app)
|
|
|
|
`RPScreenRecorder.shared().startCapture(handler:completionHandler:)` returns
|
|
`CMSampleBuffer`s of *the host app's* window. Useful for "show me what's on
|
|
the Eliza screen right now."
|
|
|
|
```swift
|
|
RPScreenRecorder.shared().startCapture(handler: { sampleBuffer, type, error in
|
|
// Receive frames for own-app capture only.
|
|
}, completionHandler: { error in
|
|
// Setup result; ReplayKit shows a system permission prompt on first use.
|
|
})
|
|
```
|
|
|
|
Constraints:
|
|
|
|
- Frame delivery rate is the display refresh rate; the bridge throttles to a
|
|
caller-supplied `frameRate` (default 1Hz).
|
|
- Hard cap of 30 seconds per session. Past that, the work has to move to a
|
|
`BGProcessingTask`.
|
|
|
|
### 2. ReplayKit broadcast extension (system-wide capture)
|
|
|
|
A separate target with a ~50MB memory ceiling. Streams frames over an App
|
|
Group shared container into the main app. The user must start the broadcast
|
|
themselves via the system share-sheet picker — apps cannot programmatically
|
|
launch the extension.
|
|
|
|
```swift
|
|
override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer,
|
|
with sampleBufferType: RPSampleBufferType) {
|
|
// Compress + dump into the App Group container.
|
|
}
|
|
```
|
|
|
|
**iOS 26 / 26.1 beta regression**: extensions are killed within ~3 seconds
|
|
even when memory headroom is fine. We surface this as
|
|
`extension_died` from `broadcastExtensionHandshake` so callers can fall back
|
|
to foreground capture. Track Apple's feedback status before shipping this
|
|
target on iOS 26.
|
|
|
|
### 3. Apple Vision OCR
|
|
|
|
`VNRecognizeTextRequest` is on-device, free, supports ~30 languages, and
|
|
typically runs sub-300ms on modern devices.
|
|
|
|
```swift
|
|
let request = VNRecognizeTextRequest { request, error in
|
|
let observations = request.results as? [VNRecognizedTextObservation]
|
|
// Top candidate per observation; each has bounding box, text, confidence.
|
|
}
|
|
request.recognitionLevel = .accurate
|
|
request.recognitionLanguages = ["en-US"]
|
|
try VNImageRequestHandler(cgImage: cg).perform([request])
|
|
```
|
|
|
|
This is the OCR provider the WS6 scene-builder will pick up on iOS. The
|
|
provider interface is in
|
|
`eliza/plugins/plugin-computeruse/src/mobile/ocr-provider.ts`.
|
|
|
|
### 4. App Intents
|
|
|
|
The only sanctioned way to drive other apps. Each target app must expose
|
|
intents (Shortcuts-style). We support invocation via x-callback URL schemes
|
|
for the system apps in the static registry
|
|
(`ios-app-intent-registry.ts`):
|
|
|
|
- Mail — `mailto:` with `subject` / `body` / `cc` / `bcc`
|
|
- Messages — `sms:` with `body`
|
|
- Maps — `http://maps.apple.com/?daddr=...&dirflg=...`
|
|
- Safari — open URL
|
|
|
|
For richer intents (Notes append, Reminders add, Music play with a query)
|
|
the user has to donate the action via Shortcuts; we can then invoke via
|
|
`AppIntent` on iOS 16+. The bridge's `appIntentList` returns the runtime
|
|
list of donated intents this app sees.
|
|
|
|
### 5. UIAccessibility (own-app reading only)
|
|
|
|
`accessibilitySnapshot` walks the key window's view hierarchy and returns
|
|
`accessibilityLabel` / `accessibilityValue` / role. iOS gives us no way to
|
|
read another app's UIAccessibility tree.
|
|
|
|
### 6. Apple Foundation Models (iOS 26+)
|
|
|
|
Apple ships an on-device LLM under the `FoundationModels` framework when
|
|
Apple Intelligence is enabled. We expose this as an *opportunistic*
|
|
fast-path. If unavailable, the existing llama-cpp-capacitor local Eliza-1
|
|
vision path stays as the default.
|
|
|
|
Entitlement and Info.plist updates are listed below.
|
|
|
|
## What does NOT work
|
|
|
|
Stock iOS does not allow any of the following from a third-party app, and
|
|
we do not pretend otherwise:
|
|
|
|
- **Driving other apps' UI**. No cross-app input synthesis, no
|
|
`MediaProjection` / `ScreenCaptureKit` equivalent, no system-wide
|
|
accessibility event injection.
|
|
- **Listing other running apps' processes**. The kernel hides this from
|
|
third-party apps; there is no `ps`-equivalent.
|
|
- **Persistent background inference past ~30s**, except via
|
|
`BGProcessingTask` (opportunistic, OS-scheduled, not guaranteed).
|
|
|
|
If a feature spec calls for any of the above, escalate it as not feasible
|
|
on iOS rather than inventing a workaround that will get the app rejected
|
|
or silently broken by Apple in the next OS update.
|
|
|
|
## Entitlements + Info.plist
|
|
|
|
Add the following to `apps/app/ios/App/App/App.entitlements`:
|
|
|
|
```xml
|
|
<key>com.apple.developer.kernel.increased-memory-limit</key>
|
|
<true/>
|
|
<key>com.apple.developer.kernel.extended-virtual-addressing</key>
|
|
<true/>
|
|
```
|
|
|
|
Add the following to `apps/app/ios/App/App/Info.plist`:
|
|
|
|
```xml
|
|
<key>NSScreenCaptureDescription</key>
|
|
<string>Captures the Eliza app window when you ask it to see the screen.</string>
|
|
<key>NSAppleEventsUsageDescription</key>
|
|
<string>Sends Shortcuts to other apps when you authorize an action.</string>
|
|
```
|
|
|
|
The screen capture string is shown on ReplayKit's first-launch system
|
|
prompt. The Apple Events string is shown only if the host (this app) ever
|
|
needs to drive AppleScript via the Catalyst surface; iOS apps that only
|
|
use `UIApplication.shared.open(url:)` to invoke x-callback intents do not
|
|
need it, but it costs nothing to include for forward compatibility with
|
|
Mac Catalyst builds.
|
|
|
|
## Validation checklist
|
|
|
|
The iOS bridge contract is covered by TypeScript tests, but physical iOS
|
|
device behavior is not proven by this repository alone. The required evidence
|
|
manifest is `docs/ios-device-validation.json`; keep it in
|
|
`requires_device_evidence` until a real device run records device/build
|
|
metadata, artifacts, and per-method results. Release gates that require
|
|
physical proof should run:
|
|
|
|
```bash
|
|
bun run --cwd plugins/plugin-computeruse validate:ios-device-evidence -- --require-complete
|
|
```
|
|
|
|
Without `--require-complete`, the same command validates that the manifest
|
|
still tracks every required method and evidence field. Before shipping any
|
|
iOS bridge release, complete the manifest for:
|
|
|
|
### Simulator vs device
|
|
|
|
| Surface | Simulator | Real device |
|
|
| -------------------------------- | --------- | ----------- |
|
|
| ReplayKit foreground capture | Partial | Required |
|
|
| Broadcast extension | No | Required |
|
|
| Vision OCR | Yes | Required |
|
|
| App Intents (x-callback) | Partial | Required |
|
|
| Accessibility snapshot | Yes | Required |
|
|
| Foundation Models | No | Required |
|
|
| Memory pressure probe | Limited | Required |
|
|
|
|
### Per-method checklist
|
|
|
|
For each method below, run on at least one A14-or-later iPhone running the
|
|
target iOS (currently iOS 26.1, with iOS 17.6 as the floor):
|
|
|
|
1. **`probe()`** — assert `data.platform === "ios"`, `osVersion` matches the
|
|
device, all six capability bits are present.
|
|
2. **`replayKitForegroundStart`** — assert the system permission prompt is
|
|
shown on first use; on accept, `replayKitForegroundDrain` returns frames
|
|
with non-empty `jpegBase64` and the device's screen resolution.
|
|
3. **`broadcastExtensionHandshake`** — bundle the extension target via Xcode,
|
|
tap the share-sheet broadcast picker, and assert `broadcastActive`
|
|
transitions to `true`. On iOS 26 betas, verify `regression.observed`
|
|
eventually flips to `true` after the extension dies.
|
|
4. **`visionOcr`** — render a known PNG with the string `"WS9 OCR SMOKE"`
|
|
into the app, run OCR with `recognitionLevel: "accurate"`, and assert
|
|
`fullText` contains the source string (case-insensitive).
|
|
5. **`appIntentList`** — verify the returned list is empty for a fresh
|
|
install and grows after the user donates intents via Shortcuts.
|
|
6. **`appIntentInvoke`** with `com.apple.mobilesafari.open-url` — assert
|
|
Safari opens to the provided URL. With `com.apple.MobileSMS.send-message`
|
|
— assert Messages opens with the recipient and body pre-filled.
|
|
7. **`accessibilitySnapshot`** — assert the returned tree's top-level node
|
|
has `role !== "labeled"` and `children.length > 0` on a populated screen.
|
|
8. **`foundationModelGenerate`** — on a device with Apple Intelligence
|
|
enabled, assert a short prompt returns non-empty text. With AI disabled,
|
|
assert the call resolves with `foundation_model_unavailable`.
|
|
9. **`memoryPressureProbe`** — invoke `UIApplication.shared.performMemoryWarning()`
|
|
(debug-only) and assert the next probe call returns
|
|
`severity >= 0.7` with `lastWarningAt` set.
|
|
|
|
## Hand-off to other workstreams
|
|
|
|
- **WS1 (memory-pressure arbiter)** consumes `MemoryPressureSample` via the
|
|
shared `IPressureSignal` contract in `ios-bridge.ts`. The bridge is the
|
|
producer; the arbiter is the consumer. WS1 will subscribe to push events
|
|
via the bridge once that channel lands; for now it polls `memoryPressureProbe`.
|
|
- **WS6 (scene-builder OCR)** picks up the iOS Vision provider through
|
|
`selectOcrProvider()` in `ocr-provider.ts`. Register the provider at app
|
|
boot when running on iOS:
|
|
```ts
|
|
registerOcrProvider(
|
|
createIosVisionOcrProvider(() => Capacitor.Plugins.ComputerUse),
|
|
);
|
|
```
|
|
|
|
## Apple documentation references
|
|
|
|
- ReplayKit — https://developer.apple.com/documentation/replaykit
|
|
- Vision Text Recognition — https://developer.apple.com/documentation/vision/recognizing_text_in_images
|
|
- App Intents — https://developer.apple.com/documentation/appintents
|
|
- Foundation Models — https://developer.apple.com/documentation/foundationmodels (iOS 26)
|
|
- App Groups — https://developer.apple.com/documentation/xcode/configuring-app-groups
|
|
- `os_proc_available_memory()` — https://developer.apple.com/documentation/foundation/process_info/3743117-os_proc_available_memory
|
|
- Background Tasks — https://developer.apple.com/documentation/backgroundtasks
|