mirror of
https://github.com/morluto/rea.git
synced 2026-11-09 08:17:02 +00:00
docs(skill): build agent environments from a prompt with request steps and the bundle tool
Add the environments reference to the skill and install it with setup: record against a disposable instance, sign in through the UI, write with request steps and read each write back, check coverage, re-record by withheld reason and export the bundle. Route it from SKILL.md, name agent environments in the skill description, and document the request step, the export tool and recording writes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
51723daab9
commit
f7213a4c08
12 files changed
+360
-7
No files matched your search
@@ -187,6 +187,20 @@ Evidence also records the capture file's absolute path (`subject.local_path`,
|
||||
`parameters.capture_path`, `locations`, `artifact.path`), and the bundle
|
||||
carries it: build from a neutral folder when that path should not ship.
|
||||
|
||||
Over MCP, `export_environment_bundle` builds the same bundle and returns the
|
||||
same report. Its `source` is capture Evidence the session retained
|
||||
(`{"kind": "retained-evidence", "evidence_id"}`), such as a
|
||||
`capture_browser_scenario` or `inspect_web_network_capture` result, or a
|
||||
capture Evidence file (`{"kind": "evidence-file", "path"}`); inspect a HAR with
|
||||
`inspect_web_network_capture` first. Retained Evidence is written into the
|
||||
bundle as `--format json` writes it, so the bundle is the one
|
||||
`environment-build` writes from that file. `output_directory` must be an
|
||||
absolute path that does not exist yet, whose parent does; the tool creates it
|
||||
and refuses an existing directory, empty or not. `name`, `template_rules`,
|
||||
`package_tarball` and `dry_run` work as `--name`, `--template-rule`,
|
||||
`--package-tarball` and `--dry-run` do, and retained Evidence has no file to
|
||||
name the environment after, so a loopback or IP origin needs `name`.
|
||||
|
||||
## From a browser scenario
|
||||
|
||||
The capture can also come from REA itself: a
|
||||
@@ -275,6 +289,35 @@ the replayed body is the bytes the browser exposes for each response, with any
|
||||
declared secret replaced by its redaction marker (a body that redaction had to
|
||||
empty replays no body).
|
||||
|
||||
To record writes, sign in through the UI and then send the application's API
|
||||
calls with [request steps](browser-scenario-contract.md#request-steps): each
|
||||
sends one request to the start origin through the page's own `fetch`, with the
|
||||
session's cookies, and a header can carry the CSRF cookie or stored token the
|
||||
application's own requests send. A write is served only when the capture shows
|
||||
its effect, so read each collection before writing, read every written entity
|
||||
back by id and in its list right after the write, and leave more than a second
|
||||
between a create and its first edit. The environment coverage view names each
|
||||
write it withholds and what to record next. A scenario with write requests
|
||||
changes the application's data: record against an instance whose data may
|
||||
change.
|
||||
|
||||
```json
|
||||
{
|
||||
"step_id": "create-folder-1",
|
||||
"action": "request",
|
||||
"method": "POST",
|
||||
"path": "/api/folders",
|
||||
"headers": [
|
||||
{
|
||||
"name": "X-CSRFToken",
|
||||
"value": { "source": "cookie", "name": "csrftoken" }
|
||||
}
|
||||
],
|
||||
"json": { "title": "Scratch folder 1" },
|
||||
"expected_status": [200, 201]
|
||||
}
|
||||
```
|
||||
|
||||
## What a manifest is
|
||||
|
||||
An environment manifest is a projection of one application's recorded request
|
||||
|
||||
@@ -10,7 +10,8 @@ liveness deadlines. `INPUT_JSON` may be inline JSON or a JSON file path.
|
||||
The minimal request contains only browser launch/connect selection, `start_url`,
|
||||
and at least one `actions` entry. Launch `headless` defaults to `true` and can
|
||||
be set to `false` where the host supports a visible browser. Navigations follow
|
||||
the destinations named by the scenario and the page's ordinary browser requests;
|
||||
the destinations named by the scenario and the page's ordinary browser requests,
|
||||
and [request steps](#request-steps) send only to the start URL's origin;
|
||||
there is no separate origin allowlist or request-interception layer. Environment
|
||||
settings (including service workers blocked by default), empty storage, and a
|
||||
final sanitized URL capture are supplied by default. Set
|
||||
@@ -204,7 +205,8 @@ language environments.
|
||||
HTTP(S) URLs may include ordinary query values and fragments directly. Use
|
||||
ordered structured query entries when a value needs to reference a declared
|
||||
environment-backed secret. Form, storage, and cookie values are either literal
|
||||
strings or declared secret references. Raw URL userinfo credentials are rejected.
|
||||
strings or declared secret references; request step headers can also read a
|
||||
cookie or local storage value from the page. Raw URL userinfo credentials are rejected.
|
||||
Captured credential-header
|
||||
values are not retained, and declared secret values are redacted automatically.
|
||||
A secret may be declared solely to
|
||||
@@ -315,6 +317,76 @@ external browser without closing it. The request itself defines the operation;
|
||||
there is no additional REA grant step. The browser and host still enforce their
|
||||
own access and process rules.
|
||||
|
||||
## Request steps
|
||||
|
||||
A `request` step sends one HTTP request from the page with the page's own
|
||||
`fetch`, so the browser attaches the session's cookies and the request is
|
||||
recorded in the step's network events like any request the application makes.
|
||||
Use it to record an application's API, including writes, when driving the UI
|
||||
for each call would be slower or less exact than calling the endpoint the UI
|
||||
calls. It is the only action that changes application state without a user
|
||||
gesture, so run scenarios with write requests only against an instance whose
|
||||
data you may change.
|
||||
|
||||
```json
|
||||
{
|
||||
"step_id": "create-item",
|
||||
"action": "request",
|
||||
"method": "POST",
|
||||
"path": "/api/items",
|
||||
"query": [
|
||||
{ "name": "notify", "value": { "source": "literal", "value": "false" } }
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"name": "X-CSRFToken",
|
||||
"value": { "source": "cookie", "name": "csrftoken" }
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": {
|
||||
"source": "local_storage",
|
||||
"key": "auth",
|
||||
"json_pointer": "/token"
|
||||
},
|
||||
"prefix": "Bearer "
|
||||
}
|
||||
],
|
||||
"json": { "title": "Scratch item 1" },
|
||||
"expected_status": [201],
|
||||
"timeout_ms": 10000
|
||||
}
|
||||
```
|
||||
|
||||
- `method` is `GET`, `HEAD`, `POST`, `PUT`, `PATCH` or `DELETE`. `json` is the
|
||||
body, sent with `Content-Type: application/json` unless a header sets one; a
|
||||
`GET` or `HEAD` step cannot carry it.
|
||||
- `path` starts with one `/` and must resolve to the start URL's origin, which
|
||||
validation checks before the browser starts. When the step runs, the page must
|
||||
be on that origin; add a `goto` step first if an earlier action left it.
|
||||
`query` entries are appended in order and may reference declared secrets.
|
||||
- Header values are literals, declared secrets, a cookie the browser holds for
|
||||
the request URL (`{"source": "cookie", "name"}`), or a local storage entry on
|
||||
the start origin (`{"source": "local_storage", "key"}`, with an optional
|
||||
`json_pointer` into a stored JSON value). `prefix` is sent before the value.
|
||||
Values read from the page are redacted like declared secrets, as
|
||||
`[REDACTED:page:<step_id>.<header>]`, from records made after the step reads
|
||||
them; a response recorded earlier, such as the login response that returned
|
||||
a token, keeps the value. A missing cookie, key or
|
||||
pointer fails the step. Header names a page's `fetch` cannot set, such as
|
||||
`Cookie`, `Origin`, `Host` or `Sec-*`, are refused at validation.
|
||||
- The step completes when the whole response has been read. With
|
||||
`expected_status`, any other status fails the step and the later actions are
|
||||
recorded as `cancelled`; without it, any response completes the step. A
|
||||
network error, or `timeout_ms` passing, fails the step.
|
||||
|
||||
The request runs in the page's main world, so an application that wraps
|
||||
`fetch` sees it, and it goes through the same network observation as other
|
||||
page requests: select `network` events and network content to record it.
|
||||
Captures with request steps state in `limitations` that the steps sent their
|
||||
declared requests through the page's fetch. A scenario without request steps
|
||||
is unchanged.
|
||||
|
||||
## Network evidence
|
||||
|
||||
`capture.events: ["network"]` retains request, response, completion, failure,
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Plan
|
||||
|
||||
1. Add the `request` action to the scenario schema: methods, path, query,
|
||||
headers with page value sources, JSON body and expected statuses; validate
|
||||
the origin and the body against the start URL; include query and header
|
||||
secrets in the declared-secret check.
|
||||
2. Add `PlaywrightScenarioRequest.ts`: check the page's origin, build the URL,
|
||||
resolve header values (admitting values read from the page to the scenario's
|
||||
redaction), fetch in the page with a timeout and read the whole response,
|
||||
then check the status. Dispatch it from the action runner with the start
|
||||
origin, and add the limitation when a scenario has a request step.
|
||||
3. Add a real-browser lane, `verify:browser:request`: a fixture item API that
|
||||
requires a CSRF cookie header and a stored bearer token for writes, a write
|
||||
session recorded through the CLI, the build's served writes, and a failing
|
||||
expected status.
|
||||
4. Add the `export_environment_bundle` contract, effects, CLI alias, catalog
|
||||
family and handler: check the destination as other MCP output tools do,
|
||||
resolve the capture bytes, and call the build with an exclusive destination.
|
||||
5. Write the skill reference, route it from SKILL.md and install it with setup.
|
||||
6. Document the request step, the MCP tool and recording writes.
|
||||
7. Tests: request step unit tests, the MCP tool boundary, setup's skill files.
|
||||
@@ -0,0 +1,63 @@
|
||||
# Build an environment from a prompt
|
||||
|
||||
## Outcome
|
||||
|
||||
`rea environment-build` builds a bundle from one capture, but an agent asked to
|
||||
"make an environment of this app" still cannot finish the job through REA's MCP
|
||||
server. A browser scenario can only reach an application's API through the UI or
|
||||
a `goto` GET, so recording the writes an environment serves means scripting every
|
||||
form, and there is no MCP tool that writes a bundle. Nothing tells the agent how
|
||||
to record a session the state can learn writes from, or when to stop recording.
|
||||
|
||||
A `request` scenario action sends one same-origin request through the page's own
|
||||
`fetch`, so a scenario can sign in through the UI and then call the application's
|
||||
API, writes included, with the session's cookies and the CSRF or bearer values
|
||||
the application's own requests send. An `export_environment_bundle` MCP tool
|
||||
builds the bundle from capture Evidence, as `environment-build` does. A skill
|
||||
reference walks an agent from the prompt to a bundle: map, record, check
|
||||
coverage, re-record by reason, export and hand off.
|
||||
|
||||
## Requirements
|
||||
|
||||
- A `request` step names a method (`GET`, `HEAD`, `POST`, `PUT`, `PATCH`,
|
||||
`DELETE`), a path on the start URL's origin, ordered query entries, headers,
|
||||
an optional JSON body and optional expected statuses. Validation refuses a
|
||||
path that resolves to another origin, a body on `GET` or `HEAD`, and header
|
||||
names a page's `fetch` cannot set.
|
||||
- Header values are literals, declared secrets, a cookie the browser holds for
|
||||
the request URL, or a local storage value on the start origin, optionally
|
||||
read at a JSON pointer, with an optional prefix. Values read from the page are
|
||||
redacted like declared secrets from records made after they are read.
|
||||
- The request runs through the page's `fetch` with same-origin credentials, from
|
||||
a page on the start origin, and completes when the response is read. A status
|
||||
outside `expected_status`, a network error or the step timeout fails the step;
|
||||
later steps are cancelled as for other actions. Its network events are
|
||||
attributed to the step.
|
||||
- Captures with request steps say so in a limitation. A scenario without one
|
||||
validates, runs and records exactly as before.
|
||||
- `export_environment_bundle` takes retained capture Evidence or a capture
|
||||
Evidence file, an absolute output directory that does not exist and whose
|
||||
parent does, and the build's name, template rules, package tarball and dry
|
||||
run. It writes the bundle `environment-build` writes from the same Evidence
|
||||
bytes (retained Evidence serialized as `--format json` writes it) and returns
|
||||
the same report, validated by its output schema. It refuses an existing
|
||||
directory, empty or not, a parentless or relative one, unknown Evidence, an
|
||||
unnamed loopback capture and repeated rules, and writes nothing when it
|
||||
refuses. `environment-build` is its CLI command.
|
||||
- The skill reference `references/environments.md` is installed with the skill
|
||||
and routed from SKILL.md. It covers disposable instances only, credentials
|
||||
from environment variables, the recording rules the state needs, coverage,
|
||||
the stop rule and the hand-off.
|
||||
- Stop rule: re-record a withheld write at most twice for a recording gap
|
||||
(`no-state-target`, `no-observation`, `unconfirmed`, `incomplete-entities`),
|
||||
once for `unexplained-change` and `no-call`, and stop at once for application
|
||||
behaviour (`side-effect`, `contradicted`, `body-unusable`).
|
||||
|
||||
## Non-goals
|
||||
|
||||
- No cross-origin requests, form or multipart bodies, or arbitrary script
|
||||
actions.
|
||||
- No retroactive redaction of values a request step reads: a login response
|
||||
recorded before the step keeps the token it returned, as captures do today.
|
||||
- No application start, seed or reset, and no publishing of bundles.
|
||||
- No change to what any view derives or any environment serves.
|
||||
@@ -0,0 +1,11 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Record the outcome, requirements and non-goals.
|
||||
- [x] Add the request action, its validation and its Playwright runner.
|
||||
- [x] Add the real-browser request lane.
|
||||
- [x] Add the export_environment_bundle tool.
|
||||
- [x] Write and install the skill reference.
|
||||
- [x] Document the request step, the tool and recording writes.
|
||||
- [ ] Run static, formatting, documentation and test gates, and the existing browser lanes.
|
||||
- [ ] Build environments from prompts with an agent against live applications on Linux VMs.
|
||||
- [ ] Address the independent review.
|
||||
@@ -59,7 +59,8 @@ change.
|
||||
|
||||
- No new browser action: scenarios still cover what the UI requests plus GET
|
||||
reads through `goto`. A fetch or script action would be a new effect and
|
||||
needs its own scope decision.
|
||||
needs its own scope decision. (The `request` action was later scoped and
|
||||
added by [environment-prompt](../environment-prompt/spec.md).)
|
||||
- No change to what the scenario redacts. Values the application returns in
|
||||
response bodies are retained, as they are in HAR captures.
|
||||
- The api-surface view keeps HAR parents only.
|
||||
Reference in new issue
Block a user