mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 05:56:19 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1e0e08614 | ||
|
|
c342ce75a9 | ||
|
|
9e87c30e58 | ||
|
|
da8f9de31b |
No files matched your search
@@ -22,9 +22,7 @@ parity.
|
||||
For an audit, follow [audit existing tools](../../../docs/tool-design.md#audit-existing-tools):
|
||||
scope representative tasks, exercise the live workflow, and distinguish
|
||||
reproduced bugs, supported limitations, design opportunities and unverified
|
||||
claims. Check relevant rendered MCP prompts, guided workflows and packaged skill
|
||||
advice against the live contracts and exercise their suggested handoffs. Report
|
||||
working alternatives and evidence limits as well as findings.
|
||||
claims. Report working alternatives and evidence limits as well as findings.
|
||||
|
||||
For a design request, return the proposed boundary, input/result shape, Evidence
|
||||
semantics, nearest alternative, and verification approach. Implement when
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: reverse-engineer-anything
|
||||
description: Reverse engineer native, managed, Electron/JavaScript, packaged, firmware, and browser targets with REA, and build agent environments (AgentEnv bundles) of web applications from a live instance or a capture. Use shipped-artifact or requested runtime evidence to explain features, compare versions, decompile code, or guide a reconstruction. Skip REA for ordinary source-repository architecture analysis.
|
||||
metadata:
|
||||
version: "60"
|
||||
version: "59"
|
||||
---
|
||||
|
||||
# REA
|
||||
@@ -121,15 +121,6 @@ explicit path or endpoint and do not need it.
|
||||
cleanup effects, and do not require a preliminary static session unless their
|
||||
own contract says so.
|
||||
- User-owned browser page already open: `list_browser_targets`.
|
||||
- Passive traffic capture of a SPA: the default scope is the page's own origin,
|
||||
and data APIs usually live on another origin. Discover API origins from
|
||||
absolute URLs in loaded bundles or script text via JavaScript analysis tools
|
||||
(another passive capture without those origins still scopes to the page
|
||||
origin, and `completeness.excluded` counts lack URLs). Pass both the page
|
||||
origin and those API origins in `allowed_origins` — the list replaces the
|
||||
default scope — and read `completeness.excluded` before reporting that an
|
||||
API is absent. For long captures with body shapes, use
|
||||
`export_evidence_bundle` instead of expecting an inline result.
|
||||
- Browser action the agent must perform (click, type, navigate):
|
||||
`capture_browser_scenario` with declared actions. See
|
||||
[runtime observation](references/runtime-observation.md) for connect/launch
|
||||
@@ -137,9 +128,6 @@ explicit path or endpoint and do not need it.
|
||||
- Agent environment (AgentEnv bundle) of a web application: read
|
||||
[environments](references/environments.md) before recording; it records with
|
||||
`capture_browser_scenario` and writes the bundle with `export_environment_bundle`.
|
||||
- Task for a desktop application's exported environment, from a
|
||||
`capture-desktop-application` folder: read
|
||||
[desktop application tasks](references/environments.md#tasks-for-a-desktop-application-environment).
|
||||
- Fiddler Everywhere traffic or a native `.saz` capture: read the
|
||||
[Fiddler workflow](references/runtime-observation.md#fiddler-everywhere-traffic-debugging).
|
||||
Inspect saved SAZ with `inspect_web_network_capture` and `format: "saz"`;
|
||||
@@ -276,15 +264,6 @@ explicit path or endpoint and do not need it.
|
||||
linker declarations, not proof of enforcement. A missing directory is observed
|
||||
absence of that declaration. See the
|
||||
[PE load configuration guide](https://github.com/morluto/rea/blob/main/docs/pe-load-config.md).
|
||||
- ELF, PE, and Mach-O hardening: `inspect_binary_hardening` with the explicit
|
||||
local file. This built-in target-free inspection requires no pwntools or other
|
||||
host tool. One image per Mach-O slice. Each property is `enabled`, `disabled`,
|
||||
`partial`, `unknown`, or `not_applicable`, with the header field or import
|
||||
behind it. A missing stack-protector or fortify import is `unknown`, not
|
||||
proof the protection was omitted. Authenticode and Mach-O code signatures
|
||||
report presence, not validity. Hardened runtime stays unknown and names
|
||||
`inspect_signature`. The image is not executed. See the
|
||||
[binary hardening guide](https://github.com/morluto/rea/blob/main/docs/binary-hardening.md).
|
||||
- ELF note tables: `inspect_elf_notes` with the explicit local ELF32/ELF64
|
||||
file. This built-in target-free inspection requires no disassembler. Preserve
|
||||
PT_NOTE and SHT_NOTE ranges, owners, types, descriptor SHA-256, and GNU
|
||||
@@ -406,10 +385,6 @@ describe possible providers, while top-level session capabilities describe the
|
||||
current route and `analysis_provider_binding` names the selected provider. Do
|
||||
not treat those scopes as interchangeable.
|
||||
|
||||
For editable Ghidra analysis that must survive a later agent process, select
|
||||
the [owned Program checkpoint workflow](references/native-and-artifacts.md#continue-editable-ghidra-analysis-across-processes)
|
||||
before making metadata edits. It is separate from Evidence/Unknown persistence.
|
||||
|
||||
For a Ghidra startup timeout on a large native target, follow
|
||||
[deadline recovery](references/native-and-artifacts.md#ghidra-startup-deadline-recovery)
|
||||
before abandoning deep analysis or repeating the same cold import. The default
|
||||
@@ -537,8 +512,6 @@ as complete while required questions remain open.
|
||||
- Agent environments (AgentEnv bundles) of a web application, recorded from
|
||||
a live instance or a HAR:
|
||||
[references/environments.md](references/environments.md)
|
||||
- Tasks for a desktop application's exported environment:
|
||||
[references/environments.md](references/environments.md#tasks-for-a-desktop-application-environment)
|
||||
|
||||
## Finish the task
|
||||
|
||||
|
||||
@@ -8,9 +8,7 @@ confirmed. The workflow records the application once with
|
||||
again if a hint names a gap, then writes the bundle with
|
||||
`export_environment_bundle`. For a HAR the user already has, skip to
|
||||
[export](#export-and-hand-off) through `inspect_web_network_capture`, or use
|
||||
`rea environment-build <capture.har> <dir>`. For a task in a desktop
|
||||
application's exported environment, read
|
||||
[its section](#tasks-for-a-desktop-application-environment).
|
||||
`rea environment-build <capture.har> <dir>`.
|
||||
|
||||
## Before recording
|
||||
|
||||
@@ -287,90 +285,3 @@ See
|
||||
[agent environments](https://github.com/morluto/rea/blob/main/docs/agent-environments.md)
|
||||
for what the state, replay and the bundle establish, and
|
||||
[evidence workflows](evidence-workflows.md) for the manifest and state views.
|
||||
|
||||
## Tasks for a desktop application environment
|
||||
|
||||
Use this when the user has a folder from `rea capture-desktop-application` and
|
||||
wants a task for the environment `rea export-desktop-application` makes from
|
||||
it: a prompt for a computer-use agent and checks that grade what the agent
|
||||
left in the app's files. You write the prompt and the checks; REA validates
|
||||
the task against the capture and exports it. These commands are CLI only.
|
||||
|
||||
### Describe the change
|
||||
|
||||
Run `rea describe-desktop-application-task CAPTURE --format json`. Read:
|
||||
|
||||
- `owned_paths`: the only paths whose files the app serves, so the only files
|
||||
a check can read.
|
||||
- `files`: each file there whose state differs between the capture's first
|
||||
and last version, with its `role`, the `baseline` and `final` versions and
|
||||
the net `change`. Text changes list each added and removed line once, and
|
||||
`line_counts` gives lines found at both ends whose count changed.
|
||||
- `task`: a draft whose `checks` are suggestions from the net change of the
|
||||
documents the user worked on, or of the app's own state when no document
|
||||
changed.
|
||||
|
||||
### Write the prompt
|
||||
|
||||
Say what the user wants done, in their words and the app's terms: "Add buy
|
||||
milk to my to-do list and save it". Never say how the file stores it: no
|
||||
paths the user would not name, line formats, XML, keys or SQL, and never the
|
||||
checks. Replace the draft's placeholder; the export refuses it.
|
||||
|
||||
### Pick the checks
|
||||
|
||||
Checks must express only the observed change:
|
||||
|
||||
- Keep a suggestion only if it says what the user did. Drop those that follow
|
||||
from how the app saves: a format rewrite, renumbered lines, counters. A
|
||||
LibreCAD drawing gaining four holes was rewritten from DXF R12 to 2007, and
|
||||
the draft suggests counts of `SECTION`, `ENDSEC` and `CIRCLE`; only
|
||||
`CIRCLE` equals 9 is the user's change.
|
||||
- Never check unchanged content, whole files, or values the app stamps:
|
||||
times, ids it assigns, window sizes. REA refuses ISO 8601 and `asctime`
|
||||
date-times (`Thu Nov 5 09:20:31 2026`); the
|
||||
rest is your judgment.
|
||||
- Prefer a few robust checks: whole lines over positions, rows by meaningful
|
||||
columns over generated ids, `at_least` when the user may add more.
|
||||
|
||||
Kinds: `file_present` and `file_absent`; `lines_present` and `lines_absent`
|
||||
(`lines`); `line_count` (`exact` or `contains`, and `equals`, `at_least` or
|
||||
`at_most`); `value_equals` and `value_absent` (a JSON `pointer` into the view,
|
||||
such as `/groups/GROUP/KEY` for a key file); `text_equals` and
|
||||
`text_contains` (an XML element's whole text at a `pointer`, optional
|
||||
`normalize_whitespace`); `row_present` and `row_absent` (`table`, `where` by
|
||||
column) and `row_count`. Each names its `file` as a path under the home. A
|
||||
document saved as a zip archive, such as FreeCAD's `.FCStd`, reads as its
|
||||
entries: point into an entry's view, and name array members by selector
|
||||
rather than position, as
|
||||
`/entries/[name=Document.xml]/view/root/children/[name=Object][@name=Hole]`,
|
||||
so the check survives an app version that writes them in another order. Give
|
||||
`value_equals` a `tolerance` to compare a number the app writes as text, such
|
||||
as `"5.0000000000000000"`, with a number. Check what the user wrote in a
|
||||
document with `text_equals` on its paragraph element, not `value_equals` on a
|
||||
text node: apps such as AbiWord and Writer split a paragraph into runs as they
|
||||
save, so its first text node holds only part of it; use `text_contains` for a
|
||||
value within a sentence, such as a time. The
|
||||
[desktop application tasks](https://github.com/morluto/rea/blob/main/docs/desktop-application-tasks.md)
|
||||
guide has the exact rules.
|
||||
|
||||
### Export the task
|
||||
|
||||
Write the task as JSON with a `name`, the `prompt` and the `checks`, then run
|
||||
`rea export-desktop-application CAPTURE DIRECTORY --task TASK.json`. REA keeps
|
||||
the task only when its checks fail on the seed and all pass on the captured
|
||||
end state. A refusal names the check: one that fails on the end state does not
|
||||
match what the user left; "every check already passes on the seed" means no
|
||||
check captures the change.
|
||||
|
||||
When the export refuses an application that no apt repository installs, ask
|
||||
the user for the `.deb` file they installed it from and capture with
|
||||
`--package-file`, then ask where the computer may download that file and pass
|
||||
it as `--package-url`. Never fetch a vendor's download page yourself: such
|
||||
sites often refuse automated downloads, and the copy and its licence are the
|
||||
user's to choose.
|
||||
|
||||
Tell the user the task's name, prompt and checks, and how to measure a solve
|
||||
rate: run `agent-env run DIRECTORY --task NAME --agent claude-code` with
|
||||
`--model MODEL` three times and count the runs reported `passed`. Do not run
|
||||
agents unless asked. The bundle's seed is the user's real data: keep it local.
|
||||
@@ -155,18 +155,6 @@ mitigations carry `evidence_kind: "inferred"`; enforcement remains unobserved.
|
||||
See
|
||||
[PE TLS callbacks and load configuration](https://github.com/morluto/rea/blob/main/docs/pe-load-config.md).
|
||||
|
||||
## Binary hardening
|
||||
|
||||
Use `inspect_binary_hardening` for an explicit local ELF, PE, or Mach-O file
|
||||
when the question concerns PIE or ASLR, NX, RELRO, stack-protector or FORTIFY
|
||||
signs, control-flow flags, or stripped state. No `open_binary`, pwntools, or
|
||||
target execution is required. The CLI equivalent is
|
||||
`inspect-binary-hardening PATH`. Universal Mach-O files return one image per
|
||||
slice. A missing import is `unknown`, not proof the protection was omitted.
|
||||
Authenticode and Mach-O code-signature rows report presence, not validity.
|
||||
`hardened_runtime` stays unknown and names `inspect_signature`. See
|
||||
[binary hardening](https://github.com/morluto/rea/blob/main/docs/binary-hardening.md).
|
||||
|
||||
## ELF note tables
|
||||
|
||||
Use `inspect_elf_notes` for an explicit local ELF32/ELF64 image when the
|
||||
@@ -320,17 +308,6 @@ cross-references. Addresses and recovered pseudocode are analysis observations,
|
||||
not original source. Provider unavailability and unsupported metadata remain
|
||||
unknown rather than false.
|
||||
|
||||
For large pseudocode, request `procedure_pseudo_code` with `detail: "summary"`
|
||||
on the first MCP call. It retains the complete observation and returns the
|
||||
original selection, text availability and length without the body. Use
|
||||
`normalized_result.parent_evidence_id` with `inspect_analysis_view` and
|
||||
`view: {kind: "native", facet: "pseudocode", offset: 0, limit: 20000}`;
|
||||
follow `coverage.next_offset` for later pages. Offsets and limits count UTF-16
|
||||
code units, not lines or tokens. Pages reuse the same observation without
|
||||
repeating decompilation. Null and empty text remain distinct. Omitted or
|
||||
`complete` detail preserves full inline delivery. Summary delivery requires a
|
||||
retaining session and does not reduce the work or memory of decompilation.
|
||||
|
||||
For a completed `analyze_function` dossier, use `inspect_analysis_view` with
|
||||
`view: {kind: "native", facet: "value_flow_operations", offset: 0, limit: 32}`
|
||||
and the exact retained Evidence ID to read selected facts without reanalysis.
|
||||
@@ -381,28 +358,6 @@ operation's full ordered list. Candidate capabilities, returned with complete
|
||||
detail, describe possible providers. Top-level session capabilities describe the current route, and
|
||||
`analysis_provider_binding` identifies the selected provider.
|
||||
|
||||
## Continue editable Ghidra analysis across processes
|
||||
|
||||
For a task spanning agent sessions, check `binary_session` after selecting
|
||||
Ghidra: the current provider's `capabilities[].shared_limitations` states whether
|
||||
Program checkpoints are enabled or the session is ephemeral. Successful native
|
||||
queries distinguish initial analysis from an actually restored packed Program;
|
||||
do not infer that restoration happened merely because checkpoint mode is enabled.
|
||||
|
||||
In ephemeral mode, choose a private absolute directory and set
|
||||
`REA_GHIDRA_CHECKPOINT_DIR` in the environment starting REA. Restart the MCP server
|
||||
after changing that launch configuration; no current tool call or runtime
|
||||
analysis-setting update enables it. Stock ELF/PE/Mach-O imports on Linux/macOS
|
||||
are supported; see the [checkpoint setup and limitations](https://github.com/morluto/rea/blob/main/docs/installation.md#automatically-checkpoint-an-owned-ghidra-program).
|
||||
|
||||
With the setting enabled, normal opens restore compatible saved Programs
|
||||
automatically. Initial completed analysis is saved, and `close_binary` saves
|
||||
later metadata edits; no separate save/restore tool is needed. Repair a reported
|
||||
save failure and retry close. Cancellation or process failure can lose edits
|
||||
after the last successful save; unfinished initial analysis cannot resume.
|
||||
Keep the same checkpoint configuration for later processes. Evidence/Unknown
|
||||
history and immutable REA query snapshots have separate persistence workflows.
|
||||
|
||||
## Reuse caller-selected Ghidra analysis
|
||||
|
||||
When the caller supplies an expanded project, configure `REA_GHIDRA_PROJECT_PATH`
|
||||
@@ -426,7 +381,7 @@ When the error names `ghidra.startup_timeout_ms`, call
|
||||
`update_analysis_settings` with `action: "set"` and a larger value within the
|
||||
reported bounds. Resolve retained cleanup before the next startup attempt.
|
||||
No REA restart is needed; an already accepted startup keeps its original
|
||||
budget. Follow the error's session-specific restart guidance, and preserve useful Evidence before closing
|
||||
budget. A new attempt repeats import. Preserve useful Evidence before closing
|
||||
and reopening the target when remediation requires that lifecycle step.
|
||||
|
||||
For a per-function timeout, update `ghidra.decompile_timeout_seconds` and retry
|
||||
@@ -682,7 +637,7 @@ numbering gaps leave coverage unknown; a DfuSe suffix CRC mismatch follows
|
||||
### Function annotations in Ghidra
|
||||
|
||||
Fresh-import sessions admit annotations; existing-project reuse is read-only.
|
||||
Use `annotate_native_function` for one function name and/or entry comments, with at least one explicit change. Review its annotation readback and refreshed dossier inline. Changes are atomic; empty comments clear them, omitted fields preserve them. Later MCP calls observe edits. Normal close saves them when owned Program checkpoint mode is enabled; ephemeral mode discards them. CLI `annotate-native-function` follows the same setting when closing after its updated result. Select the [checkpoint workflow](#continue-editable-ghidra-analysis-across-processes) before edits that must survive a later process. Original executable bytes are unchanged; edits invalidate immutable snapshots. Windows P0 does not admit database mutations. For naming only, `set_address_name` / `set_addresses_names` also work with Ghidra: they name function entries or data addresses (primary labels) and return compact per-address success booleans instead of a dossier. Ghidra rejects batch addresses that request the same name or a name another selected address holds; `raw_result.failures` gives each failure's reason.
|
||||
Use `annotate_native_function` for one function name and/or entry comments, with at least one explicit change. Review its annotation readback and refreshed dossier inline. Changes are atomic and session-scoped; empty comments clear them, omitted fields preserve them. Later MCP calls observe edits until close. CLI `annotate-native-function` returns the updated analysis before discarding the session. Original executable bytes are unchanged; edits invalidate immutable snapshots. Windows P0 does not admit database mutations. For naming only, `set_address_name` / `set_addresses_names` also work with Ghidra: they name function entries or data addresses (primary labels) and return compact per-address success booleans instead of a dossier. Ghidra rejects batch addresses that request the same name or a name another selected address holds; `raw_result.failures` gives each failure's reason.
|
||||
|
||||
For `compare_functions`, obtain `analyze_function` Evidence before and after the
|
||||
edit while the MCP session is open; annotation Evidence is not a comparison
|
||||
@@ -753,13 +708,6 @@ cache one decoded block and support retained filtered/paged views. No
|
||||
decryption or SerializedFile object decoding is provided. See
|
||||
[UnityFS](https://github.com/morluto/rea/blob/main/docs/unityfs.md).
|
||||
|
||||
WeChat mini-program packages (`.wxapkg` files with `0xBE`/`0xED` marks) list
|
||||
members with declared names and absolute carrier offsets; member bytes are
|
||||
stored verbatim. `V1MMWX` PC-client packages report `encrypted` directory
|
||||
status with unknown coverage and are refused on extraction; no decryption is
|
||||
attempted. See
|
||||
[wxapkg](https://github.com/morluto/rea/blob/main/docs/wxapkg.md).
|
||||
|
||||
U-Boot legacy uImages use the same inspection/extraction workflows. Prefer a
|
||||
retained summary and selected inventory views for header facts or component
|
||||
IDs. Header addresses/CPU/OS are declarations; payloads remain as stored.
|
||||
|
||||
@@ -54,11 +54,10 @@ jobs:
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
# The pinned framework builds its gateway and service-db images only when pulling them from
|
||||
# ghcr.io fails, from public ECR base images; public ECR caps anonymous pulls by data per source
|
||||
# IP and hosted runners share IPs, so such a build can fail here for hours. mirror.gcr.io serves
|
||||
# the same Docker official images under the same digests, so each base image those Dockerfiles
|
||||
# name is pulled there and named as its Dockerfile names it, as the framework's own CI does.
|
||||
# Public ECR caps anonymous pulls by data per source IP and hosted runners share IPs, so the
|
||||
# framework's image builds can fail here for hours. mirror.gcr.io serves the same Docker
|
||||
# official images under the same digests, so each base image the pinned framework builds from
|
||||
# is pulled there and named as its Dockerfile names it, as the framework's own CI does.
|
||||
# Digest-pinned names are set in containerd, whose images Docker reads only on the containerd
|
||||
# image store; switching stores restarts dockerd, so it happens before anything uses Docker.
|
||||
- name: Use the containerd image store
|
||||
@@ -73,16 +72,16 @@ jobs:
|
||||
run: |
|
||||
runner_root="${RUNNER_TEMP}/rea-agentenv-runner"
|
||||
python -m venv "${runner_root}"
|
||||
"${runner_root}/bin/python" -m pip install --require-virtualenv --no-cache-dir 'agentenv-framework==0.9.1431'
|
||||
"${runner_root}/bin/python" -m pip install --require-virtualenv --no-cache-dir 'agentenv-framework==0.9.1334'
|
||||
"${runner_root}/bin/agent-env" --version
|
||||
docker version --format '{{.Server.Version}}'
|
||||
echo "REA_AGENT_ENV_COMMAND=${runner_root}/bin/agent-env" >> "${GITHUB_ENV}"
|
||||
echo "REA_DOCKER_COMMAND=$(command -v docker)" >> "${GITHUB_ENV}"
|
||||
- name: Pull the gateway and service-db public ECR base images from mirror.gcr.io
|
||||
- name: Pull the pinned framework's public ECR base images from mirror.gcr.io
|
||||
shell: bash
|
||||
run: |
|
||||
package="$("${REA_AGENT_ENV_COMMAND%/agent-env}/python" -c 'import agent_env, pathlib; print(pathlib.Path(agent_env.__file__).parent)')"
|
||||
grep -rhoE '^FROM +public\.ecr\.aws/docker/library/[^ ]+' --include='Dockerfile*' "${package}/env/gateway" "${package}/env/envs/service_db" | awk '{print $2}' | sort -u |
|
||||
grep -rhoE '^FROM +public\.ecr\.aws/docker/library/[^ ]+' --include='Dockerfile*' "${package}" | awk '{print $2}' | sort -u |
|
||||
while read -r image; do
|
||||
name=${image#public.ecr.aws/docker/library/}
|
||||
source=mirror.gcr.io/library/$name
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Website](https://rea.tools/) · [Guides](https://rea.tools/guides/) · [Showcases](https://rea.tools/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Join the REA community on Discord" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[Website](https://rea.tools/) · [Guides](https://rea.tools/guides/) · [Showcases](https://rea.tools/showcase/)**
|
||||
|
||||
[Quick start](#quick-start) · [How REA works](#how-rea-works) · [What you can analyze](#what-you-can-analyze) · [Showcases](#showcases) · [Build RL environments](#build-rl-environments) · [FAQ](#faq) · [Documentation](#documentation)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -205,7 +205,6 @@ Additional tools and host support depend on the target:
|
||||
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](docs/pe-certificates.md) |
|
||||
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](docs/pe-imports.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](docs/elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](docs/binary-hardening.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and documented crash classes | Built-in static JSON inspection; [Apple IPS](docs/apple-ips.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](docs/kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, imports, lambdas and the Code header | Built-in static BEAM inspection; [Erlang BEAM](docs/erlang-beam.md) |
|
||||
|
||||
@@ -60,7 +60,7 @@ pin inspection sources; they are not included in the npm package.
|
||||
| ghidra-nativeaot | [MIT](https://github.com/Washi1337/ghidra-nativeaot/blob/effeb734fc570c32650f88b159608979dc7b423e/LICENSE.md) | Pinned inspection source; derived parser attribution above. |
|
||||
| JADX | [Apache-2.0](https://github.com/skylot/jadx/blob/v1.5.6/LICENSE) | The audited jadx-headless-mcp source uses JADX 1.5.6 for Android decompilation; the engine JAR is separately supplied. |
|
||||
| jadx-headless-mcp | [Apache-2.0](https://github.com/1013503897/jadx-headless-mcp/blob/5844800a486d2248fa949b74c9896285f5b54de2/LICENSE) | v0.7.1; pinned submodule and user-supplied JAR. |
|
||||
| Kuna | [Apache-2.0](https://github.com/Noelo-Lab/kuna/blob/v1.990/LICENSE), with its [NOTICE](https://github.com/Noelo-Lab/kuna/blob/v1.990/NOTICE) | Bring your own `kuna`; REA verified 1.990 and never downloads, installs or bundles it. REA invokes it as a separate process. |
|
||||
| Kuna | [Apache-2.0](https://github.com/Noelo-Lab/kuna/blob/v1.989/LICENSE), with its [NOTICE](https://github.com/Noelo-Lab/kuna/blob/v1.989/NOTICE) | Bring your own `kuna`; REA verified 1.989 and never downloads, installs or bundles it. REA invokes it as a separate process. |
|
||||
| Apktool | [Apache-2.0](https://github.com/iBotPeaches/Apktool/blob/master/LICENSE.md) | User-supplied Android resource decoder. |
|
||||
| Binwalk | [MIT](https://github.com/ReFirmLabs/binwalk/blob/4fdab3d464d97b68e0af9088df3f9e2e1545b21c/LICENSE) | v3.1.0; pinned submodule and user-supplied firmware engine. |
|
||||
| Unblob | [MIT](https://github.com/onekey-sec/unblob/blob/1fcc7a0a584a70a96c31f5a276c20944d199a089/LICENSE) | 26.6.4; pinned submodule and user-supplied firmware engine. |
|
||||
|
||||
@@ -1,262 +0,0 @@
|
||||
"""Run REA inside an agentenv build computer for REA on the host: capture the app, draft its task and export it.
|
||||
|
||||
REA sends this file's source through the computer's ``computer-exec/v1`` route as
|
||||
``python3 -I -c <source> <command> <JSON arguments>``; each command prints one JSON line and exits. ``start``
|
||||
leaves the capture running on its own: its stdin is a FIFO it holds open for reading and writing, so it never
|
||||
sees the end of its input, and ``mark`` and ``stop`` write their requests there and wait for the capture's own
|
||||
acknowledgement on its progress log. A small shell around the capture writes its exit status to a file, the
|
||||
mark a later command reads. REA runs as the session user in the home, with no ``AGENTENV_*`` variable, so
|
||||
neither REA nor the app it launches holds the computer's control token, and with ``APT_CONFIG`` naming the
|
||||
package lists ``start`` downloads as that user, so the capture finds the package's sources.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
POLL_S = 0.2
|
||||
TAIL_BYTES = 4096
|
||||
|
||||
|
||||
def _say(value: dict) -> int:
|
||||
print(json.dumps(value), flush=True)
|
||||
return 0
|
||||
|
||||
|
||||
def _tail(path: str) -> str:
|
||||
try:
|
||||
with open(path, "rb") as handle:
|
||||
handle.seek(max(0, os.path.getsize(path) - TAIL_BYTES))
|
||||
return handle.read().decode("utf-8", "replace")
|
||||
except OSError:
|
||||
return ""
|
||||
|
||||
|
||||
def _environment(work: str) -> dict:
|
||||
environment = {name: value for name, value in os.environ.items() if not name.startswith("AGENTENV_")}
|
||||
environment["APT_CONFIG"] = os.path.join(work, "apt", "apt.conf")
|
||||
return environment
|
||||
|
||||
|
||||
def _apt(work: str, timeout: float) -> dict:
|
||||
"""Download the package lists into the work folder as the session user, until one update succeeds."""
|
||||
root = os.path.join(work, "apt")
|
||||
lists = os.path.join(root, "lists")
|
||||
updated = os.path.join(root, "updated")
|
||||
os.makedirs(os.path.join(lists, "partial"), exist_ok=True)
|
||||
os.makedirs(os.path.join(root, "cache", "archives", "partial"), exist_ok=True)
|
||||
with open(os.path.join(root, "apt.conf"), "w", encoding="utf-8") as conf:
|
||||
conf.write(
|
||||
f'Dir::State::Lists "{lists}";\n'
|
||||
f'Dir::Cache "{root}/cache";\n'
|
||||
'Debug::NoLocking "true";\n'
|
||||
'APT::Update::Error-Mode "any";\n'
|
||||
)
|
||||
# apt keeps lists compressed as it fetched them, such as *_Packages.lz4 on Ubuntu.
|
||||
count = lambda: len([name for name in os.listdir(lists) if re.search(r"_Packages(\.\w+)?$", name)]) # noqa: E731
|
||||
# A marker left by an update that fetched nothing is not a success; download again.
|
||||
if os.path.exists(updated) and count() > 0:
|
||||
return {"lists": count(), "updated": False}
|
||||
log = os.path.join(root, "update.log")
|
||||
try:
|
||||
with open(log, "wb") as output:
|
||||
code = subprocess.run(["apt-get", "update"], env=_environment(work), stdin=subprocess.DEVNULL,
|
||||
stdout=output, stderr=subprocess.STDOUT, timeout=timeout).returncode
|
||||
except (OSError, subprocess.TimeoutExpired) as error:
|
||||
return {"lists": count(), "updated": True, "error": f"{type(error).__name__}: {error}"}
|
||||
lists_fetched = count()
|
||||
if code == 0 and lists_fetched > 0:
|
||||
# Only a whole update that fetched lists is kept, so a failed or empty one runs again.
|
||||
open(updated, "w").close()
|
||||
return {"lists": lists_fetched, "updated": True, "exit_code": code}
|
||||
if code == 0:
|
||||
return {"lists": lists_fetched, "updated": True, "exit_code": code,
|
||||
"error": "apt-get update exited 0 without a package list"}
|
||||
return {"lists": lists_fetched, "updated": True, "exit_code": code,
|
||||
"error": f"apt-get update exited {code}: {_tail(log)[-1500:].strip()}"}
|
||||
|
||||
|
||||
def _exit_code(capture: str):
|
||||
try:
|
||||
with open(os.path.join(capture, "exit"), encoding="utf-8") as handle:
|
||||
return int(handle.read().strip() or "-1")
|
||||
except (OSError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def _event_line(path: str, offset: int, kinds: set, name):
|
||||
"""The first progress line after ``offset`` whose event is of one of ``kinds``, for ``name`` when given."""
|
||||
try:
|
||||
with open(path, "rb") as handle:
|
||||
handle.seek(offset)
|
||||
lines = handle.read().decode("utf-8", "replace").splitlines()
|
||||
except OSError:
|
||||
return None
|
||||
for line in lines:
|
||||
try:
|
||||
event = json.loads(line)["rea_progress"]["event"]
|
||||
except (ValueError, KeyError, TypeError):
|
||||
continue
|
||||
if isinstance(event, dict) and event.get("kind") in kinds and (name is None or event.get("name") == name):
|
||||
return line
|
||||
return None
|
||||
|
||||
|
||||
def _wait(capture: str, offset: int, kinds: set, name, timeout: float) -> dict:
|
||||
"""Wait for the capture's event, or for it to exit, or for time to run out."""
|
||||
log = os.path.join(capture, "progress.log")
|
||||
deadline = time.monotonic() + timeout
|
||||
while True:
|
||||
line = _event_line(log, offset, kinds, name)
|
||||
if line is not None:
|
||||
return {"line": line}
|
||||
code = _exit_code(capture)
|
||||
if code is not None:
|
||||
return {"exited": code, "progress": _tail(log), "output": _tail(os.path.join(capture, "capture.out"))}
|
||||
if time.monotonic() > deadline:
|
||||
return {"timed_out": True, "progress": _tail(log)}
|
||||
time.sleep(POLL_S)
|
||||
|
||||
|
||||
def start(arguments: dict) -> int:
|
||||
work, capture = os.path.abspath(arguments["work"]), os.path.abspath(arguments["capture"])
|
||||
rea = shutil.which("rea")
|
||||
if rea is None:
|
||||
return _say({"error": "REA is not installed in this computer; start the computer from a request, which installs it."})
|
||||
if os.path.exists(capture):
|
||||
return _say({"error": f"the capture folder {capture} is already used"})
|
||||
apt = _apt(work, arguments.get("apt_timeout_s", 600))
|
||||
try:
|
||||
os.makedirs(capture)
|
||||
except FileExistsError:
|
||||
return _say({"error": f"a stop claimed {capture} before the capture launched", "apt": apt})
|
||||
control = os.path.join(capture, "control")
|
||||
os.mkfifo(control, 0o600)
|
||||
argv = [rea, "capture-desktop-application", "--desktop-entry", arguments["desktop_entry"],
|
||||
"--output", os.path.join(capture, "capture"), "--exclude", work, "--json"]
|
||||
for value in arguments.get("arguments", []):
|
||||
argv += ["--argument", value]
|
||||
if arguments.get("package_file"):
|
||||
argv += ["--package-file", arguments["package_file"]]
|
||||
# Reading and writing, so the capture never reads the end of its input between requests.
|
||||
reader = os.open(control, os.O_RDWR)
|
||||
try:
|
||||
with open(os.path.join(capture, "capture.out"), "wb") as out, \
|
||||
open(os.path.join(capture, "progress.log"), "wb") as progress:
|
||||
process = subprocess.Popen(
|
||||
["sh", "-c", 'done=$1; shift; "$@"; code=$?; printf "%s" "$code" > "$done.tmp" && mv "$done.tmp" "$done"',
|
||||
"sh", os.path.join(capture, "exit"), *argv],
|
||||
cwd=os.path.expanduser("~"), env=_environment(work), stdin=reader, stdout=out, stderr=progress,
|
||||
start_new_session=True)
|
||||
finally:
|
||||
os.close(reader)
|
||||
waited = _wait(capture, 0, {"window_open"}, None, arguments.get("timeout_s", 120))
|
||||
return _say({"pid": process.pid, "apt": apt, **waited})
|
||||
|
||||
|
||||
def _request(capture: str, line: str) -> bool:
|
||||
"""Write one request to the running capture; False when nothing reads its FIFO any more."""
|
||||
try:
|
||||
fd = os.open(os.path.join(capture, "control"), os.O_WRONLY | os.O_NONBLOCK)
|
||||
except OSError:
|
||||
return False
|
||||
try:
|
||||
os.write(fd, (line + "\n").encode("utf-8"))
|
||||
finally:
|
||||
os.close(fd)
|
||||
return True
|
||||
|
||||
|
||||
def mark(arguments: dict) -> int:
|
||||
capture, name = os.path.abspath(arguments["capture"]), arguments["name"]
|
||||
code = _exit_code(capture)
|
||||
log = os.path.join(capture, "progress.log")
|
||||
if code is not None:
|
||||
return _say({"exited": code, "progress": _tail(log)})
|
||||
# A checkpoint already taken, whose answer was lost, is answered from the capture's own log.
|
||||
taken = _event_line(log, 0, {"checkpoint"}, name)
|
||||
if taken is not None:
|
||||
return _say({"line": taken})
|
||||
offset = os.path.getsize(log)
|
||||
if not _request(capture, f"checkpoint {name}"):
|
||||
return _say({"error": "the capture is not reading its requests"})
|
||||
return _say(_wait(capture, offset, {"checkpoint", "checkpoint_refused"}, name, arguments.get("timeout_s", 600)))
|
||||
|
||||
|
||||
def stop(arguments: dict) -> int:
|
||||
capture = os.path.abspath(arguments["capture"])
|
||||
if _exit_code(capture) is None and not os.path.exists(os.path.join(capture, "control")):
|
||||
# Never launched: claim the folder, so a start still on its way finds it used and launches nothing.
|
||||
os.makedirs(capture, exist_ok=True)
|
||||
return _say({"absent": True})
|
||||
if _exit_code(capture) is None:
|
||||
_request(capture, "stop")
|
||||
deadline = time.monotonic() + arguments.get("timeout_s", 600)
|
||||
while (code := _exit_code(capture)) is None and time.monotonic() < deadline:
|
||||
time.sleep(POLL_S)
|
||||
log = os.path.join(capture, "progress.log")
|
||||
if code is None:
|
||||
return _say({"timed_out": True, "progress": _tail(log)})
|
||||
output = os.path.join(capture, "capture.out")
|
||||
return _say({"exit_code": code, "progress": _tail(log), "output_bytes": os.path.getsize(output)})
|
||||
|
||||
|
||||
def run(arguments: dict) -> int:
|
||||
"""Run one REA command with its output in a file, after writing the files it reads."""
|
||||
rea = shutil.which("rea")
|
||||
if rea is None:
|
||||
return _say({"error": "REA is not installed in this computer"})
|
||||
for path, text in arguments.get("files", {}).items():
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(path, "w", encoding="utf-8") as handle:
|
||||
handle.write(text)
|
||||
with open(arguments["stdout"], "wb") as out:
|
||||
try:
|
||||
done = subprocess.run([rea, *arguments["argv"]], cwd=os.path.expanduser("~"),
|
||||
env=_environment(os.path.abspath(arguments["work"])), stdin=subprocess.DEVNULL, stdout=out,
|
||||
stderr=subprocess.PIPE, timeout=arguments.get("timeout_s", 1800))
|
||||
except subprocess.TimeoutExpired:
|
||||
return _say({"timed_out": True})
|
||||
return _say({"exit_code": done.returncode, "stdout_bytes": os.path.getsize(arguments["stdout"]),
|
||||
"stderr": done.stderr[-TAIL_BYTES:].decode("utf-8", "replace")})
|
||||
|
||||
|
||||
def listing(arguments: dict) -> int:
|
||||
"""Every regular file under a folder, by its path relative to the home, with its size and SHA-256.
|
||||
|
||||
The list goes to the file ``output`` names, outside the folder, and the answer gives its size: a large
|
||||
bundle's list outgrows the 1 MiB of stdout a command answers with, so REA reads it through ``data/get``.
|
||||
"""
|
||||
home = os.path.expanduser("~")
|
||||
files, skipped = [], []
|
||||
for directory, folders, names in os.walk(os.path.abspath(arguments["root"])):
|
||||
folders.sort()
|
||||
for name in sorted(names):
|
||||
path = os.path.join(directory, name)
|
||||
relative = os.path.relpath(path, home)
|
||||
if os.path.islink(path) or not os.path.isfile(path):
|
||||
skipped.append(relative)
|
||||
continue
|
||||
digest = hashlib.sha256()
|
||||
with open(path, "rb") as handle:
|
||||
for chunk in iter(lambda: handle.read(1 << 20), b""):
|
||||
digest.update(chunk)
|
||||
files.append({"path": relative, "size": os.path.getsize(path), "sha256": digest.hexdigest()})
|
||||
output = os.path.abspath(arguments["output"])
|
||||
os.makedirs(os.path.dirname(output), exist_ok=True)
|
||||
with open(output, "w", encoding="utf-8") as handle:
|
||||
json.dump({"files": files, "skipped": skipped}, handle)
|
||||
return _say({"listing_bytes": os.path.getsize(output)})
|
||||
|
||||
|
||||
COMMANDS = {"start": start, "mark": mark, "stop": stop, "run": run, "list": listing}
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(COMMANDS[sys.argv[1]](json.loads(sys.argv[2])))
|
||||
@@ -1,83 +0,0 @@
|
||||
"""Keep one agentenv computer up for REA until stdin closes or SIGTERM arrives.
|
||||
|
||||
REA runs ``computer.py <bundle folder> [task]`` with the agentenv runner's own interpreter, so ``agent_env``
|
||||
imports. It deploys the bundle's task, ``smoke`` by default, on this machine with
|
||||
``run_bundle(keep=True, emulate=False)``, prints one JSON line naming the computer's address, MCP URL, control
|
||||
token and sandbox id, or ``{"error": ...}``, and tears the computer down once its stdin closes or it gets SIGTERM
|
||||
or SIGINT. Everything else it or agentenv prints, a build's output included, goes to stderr. It exits 0 once
|
||||
the teardown left nothing up, 1 when it did, and 2 when no computer came up.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import threading
|
||||
|
||||
|
||||
def _protocol():
|
||||
"""A private copy of stdout for the JSON line; fd 1 then points at stderr, as do children that inherit it."""
|
||||
protocol = os.fdopen(os.dup(1), "w", encoding="utf-8")
|
||||
os.dup2(2, 1)
|
||||
sys.stdout = sys.stderr
|
||||
return protocol
|
||||
|
||||
|
||||
def _say(protocol, value: dict) -> None:
|
||||
protocol.write(json.dumps(value) + "\n")
|
||||
protocol.flush()
|
||||
|
||||
|
||||
def _computer(result):
|
||||
"""The run's deployed computer, or why there is none."""
|
||||
run = result.runs[0] if result.runs else None
|
||||
if run is None:
|
||||
return None, "the bundle ran no task"
|
||||
if run.error is not None or run.failed_steps:
|
||||
return None, f"the task failed: {run.error or run.failed_steps}"
|
||||
for env in run.context.deployed_envs:
|
||||
if env.environment_url and env.mcp_url and getattr(env, "sandbox_id", None):
|
||||
return env, None
|
||||
return None, "the task deployed no computer"
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
protocol = _protocol()
|
||||
bundle, task = argv[1], argv[2] if len(argv) > 2 else "smoke"
|
||||
try:
|
||||
from agent_env.bundle import run_bundle
|
||||
except ImportError as error:
|
||||
_say(protocol, {"error": f"agent_env does not import with {sys.executable}: {error}"})
|
||||
return 2
|
||||
try:
|
||||
result = run_bundle(bundle, tasks=[task], keep=True, emulate=False, sandbox="local",
|
||||
on_progress=lambda line: print(line, file=sys.stderr, flush=True))
|
||||
except BaseException as error: # noqa: BLE001 - a signal's RunInterrupted too; run_bundle tore its runs down
|
||||
_say(protocol, {"error": f"{type(error).__name__}: {error}"})
|
||||
return 2
|
||||
stop = threading.Event()
|
||||
for signum in (signal.SIGTERM, signal.SIGINT):
|
||||
signal.signal(signum, lambda *_: stop.set())
|
||||
env, problem = _computer(result)
|
||||
try:
|
||||
if env is None:
|
||||
_say(protocol, {"error": problem})
|
||||
else:
|
||||
_say(protocol, {"base_url": env.environment_url, "mcp_url": env.mcp_url,
|
||||
"control_token": env.control_token, "sandbox_id": env.sandbox_id})
|
||||
threading.Thread(target=lambda: (sys.stdin.buffer.read(), stop.set()), daemon=True).start()
|
||||
while not stop.wait(0.5):
|
||||
pass
|
||||
finally:
|
||||
torn_down = result.teardown()
|
||||
still_up = [sandbox for run in torn_down.runs if run.torn_down is not None for sandbox in run.torn_down.still_up]
|
||||
if still_up:
|
||||
print(f"still up after the teardown: {still_up}", file=sys.stderr, flush=True)
|
||||
return 1
|
||||
return 0 if env is not None else 2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv))
|
||||
@@ -179,16 +179,12 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
private List<FunctionEntry> procedureInventory;
|
||||
private List<InventoryItem> stringInventory;
|
||||
private DecompInterface decompiler;
|
||||
// A refused Program may only report its admission failure to the client.
|
||||
private String identityAdmissionFailure;
|
||||
/** Per-function native decompiler limit from the session; 0 leaves it unbounded. */
|
||||
private int decompileTimeoutSeconds;
|
||||
// DecompileProcess converts seconds to an int millisecond timer delay.
|
||||
private static final int MAX_DECOMPILE_TIMEOUT_SECONDS = Integer.MAX_VALUE / 1000;
|
||||
private final ReaGhidraExtensions analysisExtensions = new ReaGhidraExtensions();
|
||||
private static AddressSpace sessionDefaultAddressSpace;
|
||||
private Path checkpointDirectory;
|
||||
private long checkpointModification;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
@@ -197,7 +193,6 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
throw new IllegalArgumentException("REA bridge requires one session descriptor");
|
||||
}
|
||||
Path descriptorPath = Path.of(arguments[0]);
|
||||
checkpointDirectory = descriptorPath.toAbsolutePath().normalize().getParent();
|
||||
SessionDescriptor descriptor = readDescriptor(descriptorPath);
|
||||
decompileTimeoutSeconds = descriptor.decompileTimeoutSeconds();
|
||||
// Windows retains a native immutable bearer descriptor lease until
|
||||
@@ -229,17 +224,14 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
throw new IllegalStateException("Ghidra provider version does not match the session");
|
||||
}
|
||||
String importedSha256 = currentProgram.getExecutableSHA256();
|
||||
if (importedSha256 == null || !importedSha256.matches("(?i)[0-9a-f]{64}")) {
|
||||
identityAdmissionFailure = "import_identity_unavailable";
|
||||
}
|
||||
else if (!constantTimeEquals(importedSha256.toLowerCase(Locale.ROOT),
|
||||
descriptor.targetSha256)) {
|
||||
identityAdmissionFailure = "import_identity_mismatch";
|
||||
}
|
||||
if (identityAdmissionFailure != null) {
|
||||
end(false);
|
||||
serve(descriptor);
|
||||
return;
|
||||
if (importedSha256 == null ||
|
||||
!constantTimeEquals(
|
||||
importedSha256.toLowerCase(Locale.ROOT),
|
||||
descriptor.targetSha256
|
||||
)) {
|
||||
throw new IllegalStateException(
|
||||
"Ghidra imported-byte digest does not match the admitted target"
|
||||
);
|
||||
}
|
||||
admittedTargetSha256 = descriptor.targetSha256;
|
||||
// GhidraScript wraps run() in a transaction. End it before serving so
|
||||
@@ -250,7 +242,6 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
throw new IOException("Windows Ghidra P0 does not admit metadata recovery or database mutation.");
|
||||
analysisExtensions.analyze(descriptor.analysisExtensions, currentProgram, monitor);
|
||||
initializeDecompiler();
|
||||
checkpointModification = currentProgram.getModificationNumber();
|
||||
serve(descriptor);
|
||||
}
|
||||
finally {
|
||||
@@ -414,14 +405,6 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
writeSuccess(writer, request.id, result);
|
||||
return;
|
||||
}
|
||||
if (identityAdmissionFailure != null) {
|
||||
// Keep the producer alive until the client has consumed the
|
||||
// refusal and closed or sent shutdown. Every other request
|
||||
// remains refused, including further startup attempts.
|
||||
writeFailure(writer, request.id, identityAdmissionFailure,
|
||||
"Ghidra imported identity admission failed");
|
||||
continue;
|
||||
}
|
||||
decompileTimeoutSeconds = request.decompileTimeoutSeconds() == null
|
||||
? descriptor.decompileTimeoutSeconds() : request.decompileTimeoutSeconds();
|
||||
writeSuccess(writer, request.id, handleRequest(request, descriptor));
|
||||
@@ -455,7 +438,6 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
if (descriptor.existingProject && isMutatingOperation(request.method))
|
||||
throw new RequestFailure("method_unavailable", "Existing Ghidra project sessions are read-only");
|
||||
return switch (request.method) {
|
||||
case "checkpoint_program" -> checkpointProgram(request.params, descriptor);
|
||||
case "read_binary_inventory_summary" -> binaryInventorySummary(request.params);
|
||||
case "inspect_native_load_image" -> inspectNativeLoadImage(request.params);
|
||||
case "read_bytes" -> readMemoryBytes(request.params);
|
||||
@@ -511,31 +493,6 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
return method.equals("annotate_native_function") || method.equals("set_address_name") || method.equals("set_addresses_names");
|
||||
}
|
||||
|
||||
/** Internal lifecycle request: the destination is inside this owned runtime. */
|
||||
private JsonObject checkpointProgram(JsonObject params, SessionDescriptor descriptor) throws Exception {
|
||||
requireKeys(params, Set.of("path", "force"));
|
||||
if (descriptor.existingProject || !descriptor.transport.equals("unix-socket"))
|
||||
throw new RequestFailure("method_unavailable", "This Program has no checkpoint authority");
|
||||
Path output = Path.of(requireString(params, "path")).toAbsolutePath().normalize();
|
||||
if (!output.getParent().equals(checkpointDirectory) ||
|
||||
!output.getFileName().toString().matches("checkpoint-[a-f0-9-]{36}\\.gzf"))
|
||||
throw new RequestFailure("invalid_request", "Checkpoint destination must belong to this runtime");
|
||||
JsonElement forced = params.get("force");
|
||||
if (!forced.isJsonPrimitive() || !forced.getAsJsonPrimitive().isBoolean())
|
||||
throw new RequestFailure("invalid_request", "Checkpoint force must be a boolean");
|
||||
boolean force = forced.getAsBoolean();
|
||||
JsonObject result = new JsonObject();
|
||||
boolean changed = currentProgram.getModificationNumber() != checkpointModification;
|
||||
result.addProperty("saved", force || changed);
|
||||
if (force || changed) {
|
||||
if (Files.exists(output)) throw new RequestFailure("invalid_request", "Checkpoint destination already exists");
|
||||
currentProgram.saveToPackedFile(output.toFile(), monitor);
|
||||
checkpointModification = currentProgram.getModificationNumber();
|
||||
result.addProperty("path", output.toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private JsonObject sessionInfo(SessionDescriptor descriptor) throws Exception {
|
||||
boolean timedOut = analysisTimeoutOccurred();
|
||||
JsonObject provider = new JsonObject();
|
||||
@@ -814,8 +771,6 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
return result;
|
||||
}
|
||||
|
||||
private record ByteSearchBlock(MemoryBlock block, Address low, Address high) {}
|
||||
|
||||
private JsonObject searchMemoryBytes(JsonObject params, SessionDescriptor descriptor) throws Exception {
|
||||
// mask, start, end, and cursor are omitted when absent; JSON encoding drops those keys.
|
||||
Set<String> keys = params.keySet();
|
||||
@@ -876,13 +831,16 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
JsonArray blocks = new JsonArray();
|
||||
JsonArray skipped = new JsonArray();
|
||||
JsonArray notLoaded = new JsonArray();
|
||||
List<ByteSearchBlock> eligible = new ArrayList<>();
|
||||
long covered = 0;
|
||||
String next = null;
|
||||
for (MemoryBlock block : ordered) {
|
||||
monitor.checkCancelled();
|
||||
Address low = block.getStart();
|
||||
Address high = block.getEnd();
|
||||
Address bound = start == null ? end : start;
|
||||
if (bound != null && !bound.getAddressSpace().equals(low.getAddressSpace())) continue;
|
||||
// Without bounds, search loaded memory only. Non-loaded file parts
|
||||
// (such as .symtab) and overlays stay reachable through explicit bounds.
|
||||
if (bound == null && (!block.isLoaded() || block.isOverlay())) {
|
||||
notLoaded.add(byteSearchCoverage(block, low, high));
|
||||
continue;
|
||||
@@ -894,66 +852,40 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
skipped.add(byteSearchCoverage(block, low, high));
|
||||
continue;
|
||||
}
|
||||
eligible.add(new ByteSearchBlock(block, low, high));
|
||||
}
|
||||
long covered = 0;
|
||||
String next = null;
|
||||
for (int first = 0; first < eligible.size() && next == null;) {
|
||||
monitor.checkCancelled();
|
||||
int last = first;
|
||||
while (last + 1 < eligible.size()) {
|
||||
monitor.checkCancelled();
|
||||
ByteSearchBlock previous = eligible.get(last);
|
||||
ByteSearchBlock following = eligible.get(last + 1);
|
||||
if (!previous.high().getAddressSpace().equals(following.low().getAddressSpace()) ||
|
||||
previous.high().next() == null || !previous.high().next().equals(following.low())) break;
|
||||
last++;
|
||||
}
|
||||
Address low = eligible.get(first).low();
|
||||
Address high = eligible.get(last).high();
|
||||
if (next != null) continue;
|
||||
if (resume != null && low.compareTo(resume) < 0) low = resume;
|
||||
if (low.compareTo(high) <= 0 && low.getAddressSpace().equals(high.getAddressSpace())) {
|
||||
Address at = low;
|
||||
Address examinedEnd = high;
|
||||
while (at != null && at.compareTo(high) <= 0 && high.subtract(at) >= bytes.length - 1L) {
|
||||
monitor.checkCancelled();
|
||||
// findBytes bounds candidate starts. The entire candidate remains in this eligible run.
|
||||
Address found = currentProgram.getMemory().findBytes(at, high.subtract(bytes.length - 1L), bytes, masks, true, monitor);
|
||||
monitor.checkCancelled();
|
||||
if (found == null || high.subtract(found) < bytes.length - 1L) break;
|
||||
if (matches.size() == limit) {
|
||||
next = binding + ":" + canonicalAddress(found);
|
||||
examinedEnd = found.addNoWrap(bytes.length - 1L);
|
||||
break;
|
||||
}
|
||||
MemoryBlock matchBlock = currentProgram.getMemory().getBlock(found);
|
||||
JsonObject match = new JsonObject();
|
||||
match.addProperty("address", canonicalAddress(found));
|
||||
match.addProperty("segment", matchBlock.getName());
|
||||
Long offset = targetFileOffset(matchBlock, found).offset();
|
||||
match.add("file_offset", offset == null ? JsonNull.INSTANCE : GSON.toJsonTree(offset));
|
||||
Function function = currentProgram.getFunctionManager().getFunctionContaining(found);
|
||||
JsonObject procedure = new JsonObject();
|
||||
if (function != null) {
|
||||
procedure.addProperty("address", canonicalAddress(function.getEntryPoint()));
|
||||
procedure.addProperty("name", function.getName(true));
|
||||
}
|
||||
match.add("procedure", function == null ? JsonNull.INSTANCE : procedure);
|
||||
matches.add(match);
|
||||
at = found.next();
|
||||
if (low.compareTo(high) > 0 || !low.getAddressSpace().equals(high.getAddressSpace())) continue;
|
||||
Address at = low;
|
||||
Address examinedEnd = high;
|
||||
while (at != null && at.compareTo(high) <= 0 && high.subtract(at) >= bytes.length - 1L) {
|
||||
monitor.checkCancelled();
|
||||
// Ghidra's end address bounds candidate starts, not the final matched byte.
|
||||
Address found = currentProgram.getMemory().findBytes(at, high.subtract(bytes.length - 1L), bytes, masks, true, monitor);
|
||||
monitor.checkCancelled();
|
||||
if (found == null || high.subtract(found) < bytes.length - 1L) break;
|
||||
if (matches.size() == limit) {
|
||||
next = binding + ":" + canonicalAddress(found);
|
||||
examinedEnd = found.addNoWrap(bytes.length - 1L);
|
||||
break;
|
||||
}
|
||||
for (int index = first; index <= last; index++) {
|
||||
monitor.checkCancelled();
|
||||
ByteSearchBlock item = eligible.get(index);
|
||||
Address coveredLow = item.low().compareTo(low) < 0 ? low : item.low();
|
||||
Address coveredHigh = item.high().compareTo(examinedEnd) > 0 ? examinedEnd : item.high();
|
||||
if (coveredLow.compareTo(coveredHigh) > 0) continue;
|
||||
JsonObject coverage = byteSearchCoverage(item.block(), coveredLow, coveredHigh);
|
||||
covered = Math.addExact(covered, coverage.get("bytes").getAsLong());
|
||||
blocks.add(coverage);
|
||||
JsonObject match = new JsonObject();
|
||||
match.addProperty("address", canonicalAddress(found));
|
||||
match.addProperty("segment", block.getName());
|
||||
Long offset = targetFileOffset(block, found).offset();
|
||||
match.add("file_offset", offset == null ? JsonNull.INSTANCE : GSON.toJsonTree(offset));
|
||||
Function function = currentProgram.getFunctionManager().getFunctionContaining(found);
|
||||
JsonObject procedure = new JsonObject();
|
||||
if (function != null) {
|
||||
procedure.addProperty("address", canonicalAddress(function.getEntryPoint()));
|
||||
procedure.addProperty("name", function.getName(true));
|
||||
}
|
||||
match.add("procedure", function == null ? JsonNull.INSTANCE : procedure);
|
||||
matches.add(match);
|
||||
at = found.next();
|
||||
}
|
||||
first = last + 1;
|
||||
JsonObject coverage = byteSearchCoverage(block, low, examinedEnd);
|
||||
covered = Math.addExact(covered, coverage.get("bytes").getAsLong());
|
||||
blocks.add(coverage);
|
||||
}
|
||||
JsonObject searched = new JsonObject();
|
||||
searched.add("blocks", blocks);
|
||||
@@ -1078,7 +1010,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
} catch (RequestFailure failure) {
|
||||
// Existing names/addresses win. Only an unresolved numeric input
|
||||
// can fall back to source coordinates; ambiguity never falls back.
|
||||
if (!failure.code.equals("procedure_not_found") || !selector.matches("0x[0-9a-fA-F]+"))
|
||||
if (!failure.code.equals("not_found") || !selector.matches("0x[0-9a-fA-F]+"))
|
||||
throw failure;
|
||||
}
|
||||
}
|
||||
@@ -1105,17 +1037,17 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
}
|
||||
List<Address> candidates = new ArrayList<>(mappedAddresses);
|
||||
if (candidates.size() > 1)
|
||||
throw new RequestFailure("file_offset_ambiguous", "File offset " + coordinate +
|
||||
throw new RequestFailure("ambiguous", "File offset " + coordinate +
|
||||
" has multiple loaded mappings: " + candidates.stream().map(ReaGhidraBridge::canonicalAddress).toList() +
|
||||
"; select an explicit analysis address");
|
||||
if (candidates.isEmpty())
|
||||
throw new RequestFailure("file_offset_unmapped", "File offset " + coordinate +
|
||||
throw new RequestFailure("not_found", "File offset " + coordinate +
|
||||
" has no loaded source mapping for the admitted target file; Ghidra image base: " + canonicalAddress(currentProgram.getImageBase()) +
|
||||
". Inspect mappings with rea inspect-native-load-image <path> --provider ghidra --json or MCP inspect_native_load_image.");
|
||||
Address mapped = candidates.get(0);
|
||||
Function function = functionAtOrContaining(mapped);
|
||||
if (function == null)
|
||||
throw new RequestFailure("procedure_not_found", "File offset " + coordinate +
|
||||
throw new RequestFailure("not_found", "File offset " + coordinate +
|
||||
" maps to analysis address " + canonicalAddress(mapped) +
|
||||
", but no procedure starts at or contains that address in the current Ghidra analysis. " +
|
||||
"Choose an observed procedure with CLI rea search <path> . --kind procedures --mode regex --provider ghidra --json or MCP list_procedures.");
|
||||
@@ -1318,12 +1250,6 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
if (component.getDataType() instanceof ghidra.program.model.data.BitFieldDataType bitfield) {
|
||||
field.addProperty("bit_size", bitfield.getBitSize());
|
||||
field.addProperty("bit_offset", bitfield.getBitOffset());
|
||||
JsonObject bitField = new JsonObject();
|
||||
bitField.addProperty("base_type_id", bitfield.getBaseDataType().getPathName());
|
||||
bitField.addProperty("declared_bit_size", bitfield.getDeclaredBitSize());
|
||||
bitField.addProperty("component_type_lookup", typeLookupResult(bitfield));
|
||||
bitField.addProperty("base_type_lookup", typeLookupResult(bitfield.getBaseDataType()));
|
||||
field.add("bit_field", bitField);
|
||||
}
|
||||
field.addProperty("status", "observed");
|
||||
field.addProperty("flexible_tail", "unknown");
|
||||
@@ -1354,7 +1280,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
result.add("members", members);
|
||||
result.addProperty("total_fields", totalFields);
|
||||
result.addProperty("truncated", totalFields > 20_000);
|
||||
result.add("limitations", GSON.toJsonTree(List.of("Layouts are Ghidra data-type database observations; original debug/source authority and flexible-tail semantics are not inferred. Component type paths do not necessarily identify separately registered types; bit-field lookups report whether the manager resolves equivalent types.", "At most 20000 fields or enum members are serialized per type to bound provider output. total_fields and truncated report omissions. Enum values preserve Ghidra's signed long representation.")));
|
||||
result.add("limitations", GSON.toJsonTree(List.of("Layouts are Ghidra data-type database observations; original debug/source authority and flexible-tail semantics are not inferred. Nested and recursive types retain category-path identities for separate inspection.", "At most 20000 fields or enum members are serialized per type to bound provider output. total_fields and truncated report omissions. Enum values preserve Ghidra's signed long representation.")));
|
||||
if (type != null) {
|
||||
JsonObject metadata = analysisExtensions.inspectDataType(currentProgram, type, address, monitor);
|
||||
if (metadata != null) result.add("metadata_recovery", metadata);
|
||||
@@ -1362,11 +1288,6 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
return result;
|
||||
}
|
||||
|
||||
private String typeLookupResult(DataType type) {
|
||||
DataType resolved = currentProgram.getDataTypeManager().getDataType(type.getPathName());
|
||||
return resolved == null ? "not-found" : resolved.isEquivalent(type) ? "equivalent" : "different";
|
||||
}
|
||||
|
||||
private JsonObject instructionReference(Reference reference) {
|
||||
JsonObject item = new JsonObject();
|
||||
JsonObject kind = referenceKind(reference);
|
||||
@@ -2537,7 +2458,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
if (matchProcedureNames(entry, selector).matched()) matches.add(entry.function);
|
||||
}
|
||||
if (matches.size() > 1)
|
||||
throw new RequestFailure("procedure_ambiguous", "Ghidra procedure name is ambiguous: " + value +
|
||||
throw new RequestFailure("ambiguous", "Ghidra procedure name is ambiguous: " + value +
|
||||
"; select an exact entry address from " + matches.stream()
|
||||
.map(function -> canonicalAddress(function.getEntryPoint())).toList());
|
||||
if (matches.size() == 1) return matches.get(0);
|
||||
@@ -2545,11 +2466,11 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
// Retain legacy bare-hex address input only when no symbol matches.
|
||||
Address address = explicitAddress ? parseReaAddress(value) : tryParseAddress(value);
|
||||
if (address == null)
|
||||
throw new RequestFailure("procedure_not_found", "Unknown Ghidra procedure name or address: " + value);
|
||||
throw new RequestFailure("not_found", "Unknown Ghidra procedure name or address: " + value);
|
||||
Function function = functionAtOrContaining(address);
|
||||
if (function == null)
|
||||
// Other procedure operations retain their analysis-address semantics.
|
||||
throw new RequestFailure("procedure_not_found", "No procedure exists at the requested address: " + value +
|
||||
throw new RequestFailure("not_found", "No procedure exists at the requested address: " + value +
|
||||
"; Ghidra image base: " + canonicalAddress(currentProgram.getImageBase()) +
|
||||
". Choose an observed analysis address. CLI: rea search <path> . --kind procedures --mode regex --provider ghidra --json; " +
|
||||
"MCP: list_procedures. Inspect the loaded base and source mappings with CLI rea analyze <path> --provider ghidra --json " +
|
||||
@@ -4995,7 +4916,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
// without retaining its cause. This producer reason is not bad syntax.
|
||||
if (exception.getDescription().equals("Stack overflow during pattern compilation"))
|
||||
throw regexStackExhausted(expression, "compiling");
|
||||
throw new RequestFailure("invalid_regex_pattern", "Invalid regex pattern " + expression + ": " + exception.getDescription() + " at index " + exception.getIndex());
|
||||
throw new RequestFailure("invalid_request", "Invalid regex pattern " + expression + ": " + exception.getDescription() + " at index " + exception.getIndex());
|
||||
}
|
||||
catch (StackOverflowError exhausted) {
|
||||
throw regexStackExhausted(expression, "compiling");
|
||||
|
||||
@@ -26,6 +26,7 @@ ARGUMENT_REGISTERS = {
|
||||
"x86_64h": ["rdi", "rsi", "rdx", "rcx", "r8", "r9"],
|
||||
}
|
||||
MAX_SELECTOR_BYTES = 1024
|
||||
MAX_REPORTED_LOCATIONS = 64
|
||||
MAX_OTHER_STOPS = 256
|
||||
MAX_TRACE_BYTES = 8 * 1024 * 1024
|
||||
MAX_TRACE_FRAMES = 65_536
|
||||
@@ -504,19 +505,20 @@ def _breakpoint_report(target, created, config):
|
||||
if not _wanted(config["breakpoints"][index], location["symbol"] or ""):
|
||||
continue
|
||||
count += 1
|
||||
remaining_bytes = (
|
||||
MAX_BREAKPOINT_LOCATION_BYTES - retained_location_bytes
|
||||
)
|
||||
if not _fits_trace_budget(location, remaining_bytes):
|
||||
locations_truncated = True
|
||||
continue
|
||||
location_bytes = len(json.dumps(location).encode("utf-8"))
|
||||
addition = location_bytes + (1 if locations else 0)
|
||||
if addition > remaining_bytes:
|
||||
locations_truncated = True
|
||||
continue
|
||||
locations.append(location)
|
||||
retained_location_bytes += addition
|
||||
if len(locations) < MAX_REPORTED_LOCATIONS:
|
||||
remaining_bytes = (
|
||||
MAX_BREAKPOINT_LOCATION_BYTES - retained_location_bytes
|
||||
)
|
||||
if not _fits_trace_budget(location, remaining_bytes):
|
||||
locations_truncated = True
|
||||
continue
|
||||
location_bytes = len(json.dumps(location).encode("utf-8"))
|
||||
addition = location_bytes + (1 if locations else 0)
|
||||
if addition > remaining_bytes:
|
||||
locations_truncated = True
|
||||
continue
|
||||
locations.append(location)
|
||||
retained_location_bytes += addition
|
||||
report.append({"index": index, "location_count": count, "locations": locations})
|
||||
return report, locations_truncated
|
||||
|
||||
|
||||
@@ -430,34 +430,6 @@ class NativeTraceBudgetTests(unittest.TestCase):
|
||||
self.assertEqual(report[0]["locations"], [])
|
||||
self.assertFalse(locations_truncated)
|
||||
|
||||
def test_breakpoint_report_keeps_all_locations_under_aggregate_budget(self):
|
||||
class Location:
|
||||
def __init__(self, index):
|
||||
self.index = index
|
||||
|
||||
def GetAddress(self):
|
||||
return self.index
|
||||
|
||||
class Breakpoint:
|
||||
def GetNumLocations(self):
|
||||
return 65
|
||||
|
||||
def GetLocationAtIndex(self, index):
|
||||
return Location(index)
|
||||
|
||||
with patch.object(
|
||||
tracer,
|
||||
"_location",
|
||||
side_effect=lambda address, _target: {"address": address, "symbol": None},
|
||||
), patch.object(tracer, "_wanted", return_value=True):
|
||||
report, locations_truncated = tracer._breakpoint_report(
|
||||
object(), [[Breakpoint()]], {"breakpoints": [{}]}
|
||||
)
|
||||
|
||||
self.assertEqual(report[0]["location_count"], 65)
|
||||
self.assertEqual(len(report[0]["locations"]), 65)
|
||||
self.assertFalse(locations_truncated)
|
||||
|
||||
def test_breakpoint_report_bounds_aggregate_location_json(self):
|
||||
tracer.lldb.LLDB_INVALID_ADDRESS = -1
|
||||
symbol_name = (
|
||||
|
||||
@@ -228,22 +228,6 @@ func processRunTokenObservation(
|
||||
parse: Result<ParsedRunToken, RunTokenReadError>,
|
||||
codeSigningFlags: () -> UInt32?
|
||||
) -> RunTokenObservation {
|
||||
// Native inspection restrictions apply even when argv is incomplete or
|
||||
// overwritten, before parsing reaches the environment. Keep this fact at
|
||||
// the native boundary; cleanup still decides whether the process is related.
|
||||
func withheldEnvironment() -> RunTokenObservation? {
|
||||
guard let flags = codeSigningFlags() else { return nil }
|
||||
if (flags & csPlatformBinary) != 0 {
|
||||
return unavailable(pid: pid, reason: "platform_binary_environment_withheld")
|
||||
}
|
||||
if (flags & csRestrict) != 0 {
|
||||
return unavailable(pid: pid, reason: "restricted_process_environment_withheld")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func unavailableEnvironment(_ reason: String) -> RunTokenObservation {
|
||||
withheldEnvironment() ?? unavailable(pid: pid, reason: reason)
|
||||
}
|
||||
switch parse {
|
||||
case .success(let parsed):
|
||||
if let runId = parsed.runId {
|
||||
@@ -254,25 +238,40 @@ func processRunTokenObservation(
|
||||
if parsed.environmentEntryCount > 0 {
|
||||
return RunTokenObservation(pid: pid, state: "readable", run_id: nil, reason: nil)
|
||||
}
|
||||
if let withheld = withheldEnvironment() { return withheld }
|
||||
let flags = codeSigningFlags()
|
||||
if let flags, (flags & csPlatformBinary) != 0 {
|
||||
return unavailable(pid: pid, reason: "platform_binary_environment_withheld")
|
||||
}
|
||||
if let flags, (flags & csRestrict) != 0 {
|
||||
return unavailable(pid: pid, reason: "restricted_process_environment_withheld")
|
||||
}
|
||||
return RunTokenObservation(pid: pid, state: "readable", run_id: nil, reason: nil)
|
||||
case .failure(let failure):
|
||||
switch failure {
|
||||
case .environmentUnavailable:
|
||||
return unavailableEnvironment("environment_unavailable")
|
||||
let flags = codeSigningFlags()
|
||||
if let flags, (flags & csPlatformBinary) != 0 {
|
||||
return unavailable(pid: pid, reason: "platform_binary_environment_withheld")
|
||||
}
|
||||
if let flags, (flags & csRestrict) != 0 {
|
||||
return unavailable(pid: pid, reason: "restricted_process_environment_withheld")
|
||||
}
|
||||
return unavailable(pid: pid, reason: "environment_unavailable")
|
||||
case .ambiguousEnvironmentBoundary:
|
||||
return unavailableEnvironment("ambiguous_environment_boundary")
|
||||
// These failures already establish that token bytes were available.
|
||||
if let flags = codeSigningFlags(), (flags & csRestrict) != 0 {
|
||||
return unavailable(pid: pid, reason: "restricted_process_environment_withheld")
|
||||
}
|
||||
return unavailable(pid: pid, reason: "ambiguous_environment_boundary")
|
||||
case .duplicateToken:
|
||||
return unavailable(pid: pid, reason: "duplicate_run_token")
|
||||
case .invalidTokenEncoding:
|
||||
return unavailable(pid: pid, reason: "invalid_run_token_encoding")
|
||||
case .appleVectorUnavailable:
|
||||
return unavailableEnvironment("apple_vector_unavailable")
|
||||
return unavailable(pid: pid, reason: "apple_vector_unavailable")
|
||||
case .invalidBuffer:
|
||||
return unavailableEnvironment("malformed_procargs")
|
||||
return unavailable(pid: pid, reason: "malformed_procargs")
|
||||
case .invalidBufferAt(let stage):
|
||||
return unavailableEnvironment("malformed_procargs_\(stage.rawValue)")
|
||||
return unavailable(pid: pid, reason: "malformed_procargs_\(stage.rawValue)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ export default defineConfig({
|
||||
{ text: "PE imports", link: "/pe-imports" },
|
||||
{ text: "PE TLS/load config", link: "/pe-load-config" },
|
||||
{ text: "ELF notes", link: "/elf-notes" },
|
||||
{ text: "Binary hardening", link: "/binary-hardening" },
|
||||
{ text: "Apple IPS crash reports", link: "/apple-ips" },
|
||||
{ text: "Linux kernel modules", link: "/kernel-modules" },
|
||||
{ text: "CPython pyc files", link: "/python-pyc" },
|
||||
@@ -62,30 +61,6 @@ export default defineConfig({
|
||||
{ text: "Electron observation", link: "/electron-observation" },
|
||||
{ text: "Electron fuses", link: "/electron-fuses" },
|
||||
{ text: "Process capture", link: "/process-capture" },
|
||||
{
|
||||
text: "Desktop application capture",
|
||||
link: "/desktop-application-capture",
|
||||
},
|
||||
{
|
||||
text: "Desktop application state",
|
||||
link: "/desktop-application-state",
|
||||
},
|
||||
{
|
||||
text: "Desktop application export",
|
||||
link: "/desktop-application-export",
|
||||
},
|
||||
{
|
||||
text: "Desktop application tasks",
|
||||
link: "/desktop-application-tasks",
|
||||
},
|
||||
{
|
||||
text: "Desktop build computer",
|
||||
link: "/desktop-build-computer",
|
||||
},
|
||||
{
|
||||
text: "Building and witnessing",
|
||||
link: "/desktop-build-witness",
|
||||
},
|
||||
{ text: ".NET assemblies", link: "/managed-code-analysis" },
|
||||
{
|
||||
text: "Python packaged applications",
|
||||
|
||||
@@ -25,21 +25,7 @@ REA never installs the Android SDK, an emulator, or a system image.
|
||||
Readiness is `rea inspect-adb-client`: it runs `adb version` and reports the
|
||||
resolved binary, its version, and where it was selected from. A missing
|
||||
binary reports `capability_unavailable` with the `REA_ADB_PATH` remediation
|
||||
rather than attempting an installation. A path that cannot resolve to a file
|
||||
(`ENOENT`, `ENOTDIR`, `ELOOP` or `ENAMETOOLONG`) is reported as missing. A
|
||||
selected path the host refuses to execute, such as a directory or a file
|
||||
without execute permission (`EACCES`) or one the host does not permit
|
||||
(`EPERM`), and any other spawn error report `capability_unavailable` naming
|
||||
that path. Exhausted host capacity (`EAGAIN`, `EMFILE`, `ENFILE` or `ENOMEM`)
|
||||
is not a configuration failure. `inspect_adb_client`, and any device
|
||||
operation whose adb cannot start at its version probe or at a later adb
|
||||
command, report a retryable `capacity` resource constraint rather than a
|
||||
capability refusal or an adb protocol failure; the `binary_session` inventory
|
||||
reports the provider as `runtime_unavailable`. Both say the host could not
|
||||
start adb and ask for a retry after freeing processes or descriptors, without
|
||||
blaming `REA_ADB_PATH`. When adb never started, the refusal diagnostics record
|
||||
the spawn error as `filesystem_error` with `path_source`, never as an
|
||||
`exit_code`.
|
||||
rather than attempting an installation.
|
||||
The `binary_session` inventory uses the same bounded client-version probe; it
|
||||
does not query devices, start the adb server, or establish that a requested
|
||||
serial is reachable. Device state is checked by each device operation.
|
||||
@@ -217,18 +203,12 @@ and during shutdown.
|
||||
`uninstall_adb_package` declare `mutatesTarget` and never run implicitly:
|
||||
pushes check for existing device files before transfer and refuse them
|
||||
unless `overwrite` is set. A failed or unexpected existence probe stops
|
||||
transfer even with `overwrite`. A completed `test -e` exit of 1 establishes
|
||||
absence only with empty stdout and either empty stderr or a recognized,
|
||||
completed adb-server startup notice sequence. Startup notices are host
|
||||
observations, not remote-path diagnostics; successful push receipts retain
|
||||
them in `raw_result.device_path_probes`. Incomplete startup, extra diagnostics,
|
||||
truncation and cancellation do not establish absence. The check is not atomic, so a different
|
||||
transfer even with `overwrite`; only a clean `test -e` exit of 1 with no
|
||||
output is interpreted as absence. The check is not atomic, so a different
|
||||
process can create the path before `adb push` writes it. When available, the device's
|
||||
`sha256sum` verifies the transfer. A missing command, output without a
|
||||
recognizable digest, or host capacity that prevents the digest check from
|
||||
starting reports `device_digest_source: "unavailable"` with an
|
||||
unverified-transfer limitation rather than a retryable error; installs
|
||||
preserve the
|
||||
`sha256sum` verifies the transfer. A missing command or output without a
|
||||
recognizable digest reports `device_digest_source: "unavailable"` with an
|
||||
unverified-transfer limitation; installs preserve the
|
||||
device's `Failure [...]` reason verbatim.
|
||||
- **Producer identity stays attached.** Every result embeds the producing
|
||||
adb client (`binary_path`, `path_source`, `version`, `installed_path`).
|
||||
|
||||
@@ -19,7 +19,7 @@ records the app you provide; you start it and prepare its test data.
|
||||
To run the exported environment, install the
|
||||
[AgentEnv framework](https://github.com/scaleapi/agentenv-framework#install)
|
||||
and start Docker. REA's [compatibility check](testing.md#exported-environment-bundles)
|
||||
uses AgentEnv version `0.9.1431` with Python 3.12.
|
||||
uses AgentEnv version `0.9.1334` with Python 3.12.
|
||||
|
||||
There are two ways to build an environment: ask your agent to record the app,
|
||||
or start from a recording you already have.
|
||||
|
||||
@@ -1049,9 +1049,8 @@ unknown recorded status stays unknown; it is not borrowed from another
|
||||
record). A bundle with no such tool yields a verifier that checks the count
|
||||
only and says so. `tasks/smoke.json` deploys the env on the local
|
||||
sandbox and runs that verifier against the env's MCP endpoint; the first run
|
||||
also pulls the framework's published infrastructure images from `ghcr.io`,
|
||||
building one only if its pull fails, and the run tears everything down when it
|
||||
ends.
|
||||
also builds the framework's own infrastructure, and the run tears everything
|
||||
down when it ends.
|
||||
|
||||
The README written into the bundle records the capture artifact's SHA-256, the
|
||||
capture and manifest Evidence ids, the manifest view digest, the served tool
|
||||
|
||||
@@ -73,9 +73,6 @@ spellings; the original SDK declaration strings remain unchanged.
|
||||
it remains null for unknown provider target SDKs, nonliteral declarations, or
|
||||
components with filters targeting API 31 or later that omit the now-required
|
||||
explicit declaration. A limitation records the latter validation error.
|
||||
As in Android's package parser, an activity, alias or receiver filter without
|
||||
an `<action>` is discarded and does not count toward either rule; a service
|
||||
keeps such a filter. `intent_filter_count` still counts every declared filter.
|
||||
Provider authorities and all inventory dimensions are bounded. `status:
|
||||
"partial"` and `limitations` identify omitted declarations or values; a
|
||||
capacity limitation appears only when an actual collection limit was reached.
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
# Offline API contract inventory
|
||||
|
||||
`inspect_api_contract` / `rea inspect-api-contract` supports local OpenAPI JSON
|
||||
and GraphQL SDL (`format: "graphql-sdl"`). Neither profile contacts endpoints.
|
||||
|
||||
## OpenAPI JSON
|
||||
|
||||
The default profile reads one local OpenAPI
|
||||
3.0, 3.1 or 3.2 JSON document. It inventories inline operation slots under
|
||||
`paths`; the facts describe declarations, not implemented, reachable or
|
||||
authorized runtime behavior. YAML, AsyncAPI, callbacks/webhooks, schema graphs,
|
||||
contract diffs and traffic correlation are outside this version.
|
||||
|
||||
The OpenAPI importer never resolves references, opens sibling documents, evaluates
|
||||
examples or contacts declared servers. OpenAPI 3.2 `query` and
|
||||
`additionalOperations` follow the [Path Item contract](https://spec.openapis.org/oas/v3.2.1.html#path-item-object),
|
||||
including case-sensitive additional HTTP method spelling.
|
||||
|
||||
## Select a useful answer
|
||||
|
||||
MCP defaults to a summary retaining the complete inventory. An optional view
|
||||
returns selected facts immediately, taking precedence over `detail`:
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "/artifacts/openapi.json",
|
||||
"view": {
|
||||
"kind": "api-contract",
|
||||
"collection": "operations",
|
||||
"methods": ["GET"],
|
||||
"limit": 20
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`operations` supports exact `operation_ids`, `operation_keys`, `methods`,
|
||||
`paths`, `declared_ids` and `states` filters. Filters combine by intersection;
|
||||
empty arrays select nothing. `fields` chooses columns; identity, source pointer,
|
||||
state and unknown reason remain. The other collections are
|
||||
`path-item-references` (optional `reference_ids`) and `diagnostics` (optional
|
||||
`codes`), also with fields and offset/limit. The default page contains 64 rows.
|
||||
|
||||
Query a retained result with `inspect_analysis_view`, using the same view and
|
||||
`source: {"kind":"retained-evidence","evidence_id":"..."}`. Copy the complete
|
||||
parent ID from `normalized_result.parent_evidence_id`, not the summary's own ID.
|
||||
These pages use immutable retained facts without rereading the specification.
|
||||
Complete Evidence remains available through `get_evidence_bundle`.
|
||||
|
||||
CLI defaults to complete Evidence. For a selected answer, save that complete
|
||||
Evidence in an absent bundle path:
|
||||
|
||||
```sh
|
||||
rea inspect-api-contract ./openapi.json --detail summary \
|
||||
--view '{"kind":"api-contract","collection":"operations","methods":["GET"],"limit":20}' \
|
||||
--evidence_output ./contract.evidence.json --json
|
||||
```
|
||||
|
||||
Later `rea inspect-analysis-view` can read that bundle through an exact
|
||||
`evidence-file` source selection, even after the original specification is
|
||||
unavailable. Publication never overwrites an existing file. CLI preserves its
|
||||
original path spelling in `caller_input_path`; analysis locations and MCP inputs
|
||||
use absolute filesystem paths.
|
||||
|
||||
## Identity, provenance and coverage
|
||||
|
||||
Each operation has an artifact-scoped `operation_id`, an exact method/path
|
||||
`operation_key`, an optional `declared_id` from `operationId`, and its original
|
||||
JSON Pointer. Keys can correlate exact declarations in a caller-selected pair
|
||||
of revisions; they do not establish equivalence across unrelated APIs, renamed
|
||||
path templates or runtime handlers. Declared IDs are not assumed unique.
|
||||
The artifact includes the verified byte length, SHA-256 and selected path.
|
||||
|
||||
Malformed siblings retain useful declarations plus diagnostics. A Path Item
|
||||
`$ref`, including a local fragment, remains unresolved; adjacent inline slots
|
||||
are unknown because referenced fields may overlap. Missing or malformed paths
|
||||
are explicit, and missing required OpenAPI 3.0 paths does not become a complete
|
||||
empty inventory. `source_coverage` is limited to inline paths and records that
|
||||
reference resolution and full schema validation were not performed. Page
|
||||
exhaustion describes the selected rows and never upgrades source coverage.
|
||||
|
||||
Structured transport userinfo credentials are excluded from reference URIs;
|
||||
`credential_redacted` reports that change, and an ambiguous credential span leaves
|
||||
the displayed reference unknown. Ordinary reference query/fragment text and
|
||||
names are retained. Original bytes remain in the caller's artifact; complete
|
||||
inventory Evidence does not embed the entire source JSON. Duplicate JSON members
|
||||
use the shared parser's last-value-wins behavior, recorded in source coverage.
|
||||
|
||||
If decoding or later validation fails after a verified read, the typed error
|
||||
retains a compact `file-snapshot-hash` observation with the exact path, digest
|
||||
and byte length. Syntax diagnostics do not quote source JSON. Cancellation
|
||||
does not replace a parser failure that has already completed.
|
||||
|
||||
## Resource bounds and verification
|
||||
|
||||
The file-byte allowance and conservative expanded-facts allowance are each
|
||||
32 MiB resource policies, not OpenAPI format limits. `max_file_bytes` can lower
|
||||
the input allowance. Native JSON parsing additionally checks available heap
|
||||
against the shared representation-expansion estimate before decoding. The
|
||||
shared JSON nesting limit is 1,000, including uninterpreted schema regions.
|
||||
Capacity refusals are typed; reply paging does not increase parser allowances.
|
||||
|
||||
The committed [Petstore fixture](https://github.com/morluto/rea/blob/b7c8caedd4166e6032c49024cdc6fb1154f1a2df/tests/fixtures/openapi/README.md) is an
|
||||
original document from the published application, replayed offline through
|
||||
public CLI/MCP tests. These check advertised schemas, complete/selected parity,
|
||||
retained paging, partial references, zero requests to a loopback reference
|
||||
server and unchanged source bytes. They do not establish runtime endpoint
|
||||
behavior or full OpenAPI conformance.
|
||||
|
||||
## GraphQL SDL and operation documents
|
||||
|
||||
Select `format: "graphql-sdl"` for SDL. Optionally supply `operations_path` to
|
||||
one local document containing operations and fragments. Both MCP paths are
|
||||
absolute; CLI accepts relative spellings:
|
||||
|
||||
```sh
|
||||
rea inspect-api-contract ./schema.graphql --contractFormat graphql-sdl \
|
||||
--operationsPath ./queries.graphql --detail summary \
|
||||
--view '{"kind":"graphql-contract","collection":"references","limit":20}' \
|
||||
--evidence_output ./graphql.evidence.json --json
|
||||
```
|
||||
|
||||
Collections are `types`, `fields`, `definitions`, `references`, `fragment-spreads`
|
||||
and `diagnostics`. Each supports `fields`, `offset` and `limit` (default 64).
|
||||
Exact batch filters include type/field/definition/reference IDs, type names,
|
||||
`field_keys` such as `Query.viewer`, owning type names, binding/spread states
|
||||
and diagnostic codes. Filters intersect; an empty array selects nothing.
|
||||
Identity, source and applicable uncertainty fields always remain. Retained pages
|
||||
and bundle-based queries use the same `graphql-contract` views without rereading
|
||||
SDL or rebuilding its schema model.
|
||||
|
||||
Types and fields retain individual declaration occurrences, including extensions,
|
||||
list/non-null wrappers, implemented interfaces and union member names. Operation
|
||||
and fragment definitions own field references with aliases and parent-reference
|
||||
IDs. Each reference links to a supplied SDL field occurrence, remains `model-only`
|
||||
for a library-provided field such as `__typename`, or records an exact unknown
|
||||
reason. Rejected schema construction keeps declarations but cannot supply trusted
|
||||
bindings. Missing or duplicate fragment names remain explicit. Fragments are
|
||||
visited once in their declared type scope and linked by spread edges; cycles
|
||||
are never recursively expanded.
|
||||
|
||||
The versioned `graphql-sdl-field-bindings-v1` profile records the GraphQL library
|
||||
version. It performs syntax parsing and schema construction for field resolution;
|
||||
it does not establish whole-schema/operation validity, spread applicability,
|
||||
reachability, authorization or endpoint behavior. Arguments, defaults,
|
||||
descriptions, directive behavior, enum value inventory, introspection JSON,
|
||||
federation composition and version comparison remain outside this profile.
|
||||
|
||||
Every fact has a document role, SHA-256, UTF-16 start/end offsets and parser token
|
||||
line/column. Offsets address decoded UTF-8 text, including an optional BOM, rather
|
||||
than byte positions. The two files are independently verified snapshots, not an
|
||||
atomic pair. Fact IDs identify source occurrences; `field_key` separately supports
|
||||
exact name-based joins and does not assert equivalence across revisions. CLI
|
||||
retains its auxiliary path spelling in `caller_input_paths.operations_path`,
|
||||
outside semantic identity, as well as its primary `caller_input_path`.
|
||||
|
||||
Malformed operation text retains the SDL inventory with partial coverage and a
|
||||
source-located diagnostic. A primary parser refusal or later resource/cancellation
|
||||
failure retains known source identities and available interpreted Evidence;
|
||||
syntax diagnostics do not quote source text. Complete Evidence contains facts,
|
||||
not original document bytes. An exhausted page never upgrades source coverage.
|
||||
|
||||
Each file has the same configurable maximum of 32 MiB. Conservatively charged
|
||||
facts share a 32 MiB allowance; text/AST allocation is admitted against available
|
||||
heap. The parser admits at most 262,144 lexical tokens and 65,536 comments per
|
||||
document; subsequent iterative AST admission limits traversal depth to 128.
|
||||
These are REA resource policies, not GraphQL language limits. Very deep input can
|
||||
be refused by the parser before AST admission. Selected replies reduce transport
|
||||
and retained queries reuse immutable facts; they do not avoid the initial parse.
|
||||
|
||||
The committed SWAPI SDL is an unchanged public application fixture with provenance
|
||||
in `tests/fixtures/graphql/README.md`. It supplies 53 types and 243 fields;
|
||||
verification does not execute its API.
|
||||
|
||||
Large complete inventories can exceed the default MCP response budget even when
|
||||
selected pages fit. Use `export_evidence_bundle` with a caller-selected local
|
||||
path for complete retained Evidence, or CLI complete JSON/bundle publication.
|
||||
A transport refusal does not erase a completed retained analysis. Narrow pages
|
||||
and source coverage remain available on the same continuing connection.
|
||||
@@ -1,62 +0,0 @@
|
||||
# Binary hardening
|
||||
|
||||
Use `inspect_binary_hardening` to read compile-time and link-time hardening from
|
||||
an explicit local ELF, PE, or Mach-O file. No disassembler, pwntools install, or
|
||||
target execution is required. Universal Mach-O files return one image per slice.
|
||||
|
||||
```sh
|
||||
rea inspect-binary-hardening /absolute/path/to/program --json
|
||||
rea inspect-binary-hardening /absolute/path/to/program --max-file-bytes 1048576 --json
|
||||
```
|
||||
|
||||
MCP accepts `{ "path": "/absolute/path/to/program" }`. CLI paths may be relative
|
||||
to the current directory; MCP paths must be absolute.
|
||||
|
||||
Each image has one row per property: `property`, `state`, `confidence`, `basis`,
|
||||
`location`, `note`, and `next_probe`. States are `enabled`, `disabled`,
|
||||
`partial`, `unknown`, and `not_applicable`. `confidence` is `observed` for a
|
||||
single header or import, and `derived` when the state combines several fields,
|
||||
such as ELF PIE or full RELRO. An image's `architecture` is its CPU family, or
|
||||
null when REA has no family for it. `machine` and `slice.architecture` keep the
|
||||
exact identity, such as `arm64e`.
|
||||
|
||||
ELF rows cover PIE (`ET_DYN` with `PT_INTERP` or `DF_1_PIE`), NX (`PT_GNU_STACK`;
|
||||
a missing header is `unknown` and the architecture default is only noted), RELRO
|
||||
(`PT_GNU_RELRO` plus `BIND_NOW`), stack protector and FORTIFY undefined dynamic
|
||||
imports, CET and AArch64 BTI/PAC GNU property notes, `DT_RPATH`, `DT_RUNPATH`, and
|
||||
whether a `SHT_SYMTAB` section is present. Only an undefined dynamic import
|
||||
counts. Imports come from the table `DT_SYMTAB` declares. A `SHT_DYNSYM` section
|
||||
only supplies the symbol count when `DT_HASH` and `DT_GNU_HASH` leave it open,
|
||||
and only if it names the same symbol and string tables. Without that count the
|
||||
import rows are `unknown` and give the reason. A missing `__stack_chk_fail` or fortify import is `unknown`. That
|
||||
absence does not establish that the protection was left out of a static or
|
||||
stripped build. A present import is a sign that some code uses the protection,
|
||||
not proof that all code does. NX, RELRO, `DT_RPATH`, and `DT_RUNPATH` are
|
||||
`not_applicable` when `e_type` is neither `ET_EXEC` nor `ET_DYN`, and `unknown`
|
||||
for a linked image with no readable program headers.
|
||||
|
||||
PE rows read `DllCharacteristics` for dynamic base, high-entropy VA, NX
|
||||
compatibility, force integrity, no SEH, and guard CF. `cfg_instrumented` is the
|
||||
load-config `GuardFlags` linker claim when `Size` covers that field. An
|
||||
unreadable load configuration, including a TLS failure in that parser, leaves
|
||||
only `cfg_instrumented` `unknown`. Authenticode and debug rows report certificate
|
||||
and debug directory presence, not signature validity or debug content. A
|
||||
directory whose address and size disagree is `unknown`.
|
||||
|
||||
Mach-O rows read `MH_PIE`, `MH_ALLOW_STACK_EXECUTION`, `MH_NO_HEAP_EXECUTION`,
|
||||
an `__RESTRICT` segment, `LC_CODE_SIGNATURE` presence, encryption from the
|
||||
existing encryption reader, the arm64e subtype, and a stack-protector import.
|
||||
`hardened_runtime` is `unknown` with `next_probe` `inspect_signature`.
|
||||
CodeDirectory flags stay with that tool.
|
||||
|
||||
The report states presence of build-time features. It does not state
|
||||
exploitability. `inspect_binary_layout` is unchanged.
|
||||
|
||||
The default read budget is 64 MiB (`max_file_bytes`, ceiling 512 MiB). A changed
|
||||
file is refused. A malformed header, a Mach-O segment or encryption command that fails admission, or a program, section,
|
||||
or segment payload declared outside the file returns no partial report, except that one bad slice in a universal Mach-O leaves the other slices. A symbol table or
|
||||
symbol name over its budget leaves the import rows `unknown` and keeps the
|
||||
header rows. A GNU property note over its budget, or a compressed note section
|
||||
that may hold one, leaves the CET and BTI/PAC rows `unknown` and keeps the header
|
||||
rows. A dynamic-table budget still fails the ELF report. The image is never
|
||||
executed.
|
||||
@@ -9,10 +9,7 @@ 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. Launched Chromium
|
||||
keeps its sandbox; on hosts that cannot provide one, such as root in a
|
||||
container, set `REA_BROWSER_NO_SANDBOX=true` for the REA process. Termux on
|
||||
Android always launches without it. Navigations follow
|
||||
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,
|
||||
and [request steps](#request-steps) send only to the start URL's origin;
|
||||
there is no separate origin allowlist. Request steps use a temporary dispatch
|
||||
|
||||
+10
-88
@@ -39,17 +39,6 @@ pass an absolute path. REA reads and authenticates the file, then projects it
|
||||
like retained Evidence, so a large file is bounded by the file reader rather
|
||||
than charged as inline input.
|
||||
|
||||
An argument that accepts inline JSON or a JSON file path also accepts `-`, which
|
||||
reads the request from standard input, so a generated request needs no
|
||||
temporary file:
|
||||
|
||||
```bash
|
||||
jq -nc '{source: {kind: "evidence-file", path: "app-evidence.json"}, view: {kind: "summary"}}' | rea inspect-analysis-view -
|
||||
```
|
||||
|
||||
Standard input is read once as a single UTF-8 JSON document without a
|
||||
byte-order mark, and it is limited to 8 MiB. Put a larger request in a file.
|
||||
|
||||
Local file inspection commands reject empty paths and NUL bytes before resolving
|
||||
relative inputs, auxiliary files or output destinations. An empty string never
|
||||
selects the working directory; use an explicit `.` when selecting that directory
|
||||
@@ -281,17 +270,6 @@ Format versions 6 through 8 retain header, block and directory facts with
|
||||
none/LZ4/LZ4HC block decompression. See [UnityFS](unityfs.md) for encryption
|
||||
and LZMA coverage boundaries.
|
||||
|
||||
Inspect a WeChat mini-program package without WeChat or an unpacker:
|
||||
|
||||
```sh
|
||||
rea inspect-artifact app.wxapkg --detail summary --evidence_output evidence.json --json
|
||||
rea extract-artifact app.wxapkg --json
|
||||
```
|
||||
|
||||
Plaintext packages retain header, table and member facts with verbatim member
|
||||
bytes. `V1MMWX` packages are detected as encrypted coverage and refused on
|
||||
extraction. See [wxapkg](wxapkg.md) for layout and coverage boundaries.
|
||||
|
||||
Inspect an ESP32-family app or bootloader image without esptool:
|
||||
|
||||
```sh
|
||||
@@ -349,10 +327,7 @@ unknown names, resource limits and retained views. Compressed bytes stay stored.
|
||||
Zstandard (standard method 93). They retain the shared ZIP CRC, declared-size,
|
||||
overlap and archive expansion checks. The native streaming decoder keeps the
|
||||
existing 128 MiB Zstandard window cap; frames requiring a larger window are
|
||||
reported as a resource limit (`reason: limit`, `boundary: memory`), rather than
|
||||
as archive corruption. The cap cannot be raised at runtime: recompress with a
|
||||
window of at most 128 MiB, or decompress externally before retrying.
|
||||
Deprecated method 20 and other unregistered ZIP codecs remain unsupported.
|
||||
refused. Deprecated method 20 and other unregistered ZIP codecs remain unsupported.
|
||||
|
||||
Inventory and extract UF2 or DfuSe regions:
|
||||
|
||||
@@ -476,8 +451,7 @@ applicable. gzip, xz, and zstd containers are unsupported and name
|
||||
utility, then inspect the decompressed file. See
|
||||
[Linux kernel modules](kernel-modules.md).
|
||||
|
||||
Inspect a CPython 3.7–3.13 `.pyc` header and nested marshal code objects (3.14 and
|
||||
3.15 headers are qualified but their marshal layout is not pinned)
|
||||
Inspect a CPython 3.7–3.13 `.pyc` header and nested marshal code objects
|
||||
without executing bytecode or installing Python:
|
||||
|
||||
```bash
|
||||
@@ -498,15 +472,6 @@ The workflow reports IFF chunk names, atom tables, exports, imports, lambdas,
|
||||
and the Code chunk header. Missing optional tables stay null. See
|
||||
[Erlang BEAM files](erlang-beam.md).
|
||||
|
||||
Complete `rea decompile --json` Evidence can be saved and inspected through
|
||||
`inspect-analysis-view` with an inline source and
|
||||
`view: {kind: "native", facet: "pseudocode", offset: 0, limit: 20000}`.
|
||||
The projection reads that saved observation without starting a provider;
|
||||
offsets/limits count UTF-16 code units. A summary view preserves the original
|
||||
selector and distinguishes null from empty text. The MCP tool additionally
|
||||
supports `detail: "summary"` on its first call to retain the complete source
|
||||
without returning the body inline; see [native investigation](native-investigation.md).
|
||||
|
||||
Configure [Hopper or Ghidra](installation.md#hopper), the
|
||||
[IDA adapter](ida-provider.md), or [Kuna](kuna-provider.md) before analyzing a
|
||||
native target. Substitute
|
||||
@@ -647,9 +612,6 @@ Saved snapshots include only cache entries whose subject identifies the selected
|
||||
target. Unbound observations and observations of child artifacts remain available
|
||||
in the open session and its Evidence bundle; they are excluded from the target's
|
||||
saved replay entries without changing their subjects.
|
||||
The snapshot also retains available Evidence linked by selected records, including
|
||||
transitive sources with an unbound or different subject. Those sources are not
|
||||
replay entries for the selected target. Unrelated records remain excluded.
|
||||
|
||||
Snapshots retain eligible target-scoped question histories in full, including
|
||||
mutation Evidence recorded while another target was active. Observations and
|
||||
@@ -664,11 +626,6 @@ rea analyze /absolute/path/to/program --provider ghidra --snapshot /absolute/pat
|
||||
```
|
||||
|
||||
An exact CLI cache hit is normally read before starting a provider process.
|
||||
`decompile --provider ghidra --snapshot ...` also retains its single selected
|
||||
Program's pseudocode result, even without an explicit document selector. Old
|
||||
snapshots that contain only the Evidence need one new query to add that replay
|
||||
entry. This does not retain every body from `decompile-all` or make a persistent
|
||||
session's implicit document replayable.
|
||||
On Windows, Ghidra first admits the selected source through its native snapshot
|
||||
owner, so replay can start that provider.
|
||||
In MCP, `open_binary` accepts `snapshot_path` to import a snapshot atomically
|
||||
@@ -721,10 +678,6 @@ results and Evidence. These are cached observations, not a saved provider
|
||||
database: only eligible exact queries can reuse a result, and new or live
|
||||
queries can still require provider startup. The CLI uses the same snapshot
|
||||
format and preserves these records when loading and updating it.
|
||||
When an MCP session replays a provider observation, its delivered Evidence identifies
|
||||
the original observation through `evidence_links` and reports that the
|
||||
provider was not rerun. The original and its available source records remain
|
||||
in the investigation bundle.
|
||||
|
||||
## Import, export and compare Evidence
|
||||
|
||||
@@ -747,12 +700,10 @@ not read artifact contents: the inventories of two builds are unresolved
|
||||
records, or one opaque change when you pair them. Use `compare-artifacts`,
|
||||
described at the end of this section, to compare their contents.
|
||||
|
||||
`--record_pairs` accepts an inline JSON array or the path to a local JSON
|
||||
file containing that array, matching MCP `compare_bundles.record_pairs`. For a
|
||||
file, use `rea compare left.json right.json --record_pairs pairs.json --json`.
|
||||
Pair IDs must exist on their named sides and be one-to-one. Without pairs,
|
||||
one-sided membership remains unknown; REA does not infer correspondence from
|
||||
position or display names.
|
||||
`--record_pairs` supplies an explicit JSON array of observation pairs, matching
|
||||
MCP `compare_bundles.record_pairs`. Pair IDs must exist on their named sides and
|
||||
be one-to-one. Without pairs, one-sided membership remains unknown; REA does
|
||||
not infer correspondence from position or display names.
|
||||
|
||||
Exports preserve an existing destination unless `--overwrite` is explicit.
|
||||
|
||||
@@ -799,8 +750,7 @@ from the bundle for every supported view kind. Bundles exported from MCP work
|
||||
for these commands. Its inline source form cannot also select a bundle.
|
||||
A missing ID is refused with `details.reason: "missing"`;
|
||||
supplying a bundle does not make incompatible Evidence valid for a workflow.
|
||||
`compare-managed-members` keeps its two assembly paths and an optional
|
||||
repeatable `--type` selection applied to both sides, and
|
||||
`compare-managed-members` keeps its two assembly paths, and
|
||||
`compare-process-captures` keeps its two capture Evidence JSON paths. The bundle
|
||||
option above selects an input; producer options that write a new bundle, such
|
||||
as `inspect-managed-members --evidence-bundle`, retain their existing meaning.
|
||||
@@ -838,7 +788,6 @@ Import an older source tree to compare with the current artifact:
|
||||
```bash
|
||||
rea import-reference-source /absolute/path/to/source
|
||||
rea import-reference-source /absolute/path/to/source --detail summary --outline-depth 2 --json
|
||||
rea import-reference-source /absolute/path/to/source --exclude-paths '["vendor/","*.generated","!keep.generated"]'
|
||||
```
|
||||
|
||||
The default (`--detail full`) returns the complete `HistoricalSourceGraph`.
|
||||
@@ -985,10 +934,6 @@ Choose the guide for the target and intended interaction:
|
||||
- [Electron fuses](electron-fuses.md): read packaged fuse bytes before expecting `--inspect` to reach an Electron main process.
|
||||
- [Node/Electron Inspector](javascript-runtime-observation.md): record script locations and execution contexts.
|
||||
- [Process capture](process-capture.md): run an executable and compare terminal, exit and filesystem observations.
|
||||
- [Desktop application capture](desktop-application-capture.md): launch a Linux desktop application and record its windows and home-directory state at checkpoints while it is used.
|
||||
- [Desktop application state](desktop-application-state.md): read a capture folder into the application's state files, typed views of each version and kinds of change.
|
||||
- [Desktop application export](desktop-application-export.md): write a capture folder as an agentenv computer app bundle with its class, skill and seed.
|
||||
- [Desktop application tasks](desktop-application-tasks.md): describe how a capture's files changed, then validate an agent-written task against the capture and export it with its verifier.
|
||||
|
||||
Runtime requests name the target and actions. Launched targets run with your
|
||||
user permissions; consult the chosen guide for host requirements and effects.
|
||||
@@ -1200,10 +1145,6 @@ such as `UNSUPPORTED_OUTPUT_COMBINATION` for `--json` with `--token-limit`. Use
|
||||
Commands reject surplus positional arguments before execution, including commands
|
||||
that accept no positionals. Use `rea <command> --help` for the declared argument
|
||||
shape; a final variadic argument continues to accept multiple values.
|
||||
`rea mcp add` accepts options only and rejects unknown options before reading or
|
||||
backing up client settings or starting registration. Its `--agent` and
|
||||
`--command`/`-c` options accept spaced and equals forms. Values must be nonempty; a following flag does not satisfy a spaced option's
|
||||
missing value.
|
||||
|
||||
`rea mcp doctor --json` checks production MCP startup and reports child logs in
|
||||
`diagnostics`. It drains stderr while retaining at most its first 64 KiB of raw
|
||||
@@ -1244,12 +1185,9 @@ treated as malformed.
|
||||
exact array path with `--filter-output` and provide comma-separated direct
|
||||
scalar columns with `--columns`; column order is the output column order:
|
||||
|
||||
For an artifact inventory request that selects the `occurrences` collection
|
||||
and includes `logical_path` and `artifact_id` in its view fields:
|
||||
|
||||
```bash
|
||||
rea inspect-analysis-view request.json --format csv \
|
||||
--filter-output normalized_result.items --columns logical_path,artifact_id
|
||||
--filter-output normalized_result.rows --columns name,address
|
||||
```
|
||||
|
||||
The first line is a REA metadata record,
|
||||
@@ -1344,9 +1282,7 @@ the command's signal handling. Commands routed through the shared
|
||||
`withCommandCancellation` handler in the CLI set status `130` for SIGINT or
|
||||
`143` for SIGTERM after their operation settles. Current source registrations
|
||||
use that handler for process capture and comparison (`capture-process`,
|
||||
`compare-process-captures`), desktop application capture and state
|
||||
(`capture-desktop-application`, `inspect-desktop-application-state`,
|
||||
`export-desktop-application`, `describe-desktop-application-task`), JavaScript application analysis (`analyze` when it
|
||||
`compare-process-captures`), JavaScript application analysis (`analyze` when it
|
||||
selects the JavaScript lane, and `analyze-javascript-application`), selected
|
||||
browser, web and Electron commands (including `analyze-web-bundle`,
|
||||
`observe-web-execution`, `capture-browser-scenario` and
|
||||
@@ -1392,11 +1328,7 @@ status with the analysis failure status, so it exits `1`. Setup has its own cont
|
||||
structured result and process status as separate signals when scripting
|
||||
cancellation.
|
||||
|
||||
`setup --dry-run` returns `planned` and exits `0`. For `setup` and
|
||||
`environment-build`, explicit `--dry-run=true` and `--dry-run=false` are
|
||||
accepted; empty or other values fail with `PARSE_ERROR` before any changes.
|
||||
Every supplied value is checked even when a later occurrence overrides it.
|
||||
A cancelled setup also exits
|
||||
`setup --dry-run` returns `planned` and exits `0`; a cancelled setup also exits
|
||||
`0`. Setup returns `1` for `needs_confirmation` or `needs_human`.
|
||||
`doctor` returns `1` when required checks in its selected readiness scope fail;
|
||||
unavailable optional providers remain informational for unrelated tasks.
|
||||
@@ -1441,16 +1373,6 @@ Reports TLS callbacks and size-covered load-config fields without execution.
|
||||
Use `--max-callbacks` and `--max-file-bytes` to set read budgets. See
|
||||
[PE TLS and load configuration](pe-load-config.md).
|
||||
|
||||
### Binary hardening
|
||||
|
||||
```sh
|
||||
rea inspect-binary-hardening /absolute/path/to/program --json
|
||||
```
|
||||
|
||||
Reports ELF, PE, and Mach-O hardening rows without execution. A missing import
|
||||
is `unknown`, not `disabled`. Use `--max-file-bytes` to set the read budget. See
|
||||
[binary hardening](binary-hardening.md).
|
||||
|
||||
Bun standalone executables use the existing `inspect-artifact` and
|
||||
`extract-artifact` commands to inventory and materialize embedded source,
|
||||
sourcemaps and bytecode without running the executable. See
|
||||
|
||||
+19
-247
@@ -24,8 +24,8 @@ the registration uses them. Clearing either makes startup fail without resolving
|
||||
another package from the client's project. Restore the same installation before
|
||||
repairing it; an unidentified registration remains untouched. See
|
||||
[installation and recovery](installation.md#package-runners-and-global-installation).
|
||||
Setup forwards supported, explicitly selected REA settings into client
|
||||
configuration, but does not copy unrelated ambient environment values. To refresh an older registration, preview
|
||||
Setup does not copy your ambient environment into client
|
||||
settings. To refresh an older registration, preview
|
||||
`rea setup --client <client-id> --skill=false --dry-run`, then run the same command
|
||||
without `--dry-run` and approve the displayed changes. Setup preserves unrelated
|
||||
settings, backs up changed configuration and leaves an aligned registration
|
||||
@@ -49,31 +49,6 @@ launcher; see [verification scope](client-verification.md#launch-environment-ver
|
||||
the registration's file does not carry as a
|
||||
[non-blocking unknown](installation.md#check-readiness-for-your-task).
|
||||
|
||||
## Setting launch variables in PowerShell
|
||||
|
||||
Shell examples that prefix a command with `REA_MCP_INPUT_SCHEMA_PROFILE=compact`
|
||||
use POSIX shell syntax. In PowerShell, set the process environment before
|
||||
launching REA:
|
||||
|
||||
```powershell
|
||||
$env:REA_MCP_INPUT_SCHEMA_PROFILE = "compact"
|
||||
rea mcp doctor --json
|
||||
```
|
||||
|
||||
The assignment applies to the current PowerShell process and commands it launches.
|
||||
Restore the previous value when finished, or use
|
||||
`Remove-Item Env:REA_MCP_INPUT_SCHEMA_PROFILE` if it was previously unset. It does
|
||||
not update an already running MCP server or desktop client.
|
||||
|
||||
To select this profile for an agent, set `REA_MCP_INPUT_SCHEMA_PROFILE` in that
|
||||
client's existing REA registration `env` object (`environment` for OpenCode) and
|
||||
restart the client, preserving its other settings. A successful terminal doctor run does not verify the client's
|
||||
launch environment. See [launch-environment verification](client-verification.md#launch-environment-verification)
|
||||
and the client-specific instructions below. REA setup forwards supported selections,
|
||||
including `REA_MCP_INPUT_SCHEMA_PROFILE`, from its environment into the registration;
|
||||
it does not copy unrelated environment values. Restoring the shell's previous
|
||||
value does not undo a selection already saved by setup.
|
||||
|
||||
## Runtime settings and client deadlines
|
||||
|
||||
`update_analysis_settings` changes curated defaults inside the running REA
|
||||
@@ -102,79 +77,6 @@ does not repair a client's naming behavior or infer permission drift from an
|
||||
unfamiliar prefix. The unavailable fork reported in
|
||||
[#5512](https://github.com/morluto/rea/issues/5512) has not been reverified.
|
||||
|
||||
## Hot/cold catalogs
|
||||
|
||||
For a client that supports MCP catalog refresh, set
|
||||
`REA_MCP_TOOL_CATALOG_PROFILE=hot-cold` in REA's server environment and restart
|
||||
the connection. REA then advertises a small initial catalog and loads other
|
||||
named tools through `discover_tools`. Setup preserves this explicit setting.
|
||||
See the [discovery contract](mcp-contracts.md#hotcold-catalog-discovery) for
|
||||
pagination, identity and family-scope semantics.
|
||||
|
||||
Analysis tools, including `inspect_analysis_view`, load on demand. For example,
|
||||
load `inspect_api_contract` and supply its `view` to answer an initial OpenAPI
|
||||
or GraphQL question directly. When later questions need retained facts, load
|
||||
`inspect_analysis_view` and use the complete `parent_evidence_id`; source files
|
||||
need not be reopened. If both tools are needed from the start, request both
|
||||
names in one `discover_tools` batch to avoid another catalog refresh. The same
|
||||
pattern applies to selected Playwright trace and endpoint-comparison results.
|
||||
This defers unused schema delivery and client validator preparation; it does
|
||||
not reduce parsing work, alter Evidence or shorten requested facts. Clients
|
||||
without catalog refresh should keep the default `full` profile.
|
||||
|
||||
Codex CLI 0.165.0 requires its experimental `mcp_tool_list_changed` feature for
|
||||
this workflow. Merge these entries into the existing `~/.codex/config.toml`
|
||||
without replacing other feature or REA settings:
|
||||
|
||||
```toml
|
||||
[features]
|
||||
mcp_tool_list_changed = true
|
||||
|
||||
[mcp_servers.rea.env]
|
||||
REA_MCP_TOOL_CATALOG_PROFILE = "hot-cold"
|
||||
```
|
||||
|
||||
The server setting alone does not enable client refresh. If a successful
|
||||
discovery call is followed by a missing tool, check the client's refresh feature
|
||||
and tool allow/deny lists. Clients without refresh should use the default
|
||||
`REA_MCP_TOOL_CATALOG_PROFILE=full`; reconnect after changing it. The
|
||||
[FastMCP HTTP trial](#fastmcp-and-unsloth-sse-catalog-limits) below covers
|
||||
explicit refreshes; other clients' automatic refresh remains unverified.
|
||||
|
||||
## FastMCP and Unsloth SSE catalog limits
|
||||
|
||||
If an HTTP client reports `Server-sent event exceeded the 1048576 byte limit`
|
||||
during `tools/list`, merge these settings into the **REA server process's**
|
||||
environment, then reconnect:
|
||||
|
||||
```text
|
||||
REA_MCP_INPUT_SCHEMA_PROFILE=compact
|
||||
REA_MCP_OUTPUT_SCHEMA_PROFILE=compact
|
||||
REA_MCP_TOOL_CATALOG_PROFILE=full
|
||||
```
|
||||
|
||||
This advertises all tools without requiring catalog refresh. Compact output
|
||||
schemas widen client-side validation; REA still validates complete results
|
||||
against the canonical contracts. See [output schemas](mcp-contracts.md#output-schemas).
|
||||
|
||||
On main `3e0599a94`, a real FastMCP 4.1.6 / Python MCP 2.3.0 HTTP client with
|
||||
the default 1 MiB SSE event limit rejected the 4,108,975-byte full catalog.
|
||||
The settings above delivered all 240 tools in 611,305 bytes and a
|
||||
`binary_session` call succeeded. These are measured SSE response sizes for
|
||||
that catalog, not a size guarantee for future catalogs or other profiles.
|
||||
|
||||
For a client that refreshes tools, combine `hot-cold` with the compact schema
|
||||
settings to reduce initial context further: the same trial delivered six
|
||||
initial tools in 33,903 bytes, then explicitly refreshed through 34 loaded
|
||||
tools. Hot/cold alone later exceeded the SSE limit during refresh. Follow
|
||||
the [refresh requirements](#hotcold-catalogs).
|
||||
|
||||
The Unsloth report [#9093](https://github.com/morluto/rea/issues/9093) fails at
|
||||
catalog delivery; the Unsloth application itself was not exercised in this
|
||||
trial. Schema compaction does not bound analysis results. Continue to use
|
||||
summary/detail selection and tool-specific paging for larger results; client
|
||||
receive limits and full Evidence access still apply.
|
||||
|
||||
## Fiddler Everywhere alongside REA
|
||||
|
||||
Connect Fiddler Everywhere's MCP server separately for HTTP traffic debugging,
|
||||
@@ -228,7 +130,7 @@ in the file the installed app reads:
|
||||
|
||||
- Without Claude Desktop's MSIX package storage, setup uses
|
||||
`%APPDATA%\Claude\claude_desktop_config.json`.
|
||||
- The legacy MSIX storage layout (package family `Claude_pzs8sxrjxfjjc`, installed under
|
||||
- The MSIX build (package family `Claude_pzs8sxrjxfjjc`, installed under
|
||||
`C:\Program Files\WindowsApps\Claude_<version>_x64__pzs8sxrjxfjjc`)
|
||||
virtualizes AppData writes. Once it has saved its configuration, it reads
|
||||
the package-private copy
|
||||
@@ -237,11 +139,6 @@ in the file the installed app reads:
|
||||
private copy when it exists. Before the package has saved one, they use the
|
||||
`%APPDATA%` file it still reads; with neither file, setup creates the
|
||||
private copy.
|
||||
- If `%LOCALAPPDATA%\Claude-Data\claude_pzs8sxrjxfjjc` exists, setup, doctor
|
||||
and uninstall report an unresolved location. Migrated MSIX installations
|
||||
have been observed reading `claude_desktop_config.json` in this directory.
|
||||
Quit Claude Desktop and verify its active configuration before registering
|
||||
manually. See [#8968](https://github.com/morluto/rea/issues/8968).
|
||||
- If `%LOCALAPPDATA%\Packages` holds a `Claude_<publisher ID>` package from
|
||||
another publisher, an `AnthropicPBC.Claude_<publisher ID>` package, or cannot
|
||||
be read, REA cannot establish which file Claude
|
||||
@@ -380,7 +277,7 @@ This headroom is a practical default, not a guarantee for every analysis
|
||||
duration. Setup preserves positive timeout values already in the `rea` entry.
|
||||
|
||||
Setup also forwards explicitly selected input/output schema profiles,
|
||||
`REA_MCP_MAX_RESPONSE_BYTES`, `REA_MCP_MAX_INPUT_ELEMENTS`, the Windows ChangeTime settle settings `REA_WINDOWS_CHANGE_TIME_DRIFT_MS`, `REA_WINDOWS_CHANGE_TIME_SETTLE_POLL_MS`, and `REA_WINDOWS_CHANGE_TIME_SETTLE_MAX_POLLS`, provider paths and supported Ghidra, JADX and Kuna
|
||||
`REA_MCP_MAX_RESPONSE_BYTES`, `REA_MCP_MAX_INPUT_ELEMENTS`, provider paths and supported Ghidra, JADX and Kuna
|
||||
settings. It also forwards explicit executable and resource selections for ADB,
|
||||
apktool, packet capture, GDB/Rizin/objdump, Cutter, WABT, Wakaru, firmware and
|
||||
signal tools, browser inspection, and pwntools/pwndbg. This includes their
|
||||
@@ -413,22 +310,6 @@ Kimi Code also discovers personal skills in `~/.agents/skills` and project-local
|
||||
and verifies the matching skill files through the normal shared-skill flow.
|
||||
Start a new Kimi Code session to pick up MCP and skill changes.
|
||||
|
||||
## LM Studio
|
||||
|
||||
For [LM Studio](https://lmstudio.ai/docs/app/mcp), setup writes `mcpServers.rea`
|
||||
into `mcp.json` in the LM Studio home directory. Setup finds that directory as
|
||||
LM Studio does: the path stored in `~/.lmstudio-home-pointer`, otherwise
|
||||
`~/.cache/lm-studio` when it exists, otherwise `~/.lmstudio`. Setup only reads
|
||||
the pointer file. LM Studio parses `mcp.json` as strict JSON, so setup rejects
|
||||
a file with comments, trailing commas, or a byte order mark without a backup
|
||||
or a write. The entry carries `command`, `args`, and `env`, and setup keeps
|
||||
every other server. Local models often have small context windows, so setup
|
||||
selects `REA_MCP_INPUT_SCHEMA_PROFILE=compact` unless the stored entry or the
|
||||
setup environment selects a profile explicitly. Doctor reports an entry with no
|
||||
explicit profile as stale, as for Kimi Code. LM Studio 0.4.26 on macOS loaded
|
||||
this registration, and a local model called an REA tool through it. Linux and
|
||||
Windows remain unverified.
|
||||
|
||||
## Pi
|
||||
|
||||
For [Pi](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/mcp.md),
|
||||
@@ -709,18 +590,6 @@ The user-level MCP paths follow the editor's VS Code layout. Trae's official
|
||||
documentation describes the raw JSON editor without specifying those paths;
|
||||
loading this registration in Trae remains unverified.
|
||||
|
||||
## CodeBuddy
|
||||
|
||||
For CodeBuddy, setup writes `mcpServers.rea` into the global
|
||||
`~/.codebuddy/mcp.json` (a standard `mcpServers` JSON file at the user home
|
||||
root on every platform). Entries carry `command`, `args`, and `env`; stdio is
|
||||
the default transport. CodeBuddy discovers personal skills from
|
||||
`~/.codebuddy/skills`, so setup installs its skill copy there.
|
||||
|
||||
The global MCP path is a plain JSON file; setup preserves every other server
|
||||
and top-level key when it upserts `rea`. Loading this registration, the
|
||||
bundled skill, and real REA tool calls through CodeBuddy are verified.
|
||||
|
||||
## WorkBuddy
|
||||
|
||||
For [WorkBuddy](https://www.workbuddy.cn/docs/workbuddy/From-Beginner-to-Expert-Guide/Function-Description/MCP-Guide), setup writes `mcpServers.rea` into the
|
||||
@@ -763,20 +632,21 @@ writing a registration ZCode cannot load.
|
||||
|
||||
## FreeBuff
|
||||
|
||||
For FreeBuff, `rea setup --client freebuff` writes `mcpServers.rea` into the
|
||||
user-scope `~/.agents/mcp.json` shared-agent file. Entries carry `command`,
|
||||
`args`, and `env`; stdio is the default transport. The personal skill location
|
||||
is `~/.agents/skills`, so setup installs its skill copy there. Because the same
|
||||
For FreeBuff, the proposed integration writes `mcpServers.rea` into the user-scope
|
||||
`~/.agents/mcp.json` shared-agent file. Entries carry `command`, `args`, and
|
||||
`env`; stdio is the default transport. The proposed personal skill location is
|
||||
`~/.agents/skills`, so setup installs its skill copy there. Because the same
|
||||
file doubles as ZCode's user-scope fallback (see above), a `freebuff`
|
||||
registration is also in the file ZCode uses until ZCode defines its own native
|
||||
server. Setup and doctor detect FreeBuff from its own profile directory
|
||||
(`~/.config/freebuff-desktop`), not from the shared `.agents` folder that REA's
|
||||
skill installs create for every client.
|
||||
registration is also in the file ZCode uses until ZCode defines its own native server.
|
||||
Setup and doctor detect FreeBuff from its own profile directory
|
||||
(`~/.config/freebuff-desktop`), not from the shared `.agents` folder that
|
||||
REA's skill installs create for every client.
|
||||
|
||||
FreeBuff MCP loading and skill discovery have not been verified with an
|
||||
installed native FreeBuff client/version; filesystem tests cover REA's editing,
|
||||
discovery, and ownership behavior only. Automatic applicability requires the
|
||||
FreeBuff profile directory even when the shared MCP file exists.
|
||||
The configuration write and personal skill locations are proposed integration
|
||||
boundaries. FreeBuff MCP loading and skill discovery have not been verified
|
||||
with an installed native FreeBuff client/version; filesystem tests cover REA's
|
||||
editing, discovery, and ownership behavior only. Automatic applicability
|
||||
requires the FreeBuff profile directory even when the shared MCP file exists.
|
||||
When uninstall cannot establish FreeBuff or another shared-storage reader from
|
||||
product discovery, a malformed or unreadable shared MCP file is retained
|
||||
unchanged and does not stop other REA removals, including `--purge-data`. When
|
||||
@@ -784,9 +654,8 @@ FreeBuff or ZCode product applicability is established or unresolved, the same
|
||||
malformed shared file stops uninstall before removals. See
|
||||
[Diagnose, update, and remove](installation.md#diagnose-update-and-remove).
|
||||
|
||||
FreeBuff setup support requires `[email protected]` or later (including the
|
||||
current npm `latest` release). Older versions reject
|
||||
`rea setup --client freebuff` as an unsupported client. See the
|
||||
FreeBuff setup support requires `[email protected]` or later. Older versions
|
||||
reject `rea setup --client freebuff` as an unsupported client. See the
|
||||
[installation guide](installation.md#supported-agents) for client availability.
|
||||
|
||||
## DeepSeek Harness
|
||||
@@ -839,103 +708,6 @@ Harness integration, with live DeepSeek API compatibility remaining unverified.
|
||||
See the [Harness MCP guide](https://github.com/deepseek-ai/deepseek-harness/blob/639ed015397290b3745d163aafe02ffee4aa3f84/packages/mcp/mcp-client/README.md)
|
||||
for its native configuration contract.
|
||||
|
||||
<a id="llamacpp"></a>
|
||||
|
||||
## llama.cpp
|
||||
|
||||
Use REA from llama.cpp's local Web UI through `llama-server`'s native stdio
|
||||
MCP support. REA supplies the analysis tools; llama.cpp runs your chosen model.
|
||||
It is not a replacement for a native analysis provider such as Ghidra or Hopper.
|
||||
|
||||
Install REA in a persistent location so the configuration below can use a stable
|
||||
entrypoint. For a project-local installation, run this in your chosen directory:
|
||||
|
||||
```bash
|
||||
npm install --save-exact rea-agents
|
||||
```
|
||||
|
||||
Or install it globally:
|
||||
|
||||
```bash
|
||||
npm install --global rea-agents
|
||||
```
|
||||
|
||||
See [package runners and global installation](installation.md#package-runners-and-global-installation)
|
||||
for runtime requirements and installation options. Running `npx rea-agents setup`
|
||||
alone does not create either installation; do not use its temporary package-cache path.
|
||||
Use a llama.cpp build whose `llama-server --help` lists
|
||||
`--mcp-servers-config`; older builds without that option cannot use this route.
|
||||
The configuration below uses an already installed REA package, so tool discovery
|
||||
does not depend on downloading an npm package during server startup.
|
||||
|
||||
Save a JSON file, for example `rea-mcp.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"rea": {
|
||||
"command": "/absolute/path/to/node",
|
||||
"args": ["/absolute/path/to/rea-agents/scripts/rea.mjs", "mcp"],
|
||||
"timeout_ms": 450000
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Replace both paths with your installation's actual paths. For a project-local npm
|
||||
installation, run `npm root` in that project; the script is under
|
||||
`$(npm root)/rea-agents/scripts/rea.mjs`. For a global installation, use
|
||||
`$(npm root -g)/rea-agents/scripts/rea.mjs` instead. Write the resulting absolute
|
||||
paths as JSON strings; llama-server does not expand shell expressions there.
|
||||
On Windows, use forward slashes, such as `"C:/Program Files/nodejs/node.exe"`,
|
||||
or double each backslash, as in `"C:\\Program Files\\nodejs\\node.exe"`.
|
||||
Apply this to both `command` and the script path in `args`. Single backslashes
|
||||
can make the JSON invalid or change the path through escape sequences.
|
||||
`node -p "JSON.stringify(process.execPath)"` prints your current Node.js executable
|
||||
as a quoted, escaped JSON string that can be used as the `command` value.
|
||||
Use the version supported by REA. Preserve any other
|
||||
servers if you are adding REA to an existing configuration. `timeout_ms` is
|
||||
llama.cpp's per-tool-call timeout, including cold engine startup. The example
|
||||
allows REA's default 330-second Ghidra startup budget plus 120 seconds for the
|
||||
operation. Increase it if you configure a longer provider allowance or need more
|
||||
operation time; see [provider deadlines](mcp-contracts.md#ghidra-first-query-deadlines-and-recovery).
|
||||
|
||||
Start the server with your own tool-capable GGUF model:
|
||||
|
||||
```bash
|
||||
llama-server -m /absolute/path/to/model.gguf \
|
||||
--host 127.0.0.1 --port 8080 --parallel 1 --ctx-size 16384 \
|
||||
--mcp-servers-config /absolute/path/to/rea-mcp.json
|
||||
```
|
||||
|
||||
Open `http://127.0.0.1:8080`. REA tools appear with a `rea_` prefix, such as
|
||||
`rea_analyze_javascript_application`. Select only the tools needed for the
|
||||
conversation in the Web UI's tool settings: discovery of REA's complete catalog
|
||||
does not mean that its schemas and results fit the selected model's context.
|
||||
Choose a context size your model and machine support. For a first check, enable
|
||||
`rea_analyze_javascript_application` and ask it to inspect a small JavaScript
|
||||
directory you own. This task needs no native analysis engine. Inspect the
|
||||
returned Evidence and limitations rather than relying only on the model's summary.
|
||||
|
||||
If the model prints a proposed call as ordinary text, REA has not been called.
|
||||
Check the selected model and its chat template against llama.cpp's function-calling
|
||||
guidance. A working MCP connection does not establish reliable tool selection by
|
||||
every model.
|
||||
|
||||
`--tools all` is not needed; it enables llama.cpp's separate built-in tools.
|
||||
`--ui-mcp-proxy` is also not needed for this server-side stdio connection.
|
||||
REA's setup wizard does not register llama.cpp automatically; pass the JSON
|
||||
file to llama-server explicitly and restart it after changes.
|
||||
|
||||
The MCP child runs with llama-server's permissions. Loopback binding does not
|
||||
sandbox it or make every REA operation read-only. Enable only trusted commands,
|
||||
keep the server local, and select targets and operations you are authorized to use.
|
||||
|
||||
See llama.cpp's [MCP server configuration](https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md#mcp-servers)
|
||||
and [function-calling guidance](https://github.com/ggml-org/llama.cpp/blob/master/docs/function-calling.md).
|
||||
The [verification record](client-verification.md#llamacpp) distinguishes connection
|
||||
checks from model behavior and lists the tested versions.
|
||||
|
||||
## Other client settings
|
||||
|
||||
For OpenCode, setup writes the V1 `mcp.rea` entry, which OpenCode V1 and V2
|
||||
|
||||
@@ -43,30 +43,6 @@ does not establish broader Pi compatibility. See the pinned upstream
|
||||
[`declarations.ts`](https://github.com/earendil-works/pi/blob/b45e0ebcc5ae2fc42a6f2f23104d10e656e6bedb/packages/codemode/src/declarations.ts)
|
||||
and [renderer usage notes](https://github.com/earendil-works/pi/blob/b45e0ebcc5ae2fc42a6f2f23104d10e656e6bedb/packages/codemode/README.md#declarations-for-the-model).
|
||||
|
||||
<a id="llamacpp"></a>
|
||||
|
||||
## llama.cpp
|
||||
|
||||
The [manual stdio connection](client-configuration.md#llamacpp) was checked on
|
||||
macOS arm64 with Node.js 24.18.0, llama-server b11715 (`9d1878a43`), and the
|
||||
published `[email protected]` package. `GET /tools` exposed 207 REA tools without
|
||||
enabling llama.cpp's built-in tools. With the source checkout at `9e372c6`, all
|
||||
207 forwarded names, descriptions and input schemas matched a direct MCP client.
|
||||
|
||||
Calls through `POST /tools` returned session metadata and JavaScript analysis
|
||||
Evidence for a fixture at a Unicode path. A missing target returned REA's input
|
||||
error, and a subsequent session call still succeeded. The owned llama-server
|
||||
process exited successfully after the checks. No native analysis engine or
|
||||
Windows host was exercised.
|
||||
|
||||
A local Qwen3-1.7B Q8_0 run selected the JavaScript analysis tool and received its
|
||||
Evidence, but its follow-up answer reached the output-token limit. Other small
|
||||
Qwen3 attempts returned proposed calls as ordinary JSON text rather than tool
|
||||
calls. These runs do not establish dependable model-driven investigations.
|
||||
A request with the full catalog and task history exceeded a 16,384-token context;
|
||||
select tools for the task instead of assuming that discovery implies model fit.
|
||||
The checks exercised HTTP and stdio, not browser interaction with the Web UI.
|
||||
|
||||
## End-to-end, integration and golden evidence
|
||||
|
||||
The optional `verify:qwen-client`, `verify:pi-client`, and `verify:hermes-client`
|
||||
|
||||
@@ -121,22 +121,9 @@ launch environment affects the next REA process.
|
||||
is the byte-budget shape: a decimal safe integer with a 10 MiB floor
|
||||
(`MINIMUM_RESPONSE_BYTES`), the same shared schema, and no silent fallback. A
|
||||
change needs a REA restart and a matching client receive buffer. It does not
|
||||
raise REA's reserved string limit or Node's string ceiling. Like the regex
|
||||
deadline, it is in `SELECTED_REGISTRATION_SETTINGS`, so setup forwards an
|
||||
explicit selection into the client registration.
|
||||
|
||||
Windows ChangeTime admission waits out a fresh timestamp before recording a
|
||||
baseline. `REA_WINDOWS_CHANGE_TIME_DRIFT_MS` (default 2000 ms, floor 1 ms,
|
||||
maximum 60000 ms), `REA_WINDOWS_CHANGE_TIME_SETTLE_POLL_MS` (default 25 ms,
|
||||
floor 1 ms, maximum 1000 ms), and `REA_WINDOWS_CHANGE_TIME_SETTLE_MAX_POLLS`
|
||||
(default 40, floor 1, maximum 1000) are launch settings. Unset keeps those
|
||||
defaults. An invalid value is a configuration error: it names the setting, the
|
||||
rejected value, source `setting`, the default, the floor, and the maximum, and
|
||||
says to set a positive decimal integer in the environment that starts REA and
|
||||
then restart REA. There is no runtime override. Setup forwards an explicit
|
||||
selection. When the poll budget runs out, admission keeps the last observation
|
||||
instead of failing the readable file. Raising a setting only changes how long
|
||||
that wait can last. It does not change Windows ChangeTime itself.
|
||||
raise REA's reserved string limit or Node's string ceiling. The setup
|
||||
allowlist does not copy this setting. A new headroom setting should, following
|
||||
the regex deadline.
|
||||
|
||||
A per-call budget can have a runtime default without having an environment
|
||||
setting. JavaScript `max_heap_mb`
|
||||
|
||||
@@ -1,379 +0,0 @@
|
||||
# Desktop application capture
|
||||
|
||||
`rea capture-desktop-application` launches an installed Linux desktop
|
||||
application, watches it while a person or a driver uses it, and records what
|
||||
it is, which windows it opened and how the files under the home directory
|
||||
changed. The result is the starting point for turning the application into an
|
||||
agent environment: the inventory says how to install and launch it, the windows
|
||||
say how to recognise it, and the checkpoints hold the state it read and wrote.
|
||||
|
||||
The application runs with the current user's permissions, in the current
|
||||
desktop session, against the user's real files. This is not a sandbox. The
|
||||
capture folder keeps copies of real files from the home directory, so treat it
|
||||
as private data and keep it local.
|
||||
|
||||
## Host support
|
||||
|
||||
Capture runs on Linux with an X11 display: `DISPLAY` must be set, and `xprop`
|
||||
(from `x11-utils`) must be installed, or `REA_XPROP_COMMAND` must name it.
|
||||
The window manager must support the EWMH client list (`_NET_CLIENT_LIST`,
|
||||
listed in `_NET_SUPPORTED`), as xfwm4, Mutter, KWin and Openbox do; without it
|
||||
the capture is refused, because no window could be seen. A supported list that
|
||||
is unset, as xfwm4 leaves it while no window is open, means no windows. Applications that run under XWayland are visible;
|
||||
native Wayland windows are not. Other hosts are refused before anything is
|
||||
launched.
|
||||
|
||||
## Capture an application
|
||||
|
||||
Name the application by its desktop entry id, the entry's absolute path, or an
|
||||
executable:
|
||||
|
||||
```sh
|
||||
rea capture-desktop-application --desktop-entry org.xfce.mousepad --output ./mousepad-capture
|
||||
rea capture-desktop-application --executable gnucash --argument /home/ada/books.gnucash --output ./gnucash-capture
|
||||
```
|
||||
|
||||
Desktop entries are looked up in `$XDG_DATA_HOME/applications` and then each
|
||||
`$XDG_DATA_DIRS` directory; an entry installed as a symbolic link, as flatpak
|
||||
and Nix do, is followed to its file. The entry's `Exec` line is expanded with no files;
|
||||
`--argument` values are appended in its place, and the application runs in the
|
||||
entry's `Path` or the home directory. When the line starts with
|
||||
`env NAME=value`, the program is found on the `PATH` those assignments set,
|
||||
with an empty or relative entry read from the folder the application runs
|
||||
in, and their XDG folders name the state directories. Otherwise the program is
|
||||
looked up on the host's `PATH` in the order the launch searches it, and one
|
||||
found only through a relative or empty entry is refused, since the exported
|
||||
computer would not have that entry. An `--executable` runs from
|
||||
the current directory, as it would from a shell, and a relative path to it is
|
||||
resolved there.
|
||||
|
||||
Before writing anything the command refuses to start if a window of the
|
||||
application is already open, or if the window list cannot be read. A launch
|
||||
beside a running instance can hand off to it, and that instance's unsaved
|
||||
state would be missing from the baseline. It then records the starting state,
|
||||
launches the application in its own process group with a run token in its
|
||||
environment, and reads commands from stdin, one per line. The application gets
|
||||
REA's environment without the `AGENTENV_*` variables an agent environment sets,
|
||||
such as its control token: an app the exported computer launches never has
|
||||
them, and an app captured inside a computer must not hold a credential to it.
|
||||
|
||||
| Line | Effect |
|
||||
| ------------------------------------------- | ----------------------------------------------------------- |
|
||||
| empty, `checkpoint`, or `checkpoint <name>` | Record the current state as a checkpoint |
|
||||
| any other text except `stop` | Record a checkpoint named by that text |
|
||||
| `stop`, or the end of stdin | Record a final checkpoint, close the application and finish |
|
||||
|
||||
An unnamed checkpoint is named `checkpoint-<ms>` after its time in the
|
||||
session. A name that repeats an earlier checkpoint, or is one of the capture's
|
||||
own names (`baseline`, `stop`, `time-limit`, `exit`), is refused with a
|
||||
progress line on stderr; no checkpoint is taken and the session continues.
|
||||
|
||||
Name the checkpoints a later step reads by what they mark. The
|
||||
[export](desktop-application-export.md) seeds the bundle from the baseline
|
||||
unless `--seed-checkpoint` names another, and a
|
||||
[task](desktop-application-tasks.md) is checked against the last checkpoint
|
||||
unless `--end-checkpoint` names another. So one capture can build a seed and
|
||||
then the change: launch the app, create and save the document, send
|
||||
`checkpoint seed`, make the edit and save it, send `checkpoint end`, then
|
||||
`stop`.
|
||||
|
||||
The session also finishes when the launched process has exited and the
|
||||
application's processes and windows are all gone in two consecutive samples,
|
||||
or when `--max-duration-seconds` passes. Before deciding the processes are
|
||||
gone, the capture looks for any process still carrying the run token, so a
|
||||
worker whose launcher exited before it was sampled keeps the session open. A
|
||||
sample whose window list could not be read is not taken as an empty one; the
|
||||
limitations say how many there were. One queued line is handled between
|
||||
samples, and reading stdin pauses while 16 lines wait.
|
||||
|
||||
Progress lines on stderr are JSON, `{"rea_progress": {"phase":
|
||||
"desktop_capture", "message": ...}}`. Those a driver waits on also carry an
|
||||
`event`: `{"kind": "window_open"}` once the window opens; `{"kind":
|
||||
"checkpoint", "name", "trigger", "at_ms", "entries", "changes",
|
||||
"retained_files", "retained_bytes"}` once a checkpoint has been taken, the
|
||||
baseline, `stop` and `exit` included, with the paths walked, the paths
|
||||
changed since the checkpoint before and the files retained; and `{"kind":
|
||||
"checkpoint_refused", "name", "reason"}` for a requested name it refused. A
|
||||
driver that sends `checkpoint <name>` waits for the event naming it rather
|
||||
than for a fixed time.
|
||||
|
||||
When the session ends the capture sends SIGTERM to the launched process, its
|
||||
process group and every process that carries the run token, waits up to five
|
||||
seconds for them to exit, and then removes every process that still carries
|
||||
the token, whether or not it stayed in the process group. If that cleanup
|
||||
cannot be verified after a second attempt, the capture is still written, and
|
||||
the command then fails, naming the run token and process group that may
|
||||
remain. Interrupting the command (Ctrl-C) ends the session the same way
|
||||
without a further checkpoint; the capture is still written with end reason
|
||||
`cancelled`, and the command exits with status 130. Interrupted before the
|
||||
application is launched, the command writes no capture and removes the
|
||||
content it had retained.
|
||||
|
||||
If the capture fails for any other reason before `capture.json` is written,
|
||||
the application is closed the same way and the content already retained is
|
||||
removed from the capture folder. The error names any process or file that
|
||||
could not be removed.
|
||||
|
||||
A person types these lines in the terminal. An agent can drive the
|
||||
application with a tool such as `xdotool` and write the lines to a FIFO that
|
||||
it passes as stdin. Because the end of stdin stops the session, the driver
|
||||
must keep the FIFO open for writing between checkpoints, for example with
|
||||
`exec 3>capture.fifo` in a shell, rather than open and close it for each
|
||||
line.
|
||||
|
||||
| Option | Default | Meaning |
|
||||
| ------------------------ | ------- | ----------------------------------------------------------------------------------------- |
|
||||
| `--state-path` | none | File or directory whose starting content is retained too (repeatable) |
|
||||
| `--exclude` | none | Path left out of every checkpoint walk (repeatable); the output folder is always left out |
|
||||
| `--max-duration-seconds` | 3600 | Stop the session after this long |
|
||||
| `--max-entries` | 250,000 | Paths recorded per checkpoint walk, its roots included |
|
||||
| `--max-file-bytes` | 64 MiB | Largest file whose content is retained |
|
||||
| `--max-content-bytes` | 1 GiB | Total retained content |
|
||||
| `--package-file` | none | The `.deb` file the application was installed from; its facts are recorded, not the file |
|
||||
|
||||
`--state-path`, `--exclude` and `--output` refuse an empty value, which
|
||||
would otherwise name the current directory.
|
||||
|
||||
## The capture folder
|
||||
|
||||
The output folder must be new, or empty and owned by the current user; the
|
||||
command makes it private (mode 0700). It holds:
|
||||
|
||||
- `capture.json`: the Evidence record, also printed on stdout.
|
||||
- `content/<sha256>`: the bytes of each retained file, stored once per
|
||||
distinct content.
|
||||
|
||||
The Evidence operation is `capture_desktop_application` and its predicate type
|
||||
is `rea.desktop-application-capture`. Its `host` names the platform,
|
||||
architecture, display and home directory, and the host's name (`hostname`)
|
||||
and the user's (`user`), which captured state can hold, as in a mail
|
||||
template's `ada@e81232be457d`; the export removes them from the bundle. A
|
||||
capture written before they were recorded has neither, and one is left out
|
||||
when the system cannot say, as for a user with no passwd entry. The rest of
|
||||
the result has four parts.
|
||||
|
||||
**Application.** The desktop entry (id, path, name, `Exec`, icon,
|
||||
`StartupWMClass`, categories, MIME types), the launch command and working
|
||||
directory, the executable as found on `PATH` and after resolving links, and the
|
||||
package that installed it. A Debian package records its name, version,
|
||||
architecture and the apt sources that list the installed version, with the
|
||||
origin (`o=`) apt reports for each source in `source_origins`, such as `Ubuntu`
|
||||
for the Ubuntu archive and its mirrors or `LP-PPA-<name>` for a PPA; an empty
|
||||
source list means the version came from a local `.deb` or the package lists
|
||||
are missing, which `package_lists_present` tells apart. apt's lists folder,
|
||||
and the trusted keys below, are where `apt-config` resolves
|
||||
`Dir::State::Lists`, `Dir::Etc::TrustedParts` and `Dir::Etc::Trusted` under
|
||||
`Dir`, so an `APT_CONFIG` file that moves them is followed: on a computer
|
||||
without lists, an ordinary user can run `apt-get update` with an `APT_CONFIG`
|
||||
naming writable `Dir::State::Lists` and `Dir::Cache` folders and capture with
|
||||
the same `APT_CONFIG`. Without `apt-config`, or when it fails, they are
|
||||
`/var/lib/apt/lists`, `/etc/apt/trusted.gpg.d` and `/etc/apt/trusted.gpg`.
|
||||
|
||||
The Debian package is the one `dpkg-query -S` names as the owner of the
|
||||
desktop entry the capture launched, or else of the executable, since the
|
||||
entry's package is what installs the application: LibreOffice Writer's entry
|
||||
belongs to `libreoffice-writer`, while the `/usr/bin/libreoffice` it runs
|
||||
belongs to `libreoffice-common`, which does not bring Writer with it. When the
|
||||
two differ, `executable_package` names the executable's package, which the
|
||||
export installs beside the entry's, since the entry's package need not depend
|
||||
on it (`vim-common` owns Vim's entry but does not depend on `vim`).
|
||||
`executable_package_reason` says why the computer could not install it by
|
||||
name, by the rule for library packages below: neither the Ubuntu 24.04 archive
|
||||
nor a vendor repository the export declares offers it, or `apt-cache policy`
|
||||
did not say. When a package owns the entry but none owns its program, such as
|
||||
a `~/.local/bin/<name>` found first on `PATH`, the entry's package is recorded
|
||||
without `executable_package`, and `executable_package_reason` says no package
|
||||
owns the program, so the export refuses rather than install a package that
|
||||
does not bring it. A program link that dpkg does not own counts as owned when
|
||||
dpkg owns the file it resolves to, as an alternatives link does. An
|
||||
`--executable` launch, or an entry no package owns, such as
|
||||
one in `~/.local/share/applications`, records the executable's package. When
|
||||
`dpkg-query` does not finish or exits with an error for the executable or the
|
||||
entry, the package is `unknown` and the limitations say which path, since a
|
||||
failed lookup is not proof that no package owns it. The sources, vendor
|
||||
repositories and `--package-file` match below are the recorded package's; the
|
||||
library walk still reads the executable.
|
||||
|
||||
Each source whose origin is not `Ubuntu` is also recorded in
|
||||
`apt_repositories`. `apt-get indextargets` names the sources file entry apt
|
||||
read it from and its release, so flat repositories match too, whether their
|
||||
suite is `./` (Typora's `deb https://downloads.typora.io/linux ./`) or `/`
|
||||
(pkgs.k8s.io's). The record holds that entry (`sources_file`, `uri`, `suite`,
|
||||
`components`, none for a flat repository, and `architectures`, the entry's
|
||||
`arch=` or `Architectures:` restriction, empty when it has none), `release`,
|
||||
the release's Suite (apt's `a=`) or else its Codename, and the key that signs
|
||||
it. A user name or password in an entry's URI (`https://user:password@host/…`)
|
||||
is never recorded: REA removes it from every URI it reads, in the sources file
|
||||
and in `apt-get indextargets` and `apt-cache policy` output alike, so entries
|
||||
match whether or not apt prints it, and sets `uri_credentials_removed` on a repository whose URI held
|
||||
one (a capture written before this was recorded omits the field). The key is
|
||||
found by verification, not by name: the candidates are the
|
||||
entry's `signed-by` keyrings, or else every `.gpg` and `.asc` key in apt's trusted
|
||||
parts folder and its trusted keyring, and each is checked with
|
||||
`gpgv` against the release's `InRelease` (or `Release` with `Release.gpg`)
|
||||
beside its index in apt's lists folder. A deb822 entry whose `Signed-By` field
|
||||
holds the armored key itself, as `add-apt-repository ppa:<owner>/<name>`
|
||||
writes it on Ubuntu 24.04, has that key as its only candidate, as apt trusts
|
||||
no other for it: REA reads the field's continuation lines as deb822 writes
|
||||
them, a `.` line standing for an empty one, from the key block's `BEGIN` line
|
||||
to its last `END` line, and records the key's path as the entry's sources
|
||||
file. A key counts when gpgv reports a valid signature by it and no bad,
|
||||
expired, revoked or unverifiable signature by it, whatever gpgv's exit status:
|
||||
a release also signed by a key the host lacks makes gpgv fail. Each candidate
|
||||
is copied, an armored key decoded to a binary keyring, into a private
|
||||
temporary folder that is removed afterwards; a folder REA cannot remove
|
||||
fails the capture with the folder as its cleanup resource. The key that
|
||||
verifies is recorded in `signing_key` with its path,
|
||||
SHA-256, format (`binary` or `armored`) and primary fingerprint, and its bytes
|
||||
are retained under `content/`. A key file holding one key is retained as it
|
||||
is; from one holding several, such as a legacy `/etc/apt/trusted.gpg`, only
|
||||
the transferable key with the verifying fingerprint is retained, in binary, so
|
||||
the bundle trusts no other key. When no candidate verifies, `gpgv` is
|
||||
missing, apt holds no signed release, no sources entry matches, the key the
|
||||
entry's `Signed-By` holds does not verify the release, or the key is larger
|
||||
than `--max-file-bytes` or the content budget, `signing_key` is null and
|
||||
`signing_key_reason` says why. Ubuntu Pro's ESM repositories (origins such as
|
||||
`UbuntuESMApps`, host `esm.ubuntu.com`) are not checked: apt reaches them only
|
||||
with the host's Ubuntu Pro credentials, which a computer does not have.
|
||||
|
||||
Unless the installed version comes from the Ubuntu 24.04 archive, so for a
|
||||
vendor repository's package and for a local install that no apt source lists,
|
||||
`library_packages` lists the packages that own the shared libraries the
|
||||
executable loads at start; the walk runs beside the repository check, and not
|
||||
at all for an archive package, whose export does not use it. REA reads
|
||||
DT_NEEDED from the resolved executable and from each library it finds, and
|
||||
looks each soname up as ld.so does: in the RPATH of the needing image and of
|
||||
each image up its chain of loaders to the executable when the needing image
|
||||
has no RUNPATH, then in its RUNPATH (with `$ORIGIN` expanded and paths
|
||||
normalized), and then in the `ldconfig -p` cache entries for the executable's
|
||||
machine. It asks `dpkg-query -S` for each library's package. Libraries in the
|
||||
executable's own folder belong to the application: they are walked but not
|
||||
listed, and neither is the application's own package. One batched
|
||||
`apt-cache policy` then keeps only the packages the Ubuntu 24.04 archive or a
|
||||
recorded vendor repository with a verified key offers, in any component its
|
||||
sources entry lists, since the export declares them all; the rest, such as
|
||||
packages from a PPA or a local `.deb`, are listed in
|
||||
`uninstallable_library_packages`. The same query checks `executable_package`.
|
||||
Each recorded repository's `supplies` lists the kept packages, and the
|
||||
executable's package, that the archive does not offer and its package files
|
||||
list, so the export can leave out those only a repository it does not declare
|
||||
supplies, such as one whose key the content budget later dropped. Sonames
|
||||
found nowhere are listed in
|
||||
`missing_libraries`, and libraries no package owns in `unpackaged_libraries`.
|
||||
`library_packages_reason` says why the lists are incomplete: the executable is
|
||||
not an ELF image (a launcher script, for example), `ldconfig`, `dpkg-query` or
|
||||
`apt-cache` is missing, a library could not be read or names more than 512
|
||||
libraries, or the walk stopped after 512 libraries.
|
||||
|
||||
A sources file, key or library whose handle REA cannot close fails the capture
|
||||
with the file as its cleanup resource. When the repository check and the
|
||||
library walk both fail, the capture reports both failures and every folder or
|
||||
file either could not release.
|
||||
|
||||
An application installed from a `.deb` file you downloaded, such as VariCAD
|
||||
from varicad.com, has no apt source a computer could install it from. Name
|
||||
that file with `--package-file` so the export can have the computer download
|
||||
it instead. REA reads the file's control fields with
|
||||
`dpkg-deb --field FILE Package Version Architecture Depends Pre-Depends`, and
|
||||
when its Package, Version and Architecture are the installed package's, it
|
||||
reads the file once more for its SHA-256 and size and records `package_file`:
|
||||
the file's `name` without its folder, `sha256`, `size`, `package`, `version`,
|
||||
`architecture`, and the `depends` and `pre_depends` entries as the file
|
||||
declares them. Otherwise `package_file_reason` says why, for example that
|
||||
`dpkg-deb` is not installed, that the file holds another version than the
|
||||
installed one, or that the file changed between the two reads (its device,
|
||||
inode, size and times are compared), since its fields and its hash could then
|
||||
describe different files. The capture never copies the file: it is the vendor's, and only
|
||||
these facts are kept.
|
||||
|
||||
Snaps, flatpaks and
|
||||
AppImages are recognised from their paths, and flatpaks also from a
|
||||
`flatpak run` launch, whose application id is the first operand after
|
||||
flatpak's options and their values. Electron applications are marked by
|
||||
the fuse sentinel in their binary or a `resources/app.asar` beside it. State
|
||||
directories are the XDG config, data, state and cache folders and the
|
||||
dot-folder named after the package, the executable, the desktop entry's full
|
||||
id and its last part, and the entry's name, with whether each existed at the
|
||||
start. A folder in those places whose name is one of these in another letter
|
||||
case joins them, such as GIMP's `~/.config/GIMP` for the `gimp` package; only
|
||||
the whole name is compared, so `~/.config/GIMPshop` does not. Dotfiles in the
|
||||
home named after the application (`.<name>rc`, or
|
||||
`.<name>` followed by `-`, `.` or `_`, for a name of three or more characters)
|
||||
that exist at the start join them, since an application may only read such a
|
||||
file during the capture. A `flatpak run` launch is named after its application
|
||||
id rather than the `flatpak` launcher, and adds `~/.var/app/<id>`.
|
||||
|
||||
**Session.** When it started, how long it ran and why it ended; the launched
|
||||
process's exit status; every process seen in the application's tree; every
|
||||
client window with its class pair, titles, owner pid and type, marked when it
|
||||
belongs to the application; when the first application window appeared; the
|
||||
most common class pair of the application's windows, for matching its window
|
||||
later; files the application's processes held open under the walked roots; the first
|
||||
8 KiB of the launched process's stderr; and whether cleanup was verified. Each
|
||||
process records whether the capture started it: the launched process, a
|
||||
process carrying the capture's run token, or a descendant of one, even when
|
||||
its environment cannot be read, as for a zombie or a sandboxed renderer.
|
||||
Processes are told apart by their start time, so a pid reused by another
|
||||
process is not counted as the application's.
|
||||
|
||||
A window belongs to the application when its owner pid is one of the
|
||||
application's live processes or its class matches the desktop entry's
|
||||
`StartupWMClass`, the executable's name, the entry's full id or its last part,
|
||||
or a flatpak's application id. A flatpak's windows report pids from its
|
||||
sandbox, so they are matched by class only.
|
||||
Ownership is checked again at every sample, because a window can appear before
|
||||
the process that owns it is found. A matching window's pid joins the
|
||||
application's processes, so a launch handed to a process the capture did not
|
||||
start, such as a D-Bus activated service, is still followed. That process does
|
||||
not carry the run token, so the capture never signals it and leaves it
|
||||
running.
|
||||
|
||||
**State.** The walked roots (the home directory plus any content root
|
||||
outside it), the content roots (state directories, `--state-path` values and
|
||||
absolute `--argument` values, except `/` or a directory that contains the home
|
||||
directory, which are named in the limitations instead; a selected path that is
|
||||
not a directory brings the `-wal` and `-journal` paths beside it, so a database
|
||||
selected on its own is captured with its logs), the state directories
|
||||
as walked (their physical spelling, which differs from the inventory's when one
|
||||
is a symlink), the paths every walk left out (the output folder and
|
||||
`--exclude` values), and the checkpoints in order: `baseline` before
|
||||
launch, each requested checkpoint, `stop` or `time-limit`, and `exit` after the
|
||||
application has gone. Each checkpoint records how many paths its walk saw,
|
||||
whether it was truncated, how many paths it could not read or directories on
|
||||
another device it did not enter (`unchecked_paths`), which paths were created, modified, deleted or
|
||||
changed type since the previous checkpoint, and which files it retained. A
|
||||
path counts as created or deleted only where the walk listed its nearest
|
||||
existing directory completely; differences under a directory that could not be
|
||||
listed, or that a truncated walk did not reach, are counted as unconfirmed
|
||||
instead. A walk does not enter a directory on another filesystem than its
|
||||
root, such as an NFS, sshfs or other FUSE mount under the home directory; the
|
||||
limitations list each such mount point. The
|
||||
baseline retains every file under the content roots; later checkpoints retain
|
||||
every changed file. A change records the file's type, size, mode,
|
||||
modification time and, when known, the SHA-256 of its content before and
|
||||
after, plus whether it lies in a state directory and whether the application
|
||||
held it open. A file is retained only if it is still the version the walk
|
||||
recorded and does not change while it is read. A file larger than the
|
||||
remaining content budget is not read, unless stored content of the same size
|
||||
could make it a duplicate. Files that could not be
|
||||
retained are listed with the reason: too large, over the content budget,
|
||||
changed since the walk, changed during the read, or unreadable.
|
||||
Directories the application held files open in, but whose starting content was
|
||||
not retained, are listed so a second capture can pass them with
|
||||
`--state-path`.
|
||||
|
||||
**Limitations.** What the capture could not establish, such as a truncated
|
||||
walk or a missing window.
|
||||
|
||||
## What the capture does not establish
|
||||
|
||||
Changes are found by comparing walks, using each path's size, modification
|
||||
time, inode and mode, so a change undone between two checkpoints is not seen
|
||||
and a rewrite that keeps all four is missed. A change is not attributed to the
|
||||
process that made it: other desktop services write under the home directory
|
||||
too, such as the dconf database, the window manager's settings and GTK's
|
||||
recently used list. The markers for state directories and open files are
|
||||
evidence for attribution, not proof.
|
||||
|
||||
Clicks and keystrokes are not recorded. Window titles are sampled every half
|
||||
second, so short-lived titles can be missed.
|
||||
@@ -1,342 +0,0 @@
|
||||
# Desktop application export
|
||||
|
||||
`rea export-desktop-application <capture-folder> <directory>` turns a folder
|
||||
written by [desktop application capture](desktop-application-capture.md) into an
|
||||
agentenv bundle holding one computer app environment: the real application,
|
||||
installed from the distribution archive, its vendor's apt repository or the
|
||||
`.deb` file it was installed from on the `ubuntu-24.04` computer base, launched
|
||||
at boot, seeded with the user's state and serving that state through agentenv's
|
||||
data operations.
|
||||
|
||||
```sh
|
||||
rea export-desktop-application ./mousepad-capture ./mousepad-bundle
|
||||
agent-env run ./mousepad-bundle --dry-run
|
||||
```
|
||||
|
||||
The export reads the capture and its [state view](desktop-application-state.md);
|
||||
it does not run the application. `--name` sets the environment name; otherwise
|
||||
it comes from the desktop entry id or the program, with an `app-` prefix when
|
||||
that name would not be a portable bundle name (for example all digits). The
|
||||
output directory must be new or empty. `--task` adds a task an agent wrote for
|
||||
the capture, once REA has validated it against the capture; see
|
||||
[desktop application tasks](desktop-application-tasks.md). `--package-url`
|
||||
says where the computer downloads the package of an application installed from
|
||||
a `.deb` file; see [Applications from a .deb file](#applications-from-a-deb-file).
|
||||
`--seed-checkpoint` names the checkpoint whose state seeds the bundle, the
|
||||
baseline by default, and `--end-checkpoint` the one a task must pass on, the
|
||||
capture's last by default; see [The seed checkpoint](#the-seed-checkpoint).
|
||||
|
||||
The command returns Evidence with operation `export_desktop_application` and
|
||||
predicate type `rea.desktop-application-export`, listing the environment name,
|
||||
the seed and end `checkpoints`, owned paths, seed files, the seeds it
|
||||
rewrote to remove the capture host's name and user (`scrubbed_files`), the
|
||||
bundle files that still hold them (`unscrubbed_files`), and written files,
|
||||
the last three by their path in the bundle. A failed export, cancellation
|
||||
included, removes the bundle it had not yet published, so no partial copy of the
|
||||
user's data remains. If the bundle was published but its output handles could
|
||||
not be closed, the command reports a cleanup error that carries the export
|
||||
Evidence; the bundle itself is complete.
|
||||
|
||||
## The bundle
|
||||
|
||||
| Path | Contents |
|
||||
| -------------------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
| `envs/<name>/env.toml` | `type = "computer_app"`, the module and class, `computer = "ubuntu-24.04"`, `driver = "reference"` |
|
||||
| `envs/<name>/<file>.py` | The `ComputerAppEnvironment` class |
|
||||
| `envs/<name>/SKILL.md` | What the app is, its window class and where its files live |
|
||||
| `envs/<name>/seed/` | The application's files at the seed checkpoint, by home-relative path |
|
||||
| `envs/<name>/apt-keys/` | The signing key of each vendor apt repository the app installs from |
|
||||
| `envs/<name>/install-package.sh` | With `--package-url`: the run script that installs the downloaded package with dpkg |
|
||||
| `tasks/smoke.json` | A task that deploys the app |
|
||||
| `tasks/<task>.json` | With `--task`: the agent's task, its verifier in `artifacts/` and the eval in `evals/` |
|
||||
| `README.md` | What was exported, from which capture, and what to keep in mind |
|
||||
|
||||
The class file is `<name>_app.py` with hyphens as underscores, for example
|
||||
`mousepad_app.py`: agentenv imports it under its stem, so a plain `<name>.py`
|
||||
could shadow a standard library module such as `calendar`. A class name that
|
||||
would collide with a name the module imports or defines, such as `Path`, takes
|
||||
an `App` suffix.
|
||||
|
||||
The class declares:
|
||||
|
||||
- `Packages(apt=[...])` with the package the capture found. A vendor package
|
||||
is named `<package>/<release>`, its repository's release (the Suite apt
|
||||
reports as `a=`, or else its Codename), so apt takes it from that repository
|
||||
even when the Ubuntu archive offers a higher version or epoch, as noble's
|
||||
transitional `firefox` does over packages.mozilla.org's; it is named alone
|
||||
when the release names neither, or when the capture was written before
|
||||
releases were recorded. Its library packages follow, so a package that
|
||||
leaves out dependencies on the libraries it loads, as Typora's Electron
|
||||
package does, still starts; library packages the capture found neither the
|
||||
Ubuntu 24.04 archive nor a declared repository offers are left out, with a
|
||||
limitation, and so are those only repositories the export leaves out supply
|
||||
(a repository's `supplies`), such as one whose key the capture could not
|
||||
retain. An archive package's declared dependencies are trusted as they
|
||||
are. When the desktop entry's package is not the one that owns the program
|
||||
it runs, as `vim-common`'s entry runs `vim`'s program, the program's package
|
||||
(`executable_package`) follows the app's by name, since the entry's package
|
||||
need not depend on it; a capture that found no archive or declared
|
||||
repository offering it records why, and the export refuses it with that
|
||||
reason, as it does when only repositories it leaves out supply it, or when
|
||||
no package owns the program at all (`executable_package_reason` without
|
||||
`executable_package`). The package must come from the Ubuntu 24.04 archive
|
||||
or a mirror of it,
|
||||
or from a vendor apt repository whose signing key the capture verified and
|
||||
retained. A source counts as the archive when apt reports its origin as
|
||||
`Ubuntu` for a noble suite; a capture made before origins were recorded
|
||||
counts only Canonical's own archive hosts.
|
||||
- For a vendor repository, `sources=[AptSource(apt=..., suite=...,
|
||||
components=[...], key="apt-keys/<name>.gpg")]`, with the retained key copied
|
||||
to that path (`.asc` when it is armored, which agentenv tells by its
|
||||
content). A PPA added with `add-apt-repository`, whose sources entry holds its
|
||||
key in `Signed-By`, exports that key as `apt-keys/<name>.asc`. apt takes one
|
||||
key per URI and suite, so sources that share them, such as one repository
|
||||
listed for two architectures, are declared once, with their components
|
||||
merged; one with a different key is left out. A repository
|
||||
whose key the capture did not verify, an Ubuntu Pro (ESM) repository, one
|
||||
whose sources entry's URI held a user name or password (which the capture
|
||||
did not record and the export never writes; the reason names neither them
|
||||
nor the URI), one not reached over http or https, or one whose `arch=`
|
||||
restriction leaves out the capture host's architecture is refused with its
|
||||
reason (a capture
|
||||
written before restrictions were recorded counts as unrestricted); when
|
||||
another usable repository lists the same version it is left out instead,
|
||||
with a limitation. The export also refuses snaps, flatpaks, AppImages, and a
|
||||
package no recorded repository lists unless it was installed from a `.deb`
|
||||
file the capture recorded and `--package-url` is given, since the computer
|
||||
could not install them by name, and a capture written before credentials
|
||||
were removed whose repository URI still holds them, since it counted that
|
||||
repository's libraries as installable; capture it again.
|
||||
- `@computer_app(supports=...)` with both `amd64` and `arm64` for an archive
|
||||
package, which the archive serves for both, and only the capture host's
|
||||
architecture for a vendor package, since its repository may serve no other.
|
||||
A vendor package captured on any other architecture is refused. A package
|
||||
installed from its `.deb` file supports only that file's architecture.
|
||||
- A launch at boot with the captured command, ready when a window of the
|
||||
captured class appears and one of them is listed in the taskbar and is not
|
||||
typed as a splash: a splash screen or a dialog of the class skips the
|
||||
taskbar or says it is a splash, and an app whose splash shows first would
|
||||
otherwise count as ready before its own window opens. Arguments that are the captured home or under it, and
|
||||
`<option>=<path>` values under it, are respelled under the computer user's
|
||||
`$HOME`. agentenv starts the app in its own folder, where an open or save
|
||||
dialog would show the framework's files, so the command first changes to the
|
||||
captured working directory (`"$HOME"` when that was the home, as for a
|
||||
desktop entry without `Path`), which a relative argument or program path
|
||||
such as `./app` also needs, and still starts if that folder is missing. The
|
||||
export refuses a capture that saw no application window.
|
||||
- For an Electron application (the capture found its fuse sentinel or a
|
||||
`resources/app.asar`), `--no-sandbox` right after the program unless the
|
||||
command already has it: Chromium's sandbox needs user namespaces the
|
||||
computer's container does not grant, and Electron exits without them. The
|
||||
README and the result's limitations say the sandbox is off.
|
||||
- `paths`: the state directories holding the application's state files and the
|
||||
folders holding its documents, nested folders collapsed. A folder beside a
|
||||
state directory whose name differs from it only in letter case is owned
|
||||
whole in the files' spelling, so a `~/.config/GIMP` profile GIMP created
|
||||
during the capture beside the candidate `~/.config/gimp` is owned as
|
||||
`.config/GIMP`, not as the folder of its first file. The home itself and
|
||||
the shared `.config`, `.local`, `.local/share`, `.local/state` and `.cache`
|
||||
folders are never owned whole: a file directly in one is owned as itself.
|
||||
Files the capture recorded under a symlinked state directory's physical
|
||||
spelling are mapped back to the innermost such directory's home spelling,
|
||||
unless that spelling is outside the captured home, as when the home itself is
|
||||
a symbolic link. When two files map to one home path, the export uses the one
|
||||
present before the capture, else the first by path, and a limitation names
|
||||
the other.
|
||||
- Data hooks, because declaring any hook switches off agentenv's default data
|
||||
plane: the class seeds the home once per boot and keeps it as the initial
|
||||
state with `keep_initial`, `data/add` and `data/reset` call agentenv's `add`
|
||||
and `reset`, and `data/get` returns the files under `paths` plus a data part
|
||||
with a typed JSON view of each, read in a worker thread. An application with
|
||||
no observed state declares no paths and no hooks.
|
||||
|
||||
A file under the owned paths whose bytes at the seed the capture did not
|
||||
retain is not seeded: one in the state view without retained content at that
|
||||
checkpoint, or one the capture omitted (for example too large or past the
|
||||
content budget). The result lists them in `unseeded_files` and the README
|
||||
names them. When the application held files open in folders whose starting
|
||||
content the capture did not retain, a limitation names those folders. The
|
||||
desktop Trash, `~/.local/share/Trash`, is never the application's, so a file
|
||||
the user moved there is neither owned nor seeded, even when a window title
|
||||
names it; a limitation lists the files the Trash held at the seed.
|
||||
|
||||
The application's dconf keys at the seed, if any, are written once when the
|
||||
app first starts, also for an application that owns no files, and the class adds
|
||||
`dconf-cli` to its apt packages for that. The server starts before the
|
||||
desktop's D-Bus session, so the class does not use `dconf load`: it dumps any
|
||||
existing user database through a `file-db:` profile, then runs `dconf compile`
|
||||
on that dump and the captured keys, in that name order so the captured keys
|
||||
win, and replaces `~/.config/dconf/user`. If that fails, the server prints a
|
||||
one-line warning and the app starts without the captured keys. `data/reset` does
|
||||
not restore them.
|
||||
|
||||
## The seed checkpoint
|
||||
|
||||
By default the seed is the state before the capture, its `baseline`, and a
|
||||
task's end state is the capture's last checkpoint. `--seed-checkpoint` and
|
||||
`--end-checkpoint` name others, so one capture can make a seed and then a
|
||||
change: the user creates and saves a document and takes `checkpoint seed`,
|
||||
then edits and saves it and takes `checkpoint end`. Exported with
|
||||
`--seed-checkpoint seed --end-checkpoint end`, the bundle seeds the document
|
||||
as it was saved at `seed`, owned paths, dconf keys and unseeded files
|
||||
included, and a task must fail on that seed and pass on the state at `end`.
|
||||
A file's state at a checkpoint is its last version recorded at or before it.
|
||||
The export refuses a name the capture does not hold exactly once, and an end
|
||||
checkpoint taken before the seed checkpoint; `inspect-desktop-application-state`
|
||||
lists the names in `capture.checkpoints`.
|
||||
|
||||
## Host name and user
|
||||
|
||||
A seed can hold the capture host's name and the user's: Geany saved its mail
|
||||
template as `pref_template_mail=ada@e81232be457d`, the user `ada` on a
|
||||
container named `e81232be457d`, and many files hold the home path
|
||||
`/home/ada`. The capture records `host.hostname` and `host.user`, and the
|
||||
export removes them from what the bundle ships:
|
||||
|
||||
- In seeded text files (text, JSON, key files and XML in the state view), in
|
||||
UTF-16 files that start with a byte-order mark (keeping the mark, byte order
|
||||
and a following U+FEFF content character), and in the dconf keys,
|
||||
`user@host` becomes `user@localhost`, the
|
||||
host name standing alone becomes `localhost`, and the captured home becomes
|
||||
`/home/user` wherever it is followed by `/`, the end of the text, or a
|
||||
character that cannot continue a file name. The host name counts only as a
|
||||
whole name: not inside a longer word, before a `-`, or as a label of a
|
||||
domain such as `e81232be457d.local`, so a host named `ubuntu` leaves
|
||||
`ports.ubuntu.com/ubuntu-ports` alone. A host named `localhost` is left
|
||||
alone, but its `user@localhost` still takes the computer's user. The user
|
||||
name alone is never replaced, since a short one such as `ada` or `user` is
|
||||
also ordinary text. A rewritten seed is written with its own SHA-256, and
|
||||
`scrubbed_files` and the README list it.
|
||||
- JSON and XML keep their syntax. In JSON only string values change, each
|
||||
read as its decoded value, so `\u0065` escapes cannot hide the host, and is
|
||||
written back as JSON only when it changed; numbers and `true`, `false` and
|
||||
`null` stay as they are even for a host named `true`. Member names are never
|
||||
rewritten, since `{"ubuntu": 1, "localhost": 2}` on a host named `ubuntu`
|
||||
would get two `localhost` keys and lose a setting; a file whose names hold
|
||||
the host is listed in `unscrubbed_files` instead. In XML only character
|
||||
data, attribute values, comments and CDATA change, character references
|
||||
decoded the same way; element and attribute names and processing
|
||||
instructions stay. A UTF-16 file is scrubbed by the format its text would
|
||||
have in UTF-8, so JSON or XML in UTF-16 follows these rules too.
|
||||
- In the dconf keys only GVariant strings change, each read as its decoded
|
||||
value and written back in its own quotes only when it changed. Group names,
|
||||
keys, dictionary keys, bytestrings and everything outside quotes stay, so a
|
||||
host named `true` leaves `wrap=true` alone. A value whose string does not
|
||||
parse, or that holds an object path or signature, ships as captured.
|
||||
- `SEED_HOME` in the class is `/home/user`, the home the seed spells, which
|
||||
the class respells to the computer's own home when it seeds the home at
|
||||
boot, as it did the captured home before. The replacements read as the
|
||||
computer's own: its user is `user`, with its home in `/home/user`, and
|
||||
`localhost` is the name every host has for itself.
|
||||
- Binary files, SQLite databases among them, and text files over 256 MiB are
|
||||
copied as captured. Then every file the bundle ships, the class module with
|
||||
its dconf keys and the README included, is searched by a looser rule than
|
||||
the replacement's: the host name and `user@host` anywhere, as in
|
||||
`e81232be457d.local` or a seed named `e81232be457d-notes.txt`, and the
|
||||
captured home as a whole path, in its path and its content, JSON and dconf
|
||||
strings and XML references decoded and the replacements themselves set
|
||||
aside. Each one found is listed in `unscrubbed_files`, and in the README
|
||||
unless it is the README, to check before sharing the bundle. Bytes that are not text are
|
||||
searched as they are, so a binary file that holds the host in another
|
||||
encoding is not listed.
|
||||
- A task check whose values name `user@host` or the host name anywhere, by
|
||||
that rule, is refused, as one naming the captured home is: the bundle does
|
||||
not hold them. Its field names, kind and id are not values, so a host named
|
||||
`file` or `kind` refuses no check. A task's checks must fail on the seed as
|
||||
the bundle ships it, scrubbed, so a check the scrub itself satisfies, such
|
||||
as a mail setting the user changed from `ada@e81232be457d` to
|
||||
`user@localhost`, does not count as failing there.
|
||||
|
||||
A capture written before the host's name and user were recorded has only its
|
||||
home removed, with a limitation saying so; capture again to remove the rest.
|
||||
|
||||
## Applications from a .deb file
|
||||
|
||||
A closed-source application you installed from a `.deb` file you downloaded,
|
||||
such as VariCAD from varicad.com, is not in any apt repository the computer can
|
||||
use. Capture it with `--package-file` naming that file (see
|
||||
[desktop application capture](desktop-application-capture.md)), then pass
|
||||
`--package-url` to the export with an `http://` or `https://` URL the computer
|
||||
can download that same file from:
|
||||
|
||||
```sh
|
||||
rea capture-desktop-application --executable varicad --package-file ~/Downloads/varicad2026-en_2.03_amd64.deb --output ./varicad-capture
|
||||
rea export-desktop-application ./varicad-capture ./varicad-bundle --package-url https://files.example.com/varicad2026-en_2.03_amd64.deb
|
||||
```
|
||||
|
||||
The class then declares, beside `Packages`:
|
||||
|
||||
- `downloads=[Download(url=..., sha256=..., to="vendor/<file>", arch=...)]`
|
||||
with the captured file's SHA-256. agentenv downloads the URL once, when the
|
||||
bundle is put, and refuses the file unless its SHA-256 matches, so the
|
||||
computer installs exactly the captured version. The bundle does not contain
|
||||
the package.
|
||||
- `Packages(apt=[...])` with the first alternative of each of the file's
|
||||
Depends and Pre-Depends entries, without version constraints or `:any`
|
||||
qualifiers, then the library packages the capture found, since a vendor's
|
||||
binary may load libraries its Depends leaves out.
|
||||
- `run=[RunScript(script="install-package.sh")]`, a script the export writes
|
||||
into the environment folder. agentenv runs it as root from the app's folder
|
||||
once the apt packages are installed and the download is in place, with the
|
||||
network off: it runs `dpkg -i` on the download and then removes it.
|
||||
|
||||
The bundle supports only the file's architecture. Vendor sites often refuse
|
||||
automated downloads, as varicad.com does, so the URL may need to point at a
|
||||
copy you keep, such as a private mirror; you must be allowed to use that copy,
|
||||
and the package's licence, such as a 30-day trial's terms, is yours to check.
|
||||
The README and the result's limitations say so.
|
||||
|
||||
The export refuses a package no recorded repository lists when `--package-url`
|
||||
is missing, saying to capture with `--package-file` if the capture lacks it. It
|
||||
also refuses `--package-url` when the capture recorded no package file, giving
|
||||
the capture's `package_file_reason` when there is one; when the archive or a
|
||||
declared vendor repository installs the package already; when the URL is not
|
||||
written as `http://` or `https://` or holds a user name or password; and when
|
||||
the file is built for an architecture other than `amd64`, `arm64` or `all`.
|
||||
|
||||
## Typed views
|
||||
|
||||
The views follow the [state view](desktop-application-state.md)'s formats and
|
||||
shapes: SQLite tables (at most 64 ordinary tables, `tables_truncated` past
|
||||
that, and per table `columns`, `primary_key`, at most 500 `rows` and
|
||||
`truncated`, with blobs, unsafe integers and text that is not UTF-8 tagged as
|
||||
REA tags them), JSON, XML as `{root, truncated}` with REA's node, depth and
|
||||
whitespace rules and its `doctype` when the document declares one, key file
|
||||
groups, text as
|
||||
`{lines, line_count, truncated}` split on `\n` and `\r\n`, and zip archives
|
||||
as their entries, read with Python's `zipfile` from the file in place, with
|
||||
REA's entry, size, compression ratio and archive limits and its wording for an
|
||||
entry it does not read. The module reads the central directory itself, as REA
|
||||
does, and shows `zipfile` only the records it lists, reading each entry in
|
||||
bounded chunks no further than a byte past its declared size. A JSON file
|
||||
holding a number JavaScript cannot read exactly, such as an integer above 2^53,
|
||||
or whose retained value nests more than 500 levels deep, gets an `error` instead of a view, as in
|
||||
REA. Duplicate keys keep their last value before the depth check. Text that is not one JSON
|
||||
document, such as JSON Lines, stays text. Format detection follows REA's
|
||||
rules. Files larger than 64 MiB get an `error` instead of a view
|
||||
and are not read. XML is read with Python's `expat` twice: with namespace
|
||||
processing, which refuses what REA's parser refuses, such as an undeclared
|
||||
prefix, then without it, so element and attribute names keep the prefixes the
|
||||
document spells and namespace declarations stay attributes, as in REA's view;
|
||||
text splits into nodes where REA's splits it, at CDATA sections, comments and
|
||||
processing instructions. Both passes read a DOCTYPE as REA does: with
|
||||
parameter entity parsing off, so the DTD it names is never read, and handlers
|
||||
that refuse an internal subset, an external entity and an entity expat would
|
||||
skip; the first pass also checks each start tag for a reference to an
|
||||
undeclared entity, which expat drops from an attribute value without a word
|
||||
when a DOCTYPE names a DTD. Known differences remain:
|
||||
|
||||
- dconf databases are reported as binary, since REA reads them with `dconf`;
|
||||
inside an archive, one is `dconf` with an error, as in REA.
|
||||
- Zip entry names: a name in an Info-ZIP Unicode Path extra field is used only
|
||||
where Python's `zipfile` reads it (3.12 and later), and an entry whose local
|
||||
header names it differently from the central directory is damaged rather
|
||||
than read.
|
||||
- SQLite databases are read in place, read-only, rather than from a staged copy;
|
||||
a write lock the running app holds is waited out for a quarter of a second
|
||||
and then gives the file an `error`, and so does a table or column name that
|
||||
is not UTF-8.
|
||||
|
||||
The seed is the user's real data. Keep the bundle local and do not publish it.
|
||||
The class was checked against agentenv-framework 0.9.1431 with
|
||||
`python -m agentenv_protocol.computer._declare` and `agent-env run --dry-run`.
|
||||
@@ -1,211 +0,0 @@
|
||||
# Desktop application state
|
||||
|
||||
`rea inspect-desktop-application-state <capture-folder>` reads a folder written
|
||||
by [desktop application capture](desktop-application-capture.md) and describes
|
||||
the application's state: which observed files are the application's, what each
|
||||
retained version contains as typed JSON, and how each file changed between
|
||||
checkpoints. It reads only the capture folder; the application is not run
|
||||
again.
|
||||
|
||||
```sh
|
||||
rea inspect-desktop-application-state ./mousepad-capture --format json
|
||||
```
|
||||
|
||||
The result is Evidence with operation `inspect_desktop_application_state` and
|
||||
predicate type `rea.desktop-application-state`. Its confidence is `inferred`,
|
||||
because roles come from heuristics; the views themselves are read from the
|
||||
retained bytes, which are checked against their SHA-256 before use.
|
||||
|
||||
## Paths and roles
|
||||
|
||||
The view covers every file retained at the baseline and every file that
|
||||
changed later. Each path records the evidence the capture holds about it:
|
||||
|
||||
| Reason | Meaning |
|
||||
| ------------------------ | ------------------------------------------------------------------------- |
|
||||
| `state_directory` | It lies in one of the application's state directories, in either spelling |
|
||||
| `opened_by_application` | An application process held it open during a sample |
|
||||
| `named_in_window_title` | An application window title spelled its name or `~/` path on its own |
|
||||
| `selected_path` | It lies under a `--state-path` or absolute `--argument` of the capture |
|
||||
| `dconf_application_keys` | It is a dconf database holding groups whose path names the application |
|
||||
| `desktop_service` | It lies where desktop services and toolkits keep shared files, or caches |
|
||||
| `desktop_trash` | It lies in the desktop Trash, `~/.local/share/Trash` |
|
||||
|
||||
A folder beside a state directory whose name differs from it only in letter
|
||||
case counts as that state directory, so a profile the application first
|
||||
creates during the capture, such as GIMP's `~/.config/GIMP` beside the
|
||||
candidate `~/.config/gimp`, is its state too.
|
||||
|
||||
The role follows from the reasons, in this order: `shared_desktop` for a file
|
||||
in the desktop Trash, which the user deleted, whatever else names it;
|
||||
`application_state` for application keys in dconf, or a state directory outside shared desktop
|
||||
locations; `document` for a file named in a window title or selected by the
|
||||
caller, even if the application holds it open; `application_state` for another
|
||||
open file outside shared locations; `shared_desktop` for other files in shared
|
||||
locations; `unrelated` otherwise. A window title names a file only when no
|
||||
other file name character adjoins the name, so `day.txt` does not match
|
||||
`today.txt - Notes`.
|
||||
|
||||
Application names, which select dconf groups, come from the package, the
|
||||
executable, the desktop entry's id and `StartupWMClass`, and the window match.
|
||||
The entry's display name is left out, since a generic name such as `Settings`
|
||||
would claim other applications' groups.
|
||||
|
||||
## Versions and views
|
||||
|
||||
Each path lists its versions: the baseline, then its state after each
|
||||
checkpoint where it, or a database's `-wal` or `-journal` file, changed. A
|
||||
checkpoint that leaves an absent file absent adds no version. A file's state
|
||||
at any checkpoint is its last version recorded at or before it, which is how
|
||||
the [export](desktop-application-export.md#the-seed-checkpoint) and the
|
||||
[task draft](desktop-application-tasks.md) read it at the seed and end
|
||||
checkpoints they are given; `capture.checkpoints` lists the names in order.
|
||||
Views hold the captured bytes as they are, the capture host's name and user
|
||||
included; the export removes those from the bundle. A version records
|
||||
whether the file was present, the SHA-256 of its retained bytes, and a view:
|
||||
|
||||
| Format | Detected by | View |
|
||||
| --------- | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
|
||||
| `sqlite` | The `SQLite format 3` header | Every ordinary table's columns, primary key and up to 500 rows |
|
||||
| `dconf` | The `GVariant` header | Groups whose path names the application, plus the names of the other groups |
|
||||
| `zip` | A zip local file header, `PK\x03\x04`, whatever the name | Its file entries in archive order, each with its own format and view |
|
||||
| `json` | A `.json` name or a leading `{` or `[` that parses | The parsed value, from files up to 4 MiB nested up to 500 levels |
|
||||
| `xml` | An XML name or declaration that parses | An element tree (`name`, `attributes`, `children`); text kept exactly, DTDs never read |
|
||||
| `keyfile` | Only `[group]`, `key=value` and comment lines, with at least one group | Groups of `key` → string value, from files up to 4 MiB |
|
||||
| `text` | Other UTF-8 text | Lines, up to 5,000 |
|
||||
| `binary` | NUL bytes or invalid UTF-8 | None |
|
||||
|
||||
A JSON value holding a number JavaScript would round, such as an integer above
|
||||
2^53, is not read rather than shown rounded.
|
||||
|
||||
An XML view names elements and attributes as the document spells them, prefix
|
||||
included: a saved Inkscape drawing's root is `svg`, in its default namespace,
|
||||
with `sodipodi:docname` and the declarations `xmlns` and `xmlns:sodipodi`
|
||||
among its attributes, and a Writer paragraph is `text:p`. An element's
|
||||
`children` hold its elements and text in order, text split where a CDATA
|
||||
section, comment or processing instruction falls. Whitespace-only text between
|
||||
child elements is formatting, and dropped, in an element that holds no other
|
||||
text, unless `xml:space="preserve"` applies; in mixed content, an element with
|
||||
text of its own beside its children such as a paragraph split into runs, it is
|
||||
kept, so `<p>Meet on <s>Sunday</s> <s>evening</s></p>` keeps the space between
|
||||
the runs. Whitespace here is XML's: space, tab, carriage return and line feed;
|
||||
a no-break space is text. The view keeps up to 5,000 nodes, comments and
|
||||
processing instructions counted, nested up to 64 levels, and sets `truncated`
|
||||
when it leaves some out.
|
||||
|
||||
A document that begins with a DOCTYPE naming its DTD, as AbiWord's `.abw`
|
||||
does, reads as the same element tree, and the view records the declaration
|
||||
beside `root`, its public identifier normalized as XML matches it:
|
||||
|
||||
```json
|
||||
{
|
||||
"doctype": {
|
||||
"name": "abiword",
|
||||
"public_id": "-//ABISOURCE//DTD AWML 1.0 Strict//EN",
|
||||
"system_id": "http://www.abisource.com/awml.dtd"
|
||||
},
|
||||
"root": { "name": "abiword", "attributes": {}, "children": [] },
|
||||
"truncated": false
|
||||
}
|
||||
```
|
||||
|
||||
An identifier the DOCTYPE leaves out is `null`, and a document without one
|
||||
has no `doctype`. The DTD is never fetched or read, so it adds no default
|
||||
attributes and declares no entities. A DOCTYPE with an internal subset
|
||||
(`<!DOCTYPE a [ … ]>`), which could declare entities, is not read as XML, nor is
|
||||
a document that refers to any entity but XML's five (`&`, `<`, `>`,
|
||||
`'`, `"`); such a file is text.
|
||||
|
||||
Exported XML views use REA's same formatting-whitespace classification, so
|
||||
retained XML text and the task checks that address it stay aligned.
|
||||
|
||||
SQLite databases are read through REA's SQLite worker from a private copy,
|
||||
with any retained `-wal` or `-journal` file staged beside it; the worker
|
||||
accepts an inactive rollback journal and refuses a hot one. A version is not
|
||||
read when its `-wal` or `-journal` file existed but was not retained, or when
|
||||
the capture did not record whether either existed, since the database alone
|
||||
could misstate its committed rows. The capture records them under its content
|
||||
roots, which include both beside a database selected on its own, when its
|
||||
baseline walk listed every directory, read every path, entered no other
|
||||
device, did not stop early and recorded what it left out, and wherever they change; an excluded log, and any elsewhere, is
|
||||
unknown, since an unchanged file and a missing one look alike. A table
|
||||
that cannot be read keeps its `error` in place of its rows, and changes to that
|
||||
database are marked incomplete. dconf
|
||||
databases are read with `dconf dump /` against a private file profile, so the
|
||||
live database and the dconf service are not touched; this needs `dconf-cli`, or
|
||||
`REA_DCONF_COMMAND`.
|
||||
|
||||
A version has a `view_error` when its bytes were not retained, are over the 64
|
||||
MiB read limit, are missing or no longer match their digest, exceed a format's
|
||||
limit above, or its reader failed.
|
||||
|
||||
## Archives
|
||||
|
||||
Many applications save a document as a zip archive of XML and other files:
|
||||
FreeCAD's `.FCStd` and `.FCBak`, OpenDocument (`.odt`, `.ods`, whose
|
||||
`content.xml` is the document), Office Open XML, EPUB and JAR. Such a file is
|
||||
`zip`, and its view lists its file entries in archive order, leaving out
|
||||
directories: an entry whose name ends in `/`, or whose attributes mark a Unix
|
||||
directory or, from an MS-DOS host, the directory attribute:
|
||||
|
||||
```json
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"name": "Document.xml",
|
||||
"size": 11923,
|
||||
"sha256": "…",
|
||||
"format": "xml",
|
||||
"view": {
|
||||
"root": { "name": "Document", "attributes": {}, "children": [] },
|
||||
"truncated": false
|
||||
},
|
||||
"view_error": null
|
||||
}
|
||||
],
|
||||
"entry_count": 15,
|
||||
"truncated": false
|
||||
}
|
||||
```
|
||||
|
||||
Each entry's bytes are read from the archive's verified bytes in memory,
|
||||
checked against its CRC-32 and declared size, and given a `format` and `view`
|
||||
by the rules and limits above, with the entry's name in place of a file name.
|
||||
Nothing is extracted to disk. An entry has a `view_error`, with `sha256`,
|
||||
`format` and `view` null, when it is encrypted, compressed other than stored or
|
||||
deflated, larger than 16 MiB, more than 100 times its compressed size, past the
|
||||
64 MiB read from one archive, or its data is damaged. A SQLite or dconf
|
||||
database, or an archive, inside an archive keeps its format and `sha256` and
|
||||
has a `view_error` instead of a view: it is not read. The view lists at most
|
||||
1,000 entries; `entry_count` counts them all, and `truncated` is true when some
|
||||
are not listed. REA reads the central directory itself, a record at a time,
|
||||
through the Zip64 end record whenever a Zip64 locator precedes the end record,
|
||||
as Python's `zipfile` does, and hands the zip reader only the records it lists,
|
||||
so an archive of hundreds of thousands of entries costs no more to read
|
||||
than one of a thousand. An archive whose central directory cannot be read has a
|
||||
`view_error` of its own.
|
||||
|
||||
## Kinds of change
|
||||
|
||||
Between consecutive versions a change is `created`, `deleted`, or a
|
||||
format-specific kind with what was added, removed and changed (up to 100 of
|
||||
each, with counts):
|
||||
|
||||
- `lines` for text: lines added and removed, as a multiset.
|
||||
- `values` for JSON and XML: JSON pointers to values, containers included, so
|
||||
an array that becomes an object with the same members is changed. A pointer
|
||||
longer than 4096 characters is counted but not listed.
|
||||
- `values` for zip archives too, with array members named by
|
||||
[selectors](desktop-application-tasks.md#pointers-and-selectors) rather than
|
||||
positions wherever a selector names one member in both versions: entries by
|
||||
name, as `/entries/[name=Document.xml]`, and XML elements by tag and an
|
||||
attribute, as `/view/root/children/[name=Object][@name=Hole]`, so an object
|
||||
inserted before another does not shift it. A comparison is incomplete when
|
||||
either view leaves out entries, or an entry's view is missing or truncated.
|
||||
- `keys` for key files and dconf: `group/key`, with `~` and `/` in the key
|
||||
written `~0` and `~1` as in a JSON pointer, so `[a/b]` `c` and `[a]` `b/c`
|
||||
stay apart.
|
||||
- `rows` for SQLite: `table:` and the row's primary key, or the whole row when
|
||||
the table has none.
|
||||
- `content` for binary files, and `unreadable` when either version has no
|
||||
view.
|
||||
@@ -1,343 +0,0 @@
|
||||
# Desktop application tasks
|
||||
|
||||
A [desktop application export](desktop-application-export.md) can carry one
|
||||
task: a prompt for a computer-use agent and checks that grade what the agent
|
||||
left in the application's files. An agent, not REA, writes the prompt and the
|
||||
checks from the change the capture recorded. REA validates the task against
|
||||
the capture and keeps it only when its checks fail on the seed and all pass on
|
||||
the captured end state. REA runs no model and no code the agent wrote: the
|
||||
checks are declarative, and the exported verifier evaluates them the same way
|
||||
REA does.
|
||||
|
||||
```sh
|
||||
rea describe-desktop-application-task ./mousepad-capture --format json > draft.json
|
||||
# edit the draft's task into task.json: a name, the user's request, the checks
|
||||
rea export-desktop-application ./mousepad-capture ./mousepad-bundle --task task.json
|
||||
agent-env run ./mousepad-bundle --task add-milk --agent claude-code
|
||||
```
|
||||
|
||||
## Describe the change
|
||||
|
||||
`rea describe-desktop-application-task <capture-folder>` reads the capture and
|
||||
its [state view](desktop-application-state.md) and returns Evidence with
|
||||
operation `describe_desktop_application_task` and predicate type
|
||||
`rea.desktop-application-task-draft`:
|
||||
|
||||
- `application`: the environment name the export would use, the title and the
|
||||
window class.
|
||||
- `owned_paths`: the paths the export owns, the only ones whose files the
|
||||
app's `data/get` serves and checks can read.
|
||||
- `checkpoints`: the two checkpoints compared, `baseline` for the seed and
|
||||
`final` for the end. They are the capture's first and last unless
|
||||
`--seed-checkpoint` and `--end-checkpoint` name others, as for the
|
||||
[export](desktop-application-export.md#the-seed-checkpoint), which takes
|
||||
the same options; a name the capture does not hold, or an end before the
|
||||
seed, is refused.
|
||||
- `files`: each file under them whose state differs between those
|
||||
checkpoints, with its home path, role, its versions at both (`baseline` and
|
||||
`final`, the last recorded at or before each) and the net change between them, from the state view's own diff (`lines`, `values`,
|
||||
`keys` or `rows` with what was added, removed and changed, or `created`,
|
||||
`deleted`, `format`, `content`, `unreadable`; `content` also when the
|
||||
views hold only part of a file and match while the file changed). A
|
||||
database is compared by its rows, since its hash leaves out the `-wal`
|
||||
file. Text changes list each line
|
||||
once and add `line_counts`, the lines found at both ends whose number of
|
||||
occurrences changed. Views appear only for the files the suggestions cover,
|
||||
with arrays past 200 items and strings past 2,000 characters cut
|
||||
(`view_cut`).
|
||||
- `task`: a draft with the name `change`, a placeholder prompt the export
|
||||
refuses, and suggested checks derived from the net change of the files with
|
||||
role `document`, or of the app's own state files when no document changed.
|
||||
A text file gains `lines_present` for its new lines and `lines_absent` for
|
||||
lines it lost (when either is at most ten), and `line_count` for each line
|
||||
holding a letter whose count changed; JSON and key files gain
|
||||
`value_equals` and `value_absent` by pointer; SQLite tables gain rows added,
|
||||
changed and removed, by primary key, and their new row count. An XML file,
|
||||
and the JSON, key file and XML views of a zip archive's entries, gain checks
|
||||
by [selectors](#pointers-and-selectors) and never through an element by
|
||||
position: in XML, first [`text_equals`](#whole-text) for each element whose
|
||||
text changed, then an element added whole that it exists, one removed whole
|
||||
that it is absent, then values changed, added and removed, an entry or a
|
||||
JSON value added whole through its members, and an empty array or object as
|
||||
it is. The text check names the paragraph rather than a run inside it: it
|
||||
goes up from an element sitting in its parent's text, and from one named by
|
||||
its tag or position alone to the nearest ancestor named by an attribute, as
|
||||
AbiWord's `p` by its `xid`; the text nodes and runs inside it get no other
|
||||
check, but an element holding no text that is added or removed there, such
|
||||
as an image or a line break, keeps its own, since the text check never
|
||||
reads it. Whitespace an app lays out between elements is not checked, and
|
||||
values that look like a time the app stamps, in ISO 8601 or as C's
|
||||
`asctime` writes it (`Thu Nov 5 09:20:31 2026`), are left out.
|
||||
|
||||
The suggestions are a starting point. Keep the ones that say what the user
|
||||
did; drop the ones that only follow from how the app saves its file. A LibreCAD
|
||||
capture that added four holes to a drawing rewrote it from DXF R12 to DXF 2007,
|
||||
so most of its lines changed; the draft suggests counts of `SECTION`, `ENDSEC`
|
||||
and `CIRCLE`, and only `CIRCLE` going from 5 to 9 is the user's change.
|
||||
|
||||
## The task file
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "add-milk",
|
||||
"prompt": "Add buy milk to my to-do list in Mousepad and save it.",
|
||||
"checks": [
|
||||
{
|
||||
"id": "milk",
|
||||
"kind": "lines_present",
|
||||
"file": "Documents/todo.txt",
|
||||
"lines": ["buy milk"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`name` is a portable bundle name other than `smoke` (lowercase letters, digits
|
||||
and hyphens); `prompt` is what the user asks for; `checks` holds 1 to 32
|
||||
checks. Each check has a `kind`, a `file` given as a path under the home such as
|
||||
`Documents/todo.txt`, and an optional `id` (`check-1`, `check-2` and so on by
|
||||
position otherwise). A check reads the typed view `data/get` returns for that
|
||||
file, in the shapes of the state view.
|
||||
|
||||
| Kind | Fields | Passes when |
|
||||
| --------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------ |
|
||||
| `file_present` | | The file exists |
|
||||
| `file_absent` | | The file does not exist |
|
||||
| `lines_present` | `lines` | Every listed line is a whole line of the text view |
|
||||
| `lines_absent` | `lines` | None of the listed lines is |
|
||||
| `line_count` | `exact` or `contains`; `equals`, `at_least` or `at_most` | The number of lines equal to `exact`, or holding `contains`, compares so |
|
||||
| `value_equals` | `pointer`, `value`; optional `tolerance` | The pointer names a value equal to `value`, or within `tolerance` of it |
|
||||
| `value_absent` | `pointer` | The pointer names no value |
|
||||
| `text_equals` | `pointer`, `value`; optional `normalize_whitespace` | The XML element's [whole text](#whole-text) equals `value` |
|
||||
| `text_contains` | `pointer`, `value`; optional `normalize_whitespace` | The XML element's whole text holds `value` |
|
||||
| `row_present` | `table`, `where` | A row of the SQLite table has every column in `where` equal |
|
||||
| `row_absent` | `table`, `where` | No row does |
|
||||
| `row_count` | `table`; optional `where`; `equals`, `at_least` or `at_most` | The number of rows, or of rows matching `where`, compares so |
|
||||
|
||||
The rules both evaluators follow:
|
||||
|
||||
- An absent file reads as empty: no lines, values or rows. So `lines_absent`,
|
||||
`value_absent` and `row_absent` pass on it and a count is 0.
|
||||
- A file that has no view (binary, over the read limit, or unreadable) fails
|
||||
every check but `file_present` and `file_absent`. Line checks need a text
|
||||
view, row checks a SQLite view and text checks an XML view, in a file or an
|
||||
archive entry; another format fails.
|
||||
- Equality is JSON equality: numbers by value, never a boolean for a number,
|
||||
objects by their keys whatever their order, arrays in order.
|
||||
- Pointers follow RFC 6901 (`~1` for `/`, `~0` for `~`) and must start with
|
||||
`/`; on an array a segment may be a selector, below. Key files read as
|
||||
`{"groups": {"GROUP": {"KEY": "VALUE"}}}`, so a setting is
|
||||
`/groups/GROUP/KEY`; XML as `{"root": {"name", "attributes", "children"}}`;
|
||||
SQLite as `{"tables": {"TABLE": {"columns", "rows"}}}`; a zip archive as
|
||||
`{"entries": [{"name", "format", "view", …}]}`.
|
||||
- With `tolerance`, a non-negative number, `value` must be a number, and the
|
||||
value found passes when it is a number, or a string that is a decimal
|
||||
numeral such as `"5.0000000000000000"`, at most `tolerance` from `value`.
|
||||
`tolerance: 0` compares numerically, so `"1.2500"` equals `1.25`. Anything
|
||||
else fails as not a number.
|
||||
- `contains` matches a line that holds it anywhere, case and all. Checks take
|
||||
no regular expressions: a pattern run on what the agent wrote could stall
|
||||
the verifier, and JavaScript and Python read some patterns differently.
|
||||
- A check decided by what a view leaves out (`lines_absent`, `value_absent`,
|
||||
`row_absent`, the counts, `value_equals` of a whole object or array, and
|
||||
the text checks of an element) fails when that view is truncated; a check
|
||||
satisfied by what the view holds is decided on it.
|
||||
|
||||
The export refuses, naming the check, a file outside the owned paths, a file
|
||||
spelled with a leading slash, `.` or `..`, a pointer into a file that is text
|
||||
at the seed, as the bundle ships it, or at the end checkpoint, the empty
|
||||
pointer (the whole file), a value that names the captured home, or the
|
||||
capture host's name or `user@host` anywhere (which the
|
||||
[export removes from the seed](desktop-application-export.md#host-name-and-user)), a
|
||||
`tolerance` with a value that is not a number, an empty `text_contains`
|
||||
value, or one of only whitespace with `normalize_whitespace`, and a value that
|
||||
looks like a date and time, in ISO 8601 or `asctime`'s form. Other volatile
|
||||
content, such as a
|
||||
counter the app bumps on every save, is for the author to leave out.
|
||||
|
||||
## Pointers and selectors
|
||||
|
||||
An app that rewrites a document moves things: FreeCAD 1.0 records more
|
||||
properties than 0.21, so a hole's radius sits at another position among its
|
||||
siblings, and a plate's entry moves once a hole is added before it. A pointer
|
||||
segment applied to an array may therefore name a member by what it holds
|
||||
rather than by its position:
|
||||
|
||||
- `[KEY=VALUE]` names the member whose string field `KEY` is `VALUE`, such as
|
||||
a zip entry by `[name=Document.xml]` or an XML element by its tag,
|
||||
`[name=Object]`.
|
||||
- `[@KEY=VALUE]` names the member whose `attributes` hold `KEY="VALUE"`, as
|
||||
the XML view keeps an element's attributes.
|
||||
- Conditions can follow one another, `[name=Object][@name=Hole]`, and all must
|
||||
hold. They compare strings only, the selector is escaped like any pointer
|
||||
segment (`~1` for `/`), and a value cannot hold `][`.
|
||||
|
||||
A selector must name exactly one member. When several match, or none does
|
||||
before the last segment, the check fails and says which selector matched what.
|
||||
When the last segment's selector matches none, the pointer names no value:
|
||||
`value_absent` passes, which is how a check says an element was removed, and
|
||||
`value_equals` fails. On an object, a bracketed segment is an ordinary key.
|
||||
|
||||
A FreeCAD task that checks the user cut a 10 mm hole through the plate, in
|
||||
FreeCAD 0.21 and 1.0 alike:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "cut-hole",
|
||||
"prompt": "Cut a 10 mm hole through the middle of the plate and save it.",
|
||||
"checks": [
|
||||
{
|
||||
"id": "cut",
|
||||
"kind": "value_equals",
|
||||
"file": "Documents/plate.FCStd",
|
||||
"pointer": "/entries/[name=Document.xml]/view/root/children/[name=Objects]/children/[name=Object][@type=Part::Cut]/attributes/type",
|
||||
"value": "Part::Cut"
|
||||
},
|
||||
{
|
||||
"id": "radius",
|
||||
"kind": "value_equals",
|
||||
"file": "Documents/plate.FCStd",
|
||||
"pointer": "/entries/[name=Document.xml]/view/root/children/[name=ObjectData]/children/[name=Object][@name=Hole]/children/[name=Properties]/children/[name=Property][@name=Radius]/children/[name=Float]/attributes/value",
|
||||
"value": 5,
|
||||
"tolerance": 0.01
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Before the edit the archive has no `Part::Cut` and no `Hole`, so both checks
|
||||
fail on the seed; after it, `Radius` is `"5.0000000000000000"` and both pass.
|
||||
A check decided by what a view leaves out depends, in an archive, on the entry
|
||||
its pointer goes into: it fails when that entry's view is missing or
|
||||
truncated, or when the entry is not listed and the listing is truncated. A
|
||||
selector must see every member to know it names one, so a check whose pointer
|
||||
searches an array the view may hold only part of fails as undecided: any array
|
||||
of a truncated XML view, of a truncated entry's view, or the listing when it
|
||||
is cut. A position is decided on what the view holds. Like a file, an entry
|
||||
that has no view (binary, not read, or a database or archive inside the
|
||||
archive) fails every check into its `view`; its `null` there is no value, while
|
||||
an entry holding the JSON document `null` reads as `null`. Checks on its `name`,
|
||||
`size`, `sha256` and `format` still read.
|
||||
|
||||
XML that uses namespaces is named as the document spells it, in both
|
||||
evaluators: an element or attribute by its prefixed name, such as `text:p`
|
||||
or `inkscape:label`, an element in a default namespace by its bare name, such
|
||||
as `svg`, and namespace declarations as the attributes `xmlns` and
|
||||
`xmlns:PREFIX`. Text is a string among an element's `children`. These checks
|
||||
hold a poster's headline in an Inkscape SVG and a date in a LibreOffice Writer
|
||||
document:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "headline",
|
||||
"kind": "value_equals",
|
||||
"file": "Pictures/poster.svg",
|
||||
"pointer": "/root/children/[name=g][@inkscape:label=Layer 1]/children/[name=text][@id=headline]/children/[name=tspan]/children/0",
|
||||
"value": "Summer Bake Sale"
|
||||
},
|
||||
{
|
||||
"id": "date",
|
||||
"kind": "value_equals",
|
||||
"file": "Documents/flyer.odt",
|
||||
"pointer": "/entries/[name=content.xml]/view/root/children/[name=office:body]/children/[name=office:text]/children/[name=text:p][@text:style-name=Subtitle]/children/0",
|
||||
"value": "Monday 24 November"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Inkscape writes a line of text as a `tspan` inside its `text` element, and
|
||||
Writer keeps a paragraph's style in `text:style-name`. A prefix is the one the
|
||||
document uses, so a check names the prefixes the app writes.
|
||||
|
||||
## Whole text
|
||||
|
||||
A text node is only the stretch of text between two pieces of markup, and
|
||||
apps split a paragraph into runs as they save it. AbiWord wraps text typed
|
||||
into a reopened document in `c` elements, so changing a time to `3:30` saves
|
||||
`<p>The team meeting starts at <c>3</c>:<c>30</c> in room 4.</p>`; Writer,
|
||||
which records revision IDs by default, writes text typed in one session as
|
||||
`text:span` runs. A `value_equals` on the paragraph's `children/0` then reads
|
||||
`"The team meeting starts at "` and fails. `text_equals` and `text_contains`
|
||||
read the element's whole text instead:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "time",
|
||||
"kind": "text_equals",
|
||||
"file": "Documents/meeting.abw",
|
||||
"pointer": "/root/children/[name=section]/children/[name=p][@xid=3]",
|
||||
"value": "The team meeting starts at 3:30 in room 4."
|
||||
}
|
||||
```
|
||||
|
||||
- The pointer names an element of an XML view, in a file or in an archive
|
||||
entry's `view`, or a text node or attribute value in one, whose text is its
|
||||
value. Another format, a pointer into an archive outside an XML entry's
|
||||
view, or any other value fails, an element's `name` and the `doctype`'s
|
||||
fields included: they are strings of the view, not text of the document.
|
||||
- An element's text is XPath's `string()` of what the view keeps: every text
|
||||
node in it and in its descendants, CDATA included, in document order, with
|
||||
nothing between them. The view drops whitespace-only text between child
|
||||
elements only in an element that holds no other text, outside
|
||||
`xml:space="preserve"`, so the space between two runs of a paragraph is
|
||||
part of its text. Elements that stand for characters, such as Writer's
|
||||
`text:s`, `text:tab` and `text:line-break`, add none.
|
||||
- `text_equals` passes when the text equals `value` exactly, case and all;
|
||||
`text_contains` when it holds `value`, as a time within a sentence, so the
|
||||
check holds whatever the rest of the sentence says.
|
||||
- `normalize_whitespace: true` collapses each run of XML whitespace (space,
|
||||
tab, carriage return, line feed) in both the text and `value` to one space
|
||||
and trims both ends before comparing, as XPath's `normalize-space()` does,
|
||||
for an app that reflows text or a value written across lines. A no-break
|
||||
space is not whitespace there.
|
||||
|
||||
## Validation and export
|
||||
|
||||
`rea export-desktop-application <capture> <directory> --task <task.json>`
|
||||
evaluates the checks on the capture's state view: on each file as it was at
|
||||
the seed checkpoint, which is the seed, as the bundle ships it with the
|
||||
capture host's name and user removed, and at the end checkpoint, the
|
||||
captured end state. Those are the capture's first and last checkpoints unless
|
||||
`--seed-checkpoint` and `--end-checkpoint` name others.
|
||||
It refuses a task whose every check passes on the seed, and a check that fails
|
||||
on the end state, with that check's reason. Only a failure on what the seed's
|
||||
view shows counts: a task whose checks fail on the seed only because a view
|
||||
could not be read or may have left out what they look for, such as a line past
|
||||
the 5,000 a text view keeps, is refused, naming each failure. A capture yields
|
||||
one task, so the option is taken once. The result's `task` names the task, its
|
||||
number of checks and those that fail on what the seed shows. A refused task
|
||||
writes no bundle.
|
||||
|
||||
The bundle then holds, besides the app and `tasks/smoke.json`:
|
||||
|
||||
| Path | Contents |
|
||||
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
|
||||
| `tasks/<name>.json` | Deploy the app, deploy the agent and its skills, record the screen, prompt the agent, run the verifier, stop recording |
|
||||
| `artifacts/<name>-check/verify.py` | Reads the app's `data/get` through the computer and evaluates the checks, one result per check |
|
||||
| `evals/<env>.toml` | The task, so a run of the bundle runs it; the smoke task runs with `--task smoke` |
|
||||
|
||||
The verifier finds the app among the computer's child environments, calls its
|
||||
`data/get` under the run's grant of the computer's control token, and reads
|
||||
the `state` data part. It raises when the app cannot be reached, so the run
|
||||
fails rather than scoring the agent. The README lists the task, its prompt, its
|
||||
checks and the recipe below.
|
||||
|
||||
## Solve rate
|
||||
|
||||
REA does not run agents. To measure how often an agent solves the task, run it
|
||||
k times, three by default, and count the runs whose summary line says
|
||||
`passed`:
|
||||
|
||||
```sh
|
||||
for run in 1 2 3; do
|
||||
agent-env run ./mousepad-bundle --task add-milk --agent claude-code --model MODEL
|
||||
done
|
||||
```
|
||||
|
||||
The agent defaults to Claude Code and the model is the caller's choice (Claude
|
||||
Sonnet 5.5 is a reasonable reference). A run reported as `scored below 1`
|
||||
failed the checks and counts as a miss; one reported as `failed` did not finish,
|
||||
as when the computer or the app could not be reached, and is run again rather
|
||||
than counted. Each run's instance keeps the verifier's results per check under
|
||||
`verifications`.
|
||||
@@ -110,15 +110,6 @@ not establish full metadata validity or correspondence to a native binary;
|
||||
version 24 inspection still applies only to supported header layouts. See
|
||||
[IL2CPP metadata](il2cpp-metadata.md).
|
||||
|
||||
In a managed directory with a Unity Mono witness, `Assembly-CSharp.dll` and
|
||||
`Assembly-CSharp-firstpass.dll` get an `inspect_managed_members` probe listed
|
||||
first; project-named asmdef assemblies keep only the ordinary managed probes.
|
||||
The same probe is offered for those names in `Managed/` of a
|
||||
`<Product>_BackUpThisFolder_ButDontShipItWithYourGame` folder (or when that
|
||||
folder is the root); whether its IL matches the shipped IL2CPP code is unknown.
|
||||
Point the scan at the game folder: a root that is the `<Product>_Data` or
|
||||
`Managed` folder itself has no Mono witness path and gets no member probe.
|
||||
|
||||
Candidates are derived or inferred from these facts and do not prove loading or
|
||||
execution. BepInEx, MelonLoader, Easy Anti-Cheat and BattlEye labels identify
|
||||
path conventions only; runtime presence stays unknown. No bypass is provided.
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
# Desktop build computer
|
||||
|
||||
A build computer is a local agentenv computer that an agent drives while it
|
||||
builds a desktop app environment. Claude Code cannot attach a new MCP server
|
||||
in the middle of a session, so REA keeps the computer up and forwards the
|
||||
agent's computer-use actions to it through three MCP tools:
|
||||
|
||||
| Tool | What it does |
|
||||
| ----------------------- | ------------------------------------------------------------------------------------------ |
|
||||
| `start_build_computer` | Starts a computer from a request or a bundle folder; returns its id and a first screenshot |
|
||||
| `act_on_build_computer` | Sends one action to the computer's driver and returns the driver's answer, images included |
|
||||
| `close_build_computer` | Tears the computer down, by agentenv's teardown or by removing its sandbox's containers |
|
||||
|
||||
The tools are MCP-only, like the debugger sessions: the computer lives as long
|
||||
as the REA MCP server that started it, or until it is closed. Capturing the
|
||||
app, exporting its bundle and witnessing the task on such a computer are in
|
||||
[Building and witnessing an environment](desktop-build-witness.md).
|
||||
|
||||
## Requirements
|
||||
|
||||
- Docker, and an agentenv-framework install whose `agent-env` program is named
|
||||
by the absolute `REA_AGENT_ENV_COMMAND`. Bundles target agentenv
|
||||
0.9.1431. REA runs its helper, `bridge/agentenv/computer.py`, with that
|
||||
runner's Python: the interpreter on the program's `#!` line, or the
|
||||
`python3` beside it in its virtual environment.
|
||||
- `REA_DOCKER_COMMAND`, the absolute path of the Docker CLI, lets REA remove a
|
||||
computer whose agentenv teardown was not confirmed. Without it, REA names the
|
||||
command to run instead.
|
||||
- For a request, `REA_BUILD_COMPUTER_REA_ARCHIVE`: the absolute path of a tar
|
||||
archive of a runnable REA, with its production dependencies, whose one top
|
||||
folder holds `scripts/rea.mjs`. The computer runs REA from it. From a
|
||||
checkout, `npm run pack:build-computer-runtime -- /path/to/rea-prod.tar.xz`
|
||||
writes one: `npm pack`, its package folder as
|
||||
`rea-prod` with the lockfile, `npm ci --omit=dev --omit=optional`, then
|
||||
`rea-prod` as `.tar.xz`.
|
||||
|
||||
Set these where the MCP server starts; a shell export does not reach a server
|
||||
that is already running.
|
||||
|
||||
## Start a computer
|
||||
|
||||
From a request, REA renders a bundle in a private temporary folder: a computer
|
||||
app on the `ubuntu-24.04` base with the `reference` driver that installs the
|
||||
app, opens it on demand with `gtk-launch <desktop entry>` from the computer
|
||||
user's home, so its open and save dialogs start there rather than in the
|
||||
framework's folder for the app, and keeps no seed and no data hooks. The app is not open when the computer starts: agentenv lists it
|
||||
in the desktop's Applications menu under Other by the environment's name, such
|
||||
as `mousepad`, and that entry launches it through the app's server.
|
||||
|
||||
When the app is Electron, set `electron` to true. The install rewrites that
|
||||
desktop entry's command with the same policy an export uses: `--no-sandbox`
|
||||
immediately after the program, unless the command already has it. The
|
||||
computer's container does not grant the user namespaces Chromium's sandbox
|
||||
needs, and Electron exits without the flag. With the rewritten command, the
|
||||
Applications menu entry can open the app.
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"kind": "request",
|
||||
"desktop_entry": "org.xfce.mousepad",
|
||||
"window_class": "Mousepad",
|
||||
"install": { "kind": "archive", "package": "mousepad" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`install` takes one of:
|
||||
|
||||
| `kind` | Installs |
|
||||
| ---------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| `archive` | `package` from the Ubuntu 24.04 archive, on amd64 and arm64 |
|
||||
| `apt_repository` | `package` from the repository at `uri` with `suite` and `components`, trusting the key file `key_file` |
|
||||
| `package_file` | The `.deb` at `url`, checked against `sha256`, for one `architecture`, after its apt `dependencies` are in |
|
||||
|
||||
Build steps run with the network off, so a `.deb`'s dependencies must be
|
||||
listed. The bundle adds REA in a build-only layer: Node.js
|
||||
24.22.1, downloaded per architecture and checked against its release checksum,
|
||||
and REA from `REA_BUILD_COMPUTER_REA_ARCHIVE`, both unpacked into `/opt/rea` by
|
||||
a root build step, with `node` and `rea` on `PATH`. An exported bundle carries
|
||||
none of it.
|
||||
|
||||
From a bundle folder, such as one `export-desktop-application` wrote, REA
|
||||
starts that bundle's `tasks/smoke.json` as it is:
|
||||
|
||||
```json
|
||||
{ "source": { "kind": "bundle", "path": "/home/ada/mousepad-bundle" } }
|
||||
```
|
||||
|
||||
REA runs its helper with `PYTHONDONTWRITEBYTECODE=1`, which agentenv's own
|
||||
import of the bundle's app class inherits, so a start leaves no `__pycache__`
|
||||
in the folder and its files stay as they were shared.
|
||||
|
||||
REA runs the deploy with `run_bundle(keep=True, emulate=False)` on the local
|
||||
sandbox provider, so only the host's own architecture is built. The first start
|
||||
of a bundle builds its image and can take minutes; later starts reuse it.
|
||||
While it waits, REA sends an MCP progress notification every five seconds with
|
||||
agentenv's latest line, so a client that resets its request timeout on
|
||||
progress keeps waiting. REA then waits until the computer's readiness checks
|
||||
pass and opens one MCP session to its driver. Readiness can pass before the
|
||||
desktop has drawn, so REA takes a whole-screen PNG screenshot every 500 ms
|
||||
while it is one colour throughout. The retries share 10 s, each given only
|
||||
what is left of it, and when that runs out REA returns the latest frame;
|
||||
progress notifications continue meanwhile. Given `screenshot` with a
|
||||
`region`, `format` or `quality`, REA then takes one with them, a `region`
|
||||
cropping that frame. The result holds
|
||||
`computer_id`, agentenv's `sandbox_id`, the environment name or bundle folder,
|
||||
and the screenshot's frame as structured content beside its image.
|
||||
|
||||
A failed start returns the last 16 KiB of the helper's stderr, where a failing
|
||||
build prints its error, and its exit status; a cancelled one stops the helper,
|
||||
which tears down whatever agentenv brought up. When that teardown is not
|
||||
confirmed, the error's `computer_id` names what REA keeps for
|
||||
`close_build_computer` to retry. Closing the server cancels starts still in
|
||||
progress and waits for them before closing every computer.
|
||||
|
||||
## Act
|
||||
|
||||
`act_on_build_computer` takes `computer_id`, an `action` and the driver's own
|
||||
arguments for `computer_<action>`, which REA passes through:
|
||||
|
||||
| `action` | Arguments |
|
||||
| ------------ | --------------------------------------------------------------- |
|
||||
| `screenshot` | `region`, `format` (`png` or `jpeg`), `quality` |
|
||||
| `click` | `x`, `y`, `button`, `count` (2 double-clicks), `modifiers` |
|
||||
| `drag` | `path` of `[x, y]` points, `button`, `duration_ms`, `modifiers` |
|
||||
| `scroll` | `direction`, `x`, `y`, `amount`, `modifiers` |
|
||||
| `type` | `text`, where `\n` presses Enter and `\t` Tab |
|
||||
| `key` | `keys`, one chord pressed in order, and `repeat` |
|
||||
| `wait` | `ms` |
|
||||
| `reset` | `screenshot` |
|
||||
|
||||
`reset` is not a driver tool: REA calls the app's `data/reset`, which restores
|
||||
the bundle's seed and relaunches the app, then waits until the computer's
|
||||
readiness checks pass again, sending an MCP progress notification every five
|
||||
seconds while it waits. With `screenshot: true` it then takes a screenshot; if
|
||||
that fails, the reset still counts as applied and its result has no `frame`
|
||||
but a `screenshot_error` with the computer's message. A computer started from
|
||||
a request has no data operations, so it refuses a reset.
|
||||
|
||||
Pointer actions also take `frame_id`, `space` and `norm_scale`, and every
|
||||
action but `screenshot` takes `screenshot: true` for an image afterwards. That
|
||||
image is taken at once, so after an action that opens a menu or a window, ask
|
||||
for it with a `wait` of a few hundred milliseconds instead.
|
||||
Points are pixels of the latest frame, so take a screenshot first; with
|
||||
`space: "norm"` they are on a `0..norm_scale` grid over it. Keys are W3C names
|
||||
or one character; REA translates model spellings such as `ctrl+s`, `Return`,
|
||||
`esc` and `cmd` and leaves other names for the driver to judge. An argument the
|
||||
action does not take is refused before anything is sent.
|
||||
|
||||
The result carries the driver's content unchanged, its text and images, after
|
||||
REA's own JSON: `action_index` and the driver's structured result. A refusal,
|
||||
such as a stale frame, is an error carrying the computer's message. A response
|
||||
larger than the MCP budget is refused with how to ask for a smaller image: a
|
||||
`region`, or `jpeg` with a lower `quality`. That error keeps REA's own result,
|
||||
`computer_id` included, as `details.result`, since the action or start has
|
||||
happened.
|
||||
|
||||
Actions, the capture steps and the task checks on one computer run one at a
|
||||
time, in the order REA receives them; one that has to wait for the call
|
||||
before it sends an MCP progress notification every five seconds meanwhile.
|
||||
Different computers run side by side.
|
||||
|
||||
Every action is logged on the computer in order with the arguments sent, its
|
||||
start and end and whether it was applied, refused or failed. The log keeps the
|
||||
latest 256 actions within 256 KiB of JSON, dropping the oldest first. Each
|
||||
string argument, such as `type`'s `text`, is kept to its first 1 KiB and each
|
||||
list, such as `drag`'s `path`, to its first 64 items; an entry names the
|
||||
arguments it cut in `truncated_arguments`, and the driver gets them whole. The
|
||||
log counts every action, so it says how many it dropped, and keeps the index of
|
||||
the last applied action of each kind for rules over the whole session. The log
|
||||
and the computer's recording hooks (`computer-recording/v1` start and end under
|
||||
REA's own request id, and the segments fetched through the driver's `data/get`
|
||||
one at a time, each read within its manifest size and checked against it) are
|
||||
what the witness records; they are not tools of their own. A recording larger
|
||||
than a quarter of the V8 heap is refused before any segment is read.
|
||||
|
||||
## Close
|
||||
|
||||
`close_build_computer` ends REA's MCP session and closes the helper's stdin,
|
||||
on which the helper runs agentenv's teardown. When agentenv confirms nothing is
|
||||
left, the result says `teardown: "agentenv"`. Otherwise REA removes the
|
||||
containers of that exact sandbox: agentenv runs a computer as
|
||||
`agent-<sandbox id>` without a label and labels its other containers
|
||||
`agentenv.sandbox=<sandbox id>`, so REA lists both with `docker ps -aq
|
||||
--filter`, removes them with `docker rm -f`, and says
|
||||
`teardown: "container_label"`. When neither confirms it, the error names what
|
||||
is left and the computer stays listed, so closing it again retries. A close
|
||||
cancels the call in progress on the computer, refuses the calls waiting for
|
||||
it, and tears down once that call has ended. Closing the
|
||||
MCP server closes every computer the same way, and a helper whose REA server
|
||||
exits sees its stdin close and tears down.
|
||||
|
||||
The control token that guards the computer's framework-only routes stays in
|
||||
REA: no result, error or log carries it. Code running in the computer as its
|
||||
user can still read it, which is acceptable for a local build.
|
||||
|
||||
See the [specification](specs/desktop-app-environments/build-computer/spec.md)
|
||||
for scope and verification.
|
||||
@@ -1,175 +0,0 @@
|
||||
# Building and witnessing an environment
|
||||
|
||||
An agent such as Claude Code builds a desktop app environment from a request
|
||||
on a [build computer](desktop-build-computer.md): it makes the seed through
|
||||
the app, makes the change the task asks for, exports the bundle with the task
|
||||
it writes, and then witnesses the task in a fresh computer of that bundle.
|
||||
REA runs its own capture, task draft and export inside the build computer and
|
||||
grades the witness with the same checks the exported verifier runs.
|
||||
|
||||
| Tool | What it does |
|
||||
| -------------------------- | ------------------------------------------------------------------------------------------- |
|
||||
| `start_build_capture` | Downloads the package lists as the computer's user and launches the app under REA's capture |
|
||||
| `mark_build_checkpoint` | Takes a named checkpoint and returns the capture's acknowledgement with its counts |
|
||||
| `stop_build_capture` | Stops the capture and returns its checkpoints and the task draft between `seed` and `end` |
|
||||
| `export_build_environment` | Exports the bundle with the agent's task inside the computer and copies it to a folder here |
|
||||
| `check_build_task` | Grades a task against the app's live state; with a `stage`, files the check in the witness |
|
||||
|
||||
Like the computer tools, these are MCP-only and act on a `computer_id` from
|
||||
`start_build_computer`.
|
||||
|
||||
## Build
|
||||
|
||||
1. `start_build_computer` from a request. REA is installed in the computer.
|
||||
2. `start_build_capture`. REA writes an apt configuration under
|
||||
`~/.rea-build/apt` and runs `apt-get update` as the computer's user, so the
|
||||
capture finds the package's sources (`package_lists` counts the lists,
|
||||
and `package_lists_error` gives apt's exit status and last lines when the
|
||||
download failed; a later start runs the update again until one succeeds). It then starts `rea capture-desktop-application
|
||||
--desktop-entry <entry>`
|
||||
in the computer, leaving `~/.rea-build` out of every checkpoint, and returns
|
||||
once the app's window is open, with a screenshot. For an app installed
|
||||
from a `.deb` file, the build layer keeps that file at
|
||||
`/opt/rea/package.deb` and the capture records it with `--package-file`. Do not open the app from
|
||||
the menu first: the capture refuses an app whose window is already open.
|
||||
3. Make the seed through the app with `act_on_build_computer`, saving it
|
||||
where the user keeps such files, such as `~/Documents`, then
|
||||
`mark_build_checkpoint` `seed`.
|
||||
4. Make the change the task will ask for, save it, and
|
||||
`mark_build_checkpoint` `end`.
|
||||
5. `stop_build_capture`. The app is closed and the capture finishes. The
|
||||
result lists the checkpoints with their counts, the package and the
|
||||
sources the capture found, its limitations and the draft: each file that
|
||||
changed between `seed` and `end` with its net change, and suggested
|
||||
checks with a placeholder prompt.
|
||||
6. Write the task: its name, the prompt in the user's own words, and checks
|
||||
that hold after the change and not on the seed.
|
||||
7. `export_build_environment` with the task and a new or empty `directory`.
|
||||
The export runs inside the computer with `--seed-checkpoint seed
|
||||
--end-checkpoint end`, so it seeds what was saved at `seed`, scrubs the
|
||||
computer's host name and user from it, and refuses a task whose checks
|
||||
all pass on the seed. A task larger than 96 KiB as REA passes it to the
|
||||
computer is refused before anything runs. A file moved to the desktop Trash is not seeded,
|
||||
and the export's limitations name what the Trash held at `seed`. For a
|
||||
`.deb` app that no apt source the capture found offers, the export takes
|
||||
the request's URL with `--package-url`; one apt offers installs from apt. REA lists the bundle's files with their sizes
|
||||
and SHA-256 in the computer, into a file beside the bundle that it reads
|
||||
through `data/get`, since a large bundle's list outgrows a command's
|
||||
output; it then brings the files back through the driver's
|
||||
`data/get` in answers of at most 8 MiB, and checks and writes each answer
|
||||
into the folder before asking for the next, so one answer at a time is
|
||||
held; a failure leaves no partial folder.
|
||||
8. `close_build_computer`.
|
||||
|
||||
A checkpoint name is lowercase letters, digits, dot, dash and underscore; the
|
||||
capture's own `baseline`, `stop`, `time-limit` and `exit` are refused, and so
|
||||
is a name already taken. The acknowledgement comes from the capture after it
|
||||
has walked the home and retained what changed: `entries` walked, `changes`
|
||||
since the checkpoint before, `retained_files` and `retained_bytes`.
|
||||
|
||||
Each step can be asked again after its answer was lost:
|
||||
|
||||
- A start whose answer is lost, or whose window never opened, stops the
|
||||
capture it may have left running. Until the computer confirms that stop,
|
||||
REA keeps the capture and the next `start_build_capture` retries the stop
|
||||
before starting another.
|
||||
- A checkpoint the capture took whose acknowledgement was lost comes back
|
||||
from the capture's own log when the same name is marked again.
|
||||
- A stop whose summary or draft was not read finishes when stop is asked
|
||||
again, and a finished stop repeats its result. The draft and the export
|
||||
trust the checkpoints the capture's own Evidence records.
|
||||
- A step that saved something, such as an export's bundle or the witness's
|
||||
record, returns it even when its call is cancelled or the computer closes
|
||||
as the save finishes. `start_build_capture` saves nothing, so a close or
|
||||
cancellation during its screenshot of the opened app cancels it.
|
||||
|
||||
The capture, the draft and the export run as the computer's user in its home,
|
||||
without any `AGENTENV_*` variable, so neither REA nor the app holds the
|
||||
computer's control token. REA sends each command through agentenv's
|
||||
`computer-exec/v1` route with that token, which stays in REA, and reads larger
|
||||
outputs, such as the draft, the export's Evidence and the bundle's file
|
||||
list, from files under `~/.rea-build` through `data/get`, since a command's
|
||||
output is capped at 1 MiB.
|
||||
|
||||
Calls on one computer run one at a time, in the order REA receives them:
|
||||
an action, a capture step, a check or a close waits until the call before
|
||||
it on that computer has answered, and sends progress notifications while
|
||||
it waits. So a check reads the app with no action half done, and a witness
|
||||
stage is admitted, read and filed before anything else on that computer
|
||||
runs. Calls on different computers run side by side. Closing a computer
|
||||
cancels the call in progress on it and refuses the calls still waiting.
|
||||
|
||||
## Witness
|
||||
|
||||
Start a fresh computer from the exported folder with `start_build_computer`
|
||||
and `{"kind": "bundle", "path": ...}`; agentenv boots the app with the seed.
|
||||
Then, with the task exactly as exported:
|
||||
|
||||
| `stage` | Before it | Its checks must |
|
||||
| ------------- | ----------------------------------------------------------------- | ---------------------------- |
|
||||
| `seed` | nothing; give `directory`, a new or empty folder for the record | fail at least once |
|
||||
| `after_edit` | the task done through the app from its prompt alone | all pass |
|
||||
| `near_miss` | a change that is almost right, such as a misspelled line | fail at least once |
|
||||
| `after_reset` | `act_on_build_computer` with `action: "reset"` as the last change | each come out as on the seed |
|
||||
|
||||
`check_build_task` reads the app's `data/get`, takes the views it returns
|
||||
beside the files and evaluates each check with REA's evaluator, the one the
|
||||
export validates tasks with and the exported verifier mirrors, one result per
|
||||
check. Without `stage` it only grades. The `seed` stage begins a recording
|
||||
of the display under REA's own request id, one per computer; a seed check
|
||||
that fails after it, such as on a failed read of the app's state or a lost
|
||||
reply to the recording's start, can be asked again, and asks for the
|
||||
recording under the same id, which the computer answers with the recording
|
||||
it already began. The stages come in order. The edit and the near miss each
|
||||
need an applied click, drag, scroll, type or key since the stage before, with
|
||||
no reset after the last of them, since a reset puts the seed back rather than
|
||||
changing the app; `after_reset` needs the reset as the last change, and a
|
||||
reset that leaves any check unlike on the seed fails the witness, its
|
||||
stage's `remediation` naming those checks. On a computer started from a
|
||||
bundle folder, the seed refuses a task whose `tasks/<name>.json` or verifier
|
||||
in the bundle differs from what the export writes for it. A reset
|
||||
relaunches the app, and REA waits until the computer is ready again before
|
||||
answering, sending progress notifications while it waits.
|
||||
|
||||
Only a check that fails on what the app's view shows counts as a failure. A
|
||||
check that fails because its file or table could not be read, such as an
|
||||
SQLite file locked past the reader's wait, or because what it looks for may
|
||||
lie in what its view cut, is `undecided` in its result: it shows nothing
|
||||
about the task. That covers a line `lines_present` misses in a text view cut
|
||||
at 5,000 lines, a row `row_present` misses in a table whose view keeps only
|
||||
its first rows, a `value_equals` pointer that names nothing in a truncated
|
||||
view or past a cut zip listing, and the absence checks and counts on a cut
|
||||
view. A check that passes on what the view kept stays decided, and so does a
|
||||
value found there that differs from the one expected. The result's `passed`
|
||||
and description are the exported verifier's, which has no undecided results.
|
||||
A stage that only undecided checks would settle, such as a near miss whose
|
||||
one failing check could not read its file, files nothing and is refused;
|
||||
check it again once the files read whole.
|
||||
|
||||
A stage whose checks do not show what it must ends the witness, and so does
|
||||
`after_reset`. REA then ends the recording, fetches its segments through
|
||||
`data/get`, each checked against the recording's manifest, and writes into
|
||||
`directory`:
|
||||
|
||||
- `recording/NNNN.mp4`, the recording's segments;
|
||||
- `witness.json`, REA Evidence (`rea.desktop-build-witness`) holding the
|
||||
verdict, the stage that failed if one did, the task's name and SHA-256,
|
||||
each stage's results with how many logged actions came before it, the
|
||||
computer's [action log](desktop-build-computer.md#act) as it keeps it, each
|
||||
action with its arguments, timing and outcome, with `actions_dropped`
|
||||
counting the earliest actions it no longer keeps, and the recording's
|
||||
segments with their sizes and SHA-256. The record's limitations say when
|
||||
the log dropped actions or cut arguments, which each entry names in
|
||||
`truncated_arguments`.
|
||||
|
||||
The stages' rules read the log's count of actions and the last applied
|
||||
action of each kind, which it keeps for the whole session, so they hold
|
||||
however many entries it dropped. The result returns the same record. When writing it fails, such as for a
|
||||
folder something else wrote into, the witness keeps its stages: check the
|
||||
stage that ended it again, with the same task, and REA writes the record
|
||||
without checking again. Once it is saved, the same check returns the saved
|
||||
record again, so a record saved as its call was cancelled is not lost. A
|
||||
computer is witnessed once; witness again from a fresh computer.
|
||||
|
||||
See the [specification](specs/desktop-app-environments/build-witness/spec.md)
|
||||
for scope and verification.
|
||||
+1
-7
@@ -38,12 +38,6 @@ were examined; an unavailable section name does not erase those note facts.
|
||||
For GNU notes, `gnu.build_id_hex`, `gnu.abi_tag`, or `gnu.gold_version` are
|
||||
filled when the descriptor matches that type. Other owners keep `gnu` null.
|
||||
|
||||
For Linux `CORE` notes, `core.prpsinfo`, `core.file`, `core.siginfo`, or
|
||||
`core.auxv` are decoded for process arguments and status (`NT_PRPSINFO`), mapped
|
||||
file memory regions and paths (`NT_FILE`), fault signal and code (`NT_SIGINFO`), and
|
||||
auxiliary vectors (`NT_AUXV`) across 32-bit and 64-bit images. Other owners or unparsed
|
||||
notes keep `core` null.
|
||||
|
||||
`PT_NOTE` and `SHT_NOTE` are examined independently. The same file range may
|
||||
appear twice when both tables declare it.
|
||||
|
||||
@@ -54,7 +48,7 @@ the image never carried notes in another container or overlay.
|
||||
|
||||
The selected file is limited to 64 MiB by default (512 MiB maximum). At most
|
||||
32 note entries are examined by default across every table. Retained note
|
||||
names, section names, GNU descriptor text, and core dump string representations share a scan budget based on
|
||||
names, section names, and GNU descriptor text share a scan budget based on
|
||||
the source bytes plus any decoded section-name table, capped at
|
||||
`max_file_bytes`. The raw source and decoded table must also fit together
|
||||
within that ceiling. If admission fails, the error reports the required
|
||||
|
||||
@@ -13,19 +13,6 @@ On Linux, Binwalk and Unblob run as REA-owned processes and require a
|
||||
procps-compatible `ps` on REA's `PATH`; see the shared
|
||||
[Linux process prerequisite](installation.md#linux-owned-process-prerequisite).
|
||||
|
||||
## Compare stored firmware components
|
||||
|
||||
For supported FIT and legacy uImage containers, use the built-in
|
||||
`inspect-artifact` / `inspect_artifact` inventory and shared
|
||||
`compare-artifacts` / `compare_artifacts` workflow. This route needs no Binwalk,
|
||||
Unblob or extraction: exact logical paths, stored-byte hashes and declared
|
||||
metadata establish component changes, with source Evidence and explicit unknowns.
|
||||
The outer container at `.` is separate from its children. See the
|
||||
[FIT version comparison recipe](fit-images.md#comparison) for CLI,
|
||||
MCP, summary delivery and selected supporting facts. Its real-data verification
|
||||
covers a pair of OpenWrt FIT releases; it does not establish comparison of
|
||||
decoded root filesystems or arbitrary firmware formats.
|
||||
|
||||
## Caller-supplied tools
|
||||
|
||||
Provide existing tools using absolute executable paths, scoped to your shell:
|
||||
@@ -258,12 +245,9 @@ tree semantics, vulnerability scanning, emulation or hardware runtime capture.
|
||||
3, allowed range 1–10.
|
||||
|
||||
Staging budgets are polled and rechecked after exit; short-lived overshoot is
|
||||
possible. The per-file process limit is one byte above the larger of the selected
|
||||
payload budget and the fixed report/diagnostic file allowances, so a small payload
|
||||
selection still permits the mandatory report, and a staged file the kernel stops
|
||||
at the limit exceeds its budget instead of passing as complete. A tighter
|
||||
inherited host limit still applies and can remove that byte, so a staged file
|
||||
that reaches the installed limit is rejected as possibly truncated.
|
||||
possible. The per-file process limit is the larger of the selected payload budget
|
||||
and the fixed report/diagnostic file allowances, so a small payload selection
|
||||
still permits the mandatory report. A tighter inherited host limit still applies.
|
||||
The inherited file-size limit bounds each file, not total storage;
|
||||
address-space limits are per process, not an aggregate memory quota. Worker
|
||||
count is not CPU affinity for every external extractor. These limits do not
|
||||
|
||||
+4
-125
@@ -149,116 +149,10 @@ After extraction:
|
||||
|
||||
## Comparison
|
||||
|
||||
Use the shared `compare-artifacts` / `compare_artifacts` workflow for exact
|
||||
component paths and stored content identities. Neither extraction nor an external
|
||||
firmware engine is required. This compares the bytes exposed by this FIT reader;
|
||||
it does not inspect files inside a compressed root filesystem, match renamed
|
||||
components, or establish runtime equivalence.
|
||||
|
||||
### CLI
|
||||
|
||||
Retain each complete inspection while returning a summary:
|
||||
|
||||
```sh
|
||||
rea inspect-artifact /absolute/baseline.itb --detail summary \
|
||||
--evidence_output /absolute/baseline-evidence.json --json
|
||||
rea inspect-artifact /absolute/candidate.itb --detail summary \
|
||||
--evidence_output /absolute/candidate-evidence.json --json
|
||||
```
|
||||
|
||||
The CLI returns a `{summary, evidence_bundle}` receipt. Use each response's
|
||||
`summary.normalized_result.parent_evidence_id` as the corresponding
|
||||
`evidence_id` below. Save this request as `comparison.json`, replacing the paths
|
||||
and IDs with the returned values, then run `rea compare-artifacts comparison.json
|
||||
--json`. Evidence-file paths must be absolute. Naming the exact complete parent
|
||||
avoids ambiguity when a bundle contains several inspection or inventory records.
|
||||
|
||||
```json
|
||||
{
|
||||
"left": {
|
||||
"kind": "evidence-file",
|
||||
"path": "/absolute/baseline-evidence.json",
|
||||
"evidence_id": "COMPLETE_BASELINE_EVIDENCE_ID"
|
||||
},
|
||||
"right": {
|
||||
"kind": "evidence-file",
|
||||
"path": "/absolute/candidate-evidence.json",
|
||||
"evidence_id": "COMPLETE_CANDIDATE_EVIDENCE_ID"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### MCP
|
||||
|
||||
For each file, call `open_binary` with its path, then `inspect_artifact` with
|
||||
`{"detail":"summary"}`. Opening the second file retains the first inspection's
|
||||
Evidence. Compare the two complete parent IDs without resending their graphs:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "compare_artifacts",
|
||||
"arguments": {
|
||||
"left": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "COMPLETE_BASELINE_EVIDENCE_ID"
|
||||
},
|
||||
"right": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "COMPLETE_CANDIDATE_EVIDENCE_ID"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The result counts unchanged, added, removed, changed, unknown and contradictory
|
||||
paths, returning every delta inline with source Evidence links. The root `.` is
|
||||
the outer FIT and is counted separately from `images/<unit-name>.bin`. A changed
|
||||
outer hash therefore does not imply every component changed. Additions and
|
||||
removals require complete container-entry coverage; ambiguous duplicate paths or
|
||||
missing observations cannot prove absence or equivalence.
|
||||
|
||||
Artifact comparison includes an image's payload interpretation, including load
|
||||
and entry declarations. Equivalent external offset forms and inventory positions
|
||||
leave those declarations unchanged. Unavailable interpretations remain unknown,
|
||||
while independently known changes survive; a changed row with an `availability`
|
||||
dimension does not establish the unavailable field's value.
|
||||
|
||||
For supporting facts, batch selected paths rather than requesting the entire
|
||||
inspection again:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_analysis_view",
|
||||
"arguments": {
|
||||
"source": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "COMPLETE_BASELINE_EVIDENCE_ID"
|
||||
},
|
||||
"view": {
|
||||
"kind": "inventory",
|
||||
"collection": "occurrences",
|
||||
"logical_paths": ["images/kernel-1.bin", "images/fdt-1.bin"],
|
||||
"fields": [
|
||||
"artifact_id",
|
||||
"logical_path",
|
||||
"hash_status",
|
||||
"source_location",
|
||||
"source_path"
|
||||
],
|
||||
"offset": 0,
|
||||
"limit": 64
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Read `collection: "nodes"` with those `artifact_ids` and `fields: ["sha256",
|
||||
"size"]` to obtain content facts. Join by `artifact_id`, not array position.
|
||||
Continue using the returned page metadata when necessary. Source locations are
|
||||
ranges in the original FIT, not decoded filesystem offsets. Complete Evidence
|
||||
remains available through the retained parent and `export_evidence_bundle`.
|
||||
Summary and field selection reduce delivery only; they do not skip inventory
|
||||
hashing or change coverage.
|
||||
Artifact comparison reports changes to an image's payload interpretation,
|
||||
including load and entry declarations. Equivalent external offset forms and
|
||||
inventory positions leave those declarations unchanged. Unavailable
|
||||
interpretations remain unknown, while independently known changes survive.
|
||||
|
||||
## Verification
|
||||
|
||||
@@ -276,18 +170,3 @@ No image was executed.
|
||||
For small containers, complete delivery avoids an additional view request.
|
||||
Choose summary and selected views when the omitted facts justify that extra
|
||||
request; summary delivery does not reduce the underlying inventory work.
|
||||
|
||||
The component comparison workflow was additionally checked on the official
|
||||
[OpenWrt 24.10.0](https://downloads.openwrt.org/releases/24.10.0/targets/mediatek/filogic/)
|
||||
and [24.10.1](https://downloads.openwrt.org/releases/24.10.1/targets/mediatek/filogic/)
|
||||
`abt_asr3000-initramfs-recovery.itb` images (8,519,680 and 8,585,216 bytes).
|
||||
Their SHA-256 values are
|
||||
`083b174e81ed373f2d2f2ea5366f9291767d95375383528fd6b9955ad815b85a` and
|
||||
`41129cce68ad6269e5c8c48ef110f97682d6c4202ff2198bfd0d300c3dac132a`.
|
||||
An independent FDT byte-range/hash oracle found changed kernel and initrd bytes
|
||||
and an identical DTB; REA reported those two changed components, the changed
|
||||
outer `.` and one unchanged component. A controlled copy changing only the root
|
||||
timestamp reported only `.` changed and all three components unchanged. Public
|
||||
CLI comparison of the exported source Evidence matched the MCP Evidence exactly;
|
||||
summary and selected inventory views matched the independent ranges and hashes.
|
||||
These checks did not decompress either initrd or execute the firmware.
|
||||
+2
-3
@@ -175,9 +175,8 @@ are separate from the initialized MZ module. Headerless COM requires the
|
||||
from the filename or unrecognized bytes. DOS extenders without an admitted
|
||||
MZ real-mode entry remain outside this import boundary.
|
||||
|
||||
DOS MZ is available through Ghidra, with no Hopper support claim. Windows Ghidra
|
||||
P0 admits DOS MZ read-only: function annotations and naming edits are refused
|
||||
there, and explicit COM is not admitted. macOS x64 DOS verification remains unverified.
|
||||
DOS MZ is available through Ghidra, with no Hopper support claim. It is outside
|
||||
the Windows native PE P0 boundary. macOS x64 DOS verification remains unverified.
|
||||
|
||||
## Real verification
|
||||
|
||||
|
||||
+4
-15
@@ -2,18 +2,9 @@
|
||||
|
||||
REA imports one target into an ephemeral Ghidra database. Linux/macOS support
|
||||
read-only analysis and atomic function-name/entry-comment edits; Windows P0 is
|
||||
read-only. GUI controls and writes to caller-selected source databases are unavailable.
|
||||
read-only. GUI controls and persistent database mutation are unavailable.
|
||||
Executable bytes remain unchanged.
|
||||
|
||||
An opt-in [owned Program checkpoint](installation.md#automatically-checkpoint-an-owned-ghidra-program)
|
||||
retains analysis across Linux/macOS processes. It saves initial completed analysis
|
||||
and normal-close metadata changes to `REA_GHIDRA_CHECKPOINT_DIR`; cancellation
|
||||
and process failure recover only the last successfully saved state. The restored
|
||||
packed Program's digest is reported with restored observations. Initial analysis
|
||||
keeps one stable generation through its first save; later saved states bind their
|
||||
content digest in profile identity. Restoration does not turn prior
|
||||
database state into a fresh discovery. The default remains ephemeral.
|
||||
|
||||
Use [installation](installation.md#ghidra) for supported hosts, prerequisites,
|
||||
configuration, startup deadlines, resource controls, and cleanup recovery.
|
||||
[Linux Ghidra launches](installation.md#linux-owned-process-prerequisite) also
|
||||
@@ -339,11 +330,9 @@ Without bounds, loaded memory blocks are eligible, in Ghidra address order.
|
||||
Blocks that are not loaded (file parts such as `.symtab`) and overlay blocks are
|
||||
listed in `searched.skipped_not_loaded` and are not searched; pass `start` or
|
||||
`end` in their address space to search them.
|
||||
Overlapping matches are included, including across adjacent eligible initialized
|
||||
blocks in the same address space. Gaps and excluded or uninitialized blocks break
|
||||
a search run. Each match reports its address, starting segment, unique source file
|
||||
offset for that starting address (or null), and containing procedure (or null).
|
||||
A starting offset does not imply the full match is contiguous in the file.
|
||||
Overlapping matches are included; matches never cross block boundaries.
|
||||
Each match reports its address, segment, unique source file offset (or null),
|
||||
and containing procedure (or null).
|
||||
|
||||
Pages default to 100 matches and accept 1 to 1000. Pass `next_cursor` as `cursor`
|
||||
with the same target, profile, pattern, mask and bounds to continue. `complete`
|
||||
|
||||
+2
-9
@@ -93,15 +93,8 @@ The maintained PSP fixture builds from `tests/conformance/psp/`, never executes,
|
||||
and is independent of commercial game data. PSP GNU tools supply ELF/ABI,
|
||||
raw ABI-record, symbol and encoding observations; REA's parsed target and saved
|
||||
snapshot must agree with the independent ABI declaration. Public CLI/MCP must
|
||||
recover known functions, a direct call, the immediate `0x1234`, BITREV, a
|
||||
global word and a marker. The additional source-owned VFPU function contains
|
||||
`vpfxs`, `vpfxt`, `vpfxd` and three `vadd.q` instructions.
|
||||
The PSP lane checks their fixed encoded bytes and Ghidra's decoded mnemonics
|
||||
through `inspect_native_instruction`; an analyzed dossier reports available
|
||||
high-p-code opaque-operation and truncation observations. Neither decoded
|
||||
instructions nor `CALLOTHER` counts establish prefix consumption, lane-level
|
||||
arithmetic or emulator/hardware equivalence.
|
||||
The verifier
|
||||
recover known functions, a direct
|
||||
call, the immediate `0x1234`, BITREV, a global word and a marker. The verifier
|
||||
checks observed loader/language, original bytes, Evidence, snapshot reopening,
|
||||
and an A-B-A target switch with different global contents at the same address.
|
||||
A snapshot for A must not open B. Cleanup must finish before success is reported.
|
||||
|
||||
@@ -25,33 +25,6 @@ by default; `--integrity-policy record-and-continue` retains the observed bytes
|
||||
and an explicit untrusted contradiction. An all-zero MD5 is an unknown claim,
|
||||
not a verified digest.
|
||||
|
||||
Artifact comparison pairs members by `logical_path`, the stored path without
|
||||
`res://`, and compares their bytes and occurrence facts. It does not compare
|
||||
`godot-pck-entry` metadata. Godot 4.3
|
||||
[stores `res://` paths](https://github.com/godotengine/godot/blob/77dcf97d82cbfe4e4615475fa52ca03da645dbd8/editor/export/editor_export_platform.cpp#L228)
|
||||
and [keys members by them](https://github.com/godotengine/godot/blob/77dcf97d82cbfe4e4615475fa52ca03da645dbd8/core/io/file_access_pack.cpp#L50-L52).
|
||||
Godot 4.4.1
|
||||
[stores paths without the prefix](https://github.com/godotengine/godot/blob/49a5bc7b616bd04689a2c89e89bda41f50241464/editor/export/editor_export_platform.cpp#L257-L260)
|
||||
and [removes it before keying](https://github.com/godotengine/godot/blob/49a5bc7b616bd04689a2c89e89bda41f50241464/core/io/file_access_pack.cpp#L48-L50).
|
||||
Each version finds `res://scenes/main.gd` in its own export, but comparing the
|
||||
stored `source_path` would mark every member changed between such exports.
|
||||
`logical_path` names the member Godot's lookup finds, except in two cases.
|
||||
REA normalizes Unicode to NFC, so a member renamed only between Unicode forms
|
||||
compares unchanged, although Godot treats it as another path. Godot reads `\`
|
||||
as `/` and REA keeps it literal, so a member spelled with backslashes compares
|
||||
as removed and added. Directory order and offsets are layout. The encryption
|
||||
bit, the only member flag REA admits, is the occurrence's `encrypted` field. A
|
||||
member whose encryption changes compares `unknown` in `metadata` and
|
||||
`availability`: the change is known, but REA does not read encrypted bytes,
|
||||
so whether the script changed is unknown. Godot's exporter
|
||||
[stores the MD5 of a member's original bytes](https://github.com/godotengine/godot/blob/a13da4feb8d8aefc283c3763d33a2f170a18d541/editor/export/editor_export_platform.cpp#L465-L468),
|
||||
as the 4.3 and 4.4.1 exporters do, so the declared MD5 does not depend on the
|
||||
encryption IV. Encrypted members whose declared MD5s differ therefore also
|
||||
compare `unknown` in `metadata` and `availability`: the MD5s declare
|
||||
different scripts, but REA cannot verify them, and equal MD5s add nothing for
|
||||
the same reason. An encrypted member with an MD5 on only one side stays
|
||||
`unknown`.
|
||||
|
||||
## CLI
|
||||
|
||||
Start with a compact summary and keep the complete Evidence bundle:
|
||||
|
||||
@@ -36,8 +36,6 @@ The MCP tool `project_harmony_application_graph` takes the same
|
||||
|
||||
- exact component paths, artifact IDs, and hashes (manifests, Ark bytecode,
|
||||
resources, native libraries, JavaScript, signing)
|
||||
- `unclassified_components` with the same paths and content identities for
|
||||
authenticated files outside those categories and the App Pack child list
|
||||
- a packaging-model hint: Stage is claimed only from an exactly cased `module.json` entry
|
||||
outside a classified resource path, FA only from `config.json` outside a
|
||||
classified resource path, otherwise `unknown`
|
||||
@@ -57,24 +55,6 @@ being classified as code files. Ark and JavaScript runtime hints summarize the
|
||||
classified bytecode and JavaScript lists; supported source extensions share the
|
||||
same classification rule as their component rows.
|
||||
|
||||
`unclassified_components` preserves files such as `pkgContextInfo.json`,
|
||||
`.pages.info`, a HarmonyOS `.har` shared library, and a non-certificate
|
||||
`META-INF` entry without interpreting their contents. The `.har` suffix also
|
||||
names HTTP Archive JSON, so identifying one needs verified ZIP bytes rather than
|
||||
a path rule. Different paths sharing the same bytes remain separate entries. App
|
||||
Pack children of an App Pack root are already reported in `app_pack_children`,
|
||||
so they are not repeated as unclassified files. A nested `.hap` or `.hsp` member
|
||||
of a HAP or HSP root is disclosed in `unclassified_components` instead, because
|
||||
child-package inventory is not recursive. Unreadable or encrypted content remains
|
||||
in `unavailable_components` with unknown identity instead.
|
||||
|
||||
`coverage.status: "complete-within-inventory"` describes the authenticated
|
||||
source inventory and bounded bridge projection, not semantic understanding of
|
||||
every file. Unclassified files do not make a complete inventory partial; missing
|
||||
inventory pages, source coverage limits, unavailable content and bridge limits
|
||||
still qualify coverage independently. An empty unclassified list on a partial
|
||||
inventory does not establish that the whole package has no unclassified files.
|
||||
|
||||
Every claim is path-presence only. Manifest semantics (module identity,
|
||||
abilities, entry points, permissions, dependencies), Ark bytecode contents and
|
||||
versions, resource resolution, signing verification, and observed native
|
||||
@@ -82,12 +62,6 @@ boundaries are out of scope for this increment and stay in the projection's
|
||||
`limitations` array until a dedicated HarmonyOS provider exists. REA fails
|
||||
closed on packages whose inventory cannot be authenticated.
|
||||
|
||||
`pkgContextInfo.json` is inventoried and listed as an unclassified component but
|
||||
never read, so its `isSO` declarations do not narrow `bridge_candidates`. When
|
||||
that entry is present the projection adds a limitation naming the unread
|
||||
declaration, so a path-convention hypothesis is not mistaken for a package
|
||||
declaration.
|
||||
|
||||
## Verification lane
|
||||
|
||||
`npm run fixtures:harmony` downloads the SHA-256-pinned 1.0.1 HAP release
|
||||
|
||||
+1
-4
@@ -36,10 +36,7 @@ regions and are never filled.
|
||||
|
||||
Region `format_metadata` (`hex-record-region`) records `start_address`,
|
||||
`end_address`, `length`, `record_count`, and `first_line`/`last_line`: the
|
||||
1-based line span of the contributing data records. `compare_artifacts`
|
||||
compares none of these fields: a region's name encodes its start and end
|
||||
addresses, its length follows from them, and record and line counts are
|
||||
layout.
|
||||
1-based line span of the contributing data records.
|
||||
|
||||
Root `format_metadata` (`hex-records`) records:
|
||||
|
||||
|
||||
@@ -51,19 +51,13 @@ was established; failed registrations remain unavailable. Correct
|
||||
launch environment. Restart/reconnect that server after changing its environment;
|
||||
an export in another shell does not update a running server.
|
||||
|
||||
An HTTP registration's `url` must use `http` or `https` without URL credentials,
|
||||
and its host must be `localhost`, `127.0.0.1`, `[::1]`, or the Docker/Podman
|
||||
host-gateway name `host.docker.internal` or `host.containers.internal`. Use a
|
||||
gateway name when REA runs in a container and IDA MCP listens on the container
|
||||
host. REA refuses every other host.
|
||||
|
||||
Open the **original input binary** in REA, not an `.idb` or `.i64` database. Its SHA-256 must match IDA's recorded input hash. REA checks the attached identity and image base around each analysis request. A mismatch or target switch fails with recovery guidance. The original input must remain readable by REA; WSL can use its mounted alias while the GUI runs on Windows. Attached profiles, including native mode, refuse universal Mach-O inputs at admission: every slice shares the recorded whole-file hash, so REA cannot verify which slice the GUI loaded. Open a thin file extracted with `lipo -thin` in both IDA and REA, or use headless mode, which analyzes the selected slice.
|
||||
|
||||
REA's close operation releases its MCP connection or proxy. It never saves or closes the existing GUI database. External edits remain visible because results are live and are not replayed from snapshots. These checks do not lock the GUI or prove that the database is unpatched.
|
||||
|
||||
## Optional native IDA MCP bridge
|
||||
|
||||
IDA Pro and IDA Free can use the separate [ida-free-mcp native plugin](https://github.com/0xshlomil/ida-free-mcp) through an explicit attached registration. Install and start that plugin using its upstream instructions, open the original binary in the IDA GUI, and wait for analysis. The native MCP 1.1.2 release binds `0.0.0.0` without a listen-address setting. Restrict inbound access with the host firewall or use an upstream build that binds only to loopback before starting it. REA's local URL restriction does not change the plugin's listener. REA does not install IDA, build the plugin, activate a license, or launch the GUI.
|
||||
IDA Pro and IDA Free can use the separate [ida-free-mcp native plugin](https://github.com/0xshlomil/ida-free-mcp) through an explicit attached registration. Install and start that plugin using its upstream instructions, open the original binary in the IDA GUI, and wait for analysis. The native MCP 1.1.2 release binds `0.0.0.0` without a listen-address setting. Restrict inbound access with the host firewall or use an upstream build that binds only to loopback before starting it. REA's loopback URL restriction does not change the plugin's listener. REA does not install IDA, build the plugin, activate a license, or launch the GUI.
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
+13
-19
@@ -47,25 +47,19 @@ For runtime instrumentation, see the [Frida guide](./frida-instrumentation.md).
|
||||
|
||||
## Choose a guide
|
||||
|
||||
| Task | Guides |
|
||||
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Identify a desktop installation | [Runtime candidates, evidence and follow-up files](desktop-applications.md) |
|
||||
| Inspect native functions, references, or types | [Native binaries](native-investigation.md), [Go metadata](go-binaries.md), [Rust dependencies](rust-binaries.md), [CPython pyc files](python-pyc.md), [PE certificates](pe-certificates.md), [PE imports](pe-imports.md), [PE TLS/load config](pe-load-config.md), [ELF notes](elf-notes.md), [Binary hardening](binary-hardening.md), [Linux kernel modules](kernel-modules.md), [Apple IPS](apple-ips.md), [Erlang BEAM](erlang-beam.md), [offline ELF layout](binary-diagnostics.md), [recorded crashes](recorded-crashes.md) |
|
||||
| Select a native provider or import format | [Ghidra semantics](ghidra-provider.md), [Windows](windows-ghidra-p0.md), [DOS](ghidra-dos.md), [raw bytes](raw-binary.md), [GameCube DOL](ghidra-dol.md), [MIPS](ghidra-mips.md), [PSP](ghidra-psp.md), [NativeAOT](ghidra-nativeaot.md), [IDA](ida-provider.md), [Kuna](kuna-provider.md), [JEB](jeb-analysis.md) |
|
||||
| Analyze JavaScript or Electron artifacts | [Static analysis](javascript-artifact-reconstruction.md), [tracing and comparisons](javascript-application-workflows.md), [application graph](javascript-application-graph.md), [source recovery](javascript-recovery.md) |
|
||||
| Compare static JavaScript with runtime observations | [Runtime attribution](javascript-runtime-observation.md), [reconciliation](javascript-runtime-reconciliation.md), [Electron observation](electron-observation.md), [Electron fuses](electron-fuses.md) |
|
||||
| Inspect assemblies, packages, or firmware | [Godot PCK](godot-pck.md), [UnityFS](unityfs.md), [wxapkg](wxapkg.md), [U-Boot uImage](uimage.md), [.NET](managed-code-analysis.md), [Python packages](python-packaged-applications.md), [Android](android-analysis.md), [APK signatures](apk-signature.md), [Apktool resources](apktool-resource-analysis.md), [ADB acquisition](adb-device-analysis.md), [Apple](apple-application-analysis.md), [Harmony](harmony-analysis.md), [firmware](firmware-analysis.md), [ESP images](esp-image.md), [ESP flash images](esp-flash-image.md), [WASM](wasm-artifacts.md), [EVM](evm-bytecode.md) |
|
||||
| Observe or compare a browser scenario | [CDP observation](browser-observation.md), [scenarios](browser-scenario-contract.md), [runtime evidence](web-runtime.md), [historical captures](web-network-captures.md) |
|
||||
| Serve recorded behavior as an agent environment | [Agent environments](agent-environments.md) |
|
||||
| Trace captured website source | [Source locations](web-source-location.md), [module traces](website-module-trace.md), [script export](website-script-export.md) |
|
||||
| Capture and compare a command | [Process capture](process-capture.md) |
|
||||
| Capture a Linux desktop application in use | [Desktop application capture](desktop-application-capture.md) |
|
||||
| Read a captured application's state | [Desktop application state](desktop-application-state.md) |
|
||||
| Export a captured application as an environment | [Desktop application export](desktop-application-export.md) |
|
||||
| Write and validate a task for an exported app | [Desktop application tasks](desktop-application-tasks.md) |
|
||||
| Drive a computer while building an environment | [Desktop build computer](desktop-build-computer.md) |
|
||||
| Capture, export and witness a built environment | [Building and witnessing](desktop-build-witness.md) |
|
||||
| Use CLI/MCP and retain evidence | [CLI](cli.md), [MCP contracts](mcp-contracts.md), [agent prompts](mcp-prompts.md), [obligation ledgers](reconstruction-obligation-ledgers.md) |
|
||||
| Task | Guides |
|
||||
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Identify a desktop installation | [Runtime candidates, evidence and follow-up files](desktop-applications.md) |
|
||||
| Inspect native functions, references, or types | [Native binaries](native-investigation.md), [Go metadata](go-binaries.md), [Rust dependencies](rust-binaries.md), [CPython pyc files](python-pyc.md), [PE certificates](pe-certificates.md), [PE imports](pe-imports.md), [PE TLS/load config](pe-load-config.md), [ELF notes](elf-notes.md), [Linux kernel modules](kernel-modules.md), [Apple IPS](apple-ips.md), [Erlang BEAM](erlang-beam.md), [offline ELF layout](binary-diagnostics.md), [recorded crashes](recorded-crashes.md) |
|
||||
| Select a native provider or import format | [Ghidra semantics](ghidra-provider.md), [Windows](windows-ghidra-p0.md), [DOS](ghidra-dos.md), [raw bytes](raw-binary.md), [GameCube DOL](ghidra-dol.md), [MIPS](ghidra-mips.md), [PSP](ghidra-psp.md), [NativeAOT](ghidra-nativeaot.md), [IDA](ida-provider.md), [Kuna](kuna-provider.md), [JEB](jeb-analysis.md) |
|
||||
| Analyze JavaScript or Electron artifacts | [Static analysis](javascript-artifact-reconstruction.md), [tracing and comparisons](javascript-application-workflows.md), [application graph](javascript-application-graph.md), [source recovery](javascript-recovery.md) |
|
||||
| Compare static JavaScript with runtime observations | [Runtime attribution](javascript-runtime-observation.md), [reconciliation](javascript-runtime-reconciliation.md), [Electron observation](electron-observation.md), [Electron fuses](electron-fuses.md) |
|
||||
| Inspect assemblies, packages, or firmware | [Godot PCK](godot-pck.md), [UnityFS](unityfs.md), [U-Boot uImage](uimage.md), [.NET](managed-code-analysis.md), [Python packages](python-packaged-applications.md), [Android](android-analysis.md), [APK signatures](apk-signature.md), [Apktool resources](apktool-resource-analysis.md), [ADB acquisition](adb-device-analysis.md), [Apple](apple-application-analysis.md), [Harmony](harmony-analysis.md), [firmware](firmware-analysis.md), [ESP images](esp-image.md), [ESP flash images](esp-flash-image.md), [WASM](wasm-artifacts.md), [EVM](evm-bytecode.md) |
|
||||
| Observe or compare a browser scenario | [CDP observation](browser-observation.md), [scenarios](browser-scenario-contract.md), [runtime evidence](web-runtime.md), [historical captures](web-network-captures.md) |
|
||||
| Serve recorded behavior as an agent environment | [Agent environments](agent-environments.md) |
|
||||
| Trace captured website source | [Source locations](web-source-location.md), [module traces](website-module-trace.md), [script export](website-script-export.md) |
|
||||
| Capture and compare a command | [Process capture](process-capture.md) |
|
||||
| Use CLI/MCP and retain evidence | [CLI](cli.md), [MCP contracts](mcp-contracts.md), [agent prompts](mcp-prompts.md), [obligation ledgers](reconstruction-obligation-ledgers.md) |
|
||||
|
||||
For development, use [testing](testing.md), [client verification](client-verification.md),
|
||||
[tool design](tool-design.md), [admission limits and recovery](design/limits-and-recovery.md),
|
||||
|
||||
+10
-125
@@ -378,12 +378,12 @@ a particular caller-supplied input will be valid. See
|
||||
|
||||
Select the part you want to check:
|
||||
|
||||
| Task | Readiness check |
|
||||
| ----------------------------------- | -------------------------------------------------------------------------- |
|
||||
| Static JavaScript/Electron analysis | Run `rea analyze-javascript-application PATH --json` directly. |
|
||||
| One analysis engine | `rea doctor --provider ghidra --json` (or `hopper`, `ida`, `jadx`, `kuna`) |
|
||||
| One agent registration | `rea doctor --client codex --json` (see [client IDs](#supported-agents)) |
|
||||
| Installed workflow instructions | `rea doctor --skill --json` |
|
||||
| Task | Readiness check |
|
||||
| ----------------------------------- | ------------------------------------------------------------------------ |
|
||||
| Static JavaScript/Electron analysis | Run `rea analyze-javascript-application PATH --json` directly. |
|
||||
| One analysis engine | `rea doctor --provider ghidra --json` (or `hopper`, `ida`, `kuna`) |
|
||||
| One agent registration | `rea doctor --client codex --json` (see [client IDs](#supported-agents)) |
|
||||
| Installed workflow instructions | `rea doctor --skill --json` |
|
||||
|
||||
A scoped report has `scope.mode: "explicit"`. Its `scope_checks` determine
|
||||
`healthy` and the exit status. Other checks appear in `informational_checks`;
|
||||
@@ -636,7 +636,6 @@ hosted connector; see [its configuration](client-configuration.md#other-client-s
|
||||
| Claude Desktop | `claude_desktop` |
|
||||
| Codex | `codex` |
|
||||
| Cursor | `cursor` |
|
||||
| CodeBuddy | `codebuddy` |
|
||||
| Gemini CLI | `gemini_cli` |
|
||||
| Windsurf | `windsurf` |
|
||||
| Devin | `devin` |
|
||||
@@ -655,7 +654,6 @@ hosted connector; see [its configuration](client-configuration.md#other-client-s
|
||||
| Factory Droid | `droid` |
|
||||
| Trae | `trae` |
|
||||
| WorkBuddy | `workbuddy` |
|
||||
| LM Studio | `lm_studio` |
|
||||
| ZCode | `zcode` |
|
||||
| DeepSeek Harness | `dsh` |
|
||||
| FreeBuff | `freebuff` |
|
||||
@@ -665,8 +663,6 @@ hosted connector; see [its configuration](client-configuration.md#other-client-s
|
||||
`freebuff` requires `[email protected]` or later. Older releases reject
|
||||
`rea setup --client freebuff` as an unsupported client.
|
||||
|
||||
`lm_studio` requires a release after 6.8.0.
|
||||
|
||||
See [client configuration and troubleshooting](client-configuration.md) for
|
||||
configuration paths, profiles, trust policies, and schema-limit workarounds.
|
||||
|
||||
@@ -674,12 +670,6 @@ configuration paths, profiles, trust policies, and schema-limit workarounds.
|
||||
|
||||
See [the Cordis patch and launcher setup](client-configuration.md#deepseek-harness).
|
||||
|
||||
### llama.cpp
|
||||
|
||||
llama-server can launch REA through its native stdio MCP configuration. This is
|
||||
a manual connection, not an automatic setup client ID. See the
|
||||
[local model setup](client-configuration.md#llamacpp).
|
||||
|
||||
### Other client settings
|
||||
|
||||
See [OpenCode, Grok Build, and Grok Bot settings](client-configuration.md#other-client-settings).
|
||||
@@ -727,11 +717,6 @@ and status when publisher verification cannot download the current package.
|
||||
Hopper metadata and package downloads require HTTPS throughout the redirect
|
||||
chain; a redirect to HTTP or to a URL carrying credentials is refused before
|
||||
that destination is requested, and those credentials are not reported.
|
||||
A package download fails when no bytes arrive for 30 seconds or when it has not
|
||||
finished within 15 minutes, so a slow connection that keeps delivering bytes can
|
||||
complete. Release metadata and the publisher key keep a 10-second limit. A
|
||||
`timeout` failure reports the `limit` it reached (`stalled` or `deadline`), its
|
||||
`timeoutMs` and the `receivedBytes` before it stopped.
|
||||
|
||||
On macOS, approved setup downloads the official DMG, checks its published size
|
||||
and digest, validates the application bundle, and atomically installs it to
|
||||
@@ -1149,8 +1134,7 @@ and `set_addresses_names` name a function entry or any other mapped address
|
||||
session metadata edits leave executable bytes unchanged and are discarded on close. GUI controls
|
||||
require Hopper; Windows P0 remains read-only.
|
||||
|
||||
Windows P0 admits native x86, x86-64, ARMNT (Thumb-2) and ARM64 PE applications and DLLs,
|
||||
[DOS MZ executables](ghidra-dos.md), plus explicitly
|
||||
Windows P0 admits native x86, x86-64, ARMNT (Thumb-2) and ARM64 PE applications and DLLs, plus explicitly
|
||||
selected [raw NMOS 6502 images](raw-binary.md#nmos-6502) with a caller-supplied
|
||||
entry at file offset 0, on fixed local NTFS volumes. Other raw CPU families remain
|
||||
unsupported on Windows.
|
||||
@@ -1176,11 +1160,7 @@ selected formula's `libexec/openjdk.jdk/Contents/Home`, for example
|
||||
Java version bounds declared by your Ghidra installation; REA does not select
|
||||
another JDK when `JAVA_HOME` is explicit or the PATH JVM runs. On macOS,
|
||||
when the PATH launcher cannot start a JVM, it tries executable Homebrew
|
||||
`opt/openjdk` and installed `opt/openjdk@<major>` candidates, preferring the host
|
||||
architecture's prefix and the unversioned formula within each prefix. Versioned
|
||||
aliases are considered in ascending major order within Ghidra's declared Java
|
||||
bounds; the selected executable still undergoes the normal Java probe and
|
||||
admission checks. REA does not invoke Homebrew or install a JDK. Failed
|
||||
`opt/openjdk` candidates, preferring the host architecture's prefix. Failed
|
||||
discovery probes remain available in installation diagnostics. Doctor distinguishes an absent Java
|
||||
command from a command that runs and fails, and retains bounded probe output
|
||||
and any parsed properties when the selected runtime cannot be admitted.
|
||||
@@ -1433,74 +1413,6 @@ not needed for the make step, only for the Ghidra provider. If your toolchain
|
||||
rejects the inherited `-mmacosx-version-min=10.6` flag, retry the make with
|
||||
`ADDITIONAL_FLAGS="-w"`.
|
||||
|
||||
### Automatically checkpoint an owned Ghidra Program
|
||||
|
||||
On Linux/macOS, opt in with an absolute storage directory:
|
||||
|
||||
```bash
|
||||
export REA_GHIDRA_CHECKPOINT_DIR=/absolute/rea-checkpoints
|
||||
rea decompile /absolute/target/app --procedure entry --provider ghidra --json
|
||||
```
|
||||
|
||||
Use the same setting in the MCP server environment. CLI and MCP share the
|
||||
provider-owned implementation. The first successful analysis saves the complete
|
||||
Program as a packed `.gzf`. Later processes restore its names, comments, types
|
||||
and analysis into a private temporary project with `-noanalysis`. Normal close
|
||||
saves metadata changes; unchanged queries do not rewrite the checkpoint.
|
||||
The target executable is never executed by this workflow.
|
||||
|
||||
For agents, `open_binary` advertises this startup setting. After selecting Ghidra,
|
||||
the default `binary_session` summary reports enabled or ephemeral mode in the
|
||||
provider's `capabilities[].shared_limitations`. Successful native results state
|
||||
whether initial analysis was created or a verified packed Program was restored.
|
||||
No separate save/restore tool or runtime analysis-setting update is required;
|
||||
enabling checkpoint mode requires the startup environment and an MCP restart.
|
||||
|
||||
Storage is separated by target SHA-256 and analysis-profile identity, including
|
||||
the supported Ghidra version. Identical executable bytes at another path can
|
||||
reuse the Program. One live REA session holds the checkpoint's socket lease;
|
||||
a competing session reports that owner and must wait until it closes. Only
|
||||
REA-owned Programs are checkpointed. Caller-selected existing projects retain
|
||||
their separate read-only workflow below.
|
||||
|
||||
The directory retains the current manifest and packed Program. Publication
|
||||
writes and syncs a new packed file before atomically replacing the manifest;
|
||||
superseded files are removed after publication. A failed save preserves the
|
||||
previous checkpoint and the live session for retry. Correct the reported storage
|
||||
problem, then call `close_binary` again. Incomplete cleanup retains its owner.
|
||||
Corrupt, malformed or changed checkpoints fail explicitly instead of falling
|
||||
back to a fresh import. Preserve that directory and repair it or explicitly
|
||||
select a new directory to start fresh.
|
||||
|
||||
Recovery reaches **the last successfully saved checkpoint**. Cancellation,
|
||||
process failure or a killed CLI can lose later edits; unfinished first-time
|
||||
auto-analysis cannot resume midway. This does not restore a whole REA
|
||||
investigation, and does not promise host-crash durability. The working project
|
||||
remains ephemeral. Packed Program import, checksums, and saving consume disk and
|
||||
time proportional to Program size; saving runs once after first analysis and
|
||||
again only when the Program changes.
|
||||
|
||||
This initial mode supports stock native ELF, PE and Mach-O imports on POSIX.
|
||||
Windows, special pinned-loader profiles, caller-selected `.gpr` projects,
|
||||
language/analyzer overrides, analysis seeds and extra analysis extensions are
|
||||
not admitted with this setting; unset it to use their existing workflows.
|
||||
The checkpoint directory must be private to the current user.
|
||||
|
||||
REA analysis snapshots remain separate immutable query results. Initial analysis
|
||||
has a stable generation bound to the target and analysis profile: publishing its
|
||||
first checkpoint neither replaces a live MCP session nor invalidates its initial
|
||||
snapshot. Later saved states bind their content digest, so an edited checkpoint
|
||||
can require reopening without an older `--snapshot`. Snapshot import and save
|
||||
after live metadata mutation remain unavailable as before. Complete Evidence
|
||||
retains the checkpoint directory and generation in its analysis profile; restored
|
||||
reads also report the packed file's path and verified digest. Returned caveats
|
||||
distinguish restored database state from fresh analysis.
|
||||
|
||||
Run `npm run verify:ghidra:checkpoints` with your existing Ghidra/JDK installation
|
||||
to verify fresh CLI/MCP processes, name/comment/type preservation, competing
|
||||
sessions, corruption refusal and explicit recovery. Lifecycle fixtures cover
|
||||
save failure/retry and cancellation during checkpoint finalization separately.
|
||||
|
||||
### Reuse an existing Ghidra project
|
||||
|
||||
For repeated investigations, select an expanded local project marker with
|
||||
@@ -1510,20 +1422,6 @@ in the project root; when omitted it defaults to the selected target's filename.
|
||||
Paths, globs and command flags are not program names. The matching `.rep`
|
||||
directory must be present; restore `.grez` archives with Ghidra first.
|
||||
|
||||
For a `.gzf` packed program, first import it into a new Ghidra project in an
|
||||
existing, caller-selected directory. A packed program is not a `.grez` project
|
||||
archive:
|
||||
|
||||
```bash
|
||||
"$GHIDRA_INSTALL_DIR/support/analyzeHeadless" "/absolute/new-project-directory" "new-project" \
|
||||
-import "/absolute/program.gzf" -noanalysis
|
||||
```
|
||||
|
||||
Select the resulting `new-project.gpr` with `REA_GHIDRA_PROJECT_PATH`. If the
|
||||
imported program name differs from the selected target's filename, set
|
||||
`REA_GHIDRA_PROGRAM_NAME` to the name reported by Ghidra's import. REA then
|
||||
opens its own read-only project copy; it does not import the packed program.
|
||||
|
||||
```bash
|
||||
export REA_ANALYSIS_PROVIDER=ghidra
|
||||
export REA_GHIDRA_PROJECT_PATH=/absolute/projects/app-analysis.gpr
|
||||
@@ -1545,31 +1443,18 @@ the original database; REA reads selected files only for fingerprinting and copy
|
||||
This mode does not copy unrelated sibling projects, rerun repair scripts, or
|
||||
admit bridge database edits. Cleanup removes the owned copy.
|
||||
The bridge checks the program's recorded import SHA-256 against the admitted
|
||||
target digest. An unavailable or malformed recorded digest is reported as an
|
||||
unverified identity, distinct from a known mismatch; neither admits the program
|
||||
or triggers a fresh import. Select a matching target or explicitly configure a
|
||||
different project or program. Changes to the source project also fail admission;
|
||||
reopen the target to resolve a new profile after intentionally changing it.
|
||||
target digest; mismatches and project changes fail explicitly. Reopen the target
|
||||
to resolve a new profile after intentionally changing the source project.
|
||||
|
||||
The profile records the caller path, resolved project/name, program and content
|
||||
fingerprint. Original analysis settings and completeness remain **unknown**;
|
||||
prior annotations or edits are caller-project state, not fresh REA discoveries.
|
||||
For supported header-selected MIPS ELF targets, the profile also binds the
|
||||
selected target's ELF metadata; it does not claim a fresh-import MIPS lane was
|
||||
used for the saved project.
|
||||
A matching recorded import digest does not claim that the database is unedited.
|
||||
Saved REA seed/analyzer reports remain historical facts in retained source Evidence;
|
||||
they are not interpreted as overrides applied by this session.
|
||||
Project content participates in snapshot identity, so different project states
|
||||
cannot reuse the same profile's cached observations.
|
||||
|
||||
If a function selector is missing, use `list_procedures` or `search_procedures`
|
||||
to inspect the selected project's observed inventory. A missing entry does not
|
||||
prove the function is absent from the executable. To explicitly compare with a
|
||||
fresh import, start another REA process without `REA_GHIDRA_PROJECT_PATH` and
|
||||
`REA_GHIDRA_PROGRAM_NAME`; the fresh analysis will not include the selected
|
||||
project's annotations.
|
||||
|
||||
To prepare a new project explicitly, use your existing Ghidra installation in a
|
||||
new directory you own (this writes that project; REA does not save it for you):
|
||||
|
||||
|
||||
@@ -171,13 +171,6 @@ resource truncation. JavaScript summary `limitation_count` counts the full,
|
||||
deduplicated union of parent, application and semantic caveats, including messages
|
||||
whose delivery is omitted by the view's byte budget.
|
||||
|
||||
Symbolic property reads and member enumeration share the expression resolver's
|
||||
existing resource budget, including recursive return spreads with fresh arguments.
|
||||
A bounded source retains independent window, preload and endpoint facts while
|
||||
reporting `parse_status: partial` and limited examination in `analysis_coverage`.
|
||||
Unresolved recursive values remain unknown; selected views retain source coverage
|
||||
separately from the completeness of the returned page.
|
||||
|
||||
Unavailable values are represented explicitly rather than as missing fields,
|
||||
so absence of evidence cannot silently become evidence of absence.
|
||||
|
||||
|
||||
@@ -347,23 +347,6 @@ counterparts while resolving to inventoried source files. This substitution
|
||||
requires import or require semantics. Literal Electron preload, renderer, and
|
||||
utility-process runtime paths retain the runtime filename selected by the caller.
|
||||
|
||||
Application `.js` and `.ts` sources use the nearest readable, same-container
|
||||
`package.json` type; explicit module/CommonJS suffixes keep their own meaning.
|
||||
An unavailable nearer manifest leaves the package type unknown. When a module
|
||||
package contains `.js` sources, a preliminary pass through the bounded worker
|
||||
collects BrowserWindow preload declarations. Exact same-container `.js`
|
||||
preloads use the script-loader context even when their only operation is a
|
||||
helper `require`. Renderer API names alone never select that context. Dynamic,
|
||||
unresolved, excluded-source, and cross-container declarations do not establish
|
||||
a preload identity; this static association is not proof that Electron loaded
|
||||
the file. Discovery performs another static pass, while semantic projection
|
||||
runs once and existing heap, deadline, cancellation, and cleanup controls remain.
|
||||
A source that exceeds its worker limits during discovery contributes no
|
||||
declarations, and any other discovery failure ends discovery; source analysis
|
||||
then records that failure and retains partial Evidence as usual. Either gap is
|
||||
also listed in the Evidence limitations, because source analysis may still read
|
||||
that source after a declared preload has kept its package scope.
|
||||
|
||||
Selected bounded text is then parsed as inert data to recover:
|
||||
|
||||
- `package.json` metadata and declared main or renderer entrypoints;
|
||||
@@ -558,14 +541,6 @@ the same artifact. Inline option syntax keeps its observed Evidence. This
|
||||
does not execute code or establish runtime configuration; popup overrides from
|
||||
`setWindowOpenHandler` are not covered by this constant-options resolution.
|
||||
|
||||
A hoisted function does not establish initialization timing by declaration order.
|
||||
For classic scripts, the shared index also accounts for matching literal global
|
||||
property accesses through `globalThis`, `window`, `self`, and `this`, including
|
||||
optional and computed literal access. An early read or an indirect activation
|
||||
keeps captured options unknown even when a later bare call exists. ES module
|
||||
function declarations are not inferred to be global properties. This remains a
|
||||
bounded source-order check, not a proof that runtime activation succeeds.
|
||||
|
||||
Recognized literal module-table bootstraps also support bounded lexical
|
||||
substitution through module exports, import helper bodies, local wrapper calls,
|
||||
returned closures, object spreads, and callback parameters. Recovered Electron
|
||||
|
||||
@@ -64,9 +64,8 @@ All four preserve target-file bytes. `open_jeb_project` selects or creates an en
|
||||
- When `decompile_jeb_item` omits `unit_path`, the engine implicitly selects the project's first code unit; REA reports `unit_path: null` and a limitation instead of guessing the effective unit.
|
||||
- `item_address` is a JEB code-item reference, such as a value returned by the engine's `list_code_items`. Preserve that representation and select its code unit explicitly. For the audited CE SQLite unit, the exported method reference was `sqlite3_libversion_number`; using its raw ELF symbol address did not produce a successful decompilation. File offsets, ELF virtual addresses and engine item references have different semantics. REA currently exposes unit listing, while code-item discovery remains an upstream JEB operation.
|
||||
- Valid engine refusals (`success: false` envelopes) return `execution_failure` with the engine's message when available. Diagnostics preserve the original envelope, including any recovery advice, and the inputs of a refused primary request. If client metadata fails after a primary reply, that reply and the caller's inputs remain in partial Evidence. REA does not infer an unsupported target from a refusal, invoke a recovery tool, or retry the request.
|
||||
- Successfully parsed client information is reused within the current MCP session, including when the engine omits its version. Explicit client inspection and availability probes refresh it; reconnecting invalidates the cached information.
|
||||
- An unreachable endpoint is a provider-availability failure with recovery guidance; REA does not spawn the engine or automatically replay failed operations.
|
||||
- After a transport or RPC failure (including a request or catalog timeout), the next explicit call attempts a new MCP connection after cleanup succeeds. Engine version and GUI mode are refreshed for that connection. Caller cancellation and decoded engine refusal envelopes preserve the existing connection.
|
||||
- Successfully parsed client information is reused for the lifetime of the MCP connection, including when the engine omits its version. Explicit client inspection and availability probes refresh it.
|
||||
- An unreachable endpoint is a provider-availability failure with recovery guidance; REA does not retry or spawn the engine.
|
||||
|
||||
Real-engine verification lives in the JEB lane described in [testing.md](testing.md); mock transport tests do not establish real-engine compatibility.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Kuna provider
|
||||
|
||||
[Kuna](https://github.com/Noelo-Lab/kuna) is an Apache-2.0 decompiler distributed as one `kuna` executable. For a new installation, use Kuna 1.990, REA's recommended verified release. REA uses a `kuna` you supply; it does not download, install, update or bundle Kuna. REA accepts other releases; `rea doctor` and analysis Evidence report them as unverified. Verification compares the reported version; it does not authenticate the executable. Analysis profiles retain the observed version and executable digest, independently of REA's current verified-release policy. The 1.990 real-provider checks ran on macOS arm64; Linux and other hosts have not been verified with that release.
|
||||
[Kuna](https://github.com/Noelo-Lab/kuna) is an Apache-2.0 decompiler distributed as one `kuna` executable. For a new installation, use Kuna 1.989, REA's recommended verified release. REA uses a `kuna` you supply; it does not download, install, update or bundle Kuna. REA accepts other releases; `rea doctor` and analysis Evidence report them as unverified. Verification compares the reported version; it does not authenticate the executable. Analysis profiles retain the observed version and executable digest, independently of REA's current verified-release policy. The 1.989 real-provider checks ran on macOS arm64; Linux and other hosts have not been verified with that release.
|
||||
|
||||
Kuna is static: it reads the selected image and never executes it.
|
||||
|
||||
@@ -83,7 +83,7 @@ The first text or assembly request in a session looks for this target's export i
|
||||
- Meanwhile, pseudocode comes from one `kuna decompile --addr` run. Name and address lookups, and listings, use the inventory the export takes with `kuna functions --json` before it decompiles. The session runs `kuna functions` itself, under `REA_KUNA_TIMEOUT_MS`, only when no export is building (an inventory-only request does not start one) or the export ends without an inventory.
|
||||
- Requests for the same function share one run; it stops when its last waiting request is cancelled. Each run analyzes the whole image again in its own process, so one REA process runs at most as many at once as the host has cores beyond `REA_KUNA_JOBS` (at least one). Further requests wait in order, and a waiting request is answered from the export if it finishes first. A run whose stop REA cannot prove keeps its slot until `close_binary` proves it stopped.
|
||||
- The session keeps each completed single-function text, up to 16 MiB in all with the least recently used dropped first, and answers repeats from it until the export can. After a failed export that lasts for the session.
|
||||
- Assembly and data names wait for the export; progress notifications report the wait every ten seconds. A session does not rebuild a failed export: requests that need it fail with `retryable: false`, the export's failure, its captured Kuna output and what to change before reopening the target. REA does not expose per-function records from a failed project export, even if the retained Kuna diagnostic refers to them. Single-function fallback Evidence reports the failed state and this limitation. If cleanup remains incomplete, its error takes precedence and `close_binary` retries the retained resources. A setting such as `REA_KUNA_PROJECT_TIMEOUT_MS` or `REA_KUNA_JOBS` is read when REA starts, so changing it also needs a restart. Pseudocode keeps using single-function runs.
|
||||
- Assembly and data names wait for the export; progress notifications report the wait every ten seconds. A session does not rebuild a failed export: requests that need it fail with `retryable: false`, the export's failure, its captured Kuna output and what to change before reopening the target. A setting such as `REA_KUNA_PROJECT_TIMEOUT_MS` or `REA_KUNA_JOBS` is read when REA starts, so changing it also needs a restart. Pseudocode keeps using single-function runs.
|
||||
- A single-function answer says so in its limitations. It lacks the export's whole-program prototypes, so a later request can return different text for the same function.
|
||||
|
||||
Pseudocode, assembly, `analyze_function` and data-name answers record where their text came from in `raw_result.source`: `single-function`, or `project-export` with the cache directory. `procedure_pseudo_code` and `procedure_assembly` answers from the export also name the file, byte span and the file's SHA-256. Combined `analyze_function` answers retain both facet records in `raw_result.source.files`, each with its file, digest and byte span. Both texts come from one export; losing that project retries the complete report. Listings and procedure lookups carry no `raw_result`; omitted [Mach-O data entries](#mach-o-data-entries) are reported in their limitations. Agents can open or search the export's `.c` and `.asm` there directly when that is cheaper than calling a tool per function. Text read that way is target-derived, like any decompiler output, but it bypasses the untrusted-content marker that REA's tool results carry.
|
||||
@@ -94,7 +94,7 @@ The assembly label index is scanned once per export reader. Cancelling a request
|
||||
|
||||
Regex procedure and string search uses ECMAScript Unicode matching in a cancellable worker with a matching deadline and a worker old-generation heap. Literal matching remains the default. Cancellation or a matching deadline leaves the session available. The deadline is `REA_REGEX_MATCHING_DEADLINE_MS`, a positive decimal integer from 1 to 2147483647 ms. Unset uses 5000 ms. A larger value is refused because Node schedules a delay past that signed 32-bit bound as 1 ms. Invalid values are configuration errors and do not fall back to the default. The setting is read from the environment that starts REA, so a change needs a restart. A deadline failure names the effective milliseconds, whether they came from the default or the setting, the default, and the maximum. The worker old-generation heap does not raise it. The worker heap request is `REA_REGEX_WORKER_HEAP_REQUEST_MIB`, a decimal integer from 7 to 16384 MiB. Unset uses 128 MiB. A request below 7 MiB aborts the worker during startup, and a larger value is refused. Invalid values are configuration errors and do not fall back to 128. The setting is read from the environment that starts REA, so a change needs a restart. A heap failure names the effective request, whether it came from the default or the setting, the default, the floor, and the maximum. A positive Node.js `--max-old-space-size` on the process that starts REA, including `NODE_OPTIONS`, overrides that request and sets the effective heap. `--max-old-space-size-percentage` takes precedence over that flag in either order. REA does not turn the percentage into a MiB, so the effective heap stays unestablished instead of the worker heap request or the absolute flag. The reported `NODE_OPTIONS` value is the process initial environment when that block can be read. A later edit to the environment is not the value Node applied, and an unreadable initial value leaves the effective heap unestablished. The launch Node options are the initial command line on Linux when that buffer still has a program entry, not a later edit to `process.execArgv`. A preload can clear that array after V8 has applied the flags. Setting `process.title` or `--title` overwrites the command line, so a buffer without a program entry leaves the effective heap unestablished. `--env-file` or `--env-file-if-exists` can supply `NODE_OPTIONS` when the initial environment has none. That file is not part of the initial environment block, so the effective heap stays unestablished instead of the worker heap request. An initial `NODE_OPTIONS` is still used, because Node does not let the file replace it. A positive `--max-heap-size`, including the underscore form and launch `NODE_OPTIONS` on Node versions that allow it, sets the total heap rather than the old generation and can be lower than the worker heap request. The effective old generation stays unestablished while that value is in effect. A zero or empty value clears it. `--experimental-config-file` or `--experimental-default-config-file` can set `nodeOptions`, including a heap percentage, from a file that is not read back. The effective heap stays unestablished when either flag is present, including when an absolute `--max-old-space-size` is also on the command line or in the recovered `NODE_OPTIONS`. Node accepts the config path as `=` or the next argument.
|
||||
|
||||
Single-function subprocess output and each cached source facet share a 16 MiB buffered-text capacity. Cached spans are checked before allocation; larger project-export spans remain in the local export and the error identifies the file and range. A single-function run does not by itself retain a complete project export. `analyze_function` with `detail: "complete"` (the CLI default) returns the complete dossier inline, which can still be large; later retained views do not remove that first transfer. The MCP default retains the dossier and returns its summary, so page the facets you need through `inspect_analysis_view`, or prefer the individual text tools for a focused question.
|
||||
Single-function subprocess output and each cached source facet share a 16 MiB buffered-text capacity. Cached spans are checked before allocation; larger text remains in the local export and the error identifies the file and range. `analyze_function` with `detail: "complete"` (the CLI default) returns the complete dossier inline, which can still be large; later retained views do not remove that first transfer. The MCP default retains the dossier and returns its summary, so page the facets you need through `inspect_analysis_view`, or prefer the individual text tools for a focused question.
|
||||
|
||||
## Mach-O data entries
|
||||
|
||||
|
||||
@@ -280,21 +280,6 @@ inspects its two selected paths sequentially, retaining each inspection rather
|
||||
than both full byte snapshots. The limit therefore remains a per-file resource
|
||||
admission rule, not an aggregate comparison limit. Comparison Evidence keeps
|
||||
the caller-selected paths as its parameters and artifact locations.
|
||||
When either side's member inspection exceeds its representation budget, the
|
||||
comparison limitations name that side and its `resource-limit` diagnostic; its
|
||||
empty member collections do not establish added or removed members. Each side's
|
||||
`resource_limits` preserves the reported diagnostic's code, scope, detail and
|
||||
nullable offset. An empty array means no resource limit was reported; historical
|
||||
comparisons omit this field, leaving those diagnostics unrecorded. Always read
|
||||
the comparison coverage alongside member counts. To compare
|
||||
a smaller selection, pass the same repeatable `--type` full names that
|
||||
`inspect-managed-members --type` accepts to `compare-managed-members`, which
|
||||
applies them to both sides and records them as `type_names`; over MCP, compare
|
||||
two `inspect_managed_members` Evidence records selected with the same
|
||||
`type_names`. A single selected type can still exceed the budget.
|
||||
The two-record MCP workflow keeps full source Evidence available for
|
||||
`inspect_analysis_view` field selection and paging; path-based CLI comparison
|
||||
records the selected paths, type names and per-side diagnostics.
|
||||
|
||||
Input overflow returns a resource constraint. Decode or projection overflow
|
||||
reports `resource-limit` with unavailable or partial coverage; omitted facts do
|
||||
@@ -739,10 +724,6 @@ refer to the pointer table when present rather than necessarily contiguous
|
||||
definition rows. Invalid or nonmonotonic list coordinates and malformed or
|
||||
ambiguous pointers leave affected owners unknown with partial coverage while
|
||||
preserving readable member and attribute facts.
|
||||
For each list column, ordering checks compare consecutive valid coordinates
|
||||
even across invalid rows. A provable decrease leaves the affected ownership
|
||||
unknown. Retained declaring-type filters count these candidates in
|
||||
`unresolved_filter_rows`; omit that filter to retrieve their known-name rows.
|
||||
The retained inverse indexes are admitted against the existing representation
|
||||
budget before allocation.
|
||||
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
# Measure MCP catalog costs in Codex
|
||||
|
||||
Use actual harness usage to evaluate catalog changes. A `tools/list` JSON byte
|
||||
count measures transport size. It does not establish how many tokens a client
|
||||
submits to a model, whether it defers schemas, or the total cost of an analysis.
|
||||
The pinned [Codex observations](verification/mcp-catalog-codex-2026-11-05.md)
|
||||
include actual loading controls, an unavailable-host negative control and a
|
||||
completed real-package pilot.
|
||||
|
||||
## Run a controlled loading comparison
|
||||
|
||||
Build the selected source checkout with the pinned Node and npm versions first:
|
||||
|
||||
```sh
|
||||
npm ci
|
||||
npm run build:cached
|
||||
npm run eval:mcp-catalog -- --model gpt-6.1-sol \
|
||||
--output "$HOME/.cache/rea-catalog-run" --repetitions 3 --plan
|
||||
```
|
||||
|
||||
Remove `--plan` to execute the matrix. This makes real model requests using the
|
||||
local Codex authentication. The output directory must not already exist. No
|
||||
account configuration is changed. The runner copies only authentication into
|
||||
an external disposable profile and removes that profile after each trial.
|
||||
Execution stays outside both source checkouts even when results are saved inside
|
||||
one, so ancestor repository instructions and skills do not pollute the trial.
|
||||
The npm entrypoint holds the driver's artifact lock throughout the matrix. A
|
||||
selected external checkout has its own lock, so the matrix also pins one runtime
|
||||
fingerprint and binds its catalog receipt to that manifest. Freshness checks
|
||||
stop further model calls if the runtime changes between trials. Each trial's
|
||||
before/after fingerprints must match the matrix pin; different builds never form
|
||||
a valid pair. Matrix admission/freshness audits sit outside individual guarded
|
||||
trial resource measurements. Do not rebuild the selected runtime concurrently.
|
||||
|
||||
This lane requires Linux, Python 3, a working user systemd manager with cgroup v2,
|
||||
and an installed Codex CLI supporting the recorded flags. It reuses the existing
|
||||
resource observer: one CPU, 4 GiB hard memory limit, 3 GiB early stop, no swap,
|
||||
and a 192-task ceiling. Runs are sequential. Unsupported clients, resource stops,
|
||||
failed startup, missing usage, and unverified cleanup remain failed measurements.
|
||||
They do not become zero-cost successes.
|
||||
|
||||
Trials disable account apps and plugins explicitly. A fresh `CODEX_HOME` alone
|
||||
does not prevent account-connected tools from appearing in the client executor
|
||||
catalog. For a deliberate mixed-catalog investigation, run `agent-trial.py` with
|
||||
`--include-account-tools`; keep those results separate from REA-only trials.
|
||||
The settings receipt records the same feature policy written to Codex config.
|
||||
|
||||
Each repetition compares no server, all REA tools, and a client-selected subset
|
||||
(`binary_session` by default), in the supported client-code host mode. Alternate
|
||||
repetitions reverse ordering. All variants receive the same developer instructions
|
||||
and exact-response prompt. A valid loading trial completes one turn, returns
|
||||
`CATALOG_OK`, and performs no tool calls. REA is marked `required` in every
|
||||
server-enabled configuration. The runner explicitly disables/enables the stable
|
||||
`code_mode_host` feature for direct/client-code modes. The old `code_mode` and
|
||||
`code_mode_only` switches do not override Codex 0.165's enabled host default.
|
||||
The requested flags and CLI version are retained; verify the actual call shape
|
||||
in completed-task traces too. `--modes direct client-code` adds a diagnostic
|
||||
host-disabled control. This installed Codex reports that disabling the host
|
||||
makes tool dispatch unavailable; it does not provide a qualified eager/direct
|
||||
comparison. An empty response alone cannot qualify tool availability. Errors
|
||||
invalidate that profile even when the model still emits the expected marker.
|
||||
|
||||
Useful controls:
|
||||
|
||||
```sh
|
||||
# Compare full input guidance with the default compact input presentation.
|
||||
npm run eval:mcp-catalog -- --model gpt-6.1-sol \
|
||||
--output "$HOME/.cache/rea-catalog-full" --input-profile full --repetitions 3
|
||||
|
||||
# Choose exact tool names in the working host mode.
|
||||
npm run eval:mcp-catalog -- --model gpt-6.1-sol \
|
||||
--output "$HOME/.cache/rea-catalog-selected" --modes client-code \
|
||||
--selected-tools analyze_javascript_application inspect_analysis_view
|
||||
```
|
||||
|
||||
`enabled_tools` filters the catalog after client discovery. It does not reduce
|
||||
the server's original `tools/list` transport. Server tool-family scope is a
|
||||
separate existing control; do not attribute client filtering results to it.
|
||||
The output-schema profile remains REA's default in these experiments.
|
||||
|
||||
## Read the evidence
|
||||
|
||||
`report.json` retains every attempted trial and compares each enabled-server
|
||||
trial with its same-mode, same-repetition no-server control. It reports valid
|
||||
pair counts and the median/range of observed input-token differences. Failed
|
||||
pairs are explicit and excluded from the median. A small or negative delta can
|
||||
reflect client framing, randomized workspace paths, and measurement variation;
|
||||
it does not establish a negative schema cost.
|
||||
|
||||
Usage, cleanup and resource receipts are collected independently of the verdict.
|
||||
A failed host, malformed event or wrong marker can retain observed usage and
|
||||
resource costs. Stage-specific issues explain invalidity; missing observations
|
||||
remain unknown. A changed matrix records its unattempted cells explicitly.
|
||||
|
||||
Each trial retains its exact prompt, runtime file digests, requested model alias,
|
||||
Codex version, settings/source hashes, JSONL events, actual usage, answer, cleanup
|
||||
receipt, and process-family resource receipt. The selected rollout capture keeps
|
||||
per-response usage and tool exchanges; authentication, ambient environment,
|
||||
account metadata, and encrypted reasoning are excluded. Tool output and local
|
||||
paths remain evidence and should be reviewed before sharing the raw files.
|
||||
|
||||
The metrics have distinct meanings:
|
||||
|
||||
| Metric | Meaning and limit |
|
||||
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| Catalog bytes and SHA-256 | Built server advertisement, including output schemas; not model input. |
|
||||
| `turn.completed.usage.input_tokens` | Observed whole-turn usage reported by Codex; includes cached input. |
|
||||
| Cached/uncached input | Report both. Cache hits do not remove tokens from the context. |
|
||||
| Per-response usage | Retained usage records help locate discovery and continuation costs. Do not add cumulative thread counts again. |
|
||||
| Paired input delta | Incremental loading observation including instructions/client framing; not an exact schema-only token count. |
|
||||
| Elapsed and family CPU | Whole guarded trial, including local setup and client/network waits; remote inference CPU is unmeasured. |
|
||||
| Family peak memory | Kernel cgroup peak at payload exit, including observer and file cache; not final whole-scope counters. |
|
||||
|
||||
These are fresh client/server sessions. OS caches and API prefix caches can
|
||||
already be warm. No cold-cache claim follows from a new session. The requested
|
||||
model alias is recorded; a resolved server-side model version is not inferred.
|
||||
The harness usage is not an independent billing reconciliation.
|
||||
The usage event shape follows the documented
|
||||
[Codex non-interactive JSON output](https://developers.openai.com/codex/noninteractive#make-output-machine-readable).
|
||||
|
||||
## Evaluate completed investigations too
|
||||
|
||||
A no-tool response can avoid deferred discovery entirely. Before changing the
|
||||
catalog based on this lane, compare completed tasks through the real harness.
|
||||
Use `npm run verify:agent` and its known-answer rubric, or run a selected real
|
||||
artifact through `tests/support/analysis-efficiency/agent-trial.py` under
|
||||
`resource-run.py --observe-payload-exit`. Use the same task, artifact digest,
|
||||
model/reasoning, authority, result coverage, and correctness oracle in every arm.
|
||||
The existing runner accepts exact `--enabled-tools` and defaults to client-code
|
||||
host mode. Its direct option is a host-disabled diagnostic, not a claim of
|
||||
working native dispatch in the installed client.
|
||||
Use `--capture-tool-context` to locate per-response costs. Its task summary
|
||||
explicitly leaves factual correctness unassessed until an independent oracle or
|
||||
transcript review establishes the requested facts.
|
||||
|
||||
Count discovery, arguments, replies, retries, and continuations. Check the
|
||||
requested facts against source bytes or another independent oracle, real produced
|
||||
Evidence IDs and source links, partial/unknown coverage, and cleanup. Preserve
|
||||
failed attempts. A successful small-package task is a pilot, not proof of broad
|
||||
application performance or provider compatibility.
|
||||
+14
-233
@@ -188,11 +188,7 @@ The complete field/type diagnostic is preserved when normalization fits the
|
||||
response budget, including request-specific rejected fields and reasons. Zod
|
||||
input issues retain their field paths and available type and range constraints.
|
||||
SDK diagnostic parts may be distributed between field issues and the main
|
||||
message so each retained explanation appears once. Known neighboring-tool
|
||||
argument-name mistakes (such as `address` instead of `procedure`, or `query`
|
||||
instead of `pattern`) provide targeted replacement guidance and the expected
|
||||
canonical property name without accepting unauthorized aliases or changing
|
||||
canonical Evidence parameters.
|
||||
message so each retained explanation appears once.
|
||||
If their conservative projection allowance is exhausted, the response states
|
||||
that field-level detail was omitted and retains the original SDK diagnostic.
|
||||
Oversized SDK diagnostics keep the existing retained-Evidence recovery path. An unknown
|
||||
@@ -222,12 +218,6 @@ Malformed provider replies return `unreadable_output` for
|
||||
`resolve_containing_procedure` before successful-result projection;
|
||||
cancellation and incomplete cleanup remain errors.
|
||||
|
||||
For a procedure match, `procedure_references` with `direction: "incoming"` can
|
||||
provide the selected provider's reference kinds when supported. Its scope covers
|
||||
the procedure body, so compare target addresses when selecting references to the
|
||||
exact entry. It is a separate observation and does not establish runtime use or
|
||||
complete reference discovery.
|
||||
|
||||
REA prepares complete MCP results within the pinned stdio client's 10 MiB
|
||||
receive-buffer budget, including both text and structured representations and
|
||||
room for the JSON-RPC envelope. Tool errors omit `structuredContent` from the
|
||||
@@ -288,17 +278,6 @@ to `/absolute/view-request.json`, then run
|
||||
`rea inspect-analysis-view /absolute/view-request.json --json`. This also reads
|
||||
pre-summary source records without changing their identity; no live provider is started.
|
||||
|
||||
Unavailable or inapplicable native collections keep `items.rows: null` and
|
||||
report `coverage: {"status":"unavailable","examined":0,"total":null,"next_offset":null,"exhausted":false}`.
|
||||
This applies to unrecorded `native-load-image` checks/sections and inapplicable
|
||||
`native-context` result limitations. Their total is unknown; no continuation is
|
||||
offered. Stop paging that collection and inspect the returned unknowns rather
|
||||
than treating it as an observed empty set or retrying a null offset. Recorded
|
||||
empty collections, empty selections and offsets past the end retain numeric
|
||||
totals and exhausted page coverage. Clients upgrading from the earlier
|
||||
zero-total representation must handle the new `unavailable` status and nullable
|
||||
total; historical saved views remain valid.
|
||||
|
||||
`procedure_address`, `procedure_callees`, `procedure_callers`,
|
||||
`resolve_containing_procedure` and `get_call_graph` also accept `detail: "summary"`
|
||||
(complete remains the default). The summary includes the first 64 recorded
|
||||
@@ -335,17 +314,6 @@ unknown. Exhausted pages describe the recorded observations and do not establish
|
||||
complete native discovery. The same views work through the CLI with historical
|
||||
Evidence files or bundles and require no live provider.
|
||||
|
||||
For retained `inspect_native_instruction` Evidence, use
|
||||
`view: {"kind":"native-instruction-references","collection":"incoming_references","offset":0,"limit":64}`
|
||||
to page typed references to the exact code or data address. Select `references`
|
||||
for outgoing instruction references. Rows preserve their original indices and
|
||||
provider-reported kind and provenance. `items.state` distinguishes available,
|
||||
unavailable, and not-recorded collections; coverage is null in the latter two
|
||||
cases. The fixed 1 MiB page projection allowance leaves room for Evidence and
|
||||
MCP encoding. An oversized single row remains in the complete parent, which
|
||||
can be exported. Page exhaustion describes retained rows, not discovery or
|
||||
runtime access.
|
||||
|
||||
For oversized native function Evidence, `details.reported_limits.continuation`
|
||||
contains a ready-to-call `inspect_analysis_view` request for the retained
|
||||
pseudocode's first page. Use its `name` and `arguments` directly, then follow
|
||||
@@ -409,12 +377,6 @@ is null. `total` counts all matching heads; each page preserves complete records
|
||||
in stable unknown ID order. Filters apply before paging. Pages reflect current
|
||||
session state, so keep the unknown ledger unchanged while traversing it.
|
||||
|
||||
`list_unknowns` only lists registered heads. Import restores the Unknown records
|
||||
present in a bundle; it does not turn residual question text embedded in Evidence
|
||||
into registry entries. An empty registry therefore does not establish that
|
||||
retained Evidence has no open questions. Inspect the relevant Evidence or its
|
||||
analysis view for its residual questions.
|
||||
|
||||
Automatically retained workflow questions include their immutable result
|
||||
Evidence ID. Distinct results retain independent questions and source links;
|
||||
repeating the same result does not create another unknown. Workflow Evidence
|
||||
@@ -424,19 +386,6 @@ Duplicate creation is a nonretryable `invalid_request`; use the existing head
|
||||
or update it with its current `expected_revision`. A stale update remains a
|
||||
retryable `revision_conflict`.
|
||||
|
||||
If automatic question publication fails after a workflow completes,
|
||||
`details.workflow_unknown_publication` identifies the Evidence and states
|
||||
whether its retention was confirmed. `created_questions` and
|
||||
`existing_questions` count processed question occurrences; duplicate questions
|
||||
may share an Unknown identity. `total_questions` counts string questions, and
|
||||
`next_question_index` is the zero-based position in the workflow's original
|
||||
`residual_unknowns` array where publication stopped. It is not a resume token.
|
||||
For a retained record on the same MCP connection, retrieve the Evidence by ID
|
||||
with `get_evidence_bundle` and inspect committed heads with `list_unknowns`.
|
||||
Unprocessed questions remain in the completed Evidence; they were not checked
|
||||
by this attempt and may already exist from earlier work. A one-shot invocation identifies session-only retention
|
||||
as `invocation-only` and carries the completed Evidence in its failure when available.
|
||||
|
||||
When `record_unknown` or `update_unknown` commits but its complete reply exceeds
|
||||
the response budget, `details.reported_limits.committed_unknown` identifies the
|
||||
exact unknown ID, committed revision and mutation Evidence IDs. Its retained
|
||||
@@ -551,12 +500,10 @@ result's `_meta`, whether the call succeeded or failed:
|
||||
```
|
||||
|
||||
The marker is declared once per tool contract (`returnsTargetContent`) and is
|
||||
on by default: `inspect_analysis_settings`, `update_analysis_settings`,
|
||||
`export_evidence_bundle`, `record_unknown`, `guided_workflows`,
|
||||
`search_guidance`, and `read_guidance_section` omit it. The first four contain
|
||||
only REA session state or caller-supplied values; the workflow and guidance
|
||||
tools return REA's own installed workflows and documentation, not text from an
|
||||
analysis target. Tools whose results or errors
|
||||
on by default: `export_evidence_bundle`, `record_unknown`, `search_guidance`,
|
||||
and `read_guidance_section` omit it. The first two contain only REA session
|
||||
state or caller-supplied values; the guidance tools return REA's own installed
|
||||
documentation, not text from an analysis target. Tools whose results or errors
|
||||
can include provider, launcher, or device output stay marked, including
|
||||
`binary_session` (provider failure
|
||||
reasons and availability diagnostics), `close_binary` (provider cleanup
|
||||
@@ -576,21 +523,6 @@ that the response budget accounts for. It does not alter `content` or
|
||||
such as requiring approval for process-launching or device-mutating tools
|
||||
after target content was read.
|
||||
|
||||
## Offline API contract declarations
|
||||
|
||||
`inspect_api_contract` reads local OpenAPI 3.0/3.1/3.2 JSON (default) or
|
||||
GraphQL SDL (`format: "graphql-sdl"`) without an active target or network requests.
|
||||
OpenAPI inventories inline path operations and unresolved Path Items. GraphQL
|
||||
inventories type/field declarations; optional `operations_path` adds operation
|
||||
owners, aliases, SDL field bindings and explicit fragment-spread edges without
|
||||
expanding fragments. Unresolved bindings and unavailable models stay explicit.
|
||||
Default summaries retain complete Evidence; `api-contract` or `graphql-contract`
|
||||
views select facts immediately with exact filters, fields and paging. Retained
|
||||
`inspect_analysis_view` uses the same projector without reopening inputs.
|
||||
Coverage describes examined declarations and does not establish runtime behavior
|
||||
or full specification/operation validity. See [offline API contracts](api-contracts.md)
|
||||
for source identities, parser allowances, supported scope and CLI publication.
|
||||
|
||||
## Retained application Evidence inputs
|
||||
|
||||
`analyze_javascript_application`, `inspect_binary_layout`, `inspect_artifact`,
|
||||
@@ -724,19 +656,12 @@ refuses the summary before analysis. Further views use the returned
|
||||
`parent_evidence_id` through `inspect_analysis_view`, or a saved Evidence file or
|
||||
portable bundle, and never repeat decompilation.
|
||||
|
||||
A null provider observation produces `status: "unavailable"` and
|
||||
`reason: "no_pseudocode"`; item limitations retain any provider explanation.
|
||||
This does not establish why pseudocode is absent or whether a retry would help.
|
||||
Operation failures and malformed non-null output remain `status: "error"`.
|
||||
The `failed` count includes both unavailable and error items. Existing retained
|
||||
records keep their original item statuses and error details.
|
||||
|
||||
A `batch-decompile` view selects one `collection` and repeats the batch counts
|
||||
and provider identities:
|
||||
|
||||
- `items`: rows in retained order with their original `index`, so duplicate
|
||||
selectors stay distinct. A row keeps the selector in `address`, its `procedure`
|
||||
identity and `status`, plus `pseudocode_code_units`, an unavailable `reason`, or its exact `error` and
|
||||
identity and `status`, plus `pseudocode_code_units` or its exact `error` and
|
||||
`limitations`. `where.indices`, `where.addresses`, `where.procedure_addresses`
|
||||
and `where.statuses` filter exactly and combine; an item with unknown identity
|
||||
never matches `procedure_addresses`. Omitted filters select all, and empty
|
||||
@@ -745,11 +670,11 @@ and provider identities:
|
||||
- `pseudocode`: one `index`'s text in UTF-16 code-unit pages. `offset` and `limit`
|
||||
default to 0 and 16,384, and `coverage` measures that body. An offset inside a
|
||||
surrogate pair is refused, and a limit ending inside one stops before it. An
|
||||
unavailable or error item has no text and is refused with a pointer to `items`.
|
||||
error item has no text and is refused with a pointer to `items`.
|
||||
|
||||
Page `coverage` counts the selection only: exhausting it establishes nothing
|
||||
about functions outside the batch. Evidence `limitations` aggregate every item's
|
||||
provider observations and are not attributed to one successful item; unavailable and error
|
||||
provider observations and are not attributed to one successful item; only error
|
||||
items keep their own. A page over the response budget offers a smaller selection
|
||||
from the same `source`: fewer rows, or one body's `pseudocode` pages when a single
|
||||
row does not fit. An inline source offers none, because the record cannot be
|
||||
@@ -836,7 +761,6 @@ arrays and nested sources declared by the tool's input schema:
|
||||
| `match_native_functions` | `left`, `right`, `left_observations[]`, `right_observations[]` |
|
||||
| `trace_application_feature`, `trace_javascript_semantics`, `compare_source_to_bundle` | `application`; feature tracing also accepts `native_observations[]` |
|
||||
| `compare_application_versions`, `compare_javascript_export_shapes` | `left`, `right`; version comparison also accepts both native-observation arrays |
|
||||
| `compare_static_and_observed_endpoints` | `application`, `capture`; summary retains the complete comparison for `endpoint-comparison` pages |
|
||||
| `reconcile_javascript_runtime` | `static_layers[].analysis`, `runtime_observations[]` |
|
||||
| `compare_managed_members`, `verify_managed_native_boundaries`, `import_managed_reconstruction` | Comparison `left`/`right`; verification `managed_boundaries`/`native_observations[]`; reconstruction `static_members` |
|
||||
| `project_managed_application_graph` | `managed_artifact`, `managed_members`, `managed_native_boundaries` |
|
||||
@@ -906,20 +830,13 @@ parent for another invocation.
|
||||
|
||||
## Identity and discovery
|
||||
|
||||
When an admitted `open_binary` attempt fails, its error details include
|
||||
`target_at_open_completion`: the selected binary target when that attempt
|
||||
settled, or `null` when none was selected. `provider_id_at_open_completion`
|
||||
names its bound deep provider, or `null` if none was bound. A later lifecycle
|
||||
operation can change either selection; call `binary_session` for current state. This field does
|
||||
not establish provider readiness or resource cleanup.
|
||||
|
||||
`binary_session` reports the active package, server, SDK, and negotiated
|
||||
protocol details. `rea doctor` separately inspects supported JSON/TOML client
|
||||
registrations, reports their command vectors as aligned, stale, missing, or
|
||||
invalid, and keeps live-server state `unknown` unless the active connection
|
||||
supplies identity.
|
||||
|
||||
By default, `tools/list` returns the complete canonical tool inventory, including tools that
|
||||
`tools/list` returns the complete canonical tool inventory, including tools that
|
||||
are currently unavailable. Opening or closing a target or observing a provider
|
||||
health transition leaves that catalog unchanged and does not emit
|
||||
`notifications/tools/list_changed`.
|
||||
@@ -931,7 +848,7 @@ share status facts. `tool_availability.client_requirements` is the baseline for
|
||||
each group; a group's optional `client_requirements` replaces that baseline for
|
||||
the group's tools. Expand each group's `names` into one tool entry, carrying its
|
||||
availability, reason, remediation, default-mode status, modes, and client
|
||||
requirements. With the default full catalog, compare the union of group names with `tools/list` when checking
|
||||
requirements. Compare the union of group names with `tools/list` when checking
|
||||
that a client received the full catalog. An operation absent from all groups is
|
||||
missing from the complete inventory; it is not an implicit unavailable entry.
|
||||
|
||||
@@ -1027,15 +944,6 @@ concrete version, selection source, and complete analysis profile through
|
||||
choices return typed selection details. A selected provider is never replaced
|
||||
automatically after a runtime failure.
|
||||
|
||||
When an admitted `open_binary` attempt fails, `error.details.selection_at_failure`
|
||||
records the canonical binary path and SHA-256, resource root (including its
|
||||
caller-selected path), or absence of selection at
|
||||
the end of that serialized attempt. This is a past observation: a queued or
|
||||
later operation may already have changed the selection. No selection does not
|
||||
establish that cleanup is complete. Call `binary_session` for current state
|
||||
and cleanup status. Validation failures that do not enter the session have no
|
||||
such receipt. The original error and any cleanup recovery remain authoritative.
|
||||
|
||||
`open_binary` resolves an Apple bundle directory (`.app`, `.appex`,
|
||||
`.framework`, `.xpc`, `.systemextension`, `.bundle` or `.plugin`, compared
|
||||
case-insensitively) to the program file its `Info.plist` names in
|
||||
@@ -1619,26 +1527,6 @@ canonical `integrity_contradictions` records and marks application graph
|
||||
coverage partial when any mismatch is continued. Contradicted nested ASARs
|
||||
remain opaque.
|
||||
|
||||
`extract_artifact` accepts optional `output_root` and `occurrence_ids` inputs.
|
||||
The CLI equivalents are `--output-root` and repeatable `--occurrence-id`.
|
||||
`output_root` is an absolute path on the REA host: its parent must exist and the
|
||||
root must not exist, even as an empty directory or a symbolic link. Omitting it
|
||||
keeps the fresh `rea-extracted-<uuid>` directory under Node's temporary directory
|
||||
(`TMPDIR` on POSIX). Successful output persists after the call and client close;
|
||||
the caller owns its later removal. A failed extraction rolls back only its own
|
||||
output and reports any cleanup failure.
|
||||
An output root inside the source directory, including a symlink alias, is refused.
|
||||
|
||||
Obtain exact IDs from the active artifact's inventory. Omitting `occurrence_ids`
|
||||
extracts all regular entries exposed by the active reader. An explicit selection
|
||||
must be nonempty and unique; unknown IDs, directories, links, the root and nested
|
||||
archive members are rejected before output creation. Open the containing archive
|
||||
separately to extract its nested members. Selection is by occurrence, not content
|
||||
hash, so equal bytes at different paths remain independently selectable. Only
|
||||
selected files face destination name collisions; input path and integrity checks
|
||||
still cover the full inventory. The extraction manifest records exactly which
|
||||
occurrences were materialized and retains the source manifest identity.
|
||||
|
||||
`extract_artifact` accepts the same policy in MCP and as `--integrity-policy`
|
||||
on `rea extract-artifact`. With `record-and-continue` it writes the observed
|
||||
bytes and returns the `integrity_contradictions` records for the extracted
|
||||
@@ -1674,49 +1562,6 @@ support for omitted historical values.
|
||||
known and differ. If only one value is known, storage comparison is unknown;
|
||||
when both are omitted, storage is not compared.
|
||||
|
||||
<!-- BEGIN:compare-artifacts:compared-format-metadata-kinds -->
|
||||
|
||||
`compare_artifacts` also compares the declarations in the `format_metadata`
|
||||
of UnityFS members (`unityfs-entry`), PyInstaller entries
|
||||
(`pyinstaller-entry`), Node SEA members (`node-sea-entry`),
|
||||
ESP image segments (`esp-image-segment`), DfuSe elements (`dfuse-element`),
|
||||
ESP flash partitions (`esp-flash-partition`) and FIT images (`fit-image`);
|
||||
each format's guide names the compared fields. A changed declaration is a
|
||||
metadata change, including when content is unavailable. A declaration that
|
||||
only one side records, or that the other side records as another kind, is
|
||||
unknown and adds a limitation. A field that either side could not read is
|
||||
unknown, and the other fields still compare. Other kinds are not compared:
|
||||
their fields record layout, come from the bytes of the artifact or its
|
||||
container or from caller options that identify the target, or repeat
|
||||
occurrence fields, except Godot PCK source paths, which
|
||||
[Godot PCK archives](godot-pck.md) explains.
|
||||
|
||||
<!-- END:compare-artifacts:compared-format-metadata-kinds -->
|
||||
|
||||
Every occurrence records `entry_kind`, `executable` and `encrypted`. A
|
||||
different entry kind, `encrypted` value or occurrence limitation text is a
|
||||
`metadata` difference, and a different `executable` value is an `executable`
|
||||
difference. A symlink's own permission bits never mark it `executable`,
|
||||
because running a symlink runs its target. When either side's content is
|
||||
unavailable, `compare_artifacts` lists the differences that it can still
|
||||
observe with `availability`, and the path compares `unknown` unless a
|
||||
declaration changed. REA does not read symlink targets or encrypted bytes,
|
||||
so a non-executable file that becomes a symlink compares `unknown` in
|
||||
`metadata` and `availability`, and so does a member that becomes encrypted.
|
||||
Declared sizes do not count without content: REA compares observed sizes.
|
||||
For a node-less metadata record with a compared declaration, only that
|
||||
declaration, these fields and any recorded ASAR storage decide the
|
||||
comparison, so a record that becomes encrypted compares `changed` in
|
||||
`metadata`. Such a record adds `availability` only when a declaration is
|
||||
unknown. REA does not compare the record's unread payload or its
|
||||
limitations, which describe the observation.
|
||||
|
||||
When either side has no content, `compare_artifacts` also compares the
|
||||
occurrences' `declared_integrity` claims, which Godot PCK and ASAR readers
|
||||
record. Claims of one algorithm that differ add `metadata`. REA cannot verify
|
||||
them, so they do not make the path `changed`, and equal claims add nothing.
|
||||
With content on both sides, the observed bytes decide.
|
||||
|
||||
POSIX letter-colon names such as `a:b` remain valid logical
|
||||
names; Windows extraction refuses names its filesystem cannot materialize.
|
||||
|
||||
@@ -1860,9 +1705,7 @@ JSON Schema:
|
||||
|
||||
- Annotation prose leaves the advertised form: nested property descriptions,
|
||||
literal examples, defaults, and titles are dropped. The root keeps its
|
||||
description because it states the accepted input groups. A root `detail`
|
||||
property keeps its description because it selects between complete Evidence
|
||||
and a bounded summary.
|
||||
description because it states the accepted input groups.
|
||||
- Schema-local references are inlined within the budget. Kimi Code expands
|
||||
references before sending provider requests, so sharing definitions cannot
|
||||
establish the provider-facing size. Expansion stops at the budget before
|
||||
@@ -1893,53 +1736,6 @@ check full-profile local-path rules. Compact MCP workflow tests verify canonical
|
||||
rejection of invalid inputs before provider work, independent of how much detail
|
||||
the selected advertisement retains.
|
||||
|
||||
### Hot/cold catalog discovery
|
||||
|
||||
`REA_MCP_TOOL_CATALOG_PROFILE=hot-cold` opts into per-connection loading for
|
||||
clients that refresh `tools/list` after `notifications/tools/list_changed`.
|
||||
The default, `full`, retains the complete upfront advertisement. Restart the
|
||||
server to change profiles. See [client configuration](client-configuration.md#hotcold-catalogs).
|
||||
|
||||
Hot/cold initially advertises `binary_session`, `open_binary`, `close_binary`,
|
||||
`get_evidence_bundle`, and the adapter's `discover_tools`.
|
||||
`inspect_analysis_view` is loaded on demand like other analysis tools. When a
|
||||
follow-up needs retained facts, include it in `discover_tools.names`; if the
|
||||
producer and follow-up are known together, load both in one batch. Producers
|
||||
that accept `view` can answer a selected first question without loading the
|
||||
cross-domain view contract. Complete output schemas and canonical result
|
||||
validation are preserved when each tool loads.
|
||||
An explicit tool-family scope can remove any of those domain tools.
|
||||
`discover_tools` searches canonical names and descriptions using case-insensitive
|
||||
words, or accepts exact `names`. It loads the matching page and returns names
|
||||
and abbreviated descriptions with their configured input schemas, so the next
|
||||
call can use the returned arguments contract. Output schemas remain in
|
||||
`tools/list`. Search defaults to three results, allows up to
|
||||
20 per page, and returns `total_matches` and `next_offset`. Exact names preserve
|
||||
request order and reject unknown or excluded names before loading anything.
|
||||
Use fewer keywords when no result matches; search requires every word to match.
|
||||
Both reply representations must fit the configured response budget before any
|
||||
tools load. An oversized page returns a resource constraint; reduce `limit` or
|
||||
request fewer exact names and retry. Input schemas are retained in full within
|
||||
the selected input profile.
|
||||
|
||||
Loaded definitions retain their original names, complete configured schemas,
|
||||
annotations, handlers and client approval rules. Loading does not execute an
|
||||
analysis, open a target, or initialize the deferred analysis runtime. A batch
|
||||
emits one list-change notification; repeating it emits none. Loaded tools remain
|
||||
available until the connection ends. There is no generic execution gateway.
|
||||
If a loaded tool does not appear in the client, enable client catalog refresh
|
||||
or reconnect using `REA_MCP_TOOL_CATALOG_PROFILE=full`.
|
||||
|
||||
The catalog identity covers the complete in-scope inventory, the discovery
|
||||
contract and the hot/cold policy; its count includes `discover_tools`. It differs
|
||||
from full advertisement and remains stable as tools load. `rea mcp doctor`
|
||||
checks the profile's initial advertisement and identity. The domain inventory in
|
||||
`binary_session.tool_availability` continues to describe provider and target
|
||||
availability, independently of whether a definition is loaded. Load a listed
|
||||
cold tool before calling it. Provider health and target changes do not load,
|
||||
unload or remove definitions. The input/output schema profiles and explicit
|
||||
family exclusions continue to apply.
|
||||
|
||||
### Tool family advertising scope
|
||||
|
||||
Agents pay context for every advertised tool schema, and clients that send
|
||||
@@ -2001,7 +1797,6 @@ bundle import/export; it does not include those view and settings tools.
|
||||
| Web investigation | `browser,pcap,session,javascript-runtime` | page, bundle and session inspection, packet captures, JS runtime observation, evidence workflow |
|
||||
| Android application analysis | `android,adb,apktool,session` | class and method inspection, device bridge, resource decoding; add `frida` for dynamic instrumentation, `jeb` for JEB projects, `flutter` for Flutter builds |
|
||||
| Native macOS or iOS binary | `native,artifact,session` | Mach-O, signature and property-list inspection plus container extraction |
|
||||
| Native analysis of a known function | `session,enhanced,application` | open a binary, analyze a known function, page retained pseudocode, and export Evidence; add `direct` for focused procedure search or assembly |
|
||||
| Managed .NET assembly | `managed,session` | managed member comparison and reconstruction against the evidence workflow |
|
||||
| Electron or browser-hosted JavaScript application | `browser,electron,application,javascript-runtime,session` | runtime targets, live page inspection, bundle and capture tools, feature tracing, JavaScript semantics and reconstruction obligations |
|
||||
| Evidence bookkeeping only | `session` | open and close binaries, bundles, comparisons, unknown tracking |
|
||||
@@ -2027,20 +1822,15 @@ profile and the advertised form that profile produces. `full`, `compact`, and
|
||||
bounded schemas clients receive; `off` keeps the canonical output contracts in
|
||||
the digest while advertising nothing.
|
||||
|
||||
Some clients reject a complete `tools/list` frame or HTTP SSE event before
|
||||
they load any tools. The opt-in `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact`
|
||||
setting bounds every advertised output schema to 4,096 bytes; `off` omits `outputSchema` entirely
|
||||
Some stdio hosts reject a complete `tools/list` frame before they load any
|
||||
tools. The opt-in `REA_MCP_OUTPUT_SCHEMA_PROFILE=compact` setting bounds every
|
||||
advertised output schema to 4,096 bytes; `off` omits `outputSchema` entirely
|
||||
(the measured pi-web-ui failure and catalog breakdown are in
|
||||
[#2512](https://github.com/morluto/rea/issues/2512)). The default `full` profile
|
||||
is unchanged. Both settings keep the same named canonical tool contract for
|
||||
server-side result validation, and both still return complete
|
||||
`structuredContent` and text results.
|
||||
|
||||
For the 1 MiB FastMCP SSE limit reported by Unsloth in
|
||||
[#9093](https://github.com/morluto/rea/issues/9093), see the
|
||||
[tested catalog configuration](client-configuration.md#fastmcp-and-unsloth-sse-catalog-limits).
|
||||
Hot/cold selection alone does not bound later catalog refreshes.
|
||||
|
||||
`compact` keeps the complete validation structure when it fits the budget.
|
||||
Otherwise it advertises the root property shape, first-level reference-resolved
|
||||
types, first-level guidance, and the root's `required`/`additionalProperties`
|
||||
@@ -2152,12 +1942,3 @@ skill and portable conformance projections in the `generated-docs` artifact.
|
||||
These outputs describe the exact source revision being built; they are not
|
||||
checked-in snapshots. For a running server, `binary_session` remains the
|
||||
authoritative source of catalog identity and tool availability.
|
||||
|
||||
## Offline Playwright traces
|
||||
|
||||
`inspect_playwright_trace` reads a local v10 trace ZIP and defaults to a retained
|
||||
summary. `playwright-trace` views select source-located action boundaries, console
|
||||
and network facts, resource references and diagnostics; long text has bounded
|
||||
previews and character pages. Import does not contact recorded URLs or use a
|
||||
browser. See [offline Playwright traces](./playwright-traces.md) for coverage,
|
||||
resource policies and the shared CLI workflow.
|
||||
+6
-12
@@ -61,18 +61,12 @@ locator unreadable.
|
||||
| `audit_residual_unknowns` | Audit current residual-unknown heads and evidence-qualified resolution. |
|
||||
| `prepare_bounded_process_capture` | Design a bounded controlled process experiment before execution. |
|
||||
|
||||
A `REA_MCP_TOOL_FAMILIES` scope advertises a workflow when its required tools
|
||||
belong to selected families. Optional APK, managed, Apple-resource, and archive
|
||||
routes in `investigate_feature` are omitted when their tools are disabled;
|
||||
they do not make the existing native and JavaScript workflow unavailable.
|
||||
Apple plist guidance requires both the session opener and the native plist
|
||||
reader. Interface Builder, asset catalog, and keyed archive guidance requires
|
||||
the opener and the artifact readers. An opener alone never advertises an Apple
|
||||
resource route; opening resources switches away from the active binary session.
|
||||
Workflows missing a required tool are omitted from `prompts/list`, and
|
||||
`prompts/get` reports them as not found. Rendering, the server identity digest,
|
||||
and `rea mcp doctor` use the same scoped contracts, so guidance never directs
|
||||
the agent to a disabled call.
|
||||
A `REA_MCP_TOOL_FAMILIES` scope advertises a workflow only when every tool in
|
||||
its suggested steps belongs to a selected family. Partially available
|
||||
workflows are omitted from `prompts/list` — `prompts/get` reports them as not
|
||||
found — so a rendered prompt never directs the agent to a disabled call. The
|
||||
server identity digest and `rea mcp doctor` prompt inventory follow the same
|
||||
scope.
|
||||
|
||||
Every rendered prompt provides optional starting points using current REA tool
|
||||
names. Agents can call tools directly, skip irrelevant suggestions, and inspect
|
||||
|
||||
@@ -46,18 +46,6 @@ fingerprint and the same known architecture. Extents are never guessed from the
|
||||
next entry, and relocations are not normalized. A larger mapped read can serve
|
||||
several observed bodies; only needed ranges need to be captured.
|
||||
|
||||
For a matched function, its `evidence_links` include the selected byte reads
|
||||
that covered its body. Request the `function-matches` view with
|
||||
`collection: "byte-sources"` to page the selected `read_bytes` source IDs,
|
||||
sides and limitations; intersect those IDs with the function's links. This
|
||||
distinguishes, for example, Ghidra initialized-memory bytes from file bytes.
|
||||
Identical overlapping reads retain the selected covering source, not every
|
||||
corroborating read. Pages preserve the original source limitations and may end
|
||||
early when the response budget is reached. If one source is too large for a
|
||||
page, the error identifies its Evidence and the next offset; inspect the
|
||||
original matching Evidence or export it if retained, then resume at that
|
||||
offset. Older matching Evidence may not record this source selection.
|
||||
|
||||
This first slice reuses existing range observations. Stripped builds can require
|
||||
many lightweight range queries when those facts are not retained yet. Matching
|
||||
does not hide that acquisition cost or silently start those queries. Full
|
||||
|
||||
@@ -43,17 +43,6 @@ focused tools such as `list_procedures` and `analyze_function` on the same sessi
|
||||
| What did the declared runtime scenario observe? | [Desktop](#native-desktop-observation) or [call observation](#native-call-observation) |
|
||||
| What can this provider and host establish? | [Provider boundaries](#provider-and-verification-boundaries) |
|
||||
|
||||
For large pseudocode, request `procedure_pseudo_code` with `detail: "summary"`
|
||||
on the first MCP call. It retains the complete observation and returns the
|
||||
original selection, text availability and length without the body. Use
|
||||
`normalized_result.parent_evidence_id` with `inspect_analysis_view` and
|
||||
`view: {kind: "native", facet: "pseudocode", offset: 0, limit: 20000}`;
|
||||
follow `coverage.next_offset` for later pages. Offsets and limits count UTF-16
|
||||
code units, not lines or tokens. Pages reuse the same observation without
|
||||
repeating decompilation. Null and empty text remain distinct. Omitted or
|
||||
`complete` detail preserves full inline delivery. Summary delivery requires a
|
||||
retaining session and does not reduce the work or memory of decompilation.
|
||||
|
||||
## Exact CLI selectors
|
||||
|
||||
`function`, `instructions` and `decompile` accept `--procedure=<name-or-address>`;
|
||||
@@ -139,16 +128,6 @@ rea inspect-native-data-type <binary> --type /MyStruct --provider ghidra
|
||||
rea inspect-native-data-type <binary> --address <typed-data-address> --provider ghidra
|
||||
```
|
||||
|
||||
Ghidra bit-field components may have a `type_id` that is not a registered
|
||||
database type. When `bit_field` is present, `base_type_id` is the provider's
|
||||
base type and the lookup fields report whether the manager resolves equivalent
|
||||
types at those exact paths. `bit_size` is the effective width;
|
||||
`declared_bit_size` can be larger. `bit_offset` is the least-significant-bit
|
||||
offset within the base type, not a byte-order-independent offset from the start
|
||||
of the containing structure. Account for the component's storage size and byte
|
||||
order before applying that offset to a larger word. Missing `bit_field` details
|
||||
are unreported, not proof that a component is not a bit-field.
|
||||
|
||||
Instruction facts include bytes, length, decoder text, ordered register/scalar/
|
||||
address tokens, typed references and flow destinations. Mid-instruction, data,
|
||||
outside-memory and unlisted addresses have separate outcomes. For Ghidra,
|
||||
@@ -247,23 +226,8 @@ the index retains the complete selected names, addresses, provider facts and
|
||||
caveats. Failure details retain at most 32 rows within 2,048 JSON-encoded
|
||||
UTF-8 bytes; names may be omitted to keep complete addresses and reasons. The
|
||||
index retains every failure and its complete observed name.
|
||||
The manifest reports the procedure-inventory limitations of the current call in
|
||||
`inventory_limitations`. `decompile-index.json`'s `latest_run` records that
|
||||
call's filters, selected and excluded counts, and inventory limitations; older
|
||||
runs remain represented only by their retained function records, so a later
|
||||
inventory warning is not presented as the history of every file.
|
||||
Each function's `decompile-index.json` record, whatever its status, keeps the
|
||||
complete limitations of the observations it used.
|
||||
When available, `provider_source` records the source of that attempt's
|
||||
pseudocode observation: a file path, SHA-256 and byte range, or a
|
||||
single-function decompile. The latter describes the method, not a unique run. A file
|
||||
descriptor records the source as observed; the file may since have changed or
|
||||
been removed, so compare its current digest before using it. The descriptor
|
||||
does not authenticate the journal. `status: "unknown"` distinguishes
|
||||
an unavailable source from an existing matching output whose original producer
|
||||
is unknown. Older journal records can omit this field; omission is unknown.
|
||||
Failed attempts retain their source in the append-only journal when the
|
||||
provider supplied one, even if a later attempt succeeds. The returned Evidence keeps
|
||||
complete limitations of the observations it used. The returned Evidence keeps
|
||||
at most eight first-observed distinct per-function provider limitations within an 8,000-byte JSON-encoded UTF-8 text budget and counts omitted observations. Repeated omitted text is counted per observation, not as a distinct caveat. This summary does not retain every omitted string in memory. A fresh output directory must not already exist. `--resume`
|
||||
continues an export for the same target SHA-256, provider, analysis profile,
|
||||
and layout; it requires the directory to exist, and an export without a known
|
||||
@@ -336,20 +300,6 @@ relationships but reports their completeness as unknown. In particular, a resolv
|
||||
tail call observed in one direction does not prove that the reverse inventory is
|
||||
complete. An empty Hopper inventory cannot establish that no such call exists.
|
||||
No whole-binary reverse scan is performed.
|
||||
|
||||
For large native traces, `trace_feature` accepts `detail: "summary"`. It retains
|
||||
the complete trace and returns counts and its parent Evidence ID. Use
|
||||
`inspect_analysis_view` with that ID and `view: {kind: "native-feature-trace",
|
||||
collection: "references", offset: 0, limit: 64}` to page the original rows;
|
||||
`matches` and `unknowns` are separate collections. Matches accept an exact
|
||||
`address`; references accept exact `source_address` and `target_address`
|
||||
filters. Row indices refer to the retained collection, and page coverage does not establish provider discovery
|
||||
completeness. `list_unknowns` pages the independently managed question heads.
|
||||
In the one-shot CLI, `rea trace ... --detail summary --evidence_output PATH`
|
||||
stores the trace and its linked source Evidence for later
|
||||
`inspect-analysis-view` calls. This bundle does not include the CLI session's
|
||||
Unknown registry; its original question texts remain available in the trace's
|
||||
`unknowns` pages.
|
||||
Regex searches use ECMAScript Unicode syntax in a cancellable worker with a
|
||||
matching deadline and a worker old-generation heap. Deadline or cancellation
|
||||
stops matching while leaving the Hopper API available. The deadline is
|
||||
@@ -484,18 +434,6 @@ nodes and edges. Node payloads are bounded to 8 MiB and serialized results to
|
||||
32 MiB. Pagination returns edges whose source is on the node page and preserves
|
||||
stable IDs for endpoints outside that page.
|
||||
|
||||
Procedure, node and edge `evidence_id` values identify the source function
|
||||
observations used by the trace. In an MCP session, retrieve them from the
|
||||
Evidence bundle for inspection or export. A replayed source keeps the original
|
||||
observation's identity; the trace also reports that replay occurred. Older
|
||||
cached value traces are not replayed under the revised workflow profile, while
|
||||
their historical Evidence remains available.
|
||||
|
||||
The one-shot CLI accepts `--evidence_output <new-path>` to save the trace and
|
||||
the transitive source Evidence closure named by its links. The destination is
|
||||
never overwritten. `--detail summary` requires `--evidence_output`; the bundle
|
||||
still retains the complete trace and source Evidence.
|
||||
|
||||
Within one value trace, each exact procedure selector is attempted at most once,
|
||||
including failed analyses. A failed seed returns the provider's original typed
|
||||
failure and corrective guidance; it cannot establish a trace or target identity.
|
||||
@@ -506,10 +444,8 @@ resources it owns stops the trace: the result is that provider's own
|
||||
`cleanup_incomplete` failure with its resources and any bound cleanup retry, and the work
|
||||
admitted before the stop is retained as partial Evidence with a located unknown
|
||||
at the stopping read. A successful seed without a native def-use
|
||||
model still returns its procedure and an explicit coverage unknown. An unrecorded
|
||||
facet stays unknown; when the provider explicitly reports unavailability, the trace
|
||||
retains its reason. REA's Kuna provider explicitly reports that it does not supply
|
||||
native def-use observations. A new trace can retry failed selectors.
|
||||
model still returns its procedure and an explicit coverage unknown. A new trace
|
||||
can retry failed selectors.
|
||||
|
||||
Paging and traversal completeness are separate facts. `page_truncated` means the
|
||||
page omits retained nodes, and the edges they source, before `offset` or after
|
||||
@@ -927,13 +863,6 @@ complete observation.
|
||||
section lengths and unsupported non-PowerPC big-endian CPU declarations return
|
||||
`unsupported_target` with the selected path and precise reason. Recovery
|
||||
selects a well-formed or supported artifact and reopens that target.
|
||||
An unreadable adopted-protocol sublist leaves its class, category or
|
||||
`objc_protocol_records` entry available with partial decode status and
|
||||
`protocol_list_unreadable`. Independently decoded metadata and any readable
|
||||
protocol-name prefix remain available; an empty partial list does not
|
||||
establish that its owner adopts no protocols. Existing list and record
|
||||
limits remain in effect. Reader v3 uses a new analysis profile; snapshots
|
||||
bound to reader v2 are incompatible and must be regenerated for this recovery.
|
||||
The byte reader holds the whole selected file in memory, so it admits files up
|
||||
to 64 MiB / 67108864 bytes by default. Select a larger budget with
|
||||
`max_input_bytes` (CLI `--max-input-bytes`), at most 1 GiB / 1073741824
|
||||
@@ -1218,8 +1147,7 @@ is present. A denied trace reports the `debugger-attach-denied` reason.
|
||||
Install a Ghidra 12.x (>=12.1) release and the 64-bit full JDK it declares, then configure REA to
|
||||
use them. Ghidra analysis supports Linux x64/arm64 and macOS x64/arm64; each installation requires
|
||||
the matching native decompiler. Experimental Windows x64 P0 admits native
|
||||
x86, x86-64, ARMNT (Thumb-2) and ARM64 PE applications and DLLs, [DOS MZ
|
||||
executables](ghidra-dos.md), plus explicitly selected
|
||||
x86, x86-64, ARMNT (Thumb-2) and ARM64 PE applications and DLLs, plus explicitly selected
|
||||
[raw NMOS 6502 images](raw-binary.md#nmos-6502) with a caller-supplied load address,
|
||||
on fixed local NTFS volumes. Other raw CPU families remain unsupported on Windows.
|
||||
The boundary uses bundled Job Object ownership, protected runtime DACLs, and
|
||||
@@ -1245,11 +1173,6 @@ and slice commitments establish the same canonical entry. The result reports
|
||||
remain separate dimensions. Equal numeric addresses without applicable coordinate
|
||||
commitments do not establish same-entry correspondence. Inspect the retained
|
||||
source Evidence and coverage when correspondence remains ambiguous.
|
||||
When both inputs authenticate to the same Evidence ID and an applicable entry
|
||||
comparison is unavailable, the match reports `method: "same-evidence"`.
|
||||
This identifies one selected observation, not a correspondence between distinct
|
||||
records or proof of complete analysis. Unavailable facets and unestablished
|
||||
call coverage remain unknown.
|
||||
To aggregate already-retained comparison results, pass their exact IDs to
|
||||
`find_changed_behavior`, for example:
|
||||
|
||||
@@ -1476,7 +1399,7 @@ procedures. The CLI equivalent is
|
||||
Use `start`/`end` to constrain the inclusive address window, and continue with
|
||||
`next_cursor` until `complete` is true. Inspect `searched` for page coverage and
|
||||
skipped uninitialized blocks. A missing match does not establish absence from
|
||||
uninitialized or excluded memory. See
|
||||
uninitialized memory or across block boundaries. See
|
||||
[Ghidra byte search](ghidra-provider.md#masked-byte-search) for mask and coverage
|
||||
semantics. This query never executes or modifies target bytes.
|
||||
|
||||
|
||||
+2
-6
@@ -82,12 +82,8 @@ override. Asset and argument decoding, padding scans, and inventory construction
|
||||
yield in bounded batches and honor caller cancellation. Asset count and name
|
||||
budgets bound retained metadata independently of stored member body sizes. The PE
|
||||
name probe reads headers from the first 64 KiB of the carrier; a PE whose
|
||||
headers extend beyond that is left to native classification. ELF note tables
|
||||
are read for at most one note per 12 carrier bytes in total, the most that
|
||||
disjoint tables can hold, so program headers that repeat the same notes do not
|
||||
rescan them once per header. Past that count the remaining tables are
|
||||
unreadable: an ELF without a confirmed blob keeps its native classification,
|
||||
and one with a blob fails closed. Snapshot and code-cache bytes remain opaque.
|
||||
headers extend beyond that is left to native classification. Snapshot and
|
||||
code-cache bytes remain opaque.
|
||||
|
||||
## Verification
|
||||
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
# Offline Playwright traces
|
||||
|
||||
Use `inspect_playwright_trace` or `inspect-playwright-trace` with an existing
|
||||
local Playwright **trace v10** ZIP. Import is offline: recorded URLs are not
|
||||
requested, and no browser, application or trace viewer is started.
|
||||
|
||||
```sh
|
||||
rea inspect-playwright-trace /absolute/trace.zip --json
|
||||
rea inspect-playwright-trace /absolute/trace.zip --detail summary \
|
||||
--evidence_output /absolute/new-evidence.bundle.json --json
|
||||
```
|
||||
|
||||
MCP defaults to a compact summary and retains complete supported Evidence.
|
||||
CLI defaults to complete delivery; summary and selected pages require an absent
|
||||
`--evidence_output` destination, so later queries can read the original parent.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_playwright_trace",
|
||||
"arguments": {
|
||||
"path": "/captures/trace.zip",
|
||||
"view": {
|
||||
"kind": "playwright-trace",
|
||||
"collection": "events",
|
||||
"kinds": ["network"],
|
||||
"fields": ["method", "url", "status"],
|
||||
"limit": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`view` takes precedence over `detail`. The retained parent can be queried with
|
||||
`inspect_analysis_view`; CLI can use the same request against an exported
|
||||
Evidence bundle with `inspect-analysis-view`.
|
||||
|
||||
## Facts, selection and text
|
||||
|
||||
The `playwright-trace` view offers:
|
||||
|
||||
| Collection | Available selection |
|
||||
| ------------- | ---------------------------------------------------------------------- |
|
||||
| `events` | Batch event/stream/call IDs, event kinds, fields, row offset and limit |
|
||||
| `streams` | Batch stream IDs, fields and row pages |
|
||||
| `members` | Batch member IDs, exact archive paths, fields and row pages |
|
||||
| `diagnostics` | Codes, fields and row pages |
|
||||
| `event-text` | One event ID, UTF-16 character offset and limit |
|
||||
|
||||
An omitted filter selects all retained rows; an explicit empty filter selects
|
||||
none. Minimal event rows keep event/stream IDs, source member/line, producer
|
||||
ordinal, kind and credential-redaction status. Page exhaustion describes only
|
||||
the selected facts and never establishes complete recording coverage.
|
||||
|
||||
Default event pages return at most 256 UTF-16 code units in `text_preview`,
|
||||
with explicit text coverage. Continue from that preview's `next_offset` using
|
||||
`collection: "event-text"`, its `event_id`, `offset`, and a character `limit`
|
||||
(default 2,048). Character offsets do not split surrogate pairs. Explicit
|
||||
`fields: ["text"]` returns the complete interpreted text value; `reported`
|
||||
returns the retained supported payload and sidecars remain selectable.
|
||||
Complete Evidence remains accessible through ordinary bundle operations.
|
||||
|
||||
## Source and coverage
|
||||
|
||||
Archive SHA-256 and directory occurrence bind stable member identity. Each event
|
||||
retains its member ID, physical one-based NDJSON line and zero-based physical
|
||||
line ordinal. Headline times retain producer monotonic milliseconds, without
|
||||
aligning stream clocks. Counts derive from the retained supported records. Call IDs are producer values; they do not establish unique actions
|
||||
across streams or imply a successfully completed before/after pair. There is no
|
||||
inferred total ordering or clock alignment between `.trace` and `.network`.
|
||||
|
||||
Member `path` preserves the reader's original ZIP name; `logical_path` separately
|
||||
records its normalized interpretation. Resource references, exact `paths` filters
|
||||
and trace/network profile pairing use original names, so `resources/x` and
|
||||
`./resources/x` remain distinct. Reader limitations remain attached to each member
|
||||
and keep source interpretation partial when present.
|
||||
|
||||
`decoder_profile` names the selected interpretation policy, not a verified source version.
|
||||
Trace members require an initial numeric v10 context header. Other declared
|
||||
versions are refused. Missing, malformed, repeated or ambiguous profiles remain
|
||||
unknown; network members require one unambiguous paired trace profile. Valid
|
||||
siblings of malformed records stay useful, with explicit diagnostics. Unsafe
|
||||
numeric values are not rounded: exact literals stay in sidecars.
|
||||
|
||||
Interpreted facts cover action boundaries (class/method, title, call/page IDs,
|
||||
selected navigation URL/selector, recorded times and available error text),
|
||||
console text/location, network metadata and screencast references. Other action
|
||||
arguments, results, context options, DOM, stacks, unsupported event types and
|
||||
resource contents are outside this version's interpretation. These omissions
|
||||
are scope limitations, not assertions that omitted values are secrets or absent.
|
||||
Original archive bytes remain the complete raw source.
|
||||
|
||||
Known HAR transport credentials and structured network, navigation and console-location
|
||||
URL userinfo are excluded using the existing capture projection. Ordinary names, query values, console text and caller data
|
||||
are preserved. Network numeric/redaction pointers are relative to `reported`;
|
||||
combine them with the event source's `record_pointer` (`/snapshot`) to locate the
|
||||
original NDJSON value. Resource-reference pointers already address the physical
|
||||
record.
|
||||
|
||||
Resource references distinguish `present-unread`, `missing`, `ambiguous` and
|
||||
`unavailable`. Presence means directory metadata only: screenshots and bodies are
|
||||
not read or validated. Member `declared_bytes` and `declared_compressed_bytes`
|
||||
come from the ZIP directory; they are not measurements of unread payloads.
|
||||
Only decoded stream members have verified ZIP CRC and
|
||||
size. Duplicate names keep separate occurrence IDs; no filename silently picks
|
||||
a resource. Recording completeness always remains unknown, including after all
|
||||
retained pages have been read.
|
||||
|
||||
## Bounds and verification
|
||||
|
||||
Each decoder admits one archive import at a time; concurrent imports receive a
|
||||
retryable capacity error. Retained queries do not require another import.
|
||||
Resource policies admit a 256-MiB archive, 32 MiB of decoded streams, a
|
||||
conservative 32-MiB retained-facts representation, 4-MiB metadata reads and
|
||||
4-MiB physical lines. JSON parsing additionally checks heap headroom, a
|
||||
131,072-node record allowance and the shared 64-level capture nesting budget.
|
||||
These are resource policies, not Playwright format limits. Paging or field
|
||||
selection does not increase parsing allowances; choose a smaller complete
|
||||
recorder chunk when admission fails.
|
||||
|
||||
The owned fixtures in `tests/fixtures/playwright-trace/` preserve actual
|
||||
Playwright 1.64.1 v10 recorder text. Public CLI/MCP tests exercise advertised
|
||||
schemas, retained and bundle queries without the source file, partial facts,
|
||||
credential exclusion, bounded Unicode previews, and a loopback listener that
|
||||
must receive no import-time requests. Recorder/version/platform compatibility
|
||||
outside this verified profile is unverified.
|
||||
+3
-17
@@ -74,16 +74,6 @@ Read the result's terminal observations, exit/settlement status, and cleanup
|
||||
outcome. [Scenario and result details](#scenario-and-result-details) describe
|
||||
limits, normalization, filesystem coverage, and incomplete observations.
|
||||
|
||||
## Prepare without launching
|
||||
|
||||
Add `"dry_run": true` to the scenario, or pass `dry_run: true` to the MCP tool,
|
||||
to resolve the executable, working directory, observation paths and admission
|
||||
checks without starting a process. The result is planned Evidence with the
|
||||
`rea.process-scenario-plan` predicate, not capture Evidence; comparison tools
|
||||
refuse it as a capture. Use it to review a scenario before separately requesting
|
||||
execution. A planned result does not establish runtime behavior, exit status,
|
||||
cleanup or the inherited ambient environment.
|
||||
|
||||
## Compare two captures
|
||||
|
||||
Compare saved capture Evidence with:
|
||||
@@ -368,13 +358,9 @@ sibling filename such as `tool-helper`; selected parent directories still
|
||||
normalize within descendant paths, including when the working directory or an
|
||||
observation path is spelled with a trailing `/` or `/.`. A selected filesystem
|
||||
root `/` also aliases descendant paths, without rewriting URL separators or
|
||||
HTML closing tags. When no temporary root applies to an observation, no temporary-root
|
||||
path is selected: literal `<no-temporary-root>` input and sampled command text
|
||||
remain unchanged. The comparison contract
|
||||
records these rules as `path-component-boundaries-v4`, so captures made with the
|
||||
earlier rules cannot be compared as if the text had the same meaning. Historical
|
||||
captures remain readable; recapture both sides with the same path normalization
|
||||
version before comparing them.
|
||||
HTML closing tags. The comparison contract
|
||||
records these rules as `path-component-boundaries-v3`, so captures made with the
|
||||
earlier rules cannot be compared as if the text had the same meaning.
|
||||
|
||||
Rendered frame `lines` hold the visible rows after normalization, with trailing
|
||||
U+0020 spaces removed. Blank rows and internal spaces remain. `columns` records
|
||||
|
||||
@@ -35,12 +35,9 @@ inferring POSIX semantics.
|
||||
the decoded PYZ image for PYZ rows. A CArchive directory record starts at
|
||||
`archive_start + toc_offset + directory_record_offset`; its size is
|
||||
`directory_record_size`. PYZ row directory extents remain unknown.
|
||||
In a POSIX carrier, CArchive type `b` retains PyInstaller's executable-member
|
||||
declaration in the generic `executable` fact; type `x` remains non-executable.
|
||||
The Windows bootloader sets no execute bit, so type `b` members of a PE carrier,
|
||||
usually DLLs and `.pyd` extensions, stay non-executable, as do those of an
|
||||
unconfirmed carrier. This comes from the carrier dialect and the archive type
|
||||
code, not the filename or member contents.
|
||||
CArchive type `b` retains PyInstaller's executable-member declaration in the
|
||||
generic `executable` fact; type `x` remains non-executable. This comes from the
|
||||
archive type code, not the filename or member contents.
|
||||
Distinct CArchive rows retain their table-index identities even when they
|
||||
share a logical path. Inventory can preserve both a file and a decoded PYZ
|
||||
directory at that path, including the PYZ children; extraction refuses a
|
||||
@@ -80,17 +77,14 @@ table index; original names remain available in the supplied artifact.
|
||||
Path failures use bounded escaped previews and report the original length;
|
||||
accepted paths and retained source names are not shortened.
|
||||
|
||||
`compare_artifacts` compares node-less records, namely runtime options,
|
||||
dependencies and namespace packages, as declarations using the container,
|
||||
container name, source name and type code, and whether the record is
|
||||
encrypted; their limitations are not compared. Equal declarations can
|
||||
therefore compare unchanged without implying that an external dependency's
|
||||
bytes were found or that unread legacy namespace payloads match. For
|
||||
materialized PyInstaller members, the same fields participate in the metadata
|
||||
delta independently of content identity, including when the member's bytes
|
||||
are unavailable; archive offsets and table positions remain location facts,
|
||||
not declaration identity. Missing declaration facts remain unknown, as does
|
||||
unavailable non-metadata content whose declaration is unchanged.
|
||||
`compare_artifacts` compares these node-less records as declarations using the
|
||||
container, container name, source name and type code. Equal option or dependency
|
||||
declarations can therefore compare unchanged without implying that an external
|
||||
dependency's bytes were found. For materialized PyInstaller members, the same
|
||||
fields participate in the metadata delta independently of content identity;
|
||||
archive offsets and table positions remain location facts, not declaration
|
||||
identity. Missing declaration facts and unavailable non-metadata content remain
|
||||
unknown.
|
||||
|
||||
Request `detail: "summary"` for a compact inspection, then query retained
|
||||
Evidence with `inspect_analysis_view`. An inventory view can select
|
||||
|
||||
+5
-12
@@ -81,14 +81,9 @@ The inline Evidence result contains the artifact path, SHA-256, and byte length.
|
||||
|
||||
- `magic`, the 16-bit little-endian field;
|
||||
- `python_version` and `code_layout`, mapped from the pinned CPython 3.7–3.13
|
||||
marshal table (`py37`, `py38`, or `py311`); 3.11 alpha magics before 3494 used
|
||||
table (`py37`, `py38`, or `py311`); 3.11 alpha magics before 3494 used
|
||||
other code layouts and are unsupported. The 3.7.0a1/a2 magics 3390/3391
|
||||
used 12-byte headers and are outside this reader's supported format. The
|
||||
identity table also recognizes 3.14 (3627) and 3.15 (3666): those magics
|
||||
carry the same packed 16-byte header, so the typed refusal names the observed
|
||||
version and flags, and says that no marshal code layout is pinned for it.
|
||||
Without a pinned layout the code objects cannot be decoded, so a recognized
|
||||
version is still not a successful decode;
|
||||
used 12-byte headers and are outside this reader's supported format;
|
||||
- `flags`, `hash_based`, and `check_source` from PEP 552;
|
||||
- `source_hash_hex` for hash-based pycs, or `source_mtime` and `source_size`
|
||||
for timestamp pycs.
|
||||
@@ -185,11 +180,9 @@ name-table entry (`co_names`, varnames, freevars, cellvars, and
|
||||
Copied constant tables and each reported nested code relationship count
|
||||
against that same object budget. Integer digits and expanded
|
||||
set or dictionary keys, including a shared tuple repeated at each level, count
|
||||
against the string budget. Magics outside the pinned identity table, truncated
|
||||
headers, trailing marshal bytes, and a non-code top-level object are typed
|
||||
failures. A recognized 3.14/3.15 magic is a typed unsupported failure too: the
|
||||
header is qualified from its packed 16-byte fields, but no marshal code layout is
|
||||
pinned, so no code object is reported and the result is never partial success.
|
||||
against the string budget. Magics outside the pinned 3.7–3.13 table,
|
||||
truncated headers, trailing marshal bytes, and a non-code top-level object are
|
||||
typed failures.
|
||||
|
||||
A decoded header does not prove the matching `.py` is present. `python_version`
|
||||
is the magic table, not a running interpreter. This workflow does not recover
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[الموقع](https://rea.tools/ar/) · [الأدلة](https://rea.tools/ar/guides/) · [دراسات الحالة](https://rea.tools/ar/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="انضم إلى مجتمع REA على Discord" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[الموقع](https://rea.tools/ar/) · [الأدلة](https://rea.tools/ar/guides/) · [دراسات الحالة](https://rea.tools/ar/showcase/)**
|
||||
|
||||
[البدء السريع](#البدء-السريع) · [كيف يعمل REA](#كيف-يعمل-rea) · [ما الذي يمكنك تحليله](#ما-الذي-يمكنك-تحليله) · [دراسات الحالة](#دراسات-الحالة) · [إنشاء بيئات التعلم المعزز](#build-rl-environments) · [الأسئلة الشائعة](#الأسئلة-الشائعة) · [أدوات GNU objdump وRizin](../reverse-engineering-tools.md) · [التوثيق](#التوثيق)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -160,7 +160,6 @@ npx rea-agents@latest setup
|
||||
| شيفرة EVM البايتية | محددات التوجيه، وإزاحات البايتات، والمعاملات المستنتجة، وقابلية تغيير الحالة | مدخل محلي يحمل بايتات خامًا أو تمثيلًا سداسيًا عشريًا على Linux x64؛ [دليل EVM دون تشغيل](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Website](https://rea.tools/de/) · [Anleitungen](https://rea.tools/de/guides/) · [Fallstudien](https://rea.tools/de/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Der REA-Community auf Discord beitreten" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[Website](https://rea.tools/de/) · [Anleitungen](https://rea.tools/de/guides/) · [Fallstudien](https://rea.tools/de/showcase/)**
|
||||
|
||||
[Schnellstart](#schnellstart) · [So funktioniert REA](#so-funktioniert-rea) · [Was du analysieren kannst](#was-du-analysieren-kannst) · [Fallstudien](#fallstudien) · [RL-Umgebungen erstellen](#build-rl-environments) · [Häufige Fragen](#häufige-fragen) · [Dokumentation](#dokumentation)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -160,7 +160,6 @@ REA benötigt Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) oder 26+ (>=26.12.1) so
|
||||
| EVM-Bytecode | Dispatch-Selektoren, Byte-Offsets, abgeleitete Argumente und Veränderbarkeit des Zustands | Lokale Eingabe mit Rohbytes oder Hexdarstellung unter Linux x64; [Offline-EVM-Leitfaden](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Ιστότοπος](https://rea.tools/) · [Οδηγοί](https://rea.tools/guides/) · [Μελέτες περίπτωσης](https://rea.tools/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Έλα στην κοινότητα REA στο Discord" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[Ιστότοπος](https://rea.tools/) · [Οδηγοί](https://rea.tools/guides/) · [Μελέτες περίπτωσης](https://rea.tools/showcase/)**
|
||||
|
||||
[Γρήγορη εκκίνηση](#γρήγορη-εκκίνηση) · [Πώς λειτουργεί το REA](#πώς-λειτουργεί-το-rea) · [Τι μπορείς να αναλύσεις](#τι-μπορείς-να-αναλύσεις) · [Μελέτες περίπτωσης](#μελέτες-περίπτωσης) · [Δημιουργία περιβαλλόντων RL](#build-rl-environments) · [Συχνές ερωτήσεις](#συχνές-ερωτήσεις) · [Τεκμηρίωση](#τεκμηρίωση)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -160,7 +160,6 @@ npx rea-agents@latest setup
|
||||
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
|
||||
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Sitio web](https://rea.tools/es/) · [Guías](https://rea.tools/es/guides/) · [Casos prácticos](https://rea.tools/es/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Únete a la comunidad de REA en Discord" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[Sitio web](https://rea.tools/es/) · [Guías](https://rea.tools/es/guides/) · [Casos prácticos](https://rea.tools/es/showcase/)**
|
||||
|
||||
[Inicio rápido](#inicio-rápido) · [Cómo funciona REA](#cómo-funciona-rea) · [Qué puedes analizar](#qué-puedes-analizar) · [Casos prácticos](#casos-prácticos) · [Crear entornos de RL](#build-rl-environments) · [Preguntas frecuentes](#preguntas-frecuentes) · [Documentación](#documentación)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -160,7 +160,6 @@ REA requiere Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) o 26+ (>=26.12.1), adem
|
||||
| Bytecode EVM | Selectores de despacho, desplazamientos en bytes, argumentos inferidos y mutabilidad | Entrada local con bytes sin procesar o representación hexadecimal, en Linux x64; [guía de EVM sin ejecutar](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55" /></a>
|
||||
|
||||
**[وبسایت](https://rea.tools/fa/) · [راهنماها](https://rea.tools/fa/guides/) · [نمونههای عملی](https://rea.tools/fa/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="به انجمن REA در دیسکورد بپیوندید" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[وبسایت](https://rea.tools/fa/) · [راهنماها](https://rea.tools/fa/guides/) · [نمونههای عملی](https://rea.tools/fa/showcase/)**
|
||||
|
||||
[شروع سریع](#شروع-سریع) · [REA چگونه کار میکند؟](#rea-چگونه-کار-میکند) · [چه چیزهایی را میتوان تحلیل کرد؟](#چه-چیزهایی-را-میتوان-تحلیل-کرد) · [نمونههای عملی](#نمونههای-عملی) · [ساخت محیطهای RL](#build-rl-environments) · [پرسشهای متداول](#پرسشهای-متداول) · [مستندات](#مستندات)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -152,7 +152,6 @@ REA به Node.js 22.x (>=22.23.4)، 24.x (>=24.22.1) یا 26+ (>=26.12.1)، به
|
||||
| بایتکد EVM | selectorهای dispatch، offsetهای بایتی، آرگومانهای استنباطشده و قابلیت تغییر وضعیت | دادهٔ خام/hex محلی روی Linux x64؛ [راهنمای EVM آفلاین](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Site web](https://rea.tools/fr/) · [Guides](https://rea.tools/fr/guides/) · [Études de cas](https://rea.tools/fr/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Rejoignez la communauté REA sur Discord" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[Site web](https://rea.tools/fr/) · [Guides](https://rea.tools/fr/guides/) · [Études de cas](https://rea.tools/fr/showcase/)**
|
||||
|
||||
[Démarrage rapide](#démarrage-rapide) · [Comment fonctionne REA](#comment-fonctionne-rea) · [Ce que vous pouvez analyser](#ce-que-vous-pouvez-analyser) · [Études de cas](#études-de-cas) · [Créer des environnements RL](#build-rl-environments) · [Questions fréquentes](#questions-fréquentes) · [Documentation](#documentation)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -160,7 +160,6 @@ REA nécessite Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) ou 26+ (>=26.12.1), ai
|
||||
| Bytecode EVM | Sélecteurs de dispatch, offsets en octets, arguments inférés et mutabilité | Fichier local brut ou hexadécimal sous Linux x64 ; [guide EVM hors ligne](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Situs Web](https://rea.tools/id/) · [Panduan](https://rea.tools/id/guides/) · [Contoh Penggunaan](https://rea.tools/id/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Bergabunglah dengan komunitas REA di Discord" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[Situs Web](https://rea.tools/id/) · [Panduan](https://rea.tools/id/guides/) · [Contoh Penggunaan](https://rea.tools/id/showcase/)**
|
||||
|
||||
[Mulai Cepat](#mulai-cepat) · [Cara Kerja REA](#cara-kerja-rea) · [Hal yang Dapat Anda Analisis](#hal-yang-dapat-anda-analisis) · [Contoh Penggunaan](#contoh-penggunaan) · [Membangun lingkungan RL](#build-rl-environments) · [FAQ](#faq) · [Dokumentasi](#dokumentasi)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -163,7 +163,6 @@ Alat tambahan dan dukungan sistem host bergantung pada target yang dianalisis:
|
||||
| Bytecode EVM | Selector dispatch, offset byte, argumen yang disimpulkan, dan sifat mutabilitas | Pembawa data raw/hex lokal di Linux x64; [panduan EVM offline](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Sito web](https://rea.tools/it/) · [Guide](https://rea.tools/it/guides/) · [Showcase](https://rea.tools/it/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Unisciti alla community REA su Discord" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[Sito web](https://rea.tools/it/) · [Guide](https://rea.tools/it/guides/) · [Showcase](https://rea.tools/it/showcase/)**
|
||||
|
||||
[Avvio rapido](#avvio-rapido) · [Come funziona REA](#come-funziona-rea) · [Cosa puoi analizzare](#cosa-puoi-analizzare) · [Showcase](#showcase) · [Creare ambienti RL](#build-rl-environments) · [FAQ](#faq) · [Documentazione](#documentazione)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -160,7 +160,6 @@ REA richiede Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) o 26+ (>=26.12.1), oltre
|
||||
| Bytecode EVM | Selettori di dispatch, offset dei byte, argomenti inferiti e mutabilità | Carrier raw/hex locale su Linux x64; [guida EVM offline](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[ウェブサイト](https://rea.tools/ja/) · [ガイド](https://rea.tools/ja/guides/) · [事例](https://rea.tools/ja/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="DiscordのREAコミュニティに参加" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[ウェブサイト](https://rea.tools/ja/) · [ガイド](https://rea.tools/ja/guides/) · [事例](https://rea.tools/ja/showcase/)**
|
||||
|
||||
[クイックスタート](#クイックスタート) · [REA の仕組み](#rea-の仕組み) · [分析できる対象](#分析できる対象) · [事例](#事例) · [RL 環境を構築する](#build-rl-environments) · [よくある質問](#よくある質問) · [ドキュメント](#ドキュメント)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -160,7 +160,6 @@ REA には Node.js 22.x(>=22.23.4)、24.x(>=24.22.1)、または 26+ (>=
|
||||
| EVM バイトコード | ディスパッチセレクター、バイトオフセット、推定された引数と状態変更の可否 | Linux x64 上のローカルの生バイト/16 進数の入力ファイル;[オフライン EVM ガイド](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[웹사이트](https://rea.tools/ko/) · [가이드](https://rea.tools/ko/guides/) · [사례](https://rea.tools/ko/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Discord에서 REA 커뮤니티에 참여하세요" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[웹사이트](https://rea.tools/ko/) · [가이드](https://rea.tools/ko/guides/) · [사례](https://rea.tools/ko/showcase/)**
|
||||
|
||||
[빠른 시작](#빠른-시작) · [REA의 작동 방식](#rea의-작동-방식) · [분석할 수 있는 대상](#분석할-수-있는-대상) · [사례](#사례) · [RL 환경 만들기](#build-rl-environments) · [자주 묻는 질문](#자주-묻는-질문) · [문서](#문서)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -160,7 +160,6 @@ REA에는 Node.js 22.x(>=22.23.4), 24.x(>=24.22.1) 또는 26+ (>=26.12.1)와 npm
|
||||
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
|
||||
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[വെബ്സൈറ്റ്](https://rea.tools/) · [ഗൈഡുകൾ](https://rea.tools/guides/) · [ഉദാഹരണങ്ങൾ](https://rea.tools/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Discord-ൽ REA കമ്മ്യൂണിറ്റിയിൽ ചേരുക" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[വെബ്സൈറ്റ്](https://rea.tools/) · [ഗൈഡുകൾ](https://rea.tools/guides/) · [ഉദാഹരണങ്ങൾ](https://rea.tools/showcase/)**
|
||||
|
||||
[ദ്രുതാരംഭം](#ദ്രുതാരംഭം) · [REA എങ്ങനെ പ്രവർത്തിക്കുന്നു](#rea-എങ്ങനെ-പ്രവർത്തിക്കുന്നു) · [എന്തൊക്കെ വിശകലനം ചെയ്യാം](#എന്തൊക്കെ-വിശകലനം-ചെയ്യാം) · [ഉദാഹരണങ്ങൾ](#ഉദാഹരണങ്ങൾ) · [RL പരിതസ്ഥിതികൾ നിർമ്മിക്കുക](#build-rl-environments) · [പതിവുചോദ്യങ്ങൾ](#പതിവുചോദ്യങ്ങൾ) · [ഡോക്യുമെന്റേഷൻ](#ഡോക്യുമെന്റേഷൻ)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -160,7 +160,6 @@ REA-യ്ക്ക് Node.js 22.x (>=22.23.4), 24.x (>=24.22.1), അല്ല
|
||||
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
|
||||
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| WASM artifact | Validation, sections, കൃത്യമായ import/export WAT രൂപങ്ങൾ, decoded WAT | Caller നൽകുന്ന WABT 1.0.42; `inspect_wasm_artifact` |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Strona internetowa](https://rea.tools/pl/) · [Przewodniki](https://rea.tools/pl/guides/) · [Przykłady zastosowań](https://rea.tools/pl/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Dołącz do społeczności REA na Discordzie" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[Strona internetowa](https://rea.tools/pl/) · [Przewodniki](https://rea.tools/pl/guides/) · [Przykłady zastosowań](https://rea.tools/pl/showcase/)**
|
||||
|
||||
[Szybki start](#szybki-start) · [Jak działa REA](#jak-działa-rea) · [Co możesz analizować](#co-możesz-analizować) · [Przykłady zastosowań](#przykłady-zastosowań) · [Tworzenie środowisk RL](#build-rl-environments) · [FAQ](#faq) · [Dokumentacja](#dokumentacja)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -160,7 +160,6 @@ REA wymaga Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) lub 26+ (>=26.12.1) oraz n
|
||||
| Kod bajtowy EVM | Selektory wywołań, przesunięcia bajtowe, wywnioskowane argumenty i mutowalność | Lokalny plik z surowymi bajtami lub zapisem szesnastkowym w systemie Linux x64; [przewodnik EVM offline](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Site](https://rea.tools/pt-BR/) · [Guias](https://rea.tools/pt-BR/guides/) · [Casos de uso](https://rea.tools/pt-BR/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Participe da comunidade REA no Discord" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[Site](https://rea.tools/pt-BR/) · [Guias](https://rea.tools/pt-BR/guides/) · [Casos de uso](https://rea.tools/pt-BR/showcase/)**
|
||||
|
||||
[Início rápido](#início-rápido) · [Como o REA funciona](#como-o-rea-funciona) · [O que você pode analisar](#o-que-você-pode-analisar) · [Casos de uso](#casos-de-uso) · [Criar ambientes de RL](#build-rl-environments) · [Perguntas frequentes](#perguntas-frequentes) · [Documentação](#documentação)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -160,7 +160,6 @@ O REA requer Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) ou 26+ (>=26.12.1), alé
|
||||
| Bytecode EVM | Seletores de despacho, offsets de bytes, argumentos inferidos e mutabilidade | Entrada local com bytes brutos ou representação hexadecimal no Linux x64; [guia de EVM sem execução](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Site web](https://rea.tools/) · [Ghiduri](https://rea.tools/guides/) · [Studii de caz](https://rea.tools/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Alătură-te comunității REA pe Discord" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[Site web](https://rea.tools/) · [Ghiduri](https://rea.tools/guides/) · [Studii de caz](https://rea.tools/showcase/)**
|
||||
|
||||
[Pornire rapidă](#pornire-rapidă) · [Cum funcționează REA](#cum-funcționează-rea) · [Ce poți analiza](#ce-poți-analiza) · [Studii de caz](#studii-de-caz) · [Construiește medii RL](#build-rl-environments) · [Întrebări frecvente](#întrebări-frecvente) · [Documentație](#documentație)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -200,7 +200,6 @@ Uneltele suplimentare și platformele acceptate depind de ținta analizată:
|
||||
| Certificate PE | Intrări Authenticode WIN_CERTIFICATE și câmpuri de identitate ale semnatarilor PKCS#7 și certificatelor X.509 | Inspectare statică integrată a fișierelor PE; [certificate PE](../pe-certificates.md) |
|
||||
| Importuri PE | Numele DLL-urilor importate și încărcate întârziat, ordinale, RVA-uri ale IAT și hash-ul importurilor calculat de pefile | Inspectare statică integrată a fișierelor PE; [importuri PE](../pe-imports.md) |
|
||||
| Note ELF | Proprietari și tipuri PT_NOTE/SHT_NOTE, câmpuri GNU build-id și ABI-tag | Inspectare statică integrată a fișierelor ELF; [note ELF](../elf-notes.md) |
|
||||
| Întărire binară | PIE, NX, RELRO, CFG și indicatori de antet pentru ELF, PE și Mach-O | Inspecție statică integrată a antetelor; [întărire binară](../binary-hardening.md) |
|
||||
| Rapoarte Apple IPS de eroare fatală | Metadate .ips, fire de execuție, imagini binare, registre și clase documentate de erori fatale | Inspectare statică integrată a datelor JSON; [Apple IPS](../apple-ips.md) |
|
||||
| Module de kernel Linux | Chei .modinfo, CRC-uri din __versions, prezența this_module și structura semnăturii de la sfârșitul fișierului | Inspectare statică integrată a fișierelor ELF; [module de kernel](../kernel-modules.md) |
|
||||
| Erlang BEAM | Blocuri IFF, atomi, exporturi, importuri, funcții lambda și antetul Code | Inspectare statică integrată a fișierelor BEAM; [Erlang BEAM](../erlang-beam.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Сайт](https://rea.tools/ru/) · [Руководства](https://rea.tools/ru/guides/) · [Примеры](https://rea.tools/ru/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Присоединяйтесь к сообществу REA в Discord" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[Сайт](https://rea.tools/ru/) · [Руководства](https://rea.tools/ru/guides/) · [Примеры](https://rea.tools/ru/showcase/)**
|
||||
|
||||
[Быстрый старт](#быстрый-старт) · [Как работает REA](#как-работает-rea) · [Что можно анализировать](#что-можно-анализировать) · [Примеры](#примеры) · [Создание RL-окружений](#build-rl-environments) · [Частые вопросы](#частые-вопросы) · [Документация](#документация)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -160,7 +160,6 @@ REA требует Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) или 26+ (>=26.1
|
||||
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
|
||||
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[เว็บไซต์](https://rea.tools/th/) · [คู่มือ](https://rea.tools/th/guides/) · [ตัวอย่างการใช้งาน](https://rea.tools/th/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="เข้าร่วมชุมชน REA บน Discord" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[เว็บไซต์](https://rea.tools/th/) · [คู่มือ](https://rea.tools/th/guides/) · [ตัวอย่างการใช้งาน](https://rea.tools/th/showcase/)**
|
||||
|
||||
[เริ่มต้นอย่างรวดเร็ว](#เริ่มต้นอย่างรวดเร็ว) · [REA ทำงานอย่างไร](#rea-ทำงานอย่างไร) · [สิ่งที่วิเคราะห์ได้](#สิ่งที่วิเคราะห์ได้) · [ตัวอย่างการใช้งาน](#ตัวอย่างการใช้งาน) · [สร้างสภาพแวดล้อม RL](#build-rl-environments) · [คำถามที่พบบ่อย](#คำถามที่พบบ่อย) · [เอกสาร](#เอกสาร)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -160,7 +160,6 @@ REA ต้องใช้ Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) หรื
|
||||
| ไบต์โค้ด EVM | ตัวเลือกฟังก์ชันสำหรับการเรียก ออฟเซ็ตไบต์ อาร์กิวเมนต์และความสามารถในการเปลี่ยนสถานะที่อนุมานได้ | ไฟล์บนเครื่องที่มีไบต์ดิบหรือข้อมูลเลขฐานสิบหกบน Linux x64; [คู่มือ EVM แบบออฟไลน์](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Web sitesi](https://rea.tools/tr/) · [Rehberler](https://rea.tools/tr/guides/) · [Örnek çalışmalar](https://rea.tools/tr/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Discord'da REA topluluğuna katılın" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[Web sitesi](https://rea.tools/tr/) · [Rehberler](https://rea.tools/tr/guides/) · [Örnek çalışmalar](https://rea.tools/tr/showcase/)**
|
||||
|
||||
[Hızlı başlangıç](#hızlı-başlangıç) · [REA nasıl çalışır](#rea-nasıl-çalışır) · [Neleri analiz edebilirsiniz](#neleri-analiz-edebilirsiniz) · [Örnek çalışmalar](#örnek-çalışmalar) · [RL ortamları oluşturun](#build-rl-environments) · [Sık sorulan sorular](#sık-sorulan-sorular) · [GNU objdump ve Rizin araçları](../reverse-engineering-tools.md) · [Belgeler](#belgeler)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -158,7 +158,6 @@ REA; Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) veya 26+ (>=26.12.1) ve npm gere
|
||||
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
|
||||
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Сайт](https://rea.tools/uk/) · [Посібники](https://rea.tools/uk/guides/) · [Приклади](https://rea.tools/uk/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Приєднуйтеся до спільноти REA в Discord" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[Сайт](https://rea.tools/uk/) · [Посібники](https://rea.tools/uk/guides/) · [Приклади](https://rea.tools/uk/showcase/)**
|
||||
|
||||
[Швидкий старт](#швидкий-старт) · [Як працює REA](#як-працює-rea) · [Що можна аналізувати](#що-можна-аналізувати) · [Приклади](#приклади) · [Створення RL-середовищ](#build-rl-environments) · [Поширені запитання](#поширені-запитання) · [Документація](#документація)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -160,7 +160,6 @@ REA потребує Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) або 26+ (>=26
|
||||
| Байт-код EVM | Селектори диспетчеризації, зміщення байтів, виведені припущення про аргументи та змінюваність стану | Локальні необроблені байти або шістнадцяткове представлення у Linux x64; [посібник EVM без запуску](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Website](https://rea.tools/) · [Guides](https://rea.tools/guides/) · [Showcases](https://rea.tools/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Discord par REA community mein shamil hon" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[Website](https://rea.tools/) · [Guides](https://rea.tools/guides/) · [Showcases](https://rea.tools/showcase/)**
|
||||
|
||||
[Jaldi shuru karein](#jaldi-shuru-karein) · [REA kaise kaam karta hai](#rea-kaise-kaam-karta-hai) · [Kya kuch analyze kar sakte hain](#kya-kuch-analyze-kar-sakte-hain) · [Amli misaalein](#amli-misaalein) · [RL environments banayein](#build-rl-environments) · [FAQ](#faq) · [Documentation](#documentation)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -200,7 +200,6 @@ Mazeed tools aur host support target par depend karte hain:
|
||||
| PE certificates | Authenticode WIN_CERTIFICATE entries aur PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
|
||||
| PE imports | Import aur delay-load DLL names, ordinals, IAT RVAs aur pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id aur ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, imports, lambdas aur Code header | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| WASM artifact | Validation, sections, exact import/export WAT forms aur decoded WAT | Aapka diya hua WABT 1.0.42; `inspect_wasm_artifact` |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[Trang web](https://rea.tools/vi/) · [Hướng dẫn](https://rea.tools/vi/guides/) · [Các nghiên cứu điển hình](https://rea.tools/vi/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="Tham gia cộng đồng REA trên Discord" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[Trang web](https://rea.tools/vi/) · [Hướng dẫn](https://rea.tools/vi/guides/) · [Các nghiên cứu điển hình](https://rea.tools/vi/showcase/)**
|
||||
|
||||
[Bắt đầu nhanh](#bắt-đầu-nhanh) · [Cách REA hoạt động](#cách-rea-hoạt-động) · [Những gì bạn có thể phân tích](#những-gì-bạn-có-thể-phân-tích) · [Các nghiên cứu điển hình](#các-nghiên-cứu-điển-hình) · [Xây dựng môi trường RL](#build-rl-environments) · [Câu hỏi thường gặp](#câu-hỏi-thường-gặp) · [Tài liệu](#tài-liệu)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -160,7 +160,6 @@ REA yêu cầu Node.js 22.x (>=22.23.4), 24.x (>=24.22.1) hoặc 26+ (>=26.12.1)
|
||||
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
|
||||
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| Erlang BEAM | IFF chunks, atoms, exports, and tables | Built-in static BEAM inspection; [Erlang BEAM](../erlang-beam.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[網站](https://rea.tools/zh-TW/) · [指南](https://rea.tools/zh-TW/guides/) · [案例](https://rea.tools/zh-TW/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="加入 REA 的 Discord 社群" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[網站](https://rea.tools/zh-TW/) · [指南](https://rea.tools/zh-TW/guides/) · [案例](https://rea.tools/zh-TW/showcase/)**
|
||||
|
||||
[快速開始](#快速開始) · [REA 的工作原理](#rea-的工作原理) · [可以分析什麼](#可以分析什麼) · [案例](#案例) · [建立 RL 環境](#build-rl-environments) · [常見問題](#常見問題) · [文件](#文件)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -161,7 +161,6 @@ REA 需要 Node.js 22.x(>=22.23.4)、24.x(>=24.22.1)或 26+ (>=26.12.1)
|
||||
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
|
||||
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| 已記錄的 Linux 崩潰 | 原始 note 記錄、每個已記錄執行緒的暫存器/訊號及可選的對映候選項 | Linux x64 上由呼叫方提供的 pwntools;可選的 GDB/pwndbg;[已記錄崩潰](../recorded-crashes.md) |
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-82054" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/82054" alt="morluto%2Frea | Trendshift" width="250" height="55"/></a>
|
||||
|
||||
**[网站](https://rea.tools/zh-CN/) · [指南](https://rea.tools/zh-CN/guides/) · [案例](https://rea.tools/zh-CN/showcase/)**
|
||||
|
||||
<a href="https://discord.com/invite/GkcryMnJDM"><img alt="加入 REA 的 Discord 社区" src="https://img.shields.io/badge/Join%20us%20on%20Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white" height="40"></a>
|
||||
|
||||
**[网站](https://rea.tools/zh-CN/) · [指南](https://rea.tools/zh-CN/guides/) · [案例](https://rea.tools/zh-CN/showcase/)**
|
||||
|
||||
[快速开始](#快速开始) · [REA 的工作原理](#rea-的工作原理) · [可以分析什么](#可以分析什么) · [案例](#案例) · [构建 RL 环境](#build-rl-environments) · [常见问题](#常见问题) · [GNU objdump 和 Rizin 工具](../reverse-engineering-tools.md) · [文档](#文档)
|
||||
|
||||
<code>npx rea-agents setup</code>
|
||||
@@ -161,7 +161,6 @@ REA 需要 Node.js 22.x(>=22.23.4)、24.x(>=24.22.1)或 26+ (>=26.12.1)
|
||||
| PE certificates | Authenticode WIN_CERTIFICATE entries and PKCS#7 signer/X.509 identity fields | Built-in static PE inspection; [PE certificates](../pe-certificates.md) |
|
||||
| PE imports | Import and delay-load DLL names, ordinals, IAT RVAs and pefile import hash | Built-in static PE inspection; [PE imports](../pe-imports.md) |
|
||||
| ELF notes | PT_NOTE/SHT_NOTE owners, types, GNU build-id and ABI-tag fields | Built-in static ELF inspection; [ELF notes](../elf-notes.md) |
|
||||
| Binary hardening | PIE, NX, RELRO, CFG and related header flags for ELF, PE and Mach-O | Built-in static header inspection; [binary hardening](../binary-hardening.md) |
|
||||
| Apple IPS crashes | .ips metadata, threads, images, registers and crash classes | Built-in static JSON inspection; [Apple IPS](../apple-ips.md) |
|
||||
| Linux kernel modules | .modinfo keys, __versions CRCs, this_module presence, and signature trailer | Built-in static ELF inspection; [kernel modules](../kernel-modules.md) |
|
||||
| 已记录的 Linux 崩溃 | 原始 note 记录、每个已记录线程的寄存器/信号及可选的映射候选项 | Linux x64 上由调用方提供的 pwntools;可选的 GDB/pwndbg;[已记录崩溃](../recorded-crashes.md) |
|
||||
|
||||
@@ -14,13 +14,7 @@ can run.
|
||||
|
||||
## GNU objdump
|
||||
|
||||
REA sends GNU-style arguments to the selected command but does not verify its
|
||||
implementation or version. Evidence identifies the command profile as
|
||||
`objdump.command` and retains the selected executable; it does not claim that
|
||||
the executable is GNU objdump. Other implementations may accept some commands
|
||||
while differing in supported options and output.
|
||||
|
||||
`inspect_with_objdump` and `rea inspect-with-objdump` capture stdout and stderr as inline Evidence. Artifact inspection Evidence records SHA-256 digests before and after the provider call and whether those observed digests differ; this cannot prove the exact bytes consumed if the artifact changes during execution. Choose one artifact operation: `file_headers`, `section_headers`, `symbols`, `relocations`, `dwarf`, `disassemble`, or `disassemble_all`. `disassemble` maps to `-d`; `disassemble_all` maps to `-D` and has different symbol-boundary semantics. The pathless `inspect_with_objdump {"operation":"architectures"}` MCP call or `rea list-objdump-architectures` CLI command maps to `-i` and produces subjectless Evidence containing the selected command’s output, not a guarantee that any target is supported. GNU objdump uses this option to list its compiled-in BFD targets.
|
||||
`inspect_with_objdump` and `rea inspect-with-objdump` capture stdout and stderr as inline Evidence. Artifact inspection Evidence records SHA-256 digests before and after the provider call and whether those observed digests differ; this cannot prove the exact bytes consumed if the artifact changes during execution. Choose one artifact operation: `file_headers`, `section_headers`, `symbols`, `relocations`, `dwarf`, `disassemble`, or `disassemble_all`. `disassemble` maps to `-d`; `disassemble_all` maps to `-D` and has different symbol-boundary semantics. The pathless `inspect_with_objdump {"operation":"architectures"}` MCP call or `rea list-objdump-architectures` CLI command maps to `-i` and produces subjectless provider-capability Evidence describing BFD targets compiled into the installed binary, not a guarantee that a target is supported.
|
||||
|
||||
Failure to launch the selected executable reports provider unavailability, the
|
||||
configured command and the host error under `boundary: "provider-launch"`.
|
||||
@@ -31,7 +25,7 @@ their rejected fields in input issues before artifact reads or provider launch.
|
||||
|
||||
For DWARF, REA does not follow local debug links by default. Set `follow_debug_links` to `true` to pass `-WK`; external debug-file identities are not bound to Evidence when following links is enabled. The CLI flag is `--follow-debug-links`; `--no-follow-debug-links` or `--follow-debug-links=false` keeps the default. A separate `true` or `false` word after the flag is not its value. REA clears `DEBUGINFOD_URLS` in the objdump child environment to disable debuginfod network access on GNU binutils versions that do not support `-WE`. The complete raw output is authoritative; REA does not normalize it into a version-independent disassembly schema.
|
||||
|
||||
For a known native symbol, use `operation: "disassemble", symbol: "my_function"` in MCP or `--symbol my_function` in CLI. REA passes `--disassemble=my_function` to objdump before collecting output, rather than producing the whole disassembly and filtering it afterward. Omit `symbol` to keep full code-section disassembly. Other operations reject this selector. Use a nonempty, NUL-free name with well-formed Unicode; unsupported values are rejected before reading the artifact or starting objdump. Symbol names are passed unchanged; matching, versioned symbols, aliases and the end of the disassembly follow the selected executable's semantics. Evidence records the selector and actual arguments. An empty match does not establish artifact-wide absence, and the selected observation does not contain the omitted disassembly; request the complete operation when needed.
|
||||
For a known native symbol, use `operation: "disassemble", symbol: "my_function"` in MCP or `--symbol my_function` in CLI. REA passes `--disassemble=my_function` to objdump before collecting output, rather than producing the whole disassembly and filtering it afterward. Omit `symbol` to keep full code-section disassembly. Other operations reject this selector. Use a nonempty, NUL-free name with well-formed Unicode; unsupported values are rejected before reading the artifact or starting objdump. Symbol names are passed unchanged; matching, versioned symbols, aliases and the end of the disassembly follow the installed GNU objdump's semantics. Evidence records the selector and actual arguments. An empty match does not establish artifact-wide absence, and the selected observation does not contain the omitted disassembly; request the complete operation when needed.
|
||||
|
||||
```sh
|
||||
rea inspect-with-objdump ./program disassemble
|
||||
@@ -46,7 +40,7 @@ Captured stdout and stderr are retained up to a 16 MiB combined limit; `output_t
|
||||
|
||||
## GDB
|
||||
|
||||
Persistent GDB sessions, including the MCP workflow and one-shot `rea debug-with-gdb` CLI, are available only on Linux. REA verifies a live inferior's output descriptors through `/proc` before detaching it; it does not claim equivalent descriptor verification on macOS. Windows is also unsupported because owned Job Object cleanup can terminate debugger inferiors. On supported Linux hosts, `start_gdb_session`, `gdb_console`, `gdb_session_status`, and `close_gdb_session` manage a persistent GDB/MI session in MCP. The one-shot CLI requires a caller-selected durable TTY: `rea debug-with-gdb --inferior-tty /dev/pts/7 'file ./program'`. The TTY must remain available after GDB exits so a detached inferior can continue writing. MCP starts GDB with `--nx --quiet --interpreter=mi3`, retries initialization with MI2 if MI3 initialization returns an error and cleanup succeeds, waits for its prompt, and disables auto-loading. Pass optional `inferior_tty` to select the terminal that future inferior runs use; REA configures it before the session becomes ready and checks the exact value with GDB/MI. `gdb_session_status` reports the selected path, whether REA still considers the configuration verified, and whether invalid UTF-8 has been observed in MI bytes (`mi_invalid_utf8`) or stderr (`stderr_invalid_utf8`). Commands that can change the inferior TTY or redirect a run revoke that verification permanently for the session, so close retains GDB ownership if a live inferior remains. The complete built-in inspections `show inferior-tty` and `help inferior-tty` preserve verification; compound commands still use the conservative mutation checks. Closing a session with one live inferior and a verified TTY automatically detaches the target, which can resume execution; MCP clients receive destructive-operation annotations for this behavior. The console remains unrestricted and can run shell commands, change target state, access files, and use network in the REA process user's security context. Raw MI records and console/target/log streams are returned with Evidence; command output is retained up to the configured budget (16 MiB by default) and explicitly marked when truncated. A single console, target or log record over the 16 MiB MI line limit is dropped and the command is marked truncated, while an oversized result or async record still stops the session. GDB startup waits 10000 ms with no runtime override. Each MI command defaults to 120000 ms; its deadline, command-output budget, session capacity and pending-command queue use the [shared debugger launch settings](#debugger-launch-settings). `command_timeout_ms` does not change a persistent debugger session.
|
||||
Persistent GDB sessions, including the MCP workflow and one-shot `rea debug-with-gdb` CLI, are available only on Linux. REA verifies a live inferior's output descriptors through `/proc` before detaching it; it does not claim equivalent descriptor verification on macOS. Windows is also unsupported because owned Job Object cleanup can terminate debugger inferiors. On supported Linux hosts, `start_gdb_session`, `gdb_console`, `gdb_session_status`, and `close_gdb_session` manage a persistent GDB/MI session in MCP. The one-shot CLI requires a caller-selected durable TTY: `rea debug-with-gdb --inferior-tty /dev/pts/7 'file ./program'`. The TTY must remain available after GDB exits so a detached inferior can continue writing. MCP starts GDB with `--nx --quiet --interpreter=mi3`, retries initialization with MI2 if MI3 initialization returns an error and cleanup succeeds, waits for its prompt, and disables auto-loading. Pass optional `inferior_tty` to select the terminal that future inferior runs use; REA configures it before the session becomes ready and checks the exact value with GDB/MI. `gdb_session_status` reports the selected path, whether REA still considers the configuration verified, and whether invalid UTF-8 has been observed in MI bytes (`mi_invalid_utf8`) or stderr (`stderr_invalid_utf8`). Commands that can change the inferior TTY or redirect a run revoke that verification permanently for the session, so close retains GDB ownership if a live inferior remains. The complete built-in inspections `show inferior-tty` and `help inferior-tty` preserve verification; compound commands still use the conservative mutation checks. Closing a session with one live inferior and a verified TTY automatically detaches the target, which can resume execution; MCP clients receive destructive-operation annotations for this behavior. The console remains unrestricted and can run shell commands, change target state, access files, and use network in the REA process user's security context. Raw MI records and console/target/log streams are returned with Evidence; command output is retained up to 16 MiB and explicitly marked when truncated. GDB startup waits 10000 ms and each MI command waits 120000 ms. This release has no runtime override for those deadlines, the 16 MiB command output budget, session capacity, or the pending-command queue. `command_timeout_ms` does not change them.
|
||||
|
||||
With multiple live inferiors, or a live inferior without a verified TTY, manual
|
||||
close requests an explicit disposition and keeps the session ready. Use
|
||||
@@ -79,38 +73,11 @@ close_gdb_session {"session_id":"…"}
|
||||
|
||||
## Rizin
|
||||
|
||||
`start_rizin_debug_session` and `rea debug-with-rizin` require an explicit `inferior_tty` on Linux. REA asks Rizin to launch the target with stdin, stdout and stderr routed to that caller-selected TTY and `dbg.exitkills=false`; the target can therefore continue running, reading input from the TTY and writing to it after REA closes the debugger. This is a visible target input and output effect, and the caller is responsible for selecting a suitable terminal that remains available. REA sends Rizin its commands on Rizin's own stdin, so a target left on that stream would read them. Before closing, REA serializes after the active command, parses the structured `olj` descriptor list, detaches only a unique `ptrace://` descriptor, confirms its absence, reads back `dbg.exitkills=false`, verifies `/proc/<pid>/fd/1` and `/proc/<pid>/fd/2` still identify the selected TTY by device and inode, and refuses to detach while `/proc/<pid>/fd/0` is Rizin's command stream. A target may close or replace its own stdin. If a previously identified inferior exits after Rizin confirms detach, REA verifies that the original process has ended instead of requiring output descriptors from an exited process. Unresolved identity or ambiguous readback keeps the Rizin session owned for retry. After output limits or a failed command interval, cleanup first waits up to 1000 ms for a fresh private NUL-delimited marker, discarding earlier frames before obtaining that new inferior readback. Synchronization never reopens user command admission. A missing marker, unavailable transport, or unknown inferior identity retains the cleanup owner. Caller cancellation keeps its exact reason and typed cancellation outcome even when cleanup remains incomplete; a successful retry resolves that retained cleanup state. Cancellation during final cleanup retains the captured observation and cleanup owner. An already observed startup or protocol failure remains primary and retains a later cancellation as a secondary failure; recovering the transport does not erase that original diagnosis. Persistent Rizin debugger sessions are unavailable outside Linux because this verification depends on `/proc`.
|
||||
`start_rizin_debug_session` and `rea debug-with-rizin` require an explicit `inferior_tty` on Linux. Rizin launches the target with stdout and stderr routed to that caller-selected TTY and `dbg.exitkills=false`; the target can therefore continue running and writing to the TTY after REA closes the debugger. This is a visible target output effect, and the caller is responsible for selecting a suitable terminal that remains available. Before closing, REA serializes after the active command, parses the structured `olj` descriptor list, detaches only a unique `ptrace://` descriptor, confirms its absence, reads back `dbg.exitkills=false`, and verifies `/proc/<pid>/fd/1` and `/proc/<pid>/fd/2` still identify the selected TTY by device and inode. If a previously identified inferior exits after Rizin confirms detach, REA verifies that the original process has ended instead of requiring output descriptors from an exited process. Unresolved identity or ambiguous readback keeps the Rizin session owned for retry. After output limits or a failed command interval, cleanup first waits up to 1000 ms for a fresh private NUL-delimited marker, discarding earlier frames before obtaining that new inferior readback. Synchronization never reopens user command admission. A missing marker, unavailable transport, or unknown inferior identity retains the cleanup owner. Caller cancellation keeps its exact reason and typed cancellation outcome even when cleanup remains incomplete; a successful retry resolves that retained cleanup state. Cancellation during final cleanup retains the captured observation and cleanup owner. An already observed startup or protocol failure remains primary and retains a later cancellation as a secondary failure; recovering the transport does not erase that original diagnosis. Persistent Rizin debugger sessions are unavailable outside Linux because this verification depends on `/proc`.
|
||||
|
||||
Persistent command Evidence retains the caller-selected and resolved startup paths in `parameters.launch_provenance`. These paths describe launch provenance, not the current target after unrestricted commands. `rizin_debug_session_status.output_invalid_utf8` reports whether lossy frame decoding has been observed during the session. Command Evidence sets `output_invalid_utf8` only when loss was observed in frames collected for that command, including retained partial output; earlier startup or command frames do not mark later valid output as lossy.
|
||||
|
||||
Session status retains up to 64 recent protocol frames. Each history entry keeps a complete-character UTF-8 prefix of at most 16 KiB, plus a truncation marker when needed; `recent_output_truncated` reports omitted history. This history budget also accounts for replacement characters from invalid UTF-8. It does not shorten command Evidence, whose output budget defaults to 16 MiB. Rizin debug startup waits 30000 ms with no runtime override. Each command frame defaults to 120000 ms; its deadline, command-output budget, session capacity and pending-command queue use the [shared debugger launch settings](#debugger-launch-settings). The 16777216-byte frame cap and 4096-frame command cap remain fixed. Evidence names the cap that stopped the command. The one-shot `command_timeout_ms` setting does not change a debug session.
|
||||
|
||||
### Debugger launch settings
|
||||
|
||||
GDB and Rizin share these settings in the environment that launches REA:
|
||||
|
||||
| Setting | Default | Accepted decimal integer |
|
||||
| --------------------------------------- | -------------- | ------------------------ |
|
||||
| `REA_DEBUGGER_COMMAND_DEADLINE_MS` | 120000 ms | 1 to 2147483647 ms |
|
||||
| `REA_DEBUGGER_MAX_COMMAND_OUTPUT_BYTES` | 16777216 bytes | 1 to 89303718 bytes |
|
||||
| `REA_DEBUGGER_MAX_SESSIONS` | 8 | 1 to 64 |
|
||||
| `REA_DEBUGGER_MAX_PENDING_COMMANDS` | 16 | 1 to 256 |
|
||||
|
||||
Unset settings keep the defaults. Invalid selections fail configuration instead
|
||||
of falling back. Restart REA after changing its launch environment; these are
|
||||
not `update_analysis_settings` options. For MCP, set the values in the client's
|
||||
REA registration and restart that server.
|
||||
|
||||
These settings also apply to one-shot `rea debug-with-gdb` and
|
||||
`rea debug-with-rizin` commands. They do not change the fixed initial-prompt
|
||||
or owned-cleanup waits, Rizin's fixed frame and frame-count limits, or the
|
||||
limits of `inspect_with_objdump` and `execute_rizin_command`. GDB
|
||||
initialization and normal-close detach MI requests use the configured command
|
||||
deadline, so a shorter selection can also time out those control commands.
|
||||
Startup/detach control replies have a separate 16 MiB allowance that a smaller command-output setting
|
||||
does not lower. A larger command deadline does not establish that a blocked
|
||||
debugger can detach or that cleanup will succeed; retain and inspect any
|
||||
`cleanup_incomplete` result before retrying.
|
||||
Session status retains up to 64 recent protocol frames. Each history entry keeps a complete-character UTF-8 prefix of at most 16 KiB, plus a truncation marker when needed; `recent_output_truncated` reports omitted history. This history budget also accounts for replacement characters from invalid UTF-8. It does not shorten command Evidence, which has its own 16 MiB output budget. Rizin debug startup waits 30000 ms and each command frame waits 120000 ms. This release has no runtime override for those deadlines, the 16777216 byte frame cap, the 16777216 byte command-output cap, the 4096 frame command cap, session capacity, or the pending-command queue. Evidence names the cap that stopped the command. The one-shot `command_timeout_ms` setting does not change a debug session.
|
||||
|
||||
```sh
|
||||
rea debug-with-rizin --inferior-tty /dev/pts/7 ./program dr
|
||||
@@ -124,120 +91,6 @@ rea execute-rizin-command ./program iI
|
||||
|
||||
Captured stdout and stderr are retained up to a 16 MiB combined limit; `output_truncated` marks output beyond that limit. One-shot commands use the `command_timeout_ms` budget described above. That 16 MiB cap has no runtime override.
|
||||
|
||||
### Decoder and map inputs
|
||||
|
||||
Four optional inputs select how Rizin decodes the artifact and where it maps
|
||||
it. REA passes each one to Rizin before the artifact path:
|
||||
|
||||
| Input | CLI option | Rizin argument | Example |
|
||||
| -------------- | ---------------- | -------------- | -------------------- |
|
||||
| `asm_arch` | `--asm-arch` | `-a` | `arm`, `x86`, `6502` |
|
||||
| `asm_bits` | `--asm-bits` | `-b` | `16` for Thumb |
|
||||
| `asm_cpu` | `--asm-cpu` | `-e asm.cpu=` | `cortexm` |
|
||||
| `base_address` | `--base-address` | `-n -m` | `0x08000000` |
|
||||
|
||||
Rizin 0.9.1 refuses `-e asm.arch=` and `-e asm.bits=` at startup, and Rizin
|
||||
0.7.4 accepts them, so REA uses `-a` and `-b`, which both accept. Rizin applies
|
||||
`-e` settings after `-a` and `-b`, so `asm_cpu` applies to the selected arch.
|
||||
|
||||
`base_address` is the address of file offset 0, as in REA's raw-binary inputs.
|
||||
REA passes it as `-m` together with `-n`, so Rizin maps the whole file there as
|
||||
a raw image and loads no format information, such as sections, symbols or an
|
||||
entry point, even from an ELF file. Without `-n`, a Rizin format loader can
|
||||
claim a raw image and give wrong reads with no error. In Rizin 0.9.1 the AVR
|
||||
loader turns `io.va` off, so every read at the mapped address returns `0xff`.
|
||||
In Rizin 0.7.4 and 0.9.1 the BIOS loader maps the file at another address.
|
||||
A loader can also claim a raw image that you open with only `asm_*` inputs, so
|
||||
pass `base_address`, `0x0` if the image has no known address. Evidence records
|
||||
each input and the complete argument list.
|
||||
|
||||
With `-n`, Rizin has no headers to read, so arch, bits and byte order stay at
|
||||
its defaults unless the call selects them. A Thumb image opened with only
|
||||
`base_address` decodes as plausible AArch64 on an arm64 host. Pass `asm_arch`
|
||||
and `asm_bits` with `base_address`, and for a big-endian image start the command
|
||||
with `e cfg.bigendian=true`.
|
||||
|
||||
REA checks only the spelling. Arch and CPU names contain letters, digits, `_`,
|
||||
`.`, `+` and `-`, and start with a letter or a digit. `asm_bits` is an integer
|
||||
from 1 to 64. `base_address` is `0x` followed by 1 to 16 hexadecimal digits.
|
||||
Rizin owns the names, and its builds differ: for example, the Homebrew build of
|
||||
Rizin 0.9.1 has no `xtensa` or `riscv` plugin, while the official static Linux
|
||||
build of Rizin 0.7.4 has both. The commands `e asm.arch=?` and `e asm.cpu=?`
|
||||
list what the installed Rizin supports. To use another build, set
|
||||
`REA_RIZIN_COMMAND`. When Rizin cannot apply an arch or bits value, it uses
|
||||
another value and exits with status 0. Rizin 0.9.1 keeps its default and
|
||||
reports the value on stderr. Rizin 0.7.4 reports an unknown arch on stderr, but
|
||||
for `arm` it used 16 bits instead of 8 without a message. Both accept any
|
||||
`asm.cpu` value, and `e asm.cpu` then echoes it, so compare it with
|
||||
`e asm.cpu=?`, which lists the CPUs of the selected arch. Evidence therefore
|
||||
states that REA does not confirm the decoder that Rizin used. To check the
|
||||
decoder and the map, start the command with
|
||||
`e asm.arch; e asm.bits; e asm.cpu; e cfg.bigendian; oml`.
|
||||
|
||||
This call reads a Cortex-M flash dump at its address, in Thumb state:
|
||||
|
||||
```sh
|
||||
rea execute-rizin-command ./flash.bin "e asm.arch; e asm.bits; oml; pxw 8 @ 0x08000000" --asm-arch arm --asm-bits 16 --asm-cpu cortexm --base-address 0x08000000
|
||||
```
|
||||
|
||||
Through MCP, the same call is:
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "/firmware/flash.bin",
|
||||
"command": "e asm.arch; e asm.bits; oml; pxw 8 @ 0x08000000",
|
||||
"asm_arch": "arm",
|
||||
"asm_bits": 16,
|
||||
"asm_cpu": "cortexm",
|
||||
"base_address": "0x08000000"
|
||||
}
|
||||
```
|
||||
|
||||
`pxw 8` prints the initial stack pointer and the Reset vector. The Reset vector
|
||||
is a Thumb address with its low bit set. Disassemble the handler at the even
|
||||
address, for example `pd 16 @ 0x08000108` for a Reset vector of `0x08000109`.
|
||||
|
||||
### Multi-segment ESP images
|
||||
|
||||
An app or bootloader image of the ESP32 family (the ESP32, ESP32-S, ESP32-C,
|
||||
ESP32-H and ESP32-P series) starts with the magic byte `0xE9` and holds several
|
||||
segments. ESP8266 images use a different header, and this recipe does not
|
||||
cover them. Each segment loads at its own address, so one `base_address` cannot
|
||||
map the image. Instead, remove the default map and add one `om` map for each
|
||||
segment, in the same command:
|
||||
|
||||
1. Find the load address and the length of each segment, and the file offset
|
||||
of its data. The image header is 24 bytes. Each segment starts with an
|
||||
8-byte header that holds the load address and the data length, both 32-bit
|
||||
little-endian, and its data follows. So the data of the first segment
|
||||
starts at file offset `0x20`, and each later segment header follows the
|
||||
data of the segment before it. `esptool image-info` lists the same
|
||||
segments; esptool v4 spells it `image_info --version 2`. Its `File offs`
|
||||
column is the offset of the 8-byte segment header, so the data starts 8
|
||||
bytes later. A padding segment carries no code or data, and you can skip
|
||||
it.
|
||||
2. Start the command with `e io.va=true`. Reads use the maps only while
|
||||
`io.va` is on, and a Rizin format loader can turn it off.
|
||||
3. Rizin opens the image as file descriptor 3, which `ol` shows. For each
|
||||
segment, add this map, with `r-x` for code and `r--` for data:
|
||||
`om 3 <load address> <length> <data offset> <permissions> <name>`. A map
|
||||
cannot have more permissions than the opened file, which is read-only.
|
||||
4. Select the decoder with `asm_arch` and set `asm_bits` to 32. ESP32, ESP32-S2
|
||||
and ESP32-S3 use Xtensa. The ESP32-C and ESP32-H series and ESP32-P4 use
|
||||
RISC-V. Use the plugin name that `e asm.arch=?` lists for the installed
|
||||
Rizin.
|
||||
|
||||
For example, for an image with an IRAM segment of 8 bytes at `0x40080000` and
|
||||
a DRAM segment of 16 bytes at `0x3ffb0000`:
|
||||
|
||||
```sh
|
||||
rea execute-rizin-command ./app.bin "e io.va=true; om-*; om 3 0x40080000 0x8 0x20 r-x iram0; om 3 0x3ffb0000 0x10 0x30 r-- dram0; oml; pd 4 @ 0x40080000" --asm-arch xtensa --asm-bits 32
|
||||
```
|
||||
|
||||
`om-*` removes the maps that Rizin made when it opened the file, such as the
|
||||
default map of the whole file at address 0. The entry address is the 32-bit
|
||||
little-endian value at file offset 4.
|
||||
|
||||
## Cutter bridge
|
||||
|
||||
Cutter commands use the local REA Python bridge and authenticated loopback messages.
|
||||
|
||||
@@ -69,7 +69,7 @@ previous increment.
|
||||
- Add the `agentenv` CI scope, the reusable `real-agentenv.yml` workflow
|
||||
(`workflow_dispatch` and `workflow_call` with `build_cache_artifact`,
|
||||
`ubuntu-24.04`, SHA-pinned actions, `setup-python` 3.12, a virtual
|
||||
environment with `agentenv-framework==0.9.1431`, the runner's Docker, 30
|
||||
environment with `agentenv-framework==0.9.1334`, the runner's Docker, 30
|
||||
minutes), the `verify:agentenv:environment` script and
|
||||
`scripts/verify-agentenv-environment.mjs`, which requires absolute
|
||||
`REA_AGENT_ENV_COMMAND` and `REA_DOCKER_COMMAND` like every other lane
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
# Desktop application environments
|
||||
|
||||
Goal: REA turns an application installed and used on a Linux desktop into an
|
||||
agent environment's computer application. The real application installs into
|
||||
a computer image, launches to ready, exposes reset, load and get over its own
|
||||
state, and ships with tasks graded through that state.
|
||||
|
||||
The work lands as one PR per step:
|
||||
|
||||
1. Pin the real agent environment lane to the framework release that carries
|
||||
the computer application contract.
|
||||
2. [Capture](capture/spec.md): launch the application on the user's desktop
|
||||
and record its inventory, windows and home-directory state at checkpoints.
|
||||
3. [State view](state-view/spec.md): decide which paths are the application's
|
||||
state and read their formats into a typed JSON view.
|
||||
4. [Export](export/spec.md): write the computer application class, environment
|
||||
card, skill and seed.
|
||||
5. Verification inside an agent environment computer: put, boot, ready, reset
|
||||
equals the seed, grader checks.
|
||||
6. [Tasks](tasks/spec.md): an agent writes a prompt and declarative checks
|
||||
from the observed change, REA keeps the task when the checks fail on the
|
||||
seed and pass on the captured end state, and a run of the bundle measures
|
||||
an agent's solve rate.
|
||||
7. Three applications end to end: Mousepad (plain files), GnuCash saving to
|
||||
SQLite, and Joplin (Electron, SQLite).
|
||||
8. [Vendor packages](vendor-packages/spec.md): closed-source applications from
|
||||
a vendor apt repository, with its verified signing key and the packages of
|
||||
the libraries they load, starting with Typora.
|
||||
9. [Vendor .deb files](vendor-deb-files/spec.md): closed-source applications
|
||||
installed from a downloaded `.deb` file, which the computer downloads from
|
||||
an operator's URL and checks by its captured SHA-256, starting with
|
||||
VariCAD.
|
||||
10. [Document views](document-views/spec.md): documents saved as zip
|
||||
archives (FreeCAD, OpenDocument, Office Open XML, EPUB) read as their
|
||||
entries, checked by name rather than position, namespaced XML included,
|
||||
and numbers compared within a tolerance, starting with FreeCAD.
|
||||
11. [Capture for building](capture-for-building/spec.md): one capture makes
|
||||
the seed and then the change, at checkpoints the export and the task
|
||||
draft are told; apt lists from a user-space `apt-get update` are found;
|
||||
the application does not inherit the computer's control token; and the
|
||||
bundle holds neither the capture host's name nor its user's.
|
||||
12. [Build computer](build-computer/spec.md): an agentenv computer REA keeps
|
||||
up from a request or a bundle, with REA installed inside for building,
|
||||
driven through REA's MCP tools while an agent builds the environment.
|
||||
13. [Build and witness](build-witness/spec.md): REA's capture, task draft
|
||||
and export run inside the build computer with acknowledged checkpoints,
|
||||
the bundle comes back to the host, and the task is witnessed live in a
|
||||
fresh computer of it: seed, edit, near miss and reset, recorded.
|
||||
14. [XML text checks](xml-text-checks/spec.md): XML whose DOCTYPE only names
|
||||
its DTD read as XML, starting with AbiWord, and checks of an element's
|
||||
whole text across the runs an app splits a paragraph into.
|
||||
|
||||
Version 1 covers Linux with X11, applications whose state lives in files or
|
||||
SQLite under the home directory, and real applications from public packages.
|
||||
It uses the user's real state and keeps everything local.
|
||||
@@ -1,21 +0,0 @@
|
||||
# Plan
|
||||
|
||||
1. Domain: the build request schema and the bundle renderer beside the
|
||||
export's renderers, reusing their env.toml, apt source, install script,
|
||||
smoke task and naming helpers; the action schema, per-action argument
|
||||
admission and key translation.
|
||||
2. Helper: `bridge/agentenv/computer.py`, shipped in the package, with its
|
||||
exit statuses (0 torn down, 1 left up, 2 nothing came up).
|
||||
3. Adapter: `src/desktop/AgentEnvComputer.ts` finds the runner's Python,
|
||||
spawns and supervises the helper, reads the handshake, waits for readiness,
|
||||
holds the driver MCP session and the token, serves the recording hooks and
|
||||
releases the computer, removing its containers by name and label when needed.
|
||||
4. Application: `DesktopBuildComputers` renders request bundles into a private
|
||||
folder, starts and holds computers, forwards actions with their log, and
|
||||
closes them, keeping unconfirmed ones for retry.
|
||||
5. MCP: contracts, effects, the `application` family, the capability service,
|
||||
registration, image pass-through in `ToolResultDelivery`, and the MCP-only
|
||||
list for CLI parity.
|
||||
6. Docs and tests: the guide; renderer and key tests; boundary tests with a
|
||||
fake driver and a fake `agent_env` behind the real helper; an opt-in real
|
||||
test with agentenv and Docker.
|
||||
@@ -1,77 +0,0 @@
|
||||
# Build computer
|
||||
|
||||
## Outcome
|
||||
|
||||
An agent that builds a desktop app environment, such as Claude Code, drives a
|
||||
real agentenv computer through REA's own MCP tools: REA keeps the computer up,
|
||||
forwards each computer-use action to its driver and returns the driver's answer,
|
||||
screenshots included, then tears the computer down. The computer starts from a
|
||||
request (the app's install source and desktop entry), with REA installed inside
|
||||
it for building, or from an existing bundle folder.
|
||||
|
||||
## Requirements
|
||||
|
||||
- MCP-only session tools in the `application` family: `start_build_computer`,
|
||||
`act_on_build_computer` and `close_build_computer`, held by one owner per
|
||||
server and closed with it.
|
||||
- Lifecycle: one helper process per computer, `bridge/agentenv/computer.py`,
|
||||
spawned with REA's owned-process supervision and run with the agentenv
|
||||
runner's Python, found from the absolute `REA_AGENT_ENV_COMMAND`. It calls
|
||||
`run_bundle(<bundle>, tasks=["smoke"], keep=True, emulate=False,
|
||||
sandbox="local")`, prints one JSON line (base URL, MCP URL, control token,
|
||||
sandbox id) on a private copy of stdout, sends everything else to stderr,
|
||||
and tears down on stdin EOF, SIGTERM or SIGINT. REA waits for
|
||||
`GET /agentenv/ext/computer-ready/status` with the token, then holds one MCP
|
||||
session to the driver's `/mcp`.
|
||||
- Teardown: closing stdin, then SIGTERM, then the process group. When the helper
|
||||
does not confirm the teardown, REA removes the containers of the exact
|
||||
sandbox id it reported, the one named `agent-<sandbox id>` and any labelled
|
||||
`agentenv.sandbox=<sandbox id>`, through the absolute `REA_DOCKER_COMMAND`. An
|
||||
unconfirmed teardown keeps the computer listed for a retried close.
|
||||
Cancelling a start stops the helper, whose deploy tears down what it began;
|
||||
a helper that reported its failure finishes its own teardown first. The owner
|
||||
holds each start from its helper's spawn: a failed start whose teardown is
|
||||
unconfirmed stays held under the `computer_id` its error names, and shutdown
|
||||
cancels and joins starts in progress before closing every computer.
|
||||
- Diagnostics: the helper's stderr is kept as its last 16 KiB, through the
|
||||
shared supervisor's tail retention, so a failed build reports its error
|
||||
rather than its first package downloads.
|
||||
- The control token never appears in a result, an error or a log; the helper's
|
||||
stderr is redacted of it.
|
||||
- Build bundle from a request: the app from the Ubuntu archive, an apt
|
||||
repository with its key file, or a `.deb` URL with its SHA-256, dependencies
|
||||
and architecture, rendered with the export's install shapes and Python
|
||||
helpers; `Launch(at="demand")` through `gtk-launch`; no seed and no data
|
||||
hooks. When the request sets `electron`, the install rewrites that desktop
|
||||
entry's command with the export's launch policy (`--no-sandbox` right after
|
||||
the program, unless the command already has it) before `gtk-launch`; REA in
|
||||
a build-only layer, with Node.js 24.22.1 downloaded per
|
||||
architecture and checked, and REA's payload a parameter: a local archive
|
||||
shipped in the environment folder (`REA_BUILD_COMPUTER_REA_ARCHIVE`) or,
|
||||
later, a released download.
|
||||
- Actions: one tool with an `action` (screenshot, click, drag, scroll, type,
|
||||
key, wait) and the driver's own arguments, passed through, frame or `norm`
|
||||
coordinates included. Common key spellings become W3C names. An argument the
|
||||
action does not take is refused before sending. Results carry the driver's
|
||||
content blocks unchanged after REA's JSON, which `ToolResultDelivery` measures
|
||||
against the response budget as a whole; a response over it is refused with
|
||||
REA's own result, `computer_id` included, kept in the error. The first
|
||||
screenshot takes `region`, `format` and `quality`. Each action is logged per
|
||||
computer within a fixed allowance: the latest 256 actions in 256 KiB of JSON,
|
||||
oldest dropped first, each string argument cut to 1 KiB and each list to 64
|
||||
items with the cut arguments named, beside a count of every action and the
|
||||
index of the last applied action of each kind.
|
||||
- Recording hooks, internal: start and end `computer-recording/v1` under REA's
|
||||
request id, and fetch the segments through the driver's `data/get` one per
|
||||
request, each answer read within its segment's base64 size and the segment
|
||||
checked against the manifest's size and SHA-256, before teardown. A
|
||||
recording over the caller's allowance, a quarter of the V8 heap by default,
|
||||
is refused before any read.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Capture checkpoints, the apt lists folder, the capture's launch environment
|
||||
and the host scrub, which a sibling change owns.
|
||||
- The witness and its record, which read the action log and recordings.
|
||||
- Remote sandbox providers, emulated architectures, VNC, and reaping orphans
|
||||
left by an earlier REA process.
|
||||
@@ -1,10 +0,0 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Record the outcome, requirements and non-goals.
|
||||
- [x] Render the build bundle from a request and admit actions.
|
||||
- [x] Keep a computer up with the helper and tear it down, by name and label when needed.
|
||||
- [x] Forward actions with images and log them; add the recording hooks.
|
||||
- [x] Register the MCP tools and their catalog entries.
|
||||
- [x] Document the tools.
|
||||
- [x] Add boundary tests with a fake driver and an opt-in real test.
|
||||
- [x] Run the opt-in real test on an amd64 host and record it here.
|
||||
@@ -1,77 +0,0 @@
|
||||
# Verification
|
||||
|
||||
Run on 2026-11-05 on macOS 26.5 with Node v24.22.1 and Python 3.11. Docker and
|
||||
agentenv were not used by the gates or the boundary tests.
|
||||
|
||||
## Tests
|
||||
|
||||
- `src/domain/desktop/appBuild.test.ts`: the bundle for an archive package, an
|
||||
apt repository with its key and a `.deb` with a downloaded REA; the Node.js
|
||||
pin matches REA's; refused desktop entries, credentialed or non-http URLs and
|
||||
relative bundle paths.
|
||||
- `src/domain/desktop/computerActions.test.ts`: key translation, argument
|
||||
pass-through and refusal of an argument the action does not take.
|
||||
- `tests/boundary/mcp/buildComputerMcp.test.ts`, through the public MCP tools
|
||||
with REA's real helper, a fake `agent_env` package on `PYTHONPATH` and a fake
|
||||
driver serving readiness, `computer_*` tools over streamable HTTP,
|
||||
recording and `data/get`: start with readiness polling, the image pass-through,
|
||||
argument pass-through and key translation, a driver refusal, a refused
|
||||
argument, close and a later unknown id; a request bundle with REA's payload,
|
||||
compiled by the fake runner; the missing payload and runner settings; a
|
||||
runner failure with its output; removal by name and label; an unconfirmed teardown
|
||||
retried after Docker recovers; cancellation of a start stopping the helper;
|
||||
a long build's failure reported from the end of its output; a first
|
||||
screenshot too large for the response that keeps `computer_id` for act and
|
||||
close; the first screenshot's options. No result carries the control token.
|
||||
- `tests/boundary/providers/buildComputerRecording.test.ts`: recording start,
|
||||
end and one fetch per segment under REA's request id, the action log, a
|
||||
long session's log kept within its allowance with every action counted and
|
||||
the driver still getting each argument whole, a segment that differs from
|
||||
its manifest, a recording over the allowance refused before any read, and an
|
||||
answer outgrowing its segment's size.
|
||||
- `src/application/desktop/DesktopBuildActionLog.test.ts`: the log names only
|
||||
the arguments it cut, cuts text without splitting a character, and records
|
||||
the outcome of an action dropped while it was pending.
|
||||
- `tests/boundary/providers/buildComputerLifecycle.test.ts`: shutdown cancels
|
||||
and joins a start in progress and refuses later starts; a failed start whose
|
||||
teardown is unconfirmed stays held under the id its error names, keeps its
|
||||
primary failure, and is released by a retried close and by shutdown.
|
||||
- `src/process/ProviderProcessOutput.test.ts`: the supervisor's tail retention
|
||||
keeps each stream's latest bytes, reports checkpoints over dropped bytes as
|
||||
incomplete, and drops a UTF-8 character the cut would split.
|
||||
- `tests/boundary/providers/buildComputerReal.test.ts`: opt-in with
|
||||
`REA_BUILD_COMPUTER_REAL=1`, `REA_AGENT_ENV_COMMAND`, `REA_DOCKER_COMMAND` and
|
||||
`REA_BUILD_COMPUTER_REA_ARCHIVE`; skipped here.
|
||||
|
||||
## Checks against agentenv
|
||||
|
||||
- `python -m agentenv_protocol.computer._declare` from agentenv-framework
|
||||
0.9.1431 declared both a Mousepad archive bundle and a `.deb` bundle with a
|
||||
downloaded REA: packages, downloads per architecture, both run scripts and
|
||||
the demand launch.
|
||||
- `install-rea.sh` ran as root with the network off in a local Ubuntu 24.04
|
||||
container against a Node.js-shaped and an REA-shaped archive: it unpacked
|
||||
both into `/opt/rea`, put `node` and `rea` on `PATH` and removed the
|
||||
archives.
|
||||
|
||||
## Real computer
|
||||
|
||||
Run on 2026-11-05 on Ubuntu 24.04 amd64 with Docker, agentenv-framework
|
||||
0.9.1431 and Node v24.22.1. REA's archive was packed from this tree with
|
||||
`npm pack`, then `npm ci --omit=dev --omit=optional` in its `rea-prod` folder,
|
||||
18.5 MB as `.tar.xz`.
|
||||
|
||||
- `tests/boundary/providers/buildComputerReal.test.ts` passed in 25 s with the
|
||||
image built; the first run, which builds it, took 109 s.
|
||||
- Through `rea mcp` over stdio: `start_build_computer` from the Mousepad
|
||||
request came up in 31 s with its first screenshot as a PNG. Mousepad opened
|
||||
from Applications, Other, `mousepad`; a typed line was saved with `ctrl+s`
|
||||
through the Save As dialog; actions took 6 to 60 ms beside the driver's own
|
||||
typing and waits. `rea --version` answered 6.8.0 in the computer for its user
|
||||
and root. `close_build_computer` took 0.5 s with `teardown: "agentenv"` and
|
||||
left no container. The exported Inkscape bundle started cold in 59 s and
|
||||
closed the same way; a start cancelled at 60 s left nothing up.
|
||||
- With the helper killed, close removed the computer's container by its name,
|
||||
`agent-<sandbox id>`: agentenv labels only its VM and closed-network
|
||||
containers.
|
||||
- No result or log held the control token.
|
||||
@@ -1,32 +0,0 @@
|
||||
# Plan
|
||||
|
||||
1. Capture acknowledgement: `desktopCaptureEventSchema` and
|
||||
`desktopCaptureProgressSchema` in the capture's domain schema; the session
|
||||
reports `window_open` and `checkpoint_refused`, the capture a
|
||||
`checkpoint` event after each checkpoint is taken; the CLI writes the event
|
||||
in its progress line.
|
||||
2. Controller: `bridge/agentenv/capture.py`, with `start`, `mark`, `stop`,
|
||||
`run` and `list`, each answering one JSON line.
|
||||
3. Adapter: `AgentEnvComputer` gains `exec`, `homeFiles`, `appData` and
|
||||
`waitUntilReady`, and reports start progress;
|
||||
`src/desktop/BuildComputerController.ts` runs the controller and fetches a
|
||||
home folder in bounded, checked batches.
|
||||
4. Domain: `buildCapture.ts` (checkpoint names, `seed` and `end`) and
|
||||
`buildWitness.ts` (stages, their order and expectations, the app's state
|
||||
views); `computerActions.ts` gains `reset`.
|
||||
5. Application: `DesktopBuildCapture` (start, mark, stop and draft) with
|
||||
`DesktopBuildExport` and the shared `DesktopBuildCommands`, and
|
||||
`DesktopBuildWitness` with `DesktopBuildWitnessRecord`, held per live
|
||||
computer by `DesktopBuildComputers`, which also resets;
|
||||
`DesktopBuildSteps` serves the step tools by `computer_id`. The output
|
||||
tree writer moves to `DesktopOutputTree.ts` for the export, the build
|
||||
export and the witness record.
|
||||
6. MCP: contracts, effects, CLI-parity list, registration with heartbeat
|
||||
progress.
|
||||
7. Launch from the home in `launchCommand` and the build bundle; the packing
|
||||
script.
|
||||
8. Docs: the guide, the capture, export and build computer guides, this spec.
|
||||
9. Tests first: the witness rules, the reset action, the launch commands, the
|
||||
capture's acknowledgements on Linux, and boundary tests through the MCP
|
||||
tools with a fake computer that runs the real controller against a REA
|
||||
stand-in printing the real CLI's outputs; then the amd64 VM.
|
||||
@@ -1,113 +0,0 @@
|
||||
# Build and witness
|
||||
|
||||
## Outcome
|
||||
|
||||
An agent that builds a desktop app environment on a build computer captures
|
||||
the app with REA inside the computer, marks the seed and the end and gets an
|
||||
acknowledgement for each, drafts the task, exports the bundle with the task
|
||||
it wrote and brings the bundle back to the host, all through REA's MCP tools.
|
||||
It then witnesses the task live in a fresh computer of that bundle: the seed
|
||||
fails a check, the edit made from the prompt passes every check, a near miss
|
||||
fails one, and after `data/reset` one fails again. REA keeps the action log
|
||||
and the recording and writes the record as Evidence.
|
||||
|
||||
## Requirements
|
||||
|
||||
- MCP-only session tools in the `application` family, on a build computer's
|
||||
`computer_id`: `start_build_capture`, `mark_build_checkpoint`,
|
||||
`stop_build_capture`, `export_build_environment` and `check_build_task`, and
|
||||
a `reset` action of `act_on_build_computer`.
|
||||
- Commands in the computer go through agentenv's `computer-exec/v1` route
|
||||
with the control token, which stays in REA and is removed from any output
|
||||
REA reports. A controller, `bridge/agentenv/capture.py`, travels with each
|
||||
command as `python3 -I -c`; it runs REA as the session user in the home
|
||||
without `AGENTENV_*` variables and with `APT_CONFIG` naming apt lists it
|
||||
downloads into `~/.rea-build/apt` before the first capture.
|
||||
- The capture runs detached, its stdin a FIFO it holds open for reading and
|
||||
writing; a shell around it records its exit status. `~/.rea-build` is left
|
||||
out of every walk. A start returns once the capture reports the window
|
||||
open; a refused or early-ended start stops the capture and reports its
|
||||
output.
|
||||
- Acknowledgement: `capture-desktop-application` reports, beside each progress
|
||||
message, an `event`: `window_open`; `checkpoint` with its name, trigger,
|
||||
time, entries walked, changes and retained files and bytes, once it is
|
||||
taken, the baseline, `stop` and `exit` included; and `checkpoint_refused`
|
||||
with the reason. `mark_build_checkpoint` writes `checkpoint <name>` and
|
||||
waits for the event naming it, returning its counts. Names follow a narrow
|
||||
pattern, without the capture's own names; a name already taken is refused.
|
||||
- `stop_build_capture` sends `stop`, waits for the exit status, reads the
|
||||
capture's Evidence and, when `seed` and `end` were acknowledged, runs
|
||||
`describe-desktop-application-task --seed-checkpoint seed --end-checkpoint
|
||||
end` and returns the changed files, their net change and suggested checks.
|
||||
- `export_build_environment` writes the agent's task under `~/.rea-build`,
|
||||
runs `export-desktop-application` with it and the same checkpoints into a
|
||||
new folder there, reports a refused task with the export's own reason,
|
||||
lists the bundle with sizes and SHA-256, fetches it through the driver's
|
||||
`data/get` in answers of at most 8 MiB, checks each file and writes the
|
||||
caller's new or empty folder through the safe output tree.
|
||||
- `check_build_task` reads the one app's `data/get` through the card's
|
||||
child URL, takes the state views beside its files and evaluates the task's
|
||||
checks with `evaluateTaskChecks`, one result per check. With `stage`, the
|
||||
checks file a witness stage: `seed` (with `directory`) starts a recording
|
||||
and must fail a check; `after_edit` must pass all; `near_miss` must fail
|
||||
one; `after_reset` must give each check its seed result. Stages come in order, for one task; the
|
||||
edit and the near miss each need an applied input action since the stage
|
||||
before, `after_reset` a reset as the last change. A stage that does not hold,
|
||||
or the last, ends the witness: the recording ends, its segments are fetched
|
||||
and checked, and `directory` gets the segments and `witness.json`, Evidence
|
||||
of the verdict, the stages, the action log and the recording.
|
||||
- The `reset` action calls the app's `data/reset`, then waits for the
|
||||
computer's readiness checks, since the reset relaunches the app.
|
||||
- Gaps of the build computer's first real run: a packing script for
|
||||
`REA_BUILD_COMPUTER_REA_ARCHIVE`; build and exported apps start in the home
|
||||
(the captured working directory), not in agentenv's folder for the app; and
|
||||
MCP progress notifications every five seconds while a computer starts and
|
||||
while the capture, draft, export and witness tools work.
|
||||
|
||||
- Retries keep ownership: a start whose answer is lost stops its capture,
|
||||
and REA keeps a capture whose stop is unconfirmed, retrying that stop
|
||||
before another start; a lost checkpoint acknowledgement comes back from
|
||||
the capture's log; a stop resumes reading its result or repeats it; the
|
||||
draft and export read the checkpoints from the capture's Evidence; a seed
|
||||
check that failed after opening is asked again with its task and folder;
|
||||
a record whose save failed is written again on a repeat of its stage.
|
||||
- The edit and the near miss count only click, drag, scroll, type and key,
|
||||
and a reset after the last of them refuses the stage.
|
||||
- A `.deb` app's file is kept in the build layer for `--package-file`, and
|
||||
its URL reaches the export's `--package-url` only when the capture's
|
||||
package facts say apt does not install it, as the export itself decides.
|
||||
A failed `apt-get update` reports its status and last lines, and the next
|
||||
start runs it again. The bundle is
|
||||
written as it arrives.
|
||||
- Gaps of a blind validation run: the desktop Trash is never the app's state
|
||||
or a document, so a file the builder trashed is neither owned nor seeded,
|
||||
and the export's limitations name what the Trash held at the seed; a
|
||||
start from a bundle folder writes no bytecode into it; the first
|
||||
screenshot waits, briefly and bounded, until the frame is not one colour.
|
||||
|
||||
- Gaps of the second review: calls on one computer (actions, capture
|
||||
steps, checks and close) run one at a time in arrival order, so a stage's
|
||||
admission, read and filing see no other call, while computers run side by
|
||||
side; a close cancels the call in progress and refuses those waiting. The
|
||||
witness's recording has one request id per computer, so a seed retried
|
||||
after a lost reply asks for the recording it began. A check that fails for
|
||||
want of a readable or whole view is `undecided` and proves nothing; a
|
||||
stage only such checks would settle files nothing and can be checked
|
||||
again. Undecided includes a failure to find a line, row or value that may
|
||||
lie in what a cut view left out; a check that passes on what the view kept
|
||||
stays decided. The export likewise keeps a task only when a check fails on
|
||||
what the seed's view shows. The controller writes a folder's file list to a
|
||||
file that REA reads through `data/get`, so a large bundle's list is not cut
|
||||
at the 1 MiB of a command's output.
|
||||
- The witness reads the build computer's bounded action log: its stage rules
|
||||
use the log's count and the last applied action of each kind, and the
|
||||
record holds the entries the log keeps, `actions_dropped`, and a
|
||||
limitation when the log dropped actions or cut arguments.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Running the exported `verify.py` in the witness; REA's evaluator is the
|
||||
one it mirrors, and the Python parity tests cover the port.
|
||||
- A witness by a separate solving agent, remote sandbox providers, emulated
|
||||
architectures, and choosing the near miss for the agent.
|
||||
- Recording the build itself; only the witness is recorded.
|
||||
@@ -1,13 +0,0 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Record the outcome, requirements and non-goals.
|
||||
- [x] Acknowledge checkpoints from the capture with their counts.
|
||||
- [x] Run capture, draft and export in the computer and bring the bundle back.
|
||||
- [x] Check a task against a live app and witness it with a recording.
|
||||
- [x] Reset the app's data and wait for it to be ready.
|
||||
- [x] Pack REA's runtime, start apps in the home, report start progress.
|
||||
- [x] Document the tools.
|
||||
- [x] Add the tests and run the build and witness on an amd64 host.
|
||||
- [x] Fix the blind validation run's gaps and the export's install source for a `.deb`.
|
||||
- [x] Fix the second review's gaps: one call at a time per computer, the recording's request id, undecided checks and the bundle's file list.
|
||||
- [x] Leave a failure that a cut view may have caused undecided: a missing line, row or value.
|
||||
@@ -1,252 +0,0 @@
|
||||
# Verification
|
||||
|
||||
Run on 2026-11-05: gates and source tests on macOS 26.5 with Node v22.23.1
|
||||
and Python 3.11; Linux tests and the real run on Ubuntu 24.04 amd64 with
|
||||
Docker, agentenv-framework 0.9.1431 and Node v24.22.1.
|
||||
|
||||
## Tests
|
||||
|
||||
Each new test fails on the code before this change, run from that code with
|
||||
only the new tests and fixtures added: on macOS, and on Linux for the
|
||||
capture's acknowledgements.
|
||||
|
||||
- `src/application/desktop/DesktopApplicationCapture.test.ts` (Linux): the
|
||||
capture reports the baseline, `window_open`, `seed` with the counts its
|
||||
checkpoint recorded, a refused second `seed`, then `stop` and `exit`.
|
||||
Before, no event reached the driver and the test ran out of time.
|
||||
- `src/domain/desktop/buildWitness.test.ts`: each stage's expectation, the
|
||||
order of stages with the actions each needs, and the app's state views.
|
||||
- `src/domain/desktop/computerActions.test.ts`: `reset` maps to
|
||||
`data/reset` and takes only `screenshot`.
|
||||
- `src/domain/desktop/appExport.test.ts`, `appBuild.test.ts`: exported and
|
||||
build apps start in the captured working directory, `"$HOME"` for the home.
|
||||
- `tests/boundary/mcp/buildCaptureMcp.test.ts`: through the MCP tools, a
|
||||
request computer whose `computer-exec` runs REA's real controller on this
|
||||
host, against a REA stand-in that acknowledges requests as the capture
|
||||
does and prints what the real CLI wrote for a seed-and-end capture: package
|
||||
lists under `APT_CONFIG` (compressed, as Ubuntu keeps them), the start with
|
||||
a screenshot, `seed` with its counts, a repeated and a reserved name
|
||||
refused, `end`, the draft, and the export with the task, whose files arrive
|
||||
byte for byte. REA ran without `AGENTENV_*`, in the home, with
|
||||
`--exclude ~/.rea-build` and both checkpoints. Steps out of order and a
|
||||
capture on a bundle's computer are refused. No result holds the token.
|
||||
- `tests/boundary/mcp/buildWitnessMcp.test.ts`: a plain check; the witness
|
||||
with a fake app whose state the test changes; the edit refused before an
|
||||
action, `after_reset` before a reset; the reset relaunch; the record with
|
||||
four held stages, the action log, the recording segment and
|
||||
`witness.json` read back as Evidence; a seed that already passes ends the
|
||||
witness as failed and later stages are refused; `directory` missing or not
|
||||
empty is refused.
|
||||
- PR 2's boundary tests now share `tests/fixtures/buildComputerMcp.ts`.
|
||||
|
||||
## Gates
|
||||
|
||||
`typecheck`, `format:check`, `build:cached`, `knip`, `jscpd`, `docs:check`,
|
||||
`docs:build`, `verify:test-discovery`, `verify:architecture-guards`,
|
||||
`verify:module-boundaries` and `mcp-catalog:check` passed. `lint` and
|
||||
`lint:typed` fail only on main's `tests/acceptance/webSourceLocationCli.test.ts`.
|
||||
jscpd's one clone in a touched file is the existing pair of cancellation
|
||||
tests in `DesktopApplicationCapture.test.ts`. The desktop, contract, schema,
|
||||
CLI-parity and build boundary tests passed on macOS (Linux-only cases
|
||||
skipped) and, on Linux, the desktop and build tests ran 467 passed.
|
||||
|
||||
## Real computer
|
||||
|
||||
REA's archive was packed from this tree with `npm run
|
||||
pack:build-computer-runtime`, 16.8 MiB in 54 s. Through `rea mcp` over stdio,
|
||||
driven as the building agent:
|
||||
|
||||
- `start_build_computer` from the Mousepad request built its image and came
|
||||
up in 80 s, with an MCP progress notification every 5 s naming agentenv's
|
||||
stage. A later start took 22.5 s.
|
||||
- `start_build_capture` returned in 7.3 s with Mousepad open under capture.
|
||||
A typed two-line list saved through Save As, which opened on the home, as
|
||||
`~/Documents/todo.txt`; `seed` was acknowledged in 0.45 s (54 entries, 7
|
||||
changes, 4 files retained) and a second `seed` refused; a third line saved,
|
||||
`end` in 0.45 s.
|
||||
- `stop_build_capture` took 1.7 s: checkpoints `baseline`, `seed`, `end`,
|
||||
`stop`, `exit`; `mousepad` 0.6.1-1build2 from `noble/universe`; the draft's
|
||||
one file, `Documents/todo.txt`, with the line added and a suggested
|
||||
`lines_present`. This run counted 0 package lists, because apt keeps them
|
||||
as `*_Packages.lz4`; the count was fixed and a later start reported 15.
|
||||
- `export_build_environment` refused a `line_count` without `exact` or
|
||||
`contains` with the export's reason, then exported the task in 1.3 s: 9
|
||||
files, 50,846 bytes, `failing_on_seed` `dentist` and `one-dentist`, seed
|
||||
`Documents/todo.txt`, nothing left unscrubbed, and the app launched with
|
||||
`cd "$HOME"`. `close_build_computer` took 0.5 s.
|
||||
- A fresh computer of the bundle came up in 72 s. The witness held: seed
|
||||
failed two checks; the line added through Open and Save passed all three;
|
||||
`call the dentists` failed `dentist`; the reset, ready in 0.8 s, relaunched
|
||||
Mousepad and its check failed two again. The record has 15 actions and 5
|
||||
recording segments, 68.6 s and 1.2 MB, written with `witness.json`.
|
||||
- Every computer closed with `teardown: "agentenv"`; Docker held only the
|
||||
local registry it had before. The MCP server's log never names a token.
|
||||
|
||||
## Review fixes
|
||||
|
||||
Run on macOS after the first review; the VM was not used. Each new test
|
||||
failed on the reviewed head, with only the new tests and fixtures added, and
|
||||
passes now. The fake computer now runs a command and then loses its reply
|
||||
when a test asks, fails an app's `data/get` and keeps a relaunched app
|
||||
unready for some polls.
|
||||
|
||||
- `buildWitness.test.ts`: a reset is neither the edit nor the near miss, and
|
||||
a reset after the last input refuses them; a near miss made after a reset
|
||||
is admitted.
|
||||
- `appBuild.test.ts`: the build layer copies a `.deb` to
|
||||
`/opt/rea/package.deb` before the app's own install removes it.
|
||||
- `BuildComputerController.test.ts`: each checked answer is handed over
|
||||
before the next is fetched.
|
||||
- `buildCaptureRetryMcp.test.ts`: a start whose reply was lost stops its
|
||||
capture, and while that stop's reply is lost too REA refuses another start
|
||||
naming the capture, then retries the stop first; a lost checkpoint reply
|
||||
comes back on a repeat; a stop whose draft reply was lost finishes on a
|
||||
repeat and repeats its result, and the export follows; apt's exit 100 and
|
||||
message reach `package_lists_error`; a `.deb` request captures with
|
||||
`--package-file` and exports with `--package-url`.
|
||||
- `buildWitnessMcp.test.ts`: a seed whose read failed is checked again with
|
||||
the one recording; a record whose save was refused is saved on a repeat of
|
||||
`after_reset` without reading the app again; a slow reset sends progress.
|
||||
|
||||
## Validation-run fixes
|
||||
|
||||
Run on macOS after a blind validation run of the build flow and a review
|
||||
finding; the VM was not used. Each new test failed on be30603ec with only it
|
||||
and its fixture changes added, unless noted, and passes now.
|
||||
|
||||
- `stateRoles.test.ts`: a path in `~/.local/share/Trash` has the reason
|
||||
`desktop_trash` and the role `shared_desktop`, whatever else names it,
|
||||
a window title included; `~/.local/share/Trashcan` is not the Trash.
|
||||
- `DesktopApplicationExport.test.ts`: a file named in the window title and
|
||||
moved to the Trash before the seed checkpoint is neither owned nor seeded,
|
||||
and a limitation names both of the Trash's files. Before,
|
||||
`.local/share/Trash/files` was owned and the file seeded, as the run saw
|
||||
with `poster.svg`.
|
||||
- `buildComputerMcp.test.ts`: the fake runner loads each of the bundle's
|
||||
Python files in a child Python as agentenv's `_declare` does, and a start
|
||||
from a bundle folder leaves its file list unchanged. Before,
|
||||
`envs/notes/__pycache__/notes_app.cpython-311.pyc` appeared. In
|
||||
agentenv-framework 0.9.1444 that child is the only Python that imports the
|
||||
bundle's files, and it inherits the helper's environment.
|
||||
- `buildComputerMcp.test.ts`: after two black frames, the start returns the
|
||||
third, drawn one; a `region`, `format` or `quality` applies to a screenshot
|
||||
taken after it. The fake's frame is now a 2x1 PNG of two colours.
|
||||
`PngVisualDiff.test.ts` covers the new one-colour check.
|
||||
- `buildComputerMcp.test.ts`, after review: with every frame black and the
|
||||
third screenshot's reply a minute late, the start returns the second frame
|
||||
at the 10 s bound and reports `waiting for the desktop to draw` as
|
||||
progress meanwhile. Before, the retry kept its 120 s request timeout and
|
||||
progress stopped once readiness passed, so the test ran out of time.
|
||||
- `buildCaptureRetryMcp.test.ts`: a `.deb` request whose captured package
|
||||
apt also offers exports without `--package-url`; one that no apt source
|
||||
offers exports with it, from the real CLI's output for that capture (this
|
||||
case passed before too). `appExportInstall.test.ts`: the new check agrees
|
||||
with the export's own install choice.
|
||||
|
||||
`typecheck`, `format:check`, `build:cached`, `knip`, `jscpd`, `docs:check`,
|
||||
`docs:build`, `verify:test-discovery`, `verify:architecture-guards`,
|
||||
`verify:module-boundaries` and `mcp-catalog:check` passed; `lint` and
|
||||
`lint:typed` fail only on main's `tests/acceptance/webSourceLocationCli.test.ts`.
|
||||
The desktop source tests ran 453 passed (Linux-only cases skipped), and the
|
||||
build and desktop boundary tests 67 passed with the real test skipped.
|
||||
|
||||
Not verified on a real computer: that the first frame not of one colour
|
||||
shows the panel on the computer's desktop.
|
||||
|
||||
## Second review fixes
|
||||
|
||||
Run on macOS after the second review; the VM was not used. Each new
|
||||
boundary test failed on the reviewed head, with only the new tests and
|
||||
fixtures added, and passes now. The fake computer now cuts a command's
|
||||
stdout at 1 MiB as the computer does, loses a recording start's reply when
|
||||
a test asks, and a bundle folder can name the fake computer its start
|
||||
hands out, so one server can hold two.
|
||||
|
||||
- `buildWitnessMcp.test.ts`: two `after_edit` checks sent together file
|
||||
one stage, the other is refused as out of order, and `after_reset` stays
|
||||
refused until a near miss. Before, both were filed, and after a reset the
|
||||
record held with the stages `seed`, `after_edit`, `after_edit`,
|
||||
`after_reset`. An `after_reset` check sent while a reset relaunches the
|
||||
app waits for it and is admitted; before, it ran during the reset and was
|
||||
refused. A seed retried after its recording's start reply was lost asks
|
||||
for the same request id; before, a new one. A seed and a near miss whose
|
||||
only failing check could not read its file are refused and filed once
|
||||
the file reads; before, the read error was filed as the failure.
|
||||
- `buildComputerMcp.test.ts`: a screenshot sent behind a slow reset on one
|
||||
computer answers after it, while a screenshot on another computer answers
|
||||
first, and the waiting action sends progress; a close during a reset
|
||||
cancels it and refuses an action and a capture step waiting behind it,
|
||||
and tears the computer down. Before, the screenshot answered first and
|
||||
the close left the reset running.
|
||||
- `buildComputerLifecycle.test.ts`: the owner's shutdown cancels a call in
|
||||
progress and the one waiting behind it.
|
||||
- `buildCaptureRetryMcp.test.ts`: an export whose bundle's file list is
|
||||
about 1.1 MB brings every file back. Before, the list was cut at 1 MiB and
|
||||
the export failed with the controller not answering.
|
||||
- `DesktopBuildTurns.test.ts`: calls run one at a time in arrival order, a
|
||||
call that throws frees its turn, a call cancelled while it waits keeps its
|
||||
place until the one before it ends, and a close runs once the call it
|
||||
cancelled has ended.
|
||||
- `buildWitness.test.ts` and `appTaskChecks.test.ts`: the three-way stage
|
||||
verdict, and which failures are `undecided`.
|
||||
|
||||
## Bounded action log
|
||||
|
||||
Run on macOS after merging the build computer's bounded action log; the VM
|
||||
was not used. The stage rules now read the log's count and the last applied
|
||||
action of each kind, and the record its kept entries.
|
||||
|
||||
- `buildWitness.test.ts`: every earlier admission case, through the log a
|
||||
list of actions makes, and the rules over a log of 1,000 actions whose
|
||||
earliest are dropped.
|
||||
- `buildWitnessMcp.test.ts`: a witness of 263 actions, one typing 2 KiB,
|
||||
is admitted at every stage and held; its record keeps 256 actions from
|
||||
index 7 with `actions_dropped` 7, names `text` in the typed action's
|
||||
`truncated_arguments`, and its Evidence states both limitations.
|
||||
|
||||
## Failures on cut views
|
||||
|
||||
Run on macOS; the VM was not used. The first three tests failed on the
|
||||
reviewed head, with only the tests and fixtures added, and pass now.
|
||||
|
||||
- `appTaskChecks.test.ts`: `lines_present` missing line 5,500 of a
|
||||
6,000-line file, `row_present` missing row 501 of a table cut at 500, and
|
||||
`value_equals` whose pointer names nothing in a cut text, SQLite, XML or
|
||||
zip listing view are undecided; a line, row or value the view kept stays
|
||||
decided. Before, each was a decided failure.
|
||||
- `buildWitnessMcp.test.ts`: a seed and a near miss whose only failing check
|
||||
misses `eggs` in a text view cut at 5,000 lines are refused and filed once
|
||||
the view is whole. Before, the seed was filed as held.
|
||||
- `DesktopTaskExport.test.ts`: a task whose only seed failure misses `milk`
|
||||
in a 6,000-line note is refused, naming the failure, and with a row check
|
||||
that fails on the seed too it is kept with only that check in
|
||||
`failing_on_seed`. Before, the export kept the first task.
|
||||
- `desktopTaskPython.test.ts`: the exported verifier fails the same cut-view
|
||||
cases with the same descriptions, as it did before.
|
||||
|
||||
## Cancellation after a step
|
||||
|
||||
Run on macOS; the VM was not used. A step returns what it saved even when
|
||||
its call is cancelled or the computer closes as the save finishes; only
|
||||
`start_build_capture`, which saves nothing, checks after its last await, the
|
||||
screenshot of the opened app, and ends cancelled. Each test below except the
|
||||
first failed on the head that cancelled every aborted turn, with only the
|
||||
tests added, and passes now.
|
||||
|
||||
- `buildCaptureRetryMcp.test.ts`: a close while `start_build_capture` waits
|
||||
for that screenshot makes it return `cancelled`. Before the start's own
|
||||
check, it returned success for the computer being removed. A close while
|
||||
an export's bundle commits still returns the bundle; before, `cancelled`.
|
||||
- `buildWitnessMcp.test.ts`: a close while the witness record commits still
|
||||
returns the record, and a check cancelled while it commits gets the saved
|
||||
record when asked again; a saved witness's repeated final check returns the
|
||||
same record without checking or writing. Before, `cancelled`, and the
|
||||
repeat was refused as ended.
|
||||
- `DesktopBuildTurns.test.ts`: a running call that finishes after a close
|
||||
keeps its result.
|
||||
|
||||
## Limits of this verification
|
||||
|
||||
- The witness acts were scripted by this agent from the task's prompt; no
|
||||
separate solving agent ran, and the exported `verify.py` was not run.
|
||||
- Only the local provider on amd64, and only Mousepad.
|
||||
@@ -1,23 +0,0 @@
|
||||
# Plan
|
||||
|
||||
1. Checkpoints: `versionAt` and `StateEnds` beside the state view's schema
|
||||
in `src/domain/desktop/desktopState.ts`, since the draft is domain code;
|
||||
`selectStateEnds` in `DesktopStateVersions.ts` turns names into positions
|
||||
and refuses under the calling operation. The export's plan, its task
|
||||
validation and the draft read every file through `versionAt`.
|
||||
2. apt folders: `parseAptConfigShell` in `aptRepositories.ts` and
|
||||
`resolveAptLocations` in `AptRepositoryInventory.ts`, called once per
|
||||
Debian package with the host's folders as the fallback.
|
||||
3. Launch: `applicationEnvironment` in `DesktopApplicationSession.ts` filters
|
||||
the environment the owned process is spawned with.
|
||||
4. Identity: `host.hostname` and `host.user` in the capture schema and
|
||||
record; `src/domain/desktop/hostIdentity.ts` owns the matching rules, the
|
||||
replacements and the per-seed decision, used by the export and by the task
|
||||
refusal. The bundle files move to `DesktopExportFiles.ts`, which rewrites
|
||||
seeds as it copies them and writes the README last, listing them.
|
||||
5. CLI options, the guides, this spec and its verification.
|
||||
6. Tests first: domain rules, the seed and end scenario through the export
|
||||
and the draft, the export's scrub with a dconf database and a binary seed,
|
||||
apt folders from a stub and from real `apt-config`, the launch environment
|
||||
and the recorded host on Linux, and the CLI options; then Mousepad in the
|
||||
Ubuntu 24.04 container.
|
||||
@@ -1,74 +0,0 @@
|
||||
# Capture for building
|
||||
|
||||
## Outcome
|
||||
|
||||
REA's capture, state view, task draft and export fit a build in which an
|
||||
agent drives the application inside an agentenv computer, makes the seed and
|
||||
then the change in one capture, and exports a bundle that can be shared. The
|
||||
seed is the state at a checkpoint the agent names rather than the state before
|
||||
launch; the capture finds the package's sources from lists an ordinary user
|
||||
downloaded; the application does not inherit the computer's control token;
|
||||
and the bundle holds neither the capture host's name nor its user's.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Checkpoints: the export takes `--seed-checkpoint` and `--end-checkpoint`,
|
||||
and the task draft the same two options. The seed checkpoint chooses the
|
||||
seed files, their text formats, the unseeded files and the dconf keys; the
|
||||
end checkpoint is where a task's checks must all pass, the seed where at
|
||||
least one must fail; the draft compares the two. A file's state at a
|
||||
checkpoint is its last version recorded at or before it. Without the
|
||||
options they are the baseline and the capture's last checkpoint, so earlier
|
||||
captures export and draft as before. A name the capture does not hold
|
||||
exactly once, and an end before the seed, are refused naming the option.
|
||||
The export reports both names in `checkpoints`, and the draft in its own
|
||||
`checkpoints` as `baseline` and `final`.
|
||||
- apt folders: the lists folder, the trusted parts folder and the trusted
|
||||
keyring are where `apt-config shell` resolves `Dir::State::Lists/d`,
|
||||
`Dir::Etc::TrustedParts/d` and `Dir::Etc::Trusted/f`, so an `APT_CONFIG`
|
||||
file that moves them is followed; each is apt's default when `apt-config` is
|
||||
missing, fails or names no absolute path.
|
||||
- Launch environment: the application gets REA's environment without any
|
||||
variable whose name starts with `AGENTENV_`, plus the run token.
|
||||
- Host identity: the capture records `host.hostname` and `host.user`, each
|
||||
optional, so captures written before parse; one is left out when the system
|
||||
cannot say.
|
||||
- Scrub: the export replaces `user@host` with `user@localhost`, the host name
|
||||
as a whole name with `localhost`, and the captured home where a whole path
|
||||
segment ends after it with `/home/user`, in seeded text files read as
|
||||
UTF-8, in UTF-16 seeds with a byte-order mark, and in the dconf keys. A
|
||||
host named `localhost` keeps that name, but its captured `user@localhost`
|
||||
is still replaced. The user name alone is never replaced. JSON is scrubbed
|
||||
by its string values, decoded, never its member names, and XML by its
|
||||
decoded character data and attribute values, plus comments and CDATA, so
|
||||
neither's syntax or names change and escapes hide nothing; a UTF-16 seed is
|
||||
scrubbed by the format its text would have in UTF-8. dconf keys are
|
||||
scrubbed by their decoded GVariant strings, never group names, keys,
|
||||
dictionary keys or other values, and a value that does not parse or holds
|
||||
an object path or signature ships as captured. A rewritten seed is a
|
||||
generated file with its own SHA-256. The class's `SEED_HOME` is `/home/user`, which it respells
|
||||
to the computer's home at boot as before. A capture whose home is already
|
||||
`/home/user`, as inside a computer, has no home to replace. A capture
|
||||
without the host's name and user has only its home replaced, with a
|
||||
limitation.
|
||||
- Leftovers: every shipped file, generated ones included, is searched in its
|
||||
path and content, escapes decoded, for the host name and `user@host`
|
||||
anywhere and the captured home as a whole path, with the replacements set
|
||||
aside. `scrubbed_files` lists the rewritten seeds and `unscrubbed_files`
|
||||
every file found, by bundle path; the README lists both.
|
||||
- Tasks: checks whose values name `user@host` or the host name by that rule
|
||||
are refused, like those that name the captured home, but not for their
|
||||
field names, kind or id; and the checks that must fail on the seed are
|
||||
evaluated on the seed as the bundle ships it. A value check is refused as
|
||||
pointing into text only when its file is text at the seed or the end
|
||||
checkpoint, so versions before or after them do not count.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- No MCP tools, no build computer, no control channel with acknowledgements
|
||||
and no witness; later steps own those.
|
||||
- No change to captured Evidence or the state view's views: they keep the
|
||||
captured bytes, and only the bundle is scrubbed.
|
||||
- No rewriting of binary files, SQLite databases or file names, and no
|
||||
replacement of the user name standing alone.
|
||||
- No change to the draft's field names, `baseline` and `final`.
|
||||
@@ -1,186 +0,0 @@
|
||||
# Verification
|
||||
|
||||
Run on 2026-11-05 on top of the namespaced XML and capture fixes branches:
|
||||
gates and source tests on macOS 26.5 with Node v22.23.1 and Python 3.11.14;
|
||||
Linux tests and the end-to-end capture in an Ubuntu 24.04.5 arm64 container
|
||||
with Node v24.22.1, Python 3.12.3, apt 2.8.3, Xvfb, xfwm4 and Mousepad
|
||||
0.6.1-1build2, run as the ordinary user `ada` on a container whose host name
|
||||
is its id.
|
||||
|
||||
## Gates
|
||||
|
||||
- `npm run typecheck`, `format:check`, `build:cached`, `knip`, `jscpd`,
|
||||
`docs:check`, `docs:build`, `verify:test-discovery` and
|
||||
`verify:architecture-guards`: passed. `lint` and `lint:typed` fail only on
|
||||
main's own `tests/acceptance/webSourceLocationCli.test.ts`, with no
|
||||
diagnostics in the touched files. Of the two clones jscpd lists in touched
|
||||
files, one is a new test's nine-line import block and the other a pair of
|
||||
cancellation tests already in `DesktopApplicationCapture.test.ts`.
|
||||
- `npx vitest run src/domain/desktop src/application/desktop src/desktop`
|
||||
with `tests/boundary/providers/desktop*.test.ts` and the desktop CLI
|
||||
boundary tests `cliStartupImports`, `desktopCaptureCli`,
|
||||
`desktopApplicationCli`, `cliInputSchemaRequired` and `cliToolCoverage`:
|
||||
468 passed and 20 skipped on macOS (the Linux-only cases); 485 passed and 3
|
||||
skipped in the container.
|
||||
|
||||
## Tests
|
||||
|
||||
Each new test fails on the code before this change, run from that code with
|
||||
only the new tests and fixtures added: on macOS for the source and CLI tests,
|
||||
in the container for the three Linux-only ones.
|
||||
|
||||
- The scenario: a capture where the app saves a new note before `seed`, edits
|
||||
it before `end`, and writes its settings as it exits. Exported with
|
||||
`--seed-checkpoint seed --end-checkpoint end`, the bundle seeds the note as
|
||||
saved at `seed` and the settings as they were then, and a task whose check
|
||||
of the edit fails at `seed` and passes at `end` is kept. A check that holds
|
||||
only at `exit` is refused with the end at `end` and kept with the end left
|
||||
to default. Without the options the seed is the baseline, without the note.
|
||||
The draft between the two lists only the note, with `eggs` added. An
|
||||
unknown name and an end before the seed are refused naming the option,
|
||||
under the export's and the draft's own operations, and write no bundle.
|
||||
The same options through the public CLI, with a refusal's structured
|
||||
issue.
|
||||
- The scrub's rules: Geany's `pref_template_mail=ada@e81232be457d` becomes
|
||||
`user@localhost` while `pref_template_developer=ada` stays; the host name
|
||||
alone becomes `localhost` but not inside `xe81232be457d`, before `-2` or in
|
||||
`e81232be457d.local`, and a host named `ubuntu` leaves
|
||||
`ports.ubuntu.com/ubuntu-ports` alone; the home where a path segment ends,
|
||||
not `/home/adam` or `/home/ada.bak`; only the home for a capture without
|
||||
the host's name and user; a UTF-8 byte-order mark kept; UTF-16 seeds in
|
||||
both byte orders; leftovers found in a binary database and in
|
||||
`e81232be457d.local`; nothing to replace in a capture whose home is
|
||||
`/home/user`.
|
||||
- Through the export: a capture whose settings file, SQLite database and
|
||||
dconf keys name the host and user. No bundle file but the database holds
|
||||
the host name, `ada@` or `/home/ada`; the settings seed is rewritten, the
|
||||
class holds `SEED_HOME = "/home/user"` and the scrubbed dconf keys, and the
|
||||
result and README list the rewritten settings and the database. Checks
|
||||
naming `ada@e81232be457d` or the host name are refused; a capture written
|
||||
before the host was recorded still parses and has its home replaced.
|
||||
- apt folders: lists in the folder a stubbed `apt-config` names, with a
|
||||
quoted `'` in its name, are found, and the default folder is used when
|
||||
`apt-config` fails or is missing (adapter test); on Linux, real
|
||||
`apt-config` under an `APT_CONFIG` file resolves `Dir::State::Lists`
|
||||
relative to `Dir::State`, and a missing `apt-config` gives the defaults.
|
||||
- Linux: a launched shell's environment has no `AGENTENV_*` variable while
|
||||
it keeps the others and the run token; a capture of the fake application
|
||||
records the host's name and user as Node reads them.
|
||||
|
||||
## Mousepad end to end
|
||||
|
||||
As `ada`, with no apt lists in `/var/lib/apt/lists`, an `apt-get update`
|
||||
under an `APT_CONFIG` naming `/tmp/apt-user/lists` and `/tmp/apt-user/cache`
|
||||
wrote 15 Packages lists there. With that `APT_CONFIG` and
|
||||
`AGENTENV_CONTROL_TOKEN` set, `capture-desktop-application --desktop-entry
|
||||
org.xfce.mousepad` ran while `xdotool` typed `milk` and
|
||||
`from ada@<host name>`, saved the file as `~/Documents/list.txt`, sent
|
||||
`checkpoint seed`, typed `eggs`, saved, sent `checkpoint end` and then
|
||||
`stop`.
|
||||
|
||||
- Mousepad's environment held the run token and no `AGENTENV_*` variable.
|
||||
- The package is `mousepad` 0.6.1-1build2 from
|
||||
`http://ports.ubuntu.com/ubuntu-ports noble/universe arm64 Packages`, origin
|
||||
`Ubuntu`, with `package_lists_present` true and no apt limitation.
|
||||
- `host` records `/home/ada`, the container's host name and `ada`; the
|
||||
checkpoints are `baseline`, `seed`, `end`, `stop` and `exit`.
|
||||
- The draft between `seed` and `end` lists `Documents/list.txt` with `eggs`
|
||||
added and suggests `lines_present` of `eggs`.
|
||||
- The export with `--seed-checkpoint seed --end-checkpoint end` and that check
|
||||
kept the task (`failing_on_seed: ["eggs"]`), seeded `list.txt` as
|
||||
`milk\nfrom user@localhost\n` and listed it in `scrubbed_files`;
|
||||
`unscrubbed_files` is empty and no bundle file holds the host name, `ada@`
|
||||
or `/home/ada`. The dconf keys' recent file is
|
||||
`file:///home/user/Documents/list.txt`. A task checking the line
|
||||
`from ada@<host name>` was refused, and the export without options seeded
|
||||
nothing, since the note did not exist at the baseline.
|
||||
- The exported module's own `_seed_home` wrote `list.txt` unchanged into
|
||||
another home and respelled the dconf keys' path to it.
|
||||
`python -m agentenv_protocol.computer._declare` of agentenv-framework
|
||||
0.9.1431 declared the class, and `agent-env run --task smoke --dry-run`
|
||||
planned the deploy.
|
||||
|
||||
## Review fixes
|
||||
|
||||
Run on 2026-11-05 on the same toolchains, after the first review. Each new
|
||||
test failed on the reviewed head, with only the new tests added, and passes
|
||||
now.
|
||||
|
||||
- A task's seed checks read the seed as the bundle ships it. A mail setting
|
||||
changed from `ada@e81232be457d` to `user@localhost` already reads
|
||||
`user@localhost` in the scrubbed seed, so its check is no longer counted as
|
||||
failing there: `failing_on_seed` went from `mail` and `note` to `note`.
|
||||
- JSON is scrubbed by its strings' decoded values, so a host named `true`
|
||||
leaves `{"enabled": true}` valid and a `\u0065` escape no longer hides the
|
||||
host; an untouched string keeps its escapes. XML is scrubbed in its decoded
|
||||
character data and attribute values, comments and CDATA, so a host named
|
||||
`item` leaves `<item>` alone and `e` references hide nothing; the
|
||||
output is still read as XML.
|
||||
- One search for what is left covers every shipped file, in its path and its
|
||||
content: a clean seed named `e81232be457d-notes.txt`, the class module
|
||||
whose dconf keys hold `[email protected]` and the README that lists
|
||||
them are listed in `unscrubbed_files`, which now names bundle paths and
|
||||
equals an independent search of the bundle. A task check holding
|
||||
`e81232be457d.local` is refused by the same rule. The replacements are set
|
||||
aside, so a host named `host` is not found in `localhost`.
|
||||
- A host named `localhost` keeps that name, but `ada@localhost` becomes
|
||||
`user@localhost`, and a binary file holding it is reported.
|
||||
- A second review found member names rewritten too: on a host named
|
||||
`ubuntu`, `{"ubuntu": 1, "localhost": 2}` got two `localhost` keys. Names
|
||||
are now kept, an escaped one before a colon on the next line included, and
|
||||
a seed whose JSON key holds the host ships as captured and is listed in
|
||||
`unscrubbed_files`.
|
||||
- The same review found that the regular expression that found JSON strings
|
||||
threw `RangeError: Maximum call stack size exceeded` on one string of
|
||||
16,000,000 characters, which a 64 MiB capture can retain, so such a seed
|
||||
stopped the export. The scrub now walks the document one string at a
|
||||
time, and that seed is scrubbed in about 20 ms. The XML expressions, run on
|
||||
a comment, CDATA section, processing instruction, attribute value and text
|
||||
of the same length, finished within 6 ms each.
|
||||
|
||||
The gates passed as before, `lint` and `lint:typed` again failing only on
|
||||
main's `webSourceLocationCli.test.ts` with no diagnostics in this change's
|
||||
files; the desktop tests ran 483 passed and 20 skipped on macOS, and 500
|
||||
passed and 3 skipped in the container, where the Mousepad run above gave the
|
||||
same result, its rewritten seed now listed by bundle path.
|
||||
|
||||
A third review found four more. Each new test failed on the reviewed head and
|
||||
passes now; `src/domain/desktop`, `src/application/desktop` and
|
||||
`tests/boundary/providers/desktop` ran 404 passed and 11 skipped on macOS.
|
||||
|
||||
- dconf keys were scrubbed as one text, so on a host named `true` the setting
|
||||
`wrap=true` became `wrap=localhost`, which `dconf compile` refuses, and
|
||||
keys and group names could be renamed. Only decoded GVariant strings change
|
||||
now; a value that does not parse ships as captured and the class module is
|
||||
listed in `unscrubbed_files`.
|
||||
- A task's check was searched as serialized JSON, so on a host named `file`
|
||||
or `kind` every check was refused for its field names. Only its values are
|
||||
searched now, not its field names, kind or id.
|
||||
- A value check was refused when its file was text at any recorded version;
|
||||
an empty `.json` before `seed` or after `end` refused a task whose file is
|
||||
JSON at both. Only the seed, as shipped, and the end are read now.
|
||||
- UTF-16 JSON and XML were scrubbed as plain text, so on a host named
|
||||
`ubuntu` `{"ubuntu": 1, "localhost": 2}` got two `localhost` keys. They are
|
||||
scrubbed by the format their text would have in UTF-8, in their own byte
|
||||
order.
|
||||
|
||||
## UTF-16 format detection
|
||||
|
||||
- A decoded UTF-16 seed's scrubber is chosen by a syntax pass that builds
|
||||
no value or document (`textSyntaxFormat`: the JSON token scan, and the
|
||||
XML parser's events with only the open elements kept), and the XML
|
||||
scrubber copies out only changed spans. A UTF-16 XML seed of a million
|
||||
elements is scrubbed in a 24 MiB heap, where the previous code needed
|
||||
more than 768 MiB; `desktopStateHeap.test.ts` runs it in 32 MiB and failed
|
||||
on the previous code. On 200,000 random XML-like inputs and 80 UTF-16
|
||||
cases the output is byte-identical to the previous code's.
|
||||
|
||||
## Limits of this verification
|
||||
|
||||
- The bundle was not run in an agentenv computer, and REA was not run inside
|
||||
one; the build computer is a later step.
|
||||
- Geany's leak is covered by a fixture with its line, not by capturing
|
||||
Geany. No real application was captured with a UTF-16 seed or a binary
|
||||
file holding the host name.
|
||||
- The apt-config fallback for a missing command and a failing one is covered
|
||||
by the adapter test, and the real command only on Ubuntu 24.04.
|
||||
@@ -1,15 +0,0 @@
|
||||
# Plan
|
||||
|
||||
1. Pure parsers: desktop entries and `Exec` lines, `xprop` output, dpkg and
|
||||
apt-cache output, package kinds from paths, and the walk diff.
|
||||
2. Host adapters: the home walk, the content store, the application
|
||||
inventory, the `xprop` window observer and the session loop.
|
||||
3. Application service: host checks, checkpoints, Evidence and the capture
|
||||
folder.
|
||||
4. CLI: `capture-desktop-application`, stdin requests and progress lines.
|
||||
5. Docs: the user guide and links from the index, sidebar, README and CLI
|
||||
reference.
|
||||
6. Tests: parser units, adapter tests on temporary trees, service refusals,
|
||||
and two Linux captures of a fake application with a stub `xprop`.
|
||||
7. End to end: Mousepad in an Ubuntu 24.04 container under Xvfb and xfwm4,
|
||||
driven by `xdotool`, with a screen recording.
|
||||
@@ -1,126 +0,0 @@
|
||||
# Capture a Linux desktop application in use
|
||||
|
||||
## Outcome
|
||||
|
||||
`rea capture-desktop-application` launches an installed Linux desktop
|
||||
application on the user's X11 display, watches it while a person or a driver
|
||||
uses it, and writes a capture folder: Evidence in `capture.json` and the bytes
|
||||
of retained files in `content/<sha256>`. The Evidence says what the
|
||||
application is and how it was installed, how to launch and recognise it, when
|
||||
its window appeared, and how files under the home directory changed between
|
||||
checkpoints, with the content of the files involved. Later steps read this
|
||||
folder to build the environment; this step builds nothing.
|
||||
|
||||
## Requirements
|
||||
|
||||
- The command is CLI only. A capture is a long interactive session whose
|
||||
checkpoints come from stdin lines (empty, `checkpoint [name]` or other
|
||||
text) and which ends on `stop` or the end of stdin, when the launched
|
||||
process has exited and the application's processes and windows are gone,
|
||||
or at the time limit. One queued line is handled per sample, and stdin
|
||||
reading pauses while 16 wait. A repeated name or one of the capture's own
|
||||
names (`baseline`, `stop`, `time-limit`, `exit`) is refused on stderr and
|
||||
the session continues. The application has exited only after two
|
||||
consecutive samples with the launched process gone, no process carrying the
|
||||
run token left, and a window list that was read and holds none of its
|
||||
windows; an unreadable list counts as unknown and is named in the
|
||||
limitations.
|
||||
- It runs only on Linux with `DISPLAY` set and `xprop` available (or
|
||||
`REA_XPROP_COMMAND`). Before writing or launching anything it refuses other
|
||||
hosts, a request naming neither or both of `--desktop-entry` and
|
||||
`--executable`, an empty `--state-path`, `--exclude` or `--output` value,
|
||||
a program found only through a relative or empty entry of the host's
|
||||
`PATH` (lookup follows the launch's search order; an `env` prefix's `PATH`
|
||||
travels with the command and may hold such entries),
|
||||
an output folder that is not new or empty or is owned by another user, an
|
||||
unreadable window list, a window manager that does not publish
|
||||
`_NET_CLIENT_LIST` (EWMH), and an application that already has a window
|
||||
open. The output folder is made private (0700) and its files are
|
||||
created exclusively (0600).
|
||||
- The inventory resolves a desktop entry id through `$XDG_DATA_HOME` and
|
||||
`$XDG_DATA_DIRS`, following an entry installed as a symbolic link, expands
|
||||
its `Exec` line with no files and appends `--argument` values, resolves the
|
||||
program on `PATH` (the one an `env NAME=value` prefix sets, whose XDG
|
||||
folders also name the state directories), and identifies the package:
|
||||
snap, flatpak and AppImage from paths (and the AppImage magic, and a
|
||||
`flatpak run` launch), otherwise
|
||||
the dpkg owner of the launched desktop entry, or else of the executable,
|
||||
with its version, architecture, the apt sources listing the
|
||||
installed version and the `o=` origin apt reports for each source. The
|
||||
entry's package is the one that installs the application: LibreOffice
|
||||
Writer's entry belongs to `libreoffice-writer`, while the `libreoffice` it
|
||||
runs belongs to `libreoffice-common`, which does not depend on Writer. When
|
||||
the two differ, `executable_package` names the executable's, with
|
||||
`executable_package_reason` when neither the Ubuntu 24.04 archive nor a
|
||||
declared vendor repository offers it or apt could not say; each vendor
|
||||
repository records in `supplies` the library packages and executable's
|
||||
package only vendor repositories offer that its package files list. When
|
||||
a package owns the entry but none owns the program, nor the file it resolves
|
||||
to, `executable_package_reason` says so without `executable_package`. A
|
||||
`dpkg-query` lookup that does not finish or exits with an error leaves the
|
||||
package
|
||||
`unknown`, with a limitation naming the path. It marks
|
||||
Electron applications by the fuse sentinel or `resources/app.asar`, and
|
||||
lists candidate state directories under the XDG folders and dot-folders
|
||||
named after the package, executable, desktop entry's full id and last part,
|
||||
and name, and every folder present there whose name is one of those in
|
||||
another letter case, such as GIMP's `~/.config/GIMP`; a `flatpak run`
|
||||
launch uses the application id (its first operand
|
||||
after options and their values) instead of the launcher's name and adds
|
||||
`~/.var/app/<id>`. An `--executable` runs from the caller's
|
||||
directory, which also resolves a relative path to it.
|
||||
- Paths are compared in their physical spelling. The walked roots are the
|
||||
home directory and every content root outside it: the state directories,
|
||||
`--state-path` values and absolute arguments other than `/` or a directory
|
||||
containing the home directory, which the limitations name instead, plus the
|
||||
`-wal` and `-journal` paths beside each of those selected paths that is not
|
||||
a directory, so a database selected on its own is captured with its logs. The
|
||||
baseline walk runs before launch and retains every file under the content
|
||||
roots; each later checkpoint reports created, modified, deleted and retyped
|
||||
paths against the previous walk and retains every changed file. A creation
|
||||
or deletion is reported only where the walk listed the path's nearest
|
||||
existing directory completely; the rest are counted as unconfirmed. Walks
|
||||
do not follow symbolic links, do not enter a directory on another device
|
||||
than their root (the limitations name each such mount point), leave out the
|
||||
output folder and `--exclude` paths, which the capture records as
|
||||
`excluded`, and stop at `--max-entries`, roots included.
|
||||
- Retention opens files without following links or blocking, keeps a file
|
||||
only if it is still the version the walk recorded and does not change while
|
||||
read, respects `--max-file-bytes`, and charges `--max-content-bytes` only
|
||||
for content not already stored. A file over the remaining budget is not
|
||||
read unless stored content of the same size could make it a duplicate.
|
||||
Every omission is recorded with its reason.
|
||||
- The application is launched through REA's owned process path: its own
|
||||
process group and a run token in its environment. Every half second the
|
||||
session reads its process tree from procfs, the client list with `xprop`,
|
||||
and the files its processes hold open under the walked roots. A window
|
||||
belongs to the application by live owner pid or by class (including the
|
||||
entry's full id and a flatpak's application id), re-evaluated every sample;
|
||||
a flatpak's windows are matched by class only. A matching window's pid is
|
||||
followed too. Processes are identified by pid and start time, and each
|
||||
records whether the capture started it: the launched process, a run-token
|
||||
carrier or a descendant of one.
|
||||
- When the session ends it records a `stop` or `time-limit` checkpoint if the
|
||||
application is still running, sends SIGTERM to the launched process, its
|
||||
process group and every process carrying the run token, waits up to five
|
||||
seconds, removes every process still carrying the run token, and records an
|
||||
`exit` checkpoint. Processes without the token are never signalled outside
|
||||
the launch group. Cancellation ends the session without further
|
||||
checkpoints, releases the application the same way, and still writes the
|
||||
capture with end reason `cancelled`; cancellation before launch writes no
|
||||
capture and removes the retained content. Any other failure before `capture.json` is written releases the
|
||||
application the same way and removes the retained content; processes or
|
||||
files that remain are reported as cleanup facts with the failure.
|
||||
- The Evidence uses provider `rea-desktop` version 1, operation
|
||||
`capture_desktop_application` and predicate type
|
||||
`rea.desktop-application-capture`; its parameters carry only counts and the
|
||||
launch kind.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- No state view, format readers, export, tasks or verification; later steps
|
||||
own those.
|
||||
- No click or keystroke recording, no Wayland-native windows, no MCP tool.
|
||||
- No writer attribution. State-directory and open-file markers are evidence,
|
||||
not proof.
|
||||
- No redaction of the user's files. The capture folder is private data.
|
||||
@@ -1,14 +0,0 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Record the outcome, requirements and non-goals.
|
||||
- [x] Parse desktop entries, `Exec` lines, `xprop`, dpkg and apt-cache output.
|
||||
- [x] Walk home state, diff walks and retain content by SHA-256.
|
||||
- [x] Build the inventory: entry, launch command, program, package, Electron markers, state directories.
|
||||
- [x] Watch processes, windows and open files; take checkpoints from stdin; stop and terminate.
|
||||
- [x] Write `capture.json` Evidence and the content folder.
|
||||
- [x] Register the CLI command and document it.
|
||||
- [x] Add unit, adapter, service and Linux capture tests.
|
||||
- [x] Capture Mousepad end to end in a container.
|
||||
- [x] Run the gates and record verification.
|
||||
- [x] Take a Debian package from the launched desktop entry's owner (LibreOffice Writer).
|
||||
- [x] Find state folders named after the application in another letter case (GIMP).
|
||||
@@ -1,138 +0,0 @@
|
||||
# Verification
|
||||
|
||||
Run on 2026-11-03 against main `d7df9df33`: gates and source tests on macOS 26.5
|
||||
with Node v22.23.1; Linux tests and the end-to-end captures in an Ubuntu 24.04.5
|
||||
arm64 container with Node v24.22.1, Xvfb, xfwm4 and Mousepad 0.6.1-1build2.
|
||||
|
||||
## Gates
|
||||
|
||||
- `npm run typecheck`, `lint`, `lint:typed`, `format:check`, `knip`,
|
||||
`knip:production`, `jscpd`, `docs:check`, `mcp-catalog:check`,
|
||||
`verify:test-discovery`, `verify:architecture-guards` and `scan:todos`:
|
||||
passed. Lint and typed lint report no diagnostics in the new files.
|
||||
- `npx vitest run --project domain --project services --project adapters` on
|
||||
macOS: 21,581 passed, 29 skipped, 5 failed. The same 5 fail on main in the
|
||||
same environment (two zstd cases, Inspector target authorization
|
||||
cancellation, and two `setupEnvironment` cases).
|
||||
- `npx vitest run --project cli-boundary` on macOS: 1,499 passed, 5 skipped, 39
|
||||
failed. 38 of them fail on main in the same environment (client profiles,
|
||||
setup, doctor and version reports). The 39th, `closedStdout`, passed three
|
||||
runs out of three when rerun alone.
|
||||
- The new domain, adapter and service tests on Linux: 51 passed, including
|
||||
five captures of a fake application with a stub `xprop`: a requested
|
||||
checkpoint with the state change and its content, an application that exits
|
||||
on its own, a refusal while the application's window is already open, a
|
||||
refusal when the window list cannot be read, and a cancellation after a
|
||||
checkpoint that still writes the capture and verifies cleanup.
|
||||
|
||||
## Mousepad end to end
|
||||
|
||||
`capture-desktop-application --desktop-entry org.xfce.mousepad` ran as an
|
||||
ordinary user while `xdotool` typed a line, sent `typed`, saved the file as
|
||||
`~/Documents/groceries.txt` through the Save As dialog, sent `checkpoint saved`
|
||||
and then `stop`. The session was recorded with `ffmpeg` (30 seconds).
|
||||
|
||||
- Inventory: the entry's `Exec=mousepad %U` became `["mousepad"]`; the
|
||||
package is `mousepad` 0.6.1-1build2 from
|
||||
`http://ports.ubuntu.com/ubuntu-ports noble/universe arm64 Packages`; no
|
||||
Electron markers.
|
||||
- Session: the first window appeared after about 530 ms; the Mousepad window's
|
||||
titles went from `Untitled 1 - Mousepad` to `*Untitled 1 - Mousepad` to
|
||||
`~/Documents/groceries.txt - Mousepad`; the `Save As` dialog was recorded as a
|
||||
dialog of the application; the progress terminal's window was recorded and
|
||||
not attributed. The window match is class `Mousepad`, instance `mousepad`.
|
||||
The session ended on `stop` after 25.9 s and Mousepad exited with code 0.
|
||||
- Cleanup: verified; the only process recorded was `mousepad`, carrying the
|
||||
run token.
|
||||
- State: checkpoints `baseline`, `typed`, `saved`, `stop` and `exit`, none
|
||||
with unconfirmed changes. `saved` holds `~/Documents/groceries.txt` as
|
||||
created, and its retained content is `Groceries: milk, eggs, coffee` with a
|
||||
matching SHA-256. `exit` holds `~/.config/Mousepad/accels.scm`, marked as a
|
||||
state directory. The dconf database, the window manager's settings and GTK's
|
||||
recently used list also changed and are not marked. Nine files were retained
|
||||
and none were omitted.
|
||||
|
||||
A second run started Mousepad before the capture. The capture refused with
|
||||
`The application is already open: "Untitled 1 - Mousepad" (pid 35).` and wrote
|
||||
nothing to the output folder. Before that refusal existed, the same setup
|
||||
showed Mousepad handing the launch to the running instance, which opened a new
|
||||
tab in its existing window while the launched process exited at once.
|
||||
|
||||
## Fixes after the five-application test
|
||||
|
||||
Run on 2026-11-05 on macOS 26.5 with Node v22.23.1. A test that captured and
|
||||
exported five applications found two inventory gaps:
|
||||
|
||||
- LibreOffice Writer, captured with `--desktop-entry libreoffice-writer`,
|
||||
recorded `libreoffice-common`, the owner of the `/usr/bin/libreoffice` its
|
||||
entry runs, which does not depend on Writer. The exported computer installed
|
||||
only that package and failed its readiness check with
|
||||
`no window with class 'libreoffice-writer'`. The package is now the entry's
|
||||
owner, with the executable's in `executable_package`. Adapter tests on a
|
||||
fake dpkg host whose entry and program have different owners cover an entry
|
||||
launch, an `--executable` launch, an entry no package owns and a
|
||||
`--package-file` matched against the entry's package.
|
||||
- GIMP 2.10 keeps its profile in `~/.config/GIMP/2.10`, which no candidate
|
||||
name (`gimp`) matched, so it was neither seeded nor reset. A folder in the
|
||||
XDG folders or the home whose name is one of the candidate names in another
|
||||
letter case is now a state directory, and the state view counts a path in a
|
||||
folder beside a state directory that differs from it only in case as in it.
|
||||
An adapter test with GIMP's layout and a domain test cover both, and leave
|
||||
out `GIMPshop`, `MyGIMP` and a nested `Other/GIMP`.
|
||||
|
||||
Review of those fixes found three more gaps, each now covered by a test that
|
||||
failed on the first fix:
|
||||
|
||||
- The entry's package need not depend on the program's (`vim-common` owns
|
||||
Vim's entry, `vim` its program), so the export now installs
|
||||
`executable_package` by name beside the app's package, for archive, vendor
|
||||
repository and package-file installs alike. The capture checks it by the
|
||||
library packages' rule and records `executable_package_reason` when neither
|
||||
the archive nor a declared repository offers it, or apt could not say; the
|
||||
export then refuses. Adapter, domain and export service tests cover the
|
||||
record, the three install paths, the refusal and the rendered
|
||||
`Packages(apt=[...])`.
|
||||
- A profile folder created during the capture in another letter case than its
|
||||
candidate was application state but owned as the folder of its first file
|
||||
(`.config/GIMP/2.10`), so later files elsewhere in it escaped `data/get` and
|
||||
`data/reset`. Owned paths now use the same match as roles and own
|
||||
`.config/GIMP` in the files' spelling; a domain test and an export service
|
||||
test of a created profile cover it.
|
||||
- A `dpkg-query -S` that timed out or exited with an error counted as "no
|
||||
owner", so a failed entry lookup quietly recorded the program's package. It
|
||||
now leaves the package `unknown` with a limitation naming the path, for the
|
||||
entry and the executable, after a timeout or an exit status of 2.
|
||||
|
||||
A later review found that both the program's package and library packages
|
||||
were approved at capture against every repository with a verified key, while
|
||||
the content budget can then drop a repository's key and the export leaves that
|
||||
repository out. Repositories now record what they `supply`, and the export
|
||||
leaves out library packages, and refuses a program's package, that only
|
||||
repositories it leaves out supply. A capture adapter test, two export domain
|
||||
tests and a key-retention service test that drops one of two keys under the
|
||||
per-file limit cover it.
|
||||
|
||||
A third review found that an entry owned by a package whose program no
|
||||
package owns, such as a `~/.local/bin` program found first on `PATH`, was
|
||||
recorded as the entry's package alone, which the export then installed though
|
||||
it does not bring that program. The capture now records that no package owns
|
||||
the program, and the export refuses it. An adapter test, an export domain test
|
||||
and an export service test cover it, and an adapter test keeps a program link
|
||||
whose resolved file dpkg owns counted as owned.
|
||||
|
||||
Each new test failed on the code before its fix, run in a copy of that commit
|
||||
with only the new tests added. `npm run typecheck`, `lint`, `lint:typed`
|
||||
(no diagnostics in the touched files), `format:check`, `build:cached`,
|
||||
`knip`, `jscpd`, `docs:check`, `docs:build`, `verify:test-discovery` and
|
||||
`verify:architecture-guards` passed, and
|
||||
`npx vitest run src/domain/desktop src/application/desktop src/desktop tests/boundary/cli/cliStartupImports.test.ts`
|
||||
ran 351 passed and 18 skipped. Neither application was captured again.
|
||||
|
||||
## Limits of this verification
|
||||
|
||||
- Only Mousepad was captured end to end, on arm64 in a container. GnuCash,
|
||||
Joplin and a full desktop VM are for later steps.
|
||||
- No real snap, flatpak or AppImage application was captured; those paths are
|
||||
covered by parser tests only.
|
||||
- The time limit is covered by code review, not by a test. Cancellation is
|
||||
covered by a Linux test with a fake application, not by Ctrl-C on Mousepad.
|
||||
@@ -1,25 +0,0 @@
|
||||
# Plan
|
||||
|
||||
1. Format and reader: `zip` detected by its local file header; the text, JSON,
|
||||
key file and XML readers shared as `textualView`, so a file and an entry
|
||||
read alike; a domain module for the entry limits, the entry view and the
|
||||
archive view, and an application reader that walks the verified bytes with
|
||||
zip.js under the options and name decoding `ZipArtifactReader` uses.
|
||||
2. Selectors: `locatePointer` in `src/domain/desktop/viewPointer.ts`, used by
|
||||
REA's evaluator and the draft, and `_locate` in the exported verifier, with
|
||||
the same messages; `tolerance` in the task schema and both evaluators.
|
||||
3. Changes and draft: member keys chosen per array from the candidate
|
||||
selectors of both versions, a keyed walk for zip views in `compareTrees`,
|
||||
and a walk that notes a member added or removed whole once for the draft's
|
||||
archive suggestions.
|
||||
4. Exported module: `_zip` and `_entry_view` beside `_view`, with
|
||||
`zipfile`, `zlib` and `hashlib` imported at the top.
|
||||
5. Docs and tests: real FreeCAD 0.21 and 1.0 `Document.xml` files zipped into
|
||||
`.FCStd` archives and checked by both evaluators, an `.odt` built in the
|
||||
test, archives crafted for each limit, and the shared check cases run
|
||||
through both evaluators.
|
||||
6. Namespaced XML: the exported module reads XML with `expat` rather than
|
||||
`ElementTree`, one pass with namespace processing to check the document and
|
||||
one without to build the view as REA's DOM does, names and text nodes
|
||||
alike; the export's namespace refusal and the draft's namespace gate go.
|
||||
Parity tests read real Inkscape and LibreOffice Writer saves.
|
||||
Loaded 100 of 1351 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user