Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,49 @@ uvx openadapt-agent serve --bundles /path/to/bundles # read-only
uvx openadapt-agent serve --bundles /path/to/bundles --allow-run
```

## Author a first demo (Claude Code)

Local Claude Code (or Cursor, Codex, Grok CLI) is the first authoring UI
over stdio. Hosted ChatGPT.com / Claude.ai cannot talk to localhost; they
need the outbound mailbox client below, not `serve --authoring`.

```bash
claude mcp add openadapt-authoring -- \
uvx --from 'openadapt-agent[tutorial]' openadapt-agent \
serve --authoring
```

`--bundles` is omitted. Probe tools are `observe`, `start_record`, `click`,
and `halt`. `--authoring` does not enable run tools. This process stays
stdio. Pass `--url` to pin a fresh Playwright Chromium with empty cookies.
Windows native, Citrix, and RDP are coach-only in v1.

## Connect this computer (ChatGPT.com / Claude.ai)

OpenAdapt is installed on this computer, so an agent can drive only through
OpenAdapt.

Desktop: the tray is already running. **Connect this computer** on the job
page (`openadapt://runner`) just works.

Pip: after `pip install openadapt`, start the same mailbox client:

```bash
openadapt-agent authoring connect \
'openadapt://runner?pack=p.…&bind=oab_…&origin=https://openadapt.ai'
```

That claims `oab_`, polls `/j/{id}/runner/poll` with `wait_seconds: 0`,
prints Allow (`y/n`) per chat account, and on pause prints `Sign in in
the app, then press Enter`. Continue records with `record_observed`. It
never types your password. Pass `--url` for a fresh Playwright Chromium
with empty cookies.

The job page should offer `openadapt connect <url>` next to Open
OpenAdapt (meta-package alias; this repo implements
`openadapt-agent authoring connect`). Overlay chrome, launchd, and the
OS URL handler stay Desktop-only. See [`docs/MAILBOX_CLI.md`](docs/MAILBOX_CLI.md).

## Serve a bundle

`--allow-run` with no `--bundles` records, compiles, and certifies the
Expand Down Expand Up @@ -150,6 +193,7 @@ Continue and Skip.
| `run_local_quickstart` | `--allow-run` with no `--bundles` |
| `reject_attention`, `teach_attention`, `escalate_attention` | `--allow-attended-actions` |
| `continue_attention`, `skip_attention` | `--allow-attended-actions` plus a qualified deployment `--config` |
| `observe`, `start_record`, `click`, `halt` | `--authoring` |

## Run outcomes

Expand Down
88 changes: 73 additions & 15 deletions docs/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,30 @@ MCP client / Agent Skill
│ local stdio + exact JSON schemas
openadapt_agent.mcp
openadapt_agent.mcp (local stdio only; HTTP shim forbidden)
openadapt_agent.bridge
├── bundle discovery and typed run tools
├── PHI-safe Needs Attention projection
├── action-specific operator decisions
└── structured success / halt / refusal results
├── openadapt_agent.bridge
│ ├── bundle discovery and typed run tools
│ ├── PHI-safe Needs Attention projection
│ ├── action-specific operator decisions
│ └── structured success / halt / refusal results
│ │
│ ├── new run ──► openadapt-flow run subprocess
│ └── attended ─► openadapt-flow durable API
├── new run ──────────────► openadapt-flow run subprocess
│ fail-closed admission + execution
├── openadapt_agent.authoring (--authoring first demo, stdio)
│ observe / start_record / click / halt
│ local type (agent-driven Recorder.type_text)
│ pause Continue → record_observed (never type_text)
│ │
│ ▼
│ openadapt_flow.authoring.AuthoringSession
└── attended decision ────► openadapt-flow durable API
signed capability + idempotency
+ live revalidation + audit
└── openadapt_agent.mailbox (authoring connect, outbound HTTPS)
parse openadapt://runner / pack URL
POST claim oab_ → poll wait=0 → Allow-per-sub
Continue → record_observed (never type_text)
overlay chrome stays Desktop-only
```

The MCP adapter is intentionally thin. Tool descriptions and dispatch
Expand Down Expand Up @@ -120,6 +129,38 @@ operation:
Skip are registered only when a deployment configuration lets Flow
construct its bound live executor.

`--authoring` registers first-demo tools over the same local stdio
server. Probe names match hosted MCP: `observe`, `start_record`,
`click`, `halt`. Local stdio may also include `type` for agent-driven
typing through Flow's Recorder. Hosted MCP remains pause-only. Human
type during `pause_for_input` is persisted with `Recorder.record_observed`
on the pause-target node, never `type_text`. `compile` wraps Flow
`compile_recording` and returns `needs_human_admit`; an agent click never
paints `VERIFIED`.

`--authoring` does not imply `--allow-run`. `--bundles` is optional iff
`--authoring` (or the existing `--tutorial` / implied-tutorial path). The
published run recipe in `server.json` still requires `--bundles` and
stays `transport: stdio`. Authoring is a first demo; there is no bundle
yet.

Observe is a fail-closed PHI projection (`openadapt.authoring.observe/v1`):
no `value`, `text`, window `title`, screenshot, OCR, URL, or backend
pixels. Windows native, Citrix, and RDP are `COACH_ONLY` in v1.

The session object is Flow's public `openadapt_flow.authoring` module
(`AuthoringSession(backend, out_dir, backend_kind=…)` when F1 is
importable). Until that module is importable, `serve --authoring` fails
closed with an explicit dependency error. Windows native, Citrix, and
RDP construct a coach-only stand-in and never spawn `win_agent`. Observe
is fail-closed to the T1 wire (`additionalProperties: false`, node ids
`n_` + 8 hex, 200 nodes / 32 KiB). Capture's projector is used when
importable. If Desktop has advertised authoring IPC, overlay stays
Desktop-owned; stdio `--authoring` does not speak the D2 protocol.
`authoring connect` is the outbound mailbox client for hosted chat apps.
Tests cover the stdio tool surface with a fake session and an F1-shaped
session, and the mailbox client against a mocked wait=0 poll.

## Governed runs

Each run tool shells out to the `openadapt-flow` installed in the same
Expand Down Expand Up @@ -273,9 +314,17 @@ caller-controlled `USERNAME` environment variable. A blank operator
identity fails closed.

This process must not be port-forwarded or exposed as an unauthenticated
network service. OpenAdapt Cloud owns remote authentication,
network service. An HTTP / Streamable-HTTP **listener** in this MIT package
remains forbidden, including when `--authoring` is set. Hosted ChatGPT.com
/ Claude.ai cannot talk to localhost. Pip users run `openadapt-agent
authoring connect` — an **outbound** mailbox client (claim `oab_`, poll
`wait_seconds: 0`, Allow-per-`sub`) copied from Desktop
`engine/authoring_runner.py` when that engine is not importable. Overlay
chrome, launchd, and the `openadapt://` URL handler stay Desktop-only.
See `docs/MAILBOX_CLI.md`. OpenAdapt Cloud owns remote authentication,
multi-tenancy, tenant-scoped authorization, fleet policy, and managed
transport.
execute. `--authoring` does not add those, and it does not imply
`--allow-run`.

## Dependency boundary

Expand Down Expand Up @@ -309,7 +358,16 @@ Tests cover:
- compatibility with Flow's public, thread-owned attended service;
- success/halt/refusal/timeout outcome mapping;
- MCP serialization and thread ownership;
- Agent Skill emission.
- Agent Skill emission;
- `--authoring` probe tools (`observe`, `start_record`, `click`, `halt`)
and local `type`; observe projection drops values/titles/screenshots
and extra keys, caps the wire at 32 KiB, and uses `n_` + 8 hex node
ids; pause Continue uses `record_observed` rather than `type_text`;
compile returns `needs_human_admit`; `--authoring` does not enable
run tools; `server.json` stays stdio with `--bundles` required;
`authoring connect` parses `openadapt://runner` / pack URLs, claims
`oab_`, polls `wait_seconds: 0`, prompts Allow-per-`sub`, and Continue
uses `record_observed` (never `type_text`).

CI runs on Python 3.10, 3.11, and 3.12. It also builds the wheel and
sdist, verifies MIT metadata and license inclusion, and refuses package
Expand Down
2 changes: 1 addition & 1 deletion docs/DISTRIBUTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ and [`../manifest.json`](../manifest.json).
- **License:** MIT
- **Transport:** stdio
- **Run command (uvx):** `uvx --from 'openadapt-agent[tutorial]' openadapt-agent serve --allow-run`
- **Config:** `--allow-run` with no `--bundles` (public synthetic bundle, generated at serve time), `--tutorial` (same path without implying run tools), or `--bundles` (operator's private artifact), `--runs-dir`, `--allow-attended-actions`, qualified `--config` for Continue/Skip, and optional secret `OPENADAPT_BUNDLE_KEY`
- **Config:** `--allow-run` with no `--bundles` (public synthetic bundle, generated at serve time), `--tutorial` (same path without implying run tools), `--authoring` (local Claude Code first demo; not the published registry recipe), `authoring connect` (outbound hosted mailbox; not the registry recipe), or `--bundles` (operator's private artifact), `--runs-dir`, `--allow-attended-actions`, qualified `--config` for Continue/Skip, and optional secret `OPENADAPT_BUNDLE_KEY`
- **Tools:**
- `list_workflows` / `get_workflow` — PHI-safe structural bundle projections with opaque IDs.
- `get_run_report` — PHI-safe status and count summary; raw evidence stays local unless protected export was explicitly enabled.
Expand Down
100 changes: 100 additions & 0 deletions docs/MAILBOX_CLI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Pip mailbox client: ChatGPT.com drives this computer

One sentence: **OpenAdapt is installed on this computer, so an agent can drive only through OpenAdapt.**

ChatGPT.com and Claude.ai cannot talk to localhost. They call `https://openadapt.ai/mcp`. A process on the laptop must poll `/j/{id}/runner/poll` with `wait_seconds: 0`, claim `oab_`, Allow per OAuth `sub`, pause so the person signs in **in the real app**, and `record_observed` on Continue. Desktop already does that. `openadapt-agent serve --authoring` is local stdio for Claude Code. That never reaches ChatGPT.com. `pip install` alone is not a listener.

## Command

Canonical in this package:

```bash
openadapt-agent authoring connect '<openadapt://runner?pack=…&bind=oab_…&origin=https://openadapt.ai>'
```

Playwright web (fresh Chromium, empty cookies):

```bash
openadapt-agent authoring connect '<runner-link>' --url https://example.invalid/app
```

Rejected alternative: `openadapt-agent serve --authoring --mailbox`. `serve` is stdio MCP. Mixing it with an outbound poll loop would look like this package grew a hosted transport. The mailbox client is a separate verb.

Job-page / meta-package alias (not implemented here; `openadapt` wraps this package):

```bash
openadapt connect '<runner-link-or-pack-url>'
```

The pack “Connect this computer” control should offer that command next to **Open OpenAdapt**. Do not implement the web page in this repository. GET `/j/{id}` stays presence. It is not actuation.

## Shared engine

The protocol is Desktop’s authoring mailbox (openadapt-desktop PR 154, `engine/authoring_runner.py`):

| Step | Wire |
|---|---|
| Parse | `openadapt://runner` only. Fields: `pack`, `bind`, `origin`. Origin pin `https://openadapt.ai`. |
| Claim | `POST /j/{id}/runner/claim` `{ bind: "oab_…" }` → 201 `{ leaseSecret: "oals_…", lease_s: 900 }` |
| Poll | `POST /j/{id}/runner/poll` `Authorization: Bearer oals_…` `{ wait_seconds: 0, lease_seconds: 900 }`. Empty 204. Sleep 1 s locally. Do not copy hosted-runner `wait=25`. |
| Allow | Terminal `y/n` for **that** pending `bind_pack` `sub`. `POST /j/{id}/runner/allow` `{ command_id }`. |
| Continue | `Recorder.record_observed`. Never `type_text` for secrets or for text the person already typed. |

This package prefers Desktop `engine.authoring_runner.AuthoringMailboxTransport` when that module is importable (Desktop installed in-process). Otherwise it uses the copy in `openadapt_agent.mailbox` (stdlib `urllib`, outbound POST only). There is no new repository and no `control_plane` path.

Flow `AuthoringSession` / `Recorder` is the actuation engine when importable. Overlay chrome is not.

## What this CLI does

- Parse `openadapt://runner` or `https://openadapt.ai/j/{pack}` (pack URL is not enough to claim without `bind`).
- Claim `oab_`. First claim wins. Do not print `leaseSecret`.
- Poll `wait_seconds: 0`.
- Print Allow (`Allow ChatGPT to drive this job?` / replace-account copy). stdin `y/n`.
- Pause: print `Sign in in the app, then press Enter`. Do not ask for a password. Do not `type_text` the secret.
- Continue → `record_observed` on the pause-target node.
- `--url` pins Playwright Chromium with **empty cookies**. No debug-port attach.
- Unique-window fail-closed: macOS / Linux without a unique frontmost title is coach-only. Windows native / Citrix / RDP are coach-only. Never spawn `win_agent`.
- Allow-per-`sub` before observe / click / halt.
- GET handshake is not actuation (the CLI does not GET the pack page to click).
- Uncertain delivery: no blind retry (`RECONCILIATION_REQUIRED`).
- Titles, values, screenshots, backend pixels never go to the mailbox callback.

## What stays Desktop-only

| Capability | Why it is not this package |
|---|---|
| Overlay chrome (ghost ring, pause card, pointer-transparent HUD) | Native overlay contract. Terminal prints instead. |
| `openadapt://` OS URL handler | Tauri / protocol registration. Pip users paste the command. |
| launchd / Login Item / tray always-on listener | Desktop tray is already running. Pip starts a foreground process. |
| Keychain lease persistence across process restarts | Thin CLI keeps `oals_` in memory for this process. |
| Coach HUD chrome on Windows native | Coach-only here too; Desktop owns the HUD. |

Pip is not a worse cousin for the **mailbox protocol**. It is a worse cousin for **chrome**: no overlay, no protocol handler, no tray. The hosted tools still only drive through OpenAdapt.

## Safety invariants (Desktop 154)

- Allow-per-`sub` before observe / click / halt.
- Continue → `record_observed`, never `type_text` for secrets.
- GET handshake is not actuation.
- Uncertain delivery: no blind retry.
- Titles do not go to MCP / mailbox callbacks.
- No public HTTP / Streamable-HTTP listener in this MIT package.
- No localhost tunnel.
- Bind tokens: exact `oab_[A-Za-z0-9_-]{43}`. Lease: exact `oals_[a-f0-9]{64}`. Reject `oar_`, `oap_`, swapped encodings, and anything that merely starts with `oa`.

## Job page (openadapt-web, not this PR)

Do not edit web 462 from this repository. The pack “Connect this computer” should eventually show, next to Open OpenAdapt:

```text
pip: openadapt connect '<runner-link>'
```

Desktop users keep the `openadapt://runner` button. Pip users paste the same bind into this command.

## Remaining gaps

- Overlay chrome.
- launchd / tray autostart.
- OS URL handler so a click on the pack page starts the pip client without paste.
- Extracting the rest of Desktop `AuthoringRunner` (node-table HMAC files, overlay Continue) into a shared import. This PR copies claim / poll / allow / Continue-without-`type_text` only.
2 changes: 1 addition & 1 deletion llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Default runtime interface for a calling agent. Computer-use agents are the user

## What it provides

- `openadapt-agent serve --allow-run`: generate and serve the public synthetic tutorial at serve time. `openadapt-agent serve --tutorial` is the same path without run tools. `openadapt-agent serve --bundles <dir> [--allow-run]`: serve a private compiled bundle. `list_workflows`, `get_workflow`, `get_run_report`, `list_needs_attention`, and `get_attention_item` are always available as PHI-safe read-only projections. `run_workflow_<opaque-id>` tools require `--allow-run`. The synthetic tutorial registers `run_local_quickstart`. If a run returns HALTED, tell the user the record did not change. Never summarize halt, refused, timeout, or error as success.
- `openadapt-agent serve --allow-run`: generate and serve the public synthetic tutorial at serve time. `openadapt-agent serve --tutorial` is the same path without run tools. `openadapt-agent serve --bundles <dir> [--allow-run]`: serve a private compiled bundle. `openadapt-agent serve --authoring`: local Claude Code first-demo tools `observe`, `start_record`, `click`, and `halt` over stdio. `--authoring` does not enable run tools and does not open an HTTP listener. `openadapt-agent authoring connect <openadapt://runner or pack URL>`: outbound mailbox client for hosted ChatGPT.com / Claude.ai (claim `oab_`, poll wait=0, Allow-per-sub, Continue via `record_observed`). Overlay chrome stays Desktop-only. Hosted MCP is `https://openadapt.ai/mcp`. `list_workflows`, `get_workflow`, `get_run_report`, `list_needs_attention`, and `get_attention_item` are always available as PHI-safe read-only projections. `run_workflow_<opaque-id>` tools require `--allow-run`. The synthetic tutorial registers `run_local_quickstart`. If a run returns HALTED, tell the user the record did not change. Never summarize halt, refused, timeout, or error as success.
- `--allow-attended-actions` adds exact Reject, Teach, and Escalate tools for signed durable pauses. With a qualified Flow `--config`, the same server also exposes Continue and Skip through Flow's deployment-bound live verifier and deterministic resume path.
- `openadapt-agent emit-skill <bundle> --out <dir>` wraps Flow's skill emitter and appends MCP, halt, and attended-action guidance.

Expand Down
Loading