Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions cli-reference/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# 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.
#
# The Flow pin must name the same version as FLOW_VERSION in
# scripts/flow_cli_reference.py.
openadapt==1.17.0
openadapt-flow==2.1.0 # must equal FLOW_VERSION in scripts/flow_cli_reference.py
openadapt-agent==2.2.0
openadapt-flow==2.2.0 # must equal FLOW_VERSION in scripts/flow_cli_reference.py
openadapt-agent==2.2.1
# The launcher's help is formatted by click; pin it so a click release can't
# change the page without a change here.
click==8.5.0
2 changes: 1 addition & 1 deletion docs/explanation/surfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
63 changes: 37 additions & 26 deletions docs/explanation/verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

<!-- The R0–R3 read-back ladder from the reopen read-back work goes here when
it ships: one more column naming each row's rung, and minimum_readback and
checked_how in "What each result needs". Until then, these are the tiers
Flow uses. -->

| 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.
Expand Down
54 changes: 43 additions & 11 deletions docs/how-to/certify-and-qualify.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:

Expand All @@ -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/
Expand All @@ -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.
Expand All @@ -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
Expand Down
95 changes: 82 additions & 13 deletions docs/how-to/connect-an-agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -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_<workflow>` | 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

Expand Down
Loading
Loading