From 736eb6fdfe5f6573ed0e400e4b45bc9dd8add68e Mon Sep 17 00:00:00 2001 From: abrichr Date: Tue, 18 Aug 2026 12:10:23 -0400 Subject: [PATCH 1/4] fix launcher quickstart and product docs --- .github/workflows/quickstart-lifecycle.yml | 55 +++ README.md | 7 +- docs/architecture.md | 334 ++++-------------- docs/cli.md | 380 ++++----------------- docs/getting-started/installation.md | 143 +++----- docs/getting-started/permissions.md | 163 ++------- docs/getting-started/quickstart.md | 157 +++------ docs/index.md | 7 +- docs/packages/capture.md | 152 +++------ docs/packages/index.md | 177 ++-------- docs/permissions-macos.md | 149 +------- openadapt/__init__.py | 4 +- openadapt/cli.py | 227 ++++-------- pyproject.toml | 9 +- scripts/quickstart_lifecycle.py | 315 +++++++++++++++++ tests/test_cli_smoke.py | 153 ++++++--- tests/test_quickstart_lifecycle.py | 97 ++++++ uv.lock | 12 +- 18 files changed, 1003 insertions(+), 1538 deletions(-) create mode 100644 .github/workflows/quickstart-lifecycle.yml create mode 100644 scripts/quickstart_lifecycle.py create mode 100644 tests/test_quickstart_lifecycle.py diff --git a/.github/workflows/quickstart-lifecycle.yml b/.github/workflows/quickstart-lifecycle.yml new file mode 100644 index 000000000..58a972a8d --- /dev/null +++ b/.github/workflows/quickstart-lifecycle.yml @@ -0,0 +1,55 @@ +name: Launcher quickstart lifecycle + +on: + schedule: + - cron: "15 8 * * 3" # Wednesdays 08:15 UTC + workflow_dispatch: + +concurrency: + group: launcher-quickstart-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + quickstart: + name: public launcher command (ubuntu-latest) + runs-on: ubuntu-latest + env: + PYTHONUTF8: "1" + PYTHONIOENCODING: "utf-8" + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12" + cache: pip + cache-dependency-path: pyproject.toml + + - name: Build launcher wheel + run: | + python -m pip install build + python -m build --wheel --outdir lifecycle-dist + + - name: Run the public quickstart from a clean environment + run: >- + python scripts/quickstart_lifecycle.py + --launcher-wheel "lifecycle-dist/*.whl" + --work-dir "runs/launcher-lifecycle" + --browser-with-deps + --source-revision "${{ github.sha }}" + + - name: Upload lifecycle evidence + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0 # v7.0.1 + with: + name: launcher-quickstart-ubuntu + path: | + runs/launcher-lifecycle/summary.json + runs/launcher-lifecycle/logs/*.log + runs/launcher-lifecycle/artifacts/**/REPORT.md + runs/launcher-lifecycle/artifacts/**/report.json + runs/launcher-lifecycle/artifacts/**/receipt.json + if-no-files-found: warn diff --git a/README.md b/README.md index d217abb41..82672275e 100644 --- a/README.md +++ b/README.md @@ -241,13 +241,18 @@ defines what can be claimed for a new deployment. - **[`openadapt-flow`](https://github.com/OpenAdaptAI/openadapt-flow):** canonical compiler, governed runtime, CLI implementation, and conformance tests +- **[`openadapt-capture`](https://github.com/OpenAdaptAI/openadapt-capture):** + Beta native screen, mouse, keyboard, timing, window-scope, and media capture + component used by the Flow desktop recording path +- **[`openadapt-privacy`](https://github.com/OpenAdaptAI/openadapt-privacy):** + local sanitization and review mechanisms for approved derivatives - **[Documentation](https://docs.openadapt.ai):** installation, workflow authoring, qualification, operation, deployment, and reference material - **[Desktop](https://github.com/OpenAdaptAI/openadapt-desktop):** native record, inspect, qualify, execute, and review application The pre-1.0 monolith remains available under [`legacy/`](legacy/) for migration -history. New product and engine development belongs in `openadapt-flow`. +history. New compiler and runtime development belongs in `openadapt-flow`.
Research and legacy history diff --git a/docs/architecture.md b/docs/architecture.md index 664eddcdc..55c40e541 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,295 +1,85 @@ -# OpenAdapt Architecture +# OpenAdapt architecture -OpenAdapt v1.0+ uses a **modular meta-package architecture** where the main `openadapt` package provides a unified CLI and depends on focused sub-packages. +> **Canonical architecture.** See +> [docs.openadapt.ai/concepts](https://docs.openadapt.ai/concepts/) for the +> maintained design and trust-boundary documentation. -## System Overview - -```mermaid -flowchart TB - subgraph User["User"] - UI[Desktop/Web GUI] - end - - subgraph OpenAdapt["OpenAdapt Meta-Package"] - CLI[openadapt CLI] - LAZY[Lazy Imports] - end - - subgraph Core["Core Packages"] - CAPTURE[openadapt-capture] - ML[openadapt-ml] - EVALS[openadapt-evals] - VIEWER[openadapt-viewer] - end - - subgraph Optional["Optional Packages"] - GROUNDING[openadapt-grounding] - RETRIEVAL[openadapt-retrieval] - PRIVACY[openadapt-privacy] - end - - subgraph Storage["Storage"] - DEMO[(Demonstration
JSON/Parquet)] - MODEL[(Model
Checkpoints)] - RESULTS[(Evaluation
Results)] - end - - %% User interactions - UI --> CAPTURE - - %% CLI orchestration - CLI --> CAPTURE - CLI --> ML - CLI --> EVALS - CLI --> VIEWER - - %% Lazy loading - LAZY -.-> GROUNDING - LAZY -.-> RETRIEVAL - LAZY -.-> PRIVACY - - %% Data flow - CAPTURE --> DEMO - DEMO --> ML - ML --> MODEL - MODEL --> EVALS - EVALS --> RESULTS - DEMO --> VIEWER - - %% Optional integrations - GROUNDING -.-> ML - RETRIEVAL -.-> ML - PRIVACY -.-> CAPTURE - PRIVACY -.-> VIEWER - - classDef metaPkg fill:#4A90D9,stroke:#2E5A8B,color:#fff - classDef corePkg fill:#5CB85C,stroke:#3D7A3D,color:#fff - classDef optPkg fill:#F0AD4E,stroke:#C79121,color:#fff - classDef storage fill:#9B59B6,stroke:#6C3483,color:#fff - classDef user fill:#E74C3C,stroke:#A93226,color:#fff - - class CLI,LAZY metaPkg - class CAPTURE,ML,EVALS,VIEWER corePkg - class GROUNDING,RETRIEVAL,PRIVACY optPkg - class DEMO,MODEL,RESULTS storage - class UI user -``` - -## Data Flow Pipeline +OpenAdapt is a governed demonstration compiler. A human demonstrates a task. +The engine retains evidence, compiles a deterministic program, qualifies it +against a policy, and runs it through a fail-closed runtime. ```mermaid flowchart LR - subgraph Demonstrate["1. Demonstrate"] - A[Human Trajectory] --> B[Capture Session] - B --> C[Observations + Actions] - end - - subgraph Store["2. Store"] - C --> D[JSON/Parquet Files] - D --> E[Demonstration Library] - end - - subgraph Learn["3. Learn"] - E --> F[Trajectory Abstraction] - F --> G[Policy Learning] - G --> H[Checkpoint] - end - - subgraph Execute["4. Execute"] - H --> I[Trained Policy] - I --> J[Inference] - J --> K[Agent Deployment] - end - - subgraph Evaluate["5. Evaluate"] - I --> L[Benchmark Runner] - L --> M[Metrics] - M --> N[Results Report] - end - - %% Optional enhancements - GROUND[Grounding] -.-> J - RETRIEVE[Retrieval] -.-> F - PRIV[Privacy] -.-> C - - classDef phase fill:#3498DB,stroke:#1A5276,color:#fff - classDef optional fill:#F39C12,stroke:#B7950B,color:#fff - - class A,B,C,D,E,F,G,H,I,J,K,L,M,N phase - class GROUND,RETRIEVE,PRIV optional -``` - -## Package Dependencies - -```mermaid -graph TD - OA[openadapt
Meta-package] - - OA -->|capture| CAP[openadapt-capture] - OA -->|ml| MLP[openadapt-ml] - OA -->|evals| EVL[openadapt-evals] - OA -->|viewer| VWR[openadapt-viewer] - OA -->|grounding| GRD[openadapt-grounding] - OA -->|retrieval| RET[openadapt-retrieval] - OA -->|privacy| PRV[openadapt-privacy] - - %% Core bundle - OA -->|core| CORE[Core Bundle] - CORE --> CAP - CORE --> MLP - CORE --> EVL - CORE --> VWR - - %% All bundle - OA -->|all| ALL[Full Bundle] - ALL --> CORE - ALL --> GRD - ALL --> RET - ALL --> PRV - - classDef meta fill:#2C3E50,stroke:#1A252F,color:#fff - classDef core fill:#27AE60,stroke:#1E8449,color:#fff - classDef optional fill:#E67E22,stroke:#A04000,color:#fff - classDef bundle fill:#8E44AD,stroke:#5B2C6F,color:#fff - - class OA meta - class CAP,MLP,EVL,VWR core - class GRD,RET,PRV optional - class CORE,ALL bundle + H[Human demonstration] --> R[Surface recorder] + R --> C[openadapt-flow compiler] + C --> B[Inspectable bundle] + B --> Q[Qualification and certification] + Q --> X[Governed runtime] + X --> V[Independent effect verifier] + V -->|contract passes| OK[VERIFIED] + V -->|uncertain or failed| STOP[HALTED or reconciliation] ``` -## Component Details - -### Core Packages - -| Package | Responsibility | Key Exports | -|---------|---------------|-------------| -| **openadapt-capture** | Demonstration collection, observation-action capture, storage | `CaptureSession`, `Recorder`, `Action` | -| **openadapt-ml** | Policy learning, training, inference | `QwenVLAdapter`, `Trainer`, `AgentPolicy` | -| **openadapt-evals** | Benchmark evaluation, metrics | `ApiAgent`, `BenchmarkAdapter`, `evaluate_agent_on_benchmark` | -| **openadapt-viewer** | Trajectory visualization | `PageBuilder`, `HTMLBuilder` | - -### Optional Packages - -| Package | Responsibility | Use Case | -|---------|---------------|----------| -| **openadapt-grounding** | UI element grounding | Improved action accuracy with element detection | -| **openadapt-retrieval** | Multimodal trajectory search | Find similar demonstrations for few-shot policy learning | -| **openadapt-privacy** | PII/PHI scrubbing | Redact sensitive data before storage/training | - -## Evaluation Loop +## Repository roles -```mermaid -flowchart TB - subgraph Agent["Agent Under Test"] - POLICY[Agent Policy] - API[API Agent
Claude/GPT] - end - - subgraph Benchmark["Benchmark System"] - ADAPTER[Benchmark Adapter] - MOCK[Mock Adapter] - LIVE[Live WAA Adapter] - end - - subgraph Tasks["Task Execution"] - TASK[Get Task] - OBS[Observe State] - ACT[Execute Action] - CHECK[Check Success] - end - - subgraph Metrics["Metrics"] - SUCCESS[Success Rate] - STEPS[Avg Steps] - TIME[Execution Time] - end - - POLICY --> ADAPTER - API --> ADAPTER - ADAPTER --> MOCK - ADAPTER --> LIVE - - MOCK --> TASK - LIVE --> TASK - TASK --> OBS - OBS --> POLICY - OBS --> API - POLICY --> ACT - API --> ACT - ACT --> CHECK - CHECK -->|next| TASK - CHECK -->|done| SUCCESS - CHECK --> STEPS - CHECK --> TIME +| Component | Product role | Source availability | +| --- | --- | --- | +| `OpenAdapt` | Launcher, meta-package, unified CLI, and stable public entry point | MIT | +| `openadapt-flow` | Compiler, certification, replay, governed repair, and run reports | MIT | +| `openadapt-capture` | Native screen, mouse, keyboard, timing, window, and media capture | MIT | +| `openadapt-desktop` | Cross-platform authoring and operator cockpit | MIT | +| `openadapt-privacy` | Local sanitization and review mechanisms | MIT | +| OpenAdapt Cloud | Managed control plane, identity, billing, fleet coordination, and hosted execution | Proprietary | - classDef agent fill:#3498DB,stroke:#1A5276,color:#fff - classDef bench fill:#2ECC71,stroke:#1E8449,color:#fff - classDef task fill:#9B59B6,stroke:#6C3483,color:#fff - classDef metric fill:#E74C3C,stroke:#A93226,color:#fff +The launcher installs Flow in its base dependency set. Capability extras select +the surface-specific dependencies. The launcher does not implement a second +compiler or runtime. - class POLICY,API agent - class ADAPTER,MOCK,LIVE bench - class TASK,OBS,ACT,CHECK task - class SUCCESS,STEPS,TIME metric -``` - -## CLI Command Structure - -```mermaid -graph LR - OA[openadapt] +## Recording paths - OA --> CAP[capture] - OA --> TRN[train] - OA --> EVL[eval] - OA --> SRV[serve] - OA --> VER[version] - OA --> DOC[doctor] +The browser recorder uses Playwright. DOM identity, field geometry, and +source-time secret exclusion are required on this path. - CAP --> CS[start] - CAP --> CT[stop] - CAP --> CL[list] - CAP --> CV[view] +Native and remote demonstrations use `openadapt-capture` for screen, input, +timing, window scope, and media. Optional UIA, Accessibility, or AT-SPI +observers add structural evidence on the local desktop. RDP and Citrix remain +external pixel surfaces. - TRN --> TS[start] - TRN --> TST[status] - TRN --> TSP[stop] +All paths normalize into the recording contract that Flow compiles. - EVL --> ER[run] - EVL --> EM[mock] +## Healthy execution - classDef root fill:#2C3E50,stroke:#1A252F,color:#fff - classDef group fill:#3498DB,stroke:#1A5276,color:#fff - classDef cmd fill:#27AE60,stroke:#1E8449,color:#fff +A healthy run uses the compiled program and retained evidence. It makes no +generative-model call. The runtime resolves targets through the strongest +available deterministic evidence. It checks the live state before an action +and the declared result after an action. - class OA root - class CAP,TRN,EVL,SRV,VER,DOC group - class CS,CT,CL,CV,TS,TST,TSP,ER,EM cmd -``` +An optional model can propose a repair when policy permits it. The proposal is +not authorization. A repair remains a versioned candidate until review, +qualification, approval, and promotion complete. -## Installation Options +## Result states -```bash -# Minimal CLI only -pip install openadapt +`VERIFIED` means that the complete configured production contract confirmed the +declared business effect. `COMPLETED_UNVERIFIED` is a Demo outcome. It is not a +production success. Uncertainty after possible delivery requires +reconciliation. The runtime does not retry a possibly dispatched effect +without proof. -# Individual packages -pip install openadapt[capture] # Demonstration collection -pip install openadapt[ml] # Policy learning and inference -pip install openadapt[evals] # Benchmark evaluation -pip install openadapt[viewer] # Trajectory visualization +## Data boundary -# Optional packages -pip install openadapt[grounding] # UI element grounding -pip install openadapt[retrieval] # Trajectory retrieval -pip install openadapt[privacy] # PII/PHI scrubbing +Raw recordings and live observations stay local by default. Compilation does +not make a recording safe to upload. A derivative crosses a boundary only +after local sanitization, complete inventory, review, exact-hash approval, and +destination policy checks. -# Bundles -pip install openadapt[core] # capture + ml + evals + viewer -pip install openadapt[all] # Everything -``` +## Maturity boundary ---- +The launcher and Flow engine are Beta. Browser workflows run through the +complete clean-machine lifecycle on Linux, macOS, and Windows. Native and +remote evidence is bounded to named tasks and environments. Citrix is +code-qualified and requires a live deployment qualification. No repository +status certifies an arbitrary customer workflow. -*This architecture enables independent development and versioning of each component while maintaining a unified CLI experience.* +The former model-training architecture remains in Git history and optional +research packages. It is not the current product architecture. diff --git a/docs/cli.md b/docs/cli.md index 02ac8bdc4..84754ce17 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -1,347 +1,115 @@ -# CLI Reference +# OpenAdapt CLI reference -OpenAdapt provides a unified command-line interface for all functionality. +> **Canonical reference.** The complete current option list is at +> [docs.openadapt.ai/reference/cli](https://docs.openadapt.ai/reference/cli/). +> Run `openadapt flow --help` for the exact installed engine version. -## Global Commands +The launcher command is `openadapt`. It delegates the product lifecycle to the +installed `openadapt-flow` engine. -### Version - -Show installed package versions: - -```bash -openadapt version -``` - -Output: - -``` -openadapt: 1.0.0 -openadapt-capture: 1.0.0 -openadapt-ml: 1.0.0 -openadapt-evals: 1.0.0 -openadapt-viewer: 1.0.0 -``` - -### Doctor - -Check system requirements and configuration: - -```bash -openadapt doctor -``` - -This verifies: - -- Python version -- Required packages -- System permissions (macOS) -- GPU availability -- Environment variables - ---- - -## Capture Commands - -Commands for collecting human demonstrations. - -### capture start - -Start a new demonstration collection session. - -```bash -openadapt capture start --name [options] -``` - -**Arguments:** - -| Argument | Required | Description | -|----------|----------|-------------| -| `--name` | Yes | Name for the capture session | -| `--interval` | No | Screenshot interval in seconds (default: 0.1) | -| `--no-screenshots` | No | Disable screenshot capture | -| `--no-keyboard` | No | Disable keyboard event capture | - -**Examples:** - -```bash -# Basic demonstration collection -openadapt capture start --name login-task - -# Demonstration collection without screenshots -openadapt capture start --name audio-task --no-screenshots - -# Demonstration collection with slower screenshot interval -openadapt capture start --name slow-task --interval 1.0 -``` - -### capture stop - -Stop the current demonstration collection. - -```bash -openadapt capture stop -``` - -Alternatively, press `Ctrl+C` in the capture terminal. - -### capture list - -List all captured demonstrations. - -```bash -openadapt capture list -``` - -Output: - -``` -NAME EVENTS DURATION DATE -login-task 45 2m 30s 2026-01-16 -email-reply 23 1m 15s 2026-01-15 -form-fill 89 5m 42s 2026-01-14 -``` - -### capture view - -Open the trajectory viewer for a demonstration. - -```bash -openadapt capture view [options] -``` - -**Arguments:** - -| Argument | Required | Description | -|----------|----------|-------------| -| `` | Yes | Name of the demonstration to view | -| `--port` | No | Server port (default: 8080) | -| `--no-browser` | No | Don't open browser automatically | - -### capture delete - -Delete a demonstration. +## First run ```bash -openadapt capture delete +openadapt quickstart [--headed] [--break-it] [--out NEW_DIRECTORY] ``` ---- +`quickstart` runs the bundled synthetic workflow from recording through an +independently verified Standard-profile result. It refuses to overwrite an +existing output directory. -## Train Commands +## Flow lifecycle -Commands for policy learning from demonstrations. - -### train start - -Start policy learning from a demonstration. - -```bash -openadapt train start --capture --model [options] -``` - -**Arguments:** - -| Argument | Required | Description | -|----------|----------|-------------| -| `--capture` | Yes | Name of the demonstration to train on | -| `--model` | Yes | Model architecture | -| `--epochs` | No | Number of training epochs (default: 10) | -| `--batch-size` | No | Batch size (default: 4) | -| `--learning-rate` | No | Learning rate (default: 1e-4) | -| `--output` | No | Output directory (default: training_output/) | - -**Available Models:** - -- `qwen3vl-2b` - Qwen3-VL 2B parameters -- `qwen3vl-7b` - Qwen3-VL 7B parameters -- `llava-1.6-7b` - LLaVA 1.6 7B parameters - -**Examples:** - -```bash -# Basic policy learning -openadapt train start --capture login-task --model qwen3vl-2b - -# Policy learning with custom parameters -openadapt train start \ - --capture login-task \ - --model qwen3vl-7b \ - --epochs 20 \ - --batch-size 2 \ - --learning-rate 5e-5 -``` - -### train status - -Check policy learning progress. - -```bash -openadapt train status -``` - -Output: - -``` -Training: login-task -Model: qwen3vl-2b -Progress: Epoch 5/10 (50%) -Loss: 0.234 -ETA: 15 minutes -``` - -### train stop - -Stop the current policy learning. - -```bash -openadapt train stop -``` - -### train models - -List available model architectures. +The launcher and engine forms are equivalent: ```bash -openadapt train models +openadapt flow [options] +openadapt-flow [options] ``` ---- - -## Eval Commands - -Commands for evaluating agents. +Use one form for the complete command. `demo-record` is a Flow subcommand. It +is not a standalone executable. -### eval run +| Command | Purpose | +| --- | --- | +| `record` | Record a human demonstration on a declared surface. | +| `demo-record` | Create the bundled synthetic demonstration. | +| `compile` | Compile a recording into a deterministic bundle. | +| `lint` | Report identity, action-risk, effect, and policy gaps. | +| `certify` | Evaluate a bundle against a named policy. | +| `replay` | Run a bundle in the explicit Demo posture. | +| `run` | Admit and run a bundle with a deployment configuration. | +| `resume` | Resume a verified durable pause. | +| `visualize` | Render the compiled program for inspection. | +| `report-run` | Create a closed-schema receipt from a verified run. | +| `sanitize` | Create a sanitized derivative without changing the source. | +| `review-sanitized` | Compare a derivative with its local source. | +| `approve-sanitized` | Bind approval to the exact derivative bytes. | +| `repair` | Review, test, promote, or roll back a repair candidate. | -Run an evaluation. +The manual synthetic lifecycle is: ```bash -openadapt eval run [options] +openadapt flow demo-record --out rec +openadapt flow compile rec --out bundle --name my-task +openadapt flow lint bundle --strict +openadapt flow certify bundle --policy permissive +openadapt flow replay bundle --run-dir run ``` -**Arguments:** - -| Argument | Required | Description | -|----------|----------|-------------| -| `--checkpoint` | No* | Path to model checkpoint | -| `--agent` | No* | Agent type (api-claude, api-gpt4v) | -| `--benchmark` | Yes | Benchmark name | -| `--tasks` | No | Number of tasks (default: all) | -| `--output` | No | Output directory for results | - -*One of `--checkpoint` or `--agent` is required. - -**Available Benchmarks:** +The strict lint step refuses the unarmed demo write. The permissive policy is a +smoke gate. The replay uses the Demo profile and returns +`COMPLETED_UNVERIFIED`. -- `waa` - Windows Agent Arena -- `osworld` - OSWorld -- `webarena` - WebArena -- `mock` - Mock benchmark for testing - -**Examples:** +## Record and replay one surface ```bash -# Evaluate a trained model -openadapt eval run --checkpoint training_output/model.pt --benchmark waa - -# Evaluate Claude API agent -openadapt eval run --agent api-claude --benchmark waa - -# Run subset of tasks -openadapt eval run --agent api-claude --benchmark waa --tasks 10 +openadapt flow record --backend web --url https://your-app.example --out rec +openadapt flow replay bundle --backend web \ + --url https://your-app.example --run-dir run ``` -### eval mock - -Run a mock evaluation to test setup. +Supported selectors are `web`, `windows`, `macos`, `linux`, `rdp`, and +`citrix`. The required target flags differ by surface. Run these commands for +the installed option contract: ```bash -openadapt eval mock --tasks +openadapt flow record --help +openadapt flow replay --help ``` -**Arguments:** - -| Argument | Required | Description | -|----------|----------|-------------| -| `--tasks` | No | Number of mock tasks (default: 10) | - -### eval benchmarks - -List available benchmarks. +## Launcher diagnostics ```bash -openadapt eval benchmarks +openadapt version +openadapt doctor --backend web +openadapt deploy --backend web ``` ---- - -## Serve Commands +`doctor` checks the local capability dependencies. `deploy` performs a +read-only deployment preflight and prints the applicable Flow and Desktop +path. Neither command certifies a customer workflow. -Start the dashboard server. +## Hosted connection ```bash -openadapt serve [options] -``` - -**Arguments:** - -| Argument | Required | Description | -|----------|----------|-------------| -| `--port` | No | Server port (default: 8080) | -| `--host` | No | Host address (default: localhost) | - -Access the dashboard at `http://localhost:8080`. - ---- - -## Command Structure - -```mermaid -graph LR - OA[openadapt] - - OA --> CAP[capture] - OA --> TRN[train] - OA --> EVL[eval] - OA --> SRV[serve] - OA --> VER[version] - OA --> DOC[doctor] - - CAP --> CS[start] - CAP --> CT[stop] - CAP --> CL[list] - CAP --> CV[view] - CAP --> CD[delete] - - TRN --> TS[start] - TRN --> TST[status] - TRN --> TSP[stop] - TRN --> TM[models] - - EVL --> ER[run] - EVL --> EM[mock] - EVL --> EB[benchmarks] +openadapt flow connect +openadapt flow login --token oai_ingest_... +openadapt flow push APPROVED_DERIVATIVE --kind recording ``` ---- - -## Environment Variables +Do not upload a raw recording. The push path accepts an approved sanitized +derivative and validates its exact bytes. -| Variable | Description | -|----------|-------------| -| `ANTHROPIC_API_KEY` | API key for Claude agent | -| `OPENAI_API_KEY` | API key for GPT-4V agent | -| `OPENADAPT_CAPTURES_DIR` | Directory for captures (default: ./captures) | -| `OPENADAPT_OUTPUT_DIR` | Directory for outputs (default: ./training_output) | +## Capture and research commands ---- +The optional `capture` group exposes the supported low-level Capture component +for raw native sessions. Use `openadapt flow record` when the output must enter +the compiler directly. The `train` and `eval` groups are research surfaces and +are not required for the record-compile-replay product path. -## Exit Codes +## Exit status -| Code | Description | -|------|-------------| -| 0 | Success | -| 1 | General error | -| 2 | Invalid arguments | -| 3 | Missing dependencies | -| 4 | Permission denied | +An exit status of zero means that the requested command completed. It does not +by itself prove a business effect. Read `report.json` and the transaction +outcome. A production success requires a `VERIFIED` outcome from the complete +configured contract. diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index cb6a0992e..7a17d6d2a 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -1,128 +1,81 @@ -# Installation +# Install OpenAdapt -This guide covers how to install OpenAdapt and its sub-packages. +> **Current product path.** The canonical documentation is at +> [docs.openadapt.ai](https://docs.openadapt.ai). This repository page keeps the +> launcher install contract close to the package that implements it. -## Requirements +OpenAdapt supports Python 3.10, 3.11, and 3.12. Use a virtual environment for +an isolated install. -- **Python**: 3.10 or higher -- **Operating System**: macOS, Windows, or Linux -- **Platform-specific**: See [Permissions](permissions.md) for macOS requirements +## Browser tutorial -## Installation Options - -### Minimal CLI Only - -Install just the CLI without any sub-packages: +Install the launcher with the browser capability: ```bash -pip install openadapt +python -m pip install --upgrade 'openadapt[browser]' +openadapt quickstart ``` -This gives you the `openadapt` command with help and version information, but no actual functionality. - -### Individual Packages +On Windows `cmd.exe`, use double quotes: -Install specific functionality as needed: - -```bash -pip install openadapt[capture] # GUI capture/recording -pip install openadapt[ml] # ML training and inference -pip install openadapt[evals] # Benchmark evaluation -pip install openadapt[viewer] # HTML visualization +```bat +python -m pip install --upgrade "openadapt[browser]" +openadapt quickstart ``` -### Optional Packages +The launcher installs the compatible `openadapt-flow` engine. Do not install +the launcher and engine separately. The first browser action downloads the +matching Chromium build once. -For additional features: +The tutorial uses the bundled synthetic MockMed application. It records, +compiles, certifies, and replays one workflow. A separate read-only interface +verifies the saved record. The healthy result is `VERIFIED` under the Standard +profile with no model or Cloud call. -```bash -pip install openadapt[grounding] # UI element localization -pip install openadapt[retrieval] # Demo search/retrieval -pip install openadapt[privacy] # PII/PHI scrubbing -``` +## Capability-specific installs -### Bundles - -Install common combinations: +The base install includes the launcher and the Flow engine. Add only the +capabilities that the target workflow needs: ```bash -pip install openadapt[core] # capture + ml + evals + viewer -pip install openadapt[all] # Everything +python -m pip install 'openadapt[capture]' # local human demonstration +python -m pip install 'openadapt[capture,windows]' # Windows UI Automation +python -m pip install 'openadapt[capture,macos]' # macOS Accessibility +python -m pip install 'openadapt[capture,linux]' # Linux AT-SPI +python -m pip install 'openadapt[capture,rdp]' # RDP transport +python -m pip install 'openadapt[privacy]' # PII/PHI scrubbing ``` -## Verify Installation +These extras install platform bindings. They do not certify an arbitrary +application. Qualify each workflow against its exact application, environment, +identity rules, and independent effect verifier. -Check that OpenAdapt is installed correctly: +## Verify the install ```bash openadapt version +openadapt doctor --backend web +openadapt flow --help ``` -This shows installed package versions: - -``` -openadapt: 1.0.0 -openadapt-capture: 1.0.0 -openadapt-ml: 1.0.0 -... -``` - -Run the system check: - -```bash -openadapt doctor -``` - -This verifies system requirements and permissions. - -## Development Installation - -For contributing to OpenAdapt: +For the visual authoring and review interface, install +[OpenAdapt Desktop](https://openadapt.ai/download). -### Main Package +## Development install ```bash git clone https://github.com/OpenAdaptAI/OpenAdapt cd OpenAdapt -pip install -e ".[dev]" +python -m pip install -e '.[dev]' ``` -### Sub-packages - -Clone and install the specific sub-package you want to work on: - -```bash -git clone https://github.com/OpenAdaptAI/openadapt-ml # or other sub-package -cd openadapt-ml -pip install -e ".[dev]" -``` - -## Troubleshooting - -### Permission Denied Errors (macOS) - -See the [Permissions Guide](permissions.md) for granting necessary permissions. - -### ImportError: No module named 'openadapt_capture' - -Install the required sub-package: - -```bash -pip install openadapt[capture] -``` - -### Conflicts with Other Packages - -Use a virtual environment: - -```bash -python -m venv .venv -source .venv/bin/activate # On Windows: .venv\Scripts\activate -pip install openadapt[all] -``` +The engine source is in +[`OpenAdaptAI/openadapt-flow`](https://github.com/OpenAdaptAI/openadapt-flow). +Read its `CONTRIBUTING.md` before a package or release change. -## Next Steps +## Next steps -- [Quick Start](quickstart.md) - Record your first demonstration -- [Permissions](permissions.md) - Configure macOS permissions -- [CLI Reference](../cli.md) - Full command reference +- [Run the local tutorial](quickstart.md) +- [Read the canonical first-workflow guide](https://docs.openadapt.ai/get-started/) +- [Review current substrate evidence](https://docs.openadapt.ai/get-started/what-works-today/) +- [Read the CLI reference](https://docs.openadapt.ai/reference/cli/) diff --git a/docs/getting-started/permissions.md b/docs/getting-started/permissions.md index 29da64a03..446e26dd8 100644 --- a/docs/getting-started/permissions.md +++ b/docs/getting-started/permissions.md @@ -1,154 +1,29 @@ -# macOS Permissions Guide +# Native desktop permissions -OpenAdapt requires several system permissions on macOS to capture user interactions and replay actions. This guide covers the required permissions and how to enable them. +The maintained recording guide is at +[docs.openadapt.ai](https://docs.openadapt.ai/guides/record-your-app/). -**Compatibility**: macOS 13 Ventura, macOS 14 Sonoma, and macOS 15 Sequoia. Earlier versions may have different menu layouts. +Use `openadapt doctor --backend ` to inspect the installed capability. +The check does not grant access or certify the target application. -## Overview +Native recording and replay use operating system controls: -OpenAdapt needs three types of permissions: +| Surface | Typical local requirements | +| --- | --- | +| macOS | Screen Recording and Input Monitoring for capture; Accessibility for actuation | +| Windows | An interactive desktop; UI Automation and input rights at the target integrity level | +| Linux | An interactive X11 or approved portal session plus AT-SPI for structural evidence | +| RDP / Citrix | Access to the exact visible local client window and its input path | -| Permission | Purpose | Required For | -|------------|---------|--------------| -| **Input Monitoring** | Capture keyboard and mouse input | Recording user actions | -| **Screen Recording** | Capture screenshots | Recording screen state | -| **Accessibility** | Control mouse and keyboard | Replaying actions | +OpenAdapt must refuse an action when a required permission or target boundary +is absent. A permission failure must not become a silent success. -!!! warning "Important" - While macOS will prompt you for Input Monitoring and Screen Recording permissions on first run, it will **not** prompt for Accessibility permissions. If Accessibility permission is not granted, action replay will fail silently. - -## Which Application Needs Permissions? - -Grant permissions to the application you use to run OpenAdapt: - -- **Terminal.app** - If you run OpenAdapt from the built-in macOS Terminal -- **iTerm** - If you use iTerm2 as your terminal -- **Visual Studio Code** - If you run from the VS Code integrated terminal -- **PyCharm** or **other IDE** - If you run from an IDE's terminal -- **Python** - In some cases, the Python executable itself may need permissions - -!!! tip - If permissions don't seem to work, try granting them to both your terminal application and the Python executable. - -## Enabling Input Monitoring - -Input monitoring allows OpenAdapt to capture keyboard and mouse events during recording. - -1. Open **System Settings** (or System Preferences on older macOS) -2. Navigate to **Privacy & Security** in the sidebar -3. Click **Input Monitoring** in the right panel -4. Click the **+** button to add your terminal application -5. Toggle the switch to enable access - -## Enabling Screen Recording - -Screen recording allows OpenAdapt to capture screenshots during recording. - -1. Open **System Settings** -2. Navigate to **Privacy & Security** in the sidebar -3. Click **Screen Recording** in the right panel -4. Click the **+** button to add your terminal application -5. Toggle the switch to enable access -6. You may need to restart your terminal for changes to take effect - -## Enabling Accessibility (for Action Replay) - -Accessibility permissions allow OpenAdapt to control the mouse and keyboard during replay. - -!!! note - This permission is required for replaying recorded actions. Without it, replay will fail silently. - -1. Open **System Settings** -2. Navigate to **Privacy & Security** in the sidebar -3. Click **Accessibility** in the right panel -4. Click the **+** button to add your terminal application -5. Toggle the switch to enable access - -## Quick Access via Terminal - -You can quickly open the Privacy & Security settings from the command line: +Install the applicable capability before the check: ```bash -# Open Privacy & Security settings -open "x-apple.systempreferences:com.apple.preference.security?Privacy" - -# Open Input Monitoring directly -open "x-apple.systempreferences:com.apple.preference.security?Privacy_ListenEvent" - -# Open Screen Recording directly -open "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture" - -# Open Accessibility directly -open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility" +python -m pip install 'openadapt[capture,macos]' +openadapt doctor --backend macos ``` -## Troubleshooting - -### Permission prompts not appearing - -If you don't see a permission prompt when running OpenAdapt: - -1. Check if the permission is already granted in System Settings -2. Try removing and re-adding the application in the permissions list -3. Restart your terminal application -4. Restart your Mac if issues persist - -### Replay actions not working - -If recording works but replay does not: - -1. Verify Accessibility permission is granted -2. Check that the correct application has the permission -3. Try granting permission to both your terminal and the Python executable - -### Screen recording shows black screenshots - -1. Ensure Screen Recording permission is granted -2. Restart your terminal application after granting permission -3. Some applications may require a system restart - -### Finding the Python executable - -If you need to grant permissions to Python directly: - -```bash -# Find which Python is being used -which python - -# Or for Python 3 specifically -which python3 - -# If using a virtual environment, it will show the venv path -# Grant permissions to that specific Python executable -``` - -## Usage with OpenAdapt - -When using OpenAdapt modular packages (v1.0.0+): - -```bash -# Recording (requires Input Monitoring + Screen Recording) -openadapt capture start --name "my-task" - -# Replaying (requires Accessibility) -openadapt replay --strategy visual -``` - -The CLI commands are run from your terminal, so ensure your terminal application has the necessary permissions. - -## Windows Permissions - -On Windows, you may need to run your terminal as Administrator for input capture to work correctly: - -1. Right-click on your terminal application -2. Select "Run as administrator" -3. Run OpenAdapt commands from the elevated terminal - -## Linux Permissions - -On Linux, you may need to add your user to the `input` group: - -```bash -sudo usermod -a -G input $USER -# Log out and back in for changes to take effect -``` +Run `openadapt flow record --help` and `openadapt flow replay --help` for the +exact installed target options. diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md index 29e139fdf..aaa0cc06b 100644 --- a/docs/getting-started/quickstart.md +++ b/docs/getting-started/quickstart.md @@ -1,138 +1,85 @@ -# Quick Start +# Run the OpenAdapt quickstart -This guide walks you through collecting a demonstration, learning a policy, and evaluating the agent. +> **Current product path.** The canonical walkthrough is at +> [docs.openadapt.ai/get-started](https://docs.openadapt.ai/get-started/). -## Prerequisites +## Run the verified tutorial -- OpenAdapt installed with required packages: `pip install openadapt[all]` -- macOS users: [Grant required permissions](permissions.md) - -## 1. Collect a Demonstration - -Start capturing your screen and inputs: +Use Python 3.10, 3.11, or 3.12: ```bash -openadapt capture start --name my-task +python -m pip install --upgrade 'openadapt[browser]' +openadapt quickstart ``` -Now perform the task you want to automate: - -1. Click on applications -2. Type text -3. Navigate menus -4. Complete your workflow +On Windows `cmd.exe`, use double quotes around the install target. -When finished, stop the capture: +The command runs one complete local lifecycle against synthetic MockMed data: -```bash -# Press Ctrl+C in the terminal, or: -openadapt capture stop -``` +1. It records a demonstrated browser workflow. +2. It compiles the recording into an inspectable bundle. +3. It certifies the bundle with the shipped tutorial policy. +4. It runs the bundle under the Standard profile. +5. It verifies the saved record through a separate read-only interface. +6. It writes a report and a privacy-safe synthetic receipt. -## 2. View the Trajectory +The healthy run returns `VERIFIED`. It makes no model or Cloud call. -Inspect what was captured: +Inspect the artifacts: ```bash -openadapt capture view my-task +openadapt flow visualize openadapt-quickstart/bundle --out graph.html +openadapt flow lint openadapt-quickstart/bundle ``` -This opens a trajectory viewer showing: - -- Observations (screenshots) at each step -- Actions (mouse and keyboard events) -- Timing information - -## 3. List Your Demonstrations - -See all collected demonstrations: +Run the same certified bundle against a fault-injecting backend: ```bash -openadapt capture list -``` - -Output: - -``` -NAME EVENTS DURATION DATE -my-task 45 2m 30s 2026-01-16 -login-demo 23 1m 15s 2026-01-15 +openadapt quickstart --break-it --out openadapt-quickstart-broken ``` -## 4. Learn a Policy +The application displays success, but the backend does not save the record. +The independent effect verifier detects the mismatch and returns `HALTED`. -Learn an agent policy from your demonstration trajectory: +## Run the manual demo lifecycle -```bash -openadapt train start --capture my-task --model qwen3vl-2b -``` - -Monitor policy learning progress: +The engine command is `openadapt-flow`. The launcher provides the equivalent +two-word form `openadapt flow`. There is no standalone `demo-record` command. ```bash -openadapt train status +openadapt flow demo-record --out rec +openadapt flow compile rec --out bundle --name my-task +openadapt flow lint bundle --strict +openadapt flow certify bundle --policy permissive +openadapt flow replay bundle --run-dir run ``` -Policy learning creates a checkpoint file in `training_output/`. +The strict lint step returns a nonzero exit code. The bundled manual demo has +an unarmed irreversible click. The permissive certification is only a smoke +gate. The Demo replay returns `COMPLETED_UNVERIFIED`, not `VERIFIED`. -## 5. Evaluate the Agent +Use `openadapt quickstart` for the effect-verified first run. For a real +workflow, add the application boundary, action risks, identity requirements, +effect verifier, fault cases, and deployment policy before production use. -Test your trained policy on a benchmark: +## Record a browser workflow ```bash -openadapt eval run --checkpoint training_output/model.pt --benchmark waa +openadapt flow record --backend web --url https://your-app.example --out rec +openadapt flow compile rec --out bundle --name my-workflow +openadapt flow replay bundle --backend web \ + --url https://your-app.example --run-dir run ``` -Or run a mock evaluation to verify the setup: - -```bash -openadapt eval mock --tasks 10 -``` - -## 6. Evaluate an API Agent - -Test API-based agents (Claude, GPT-4V): - -```bash -# Set your API key -export ANTHROPIC_API_KEY=your-key-here - -# Run evaluation -openadapt eval run --agent api-claude --benchmark waa -``` - -## Complete Workflow Example - -Here is a complete example demonstrating the full pipeline: - -```bash -# 1. Install OpenAdapt -pip install openadapt[all] - -# 2. Check system requirements -openadapt doctor - -# 3. Collect a demonstration -openadapt capture start --name email-reply -# ... perform the task ... -# Press Ctrl+C to stop - -# 4. View the trajectory -openadapt capture view email-reply - -# 5. Learn a policy -openadapt train start --capture email-reply --model qwen3vl-2b - -# 6. Wait for policy learning to complete -openadapt train status - -# 7. Evaluate the agent -openadapt eval run --checkpoint training_output/model.pt --benchmark waa -``` +Password fields and fields declared with `--secret` exclude their values at +record time. Read the +[canonical recording guide](https://docs.openadapt.ai/guides/record-your-app/) +before a real-data demonstration. -## Next Steps +## Product boundary -- [CLI Reference](../cli.md) - Full command documentation -- [Architecture](../architecture.md) - How OpenAdapt works -- [Packages](../packages/index.md) - Explore individual packages -- [Contributing](../contributing.md) - Help improve OpenAdapt +The launcher and Flow engine are Beta. Browser workflows run end to end in the +three-OS clean-machine lifecycle. Native and remote evidence is task- and +environment-specific. Citrix support is code-qualified and still requires a +real deployment qualification. A runnable workflow is not automatically a +certified production workflow. diff --git a/docs/index.md b/docs/index.md index 1766711df..eab6e923a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -15,6 +15,7 @@ Start here: - [Canonical engine](https://github.com/OpenAdaptAI/openadapt-flow) - [Launcher and migration guidance](https://github.com/OpenAdaptAI/OpenAdapt#readme) -The remaining files under this directory describe earlier package/training -architectures. They are historical reference, not current onboarding or product -claims. +The getting-started, CLI, architecture, and package-map pages keep selected +install and package contracts close to the code. Files that identify themselves +as legacy, frozen, design-only, or architecture evolution are historical +reference. They are not current onboarding or product claims. diff --git a/docs/packages/capture.md b/docs/packages/capture.md index b27b6d846..8500c2b3d 100644 --- a/docs/packages/capture.md +++ b/docs/packages/capture.md @@ -1,138 +1,78 @@ # openadapt-capture -Demonstration collection, observation-action capture, and storage. +**Lifecycle: Beta.** `openadapt-capture` is the canonical OpenAdapt component +for native screen, mouse, keyboard, timing, window-scope, and media capture. It +is not an experimental prototype. -**Repository**: [OpenAdaptAI/openadapt-capture](https://github.com/OpenAdaptAI/openadapt-capture) +Repository: +[`OpenAdaptAI/openadapt-capture`](https://github.com/OpenAdaptAI/openadapt-capture) -## Installation +## Install ```bash -pip install openadapt[capture] -# or -pip install openadapt-capture +python -m pip install 'openadapt[capture]' ``` -## Overview - -The capture package collects human demonstrations from desktop and web GUIs, including: - -- Observations (screenshots) at configurable intervals -- Actions: mouse events (clicks, movement, scrolling) -- Actions: keyboard events (key presses, text input) -- Window and application context -- Timing information for trajectory reconstruction - -## CLI Commands - -### Start Demonstration Collection +Add the replay surface extra when the workflow needs one: ```bash -openadapt capture start --name my-task +python -m pip install 'openadapt[capture,windows]' +python -m pip install 'openadapt[capture,macos]' +python -m pip install 'openadapt[capture,linux]' +python -m pip install 'openadapt[capture,rdp]' ``` -Options: - -- `--name` - Name for the capture session (required) -- `--interval` - Screenshot interval in seconds (default: 0.1) -- `--no-screenshots` - Disable screenshot capture -- `--no-keyboard` - Disable keyboard capture +## Product role -### Stop Demonstration Collection +Flow uses Capture for native and remote-display demonstrations. The Flow +adapter consumes Capture's public `CaptureSession` API and converts each +action, aligned frame, window hint, and optional structural observation into +the recording contract that the compiler accepts. ```bash -openadapt capture stop +openadapt flow record --backend macos --window TextEdit --out rec +openadapt flow compile rec --out bundle --name my-task ``` -Or press `Ctrl+C` in the capture terminal. - -### List Demonstrations +Use the exact backend and target options from the installed engine: ```bash -openadapt capture list +openadapt flow record --help +openadapt flow replay --help ``` -### View a Demonstration Trajectory +Capture needs an interactive desktop and the applicable operating system +permissions. RDP and Citrix capture the visible local client window. They do +not claim access to a remote accessibility tree. -```bash -openadapt capture view my-task -``` +## Direct raw session -### Delete a Demonstration +The compatibility launcher can start a raw Capture session: ```bash -openadapt capture delete my-task -``` - -## Python API - -```python -from openadapt_capture import CaptureSession, Recorder - -# Create a capture session -session = CaptureSession(name="my-task") - -# Start recording -recorder = Recorder(session) -recorder.start() - -# ... user demonstrates the task ... - -# Stop recording -recorder.stop() - -# Access captured trajectory data -actions = session.get_actions() -observations = session.get_observations() # screenshots -``` - -## Data Format - -Demonstrations are stored as JSON/Parquet files: - -``` -demonstrations/ - my-task/ - metadata.json # Session metadata - actions.parquet # Action data (observation-action pairs) - observations/ # Screenshot images (observations) - 0001.png - 0002.png - ... -``` - -### Action Schema - -```python -{ - "timestamp": float, # Unix timestamp - "action_type": str, # "click", "type", "scroll", etc. - "data": { - # Action-specific data - }, - "observation_id": int # Reference to observation (screenshot) -} +openadapt capture start --name my-task ``` -## Key Exports +Stop it with Ctrl-C in the same terminal. The separate `openadapt capture stop` +command does not control another process and returns non-success. Use +`openadapt flow record` when the output must compile directly. -| Export | Description | -|--------|-------------| -| `CaptureSession` | Manages a demonstration collection session | -| `Recorder` | Captures observation-action pairs | -| `Action` | Represents a user action | -| `Observation` | Represents an observation (screenshot) | -| `Trajectory` | Sequence of observation-action pairs | +## Storage and privacy -## Platform Support +A session stores structured events plus time-aligned media in its capture +directory. Consumers must use `CaptureSession.load()`, `.actions()`, and +`.get_frame_at()` instead of reading the private database schema. -| Platform | Status | -|----------|--------| -| macOS | Full support (requires [permissions](../getting-started/permissions.md)) | -| Windows | Full support | -| Linux | Full support | +Raw screenshots and typed values can contain sensitive data. Keep the source +inside its trusted boundary. Compilation does not make it safe to upload. Use +the local sanitize, review, and exact-hash approval path before any artifact +crosses a boundary. -## Related Packages +## Evidence boundary -- [openadapt-privacy](privacy.md) - Scrub PII/PHI from demonstrations -- [openadapt-viewer](viewer.md) - Visualize trajectories -- [openadapt-ml](ml.md) - Learn policies from demonstrations +Required CI validates the released Capture API, action conversion, frame +alignment, coordinate scaling, secret exclusion, structural observations, and +all Flow desktop selectors. Native actuation evidence remains bound to a named +task, application, operating system, and verifier. Review the +[current capability matrix](https://docs.openadapt.ai/get-started/what-works-today/) +before a deployment claim. diff --git a/docs/packages/index.md b/docs/packages/index.md index 3806b2a04..23fe62318 100644 --- a/docs/packages/index.md +++ b/docs/packages/index.md @@ -1,163 +1,38 @@ -# Packages Overview +# OpenAdapt package map -OpenAdapt v1.0+ uses a modular meta-package architecture. The main `openadapt` package provides a unified CLI and depends on focused sub-packages via PyPI. +The product path uses the launcher, Flow engine, Capture component, Desktop +cockpit, privacy tools, and optional managed control plane. -## Architecture +| Package | Current role | Install route | +| --- | --- | --- | +| `openadapt` | Beta launcher and unified CLI | `pip install openadapt` | +| `openadapt-flow` | Beta compiler and governed runtime; installed by the launcher | `pip install openadapt-flow` for engine-only use | +| `openadapt-capture` | Beta native capture component | `pip install 'openadapt[capture]'` | +| `openadapt-privacy` | Local privacy and sanitization support | `pip install 'openadapt[privacy]'` | +| `openadapt-desktop` | Beta visual authoring and operator application | [Download an installer](https://openadapt.ai/download) | -```mermaid -graph TD - OA[openadapt
Meta-package] - - OA -->|capture| CAP[openadapt-capture] - OA -->|ml| MLP[openadapt-ml] - OA -->|evals| EVL[openadapt-evals] - OA -->|viewer| VWR[openadapt-viewer] - OA -->|grounding| GRD[openadapt-grounding] - OA -->|retrieval| RET[openadapt-retrieval] - OA -->|privacy| PRV[openadapt-privacy] - - OA -->|core| CORE[Core Bundle] - CORE --> CAP - CORE --> MLP - CORE --> EVL - CORE --> VWR - - OA -->|all| ALL[Full Bundle] - ALL --> CORE - ALL --> GRD - ALL --> RET - ALL --> PRV - - classDef meta fill:#2C3E50,stroke:#1A252F,color:#fff - classDef core fill:#27AE60,stroke:#1E8449,color:#fff - classDef optional fill:#E67E22,stroke:#A04000,color:#fff - classDef bundle fill:#8E44AD,stroke:#5B2C6F,color:#fff - - class OA meta - class CAP,MLP,EVL,VWR core - class GRD,RET,PRV optional - class CORE,ALL bundle -``` - -## Core Packages - -These packages provide the essential functionality for recording, training, evaluating, and visualizing. - -| Package | Description | Install Extra | -|---------|-------------|---------------| -| [openadapt-capture](capture.md) | GUI recording, event capture, storage | `capture` | -| [openadapt-ml](ml.md) | ML engine, training, inference | `ml` | -| [openadapt-evals](evals.md) | Benchmark evaluation infrastructure | `evals` | -| [openadapt-viewer](viewer.md) | HTML visualization components | `viewer` | - -Install all core packages: +Install the browser tutorial path: ```bash -pip install openadapt[core] +python -m pip install --upgrade 'openadapt[browser]' +openadapt quickstart ``` -## Optional Packages - -These packages provide enhanced functionality for specific use cases. - -| Package | Description | Install Extra | -|---------|-------------|---------------| -| [openadapt-grounding](grounding.md) | UI element localization | `grounding` | -| [openadapt-retrieval](retrieval.md) | Multimodal demonstration retrieval | `retrieval` | -| [openadapt-privacy](privacy.md) | PII/PHI scrubbing | `privacy` | - -Install all packages: - -```bash -pip install openadapt[all] -``` - -## Installation Options - -### Individual Packages +Install a native or remote capability: ```bash -pip install openadapt[capture] # GUI capture/recording -pip install openadapt[ml] # ML training and inference -pip install openadapt[evals] # Benchmark evaluation -pip install openadapt[viewer] # HTML visualization -pip install openadapt[grounding] # UI element localization -pip install openadapt[retrieval] # Demo search/retrieval -pip install openadapt[privacy] # PII/PHI scrubbing +python -m pip install 'openadapt[capture,windows]' +python -m pip install 'openadapt[capture,macos]' +python -m pip install 'openadapt[capture,linux]' +python -m pip install 'openadapt[capture,rdp]' ``` -### Multiple Packages - -```bash -pip install openadapt[capture,ml,evals] -``` - -### Bundles - -```bash -pip install openadapt[core] # capture + ml + evals + viewer -pip install openadapt[all] # Everything -``` - -## Data Flow - -```mermaid -flowchart LR - subgraph Record["1. Record"] - A[User Demo] --> B[Capture Session] - B --> C[Screenshots + Events] - end - - subgraph Store["2. Store"] - C --> D[JSON/Parquet Files] - D --> E[Demo Library] - end - - subgraph Train["3. Train"] - E --> F[Data Loading] - F --> G[Model Training] - G --> H[Checkpoint] - end - - subgraph Deploy["4. Deploy"] - H --> I[Agent Policy] - I --> J[Inference] - J --> K[Action Replay] - end - - subgraph Evaluate["5. Evaluate"] - I --> L[Benchmark Runner] - L --> M[Metrics] - M --> N[Results Report] - end - - GROUND[Grounding] -.-> J - RETRIEVE[Retrieval] -.-> F - PRIV[Privacy] -.-> C -``` - -## Package Repositories - -Each package is maintained in its own repository: - -| Package | Repository | -|---------|------------| -| openadapt | [OpenAdaptAI/OpenAdapt](https://github.com/OpenAdaptAI/OpenAdapt) | -| openadapt-capture | [OpenAdaptAI/openadapt-capture](https://github.com/OpenAdaptAI/openadapt-capture) | -| openadapt-ml | [OpenAdaptAI/openadapt-ml](https://github.com/OpenAdaptAI/openadapt-ml) | -| openadapt-evals | [OpenAdaptAI/openadapt-evals](https://github.com/OpenAdaptAI/openadapt-evals) | -| openadapt-viewer | [OpenAdaptAI/openadapt-viewer](https://github.com/OpenAdaptAI/openadapt-viewer) | -| openadapt-grounding | [OpenAdaptAI/openadapt-grounding](https://github.com/OpenAdaptAI/openadapt-grounding) | -| openadapt-retrieval | [OpenAdaptAI/openadapt-retrieval](https://github.com/OpenAdaptAI/openadapt-retrieval) | -| openadapt-privacy | [OpenAdaptAI/openadapt-privacy](https://github.com/OpenAdaptAI/openadapt-privacy) | - -## Contributing - -To contribute to a specific package: - -1. Fork and clone the package repository -2. Install in development mode: `pip install -e ".[dev]"` -3. Make your changes -4. Submit a pull request +The `openadapt-ml`, `openadapt-evals`, `openadapt-viewer`, +`openadapt-grounding`, and `openadapt-retrieval` packages are research or +historical surfaces. They are not required to record, compile, replay, or +verify a workflow. The compatibility extras remain available for existing +users, but new onboarding must not lead with model training. -See [Contributing](../contributing.md) for more details. +See the [project map](https://docs.openadapt.ai/concepts/ecosystem/) and the +[current capability evidence](https://docs.openadapt.ai/get-started/what-works-today/) +for the maintained status of each surface. diff --git a/docs/permissions-macos.md b/docs/permissions-macos.md index bbc4e86ec..3afaf6ea0 100644 --- a/docs/permissions-macos.md +++ b/docs/permissions-macos.md @@ -1,145 +1,18 @@ -# macOS Permissions Guide +# macOS permissions -OpenAdapt requires several system permissions on macOS to capture user interactions and replay actions. This guide covers the required permissions and how to enable them. +This historical path now points to the maintained +[recording guide](https://docs.openadapt.ai/guides/record-your-app/). -**Compatibility**: macOS 13 Ventura, macOS 14 Sonoma, and macOS 15 Sequoia. Earlier versions may have different menu layouts. - -## Overview - -OpenAdapt needs three types of permissions: - -| Permission | Purpose | Required For | -|------------|---------|--------------| -| **Input Monitoring** | Capture keyboard and mouse input | Recording user actions | -| **Screen Recording** | Capture screenshots | Recording screen state | -| **Accessibility** | Control mouse and keyboard | Replaying actions | - -**Important**: While macOS will prompt you for Input Monitoring and Screen Recording permissions on first run, it will **not** prompt for Accessibility permissions. If Accessibility permission is not granted, action replay will fail silently. - -## Which Application Needs Permissions? - -Grant permissions to the application you use to run OpenAdapt: - -- **Terminal.app** - If you run OpenAdapt from the built-in macOS Terminal -- **iTerm** - If you use iTerm2 as your terminal -- **Visual Studio Code** - If you run from the VS Code integrated terminal -- **PyCharm** or **other IDE** - If you run from an IDE's terminal -- **Python** - In some cases, the Python executable itself may need permissions - -**Tip**: If permissions don't seem to work, try granting them to both your terminal application and the Python executable. - -## Enabling Input Monitoring - -Input monitoring allows OpenAdapt to capture keyboard and mouse events during recording. - -1. Open **System Settings** (or System Preferences on older macOS) -2. Navigate to **Privacy & Security** in the sidebar -3. Click **Input Monitoring** in the right panel -4. Click the **+** button to add your terminal application -5. Toggle the switch to enable access - -![Enabling input monitoring](assets/macOS_input_monitoring.png) - -## Enabling Screen Recording - -Screen recording allows OpenAdapt to capture screenshots during recording. - -1. Open **System Settings** -2. Navigate to **Privacy & Security** in the sidebar -3. Click **Screen Recording** in the right panel -4. Click the **+** button to add your terminal application -5. Toggle the switch to enable access -6. You may need to restart your terminal for changes to take effect - -![Enabling screen recording](assets/macOS_screen_recording.png) - -## Enabling Accessibility (for Action Replay) - -Accessibility permissions allow OpenAdapt to control the mouse and keyboard during replay. - -**Note**: This permission is required for replaying recorded actions. Without it, replay will fail silently. - -1. Open **System Settings** -2. Navigate to **Privacy & Security** in the sidebar -3. Click **Accessibility** in the right panel -4. Click the **+** button to add your terminal application -5. Toggle the switch to enable access - -![Enabling accessibility](assets/macOS_accessibility.png) - -## Quick Access via Terminal - -You can quickly open the Privacy & Security settings from the command line: +Install the macOS recording and replay capabilities: ```bash -# Open Privacy & Security settings -open "x-apple.systempreferences:com.apple.preference.security?Privacy" - -# Open Input Monitoring directly -open "x-apple.systempreferences:com.apple.preference.security?Privacy_ListenEvent" - -# Open Screen Recording directly -open "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture" - -# Open Accessibility directly -open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility" -``` - -## Troubleshooting - -### Permission prompts not appearing - -If you don't see a permission prompt when running OpenAdapt: - -1. Check if the permission is already granted in System Settings -2. Try removing and re-adding the application in the permissions list -3. Restart your terminal application -4. Restart your Mac if issues persist - -### Replay actions not working - -If recording works but replay does not: - -1. Verify Accessibility permission is granted -2. Check that the correct application has the permission -3. Try granting permission to both your terminal and the Python executable - -### Screen recording shows black screenshots - -1. Ensure Screen Recording permission is granted -2. Restart your terminal application after granting permission -3. Some applications may require a system restart - -### Finding the Python executable - -If you need to grant permissions to Python directly: - -```bash -# Find which Python is being used -which python - -# Or for Python 3 specifically -which python3 - -# If using a virtual environment, it will show the venv path -# Grant permissions to that specific Python executable -``` - -## Usage with New Modular Architecture - -When using the new OpenAdapt modular packages (v1.0.0+): - -```bash -# Recording (requires Input Monitoring + Screen Recording) -openadapt capture start --name "my-task" - -# Replaying (requires Accessibility) -openadapt replay --strategy visual +python -m pip install 'openadapt[capture,macos]' +openadapt doctor --backend macos ``` -The CLI commands are run from your terminal, so ensure your terminal application has the necessary permissions. - -## Related Documentation +The local host typically needs Screen Recording and Input Monitoring for +capture. It needs Accessibility for actuation. Grant access to the exact +terminal, Desktop application, or signed executable that runs OpenAdapt. -- [Legacy Codebase Freeze](./LEGACY_FREEZE.md) - Information about the transition to the new modular architecture -- [OpenAdapt Documentation](https://github.com/OpenAdaptAI/OpenAdapt) - Main project documentation +OpenAdapt must return a non-success result when a required permission is +absent. Do not treat process exit alone as proof of a business effect. diff --git a/openadapt/__init__.py b/openadapt/__init__.py index e755485d8..17cfb0344 100644 --- a/openadapt/__init__.py +++ b/openadapt/__init__.py @@ -6,8 +6,8 @@ research packages: pip install openadapt # launcher + openadapt-flow - pip install openadapt[capture] # experimental native capture - pip install openadapt[privacy] # experimental privacy scrubbing + pip install openadapt[capture] # Beta native capture component + pip install openadapt[privacy] # local privacy and scrubbing support pip install openadapt[ml,evals] # research toolkits """ diff --git a/openadapt/cli.py b/openadapt/cli.py index 847d40357..1e5761a9c 100644 --- a/openadapt/cli.py +++ b/openadapt/cli.py @@ -49,68 +49,6 @@ def list_commands(self, ctx): return sorted(commands, key=lambda name: (name != "flow", name)) -_FLOW_PASSTHROUGH_COMMANDS = { - "record": ( - "Record a human demonstration on this interactive host (web browser " - "by default; --backend windows/macos/linux/rdp/citrix selects the " - "intended replay substrate)." - ), - "replay": ( - "Replay a bundle through web/windows/macos/linux/rdp/citrix; all " - "engine backend, target, config, and governance flags pass through." - ), - "induce": "Induce a parameterized program from multiple recordings.", - "run": "Run a bundle under a fail-closed deployment configuration.", - "resume": "Resume a durably paused run from its verified checkpoint.", - "approve": "Authorize a durably paused run to resume.", - "bench": "Benchmark deterministic replay against the bundled fixture.", - "benchmark": "Compare replay with an optional computer-use agent arm.", - "disambiguate": "Resolve compile-time ambiguity without guessing.", - "emit-skill": "Emit an Agent Skills folder for a bundle.", - "emit-mcp": "Emit a standalone MCP server for a bundle.", - "teach": "Teach a governed correction after a halt.", - "connect": "Connect this computer to an authenticated Cloud workspace.", - "login": "Validate and store a hosted ingest token.", - "sanitize": "Create a verified sanitized derivative locally.", - "review-sanitized": "Review original and sanitized content locally.", - "approve-sanitized": "Approve and freeze exact sanitized bytes.", - "validate-hosted": "Bind local evidence to an expiring hosted challenge.", - "push": "Upload an approved sanitized derivative.", - "report-break": "Upload a schema-minimized halt diagnostic.", -} - - -class _FlowPassthroughGroup(click.Group): - """Delegate engine commands not wrapped by this compatibility launcher.""" - - def list_commands(self, ctx): - commands = set(super().list_commands(ctx)) - commands.update(_FLOW_PASSTHROUGH_COMMANDS) - return sorted(commands) - - def get_command(self, ctx, cmd_name): - command = super().get_command(ctx, cmd_name) - if command is not None: - return command - - @click.command( - name=cmd_name, - help=_FLOW_PASSTHROUGH_COMMANDS.get( - cmd_name, "Delegate this command to openadapt-flow." - ), - context_settings={ - "ignore_unknown_options": True, - "allow_extra_args": True, - "help_option_names": [], - }, - ) - @click.pass_context - def passthrough(command_ctx): - _run_flow([cmd_name, *command_ctx.args]) - - return passthrough - - @click.group(cls=_FlowFirstGroup) @click.version_option(version=__version__, prog_name="openadapt") def main(): @@ -123,10 +61,19 @@ def main(): \b Quick Start: + python -m pip install --upgrade 'openadapt[browser]' + openadapt quickstart + + \b + Manual Demo Lifecycle: openadapt flow demo-record --out rec openadapt flow compile rec --out bundle --name demo - openadapt flow lint bundle + openadapt flow lint bundle --strict + openadapt flow certify bundle --policy permissive openadapt flow replay bundle + + The manual demo is runnable but not certified for consequential work. + Use `openadapt quickstart` for the effect-verified first run. """ pass @@ -263,6 +210,7 @@ def quickstart(out: Path, headed: bool, break_it: bool) -> None: _SECRET_REFERENCE = re.compile(r"^(?:env:[A-Z][A-Z0-9_]*|keychain:[^/\s]+/[^/\s]+)$") _SUPPORTED_FLOW_RANGE = ">=1.29.0,<2.0.0" +_RDP_INSTALL_COMMAND = "python -m pip install 'openadapt[rdp]'" def _supported_flow_version(value: str) -> bool: @@ -359,6 +307,15 @@ def deploy(backend: str, secret_ref: tuple[str, ...]) -> None: " [SETUP] install the web extra before recording or replay: " "python -m pip install 'openadapt[browser]'" ) + elif backend == "rdp": + if find_spec("aardwolf") is not None: + click.echo(" [OK] RDP transport dependency is installed") + else: + failures.append("rdp") + click.echo( + " [MISSING] RDP transport dependency is not installed. Run: " + + _RDP_INSTALL_COMMAND + ) else: click.echo( f" [SETUP] {backend} readiness is checked by Flow when the " @@ -432,31 +389,24 @@ def deploy(backend: str, secret_ref: tuple[str, ...]) -> None: ) -@main.group(cls=_FlowPassthroughGroup) -def flow(): - """Record, compile, and replay workflows (the demonstration compiler). - - Compile a recording into deterministic local replay. Supported drift can - be re-resolved; configured identity and verification gates halt on failure. - - \b - Examples: - openadapt flow demo-record --out rec - openadapt flow compile rec --out bundle --name demo - openadapt flow replay bundle - openadapt flow lint bundle - openadapt flow certify bundle --policy clinical-write - openadapt flow sanitize rec --kind recording --out rec-sanitized - openadapt flow review-sanitized rec-sanitized --original rec - openadapt flow approve-sanitized rec-sanitized --original rec --reviewer USER - openadapt flow login --token oai_ingest_... - openadapt flow push rec-sanitized --kind recording +@main.command( + "flow", + context_settings={ + "ignore_unknown_options": True, + "allow_extra_args": True, + # Flow owns its argparse help. Do not let Click consume --help first. + "help_option_names": [], + }, +) +@click.pass_context +def flow(command_ctx: click.Context) -> None: + """Run the canonical demonstration compiler and governed runtime. - \b - The standalone `openadapt-flow ` command keeps working and behaves - identically; `openadapt flow ` is the recommended path. + Every argument passes to openadapt-flow unchanged. This keeps the launcher + command list, help, options, validation, and exit codes identical to the + installed engine version. """ - pass + _run_flow(list(command_ctx.args)) @main.command("connect") @@ -513,78 +463,6 @@ def connect(pairing, uri, host, device_name, destination_kind, trusted_host): _run_flow(argv) -# NOTE: `record` and `replay` are intentionally NOT wrapped with explicit -# click options. They delegate through _FlowPassthroughGroup so every engine -# option (--backend web/windows/macos/linux/rdp/citrix, --config, --agent-url, -# --macos-app, --linux-app, --rdp-host, ...) forwards verbatim and new engine -# options never need a launcher release. Earlier explicit wrappers hid backend -# options or drifted behind the engine. - - -@flow.command("demo-record") -@click.option("--out", required=True, help="Recording output directory") -@click.option( - "--note-text", - default="Follow-up in 2 weeks; BP recheck.", - help="Note text typed during the demo (recorded as a parameter)", -) -@click.option("--param-name", default="note", help="Parameter name for the note") -@click.option("--drift", default=None, help="Comma-separated MockMed drift modes") -@click.option("--headed", is_flag=True, help="Run the browser headed") -def flow_demo_record(out, note_text, param_name, drift, headed): - """Serve the bundled MockMed app and record the canonical triage demo.""" - argv = [ - "demo-record", - "--out", - out, - "--note-text", - note_text, - "--param-name", - param_name, - ] - if drift: - argv += ["--drift", drift] - if headed: - argv.append("--headed") - _run_flow(argv) - - -@flow.command("compile") -@click.argument("recording") -@click.option("--out", required=True, help="Output bundle directory") -@click.option("--name", required=True, help="Workflow name") -def flow_compile(recording, out, name): - """Compile a recording directory into a workflow bundle.""" - _run_flow(["compile", recording, "--out", out, "--name", name]) - - -@flow.command("lint") -@click.argument("bundle") -@click.option( - "--strict", - is_flag=True, - help="Exit nonzero on warnings too (default: only on errors)", -) -def flow_lint(bundle, strict): - """Report a bundle's coverage gaps; exits nonzero by severity.""" - argv = ["lint", bundle] - if strict: - argv.append("--strict") - _run_flow(argv) - - -@flow.command("certify") -@click.argument("bundle") -@click.option( - "--policy", - required=True, - help="Policy YAML path, or a built-in name (permissive, clinical-write)", -) -def flow_certify(bundle, policy): - """Enforce a safety policy on a bundle (refuse it if it fails).""" - _run_flow(["certify", bundle, "--policy", policy]) - - # ============================================================================= # Capture Commands # ============================================================================= @@ -649,10 +527,13 @@ def capture_start(name: str, video: bool, audio: bool): @capture.command("stop") def capture_stop(): - """Stop the current capture session.""" - click.echo("Stopping active capture session...") - # TODO: Implement stop via signal/file - click.echo("Note: Use Ctrl+C in the capture terminal to stop") + """Explain how to stop a capture started in another terminal.""" + raise click.ClickException( + "No separate capture-stop control channel is available. Stop the capture " + "with Ctrl+C in the recorder terminal. A separate stop command will remain " + "unavailable until Capture provides an authenticated, owner-only local " + "control channel." + ) @capture.command("list") @@ -1067,8 +948,22 @@ def doctor(backend: str | None): from importlib.util import find_spec + failures = [] click.echo("\nSelected execution surface:") - if backend and backend != "web": + if backend == "rdp": + if find_spec("aardwolf") is not None: + click.echo( + " [OK] rdp: browser support is not required and the RDP " + "transport dependency is installed. Flow checks the target " + "and credentials when it opens the connection." + ) + else: + failures.append("rdp") + click.echo( + " [MISSING] rdp: the RDP transport dependency is not installed. " + "Run: " + _RDP_INSTALL_COMMAND + ) + elif backend and backend != "web": click.echo( f" [OK] {backend}: browser support is not required; " "no Playwright or Chromium setup will run. The selected " @@ -1164,6 +1059,12 @@ def doctor(backend: str | None): else: click.echo(f" [--] {key} not set") + if failures: + raise click.ClickException( + "System check failed. Install each required dependency shown as " + "[MISSING], and then run this command again." + ) + if __name__ == "__main__": main() diff --git a/pyproject.toml b/pyproject.toml index dd3bde12a..9b5483eaf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,15 +40,18 @@ dependencies = [ # Individual packages browser = [ # Web recording/replay only. The base launcher stays lightweight for - # native desktop, RDP, and Citrix users; Chromium remains lazy on first use. - "playwright>=1.44", + # native desktop, RDP, and Citrix users. Select Flow's browser contract + # instead of duplicating its current driver dependency here, so future + # browser-runtime changes remain compatible across the package boundary. + # Chromium remains lazy on first use. + "openadapt-flow[browser]>=1.29.0,<2.0.0", ] # Local human desktop recording. Flow owns the supported capture adapter # contract; the direct floor prevents an already-installed pre-1.0 Capture # from satisfying the launcher's public recording path. FFmpeg remains a # separately provisioned executable, not a Python-package dependency. capture = [ - "openadapt-capture>=1.0.4,<2.0.0", + "openadapt-capture>=1.2.0,<2.0.0", "openadapt-flow[capture]>=1.29.0,<2.0.0", ] # Replay substrate dependencies stay separate from capture: recording observes diff --git a/scripts/quickstart_lifecycle.py b/scripts/quickstart_lifecycle.py new file mode 100644 index 000000000..e0ad88c36 --- /dev/null +++ b/scripts/quickstart_lifecycle.py @@ -0,0 +1,315 @@ +#!/usr/bin/env python3 +"""Verify the public launcher quickstart from wheel install through uninstall.""" + +from __future__ import annotations + +import argparse +import glob +import hashlib +import json +import os +import subprocess +import sys +import venv +from pathlib import Path +from typing import Sequence + +_UNHANDLED_RUNTIME_MARKERS = ( + "Task was destroyed but it is pending!", + "Future exception was never retrieved", +) + + +def _resolve_wheel(pattern: str, distribution: str) -> Path: + matches = [Path(item).resolve() for item in glob.glob(pattern)] + if len(matches) != 1: + raise ValueError( + f"{distribution} wheel pattern must match exactly one file; " + f"{pattern!r} matched {len(matches)}: {matches}" + ) + wheel = matches[0] + if wheel.suffix != ".whl": + raise ValueError(f"{distribution} artifact is not a wheel: {wheel}") + return wheel + + +def _venv_python(root: Path) -> Path: + return root / ("Scripts/python.exe" if os.name == "nt" else "bin/python") + + +def _console(root: Path) -> Path: + return root / ("Scripts/openadapt.exe" if os.name == "nt" else "bin/openadapt") + + +def _sha256(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def _run( + command: Sequence[str], + *, + cwd: Path, + env: dict[str, str], + log: Path, + expected: int = 0, +) -> subprocess.CompletedProcess[str]: + printable = subprocess.list2cmdline(list(command)) + print(f"\n$ {printable}", flush=True) + child_env = env.copy() + child_env["PYTHONUTF8"] = "1" + child_env["PYTHONIOENCODING"] = "utf-8" + result = subprocess.run( + list(command), + cwd=cwd, + env=child_env, + text=True, + encoding="utf-8", + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + check=False, + ) + print(result.stdout, end="", flush=True) + log.parent.mkdir(parents=True, exist_ok=True) + log.write_text(f"$ {printable}\n\n{result.stdout}", encoding="utf-8") + marker = next( + (item for item in _UNHANDLED_RUNTIME_MARKERS if item in result.stdout), None + ) + if marker is not None: + raise RuntimeError( + f"{printable} emitted an unhandled runtime error ({marker}); see {log}" + ) + if result.returncode != expected: + raise RuntimeError( + f"{printable} exited {result.returncode}; expected {expected} (see {log})" + ) + return result + + +def _load_object(path: Path) -> dict: + if not path.is_file(): + raise AssertionError(f"missing JSON artifact: {path}") + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise AssertionError(f"JSON artifact is not an object: {path}") + return value + + +def _inspect_quickstart(root: Path) -> dict[str, object]: + run = root / "run" + report = _load_object(run / "report.json") + if report.get("execution_outcome") != "VERIFIED": + raise AssertionError( + f"quickstart outcome is {report.get('execution_outcome')!r}, not VERIFIED" + ) + if report.get("execution_profile") != "standard": + raise AssertionError( + f"quickstart profile is {report.get('execution_profile')!r}, not standard" + ) + if report.get("transaction_outcome") != "VERIFIED": + raise AssertionError("quickstart did not verify the transaction outcome") + if report.get("model_calls") != 0: + raise AssertionError("quickstart made a model call") + + envelope = report.get("outcome_envelope") or {} + required = int((envelope.get("required_contracts") or {}).get("effect") or 0) + passed = int((envelope.get("passed_contracts") or {}).get("effect") or 0) + if required < 1 or passed != required: + raise AssertionError(f"quickstart effect coverage is {passed}/{required}") + + receipt = _load_object(run / "receipt.json") + if receipt.get("outcome") != "VERIFIED": + raise AssertionError("quickstart receipt is not VERIFIED") + if receipt.get("provenance") != "synthetic-tutorial": + raise AssertionError("quickstart receipt has the wrong provenance") + for path in (run / "REPORT.md", run / "receipt.png", run / "receipt.md"): + if not path.is_file(): + raise AssertionError(f"quickstart is missing {path.name}") + + return { + "outcome": "VERIFIED", + "profile": "standard", + "transaction_outcome": "VERIFIED", + "effect_contracts": passed, + "model_calls": 0, + "receipt_emitted": True, + } + + +def run_lifecycle( + launcher_wheel: Path, + work_dir: Path, + *, + flow_wheel: Path | None, + browser_with_deps: bool, + source_revision: str | None, +) -> dict[str, object]: + if work_dir.exists(): + raise FileExistsError(f"work directory already exists: {work_dir}") + work_dir.mkdir(parents=True) + venv_dir = work_dir / "venv" + artifacts = work_dir / "artifacts" + logs = work_dir / "logs" + artifacts.mkdir() + venv.EnvBuilder(with_pip=True, clear=True).create(venv_dir) + + python = _venv_python(venv_dir) + console = _console(venv_dir) + env = os.environ.copy() + env.pop("PYTHONPATH", None) + env["PYTHONUTF8"] = "1" + env["PYTHONIOENCODING"] = "utf-8" + env["OPENADAPT_FLOW_SCRUB"] = "off" + + summary: dict[str, object] = { + "launcher_wheel": launcher_wheel.name, + "launcher_wheel_sha256": _sha256(launcher_wheel), + "flow_wheel": flow_wheel.name if flow_wheel else "resolved-release", + "flow_wheel_sha256": _sha256(flow_wheel) if flow_wheel else None, + "platform": sys.platform, + "source_revision": source_revision or "local-unbound", + } + installed = False + try: + if flow_wheel is not None: + _run( + [ + str(python), + "-m", + "pip", + "install", + f"{flow_wheel}[browser,hosted]", + ], + cwd=artifacts, + env=env, + log=logs / "01-install-flow.log", + ) + _run( + [ + str(python), + "-m", + "pip", + "install", + f"{launcher_wheel}[browser]", + ], + cwd=artifacts, + env=env, + log=logs / "02-install-launcher.log", + ) + installed = True + if not console.is_file(): + raise AssertionError(f"launcher entry point is missing: {console}") + + _run( + [str(console), "--help"], + cwd=artifacts, + env=env, + log=logs / "03-launcher-help.log", + ) + _run( + [str(console), "flow", "--help"], + cwd=artifacts, + env=env, + log=logs / "04-flow-help.log", + ) + if browser_with_deps: + _run( + [ + str(python), + "-m", + "playwright", + "install", + "--with-deps", + "chromium", + ], + cwd=artifacts, + env=env, + log=logs / "05-browser-host-deps.log", + ) + + quickstart = artifacts / "openadapt-quickstart" + _run( + [str(console), "quickstart", "--out", str(quickstart)], + cwd=artifacts, + env=env, + log=logs / "06-quickstart.log", + ) + summary.update(_inspect_quickstart(quickstart)) + _run( + [str(console), "flow", "lint", str(quickstart / "bundle")], + cwd=artifacts, + env=env, + log=logs / "07-lint.log", + ) + finally: + if installed: + _run( + [ + str(python), + "-m", + "pip", + "uninstall", + "-y", + "openadapt", + "openadapt-flow", + ], + cwd=artifacts, + env=env, + log=logs / "08-uninstall.log", + ) + _run( + [ + str(python), + "-c", + ( + "import importlib.util; " + "assert importlib.util.find_spec('openadapt') is None; " + "assert importlib.util.find_spec('openadapt_flow') is None" + ), + ], + cwd=artifacts, + env=env, + log=logs / "09-uninstall-probe.log", + ) + summary["uninstall_verified"] = True + (work_dir / "summary.json").write_text( + json.dumps(summary, indent=2, sort_keys=True) + "\n", encoding="utf-8" + ) + + print(f"\nLauncher lifecycle PASS: {work_dir / 'summary.json'}") + return summary + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--launcher-wheel", required=True) + parser.add_argument( + "--flow-wheel", + default=None, + help="Optional local Flow wheel; omit to resolve the supported release", + ) + parser.add_argument("--work-dir", required=True) + parser.add_argument( + "--browser-with-deps", + action="store_true", + help="Pre-provision Chromium and Linux host dependencies", + ) + parser.add_argument("--source-revision", default=None) + return parser + + +def main(argv: Sequence[str] | None = None) -> int: + args = build_parser().parse_args(argv) + launcher_wheel = _resolve_wheel(args.launcher_wheel, "launcher") + flow_wheel = _resolve_wheel(args.flow_wheel, "Flow") if args.flow_wheel else None + run_lifecycle( + launcher_wheel, + Path(args.work_dir).resolve(), + flow_wheel=flow_wheel, + browser_with_deps=args.browser_with_deps, + source_revision=args.source_revision, + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_cli_smoke.py b/tests/test_cli_smoke.py index 4f90aa74b..aec814efa 100644 --- a/tests/test_cli_smoke.py +++ b/tests/test_cli_smoke.py @@ -255,20 +255,6 @@ def test_doctor_lists_flow_as_core_not_extras(): assert "pip install openadapt[" in optional_section -# --------------------------------------------------------------------------- -# Flow command (the demonstration compiler — flagship path) -# --------------------------------------------------------------------------- - -FLOW_VERBS = {"demo-record", "record", "compile", "replay", "lint", "certify"} - -# Verbs wrapped with explicit click options (local --help, no engine import). -# `record` and `replay` are intentionally absent: they delegate through the -# passthrough group so every engine option (--backend, --config, native target -# selectors, ...) forwards verbatim — their --help comes from the engine. -FLOW_PASSTHROUGH_VERBS = {"record", "replay"} -FLOW_WRAPPED_VERBS = FLOW_VERBS - FLOW_PASSTHROUGH_VERBS - - def test_launcher_flow_and_substrate_extras_metadata(): """Install routes resolve the Flow release whose replay parser exposes every native/remote backend, without installing OS bindings elsewhere.""" @@ -279,10 +265,10 @@ def test_launcher_flow_and_substrate_extras_metadata(): assert metadata["dependencies"].count("openadapt-flow[hosted]>=1.29.0,<2.0.0") == 1 assert extras["flow"] == ["openadapt-flow>=1.29.0,<2.0.0"] - assert extras["browser"] == ["playwright>=1.44"] + assert extras["browser"] == ["openadapt-flow[browser]>=1.29.0,<2.0.0"] assert extras["privacy"] == ["openadapt-flow[privacy]>=1.29.0,<2.0.0"] assert extras["capture"] == [ - "openadapt-capture>=1.0.4,<2.0.0", + "openadapt-capture>=1.2.0,<2.0.0", "openadapt-flow[capture]>=1.29.0,<2.0.0", ] assert extras["windows"] == ["openadapt-flow[windows]>=1.29.0,<2.0.0"] @@ -308,6 +294,29 @@ def test_doctor_does_not_require_browser_for_citrix(monkeypatch): assert "no Playwright or Chromium setup will run" in result.output +def test_doctor_rdp_fails_without_transport_dependency(monkeypatch): + monkeypatch.setattr("importlib.util.find_spec", lambda _name: None) + + result = CliRunner().invoke(cli_main, ["doctor", "--backend", "rdp"]) + + assert result.exit_code != 0 + assert "[MISSING] rdp" in result.output + assert "python -m pip install 'openadapt[rdp]'" in result.output + assert "System check failed" in result.output + + +def test_doctor_rdp_reports_transport_ready(monkeypatch): + monkeypatch.setattr( + "importlib.util.find_spec", + lambda name: object() if name in {"aardwolf", "openadapt_flow"} else None, + ) + + result = CliRunner().invoke(cli_main, ["doctor", "--backend", "rdp"]) + + assert result.exit_code == 0, result.output + assert "RDP transport dependency is installed" in result.output + + def test_deploy_preflight_composes_existing_flow_interfaces_without_secrets( monkeypatch, ): @@ -320,7 +329,8 @@ def test_deploy_preflight_composes_existing_flow_interfaces_without_secrets( "importlib.util.find_spec", lambda name: ( object() - if name in {"openadapt_flow", "fastapi", "uvicorn", "openadapt_types"} + if name + in {"openadapt_flow", "aardwolf", "fastapi", "uvicorn", "openadapt_types"} else None ), ) @@ -345,7 +355,7 @@ def test_deploy_base_hosted_install_gives_conditional_console_setup(monkeypatch) """Base Flow hosted installs must not receive an unusable console command.""" monkeypatch.setattr( "importlib.util.find_spec", - lambda name: object() if name == "openadapt_flow" else None, + lambda name: object() if name in {"openadapt_flow", "aardwolf"} else None, ) monkeypatch.setattr("importlib.metadata.version", lambda _name: "1.29.0") @@ -363,7 +373,9 @@ def test_deploy_console_requires_openadapt_types(monkeypatch): monkeypatch.setattr( "importlib.util.find_spec", lambda name: ( - object() if name in {"openadapt_flow", "fastapi", "uvicorn"} else None + object() + if name in {"openadapt_flow", "aardwolf", "fastapi", "uvicorn"} + else None ), ) monkeypatch.setattr("importlib.metadata.version", lambda _name: "1.30.0") @@ -407,6 +419,22 @@ def test_deploy_preflight_fails_without_web_runtime(monkeypatch): assert "Preflight passed" not in result.output +def test_deploy_preflight_fails_without_rdp_transport(monkeypatch): + monkeypatch.setattr( + "importlib.util.find_spec", + lambda name: object() if name == "openadapt_flow" else None, + ) + monkeypatch.setattr("importlib.metadata.version", lambda _name: "1.29.0") + + result = CliRunner().invoke(cli_main, ["deploy", "--backend", "rdp"]) + + assert result.exit_code != 0 + assert "[MISSING] RDP transport dependency" in result.output + assert "python -m pip install 'openadapt[rdp]'" in result.output + assert "Preflight failed" in result.output + assert "Preflight passed" not in result.output + + @pytest.mark.parametrize("flow_version", ["1.28.9", "2.0.0", "2.1.0", "invalid"]) def test_deploy_preflight_fails_for_unsupported_flow(monkeypatch, flow_version): monkeypatch.setattr("importlib.util.find_spec", lambda _name: object()) @@ -428,6 +456,8 @@ def test_top_level_help_leads_with_flow(): # Quick Start headline and Commands listing both lead with flow. assert "Beta launcher" in result.output assert "openadapt flow demo-record" in result.output + assert "openadapt quickstart" in result.output + assert "effect-verified first run" in result.output assert "Standalone local human GUI capture" in result.output assert "Research: evaluate" in result.output assert "Research: train" in result.output @@ -475,44 +505,79 @@ def wait_for_ready(self): assert "Capture saved" not in result.output -def test_flow_help_lists_verbs(): - """`openadapt flow --help` lists every mounted verb (no flow install - needed — click renders help before importing openadapt-flow).""" - runner = CliRunner() - result = runner.invoke(cli_main, ["flow", "--help"]) - assert result.exit_code == 0, result.output - for verb in FLOW_VERBS: - assert verb in result.output, f"'{verb}' missing from `flow --help`" +def test_capture_stop_fails_until_capture_has_a_control_channel(): + result = CliRunner().invoke(cli_main, ["capture", "stop"]) + assert result.exit_code != 0 + assert "Ctrl+C in the recorder terminal" in result.output + assert "authenticated, owner-only local control channel" in result.output + assert "Stopping active capture session" not in result.output -def test_flow_subcommand_help_renders(): - """Each explicitly wrapped flow subcommand renders --help without - importing flow.""" - runner = CliRunner() - for verb in FLOW_WRAPPED_VERBS: - result = runner.invoke(cli_main, ["flow", verb, "--help"]) - assert result.exit_code == 0, f"`flow {verb} --help` failed: {result.output}" + +def test_flow_help_is_current_engine_help(): + """The launcher must not maintain a second, stale Flow command list.""" + _require_openadapt_flow() + import openadapt_flow.__main__ as flow_main_mod + + result = CliRunner().invoke(cli_main, ["flow", "--help"]) + + assert result.exit_code == 0, result.output + assert result.output == flow_main_mod.build_parser().format_help() -@pytest.mark.parametrize("verb", sorted(FLOW_PASSTHROUGH_VERBS)) -def test_flow_capture_and_replay_help_is_engine_help(verb): - """Record/replay help must come from the engine, not a stale launcher - wrapper that hides backend and native-target options.""" +@pytest.mark.parametrize( + ("verb", "expected_options"), + [ + ("record", ("--backend", "--agent-url", "--task")), + ("replay", ("--backend", "--config", "--rdp-readiness-text")), + ("demo-record", ("--record-video", "--note-text", "--param-name")), + ("compile", ("--accept-params", "--params-from", "--no-confirm-params")), + ("certify", ("--config", "--policy")), + ], +) +def test_flow_subcommand_help_is_engine_help(verb, expected_options): _require_openadapt_flow() result = CliRunner().invoke(cli_main, ["flow", verb, "--help"]) assert result.exit_code == 0, result.output - expected_options = ( - ("--backend", "--agent-url", "--task") - if verb == "record" - else ("--backend", "--config", "--rdp-readiness-text") - ) for option in expected_options: assert option in result.output, ( f"{option} missing from `flow {verb} --help`; the launcher is " - "hiding engine options again" + "hiding current engine options" ) +@pytest.mark.parametrize( + "argv", + [ + ["demo-record", "--out", "rec", "--record-video"], + [ + "compile", + "rec", + "--out", + "bundle", + "--name", + "demo", + "--params-from", + "params.json", + "--accept-params", + "note", + "--no-confirm-params", + ], + ["certify", "bundle", "--config", "deployment.yaml"], + ], +) +def test_flow_forwards_current_engine_options_verbatim(monkeypatch, argv): + captured = {} + monkeypatch.setattr( + "openadapt.cli._run_flow", lambda value: captured.update(argv=list(value)) + ) + + result = CliRunner().invoke(cli_main, ["flow", *argv]) + + assert result.exit_code == 0, result.output + assert captured["argv"] == argv + + def test_flow_record_forwards_backend_options(monkeypatch): """Regression (launcher <=1.7.0): the explicit record wrapper rejected `--backend windows` with "No such option". record must forward every diff --git a/tests/test_quickstart_lifecycle.py b/tests/test_quickstart_lifecycle.py new file mode 100644 index 000000000..476b79490 --- /dev/null +++ b/tests/test_quickstart_lifecycle.py @@ -0,0 +1,97 @@ +from __future__ import annotations + +import importlib.util +import json +import subprocess +from pathlib import Path + +import pytest +import yaml + +ROOT = Path(__file__).resolve().parents[1] +SCRIPT = ROOT / "scripts" / "quickstart_lifecycle.py" +WORKFLOW = ROOT / ".github" / "workflows" / "quickstart-lifecycle.yml" + + +def _module(): + spec = importlib.util.spec_from_file_location("launcher_lifecycle", SCRIPT) + assert spec is not None and spec.loader is not None + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def _write_verified_quickstart(root: Path) -> None: + run = root / "run" + run.mkdir(parents=True) + (run / "report.json").write_text( + json.dumps( + { + "execution_outcome": "VERIFIED", + "execution_profile": "standard", + "transaction_outcome": "VERIFIED", + "model_calls": 0, + "outcome_envelope": { + "required_contracts": {"effect": 2}, + "passed_contracts": {"effect": 2}, + }, + } + ), + encoding="utf-8", + ) + (run / "receipt.json").write_text( + json.dumps({"outcome": "VERIFIED", "provenance": "synthetic-tutorial"}), + encoding="utf-8", + ) + (run / "REPORT.md").write_text("# VERIFIED\n", encoding="utf-8") + (run / "receipt.md").write_text("# VERIFIED\n", encoding="utf-8") + (run / "receipt.png").write_bytes(b"png") + + +def test_inspector_requires_a_verified_standard_effect(tmp_path): + lifecycle = _module() + _write_verified_quickstart(tmp_path) + + summary = lifecycle._inspect_quickstart(tmp_path) + + assert summary["outcome"] == "VERIFIED" + assert summary["effect_contracts"] == 2 + + report = tmp_path / "run" / "report.json" + payload = json.loads(report.read_text(encoding="utf-8")) + payload["transaction_outcome"] = "COMPLETED_UNVERIFIED" + report.write_text(json.dumps(payload), encoding="utf-8") + with pytest.raises(AssertionError, match="transaction outcome"): + lifecycle._inspect_quickstart(tmp_path) + + +def test_zero_exit_with_an_unhandled_async_error_fails(tmp_path, monkeypatch): + lifecycle = _module() + monkeypatch.setattr( + lifecycle.subprocess, + "run", + lambda command, **kwargs: subprocess.CompletedProcess( + command, 0, stdout="VERIFIED\nTask was destroyed but it is pending!\n" + ), + ) + + with pytest.raises(RuntimeError, match="unhandled runtime error"): + lifecycle._run( + ["openadapt", "quickstart"], + cwd=tmp_path, + env={}, + log=tmp_path / "quickstart.log", + ) + + +def test_workflow_runs_the_public_command_in_one_bounded_weekly_job(): + document = yaml.safe_load(WORKFLOW.read_text(encoding="utf-8")) + triggers = document[True] + jobs = document["jobs"] + + assert set(triggers) == {"schedule", "workflow_dispatch"} + assert list(jobs) == ["quickstart"] + steps = jobs["quickstart"]["steps"] + run = next(step["run"] for step in steps if step.get("name", "").startswith("Run")) + assert "scripts/quickstart_lifecycle.py" in run + assert "--browser-with-deps" in run diff --git a/uv.lock b/uv.lock index 2a1380abf..1d6cca96f 100644 --- a/uv.lock +++ b/uv.lock @@ -2326,17 +2326,16 @@ dependencies = [ all = [ { name = "openadapt-capture" }, { name = "openadapt-evals" }, - { name = "openadapt-flow", extra = ["capture", "privacy", "rdp", "windows"] }, + { name = "openadapt-flow", extra = ["browser", "capture", "privacy", "rdp", "windows"] }, { name = "openadapt-flow", extra = ["linux"], marker = "sys_platform == 'linux'" }, { name = "openadapt-flow", extra = ["macos"], marker = "sys_platform == 'darwin'" }, { name = "openadapt-grounding" }, { name = "openadapt-ml" }, { name = "openadapt-retrieval" }, { name = "openadapt-viewer" }, - { name = "playwright" }, ] browser = [ - { name = "playwright" }, + { name = "openadapt-flow", extra = ["browser"] }, ] capture = [ { name = "openadapt-capture" }, @@ -2396,9 +2395,10 @@ requires-dist = [ { name = "openadapt", extras = ["capture", "ml", "evals", "viewer"], marker = "extra == 'core'" }, { name = "openadapt", extras = ["linux"], marker = "sys_platform == 'linux' and extra == 'all'" }, { name = "openadapt", extras = ["macos"], marker = "sys_platform == 'darwin' and extra == 'all'" }, - { name = "openadapt-capture", marker = "extra == 'capture'", specifier = ">=1.0.4,<2.0.0" }, + { name = "openadapt-capture", marker = "extra == 'capture'", specifier = ">=1.2.0,<2.0.0" }, { name = "openadapt-evals", marker = "extra == 'evals'", specifier = ">=0.1.0" }, { name = "openadapt-flow", marker = "extra == 'flow'", specifier = ">=1.29.0,<2.0.0" }, + { name = "openadapt-flow", extras = ["browser"], marker = "extra == 'browser'", specifier = ">=1.29.0,<2.0.0" }, { name = "openadapt-flow", extras = ["capture"], marker = "extra == 'capture'", specifier = ">=1.29.0,<2.0.0" }, { name = "openadapt-flow", extras = ["hosted"], specifier = ">=1.29.0,<2.0.0" }, { name = "openadapt-flow", extras = ["linux"], marker = "sys_platform == 'linux' and extra == 'linux'", specifier = ">=1.29.0,<2.0.0" }, @@ -2410,7 +2410,6 @@ requires-dist = [ { name = "openadapt-ml", marker = "extra == 'ml'", specifier = ">=0.2.0" }, { name = "openadapt-retrieval", marker = "extra == 'retrieval'", specifier = ">=0.1.0" }, { name = "openadapt-viewer", marker = "extra == 'viewer'", specifier = ">=0.1.0" }, - { name = "playwright", marker = "extra == 'browser'", specifier = ">=1.44" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" }, ] @@ -2503,6 +2502,9 @@ wheels = [ ] [package.optional-dependencies] +browser = [ + { name = "playwright" }, +] capture = [ { name = "openadapt-capture" }, ] From 5e40fd06c0ba846d7bb9627faee5c1f320f95cb9 Mon Sep 17 00:00:00 2001 From: abrichr Date: Tue, 18 Aug 2026 12:22:34 -0400 Subject: [PATCH 2/4] fix platform manifest capture requirement --- platform-manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform-manifest.json b/platform-manifest.json index f833326cd..4456340ac 100644 --- a/platform-manifest.json +++ b/platform-manifest.json @@ -89,7 +89,7 @@ "python": ">=3.10,<3.13", "launcher_requires": { "openadapt-capture": [ - ">=1.0.4,<2.0.0" + ">=1.2.0,<2.0.0" ], "openadapt-evals": [ ">=0.1.0" From c4a4f2eb90eae1f941e63f7262e1a0586e2245e7 Mon Sep 17 00:00:00 2001 From: abrichr Date: Tue, 18 Aug 2026 12:52:00 -0400 Subject: [PATCH 3/4] docs: align Desktop lifecycle in hero diagram --- media/openadapt-hero.svg | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/media/openadapt-hero.svg b/media/openadapt-hero.svg index 58fe3598d..5f421b5b6 100644 --- a/media/openadapt-hero.svg +++ b/media/openadapt-hero.svg @@ -97,8 +97,8 @@ CLI Desktop app - -EXPERIMENTAL + +BETA Tray @@ -109,4 +109,4 @@ MCP servers Agent Skills - \ No newline at end of file + From e720b1df5213cba1782936fd78ee20951cbfa9f2 Mon Sep 17 00:00:00 2001 From: Richard Abrich Date: Wed, 19 Aug 2026 17:58:41 -0400 Subject: [PATCH 4/4] docs: keep Capture at its canonical Experimental lifecycle The docs rewrite introduced a Beta lifecycle for openadapt-capture in the package map, the Capture package page, the README component list, and the package docstring. The canonical organization registry (OpenAdaptAI/.github REPOSITORY_LIFECYCLE.md) still records Capture as Experimental, and no acceptance evidence exists yet. Keep the stale-fact corrections in this PR and state the lifecycle the registry actually records. The privacy extra keeps its experimental qualifier for the same reason. Co-Authored-By: Claude Opus 5 --- README.md | 2 +- docs/packages/capture.md | 7 ++++--- docs/packages/index.md | 2 +- openadapt/__init__.py | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 82672275e..b63a6b966 100644 --- a/README.md +++ b/README.md @@ -242,7 +242,7 @@ defines what can be claimed for a new deployment. canonical compiler, governed runtime, CLI implementation, and conformance tests - **[`openadapt-capture`](https://github.com/OpenAdaptAI/openadapt-capture):** - Beta native screen, mouse, keyboard, timing, window-scope, and media capture + native screen, mouse, keyboard, timing, window-scope, and media capture component used by the Flow desktop recording path - **[`openadapt-privacy`](https://github.com/OpenAdaptAI/openadapt-privacy):** local sanitization and review mechanisms for approved derivatives diff --git a/docs/packages/capture.md b/docs/packages/capture.md index 8500c2b3d..a30a962c4 100644 --- a/docs/packages/capture.md +++ b/docs/packages/capture.md @@ -1,8 +1,9 @@ # openadapt-capture -**Lifecycle: Beta.** `openadapt-capture` is the canonical OpenAdapt component -for native screen, mouse, keyboard, timing, window-scope, and media capture. It -is not an experimental prototype. +**Lifecycle: Experimental**, as recorded in the canonical +[organization lifecycle registry](https://github.com/OpenAdaptAI/.github/blob/main/REPOSITORY_LIFECYCLE.md). +`openadapt-capture` is the component OpenAdapt uses for native screen, mouse, +keyboard, timing, window-scope, and media capture. Repository: [`OpenAdaptAI/openadapt-capture`](https://github.com/OpenAdaptAI/openadapt-capture) diff --git a/docs/packages/index.md b/docs/packages/index.md index 23fe62318..3caf008f2 100644 --- a/docs/packages/index.md +++ b/docs/packages/index.md @@ -7,7 +7,7 @@ cockpit, privacy tools, and optional managed control plane. | --- | --- | --- | | `openadapt` | Beta launcher and unified CLI | `pip install openadapt` | | `openadapt-flow` | Beta compiler and governed runtime; installed by the launcher | `pip install openadapt-flow` for engine-only use | -| `openadapt-capture` | Beta native capture component | `pip install 'openadapt[capture]'` | +| `openadapt-capture` | Experimental native capture component | `pip install 'openadapt[capture]'` | | `openadapt-privacy` | Local privacy and sanitization support | `pip install 'openadapt[privacy]'` | | `openadapt-desktop` | Beta visual authoring and operator application | [Download an installer](https://openadapt.ai/download) | diff --git a/openadapt/__init__.py b/openadapt/__init__.py index 17cfb0344..e755485d8 100644 --- a/openadapt/__init__.py +++ b/openadapt/__init__.py @@ -6,8 +6,8 @@ research packages: pip install openadapt # launcher + openadapt-flow - pip install openadapt[capture] # Beta native capture component - pip install openadapt[privacy] # local privacy and scrubbing support + pip install openadapt[capture] # experimental native capture + pip install openadapt[privacy] # experimental privacy scrubbing pip install openadapt[ml,evals] # research toolkits """