Skip to content

feat(desktop): authoring runner URI, Allow-per-sub, and mailbox poll - #154

Merged
abrichr merged 5 commits into
mainfrom
feat/authoring-runner-2026-08-31
Sep 1, 2026
Merged

feat(desktop): authoring runner URI, Allow-per-sub, and mailbox poll#154
abrichr merged 5 commits into
mainfrom
feat/authoring-runner-2026-08-31

Conversation

@abrichr

@abrichr abrichr commented Aug 31, 2026

Copy link
Copy Markdown
Member

Implementation continues. Synthetic CI is the gate. MockMed is tests only. Production mailbox is web 464; this PR is the laptop Allow/poll.

Summary

D1 + D2 of the hosted authoring MCP stack. ChatGPT.com / Claude.ai can bind this laptop to a job pack over outbound HTTPS, Allow that connector sub, and drive a first demonstration. This is not Cloud BYOC execute and not Operator.

Does not merge or edit desktop PR 153 (coach HUD). Overlay Continue/Stop reuse origin/main ControlOverlay via existing resume_recording / stop_recording. Pause card copy: "Type in the application. Continue here when done."

D1 — URI grammar (parse-only)

feat(desktop): openadapt://runner URI grammar, distinct from connect

  • Second scheme openadapt://runner in src-tauri/src/pairing.rs. Connect is not widened.
  • engine/auth/runner_bind.py parse-only: pack, bind, origin. Origin pin https://openadapt.ai.
  • Prefix parsers fail closed: oab_[A-Za-z0-9_-]{43}, oals_[a-f0-9]{64}.
  • Tests reject oar_ + 64 hex, oap_ + 43 unreserved, oab_ with a hex body, oals_ with a base64url body, and anything that merely starts with oa.
  • No claim, no keychain, no poll in this commit.

D2 — claim, Allow-per-sub, wait=0 poll, record_observed

feat(desktop): authoring claim, Allow-per-sub, mailbox poll, record_observed

  • engine/authoring_runner.py copies poll / lease / TTL / kill-as-command / metadata-callback shape from hosted_runner.py. Does not copy org, Stripe, oar_, wait=25, trust-manifest, or journal dispatch.
  • Claim POST /j/{id}/runner/claim with oab_. 201 leaseSecret (oals_) stored in keychain openadapt-authoring-lease|{pack}. Secret never returned over IPC.
  • Poll wait_seconds: 0, sleep 1s locally. DEFAULT_LEASE_S = 900.
  • bind_pack pending Allow: "Allow ChatGPT to drive this job" for that sub. Confirm POSTs /j/{id}/runner/allow over Bearer oals_ so the mailbox stores allowedSub. A second account asks to replace.
  • Every enqueue tool except bind_pack requires allowedSub match, including hosted halt.
  • Unsigned abort is overlay/Desktop Stop over Bearer oals_.
  • Continue → Recorder.record_observed on the pause-target node (captured at pause start). Never type_text. Secret pauses store no text. pause_for_input stays in-flight until Continue, then callbacks { recorded: true, param } with no value.
  • Compile wraps as { status: "needs_human_admit" }. Refuses a secret pause with no TYPE. An agent click never paints VERIFIED.
  • Click uses laptop node-table backend_pixels (mode 0600, 15-minute lifetime). Stale id → no click. Uncertain delivery → RECONCILIATION_REQUIRED, no retry.
  • macOS native + unique-title Linux + Playwright web (empty cookies, URL typed into Desktop). Windows native / Citrix / RDP: COACH_ONLY. Do not spawn win_agent or call parallels_vm.launch_agent().
  • Bind prefix oab_ (Cloud already owns oar_).
  • Mailbox HTTP is mocked in tests against the design's bind/poll/callback/allow shapes (web mailbox need not be live).

Gaps closed in this review (2026-09-01)

fix(desktop): close remaining authoring D1/D2 Allow, pause, and substrate holes

  • Mailbox allowedSub is stored by POST /runner/allow, not a generic callback. Without that, web enqueue stays not_allowed after Desktop Allow.
  • pause_for_input no longer completes before the person types. Continue is the result: { recorded: true, param }, never field text.
  • macOS and Linux default to COACH_ONLY until uniqueness is proven (Use this window). Titles stay on disk / local pin; they never go to observe or callback.
  • Playwright launch requires empty cookies and a URL typed into Desktop, not MCP.
  • Overlay pause card: "Type in the application. Continue here when done." Continue, not type_text.
  • Node HMAC key is the oals_ hex body, not SHA256-as-password.
  • Prefix parsers explicitly reject oab_+64 hex and oals_+43 base64url.

fix(desktop): keep first-workflow boot off Loading when authoring status is missing

  • Authoring bind is optional. A null authoring_status no longer throws before setCheckedAuth, so the shell cannot stay on Loading and first-workflow navigation still mounts.

fix(ci): fetch vendored design tokens through the GitHub API

  • tokens:check reads private openadapt-web through the GitHub Contents API. raw.githubusercontent.com 404s because that repo is private.

Tests

uv run ruff check engine/ tests/
uv run pytest tests/test_engine/test_auth_runner_bind.py tests/test_engine/test_authoring_runner.py tests/test_pairing_protocol_boundary.py -v
npm run test:ui

Python engine tests passed. Overlay pause-card tests passed. First-workflow navigation vitest now includes idle/missing authoring status so the shell leaves Loading.

How a person Allows ChatGPT to drive this job

  1. OpenAdapt Desktop is running on this computer.
  2. From the job pack page, choose Connect this computer. The OS opens openadapt://runner?pack=…&bind=oab_…&origin=https://openadapt.ai. Desktop claims the bind over outbound HTTPS.
  3. In ChatGPT, after the connector is added, ask it to drive this pack (bind_pack).
  4. Desktop shows Allow ChatGPT to drive this job for that ChatGPT account (sub). Click Allow. A different account asks to replace.
  5. Pin the target locally (titles never leave the laptop):
    • Web: paste the https:// URL and Pin browser. Desktop launches Playwright Chromium with empty cookies. Log in again in that window if the app needs a session.
    • macOS / Linux: bring the app window frontmost and click Use this window. If the title is not unique, the job stays coach-only.
    • Windows native, Citrix, RDP: coach-only. You click; ChatGPT suggests. Desktop does not spawn win_agent.
  6. When ChatGPT pauses for a secret or a note, type in the application, then Continue on the overlay. Desktop records that with record_observed. It never types the secret.

Possession of the pack URL plus a bound laptop is not enough. Allow is per ChatGPT/Claude sub.

Out of scope

  • Desktop PR 153 coach HUD overlay rewrite.
  • Taxes/RBC/Amex are an example job only.
  • Production mailbox (W1) and /mcp (W2) live in openadapt-web PR 464. This PR is the laptop Allow/poll.

Opened/updated by an agent session, not the founder.

Parse-only runner deep links (pack, bind, origin) beside the existing
connect action. Prefix parsers reject oar_/oap_ mix-ups, hex-bodied
oab_ tokens, and base64url-bodied oals_ secrets. No claim, keychain,
or mailbox poll.
…bserved

Outbound HTTPS claim of oab_ binds, keychain oals_ leases, wait=0 poll
with a local 1s sleep, and Allow of a specific connector sub. Overlay
Continue persists pause-target input via record_observed, never type_text.
Windows native is COACH_ONLY; do not spawn win_agent.
Comment thread engine/authoring_runner.py Fixed
Comment thread engine/authoring_runner.py Fixed
…rate holes

Store mailbox allowedSub via POST /runner/allow. Hold pause_for_input until
overlay Continue, then callback {recorded, param} with no field value.
macOS/Linux default to COACH_ONLY until the window is unique; Playwright
launches empty-cookie Chromium from a Desktop URL. Titles never go to MCP.
…tus is missing

Authoring bind is optional. A null authoring_status used to throw before
setCheckedAuth, so the shell stayed on Loading and first-workflow
navigation never mounted.
openadapt-web is private, so raw.githubusercontent.com 404s. CI already
passes a token; use the Contents API so tokens:check can still gate
palette drift.
@abrichr
abrichr merged commit 20c9304 into main Sep 1, 2026
17 checks passed
@abrichr
abrichr deleted the feat/authoring-runner-2026-08-31 branch September 1, 2026 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants