AI Engineering
Reverse Engineering with AI Agents: Exploring REA
What changes when a coding agent can inspect the program behind a feature instead of its surface? A close reading of REA: how it reaches the agent over MCP, what its Evidence records actually claim, three investigations and what each left unknown, and where the approach stops.
- Author
- Akshay Shinde
- Published
- November 1, 2026
- Read
- 28 min read
- Topics
- AI Agents, Reverse Engineering, Tool Calling, Evaluation
AI Engineering / AI Agents / Reverse Engineering / Tool Calling / Evaluation
Reverse Engineering with AI Agents: Exploring REA
What changes when a coding agent can inspect the program behind a feature instead of its surface? A close reading of REA: how it reaches the agent over MCP, what its Evidence records actually claim, three investigations and what each left unknown, and where the approach stops.
On this page
- What the surface cannot tell an agent
- What REA is
- The Evidence record
- How an investigation moves
- Reading it and running it
- Not every target gives the same evidence
- Calculator: separating a branch from its meaning
- Notion: partial results across a process boundary
- DX-Ball: when the pseudocode is wrong
- From recovered rule to working code
- A second capability: environments from recorded sessions
- The limits of the evidence
- Privacy, safety and responsible use
- When it earns its place
- What the evidence makes possible
Windows Calculator gives 220 for 200 + 10% and 20 for 200 × 10%. The same button produces two different meanings of "percent", and nothing on the screen says why. An agent that can press the buttons is doing black-box testing, which can go a long way: with enough inputs it can infer what the feature does and predict the next output. What it can't establish from outputs alone is which internal code path produced them. The rule that actually runs lives in a compiled library, behind a branch on the pending operator.
REA (Reverse Engineer Anything) exists for that gap. It gives a coding agent tools to inspect the program itself: instructions in a binary, modules in an Electron package, scripts loaded by a running page. The question this article follows is what changes when an agent has that access, and how far you can trust what comes back. The second half of that question is the more interesting one, because REA's own records are unusually careful about the difference between what a tool saw and what somebody concluded.
What the surface cannot tell an agent
Engineers already have a routine for software they can't read the source of. You observe it, form a hypothesis, then go looking for confirmation: strings in the binary, traffic in a proxy, a debugger attached at the right moment. The skilled part is knowing which layer holds the answer to which question. A network capture can tell you what a feature sends. It says little about the local rule that decided to send it.
A coding agent working from a browser tool or screenshots has the first half of that routine and none of the second. It sees outputs, so it can only produce models that fit outputs. For 200 + 10% a plausible model is "percent is relative to the left operand". That happens to be right for addition, and it fails for multiplication, and a handful of test inputs could easily miss the difference. The failure mode is worse than an honest "I don't know", because the agent's answer sounds like an explanation.
Access to the program changes the character of the claim. "Calculator probably scales by the previous value" becomes "this function compares the operator field against two constants and takes one of two paths", with an address attached that somebody else can check. That is the shift worth examining: from statements that are merely plausible to statements with a location. Everything else in this article is about how much that location is worth.
What REA is
REA ships as one npm package, rea-agents, with two ways in: a local MCP server, stdio by default, and a CLI that uses the same workflows. Following one question through it shows how the parts divide the work.
Setup registers the MCP server with your agent and installs a matching skill. The skill is plain instructions. It tells the agent how to route a target (an ASAR goes to analyze_javascript_application, an APK to inspect_android_package, a native binary to open_binary) and what standard a conclusion has to meet. Installing it alone adds no tools, which is a useful reminder of the split harness engineering draws between what an agent is told and what it is able to do.
The MCP server provides the tools themselves. There are also six guided prompts, such as investigate_feature and verify_reconstruction, which suggest starting points and, in the docs' words, do not "add, remove, or invoke tools by themselves".
Below the tools sit the analysis engines. For deep native work REA drives an external decompiler (Hopper, Ghidra, IDA or Kuna). For other targets it has its own readers: static JavaScript and .NET inspection need no native engine at all, and browsers are reached over the Chrome DevTools Protocol. Provider choice is deterministic with no fallback. With two suitable engines configured, auto reports ambiguous instead of quietly picking one.
The boundary that matters is the one between REA and the agent. REA has no model in this loop and writes no implementation. It answers typed questions about a target. The agent decides which questions to ask, reads the answers, and does all of the reasoning, so a wrong conclusion can come from a correct tool result. Tool calling is an application contract made this point about writes, and it applies just as well to reads: a tool's guarantee ends at its return value.
The Evidence record
The most interesting engineering in REA is the shape of what comes back. Evidence-producing tools return a canonical record, and that record is a strict schema. Abridged from the Zod definition:
type Evidence = {
evidence_id: string; // digest, stable identity for citation
subject: Subject | null; // the artifact this is about
provider: Provider; // which engine, which version
operation: string; // e.g. 'analyze_function'
parameters: JsonObject; // what was asked
raw_result: JsonValue | null; // provider's own form, when distinct
normalized_result: JsonValue; // the operation's result
confidence: 'observed' | 'derived' | 'inferred';
authority:
| 'shipped-artifact'
| 'controlled-replay'
| 'historical-reference'
| 'external-service'
| 'analyst-inference';
environment: { platform: string; isolation: string /* ... */ } | null;
limitations: string[]; // what this result does not establish
relationship_coverage: Coverage | null; // how complete the edges are
locations: EvidenceLocation[]; // where in the target
evidence_links: string[]; // records this one was built from
};Most tool results tell a model what was found. This one also says who found it, in what build, how, with what standing, and what it failed to cover. Two fields do the heavy lifting.
confidence grades the record. REA's docs don't define the three values in one place, so the following is my reading of how the code uses them. observed is the default for a direct result. derived marks a result computed from other records: compare_functions and build_call_path are derived, and both carry the authority analyst-inference. inferred marks a relationship that goes beyond what the sources directly show, such as correlate_static_and_runtime, and the MCP contract says outright that "correlation does not establish causality".
authority answers a separate question: where the standing of the result comes from. A shipped binary and a controlled replay can both be observed and still mean different things. The first tells you what the product contains and the second what happened in one run. Keeping those on separate axes stops one from being mistaken for the other.
Then there are unknowns, which aren't a fourth grade. A residual unknown is its own versioned record: a question, a status (open, investigating, blocked, contradicted, resolved), a severity, supporting and contradicting evidence, recommended probes, and a statement of what would count as an answer.
That last part is what turns the grades from labels into a mechanism. The ranking is written into the code as inferred below derived below observed. An unknown can demand, say, required_confidence: "observed" with required_authority: "controlled-replay". When a bundle containing a resolution marked verified is validated, REA rejects it if any contradicting evidence is attached, or if no supporting record meets the requirements. That check applies to evidence bundles and the resolutions recorded in them. A confident paragraph doesn't satisfy it, and REA doesn't validate the natural-language claims an agent makes in its answer.
There is a limit worth stating, and the rest of this article keeps running into it. A grade belongs to a record, and the sentence the agent writes afterwards has no grade at all. REA's skill tells the agent that "every conclusion must distinguish observations, inferences, and unknowns". That line is an instruction, and a model can fail to follow it. The structure makes honest reporting possible and makes dishonest reporting checkable. It cannot make the agent honest.
How an investigation moves
The documented flow is short: the agent calls REA, REA returns findings with their evidence, and the agent uses them to explain the behaviour or to write and test an implementation. Expanded into stages and assigned to owners, which is my framing rather than REA's:
- Question (developer). Name a target and one behaviour. "How does export work in this app" gives the agent a stopping condition, and "reverse engineer this app" doesn't.
- Route (agent, guided by the skill). Choose the first tool from the target type.
- Inspect (REA and a provider). Run the requested operation. Nothing broader runs: each request names its target and lifecycle, and REA does not infer a wider action from it.
- Evidence (REA). Return the record, with limitations and coverage.
analyze_function, since 6.8.0, keeps the full dossier in the session and returns a summary with a parent ID, so the agent pages through detail as it needs it. - Trace (agent). Follow calls and references, and use each result to pick the next question. This is the loop where most of the work happens, and it is an ordinary agent loop with better observations.
- Reconstruct (agent). Write the rule down as code, if that was the goal.
- Verify (agent and developer). Compare the reconstruction against the original where that is possible. This stage isn't guaranteed: some targets can't be run, and some checks need work outside REA, as the walkthroughs below show.
Reading it and running it
The most important distinction when weighing a finding is whether it came from reading the program or from running it. They answer different questions, and neither substitutes for the other.
Static analysis reads files. Static JavaScript and .NET inspection read the supplied files without running the application, and analyze_javascript_application starts its own worker process but does not execute application code. The strength of reading is coverage of paths that didn't happen to run: Calculator's multiply branch is in the binary whether or not anybody pressed × today. Its weakness is anything decided late. A channel name computed at runtime stays a variable, code fetched after launch isn't there, and no value is ever measured. The skill is blunt about this: trace_native_values establishes static facts and "does not measure runtime values".
Runtime analysis has the opposite profile, and REA's runtime tools sit at quite different strengths, which matters for safety:
- Passive observation.
inspect_web_pageattaches to a browser you started, over a loopback debugging port, and collects the DOM, scripts and network metadata in a bounded window. REA describes this as passive, not as a general automation surface. Even so, disconnecting can clearnavigator.platformemulation that another client set, so passive means unobtrusive and still falls short of having no effect. - Declared scenarios.
capture_browser_scenarioperforms a fixed vocabulary of actions, andcapture_process_scenariolaunches a command and records terminal output, lifetime and selected files. The process guide says plainly that this "is not a sandbox", and that the paths it observes don't restrict what the process can touch. - Instrumentation. Frida scripts execute inside the target, with that process's memory, filesystem and network authority.
What runtime gives you in exchange is ground truth for one execution, including values no static tool can know. What it cannot give you is anything about the paths that run didn't take, and the skill warns against treating no retained event as proof that a behaviour never occurs.
The two combine naturally: static reading narrows the search to a function, and a controlled run tests a specific claim about it. REA has a tool for joining the results, correlate_static_and_runtime, and it is deliberately narrow. It takes explicit mappings, doesn't match names automatically, and records its output as inferred.
Not every target gives the same evidence
"Reverse engineer anything" is a statement of ambition. In practice each target class has its own evidence, prerequisites and blind spots, and the full table in the README runs to more than thirty rows. These are the classes most developers will reach for, condensed from it and the linked guides. Host support moves quickly, so follow the links for current requirements.
| Target | Evidence returned | Needs | Main limit |
|---|---|---|---|
| Native binaries | Pseudocode, assembly, strings, symbols, calls, references | Hopper, Ghidra, IDA or Kuna. Formats and hosts vary by provider, and Windows Ghidra is experimental | Pseudocode can be incomplete, as DX-Ball shows |
| JavaScript and Electron | Modules, imports, source maps, routes, IPC, native add-ons | Node.js and npm. No native engine | Dynamic values stay unresolved |
| Websites | Page structure, scripts, network observations | A Chrome-family browser | Only what happens during the capture window |
| .NET assemblies | Metadata, CIL, declared native dependencies, build comparisons | None | Static only. Managed execution is outside the tool set |
| Android APKs | Manifest declarations, classes, decompiled methods, references | Headless JADX and a full JDK, both caller-supplied. Device work needs adb | APK authenticity and runtime behaviour stay unverified |
| Saved traffic | Requests, responses, exposed payloads | HAR or SAZ file. Packet captures need tshark | Offline: only what was recorded, and URLs are never fetched |
| Process behaviour | Terminal output, interactions, exit and file observations, run comparisons | Linux or macOS with a native PTY | Native Windows capture is unavailable |
A request that sounds uniform can have very different costs. Asking about an Electron app needs nothing beyond Node, while a native game means setting up a decompiler, and with Hopper, IDA or JEB you must hold the licences yourself. Many of the rarer readers, such as those for Go build metadata or Unity IL2CPP metadata, return identity and structure facts instead of behaviour. The three walkthroughs below sit at the end of the range where behaviour can actually be recovered.
Calculator: separating a branch from its meaning
This walkthrough shows that evidence and meaning can come from different places. It is based on REA's Calculator evidence record, collected on 9 October 2026 with REA 4.1.0 against Windows Calculator 11.2508.4.0, x64.
calc.exe turns out to be a launcher. REA's dossier for it shows a call to ShellExecuteW with ms-calculator:, so the logic lives elsewhere, in the installed package's CalcViewModel.dll. REA inspected a function at 0x1801245d0 occupying 10,656 bytes across two ranges. Its case 0x76 contains this, selected from the instruction list:
0x180124945: MOV EAX, dword ptr [R13 + 0x18]
0x180124949: CMP EAX, 0x5c
0x18012494c: JZ 0x180124aba
0x180124952: CMP EAX, 0x5b
0x180124955: JZ 0x180124aba
0x18012495b: MOV EDX, 0x64Read mechanically: load a field, compare it with two constants, jump to the same place if either matches, otherwise continue with 0x64, which is 100. The record says that both paths construct the constant 100, that one divides the current value by 100, and that the other combines the current value with the previous value divided by 100.
That is a complete description of the branch, and it still doesn't say what 0x5c means. The binary is stripped. The meaning comes from outside it, from Microsoft's MIT-licensed source, where IDC_MUL is 92 (0x5c), IDC_DIV is 91 (0x5b) and IDC_PERCENT is 118 (0x76):
// If the operator is multiply/divide, we evaluate this as "X [op] (Y%)"
// Otherwise, we evaluate it as "X [op] (X * Y%)"
if (m_nOpCode == IDC_MUL || m_nOpCode == IDC_DIV)
result = rat / 100;
else
result = rat * (m_lastVal / 100);So 200 + 10% evaluates to 200 + (10 × 200/100), giving 220, and 200 × 10% evaluates to 200 × 0.1, giving 20.
- REA established: the launcher's target, the function's extent, its pseudocode, instructions, calls and references, the two operator comparisons and the constant.
- Needed something else: finding the function. The record states that a local instruction scan for the operator IDs and the constant located candidates and that selection was analyst-assisted, so the example prompt on the website is "not a recording of an autonomous end-to-end discovery". Operator names and helper meanings came from Microsoft's public source.
- Remained unknown: which source commit matches the installed build, since the public commit is a crosscheck and no PDB symbols were loaded. Microsoft's tests were inspected and not executed, and no completed GUI input check is claimed.
My interpretation: Calculator is close to a best case, and it still needed a human-assisted search and a public source tree to name its constants. With a closed-source target those constants would remain unlabelled, and "this path is multiply" would have to be inferred from behaviour. That inference would be worth a lower grade than the instructions it rests on.
Notion: partial results across a process boundary
This walkthrough shows what an investigation looks like when the tool's result is honest about being incomplete. REA's Notion case study and its evidence record follow a clipboard write in Notion Desktop 7.6.1. The analysis was recorded on 7 October 2026 with REA 4.1.0.
An Electron app splits a feature across trust boundaries. Page code runs in a renderer, a preload script decides what that page may call, IPC carries the request, and the main process does the privileged work. A copy therefore crosses four layers before it reaches the system clipboard.
REA's focused analysis ran over one extracted file, the tab preload.js at 24,579 bytes. It returned two ast-static-analysis observations with exact source ranges in the minified bundle. One located a context bridge exposing __electronApi, with api_status: "dynamic", members: [] and unknown_members: 1. The other located an IPC invoke with channel: null and resolution: "dynamic". In other words, REA found where the bridge and the IPC call are, and reported that it could not resolve what they expose or which channel they use. Static analysis stopped exactly where the values became runtime variables, and the result said so.
From those two locations, reading the packaged code completed the route, as the case study lays out:
- The API object assembled in the preload has a clipboard
writemember, bound to the literal channelnotion:clipboard:write. - A wrapper forwards that call through
ipcRenderer.invoke. - In the main bundle, another wrapper registers the same channel with
ipcMain.handle. - The receiver checks the sender with
isNotionWebContents, then calls Electron'sclipboard.write(data).
The record is also frank about what the graph tools did with this. A feature trace seeded on __electronApi gave partial coverage, 17 nodes, 16 edges and no terminal paths. A trace seeded on notion:clipboard:write matched nothing, because propagation through the wrappers was unresolved. Neither produced the clipboard route. The record says the four-step figure "summarizes this source reading; it is not an automatically paired IPC graph or a captured desktop execution".
- REA established: the file's identity and parse completeness, and source ranges for the bridge and the invoke, with their dynamic status reported.
- Needed something else: the channel name and the connection across processes, recovered by reading the wrappers and the main bundle. The account of how rich blocks survive a copy comes from a separate web asset cache of 13 July 2026, tested with Node/JSDOM probes, extracted modules and stubs.
- Remained unknown: behaviour in the running desktop app. No live copy and paste was captured, and the case explicitly stops short of proving clipboard hydration or the fidelity of a complex block.
My interpretation: this is a stronger advertisement for the Evidence model than a clean success would have been. channel: null is more useful to an agent than a guess, because it points at the next thing to read, and a wrong channel name would have sent the investigation through minified code after something that doesn't exist.
DX-Ball: when the pseudocode is wrong
This walkthrough shows why a reconstruction needs checking against something other than the representation it came from. REA's DX-Ball case study and evidence record look at one function in the 1996 Windows game: 63 bytes at 0x406400, in the English 1.07 executable, analyzed with REA 4.1.0 and Ghidra 12.1.4. Validation claims are pinned to a reconstruction project commit of 7 October 2026.
The decompiler's output for this function was FUN_00406400(void) and a conversion call. It lost the stack parameter and the entire x87 floating-point expression, so reading only the pseudocode, you would conclude that the function takes nothing and computes nothing of interest. The instruction view told a different story:
0x406409: MOV EAX, dword ptr [EBP + 0x8]
0x40640c: MOV dword ptr [EBP + -0xc], EAX
0x40640f: FILD dword ptr [EBP + -0xc]
0x406415: FMUL double ptr [0x00420068]
0x40641e: FSUB double ptr [0x00420070]
0x406427: FMUL double ptr [0x004210a0]
0x406430: CALL 0x0041678cEach operand is a memory address, so the agent needed more calls. read_bytes at those addresses returned bytes that decode to the doubles 1.5625, 500.0 and an initial scale of 1.0. The caller, a brick-hit handler, multiplies a coordinate by 30, adds 20 and pushes the result, which gives the parameter its meaning as a horizontal screen position. Together, in simplified form:
// Simplified from the recorded observations; names are the project's.
pan = (x * 1.5625 - 500.0) * dxball_pan_scale // then __ftol to an integerBy my arithmetic, at a scale of 1.0 that maps positions 0 to 640 onto −500 to 500, which is why it reads as a stereo pan.
- REA established: the function's instructions, its caller, the raw bytes behind each constant, and the fact that the pseudocode was incomplete.
- Needed something else: the interpretation and names, which belong to the maintained source. The validation also sits outside REA: the project records 3,205 direct differential cases against the original x86 code, over integer positions 0 to 640 at five scales, and 63 matching bytes from a period VC4.0 compile after applying reviewed relocations.
- Remained unknown: integration at the level of the whole game, which the record treats as a separate task. These claims apply to this one function at that checkpoint.
Here the explanation could also be tested, by two independent routes: running the original code on inputs and recompiling to the same bytes. Both checks were done by the reconstruction project, and the REA website's update reused saved results without repeating them. My interpretation is that the byte match isn't a general method, because it depends on a small function and a compiler that can still be reproduced. The differential check is the part that transfers to everyday work.
From recovered rule to working code
Rebuilding a feature is where a developer gets the most value from all this, and the dinosaur example from REA's homepage is a compact demonstration of how to scope the claim. It comes from the same evidence record as Calculator (REA 4.1.0, 9 October 2026).
One correction to the usual description: the target was wayou's Chromium-derived browser edition over HTTP. REA's page inspection doesn't select chrome://dino/. REA attached through a loopback debugging connection and returned the loaded index.js, and its digest matches the public repository. The rule was then short:
// Settings: ACCELERATION 0.001, MAX_SPEED 13, SPEED 6
if (this.currentSpeed < this.config.MAX_SPEED) {
this.currentSpeed += this.config.ACCELERATION;
}The check came from a separate tool. A Playwright controller called the original Runner.update() with obstacles and scheduling disabled, and the record credits this separately from REA's read-only inspection. It found speed 10.000 at 4,000 updates and 13.001 at 10,000, rounded to three decimals. The extra 0.001 is the interesting part. The branch compares before adding, so floating-point increments can cross the threshold by one step. A reader of the constants would write a clamp at 13, and the original has none. The reconstruction keeps that detail. The rest of the mini-game (drawing, jump physics, collision) is newly written teaching code, and the reconstruction claim covers the speed rule only.
That pattern generalizes into a reasonable workflow, and this part is my opinion:
- Scope the claim to one rule and write down what is out of scope. "The speed rule" can be finished and checked. "The game" can't.
- Recover the rule from the artifact, keeping addresses or source ranges with it.
- Reconstruct it separately from everything around it, in your own code and design.
- Compare against the original with inputs chosen to reach the edges: boundaries, thresholds, the path that wasn't exercised by accident. The Aegis case study (REA 6.1.0, 9 October 2026) is a good model. Its three independent implementations agreed on 133 inputs including the RFC 6238 vectors, with recorded limits: the original APK was never run, and vault storage and signature checks were out of scope.
My expectation is that the time saved is mostly in the search, in getting from "where is this" to a function worth reading. I can't put a number on it: the Calculator record says outright that its manual-versus-agent illustration is not a measured comparison. The parts that stay with an engineer are the ones the examples keep surfacing: choosing which rule matters, deciding whether a test covers the risky path, and noticing when a result is clean only because the tool didn't see the hard part.
A second capability: environments from recorded sessions
The walkthroughs so far used REA to understand a program and, at most, to rebuild one rule from it. The saved-traffic row in the earlier table points at a separate use of the same capture and Evidence machinery: building reinforcement-learning environments from a recorded web session. Nobody is trying to explain a feature here. The aim is to give an agent somewhere to practise and be scored, without running the original application.
You record a session against a test instance, either as a HAR file or through a scripted browser scenario, and rea environment-build turns it into a bundle:
rea environment-build ./session.har ./my-env --name my-env --json
agent-env run ./my-envThe environment reference describes the pipeline. The capture is projected into a manifest with one tool per method and path template, carrying inferred path parameters and value-free body shapes. At startup the server derives state from the capture, meaning the entities and documents the reads observed before the first related write. Read tools answer from that state where it reproduces their recordings, and fall back to replaying a recorded response where it doesn't.
Running it needs the AgentEnv framework and Docker. REA's compatibility check uses AgentEnv 0.9.1334 with Python 3.12.
The important rule concerns writes. A POST or DELETE tool is served only if the capture confirmed its effect: a later read has to show the change the plan models. Everything else is withheld, and the report names a reason (unconfirmed, no-observation, side-effect, contradicted) with a hint about what to record next. Without that gate, an environment would accept a delete and do nothing, or apply a change the real application would have refused, and an agent trained or graded on it would learn a false world. So the recording has to be deliberate. The guide's advice is to read an item, change it, then read the item and its list again before the next change.
This is the Evidence idea again, applied to behaviour: the environment refuses to claim what its recording doesn't show. Evaluation also needs repeatability, so generated values are deterministic, the clock advances per served write, and data/reset restores the starting state. The same calls after a reset give the same state, and two runs of a policy can be compared.
The docs are careful about the limits. "Replay is not emulation": an environment doesn't establish the application's authorization model, and it doesn't validate values or model side effects on counters and relations. A served write changes only the one entity it targets. Passing the bundled smoke task shows only that the environment starts and responds. Task goals and reward checks are yours to write, and matching the original app is a separate check, using environment-compare with a fresh recording. REA's own evidence note says its guide makes no claim about training results.
One operational warning: the bundle ships evidence.json, the whole capture with response bodies. Standard authentication headers are excluded, but a custom key header, a token in a query string and tokens returned in bodies all remain, along with the capture file's absolute path. The credential warnings identify places to review and remove nothing, so treat the folder as sensitive until you've read it. And record only against an instance whose data may change, using an app you're permitted to capture.
The limits of the evidence
Most limits are visible above. Collected, they compound.
- External engines and hosts. Deep native work depends on a decompiler you install and, for commercial ones, license. Process capture is unavailable on native Windows, Windows Ghidra is experimental, and several readers are Linux-only or have version floors.
- Incomplete results. DX-Ball's pseudocode dropped a parameter, and the gap showed only in another representation. Notion's channels stayed dynamic, and that showed only to a reader of the result fields.
- Coverage that isn't completeness. A missing match in partial evidence does not establish absence, and static equality between artifacts does not establish unchanged runtime behaviour.
- Edge cases. A reconstruction reproduces what was recovered and tested. Dinosaur's 13.001 survived because somebody looked. Plenty of similar details won't be found.
- Correct evidence, wrong reasoning. The model can misread a branch, attach a name from a similar-looking source tree, or drop a limitation while summarizing. A perfectly accurate record offers no protection once it has been paraphrased. REA's disclaimer covers this directly: decompilation, reconstruction and AI-assisted analysis "may be incomplete or incorrect", and consequential conclusions should be verified against the underlying evidence.
In practice this means reading an agent's REA-backed answer the way you would read a colleague's incident notes. Open the evidence for the two or three claims the conclusion depends on, and ask for the unknowns explicitly.
Privacy, safety and responsible use
"REA analyzes targets locally" describes where the analysis runs, and says nothing about what happens next. Your agent receives the tool results, which can contain decompiled code, strings, script bodies and captured traffic. When the agent uses a hosted model, those results may be sent to its model provider under that provider's data policy, a separate question REA can't answer for you. A locally hosted model can have a different data flow, so the answer depends on how your agent is set up. If a target's contents shouldn't reach a hosted provider, local analysis alone doesn't protect them.
Local also doesn't mean safe. REA's security policy states that its bridge isolation "is not a sandbox", and that opening an untrusted binary delegates parsing to the selected provider with your user's permissions. Parsers have bugs, so even a purely static inspection of a hostile file carries risk. Runtime tools go further and execute the target. REA does not require permission grants or per-call approval flags. Its tool annotations are hints, and any confirmation UI belongs to your MCP client. The disclaimer's advice is to use an appropriate environment for untrusted targets, and it notes that local operation does not "guarantee that executed targets and third-party tools have no external effects". For anything unknown, that means a disposable VM with nothing you'd miss.
Agents also face a risk classic tools didn't: the target can talk to the model. Strings and page text can be written to look like instructions. REA marks results from target-derived tools with untrusted_content: true and tells the agent to treat them as data and report requests to run or download something as injection attempts. Hosts and hooks can use the marker to require approval for process-launching tools after target content has been read, which I would configure before pointing an agent at anything hostile. The marker is a label, so enforcement has to come from your harness.
Authorization is yours to establish. REA's disclaimer limits the project's support to lawful analysis of software you own or are authorized to examine, and notes that having a file, or access to a debugging endpoint, doesn't by itself establish permission. Permission to inspect an artifact doesn't necessarily extend to running it either. The project won't help with circumventing DRM, licence checks or anti-cheat, or with copying proprietary software or reproducing its protected code.
Rebuilding a feature adds its own questions. Reverse engineering may be restricted by your jurisdiction or the software's licence, and the disclaimer states that obtaining source-like output grants no rights to the original program. REA's examples credit their licences explicitly: MIT for Calculator, BSD for the dinosaur code, GPL-3.0 for Aegis. My practical reading, which is not legal advice, is that recovering how something works and copying its expression are different acts, and an agent can blur them if you ask it to "rebuild" something. Implement the rule from your own understanding, and take legal advice before shipping anything derived from a proprietary product.
When it earns its place
The most useful guidance comes from REA's own skill, which opens by limiting itself: for ordinary analysis of a complete source repository, skip it and use normal repository tools. If you have the source, read the source.
The cases that fit are the ones where a claim depends on what actually shipped. A third-party Electron app behaves differently from its docs. You need to know what changed between two releases, which is what compare_application_versions is for. A dependency without source does something surprising in production. A security review needs to establish what a package does, as opposed to what it says. In each of these the alternative is specialist time or a guess, and a checkable partial answer is a real improvement.
It fits poorly when the logic lives on a server, because no amount of client inspection recovers a backend. It also fits poorly when you can't check the result. A reconstruction nobody can compare against the original is just a confident hypothesis with better citations.
What the evidence makes possible
Back to the original question. When an agent can inspect the software behind a feature, the first thing that changes is the unit of its claims: from an explanation that fits what it saw to a statement about a function, a source range or a recorded response.
The more durable change is that those claims can be checked. Every walkthrough here ended with some part unknown: a build commit, a runtime channel, a game-level integration. A tool that hid those gaps would produce better demos and worse engineering.
None of it replaces the person deciding what the question is, whether the check covered the risky path, and whether the result may be used at all. It does give that person something better to review than a plausible paragraph: a list of what was seen, what was concluded from it, and what nobody knows yet.