chore: merge upstream main into FreeBuff client support

Resolve conflicts between the FreeBuff setup support and upstream's
cleanup-aware registration reads and uninstall publication:

- Keep upstream's permission-fail-closed evidence probe as a Result while
  extending it with the explicit-selection and shared-ownership probes.
- Keep upstream's serialized uninstall operations and cleanup retries while
  retaining the FreeBuff/ZCode shared-storage applicability and ownership
  evidence paths.
- Keep upstream's removal refusal algebra and adapt ownership evidence to it.
- Adapt the FreeBuff boundary fake filesystem to publishConfiguration.

Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
This commit is contained in:
0xLLVEL committed 2026-10-19 06:07:55 +07:00
commit e2fdec3847
981 files changed
+150894 -50162

No files matched your search

@@ -0,0 +1,113 @@
---
name: rea-analysis-optimization
description: Optimize REA providers and shared analysis workflows for agent context, latency, CPU, and memory while preserving correctness and Evidence. Use for analysis bottlenecks and cache or query design; CI/test-suite optimization belongs to rea-ci-tests-audit.
---
# REA analysis optimization
Find a concrete source of unnecessary work, change it at its owner, and verify
equivalent completed investigations. A useful outcome can also be a measured
decision to leave an implementation alone.
Follow the checkout's [performance priorities](../../../AGENTS.md#performance),
[tool design](../../../docs/tool-design.md), and
[testing guide](../../../docs/testing.md). This skill supplies an optimization
workflow, not additional permission to execute targets, publish, or merge.
Use the current contracts, provider prerequisites, and user authorization.
Read [the Kuna case study](references/kuna-case-study.md) when selecting a
reuse strategy or interpreting a benchmark. Its pinned observations illustrate
tradeoffs; they are not current-provider guarantees or universal speedups.
## Pin the question and the correctness oracle
Describe the analyst's completed task, including the facts and relationships
needed to answer it, requested detail, source access, and meaningful unknowns.
Choose a representative real artifact and a small or empty case. Record the
baseline revision, target digest, engine identity, options, host, and entrypoint.
Use a separate checkout for changes without disturbing other work.
Establish an oracle independent of the changed implementation: source-built
fixture expectations, independently mapped file bytes, or a native producer
paired with checks of its interpretation. Comparing two wrappers around the
same faulty decoder does not establish correctness. Preserve address widths,
same-address alternatives, unresolved endpoints, coverage, and source linkage.
An empty result proves absence only within the actually examined scope.
## Locate the earliest unnecessary work
Trace one public CLI or MCP task through shared application code to its producer.
Measure relevant phases: admission and identity checks, native analysis,
decoding, lookup, projection, serialization, Evidence retention, and cleanup.
Include catalog discovery and extra calls when they affect the completed task.
Use the evidence to choose the smallest structural correction:
| Observed cost | Candidate correction | Condition to establish |
| --------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| Repeated scans of validated facts | Reuse existing order or lookup structure | The owner establishes ordering and identity; every matching alternative survives |
| Repeated equivalent engine work | Share a validated result or in-flight producer | Inputs, freshness, lifetime, and per-caller outcomes remain explicit |
| Full reports for selected facts | Push selection or projection to the earliest capable owner | Required facts and evidence remain reachable; examined coverage is reported |
| Large temporary transport buffers | Decode incrementally or use bounded source reads | Validate the whole admitted document; retained facts and copies still consume memory |
| Many repeated homogeneous reads | Share admission, decoding, or batch work | Preserve item identity, order where required, and individual failures |
Do not introduce an index, generic cache, batch endpoint, or orchestration layer
without identifying the work it removes and its maintenance/resource cost.
Projection after materializing a full report may reduce output while leaving
analysis time and peak memory unchanged. Fix missing or incorrect facts before
benchmarking an alternative composition against a better-covered workflow.
## Keep one interpretation and a defined reuse lifetime
Let the producer boundary validate and normalize facts once. Consumers query
that representation through the shared CLI/MCP implementation; adapters should
not rebuild relationships or independently reinterpret raw reports. Keep lookup
identity separate from display formatting. Reuse contracts when their intent
matches, honoring requested detail and complete Evidence access.
For every cache or shared producer, state its key, owner, invalidation, retained
size, and lifetime. Account for result-affecting target bytes, engine contents,
analysis options, architecture/load mapping, and parser/schema versions where
they affect reuse. Validate live state before calling a cached observation
current. Cache a refusal only when it is deterministic for that validated key;
malformed, partial, transient, or cancelled attempts need explicit retry policy.
One cancelled waiter must not invalidate a successful peer. Preserve producer
failure context and each caller's cancellation reason. Retain ownership and
capacity charges until shared work and cleanup settle; preserve completed
observations when cleanup fails. Exercise invalidation, concurrent consumers,
last-waiter cancellation, close, and cleanup retry when those paths change.
Bound the process family and retained state, not just a parent heap or worker
count. Account for decoding and serialization expansion before allocation.
Choose CPU, memory, concurrency, and disk limits from the host and workload;
stop before OOM and verify owned cleanup. A guard-stopped task is incomplete.
Do not replace a transport buffer cap with an unbounded retained inventory or
hide partial results behind a success status.
## Compare completed tasks and limit the claim
Run the affected real public workflow before and after with equivalent inputs,
results, coverage, and Evidence semantics. Include small inputs so setup costs
and regressions are visible. Distinguish fresh sessions, warm runtime caches,
and OS file caches. Alternate or repeat runs when variance affects the decision.
Choose meaningful boundary regressions for cases the actual engine cannot
reliably reproduce; a fake engine does not qualify a real provider.
Report task outcome, relevant phase timings, total elapsed and CPU time, peak
process-family memory and its accounting method, resource stops, and cleanup.
Check the payload exit status as well as the profiler's. Keep failed attempts
and identify unmeasured costs; a passing retry does not explain a failure.
For token claims, count schemas/discovery, arguments, replies, retries, and
continuations needed to finish the task. Label tokenizer estimates separately
from actual model usage and record the client/catalog behavior. Smaller JSON
alone establishes neither fewer model tokens nor better investigation quality.
Do not claim a complex-software advantage without completed representative
investigations and comparable correctness/coverage.
Finish with the bottleneck, owning change, preserved semantics, executed
checks, before/after measurements, and remaining limits. Keep any integration
record current with its tested revision and distinguish merged production work
from experiment-only code. Stop when the demonstrated bottleneck is resolved
or further benefit remains speculative.
@@ -0,0 +1,4 @@
interface:
display_name: "REA analysis optimization"
short_description: "Optimize analysis with preserved Evidence and measured costs"
default_prompt: "Use $rea-analysis-optimization to find and verify a concrete analysis bottleneck while preserving correctness and Evidence."
@@ -0,0 +1,112 @@
# Kuna: reuse before adding machinery
Read this reference for concrete examples of the skill's decisions. The records
below concern static Linux analysis with caller-supplied Kuna. They do not
establish parity with another provider or a general advantage for Code Mode,
atomic tools, or any particular orchestration design.
## Exact-address lookup: a measured, narrow improvement
[PR #4695](https://github.com/morluto/rea/pull/4695) reused the validated string
inventory's numeric address order. Previously, each `list_strings(address)`
filtered all cached facts. The owner now performs a binary lower-bound lookup
and returns every encoding at that address: O(log n + matches), without a
second retained lookup index.
The owning implementation is
[`KunaStringInventory.ts`](../../../../src/kuna/KunaStringInventory.ts), consumed
by [`KunaStringQueries.ts`](../../../../src/kuna/KunaStringQueries.ts). Inspect
their current versions before adapting the example. Numeric u64 addresses,
same-address encodings, duplicate rejection, source digests, caller selection,
and the existing session lifetime were part of the contract. Full listings and
literal/regex searches kept their semantics. An attempted removal of the
duplicate-detection set was dropped: no stable memory benefit was established,
and it delayed malformed-input rejection.
### Recorded workload and results
The benchmark used unmodified Kuna 1.833 and LLVM 16 on Linux x86-64, with
602,544 string facts. Baseline was `be0b2d138`; the final feature was
`5f564962da0e1b17f00a68a74a312539f0209a01`, subsequently integrated in
`c6b62754b095a134d6d67877508fa34311a2eda2`. These are historical observations,
not a benchmark of every subsequent main revision.
The same production stdio MCP task discovered the catalog, opened the binary,
made one cold selected read and 28 warm exact reads, checked retained Evidence,
and closed the session. Five selected addresses and two absent selectors were
read four times. A native filtered scan selected the strings; an independent
ELF load-header mapping checked the source bytes and terminators. Returned
facts, source linkage, target digests, and cleanup were checked.
Three alternating fresh sessions per implementation used one CPU, a 4 GiB
process-family hard limit, a 3 GiB early stop, and no swap. OS file caches were
warm. Whole-harness costs include the independent native oracle, hashing,
catalog/client work, and correctness checks.
| Median | Before | After |
| ---------------------------------------- | ---------: | ---------: |
| 28 warm exact MCP reads | 495.2 ms | 90.8 ms |
| Cold selected MCP read | 11.30 s | 11.01 s |
| Whole harness elapsed | 28.24 s | 27.43 s |
| Whole process-family CPU | 27.71 s | 26.89 s |
| Kernel family peak, including file cache | 2237.6 MiB | 2240.6 MiB |
Warm totals ranged from 491.7–544.3 ms before to 84.8–99.5 ms after: about
5.5 times faster at this phase. Cold/whole-task timing and memory overlapped;
no stable improvement in those costs was established. One small SQLite pair
(7,985 facts) measured 20 warm reads at 88.6 → 80.5 ms and the whole harness at
13.68 → 13.94 s. The large-inventory lookup benefit does not generalize to a
whole-task speedup on small inputs.
Catalog hashes, call counts, and normalized facts were unchanged. A
`cl100k_base` estimate over one pass of raw catalog JSON, arguments, and replies
was about 846,000 tokens in both lanes, with about 802,562 from the catalog.
This was transport serialization, not rendered model context or actual usage;
client discovery, prompts, replay, and protocol framing were unmeasured.
**No token reduction or improved agent investigation outcome was established.**
Pinned target SHA-256:
`f62d254b7f2bf42df8c8b07d46ee3bb4c2cafeca436b2e6bc6ccbe4581f58f40`.
Pinned engine SHA-256:
`d858918b0766435cd96b999a77f2d5a31a0cc4f07256421c883b5b9dc4dd58e4`.
All six normalized-result sequence digests were
`4d2f90269da8568984090b20274163c4a7c2a0538aab63530bd2fe44840a0bba`.
Selected byte agreement does not prove native discovery completeness; missing
strings and relationships can remain unknown.
## Other transferable changes and their limits
- [Cached string inventory and search, #4095](https://github.com/morluto/rea/pull/4095):
share validated facts across queries in a defined session. Audit key inputs
and lifetime before extending reuse across targets or engine revisions.
- [Shared single-function work, #4045](https://github.com/morluto/rea/pull/4045)
and [repeat-request reuse, #4169](https://github.com/morluto/rea/pull/4169):
avoid duplicate native work while retaining per-caller cancellation and
cleanup ownership. A query count alone cannot reveal duplicate subprocesses.
- [Streamed strings, #4318](https://github.com/morluto/rea/pull/4318)
and [streamed call graph, #4329](https://github.com/morluto/rea/pull/4329):
remove fixed aggregate transport-buffer failures through incremental parsing.
Streaming transport does not bound every retained object or final reply.
These links establish changes; no numerical speedup is asserted here.
Typed reference facts, native string-query projection, and shared native byte
reads remained experimental at the 2026-10-17 checkpoint
`65f2f9a22bc892f61d2b00f9a4611124f57d4bff` on
`experiment/atomic-codemode-20261016`. Some experiments used a patched native
engine. Check current integration and bring-your-own engine capabilities before
proposing them; a working experiment is not a stock-provider guarantee.
## Applying the reasoning elsewhere
For another provider or format, inspect its actual representation and native
capabilities before borrowing the implementation. Existing numeric ordering
may support range reads; an on-disk native query may avoid materializing an
inventory; an immutable parse may support several views. Measure the relevant
alternative rather than requiring every provider to use Kuna's cached array.
For live observations, immutable artifact caching rules are insufficient:
validate the process/session identity and freshness required by the caller.
For compact replies, preserve access to source Evidence and report examined
coverage; projection must not silently turn an unresolved edge into no edge.
For batching, test mixed successes and failures and measure the entire task,
including discovery, extra calls, native work, and serialization.
@@ -124,6 +124,26 @@ operation's full ordered list. Candidate capabilities describe possible
providers. Top-level session capabilities describe the current route, and
`analysis_provider_binding` identifies the selected provider.
## Static memory and original file bytes
For Ghidra, `read_bytes` reads initialized analysis memory, including loader
fixups; it does not read a running process. Check `returned_bytes` and `complete`
before decoding a table. An incomplete read is missing coverage, not a zero-filled
tail. Use `list_segments` to inspect the selected block's initialization and address-space
facts, and retain unavailable facts as unknown.
Use `address_to_file_offset` to anchor one observed address to original file
bytes. A mapping for the first address does not establish a contiguous mapping
for an entire table, and loader fixups can make analysis bytes differ from source
bytes. Keep source ranges and analysis-memory observations separate.
A pointer table can refer to writable slots populated by a constructor rather
than directly to immutable resources. An uninitialized block or an observed zero
does not establish the slot's later runtime value. Follow `xrefs` and the relevant
writer's assembly or pseudocode; distinguish recorded assignments from inferred
execution order. Do not replace unreadable bytes with zeros or conclude that a
resource is absent without the necessary initialization evidence.
## DOS MZ with Ghidra
For an admitted DOS MZ target, use `inspect_native_load_image` to check measured
@@ -133,10 +153,8 @@ reported static image, not runtime DOS or PC-98 hardware behavior.
For Ghidra decompilation, `procedure_pseudo_code` also accepts `procedure: "file-offset:0x..."`; CLI `rea decompile <path> --file-offset=0x... --provider ghidra` resolves the source byte offset internally. An unresolved 0x-prefixed numeric procedure selector also falls back to a unique file mapping. Existing symbols and analysis addresses win; multiple mappings fail as ambiguous. Preserve the returned file-offset and address locations and raw selection observations. ELF virtual addresses are distinct from file offsets; never assume an arbitrary offset plus image base is an analysis address.
Use `read_bytes` for initialized analysis memory and `address_to_file_offset` to
anchor one observed address to original file bytes. Loader fixups can change a
word; a source offset does not imply byte equality. Complete function body ranges
use inclusive ends and can contain gaps. Do not treat the enclosing span as code.
Complete function body ranges use inclusive ends and can contain gaps. Do not
treat the enclosing span as code.
For packed targets, retain the original and separately derived artifact identities;
decompiling an unpacking stub does not recover the unpacked program.
@@ -24,19 +24,7 @@ runs:
BUILD_CACHE_PREFIX: rea-portable-build-v2-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.npmrc', '.nvmrc', 'package-lock.json') }}
run: |
npm run --silent test:prepare -- --dry=json |
node --input-type=module -e '
import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";
const { tasks } = JSON.parse(readFileSync(0, "utf8"));
const hashes = tasks.map(({ taskId, hash }) => {
if (typeof hash !== "string")
throw new Error(`Build task ${taskId} must have an eager Turbo hash`);
return `${taskId}:${hash}`;
}).sort();
const hash = createHash("sha256").update(hashes.join("\n")).digest("hex");
console.log(`key=${process.env.BUILD_CACHE_PREFIX}-${hash}`);
console.log(`prefix=${process.env.BUILD_CACHE_PREFIX}-`);
' >> "$GITHUB_OUTPUT"
node scripts/ci/portable-build-cache.mjs key >> "$GITHUB_OUTPUT"
- name: Restore portable build outputs
if: inputs.build_cache_artifact == '' && inputs.save != 'true'
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
+8 -1
View File
@@ -357,10 +357,15 @@ jobs:
# test:prepare caches only portable JS, skill, and catalog outputs.
# Native package jobs still run their build and prepack checks; Turbo
# restores matching outputs or rebuilds when their inputs differ.
- name: Stage current portable build task cache
shell: bash
run: |
# A new Turbo dry run can evict the entries this build just used.
node scripts/ci/portable-build-cache.mjs snapshot < .cache/ci-build-plan.json
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: rea-build-cache
path: .turbo/cache/
path: .cache/ci-build-cache/
include-hidden-files: true
if-no-files-found: error
retention-days: 7
@@ -648,6 +653,7 @@ jobs:
tests/boundary/cli/nativePlistPrototypeKeyCli.test.ts
tests/boundary/cli/nativePlistSelectionFailureCli.test.ts
tests/boundary/cli/nativeDemangleCli.test.ts
tests/boundary/cli/nativeDispatchPropertyAttributes.test.ts
tests/boundary/mcp/nativeDemangleMcp.test.ts
tests/boundary/providers/native/nativeSignatureTargetBinding.test.ts
tests/boundary/cli/nativeSignatureCli.test.ts
@@ -748,6 +754,7 @@ jobs:
tests/boundary/filesystem/webScriptManifestPaths.test.ts
tests/boundary/process/ProcessEvidence.host.test.ts
tests/boundary/process/packageNpmInvocation.test.ts
tests/boundary/process/packedBridgeVerification.test.ts
tests/boundary/process/windowsOutputCapture.test.ts
tests/acceptance/webModuleCli.test.ts
tests/acceptance/webSourceLocationCli.test.ts
+12 -5
View File
@@ -24,7 +24,19 @@ jobs:
real-recorded-crash:
runs-on: ubuntu-24.04
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
image: ["ubuntu:24.04", "debian:trixie-slim"]
container: ${{ matrix.image }}
steps:
- name: Install fixture and debugger prerequisites in the disposable container
shell: bash
run: |
apt-get update -qq
apt-get install --yes --no-install-recommends \
ca-certificates git python3-venv gcc libc6-dev strace gdb
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
@@ -36,11 +48,6 @@ jobs:
- uses: ./.github/actions/portable-build-cache
with:
build_cache_artifact: ${{ inputs.build_cache_artifact }}
- name: Install GDB on the disposable CI runner
shell: bash
run: |
sudo apt-get update -qq
sudo apt-get install --yes --no-install-recommends gdb
- name: Acquire exact unchanged upstreams in isolated runner storage
shell: bash
run: |
+1 -1
View File
@@ -1,3 +1,3 @@
{
".": "6.5.0"
".": "6.7.0"
}
+88
View File
@@ -1,5 +1,93 @@
# Changelog
## [6.7.0](https://github.com/morluto/rea/compare/rea-agents-6.6.0...rea-agents-6.7.0) (2026-10-18)
### Highlights
- Client configuration edits follow the duplicate-member semantics used by MCP clients.
### Changes
- Includes the [6.6.0 changes and migration notes](https://github.com/morluto/rea/releases/tag/rea-agents-6.6.0). That version was tagged but was not published to npm.
### Fixes
- **Client configuration:** Setup edits the last repeated JSON/JSONC member, matching the value clients actually read. Setup and uninstall refuse a removal that would reactivate a shadowed duplicate, preserving the original configuration. ([#4957](https://github.com/morluto/rea/pull/4957), [morluto/rea#4952](https://github.com/morluto/rea/issues/4952)). Thanks @kaoru0822-kitauji, @xu91102.
Full contribution record: [4 commits and 1 merged PR](https://github.com/morluto/rea/blob/rea-agents-6.7.0/docs/releases/6.7.0.contributions.json).
## [6.6.0](https://github.com/morluto/rea/compare/rea-agents-6.5.0...rea-agents-6.6.0) (2026-10-18)
### Highlights
- Add Kuna as a caller-supplied static-analysis provider.
- Query selected JavaScript facts and multiple APK locale tables without repeating the underlying analysis.
- Extend Ghidra analysis to Windows ARM PE targets and explicit raw m68k images.
- Preserve completed Evidence, cancellation reasons and retryable cleanup across provider and CLI finalization.
### Changes
- **Kuna analysis:** Use a caller-installed Kuna for procedure listings, pseudocode, assembly, call relationships, exact-address xrefs and cached string searches. Project exports retain source spans and digests; repeated requests reuse completed work. Recorded real-provider coverage uses Kuna 1.833 on Linux; Windows, PE and universal Mach-O are unsupported. See [the provider guide](docs/kuna-provider.md). ([#3832](https://github.com/morluto/rea/pull/3832), [#3854](https://github.com/morluto/rea/pull/3854), [#4071](https://github.com/morluto/rea/pull/4071), [#4095](https://github.com/morluto/rea/pull/4095)). Thanks @gensokyo-dev, @kaoru0822-kitauji, @N0zoM1z0.
- **Retained JavaScript facts:** Query exact application paths, selected properties and filtered module references through `inspect_analysis_view`, with paging and complete row Evidence. These views reuse completed analysis and preserve source selection and unknown coverage. ([#4265](https://github.com/morluto/rea/pull/4265), [morluto/rea#4190](https://github.com/morluto/rea/issues/4190), [#4097](https://github.com/morluto/rea/pull/4097), [morluto/rea#4092](https://github.com/morluto/rea/issues/4092)). Thanks @kaoru0822-kitauji, @N0zoM1z0.
- **APK locale tables:** Request several resource locales in one decode with repeatable CLI `--locales` or MCP `locales`. Each table reports its status and coverage; existing single-locale fields remain available. ([#4804](https://github.com/morluto/rea/pull/4804), [morluto/rea#4777](https://github.com/morluto/rea/issues/4777)). Thanks @abhay-2108, @N0zoM1z0.
- **Windows ARM binaries:** Ghidra admits ARMNT/Thumb-2 and ARM64 PE applications and DLLs on the Windows x64 host lane. Recorded Ghidra 12.1.4 verification uses `WindowsResourceReference` disabled and raised startup budgets; default-analyzer completion, ARM64EC/ARM64X and Windows-on-Arm hosts remain unverified or unsupported. See [the exact scope](docs/windows-ghidra-p0.md#native-authority-and-target-identity). ([#3965](https://github.com/morluto/rea/pull/3965), [morluto/rea#2186](https://github.com/morluto/rea/issues/2186)). Thanks @N0zoM1z0, @WestLabsAI.
- **Raw m68k images:** Analyze explicitly selected big-endian m68k raw binaries using Ghidra’s fixed 68000 profile and a caller-supplied aligned entry. Real CLI/MCP verification used Ghidra 12.1.4 on macOS arm64; Linux is unverified and Windows P0 does not admit m68k. See [mapping requirements](docs/raw-binary.md). ([#4018](https://github.com/morluto/rea/pull/4018)). Thanks @icedac.
- **JADX deadlines:** Set `REA_JADX_OPERATION_TIMEOUT_MS` to extend APK loading and upstream operation deadlines together. Doctor reports the effective value and setup forwards an explicit selection. ([#4466](https://github.com/morluto/rea/pull/4466), [morluto/rea#3918](https://github.com/morluto/rea/issues/3918)). Thanks @jaychou813, @SandstromPL.
- **Electron window options:** Resolve stable local constant `BrowserWindow` options while retaining source provenance. Relevant writes and escapes keep the result unknown. ([#4400](https://github.com/morluto/rea/pull/4400), [morluto/rea#4366](https://github.com/morluto/rea/issues/4366)). Thanks @kaoru0822-kitauji, @N0zoM1z0.
- **Language editions:** Add the Greek website edition and Malayalam documentation, with Korean typography and translation corrections across the site. ([#3520](https://github.com/morluto/rea/pull/3520), [#4792](https://github.com/morluto/rea/pull/4792), [#3821](https://github.com/morluto/rea/pull/3821), [morluto/rea#3820](https://github.com/morluto/rea/issues/3820), [#3799](https://github.com/morluto/rea/pull/3799), [morluto/rea#3798](https://github.com/morluto/rea/issues/3798), [#4344](https://github.com/morluto/rea/pull/4344), [#3856](https://github.com/morluto/rea/pull/3856), [#4166](https://github.com/morluto/rea/pull/4166)). Thanks @gensokyo-dev, @hi2242, @kalayciburak, @kaoru0822-kitauji, @kraken-503, @kukinghan, @N0zoM1z0, @yeseong0412.
### Fixes
- **Cancellation and cleanup:** Keep completed observations, exact cancellation reasons and the original physical failure through finalization. Failed cleanup retains its process or file owner for retry; recovered resources disappear from pending-cleanup reports without replacing the original error. ([#4238](https://github.com/morluto/rea/pull/4238), [morluto/rea#4232](https://github.com/morluto/rea/issues/4232), [#4274](https://github.com/morluto/rea/pull/4274), [morluto/rea#3923](https://github.com/morluto/rea/issues/3923), [3e82366](https://github.com/morluto/rea/commit/3e8236639abffa39deb5d5e939ec8baf6aaf3ca3), [16924e2](https://github.com/morluto/rea/commit/16924e275e939d0543ba01493324959a9e1d731c), [bbfe6ed](https://github.com/morluto/rea/commit/bbfe6edd60290f9bfbec68c8a0bcf605d8bbf5b3), [1f23e97](https://github.com/morluto/rea/commit/1f23e977b50301e91f02a4a17c9f8d48bb7b60f3), [08fbeb8](https://github.com/morluto/rea/commit/08fbeb8bdab27118b6c57039aef7c0fa8b6463bc)). Thanks @coygeek.
- **Provider startup:** Join prerequisite probes and in-flight readiness work before shutdown, preserve typed timeout and JVM-memory failures, and retain startup diagnostics through cleanup. Ghidra recognizes keg-only Homebrew JDKs and names the setting responsible for a startup timeout. ([376eeca](https://github.com/morluto/rea/commit/376eeca770820b31f7f8d4c5fcc397315621643e), [ceeff5a](https://github.com/morluto/rea/commit/ceeff5a41788e47e5aee96a4baf191a6ce7fde4b), [3ad1032](https://github.com/morluto/rea/commit/3ad1032b11edea2dcb054e78f951cc43159ae83d), [964decd](https://github.com/morluto/rea/commit/964decd989acfa84c4bfc3295362c4c46877fc20), [3fa972a](https://github.com/morluto/rea/commit/3fa972af7c7cb6cf014ffc426bdf7130dc6f2fd2), [#4557](https://github.com/morluto/rea/pull/4557), [morluto/rea#4556](https://github.com/morluto/rea/issues/4556), [#4590](https://github.com/morluto/rea/pull/4590), [morluto/rea#4241](https://github.com/morluto/rea/issues/4241)). Thanks @DanielCvz1, @J0UH, @Rinkia, @RishTheFish21, @vadlachary2002.
- **Client configuration:** Preserve the selected launcher, destination file, caller timeout choices and unresolved configuration evidence. Setup/uninstall recovery stays bound to the exact published file, keeps current verified backups, and transfers failed cleanup ownership for retry. ([d0253c9](https://github.com/morluto/rea/commit/d0253c99d65afbbd12732a2fdaf711640574e17f), [7e4eed9](https://github.com/morluto/rea/commit/7e4eed9dd48485b6a7100300aa98401811c2deb9), [2fb5e20](https://github.com/morluto/rea/commit/2fb5e204a35723f6f843d56ca47b0db94d8fcc44), [8235210](https://github.com/morluto/rea/commit/823521085a28f825e7fee1929d852d420042d1f7), [d818c3d](https://github.com/morluto/rea/commit/d818c3d2639d3115edb8a5269a754101aea6c3bd), [19329e4](https://github.com/morluto/rea/commit/19329e43bb38ca9d64512d801b25abcb188d2512), [dadf909](https://github.com/morluto/rea/commit/dadf909dce2e8b3d055541de69b4118d21276316), [333e0ab](https://github.com/morluto/rea/commit/333e0ab0dfc126a9915907d5079ed6195190e38e), [c15c206](https://github.com/morluto/rea/commit/c15c2062ad815cc9a3222ccfaeb7ef53cacd8b72)). Thanks @Adolanium.
- **Configuration formats:** Read signed 64-bit TOML integers without rounding, handle large TOML documents without argument-spread failures, preserve JSONC trivia and Hermes YAML scalar text, and reject NUL bytes in registration environment values. ([#4937](https://github.com/morluto/rea/pull/4937), [morluto/rea#4917](https://github.com/morluto/rea/issues/4917), [c43bc0b](https://github.com/morluto/rea/commit/c43bc0bf66a4e245a0cafa40def0d0207bcbd73c), [4fd28fc](https://github.com/morluto/rea/commit/4fd28fc9e0c4f8358147af38c72ab9e814f52d61), [#3568](https://github.com/morluto/rea/pull/3568), [#4335](https://github.com/morluto/rea/pull/4335)). Thanks @alfredo1491, @gensokyo-dev, @kaoru0822-kitauji, @N0zoM1z0, @ShuaixinHuang, @xu91102.
- **Claude Code configuration:** Setup, doctor and uninstall now accept only the strict JSON format Claude Code reads, including a leading UTF-8 BOM. Comments and trailing commas are refused before backup or writing; clients that support JSONC retain their existing behavior. ([#4944](https://github.com/morluto/rea/pull/4944), [morluto/rea#4940](https://github.com/morluto/rea/issues/4940)). Thanks @kaoru0822-kitauji, @xu91102.
- **CLI output and dispatch:** Accept `--flag=value` and spaced global output values, keep array positions in filtered JSON, validate integer tokens before coercion, and exit quietly when the output reader closes. Cancellation remains effective through command finalization; app bundles and static native inspections select the intended workflow. ([#3811](https://github.com/morluto/rea/pull/3811), [#4823](https://github.com/morluto/rea/pull/4823), [2ae4e16](https://github.com/morluto/rea/commit/2ae4e1608f290c95ff7cff28c4d16a79b301d03d), [8d82579](https://github.com/morluto/rea/commit/8d8257928bbe8d43fedffc4db5fca26c8108820f), [#3781](https://github.com/morluto/rea/pull/3781), [279c590](https://github.com/morluto/rea/commit/279c590ef6bb737d4a48ea696a0ae5eb30010b3d), [#3511](https://github.com/morluto/rea/pull/3511), [morluto/rea#3321](https://github.com/morluto/rea/issues/3321), [f31b231](https://github.com/morluto/rea/commit/f31b231a92a61be6bd1800b16be0b3210ae82944)). Thanks @arjunprabhulal, @coygeek, @kaoru0822-kitauji, @ShuaixinHuang, @Sriram-PR, @Tapraj-Pathak.
- **Updates:** Route `--update` through the verified updater and bound retained package-manager diagnostics. ([4f7b5fb](https://github.com/morluto/rea/commit/4f7b5fb6023ce2ec449561782d09e3c7e5ac7c7b), [01cdbbd](https://github.com/morluto/rea/commit/01cdbbdc556262c47d04a5b3391dd9c17946d3fc)).
- **MCP contracts:** Advertise self-contained tool inputs while preserving tuple, array, Boolean and nested-JSON constraints across schema profiles. Schema-reference handling no longer rewrites literal metadata; catalog identity includes the output-schema profile. ([5e30969](https://github.com/morluto/rea/commit/5e30969cc5f1255d44ec7a1f1ca8dbac0af5e7c4), [10aa74d](https://github.com/morluto/rea/commit/10aa74dfedc3ae776f50d1fa1eb83b64ae82d3d6), [204b9f3](https://github.com/morluto/rea/commit/204b9f3ac140a993c3e458bb0e117de31818ab29), [b3389a5](https://github.com/morluto/rea/commit/b3389a5b0834265fe7a40391be315d810a53a97e), [a2e3016](https://github.com/morluto/rea/commit/a2e30168a046f3b79319eb7fbaf807b802104199), [ba98130](https://github.com/morluto/rea/commit/ba98130049fa40ade66b03611ff70c7ddc011a1d), [c5fb86d](https://github.com/morluto/rea/commit/c5fb86d1948af0a287a581b29b2b1d554743c607)). Thanks @akushonkamen, @HarsH-1877, @kaoru0822-kitauji, @xingzihai.
- **Session ordering:** Keep snapshot admission ahead of later calls on the same connection, retain shutdown signals until cleanup succeeds, and preserve inspection authority through investigation publication. ([#3661](https://github.com/morluto/rea/pull/3661), [morluto/rea#3334](https://github.com/morluto/rea/issues/3334), [8873b39](https://github.com/morluto/rea/commit/8873b39770e89eae22b7693bc492d7e423b22bea), [663d07f](https://github.com/morluto/rea/commit/663d07f02cd62c99d81c59391d2c6d745c875335)). Thanks @coygeek, @rudycelekli, @Swatantra-66.
- **JavaScript exports and resolution:** Retain CommonJS exports created with `Object.defineProperty` and track export receiver lifetimes. Resolve scoped packages, package `main`, JSON requires and `.mts`/`.cts` imports using the owning loader; overwritten `require` bindings and erased type-only imports no longer create false runtime links. ([#4725](https://github.com/morluto/rea/pull/4725), [morluto/rea#4724](https://github.com/morluto/rea/issues/4724), [#3567](https://github.com/morluto/rea/pull/3567), [morluto/rea#3541](https://github.com/morluto/rea/issues/3541), [#4838](https://github.com/morluto/rea/pull/4838), [morluto/rea#4632](https://github.com/morluto/rea/issues/4632), [#3844](https://github.com/morluto/rea/pull/3844), [morluto/rea#3383](https://github.com/morluto/rea/issues/3383), [#4164](https://github.com/morluto/rea/pull/4164), [morluto/rea#3915](https://github.com/morluto/rea/issues/3915), [#4223](https://github.com/morluto/rea/pull/4223), [#3801](https://github.com/morluto/rea/pull/3801), [morluto/rea#3393](https://github.com/morluto/rea/issues/3393), [#4165](https://github.com/morluto/rea/pull/4165), [morluto/rea#3932](https://github.com/morluto/rea/issues/3932)). Thanks @Abishek0070, @coygeek, @JNTMTMTM, @kaoru0822-kitauji, @prasad2512, @rodricksz4h5, @xu91102.
- **JavaScript value flow:** Preserve values captured before later assignments, guarded factory fallthrough and nested destructuring. Track exact property keys and setter writes; direct `eval`, compound assignments and incoming callbacks preserve unresolved effects instead of inventing certainty. ([#4196](https://github.com/morluto/rea/pull/4196), [morluto/rea#3881](https://github.com/morluto/rea/issues/3881), [#4341](https://github.com/morluto/rea/pull/4341), [morluto/rea#3963](https://github.com/morluto/rea/issues/3963), [bfb7ed0](https://github.com/morluto/rea/commit/bfb7ed0bd43edc605e00fc52c6e21e735b285904), [#3480](https://github.com/morluto/rea/pull/3480), [#3831](https://github.com/morluto/rea/pull/3831), [morluto/rea#3312](https://github.com/morluto/rea/issues/3312), [9ac76d3](https://github.com/morluto/rea/commit/9ac76d3d9486856d25395b413d7ac5ffa744cad0), [#4342](https://github.com/morluto/rea/pull/4342), [morluto/rea#4206](https://github.com/morluto/rea/issues/4206), [#4243](https://github.com/morluto/rea/pull/4243), [morluto/rea#3880](https://github.com/morluto/rea/issues/3880)). Thanks @coygeek, @kaoru0822-kitauji, @maxazure, @N0zoM1z0, @ShuaixinHuang.
- **JavaScript relationships:** Distinguish `Map` reads from network endpoints, associate response endpoints only with writes that reach the read, and preserve child-process receiver ownership and effective stdio choices. Version comparisons keep relocation and unknown relationships distinct from application changes. ([fbfdb6a](https://github.com/morluto/rea/commit/fbfdb6abe9d663be0be146a15a489e33ccba0f68), [#4759](https://github.com/morluto/rea/pull/4759), [morluto/rea#4606](https://github.com/morluto/rea/issues/4606), [#3819](https://github.com/morluto/rea/pull/3819), [morluto/rea#3362](https://github.com/morluto/rea/issues/3362), [#4336](https://github.com/morluto/rea/pull/4336), [morluto/rea#3355](https://github.com/morluto/rea/issues/3355), [556504e](https://github.com/morluto/rea/commit/556504edcbb0f8cd9eaf81a91b18ff891b8f5457), [4d980d0](https://github.com/morluto/rea/commit/4d980d017622d3175d86aaf3b0dcfbdf4ca3d091)). Thanks @Abdooo2235, @coygeek, @kaoru0822-kitauji, @Xinglan233.
- **JavaScript source admission:** Parse JavaScript with its own grammar, refuse FIFO recovery inputs without blocking, and report oversized or resource-skipped files with their actual capacity and coverage state. Malformed runtime events stay excluded rather than becoming policy-filtered facts. ([#3782](https://github.com/morluto/rea/pull/3782), [morluto/rea#3780](https://github.com/morluto/rea/issues/3780), [#3788](https://github.com/morluto/rea/pull/3788), [morluto/rea#3332](https://github.com/morluto/rea/issues/3332), [#4757](https://github.com/morluto/rea/pull/4757), [morluto/rea#4629](https://github.com/morluto/rea/issues/4629), [#3852](https://github.com/morluto/rea/pull/3852), [morluto/rea#3670](https://github.com/morluto/rea/issues/3670), [#4755](https://github.com/morluto/rea/pull/4755), [morluto/rea#4685](https://github.com/morluto/rea/issues/4685)). Thanks @andresaragon, @coygeek, @kaoru0822-kitauji, @sx4im.
- **Browser capture:** Bound CDP handshakes and command responses, preserve malformed inventory uncertainty and partial cleanup diagnostics, and report cancellation after content reads and final cleanup. Accessibility comparisons require observed coverage; attached emulation teardown limits remain explicit. ([#3555](https://github.com/morluto/rea/pull/3555), [morluto/rea#3553](https://github.com/morluto/rea/issues/3553), [#3901](https://github.com/morluto/rea/pull/3901), [morluto/rea#3379](https://github.com/morluto/rea/issues/3379), [4cc044b](https://github.com/morluto/rea/commit/4cc044be68f813e96907e6cedcf422d0a3170de3), [#4406](https://github.com/morluto/rea/pull/4406), [morluto/rea#4403](https://github.com/morluto/rea/issues/4403), [#2893](https://github.com/morluto/rea/pull/2893), [0123d59](https://github.com/morluto/rea/commit/0123d5976828bfe621bd51daa4cf39fee48a1349), [2ff0867](https://github.com/morluto/rea/commit/2ff086796ad7b8f60397956750584e170dbe2910)). Thanks @coygeek, @dakjdakd, @kaoru0822-kitauji, @maxazure, @N0zoM1z0, @YunzhouLi-hub.
- **Browser page facts:** Preserve URL path bytes when removing credentials, repeated document `Link` headers and complete COOP/COEP structured items. WebMCP discovery reads the actual schema properties and exact receiver path. ([#4731](https://github.com/morluto/rea/pull/4731), [df2fc25](https://github.com/morluto/rea/commit/df2fc251e318a1255309dba232f9f73c700777b6), [c360cfc](https://github.com/morluto/rea/commit/c360cfcd6d7901514b16dd6113eba2e0b0fdf422), [ebd5a82](https://github.com/morluto/rea/commit/ebd5a82c439f6deb3a06062225ce009eb03257cf), [38f92d1](https://github.com/morluto/rea/commit/38f92d108e3148cdf47e4ffbfe3de8a76e795d51)). Thanks @Abdooo2235, @geekwolf, @ShuaixinHuang.
- **Electron runtime capture:** Keep native IPC listener and one-shot dispatch behavior, block synchronous shell effects during capture, refuse active scenarios when the inspect fuse is disabled, and preserve fuse/transport cleanup ownership. ([#4389](https://github.com/morluto/rea/pull/4389), [morluto/rea#4390](https://github.com/morluto/rea/issues/4390), [#4605](https://github.com/morluto/rea/pull/4605), [morluto/rea#4593](https://github.com/morluto/rea/issues/4593), [#4562](https://github.com/morluto/rea/pull/4562), [morluto/rea#4393](https://github.com/morluto/rea/issues/4393), [#3605](https://github.com/morluto/rea/pull/3605), [morluto/rea#3453](https://github.com/morluto/rea/issues/3453), [f9de413](https://github.com/morluto/rea/commit/f9de41371f7536e2e22ce6e43c9314fa2f238a30), [e7df00e](https://github.com/morluto/rea/commit/e7df00e80777757b329bbe8aba6d421d8892b9d5)). Thanks @Adolanium, @gensokyo-dev, @gkobayashi0, @HarsH-1877, @kaoru0822-kitauji, @keeponlight, @N0zoM1z0, @Nitin13-git, @xu91102.
- **APK resource accuracy:** Retain completed tables and decode facts after interrupted or failed reads. Derive locale limits from resource configuration fields, disclose unselectable qualified tables, distinguish document failures from omitted entries, and bind Evidence to the decoded snapshot. ([34a88d6](https://github.com/morluto/rea/commit/34a88d6c1674710953e2c94bd0a17332cd69ca6f), [f5fcf52](https://github.com/morluto/rea/commit/f5fcf5216ffb3a266b8c00f6d9a0943051084ab5), [6385ef4](https://github.com/morluto/rea/commit/6385ef4355d7a794b3154ee38529cbddee583336), [98886ad](https://github.com/morluto/rea/commit/98886adddfcbd7b4f4236b3a9825f91878c3cb11), [dc2e811](https://github.com/morluto/rea/commit/dc2e8117b60c4481372ed89b077e6c1cf81ee653), [#4771](https://github.com/morluto/rea/pull/4771), [morluto/rea#4768](https://github.com/morluto/rea/issues/4768)). Thanks @abhay-2108, @gensokyo-dev, @Iyanuoluwa007, @kaoru0822-kitauji, @N0zoM1z0.
- **Apktool guidance:** Retain the observed version and disclose the matching platform/release advisory windows without treating a version string as proof of decoder safety. Missing prerequisites and decode refusals identify the selected APK or launcher. ([d38f099](https://github.com/morluto/rea/commit/d38f099692394ec90c582f9f0a14b036b421b160), [63696d2](https://github.com/morluto/rea/commit/63696d277328f556c971d1fc9f81ae83128d468b), [cc038da](https://github.com/morluto/rea/commit/cc038da218cada68c87700477aba3db4739791ff)). Thanks @GovinduSasanka, @peter-research.
- **Android method bodies:** Recover original bytecode bodies when Java source is omitted while preserving genuine native/abstract body absence and primary failures during retryable cleanup. ([63c65a2](https://github.com/morluto/rea/commit/63c65a22430a4bbc0c1807a0298edb867775001f), [742b6f5](https://github.com/morluto/rea/commit/742b6f587f3c4ef1b8212e7376d6f3a305b194dc), [9fa6d2b](https://github.com/morluto/rea/commit/9fa6d2b254cc390665974efb52463386a029e9fc)). Thanks @balkan16, @Chessing234.
- **ADB observations and transfers:** Preserve display-scoped focus, distinguish missing declarations from absent focus, retain numeric physical density and mark unknown permission coverage partial. Transfers use consumed snapshot identity and refuse unknown destination existence; uninstall requests explicitly select user zero. ([7086768](https://github.com/morluto/rea/commit/7086768a27242a15daa93486aefbaacc3c891e78), [3d028db](https://github.com/morluto/rea/commit/3d028dbc74a465f6f8df1813a931fbb5362e7dc3), [#4074](https://github.com/morluto/rea/pull/4074), [morluto/rea#3942](https://github.com/morluto/rea/issues/3942), [#4663](https://github.com/morluto/rea/pull/4663), [#4665](https://github.com/morluto/rea/pull/4665), [1342127](https://github.com/morluto/rea/commit/1342127e91d4db8b4113a7a4e85be909bc96e1db), [61e6880](https://github.com/morluto/rea/commit/61e688070f48016b6d178cd04c02fcc2f33e19ad), [be0b2d1](https://github.com/morluto/rea/commit/be0b2d138c46bb951fc3c8ee57ef7b0d57fed312)). Thanks @Copilot, @coygeek, @gongfpp, @Tapraj-Pathak, @yankikucuk.
- **Apple metadata:** Preserve exact plist/keyed-archive integers, negative zero, non-finite real identity and dictionary keys. Reject malformed XML before value decoding, retain undecodable dates as unknown and report unsupported OpenStep entries explicitly. ([#4324](https://github.com/morluto/rea/pull/4324), [morluto/rea#4292](https://github.com/morluto/rea/issues/4292), [2cf9c28](https://github.com/morluto/rea/commit/2cf9c28f27261b7e64af2ba7ecb4e23ea7714aee), [#4839](https://github.com/morluto/rea/pull/4839), [morluto/rea#4834](https://github.com/morluto/rea/issues/4834), [f2b7bbb](https://github.com/morluto/rea/commit/f2b7bbb31279a820f4cf32d439570f8e5381d010), [6c98708](https://github.com/morluto/rea/commit/6c98708793c23615de8053b2cd14d9f164b960c5), [78cf610](https://github.com/morluto/rea/commit/78cf6100f33ad2412c0a3d60b83f8a44a43fbbb5), [c7e9e09](https://github.com/morluto/rea/commit/c7e9e096d3e2444e4c251123b8e914e9c0941f4e), [#3793](https://github.com/morluto/rea/pull/3793), [#4105](https://github.com/morluto/rea/pull/4105), [morluto/rea#3892](https://github.com/morluto/rea/issues/3892)). Thanks @coygeek, @kaoru0822-kitauji, @mertkayacs, @pablodiazjorge, @pollychen-lab, @xu91102.
- **Apple application structure:** Derive bundle components and launchd domains from their owning directories, retain unavailable components, and select only exact `Assets.car` names as asset catalogs. Interface Builder keeps unresolved classes and counts hierarchy records beyond the returned object page. ([149a0fc](https://github.com/morluto/rea/commit/149a0fc06c3c67ee45624e6da14dfb0754473758), [5d6029e](https://github.com/morluto/rea/commit/5d6029ea62a65ec34fd739ee40223c77c6fdb63b), [c192c58](https://github.com/morluto/rea/commit/c192c58815ddeaf5cf30159e7082c742c0bc36b7), [#4094](https://github.com/morluto/rea/pull/4094), [morluto/rea#3914](https://github.com/morluto/rea/issues/3914), [#4118](https://github.com/morluto/rea/pull/4118), [morluto/rea#3958](https://github.com/morluto/rea/issues/3958), [#4162](https://github.com/morluto/rea/pull/4162), [morluto/rea#3950](https://github.com/morluto/rea/issues/3950)). Thanks @coygeek, @kaoru0822-kitauji.
- **Native names and layout:** Keep valid Objective-C records beside malformed neighbors, preserve leading U+FEFF and repeated load-command fields, and select exact arm64e.x1 slices. PE resource ownership and NativeAOT reads stay within declared image/header ranges. ([#3833](https://github.com/morluto/rea/pull/3833), [morluto/rea#3336](https://github.com/morluto/rea/issues/3336), [#4549](https://github.com/morluto/rea/pull/4549), [morluto/rea#4540](https://github.com/morluto/rea/issues/4540), [morluto/rea#4541](https://github.com/morluto/rea/issues/4541), [#4730](https://github.com/morluto/rea/pull/4730), [#4048](https://github.com/morluto/rea/pull/4048), [morluto/rea#4008](https://github.com/morluto/rea/issues/4008), [#3500](https://github.com/morluto/rea/pull/3500), [morluto/rea#3389](https://github.com/morluto/rea/issues/3389), [#4463](https://github.com/morluto/rea/pull/4463), [morluto/rea#4373](https://github.com/morluto/rea/issues/4373), [morluto/rea#4455](https://github.com/morluto/rea/issues/4455), [3fc0075](https://github.com/morluto/rea/commit/3fc007534bb2104d662b43cc712281f9d1d6a7d3), [4a076f3](https://github.com/morluto/rea/commit/4a076f368c62bdfb19415accad342e03a38d1005)). Thanks @coygeek, @Iyanuoluwa007, @jayinvers, @kaoru0822-kitauji, @kevinjosh10, @maxazure, @ShuaixinHuang.
- **Native analysis providers:** Keep Ghidra byte matches within examined coverage, retain PE PDB/function-boundary limitations and preserve configured MIPS choices. Attached IDA refuses universal Mach-O rather than silently choosing a slice; request timeout identity and captured output survive adapter handling. ([#4531](https://github.com/morluto/rea/pull/4531), [#2933](https://github.com/morluto/rea/pull/2933), [morluto/rea#2259](https://github.com/morluto/rea/issues/2259), [01a1b3f](https://github.com/morluto/rea/commit/01a1b3fda6a7dacd24a0b10619d41d12153080d4), [50dd9fc](https://github.com/morluto/rea/commit/50dd9fcad1efb47cb404192c30bdf190dcdaca58), [#4765](https://github.com/morluto/rea/pull/4765), [morluto/rea#3871](https://github.com/morluto/rea/issues/3871), [c0cfccb](https://github.com/morluto/rea/commit/c0cfccb45fe43e7eeff18e56c64caa5b121988f9)). Thanks @BeLit-Development-2, @coygeek, @kaoru0822-kitauji, @N0zoM1z0, @ShuaixinHuang.
- **Recorded crashes:** Prevent GDB from discovering an executable named by an untrusted core, reject core mapping paths that escape the isolated sysroot, and preserve pwntools worker ownership across launcher exec. Completed decode facts and cancellation reasons remain available through failed final cleanup. ([5bf4fff](https://github.com/morluto/rea/commit/5bf4fffc1b835130ddb16f96db9ce13b639591db), [b33c0df](https://github.com/morluto/rea/commit/b33c0df02e93fabca00a44f2de625b1a63637ee9), [#4435](https://github.com/morluto/rea/pull/4435), [morluto/rea#4011](https://github.com/morluto/rea/issues/4011)). Thanks @coygeek, @N0zoM1z0.
- **Cutter and Rizin:** Read Cutter’s active document from core configuration and attribute its host version correctly. Keep interrupted Rizin frames and output whitespace before completion markers, with exact command and cleanup failure attribution. ([#4507](https://github.com/morluto/rea/pull/4507), [morluto/rea#4497](https://github.com/morluto/rea/issues/4497), [#4514](https://github.com/morluto/rea/pull/4514), [morluto/rea#4498](https://github.com/morluto/rea/issues/4498), [#4459](https://github.com/morluto/rea/pull/4459), [morluto/rea#4015](https://github.com/morluto/rea/issues/4015), [d5fc7e6](https://github.com/morluto/rea/commit/d5fc7e6ebf9af050081fc4c6e18b9079cfaa5cdc), [199396f](https://github.com/morluto/rea/commit/199396f61c8f61da326d059606a992304a760147)). Thanks @coygeek, @N0zoM1z0, @Xander-AJ.
- **Windows cleanup:** Retain unpublished runtimes after failed removal, handle read-only attributes and sharing conflicts, preserve primary snapshot failures and honor caller environment overrides. ([f78d137](https://github.com/morluto/rea/commit/f78d137b42741a0ed5fb8ac161286f9a50220c4d), [d69acc3](https://github.com/morluto/rea/commit/d69acc3455c33f90a2fc4381db43d38b6af50622), [0679a79](https://github.com/morluto/rea/commit/0679a7972b4f2b02eee6a86a44f50f4c2967d3cb), [5f9ac2c](https://github.com/morluto/rea/commit/5f9ac2c98a6fe11988c41afb737df9b6d5c3a1b3), [1bbf4bd](https://github.com/morluto/rea/commit/1bbf4bd16ca134cdb2187eaf29291cae90d14a57)). Thanks @DeryFerd.
- **Managed metadata:** Compare nominal signatures and declaring identities across builds, preserve empty names without display substitutions, verify ABI-prefixed Mach-O exports exactly, and charge cached metadata once. Metadata errors report artifact file offsets; mixed-mode assemblies retain their actual boundary classification. ([#3059](https://github.com/morluto/rea/pull/3059), [#3079](https://github.com/morluto/rea/pull/3079), [#3853](https://github.com/morluto/rea/pull/3853), [morluto/rea#3352](https://github.com/morluto/rea/issues/3352), [#3974](https://github.com/morluto/rea/pull/3974), [morluto/rea#3961](https://github.com/morluto/rea/issues/3961), [#3491](https://github.com/morluto/rea/pull/3491), [morluto/rea#3449](https://github.com/morluto/rea/issues/3449), [#4418](https://github.com/morluto/rea/pull/4418), [morluto/rea#4372](https://github.com/morluto/rea/issues/4372), [#3516](https://github.com/morluto/rea/pull/3516), [morluto/rea#3366](https://github.com/morluto/rea/issues/3366)). Thanks @8rulerstar, @coygeek, @duckhunt113, @Hahaa909, @JayOfTheKeyboard, @kaoru0822-kitauji, @keeponlight, @N0zoM1z0, @tomyrms.
- **Archive and filesystem identity:** Reject colliding extraction paths before writing, classify ZIP offsets beyond EOF as malformed, normalize ZIP current-directory prefixes once, and preserve symlink and unreadable-path provenance. A directory named `*.map` is no longer mistaken for a source-map file. ([862a0b2](https://github.com/morluto/rea/commit/862a0b2631785492f8dc20c8acd396646c545fd1), [c209dcb](https://github.com/morluto/rea/commit/c209dcb209118069f123e789949eeba1ef78853e), [6b1c36e](https://github.com/morluto/rea/commit/6b1c36e336eeacbb7159b211e687b4b84f99b9f5), [25c2222](https://github.com/morluto/rea/commit/25c22222abe7a681491bdb4d7316250fdcf32973), [d6e4fe4](https://github.com/morluto/rea/commit/d6e4fe4a4dd8914ec3db50d2a7fc3d0802649262), [#4131](https://github.com/morluto/rea/pull/4131), [morluto/rea#3909](https://github.com/morluto/rea/issues/3909)). Thanks @Adkid-Zephyr, @coygeek, @jackie-cqz, @kaoru0822-kitauji, @maxazure, @XY-code1.
- **Evidence and reconstruction:** Authenticate all retained inventory substeps, preserve source-attributed caveats in composed workflows, and require comparable cited Evidence before declaring reconstruction coverage or readiness. Missing Evidence and residual replay uncertainty stay explicit. ([9ac91e0](https://github.com/morluto/rea/commit/9ac91e0c1e13e91cd21341635ad5be89b0e8642d), [c4608a2](https://github.com/morluto/rea/commit/c4608a29620ea3e7bb8259b47357b942fe4e4709), [#4075](https://github.com/morluto/rea/pull/4075), [morluto/rea#3357](https://github.com/morluto/rea/issues/3357), [#3526](https://github.com/morluto/rea/pull/3526), [morluto/rea#3363](https://github.com/morluto/rea/issues/3363), [153017d](https://github.com/morluto/rea/commit/153017d750322188c1a112b2284c1b8065c67320)). Thanks @coygeek, @gensokyo-dev, @kaoru0822-kitauji, @N0zoM1z0, @rudycelekli.
- **Offline data:** Preserve exact UTC packet-capture instants, SQLite column identity and snapshot admission failures, and cooked WASM directive literals. Cleanup cancellation retains completed EVM/WASM observations rather than losing them. ([796faa0](https://github.com/morluto/rea/commit/796faa0e4e6bf745bcdc33acef65af8ccfc3622d), [748ac7f](https://github.com/morluto/rea/commit/748ac7f0df340f0d0569eca2e803a59cd51ffdbd), [bc51d0e](https://github.com/morluto/rea/commit/bc51d0ea5df7dcee1fcc42dfae3ab0c62c9cca5e), [4f87f02](https://github.com/morluto/rea/commit/4f87f02bb19bf77349a683a5bcb2da2d1da427d0), [d694aef](https://github.com/morluto/rea/commit/d694aef227e90164e6654ea9e5ce6a854d099404), [321cacb](https://github.com/morluto/rea/commit/321cacb84a020f447e1110df5c58fa71d99b7bf1)). Thanks @himamaheswarreddy, @mertkayacs, @rollroyces, @viniciusdsandrade.
- **Kuna recovery:** Share concurrent single-function requests and cache repeats, validate warm exports once, stream string inventories beyond the former fixed buffer ceiling, and keep unproven processes in their run slots until close confirms release. Failure messages distinguish final buffer refusals from configurable deadlines. ([#4045](https://github.com/morluto/rea/pull/4045), [#4169](https://github.com/morluto/rea/pull/4169), [#4049](https://github.com/morluto/rea/pull/4049), [#4318](https://github.com/morluto/rea/pull/4318), [#4052](https://github.com/morluto/rea/pull/4052), [#4098](https://github.com/morluto/rea/pull/4098), [#4343](https://github.com/morluto/rea/pull/4343)). Thanks @kaoru0822-kitauji.
- **Flutter snapshots:** Check symbol-table ranges against loadable segments and retain completed inspection facts when cancellation occurs during snapshot cleanup. ([#3859](https://github.com/morluto/rea/pull/3859), [morluto/rea#3346](https://github.com/morluto/rea/issues/3346), [#4766](https://github.com/morluto/rea/pull/4766), [morluto/rea#4717](https://github.com/morluto/rea/issues/4717), [#4762](https://github.com/morluto/rea/pull/4762), [morluto/rea#4640](https://github.com/morluto/rea/issues/4640)). Thanks @coygeek, @kaoru0822-kitauji.
- **Native metadata recovery:** Keep unknown Objective-C category classes, empty protocol names, undecoded Swift aliases and unfinished property types explicit instead of discarding neighboring valid records. Preserve unnamed segment identity and exclude a dylib’s own install name from dependencies. ([#4093](https://github.com/morluto/rea/pull/4093), [morluto/rea#4012](https://github.com/morluto/rea/issues/4012), [#4078](https://github.com/morluto/rea/pull/4078), [morluto/rea#3873](https://github.com/morluto/rea/issues/3873), [#4085](https://github.com/morluto/rea/pull/4085), [morluto/rea#3934](https://github.com/morluto/rea/issues/3934), [#3825](https://github.com/morluto/rea/pull/3825), [morluto/rea#3345](https://github.com/morluto/rea/issues/3345), [#4051](https://github.com/morluto/rea/pull/4051), [morluto/rea#3875](https://github.com/morluto/rea/issues/3875), [#4050](https://github.com/morluto/rea/pull/4050), [morluto/rea#3872](https://github.com/morluto/rea/issues/3872)). Thanks @coygeek, @kaoru0822-kitauji.
- **Document selection:** Preserve explicitly selected plist paths and bundle defaults, and only use document fallback after confirming its applicability. Finalization keeps completed selection facts and cancellation outcomes. ([e6c4877](https://github.com/morluto/rea/commit/e6c487799bb64f559e66b69be1863e6ec5187548), [#3774](https://github.com/morluto/rea/pull/3774), [9df559b](https://github.com/morluto/rea/commit/9df559b7e712298a761ff26e9caae8d659b7e54b)). Thanks @kaoru0822-kitauji.
- **Capture and comparison accuracy:** Admit schema-valid HAR numeric values while retaining exact-publication limits, preserve empty URL path segments, and keep unrelated process traces and unnamed reference endpoints distinct or unknown. ([#3159](https://github.com/morluto/rea/pull/3159), [morluto/rea#3145](https://github.com/morluto/rea/issues/3145), [#3792](https://github.com/morluto/rea/pull/3792), [morluto/rea#3307](https://github.com/morluto/rea/issues/3307), [#4178](https://github.com/morluto/rea/pull/4178), [morluto/rea#3931](https://github.com/morluto/rea/issues/3931), [#3809](https://github.com/morluto/rea/pull/3809), [morluto/rea#3388](https://github.com/morluto/rea/issues/3388)). Thanks @coygeek, @gensokyo-dev, @kaoru0822-kitauji, @N0zoM1z0, @pablodiazjorge, @xu91102.
- **Evidence publication:** Recheck staged output identity after its final write, classify missing review Evidence and duplicate ledger entries as caller input, and preserve the selected freshness requirement in comparison parameters. ([#4764](https://github.com/morluto/rea/pull/4764), [morluto/rea#4613](https://github.com/morluto/rea/issues/4613), [#4452](https://github.com/morluto/rea/pull/4452), [morluto/rea#4444](https://github.com/morluto/rea/issues/4444), [#4065](https://github.com/morluto/rea/pull/4065), [morluto/rea#4000](https://github.com/morluto/rea/issues/4000)). Thanks @amyanger, @coygeek, @kaoru0822-kitauji, @Rinkia, @Swatantra-66.
### ⚠ BREAKING CHANGES
- **Saved Evidence:** Evidence and analysis snapshots now require explicit nullable `relationship_coverage` in their identity commitments. Artifact inventories require scoped coverage and explicit `integrity_contradictions`; obsolete `coverage_status` and prose-based coverage inference are removed. Regenerate old Evidence/snapshots and inventory observations with this release: saved files are not migrated and omitted facts are not filled with guessed defaults. ([db8128d](https://github.com/morluto/rea/commit/db8128dc0d1c4f731f3d19877fa00d25cf17f657), [0405c9d](https://github.com/morluto/rea/commit/0405c9d361de6d904dfa16cc0b6d8350407f2c9b), [a9588be](https://github.com/morluto/rea/commit/a9588be4ebdd20eeda975cb63ffcdb1cd868b340)).
- **Browser capture results:** Consumers must retain resolved capture limits, event families and omission facts. Set desired budgets through the supported capture options; omitted caller inputs still select defaults, but stored observations must report the resolved values. Admitted content reads queue behind bounded concurrency. ([57a23f0](https://github.com/morluto/rea/commit/57a23f0a2da0fdc3835e203718ded8acea5a34e6)).
- **Android version codes:** `inspect_adb_package.version_code` is a decimal string rather than a JSON number. Read it as a string, or use `BigInt` for numeric comparison. ([#3485](https://github.com/morluto/rea/pull/3485), [morluto/rea#3368](https://github.com/morluto/rea/issues/3368), [7666a48](https://github.com/morluto/rea/commit/7666a482b69dc7cabdbac3bbaf89a43e32300192)). Thanks @coygeek, @kaoru0822-kitauji.
- **Native UI failures:** Failed or cancelled native UI scenarios now return typed errors with the collected report in `partial_observation`. Handle the error path and read that report instead of expecting a successful result containing failed steps. ([d909919](https://github.com/morluto/rea/commit/d9099190ab3a819bd3a9fd62a8714cef398016d5)).
- **Provider status:** `binary_session` candidate availability can be unknown during owner finalization. Preserve that state rather than treating it as unavailable; internal consumers must await the asynchronous session status API. ([150e996](https://github.com/morluto/rea/commit/150e996a86f2bec4982ba87f37bf502a8d5e8214)).
- **Swift symbol locations:** `swift_symbols` entries may have a null `address` when the provider supplies no location. Handle unknown addresses; optional `evidence.reported_address` retains the original provider value. ([8116dc0](https://github.com/morluto/rea/commit/8116dc06512b4609d8db63cebc9f2ff8cb964151)).
- **Linux Hopper admission:** Older builds previously admitted by a fixed allowlist need retained publisher-signed package evidence or an approved update to the current public demo. Update explicit `HOPPER_LAUNCHER_PATH` selections when migrating; the live feed offers only its current release. The official RPM metadata mismatch remains unresolved and is refused. ([#3590](https://github.com/morluto/rea/pull/3590), [morluto/rea#3577](https://github.com/morluto/rea/issues/3577), [70cd4cb](https://github.com/morluto/rea/commit/70cd4cbfb849b61f05d97d2690372a9782831426)). Thanks @gensokyo-dev, @kaoru0822-kitauji, @N0zoM1z0.
Full contribution record: [571 commits and 236 merged PRs](https://github.com/morluto/rea/blob/rea-agents-6.6.0/docs/releases/6.6.0.contributions.json).
## [6.5.0](https://github.com/morluto/rea/compare/rea-agents-6.4.0...rea-agents-6.5.0) (2026-10-13)
Provider prerequisites and recorded verification scope are listed in [the testing guide](docs/testing.md).
+3
View File
@@ -21,6 +21,9 @@ For analysis workflows and agent interfaces, follow the
[performance priorities](AGENTS.md#performance). Explain the selected output
scope, reuse/cache decisions, and relevant before/after measurements when these
costs change.
Use the [analysis optimization skill](.agents/skills/rea-analysis-optimization/SKILL.md)
to locate provider and shared-workflow bottlenecks, preserve Evidence, and
compare equivalent real tasks before claiming an improvement.
When adding or changing an MCP tool, follow the [tool design guide](docs/tool-design.md) and preserve the canonical contracts and generated catalog.
+10 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
**English** · [简体中文](docs/readme/README_zh.md) · [繁體中文](docs/readme/README_zh-TW.md) · [日本語](docs/readme/README_ja.md) · [한국어](docs/readme/README_ko.md) · [Türkçe](docs/readme/README_tr.md) · [Русский](docs/readme/README_ru.md) · [Tiếng Việt](docs/readme/README_vi.md) · [ไทย](docs/readme/README_th.md) · [Deutsch](docs/readme/README_de.md) · [Español](docs/readme/README_es.md) · [Français](docs/readme/README_fr.md) · [Українська](docs/readme/README_uk.md) · [Polski](docs/readme/README_pl.md) · [Português (Brasil)](docs/readme/README_pt-BR.md) · [العربية](docs/readme/README_ar.md) · [فارسی](docs/readme/README_fa.md) · [Bahasa Indonesia](docs/readme/README_id.md) · [Italiano](docs/readme/README_it.md)
· [Ελληνικά](docs/readme/README_el.md)
· [Ελληνικά](docs/readme/README_el.md) · [മലയാളം](docs/readme/README_ml.md)
# REA: Reverse Engineer Anything
@@ -132,6 +132,14 @@ For a CLI installed with `npm install --global` or `bun add --global`:
rea update
```
`rea --update` is an alias for this verified workflow and accepts the same global
output flags. Use `rea update` when combining it with other command controls.
Updater commands retain at most 4 MiB of stdout and stderr combined. Oversized
metadata replies are rejected after the command exits; verbose installs continue
with bounded diagnostics. Human progress output respects terminal backpressure
and waits for submitted writes to finish.
Bun global updates require Bun 1.2.17 or later. With an older Bun release,
upgrade Bun manually before running `rea update`.
@@ -345,7 +353,7 @@ Report vulnerabilities through [SECURITY.md](SECURITY.md).
## Star history
🎉 **250,000 GitHub stars — thank you!**
🎉 **300,000 GitHub stars — thank you!**
Thanks to everyone using REA, reporting bugs, requesting features, testing builds, and contributing fixes.
+167 -23
View File
@@ -15,11 +15,14 @@ import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
import kotlin.Unit;
import kotlin.ResultKt;
import kotlin.coroutines.Continuation;
import kotlin.coroutines.CoroutineContext;
import kotlin.coroutines.EmptyCoroutineContext;
import kotlin.coroutines.intrinsics.IntrinsicsKt;
import kotlinx.coroutines.BuildersKt;
import kotlinx.io.CoreKt;
import kotlinx.io.JvmCoreKt;
import kotlinx.serialization.json.JsonObject;
/** REA-owned metadata boundary for the unmodified jadx-headless-mcp 0.7.1 JAR. */
class ReaJadxBridge {
@@ -28,7 +31,8 @@ class ReaJadxBridge {
private JadxSession indexedSession;
private Map<String, List<ClassSnapshot>> classes = Map.of();
private record MethodSnapshot(String name, String shortId, Map<String, Object> summary) {}
private record MethodSnapshot(String name, String rawName, String shortId, List<String> bodyAbsenceMarkers,
Map<String, Object> summary) {}
private record ClassSnapshot(JavaClass node, List<MethodSnapshot> methods, Map<String, Object> summary) {}
private static final class InputFailure extends IllegalArgumentException {
@@ -44,15 +48,93 @@ class ReaJadxBridge {
private static CallToolResult json(Object value) { return text(JSON.toJson(value), false); }
/** Translate the typed load outcome before upstream drops its exception identity. */
static Object loadResult(Object result) {
try { ResultKt.throwOnFailure(result); }
catch (Throwable failure) {
if (failure instanceof java.util.concurrent.CancellationException
|| failure instanceof Error && !(failure instanceof OutOfMemoryError)) return result;
if (failure instanceof OutOfMemoryError) {
String reason = failure.getMessage();
boolean heap = "Java heap space".equals(reason) || "GC overhead limit exceeded".equals(reason);
Map<String, Object> reply = new LinkedHashMap<>();
reply.put("kind", "jvm-memory-failure");
reply.put("memory_area", heap ? "heap" : "unknown");
reply.put("exception_type", "java.lang.OutOfMemoryError");
if (reason != null) reply.put("message", reason);
reply.put("max_heap_bytes", Runtime.getRuntime().maxMemory());
return text(JSON.toJson(reply), true);
}
return text("load failed: " + failure.getMessage(), true);
}
SessionHolder.LoadResult loaded = (SessionHolder.LoadResult) result;
Map<String, Object> reply = new LinkedHashMap<>();
reply.put("state", "LOADED");
reply.put("apk_path", loaded.getApkPath());
reply.put("class_count", loaded.getClassCount());
reply.put("raw_class_count", loaded.getRawClassCount());
reply.put("resource_count", loaded.getResourceCount());
reply.put("load_duration_ms", loaded.getLoadDurationMs());
reply.put("decompile_timeout_ms", loaded.getDecompileTimeoutMs());
reply.put("threads", loaded.getThreads());
reply.put("code_cache_size", loaded.getCodeCacheSize());
reply.put("resources", loaded.getResourceMode());
if (!loaded.getIncludePackages().isEmpty()) reply.put("include_packages", loaded.getIncludePackages());
if (!loaded.getExcludePackages().isEmpty()) reply.put("exclude_packages", loaded.getExcludePackages());
return json(reply);
}
private Object load(kotlinx.serialization.json.JsonObject arguments, SessionConfig defaults,
Continuation<? super CallToolResult> continuation) {
JsonObject args = JsonParser.parseString(arguments.toString()).getAsJsonObject();
String path = string(args, "path");
SessionConfig options = defaults.withLoadOverrides(
optionalString(args, "include_packages"), optionalString(args, "exclude_packages"),
args.has("threads") ? integer(args, "threads", 1) : null,
args.has("code_cache_size") ? integer(args, "code_cache_size", 64) : null,
optionalString(args, "resources"));
return loadCall(next -> holder.load(path, options, next), continuation);
}
static Object loadCall(kotlin.jvm.functions.Function1<Continuation<SessionHolder.LoadResult>, Object> load,
Continuation<? super CallToolResult> continuation) {
var forwarded = new java.util.concurrent.atomic.AtomicBoolean();
Continuation<SessionHolder.LoadResult> resumed = new Continuation<>() {
public CoroutineContext getContext() { return continuation.getContext(); }
public void resumeWith(Object result) {
if (!forwarded.compareAndSet(false, true)) throw new IllegalStateException("Load resumed twice");
Object translated;
try { translated = loadResult(result); }
catch (Throwable failure) { translated = ResultKt.createFailure(failure); }
// Keep the consumer outside the translation catch, including inline completion.
continuation.resumeWith(translated);
}
};
Object result;
try { result = load.invoke(resumed); }
catch (Throwable failure) {
if (forwarded.get()) ResultKt.throwOnFailure(ResultKt.createFailure(failure));
result = ResultKt.createFailure(failure);
}
if (result == IntrinsicsKt.getCOROUTINE_SUSPENDED()) return result;
Object translated = loadResult(result);
ResultKt.throwOnFailure(translated);
return translated;
}
private static String optionalString(JsonObject args, String name) {
return args.has(name) ? string(args, name) : null;
}
private static String string(JsonObject args, String name) {
JsonElement value = JsonParser.parseString(args.toString()).getAsJsonObject().get(name);
JsonElement value = args.get(name);
if (value == null || !value.isJsonPrimitive() || !value.getAsJsonPrimitive().isString())
throw new IllegalArgumentException(name + " must be a string");
return value.getAsString();
}
private static int integer(JsonObject args, String name, int fallback) {
JsonElement value = JsonParser.parseString(args.toString()).getAsJsonObject().get(name);
JsonElement value = args.get(name);
if (value == null) return fallback;
if (!value.isJsonPrimitive() || !value.getAsJsonPrimitive().isNumber())
throw new IllegalArgumentException(name + " must be a nonnegative integer");
@@ -90,10 +172,13 @@ class ReaJadxBridge {
if (!shortId.startsWith(rawName + "("))
throw new IllegalStateException("JADX method ID has no raw-name prefix: " + shortId);
String descriptor = shortId.substring(rawName.length());
List<String> bodyAbsenceMarkers = new ArrayList<>();
if (method.getAccessFlags().isNative()) bodyAbsenceMarkers.add("native_method");
if (method.getAccessFlags().isAbstract()) bodyAbsenceMarkers.add("abstract_method");
String signature = name + "(" + method.getArgTypes().stream()
.map(Object::toString).collect(Collectors.joining(", "))
+ "): " + method.getReturnType().toString();
return new MethodSnapshot(name, shortId, Map.of(
return new MethodSnapshot(name, rawName, shortId, List.copyOf(bodyAbsenceMarkers), Map.of(
"name", name, "signature", signature,
"dex_descriptor", descriptor,
"is_constructor", method.isConstructor(), "def_pos", 0));
@@ -140,16 +225,52 @@ class ReaJadxBridge {
private JadxSession.SmartCode methodBody(JadxSession current, ClassSnapshot cls,
MethodSnapshot selected, JavaMethod method) {
if (!selected.shortId().equals("<clinit>()V"))
return current.getMethodBodySmart(method, current.getMaxSourceBytes(), true);
// Access flags belong to the parsed member, before code generation rewrites it.
// These methods have no Java body; source slicing can include unrelated members.
if (!selected.bodyAbsenceMarkers().isEmpty())
return new JadxSession.SmartCode("", "java", false, selected.bodyAbsenceMarkers());
// Java code generation can fold all or part of <clinit> into field declarations.
// Its method text cannot establish coverage, even when a static block remains.
JadxSession.MethodSmali smali = current.getMethodSmali(cls.node(), method.getName());
if (!smali.getFound() || smali.getBlocks().size() != 1)
throw new IllegalArgumentException("Static initializer disassembly requires one exact method: " + selected.shortId());
if (selected.shortId().equals("<clinit>()V"))
return exactMethodSmali(current, cls, selected, List.of("static_initializer_disassembly"));
String java = current.getMethodSource(method);
if (java.startsWith("// ERROR:")) throw new IllegalArgumentException(java);
// The upstream placeholder means only that Java code was omitted, including
// methods merged into synthetic bridges. Access flags established absence above.
if (java.isEmpty() || java.equals(
"// method exists but has no decompiled body (native/abstract)"))
return exactMethodSmali(current, cls, selected, List.of("java_body_unavailable"));
List<String> markers = DecompileFailureKt.detectDecompileFailure(java);
if (markers.stream().anyMatch(DecompileFailureKt.getSTRONG_FAILURE_MARKERS()::contains))
return exactMethodSmali(current, cls, selected, markers);
return new JadxSession.SmartCode(
TextUtilKt.truncateToBytes(smali.getBlocks().get(0), current.getMaxSourceBytes()),
"smali", true, List.of("static_initializer_disassembly"));
TextUtilKt.truncateToBytes(java, current.getMaxSourceBytes()), "java", false, markers);
}
private JadxSession.SmartCode exactMethodSmali(JadxSession current, ClassSnapshot cls,
MethodSnapshot selected, List<String> markers) {
JadxSession.MethodSmali smali = current.getMethodSmali(cls.node(), selected.rawName());
String block = selectMethodSmali(smali, selected.shortId());
return new JadxSession.SmartCode(
TextUtilKt.truncateToBytes(block, current.getMaxSourceBytes()), "smali", true, markers);
}
private static String selectMethodSmali(JadxSession.MethodSmali smali, String shortId) {
if (!smali.getFound())
throw new IllegalArgumentException("JADX could not disassemble " + shortId
+ (smali.getNote() == null ? "" : ": " + smali.getNote()));
// Upstream returns every same-name overload, and can return an unterminated
// trailing block. Match the full original identity before applying output limits.
List<String> matches = smali.getBlocks().stream().filter(block -> {
String header = block.lines().findFirst().orElse("").strip();
return header.startsWith(".method ") && header.endsWith(" " + shortId);
}).toList();
if (matches.size() != 1)
throw new IllegalArgumentException("Disassembly could not establish one selected method: " + shortId);
String block = matches.get(0);
if (!block.stripTrailing().endsWith("\n.end method"))
throw new IllegalArgumentException("Selected method disassembly is incomplete: " + shortId);
return block;
}
// The local transport admits 8 MiB frames. Reserve 1 KiB for the fixed
@@ -203,7 +324,8 @@ class ReaJadxBridge {
return new DirectInvokes(sites, exhaustive[0]);
}
private CallToolResult handle(String name, JsonObject args) {
private CallToolResult handle(String name, kotlinx.serialization.json.JsonObject input) {
JsonObject args = JsonParser.parseString(input.toString()).getAsJsonObject();
if (name.equals("rea_jvm_status")) return json(Map.of(
"engine_reported_version", BuildInfo.VERSION,
"max_heap_bytes", Runtime.getRuntime().maxMemory(),
@@ -213,12 +335,25 @@ class ReaJadxBridge {
JadxSession current = session();
if (name.equals("list_classes")) {
List<String> names = current.getClassFqns();
String query = args.has("query") ? string(args, "query") : "";
int offset = integer(args, "offset", 0);
int limit = integer(args, "limit", 200);
int start = Math.min(offset, names.size());
int end = (int) Math.min((long) start + limit, names.size());
return json(Map.of("total", names.size(), "offset", offset, "limit", limit,
"items", names.subList(start, end)));
int end = start;
List<String> items;
if (query.isEmpty()) {
end = (int) Math.min((long) start + limit, names.size());
items = names.subList(start, end);
} else {
items = new ArrayList<>();
while (end < names.size() && items.size() < limit) {
String nameInInventory = names.get(end++);
if (nameInInventory.contains(query)) items.add(nameInInventory);
}
}
return json(Map.of("total_classes", names.size(), "next_offset", end,
"scope", "top_level", "offset", offset, "limit", limit,
"items", items));
}
ClassSnapshot cls = selected(args);
if (name.equals("get_class_summary")) return json(cls.summary());
@@ -230,6 +365,7 @@ class ReaJadxBridge {
JadxSession.SmartCode code = methodBody(current, cls, overloads.get(index), method);
return json(Map.of("class_name", cls.summary().get("full_name"),
"method_name", overloads.get(index).name(), "full_name", method.getFullName(),
"dex_descriptor", overloads.get(index).summary().get("dex_descriptor"),
"overload_index", index, "overload_count", overloads.size(),
"mode", code.getKind(), "fell_back", code.getFellBack(),
"markers", code.getMarkers(), "body", code.getText()));
@@ -241,7 +377,7 @@ class ReaJadxBridge {
if (name.equals("get_xrefs_to_class")) uses = cls.node().getUseIn();
else {
List<MethodSnapshot> overloads = methods(cls, args);
if (overloads.size() != 1 && !args.containsKey("overload_index"))
if (overloads.size() != 1 && !args.has("overload_index"))
throw new InputFailure("overload_index", "Select one of " + overloads.size() + " method overloads");
overloadIndex = integer(args, "overload_index", 0);
if (overloadIndex >= overloads.size())
@@ -287,11 +423,12 @@ class ReaJadxBridge {
public static void main(String[] args) throws Exception {
PrintStream protocolOut = System.out;
System.setOut(System.err);
SessionHolder holder = new SessionHolder(new SessionConfig(
1048576, 0, 90000L, 1, List.of(), List.of(), 64, ResourceMode.FULL));
SessionConfig defaults = new SessionConfig(
1048576, 0, 90000L, 1, List.of(), List.of(), 64, ResourceMode.FULL);
SessionHolder holder = new SessionHolder(defaults);
ServerCapabilities capabilities = new ServerCapabilities(
new ServerCapabilities.Tools(null), null, null, null, null, null, null, Map.of());
Server server = new Server(new Implementation("rea-jadx-bridge", "1", null, null, null),
Server server = new Server(new Implementation("rea-jadx-bridge", "4", null, null, null),
new ServerOptions(capabilities), "", ignored -> Unit.INSTANCE);
SessionToolsKt.registerSessionTools(server, holder);
ManifestToolsKt.registerManifestTools(server, holder);
@@ -299,13 +436,17 @@ class ReaJadxBridge {
MethodToolsKt.registerMethodTools(server, holder);
XrefToolsKt.registerXrefTools(server, holder);
ReaJadxBridge bridge = new ReaJadxBridge(holder);
Tool loadTool = server.getTools().get("load_apk").getTool();
server.removeTool("load_apk");
server.addTool(loadTool, (connection, request, continuation) ->
bridge.load(request.getArguments(), defaults, continuation));
Set<String> consumed = Set.of("load_apk", "get_app_info", "get_android_manifest",
"list_classes", "get_class_summary", "get_method_body",
"get_xrefs_to_class", "get_xrefs_to_method");
for (String name : new ArrayList<>(server.getTools().keySet()))
if (!consumed.contains(name)) server.removeTool(name);
Tool diagnostics = new Tool("rea_jvm_status",
new ToolSchema(null, new JsonObject(Map.of()), List.of(), null),
new ToolSchema(null, new kotlinx.serialization.json.JsonObject(Map.of()), List.of(), null),
"Observed worker JVM and metadata scope", null, null, null, null, null, null);
server.addTool(diagnostics, (connection, request, continuation) -> bridge.handle("rea_jvm_status", request.getArguments()));
for (String name : List.of("list_classes", "get_class_summary",
@@ -320,13 +461,16 @@ class ReaJadxBridge {
};
Map<String, kotlinx.serialization.json.JsonElement> properties = new LinkedHashMap<>();
for (String field : fields) properties.put(field, tool.getInputSchema().getProperties().get(field));
if (name.equals("list_classes")) properties.put("query",
new kotlinx.serialization.json.JsonObject(Map.of(
"type", kotlinx.serialization.json.JsonElementKt.JsonPrimitive("string"))));
String description = switch (name) {
case "list_classes" -> "Indexed pages of all top-level class names, with offset and limit.";
case "list_classes" -> "Pages of case-sensitive matching top-level class names; offset and next_offset are inventory positions, limit bounds matching rows. Omitted or empty query inventories all names.";
case "get_class_summary" -> "Parsed member metadata for one exact class name; no source generation.";
case "get_method_body" -> "Source for an exact class and stable same-name overload index; static initializers use smali to preserve folded field initialization.";
default -> "Complete static using-node inventory for an exact class or selected method overload; one node may contain multiple invoke instructions, and source lines are unresolved.";
};
Tool narrowed = new Tool(name, new ToolSchema(null, new JsonObject(properties),
Tool narrowed = new Tool(name, new ToolSchema(null, new kotlinx.serialization.json.JsonObject(properties),
tool.getInputSchema().getRequired(), null), description, null, null, null, null, null, null);
server.addTool(narrowed, (connection, request, continuation) -> {
try { return bridge.handle(name, request.getArguments()); }
+13 -5
View File
@@ -783,14 +783,22 @@ def _within_response_limit(value: Any) -> bool:
current = pending.pop()
if isinstance(current, str):
size += 2
if len(current) > MAX_FRAME_BYTES - size:
return False
if current.isascii():
size += len(current)
size += current.count('"') + current.count("\\")
size += sum(5 for character in current if ord(character) < 0x20)
else:
size += 4 * len(current)
size += current.count('"') + current.count("\\")
size += sum(5 for character in current if ord(character) < 0x20)
# Count UTF-8 bytes without allocating a copy of the whole output.
try:
size += sum(
len(current[start:start + 65536].encode("utf-8"))
for start in range(0, len(current), 65536)
)
except UnicodeEncodeError:
# Unencodable output also needs the signed omission fallback.
return False
size += current.count('"') + current.count("\\")
size += sum(5 for character in current if ord(character) < 0x20)
elif isinstance(current, dict):
identity = id(current)
if identity in seen:
+18
View File
@@ -140,6 +140,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
"params"
);
private static final String[] CAPABILITIES = {
"read_binary_inventory_summary",
"annotate_native_function",
"set_address_name",
"set_addresses_names",
@@ -427,6 +428,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
);
}
return switch (request.method) {
case "read_binary_inventory_summary" -> binaryInventorySummary(request.params);
case "inspect_native_load_image" -> inspectNativeLoadImage(request.params);
case "read_bytes" -> readMemoryBytes(request.params);
case "search_bytes" -> searchMemoryBytes(request.params, descriptor);
@@ -1974,6 +1976,18 @@ public final class ReaGhidraBridge extends HeadlessScript {
return result;
}
private JsonObject binaryInventorySummary(JsonObject params) throws Exception {
requireKeys(params, Set.of());
JsonObject result = new JsonObject();
result.addProperty("document", currentProgram.getName());
result.add("segments", segments());
// Count the owning inventories, preserving their exact discovery rules
// without serializing function identities or string values for counts.
result.addProperty("procedure_count", procedures().size());
result.addProperty("string_count", strings().size());
return result;
}
private JsonArray listNames(JsonObject params) throws Exception {
requireKeys(params, Set.of("document", "address"));
requireDocument(params);
@@ -2000,6 +2014,10 @@ public final class ReaGhidraBridge extends HeadlessScript {
private JsonArray listSegments(JsonObject params) {
requireKeys(params, Set.of("document"));
requireDocument(params);
return segments();
}
private JsonArray segments() {
JsonArray result = new JsonArray();
String imageBase = canonicalAddress(currentProgram.getImageBase());
MemoryBlock[] blocks = currentProgram.getMemory().getBlocks();
+10 -20
View File
@@ -431,10 +431,6 @@ def _procedure_map(document):
return result
def _strings(document):
return {address: record["value"] for address, record in _string_inventory(document).items()}
def _string_record(segment, address, display):
"""Read one native typed object, retaining its shortened display separately."""
length = segment.getObjectLength(address)
@@ -521,7 +517,7 @@ def _search_inventory(document, kind):
if kind == "procedure":
values = _procedure_map(document)
elif kind == "string":
values = _strings(document)
values = _string_inventory(document)
elif kind == "name":
values = {_hex(address): value for address, value in _name_map(document).items()}
else:
@@ -548,23 +544,17 @@ def _search_results(document, kind, params):
else:
raise CapabilityUnavailableError("Regex matching requires REA's supervised worker; the native bridge only executes literal searches")
selected = []
for item in _search_inventory(document, kind):
if not matches(item[1]):
continue
selected.append(item)
if not selected:
return []
if kind == "string":
records = _string_inventory(document)
return [{"address": address, **records[address]} for address, _ in selected]
return [
{
"address": address,
"value": value,
}
for address, value in selected
return [
{"address": address, **record}
for address, record in _search_inventory(document, kind)
if matches(record["value"])
]
return [
{"address": address, "value": value}
for address, value in _search_inventory(document, kind)
if matches(value)
]
def _unavailable(reason):
+15 -2
View File
@@ -20,8 +20,9 @@ def inspect(request):
root = Path(os.environ["REA_CORE_CONTEXT_REQUEST"]).parent
empty = root / "no-libraries"
empty.mkdir(mode=0o700)
for command in ["set auto-load off", "set debuginfod enabled off", "set auto-solib-add off", "set history save off", "set pagination off",
"set sysroot ./no-libraries", "set solib-search-path ./no-libraries"]:
for command in ["set auto-load off", "set debuginfod enabled off", "set auto-solib-add off", "set history save off", "set pagination off", "set target-file-system-kind unix",
"set sysroot ./no-libraries", "set solib-search-path ./no-libraries",
"set debug-file-directory ./no-libraries"]:
gdb.execute(command, to_string=True)
entry = request["gdbinit"]
try:
@@ -38,7 +39,19 @@ def inspect(request):
# relative filename in the owned cwd so no selected path becomes GDB syntax.
if Path(request["snapshot_path"]) != root.parent / "object.snapshot":
raise ContextFailure("decoder", "Core context request did not select the shared owned snapshot.")
# GDB 16 searches AT_EXECFN/NT_FILE outside sysroot when no executable BFD
# is selected. Occupy that slot with the already running, owned debugger;
# exec-file loads no symbols and never runs it. Clear it before any query.
# /proc/self/exe identifies this process, not the core's historical PID.
architecture = {"x86_64-little-endian": "i386:x86-64", "aarch64-little-endian": "aarch64"}.get(request["architecture"])
if architecture is None:
raise ContextFailure("unsupported", "Unsupported recorded core architecture.")
gdb.execute("exec-file /proc/self/exe", to_string=True)
# The placeholder must not select the core's architecture. Reuse the
# decoder's validated profile, including for cross-architecture GDB builds.
gdb.execute("set architecture " + architecture, to_string=True)
gdb.execute("core-file ../object.snapshot", to_string=True)
gdb.execute("file", to_string=True)
inferior = gdb.selected_inferior()
if inferior.connection is None or inferior.connection.type != "core" or gdb.current_progspace().filename is not None:
raise ContextFailure("decoder", "Requested core-only boundary was not established; no mapping query performed.")
+117
View File
@@ -0,0 +1,117 @@
"""Producer regressions with controlled GDB state, not native debugger qualification."""
import json
import os
from pathlib import Path
import runpy
import sys
import tempfile
import types
import unittest
from unittest.mock import patch
BRIDGE = Path(__file__).with_name("core_context.py")
class CoreContextTests(unittest.TestCase):
def inspect(self, failure=None, connection="core", symbols=None, architecture="x86_64-little-endian"):
commands, reads, queries = [], [], []
state = {"executable": None, "symbols": None, "connection": None}
gdb = types.ModuleType("gdb")
gdb.VERSION = "16.3"
gdb.selected_inferior = lambda: types.SimpleNamespace(
connection=state["connection"], pid=123)
gdb.current_progspace = lambda: types.SimpleNamespace(filename=state["symbols"])
def execute(command, to_string):
self.assertTrue(to_string)
commands.append(command)
if command == failure:
raise RuntimeError("controlled command failure")
if command == "exec-file /proc/self/exe":
state["executable"] = "/proc/self/exe"
elif command == "core-file ../object.snapshot":
if state["executable"] is None:
reads.append("recorded-executable")
state["executable"] = state["symbols"] = "recorded-executable"
state["connection"] = types.SimpleNamespace(type=connection)
elif command == "file":
state["executable"] = None
state["symbols"] = symbols
gdb.execute = execute
pwndbg = types.ModuleType("pwndbg")
pwndbg.aglib = types.ModuleType("pwndbg.aglib")
pwndbg.aglib.vmmap = types.ModuleType("pwndbg.aglib.vmmap")
pwndbg.lib = types.ModuleType("pwndbg.lib")
pwndbg.lib.version = types.ModuleType("pwndbg.lib.version")
pwndbg.lib.version.__version__ = "2026.09.15"
def maps():
self.assertIsNone(state["executable"], "Helper executable must be discarded before mapping queries")
queries.append("maps")
return [types.SimpleNamespace(start=4096, end=8192, offset=0, flags=5, objfile="recorded-executable")]
pwndbg.aglib.vmmap.get = maps
modules = {module.__name__: module for module in [
gdb, pwndbg, pwndbg.aglib, pwndbg.aglib.vmmap, pwndbg.lib, pwndbg.lib.version]}
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
stage = root / "debugger"
stage.mkdir()
plugin = root / "gdbinit.py"
plugin.write_text("")
request = stage / "request.json"
reply = stage / "reply.json"
request.write_text(json.dumps({"gdbinit": str(plugin), "architecture": architecture, "snapshot_path": str(root / "object.snapshot"), "reply_path": str(reply)}))
with patch.dict(sys.modules, modules), patch.dict(os.environ, REA_CORE_CONTEXT_REQUEST=str(request)):
runpy.run_path(str(BRIDGE))
result = json.loads(reply.read_text())
return result, commands, reads, queries
def test_core_load_never_opens_recorded_executable_and_queries_only_after_discard(self):
result, commands, reads, queries = self.inspect()
self.assertTrue(result["ok"], result)
self.assertEqual(reads, [])
self.assertEqual(queries, ["maps"])
self.assertEqual(commands[-4:], ["exec-file /proc/self/exe", "set architecture i386:x86-64", "core-file ../object.snapshot", "file"])
self.assertLess(commands.index("set debug-file-directory ./no-libraries"), commands.index("core-file ../object.snapshot"))
self.assertIsNone(result["value"]["executable"])
self.assertEqual(result["value"]["maps"][0]["current_file_identity"], "unknown")
def test_failed_guard_core_load_or_discard_never_queries_mappings(self):
for command in ["exec-file /proc/self/exe", "set architecture i386:x86-64", "core-file ../object.snapshot", "file"]:
with self.subTest(command=command):
result, commands, reads, queries = self.inspect(failure=command)
self.assertFalse(result["ok"])
self.assertEqual(reads, [])
self.assertEqual(queries, [])
if command in ["exec-file /proc/self/exe", "set architecture i386:x86-64"]:
self.assertNotIn("core-file ../object.snapshot", commands)
def test_cross_architecture_uses_the_validated_decoder_profile(self):
result, commands, reads, queries = self.inspect(architecture="aarch64-little-endian")
self.assertTrue(result["ok"], result)
self.assertEqual(reads, [])
self.assertEqual(queries, ["maps"])
self.assertLess(commands.index("set architecture aarch64"), commands.index("core-file ../object.snapshot"))
def test_unknown_architecture_refuses_before_loading_core(self):
result, commands, reads, queries = self.inspect(architecture="unknown")
self.assertFalse(result["ok"])
self.assertEqual(result["reason"], "unsupported")
self.assertNotIn("core-file ../object.snapshot", commands)
self.assertEqual(reads, [])
self.assertEqual(queries, [])
def test_wrong_connection_or_remaining_symbols_never_queries_mappings(self):
for options in [{"connection": "native"}, {"symbols": "unexpected-symbols"}]:
with self.subTest(options=options):
result, _, reads, queries = self.inspect(**options)
self.assertFalse(result["ok"])
self.assertEqual(reads, [])
self.assertEqual(queries, [])
if __name__ == "__main__":
unittest.main()
+15 -1
View File
@@ -98,11 +98,25 @@ existing analysis families directly, for example
| `collect_adb_bugreport` | read | Device-generated bugreport zip, digested |
| `push_adb_file` | **mutates device** | One local file to a device path (e.g. pushing tools into `/data/local/tmp`); checks for existing targets and refuses them without explicit overwrite (the check is not atomic); compares digests when device `sha256sum` is available |
| `install_adb_package` | **mutates device** | Local APK via `adb install [-r]`; preserves its Success/Failure verdict, records `replace_requested`, and leaves actual replacement `null` because prior package state is not observed |
| `uninstall_adb_package` | **mutates device** | Removes a package and its data for user 0 |
| `uninstall_adb_package` | **mutates device** | Requests package removal for user 0 via `adb shell pm uninstall --user 0`, without data retention |
| `start_adb_app` | **mutates device** | Resolves the package's launcher activity (`cmd package resolve-activity --brief`) and starts it (`am start -n`) |
| `start_adb_activity` | **mutates device** | Starts an intent (`am start -a` with optional URI, component, and up to 16 typed extras); preserves the activity manager's verdict |
| `stop_adb_app` | **mutates device** | Force-stops every process of a package, ending a dynamic-analysis run |
For `inspect_adb_window`, `focus_contexts` retains each display header, its exact
display ID when recognized, and every focus declaration with its original line.
Declarations without a display header are explicitly `unscoped`. A missing
declaration differs from a declaration whose `value` is JSON `null`.
`source_section` identifies the selected dumpsys section; `observed_lines`
preserves its focus declarations and display headers in source order.
`focus_summary` is `single_context` only for one context with no repeated focus
field. The singular `focused_window` and `focused_app` summarize that context;
they do not establish device-wide focus. Multiple contexts or repeated fields
produce `ambiguous` with null singular fields and all declarations retained.
When no focus field was declared, the status is `unavailable`. REA reads the
displays section only when the windows section declares neither focus field.
For `install_adb_package`, `replace_requested` records whether `-r` was sent.
The `replaced` result is `null`: the successful install response does not say
whether the package was installed fresh or replaced, and REA does not query the
+45 -11
View File
@@ -103,9 +103,10 @@ that every class or engine operation will load successfully. Discovery limits
each ZIP metadata read to 8 MiB before allocation; a larger directory reports
the exhausted read budget and the selected JAR path.
The current metadata bridge is verified on macOS arm64 with OpenJDK 21 and the
public Appium ApiDemos fixture. The POSIX adapter also supports Linux; the new
bridge has not yet undergone real verification on Linux/JDK 25.
The filtered class-page bridge (protocol 2) is verified on Linux x86-64 with
Temurin 21, public Appium ApiDemos 6.0.18 and NewPipe 0.29.1 APKs. Earlier
metadata-bridge records covered macOS arm64/OpenJDK 21 and Windows x64/JDK 21;
this protocol revision has not been rerun on those hosts or Linux/JDK 25.
Windows x64 uses an owned Job Object with bidirectional stdio, atomic job
assignment and kill-on-owner-close. Real Windows verification uses JDK 21 and
the pinned JADX 0.7.1 JAR; see the real verification lane below.
@@ -146,10 +147,20 @@ queries accept the same `overload_index`; the bridge resolves the selected
method node and the result reports its index and displayed signature. These
indices are tied to the current artifact and engine, not stable across builds.
The engine's smali fallback also joins same-name overloads; REA rejects that
fallback for an overloaded method instead of attributing all bodies to one
selected overload. Native/abstract methods with no decompiled body report
`body_status: "not_available"` without inferring which implementation flag caused it.
Smali fallbacks select one complete block using the original method name and DEX
descriptor, before applying the source byte limit. This preserves the selected
overload even when Java code generation renames a method. If the selected Java
body was omitted, including a method merged into a synthetic bridge, REA returns
its original smali with the `java_body_unavailable` marker. The marker describes
missing Java; it does not infer why the method was omitted or name a replacement.
Unresolved or duplicate disassembly identities fail instead of attributing another
method's instructions to the selection.
Native and abstract methods are identified from access flags
captured before code generation. They report `body_status: "not_available"`, an
empty source, and the `native_method` or `abstract_method` marker; their declarations
are not passed through the engine's source slicer. Other missing decompiled bodies
do not establish either access flag.
Static initializers (`<clinit>`) return smali with the
`static_initializer_disassembly` marker. Java decompilation can move their
@@ -163,7 +174,10 @@ the same source byte limit and explicit truncation reporting.
permissions and class/resource counts. Summary fields are extracted by the
upstream engine and should be checked against the retained XML when needed.
Signature verification is explicitly `not_performed`.
- Class search consumes all pages and checks their counts. An empty `query`
- Class search filters the engine's cached top-level names before transferring
pages, then follows contiguous inventory positions without rescanning earlier
rows. It checks selection, scope, examined ranges and counts before reporting
complete coverage. Complete producer replies stay in Evidence; an empty `query`
inventories all class-name rows; class names are decompiler representations.
Repeated names remain repeated in `matches` so the inventory row count and
producer evidence stay visible. `duplicate_names` identifies matching names
@@ -233,8 +247,12 @@ rea project-android-application-graph '{"inventory_evidence":[<inventory_artifac
```
The projection reports exact component paths and hashes, runtime-family hints,
and path-based bridge hypotheses. It does not decode DEX or claim observed
runtime calls. Framework hints match complete native library names, such as
and path-based bridge hypotheses. File and slice occurrences supply component
endpoints; directory trees and symlinks retain their identities in the source
inventory without being classified as code files. It does not decode DEX or
claim observed runtime calls. DEX/JVM runtime hints summarize the classified
bytecode component lists. Framework hints match complete native library names,
such as
`libreactnativejni.so`, `libreactnative.so`, `libhermes.so`,
`libhermes_executor.so`, `libflutter.so`, and `libunity.so`; unrelated names
such as `libcommunity.so` and `libreactivity.so` do not identify those families.
@@ -299,7 +317,15 @@ settings. Evidence reports the worker's observed `Runtime.maxMemory()` rounded
down to MiB and retains exact bytes, Java version and visible processors in its
raw runtime response. The observed allocatable heap can differ from configured
`-Xmx`, especially with Serial GC. Heap is not a total RSS cap: the JVM also
uses native memory. Independent REA instances have independent queues. Whole
uses native memory. A typed JVM memory allocation failure during APK loading
reports `resource_constraint` with the observed maximum heap in bytes. Known
heap failures name `REA_JADX_HEAP_MIB` in remediation; other memory regions
remain unknown. A healthy doctor configuration check does not establish that
any particular APK fits. Failures before the bridge can report a typed result
and upstream errors from other operations retain their existing diagnostics;
error text alone does not establish memory exhaustion.
Independent REA instances have independent queues. Whole
operations have a **120-second** execution deadline by default, starting after
they reach the queue's front. For a known slow cold load, such as a large
multidex APK, select another finite deadline:
@@ -337,6 +363,14 @@ export REA_ANDROID_TEST_APK="$PWD/_reference/apk-integration/ApiDemos-debug.apk"
npm run verify:android
```
The main lane and Windows Java-selection checks validate the selected JDK and
engine archive through `validateVerifierJadxConfiguration`. This shared verifier
boundary supplies the production validator's resource owner and waits for
retained cleanup before proceeding. Failed closes retain their exact owners for
retry; prerequisite failures must not be mistaken for completed bridge tests.
Recovery checks match the typed Java or archive failure and its applicable
remediation.
The lane also uses the selected full JDK's `jlink` to create a disposable JRE
without the compiler module. It checks real Java startup failures, missing
selectors, recovery after failures, and CLI cancellation with owned cleanup.
+81 -11
View File
@@ -8,7 +8,7 @@ installs Apktool or a Java runtime.
## Supply tools
Use Apktool 2.7.0 or newer (verified with 2.7.0) and a Java runtime the
Use Apktool 2.7.0 or newer (verified with 2.7.0 and 3.0.3 on Linux) and a Java runtime the
launcher can find. Select the launcher through the environment:
```sh
@@ -24,6 +24,30 @@ missing launcher reports `capability_unavailable` with the
`binary_session` uses the same host probe for tool availability; it does not
validate a particular APK or guarantee that the selected input can be decoded.
Client inspection and decode results disclose the following reported-version
advisory windows without refusing otherwise compatible launchers:
| Advisory | Reported version window | Host scope |
| ----------------------------------------------------------------------------------------------------- | ------------------------------------------------ | ---------- |
| [CVE-2024-21633](https://github.com/iBotPeaches/Apktool/security/advisories/GHSA-2hqv-2xv4-5h5w) | Before 2.9.2 | All |
| [GHSA-vgwr-4w3p-xmjv](https://github.com/iBotPeaches/Apktool/security/advisories/GHSA-vgwr-4w3p-xmjv) | Before 2.9.3 | Windows |
| [CVE-2026-39973](https://github.com/iBotPeaches/Apktool/security/advisories/GHSA-m8mh-x359-vm8m) | 3.0.0 release line through versions before 3.0.2 | All |
The 2026 advisory names 3.0.1 and its 3.0.2 fix. Inclusion of 3.0.0 is
source-derived: [tagged 3.0.0](https://github.com/iBotPeaches/Apktool/blob/f770b39e425af4fa6ac4ca1e9ea6c4824339c6d3/brut.apktool/apktool-lib/src/main/java/brut/androlib/res/table/ResTypeSpec.java)
passes type names through unchanged; the [3.0.2 patch](https://github.com/iBotPeaches/Apktool/commit/65dd8480dfcb63068562ffaa527f71bb0a9f772c)
validates those names at their owner before they reach the output-path producer.
Prereleases before the fixed release are conservatively disclosed; the exact
`-dirty` downstream marker is compared as a release while preserving its spelling.
This is version guidance, not verification of a particular prerelease or decoder.
A launcher version does not prove the decoder's contents, execution platform,
downstream backports, or overall safety. Windows applicability uses the selected
local host; an opaque wrapper may run another engine. A missing warning means
only that none of these version/host windows matched. Verify the selected
decoder's patches before decoding untrusted APKs; the compatibility floor above
is not a security recommendation. REA does not fetch advisories at runtime.
On Windows, a `.bat` or `.cmd` launcher (such as Chocolatey's `apktool.bat`)
runs through an explicit `cmd.exe /d /s /c` call. That call has no AutoRun,
no delayed expansion, and quotes every argument. A launcher path that
@@ -38,20 +62,36 @@ reports `capability_unavailable`.
rea inspect-apktool-client
rea decode-android-resources /targets/Example.apk
rea decode-android-resources /targets/Example.apk --locale de
rea decode-android-resources /targets/Example.apk --locales de --locales pt-BR
rea decode-android-resources /targets/Example.apk --locales de --locales pt-BR --string-tables-only
rea decode-android-resources /targets/Example.apk --no-strings
```
Equivalent MCP methods are `inspect_apktool_client` and
`decode_android_resources`. The decode request takes `path`, an optional
`locale` (project `res/values-<locale>/strings.xml` instead of the default
table), and `include_strings` (default true). Regional locales such as `pt-BR`
select Android's `values-pt-rBR`; Android qualifiers such as `pt-rBR` and
`b+zh+Hans+CN` may also be supplied directly.
primary `locale` (project `res/values-<locale>/strings.xml` instead of the default
table), `locales` (repeatable array of additional localized string tables to project
from the same single decode pass), and `include_strings` (default true). Regional locales such as `pt-BR`
select Android's `values-pt-rBR`. A three-digit region such as `es-419`
selects `values-b+es+419`; `es-r419` is rejected because Android's legacy
`-r` form requires a two-letter region. Android qualifiers such as `pt-rBR`
and `b+zh+Hans+CN` may also be supplied directly.
Set `string_tables_only: true` (CLI `--string-tables-only`) to return string
entries only inside `string_tables`, including when selecting a single table.
The top-level `strings` array is then empty by request; it does not establish
that the primary table is empty. Table status, counts and coverage still describe
the examined source. This avoids repeating primary entries in multi-locale
responses and charges the aggregate output budget for one copy per table.
Omitting the option preserves existing responses. `include_strings: false`
still suppresses all string projection, including in this mode.
## Interpret the result
- **An owned APK snapshot is digested and decoded.** `target.sha256` and
`target.bytes` bind the observation to the exact APK that was decoded.
The Evidence subject carries the same snapshot digest and caller-selected
path, including when the original file changes during decoding.
- **Metadata comes from apktool.yml as apktool printed it.** Version codes
keep their own spelling (`'7'` versus `7`); `package_name` is read from
the decoded manifest, not inferred.
@@ -59,9 +99,29 @@ select Android's `values-pt-rBR`; Android qualifiers such as `pt-rBR` and
truncated when larger.
- **Strings are projected verbatim.** Escaped entities stay escaped, and
entries containing nested markup (`<b>`, `<xliff:g>`) are counted as
unparsed rather than flattened — the limitation names the count. The
default table is bounded to 2000 entries with partial coverage when more
exist; `locales` lists every `values-<locale>` that has a strings table.
unparsed rather than flattened — the limitation names the examined count.
Malformed XML and a wrong document root are whole-document projection
failures: the result is partial, the failure reason is disclosed, and entry
counts remain unknown. They are not counted as one unsupported string.
The default table is bounded to 2000 entries with partial coverage when more
exist. `locales` lists observed regular `strings.xml` tables whose exact
`values-<locale>` directory is selectable by the request's locale grammar.
Locale suffixes allow up to 23 ASCII characters, derived from Apktool's
language, script, region, and variant fields; for example,
`b+sr+Latn+RS+REVISED` selects that exact table.
Other table directories, including combined qualifiers such as
`values-en-rUS-v21` and `values-mcc310-fr`, are named in limitations with
their source spelling. They are not advertised as selectable locales;
REA does not infer Android's runtime resource matching. Non-regular
strings-table paths are disclosed with partial coverage. Discovery uses
the same bounded traversal as the file count; filesystem failures remain
failures rather than being interpreted as missing tables.
When multiple tables are requested with `locales`, `string_tables` records
each projected table with its locale, status (`present`, `missing`,
`oversized`, `malformed`, or `skipped`), entry counts, and coverage,
bounded at an aggregate budget of 10,000 strings and 32 MiB XML. When `locales`
is omitted and `string_tables_only` is not true, legacy single-table payloads are preserved
without adding `string_tables`.
- **Smali is skipped by design** (`apktool d --no-src`): Java-source
recovery belongs to the JADX family. Resource smali edits and rebuild are
a follow-up lane with mutation authority.
@@ -81,9 +141,11 @@ select Android's `values-pt-rBR`; Android qualifiers such as `pt-rBR` and
coverage and an explicit limit, rather than reported as missing.
- `decoded_file_count` is capped at 100,000 files; the cap is reported in
the limitations when hit.
- Apktool's own decode failures (`AndrolibException`, directory traversal
refusals) surface as `unsupported_target` with apktool's reason preserved
verbatim.
- An ordinary nonzero decode exit surfaces as `unsupported_target` naming
the caller-selected APK, with Apktool's diagnostic and captured output.
Launcher startup, ownership preparation, signal termination and incomplete
output collection remain provider failures; they do not establish that the
selected APK is unsupported.
## Real verification
@@ -94,3 +156,11 @@ locales — launcher version, on-disk digest agreement, apktool.yml metadata
(1.2.3, SDK 24–34), manifest package agreement, default and `de` string
tables projected. Parser goldens in the test suite come from the same
decode. See `docs/testing.md` for the lane summary.
Multi-locale and table-only projection were also verified through real CLI and
stdio MCP workflows on Linux with Apktool 3.0.3/JDK 21 against NewPipe 0.29.1,
ApiDemos 6.0.18 and Appium Settings 8.2.1. Independent XML projection checks,
exact facts/Evidence parity and bounded task costs are recorded in the
[shared-decode and table-only checkpoint](../experiments/analysis-efficiency/apktool-string-tables.md).
This qualifies selected resource facts, not application execution or independent
binary resource-decoder completeness.
+18
View File
@@ -60,6 +60,15 @@ states that absence is unknown. Component arrays remain complete when candidate
hypotheses are omitted; component arrays still include every component from
the supplied inventory pages, and overall coverage is `partial` in that case.
Inventoried files whose bytes were not read, such as encrypted archive members,
are listed in `unavailable_components` with their original path, format,
encryption flag, limitations, and null content identity. The same application-root
and AppleDouble attribution rules apply. These files make overall coverage
`partial`; component absence is unknown even when `inventory_complete` is true.
Directories have no file bytes and symlinks remain in `symlinks`, so neither is
an unavailable file component. The additive field participates in `projection_id`,
including when empty, so Apple projection identifiers change.
## Application roots
| Inventory root | Application root |
@@ -68,6 +77,15 @@ the supplied inventory pages, and overall coverage is `partial` in that case.
| `.app` directory | `.`, the inventoried bundle itself |
| Directory, ZIP, or DMG containing apps | Each outermost `<Name>.app` followed by `Contents/` |
File and slice occurrences supply component endpoints. Directory tree identities
remain in source inventory and bundle anatomy, and links remain in `symlinks`.
Root and layout discovery use known directories and ancestors implied by deeper
inventory paths. A leaf file or symlink named `*.app`, `Contents`, or `Versions`
does not establish that directory. Directory and authenticated-file identity are
selected before applying anatomy case preferences, so a file cannot shadow a
folded directory name or a directory shadow a metadata/signature file.
ZIP archives made with `ditto -c -k --keepParent` report `<Name>.app`. A DMG
reports `<image>.dmg/<Volume>/<Name>.app`; DMG child inventory needs the native
macOS mounting adapter. When a DMG has only its root identity, the projection is
+6
View File
@@ -26,6 +26,7 @@ graph TD
end
subgraph "Provider Layers"
PROVIDER_OWNERS["src/providers/<br/>shared deferred client and inspection owners<br/>exact cleanup retry and attributed failures"]
PWNTOOLS["src/native/pwntools/ + src/native/pwndbg/<br/>BYO offline ELF/core adapters<br/>owned snapshots/Python and optional core-only GDB<br/>unchanged upstream parsers, ABI structures and mapping API"]
WASM["src/wasm/<br/>caller-supplied WABT; immutable artifact inspection<br/>owned process supervision and retryable workspace cleanup"]
EVM["src/evm/<br/>unchanged EVMole/WASM in bounded owned worker<br/>explicit carrier parsing and inferred interface adapter"]
@@ -138,6 +139,11 @@ graph TD
GHIDRA --> GHIDRA_BRIDGE
HOPPER --> PROCESS
GHIDRA --> PROCESS
GHIDRA --> PROVIDER_OWNERS
APP --> PROVIDER_OWNERS
PROVIDER_OWNERS --> PROCESS
PROVIDER_OWNERS --> AUX
PROVIDER_OWNERS --> DOMAIN
GHIDRA --> WINDOWS
PROCESS --> WINDOWS
WINDOWS --> WINDOWS_NATIVE
+8
View File
@@ -285,6 +285,14 @@ REA establishes a main-frame origin boundary before enabling Runtime, Debugger,
## Data minimization
The Link projection parses repeated CDP header field lines independently and
retains their declarations in order, including derived untrusted agent hints.
Commas inside URI targets or quoted parameters remain part of those values.
COOP and COEP projections retain the exact declared token after validating the
complete Structured Field Item, including parameters such as `report-to`.
These are declarations, not an assessment of the document's effective policy;
absent, malformed or non-token items project as `null`.
REA retains local observation data and removes only URL userinfo credentials, explicitly declared scenario secrets, and values from structured credential/storage fields:
- URL observations preserve the complete local URL, including query values, fragments, duplicate parameters, and their order. Only username/password userinfo is removed; malformed original URL strings remain available with `origin: null`.
+12
View File
@@ -144,6 +144,18 @@ distinction: it closes and deletes provider-owned profiles, but only disconnects
from an external CDP browser. Real-browser verification checks both outcomes and
confirms that an attached external browser remains alive.
That cleanup requires REA to remain running. Abrupt termination, including
Windows process-tree termination or POSIX `SIGKILL`, can leave the launch
profile and Playwright artifacts behind. A profile can retain cookies, history,
cache and origin storage. Later REA runs currently do not reclaim these paths.
Playwright 1.58.2 can also leave an artifacts directory when launch preparation
fails before a browser process exists, such as for a missing executable.
POSIX wrappers can request awaited cancellation with `SIGINT` or `SIGTERM`;
Windows process termination is not an equivalent graceful-cancellation channel.
Do not identify owned artifacts by directory prefix or creation time alone.
See [#4659](https://github.com/morluto/rea/issues/4659) for the remaining recovery
and wrapper-cancellation work.
Keeping that browser alive does not guarantee its emulation state survives.
Chromium can clear caller-owned `navigator.platform` emulation when a page CDP
session disconnects, including sessions that sent no emulation commands. REA
+9 -2
View File
@@ -56,8 +56,15 @@ rea inspect-electron-page http://127.0.0.1:9223 TARGET_ID \
The normalized result contains canonical local paths, frame and DOM
structure, resource metadata, stable script/resource identities, explicit
completeness, and content-addressed script artifacts. Script metadata
retains its execution-context frame ID when CDP supplies one. The capture also
completeness, and content-addressed script artifacts. Resource sizes are unknown
when omitted or invalid; supplied invalid sizes also produce an explicit
completeness diagnostic.
DOM node indices retain their snapshot positions. Invalid rows are omitted with
completeness diagnostics, so `total_nodes` can exceed the retained node count
and a parent index can refer to an omitted source node.
Script metadata retains its execution-context frame ID when CDP supplies one. The capture also
inventories worker, service-worker, and shared-worker targets with
validated opener-target and parent-frame IDs. Worker discovery uses passive
target metadata; REA does not attach to or execute code in those targets.
+3 -1
View File
@@ -153,7 +153,9 @@ tree semantics, vulnerability scanning, emulation or hardware runtime capture.
## Resource and lifecycle policy
- Nonempty regular firmware inputs: at most **128 MiB**, copied and hashed into
a private workspace before analysis. No in-place source modification.
a private workspace before analysis. Growth beyond that allowance during
copying receives the same typed input refusal as an already oversized file.
Supply a stable copy within the allowance. No in-place source modification.
- One queued heavy operation per provider instance; Binwalk one thread and
Unblob one worker. No automatic parallel extraction engines.
- **120-second** deadline; inherited **1 GiB per-process address-space** limit,
+8
View File
@@ -124,6 +124,10 @@ FileBytes digests, every source mapping and its memory digest, relocation record
module partitions, relocation fixups and linear coordinates rather than relying
on the imported executable SHA alone.
Expected DOS relocation records retain each fixup's pre-application bytes and
applied value. Their memory-byte windows come from the final relocated image,
including changes made by later overlapping fixups.
The result is `verified`, `mismatch`, or `unsupported`. Every check retains its
expected and observed values. A memory-digest mismatch identifies the source
range's file offset and analysis address; it does not locate the first differing
@@ -134,6 +138,10 @@ their contents and DOS runtime semantics are not verified. Independent verificat
also covers explicit [raw-binary imports](raw-binary.md); other formats return
`unsupported` with measured observations.
MZ verification retains the initialized image described by the header's page
count and streams appended overlay bytes into the complete original and modified
source digests. Overlay size does not increase the retained image allocation.
`read_bytes` returns initialized provider memory, including loader fixups; an
unmapped or uninitialized byte ends the read with an explicit returned length and
`complete: false`. `address_to_file_offset` uses the provider's source mapping;
+8
View File
@@ -65,6 +65,14 @@ PDB, or change analyzer settings.
## Loaded addresses and file offsets
Ghidra computes overview procedure and string counts from the same inventories
used by `list_procedures` and `list_strings`, without transporting those rows
solely to count them. Counts include external functions and only Ghidra-defined
strings; they do not establish discovery of unidentified functions or undefined
string bytes. Complete inventories remain available through the list tools.
Overview reuse follows the target/profile snapshot lifetime and is invalidated
by session metadata edits.
`rea analyze <path> --provider ghidra --json` and MCP `binary_overview` report
the observed loaded `image_base` in `normalized_result`. They retain each
segment's reported `image_base` and `address_space`. A provider that does not
+4
View File
@@ -91,6 +91,10 @@ host permission denial, changed artifacts and cancellation retain distinct
errors. The selected file is never modified; this inspection creates no
temporary files, starts no subprocesses and accesses no network endpoint.
An input above 256 MiB returns `resource_constraint` with resource `file-size`
and `reported_limits.maximum_input_bytes`. Select a suitable input within that
fixed allowance; changing an output-write or transport budget does not admit it.
Resource guards bound the selected file and cumulative header searches to 256 MiB
each, aggregate embedded strings
to 1 MiB, structural table decoding to 16 MiB and the complete normalized metadata
+6
View File
@@ -45,6 +45,12 @@ The MCP tool `project_harmony_application_graph` takes the same
- path-based N-API bridge hypotheses between bytecode and `libs/<abi>` native
libraries
File and slice occurrences supply component and child-package paths; directory
trees and symlinks retain their identities in the source inventory without
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.
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
+3 -1
View File
@@ -8,6 +8,8 @@ Choose one lifecycle mode in an upstream MCP JSON registration. Set `REA_IDA_MCP
Each mode accepts one upstream tool profile, checked against `tools/list` before any analysis runs. The attached profile requires the legacy GUI tool names; upstream 2.0.0 renamed those tools, and REA has not established an equivalent attached-mode lifecycle and ownership contract for that catalog. It therefore fails closed with the missing tool names in the error diagnostics.
Startup failures identify the accepted REA analysis operation. Missing-profile and typed startup transport failures retain the original stage in `details.diagnostics.startup_operation`; existing diagnostics, timeout budgets and resource limits remain available. Startup remains shared within a session; each caller receives its own operation attribution. Cancellation retains live cleanup ownership, so a successful retry releases the originating cancellation outcome as well.
The headless profile is defined by the database-supervisor API, not by an upstream version string. The upstream source snapshot [`c133c385`](https://github.com/mrexodia/ida-pro-mcp/tree/c133c3853faa111a9b00ee615c013b720d0c4acd) includes `idb_close` (introduced by [upstream PR #495](https://github.com/mrexodia/ida-pro-mcp/pull/495)) alongside `idb_open` and `idb_list`. Earlier builds may report the same `2.0.0` version while lacking that tool, so REA checks the live catalog rather than treating a version number or branch name as a compatibility guarantee.
| Mode | Upstream profile | Known working |
@@ -39,7 +41,7 @@ rea function /absolute/path/to/program main --json
rea decompile /absolute/path/to/program main --json
```
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.
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.
+75 -6
View File
@@ -57,14 +57,26 @@ Before applying changes, REA checks your current configuration. The plan lists:
Malformed or unsafe existing configuration blocks the whole transaction before
Hopper installation or any file write. Declining or pressing Ctrl-C makes no
changes. Agent configuration writes preserve unrelated entries and comments,
create backups, use atomic replacement, and verify their result. Setup and
uninstall retain an existing `.rea.backup` rather than replacing the first snapshot.
create backups, stage changes in a private directory, and verify their result.
Before publication, they refuse detected changes to the selected configuration.
Replacement uses an explicit destination under the selected canonical parent;
it does not follow a newly inserted destination symlink. First-run setup uses
an exclusive hard link and fails without overwriting a newly created destination;
filesystems without hard-link support cannot publish a first-run configuration.
Setup and uninstall retain an existing `.rea.backup` rather than replacing the
first snapshot.
If that snapshot differs from the current configuration, setup and uninstall
preserve a verified `.rea.backup.<sha256>.recovery` copy of the current bytes
before editing; setup also previews it. Both snapshots remain available.
When an edit made a backup, a failed uninstall update or restore, and a failed
setup readback or rollback, name the backup path that holds the bytes from just
before that edit.
If setup cannot verify a newly created configuration, it retains the file and
reports manual recovery instead of deleting a pathname that may have changed.
For an existing configuration, setup and uninstall recovery refuse a detected
replacement or edit and name the recovery backup. Quit clients before editing their configuration;
these checks do not lock out another writer modifying the same file in place,
or exclude pathname changes between the final check and publication.
Setup also retains valid environment overrides in REA's existing server entry,
such as `REA_MCP_INPUT_SCHEMA_PROFILE`, `REA_MCP_OUTPUT_SCHEMA_PROFILE`, and
`REA_MCP_MAX_RESPONSE_BYTES`.
@@ -264,14 +276,14 @@ Register with the variable unset:
<!-- x-release-please-start-version -->
```bash
claude mcp add --scope local rea -- npx -y rea-agents@6.5.0 mcp
claude mcp add --scope local rea -- npx -y rea-agents@6.7.0 mcp
```
On native Windows, use the same project directory and wrap the package-runner
shim so the stdio client can launch it:
```powershell
claude mcp add --scope local rea -- cmd.exe /d /c npx -y rea-agents@6.5.0 mcp
claude mcp add --scope local rea -- cmd.exe /d /c npx -y rea-agents@6.7.0 mcp
```
<!-- x-release-please-end -->
@@ -364,7 +376,12 @@ Uninstall preserves Hopper, Node.js, Evidence files, captures, unrelated skills
and other MCP servers. Purging removes only REA's cache and state under
`~/.rea`. A client configuration that is malformed, unreadable, or at an unsafe
path stops the operation before anything is removed, and a client that fails
while being updated stops the remaining removals. A purge path that is a
while being updated stops the remaining removals. For JSON clients, uninstall
removes every repeated `rea` member in the effective server table only when
each registration is REA-owned. An unowned earlier member, or an unowned
OpenCode V1 entry that removing V2 would expose, stops preflight before any
removal. Repeats inside an inactive earlier copy of the server table stay
unchanged. A purge path that is a
symbolic link is retained and reported rather than followed. Uninstall checks
REA's managed shared and Claude Code personal skill locations. See the
[CLI guide](cli.md#output-and-exit-status) for exit statuses.
@@ -589,6 +606,20 @@ host mount and the rest of `/tmp` remain unchanged; this fallback never invokes
`sudo`. `rea doctor --provider hopper --json` reports the selected
strategy and both host and effective mount facts.
Hopper also embeds Python; the Python executable and standard library alone
are insufficient on Ubuntu. Approved setup checks the system interpreter
at `/usr/bin/python3` and, when necessary, installs its matching
`libpython3.X` package, then verifies that its shared library can load.
APT resolves the concrete package (for example, `libpython3.12t64` on Ubuntu
24.04). Doctor includes these fresh observations in the `embedded_python`
details of its `hopper-demo-runtime` check, alongside the private-display
diagnostic. Failed inspections remain unknown rather than proving a library
absent. Run `rea setup --yes --install-hopper` to repair a supported install.
This prerequisite check does not launch the bridge or validate a custom Python
interpreter configured inside Hopper; that interpreter needs its own matching
shared runtime. Custom installation prefixes or shared-library ABIs require
manual dependency repair.
When the package-manager step fails, setup reports the failing command and its
captured output, so a package conflict is distinguishable from an authorization
failure. On CachyOS, an unversioned compatibility package such as
@@ -896,6 +927,19 @@ the launcher fails before preparing its home/cache/config/temp directories or
starting Java. The client may already have allocated private runtime/endpoint
resources and a target snapshot; existing startup-failure cleanup still applies.
Restore the selected release's properties instead of renaming its installation folder.
Inspection follows selected properties symlinks, admits only a regular file,
and caps the metadata payload at 1 MiB under REA's resource budget. A final
one-byte probe checks for growth. This is not a Java
Properties format limit. Reads preserve Ghidra's Latin-1 byte mapping and check
the descriptor and selected name for changes. Permission failures and resource
refusals remain explicit; failed descriptor cleanup must succeed before that
owner admits another inspection.
Native decompiler compatibility inspection follows the selected executable symlink
and reads at most the first 4096 bytes through the same descriptor owner. It
checks the full extent and selected name for changes without reading the remainder.
An unreadable or unrecognized header leaves compatibility unknown; diagnostics
retain read failures. Cancellation and incomplete cleanup propagate as failures,
and failed cleanup retains the exact descriptor for retry.
This coordinate selects only REA's session-private configuration directory, not
the installation or an existing user configuration directory.
@@ -914,6 +958,10 @@ ownership inspection, so retaining those wrappers would prevent verified
process-group cancellation during startup.
If ownership remains unverifiable, it reports the reason and retains the process
supervisor and private runtime instead of removing files beneath a live provider.
Startup preserves an observed launch failure when cancellation arrives during
rollback. Producer cancellation retains the caller's exact reason separately
from LaunchSupport warnings and cleanup failures. Failed rollback keeps the
original process owner for a later cleanup retry.
The experimental Windows transport uses authenticated IPv4 loopback with a
private native-owned bearer descriptor and Job Object process ownership.
@@ -1009,6 +1057,8 @@ export REA_GHIDRA_COMPILER_SPEC_ID="borlandcpp" # optional; requires a languag
REA passes `-processor`/`-cspec`, checks both against the installation's `.ldefs`, and records them in the analysis profile. DOS imports reject an override.
Language definitions must be well-formed XML encoded as UTF-8 or BOM-marked UTF-16, with any encoding declaration matching the bytes. Literal Unicode replacement characters (U+FFFD) are accepted; malformed source bytes are rejected.
To seed code that auto-analysis cannot reach (computed jumps, pointers in data):
```bash
@@ -1108,6 +1158,10 @@ compiler remains distinguishable from a compiler that exited with an error.
The helper compile deadline defaults to 120,000 ms; set
`REA_SWIFTC_COMPILE_TIMEOUT_MS` to an integer between 1 and 2,147,483,647 when
a busy host needs longer.
On macOS, the Java prerequisite probe waits for this independently bounded
preparation before starting its five-second command deadline. Caller cancellation
still interrupts preparation, and failed helper cleanup remains owned for retry.
Linux process-table preparation remains inside the command deadline.
Other rejection values retain their primitive value or an explicit type.
Shutdown warnings include the failure
kind, message, and diagnostics, while process and temporary-project cleanup
@@ -1154,7 +1208,7 @@ For a client that requires manual configuration, use:
"mcpServers": {
"rea": {
"command": "npx",
"args": ["-y", "rea-agents@6.5.0", "mcp"]
"args": ["-y", "rea-agents@6.7.0", "mcp"]
}
}
}
@@ -1219,6 +1273,21 @@ on the shell `PATH`. Use the setup command above for the guided setup
journey, `npx -y rea-agents@latest` for unattended one-off commands, or install globally
with `npm install --global rea-agents` for a shell-visible `rea` command.
For many concurrent agent sessions, run setup directly from a global installation
to avoid keeping a separate npm launcher process for each MCP connection:
```bash
npm install --global rea-agents
rea setup
```
Review the plan, approve the selected client changes, then restart those clients.
This registers the installed entrypoint directly; installing the global package
alone does not replace existing `npx` registrations. Each client still has its own
REA process and analysis state. Direct registrations follow the installed CLI;
use `rea update` to update it. Running setup through `npx` again selects the pinned
package-runner command. Do not register paths inside npm's temporary package cache.
The optional curl wrapper installs only the global npm package:
```bash
+21
View File
@@ -288,6 +288,27 @@ and the result includes its basis, candidates, changed dimensions, Evidence
links, limitations, and a `changed_from` graph containing all compared nodes,
their observations, and relationships.
A matched pair whose only differing dimension is `location` counts as
`unchanged` when both input graphs have complete coverage and its neighboring
relationship identities are resolved. This includes an
identical module shifted by an earlier bundle edit and a named IPC channel
observed at a new offset. The item still reports `dimensions: ["location"]`
and both node IDs; the graph retains both original observations and coordinates.
Content, property, and complete relationship differences still count as
`changed`. With incomplete coverage, relocation alone remains `unknown` and
retains the location and coverage dimensions. Matching confidence is unchanged:
an inferred pair is not promoted to exact identity by this classification.
Complete inventory does not resolve ambiguous or unmatched relationship targets.
A matched module with identical bytes can therefore remain `unknown` when a
neighbor, including its containing chunk, has no cross-version match. The item
retains its exact content match, coordinates, candidate inventory, and an explicit
relationship-identity limitation. `coverage` is added to its dimensions only for
incomplete inventory. Definite content, property, or complete topology differences
remain `changed` while retaining any separate relationship uncertainty.
An unknown item with no observed content, property, or relationship difference
does not emit a `changed_from` edge; its pairing and uncertainty remain in the item.
One-sided absence is `added` or `removed` only when the opposite input graph has
complete coverage. With partial, unavailable, or truncated input, the same
condition is `unknown`. The comparison retains every classified item and
@@ -586,6 +586,21 @@ not bypass worker heap, execution deadline or parent result admission. Truncated
coverage reports both effective node ceilings; omitted relationships remain
unknown.
Unknown records have independent defaults of 100000 across the application and
20000 per source. Select `max_semantic_unknowns` and
`max_semantic_unknowns_per_file` (CLI: `--max-semantic-unknowns` and
`--max-semantic-unknowns-per-file`), each from 1 to 1000000. The parent offers the
remaining capacity to the worker, and rejects excess records before adoption.
Unknown admission happens before canonical identity and provenance construction.
When capacity is exhausted, coverage is partial and truncated and names both
effective unknown ceilings. `coverage.omitted_unknown_occurrences` counts examined
emission occurrences by family and reason, including repeated emissions. It does
not claim distinct discarded identities or count frontiers in unexamined source.
Omitted unknown provenance and IDs are not retained. Existing source, node,
execution and memory limits continue to determine examined coverage. Raising only
a node capacity does not raise the unknown capacity. Graphs without unknown
omissions omit this optional coverage field, preserving their existing identities.
While sources remain, the sequential worker reserves one next compact static
checkpoint at its 8 MiB bound with the same 6x retention expansion used at
admission. Earlier checkpoints are already charged to retained bytes; the
+5 -2
View File
@@ -133,8 +133,11 @@ Each operation uses one worker, a 1 GiB address-space ceiling and a 120-second
deadline; it accepts up to 64 MiB of total input across all selected files and
128 MiB/10000 entries of staged
output, with 8 MiB per diagnostic/report stream. Exceeding a limit is an explicit
failure with rollback. These limits apply per operation and do not claim aggregate
host containment across concurrent callers.
failure with rollback. Selected inputs beyond the total input allowance return a
non-retryable `resource_constraint` with `resource: "file-size"` and
`reported_limits.maximum_input_bytes`/`remaining_input_bytes`; input that is not
valid UTF-8 is an `invalid_format` request issue. These limits apply per
operation and do not claim aggregate host containment across concurrent callers.
See [testing.md](testing.md#javascript-source-recovery) for the focused real
Wakaru CLI/MCP lane and its source-owned compiler fixtures. Other host platforms
+3 -1
View File
@@ -109,7 +109,9 @@ The direct observations are:
- `Runtime.executionContextCreated`,
`Runtime.executionContextDestroyed`, and
`Runtime.executionContextsCleared`, establishing context lifecycle within
the capture window.
the capture window. A created or destroyed event whose context ID is
missing or not a safe integer adds no context record; it is retained only
as a count reported in `unknowns`.
The Inspector protocol does not directly expose require/import caller edges,
EventEmitter emissions or listener invocation, Electron IPC messages or
+14 -4
View File
@@ -8,7 +8,16 @@ Start a JEB client that serves MCP and point REA at it:
- Default endpoint: `http://127.0.0.1:8425/mcp`. Override with `REA_JEB_MCP_URL` (absolute `http`/`https` URL with literal host `127.0.0.1` or `[::1]`; redirects, remote hosts, credentials, query strings, and fragments are rejected rather than silently dropped).
- In the JEB GUI client, start the MCP server from the _File_ menu (or via the VIBRE assistant).
- Headless, use JEB's documented programmatic route (`JebMcpServerInstance` from the JEB API) with the JEB client on your classpath; JEB 5.48's `-c` headless mode does not accept `--mcp` directly.
- Headless, the official Linux CE build `5.48.0.202610071539` supports `-c --mcp`. From the JEB installation directory, select JDK 21 or newer through `JAVA_HOME`, choose your target and bind explicitly to loopback:
```sh
"$JAVA_HOME/bin/java" -jar bin/app/jeb.jar -c --mcp --interface=127.0.0.1 \
--port=8425 --infile=/absolute/path/to/target
```
Complete the vendor's first-use EULA and license-key activation when prompted. The Linux audit used an isolated installation, a 1536 MiB Java heap and one active processor inside a separately bounded process family; JVM heap limits alone do not bound the whole family.
- JEB's documented programmatic route (`JebMcpServerInstance` from the JEB API) remains an alternative. The earlier licensed 5.48 headless verification used that route because its CLI did not accept `--mcp`. Check your installed build's `-c --help` and live tool catalog; the version number alone does not establish the launcher options or supported operations.
For setup registration, set `REA_JEB_MCP_URL` in the REA process environment
before running `rea setup`. The approved setup plan preserves the exact selected
@@ -31,7 +40,7 @@ or establish one end-to-end deadline across a whole CLI or MCP operation. A
request that reaches its budget returns a typed timeout, while caller
cancellation remains a cancellation.
Verified against JEB 5.48.0 (Linux x64, licensed build) serving `jeb-mcp-server` 1.3.0 over streamable HTTP.
Earlier verification used JEB 5.48.0 (Linux x64, licensed build) serving `jeb-mcp-server` 1.3.0 over streamable HTTP. The official Linux x64 CE build `5.48.0.202610071539` was also verified through real CLI and production stdio MCP native-code workflows; see the [Linux CE audit](../experiments/analysis-efficiency/jeb-linux-native.md) for measurements and limits. CE verification does not establish licensed/Demo Android decompilation or GUI behavior.
## Operations
@@ -42,15 +51,16 @@ Verified against JEB 5.48.0 (Linux x64, licensed build) serving `jeb-mcp-server`
| `list_jeb_units` / `list-jeb-units` | Page units by stable path and type (`filter`, `parent-unit-path`, `index`, `count`) |
| `decompile_jeb_item` / `decompile-jeb-item` | Decompile one type or method to engine pseudo-code |
All four are read-only with respect to the target file; only `open_jeb_project` changes engine-side project state, and Evidence records the endpoint, the caller-selected parameters, and the engine-reported digests. Mutation workflows the engine also offers (renames, comments, `save_project`, `close_project`) are intentionally not exposed yet.
All four preserve target-file bytes. `open_jeb_project` selects or creates an engine-side project; analysis queries can populate computed analysis within it. Evidence records the endpoint, caller-selected parameters, and available engine-reported digests. Mutation workflows the engine also offers (renames, comments, `save_project`, `close_project`) are intentionally not exposed yet.
**Engine coverage note (verified):** JEB 5.48.0 headless MCP instances do not advertise `open_project` — the target must be selected when the engine client starts (`--infile` on the programmatic launcher, or the GUI's file opening). REA preserves that refusal with the engine's reason instead of failing silently; `open_jeb_project` completes only against engine builds that advertise the tool, such as GUI clients.
**Engine coverage note:** The earlier licensed 5.48.0 headless verification did not advertise `open_project`; it selected the target when the engine client started. For that build, use `--infile` on the programmatic launcher or open the file in the GUI. The audited CE headless catalog also omitted `open_project`; the verified CLI route above selects its input at startup. Use `binary_session` to inspect the current engine's advertised operations. REA preserves an unavailable tool's refusal; `open_jeb_project` requires an engine that advertises `open_project`. The CE audit does not establish GUI project-opening support.
## Semantics
- Paths in `open_jeb_project` are resolved by the JEB process, not by REA; REA does not require the artifact to exist locally.
- The engine caps `list_units` pages at 100; a full page is reported as `coverage: "partial"` because more units may exist. An empty remainder is `complete` only when fewer units than requested were returned.
- 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.
- Engine refusals (`success: false` envelopes) are preserved as unsupported-target errors carrying the engine's own message, for example a nonexistent item address.
- 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.
+21 -1
View File
@@ -22,6 +22,8 @@ Select it explicitly with `--provider kuna`, MCP's `open_binary.provider_id: "ku
| `REA_KUNA_TIMEOUT_MS` | 120 s | Deadline for one single-function decompile, and for the inventory run REA makes when the export ends without one. |
| `REA_KUNA_JOBS` | 1 | `decompile-project --jobs`. 1 is Kuna's reference answer; more jobs finish sooner but lose callee-first prototypes, and change about 40–60% of function bodies on the measured binaries. |
A single-function decompile, inventory, string, graph or xref run that exceeds its deadline fails as `provider_timeout`, naming the setting that owns the deadline and that raising it takes a restart. A project export that exceeds `REA_KUNA_PROJECT_TIMEOUT_MS` is an export failure instead: requests that need the export report `capability_unavailable` with the export's own retry guidance.
Windows is not supported yet. Supported targets are ELF and thin Mach-O executables for x86-64, AArch64 and ARM; PE and universal Mach-O are rejected with a typed reason.
## What Kuna answers
@@ -68,7 +70,7 @@ Single-function subprocess output and each cached source facet share a 16 MiB bu
Results keep exact u64 virtual addresses, decoded text, native encoding, terminator status and byte length including any terminator. Kuna scans mapped sections or segments for runs of at least five characters; unterminated fragments can include instruction bytes. `all` includes ASCII, UTF-8 and the engine's Latin-range UTF-16LE reading. Other text, including short strings, non-Latin wide text and UTF-16BE, can remain undiscovered. An empty result does not prove absence.
Use `search_strings` for the matching rows or `list_strings` with an exact address for that location. Literal and regex matching run in REA, preserving the existing case and ECMAScript Unicode semantics instead of substituting Kuna's different regex dialect. Procedure and string regex queries share one worker slot per session. Input projections are materialized only after admission, so queued queries retain no per-query inventory arrays. Close cancels and drains matching. Native metadata queries also share one admission slot, including references; a failed stop retains admission until cleanup is proven.
Use `search_strings` for the matching rows or `list_strings` with an exact address for that location. The validated inventory is sorted by numeric address and encoding. Once cached, exact-address reads use binary search and return every encoding at that address without scanning the full inventory or retaining another lookup index. Literal and regex matching run in REA, preserving the existing case and ECMAScript Unicode semantics instead of substituting Kuna's different regex dialect. Procedure and string regex queries share one worker slot per session. Input projections are materialized only after admission, so queued queries retain no per-query inventory arrays. Close cancels and drains matching. Native metadata queries also share one admission slot, including references; a failed stop retains admission until cleanup is proven.
`raw_result.strings` reports the source JSON digest/size, scanned scope, extraction policy, complete inventory count and selected count without copying unrequested text into the answer. Omit the address from `list_strings` to retrieve the complete inventory as Evidence; normal session retention and bundle export apply, including transport-capacity errors for oversized inline replies. These operations preserve their complete-result contracts and do not add paging. Function-referenced string facets in `analyze_function` remain unavailable; call `xrefs` explicitly when references to one returned string are needed.
@@ -82,6 +84,24 @@ REA parses the graph as Kuna writes it, like the string scan, so the graph has n
Evidence includes only selected addresses or dossier neighbors, the producing JSON's SHA-256/byte count, coverage and relevant function facts; unrequested program-wide JSON and instruction text are omitted. Reference caches are in memory and expire on close: a fresh CLI process or MCP server rebuilds them. A first call query therefore costs a whole-program reference walk even when a C export is warm. These operations do not establish full Ghidra/IDA feature parity.
Failures from shared installation or inventory work identify the operation the
caller requested. `diagnostics.producer_operation` retains the internal producer
name when it differs. Caller-specific failures keep the producer's typed outcome,
captured output, partial facts and live cleanup dependencies; cleanup retry
updates every projection rather than preserving an independent stale marker.
Installation acquisition is shared, with independently cancellable caller waits.
The final departing caller stops the probe and joins its cleanup; session close
also stops and joins acquisition. A cleanly cancelled acquisition can be retried,
while a retained cleanup failure blocks replacement until its owner is closed.
Shared string and reference queries preserve caller cancellation reasons and
join the final departing caller's command cleanup. A command cleanup error keeps
its typed primary failure and captured output. Its live resource list follows
both process and workspace ownership: proving process exit does not prove that
the private workspace was removed, and retry updates earlier caller errors.
If workspace setup fails, an unsuccessful rollback retains the original target
or read failure together with its cleanup owner. Replacement stays blocked until
that rollback is resolved; a clean rollback permits another setup attempt.
## Provenance and limitations
- Addresses and bytes are Kuna's observations of the image. Procedure entries and extents are its derivations: extents are upper bounds clipped at the next entry, and Kuna can split one function at an entry its own analysis infers. Pseudocode is its reconstruction, never original source.
+39
View File
@@ -355,6 +355,17 @@ 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.
`get_call_graph` and `trace_call_path` report `forward` and `backward` modes.
Each requires `procedure_address` plus `procedure_callees` or `procedure_callers`,
respectively. The tool is available when either traversal mode is supported;
`default_mode_available` describes the default forward direction. Read each mode's
missing operations and remediation before selecting a direction. These facts
describe traversal prerequisites, not relationship completeness or a guarantee
for every selector. A trace whose start and goal resolve to the same address can
finish without reading relationships, even when neither traversal mode is
available. Discovery does not block that direct invocation or replace its typed
partial/error outcomes.
The `effects` and `annotations` arrays list the true members of their fixed
vocabularies. A known member omitted from either array is false. This applies
only to these closed flag sets; it does not make an omitted tool, provider, mode,
@@ -435,6 +446,9 @@ Provider calls receive the request cancellation signal. Artifact traversal,
hashing, version comparisons, Hopper requests, and process capture
check the same signal. Cancellation is distinct from timeout. A cleanup failure
uses `cleanup_incomplete` and lists only the owned resource kinds that remain.
Once those owners confirm cleanup, composed failures recover their original
typed primary outcome and recovery guidance. Historical composition diagnostics
remain in `details.cleanup_history`, separate from the primary diagnostics.
An atomic Evidence bundle or snapshot write can publish its complete output
before staging cleanup fails. In that case, `details.published: true`,
`details.published_path`, and `details.bytes` retain the publication receipt.
@@ -521,6 +535,12 @@ analysis runtime. These failures are reported at the operation that needs
them. A configured provider or an endpoint alone does not establish that a
target is supported.
Composite Hopper regex searches report failures against the accepted
`search_strings` or `search_procedures` operation. The underlying inventory
method and request ID remain in `details.diagnostics.provider_request`, with
the bridge error code and diagnostic type when available. Reattribution
preserves the failure class, captured output, and live cleanup ownership.
Evidence bundles, snapshots, and extraction use the paths and output behavior
declared by their tools. Artifact extraction materializes the selected regular
files into a fresh REA-chosen temporary directory and reports its path.
@@ -531,6 +551,25 @@ temporary source files are declared as `launchesProcess` and `writesFilesystem`,
including for a single JavaScript source. This internal analysis does not execute
the supplied application code.
## Static Ghidra bytes and source mappings
`read_bytes` returns initialized analysis memory, including loader fixups, rather
than live process memory. Check `returned_bytes` and `complete` before decoding:
reads stop at unmapped or uninitialized bytes. Missing bytes are unknown; do not
pad an incomplete read with zeros and treat the result as an observation.
`list_segments` reports memory-block initialization and address-space facts.
`address_to_file_offset` maps one address through the provider's source-byte
mapping. A successful start-address mapping does not establish that an entire
range is contiguous in the original file, or that its bytes equal analysis
memory after loader fixups. Uninitialized, missing, and ambiguous mappings fail
explicitly; preserve the reported failure rather than guessing an offset.
For constructor-populated pointer or resource slots, a static zero is not proof
of a later null value. Inspect the slot's `xrefs` and the writer's assembly or
pseudocode, retaining uncertainty about execution order and runtime values.
These operations inspect static evidence and do not execute the target.
## Ghidra first-query deadlines and recovery
A successful MCP initialize handshake establishes the REA connection.
+106 -11
View File
@@ -216,6 +216,24 @@ sites remain distinct. A static route does not establish runtime reachability.
Swift/closure dispatch requires resolved static references; unavailable ABI
forms remain unknown.
Incomplete reference kinds do not discard known typed calls. Callee fallback
adds inferred targets while each retained route keeps its original provider
and call site. If enumeration fails, known routes remain alongside an unresolved
failure; cancellation still terminates the request. A node-limit omission keeps
the route's known relation while marking its target unresolved. Distinct call
sites to the same omitted target retain separate evidence.
Session discovery describes two automatic source routes for this trace:
`authored_ui_dispatch` needs Interface Builder decoding and dispatch metadata;
`native_seed_call_trace` needs native address resolution, dispatch metadata and
a call reader. Metadata can come from direct inspection or `list_names`;
the call reader can supply typed `procedure_references` or inferred
`procedure_callees`. An authored UI-to-handler edge remains useful without
call expansion, and native-seed tracing can proceed without UI decoding.
These routes add no request option or default mode, so discovery omits
`default_mode_available`. Availability describes source prerequisites;
the result reports the query's actual matches, coverage and failures.
`trace_native_values` / `rea trace-native-values <binary> <procedure> --provider
ghidra` composes high-p-code def-use graphs across statically resolved calls.
Logical CALL arguments bind to recovered parameter ordinals; recovered RETURN
@@ -261,6 +279,23 @@ Mach-O fixtures check each case against source-owned return values.
## Static inspection
`inspect_plist.format` describes the selected source, not plutil's conversion
format. Sources explicitly identified as XML, binary or JSON retain those
labels. For generic text, a bounded source-prefix probe recognizes leading XML
markup, including UTF-8 and BOM-marked UTF-16 sources. Unidentified text and
OpenStep plists, including a data-only value such as `<deadbeef>`, report
`unknown` even when plutil decodes their values through an XML conversion.
A plist explicitly selected through the path input or as the active plist target
reports an unreadable or malformed selection as `invalid_request`. A missing or
unreadable default plist of another target reports `capability_unavailable`.
Permission failures remain `access_denied`; output and cleanup failures retain
their own error categories.
A plist decode refusal includes plutil's diagnostic from either output stream.
`details.captured_output` retains bounded stdout/stderr excerpts, observed byte
totals and exit status. `truncated: true` means those excerpts are incomplete.
`inspect_macho.selected_architecture` identifies the exact selected lipo slice
and its `container_file_offset` in the input file. A thin Mach-O has a known
offset of zero; an unknown universal-slice offset stays null. The Evidence
@@ -317,12 +352,22 @@ complete observation.
shared/cyclic references, class descriptors, raw serialized fields and
malformed/unresolved/nil references. Missing fields stay absent; unknown
classes are preserved. Select `root`, `offset` and `limit` explicitly.
Object `kind` describes the decoded archive value: data, dates and numbers
remain `scalar` when their JSON value uses a `$plist_type` wrapper. An actual
dictionary with the same fields remains `dictionary`.
XML must be well-formed. Data literals use
[CoreFoundation's raw ASCII quartet rules](https://github.com/swiftlang/swift-corelibs-foundation/blob/384ff680be7c7445fee6ce0c55e9ae11a46e07c5/Sources/CoreFoundation/CFPropertyList.c#L1588):
non-alphabet ASCII characters are ignored and an incomplete final quartet
emits no bytes. Character references retain their source spelling for data
decoding. Non-ASCII content, child markup and self-closing `<data/>` are format
failures; explicit `<data></data>` represents empty data.
UID 0 cannot distinguish conditional nil from ordinary nil. Limits are
64 MiB of input, 200,000 objects/references and 128 nesting levels.
XML dictionary entries decoded as `__proto__` are omitted with an explicit
count because REA results cannot represent that key. XML tag whitespace and
comments do not change that handling. Other values and the original archive
digest are preserved; key interpretation follows the pinned plist decoder.
comments do not change that handling. XML key names combine their text and
CDATA segments without trimming or expanding literal CDATA entities. Other
values and the original archive digest are preserved.
- `decode_interface_builder` / `rea decode-interface-builder <app>` reads
compiled storyboard and nib resources into objects, outlets, actions,
connections and controller/class names. A full app graph is often several
@@ -403,6 +448,12 @@ complete observation.
address is retained as `reported_address` in the symbol's evidence, whose
location also remains unknown. Nonempty provider address syntax is preserved.
Session discovery reports this workflow available when the bound provider
offers direct metadata inspection or a `list_names` symbol inventory. The
workflow tries direct metadata first and uses the symbol fallback only when
that operation is unavailable; runtime failures remain failures rather than
being replaced by symbol-only results.
## Native desktop observation
`observe_native_ui` captures one explicitly selected existing PID/window ID.
@@ -418,7 +469,11 @@ decrement scrolling and text-value entry, or bounded waits. No global event
injection is used. Unsupported AX actions fail explicitly. The result preserves
ordered before/after captures and gaps; an action may have occurred before a
post-action capture fails. Application state is left as-is; REA does not attempt
to restore it.
to restore it. Failed or cancelled scenarios return a typed error with the
collected report in `partial_observation`, including ordered step outcomes.
Cancellation preserves the caller's reason and remains observable through helper
cleanup. A later abort does not replace an earlier helper or output failure.
Cleanup failures retain their runtime owner until a confirmed cleanup retry.
Scenarios accept caller-selected action lists and accessibility node counts.
Individual waits cannot exceed the operation's 180-second deadline, and the
@@ -572,6 +627,12 @@ digests identify the original serialized bytes.
## Keyed archive integer precision
The archive reader observes XML number element types and binary number markers.
XML values and their number facts are interpreted together from the validated
DOM and its original scalar source spans. Hexadecimal `<integer>` text, including
an optional sign, retains its value;
`<real>` text and CDATA segments are combined without inferring text from nested
elements. Real character references expand outside CDATA; integer and date
literals use raw source text.
Integers beyond the exact range of a JSON number are reported with the original
decimal text as `{ "$plist_type": "integer", "decimal": "<exact digits>" }` when
that source value can be associated unambiguously. Integral real values remain
@@ -580,9 +641,9 @@ markers remain decoded with an explicit precision limitation. The limitation
also counts observed unsafe integer literals, including observations outside the
selected object page. Incomplete supplemental metadata produces an explicit
precision note without rejecting an archive accepted by the byte decoder.
Mixed XML number content (comments, CDATA, processing instructions or child
elements) and integer text the supplemental reader cannot classify disable exact
association and retain decoded numbers with that precision note. Nonnumeric
Malformed XML scalar content becomes an explicit unknown rather than a prefix
number, guessed timestamp, or tag-only boolean. These unknowns do not disable
exact association for unrelated observed numbers. Nonnumeric
`UID` and `CF$UID` dictionary values remain ordinary data and are projected
recursively; numeric reference markers retain their original representation.
@@ -603,11 +664,45 @@ decode budget reserves estimated room for number metadata and attribute
projection; these estimates are not a hard process-memory bound. Native
NIBArchive decoding is unchanged by this plist-specific representation.
Dates are reported as `{ "$plist_type": "date", "iso8601": "<timestamp>" }`. A
date the decoder cannot represent, such as an XML date with a year beyond 9999
or a negative year, is reported as `{ "$plist_type": "date", "iso8601": null }`
with a counted limitation; the rest of the plist or archive is still decoded.
`inspect_plist` and Interface Builder decoding use the same projection.
Dates are reported as `{ "$plist_type": "date", "iso8601": "<timestamp>" }`.
XML dates require a complete UTC spelling with fixed two-digit month, day and
time fields; short, negative and extended years are interpreted as UTC year
fields within JavaScript's date range. Unsupported calendar fields (including
leap seconds) and out-of-range dates remain explicit unknowns. REA does not
emulate CoreFoundation's normalization of overflowing calendar fields. Invalid
dates returned by other decoders retain `{ "$plist_type": "date", "iso8601": null }`
with a counted limitation. `inspect_plist` and Interface Builder decoding use
the same projection.
A negative-zero real is reported as `{ "$plist_type": "real", "value": "-0" }`
because a JSON number cannot carry the sign of zero. `inspect_plist` and
`inspect_keyed_archive` count these values in a limitation, and Interface
Builder node attributes use the same value. Positive zero and integer zero,
including an `<integer>-0</integer>` literal, remain the number `0`.
Known non-finite reals are reported as
`{ "$plist_type": "real", "value": "nan" | "+infinity" | "-infinity" }`.
These are canonical numeric labels, not a claim about original source spelling.
Binary IEEE reals retain NaN versus signed infinity; NaN payload bits and sign
are not represented in the JSON value, and the original bytes remain the source
for those details. XML CFPropertyList literals (`nan`, `+infinity`, `-infinity`,
`infinity`, `inf`, `+inf`, `-inf`, case-insensitively) decode to the same known
values. Case and aliases normalize; original archive bytes retain source text.
XML scalars whose identity cannot be established use
`{ "$plist_type": "unknown", "element": "integer" | "real" | "date" | "true" | "false", "literal": "<original content>", "reason": "invalid-literal" | "unsupported-markup" | "unrepresentable" }`.
A counted limitation accompanies these wrappers. Literal content retains its
original entity spelling, markup and line endings. Whole XML documents must
still pass strict well-formedness checks. Empty integer, real and date elements
are unknown scalars; boolean elements must be empty. Complete decimal/exponent
reals and signed decimal/hexadecimal integers accept their respective native
leading whitespace but reject trailing text. Arbitrary-precision integers
remain an intentional REA extension beyond CoreFoundation's integer range.
Finite real spellings that overflow the numeric representation remain unknown,
rather than becoming observed infinities. Known non-finite and unresolved scalar
values have separate counts. This replaces the former prefix-number and
incomplete-metadata fallback for malformed XML numeric content. Native
`inspect_plist` uses the same projection for plutil's XML conversion.
## Locate a byte signature
+5
View File
@@ -58,6 +58,11 @@ tools read that copy, which is hashed again after analysis. Capture metadata and
literal field values are producer observations; address/protocol counts and DNS
associations are derivations. Capture Evidence has historical-reference authority.
`capture_start` and `capture_end` are RFC 3339 UTC instants derived from
`capinfos -S` epoch seconds, retaining all producer fractional digits (up to
nanoseconds). Empty captures, malformed timestamps and instants outside years
0000–9999 remain `null`; host-local timestamps without a zone are not inferred.
Addresses count once per packet, including loopback and nested IP occurrences.
DNS addresses are associated only with one question in the same response packet;
additional records may be included, so this does not prove final resolution.
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · **العربية** · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA: هندسة عكسية لأي شيء
@@ -274,7 +274,7 @@ npx rea-agents@latest setup
## سجل النجوم
🎉 **250,000 نجمة على GitHub — شكرًا لكم!**
🎉 **300,000 نجمة على GitHub — شكرًا لكم!**
شكرًا لكل من يستخدم REA، ويبلّغ عن الأخطاء، ويقترح ميزات، ويختبر إصدارات البناء، ويساهم بالإصلاحات.
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · **Deutsch** · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA: Alles reverse-engineeren
@@ -272,7 +272,7 @@ Melde Sicherheitslücken gemäß [SECURITY.md](../../SECURITY.md).
## Sternverlauf
🎉 **250.000 GitHub-Sterne – vielen Dank!**
🎉 **300.000 GitHub-Sterne – vielen Dank!**
Danke an alle, die REA nutzen, Fehler melden, Funktionswünsche äußern, Builds testen und Fehlerbehebungen beitragen.
+2 -2
View File
@@ -1,6 +1,6 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md) · **Ελληνικά**
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md) · **Ελληνικά** · [മലയാളം](README_ml.md)
# REA: Reverse Engineer Anything
@@ -285,7 +285,7 @@ npx rea-agents@latest setup
## Ιστορικό αστεριών
🎉 **250.000 αστέρια στο GitHub — σας ευχαριστούμε!**
🎉 **300.000 αστέρια στο GitHub — σας ευχαριστούμε!**
Ευχαριστούμε όλους όσοι χρησιμοποιούν το REA, αναφέρουν σφάλματα, ζητούν νέες λειτουργίες, δοκιμάζουν builds και συνεισφέρουν διορθώσεις.
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · **Español** · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA: Ingeniería inversa de cualquier cosa
@@ -272,7 +272,7 @@ Informa de vulnerabilidades siguiendo [SECURITY.md](../../SECURITY.md).
## Historial de estrellas
🎉 **250.000 estrellas en GitHub: ¡gracias!**
🎉 **300.000 estrellas en GitHub: ¡gracias!**
Gracias a quienes usan REA, informan de errores, solicitan funciones, prueban compilaciones y contribuyen con correcciones.
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center" dir="rtl">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · **فارسی** · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA: مهندسی معکوس هر چیزی
@@ -257,7 +257,7 @@ npx rea-agents@latest setup
## تاریخچهٔ ستاره‌ها
🎉 **۲۵۰٬۰۰۰ ستارهٔ GitHub — سپاسگزاریم!**
🎉 **۳۰۰٬۰۰۰ ستارهٔ GitHub — سپاسگزاریم!**
از همهٔ کسانی که از REA استفاده می‌کنند، خطاها را گزارش می‌دهند، قابلیت‌های جدید پیشنهاد می‌کنند، buildها را آزمایش می‌کنند و در بهبود پروژه مشارکت دارند سپاسگزاریم.
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · **Français** · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA : rétro-ingénierie de tout
@@ -276,7 +276,7 @@ Signalez les vulnérabilités via [SECURITY.md](../../SECURITY.md).
## Historique des étoiles
🎉 **250 000 étoiles GitHub — merci !**
🎉 **300 000 étoiles GitHub — merci !**
Merci à toutes les personnes qui utilisent REA, signalent des bugs, proposent des fonctionnalités, testent les builds et contribuent des correctifs.
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · **Bahasa Indonesia** · [Italiano](README_it.md)
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA: Rekayasa Balik Apa Pun
@@ -297,7 +297,7 @@ Laporkan kerentanan melalui [SECURITY.md](../../SECURITY.md).
## Riwayat Bintang
🎉 **250.000 bintang GitHub, terima kasih!**
🎉 **300.000 bintang GitHub, terima kasih!**
Terima kasih kepada semua orang yang menggunakan REA, melaporkan bug, meminta fitur, menguji build, dan berkontribusi dalam perbaikan.
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · **Italiano**
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA: Reverse Engineer Anything
@@ -276,7 +276,7 @@ Segnala vulnerabilità tramite [SECURITY.md](../../SECURITY.md).
## Cronologia delle stelle
🎉 **250.000 stelle su GitHub — grazie!**
🎉 **300.000 stelle su GitHub — grazie!**
Grazie a tutti coloro che usano REA, segnalano bug, propongono funzionalità, testano le build e contribuiscono con correzioni.
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · **日本語** · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA:あらゆるものをリバースエンジニアリング
@@ -274,7 +274,7 @@ npx rea-agents@latest setup
## スター履歴
🎉 **GitHub スター 250,000 件、ありがとうございます!**
🎉 **GitHub スター 300,000 件、ありがとうございます!**
REA の利用、バグ報告、機能の要望、ビルドのテスト、修正への貢献に感謝します。
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · **한국어** · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA: 무엇이든 리버스 엔지니어링
@@ -283,7 +283,7 @@ npx rea-agents@latest setup
## 스타 기록
🎉 **GitHub 스타 250,000개, 감사합니다!**
🎉 **GitHub 스타 300,000개, 감사합니다!**
REA를 사용하고, 버그를 보고하고, 기능을 요청하고, 빌드를 테스트하고, 수정에 기여해 주신 모든 분께 감사드립니다.
+325
View File
@@ -0,0 +1,325 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md) · **മലയാളം**
# REA: എന്തും Reverse Engineer ചെയ്യുക
### Binaries, applications, runtime behavior എന്നിവയുടെ Reverse Engineering-നായി ഒരൊറ്റ MCP.
**നിങ്ങൾക്ക് ഇഷ്ടപ്പെട്ട ഒരു feature കണ്ടോ? അത് എങ്ങനെ പ്രവർത്തിക്കുന്നു എന്ന് binary തലത്തിലേക്ക് വരെ മനസ്സിലാക്കൂ.**
[![npm version](https://img.shields.io/npm/v/rea-agents?style=flat-square&color=cb3837)](https://www.npmjs.com/package/rea-agents)
[![MCP tool catalog](https://img.shields.io/badge/MCP-tool_catalog-5c4ee5?style=flat-square)](../mcp-contracts.md#generated-catalog)
[![Node.js requirements](https://img.shields.io/badge/Node.js-requirements-339933?style=flat-square&logo=nodedotjs&logoColor=white)](#what-you-can-analyze)
[![skills.sh](https://skills.sh/b/morluto/rea?style=flat-square)](https://skills.sh/morluto/rea/reverse-engineer-anything)
[![MIT license](https://img.shields.io/badge/license-MIT-f4c430?style=flat-square)](../../LICENSE)
[![Discord](https://img.shields.io/discord/1556595354999332884?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/GkcryMnJDM)
<a href="https://trendshift.io/repositories/82054?utm_source=repository-badge&amp;utm_medium=badge&amp;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/)**
[ദ്രുതാരംഭം](#ദ്രുതാരംഭം) · [REA എങ്ങനെ പ്രവർത്തിക്കുന്നു](#rea-എങ്ങനെ-പ്രവർത്തിക്കുന്നു) · [എന്തൊക്കെ വിശകലനം ചെയ്യാം](#എന്തൊക്കെ-വിശകലനം-ചെയ്യാം) · [ഉദാഹരണങ്ങൾ](#ഉദാഹരണങ്ങൾ) · [പതിവുചോദ്യങ്ങൾ](#പതിവുചോദ്യങ്ങൾ) · [ഡോക്യുമെന്റേഷൻ](#ഡോക്യുമെന്റേഷൻ)
<code>npx rea-agents setup</code>
</div>
> [!IMPORTANT]
> REA ലഭ്യമാക്കേണ്ടത് [morluto/rea](https://github.com/morluto/rea),
> [rea.tools](https://rea.tools/), അല്ലെങ്കിൽ [rea-agents npm package](https://www.npmjs.com/package/rea-agents) എന്നിവയിൽ നിന്നാണ്.
> ഈ ഔദ്യോഗിക ഉറവിടങ്ങളിലെ ഇൻസ്റ്റലേഷൻ, റിലീസ് ലിങ്കുകൾ പിന്തുടരുക. സമാനമായ
> repository പേരോ പകർത്തിയ README-യോ മാത്രം ഒരു download ഔദ്യോഗികമാണെന്ന് ഉറപ്പാക്കുന്നില്ല.
<div align="center">
<br />
<img src="../assets/rea-hopper-analysis.png" alt="ഒരു native binary പരിശോധിക്കുന്നതിനിടെ Hopper-ൽ REA അതിന്റെ analysis bridge ആരംഭിക്കുന്നു" width="1200" />
<br /><br />
<table aria-label="REA community">
<tr>
<td align="center" width="360">
<a href="https://discord.gg/GkcryMnJDM">
<img src="../assets/discord.svg" height="42" alt="Discord" /><br />
<strong>Reverse Engineering കമ്മ്യൂണിറ്റിയിൽ ചേരുക</strong>
</a><br />
<sub>Discord · ചോദ്യോത്തരങ്ങൾ · അനുഭവങ്ങളും കണ്ടെത്തലുകളും പങ്കിടുക</sub>
</td>
</tr>
</table>
<br />
</div>
---
നിങ്ങളുടെ സ്വന്തം product-ൽ ഉൾപ്പെടുത്താൻ ആഗ്രഹിക്കുന്ന ഒരു feature ഏതെങ്കിലും app-ൽ കണ്ടോ? REA ഉപയോഗിച്ച് അത് പരിശോധിക്കാൻ നിങ്ങളുടെ agent-നോട് ആവശ്യപ്പെടുക. source code ഇല്ലാതെയും അതിന് app പരിശോധിക്കാനും feature എങ്ങനെ പ്രവർത്തിക്കുന്നുവെന്ന് വിശദീകരിക്കാനും അതിനുള്ള തെളിവുകൾ കാണിക്കാനും നിങ്ങളുടെ project-നായി അതിന് സമാനമായൊരു പതിപ്പ് നിർമ്മിക്കാനും കഴിയും.
Native binaries, JavaScript, Electron apps, .NET assemblies, websites എന്നിവ പരിശോധിക്കുന്നതിനുള്ള tools-ലേക്ക് REA നിങ്ങളുടെ agent-നെ ബന്ധിപ്പിക്കുന്നു. ഇതേ tools നിങ്ങളുടെ terminal-ൽ നിന്നും ഉപയോഗിക്കാം. വിശകലനം local ആയി നടക്കുന്നു; ഓരോ നിഗമനത്തിനും പിന്നിലെ തെളിവുകളും പരിമിതികളും ഫലങ്ങളിൽ ഉൾപ്പെടുന്നു.
Setup നിങ്ങളുടെ agent-ിൽ REA രജിസ്റ്റർ ചെയ്യുകയും അനുയോജ്യമായ workflow നിർദ്ദേശങ്ങൾ ഇൻസ്റ്റാൾ ചെയ്യുകയും ചെയ്യുന്നു. Native analysis-നായി നിലവിലുള്ള Hopper, Ghidra, അല്ലെങ്കിൽ IDA installation ഉപയോഗിക്കാം; അനുമതി നൽകിയാൽ setup-ന് Hopper ഇൻസ്റ്റാൾ ചെയ്യാനും കഴിയും. Static JavaScript analysis-ന് native analysis engine ആവശ്യമില്ല.
> **[REA വെബ്സൈറ്റ് സന്ദർശിക്കുക](https://rea.tools/)** — setup നിർദ്ദേശങ്ങൾക്കും ചിത്രങ്ങളോടുകൂടിയ ഗൈഡുകൾക്കും യഥാർത്ഥ case study-കൾക്കും.
## ദ്രുതാരംഭം
Linux-ൽ Ghidra, JADX, firmware tool launches, static JavaScript/Electron analysis എന്നിവയ്ക്ക് process ownership പരിശോധിക്കാൻ REA-യുടെ `PATH`-ൽ procps-compatible `ps` ആവശ്യമാണ്. പരിഹാര നടപടികൾ `docs/installation.md`-യിലെ "Linux owned-process prerequisite" എന്ന വിഭാഗത്തിൽ ലഭ്യമാണ്.
### നിങ്ങളുടെ agent സജ്ജമാക്കുക
Node.js-ഉം npm-ഉം ഇൻസ്റ്റാൾ ചെയ്തശേഷം ഈ command പ്രവർത്തിപ്പിക്കുക:
```bash
npx rea-agents setup
```
നിങ്ങൾ ഉപയോഗിക്കുന്ന agents തിരഞ്ഞെടുക്കുക, നിർദ്ദേശിച്ച മാറ്റങ്ങൾ പരിശോധിക്കുക, തുടർന്ന് അവയ്ക്ക് അനുമതി നൽകുക. നിലവിലുള്ള configuration-ന്റെ backup-ുകൾ സൂക്ഷിച്ചുകൊണ്ട് setup, REA-യുടെ MCP server-ഉം അനുയോജ്യമായ workflow നിർദ്ദേശങ്ങളും ചേർക്കും. അതിനുശേഷം നിങ്ങളുടെ agent വീണ്ടും ആരംഭിക്കുക.
Claude Code, Codex, Cursor, Gemini CLI, Grok Build, കൂടാതെ [മറ്റ് agents](../installation.md#supported-agents) എന്നിവയെ setup പിന്തുണയ്ക്കുന്നു. provider configuration-നും manual MCP registration-നും [installation and setup](../installation.md) കാണുക.
### നിങ്ങളുടെ agent-നോട് ആവശ്യപ്പെടുക
```text
Notes app-ൽ search എങ്ങനെ പ്രവർത്തിക്കുന്നു എന്ന് മനസ്സിലാക്കി, അതിനുള്ള തെളിവുകൾ കാണിച്ചുതരികയും എന്റെ project-നായി സമാനമായൊരു feature നിർമ്മിക്കുകയും ചെയ്യുക.
```
`Notes` എന്നതിന് പകരം നിങ്ങൾ പരിശോധിക്കാൻ ആഗ്രഹിക്കുന്ന app-ിന്റെയും മനസ്സിലാക്കേണ്ട feature-ിന്റെയും പേരുകൾ നൽകുക.
### Terminal ഉപയോഗിക്കുക
Extract ചെയ്ത JavaScript/Electron app directory അല്ലെങ്കിൽ ASAR പരിശോധിക്കുക:
```bash
npx -y rea-agents@latest analyze-javascript-application /absolute/path/to/app --json
```
ഫലത്തിൽ modules, imports, Electron boundaries, അവയ്ക്കുള്ള തെളിവുകൾ എന്നിവ ഉൾപ്പെടും. `/absolute/path/to/app` എന്നതിന് പകരം നിങ്ങളുടെ target-ന്റെ path നൽകുക; Windows-ൽ ഉദാഹരണത്തിന് `"D:/apps/example"` ഉപയോഗിക്കാം.
സാധാരണ ഉപയോഗത്തിനായി `rea` command ഇൻസ്റ്റാൾ ചെയ്യാൻ:
```bash
npm install --global rea-agents
rea --help
```
Native analysis നടത്തുന്നതിന് മുമ്പ് ഒരു provider ക്രമീകരിക്കുക. Native commands, provider തിരഞ്ഞെടുക്കൽ, snapshots, scripting എന്നിവയ്ക്കായി [CLI and Evidence guide](../cli.md) കാണുക.
### REA അപ്‌ഡേറ്റ് ചെയ്യുക
REA-യിൽ പതിവായി മാറ്റങ്ങൾ വരുന്നു; പുതിയ releases-ൽ പല bug fixes-ഉം ഉൾപ്പെടുന്നു. നിങ്ങളുടെ installation ഏറ്റവും പുതിയ പതിപ്പിലേക്ക് അപ്‌ഡേറ്റ് ചെയ്യുക.
`npm install --global` അല്ലെങ്കിൽ `bun add --global` ഉപയോഗിച്ച് ഇൻസ്റ്റാൾ ചെയ്ത CLI-ക്കായി:
```bash
rea update
```
Bun-ന്റെ global updates-ന് Bun 1.2.17 അല്ലെങ്കിൽ അതിനുശേഷമുള്ള പതിപ്പ് ആവശ്യമാണ്. പഴയ Bun പതിപ്പാണെങ്കിൽ `rea update` പ്രവർത്തിപ്പിക്കുന്നതിന് മുമ്പ് Bun സ്വമേധയാ അപ്‌ഗ്രേഡ് ചെയ്യുക.
Agent registrations-ഉം skill-ഉം പുതുക്കാൻ, update കാണിക്കുന്ന setup command പ്രവർത്തിപ്പിക്കുക.
`npx` ഉപയോഗിക്കുന്നുവെങ്കിൽ, agent setup പുതുക്കാൻ:
```bash
npx rea-agents@latest setup
```
Setup-ൽ വരുത്തുന്ന മാറ്റങ്ങൾ പരിശോധിച്ച് നിങ്ങളുടെ agent വീണ്ടും ആരംഭിക്കുക. ഒറ്റത്തവണ മാത്രം ഉപയോഗിക്കുന്ന CLI commands-നായി command-ന് മുമ്പ് `npx rea-agents@latest` ഉപയോഗിക്കുക. Bun ഉപയോഗിക്കുമ്പോൾ `npx rea-agents@latest`-ന് പകരം `bunx rea-agents@latest` ഉപയോഗിക്കുക.
## REA എങ്ങനെ പ്രവർത്തിക്കുന്നു
Target പരിശോധിക്കാനും പ്രസക്തമായ code-ന്റെ പാത പിന്തുടരാനും നിങ്ങളുടെ agent MCP വഴി REA-യെ വിളിക്കുന്നു. REA കണ്ടെത്തലുകളും അവയ്ക്കുള്ള തെളിവുകളും തിരികെ നൽകുന്നു. തുടർന്ന് agent കൂടുതൽ ചോദ്യങ്ങൾ ചോദിക്കാനും പെരുമാറ്റം വിശദീകരിക്കാനും implementation എഴുതുകയും പരിശോധിക്കുകയും ചെയ്യാനും അവ ഉപയോഗിക്കുന്നു. CLI commands-ഉം ഇതേ workflows ഉപയോഗിക്കുന്നു.
![REA അന്വേഷണ പ്രവാഹം: നിങ്ങളുടെ agent ഒരു local target-നെക്കുറിച്ച് ചോദിക്കുന്നു; REA analysis tools ഉപയോഗിച്ച് അത് പരിശോധിക്കുകയും code-ന്റെ പാത പിന്തുടരുകയും ചെയ്യുന്നു; തിരികെ ലഭിക്കുന്ന code, references, അറിയാത്ത കാര്യങ്ങൾ എന്നിവ ഉപയോഗിച്ച് agent വിശദീകരിക്കുകയും implementation നടത്തുകയും പരിശോധിക്കുകയും ചെയ്യുന്നു.](../../website/public/assets/figures/rea-investigation-flow.svg)
[പൂർണ്ണ വലുപ്പത്തിലുള്ള ചിത്രം തുറക്കുക](../../website/public/assets/figures/rea-investigation-flow.svg).
<a id="current-status"></a>
## എന്തൊക്കെ വിശകലനം ചെയ്യാം
REA-യ്ക്ക് Node.js 22.x (>=22.19), 24.x (>=24.11), അല്ലെങ്കിൽ 26+ പതിപ്പും npm-ഉം ആവശ്യമാണ്. Target അനുസരിച്ച് അധിക tools-ഉം host support-ഉം വ്യത്യാസപ്പെടും:
| Target | REA നൽകുന്ന ഫലങ്ങൾ | ആവശ്യകതകളും ഗൈഡും |
| ---------------------- | ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| Native binaries | Pseudocode, assembly, strings, symbols, calls, references | Hopper, Ghidra അല്ലെങ്കിൽ IDA; [native analysis](../native-investigation.md) |
| Offline ELF layout | Sections, segments, യഥാർത്ഥ symbols/relocations, static mitigation candidates | Linux x64-ൽ caller നൽകുന്ന pwntools; [binary diagnostics](../binary-diagnostics.md) |
| Go binaries | ഉൾച്ചേർത്ത compiler version, modules, replacements, build settings | Built-in static ELF/PE/thin Mach-O പരിശോധന; Go installation ആവശ്യമില്ല |
| Rust binaries | ഉൾച്ചേർത്ത cargo-auditable crate പേരുകൾ, versions, sources, dependency edges | Built-in static ELF/PE/thin Mach-O പരിശോധന; [Rust binaries](../rust-binaries.md) |
| WASM artifact | Validation, sections, കൃത്യമായ import/export WAT രൂപങ്ങൾ, decoded WAT | Caller നൽകുന്ന WABT 1.0.42; `inspect_wasm_artifact` |
| EVM bytecode | Dispatch selectors, byte offsets, അനുമാനിച്ച arguments, mutability | Linux x64-ൽ local raw/hex carrier; [offline EVM guide](../evm-bytecode.md) |
| SQLite databases | Tables, columns, indexes, views, triggers, തിരഞ്ഞെടുത്ത സാധാരണ table rows | Local database/WAL snapshot; native SQLite പരിമിതികളോടെ Node.js 24.x >=24.15 അല്ലെങ്കിൽ 26+ |
| Recorded Linux crashes | Raw notes, രേഖപ്പെടുത്തിയ ഓരോ thread-ന്റെയും registers/signals, ഐച്ഛിക mapping candidates | Linux x64-ൽ caller നൽകുന്ന pwntools; ഐച്ഛിക GDB/pwndbg; [recorded crashes](../recorded-crashes.md) |
| JavaScript / Electron | Modules, imports, source maps, routes, IPC, native add-on ബന്ധങ്ങൾ | Node.js, npm; Linux: procps-compatible `ps`; [application analysis](../javascript-application-workflows.md) |
| Websites | Page structure, scripts, network observations, ആവശ്യപ്പെട്ട screenshots | Chrome-family browser; [browser analysis](../browser-observation.md) |
| Saved network captures | Requests, responses, പുറത്തുകാണുന്ന payload-ുകൾ, source locations | HAR അല്ലെങ്കിൽ Fiddler Everywhere SAZ; native mitmproxy-നായി Linux-ൽ mitmdump; [capture guide](../web-network-captures.md) |
| .NET assemblies | Metadata, CIL instructions, പ്രഖ്യാപിച്ച native dependencies, build comparisons | ഒന്നും ആവശ്യമില്ല (static മാത്രം); [managed-code guide](../managed-code-analysis.md) |
| Android APKs | Manifest declarations, classes, decompiled methods, references | Linux/macOS/Windows x64-ൽ Headless JADX-ഉം full JDK-ഉം; [Android guide](../android-analysis.md) |
| Android devices | Devices, packages, processes, logs, dumps, screen captures, APK/file transfers | Caller നൽകുന്ന adb (emulator അല്ലെങ്കിൽ hardware); [ADB guide](../adb-device-analysis.md) |
| JEB projects | Engine-backed project units, artifact digests, type/method pseudo-code | MCP സേവനം നൽകുന്ന caller ആരംഭിച്ച JEB client; REA_JEB_MCP_URL ഉപയോഗിച്ച് തിരഞ്ഞെടുക്കുക; [JEB guide](../jeb-analysis.md) |
| Android resources | Decoded manifest, string tables, locales, version വിവരങ്ങൾ | Caller നൽകുന്ന Apktool launcher; [resource guide](../apktool-resource-analysis.md) |
| Flutter apps | Dart snapshot hash, ഓരോ ABI-യിലെയും library digests, engine build provenance | ഒന്നും ആവശ്യമില്ല (static APK parsing); [Flutter guide](../flutter-analysis.md) |
| Firmware | Regions, extraction results, native-analysis handoffs | Linux-ൽ Binwalk / Unblob; [firmware guide](../firmware-analysis.md) |
| Recorded packets | തിരഞ്ഞെടുത്ത fields, ആവർത്തിക്കുന്ന values, filters, decode-as rules | Linux: TShark 4.x + prlimit; [capture guide](../packet-captures.md) |
| Packet summaries | Endpoints, DNS, TLS SNI, protocol distribution, HTTP requests | Caller നൽകുന്ന tshark, capinfos (Linux-ൽ പരിശോധിച്ചത്); [pcap guide](../pcap-analysis.md) |
| Recorded logic signals | Decoder annotations, sample ranges, diagnostics | Linux: sigrok-cli 0.7.2 + prlimit; [signal recordings](../signal-recordings.md) |
| Packages and resources | File inventories, digests, plists, Apple bundle ഘടന, extracted resources | [Artifact and JavaScript guide](../javascript-artifact-reconstruction.md), [Apple applications](../apple-application-analysis.md) |
| Process behavior | Terminal output, interactions, exit, filesystem observations, run comparisons | Native PTY ഉള്ള Linux/macOS; [process capture](../process-capture.md) |
Static JavaScript, .NET പരിശോധനകൾ application പ്രവർത്തിപ്പിക്കാതെ നൽകിയ files വായിച്ചാണ് നടത്തുന്നത്. Runtime capture, നിങ്ങളുടെ user permissions ഉപയോഗിച്ച് തിരഞ്ഞെടുത്ത target പ്രവർത്തിപ്പിക്കുകയോ അതുമായി ഇടപെടുകയോ ചെയ്യുന്നു; ഓരോ runtime guide-ലും അതിന്റെ പ്രത്യാഘാതങ്ങൾ വിശദീകരിച്ചിട്ടുണ്ട്.
Offline native-binary പരിശോധനയ്ക്കായി REA, caller നൽകുന്ന GNU objdump, Rizin എന്നിവയും പിന്തുണയ്ക്കുന്നു. Provider setup, CLI commands, Evidence-ന്റെ പരിമിതികൾ എന്നിവയ്ക്കായി [reverse-engineering tools guide](../reverse-engineering-tools.md) കാണുക. ഈ providers ഒന്നും REA ഇൻസ്റ്റാൾ ചെയ്യുകയോ അപ്‌ഡേറ്റ് ചെയ്യുകയോ ചെയ്യുന്നില്ല.
<a id="choosing-a-deep-analysis-provider"></a>
Native formats-ഉം host support-ഉം provider അനുസരിച്ച് വ്യത്യാസപ്പെടും. [Hopper and Ghidra setup](../installation.md#hopper), [IDA guide](../ida-provider.md) (ഐച്ഛിക native MCP bridge ഉൾപ്പെടെ), [Kuna decompiler guide](../kuna-provider.md), [പരീക്ഷണഘട്ടത്തിലുള്ള Windows Ghidra support](../windows-ghidra-p0.md) എന്നിവ കാണുക.
Ghidra, [16-bit DOS analysis](../ghidra-dos.md)-ഉം പിന്തുണയ്ക്കുന്നു.
വലിയ binaries-നായി `REA_GHIDRA_STARTUP_TIMEOUT_MS` അല്ലെങ്കിൽ `REA_HOPPER_STARTUP_TIMEOUT_MS` ഉപയോഗിച്ച് provider-ന്റെ startup deadline വർധിപ്പിക്കുക.
Provider തിരഞ്ഞെടുക്കാൻ [CLI guide](../cli.md#choose-a-provider) കാണുക.
ഏറ്റവും പുതിയ npm release-നുശേഷം ചേർത്ത features അറിയാൻ [release availability](../installation.md#released-package-and-main) പരിശോധിക്കുക.
## ഉദാഹരണങ്ങൾ
[![DX-Ball sound-pan, Notion clipboard-bridge, TH04 bullet-ring എന്നിവയുടെ ചിത്രീകരണങ്ങൾ](../assets/rea-showcases.png)](https://rea.tools/showcase/)
### DX-Ball: sound-pan കണക്കുകൂട്ടൽ പുനർനിർമ്മിക്കുക
ഒരു sound call-നെ അതിന്റെ position-to-pan helper വരെ പിന്തുടരുക, instructions പരിശോധിക്കുക, അപൂർണ്ണമായ pseudocode-നെ C-യാക്കി മാറ്റുക. ഈ പുനർനിർമ്മാണം യഥാർത്ഥ x86-ലെ 3,205 test cases വിജയിക്കുകയും compile ചെയ്ത function-ന്റെ 63 bytes-ഉം അതേപടി പുനരുത്പാദിപ്പിക്കുകയും ചെയ്യുന്നു.
[Case study വായിക്കുക](https://rea.tools/showcase/dx-ball/) ·
[പുനർനിർമ്മാണ repository](https://github.com/N0zoM1z0/dx-ball)
### Notion: Electron clipboard bridge പിന്തുടരുക
Renderer-ന്റെ clipboard API കണ്ടെത്തി, അതിനെ preload-ഉം IPC-ഉം വഴി main process വരെ പിന്തുടർന്ന് rich clipboard format പരിശോധിക്കുക.
[Case study വായിക്കുക](https://rea.tools/showcase/notion/)
### TH04: DOS bullet-ring കണക്കുകൂട്ടൽ വീണ്ടെടുക്കുക
യഥാർത്ഥ PC-98 game-ന്റെ 16-bit instructions പരിശോധിക്കുക, fixed, aimed angle കണക്കുകൂട്ടലുകൾ വീണ്ടെടുക്കുക, പുനർനിർമ്മിച്ച C++-നെ ചരിത്രപരമായ compiler output-ുമായി താരതമ്യം ചെയ്യുക.
[Case study വായിക്കുക](https://rea.tools/showcase/th04/) ·
[പുനർനിർമ്മാണ repository](https://github.com/N0zoM1z0/th04)
REA ഉപയോഗിച്ച് നിങ്ങൾ രസകരമായ എന്തെങ്കിലും പരിശോധിച്ചിട്ടുണ്ടെങ്കിൽ അത് കാണാൻ ഞങ്ങൾ ആഗ്രഹിക്കുന്നു. പരിശോധിച്ച target, നിങ്ങളുടെ ചോദ്യം, REA എങ്ങനെ സഹായിച്ചു, നിങ്ങൾ കണ്ടെത്തിയത് എന്നിവ ഉൾപ്പെടുത്തി [issue](https://github.com/morluto/rea/issues) അല്ലെങ്കിൽ [pull request](https://github.com/morluto/rea/pulls) വഴി നിങ്ങളുടെ case പങ്കിടുക.
## പതിവുചോദ്യങ്ങൾ
<details>
<summary><strong>ഏതൊക്കെ agents-ന് REA ഉപയോഗിക്കാം?</strong></summary>
Local MCP servers പിന്തുണയ്ക്കുന്ന ഏത് agent-നും REA ഉപയോഗിക്കാം. Setup, [പിന്തുണയ്ക്കുന്ന agents](../installation.md#supported-agents) ക്രമീകരിക്കുന്നു; മറ്റ് clients-ന് [manual MCP registration](../installation.md#mcp-registry) ഉപയോഗിക്കാം.
</details>
<details>
<summary><strong>Hopper, Ghidra അല്ലെങ്കിൽ IDA ആവശ്യമാണോ?</strong></summary>
ആഴത്തിലുള്ള native analysis-ന് ഇവയിൽ ഒന്ന് ആവശ്യമാണ്. Static JavaScript, .NET പരിശോധനകൾ native analysis engine ഇല്ലാതെയും പ്രവർത്തിക്കും. അനുമതി ലഭിച്ചാൽ setup-ന് Hopper ഇൻസ്റ്റാൾ ചെയ്യാം; Ghidra, IDA എന്നിവയ്ക്കായി നിങ്ങളുടെ നിലവിലുള്ള installations ഉപയോഗിക്കുന്നു. [Provider setup](../installation.md#hopper) കാണുക.
</details>
<details>
<summary><strong>Hopper ആദ്യം ആരംഭിക്കേണ്ടതുണ്ടോ?</strong></summary>
ഒരു operation-ന് ആവശ്യമാകുമ്പോൾ REA Hopper ആരംഭിക്കും. macOS-ൽ ആദ്യമായി പ്രവർത്തിപ്പിക്കുമ്പോൾ demo mode തിരഞ്ഞെടുക്കാനോ license activate ചെയ്യാനോ ആവശ്യപ്പെടുന്ന dialog പ്രത്യക്ഷപ്പെടാം. ആദ്യ CLI/MCP operation അല്ലെങ്കിൽ unattended job നടത്തുന്നതിന് മുമ്പ് അതേ user session-ൽ ആ prompt പൂർത്തിയാക്കുക. ആവശ്യമെങ്കിൽ Hopper സ്വമേധയാ തുറക്കുക. [Hopper startup and troubleshooting](../installation.md#launcher-paths-and-troubleshooting) കാണുക.
</details>
<details>
<summary><strong>skills.sh-ൽ നിന്ന് skill ഇൻസ്റ്റാൾ ചെയ്യുന്നത് എന്താണ് ചെയ്യുന്നത്?</strong></summary>
ഈ skill നിങ്ങളുടെ agent-ന് അന്വേഷണ നിർദ്ദേശങ്ങൾ നൽകുന്നു. REA-യുടെ MCP server രജിസ്റ്റർ ചെയ്യാനും അനുയോജ്യമായ നിർദ്ദേശങ്ങൾ ഇൻസ്റ്റാൾ ചെയ്യാനും `npx rea-agents setup` ഉപയോഗിക്കുക; തുടർന്ന് നിങ്ങളുടെ agent വീണ്ടും ആരംഭിക്കുക. [Skill-only installation](../installation.md#skill-only-installation) കാണുക.
</details>
<details>
<summary><strong>REA ഏത് തരത്തിലുള്ള code ആണ് നൽകുന്നത്?</strong></summary>
Native analysis pseudocode-ഉം assembly-ഉം നൽകുന്നു. JavaScript/Electron analysis modules-ഉം അവയുടെ ബന്ധങ്ങളും വീണ്ടെടുക്കുന്നു. ഈ കണ്ടെത്തലുകൾ ഉപയോഗിച്ച് നിങ്ങളുടെ agent implementation എഴുതുകയും പരിശോധിക്കുകയും ചെയ്യും; പ്രായോഗിക ഉദാഹരണങ്ങൾക്കായി [ഉദാഹരണങ്ങൾ](#showcases) കാണുക.
</details>
<details>
<summary><strong>REA എന്റെ app upload ചെയ്യുമോ?</strong></summary>
REA targets local ആയി വിശകലനം ചെയ്യുന്നു. Tool-ൽ നിന്നുള്ള ഫലങ്ങൾ നിങ്ങളുടെ agent-ന് ലഭിക്കും; അതിന്റെ model provider-ന് സ്വന്തമായ data policy ഉണ്ടായിരിക്കും.
</details>
<details>
<summary><strong>ഒരു bug നേരിട്ടാൽ എന്തുചെയ്യണം?</strong></summary>
ആദ്യം അപ്‌ഡേറ്റ് ചെയ്യുക; പുതിയ release-ൽ ആ പ്രശ്നത്തിനുള്ള പരിഹാരം ഇതിനകം ഉണ്ടായേക്കാം.
`npm install --global` അല്ലെങ്കിൽ `bun add --global` ഉപയോഗിച്ച് ഇൻസ്റ്റാൾ ചെയ്ത CLI-ക്കായി:
```bash
rea update
```
`npx` വഴി agent setup നടത്തുന്നതിനായി:
```bash
npx rea-agents@latest setup
```
നിങ്ങൾ agent ഉപയോഗിക്കുന്നുണ്ടെങ്കിൽ, [setup പുതുക്കൽ](#update-rea) പൂർത്തിയാക്കി അത് വീണ്ടും ആരംഭിക്കുക. അതേ task വീണ്ടും ശ്രമിക്കുക. പ്രശ്നം തുടരുകയാണെങ്കിൽ, നിങ്ങളുടെ REA version, target type, പ്രശ്നം ആവർത്തിക്കാൻ ആവശ്യമായ ഘട്ടങ്ങൾ, error output എന്നിവ ഉൾപ്പെടുത്തി [issue തുറക്കുക](https://github.com/morluto/rea/issues).
</details>
## ഡോക്യുമെന്റേഷൻ
വെബ്സൈറ്റിലെ [പ്രായോഗിക ഗൈഡുകൾ](https://rea.tools/guides/) ഉപയോഗിച്ച് തുടങ്ങുക.
കൃത്യമായ options, prerequisites, result contracts എന്നിവയ്ക്കായി:
- [Installation and setup](../installation.md): agent registration, provider configuration, updates, uninstall.
- [Readiness and troubleshooting](../installation.md#check-readiness-for-your-task): ഒരു agent-ന്റെയോ analysis engine-ന്റെയോ പ്രശ്നങ്ങൾ കണ്ടെത്തുക.
- [CLI and Evidence](../cli.md): commands, provider തിരഞ്ഞെടുക്കൽ, snapshots, import/export, exit statuses.
- [MCP contracts](../mcp-contracts.md), [agent prompts](../mcp-prompts.md): tool results, sessions, guided investigations.
- [Tool catalog](../mcp-contracts.md#generated-catalog): tools, providers, CLI commands എന്നിവയുടെ സ്വയമേവ സൃഷ്ടിച്ച പട്ടിക.
- [Roadmap](../roadmap.md): ആസൂത്രണം ചെയ്ത പ്രവർത്തനങ്ങളും capability trackers-ഉം.
സുരക്ഷാ ദുർബലതകൾ [SECURITY.md](../../SECURITY.md) വഴി റിപ്പോർട്ട് ചെയ്യുക.
## Star ചരിത്രം
🎉 **250,000 GitHub stars — നന്ദി!**
REA ഉപയോഗിക്കുന്നവർക്കും, bugs റിപ്പോർട്ട് ചെയ്യുന്നവർക്കും, features ആവശ്യപ്പെടുന്നവർക്കും, builds പരിശോധിക്കുന്നവർക്കും, fixes സംഭാവന ചെയ്യുന്നവർക്കും നന്ദി.
<a href="https://www.star-history.com/?repos=morluto%2Frea&amp;type=date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=morluto/rea&amp;type=date&amp;theme=dark&amp;legend=top-left" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=morluto/rea&amp;type=date" />
<img alt="REA GitHub star ചരിത്രം" src="https://api.star-history.com/chart?repos=morluto/rea&amp;type=date" />
</picture>
</a>
## നിരാകരണം
ഉത്തരവാദിത്തത്തോടെ ഉപയോഗിക്കുന്നതിനുള്ള മാർഗനിർദ്ദേശങ്ങൾക്ക് [DISCLAIMER.md](../../DISCLAIMER.md) കാണുക.
നിയമവിരുദ്ധമോ അനുമതിയില്ലാത്തതോ ആയ ഉപയോഗത്തെക്കുറിച്ചുള്ള project-ന്റെ നിലപാട് എല്ലാ REA versions-നും distribution channels-നും platforms-നും ബാധകമാണ്; ഓരോ copy-യും അതിന് ബാധകമായ licenses നിലനിർത്തുന്നു.
നിയമാനുസൃതമായ research, education, interoperability, software preservation, കൂടാതെ നിങ്ങൾക്ക് സ്വന്തമായതോ പരിശോധിക്കാൻ അനുമതിയുള്ളതോ ആയ software-ന്റെ analysis എന്നിവയ്ക്കാണ് REA ഉദ്ദേശിച്ചിരിക്കുന്നത്.
## സംഭാവനകൾ
REA-യ്ക്കായി നിങ്ങളുടെ സഹായം ഞങ്ങൾ സ്വാഗതം ചെയ്യുന്നു! bug റിപ്പോർട്ട് ചെയ്യാനോ feature നിർദ്ദേശിക്കാനോ [issue തുറക്കുക](https://github.com/morluto/rea/issues), code അല്ലെങ്കിൽ docs മെച്ചപ്പെടുത്താൻ [pull request അയയ്ക്കുക](https://github.com/morluto/rea/pulls).
Development setup-നും checks-നും [CONTRIBUTING.md](../../CONTRIBUTING.md), verification lanes-നായി [testing](../testing.md), project ഘടനയ്ക്കായി [architecture map](../architecture.mermaid) എന്നിവ കാണുക.
## ലൈസൻസ്
[MIT](../../LICENSE)
REA-യുടെ രചയിതാക്കൾ സൃഷ്ടിച്ച code MIT license പ്രകാരമാണ് ലൈസൻസ് ചെയ്തിരിക്കുന്നത്. Third-party material അതിന്റെ സ്വന്തം licenses-ഉം copyright notices-ഉം നിലനിർത്തുന്നു; dependencies-ന്റെ പട്ടിക, നിലനിർത്തിയ license text-ുകൾ, external tool നിബന്ധനകൾ എന്നിവയ്ക്കായി [Third-party notices](../../THIRD_PARTY_NOTICES.md) കാണുക.
**IDA Pro / Hex-Rays decompilers, Hopper, JEB എന്നിവ ഉൾപ്പെടെയുള്ള commercial tools നിയമാനുസൃതമായി സ്വന്തമാക്കുകയും അവയുടെ ബാധകമായ licenses പ്രകാരം ഉപയോഗിക്കാൻ അനുമതിയുണ്ടെന്ന് ഉറപ്പാക്കുകയും വേണം.** REA ഒരു tool license-ഓ key-യോ activation entitlement-ഓ നൽകുന്നില്ല. Trials-ഉം demos-ഉം ഉൾപ്പെടെ vendor നിബന്ധനകൾക്ക് വിധേയമാണ്; ഇതിൽ user, feature, automation, output sharing നിയന്ത്രണങ്ങളും ഉൾപ്പെടാം. Cracked copies, മോഷ്ടിച്ച keys, അനുമതിയില്ലാത്ത key sharing, license നിയന്ത്രണങ്ങൾ മറികടക്കൽ എന്നിവയ്ക്ക് അനുമതിയില്ല. ഈ മാർഗനിർദ്ദേശം MIT അല്ലെങ്കിൽ third-party licenses-ൽ മാറ്റം വരുത്തുന്നില്ല.
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · **Polski** · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA: Reverse Engineer Anything
@@ -272,7 +272,7 @@ Zgłaszaj podatności zgodnie z [SECURITY.md](../../SECURITY.md).
## Historia gwiazdek
🎉 **250 000 gwiazdek na GitHubie — dziękujemy!**
🎉 **300 000 gwiazdek na GitHubie — dziękujemy!**
Dziękujemy wszystkim, którzy używają REA, zgłaszają błędy, proponują nowe funkcje, testują kompilacje i przesyłają poprawki.
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · **Português (Brasil)** · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA: Faça engenharia reversa de qualquer coisa
@@ -272,7 +272,7 @@ Relate vulnerabilidades conforme [SECURITY.md](../../SECURITY.md).
## Histórico de estrelas
🎉 **250.000 estrelas no GitHub — muito obrigado!**
🎉 **300.000 estrelas no GitHub — muito obrigado!**
Obrigado a todos que usam o REA, relatam bugs, sugerem funcionalidades, testam builds e contribuem com correções.
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · **Русский** · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA: Реверс-инжиниринг чего угодно
@@ -272,7 +272,7 @@ npx rea-agents@latest setup
## История звёзд
🎉 **250 000 звёзд на GitHub — спасибо!**
🎉 **300 000 звёзд на GitHub — спасибо!**
Спасибо всем, кто использует REA, сообщает об ошибках, предлагает новые функции, тестирует сборки и помогает с исправлениями.
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · **ไทย** · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA: Reverse Engineer Anything
@@ -272,7 +272,7 @@ npx rea-agents@latest setup
## ประวัติดาว
🎉 **250,000 ดาวบน GitHub — ขอบคุณทุกคน!**
🎉 **300,000 ดาวบน GitHub — ขอบคุณทุกคน!**
ขอบคุณทุกคนที่ใช้ REA รายงานบั๊ก เสนอฟีเจอร์ ทดสอบบิลด์ และช่วยแก้ไข
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · **Türkçe** · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA: Her şeyi tersine mühendislikle inceleyin
@@ -277,7 +277,7 @@ Güvenlik açıklarını [SECURITY.md](../../SECURITY.md) üzerinden bildirin.
## Yıldız geçmişi
🎉 **GitHub'da 250.000 yıldız — teşekkürler!**
🎉 **GitHub'da 300.000 yıldız — teşekkürler!**
REA'yı kullanan, hata bildiren, özellik talep eden, derlemeleri test eden ve düzeltmelere katkıda bulunan herkese teşekkürler.
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · **Українська** · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA: Реверс-інжиніринг будь-чого
@@ -272,7 +272,7 @@ npx rea-agents@latest setup
## Історія зірок
🎉 **250 000 зірок на GitHub — дякуємо!**
🎉 **300 000 зірок на GitHub — дякуємо!**
Дякуємо всім, хто використовує REA, повідомляє про помилки, пропонує нові функції, тестує збірки й допомагає з виправленнями.
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · **Tiếng Việt** · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA: Kỹ thuật dịch ngược cho mọi thứ
@@ -272,7 +272,7 @@ Báo cáo lỗ hổng theo [SECURITY.md](../../SECURITY.md).
## Lịch sử sao
🎉 **250.000 sao trên GitHub — xin cảm ơn!**
🎉 **300.000 sao trên GitHub — xin cảm ơn!**
Cảm ơn mọi người đã sử dụng REA, báo lỗi, đề xuất tính năng, kiểm thử bản dựng và đóng góp bản sửa lỗi.
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · [简体中文](README_zh.md) · **繁體中文** · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA:逆向一切
@@ -278,7 +278,7 @@ npx rea-agents@latest setup
## Star 歷史
🎉 **GitHub Star 達到 250,000 個,感謝大家!**
🎉 **GitHub Star 達到 300,000 個,感謝大家!**
感謝每一位使用 REA、報告錯誤、提出功能需求、測試建置和貢獻修復的朋友。
+2 -2
View File
@@ -1,7 +1,7 @@
<div align="center">
[English](../../README.md) · **简体中文** · [繁體中文](README_zh-TW.md) · [日本語](README_ja.md) · [한국어](README_ko.md) · [Türkçe](README_tr.md) · [Русский](README_ru.md) · [Tiếng Việt](README_vi.md) · [ไทย](README_th.md) · [Deutsch](README_de.md) · [Español](README_es.md) · [Français](README_fr.md) · [Українська](README_uk.md) · [Polski](README_pl.md) · [Português (Brasil)](README_pt-BR.md) · [العربية](README_ar.md) · [فارسی](README_fa.md) · [Bahasa Indonesia](README_id.md) · [Italiano](README_it.md)
· [Ελληνικά](README_el.md)
· [Ελληνικά](README_el.md) · [മലയാളം](README_ml.md)
# REA:逆向一切
@@ -278,7 +278,7 @@ npx rea-agents@latest setup
## Star 历史
🎉 **GitHub Star 达到 250,000 个,感谢大家!**
🎉 **GitHub Star 达到 300,000 个,感谢大家!**
感谢每一位使用 REA、报告错误、提出功能需求、测试构建和贡献修复的朋友。
+18 -4
View File
@@ -61,9 +61,21 @@ The outer provider owns both processes and one stable core snapshot. An owned
Python launcher lowers inherited soft limits before replacing itself with GDB.
GDB starts without initfiles, auto-load, debuginfod or automatic shared-library
loading. Owned empty library search paths and caches are used, with plugin
updates disabled. Fixed package code verifies a core connection and no paired
executable before querying mapping candidates. No caller GDB command language is
exposed. The exact observed GDB/plugin version strings are retained.
updates disabled. Debug-file lookup is also restricted to the owned empty
search directory. GDB 16 can otherwise locate the recorded executable independently
of these settings. While opening the core, fixed package code temporarily selects
`/proc/self/exe` (the already running debugger) with `exec-file`, without loading
symbols or running an inferior. This occupies GDB's executable slot and prevents
its automatic executable discovery. The validated decoder profile selects the
core architecture explicitly, so the placeholder cannot select it instead. The
bridge discards that selection with `file` before querying mapping candidates,
and verifies the core connection and absence of a main symbol file. Before launching GDB, the provider also checks
source-bound `NT_FILE` descriptors: mapping names must be absolute Unix paths
without dot segments. Relative names bypass GDB's sysroot, and traversal can
escape it. Unresolved or unsafe mapping coverage refuses debugger enrichment
while retaining the basic decoded observation and original bytes. No caller GDB
command language is exposed. The exact observed GDB/plugin version strings are
retained.
Mapping names are producer display metadata, with unknown current file identity.
Map permissions are derived from reported flags; zero/unfamiliar flags leave
@@ -93,4 +105,6 @@ GDB/Python/pwndbg/strace. Fixture generation explicitly launches its owned test
program; subsequent inspection only reads the resulting core. Real acceptance
checks recorded threads, high registers, missing/malformed notes, opaque owners,
CLI/MCP parity, optional debugger context and historical PID collision with an
owned live sentinel. Original inputs and host configuration remain unchanged.
owned live sentinel. The syscall trace also rejects access to the still-present
source executable. The CI workflow defines Ubuntu 24.04 and Debian 13 (GDB 16)
container lanes. Original inputs and host configuration remain unchanged.
File diff suppressed because it is too large. Load diff
+112
View File
@@ -0,0 +1,112 @@
{
"schemaVersion": 1,
"repository": "morluto/rea",
"version": "6.7.0",
"base": "2c4338db1fdb7773e05376301c558cff2a0b4b80",
"target": "b83b9caf4a5f0241a5fd370792f1aed1bffbb2cf",
"commits": [
{
"sha": "8cbaf6c7021b97dd4de6b5771c35fbed06b240e9",
"subject": "fix(tests): isolate command finalization module mocks",
"body": "fix(tests): isolate command finalization module mocks\n\nMove the five registered WASM finalization cases into the existing per-file isolated project. Preserve all result, exit-code, observation and cleanup assertions. A preloaded shared CLI graph reproduces three failures before the move; the isolated covered packet passes.\n\nFixes #4959\n",
"parents": ["2c4338db1fdb7773e05376301c558cff2a0b4b80"],
"authors": [
{
"name": "morluto",
"email": "76467478+morluto@users.noreply.github.com",
"user": {
"login": "morluto",
"__typename": "User"
}
}
],
"pullRequests": []
},
{
"sha": "6b180783ed04ebc87548c8385c80e05725804c47",
"subject": "fix(tests): budget CLI coverage from parsed options",
"body": "fix(tests): budget CLI coverage from parsed options\n\nUse the pinned Vitest parser for coverage and shard flags. Keep ordinary CLI workers at 384 MiB and covered workers at the tested 512 MiB with unchanged concurrency. Retain complete coverage and add actual-config regressions for false, provider-only and spaced-shard options.\n\nValidation: 27 focused cases; 16 covered cases with CLI preloaded; 5 docs checks; typecheck/lint; exact discovery of 1578 files. Measured covered control: 22.18s elapsed, 46.52s CPU, about 2.33 GiB sampled summed process-family RSS on macOS arm64. Full Linux CI pending. No runtime/device verification claimed.\n\nFixes #4960\n",
"parents": ["8cbaf6c7021b97dd4de6b5771c35fbed06b240e9"],
"authors": [
{
"name": "morluto",
"email": "76467478+morluto@users.noreply.github.com",
"user": {
"login": "morluto",
"__typename": "User"
}
}
],
"pullRequests": []
},
{
"sha": "bb212ffecb47e129cc569b9867b2b6e7c9b65685",
"subject": "fix(tests): inherit server identity deadline policy",
"body": "fix(tests): inherit server identity deadline policy\n\nRemove per-case ten-second overrides from MCP catalog and identity checks. Keep the suite-owned bounded ordinary and coverage deadlines, all assertions, and session cleanup.\n\nFixes #4967\n",
"parents": ["6b180783ed04ebc87548c8385c80e05725804c47"],
"authors": [
{
"name": "morluto",
"email": "76467478+morluto@users.noreply.github.com",
"user": {
"login": "morluto",
"__typename": "User"
}
}
],
"pullRequests": []
},
{
"sha": "b83b9caf4a5f0241a5fd370792f1aed1bffbb2cf",
"subject": "fix(setup): edit the duplicate JSON member that clients read (#4957)",
"body": "fix(setup): edit the duplicate JSON member that clients read (#4957)\n\nJSON.parse, JSONC parse, and native Claude Code keep the last duplicate\nobject member, and parseClientConfiguration therefore validates the last\none. The shared setup/uninstall serializer instead edited text through\njsonc-parser's modify and findNodeAtLocation, which select the first\nmatching member, so setup and uninstall on configurations with repeated\nserver tables or rea members wrote into a shadowed member and failed\nreadback.\n\nLocate every JSON text edit by the effective (last) member in one place,\nreplacing the first-member modify path. Removing an effective member\nthat shadows an earlier duplicate would activate that duplicate, whatever\nits ownership, so the serializer now returns a tagged shadowed_member\nfailure and setup and uninstall refuse before writing a backup or the\nconfiguration.\n\nFixes #4952\n\nCo-authored-by: Claude Opus 5.5 <noreply@anthropic.com>",
"parents": ["bb212ffecb47e129cc569b9867b2b6e7c9b65685"],
"authors": [
{
"name": "Kaoru0822",
"email": "kanadekitauji@proton.me",
"user": {
"login": "kaoru0822-kitauji",
"__typename": "User"
}
},
{
"name": "Claude Opus 5.5",
"email": "noreply@anthropic.com",
"user": {
"login": "claude",
"__typename": "User"
}
}
],
"pullRequests": [4957]
}
],
"pullRequests": [
{
"number": 4957,
"title": "fix(setup): edit the duplicate JSON member that clients read",
"url": "https://github.com/morluto/rea/pull/4957",
"author": {
"login": "kaoru0822-kitauji",
"__typename": "User"
},
"mergeCommit": {
"oid": "b83b9caf4a5f0241a5fd370792f1aed1bffbb2cf"
},
"issues": [
{
"number": 4952,
"url": "https://github.com/morluto/rea/issues/4952",
"author": {
"login": "xu91102",
"__typename": "User"
},
"repository": {
"nameWithOwner": "morluto/rea"
}
}
]
}
]
}
+6 -1
View File
@@ -73,7 +73,12 @@ invocation. A signed status response also confirms completion of older bridge
connections after its UI dispatch, preserving recovery proof for a caller whose
receipt another caller has acknowledged. Neither receipt recovery nor a status
probe retries the original command. Failed acknowledgements remain pending and
block new commands until they can be retried successfully.
block new commands until they can be retried successfully. Status proof failures
identify `cutter_command` and retain `status` in
`details.diagnostics.auxiliary_operation`; malformed proof still blocks dispatch.
Descriptor failures likewise retain the requesting operation through cleanup.
Command admission reports typed cancellation and treats the caller's abort reason
as opaque metadata, without inferring cleanup ownership from it.
## Configuration and coverage
+4 -1
View File
@@ -57,7 +57,10 @@ triggers or arbitrary SQL.
Rows are optional. `table` selects one exact ordinary-table name. `columns`
(CLI repeated `--column`) selects exact visible column names in the requested
order; omission selects all visible columns, including generated columns.
Unselected cells are not evaluated or projected. Column selection requires a
Unselected cells are not evaluated or projected. Malformed column-name bytes
cause a format refusal when all columns are requested. In inventory scope, an explicit valid subset can
still be sampled without decoding unrelated malformed column names.
Column selection requires a
table and at least one name; empty-string identifiers are valid. `row_limit`
requires `table`, defaults to 20 and accepts positive safe integers. Samples
remain subject to the byte and worker resource limits below, regardless of row count. The result reports
+60 -10
View File
@@ -110,9 +110,14 @@ rendered declarations. Keep canonical rejection checks separate from the
intentionally reduced compact advertisement, and probe actual client renderers
when changing schema representations.
The reused CLI boundary workers cap V8 old space at 384 MiB, based on their
profiled allocation and cleanup behavior. This encourages collection between
files; spawned REA processes retain their own heap settings.
The reused CLI boundary workers cap V8 old space at 384 MiB for ordinary runs
and 512 MiB with coverage enabled. Covered ADB fixtures exhaust the ordinary
384 MiB budget; those fixtures also complete with the shared CLI graph preloaded
at 512 MiB. This is a measured capacity control, not a suite-wide speedup claim.
Both profiles keep the
same bounded worker count, and spawned REA processes retain their own heap
settings. Explicitly disabling coverage retains the ordinary profile; coverage
provider options alone do not enable instrumentation.
Vitest worker limits do not bound subprocesses launched within a test. Bound
those batches separately so repeated CLI validation cannot oversubscribe the
@@ -216,6 +221,12 @@ controlled production seam for interleavings that cannot be reproduced reliably.
| Cleanup fails after success or primary failure | Public output retains the completed observation or primary diagnostic, remaining resources, and owner recovery action. Restore the denied resource and verify the owning cleanup retry. |
| Target exits without an acknowledgement, or transport disappears while it stays alive | Positive owned exit proof resolves target lifetime; other resources still need cleanup. Lost transport alone does not prove exit. Verify unrelated/attached targets survive and same-connection recovery works when supported. |
Shared process-supervision controls separate leader exit from output closure.
Cover output during held or failed cleanup, bounded retention and drain timeout,
retry, and borrowed stream authority. The POSIX inherited-stderr control uses a
separate pipe as a release barrier after leader exit; it does not establish
native Windows Job Object behavior.
Synchronize cancellation with the finalization boundary being tested; a fixed
delay after dispatch does not establish that phase. Use markers, barriers, or
owned cleanup seams while preserving real cleanup where feasible. Check server
@@ -225,6 +236,21 @@ cover cancellation during final cleanup.
Use explicit abort reasons and assert their identity at admission and after
provider completion, alongside retained observations and cleanup state.
Electron fuse regressions cover actual descriptor close denial and retry through
the built CLI, same-connection MCP recovery, mixed descriptor outcomes, and
server shutdown. Controlled seams cover cancellation during final close and
after reader finalization, plus active-provider shutdown during preflight.
They preserve real filesystem cleanup where possible; mocked launch boundaries
do not establish real Electron teardown or naturally occurring OS failures.
Active Electron cleanup controls retain independent transport and process-family
owners, block replacement captures until recovery, and retry only unresolved
owners. Cover late close acknowledgement and rejection after a wait deadline,
opaque rejection reasons, cancellation during failed finalization, and missing
lineage followed by a confirmed passive family census. Windows controls require
a fresh lineage observation and do not signal cached descendant PIDs after root
termination. Simulated Windows controls do not establish native Windows behavior.
For atomic writes, exercise cancellation on both sides of the documented commit
point and preserve any publication receipt. A late interruption does not prove
that no output was published.
@@ -329,6 +355,11 @@ The process-global Vitest configuration contract rejects new direct temporary-ro
creation outside the workspace seam and its narrowly documented boundary/package
exceptions.
The shared temporary-directory and workspace helpers preserve caller labels
under an owned `rea-` prefix when needed. `npm run verify:test-temp-hygiene`
uses the same ownership rule to find retained fixtures under a private temporary
root. Third-party caches remain outside that ownership rule.
Real Hopper, Ghidra, IDA, browser, package, and managed-code claims belong to their
explicit `npm run verify:*` lanes. The reconstruction-readiness lane also
checks deterministic rerun, tamper, and stale-input handling; those checks do
@@ -503,7 +534,7 @@ costs. Its queue, execution, and failures are independent of the required gate
and publication. CPU time and peak process-family memory are not measured by this API.
The build job saves portable Turbo outputs across runs. Eight Linux real-system
lanes restore this cache without waiting for the build job. The shared action
lanes reuse its current-run artifact; standalone dispatches restore the cross-run cache. The shared action
derives its key from `test:prepare --dry=json` task hashes, runner OS and
architecture, `.npmrc`, `.nvmrc`, and the lockfile; test-only changes can reuse compiled outputs. Every
build and verification command still runs, so Turbo checks task inputs and
@@ -513,7 +544,15 @@ that generator's entry. Native outputs are not stored in this cache.
Native package jobs, the Windows curated lane, and the Apple artifact lane reuse
the same workflow's Turbo cache for portable compiled JavaScript, skills, and
catalogs. The Windows lane relies on the Linux static job for typechecking. They retain the normal build and prepack
catalogs. The key parser retains the validated `test:prepare` task identities in
`.cache/ci-build-plan.json`; staging reads this plan without starting another
Turbo run that could evict cache entries. The producer stages matching archives,
metadata, and available manifests into `.cache/ci-build-cache/`.
`portable-build-cache.json` records availability per task. Evicted or partially
missing entries are omitted as whole entries, and consumers rebuild cache misses,
including when every entry is unavailable. Malformed plans, non-file entries,
and I/O errors other than disappearance still fail publication. Older cross-run entries stay in the
original cache. The Windows lane relies on the Linux static job for typechecking. They retain the normal build and prepack
commands, which restore matching outputs or rebuild on a cache miss. Native
artifacts, dependency installs, package archives, and runtime verification remain
host-local. Keep platform-dependent outputs out of this shared cache.
@@ -712,11 +751,12 @@ See [Android analysis](android-analysis.md) for boundaries and resource budgets.
`verify:jeb` requires a caller-started JEB client serving MCP at
`REA_JEB_MCP_URL` (default `http://127.0.0.1:8425/mcp`) with a project
already open, and verifies real CLI client inspection, unit listing coverage,
already open, and exercises shared application/provider client inspection, unit listing coverage,
and method decompilation. The script records the engine's exact response to
`open_jeb_project`; JEB 5.48.0 headless instances do not advertise that tool.
`open_jeb_project`; the earlier licensed 5.48.0 headless verification did not
advertise that tool.
REA does not install or launch JEB; see [JEB analysis](jeb-analysis.md) for
the bring-your-own boundary. Verified against JEB 5.48.0 serving
the bring-your-own boundary. The earlier licensed build was verified against JEB 5.48.0 serving
`jeb-mcp-server` 1.3.0 at four scales: a compiled Java class fixture, a
locally built signed probe APK with a launcher activity (manifest, v1/v2/v3
certificates, dex bytecode, filtered and paginated listing), the published
@@ -727,7 +767,12 @@ decompilation through both CLI and MCP, and the official Flutter Gallery
analyzed as native code (75k methods on arm64) and decompiled through both
CLI and MCP with explicit unit selection, plus the thin Java wrapper and
decoded manifest. The dedicated Dart snapshot processor and `run_script`
remain GUI-only surfaces in JEB 5.48.0 headless.
remained GUI-only surfaces in that headless build.
The separate [Linux CE native audit](../experiments/analysis-efficiency/jeb-linux-native.md)
uses the official CE `5.48.0.202610071539` direct CLI launcher and real
CLI/stdio MCP ELF workflows. That audit does not qualify CE Java/Android
decompilation or GUI project opening, and it does not replace the licensed
verification above.
Authenticated IPA and macOS application inventory projection is documented in
[Apple application analysis](apple-application-analysis.md).
@@ -1536,7 +1581,12 @@ NUL-containing names. It compares exact WAT,
section output and selected/tool digests through the compiled CLI and real MCP
SDK, validates advertised schemas, checks identical CLI/MCP Evidence IDs and
session bundle readback, retains invalid/truncated module diagnostics, preserves
all inputs and checks workspace cleanup. Pass an installed package's
all inputs and checks workspace cleanup.
The lane also injects one cleanup denial for exact private runtime roots and
input descriptors, checks recovered CLI errors, and verifies retry on the same
MCP connection after repeated runtime-removal denial. These are controlled
faults, not naturally occurring permission failures; no WASM target is executed.
Pass an installed package's
`scripts/rea.mjs` as the first argument to run the same lane against the package.
`npm run verify:wasm:package` packs and installs REA into an isolated temporary
prefix with lifecycle scripts disabled and runs the same required real-tool lane.
+13
View File
@@ -79,10 +79,20 @@ providers need not expose identical state machines.
and resource release separate. A valid process handle, successful launcher,
or selected target establishes only its own fact. Publish available identity
and exit observations even when launch fails; retain unknown ownership.
- Process exit does not establish output closure. The shared process supervisor
retains at most 64 KiB of additional diagnostics from owned stop entry,
including pending cleanup and retries, and drains for up to one second after
verified stop. It marks output incomplete when closure cannot be
observed and releases owned event-loop references without changing the proven
process-cleanup outcome. Failed process cleanup retains capture for retry;
borrowed processes and streams retain their own lifetime and reference state.
- Authenticate and capture identity through the same boundary on initial and
recovery handshakes. Bound abandoned unauthenticated listeners where the
protocol permits it; that deadline must not expire authenticated idle sessions
or long-running native work. Listener expiry does not prove target closure.
- Readiness observations may await owned prerequisite commands. Status must
remain readable during owner finalization without starting replacement work;
report readiness as unknown when it cannot be re-observed.
- Shared startup has one producer and independently cancellable caller waits.
Keep it running while any caller still needs it. The final departing caller
waits for owned cleanup and retains its failure. Provider closure is distinct
@@ -95,6 +105,9 @@ providers need not expose identical state machines.
primary failure and let incomplete cleanup retain its own classification.
Preserve the caller's abort reason as the typed cancellation error's cause,
including when cancellation is observed only after provider completion.
Shared command owners must retain the observed cancellation reason separately
from physical failures and cleanup ownership. Carry that fact through failure
wrapping so adapters can preserve the exact reason without interpreting it.
Client cancellation alone does not prove native work stopped.
For atomic publication, document the cancellation cutoff and preserve any
committed output's publication receipt.
+4 -1
View File
@@ -46,7 +46,10 @@ Equivalent MCP input:
```
Each selected regular file is read through the existing stable artifact reader;
symlinks, changing files and files over 8 MiB fail closed. WASM bytes are copied
symlinks, changing files and files over 8 MiB fail closed. An oversized input
returns `resource_constraint` with resource `file-size` and its fixed byte
allowance in `reported_limits.maximum_input_bytes`; retrying the unchanged file
does not raise that allowance. WASM bytes are copied
into a private workspace as read-only `module.wasm`; its digest is rechecked
before and after each tool. WABT validates with `--enable-all`,
reports sections through `wasm-objdump -h -x`, and decodes through
+30
View File
@@ -1,5 +1,35 @@
# Analysis efficiency measurements
For Hopper literal string queries, see the [decoded-record checkpoint](hopper-string-records.md).
It measures record reuse through real Linux CLI/MCP workflows, preserving
native recognition gaps, guard-stopped attempts and negative whole-task costs.
The [Linux embedding-runtime follow-up](hopper-linux-python-runtime.md) checks
actual clean setup, dependency repair and normal-user analysis, retaining
failed attempts and distinguishing prerequisite correctness from speedups.
For JEB on Linux, see the [CE native audit](jeb-linux-native.md). It qualifies
the direct headless launch route and measures selected function reads through
real CLI and production stdio MCP, preserving timeout attempts and the limits
of wrapper-cache and token claims.
For Apktool locale queries, see the
[shared-decode and table-only checkpoint](apktool-string-tables.md). It separates
the existing decode reuse from duplicate string delivery, with three real APKs,
CLI/MCP parity, whole-task estimates and negative timing results.
For immutable imported native/JavaScript parents, see the
[validation-reuse checkpoint](immutable-parent-validation.md). It measures the
owning format change with the existing retained-query driver, including first
view costs, small cases, unchanged token estimates, and noisy whole-task results.
For authentic old Evidence and mixed investigations, see the
[retained-query checkpoint](retained-investigation-workflows.md).
`retained-investigation.mjs` compares existing complete delivery with selected
pages, including capacity-refusal recovery and original-record round trips.
It records negative model results and separates imported-format validation work
from the query contract.
For genuine Fiddler captures, see the [SAZ selection checkpoint](saz-selection-workflows.md).
`measure.mjs --saz-selection baseline|focused|retained` compares default complete
delivery (including retained-reference recovery), caller-selected ordinals, and
@@ -187,6 +187,7 @@ def main():
'inspect_managed_members', 'inspect_managed_native_boundaries',
'inspect_artifact', 'inspect_wasm_artifact',
'inspect_evm_interface', 'get_evidence_bundle', 'export_evidence_bundle',
'import_evidence_bundle', 'list_unknowns', 'verify_unknown_resolution',
'capture_browser_scenario',
'resolve_containing_procedure', 'analyze_function', 'close_binary']:
config += '[mcp_servers.rea.tools.' + tool + ']\napproval_mode = "approve"\n'
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,171 @@
# Apktool shared decoding and table-only projection
The measured question is the same in every comparison: inspect an APK's
metadata/manifest and its default, German and Brazilian Portuguese string tables.
One existing batch removes repeated decoding. An optional table-only projection
then removes repeated primary strings without changing the selected facts.
This is a static resource-query measurement, not an agent evaluation or a claim
about reconstructing an application's behavior. The machine-readable measured
summary is [apktool-string-tables.json](apktool-string-tables.json).
## Revisions, inputs and execution
- Baseline: `8f7d12e30d63f3d3b4cfe2a90336362841438f56`, latest main at issue start.
- Projection source: `406af90023a545fb280601bf51f445e122c36378`.
- Existing shared decode: [#4777](https://github.com/morluto/rea/issues/4777),
integrated by [#4804](https://github.com/morluto/rea/pull/4804).
- Projection follow-up: [#4982](https://github.com/morluto/rea/issues/4982).
- Linux x86-64, kernel 6.1.0-53-cloud-amd64, Node 24.18.0, npm 11.16.0,
Temurin 21.0.12.1, Apktool 3.0.3. The selected JAR's closeout digest is
`dbf930b076c6b9be08d57c449cacefc3bdd6b71ebd59b3066fc0e1f5b14f9423`;
decoder contents were not independently hashed for every request.
- [newpipe](https://github.com/TeamNewPipe/NewPipe/releases/tag/v0.29.1): 11,507,122 bytes; SHA-256 `18447bfb1e06d113edc88df93f471827280de06f6f3d4dc42f56f28b9c1bab79`. Valid projected counts: 1126/833/779; unsupported entries: 1/1/1.
- [apidemos](https://github.com/appium/android-apidemos/releases/tag/v6.0.18): 6,521,217 bytes; SHA-256 `a9eecf37b26cd084855c530db81c2bb1b91f4c1b095a04f47aa7c20e2791f686`. Valid projected counts: 967/29/29; unsupported entries: 8/0/0.
- [settings](https://github.com/appium/io.appium.settings/releases/tag/v8.2.1): 3,099,589 bytes; SHA-256 `e4024b3ee348929252bcaf5eb422bcab9e4ace96703499a84f3332281c03375f`. Valid projected counts: 25/19/19; unsupported entries: 0/0/0.
The counts are default/de/pt-BR respectively. Source paths, XML digests, exact
caller paths, runtime hashes and per-run identities/costs are in the JSON summary.
The APKs were never installed or executed. Input digests were checked before and
after each completed task; provider-owned workspaces were confirmed absent.
Each task uses a fresh production stdio MCP runtime, complete paginated tool
discovery, client inspection, resource call(s), `close_binary`, transport close
and owned cleanup. OS file caches were not flushed. Every decode has a fresh JVM,
APK snapshot, framework directory and workspace: these are not warm retained
engine/database queries, nor cold-disk measurements.
Tasks run serially on one logical CPU, with an 8 GiB family hard limit, 7 GiB
early stop and no swap. Admission requires 16 GiB host availability; running
tasks stop below 8 GiB host availability. CPU and kernel `memory.peak` cover
the complete owned cgroup, including subprocesses, observer and file cache.
Counters are captured at payload exit while the observer is alive; disappeared
final whole-scope counters remain unknown. All completed payload receipts show
successful owned cleanup and zero observed OOM/limit events at that checkpoint.
## Correctness and owning change
Separate native decodes were retained for independent Python Expat validation.
It checks direct string entries, source entity spelling, unsupported markup
counts, ordering and emitted prefixes against the XML bytes. Manifest text and
package identity are checked separately. All six serial/batch pairs agree on
selected strings, manifest, metadata and target identity. All nine projection
pairs additionally preserve table counts and coverage; batch/table-only counts
and coverage match the XML oracle. This qualifies REA's projection of these
native outputs, not independent binary ARSC decoder completeness.
The existing default batch repeats its primary table in top-level `strings` and
`string_tables[0]`. On NewPipe this made the full resource replies larger than
three serial replies despite reducing decode calls: 616,059 → 680,188 bytes.
ApiDemos' larger common manifest produces the opposite tradeoff. The existing
batch remains the compatibility default.
`string_tables_only: true` (CLI `--string-tables-only`) keeps the complete
requested table facts inline in `string_tables` and leaves `strings` empty by
request. An unrequested field does not establish that a source table is empty.
This also supports one table without `locales`. `include_strings: false` still
suppresses string projection. Omitting the new option preserves old requests:
three real legacy workflows produce identical complete MCP replies, including
Evidence identity, at the new source revision.
The resource projection owner computes the output choice and charges aggregate
entries for the copies actually emitted. The same 10,000-entry policy can thus
return five distinct 2,000-entry tables rather than consuming capacity on a
duplicate primary copy. Per-table limits, XML budgets, physical alias reuse,
coverage, partial observations and cancellation remain at the existing owner.
CLI and MCP share this implementation; there is no new tool or cross-call cache.
## Existing shared decode: three serial calls versus one batch
Three alternating fresh-session pairs per APK, all at the same main revision.
The table reports marginal medians; paired speedups are reported separately.
Elapsed/CPU include discovery, client/Java startup, calls, transcript/hash checks
and cleanup. Native oracle creation, tokenization and verification gates are
separate costs in the JSON, not hidden inside these task timings.
| Target | Resource calls, s | Whole elapsed, s | Whole CPU, s | Family peak, MiB |
| -------- | ----------------: | ---------------: | ------------: | ---------------: |
| newpipe | 23.19 → 7.40 | 35.32 → 19.86 | 34.95 → 19.65 | 1341.7 → 1309.5 |
| apidemos | 17.43 → 6.22 | 30.32 → 18.85 | 30.05 → 18.63 | 1187.1 → 1202.7 |
Paired resource-call speedup medians: NewPipe **3.23×** (2.90–3.23), ApiDemos
**2.80×** (2.66–3.09). Instrumentation confirms three versus one actual decoder
invocations. These call timings include protocol/projection costs; they do not
isolate the Java engine phase. NewPipe's whole raw token estimate increases
951,332 → 962,330 with the default batch; ApiDemos decreases
1,054,893 → 948,591. Fewer calls alone do not establish less context.
## New selector: default batch versus table-only batch
Three alternating pairs per APK; NewPipe repetitions 1/4/3, others 1/2/3.
Repetition 4 replaces a refused start, described below. Both variants still
perform one decode and return the same required facts.
| Target | Resource call, s | Whole elapsed, s | Whole CPU, s | Family peak, MiB |
| -------- | ---------------: | ---------------: | ------------: | ---------------: |
| newpipe | 9.18 → 9.35 | 23.95 → 24.85 | 23.65 → 23.82 | 1327.5 → 1323.6 |
| apidemos | 6.46 → 5.81 | 23.28 → 19.79 | 22.90 → 19.47 | 1247.8 → 1197.0 |
| settings | 4.21 → 4.06 | 17.66 → 19.39 | 17.42 → 19.04 | 1191.6 → 1122.4 |
There is no consistent latency or whole-family memory improvement established
for the selector. NewPipe and Settings have higher whole-task elapsed and CPU
medians. Elapsed ranges overlap: NewPipe 23.53–34.46 → 21.59–42.80 s;
ApiDemos 18.55–27.07 → 19.25–25.96 s; Settings 16.55–23.39 → 16.14–21.72 s.
The cause of this variation was not established; a passing or faster run does
not explain a slower one. The selector's supported benefit is reduced duplicate
delivery, with its full task costs retained here.
Full resource reply bytes fall 680,188 → 506,481 for NewPipe,
616,841 → 463,588 for ApiDemos and 38,725 → 34,199 for Settings.
Offline `tiktoken` 0.12.0 / `cl100k_base` estimates count one complete catalog
page-array plus every call name/argument/reply, including inspection and close.
Full replies include both textual and structured copies. Text-only replies are
also counted separately. These are serialization estimates, not actual model
usage; initialization/framing, prompts, client filtering/rendering/replay and
continuations are unmeasured.
| Target | Text reply tokens | Whole raw transport tokens | Whole estimated reduction |
| -------- | ----------------: | -------------------------: | ------------------------: |
| newpipe | 73,661 → 55,797 | 962,330 → 924,352 | 3.95% |
| apidemos | 65,693 → 49,162 | 948,591 → 913,693 | 3.68% |
| settings | 4,387 → 3,942 | 814,859 → 814,029 | 0.10% |
Discovery still has 207 tools; only `decode_android_resources` changes.
The added schema costs 557 catalog bytes / 113 estimated catalog tokens
(805,217 → 805,330). Raw discovery dominates this transport estimate; it does
not establish what a client supplies to its model. Smaller replies and these
estimates do not prove an actual agent context or investigation advantage.
## Verification, attempts and limits
- 94 source tests: provider/resource projection/service, including alias reads,
aggregate output exhaustion, malformed/physical failures, partial facts,
cancellation and cleanup. The table-only capacity, single-table provider
and CLI cases fail on the old implementation (three selected failures,
82 unrelated tests skipped).
- Nine compiled CLI/MCP tests: schema conversion/advertisement, option forwarding,
Evidence parameters, legacy locale grammar and existing boolean choices.
- 36 completed real MCP tasks and three real CLI tasks. Nine exact projection
oracle pairs; three legacy full-reply comparisons; alias/missing, single-table
and omitted-string cases pass. CLI normalized data, subjects and Evidence IDs
match MCP exactly on all three APKs.
- `check:fast`, `build:cached` and `verify:test-discovery` passed; the last
discovered all 1,579 test files exactly once. `npm run check` and `npm run docs:check` also passed.
- An authored test group initially exceeded the 150-line lint limit (153).
That gate failed after 189.31 s / 186.19 CPU s, peak 4,487,798,784 bytes;
build/compiled checks did not run in that attempt. The capacity test was moved
out of that group, and the complete corrected gate passed after 343.97 s /
338.67 CPU s, peak 4,311,293,952 bytes. No rule was disabled.
- `projection-newpipe-after-2` was refused before scope/payload creation because
host availability was below 16 GiB; a following poll showed about 10.5 GiB.
It has no payload timing/counters and is not a completed measurement. The
refusal stays recorded; a fresh whole repetition-4 pair ran after recovery.
No existing receipts were overwritten and resource thresholds were unchanged.
Raw transcripts, native outputs, intermediate failures and per-request workspace
arguments remain in ignored local audit storage; the committed JSON is a curated
summary. Small Settings resources and negative time results are retained.
Actual models, APK execution, other providers/platforms and general complex
software effectiveness were not verified by this checkpoint.
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,203 @@
# Linux Hopper embedding-runtime qualification
This follow-up to the [string-record audit](hopper-string-records.md) resolves
[#5015](https://github.com/morluto/rea/issues/5015): a successful package install
could leave Hopper unable to start its Python bridge. This is a prerequisite
correctness fix. It does not establish faster equivalent investigations.
## Reproduction and owning correction
Baseline REA `093ce695f0a7c3407c7fde520ed8b20c0981b408` ran the actual public
`rea setup --yes --install-hopper --format json` in a fresh Ubuntu Base root,
with full Linux UID/GID support, successful strict APT metadata refresh,
default APT recommendations and unchanged vendor post-install scripts. Setup
reported `ready` and `installed_hopper`; `dpkg --audit` was empty. Python 3.12
was configured, but its reported `/usr/lib/x86_64-linux-gnu/libpython3.12.so.1.0`
was absent. The signed Hopper package did not declare this dependency.
An actual public MCP control-ELF workflow then timed out at its selected
30-second startup budget. Installing only `libpython3.12` resolved to
`libpython3.12t64` version `3.12.3-1ubuntu0.17`: one new package, no upgrades or
removals. The unchanged REA runtime and target then completed all 13 calls.
The independent earlier bootstrap dialog and controlled package intervention
support the embedding-dependency diagnosis; timing alone does not.
The shared Linux installer now asks `/usr/bin/python3` for its version and
shared-library location after the original package transaction. It attempts an
actual load. For a missing standard Ubuntu ABI/layout it installs the matching
versioned library and verifies loadability again. APT success alone cannot
establish readiness. Unknown metadata, alternate prefixes and alternate SONAMEs
retain an explicit unknown diagnosis instead of guessing a package.
Doctor consumes the same fresh, bounded producer, retaining both private-display
and Python observations. Its Python facts concern the system interpreter;
the interpreter actually selected inside Hopper remains unknown. The query
uses isolated Python mode, disables site hooks and bytecode writes, has a
10-second work deadline and 64-KiB output bound, and is not cached: package/interpreter
changes must be visible immediately. The approval plan discloses the conditional
package change without returning the probe program. Existing MCP analysis
contracts and Evidence semantics are unchanged.
Cancellation prevents starting another install stage and preserves the original
reason. A running package transaction is allowed to settle, avoiding forced
interruption of dpkg. This qualifies the install API's signal behavior through
boundary tests; it does not claim new CLI interrupt plumbing or actual
interruption of a real package transaction.
A review follow-up preserves cancellation through doctor as well. A cancelled
Python probe now raises the existing domain cancellation error with its original
reason and completed private-display observations; the owned doctor workflow
performs cleanup and attaches any cleanup failure. It no longer becomes an
ordinary unknown diagnostic. The probe also opts into the existing
stop-and-wait supervisor: cancellation and timeout wait for Python to exit,
with the shared five-second grace period and forced-kill fallback. A real
Python fixture holds its SIGTERM handler until the test releases it, checking
that the probe retains ownership until exit. The success-path native measurements below precede
this error-branch correction. Its boundary tests cover both successful cleanup
and cleanup refusal; final-head validation is tracked on the PR. A pre-fix red
execution and a final-head native rerun are not claimed.
## Pinned environment and artifacts
Native success measurements pin the initial fix at
`0c70be0030f6bb821c4292c4f5b7f776b365c8ec`, based on main
`23f984b1a884baefc3c44e4ab697f6eff61dffb6`.
The measured runtime and bridge manifest covers 1,733 regular files, 26,537,393
bytes. Its SHA-256 is
`8800edc8e70965ae2132e57ca6d999ba276503f27f1045b33305c83cd0e2ff2a`
(sorted paths, compact sorted-key JSON records containing path, bytes and
SHA-256). The tiny MCP loader alone is not a whole-runtime identity.
Earlier prototype runs have a separately recorded manifest; they are not
presented as executions of that same artifact.
- Node 24.18.0; npm 11.16.0; Debian 12 x86-64 host.
- Official [Hopper 6.6.0 Linux demo](https://www.hopperapp.com:443/downloader/public/Hopper-6.6.0-Linux-demo.deb),
35,748,722 bytes, SHA-256
`59705e50f7ebf9104abe1e72ac3949bed1a8e43ec67a8c9510a1c5056c200d27`.
REA's real publisher verifier authenticated signing fingerprint
`7C2D73BAFAF5B9A710A69DA1A26DF815099B1BCD`; launcher SHA-256
`b2271008fb82bf14cfae5c3644cdde2b11fc6804bd7c99d67032ec1b1942d53c`.
- Private [Ubuntu Base 24.04.5 root](https://cdimage.ubuntu.com/ubuntu-base/releases/24.04/release/ubuntu-base-24.04.5-base-amd64.tar.gz),
SHA-256 `e77b6f10c2590cef872b33ee9f635a0e3fd1f57fb074c0e52b5c7f56147a0c86`,
matched its official HTTPS checksum; its publisher signature was not
independently verified. The host glibc 2.36 cannot run this Hopper package;
the private root supplies glibc 2.39. Related packages were installed only
there. Strict refresh used `APT::Update::Error-Mode=any`.
- Small control ELF: 17,360 bytes, SHA-256
`1a6f55303712e096ff5196c7fd6e8acf75473fbf0f7467d7b315f96f50b3f841`.
GNU objdump independently maps the arithmetic at `0x401126` to
`arg0 * 7 + 13`; the original build flags were not recovered. The known
`rea_jeb_native_probe` literal has one native string hit.
- Shipped Debian SQLite 3.40.1-2+deb12u2, `libsqlite3.so.0.8.6`, 1,437,848
bytes, SHA-256
`2e6eef9a727f081f0d453b4e5e6cbd8b9ef8b6f86cbf7681cbad444d3b0b55c8`.
Independent objdump identifies `sqlite3_libversion_number` at `0xa1d50`,
returning `0x2e6301` (`3040001`). This is a shipped library qualification,
not a claim about current SQLite versions or whole-program correctness.
## Actual workflows and measurements
The accompanying [receipt](hopper-linux-python-runtime.json) preserves native
stage outcomes, elapsed time, CPU time, peak memory, cleanup and token-estimate
scope, including failed and stopped attempts. Installation uses root inside the
owned fixture. Final analysis also uses a normal UID 1004 with a separate home
and the authenticated public publisher evidence copied unchanged into that
user's cache; production verification still checks it. No target was executed
or modified. Non-root package installation through real pkexec authorization,
custom Hopper Python configuration, native Debian installation and other
operating systems remain unverified.
Fresh final setup reported `ready` / `installed_hopper`, installed the matching
shared library, left `dpkg --audit` empty and passed actual public doctor.
Final normal-user sessions completed the positive, repeated, negative and
decompilation checks: 19 `sqlite_master` matches in 1,437 recognized strings,
74 broader `sqlite` matches and the expected version constant. Both CLI runs
matched their MCP facts and Evidence identity; both target files stayed unchanged.
| Stage / outcome | Elapsed (s) | Native CPU (s) | Native peak (MiB) |
| ---------------------------------------------- | ----------: | -------------: | ----------------: |
| Baseline setup: ready, library absent | 75.353 | 39.002 | 1395.4 |
| Baseline control MCP: startup timeout | 49.541 | 24.158 | 1282.5 |
| Install only matching libPython | 5.012 | 1.925 | 174.8 |
| Unchanged runtime control MCP: passed | 24.791 | 23.608 | 1228.4 |
| Fixed fresh setup: ready | 80.739 | 40.404 | 1382.6 |
| Fixed public doctor: healthy | 6.035 | 5.160 | 560.9 |
| Fixed normal-user control MCP: 13 calls passed | 24.434 | 23.305 | 1195.7 |
| Fixed normal-user SQLite MCP: 16 calls passed | 92.504 | 90.138 | 1507.2 |
| Fixed normal-user control CLI: parity passed | 8.981 | 7.831 | 613.5 |
| Fixed normal-user SQLite CLI: parity passed | 70.207 | 68.704 | 781.6 |
Each control MCP session includes discovery and 13 calls: open, status, first
literal query, six repeats, absent query, procedure listing, decompilation and
close. SQLite uses 16 calls: a known exported address avoids a separate name
lookup, and full native string inventory, broader query, regex, case and absence
checks are included. Applicable returned string bytes are checked against the
file. Warm queries must preserve facts and Evidence ID. CLI searches compare
normalized facts and Evidence ID with their corresponding MCP result. Both
target digests are checked after the task.
“Fresh” means a new installation state or Hopper process, as indicated; OS
caches were not flushed. Installation fixtures preload 247 previously fetched
APT archives (138,023,324 bytes) without installing packages. These are not
cold-network benchmarks. The baseline failure and successful repair are
different outcomes, not a paired speedup. This change adds no analysis cache
and makes no token reduction or complex-investigation advantage claim.
Native stages have a 6-GiB container cap, 5-GiB early stop, no swap, 128 tasks,
one CPU and explicit affinity. Their separate controller has a 512-MiB cap,
no swap and 64 tasks. Native plus controller caps total 6.5 GiB. Heavy tasks
run serially; admission waits for at least 16 GiB host availability and aborts
below 8 GiB. Host build/check stages have an 8-GiB cap and 7-GiB early stop.
No other user's processes were signalled.
Native CPU and kernel memory peaks include the container guardian and observer.
Payload-exit observations verify no analysis children remain before stopping
the container. Controller counters are separate partial snapshots; adding
separate peaks is only an upper bound, not a simultaneous measurement. Shared
Docker daemon, containerd and system-manager costs and final counters after
scope removal are unknown. Stopped attempts without payload-exit observations
retain unknown cleanup/counter fields. Observed zero OOM counters at payload
exit are not a claim about the whole host.
## Failed attempts and limits
- A single-ID namespace could not apply the real vendor/package UID/GID
ownership changes. It did not qualify a successful installation.
- An inherited container-loopback proxy prevented publisher downloads; the
fixture was corrected to forward the same configured proxy through the
owned bridge gateway. No global proxy configuration was changed.
- An early APT refresh exited zero with failed-index warnings. It was not
counted as a successful refresh; the strict clean run establishes the result.
- One earlier setup was stopped by the host-memory guard during unpacking.
Its interrupted package state was retained separately; clean fixtures were
used for the qualified baseline and final installations.
- SQLite exceeded a caller-selected 30-second startup budget. With the normal
120-second budget the bridge started, but an initial `libc` positive oracle
failed: those `.dynstr` bytes were not a recognized Hopper string. The
corrected native positive is `sqlite_master`. These attempts remain in the
receipt. Empty recognized-string results do not prove physical absence.
- Early candidate compilation found a TypeScript signal-narrowing error; the
fix reads live signal state through a helper. Lint found oversized new test
groups; they were split by behavior. Failed checks are retained as failures.
- Full local fast checks were stopped twice when other host workloads reduced
available memory. Those attempts did not pass; complete CI and later executed
checks must be reported separately.
- Local package verification completed installation, update, module loading,
discovery and several CLI stages, then the same host-memory guard stopped it
during setup/uninstall after 336.196 seconds (sampled peak 1,742,426,112
bytes). This is an incomplete package gate, despite the separately completed
real Hopper setup and analysis workflows. Its payload-exit counters and
cleanup observation are unknown. CI must complete this gate before merging.
Offline `cl100k_base` estimates include each discovered full catalog, named
arguments and raw replies once per recorded session, plus selected CLI arguments
and stdout. Raw MCP replies can contain both text and structured copies.
The final control and SQLite MCP validation transcripts estimate 845,126 and
1,064,574 tokens respectively, including 805,330 catalog tokens each. The CLI
rows estimate 695 and 2,905 tokens for one selected search each. Those CLI rows
cover different tasks from the multi-call MCP qualification sessions, which
also include full-inventory validation; they do not form an interface-efficiency
comparison. Failed attempts and retries have separate rows in the receipt.
Actual agent/model usage, host schema filtering, protocol framing, prompts,
rendering/replay, independent oracle context and unrecorded in-flight failures
are unmeasured. This is provider qualification, not an agent benchmark.
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,236 @@
# Hopper literal search: reuse decoded records
For [#5010](https://github.com/morluto/rea/issues/5010), the bridge now keeps
the existing decoded records in its sorted search inventory and projects only
matches. Previously, every positive literal search called `_string_inventory`
again, sorting all native object addresses and building another complete map
before selecting records. The value-only `_strings` representation and the
intermediate match list have been removed.
This removes repeated work at its owner. Three paired SQLite workflows found
shorter warm queries, but **did not demonstrate an overall task speedup**:
whole elapsed and CPU were worse. Token estimates were effectively unchanged; no memory
improvement was established. The [curated receipt](hopper-string-records.json)
keeps individual measurements, equality checks and failed attempts.
## Representation and reuse boundary
The existing `(id(document), kind)` search cache now holds references to full
records rather than just their decoded values. The existing string-record
owner still performs byte decoding, encoding inference and unknown handling.
Matching reads `record.value`; output copies the selected record with its
address, retaining native display, encoding/extent metadata and decoding
failures. The original document lifetime and annotation invalidation apply.
This does not add another cache or duplicate decoded payloads. Both CLI and
MCP use the same bridge through the existing application workflow. Regex keeps
its supervised worker implementation.
The lookup remains a linear scan of recognized native strings. A complete
inventory is still decoded for the first exhaustive search, and returned hits
still consume output and retained Evidence. This change does not introduce
pagination, broader binary string discovery or a new context contract.
## Pinned real-provider scope
Baseline: REA 6.7.0 at `c12d9eda87aa5a022ee7e18ea96d8d03bfd12e96`, Node
24.18.0, Debian 12 x86-64, Linux `6.1.0-53-cloud-amd64`. All paired tasks used
the same compiled runtime SHA-256
`0809b5004f214efa4b79775d5fda796eaecf4ad5756c55ee3b9d7cf82d91b750`.
Baseline and candidate bridge digests are recorded separately in the receipt;
the baseline file was mounted read-only over the bridge for baseline runs.
The official [Hopper 6.6.0 Linux demo package](https://www.hopperapp.com:443/downloader/public/Hopper-6.6.0-Linux-demo.deb)
was authenticated through REA's production publisher verifier: 35,748,722
bytes, SHA-256
`59705e50f7ebf9104abe1e72ac3949bed1a8e43ec67a8c9510a1c5056c200d27`,
primary signing fingerprint `7C2D73BAFAF5B9A710A69DA1A26DF815099B1BCD`.
The extracted launcher digest was
`b2271008fb82bf14cfae5c3644cdde2b11fc6804bd7c99d67032ec1b1942d53c`.
Normal demo activation used an owned private Xvfb display.
The host's glibc 2.36 could not satisfy this package's GLIBC_2.38 requirement.
Analysis therefore ran in a user-mapped private Ubuntu Base 24.04.5 namespace
with glibc 2.39, Qt6/X11 and embedded Python 3.12. The
[rootfs archive](https://cdimage.ubuntu.com/ubuntu-base/releases/24.04/release/ubuntu-base-24.04.5-base-amd64.tar.gz)
matched its official HTTPS checksum, SHA-256
`e77b6f10c2590cef872b33ee9f635a0e3fd1f57fb074c0e52b5c7f56147a0c86`;
its publisher signature was not independently verified. Related libraries were
installed only into that private image. Actual `rea setup` and vendor package
post-install scripts were not executed. Native Debian installation and other
operating systems remain unqualified.
| Artifact | Bytes | Independent oracle |
| ------------------------------------ | --------: | -------------------------------------------------------- |
| Small authored C ELF | 17,360 | Source `input * 7 + 13` and ELF symbol |
| Source-built conformance ELF | 22,584 | Fixture source, known literal bytes and symbol addresses |
| Shipped Debian `libsqlite3.so.0.8.6` | 1,437,848 | GNU objdump: version function returns `3040001` |
Artifact digests, compiler and flags are in the receipt. No target was executed.
The small ELF was a baseline setup/decompilation smoke test; the conformance
ELF had baseline/candidate semantic and CLI/MCP checks. SQLite supplied the
repeated performance cohort. A constant-return oracle does not establish
whole-program correctness, ABI inference or complex-investigation effectiveness.
## Completed task and correctness
Each of three baseline/candidate pairs used a fresh Hopper process and the
same public stdio MCP workflow, including catalog discovery and 17 tool calls:
1. Open the SQLite copy and inspect `binary_session`.
2. Search `sqlite_master` in case-sensitive literal mode, then repeat six times.
3. Read `list_strings` as a full-inventory validation oracle; compare all
matching records, including their metadata, with the selected result.
4. Check broader `sqlite` search, regex, case-insensitive and absent-pattern
queries against their expected native inventory scope.
5. Resolve `sqlite3_libversion_number`, decompile it, check its constant against
independent objdump, and close the binary and MCP transport.
6. Verify unchanged target bytes and cleanup of the owned process family.
There were 1,437 recognized string objects, 19 `sqlite_master` matches and 74
broader `sqlite` matches. Applicable decoded hit bytes were also checked
against file contents. All 42 Evidence payloads across the three pairs matched
on normalized and raw facts, identity, subject, provider/profile, authority,
confidence, limitations, relationship coverage, locations and evidence links.
Live environment observations were excluded from cross-session equality.
The real conformance workflows each completed 70 public calls, including six
expected errors. They exercised Unicode symbol identity, literal metacharacters,
case/regex modes, annotation cache invalidation, typed string fragments,
display escaping and source-byte checks using the existing real-provider
verifier. CLI `fragment_tail` search returned the same normalized facts and
Evidence ID as MCP on both revisions (2,293 stdout bytes each).
Native recognition remains qualified: the full 4,120-byte source literal and
some known UTF-8, ellipsis and Latin-1 literals were not recognized. Their file
bytes were verified, while decoded native-object coverage remains unverified.
The fragmented 549-byte source literal was recognized as separate objects;
matching does not pretend to join those fragments. Escaped text and literal
backslash-n were distinguished. Real UTF-16 provider decoding was not qualified;
the focused boundary fixture covers that representation separately. Empty
search results establish absence only within the native typed-object inventory.
## Measurements
Runs were serial and ordered B1/C1, C2/B2, B3/C3. C2 first stopped under the
host memory guard; its separate completed retry supplies the C2 row. OS caches
were not flushed. “Cold” means the first string query in a fresh provider;
“warm” means six repeats within that provider.
Medians across the three completed sessions per revision:
| Cost | Baseline | Candidate |
| ---------------------------------------- | ----------: | ----------: |
| Per-run median warm query | 14.849 ms | 12.628 ms |
| Six warm queries, total | 100.202 ms | 75.552 ms |
| Whole SDK workflow | 86.583 s | 90.786 s |
| Whole guarded workflow | 89.384 s | 93.630 s |
| Family CPU at payload exit | 88.333 s | 92.590 s |
| Kernel family peak, including file cache | 1,438.4 MiB | 1,440.0 MiB |
Warm-query medians were lower in all three pairs, although one baseline run
was particularly variable. Whole guarded elapsed was about 4.7% worse and CPU
about 4.8% worse. The first query accounted for 65.2–71.2 seconds on baseline
and 72.3–77.5 seconds on candidate; that interval includes native preparation.
Do not interpret this cohort as end-to-end acceleration or dismiss the
unfavorable rows.
A separate pair moved the **existing** `procedure_address` call before the
first string search, keeping the same 17 calls and equivalent facts/Evidence.
Most preparation cost then occurred in that unchanged lookup: 67.949 seconds
baseline versus 94.358 seconds candidate, before the changed search path ran.
The subsequent first string query took 329.254 versus 337.533 ms; total guarded
elapsed was 89.096 versus 113.917 seconds. This locates substantial cost outside
literal projection and preserves the negative whole-task result. It does not
prove its cause, establish a cold-query improvement or justify pooling the
reordered pair with the original cohort.
## Resource and context accounting
Every heavy task used an owned cgroup v2 family: 8 GiB hard limit, 7 GiB early
stop, zero swap, one CPU quota and affinity, 192 tasks, 3,072 MiB Node heap,
and one heavy job at a time. Host admission required 16 GiB available; tasks
stopped below 8 GiB. Later long runs added admission at 24 GiB sustained for
20 seconds without relaxing the family guard.
Family counters include the namespace, native engine, MCP client/runtime,
validation and cleanup, including charged file cache. SDK wall time includes
catalog discovery and correctness assertions. Admission wait is outside the
guarded time and recorded separately where available; older waits and outer
guard/systemd costs were not measured. Payload-exit CPU, kernel memory peak,
limit counters and owned cleanup were verified while an observer remained.
Final counters after the scope disappeared remain unknown. Completed paired
payloads observed zero memory-limit, OOM/OOM-kill and task-limit events.
The unchanged catalog advertised 207 tools, 3,158,927 serialized bytes,
SHA-256 `a1851aa1bfe3d72d6be6bec130519dbdfc1ee49288320cfac0144ced77755c61`.
Offline tiktoken 0.12.0 `cl100k_base` estimates for every completed SQLite
comparison were effectively unchanged (medians across three runs):
| Serialization counted once | Baseline estimate | Candidate estimate |
| -------------------------------------------------- | ----------------: | -----------------: |
| Full catalog page array | 805,330 | 805,330 |
| Named tool requests | 361 | 361 |
| Raw replies | 259,809 | 259,811 |
| Total above | 1,065,500 | 1,065,502 |
| Text reply blocks alone, overlapping the raw count | 126,832 | 126,833 |
Whole raw estimates ranged from 1,065,496–1,065,500 on baseline and
1,065,500–1,065,506 on candidate. The small variations include live environment
observations; there is no context reduction.
The full inventory and extra controls are validation overhead included in
these totals. Raw replies may contain text and structured copies. Actual model
usage, prompts, initialization/protocol framing, client filtering/rendering or
replay and independent-oracle context were not measured. The receipt separates
other tasks and the interrupted transcript's persisted lower bound; it does
not count an unrecorded in-flight request or reply as observed. No model-token
saving or agent-reasoning advantage is claimed.
## Failed attempts and verification
The receipt preserves setup, diagnostic, failed and completed costs separately:
- Initial private-rootfs configuration failed on an unmapped package GID, then
on the harness PATH; ordinary pending configuration completed successfully.
- One low-host-memory probe was refused before creating a payload.
- The first MCP harness used a relative path, correctly refused by the public
input boundary. A later custom-runtime launch lacked libXtst6, which the
production dependency list already includes. Both were harness/setup errors.
- Two unchanged-baseline launches timed out at bridge startup. An owned-display
observation showed completed native analysis but a Python bootstrap failure.
Adding only the missing embedded Python library made the baseline pass;
[#5015](https://github.com/morluto/rea/issues/5015) records the separate
installer dependency hypothesis and its unverified actual-installer scope.
- C2 stopped after 82.607 seconds when host available memory crossed the
unchanged stop threshold. Its sampled family peak was 1,517,019,136 bytes
and last observed CPU 78.255 seconds. Payload-exit/final counters are unknown;
it is incomplete and excluded from completed-run statistics.
- An initial `test:local` invocation rejected built boundary projects before
running tests. The documented `test:focused` invocation then passed all
35 tests in five files, covering string search, regex, bridge facade and
real-provider verifier behavior. Those local tests do not themselves qualify
a native engine; the public native workflows above provide that evidence.
`npm run check:fast` also passed (typecheck and lint both cached). Build and
metadata preparation passed before the native workflows. `npm run docs:check`
passed its cached metadata checks and all five README consistency tests.
Reproduction uses `node scripts/rea.mjs --mcp` with an explicitly configured
Hopper launcher and private display. Drive the named calls above with absolute
target paths, retain full replies and compare independently verified bytes,
facts and Evidence. For the source fixture, compile
`tests/conformance/c/fixture.c` with
`gcc -O0 -g -fno-inline -fno-omit-frame-pointer -fno-pie -no-pie`.
Debug build paths can change rebuilt artifact digests. The shipped SQLite
digest pins the measured artifact rather than every build of its package.
The focused local gate was:
```sh
npm run test:focused -- \
tests/boundary/providers/hopper/hopperStringSearch.test.ts \
src/hopper/HopperRegexSearch.test.ts \
tests/boundary/providers/hopper/hopperBridgeFacade.test.ts \
tests/conformance/providers/hopper/realHopperSearch.test.ts \
tests/conformance/providers/hopper/realHopperExhaustiveSearch.test.ts
```
@@ -0,0 +1,654 @@
{
"issue": "https://github.com/morluto/rea/issues/4810",
"baseline": "2c4338db1fdb7773e05376301c558cff2a0b4b80",
"candidate": "3930a640fb8387cbd3079eee13fb90f82d1881dc",
"units": {
"latency": "milliseconds",
"elapsed_cpu": "seconds",
"memory": "kernel cgroup memory.peak bytes, including charged file cache and descendants"
},
"scope": "Three alternating before/after pairs per authentic retained native/JavaScript/small workflow; fresh stdio servers, three traversals of three two-row pages; catalog/import/oracles/export/close included in whole cost. OS caches uncontrolled. No actual model trial.",
"resource_bounds": {
"cpu_count": 1,
"hard_memory_bytes": 8589934592,
"early_stop_bytes": 7516192768,
"swap_bytes": 0,
"tasks_max": 192,
"host_start_available_bytes_min": 17179869184,
"host_stop_available_bytes_min": 8589934592,
"observer_sha256": "6f089e645459eba80c5d73e39c21606c78173a44daf49fabc3b88c4631ce51c6",
"guard_sha256s": [
"760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992",
"d2047721e50dd244900eb514d07025386e60908f77bb546bee5684baa1da2f19"
],
"guard_observability_note": "The newer private guard only records existing host-available-memory readings and its own cgroup path. Resource thresholds, CPU affinity, observer and stop/cleanup logic are unchanged."
},
"resource_accounting": "Final complete corpus tasks exit0/cleanup verified/oom0 at payload exit while observer remains alive. Resource-stopped and earlier failed attempts are recorded separately; terminal whole-scope counters are unavailable. Weak cache metadata/private heap not measured independently.",
"catalog": {
"tools": 207,
"bytes": 3158368,
"sha256": "1d6c9a679b7c935318f5ea59d5362ef0a06945b3f4a4f210c86997ec15ba4f4c",
"equal_across_all_18_runs": true
},
"facts": {
"native": {
"answer_sha256": "4e28c2c2a25620576d27abe846a05835ec49386b889779e33410c31971523b54",
"calls": 25,
"query_structured_content_equal": true,
"original_records_preserved": true,
"closed_session_cleared": true
},
"javascript": {
"answer_sha256": "0fe3ad249eb1a7233a4966a56652c173667c48fdaf395b6c08fc6901ef3b46f7",
"calls": 28,
"query_structured_content_equal": true,
"original_records_preserved": true,
"closed_session_cleared": true
},
"small": {
"answer_sha256": "9ca5874ce7bb610e39632157304fe4f52b09401d5fbd97ec981ad0588b8da485",
"calls": 19,
"query_structured_content_equal": true,
"original_records_preserved": true,
"closed_session_cleared": true
}
},
"runs": [
{
"name": "topology-paired-after-javascript-1",
"first_page_ms": 2720.542831999999,
"eight_later_pages_ms": 82.69408900000417,
"elapsed_seconds": 20.713,
"family_cpu_seconds": 20.50093,
"family_peak_bytes": 1311358976,
"import_ms": 10970.411619,
"catalog_ms": 1194.7045099999996,
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
},
{
"name": "topology-paired-after-javascript-2",
"first_page_ms": 1978.732818999999,
"eight_later_pages_ms": 84.12934999999197,
"elapsed_seconds": 20.011,
"family_cpu_seconds": 19.733879,
"family_peak_bytes": 1349349376,
"import_ms": 10382.461203,
"catalog_ms": 1436.8567430000003,
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
},
{
"name": "topology-paired-after-javascript-4",
"first_page_ms": 1864.8931889999985,
"eight_later_pages_ms": 75.18833300000188,
"elapsed_seconds": 23.023,
"family_cpu_seconds": 22.22643,
"family_peak_bytes": 1410211840,
"import_ms": 10857.059245,
"catalog_ms": 2247.621502,
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
},
{
"name": "topology-paired-after-native-1",
"first_page_ms": 216.87685000000056,
"eight_later_pages_ms": 85.03580100000181,
"elapsed_seconds": 17.253,
"family_cpu_seconds": 17.002728,
"family_peak_bytes": 1003585536,
"import_ms": 10361.605091,
"catalog_ms": 1289.5255869999992,
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
},
{
"name": "topology-paired-after-native-2",
"first_page_ms": 209.0475239999996,
"eight_later_pages_ms": 89.01302899999973,
"elapsed_seconds": 14.383,
"family_cpu_seconds": 14.091764,
"family_peak_bytes": 1031806976,
"import_ms": 7696.751557,
"catalog_ms": 1175.233292,
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
},
{
"name": "topology-paired-after-native-3",
"first_page_ms": 203.10824999999932,
"eight_later_pages_ms": 65.1013469999998,
"elapsed_seconds": 14.061,
"family_cpu_seconds": 13.773228,
"family_peak_bytes": 1098002432,
"import_ms": 7967.397724,
"catalog_ms": 1246.0458209999997,
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
},
{
"name": "topology-paired-before-javascript-1",
"first_page_ms": 1681.2564180000008,
"eight_later_pages_ms": 10378.131188999992,
"elapsed_seconds": 31.213,
"family_cpu_seconds": 30.872607,
"family_peak_bytes": 1618685952,
"import_ms": 11051.493961999999,
"catalog_ms": 1508.7439510000004,
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
},
{
"name": "topology-paired-before-javascript-2",
"first_page_ms": 2441.075147000003,
"eight_later_pages_ms": 15534.410540000004,
"elapsed_seconds": 40.856,
"family_cpu_seconds": 40.485251,
"family_peak_bytes": 1364238336,
"import_ms": 15032.366213,
"catalog_ms": 1319.6895839999997,
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
},
{
"name": "topology-paired-before-javascript-4",
"first_page_ms": 2479.6895360000017,
"eight_later_pages_ms": 16864.908822999998,
"elapsed_seconds": 39.629,
"family_cpu_seconds": 39.267656,
"family_peak_bytes": 1403023360,
"import_ms": 12437.073842999998,
"catalog_ms": 1212.8024130000003,
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
},
{
"name": "topology-paired-before-native-1",
"first_page_ms": 170.1303939999998,
"eight_later_pages_ms": 469.8933869999928,
"elapsed_seconds": 19.996,
"family_cpu_seconds": 19.55106,
"family_peak_bytes": 1063051264,
"import_ms": 11242.171146,
"catalog_ms": 1922.7818150000003,
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
},
{
"name": "topology-paired-before-native-2",
"first_page_ms": 183.56196199999977,
"eight_later_pages_ms": 393.4744759999994,
"elapsed_seconds": 15.26,
"family_cpu_seconds": 15.008304,
"family_peak_bytes": 1058492416,
"import_ms": 8713.099498,
"catalog_ms": 1272.265849,
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
},
{
"name": "topology-paired-before-native-3",
"first_page_ms": 244.26586400000087,
"eight_later_pages_ms": 517.9172599999911,
"elapsed_seconds": 19.083,
"family_cpu_seconds": 18.752553,
"family_peak_bytes": 1109987328,
"import_ms": 9948.476274999999,
"catalog_ms": 1879.787958,
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
},
{
"name": "topology-after-small-1",
"first_page_ms": 226.6032009999999,
"eight_later_pages_ms": 57.69190299999718,
"elapsed_seconds": 12.312,
"family_cpu_seconds": 12.043895,
"family_peak_bytes": 1056452608,
"import_ms": 6572.925846000001,
"catalog_ms": 1173.0129960000004,
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
},
{
"name": "topology-after-small-2",
"first_page_ms": 556.2178050000002,
"eight_later_pages_ms": 96.23635600000307,
"elapsed_seconds": 16.246,
"family_cpu_seconds": 16.068419,
"family_peak_bytes": 1002749952,
"import_ms": 9666.640497,
"catalog_ms": 1821.1315130000003,
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
},
{
"name": "topology-after-small-4",
"first_page_ms": 111.79372199999852,
"eight_later_pages_ms": 62.58284999999887,
"elapsed_seconds": 11.901,
"family_cpu_seconds": 11.707784,
"family_peak_bytes": 976646144,
"import_ms": 6473.91882,
"catalog_ms": 1203.5203539999998,
"guard_sha256": "d2047721e50dd244900eb514d07025386e60908f77bb546bee5684baa1da2f19"
},
{
"name": "topology-before-small-1",
"first_page_ms": 242.57812199999898,
"eight_later_pages_ms": 299.467095,
"elapsed_seconds": 17.585,
"family_cpu_seconds": 17.312434,
"family_peak_bytes": 1066864640,
"import_ms": 9102.074585,
"catalog_ms": 1829.971928,
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
},
{
"name": "topology-before-small-2",
"first_page_ms": 133.3346359999996,
"eight_later_pages_ms": 122.92684500000178,
"elapsed_seconds": 14.302,
"family_cpu_seconds": 13.936122,
"family_peak_bytes": 1062400000,
"import_ms": 7254.973107,
"catalog_ms": 1170.888121,
"guard_sha256": "760d541c60f4c0538f9c015964196e3be7bba12316f6085903244354e11aa992"
},
{
"name": "topology-before-small-4",
"first_page_ms": 100.81227200000103,
"eight_later_pages_ms": 76.37957900000038,
"elapsed_seconds": 12.0,
"family_cpu_seconds": 11.765305,
"family_peak_bytes": 983314432,
"import_ms": 6614.461618999999,
"catalog_ms": 1138.6469730000003,
"guard_sha256": "d2047721e50dd244900eb514d07025386e60908f77bb546bee5684baa1da2f19"
}
],
"token_estimates": {
"tokenizer": "tiktoken",
"version": "0.12.0",
"encoding": "cl100k_base",
"kind": "offline raw transport serialization estimate; one complete catalog plus each call name/arguments/reply exactly once; excludes initialization/framing, prompts, client rendering/discovery/replay, continuations and actual model usage; not an agent-token measurement",
"runs": [
{
"run": "topology-paired-before-native-1",
"catalog_tokens_estimated": 805215,
"named_call_arguments_tokens_estimated": 1200,
"reply_tokens_estimated": 43321,
"total_tokens_estimated": 849736,
"calls": 25
},
{
"run": "topology-paired-after-native-1",
"catalog_tokens_estimated": 805215,
"named_call_arguments_tokens_estimated": 1200,
"reply_tokens_estimated": 43321,
"total_tokens_estimated": 849736,
"calls": 25
},
{
"run": "topology-paired-before-javascript-1",
"catalog_tokens_estimated": 805215,
"named_call_arguments_tokens_estimated": 1354,
"reply_tokens_estimated": 49815,
"total_tokens_estimated": 856384,
"calls": 28
},
{
"run": "topology-paired-after-javascript-1",
"catalog_tokens_estimated": 805215,
"named_call_arguments_tokens_estimated": 1354,
"reply_tokens_estimated": 49815,
"total_tokens_estimated": 856384,
"calls": 28
},
{
"run": "topology-before-small-1",
"catalog_tokens_estimated": 805215,
"named_call_arguments_tokens_estimated": 1118,
"reply_tokens_estimated": 33970,
"total_tokens_estimated": 840303,
"calls": 19
},
{
"run": "topology-after-small-1",
"catalog_tokens_estimated": 805215,
"named_call_arguments_tokens_estimated": 1118,
"reply_tokens_estimated": 33970,
"total_tokens_estimated": 840303,
"calls": 19
}
]
},
"mixed_verification": [
{
"scenario": "native",
"heads_checked": 64,
"pages": 32,
"revised_unknown_id": "unk_057c8f2dbf30ec98851a66ee054366bc6e2e168874581e6f99cb80673d0ff9b8",
"revision_checked": 2,
"stale_update_rejected": true,
"tampered_import_rejected_atomically": true,
"unknowns_after_close": 0,
"old_reference_refused": true
},
{
"scenario": "javascript",
"heads_checked": 64,
"pages": 32,
"revised_unknown_id": "unk_057c8f2dbf30ec98851a66ee054366bc6e2e168874581e6f99cb80673d0ff9b8",
"revision_checked": 2,
"stale_update_rejected": true,
"tampered_import_rejected_atomically": true,
"unknowns_after_close": 0,
"old_reference_refused": true
}
],
"cli_parity": [
{
"scenario": "native",
"exact_json_equal": true,
"exact_bytes_equal": true,
"bytes": 7316,
"sha256_before": "eada9068db3b499da332b2c5226fdd3a48909ebcaa290fd72166325e2f664fca",
"sha256_after": "eada9068db3b499da332b2c5226fdd3a48909ebcaa290fd72166325e2f664fca",
"evidence_id": "ev_521a58e81427bb4b2c01b6a291bdfd7698068ee0eba371696daa9cc5ab32f3f0",
"evidence_links": [
"ev_14b4582084e425b597bea6246f0df94ab4856ac3ab9502c96551147210ca9c8b"
]
},
{
"scenario": "javascript",
"exact_json_equal": true,
"exact_bytes_equal": true,
"bytes": 8876,
"sha256_before": "1dc023e5ece8f9c8cea736cb0db4d4f60c9418f1573e1e46ff0ac55a4273cb5a",
"sha256_after": "1dc023e5ece8f9c8cea736cb0db4d4f60c9418f1573e1e46ff0ac55a4273cb5a",
"evidence_id": "ev_97cac46d5139f6c48c3142798087814dcf36a37a9c99f7253668576bd3ae282a",
"evidence_links": [
"ev_dd2ffe5ab2faa6e07e422886f64a87f99e35a0a1dce1acb5351b7a81cb2c9a11"
]
},
{
"scenario": "small",
"exact_json_equal": true,
"exact_bytes_equal": true,
"bytes": 7181,
"sha256_before": "00dd070690e7b113e0cad73f9f7bcda6f42c515f8fdcc5173e8815bd3748b084",
"sha256_after": "00dd070690e7b113e0cad73f9f7bcda6f42c515f8fdcc5173e8815bd3748b084",
"evidence_id": "ev_3dcbcc5b2002f1e6caf7df005e3fcfd06a3afe14ccd72fff924c8e76d8498914",
"evidence_links": [
"ev_775d6cc6d0de96402b6e57a4258a0b78c337cfb1b7760aa22c39c3ee450c7dff"
]
}
],
"stopped_source_validation": {
"reason": "Started during initial before-javascript-3; stopped only the new owned validation scope, then retried serially. Baseline repetition3 retained but excluded from uncontended exploratory comparisons; all final cohort runs retained.",
"status": "incomplete, shell reported exit241; no payload-completion receipt",
"elapsed_seconds": 17.272,
"family_cpu_seconds_last_observed": 12.204013,
"peak_cgroup_bytes_sampled": 589766656,
"final_scope_counters": null
},
"exploratory_costs": {
"completed_tasks": 21,
"elapsed_seconds_total": 649.262,
"family_cpu_seconds_total": 599.762599,
"family_peak_bytes_max": 1741795328,
"comparison_status": "Earlier candidate and exploratory inputs; excluded from final comparison, raw individual reports retained in private audit cache.",
"contended_baseline_task": {
"name": "before-javascript-3",
"revision": "2c4338db1fdb7773e05376301c558cff2a0b4b80",
"status": "passed",
"elapsed_seconds": 100.722,
"family_cpu_seconds": 82.656181,
"family_peak_bytes": 1290514432,
"excluded_from_final_comparison": true
}
},
"runtime_entry_sha256": {
"before": "1586cea18f4d4702c949502cfc143a7dfd2c28414779e190ce8a42fedf90bd74",
"after": "e05c70bed21c680445a5c6e314a16cce6f22f3909f93388bf602338062e63d1a"
},
"inputs": {
"native": {
"bundle_sha256": "180c6fe6d3706d9272c8bb33503c2dca6f145d5061e102e3457dd5f2d754b5c9",
"bundle_bytes": 1745224,
"source_evidence_id": "ev_14b4582084e425b597bea6246f0df94ab4856ac3ab9502c96551147210ca9c8b"
},
"javascript": {
"bundle_sha256": "747ff602093a24d4876e3eb47da3f7ef68716a10ac7c09938f43c3be5c33e31d",
"bundle_bytes": 21058430,
"source_evidence_id": "ev_dd2ffe5ab2faa6e07e422886f64a87f99e35a0a1dce1acb5351b7a81cb2c9a11"
},
"small": {
"bundle_sha256": "44ca561c2455706897d2ae8f51f45693e7276badd5e8454ec31d8c1b0cfb77eb",
"bundle_bytes": 73051,
"source_evidence_id": "ev_775d6cc6d0de96402b6e57a4258a0b78c337cfb1b7760aa22c39c3ee450c7dff"
}
},
"validation": {
"source": {
"executed": "130 source tests across5files; check:fast; build:cached",
"elapsed_seconds": 334.264,
"family_cpu_seconds": 326.441674,
"family_peak_bytes": 4487696384,
"payload_exit_code": 0,
"payload_cleanup_verified": true,
"oom_events_at_payload_exit": 0
},
"compiled_boundary": {
"executed": "36 CLI/MCP tests across4files",
"elapsed_seconds": 93.343,
"family_cpu_seconds": 91.926502,
"family_peak_bytes": 2190270464,
"payload_exit_code": 0,
"payload_cleanup_verified": true,
"oom_events_at_payload_exit": 0
},
"full_checks": {
"source": "3930a640fb8387cbd3079eee13fb90f82d1881dc",
"executed": "npm run check; npm run docs:check (five README consistency tests)",
"elapsed_seconds": 45.552,
"family_cpu_seconds": 43.309041,
"family_peak_bytes": 1127768064,
"payload_exit_code": 0,
"payload_cleanup_verified": true,
"oom_events_at_payload_exit": 0
},
"integration_current": {
"source": "3930a640fb8387cbd3079eee13fb90f82d1881dc",
"executed": "npm run test:focused -- three latest-main integration files; default test deadlines; 27 passed",
"elapsed_seconds": 62.437,
"family_cpu_seconds": 61.506209,
"family_peak_bytes": 1846538240,
"payload_exit_code": 0,
"payload_cleanup_verified": true,
"oom_events_at_payload_exit": 0
}
},
"comparison_probe": {
"scope": "Candidate-only internal JSON-owner regression, not baseline public-provider reproduction. Reflect.ownKeys instrumentation aborts at10000 inspections rather than consuming exponential CPU.",
"objects_per_graph": 27,
"before_candidate": "e30a94a4de6a3c4ec7c577c1551d2b448d82081e",
"before_inspections": 10001,
"before_status": "probe inspection budget exhausted",
"after_candidate": "646312dad75e2cb2becb0dd4139b99a1c04d6bcc",
"after_inspections": 54,
"after_borrowed": true
},
"main_integration": {
"main_commit": "bb212ffecb47e129cc569b9867b2b6e7c9b65685",
"merge_commit": "71e0b2739a9cf9903e1cfa63dc5a5b576040fea7",
"production_files_unchanged_from_measured_candidate": true,
"scope": "Historical integration at71e0b2739 with production source646312dad; all attempts retained. Current source-specific checks are listed separately.",
"attempts": [
{
"case": "initial_feature",
"entrypoint": "npm run test:focused",
"test_timeout_override_ms": null,
"tests_passed": 26,
"tests_failed": 1,
"tests_skipped": 0,
"payload_exit_code": 1,
"elapsed_seconds": 139.805,
"family_cpu_seconds": 125.252141,
"family_peak_bytes": 1994223616,
"payload_cleanup_verified": true,
"oom_events_at_payload_exit": 0
},
{
"case": "isolated_main",
"entrypoint": "vitest run; one named test",
"test_timeout_override_ms": null,
"tests_passed": 1,
"tests_failed": 0,
"tests_skipped": 10,
"payload_exit_code": 0,
"elapsed_seconds": 25.96,
"family_cpu_seconds": 25.502365,
"family_peak_bytes": 1221394432,
"payload_cleanup_verified": true,
"oom_events_at_payload_exit": 0
},
{
"case": "direct_main",
"entrypoint": "vitest run; three files",
"test_timeout_override_ms": null,
"tests_passed": 27,
"tests_failed": 0,
"tests_skipped": 0,
"payload_exit_code": 0,
"elapsed_seconds": 68.868,
"family_cpu_seconds": 68.020337,
"family_peak_bytes": 1605603328,
"payload_cleanup_verified": true,
"oom_events_at_payload_exit": 0
},
{
"case": "extended_feature",
"entrypoint": "vitest run; three files",
"test_timeout_override_ms": 120000,
"tests_passed": 27,
"tests_failed": 0,
"tests_skipped": 0,
"payload_exit_code": 0,
"elapsed_seconds": 75.866,
"family_cpu_seconds": 75.193788,
"family_peak_bytes": 1937674240,
"payload_cleanup_verified": true,
"oom_events_at_payload_exit": 0
},
{
"case": "exact_main",
"entrypoint": "npm run test:focused",
"test_timeout_override_ms": null,
"tests_passed": 27,
"tests_failed": 0,
"tests_skipped": 0,
"payload_exit_code": 0,
"elapsed_seconds": 66.443,
"family_cpu_seconds": 65.304325,
"family_peak_bytes": 2055409664,
"payload_cleanup_verified": true,
"oom_events_at_payload_exit": 0
},
{
"case": "exact_feature",
"entrypoint": "npm run test:focused",
"test_timeout_override_ms": null,
"tests_passed": 27,
"tests_failed": 0,
"tests_skipped": 0,
"payload_exit_code": 0,
"elapsed_seconds": 66.878,
"family_cpu_seconds": 66.012242,
"family_peak_bytes": 1907380224,
"payload_cleanup_verified": true,
"oom_events_at_payload_exit": 0
}
],
"measured_production_commit": "646312dad75e2cb2becb0dd4139b99a1c04d6bcc"
},
"small_cache_diagnostic": {
"scope": "Additional real public MCP run with return-preserving WeakSet method instrumentation. Timings excluded from before/after comparison. Owner identified from captured add stack; raw trace retained privately.",
"format_owner": "safeParseOwnedBinaryLayout",
"source_objects": 1,
"successful_admissions": 1,
"misses": 1,
"hits": 8,
"queries": 9,
"original_records_preserved": true,
"closed_session_cleared": true,
"small_workflow_latency_regression_retained": true,
"measured_production_commit": "646312dad75e2cb2becb0dd4139b99a1c04d6bcc"
},
"previous_checkpoints": [
{
"production_commit": "e30a94a4de6a3c4ec7c577c1551d2b448d82081e",
"report_commit": "3f271efdef8d71020bdcfd7b2d3d9817bf0a6ad8",
"report_url": "https://github.com/morluto/rea/blob/3f271efdef8d71020bdcfd7b2d3d9817bf0a6ad8/experiments/analysis-efficiency/immutable-parent-validation.json",
"completed_paired_tasks": 18,
"elapsed_seconds_total": 498.234,
"family_cpu_seconds_total": 477.513448,
"family_peak_bytes_max": 1519980544,
"scope": "Historical candidate before shared-node comparison memoization; every result retained, including slow npm repetition3. Not the final comparison."
},
{
"production_commit": "646312dad75e2cb2becb0dd4139b99a1c04d6bcc",
"report_commit": "fc55be1e05555c6b295209ef5f354617f663b8f8",
"report_url": "https://github.com/morluto/rea/blob/fc55be1e05555c6b295209ef5f354617f663b8f8/experiments/analysis-efficiency/immutable-parent-validation.json",
"completed_paired_tasks": 18,
"elapsed_seconds_total": 483.511,
"family_cpu_seconds_total": 458.990461,
"family_peak_bytes_max": 1598119936,
"scope": "Historical candidate before shared-node comparison memoization; every result retained, including slow npm repetition3. Not the final comparison."
}
],
"review_normalization_fix": {
"comment": "https://github.com/morluto/rea/pull/4962#discussion_r4305886491",
"before_source": "646312dad75e2cb2becb0dd4139b99a1c04d6bcc",
"before_pr_head": "fc55be1e05555c6b295209ef5f354617f663b8f8",
"expected_failing_regressions": 4,
"after_source": "3930a640fb8387cbd3079eee13fb90f82d1881dc",
"source_tests_passed": 130,
"structural_fix": "Two-way node identity mapping visits preserved descendants; already-owned JavaScript graph path keeps parsed metadata and avoids whole-graph comparison. Only the expensive imported fallback admits the full-result cache.",
"red_probe_cost": {
"elapsed_seconds": 5.332,
"family_cpu_seconds": 4.412336,
"family_peak_bytes": 438231040,
"payload_exit_code": 1,
"payload_cleanup_verified": true,
"expected_failures": 4
},
"current_dag_probe": {
"source": "3930a640fb8387cbd3079eee13fb90f82d1881dc",
"objects_per_graph": 27,
"inspections": 54,
"borrowed": true
}
},
"resource_stops": [
{
"task": "topology-paired-after-javascript-3",
"status": "incomplete",
"reason": "host_available_memory_early_stop",
"elapsed_seconds": 15.048,
"family_cpu_seconds_last_observed": 14.048702,
"peak_cgroup_bytes_sampled": 947695616,
"payload_completion_counters": null,
"owned_scope_after_stop": "inactive/dead",
"replacement": "fresh JavaScript pair4, after then before; final pairs1,2,4"
},
{
"task": "topology-before-small-3",
"status": "incomplete",
"reason": "host_available_memory_early_stop",
"elapsed_seconds": 11.83,
"family_cpu_seconds_last_observed": 11.535779,
"peak_cgroup_bytes_sampled": 947818496,
"payload_completion_counters": null,
"replacement": "fresh small pair4 after memory recovered; final small pairs1,2,4"
}
],
"unpaired_completed_attempts": [
{
"task": "topology-paired-before-javascript-3",
"status": "passed",
"original_records_preserved": true,
"elapsed_seconds": 38.862,
"family_cpu_seconds": 38.570479,
"family_peak_bytes": 1390338048,
"reason_not_in_paired_summary": "Its paired candidate was resource-stopped; kept separately, with both sides rerun as pair4."
}
]
}
@@ -0,0 +1,249 @@
# Reuse successful immutable parent validation
This checkpoint addresses [#4810](https://github.com/morluto/rea/issues/4810),
found in the [retained-investigation audit](retained-investigation-workflows.md).
It changes the shared format interpretation used by CLI and MCP views, without
adding a tool, changing a schema, projecting away source data, or rerunning a
provider.
## Question, ownership, and compatibility
The task resumes authentic exported Evidence in a fresh production stdio MCP
server, discovers its source, reads six original symbols or modules in two-row
pages, repeats that traversal three times, queries the existing Unknowns,
exports the investigation, and closes it. The original source records must
survive the round trip exactly. Pages are compared to original retained facts.
Independent ELF byte decoding checks symbol names, values, and ranges; JavaScript
checks cover selected source bytes and the `lib/cli.js` module identity, rather
than independently proving the entire application inventory or semantic graph.
Imported JavaScript graphs lack the producer's realm-private format proofs.
Previously each view repeated full schema, graph/hash, and cross-graph binding
validation. Native layout views likewise parsed the entire layout for every
page. An immutable Evidence root alone was insufficient: its selected normalized
result did not carry the JSON owner's private sealing proof.
The JSON owner now transfers that proof to a selected own member without another
walk or per-node registration. Native layouts and imported JavaScript results
record a **successful full
validation of the exact immutable source** in their owning WeakSets. Already
owned JavaScript graphs keep the existing metadata normalization and binding
checks, returning parsed metadata with their original graph references. That
path avoids rewalking a large owned graph just to borrow its wrapper. There is
no second
retained parsed graph, strong cache, cross-process cache, or live-state cache.
The schema implementation is fixed for the process; a different source object
must validate independently. Weak entries do not keep Evidence alive, and session
lookup continues to gate access after close.
Cache admission follows full validation and requires that parsing preserved the
source representation. A shared iterative JSON comparison checks own data keys,
scalar values, prototypes, and enumerability without calling accessors. Object
member order is immaterial; array order remains exact. Two-way identity maps
visit preserved descendants once and reject source-node splits or merges. Both
comparison maps are temporary and discarded on return. Parser defaults,
hidden-field removal, or other normalization retain the parser's
result and do not cache the source. Writable/configurable descriptor differences
are immaterial: the source is sealed while the temporary parse is mutable.
The JSON sealing boundary rejects proxies before reflection, so arbitrary
mutable proxy behavior cannot acquire an immutable proof. Mutable, merely
Object.freeze'd, failed, and abandoned inputs do not qualify for successful
format reuse.
No Evidence identity, source linkage, coverage, unknown, omission, error, or
caller-selected page changes are intended. Complete source Evidence remains
accessible through the existing contracts. The first imported view still pays for full
validation and the representation comparison; a one-off CLI invocation cannot
benefit from a cache in a previous process.
## Inputs and measurement scope
Baseline: `2c4338db1fdb7773e05376301c558cff2a0b4b80` (REA 6.6.0).
Node 24.18.0, npm 11.16.0, Linux x86-64 (AMD EPYC 7B12, kernel 6.1.0-53-cloud).
The ELF producer reports pwntools 4.15.0, pyelftools 0.33, and Unicorn 2.1.2.
The baseline's built runtime was preserved
before production changes. Candidate and baseline use the same locked packages.
- SQLite `/usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6`: SHA-256
`2e6eef9a727f081f0d453b4e5e6cbd8b9ef8b6f86cbf7681cbad444d3b0b55c8`;
26 sections, 1,479 symbols, 2,963 relocations. Original history: 1,745,224 bytes.
- Shipped npm 11.16.0: inventory digest
`f8e576a7d162542c616168b25a95a5f62b9accf29467cc37e149905ad4b8e603`;
five explicitly selected paths (`package.json`, `bin/npm-cli.js`, `lib/cli.js`,
`lib/cli/entry.js`, `lib/commands/install.js`), 2,822 application nodes,
1,796 semantic nodes, 13,682 semantic unknowns. The 1,416 unselected files stay
unknown. Original history: 21,058,430 bytes.
- Small case `/usr/bin/true`: SHA-256
`c79bf44242829108e323378531f4ac839513ca1fba45efd6583643526e1e9fd2`;
31 sections, 53 symbols, 66 relocations. Produced through the real production
`inspect_binary_layout` MCP operation. An independent raw ELF reader checked
every symbol name, value, and source range plus section/relocation counts.
The two larger histories were produced and independently checked in the linked
previous audit. They are historical Evidence, not a claim of fresh provider
analysis during a resumed view. All tasks here are static; no target executable
is run.
Each completed comparison includes startup, the complete 207-tool catalog,
import, queries, oracle checks, export, close, receipt writing, and SDK cleanup.
RPC timings exclude subsequent client-side oracle/receipt work. Fresh servers
separate first-view validation from later pages; OS file caches are uncontrolled.
Runs alternate baseline/candidate order. One CPU, 8 GiB hard family memory,
7 GiB early stop, no swap, and a 192-task ceiling bound each serial task.
Resource counters are observed at payload exit while the observer remains alive;
final whole-scope counters are unavailable. Kernel cgroup peaks include charged
file cache and descendants, rather than just the server's JS heap.
## Results and executed validation
Candidate production code: `3930a640fb8387cbd3079eee13fb90f82d1881dc`.
The [curated receipt](immutable-parent-validation.json) retains every final run,
offline token estimates, compatibility checks, and costs of previous attempts.
Values below are medians with min–max ranges across three fresh alternating pairs.
| Source | First page, before → after (ms) | Eight later pages, before → after (ms) |
| ---------- | ----------------------------------------------------- | -------------------------------------------------- |
| SQLite ELF | 183.56 [170.13–244.27] → 209.05 [203.11–216.88] | 469.89 [393.47–517.92] → 85.04 [65.10–89.01] |
| npm | 2441.08 [1681.26–2479.69] → 1978.73 [1864.89–2720.54] | 15534.41 [10378.13–16864.91] → 82.69 [75.19–84.13] |
| Small ELF | 133.33 [100.81–242.58] → 226.60 [111.79–556.22] | 122.93 [76.38–299.47] → 62.58 [57.69–96.24] |
| Source / runtime | Whole elapsed (s) | Family CPU (s) | Kernel family peak (MiB) |
| ------------------- | ------------------- | ------------------- | ------------------------ |
| SQLite ELF / before | 19.08 [15.26–20.00] | 18.75 [15.01–19.55] | 1013.8 [1009.5–1058.6] |
| SQLite ELF / after | 14.38 [14.06–17.25] | 14.09 [13.77–17.00] | 984.0 [957.1–1047.1] |
| npm / before | 39.63 [31.21–40.86] | 39.27 [30.87–40.49] | 1338.0 [1301.0–1543.7] |
| npm / after | 20.71 [20.01–23.02] | 20.50 [19.73–22.23] | 1286.8 [1250.6–1344.9] |
| Small ELF / before | 14.30 [12.00–17.59] | 13.94 [11.77–17.31] | 1013.2 [937.8–1017.4] |
| Small ELF / after | 12.31 [11.90–16.25] | 12.04 [11.71–16.07] | 956.3 [931.4–1007.5] |
| Source | Paired warm-read ratio, before / after | Paired first-view change, after − before (ms) |
| ---------- | -------------------------------------- | --------------------------------------------- |
| SQLite ELF | 5.53× [4.42–7.96] | +25.49 [-41.16–+46.75] |
| npm | 184.65× [125.50–224.30] | -462.34 [-614.80–+1039.29] |
| Small ELF | 1.28× [1.22–5.19] | +10.98 [-15.97–+422.88] |
Each pair matches the before/after runs with the same repetition number.
The paired effect can differ from the ratio of marginal medians when the host
speed changes. Positive first-view changes mean higher latency.
The repeated-read benefit is measured per source and pair above. First-view
validation
also performs the representation comparison and can be more expensive. Whole
task, CPU and memory figures above are measured SDK workflow costs, not a
general provider-analysis or agent-investigation speedup. The previous e30
checkpoint had a slow npm repetition on both sides, including a 65-second
candidate task; its cause was not profiled. The original checkpoint and every
fresh run remain retained. The previous `646312dad` checkpoint also had slower
small-case warm reads in two of three pairs (135.54 → 373.14 ms marginal
medians). Those negative results remain available in its linked historical
receipt; the current cohort improves all three small pairs, without establishing
a general small-input or whole-task benefit. No model-token saving, general memory improvement,
or complex-software investigation advantage is established.
All 18 fresh workflow runs preserve original records and canonical answer
digests. Every corresponding query's structured result and complete catalog
is equal. The three public CLI source pairs are also byte-for-byte identical.
The two additional mixed-history tasks preserve all 97 original records, check
64 follow-up heads over 32 pages and revision two, reject stale updates and
tampered imports atomically, clear Unknowns on close, and refuse old references.
Offline `tiktoken` 0.12.0 / `cl100k_base` estimates for one pass over the complete
catalog, named-call arguments and replies are **unchanged** within each pair:
849,736 native, 856,384 JavaScript, and 840,303 small-case tokens. The catalog
contributes 805,215 tokens alone.
These are raw transport serialization estimates, not actual model context or
usage; initialization/framing, prompts, client discovery, rendering/replay,
and continuations are unmeasured. Metadata paths differ from the historical
checkpoint; compare equivalent runs within each checkpoint.
Executed checks at the final production code:
- `npm run check` and `npm run docs:check`: pass, including five README
consistency tests.
- Three latest-main integration files through `npm run test:focused`, using
default deadlines: 27 tests pass at the current production source.
- 130 source tests across immutable JSON, native/JavaScript format validation,
and the shared application view service; `npm run check:fast` and
`npm run build:cached` pass.
- Four compiled CLI/MCP files (`evidenceBundleInline`, `serverUnknownsMcp`,
`analysisViewMcp`, and `analysisViewCli`): 36 tests pass.
- Real public stdio MCP: 18 fresh paired tasks and two additional mixed tasks;
public CLI: six invocations, with three exact output pairs. These corpus
workflows and four-file boundary checks have exit zero, verified payload
cleanup and zero observed payload-exit OOM events.
Internal and automated review reproduced **candidate-only** defects before integration.
Generic deep equality ignored hidden fields removed by parsing; the JSON owner
comparison now preserves that normalization. Review also reproduced aliases crossing
preserved subtrees and cloned metadata; four regressions failed against the prior
source. The comparer now visits preserved descendants with two-way identity mapping.
Already-owned JavaScript graphs keep their parsed-metadata fast path, so the generic
comparison and full-result cache only apply to the expensive imported fallback. Its
initial iterative traversal
also repeatedly expanded shared nodes: a valid 27-object graph exhausted an
instrumented 10,000-inspection budget. Tracking source-to-parsed node pairs
reduces that same compiled probe to 54 inspections and successful borrowing.
A parser that splits or merges source nodes keeps its parsed result rather than
expanding repeated paths. Deterministic regressions enforce the inspection
budget instead of relying on timing or letting a regression consume exponential
CPU. These are internal JSON-owner probes, not claimed baseline public-provider
reproductions.
The initial exploratory cohort and historical e30 paired checkpoint are not
the final comparison. One earlier source-check attempt was mistakenly started
during an initial baseline run; the newly started owned scope was stopped after
17.27 s and that check remains incomplete (exit 241). Observed counters, the
contended baseline, subsequent successful retries, and prior checkpoints remain
retained; no OOM or terminal-counter claim is inferred from that stopped task.
Two later tasks stopped because the host-available-memory guard reported its
early-stop condition: candidate JavaScript repetition three and baseline small
repetition three. Both incomplete attempts and the unmatched completed
JavaScript baseline remain separate. After memory recovered, both sides were
rerun as fresh pair four; final JavaScript and small samples use pairs 1, 2, 4.
Neither interrupted task is a completed comparison. The guard later gained
read-only host-memory/cgroup receipt fields; all resource thresholds and its
observer/stop logic remained unchanged.
Real Ghidra, Hopper, JEB, fresh JavaScript producer performance, and actual
agent trials are outside this checkpoint. Private WeakSet metadata/heap retention
was not measured independently; the absence of a strong retained parsed graph
is source-derived. The comparison maps exist only during the first
interpretation; measured family
peaks include their temporary cost.
## Latest-main integration and an unexplained timeout
Merge `71e0b2739` brings in main `bb212ffec`'s three test/config/guide fixes.
The current source passes all 27 tests in those three integration files through
`npm run test:focused` with the default deadlines. The earlier `646312dad`
checkpoint initially passed 26 and timed out the live identity/catalog test at
30 seconds. Matching official-command controls subsequently passed all 27 on
both unmodified main and that candidate. Direct/extended-deadline diagnostics
are recorded separately. Their passing results do not establish the cause of
the original timeout, which remains preserved and unexplained. No test-policy
or product file was changed to obtain a pass.
## Reproduction and retained evidence
Use `retained-investigation.mjs` from this directory with an authentic exported
bundle and its original source Evidence ID. Build both selected runtime revisions
with the pinned toolchain, preserving a separate baseline. For each runtime run:
```sh
python3 experiments/analysis-efficiency/resource-run.py --observe-payload-exit -- \
node experiments/analysis-efficiency/retained-investigation.mjs \
--runtime-root /absolute/runtime --revision REVISION \
--bundle /absolute/history.json --evidence-id ORIGINAL_EVIDENCE_ID \
--scenario native --delivery focused --output /absolute/new-receipt-directory
```
Use `--scenario javascript` for the npm history, and `--verify-mixed` with an
actual mixed investigation. Reuse the same bytes and source identity on both
sides. Do not regenerate parent identities or synthesize bundle records.
The checked-in wrapper defaults to a 4 GiB hard / 3 GiB early stop. This
checkpoint used a private resource-only copy with 8 GiB / 7 GiB thresholds,
a 16 GiB available-memory start floor and 8 GiB host early stop. Its source
and unchanged observer hashes are recorded in the curated receipt. This local
resource policy does not change the public workflow or global wrapper defaults.
Raw histories, transcripts, oracle outputs, and resource logs stay in the private
local audit cache; only curated receipts are committed.
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,238 @@
# JEB Linux CE native workflow checkpoint
This checkpoint qualifies Linux CE setup and selected native reads for
[#4996](https://github.com/morluto/rea/issues/4996). It changes documentation;
it does not introduce a provider cache or claim a before/after speedup.
The existing shared CLI/MCP implementation returned consistent facts and
Evidence, with one metadata request per persistent MCP task. Further wrapper
caching is not justified by these measurements.
## Pinned inputs and scope
Baseline: REA 6.6.0 at `7c458f04b9155707c8ba406f8b455b10016f1b99`, Node 24.18.0,
npm 11.16.0, Temurin 21.0.12.1, Linux x86-64
`6.1.0-53-cloud-amd64`. All six final sessions used the same compiled MCP
runtime digest, recorded in the [curated receipt](jeb-linux-native.json).
The official CE archive was obtained through
<https://www.pnfsoftware.com/dl?jeb4ce>: 697,549,250 bytes, observed SHA-256
`709e7fe8e88bb0e9e2bc03b028f8b9fc78cec0289d04abe54fc26cda6369e175`.
HTTPS download and ZIP CRC checks completed; a publisher signature was not
independently verified. This distribution identifies as
`5.48.0.202610071539`, serving MCP 1.3.0. The installation and first-use
activation remained private; no engine binaries, keys or raw decompilation
are included here.
| Artifact | Bytes | Independent check |
| -------------------------------------------------------- | --------: | ----------------------------------------------------------------------------- |
| Authored C ELF, ET_EXEC | 17,360 | Source arithmetic and GNU `nm` symbol |
| Debian `libsqlite3.so.0.8.6`, package `3.40.1-2+deb12u2` | 1,437,848 | GNU `nm` and `objdump`: exported version function returns immediate `3040001` |
Target SHA-256 digests:
- Fixture: `1a6f55303712e096ff5196c7fd6e8acf75473fbf0f7467d7b315f96f50b3f841`.
- SQLite: `2e6eef9a727f081f0d453b4e5e6cbd8b9ef8b6f86cbf7681cbad444d3b0b55c8`.
Neither target was executed. The fixture's expected arithmetic was `input * 7 + 13`.
That check does not establish inferred ABI, signedness or equivalence for every
input. SQLite's constant-return oracle is also narrower than complete-program
correctness.
## Setup and item identity
The current official CE build successfully accepted `-c --mcp`, using
`--interface=127.0.0.1`, a dedicated port and `--infile` for the selected ELF.
This agrees with the [current upstream MCP manual](https://www.pnfsoftware.com/jeb/manual/mcp/).
The earlier documented licensed-build result used a programmatic launcher;
its CLI behavior should not be generalized to all 5.48 distributions.
Code-item references also need their owning representation. The CE native
`list_code_items` tool returned `sqlite3_libversion_number` for a filtered
method query. That exact reference succeeded through REA. Earlier raw ELF
address attempts did not complete successfully. A numeric reference did work
for the authored ET_EXEC pilot, so neither observation establishes a universal
numeric-address rule. Preserve the engine's reference and explicit unit path;
keep file offsets and independently obtained ELF addresses separate.
The native CE catalog advertised 27 tools. REA currently exposes four JEB
operations; native code-item discovery is not among them. The final tasks
therefore start with a known reference. This is a selected-read qualification,
not a complete method-discovery or provider-parity comparison.
## Completed workflow and accounting
Three alternating fresh-engine sessions per artifact completed the same task:
1. Start the private headless engine; prove that its child PID owns the
selected IPv4 or mapped-IPv6 loopback listener before connecting.
2. Connect the production stdio MCP runtime and discover its full catalog.
3. Inspect JEB, list all three units, then concatenate four one-item pages
including the empty remainder and compare with the original listing.
4. Decompile the selected method, check the independent oracle and complete
inline Evidence, then repeat it six times with identical facts and Evidence IDs.
5. Check a missing item returns an error; make one real CLI query and compare
its normalized facts, raw text and Evidence ID with MCP; close the client.
6. Verify target bytes are unchanged and the borrowed engine survived REA
cleanup, then stop the harness-owned engine.
Each task made 15 public MCP calls and one CLI parity query. Request tracing
observed one upstream catalog walk, one `get_client_information`, five
`list_units` and eight `decompile_code_item` calls. Explicit metadata refresh
and reconnect behavior were not part of this repeat-read measurement.
Tracing covered the production MCP child; the separate CLI query's upstream
requests were not instrumented.
Evidence retained `subject: null` and the artifact-identity limitation;
an independent native project-digest observation was not substituted for
identity atomically bound to each read.
All heavy work ran serially inside a cgroup v2 family with an 8 GiB hard cap,
7 GiB early stop, one CPU quota and affinity, and zero swap. Startup required
16 GiB host availability; a running task would stop below 8 GiB. The JVM used
`-Xmx1536m -XX:ActiveProcessorCount=1`; the production MCP child used a
2048 MiB Node heap, and the harness/CLI inherited a 3072 MiB Node heap limit.
These are audit settings, not general engine-size recommendations.
OS and vendor file caches were not flushed. “First” means the first selected
function read in a fresh JVM; “warm” means repeated reads in that same engine.
The private fetch observer recorded request names and byte counts without
cloning response bodies; its overhead is included.
Whole-task counters include the JVM, MCP client, production runtime, CLI
parity, native oracle and cleanup, including cgroup-charged file cache.
Host-admission wait is recorded separately. Outer guard/systemd-manager costs
were not measured. Payload-exit counters and cleanup were verified while an
observer remained alive; disappeared final whole-scope counters remain unknown.
## Measured costs
Medians with observed minimum–maximum across three completed sessions:
| Cost | Authored ELF | Shipped SQLite |
| ---------------------------------------- | -------------------------: | -------------------------: |
| First selected MCP read | 4.134 s (3.845–5.037) | 24.508 s (22.823–49.977) |
| Six warm MCP reads, total | 253.2 ms (113.4–842.9) | 190.0 ms (160.9–416.9) |
| Whole guarded task elapsed | 27.638 s (26.749–32.622) | 48.296 s (45.625–82.039) |
| Whole family CPU | 27.291 s (26.399–32.155) | 47.753 s (45.370–78.877) |
| Kernel family peak, including file cache | 1540.1 MiB (1531.5–1614.9) | 1799.6 MiB (1714.7–1865.8) |
The slower third sessions remain in the results. Their extra elapsed and CPU
costs are unexplained; no favorable resampling, stable latency improvement or
memory reduction is claimed. Warm reads are already short relative to first
reads. Adding another live-project cache would introduce identity and
invalidation obligations without a demonstrated bottleneck here.
All six completed payloads recorded zero memory-limit, task-limit, OOM and
OOM-kill events. This is a payload-exit observation, not a claim that missing
final counters equal zero.
## Context estimates
The unchanged production catalog contained 207 tools, 3,158,927 serialized
bytes, SHA-256
`a1851aa1bfe3d72d6be6bec130519dbdfc1ee49288320cfac0144ced77755c61`.
An offline tiktoken 0.12.0 `cl100k_base` count assigned 805,330 tokens to one
raw catalog page-array in every session.
| Estimated serialization | Authored ELF median | SQLite median |
| ---------------------------------------------------- | ------------------: | ------------: |
| Catalog + named MCP arguments + raw replies | 814,268 | 814,878 |
| Above, plus selected CLI arguments and parity stdout | 814,610 | 815,249 |
| MCP text reply blocks alone | 3,957 | 4,203 |
Raw replies contain both text and structured copies. These counts describe
the validation exchange, including its extra pagination, repeated and negative
queries. Actual model usage, prompts, protocol/initialization framing, client
filtering/rendering/replay, shell framing and context for independent oracles
were not measured. No model-token savings or complex-investigation advantage
is established. Catalog delivery is a shared concern; existing
[PR #3026](https://github.com/morluto/rea/pull/3026) already explores family
selection and is outside this checkpoint.
## Preserved attempts and verification limits
The JSON receipt retains costs and outcomes for all earlier attempts:
- Three host-memory refusals happened before scope/payload creation.
- First-use EULA and missing-key attempts exited before analysis. Although
JEB exited zero, the harness correctly reported failure.
- The 30-second raw-address SQLite attempt returned typed `provider_timeout`;
the 60-second raw-address attempt returned an upstream refusal. Neither is a
completed benchmark or proof of a REA defect.
- A producer-reference SQLite read succeeded, but the initial test oracle
rejected its valid C `L` integer suffix. The oracle was corrected before
the final cohort; that incomplete workflow and its cost remain recorded.
- Setup, diagnostic help, successful pilots, native discovery checks and
offline tokenizer work are recorded separately from the final cohort.
This audit does not qualify CE Java/Android decompilation, GUI project opening,
Windows/macOS, more than 100 units, real caller cancellation, or recovery after
engine restart. Existing reconnect/invalidation work is
[PR #3754](https://github.com/morluto/rea/pull/3754). The selected function and
constant checks do not establish broader software-analysis effectiveness.
## Release 6.7.0 integration control
Main advanced to `a25eb5fbb424197cb8e699e15a82a6ff869658aa` during review.
The branch rebased onto that release checkpoint, rebuilt, and passed
`docs:check` plus uncached typecheck/lint. Two additional fresh-engine public
workflows passed against `837718b1968b5fd90762c4c54895b11e9f5478fd`: one
authored ELF and one shipped SQLite, adding 30 MCP calls and two CLI parity
queries. They verify the rebuilt 6.7.0 runtime and remain separate from the
six-session performance cohort above. Their runtime/catalog digests and
process-family costs are recorded in the JSON receipt.
The first additional SQLite attempt was stopped after 28.244 seconds when
host available memory fell below the unchanged 8 GiB stop threshold. Final
scope limit/OOM counters were observed as zero; payload-exit counters were
unavailable, so that attempt is incomplete. Its sampled peak is not a kernel
peak claim. Target bytes remained unchanged. The successful retry used a
stricter 24 GiB admission wait with the original 8 GiB family cap, 7 GiB early
stop, one CPU and zero swap. No failed attempt was replaced or counted passed.
## Reproduction entrypoints
The small source fixture was compiled with `gcc -O1 -g -fno-pie -no-pie`:
```c
#include <stdio.h>
__attribute__((noinline)) int rea_probe_value(int input) { return input * 7 + 13; }
int main(void) { puts("rea_jeb_native_probe"); return rea_probe_value(4) == 41 ? 0 : 1; }
```
Its recorded digest pins the tested build; debug/source paths can change a
rebuilt binary's digest. The source expression and independently resolved
symbol remain the fixture oracle.
From the caller's JEB directory, after completing its official first-use setup
and selecting JDK 21 through `JAVA_HOME`:
```sh
"$JAVA_HOME/bin/java" -Xmx1536m -XX:ActiveProcessorCount=1 -jar bin/app/jeb.jar -c --mcp \
--interface=127.0.0.1 --port=18425 \
--infile=/usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
```
In the pinned REA checkout, with the pinned Node toolchain and built runtime:
```sh
export REA_JEB_MCP_URL=http://127.0.0.1:18425/mcp
export REA_JEB_MCP_TIMEOUT_MS=60000
node scripts/rea.mjs inspect-jeb-client --json
node scripts/rea.mjs list-jeb-units --count 100 --json
node scripts/rea.mjs decompile-jeb-item sqlite3_libversion_number method \
--unit-path libsqlite3.so.0.8.6/libsqlite3.so.0.8.6/x86_64-image --json
nm -D --defined-only /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
objdump -d --disassemble=sqlite3_libversion_number \
/usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
```
The listed commands reproduce individual reads and the file oracle; the
full measurement also used the MCP sequence and process-family observer
described above. JVM heap flags alone do not impose its family/host guards.
Private raw transcripts and engine documents are excluded from the repository.
The baseline gate passed `npm run docs:check`, `npm run check:fast` and
`git diff --check`; its typecheck/lint were cache hits. The release integration
gate above also passed with uncached typecheck/lint. The first gate attempt was refused
before scope creation; the admitted gate passed under the unchanged bounds.
The runtime results above were executed against the pinned baseline.
@@ -0,0 +1,635 @@
{
"runtime_revision": "f4322b15ed1423d736bd2572cf7a8eadf2b466e1",
"host": "Linux x86-64",
"node": "24.18.0",
"npm": "11.16.0",
"codex_cli": "0.161.0",
"requested_model": "gpt-6.1-sol",
"reasoning": "high",
"resolved_model_identity": "unknown",
"code_mode": false,
"client_enabled_tools": [
"import_evidence_bundle",
"get_evidence_bundle",
"inspect_analysis_view",
"list_unknowns"
],
"catalog": {
"tools": 207,
"serialized_bytes": 3152125,
"sha256": "4f31d9d56c400bc48f5639a9dc4b4cdad7faf862e4be81641d94fa70552438e4"
},
"bounds": {
"cpu_quota_percent": 100,
"family_hard_mib": 8192,
"early_stop_mib": 7168,
"swap": false,
"tasks": 192
},
"counter_scope": "Payload-exit counters with observer alive; kernel family peaks include file cache; terminal whole-scope counters and remote model compute unknown",
"sources": {
"native": {
"target_sha256": "2e6eef9a727f081f0d453b4e5e6cbd8b9ef8b6f86cbf7681cbad444d3b0b55c8",
"sections": 26,
"symbols": 1479,
"relocations": 2963,
"all_symbol_locations_names_values_agree_with_independent_ELF_parser": true
},
"javascript": {
"package": "npm",
"version": "11.16.0",
"source_files": [
{
"path": "package.json",
"sha256": "60ff022e32b5d8291d3e09849fd0cee6bbbff4e4d8352ad258214b1adf070714"
},
{
"path": "bin/npm-cli.js",
"sha256": "8e5f6f3429f8cdbe693cdc29904e9d5a7b127a494bd15c804bd54c7403bfcbe7"
},
{
"path": "lib/cli.js",
"sha256": "67666f06479f9b0bbc01412c198caadd4287d34d5ed74a02871ea78f186451e7"
},
{
"path": "lib/cli/entry.js",
"sha256": "667a44c1fa8795fdbba8ad843736ca7f8f24e64511985e9a7060b002123560c6"
},
{
"path": "lib/commands/install.js",
"sha256": "f7a60e8b40ab1bece76a2441b8b15378d199f05c6050eebe455cd9961f67b816"
}
],
"lib_cli_module": {
"node_id": "jag_node_131257ad52f3333971c2a5ed325c64357e52604489e42ccc67a8849770cb5b10",
"kind": "javascript-module",
"path": "lib/cli.js"
},
"source_bytes_checked": true,
"inventory_completeness": "producer-relative, not independently established",
"unselected_sources": 1416
}
},
"sdk": [
{
"name": "sdk-native-complete-1",
"status": "passed",
"answer_sha256": "4e28c2c2a25620576d27abe846a05835ec49386b889779e33410c31971523b54",
"query_ms": 1328.7,
"query_reply_bytes": 11224227,
"rpc_count": 7,
"elapsed_seconds": 14.117,
"cpu_seconds": 13.915,
"peak_family_mib": 1160.8,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
},
"payload_cleanup_verified": true,
"response_capacity_refused": false,
"warm_page_ms": []
},
{
"name": "sdk-native-complete-2",
"status": "passed",
"answer_sha256": "4e28c2c2a25620576d27abe846a05835ec49386b889779e33410c31971523b54",
"query_ms": 1385.4,
"query_reply_bytes": 11224227,
"rpc_count": 7,
"elapsed_seconds": 14.722,
"cpu_seconds": 14.485,
"peak_family_mib": 1109.9,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
},
"payload_cleanup_verified": true,
"response_capacity_refused": false,
"warm_page_ms": []
},
{
"name": "sdk-native-focused-1",
"status": "passed",
"answer_sha256": "4e28c2c2a25620576d27abe846a05835ec49386b889779e33410c31971523b54",
"query_ms": 529.7,
"query_reply_bytes": 167985,
"rpc_count": 25,
"elapsed_seconds": 12.907,
"cpu_seconds": 12.679,
"peak_family_mib": 934.8,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
},
"payload_cleanup_verified": true,
"response_capacity_refused": false,
"warm_page_ms": [44.1, 59.6, 24.0, 30.8, 26.7, 47.4]
},
{
"name": "sdk-native-focused-2",
"status": "passed",
"answer_sha256": "4e28c2c2a25620576d27abe846a05835ec49386b889779e33410c31971523b54",
"query_ms": 572.2,
"query_reply_bytes": 167985,
"rpc_count": 25,
"elapsed_seconds": 13.316,
"cpu_seconds": 13.116,
"peak_family_mib": 1000.0,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
},
"payload_cleanup_verified": true,
"response_capacity_refused": false,
"warm_page_ms": [44.0, 64.6, 26.8, 33.2, 29.8, 50.4]
},
{
"name": "sdk-javascript-complete-1",
"status": "passed",
"answer_sha256": "0fe3ad249eb1a7233a4966a56652c173667c48fdaf395b6c08fc6901ef3b46f7",
"query_ms": 11241.5,
"query_reply_bytes": 212856,
"rpc_count": 31,
"elapsed_seconds": 26.3,
"cpu_seconds": 26.1,
"peak_family_mib": 1333.5,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
},
"payload_cleanup_verified": true,
"response_capacity_refused": true,
"warm_page_ms": [1124.6, 1073.8, 1474.7, 1074.7, 1081.6, 1109.5]
},
{
"name": "sdk-javascript-complete-2",
"status": "passed",
"answer_sha256": "0fe3ad249eb1a7233a4966a56652c173667c48fdaf395b6c08fc6901ef3b46f7",
"query_ms": 11938.1,
"query_reply_bytes": 212856,
"rpc_count": 31,
"elapsed_seconds": 27.105,
"cpu_seconds": 26.87,
"peak_family_mib": 1320.9,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
},
"payload_cleanup_verified": true,
"response_capacity_refused": true,
"warm_page_ms": [1155.4, 1096.2, 1489.3, 1121.3, 1088.3, 1484.6]
},
{
"name": "sdk-javascript-focused-2",
"status": "passed",
"answer_sha256": "0fe3ad249eb1a7233a4966a56652c173667c48fdaf395b6c08fc6901ef3b46f7",
"query_ms": 10967.3,
"query_reply_bytes": 208350,
"rpc_count": 28,
"elapsed_seconds": 25.708,
"cpu_seconds": 25.505,
"peak_family_mib": 1344.0,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
},
"payload_cleanup_verified": true,
"response_capacity_refused": false,
"warm_page_ms": [1342.7, 1085.1, 1055.0, 1034.6, 1082.7, 1440.7]
},
{
"name": "sdk-javascript-focused-3",
"status": "passed",
"answer_sha256": "0fe3ad249eb1a7233a4966a56652c173667c48fdaf395b6c08fc6901ef3b46f7",
"query_ms": 10990.4,
"query_reply_bytes": 208350,
"rpc_count": 28,
"elapsed_seconds": 26.197,
"cpu_seconds": 25.98,
"peak_family_mib": 1336.0,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
},
"payload_cleanup_verified": true,
"response_capacity_refused": false,
"warm_page_ms": [1338.1, 1088.5, 1069.1, 1038.3, 1087.5, 1483.0]
},
{
"name": "sdk-mixed-native",
"status": "passed",
"answer_sha256": "4e28c2c2a25620576d27abe846a05835ec49386b889779e33410c31971523b54",
"query_ms": 560.0,
"query_reply_bytes": 167991,
"rpc_count": 25,
"elapsed_seconds": 16.933,
"cpu_seconds": 16.714,
"peak_family_mib": 1261.4,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
},
"payload_cleanup_verified": true,
"response_capacity_refused": false,
"warm_page_ms": [46.9, 51.0, 36.3, 24.4, 38.6, 43.8]
},
{
"name": "sdk-mixed-javascript",
"status": "passed",
"answer_sha256": "0fe3ad249eb1a7233a4966a56652c173667c48fdaf395b6c08fc6901ef3b46f7",
"query_ms": 10904.5,
"query_reply_bytes": 208356,
"rpc_count": 28,
"elapsed_seconds": 26.5,
"cpu_seconds": 26.297,
"peak_family_mib": 1346.5,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
},
"payload_cleanup_verified": true,
"response_capacity_refused": false,
"warm_page_ms": [1321.9, 1063.3, 1070.8, 1309.9, 1041.7, 1074.2]
}
],
"model_trials": [
{
"name": "model-javascript-complete-1",
"facts_pass": true,
"elapsed_seconds": 76.49,
"cpu_seconds": 24.687,
"peak_family_mib": 818.7,
"input_tokens": 454491,
"cached_input_tokens": 396288,
"uncached_input_tokens": 58203,
"output_tokens": 2033,
"reported_reasoning_output_tokens": 207,
"mcp_calls_including_discovery": 9,
"payload_cleanup_verified": true,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
}
},
{
"name": "model-javascript-complete-2",
"facts_pass": true,
"elapsed_seconds": 58.335,
"cpu_seconds": 14.993,
"peak_family_mib": 971.4,
"input_tokens": 328066,
"cached_input_tokens": 280704,
"uncached_input_tokens": 47362,
"output_tokens": 1676,
"reported_reasoning_output_tokens": 129,
"mcp_calls_including_discovery": 9,
"payload_cleanup_verified": true,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
}
},
{
"name": "model-javascript-focused-1",
"facts_pass": false,
"elapsed_seconds": 34.206,
"cpu_seconds": 16.31,
"peak_family_mib": 811.9,
"input_tokens": 167470,
"cached_input_tokens": 136320,
"uncached_input_tokens": 31150,
"output_tokens": 633,
"reported_reasoning_output_tokens": 172,
"mcp_calls_including_discovery": 4,
"payload_cleanup_verified": true,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
}
},
{
"name": "model-javascript-focused-2",
"facts_pass": true,
"elapsed_seconds": 66.455,
"cpu_seconds": 14.337,
"peak_family_mib": 955.3,
"input_tokens": 339528,
"cached_input_tokens": 307456,
"uncached_input_tokens": 32072,
"output_tokens": 2051,
"reported_reasoning_output_tokens": 169,
"mcp_calls_including_discovery": 10,
"payload_cleanup_verified": true,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
}
},
{
"name": "model-javascript-guided-complete-1",
"facts_pass": true,
"elapsed_seconds": 69.206,
"cpu_seconds": 13.65,
"peak_family_mib": 888.8,
"input_tokens": 168862,
"cached_input_tokens": 121728,
"uncached_input_tokens": 47134,
"output_tokens": 2385,
"reported_reasoning_output_tokens": 737,
"mcp_calls_including_discovery": 6,
"payload_cleanup_verified": true,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
}
},
{
"name": "model-javascript-guided-complete-2",
"facts_pass": false,
"elapsed_seconds": 29.187,
"cpu_seconds": 15.105,
"peak_family_mib": 697.4,
"input_tokens": 28696,
"cached_input_tokens": 23296,
"uncached_input_tokens": 5400,
"output_tokens": 610,
"reported_reasoning_output_tokens": 379,
"mcp_calls_including_discovery": 0,
"payload_cleanup_verified": true,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
}
},
{
"name": "model-javascript-guided-focused-1",
"facts_pass": true,
"elapsed_seconds": 53.464,
"cpu_seconds": 12.059,
"peak_family_mib": 944.5,
"input_tokens": 124218,
"cached_input_tokens": 92416,
"uncached_input_tokens": 31802,
"output_tokens": 1746,
"reported_reasoning_output_tokens": 330,
"mcp_calls_including_discovery": 5,
"payload_cleanup_verified": true,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
}
},
{
"name": "model-javascript-guided-focused-2",
"facts_pass": true,
"elapsed_seconds": 58.526,
"cpu_seconds": 22.852,
"peak_family_mib": 924.2,
"input_tokens": 129134,
"cached_input_tokens": 97536,
"uncached_input_tokens": 31598,
"output_tokens": 1478,
"reported_reasoning_output_tokens": 69,
"mcp_calls_including_discovery": 5,
"payload_cleanup_verified": true,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
}
},
{
"name": "model-native-complete-1",
"facts_pass": true,
"elapsed_seconds": 52.103,
"cpu_seconds": 8.686,
"peak_family_mib": 1028.5,
"input_tokens": 287913,
"cached_input_tokens": 249984,
"uncached_input_tokens": 37929,
"output_tokens": 1724,
"reported_reasoning_output_tokens": 121,
"mcp_calls_including_discovery": 4,
"payload_cleanup_verified": true,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
}
},
{
"name": "model-native-complete-2",
"facts_pass": true,
"elapsed_seconds": 58.261,
"cpu_seconds": 9.102,
"peak_family_mib": 837.4,
"input_tokens": 348976,
"cached_input_tokens": 286976,
"uncached_input_tokens": 62000,
"output_tokens": 2009,
"reported_reasoning_output_tokens": 246,
"mcp_calls_including_discovery": 5,
"payload_cleanup_verified": true,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
}
},
{
"name": "model-native-focused-1",
"facts_pass": true,
"elapsed_seconds": 62.078,
"cpu_seconds": 10.198,
"peak_family_mib": 823.9,
"input_tokens": 335445,
"cached_input_tokens": 293120,
"uncached_input_tokens": 42325,
"output_tokens": 1924,
"reported_reasoning_output_tokens": 87,
"mcp_calls_including_discovery": 8,
"payload_cleanup_verified": true,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
}
},
{
"name": "model-native-focused-2",
"facts_pass": true,
"elapsed_seconds": 57.933,
"cpu_seconds": 8.058,
"peak_family_mib": 821.3,
"input_tokens": 286938,
"cached_input_tokens": 256384,
"uncached_input_tokens": 30554,
"output_tokens": 1831,
"reported_reasoning_output_tokens": 106,
"mcp_calls_including_discovery": 8,
"payload_cleanup_verified": true,
"memory_events": {
"low": 0,
"high": 0,
"max": 0,
"oom": 0,
"oom_kill": 0,
"oom_group_kill": 0
}
}
],
"failed_trials": {
"model-javascript-focused-1": "Wrong connector discovery; approval-policy refusal; no source facts established.",
"model-javascript-guided-complete-2": "Premature agent refusal with no REA RPC; asserted tool unavailability not established by a connection/discovery observation."
},
"pilot_failures": [
"Private pilot oracle read view.item instead of view.items; corrected before the committed SDK cohorts. No product failure inferred."
],
"decision": "Existing query APIs suffice for these bounded questions; no general token/time advantage established. Separate imported immutable-format validation optimization is tracked in #4810.",
"review_followup_verification": {
"review-mixed-native": {
"status": "passed",
"checks": {
"heads_checked": 64,
"pages": 32,
"revised_unknown_id": "unk_057c8f2dbf30ec98851a66ee054366bc6e2e168874581e6f99cb80673d0ff9b8",
"revision_checked": 2,
"stale_update_rejected": true,
"tampered_import_rejected_atomically": true,
"unknowns_after_close": 0,
"old_reference_refused": true
},
"original_records_preserved": true,
"elapsed_seconds": 18.444,
"cpu_seconds": 18.16515,
"peak_family_mib": 1401.6328125,
"oom_events_at_payload_exit": 0,
"payload_cleanup_verified": true
},
"review-mixed-javascript": {
"status": "passed",
"checks": {
"heads_checked": 64,
"pages": 32,
"revised_unknown_id": "unk_057c8f2dbf30ec98851a66ee054366bc6e2e168874581e6f99cb80673d0ff9b8",
"revision_checked": 2,
"stale_update_rejected": true,
"tampered_import_rejected_atomically": true,
"unknowns_after_close": 0,
"old_reference_refused": true
},
"original_records_preserved": true,
"elapsed_seconds": 27.617,
"cpu_seconds": 27.275299,
"peak_family_mib": 1444.30859375,
"oom_events_at_payload_exit": 0,
"payload_cleanup_verified": true
},
"deterministic_receipt_tests": {
"passed": 4,
"scope": "Rejected SDK call, protocol error, receipt-write failure, simultaneous call/write failure; not real-provider evidence"
},
"cleanup_receipt_regressions": {
"passed": 6,
"scope": "Real stream/file refusals, independent peer close, and injected partial publication at the filesystem boundary. Original failure and call costs survive; failed cleanup retains workspace ownership. Harness failure checks, not real-provider evidence.",
"baseline_reproduction": {
"revision": "b025a919b36ec091ca888339df6b24e78e89b79b",
"method": "Execute the prior committed finalization block with deterministic workspace or log rejection; not a production-provider reproduction.",
"workspace_error": {
"file_receipt_attempts": 0,
"stdout_receipt_attempts": 0
},
"log_error": {
"file_receipt_attempts": 0,
"stdout_receipt_attempts": 0
}
},
"partial_retry_baseline": {
"revision": "5d6259e658ee54fc9829b9143a8721a2ab426e80",
"regression_exit_code": 1,
"observed_saved_report": "partial",
"method": "Run the unchanged partial-write regression against the prior committed finalizer with deterministic filesystem-boundary failure injection."
}
}
}
}
@@ -0,0 +1,201 @@
# Retained investigation query checkpoint
[#3134](https://github.com/morluto/rea/issues/3134) measured existing retained
queries on Linux. Selected reads preserved the requested facts, original
Evidence and Unknown revisions, including a mixed investigation. Existing
contracts suffice for these questions; this checkpoint adds no production API,
cache, persistence or default change.
Imported JavaScript pages repeatedly validated their entire source graph. That
separate, observed bottleneck is owned by
[#4810](https://github.com/morluto/rea/issues/4810). Smaller replies did not
establish a general model-token or whole-task latency improvement.
The [curated measurements](retained-investigation-results.json) retain individual
SDK/model costs and unsuccessful attempts. Raw bundles, transcripts, profiles
and detailed receipts stay private and are not build inputs.
## Sources and questions
Runtime: `f4322b15ed1423d736bd2572cf7a8eadf2b466e1`, Linux x86-64,
Node 24.18.0/npm 11.16.0. The SDK discovered all 207 advertised tools
(3,152,125 serialized catalog bytes). Model trials used Codex CLI 0.161.0,
requested `gpt-6.1-sol`/high, direct MCP, and the same four enabled tool names:
`import_evidence_bundle`, `get_evidence_bundle`, `inspect_analysis_view`, and
`list_unknowns`. The resolved service-side model identity is unknown. These are
selected-client-tool trials, not an evaluation of full-catalog agent discovery.
Two public source workflows produced authentic bundles through production MCP:
- Native: `inspect_binary_layout` on Debian's `libsqlite3.so.0.8.6`, SHA-256
`2e6eef9a727f081f0d453b4e5e6cbd8b9ef8b6f86cbf7681cbad444d3b0b55c8`.
Pwntools 4.15.0/pyelftools 0.33/Unicorn 2.1.2 observed 26 sections, 1,479
symbols and 2,963 relocations. An independent ELF64 decoder checked section
and relocation counts and every symbol's table/entry index, value, raw name
bytes and source entry range against the original file.
- JavaScript: shipped npm 11.16.0, directory inventory digest
`f8e576a7d162542c616168b25a95a5f62b9accf29467cc37e149905ad4b8e603`.
`analyze_javascript_application` selected exactly `package.json`,
`bin/npm-cli.js`, `lib/cli.js`, `lib/cli/entry.js`, and
`lib/commands/install.js`. It parsed four JavaScript files; 1,416 other
relevant files remained unselected. The complete retained result contained
2,822 application nodes, 1,796 semantic nodes and 13,682 semantic unknowns.
Source-file digests and the `lib/cli.js` source-module identity were checked
against shipped bytes. Inventory/discovery completeness remains producer-relative.
Analyst-recorded Unknowns came from the actual source limitations: five native
and seven JavaScript questions. These registry heads are distinct from the
13,682 semantic-graph unknowns. The exported histories contained 15 and 17
records, respectively, and approximately 1.75 and 21.06 MB of canonical JSON.
No synthetic payload arrays were used.
The SDK task read the first six original symbol/module rows and the relevant
current Unknown heads. Complete delivery and selected delivery answered the
same question; selected delivery intentionally exercised two-row pages. Each
fresh server performed three traversals, exported again and closed. The model
task additionally requested native identity/counts, or the five JavaScript source
choices and exact `lib/cli.js` source module, with explicit runtime uncertainty.
Compare arms within each declared task, not SDK costs against model costs.
## Correctness, capacity and lifetime
Both fresh-server SDK repetitions passed in both delivery arms. Selected rows,
ordering and counts agreed with original records; all original records and
Unknown revisions survived export/import, including links, omitted fields,
nulls and scalar types. Query calls did not rerun either producer. The resumed
servers had no native producer configuration.
The JavaScript complete bundle exceeded the default 10 MiB receive-buffer
boundary. Its typed capacity refusal preserved the complete retained data and a
usable connection. Complete-arm costs include that refusal and subsequent
selected-view recovery; they are not timings for a successful complete transfer.
A mixed history combined both authentic investigations and promoted 64 actual
semantic unknowns to separately identified analyst follow-ups. One follow-up
was updated to investigating. It contained 97 records, 76 heads and 77 revisions.
Both source queries preserved all original records/revisions. Thirty-two
two-head pages verified every semantic-follow-up head, including revision two.
A stale update and a tampered bundle import were rejected without changing the
ledger. Export reproduced the complete original mixed bundle. Closing cleared
records/Unknowns and made the old reference unavailable; no implicit persistence
or producer restart was introduced.
Public CLI `inspect-evidence-bundle` summary and inline `inspect-analysis-view`
summary also passed on both authentic sources. Portable CLI input still requires
complete input admission/validation; small output does not remove that cost.
## SDK costs
Each row spans two fresh MCP servers. Query time is the sum of SDK RPC elapsed
times over three traversals. Local post-call contract validation, oracle checks
and transcript writes are included only in the whole harness, along with startup,
full discovery, file reads, import, export and close. OS caches were uncontrolled/warm. A fresh imported
session is not a cold native-analysis measurement.
| Source / arm | Three traversals | Query reply bytes | Whole elapsed | Local family CPU | Family peak |
| ------------------------------ | ---------------: | ----------------: | --------------: | ---------------: | --------------: |
| Native complete | 1.329–1.385 s | 11,224,227 | 14.117–14.722 s | 13.915–14.485 s | 1,110–1,161 MiB |
| Native selected | 0.530–0.572 s | 167,985 | 12.907–13.316 s | 12.679–13.116 s | 935–1,000 MiB |
| JavaScript complete + recovery | 11.242–11.938 s | 212,856 | 26.300–27.105 s | 26.100–26.870 s | 1,321–1,334 MiB |
| JavaScript selected | 10.967–10.990 s | 208,350 | 25.708–26.197 s | 25.505–25.980 s | 1,336–1,344 MiB |
Mixed-history selected tasks took 16.933 s native and 26.500 s JavaScript,
including the larger import and complete round-trip checks. Their family peaks
were 1,261 and 1,347 MiB. This does not establish scaling to thousands of
registry heads or every provider.
Original-session npm warm pages measured 8–31 ms; after import, repeated pages
cost about 1.1–1.4 s. Source inspection shows that imported immutable Evidence
does not carry the format owner's private graph proofs, so each view repeats
full validation. Native layout views also parse the complete layout on each
page. #4810 owns equivalent before/after verification of a correction; this
checkpoint claims no speedup from an unimplemented cache.
## Actual model tasks
All twelve attempts retained their costs. Ten supplied every checked fact and
current Unknown identity/status; runtime and unselected-source coverage stayed
unknown. One initial JavaScript selected attempt searched the wrong connector
and received an approval-policy refusal. One guided complete attempt stopped
without an REA RPC and asserted tool unavailability without a supporting
connection/discovery observation. Both are failed tasks, despite CLI exit zero.
| Cohort / arm | Successful tasks | Total input tokens | Elapsed |
| ------------------------------------- | ---------------: | -----------------------: | -----------------------: |
| Native complete | 2/2 | 287,913 / 348,976 | 52.103 / 58.261 s |
| Native selected, two-head pages | 2/2 | 335,445 / 286,938 | 62.078 / 57.933 s |
| JavaScript complete + recovery | 2/2 | 454,491 / 328,066 | 76.490 / 58.335 s |
| JavaScript selected, two-head pages | 1/2 | 167,470 failed / 339,528 | 34.206 failed / 66.455 s |
| JavaScript guided complete + recovery | 1/2 | 168,862 / 28,696 failed | 69.206 / 29.187 failed s |
| JavaScript guided selected | 2/2 | 124,218 / 129,134 | 53.464 / 58.526 s |
The separate guided JavaScript cohort named the exact configured tools and
allowed one sufficiently large Unknown page for this bounded question. Both
arms received the same discovery guidance. Its single completed complete-arm
sample is insufficient for a stable repeated-pair benefit claim. Native total
input/time overlapped; extra pages and model turns can consume the reply saving.
Do not pool failed tasks as fast answers or attribute guidance changes to REA.
The JSON report records total/cached/uncached input, output, local CPU and peak
memory per attempt. These are client-reported usage counters, not tokenizer
estimates; output includes the client's reported reasoning accounting without
adding it again. Schema/discovery, errors, retries and continuations remain in
the task. Remote model compute, full-catalog model behavior and other hosts were
not measured.
Heavy runs were serial, one CPU, 8 GiB family hard limit, 7 GiB early stop,
no swap. Kernel peaks include charged file cache. Counter observations were
taken at payload exit while the owned observer remained alive; terminal
whole-scope counters remain unknown. Observed payload OOM/capacity events were
zero, all payloads exited and cleanup was verified. The expected MCP response
refusal is a separate transport-capacity outcome.
## Reproduce
Build the pinned runtime, acquire the matching real artifacts and produce the
two authentic histories with the public operations above. Export before close.
Keep bundles, transcripts and model profiles private. For a known source ID:
```sh
node experiments/analysis-efficiency/retained-investigation.mjs \
--runtime-root /absolute/built-rea \
--revision f4322b15ed1423d736bd2572cf7a8eadf2b466e1 \
--bundle /absolute/authentic-history.json \
--evidence-id ev_ORIGINAL_SHA256 \
--scenario native --delivery focused \
--output /absolute/new-receipt-directory
```
Run under the existing process-family resource wrapper, with sufficient bounds
for the selected source. Repeat with `--delivery complete`; use
`--scenario javascript` for module rows. The task's Unknown domain is
`retained-native` or `retained-javascript`. Output is never overwritten.
`agent-trial.py --mode direct` supports the same four enabled names; its success
exit only establishes client completion. Grade original IDs/digests, exact
facts, coverage and Unknowns from the retained transcript and final answer.
For a privately supplied authentic mixed bundle, add `--verify-mixed`. Besides
the normal source query and original-record round trip, this pages every current
head in `javascript-semantic-followup`, checks the current updated revision,
submits a deliberately stale update and a tampered copy of the bundle, verifies
atomic refusal, then checks Unknown/reference lifetime after close. The bundle
must contain genuine follow-ups and at least one updated head. The checkpoint's
64-head bundle passed this mode for both source scenarios. Its extra verification
costs are separate from the original SDK comparison rows above.
Rejected SDK calls are recorded before propagating the original failure, with
request, phase and elapsed time. Reply bytes stay null when no usable reply was
received. Receipt-write failures remain distinct; simultaneous call/write
failures retain both causes. Deterministic failure tests run with
`python3 -m unittest discover -s experiments/analysis-efficiency -p test_retained_queries.py`.
Finalization independently attempts client/transport close, log completion and
both receipt outputs. Cleanup failures preserve the original task failure and
call costs, record the failing phase and retain the workspace for retry. A file
receipt refusal still permits stdout; a broken stdout write is recorded in a
file retry. Files publish through a sibling temporary file and rename; an
interrupted retry preserves the previous receipt. Failed publication files stay
in the owned output directory. Cleanup or receipt-output failures make the
command fail. Six additional regressions exercise stream/file errors, partial
publication and peer cleanup; these are harness failure checks, not real-provider
evidence.
@@ -0,0 +1,310 @@
/** Measure public retained queries against an authentic, previously exported bundle. */
import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { createWriteStream } from "node:fs";
import {
appendFile,
mkdir,
mkdtemp,
readFile,
writeFile,
} from "node:fs/promises";
import { tmpdir } from "node:os";
import { dirname, isAbsolute, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { parseArgs } from "node:util";
import { finished } from "node:stream/promises";
import canonicalize from "canonicalize";
import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
import {
requireMcpOperationResult,
requireMcpToolError,
} from "../../scripts/lib/mcp-verifier-results.mjs";
import { createRetainedQueryCaller } from "./retained-query-calls.mjs";
import { finalizeRetainedQueryReceipt } from "./retained-query-finalization.mjs";
import {
verifyMixedRetainedHistory,
verifyMixedRetainedClosure,
retainedUnknownHeads,
} from "./retained-mixed-verification.mjs";
const { values } = parseArgs({
options: {
"runtime-root": {
type: "string",
default: resolve(dirname(fileURLToPath(import.meta.url)), "../.."),
},
revision: { type: "string" },
bundle: { type: "string" },
"evidence-id": { type: "string" },
scenario: { type: "string" },
delivery: { type: "string" },
output: { type: "string" },
"verify-mixed": { type: "boolean", default: false },
},
});
for (const key of ["runtime-root", "bundle", "output"])
assert.ok(values[key] && isAbsolute(values[key]), `${key} must be absolute`);
assert.ok(values.revision && values["evidence-id"]);
assert.ok(["native", "javascript"].includes(values.scenario));
assert.ok(["complete", "focused"].includes(values.delivery));
const root = values["runtime-root"];
const output = values.output;
await mkdir(output, { recursive: false, mode: 0o700 });
const digest = (bytes) => createHash("sha256").update(bytes).digest("hex");
const sourceBytes = await readFile(values.bundle);
const original = JSON.parse(sourceBytes);
const source = original.records.find(
(record) => record.evidence_id === values["evidence-id"],
);
const operation =
values.scenario === "native"
? "inspect_binary_layout"
: "analyze_javascript_application";
assert.equal(source?.operation, operation);
const unknownDomain = `retained-${values.scenario}`;
const expectedUnknowns = retainedUnknownHeads(original.unknowns, unknownDomain);
// These oracles check selected original facts, not producer discovery completeness.
const expectedRows =
values.scenario === "native"
? source.normalized_result.symbols
: source.normalized_result.graph.nodes
.filter((node) =>
["javascript-asset", "javascript-module", "source-module"].includes(
node.kind,
),
)
.map((node) => {
const candidates = node.observations.flatMap((observation) => {
const property = observation.properties.path;
const where = observation.evidence.location;
return [
...(typeof property === "string" ? [property] : []),
...(where.available && where.value.kind === "artifact-path"
? [where.value.path]
: where.available && where.value.kind === "source-range"
? [where.value.source]
: []),
];
});
return {
node_id: node.node_id,
kind: node.kind,
path:
node.identity.strategy === "canonical-path"
? node.identity.path
: node.identity.strategy === "source-map-original"
? node.identity.original_source
: (candidates[0] ?? null),
};
});
assert.ok(expectedRows.length >= 6, "This task needs six original rows");
const runtimeEntrySha256 = digest(
await readFile(join(root, "dist/mcp/main.js")),
);
const account = await mkdtemp(join(tmpdir(), "rea-retained-query-"));
const transport = new StdioClientTransport({
command: process.execPath,
args: [join(root, "scripts/rea.mjs"), "mcp"],
env: {
PATH: process.env.PATH,
HOME: account,
NODE_OPTIONS: "--max-old-space-size=2048",
REA_LOG_LEVEL: "silent",
},
stderr: "pipe",
});
const serverLog = createWriteStream(join(output, "server.log"));
const serverLogFinished = finished(serverLog);
serverLogFinished.catch(() => {});
transport.stderr?.pipe(serverLog);
const client = new Client({
name: "retained-investigation-measurement",
version: "1",
});
const report = {
revision: values.revision,
runtime_entry_sha256: runtimeEntrySha256,
bundle_sha256: digest(sourceBytes),
bundle_bytes: sourceBytes.length,
source_evidence_id: source.evidence_id,
scenario: values.scenario,
delivery: values.delivery,
verify_mixed: values["verify-mixed"],
calls: [],
status: "running",
};
const call = createRetainedQueryCaller(client, async (receipt, event) => {
report.calls.push(receipt);
await appendFile(
join(output, "transcript.jsonl"),
JSON.stringify(event) + "\n",
);
});
const result = (reply, name) => requireMcpOperationResult(reply, name);
try {
await client.connect(transport);
const started = performance.now();
const catalog = await client.listTools();
report.catalog = {
elapsed_ms: performance.now() - started,
tools: catalog.tools.length,
bytes: Buffer.byteLength(JSON.stringify(catalog)),
sha256: digest(JSON.stringify(catalog)),
};
assert.equal(catalog.nextCursor, undefined);
await writeFile(join(output, "catalog.json"), JSON.stringify(catalog));
result(
await call("import_evidence_bundle", { path: values.bundle }, "import"),
"import_evidence_bundle",
);
const answers = [];
for (let repeat = 0; repeat < 3; repeat += 1) {
let answer;
if (values.delivery === "complete") {
const reply = await call("get_evidence_bundle", {}, `complete-${repeat}`);
if (reply.isError !== true) {
const bundle = result(reply, "get_evidence_bundle");
assert.deepEqual(
bundle.records.find(
(record) => record.evidence_id === source.evidence_id,
),
source,
);
assert.deepEqual(bundle.unknowns, original.unknowns);
answer = [expectedRows.slice(0, 6), expectedUnknowns];
} else {
// Keep capacity refusals and every recovery RPC in the completed task.
const failure = requireMcpToolError(reply);
assert.equal(failure.code, "resource_constraint");
assert.equal(failure.details?.resource, "transport");
report.complete_delivery_refused = true;
}
}
if (answer === undefined) {
const metadata = result(
await call(
"get_evidence_bundle",
{
detail: "summary",
filters: { evidence_id: source.evidence_id },
},
`discovery-${repeat}`,
),
"get_evidence_bundle",
);
assert.equal(metadata.matching_records, 1);
assert.equal(metadata.records[0].evidence_id, source.evidence_id);
const rows = [];
for (const offset of [0, 2, 4]) {
const view = result(
await call(
"inspect_analysis_view",
{
source: {
kind: "retained-evidence",
evidence_id: source.evidence_id,
},
view: {
kind: "page",
collection:
values.scenario === "native" ? "symbols" : "modules",
offset,
limit: 2,
},
},
`page-${repeat}-${offset}`,
),
"inspect_analysis_view",
);
assert.equal(view.parent_evidence_id, source.evidence_id);
assert.equal(view.coverage.total, expectedRows.length);
assert.equal(view.coverage.examined, 2);
assert.equal(
view.coverage.next_offset,
offset + 2 < expectedRows.length ? offset + 2 : null,
);
assert.deepEqual(view.items, expectedRows.slice(offset, offset + 2));
rows.push(...view.items);
}
const unknowns = [];
let offset = 0;
do {
const page = result(
await call(
"list_unknowns",
{ domain: unknownDomain, offset, limit: 2 },
`unknowns-${repeat}-${offset}`,
),
"list_unknowns",
);
assert.equal(page.total, expectedUnknowns.length);
assert.equal(page.offset, offset);
assert.deepEqual(
page.items,
expectedUnknowns.slice(offset, offset + 2),
);
assert.equal(
page.next_offset,
offset + 2 < expectedUnknowns.length ? offset + 2 : null,
);
unknowns.push(...page.items);
offset = page.next_offset;
} while (offset !== null);
assert.deepEqual(unknowns, expectedUnknowns);
answer = [rows, unknowns];
}
answers.push(digest(canonicalize(answer)));
}
assert.equal(new Set(answers).size, 1);
report.answer_sha256 = answers[0];
if (values["verify-mixed"])
report.mixed_verification = await verifyMixedRetainedHistory({
call,
original,
sourceId: source.evidence_id,
output,
});
const roundtrip = join(output, "roundtrip.json");
result(
await call("export_evidence_bundle", { path: roundtrip }, "export"),
"export_evidence_bundle",
);
const exported = JSON.parse(await readFile(roundtrip, "utf8"));
const exportedRecords = new Map(
exported.records.map((record) => [record.evidence_id, record]),
);
for (const record of original.records)
assert.deepEqual(exportedRecords.get(record.evidence_id), record);
assert.deepEqual(exported.unknowns, original.unknowns);
report.original_records_preserved = true;
result(await call("close_binary", {}, "close"), "close_binary");
const closed = result(
await call("get_evidence_bundle", { detail: "summary" }, "after-close"),
"get_evidence_bundle",
);
assert.equal(closed.total_retained_records, 0);
report.close_cleared_records = true;
if (values["verify-mixed"])
Object.assign(
report.mixed_verification,
await verifyMixedRetainedClosure(call, source.evidence_id),
);
assert.equal(digest(await readFile(values.bundle)), report.bundle_sha256);
report.status = "passed";
} catch (cause) {
report.status = "failed";
report.failure = { name: cause.name, message: cause.message };
} finally {
const finalized = await finalizeRetainedQueryReceipt({
report,
client,
transport,
account,
serverLog,
serverLogFinished,
output,
});
if (report.status !== "passed" || !finalized) process.exitCode = 1;
}
@@ -0,0 +1,163 @@
import assert from "node:assert/strict";
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import {
requireMcpOperationResult as result,
requireMcpToolError,
} from "../../scripts/lib/mcp-verifier-results.mjs";
/** Expected current heads from the original exported revisions, not displayed pages. */
export const retainedUnknownHeads = (unknowns, domain) => {
const heads = new Map();
for (const unknown of unknowns) {
const previous = heads.get(unknown.unknown_id);
if (previous === undefined || previous.revision < unknown.revision)
heads.set(unknown.unknown_id, unknown);
}
return [...heads.values()]
.filter((unknown) => unknown.domain === domain)
.sort((a, b) =>
a.unknown_id < b.unknown_id ? -1 : a.unknown_id > b.unknown_id ? 1 : 0,
);
};
const verifyFollowupPages = async (call, original) => {
const expected = retainedUnknownHeads(
original.unknowns,
"javascript-semantic-followup",
);
assert.ok(
expected.length > 0,
"Supply an authentic mixed history with semantic follow-ups",
);
const revised = expected.find((unknown) => unknown.revision > 1);
assert.ok(revised, "Mixed verification requires a retained updated Unknown");
let offset = 0;
let pages = 0;
const observed = [];
do {
const page = result(
await call(
"list_unknowns",
{
domain: "javascript-semantic-followup",
offset,
limit: 2,
},
`mixed-heads-${offset}`,
),
"list_unknowns",
);
assert.equal(page.total, expected.length);
assert.equal(page.offset, offset);
assert.deepEqual(page.items, expected.slice(offset, offset + 2));
assert.equal(
page.next_offset,
offset + 2 < expected.length ? offset + 2 : null,
);
observed.push(...page.items);
offset = page.next_offset;
pages += 1;
} while (offset !== null);
assert.deepEqual(observed, expected);
return { revised, headsChecked: expected.length, pages };
};
const verifyMutationRefusals = async ({
call,
original,
sourceId,
output,
revised,
}) => {
const before = result(
await call(
"get_evidence_bundle",
{ detail: "summary" },
"mixed-before-refusals",
),
"get_evidence_bundle",
);
const stale = await call(
"update_unknown",
{
unknown_id: revised.unknown_id,
expected_revision: revised.revision - 1,
status: revised.status,
severity: revised.severity,
supporting_evidence_ids: revised.supporting_evidence_ids,
contradicting_evidence_ids: revised.contradicting_evidence_ids,
required_authority: revised.required_authority,
required_confidence: revised.required_confidence,
required_environment: revised.required_environment,
recommended_probes: revised.recommended_probes,
relationships: revised.relationships,
resolution: revised.resolution,
},
"mixed-stale-update",
);
assert.equal(requireMcpToolError(stale).code, "revision_conflict");
const tampered = structuredClone(original);
const target = tampered.records.find(
(record) => record.evidence_id === sourceId,
);
assert.ok(target);
target.normalized_result = { checkpoint_tampered: true };
const path = join(output, "tampered-import.json");
await writeFile(path, JSON.stringify(tampered), { flag: "wx" });
const rejected = await call(
"import_evidence_bundle",
{ path },
"mixed-tampered-import",
);
assert.equal(
requireMcpToolError(rejected).code,
"evidence_integrity_mismatch",
);
const after = result(
await call(
"get_evidence_bundle",
{ detail: "summary" },
"mixed-after-refusals",
),
"get_evidence_bundle",
);
assert.deepEqual(after, before);
};
/** Verify original mixed-history heads, CAS rejection and atomic import failure. */
export const verifyMixedRetainedHistory = async (options) => {
const { revised, headsChecked, pages } = await verifyFollowupPages(
options.call,
options.original,
);
await verifyMutationRefusals({ ...options, revised });
return {
heads_checked: headsChecked,
pages,
revised_unknown_id: revised.unknown_id,
revision_checked: revised.revision,
stale_update_rejected: true,
tampered_import_rejected_atomically: true,
};
};
/** Closing must neither retain old Unknowns nor rehydrate a closed source. */
export const verifyMixedRetainedClosure = async (call, sourceId) => {
const unknowns = result(
await call("list_unknowns", {}, "mixed-after-close-heads"),
"list_unknowns",
);
assert.equal(unknowns.total, 0);
const missing = await call(
"inspect_analysis_view",
{
source: { kind: "retained-evidence", evidence_id: sourceId },
view: { kind: "summary" },
},
"mixed-after-close-reference",
);
const failure = requireMcpToolError(missing);
assert.equal(failure.details?.reason, "missing");
return { unknowns_after_close: 0, old_reference_refused: true };
};
@@ -0,0 +1,46 @@
/** Keep settled and rejected RPCs in the same receipt before returning control. */
export const createRetainedQueryCaller =
(client, record) => async (name, input, phase) => {
const start = performance.now();
let reply;
try {
reply = await client.callTool(
{ name, arguments: input },
{ timeout: 300000 },
);
} catch (cause) {
const failure =
cause instanceof Error
? { name: cause.name, message: cause.message }
: { name: typeof cause, message: String(cause) };
const receipt = {
name,
input,
phase,
elapsed_ms: performance.now() - start,
reply_bytes: null,
is_error: true,
failure,
};
try {
await record(receipt, { name, input, phase, failure });
} catch (recordingFailure) {
throw new AggregateError(
[cause, recordingFailure],
"MCP call and receipt recording both failed",
);
}
throw cause;
}
const receipt = {
name,
input,
phase,
elapsed_ms: performance.now() - start,
reply_bytes: Buffer.byteLength(JSON.stringify(reply)),
is_error: reply.isError === true,
};
// A receipt-write failure is distinct from a rejected MCP call.
await record(receipt, { name, input, phase, reply });
return reply;
};
@@ -0,0 +1,81 @@
import { rename, rm, writeFile } from "node:fs/promises";
import { join } from "node:path";
const attempt = async (failures, phase, action) => {
try {
await action();
return true;
} catch (cause) {
failures.push(`${phase}: ${String(cause)}`);
return false;
}
};
const writeReceipt = (stream, text) =>
new Promise((resolve, reject) => {
const onError = (cause) => reject(cause);
stream.once("error", onError);
try {
stream.write(text, (cause) => {
if (cause !== null && cause !== undefined) {
// A Writable emits its error after notifying the write callback.
reject(cause);
} else {
stream.off("error", onError);
resolve();
}
});
} catch (cause) {
stream.off("error", onError);
reject(cause);
}
});
/** Preserve task costs through independent cleanup and receipt-output failures. */
export const finalizeRetainedQueryReceipt = async ({
report,
client,
transport,
account,
serverLog,
serverLogFinished,
output,
stdout = process.stdout,
}) => {
report.cleanup_failures = [];
await Promise.all([
attempt(report.cleanup_failures, "client", () => client.close()),
attempt(report.cleanup_failures, "transport", () => transport.close()),
]);
await attempt(report.cleanup_failures, "server-log", async () => {
if (!serverLog.writableEnded) serverLog.end();
await serverLogFinished;
});
if (report.cleanup_failures.length === 0)
await attempt(report.cleanup_failures, "workspace", () =>
rm(account, { recursive: true }),
);
if (report.cleanup_failures.length > 0) report.retained_workspace = account;
report.receipt_failures = [];
let publication = 0;
const saveReport = async () => {
// Keep failed publications in the owned output directory for inspection.
// A partial retry must never truncate an already published cost receipt.
const pending = join(output, `.report-${++publication}.json.pending`);
await writeFile(pending, JSON.stringify(report, null, 2) + "\n", {
flag: "wx",
});
await rename(pending, join(output, "report.json"));
};
await attempt(report.receipt_failures, "report-file", saveReport);
const stdoutWritten = await attempt(report.receipt_failures, "stdout", () =>
writeReceipt(stdout, JSON.stringify(report) + "\n"),
);
// Preserve a broken stdout diagnostic in the file that may already exist.
if (!stdoutWritten)
await attempt(report.receipt_failures, "report-file-retry", saveReport);
return (
report.cleanup_failures.length === 0 && report.receipt_failures.length === 0
);
};
@@ -0,0 +1,166 @@
"""Deterministic RPC receipt failures; not real-provider or model evidence."""
import json
from pathlib import Path
import subprocess
import unittest
class RetainedQueryReceipts(unittest.TestCase):
def run_node(self, body):
module = Path(__file__).with_name('retained-query-calls.mjs').resolve().as_uri()
finalizer = Path(__file__).with_name('retained-query-finalization.mjs').resolve().as_uri()
source = ('import assert from "node:assert/strict";\n'
'const {createRetainedQueryCaller} = await import(' + json.dumps(module) + ');\n'
'const {finalizeRetainedQueryReceipt} = await import(' + json.dumps(finalizer) + ');\n' + body)
result = subprocess.run(['node', '--input-type=module', '-'], input=source,
text=True, capture_output=True, timeout=15)
self.assertEqual(result.returncode, 0, result.stderr)
def run_finalization(self, body):
self.run_node('''
import {mkdtemp,mkdir,readFile,writeFile,rm,stat} from 'node:fs/promises';
import {join} from 'node:path';
import {tmpdir} from 'node:os';
import {Writable} from 'node:stream';
import {finished} from 'node:stream/promises';
const root=await mkdtemp(join(tmpdir(),'rea-receipt-test-'));
const account=join(root,'account');const output=join(root,'output');
await mkdir(account);await mkdir(output);
const chunks=[];
const stdout=new Writable({write(chunk,encoding,done){chunks.push(chunk.toString());done();}});
const serverLog=new Writable({write(chunk,encoding,done){done();}});
const serverLogFinished=finished(serverLog);serverLogFinished.catch(()=>{});
const originalFailure={name:'Error',message:'original request failed'};
const report={status:'failed',failure:originalFailure,calls:[{phase:'page',elapsed_ms:17}]};
const input={report,client:{close:async()=>{}},transport:{close:async()=>{}},
account,output,stdout,serverLog,serverLogFinished};
const saved=async()=>JSON.parse(await readFile(join(output,'report.json'),'utf8'));
try {
''' + body + '''
} finally {
serverLog.destroy();await serverLogFinished.catch(()=>{});stdout.destroy();
await rm(root,{recursive:true,force:true});
}
''')
def test_rejected_call_preserves_request_phase_cost_and_original_failure(self):
self.run_node('''
const failure = new Error('owned server closed');
const input = {offset:0, limit:2}; const rows=[]; const events=[];
const call = createRetainedQueryCaller({callTool:async request=>{
assert.strictEqual(request.arguments,input);throw failure;
}}, async(row,event)=>{rows.push(row);events.push(event);});
await assert.rejects(call('list_unknowns',input,'resume-2'), e=>e===failure);
assert.equal(rows.length,1);assert.equal(events.length,1);
assert.strictEqual(rows[0].input,input);assert.equal(rows[0].name,'list_unknowns');
assert.equal(rows[0].phase,'resume-2');assert.ok(rows[0].elapsed_ms>=0);
assert.equal(rows[0].reply_bytes,null);assert.equal(rows[0].is_error,true);
assert.deepEqual(events[0].failure,{name:'Error',message:'owned server closed'});
''')
def test_protocol_error_is_a_received_reply_with_known_bytes(self):
self.run_node('''
const reply={isError:true,content:[{type:'text',text:'capacity refusal'}]};const rows=[];
const call=createRetainedQueryCaller({callTool:async()=>reply},async row=>rows.push(row));
assert.strictEqual(await call('get_evidence_bundle',{},'complete'),reply);
assert.equal(rows.length,1);assert.equal(rows[0].is_error,true);
assert.equal(rows[0].reply_bytes,Buffer.byteLength(JSON.stringify(reply)));
assert.equal(Object.hasOwn(rows[0],'failure'),false);
''')
def test_receipt_write_error_does_not_reclassify_a_completed_rpc(self):
self.run_node('''
const failure=new Error('receipt disk unavailable');const rows=[];
const call=createRetainedQueryCaller({callTool:async()=>({content:[]})},async row=>{
rows.push(row);throw failure;
});
await assert.rejects(call('list_unknowns',{},'read'),e=>e===failure);
assert.equal(rows.length,1);assert.equal(rows[0].is_error,false);
assert.equal(Object.hasOwn(rows[0],'failure'),false);
''')
def test_failed_recording_preserves_the_prior_rpc_failure(self):
self.run_node('''
const rpc=new Error('request timeout');const disk=new Error('receipt close failed');
const call=createRetainedQueryCaller({callTool:async()=>{throw rpc;}},async()=>{throw disk;});
await assert.rejects(call('inspect_analysis_view',{},'selected'),e=>
e instanceof AggregateError && e.errors[0]===rpc && e.errors[1]===disk);
''')
def test_log_stream_failure_keeps_costs_both_receipts_and_workspace(self):
self.run_finalization('''
const log=new Writable({write(chunk,encoding,done){done(new Error('log disk failed'));}});
const logFinished=finished(log);logFinished.catch(()=>{});log.write('diagnostic');
assert.equal(await finalizeRetainedQueryReceipt({...input,serverLog:log,
serverLogFinished:logFinished}),false);
assert.match(report.cleanup_failures[0],/server-log:.*log disk failed/);
assert.strictEqual(report.failure,originalFailure);
assert.equal(report.calls[0].elapsed_ms,17);assert.equal(report.retained_workspace,account);
assert.equal((await stat(account)).isDirectory(),true);
assert.deepEqual(await saved(),report);assert.deepEqual(JSON.parse(chunks.join('')),report);
''')
def test_workspace_removal_error_still_writes_the_original_failure(self):
self.run_finalization('''
const blocked=join(root,'owned-file');await writeFile(blocked,'owned');
const malformedAccount=join(blocked,'child');
assert.equal(await finalizeRetainedQueryReceipt({...input,account:malformedAccount}),false);
assert.match(report.cleanup_failures[0],/^workspace:/);
assert.equal(report.retained_workspace,malformedAccount);
assert.deepEqual((await saved()).failure,originalFailure);
assert.deepEqual(JSON.parse(chunks.join('')),report);
''')
def test_report_file_refusal_preserves_the_stdout_receipt(self):
self.run_finalization('''
await mkdir(join(output,'report.json'));
assert.equal(await finalizeRetainedQueryReceipt(input),false);
assert.deepEqual(report.cleanup_failures,[]);
assert.match(report.receipt_failures[0],/^report-file:/);
assert.deepEqual(JSON.parse(chunks.join('')),report);
assert.deepEqual(report.failure,originalFailure);
''')
def test_stdout_write_error_is_handled_and_saved_in_the_file(self):
self.run_finalization('''
const failedStdout=new Writable({write(chunk,encoding,done){done(new Error('broken pipe'));}});
assert.equal(await finalizeRetainedQueryReceipt({...input,stdout:failedStdout}),false);
assert.match(report.receipt_failures[0],/stdout:.*broken pipe/);
assert.deepEqual(await saved(),report);assert.deepEqual(report.failure,originalFailure);
''')
def test_partial_retry_after_stdout_failure_preserves_the_prior_receipt(self):
self.run_finalization('''
const fs=await import('node:fs/promises');
const {syncBuiltinESMExports}=await import('node:module');
const originalWrite=fs.default.writeFile;let writes=0;let before;
fs.default.writeFile=async(...args)=>{
if(++writes===2){await originalWrite(args[0],'partial',args[2]);throw new Error('disk quota');}
return originalWrite(...args);
};
syncBuiltinESMExports();
try {
const failedStdout=new Writable({async write(chunk,encoding,done){
before=await readFile(join(output,'report.json'),'utf8');done(new Error('broken pipe'));
}});
assert.equal(await finalizeRetainedQueryReceipt({...input,stdout:failedStdout}),false);
assert.equal(writes,2);assert.equal(report.receipt_failures.length,2);
assert.match(report.receipt_failures[1],/report-file-retry:.*disk quota/);
assert.equal(await readFile(join(output,'report.json'),'utf8'),before);
assert.deepEqual((await saved()).failure,originalFailure);
assert.equal((await saved()).calls[0].elapsed_ms,17);
} finally {fs.default.writeFile=originalWrite;syncBuiltinESMExports();}
''')
def test_failed_client_close_keeps_ownership_and_attempts_peer_cleanup(self):
self.run_finalization('''
const closed=[];
assert.equal(await finalizeRetainedQueryReceipt({...input,
client:{close(){closed.push('client');throw new Error('close failed');}},
transport:{close:async()=>{closed.push('transport');}}}),false);
assert.deepEqual(closed,['client','transport']);
assert.match(report.cleanup_failures[0],/client:.*close failed/);
assert.equal(report.retained_workspace,account);assert.equal(serverLog.writableEnded,true);
assert.equal((await stat(account)).isDirectory(),true);
assert.deepEqual(await saved(),report);assert.deepEqual(JSON.parse(chunks.join('')),report);
''')
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "rea-agents",
"version": "6.5.0",
"version": "6.7.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "rea-agents",
"version": "6.5.0",
"version": "6.7.0",
"license": "MIT",
"dependencies": {
"@babel/parser": "8.0.4",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "rea-agents",
"mcpName": "io.github.morluto/rea",
"version": "6.5.0",
"version": "6.7.0",
"description": "Reverse engineer anything from your terminal or agent with one CLI and MCP server.",
"license": "MIT",
"repository": {
+148
View File
@@ -0,0 +1,148 @@
import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";
import { copyFile, lstat, mkdir, rm, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { z } from "zod";
const output = ".cache/ci-build-cache";
const plan = ".cache/ci-build-plan.json";
function readTasks() {
const { tasks } = z
.object({
tasks: z
.array(
z.object({
taskId: z.string().min(1),
// The pinned Turbo version uses eager 64-bit hexadecimal hashes.
hash: z.string().regex(/^[0-9a-f]{16}$/u),
}),
)
.min(1),
})
.parse(JSON.parse(readFileSync(0, "utf8")));
const identities = new Set();
for (const { taskId } of tasks) {
if (identities.has(taskId))
throw new Error(`Duplicate portable build task: ${taskId}`);
identities.add(taskId);
}
return tasks;
}
async function copyEntry(hash) {
const entries = [];
for (const suffix of [".tar.zst", "-meta.json", "-manifest.json"]) {
const name = `${hash}${suffix}`;
const source = join(".turbo/cache", name);
const entry = await lstat(source).catch((error) => {
// Eviction can remove a cache hit even during the successful build.
// Manifests are also optional in supported local cache entries.
if (error.code === "ENOENT") return undefined;
throw error;
});
if (!entry) continue;
if (!entry.isFile())
throw new Error(
`Portable build cache entry must be a regular file: ${source}`,
);
entries.push({ suffix, name, source, size: entry.size });
}
// Publish whole reusable entries only. Consumers rebuild missing tasks.
if (
![".tar.zst", "-meta.json"].every((suffix) =>
entries.some((entry) => entry.suffix === suffix),
)
)
return undefined;
const copied = [];
let missing = false;
for (const entry of entries) {
try {
await copyFile(entry.source, join(output, entry.name));
copied.push(entry);
} catch (error) {
if (error.code !== "ENOENT") throw error;
if (entry.suffix === "-manifest.json") continue;
missing = true;
break;
}
}
if (missing) {
for (const entry of copied) await rm(join(output, entry.name));
return undefined;
}
return {
files: copied.length,
bytes: copied.reduce((sum, entry) => sum + entry.size, 0),
};
}
async function snapshot(tasks) {
await mkdir(output, { recursive: true });
let files = 0;
let bytes = 0;
const available = new Set();
try {
for (const hash of new Set(tasks.map((task) => task.hash))) {
const entry = await copyEntry(hash);
if (!entry) continue;
available.add(hash);
files += entry.files;
bytes += entry.bytes;
}
// A fully evicted cache still has an explicit artifact and coverage record;
// required build outputs and downstream verification remain separate gates.
const manifest = `${JSON.stringify({
version: 1,
tasks: tasks.map((task) => ({
...task,
available: available.has(task.hash),
})),
})}\n`;
await writeFile(join(output, "portable-build-cache.json"), manifest);
files++;
bytes += Buffer.byteLength(manifest);
} catch (error) {
await rm(output, { recursive: true, force: true });
throw error;
}
console.log(
JSON.stringify({
tasks: tasks.length,
files,
bytes,
path: output,
missing_tasks: tasks
.filter((task) => !available.has(task.hash))
.map((task) => task.taskId),
}),
);
}
const [mode, ...extra] = process.argv.slice(2);
if (extra.length || !["key", "snapshot"].includes(mode))
throw new Error(
"Usage: portable-build-cache.mjs key|snapshot < turbo-dry-plan.json",
);
// Invalidate this mode's previous result before parsing its input. Failed
// validation must not leave stale plans or snapshots for downstream reuse.
if (mode === "snapshot") await rm(output, { recursive: true, force: true });
else await rm(plan, { force: true });
const tasks = readTasks();
if (mode === "key") {
const prefix = z
.string()
.min(1)
.regex(/^[^\r\n]+$/u)
.parse(process.env.BUILD_CACHE_PREFIX);
const hashes = tasks.map(({ taskId, hash }) => `${taskId}:${hash}`).sort();
const digest = createHash("sha256").update(hashes.join("\n")).digest("hex");
// Retain only validated identities for the later snapshot. Running Turbo
// again after the build can evict cache entries before they are copied.
await mkdir(".cache", { recursive: true });
await writeFile(plan, `${JSON.stringify({ tasks })}\n`);
console.log(`key=${prefix}-${digest}\nprefix=${prefix}-`);
} else {
await snapshot(tasks);
}
+3
View File
@@ -77,6 +77,7 @@ export const SCOPES = {
"tests/boundary/cli/nativePlistPrototypeKeyCli.test.ts",
"tests/boundary/cli/nativePlistSelectionFailureCli.test.ts",
"tests/boundary/cli/nativeDemangleCli.test.ts",
"tests/boundary/cli/nativeDispatchPropertyAttributes.test.ts",
"tests/boundary/mcp/nativeDemangleMcp.test.ts",
"tests/boundary/providers/native/nativeSignatureTargetBinding.test.ts",
"tests/boundary/cli/nativeSignatureCli.test.ts",
@@ -118,6 +119,7 @@ export const SCOPES = {
"scripts/verify/inspector/**",
"scripts/verify-package.mjs",
"scripts/verify-package-*",
"scripts/verify-packed-bridge.*",
"scripts/verify-windows-*.mjs",
"scripts/build-windows-native.mjs",
"scripts/windows-capabilities.mjs",
@@ -145,6 +147,7 @@ export const SCOPES = {
"tests/boundary/filesystem/webScriptManifestPaths.test.ts",
"tests/boundary/process/ProcessEvidence.host.test.ts",
"tests/boundary/process/packageNpmInvocation.test.ts",
"tests/boundary/process/packedBridgeVerification.test.ts",
"tests/boundary/process/windowsOutputCapture.test.ts",
"tests/acceptance/webModuleCli.test.ts",
"tests/acceptance/webSourceLocationCli.test.ts",
+186
View File
@@ -0,0 +1,186 @@
import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { fileURLToPath } from "node:url";
import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
import { CdpConnection } from "../../dist/browser/CdpConnection.js";
import { parseEvidence } from "../../dist/domain/evidence.js";
import { artifactCliEvidence } from "./artifact-e2e.mjs";
import { closeVerifierResources } from "./verifier-cleanup.mjs";
/** Verify real V8 source labels, which retain spelling that navigation normalizes. */
export async function verifyBrowserUrlUserInfo(endpoint, origin) {
const rawUrl = `${origin.replace("://", "://user:pass@")}\\path@tail/../keep.js?x=1#frag`;
const expectedUrl = `${origin}\\path@tail/../keep.js?x=1#frag`;
const sources = [rawUrl, expectedUrl].map(
(url, index) =>
`globalThis.reaUserinfoUrlProbe${index} = () => 4731;\n//# sourceURL=${url}`,
);
const response = await fetch(`${endpoint}/json/new?about:blank`, {
method: "PUT",
});
assert.equal(
response.ok,
true,
"Browser refused the owned URL fixture target",
);
const target = await response.json();
assert.equal(typeof target.id, "string");
let connection;
let transport;
let client;
let verificationError;
try {
connection = await CdpConnection.connect(
target.webSocketDebuggerUrl,
"inspect_web_page",
);
await loadFixturePage(connection, `${origin}/app?userinfo-probe`);
const producerUrls = [];
connection.onEvent((event) => {
if (event.method === "Debugger.scriptParsed")
producerUrls.push(event.params.url);
});
await connection.send("Debugger.enable");
const browserUrl = await connection.send("Runtime.evaluate", {
expression: `new URL(${JSON.stringify(rawUrl)}).href`,
returnByValue: true,
});
assert.equal(browserUrl.exceptionDetails, undefined);
assert.equal(browserUrl.result.value, new URL(rawUrl).href);
assert.notEqual(browserUrl.result.value, rawUrl);
assert.equal(new URL(browserUrl.result.value).pathname, "/keep.js");
for (const source of sources) {
const evaluated = await connection.send("Runtime.evaluate", {
expression: source,
returnByValue: true,
});
assert.equal(evaluated.exceptionDetails, undefined);
}
assert.ok(
[rawUrl, expectedUrl].every((url) => producerUrls.includes(url)),
"Real V8 did not retain the exact sourceURL spelling on the CDP wire",
);
const assertScripts = (evidence) => {
const result = parseEvidence(evidence).normalized_result;
return sources.map((source) => {
const script = result.scripts.items.find(
(item) =>
item.source.included && item.source.artifact.text === source,
);
assert.ok(
script,
"Public capture omitted the retained V8 fixture script",
);
assert.equal(script.url, expectedUrl);
assert.equal(new URL(script.url).origin, origin);
assert.equal(
script.source.artifact.sha256,
createHash("sha256").update(source).digest("hex"),
);
return script.url;
});
};
const cliUrl = assertScripts(
await artifactCliEvidence("inspect-web-page", endpoint, [
target.id,
"--allowed-origins",
origin,
"--include-script-sources",
"--observation-ms",
"0",
]),
);
transport = new StdioClientTransport({
command: process.execPath,
args: [fileURLToPath(new URL("../rea.mjs", import.meta.url)), "mcp"],
env: { ...process.env, REA_LOG_LEVEL: "silent" },
stderr: "pipe",
});
client = new Client({ name: "browser-url-userinfo-e2e", version: "1" });
await client.connect(transport);
const mcp = await client.callTool({
name: "inspect_web_page",
arguments: {
cdp_endpoint: endpoint,
allowed_origins: [origin],
target_id: target.id,
include_script_sources: true,
observation_ms: 0,
},
});
assert.notEqual(mcp.isError, true, JSON.stringify(mcp));
assert.deepEqual(assertScripts(mcp.structuredContent), cliUrl);
return {
cli: true,
stdio_mcp: true,
raw_v8_source_url: true,
credential_free_control: true,
original_source_digest: true,
browser_url_parser_normalizes_backslash_and_dot_segments: true,
};
} catch (cause) {
verificationError = cause;
throw cause;
} finally {
await closeVerifierResources(
[
async () => client?.close(),
async () => transport?.close(),
async () => connection?.close(),
async () => {
const closed = await fetch(`${endpoint}/json/close/${target.id}`);
assert.equal(
closed.ok,
true,
"Browser retained the owned URL fixture target",
);
},
],
verificationError,
);
}
}
async function loadFixturePage(connection, url) {
const loaded = new Set();
let loaderId;
let resolveLoaded;
let timer;
const load = new Promise((resolve) => {
resolveLoaded = resolve;
});
const unsubscribe = connection.onEvent((event) => {
if (event.method !== "Page.lifecycleEvent" || event.params.name !== "load")
return;
loaded.add(event.params.loaderId);
if (event.params.loaderId === loaderId) resolveLoaded();
});
try {
await connection.send("Page.enable");
await connection.send("Page.setLifecycleEventsEnabled", { enabled: true });
const navigation = await connection.send("Page.navigate", { url });
assert.equal(navigation.errorText, undefined);
assert.equal(typeof navigation.loaderId, "string");
loaderId = navigation.loaderId;
if (!loaded.has(loaderId))
await Promise.race([
load,
new Promise((_, reject) => {
timer = setTimeout(
() => reject(new Error("Owned URL fixture did not finish loading")),
10_000,
);
}),
]);
assert.equal(
(await connection.send("Page.getFrameTree")).frameTree.frame.url,
url,
);
} finally {
clearTimeout(timer);
unsubscribe();
await connection.send("Page.setLifecycleEventsEnabled", { enabled: false });
}
}
@@ -0,0 +1,11 @@
import type {
GhidraInstallationHost,
GhidraInstallationInspection,
GhidraInstallationOptions,
} from "../../src/ghidra/GhidraInstallation.js";
/** Inspect prerequisites with a real owner, including cleanup before completion. */
export function inspectVerifierGhidraInstallation(
options: Omit<GhidraInstallationOptions, "resourceOwner">,
host?: GhidraInstallationHost,
): Promise<GhidraInstallationInspection>;
@@ -0,0 +1,9 @@
import { inspectGhidraInstallation } from "../../dist/ghidra/GhidraInstallation.js";
import { withVerifierResourceOwner } from "./verifier-file-resources.mjs";
/** Complete one prerequisite inspection and release its exact retained owners. */
export function inspectVerifierGhidraInstallation(options, host) {
return withVerifierResourceOwner((resourceOwner) =>
inspectGhidraInstallation({ ...options, resourceOwner }, host),
);
}
@@ -0,0 +1,11 @@
import type { validateJadxConfiguration } from "../../src/android/JadxConfiguration.js";
type ConfigurationArguments = Parameters<typeof validateJadxConfiguration>;
/** Validate prerequisites with a real owner, joining cleanup before completion. */
export function validateVerifierJadxConfiguration(
configuration: ConfigurationArguments[0],
environment: ConfigurationArguments[1],
operation: ConfigurationArguments[2],
options?: Omit<ConfigurationArguments[3], "resourceOwner" | "operation">,
): ReturnType<typeof validateJadxConfiguration>;
@@ -0,0 +1,17 @@
import { validateJadxConfiguration } from "../../dist/android/JadxConfiguration.js";
import { withVerifierResourceOwner } from "./verifier-file-resources.mjs";
/** Validate Java and the engine archive before beginning real verification. */
export function validateVerifierJadxConfiguration(
configuration,
environment,
operation,
options = {},
) {
return withVerifierResourceOwner((resourceOwner) =>
validateJadxConfiguration(configuration, environment, operation, {
...options,
resourceOwner,
}),
);
}
@@ -2,6 +2,7 @@ import assert from "node:assert/strict";
import { realpath } from "node:fs/promises";
import { runKunaCommand } from "../../dist/kuna/KunaCommand.js";
import { kunaProjectDeadline } from "../../dist/kuna/KunaConfiguration.js";
import { inspectKunaInstallation } from "../../dist/kuna/KunaInstallation.js";
import { KUNA_XREF_OUTPUT_BYTES } from "../../dist/kuna/KunaReferenceFiles.js";
@@ -24,14 +25,15 @@ export const nativeKunaIncomingSites = async ({
);
assert.ok(installed.ok, "Native reference oracle could not inspect Kuna");
const input = await realpath(binary);
const timeoutMs = installed.value.configuration.functionTimeoutMs;
// Match the provider's own xrefs scan, which runs under the project deadline.
const deadline = kunaProjectDeadline(installed.value.configuration);
const outcome = await runKunaCommand({
installation: installed.value,
arguments: ["xrefs", input, "--to", address, "--json"],
workspace,
operation: "verify:kuna-native-xrefs",
timeoutMs,
reportedTimeoutMs: timeoutMs,
timeoutMs: deadline.ms,
deadline,
outputBytes: KUNA_XREF_OUTPUT_BYTES,
signal: new AbortController().signal,
retainFailedFileClose: (owner, resource) =>
+2 -1
View File
@@ -3,6 +3,7 @@ import { readdir, realpath } from "node:fs/promises";
import { join } from "node:path";
import { runKunaCommand } from "../../dist/kuna/KunaCommand.js";
import { kunaProjectDeadline } from "../../dist/kuna/KunaConfiguration.js";
import { inspectKunaInstallation } from "../../dist/kuna/KunaInstallation.js";
const entries = (path) =>
@@ -46,7 +47,7 @@ export const nativeKunaStrings = async ({
workspace,
operation: "verify:kuna-native-strings",
timeoutMs: installed.value.configuration.projectTimeoutMs,
reportedTimeoutMs: installed.value.configuration.projectTimeoutMs,
deadline: kunaProjectDeadline(installed.value.configuration),
outputBytes: 16 * 1024 * 1024,
signal: new AbortController().signal,
retainFailedFileClose: (owner, resource) =>
@@ -7,7 +7,7 @@ import { tmpdir } from "node:os";
import { basename, dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
import { inspectGhidraInstallation } from "../../dist/ghidra/GhidraInstallation.js";
import { inspectVerifierGhidraInstallation } from "./ghidra-installation-inspection.mjs";
/** Exercise qualified annotation names against real demangled C++ ABI symbols. */
export async function verifyGhidraNamespaceAnnotations({
@@ -353,8 +353,8 @@ async function verifyAddressNames(call, symbols, kind) {
}
async function requireNamespaceDemangler(env) {
const inspection = inspectGhidraInstallation({
environment: process.env,
const inspection = await inspectVerifierGhidraInstallation({
environment: env,
installDir: env.GHIDRA_INSTALL_DIR,
...(env.JAVA_HOME === undefined ? {} : { javaHome: env.JAVA_HOME }),
});
Loaded 100 of 981 files, more files were not shown because too many files have changed in this diff. Show more