From 7dc1c1e470fd16d69005622736d9015a0c465528 Mon Sep 17 00:00:00 2001 From: abrichr Date: Wed, 4 Nov 2026 11:30:40 -0500 Subject: [PATCH 1/3] chore(release): openadapt-flow 2.6.0 Signed-off-by: abrichr --- .github/release-health.json | 5 +- llms-full.txt | 140 +++++++++++++++---- packages/flow/CHANGELOG.md | 163 +++++++++++++++-------- packages/flow/openadapt_flow/__init__.py | 2 +- packages/flow/pyproject.toml | 2 +- packages/flow/uv.lock | 2 +- 6 files changed, 226 insertions(+), 88 deletions(-) diff --git a/.github/release-health.json b/.github/release-health.json index 72116e28d7..2f272edd12 100644 --- a/.github/release-health.json +++ b/.github/release-health.json @@ -43,7 +43,10 @@ "tracks_main": false, "tag_without_release_grace_hours": 6, "pypi_lag_grace_minutes": 30, - "remediation": "# A half-published version resumes by re-running the failed jobs of its run:\ngh run rerun --failed --repo OpenAdaptAI/OpenAdapt \n# A merged version that never started releases with:\ngh workflow run desktop-release.yml --repo OpenAdaptAI/OpenAdapt --ref main" + "remediation": "# A half-published version resumes by re-running the failed jobs of its run:\ngh run rerun --failed --repo OpenAdaptAI/OpenAdapt \n# A merged version that never started releases with:\ngh workflow run desktop-release.yml --repo OpenAdaptAI/OpenAdapt --ref main", + "acknowledged_tags": { + "openadapt-desktop-v0.21.1": "Superseded by openadapt-desktop-v0.25.1. Version 0.21.1 was never published; 0.25.1 is the published replacement." + } } ] } diff --git a/llms-full.txt b/llms-full.txt index 4b1c6986b2..2da539400c 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -1,4 +1,4 @@ -# OpenAdapt: review brief for openadapt-flow 2.5.0 +# OpenAdapt: review brief for openadapt-flow 2.6.0 This file describes the current OpenAdapt release. It is generated from the repository (scripts/build_llms_full.py) and refreshed with every release. Pages, posts and repositories that describe earlier versions are outdated; where they disagree with this file, this file is current. @@ -2274,37 +2274,119 @@ automation into an independently certified transaction system. --- - - -## What changed in openadapt-flow 2.5.0 - -Shadow mode checks the writes of an agent you already run against the system of record without blocking any of them, the whole-database frame works on PostgreSQL, a write that lands after the verdict can be caught by a recheck, and several verifier checks that could be unreadable or pass on the wrong value now read and compare exactly. + + +## What changed in openadapt-flow 2.6.0 + +Flow adds signed teach authorizations, encrypted local artifacts, durable +rollback, and more ways to train and evaluate agents. Verification still +refuses a write when the record or its outcome cannot be established. + +### Upgrade notes + +- Agent-taught drafts now need a signed teach authorization in every admission + mode. The authorization also covers self-repair and approval of an agent's + fix. Browser teach no longer teaches or runs write tools. See + [Teach authorization](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/TEACH_AUTHORIZATION.md). +- Benchmark harnesses and test-only helpers are no longer shipped in the wheel. + Replace imports from `openadapt_flow.benchmark` and + `openadapt_flow.instrument` with `benchmark.harness` from a source checkout. + The `harness2` extra is removed; `openadapt-flow benchmark` needs a checkout. +- REST verifiers require the app's record path, record key and delete path; + FHIR verifiers require its resource type and field map. API-save admission + and idempotency-key detection use explicit app configuration. Contracts that + pin an app profile must adopt its updated digest before using the revised + bookkeeping rules. Existing verifier identities and contract digests stay + unchanged when their configuration is unchanged. +- Re-record macOS bundles whose record identity was captured from a table cell. + Identity now uses the whole row; a table with no accessible row falls back + to its recorded identity band. ### 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. - -- A lost update now stops the run. `frame.versions` names a read of the target's versions (HAPI's `hfj_res_ver`, a version column, or an app's action-history table) and, where the app records one, the author of each version. The verifier reads them before and after the write; a version of a pre-existing target by anyone other than the agent, or more than one version step when no author is read, refutes the frame. A versions read that fails, a target with no version, or a version with no author leaves the frame not checked. The receipt holds counts and a problem code (`evidence.frame.versions`). Contracts without `frame.versions` keep their digests. - -- A write that lands after the verdict can now be caught. With `frame.database.recheck_after_s`, a verified write's receipt says when a recheck is due (`recheck_due_at`), and `openadapt_flow.verifier.recheck` reads the whole database again at or after that time. It judges every change since the post-save read with the frame's rules: the app profile's bookkeeping, other users' writes the audit log attributes, and changes a later write by the same actor under the same contract saw in its own window (`excused_by`, any outcome). Anything else turns the earlier result into `REFUTED` (late). A change that may be what refuted a later write, a database that can't be read, or a saved state that wasn't issued here or doesn't match its receipt gives `UNCERTAIN`. The saved state holds keyed hashes, not values. `frame.database.settle_attempts` lets the reads after a save, and at a recheck, retry within a stated budget until two in a row match; rows added to append-only tables between them don't count as movement. Without these settings, nothing changes ([#1639](https://github.com/OpenAdaptAI/OpenAdapt/pull/1639)). - -- A `CONFIRMED` write with a recheck due now says it is provisional (`WriteResult.provisional`, `provisional` in `to_dict()`). MCP clients of `openadapt verify serve` can run the recheck with the new `verify_recheck` tool. A recheck that gives `REFUTED` or `UNCERTAIN` now resolves the write's ledger attempt again for review, so a retry under the same key is held for a person instead of being handed the earlier `CONFIRMED`. A recheck from a saved `recheck.json` finds the attempt by the receipt's `correlation_id`; the result carries its ledger (`WriteResult.ledger`); when no attempt is re-resolved, the recheck's reasons say so. Over MCP, `verify_recheck` excuses the agent's own later writes under the same contract. +- A code-as-action agent is now the default for the first run. Every save is + read back before that run becomes a replayable program. Form scripts can run + before an admitted API save. +- Teach authorizations bind the task, origins, inputs, permitted write and + forbidden actions to a person on the signed approver roster. Create, sign + and verify them with `openadapt-flow teach-authorization`. +- Generated contracts can be admitted through a sandbox without setup answers. + They compare their own tables at recheck, support field sets absent from a + manifest, and refuse joins or parameter bindings the demonstrations cannot + distinguish. Dialog checks can be learned in any language when multiple + demonstrations agree; an ambiguous check is refused. +- Learn an app's per-request bookkeeping writes, then enforce the rules a + person approved. See [Learned excuses](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/LEARNED_EXCUSES.md). +- Signed write receipts bind each verifier verdict to the stores and time + window checked, its strength, and an explicit list of what was not checked. + The `receipts` extra supports offline verification and optional transparency + log anchoring. See [Write receipts](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/WRITE_RECEIPTS.md). +- Encrypt recordings and bundles at rest with a local key. Strict mode refuses + unsealed inputs. Compile, compose, route, process, visualize, variant, teach + and library paths can read sealed artifacts without a plaintext export. + See [Encryption at rest](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/phi_at_rest.md). +- Roll back durable runs, including after a crash. Desktop and remote-display + rollback checks the starting screen through the same step resolver used by + a run. The ledger retains owed rechecks across restarts and distinguishes a + consumed, missing or incorrectly invoked recheck. +- Serve record-checked fault environments through OpenEnv and Prime Intellect, + with shared observation types and MCP tools. Windows desktop packs cover + UI Automation and pixel-only remote display. The `openenv` extra requires + Python 3.11 or later; individual environments retain their documented host + and qualification requirements. +- A confirmed write followed by an escalation can earn configurable partial + reward credit; the default is 0.5. ### Fixes -- A composite primary key in the whole-database frame is now kept as a JSON array of its values. Before, the values were joined with `|`, so the keys (`x|y`, `z`) and (`x`, `y|z`) were the same, one row hid the other, and deleting it went unseen. This applies to every dialect. A snapshot or recheck state saved by an earlier version keys rows differently, so recheck a write with the version that checked it. - -- The read-only SQL guard now reads the query's tokens instead of matching substrings. `REPLACE(phone, '-', '')`, a `;` inside a string literal, and a table quoted as `` `call` `` no longer refuse the query. Every write and multi-statement form it refused before is still refused. It now also refuses a backslash, a dollar-quoted string, an unterminated literal, and a call to `query_to_xml` or `eval`, which run a query passed as text. When the query has parameters, it also refuses a `%` placeholder inside a literal or quoted name (the driver splices the value in as SQL; `%%` is fine). It refuses a dollar quote right after a number, row locks (`FOR SHARE`, `FOR KEY SHARE`, `LOCK IN SHARE MODE`), and `get_lock`, `pg_advisory_*`, `pg_sleep_for`, `pg_sleep_until`, `master_pos_wait`, `source_pos_wait`, `pg_terminate_backend`, `pg_cancel_backend`, `load_file`, `pg_read_file`, `pg_ls_dir` and `pg_notify`. - -- The whole-database snapshot reads a SQLite `REAL` with 17 digits, so 0.3 and 0.1+0.2 no longer read the same, and the text `"\x00null"` no longer reads as NULL. A number's plain value, which selectors compare with, is the shortest text that reads back as the stored value (`1.1` for a MySQL `FLOAT`, `1.5` for a `DECIMAL(24,8)`), so a correct write is no longer stopped because a selector stopped matching. - -- The whole-database frame (`frame.database`) now reads MySQL and MariaDB schemas with every column type. A `POINT` column (Dolibarr 21) made the read raise `TypeError`; a `BIT` column did the same; `FLOAT` values compared by six printed digits; decimals lost digits past 28. Binary strings, `BIT`, geometries and vectors are read as hex, `FLOAT` is widened to `DOUBLE`, `JSON` is read as its stored text, and numbers parse as `Decimal`. A value that still can't be read exactly makes the database unreadable, never a column dropped, and `begin_write` now says why the frame could not be read. - -- A parameter set to `None` no longer makes the record check unreadable. Before, `verify_write` returned `UNCERTAIN` ("could not be read back") whatever was written, because the null could not be turned into text. Now a `None` in an effect's `match`, `new_records` or `value` matches only a field that is present and null. A missing field matches only where the contract declares `absent_is_null: true` on it. A null never matches `""` or any other value, and a stored null no longer matches the text `"None"`. A `None` where a null can't be bound (a query parameter, a key, the frame, a screen read) is refused at `begin_write`. The write ledger keys `None` and `""` as different writes; a null write is held while any attempt stands under its old key (`None` keyed as `""`), and that attempt's result is never handed back. A read that loses information (a FHIR path that never resolves, an outer join that filters rows) can confirm a null, so match a null only on a base column ([#1660](https://github.com/OpenAdaptAI/OpenAdapt/issues/1660)). -- Behavior change: `field_equals` with an expected `""` no longer passes on a stored `NULL`. A contract that clears a field on an app that stores a cleared field as `NULL` should expect `None`. - -- A whole-number float parameter now matches its stored value. Before, `hours: 2.0` reached the check as the text `"2"` and a SQL `float8` column read back as `2.0`, so the two never matched and a correct write was refuted. Now a value the store typed as a number (a SQL numeric column; a JSON number) compares with an expected number by exact decimal value. Text stays byte-exact, and there is no tolerance. +- Typed input must match the complete field value. Native and pixel-only runs + stop when delivery cannot be confirmed. Windows input checks the foreground + window throughout delivery and reports when focus changes may leave partial + input. Native date fields receive ISO dates correctly. +- Read-back compares exact, typed whole-field values. Generated save checks + retain duplicate keys and recognize a copied value even when it also looks + like the row's own key. A value is bound to a parameter only when equality + distinguishes it from every other candidate. +- Rechecks hold for review when a later write's frame was not judged. A failed + saved state cannot supply a trusted ledger id. Later successful HTTP writes + without a known record count as unexplained changes. Volatile FHIR metadata + is refused before the first run. +- Whole-database checks reject ambiguous PostgreSQL table and sequence names + and non-PostgreSQL schema wildcards. A column rule cannot excuse a row with + no cell hashes. Rollback audit entries identify exactly one snapshot table. + API spans re-read checks after writes and preserve an earlier halt reason. +- Covered or off-viewport targets stop before pixel fallback. Self-repair may + dismiss a proved app-owned notice over the identity band, then checks the + full identity again. It refuses questions, unrelated windows and incomplete + coverage of the obscured band. +- macOS supports the pinned window's AppKit sheet, Qt field delivery and row + identity, and receipts bound to the resolved click target. Remote-display + focus checks apply pending workspace updates before reading the active app. + Function keys F1–F12 have native key codes. +- Linux reads editable field values and element identities through AT-SPI; + password fields abstain. Physical input accepts an already focused pinned + window. Governed self-repair can admit a new click with a unique element + identity and a record check. +- Desktop self-repair can start a fresh app session for each probe when the + backend can bind its local window to the configured process. Windows over + its agent and FreeRDP still refuse this mode. Probe read-back handles words + joined by OCR without weakening single-word checks. +- Windows qualification observes the executable, machine, OS and live session, + including connected remote sessions. Input remains bound to that environment; + an old agent without the required fields refuses. Lab profiles choose the + test app's interpreter, and headless FreeRDP can request AVC444. +- Strict no-outbound mode permits child processes only under named containment. + Redaction identifies credential names by their parts. Sealed input admission + checks named path arguments and refuses a missing deployment key. + +### Evaluation and packaging + +- Verification benchmarks include operation semantics, model-judge and API + read-back baselines, and revised public leaderboard scores. These reports + retain their stated task and evaluation scope. +- Qualification includes Linux AT-SPI, native macOS record/compile/run cases, + desktop replay on hosted runners, and backend capability evidence by surface. + Published qualification results do not qualify every app or deployment. +- Benchmark outputs, demo fixtures and restricted reference environments stay + outside distributed package artifacts. CI checks application-independent + engine code and validates the built wheel and source distribution. diff --git a/packages/flow/CHANGELOG.md b/packages/flow/CHANGELOG.md index 8854402d95..5648f4c529 100644 --- a/packages/flow/CHANGELOG.md +++ b/packages/flow/CHANGELOG.md @@ -15,67 +15,120 @@ -## Unreleased - -### Changed - -- The wheel no longer ships the benchmark harnesses. `openadapt_flow.benchmark` and `openadapt_flow.instrument` moved to `packages/flow/benchmark/harness/` in the source tree (import `benchmark.harness`); `openadapt-flow benchmark` runs from a source checkout and says so from a wheel. The engine audit for application-specific code is in `docs/GENERALITY_AUDIT.md`. Code that imported `openadapt_flow.benchmark` or `openadapt_flow.instrument` from an installed wheel imports `benchmark.harness` from a source checkout instead. +## v2.6.0 (2026-11-04) + +Flow adds signed teach authorizations, encrypted local artifacts, durable +rollback, and more ways to train and evaluate agents. Verification still +refuses a write when the record or its outcome cannot be established. + +### Upgrade notes + +- Agent-taught drafts now need a signed teach authorization in every admission + mode. The authorization also covers self-repair and approval of an agent's + fix. Browser teach no longer teaches or runs write tools. See + [Teach authorization](docs/TEACH_AUTHORIZATION.md). +- Benchmark harnesses and test-only helpers are no longer shipped in the wheel. + Replace imports from `openadapt_flow.benchmark` and + `openadapt_flow.instrument` with `benchmark.harness` from a source checkout. + The `harness2` extra is removed; `openadapt-flow benchmark` needs a checkout. +- REST verifiers require the app's record path, record key and delete path; + FHIR verifiers require its resource type and field map. API-save admission + and idempotency-key detection use explicit app configuration. Contracts that + pin an app profile must adopt its updated digest before using the revised + bookkeeping rules. Existing verifier identities and contract digests stay + unchanged when their configuration is unchanged. +- Re-record macOS bundles whose record identity was captured from a table cell. + Identity now uses the whole row; a table with no accessible row falls back + to its recorded identity band. ### Features -- Linux: self-repair admits a fix's new click on its AT-SPI element identity plus the record check, as on macOS and Windows. `LinuxBackend.element_identity_at` reads the role, name, id, ancestry up to the pinned window (exact title, bounds and process) and application; anything else is no identity, and the click is refused. Before, a repair on Linux refused every new click ("reads an accessibility tree but can't read element identity yet") ([#1704](https://github.com/OpenAdaptAI/OpenAdapt/issues/1704)). - -- Linux: `LinuxBackend.text_value_at` and `focused_text_value` read an editable field's exact text over AT-SPI, so a TYPE step is verified from the field rather than by OCR. Password fields abstain. - -- The monorepo CI runs Linux on real AT-SPI: the `flow-linux-atspi` job (post-merge tier) runs `scripts/qualify_linux_atspi.py` and records, compiles, runs governed and repairs a write workflow on a GTK3 app with SQLite (`benchmark/repair_every_backend/qualify_linux.py`), and uploads both results ([#1704](https://github.com/OpenAdaptAI/OpenAdapt/issues/1704)). - -- macOS: record, compile and a governed run on a third-party app. `benchmark/macos_native/qualify_calibre.py` sets a book's tag in calibre (Qt, SQLite library), certified under `clinical-write`, run under `standard` with a SQL record check; a book with the same title by another author stops at the identity check and two identical books are refused as ambiguous, each with nothing written ([#1705](https://github.com/OpenAdaptAI/OpenAdapt/issues/1705)). - -- Teach authorizations (`openadapt.teach-authorization/v1`). A person signs, with an approver key on the signed approver roster, what an agent may do when it teaches or repairs a program: the task, origins, inputs, the one write and forbidden actions and controls. `openadapt-flow teach-authorization create|sign|verify`; `--teach-authorization` on `compile-agent` and `openadapt-agent serve --teach`. Design and the upgrade notes: `docs/TEACH_AUTHORIZATION.md`. - - **Breaking:** admission of every JIT draft now needs one (`teach_auth_not_recorded` otherwise), in every admission mode; so does a self-repair of an agent-taught program and the approval of a calling agent's fix. Drafts from the airbag handoff are refused, and browser teach (`serve --teach-browser`) no longer teaches or runs write tools. - -- A dialog's empty-result check in any language. Induction mines the one line a named confirmation dialog adds when every demonstration shows the same line and every value typed before the dialog differs between them. Questions, numbers, typed values and lines from one demonstration only are not mined. When the demonstrations disagree on a mined check, induction refuses (`ambiguous_guard`) instead of copying the first recording's. The opener and its Confirm click no longer read as a two-row loop. Qualification asks for a business-rule case that halts at such a check. See `docs/DUPLICATE_CHECK.md`. - -- `run --self-repair` (and repair by default) now works outside a browser. `repair.session` in `deploy.yaml` says how to start the app for each probe, fault probe and resumed run (an argv, no shell, kept running while the app is up; `fault_commands`, `close_command`, `ready_s`). The run's configured backend must bind to a window owned by that command's process group, so a session never binds to the halted session or a copy already open; Flow ends the group afterwards, and closes the halted session before each probe. This works where the bound window is on Flow's machine (macOS, and the local RDP or Citrix client window); Windows over its agent and FreeRDP are refused for now. Without `repair.session`, a desktop run refuses repair with that reason, as before. - -- Signed write receipts. A verifier verdict, with its scope (stores read, time window, strength) and a plain list of what it did not check, is signed as a COSE_Sign1 in the IETF SCITT shape and appended to a local transparency log. `openadapt-flow receipt verify FILE --issuer-key --log-key` checks one offline and prints the not-checked list every time; a receipt that claims more than its scope is neither issued nor accepted. `verify_write(..., write_receipt=True)`, `issue_write_receipt(result)` and `openadapt-flow shadow --receipts` issue them; `receipt anchor` puts the digest in Rekor. Extra: `openadapt-flow[receipts]`. Design: `docs/WRITE_RECEIPTS.md`. +- A code-as-action agent is now the default for the first run. Every save is + read back before that run becomes a replayable program. Form scripts can run + before an admitted API save. +- Teach authorizations bind the task, origins, inputs, permitted write and + forbidden actions to a person on the signed approver roster. Create, sign + and verify them with `openadapt-flow teach-authorization`. +- Generated contracts can be admitted through a sandbox without setup answers. + They compare their own tables at recheck, support field sets absent from a + manifest, and refuse joins or parameter bindings the demonstrations cannot + distinguish. Dialog checks can be learned in any language when multiple + demonstrations agree; an ambiguous check is refused. +- Learn an app's per-request bookkeeping writes, then enforce the rules a + person approved. See [Learned excuses](docs/LEARNED_EXCUSES.md). +- Signed write receipts bind each verifier verdict to the stores and time + window checked, its strength, and an explicit list of what was not checked. + The `receipts` extra supports offline verification and optional transparency + log anchoring. See [Write receipts](docs/WRITE_RECEIPTS.md). +- Encrypt recordings and bundles at rest with a local key. Strict mode refuses + unsealed inputs. Compile, compose, route, process, visualize, variant, teach + and library paths can read sealed artifacts without a plaintext export. + See [Encryption at rest](docs/phi_at_rest.md). +- Roll back durable runs, including after a crash. Desktop and remote-display + rollback checks the starting screen through the same step resolver used by + a run. The ledger retains owed rechecks across restarts and distinguishes a + consumed, missing or incorrectly invoked recheck. +- Serve record-checked fault environments through OpenEnv and Prime Intellect, + with shared observation types and MCP tools. Windows desktop packs cover + UI Automation and pixel-only remote display. The `openenv` extra requires + Python 3.11 or later; individual environments retain their documented host + and qualification requirements. +- A confirmed write followed by an escalation can earn configurable partial + reward credit; the default is 0.5. ### Fixes -- Typed input now needs the complete whitespace-squashed OCR value, including values shorter than four characters. Native and pixel-only runs halt when delivery cannot be confirmed instead of clicking and typing again. The Windows agent checks the foreground window before and after each character or clipboard paste; a change stops delivery and reports that input may be partial ([#1478](https://github.com/OpenAdaptAI/OpenAdapt/issues/1478)). - -- A REST read names its own records path: `effects.kind: rest`, a reward recipe of kind `rest`, `RestRecordVerifier`, `RestCompensator` and the fault gate's in-memory copy require `records_path` (and `records_key`, `delete_path_template`) instead of defaulting to the demo app's `/api/db`; `run --effects-kind rest` takes `--effects-records-path`. Compile-time REST binding admission (`compile_recording(admit_api_bindings=True, admission_fixture=...)`) takes the app's read path, write path, server-assigned fields and idempotency key field from an `AdmissionFixture`, and proposes nothing when no write was captured and no write path is named. Effect mining and `scaffold-verifier` mine an idempotency key only from a field the caller names. Verifier identities and fault-gate check digests of existing configs are unchanged. - -- A FHIR read names its resource and field map: `effects.kind: fhir`, a reward recipe of kind `fhir` and `FhirEffectVerifier` require `resource_type` and `field_paths` instead of defaulting to one app's note-as-Observation layout (`DEFAULT_OBSERVATION_PATHS` stays importable for callers whose app stores notes that way). An app profile's `no_record_values` lists the record-field values its audit entries use for "no record" (OpenEMR's profile: `"0"`; profile version `openemr-frame-profile/2026-11-03.1`, so a config that pins the old profile digest is refused until it pins the new one); the frame and rollback no longer treat `0` as "no record" in every app. `generate ensure-contract` reads only the generic server-fact names from `facts.json`; the Medplum probe's translation moved to `benchmark/fhir_medplum/readpath/facts.py`. - -- A qualification case can run on `--backend windows`. `WindowsBackend` now answers the qualification environment observer from one `/context/identity` call: the foreground executable's name, the SHA-256 of that executable file, the live logon-session digest, and a digest of the machine id and exact OS build. Before, `qualify run-case` on Windows refused with "qualification backend cannot observe the complete environment". The digest is withheld when the file changed after the process started (an updater replacing a running program), and it describes the host executable for an app run by an interpreter, browser or shared runtime. Guarded input now also binds the digest and the environment, so a same-named program from another path cannot take the foreground between the check and the input. The agent's session digest now accepts a connected RDP or Citrix session, not only the console session, so the agent can run inside a remote user session. An agent without the new `context_environment_v1` fields still refuses ([#1696](https://github.com/OpenAdaptAI/OpenAdapt/issues/1696)). - -- A covered-band dismiss step takes its dialog box from the window the backend proved covers the band, before any pixel search, so a native message box that the accessibility tree doesn't label as a dialog can be dismissed. Without a proved window the step is refused as before. - -- macOS: Qt apps' table cells and inline editors report no `AXTopLevelUIElement`, so `MacOSBackend` couldn't scope them to the window and refused them. Their window is now the nearest `AXWindow` or `AXSheet` up the `AXParent` chain (within 32 levels; reaching the application refuses). - -- macOS: a text field that accepts only `AXValue` (Qt's line edits) now takes typed text: the text is spliced over the selection and counts as delivered only when the field reads back exactly that value. It refuses when the field changes between the read and the write, and when the field holds characters outside the Basic Multilingual Plane (AX ranges count UTF-16 units). Before, every such field refused AX text delivery. - -- macOS: a table cell's identity text is now its whole row, not the cell alone, and the row census reads rows the same way; a cell under a table that exposes no row (Qt's item views) abstains, so identity falls back to the recorded band. Before, the clicked cell's own text could verify a different record with the same title. A read-back (`displayed_text_at`) still reads the cell's own text. A macOS bundle recorded on a table cell before this change stored the cell's text and halts at that step; record it again. - -- macOS: F1 to F12 have key codes. `MacOSBackend` takes `pointer_settle_timeout_s` for slow captures. - -- Linux: physical input works on a real session. `focus_window` accepts the pinned window when it is already active (GTK refuses `grab_focus` on a top-level frame, so every physical click and keystroke was refused). - -- The wheel no longer ships test fixtures and demo harnesses: the Harness² integration, the MockLoan recorder, `guard_demo`, the MockMed teach-scope adapter, the synthetic drift stream (the `StructuralDiffInducer` it used now lives in `openadapt_flow.learning.induce`) and the task-ledger simulation moved to `benchmark/harness/` or beside their tests, the reward bundle helpers moved to `openadapt_flow.reward.bundle`, and the `harness2` extra is gone. The MockLoan server and static app remain in the wheel because the shipped OpenEnv environment serves them from an installed package. - -- Self-repair on a covered identity band dismisses only the proved dialog: the covering window must span the band from where the read stops to its right edge at full height, the dismiss click must land in that same window, and only a dialog whose every word is a notice word (no question) may be dismissed ([#1724](https://github.com/OpenAdaptAI/OpenAdapt/issues/1724)). - -- Self-repair can take a stop where the app's own dialog covers the next step's identity band: when the backend proves the covering window belongs to the window the last verified step was in, and the part of the band read still matches the record, the dialog is dismissed after the previous click and the step re-checks its identity in full. Another record behind the dialog stays an identity halt. The runtime lets a backend remember a verified record's window (`record_verified_window`); macOS answers `owned_window_over` from the window's proved sheet ([#1724](https://github.com/OpenAdaptAI/OpenAdapt/issues/1724)). - -- macOS: a run can now act in the pinned window's own AppKit sheet (an `NSAlert` attached to it), which is a separate window. `MacOSBackend` accepts it only when AX lists exactly one `AXSheet` on the window and exactly one window of the same process has its bounds inside it; the capture composites exactly that sheet over the window, and focus, pointer and click-target checks accept it only inside its bounds. Any other window is still refused ([#1711](https://github.com/OpenAdaptAI/OpenAdapt/issues/1711)). - -- On a macOS host, a pixel-only run (`RemoteDisplayBackend`: Parallels, RDP and Citrix clients) no longer reads a stale frontmost app. `MacWindowClient` applies NSWorkspace's pending updates before each read, so a freshly started app's window passes the focus check instead of failing it as if an app that already exited were still frontmost ([#1712](https://github.com/OpenAdaptAI/OpenAdapt/issues/1712)). - -- A durable run (`standard` or `regulated`) on macOS no longer refuses every click the AX tree resolves (`remote delivery receipt does not bind the resolved target`). `MacOSBackend.act_structural` re-finds the locator's one element, refuses unless its fingerprint (AX path, role, name, app, window, bounds) still equals the resolved handle's, and delivers the gated physical click at its center; the receipt binds that fingerprint. The receipt check is unchanged ([#1710](https://github.com/OpenAdaptAI/OpenAdapt/issues/1710)). - -- Self-repair's probe on a backend with no form to read (native or pixel-only) no longer refuses a typed value because OCR glued its words together: a value of two or more words and at least six letters also counts when it shows on one line with its words joined. Single words keep the word rule. +- Typed input must match the complete field value. Native and pixel-only runs + stop when delivery cannot be confirmed. Windows input checks the foreground + window throughout delivery and reports when focus changes may leave partial + input. Native date fields receive ISO dates correctly. +- Read-back compares exact, typed whole-field values. Generated save checks + retain duplicate keys and recognize a copied value even when it also looks + like the row's own key. A value is bound to a parameter only when equality + distinguishes it from every other candidate. +- Rechecks hold for review when a later write's frame was not judged. A failed + saved state cannot supply a trusted ledger id. Later successful HTTP writes + without a known record count as unexplained changes. Volatile FHIR metadata + is refused before the first run. +- Whole-database checks reject ambiguous PostgreSQL table and sequence names + and non-PostgreSQL schema wildcards. A column rule cannot excuse a row with + no cell hashes. Rollback audit entries identify exactly one snapshot table. + API spans re-read checks after writes and preserve an earlier halt reason. +- Covered or off-viewport targets stop before pixel fallback. Self-repair may + dismiss a proved app-owned notice over the identity band, then checks the + full identity again. It refuses questions, unrelated windows and incomplete + coverage of the obscured band. +- macOS supports the pinned window's AppKit sheet, Qt field delivery and row + identity, and receipts bound to the resolved click target. Remote-display + focus checks apply pending workspace updates before reading the active app. + Function keys F1–F12 have native key codes. +- Linux reads editable field values and element identities through AT-SPI; + password fields abstain. Physical input accepts an already focused pinned + window. Governed self-repair can admit a new click with a unique element + identity and a record check. +- Desktop self-repair can start a fresh app session for each probe when the + backend can bind its local window to the configured process. Windows over + its agent and FreeRDP still refuse this mode. Probe read-back handles words + joined by OCR without weakening single-word checks. +- Windows qualification observes the executable, machine, OS and live session, + including connected remote sessions. Input remains bound to that environment; + an old agent without the required fields refuses. Lab profiles choose the + test app's interpreter, and headless FreeRDP can request AVC444. +- Strict no-outbound mode permits child processes only under named containment. + Redaction identifies credential names by their parts. Sealed input admission + checks named path arguments and refuses a missing deployment key. + +### Evaluation and packaging + +- Verification benchmarks include operation semantics, model-judge and API + read-back baselines, and revised public leaderboard scores. These reports + retain their stated task and evaluation scope. +- Qualification includes Linux AT-SPI, native macOS record/compile/run cases, + desktop replay on hosted runners, and backend capability evidence by surface. + Published qualification results do not qualify every app or deployment. +- Benchmark outputs, demo fixtures and restricted reference environments stay + outside distributed package artifacts. CI checks application-independent + engine code and validates the built wheel and source distribution. ## v2.5.0 (2026-11-02) diff --git a/packages/flow/openadapt_flow/__init__.py b/packages/flow/openadapt_flow/__init__.py index 2214b30f3d..63022d0da7 100644 --- a/packages/flow/openadapt_flow/__init__.py +++ b/packages/flow/openadapt_flow/__init__.py @@ -1,6 +1,6 @@ """openadapt-flow: record, compile, replay deterministically, heal on drift.""" -__version__ = "2.5.0" +__version__ = "2.6.0" # Strict no-outbound mode follows the package into child interpreters, so a # Python child that imports openadapt_flow is refused off-machine connections diff --git a/packages/flow/pyproject.toml b/packages/flow/pyproject.toml index 5944433b25..b66e93825f 100644 --- a/packages/flow/pyproject.toml +++ b/packages/flow/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "openadapt-flow" -version = "2.5.0" +version = "2.6.0" description = "Compile demonstrated GUI workflows into deterministic local replay with governed repair and refusal" readme = "README.md" license = { text = "MIT" } diff --git a/packages/flow/uv.lock b/packages/flow/uv.lock index 421937709a..754df4e106 100644 --- a/packages/flow/uv.lock +++ b/packages/flow/uv.lock @@ -2622,7 +2622,7 @@ wheels = [ [[package]] name = "openadapt-flow" -version = "2.5.0" +version = "2.6.0" source = { editable = "." } dependencies = [ { name = "cryptography" }, From a11b31bf39e166926bfcb46a1f380b04806cacda Mon Sep 17 00:00:00 2001 From: abrichr Date: Wed, 4 Nov 2026 11:34:16 -0500 Subject: [PATCH 2/3] docs: include attended-state encryption in Flow release notes Signed-off-by: abrichr --- llms-full.txt | 4 +++- packages/flow/CHANGELOG.md | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/llms-full.txt b/llms-full.txt index 2da539400c..880019b53a 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -2324,7 +2324,9 @@ refuses a write when the record or its outcome cannot be established. - Encrypt recordings and bundles at rest with a local key. Strict mode refuses unsealed inputs. Compile, compose, route, process, visualize, variant, teach and library paths can read sealed artifacts without a plaintext export. - See [Encryption at rest](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/phi_at_rest.md). + Attended state, checkpoints and the approval journal are encrypted by default; + legacy plaintext state is upgraded before use, and raw authority paths are + refused in strict mode. See [Encryption at rest](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/phi_at_rest.md). - Roll back durable runs, including after a crash. Desktop and remote-display rollback checks the starting screen through the same step resolver used by a run. The ledger retains owed rechecks across restarts and distinguishes a diff --git a/packages/flow/CHANGELOG.md b/packages/flow/CHANGELOG.md index 5648f4c529..cc4eee5766 100644 --- a/packages/flow/CHANGELOG.md +++ b/packages/flow/CHANGELOG.md @@ -63,7 +63,9 @@ refuses a write when the record or its outcome cannot be established. - Encrypt recordings and bundles at rest with a local key. Strict mode refuses unsealed inputs. Compile, compose, route, process, visualize, variant, teach and library paths can read sealed artifacts without a plaintext export. - See [Encryption at rest](docs/phi_at_rest.md). + Attended state, checkpoints and the approval journal are encrypted by default; + legacy plaintext state is upgraded before use, and raw authority paths are + refused in strict mode. See [Encryption at rest](docs/phi_at_rest.md). - Roll back durable runs, including after a crash. Desktop and remote-display rollback checks the starting screen through the same step resolver used by a run. The ledger retains owed rechecks across restarts and distinguishes a From 364a66c5ac9af4c3354b81ffa93bb3e3e5b23bb1 Mon Sep 17 00:00:00 2001 From: abrichr Date: Wed, 4 Nov 2026 11:55:29 -0500 Subject: [PATCH 3/3] docs: cover identity and version checks in Flow 2.6 notes Signed-off-by: abrichr --- llms-full.txt | 9 +++++++++ packages/flow/CHANGELOG.md | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/llms-full.txt b/llms-full.txt index 880019b53a..66a3248b3a 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -2284,6 +2284,9 @@ refuses a write when the record or its outcome cannot be established. ### Upgrade notes +- A differing record identifier now counts as a contradiction, even when most + of the identity band matches. Runs may refuse some same-record pairs they + accepted before. Check the stopped record before retrying. - Agent-taught drafts now need a signed teach authorization in every admission mode. The authorization also covers self-repair and approval of an agent's fix. Browser teach no longer teaches or runs write tools. See @@ -2341,6 +2344,10 @@ refuses a write when the record or its outcome cannot be established. ### Fixes +- A versions check with an unknown `target_surface` reports `not_checked` + with `unknown_surface`. A surface read only by a zero-count guard cannot + supply a write target. Generated setup questions now show a missing version + check instead of leaving it unnoticed. - Typed input must match the complete field value. Native and pixel-only runs stop when delivery cannot be confirmed. Windows input checks the foreground window throughout delivery and reports when focus changes may leave partial @@ -2383,6 +2390,8 @@ refuses a write when the record or its outcome cannot be established. ### Evaluation and packaging +- The threat model explains which verification layer provides each guarantee. + Additional adversarial tests exercise exact read-back and capture ambiguity. - Verification benchmarks include operation semantics, model-judge and API read-back baselines, and revised public leaderboard scores. These reports retain their stated task and evaluation scope. diff --git a/packages/flow/CHANGELOG.md b/packages/flow/CHANGELOG.md index cc4eee5766..fc5a8eb989 100644 --- a/packages/flow/CHANGELOG.md +++ b/packages/flow/CHANGELOG.md @@ -23,6 +23,9 @@ refuses a write when the record or its outcome cannot be established. ### Upgrade notes +- A differing record identifier now counts as a contradiction, even when most + of the identity band matches. Runs may refuse some same-record pairs they + accepted before. Check the stopped record before retrying. - Agent-taught drafts now need a signed teach authorization in every admission mode. The authorization also covers self-repair and approval of an agent's fix. Browser teach no longer teaches or runs write tools. See @@ -80,6 +83,10 @@ refuses a write when the record or its outcome cannot be established. ### Fixes +- A versions check with an unknown `target_surface` reports `not_checked` + with `unknown_surface`. A surface read only by a zero-count guard cannot + supply a write target. Generated setup questions now show a missing version + check instead of leaving it unnoticed. - Typed input must match the complete field value. Native and pixel-only runs stop when delivery cannot be confirmed. Windows input checks the foreground window throughout delivery and reports when focus changes may leave partial @@ -122,6 +129,8 @@ refuses a write when the record or its outcome cannot be established. ### Evaluation and packaging +- The threat model explains which verification layer provides each guarantee. + Additional adversarial tests exercise exact read-back and capture ambiguity. - Verification benchmarks include operation semantics, model-judge and API read-back baselines, and revised public leaderboard scores. These reports retain their stated task and evaluation scope.