native macOS codings agent orchestrator prowl.onev.cat
README.md

Prowl Mirror for Android #

Experimental native Kotlin / Jetpack Compose text mirror client. One application supports Android phones and tablets (Android 8.0 / API 26 or later). At 840 dp available width the pane drawer becomes a persistent sidebar. Both layouts share session, protocol and rendering code; rotation is supported, not locked.

The Android project lives in Mirror/Android/ in the Prowl repository. The original Gradle project name and application ID are preserved.

Connect #

Use a Prowl Host built from the same revision (or a matching Prowl release), with PROWL_REMOTE_MIRROR=1. On Mac: Start Host → Add a Device. On Android: Add Remote Pane → enter IP/port and the two halves of the single-use, 60-second code. The default port is 7880. After enrollment, credentials are encrypted using Android Keystore and the short code is discarded. Pick a saved Host and leave code blank on subsequent connections. If the first runtime connection fails after enrollment, Retry uses the saved device credential without another code.

Select an open pane, or New Agent Pane → workspace → available Host Agent Profile → optional initial message. Models and permissions come from that Profile. There is no private AI console or dependency on personal shell wrappers.

  • Live text replaces the last viewport snapshot; it is not an append-only transcript.
  • Markdown emphasis/inline code, code fences and tables have readable previews; expanding code/table freezes the detail and supports copying the original text.
  • Multiline input sends only on Send or two physical Returns within 350 ms. IME composition disables Send. Host checks Agent readiness through shared interactive input; shell Send requires an explicit Host capability. Current Hosts refuse it because they cannot verify an empty command line; the draft remains available.
  • An unconfirmed delivery preserves the draft and blocks accidental resending. Check receipt only queries the original request; it never repeats input.
  • Mirror, Retry and foreground reconnection use ifFree; Take Over is explicit. If another client claims a free pane before Mirror completes, Android reports it busy.
  • History is a bounded frozen snapshot with earlier-page loading. Returning to Live keeps receiving current output. Closing a mirror never stops the Host program.
  • Hosts with remote-scroll show Scroll up / Scroll down above the Live text. Only these buttons send remote scroll input; swipes, edge pulls, selection and History remain local. Follow latest stays below the text and follows new frames. These actions scroll the actual Host pane, including a terminal application's own scroll handling. Host targets its pane height minus three rows (at least one row), with exact native scrollback movement and equivalent precision wheel input for applications. Internal Host profiles adjust known Agent wheel steps (Codex: three rows per event, reserving eight composer/status rows). Host precision-scroll settings and the application can affect the distance. One request runs at a time with a loading indicator; an unchanged screen also completes normally. Requests time out after five seconds and are never replayed after reconnecting. Hosts with scroll-state-v1 can confirm the top/bottom boundary: the matching button is disabled until a newer frame permits scrolling again. Unknown bounds, including applications that manage their own scrolling, stay enabled; unchanged text alone never disables a direction. Older Hosts need no boundary metadata. Both directions resume reading at the top of the returned screen. Automatic following is paused by remote scrolling. Completion is consumed once: switching panes or recreating the reader preserves later local reading positions.
  • Saving credentials does not persist terminal output or drafts across process death. Rotation retains the current ViewModel; reopening the app offers saved Hosts.

Build and test #

Open Mirror/Android/ in Android Studio, or change to that directory and use Android Studio's bundled JBR or JDK 17 with an installed Android SDK 34. Set ANDROID_HOME or create an untracked local.properties with sdk.dir.

./gradlew :app:assembleDebug :app:testDebugUnitTest :app:lintDebug

From the repository root, make test-mirror-android runs the same tasks.

APK: app/build/outputs/apk/debug/app-debug.apk. With an available emulator/device:

./gradlew :app:connectedDebugAndroidTest

Dependencies are pinned to the tested Gradle 8.7 / AGP 8.4.2 / Kotlin 1.9.24 / Compose compiler 1.5.14 toolchain. This is not yet a Play Store release configuration. The native TLS interop test is opt-in (MIRROR_NATIVE_TEST_PORT) and is skipped without its external macOS fixture; the normal unit suite must not be described as executing that fixture. See validation for actual run evidence.

Structure #

  • Wire.kt: Swift Codable-compatible controls, bounded binary frames, public commands.
  • Transport.kt: ECDHE-PSK TLS, challenge proof, enrollment, heartbeat and cancellation.
  • Vault.kt: device credentials encrypted at rest with an Android Keystore key.
  • Session.kt, LaunchModel.kt: subscriptions, takeover, input receipts, history and creation.
  • Document.kt: bounded text/code/table parsing and hardware Return gesture.
  • MirrorApp.kt: adaptive UI; MainActivity.kt: retained model and foreground lifecycle.

The shared wire contract is owned by Prowl. There is no legacy protocol or 64-hex-code compatibility. Update both ends together.

Source attribution and upstream terms: ThirdPartyNotices.

Ordinary Send requires Host capability agent-input. It does not add a completion prompt or create a pending dispatch; prior automation receipts remain unchanged. Delivery confirmation and reconnect receipt lookup remain separate from task success.

Use Scan QR Code in the Host connection form to scan Add a Device on Host. Scanning connects with the encoded address, port and short-lived code. Camera permission is optional; manual entry remains available. The scanner opens in portrait without a red scanning line. Refresh expired codes on Host. The Live scroll toolbar places Scroll up on the left and Scroll down on the right, with loading feedback centered between them.