diff --git a/CHANGELOG.md b/CHANGELOG.md
index 54f7e769d..551d8dcac 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,41 @@
+## v1.18.0 (2026-10-20)
+
+### Breaking Changes
+
+- Require openadapt-flow 2.2 (`>=2.2.0,<3.0.0`). The quickstart uses Flow's
+ offline Seals, shared browser and OCR prewarm, and `openadapt pull` uses
+ `openadapt-flow pull`; none of these are in Flow 2.0 or 2.1. `openadapt
+ doctor` and `openadapt deploy` report an older Flow as unsupported.
+
+### Features
+
+- `openadapt quickstart` is three short acts: show the task in the synthetic
+ clinic app and compile it, run the program on five new notes at once, then
+ make one save fail silently and watch the run stop. It ends with one receipt
+ card, the command to check its Seal, and two next steps
+ ([#1408](https://github.com/OpenAdaptAI/OpenAdapt/pull/1408)).
+- When an older `openadapt` comes first on PATH, the quickstart names it and
+ gives the fix. Under `uvx` it also says how to keep the installed version
+ ([#1408](https://github.com/OpenAdaptAI/OpenAdapt/pull/1408)).
+- `openadapt pull` downloads a packaged workflow from the registry, and
+ `openadapt pull --list` shows what is available
+ ([#1173](https://github.com/OpenAdaptAI/OpenAdapt/pull/1173)).
+
+### Fixes
+
+- Next-step hints repeat the `uvx` form when the launcher runs from a uvx
+ environment, so they reach the same version
+ ([#1404](https://github.com/OpenAdaptAI/OpenAdapt/pull/1404)).
+- The quickstart describes the failing run as a save that didn't reach the
+ record ([#1236](https://github.com/OpenAdaptAI/OpenAdapt/pull/1236)), and
+ `openadapt agent serve` help shows `--bundles DIR --mode production`
+ ([#1238](https://github.com/OpenAdaptAI/OpenAdapt/pull/1238)).
+- CLI help and built-in links point at the docs glossary and the monorepo
+ ([#1200](https://github.com/OpenAdaptAI/OpenAdapt/pull/1200)).
+
## v1.17.0 (2026-10-15)
### Breaking Changes
diff --git a/docs/platform-compatibility-report.md b/docs/platform-compatibility-report.md
index 91f777a0f..294e81709 100644
--- a/docs/platform-compatibility-report.md
+++ b/docs/platform-compatibility-report.md
@@ -2,7 +2,7 @@
-- Generated: `2026-10-20T16:04:12+00:00`
+- Generated: `2026-10-20T17:25:50+00:00`
- Selection: `explicit-published`
- Compatibility: **dependency-compatible**
- Signature: `unsigned (signing infrastructure pending)`
@@ -12,21 +12,21 @@
| 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.0.0` | [`v2.0.0`](https://github.com/OpenAdaptAI/openadapt-flow/tree/v2.0.0)
commit `04fe6febda81cd611581612980056ae195f476a2`
tree `0d99b061595101b8a2373d00e02c72af77b20f7d` | `openadapt_flow-2.0.0-py3-none-any.whl` `3db9f5b956304ebac54d554c999cba4feece6513d27b43f4c969134deeeca6b3`
`openadapt_flow-2.0.0.tar.gz` `76a18d19f390e4ef3ab1520f5d248a51f2b8086963b69057a293bdfe2fae10d9` |
+| 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` |
| 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.18.0` | [`v0.18.0`](https://github.com/OpenAdaptAI/openadapt-types/tree/v0.18.0)
commit `07b8b0cb4ade5b81932adb0d30be4e97b765412c`
tree `db0cea2d88e211036f256435abf371663eec56fd` | `openadapt_types-0.18.0-py3-none-any.whl` `20addebfcccf6052e199af96ff0304197d65fcdae61f3614af51c4f7e1b3ff0a`
`openadapt_types-0.18.0.tar.gz` `39947771692b9e869fdc1636cd073f4bc1ed18758661f40f15b436f09fee9fa8` |
+| 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` |
| desktop | `openadapt-desktop` | `0.16.0` | [`v0.16.0`](https://github.com/OpenAdaptAI/openadapt-desktop/tree/v0.16.0)
commit `7c59750d37be09a2871cd1bada8cb0bce946d363`
tree `9a1307794df6f2c77ee3dab90973845f3d08ee53` | `openadapt_desktop-0.16.0-py3-none-any.whl` `06e608a934deef56c93ee1ca8de511d6daba08459626e8245bd0db4c391e64e9`
`openadapt_desktop-0.16.0.tar.gz` `15f0c59a4d481328da76f3e5aa6a29ab8cdced8ab183c97346d69b4c14c996ff` |
-| agent | `openadapt-agent` | `2.1.0` | [`v2.1.0`](https://github.com/OpenAdaptAI/openadapt-agent/tree/v2.1.0)
commit `588cc172d7c61f1a638c9cbe23c2da059f7194e6`
tree `ec1eb2df42311188bac0b7ad4274c1581843099c` | `openadapt_agent-2.1.0-py3-none-any.whl` `3bd3db93762be343b6afac5c76f30526505a21af86427ddf52b39e24a278abb5`
`openadapt_agent-2.1.0.tar.gz` `42f7633b0c39e4c9af84820b92aa08d01be21c62e1e68585befe9037bf3b31e9` |
+| agent | `openadapt-agent` | `2.2.1` | [`openadapt-agent-v2.2.1`](https://github.com/OpenAdaptAI/OpenAdapt/tree/openadapt-agent-v2.2.1/packages/agent)
commit `8d0758b9687744b07355c6512b6a57942bec4bb4`
tree `5300b0849929dd3051c57640ba6fcd74ac483728` | `openadapt_agent-2.2.1-py3-none-any.whl` `847499c4091144cb0c1bb1d2731ef064ae9cd844867c2b603acf573c6e12928a`
`openadapt_agent-2.2.1.tar.gz` `859e6b7d464ab813aba85af7f1f227ac9afcdb60112bc564719a82d3766fb733` |
## Runtime selections
| Unit | Version | Selected or locked OpenAdapt packages |
|---|---:|---|
-| launcher_environment | `1.17.0` | `openadapt==1.17.0`
`openadapt-flow==2.0.0` |
-| customer_runner | `2.0.0` | `openadapt-flow==2.0.0`
`openadapt-types==0.18.0` |
+| 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` |
| 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.1.0` | `openadapt-agent==2.1.0`
`openadapt-flow==2.0.0` |
+| agent_bridge | `2.2.1` | `openadapt-agent==2.2.1`
`openadapt-flow==2.3.0` |
## Selected-platform dependency compatibility
@@ -38,28 +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.1.0` | `openadapt-flow` | `<3,>=2.0.0` | `2.0.0` | `always`
`extra == "tutorial"` |
+| 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-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.0.0` | `openadapt-capture` | `>=1.2.0` | `1.2.2` | `extra == "capture"` |
-| selected-platform | `openadapt-flow==2.0.0` | `openadapt-privacy` | `>=1.0.0` | `1.1.2` | `extra == "privacy"` |
-| selected-platform | `openadapt-flow==2.0.0` | `openadapt-types` | `<0.19.0,>=0.18.0` | `0.18.0` | `always`
`extra == "console"`
`extra == "dev"`
`extra == "execute"`
`extra == "interop"`
`extra == "reward"` |
-| selected-platform | `openadapt==1.17.0` | `openadapt-agent` | `<3,>=2.1.0` | `2.1.0` | `python_version < "3.13"`
`python_version < "3.13" and extra == "agent"`
`python_version < "3.13" and extra == "all"` |
+| 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==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.0.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.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"` |
## Cross-component schemas
| Contract | Owner | Accepted versions | Exact source |
|---|---|---|---|
-| `workflow_bundle` | `flow` | `schema_version` [1, 2] | [`openadapt_flow/bundle_validation.py`](https://github.com/OpenAdaptAI/openadapt-flow/blob/04fe6febda81cd611581612980056ae195f476a2/openadapt_flow/bundle_validation.py) `1919432f951cc7a8ee035262e15d4d60329ffc5b22c2a71fca38330b786707a3`
[`openadapt_flow/ir.py`](https://github.com/OpenAdaptAI/openadapt-flow/blob/04fe6febda81cd611581612980056ae195f476a2/openadapt_flow/ir.py) `04ebb9bdd9e49000f5ed56f6b0a3cb237156f46c1517cf7f2dd8633ee74fdd9e` |
+| `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` |
| `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] | [`openadapt_types/control_overlay.py`](https://github.com/OpenAdaptAI/openadapt-types/blob/07b8b0cb4ade5b81932adb0d30be4e97b765412c/openadapt_types/control_overlay.py) `3aa01f89b43e12478c9e2ff1a9e61689397370691d15247ded9dcd5b05242790`
[`openadapt_types/control_overlay_tracking.py`](https://github.com/OpenAdaptAI/openadapt-types/blob/07b8b0cb4ade5b81932adb0d30be4e97b765412c/openadapt_types/control_overlay_tracking.py) `af8d934f0572c5f4e9c63ba720d4a1d0e73c8cdf3f7b87cc4562c01a8a551755` |
-| `control_overlay_timeline` | `types` | `openadapt.control-overlay-timeline/v` [1, 2] | [`openadapt_types/control_overlay.py`](https://github.com/OpenAdaptAI/openadapt-types/blob/07b8b0cb4ade5b81932adb0d30be4e97b765412c/openadapt_types/control_overlay.py) `3aa01f89b43e12478c9e2ff1a9e61689397370691d15247ded9dcd5b05242790`
[`openadapt_types/control_overlay_tracking.py`](https://github.com/OpenAdaptAI/openadapt-types/blob/07b8b0cb4ade5b81932adb0d30be4e97b765412c/openadapt_types/control_overlay_tracking.py) `af8d934f0572c5f4e9c63ba720d4a1d0e73c8cdf3f7b87cc4562c01a8a551755` |
-| `human_decision_task` | `types` | `openadapt.human-decision-task/v` [1, 2] | [`openadapt_types/human_decision.py`](https://github.com/OpenAdaptAI/openadapt-types/blob/07b8b0cb4ade5b81932adb0d30be4e97b765412c/openadapt_types/human_decision.py) `6ed5d2db82f5c73dfab568868f13f7a2cbfde6fb7b3f05b027b40c503ab04e29` |
-| `human_decision_receipt` | `types` | `openadapt.human-decision-receipt/v` [1] | [`openadapt_types/human_decision.py`](https://github.com/OpenAdaptAI/openadapt-types/blob/07b8b0cb4ade5b81932adb0d30be4e97b765412c/openadapt_types/human_decision.py) `6ed5d2db82f5c73dfab568868f13f7a2cbfde6fb7b3f05b027b40c503ab04e29` |
-| `human_decision_relay` | `flow` | `openadapt.human-decision-relay/v` [2] | [`openadapt_flow/console/decision_relay.py`](https://github.com/OpenAdaptAI/openadapt-flow/blob/04fe6febda81cd611581612980056ae195f476a2/openadapt_flow/console/decision_relay.py) `d5320404d4342dbb52be1f2ead1cdb5cf0e22877feaaa1e47974b301df697292` |
-| `remote_decision_projection` | `flow` | `openadapt.remote-decision-projection/v` [1] | [`openadapt_flow/console/human_decisions.py`](https://github.com/OpenAdaptAI/openadapt-flow/blob/04fe6febda81cd611581612980056ae195f476a2/openadapt_flow/console/human_decisions.py) `02651ad8888a5c17bfff6fb47165ba861193243dc0f7c951f8b94f544ca19477` |
-| `runtime_validation` | `flow` | `openadapt.runtime-validation/v` [1, 2, 3] | [`openadapt_flow/runtime_validation.py`](https://github.com/OpenAdaptAI/openadapt-flow/blob/04fe6febda81cd611581612980056ae195f476a2/openadapt_flow/runtime_validation.py) `18675d6b4c40458394febb36cbe28e99f410cb65b4cab138b68df3cacc6f7dd4` |
+| `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` |
## Host operating systems
@@ -81,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-flow/tree/04fe6febda81cd611581612980056ae195f476a2/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-flow/blob/04fe6febda81cd611581612980056ae195f476a2/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/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.
diff --git a/openadapt/cli.py b/openadapt/cli.py
index dff42a552..79180b869 100644
--- a/openadapt/cli.py
+++ b/openadapt/cli.py
@@ -848,8 +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.17 requires Flow 2.
-_SUPPORTED_FLOW_RANGE = ">=2.0.0,<3.0.0"
+# 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"
_RDP_INSTALL_COMMAND = _pip_install("openadapt[rdp]")
_CHROMIUM_SYSTEM_LIBS_COMMAND = (
f"{shlex.quote(sys.executable)} -m playwright install-deps chromium"
@@ -859,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, 0, 0) <= parsed < (3, 0, 0)
+ return parsed is not None and (2, 2, 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 476f98b50..de4b51e52 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-20T16:04:12+00:00",
+ "generated_at": "2026-10-20T17:25:50+00:00",
"generation": {
"files": [
{
@@ -17,13 +17,13 @@
"release_selection": {
"mode": "explicit-published",
"component_versions": {
- "agent": "2.1.0",
+ "agent": "2.2.1",
"capture": "1.2.2",
"desktop": "0.16.0",
- "flow": "2.0.0",
+ "flow": "2.3.0",
"launcher": "1.17.0",
"privacy": "1.1.2",
- "types": "0.18.0"
+ "types": "0.24.0"
}
},
"components": {
@@ -306,7 +306,7 @@
},
"flow": {
"package": "openadapt-flow",
- "version": "2.0.0",
+ "version": "2.3.0",
"source": "pypi",
"requires_python": "<3.13,>=3.10",
"requires": {
@@ -320,7 +320,7 @@
">=1.0.0"
],
"openadapt-types": [
- "<0.19.0,>=0.18.0"
+ "<0.25.0,>=0.24.0"
]
},
"dependency_constraints": [
@@ -344,60 +344,31 @@
},
{
"package": "openadapt-types",
- "requires": "<0.19.0,>=0.18.0",
+ "requires": "<0.25.0,>=0.24.0",
"extras": null,
"marker": null
- },
- {
- "package": "openadapt-types",
- "requires": "<0.19.0,>=0.18.0",
- "extras": null,
- "marker": "extra == \"console\""
- },
- {
- "package": "openadapt-types",
- "requires": "<0.19.0,>=0.18.0",
- "extras": null,
- "marker": "extra == \"dev\""
- },
- {
- "package": "openadapt-types",
- "requires": "<0.19.0,>=0.18.0",
- "extras": null,
- "marker": "extra == \"execute\""
- },
- {
- "package": "openadapt-types",
- "requires": "<0.19.0,>=0.18.0",
- "extras": null,
- "marker": "extra == \"interop\""
- },
- {
- "package": "openadapt-types",
- "requires": "<0.19.0,>=0.18.0",
- "extras": null,
- "marker": "extra == \"reward\""
}
],
"provenance": {
- "repository": "OpenAdaptAI/openadapt-flow",
- "release_ref": "v2.0.0",
- "commit": "04fe6febda81cd611581612980056ae195f476a2",
- "tree": "0d99b061595101b8a2373d00e02c72af77b20f7d",
- "url": "https://github.com/OpenAdaptAI/openadapt-flow/tree/v2.0.0"
+ "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",
+ "source_root": "packages/flow"
},
"artifacts": [
{
"type": "bdist_wheel",
- "filename": "openadapt_flow-2.0.0-py3-none-any.whl",
- "url": "https://files.pythonhosted.org/packages/c0/31/9dbe102c481edd4d5e6de0b77434708199a13b5c5fa2cf829c00415f8257/openadapt_flow-2.0.0-py3-none-any.whl",
- "sha256": "3db9f5b956304ebac54d554c999cba4feece6513d27b43f4c969134deeeca6b3"
+ "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"
},
{
"type": "sdist",
- "filename": "openadapt_flow-2.0.0.tar.gz",
- "url": "https://files.pythonhosted.org/packages/52/9f/2cc42951965b3ef28da85bc065d0894d726365e0a04bf63f40bb8bdab782/openadapt_flow-2.0.0.tar.gz",
- "sha256": "76a18d19f390e4ef3ab1520f5d248a51f2b8086963b69057a293bdfe2fae10d9"
+ "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"
}
]
},
@@ -478,30 +449,31 @@
},
"types": {
"package": "openadapt-types",
- "version": "0.18.0",
+ "version": "0.24.0",
"source": "pypi",
"requires_python": ">=3.10",
"requires": {},
"dependency_constraints": [],
"provenance": {
- "repository": "OpenAdaptAI/openadapt-types",
- "release_ref": "v0.18.0",
- "commit": "07b8b0cb4ade5b81932adb0d30be4e97b765412c",
- "tree": "db0cea2d88e211036f256435abf371663eec56fd",
- "url": "https://github.com/OpenAdaptAI/openadapt-types/tree/v0.18.0"
+ "repository": "OpenAdaptAI/OpenAdapt",
+ "release_ref": "openadapt-types-v0.24.0",
+ "commit": "f2f04c5b432d88b262d46103a542b0e2c3dcc8a9",
+ "tree": "38ccf341ec74a0dd0facc9f64343c27644a7ca7d",
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/tree/openadapt-types-v0.24.0/packages/types",
+ "source_root": "packages/types"
},
"artifacts": [
{
"type": "bdist_wheel",
- "filename": "openadapt_types-0.18.0-py3-none-any.whl",
- "url": "https://files.pythonhosted.org/packages/74/93/872926437f9b127c2e945881476d6378f5b7e6623dfce7eb75186edeb275/openadapt_types-0.18.0-py3-none-any.whl",
- "sha256": "20addebfcccf6052e199af96ff0304197d65fcdae61f3614af51c4f7e1b3ff0a"
+ "filename": "openadapt_types-0.24.0-py3-none-any.whl",
+ "url": "https://files.pythonhosted.org/packages/c8/38/2c7c3a7ebac23cc2e79039c4eca66aae5c3fd1c11a1076cb5cdc189771d6/openadapt_types-0.24.0-py3-none-any.whl",
+ "sha256": "8025e8a4dc9235fd81d4b24ae7fa7e544120abdb8d4ee7df160e4f50d9230d75"
},
{
"type": "sdist",
- "filename": "openadapt_types-0.18.0.tar.gz",
- "url": "https://files.pythonhosted.org/packages/88/e5/214616a76371df51e3d32b1ff9087eb73fbceec54cef1273b356875d732c/openadapt_types-0.18.0.tar.gz",
- "sha256": "39947771692b9e869fdc1636cd073f4bc1ed18758661f40f15b436f09fee9fa8"
+ "filename": "openadapt_types-0.24.0.tar.gz",
+ "url": "https://files.pythonhosted.org/packages/c5/fb/9ba7017c2dd42132425a2b15ebcbb79fc7920d8a98961da9bb93d3a9f79b/openadapt_types-0.24.0.tar.gz",
+ "sha256": "16dcd1da1c2c78c08d650a19eb9cc439474f996812a87198b316d51bfac443db"
}
]
},
@@ -574,47 +546,57 @@
},
"agent": {
"package": "openadapt-agent",
- "version": "2.1.0",
+ "version": "2.2.1",
"source": "pypi",
"requires_python": "<3.13,>=3.10",
"requires": {
"openadapt-flow": [
- "<3,>=2.0.0"
+ "<3,>=2.2.0"
+ ],
+ "openadapt-privacy": [
+ ">=1.1.2"
]
},
"dependency_constraints": [
{
"package": "openadapt-flow",
- "requires": "<3,>=2.0.0",
+ "requires": "<3,>=2.2.0",
"extras": null,
"marker": null
},
{
"package": "openadapt-flow",
- "requires": "<3,>=2.0.0",
+ "requires": "<3,>=2.2.0",
"extras": "browser,macos",
"marker": "extra == \"tutorial\""
+ },
+ {
+ "package": "openadapt-privacy",
+ "requires": ">=1.1.2",
+ "extras": "presidio",
+ "marker": "extra == \"repair-screens\""
}
],
"provenance": {
- "repository": "OpenAdaptAI/openadapt-agent",
- "release_ref": "v2.1.0",
- "commit": "588cc172d7c61f1a638c9cbe23c2da059f7194e6",
- "tree": "ec1eb2df42311188bac0b7ad4274c1581843099c",
- "url": "https://github.com/OpenAdaptAI/openadapt-agent/tree/v2.1.0"
+ "repository": "OpenAdaptAI/OpenAdapt",
+ "release_ref": "openadapt-agent-v2.2.1",
+ "commit": "8d0758b9687744b07355c6512b6a57942bec4bb4",
+ "tree": "5300b0849929dd3051c57640ba6fcd74ac483728",
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/tree/openadapt-agent-v2.2.1/packages/agent",
+ "source_root": "packages/agent"
},
"artifacts": [
{
"type": "bdist_wheel",
- "filename": "openadapt_agent-2.1.0-py3-none-any.whl",
- "url": "https://files.pythonhosted.org/packages/c2/6b/98e3ade91a17fddd85258f3c92956ff2f757d5da0b8c739af915d4ec3bbc/openadapt_agent-2.1.0-py3-none-any.whl",
- "sha256": "3bd3db93762be343b6afac5c76f30526505a21af86427ddf52b39e24a278abb5"
+ "filename": "openadapt_agent-2.2.1-py3-none-any.whl",
+ "url": "https://files.pythonhosted.org/packages/0b/0c/5b1e5fa74a78a179f1a8ba3e022f93b1638654238c81056c532cf2f4b6ea/openadapt_agent-2.2.1-py3-none-any.whl",
+ "sha256": "847499c4091144cb0c1bb1d2731ef064ae9cd844867c2b603acf573c6e12928a"
},
{
"type": "sdist",
- "filename": "openadapt_agent-2.1.0.tar.gz",
- "url": "https://files.pythonhosted.org/packages/77/6f/e084daf2974685479fdd824c54155e13751a115d62da327765b2b0907f55/openadapt_agent-2.1.0.tar.gz",
- "sha256": "42f7633b0c39e4c9af84820b92aa08d01be21c62e1e68585befe9037bf3b31e9"
+ "filename": "openadapt_agent-2.2.1.tar.gz",
+ "url": "https://files.pythonhosted.org/packages/6b/e0/0929a2988a191fe873faddb6a527f175f5c21adfed04ff356c4b14f77cc6/openadapt_agent-2.2.1.tar.gz",
+ "sha256": "859e6b7d464ab813aba85af7f1f227ac9afcdb60112bc564719a82d3766fb733"
}
]
}
@@ -631,21 +613,21 @@
},
"selected": {
"openadapt": "1.17.0",
- "openadapt-flow": "2.0.0"
+ "openadapt-flow": "2.3.0"
}
},
"customer_runner": {
"kind": "customer-controlled-runner",
- "version": "2.0.0",
+ "version": "2.3.0",
"source_component": "flow",
"entry_point": "openadapt-flow",
"requirements": {
- "openadapt-flow": "==2.0.0",
- "openadapt-types": "<0.19.0,>=0.18.0"
+ "openadapt-flow": "==2.3.0",
+ "openadapt-types": "<0.25.0,>=0.24.0"
},
"selected": {
- "openadapt-flow": "2.0.0",
- "openadapt-types": "0.18.0"
+ "openadapt-flow": "2.3.0",
+ "openadapt-types": "0.24.0"
}
},
"desktop_sidecar": {
@@ -671,16 +653,16 @@
},
"agent_bridge": {
"kind": "governed-agent-bridge",
- "version": "2.1.0",
+ "version": "2.2.1",
"source_component": "agent",
"entry_point": "openadapt-agent",
"requirements": {
- "openadapt-agent": "==2.1.0",
- "openadapt-flow": "<3,>=2.0.0"
+ "openadapt-agent": "==2.2.1",
+ "openadapt-flow": "<3,>=2.2.0"
},
"selected": {
- "openadapt-agent": "2.1.0",
- "openadapt-flow": "2.0.0"
+ "openadapt-agent": "2.2.1",
+ "openadapt-flow": "2.3.0"
}
}
},
@@ -753,11 +735,11 @@
"scope": "selected-platform",
"source_role": "agent",
"source_package": "openadapt-agent",
- "source_version": "2.1.0",
+ "source_version": "2.2.1",
"target_role": "flow",
"target_package": "openadapt-flow",
- "requires": "<3,>=2.0.0",
- "resolved": "2.0.0",
+ "requires": "<3,>=2.2.0",
+ "resolved": "2.3.0",
"activations": [
{
"extras": null,
@@ -769,6 +751,22 @@
}
]
},
+ {
+ "scope": "selected-platform",
+ "source_role": "agent",
+ "source_package": "openadapt-agent",
+ "source_version": "2.2.1",
+ "target_role": "privacy",
+ "target_package": "openadapt-privacy",
+ "requires": ">=1.1.2",
+ "resolved": "1.1.2",
+ "activations": [
+ {
+ "extras": "presidio",
+ "marker": "extra == \"repair-screens\""
+ }
+ ]
+ },
{
"scope": "selected-platform",
"source_role": "capture",
@@ -793,7 +791,7 @@
"scope": "selected-platform",
"source_role": "flow",
"source_package": "openadapt-flow",
- "source_version": "2.0.0",
+ "source_version": "2.3.0",
"target_role": "capture",
"target_package": "openadapt-capture",
"requires": ">=1.2.0",
@@ -809,7 +807,7 @@
"scope": "selected-platform",
"source_role": "flow",
"source_package": "openadapt-flow",
- "source_version": "2.0.0",
+ "source_version": "2.3.0",
"target_role": "privacy",
"target_package": "openadapt-privacy",
"requires": ">=1.0.0",
@@ -825,35 +823,15 @@
"scope": "selected-platform",
"source_role": "flow",
"source_package": "openadapt-flow",
- "source_version": "2.0.0",
+ "source_version": "2.3.0",
"target_role": "types",
"target_package": "openadapt-types",
- "requires": "<0.19.0,>=0.18.0",
- "resolved": "0.18.0",
+ "requires": "<0.25.0,>=0.24.0",
+ "resolved": "0.24.0",
"activations": [
{
"extras": null,
"marker": null
- },
- {
- "extras": null,
- "marker": "extra == \"console\""
- },
- {
- "extras": null,
- "marker": "extra == \"dev\""
- },
- {
- "extras": null,
- "marker": "extra == \"execute\""
- },
- {
- "extras": null,
- "marker": "extra == \"interop\""
- },
- {
- "extras": null,
- "marker": "extra == \"reward\""
}
]
},
@@ -865,7 +843,7 @@
"target_role": "agent",
"target_package": "openadapt-agent",
"requires": "<3,>=2.1.0",
- "resolved": "2.1.0",
+ "resolved": "2.2.1",
"activations": [
{
"extras": null,
@@ -913,7 +891,7 @@
"target_role": "flow",
"target_package": "openadapt-flow",
"requires": "<3.0.0,>=2.0.0",
- "resolved": "2.0.0",
+ "resolved": "2.3.0",
"activations": [
{
"extras": null,
@@ -1001,19 +979,19 @@
],
"minimum": 1,
"maximum": 2,
- "source_commit": "04fe6febda81cd611581612980056ae195f476a2",
+ "source_commit": "b6a9d46b00b92454c18e844f0a8f6929330e87b6",
"source_files": [
{
- "path": "openadapt_flow/bundle_validation.py",
- "url": "https://github.com/OpenAdaptAI/openadapt-flow/blob/04fe6febda81cd611581612980056ae195f476a2/openadapt_flow/bundle_validation.py",
- "git_blob": "096637a35d8645f947f73589fcdb81a0fa531c19",
- "sha256": "1919432f951cc7a8ee035262e15d4d60329ffc5b22c2a71fca38330b786707a3"
+ "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"
},
{
- "path": "openadapt_flow/ir.py",
- "url": "https://github.com/OpenAdaptAI/openadapt-flow/blob/04fe6febda81cd611581612980056ae195f476a2/openadapt_flow/ir.py",
- "git_blob": "6bd3a2ff222209bfb820e5346fb38ee718507f20",
- "sha256": "04ebb9bdd9e49000f5ed56f6b0a3cb237156f46c1517cf7f2dd8633ee74fdd9e"
+ "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"
}
]
},
@@ -1044,17 +1022,17 @@
],
"minimum": 1,
"maximum": 2,
- "source_commit": "07b8b0cb4ade5b81932adb0d30be4e97b765412c",
+ "source_commit": "f2f04c5b432d88b262d46103a542b0e2c3dcc8a9",
"source_files": [
{
- "path": "openadapt_types/control_overlay.py",
- "url": "https://github.com/OpenAdaptAI/openadapt-types/blob/07b8b0cb4ade5b81932adb0d30be4e97b765412c/openadapt_types/control_overlay.py",
+ "path": "packages/types/openadapt_types/control_overlay.py",
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/f2f04c5b432d88b262d46103a542b0e2c3dcc8a9/packages/types/openadapt_types/control_overlay.py",
"git_blob": "8eaa7b82ae2606fa59943218e3839efe19ebc54b",
"sha256": "3aa01f89b43e12478c9e2ff1a9e61689397370691d15247ded9dcd5b05242790"
},
{
- "path": "openadapt_types/control_overlay_tracking.py",
- "url": "https://github.com/OpenAdaptAI/openadapt-types/blob/07b8b0cb4ade5b81932adb0d30be4e97b765412c/openadapt_types/control_overlay_tracking.py",
+ "path": "packages/types/openadapt_types/control_overlay_tracking.py",
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/f2f04c5b432d88b262d46103a542b0e2c3dcc8a9/packages/types/openadapt_types/control_overlay_tracking.py",
"git_blob": "222b9bade823332df869f496bc035aa31faed5a2",
"sha256": "af8d934f0572c5f4e9c63ba720d4a1d0e73c8cdf3f7b87cc4562c01a8a551755"
}
@@ -1069,17 +1047,17 @@
],
"minimum": 1,
"maximum": 2,
- "source_commit": "07b8b0cb4ade5b81932adb0d30be4e97b765412c",
+ "source_commit": "f2f04c5b432d88b262d46103a542b0e2c3dcc8a9",
"source_files": [
{
- "path": "openadapt_types/control_overlay.py",
- "url": "https://github.com/OpenAdaptAI/openadapt-types/blob/07b8b0cb4ade5b81932adb0d30be4e97b765412c/openadapt_types/control_overlay.py",
+ "path": "packages/types/openadapt_types/control_overlay.py",
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/f2f04c5b432d88b262d46103a542b0e2c3dcc8a9/packages/types/openadapt_types/control_overlay.py",
"git_blob": "8eaa7b82ae2606fa59943218e3839efe19ebc54b",
"sha256": "3aa01f89b43e12478c9e2ff1a9e61689397370691d15247ded9dcd5b05242790"
},
{
- "path": "openadapt_types/control_overlay_tracking.py",
- "url": "https://github.com/OpenAdaptAI/openadapt-types/blob/07b8b0cb4ade5b81932adb0d30be4e97b765412c/openadapt_types/control_overlay_tracking.py",
+ "path": "packages/types/openadapt_types/control_overlay_tracking.py",
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/f2f04c5b432d88b262d46103a542b0e2c3dcc8a9/packages/types/openadapt_types/control_overlay_tracking.py",
"git_blob": "222b9bade823332df869f496bc035aa31faed5a2",
"sha256": "af8d934f0572c5f4e9c63ba720d4a1d0e73c8cdf3f7b87cc4562c01a8a551755"
}
@@ -1094,13 +1072,13 @@
],
"minimum": 1,
"maximum": 2,
- "source_commit": "07b8b0cb4ade5b81932adb0d30be4e97b765412c",
+ "source_commit": "f2f04c5b432d88b262d46103a542b0e2c3dcc8a9",
"source_files": [
{
- "path": "openadapt_types/human_decision.py",
- "url": "https://github.com/OpenAdaptAI/openadapt-types/blob/07b8b0cb4ade5b81932adb0d30be4e97b765412c/openadapt_types/human_decision.py",
- "git_blob": "1b60d8d6874c666494ef3c9eaa599e8d9836db6b",
- "sha256": "6ed5d2db82f5c73dfab568868f13f7a2cbfde6fb7b3f05b027b40c503ab04e29"
+ "path": "packages/types/openadapt_types/human_decision.py",
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/f2f04c5b432d88b262d46103a542b0e2c3dcc8a9/packages/types/openadapt_types/human_decision.py",
+ "git_blob": "ff7c1181075d87116f2d0c144bb72b1fffc256dd",
+ "sha256": "0bb39a3575f511e10d01d92858b340982adee2f0fc20d2409441dbc0b2c55c4f"
}
]
},
@@ -1112,13 +1090,13 @@
],
"minimum": 1,
"maximum": 1,
- "source_commit": "07b8b0cb4ade5b81932adb0d30be4e97b765412c",
+ "source_commit": "f2f04c5b432d88b262d46103a542b0e2c3dcc8a9",
"source_files": [
{
- "path": "openadapt_types/human_decision.py",
- "url": "https://github.com/OpenAdaptAI/openadapt-types/blob/07b8b0cb4ade5b81932adb0d30be4e97b765412c/openadapt_types/human_decision.py",
- "git_blob": "1b60d8d6874c666494ef3c9eaa599e8d9836db6b",
- "sha256": "6ed5d2db82f5c73dfab568868f13f7a2cbfde6fb7b3f05b027b40c503ab04e29"
+ "path": "packages/types/openadapt_types/human_decision.py",
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/f2f04c5b432d88b262d46103a542b0e2c3dcc8a9/packages/types/openadapt_types/human_decision.py",
+ "git_blob": "ff7c1181075d87116f2d0c144bb72b1fffc256dd",
+ "sha256": "0bb39a3575f511e10d01d92858b340982adee2f0fc20d2409441dbc0b2c55c4f"
}
]
},
@@ -1130,11 +1108,11 @@
],
"minimum": 2,
"maximum": 2,
- "source_commit": "04fe6febda81cd611581612980056ae195f476a2",
+ "source_commit": "b6a9d46b00b92454c18e844f0a8f6929330e87b6",
"source_files": [
{
- "path": "openadapt_flow/console/decision_relay.py",
- "url": "https://github.com/OpenAdaptAI/openadapt-flow/blob/04fe6febda81cd611581612980056ae195f476a2/openadapt_flow/console/decision_relay.py",
+ "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",
"git_blob": "297d2f71727f83496221091a4237d704c25665d6",
"sha256": "d5320404d4342dbb52be1f2ead1cdb5cf0e22877feaaa1e47974b301df697292"
}
@@ -1148,11 +1126,11 @@
],
"minimum": 1,
"maximum": 1,
- "source_commit": "04fe6febda81cd611581612980056ae195f476a2",
+ "source_commit": "b6a9d46b00b92454c18e844f0a8f6929330e87b6",
"source_files": [
{
- "path": "openadapt_flow/console/human_decisions.py",
- "url": "https://github.com/OpenAdaptAI/openadapt-flow/blob/04fe6febda81cd611581612980056ae195f476a2/openadapt_flow/console/human_decisions.py",
+ "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",
"git_blob": "caaf87396aca4770eace5eead3fdf6515e31fbfc",
"sha256": "02651ad8888a5c17bfff6fb47165ba861193243dc0f7c951f8b94f544ca19477"
}
@@ -1168,13 +1146,13 @@
],
"minimum": 1,
"maximum": 3,
- "source_commit": "04fe6febda81cd611581612980056ae195f476a2",
+ "source_commit": "b6a9d46b00b92454c18e844f0a8f6929330e87b6",
"source_files": [
{
- "path": "openadapt_flow/runtime_validation.py",
- "url": "https://github.com/OpenAdaptAI/openadapt-flow/blob/04fe6febda81cd611581612980056ae195f476a2/openadapt_flow/runtime_validation.py",
- "git_blob": "839e07ab733d5afc78aa5625541ee746b16f2c6a",
- "sha256": "18675d6b4c40458394febb36cbe28e99f410cb65b4cab138b68df3cacc6f7dd4"
+ "path": "packages/flow/openadapt_flow/runtime_validation.py",
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/b6a9d46b00b92454c18e844f0a8f6929330e87b6/packages/flow/openadapt_flow/runtime_validation.py",
+ "git_blob": "fd6121209e50abcab41bb8b70f95ff2647d9303f",
+ "sha256": "a5a220f570c5ee5769ac2f21d8d140bb69373d4de2ccc0ebfd08540122883f0e"
}
]
}
@@ -1192,7 +1170,7 @@
">=0.1.0"
],
"openadapt-flow": [
- ">=2.0.0,<3.0.0"
+ ">=2.2.0,<3.0.0"
],
"openadapt-grounding": [
">=0.1.0"
@@ -1255,25 +1233,25 @@
"id": "flow-public-demo-evidence-packs",
"kind": "evidence-pack-collection",
"source_component": "flow",
- "repository": "OpenAdaptAI/openadapt-flow",
- "path": "public-demo/evidence-packs",
+ "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": "04fe6febda81cd611581612980056ae195f476a2",
+ "source_commit": "b6a9d46b00b92454c18e844f0a8f6929330e87b6",
"source_object_type": "tree",
"source_object_sha": "2eac2252f62349e873b461866546dd0295169a19",
- "url": "https://github.com/OpenAdaptAI/openadapt-flow/tree/04fe6febda81cd611581612980056ae195f476a2/public-demo/evidence-packs"
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/tree/b6a9d46b00b92454c18e844f0a8f6929330e87b6/packages/flow/public-demo/evidence-packs"
},
{
"id": "flow-effectbench-task-pack",
"kind": "benchmark-manifest",
"source_component": "flow",
- "repository": "OpenAdaptAI/openadapt-flow",
- "path": "benchmark/effectbench/task_pack/manifest.json",
+ "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": "04fe6febda81cd611581612980056ae195f476a2",
+ "source_commit": "b6a9d46b00b92454c18e844f0a8f6929330e87b6",
"source_object_type": "blob",
"source_object_sha": "1c568d2289342e10e3556c5c46cd08a0e370ac84",
- "url": "https://github.com/OpenAdaptAI/openadapt-flow/blob/04fe6febda81cd611581612980056ae195f476a2/benchmark/effectbench/task_pack/manifest.json"
+ "url": "https://github.com/OpenAdaptAI/OpenAdapt/blob/b6a9d46b00b92454c18e844f0a8f6929330e87b6/packages/flow/benchmark/effectbench/task_pack/manifest.json"
}
],
"signature": {
diff --git a/pyproject.toml b/pyproject.toml
index 26912824d..ee959eb76 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "openadapt"
-version = "1.17.0"
+version = "1.18.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 1.35.0: Flow 1.33 and 1.34 probe four X11 libraries in
- # the wrong case and refuse Chromium on Linux hosts that have them
+ # 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.0.0,<3.0.0; python_version < '3.13'",
+ "openadapt-flow[browser,hosted]>=2.2.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.0.0,<3.0.0; python_version < '3.13'",
+ "openadapt-flow[browser]>=2.2.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.0.0,<3.0.0; python_version < '3.13'",
+ "openadapt-flow[capture]>=2.2.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.0.0,<3.0.0; python_version < '3.13'",
+ "openadapt-flow[windows]>=2.2.0,<3.0.0; python_version < '3.13'",
]
macos = [
- "openadapt-flow[macos]>=2.0.0,<3.0.0; sys_platform == 'darwin' and python_version < '3.13'",
+ "openadapt-flow[macos]>=2.2.0,<3.0.0; sys_platform == 'darwin' and python_version < '3.13'",
]
linux = [
- "openadapt-flow[linux]>=2.0.0,<3.0.0; sys_platform == 'linux' and python_version < '3.13'",
+ "openadapt-flow[linux]>=2.2.0,<3.0.0; sys_platform == 'linux' and python_version < '3.13'",
]
rdp = [
- "openadapt-flow[rdp]>=2.0.0,<3.0.0; python_version < '3.13'",
+ "openadapt-flow[rdp]>=2.2.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.0.0,<3.0.0; python_version < '3.13'",
+ "openadapt-flow[privacy]>=2.2.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.0.0,<3.0.0; python_version < '3.13'",
+ "openadapt-flow>=2.2.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/scripts/verify_release_components.py b/scripts/verify_release_components.py
index 7e3919de4..b4278286f 100644
--- a/scripts/verify_release_components.py
+++ b/scripts/verify_release_components.py
@@ -38,6 +38,9 @@
"flow": ("openadapt-flow", "openadapt_flow", "OpenAdaptAI/openadapt-flow"),
"agent": ("openadapt-agent", "openadapt_agent", "OpenAdaptAI/openadapt-agent"),
}
+# Releases from before the fold are tagged v in the standalone
+# repository above; later ones -v here, under packages/.
+MONOREPO_REPOSITORY = "OpenAdaptAI/OpenAdapt"
HEX40 = re.compile(r"^[0-9a-f]{40}$")
SHA256 = re.compile(r"^[0-9a-f]{64}$")
STABLE_VERSION = re.compile(r"^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$")
@@ -106,11 +109,19 @@ def load_component(manifest_path: Path, launcher_version: str, role: str) -> Com
if entry.get("package") != package or entry.get("source") != "pypi":
raise ValueError(f"{role} component is not the canonical published package")
provenance = _object(entry.get("provenance"), f"{role} provenance")
- tag = f"v{version}"
commit = provenance.get("commit")
+ bound = (
+ provenance.get("repository"),
+ provenance.get("release_ref"),
+ provenance.get("source_root", ""),
+ )
+ releases = {
+ (MONOREPO_REPOSITORY, f"{package}-v{version}", f"packages/{role}"),
+ (repository, f"v{version}", ""),
+ }
+ tag = str(provenance.get("release_ref"))
if (
- provenance.get("repository") != repository
- or provenance.get("release_ref") != tag
+ bound not in releases
or not isinstance(commit, str)
or HEX40.fullmatch(commit) is None
):
diff --git a/tests/test_cli_shell_hints.py b/tests/test_cli_shell_hints.py
index 443f32329..ccda94c32 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.0.1")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.1")
result = CliRunner().invoke(cli_main, ["deploy"])
assert result.exit_code == 0, result.output
- assert '"openadapt-flow[console]==2.0.1"' in result.output
+ assert '"openadapt-flow[console]==2.2.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 4aaafe8fe..2dabd0107 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.0.0,<3.0.0; {supported}"
+ f"openadapt-flow[browser,hosted]>=2.2.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.0.0,<3.0.0; {supported}"]
+ assert extras["flow"] == [f"openadapt-flow>=2.2.0,<3.0.0; {supported}"]
assert extras["agent"] == [f"openadapt-agent>=2.1.0,<3; {supported}"]
- assert extras["browser"] == [f"openadapt-flow[browser]>=2.0.0,<3.0.0; {supported}"]
- assert extras["privacy"] == [f"openadapt-flow[privacy]>=2.0.0,<3.0.0; {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["capture"] == [
"openadapt-capture>=1.2.0,<2.0.0",
- f"openadapt-flow[capture]>=2.0.0,<3.0.0; {supported}",
+ f"openadapt-flow[capture]>=2.2.0,<3.0.0; {supported}",
]
- assert extras["windows"] == [f"openadapt-flow[windows]>=2.0.0,<3.0.0; {supported}"]
+ assert extras["windows"] == [f"openadapt-flow[windows]>=2.2.0,<3.0.0; {supported}"]
assert extras["macos"] == [
- f"openadapt-flow[macos]>=2.0.0,<3.0.0; sys_platform == 'darwin' and {supported}"
+ f"openadapt-flow[macos]>=2.2.0,<3.0.0; sys_platform == 'darwin' and {supported}"
]
assert extras["linux"] == [
- f"openadapt-flow[linux]>=2.0.0,<3.0.0; sys_platform == 'linux' and {supported}"
+ f"openadapt-flow[linux]>=2.2.0,<3.0.0; sys_platform == 'linux' and {supported}"
]
- assert extras["rdp"] == [f"openadapt-flow[rdp]>=2.0.0,<3.0.0; {supported}"]
+ assert extras["rdp"] == [f"openadapt-flow[rdp]>=2.2.0,<3.0.0; {supported}"]
assert extras["all"] == [
"openadapt[browser,core,grounding,retrieval,privacy,flow,windows,rdp,agent]",
"openadapt[macos]; sys_platform == 'darwin'",
@@ -433,12 +433,12 @@ def test_released_flow_x11_sonames_are_case_corrected(monkeypatch):
)
-@pytest.mark.parametrize("version", ["2.0.0", "2.0.1", "2.99.0"])
+@pytest.mark.parametrize("version", ["2.2.0", "2.2.1", "2.99.0"])
def test_launcher_accepts_flow_versions_in_the_supported_range(version):
assert _supported_flow_version(version)
-@pytest.mark.parametrize("version", ["1.28.9", "1.35.1", "3.0.0", "invalid"])
+@pytest.mark.parametrize("version", ["1.28.9", "1.35.1", "2.1.0", "3.0.0", "invalid"])
def test_launcher_rejects_flow_versions_outside_the_supported_range(version):
assert not _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.0.0")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.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.0.0")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.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.0.0")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.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.0.0")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.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.0.0"' in result.output
+ assert 'python -m pip install "openadapt-flow[console]==2.2.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.0.0")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.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.0.0"' in result.output
+ assert 'python -m pip install "openadapt-flow[console]==2.2.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.0.0")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.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.0.0")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.0")
result = CliRunner().invoke(cli_main, ["deploy", "--backend", "rdp"])
@@ -796,7 +796,9 @@ def test_deploy_preflight_fails_without_rdp_transport(monkeypatch):
assert "Preflight passed" not in result.output
-@pytest.mark.parametrize("flow_version", ["1.35.1", "3.0.0", "3.1.0", "invalid"])
+@pytest.mark.parametrize(
+ "flow_version", ["1.35.1", "2.1.0", "3.0.0", "3.1.0", "invalid"]
+)
def test_deploy_preflight_fails_for_unsupported_flow(monkeypatch, flow_version):
monkeypatch.setattr("importlib.util.find_spec", lambda _name: object())
monkeypatch.setattr("importlib.metadata.version", lambda _name: flow_version)
@@ -805,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.0.0,<3.0.0" in result.output
+ assert ">=2.2.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 f13b48ff0..b4999cdfd 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.0.0")
+ monkeypatch.setattr("importlib.metadata.version", lambda _name: "2.2.0")
return CliRunner().invoke(cli_main, ["deploy", *args])
diff --git a/tests/test_flow_floor.py b/tests/test_flow_floor.py
index b99d81797..c653c91c3 100644
--- a/tests/test_flow_floor.py
+++ b/tests/test_flow_floor.py
@@ -24,9 +24,10 @@
import tomli as tomllib
ROOT = Path(__file__).resolve().parents[1]
-# Launcher 1.17 requires Flow 2 (approval by default), so 1.x is below the floor.
-BELOW_FLOOR = ["1.29.0", "1.33.0", "1.34.0", "1.34.9", "1.35.0", "1.35.1"]
-ACCEPTED = "2.0.0"
+# 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"
def _flow_requirements() -> list[Requirement]:
diff --git a/tests/test_platform_manifest_drift.py b/tests/test_platform_manifest_drift.py
index c213e7e9a..8be198b55 100644
--- a/tests/test_platform_manifest_drift.py
+++ b/tests/test_platform_manifest_drift.py
@@ -396,8 +396,9 @@ def test_runtime_requirements_come_from_selected_release_metadata(
}
assert actual == expected
- # Launcher 1.17.0 adds its openadapt-agent edge.
- assert len(manifest["dependency_edges"]) == 12
+ # Launcher 1.17.0 adds its openadapt-agent edge; Flow 2.3.0 its
+ # openadapt-types edge.
+ assert len(manifest["dependency_edges"]) == 13
components = deepcopy(manifest["components"])
components["capture"]["version"] = "0.0.1"
edges = _dependency_edges(components, manifest["runtime_units"])
diff --git a/tests/test_platform_manifest_monorepo.py b/tests/test_platform_manifest_monorepo.py
index 40b2741aa..84cc87050 100644
--- a/tests/test_platform_manifest_monorepo.py
+++ b/tests/test_platform_manifest_monorepo.py
@@ -69,7 +69,7 @@ def _monorepo_flow(manifest: dict, version: str = "2.3.0") -> dict:
for evidence in moved["qualification_evidence"]:
if evidence.get("source_component") != "flow":
continue
- package_path = evidence["path"]
+ package_path = evidence["path"].removeprefix("packages/flow/")
evidence["repository"] = MONOREPO_REPOSITORY
evidence["path"] = f"packages/flow/{package_path}"
evidence["source_commit"] = COMMIT
@@ -235,10 +235,12 @@ def fake_source(repository: str, commit: str, path: str, *, optional=False):
MONOREPO_REPOSITORY
}
assert schemas["workflow_bundle"]["source_commit"] == COMMIT
- # Components still on standalone releases keep their package-relative paths.
- assert ("OpenAdaptAI/openadapt-types", "openadapt_types/human_decision.py") in (
- requested
- )
+ # Every other component is read where its own selected release lives.
+ types = components["types"]["provenance"]
+ assert (
+ types["repository"],
+ _source_path(types, "openadapt_types/human_decision.py"),
+ ) in requested
def test_validator_accepts_a_monorepo_release(manifest: dict) -> None:
@@ -247,7 +249,7 @@ def test_validator_accepts_a_monorepo_release(manifest: dict) -> None:
assert _provenance_and_evidence_errors(report) == []
-def test_validator_accepts_the_standalone_releases_already_pinned(
+def test_validator_accepts_the_releases_already_pinned(
manifest: dict,
) -> None:
report = Report()
diff --git a/tests/test_verify_release_components.py b/tests/test_verify_release_components.py
index 008d4ae95..522a7487f 100644
--- a/tests/test_verify_release_components.py
+++ b/tests/test_verify_release_components.py
@@ -167,3 +167,45 @@ def broken(_url: str) -> bytes:
raise OSError("offline")
assert _run(tmp_path, broken) == 1
+
+
+def test_monorepo_release_provenance_passes(tmp_path: Path) -> None:
+ document, _, _, fetch = _setup(tmp_path)
+ for role, (package, _dist, _repository) in MODULE.COMPONENTS.items():
+ version = document["components"][role]["version"]
+ document["components"][role]["provenance"] = {
+ "repository": "OpenAdaptAI/OpenAdapt",
+ "release_ref": f"{package}-v{version}",
+ "commit": "e" * 40,
+ "source_root": f"packages/{role}",
+ }
+ (tmp_path / "platform-manifest.json").write_text(json.dumps(document))
+ assert _run(tmp_path, fetch) == 0
+
+
+@pytest.mark.parametrize(
+ "provenance",
+ [
+ # The monorepo tag in the standalone repository.
+ {
+ "repository": "OpenAdaptAI/openadapt-flow",
+ "release_ref": "openadapt-flow-v2.0.0",
+ },
+ # A monorepo release without its package directory.
+ {"repository": "OpenAdaptAI/OpenAdapt", "release_ref": "openadapt-flow-v2.0.0"},
+ # Another package's directory.
+ {
+ "repository": "OpenAdaptAI/OpenAdapt",
+ "release_ref": "openadapt-flow-v2.0.0",
+ "source_root": "packages/agent",
+ },
+ ],
+)
+def test_mismatched_monorepo_provenance_fails(
+ tmp_path: Path, provenance: dict, capsys
+) -> None:
+ document, _, _, fetch = _setup(tmp_path)
+ document["components"]["flow"]["provenance"] = {**provenance, "commit": "e" * 40}
+ (tmp_path / "platform-manifest.json").write_text(json.dumps(document))
+ assert _run(tmp_path, fetch) == 1
+ assert "flow provenance does not bind" in capsys.readouterr().err
diff --git a/uv.lock b/uv.lock
index 4dcf4a0d0..d3afcf6d2 100644
--- a/uv.lock
+++ b/uv.lock
@@ -3202,12 +3202,13 @@ wheels = [
[[package]]
name = "openadapt"
-version = "1.17.0"
+version = "1.18.0"
source = { editable = "." }
dependencies = [
{ name = "click" },
{ name = "openadapt-agent", marker = "python_full_version < '3.13'" },
{ name = "openadapt-flow", extra = ["browser", "hosted"], marker = "python_full_version < '3.13'" },
+ { name = "rich", marker = "python_full_version < '3.13'" },
]
[package.optional-dependencies]
@@ -3291,27 +3292,28 @@ 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.0.0,<3.0.0" },
- { name = "openadapt-flow", extras = ["browser"], marker = "python_full_version < '3.13' and extra == 'browser'", specifier = ">=2.0.0,<3.0.0" },
- { name = "openadapt-flow", extras = ["browser", "hosted"], marker = "python_full_version < '3.13'", specifier = ">=2.0.0,<3.0.0" },
- { name = "openadapt-flow", extras = ["capture"], marker = "python_full_version < '3.13' and extra == 'capture'", specifier = ">=2.0.0,<3.0.0" },
- { name = "openadapt-flow", extras = ["linux"], marker = "python_full_version < '3.13' and sys_platform == 'linux' and extra == 'linux'", specifier = ">=2.0.0,<3.0.0" },
- { name = "openadapt-flow", extras = ["macos"], marker = "python_full_version < '3.13' and sys_platform == 'darwin' and extra == 'macos'", specifier = ">=2.0.0,<3.0.0" },
- { name = "openadapt-flow", extras = ["privacy"], marker = "python_full_version < '3.13' and extra == 'privacy'", specifier = ">=2.0.0,<3.0.0" },
- { name = "openadapt-flow", extras = ["rdp"], marker = "python_full_version < '3.13' and extra == 'rdp'", specifier = ">=2.0.0,<3.0.0" },
- { name = "openadapt-flow", extras = ["windows"], marker = "python_full_version < '3.13' and extra == 'windows'", specifier = ">=2.0.0,<3.0.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-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" },
{ name = "openadapt-viewer", marker = "extra == 'viewer'", specifier = ">=0.1.0" },
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" },
+ { name = "rich", marker = "python_full_version < '3.13'", specifier = ">=13.0.0" },
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" },
]
provides-extras = ["browser", "capture", "windows", "macos", "linux", "rdp", "ml", "evals", "viewer", "grounding", "retrieval", "privacy", "flow", "agent", "core", "all", "dev"]
[[package]]
name = "openadapt-agent"
-version = "2.1.0"
+version = "2.2.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
@@ -3320,9 +3322,9 @@ dependencies = [
{ name = "mcp" },
{ name = "openadapt-flow" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/77/6f/e084daf2974685479fdd824c54155e13751a115d62da327765b2b0907f55/openadapt_agent-2.1.0.tar.gz", hash = "sha256:42f7633b0c39e4c9af84820b92aa08d01be21c62e1e68585befe9037bf3b31e9", size = 297078, upload-time = "2026-10-15T09:14:10.453Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/6b/e0/0929a2988a191fe873faddb6a527f175f5c21adfed04ff356c4b14f77cc6/openadapt_agent-2.2.1.tar.gz", hash = "sha256:859e6b7d464ab813aba85af7f1f227ac9afcdb60112bc564719a82d3766fb733", size = 286603, upload-time = "2026-10-18T15:58:53.698Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c2/6b/98e3ade91a17fddd85258f3c92956ff2f757d5da0b8c739af915d4ec3bbc/openadapt_agent-2.1.0-py3-none-any.whl", hash = "sha256:3bd3db93762be343b6afac5c76f30526505a21af86427ddf52b39e24a278abb5", size = 126172, upload-time = "2026-10-15T09:14:08.941Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/0c/5b1e5fa74a78a179f1a8ba3e022f93b1638654238c81056c532cf2f4b6ea/openadapt_agent-2.2.1-py3-none-any.whl", hash = "sha256:847499c4091144cb0c1bb1d2731ef064ae9cd844867c2b603acf573c6e12928a", size = 17386, upload-time = "2026-10-18T15:58:52.443Z" },
]
[[package]]
@@ -3392,7 +3394,7 @@ wheels = [
[[package]]
name = "openadapt-flow"
-version = "2.0.0"
+version = "2.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cryptography" },
@@ -3407,9 +3409,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/52/9f/2cc42951965b3ef28da85bc065d0894d726365e0a04bf63f40bb8bdab782/openadapt_flow-2.0.0.tar.gz", hash = "sha256:76a18d19f390e4ef3ab1520f5d248a51f2b8086963b69057a293bdfe2fae10d9", size = 22244078, upload-time = "2026-10-15T09:01:06.322Z" }
+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" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c0/31/9dbe102c481edd4d5e6de0b77434708199a13b5c5fa2cf829c00415f8257/openadapt_flow-2.0.0-py3-none-any.whl", hash = "sha256:3db9f5b956304ebac54d554c999cba4feece6513d27b43f4c969134deeeca6b3", size = 2303072, upload-time = "2026-10-15T09:01:03.354Z" },
+ { 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" },
]
[package.optional-dependencies]
@@ -3520,14 +3522,14 @@ wheels = [
[[package]]
name = "openadapt-types"
-version = "0.18.0"
+version = "0.24.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pydantic" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/88/e5/214616a76371df51e3d32b1ff9087eb73fbceec54cef1273b356875d732c/openadapt_types-0.18.0.tar.gz", hash = "sha256:39947771692b9e869fdc1636cd073f4bc1ed18758661f40f15b436f09fee9fa8", size = 231751, upload-time = "2026-09-03T21:39:20.418Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/c5/fb/9ba7017c2dd42132425a2b15ebcbb79fc7920d8a98961da9bb93d3a9f79b/openadapt_types-0.24.0.tar.gz", hash = "sha256:16dcd1da1c2c78c08d650a19eb9cc439474f996812a87198b316d51bfac443db", size = 318220, upload-time = "2026-10-20T16:40:43.505Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/74/93/872926437f9b127c2e945881476d6378f5b7e6623dfce7eb75186edeb275/openadapt_types-0.18.0-py3-none-any.whl", hash = "sha256:20addebfcccf6052e199af96ff0304197d65fcdae61f3614af51c4f7e1b3ff0a", size = 168883, upload-time = "2026-09-03T21:39:19.024Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/38/2c7c3a7ebac23cc2e79039c4eca66aae5c3fd1c11a1076cb5cdc189771d6/openadapt_types-0.24.0-py3-none-any.whl", hash = "sha256:8025e8a4dc9235fd81d4b24ae7fa7e544120abdb8d4ee7df160e4f50d9230d75", size = 224713, upload-time = "2026-10-20T16:40:42.061Z" },
]
[[package]]