Files
callstack__agent-device/ios-runner/RUNNER_PROTOCOL.md
Aldo Ryanda 73dc7f882d feat(recording): align quality and max-size controls (#816)
* feat(recording): make iOS export quality configurable

Wire the existing recording-export-quality enum through the record command
down to the Swift export preset. Adds a `--export-quality <medium|high>`
option for iOS recordings that controls the AVAssetExportSession preset used
when a recording is re-encoded.

`medium` stays the default and selects AVAssetExportPresetMediumQuality, which
preserves the fast simulator-friendly export. `high` opts into
AVAssetExportPresetHighestQuality for evidence-grade output. This is separate
from the existing integer `--quality <5-10>` capture flag that scales render
resolution.

Closes #568

* fix(recording): apply export quality to touch-overlay export path

The --export-quality flag was only wired into the resize export path. The
touch-overlay re-encode (finalizeRecordingOverlay -> overlayRecordingTouches ->
recording-overlay.swift) ignored it and always picked AVAssetExportPresetMediumQuality,
so record stop with --export-quality high had no effect when the stop path
re-encodes only to burn in touch overlays.

Thread the recording's exportQuality through finalizeRecordingOverlay and
overlayRecordingTouches, pass it as --export-quality to recording-overlay.swift,
and resolve the preset there via the same exportPresetName() helper used by
recording-resize.swift. Medium stays the default when the arg is absent, so
behavior is unchanged for callers that do not set it.

* feat: align recording quality and size flags

---------

Co-authored-by: Michał Pierzchała <thymikee@gmail.com>
2026-06-19 18:44:38 +02:00

2.1 KiB

iOS Runner Protocol

The Apple runner speaks a small internal HTTP+JSON protocol between the TypeScript daemon and the XCUITest host. This protocol is a maintainer document, not part of the public user docs, but it should stay explicit so the TypeScript and Swift sides do not drift.

Transport

  • Endpoint: POST /command
  • Content type: application/json
  • Request body: one JSON command object
  • Response body: one JSON envelope

The daemon probes http://127.0.0.1:<port>/command for simulator and desktop flows, and can use a tunneled device address for physical iOS/tvOS devices before falling back to localhost.

Request Shape

Every request includes a command field. Additional fields depend on the command family.

Examples:

{ "command": "tap", "x": 120, "y": 240 }
{
  "command": "snapshot",
  "interactiveOnly": true,
  "depth": 2,
  "scope": "app",
  "raw": false
}
{ "command": "recordStart", "outPath": "/tmp/demo.mp4", "fps": 30, "maxSize": 720 }
{ "command": "rotate", "orientation": "landscape-left" }

The current command names are defined in:

Response Shape

Successful and failed responses use the same top-level envelope:

{
  "ok": true,
  "data": {
    "message": "ok"
  }
}
{
  "ok": false,
  "error": {
    "code": "UNSUPPORTED_OPERATION",
    "message": "Unable to dismiss the iOS keyboard without a native dismiss gesture or control"
  }
}

data is command-specific. Common fields include snapshot nodes, text lookup results, gesture timing, visibility metadata, and screenshot or recording output details.

Maintenance Rules

  • Treat the TypeScript and Swift wire models as a single contract.
  • When adding, removing, or renaming a command, update the protocol fixtures/tests in the same change.
  • Keep this file focused on the actual wire shape rather than implementation details of command execution.