openadapt-agent 2.2.1__tar.gz → 2.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/CHANGELOG.md +5 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/PKG-INFO +142 -15
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/README.md +139 -12
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/docs/DESIGN.md +18 -0
- openadapt_agent-2.2.2/docs/task-ledger-measurement.json +121 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/manifest.json +1 -1
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/pyproject.toml +16 -3
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/scripts/capture_sandbox_examples.py +1 -1
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/scripts/check_dist.py +1 -1
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/scripts/check_source_boundary.py +1 -1
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/scripts/smoke_client.py +1 -1
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/server.json +2 -2
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/conftest.py +14 -0
- openadapt_agent-2.2.2/tests/ledger_demo.py +735 -0
- openadapt_agent-2.2.2/tests/test_admission_tools.py +312 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_approval_flow.py +41 -1
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_authoring_native.py +98 -0
- openadapt_agent-2.2.2/tests/test_guard_tools.py +143 -0
- openadapt_agent-2.2.2/tests/test_ledger.py +651 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_partner_kit.py +4 -34
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_repair.py +3 -1
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_repair_e2e.py +32 -2
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/uv.lock +111 -16
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/.github/CODEOWNERS +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/.github/dependabot.yml +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/.github/workflows/ci.yml +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/.github/workflows/codeql.yml +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/.github/workflows/dependency-review.yml +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/.github/workflows/release.yml +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/.github/workflows/secret-scan.yml +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/.gitignore +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/.gitleaks.toml +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/.mcpbignore +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/LICENSE +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/SECURITY.md +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/docs/DISTRIBUTION.md +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/docs/MAILBOX_CLI.md +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/docs/REPAIR.md +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/docs/examples/sandbox-results.json +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/scripts/check_mcpb.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/scripts/check_release_artifacts.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/skills/openadapt-gui-write/SKILL.md +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/source-policy.public.json +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/src/openadapt_agent/__init__.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/src/openadapt_agent/mcpb_entry.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/fixtures/cloud-ts-signed-seal.json +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_approval.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_attended_bridge.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_authoring.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_bridge.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_cards.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_cli.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_contract.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_delegated_approval.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_distribution.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_mailbox.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_mcp_server.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_mcpb_entry.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_public_lifecycle.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_readme_commands.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_repair_branch.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_repair_hold.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_runner.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_runs.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_sandbox.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_sandbox_flow.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_seal.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_seal_cli.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_seal_e2e.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_service.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_shim.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_skill.py +0 -0
- {openadapt_agent-2.2.1 → openadapt_agent-2.2.2}/tests/test_tutorial.py +0 -0
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Releases up to v2.2.0 are recorded in the [openadapt-agent repository](https://github.com/OpenAdaptAI/openadapt-agent/blob/main/CHANGELOG.md).
|
|
4
4
|
|
|
5
|
+
## v2.2.2 (2026-11-14)
|
|
6
|
+
|
|
7
|
+
- Accept `openadapt-flow` 3.x. Flow 3 moved its vision stack to the `vision`
|
|
8
|
+
extra, so this package now requires `openadapt-flow[vision]>=2.2.0,<4`.
|
|
9
|
+
|
|
5
10
|
## v2.2.1 (2026-10-18)
|
|
6
11
|
|
|
7
12
|
First release from the [OpenAdapt monorepo](https://github.com/OpenAdaptAI/OpenAdapt) (`packages/agent`). The MCP agent server now lives in `openadapt_flow.agent`; this package is a compatibility package over it, so `openadapt-agent` and `import openadapt_agent` keep working. It requires `openadapt-flow>=2.2.0,<3`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openadapt-agent
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.2
|
|
4
4
|
Summary: Let your AI agent enter information in apps with no API and get back whether it saved (MCP and Agent Skills)
|
|
5
5
|
Project-URL: Homepage, https://github.com/OpenAdaptAI/OpenAdapt/tree/main/packages/agent
|
|
6
6
|
Project-URL: Repository, https://github.com/OpenAdaptAI/OpenAdapt/tree/main/packages/agent
|
|
@@ -24,7 +24,7 @@ Requires-Dist: anyio>=4.0
|
|
|
24
24
|
Requires-Dist: cryptography>=50.0.0
|
|
25
25
|
Requires-Dist: jsonschema>=4.20.0
|
|
26
26
|
Requires-Dist: mcp<3,>=1.28
|
|
27
|
-
Requires-Dist: openadapt-flow<
|
|
27
|
+
Requires-Dist: openadapt-flow[vision]<4,>=2.2.0
|
|
28
28
|
Provides-Extra: dev
|
|
29
29
|
Requires-Dist: build>=1.2.0; extra == 'dev'
|
|
30
30
|
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
@@ -32,7 +32,7 @@ Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
|
32
32
|
Provides-Extra: repair-screens
|
|
33
33
|
Requires-Dist: openadapt-privacy[presidio]>=1.1.2; extra == 'repair-screens'
|
|
34
34
|
Provides-Extra: tutorial
|
|
35
|
-
Requires-Dist: openadapt-flow[browser,macos]<
|
|
35
|
+
Requires-Dist: openadapt-flow[browser,macos,vision]<4,>=2.2.0; extra == 'tutorial'
|
|
36
36
|
Description-Content-Type: text/markdown
|
|
37
37
|
|
|
38
38
|
# OpenAdapt Agent
|
|
@@ -46,7 +46,7 @@ Your AI agent decides what to enter. OpenAdapt enters it in the app and checks t
|
|
|
46
46
|
|
|
47
47
|
Use it if your agent reads referrals, intake forms, or authorizations and works out what belongs in the record, but a person still types the result into an app with no usable API. Examples are an electronic medical record (EMR) and a payer portal. This package is the tool your agent calls to do that typing.
|
|
48
48
|
|
|
49
|
-
- A person or your agent shows OpenAdapt the task in the real app. OpenAdapt replays those steps, and by default a run makes no AI model
|
|
49
|
+
- A person or your agent shows OpenAdapt the task in the real app. OpenAdapt replays those steps, and by default a run makes no calls to a hosted AI model.
|
|
50
50
|
- Before it types, OpenAdapt checks that the right record is open. After it saves, it reads the record back.
|
|
51
51
|
- Your agent gets one of four outcomes: done and checked, stopped before saving, check the record, or didn't run. When OpenAdapt can't confirm a save, it says so, and a person checks the record.
|
|
52
52
|
- It runs on your customer's computer, one that can already open the app. Your agent, or a small program acting for it, has to run on that computer too, since it starts OpenAdapt and talks to it directly. If your agent runs in your own cloud, ask us about a hosted route.
|
|
@@ -73,7 +73,7 @@ openadapt-agent emit-skill <bundle> --out ~/.claude/skills
|
|
|
73
73
|
| Type the values and save | Staff, retyping your agent's output | OpenAdapt, in the same screens staff use |
|
|
74
74
|
| Confirm the save | Staff, if they look again | OpenAdapt reads the record back before it reports done |
|
|
75
75
|
| Something doesn't match | Staff notice while typing, or someone finds it later | OpenAdapt stops before saving, and the run waits for a person |
|
|
76
|
-
| What staff handle | Every document | Runs that stopped and records to check.
|
|
76
|
+
| What staff handle | Every document | Runs that stopped and records to check. For work in a [task-ledger job](#track-a-long-job), each one is a question on their phone or desktop; otherwise someone checks the list of stopped runs. |
|
|
77
77
|
|
|
78
78
|
For scale, two hospital time studies put keying one faxed referral into an electronic record at about 10 to 12 minutes ([UCSF, JAMIA Open 2020](https://pmc.ncbi.nlm.nih.gov/articles/PMC7660949/); [Calderdale and Huddersfield NHS Foundation Trust, HFMA 2024](https://www.hfma.org.uk/system/files/2024-04/Using%20digital%20technologies%20to%20process%20admin%20tasks%20Case%20Study%20v7.pdf)). We don't publish a time-saved number until a deployment measures one.
|
|
79
79
|
|
|
@@ -97,7 +97,7 @@ flowchart LR
|
|
|
97
97
|
| What your product does with each outcome | One result per request, and no second entry when your agent repeats a `request_id` |
|
|
98
98
|
| Your tests against the four outcomes | A sandbox that returns each outcome on demand |
|
|
99
99
|
|
|
100
|
-
Your customer provides the computer and a person for runs that stop. A person or an agent shows the task, in web apps, native Windows apps, and
|
|
100
|
+
Your customer provides the computer and a person for runs that stop. A person or an agent shows the task, in web apps, native Windows apps, and remote-desktop sessions. A person accepts the result before it runs. See [Record a first workflow](#record-a-first-workflow), or [talk to us](https://openadapt.ai/qualify?why=embed) about setting it up.
|
|
101
101
|
|
|
102
102
|
## Try it in three steps
|
|
103
103
|
|
|
@@ -181,6 +181,8 @@ The simulated sandbox suggests the same command in its `list_workflows` result.
|
|
|
181
181
|
| `did_not_run` (Didn't run) | This call wrote nothing. With `request_id_conflict`, an earlier request with the same `request_id` may have saved. | If `safe_to_retry` is true, fix the cause and resend the same request. Otherwise follow `next_action`. |
|
|
182
182
|
| `running` (Running) | The run didn't finish within `wait_seconds` (default 60). | Call `get_run` with the `run_id` until `outcome` changes. |
|
|
183
183
|
|
|
184
|
+
`checked_how` says how the save was checked, when the run recorded it: `system_of_record` (Checked in your system's records), `separate_session` (Checked in a separate session), `reopened` (Reopened in the same session), `same_screen` (The screen said it saved), or `unchecked`. Only `separate_session` or stronger makes a run `done`. A save that was confirmed only by reopening it in the same session, or on the screen it left, is `not_sure_if_saved` with reason `checked_below_minimum`. A workflow can require `system_of_record`.
|
|
185
|
+
|
|
184
186
|
Branch on `outcome` and `safe_to_retry`. `next_step` names the next move as a fixed code, so your code doesn't have to parse `next_action`: `none`, `call_get_run`, `wait_for_person`, `check_record`, `fix_inputs`, `fix_setup`, or `retry`.
|
|
185
187
|
|
|
186
188
|
### Resend a request
|
|
@@ -273,6 +275,8 @@ asyncio.run(main())
|
|
|
273
275
|
|
|
274
276
|
The SDK starts the server with only a few environment variables. To pass others, such as `OPENADAPT_BUNDLE_KEY`, add `env=` to `StdioServerParameters`.
|
|
275
277
|
|
|
278
|
+
To let your agent add workflows itself, see [Teach a new workflow from your agent](#teach-a-new-workflow-from-your-agent).
|
|
279
|
+
|
|
276
280
|
[scripts/smoke_client.py](scripts/smoke_client.py) is a fuller example that runs every sandbox case. From a clone of this repository, run `pip install -e .`, then:
|
|
277
281
|
|
|
278
282
|
```bash
|
|
@@ -317,8 +321,10 @@ Flow's [execution profiles](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/p
|
|
|
317
321
|
| `--config YAML` | Flow's deployment config ([example](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/deployment.example.yaml)), which sets the profile and whether model grounding is allowed. |
|
|
318
322
|
| `--policy NAME-OR-PATH` | The policy Flow certifies against, such as `clinical-write` or `permissive`. |
|
|
319
323
|
| `--timeout SECONDS` | How long one run may work (default 600). Time a person spends approving a save doesn't count. A run stopped at the limit is `not_sure_if_saved`. |
|
|
320
|
-
| `--approval-mode MODE` | Whether each save waits for approval. Without it, Flow decides
|
|
321
|
-
| `--delegate-approvals AGENT_ID` | Lets your agent answer a waiting save as `AGENT_ID` (an opaque id such as `agent:intake-bot`), with `list_approval_requests` and `answer_approval_request`. Flow accepts the 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. Needs
|
|
324
|
+
| `--approval-mode MODE` | Whether each save waits for approval. Without it, Flow decides, and a run stops before each save until it's approved (`each_item`). `not_gated` turns that off for every run this server starts; each result records it under `technical.approval_mode`. |
|
|
325
|
+
| `--delegate-approvals AGENT_ID` | Lets your agent answer a waiting save as `AGENT_ID` (an opaque id such as `agent:intake-bot`), with `list_approval_requests` and `answer_approval_request`. Flow accepts the 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. Needs openadapt-flow 2.2.0 or later. |
|
|
326
|
+
| `--admissions DIR` | Adds `prepare_admission`, `list_admissions` and `decide_admission`, the same steps as `openadapt-flow admit`, over recordings in `DIR/recordings` and shadow candidates in `DIR/shadow-candidates`. A person decides in your client's form. With `--delegate-approvals` and `--approver-grant`, your agent decides as that approver instead, under a grant a person signed that names it. It never decides an admission it prepared or one prepared on a server without `--delegate-approvals`, and never under the `regulated` policy pack. Accepting sets the workflow's runs to `problems_only`. The record names who decided, and for your agent the grant and who signed it. |
|
|
327
|
+
| `--approver-grant FILE` | With `--admissions` and `--delegate-approvals`: the approver grant a person signed (`openadapt-flow approver-grant sign`) that lets your agent decide admissions. Flow checks it on every decision. Without it only a person decides. |
|
|
322
328
|
| `--headed` | Shows the browser. A person needs it to continue or skip a stopped run in a web app. |
|
|
323
329
|
| `--extra-run-arg ARG`, `--flow-cli CMD` | Change how Flow is called. Either one can relax Flow's checks, for example `--extra-run-arg=--allow-unencrypted`. |
|
|
324
330
|
| `--allow-protected-export`, `--allow-synthetic-recorded-defaults`, `--allow-model-grounding` | Off by default. See [Security](#security). |
|
|
@@ -327,7 +333,7 @@ Callers can't change `--url`, `--config`, `--policy`, `--timeout`, or `--approva
|
|
|
327
333
|
|
|
328
334
|
### Runs that wait for a person's approval
|
|
329
335
|
|
|
330
|
-
|
|
336
|
+
Unless the operator names another `--approval-mode`, Flow stops before each save and waits for an answer (up to 15 minutes by default). While it waits, `run_workflow` and `get_run` return `needs_review` with reason `waiting_for_approval` and `record_changed: "unknown"`, because an earlier save in the same run may have gone through. The person sees the waiting save with `openadapt-flow item-approval show RUN_DIR`, where `RUN_DIR` is the `run_id` folder in the runs folder, and answers with `item-approval approve` or `decline`. If nobody answers in time, the run stops before saving and reads `needs_review` with reason `approval_needed`. `get_approval_mode` reports the mode and whether your agent may answer. With `--delegate-approvals`, your agent can list waiting saves with `list_approval_requests` and answer one with `answer_approval_request`, where the run's approver policy allows an agent for that step; otherwise a person answers.
|
|
331
337
|
|
|
332
338
|
### Use the other tools
|
|
333
339
|
|
|
@@ -337,7 +343,7 @@ With `--bundles`, the server also registers read-only tools for workflows and ru
|
|
|
337
343
|
|
|
338
344
|
### Decide stopped runs
|
|
339
345
|
|
|
340
|
-
A run that stops for a person waits in Flow's Needs Attention list on that computer. Nobody is notified
|
|
346
|
+
A run that stops for a person waits in Flow's Needs Attention list on that computer. `list_needs_attention` shows the list. Nobody is notified, unless the run is part of a [task-ledger job](#track-a-long-job): then the ledger asks a person on their phone or desktop.
|
|
341
347
|
|
|
342
348
|
With `--mode attended`, your agent can send a decision with `continue_attention`, `skip_attention`, `reject_attention`, `teach_attention`, or `escalate_attention`. The person at the computer confirms each decision in a form. Continue and skip also need `--config`. If the client can't show forms, the server refuses the decision, and the person uses Flow's console instead:
|
|
343
349
|
|
|
@@ -399,6 +405,8 @@ For a native Windows app, start Flow's Windows agent server in the signed-in des
|
|
|
399
405
|
|
|
400
406
|
For an RDP or Citrix session, pass `--remote-window APP` (the client app, for example `"Microsoft Remote Desktop"`) or `--citrix`, and `--remote-window-title` when the app has more than one window. A remote session is screen-only: there's no DOM or UI Automation tree, so `observe` returns no nodes and your agent clicks x,y pixels in the client window and presses keys, the way a person would. To let your agent see that window, add `--authoring-screens scrubbed`, which turns on a `screen` tool that returns the frame with identifiers scrubbed and shares nothing if scrubbing fails. `--authoring-screens unscrubbed` skips scrubbing. Screens are off by default.
|
|
401
407
|
|
|
408
|
+
You can also name the app with a deployment file instead of these flags: `--authoring --config deploy.yaml --authoring-from-config`. The file's `backend:` section names a Windows app (`agent_url`), one macOS window (`macos_app`, optionally `macos_window_title`), one Linux window (`linux_app` and `linux_window_title`), or a local RDP or Citrix client window (`rdp_window`). It is the same section `openadapt-flow run --config` uses, so the recording and its runs drive the same app. This is how you record a Linux app, or a macOS app that isn't the frontmost window. Your agent clicks at window coordinates, so a Linux section also needs `linux_allow_physical_input: true`. Network RDP (`rdp_host`) is refused for authoring, because each run would open its own session and disconnect the recording's. Without `--authoring-from-config`, `--config` only configures runs.
|
|
409
|
+
|
|
402
410
|
[Recording on Windows, RDP and Citrix](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/RECORDING_ON_WINDOWS_RDP_CITRIX.md) explains the Windows agent server, why it's needed when the agent runs on another computer, how to start it, the token, and the RDP and Citrix pixel path.
|
|
403
411
|
|
|
404
412
|
The recording marks every step as the agent's or the person's (`actor` in `events.jsonl`, `demonstrated_by` in `meta.json`), and a person still accepts the result. On Windows with none of these flags, the tools can only tell the person what to click.
|
|
@@ -415,6 +423,123 @@ ChatGPT and Claude in a web browser can't start a server on your computer. To re
|
|
|
415
423
|
|
|
416
424
|
It claims the job, polls openadapt.ai over outgoing HTTPS, and asks you in the terminal before a chat account may drive it. Don't confuse it with the launcher's `openadapt connect`, which pairs a computer with OpenAdapt Cloud. [docs/MAILBOX_CLI.md](docs/MAILBOX_CLI.md) has the details.
|
|
417
425
|
|
|
426
|
+
### Guard an agent you already run
|
|
427
|
+
|
|
428
|
+
If your agent already drives a Chromium (a Playwright script, Playwright MCP, browser-use, or a computer-use agent with a browser), you don't have to move it onto these tools. Start its browser with a DevTools port, which is the one line you add, and run the guard next to it for each task:
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
openadapt-flow guard --cdp http://127.0.0.1:9222 --config guard.yaml --task task.json --out runs/task-0001
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
The guard holds every save the agent's tab sends until the record on the screen is the one `task.json` names. When it isn't, the save is blocked and the agent's page says why. A save that goes through is read back from the system of record (`VERIFIED`, `REFUTED` or `RECONCILIATION_REQUIRED`). `--observe-only` never blocks and shows what the guard would have done.
|
|
435
|
+
|
|
436
|
+
When a run reads back correctly, it becomes a draft in the teach dir that `guard.yaml` names, with a record check built from the app's own reads. A person confirms the check and admits the draft (`openadapt-flow compile-agent admit`); then `serve --teach verify.yaml --teach-dir <that dir>` serves it as a `taught_<name>` tool that makes no model calls. [GUARD_AN_AGENT.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/GUARD_AN_AGENT.md) has the files, what counts as a save, and the measurements.
|
|
437
|
+
|
|
438
|
+
### Teach a new workflow from your agent
|
|
439
|
+
|
|
440
|
+
`serve --teach` lets your agent add a workflow it doesn't have yet, without leaving the conversation. It does the task once through OpenAdapt; OpenAdapt checks the record it saved, compiles the run, and once the run is admitted your agent calls it as a tool. Every later call runs the compiled program with no model calls and reads the save back.
|
|
441
|
+
|
|
442
|
+
```bash
|
|
443
|
+
claude mcp add --scope user openadapt-teach -- uvx --python 3.12 --from 'openadapt-agent[tutorial]' openadapt-agent serve --teach verify.yaml --url https://emr-test.example.org --runs-dir ~/.openadapt/runs
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
`verify.yaml` needs only a `verifier:` section that reads the app's records (`rest`, `sql`, `fhir`, `graphql` or `file`), the same file `openadapt-flow record --verify` uses. The first run makes a real entry in the app, so point `--url` at a test copy with test records.
|
|
447
|
+
|
|
448
|
+
Your agent then:
|
|
449
|
+
|
|
450
|
+
1. Calls `teach_start` with the task (`{note}` for each input), this run's `inputs`, the `record_fields` the saved record must have (such as its patient id), and `on_screen`, the record's identity as the screen shows it (a name and an MRN).
|
|
451
|
+
2. Calls `teach_run` with short scripts (read the page, act on elements by role and name, read the result back; web apps), or `teach_observe` and `teach_act` one action at a time, until the task is done. After every save the gate detects, OpenAdapt reads the app's records back and tells the agent what landed; a write on a record the task does not name ends the run after it lands. On the web, `teach_observe` lists the page as short lines with a ref for each control, and before every save OpenAdapt checks that the record's identity is shown next to the control. A save on the wrong record is refused and ends the run.
|
|
452
|
+
3. Calls `teach_finish`. OpenAdapt compiles the run and reads the app's records: exactly one record must have appeared, with every `record_fields` value and every input. The result is the draft: its steps, its inputs, the proposed record check, and what still needs a person.
|
|
453
|
+
4. Calls `teach_confirm_checks`. Your MCP client shows a person the proposed check, and only their answer there, with their name, confirms it. Your agent has no argument that confirms it.
|
|
454
|
+
5. Calls `teach_admit` with `canary_inputs`, the values each canary saves. A canary is a real save on the same record, so use values that are safe there.
|
|
455
|
+
|
|
456
|
+
`teach_admit` admits only under the rules in [JIT_ADMISSION.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/JIT_ADMISSION.md). The operator starts the server with `--teach-key agent.pem --teach-policy signed-policy.json`: the key of the agent registered with `openadapt-flow jit register --role agent`, and an admission policy a person signed with `openadapt-flow jit sign-policy`. The server records the run as that agent, so admitting it is self-admission, which the policy must allow (`allow_self_admission`). Without a key and a policy, `teach_admit` refuses and returns the command a person runs at a terminal instead:
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
openadapt-flow compile-agent admit <task dir> --admit-as "<your name>" --canary-param note="Canary test note"
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Once the program is admitted, it is in `list_workflows` and `run_workflow` at once and has its own `taught_<name>` tool, and the server sends `notifications/tools/list_changed` to clients that support it. A server started later lists every program taught in its `--teach-dir`.
|
|
463
|
+
|
|
464
|
+
It works on a web app (`--url`) and on the app a deployment file's `backend:` section names (`--config deploy.yaml`): a native Windows, macOS or Linux window, or a local RDP or Citrix client window. Off the web, OpenAdapt can't read which record a form shows, so a person approves, in your client's form, every action that might save: any click except on a text field, a check box, a tab or a control that plainly navigates (Open, New, Cancel), any click at x,y, Enter, typing outside a text field, and shortcuts. Each answer covers that one action on the screen as it was; if anything changes first, the action isn't done. A client without forms can't save. RDP and Citrix have no control tree, and the Windows, macOS and Linux teach backends work from the screen, so off the web your agent needs `--teach-screens scrubbed` to see the screen, and clicks x,y pixels. Admission there fails closed: a pixel-only click carries no checked record identity, so the canary is refused and says why. Network RDP (`rdp_host`) is refused.
|
|
465
|
+
|
|
466
|
+
| Flag | What it does |
|
|
467
|
+
| --- | --- |
|
|
468
|
+
| `--teach VERIFY_YAML` | Adds `teach_start`, `teach_run` (web apps), `teach_observe`, `teach_act`, `teach_finish`, `teach_abort`, `teach_status`, `teach_confirm_checks` and `teach_admit`, and turns on runs of taught workflows. |
|
|
469
|
+
| `--teach-dir DIR` | Where drafts and taught programs live (default `RUNS_DIR/teach`). |
|
|
470
|
+
| `--teach-key PEM`, `--teach-policy JSON` | The registered agent's encrypted key (passphrase in `OPENADAPT_JIT_KEY_PASSPHRASE`) and the signed admission policy. Only an agent key under a policy; an admitter key admits from CI with `openadapt-flow jit admit`. |
|
|
471
|
+
| `--key-dir DIR` | The JIT key directory (default `OPENADAPT_JIT_KEY_DIR` or `~/.openadapt/jit`). |
|
|
472
|
+
| `--teach-hold` | On the web, a person also approves every save in the first run. Without it, your agent names the identity the save gate checks (`on_screen`); a value every form shows proves nothing, and then only the record check after the run and the person confirming it catch a wrong record. |
|
|
473
|
+
| `--teach-tasks DIR` | Run 1's record and inputs come from the operator's task files (`openadapt.guard-task/v1`); `teach_start` names one with `task_file`. Without it, your agent declares them at `teach_start`, before it reads the app. |
|
|
474
|
+
| `--teach-allow-origin URL` | Another origin run 1's browser may reach. Every origin but the app's is refused otherwise. |
|
|
475
|
+
| `--teach-write-adapter MODULE:ATTR` | The app's write adapter: each write in run 1 is read before it is sent, and one to another record, field or value is refused. |
|
|
476
|
+
| `--teach-screens scrubbed` | Adds `teach_screen`: the app's screen with identifiers scrubbed, and nothing shared if scrubbing fails. `unscrubbed` skips scrubbing. Off by default. |
|
|
477
|
+
| `--mode production --bundles DIR` | Serves your other workflows next to the taught ones. |
|
|
478
|
+
|
|
479
|
+
[COMPILE_AGENT.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/COMPILE_AGENT.md) describes the pipeline underneath: the harness, the record check, the canaries and the files it keeps.
|
|
480
|
+
|
|
481
|
+
## Track a long job
|
|
482
|
+
|
|
483
|
+
On long work, agents lose track of the rules they were given, miss information that arrives partway through, guess instead of asking, and skip checking their work ([OSWorld 2.0](https://arxiv.org/abs/2606.29537)). The task ledger keeps that state on the server, so your agent reads it instead of rebuilding it from its own history. It's on whenever runs or guarded saves are; `serve --no-ledger` turns it off.
|
|
484
|
+
|
|
485
|
+
| Tool | What it does |
|
|
486
|
+
| --- | --- |
|
|
487
|
+
| `open_job` | Opens a job with the user's intent, rules, and items. Rules are locked when the job opens. |
|
|
488
|
+
| `get_job` | Returns a short summary: current rules, counts by status, items not verified yet with their `request_id`, waiting questions, answers that came back, and what changed since the last read. |
|
|
489
|
+
| `add_items` | Adds up to 100 items per call, 500 per job. |
|
|
490
|
+
| `record_evidence` | Notes a fact and where it came from: `user` (trusted), or `app` or `document` (never trusted). |
|
|
491
|
+
| `ask_user` | Asks a person a yes-or-no question. Optional `wait_seconds`, 0 to 600. |
|
|
492
|
+
| `get_answer` | Returns `waiting`, `yes`, `no`, `expired`, or `refused`. Optional `wait_seconds`, 0 to 600. |
|
|
493
|
+
| `change_constraint` | Asks a person to change or add a rule. The old rule holds until they approve. |
|
|
494
|
+
| `close_job` | Closes the job. `complete` is true only when every item is verified and no question waits. `verified_items` lists what your agent may report as done. |
|
|
495
|
+
|
|
496
|
+
Each item has its own `request_id`, `<job_id>.<item_id>`. Pass it to `run_workflow`, and the server updates the item from its own result:
|
|
497
|
+
|
|
498
|
+
| Item status | Set when |
|
|
499
|
+
| --- | --- |
|
|
500
|
+
| `pending` | The item was added, a run didn't run, or a person asked for another try. |
|
|
501
|
+
| `running` | The run hasn't finished. |
|
|
502
|
+
| `verified` | The run ended `done`, a guarded save ended `VERIFIED`, or a person checked the record and said it saved. |
|
|
503
|
+
| `stopped_before_saving` | The run ended `needs_review`. |
|
|
504
|
+
| `check_the_record` | The run ended `not_sure_if_saved`, or a guarded save wasn't verified. |
|
|
505
|
+
| `saved_awaiting_check` | The save went through and waits for a later check, or a guarded save may write now. |
|
|
506
|
+
| `needs_person` | A question about the item waits, a save waits for approval, or a person said they'll handle it. |
|
|
507
|
+
|
|
508
|
+
### What the server enforces
|
|
509
|
+
|
|
510
|
+
- Your agent can't set a status. Only this server's results and a person's answers do.
|
|
511
|
+
- A rule can name a `run_workflow` input and say it `equals` a value, is `one_of` some values, or is `not_one_of` them. The server refuses a run for the job that breaks a rule or leaves that input out, and the error ends "Nothing ran." A rule with only `text` is shown but not checked.
|
|
512
|
+
- Only a person changes a rule after the job opens. That holds when the change comes from the user, too; the person confirms it.
|
|
513
|
+
- The server refuses a run for an item while a question about it waits, and while it's `check_the_record` or `needs_person`. After a person asks for another try, the item gets a new `request_id`, `<job_id>.<item_id>.r2`, and the old one is refused.
|
|
514
|
+
- A run with any other `request_id` doesn't change an item. While a job is open, its summary counts those runs under `unplanned_writes`.
|
|
515
|
+
- Evidence never changes a rule or a status.
|
|
516
|
+
|
|
517
|
+
### Questions reach a person
|
|
518
|
+
|
|
519
|
+
A question is a save-approval request, the same files Flow writes before a save. It shows on the paired phone and in Desktop's Run it, and a person answers there. Save means yes, Don't save means no, and the answer saves nothing in any app. The server checks each answer as a run checks a save approval: it must come from a person on Desktop or the phone, for that exact question, before it expired (4 hours). An answer from an agent or a script is refused.
|
|
520
|
+
|
|
521
|
+
When a run for an item stops before saving, the ledger asks whether to try again. When a save can't be confirmed, it asks the person to check the record. So a stop in a job reaches someone without anyone watching Needs Attention. Questions are written to `task-ledger` inside the runs folder. Point the console or Desktop at that runs folder.
|
|
522
|
+
|
|
523
|
+
### Measured on a synthetic worklist
|
|
524
|
+
|
|
525
|
+
A scripted agent worked a 30-item synthetic MockMed worklist. The user changes a rule at item 13, item 20's referral tells the reader to mark everything urgent, two patients share a name, and two saves can't be confirmed (one didn't land). The agent has the same flaws in every arm: it acts on a rule only while the message that stated it is among its last 5 turns, it follows instructions it reads in a referral, it picks the first matching patient, and it reports every run that wrote something as done. A simulated person answers through the same list the phone reads. No model is called.
|
|
526
|
+
|
|
527
|
+
| | Agent alone | Ledger, enforcement only | Agent follows the ledger |
|
|
528
|
+
| --- | --- | --- | --- |
|
|
529
|
+
| Items saved against a rule | 12 | 0 | 0 |
|
|
530
|
+
| Items guessed instead of asked | 2 | 2 | 0 |
|
|
531
|
+
| Unverified items reported as done | 2 | 1 | 0 |
|
|
532
|
+
| Correct records (of 30) | 16 | 27 | 30 |
|
|
533
|
+
| Runs the ledger refused | 0 | 12 | 0 |
|
|
534
|
+
|
|
535
|
+
In the middle arm, the agent opens a job and uses each item's `request_id` but keeps its flaws. The ledger stops every rule break, but it can't stop an agent from guessing a value or misreporting its own tally. `close_job` still lists what was verified, so a caller can check the agent's report. The scripted agent stands for a policy, not a model, so these numbers test the mechanism. They aren't a model benchmark. To reproduce them (about 3 seconds):
|
|
536
|
+
|
|
537
|
+
```bash
|
|
538
|
+
python packages/agent/tests/ledger_demo.py
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
Results: [docs/task-ledger-measurement.json](docs/task-ledger-measurement.json).
|
|
542
|
+
|
|
418
543
|
## Security
|
|
419
544
|
|
|
420
545
|
- The server talks only over stdio and has no sign-in. Anything that can write to its input acts as the OS account that started it, so don't expose it over a network or a tunnel.
|
|
@@ -450,15 +575,17 @@ openadapt-agent seal verify runs/<run_id>/report.json
|
|
|
450
575
|
openadapt-agent seal verify https://example.com/receipt.json --json
|
|
451
576
|
```
|
|
452
577
|
|
|
453
|
-
##
|
|
578
|
+
## How runs work
|
|
454
579
|
|
|
455
|
-
- Your agent, or a program acting for it,
|
|
456
|
-
-
|
|
580
|
+
- Your agent, or a program acting for it, runs on your customer's computer.
|
|
581
|
+
- Runs that stop show up in `list_needs_attention` and Flow's console; inside a [task-ledger job](#track-a-long-job), a person gets the question on the phone or in Desktop.
|
|
582
|
+
- A job belongs to one server process. Give each server its own runs folder for a job.
|
|
583
|
+
- Ledger questions are yes or no. The phone card shows the question and says the answer saves nothing in any app.
|
|
457
584
|
- The check that stops a second entry covers one runs folder. Pass the same absolute `--runs-dir` every time.
|
|
458
585
|
- If the server exits during a run, the run reads as `not_sure_if_saved`, and Flow may still finish it.
|
|
459
|
-
-
|
|
586
|
+
- `proof` is `sealed` when a run is connected to OpenAdapt Cloud Execute, Cloud signed its receipt, and that signature verifies against keys you pinned (see [Seals](#seals)).
|
|
460
587
|
- With openadapt-flow 2, each save waits for approval unless the operator names another mode (`serve --approval-mode problems_only` or `not_gated`). Your agent gets `needs_review` with the reason `waiting_for_approval`. A person answers on the computer that runs OpenAdapt, or your agent does when the server runs with `--delegate-approvals` and the run's approver policy allows an agent for that step. Nothing is saved without that answer. `get_approval_mode` tells your agent which mode applies; no tool changes it.
|
|
461
|
-
- Each call carries one request.
|
|
588
|
+
- Each call carries one request.
|
|
462
589
|
- Accounts, separation between customers, fleet policy, and hosted runs belong to OpenAdapt Cloud.
|
|
463
590
|
|
|
464
591
|
## Release status
|
|
@@ -9,7 +9,7 @@ Your AI agent decides what to enter. OpenAdapt enters it in the app and checks t
|
|
|
9
9
|
|
|
10
10
|
Use it if your agent reads referrals, intake forms, or authorizations and works out what belongs in the record, but a person still types the result into an app with no usable API. Examples are an electronic medical record (EMR) and a payer portal. This package is the tool your agent calls to do that typing.
|
|
11
11
|
|
|
12
|
-
- A person or your agent shows OpenAdapt the task in the real app. OpenAdapt replays those steps, and by default a run makes no AI model
|
|
12
|
+
- A person or your agent shows OpenAdapt the task in the real app. OpenAdapt replays those steps, and by default a run makes no calls to a hosted AI model.
|
|
13
13
|
- Before it types, OpenAdapt checks that the right record is open. After it saves, it reads the record back.
|
|
14
14
|
- Your agent gets one of four outcomes: done and checked, stopped before saving, check the record, or didn't run. When OpenAdapt can't confirm a save, it says so, and a person checks the record.
|
|
15
15
|
- It runs on your customer's computer, one that can already open the app. Your agent, or a small program acting for it, has to run on that computer too, since it starts OpenAdapt and talks to it directly. If your agent runs in your own cloud, ask us about a hosted route.
|
|
@@ -36,7 +36,7 @@ openadapt-agent emit-skill <bundle> --out ~/.claude/skills
|
|
|
36
36
|
| Type the values and save | Staff, retyping your agent's output | OpenAdapt, in the same screens staff use |
|
|
37
37
|
| Confirm the save | Staff, if they look again | OpenAdapt reads the record back before it reports done |
|
|
38
38
|
| Something doesn't match | Staff notice while typing, or someone finds it later | OpenAdapt stops before saving, and the run waits for a person |
|
|
39
|
-
| What staff handle | Every document | Runs that stopped and records to check.
|
|
39
|
+
| What staff handle | Every document | Runs that stopped and records to check. For work in a [task-ledger job](#track-a-long-job), each one is a question on their phone or desktop; otherwise someone checks the list of stopped runs. |
|
|
40
40
|
|
|
41
41
|
For scale, two hospital time studies put keying one faxed referral into an electronic record at about 10 to 12 minutes ([UCSF, JAMIA Open 2020](https://pmc.ncbi.nlm.nih.gov/articles/PMC7660949/); [Calderdale and Huddersfield NHS Foundation Trust, HFMA 2024](https://www.hfma.org.uk/system/files/2024-04/Using%20digital%20technologies%20to%20process%20admin%20tasks%20Case%20Study%20v7.pdf)). We don't publish a time-saved number until a deployment measures one.
|
|
42
42
|
|
|
@@ -60,7 +60,7 @@ flowchart LR
|
|
|
60
60
|
| What your product does with each outcome | One result per request, and no second entry when your agent repeats a `request_id` |
|
|
61
61
|
| Your tests against the four outcomes | A sandbox that returns each outcome on demand |
|
|
62
62
|
|
|
63
|
-
Your customer provides the computer and a person for runs that stop. A person or an agent shows the task, in web apps, native Windows apps, and
|
|
63
|
+
Your customer provides the computer and a person for runs that stop. A person or an agent shows the task, in web apps, native Windows apps, and remote-desktop sessions. A person accepts the result before it runs. See [Record a first workflow](#record-a-first-workflow), or [talk to us](https://openadapt.ai/qualify?why=embed) about setting it up.
|
|
64
64
|
|
|
65
65
|
## Try it in three steps
|
|
66
66
|
|
|
@@ -144,6 +144,8 @@ The simulated sandbox suggests the same command in its `list_workflows` result.
|
|
|
144
144
|
| `did_not_run` (Didn't run) | This call wrote nothing. With `request_id_conflict`, an earlier request with the same `request_id` may have saved. | If `safe_to_retry` is true, fix the cause and resend the same request. Otherwise follow `next_action`. |
|
|
145
145
|
| `running` (Running) | The run didn't finish within `wait_seconds` (default 60). | Call `get_run` with the `run_id` until `outcome` changes. |
|
|
146
146
|
|
|
147
|
+
`checked_how` says how the save was checked, when the run recorded it: `system_of_record` (Checked in your system's records), `separate_session` (Checked in a separate session), `reopened` (Reopened in the same session), `same_screen` (The screen said it saved), or `unchecked`. Only `separate_session` or stronger makes a run `done`. A save that was confirmed only by reopening it in the same session, or on the screen it left, is `not_sure_if_saved` with reason `checked_below_minimum`. A workflow can require `system_of_record`.
|
|
148
|
+
|
|
147
149
|
Branch on `outcome` and `safe_to_retry`. `next_step` names the next move as a fixed code, so your code doesn't have to parse `next_action`: `none`, `call_get_run`, `wait_for_person`, `check_record`, `fix_inputs`, `fix_setup`, or `retry`.
|
|
148
150
|
|
|
149
151
|
### Resend a request
|
|
@@ -236,6 +238,8 @@ asyncio.run(main())
|
|
|
236
238
|
|
|
237
239
|
The SDK starts the server with only a few environment variables. To pass others, such as `OPENADAPT_BUNDLE_KEY`, add `env=` to `StdioServerParameters`.
|
|
238
240
|
|
|
241
|
+
To let your agent add workflows itself, see [Teach a new workflow from your agent](#teach-a-new-workflow-from-your-agent).
|
|
242
|
+
|
|
239
243
|
[scripts/smoke_client.py](scripts/smoke_client.py) is a fuller example that runs every sandbox case. From a clone of this repository, run `pip install -e .`, then:
|
|
240
244
|
|
|
241
245
|
```bash
|
|
@@ -280,8 +284,10 @@ Flow's [execution profiles](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/p
|
|
|
280
284
|
| `--config YAML` | Flow's deployment config ([example](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/deployment.example.yaml)), which sets the profile and whether model grounding is allowed. |
|
|
281
285
|
| `--policy NAME-OR-PATH` | The policy Flow certifies against, such as `clinical-write` or `permissive`. |
|
|
282
286
|
| `--timeout SECONDS` | How long one run may work (default 600). Time a person spends approving a save doesn't count. A run stopped at the limit is `not_sure_if_saved`. |
|
|
283
|
-
| `--approval-mode MODE` | Whether each save waits for approval. Without it, Flow decides
|
|
284
|
-
| `--delegate-approvals AGENT_ID` | Lets your agent answer a waiting save as `AGENT_ID` (an opaque id such as `agent:intake-bot`), with `list_approval_requests` and `answer_approval_request`. Flow accepts the 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. Needs
|
|
287
|
+
| `--approval-mode MODE` | Whether each save waits for approval. Without it, Flow decides, and a run stops before each save until it's approved (`each_item`). `not_gated` turns that off for every run this server starts; each result records it under `technical.approval_mode`. |
|
|
288
|
+
| `--delegate-approvals AGENT_ID` | Lets your agent answer a waiting save as `AGENT_ID` (an opaque id such as `agent:intake-bot`), with `list_approval_requests` and `answer_approval_request`. Flow accepts the 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. Needs openadapt-flow 2.2.0 or later. |
|
|
289
|
+
| `--admissions DIR` | Adds `prepare_admission`, `list_admissions` and `decide_admission`, the same steps as `openadapt-flow admit`, over recordings in `DIR/recordings` and shadow candidates in `DIR/shadow-candidates`. A person decides in your client's form. With `--delegate-approvals` and `--approver-grant`, your agent decides as that approver instead, under a grant a person signed that names it. It never decides an admission it prepared or one prepared on a server without `--delegate-approvals`, and never under the `regulated` policy pack. Accepting sets the workflow's runs to `problems_only`. The record names who decided, and for your agent the grant and who signed it. |
|
|
290
|
+
| `--approver-grant FILE` | With `--admissions` and `--delegate-approvals`: the approver grant a person signed (`openadapt-flow approver-grant sign`) that lets your agent decide admissions. Flow checks it on every decision. Without it only a person decides. |
|
|
285
291
|
| `--headed` | Shows the browser. A person needs it to continue or skip a stopped run in a web app. |
|
|
286
292
|
| `--extra-run-arg ARG`, `--flow-cli CMD` | Change how Flow is called. Either one can relax Flow's checks, for example `--extra-run-arg=--allow-unencrypted`. |
|
|
287
293
|
| `--allow-protected-export`, `--allow-synthetic-recorded-defaults`, `--allow-model-grounding` | Off by default. See [Security](#security). |
|
|
@@ -290,7 +296,7 @@ Callers can't change `--url`, `--config`, `--policy`, `--timeout`, or `--approva
|
|
|
290
296
|
|
|
291
297
|
### Runs that wait for a person's approval
|
|
292
298
|
|
|
293
|
-
|
|
299
|
+
Unless the operator names another `--approval-mode`, Flow stops before each save and waits for an answer (up to 15 minutes by default). While it waits, `run_workflow` and `get_run` return `needs_review` with reason `waiting_for_approval` and `record_changed: "unknown"`, because an earlier save in the same run may have gone through. The person sees the waiting save with `openadapt-flow item-approval show RUN_DIR`, where `RUN_DIR` is the `run_id` folder in the runs folder, and answers with `item-approval approve` or `decline`. If nobody answers in time, the run stops before saving and reads `needs_review` with reason `approval_needed`. `get_approval_mode` reports the mode and whether your agent may answer. With `--delegate-approvals`, your agent can list waiting saves with `list_approval_requests` and answer one with `answer_approval_request`, where the run's approver policy allows an agent for that step; otherwise a person answers.
|
|
294
300
|
|
|
295
301
|
### Use the other tools
|
|
296
302
|
|
|
@@ -300,7 +306,7 @@ With `--bundles`, the server also registers read-only tools for workflows and ru
|
|
|
300
306
|
|
|
301
307
|
### Decide stopped runs
|
|
302
308
|
|
|
303
|
-
A run that stops for a person waits in Flow's Needs Attention list on that computer. Nobody is notified
|
|
309
|
+
A run that stops for a person waits in Flow's Needs Attention list on that computer. `list_needs_attention` shows the list. Nobody is notified, unless the run is part of a [task-ledger job](#track-a-long-job): then the ledger asks a person on their phone or desktop.
|
|
304
310
|
|
|
305
311
|
With `--mode attended`, your agent can send a decision with `continue_attention`, `skip_attention`, `reject_attention`, `teach_attention`, or `escalate_attention`. The person at the computer confirms each decision in a form. Continue and skip also need `--config`. If the client can't show forms, the server refuses the decision, and the person uses Flow's console instead:
|
|
306
312
|
|
|
@@ -362,6 +368,8 @@ For a native Windows app, start Flow's Windows agent server in the signed-in des
|
|
|
362
368
|
|
|
363
369
|
For an RDP or Citrix session, pass `--remote-window APP` (the client app, for example `"Microsoft Remote Desktop"`) or `--citrix`, and `--remote-window-title` when the app has more than one window. A remote session is screen-only: there's no DOM or UI Automation tree, so `observe` returns no nodes and your agent clicks x,y pixels in the client window and presses keys, the way a person would. To let your agent see that window, add `--authoring-screens scrubbed`, which turns on a `screen` tool that returns the frame with identifiers scrubbed and shares nothing if scrubbing fails. `--authoring-screens unscrubbed` skips scrubbing. Screens are off by default.
|
|
364
370
|
|
|
371
|
+
You can also name the app with a deployment file instead of these flags: `--authoring --config deploy.yaml --authoring-from-config`. The file's `backend:` section names a Windows app (`agent_url`), one macOS window (`macos_app`, optionally `macos_window_title`), one Linux window (`linux_app` and `linux_window_title`), or a local RDP or Citrix client window (`rdp_window`). It is the same section `openadapt-flow run --config` uses, so the recording and its runs drive the same app. This is how you record a Linux app, or a macOS app that isn't the frontmost window. Your agent clicks at window coordinates, so a Linux section also needs `linux_allow_physical_input: true`. Network RDP (`rdp_host`) is refused for authoring, because each run would open its own session and disconnect the recording's. Without `--authoring-from-config`, `--config` only configures runs.
|
|
372
|
+
|
|
365
373
|
[Recording on Windows, RDP and Citrix](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/RECORDING_ON_WINDOWS_RDP_CITRIX.md) explains the Windows agent server, why it's needed when the agent runs on another computer, how to start it, the token, and the RDP and Citrix pixel path.
|
|
366
374
|
|
|
367
375
|
The recording marks every step as the agent's or the person's (`actor` in `events.jsonl`, `demonstrated_by` in `meta.json`), and a person still accepts the result. On Windows with none of these flags, the tools can only tell the person what to click.
|
|
@@ -378,6 +386,123 @@ ChatGPT and Claude in a web browser can't start a server on your computer. To re
|
|
|
378
386
|
|
|
379
387
|
It claims the job, polls openadapt.ai over outgoing HTTPS, and asks you in the terminal before a chat account may drive it. Don't confuse it with the launcher's `openadapt connect`, which pairs a computer with OpenAdapt Cloud. [docs/MAILBOX_CLI.md](docs/MAILBOX_CLI.md) has the details.
|
|
380
388
|
|
|
389
|
+
### Guard an agent you already run
|
|
390
|
+
|
|
391
|
+
If your agent already drives a Chromium (a Playwright script, Playwright MCP, browser-use, or a computer-use agent with a browser), you don't have to move it onto these tools. Start its browser with a DevTools port, which is the one line you add, and run the guard next to it for each task:
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
openadapt-flow guard --cdp http://127.0.0.1:9222 --config guard.yaml --task task.json --out runs/task-0001
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
The guard holds every save the agent's tab sends until the record on the screen is the one `task.json` names. When it isn't, the save is blocked and the agent's page says why. A save that goes through is read back from the system of record (`VERIFIED`, `REFUTED` or `RECONCILIATION_REQUIRED`). `--observe-only` never blocks and shows what the guard would have done.
|
|
398
|
+
|
|
399
|
+
When a run reads back correctly, it becomes a draft in the teach dir that `guard.yaml` names, with a record check built from the app's own reads. A person confirms the check and admits the draft (`openadapt-flow compile-agent admit`); then `serve --teach verify.yaml --teach-dir <that dir>` serves it as a `taught_<name>` tool that makes no model calls. [GUARD_AN_AGENT.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/GUARD_AN_AGENT.md) has the files, what counts as a save, and the measurements.
|
|
400
|
+
|
|
401
|
+
### Teach a new workflow from your agent
|
|
402
|
+
|
|
403
|
+
`serve --teach` lets your agent add a workflow it doesn't have yet, without leaving the conversation. It does the task once through OpenAdapt; OpenAdapt checks the record it saved, compiles the run, and once the run is admitted your agent calls it as a tool. Every later call runs the compiled program with no model calls and reads the save back.
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
claude mcp add --scope user openadapt-teach -- uvx --python 3.12 --from 'openadapt-agent[tutorial]' openadapt-agent serve --teach verify.yaml --url https://emr-test.example.org --runs-dir ~/.openadapt/runs
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
`verify.yaml` needs only a `verifier:` section that reads the app's records (`rest`, `sql`, `fhir`, `graphql` or `file`), the same file `openadapt-flow record --verify` uses. The first run makes a real entry in the app, so point `--url` at a test copy with test records.
|
|
410
|
+
|
|
411
|
+
Your agent then:
|
|
412
|
+
|
|
413
|
+
1. Calls `teach_start` with the task (`{note}` for each input), this run's `inputs`, the `record_fields` the saved record must have (such as its patient id), and `on_screen`, the record's identity as the screen shows it (a name and an MRN).
|
|
414
|
+
2. Calls `teach_run` with short scripts (read the page, act on elements by role and name, read the result back; web apps), or `teach_observe` and `teach_act` one action at a time, until the task is done. After every save the gate detects, OpenAdapt reads the app's records back and tells the agent what landed; a write on a record the task does not name ends the run after it lands. On the web, `teach_observe` lists the page as short lines with a ref for each control, and before every save OpenAdapt checks that the record's identity is shown next to the control. A save on the wrong record is refused and ends the run.
|
|
415
|
+
3. Calls `teach_finish`. OpenAdapt compiles the run and reads the app's records: exactly one record must have appeared, with every `record_fields` value and every input. The result is the draft: its steps, its inputs, the proposed record check, and what still needs a person.
|
|
416
|
+
4. Calls `teach_confirm_checks`. Your MCP client shows a person the proposed check, and only their answer there, with their name, confirms it. Your agent has no argument that confirms it.
|
|
417
|
+
5. Calls `teach_admit` with `canary_inputs`, the values each canary saves. A canary is a real save on the same record, so use values that are safe there.
|
|
418
|
+
|
|
419
|
+
`teach_admit` admits only under the rules in [JIT_ADMISSION.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/JIT_ADMISSION.md). The operator starts the server with `--teach-key agent.pem --teach-policy signed-policy.json`: the key of the agent registered with `openadapt-flow jit register --role agent`, and an admission policy a person signed with `openadapt-flow jit sign-policy`. The server records the run as that agent, so admitting it is self-admission, which the policy must allow (`allow_self_admission`). Without a key and a policy, `teach_admit` refuses and returns the command a person runs at a terminal instead:
|
|
420
|
+
|
|
421
|
+
```bash
|
|
422
|
+
openadapt-flow compile-agent admit <task dir> --admit-as "<your name>" --canary-param note="Canary test note"
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Once the program is admitted, it is in `list_workflows` and `run_workflow` at once and has its own `taught_<name>` tool, and the server sends `notifications/tools/list_changed` to clients that support it. A server started later lists every program taught in its `--teach-dir`.
|
|
426
|
+
|
|
427
|
+
It works on a web app (`--url`) and on the app a deployment file's `backend:` section names (`--config deploy.yaml`): a native Windows, macOS or Linux window, or a local RDP or Citrix client window. Off the web, OpenAdapt can't read which record a form shows, so a person approves, in your client's form, every action that might save: any click except on a text field, a check box, a tab or a control that plainly navigates (Open, New, Cancel), any click at x,y, Enter, typing outside a text field, and shortcuts. Each answer covers that one action on the screen as it was; if anything changes first, the action isn't done. A client without forms can't save. RDP and Citrix have no control tree, and the Windows, macOS and Linux teach backends work from the screen, so off the web your agent needs `--teach-screens scrubbed` to see the screen, and clicks x,y pixels. Admission there fails closed: a pixel-only click carries no checked record identity, so the canary is refused and says why. Network RDP (`rdp_host`) is refused.
|
|
428
|
+
|
|
429
|
+
| Flag | What it does |
|
|
430
|
+
| --- | --- |
|
|
431
|
+
| `--teach VERIFY_YAML` | Adds `teach_start`, `teach_run` (web apps), `teach_observe`, `teach_act`, `teach_finish`, `teach_abort`, `teach_status`, `teach_confirm_checks` and `teach_admit`, and turns on runs of taught workflows. |
|
|
432
|
+
| `--teach-dir DIR` | Where drafts and taught programs live (default `RUNS_DIR/teach`). |
|
|
433
|
+
| `--teach-key PEM`, `--teach-policy JSON` | The registered agent's encrypted key (passphrase in `OPENADAPT_JIT_KEY_PASSPHRASE`) and the signed admission policy. Only an agent key under a policy; an admitter key admits from CI with `openadapt-flow jit admit`. |
|
|
434
|
+
| `--key-dir DIR` | The JIT key directory (default `OPENADAPT_JIT_KEY_DIR` or `~/.openadapt/jit`). |
|
|
435
|
+
| `--teach-hold` | On the web, a person also approves every save in the first run. Without it, your agent names the identity the save gate checks (`on_screen`); a value every form shows proves nothing, and then only the record check after the run and the person confirming it catch a wrong record. |
|
|
436
|
+
| `--teach-tasks DIR` | Run 1's record and inputs come from the operator's task files (`openadapt.guard-task/v1`); `teach_start` names one with `task_file`. Without it, your agent declares them at `teach_start`, before it reads the app. |
|
|
437
|
+
| `--teach-allow-origin URL` | Another origin run 1's browser may reach. Every origin but the app's is refused otherwise. |
|
|
438
|
+
| `--teach-write-adapter MODULE:ATTR` | The app's write adapter: each write in run 1 is read before it is sent, and one to another record, field or value is refused. |
|
|
439
|
+
| `--teach-screens scrubbed` | Adds `teach_screen`: the app's screen with identifiers scrubbed, and nothing shared if scrubbing fails. `unscrubbed` skips scrubbing. Off by default. |
|
|
440
|
+
| `--mode production --bundles DIR` | Serves your other workflows next to the taught ones. |
|
|
441
|
+
|
|
442
|
+
[COMPILE_AGENT.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/COMPILE_AGENT.md) describes the pipeline underneath: the harness, the record check, the canaries and the files it keeps.
|
|
443
|
+
|
|
444
|
+
## Track a long job
|
|
445
|
+
|
|
446
|
+
On long work, agents lose track of the rules they were given, miss information that arrives partway through, guess instead of asking, and skip checking their work ([OSWorld 2.0](https://arxiv.org/abs/2606.29537)). The task ledger keeps that state on the server, so your agent reads it instead of rebuilding it from its own history. It's on whenever runs or guarded saves are; `serve --no-ledger` turns it off.
|
|
447
|
+
|
|
448
|
+
| Tool | What it does |
|
|
449
|
+
| --- | --- |
|
|
450
|
+
| `open_job` | Opens a job with the user's intent, rules, and items. Rules are locked when the job opens. |
|
|
451
|
+
| `get_job` | Returns a short summary: current rules, counts by status, items not verified yet with their `request_id`, waiting questions, answers that came back, and what changed since the last read. |
|
|
452
|
+
| `add_items` | Adds up to 100 items per call, 500 per job. |
|
|
453
|
+
| `record_evidence` | Notes a fact and where it came from: `user` (trusted), or `app` or `document` (never trusted). |
|
|
454
|
+
| `ask_user` | Asks a person a yes-or-no question. Optional `wait_seconds`, 0 to 600. |
|
|
455
|
+
| `get_answer` | Returns `waiting`, `yes`, `no`, `expired`, or `refused`. Optional `wait_seconds`, 0 to 600. |
|
|
456
|
+
| `change_constraint` | Asks a person to change or add a rule. The old rule holds until they approve. |
|
|
457
|
+
| `close_job` | Closes the job. `complete` is true only when every item is verified and no question waits. `verified_items` lists what your agent may report as done. |
|
|
458
|
+
|
|
459
|
+
Each item has its own `request_id`, `<job_id>.<item_id>`. Pass it to `run_workflow`, and the server updates the item from its own result:
|
|
460
|
+
|
|
461
|
+
| Item status | Set when |
|
|
462
|
+
| --- | --- |
|
|
463
|
+
| `pending` | The item was added, a run didn't run, or a person asked for another try. |
|
|
464
|
+
| `running` | The run hasn't finished. |
|
|
465
|
+
| `verified` | The run ended `done`, a guarded save ended `VERIFIED`, or a person checked the record and said it saved. |
|
|
466
|
+
| `stopped_before_saving` | The run ended `needs_review`. |
|
|
467
|
+
| `check_the_record` | The run ended `not_sure_if_saved`, or a guarded save wasn't verified. |
|
|
468
|
+
| `saved_awaiting_check` | The save went through and waits for a later check, or a guarded save may write now. |
|
|
469
|
+
| `needs_person` | A question about the item waits, a save waits for approval, or a person said they'll handle it. |
|
|
470
|
+
|
|
471
|
+
### What the server enforces
|
|
472
|
+
|
|
473
|
+
- Your agent can't set a status. Only this server's results and a person's answers do.
|
|
474
|
+
- A rule can name a `run_workflow` input and say it `equals` a value, is `one_of` some values, or is `not_one_of` them. The server refuses a run for the job that breaks a rule or leaves that input out, and the error ends "Nothing ran." A rule with only `text` is shown but not checked.
|
|
475
|
+
- Only a person changes a rule after the job opens. That holds when the change comes from the user, too; the person confirms it.
|
|
476
|
+
- The server refuses a run for an item while a question about it waits, and while it's `check_the_record` or `needs_person`. After a person asks for another try, the item gets a new `request_id`, `<job_id>.<item_id>.r2`, and the old one is refused.
|
|
477
|
+
- A run with any other `request_id` doesn't change an item. While a job is open, its summary counts those runs under `unplanned_writes`.
|
|
478
|
+
- Evidence never changes a rule or a status.
|
|
479
|
+
|
|
480
|
+
### Questions reach a person
|
|
481
|
+
|
|
482
|
+
A question is a save-approval request, the same files Flow writes before a save. It shows on the paired phone and in Desktop's Run it, and a person answers there. Save means yes, Don't save means no, and the answer saves nothing in any app. The server checks each answer as a run checks a save approval: it must come from a person on Desktop or the phone, for that exact question, before it expired (4 hours). An answer from an agent or a script is refused.
|
|
483
|
+
|
|
484
|
+
When a run for an item stops before saving, the ledger asks whether to try again. When a save can't be confirmed, it asks the person to check the record. So a stop in a job reaches someone without anyone watching Needs Attention. Questions are written to `task-ledger` inside the runs folder. Point the console or Desktop at that runs folder.
|
|
485
|
+
|
|
486
|
+
### Measured on a synthetic worklist
|
|
487
|
+
|
|
488
|
+
A scripted agent worked a 30-item synthetic MockMed worklist. The user changes a rule at item 13, item 20's referral tells the reader to mark everything urgent, two patients share a name, and two saves can't be confirmed (one didn't land). The agent has the same flaws in every arm: it acts on a rule only while the message that stated it is among its last 5 turns, it follows instructions it reads in a referral, it picks the first matching patient, and it reports every run that wrote something as done. A simulated person answers through the same list the phone reads. No model is called.
|
|
489
|
+
|
|
490
|
+
| | Agent alone | Ledger, enforcement only | Agent follows the ledger |
|
|
491
|
+
| --- | --- | --- | --- |
|
|
492
|
+
| Items saved against a rule | 12 | 0 | 0 |
|
|
493
|
+
| Items guessed instead of asked | 2 | 2 | 0 |
|
|
494
|
+
| Unverified items reported as done | 2 | 1 | 0 |
|
|
495
|
+
| Correct records (of 30) | 16 | 27 | 30 |
|
|
496
|
+
| Runs the ledger refused | 0 | 12 | 0 |
|
|
497
|
+
|
|
498
|
+
In the middle arm, the agent opens a job and uses each item's `request_id` but keeps its flaws. The ledger stops every rule break, but it can't stop an agent from guessing a value or misreporting its own tally. `close_job` still lists what was verified, so a caller can check the agent's report. The scripted agent stands for a policy, not a model, so these numbers test the mechanism. They aren't a model benchmark. To reproduce them (about 3 seconds):
|
|
499
|
+
|
|
500
|
+
```bash
|
|
501
|
+
python packages/agent/tests/ledger_demo.py
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
Results: [docs/task-ledger-measurement.json](docs/task-ledger-measurement.json).
|
|
505
|
+
|
|
381
506
|
## Security
|
|
382
507
|
|
|
383
508
|
- The server talks only over stdio and has no sign-in. Anything that can write to its input acts as the OS account that started it, so don't expose it over a network or a tunnel.
|
|
@@ -413,15 +538,17 @@ openadapt-agent seal verify runs/<run_id>/report.json
|
|
|
413
538
|
openadapt-agent seal verify https://example.com/receipt.json --json
|
|
414
539
|
```
|
|
415
540
|
|
|
416
|
-
##
|
|
541
|
+
## How runs work
|
|
417
542
|
|
|
418
|
-
- Your agent, or a program acting for it,
|
|
419
|
-
-
|
|
543
|
+
- Your agent, or a program acting for it, runs on your customer's computer.
|
|
544
|
+
- Runs that stop show up in `list_needs_attention` and Flow's console; inside a [task-ledger job](#track-a-long-job), a person gets the question on the phone or in Desktop.
|
|
545
|
+
- A job belongs to one server process. Give each server its own runs folder for a job.
|
|
546
|
+
- Ledger questions are yes or no. The phone card shows the question and says the answer saves nothing in any app.
|
|
420
547
|
- The check that stops a second entry covers one runs folder. Pass the same absolute `--runs-dir` every time.
|
|
421
548
|
- If the server exits during a run, the run reads as `not_sure_if_saved`, and Flow may still finish it.
|
|
422
|
-
-
|
|
549
|
+
- `proof` is `sealed` when a run is connected to OpenAdapt Cloud Execute, Cloud signed its receipt, and that signature verifies against keys you pinned (see [Seals](#seals)).
|
|
423
550
|
- With openadapt-flow 2, each save waits for approval unless the operator names another mode (`serve --approval-mode problems_only` or `not_gated`). Your agent gets `needs_review` with the reason `waiting_for_approval`. A person answers on the computer that runs OpenAdapt, or your agent does when the server runs with `--delegate-approvals` and the run's approver policy allows an agent for that step. Nothing is saved without that answer. `get_approval_mode` tells your agent which mode applies; no tool changes it.
|
|
424
|
-
- Each call carries one request.
|
|
551
|
+
- Each call carries one request.
|
|
425
552
|
- Accounts, separation between customers, fleet policy, and hosted runs belong to OpenAdapt Cloud.
|
|
426
553
|
|
|
427
554
|
## Release status
|
|
@@ -135,6 +135,7 @@ pause, and a consistency check of the persisted report:
|
|
|
135
135
|
| any proven-no-effect outcome whose pause a person rejected | `stopped_by_person` | `did_not_run` (no retry) |
|
|
136
136
|
| `RECONCILIATION_REQUIRED` | `save_not_confirmed` | `not_sure_if_saved` |
|
|
137
137
|
| `COMPLETED_UNVERIFIED` | `not_checked` | `not_sure_if_saved` |
|
|
138
|
+
| `COMPLETED_UNVERIFIED` whose saves were all confirmed, but at least one only on the same screen or by a same-session reopen | `checked_below_minimum` | `not_sure_if_saved` |
|
|
138
139
|
| `ROLLED_BACK` | `change_reversed` | `not_sure_if_saved` |
|
|
139
140
|
| timeout after start (time a person spends approving doesn't count), inconsistent or missing report, coarse `HALTED` alone | `timed_out`, `result_unreadable` | `not_sure_if_saved` |
|
|
140
141
|
| a server that stopped mid-run | `interrupted` | `not_sure_if_saved` |
|
|
@@ -145,6 +146,14 @@ pause, and a consistency check of the persisted report:
|
|
|
145
146
|
| a used `request_id` with a different workflow or inputs | `request_id_conflict`, with `record_changed: "unknown"` | `did_not_run` (no retry) |
|
|
146
147
|
| a `request_id` whose five attempts each proved nothing was written | `retry_limit` | `did_not_run` (no retry) |
|
|
147
148
|
|
|
149
|
+
A `done` or `not_sure_if_saved` result whose report records how its saves
|
|
150
|
+
were checked also carries `checked_how` (`unchecked`, `same_screen`,
|
|
151
|
+
`reopened`, `separate_session`, `system_of_record`) and `checked_how_label`,
|
|
152
|
+
the weakest check level any save reached
|
|
153
|
+
([how a save was checked](../../flow/docs/READBACK_LADDER.md)). `done` needs
|
|
154
|
+
`separate_session` or stronger; a same-session reopen is never `done` by
|
|
155
|
+
itself.
|
|
156
|
+
|
|
148
157
|
Only `done` says the change was saved. `record_changed: "no"` and
|
|
149
158
|
`safe_to_retry: true` appear only when the evidence proves nothing was
|
|
150
159
|
written. `request_id_conflict` starts nothing, but an earlier attempt
|
|
@@ -210,6 +219,7 @@ repeats the text. Every declared input stays required unless
|
|
|
210
219
|
| `--mode production --bundles DIR` (or `--allow-run`) | production | the above plus `run_workflow` and deprecated `run_<opaque-id>` |
|
|
211
220
|
| `--mode attended --bundles DIR` (or `--allow-run --allow-attended-actions`) | attended | the above plus Reject, Teach, Escalate; Continue and Skip with a qualified `--config` |
|
|
212
221
|
| `--bundles DIR --allow-attended-actions` (older flags, no runs) | attended, read-only | the read-only tools plus the same decisions on paused runs |
|
|
222
|
+
| `--teach VERIFY_YAML` with `--url` or `--config` | production (taught programs) | the production tools plus `teach_start`, `teach_observe`, `teach_act`, `teach_finish`, `teach_abort`, `teach_status`, `teach_confirm_checks`, `teach_admit`, `teach_screen` (with `--teach-screens`), and `taught_<name>` for each admitted program |
|
|
213
223
|
|
|
214
224
|
The read-only tools return PHI-safe projections: workflow cards, opaque
|
|
215
225
|
ids, availability, status, and count or boolean metrics. They do not
|
|
@@ -218,6 +228,14 @@ observed text, local paths, subprocess output, or exception text. The
|
|
|
218
228
|
attention tools use the same boundary plus typed categories, artifact
|
|
219
229
|
ids, and non-authorizing capability metadata.
|
|
220
230
|
|
|
231
|
+
The teach tools are the exception. While the agent does run 1 of a task,
|
|
232
|
+
`teach_observe` and `teach_act` return what it needs to find the right record:
|
|
233
|
+
on the web, the page's text and controls, scrubbed by
|
|
234
|
+
`openadapt_flow.privacy` except for the values the agent put in the task. Run
|
|
235
|
+
1 makes a real entry, so the operator points it at a test copy. The draft
|
|
236
|
+
report, the record check and the admission return the same kind of facts as
|
|
237
|
+
the other tools, plus the proposed record check's field names and values.
|
|
238
|
+
|
|
221
239
|
The deprecated per-workflow `run_<opaque-id>` tools keep their schemas and
|
|
222
240
|
legacy `status` field and add the contract fields. They take no
|
|
223
241
|
`request_id`, so their descriptions say a retry can write twice. Missing
|