From 7494445a170322dbf0c979b30ab70bd21f0803ed Mon Sep 17 00:00:00 2001 From: abrichr Date: Sat, 17 Oct 2026 23:38:53 -0400 Subject: [PATCH 1/5] docs: correct approval, Desktop, and network disclosures for released versions connect-an-agent said no tool approves a save without saying what does. It now names the released route (openadapt flow item-approval) and the three approval tools on main. Desktop pages described a per-save approval form that desktop-v0.15.0 doesn't have. data-handling gains a by-command table of what leaves the computer, including the countersign timestamp request on main. configuration listed OPENADAPT_FLOW_BROWSER, which isn't in openadapt-flow 2.1.0. --- docs/explanation/verification.md | 10 ++++++---- docs/how-to/connect-an-agent.md | 20 +++++++++++++++++-- docs/how-to/desktop.md | 21 ++++++++++++-------- docs/how-to/run.md | 21 +++++++++++++++++++- docs/reference/configuration.md | 2 +- docs/security/data-handling.md | 34 +++++++++++++++++++++++++------- docs/security/procurement-faq.md | 8 +++++--- 7 files changed, 90 insertions(+), 26 deletions(-) diff --git a/docs/explanation/verification.md b/docs/explanation/verification.md index 3f7ff642..70a3e567 100644 --- a/docs/explanation/verification.md +++ b/docs/explanation/verification.md @@ -72,12 +72,14 @@ read can see, so it should cover every table a save can change. ## How a save is approved -Checking a save and approving one are separate. The approval mode decides +Checking a save and approving one are separate, and both are separate from +admission, which happens once per workflow. The approval mode decides whether a run waits for an answer before each save: -- `each_item`: the run waits before each save until a person approves the - filled form in OpenAdapt Desktop. This is what `openadapt flow run` uses - unless another mode is set. +- `each_item`: the run waits before each save until the filled form is + approved. This is what `openadapt flow run` uses unless another mode is + set. [Run a workflow](../how-to/run.md#3-choose-how-saves-are-approved) + shows how to answer. - `problems_only`: only saves that fail a check go to a person. It isn't available to customers yet; until it is configured, every save still asks. - `not_gated`: no pause. Runs that OpenAdapt Cloud dispatches use this, and diff --git a/docs/how-to/connect-an-agent.md b/docs/how-to/connect-an-agent.md index 4124f8a9..cc1046aa 100644 --- a/docs/how-to/connect-an-agent.md +++ b/docs/how-to/connect-an-agent.md @@ -128,8 +128,24 @@ request again never enters the values twice; it returns the earlier result. Each save waits for approval unless the operator starts the server with another `--approval-mode`. While it waits, the agent gets `needs_review` with -the reason `waiting_for_approval`. A person answers on the computer that runs -OpenAdapt. In version 2.2.0, the server has no tool that approves a save. +the reason `waiting_for_approval`. + +In openadapt-agent 2.2.0, the server has no approval tools. A person answers +on the computer that runs OpenAdapt with `openadapt flow item-approval`; see +[Run a workflow](run.md#3-choose-how-saves-are-approved). + +The `main` branch, not yet released, adds three tools: + +- `get_approval_mode` reports the server's approval mode and whether the + agent may answer. +- `list_approval_requests` lists a run's waiting saves, without values or + screenshots. +- `answer_approval_request` approves or declines one of them. + +The last two appear only when the operator starts the server with +`serve --delegate-approvals AGENT_ID`. Flow accepts the agent's answer only +where the run's approver policy allows an agent for that step, and records the +agent as the approver. By default, only a person may answer. ## Write an Agent Skill diff --git a/docs/how-to/desktop.md b/docs/how-to/desktop.md index 8c657213..145021e4 100644 --- a/docs/how-to/desktop.md +++ b/docs/how-to/desktop.md @@ -7,15 +7,18 @@ description: >- # Install the desktop app OpenAdapt Desktop is a graphical front end for the same engine the -`openadapt flow` commands use. You record, compile, replay, review, and -approve saves on your own computer. Nothing leaves the computer unless you +`openadapt flow` commands use. You record, review, teach, and run workflows +on your own computer. Nothing leaves the computer unless you connect it to [OpenAdapt Cloud](deploy.md#openadapt-cloud). ## 1. Download and install Get the installer from [openadapt.ai/download](https://openadapt.ai/download). The page detects your operating system and offers the right build. The -current build is the GitHub pre-release `desktop-v0.15.0`: +current build is the GitHub pre-release `desktop-v0.15.0`. It includes +openadapt-flow 1.27.1. Its sidebar has **Workflows**, **Record**, **Runner**, +**Phone**, and **Settings**. **Runner** runs workflows that OpenAdapt Cloud sends +to this computer; it's off until you turn it on. | Operating system | Installer | |---|---| @@ -70,9 +73,11 @@ Record a few seconds of any app, then stop. If the frames show your screen, the permissions are right. If they're blank, recheck step 2 and restart the app. [Troubleshoot](troubleshoot.md) covers other first-week problems. -## Approving saves in Desktop +## Approving saves -When a run waits for approval before a save (the `each_item` mode), Desktop -shows the filled form in **Run it**. Approve it to save exactly what you see, -or decline with a reason: wrong values, wrong record, or a screen problem. -See [Run a workflow](run.md#3-choose-how-saves-are-approved). +`desktop-v0.15.0` predates per-save approval, so its runs don't pause before +a save. In the next Desktop build, when a run waits before a save, Desktop +shows the filled form. You approve it to save exactly what you see, or decline +with a reason. Until then, answer waiting saves from `openadapt flow` runs +with `openadapt flow item-approval`; see +[Run a workflow](run.md#3-choose-how-saves-are-approved). diff --git a/docs/how-to/run.md b/docs/how-to/run.md index 5d11d1be..c8fa8a7c 100644 --- a/docs/how-to/run.md +++ b/docs/how-to/run.md @@ -49,7 +49,7 @@ answer before each save. | Mode | Before each save | |---|---| -| `each_item` | The run waits until a person approves that save in OpenAdapt Desktop, then saves exactly what the person saw. | +| `each_item` | The run waits until that save is approved, then saves exactly what was approved. | | `problems_only` | Only problems go to a person. This mode isn't available to customers yet: until it is configured, every save still asks. | | `not_gated` | No pause. The run report records that the run wasn't gated. | @@ -61,6 +61,25 @@ so. A run that waits longer than `--approval-wait` (default 900 seconds, at most 14,400) stops before the save. +Approval modes are separate from admission, which happens once per workflow +before production runs. + +In openadapt-flow 2.1.0, a person answers a waiting save on the computer that +runs it. `show` prints the waiting requests as JSON, including each +`request_id` and `request_sha256`: + +```bash +openadapt flow item-approval show RUN_DIR +openadapt flow item-approval approve RUN_DIR --request-id ID \ + --request-sha256 SHA256 --decided-by human --route desktop_run_it +``` + +`decline` takes the same options plus `--reason`: `wrong_values`, +`wrong_record`, or `screen_problem`. Version 2.1.0 accepts only +`--decided-by human` and `--route desktop_run_it`. The next OpenAdapt Desktop +build adds an approval form for this; the released installer doesn't have it +yet. See [Install the desktop app](desktop.md#approving-saves). + ## 4. Read the result `run` prints a one-line result and writes `REPORT.md` and `report.json` to diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index 6541f5fb..a18454b6 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -171,7 +171,7 @@ policy: | `OPENADAPT_FLOW_SCRUB` | Scrubbing of identifiers from saved text: `auto` (default), `on`, or `off`. `on` stops with an error if the scrubber isn't installed. | | `OPENADAPT_FLOW_PHI_MODE` | Turns patient-data mode on; `OPENADAPT_FLOW_SCRUB=on` also does | | `OPENADAPT_FLOW_VLM_URL` | The address of an on-site model server | -| `OPENADAPT_FLOW_BROWSER` | Which browser to use: `auto` (default), `chrome`, `msedge`, or `bundled` | +| `OPENADAPT_FLOW_NO_AUTO_INSTALL` | `1` skips the Chromium download on the first browser run, for computers without internet access | | `OPENADAPT_HOME` | Where OpenAdapt keeps local state (default `~/.openadapt`) | | `OPENADAPT_AGENT_RUNS_DIR` | The MCP server's runs folder (default `~/.openadapt/runs`) | | `OAFLOW_AGENT_TOKEN` | The shared token for the Windows agent server | diff --git a/docs/security/data-handling.md b/docs/security/data-handling.md index 02410b50..87f3699b 100644 --- a/docs/security/data-handling.md +++ b/docs/security/data-handling.md @@ -63,15 +63,35 @@ archives, and symlinks, refuse the whole copy instead of passing through. The sanitizer's success isn't proof of de-identification; the person's review is the gate. -## What can leave your network +## What leaves the computer, by command -| Traffic | When | What it carries | +A local or on-premises run with default settings sends nothing outside your +network. These are the commands that connect to something other than the +application they drive. + +| Command | Where it connects | What it sends | |---|---|---| -| None | A local or on-premises run with default settings | | -| Model requests | Only with model grounding turned on | Screenshots. Under patient-data mode, only to hosts on `phi_grounding_allowlist`. | -| A model server on your network | When `OPENADAPT_FLOW_VLM_URL` is set | Crops and screenshots, within your network. Identity crops sent to it aren't scrubbed. | -| Cloud reports | With `--report`, `report-break`, or a Cloud connector | Counts, digests, and closed values. See [Subprocessors](subprocessors.md#what-runs-send-to-openadapt). | -| Cloud uploads | `push` of an approved sanitized copy | The approved bytes only | +| `record`, `replay`, `run`, `resume` | The record check's endpoint, and the API in `actuation:` when one is set | The record check's query, and the values an API save writes | +| The same, with model grounding on | The model endpoint | Screenshots. Under patient-data mode, only to hosts on `phi_grounding_allowlist`. | +| The same, with `OPENADAPT_FLOW_VLM_URL` set | A model server on your network | Crops and screenshots. Identity crops sent to it aren't scrubbed. | +| The first browser run on a computer | Playwright's download server, once | A request for Chromium; nothing from the workflow. `OPENADAPT_FLOW_NO_AUTO_INSTALL=1` skips the download. | +| `run --report`, `report-run`, `report-break`, `connector` | OpenAdapt Cloud | Counts, digests, and closed values. See [Subprocessors](subprocessors.md#what-runs-send-to-openadapt). | +| `run --cloud-execution-id`, `attach-seal` | OpenAdapt Cloud | The execution id and the run's receipt, to get a Seal | +| `connect`, `login` | OpenAdapt Cloud | A pairing code or an ingest token | +| `push` | OpenAdapt Cloud | The approved sanitized bytes only | +| `openadapt seal keys pin` | `app.openadapt.ai/api/public/seal-keys`, or `--url` Nothing; it downloads the public keys. `openadapt seal verify` connects only when you give it a URL. | +| `openadapt agent authoring connect` | `openadapt.ai`, outgoing HTTPS only | The link it claims, then it polls for authoring requests | + +Commands are `openadapt flow` commands unless the table names another one. + +On the `main` branch, not yet released, `countersign add` and +`countersign timestamp-baseline` ask a timestamp service to date a record. +By default that is Sigstore's public service, +`https://timestamp.sigstore.dev/api/v1/timestamp`. Only a 32-byte digest +and a random nonce are sent; no record content, names, or +identifiers. `--tsa-url URL` points them at another RFC 3161 timestamp +service, and `--tsa-cert PEM` pins its certificate. `--no-timestamp` sends +nothing. No other `countersign` command connects to the network. ## Encryption diff --git a/docs/security/procurement-faq.md b/docs/security/procurement-faq.md index a6c02422..05a5a434 100644 --- a/docs/security/procurement-faq.md +++ b/docs/security/procurement-faq.md @@ -62,9 +62,11 @@ log. See [Files and evidence](../reference/artifacts.md). ## Operation **Does a person approve every save?** -That's the default: every run waits for a person to approve each save in -OpenAdapt Desktop unless the deployment chooses a different approval mode. -Runs dispatched from OpenAdapt Cloud don't wait on a person per save; they +Not necessarily. Each workflow is certified and admitted once before it runs +in production. Separately, the approval mode decides whether a run pauses +before each save: `openadapt flow run` pauses (`each_item`) unless the +deployment chooses another mode. Runs dispatched from OpenAdapt Cloud don't +pause per save; they rely on certification, the right-record check, and the record check, and stop when any of them can't confirm. See [Verification](../explanation/verification.md#how-a-save-is-approved). From 5ed339846a31a18276a293af0a2afaa020b238bb Mon Sep 17 00:00:00 2001 From: abrichr Date: Sat, 17 Oct 2026 23:51:51 -0400 Subject: [PATCH 2/5] docs(run): a person's approval names the approver or says it has none item-approval approve refuses an answer with neither --approver-id nor --unauthenticated, so the example would have failed. --- docs/how-to/run.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/how-to/run.md b/docs/how-to/run.md index c8fa8a7c..16f1f750 100644 --- a/docs/how-to/run.md +++ b/docs/how-to/run.md @@ -71,12 +71,16 @@ runs it. `show` prints the waiting requests as JSON, including each ```bash openadapt flow item-approval show RUN_DIR openadapt flow item-approval approve RUN_DIR --request-id ID \ - --request-sha256 SHA256 --decided-by human --route desktop_run_it + --request-sha256 SHA256 --decided-by human --route desktop_run_it \ + --unauthenticated ``` `decline` takes the same options plus `--reason`: `wrong_values`, `wrong_record`, or `screen_problem`. Version 2.1.0 accepts only -`--decided-by human` and `--route desktop_run_it`. The next OpenAdapt Desktop +`--decided-by human` and `--route desktop_run_it`. `--unauthenticated` +records the answer without a name; to name the approver instead, pass their +OpenAdapt Cloud user id with `--approver-id` and how they signed in with +`--auth-method cloud_aal2` or `cloud_aal1`. The next OpenAdapt Desktop build adds an approval form for this; the released installer doesn't have it yet. See [Install the desktop app](desktop.md#approving-saves). From 9cfb26a9feab8c47d8047f4cf0390ea1e6050eee Mon Sep 17 00:00:00 2001 From: abrichr Date: Sun, 18 Oct 2026 00:07:47 -0400 Subject: [PATCH 3/5] docs: point the phone guide at the renamed data-handling section --- docs/how-to/answer-from-a-phone.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/how-to/answer-from-a-phone.md b/docs/how-to/answer-from-a-phone.md index ccf83ec0..6fe0429d 100644 --- a/docs/how-to/answer-from-a-phone.md +++ b/docs/how-to/answer-from-a-phone.md @@ -131,5 +131,5 @@ workflow, record, or step. ## Related - [Handle a halt](handle-a-halt.md) -- [Data handling](../security/data-handling.md#what-can-leave-your-network) +- [Data handling](../security/data-handling.md#what-leaves-the-computer-by-command) - [Configuration: `human_decisions`](../reference/configuration.md#human_decisions) From 2fe36e695bcfd3bc94a8bb3fbaf50d6367299b03 Mon Sep 17 00:00:00 2001 From: abrichr Date: Sun, 18 Oct 2026 00:08:06 -0400 Subject: [PATCH 4/5] docs: document flow 2.2.0: approvals, MCP tools, receipts and countersigning, configuration, verify.yaml Write the reference for what ships in openadapt-flow 2.2.0 and openadapt-agent 2.2.1, and pin the generated command help to OpenAdapt main until those versions are on PyPI. --- cli-reference/requirements.txt | 13 +- docs/explanation/surfaces.md | 2 +- docs/explanation/verification.md | 63 +- docs/how-to/certify-and-qualify.md | 54 +- docs/how-to/connect-an-agent.md | 95 ++- docs/how-to/record-checks.md | 114 ++- docs/how-to/record.md | 24 +- docs/how-to/run.md | 84 +- docs/reference/artifacts.md | 45 +- docs/reference/cli-help.md | 1060 ++++++++++++++++++++++--- docs/reference/cli.md | 25 +- docs/reference/configuration.md | 101 ++- docs/reference/packages.md | 11 +- docs/reference/receipts-and-seals.md | 299 +++++++ docs/security/data-handling.md | 16 +- docs/security/evidence.md | 2 +- docs/security/execute.md | 26 +- mkdocs.yml | 1 + scripts/generate_cli_reference.py | 40 +- tests/fixtures/agent_cli.json | 12 +- tests/fixtures/flow_cli.json | 272 ++++++- tests/test_documented_flow_options.py | 20 +- 22 files changed, 2067 insertions(+), 312 deletions(-) create mode 100644 docs/reference/receipts-and-seals.md diff --git a/cli-reference/requirements.txt b/cli-reference/requirements.txt index 8921fe76..d9f0a6da 100644 --- a/cli-reference/requirements.txt +++ b/cli-reference/requirements.txt @@ -1,5 +1,10 @@ -# The released versions the command reference describes. Change a pin, then -# rerun scripts/generate_cli_reference.py and commit what it writes. +# The versions the command reference describes. Change a pin, then rerun +# scripts/generate_cli_reference.py and commit what it writes. +# +# openadapt-flow 2.2.0 and openadapt-agent 2.2.1 aren't published yet. Until +# they are, the pins build them from OpenAdapt main, which holds everything +# the 2.2.0 release ships. On release day, replace both lines with +# openadapt-flow==2.2.0 and openadapt-agent==2.2.1. openadapt==1.17.0 -openadapt-flow==2.1.0 -openadapt-agent==2.2.0 +openadapt-flow @ git+https://github.com/OpenAdaptAI/OpenAdapt@9104577e603e030f89318ead9cd9d586d439aefe#subdirectory=packages/flow # release 2.2.0 +openadapt-agent @ git+https://github.com/OpenAdaptAI/OpenAdapt@9104577e603e030f89318ead9cd9d586d439aefe#subdirectory=packages/agent # release 2.2.1 diff --git a/docs/explanation/surfaces.md b/docs/explanation/surfaces.md index ed05cb1c..3f257dde 100644 --- a/docs/explanation/surfaces.md +++ b/docs/explanation/surfaces.md @@ -61,5 +61,5 @@ surface still needs its own qualification. | Citrix | Required CI and a stand-in with no DOM | 3 confirmed runs and 3 stops under drift on a stand-in; no run over real Citrix ICA/HDX is on record yet | An agent can record on native Windows, RDP, and Citrix as well as in a -browser. That support is on the `main` branch and not yet in a release, and its +browser; see [Record a workflow](../how-to/record.md#an-agent-records). Its tests use a mock Windows server and stand-in client windows. diff --git a/docs/explanation/verification.md b/docs/explanation/verification.md index 70a3e567..ab808ef5 100644 --- a/docs/explanation/verification.md +++ b/docs/explanation/verification.md @@ -18,35 +18,46 @@ it isn't used as proof. A record check returns `confirmed`, `refuted`, or ## How strong a read-back is -The engine ranks every read-back by how independent it is of the screen that -did the work: - -| Rank | Read-back | Example | -|---|---|---| -| 1, strongest | The system of record, through its API, database, or files | REST, GraphQL, FHIR, read-only SQL, a file or report drop, an email capture folder | -| 2 | The app's record page in a separate, separately signed-in, read-only session | `ui_readback` | -| 3 | The same session navigates back to the record and reads it again | | -| 4, weakest | The screen that did the work still shows the value | | - -A rank-2 read is independent of the session that acted, but it still reads -a screen. It can't tell a record the app stored from one the app only -displays. +OpenAdapt ranks every read-back by how independent it is of the screen that +did the work. The rank is the Flow tier, 1 the strongest. The run report and +the receipt also give each read a grade, and a Seal names a check level. +This table maps the three: + + + +| Read-back | How you set it up | Flow tier | Grade | Ends `VERIFIED` | Certifies through qualification | Seal check level | +|---|---|---|---|---|---|---| +| The system of record, through its API, database, or files | `effects.kind` `rest`, `graphql`, `fhir`, `sql`, `file`, `document`, `email`, or `document-hash` | 1, `independent_system` | `independent_readback` | Yes | Yes | 2 | +| The app's record page, in a separate, read-only, freshly signed-in session | `effects.kind: ui_readback` | 2, `independent_session` | `screen_readback` | Yes, except under the `regulated` policy | No | 1 | +| The acting session clears what the screen remembers, reopens the record, and reads it | A check shown by demonstration; see [Show how you'd check the save](../how-to/record-checks.md#show-how-youd-check-the-save) | 3, `persisted_state_reacquisition` | `screen_readback` | No | No | 0 | +| The screen that did the work still shows the value | `effects.kind: onscreen` | 4, `immediate_screen` | `screen_readback` | No | No | 0 | +| No read-back | | | `unchecked` | No | No | | + +A tier-2 read is independent of the session that acted, but it still reads +the app's screens, so it can't tell a record the app stored from one it only +displays. Tiers 3 and 4 read the acting session's screen; they can stop a +run, but never make one `VERIFIED`. Seal check level 3 is a counterparty +artifact, such as a payer status, which Flow doesn't read. + +A run's receipt records the weakest read among its saves as +`effect_tier_reached` and its grade as `effect_assurance`. See +[Receipts, Seals, and countersigning](../reference/receipts-and-seals.md). ## What each result needs -**`VERIFIED` under Standard or Regulated.** On the `main` branch and in -`openadapt-flow` 2.1.0, every save must be confirmed at rank 2 or stronger: -the system of record, or a separate-session `ui_readback`. The `regulated` -policy pack raises that to rank 1. Ranks 3 and 4 can never produce -`VERIFIED`. A pixel-only Citrix save with no rank-1 or rank-2 read ends in a -halt or `RECONCILIATION_REQUIRED`, whatever an operator or the screen says. - -**Certification through qualification.** Qualification labels each read -`independent_readback` (rank 1), `screen_readback` (any read through the -app's screens, including `ui_readback`), or `unchecked`. To certify, the -normal case must be confirmed and every fault case must show nothing landed, -all at `independent_readback`. A workflow whose only read-back is -`ui_readback` can run the cases but can't be certified this way. +**`VERIFIED` under Standard or Regulated.** Every save must be confirmed at +tier 2 or stronger: the system of record, or a separate-session +`ui_readback`. The `regulated` policy pack raises that to tier 1. A +pixel-only Citrix save with no tier-1 or tier-2 read ends in a halt or +`RECONCILIATION_REQUIRED`, whatever an operator or the screen says. + +**Certification through qualification.** To certify, the normal case must be +confirmed and every fault case must show nothing landed, all at +`independent_readback`. A workflow whose only read-back is `ui_readback` can +run the cases but can't be certified this way. **A Seal.** OpenAdapt Cloud signs a Seal only when the run was confirmed by a system-of-record read. diff --git a/docs/how-to/certify-and-qualify.md b/docs/how-to/certify-and-qualify.md index 5640468d..96580aed 100644 --- a/docs/how-to/certify-and-qualify.md +++ b/docs/how-to/certify-and-qualify.md @@ -76,15 +76,17 @@ nothing counts as a silent wrong success, and one is enough to fail. ### Run a campaign in one command -On the `main` branch, one command runs every case from a `verify.yaml` file -that says how to sign in, reset, and read the record back: +One command runs every case from a `verify.yaml` file that says how to sign +in, reset, and read the record back: ```bash openadapt-flow qualify bundle --verify verify.yaml --out runs/qual-1 --certify clinical-write ``` -This form isn't in `openadapt-flow` 2.1.0; it is unreleased. A -shortened `verify.yaml`: +`--cases` runs only some cases and `--trials` sets trials per case; +certification needs all six cases and at least three trials of each. +`--run-params inputs.json` also runs the certified bundle once afterwards. +A shortened `verify.yaml`: ```yaml schema_version: openadapt.verify/v1 @@ -117,9 +119,9 @@ save's record checks from the demonstration. For your own app, add `effects` or the shorter `record` block to say which record the save must create. `${NAME}` reads an environment variable. -`reset` takes exactly one of `command`, `http`, or `python`. Before it runs -anything, `qualify` lists every problem it finds, such as a save step with no -right-record check. +Before it runs anything, `qualify` lists every problem it finds, such as a +save step with no right-record check. [The `verify.yaml` reference](#verifyyaml-reference) +lists every field. Run against MockMed with the quickstart's bundle, this campaign produced: @@ -128,8 +130,8 @@ qualify PASS: 18 trial(s) in 71.2 s ``` with every representative trial `VERIFIED` by an exact record match, every -fault trial stopped with no write, and zero model calls (`main`, MockMed, a -Mac, 17 October 2026). The output directory holds: +fault trial stopped with no write, and zero model calls (OpenAdapt `main` +before the 2.2.0 release, MockMed, a Mac, 17 October 2026). The output directory holds: ```text runs/qual-1/ @@ -143,9 +145,9 @@ runs/qual-1/ QUALIFICATION.md a summary to read ``` -### With openadapt-flow 2.1.0 +### Step by step -In the released version, the same cases run one at a time: +The same cases can run one at a time: - `qualify from-demo` proposes the record, identity, and record-check pins from the demonstration and accepts them. @@ -156,6 +158,36 @@ In the released version, the same cases run one at a time: OpenAdapt Desktop prepares the inputs that `qualify run-case` takes. +### verify.yaml reference + +`${NAME}` and `${NAME:-default}` read environment variables; a missing +variable without a default is an error. + +| Field | What it says | +|---|---| +| `schema_version` | `openadapt.verify/v1` | +| `qualification_id`, `environment_id` | Optional opaque ids written into the result | +| `minimum_effect_strength` | `independent_system_of_record` (the default) or `independent_session`. `qualify` refuses a verifier that reads weaker. Certification needs a system-of-record read either way. | +| `app.target` | The surface. Only `web` runs today. | +| `app.name`, `app.version` | What you're qualifying. The version goes into the environment digest. | +| `app.url`, `app.entry_url` | The app's address, whose origin is checked before every input, and where each trial starts (default `url`) | +| `app.headless`, `app.viewport` | Browser settings (defaults `true` and `[1280, 800]`) | +| `app.storage_state` | A Playwright storage-state file for a signed-in session | +| `app.session` | A `module:function` that opens a signed-in session, for sign-ins `storage_state` can't hold | +| `app.reset` | How to reset the app between trials: exactly one of `command` (a shell command), `http` (`method`, `url`, `body`), or `python` (`module:function`). `timeout_s` defaults to 600. | +| `verifier` | The read-back, with the same fields as a deployment file's `effects:` section: `rest`, `sql`, `fhir`, `graphql`, `ui_readback`, or a plugin kind. `onscreen` is refused. | +| `verifier.session` | For `kind: rest`, a `module:function` that returns a signed-in HTTP client, for sign-ins `auth` can't express | +| `effects` | The save's checks as an effect contract list, as in `effect_contract.yaml` | +| `record` | A shorter form of `effects`: `match` (the fields that find the record), `fields` (record field to input name, or `{literal: VALUE}`), `duplicate_key`, `expected_count` (default 1), and `timeout_s` (default 5). Give `effects` or `record`, not both; leave both out when the compiler already bound the checks. | +| `save_step` | `auto` picks the one irreversible step; otherwise name the step id | +| `inputs.source` | `recorded` reuses the demonstration's values; `file` loads `inputs.file` | +| `inputs.params`, `inputs.unique` | Override single values; suffix these inputs per campaign so reruns don't collide | +| `campaign.id`, `campaign.trials`, `campaign.cases`, `campaign.policy` | The id in the report, trials per case (default 3), the cases (default all six), and the policy `--certify` uses without a name (default `clinical-write`) | + +The six cases are `representative`, `fault-ambiguity`, +`fault-wrong-identity`, `fault-stale-identity`, `fault-weak-effect`, and +`fault-missing-effect`. + ## Add a human decision If the workflow has a point where a person must choose between reviewed diff --git a/docs/how-to/connect-an-agent.md b/docs/how-to/connect-an-agent.md index cc1046aa..4aa73eb3 100644 --- a/docs/how-to/connect-an-agent.md +++ b/docs/how-to/connect-an-agent.md @@ -128,24 +128,93 @@ request again never enters the values twice; it returns the earlier result. Each save waits for approval unless the operator starts the server with another `--approval-mode`. While it waits, the agent gets `needs_review` with -the reason `waiting_for_approval`. +the reason `waiting_for_approval`. `get_approval_mode` tells the agent which +mode applies and whether it may answer; no tool changes the mode. -In openadapt-agent 2.2.0, the server has no approval tools. A person answers -on the computer that runs OpenAdapt with `openadapt flow item-approval`; see -[Run a workflow](run.md#3-choose-how-saves-are-approved). +By default a person answers, on the computer that runs OpenAdapt; see +[Run a workflow](run.md#answer-a-waiting-save). To let your agent answer, +start the server with `--delegate-approvals AGENT_ID`, an opaque id such as +`agent:intake-bot`. The agent then gets two more tools: -The `main` branch, not yet released, adds three tools: - -- `get_approval_mode` reports the server's approval mode and whether the - agent may answer. -- `list_approval_requests` lists a run's waiting saves, without values or +- `list_approval_requests` lists a run's waiting saves: each `request_id`, + `request_sha256`, step, step class, and expiry, without values or screenshots. - `answer_approval_request` approves or declines one of them. -The last two appear only when the operator starts the server with -`serve --delegate-approvals AGENT_ID`. Flow accepts the agent's answer only -where the run's approver policy allows an agent for that step, and records the -agent as the approver. By default, only a person may answer. +Flow accepts the agent's answer only where the run's approver policy allows +an agent for that step class, and records the agent as the approver. Without +a policy, or for a clinical write without the customer's opt-in, the answer +is refused and a person answers. See +[Let an agent or system answer](run.md#let-an-agent-or-system-answer). + +## Tools + +Which tools the agent gets depends on how the server starts. + +| Tool | What it does | Offered when | +|---|---|---| +| `list_workflows` | Lists each workflow's name, purpose, what done means, and inputs | Always | +| `run_workflow` | Runs a workflow with your inputs and `request_id`, waiting up to `wait_seconds` | The sandbox, `--mode production`, or `--mode attended` | +| `get_run` | Gets a run's result, waiting up to `wait_seconds` while it runs | Always | +| `get_approval_mode` | Reports the approval mode and whether the agent may answer | `--mode production` or `attended` | +| `list_approval_requests`, `answer_approval_request` | List and answer waiting saves | Also `--delegate-approvals` | +| `get_repair_screen`, `teach_repair` | Get the screen where a run stopped, and show the one click past it | Also `--repair-access`; see [Handle a halt](handle-a-halt.md) | +| `get_workflow` | One workflow's structure and certification result, without recorded values | `--bundles` | +| `get_run_report` | A count-only summary of a stored run; `get_run` is preferred | `--bundles` | +| `list_needs_attention`, `get_attention_item` | Stopped runs and the decisions allowed on each; reload one before deciding | `--bundles` | +| `reject_attention`, `teach_attention`, `escalate_attention` | End a stopped run, ask for a corrective demonstration, or keep it paused for a colleague | `--mode attended` | +| `continue_attention`, `skip_attention` | After a person finishes the step in the app, check it and resume; or take a skip path the workflow declares | `--mode attended` with `--config` | +| `run_` | One older run tool per workflow. It takes no `request_id`, so a retry can enter the values twice. Use `run_workflow`. | `--mode production` or `attended` | + +`serve --authoring` offers a separate set for recording with an agent; see +[Record a demonstration](record.md#an-agent-records): + +| Tool | What it does | +|---|---| +| `observe` | Returns the pinned window's structure, without screenshots, text values, titles, or URLs | +| `start_record` | Starts a recording on the pinned browser, window, or remote session | +| `click` | Clicks one observed node, or `x`,`y` pixels | +| `type` | Types text the agent chose. Not for secrets or for what a person types. | +| `pause_for_input` | Pauses so a person types in the app, such as a sign-in. The agent never sees the value. | +| `continue_input` | Ends the pause. In Desktop, its overlay's **Continue** does this. | +| `halt`, `stop_record` | Stop the session without compiling | +| `compile` | Compiles the recording into a draft bundle | +| `admit` | Asks the person to accept the draft in a form the client shows. No agent argument accepts it. | +| `get_command_result` | Returns the last authoring result | +| `set_coach`, `get_coach` | Set and read a short hint shown to the person | +| `bind_status` | Reports the local session's status | + +## Server options + +`openadapt agent serve --help` lists every flag; the +[command help](../reference/cli-help.md#openadapt-agent-serve) has the full +text. The agent can't change any of them. + +| Flag | What it does | +|---|---| +| `--mode sandbox`, `production`, or `attended` | What the agent gets; see [Serve your own workflows](#serve-your-own-workflows). `sandbox` is the default without `--bundles`. | +| `--bundles DIR` | One bundle, or a folder of bundles | +| `--url URL`, `--config YAML`, `--policy NAME-OR-PATH` | The app, the deployment file, and the policy passed to every run | +| `--runs-dir DIR` | Where runs and evidence go (default `OPENADAPT_AGENT_RUNS_DIR`, else `~/.openadapt/runs`) | +| `--timeout SECONDS` | How long one run may work (default 600). Time waiting for an approval doesn't count. | +| `--approval-mode MODE` | The approval mode for every run; see [Approvals](#approvals) | +| `--delegate-approvals AGENT_ID` | Lets the agent answer waiting saves where the approver policy allows | +| `--repair-access FILE`, `--repair-dir DIR`, `--hold-on-halt SECONDS` | Screen access for repairs, where repair records live, and how long a halted browser page stays open (default 300) | +| `--allow-attended-actions` | Part of `--mode attended`: the decision tools for stopped runs | +| `--headed` | Shows the browser; a person needs it to continue or skip a stopped web run | +| `--authoring` | The recording tools above. `--url` with `--headed` opens a new browser with no saved sign-ins. | +| `--windows-agent-url URL` | With `--authoring`: record a native Windows app through the Windows agent server. The token comes from `OAFLOW_AGENT_TOKEN`. | +| `--remote-window APP`, `--citrix`, `--remote-window-title TITLE` | With `--authoring`: record in an RDP or Citrix client window, from pixels | +| `--authoring-screens off`, `scrubbed`, or `unscrubbed` | With `--authoring`: whether the agent may see the pinned window. `scrubbed` removes identifiers first and shares nothing if that fails. Default `off`. | +| `--sandbox-engine auto`, `flow`, or `simulated` | Whether the sandbox drives MockMed in a hidden browser (`flow`, needs the `tutorial` extra) or simulates it | +| `--allow-protected-export` | Sends recorded values, reports, and output to the agent. See [Security](#security). | +| `--allow-model-grounding` | Allows a configured model grounder; screenshots can leave the computer | +| `--allow-url-override` | Lets callers pass a `url` per call | +| `--allow-synthetic-recorded-defaults` | Demo only: omitted inputs reuse recorded values | +| `--flow-cli CMD`, `--extra-run-arg ARG` | Change how Flow is called. Either can relax Flow's checks. | + +`--allow-run` is an older name for `--mode production`, or `--mode sandbox` +without `--bundles`. `--tutorial` is an older name for `--mode sandbox`. ## Write an Agent Skill diff --git a/docs/how-to/record-checks.md b/docs/how-to/record-checks.md index 9da024ef..abc5685b 100644 --- a/docs/how-to/record-checks.md +++ b/docs/how-to/record-checks.md @@ -59,15 +59,24 @@ openadapt flow run bundle --effects-kind rest --effects-base-url https://app.exa ## 3. Pick the strongest check you can reach -| Where the record is read | `effects.kind` | Strength | +| Where the record is read | How | Strength | |---|---|---| -| The system's API or database: REST, GraphQL, FHIR, read-only SQL, a file or report drop, an email capture folder, a document store | `rest`, `graphql`, `fhir`, `sql`, `file`, `document`, `email`, `document-hash` | Strongest. Reads the system of record, not the screen. | -| The app's own record page, opened by id in a separate, freshly signed-in, read-only browser session | `ui_readback` | Weaker. Independent of the session that typed, but it is still a screen. | -| The screen the run worked on | `onscreen` | Weakest. A consistency signal, not proof of a save. | +| The system's API, database, or files: REST, GraphQL, FHIR, read-only SQL, a file or report drop, an email capture folder, a document store | `effects.kind` `rest`, `graphql`, `fhir`, `sql`, `file`, `document`, `email`, `document-hash` | Strongest. Reads the system of record, not the screen. | +| The app's own record page, opened by id in a separate, freshly signed-in, read-only browser session | `effects.kind: ui_readback` | Independent of the session that typed, but still a screen | +| The acting session clears what the screen remembers, opens the record again, and reads it | A check you show by demonstration | Can stop a run; never makes one `VERIFIED` on its own | +| The screen the run worked on | `effects.kind: onscreen` | Weakest. A consistency signal, not proof of a save. | -Use `ui_readback` when there is no API or database to read. Configure it -with a separate read-only account so the check never uses the session that -did the typing: +[How a save is checked](../explanation/verification.md#how-strong-a-read-back-is) +maps each one to what it can reach: `VERIFIED`, certification, and a Seal. +When the deployment can read more than one way, list them under +`effects.candidates` instead of `effects.kind`; Flow uses the strongest one +for each save. + +### Read the record page in a separate session + +Use `ui_readback` for a web app with no API or database to read. Configure +it with a separate read-only account, so the check never uses the session +that did the typing: ```yaml effects: @@ -84,18 +93,103 @@ effects: username_env: APP_READER_USERNAME password_env: APP_READER_PASSWORD ready_selector: "#encounters" + empty_selector: "#no-encounters" row_selector: "tr.encounter" fields: id: { attribute: data-id } - note: { selector: "td.note" } + note: { selector: "td.note", attribute: title } identity: reader: { selector: "#patient-mrn" } value: { param: patient_id } field: patient_id ``` -How strength affects outcomes and certification is explained in -[How a save is checked](../explanation/verification.md). +After it signs in, the session can only read: requests other than GET, HEAD, +and OPTIONS are refused, which makes the read uncertain and stops the run. +`read_post_paths` lists read endpoints that use POST. Read full values from +attributes where the page shortens text, declare `empty_selector`, and set +the effect's `timeout_s` above any replica lag; each of those otherwise +stops the run. A field's `date_format` and `casefold` change how it's +compared. Single sign-on and multi-factor sign-in aren't supported. + +A confirmed `ui_readback` counts as the record check while each save is +approved (`each_item`, the default). Under `problems_only`, a save checked +only this way still goes to a person. + +### Show how you'd check the save + +For an app with no API, database, export, or web record page, such as a +desktop app or one reached over RDP or Citrix, show OpenAdapt how you'd +check a save yourself. Record the task, save, then clear what the screen +remembers, open the saved record again, and look at the fields: + +```bash +openadapt flow record --with-check --out rec ... +``` + +After the save, Flow prompts "Now show how you'd check it saved." When you +stop, it lists the recorded events and asks where the check starts and which +regions show the values you typed. It writes them to `check.json` beside the +recording. For a recording made without `--with-check`, write the same file +with `check mark`: + +```bash +openadapt flow check mark rec --start-event 14 --field patient_id=410,212,180,24 +``` + +The check must contain a step that clears what the screen remembers: a +reload, a new search, or a window closed and reopened. Flow recognizes those +from the recording. Name one it can't recognize, such as a click on the +browser's reload button or a fresh sign-in, with +`--reset KIND@EVENT`, where `KIND` is `reload`, `new_search`, +`reopen_window`, or `fresh_session`. `compile` refuses a check with no +reset, a marked region that doesn't show the typed value, or a check that +types a secret. + +A compiled check confirms nothing until a person admits it, at a terminal: + +```bash +openadapt flow check show bundle +openadapt flow check admit bundle +``` + +`check admit` prints each check, its reset, and its fields, and asks you to +type `admit`. It records who admitted it, when, and a digest of the check, so +a check that changes needs admitting again. An admission in the name of an +agent or a service is refused. + +At run time the check replays the navigation with this run's values and reads +each field from the page structure where the app exposes it (browser, +macOS, Linux), or by OCR (always on RDP and Citrix). Each field is compared +by kind, inferred from the typed value or set with `--kind PARAM=KIND`: + +| Kind | Compared after | Matches | +|---|---|---| +| `text` | lowercasing and collapsing whitespace | `Chest pain` and `chest pain` | +| `code` | lowercasing and removing spaces, hyphens, dots, and slashes | `MG-44-08` and `mg4408` | +| `date` | reading it as a calendar date | `Mar 4, 2026` and `2026-03-04` | +| `currency` | reading it as an amount | `$1,250.00` and `1250` | + +A numeric date such as `03/04/2026` is uncertain unless `--date-order` +sets `dmy` or `mdy`. An OCR read that differs only in characters OCR can't +tell apart, such as `O` and `0` or `l` and `1`, is uncertain, never a match. +A blank or unreadable field is uncertain too, and an uncertain check stops +the run. + +### Re-read the acting screen + +`onscreen` re-reads the value on the screen the run worked on. It's a +consistency signal: an app can show a value it never stored. Set it with +`effects.kind: onscreen`; `readback_region` and `readback_min_ratio` set the +region to read and the match threshold. Prefer a check you show by +demonstration, which reopens the record first. + +### Write your own verifier + +A package can add an `effects.kind` by registering a factory under the +`openadapt_flow.effect_verifiers` entry-point group. A plugin can't replace a +built-in kind. See the +[effect-verifier kit](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/EFFECT_KIT.md#shipping-your-own-adapter-plugin-sdk). ## 4. Attach the checks to the compiled program diff --git a/docs/how-to/record.md b/docs/how-to/record.md index d4d4947e..a6a3e18c 100644 --- a/docs/how-to/record.md +++ b/docs/how-to/record.md @@ -129,13 +129,27 @@ calls `compile`, which writes the bundle to person's answer accepts the bundle. A client that can't show forms can't accept a bundle. The authoring server doesn't turn on runs. -An agent can also record on native Windows apps, through the Windows agent -server, and in RDP and Citrix client windows, from pixels alone. This is on the -`main` branch and not yet in a release. Its tests run against a mock Windows -server and stand-in client windows; no recording against a real Windows, RDP, -or Citrix session is on record yet. See +An agent can also record on a native Windows app, through the Windows agent +server, or in an RDP or Citrix client window, from pixels alone: + +```bash +openadapt agent serve --authoring --windows-agent-url http://localhost:5001 +openadapt agent serve --authoring --remote-window "Microsoft Remote Desktop" +openadapt agent serve --authoring --citrix --authoring-screens scrubbed +``` + +The Windows agent server's token comes from `OAFLOW_AGENT_TOKEN`. +`--remote-window-title` picks one window when the client has several. In a +remote session the agent clicks pixels and presses keys, so it needs +`--authoring-screens scrubbed` or `unscrubbed` to see the window; `scrubbed` +removes identifiers first and shares nothing if that fails. These paths are +tested against a mock Windows server and stand-in client windows; no +recording against a real Windows, RDP, or Citrix session is on record yet. +See [Recording on Windows, RDP and Citrix](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/RECORDING_ON_WINDOWS_RDP_CITRIX.md). +[Connect an agent](connect-an-agent.md#tools) lists every authoring tool. + ## One program per surface A compiled program is bound to the surface it was recorded on: one browser diff --git a/docs/how-to/run.md b/docs/how-to/run.md index 16f1f750..9b8aae91 100644 --- a/docs/how-to/run.md +++ b/docs/how-to/run.md @@ -45,7 +45,8 @@ openadapt flow run bundle --profile standard --config deploy.yaml \ ## 3. Choose how saves are approved Each run has an approval mode. It decides whether the run waits for an -answer before each save. +answer before each save. Approval modes are separate from admission, which +happens once per workflow before production runs. | Mode | Before each save | |---|---| @@ -54,19 +55,18 @@ answer before each save. | `not_gated` | No pause. The run report records that the run wasn't gated. | `openadapt flow run` uses `each_item` unless `--approval-mode` or -`runtime.approval_mode` names another mode. The MCP server passes the mode its -operator set with `serve --approval-mode`; callers can't change it. Runs that -OpenAdapt Cloud dispatches pass `not_gated` explicitly, and their reports say -so. A run that waits -longer than `--approval-wait` (default 900 seconds, at most 14,400) stops -before the save. +`runtime.approval_mode` names another mode, under every profile. The MCP +server passes the mode its operator set with `serve --approval-mode`; callers +can't change it. Runs that OpenAdapt Cloud dispatches pass `not_gated` +explicitly, and their reports say so. A run that waits longer than +`--approval-wait` (default 900 seconds, at most 14,400) stops before the +save; `resume` can still use an answer given later. -Approval modes are separate from admission, which happens once per workflow -before production runs. +### Answer a waiting save -In openadapt-flow 2.1.0, a person answers a waiting save on the computer that -runs it. `show` prints the waiting requests as JSON, including each -`request_id` and `request_sha256`: +`show` prints the run's waiting requests as JSON, including each +`request_id` and `request_sha256`. A person answers one with `approve` or +`decline`: ```bash openadapt flow item-approval show RUN_DIR @@ -76,13 +76,59 @@ openadapt flow item-approval approve RUN_DIR --request-id ID \ ``` `decline` takes the same options plus `--reason`: `wrong_values`, -`wrong_record`, or `screen_problem`. Version 2.1.0 accepts only -`--decided-by human` and `--route desktop_run_it`. `--unauthenticated` -records the answer without a name; to name the approver instead, pass their -OpenAdapt Cloud user id with `--approver-id` and how they signed in with -`--auth-method cloud_aal2` or `cloud_aal1`. The next OpenAdapt Desktop -build adds an approval form for this; the released installer doesn't have it -yet. See [Install the desktop app](desktop.md#approving-saves). +`wrong_record`, or `screen_problem`. `--unauthenticated` records the answer +without a name; to name the approver instead, pass their OpenAdapt Cloud user +id with `--approver-id` and how they signed in with `--auth-method cloud_aal2` +or `cloud_aal1`. OpenAdapt Desktop shows the same request as a form +in its next build; see [Install the desktop app](desktop.md#approving-saves). + +### Let an agent or system answer + +By default only a person may answer. An approver policy lets an agent, a +rule, or a system answer some kinds of save. Pass it with +`--approver-policy` on `run`, `resume`, or `qualify run-case`: + +```json +{ + "schema_version": "openadapt.approver-policy/v1", + "allowed": { + "read_only": ["person", "agent"], + "reversible": ["person", "agent"], + "consequential": ["person", "agent"], + "irreversible": ["person"], + "clinical_write": ["person"] + } +} +``` + +The policy names every step class: `read_only`, `reversible`, +`consequential`, `irreversible`, and `clinical_write`. A `clinical_write` +save takes a non-person approver only when `clinical_write_opt_in_sha256` +pins the customer's signed opt-in. Under the `clinical-write` and +`regulated` policies, every save is a clinical write. An unreadable policy +means person-only. + +A delegated approver answers in one of three ways: + +- **At the command line.** `item-approval approve` or `decline` with + `--decided-by automation`, `--delegate-kind agent` or `system`, + `--delegate-id ID`, and `--route local_cli`. `--on-behalf-of` names the + organization or person it acts for. +- **Over MCP.** With `serve --delegate-approvals AGENT_ID`, the agent uses + `list_approval_requests` and `answer_approval_request`; see + [Connect an agent](connect-an-agent.md#approvals). +- **By webhook.** `--approver-webhook URL` with `--approver-webhook-id ID` + asks an https endpoint about each waiting save. `--approver-webhook-kind` + is `system` (the default) or `agent`. Each message is signed with + HMAC-SHA256 using `OPENADAPT_APPROVER_WEBHOOK_SECRET`, in the + `X-OpenAdapt-Signature` header. It carries ids, the step class, the mode, + digests, and the expiry, never the step's words, a screenshot, or a value. + The reply must be signed and say `approve`, `decline` with a reason, or + `pending`. Any failure leaves the save to a person. + +The run checks every answer against the policy, and the report records who +or what approved, by which route. A person can always answer instead. +OpenAdapt Desktop and Cloud don't show delegated approvers yet. ## 4. Read the result diff --git a/docs/reference/artifacts.md b/docs/reference/artifacts.md index 80207b9b..eed93d84 100644 --- a/docs/reference/artifacts.md +++ b/docs/reference/artifacts.md @@ -31,46 +31,11 @@ number of model calls, and whether screenshots could have left the computer. `REPORT.md` is the same information to read. `openadapt flow explain` turns it into a short account with the next command to run. -## The receipt +## The receipt and the Seal A `VERIFIED` run writes `receipt.json` (schema `openadapt.run-receipt/v2`) -and an image of it. From the quickstart: +and `receipt.png`: counts, digests, and versions, no record values. A run +tracked by Cloud Execute can also carry Cloud's signature on it, the Seal, in +`report.json` as `execute_receipt`. See +[Receipts, Seals, and countersigning](receipts-and-seals.md). -```json -{ - "outcome": "VERIFIED", - "transaction_outcome": "VERIFIED", - "profile": "standard", - "substrate": "web", - "steps_ok": 6, "steps_total": 6, - "identity_confirmed": 5, "identity_required": 5, - "effects_confirmed": 2, "effects_required": 2, - "effect_tier_reached": "independent_system", - "model_calls": 0, - "external_network_calls": "none", - "bundle_digest": "d9b3102c…", - "provenance": "synthetic-tutorial" -} -``` - -A stopped run gets no receipt, only its local report. - -## The Seal - -A Seal is OpenAdapt Cloud's Ed25519 signature on the receipt of a run that -Cloud Execute tracked. A run gets one when it ends `VERIFIED` with a record -check that read the system of record, and it was started with -`--cloud-execution-id` and `OPENADAPT_EXECUTE_API_KEY` set. Flow then fetches -the signed receipt into `report.json` as `execute_receipt`. -`openadapt flow attach-seal RUN_DIR --execution-id ID` does the same later. - -To check a Seal offline, pin Cloud's public keys once, comparing the printed -fingerprint with the one OpenAdapt publishes: - -```bash -openadapt seal keys pin -openadapt seal verify runs//report.json -``` - -`seal verify` also accepts a receipt file or a webhook body, and `--json` -prints the result as JSON. It never fetches keys while it checks. diff --git a/docs/reference/cli-help.md b/docs/reference/cli-help.md index 4e86783a..352f1810 100644 --- a/docs/reference/cli-help.md +++ b/docs/reference/cli-help.md @@ -8,7 +8,7 @@ description: >- -This page is the `--help` text of every command in openadapt 1.17.0, openadapt-flow 2.1.0, openadapt-agent 2.2.0. +This page is the `--help` text of every command in openadapt 1.17.0, openadapt-flow 2.2.0, openadapt-agent 2.2.1. It's generated from each program's parser, and CI fails when it differs from those versions. [Commands](cli.md) explains what each command is for. The launcher's research commands (`capture`, `eval`, `serve`, and @@ -218,16 +218,16 @@ Options: ```text usage: openadapt-flow [-h] [--version] - {demo,record,demo-record,tutorial,compile,induce,for-each,compose,process,replay,run,attach-seal,resume,approve,item-approval,bench,benchmark,intake,lint,scaffold-verifier,explain,visualize,seal,bind-record-checks,certify,qualify,propose-qualification,disambiguate,emit-skill,emit-mcp,teach,teach-branch,repair,connect,login,sanitize,review-sanitized,materialize-approved,approve-sanitized,validate-hosted,push,report-break,report-run,console,serve-execute,serve-reward,business-decisions,connector,admit} + {demo,record,demo-record,tutorial,compile,induce,for-each,compose,process,replay,run,attach-seal,verify-run,resume,approve,item-approval,bench,benchmark,pull,intake,triage,lint,scaffold-verifier,explain,visualize,seal,countersign,bind-record-checks,certify,qualify,propose-qualification,disambiguate,emit-skill,emit-mcp,teach,teach-branch,repair,connect,login,sanitize,review-sanitized,materialize-approved,approve-sanitized,validate-hosted,push,report-break,report-run,console,serve-execute,serve-reward,business-decisions,connector,admit,check,jit} ... -Record a task once and compile it into a program that runs on this computer. With a record check +Record a task and compile it into a program that runs on this computer. With a record check configured, a governed run confirms each save by reading the record back, and a run that can't confirm a save stops and asks a person. When the screen changes, it finds the same field again or stops, and a lasting fix needs a person's approval. positional arguments: - {demo,record,demo-record,tutorial,compile,induce,for-each,compose,process,replay,run,attach-seal,resume,approve,item-approval,bench,benchmark,intake,lint,scaffold-verifier,explain,visualize,seal,bind-record-checks,certify,qualify,propose-qualification,disambiguate,emit-skill,emit-mcp,teach,teach-branch,repair,connect,login,sanitize,review-sanitized,materialize-approved,approve-sanitized,validate-hosted,push,report-break,report-run,console,serve-execute,serve-reward,business-decisions,connector,admit} + {demo,record,demo-record,tutorial,compile,induce,for-each,compose,process,replay,run,attach-seal,verify-run,resume,approve,item-approval,bench,benchmark,pull,intake,triage,lint,scaffold-verifier,explain,visualize,seal,countersign,bind-record-checks,certify,qualify,propose-qualification,disambiguate,emit-skill,emit-mcp,teach,teach-branch,repair,connect,login,sanitize,review-sanitized,materialize-approved,approve-sanitized,validate-hosted,push,report-break,report-run,console,serve-execute,serve-reward,business-decisions,connector,admit,check,jit} demo See the proof in one command: a checked run and a run where the app lies, side by side on one page (fake clinic data, no AI calls) record Record YOUR workflow interactively: a headed browser (--backend web @@ -259,6 +259,9 @@ positional arguments: wired for a real deployment (backend / effects / actuation / durable / policy) instead of the demo attach-seal Attach Cloud's signed Execute receipt (the Seal) to a VERIFIED run report + verify-run Read the system of record and rule on an effect that an agent, script, or + person claims it made; writes an unsigned openadapt.effect-receipt/v1. The + executor's own record is hashed, never trusted resume Resume a durably-paused run from its last verified checkpoint (never re- running an already-confirmed write) approve Record an authenticated approval (approver / resolution / bundle version) @@ -268,7 +271,9 @@ positional arguments: bench Replay a bundle N times against MockMed and aggregate benchmark Benchmark compiled replay vs. a Claude computer-use agent on the MockMed triage task (agent arm needs an Anthropic API key and costs real money) + pull Download a packaged workflow bundle by name intake Fill a workflow's parameters from a referral PDF, fax image, or email + triage Order verified saves for review and propose one batch approval lint Report a bundle's coverage gaps (unarmed clicks, vacuous postconditions, under-classified risk); exits nonzero by severity scaffold-verifier Draft an effect-oracle contract (effect_contract.yaml) from a recording or @@ -281,14 +286,19 @@ positional arguments: handoffs) as self-contained HTML, Mermaid, or the shared JSON graph spec seal Copy a bundle to a new path, encrypt its workflow and template evidence with OPENADAPT_BUNDLE_KEY, verify integrity, and publish it atomically + countersign Timestamp, countersign, selectively disclose, and export a dispute dossier + for a Seal bind-record-checks Attach reviewed record checks (system-of-record effect contracts) to a compiled program's save step, in a new bundle; reports which entered fields they compare certify Enforce a policy on a bundle (exits nonzero + reports if it fails); passing means the bundle meets that policy's rules, not that it is safe to run - qualify Create, edit, test, explain, and certify the versioned qualification - project sealed into a workflow bundle + qualify Qualify: before a compiled workflow may run unattended on a real system, + run it against the normal case and deliberate faults, and confirm it is + verified by reading back the system of record and halts on every fault. + Also: create, edit, test, explain and certify the qualification project + sealed into a bundle. propose-qualification Draft a production-shaped qualification contract from a compiled demo. Alias of `qualify propose`. @@ -343,6 +353,9 @@ positional arguments: jobs LOCALLY (customer owns the data boundary; PHI stays on this side) admit Read the published release-admission ledger. Does not sign or issue an admission. + check Teach a save's read-back by demonstration, and admit it. Nothing confirms + a save until a person admits the check. + jit Review, admit or reject a DRAFT compiled from a computer-use agent run options: -h, --help show this help message and exit @@ -381,7 +394,7 @@ usage: openadapt-flow record [-h] [--url URL] [--browser-cdp-endpoint URL] [--browser-page-url URL] --out OUT [--secret FIELD] [--param FIELD] [--identifier FIELD|X,Y,W,H] [--task TASK] [--until-cmd CMD] [--window OWNER] [--window-title SUBSTRING] [--headless] - [--profile {demo,standard,regulated}] + [--profile {demo,standard,regulated}] [--with-check] [--backend {web,windows,macos,linux,rdp,citrix}] [--agent-url URL] [--macos-app APP] [--macos-window-title TITLE] [--linux-app APP] [--linux-window-title TITLE] [--linux-allow-physical-input] @@ -440,6 +453,9 @@ options: --backend: it defaults to capture on this OS (macos/windows/linux), or to the browser when --url is given, and prints a visible notice. Omitted: the same permissive pre-profile default. + --with-check After the save, also show how you'd check it saved (reload or search + again, open the record). Flow then asks which fields show the values you + typed and writes check.json --backend {web,windows,macos,linux,rdp,citrix} Backend to drive: 'web' (Playwright/Chromium), 'windows' (native Windows via the WAA HTTP agent at replay), 'macos' (one native Mac app window), @@ -543,7 +559,7 @@ options: ```text usage: openadapt-flow compile [-h] --out OUT --name NAME [--accept-params NAME1,NAME2] - [--params-from FILE] [--no-confirm-params] + [--params-from FILE] [--no-confirm-params] [--pixel-verify-enabled] recording positional arguments: @@ -563,6 +579,11 @@ options: name, for rename>"}. Unlisted proposals stay constants (fail-closed). --no-confirm-params Skip the interactive parameter review even on a TTY; flagged proposals stay demonstrated constants. + --pixel-verify-enabled + The runtime that will run this bundle sets runtime.pixel_verify_enabled. + Lets the compiler give an unlabeled button a pixel identifier crop as its + identity check. Without it the compiler doesn't, because a crop that can't + be verified halts an irreversible step on every run. ``` ### `openadapt-flow induce` @@ -834,7 +855,9 @@ usage: openadapt-flow run [-h] [--profile {demo,standard,regulated}] [--url URL] [--process-environment-id PROCESS_ENVIRONMENT_ID] [--process-runner-id PROCESS_RUNNER_ID] [--qualification-authority-file PATH] [--dry-run] [--report] - [--cloud-execution-id CLOUD_EXECUTION_ID] + [--cloud-execution-id CLOUD_EXECUTION_ID] [--approver-policy FILE] + [--approver-webhook URL] [--approver-webhook-id APPROVER_WEBHOOK_ID] + [--approver-webhook-kind {agent,system}] [--approval-mode {each_item,problems_only,not_gated}] [--approval-wait SECONDS] bundle @@ -1013,6 +1036,21 @@ options: OPENADAPT_CLOUD_EXECUTION_ID). After a VERIFIED outcome, Cloud's signed receipt is fetched with OPENADAPT_EXECUTE_API_KEY and written into report.json as execute_receipt. + --approver-policy FILE + An openadapt.approver-policy/v1 JSON file: which kinds of approver + (person, rule, agent, system) may answer each class of save. Without it + only a person may answer. A clinical_write save takes a non-person + approver only when the policy pins an opt-in. + --approver-webhook URL + Ask this https endpoint (the customer's approver agent or system) about + each waiting save, with HMAC-signed, PHI-free messages. The secret is read + from OPENADAPT_APPROVER_WEBHOOK_SECRET. A person can still answer in + Desktop. Needs --approver-webhook-id and a policy that allows the + approver's kind. + --approver-webhook-id APPROVER_WEBHOOK_ID + The webhook approver's opaque id, such as system:acme-approvals + --approver-webhook-kind {agent,system} + Whether the webhook approver is an agent or a system (default system) --approval-mode {each_item,problems_only,not_gated} Per-item approval before each consequential save. each_item (the default for every run): the run waits before every save until a person approves it @@ -1043,6 +1081,38 @@ options: Cloud base URL (or OPENADAPT_CLOUD_URL; default https://app.openadapt.ai) ``` +### `openadapt-flow verify-run` + +```text +usage: openadapt-flow verify-run [-h] [--verify YAML] [--params JSON] [--run-record JSON] + [--authorization-ref AUTHORIZATION_REF] [--baseline JSON] + [--capture-baseline JSON] [--out DIR] [--check JSON] + [--keys JSON] + [--accept-outcome {halted_before_effect,reconciliation_required,rejected_policy,failed_platform}] + [--approver JSON] [--remediates RECEIPT_ID] [--correlation-id ID] + +options: + -h, --help show this help message and exit + --verify YAML verify.yaml: verifier wiring and effect contract + --params JSON The authorized task's values the contract binds + --run-record JSON The executor's run record (needs executor_kind) + --authorization-ref AUTHORIZATION_REF + Opaque id of the authorization in your own system + --baseline JSON Baseline captured before the executor ran + --capture-baseline JSON + Read the store now and write a private baseline, then exit + --out DIR Directory for effect-receipt.json + --check JSON Check a signed effect receipt offline instead + --keys JSON Pinned Seal key registry for --check + --accept-outcome {halted_before_effect,reconciliation_required,rejected_policy,failed_platform} + With --check: also exit 0 for this outcome when the signature is genuine + (repeatable; no value accepts every outcome) + --approver JSON Who approved the write: {kind, id, policy_ref}; id is opaque + --remediates RECEIPT_ID + receipt_id of an earlier effect receipt this run remediates + --correlation-id ID Your own opaque id linking this receipt to a ticket or trace +``` + ### `openadapt-flow resume` ```text @@ -1061,6 +1131,9 @@ usage: openadapt-flow resume [-h] [--qualification-authority-file PATH] [--api-actuator] [--api-base-url API_BASE_URL] [--durable] [--attest-contract PATH] [--attest-sign-key PATH] [--attest-audit-log PATH] [--attest-pre-state PATH] + [--approver-policy FILE] [--approver-webhook URL] + [--approver-webhook-id APPROVER_WEBHOOK_ID] + [--approver-webhook-kind {agent,system}] [--approval-mode {each_item,problems_only,not_gated}] [--approval-wait SECONDS] run_dir @@ -1183,6 +1256,21 @@ options: omitted, replay captures one into the run directory (attest_pre_state.json) before actuation begins (env fallback: OPENADAPT_FLOW_ATTEST_PRE_STATE) + --approver-policy FILE + An openadapt.approver-policy/v1 JSON file: which kinds of approver + (person, rule, agent, system) may answer each class of save. Without it + only a person may answer. A clinical_write save takes a non-person + approver only when the policy pins an opt-in. + --approver-webhook URL + Ask this https endpoint (the customer's approver agent or system) about + each waiting save, with HMAC-signed, PHI-free messages. The secret is read + from OPENADAPT_APPROVER_WEBHOOK_SECRET. A person can still answer in + Desktop. Needs --approver-webhook-id and a policy that allows the + approver's kind. + --approver-webhook-id APPROVER_WEBHOOK_ID + The webhook approver's opaque id, such as system:acme-approvals + --approver-webhook-kind {agent,system} + Whether the webhook approver is an agent or a system (default system) --approval-mode {each_item,problems_only,not_gated} Per-item approval before each consequential save. each_item (the default for every run): the run waits before every save until a person approves it @@ -1252,6 +1340,9 @@ options: ```text usage: openadapt-flow item-approval approve [-h] --request-id REQUEST_ID --request-sha256 REQUEST_SHA256 --decided-by DECIDED_BY --route ROUTE + [--delegate-kind {agent,system}] + [--delegate-id DELEGATE_ID] + [--on-behalf-of ON_BEHALF_OF] [--approver-id APPROVER_ID] [--auth-method {cloud_aal2,cloud_aal1,none}] [--unauthenticated] @@ -1266,8 +1357,16 @@ options: --request-sha256 REQUEST_SHA256 The request digest from `item-approval show` that the person saw --decided-by DECIDED_BY - Who decided. Only 'human' is accepted. - --route ROUTE Where the person decided. Only 'desktop_run_it' is accepted. + Who decided: 'human', or 'automation' with --delegate-kind for an agent, + system or rule the run's policy allows + --route ROUTE Where it was decided: 'desktop_run_it' for a person; 'local_cli' or 'mcp' + for a delegated approver + --delegate-kind {agent,system} + A delegated approver's kind. The run's approver policy must allow it + --delegate-id DELEGATE_ID + The delegated approver's opaque id, such as agent:approver-bot + --on-behalf-of ON_BEHALF_OF + The organization or person the delegated approver acts for --approver-id APPROVER_ID The approver's own OpenAdapt Cloud user id --auth-method {cloud_aal2,cloud_aal1,none} @@ -1280,6 +1379,9 @@ options: ```text usage: openadapt-flow item-approval decline [-h] --request-id REQUEST_ID --request-sha256 REQUEST_SHA256 --decided-by DECIDED_BY --route ROUTE + [--delegate-kind {agent,system}] + [--delegate-id DELEGATE_ID] + [--on-behalf-of ON_BEHALF_OF] [--approver-id APPROVER_ID] [--auth-method {cloud_aal2,cloud_aal1,none}] [--unauthenticated] --reason @@ -1295,8 +1397,16 @@ options: --request-sha256 REQUEST_SHA256 The request digest from `item-approval show` that the person saw --decided-by DECIDED_BY - Who decided. Only 'human' is accepted. - --route ROUTE Where the person decided. Only 'desktop_run_it' is accepted. + Who decided: 'human', or 'automation' with --delegate-kind for an agent, + system or rule the run's policy allows + --route ROUTE Where it was decided: 'desktop_run_it' for a person; 'local_cli' or 'mcp' + for a delegated approver + --delegate-kind {agent,system} + A delegated approver's kind. The run's approver policy must allow it + --delegate-id DELEGATE_ID + The delegated approver's opaque id, such as agent:approver-bot + --on-behalf-of ON_BEHALF_OF + The organization or person the delegated approver acts for --approver-id APPROVER_ID The approver's own OpenAdapt Cloud user id --auth-method {cloud_aal2,cloud_aal1,none} @@ -1341,6 +1451,22 @@ options: --headed Run the browsers headed ``` +### `openadapt-flow pull` + +```text +usage: openadapt-flow pull [-h] [--out OUT] [--index INDEX] [--force] [--list] [workflow] + +positional arguments: + workflow Workflow id, for example openemr/new-referral + +options: + -h, --help show this help message and exit + --out OUT Destination directory (default: ./) + --index INDEX Registry index URL or path + --force Replace an existing destination + --list List available workflows +``` + ### `openadapt-flow intake` ```text @@ -1366,6 +1492,152 @@ options: Apply the calling agent's answer to --agent-request ``` +### `openadapt-flow triage` + +```text +usage: openadapt-flow triage [-h] {plan,approve-batch,decide,calibrate,estimate,eligibility} ... + +positional arguments: + {plan,approve-batch,decide,calibrate,estimate,eligibility} + plan Write a triage plan; print the review view + approve-batch Approve a plan's clean batch + decide Approve or decline one save + calibrate Fit a calibration artifact from labels + estimate Estimate the miss rate from audit labels + eligibility Check whether a workflow may switch to only asking about problems: a + pinned, eligible calibration artifact and an audit miss-rate bound under + the target. Exit 0 eligible, 3 not yet, 2 no evidence. + +options: + -h, --help show this help message and exit +``` + +### `openadapt-flow triage plan` + +```text +usage: openadapt-flow triage plan [-h] [--observations OBSERVATIONS] [--run-dir RUN_DIR] --mode + {each_item,problems_only,not_gated} + [--policy {off,rule_v0,learned}] [--artifact ARTIFACT] + [--artifact-sha256 ARTIFACT_SHA256] [--audit-rate AUDIT_RATE] + --out OUT + +options: + -h, --help show this help message and exit + --observations OBSERVATIONS + SaveObservation JSON lines + --run-dir RUN_DIR A finished run (repeatable) + --mode {each_item,problems_only,not_gated} + The approval mode the runs used. Only problems_only is triaged. + --policy {off,rule_v0,learned} + --artifact ARTIFACT Calibration artifact (learned only) + --artifact-sha256 ARTIFACT_SHA256 + Pinned artifact digest + --audit-rate AUDIT_RATE + --out OUT Where to write the plan +``` + +### `openadapt-flow triage approve-batch` + +```text +usage: openadapt-flow triage approve-batch [-h] --batch-sha256 BATCH_SHA256 [--pull-back ITEM_ID] + --labels LABELS --decided-by DECIDED_BY + [--approver-id APPROVER_ID] + [--auth-method {cloud_aal2,cloud_aal1}] + [--unauthenticated] + plan + +positional arguments: + plan + +options: + -h, --help show this help message and exit + --batch-sha256 BATCH_SHA256 + --pull-back ITEM_ID Review this one alone + --labels LABELS Label log (JSON lines) + --decided-by DECIDED_BY + Must be 'human' + --approver-id APPROVER_ID + The reviewer's Cloud user id + --auth-method {cloud_aal2,cloud_aal1} + --unauthenticated +``` + +### `openadapt-flow triage decide` + +```text +usage: openadapt-flow triage decide [-h] --item ITEM + (--approve | --decline {wrong_values,wrong_record,screen_problem}) + --labels LABELS --decided-by DECIDED_BY + [--approver-id APPROVER_ID] + [--auth-method {cloud_aal2,cloud_aal1}] [--unauthenticated] + plan + +positional arguments: + plan + +options: + -h, --help show this help message and exit + --item ITEM + --approve + --decline {wrong_values,wrong_record,screen_problem} + --labels LABELS Label log (JSON lines) + --decided-by DECIDED_BY + Must be 'human' + --approver-id APPROVER_ID + The reviewer's Cloud user id + --auth-method {cloud_aal2,cloud_aal1} + --unauthenticated +``` + +### `openadapt-flow triage calibrate` + +```text +usage: openadapt-flow triage calibrate [-h] --labels LABELS --group GROUP + [--target-miss TARGET_MISS] [--delta DELTA] + [--window WINDOW] --out OUT + +options: + -h, --help show this help message and exit + --labels LABELS + --group GROUP Group key (64 hex) + --target-miss TARGET_MISS + --delta DELTA + --window WINDOW + --out OUT +``` + +### `openadapt-flow triage estimate` + +```text +usage: openadapt-flow triage estimate [-h] --labels LABELS + +options: + -h, --help show this help message and exit + --labels LABELS +``` + +### `openadapt-flow triage eligibility` + +```text +usage: openadapt-flow triage eligibility [-h] --workflow WORKFLOW --artifact ARTIFACT + --artifact-sha256 ARTIFACT_SHA256 --labels LABELS + [--verified-runs VERIFIED_RUNS] + [--target-miss TARGET_MISS] [--delta DELTA] + [--audit-rate AUDIT_RATE] + +options: + -h, --help show this help message and exit + --workflow WORKFLOW Opaque workflow id + --artifact ARTIFACT Calibration artifact + --artifact-sha256 ARTIFACT_SHA256 + Pinned digest + --labels LABELS Label log (JSON lines) + --verified-runs VERIFIED_RUNS + --target-miss TARGET_MISS + --delta DELTA + --audit-rate AUDIT_RATE +``` + ### `openadapt-flow lint` ```text @@ -1440,6 +1712,231 @@ options: -o OUT, --out OUT New destination directory (must not already exist) ``` +### `openadapt-flow countersign` + +```text +usage: openadapt-flow countersign [-h] + {add,exceptions,resolve,close,purge,timestamp-baseline,disclose,verify,dossier} + ... + +positional arguments: + {add,exceptions,resolve,close,purge,timestamp-baseline,disclose,verify,dossier} + add Add a countersign block: field commitments, RFC 3161 timestamp, + countersignature + exceptions List Seals queued on a REFUTED or INDETERMINATE outcome + resolve Resolve an exception with a later verified Seal that links to it + close Close an exception with a disposition signed by a person whose key is + pinned + purge Export, then delete, closed exceptions past their retention period + timestamp-baseline Timestamp a baseline read before the executor runs + disclose Write a disclosure bundle revealing chosen committed fields + verify Check a Seal offline and show executor, verification tier, timestamps, and + disclosures + dossier Export one offline-verifiable HTML or zip for a dispute + +options: + -h, --help show this help message and exit +``` + +### `openadapt-flow countersign add` + +```text +usage: openadapt-flow countersign add [-h] -o OUT [--fields-file FIELDS_FILE] + [--tsa-url TSA_URL | --no-timestamp | --tsa-local DIR] + [--tsa-cert PEM] [--baseline-timestamp BASELINE_TIMESTAMP] + [--countersign-key COUNTERSIGN_KEY] [--vault VAULT] + [--keys KEYS] [--retention-days RETENTION_DAYS] + document + +positional arguments: + document Receipt, effect-receipt wire document, or report.json + +options: + -h, --help show this help message and exit + -o OUT, --out OUT Countersigned document to write (mode 0600) + --fields-file FIELDS_FILE + JSON object of sensitive fields to commit; values go to the vault only + --tsa-url TSA_URL RFC 3161 TSA URL (default: Sigstore's timestamp.sigstore.dev). Only a + 32-byte digest is sent + --no-timestamp Do not request a timestamp (offline use) + --tsa-local DIR Offline TSA keeping its key in DIR. Test only: its time is this machine's + clock + --tsa-cert PEM Pinned TSA certificate or CA the new token must verify against before it + is kept (repeatable; default: the bundled Sigstore TSA root) + --baseline-timestamp BASELINE_TIMESTAMP + Output of 'countersign timestamp-baseline' for this run + --countersign-key COUNTERSIGN_KEY + Ed25519 PEM for a local countersignature + --vault VAULT Vault directory (default $OPENADAPT_COUNTERSIGN_VAULT or + ~/.openadapt/countersign-vault) + --keys KEYS Pinned Seal key registry used to decide whether to queue an exception + --retention-days RETENTION_DAYS + Keep a closed exception this long (default 2190, minimum 183, or + $OPENADAPT_EXCEPTION_RETENTION_DAYS) +``` + +### `openadapt-flow countersign exceptions` + +```text +usage: openadapt-flow countersign exceptions [-h] + [--status {open,resolved_by_seal,closed_by_attestation,all}] + [--json] [--vault VAULT] + +options: + -h, --help show this help message and exit + --status {open,resolved_by_seal,closed_by_attestation,all} + --json + --vault VAULT +``` + +### `openadapt-flow countersign resolve` + +```text +usage: openadapt-flow countersign resolve [-h] --with DOCUMENT [--keys KEYS] [--tsa-cert PEM] + [--vault VAULT] + entry + +positional arguments: + entry receipt_sha256 of the open entry + +options: + -h, --help show this help message and exit + --with DOCUMENT Countersigned document of the later receipt (added with 'countersign add') + --keys KEYS Pinned Seal key file; the keys the entry was queued under + --tsa-cert PEM Pinned TSA certificate or CA (repeatable) + --vault VAULT +``` + +### `openadapt-flow countersign close` + +```text +usage: openadapt-flow countersign close [-h] --approver-id APPROVER_ID --role ROLE --code + {cancelled_by_requester,remediated_manually_in_sor,superseded_by_external_workflow,verified_manually_oracle_unavailable} + --method + {counterparty_confirmation,ehr_audit_log_review,sor_direct_inspection,task_cancelled_no_check_needed} + --external-reference EXTERNAL_REFERENCE + [--notes-file NOTES_FILE] --approver-key APPROVER_KEY + --approver-keys APPROVER_KEYS [--vault VAULT] + entry + +positional arguments: + entry receipt_sha256 of the open entry + +options: + -h, --help show this help message and exit + --approver-id APPROVER_ID + Opaque approver id (no name or email) + --role ROLE Approver role, for example nurse-manager + --code {cancelled_by_requester,remediated_manually_in_sor,superseded_by_external_workflow,verified_manually_oracle_unavailable} + --method {counterparty_confirmation,ehr_audit_log_review,sor_direct_inspection,task_cancelled_no_check_needed} + --external-reference EXTERNAL_REFERENCE + Ticket or case id; only its HMAC is recorded + --notes-file NOTES_FILE + Free-text notes; kept only in the vault (0600) + --approver-key APPROVER_KEY + The approver's Ed25519 PEM + --approver-keys APPROVER_KEYS + Pinned approver key registry; the signature must verify against it + --vault VAULT +``` + +### `openadapt-flow countersign purge` + +```text +usage: openadapt-flow countersign purge [-h] --export EXPORT [--vault VAULT] + +options: + -h, --help show this help message and exit + --export EXPORT Export file to write first (0600) + --vault VAULT +``` + +### `openadapt-flow countersign timestamp-baseline` + +```text +usage: openadapt-flow countersign timestamp-baseline [-h] + (--baseline BASELINE | --baseline-sha256 BASELINE_SHA256) + --read-at READ_AT --nonce NONCE -o OUT + [--tsa-url TSA_URL | --no-timestamp | --tsa-local DIR] + [--tsa-cert PEM] + +options: + -h, --help show this help message and exit + --baseline BASELINE Baseline file to hash locally (it is never sent) + --baseline-sha256 BASELINE_SHA256 + sha256: of the baseline + --read-at READ_AT baseline_read_at (RFC 3339) + --nonce NONCE The run nonce the baseline belongs to + -o OUT, --out OUT + --tsa-url TSA_URL RFC 3161 TSA URL (default: Sigstore's timestamp.sigstore.dev). Only a + 32-byte digest is sent + --no-timestamp Do not request a timestamp (offline use) + --tsa-local DIR Offline TSA keeping its key in DIR. Test only: its time is this machine's + clock + --tsa-cert PEM Pinned TSA certificate or CA the new token must verify against before it + is kept (repeatable; default: the bundled Sigstore TSA root) +``` + +### `openadapt-flow countersign disclose` + +```text +usage: openadapt-flow countersign disclose [-h] --field LABEL -o OUT [--vault VAULT] document + +positional arguments: + document Countersigned document + +options: + -h, --help show this help message and exit + --field LABEL Commitment label to reveal (repeatable) + -o OUT, --out OUT + --vault VAULT +``` + +### `openadapt-flow countersign verify` + +```text +usage: openadapt-flow countersign verify [-h] [--keys KEYS] [--tsa-cert PEM] [--disclosure JSON] + [--accept-non-flow] [--json] + document + +positional arguments: + document Countersigned document, receipt, report.json, or dossier .html + +options: + -h, --help show this help message and exit + --keys KEYS Pinned Seal key file (default: same as openadapt-agent seal) + --tsa-cert PEM Pinned TSA certificate or CA (repeatable; default: the bundled Sigstore TSA + root). Certificates inside the document are never trusted + --disclosure JSON Disclosure bundle to check (repeatable) + --accept-non-flow Exit 0 for a verified effect receipt from an agent, script, or person + --json +``` + +### `openadapt-flow countersign dossier` + +```text +usage: openadapt-flow countersign dossier [-h] -o OUT [--format {html,zip}] [--screenshot PNG] + [--keys KEYS] [--tsa-cert PEM] [--disclosure JSON] + [--accept-non-flow] [--vault VAULT] + document + +positional arguments: + document Countersigned document + +options: + -h, --help show this help message and exit + -o OUT, --out OUT Output .html or .zip + --format {html,zip} + --screenshot PNG Embedded only after automatic redaction; left out when no redaction + provider is installed + --keys KEYS Pinned Seal key file (default: same as openadapt-agent seal) + --tsa-cert PEM Pinned TSA certificate or CA (repeatable; default: the bundled Sigstore TSA + root). Certificates inside the document are never trusted + --disclosure JSON Disclosure bundle to check (repeatable) + --accept-non-flow Exit 0 for a verified effect receipt from an agent, script, or person + --vault VAULT Vault whose exception ledger the dossier includes +``` + ### `openadapt-flow bind-record-checks` ```text @@ -1474,12 +1971,29 @@ options: ### `openadapt-flow qualify` ```text -usage: openadapt-flow qualify [-h] - {schema,label,init,propose,accept,admit-local,from-demo,inspect,explain,report,set-identity,set-risk,trust-runner,set-effect,business-decision,judgment-cases,add-case,run,run-case,drills,add-requalification,certify} - ... +Qualify: before a compiled workflow may run unattended on a real system, run it against the normal case and deliberate faults, and confirm it is verified by reading back the system of record and halts on every fault. + +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... + +The faults are a duplicated save button, a relabeled save button, a blanked +screen, and a weak or missing check that the write happened. Certifying +(--certify) signs the result under a policy such as clinical-write; you get a +signed report and a certified bundle the runner will admit. A certificate +covers only the environment the workflow was qualified in. + +Definitions of qualify and certify: + https://docs.openadapt.ai/reference/glossary/#qualify +Certification policies (built-in clinical-write and permissive, or your own +YAML file passed to --certify): + https://docs.openadapt.ai/reference/glossary/#certification-policy +How to write verify.yaml: + https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/QUALIFY_A_WORKFLOW.md positional arguments: - {schema,label,init,propose,accept,admit-local,from-demo,inspect,explain,report,set-identity,set-risk,trust-runner,set-effect,business-decision,judgment-cases,add-case,run,run-case,drills,add-requalification,certify} + {campaign,schema,label,init,propose,accept,admit-local,from-demo,inspect,explain,report,set-identity,set-risk,trust-runner,set-effect,business-decision,judgment-cases,add-case,run,run-case,drills,add-requalification,certify} + campaign Qualify a bundle from verify.yaml (also the default form: `qualify + --verify verify.yaml`) schema Print the qualification-project JSON Schema label Optionally set, remove, or list presentation-only entity labels init Initialize a bundle's qualification project @@ -1518,10 +2032,58 @@ options: -h, --help show this help message and exit ``` +### `openadapt-flow qualify campaign` + +```text +Qualify: before a compiled workflow may run unattended on a real system, run it against the normal case and deliberate faults, and confirm it is verified by reading back the system of record and halts on every fault. + +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... campaign + [-h] --verify YAML [--cases CASE [CASE ...]] [--trials TRIALS] [--out DIR] + [--certify [POLICY]] [--run-params JSON] [--campaign-id CAMPAIGN_ID] + bundle + +The faults are a duplicated save button, a relabeled save button, a blanked +screen, and a weak or missing check that the write happened. Certifying +(--certify) signs the result under a policy such as clinical-write; you get a +signed report and a certified bundle the runner will admit. A certificate +covers only the environment the workflow was qualified in. + +Definitions of qualify and certify: + https://docs.openadapt.ai/reference/glossary/#qualify +Certification policies (built-in clinical-write and permissive, or your own +YAML file passed to --certify): + https://docs.openadapt.ai/reference/glossary/#certification-policy +How to write verify.yaml: + https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/QUALIFY_A_WORKFLOW.md + +positional arguments: + bundle Compiled workflow bundle directory + +options: + -h, --help show this help message and exit + --verify YAML verify.yaml: the per-workflow read-back, field mapping, save step and + inputs + --cases CASE [CASE ...] + Run only these cases (representative, fault-ambiguity, fault-wrong- + identity, fault-stale-identity, fault-weak-effect, fault-missing-effect). + Certification needs all six. + --trials TRIALS Trials per case (default: verify.yaml, 3) + --out DIR New output directory + --certify [POLICY] Certify under this policy (default: campaign.policy in verify.yaml, + usually clinical-write). See + https://docs.openadapt.ai/reference/glossary/#certification-policy. + --run-params JSON After certifying, run the certified bundle once with these inputs + --campaign-id CAMPAIGN_ID + Campaign id (default: verify.yaml or generated) +``` + ### `openadapt-flow qualify schema` ```text -usage: openadapt-flow qualify schema [-h] +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... schema + [-h] options: -h, --help show this help message and exit @@ -1530,7 +2092,9 @@ options: ### `openadapt-flow qualify label` ```text -usage: openadapt-flow qualify label [-h] {set,remove,list} ... +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... label + [-h] {set,remove,list} ... positional arguments: {set,remove,list} @@ -1545,8 +2109,9 @@ options: ### `openadapt-flow qualify label set` ```text -usage: openadapt-flow qualify label set [-h] --step STEP --label LABEL [--fallback {record,item}] - bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... label set + [-h] --step STEP --label LABEL [--fallback {record,item}] bundle positional arguments: bundle Workflow bundle directory @@ -1564,7 +2129,9 @@ options: ### `openadapt-flow qualify label remove` ```text -usage: openadapt-flow qualify label remove [-h] --step STEP bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... label remove + [-h] --step STEP bundle positional arguments: bundle Workflow bundle directory @@ -1577,7 +2144,9 @@ options: ### `openadapt-flow qualify label list` ```text -usage: openadapt-flow qualify label list [-h] bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... label list + [-h] bundle positional arguments: bundle Workflow bundle directory @@ -1589,13 +2158,13 @@ options: ### `openadapt-flow qualify init` ```text -usage: openadapt-flow qualify init [-h] --target {web,windows,macos,linux,rdp,citrix} - --application APPLICATION --application-version - APPLICATION_VERSION --environment-digest ENVIRONMENT_DIGEST - [--runtime-version RUNTIME_VERSION] - [--require-capability REQUIRE_CAPABILITY] - [--minimum-tier {1,2,3,4}] [--replace] - bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... init + [-h] --target {web,windows,macos,linux,rdp,citrix} --application APPLICATION + --application-version APPLICATION_VERSION --environment-digest ENVIRONMENT_DIGEST + [--runtime-version RUNTIME_VERSION] [--require-capability REQUIRE_CAPABILITY] + [--minimum-tier {1,2,3,4}] [--replace] + bundle positional arguments: bundle Workflow bundle directory @@ -1616,10 +2185,11 @@ options: ### `openadapt-flow qualify propose` ```text -usage: openadapt-flow qualify propose [-h] [--recording RECORDING] - [--policy-pack {community,cloud,regulated}] - [--proposer {off,llm}] [--out OUT] - bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... propose + [-h] [--recording RECORDING] [--policy-pack {community,cloud,regulated}] + [--proposer {off,llm}] [--out OUT] + bundle positional arguments: bundle Compiled workflow bundle directory @@ -1639,10 +2209,11 @@ options: ### `openadapt-flow qualify accept` ```text -usage: openadapt-flow qualify accept [-h] --proposal PROPOSAL - [--refuse-pin {application,environment,identity,effect}] - [--admit-local] [--replace] [--out OUT] - bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... accept + [-h] --proposal PROPOSAL [--refuse-pin {application,environment,identity,effect}] + [--admit-local] [--replace] [--out OUT] + bundle positional arguments: bundle Compiled workflow bundle directory @@ -1660,7 +2231,9 @@ options: ### `openadapt-flow qualify admit-local` ```text -usage: openadapt-flow qualify admit-local [-h] --proposal PROPOSAL [--out OUT] bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... admit-local + [-h] --proposal PROPOSAL [--out OUT] bundle positional arguments: bundle Compiled workflow bundle directory @@ -1674,10 +2247,11 @@ options: ### `openadapt-flow qualify from-demo` ```text -usage: openadapt-flow qualify from-demo [-h] --recording RECORDING - [--policy-pack {community,cloud,regulated}] - [--admit-local] [--proposer {off,llm}] [--out OUT] - bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... from-demo + [-h] --recording RECORDING [--policy-pack {community,cloud,regulated}] [--admit-local] + [--proposer {off,llm}] [--out OUT] + bundle positional arguments: bundle Compiled workflow bundle directory @@ -1695,9 +2269,9 @@ options: ### `openadapt-flow qualify inspect` ```text -usage: openadapt-flow qualify inspect [-h] [--policy POLICY] [--evidence-root EVIDENCE_ROOT] - [--json] - bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... inspect + [-h] [--policy POLICY] [--evidence-root EVIDENCE_ROOT] [--json] bundle positional arguments: bundle Workflow bundle directory @@ -1712,9 +2286,9 @@ options: ### `openadapt-flow qualify explain` ```text -usage: openadapt-flow qualify explain [-h] [--policy POLICY] [--evidence-root EVIDENCE_ROOT] - [--json] - bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... explain + [-h] [--policy POLICY] [--evidence-root EVIDENCE_ROOT] [--json] bundle positional arguments: bundle Workflow bundle directory @@ -1729,9 +2303,9 @@ options: ### `openadapt-flow qualify report` ```text -usage: openadapt-flow qualify report [-h] [--policy POLICY] [--evidence-root EVIDENCE_ROOT] - [--json] - bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... report + [-h] [--policy POLICY] [--evidence-root EVIDENCE_ROOT] [--json] bundle positional arguments: bundle Workflow bundle directory @@ -1746,12 +2320,12 @@ options: ### `openadapt-flow qualify set-identity` ```text -usage: openadapt-flow qualify set-identity [-h] --step STEP [--canonical-ladder] - [--signal KEY=SOURCE:MODE[:NORMALIZER,...]] - [--signal-region KEY=X,Y,W,H] - [--signal-param KEY=PARAM] [--signal-extract KEY=REGEX] - [--signal-expected KEY=VALUE] [--quorum QUORUM] - bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... set-identity + [-h] --step STEP [--canonical-ladder] [--signal KEY=SOURCE:MODE[:NORMALIZER,...]] + [--signal-region KEY=X,Y,W,H] [--signal-param KEY=PARAM] [--signal-extract KEY=REGEX] + [--signal-expected KEY=VALUE] [--quorum QUORUM] + bundle positional arguments: bundle Workflow bundle directory @@ -1777,10 +2351,11 @@ options: ### `openadapt-flow qualify set-risk` ```text -usage: openadapt-flow qualify set-risk [-h] --step STEP --classification - {read_only,state_changing,consequential,irreversible} - --explanation EXPLANATION - bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... set-risk + [-h] --step STEP --classification {read_only,state_changing,consequential,irreversible} + --explanation EXPLANATION + bundle positional arguments: bundle Workflow bundle directory @@ -1795,7 +2370,9 @@ options: ### `openadapt-flow qualify trust-runner` ```text -usage: openadapt-flow qualify trust-runner [-h] --key-id KEY_ID --public-key PUBLIC_KEY bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... trust-runner + [-h] --key-id KEY_ID --public-key PUBLIC_KEY bundle positional arguments: bundle Workflow bundle directory @@ -1810,9 +2387,9 @@ options: ### `openadapt-flow qualify set-effect` ```text -usage: openadapt-flow qualify set-effect [-h] --step STEP --effect-index EFFECT_INDEX - [--path {gui,api}] --tier {1,2,3,4} - bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... set-effect + [-h] --step STEP --effect-index EFFECT_INDEX [--path {gui,api}] --tier {1,2,3,4} bundle positional arguments: bundle Workflow bundle directory @@ -1828,7 +2405,9 @@ options: ### `openadapt-flow qualify business-decision` ```text -usage: openadapt-flow qualify business-decision [-h] [--input JSON] [--check] bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... business-decision + [-h] [--input JSON] [--check] bundle positional arguments: bundle Workflow bundle directory @@ -1843,7 +2422,9 @@ options: ### `openadapt-flow qualify judgment-cases` ```text -usage: openadapt-flow qualify judgment-cases [-h] [--input JSON] [--check] bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... judgment-cases + [-h] [--input JSON] [--check] bundle positional arguments: bundle Workflow bundle directory @@ -1857,13 +2438,13 @@ options: ### `openadapt-flow qualify add-case` ```text -usage: openadapt-flow qualify add-case [-h] --case-id CASE_ID --kind - {representative,ambiguity,wrong_identity,stale_identity,weak_effect,missing_effect} - --expected-outcome - {verified,completed_unverified,halted,failed,rolled_back} - [--description DESCRIPTION] [--input-ref INPUT_REF] - [--optional] - bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... add-case + [-h] --case-id CASE_ID --kind + {representative,ambiguity,wrong_identity,stale_identity,weak_effect,missing_effect} + --expected-outcome {verified,completed_unverified,halted,failed,rolled_back} + [--description DESCRIPTION] [--input-ref INPUT_REF] [--optional] + bundle positional arguments: bundle Workflow bundle directory @@ -1881,7 +2462,9 @@ options: ### `openadapt-flow qualify run` ```text -usage: openadapt-flow qualify run [-h] --results JSON --evidence-root EVIDENCE_ROOT bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... run + [-h] --results JSON --evidence-root EVIDENCE_ROOT bundle positional arguments: bundle Workflow bundle directory @@ -1895,26 +2478,21 @@ options: ### `openadapt-flow qualify run-case` ```text -usage: openadapt-flow qualify run-case [-h] --case-id CASE_ID --inputs JSON --campaign-id - CAMPAIGN_ID --run-id RUN_ID - [--qualification-campaign-authority-file PATH] --run-dir - RUN_DIR [--url URL] [--headed] [--dry-run] - [--fault-driver NAME] - [--backend {web,windows,macos,linux,rdp,citrix}] - [--agent-url URL] [--macos-app APP] - [--macos-window-title TITLE] [--linux-app APP] - [--linux-window-title TITLE] [--linux-allow-physical-input] - [--rdp-host HOST] [--rdp-window OWNER] - [--rdp-window-title TITLE] [--rdp-readiness-text TEXT] - [--allow-surface-override] [--browser-cdp-endpoint URL] - [--browser-page-url URL] [--config YAML] - [--effects-kind {none,rest,fhir,sql,file,document-hash}] - [--effects-base-url EFFECTS_BASE_URL] - [--effects-root EFFECTS_ROOT] [--api-actuator] - [--api-base-url API_BASE_URL] [--durable] - [--approval-mode {each_item,problems_only,not_gated}] - [--approval-wait SECONDS] - bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... run-case + [-h] --case-id CASE_ID --inputs JSON --campaign-id CAMPAIGN_ID --run-id RUN_ID + [--qualification-campaign-authority-file PATH] --run-dir RUN_DIR [--url URL] [--headed] + [--dry-run] [--fault-driver NAME] [--backend {web,windows,macos,linux,rdp,citrix}] + [--agent-url URL] [--macos-app APP] [--macos-window-title TITLE] [--linux-app APP] + [--linux-window-title TITLE] [--linux-allow-physical-input] [--rdp-host HOST] + [--rdp-window OWNER] [--rdp-window-title TITLE] [--rdp-readiness-text TEXT] + [--allow-surface-override] [--browser-cdp-endpoint URL] [--browser-page-url URL] + [--config YAML] [--effects-kind {none,rest,fhir,sql,file,document-hash}] + [--effects-base-url EFFECTS_BASE_URL] [--effects-root EFFECTS_ROOT] [--api-actuator] + [--api-base-url API_BASE_URL] [--durable] [--approver-policy FILE] [--approver-webhook URL] + [--approver-webhook-id APPROVER_WEBHOOK_ID] [--approver-webhook-kind {agent,system}] + [--approval-mode {each_item,problems_only,not_gated}] [--approval-wait SECONDS] + bundle positional arguments: bundle Sealed workflow bundle directory @@ -2016,6 +2594,21 @@ options: --durable Enable the Tier-3 durable runtime: checkpoint each verified step and durably PAUSE on halt, so the run is resumable via `resume` (never re- performing a confirmed write) + --approver-policy FILE + An openadapt.approver-policy/v1 JSON file: which kinds of approver + (person, rule, agent, system) may answer each class of save. Without it + only a person may answer. A clinical_write save takes a non-person + approver only when the policy pins an opt-in. + --approver-webhook URL + Ask this https endpoint (the customer's approver agent or system) about + each waiting save, with HMAC-signed, PHI-free messages. The secret is read + from OPENADAPT_APPROVER_WEBHOOK_SECRET. A person can still answer in + Desktop. Needs --approver-webhook-id and a policy that allows the + approver's kind. + --approver-webhook-id APPROVER_WEBHOOK_ID + The webhook approver's opaque id, such as system:acme-approvals + --approver-webhook-kind {agent,system} + Whether the webhook approver is an agent or a system (default system) --approval-mode {each_item,problems_only,not_gated} Per-item approval before each consequential save. each_item (the default for every run): the run waits before every save until a person approves it @@ -2033,8 +2626,9 @@ options: ### `openadapt-flow qualify drills` ```text -usage: openadapt-flow qualify drills [-h] [--from-case FROM_CASE] [--save-step SAVE_STEP] [--json] - bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... drills + [-h] [--from-case FROM_CASE] [--save-step SAVE_STEP] [--json] bundle positional arguments: bundle Sealed workflow bundle directory @@ -2051,10 +2645,12 @@ options: ### `openadapt-flow qualify add-requalification` ```text -usage: openadapt-flow qualify add-requalification [-h] --kind - {workflow_changed,application_version_changed,environment_changed,identity_policy_changed,effect_policy_changed,runtime_version_changed,expiry,operator_requested} - [--description DESCRIPTION] - bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... add-requalification + [-h] --kind + {workflow_changed,application_version_changed,environment_changed,identity_policy_changed,effect_policy_changed,runtime_version_changed,expiry,operator_requested} + [--description DESCRIPTION] + bundle positional arguments: bundle Workflow bundle directory @@ -2068,9 +2664,11 @@ options: ### `openadapt-flow qualify certify` ```text -usage: openadapt-flow qualify certify [-h] [--policy POLICY] [--profile {standard,regulated}] - --evidence-root EVIDENCE_ROOT [--json] - bundle +usage: openadapt-flow qualify --verify verify.yaml [--cases ...] [--out DIR] [--certify [POLICY]] [--run-params FILE] + openadapt-flow qualify ... certify + [-h] [--policy POLICY] [--profile {standard,regulated}] --evidence-root EVIDENCE_ROOT + [--json] + bundle positional arguments: bundle Workflow bundle directory @@ -2956,6 +3554,217 @@ options: --check Exit 1 unless Flow currently holds a live admission ``` +### `openadapt-flow check` + +```text +usage: openadapt-flow check [-h] {mark,show,admit} ... + +positional arguments: + {mark,show,admit} + mark Write check.json: where the check starts and what it reads + show List a bundle's demonstrated checks + admit Admit a bundle's demonstrated checks (a person, at a terminal) + +options: + -h, --help show this help message and exit +``` + +### `openadapt-flow check mark` + +```text +usage: openadapt-flow check mark [-h] --start-event START_EVENT --field PARAM=X,Y,W,H + [--kind PARAM=KIND] [--date-order PARAM=dmy|mdy] + [--reset KIND@EVENT] + recording + +positional arguments: + recording Recording directory + +options: + -h, --help show this help message and exit + --start-event START_EVENT + Index of the first recorded event of the check + --field PARAM=X,Y,W,H + A field that shows the value typed for PARAM, in frame pixels + --kind PARAM=KIND Compare PARAM as text, code, date or currency (default: inferred) + --date-order PARAM=dmy|mdy + Order of a numeric date field + --reset KIND@EVENT Name the state reset when Flow can't recognize it: reload, new_search, + reopen_window or fresh_session, at a recorded event +``` + +### `openadapt-flow check show` + +```text +usage: openadapt-flow check show [-h] [--json] bundle + +positional arguments: + bundle Bundle directory + +options: + -h, --help show this help message and exit + --json +``` + +### `openadapt-flow check admit` + +```text +usage: openadapt-flow check admit [-h] [--step STEP] [--by BY] bundle + +positional arguments: + bundle Bundle directory + +options: + -h, --help show this help message and exit + --step STEP Admit only this save step + --by BY Your name (default: the login name) +``` + +### `openadapt-flow jit` + +```text +usage: openadapt-flow jit [-h] {status,review,admit,reject,keygen,register,sign-policy} ... + +positional arguments: + {status,review,admit,reject,keygen,register,sign-policy} + status Print the state, steps and what admission needs + review Confirm the parameters, then record that you reviewed every step + admit Admit a draft whose canary passed: with a registered key, as an agent + under a signed policy, or as a person at a terminal + reject Set the draft aside + keygen Write a new encrypted Ed25519 key + register Register a key as a policy signer, admitter key or agent + sign-policy Sign an admission policy with your policy-signer key + +options: + -h, --help show this help message and exit +``` + +### `openadapt-flow jit status` + +```text +usage: openadapt-flow jit status [-h] [--key-dir KEY_DIR] [program] + +positional arguments: + program The task's directory or its program/ directory + +options: + -h, --help show this help message and exit + --key-dir KEY_DIR Admission signing key directory (default: $OPENADAPT_JIT_KEY_DIR or + ~/.openadapt/jit) +``` + +### `openadapt-flow jit review` + +```text +usage: openadapt-flow jit review [-h] [--key-dir KEY_DIR] [--principal PRINCIPAL] + [--queue CACHE_DIR] [--accept-param ACCEPT_PARAM] + [--step STEP | --all-steps-shown] + [program] + +positional arguments: + program The task's directory or its program/ directory + +options: + -h, --help show this help message and exit + --key-dir KEY_DIR Admission signing key directory (default: $OPENADAPT_JIT_KEY_DIR or + ~/.openadapt/jit) + --principal PRINCIPAL + Your name or user id + --queue CACHE_DIR List the drafts waiting for a person (needs_person) under CACHE_DIR + --accept-param ACCEPT_PARAM + A task parameter to confirm (repeat; must be all of them). Applied before + the steps are shown, because it recompiles the program + --step STEP A step id you reviewed (repeat) + --all-steps-shown You reviewed every step this command prints +``` + +### `openadapt-flow jit admit` + +```text +usage: openadapt-flow jit admit [-h] [--key-dir KEY_DIR] [--principal PRINCIPAL] [--key KEY] + [--policy POLICY] [--require-distinct] + [program] + +positional arguments: + program The task's directory or its program/ directory + +options: + -h, --help show this help message and exit + --key-dir KEY_DIR Admission signing key directory (default: $OPENADAPT_JIT_KEY_DIR or + ~/.openadapt/jit) + --principal PRINCIPAL + A person admitting at a terminal + --key KEY Encrypted admitter or agent key; passphrase from + $OPENADAPT_JIT_KEY_PASSPHRASE. The principal comes from the key + --policy POLICY Signed admission policy, for an agent key + --require-distinct Refuse if you also reviewed the draft +``` + +### `openadapt-flow jit reject` + +```text +usage: openadapt-flow jit reject [-h] [--key-dir KEY_DIR] [--principal PRINCIPAL] + [--reason REASON] + [program] + +positional arguments: + program The task's directory or its program/ directory + +options: + -h, --help show this help message and exit + --key-dir KEY_DIR Admission signing key directory (default: $OPENADAPT_JIT_KEY_DIR or + ~/.openadapt/jit) + --principal PRINCIPAL + Your name or user id + --reason REASON +``` + +### `openadapt-flow jit keygen` + +```text +usage: openadapt-flow jit keygen [-h] [--key-dir KEY_DIR] --out OUT + +options: + -h, --help show this help message and exit + --key-dir KEY_DIR Admission signing key directory (default: $OPENADAPT_JIT_KEY_DIR or + ~/.openadapt/jit) + --out OUT +``` + +### `openadapt-flow jit register` + +```text +usage: openadapt-flow jit register [-h] [--key-dir KEY_DIR] --role + {policy_signer,admitter_key,agent} --principal PRINCIPAL + --public-key PUBLIC_KEY + +options: + -h, --help show this help message and exit + --key-dir KEY_DIR Admission signing key directory (default: $OPENADAPT_JIT_KEY_DIR or + ~/.openadapt/jit) + --role {policy_signer,admitter_key,agent} + --principal PRINCIPAL + --public-key PUBLIC_KEY + hex, from jit keygen +``` + +### `openadapt-flow jit sign-policy` + +```text +usage: openadapt-flow jit sign-policy [-h] [--key-dir KEY_DIR] --key KEY --out OUT policy + +positional arguments: + policy Unsigned AdmissionPolicy JSON + +options: + -h, --help show this help message and exit + --key-dir KEY_DIR Admission signing key directory (default: $OPENADAPT_JIT_KEY_DIR or + ~/.openadapt/jit) + --key KEY Your encrypted policy-signer key + --out OUT +``` + ## openadapt agent ### `openadapt-agent` @@ -2986,15 +3795,19 @@ options: ```text usage: openadapt-agent serve [-h] [--mode {sandbox,attended,production}] [--bundles BUNDLES] - [--sandbox-engine {auto,flow,simulated}] [--authoring] [--tutorial] + [--sandbox-engine {auto,flow,simulated}] [--authoring] + [--windows-agent-url URL] [--remote-window APP] + [--remote-window-title TITLE] [--citrix] + [--authoring-screens {off,scrubbed,unscrubbed}] [--tutorial] [--allow-run] [--allow-protected-export] [--allow-synthetic-recorded-defaults] [--allow-attended-actions] [--url URL] [--config YAML] [--policy NAME-OR-PATH] [--runs-dir RUNS_DIR] [--timeout SECONDS] [--approval-mode {each_item,problems_only,not_gated}] - [--allow-url-override] [--flow-cli FLOW_CLI] [--headed] - [--allow-model-grounding] [--extra-run-arg ARG] - [--repair-access FILE] [--repair-dir DIR] [--hold-on-halt SECONDS] + [--delegate-approvals AGENT_ID] [--allow-url-override] + [--flow-cli FLOW_CLI] [--headed] [--allow-model-grounding] + [--extra-run-arg ARG] [--repair-access FILE] [--repair-dir DIR] + [--hold-on-halt SECONDS] options: -h, --help show this help message and exit @@ -3018,6 +3831,24 @@ options: Chrome window after login (macOS; no DOM identity). Local stdio may also type through the recorder; hosted MCP remains pause-only. Does not enable run tools. This process stays stdio and must not be served over HTTP. + --windows-agent-url URL + With --authoring: drive a native Windows app through the Flow Windows + agent server running on the Windows computer (for example + http://localhost:5001, or an SSH-forwarded port from another computer). + The agent's input goes through the same synthetic-input path replay uses. + The token comes from OAFLOW_AGENT_TOKEN. + --remote-window APP With --authoring: drive the RDP or Citrix session shown in this local + client window (app name, for example 'Microsoft Remote Desktop'). Screen- + only: the agent clicks pixels and presses keys. + --remote-window-title TITLE + With --remote-window or --citrix: pick one window by its title. + --citrix With --authoring: drive the local Citrix Workspace window (--remote-window + overrides the app name). Screen-only. + --authoring-screens {off,scrubbed,unscrubbed} + With --authoring: let the agent see the pinned window through the screen + tool, so it can drive a native Windows app or a remote session from + pixels. scrubbed removes identifiers first and shares nothing if that + fails. Default off. --tutorial Older name for --mode sandbox. Synthetic only. Cannot be combined with --bundles, --url, or --config. --allow-run Older name for --mode production (with --bundles) or --mode sandbox @@ -3047,11 +3878,18 @@ options: --timeout SECONDS Per-call timeout for each governed run (default: 600) --approval-mode {each_item,problems_only,not_gated} Per-save approval passed to every `openadapt-flow run`. Unset (the - default): Flow decides, and under the Standard and Regulated profiles a - person approves each save (each_item); a run waiting for that approval + default): Flow decides, and under the Standard and Regulated profiles each + save waits for approval (each_item); a run waiting for that approval reports needs_review. not_gated turns the pause off for every run this server starts, and each run's result and Flow's report record that. Callers can't change it. + --delegate-approvals AGENT_ID + Let the calling agent answer saves that wait for an approval, as the + opaque id AGENT_ID (for example agent:intake-bot). Adds the + list_approval_requests and answer_approval_request tools. Flow accepts the + agent's answer only where the run's approver policy (openadapt-flow run + --approver-policy) allows an agent for that step; by default only a person + may answer. Flow records the agent as the approver. --allow-url-override Permit MCP callers to pass a per-call `url` argument (default: the target URL is fixed by --url/--config at server start) --flow-cli FLOW_CLI Command used to invoke the flow CLI (default: this interpreter's `python diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 793f7fc7..d2d90f8c 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -1,7 +1,7 @@ --- description: >- Every openadapt and openadapt flow command in openadapt 1.17.0 and - openadapt-flow 2.1.0, what it does, and its main options. + openadapt-flow 2.2.0, what it does, and its main options. --- # Command reference @@ -13,7 +13,7 @@ commands of its own and passes two groups through to the engine: - `openadapt agent ...` runs `openadapt-agent`, the MCP server. Every command takes `--help`, which is the authority for its options. This -page lists the commands in `openadapt` 1.17.0 and `openadapt-flow` 2.1.0. +page lists the commands in `openadapt` 1.17.0 and `openadapt-flow` 2.2.0. [Command help](cli-help.md) has the full `--help` text of every command. ## Launcher commands @@ -47,10 +47,13 @@ page lists the commands in `openadapt` 1.17.0 and `openadapt-flow` 2.1.0. | [`visualize`](#inspect) | Draws a bundle as HTML, Mermaid, or JSON. | | [`lint`](#check) | Lists what the program doesn't cover yet. | | [`scaffold-verifier`](#check) | Drafts record checks from a recording or bundle, for a person to complete. | +| [`check`](#check) | Records how a person would check a save, then admits that check for a bundle. | | [`bind-record-checks`](#check) | Attaches reviewed record checks to a compiled bundle, in a new bundle. | | [`certify`](#check) | Checks a bundle against a policy. | | [`qualify`](#check) | Manages and runs a bundle's qualification against normal and fault cases. | | [`propose-qualification`](#check) | Same as `qualify propose`. | +| [`verify-run`](#check) | Reads the system of record and rules on a save that an agent, a script, or a person reported. | +| [`countersign`](#check) | Timestamps, countersigns, and discloses fields of a Seal, and exports a dossier. | | [`seal`](#check) | Writes an encrypted copy of a bundle using `OPENADAPT_BUNDLE_KEY`. | | [`admit`](#check) | Reads the public release-admission list. It never signs anything. | | [`replay`](#run) | Performs every step of a bundle against a real app. Never checks the record. | @@ -59,14 +62,17 @@ page lists the commands in `openadapt` 1.17.0 and `openadapt-flow` 2.1.0. | [`approve`](#run) | Records a decision that lets a paused run resume. | | [`item-approval`](#run) | Lists, approves, or declines the saves a run is waiting on. | | [`explain`](#inspect) | Explains a finished run in plain words, with the next command. | +| [`triage`](#run) | Orders verified saves for review and proposes one batch approval. | | [`attach-seal`](#run) | Adds Cloud's signed receipt to a `VERIFIED` run report. | | [`bench`](#inspect) | Replays a bundle many times against MockMed and summarizes. | | [`benchmark`](#inspect) | Compares compiled replay with a computer-use agent on MockMed. The agent side needs an Anthropic API key and costs money. | | [`teach`](#repair) | Turns a fix demonstration for a halted run into a guarded branch. | | [`teach-branch`](#repair) | Learns a guarded branch from two or more halts fixed the same way. | | [`repair`](#repair) | Moves a repaired bundle through review, campaigns, approval, staging, canary, and rollback. | +| [`jit`](#agents) | Reviews, admits, or rejects a draft bundle compiled from a computer-use agent's run. | | [`emit-skill`](#agents) | Writes an Agent Skill for a bundle. | | [`emit-mcp`](#agents) | Writes a standalone MCP `server.py` for a bundle. | +| [`pull`](#cloud) | Downloads a packaged workflow bundle by name. | | [`connect`](#cloud) | Claims a pairing and stores the workspace credential in the keychain. | | [`login`](#cloud) | Checks an ingest token and stores it in the keychain. | | [`sanitize`](#cloud) | Writes a redacted copy of a recording or bundle. Never changes the original. | @@ -135,16 +141,22 @@ values, selectors, and URLs from the view. openadapt flow lint bundle [--strict] openadapt flow scaffold-verifier rec [-o DIR] openadapt flow bind-record-checks bundle --binding FILE (--check | -o OUT) +openadapt flow check {mark,show,admit} +openadapt flow verify-run --verify verify.yaml --params task.json --run-record RUN.json --baseline before.json --out DIR +openadapt flow countersign {add,verify,disclose,dossier,timestamp-baseline,exceptions,resolve,close,purge} openadapt flow certify bundle [--policy clinical-write|permissive|FILE] [--config deploy.yaml] openadapt flow seal bundle -o bundle-sealed ``` -`qualify` subcommands in 2.1.0: `schema`, `label`, `init`, `propose`, +`qualify bundle --verify verify.yaml` runs a whole campaign (`qualify +campaign`). The other `qualify` subcommands: `schema`, `label`, `init`, `propose`, `accept`, `admit-local`, `from-demo`, `inspect`, `explain`, `report`, `set-identity`, `set-risk`, `trust-runner`, `set-effect`, `business-decision`, `judgment-cases`, `add-case`, `run`, `run-case`, `drills`, `add-requalification`, and `certify`. See -[Certify and qualify a workflow](../how-to/certify-and-qualify.md). +[Certify and qualify a workflow](../how-to/certify-and-qualify.md), +[Add record checks](../how-to/record-checks.md), and +[Receipts, Seals, and countersigning](receipts-and-seals.md). ## Run @@ -155,11 +167,12 @@ openadapt flow resume RUN_DIR [--require-approval] openadapt flow approve RUN_DIR [--approver ID] [--resolution OPTION] [--authorize-uncertain-retry] openadapt flow item-approval show RUN_DIR openadapt flow attach-seal RUN_DIR --execution-id ID +openadapt flow triage {plan,approve-batch,decide,calibrate,estimate,eligibility} ``` Main `run` options: `--profile`, `--config`, `--param`, `--params-file`, `--worklist`, `--policy`, `--durable`, `--dry-run`, `--approval-mode`, -`--approval-wait`, `--effects-kind`, `--effects-base-url`, +`--approval-wait`, `--approver-policy`, `--approver-webhook`, `--effects-kind`, `--effects-base-url`, `--allow-model-grounding`, `--hold-on-halt`, `--browser-cdp-endpoint`, `--cloud-execution-id`, and the surface options. See [Run a workflow](../how-to/run.md). @@ -181,6 +194,7 @@ openadapt flow emit-skill bundle --out DIR openadapt flow emit-mcp bundle --out server.py openadapt agent serve [--mode sandbox|attended|production] [--bundles DIR] [--runs-dir DIR] [--config YAML] openadapt agent emit-skill bundle --out DIR [--include-bundle] +openadapt flow jit {status,review,admit,reject,keygen,register,sign-policy} ``` See [Connect an agent](../how-to/connect-an-agent.md). @@ -192,6 +206,7 @@ openadapt flow sanitize PATH --kind recording|bundle --out DIR openadapt flow review-sanitized DIR --original PATH openadapt flow approve-sanitized DIR --original PATH --reviewer ID openadapt flow push DIR --kind recording|bundle [--validation-attestation FILE] +openadapt flow pull WORKFLOW [--out DIR] [--list] ``` See [Deploy](../how-to/deploy.md#openadapt-cloud). diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md index c251967d..2c8e8e61 100644 --- a/docs/reference/configuration.md +++ b/docs/reference/configuration.md @@ -23,7 +23,9 @@ python -m openadapt_flow.cli_config init bundle --out deploy.yaml `unresolved` is filled. The complete annotated example is [`deployment.example.yaml`](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/deployment.example.yaml). -Credentials are always the names of environment variables, never values. +Credentials are names of environment variables, except the three backend +fields marked below, which hold values. Keep a file that sets them as private +as the credentials. ### `backend` @@ -36,9 +38,23 @@ backend: # kind: web | windows | macos | linux | rdp | citrix ``` -The surface fields match the command-line surface options in the -[command reference](cli.md#surface-options), for example `macos_app`, -`linux_app`, `rdp_window`, and `browser_cdp_endpoint`. +| Field | For | Meaning | +|---|---|---| +| `kind` | all | `web` (default), `windows`, `macos`, `linux`, `rdp`, or `citrix` | +| `url`, `headed` | `web` | The app's address, and whether the browser is visible (default `false`) | +| `browser_cdp_endpoint` | `web` | A loopback DevTools address of a running, signed-in Chrome, such as `http://127.0.0.1:9222`. The run attaches to its one tab on the `url` origin and detaches afterwards. | +| `browser_page_url` | `web` | The exact address of the tab to bind when more than one is on the origin | +| `agent_url` | `windows` | The Windows agent's address. Required. | +| `agent_token` | `windows` | The agent's shared token. A value. | +| `agent_tls_pin` | `windows` | SHA-256 fingerprint of the agent's TLS certificate. Any other certificate fails the connection. | +| `macos_app`, `macos_window_title` | `macos` | The owning app (required), and a title that picks one window. Ambiguous matches are refused. | +| `linux_app`, `linux_window_title` | `linux` | The exact accessibility app name and window title. Both required. | +| `linux_allow_physical_input` | `linux` | Allow pointer and keyboard input bound to the window when the target has no accessibility action (default `false`) | +| `rdp_host`, `rdp_port`, `rdp_domain` | `rdp` | A network RDP session (port default `3389`) | +| `rdp_username`, `rdp_password` | `rdp` | The session's sign-in. Values. | + +The same fields are command-line options; see the +[command reference](cli.md#surface-options). #### Remote sessions and Citrix @@ -53,6 +69,8 @@ fields bind the session and guard each input: | `rdp_readiness_text` | none | Stable text that must be on screen before input, such as a menu label. | | `rdp_readiness_min_ratio` | `0.85` | How closely the text read from the screen must match the readiness text, from `0.0` to `1.0`. | | `rdp_host`, `rdp_port` (`3389`) | none | A network RDP session instead of a local window. Only for `kind: rdp`. | +| `rdp_application_marker`, `rdp_application_version_marker`, `rdp_environment_marker`, `rdp_session_marker` | none | Stable text read from the session for qualification, such as the application name and version. Compared with the qualified environment, not kept in evidence. | +| `remote_frame_contract` | none | Regions of the session's screen that change on their own, such as a clock, left out when OpenAdapt checks that the screen has settled | Citrix is its own backend, not an alias for RDP. `kind: citrix` makes Flow build `CitrixWorkspaceBackend`, which binds the local Workspace or Viewer @@ -68,6 +86,22 @@ backend: rdp_readiness_min_ratio: 0.90 ``` +A `remote_frame_contract` names the exact frame size and its regions as +`[x, y, width, height]`: + +```yaml +backend: + remote_frame_contract: + schema_version: openadapt.remote-frame-contract/v1 + frame_width: 1920 + frame_height: 1080 + volatile_regions: [[1780, 1040, 140, 40]] # the session clock + protected_regions: [] # never masked +``` + +A size that doesn't match the live frame, or a changing region that overlaps +a target, an identity check, or a record check, stops the run before input. + `kind: citrix` rejects `rdp_host`; use `kind: rdp` for a network session. Governed Citrix `run` and `resume` require a nonblank `rdp_readiness_text` and refuse before any input without one. Choose text from the application's @@ -103,6 +137,8 @@ Where record checks read the saved record. `kind` is one of: | `document` | A report file whose contents are parsed | `root`, `file_pattern`, `document_format`, `document_field_paths` | | `document-hash` | A document store, by exact SHA-256 | `root`, `glob` | | `ui_readback` | The app's record page in a separate, signed-in, read-only browser session | `ui_readback.record_url`, `sign_in`, `row_selector`, `fields`, `identity` | +| `onscreen` | The saved value on the acting screen, read with OCR | `readback_region`, `readback_min_ratio` (default `0.9`) | +| a plugin kind | Whatever the installed verifier reads | `options`, `option_params`, `secret_env` | `auth` takes exactly one of `bearer_env`, `header` with `value_env`, or `basic_env`. Values that come from run inputs are written @@ -110,9 +146,41 @@ Where record checks read the saved record. `kind` is one of: `evidence_redact_fields` or `evidence_keep_fields` to keep field values out of the evidence, and `timeout_s` and `poll_interval_s`. +A plugin's `options` hold plain settings; a key that names a credential, +such as `password`, is refused. Put credentials in `secret_env`, which maps +a slot name to an environment variable name. + +To offer more than one read, list them under `candidates` instead of `kind`. +OpenAdapt builds every candidate before input and uses the strongest one; it +never switches to another after the save: + +```yaml +effects: + candidates: + - kind: file + root: /exports/claims + file_pattern: "claim-*.json" + - kind: document + root: /exports/reports + file_pattern: "*.json" + document_format: json +``` + Which kinds count as strong enough for each profile is explained in [How a save is checked](../explanation/verification.md). +### `identity` and `idempotency` + +```yaml +identity: + require_fresh_frame: true # check identity on a fresh screen capture + record_id_env: OPENADAPT_RECORD_ID # the variable that holds the record id +idempotency: + key_env: OPENADAPT_IDEMPOTENCY_KEY # the variable that holds the run's idempotency key +``` + +Both name environment variables; the file never holds a record id or a key. + ### `runtime` ```yaml @@ -120,6 +188,8 @@ runtime: profile: regulated # demo | standard | regulated durable: true # on by default under standard and regulated approval_mode: each_item # each_item | problems_only | not_gated + require_settled: false # forced on under standard and regulated + layout_drift_check: null # null: on for rdp and citrix, off elsewhere allow_model_grounding: false pixel_verify_enabled: false grounding_model: @@ -137,6 +207,11 @@ runtime: - When patient-data mode is on, a model endpoint is refused unless its host is in `phi_grounding_allowlist`. Public model APIs stay blocked unless `phi_egress_attested` is true. +- `require_settled` waits for a settled screen before every action. +- `layout_drift_check` compares the screen's resolution, fonts, and layout + with the recording before each recorded click, and stops the run when they + differ, for example `font_size_changed` or `landmark_missing`. Bundles + compiled before 2.2.0 carry no fingerprint and need recompiling to use it. - `pixel_verify_enabled` lets the pixel right-record check confirm a match, not only refuse one. Leave it off unless that check was validated for your screens. @@ -157,7 +232,8 @@ enabled with both ids, the attended console started with `--remote-decisions` relays a signed decision task with no screenshot and no patient data to OpenAdapt Cloud, where a signed-in member answers it from a phone. `context_tier` sets how much the task says about why the run stopped; neither -value can carry an image. See [Answer a halt from a phone](../how-to/answer-from-a-phone.md). +value can carry an image. `peer_task_schemas` lists the task versions the +hosted side accepts. See [Answer a halt from a phone](../how-to/answer-from-a-phone.md). ### `policy` @@ -174,11 +250,20 @@ policy: | `OPENADAPT_BUNDLE_KEY` | The key for encrypted bundles and checkpoints; required under Regulated | | `OPENADAPT_FLOW_SCRUB` | Scrubbing of identifiers from saved text: `auto` (default), `on`, or `off`. `on` stops with an error if the scrubber isn't installed. | | `OPENADAPT_FLOW_PHI_MODE` | Turns patient-data mode on; `OPENADAPT_FLOW_SCRUB=on` also does | -| `OPENADAPT_FLOW_VLM_URL` | The address of an on-site model server | -| `OPENADAPT_FLOW_NO_AUTO_INSTALL` | `1` skips the Chromium download on the first browser run, for computers without internet access | +| `OPENADAPT_FLOW_VLM_URL`, `OPENADAPT_FLOW_VLM_TOKEN`, `OPENADAPT_FLOW_VLM_TIMEOUT` | The address of an on-site model server, its bearer token, and the request timeout in seconds (default `2.0`) | +| `OPENADAPT_FLOW_BROWSER` | Which browser web runs use: `auto` (default: installed Chrome, then Edge, then Playwright's Chromium), `chrome`, `msedge`, or `bundled` | +| `OPENADAPT_FLOW_NO_AUTO_INSTALL` | `1` skips the Chromium download when no installed browser is used, for computers without internet access | +| `OPENADAPT_FLOW_OCR` | The OCR engine: `auto` (default, RapidOCR), `rapidocr`, or `native` (Apple Vision or Windows OCR, with the `native-ocr` extra) | | `OPENADAPT_HOME` | Where OpenAdapt keeps local state (default `~/.openadapt`) | | `OPENADAPT_AGENT_RUNS_DIR` | The MCP server's runs folder (default `~/.openadapt/runs`) | -| `OAFLOW_AGENT_TOKEN` | The shared token for the Windows agent server | +| `OAFLOW_AGENT_TOKEN` | The shared token for the Windows agent server, and for `openadapt agent serve --windows-agent-url` | +| `OAFLOW_AGENT_CERTFILE`, `OAFLOW_AGENT_KEYFILE` | The Windows agent server's TLS certificate and key | +| `OPENADAPT_AGENT_SANDBOX_DIR` | The MCP server's sandbox folder (default `~/.openadapt/agent-sandbox`) | +| `OPENADAPT_AGENT_BUNDLES_DIR`, `OPENADAPT_AGENT_ALLOW_RUN`, `OPENADAPT_AGENT_ALLOW_ATTENDED_ACTIONS`, `OPENADAPT_AGENT_DEPLOYMENT_CONFIG`, `OPENADAPT_AGENT_POLICY`, `OPENADAPT_AGENT_HEADED`, `OPENADAPT_AGENT_BUNDLE_KEY` | Settings for the MCP server when it's installed as a desktop extension; each maps to the matching `serve` option, and the key to `OPENADAPT_BUNDLE_KEY` | +| `OPENADAPT_APPROVER_WEBHOOK_SECRET` | The key that signs approval requests sent to `--approver-webhook` | +| `OPENADAPT_SEAL_KEYS_PATH`, `OPENADAPT_SEAL_KEYS_JSON` | Pinned Seal keys, as a file or inline JSON (default `~/.openadapt/seal-keys.json`) | +| `OPENADAPT_COUNTERSIGN_VAULT` | Where `countersign` keeps field values and salts (default `~/.openadapt/countersign-vault`) | +| `OPENADAPT_EXCEPTION_RETENTION_DAYS` | How long closed exceptions are kept (default 2,190, at least 183) | | `OPENADAPT_INGEST_TOKEN` | The Cloud ingest token, if not in the keychain | | `OPENADAPT_FLOW_REPORT_RUN` | `1` sends a run summary to Cloud after a successful run, like `--report` | | `OPENADAPT_CLOUD_EXECUTION_ID`, `OPENADAPT_EXECUTE_API_KEY` | Link a run to a Cloud Execute execution so it can get a Seal | diff --git a/docs/reference/packages.md b/docs/reference/packages.md index 9a70144a..7bbac042 100644 --- a/docs/reference/packages.md +++ b/docs/reference/packages.md @@ -28,12 +28,11 @@ source. ## How the engine packages fit together -On the `main` branch, the agent and types code is part of `openadapt-flow` -(as `openadapt_flow.agent` and `openadapt_flow.contracts`). The -`openadapt-agent` and `openadapt-types` packages keep their names, imports, -and commands working. The released versions on PyPI, `openadapt-flow` 2.1.0 -and `openadapt-agent` 2.2.0, are still separate packages and install -`openadapt-types` separately. +From `openadapt-flow` 2.2.0, the agent and types code is part of +`openadapt-flow`, as `openadapt_flow.agent` and `openadapt_flow.contracts`. +The `openadapt-agent` and `openadapt-types` packages keep their names, +imports, and commands working, and each installs `openadapt-flow`. New code +should import `openadapt_flow.contracts`. ## Research diff --git a/docs/reference/receipts-and-seals.md b/docs/reference/receipts-and-seals.md new file mode 100644 index 00000000..8e3eafb1 --- /dev/null +++ b/docs/reference/receipts-and-seals.md @@ -0,0 +1,299 @@ +--- +description: >- + The receipt a verified run writes, the Seal OpenAdapt Cloud signs, the + effect receipt for a write another executor made, and how to timestamp, + countersign, and disclose them. +--- + +# Receipts, Seals, and countersigning + +A run that ends `VERIFIED` writes a **receipt**: a summary of what was +checked, safe to share. OpenAdapt Cloud can sign it, and the signed receipt +is a **Seal**. `verify-run` writes an **effect receipt** for a write that an +agent, a script, or a person made. `countersign` adds trusted timestamps, +field commitments you can reveal one at a time, and a dossier a third party +can check offline. + +## The run receipt + +A `VERIFIED` run writes `receipt.json` (schema `openadapt.run-receipt/v2`) +and an image of it, `receipt.png`. From the quickstart: + +```json +{ + "outcome": "VERIFIED", + "transaction_outcome": "VERIFIED", + "profile": "standard", + "substrate": "web", + "steps_ok": 6, "steps_total": 6, + "identity_confirmed": 5, "identity_required": 5, + "effects_confirmed": 2, "effects_required": 2, + "effect_tier_reached": "independent_system", + "model_calls": 0, + "external_network_calls": "none", + "bundle_digest": "d9b3102c…", + "provenance": "synthetic-tutorial" +} +``` + +It holds counts, digests, closed values, and versions; no record values. +`effect_tier_reached` is the weakest read-back among the run's saves: +`independent_system`, `independent_session`, or +`persisted_state_reacquisition`. `effect_assurance` gives its grade, +`independent_readback` or `screen_readback`. +[How a save is checked](../explanation/verification.md#how-strong-a-read-back-is) +maps the two. A stopped run gets no receipt, only its local report. + +## The Seal + +A Seal is OpenAdapt Cloud's Ed25519 signature on the receipt of a run that +Cloud Execute tracked. A run gets one when it ends `VERIFIED` with a record +check that read the system of record, and it was started with +`--cloud-execution-id` and `OPENADAPT_EXECUTE_API_KEY` set: + +```bash +export OPENADAPT_EXECUTE_API_KEY=... +openadapt flow run bundle --config deploy.yaml --cloud-execution-id EXECUTION_ID +``` + +Flow then fetches the signed receipt into `report.json` as +`execute_receipt`. `openadapt flow attach-seal RUN_DIR --execution-id ID` +does the same for a finished run. + +The Seal names the execution, the exact workflow version and digest, the +qualification, the environment and runner, the outcome, the check level that +confirmed the save, whether delivery was uncertain, and a digest of the +evidence. + +| Check level | Source | Can back a Seal? | +|---|---|---| +| 0 | The screen or OCR | No | +| 1 | A second session's view of the application | No | +| 2 | The system of record, such as its API or database | Yes | +| 3 | A counterparty artifact, such as a payer status or a legal export | Yes | + +### Check a Seal offline + +Pin Cloud's public keys once, comparing the printed fingerprint with the one +OpenAdapt publishes, then check: + +```bash +openadapt seal keys pin +openadapt seal verify runs//report.json +``` + +`keys pin` downloads the keys from +`https://app.openadapt.ai/api/public/seal-keys` (or `--url`) and writes +`~/.openadapt/seal-keys.json` (or `--path`). `seal verify` also accepts a +receipt file or a webhook body, and `--json` prints the result as JSON. It +never fetches keys while it checks. To check one in a browser instead, see +[Check a Seal online](../cloud/verify-a-seal.md). + +`openadapt flow seal` is a different command: it encrypts a bundle for +deployment and doesn't issue a Seal. + +## Check a write that something else made + +`verify-run` reads the system of record and decides whether a write an +agent, a script, or a person reported actually landed. It uses the same +record checks as `run` and makes no model calls. It's a preview: the effect +receipt format follows a draft specification and may change. + +```bash +# Before the agent starts: read the store, so new rows can be told from old ones. +openadapt flow verify-run --verify verify.yaml --params task.json \ + --capture-baseline before.json + +# After it finishes: +openadapt flow verify-run --verify verify.yaml --params task.json \ + --run-record agent-run.json --authorization-ref TICKET-1234 \ + --baseline before.json --out share/ +``` + +`verify.yaml` is the file `qualify` reads; `verify-run` uses its `verifier` +and `effects` (or `record`) and ignores the rest. See the +[`verify.yaml` reference](../how-to/certify-and-qualify.md#verifyyaml-reference). +`max_execution_window_s` (default 3600) is read only by `verify-run`. + +The inputs come from different people on purpose: + +- `--params` holds the values the task named, such as which patient, from + whoever assigned the task. The executor can't choose what it's checked + against. +- `--run-record` is what the executor reports. It must name `executor_kind`: + `agent`, `script`, `human`, `flow_replay`, or `unknown`. +- `--baseline` is the read taken before the executor started. Without it, a + check that counts new records is `INDETERMINATE`. A baseline works once, + and one older than `max_execution_window_s` counts as none. + +`--approver FILE` names who approved the write; an executor that approved its +own write gets `rejected_policy`. `--remediates RECEIPT_ID` links a receipt +to an earlier one it fixes, and `--correlation-id ID` carries your own ticket +or trace id. + +| What the store shows | `effect_verdict` | `outcome` | +|---|---|---| +| The declared record, exactly once, through a system-of-record read | `CONFIRMED` | `verified` | +| The same, but the executor approved its own write | `CONFIRMED` | `rejected_policy` | +| Nothing that matches | `REFUTED` | `halted_before_effect` | +| A duplicate, a wrong value, or only some of the effects | `REFUTED` | `reconciliation_required` | +| The store couldn't be read, no baseline, or a separate-session read | `INDETERMINATE` or `CONFIRMED` | `reconciliation_required` | + +Only a system-of-record read produces `verified`; `onscreen` is refused +before anything runs. `share/effect-receipt.json` (schema +`openadapt.effect-receipt/v1`) holds no record values, URLs, or the +executor's text. Every digest whose input could hold customer data is keyed +with a salt drawn for the run, which stays in the baseline file or +`share/effect-run.private.json`. Those files are written with mode 0600; +delete them when you no longer need them. + +The effect receipt is unsigned. To check a signed one against keys you +pinned: + +```bash +openadapt flow verify-run --check signed.json --keys seal-keys.json +``` + +It exits 0 only for a genuine signature on a `verified` outcome. +`--accept-outcome OUTCOME` accepts another outcome. + +## Timestamps and countersigning + +`countersign` adds three things to a sealed receipt, beside it in the same +document: + +- **Trusted timestamps** (RFC 3161) over the receipt's hash. A Seal with one + is a Timestamped Seal. +- **Salted field commitments**, so you can later reveal chosen fields, such + as one patient's record number, and keep the others hidden. +- **A countersignature** by a second issuer. A Seal with one is a + Countersigned Seal. OpenAdapt Cloud doesn't countersign yet; a local key + can, for tests and self-hosted trials. + +Neither is a notarial act, a government certification, or an eIDAS +qualified seal or time stamp. + +Install the extra first: + +```bash +pip install 'openadapt-flow[countersign]' +``` + +```bash +# 1. Before the executor runs: timestamp the baseline read for this run. +openadapt flow countersign timestamp-baseline --baseline baseline.json \ + --read-at 2026-10-17T04:58:00Z --nonce "$RUN_NONCE" --out baseline-ts.json + +# 2. After the receipt exists: commit fields, timestamp, and countersign. +openadapt flow countersign add receipt.json --out countersigned.json \ + --fields-file fields.json --baseline-timestamp baseline-ts.json + +# 3. Check it. +openadapt flow countersign verify countersigned.json + +# 4. In a dispute: reveal one field, then export a dossier. +openadapt flow countersign disclose countersigned.json --field f-1a2b3c4d --out mrn.disclosure.json +openadapt flow countersign dossier countersigned.json --disclosure mrn.disclosure.json \ + --out dossier.html +``` + +`fields.json` maps field names to values, such as `{"mrn": "004217"}`. +Values never go on the command line, where shell history would keep them. +Delete the file once the vault has its copy. + +### The timestamp service + +`countersign add` and `timestamp-baseline` are the only `countersign` +commands that connect to the network. By default they use Sigstore's public +timestamp service, `https://timestamp.sigstore.dev/api/v1/timestamp`, which +receives only a 32-byte digest and a nonce. It's free and has no service +level agreement, so for high volume use your own RFC 3161 service: + +| Flag | Effect | +|---|---| +| `--tsa-url URL` | Use another RFC 3161 timestamp service | +| `--tsa-cert PEM` | Pin that service's certificate or a CA above it. Without it, the bundled Sigstore root is pinned. | +| `--no-timestamp` | Request no timestamp. `timestamp-baseline` refuses it. | +| `--tsa-local DIR` | A test-only timestamp service on this computer. Its time is this computer's clock. | + +Each new token is checked against the pinned certificate before anything is +written. A service that can't be reached, refuses, or returns a token that +doesn't verify leaves no output file. + +### What `countersign verify` shows + +`verify` prints the executor first (a Flow replay, or another executor such +as a computer-use agent), then the verification tier, the effect verdict, the +outcome, and the result of each check: the Seal signature, the evidence +manifest, each timestamp with the service's name and time, the +countersignature, the field commitments, and each disclosure. `--json` prints +the same report as data. + +It exits 0 only for a genuine Seal on a `verified` or `rolled_back_verified` +outcome, with every part present checked and passed. It exits 1 for a Seal +on a `REFUTED` or `INDETERMINATE` outcome, for any failed check, for a +timestamp with no pinned certificate, and for an effect receipt from anything +but a Flow replay unless you pass `--accept-non-flow`. It warns, without +changing the exit code, when the timestamp's time and the receipt's issue +time are more than 300 seconds apart. + +Seal keys come from `--keys`, `OPENADAPT_SEAL_KEYS_PATH`, +`OPENADAPT_SEAL_KEYS_JSON`, or `~/.openadapt/seal-keys.json`. Nothing is +fetched. + +### Field commitments and the vault + +Each field gets its own random salt, and its commitment names no field. A +disclosure gives one field's name, value, and salt, and anyone can recompute +the commitment. A disclosure counts only when a verified timestamp or +countersignature binds the commitments. + +`countersign add` keeps the salts and values in a vault: +`$OPENADAPT_COUNTERSIGN_VAULT//` (default +`~/.openadapt/countersign-vault`), mode 0600. The vault holds patient data: +back it up and encrypt it like the system of record. Losing it loses the +ability to disclose; it never weakens the commitments. + +### The exception queue + +`countersign add --keys FILE` queues an exception for each genuine Seal on a +`REFUTED` or `INDETERMINATE` outcome. An entry starts `open` and ends one of +two ways: + +- **Resolved by a Seal.** `countersign resolve ENTRY --with fix.json` takes a + later receipt with a genuine Seal on a verified outcome that links to the + failed run and has the same workflow, contract, and committed inputs. +- **Closed by attestation.** `countersign close` takes a signed disposition + from a person: a closed `--code` and `--method`, a role, and a ticket id + kept only as a keyed digest. The key that sealed the failed run, or that + run's executor, can't close it. + +No flag marks an entry resolved or closed on someone's word. Closed entries +are kept for 6 years (`--retention-days` or +`OPENADAPT_EXCEPTION_RETENTION_DAYS`, at least 183 days). +`countersign purge --export FILE` writes expired entries to `FILE` before +deleting them; open entries are never purged. `countersign exceptions` lists +entries. + +### The dossier + +`countersign dossier` writes one HTML page, or with `--format zip` the page +plus the raw token files. When opened, the page checks the receipt hash, the +Seal signature, the evidence digest, and each disclosed field in the browser. +Timestamps need `openadapt flow countersign verify dossier.html` or +`openssl ts -verify`. The dossier lists every exception in the vault and how +each ended. + +It holds no patient data unless you pass a disclosure, and then only that +field. `--screenshot` adds screenshots after automatic redaction, which can +miss text, so review a dossier before you send it. + +### What a timestamp proves + +A verified token shows that the timestamp service signed this exact hash at +its stated time. If the service's key and clock were sound, the receipt, its +evidence digest, and its commitments existed by then and haven't changed. + +It doesn't prove that the write happened (the read-back and the Seal carry +that), when the write happened, that the baseline came before the executor, +or who asked for the timestamp. Revocation isn't checked offline. diff --git a/docs/security/data-handling.md b/docs/security/data-handling.md index 5a9cc7f0..31cdadf5 100644 --- a/docs/security/data-handling.md +++ b/docs/security/data-handling.md @@ -74,7 +74,7 @@ application they drive. | `record`, `replay`, `run`, `resume` | The record check's endpoint, and the API in `actuation:` when one is set | The record check's query, and the values an API save writes | | The same, with model grounding on | The model endpoint | Screenshots. Under patient-data mode, only to hosts on `phi_grounding_allowlist`. | | The same, with `OPENADAPT_FLOW_VLM_URL` set | A model server on your network | Crops and screenshots. Identity crops sent to it aren't scrubbed. | -| The first browser run on a computer | Playwright's download server, once | A request for Chromium; nothing from the workflow. `OPENADAPT_FLOW_NO_AUTO_INSTALL=1` skips the download. | +| The first browser run on a computer with no Chrome or Edge, or with `OPENADAPT_FLOW_BROWSER=bundled` | Playwright's download server, once | A request for Chromium; nothing from the workflow. `OPENADAPT_FLOW_NO_AUTO_INSTALL=1` skips the download. | | `run --report`, `report-run`, `report-break`, `connector` | OpenAdapt Cloud | Counts, digests, and closed values. See [Subprocessors](subprocessors.md#what-runs-send-to-openadapt). | | `run --cloud-execution-id`, `attach-seal` | OpenAdapt Cloud | The execution id and the run's receipt, to get a Seal | | `connect`, `login` | OpenAdapt Cloud | A pairing code or an ingest token | @@ -82,18 +82,16 @@ application they drive. | `openadapt seal keys pin` | `app.openadapt.ai/api/public/seal-keys`, or `--url` | Nothing; it downloads the public keys. `openadapt seal verify` connects only when you give it a URL. | | `console --remote-decisions`, with `human_decisions.remote.enabled` | OpenAdapt Cloud | A signed task: IDs, digests, fixed codes, counts, and expiry. No screenshot or free text. See [Answer a halt from a phone](../how-to/answer-from-a-phone.md). | | The desktop decision portal, when your organization publishes it with `customer_ingress` | A paired phone, through your own HTTPS ingress | Screen crops and halt detail. Nothing goes to OpenAdapt Cloud. | +| `countersign add`, `countersign timestamp-baseline` | Sigstore's public timestamp service by default, or the service named by `--tsa-url` | A 32-byte digest and a random nonce; no record content, names, or identifiers. `--no-timestamp` on `add` sends nothing. | | `openadapt agent authoring connect` | `openadapt.ai`, outgoing HTTPS only | The link it claims, then it polls for authoring requests | Commands are `openadapt flow` commands unless the table names another one. -On the `main` branch, not yet released, `countersign add` and -`countersign timestamp-baseline` ask a timestamp service to date a record. -By default that is Sigstore's public service, -`https://timestamp.sigstore.dev/api/v1/timestamp`. Only a 32-byte digest -and a random nonce are sent; no record content, names, or -identifiers. `--tsa-url URL` points them at another RFC 3161 timestamp -service, and `--tsa-cert PEM` pins its certificate. `--no-timestamp` sends -nothing. No other `countersign` command connects to the network. +The default timestamp service is +`https://timestamp.sigstore.dev/api/v1/timestamp`. `--tsa-cert PEM` pins +the certificate of another service. No other `countersign` command connects +to the network; see +[Receipts, Seals, and countersigning](../reference/receipts-and-seals.md#the-timestamp-service). ## Encryption diff --git a/docs/security/evidence.md b/docs/security/evidence.md index a50235b7..85807f1f 100644 --- a/docs/security/evidence.md +++ b/docs/security/evidence.md @@ -103,6 +103,6 @@ runs happen on a runner you control. - A real Citrix ICA/HDX session. - Recording Windows, RDP, and Citrix through the agent's Windows agent - server. This is on `main` only and has been tested against mock servers only. + server, tested so far against mock servers only. - Production qualification of a customer workflow. diff --git a/docs/security/execute.md b/docs/security/execute.md index fb1fe1ac..82a3d2db 100644 --- a/docs/security/execute.md +++ b/docs/security/execute.md @@ -68,27 +68,15 @@ Only `verified` is business success. The Seal is an `ExecuteEvidenceReceiptV1` signed by OpenAdapt Cloud. It names the execution, the exact workflow version and digest, the qualification, the environment and runner, the outcome, the check level that confirmed the -save, whether delivery was uncertain, and a digest of the evidence. - -| Check level | Source | Can back a Seal? | -|---|---|---| -| 0 | The screen or OCR | No | -| 1 | A second session's view of the application | No | -| 2 | The system of record, such as its API or database | Yes | -| 3 | A counterparty artifact, such as a payer status or a legal export | Yes | +save, whether delivery was uncertain, and a digest of the evidence. Only a +check level of 2 (the system of record) or 3 (a counterparty artifact) can +back one. A local run that ends `VERIFIED` is still reported as done, with -`"proof": "local"`, to an agent; a Seal is not required for that. -To attach Cloud's Seal to a local run, pass the execution ID: - -```bash -export OPENADAPT_EXECUTE_API_KEY=... -openadapt flow run bundle --config deploy.yaml --cloud-execution-id EXECUTION_ID -``` - -After a `VERIFIED` outcome, the run fetches the signed receipt and writes it -into `report.json` as `execute_receipt`. `openadapt flow attach-seal RUN_DIR --execution-id EXECUTION_ID` does the -same for a finished run. +`"proof": "local"`, to an agent; a Seal is not required for that. To attach +Cloud's Seal to a local run, pass `--cloud-execution-id`. How to get, check, +timestamp, and countersign a Seal is in +[Receipts, Seals, and countersigning](../reference/receipts-and-seals.md). Anyone with the receipt can check the Seal online or offline. See [Check a Seal online](../cloud/verify-a-seal.md). To be told when an execution diff --git a/mkdocs.yml b/mkdocs.yml index def10778..a01dca22 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -275,6 +275,7 @@ nav: - Command help: reference/cli-help.md - Configuration: reference/configuration.md - Files and evidence: reference/artifacts.md + - Receipts, Seals, and countersigning: reference/receipts-and-seals.md - Packages: reference/packages.md - Versions and compatibility: reference/compatibility.md - Glossary: reference/glossary.md diff --git a/scripts/generate_cli_reference.py b/scripts/generate_cli_reference.py index 3c6d9599..4050c246 100644 --- a/scripts/generate_cli_reference.py +++ b/scripts/generate_cli_reference.py @@ -38,23 +38,37 @@ LAUNCHER_RESEARCH = {"capture", "eval", "serve", "train"} -def pinned_versions(path: Path = REQUIREMENTS) -> dict[str, str]: - """Return {distribution: version} from `name==version` lines.""" - pins: dict[str, str] = {} - for line in path.read_text(encoding="utf-8").splitlines(): - line = line.split("#", 1)[0].strip() +def pinned_versions(path: Path = REQUIREMENTS) -> dict[str, tuple[str, bool]]: + """Return {distribution: (version, is_release)} from the requirements. + + A line is either `name==version`, a published release, or + `name @ git+URL@SHA#subdirectory=DIR # release VERSION`, a source build + of a release that isn't published yet. VERSION is what the docs call it. + """ + pins: dict[str, tuple[str, bool]] = {} + for raw in path.read_text(encoding="utf-8").splitlines(): + if " @ " in raw: + name = raw.split(" @ ", 1)[0].strip() + requirement, sep, version = raw.partition(" # release ") + if not sep or not version.strip(): + raise SystemExit(f"{path}: a source pin needs ' # release VERSION': {raw!r}") + pins[name] = (version.strip(), False) + continue + line = raw.split("#", 1)[0].strip() if not line: continue name, sep, version = line.partition("==") if not sep: - raise SystemExit(f"{path}: pin every package with ==: {line!r}") - pins[name.strip()] = version.strip() + raise SystemExit(f"{path}: pin every package with == or a source URL: {raw!r}") + pins[name.strip()] = (version.strip(), True) return pins -def _check_installed(pins: dict[str, str]) -> None: +def _check_installed(pins: dict[str, tuple[str, bool]]) -> None: wrong = [] - for name, version in pins.items(): + for name, (version, is_release) in pins.items(): + if not is_release: + continue try: installed = metadata.version(name) except metadata.PackageNotFoundError: @@ -108,8 +122,8 @@ def collect() -> dict[str, list]: return programs -def render_page(pins: dict[str, str], programs: dict[str, list]) -> str: - versions = ", ".join(f"{name} {version}" for name, version in pins.items()) +def render_page(pins: dict[str, tuple[str, bool]], programs: dict[str, list]) -> str: + versions = ", ".join(f"{name} {version}" for name, (version, _) in pins.items()) lines = [ "---", "description: >-", @@ -165,11 +179,11 @@ def main() -> int: programs = collect() PAGE.write_text(render_page(pins, programs), encoding="utf-8") (FIXTURES / "flow_cli.json").write_text( - fixture("openadapt-flow", pins["openadapt-flow"], programs["openadapt-flow"]), + fixture("openadapt-flow", pins["openadapt-flow"][0], programs["openadapt-flow"]), encoding="utf-8", ) (FIXTURES / "agent_cli.json").write_text( - fixture("openadapt-agent", pins["openadapt-agent"], programs["openadapt-agent"]), + fixture("openadapt-agent", pins["openadapt-agent"][0], programs["openadapt-agent"]), encoding="utf-8", ) print(f"Wrote {PAGE.relative_to(ROOT)} and the flow and agent CLI fixtures") diff --git a/tests/fixtures/agent_cli.json b/tests/fixtures/agent_cli.json index 391d13dd..d9a62f1d 100644 --- a/tests/fixtures/agent_cli.json +++ b/tests/fixtures/agent_cli.json @@ -1,7 +1,7 @@ { "program": "openadapt-agent", - "version": "2.2.0", - "source": "argparse parser of openadapt-agent 2.2.0, long options only. Generated by scripts/generate_cli_reference.py.", + "version": "2.2.1", + "source": "argparse parser of openadapt-agent 2.2.1, long options only. Generated by scripts/generate_cli_reference.py.", "global_options": [ "--help", "--version" @@ -67,8 +67,11 @@ "--allow-url-override", "--approval-mode", "--authoring", + "--authoring-screens", "--bundles", + "--citrix", "--config", + "--delegate-approvals", "--extra-run-arg", "--flow-cli", "--headed", @@ -76,13 +79,16 @@ "--hold-on-halt", "--mode", "--policy", + "--remote-window", + "--remote-window-title", "--repair-access", "--repair-dir", "--runs-dir", "--sandbox-engine", "--timeout", "--tutorial", - "--url" + "--url", + "--windows-agent-url" ] } } diff --git a/tests/fixtures/flow_cli.json b/tests/fixtures/flow_cli.json index eef44c4e..a6064b06 100644 --- a/tests/fixtures/flow_cli.json +++ b/tests/fixtures/flow_cli.json @@ -1,7 +1,7 @@ { "program": "openadapt-flow", - "version": "2.1.0", - "source": "argparse parser of openadapt-flow 2.1.0, long options only. Generated by scripts/generate_cli_reference.py.", + "version": "2.2.0", + "source": "argparse parser of openadapt-flow 2.2.0, long options only. Generated by scripts/generate_cli_reference.py.", "global_options": [ "--help", "--version" @@ -72,13 +72,34 @@ "--help", "--policy" ], + "check": [ + "--help" + ], + "check admit": [ + "--by", + "--help", + "--step" + ], + "check mark": [ + "--date-order", + "--field", + "--help", + "--kind", + "--reset", + "--start-event" + ], + "check show": [ + "--help", + "--json" + ], "compile": [ "--accept-params", "--help", "--name", "--no-confirm-params", "--out", - "--params-from" + "--params-from", + "--pixel-verify-enabled" ], "compose": [ "--after", @@ -157,6 +178,90 @@ "--skills", "--url" ], + "countersign": [ + "--help" + ], + "countersign add": [ + "--baseline-timestamp", + "--countersign-key", + "--fields-file", + "--help", + "--keys", + "--no-timestamp", + "--out", + "--retention-days", + "--tsa-cert", + "--tsa-local", + "--tsa-url", + "--vault" + ], + "countersign close": [ + "--approver-id", + "--approver-key", + "--approver-keys", + "--code", + "--external-reference", + "--help", + "--method", + "--notes-file", + "--role", + "--vault" + ], + "countersign disclose": [ + "--field", + "--help", + "--out", + "--vault" + ], + "countersign dossier": [ + "--accept-non-flow", + "--disclosure", + "--format", + "--help", + "--keys", + "--out", + "--screenshot", + "--tsa-cert", + "--vault" + ], + "countersign exceptions": [ + "--help", + "--json", + "--status", + "--vault" + ], + "countersign purge": [ + "--export", + "--help", + "--vault" + ], + "countersign resolve": [ + "--help", + "--keys", + "--tsa-cert", + "--vault", + "--with" + ], + "countersign timestamp-baseline": [ + "--baseline", + "--baseline-sha256", + "--help", + "--no-timestamp", + "--nonce", + "--out", + "--read-at", + "--tsa-cert", + "--tsa-local", + "--tsa-url" + ], + "countersign verify": [ + "--accept-non-flow", + "--disclosure", + "--help", + "--json", + "--keys", + "--tsa-cert" + ], "demo": [ "--headed", "--help", @@ -223,7 +328,10 @@ "--approver-id", "--auth-method", "--decided-by", + "--delegate-id", + "--delegate-kind", "--help", + "--on-behalf-of", "--request-id", "--request-sha256", "--route", @@ -233,7 +341,10 @@ "--approver-id", "--auth-method", "--decided-by", + "--delegate-id", + "--delegate-kind", "--help", + "--on-behalf-of", "--reason", "--request-id", "--request-sha256", @@ -244,6 +355,54 @@ "--all", "--help" ], + "jit": [ + "--help" + ], + "jit admit": [ + "--help", + "--key", + "--key-dir", + "--policy", + "--principal", + "--require-distinct" + ], + "jit keygen": [ + "--help", + "--key-dir", + "--out" + ], + "jit register": [ + "--help", + "--key-dir", + "--principal", + "--public-key", + "--role" + ], + "jit reject": [ + "--help", + "--key-dir", + "--principal", + "--reason" + ], + "jit review": [ + "--accept-param", + "--all-steps-shown", + "--help", + "--key-dir", + "--principal", + "--queue", + "--step" + ], + "jit sign-policy": [ + "--help", + "--key", + "--key-dir", + "--out" + ], + "jit status": [ + "--help", + "--key-dir" + ], "lint": [ "--help", "--strict" @@ -283,6 +442,13 @@ "--proposer", "--recording" ], + "pull": [ + "--force", + "--help", + "--index", + "--list", + "--out" + ], "push": [ "--attest-non-phi", "--auto-approve", @@ -335,6 +501,16 @@ "--help", "--input" ], + "qualify campaign": [ + "--campaign-id", + "--cases", + "--certify", + "--help", + "--out", + "--run-params", + "--trials", + "--verify" + ], "qualify certify": [ "--evidence-root", "--help", @@ -425,6 +601,10 @@ "--api-base-url", "--approval-mode", "--approval-wait", + "--approver-policy", + "--approver-webhook", + "--approver-webhook-id", + "--approver-webhook-kind", "--backend", "--browser-cdp-endpoint", "--browser-page-url", @@ -513,7 +693,8 @@ "--until-cmd", "--url", "--window", - "--window-title" + "--window-title", + "--with-check" ], "repair": [ "--help" @@ -642,6 +823,10 @@ "--api-base-url", "--approval-mode", "--approval-wait", + "--approver-policy", + "--approver-webhook", + "--approver-webhook-id", + "--approver-webhook-kind", "--attest-audit-log", "--attest-contract", "--attest-pre-state", @@ -691,6 +876,10 @@ "--approval-mode", "--approval-wait", "--approve-unverified-writes", + "--approver-policy", + "--approver-webhook", + "--approver-webhook-id", + "--approver-webhook-kind", "--attest-audit-log", "--attest-contract", "--attest-pre-state", @@ -793,6 +982,65 @@ "--out", "--skill-id" ], + "triage": [ + "--help" + ], + "triage approve-batch": [ + "--approver-id", + "--auth-method", + "--batch-sha256", + "--decided-by", + "--help", + "--labels", + "--pull-back", + "--unauthenticated" + ], + "triage calibrate": [ + "--delta", + "--group", + "--help", + "--labels", + "--out", + "--target-miss", + "--window" + ], + "triage decide": [ + "--approve", + "--approver-id", + "--auth-method", + "--decided-by", + "--decline", + "--help", + "--item", + "--labels", + "--unauthenticated" + ], + "triage eligibility": [ + "--artifact", + "--artifact-sha256", + "--audit-rate", + "--delta", + "--help", + "--labels", + "--target-miss", + "--verified-runs", + "--workflow" + ], + "triage estimate": [ + "--help", + "--labels" + ], + "triage plan": [ + "--artifact", + "--artifact-sha256", + "--audit-rate", + "--help", + "--mode", + "--observations", + "--out", + "--policy", + "--run-dir" + ], "tutorial": [ "--break-it", "--guided", @@ -822,6 +1070,22 @@ "--token", "--trusted-host" ], + "verify-run": [ + "--accept-outcome", + "--approver", + "--authorization-ref", + "--baseline", + "--capture-baseline", + "--check", + "--correlation-id", + "--help", + "--keys", + "--out", + "--params", + "--remediates", + "--run-record", + "--verify" + ], "visualize": [ "--format", "--help", diff --git a/tests/test_documented_flow_options.py b/tests/test_documented_flow_options.py index 64b8e2c3..570b1671 100644 --- a/tests/test_documented_flow_options.py +++ b/tests/test_documented_flow_options.py @@ -30,10 +30,10 @@ # Options that exist on OpenAdapt `main` but not in the pinned release. A page # may show them only where it labels them as unreleased; each entry names the # page that does. Remove an entry when the release that ships it is pinned. -UNRELEASED_OPTIONS = { - # docs/how-to/certify-and-qualify.md: the one-command qualification campaign. - "qualify": {"--verify", "--out", "--certify"}, -} +UNRELEASED_OPTIONS: dict[str, set[str]] = {} +# Flow rewrites `qualify ...` to `qualify campaign ...` +# (`_qualify_default_form`), so that form takes the campaign's options. +DEFAULT_SUBCOMMANDS = {"qualify": "qualify campaign"} # Regenerated from other repositories' release notes; they quote history. # The command help page is generated from the pinned parsers themselves. GENERATED_PAGES = { @@ -122,6 +122,14 @@ def _problems( if command not in commands: problems.append(f"line {number}: unknown subcommand `{command}` in `{code}`") continue + default = DEFAULT_SUBCOMMANDS.get(command) + if ( + default in commands + and len(tokens) > 1 + and not tokens[1].startswith("-") + and f"{command} {tokens[1]}" not in commands + ): + command = default for token in tokens[1:]: if f"{command} {token}" in commands: command = f"{command} {token}" @@ -135,8 +143,12 @@ def _problems( def _pins() -> dict[str, str]: + """{name: version} for `name==version` and `name @ URL # release VERSION` lines.""" pins = {} for line in REQUIREMENTS.read_text(encoding="utf-8").splitlines(): + if " @ " in line: + pins[line.split(" @ ", 1)[0].strip()] = line.rsplit(" # release ", 1)[1].strip() + continue line = line.split("#", 1)[0].strip() if line: name, _, version = line.partition("==") From 21f12e583808015bead1b4d0b554ea279fc28d04 Mon Sep 17 00:00:00 2001 From: abrichr Date: Sun, 18 Oct 2026 12:05:06 -0400 Subject: [PATCH 5/5] docs: describe the released Flow 2.2.0 CLI Release day for #269: pins openadapt-flow==2.2.0 and openadapt-agent==2.2.1, snapshots the Flow command reference from the PyPI release, regenerates the launcher and agent help, and lists the six verbs 2.2.0 added in the curated table. Co-Authored-By: Claude Opus 5.5 --- cli-reference/requirements.txt | 9 +- docs/reference/cli-help.md | 8 +- docs/reference/cli.md | 6 + docs/reference/flow-commands.md | 132 +++++++++++++++- scripts/flow_cli_reference.py | 5 +- tests/fixtures/flow_cli_2.2.0.json | 245 ++++++++++++++++++++++++++++- 6 files changed, 384 insertions(+), 21 deletions(-) diff --git a/cli-reference/requirements.txt b/cli-reference/requirements.txt index 7697f53c..36968cc7 100644 --- a/cli-reference/requirements.txt +++ b/cli-reference/requirements.txt @@ -1,13 +1,8 @@ # The versions the command reference describes. Change a pin, then rerun # scripts/generate_cli_reference.py and commit what it writes. # -# openadapt-flow 2.2.0 and openadapt-agent 2.2.1 aren't published yet. Until -# they are, the pins build them from OpenAdapt main, which holds everything -# the 2.2.0 release ships. On release day, replace both lines with -# openadapt-flow==2.2.0 and openadapt-agent==2.2.1. -# # The Flow pin must name the same version as FLOW_VERSION in # scripts/flow_cli_reference.py. openadapt==1.17.0 -openadapt-flow @ git+https://github.com/OpenAdaptAI/OpenAdapt@9104577e603e030f89318ead9cd9d586d439aefe#subdirectory=packages/flow # release 2.2.0 -openadapt-agent @ git+https://github.com/OpenAdaptAI/OpenAdapt@9104577e603e030f89318ead9cd9d586d439aefe#subdirectory=packages/agent # release 2.2.1 +openadapt-flow==2.2.0 +openadapt-agent==2.2.1 diff --git a/docs/reference/cli-help.md b/docs/reference/cli-help.md index 7149b4b7..51547434 100644 --- a/docs/reference/cli-help.md +++ b/docs/reference/cli-help.md @@ -335,9 +335,11 @@ options: Let the calling agent answer saves that wait for an approval, as the opaque id AGENT_ID (for example agent:intake-bot). Adds the list_approval_requests and answer_approval_request tools. Flow accepts the - agent's answer only where the run's approver policy (openadapt-flow run - --approver-policy) allows an agent for that step; by default only a person - may answer. Flow records the agent as the approver. + agent's answer only under a person-signed approver grant (openadapt-flow + run --approver-policy) that names AGENT_ID and allows an agent for that + step; by default only a person may answer. Runs this server starts record + AGENT_ID as their initiator, so the agent can never approve its own runs. + Flow records the agent and the grant's digest as the approver. --allow-url-override Permit MCP callers to pass a per-call `url` argument (default: the target URL is fixed by --url/--config at server start) --flow-cli FLOW_CLI Command used to invoke the flow CLI (default: this interpreter's `python diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 4946817b..5ab9726b 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -56,7 +56,9 @@ with its long options, generated from the released package. | [`qualify`](#check) | Manages and runs a bundle's qualification against normal and fault cases. | | [`propose-qualification`](#check) | Same as `qualify propose`. | | [`verify-run`](#check) | Reads the system of record and rules on a save that an agent, a script, or a person reported. | +| [`known-ids`](#check) | Admits a system-of-record export as the known-ID list that look-alike identity checks use. | | [`countersign`](#check) | Timestamps, countersigns, and discloses fields of a Seal, and exports a dossier. | +| [`local-seal`](#check) | Signs offline Seals with this machine's key. | | [`seal`](#check) | Writes an encrypted copy of a bundle using `OPENADAPT_BUNDLE_KEY`. | | [`admit`](#check) | Reads the public release-admission list. It never signs anything. | | [`replay`](#run) | Performs every step of a bundle against a real app. Never checks the record. | @@ -64,6 +66,7 @@ with its long options, generated from the released package. | [`resume`](#run) | Continues a paused durable run from its last confirmed step. | | [`approve`](#run) | Records a decision that lets a paused run resume. | | [`item-approval`](#run) | Lists, approves, or declines the saves a run is waiting on. | +| [`approver-grant`](#run) | Signs or checks a grant that lets named agents or systems answer saves. | | [`explain`](#inspect) | Explains a finished run in plain words, with the next command. | | [`triage`](#run) | Orders verified saves for review and proposes one batch approval. | | [`attach-seal`](#run) | Adds Cloud's signed receipt to a `VERIFIED` run report. | @@ -72,6 +75,7 @@ with its long options, generated from the released package. | [`teach`](#repair) | Turns a fix demonstration for a halted run into a guarded branch. | | [`teach-branch`](#repair) | Learns a guarded branch from two or more halts fixed the same way. | | [`repair`](#repair) | Moves a repaired bundle through review, campaigns, approval, staging, canary, and rollback. | +| [`fleet`](#repair) | Sets fleet drift patches, opt-in per app family. | | [`jit`](#agents) | Reviews, admits, or rejects a draft bundle compiled from a computer-use agent's run. | | [`emit-skill`](#agents) | Writes an Agent Skill for a bundle. | | [`emit-mcp`](#agents) | Writes a standalone MCP `server.py` for a bundle. | @@ -90,6 +94,8 @@ with its long options, generated from the released package. | [`console`](#operate) | Serves a local operator page over bundles, runs, and halts. Read-only unless `--allow-actions`. | | [`business-decisions`](#operate) | Relays typed business decisions on a customer runner. It never resumes or acts. | | [`serve-execute`](#operate) | Serves the Execute API locally. Its receipts are signed with a local key and aren't Seals. | +| [`service`](#operate) | Runs the unattended on-prem service, with health checks and restart. | +| [`no-outbound`](#operate) | Lists and edits the allow list for strict no-outbound mode. | | [`serve-reward`](#operate) | Scores training episodes by reading the system of record. Its receipts aren't Seals. | `certify`, `replay`, `run`, and `resume` all accept `--config deploy.yaml`. diff --git a/docs/reference/flow-commands.md b/docs/reference/flow-commands.md index a132cb05..1aa460cc 100644 --- a/docs/reference/flow-commands.md +++ b/docs/reference/flow-commands.md @@ -41,6 +41,19 @@ Approve and freeze the exact reviewed derivative as an immutable archive |---|---| | `openadapt flow approve-sanitized` | `--original`, `--reviewer` | +## `approver-grant` + +Sign or check a grant that lets named agents or systems answer some saves. Only a registered person signs, at a terminal + +| Command | Long options | +|---|---| +| `openadapt flow approver-grant` | | +| `openadapt flow approver-grant sign` | `--key`, `--key-dir`, `--out` | +| `openadapt flow approver-grant verify` | `--key-dir` | + +- `approver-grant sign`: Sign a grant with your policy-signer key +- `approver-grant verify`: Check a signed grant against the registry + ## `attach-seal` Attach Cloud's signed Execute receipt (the Seal) to a VERIFIED run report @@ -181,7 +194,7 @@ Timestamp, countersign, selectively disclose, and export a dispute dossier for a ## `demo` -See the proof in one command: a checked run and a run where the app lies, side by side on one page (fake clinic data, no AI calls) +See the proof in one command: a checked run and a run whose save fails, side by side on one page (fake clinic data, no AI calls) | Command | Long options | |---|---| @@ -227,6 +240,33 @@ Plain-language read of a completed run dir: what happened, why the outcome is th |---|---| | `openadapt flow explain` | | +## `fleet` + +Fleet drift patch settings: opt-in per app family, roles, and the signed promotion policy. Off for every app by default. + +| Command | Long options | +|---|---| +| `openadapt flow fleet` | `--fleet-dir` | +| `openadapt flow fleet allow` | `--admin`, `--app-family`, `--domain`, `--export`, `--import` | +| `openadapt flow fleet disallow` | `--app-family`, `--domain`, `--export`, `--import` | +| `openadapt flow fleet policy` | | +| `openadapt flow fleet policy keygen` | `--out`, `--unencrypted` | +| `openadapt flow fleet policy sign` | `--approver-id`, `--approver-name`, `--approver-title`, `--attest-security-official`, `--authorised-at`, `--draft`, `--key`, `--out`, `--security-official-name`, `--security-official-title` | +| `openadapt flow fleet policy verify` | `--policy` | +| `openadapt flow fleet roles` | `--approver`, `--author`, `--public-key` | +| `openadapt flow fleet simulate` | `--out` | +| `openadapt flow fleet status` | `--json` | + +- `fleet allow`: Allow patch export and/or import for an app family or domain +- `fleet disallow`: Remove an app family or domain from the allowlist +- `fleet policy`: Sign or verify the promotion policy +- `fleet policy keygen`: Create a policy approver key (PEM, 0600) +- `fleet policy sign`: Sign a policy draft as a policy approver +- `fleet policy verify`: Verify the signed policy against roles.json +- `fleet roles`: Add a workflow author or a policy approver (never both) +- `fleet simulate`: Run the three-tenant local simulator +- `fleet status`: Show opt-in, roles, policy and trust + ## `for-each` Author a DATA-DRIVEN LOOP bundle: wrap an existing single-demonstration bundle's linear body in a LOOP that runs once per record of a worklist (CSV/JSON), binding each record's columns to the workflow's parameters. Emits a program:true bundle the runtime executes bounded, $0, identity-gated and effect-verified per record @@ -289,6 +329,19 @@ Review, admit or reject a DRAFT compiled from a computer-use agent run - `jit sign-policy`: Sign an admission policy with your policy-signer key - `jit status`: Print the state, steps and what admission needs +## `known-ids` + +Admit a system-of-record export as the known-ID list, and check it + +| Command | Long options | +|---|---| +| `openadapt flow known-ids` | | +| `openadapt flow known-ids confirm` | `--by`, `--column`, `--confirmation`, `--exported-at`, `--source`, `--yes` | +| `openadapt flow known-ids status` | `--column`, `--confirmation`, `--max-age-hours` | + +- `known-ids confirm`: Confirm the export's source and completeness (recorded) +- `known-ids status`: Would replay accept this list now? + ## `lint` Report a bundle's coverage gaps (unarmed clicks, vacuous postconditions, under-classified risk); exits nonzero by severity @@ -297,6 +350,29 @@ Report a bundle's coverage gaps (unarmed clicks, vacuous postconditions, under-c |---|---| | `openadapt flow lint` | `--strict` | +## `local-seal` + +Offline Seals signed with this machine's key; optional batched timestamps + +| Command | Long options | +|---|---| +| `openadapt flow local-seal` | `--home` | +| `openadapt flow local-seal batch` | | +| `openadapt flow local-seal file` | `--kind`, `--out`, `--run-id` | +| `openadapt flow local-seal key` | | +| `openadapt flow local-seal proof` | | +| `openadapt flow local-seal run` | | +| `openadapt flow local-seal submit` | `--tsa-url` | +| `openadapt flow local-seal verify` | `--keys`, `--seal` | + +- `local-seal batch`: Fold queued Seals into one Merkle batch (local) +- `local-seal file`: Seal any file: a record, an export, an audit report +- `local-seal key`: Print this machine's public Seal key for pinning +- `local-seal proof`: Show the batch, root and inclusion proof for a Seal +- `local-seal run`: Seal a run directory's report.json +- `local-seal submit`: Timestamp every unsent batch root (sends one 32-byte digest per batch) +- `local-seal verify`: Verify a run directory, or a file with --seal + ## `login` Validate an ingest token against the hosted control plane and store it in the OS keychain @@ -313,6 +389,25 @@ Rebuild an approved derivative from its archive and approval record |---|---| | `openadapt flow materialize-approved` | `--approval`, `--archive`, `--expect-archive-sha256`, `--out` | +## `no-outbound` + +Strict no-outbound mode: list and edit the allow list + +| Command | Long options | +|---|---| +| `openadapt flow no-outbound` | | +| `openadapt flow no-outbound allow` | `--file` | +| `openadapt flow no-outbound allow add` | `--port` | +| `openadapt flow no-outbound allow derive` | | +| `openadapt flow no-outbound allow list` | `--json` | +| `openadapt flow no-outbound allow remove` | | + +- `no-outbound allow`: The allow-list file +- `no-outbound allow add`: Allow an IP address or private CIDR range by hand +- `no-outbound allow derive`: Allow only the host(s) a remote-desktop session profile connects to +- `no-outbound allow list`: Show every allowed address and where it came from +- `no-outbound allow remove`: Remove an address or range + ## `process` Author a ProcessContract parent from independently admitted capabilities. Each --child needs a --admission envelope. certify/run the output directory; replay refuses it. @@ -372,7 +467,7 @@ Qualify: before a compiled workflow may run unattended on a real system, run it | `openadapt flow qualify propose` | `--out`, `--policy-pack`, `--proposer`, `--recording` | | `openadapt flow qualify report` | `--evidence-root`, `--json`, `--policy` | | `openadapt flow qualify run` | `--evidence-root`, `--results` | -| `openadapt flow qualify run-case` | `--agent-url`, `--allow-surface-override`, `--api-actuator`, `--api-base-url`, `--approval-mode`, `--approval-wait`, `--approver-policy`, `--approver-webhook`, `--approver-webhook-id`, `--approver-webhook-kind`, `--backend`, `--browser-cdp-endpoint`, `--browser-page-url`, `--campaign-id`, `--case-id`, `--config`, `--dry-run`, `--durable`, `--effects-base-url`, `--effects-kind`, `--effects-root`, `--fault-driver`, `--headed`, `--inputs`, `--linux-allow-physical-input`, `--linux-app`, `--linux-window-title`, `--macos-app`, `--macos-window-title`, `--qualification-campaign-authority-file`, `--rdp-host`, `--rdp-readiness-text`, `--rdp-window`, `--rdp-window-title`, `--run-dir`, `--run-id`, `--save-healed-to`, `--url` | +| `openadapt flow qualify run-case` | `--agent-url`, `--allow-surface-override`, `--api-actuator`, `--api-base-url`, `--approval-mode`, `--approval-wait`, `--approver-policy`, `--approver-webhook`, `--approver-webhook-id`, `--approver-webhook-kind`, `--backend`, `--browser-cdp-endpoint`, `--browser-page-url`, `--campaign-id`, `--case-id`, `--config`, `--dry-run`, `--durable`, `--effects-base-url`, `--effects-kind`, `--effects-root`, `--fault-driver`, `--headed`, `--inputs`, `--linux-allow-physical-input`, `--linux-app`, `--linux-window-title`, `--macos-app`, `--macos-window-title`, `--qualification-campaign-authority-file`, `--rdp-host`, `--rdp-readiness-text`, `--rdp-window`, `--rdp-window-title`, `--run-dir`, `--run-id`, `--save-healed-to`, `--started-by`, `--url` | | `openadapt flow qualify schema` | | | `openadapt flow qualify set-effect` | `--effect-index`, `--path`, `--step`, `--tier` | | `openadapt flow qualify set-identity` | `--canonical-ladder`, `--quorum`, `--signal`, `--signal-expected`, `--signal-extract`, `--signal-param`, `--signal-region`, `--step` | @@ -412,7 +507,7 @@ Record YOUR workflow interactively: a headed browser (--backend web --url), or t | Command | Long options | |---|---| -| `openadapt flow record` | `--agent-url`, `--allow-surface-override`, `--backend`, `--browser-cdp-endpoint`, `--browser-page-url`, `--headless`, `--identifier`, `--linux-allow-physical-input`, `--linux-app`, `--linux-window-title`, `--macos-app`, `--macos-window-title`, `--out`, `--param`, `--profile`, `--rdp-host`, `--rdp-readiness-text`, `--rdp-window`, `--rdp-window-title`, `--secret`, `--task`, `--until-cmd`, `--url`, `--window`, `--window-title`, `--with-check` | +| `openadapt flow record` | `--agent-url`, `--allow-surface-override`, `--backend`, `--browser-cdp-endpoint`, `--browser-page-url`, `--headless`, `--identifier`, `--linux-allow-physical-input`, `--linux-app`, `--linux-window-title`, `--macos-app`, `--macos-window-title`, `--out`, `--param`, `--profile`, `--rdp-host`, `--rdp-readiness-text`, `--rdp-window`, `--rdp-window-title`, `--secret`, `--task`, `--until-cmd`, `--url`, `--verify`, `--window`, `--window-title`, `--with-check` | ## `repair` @@ -475,7 +570,7 @@ Resume a durably-paused run from its last verified checkpoint (never re-running | Command | Long options | |---|---| -| `openadapt flow resume` | `--agent-url`, `--allow-surface-override`, `--api-actuator`, `--api-base-url`, `--approval-mode`, `--approval-wait`, `--approver-policy`, `--approver-webhook`, `--approver-webhook-id`, `--approver-webhook-kind`, `--attest-audit-log`, `--attest-contract`, `--attest-pre-state`, `--attest-sign-key`, `--backend`, `--browser-cdp-endpoint`, `--browser-page-url`, `--config`, `--durable`, `--effects-base-url`, `--effects-kind`, `--effects-root`, `--headed`, `--linux-allow-physical-input`, `--linux-app`, `--linux-window-title`, `--macos-app`, `--macos-window-title`, `--managed-dispatch-file`, `--param`, `--params-file`, `--qualification-authority-file`, `--qualification-campaign-authority-file`, `--rdp-host`, `--rdp-readiness-text`, `--rdp-window`, `--rdp-window-title`, `--report`, `--require-approval`, `--url` | +| `openadapt flow resume` | `--agent-url`, `--allow-surface-override`, `--api-actuator`, `--api-base-url`, `--approval-mode`, `--approval-wait`, `--approver-policy`, `--approver-webhook`, `--approver-webhook-id`, `--approver-webhook-kind`, `--attest-audit-log`, `--attest-contract`, `--attest-pre-state`, `--attest-sign-key`, `--backend`, `--browser-cdp-endpoint`, `--browser-page-url`, `--config`, `--durable`, `--effects-base-url`, `--effects-kind`, `--effects-root`, `--headed`, `--linux-allow-physical-input`, `--linux-app`, `--linux-window-title`, `--macos-app`, `--macos-window-title`, `--managed-dispatch-file`, `--param`, `--params-file`, `--qualification-authority-file`, `--qualification-campaign-authority-file`, `--rdp-host`, `--rdp-readiness-text`, `--rdp-window`, `--rdp-window-title`, `--report`, `--require-approval`, `--started-by`, `--url` | ## `review-sanitized` @@ -491,7 +586,7 @@ Execute a bundle under a deployment config (--config): the replay path wired for | Command | Long options | |---|---| -| `openadapt flow run` | `--agent-url`, `--allow-model-grounding`, `--allow-surface-override`, `--allow-trusted-code`, `--allow-unencrypted`, `--api-actuator`, `--api-base-url`, `--approval-mode`, `--approval-wait`, `--approve-unverified-writes`, `--approver-policy`, `--approver-webhook`, `--approver-webhook-id`, `--approver-webhook-kind`, `--attest-audit-log`, `--attest-contract`, `--attest-pre-state`, `--attest-sign-key`, `--backend`, `--browser-cdp-endpoint`, `--browser-page-url`, `--cloud-execution-id`, `--code-runtime-environment-digest`, `--code-trust`, `--config`, `--dry-run`, `--durable`, `--effects-base-url`, `--effects-kind`, `--effects-root`, `--execute-environment-id`, `--execute-token`, `--execute-url`, `--explain`, `--headed`, `--hold-on-halt`, `--linux-allow-physical-input`, `--linux-app`, `--linux-window-title`, `--macos-app`, `--macos-window-title`, `--managed-dispatch-file`, `--param`, `--params-file`, `--pin-digest`, `--pin-version`, `--policy`, `--process-environment-id`, `--process-receipt-issuer-key-id`, `--process-receipt-private-key`, `--process-runner-id`, `--profile`, `--qualification-authority-file`, `--qualification-trust`, `--rdp-host`, `--rdp-readiness-text`, `--rdp-window`, `--rdp-window-title`, `--record-video`, `--report`, `--run-dir`, `--save-healed-to`, `--strict-templates`, `--url`, `--worklist` | +| `openadapt flow run` | `--agent-url`, `--allow-model-grounding`, `--allow-surface-override`, `--allow-trusted-code`, `--allow-unencrypted`, `--api-actuator`, `--api-base-url`, `--approval-mode`, `--approval-wait`, `--approve-unverified-writes`, `--approver-policy`, `--approver-webhook`, `--approver-webhook-id`, `--approver-webhook-kind`, `--attest-audit-log`, `--attest-contract`, `--attest-pre-state`, `--attest-sign-key`, `--backend`, `--browser-cdp-endpoint`, `--browser-page-url`, `--cloud-execution-id`, `--code-runtime-environment-digest`, `--code-trust`, `--config`, `--dry-run`, `--durable`, `--effects-base-url`, `--effects-kind`, `--effects-root`, `--execute-environment-id`, `--execute-token`, `--execute-url`, `--explain`, `--headed`, `--hold-on-halt`, `--linux-allow-physical-input`, `--linux-app`, `--linux-window-title`, `--macos-app`, `--macos-window-title`, `--managed-dispatch-file`, `--param`, `--params-file`, `--pin-digest`, `--pin-version`, `--policy`, `--process-environment-id`, `--process-receipt-issuer-key-id`, `--process-receipt-private-key`, `--process-runner-id`, `--profile`, `--qualification-authority-file`, `--qualification-trust`, `--rdp-host`, `--rdp-readiness-text`, `--rdp-window`, `--rdp-window-title`, `--record-video`, `--report`, `--run-dir`, `--save-healed-to`, `--started-by`, `--strict-templates`, `--url`, `--worklist` | ## `sanitize` @@ -533,6 +628,33 @@ Score training episodes by reading the system of record through an independent o |---|---| | `openadapt flow serve-reward` | `--contract`, `--data-dir`, `--host`, `--port`, `--seed-mockmed`, `--token` | +## `service` + +Unattended on-prem service: run, health, restart, egress audit + +| Command | Long options | +|---|---| +| `openadapt flow service` | | +| `openadapt flow service boot-check` | `--account`, `--root` | +| `openadapt flow service egress-audit` | `--interval`, `--pid`, `--root`, `--samples` | +| `openadapt flow service health` | `--label`, `--max-age`, `--restart-on-fail`, `--root`, `--stuck-after` | +| `openadapt flow service notices` | `--ack`, `--all`, `--by`, `--root` | +| `openadapt flow service pf` | | +| `openadapt flow service pf check` | `--pf-conf`, `--user` | +| `openadapt flow service pf render` | `--allow-file`, `--out`, `--user` | +| `openadapt flow service restart` | `--label` | +| `openadapt flow service run` | `--allow-outbound`, `--max-cycles`, `--poll-seconds`, `--queue-script`, `--root` | + +- `service boot-check`: After a restart: wait for the service account to sign in; alert if late +- `service egress-audit`: Sample the service process tree's sockets; exit 0 only if no peer is off the machine +- `service health`: Check the service; exit 1 when unhealthy +- `service notices`: List or acknowledge service notices +- `service pf`: macOS pf rules for the service account (text only) +- `service pf check`: Check a rendered anchor (and pf.conf) +- `service pf render`: Render the anchor from the allow list +- `service restart`: Ask the service manager to restart the service +- `service run`: Run the queue loop (what launchd/systemd keeps alive) + ## `teach` Self-serve HALT -> LEARN: resolve a halted run from a fix demonstration. Induces the correction as a guarded exception branch, gates + validates it, and writes an updated bundle ONLY if it passes (governed refusal otherwise; nonzero exit) diff --git a/scripts/flow_cli_reference.py b/scripts/flow_cli_reference.py index d37826b3..e5df6d48 100644 --- a/scripts/flow_cli_reference.py +++ b/scripts/flow_cli_reference.py @@ -41,10 +41,7 @@ #: Until FLOW_VERSION is on PyPI, build it from this OpenAdapt commit instead, #: which holds everything the release ships. Set it to None on release day and #: rerun `snapshot`; the snapshot must not change. -FLOW_SOURCE: str | None = ( - "openadapt-flow @ git+https://github.com/OpenAdaptAI/OpenAdapt" - "@9104577e603e030f89318ead9cd9d586d439aefe#subdirectory=packages/flow" -) +FLOW_SOURCE: str | None = None SNAPSHOT = ROOT / "tests" / "fixtures" / f"flow_cli_{FLOW_VERSION}.json" PAGE = ROOT / "docs" / "reference" / "flow-commands.md" CURATED_PAGE = ROOT / "docs" / "reference" / "cli.md" diff --git a/tests/fixtures/flow_cli_2.2.0.json b/tests/fixtures/flow_cli_2.2.0.json index 08416af1..1cb43d6a 100644 --- a/tests/fixtures/flow_cli_2.2.0.json +++ b/tests/fixtures/flow_cli_2.2.0.json @@ -1,6 +1,6 @@ { "flow_version": "2.2.0", - "source": "argparse parser of openadapt-flow 2.2.0 from OpenAdapt main, before the release, (openadapt_flow.__main__.build_parser), long options only. Written by scripts/flow_cli_reference.py snapshot.", + "source": "argparse parser of openadapt-flow 2.2.0 from PyPI (openadapt_flow.__main__.build_parser), long options only. Written by scripts/flow_cli_reference.py snapshot.", "global_options": [ "--help", "--version" @@ -26,6 +26,19 @@ "--original", "--reviewer" ], + "approver-grant": [ + "--help" + ], + "approver-grant sign": [ + "--help", + "--key", + "--key-dir", + "--out" + ], + "approver-grant verify": [ + "--help", + "--key-dir" + ], "attach-seal": [ "--cloud-url", "--execution-id", @@ -294,6 +307,64 @@ "explain": [ "--help" ], + "fleet": [ + "--fleet-dir", + "--help" + ], + "fleet allow": [ + "--admin", + "--app-family", + "--domain", + "--export", + "--help", + "--import" + ], + "fleet disallow": [ + "--app-family", + "--domain", + "--export", + "--help", + "--import" + ], + "fleet policy": [ + "--help" + ], + "fleet policy keygen": [ + "--help", + "--out", + "--unencrypted" + ], + "fleet policy sign": [ + "--approver-id", + "--approver-name", + "--approver-title", + "--attest-security-official", + "--authorised-at", + "--draft", + "--help", + "--key", + "--out", + "--security-official-name", + "--security-official-title" + ], + "fleet policy verify": [ + "--help", + "--policy" + ], + "fleet roles": [ + "--approver", + "--author", + "--help", + "--public-key" + ], + "fleet simulate": [ + "--help", + "--out" + ], + "fleet status": [ + "--help", + "--json" + ], "for-each": [ "--help", "--loop-var", @@ -402,10 +473,59 @@ "--help", "--key-dir" ], + "known-ids": [ + "--help" + ], + "known-ids confirm": [ + "--by", + "--column", + "--confirmation", + "--exported-at", + "--help", + "--source", + "--yes" + ], + "known-ids status": [ + "--column", + "--confirmation", + "--help", + "--max-age-hours" + ], "lint": [ "--help", "--strict" ], + "local-seal": [ + "--help", + "--home" + ], + "local-seal batch": [ + "--help" + ], + "local-seal file": [ + "--help", + "--kind", + "--out", + "--run-id" + ], + "local-seal key": [ + "--help" + ], + "local-seal proof": [ + "--help" + ], + "local-seal run": [ + "--help" + ], + "local-seal submit": [ + "--help", + "--tsa-url" + ], + "local-seal verify": [ + "--help", + "--keys", + "--seal" + ], "login": [ "--allow-plaintext-token", "--destination-kind", @@ -422,6 +542,27 @@ "--help", "--out" ], + "no-outbound": [ + "--help" + ], + "no-outbound allow": [ + "--file", + "--help" + ], + "no-outbound allow add": [ + "--help", + "--port" + ], + "no-outbound allow derive": [ + "--help" + ], + "no-outbound allow list": [ + "--help", + "--json" + ], + "no-outbound allow remove": [ + "--help" + ], "process": [ "--admission", "--after", @@ -632,6 +773,7 @@ "--run-dir", "--run-id", "--save-healed-to", + "--started-by", "--url" ], "qualify schema": [ @@ -691,6 +833,7 @@ "--task", "--until-cmd", "--url", + "--verify", "--window", "--window-title", "--with-check" @@ -856,6 +999,7 @@ "--rdp-window-title", "--report", "--require-approval", + "--started-by", "--url" ], "review-sanitized": [ @@ -928,6 +1072,7 @@ "--report", "--run-dir", "--save-healed-to", + "--started-by", "--strict-templates", "--url", "--worklist" @@ -964,6 +1109,62 @@ "--seed-mockmed", "--token" ], + "service": [ + "--help" + ], + "service boot-check": [ + "--account", + "--help", + "--root" + ], + "service egress-audit": [ + "--help", + "--interval", + "--pid", + "--root", + "--samples" + ], + "service health": [ + "--help", + "--label", + "--max-age", + "--restart-on-fail", + "--root", + "--stuck-after" + ], + "service notices": [ + "--ack", + "--all", + "--by", + "--help", + "--root" + ], + "service pf": [ + "--help" + ], + "service pf check": [ + "--help", + "--pf-conf", + "--user" + ], + "service pf render": [ + "--allow-file", + "--help", + "--out", + "--user" + ], + "service restart": [ + "--help", + "--label" + ], + "service run": [ + "--allow-outbound", + "--help", + "--max-cycles", + "--poll-seconds", + "--queue-script", + "--root" + ], "teach": [ "--bundle", "--fix", @@ -1097,6 +1298,9 @@ "admit status": "Print admitted vs not-admitted for Flow from the public ledger. Never mints a signature.", "approve": "Record an authenticated approval (approver / resolution / bundle version) authorizing a durably-paused run to resume", "approve-sanitized": "Approve and freeze the exact reviewed derivative as an immutable archive", + "approver-grant": "Sign or check a grant that lets named agents or systems answer some saves. Only a registered person signs, at a terminal", + "approver-grant sign": "Sign a grant with your policy-signer key", + "approver-grant verify": "Check a signed grant against the registry", "attach-seal": "Attach Cloud's signed Execute receipt (the Seal) to a VERIFIED run report", "bench": "Replay a bundle N times against MockMed and aggregate", "benchmark": "Benchmark compiled replay vs. a Claude computer-use agent on the MockMed triage task (agent arm needs an Anthropic API key and costs real money)", @@ -1125,12 +1329,22 @@ "countersign resolve": "Resolve an exception with a later verified Seal that links to it", "countersign timestamp-baseline": "Timestamp a baseline read before the executor runs", "countersign verify": "Check a Seal offline and show executor, verification tier, timestamps, and disclosures", - "demo": "See the proof in one command: a checked run and a run where the app lies, side by side on one page (fake clinic data, no AI calls)", + "demo": "See the proof in one command: a checked run and a run whose save fails, side by side on one page (fake clinic data, no AI calls)", "demo-record": "Serve MockMed and record the canonical triage demo", "disambiguate": "Surface compile-time multiple-choice questions for an ambiguous demo and apply the answers as guards/params (ask, don't guess)", "emit-mcp": "Emit a standalone MCP server.py for a bundle", "emit-skill": "Emit an Agent Skills folder for a bundle", "explain": "Plain-language read of a completed run dir: what happened, why the outcome is the safe one, and the next suggested command (read-only)", + "fleet": "Fleet drift patch settings: opt-in per app family, roles, and the signed promotion policy. Off for every app by default.", + "fleet allow": "Allow patch export and/or import for an app family or domain", + "fleet disallow": "Remove an app family or domain from the allowlist", + "fleet policy": "Sign or verify the promotion policy", + "fleet policy keygen": "Create a policy approver key (PEM, 0600)", + "fleet policy sign": "Sign a policy draft as a policy approver", + "fleet policy verify": "Verify the signed policy against roles.json", + "fleet roles": "Add a workflow author or a policy approver (never both)", + "fleet simulate": "Run the three-tenant local simulator", + "fleet status": "Show opt-in, roles, policy and trust", "for-each": "Author a DATA-DRIVEN LOOP bundle: wrap an existing single-demonstration bundle's linear body in a LOOP that runs once per record of a worklist (CSV/JSON), binding each record's columns to the workflow's parameters. Emits a program:true bundle the runtime executes bounded, $0, identity-gated and effect-verified per record", "induce": "Induce a parameterized PROGRAM bundle from MULTIPLE recordings (multi-trace induction: infer params / loops / branches). REFUSES (nonzero exit, no bundle) when intent is underdetermined, printing a record-next worklist of missing demonstrations", "intake": "Fill a workflow's parameters from a referral PDF, fax image, or email", @@ -1146,9 +1360,26 @@ "jit review": "Confirm the parameters, then record that you reviewed every step", "jit sign-policy": "Sign an admission policy with your policy-signer key", "jit status": "Print the state, steps and what admission needs", + "known-ids": "Admit a system-of-record export as the known-ID list, and check it", + "known-ids confirm": "Confirm the export's source and completeness (recorded)", + "known-ids status": "Would replay accept this list now?", "lint": "Report a bundle's coverage gaps (unarmed clicks, vacuous postconditions, under-classified risk); exits nonzero by severity", + "local-seal": "Offline Seals signed with this machine's key; optional batched timestamps", + "local-seal batch": "Fold queued Seals into one Merkle batch (local)", + "local-seal file": "Seal any file: a record, an export, an audit report", + "local-seal key": "Print this machine's public Seal key for pinning", + "local-seal proof": "Show the batch, root and inclusion proof for a Seal", + "local-seal run": "Seal a run directory's report.json", + "local-seal submit": "Timestamp every unsent batch root (sends one 32-byte digest per batch)", + "local-seal verify": "Verify a run directory, or a file with --seal", "login": "Validate an ingest token against the hosted control plane and store it in the OS keychain", "materialize-approved": "Rebuild an approved derivative from its archive and approval record", + "no-outbound": "Strict no-outbound mode: list and edit the allow list", + "no-outbound allow": "The allow-list file", + "no-outbound allow add": "Allow an IP address or private CIDR range by hand", + "no-outbound allow derive": "Allow only the host(s) a remote-desktop session profile connects to", + "no-outbound allow list": "Show every allowed address and where it came from", + "no-outbound allow remove": "Remove an address or range", "process": "Author a ProcessContract parent from independently admitted capabilities. Each --child needs a --admission envelope. certify/run the output directory; replay refuses it.", "propose-qualification": "Draft a production-shaped qualification contract from a compiled demo. Alias of `qualify propose`.", "pull": "Download a packaged workflow bundle by name", @@ -1204,6 +1435,16 @@ "seal": "Copy a bundle to a new path, encrypt its workflow and template evidence with OPENADAPT_BUNDLE_KEY, verify integrity, and publish it atomically", "serve-execute": "Host the public Execute v1 HTTP+MCP contract locally. Receipts are self-signed with a local key. They are not OpenAdapt production Seals. Needs `pip install 'openadapt-flow[execute]'`", "serve-reward": "Score training episodes by reading the system of record through an independent oracle. Receipts are self-signed reward receipts, not Execute Seals. Needs `pip install 'openadapt-flow[reward]'`", + "service": "Unattended on-prem service: run, health, restart, egress audit", + "service boot-check": "After a restart: wait for the service account to sign in; alert if late", + "service egress-audit": "Sample the service process tree's sockets; exit 0 only if no peer is off the machine", + "service health": "Check the service; exit 1 when unhealthy", + "service notices": "List or acknowledge service notices", + "service pf": "macOS pf rules for the service account (text only)", + "service pf check": "Check a rendered anchor (and pf.conf)", + "service pf render": "Render the anchor from the allow list", + "service restart": "Ask the service manager to restart the service", + "service run": "Run the queue loop (what launchd/systemd keeps alive)", "teach": "Self-serve HALT -> LEARN: resolve a halted run from a fix demonstration. Induces the correction as a guarded exception branch, gates + validates it, and writes an updated bundle ONLY if it passes (governed refusal otherwise; nonzero exit)", "teach-branch": "Learn a guarded branch from two or more halts resolved the same way. The branch keys on the screen text they share (narrowed by an enum parameter when normal runs show that text too) and is written as a repair candidate that needs approval", "triage": "Order verified saves for review and propose one batch approval",