diff --git a/CHANGELOG.md b/CHANGELOG.md
index 551d8dcac9..114bbdeb65 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,36 @@
+## v1.19.0 (2026-11-02)
+
+### Breaking Changes
+
+- Require openadapt-flow 2.5 (`>=2.5.0,<3.0.0`). `openadapt shadow` forwards
+ to Flow's shadow command, and `openadapt verify serve` offers its recheck;
+ neither is in Flow 2.4 or earlier. `openadapt doctor` and `openadapt deploy`
+ report an older Flow as unsupported.
+
+### Features
+
+- `openadapt shadow` runs beside an agent you already have, as a loopback
+ pass-through proxy. It checks each write the agent makes against the
+ system of record, checks each confirmed write again later, and writes
+ `report.html` and `report.json`. It never blocks, changes or resends a write
+ ([#1678](https://github.com/OpenAdaptAI/OpenAdapt/pull/1678)).
+- `openadapt verify` checks any agent's writes against a contract you own
+ and returns `CONFIRMED`, `REFUTED` or `UNCERTAIN`. `openadapt verify serve`
+ offers the same check to any MCP client, recheck included, and `openadapt
+ verify ledger` lists, reconciles and releases writes whose outcome isn't
+ known yet ([#1589](https://github.com/OpenAdaptAI/OpenAdapt/pull/1589),
+ [#1628](https://github.com/OpenAdaptAI/OpenAdapt/pull/1628),
+ [#1669](https://github.com/OpenAdaptAI/OpenAdapt/pull/1669)).
+- `openadapt bench medplum` runs the public Medplum write benchmark with your
+ own agent ([#1652](https://github.com/OpenAdaptAI/OpenAdapt/pull/1652)).
+- `openadapt setup` adds the OpenAdapt MCP server and a skill to Claude Code,
+ Cursor, Codex and Gemini CLI, so the agent can turn a read-only browser
+ task into a tool it calls again. It prints every change, and `--dry-run`
+ and `--undo` are there.
+
## v1.18.0 (2026-10-20)
### Breaking Changes
diff --git a/docs/platform-compatibility-report.md b/docs/platform-compatibility-report.md
index 294e817095..2c1b4199d6 100644
--- a/docs/platform-compatibility-report.md
+++ b/docs/platform-compatibility-report.md
@@ -2,7 +2,7 @@
-- Generated: `2026-10-20T17:25:50+00:00`
+- Generated: `2026-11-03T00:23:35+00:00`
- Selection: `explicit-published`
- Compatibility: **dependency-compatible**
- Signature: `unsigned (signing infrastructure pending)`
@@ -12,7 +12,7 @@
| Role | Package | Version | Release source reference | Published artifacts |
|---|---|---:|---|---|
| launcher | `openadapt` | `1.17.0` | [`v1.17.0`](https://github.com/OpenAdaptAI/OpenAdapt/tree/v1.17.0)
commit `e022d97ee8b3c3992f4a4cb6e522f1532fef0834`
tree `2e0efeaa337a26d3425320ffa8f9aa3ac31d3f9c` | `openadapt-1.17.0-py3-none-any.whl` `c33c069c8cd9c270562200339b631fb77423b5b7f24364b5f0a6a0730e5ca8b8`
`openadapt-1.17.0.tar.gz` `03926f94f4d9d37eeb8c6c9cbdc94516d43c0ace971f2282b3310d77d73b6547` |
-| flow | `openadapt-flow` | `2.3.0` | [`openadapt-flow-v2.3.0`](https://github.com/OpenAdaptAI/OpenAdapt/tree/openadapt-flow-v2.3.0/packages/flow)
commit `b6a9d46b00b92454c18e844f0a8f6929330e87b6`
tree `14bf88ccefe00708bb51003d6262876c555e7271` | `openadapt_flow-2.3.0-py3-none-any.whl` `6a2dbd22eb15bd449fc12a6dc36dcc8da7c0316f9faac80cbff8d6e9a433222b`
`openadapt_flow-2.3.0.tar.gz` `39f3b853987227063257a1ffcd402ca3a3120e508d901578622cd0351f1ce12d` |
+| flow | `openadapt-flow` | `2.5.0` | [`openadapt-flow-v2.5.0`](https://github.com/OpenAdaptAI/OpenAdapt/tree/openadapt-flow-v2.5.0/packages/flow)
commit `5a3bf49a0bf2d662430386295a1c34036c064d58`
tree `cab536c90e79fcfe887b8876e884ec2ed651f23e` | `openadapt_flow-2.5.0-py3-none-any.whl` `1dbc5d42d103cfa8a452d95805dde60c7d460035ec4d81df5cc642a32f118339`
`openadapt_flow-2.5.0.tar.gz` `54d99e77976ee7d9e6bc171b2ea148497528ead77b226fdf6fd4713b37c689b3` |
| capture | `openadapt-capture` | `1.2.2` | [`v1.2.2`](https://github.com/OpenAdaptAI/openadapt-capture/tree/v1.2.2)
commit `06ade22948c12dcc6608bd54b5cca5c9c306e77c`
tree `b0a3b5b53f23536e01314f36aa46fd5b2d8baba6` | `openadapt_capture-1.2.2-py3-none-any.whl` `550fffe50990fa8431d332bdd5fe4755976d83baa7ad62553f50ad9e4ddcd1ae`
`openadapt_capture-1.2.2.tar.gz` `6f4298d3daadf9cced8994207e34b3116a79209ef792e98f778a0770e2f3406c` |
| privacy | `openadapt-privacy` | `1.1.2` | [`v1.1.2`](https://github.com/OpenAdaptAI/openadapt-privacy/tree/v1.1.2)
commit `9ae96d8ed73ea5b9e445d0604b5db48788936a04`
tree `fe59ca5f04fc99d16424e9228dcd797b05115cb8` | `openadapt_privacy-1.1.2-py3-none-any.whl` `6e542b2fea44bdf8872430687946ec805fef22dc7d228a56a1e65871beebe47a`
`openadapt_privacy-1.1.2.tar.gz` `d90287edaa13470955f15d5f6eeb37b075678aeb55de324f8b8207682009bcc2` |
| types | `openadapt-types` | `0.24.0` | [`openadapt-types-v0.24.0`](https://github.com/OpenAdaptAI/OpenAdapt/tree/openadapt-types-v0.24.0/packages/types)
commit `f2f04c5b432d88b262d46103a542b0e2c3dcc8a9`
tree `38ccf341ec74a0dd0facc9f64343c27644a7ca7d` | `openadapt_types-0.24.0-py3-none-any.whl` `8025e8a4dc9235fd81d4b24ae7fa7e544120abdb8d4ee7df160e4f50d9230d75`
`openadapt_types-0.24.0.tar.gz` `16dcd1da1c2c78c08d650a19eb9cc439474f996812a87198b316d51bfac443db` |
@@ -23,10 +23,10 @@
| Unit | Version | Selected or locked OpenAdapt packages |
|---|---:|---|
-| launcher_environment | `1.17.0` | `openadapt==1.17.0`
`openadapt-flow==2.3.0` |
-| customer_runner | `2.3.0` | `openadapt-flow==2.3.0`
`openadapt-types==0.24.0` |
+| launcher_environment | `1.17.0` | `openadapt==1.17.0`
`openadapt-flow==2.5.0` |
+| customer_runner | `2.5.0` | `openadapt-flow==2.5.0`
`openadapt-types==0.24.0` |
| desktop_sidecar | `0.16.0` | `openadapt-capture==1.2.1`
`openadapt-flow==1.34.0`
`openadapt-privacy==1.0.0`
`openadapt-types==0.10.0` |
-| agent_bridge | `2.2.1` | `openadapt-agent==2.2.1`
`openadapt-flow==2.3.0` |
+| agent_bridge | `2.2.1` | `openadapt-agent==2.2.1`
`openadapt-flow==2.5.0` |
## Selected-platform dependency compatibility
@@ -38,29 +38,29 @@ These edges show whether each selected integration version satisfies its publish
| desktop-sidecar-lock | `openadapt-desktop==0.16.0` | `openadapt-flow` | `==1.34.0` | `1.34.0` | `extra == "build"` |
| desktop-sidecar-lock | `openadapt-desktop==0.16.0` | `openadapt-privacy` | `>=1.0.0` | `1.0.0` | `always` |
| desktop-sidecar-lock | `openadapt-desktop==0.16.0` | `openadapt-types` | `==0.10.0` | `0.10.0` | `always` |
-| selected-platform | `openadapt-agent==2.2.1` | `openadapt-flow` | `<3,>=2.2.0` | `2.3.0` | `always`
`extra == "tutorial"` |
+| selected-platform | `openadapt-agent==2.2.1` | `openadapt-flow` | `<3,>=2.2.0` | `2.5.0` | `always`
`extra == "tutorial"` |
| selected-platform | `openadapt-agent==2.2.1` | `openadapt-privacy` | `>=1.1.2` | `1.1.2` | `extra == "repair-screens"` |
| selected-platform | `openadapt-capture==1.2.2` | `openadapt-privacy` | `>=0.1.0` | `1.1.2` | `extra == "all"`
`extra == "privacy"` |
-| selected-platform | `openadapt-flow==2.3.0` | `openadapt-capture` | `>=1.2.0` | `1.2.2` | `extra == "capture"` |
-| selected-platform | `openadapt-flow==2.3.0` | `openadapt-privacy` | `>=1.0.0` | `1.1.2` | `extra == "privacy"` |
-| selected-platform | `openadapt-flow==2.3.0` | `openadapt-types` | `<0.25.0,>=0.24.0` | `0.24.0` | `always` |
+| selected-platform | `openadapt-flow==2.5.0` | `openadapt-capture` | `>=1.2.0` | `1.2.2` | `extra == "capture"` |
+| selected-platform | `openadapt-flow==2.5.0` | `openadapt-privacy` | `>=1.0.0` | `1.1.2` | `extra == "privacy"` |
+| selected-platform | `openadapt-flow==2.5.0` | `openadapt-types` | `<0.25.0,>=0.24.0` | `0.24.0` | `always` |
| selected-platform | `openadapt==1.17.0` | `openadapt-agent` | `<3,>=2.1.0` | `2.2.1` | `python_version < "3.13"`
`python_version < "3.13" and extra == "agent"`
`python_version < "3.13" and extra == "all"` |
| selected-platform | `openadapt==1.17.0` | `openadapt-capture` | `<2.0.0,>=1.2.0` | `1.2.2` | `extra == "all"`
`extra == "capture"`
`extra == "core"` |
-| selected-platform | `openadapt==1.17.0` | `openadapt-flow` | `<3.0.0,>=2.0.0` | `2.3.0` | `(sys_platform == "darwin" and python_version < "3.13") and extra == "all"`
`(sys_platform == "darwin" and python_version < "3.13") and extra == "macos"`
`(sys_platform == "linux" and python_version < "3.13") and extra == "all"`
`(sys_platform == "linux" and python_version < "3.13") and extra == "linux"`
`python_version < "3.13"`
`python_version < "3.13" and extra == "all"`
`python_version < "3.13" and extra == "browser"`
`python_version < "3.13" and extra == "capture"`
`python_version < "3.13" and extra == "core"`
`python_version < "3.13" and extra == "flow"`
`python_version < "3.13" and extra == "privacy"`
`python_version < "3.13" and extra == "rdp"`
`python_version < "3.13" and extra == "windows"` |
+| selected-platform | `openadapt==1.17.0` | `openadapt-flow` | `<3.0.0,>=2.0.0` | `2.5.0` | `(sys_platform == "darwin" and python_version < "3.13") and extra == "all"`
`(sys_platform == "darwin" and python_version < "3.13") and extra == "macos"`
`(sys_platform == "linux" and python_version < "3.13") and extra == "all"`
`(sys_platform == "linux" and python_version < "3.13") and extra == "linux"`
`python_version < "3.13"`
`python_version < "3.13" and extra == "all"`
`python_version < "3.13" and extra == "browser"`
`python_version < "3.13" and extra == "capture"`
`python_version < "3.13" and extra == "core"`
`python_version < "3.13" and extra == "flow"`
`python_version < "3.13" and extra == "privacy"`
`python_version < "3.13" and extra == "rdp"`
`python_version < "3.13" and extra == "windows"` |
## Cross-component schemas
| Contract | Owner | Accepted versions | Exact source |
|---|---|---|---|
-| `workflow_bundle` | `flow` | `schema_version` [1, 2] | [`packages/flow/openadapt_flow/bundle_validation.py`](https://github.com/OpenAdaptAI/OpenAdapt/blob/b6a9d46b00b92454c18e844f0a8f6929330e87b6/packages/flow/openadapt_flow/bundle_validation.py) `936c42daa7618a07222a6807080045aa5e8a25ebcf4dd2c7488351fc13663d18`
[`packages/flow/openadapt_flow/ir.py`](https://github.com/OpenAdaptAI/OpenAdapt/blob/b6a9d46b00b92454c18e844f0a8f6929330e87b6/packages/flow/openadapt_flow/ir.py) `739fe7f4841a018668840559cead446d25c6a22d1a33ed3721bd553d10e9366a` |
+| `workflow_bundle` | `flow` | `schema_version` [1, 2] | [`packages/flow/openadapt_flow/bundle_validation.py`](https://github.com/OpenAdaptAI/OpenAdapt/blob/5a3bf49a0bf2d662430386295a1c34036c064d58/packages/flow/openadapt_flow/bundle_validation.py) `63532fb8e49f25a8aba8dccca505362a9293b8db9cbc0f2904dd8f9a6e23fea8`
[`packages/flow/openadapt_flow/ir.py`](https://github.com/OpenAdaptAI/OpenAdapt/blob/5a3bf49a0bf2d662430386295a1c34036c064d58/packages/flow/openadapt_flow/ir.py) `efb7fdce6fa625f631901389ca767b016f4db3111caa2905321ff3da675e376d` |
| `capture_structural_observation` | `capture` | `openadapt.capture.structural-observation/v` [1] | [`openadapt_capture/structural.py`](https://github.com/OpenAdaptAI/openadapt-capture/blob/06ade22948c12dcc6608bd54b5cca5c9c306e77c/openadapt_capture/structural.py) `8f8bce37148a3357ace147c46a7c45e6d5501b70622b24b1ac2490f2def304fc` |
| `control_overlay_frame` | `types` | `openadapt.control-overlay-frame/v` [1, 2] | [`packages/types/openadapt_types/control_overlay.py`](https://github.com/OpenAdaptAI/OpenAdapt/blob/f2f04c5b432d88b262d46103a542b0e2c3dcc8a9/packages/types/openadapt_types/control_overlay.py) `3aa01f89b43e12478c9e2ff1a9e61689397370691d15247ded9dcd5b05242790`
[`packages/types/openadapt_types/control_overlay_tracking.py`](https://github.com/OpenAdaptAI/OpenAdapt/blob/f2f04c5b432d88b262d46103a542b0e2c3dcc8a9/packages/types/openadapt_types/control_overlay_tracking.py) `af8d934f0572c5f4e9c63ba720d4a1d0e73c8cdf3f7b87cc4562c01a8a551755` |
| `control_overlay_timeline` | `types` | `openadapt.control-overlay-timeline/v` [1, 2] | [`packages/types/openadapt_types/control_overlay.py`](https://github.com/OpenAdaptAI/OpenAdapt/blob/f2f04c5b432d88b262d46103a542b0e2c3dcc8a9/packages/types/openadapt_types/control_overlay.py) `3aa01f89b43e12478c9e2ff1a9e61689397370691d15247ded9dcd5b05242790`
[`packages/types/openadapt_types/control_overlay_tracking.py`](https://github.com/OpenAdaptAI/OpenAdapt/blob/f2f04c5b432d88b262d46103a542b0e2c3dcc8a9/packages/types/openadapt_types/control_overlay_tracking.py) `af8d934f0572c5f4e9c63ba720d4a1d0e73c8cdf3f7b87cc4562c01a8a551755` |
| `human_decision_task` | `types` | `openadapt.human-decision-task/v` [1, 2] | [`packages/types/openadapt_types/human_decision.py`](https://github.com/OpenAdaptAI/OpenAdapt/blob/f2f04c5b432d88b262d46103a542b0e2c3dcc8a9/packages/types/openadapt_types/human_decision.py) `0bb39a3575f511e10d01d92858b340982adee2f0fc20d2409441dbc0b2c55c4f` |
| `human_decision_receipt` | `types` | `openadapt.human-decision-receipt/v` [1] | [`packages/types/openadapt_types/human_decision.py`](https://github.com/OpenAdaptAI/OpenAdapt/blob/f2f04c5b432d88b262d46103a542b0e2c3dcc8a9/packages/types/openadapt_types/human_decision.py) `0bb39a3575f511e10d01d92858b340982adee2f0fc20d2409441dbc0b2c55c4f` |
-| `human_decision_relay` | `flow` | `openadapt.human-decision-relay/v` [2] | [`packages/flow/openadapt_flow/console/decision_relay.py`](https://github.com/OpenAdaptAI/OpenAdapt/blob/b6a9d46b00b92454c18e844f0a8f6929330e87b6/packages/flow/openadapt_flow/console/decision_relay.py) `d5320404d4342dbb52be1f2ead1cdb5cf0e22877feaaa1e47974b301df697292` |
-| `remote_decision_projection` | `flow` | `openadapt.remote-decision-projection/v` [1] | [`packages/flow/openadapt_flow/console/human_decisions.py`](https://github.com/OpenAdaptAI/OpenAdapt/blob/b6a9d46b00b92454c18e844f0a8f6929330e87b6/packages/flow/openadapt_flow/console/human_decisions.py) `02651ad8888a5c17bfff6fb47165ba861193243dc0f7c951f8b94f544ca19477` |
-| `runtime_validation` | `flow` | `openadapt.runtime-validation/v` [1, 2, 3] | [`packages/flow/openadapt_flow/runtime_validation.py`](https://github.com/OpenAdaptAI/OpenAdapt/blob/b6a9d46b00b92454c18e844f0a8f6929330e87b6/packages/flow/openadapt_flow/runtime_validation.py) `a5a220f570c5ee5769ac2f21d8d140bb69373d4de2ccc0ebfd08540122883f0e` |
+| `human_decision_relay` | `flow` | `openadapt.human-decision-relay/v` [2] | [`packages/flow/openadapt_flow/console/decision_relay.py`](https://github.com/OpenAdaptAI/OpenAdapt/blob/5a3bf49a0bf2d662430386295a1c34036c064d58/packages/flow/openadapt_flow/console/decision_relay.py) `d5320404d4342dbb52be1f2ead1cdb5cf0e22877feaaa1e47974b301df697292` |
+| `remote_decision_projection` | `flow` | `openadapt.remote-decision-projection/v` [1] | [`packages/flow/openadapt_flow/console/human_decisions.py`](https://github.com/OpenAdaptAI/OpenAdapt/blob/5a3bf49a0bf2d662430386295a1c34036c064d58/packages/flow/openadapt_flow/console/human_decisions.py) `02651ad8888a5c17bfff6fb47165ba861193243dc0f7c951f8b94f544ca19477` |
+| `runtime_validation` | `flow` | `openadapt.runtime-validation/v` [1, 2, 3] | [`packages/flow/openadapt_flow/runtime_validation.py`](https://github.com/OpenAdaptAI/OpenAdapt/blob/5a3bf49a0bf2d662430386295a1c34036c064d58/packages/flow/openadapt_flow/runtime_validation.py) `a5a220f570c5ee5769ac2f21d8d140bb69373d4de2ccc0ebfd08540122883f0e` |
## Host operating systems
@@ -82,5 +82,5 @@ Compatibility and delivery facts do not assign lifecycle state. Use the signed P
## Qualification evidence
- [openadapt-public-status](https://openadapt.ai/status.json): Canonical machine-readable release, capability, and substrate qualification status, maintained in openadapt-web (public/status.json).
-- [flow-public-demo-evidence-packs](https://github.com/OpenAdaptAI/OpenAdapt/tree/b6a9d46b00b92454c18e844f0a8f6929330e87b6/packages/flow/public-demo/evidence-packs): Versioned public evidence packs with per-pack manifest files, replay evidence, and fault-case results.
-- [flow-effectbench-task-pack](https://github.com/OpenAdaptAI/OpenAdapt/blob/b6a9d46b00b92454c18e844f0a8f6929330e87b6/packages/flow/benchmark/effectbench/task_pack/manifest.json): Effect-verification benchmark task pack manifest used by the flow qualification harness.
+- [flow-public-demo-evidence-packs](https://github.com/OpenAdaptAI/OpenAdapt/tree/5a3bf49a0bf2d662430386295a1c34036c064d58/packages/flow/public-demo/evidence-packs): Versioned public evidence packs with per-pack manifest files, replay evidence, and fault-case results.
+- [flow-effectbench-task-pack](https://github.com/OpenAdaptAI/OpenAdapt/blob/5a3bf49a0bf2d662430386295a1c34036c064d58/packages/flow/benchmark/effectbench/task_pack/manifest.json): Effect-verification benchmark task pack manifest used by the flow qualification harness.
diff --git a/llms-full.txt b/llms-full.txt
index 05a1ee7a21..97679a6746 100644
--- a/llms-full.txt
+++ b/llms-full.txt
@@ -2253,6 +2253,8 @@ Shadow mode checks the writes of an agent you already run against the system of
### Features
+- Self-repair's new clicks no longer need a browser. On a backend with an accessibility tree, a click with no record identity under it is admitted when its element (role, name, ancestry, window) is the only one with that locator in the pinned window and a record check covers every save: a system-of-record verifier, a `standard` or `regulated` profile, and confirmed effects and an armed identity check on each save. On a pixel-only backend the record check alone admits it. The new step carries the dialog around it as its identity band, a screen pre-check, and only a person approves such a fix; a signed standard change can't. The browser rule is unchanged. `MacOSBackend.element_identity_at` reads the AX element, the repair hold answers `element_identity`, and agent teach takes the dialog box from it. Design: `docs/REPAIR_ON_EVERY_BACKEND.md` ([#1709](https://github.com/OpenAdaptAI/OpenAdapt/pull/1709)).
+
- Shadow mode: `openadapt shadow` runs beside an agent you already have, as a loopback pass-through proxy, and checks each write it makes against the system of record (`CONFIRMED`, `REFUTED`, `UNCERTAIN`). It never blocks, changes or resends a write; the only delay is the read before a write, at most `hold_ms`, measured per write. FHIR writes are checked by `ensure_record.verify_ensure` and SQL writes by `verifier.begin_write`/`verify_write` with the whole-database frame. Each `CONFIRMED` is checked again later (`recheck_s`, or the contract's `recheck_after_s`), and a late `REFUTED` gets its own line in `report.html` and `report.json`. `verify_ensure` takes `declared`: resources of other writes the caller saw, judged on their own ([#1678](https://github.com/OpenAdaptAI/OpenAdapt/pull/1678)).
- The whole-database frame (`frame.database`) and its recheck now work on PostgreSQL (`dialect: postgresql`). The snapshot is one read-only repeatable-read transaction over every schema but the system ones. Every column type is read as text by a declared rule, in a session pinned to UTC and exact float output: `numeric` at full precision (with its stored scale when it has no declared one), floats exactly, `bytea` as hex, `json` as stored and `jsonb` canonical, arrays, ranges, enums, domains, `uuid`, network, time, `interval`, `money`, bit, `xml`, text-search and geometric types. A table without a primary key is keyed by the whole row, with duplicates counted. A column type without a rule, a table the account can't read in full (no `SELECT`, row-level security), a foreign table and an unreadable sequence are refused unless the allow-list skips them with a reason. A sequence a column owns is that table's counter, and a counter set back counts as a change; any other sequence is compared as a one-row table. Large objects are read by the SHA-256 of their content. `frame.database.schemas` means the same as on MySQL (each must be visible, and every visible schema is read); a schema is left out only by an `allow_tables` entry `.*` with `mode: skip` and a reason.
diff --git a/openadapt/cli.py b/openadapt/cli.py
index 6c046d70a9..d8ca1a1bce 100644
--- a/openadapt/cli.py
+++ b/openadapt/cli.py
@@ -848,9 +848,9 @@ def _warn_about_launcher_conflicts() -> None:
_SECRET_REFERENCE = re.compile(r"^(?:env:[A-Z][A-Z0-9_]*|keychain:[^/\s]+/[^/\s]+)$")
# Flow 1.33 and 1.34 probe four X11 libraries in the wrong case, so on Linux
# they refuse to download Chromium on hosts that have them. 1.35.0 fixed it.
-# Launcher 1.18 requires Flow 2.2: the quickstart uses its offline Seals,
-# shared browser and OCR prewarm.
-_SUPPORTED_FLOW_RANGE = ">=2.2.0,<3.0.0"
+# Launcher 1.19 requires Flow 2.5: `openadapt shadow` forwards to its shadow
+# command, and `openadapt verify serve` offers its recheck.
+_SUPPORTED_FLOW_RANGE = ">=2.5.0,<3.0.0"
_RDP_INSTALL_COMMAND = _pip_install("openadapt[rdp]")
_CHROMIUM_SYSTEM_LIBS_COMMAND = (
f"{shlex.quote(sys.executable)} -m playwright install-deps chromium"
@@ -860,7 +860,7 @@ def _warn_about_launcher_conflicts() -> None:
def _supported_flow_version(value: str) -> bool:
"""Return whether a stable Flow version is in the launcher's supported range."""
parsed = _release_tuple(value)
- return parsed is not None and (2, 2, 0) <= parsed < (3, 0, 0)
+ return parsed is not None and (2, 5, 0) <= parsed < (3, 0, 0)
# The Python modules each native or remote Flow backend imports when its
diff --git a/platform-manifest.json b/platform-manifest.json
index de4b51e526..a59c049229 100644
--- a/platform-manifest.json
+++ b/platform-manifest.json
@@ -1,7 +1,7 @@
{
"manifest_kind": "openadapt-platform-release-manifest",
"schema_version": "2.0.0",
- "generated_at": "2026-10-20T17:25:50+00:00",
+ "generated_at": "2026-11-03T00:23:35+00:00",
"generation": {
"files": [
{
@@ -20,7 +20,7 @@
"agent": "2.2.1",
"capture": "1.2.2",
"desktop": "0.16.0",
- "flow": "2.3.0",
+ "flow": "2.5.0",
"launcher": "1.17.0",
"privacy": "1.1.2",
"types": "0.24.0"
@@ -306,7 +306,7 @@
},
"flow": {
"package": "openadapt-flow",
- "version": "2.3.0",
+ "version": "2.5.0",
"source": "pypi",
"requires_python": "<3.13,>=3.10",
"requires": {
@@ -351,24 +351,24 @@
],
"provenance": {
"repository": "OpenAdaptAI/OpenAdapt",
- "release_ref": "openadapt-flow-v2.3.0",
- "commit": "b6a9d46b00b92454c18e844f0a8f6929330e87b6",
- "tree": "14bf88ccefe00708bb51003d6262876c555e7271",
- "url": "https://github.com/OpenAdaptAI/OpenAdapt/tree/openadapt-flow-v2.3.0/packages/flow",
+ "release_ref": "openadapt-flow-v2.5.0",
+ "commit": "5a3bf49a0bf2d662430386295a1c34036c064d58",
+ "tree": "cab536c90e79fcfe887b8876e884ec2ed651f23e",
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/tree/openadapt-flow-v2.5.0/packages/flow",
"source_root": "packages/flow"
},
"artifacts": [
{
"type": "bdist_wheel",
- "filename": "openadapt_flow-2.3.0-py3-none-any.whl",
- "url": "https://files.pythonhosted.org/packages/01/a7/915b9d0110da17393741e60d0c19574f7a14c9ca54c1a9e5a13af2b15280/openadapt_flow-2.3.0-py3-none-any.whl",
- "sha256": "6a2dbd22eb15bd449fc12a6dc36dcc8da7c0316f9faac80cbff8d6e9a433222b"
+ "filename": "openadapt_flow-2.5.0-py3-none-any.whl",
+ "url": "https://files.pythonhosted.org/packages/7d/7b/76f2027c37fd059c0c1409cd6ca6c995da13fc5891c40b138f9b0cf1362f/openadapt_flow-2.5.0-py3-none-any.whl",
+ "sha256": "1dbc5d42d103cfa8a452d95805dde60c7d460035ec4d81df5cc642a32f118339"
},
{
"type": "sdist",
- "filename": "openadapt_flow-2.3.0.tar.gz",
- "url": "https://files.pythonhosted.org/packages/08/81/a557d7472ebd192992fe37bd9493bb998941f6af5b0258a95403c4634dc5/openadapt_flow-2.3.0.tar.gz",
- "sha256": "39f3b853987227063257a1ffcd402ca3a3120e508d901578622cd0351f1ce12d"
+ "filename": "openadapt_flow-2.5.0.tar.gz",
+ "url": "https://files.pythonhosted.org/packages/b3/9e/c4b87cfdba0b1ae03d57062bad41686cb9b3affd46e901ee1749fcf1e5b8/openadapt_flow-2.5.0.tar.gz",
+ "sha256": "54d99e77976ee7d9e6bc171b2ea148497528ead77b226fdf6fd4713b37c689b3"
}
]
},
@@ -613,20 +613,20 @@
},
"selected": {
"openadapt": "1.17.0",
- "openadapt-flow": "2.3.0"
+ "openadapt-flow": "2.5.0"
}
},
"customer_runner": {
"kind": "customer-controlled-runner",
- "version": "2.3.0",
+ "version": "2.5.0",
"source_component": "flow",
"entry_point": "openadapt-flow",
"requirements": {
- "openadapt-flow": "==2.3.0",
+ "openadapt-flow": "==2.5.0",
"openadapt-types": "<0.25.0,>=0.24.0"
},
"selected": {
- "openadapt-flow": "2.3.0",
+ "openadapt-flow": "2.5.0",
"openadapt-types": "0.24.0"
}
},
@@ -662,7 +662,7 @@
},
"selected": {
"openadapt-agent": "2.2.1",
- "openadapt-flow": "2.3.0"
+ "openadapt-flow": "2.5.0"
}
}
},
@@ -739,7 +739,7 @@
"target_role": "flow",
"target_package": "openadapt-flow",
"requires": "<3,>=2.2.0",
- "resolved": "2.3.0",
+ "resolved": "2.5.0",
"activations": [
{
"extras": null,
@@ -791,7 +791,7 @@
"scope": "selected-platform",
"source_role": "flow",
"source_package": "openadapt-flow",
- "source_version": "2.3.0",
+ "source_version": "2.5.0",
"target_role": "capture",
"target_package": "openadapt-capture",
"requires": ">=1.2.0",
@@ -807,7 +807,7 @@
"scope": "selected-platform",
"source_role": "flow",
"source_package": "openadapt-flow",
- "source_version": "2.3.0",
+ "source_version": "2.5.0",
"target_role": "privacy",
"target_package": "openadapt-privacy",
"requires": ">=1.0.0",
@@ -823,7 +823,7 @@
"scope": "selected-platform",
"source_role": "flow",
"source_package": "openadapt-flow",
- "source_version": "2.3.0",
+ "source_version": "2.5.0",
"target_role": "types",
"target_package": "openadapt-types",
"requires": "<0.25.0,>=0.24.0",
@@ -891,7 +891,7 @@
"target_role": "flow",
"target_package": "openadapt-flow",
"requires": "<3.0.0,>=2.0.0",
- "resolved": "2.3.0",
+ "resolved": "2.5.0",
"activations": [
{
"extras": null,
@@ -979,19 +979,19 @@
],
"minimum": 1,
"maximum": 2,
- "source_commit": "b6a9d46b00b92454c18e844f0a8f6929330e87b6",
+ "source_commit": "5a3bf49a0bf2d662430386295a1c34036c064d58",
"source_files": [
{
"path": "packages/flow/openadapt_flow/bundle_validation.py",
- "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/b6a9d46b00b92454c18e844f0a8f6929330e87b6/packages/flow/openadapt_flow/bundle_validation.py",
- "git_blob": "2c8143baabcf53f7fb2b8fea7dcf75794d60e38f",
- "sha256": "936c42daa7618a07222a6807080045aa5e8a25ebcf4dd2c7488351fc13663d18"
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/5a3bf49a0bf2d662430386295a1c34036c064d58/packages/flow/openadapt_flow/bundle_validation.py",
+ "git_blob": "78c30442341699d497d934699154a72fe213791d",
+ "sha256": "63532fb8e49f25a8aba8dccca505362a9293b8db9cbc0f2904dd8f9a6e23fea8"
},
{
"path": "packages/flow/openadapt_flow/ir.py",
- "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/b6a9d46b00b92454c18e844f0a8f6929330e87b6/packages/flow/openadapt_flow/ir.py",
- "git_blob": "aee7c6ba869c0b8db388d83bd6953ae44d31e577",
- "sha256": "739fe7f4841a018668840559cead446d25c6a22d1a33ed3721bd553d10e9366a"
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/5a3bf49a0bf2d662430386295a1c34036c064d58/packages/flow/openadapt_flow/ir.py",
+ "git_blob": "fd235efbebf639c3f047eb0ca7421115733cfb85",
+ "sha256": "efb7fdce6fa625f631901389ca767b016f4db3111caa2905321ff3da675e376d"
}
]
},
@@ -1108,11 +1108,11 @@
],
"minimum": 2,
"maximum": 2,
- "source_commit": "b6a9d46b00b92454c18e844f0a8f6929330e87b6",
+ "source_commit": "5a3bf49a0bf2d662430386295a1c34036c064d58",
"source_files": [
{
"path": "packages/flow/openadapt_flow/console/decision_relay.py",
- "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/b6a9d46b00b92454c18e844f0a8f6929330e87b6/packages/flow/openadapt_flow/console/decision_relay.py",
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/5a3bf49a0bf2d662430386295a1c34036c064d58/packages/flow/openadapt_flow/console/decision_relay.py",
"git_blob": "297d2f71727f83496221091a4237d704c25665d6",
"sha256": "d5320404d4342dbb52be1f2ead1cdb5cf0e22877feaaa1e47974b301df697292"
}
@@ -1126,11 +1126,11 @@
],
"minimum": 1,
"maximum": 1,
- "source_commit": "b6a9d46b00b92454c18e844f0a8f6929330e87b6",
+ "source_commit": "5a3bf49a0bf2d662430386295a1c34036c064d58",
"source_files": [
{
"path": "packages/flow/openadapt_flow/console/human_decisions.py",
- "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/b6a9d46b00b92454c18e844f0a8f6929330e87b6/packages/flow/openadapt_flow/console/human_decisions.py",
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/5a3bf49a0bf2d662430386295a1c34036c064d58/packages/flow/openadapt_flow/console/human_decisions.py",
"git_blob": "caaf87396aca4770eace5eead3fdf6515e31fbfc",
"sha256": "02651ad8888a5c17bfff6fb47165ba861193243dc0f7c951f8b94f544ca19477"
}
@@ -1146,11 +1146,11 @@
],
"minimum": 1,
"maximum": 3,
- "source_commit": "b6a9d46b00b92454c18e844f0a8f6929330e87b6",
+ "source_commit": "5a3bf49a0bf2d662430386295a1c34036c064d58",
"source_files": [
{
"path": "packages/flow/openadapt_flow/runtime_validation.py",
- "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/b6a9d46b00b92454c18e844f0a8f6929330e87b6/packages/flow/openadapt_flow/runtime_validation.py",
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/5a3bf49a0bf2d662430386295a1c34036c064d58/packages/flow/openadapt_flow/runtime_validation.py",
"git_blob": "fd6121209e50abcab41bb8b70f95ff2647d9303f",
"sha256": "a5a220f570c5ee5769ac2f21d8d140bb69373d4de2ccc0ebfd08540122883f0e"
}
@@ -1170,7 +1170,7 @@
">=0.1.0"
],
"openadapt-flow": [
- ">=2.2.0,<3.0.0"
+ ">=2.5.0,<3.0.0"
],
"openadapt-grounding": [
">=0.1.0"
@@ -1236,10 +1236,10 @@
"repository": "OpenAdaptAI/OpenAdapt",
"path": "packages/flow/public-demo/evidence-packs",
"description": "Versioned public evidence packs with per-pack manifest files, replay evidence, and fault-case results.",
- "source_commit": "b6a9d46b00b92454c18e844f0a8f6929330e87b6",
+ "source_commit": "5a3bf49a0bf2d662430386295a1c34036c064d58",
"source_object_type": "tree",
"source_object_sha": "2eac2252f62349e873b461866546dd0295169a19",
- "url": "https://github.com/OpenAdaptAI/OpenAdapt/tree/b6a9d46b00b92454c18e844f0a8f6929330e87b6/packages/flow/public-demo/evidence-packs"
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/tree/5a3bf49a0bf2d662430386295a1c34036c064d58/packages/flow/public-demo/evidence-packs"
},
{
"id": "flow-effectbench-task-pack",
@@ -1248,10 +1248,10 @@
"repository": "OpenAdaptAI/OpenAdapt",
"path": "packages/flow/benchmark/effectbench/task_pack/manifest.json",
"description": "Effect-verification benchmark task pack manifest used by the flow qualification harness.",
- "source_commit": "b6a9d46b00b92454c18e844f0a8f6929330e87b6",
+ "source_commit": "5a3bf49a0bf2d662430386295a1c34036c064d58",
"source_object_type": "blob",
"source_object_sha": "1c568d2289342e10e3556c5c46cd08a0e370ac84",
- "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/b6a9d46b00b92454c18e844f0a8f6929330e87b6/packages/flow/benchmark/effectbench/task_pack/manifest.json"
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/5a3bf49a0bf2d662430386295a1c34036c064d58/packages/flow/benchmark/effectbench/task_pack/manifest.json"
}
],
"signature": {
diff --git a/pyproject.toml b/pyproject.toml
index 368b201a23..a59fbe1195 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openadapt"
-version = "1.18.0"
+version = "1.19.0"
description = "Enters approved information into the apps your team already uses, checks that it saved, and stops for a person when something doesn't match."
# The package description is README.md with its lifecycle block swapped for a
# link to the live record; see hatch_build.py.
@@ -59,10 +59,10 @@ dependencies = [
# The quickstart's terminal view (MIT). Pure Python; the engine doesn't
# depend on it. Gated like the engine: without it the quickstart can't run.
"rich>=13.0.0; python_version < '3.13'",
- # Every Flow floor is 2.2.0: the quickstart uses Flow's offline Seals,
- # shared browser and OCR prewarm, which 2.0 and 2.1 lack
- # (tests/test_flow_floor.py).
- "openadapt-flow[browser,hosted]>=2.2.0,<3.0.0; python_version < '3.13'",
+ # Every Flow floor is 2.5.0: `openadapt shadow` forwards to Flow's shadow
+ # command, and `openadapt verify serve` offers its recheck, which 2.4 and
+ # earlier lack (tests/test_flow_floor.py).
+ "openadapt-flow[browser,hosted]>=2.5.0,<3.0.0; python_version < '3.13'",
# Day-1 partner kit: `pip install openadapt` must expose the
# `openadapt-agent` command without a second package. 2.1.0 adds Seal checks;
# `--bundles`; the bundle-free tutorial `serve --allow-run` needs a later
@@ -77,7 +77,7 @@ browser = [
# launcher now selects this Flow contract because the bundled quickstart
# uses the browser. OpenAdapt requests Chromium on first browser use after
# the host dependency check passes.
- "openadapt-flow[browser]>=2.2.0,<3.0.0; python_version < '3.13'",
+ "openadapt-flow[browser]>=2.5.0,<3.0.0; python_version < '3.13'",
]
# Local human desktop recording. Flow owns the supported capture adapter
# contract; the direct floor prevents an already-installed pre-1.0 Capture
@@ -85,22 +85,22 @@ browser = [
# separately provisioned executable, not a Python-package dependency.
capture = [
"openadapt-capture>=1.2.0,<2.0.0",
- "openadapt-flow[capture]>=2.2.0,<3.0.0; python_version < '3.13'",
+ "openadapt-flow[capture]>=2.5.0,<3.0.0; python_version < '3.13'",
]
# Replay substrate dependencies stay separate from capture: recording observes
# the human on the local interactive desktop, while replay may target a WAA
# agent, a native app, or a remote framebuffer.
windows = [
- "openadapt-flow[windows]>=2.2.0,<3.0.0; python_version < '3.13'",
+ "openadapt-flow[windows]>=2.5.0,<3.0.0; python_version < '3.13'",
]
macos = [
- "openadapt-flow[macos]>=2.2.0,<3.0.0; sys_platform == 'darwin' and python_version < '3.13'",
+ "openadapt-flow[macos]>=2.5.0,<3.0.0; sys_platform == 'darwin' and python_version < '3.13'",
]
linux = [
- "openadapt-flow[linux]>=2.2.0,<3.0.0; sys_platform == 'linux' and python_version < '3.13'",
+ "openadapt-flow[linux]>=2.5.0,<3.0.0; sys_platform == 'linux' and python_version < '3.13'",
]
rdp = [
- "openadapt-flow[rdp]>=2.2.0,<3.0.0; python_version < '3.13'",
+ "openadapt-flow[rdp]>=2.5.0,<3.0.0; python_version < '3.13'",
]
ml = [
"openadapt-ml>=0.2.0",
@@ -118,13 +118,13 @@ retrieval = [
"openadapt-retrieval>=0.1.0",
]
privacy = [
- "openadapt-flow[privacy]>=2.2.0,<3.0.0; python_version < '3.13'",
+ "openadapt-flow[privacy]>=2.5.0,<3.0.0; python_version < '3.13'",
]
# `flow` now ships in the base install (see `dependencies` above). This
# extra is kept for backward compatibility so `pip install openadapt[flow]`
# and the `all` bundle still resolve.
flow = [
- "openadapt-flow>=2.2.0,<3.0.0; python_version < '3.13'",
+ "openadapt-flow>=2.5.0,<3.0.0; python_version < '3.13'",
]
# `openadapt-agent` also ships in the base install. Keep the extra so
# `pip install openadapt[agent]` still resolves.
diff --git a/tests/test_cli_shell_hints.py b/tests/test_cli_shell_hints.py
index ccda94c325..129718ff3c 100644
--- a/tests/test_cli_shell_hints.py
+++ b/tests/test_cli_shell_hints.py
@@ -95,12 +95,12 @@ def test_deploy_console_hint_is_double_quoted(monkeypatch, supported_python):
"importlib.util.find_spec",
lambda name, *args: object() if name in present else None,
)
- monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.1")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.5.1")
result = CliRunner().invoke(cli_main, ["deploy"])
assert result.exit_code == 0, result.output
- assert '"openadapt-flow[console]==2.2.1"' in result.output
+ assert '"openadapt-flow[console]==2.5.1"' in result.output
assert not _UNQUOTED_SPEC.search(result.output), result.output
diff --git a/tests/test_cli_smoke.py b/tests/test_cli_smoke.py
index 2dabd01078..91f17f2afc 100644
--- a/tests/test_cli_smoke.py
+++ b/tests/test_cli_smoke.py
@@ -380,29 +380,29 @@ def test_launcher_flow_and_substrate_extras_metadata():
assert (
metadata["dependencies"].count(
- f"openadapt-flow[browser,hosted]>=2.2.0,<3.0.0; {supported}"
+ f"openadapt-flow[browser,hosted]>=2.5.0,<3.0.0; {supported}"
)
== 1
)
assert (
metadata["dependencies"].count(f"openadapt-agent>=2.1.0,<3; {supported}") == 1
)
- assert extras["flow"] == [f"openadapt-flow>=2.2.0,<3.0.0; {supported}"]
+ assert extras["flow"] == [f"openadapt-flow>=2.5.0,<3.0.0; {supported}"]
assert extras["agent"] == [f"openadapt-agent>=2.1.0,<3; {supported}"]
- assert extras["browser"] == [f"openadapt-flow[browser]>=2.2.0,<3.0.0; {supported}"]
- assert extras["privacy"] == [f"openadapt-flow[privacy]>=2.2.0,<3.0.0; {supported}"]
+ assert extras["browser"] == [f"openadapt-flow[browser]>=2.5.0,<3.0.0; {supported}"]
+ assert extras["privacy"] == [f"openadapt-flow[privacy]>=2.5.0,<3.0.0; {supported}"]
assert extras["capture"] == [
"openadapt-capture>=1.2.0,<2.0.0",
- f"openadapt-flow[capture]>=2.2.0,<3.0.0; {supported}",
+ f"openadapt-flow[capture]>=2.5.0,<3.0.0; {supported}",
]
- assert extras["windows"] == [f"openadapt-flow[windows]>=2.2.0,<3.0.0; {supported}"]
+ assert extras["windows"] == [f"openadapt-flow[windows]>=2.5.0,<3.0.0; {supported}"]
assert extras["macos"] == [
- f"openadapt-flow[macos]>=2.2.0,<3.0.0; sys_platform == 'darwin' and {supported}"
+ f"openadapt-flow[macos]>=2.5.0,<3.0.0; sys_platform == 'darwin' and {supported}"
]
assert extras["linux"] == [
- f"openadapt-flow[linux]>=2.2.0,<3.0.0; sys_platform == 'linux' and {supported}"
+ f"openadapt-flow[linux]>=2.5.0,<3.0.0; sys_platform == 'linux' and {supported}"
]
- assert extras["rdp"] == [f"openadapt-flow[rdp]>=2.2.0,<3.0.0; {supported}"]
+ assert extras["rdp"] == [f"openadapt-flow[rdp]>=2.5.0,<3.0.0; {supported}"]
assert extras["all"] == [
"openadapt[browser,core,grounding,retrieval,privacy,flow,windows,rdp,agent]",
"openadapt[macos]; sys_platform == 'darwin'",
@@ -433,7 +433,7 @@ def test_released_flow_x11_sonames_are_case_corrected(monkeypatch):
)
-@pytest.mark.parametrize("version", ["2.2.0", "2.2.1", "2.99.0"])
+@pytest.mark.parametrize("version", ["2.5.0", "2.5.1", "2.99.0"])
def test_launcher_accepts_flow_versions_in_the_supported_range(version):
assert _supported_flow_version(version)
@@ -533,7 +533,7 @@ def test_deploy_native_backend_fails_without_its_platform_extra(
monkeypatch, backend, host, modules, install
):
_host(monkeypatch, host, {"openadapt_flow"})
- monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.0")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.5.0")
result = CliRunner().invoke(cli_main, ["deploy", "--backend", backend])
@@ -550,7 +550,7 @@ def test_deploy_native_backend_passes_with_its_platform_extra(
monkeypatch, backend, host, modules, install
):
_host(monkeypatch, host, {"openadapt_flow"} | modules)
- monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.0")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.5.0")
result = CliRunner().invoke(cli_main, ["deploy", "--backend", backend])
@@ -690,7 +690,7 @@ def test_deploy_preflight_composes_existing_flow_interfaces_without_secrets(
else None
),
)
- monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.0")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.5.0")
monkeypatch.setenv("BYOC_CONNECTOR_TOKEN", "connector-token-value")
result = CliRunner().invoke(
cli_main,
@@ -715,13 +715,13 @@ def test_deploy_base_hosted_install_gives_conditional_console_setup(monkeypatch)
"importlib.util.find_spec",
lambda name: object() if name in {"openadapt_flow", "aardwolf"} else None,
)
- monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.0")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.5.0")
result = CliRunner().invoke(cli_main, ["deploy", "--backend", "rdp"])
assert result.exit_code == 0, result.output
assert "optional local operator console is not installed" in result.output
- assert 'python -m pip install "openadapt-flow[console]==2.2.0"' in result.output
+ assert 'python -m pip install "openadapt-flow[console]==2.5.0"' in result.output
assert "openadapt flow console --bundles" not in result.output
assert "Re-run this preflight" in result.output
@@ -736,13 +736,13 @@ def test_deploy_console_requires_openadapt_types(monkeypatch):
else None
),
)
- monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.0")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.5.0")
result = CliRunner().invoke(cli_main, ["deploy", "--backend", "rdp"])
assert result.exit_code == 0, result.output
assert "optional local operator console is not installed" in result.output
- assert 'python -m pip install "openadapt-flow[console]==2.2.0"' in result.output
+ assert 'python -m pip install "openadapt-flow[console]==2.5.0"' in result.output
assert "openadapt flow console --bundles" not in result.output
@@ -767,7 +767,7 @@ def test_deploy_preflight_fails_without_web_runtime(monkeypatch):
"importlib.util.find_spec",
lambda name: object() if name == "openadapt_flow" else None,
)
- monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.0")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.5.0")
result = CliRunner().invoke(cli_main, ["deploy", "--backend", "web"])
@@ -785,7 +785,7 @@ def test_deploy_preflight_fails_without_rdp_transport(monkeypatch):
"importlib.util.find_spec",
lambda name: object() if name == "openadapt_flow" else None,
)
- monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.0")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.5.0")
result = CliRunner().invoke(cli_main, ["deploy", "--backend", "rdp"])
@@ -807,7 +807,7 @@ def test_deploy_preflight_fails_for_unsupported_flow(monkeypatch, flow_version):
assert result.exit_code != 0
assert "[UNSUPPORTED]" in result.output
- assert ">=2.2.0,<3.0.0" in result.output
+ assert ">=2.5.0,<3.0.0" in result.output
assert "Preflight passed" not in result.output
diff --git a/tests/test_deploy_preflight_readiness.py b/tests/test_deploy_preflight_readiness.py
index b4999cdfd7..d1eef428ad 100644
--- a/tests/test_deploy_preflight_readiness.py
+++ b/tests/test_deploy_preflight_readiness.py
@@ -29,7 +29,7 @@ def _deploy(monkeypatch, args, installed=READY_WEB, host=None):
"importlib.util.find_spec",
lambda name: object() if name in installed else None,
)
- monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.0")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.5.0")
return CliRunner().invoke(cli_main, ["deploy", *args])
diff --git a/tests/test_flow_floor.py b/tests/test_flow_floor.py
index c653c91c3f..751df09bcb 100644
--- a/tests/test_flow_floor.py
+++ b/tests/test_flow_floor.py
@@ -24,10 +24,10 @@
import tomli as tomllib
ROOT = Path(__file__).resolve().parents[1]
-# Launcher 1.18 requires Flow 2.2 (the quickstart's offline Seals, shared
-# browser and OCR prewarm), so 1.x, 2.0 and 2.1 are below the floor.
-BELOW_FLOOR = ["1.29.0", "1.34.9", "1.35.1", "2.0.0", "2.1.0", "2.1.9"]
-ACCEPTED = "2.2.0"
+# Launcher 1.19 requires Flow 2.5 (`openadapt shadow` and the recheck that
+# `openadapt verify serve` offers), so 1.x and 2.0 to 2.4 are below the floor.
+BELOW_FLOOR = ["1.29.0", "1.35.1", "2.0.0", "2.2.0", "2.4.1"]
+ACCEPTED = "2.5.0"
def _flow_requirements() -> list[Requirement]:
diff --git a/uv.lock b/uv.lock
index 4ff362503c..9b636a0e1b 100644
--- a/uv.lock
+++ b/uv.lock
@@ -3245,7 +3245,7 @@ wheels = [
[[package]]
name = "openadapt"
-version = "1.18.0"
+version = "1.19.0"
source = { editable = "." }
dependencies = [
{ name = "click" },
@@ -3335,15 +3335,15 @@ requires-dist = [
{ name = "openadapt-agent", marker = "python_full_version < '3.13' and extra == 'agent'", specifier = ">=2.1.0,<3" },
{ 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 = "python_full_version < '3.13' and extra == 'flow'", specifier = ">=2.2.0,<3.0.0" },
- { name = "openadapt-flow", extras = ["browser"], marker = "python_full_version < '3.13' and extra == 'browser'", specifier = ">=2.2.0,<3.0.0" },
- { name = "openadapt-flow", extras = ["browser", "hosted"], marker = "python_full_version < '3.13'", specifier = ">=2.2.0,<3.0.0" },
- { name = "openadapt-flow", extras = ["capture"], marker = "python_full_version < '3.13' and extra == 'capture'", specifier = ">=2.2.0,<3.0.0" },
- { name = "openadapt-flow", extras = ["linux"], marker = "python_full_version < '3.13' and sys_platform == 'linux' and extra == 'linux'", specifier = ">=2.2.0,<3.0.0" },
- { name = "openadapt-flow", extras = ["macos"], marker = "python_full_version < '3.13' and sys_platform == 'darwin' and extra == 'macos'", specifier = ">=2.2.0,<3.0.0" },
- { name = "openadapt-flow", extras = ["privacy"], marker = "python_full_version < '3.13' and extra == 'privacy'", specifier = ">=2.2.0,<3.0.0" },
- { name = "openadapt-flow", extras = ["rdp"], marker = "python_full_version < '3.13' and extra == 'rdp'", specifier = ">=2.2.0,<3.0.0" },
- { name = "openadapt-flow", extras = ["windows"], marker = "python_full_version < '3.13' and extra == 'windows'", specifier = ">=2.2.0,<3.0.0" },
+ { name = "openadapt-flow", marker = "python_full_version < '3.13' and extra == 'flow'", specifier = ">=2.5.0,<3.0.0" },
+ { name = "openadapt-flow", extras = ["browser"], marker = "python_full_version < '3.13' and extra == 'browser'", specifier = ">=2.5.0,<3.0.0" },
+ { name = "openadapt-flow", extras = ["browser", "hosted"], marker = "python_full_version < '3.13'", specifier = ">=2.5.0,<3.0.0" },
+ { name = "openadapt-flow", extras = ["capture"], marker = "python_full_version < '3.13' and extra == 'capture'", specifier = ">=2.5.0,<3.0.0" },
+ { name = "openadapt-flow", extras = ["linux"], marker = "python_full_version < '3.13' and sys_platform == 'linux' and extra == 'linux'", specifier = ">=2.5.0,<3.0.0" },
+ { name = "openadapt-flow", extras = ["macos"], marker = "python_full_version < '3.13' and sys_platform == 'darwin' and extra == 'macos'", specifier = ">=2.5.0,<3.0.0" },
+ { name = "openadapt-flow", extras = ["privacy"], marker = "python_full_version < '3.13' and extra == 'privacy'", specifier = ">=2.5.0,<3.0.0" },
+ { name = "openadapt-flow", extras = ["rdp"], marker = "python_full_version < '3.13' and extra == 'rdp'", specifier = ">=2.5.0,<3.0.0" },
+ { name = "openadapt-flow", extras = ["windows"], marker = "python_full_version < '3.13' and extra == 'windows'", specifier = ">=2.5.0,<3.0.0" },
{ name = "openadapt-grounding", marker = "extra == 'grounding'", specifier = ">=0.1.0" },
{ name = "openadapt-ml", marker = "extra == 'ml'", specifier = ">=0.2.0" },
{ name = "openadapt-retrieval", marker = "extra == 'retrieval'", specifier = ">=0.1.0" },
@@ -3437,7 +3437,7 @@ wheels = [
[[package]]
name = "openadapt-flow"
-version = "2.3.0"
+version = "2.5.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cryptography" },
@@ -3452,9 +3452,9 @@ dependencies = [
{ name = "rapidocr-onnxruntime" },
{ name = "tomli", marker = "python_full_version < '3.11' or python_full_version >= '3.13'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/08/81/a557d7472ebd192992fe37bd9493bb998941f6af5b0258a95403c4634dc5/openadapt_flow-2.3.0.tar.gz", hash = "sha256:39f3b853987227063257a1ffcd402ca3a3120e508d901578622cd0351f1ce12d", size = 27680059, upload-time = "2026-10-20T17:21:13.302Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/b3/9e/c4b87cfdba0b1ae03d57062bad41686cb9b3affd46e901ee1749fcf1e5b8/openadapt_flow-2.5.0.tar.gz", hash = "sha256:54d99e77976ee7d9e6bc171b2ea148497528ead77b226fdf6fd4713b37c689b3", size = 31358408, upload-time = "2026-11-03T00:16:39.944Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/01/a7/915b9d0110da17393741e60d0c19574f7a14c9ca54c1a9e5a13af2b15280/openadapt_flow-2.3.0-py3-none-any.whl", hash = "sha256:6a2dbd22eb15bd449fc12a6dc36dcc8da7c0316f9faac80cbff8d6e9a433222b", size = 3783372, upload-time = "2026-10-20T17:21:10.135Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/7b/76f2027c37fd059c0c1409cd6ca6c995da13fc5891c40b138f9b0cf1362f/openadapt_flow-2.5.0-py3-none-any.whl", hash = "sha256:1dbc5d42d103cfa8a452d95805dde60c7d460035ec4d81df5cc642a32f118339", size = 4335676, upload-time = "2026-11-03T00:16:37.35Z" },
]
[package.optional-dependencies]