Compare commits
94
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ede7a0864 | ||
|
|
7b2087e449 | ||
|
|
f5d1012fab | ||
|
|
d2a3269d0c | ||
|
|
fd7da77fc0 | ||
|
|
44521d3d75 | ||
|
|
eac296a7da | ||
|
|
55cce6fc11 | ||
|
|
57e76599c0 | ||
|
|
498a667234 | ||
|
|
1195929fa5 | ||
|
|
c4007f4542 | ||
|
|
a74d859a19 | ||
|
|
2b2e362a00 | ||
|
|
497037e6e3 | ||
|
|
fa36657251 | ||
|
|
d43d3568f2 | ||
|
|
25d3805e7f | ||
|
|
82902465c4 | ||
|
|
cf5194f68e | ||
|
|
52d1a72af0 | ||
|
|
e50a271c72 | ||
|
|
c374c2fe72 | ||
|
|
0c440bfc07 | ||
|
|
a4dbb36d8e | ||
|
|
0e83d92c58 | ||
|
|
ee12d517a2 | ||
|
|
9cf7a7cb0d | ||
|
|
00a055be0f | ||
|
|
22278b20c0 | ||
|
|
3cf6206614 | ||
|
|
4714c7eaa3 | ||
|
|
cec8f1e8f3 | ||
|
|
21e0c580e7 | ||
|
|
f01dccab77 | ||
|
|
5f0bc86d1a | ||
|
|
f69332c94c | ||
|
|
2e03251b61 | ||
|
|
108c0e7e49 | ||
|
|
266dd1d958 | ||
|
|
cac12ae334 | ||
|
|
861c11ec77 | ||
|
|
9051809343 | ||
|
|
9db5f1b276 | ||
|
|
3a9176098d | ||
|
|
483678bf9f | ||
|
|
052956af34 | ||
|
|
aa314ab682 | ||
|
|
c23e161629 | ||
|
|
1417bf943b | ||
|
|
e84468c0bb | ||
|
|
afde435c94 | ||
|
|
e9fc8fb30c | ||
|
|
c284168c4b | ||
|
|
be20ac3740 | ||
|
|
cc73587ec4 | ||
|
|
98edca9f96 | ||
|
|
98a5e56380 | ||
|
|
7c6142efdc | ||
|
|
562dab00c5 | ||
|
|
3b02179560 | ||
|
|
599fdac029 | ||
|
|
696832caa3 | ||
|
|
459a356edb | ||
|
|
419502c8d0 | ||
|
|
1006714271 | ||
|
|
e367750b1b | ||
|
|
cf3288d8cf | ||
|
|
d532b18123 | ||
|
|
074768901f | ||
|
|
2538214e2c | ||
|
|
b5d540f3a2 | ||
|
|
4eaf3a117b | ||
|
|
0134732fcd | ||
|
|
799e8265b2 | ||
|
|
4668cea246 | ||
|
|
58d6043303 | ||
|
|
d148ce0905 | ||
|
|
3fca2e0ca6 | ||
|
|
4e716b0607 | ||
|
|
d72021b0da | ||
|
|
f93a0335a8 | ||
|
|
5fa4defcbc | ||
|
|
833136507d | ||
|
|
82c76e2cba | ||
|
|
bf1eeee94f | ||
|
|
7a0c47bfa4 | ||
|
|
b4938aed58 | ||
|
|
71ae02c6ef | ||
|
|
10f1994fe1 | ||
|
|
909b9f7b6b | ||
|
|
e84e17e30f | ||
|
|
1c2c2f1e78 | ||
|
|
1b3ac94242 |
No files matched your search
@@ -2,7 +2,7 @@
|
||||
name: reverse-engineer-anything
|
||||
description: Reverse engineer native, managed, Electron/JavaScript, packaged, firmware, and browser targets with REA. Use shipped-artifact or requested runtime evidence to explain features, compare versions, decompile code, or guide a reconstruction. Skip REA for ordinary source-repository architecture analysis.
|
||||
metadata:
|
||||
version: "42"
|
||||
version: "43"
|
||||
---
|
||||
|
||||
# REA
|
||||
@@ -169,6 +169,17 @@ explicit path or endpoint and do not need it.
|
||||
absent metadata does not prove a binary is not Go, and unparsed text records
|
||||
remain explicit. See the
|
||||
[Go binary guide](https://github.com/morluto/rea/blob/main/docs/go-binaries.md).
|
||||
- Unity IL2CPP names, member owners and string literals from `global-metadata.dat`:
|
||||
`inspect_il2cpp_metadata` with the explicit local file; MCP defaults to compact
|
||||
counts. Query sections, images, types, methods, fields, properties, events,
|
||||
parameters or string_literals through `inspect_analysis_view`
|
||||
with the returned `parent_evidence_id`, using exact filters, batch indices and
|
||||
selected fields. Parameters accept `method_indices`, avoiding a scan of all
|
||||
parameter pages. Missing collections in historical Evidence remain unknown.
|
||||
Versions 24, 27, 29, 31 and 39; no Unity, IL2CPP
|
||||
binary or disassembler is run. Unrecognized magic (possibly encrypted) and
|
||||
other versions are unsupported. Optional matching `binary_path` enables static native addresses and type references; query `method_addresses` with indices or addresses/states, preserving every shared-body method and both artifact identities. Without a linked binary native counts remain unknown. Byte-pattern fallback is inferred; no runtime behavior or same-build pairing is proved. See the
|
||||
[IL2CPP metadata guide](https://github.com/morluto/rea/blob/main/docs/il2cpp-metadata.md).
|
||||
- Embedded cargo-auditable Rust dependency metadata: `inspect_rust_binary`
|
||||
with the explicit local ELF, PE, or thin Mach-O file. This built-in
|
||||
target-free inspection requires no Rust installation or disassembler. Preserve
|
||||
@@ -247,6 +258,12 @@ Provider candidate facts describe possible providers, while top-level session
|
||||
capabilities describe the current route and `analysis_provider_binding` names the
|
||||
selected provider. Do not treat those scopes as interchangeable.
|
||||
|
||||
For a Ghidra startup timeout on a large native target, follow
|
||||
[deadline recovery](references/native-and-artifacts.md#ghidra-startup-deadline-recovery)
|
||||
before abandoning deep analysis or repeating the same cold import. The default
|
||||
330 seconds is configurable; changing the server's environment requires a new
|
||||
REA process.
|
||||
|
||||
## Work summary-first
|
||||
|
||||
Start with the default result and use its inline Evidence and graph context.
|
||||
|
||||
@@ -24,6 +24,17 @@ retain its complete source and receive that view immediately. Without that
|
||||
advertised choice, keep the existing complete call. CLI inspection returns
|
||||
portable complete Evidence; save it before projecting a selected inline view.
|
||||
|
||||
For native procedure/name/string inventories, select `detail: "summary"` when
|
||||
the connected tool advertises it and the task needs counts or selected rows.
|
||||
Use its parent Evidence ID with
|
||||
`view: {kind: "native-inventory", offset: 0, limit: 32, fields: ["value"]}`.
|
||||
Search tools retain the existing pattern/mode/case/document selection. Omit
|
||||
`fields` for complete records; `record.address` and `source_index` remain on
|
||||
selected rows. Selecting `value` keeps its string/decoding qualifications.
|
||||
Parent Evidence ID plus source index preserves same-address
|
||||
alternatives. Follow `coverage.next_offset` only as far as the question needs;
|
||||
exhausting retained rows does not establish complete program discovery.
|
||||
|
||||
Use `record_unknown` to track unresolved questions in the user's investigation
|
||||
and name the authority or environment still required. Supply supporting and
|
||||
contradicting evidence IDs. It updates the session registry directly.
|
||||
@@ -77,3 +88,9 @@ body shapes and record pointers for an agent environment. Opt into
|
||||
`template_rules` only when recorded identifiers need them; every template,
|
||||
parameter and merged sibling is an inference, no header, cookie or body value is
|
||||
copied, and the three stated unknowns stay unresolved.
|
||||
|
||||
Use `view: {kind: "environment-state"}` with the manifest's `template_rules` to
|
||||
extract the data those reads observed instead: entity types and identity
|
||||
fields, entities with recorded values, documents, and per-tool render plans
|
||||
checked against the recordings. The state comes only from reads before the
|
||||
first related write; types, identity and list membership are inferences.
|
||||
@@ -1,5 +1,32 @@
|
||||
# Native, managed, and packaged artifacts
|
||||
|
||||
For release comparisons, export `list_procedures` Evidence from each target before
|
||||
closing it. Close clears session records: import both bundles after collecting the
|
||||
two targets, or supply portable inline Evidence. `match_native_functions` pairs unique exact names and, when supplied,
|
||||
complete observed body bytes. Reuse optional same-context `list_names`,
|
||||
`procedure_info`, `read_bytes` and existing `analyze_function` Evidence; avoid
|
||||
decompiling every function merely to match it. Its summary retains a parent for
|
||||
`inspect_analysis_view` `function-matches` collections `matches` and `functions`.
|
||||
Select columns, batch IDs and page individual facts; aliases remain unresolved.
|
||||
Unpaired observations do not prove additions/removals. Existing matched dossier
|
||||
pairs include inputs for `compare_functions`; otherwise analyze just chosen pairs.
|
||||
|
||||
## Desktop installation directories
|
||||
|
||||
When an installation directory is supplied and its runtime is unknown, use
|
||||
`inspect_desktop_application` with its absolute path. Default MCP summary
|
||||
delivery retains complete Evidence. Read `files`, `omissions`, `candidates`, `components` or
|
||||
`next_probes` with `inspect_analysis_view` kind `desktop-application`, selecting
|
||||
exact paths, batched file/omission IDs or families and only needed fields and pages.
|
||||
Summary `probe_issues` retains counts and bounded previews. Select files with
|
||||
`has_issues: true` and fields `path`, `read_status`, `issues` to retrieve exact
|
||||
failures directly; preview text is display data, while `file_id` selects full facts. Preserve source
|
||||
coverage, read-range identity and unknowns; directory snapshot identity is not
|
||||
a digest of every file's contents. Component path markers do not establish
|
||||
runtime presence. For compact CLI output, use
|
||||
`inspect-desktop-application PATH --detail summary --evidence_output BUNDLE`.
|
||||
See [desktop installations](https://github.com/morluto/rea/blob/main/docs/desktop-applications.md).
|
||||
|
||||
## Go build metadata
|
||||
|
||||
Use `inspect_go_binary` for an explicit local ELF, PE or thin Mach-O when the
|
||||
@@ -11,6 +38,24 @@ unparsed module-text line. A missing record is an unknown, not proof of a
|
||||
non-Go binary; observed metadata does not authenticate its claims. Function,
|
||||
source and type recovery remain a separate native-analysis question.
|
||||
|
||||
## IL2CPP metadata
|
||||
|
||||
Use `inspect_il2cpp_metadata` for an explicit local Unity `global-metadata.dat`
|
||||
(versions 24, 27, 29, 31 and 39) when the question concerns assembly images,
|
||||
names, member owners, string literals, namespaces or index ranges. No `open_binary`, Unity installation or target
|
||||
execution is required. The CLI equivalent is `inspect-il2cpp-metadata PATH`.
|
||||
MCP defaults to counts with complete retained Evidence. Query `sections`,
|
||||
`images`, `types`, `methods`, `fields`, `properties`, `events`, `parameters` or
|
||||
`string_literals` through `inspect_analysis_view`; use exact names,
|
||||
batch indices and selected fields. Row indices are scoped by artifact SHA-256
|
||||
and collection; page offsets count filtered matches. Parameters support batch
|
||||
`method_indices` to read one method's parameters directly. Historical reports
|
||||
without member collections retain unknown counts rather than empty inventories. Preserve
|
||||
artifact SHA-256 and index widths. Unrecognized magic may mean encryption and
|
||||
is unsupported, not decrypted. Linking names to `GameAssembly` or
|
||||
`UnityFramework` code addresses remains a separate native-analysis question.
|
||||
See [IL2CPP metadata](https://github.com/morluto/rea/blob/main/docs/il2cpp-metadata.md).
|
||||
|
||||
## Rust dependency metadata
|
||||
|
||||
Use `inspect_rust_binary` for an explicit local ELF, PE, or thin Mach-O when
|
||||
@@ -124,6 +169,36 @@ 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.
|
||||
|
||||
## Ghidra startup deadline recovery
|
||||
|
||||
`provider_timeout` during the first Ghidra query can cover import, auto-analysis,
|
||||
bridge startup, and readiness. The default 330,000 ms is configurable through
|
||||
`REA_GHIDRA_STARTUP_TIMEOUT_MS`; a smaller query still needs the same cold import.
|
||||
|
||||
Preserve useful Evidence before closing the session or restarting REA. Check
|
||||
`close_binary` and resolve reported cleanup failures. Set a larger startup budget
|
||||
in the environment that launches REA, then restart REA and reconnect the MCP
|
||||
client before reopening the same target. Closing and reopening a binary in the
|
||||
existing server keeps its original configuration and starts import again.
|
||||
Environment changes in an agent's shell do not update an already running MCP
|
||||
server. If its launch configuration cannot be changed, report that constraint
|
||||
and the exact retry setting instead of claiming a fixed technical limit.
|
||||
|
||||
The client's tool-call deadline must allow startup plus the requested query;
|
||||
server settings and progress notifications cannot override it. CLI analysis
|
||||
avoids the MCP client deadline, but retains the provider deadline. For example:
|
||||
|
||||
```sh
|
||||
REA_GHIDRA_STARTUP_TIMEOUT_MS=1800000 rea analyze /absolute/path/to/app.exe --provider ghidra --json
|
||||
```
|
||||
|
||||
Thirty minutes is an example budget, not a completion guarantee. Keep host CPU
|
||||
and memory controls in place. Disable specific expensive analyzers only when
|
||||
their missing coverage is acceptable and retain the resulting limitations.
|
||||
Exported C and embedded resources remain useful independent evidence, but a
|
||||
timeout does not establish complete analysis or absence of code. See the
|
||||
[deadline and client guide](https://github.com/morluto/rea/blob/main/docs/mcp-contracts.md#ghidra-first-query-deadlines-and-recovery).
|
||||
|
||||
## Static memory and original file bytes
|
||||
|
||||
For Ghidra, `read_bytes` reads initialized analysis memory, including loader
|
||||
@@ -295,3 +370,22 @@ with two complete inspection Evidence records (or current-session retained
|
||||
references) and explicit scope. Preserve unknown types/duplicate names; static
|
||||
interface equality is not runtime compatibility. CLI accepts complete inline
|
||||
Evidence in `rea compare-wasm-interfaces input.json --json`.
|
||||
|
||||
## Godot PCK
|
||||
|
||||
For a standalone PCK, `open_binary` then `inspect_artifact` with
|
||||
`detail: "summary"` retains the complete static inventory. Use the returned
|
||||
`parent_evidence_id` with `inspect_analysis_view` inventory occurrences filtered
|
||||
by exact `logical_paths` or `occurrence_ids`, and join `artifact_id` to nodes
|
||||
filtered by `artifact_ids`. Select only needed fields; identity links remain.
|
||||
Prefer `inspect-artifact --detail summary --evidence_output PATH` for CLI tasks;
|
||||
the same CLI discovers embedded packs via their EOF trailer without opening a
|
||||
native analysis provider. Normal native opening remains unchanged.
|
||||
|
||||
Distinguish declared MD5 from observed SHA-256 and graph/page completeness from
|
||||
`source_coverage`. Encrypted directories and sparse bundles have unknown entry
|
||||
coverage; encrypted members have unknown content identity. An empty filtered
|
||||
page cannot prove absence in unknown coverage. Retained views are historical,
|
||||
not fresh filesystem checks. No decryption, Godot resource decoding, GDScript
|
||||
decompilation or target execution is provided. See
|
||||
[Godot PCK](https://github.com/morluto/rea/blob/main/docs/godot-pck.md).
|
||||
@@ -69,6 +69,12 @@ with `inspect_analysis_view` and `view: {kind: "capture", detail: "preview", off
|
||||
Select complete record pages only for needed headers, metadata or payloads; views
|
||||
read the historical parent without repeating capture decoding.
|
||||
|
||||
For selected protobuf/gRPC bodies, keep raw bytes as the source and use
|
||||
`inspect_analysis_view` kind `protobuf`, collection `bodies`, `frames` or
|
||||
`fields`. Batch exact record ordinals and field numbers/IDs, and select
|
||||
`schema_value` or `text` only when needed. Supply a local descriptor set and
|
||||
message type for schema interpretation; compressed payloads remain unknown.
|
||||
|
||||
Inspect native `.saz` snapshots with `inspect_web_network_capture` and
|
||||
`format: "saz"`; encrypted archives are unsupported. For live
|
||||
traffic analysis, connect Everywhere's separate MCP server and use its
|
||||
|
||||
@@ -59,6 +59,7 @@ jobs:
|
||||
web_network: ${{ steps.plan.outputs.web_network }}
|
||||
web_runtime: ${{ steps.plan.outputs.web_runtime }}
|
||||
web_source_map: ${{ steps.plan.outputs.web_source_map }}
|
||||
agentenv: ${{ steps.plan.outputs.agentenv }}
|
||||
nativeaot: ${{ steps.plan.outputs.nativeaot }}
|
||||
ghidra_bridge: ${{ steps.plan.outputs.ghidra_bridge }}
|
||||
website: ${{ steps.plan.outputs.website }}
|
||||
@@ -302,7 +303,8 @@ jobs:
|
||||
needs.changes.outputs.recorded_crash == 'true' ||
|
||||
needs.changes.outputs.web_network == 'true' ||
|
||||
needs.changes.outputs.web_runtime == 'true' ||
|
||||
needs.changes.outputs.web_source_map == 'true'
|
||||
needs.changes.outputs.web_source_map == 'true' ||
|
||||
needs.changes.outputs.agentenv == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
@@ -748,6 +750,9 @@ jobs:
|
||||
tests/boundary/providers/native/artifactProvider.extractionCompleteness.test.ts
|
||||
tests/boundary/filesystem/setupUnavailableProvider.test.ts
|
||||
tests/boundary/filesystem/windowsNpxRegistration.test.ts
|
||||
tests/boundary/filesystem/clientConfigurationEnvironment.test.ts
|
||||
tests/boundary/filesystem/clientConfigurationLayeredEnvironment.test.ts
|
||||
tests/acceptance/setup/registrationRuntimeEnvironmentCli.test.ts
|
||||
tests/boundary/android/workspaceGuardianRetry.test.ts
|
||||
tests/acceptance/setup/homeDirectoryFallback.test.ts
|
||||
tests/boundary/filesystem/javascriptApplicationWorkflows.test.ts
|
||||
@@ -909,6 +914,13 @@ jobs:
|
||||
with:
|
||||
build_cache_artifact: rea-build-cache
|
||||
|
||||
agentenv:
|
||||
needs: [changes, static, build]
|
||||
if: needs.changes.outputs.agentenv == 'true'
|
||||
uses: ./.github/workflows/real-agentenv.yml
|
||||
with:
|
||||
build_cache_artifact: rea-build-cache
|
||||
|
||||
nativeaot:
|
||||
needs: [changes, static]
|
||||
if: needs.changes.outputs.nativeaot == 'true'
|
||||
@@ -953,6 +965,7 @@ jobs:
|
||||
web_network,
|
||||
web_runtime,
|
||||
web_source_map,
|
||||
agentenv,
|
||||
nativeaot,
|
||||
ghidra_bridge,
|
||||
website,
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
name: Real agentenv environment bundle
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
inputs:
|
||||
build_cache_artifact:
|
||||
description: Portable build cache artifact from the calling CI run.
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
NODE_OPTIONS: --max-old-space-size=768
|
||||
PIP_DISABLE_PIP_VERSION_CHECK: "1"
|
||||
|
||||
jobs:
|
||||
real-agentenv:
|
||||
name: Export and run an environment bundle with the agentenv runner
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- name: Cache npm package downloads
|
||||
id: npm-cache
|
||||
uses: ./.github/actions/npm-cache
|
||||
- run: npm ci
|
||||
- uses: ./.github/actions/portable-build-cache
|
||||
with:
|
||||
build_cache_artifact: ${{ inputs.build_cache_artifact }}
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
# Public ECR caps anonymous pulls by data per source IP and hosted runners share IPs, so the
|
||||
# framework's image builds can fail here for hours. mirror.gcr.io serves the same Docker
|
||||
# official images under the same digests, so each base image the pinned framework builds from
|
||||
# is pulled there and named as its Dockerfile names it, as the framework's own CI does.
|
||||
# Digest-pinned names are set in containerd, whose images Docker reads only on the containerd
|
||||
# image store; switching stores restarts dockerd, so it happens before anything uses Docker.
|
||||
- name: Use the containerd image store
|
||||
run: |
|
||||
[ -s /etc/docker/daemon.json ] || echo '{}' | sudo tee /etc/docker/daemon.json >/dev/null
|
||||
jq '.features["containerd-snapshotter"] = true' /etc/docker/daemon.json > "$RUNNER_TEMP/daemon.json"
|
||||
sudo mv "$RUNNER_TEMP/daemon.json" /etc/docker/daemon.json
|
||||
sudo systemctl restart docker
|
||||
docker info --format '{{json .DriverStatus}}' | grep -q io.containerd.snapshotter
|
||||
- name: Acquire the pinned agentenv runner in isolated runner storage and check host Docker
|
||||
shell: bash
|
||||
run: |
|
||||
runner_root="${RUNNER_TEMP}/rea-agentenv-runner"
|
||||
python -m venv "${runner_root}"
|
||||
"${runner_root}/bin/python" -m pip install --require-virtualenv --no-cache-dir 'agentenv-framework==0.9.1334'
|
||||
"${runner_root}/bin/agent-env" --version
|
||||
docker version --format '{{.Server.Version}}'
|
||||
echo "REA_AGENT_ENV_COMMAND=${runner_root}/bin/agent-env" >> "${GITHUB_ENV}"
|
||||
echo "REA_DOCKER_COMMAND=$(command -v docker)" >> "${GITHUB_ENV}"
|
||||
- name: Pull the pinned framework's public ECR base images from mirror.gcr.io
|
||||
shell: bash
|
||||
run: |
|
||||
package="$("${REA_AGENT_ENV_COMMAND%/agent-env}/python" -c 'import agent_env, pathlib; print(pathlib.Path(agent_env.__file__).parent)')"
|
||||
grep -rhoE '^FROM +public\.ecr\.aws/docker/library/[^ ]+' --include='Dockerfile*' "${package}" | awk '{print $2}' | sort -u |
|
||||
while read -r image; do
|
||||
name=${image#public.ecr.aws/docker/library/}
|
||||
source=mirror.gcr.io/library/$name
|
||||
case $name in *@*) source=mirror.gcr.io/library/${name%%[:@]*}@${name#*@} ;; esac
|
||||
for attempt in 1 2 3; do
|
||||
docker pull --quiet "$source" && break
|
||||
if [ "$attempt" = 3 ]; then echo "::warning::couldn't pull $source; $image's build will pull it"; continue 2; fi
|
||||
sleep $((attempt * 10))
|
||||
done
|
||||
case ${name%@*} in *:*) docker tag "$source" "${image%@*}" ;; esac
|
||||
case $image in *@*) sudo ctr --namespace moby images tag "$source" "$image" >/dev/null ;; esac
|
||||
done
|
||||
- name: Export a bundle from the source-owned capture and run it with the agentenv runner
|
||||
run: npm run verify:agentenv:environment
|
||||
@@ -174,31 +174,34 @@ CLI commands use the same workflows.
|
||||
REA requires Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+, plus npm.
|
||||
Additional tools and host support depend on the target:
|
||||
|
||||
| Target | What REA returns | Requirements and guide |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Native binaries | Pseudocode, assembly, strings, symbols, calls and references | Hopper, Ghidra or IDA; [native analysis](docs/native-investigation.md) |
|
||||
| Offline ELF layout | Sections, segments, original symbols/relocations and static mitigation candidates | Caller-supplied pwntools on Linux x64; [binary diagnostics](docs/binary-diagnostics.md) |
|
||||
| Go binaries | Embedded compiler version, modules, replacements and build settings | Built-in static ELF/PE/thin Mach-O inspection; no Go installation required |
|
||||
| Rust binaries | Embedded cargo-auditable crate names, versions, sources and dependency edges | Built-in static ELF/PE/thin Mach-O inspection; [Rust binaries](docs/rust-binaries.md) |
|
||||
| WASM artifact | Validation, sections, exact import/export WAT forms and decoded WAT | Caller-supplied WABT 1.0.42; `inspect_wasm_artifact` |
|
||||
| EVM bytecode | Dispatch selectors, byte offsets, inferred arguments and mutability | Local raw/hex carrier on Linux x64; [offline EVM guide](docs/evm-bytecode.md) |
|
||||
| SQLite databases | Tables, columns, indexes, views, triggers and selected ordinary-table rows | Local database/WAL snapshot; Node.js 24.x >=24.15 or 26+ with native SQLite limits |
|
||||
| Recorded Linux crashes | Raw notes, every recorded thread's registers/signals and optional mapping candidates | Caller-supplied pwntools on Linux x64; optional GDB/pwndbg; [recorded crashes](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | Modules, imports, source maps, routes, IPC and native add-on relationships | Node.js and npm; Linux: procps-compatible `ps`; [application analysis](docs/javascript-application-workflows.md) |
|
||||
| Websites | Page structure, scripts, network observations and requested screenshots | A Chrome-family browser; [browser analysis](docs/browser-observation.md) |
|
||||
| Saved network captures | Requests, responses, exposed payloads and source locations | HAR or Fiddler Everywhere SAZ; mitmdump on Linux for native mitmproxy; [capture guide](docs/web-network-captures.md) |
|
||||
| .NET assemblies | Metadata, CIL instructions, declared native dependencies and build comparisons | None (static only); [managed-code guide](docs/managed-code-analysis.md) |
|
||||
| Android APKs | Manifest declarations, classes, decompiled methods and references | Headless JADX and a full JDK on Linux/macOS/Windows x64; [Android guide](docs/android-analysis.md) |
|
||||
| Android devices | Devices, packages, processes, logs, dumps, screen captures, and APK/file transfers | Caller-supplied adb (emulator or hardware); [ADB guide](docs/adb-device-analysis.md) |
|
||||
| JEB projects | Engine-backed project units, artifact digests and type/method pseudo-code | A caller-started JEB client serving MCP, selected with REA_JEB_MCP_URL; [JEB guide](docs/jeb-analysis.md) |
|
||||
| Android resources | Decoded manifest, string tables, locales and version facts | Caller-supplied Apktool launcher; [resource guide](docs/apktool-resource-analysis.md) |
|
||||
| Flutter apps | Dart snapshot hash, per-ABI library digests and engine build provenance | None (static APK parsing); [Flutter guide](docs/flutter-analysis.md) |
|
||||
| Firmware | Regions, extraction results and native-analysis handoffs | Binwalk / Unblob on Linux; [firmware guide](docs/firmware-analysis.md) |
|
||||
| Recorded packets | Selected fields, repeated values, filters and decode-as rules | Linux: TShark 4.x + prlimit; [capture guide](docs/packet-captures.md) |
|
||||
| Packet summaries | Endpoints, DNS, TLS SNI, protocol distribution and HTTP requests | Caller-supplied tshark and capinfos (Linux-verified); [pcap guide](docs/pcap-analysis.md) |
|
||||
| Recorded logic signals | Decoder annotations, sample ranges and diagnostics | Linux: sigrok-cli 0.7.2 + prlimit; [signal recordings](docs/signal-recordings.md) |
|
||||
| Packages and resources | File inventories, digests, plists, Apple bundle anatomy and extracted resources | [Artifact and JavaScript guide](docs/javascript-artifact-reconstruction.md), [Apple applications](docs/apple-application-analysis.md) |
|
||||
| Process behavior | Terminal output, interactions, exit and filesystem observations, and run comparisons | Linux/macOS with a native PTY; [process capture](docs/process-capture.md) |
|
||||
| Target | What REA returns | Requirements and guide |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Desktop installations | Runtime candidates, supporting files, component markers and next probes | Built-in bounded directory/header inspection; [desktop applications](docs/desktop-applications.md) |
|
||||
| Native binaries | Pseudocode, assembly, strings, symbols, calls and references | Hopper, Ghidra or IDA; [native analysis](docs/native-investigation.md) |
|
||||
| Offline ELF layout | Sections, segments, original symbols/relocations and static mitigation candidates | Caller-supplied pwntools on Linux x64; [binary diagnostics](docs/binary-diagnostics.md) |
|
||||
| Go binaries | Embedded compiler version, modules, replacements and build settings | Built-in static ELF/PE/thin Mach-O inspection; no Go installation required |
|
||||
| IL2CPP metadata | Unity global-metadata.dat assembly images, type and method names and index ranges | Built-in static decoding of versions 24, 27, 29, 31 and 39; no Unity required |
|
||||
| Godot PCK | Archive inventory and extraction, original header/directory facts and selected content identities | Built-in static formats 2, 3 and 4; no Godot required |
|
||||
| Rust binaries | Embedded cargo-auditable crate names, versions, sources and dependency edges | Built-in static ELF/PE/thin Mach-O inspection; [Rust binaries](docs/rust-binaries.md) |
|
||||
| WASM artifact | Validation, sections, exact import/export WAT forms and decoded WAT | Caller-supplied WABT 1.0.42; `inspect_wasm_artifact` |
|
||||
| EVM bytecode | Dispatch selectors, byte offsets, inferred arguments and mutability | Local raw/hex carrier on Linux x64; [offline EVM guide](docs/evm-bytecode.md) |
|
||||
| SQLite databases | Tables, columns, indexes, views, triggers and selected ordinary-table rows | Local database/WAL snapshot; Node.js 24.x >=24.15 or 26+ with native SQLite limits |
|
||||
| Recorded Linux crashes | Raw notes, every recorded thread's registers/signals and optional mapping candidates | Caller-supplied pwntools on Linux x64; optional GDB/pwndbg; [recorded crashes](docs/recorded-crashes.md) |
|
||||
| JavaScript / Electron | Modules, imports, source maps, routes, IPC and native add-on relationships | Node.js and npm; Linux: procps-compatible `ps`; [application analysis](docs/javascript-application-workflows.md) |
|
||||
| Websites | Page structure, scripts, network observations and requested screenshots | A Chrome-family browser; [browser analysis](docs/browser-observation.md) |
|
||||
| Saved network captures | Requests, responses, exposed payloads and source locations | HAR or Fiddler Everywhere SAZ; mitmdump on Linux for native mitmproxy; [capture guide](docs/web-network-captures.md) |
|
||||
| .NET assemblies | Metadata, CIL instructions, declared native dependencies and build comparisons | None (static only); [managed-code guide](docs/managed-code-analysis.md) |
|
||||
| Android APKs | Manifest declarations, classes, decompiled methods and references | Headless JADX and a full JDK on Linux/macOS/Windows x64; [Android guide](docs/android-analysis.md) |
|
||||
| Android devices | Devices, packages, processes, logs, dumps, screen captures, and APK/file transfers | Caller-supplied adb (emulator or hardware); [ADB guide](docs/adb-device-analysis.md) |
|
||||
| JEB projects | Engine-backed project units, artifact digests and type/method pseudo-code | A caller-started JEB client serving MCP, selected with REA_JEB_MCP_URL; [JEB guide](docs/jeb-analysis.md) |
|
||||
| Android resources | Decoded manifest, string tables, locales and version facts | Caller-supplied Apktool launcher; [resource guide](docs/apktool-resource-analysis.md) |
|
||||
| Flutter apps | Dart snapshot hash, per-ABI library digests and engine build provenance | None (static APK parsing); [Flutter guide](docs/flutter-analysis.md) |
|
||||
| Firmware | Regions, extraction results and native-analysis handoffs | Binwalk / Unblob on Linux; [firmware guide](docs/firmware-analysis.md) |
|
||||
| Recorded packets | Selected fields, repeated values, filters and decode-as rules | Linux: TShark 4.x + prlimit; [capture guide](docs/packet-captures.md) |
|
||||
| Packet summaries | Endpoints, DNS, TLS SNI, protocol distribution and HTTP requests | Caller-supplied tshark and capinfos (Linux-verified); [pcap guide](docs/pcap-analysis.md) |
|
||||
| Recorded logic signals | Decoder annotations, sample ranges and diagnostics | Linux: sigrok-cli 0.7.2 + prlimit; [signal recordings](docs/signal-recordings.md) |
|
||||
| Packages and resources | File inventories, digests, plists, Apple bundle anatomy and extracted resources | [Artifact and JavaScript guide](docs/javascript-artifact-reconstruction.md), [Apple applications](docs/apple-application-analysis.md) |
|
||||
| Process behavior | Terminal output, interactions, exit and filesystem observations, and run comparisons | Linux/macOS with a native PTY; [process capture](docs/process-capture.md) |
|
||||
|
||||
Static JavaScript and .NET inspection read the supplied files without running
|
||||
the application. Runtime capture runs or interacts with the selected target
|
||||
|
||||
@@ -458,6 +458,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
FunctionAnalysisObservation observation = analyzeFunctionObservation(request.params);
|
||||
JsonObject result = functionObservation(observation.dossier(), observation.limitations());
|
||||
result.add("relationship_coverage", observation.relationshipCoverage());
|
||||
if (observation.decompilerFailed()) result.addProperty("cache_policy", "live");
|
||||
yield result;
|
||||
}
|
||||
case "set_address_name", "set_addresses_names" -> {
|
||||
@@ -1540,12 +1541,23 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
requireKeys(params, Set.of("procedure"));
|
||||
Function function = resolveProcedure(requireString(params, "procedure"));
|
||||
InstructionScan scan = scanInstructions(function, Integer.MAX_VALUE);
|
||||
DecompileResults decompilation = decompile(function);
|
||||
DecompileResults decompilation;
|
||||
String decompilerFailure = null;
|
||||
try {
|
||||
decompilation = decompile(function);
|
||||
} catch (RequestFailure failure) {
|
||||
// The listing, reference and CFG facts below do not depend on the
|
||||
// native decompiler. Cancellation still ends the request.
|
||||
if (!failure.code.equals("decompile_failed")) throw failure;
|
||||
decompilation = null;
|
||||
decompilerFailure = failure.getMessage().strip();
|
||||
}
|
||||
String pseudocode = decompiledText(decompilation);
|
||||
JsonObject availability = new JsonObject();
|
||||
availability.add("pseudocode", facetAvailability(
|
||||
pseudocode != null,
|
||||
function.isExternal() ? "Ghidra does not decompile external functions."
|
||||
decompilerFailure != null ? decompilerFailure
|
||||
: function.isExternal() ? "Ghidra does not decompile external functions."
|
||||
: function.getBody().isEmpty() ? "Ghidra has no function body to decompile."
|
||||
: "Ghidra completed decompilation without returning pseudocode."
|
||||
));
|
||||
@@ -1594,9 +1606,16 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
result.add("basic_blocks", blocks);
|
||||
result.add(
|
||||
"native_api",
|
||||
nativeApiBoundary(function, decompilation)
|
||||
decompilerFailure == null
|
||||
? nativeApiBoundary(function, decompilation)
|
||||
: unavailableNativeApiBoundary(decompilerFailure)
|
||||
);
|
||||
result.add(
|
||||
"native_value_flow",
|
||||
decompilerFailure == null
|
||||
? nativeValueFlow(decompilation)
|
||||
: unavailableNativeValueFlow(decompilerFailure)
|
||||
);
|
||||
result.add("native_value_flow", nativeValueFlow(decompilation));
|
||||
// Successful full queries distinguish collected empty sets from missing facts.
|
||||
// Availability does not claim that Ghidra resolved every runtime relationship.
|
||||
for (String facet : List.of("comments", "callers", "callees", "references", "strings_names", "cfg")) {
|
||||
@@ -1643,6 +1662,12 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
!decompilation.getErrorMessage().isBlank()) {
|
||||
limitations.add("Ghidra decompiler diagnostic: " + decompilation.getErrorMessage());
|
||||
}
|
||||
if (decompilerFailure != null) {
|
||||
limitations.add(
|
||||
decompilerFailure + (decompilerFailure.endsWith(".") ? " " : ". ") +
|
||||
"Pseudocode, the native API boundary and p-code value flow are unavailable for this procedure; assembly, comments, callers, callees, references, strings, names and basic blocks come from the Ghidra program listing and were still collected."
|
||||
);
|
||||
}
|
||||
result.add("limitations", limitations);
|
||||
monitor.checkCancelled();
|
||||
int missingCallers = 0;
|
||||
@@ -1651,25 +1676,16 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
if (unrepresentedCaller(reference, callerFunctions)) missingCallers++;
|
||||
}
|
||||
return new FunctionAnalysisObservation(result, calls.limitations(),
|
||||
callRelationshipCoverage(calls, missingCallers));
|
||||
callRelationshipCoverage(calls, missingCallers), decompilerFailure != null);
|
||||
}
|
||||
|
||||
private JsonObject nativeValueFlow(DecompileResults decompilation) throws Exception {
|
||||
HighFunction highFunction =
|
||||
decompilation == null ? null : decompilation.getHighFunction();
|
||||
if (highFunction == null) {
|
||||
JsonObject unavailable = new JsonObject();
|
||||
unavailable.addProperty("available", false);
|
||||
unavailable.addProperty(
|
||||
"reason",
|
||||
return unavailableNativeValueFlow(
|
||||
"Ghidra has no HighFunction p-code model for this procedure"
|
||||
);
|
||||
JsonArray limitations = new JsonArray();
|
||||
limitations.add(
|
||||
"No p-code operations or def-use relationships were recovered for this procedure."
|
||||
);
|
||||
unavailable.add("limitations", limitations);
|
||||
return unavailable;
|
||||
}
|
||||
|
||||
List<PcodeOpAST> selected = new ArrayList<>();
|
||||
@@ -2357,6 +2373,18 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static JsonObject unavailableNativeValueFlow(String reason) {
|
||||
JsonObject unavailable = new JsonObject();
|
||||
unavailable.addProperty("available", false);
|
||||
unavailable.addProperty("reason", reason);
|
||||
JsonArray limitations = new JsonArray();
|
||||
limitations.add(
|
||||
"No p-code operations or def-use relationships were recovered for this procedure."
|
||||
);
|
||||
unavailable.add("limitations", limitations);
|
||||
return unavailable;
|
||||
}
|
||||
|
||||
private static JsonObject unavailableNativeApiBoundary(String reason) {
|
||||
JsonObject unavailable = new JsonObject();
|
||||
unavailable.addProperty("available", false);
|
||||
@@ -4875,7 +4903,7 @@ public final class ReaGhidraBridge extends HeadlessScript {
|
||||
private record InventoryItem(Address address, String value, JsonObject facts) {}
|
||||
private record AddressNameRequest(String inputAddress, Address address, String name) {}
|
||||
private record FunctionEntry(Function function, InventoryItem item) {}
|
||||
private record FunctionAnalysisObservation(JsonObject dossier, List<String> limitations, JsonObject relationshipCoverage) {}
|
||||
private record FunctionAnalysisObservation(JsonObject dossier, List<String> limitations, JsonObject relationshipCoverage, boolean decompilerFailed) {}
|
||||
private record CallBoundaryObservation(List<String> limitations, Integer unrepresentedCalleeCalls, JsonArray unresolvedCalls) {}
|
||||
private record SwiftStringLiteralDecode(JsonArray literals, JsonArray omissions) {}
|
||||
private record SwiftLiteralLayout(
|
||||
|
||||
+43
-3
@@ -236,6 +236,15 @@ def _name_matches(document, address, name):
|
||||
return observed == name or (not name and observed is None)
|
||||
|
||||
|
||||
def _bookmark_name(document, address):
|
||||
getter = getattr(document, "getBookmarkName", None)
|
||||
if not callable(getter):
|
||||
raise CapabilityUnavailableError(
|
||||
"Hopper's public Python API does not expose getBookmarkName"
|
||||
)
|
||||
return getter(address)
|
||||
|
||||
|
||||
def _address(document, value=None):
|
||||
"""Keep explicit hexadecimal coordinates distinct from symbol names."""
|
||||
if value is None:
|
||||
@@ -994,15 +1003,46 @@ def _dispatch(method, params):
|
||||
observed = getter(address)
|
||||
return observed == comment or (comment == "" and observed is None)
|
||||
if method == "list_bookmarks":
|
||||
return [{"address": _hex(item), "name": document.getBookmarkName(item)} for item in document.getBookmarks()]
|
||||
return [{"address": _hex(item), "name": _bookmark_name(document, item)} for item in document.getBookmarks()]
|
||||
if method == "set_bookmark":
|
||||
address = _address(document, params.get("address"))
|
||||
_segment(document, address)
|
||||
name = params.get("name")
|
||||
if name is not None:
|
||||
_api_text(name, "Bookmark name")
|
||||
document.setBookmarkAtAddress(address, name)
|
||||
return document.hasBookmarkAtAddress(address)
|
||||
has_bookmark = getattr(document, "hasBookmarkAtAddress", None)
|
||||
if not callable(has_bookmark):
|
||||
raise CapabilityUnavailableError(
|
||||
"Hopper's public Python API does not expose hasBookmarkAtAddress"
|
||||
)
|
||||
exists = has_bookmark(address)
|
||||
if name is None and exists:
|
||||
# Omission means preserve the existing bookmark and its name.
|
||||
return True
|
||||
if not callable(getattr(document, "getBookmarkName", None)):
|
||||
raise CapabilityUnavailableError(
|
||||
"Hopper's public Python API does not expose getBookmarkName"
|
||||
)
|
||||
if name == "" and exists:
|
||||
rename = getattr(document, "renameBookmarkAtAddress", None)
|
||||
if not callable(rename):
|
||||
raise CapabilityUnavailableError(
|
||||
"Hopper's public Python API does not expose renameBookmarkAtAddress"
|
||||
)
|
||||
rename(address, name)
|
||||
else:
|
||||
setter = getattr(document, "setBookmarkAtAddress", None)
|
||||
if not callable(setter):
|
||||
raise CapabilityUnavailableError(
|
||||
"Hopper's public Python API does not expose setBookmarkAtAddress"
|
||||
)
|
||||
setter(address, name)
|
||||
if not has_bookmark(address):
|
||||
return False
|
||||
observed = _bookmark_name(document, address)
|
||||
if name is None:
|
||||
return observed is None or observed == ""
|
||||
return observed == name or (name == "" and observed is None)
|
||||
if method == "unset_bookmark":
|
||||
address = _address(document, params.get("address"))
|
||||
# Permit removing a legacy orphan bookmark, but do not report success
|
||||
|
||||
@@ -95,7 +95,7 @@ def main():
|
||||
try:
|
||||
encoded = json.dumps(reply, ensure_ascii=True, allow_nan=False).encode("utf-8")
|
||||
if len(encoded) > OUTPUT_BYTES:
|
||||
encoded = json.dumps({"ok": False, "reason": "output-limit", "message": "Complete debugger context exceeds64 MiB reply budget."}).encode("utf-8")
|
||||
encoded = json.dumps({"ok": False, "reason": "output-limit", "message": "Complete debugger context exceeds the 64 MiB reply budget."}).encode("utf-8")
|
||||
descriptor = os.open(request["reply_path"], os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
||||
with os.fdopen(descriptor, "wb") as handle:
|
||||
handle.write(encoded)
|
||||
|
||||
@@ -14,7 +14,7 @@ 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"):
|
||||
def inspect(self, failure=None, connection="core", symbols=None, architecture="x86_64-little-endian", objfile="recorded-executable"):
|
||||
commands, reads, queries = [], [], []
|
||||
state = {"executable": None, "symbols": None, "connection": None}
|
||||
gdb = types.ModuleType("gdb")
|
||||
@@ -50,7 +50,7 @@ class CoreContextTests(unittest.TestCase):
|
||||
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")]
|
||||
return [types.SimpleNamespace(start=4096, end=8192, offset=0, flags=5, objfile=objfile)]
|
||||
|
||||
pwndbg.aglib.vmmap.get = maps
|
||||
modules = {module.__name__: module for module in [
|
||||
@@ -104,6 +104,15 @@ class CoreContextTests(unittest.TestCase):
|
||||
self.assertEqual(reads, [])
|
||||
self.assertEqual(queries, [])
|
||||
|
||||
def test_output_limit_message_separates_the_words_and_names_the_limit(self):
|
||||
result, _, reads, queries = self.inspect(objfile="m" * (64 * 1024 * 1024))
|
||||
self.assertFalse(result["ok"])
|
||||
self.assertEqual(result["reason"], "output-limit")
|
||||
self.assertNotIn("exceeds64", result["message"])
|
||||
self.assertEqual(result["message"], "Complete debugger context exceeds the 64 MiB reply budget.")
|
||||
self.assertEqual(reads, [])
|
||||
self.assertEqual(queries, ["maps"])
|
||||
|
||||
def test_wrong_connection_or_remaining_symbols_never_queries_mappings(self):
|
||||
for options in [{"connection": "native"}, {"symbols": "unexpected-symbols"}]:
|
||||
with self.subTest(options=options):
|
||||
|
||||
@@ -29,6 +29,7 @@ export default defineConfig({
|
||||
items: [
|
||||
{ text: "Native binaries", link: "/native-investigation" },
|
||||
{ text: "Go build metadata", link: "/go-binaries" },
|
||||
{ text: "IL2CPP metadata", link: "/il2cpp-metadata" },
|
||||
{ text: "Rust dependencies", link: "/rust-binaries" },
|
||||
{
|
||||
text: "JavaScript and Electron",
|
||||
@@ -43,12 +44,14 @@ export default defineConfig({
|
||||
text: "Web network captures",
|
||||
link: "/web-network-captures",
|
||||
},
|
||||
{ text: "Agent environments", link: "/agent-environments" },
|
||||
{ text: "Electron observation", link: "/electron-observation" },
|
||||
{ text: "Electron fuses", link: "/electron-fuses" },
|
||||
{ text: "Process capture", link: "/process-capture" },
|
||||
{ text: ".NET assemblies", link: "/managed-code-analysis" },
|
||||
{ text: "Android applications", link: "/android-analysis" },
|
||||
{ text: "Apple applications", link: "/apple-application-analysis" },
|
||||
{ text: "Desktop installations", link: "/desktop-applications" },
|
||||
{ text: "Firmware", link: "/firmware-analysis" },
|
||||
{ text: "SQLite databases", link: "/sqlite-databases" },
|
||||
{ text: "Packet captures", link: "/packet-captures" },
|
||||
|
||||
@@ -0,0 +1,426 @@
|
||||
# Agent environments from recorded captures
|
||||
|
||||
Use `rea environment-serve` to serve an environment manifest as an agent
|
||||
environment: an MCP server whose tools answer from the records of one retained
|
||||
web network capture, plus the environment card the agentenv framework reads to
|
||||
discover it. Nothing is fetched from the recorded application; every answer is
|
||||
retained Evidence. `rea environment-export` writes the same pair as a bundle
|
||||
folder `agent-env run` builds and runs; see [Export a bundle](#export-a-bundle).
|
||||
|
||||
```sh
|
||||
rea inspect-web-network-capture /absolute/session.har har --format json > capture.json
|
||||
rea inspect-analysis-view view.json --format json > manifest.json
|
||||
rea environment-serve manifest.json --evidence capture.json --port 8765
|
||||
```
|
||||
|
||||
The first command inspects the capture; the second projects the
|
||||
[environment manifest](web-network-captures.md#environment-manifest) view from
|
||||
it, where `view.json` wraps the capture Evidence as the inline source:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": { "kind": "inline", "evidence": { "evidence_id": "ev_…" } },
|
||||
"view": {
|
||||
"kind": "environment-manifest",
|
||||
"template_rules": ["after-uid-literal", "opaque-token", "sibling-variance"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The third command serves that manifest. Both served files are complete REA
|
||||
Evidence JSON as the commands write it with `--format json`; the `evidence`
|
||||
member of `view.json` is the whole of `capture.json`.
|
||||
|
||||
## From a browser scenario
|
||||
|
||||
The capture can also come from REA itself: a
|
||||
[browser scenario](browser-scenario-contract.md) records a scripted session
|
||||
in a browser REA launches, and its Evidence is a manifest parent like a HAR
|
||||
inspection. Select the `network` event family and response bodies. A replayed
|
||||
reply carries up to 256 KiB of a recorded body, but the scenario keeps at most
|
||||
`max_network_body_bytes` of each body (64 KiB by default), and only within its
|
||||
allocation budgets: base64 is charged about 11 bytes per body byte, and
|
||||
redaction more the shorter a declared secret is (about 35 bytes per body byte
|
||||
for a 12-character `admin_password`). A body over
|
||||
`max_network_body_allocation_bytes` (1 MiB by default) is kept as `unavailable`
|
||||
with that reason; one over `max_event_allocation_bytes` (4 MiB) or
|
||||
`max_total_event_allocation_bytes` (16 MiB) loses its whole content event,
|
||||
headers included, which the capture lists among its omissions. Either way its
|
||||
tool replays no body. The example raises all four so that 256 KiB bodies are
|
||||
kept when the declared secret has at least 12 characters:
|
||||
|
||||
```json
|
||||
{
|
||||
"browser": { "mode": "launch", "executable_path": "/usr/bin/chromium" },
|
||||
"start_url": { "url": "http://127.0.0.1:5230/auth", "query": [] },
|
||||
"secrets": [
|
||||
{ "secret_id": "admin_password", "environment_variable": "APP_PASSWORD" }
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"step_id": "user",
|
||||
"action": "fill",
|
||||
"locator": { "kind": "css", "selector": "input[name=username]" },
|
||||
"value": { "source": "literal", "value": "admin" }
|
||||
},
|
||||
{
|
||||
"step_id": "password",
|
||||
"action": "fill",
|
||||
"locator": { "kind": "css", "selector": "input[type=password]" },
|
||||
"value": { "source": "secret", "secret_id": "admin_password" }
|
||||
},
|
||||
{
|
||||
"step_id": "sign-in",
|
||||
"action": "press",
|
||||
"locator": { "kind": "css", "selector": "input[type=password]" },
|
||||
"key": "Enter"
|
||||
},
|
||||
{
|
||||
"step_id": "first-memo",
|
||||
"action": "goto",
|
||||
"destination": { "url": "http://127.0.0.1:5230/m/1", "query": [] },
|
||||
"wait_until": "load"
|
||||
}
|
||||
],
|
||||
"capture": {
|
||||
"events": ["network"],
|
||||
"network": {
|
||||
"request_body": true,
|
||||
"response_body": true,
|
||||
"header_values": false
|
||||
},
|
||||
"limits": {
|
||||
"max_network_body_bytes": 262144,
|
||||
"max_network_body_allocation_bytes": 12582912,
|
||||
"max_event_allocation_bytes": 12582912,
|
||||
"max_total_event_allocation_bytes": 67108864
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```sh
|
||||
APP_PASSWORD=… rea capture-browser-scenario scenario.json --format json > capture.json
|
||||
rea inspect-analysis-view view.json --format json > manifest.json # view.json wraps capture.json as before
|
||||
rea environment-serve manifest.json --evidence capture.json --port 8765
|
||||
```
|
||||
|
||||
Each network transaction becomes one record, in request order, and the
|
||||
record's ordinal is its zero-based position; a transaction whose URL carried a
|
||||
redacted secret is left unrepresented rather than templated, and one the
|
||||
capture cannot attribute to a single request and response is left
|
||||
unrepresented too. Tools are chosen by response media type, so a scenario that
|
||||
selected neither response bodies nor header values is refused. The browser
|
||||
scenario redacts credential headers and the secrets it declares; values the
|
||||
application returns in response bodies, such as session tokens, are kept, and
|
||||
the replayed body is the bytes the browser exposes for each response, with any
|
||||
declared secret replaced by its redaction marker (a body that redaction had to
|
||||
empty replays no body).
|
||||
|
||||
## What a manifest is
|
||||
|
||||
An environment manifest is a projection of one application's recorded request
|
||||
lines into a tool surface: one tool per method and path template on the
|
||||
primary origin, with inferred path parameters, observed query parameter
|
||||
names, value-free body shapes and pointers to the parent records each tool
|
||||
cites. It carries no bodies and no headers. The manifest's
|
||||
`parent_evidence_id` names the capture Evidence it was projected from; the
|
||||
serve command refuses a manifest whose parent is not the supplied `--evidence`
|
||||
file with an `invalid_request` error naming both identifiers, so a manifest can
|
||||
never be served from records it did not cite.
|
||||
|
||||
The MCP catalog and environment card use a 1 KiB UTF-8 budget for each
|
||||
captured display sample. Observation, body-key and cited-record lists show
|
||||
whole values with omission counts; long method, path and query-name displays
|
||||
show a marked prefix. Complete values remain in manifest Evidence. Argument
|
||||
names longer than 64 characters use collision-safe `path_<n>` or `query_<n>` aliases; replay maps
|
||||
those aliases to the complete captured names.
|
||||
|
||||
Discovery is admitted as a complete catalog under a 1 MiB serialization
|
||||
reservation before the listener binds. Reservation charges up to six bytes per
|
||||
UTF-16 code unit for JSON escaping/UTF-8 expansion, plus schema overhead;
|
||||
a catalog may therefore be refused even when its compact JSON is smaller.
|
||||
This was calibrated with 1500 query fields and eight concurrent SDK discovery
|
||||
requests (183,798-byte card, 507.27 MiB sampled process-family RSS). Over-capacity
|
||||
catalogs return `resource_constraint`, retain complete input Evidence, and require
|
||||
a smaller capture or manifest. Fields and tools are never silently omitted.
|
||||
|
||||
## What replay means and does not mean
|
||||
|
||||
Each MCP tool call selects one of the records its manifest tool cites and
|
||||
returns that record's recorded response. Selection compares the supplied path
|
||||
parameters against the record's path segments at the parameter indexes, then
|
||||
the supplied query values against the record's query string, and keeps the
|
||||
record with the most matches; ties keep manifest order. The result reports
|
||||
where the answer came from:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"content_type": "application/json",
|
||||
"body": { "id": 2, "login": "probe" },
|
||||
"replay": {
|
||||
"record_ordinal": 5,
|
||||
"evidence_id": "ev_…",
|
||||
"exact_match": true,
|
||||
"recorded_path": "/api/users/2",
|
||||
"recorded_query": null,
|
||||
"recorded_line_truncated": false,
|
||||
"body_recorded": true,
|
||||
"body_encoding": "utf-8",
|
||||
"truncated": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`exact_match` is true when every supplied path parameter and query value
|
||||
equals the recorded one; it says nothing about parameters the caller omitted.
|
||||
Recorded query parsing runs only when supplied query arguments affect selection.
|
||||
A path value matches a recorded segment as written or after percent-decoding;
|
||||
when both kinds of match are available, the record whose segment equals the
|
||||
value as written is chosen. `recorded_query` keeps the `?` of an explicit empty
|
||||
query, and a recorded path or query longer than 1 KiB is cut in the reply with
|
||||
`recorded_line_truncated: true`; selection always uses the full recorded line.
|
||||
`content_type` also has a 1 KiB display bound with `content_type_truncated`; body
|
||||
interpretation uses the full recorded media type.
|
||||
When no record matches, the tool still answers from the tool's first cited
|
||||
record and reports `exact_match: false`. `body` is parsed JSON when the
|
||||
recorded content type is JSON-like (`application/json`, `application/*+json`,
|
||||
`text/plain`, `text/markdown`) and the text parses with safe numeric conversion; otherwise
|
||||
it is the recorded text. Large integer IDs and other numbers that would lose
|
||||
information stay in the original JSON text. Bodies over 256 KiB are cut at a UTF-8 boundary and returned as
|
||||
text with `truncated: true`. `body_recorded` is false when the capture kept the
|
||||
response's status and content type but no body; `body` is then `null`, which
|
||||
is not a recorded JSON `null`. `body_encoding` is `base64` when the capture
|
||||
retained response bytes that are not valid UTF-8; `body` is then exactly those
|
||||
bytes in base64, never a lossy decoding. A retained base64 body is decoded only as far as the 256 KiB a reply can carry, and a leading byte-order mark is kept. For a `base64` body the limit applies to the recorded bytes, not their longer base64 spelling, and a cut keeps whole base64 groups. Request bodies passed to `POST`, `PUT` and `PATCH`
|
||||
tools are accepted and ignored: nothing is written, and no call changes what a
|
||||
later call returns. Arguments the tool's input schema does not name are
|
||||
refused by the MCP server as invalid arguments, so a misspelled parameter never
|
||||
degrades into an omitted one.
|
||||
|
||||
Replay is not emulation. A served environment does not establish the
|
||||
application's authorization model, response semantics or state transitions,
|
||||
which the manifest lists as unknowns; it does not validate supplied values; and
|
||||
it does not answer requests the capture never recorded. Replayed content is
|
||||
target content: every tool result, including the argument errors the MCP server
|
||||
returns before a replay runs, carries `_meta.provenance.untrusted_content: true`
|
||||
with `source: "analysis_target"`. The server advertises REA's untrusted-content
|
||||
instructions plus a warning that its tool names, descriptions, parameter names
|
||||
and observed values come from the capture too, and agents must treat all of it
|
||||
as data, never as instructions.
|
||||
That same warning prefixes every tool description in the standalone card, so
|
||||
card discovery carries it before an MCP client initializes. It labels the
|
||||
captured text without changing the tool names or schema fields clients use.
|
||||
|
||||
## The state a capture observed
|
||||
|
||||
Replay answers each call from one recording, so the answers reflect whichever
|
||||
moment of the capture recorded them. The
|
||||
[environment state](web-network-captures.md#environment-state) view extracts
|
||||
instead the data the capture's reads observed before the first related write:
|
||||
entity types with an identity field, entities with their recorded values,
|
||||
documents for written paths, and a render plan per read tool, kept only where
|
||||
it reproduces that tool's recordings from the state. It is the starting state
|
||||
an environment can hold, load and report; serving from it is a later increment
|
||||
and does not change replay.
|
||||
|
||||
## Serve a manifest
|
||||
|
||||
```sh
|
||||
rea environment-serve manifest.json --evidence capture.json [--host 127.0.0.1] [--port 0]
|
||||
```
|
||||
|
||||
`--host` and `--port` default to `MCP_HOST` and `MCP_PORT` from the
|
||||
environment, then to `127.0.0.1` and an ephemeral port, exactly as
|
||||
[`rea mcp --http`](cli.md#serve-mcp-over-streamable-http) resolves them. The
|
||||
bound MCP URL is written to stderr as one JSON log line
|
||||
(`"msg":"MCP transport listening"` with `url`); `REA_LOG_LEVEL` overrides that
|
||||
log level. `ENVIRONMENT_NAME` renames the served environment; otherwise the
|
||||
manifest's `name` is used, and a set but blank value is refused as
|
||||
`configuration_invalid` before binding rather than silently falling back. The
|
||||
listener serves one MCP client at a time with the same session, `Host` header
|
||||
and shutdown contract as [transports](mcp-contracts.md#transports) describes.
|
||||
It runs until SIGINT or SIGTERM, then closes the listener, prints one
|
||||
`{"status":"stopped", …}` document and exits `130` or `143`.
|
||||
Signals during input loading or DNS/bind cancel the owning reader/listener before
|
||||
a listening announcement;
|
||||
that path returns a structured `cancelled` result instead of a stopped-listener
|
||||
document, with the same signal exit status.
|
||||
|
||||
Three routes are served:
|
||||
|
||||
| Route | Answer |
|
||||
| --------------------------------- | ---------------------------------------------------------------------- |
|
||||
| `POST /mcp` | The MCP server: one tool per manifest tool, replaying retained records |
|
||||
| `GET /.well-known/agent-env.json` | The environment card |
|
||||
| `POST /agentenv` | JSON-RPC `-32601 method not found: <method>` for every method |
|
||||
|
||||
Numeric JSON-RPC IDs outside JavaScript's safe range or whose precision would
|
||||
be lost are refused as invalid requests with a null ID. They are never echoed
|
||||
as a different number.
|
||||
|
||||
The data plane at `/agentenv` is advertised by the card but carries no
|
||||
operations in this release; the error names the requested method so a caller
|
||||
can see that the method, not the transport, is unsupported. Its request body is
|
||||
read within the same 4 MiB bound the MCP endpoint applies: a larger body is
|
||||
answered `413` with a JSON-RPC `-32000` error before any of it is parsed, and a
|
||||
body that is not JSON is answered `400` with `-32700`. A notification (no `id`)
|
||||
gets `202` and no body, a batch gets one reply per request in it, and an empty
|
||||
batch, a batch of more than 100 messages, or a message that is not a JSON-RPC
|
||||
request (including an `id` that is not a string, number or null, or `params`
|
||||
that are not an object or array) gets `-32600 invalid request`.
|
||||
|
||||
## The environment card
|
||||
|
||||
The card is the discovery document the agentenv framework reads before
|
||||
connecting to `/mcp`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "grafana-example",
|
||||
"protocolVersion": "1.0",
|
||||
"url": "/agentenv",
|
||||
"preferredTransport": "JSONRPC",
|
||||
"additionalInterfaces": [{ "url": "/mcp", "transport": "mcp" }],
|
||||
"capabilities": {
|
||||
"extensions": null,
|
||||
"tools": [
|
||||
{
|
||||
"name": "get_users_by_id",
|
||||
"description": "GET /api/users/{numeric}; replays retained records [5, 6]; evidence ev_…",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Path segment 2 (numeric); observed: 2, 3"
|
||||
}
|
||||
},
|
||||
"required": ["id"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"operations": []
|
||||
},
|
||||
"children_environments": null
|
||||
}
|
||||
```
|
||||
|
||||
`capabilities.tools` lists the same names, descriptions and input schemas that
|
||||
`tools/list` advertises over MCP. Path parameters are required strings whose
|
||||
description names the segment index, the templating rule and the observed
|
||||
values (sampled within the list budget above; the manifest keeps them all);
|
||||
query parameter names are optional strings; write methods accept an
|
||||
optional `body` object described by the recorded top-level key types. Each
|
||||
input keeps its own argument: a query name equal to a path parameter's name is
|
||||
advertised as `query_<name>`, a query name longer than 64 characters as
|
||||
`query_<n>` (its position) with the captured name sampled in its description,
|
||||
and the write body as `request_body` when a query parameter is named `body`. Every
|
||||
schema closes with `additionalProperties: false`, a key the echo example's
|
||||
schema does not carry.
|
||||
`extensions`, `operations` and `children_environments` are fixed to `null`,
|
||||
`[]` and `null`: a replay environment declares no extensions, no data-plane
|
||||
operations and no children.
|
||||
|
||||
## Export a bundle
|
||||
|
||||
`rea environment-export` writes the same pair as a bundle folder the agentenv
|
||||
framework runs directly, shaped like its own echo example:
|
||||
|
||||
```sh
|
||||
rea environment-export manifest.json --evidence capture.json ./grafana-example --json
|
||||
agent-env run ./grafana-example
|
||||
```
|
||||
|
||||
```text
|
||||
grafana-example/
|
||||
README.md provenance, replay limits, run command, rights note
|
||||
envs/grafana-example/Dockerfile pinned image: node:24.18.0-bookworm-slim + rea-agents@<this version>
|
||||
envs/grafana-example/env.toml environment_name = "grafana-example"
|
||||
envs/grafana-example/manifest.json byte-exact copy of manifest.json
|
||||
envs/grafana-example/evidence.json byte-exact copy of capture.json
|
||||
artifacts/check_grafana-example/verify.py the replay verifier
|
||||
tasks/smoke.json deploy_env (local sandbox), then env_outcome_verifier
|
||||
```
|
||||
|
||||
The command reads each file once, after checking that reading and parsing the
|
||||
whole file, including sealing traversal, fits the process's live heap headroom (a file that does not is
|
||||
refused as a `resource_constraint` before it is read), then authenticates and cross-checks the
|
||||
pair as `environment-serve` does, so a manifest projected from another capture
|
||||
is refused with the same `invalid_request` before anything is written, and the
|
||||
bytes it copies are the bytes it authenticated. The bundle directory must be
|
||||
absent or empty; an occupied directory is refused and nothing in it changes.
|
||||
The complete tool catalog must also fit the server's advertised catalog budget
|
||||
before any files are written. Privately parsed JSON is sealed once so authentication
|
||||
and replay share immutable response trees rather than copying the capture repeatedly.
|
||||
`--name` replaces the manifest's portable kebab-case name as the env folder and env id
|
||||
(Windows device names such as `con` and `lpt1` are refused);
|
||||
`--package-tarball <path>` copies a packed `rea-agents` tarball into the env
|
||||
folder and makes the Dockerfile install it instead of the registry package and
|
||||
remove it in the same layer, which is how a checkout rather than a release is
|
||||
run. The command prints the
|
||||
written files with their sizes and the pinned package source.
|
||||
|
||||
Export uses the shared output-tree owner to check directory identities around
|
||||
exclusive creates, verify written bytes, and reject unowned entries before
|
||||
reporting success. It does not delete an inspectable partial folder on failure.
|
||||
Node's portable pathname APIs leave a syscall-boundary race, so the output's
|
||||
parent directories must be trusted. On POSIX output files use mode `0600`;
|
||||
Windows inherits ACLs and REA does not establish owner-only access there. Choose
|
||||
an already private Windows destination when captured content needs it.
|
||||
|
||||
The Dockerfile sets `MCP_HOST=0.0.0.0` and `MCP_PORT=18765`, the port the
|
||||
framework maps for an MCP server env; the framework's own deploy supplies
|
||||
`MCP_HOST`, `MCP_PORT` and `ENVIRONMENT_NAME`, so the served card carries the
|
||||
env's name. It installs Debian's `python3` because the framework's local
|
||||
sandbox probes a container's readiness with `python3` inside it, which the slim
|
||||
Node image lacks. It copies the two inputs owned by the image's non-root `node`
|
||||
user, so a private host umask cannot leave them unreadable, runs as that user and starts
|
||||
`rea environment-serve /env/manifest.json --evidence /env/evidence.json`. The
|
||||
`env.toml` names the environment because the framework otherwise reads the
|
||||
name from Python sources the image does not have.
|
||||
|
||||
`verify.py` is the framework's `async def verify(mcp_url)` shape over its
|
||||
Python MCP client: it lists the tools and checks the count equals the
|
||||
manifest's, calls the first `GET` tool without path parameters with no
|
||||
arguments and checks the call did not fail and its replayed `status` equals the
|
||||
recorded status of that tool's first cited record (an unknown recorded status
|
||||
stays unknown; it is not borrowed from another record), and returns one
|
||||
`replay` result whose description
|
||||
reports both checks. A manifest with no such tool yields a verifier that checks
|
||||
the count only and says so. `tasks/smoke.json` deploys the env on the local
|
||||
sandbox and runs that verifier against the env's MCP endpoint; the first run
|
||||
also builds the framework's own infrastructure, and the run tears everything
|
||||
down when it ends.
|
||||
|
||||
The README written into the bundle records the capture artifact's SHA-256, the
|
||||
capture and manifest Evidence ids, the manifest view digest, the tool count and
|
||||
the package source, and states that the recorded content belongs to the
|
||||
captured application and must not be published without the rights to it.
|
||||
|
||||
## Compatibility verification
|
||||
|
||||
`npm run verify:agentenv:environment` is the hosted `real-agentenv` lane
|
||||
described in [testing](testing.md#exported-environment-bundles). It projects
|
||||
the Grafana-shaped source-owned capture through the built CLI, packs the
|
||||
checkout, exports a bundle with `--package-tarball`, runs it with the pinned
|
||||
`agentenv-framework` release on the runner's Docker and requires the smoke task
|
||||
to pass with no container left behind other than the framework's own
|
||||
image-registry helper, which the receipt reports. It proves that one exported bundle runs
|
||||
on one framework release; it does not claim any hosted platform or other
|
||||
framework versions.
|
||||
|
||||
## Resource and lifecycle boundaries
|
||||
|
||||
Both Evidence files are read once at startup through the bounded JSON input
|
||||
reader and authenticated as Evidence before the listener binds; a file that is
|
||||
not Evidence, a manifest of another view kind, a capture of another format, or
|
||||
a manifest projected from a different capture is refused before any port is
|
||||
opened. Because authentication covers the capture's records, a manifest whose
|
||||
parent check passes cites only records the capture retains. The server keeps
|
||||
only the cited records' request lines and response text in memory. No outbound
|
||||
network request is made, no process is launched and nothing is written to
|
||||
disk. A listener that fails after binding is logged and ends the command with
|
||||
a `resource_constraint` error and exit `1` instead of serving nothing.
|
||||
@@ -180,8 +180,10 @@ rea trace-dylib-resolution MyApp.app --root Contents/MacOS/MyApp --architecture
|
||||
|
||||
Load commands are parsed in TypeScript from Mach-O and FAT headers, including
|
||||
the `dylib_use_command` encoding newer linkers emit for flags such as delayed
|
||||
initialization. Nothing is executed and no Apple tool is required, so the tool
|
||||
also works off macOS.
|
||||
initialization, and the `LC_LAZY_LOAD_DYLIB_INFO` records they emit for
|
||||
`-lazy_library`. Lazy-info records carry no dylib versions, so their
|
||||
`current_version` and `compatibility_version` are null. Nothing is executed and
|
||||
no Apple tool is required, so the tool also works off macOS.
|
||||
|
||||
Resolution follows dyld:
|
||||
|
||||
@@ -222,7 +224,8 @@ cache rather than on disk, so REA does not check them against the host.
|
||||
`findings` are derived, not observed:
|
||||
|
||||
- `required-load-unresolved`, `weak-load-unresolved`, and `lazy-load-unresolved`.
|
||||
`LC_LAZY_LOAD_DYLIB` dependencies are resolved but not traversed, because dyld
|
||||
Lazy dependencies (`lazy: true`, from `LC_LAZY_LOAD_DYLIB` or
|
||||
`LC_LAZY_LOAD_DYLIB_INFO`) are resolved but not traversed, because dyld
|
||||
loads them on first use; an unresolved one is not a launch failure.
|
||||
- `earlier-rpath-candidate-absent`: a compatible Mach-O placed at an earlier
|
||||
modeled search path could take precedence, subject to unmodeled search inputs
|
||||
|
||||
@@ -34,7 +34,7 @@ graph TD
|
||||
GHIDRA["src/ghidra/<br/>Ghidra discovery, profile & function analysis<br/>snapshot, Unix/Windows transport, bounded lifecycle"]
|
||||
IDA["src/ida/<br/>Upstream MCP contract adaptation<br/>live GUI binding or owned headless database"]
|
||||
IDA_MCP["External ida-pro-mcp<br/>GUI proxy or database supervisor<br/>upstream installation and engine lifecycle"]
|
||||
AUX["src/artifacts/ + src/native/<br/>artifact inventory and extraction<br/>format readers and native capabilities"]
|
||||
AUX["src/artifacts/ + src/native/<br/>artifact inventory, bounded directory triage and extraction<br/>format readers and native capabilities"]
|
||||
BROWSER["src/browser/<br/>CDP / Playwright capture<br/>captured scripts and native module resolution<br/>execution/: coverage, listener and source attribution"]
|
||||
JS_CODEC["src/javascript/sourceMaps/<br/>pinned upstream map codec<br/>owned heap-limited Node process, complete point evidence"]
|
||||
INSPECTOR["src/inspector/<br/>passive V8 Inspector discovery and capture<br/>script and target locations"]
|
||||
|
||||
@@ -107,6 +107,11 @@ Through MCP, pass the same object as the `capture_browser_scenario` arguments.
|
||||
The final step's DOM and accessibility artifacts retain the page state at that
|
||||
capture time inline. REA still launches and cleans up the browser profile.
|
||||
|
||||
Action `timeout_ms` and `wait_for_timeout.duration_ms` accept integers from 1
|
||||
through 2,147,483,647 milliseconds, matching the underlying timer representation.
|
||||
Larger values receive an input error before browser launch. Omit `timeout_ms`
|
||||
to keep an action pending until completion or request cancellation.
|
||||
|
||||
The click's `timeout_ms` covers the click action. A non-navigating `fetch` or
|
||||
delayed render can finish later, so use `wait_for` to observe a page-specific
|
||||
ready condition before final capture. A page that updates synchronously can
|
||||
|
||||
+116
-3
@@ -30,6 +30,13 @@ jq -c '{source: {kind: "inline", evidence: .}, view: {kind: "summary"}}' app-evi
|
||||
rea inspect-analysis-view app-view.json
|
||||
```
|
||||
|
||||
The request can also name the saved file instead of embedding it:
|
||||
`{"source": {"kind": "evidence-file", "path": "app-evidence.json"}, "view": {"kind": "summary"}}`.
|
||||
The CLI resolves a relative path against the working directory; MCP clients
|
||||
pass an absolute path. REA reads and authenticates the file, then projects it
|
||||
like retained Evidence, so a large file is bounded by the file reader rather
|
||||
than charged as inline input.
|
||||
|
||||
For example, a 334 MB Obsidian application Evidence file yields a summary view
|
||||
of about 10 KB. Replace the view with
|
||||
`{"kind": "page", "collection": "modules", "offset": 0, "limit": 32}` for a
|
||||
@@ -58,8 +65,10 @@ assembly-wide inventory, so the output can still be large.
|
||||
|
||||
Choose the workflow from the artifact's contents rather than its `.exe` or
|
||||
`.dll` extension. The managed parser requires PE/CLI metadata; it does not
|
||||
unpack .NET single-file hosts or decode IL2CPP metadata. NativeAOT method bodies
|
||||
require native analysis. See the [managed-code guide](managed-code-analysis.md)
|
||||
unpack .NET single-file hosts or decode IL2CPP. For the image, type and method
|
||||
names in a Unity `global-metadata.dat`, use
|
||||
[IL2CPP metadata](il2cpp-metadata.md). NativeAOT method bodies require native
|
||||
analysis. See the [managed-code guide](managed-code-analysis.md)
|
||||
for supported deployment forms, selection semantics, and coverage limits.
|
||||
|
||||
## SQLite snapshots
|
||||
@@ -83,6 +92,34 @@ The shared CLI/MCP workflow reports compiler version, modules, replacements,
|
||||
build settings, original bytes and SHA-256. See [Go binaries](go-binaries.md)
|
||||
for supported containers and absent or malformed metadata.
|
||||
|
||||
Inspect a Godot PCK without running the game:
|
||||
|
||||
```sh
|
||||
rea inspect-artifact game.pck --detail summary --evidence_output evidence.json --json
|
||||
rea extract-artifact game.pck --json
|
||||
```
|
||||
|
||||
Formats 2, 3 and 4 retain header, directory and integrity facts. Compact delivery
|
||||
keeps complete Evidence in the requested bundle. See [Godot PCK](godot-pck.md)
|
||||
for embedded packs, batch queries and unknown encrypted coverage.
|
||||
|
||||
Inspect Unity IL2CPP metadata names without Unity or a disassembler:
|
||||
|
||||
```bash
|
||||
rea inspect-il2cpp-metadata /absolute/path/global-metadata.dat --json
|
||||
```
|
||||
|
||||
The command decodes a version 24, 27, 29, 31 or 39 `global-metadata.dat` and reports assembly
|
||||
images, type, method and member names, string literals, index ranges, header
|
||||
sections and SHA-256. With `--binary PATH` it also reads the matching
|
||||
`GameAssembly.dylib`, `GameAssembly.dll` or `libil2cpp.so` (never runs it) and
|
||||
links each method to its code address; `--ghidra-seeds PATH` then writes Ghidra
|
||||
function seeds. It does not decrypt metadata. MCP defaults to compact counts
|
||||
with retained Evidence; query indexed rows with filters, batch indices and
|
||||
selected fields. The CLI defaults to complete Evidence;
|
||||
`--detail summary --evidence_output FILE` exports the full result to a new file
|
||||
and returns a compact overview. See [IL2CPP metadata](il2cpp-metadata.md).
|
||||
|
||||
Inspect an embedded cargo-auditable Rust dependency list without installing
|
||||
Rust or configuring a disassembler:
|
||||
|
||||
@@ -338,6 +375,58 @@ SIGINT or SIGTERM and does not stop when stdin closes. See
|
||||
shutdown contract. Client registrations written by setup and the MCP registry
|
||||
entry keep stdio.
|
||||
|
||||
## Serve an environment manifest
|
||||
|
||||
`rea environment-serve <manifest.json> --evidence <capture.json> [--host H]
|
||||
[--port N]` serves an environment manifest projected by `inspect-analysis-view`
|
||||
as an agent environment: an MCP server at `/mcp` with one tool per manifest
|
||||
tool, each replaying the retained capture records it cites, the environment
|
||||
card at `/.well-known/agent-env.json`, and a `/agentenv` data plane that
|
||||
answers every method with JSON-RPC `-32601`. The manifest's
|
||||
`parent_evidence_id` must equal the evidence file's `evidence_id`; otherwise
|
||||
the command refuses with `invalid_request` before binding.
|
||||
|
||||
```sh
|
||||
rea environment-serve manifest.json --evidence capture.json --port 8765
|
||||
```
|
||||
|
||||
`--host` and `--port` resolve like `rea mcp --http`: flags, then `MCP_HOST` and
|
||||
`MCP_PORT`, then `127.0.0.1` and an ephemeral port. `ENVIRONMENT_NAME` renames
|
||||
the served environment; set but blank, it is refused as `configuration_invalid`
|
||||
like a blank `MCP_HOST`. The bound URL is written to stderr as one JSON log line
|
||||
(`"msg":"MCP transport listening"`). See
|
||||
[agent environments](agent-environments.md) for what replay returns and does
|
||||
not establish.
|
||||
|
||||
## Export an environment bundle
|
||||
|
||||
`rea environment-export <manifest.json> --evidence <capture.json> <dir>
|
||||
[--package-tarball <path>] [--name <override>]` writes the same manifest and
|
||||
capture pair as a bundle folder the agentenv framework runs with
|
||||
`agent-env run <dir>`. It reads each file once, authenticates and cross-checks
|
||||
the pair with the same checks as `environment-serve`, copies the bytes it
|
||||
authenticated, refuses to write into a directory that exists and is not empty,
|
||||
and never overwrites a file.
|
||||
|
||||
```sh
|
||||
rea environment-export manifest.json --evidence capture.json ./grafana-example --json
|
||||
```
|
||||
|
||||
The folder holds a `README.md` with the bundle's provenance, an env folder
|
||||
`envs/<name>/` with a `Dockerfile` that installs this exact `rea-agents`
|
||||
version and serves the copied `manifest.json` and `evidence.json` (written
|
||||
with mode `0600` on POSIX; Windows retains inherited ACLs) with
|
||||
`environment-serve`, a Python verifier under `artifacts/check_<name>/` and the
|
||||
smoke task `tasks/smoke.json`. `--package-tarball` copies a packed `rea-agents`
|
||||
tarball into the env folder and installs it instead of the registry package,
|
||||
which is how the hosted lane proves a checkout rather than a release. `--name`
|
||||
replaces the manifest's kebab-case name as the env folder and env id; a device
|
||||
name Windows reserves (`con`, `nul`, `com1` and the like) is refused. The
|
||||
command prints one JSON document listing the written files with their sizes
|
||||
and the pinned package source. See
|
||||
[agent environments](agent-environments.md#export-a-bundle) for the folder
|
||||
layout and what the smoke task checks.
|
||||
|
||||
## Output and exit status
|
||||
|
||||
`rea mcp doctor --json` checks production MCP startup and reports child logs in
|
||||
@@ -356,6 +445,11 @@ redirected, which keeps large results smaller for agents and scripts; pipe
|
||||
through `jq .` for indented files. Output selection and formatting do not change
|
||||
operation status.
|
||||
|
||||
`--filter-output` selects fields from successful command data. Classified failures
|
||||
retain their complete typed diagnostic projection even when the selector matches
|
||||
a field such as `code` or `message`, or names a missing field. `--full-output`
|
||||
controls whether the outer `{ok, error, meta}` envelope is included.
|
||||
|
||||
For `cutter-command`, `--json-output` requests command-specific JSON from
|
||||
Cutter when available. This is separate from `--format json`, which selects the
|
||||
CLI result representation; the global `--json` flag selects that same CLI
|
||||
@@ -376,6 +470,24 @@ output formatting.
|
||||
`130` on SIGINT or `143` on SIGTERM after closing the listener, the session and
|
||||
its providers, and `1` when that cleanup fails.
|
||||
|
||||
`rea environment-serve` is also long-running: it serves until SIGINT or
|
||||
SIGTERM, closes the listener, then prints one structured
|
||||
`{"status":"stopped", …}` document naming the URL, environment and tool count
|
||||
and exits `130` or `143` through the shared cancellation handler below.
|
||||
Refusals before binding (unreadable files, non-Evidence JSON, capture Evidence
|
||||
in a format other than HAR, a manifest whose parent is not the supplied
|
||||
evidence, an unbindable port) print a structured
|
||||
error and exit `1`, as does a listener that fails after binding.
|
||||
`rea environment-export` is one-shot: it prints the written files and exits
|
||||
`0`, or prints a structured `invalid_request` error and exits `1` for the same
|
||||
pair refusals, a non-empty or file-occupied directory, a missing package
|
||||
tarball or a name that is not a kebab-case label or is a Windows device name,
|
||||
writing nothing in each case.
|
||||
A write that fails after the first file (a permission or disk-space failure
|
||||
on a later path) propagates as the host's failure and leaves the partial
|
||||
folder in place for inspection; remove it before retrying, since the next
|
||||
attempt refuses the non-empty directory.
|
||||
|
||||
A `cancelled` result does not by itself determine the exit status; it depends on
|
||||
the command's signal handling. Commands routed through the shared
|
||||
`withCommandCancellation` handler in the CLI set status `130` for SIGINT or
|
||||
@@ -388,7 +500,8 @@ browser, web and Electron commands (including `analyze-web-bundle`,
|
||||
`capture-electron-scenario`), and the platform and provider command families
|
||||
for Android/ADB, Apktool/JEB/Flutter, firmware, PCAP and signal analysis,
|
||||
WASM/EVM, Go/Rust and binary diagnostics, Frida instrumentation, the Cutter
|
||||
command, objdump, Rizin and GDB. In these paths, a returned structured
|
||||
command, objdump, Rizin and GDB, and the agent environment server
|
||||
(`environment-serve`). In these paths, a returned structured
|
||||
`cancelled` result can be printed before the wrapper assigns the
|
||||
signal-derived status. This describes the current source routing; it is not a
|
||||
claim that every command or provider runtime has been independently exercised.
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
# Desktop installation directories
|
||||
|
||||
Use `inspect_desktop_application` when an installation directory is the target
|
||||
and its runtime is unknown. It reads directory entries, selected headers and
|
||||
bounded metadata, then reports runtime candidates, supporting files, component
|
||||
path markers and concrete follow-up operations. No native provider or active
|
||||
binary session is needed. It does not run the application, extract archives,
|
||||
follow child symlinks, contact services or change target files.
|
||||
|
||||
## MCP
|
||||
|
||||
```json
|
||||
{ "path": "/artifacts/MyApplication" }
|
||||
```
|
||||
|
||||
Default `detail: "summary"` retains complete source Evidence in the session and
|
||||
returns candidates with representative supporting paths, counts, coverage,
|
||||
limitations and grouped next probes. `normalized_result.parent_evidence_id`
|
||||
identifies the complete record. A server without Evidence retention refuses
|
||||
summary delivery before reading the target; select `detail: "complete"` to
|
||||
receive every retained fact inline.
|
||||
|
||||
Read exact files in one request:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "ev_<parent-sha256>"
|
||||
},
|
||||
"view": {
|
||||
"kind": "desktop-application",
|
||||
"collection": "files",
|
||||
"paths": ["Game_Data/Managed/Assembly-CSharp.dll", "GameAssembly.dll"],
|
||||
"fields": ["path", "read_status", "ranges", "facts", "issues"],
|
||||
"offset": 0,
|
||||
"limit": 16
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Collections are `files`, `omissions`, `candidates`, `components` and `next_probes`.
|
||||
Files and omissions accept exact `paths` and batched `file_ids` / `omission_ids`.
|
||||
Files also accept `has_issues: true` to select probe diagnostics directly, or
|
||||
`false` to select rows without reported issues. This filter uses the retained
|
||||
issue facts; it does not re-read targets or infer that uninspected files are healthy.
|
||||
When both are supplied, all filters apply; an empty selection returns no rows.
|
||||
Omission records retain the observed path, kind and stable `omission_id`; their
|
||||
payloads are not read, and other special-entry metadata remains unavailable.
|
||||
`candidates` accepts exact `families`.
|
||||
Omitted selectors match all retained rows; an empty selector matches none.
|
||||
Every collection supports `fields`, `offset` and `limit`; its stable row ID is
|
||||
always included. Selection changes delivery, not source inspection. Pages keep
|
||||
source coverage and parent Evidence links. Page exhaustion does not establish
|
||||
absence in a partial source. Complete Evidence remains accessible through
|
||||
`get_evidence` and Evidence bundle export.
|
||||
|
||||
The summary's `probe_issues` counts files with reported issues and their retained
|
||||
issue entries. It previews at most two such files, including stable `file_id`,
|
||||
read status and the first issue. Path/reason previews use at most 256 UTF-16 units
|
||||
each without splitting surrogate pairs; `text_truncated` marks shortened text,
|
||||
and `preview_truncated` marks additional files. These are display prefixes;
|
||||
full caller paths and all reasons remain in Evidence. Counts describe retained
|
||||
observations, while source coverage still identifies partial inspection.
|
||||
|
||||
Use `inspect_analysis_view` with `collection: "files", has_issues: true` and
|
||||
`fields: ["path", "read_status", "issues"]` for exact reasons, with the usual
|
||||
paging and parent links. It intersects with exact paths and batched file IDs.
|
||||
|
||||
An ASAR inventory probe first selects its exact path with `open_binary`; then
|
||||
call `inspect_artifact` on that active target. Directory inspection itself does
|
||||
not select or replace the session target. The JavaScript application workflow
|
||||
accepts its own selected path directly.
|
||||
|
||||
## CLI
|
||||
|
||||
The one-shot CLI returns complete Evidence by default. For compact output,
|
||||
explicitly retain it in a bundle:
|
||||
|
||||
```bash
|
||||
rea inspect-desktop-application /artifacts/MyApplication \
|
||||
--detail summary --evidence_output /reports/application.evidence.json --json
|
||||
```
|
||||
|
||||
This returns `summary` Evidence and an `evidence_bundle` publication receipt.
|
||||
Existing output files are never overwritten. Summary delivery requires
|
||||
`--evidence_output`, checked before analysis. `inspect-evidence-bundle` selects
|
||||
saved records; an exact saved record can also feed `inspect-analysis-view`.
|
||||
`--detail complete` returns source Evidence inline and can optionally write the
|
||||
same bundle. Export writes only the caller-selected output file.
|
||||
|
||||
## Facts and uncertainty
|
||||
|
||||
| Family | Evidence used |
|
||||
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| .NET | A PE optional header declares a nonempty CLI directory. |
|
||||
| Unity Mono | A managed assembly below `*_Data/Managed` or `Contents/Resources/Data/Managed` has an exact UnityEngine assembly reference. |
|
||||
| Unity IL2CPP | An IL2CPP metadata header coexists with a conventionally named native `GameAssembly.dll`, `GameAssembly.so`, `GameAssembly.dylib` or `libil2cpp.so` image. Build correspondence remains unknown. |
|
||||
| Godot | A `GDPC` header at file start or a validated executable trailer offset. |
|
||||
| GameMaker | A bounded `FORM` container starts with a `GEN8` chunk. |
|
||||
| Unreal | `Content/Paks` path conventions or an IoStore TOC signature; arbitrary `.pak` filenames are insufficient. |
|
||||
| Electron | A `resources/app.asar` or `resources/default_app.asar` path convention; archive validity is unverified. |
|
||||
|
||||
An observed metadata header with supported raw version 29, 31 or 39 offers an
|
||||
`inspect_il2cpp_metadata` follow-up at its admitted file path, including when no
|
||||
native library was found. This probe does not establish full metadata validity
|
||||
or correspondence to a native binary. See [IL2CPP metadata](il2cpp-metadata.md).
|
||||
|
||||
Candidates are derived or inferred from these facts and do not prove loading or
|
||||
execution. BepInEx, MelonLoader, Easy Anti-Cheat and BattlEye labels identify
|
||||
path conventions only; runtime presence stays unknown. No bypass is provided.
|
||||
|
||||
`file_id` includes the admitted root namespace, recorded path and available filesystem identity. The caller-selected root path and admitted canonical path/root identity are both retained; `local_path` identifies each observed file for follow-up operations. The selected root may be a symlink; child symlinks are not followed. Exact
|
||||
metadata integers remain decimal strings. Read ranges have offsets, byte counts
|
||||
and SHA-256 digests; typed facts expose CLI RVAs, metadata/pack versions and
|
||||
assembly tokens. Directory and symlink identities unavailable from traversal
|
||||
remain null.
|
||||
|
||||
The directory's `snapshot_sha256` commits to retained paths, omission kinds,
|
||||
available metadata and range digests, composed through the admitted root,
|
||||
entry and omission identities. Caller ceilings, coverage counters, diagnostic
|
||||
wording and interpretation facts remain in Evidence, outside source identity.
|
||||
The same observations under different generous limits share a snapshot digest;
|
||||
Evidence IDs still preserve request parameters and reported coverage. Different
|
||||
retained entries or read ranges change the source snapshot.
|
||||
Omission records share the entry and representation budgets and can be fetched
|
||||
independently through their view.
|
||||
**It is not a digest of all file contents.** These are sequential stable entry
|
||||
observations, not an atomic directory snapshot. Unread bytes remain unknown.
|
||||
|
||||
## Resource bounds and reuse
|
||||
|
||||
Defaults are 20,000 examined entries and 8 MiB of total probe reads. Callers may
|
||||
select up to 100,000 entries and 64 MiB. A separate 32 MiB conservative
|
||||
representation budget bounds retained paths and facts before growth. Limits
|
||||
and failed probes produce partial coverage. Traversal and reads are serial.
|
||||
Each reader admits one inspection at a time; overlapping requests receive a
|
||||
retryable capacity refusal without cancelling the admitted caller.
|
||||
|
||||
Unity reference decoding stops after a witness in each `Data/Managed` directory;
|
||||
other assemblies' references remain unexamined, while their headers are still
|
||||
checked. AssemblyRef facts decode names and tokens only; they do not decode or
|
||||
hash unrelated key/culture/hash payloads or establish runtime binding. Pack contents, encrypted data, metadata/address correspondence and
|
||||
section-embedded PCKs without a trailer are separate capabilities.
|
||||
|
||||
Filesystem results are not cached across calls. Retained views reuse validated
|
||||
immutable metadata without rescanning files or retaining another inventory.
|
||||
+587
-17
@@ -28,6 +28,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -64,6 +91,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -100,6 +154,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -136,6 +217,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -172,6 +280,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -208,6 +343,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -244,6 +406,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -280,6 +469,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -316,6 +532,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -352,6 +595,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -388,25 +658,82 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"logical_path": { "type": "string" },
|
||||
"declared_sha256": {
|
||||
"anyOf": [{ "type": "string" }, { "type": "null" }]
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"logical_path": { "type": "string" },
|
||||
"declared_sha256": {
|
||||
"anyOf": [{ "type": "string" }, { "type": "null" }]
|
||||
},
|
||||
"calculated_sha256": {
|
||||
"anyOf": [{ "type": "string" }, { "type": "null" }]
|
||||
},
|
||||
"unpacked": { "type": "boolean" }
|
||||
},
|
||||
"required": [
|
||||
"logical_path",
|
||||
"declared_sha256",
|
||||
"calculated_sha256",
|
||||
"unpacked"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"calculated_sha256": {
|
||||
"anyOf": [{ "type": "string" }, { "type": "null" }]
|
||||
},
|
||||
"unpacked": { "type": "boolean" }
|
||||
},
|
||||
"required": [
|
||||
"logical_path",
|
||||
"declared_sha256",
|
||||
"calculated_sha256",
|
||||
"unpacked"
|
||||
],
|
||||
"additionalProperties": false
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"logical_path": { "type": "string" },
|
||||
"algorithm": { "type": "string", "const": "md5" },
|
||||
"declared_md5": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{32}$"
|
||||
},
|
||||
"calculated_md5": {
|
||||
"anyOf": [
|
||||
{ "type": "string", "pattern": "^[a-f0-9]{32}$" },
|
||||
{ "type": "null" }
|
||||
]
|
||||
},
|
||||
"unpacked": { "type": "boolean" }
|
||||
},
|
||||
"required": [
|
||||
"logical_path",
|
||||
"algorithm",
|
||||
"declared_md5",
|
||||
"calculated_md5",
|
||||
"unpacked"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -446,6 +773,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -482,6 +836,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -518,6 +899,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -554,6 +962,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -590,6 +1025,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -626,6 +1088,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -662,6 +1151,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -698,6 +1214,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
@@ -734,6 +1277,33 @@
|
||||
"required": ["action"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"recovery": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_input_contract" },
|
||||
"operation": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"required": ["kind", "operation"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": { "type": "string", "const": "inspect_timeout_state" },
|
||||
"operation": { "type": "string", "minLength": 1 },
|
||||
"timeout_ms": {
|
||||
"type": "integer",
|
||||
"exclusiveMinimum": 0,
|
||||
"maximum": 9007199254740991
|
||||
}
|
||||
},
|
||||
"required": ["kind", "operation", "timeout_ms"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"propertyNames": { "type": "string" },
|
||||
|
||||
@@ -158,7 +158,17 @@ tree semantics, vulnerability scanning, emulation or hardware runtime capture.
|
||||
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,
|
||||
- **120-second** analysis deadline through inventory. Publication and final
|
||||
cleanup start a separate **120-second** budget; select
|
||||
`publication_timeout_ms` in MCP or `--publication-timeout-ms` in CLI for a slow
|
||||
destination. Valid budgets are integers from 1 through 2,147,483,647 ms (the
|
||||
timer representation limit). Publication timeouts identify their phase and
|
||||
verified file progress; verified writes before atomic commit are not a claim
|
||||
that output remains published after rollback. Caller cancellation applies to
|
||||
both phases and final cleanup. Phase handoff, publication commit and final
|
||||
completion also check monotonic elapsed time, so delayed timer callbacks
|
||||
cannot revive an expired budget.
|
||||
- Inherited **1 GiB per-process address-space** limit,
|
||||
120 CPU seconds and zero core-dump allowance through `prlimit`.
|
||||
- JSON reports: at most **8 MiB** and valid UTF-8; malformed encoding and
|
||||
oversized reports fail without repairing or truncating producer data.
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
# Godot PCK archives
|
||||
|
||||
`inspect-artifact` and `extract-artifact` read ordinary Godot PCK formats 2, 3
|
||||
and 4 without running the game or requiring Godot. Official Godot 4.4.1, 4.6.1
|
||||
and 4.7.1 produced these respective formats in verification; other engine
|
||||
releases have not been verified. Format 1 is unsupported.
|
||||
|
||||
Inspection retains the original header and directory facts, including engine
|
||||
version, flags, declared file count, source paths, offsets and declared MD5.
|
||||
Observed plaintext bytes receive content-addressed SHA-256 identities. MD5 is a
|
||||
container integrity claim, separate from the observed SHA-256. A mismatch fails
|
||||
by default; `--integrity-policy record-and-continue` retains the observed bytes
|
||||
and an explicit untrusted contradiction. An all-zero MD5 is an unknown claim,
|
||||
not a verified digest.
|
||||
|
||||
## CLI
|
||||
|
||||
Start with a compact summary and keep the complete Evidence bundle:
|
||||
|
||||
```sh
|
||||
rea inspect-artifact /artifacts/game.pck --detail summary \
|
||||
--evidence_output /evidence/game.json --json
|
||||
rea extract-artifact /artifacts/game.pck --json
|
||||
```
|
||||
|
||||
Summary delivery requires `--evidence_output` and never overwrites that file.
|
||||
The response contains `summary` Evidence and an `evidence_bundle` receipt.
|
||||
Without `--detail summary`, inspection keeps its complete inline delivery.
|
||||
Extraction creates a new temporary directory and returns its path. It rejects
|
||||
unsafe paths and verifies container identity before creating output.
|
||||
|
||||
The same CLI commands locate a PCK at the beginning of a file or through its
|
||||
12-byte EOF trailer in an executable carrier. They do not scan arbitrary
|
||||
payloads or discover packs behind additional trailing signatures. The root
|
||||
content SHA-256 covers the whole carrier; its content classification remains
|
||||
native, while the inventory records the PCK role and exact pack bounds.
|
||||
Ordinary native `open_binary` detection remains unchanged.
|
||||
|
||||
## MCP and selected facts
|
||||
|
||||
For a standalone pack, call `open_binary` with its absolute path, then
|
||||
`inspect_artifact` with `{"detail":"summary"}`. The complete inspection and its
|
||||
nested inventory stay in the session. Use the summary's
|
||||
`normalized_result.parent_evidence_id` as the source of subsequent views:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_analysis_view",
|
||||
"arguments": {
|
||||
"source": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "<parent_evidence_id>"
|
||||
},
|
||||
"view": {
|
||||
"kind": "inventory",
|
||||
"collection": "occurrences",
|
||||
"logical_paths": ["scripts/main.gd", "project.binary"],
|
||||
"fields": [
|
||||
"logical_path",
|
||||
"declared_integrity",
|
||||
"hash_status",
|
||||
"format_metadata"
|
||||
],
|
||||
"offset": 0,
|
||||
"limit": 32
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Occurrences retain `occurrence_id`, `artifact_id` and `parent_occurrence_id`
|
||||
even when `fields` is empty. Batch occurrence IDs with `occurrence_ids`, or
|
||||
join returned content IDs to `collection: "nodes"`, `artifact_ids: [...]` and
|
||||
`fields: ["sha256", "size"]`. Filters intersect; an omitted filter selects all
|
||||
available rows and an empty array selects none. Pagination follows the filtered
|
||||
canonical order. Multiple occurrences may share one content ID.
|
||||
|
||||
Views reuse authenticated immutable inventory facts and lazy lookup indexes;
|
||||
they do not reopen the source. These are historical observations, including
|
||||
after the source changes or disappears. Close the target with `close_binary`
|
||||
when finished. To read an exported bundle through the CLI, select its complete
|
||||
inspection record as `source: {kind: "inline", evidence: ...}` in a JSON request
|
||||
file for `rea inspect-analysis-view request.json --json`.
|
||||
|
||||
## Coverage and limits
|
||||
|
||||
PCK summaries expose root format metadata, available graph counts,
|
||||
`available_hash_status_counts`, and `source_coverage`. Inventory pages preserve
|
||||
producer coverage alongside page coverage. `source_complete` means all declared
|
||||
retained graph rows are available; it does not establish that an encrypted or
|
||||
otherwise unknown directory was examined. Missing matches in unknown coverage
|
||||
are not evidence of absence.
|
||||
|
||||
Encrypted directories retain their header and declared file count with unknown
|
||||
entry coverage. Individually encrypted members retain directory metadata with
|
||||
unknown content identity; REA does not decrypt them. Sparse bundles likewise
|
||||
retain header facts with unknown member coverage. Extraction refuses an
|
||||
unreadable directory or encrypted member. This capability does not recover
|
||||
keys, decode Godot resources, decompile GDScript bytecode, analyze signals,
|
||||
apply patch/removal entries or execute targets.
|
||||
|
||||
Readable logical member bytes, including repeated physical ranges, share the ZIP
|
||||
archive expansion policy: at most the larger of 256 MiB or 500 times the PCK's
|
||||
encoded byte span. Embedded carrier bytes do not enlarge that budget. The full
|
||||
directory is checked before any member is yielded, hashed or extracted. Encrypted
|
||||
members are not read and do not consume this plaintext-work allowance. Small
|
||||
bounded aliases remain valid. This is a processing/output resource bound, separate
|
||||
from the directory metadata retention budget and format validity.
|
||||
@@ -0,0 +1,419 @@
|
||||
# IL2CPP metadata
|
||||
|
||||
`inspect_il2cpp_metadata` / `inspect-il2cpp-metadata` decodes one selected local
|
||||
Unity IL2CPP `global-metadata.dat` file and returns its assembly images, type
|
||||
definitions, methods and named members with their owners, plus managed string
|
||||
literals, by stable index, namespace, range and count,
|
||||
with the header sections and serialized index widths. It reads the file without
|
||||
running it and requires no Unity installation, IL2CPP binary, active target,
|
||||
Hopper, Ghidra or IDA.
|
||||
|
||||
Metadata versions 24, 27, 29, 31 and 39 are supported. For example, Unity 2019.4.41f2
|
||||
wrote version 24, 2020.3.49f1 version 27, 2022.3.30f1
|
||||
wrote version 29, 2022.3.62f3 wrote 31 and 6000.4.2f1 wrote 39; other Unity
|
||||
releases that write these numbers have not been checked. Other versions are
|
||||
rejected as unsupported. Version 24 decoding covers the 264-byte header layout
|
||||
used by the cited Unity 2019.4 build; earlier 24.x layouts are not inferred from
|
||||
the version number and are refused when their header or records differ. The path
|
||||
must name a regular file; directories and symbolic links are rejected.
|
||||
|
||||
With `binary_path`, the tool also reads the matching native binary and links
|
||||
each method to its code address. See [Native method addresses](#native-method-addresses).
|
||||
|
||||
## MCP
|
||||
|
||||
MCP defaults to a compact summary and retains the complete decoded Evidence in
|
||||
the session. MCP requires an absolute filesystem path:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_il2cpp_metadata",
|
||||
"arguments": {
|
||||
"path": "/artifacts/Data/Managed/Metadata/global-metadata.dat"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The summary retains the complete decoded Evidence in the session and returns a
|
||||
linked `inspect_analysis_view` overview: artifact identity, metadata version,
|
||||
index widths and `counts` (`sections`, `images`, `types`, `methods`,
|
||||
`nested_types`, `fields`, `properties`, `events`, `parameters` and
|
||||
`string_literals`) without rows. Use `normalized_result.parent_evidence_id` to page
|
||||
the original rows without reopening or decoding the file:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "<parent_evidence_id>"
|
||||
},
|
||||
"view": {
|
||||
"kind": "il2cpp-metadata",
|
||||
"collection": "types",
|
||||
"offset": 0,
|
||||
"limit": 64
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Collections are `sections`, `images`, `types`, `methods`, `fields`,
|
||||
`properties`, `events`, `parameters`, `string_literals`, `method_addresses`,
|
||||
`return_types`, `field_types`, `parameter_types` and `code_gen_modules`. Every returned row includes
|
||||
its source `index`, scoped by artifact SHA-256 and collection. All collections
|
||||
accept batch `indices` and `fields`; collections whose rows have a `name` also
|
||||
accept exact `names`. Types also accept `image_indices`, `namespaces` and
|
||||
`declaring_types`; methods, fields, properties and events accept
|
||||
`declaring_types`. Parameters accept exact `method_indices`, so a batch of
|
||||
method indices retrieves their parameters without reading the entire parameter
|
||||
table. `parameter_types` accepts the same `method_indices`, resolved through the
|
||||
retained `parameters` owners; Evidence without `parameters` refuses that filter. Filters intersect, duplicate indices are returned once, and
|
||||
rows retain metadata order. An empty filter selects no rows; `fields: []`
|
||||
returns indices only. Omitted fields return the complete selected rows.
|
||||
|
||||
For example, after resolving a type index, retrieve selected method facts:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "<parent_evidence_id>"
|
||||
},
|
||||
"view": {
|
||||
"kind": "il2cpp-metadata",
|
||||
"collection": "methods",
|
||||
"declaring_types": [1555],
|
||||
"names": ["SendMessage"],
|
||||
"fields": ["name", "declaring_type"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After finding method indices, parameters can be requested directly:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "<parent_evidence_id>"
|
||||
},
|
||||
"view": {
|
||||
"kind": "il2cpp-metadata",
|
||||
"collection": "parameters",
|
||||
"method_indices": [9785],
|
||||
"fields": ["name", "method"],
|
||||
"limit": 64
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Historical Evidence that predates these member collections remains queryable.
|
||||
A missing collection has an unknown (`null`) summary count, and requesting it
|
||||
reports that it was not collected; it is never presented as an empty inventory. The same holds for Evidence that predates binary linking: its summary
|
||||
omits `binary`, and `method_addresses`, `method_address_states`,
|
||||
`return_types`, `field_types` and `parameter_types` counts are `null`.
|
||||
Existing empty collections retain a zero count. Full Evidence is preserved.
|
||||
|
||||
`offset` counts matching rows, not source indices. `limit` defaults to 64;
|
||||
`coverage.next_offset` continues that filtered page. Coverage describes retained
|
||||
metadata facts only; runtime behavior and correspondence to native code remain
|
||||
unknown. Each view preserves the parent Evidence link, artifact identity and
|
||||
limitations. Queries work after the source file is removed. Select
|
||||
`detail: "complete"` to return every row inline within the separate MCP response
|
||||
budget.
|
||||
|
||||
## CLI
|
||||
|
||||
```bash
|
||||
rea inspect-il2cpp-metadata /absolute/path/global-metadata.dat --json
|
||||
```
|
||||
|
||||
The CLI defaults to complete Evidence. For compact delivery, explicitly preserve
|
||||
the full Evidence in a new file:
|
||||
|
||||
```bash
|
||||
rea inspect-il2cpp-metadata /absolute/path/global-metadata.dat \
|
||||
--detail summary --evidence_output /tmp/metadata-evidence.json --json
|
||||
jq -c '{source: {kind: "inline", evidence: .records[0]}, view: {kind: "il2cpp-metadata", collection: "methods", indices: [9785], fields: ["name", "declaring_type"]}}' \
|
||||
/tmp/metadata-evidence.json > /tmp/metadata-query.json
|
||||
rea inspect-analysis-view /tmp/metadata-query.json --json
|
||||
```
|
||||
|
||||
Use the exported Evidence with the CLI's inline Evidence source format described
|
||||
in [CLI and Evidence](cli.md#run-a-command). Compact delivery requires
|
||||
`--evidence_output`; existing files are never overwritten. The full export
|
||||
preserves every decoded row for subsequent queries without the original file.
|
||||
|
||||
`--binary PATH` links method addresses and `--architecture arm64|amd64` selects
|
||||
the slice of a universal Mach-O.
|
||||
|
||||
## Observations
|
||||
|
||||
The result contains:
|
||||
|
||||
- `artifact`: path, SHA-256 and byte length;
|
||||
- `magic`: unsigned little-endian header signature (`0xFAB11BAF`, delivered as a JSON number);
|
||||
- `metadata_version`: `24`, `27`, `29`, `31` or `39`;
|
||||
- `index_widths`: the serialized widths (1, 2 or 4 bytes) of
|
||||
`type_definition`, `generic_container`, `parameter` and `type` indices, and
|
||||
`type_width_source`. Version 39 derives the type index width from the
|
||||
`interface-offsets-record` or the `type-definition-record`; versions 24, 27, 29
|
||||
and 31 always use 4-byte indices (`fixed-width-version`);
|
||||
- `sections`: all version-specific header sections in header order, each with `offset`, `size`
|
||||
and `count`. Versions 24 to 31 record no counts, so `count` is `null` and
|
||||
record counts come from the section sizes;
|
||||
- `images`: `name`, `type_start` and `type_count`;
|
||||
- `types`: `name`, `namespace`, `method_start`, `method_count`,
|
||||
`nested_type_start`, `nested_type_count` and `declaring_type`;
|
||||
- `methods`: `name` and `declaring_type`;
|
||||
- `fields`, `properties` and `events`: `name` and `declaring_type`, the type
|
||||
whose member range holds the row (`-1` when none does);
|
||||
- `parameters`: `name` and `method`, the method whose parameter range holds it;
|
||||
- `string_literals`: managed string literals as `value`, or `value_base64` with
|
||||
the exact bytes when they are not valid UTF-8;
|
||||
- `limitations`.
|
||||
|
||||
A row's position in its collection is its metadata index, so `type_start`,
|
||||
`method_start` and `declaring_type` refer to positions in `types` or `methods`.
|
||||
`nested_type_start` instead indexes the metadata's `nestedTypes` table, which is
|
||||
not returned; each nested type's `declaring_type` carries that relationship.
|
||||
A type's `declaring_type` is derived from the `nestedTypes` section and is `-1`
|
||||
for top-level types. The start of an empty range can retain the original value
|
||||
`-1`.
|
||||
|
||||
The complete inventory of a large game is several megabytes and can exceed
|
||||
the MCP response budget. Summary delivery and filtered, projected pages keep
|
||||
unneeded rows out of agent context without discarding the source Evidence.
|
||||
|
||||
## Native method addresses
|
||||
|
||||
`binary_path` names the native binary built with the metadata: macOS
|
||||
`GameAssembly.dylib` (Mach-O), Windows `GameAssembly.dll` (64-bit PE) or
|
||||
Android `libil2cpp.so` (64-bit ELF, AArch64 or x86-64). A
|
||||
universal Mach-O needs `binary_architecture` (`arm64` or `amd64`); a thin binary
|
||||
rejects it. The file is read, never loaded or executed.
|
||||
|
||||
A position-independent `libil2cpp.so` stores zero in most pointer slots and
|
||||
keeps the value in `R_AARCH64_RELATIVE` or `R_X86_64_RELATIVE` relocations
|
||||
(`DT_RELA`). The reader writes those values into a private copy before
|
||||
searching it, so addresses are the link-time addresses and RVAs are relative to
|
||||
the first loaded segment. Android packed
|
||||
relocations (`DT_ANDROID_RELA`) are refused. No target code is executed.
|
||||
|
||||
The binary's `Il2CppCodeRegistration` points to one `Il2CppCodeGenModule` per
|
||||
assembly image. Each module record starts with its module name, its method
|
||||
pointer count and its method pointer array. The reader finds, for every metadata
|
||||
image, the single record whose name pointer reaches that image's name string and
|
||||
whose count equals the image's method count. It then requires exactly one
|
||||
`codeGenModulesCount`/`codeGenModules` pair that lists exactly those records. No
|
||||
symbols are needed, so stripped builds work. A method's token row id selects its
|
||||
slot in the module's method pointer array.
|
||||
|
||||
Module records establish names through their declared NUL-terminated string pointers. NUL/0xFF padding, spaces, Unicode and suffix-shared storage are supported. A pointer to a longer string does not match just its suffix; a pointer directly into a shared suffix may name it. Exact reversed UTF-8 matching avoids rescanning the file for every name and retains duplicate-name owners until module records resolve them. A contributor investigated a Linux
|
||||
`GameAssembly.so` from Unity 2023.2.13f1 whose static registration was not located. When no static pair is resolved on x86-64, the reader searches executable ranges for bytes consistent with RIP-relative LEA/MOV references to module-array candidates. Exactly one referenced array candidate must remain. `code_gen_modules_field` is then `null`, and Evidence is **inferred**: instruction boundaries, the absence of a static registration elsewhere, and runtime use were not verified. The module records and slots themselves remain file observations. Static-table associations are **derived**. Otherwise the result is `code-registration-not-found`.
|
||||
|
||||
The binary/metadata pair is caller-selected. Name/count/token agreement is structural evidence, not a cryptographic same-build proof. File-offset locations refer to the primary metadata artifact; linked virtual addresses refer to `binary.artifact`. Both SHA-256 identities remain available in compact native pages as `artifact` and `binary_source`.
|
||||
|
||||
Contributor-reported verification (not all rerun by the follow-up): macOS arm64 and x86_64 slices from Unity 2021.3.45f2, 2022.3.30f1, 2022.3.62f3 and
|
||||
6000.0.84f1 against dSYM symbols; Android arm64 and x86-64 from 2022.3.62f3
|
||||
against `libil2cpp.sym.so`; Windows x64 from 2023.2.13f1 (metadata version 29,
|
||||
the MIT-licensed game Between Two Worlds 1.0.5, `GameAssembly.dll` SHA-256
|
||||
`a3ae7f6ecd1f671297b7d98d12713ef024062d93cd7a4205298102656c93b67a`) against
|
||||
Il2CppDumper, where all 43,198 linked methods share an address with a
|
||||
same-named Il2CppDumper method and identical code folding groups 10,448 methods
|
||||
into 1,735 addresses. A macOS x86_64 build from Unity 2020.3.49f1 (metadata version 27, which Il2CppDumper reads as 27.1) links 10,555 methods, all at addresses where Il2CppDumper names the same method; its other addresses are generic instances. A build from Unity 2021.1.28f1 (also read as 27.1) gives the same result: 10,345 methods linked, every address named the same by Il2CppDumper, and its other 3,486 addresses generic instances.
|
||||
|
||||
These checks compare addresses with symbols or with another static tool. They
|
||||
do not establish that a linked address is the code the game runs (a runtime may
|
||||
patch, hook or replace it), that builds from other Unity versions, compilers or
|
||||
obfuscators link the same way, or that a method without an address has no code:
|
||||
generic instances, adjustor thunks and invokers are not linked, and an
|
||||
`unknown` row was not looked up at all.
|
||||
|
||||
The result adds:
|
||||
|
||||
- `binary`: artifact identity, `format`, `architecture`, `image_base`,
|
||||
`code_gen_modules_field` (the address of `codeGenModulesCount`, or `null` as
|
||||
described above), `code_gen_modules_array` (the module pointer array) and
|
||||
`code_gen_modules` (`image`, record `address`, `method_pointer_count`);
|
||||
- `method_addresses`: one row per method index with `state`, `address` and
|
||||
`rva` (address minus `image_base`), both hexadecimal strings. `state` is
|
||||
`code` when the module slot holds an unambiguously mapped executable address, `no-body` when an observed slot is zero,
|
||||
as for abstract, extern and most generic methods, and `unknown` when no type
|
||||
range in any image holds the method or a symbol relocation leaves its slot unresolved.
|
||||
`address` and `rva` are `null` unless `state` is `code`. The summary counts
|
||||
each state in `counts.method_address_states`. Ghidra seeds are written only
|
||||
for `code` rows.
|
||||
|
||||
- `return_types`, `field_types` and `parameter_types`: one row per method,
|
||||
field and parameter index with `type`, the index into the binary's
|
||||
`Il2CppType` table, and `name`, rendered from that table: C# keywords for
|
||||
primitives (`int`, `string`, `object`), `Namespace.Outer+Inner` for type
|
||||
definitions, `T[]`, `T[,]` and `T*`, generic instances as
|
||||
`System.Collections.Generic.List<string>` and generic parameters by their
|
||||
metadata name (`TKey`). A trailing `&` marks a by-reference type. `name` is
|
||||
`null` only for type index `-1`.
|
||||
|
||||
`binary` also records `types_field` and `types_count`: the address of
|
||||
`Il2CppMetadataRegistration.typesCount` and its value. That record is found as
|
||||
the single place whose `fieldOffsetsCount` and `typeDefinitionsSizesCount` both
|
||||
equal the metadata type-definition count and whose type table is file mapped.
|
||||
|
||||
Without `binary_path`, `binary` is `null` and the four binary collections are
|
||||
empty in the complete producer report to indicate unrequested observations. Their summary counts, including state counts, are `null`, and native page requests report unavailable observations. This does not establish an empty native inventory. Missing native fields in historical Evidence remain unknown too.
|
||||
|
||||
For metadata version 27, type names are `null` and only type indices are
|
||||
reported: versions 27.0 to 27.2 share that header number but place the
|
||||
Il2CppType byref flag in different bits, so a rendered name could gain or lose
|
||||
`&` without warning.
|
||||
|
||||
The builds checked are listed after the module search rules above.
|
||||
|
||||
| Reason | Category | Meaning |
|
||||
| --------------------------------- | ----------- | ------------------------------------------------------------------------------------------------- |
|
||||
| `unsupported-container` | unsupported | Not a 64-bit little-endian Mach-O, PE or ELF image (ELF: AArch64 or x86-64 with dynamic section). |
|
||||
| `slice-selection-required` | unsupported | A universal Mach-O was given without `binary_architecture`. |
|
||||
| `slice-not-found` | unsupported | The requested slice is absent, or a thin binary was given an architecture. |
|
||||
| `unsupported-pointer-encoding` | unsupported | Mach-O chained fixups or Android packed (APS2) ELF relocations; pointers not decoded. |
|
||||
| `malformed-container` | input error | The container headers are damaged. |
|
||||
| `code-gen-modules-not-found` | input error | An image has no module record; often a binary from a different build. |
|
||||
| `ambiguous-code-gen-module` | input error | More than one module record matches an image. |
|
||||
| `code-registration-not-found` | input error | No unique module table or executable-byte array candidate was resolved. |
|
||||
| `invalid-method-token` | input error | A method token is not a method row inside its module's pointer count. |
|
||||
| `invalid-method-pointer` | input error | A slot is unavailable or its nonzero address lacks unambiguous executable file coverage. |
|
||||
| `metadata-registration-not-found` | input error | No single MetadataRegistration matches the type-definition count. |
|
||||
| `invalid-type` | input error | A type index or `Il2CppType` record is out of range, unmapped or malformed. |
|
||||
| `unsupported-type-kind` | unsupported | A valid type kind has no implemented rendering. |
|
||||
|
||||
Format and selection failures name `binary_path`; capacity refusals remain typed resource errors. Failed linking preserves the metadata source identity, validated counts, selected binary path/architecture and its SHA-256 when observed, plus prior producer observations. It never publishes an incomplete native table as complete. The binary input is bounded to1GiB.
|
||||
|
||||
Scans yield cooperatively in bounded batches. Name-address retention and reverse-trie construction are each limited to65,536 entries/nodes and fallback module-array candidates to64 (`native-index-capacity`); exceeding capacity does not prove absence. Rendered type text has a64MiB UTF-8 cache-workspace guard, separate from the64MiB encoded-report admission, and64 nested rendering frames (`native-type-text-capacity` / `type-nesting-capacity`). Genuine cycles are malformed; unsupported type kinds are explicit coverage failures. Native row/text admission runs before allocation or retention. Unused generic parameter names are read on demand from the same stable metadata snapshot. There is no cross-inspection file cache; validated immutable Evidence is reused for session views.
|
||||
|
||||
ELF RELATIVE writes into zero-fill are kept as a private virtual-pointer overlay. Unresolved supported-width symbol relocations, including RELA PLT entries, remain unknown; stored zeros never establish no-body. COPY, TLS descriptor, REL, packed APS2 and other unsupported relocation encodings fail closed. Input bytes are unchanged.
|
||||
|
||||
Native address queries batch exact64-bit hexadecimal `addresses` and `states` (`code`, `no-body`, `unknown`), intersecting indices and pagination. Numerically equal address spellings match; original row indices and every shared-body alternative survive:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "<parent_evidence_id>"
|
||||
},
|
||||
"view": {
|
||||
"kind": "il2cpp-metadata",
|
||||
"collection": "method_addresses",
|
||||
"addresses": ["0x180c56ed0"],
|
||||
"states": ["code"],
|
||||
"fields": ["address", "rva"],
|
||||
"offset": 0,
|
||||
"limit": 64
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Ghidra function names
|
||||
|
||||
The CLI can turn a linked report into a
|
||||
[`REA_GHIDRA_SEED_FILE`](installation.md) so Ghidra names every IL2CPP method
|
||||
before auto-analysis:
|
||||
|
||||
```bash
|
||||
rea inspect-il2cpp-metadata global-metadata.dat --binary libil2cpp.so \
|
||||
--ghidra-seeds il2cpp-seeds.tsv --json > /dev/null
|
||||
export REA_GHIDRA_SEED_FILE="$PWD/il2cpp-seeds.tsv"
|
||||
```
|
||||
|
||||
Each linked method becomes one `function` seed named `Namespace.Type$$Method`
|
||||
(nested types joined with `.`, whitespace replaced by `_`), the Il2CppDumper
|
||||
convention. Seeds are at `seed-base + rva`. The default base is where Ghidra's
|
||||
loaders place the binary: `0x100000` for an ELF linked at 0, and the link base
|
||||
for Mach-O and PE; `--seed-base 0x...` overrides it for a different import. When
|
||||
identical code folding gives several methods one address, the first method names
|
||||
the function and a `# folded <address> <kept> also <names>` comment line after
|
||||
the seeds lists the others, so the kept name is visibly one of several. Ghidra still names a seeded function that is a thunk after its
|
||||
target. The file is atomically published with exclusive creation; an existing destination is never replaced. Failed staging retains its cleanup owner for retry. Source digest and link/seed-base comments make the mapping inspectable. Unknown slots are skipped separately from observed no-body slots. `--seed-base` requires `--ghidra-seeds`; overflowing rebased addresses and names/outputs beyond the existing Ghidra protocol limits (2,000 name characters,32MiB file,1,000,000 entries) are refused before publication. The CLI and reusable service use one formatter and owned output workflow; ordinary MCP inspection remains read-only.
|
||||
|
||||
## Failures
|
||||
|
||||
A file whose magic is not `0xFAB11BAF` fails as an unsupported target with
|
||||
`unrecognized-magic`. The file may be encrypted or may not be IL2CPP metadata.
|
||||
A version other than 24, 27, 29, 31 or 39 fails as an unsupported target with
|
||||
`unsupported-version`.
|
||||
|
||||
Damaged metadata returns an input error (`invalid_format`) whose
|
||||
message names one reason:
|
||||
|
||||
| Reason | Meaning |
|
||||
| ------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| `truncated-header` | The file is shorter than the header. |
|
||||
| `unexpected-header-size` | `stringLiterals.offset` is not the header size: 264 bytes for version 24, 256 for 27, 29 and 31, 380 for 39. |
|
||||
| `section-out-of-bounds` | A section extends beyond the file, or a non-empty section starts inside the header. |
|
||||
| `section-overlap` | Two sections overlap. Zero-size sections are exempt and gaps are allowed. |
|
||||
| `record-size-mismatch` | A section size does not divide into its records, or the record size differs from the one the index widths imply. |
|
||||
| `type-index-width` | The record used to derive the type index width implies no 1, 2 or 4 byte width. |
|
||||
| `index-out-of-range` | A serialized index or range points outside its target collection. |
|
||||
| `nested-type-conflict` | A type is listed as nested in two types, in itself, or in a cycle of declaring types. |
|
||||
| `declaring-type-mismatch` | A method inside a type's method range names a different type, or a method lies outside its type's range. |
|
||||
| `image-range-overlap` | Two images claim the same type definitions. |
|
||||
| `invalid-string` | A name index does not start a string, or the name is unterminated or not valid UTF-8. |
|
||||
|
||||
A version field that disagrees with the file's layout, for example a version
|
||||
39 file relabeled as 29, usually surfaces as `unexpected-header-size` or
|
||||
`record-size-mismatch`.
|
||||
|
||||
Two checks are stricter than the IL2CPP runtime: a name index must start a
|
||||
string, and an empty range's start must lie between -1 and the collection size.
|
||||
Every Unity-written file checked so far satisfies both; metadata rewritten by
|
||||
other tools may be rejected.
|
||||
|
||||
An incomplete inventory is never reported as complete. Failures after a verified
|
||||
snapshot retain partial source Evidence with its SHA-256, byte length and any
|
||||
validated section row counts; these counts do not establish that names and
|
||||
relationships were decoded. A file that changes during reading and a cancelled
|
||||
request keep distinct errors. Decode checkpoints yield for cancellation, and
|
||||
close waits for active work. Each reader admits one inspection at a time.
|
||||
Resource guards bound the selected file to 256 MiB and the encoded complete
|
||||
report to 64 MiB, checking row counts before allocation and JSON/UTF-8/base64 expansion during
|
||||
decoding, before serialization. Names and identical literal spans are decoded
|
||||
once within one stable file snapshot; no filesystem results are cached across
|
||||
inspections. Ownership assignment also yields to cancellation. MCP also applies its
|
||||
[response transport budget](mcp-contracts.md); a complete report within the
|
||||
report limit can still require the summary and paging path.
|
||||
|
||||
## Outside this tool
|
||||
|
||||
- Decrypting or deobfuscating protected metadata.
|
||||
- Linking Mach-O binaries that use chained fixups, ELF images with Android
|
||||
packed (APS2) relocations, and 32-bit images; generic method instances,
|
||||
adjustor thunks and invokers.
|
||||
- Full method signatures beyond the rendered field, parameter and return type
|
||||
names, generic method instances, and type names for metadata version 27.
|
||||
- Running Ghidra or another disassembler. `--ghidra-seeds` only writes a seed
|
||||
file for [`REA_GHIDRA_SEED_FILE`](installation.md); use the
|
||||
[native workflow](native-investigation.md) for native code.
|
||||
- Metadata versions other than 24, 27, 29, 31 and 39.
|
||||
|
||||
Names come from the metadata file only. They do not establish which methods
|
||||
were compiled into the native binary or how they behave at runtime.
|
||||
|
||||
## Real producer verification
|
||||
|
||||
`npm run verify:il2cpp:metadata -- --metadata PATH` exercises a caller-supplied
|
||||
`global-metadata.dat` through the public CLI and MCP. It is opt-in, like
|
||||
`verify:pcap`, because no real game metadata ships with the repository. Normal
|
||||
inspection does not need it.
|
||||
|
||||
The follow-up was checked against the public Cpp2IL
|
||||
[`Simple_2022_3_35` sample](https://github.com/SamboyCoding/Cpp2IL/tree/b5ad444b82267cb1e4b88b8b373c008105bdea52/TestFiles/Simple_2022_3_35):
|
||||
version 31, 1,892,268 bytes, SHA-256
|
||||
`2441635a7c234a1a34deccc6a9906d6f5abcbdab078a38b2bf488a52a4962c48`.
|
||||
An independent byte-layout oracle matched all 36 images, 2,468 types and 12,409
|
||||
methods. Corresponding managed DLLs independently matched names and ownership
|
||||
for 35 images, 2,463 types and 12,406 methods; the generated image has no matching
|
||||
DLL. This does not establish real-producer coverage for versions 29 or 39.
|
||||
|
||||
Compact native summaries omit the `binary.code_gen_modules` table and keep its
|
||||
count in `counts.code_gen_modules`. Query `collection: "code_gen_modules"`
|
||||
with batch `image_indices` or source `indices`, selected `fields` and pagination.
|
||||
Module row `index` is its retained collection position; `image` joins the
|
||||
metadata image collection. Scalar registration facts and both source identities
|
||||
remain inline. Complete Evidence retains the original full table.
|
||||
+13
-11
@@ -47,17 +47,19 @@ For runtime instrumentation, see the [Frida guide](./frida-instrumentation.md).
|
||||
|
||||
## Choose a guide
|
||||
|
||||
| Task | Guides |
|
||||
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Inspect native functions, references, or types | [Native binaries](native-investigation.md), [Go metadata](go-binaries.md), [Rust dependencies](rust-binaries.md), [offline ELF layout](binary-diagnostics.md), [recorded crashes](recorded-crashes.md) |
|
||||
| Select a native provider or import format | [Ghidra semantics](ghidra-provider.md), [Windows](windows-ghidra-p0.md), [DOS](ghidra-dos.md), [raw bytes](raw-binary.md), [MIPS](ghidra-mips.md), [PSP](ghidra-psp.md), [NativeAOT](ghidra-nativeaot.md), [IDA](ida-provider.md), [Kuna](kuna-provider.md), [JEB](jeb-analysis.md) |
|
||||
| Analyze JavaScript or Electron artifacts | [Static analysis](javascript-artifact-reconstruction.md), [tracing and comparisons](javascript-application-workflows.md), [application graph](javascript-application-graph.md), [source recovery](javascript-recovery.md) |
|
||||
| Compare static JavaScript with runtime observations | [Runtime attribution](javascript-runtime-observation.md), [reconciliation](javascript-runtime-reconciliation.md), [Electron observation](electron-observation.md), [Electron fuses](electron-fuses.md) |
|
||||
| Inspect assemblies, packages, or firmware | [.NET](managed-code-analysis.md), [Android](android-analysis.md), [Apktool resources](apktool-resource-analysis.md), [ADB acquisition](adb-device-analysis.md), [Apple](apple-application-analysis.md), [Harmony](harmony-analysis.md), [firmware](firmware-analysis.md), [WASM](wasm-artifacts.md), [EVM](evm-bytecode.md) |
|
||||
| Observe or compare a browser scenario | [CDP observation](browser-observation.md), [scenarios](browser-scenario-contract.md), [runtime evidence](web-runtime.md), [historical captures](web-network-captures.md) |
|
||||
| Trace captured website source | [Source locations](web-source-location.md), [module traces](website-module-trace.md), [script export](website-script-export.md) |
|
||||
| Capture and compare a command | [Process capture](process-capture.md) |
|
||||
| Use CLI/MCP and retain evidence | [CLI](cli.md), [MCP contracts](mcp-contracts.md), [agent prompts](mcp-prompts.md), [obligation ledgers](reconstruction-obligation-ledgers.md) |
|
||||
| Task | Guides |
|
||||
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Identify a desktop installation | [Runtime candidates, evidence and follow-up files](desktop-applications.md) |
|
||||
| Inspect native functions, references, or types | [Native binaries](native-investigation.md), [Go metadata](go-binaries.md), [Rust dependencies](rust-binaries.md), [offline ELF layout](binary-diagnostics.md), [recorded crashes](recorded-crashes.md) |
|
||||
| Select a native provider or import format | [Ghidra semantics](ghidra-provider.md), [Windows](windows-ghidra-p0.md), [DOS](ghidra-dos.md), [raw bytes](raw-binary.md), [MIPS](ghidra-mips.md), [PSP](ghidra-psp.md), [NativeAOT](ghidra-nativeaot.md), [IDA](ida-provider.md), [Kuna](kuna-provider.md), [JEB](jeb-analysis.md) |
|
||||
| Analyze JavaScript or Electron artifacts | [Static analysis](javascript-artifact-reconstruction.md), [tracing and comparisons](javascript-application-workflows.md), [application graph](javascript-application-graph.md), [source recovery](javascript-recovery.md) |
|
||||
| Compare static JavaScript with runtime observations | [Runtime attribution](javascript-runtime-observation.md), [reconciliation](javascript-runtime-reconciliation.md), [Electron observation](electron-observation.md), [Electron fuses](electron-fuses.md) |
|
||||
| Inspect assemblies, packages, or firmware | [Godot PCK](godot-pck.md), [.NET](managed-code-analysis.md), [Android](android-analysis.md), [Apktool resources](apktool-resource-analysis.md), [ADB acquisition](adb-device-analysis.md), [Apple](apple-application-analysis.md), [Harmony](harmony-analysis.md), [firmware](firmware-analysis.md), [WASM](wasm-artifacts.md), [EVM](evm-bytecode.md) |
|
||||
| Observe or compare a browser scenario | [CDP observation](browser-observation.md), [scenarios](browser-scenario-contract.md), [runtime evidence](web-runtime.md), [historical captures](web-network-captures.md) |
|
||||
| Serve recorded behavior as an agent environment | [Agent environments](agent-environments.md) |
|
||||
| Trace captured website source | [Source locations](web-source-location.md), [module traces](website-module-trace.md), [script export](website-script-export.md) |
|
||||
| Capture and compare a command | [Process capture](process-capture.md) |
|
||||
| Use CLI/MCP and retain evidence | [CLI](cli.md), [MCP contracts](mcp-contracts.md), [agent prompts](mcp-prompts.md), [obligation ledgers](reconstruction-obligation-ledgers.md) |
|
||||
|
||||
For development, use [testing](testing.md), [client verification](client-verification.md),
|
||||
[tool design](tool-design.md), and [releasing](releasing.md).
|
||||
|
||||
+26
-3
@@ -987,6 +987,24 @@ can time out earlier even when Ghidra would complete within its startup deadline
|
||||
See [Ghidra first-query deadlines and recovery](mcp-contracts.md#ghidra-first-query-deadlines-and-recovery)
|
||||
for client options and the close/reopen recovery flow.
|
||||
|
||||
The 330-second default is configurable. Configuration is read when REA starts:
|
||||
changing an environment variable in another shell, or closing and reopening a
|
||||
binary in the same MCP server, does not change that server's startup allowance.
|
||||
For a CLI retry with an example 30-minute allowance:
|
||||
|
||||
```sh
|
||||
REA_GHIDRA_STARTUP_TIMEOUT_MS=1800000 rea analyze /absolute/path/to/app.exe --provider ghidra --json
|
||||
```
|
||||
|
||||
For MCP, export useful Evidence before restarting, close the binary and resolve
|
||||
any cleanup failure, update the environment in the client's REA server launch
|
||||
configuration, then restart REA and reconnect before reopening the target. Allow
|
||||
startup plus the query in the client's own tool-call deadline. Every retry uses
|
||||
a fresh ephemeral import; smaller result requests do not reduce the prerequisite
|
||||
auto-analysis. A larger time allowance does not raise the JVM heap limit or
|
||||
guarantee completion. Keep [heap and host CPU controls](#ghidra-heap-and-cpu-controls)
|
||||
appropriate for the machine.
|
||||
|
||||
One session contains exactly one imported Program; use `provider_id: "ghidra"`, `--provider ghidra`, or
|
||||
`REA_ANALYSIS_PROVIDER=ghidra` when both Hopper and Ghidra support the target.
|
||||
One persistent decompiler is owned by the Program, and a serial queue keeps
|
||||
@@ -1089,9 +1107,14 @@ and hyphens; at most 32 names are admitted, each at most 120 characters.
|
||||
Names must identify analyzers registered for the selected program. Unknown names
|
||||
or options that remain enabled abort the headless import before auto-analysis
|
||||
and the bridge, so an unsuccessful override cannot produce a session claiming
|
||||
that the analyzer was disabled. For Windows PE resource analysis, the exact name
|
||||
is `WindowsResourceReference`. Disabling an analyzer skips the facts it would
|
||||
have produced.
|
||||
that the analyzer was disabled. The setting applies to every import in the REA
|
||||
server. A format-specific analyzer may not be registered for another target, so
|
||||
the same override can prevent that target from opening. For mixed-target work,
|
||||
omit the override or use a separate REA server configured for the affected
|
||||
format. Changing the environment requires restarting the server; reopening a
|
||||
target alone keeps the current setting. For Windows PE resource analysis, the
|
||||
exact name is `WindowsResourceReference`. Disabling an analyzer skips the facts
|
||||
it would have produced.
|
||||
|
||||
### Ghidra heap and CPU controls
|
||||
|
||||
|
||||
@@ -13,6 +13,14 @@ rea doctor --provider kuna
|
||||
|
||||
Select it explicitly with `--provider kuna`, MCP's `open_binary.provider_id: "kuna"`, or `REA_ANALYSIS_PROVIDER=kuna`. With Kuna and another provider configured for the same target, `auto` reports `ambiguous` instead of choosing one.
|
||||
|
||||
Ordinary readiness cancellation returns `cancelled` (CLI SIGINT/SIGTERM exits
|
||||
130/143); an earlier recorded timeout or physical failure keeps its diagnostic.
|
||||
Completed version, hash and script observations remain in `kuna_installation`
|
||||
with `status: "partial"` and `completed_hash` when available. Typed errors carry
|
||||
this in `details.diagnostics`; doctor cleanup checks carry it in their `details`.
|
||||
Omitted fields remain unobserved; availability remains unknown until readiness
|
||||
completes. Cleanup retries keep both the observations and the exact file owner.
|
||||
|
||||
| Setting | Default | Meaning |
|
||||
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `REA_KUNA_COMMAND` | none (required) | Absolute path of the `kuna` executable. |
|
||||
|
||||
@@ -119,9 +119,11 @@ returning an empty one.
|
||||
|
||||
The canonical parser admits PE/CLI bytes and their metadata/CIL without loading
|
||||
an assembly. It reports observed implementation markers and unavailable facts;
|
||||
it does not unpack .NET single-file hosts, decode IL2CPP metadata, or infer a
|
||||
NativeAOT identity from an ordinary native PE. Inputs without admitted CLI
|
||||
metadata do not become managed assemblies through naming or routing guesses.
|
||||
it does not unpack .NET single-file hosts or infer a NativeAOT identity from an
|
||||
ordinary native PE. It does not decode IL2CPP; use
|
||||
[IL2CPP metadata](il2cpp-metadata.md) for the image, type and method names in a
|
||||
Unity `global-metadata.dat`. Inputs without admitted CLI metadata do not
|
||||
become managed assemblies through naming or routing guesses.
|
||||
Inspect separately obtained components explicitly and preserve their identities.
|
||||
|
||||
A valid implementation marker establishes a candidate native boundary, not
|
||||
|
||||
+103
-5
@@ -1,5 +1,9 @@
|
||||
# MCP runtime contracts
|
||||
|
||||
For pairing native functions before comparing selected dossiers, see
|
||||
[native function matching](native-function-matching.md). It operates on retained
|
||||
inventories, preserves aliases/unknowns and exposes selected fact pages.
|
||||
|
||||
Use this reference to call REA tools and interpret their replies on one MCP
|
||||
connection. Start with [tool results](#tool-results), [retained Evidence](#retained-application-evidence-inputs),
|
||||
and [session discovery](#identity-and-discovery). See [transports](#transports)
|
||||
@@ -22,6 +26,14 @@ directly to `compare_functions`, and `inspect_artifact` Evidence to
|
||||
`compare_artifacts`. Use `get_evidence_bundle` when the task needs broader
|
||||
retained session history or an explicit bundle for transfer.
|
||||
|
||||
Ghidra inventories and function replies use `raw_result: null` when their facts
|
||||
are already preserved in `normalized_result`, limitations, locations and
|
||||
relationship coverage. Distinct overview details, measured load-image
|
||||
observations, file-offset selection roles and database types supplemented with
|
||||
derived metadata keep their upstream representation. Read function results from
|
||||
`normalized_result`; older retained Evidence keeps its original representation
|
||||
and identity.
|
||||
|
||||
Artifact inventories declare `coverage.scope` (`root` or `container-entries`)
|
||||
and `coverage.status` (`complete` or `unknown`) independently of diagnostic
|
||||
wording. Comparison requires matching scopes, complete graph members and complete
|
||||
@@ -59,7 +71,13 @@ Call comparison additionally retains the Evidence relationship coverage
|
||||
checks. Ghidra and Hopper declare their observed dossier facets and complete
|
||||
provider-observed caller/callee sets, including empty sets. Missing decompiler
|
||||
output remains unavailable; complete call coverage does not claim every runtime
|
||||
target. Hopper's unavailable reference-kind metadata and reported unresolved
|
||||
target. When Ghidra's decompiler fails on one function, its dossier is still
|
||||
returned: `pseudocode`, `native_api` and `native_value_flow` are unavailable
|
||||
with the decompiler's failure as the reason, while the listing-backed facets
|
||||
are collected as usual. These failed decompiler observations and workflows
|
||||
derived from them retain Evidence but skip snapshot reuse, so a later request
|
||||
can observe backend recovery; healthy dossiers keep their existing cache policy.
|
||||
`procedure_pseudo_code` still fails for that function. Hopper's unavailable reference-kind metadata and reported unresolved
|
||||
calls still prevent equality conclusions for those dimensions. Analysis profiles
|
||||
version these declarations so older snapshots are not reused as current results.
|
||||
IDA declares the facets its selected adapter cannot provide; an empty
|
||||
@@ -72,13 +90,30 @@ metadata in the supported JSON payloads (`parameters`, `raw_result`,
|
||||
|
||||
Tool execution failures return `isError: true` and the complete canonical
|
||||
`{ "error": ... }` diagnostic as JSON in text `content`. Read that diagnostic
|
||||
for the error code, target details, partial observations, and remediation.
|
||||
for the error code, target details, partial observations, remediation, and the
|
||||
optional bounded `recovery` next step. `recovery.kind=inspect_input_contract`
|
||||
names the rejected operation: re-read that operation's advertised input schema
|
||||
(call `tools/list` if the catalog may be stale), correct the listed fields, and
|
||||
do not repeat the unchanged call. `recovery.kind=inspect_timeout_state` names
|
||||
the operation and the REA server/provider deadline; inspect session/provider
|
||||
state before retrying, and raise the MCP client's tool deadline only when the
|
||||
client ended the call before REA returned this response. These hints never
|
||||
include the complete catalog or caller arguments.
|
||||
Failures omit `structuredContent`, since each advertised `outputSchema`
|
||||
describes successful results. Successful replies retain their schema-validated
|
||||
structured data and matching text projection. Oversized errors are retained
|
||||
as Evidence before delivery of a bounded diagnostic with an export reference;
|
||||
if retention is unavailable or fails, the diagnostic reports that reason.
|
||||
|
||||
SDK-generated input validation failures are normalized into the same canonical
|
||||
`invalid_request` text when the bounded diagnostic fits the response budget.
|
||||
The complete field/type diagnostic is preserved when normalization fits the
|
||||
response budget, including request-specific rejected fields and reasons. Oversized
|
||||
SDK diagnostics keep the existing retained-Evidence recovery path. An unknown
|
||||
tool remains an MCP JSON-RPC `-32602` error, but its message directs the caller
|
||||
to `tools/list` and an advertised operation instead of repeating the unknown
|
||||
name.
|
||||
|
||||
`get_call_graph` returns a `graph` keyed by traversal depth and a top-level
|
||||
`limitations` array with deduplicated execution caveats. Each node keeps its
|
||||
relationship coverage and read-specific `limitations`, alongside observed
|
||||
@@ -93,8 +128,9 @@ unresolved relationship reads. Cancellation remains a tool error.
|
||||
|
||||
REA prepares complete MCP results within the pinned stdio client's 10 MiB
|
||||
receive-buffer budget, including both text and structured representations and
|
||||
room for the JSON-RPC envelope. Tool errors carry only their text
|
||||
representation and `isError` flag, so only those count against the budget. Response budget settings are captured at startup;
|
||||
room for the JSON-RPC envelope. Tool errors omit `structuredContent` from the
|
||||
wire; their text, `isError`, provenance marker and negotiated protocol framing
|
||||
count toward the wire budget. Response budget settings are captured at startup;
|
||||
restart or recreate the server to apply changes. If a result cannot fit, REA returns
|
||||
`resource_constraint` with `details.resource: "transport"` before constructing
|
||||
a document-sized string. Analysis Evidence remains complete in the current
|
||||
@@ -104,6 +140,52 @@ that reference for a summary, one section/module, or a stable page, or call
|
||||
`export_evidence_bundle` with a destination path. Complete
|
||||
bundle exports stream canonical JSON into an atomically published file.
|
||||
|
||||
On protocol revision `2026-07-28`, completed replies retain the SDK's
|
||||
`resultType: "complete"`, including the replacement for an oversized error.
|
||||
This marks completion of the MCP exchange; Evidence coverage, partial facts
|
||||
and unknowns retain their separate meaning. Legacy replies keep their existing
|
||||
framing.
|
||||
|
||||
`list_procedures`, `search_procedures`, `list_strings`, `search_strings` and
|
||||
`list_names` accept `detail: "summary"`. Complete inline delivery remains the
|
||||
default. Summary retains the original complete Evidence and returns its row
|
||||
count, provider identity, analysis-profile digest, original query, parent Evidence
|
||||
ID, limitations and explicit unknown discovery coverage. Delivery detail is excluded from provider arguments
|
||||
and original Evidence identity.
|
||||
|
||||
Read selected rows with `inspect_analysis_view`:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "ev_<source digest>"
|
||||
},
|
||||
"view": {
|
||||
"kind": "native-inventory",
|
||||
"offset": 0,
|
||||
"limit": 32,
|
||||
"fields": ["value"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Each row has `source_index` and `record`; parent Evidence ID plus source index
|
||||
identifies same-address alternatives. Order is the original retained order.
|
||||
Omit `fields` for complete records; a selected field list always retains
|
||||
`record.address`. Selecting `value` also retains available `string` and
|
||||
`decoding` qualifications, so native display text and unterminated fragments
|
||||
remain distinguishable. Search tools apply their existing filters before retention.
|
||||
Follow `coverage.next_offset`; exhaustion describes the retained list, not
|
||||
complete discovery of all code or strings. An unbound source keeps
|
||||
`artifact: null` and the derived Evidence keeps `subject: null`.
|
||||
|
||||
The CLI uses the same projector. For an original complete Evidence JSON file,
|
||||
write `{"source":{"kind":"evidence-file","path":"/absolute/source.json"},"view":{"kind":"native-inventory","limit":32}}`
|
||||
to `/absolute/view-request.json`, then run
|
||||
`rea inspect-analysis-view /absolute/view-request.json --json`. This also reads
|
||||
pre-summary source records without changing their identity; no live provider is started.
|
||||
|
||||
For oversized native function Evidence, `details.reported_limits.continuation`
|
||||
contains a ready-to-call `inspect_analysis_view` request for the retained
|
||||
pseudocode's first page. Use its `name` and `arguments` directly, then follow
|
||||
@@ -112,6 +194,8 @@ limit is the view contract's default, not a guarantee that arbitrary source or
|
||||
metadata fits; reduce the limit or export when needed. Continuations are
|
||||
offered for completed `analyze_function` results only after session recording is
|
||||
acknowledged; the complete result and its retention reference remain intact.
|
||||
Oversized native inventories likewise include a ready-to-call first
|
||||
`native-inventory` page. Reduce its limit or select fields when a row is large.
|
||||
|
||||
A broad follow-up or `get_evidence_bundle` can also exceed the response budget; exporting
|
||||
preserves the complete session without sending it through a single MCP frame.
|
||||
@@ -218,13 +302,20 @@ no group was templated.
|
||||
See [derived API surfaces](web-network-captures.md#derived-api-surface).
|
||||
|
||||
`inspect_analysis_view` also accepts `environment-manifest` for retained HAR capture
|
||||
Evidence, projecting the primary origin's request lines into named tools with path
|
||||
Evidence and `capture_browser_scenario` Evidence, projecting the primary origin's request lines into named tools with path
|
||||
templates, parameters, observed query names, value-free body shapes and record
|
||||
pointers, plus excluded groups and three fixed unknowns. Templates, parameters and
|
||||
names are inferences; the Evidence is inferred whenever any tool or excluded group
|
||||
is templated, and derived otherwise.
|
||||
See [environment manifests](web-network-captures.md#environment-manifest).
|
||||
|
||||
`inspect_analysis_view` also accepts `environment-state` for the same parents,
|
||||
extracting the initial state the recorded reads observed: entity types with an
|
||||
identity field, entities, documents for written non-entity paths, and a render
|
||||
plan per read tool that reproduces its recordings from the state or names why it
|
||||
cannot. Types and identity are inferences, so the Evidence is always inferred.
|
||||
See [environment state](web-network-captures.md#environment-state).
|
||||
|
||||
`inspect_analysis_view` projects a caller-selected view of already completed
|
||||
`inspect_binary_layout`, `analyze_javascript_application`, or `analyze_function`
|
||||
Evidence. Native views select procedure, pseudocode, assembly, basic blocks,
|
||||
@@ -768,6 +859,13 @@ the current client, so it is not a fresh-start recovery. On the same connection:
|
||||
`provider_id: "ghidra"`, then retry the first query with an appropriate client
|
||||
deadline. The connection and selected provider do not need to change.
|
||||
|
||||
If REA's provider startup allowance also needs raising, preserve Evidence and
|
||||
close as above, then update `REA_GHIDRA_STARTUP_TIMEOUT_MS` in REA's launch
|
||||
environment and restart the server. Reconnect the MCP client before reopening
|
||||
the target. Configuration is read at process startup; close/reopen on the same
|
||||
connection cannot apply a changed environment. See the
|
||||
[CLI retry example](installation.md#ghidra).
|
||||
|
||||
This close/reopen flow was exercised on one real Linux stdio connection after a
|
||||
controlled startup timeout, followed by a successful overview and function
|
||||
analysis. A separate Windows x64 stdio run with Ghidra 12.1.4, JDK 21 and Node
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
# Match native functions across builds
|
||||
|
||||
`match_native_functions` pairs two existing `list_procedures` Evidence inventories. Select `list_procedures` `detail: "summary"` to retain
|
||||
complete source rows without transferring the entire inventory. Its
|
||||
`parent_evidence_id` is the reference matching consumes.
|
||||
Targets may be opened sequentially; matching never opens a target, rereads its
|
||||
file, executes it or starts a provider. Preserve each inventory before closing its
|
||||
target, or import its Evidence bundle later. `close_binary` clears session
|
||||
records. Export each bundle before closing; after collecting both sides and
|
||||
closing the last target, import both bundles, then use their references. Keeping
|
||||
only an Evidence ID across close is insufficient. Complete inline Evidence and
|
||||
CLI `source_bundle` inputs are portable alternatives. Pass Evidence or exact
|
||||
session references as `left` and `right`:
|
||||
|
||||
```json
|
||||
{
|
||||
"left": { "kind": "retained-evidence", "evidence_id": "ev_<left digest>" },
|
||||
"right": { "kind": "retained-evidence", "evidence_id": "ev_<right digest>" }
|
||||
}
|
||||
```
|
||||
|
||||
Unique exact reported names are paired first. Optional `list_names` Evidence
|
||||
identifies dynamic/default names to exclude. If origin is unavailable, the
|
||||
existing automatic-name exclusion rule applies and each function retains that
|
||||
limitation. Exact names establish name correspondence, not unchanged code or
|
||||
original-source identity.
|
||||
|
||||
For unmatched candidates, optionally supply `left_observations` and
|
||||
`right_observations`. `procedure_info` or existing `analyze_function` Evidence
|
||||
gives exact inclusive body ranges; `read_bytes` supplies memory bytes. Observations
|
||||
must belong to the side's exact target/provider/profile and document context.
|
||||
Adjacent, consistent overlapping and partially returned reads compose by their
|
||||
observed extent; conflicting intervals remain gaps. A byte match requires every
|
||||
body byte, equal total size, equal ordered range lengths, a unique SHA-256
|
||||
fingerprint and the same known architecture. Extents are never guessed from the
|
||||
next entry, and relocations are not normalized. A larger mapped read can serve
|
||||
several observed bodies; only needed ranges need to be captured.
|
||||
|
||||
This first slice reuses existing range observations. Stripped builds can require
|
||||
many lightweight range queries when those facts are not retained yet. Matching
|
||||
does not hide that acquisition cost or silently start those queries. Full
|
||||
decompilation is unnecessary.
|
||||
|
||||
Repeated candidates stay `unresolved` groups without a Cartesian pair expansion.
|
||||
`only_left`/`only_right` mean no exact counterpart in the supplied observations;
|
||||
renaming, inlining, stripping, changed bytes or incomplete discovery can explain
|
||||
them. They do not prove source addition/removal. `source_coverage` explicitly keeps
|
||||
inventory completeness unknown and separately counts known byte fingerprints and
|
||||
resolved correspondence. Provider limitations remain accessible.
|
||||
|
||||
Default `detail: "summary"` retains complete matching Evidence in MCP and returns
|
||||
counts, both identities and coverage. Query its `parent_evidence_id`:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": {
|
||||
"kind": "retained-evidence",
|
||||
"evidence_id": "ev_<matching digest>"
|
||||
},
|
||||
"view": {
|
||||
"kind": "function-matches",
|
||||
"collection": "functions",
|
||||
"states": ["matched"],
|
||||
"sides": ["left"],
|
||||
"fields": ["name", "body_sha256"],
|
||||
"offset": 0,
|
||||
"limit": 20
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`collection: "matches"` returns relation identities, states, basis and member
|
||||
counts. Large member lists are omitted by default; select `left`/`right` columns
|
||||
explicitly or page individual `functions`. Batch `match_ids`/`function_ids`, exact function `names`, state
|
||||
and side filters intersect. Mandatory identities and provenance survive column
|
||||
selection. Page coverage covers selected retained facts. Function identity binds
|
||||
inventory Evidence and the owning inventory’s `source_index`, preserving same-address alternatives.
|
||||
One validation, lazy member index and bounded recent filter are retained per
|
||||
immutable parent; no live target state is cached.
|
||||
|
||||
When both functions already have unambiguous `analyze_function` dossiers, a matched
|
||||
relation includes `compare_functions_input` with directly usable retained
|
||||
references. Otherwise analyze just the selected canonical addresses in their
|
||||
respective targets, then compare those dossiers. Matching never claims behavioral
|
||||
equivalence. Optional informative CFG/call/string fingerprints from supplied
|
||||
dossiers connect unresolved candidate groups only. They require observed CFG,
|
||||
callees and strings, complete observed call coverage, no unresolved calls, and
|
||||
the same analysis-profile identity. Empty leaf-function buckets are excluded.
|
||||
`structural_candidate_sha256` identifies that derived candidate signature; its
|
||||
dossier link preserves the full supporting facts. Group membership does not
|
||||
assert that every member pair is a candidate or equivalent.
|
||||
|
||||
CLI uses the same service and JSON input:
|
||||
|
||||
```bash
|
||||
rea match-native-functions request.json \
|
||||
--source_bundle left-bundle.json --source_bundle right-bundle.json \
|
||||
--evidence_output matching-bundle.json --json
|
||||
```
|
||||
|
||||
`source_bundle` resolves exact references from authenticated local bundles.
|
||||
Default summary requires `evidence_output`, retaining full matching Evidence and
|
||||
its selected source closure, including transitive Evidence links across supplied
|
||||
bundles. Missing dependencies fail with an integrity error; unrelated records
|
||||
are omitted. Publication never overwrites. Set `detail: "complete"`
|
||||
in the JSON for all groups inline. Import the bundle into MCP for views, or
|
||||
extract its matching Evidence record to a JSON file for the existing CLI
|
||||
`inspect-analysis-view` `evidence-file` source. Input bundles are unchanged.
|
||||
|
||||
Representation admission is bounded at 96 MiB before cloning selected observations
|
||||
and expanding matching indexes and repeated per-function source links. Work yields to cancellation. No worker/provider
|
||||
concurrency is introduced; this budget is not a bound on total process RSS.
|
||||
|
||||
The self-owned source fixture is [nativeFunctionMatching.c](https://github.com/morluto/rea/blob/main/tests/fixtures/nativeFunctionMatching.c).
|
||||
On Linux x86-64, compile with `cc -O2 -g0 -fno-pie -no-pie -DVERSION=1` and
|
||||
`-DVERSION=2`; copy the second binary and run `strip --strip-all` on that copy.
|
||||
It includes unchanged/changed code, a byte-identical rename, a removed/added
|
||||
observation, an inlined function, identical-code aliases and a renamed changed
|
||||
function with an observed call/string signature. Analyze without executing the
|
||||
fixture. Full-provider verification still needs caller-supplied Ghidra and Java.
|
||||
@@ -146,6 +146,7 @@ npx rea-agents@latest setup
|
||||
| شيفرة EVM البايتية | محددات التوجيه، وإزاحات البايتات، والمعاملات المستنتجة، وقابلية تغيير الحالة | مدخل محلي يحمل بايتات خامًا أو تمثيلًا سداسيًا عشريًا على Linux x64؛ [دليل EVM دون تشغيل](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| أعطال Linux المسجّلة | سجلات note الخام، ومسجّلات المعالج/الإشارات لكل خيط مسجّل، وترشيحات اختيارية لخرائط الذاكرة | pwntools يوفّره المستدعي على Linux x64؛ GDB/pwndbg اختياريان؛ [الأعطال المسجّلة](../recorded-crashes.md) |
|
||||
| تثبيتات سطح المكتب | عائلات وقت التشغيل، والملفات الداعمة، وعلامات المكونات، والخطوات التالية | فحص محدود للملفات والترويسات;[تطبيقات سطح المكتب](../desktop-applications.md) |
|
||||
| JavaScript / Electron | الوحدات، وعمليات الاستيراد، وخرائط المصدر، والمسارات، وIPC، وعلاقات إضافات الشيفرة الأصلية | Node.js وnpm؛ [تحليل التطبيقات](../javascript-application-workflows.md) |
|
||||
| المواقع | بنية الصفحة، والسكربتات، وملاحظات الشبكة، ولقطات الشاشة المطلوبة | متصفح من عائلة Chrome؛ [تحليل المتصفح](../browser-observation.md) |
|
||||
| تسجيلات الشبكة المحفوظة | الطلبات، والاستجابات، ومحتويات الحمولة المتاحة، ومواضعها في المصدر | HAR؛ وmitmdump على Linux لتسجيلات mitmproxy بصيغتها الأصلية؛ [دليل تسجيلات الشبكة](../web-network-captures.md) |
|
||||
|
||||
@@ -146,6 +146,7 @@ REA benötigt Node.js 22.x (>=22.19), 24.x (>=24.11) oder 26+ sowie npm. Zusätz
|
||||
| EVM-Bytecode | Dispatch-Selektoren, Byte-Offsets, abgeleitete Argumente und Veränderbarkeit des Zustands | Lokale Eingabe mit Rohbytes oder Hexdarstellung unter Linux x64; [Offline-EVM-Leitfaden](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| Aufgezeichnete Linux-Abstürze | Unverarbeitete Note-Einträge, Register/Signale aller aufgezeichneten Threads und optionale Zuordnungskandidaten | Vom Aufrufer bereitgestelltes pwntools unter Linux x64; optional GDB/pwndbg; [aufgezeichnete Abstürze](../recorded-crashes.md) |
|
||||
| Desktop-Installationen | Laufzeitkandidaten, Quelldateien, Komponenten und nächste Schritte | Begrenzte Verzeichnis- und Headerprüfung;[Desktop-Anwendungen](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Module, Imports, Source Maps, Routen, IPC und Beziehungen zu nativen Add-ons | Node.js und npm; [Anwendungsanalyse](../javascript-application-workflows.md) |
|
||||
| Websites | Seitenstruktur, Skripte, Netzwerkbeobachtungen und angeforderte Screenshots | Ein Browser aus der Chrome-Familie; [Browseranalyse](../browser-observation.md) |
|
||||
| Gespeicherte Netzwerkaufzeichnungen | Anfragen, Antworten, zugängliche Nutzdaten und Quellpositionen | HAR; mitmdump unter Linux für Aufzeichnungen im nativen mitmproxy-Format; [Leitfaden zu Netzwerkaufzeichnungen](../web-network-captures.md) |
|
||||
|
||||
@@ -153,6 +153,7 @@ npx rea-agents@latest setup
|
||||
| Bytecode EVM | Selectors επιλογής συνάρτησης, μετατοπίσεις σε bytes, εκτιμώμενα ορίσματα και δυνατότητα μεταβολής της κατάστασης | Τοπική είσοδος σε ακατέργαστα bytes ή δεκαεξαδική μορφή σε Linux x64· [οδηγός EVM χωρίς εκτέλεση](../evm-bytecode.md) |
|
||||
| Βάσεις δεδομένων SQLite | Πίνακες, στήλες, ευρετήρια, όψεις, triggers και επιλεγμένες γραμμές κανονικών πινάκων | Τοπικό στιγμιότυπο βάσης δεδομένων/WAL· Node.js 24.x >=24.15 ή 26+ με εγγενή όρια SQLite |
|
||||
| Καταγεγραμμένες καταρρεύσεις στο Linux | Ακατέργαστες εγγραφές notes, καταχωρητές/σήματα κάθε καταγεγραμμένου νήματος και προαιρετικές πιθανές αντιστοιχίσεις μνήμης | pwntools που παρέχει ο χρήστης σε Linux x64· προαιρετικά GDB/pwndbg· [καταγεγραμμένες καταρρεύσεις](../recorded-crashes.md) |
|
||||
| Εγκαταστάσεις επιφάνειας εργασίας | Υποψήφια περιβάλλοντα εκτέλεσης, αρχεία τεκμηρίωσης, στοιχεία και επόμενα βήματα | Περιορισμένη επιθεώρηση καταλόγων και κεφαλίδων;[Εφαρμογές επιφάνειας εργασίας](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Modules, εισαγωγές, source maps, διαδρομές, IPC και σχέσεις με πρόσθετα κώδικα μηχανής | Node.js και npm· [ανάλυση εφαρμογών](../javascript-application-workflows.md) |
|
||||
| Ιστότοποι | Δομή σελίδων, scripts, παρατηρήσεις δικτύου και ζητούμενα στιγμιότυπα οθόνης | Πρόγραμμα περιήγησης της οικογένειας Chrome· [ανάλυση μέσω προγράμματος περιήγησης](../browser-observation.md) |
|
||||
| Αποθηκευμένες καταγραφές δικτύου | Αιτήματα, αποκρίσεις, διαθέσιμα δεδομένα φορτίου και θέσεις στην πηγή | HAR ή SAZ του Fiddler Everywhere· mitmdump σε Linux για την εγγενή μορφή mitmproxy· [οδηγός καταγραφών](../web-network-captures.md) |
|
||||
|
||||
@@ -146,6 +146,7 @@ REA requiere Node.js 22.x (>=22.19), 24.x (>=24.11) o 26+, además de npm. Las h
|
||||
| Bytecode EVM | Selectores de despacho, desplazamientos en bytes, argumentos inferidos y mutabilidad | Entrada local con bytes sin procesar o representación hexadecimal, en Linux x64; [guía de EVM sin ejecutar](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| Fallos de Linux registrados | Registros note sin procesar, registros/señales de cada hilo registrado y posibles correspondencias de memoria opcionales | pwntools proporcionado por quien llama, en Linux x64; GDB/pwndbg opcionales; [fallos registrados](../recorded-crashes.md) |
|
||||
| Instalaciones de escritorio | Posibles entornos de ejecución, archivos de evidencia, componentes y siguientes pasos | Inspección acotada de directorios y cabeceras;[Aplicaciones de escritorio](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Módulos, importaciones, mapas de código fuente, rutas, IPC y relaciones con complementos nativos | Node.js y npm; [análisis de aplicaciones](../javascript-application-workflows.md) |
|
||||
| Sitios web | Estructura de la página, scripts, observaciones de red y capturas de pantalla solicitadas | Un navegador de la familia Chrome; [análisis del navegador](../browser-observation.md) |
|
||||
| Capturas de red guardadas | Solicitudes, respuestas, contenido accesible de las cargas útiles y ubicaciones de origen | HAR; mitmdump en Linux para capturas en formato nativo de mitmproxy; [guía de capturas](../web-network-captures.md) |
|
||||
|
||||
@@ -138,6 +138,7 @@ REA به Node.js 22.x (>=22.19)، 24.x (>=24.11) یا 26+، بههمراه np
|
||||
| بایتکد EVM | selectorهای dispatch، offsetهای بایتی، آرگومانهای استنباطشده و قابلیت تغییر وضعیت | دادهٔ خام/hex محلی روی Linux x64؛ [راهنمای EVM آفلاین](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| کرشهای ثبتشدهٔ لینوکس | یادداشتهای خام، رجیسترها/سیگنالهای تمام threadهای ثبتشده و گزینههای احتمالی نگاشت | pwntools تأمینشده توسط فراخواننده روی Linux x64؛ GDB/pwndbg اختیاری؛ [کرشهای ثبتشده](../recorded-crashes.md) |
|
||||
| نصبهای دسکتاپ | نامزدهای زمان اجرا، فایلهای شاهد، نشانههای مؤلفهها و مراحل بعدی | بررسی محدود پوشهها و سرآیندها;[برنامههای دسکتاپ](../desktop-applications.md) |
|
||||
| JavaScript / Electron | ماژولها، importها، source mapها، مسیرها، IPC و ارتباط افزونههای بومی | Node.js و npm؛ [تحلیل برنامه](../javascript-application-workflows.md) |
|
||||
| وبسایتها | ساختار صفحه، اسکریپتها، مشاهدات شبکه و اسکرینشاتهای درخواستی | مرورگر مبتنی بر Chrome؛ [تحلیل مرورگر](../browser-observation.md) |
|
||||
| دادههای ضبطشدهٔ شبکه | درخواستها، پاسخها، payloadهای آشکارشده و مکان آنها در منبع | HAR؛ ابزار mitmdump در لینوکس برای ضبطهای بومی mitmproxy؛ [راهنمای ضبط شبکه](../web-network-captures.md) |
|
||||
|
||||
@@ -150,6 +150,7 @@ REA nécessite Node.js 22.x (>=22.19), 24.x (>=24.11) ou 26+, ainsi que npm. Les
|
||||
| Bytecode EVM | Sélecteurs de dispatch, offsets en octets, arguments inférés et mutabilité | Fichier local brut ou hexadécimal sous Linux x64 ; [guide EVM hors ligne](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| Crashs Linux enregistrés | Notes brutes, registres et signaux de chaque thread enregistré, et correspondances de mappings facultatives | pwntools fourni par l'utilisateur sous Linux x64 ; GDB/pwndbg en option ; [crashs enregistrés](../recorded-crashes.md) |
|
||||
| Installations de bureau | Environnements candidats, fichiers justificatifs, composants et étapes suivantes | Inspection bornée des répertoires et en-têtes;[Applications de bureau](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Modules, imports, source maps, routes, IPC et relations avec les modules natifs | Node.js et npm ; [analyse d'applications](../javascript-application-workflows.md) |
|
||||
| Sites web | Structure des pages, scripts, observations réseau et captures d'écran demandées | Un navigateur de la famille Chrome ; [analyse du navigateur](../browser-observation.md) |
|
||||
| Captures réseau enregistrées | Requêtes, réponses, payloads exposés et emplacements dans le code source | HAR ; mitmdump sous Linux pour les captures mitmproxy natives ; [guide des captures](../web-network-captures.md) |
|
||||
|
||||
@@ -153,6 +153,7 @@ Alat tambahan dan dukungan sistem host bergantung pada target yang dianalisis:
|
||||
| Bytecode EVM | Selector dispatch, offset byte, argumen yang disimpulkan, dan sifat mutabilitas | Pembawa data raw/hex lokal; [panduan EVM offline](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| Crash Linux yang direkam | Catatan mentah, register/sinyal setiap thread yang direkam, dan kandidat pemetaan opsional | pwntools yang disediakan pemanggil; GDB/pwndbg opsional; [crash yang direkam](../recorded-crashes.md) |
|
||||
| Instalasi desktop | Kandidat runtime, berkas pendukung, penanda komponen, dan langkah berikutnya | Pemeriksaan direktori dan header terbatas;[Aplikasi desktop](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Modul, impor, source map, rute, IPC, dan hubungan add-on native | Node.js dan npm; [analisis aplikasi](../javascript-application-workflows.md) |
|
||||
| Situs web | Struktur halaman, skrip, pengamatan jaringan, dan tangkapan layar yang diminta | Peramban berbasis Chrome; [analisis peramban](../browser-observation.md) |
|
||||
| Rekaman jaringan tersimpan | Permintaan, respons, payload yang terekspos, dan lokasi sumber | HAR; mitmdump di Linux untuk rekaman native mitmproxy; [panduan rekaman](../web-network-captures.md) |
|
||||
|
||||
@@ -150,6 +150,7 @@ REA richiede Node.js 22.x (>=22.19), 24.x (>=24.11) o 26+, oltre a npm. Strument
|
||||
| Bytecode EVM | Selettori di dispatch, offset dei byte, argomenti inferiti e mutabilità | Carrier raw/hex locale; [guida EVM offline](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| Crash Linux registrati | Note grezze, registri/segnai di ogni thread registrato e candidati mapping opzionali | pwntools fornito dal chiamante; GDB/pwndbg opzionale; [crash registrati](../recorded-crashes.md) |
|
||||
| Installazioni desktop | Runtime candidati, file di supporto, componenti e passi successivi | Ispezione limitata di directory e intestazioni;[Applicazioni desktop](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Moduli, import, source map, route, IPC e relazioni con add-on nativi | Node.js e npm; [analisi delle applicazioni](../javascript-application-workflows.md) |
|
||||
| Siti web | Struttura della pagina, script, osservazioni di rete e screenshot richiesti | Un browser della famiglia Chrome; [analisi browser](../browser-observation.md) |
|
||||
| Acquisizioni di rete salvate | Richieste, risposte, payload esposti e posizioni sorgente | HAR; mitmdump su Linux per acquisizioni mitmproxy native; [guida alle acquisizioni](../web-network-captures.md) |
|
||||
|
||||
@@ -146,6 +146,7 @@ REA には Node.js 22.x(>=22.19)、24.x(>=24.11)、または 26+ と npm
|
||||
| EVM バイトコード | ディスパッチセレクター、バイトオフセット、推定された引数と状態変更の可否 | Linux x64 上のローカルの生バイト/16 進数の入力ファイル;[オフライン EVM ガイド](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| 記録済みの Linux クラッシュ | 生の note レコード、記録された全スレッドのレジスター/シグナル、任意のマッピング候補 | Linux x64 で利用者が用意する pwntools;GDB/pwndbg は任意;[記録済みクラッシュ](../recorded-crashes.md) |
|
||||
| デスクトップのインストール先 | ランタイム候補、根拠ファイル、コンポーネントの目印、次の調査手順 | 上限付きのディレクトリ・ヘッダー検査;[デスクトップアプリ](../desktop-applications.md) |
|
||||
| JavaScript / Electron | モジュール、インポート、ソースマップ、ルート、IPC、ネイティブアドオンの関係 | Node.js と npm;[アプリケーション分析](../javascript-application-workflows.md) |
|
||||
| ウェブサイト | ページ構造、スクリプト、ネットワークの観測結果、依頼されたスクリーンショット | Chrome 系ブラウザー;[ブラウザー分析](../browser-observation.md) |
|
||||
| 保存済みのネットワークキャプチャ | リクエスト、レスポンス、取得可能なペイロード、ソース内の位置 | HAR;mitmproxy ネイティブ形式のキャプチャには Linux 上の mitmdump;[キャプチャガイド](../web-network-captures.md) |
|
||||
|
||||
+30
-25
@@ -117,6 +117,10 @@ REA는 빠르게 바뀌며 새 릴리스에는 버그 수정이 자주 포함됩
|
||||
rea update
|
||||
```
|
||||
|
||||
`rea --update`는 이 검증된 업데이트 절차의 별칭이며 같은 전역 출력 플래그를 사용할 수 있습니다. 다른 명령 제어 옵션과 함께 사용할 때는 `rea update`를 사용하세요.
|
||||
|
||||
업데이트 명령은 stdout과 stderr를 합쳐 최대 4 MiB까지만 보관합니다. 한도를 넘긴 메타데이터 응답은 명령이 종료된 뒤 거부됩니다. 상세 출력을 사용하는 설치에서는 진단 정보를 한도 내에서만 보관하고 설치를 계속 진행합니다. 사람이 읽는 진행 출력은 터미널이 받아들이는 속도에 맞춰 전달되며, 이미 제출한 쓰기가 끝날 때까지 기다립니다.
|
||||
|
||||
Bun 전역 업데이트에는 Bun 1.2.17 이상이 필요합니다. 이전 버전의 Bun을 사용한다면 `rea update`를 실행하기 전에 Bun을 직접 업그레이드하세요.
|
||||
|
||||
에이전트 등록과 스킬을 갱신하려면 업데이트 과정에서 출력된 설정 명령을 실행하세요.
|
||||
@@ -143,31 +147,32 @@ npx rea-agents@latest setup
|
||||
|
||||
REA에는 Node.js 22.x(>=22.19), 24.x(>=24.11) 또는 26+와 npm이 필요합니다. 추가 도구와 호스트 지원은 대상에 따라 다릅니다.
|
||||
|
||||
| 대상 | REA가 반환하는 내용 | 요구 사항 및 가이드 |
|
||||
| --------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 네이티브 바이너리 | 의사 코드, 어셈블리, 문자열, 심볼, 호출, 참조 | Hopper, Ghidra 또는 IDA; [네이티브 분석](../native-investigation.md) |
|
||||
| 오프라인 ELF 레이아웃 | 섹션, 세그먼트, 원본 심볼/재배치 정보, 정적 분석으로 얻은 보안 완화 기법 후보 | Linux x64에서 호출자가 제공하는 pwntools; [바이너리 진단](../binary-diagnostics.md) |
|
||||
| Go 바이너리 | 내장된 컴파일러 버전, 모듈, `replace` 지시어, 빌드 설정 | REA 내장 ELF/PE/thin Mach-O 정적 검사; Go 설치 불필요 |
|
||||
| Rust 바이너리 | cargo-auditable이 내장한 크레이트 이름, 버전, 소스, 의존성 관계 | REA 내장 ELF/PE/thin Mach-O 정적 검사; [Rust 바이너리](../rust-binaries.md) |
|
||||
| WASM 아티팩트 | 유효성 검사, 섹션, import/export의 정확한 WAT 형식, 디코딩된 WAT | 호출자 제공 WABT 1.0.42; `inspect_wasm_artifact` |
|
||||
| EVM 바이트코드 | 디스패치 선택자, 바이트 오프셋, 추론한 인자와 상태 변경 가능 여부 | Linux x64의 로컬 원시 바이트/16진수 입력 파일; [오프라인 EVM 가이드](../evm-bytecode.md) |
|
||||
| SQLite 데이터베이스 | 테이블, 열, 인덱스, 뷰, 트리거, 선택한 일반 테이블의 행 | 로컬 데이터베이스/WAL 스냅샷; Node.js 24.15 이상의 24.x 또는 26 이상(네이티브 SQLite 한도 적용) |
|
||||
| 기록된 Linux 크래시 | 원시 note 레코드, 기록된 모든 스레드의 레지스터/시그널, 선택적 매핑 후보 | Linux x64에서 호출자가 제공하는 pwntools; GDB/pwndbg는 선택 사항; [기록된 크래시](../recorded-crashes.md) |
|
||||
| JavaScript / Electron | 모듈, 임포트, 소스 맵, 라우트, IPC, 네이티브 애드온 관계 | Node.js와 npm; Linux: procps 호환 `ps`; [애플리케이션 분석](../javascript-application-workflows.md) |
|
||||
| 웹사이트 | 페이지 구조, 스크립트, 네트워크 관찰 결과, 요청한 스크린샷 | Chrome 계열 브라우저; [브라우저 분석](../browser-observation.md) |
|
||||
| 저장된 네트워크 캡처 | 요청, 응답, 접근 가능한 페이로드, 소스 위치 | HAR 또는 Fiddler Everywhere SAZ; mitmproxy 네이티브 캡처에는 Linux의 mitmdump; [캡처 가이드](../web-network-captures.md) |
|
||||
| .NET 어셈블리 | 메타데이터, CIL 명령, 선언된 네이티브 의존성, 빌드 비교 | 별도 도구 불필요(정적 검사만); [관리 코드 가이드](../managed-code-analysis.md) |
|
||||
| Android APK | 매니페스트 선언, 클래스, 디컴파일된 메서드, 참조 | Linux/macOS/Windows x64의 헤드리스 JADX와 전체 JDK; [Android 가이드](../android-analysis.md) |
|
||||
| Android 기기 | 기기, 패키지, 프로세스, 로그, 덤프, 화면 캡처, APK/파일 전송 | 호출자 제공 adb(에뮬레이터 또는 실제 기기); [ADB 가이드](../adb-device-analysis.md) |
|
||||
| JEB 프로젝트 | 엔진 기반 프로젝트 유닛, 아티팩트 해시, 타입/메서드 의사코드 | 호출자가 시작하고 MCP를 제공하는 JEB 클라이언트(REA_JEB_MCP_URL로 선택); [JEB 가이드](../jeb-analysis.md) |
|
||||
| Android 리소스 | 디코딩된 매니페스트, 문자열 테이블, 로캘, 버전 정보 | 호출자 제공 Apktool 런처; [리소스 가이드](../apktool-resource-analysis.md) |
|
||||
| Flutter 앱 | Dart 스냅샷 해시, ABI별 라이브러리 해시, 엔진 빌드 출처 | 없음 (정적 APK 파싱); [Flutter 가이드](../flutter-analysis.md) |
|
||||
| 펌웨어 | 영역, 추출 결과, 네이티브 분석으로 전달할 정보 | Linux의 Binwalk / Unblob; [펌웨어 가이드](../firmware-analysis.md) |
|
||||
| 기록된 패킷 | 선택 필드, 반복 값, 필터 및 디코딩 규칙 | Linux: TShark 4.x + prlimit; [캡처 가이드](../packet-captures.md) |
|
||||
| 패킷 요약 | 엔드포인트, DNS, TLS SNI, 프로토콜 분포, HTTP 요청 | 호출자 제공 tshark 및 capinfos(Linux에서 검증됨); [pcap 가이드](../pcap-analysis.md) |
|
||||
| 기록된 논리 신호 | 디코더 주석, 샘플 범위, 진단 정보 | Linux: sigrok-cli 0.7.2 + prlimit; [신호 기록 가이드](../signal-recordings.md) |
|
||||
| 패키지 및 리소스 | 파일 목록, 다이제스트, plist, Apple 번들 구조, 추출한 리소스 | [아티팩트 및 JavaScript 가이드](../javascript-artifact-reconstruction.md), [Apple 애플리케이션](../apple-application-analysis.md) |
|
||||
| 프로세스 동작 | 터미널 출력, 상호작용, 종료 및 파일 시스템 관찰 결과, 실행 비교 | 네이티브 PTY를 지원하는 Linux/macOS; [프로세스 캡처](../process-capture.md) |
|
||||
| 대상 | REA가 반환하는 내용 | 요구 사항 및 가이드 |
|
||||
| ---------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 네이티브 바이너리 | 의사 코드, 어셈블리, 문자열, 심볼, 호출, 참조 | Hopper, Ghidra 또는 IDA; [네이티브 분석](../native-investigation.md) |
|
||||
| 오프라인 ELF 레이아웃 | 섹션, 세그먼트, 원본 심볼/재배치 정보, 정적 분석으로 얻은 보안 완화 기법 후보 | Linux x64에서 호출자가 제공하는 pwntools; [바이너리 진단](../binary-diagnostics.md) |
|
||||
| Go 바이너리 | 내장된 컴파일러 버전, 모듈, `replace` 지시어, 빌드 설정 | REA 내장 ELF/PE/thin Mach-O 정적 검사; Go 설치 불필요 |
|
||||
| Rust 바이너리 | cargo-auditable이 내장한 크레이트 이름, 버전, 소스, 의존성 관계 | REA 내장 ELF/PE/thin Mach-O 정적 검사; [Rust 바이너리](../rust-binaries.md) |
|
||||
| WASM 아티팩트 | 유효성 검사, 섹션, import/export의 정확한 WAT 형식, 디코딩된 WAT | 호출자 제공 WABT 1.0.42; `inspect_wasm_artifact` |
|
||||
| EVM 바이트코드 | 디스패치 선택자, 바이트 오프셋, 추론한 인자와 상태 변경 가능 여부 | Linux x64의 로컬 원시 바이트/16진수 입력 파일; [오프라인 EVM 가이드](../evm-bytecode.md) |
|
||||
| SQLite 데이터베이스 | 테이블, 열, 인덱스, 뷰, 트리거, 선택한 일반 테이블의 행 | 로컬 데이터베이스/WAL 스냅샷; Node.js 24.15 이상의 24.x 또는 26 이상(네이티브 SQLite 한도 적용) |
|
||||
| 기록된 Linux 크래시 | 원시 note 레코드, 기록된 모든 스레드의 레지스터/시그널, 선택적 매핑 후보 | Linux x64에서 호출자가 제공하는 pwntools; GDB/pwndbg는 선택 사항; [기록된 크래시](../recorded-crashes.md) |
|
||||
| 데스크톱 설치 디렉터리 | 런타임 후보, 근거 파일, 구성 요소 표시와 다음 조사 단계 | 범위가 제한된 디렉터리 및 헤더 검사;[데스크톱 애플리케이션](../desktop-applications.md) |
|
||||
| JavaScript / Electron | 모듈, 임포트, 소스 맵, 라우트, IPC, 네이티브 애드온 관계 | Node.js와 npm; Linux: procps 호환 `ps`; [애플리케이션 분석](../javascript-application-workflows.md) |
|
||||
| 웹사이트 | 페이지 구조, 스크립트, 네트워크 관찰 결과, 요청한 스크린샷 | Chrome 계열 브라우저; [브라우저 분석](../browser-observation.md) |
|
||||
| 저장된 네트워크 캡처 | 요청, 응답, 접근 가능한 페이로드, 소스 위치 | HAR 또는 Fiddler Everywhere SAZ; mitmproxy 네이티브 캡처에는 Linux의 mitmdump; [캡처 가이드](../web-network-captures.md) |
|
||||
| .NET 어셈블리 | 메타데이터, CIL 명령, 선언된 네이티브 의존성, 빌드 비교 | 별도 도구 불필요(정적 검사만); [관리 코드 가이드](../managed-code-analysis.md) |
|
||||
| Android APK | 매니페스트 선언, 클래스, 디컴파일된 메서드, 참조 | Linux/macOS/Windows x64의 헤드리스 JADX와 전체 JDK; [Android 가이드](../android-analysis.md) |
|
||||
| Android 기기 | 기기, 패키지, 프로세스, 로그, 덤프, 화면 캡처, APK/파일 전송 | 호출자 제공 adb(에뮬레이터 또는 실제 기기); [ADB 가이드](../adb-device-analysis.md) |
|
||||
| JEB 프로젝트 | 엔진 기반 프로젝트 유닛, 아티팩트 해시, 타입/메서드 의사코드 | 호출자가 시작하고 MCP를 제공하는 JEB 클라이언트(REA_JEB_MCP_URL로 선택); [JEB 가이드](../jeb-analysis.md) |
|
||||
| Android 리소스 | 디코딩된 매니페스트, 문자열 테이블, 로캘, 버전 정보 | 호출자 제공 Apktool 런처; [리소스 가이드](../apktool-resource-analysis.md) |
|
||||
| Flutter 앱 | Dart 스냅샷 해시, ABI별 라이브러리 해시, 엔진 빌드 출처 | 없음 (정적 APK 파싱); [Flutter 가이드](../flutter-analysis.md) |
|
||||
| 펌웨어 | 영역, 추출 결과, 네이티브 분석으로 전달할 정보 | Linux의 Binwalk / Unblob; [펌웨어 가이드](../firmware-analysis.md) |
|
||||
| 기록된 패킷 | 선택 필드, 반복 값, 필터 및 디코딩 규칙 | Linux: TShark 4.x + prlimit; [캡처 가이드](../packet-captures.md) |
|
||||
| 패킷 요약 | 엔드포인트, DNS, TLS SNI, 프로토콜 분포, HTTP 요청 | 호출자 제공 tshark 및 capinfos(Linux에서 검증됨); [pcap 가이드](../pcap-analysis.md) |
|
||||
| 기록된 논리 신호 | 디코더 주석, 샘플 범위, 진단 정보 | Linux: sigrok-cli 0.7.2 + prlimit; [신호 기록 가이드](../signal-recordings.md) |
|
||||
| 패키지 및 리소스 | 파일 목록, 다이제스트, plist, Apple 번들 구조, 추출한 리소스 | [아티팩트 및 JavaScript 가이드](../javascript-artifact-reconstruction.md), [Apple 애플리케이션](../apple-application-analysis.md) |
|
||||
| 프로세스 동작 | 터미널 출력, 상호작용, 종료 및 파일 시스템 관찰 결과, 실행 비교 | 네이티브 PTY를 지원하는 Linux/macOS; [프로세스 캡처](../process-capture.md) |
|
||||
|
||||
정적 JavaScript 및 .NET 검사는 제공된 파일을 읽으며 애플리케이션을 실행하지 않습니다. 런타임 캡처는 사용자의 권한으로 선택한 대상을 실행하거나 대상과 상호작용합니다. 각 런타임 가이드에서 그 영향을 설명합니다.
|
||||
|
||||
|
||||
@@ -155,6 +155,7 @@ REA-യ്ക്ക് Node.js 22.x (>=22.19), 24.x (>=24.11), അല്ലെ
|
||||
| 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) |
|
||||
| ഡെസ്ക്ടോപ്പ് ഇൻസ്റ്റലേഷനുകൾ | റൺടൈം സാധ്യതകൾ, തെളിവ് ഫയലുകൾ, ഘടക സൂചനകൾ, തുടർ നടപടികൾ | പരിധിയുള്ള ഡയറക്ടറി, ഹെഡർ പരിശോധന;[ഡെസ്ക്ടോപ്പ് ആപ്ലിക്കേഷനുകൾ](../desktop-applications.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) |
|
||||
|
||||
@@ -146,6 +146,7 @@ REA wymaga Node.js 22.x (>=22.19), 24.x (>=24.11) lub 26+ oraz npm. Dodatkowe na
|
||||
| Kod bajtowy EVM | Selektory wywołań, przesunięcia bajtowe, wywnioskowane argumenty i mutowalność | Lokalny plik z surowymi bajtami lub zapisem szesnastkowym w systemie Linux x64; [przewodnik EVM offline](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| Zarejestrowane awarie systemu Linux | Surowe rekordy note, rejestry/sygnały każdego zarejestrowanego wątku i opcjonalni kandydaci na mapowania | pwntools dostarczone przez użytkownika w systemie Linux x64; opcjonalnie GDB/pwndbg; [zarejestrowane awarie](../recorded-crashes.md) |
|
||||
| Instalacje desktopowe | Kandydaci środowisk uruchomieniowych, pliki źródłowe dowodów, komponenty i dalsze kroki | Ograniczona inspekcja katalogów i nagłówków;[Aplikacje desktopowe](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Moduły, importy, mapy źródeł, trasy, IPC i powiązania z natywnymi dodatkami | Node.js i npm; [analiza aplikacji](../javascript-application-workflows.md) |
|
||||
| Strony internetowe | Struktura strony, skrypty, obserwacje sieciowe i zamówione zrzuty ekranu | Przeglądarka z rodziny Chrome; [analiza w przeglądarce](../browser-observation.md) |
|
||||
| Zapisane przechwycenia ruchu sieciowego | Żądania, odpowiedzi, dostępne dane przesyłane i lokalizacje źródłowe | HAR; mitmdump w systemie Linux dla natywnych przechwyceń mitmproxy; [przewodnik przechwytywania](../web-network-captures.md) |
|
||||
|
||||
@@ -146,6 +146,7 @@ O REA requer Node.js 22.x (>=22.19), 24.x (>=24.11) ou 26+, além do npm. As fer
|
||||
| Bytecode EVM | Seletores de despacho, offsets de bytes, argumentos inferidos e mutabilidade | Entrada local com bytes brutos ou representação hexadecimal no Linux x64; [guia de EVM sem execução](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| Falhas do Linux registradas | Registros note brutos, registradores/sinais de cada thread registrada e candidatos opcionais a mapeamentos | pwntools fornecido pelo chamador no Linux x64; GDB/pwndbg opcionais; [falhas registradas](../recorded-crashes.md) |
|
||||
| Instalações desktop | Ambientes candidatos, arquivos de evidência, componentes e próximos passos | Inspeção limitada de diretórios e cabeçalhos;[Aplicações desktop](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Módulos, importações, source maps, rotas, IPC e relações com complementos nativos | Node.js e npm; [análise de aplicativos](../javascript-application-workflows.md) |
|
||||
| Sites | Estrutura da página, scripts, observações de rede e capturas de tela solicitadas | Um navegador da família Chrome; [análise do navegador](../browser-observation.md) |
|
||||
| Capturas de rede salvas | Requisições, respostas, payloads acessíveis e localizações de origem | HAR; mitmdump no Linux para capturas no formato nativo do mitmproxy; [guia de capturas](../web-network-captures.md) |
|
||||
|
||||
+23
-22
@@ -139,28 +139,29 @@ npx rea-agents@latest setup
|
||||
|
||||
REA требует Node.js 22.x (>=22.19), 24.x (>=24.11) или 26+, а также npm. Дополнительные инструменты и поддерживаемые системы зависят от цели:
|
||||
|
||||
| Цель | Что возвращает REA | Требования и руководство |
|
||||
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Нативные бинарные файлы | Псевдокод, ассемблер, строки, символы, вызовы и ссылки | Hopper, Ghidra или IDA; [нативный анализ](../native-investigation.md) |
|
||||
| Структура ELF без запуска | Секции, сегменты, исходные символы/релокации и возможные механизмы защиты по данным статического анализа | pwntools, предоставленный вызывающей стороной, в Linux x64; [диагностика бинарных файлов](../binary-diagnostics.md) |
|
||||
| Байт-код EVM | Селекторы диспетчеризации, смещения байтов, предполагаемые аргументы и изменяемость состояния | Локальные необработанные байты или шестнадцатеричное представление в Linux x64; [руководство по EVM без запуска](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| Записанные сбои Linux | Необработанные записи note, регистры/сигналы каждого записанного потока и необязательные кандидаты сопоставления с памятью | pwntools, предоставленный вызывающей стороной, в Linux x64; необязательные GDB/pwndbg; [записанные сбои](../recorded-crashes.md) |
|
||||
| JavaScript / Electron | Модули, импорты, карты исходного кода, маршруты, IPC и связи с нативными дополнениями | Node.js и npm; [анализ приложений](../javascript-application-workflows.md) |
|
||||
| Сайты | Структура страницы, скрипты, наблюдения за сетью и запрошенные снимки экрана | Браузер семейства Chrome; [анализ браузера](../browser-observation.md) |
|
||||
| Сохранённые сетевые захваты | Запросы, ответы, доступные полезные данные и позиции в источнике | HAR; mitmdump в Linux для захватов в собственном формате mitmproxy; [руководство по сетевым захватам](../web-network-captures.md) |
|
||||
| Сборки .NET | Метаданные, инструкции CIL, объявленные нативные зависимости и сравнения сборок | Статическое исследование; [руководство по управляемому коду](../managed-code-analysis.md) |
|
||||
| Android APK | Объявления манифеста, классы, декомпилированные методы и ссылки | JADX без графического интерфейса и полный JDK в Linux/macOS/Windows x64; [руководство по Android](../android-analysis.md) |
|
||||
| Устройства Android | Устройства, пакеты, процессы, журналы, дампы, снимки экрана и передача APK/файлов | adb, предоставленный вызывающей стороной (эмулятор или устройство); [руководство по ADB](../adb-device-analysis.md) |
|
||||
| Проекты JEB | Единицы проекта от движка, дайджесты артефактов и псевдокод типов/методов | Запущенный вызывающей стороной клиент JEB с интерфейсом MCP, выбирается через REA_JEB_MCP_URL; [руководство по JEB](../jeb-analysis.md) |
|
||||
| Ресурсы Android | Декодированный манифест, таблицы строк, языки и сведения о версии | Запуск Apktool, предоставленный вызывающей стороной; [руководство по ресурсам](../apktool-resource-analysis.md) |
|
||||
| Приложения Flutter | Хеш снапшота Dart, дайджесты библиотек по ABI и происхождение сборки движка | Не требуется (статический разбор APK); [руководство по Flutter](../flutter-analysis.md) |
|
||||
| Прошивки | Области, результаты извлечения и передача в нативный анализ | Binwalk / Unblob в Linux; [руководство по прошивкам](../firmware-analysis.md) |
|
||||
| Записанные пакеты | Выбранные поля, повторяющиеся значения, фильтры и правила декодирования | Linux: TShark 4.x + prlimit; [руководство по захватам](../packet-captures.md) |
|
||||
| Захват пакетов | Конечные точки, DNS, TLS SNI, распределение протоколов и HTTP-запросы | Предоставленные вызывающей стороной tshark и capinfos; [руководство по pcap](../pcap-analysis.md) |
|
||||
| Записанные логические сигналы | Аннотации декодера, диапазоны отсчётов и диагностика | Linux: sigrok-cli 0.7.2 + prlimit; [руководство по сигналам](../signal-recordings.md) |
|
||||
| Пакеты и ресурсы | Списки файлов, хеши, plist, структура пакетов Apple и извлечённые ресурсы | [Руководство по артефактам и JavaScript](../javascript-artifact-reconstruction.md), [приложения Apple](../apple-application-analysis.md) |
|
||||
| Поведение процессов | Вывод терминала, взаимодействия, наблюдения за завершением и файловой системой, сравнения запусков | Linux/macOS с нативным PTY; [захват процессов](../process-capture.md) |
|
||||
| Цель | Что возвращает REA | Требования и руководство |
|
||||
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Нативные бинарные файлы | Псевдокод, ассемблер, строки, символы, вызовы и ссылки | Hopper, Ghidra или IDA; [нативный анализ](../native-investigation.md) |
|
||||
| Структура ELF без запуска | Секции, сегменты, исходные символы/релокации и возможные механизмы защиты по данным статического анализа | pwntools, предоставленный вызывающей стороной, в Linux x64; [диагностика бинарных файлов](../binary-diagnostics.md) |
|
||||
| Байт-код EVM | Селекторы диспетчеризации, смещения байтов, предполагаемые аргументы и изменяемость состояния | Локальные необработанные байты или шестнадцатеричное представление в Linux x64; [руководство по EVM без запуска](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| Записанные сбои Linux | Необработанные записи note, регистры/сигналы каждого записанного потока и необязательные кандидаты сопоставления с памятью | pwntools, предоставленный вызывающей стороной, в Linux x64; необязательные GDB/pwndbg; [записанные сбои](../recorded-crashes.md) |
|
||||
| Установленные настольные приложения | Возможные среды выполнения, файлы с доказательствами, компоненты и дальнейшие шаги | Ограниченная проверка каталогов и заголовков;[Настольные приложения](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Модули, импорты, карты исходного кода, маршруты, IPC и связи с нативными дополнениями | Node.js и npm; [анализ приложений](../javascript-application-workflows.md) |
|
||||
| Сайты | Структура страницы, скрипты, наблюдения за сетью и запрошенные снимки экрана | Браузер семейства Chrome; [анализ браузера](../browser-observation.md) |
|
||||
| Сохранённые сетевые захваты | Запросы, ответы, доступные полезные данные и позиции в источнике | HAR; mitmdump в Linux для захватов в собственном формате mitmproxy; [руководство по сетевым захватам](../web-network-captures.md) |
|
||||
| Сборки .NET | Метаданные, инструкции CIL, объявленные нативные зависимости и сравнения сборок | Статическое исследование; [руководство по управляемому коду](../managed-code-analysis.md) |
|
||||
| Android APK | Объявления манифеста, классы, декомпилированные методы и ссылки | JADX без графического интерфейса и полный JDK в Linux/macOS/Windows x64; [руководство по Android](../android-analysis.md) |
|
||||
| Устройства Android | Устройства, пакеты, процессы, журналы, дампы, снимки экрана и передача APK/файлов | adb, предоставленный вызывающей стороной (эмулятор или устройство); [руководство по ADB](../adb-device-analysis.md) |
|
||||
| Проекты JEB | Единицы проекта от движка, дайджесты артефактов и псевдокод типов/методов | Запущенный вызывающей стороной клиент JEB с интерфейсом MCP, выбирается через REA_JEB_MCP_URL; [руководство по JEB](../jeb-analysis.md) |
|
||||
| Ресурсы Android | Декодированный манифест, таблицы строк, языки и сведения о версии | Запуск Apktool, предоставленный вызывающей стороной; [руководство по ресурсам](../apktool-resource-analysis.md) |
|
||||
| Приложения Flutter | Хеш снапшота Dart, дайджесты библиотек по ABI и происхождение сборки движка | Не требуется (статический разбор APK); [руководство по Flutter](../flutter-analysis.md) |
|
||||
| Прошивки | Области, результаты извлечения и передача в нативный анализ | Binwalk / Unblob в Linux; [руководство по прошивкам](../firmware-analysis.md) |
|
||||
| Записанные пакеты | Выбранные поля, повторяющиеся значения, фильтры и правила декодирования | Linux: TShark 4.x + prlimit; [руководство по захватам](../packet-captures.md) |
|
||||
| Захват пакетов | Конечные точки, DNS, TLS SNI, распределение протоколов и HTTP-запросы | Предоставленные вызывающей стороной tshark и capinfos; [руководство по pcap](../pcap-analysis.md) |
|
||||
| Записанные логические сигналы | Аннотации декодера, диапазоны отсчётов и диагностика | Linux: sigrok-cli 0.7.2 + prlimit; [руководство по сигналам](../signal-recordings.md) |
|
||||
| Пакеты и ресурсы | Списки файлов, хеши, plist, структура пакетов Apple и извлечённые ресурсы | [Руководство по артефактам и JavaScript](../javascript-artifact-reconstruction.md), [приложения Apple](../apple-application-analysis.md) |
|
||||
| Поведение процессов | Вывод терминала, взаимодействия, наблюдения за завершением и файловой системой, сравнения запусков | Linux/macOS с нативным PTY; [захват процессов](../process-capture.md) |
|
||||
|
||||
Статическое исследование JavaScript и .NET читает предоставленные файлы, не запуская приложение. Захват во время выполнения запускает выбранную цель или взаимодействует с ней с правами вашего пользователя; соответствующие руководства описывают последствия.
|
||||
|
||||
|
||||
@@ -146,6 +146,7 @@ REA ต้องใช้ Node.js 22.x (>=22.19), 24.x (>=24.11) หรือ 26
|
||||
| ไบต์โค้ด EVM | ตัวเลือกฟังก์ชันสำหรับการเรียก ออฟเซ็ตไบต์ อาร์กิวเมนต์และความสามารถในการเปลี่ยนสถานะที่อนุมานได้ | ไฟล์บนเครื่องที่มีไบต์ดิบหรือข้อมูลเลขฐานสิบหกบน Linux x64; [คู่มือ EVM แบบออฟไลน์](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| ข้อมูลการแครชของ Linux ที่บันทึกไว้ | ระเบียน note ดิบ รีจิสเตอร์/สัญญาณของทุกเธรดที่บันทึกไว้ และตัวเลือกการแมปเพิ่มเติม | pwntools ที่ผู้ใช้จัดเตรียมบน Linux x64; เลือกใช้ GDB/pwndbg เพิ่มเติมได้; [การแครชที่บันทึกไว้](../recorded-crashes.md) |
|
||||
| ไดเรกทอรีติดตั้งแอปเดสก์ท็อป | รันไทม์ที่เป็นไปได้ ไฟล์หลักฐาน ตัวบ่งชี้ส่วนประกอบ และขั้นตอนถัดไป | ตรวจสอบไดเรกทอรีและส่วนหัวโดยจำกัดขอบเขต;[แอปเดสก์ท็อป](../desktop-applications.md) |
|
||||
| JavaScript / Electron | โมดูล การนำเข้า ซอร์สแมป เส้นทาง IPC และความสัมพันธ์กับส่วนเสริมเนทีฟ | Node.js และ npm; [การวิเคราะห์แอปพลิเคชัน](../javascript-application-workflows.md) |
|
||||
| เว็บไซต์ | โครงสร้างหน้า สคริปต์ ข้อมูลเครือข่ายที่สังเกตได้ และภาพหน้าจอที่ร้องขอ | เบราว์เซอร์ในตระกูล Chrome; [การวิเคราะห์ผ่านเบราว์เซอร์](../browser-observation.md) |
|
||||
| ข้อมูลเครือข่ายที่บันทึกไว้ | คำขอ คำตอบ เพย์โหลดที่ปรากฏ และตำแหน่งต้นทาง | HAR; mitmdump บน Linux สำหรับข้อมูลที่บันทึกในรูปแบบเนทีฟของ mitmproxy; [คู่มือการบันทึกข้อมูล](../web-network-captures.md) |
|
||||
|
||||
@@ -151,6 +151,7 @@ REA; Node.js 22.x (>=22.19), 24.x (>=24.11) veya 26+ ve npm gerektirir. Ek araç
|
||||
| EVM bayt kodu | Yönlendirme seçicileri, bayt ofsetleri, çıkarımla belirlenen argümanlar ve durum değiştirilebilirliği | Linux x64 üzerinde yerel ham bayt/onaltılık girdi; [çevrimdışı EVM rehberi](../evm-bytecode.md) |
|
||||
| SQLite veritabanları | Tablolar, sütunlar, index'ler, view'lar, trigger'lar ve seçilen sıradan tablo satırları | Yerel database/WAL snapshot'ı; native SQLite sınırlarıyla Node.js 24.x >=24.15 veya 26+ |
|
||||
| Kaydedilmiş Linux çökmeleri | Ham note kayıtları, kaydedilen her iş parçacığının yazmaçları/sinyalleri ve isteğe bağlı eşleme adayları | Linux x64 üzerinde çağıranın sağladığı pwntools; isteğe bağlı GDB/pwndbg; [kaydedilmiş çökmeler](../recorded-crashes.md) |
|
||||
| Masaüstü kurulumları | Çalışma zamanı adayları, kanıt dosyaları, bileşen işaretleri ve sonraki adımlar | Sınırlı dizin ve başlık incelemesi;[Masaüstü uygulamaları](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Modüller, içe aktarmalar, kaynak haritaları, rotalar, IPC ve yerel eklenti ilişkileri | Node.js ve npm; [uygulama analizi](../javascript-application-workflows.md) |
|
||||
| Web siteleri | Sayfa yapısı, betikler, ağ gözlemleri ve istenen ekran görüntüleri | Chrome ailesinden bir tarayıcı; [tarayıcı analizi](../browser-observation.md) |
|
||||
| Kaydedilmiş ağ yakalamaları | İstekler, yanıtlar, erişilebilir yük içerikleri ve kaynak konumları | HAR veya Fiddler Everywhere SAZ; Linux'ta native mitmproxy için mitmdump; [ağ yakalama rehberi](../web-network-captures.md) |
|
||||
|
||||
@@ -146,6 +146,7 @@ REA потребує Node.js 22.x (>=22.19), 24.x (>=24.11) або 26+, а та
|
||||
| Байт-код EVM | Селектори диспетчеризації, зміщення байтів, виведені припущення про аргументи та змінюваність стану | Локальні необроблені байти або шістнадцяткове представлення у Linux x64; [посібник EVM без запуску](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| Записані аварійні завершення в Linux | Необроблені записи note, регістри/сигнали кожного записаного потоку й необов'язкові кандидати зіставлення пам'яті | pwntools, наданий стороною, що викликає інструмент, у Linux x64; необов'язкові GDB/pwndbg; [записані аварійні завершення](../recorded-crashes.md) |
|
||||
| Встановлені настільні програми | Можливі середовища виконання, файли доказів, компоненти та наступні кроки | Обмежена перевірка каталогів і заголовків;[Настільні програми](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Модулі, імпорти, карти вихідного коду, маршрути, IPC і зв'язки з нативними доповненнями | Node.js і npm; [аналіз застосунків](../javascript-application-workflows.md) |
|
||||
| Сайти | Структура сторінки, скрипти, мережеві спостереження й запитані знімки екрана | Браузер сімейства Chrome; [аналіз браузера](../browser-observation.md) |
|
||||
| Збережені мережеві захоплення | Запити, відповіді, доступні корисні дані та місця в джерелі | HAR; mitmdump у Linux для захоплень у власному форматі mitmproxy; [посібник мережевих захоплень](../web-network-captures.md) |
|
||||
|
||||
+23
-22
@@ -139,28 +139,29 @@ Tác nhân gọi REA qua MCP để kiểm tra mục tiêu và lần theo mã li
|
||||
|
||||
REA yêu cầu Node.js 22.x (>=22.19), 24.x (>=24.11) hoặc 26+, cùng với npm. Công cụ bổ sung và nền tảng chạy REA được hỗ trợ tùy thuộc vào mục tiêu:
|
||||
|
||||
| Mục tiêu | REA trả về gì | Yêu cầu và hướng dẫn |
|
||||
| ----------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Tệp nhị phân mã máy | Mã giả, hợp ngữ, chuỗi, ký hiệu, lời gọi và tham chiếu | Hopper, Ghidra hoặc IDA; [phân tích mã máy](../native-investigation.md) |
|
||||
| Bố cục ELF ngoại tuyến | Các section, segment, ký hiệu/thông tin tái định vị gốc và các biện pháp giảm thiểu có thể có từ phân tích tĩnh | pwntools do bên gọi cung cấp trên Linux x64; [chẩn đoán tệp nhị phân](../binary-diagnostics.md) |
|
||||
| Bytecode EVM | Bộ chọn điều phối, độ lệch byte, tham số suy luận và khả năng thay đổi trạng thái | Đầu vào cục bộ chứa byte thô hoặc dạng thập lục phân trên Linux x64; [hướng dẫn EVM ngoại tuyến](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| Sự cố Linux đã ghi lại | Bản ghi note thô, thanh ghi/tín hiệu của mọi luồng được ghi lại và các ứng viên ánh xạ tùy chọn | pwntools do bên gọi cung cấp trên Linux x64; GDB/pwndbg tùy chọn; [sự cố đã ghi lại](../recorded-crashes.md) |
|
||||
| JavaScript / Electron | Mô-đun, các khai báo import, source map, tuyến, IPC và quan hệ với tiện ích mở rộng mã máy | Node.js và npm; [phân tích ứng dụng](../javascript-application-workflows.md) |
|
||||
| Trang web | Cấu trúc trang, script, quan sát mạng và ảnh chụp màn hình được yêu cầu | Trình duyệt thuộc họ Chrome; [phân tích trình duyệt](../browser-observation.md) |
|
||||
| Bản ghi lưu lượng mạng đã lưu | Yêu cầu, phản hồi, payload có thể truy cập và vị trí nguồn | HAR; mitmdump trên Linux cho bản ghi định dạng gốc của mitmproxy; [hướng dẫn bản ghi mạng](../web-network-captures.md) |
|
||||
| Bắt gói tin | Điểm cuối, DNS, TLS SNI, phân bố giao thức và yêu cầu HTTP | tshark và capinfos do nơi gọi cung cấp; [hướng dẫn pcap](../pcap-analysis.md) |
|
||||
| Assembly .NET | Siêu dữ liệu, lệnh CIL, các phụ thuộc mã máy được khai báo và so sánh bản dựng | Kiểm tra tĩnh; [hướng dẫn mã được quản lý](../managed-code-analysis.md) |
|
||||
| APK Android | Khai báo manifest, lớp, phương thức được dịch ngược và tham chiếu | JADX không giao diện đồ họa và JDK đầy đủ trên Linux/macOS/Windows x64; [hướng dẫn Android](../android-analysis.md) |
|
||||
| Thiết bị Android | Thiết bị, gói, tiến trình, nhật ký, dump, ảnh chụp màn hình và truyền APK/tệp | adb do nơi gọi cung cấp (trình giả lập hoặc thiết bị); [hướng dẫn ADB](../adb-device-analysis.md) |
|
||||
| Dự án JEB | Đơn vị dự án từ engine, hash của artifact và mã giả của kiểu/phương thức | Một ứng dụng JEB do nơi gọi khởi động, phục vụ MCP, chọn bằng REA_JEB_MCP_URL; [hướng dẫn JEB](../jeb-analysis.md) |
|
||||
| Tài nguyên Android | Manifest đã giải mã, bảng chuỗi, ngôn ngữ và thông tin phiên bản | Trình khởi chạy Apktool do nơi gọi cung cấp; [hướng dẫn tài nguyên](../apktool-resource-analysis.md) |
|
||||
| Ứng dụng Flutter | Băm snapshot Dart, hash thư viện theo ABI và nguồn gốc build engine | Không cần (phân tích APK tĩnh); [hướng dẫn Flutter](../flutter-analysis.md) |
|
||||
| Phần sụn | Các vùng, kết quả trích xuất và dữ liệu chuyển sang phân tích mã máy | Binwalk / Unblob trên Linux; [hướng dẫn phần sụn](../firmware-analysis.md) |
|
||||
| Gói tin đã ghi | Trường được chọn, giá trị lặp, bộ lọc và quy tắc giải mã | Linux: TShark 4.x + prlimit; [hướng dẫn bản ghi gói tin](../packet-captures.md) |
|
||||
| Tín hiệu logic đã ghi | Chú thích bộ giải mã, phạm vi mẫu và chẩn đoán | Linux: sigrok-cli 0.7.2 + prlimit; [hướng dẫn tín hiệu](../signal-recordings.md) |
|
||||
| Gói và tài nguyên | Danh sách tệp, giá trị băm, plist, cấu trúc bundle Apple và tài nguyên được trích xuất | [Hướng dẫn hiện vật phần mềm và JavaScript](../javascript-artifact-reconstruction.md), [ứng dụng Apple](../apple-application-analysis.md) |
|
||||
| Hành vi tiến trình | Đầu ra terminal, tương tác, quan sát khi thoát và hệ thống tệp, cùng so sánh các lần chạy | Linux/macOS có PTY gốc; [ghi lại tiến trình](../process-capture.md) |
|
||||
| Mục tiêu | REA trả về gì | Yêu cầu và hướng dẫn |
|
||||
| --------------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Tệp nhị phân mã máy | Mã giả, hợp ngữ, chuỗi, ký hiệu, lời gọi và tham chiếu | Hopper, Ghidra hoặc IDA; [phân tích mã máy](../native-investigation.md) |
|
||||
| Bố cục ELF ngoại tuyến | Các section, segment, ký hiệu/thông tin tái định vị gốc và các biện pháp giảm thiểu có thể có từ phân tích tĩnh | pwntools do bên gọi cung cấp trên Linux x64; [chẩn đoán tệp nhị phân](../binary-diagnostics.md) |
|
||||
| Bytecode EVM | Bộ chọn điều phối, độ lệch byte, tham số suy luận và khả năng thay đổi trạng thái | Đầu vào cục bộ chứa byte thô hoặc dạng thập lục phân trên Linux x64; [hướng dẫn EVM ngoại tuyến](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| Sự cố Linux đã ghi lại | Bản ghi note thô, thanh ghi/tín hiệu của mọi luồng được ghi lại và các ứng viên ánh xạ tùy chọn | pwntools do bên gọi cung cấp trên Linux x64; GDB/pwndbg tùy chọn; [sự cố đã ghi lại](../recorded-crashes.md) |
|
||||
| Thư mục cài đặt ứng dụng máy tính | Các runtime có khả năng được dùng, tệp bằng chứng, dấu hiệu thành phần và bước tiếp theo | Kiểm tra thư mục và phần đầu tệp có giới hạn;[Ứng dụng máy tính](../desktop-applications.md) |
|
||||
| JavaScript / Electron | Mô-đun, các khai báo import, source map, tuyến, IPC và quan hệ với tiện ích mở rộng mã máy | Node.js và npm; [phân tích ứng dụng](../javascript-application-workflows.md) |
|
||||
| Trang web | Cấu trúc trang, script, quan sát mạng và ảnh chụp màn hình được yêu cầu | Trình duyệt thuộc họ Chrome; [phân tích trình duyệt](../browser-observation.md) |
|
||||
| Bản ghi lưu lượng mạng đã lưu | Yêu cầu, phản hồi, payload có thể truy cập và vị trí nguồn | HAR; mitmdump trên Linux cho bản ghi định dạng gốc của mitmproxy; [hướng dẫn bản ghi mạng](../web-network-captures.md) |
|
||||
| Bắt gói tin | Điểm cuối, DNS, TLS SNI, phân bố giao thức và yêu cầu HTTP | tshark và capinfos do nơi gọi cung cấp; [hướng dẫn pcap](../pcap-analysis.md) |
|
||||
| Assembly .NET | Siêu dữ liệu, lệnh CIL, các phụ thuộc mã máy được khai báo và so sánh bản dựng | Kiểm tra tĩnh; [hướng dẫn mã được quản lý](../managed-code-analysis.md) |
|
||||
| APK Android | Khai báo manifest, lớp, phương thức được dịch ngược và tham chiếu | JADX không giao diện đồ họa và JDK đầy đủ trên Linux/macOS/Windows x64; [hướng dẫn Android](../android-analysis.md) |
|
||||
| Thiết bị Android | Thiết bị, gói, tiến trình, nhật ký, dump, ảnh chụp màn hình và truyền APK/tệp | adb do nơi gọi cung cấp (trình giả lập hoặc thiết bị); [hướng dẫn ADB](../adb-device-analysis.md) |
|
||||
| Dự án JEB | Đơn vị dự án từ engine, hash của artifact và mã giả của kiểu/phương thức | Một ứng dụng JEB do nơi gọi khởi động, phục vụ MCP, chọn bằng REA_JEB_MCP_URL; [hướng dẫn JEB](../jeb-analysis.md) |
|
||||
| Tài nguyên Android | Manifest đã giải mã, bảng chuỗi, ngôn ngữ và thông tin phiên bản | Trình khởi chạy Apktool do nơi gọi cung cấp; [hướng dẫn tài nguyên](../apktool-resource-analysis.md) |
|
||||
| Ứng dụng Flutter | Băm snapshot Dart, hash thư viện theo ABI và nguồn gốc build engine | Không cần (phân tích APK tĩnh); [hướng dẫn Flutter](../flutter-analysis.md) |
|
||||
| Phần sụn | Các vùng, kết quả trích xuất và dữ liệu chuyển sang phân tích mã máy | Binwalk / Unblob trên Linux; [hướng dẫn phần sụn](../firmware-analysis.md) |
|
||||
| Gói tin đã ghi | Trường được chọn, giá trị lặp, bộ lọc và quy tắc giải mã | Linux: TShark 4.x + prlimit; [hướng dẫn bản ghi gói tin](../packet-captures.md) |
|
||||
| Tín hiệu logic đã ghi | Chú thích bộ giải mã, phạm vi mẫu và chẩn đoán | Linux: sigrok-cli 0.7.2 + prlimit; [hướng dẫn tín hiệu](../signal-recordings.md) |
|
||||
| Gói và tài nguyên | Danh sách tệp, giá trị băm, plist, cấu trúc bundle Apple và tài nguyên được trích xuất | [Hướng dẫn hiện vật phần mềm và JavaScript](../javascript-artifact-reconstruction.md), [ứng dụng Apple](../apple-application-analysis.md) |
|
||||
| Hành vi tiến trình | Đầu ra terminal, tương tác, quan sát khi thoát và hệ thống tệp, cùng so sánh các lần chạy | Linux/macOS có PTY gốc; [ghi lại tiến trình](../process-capture.md) |
|
||||
|
||||
Kiểm tra tĩnh JavaScript và .NET đọc các tệp được cung cấp mà không chạy ứng dụng. Ghi lại khi chạy sẽ khởi chạy hoặc tương tác với mục tiêu đã chọn bằng quyền người dùng của bạn; từng hướng dẫn mô tả tác động của nó.
|
||||
|
||||
|
||||
@@ -150,6 +150,7 @@ REA 需要 Node.js 22.x(>=22.19)、24.x(>=24.11)或 26+,以及 npm。
|
||||
| EVM 位元組碼 | 分派選擇器、位元組偏移、推斷的參數和狀態可變性 | Linux x64 上的本地原始位元組/十六進位輸入載體;[離線 EVM 指南](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| 已記錄的 Linux 崩潰 | 原始 note 記錄、每個已記錄執行緒的暫存器/訊號及可選的對映候選項 | Linux x64 上由呼叫方提供的 pwntools;可選的 GDB/pwndbg;[已記錄崩潰](../recorded-crashes.md) |
|
||||
| 桌面安裝目錄 | 執行階段候選、支持檔案、元件標記與後續操作 | 有界的目錄與檔頭檢查;[桌面應用](../desktop-applications.md) |
|
||||
| JavaScript / Electron | 模組、匯入、source map、路由、IPC 和原生附加元件關係 | Node.js 和 npm;[應用分析](../javascript-application-workflows.md) |
|
||||
| 網站 | 頁面結構、腳本、網路觀察結果和按請求獲取的螢幕擷取畫面 | Chrome 系瀏覽器;[瀏覽器分析](../browser-observation.md) |
|
||||
| 已保存的網路擷取 | 請求、回應、可訪問的酬載和來源位置 | HAR;原生 mitmproxy 擷取需要 Linux 上的 mitmdump;[擷取指南](../web-network-captures.md) |
|
||||
|
||||
@@ -150,6 +150,7 @@ REA 需要 Node.js 22.x(>=22.19)、24.x(>=24.11)或 26+,以及 npm。
|
||||
| EVM 字节码 | 分派选择器、字节偏移、推断的参数和状态可变性 | Linux x64 上的本地原始字节/十六进制输入载体;[离线 EVM 指南](../evm-bytecode.md) |
|
||||
| Rust binaries | Embedded cargo-auditable dependency list | [Rust binaries](../rust-binaries.md) |
|
||||
| 已记录的 Linux 崩溃 | 原始 note 记录、每个已记录线程的寄存器/信号及可选的映射候选项 | Linux x64 上由调用方提供的 pwntools;可选的 GDB/pwndbg;[已记录崩溃](../recorded-crashes.md) |
|
||||
| 桌面安装目录 | 运行时候选、支持文件、组件标记和后续操作 | 有界的目录与文件头检查;[桌面应用](../desktop-applications.md) |
|
||||
| JavaScript / Electron | 模块、导入、source map、路由、IPC 和原生扩展关系 | Node.js 和 npm;[应用分析](../javascript-application-workflows.md) |
|
||||
| 网站 | 页面结构、脚本、网络观察结果和按请求获取的截图 | Chrome 系浏览器;[浏览器分析](../browser-observation.md) |
|
||||
| 已保存的网络捕获 | 请求、响应、可访问的载荷和来源位置 | HAR;原生 mitmproxy 捕获需要 Linux 上的 mitmdump;[捕获指南](../web-network-captures.md) |
|
||||
|
||||
@@ -105,6 +105,27 @@ 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. The syscall trace also rejects access to the still-present
|
||||
owned live sentinel. An observed Node thread group must launch the exact ownership
|
||||
`ps` inventory before it can read that live process's `stat` or `status` for native
|
||||
identity/UID facts. Only traced `CLONE_THREAD` members share this allowance;
|
||||
exec-replaced threads, ordinary children, debugger and decoder processes cannot
|
||||
use it. The purpose of stat/status reads within the same Node thread group
|
||||
cannot be distinguished by strace. A second recording uses a valid
|
||||
historical PID above the host's assignable PID range and rejects resolved lookups,
|
||||
including Node `stat`/`status` reads, to detect historical-PID-driven access separately
|
||||
from live inventory. Descriptor paths come from strace's `-yy` decoding; exact
|
||||
PID-directory opens and relative accesses through those handles are rejected.
|
||||
Failed FD-relative pathname attempts are checked across syscall variants.
|
||||
Legacy relative pathname calls without dirfd/cwd evidence and symlink aliases
|
||||
remain unverified; the verification report declares these gaps rather than
|
||||
establishing their absence.
|
||||
One syscall/selector contract supplies trace selection, target interpretation
|
||||
and the report's explicit coverage catalog. Unlisted PID-directed syscalls remain
|
||||
unverified. The catalog includes PID-directed resource
|
||||
limits, scheduling, priority, process metadata, signals and pidfd controls.
|
||||
Self, group and UID selectors retain their syscall-defined meanings; unresolved
|
||||
targets cannot qualify. Coverage is reported once for all CLI/MCP traces and
|
||||
does not establish containment of every possible kernel interaction.
|
||||
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.
|
||||
@@ -45,10 +45,12 @@ close_gdb_session {"session_id":"…"}
|
||||
|
||||
## Rizin
|
||||
|
||||
`start_rizin_debug_session` and `rea debug-with-rizin` require an explicit `inferior_tty` on Linux. Rizin launches the target with stdout and stderr routed to that caller-selected TTY and `dbg.exitkills=false`; the target can therefore continue running and writing to the TTY after REA closes the debugger. This is a visible target output effect, and the caller is responsible for selecting a suitable terminal that remains available. Before closing, REA serializes after the active command, parses the structured `olj` descriptor list, detaches only a unique `ptrace://` descriptor, confirms its absence, reads back `dbg.exitkills=false`, and verifies `/proc/<pid>/fd/1` and `/proc/<pid>/fd/2` still identify the selected TTY by device and inode. Any malformed or ambiguous readback keeps the Rizin session owned for retry. Persistent Rizin debugger sessions are unavailable outside Linux because this verification depends on `/proc`.
|
||||
`start_rizin_debug_session` and `rea debug-with-rizin` require an explicit `inferior_tty` on Linux. Rizin launches the target with stdout and stderr routed to that caller-selected TTY and `dbg.exitkills=false`; the target can therefore continue running and writing to the TTY after REA closes the debugger. This is a visible target output effect, and the caller is responsible for selecting a suitable terminal that remains available. Before closing, REA serializes after the active command, parses the structured `olj` descriptor list, detaches only a unique `ptrace://` descriptor, confirms its absence, reads back `dbg.exitkills=false`, and verifies `/proc/<pid>/fd/1` and `/proc/<pid>/fd/2` still identify the selected TTY by device and inode. If a previously identified inferior exits after Rizin confirms detach, REA verifies that the original process has ended instead of requiring output descriptors from an exited process. Unresolved identity or ambiguous readback keeps the Rizin session owned for retry. Persistent Rizin debugger sessions are unavailable outside Linux because this verification depends on `/proc`.
|
||||
|
||||
Persistent command Evidence retains the caller-selected and resolved startup paths in `parameters.launch_provenance`. These paths describe launch provenance, not the current target after unrestricted commands. `rizin_debug_session_status.output_invalid_utf8` reports whether lossy frame decoding has been observed during the session. Command Evidence sets `output_invalid_utf8` only when loss was observed in frames collected for that command, including retained partial output; earlier startup or command frames do not mark later valid output as lossy.
|
||||
|
||||
Session status retains up to 64 recent protocol frames. Each history entry keeps a complete-character UTF-8 prefix of at most 16 KiB, plus a truncation marker when needed; `recent_output_truncated` reports omitted history. This history budget also accounts for replacement characters from invalid UTF-8. It does not shorten command Evidence, which has its own 16 MiB output budget.
|
||||
|
||||
```sh
|
||||
rea debug-with-rizin --inferior-tty /dev/pts/7 ./program dr
|
||||
```
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Implementation plan
|
||||
|
||||
1. Extend `src/application/environment/environmentSource.ts` so
|
||||
`loadEnvironmentSource` takes the reporting operation and retains the
|
||||
capture artifact's SHA-256, letting the export reuse the serve command's
|
||||
authentication and cross-check unchanged.
|
||||
2. Add `environmentBundle.ts` to plan the bundle from the source alone: the
|
||||
name rule, the Dockerfile for the registry and tarball package sources, the
|
||||
verifier, the smoke task, the README and the copy targets.
|
||||
3. Add `environmentExport.ts` to read each input once through the
|
||||
regular-file reader, refuse an occupied directory, a missing tarball or an
|
||||
unreadable input before writing, then write the planned files with
|
||||
exclusive creation and copy the authenticated bytes and the tarball
|
||||
byte-exact.
|
||||
4. Register `environment-export` in `src/cli/environmentCommands.ts` beside
|
||||
`environment-serve`, sharing the argument descriptions, and in
|
||||
`CLI_COMMANDS`.
|
||||
5. Add the `agentenv` scope, `real-agentenv.yml`, the package script and
|
||||
`scripts/verify-agentenv-environment.mjs`, wire `ci.yml` and extend the
|
||||
`ciRequiredLanes` and `ciChangeScope` tables.
|
||||
6. Add services, conformance and cli-boundary tests; document the command in
|
||||
`docs/cli.md`, the bundle and lane in `docs/agent-environments.md` and
|
||||
`docs/testing.md`; record the spec.
|
||||
|
||||
No new dependency is introduced; the CLI inventory gains one command.
|
||||
@@ -0,0 +1,116 @@
|
||||
# Export an environment manifest as an agent environment bundle
|
||||
|
||||
Issue: https://github.com/morluto/rea/issues/4933
|
||||
|
||||
## Outcome
|
||||
|
||||
`rea environment-export <manifest.json> --evidence <capture.json> <dir>
|
||||
[--package-tarball <path>] [--name <override>]` writes the manifest and
|
||||
capture pair that `environment-serve` serves as a bundle folder the agentenv
|
||||
framework runs with `agent-env run <dir>`: a README with provenance, an env
|
||||
folder whose Dockerfile installs this exact `rea-agents` version and serves the
|
||||
copied pair, a Python replay verifier and a smoke task. The hosted
|
||||
`real-agentenv` lane exports such a bundle from the source-owned Grafana-shaped
|
||||
capture through the built CLI, packs the checkout into the image and runs it
|
||||
with the pinned framework release on the runner's Docker. This increment
|
||||
completes the agent-environment work of the issue; serving came in the
|
||||
previous increment.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Read each of the two files once through the regular-file reader, parse it
|
||||
from those bytes and authenticate and cross-check the pair with
|
||||
`loadEnvironmentSource` as `environment-serve` does, so the bytes copied into
|
||||
the bundle are the bytes that were authenticated; every refusal reports
|
||||
operation `environment-export`, and a manifest projected from a different
|
||||
capture is refused before anything is written.
|
||||
- Refuse a bundle directory that exists and is not empty, or that names a file,
|
||||
with an `invalid_request` whose issue path is `directory`; an absent or empty
|
||||
directory is accepted. Write every file with the exclusive-create flag so
|
||||
nothing is ever overwritten.
|
||||
- Write `README.md`, `envs/<name>/Dockerfile`, `envs/<name>/env.toml`,
|
||||
`envs/<name>/manifest.json` and `envs/<name>/evidence.json` as byte-exact
|
||||
copies of the inputs (mode `0600` on POSIX; Windows retains inherited ACLs
|
||||
and does not provide an owner-only guarantee), `artifacts/check_<name>/verify.py` and
|
||||
`tasks/smoke.json`, where `<name>` defaults to the manifest's kebab-case name
|
||||
and `--name` must be a kebab-case label of at most 64 characters that is not a
|
||||
device name Windows reserves (`con`, `prn`, `aux`, `nul`, `com1`–`com9`,
|
||||
`lpt1`–`lpt9`), since the env folder could not be created there.
|
||||
- The Dockerfile reads `FROM node:24.18.0-bookworm-slim`, sets
|
||||
`MCP_HOST=0.0.0.0` and `MCP_PORT=18765`, installs Debian's `python3` for the
|
||||
framework's readiness probe, installs
|
||||
`rea-agents@<PRODUCT_IDENTITY.packageVersion>` from the registry or, with
|
||||
`--package-tarball`, copies the tarball as `rea-agents.tgz`, installs it
|
||||
from `/tmp/` and removes it in the same layer, copies the pair to `/env/`,
|
||||
switches to the image's `node` user
|
||||
and runs `rea environment-serve /env/manifest.json --evidence
|
||||
/env/evidence.json`.
|
||||
- `verify.py` mirrors the framework's echo example: `async def verify(mcp_url)`
|
||||
over `mcp.client.streamable_http`, lists the tools and checks the count
|
||||
equals the manifest's, calls the first `GET` tool without path parameters
|
||||
with no arguments and checks the replayed `status` equals the recorded status
|
||||
of that tool's first cited record, prints the description and returns
|
||||
`[{"id": "replay", "result": <bool>, "description": <both checks>}]`; with no
|
||||
such tool it checks the count only and says so.
|
||||
- `tasks/smoke.json` is `deploy_env` of `<name>` on the local sandbox followed
|
||||
by `env_outcome_verifier` with `file_artifact_id` `check_<name>` and
|
||||
`verifier_id` `replay`. The README records the capture artifact SHA-256, both
|
||||
Evidence ids, the manifest view digest, the tool count and the package
|
||||
source, states what replay does and does not establish, names the run
|
||||
command as `agent-env run .` from the bundle root (the folder it sits in,
|
||||
whatever the caller named it), and carries the rights note.
|
||||
- Print one JSON document with `status: "exported"`, the directory, name, tool
|
||||
count, both Evidence ids, the pinned `package_source` and the written files
|
||||
with sizes. Register the flat `environment-export` command in the CLI
|
||||
inventory; no MCP tool is added.
|
||||
- Add the `agentenv` CI scope, the reusable `real-agentenv.yml` workflow
|
||||
(`workflow_dispatch` and `workflow_call` with `build_cache_artifact`,
|
||||
`ubuntu-24.04`, SHA-pinned actions, `setup-python` 3.12, a virtual
|
||||
environment with `agentenv-framework==0.9.1334`, the runner's Docker, 30
|
||||
minutes), the `verify:agentenv:environment` script and
|
||||
`scripts/verify-agentenv-environment.mjs`, which requires absolute
|
||||
`REA_AGENT_ENV_COMMAND` and `REA_DOCKER_COMMAND` like every other lane
|
||||
verifier, runs `agent-env run <dir> --sandbox local` bounded to 15 minutes
|
||||
and resolves nothing from `PATH`; wire `ci.yml` at the four spots and extend
|
||||
the two CI guard tests.
|
||||
|
||||
## Acceptance and limits
|
||||
|
||||
Services tests cover the planned file set, the Dockerfile for both package
|
||||
sources, the verifier text, the smoke task, the README, the name override and
|
||||
its refusals, writing into an absent and an empty directory with byte-exact
|
||||
copies and correct sizes, the tarball copy, and the refusals of a non-empty
|
||||
directory, a file path, a missing tarball and an inline manifest. The
|
||||
conformance test pins the registry install to the `package.json` version. The
|
||||
cli-boundary test writes the folder from the fixture pair through the built
|
||||
CLI, refuses a non-empty directory leaving it unchanged, and refuses a
|
||||
mismatched pair without creating the directory. The CI guard tests enumerate
|
||||
the new consumer, route and classification cases. The local end-to-end run
|
||||
recorded in `verification.md` is the proof that one exported bundle runs under
|
||||
`agent-env run` with the smoke task passed and no container left behind.
|
||||
|
||||
The export copies bytes and never inspects the tarball; it does not build or
|
||||
run the image itself. Its writes are not transactional: a host failure after
|
||||
the first file leaves the partial folder for inspection, as `docs/cli.md`
|
||||
states. The verifier checks one replayed status and the tool count, not every
|
||||
tool. The lane passes `--sandbox local` as designed although
|
||||
`tasks/smoke.json` already pins `sandbox_type: local` on its deploy step, so
|
||||
the flag restates what the task fixes. The lane verifier imports the
|
||||
Grafana-shaped HAR builder from `tests/fixtures/historicalHar.ts` through
|
||||
Node's type stripping rather than carrying a second copy under `scripts/`; the
|
||||
`agentenv` scope lists that fixture so a change to it runs the lane. The lane
|
||||
proves one framework release on one hosted runner and claims nothing about
|
||||
any hosted platform. Three additions to the
|
||||
designed Dockerfile and file set were needed by the framework, each found by
|
||||
the local run: `MCP_PORT=18765` as the listener default for the port the
|
||||
framework maps, Debian's `python3` because the framework's local sandbox
|
||||
probes readiness with `python3 -c "import socket; …"` inside the container and
|
||||
the slim Node image has none, and `env.toml` naming the environment, because
|
||||
the framework otherwise reads the name from Python sources the Node image does
|
||||
not have.
|
||||
|
||||
## Deferred issue criteria
|
||||
|
||||
This increment closes the agent-environment scope of #4933 together with the
|
||||
previous three. Running the lane on the maintainer's CI and any follow-up on
|
||||
hosted platforms are outside this PR.
|
||||
@@ -0,0 +1,12 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Record the export contract, requirements and the lane.
|
||||
- [x] Thread the operation name and capture digest through the source loader.
|
||||
- [x] Plan the bundle: name rule, Dockerfile for both package sources, verifier, smoke task, README.
|
||||
- [x] Write the bundle with directory, tarball and input refusals and exclusive creation.
|
||||
- [x] Register the flat CLI command with described arguments and options.
|
||||
- [x] Add the agentenv scope, workflow, package script and verifier; wire ci.yml; extend the guard tests.
|
||||
- [x] Add services, conformance and cli-boundary coverage.
|
||||
- [x] Document the command, the bundle and the lane; record the spec.
|
||||
- [x] Run static, formatting, duplication, documentation and test gates and the local end-to-end run.
|
||||
- [x] Repair the review findings (README run command, absolute tool paths, single read, tarball cleanup) and re-run the gates and the end-to-end run.
|
||||
@@ -0,0 +1,362 @@
|
||||
# Verification
|
||||
|
||||
The initial author record below describes the original macOS tree. The Linux
|
||||
followup record at the end covers the integrated serve changes and subsequent
|
||||
repairs in this PR. The two records have different toolchains and cache states.
|
||||
|
||||
Verified base: 24574576e4fa74ff56eda965ae287555b7552c03, the tip of the environment-serve increment on fetched main b83b9caf (this increment builds on it).
|
||||
|
||||
The worktree carries the manifest-view, streamable-HTTP and serve increments
|
||||
as commits on upstream main b83b9caf; this increment's changes are uncommitted
|
||||
edits on top. Every check below ran as written on this tree after the review
|
||||
repairs; nothing is reported that did not run. Timings are macOS
|
||||
`/usr/bin/time -l` command accounting (elapsed, user, system seconds and
|
||||
maximum resident set size of the command and the children it waited for) on
|
||||
macOS 26.5.1 with Node 22.23.1, npm 12.0.2, Docker 29.5.3 and a Python 3.12.12
|
||||
virtual environment holding `agentenv-framework 0.9.1334`.
|
||||
|
||||
## Review findings addressed
|
||||
|
||||
An adversarial review of the first pass found two should-fix findings and
|
||||
seven nits; each was repaired at its owning boundary before the gates below
|
||||
ran.
|
||||
|
||||
- The bundle README told the reader to run `agent-env run <name>`, which only
|
||||
works from the bundle's parent when the folder happens to carry the env's
|
||||
name. The README now says `agent-env run .` from the bundle root and names
|
||||
the folder's path for any other working directory; the services test
|
||||
asserts the new block and the absence of the old command.
|
||||
- The lane verifier resolved `agent-env` and `docker` from `PATH` when the
|
||||
`REA_*` variables were unset. It now requires absolute
|
||||
`REA_AGENT_ENV_COMMAND` and `REA_DOCKER_COMMAND`, throwing the standard
|
||||
actionable message naming the npm script and that nothing is installed, and
|
||||
checks both paths exist; the workflow exports `REA_DOCKER_COMMAND` from
|
||||
`command -v docker` beside the virtual environment's `agent-env`;
|
||||
`docs/testing.md` names both variables and drops the `PATH` default. No
|
||||
Linux preflight was added, so the lane stays runnable on a macOS host.
|
||||
- The brittle `assert.doesNotMatch(stdout, /failed/)` is gone; exit code `0`
|
||||
and the `tasks/smoke.json v<n>: passed` summary already decide the case.
|
||||
- The inner `agent-env run` timeout is 15 minutes inside the 30-minute job,
|
||||
so a hung run ends with the verifier's failed receipt rather than the
|
||||
runner's kill; `docs/testing.md` says so.
|
||||
- The pair was read twice: parsed once for authentication and re-read from
|
||||
disk for the byte-exact copies, so a file replaced between the reads was
|
||||
copied unchecked. `readEnvironmentExportInputs` now reads each file once
|
||||
through the regular-file reader, parses the value from those bytes with the
|
||||
shared UTF-8 JSON parser, and the command passes the same buffers to the
|
||||
export, which copies them; the refusals of inline JSON, a directory and a
|
||||
file that is not JSON are typed by role, and the spec, plan and both guides
|
||||
state the single read.
|
||||
- The tarball Dockerfile left the 6 MB `rea-agents.tgz` in `/tmp` of the
|
||||
image; the install layer now removes it (`&& rm /tmp/rea-agents.tgz`),
|
||||
confirmed by listing `/tmp` in the built image below.
|
||||
- The verifier passes `--sandbox local` as the design specifies; the spec
|
||||
records that the task's `sandbox_type: local` already pins the provider and
|
||||
the flag restates it.
|
||||
- The verifier's import of the HAR builder from `tests/fixtures/` through
|
||||
Node's type stripping is kept and recorded in the spec's limits with the
|
||||
reason (no second copy under `scripts/`; the scope lists the fixture).
|
||||
- Writes are not transactional; `docs/cli.md` now states that a host failure
|
||||
after the first file leaves the partial folder for inspection and that the
|
||||
next attempt refuses the non-empty directory, and the spec's limits say so.
|
||||
|
||||
## Setup
|
||||
|
||||
`npm ci` completed (exit 0; 373 top-level entries). `/usr/bin/time -l npm run
|
||||
build` passed with 3 tasks successful in 7.60 s elapsed (14.81 s user, 2.69 s
|
||||
sys, 3013410816 bytes); `/usr/bin/time -l npm run test:prepare` passed with 6
|
||||
tasks successful, 3 cached, in 1.63 s (3.47 s user, 0.81 s sys, 400752640
|
||||
bytes).
|
||||
|
||||
## Static gates
|
||||
|
||||
Every gate ran on the final tree after the last source edit, sequentially:
|
||||
`npm run check:fast` (typecheck and lint with module boundaries; 2 tasks
|
||||
successful; 5.28 s elapsed, 23.22 s user, 2.93 s sys, 4564680704 bytes);
|
||||
`npm run format` rewrote nothing outside this increment's files ("Finished in
|
||||
635ms on 4077 files"), and `npm run format:check` passed ("All matched files
|
||||
use the correct format"; 0.87 s, 4.30 s user, 0.64 s sys, 224051200 bytes);
|
||||
`npx knip` reported no findings (3.49 s, 5.04 s user, 0.72 s sys, 805191680
|
||||
bytes); `npm run verify:test-discovery` reported "Discovered all 1597 test
|
||||
files exactly once" (0.55 s, 0.38 s user, 0.14 s sys, 141492224 bytes);
|
||||
`npm run check:ci:tasks` (typecheck, lint:built, format:check, knip,
|
||||
metadata:check, jscpd, scan:todos) passed with 8 tasks successful, lint "3764
|
||||
files, 0 errors, 1690 warnings" and jscpd "3171 files, 2587 clones, 3.34%
|
||||
duplicated lines" under the 3.5% threshold (4.89 s elapsed, 15.19 s user,
|
||||
3.86 s sys, 874627072 bytes); `npm run docs:check` passed including the README
|
||||
consistency test (6 tasks, 5 tests; 4.00 s elapsed, 3.86 s user, 0.94 s sys,
|
||||
290111488 bytes). `actionlint 1.7.7` (`-shellcheck= -pyflakes=`, from the
|
||||
review's scratch virtual environment) reported no findings on
|
||||
`.github/workflows/real-agentenv.yml` and `.github/workflows/ci.yml`. A
|
||||
case-insensitive grep for the forbidden words and marker comments over the 23
|
||||
new or modified files matched only the `scan:todos` script name in
|
||||
`package.json` and this file.
|
||||
|
||||
## Test projects
|
||||
|
||||
Source group: `/usr/bin/time -l npx vitest run --project services --project
|
||||
conformance` passed 122 files, 1524 tests in 11.08 s elapsed, 23.10 s user,
|
||||
2.24 s sys, 1631240192 bytes. The increment's files are
|
||||
`environmentBundle.test.ts` (7 tests: the planned file set and copy targets,
|
||||
the registry Dockerfile pinned to `PACKAGE_METADATA.version` with the
|
||||
readiness-probe layer, the tarball Dockerfile installing and removing
|
||||
`/tmp/rea-agents.tgz`, the verifier text naming `get_annotations` as the
|
||||
first parameterless `GET` tool with recorded status 200 of record 25, the
|
||||
smoke task, the README's provenance, `agent-env run .` block and rights note,
|
||||
the name override and six refused names), `environmentExport.test.ts` (6
|
||||
tests: the reader returning each file's bytes and parsed value, its refusals
|
||||
of inline JSON, a directory and malformed JSON by role, writing into an absent
|
||||
directory with the read bytes copied unchanged and reported sizes, the
|
||||
tarball copy, the refusals of a non-empty directory, a file path and a missing
|
||||
tarball with nothing written, and an existing empty directory accepted),
|
||||
`environmentSource.test.ts` (4 tests; the loader takes the reporting
|
||||
operation) and the conformance test `environmentBundlePackagePin.test.ts` (1
|
||||
test: the registry install line ends with `rea-agents@<package.json version>`
|
||||
and never uses `latest` or a range).
|
||||
|
||||
Built group after `npm run test:prepare`: `/usr/bin/time -l npx vitest run
|
||||
--project cli-boundary --project boundary` over `environmentExportCli` (3),
|
||||
`ciChangeScope` (228), `ciRequiredLanes` (17), `workflowActionPinning` (5),
|
||||
`ciCachePolicy` (6), `cliToolCoverage` (1), `cliStartupImports` (5) and
|
||||
`ciTestProjects` (11) passed 8 files, 276 tests in 432.80 s elapsed, 303.47 s
|
||||
user, 92.63 s sys, 422412288 bytes; `tests/boundary/filesystem/productCatalog`
|
||||
passed separately (10 tests, 5.32 s). The cli-boundary file writes the folder
|
||||
from the fixture pair through the built CLI and checks the seven written
|
||||
paths, the byte-exact copies and the Dockerfile's first line and `CMD`;
|
||||
refuses a non-empty directory leaving its one file in place; and refuses a
|
||||
mismatched pair without creating the directory.
|
||||
|
||||
## Local end-to-end run
|
||||
|
||||
With the built CLI in this worktree, from a fresh scratch directory:
|
||||
`inspect-web-network-capture grafana-like.har har --format json --json` wrote
|
||||
capture Evidence `ev_4ee4759b…` (28 records); `inspect-analysis-view` with the
|
||||
inline source and all three template rules wrote manifest Evidence
|
||||
`ev_6553a8e5…` naming that parent, `grafana-example`, 19 tools, view digest
|
||||
`8d1eb3ee…`. `npm pack --ignore-scripts` of the checkout produced
|
||||
`rea-agents-6.6.0.tgz` (6039238 bytes). `environment-export manifest.json
|
||||
--evidence capture.json <tmp>/grafana-example --package-tarball <tgz> --json`
|
||||
exited 0 and printed `status: exported`, `tool_count: 19`, `package_source:
|
||||
{kind: tarball}` and eight files: `README.md` (2076 bytes),
|
||||
`envs/grafana-example/env.toml` (37), `envs/grafana-example/Dockerfile` (441),
|
||||
`artifacts/check_grafana-example/verify.py` (1534), `tasks/smoke.json` (291),
|
||||
`envs/grafana-example/manifest.json` (15701),
|
||||
`envs/grafana-example/evidence.json` (58296) and
|
||||
`envs/grafana-example/rea-agents.tgz` (6039238); `cmp` confirmed the two
|
||||
Evidence copies and the tarball byte-identical to the inputs. The Dockerfile's
|
||||
install line reads `RUN npm install -g --ignore-scripts --no-fund --no-audit
|
||||
/tmp/rea-agents.tgz && rm /tmp/rea-agents.tgz`, and the README's run section
|
||||
is the `agent-env run .` block.
|
||||
|
||||
`/usr/bin/time -l agent-env run <tmp>/grafana-example` exited 0 in 43.66 s
|
||||
elapsed (5.19 s user, 3.26 s sys, 129318912 bytes), output
|
||||
|
||||
```text
|
||||
[tasks/smoke.json] step 1/2 env done in 25.1s
|
||||
[tasks/smoke.json] step 2/2 replay (env_outcome_verifier)
|
||||
replay: listed 19 tools, expected 19; get_annotations replayed status 200, recorded 200
|
||||
[tasks/smoke.json] passed in 25.1s
|
||||
|
||||
Tasks:
|
||||
tasks/smoke.json v1: passed (replay: 1), 25.1s, instance @local/…/grafana-example/smoke-nwvvojs7
|
||||
|
||||
Tore down 1 sandbox.
|
||||
```
|
||||
|
||||
`docker ps -aq` before and after the run listed the same three pre-existing
|
||||
containers (a Grafana container, an exited container and the framework's
|
||||
registry from earlier sessions, unrelated to this run); no container was
|
||||
added, and the image count rose from 19 to 20 for the built env image
|
||||
`5a5b3bf62aae`. `docker run --rm --entrypoint sh 5a5b3bf62aae -c 'ls -la
|
||||
/tmp; rea --version; id -un'` listed only `node-compile-cache` in `/tmp` (no
|
||||
`rea-agents.tgz`), printed `6.6.0` and ran as `node`.
|
||||
|
||||
`REA_AGENT_ENV_COMMAND=<venv>/bin/agent-env REA_DOCKER_COMMAND=$(command -v
|
||||
docker) /usr/bin/time -l node scripts/verify-agentenv-environment.mjs` exited
|
||||
0 in 37.24 s elapsed (10.42 s user, 4.45 s sys, 268533760 bytes) and printed
|
||||
the receipt `status: passed`, `public_cases: 5`, `upstream: agentenv-framework
|
||||
0.9.1334`, `runner: darwin arm64; host Docker`, `docker_before: {containers:
|
||||
3, images: 20}`, `agent_env_run: {exit_code: 0, seconds: 32}` with the same
|
||||
passed summary in its stdout tail, `docker_after: {containers: 3, images: 21,
|
||||
new_containers: []}` and `process_lineage.status: verified` with no
|
||||
descendants. The first hosted run of the lane, on a runner with no prior
|
||||
framework state, created the framework's image-store helper `agentenv-registry`
|
||||
(`registry:2`, container `b2e047f4c6cd`) during `agent-env run`; the run itself
|
||||
passed (`exit_code: 0`, 354 s, `tasks/smoke.json v1: passed`, "Tore down 1
|
||||
sandbox.") but the first form of the leak check counted the helper and failed.
|
||||
The check now lists containers with their names, excludes
|
||||
`agentenv-registry[-<port>]` and reports it under `image_store_helpers`; the
|
||||
local receipt gained `image_store_helpers: []` because the helper already
|
||||
existed before the run here. Public ECR's anonymous pull quota refused the
|
||||
framework's `postgres:16-alpine` pull with `429 Too Many Requests, Data limit
|
||||
exceeded` on two hosted runs out of six (the second and, after the stack was
|
||||
rebuilt, the sixth); the verifier now retries the run once, after 90 seconds,
|
||||
when the failure carries that registry's signature, and records
|
||||
`attempts: 2` with `retried_for` in the receipt. The local receipt reports
|
||||
`attempts: 1`. Removing that helper (`docker rm -f
|
||||
agentenv-registry`) and running the verifier again reproduced the fresh-host
|
||||
path locally: the run recreated it and the receipt passed with
|
||||
`image_store_helpers: ["agentenv-registry"]`, `new_containers: []`
|
||||
(`agent_env_run: {exit_code: 0, seconds: 33}`). Without `REA_DOCKER_COMMAND` the verifier exited 1 before
|
||||
touching anything with "verify:agentenv:environment requires absolute
|
||||
REA_DOCKER_COMMAND; see docs/testing.md. No dependencies are installed by this
|
||||
verifier."; with a relative `REA_AGENT_ENV_COMMAND=agent-env` it exited 1 with
|
||||
the same sentence naming that variable.
|
||||
|
||||
Refusals through the built CLI: exporting into the already written bundle
|
||||
directory exited 1 with `invalid_request`, issue path `["directory"]` and the
|
||||
message "is not empty (4 entries); choose an absent or empty directory";
|
||||
exporting the manifest against the capture of one selected record
|
||||
(`--record 0`, `ev_3249d233…`) exited 1 with `invalid_request`, issue path
|
||||
`["evidence", "evidence_id"]` and "The manifest was projected from a
|
||||
different capture than the supplied evidence file.", creating no directory;
|
||||
a truncated `broken.json` as the evidence exited 1 with `invalid_request`,
|
||||
issue path `["evidence"]`, reason `invalid_format` and "broken.json is not
|
||||
valid JSON: Unexpected end of JSON input", creating no directory. An export
|
||||
without `--package-tarball` wrote a Dockerfile whose install line is
|
||||
`RUN npm install -g --ignore-scripts --no-fund --no-audit rea-agents@6.6.0`,
|
||||
the `package.json` version, with no tarball beside it; the tarball variant
|
||||
copies `rea-agents.tgz` and installs and removes `/tmp/rea-agents.tgz`.
|
||||
|
||||
## Independent review
|
||||
|
||||
The adversarial review of the first pass is the independent review of this
|
||||
increment; its findings and their repairs are listed above. One further
|
||||
observation surfaced while repairing them: the single-read helper routes the
|
||||
export's input refusals through the export module rather than the CLI's
|
||||
shared JSON-input parser, so the inline-JSON refusal is now a typed
|
||||
`invalid_request` on the `manifest` or `evidence` issue path instead of the
|
||||
parser's `input_path`/`input_reason` envelope; the cli-boundary tests did not
|
||||
depend on that envelope.
|
||||
|
||||
## Scope and measurement limits
|
||||
|
||||
The framework readiness probe, the port variable default and the `env.toml`
|
||||
name were found by running the bundle in the first pass, not by reading a
|
||||
published contract; they are recorded in the spec as the three additions to
|
||||
the designed file set. The local run and the verifier used the host's Docker
|
||||
with the framework's gateway and service-database images already built from
|
||||
earlier sessions, so the "first run also builds the infrastructure" path was
|
||||
not timed here. The verifier's container check compares `docker ps -aq`
|
||||
before and after; images built by the run remain by design and are only
|
||||
counted. `npm pack --json` has the npm 12 object shape on this host and the
|
||||
npm 11 array shape on the hosted runner; the verifier accepts both but only
|
||||
the former was exercised locally. The hosted `real-agentenv` workflow was not
|
||||
dispatched from this tree. The built projects ran on this host's Node 22.23.1
|
||||
rather than the pinned 24.18.0. The `/usr/bin/time -l` figures account for
|
||||
each command and the children it waited for; Docker's own work runs in the
|
||||
daemon and is outside them. Windows was not run.
|
||||
|
||||
## Linux followup verification (2026-10-19)
|
||||
|
||||
The original PR was updated from main after #4955 and #4975 merged. The
|
||||
followups retain the author's commits and portable-name checks, seal private
|
||||
JSON parser output once for shared immutable authentication/replay, reserve
|
||||
sealing work before reading, preflight the served catalog before exporting,
|
||||
translate changed-file failures by input role, defer export imports during CLI
|
||||
registration, and verify the supplied framework executable before attributing
|
||||
its results. The default `rea mcp` transport remains stdio.
|
||||
|
||||
Executed on Linux x64 with Node 24.18.0, npm 11.16.0, Docker 20.10.24,
|
||||
Python 3.12.13 and an isolated `agentenv-framework==0.9.1334` environment:
|
||||
|
||||
- `turbo run typecheck lint:built format:check docs:check:built knip metadata:check artifacts:skills --concurrency=1`: ten tasks passed, one cached, 289.315 s elapsed, 284.552 s CPU, 4450.18 MiB peak process-family RSS.
|
||||
- The focused source/compiled CLI/SDK/lifecycle/configuration run passed 399 tests in 24 files. A later run passed the updated 231 CI-selection cases, three executable-identity cases and ten product-catalog cases. The latter needed its generated catalog first; its initial missing-file failure was preparation, not a passing check.
|
||||
- Four previously recorded, unauthenticated real GitHub API responses were exported with the checkout tarball. The generated Dockerfile built unchanged. A fresh non-root container replayed every response twice through the official MCP SDK, matching body, status, Evidence ordinal/id and untrusted-content provenance. Discovery/card parity and tarball removal passed. The standalone test supplies `ENVIRONMENT_NAME` as the framework does. Runtime verification with the built image took 12.393 s / 6.002 s CPU / 313.11 MiB process-family RSS; image-build cost is excluded. A Docker stats sample was 250.5 MiB, not a measured container peak.
|
||||
- The production `verify-agentenv-environment.mjs` observed `agent-env 0.9.1334`, exported its 28-record source-owned Grafana-shaped fixture, deployed it with the real framework and passed `tasks/smoke.json`. The receipt reported five public cases, one sandbox torn down, no new task containers and no supervised descendants. This successful run used already prepared infrastructure: 141.697 s / 101.971 s CPU / 501.61 MiB process-family RSS. Earlier attempts failed because this host's proxy environment required optional SOCKS support and its default home sandbox directory was unwritable. Only the test process's proxy variables were cleared; supported `XDG_STATE_HOME` and `AGENT_ENV_LOCAL_SANDBOX_DIR` pointed into temporary storage. The shared Docker daemon was not reconfigured or restarted. The framework's persistent registry helper and images remain as its documented behavior.
|
||||
|
||||
A matched public `environment-serve`/SDK task compared the pre-followup serve
|
||||
build with the final implementation. Both used the same GitHub capture with
|
||||
1,000 synthetic HAR-extension items (each containing additional containers),
|
||||
one first call and twenty warm identical replays. Fixture generation was
|
||||
excluded. This is controlled stress data, not newly observed application data.
|
||||
Larger 25,000/75,000-item projection attempts returned the existing typed
|
||||
representation-budget refusal; that budget was not relaxed.
|
||||
|
||||
| Measurement | Before | After |
|
||||
| ----------------------- | ----------: | ----------: |
|
||||
| Cold listener startup | 2189.064 ms | 2862.860 ms |
|
||||
| First call | 76.950 ms | 45.265 ms |
|
||||
| Twenty warm calls | 324.119 ms | 267.132 ms |
|
||||
| Completed task elapsed | 3.229 s | 3.885 s |
|
||||
| Completed task CPU | 3.196 s | 3.827 s |
|
||||
| Peak process-family RSS | 466.43 MiB | 470.21 MiB |
|
||||
|
||||
These single completed samples do not establish an overall speed or memory
|
||||
improvement. Separate identity regressions establish that large reported JSON
|
||||
trees are shared rather than copied and that caller-owned mutable trees are
|
||||
not frozen. Token usage was not measured. CPU is child user+system time;
|
||||
process-family memory sums sampled descendant RSS every 50 ms and may count
|
||||
shared pages twice. Docker daemon/container CPU and peaks are outside that
|
||||
process-family measurement and remain unmeasured. Builds used one task at a
|
||||
time, one CPU and a soft 3 GiB Go limit; Docker builds were serialized and
|
||||
limited to one CPU/4 GiB, and the standalone replay container to one CPU/2 GiB.
|
||||
Windows execution and the published registry package were not tested in this
|
||||
followup; the checkout tarball proves the code before its release.
|
||||
|
||||
### Output ownership and final review followups
|
||||
|
||||
The output boundary now uses the existing `SafeOutputTree` owner instead of
|
||||
separate directory admission and pathname writes. It checks the admitted root,
|
||||
parents, file identities and complete entry inventory around publication, keeps
|
||||
failed-close authority, and preserves an existing empty caller root on rollback.
|
||||
The optional package is held as one regular-file descriptor before output
|
||||
writes, streamed with hash/readback verification, and checked for changes before
|
||||
release. POSIX file modes are set and verified as `0600` independently of umask;
|
||||
newly owned directories are `0700`. Existing root permissions remain caller owned.
|
||||
Windows privacy depends on the selected destination ACL; no owner-only Windows
|
||||
ACL guarantee or Windows runtime validation is claimed. Portable pathname
|
||||
operations still require a trusted destination and parents against concurrent
|
||||
replacement.
|
||||
|
||||
A compiled CLI counterexample under umask `0400` failed before the mode fix and
|
||||
succeeded afterward with every generated file readable at `0600` and exact
|
||||
Evidence bytes. Public CLI coverage also verifies that `symlink/..` retains its
|
||||
filesystem meaning for the destination and package path. Real-filesystem tests
|
||||
exercise destination replacement, foreign entries, parent symlinks, read-only
|
||||
permissions and admitted-tarball replacement. Controlled close failures exercise
|
||||
post-publication recovery and persistent retained ownership.
|
||||
|
||||
The inventory owner accepts canonically equivalent Unicode directory listings
|
||||
without collapsing distinct filenames: an NFC multiset comparison is followed
|
||||
by the original path identity checks. Tests simulate decomposed `readdir`
|
||||
spelling over a real owned file and create an extra distinct Linux decomposed
|
||||
foreign file. These establish the boundary behavior, not actual HFS+ execution.
|
||||
Manifest, Evidence and tarball descriptors use the shared resource scope's close
|
||||
retry. Persistent failures retain the canonical cleanup lease and produce a
|
||||
tagged `environment-export` error. The compiled JSON CLI regression injects a
|
||||
persistent manifest close failure before any destination write.
|
||||
|
||||
The framework verifier reuses `execNpm` and honors selected `npm_execpath`.
|
||||
Four process-boundary attribution cases pass, including a conflicting PATH npm.
|
||||
Those controlled executables verify attribution only.
|
||||
|
||||
After merging main `b4938aed5`, the generated Dockerfile built unchanged with the
|
||||
checkout package and replayed all four captured public GitHub API responses
|
||||
twice through the official MCP SDK in a fresh non-root container. Input bytes,
|
||||
status/body/Evidence/provenance, discovery/card parity and package removal passed.
|
||||
Warm base-image build plus pack/export/runtime verification: **40.699 s elapsed,
|
||||
8.274 s process CPU, 421.04 MiB peak process-family RSS**. The real pinned
|
||||
`agent-env 0.9.1334` verifier then passed all five public cases and the smoke task,
|
||||
tore down its sandbox, and reported no new task containers or supervised
|
||||
descendants: **156.895 s / 102.871 s CPU / 500.34 MiB**. Framework infrastructure
|
||||
was already prepared. The later Unicode, close-failure and npm-selection repairs
|
||||
have separate boundary regressions; they do not establish new real-platform
|
||||
coverage.
|
||||
|
||||
A matched export comparison before the main merge used the same manifest
|
||||
(6,416 bytes), capture (98,519 bytes) and checkout tarball (6,221,957 bytes), with
|
||||
all copied inputs byte-exact. Before/after the output-owner repair: **1.827/2.604
|
||||
s elapsed, 1.807/2.545 s CPU, 316.43/334.11 MiB** peak process-family RSS. These are
|
||||
single process samples with uncontrolled host filesystem cache; the additional
|
||||
ownership checks increased measured cost. They do not establish a speed or
|
||||
memory improvement.
|
||||
|
||||
All followup heavy work uses one CPU and one test/build task at a time. Docker
|
||||
builds are limited to one CPU/4 GiB, standalone runtime containers one CPU/2 GiB.
|
||||
Process-family RSS is sampled every 50 ms and may double-count shared pages.
|
||||
Docker daemon/container CPU and peak memory, agent tokens and fresh-framework
|
||||
infrastructure cost are unmeasured. The previously documented early catalog
|
||||
heap refusal is not represented as a passing run.
|
||||
|
||||
Final focused verification on the reviewed source passed **452 tests in 33 files** (including 232 CI-selection cases and the compiled CLI regressions): **161.276 s elapsed / 159.900 s CPU / 1242.43 MiB** peak process-family RSS. The final typecheck/build/lint gate passed: **218.427 s / 218.130 s CPU / 4449.45 MiB**.
|
||||
@@ -0,0 +1,36 @@
|
||||
# Implementation plan
|
||||
|
||||
1. Add `src/application/environment/environmentSource.ts` to authenticate the
|
||||
two Evidence files, require the manifest view kind and the capture
|
||||
operation, refuse a parent mismatch, and reduce every cited record to its
|
||||
request line, status, content type and retained response text through the
|
||||
slice-1 request-line reader, so the server holds only what replay returns.
|
||||
2. Add `environmentTools.ts` to map manifest tools to MCP tool definitions with
|
||||
one hand-built JSON Schema per tool that both `registerTool` (through the
|
||||
SDK's `fromJsonSchema`) and the card consume, and to build the card in the
|
||||
echo example's key order.
|
||||
3. Add `environmentReplay.ts` to score cited records by path-segment matches,
|
||||
then query matches, keep manifest order on ties, and shape the replay result
|
||||
with JSON-like parsing, UTF-8-safe truncation and the replay provenance
|
||||
block; export the slice-1 JSON-like predicate instead of restating it.
|
||||
4. Add `src/server/environment/serveEnvironment.ts` to build the `McpServer`
|
||||
whose handlers attach REA's target-content `_meta`, the card and `/agentenv`
|
||||
routes (the latter reading its body through the SDK's bounded reader), and
|
||||
to serve them through the slice-2 `serveStreamableHttp` with its `routes`
|
||||
and `onerror` seams; add `src/config/environmentName.ts` for
|
||||
`ENVIRONMENT_NAME`.
|
||||
5. Add `src/cli/environmentCommands.ts` registering `environment-serve` through
|
||||
`withCommandCancellation` and `logCliCommand`, resolving host and port with
|
||||
the slice-2 endpoint parser, reading files through `parseCliJsonInput`,
|
||||
importing the listener lazily, awaiting the cancellation signal or a
|
||||
listener failure and returning one `stopped` document or a typed error;
|
||||
register the name in `CLI_COMMANDS` and the lazy module in the startup
|
||||
import probe.
|
||||
6. Share the fixture pair through `tests/fixtures/environmentServe.ts` and the
|
||||
listener spawn through a generalised `startListeningProcess`; add services,
|
||||
adapters, mcp-boundary, cli-boundary and process-boundary tests.
|
||||
7. Document the command in `docs/cli.md`, add `docs/agent-environments.md` with
|
||||
its sidebar and index entries, and record the spec.
|
||||
|
||||
No new dependency is introduced. REA's tool contracts, catalog digests and the
|
||||
stdio transport are unchanged; the CLI inventory gains one command.
|
||||
@@ -0,0 +1,133 @@
|
||||
# Serve an environment manifest as a replay environment
|
||||
|
||||
Issue: https://github.com/morluto/rea/issues/4933
|
||||
|
||||
## Outcome
|
||||
|
||||
`rea environment-serve <manifest.json> --evidence <capture.json> [--host H]
|
||||
[--port N]` serves an environment manifest, projected by `inspect-analysis-view`
|
||||
from retained HAR capture Evidence, as an agent environment: an MCP server at
|
||||
`/mcp` with one tool per manifest tool that replays the retained records the
|
||||
tool cites, the environment card at `/.well-known/agent-env.json` in the shape
|
||||
the agentenv framework's echo example publishes, and a `/agentenv` data plane
|
||||
that answers every JSON-RPC method as not found. Nothing is fetched from the
|
||||
recorded application; every answer is retained Evidence marked as target
|
||||
content. This increment adds the serve command only; exporting a bundle is a
|
||||
later increment.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Read both files as bounded JSON input, authenticate each as REA Evidence and
|
||||
require `inspect_analysis_view` Evidence of kind `environment-manifest` and
|
||||
complete `inspect_web_network_capture` Evidence; refuse any other pair with an
|
||||
`invalid_request` whose issue path names the offending file.
|
||||
- Forward caller cancellation to file reads and check startup boundaries before
|
||||
authentication/listener work; an already observed abort must not bind or
|
||||
announce a listener.
|
||||
- Refuse a manifest whose `parent_evidence_id` differs from the evidence file's
|
||||
`evidence_id` before binding any port; Evidence authentication covers the
|
||||
capture's records, so no further record-coverage refusal is defined.
|
||||
- Build an `McpServer` from the manifest alone: each tool's input schema lists
|
||||
every path parameter as a required string described by its segment index,
|
||||
rule and observed values, every observed query name as an optional string,
|
||||
and an optional `body` object for `POST`, `PUT` and `PATCH` (a query name
|
||||
that collides with a path parameter becomes `query_<name>`, a query name
|
||||
longer than 64 characters becomes `query_<n>`, and the body
|
||||
becomes `request_body` when a query parameter is named `body`), closed with
|
||||
`additionalProperties: false` so unknown arguments are refused; the
|
||||
description reads `METHOD template; replays retained records [ordinals];
|
||||
evidence ev_…`. The served catalog stays separate from REA's own tool
|
||||
contracts and digests.
|
||||
- Prefix tool descriptions with the shared captured-catalog warning so
|
||||
standalone card readers receive the trust notice before MCP initialization.
|
||||
- Bound each path-observation, body-key and cited-ordinal description sample to 1 KiB
|
||||
of UTF-8 whole values before joining them, with a separate omitted-count and
|
||||
manifest-Evidence notice. Keep complete arrays and replay semantics unchanged.
|
||||
- Bound captured method, path and query-name prose to UTF-8-safe display prefixes
|
||||
under the same budget, with an explicit full-value Evidence notice. Preserve
|
||||
complete lookup values; long path and query names use collision-safe aliases.
|
||||
- Parse a recorded query only when supplied query arguments can affect selection.
|
||||
- Replay by selecting, among the tool's cited records, the one whose path
|
||||
segments at the parameter indexes match the most supplied values, then the
|
||||
most of those segments equal to the value as written rather than only after
|
||||
percent-decoding, then the most supplied query values, keeping manifest order
|
||||
on ties; return `status`,
|
||||
`content_type`, `body` (parsed JSON for JSON-like content types with safe numeric conversion,
|
||||
else text; bodies over 256 KiB cut at a UTF-8 boundary with
|
||||
`truncated: true`) and `replay` with `record_ordinal`, `evidence_id`,
|
||||
`exact_match`, `recorded_path` and `recorded_query` (each cut to 1 KiB, with
|
||||
`recorded_line_truncated`; an explicit empty query is `?`), `body_recorded` (false
|
||||
when the capture kept no response body, so a `null` body is not mistaken for
|
||||
a recorded one), `body_encoding` (`base64` when the retained bytes are not
|
||||
UTF-8, so they are returned exactly rather than decoded lossily) and
|
||||
`truncated` (a large retained base64 body is decoded only up to that limit),
|
||||
as both
|
||||
structured content and text, with REA's existing target-content provenance
|
||||
marker in `_meta`.
|
||||
- Serve the card with exactly the echo example's keys and nesting: `name`
|
||||
(`ENVIRONMENT_NAME` from the environment snapshot, else the manifest name; a
|
||||
set but blank value is refused as `configuration_invalid`),
|
||||
`protocolVersion` `1.0`, `url` `/agentenv`, `preferredTransport` `JSONRPC`,
|
||||
`additionalInterfaces` `[{url: /mcp, transport: mcp}]`, `capabilities` with
|
||||
`extensions: null`, the tools and `operations: []`, and
|
||||
`children_environments: null`. Read the `/agentenv` body within the SDK's
|
||||
4 MiB bound and answer `413` with JSON-RPC `-32000` beyond it.
|
||||
- Resolve host and port as `rea mcp --http` does (flags, `MCP_HOST`/`MCP_PORT`,
|
||||
then `127.0.0.1` and port `0`), announce the bound URL as one pino JSON line
|
||||
on stderr, serve through the shared streamable HTTP listener, and run until
|
||||
SIGINT or SIGTERM only, then close, print one `stopped` document and exit
|
||||
`130` or `143` through the shared command cancellation handler; a signal
|
||||
during loading returns `cancelled` before binding; a listener
|
||||
failure after binding is logged and ends the command with a
|
||||
`resource_constraint` error and exit `1`.
|
||||
- Register the flat `environment-serve` command in the CLI inventory with
|
||||
described arguments and no MCP tool contract; keep the CLI wiring thin, the
|
||||
mapping and replay in the application layer and the server assembly in the
|
||||
server layer; load the listener lazily so the command catalog registers
|
||||
without it.
|
||||
|
||||
## Acceptance and limits
|
||||
|
||||
Services tests cover source loading and each refusal, tool mapping and schema
|
||||
derivation, the card shape, exact and best-effort selection, query matching,
|
||||
tie order, text and non-parsing bodies, truncation at a UTF-8 boundary and
|
||||
base64 content. Adapters tests byte-compare the served card with the echo card
|
||||
shape carrying the fixture tools, drive `/agentenv` including a body over the
|
||||
bound, and call tools over the SDK streamable HTTP client with provenance and
|
||||
the unknown-argument refusal asserted. The mcp-boundary test
|
||||
spawns the built CLI on a manifest and capture produced from the slice-1
|
||||
Grafana-shaped fixture and asserts tool count, an exact `get_users_by_id`
|
||||
replay with `body.login === "probe"`, an inexact replay, the card under
|
||||
`ENVIRONMENT_NAME` and the data-plane refusal; the cli-boundary test covers
|
||||
the mismatched-parent refusal, the missing `--evidence` usage error and an
|
||||
unreadable path; the process-boundary test asserts a `143` exit with a
|
||||
released port. Replay validates no supplied values, performs no network
|
||||
activity and establishes no authorization, response semantics or state
|
||||
transitions. One MCP client is served at a time, as the shared listener
|
||||
defines. Non-loopback binds relax the `Host` check and are not exercised; a
|
||||
listener `error` after binding is wired but not provoked by a test.
|
||||
|
||||
## Resource and cancellation boundaries
|
||||
|
||||
- Whole discovery admission reserves at most 1 MiB using six bytes per UTF-16
|
||||
unit plus schema overhead before retaining each schema property. Aggregate
|
||||
tools, arguments and selected environment identity participate; oversized
|
||||
catalogs fail with a non-retryable capacity error before bind. The calibration
|
||||
used 1500 query fields with eight concurrent discovery requests; compact wire
|
||||
size can be smaller than the conservative reservation.
|
||||
- Query matching scans selected names one field at a time; path scoring scans
|
||||
once and retains only supplied parameter coordinates. Native form-decoding
|
||||
semantics, malformed escape handling and complete captured facts are preserved.
|
||||
- Content-type display is bounded to 1 KiB and reports `content_type_truncated`;
|
||||
complete media-type semantics remain available to body interpretation.
|
||||
- Cancellation also owns DNS/listen startup; an observed abort closes the
|
||||
listener before its URL is announced.
|
||||
|
||||
## Deferred issue criteria
|
||||
|
||||
Exporting a manifest and its evidence as an agent-environment bundle with a
|
||||
verifier, and the hosted verification lane, are later independent increments
|
||||
of #4933. This PR must reference rather than close the whole issue.
|
||||
|
||||
Numeric data-plane IDs that lose value through JSON number conversion are invalid
|
||||
requests with null response IDs, rather than silently changed correlation values.
|
||||
@@ -0,0 +1,12 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Record the serve contract, requirements and deferred increments.
|
||||
- [x] Load and cross-check the manifest and capture Evidence with typed refusals.
|
||||
- [x] Map manifest tools to MCP input schemas and the environment card.
|
||||
- [x] Select and shape replayed records with exact-match, truncation and provenance.
|
||||
- [x] Assemble the MCP server, card and data-plane routes on the shared listener.
|
||||
- [x] Register the flat CLI command with host, port and environment-name resolution.
|
||||
- [x] Add services, adapters, mcp-boundary, cli-boundary and process-boundary coverage.
|
||||
- [x] Document the command and the agent environment page; record the spec.
|
||||
- [x] Run static, formatting, duplication, documentation and test gates and the
|
||||
built-CLI exchange with the Python MCP client.
|
||||
@@ -0,0 +1,245 @@
|
||||
# Verification
|
||||
|
||||
Verified base: cf4cc6faab0fb2c444ca054db8b98cb981bae6c9 (fetched main).
|
||||
|
||||
The worktree carries the manifest-view and streamable-HTTP increments as
|
||||
commits on that base; this increment's changes are uncommitted edits on top.
|
||||
Every check below ran as written on this tree in the review-response pass;
|
||||
nothing is reported that did not run. Timings are macOS `/usr/bin/time -l`
|
||||
command accounting (elapsed, user, system seconds and maximum resident set
|
||||
size of the command and the children it waited for).
|
||||
|
||||
## Review findings addressed
|
||||
|
||||
An adversarial review of the first pass found one should-fix and six nits;
|
||||
each was repaired at its owning boundary before the gates below ran.
|
||||
|
||||
- `POST /agentenv` read its body with `request.json()` on the streamed
|
||||
`node:http` body, so a 60 MB payload was buffered before the `-32700`
|
||||
answer. The route now reads through the SDK's `readRequestBody` with
|
||||
`DEFAULT_MAX_REQUEST_BODY_SIZE`, answering `413` with JSON-RPC `-32000`
|
||||
(`id: null`) before parsing, and parses within the bound through the
|
||||
domain's `safeParseJson`. The JSON-RPC error helper of the shared listener
|
||||
is exported as `jsonRpcErrorResponse` and reused rather than restated.
|
||||
- A set but blank `ENVIRONMENT_NAME` refuses with `configuration_invalid`,
|
||||
as a blank `MCP_HOST` does in the sibling parser; `docs/cli.md` and
|
||||
`docs/agent-environments.md` now say so, and the spec records the
|
||||
divergence from the design's `||` reading as deliberate.
|
||||
- The record-coverage refusal in `loadEnvironmentSource` was unreachable for
|
||||
authenticated pairs (the Evidence id is computed over `normalized_result`,
|
||||
so a matching parent id implies identical records, each with a request
|
||||
line by construction of the projector). The branch is gone: cited records
|
||||
are indexed unconditionally, a record without a request line is left out
|
||||
and the tool refuses at call time through the existing no-record path. The
|
||||
claim was removed from `docs/agent-environments.md` and `spec.md`, and the
|
||||
services test now asserts an empty manifest indexes nothing and a tampered
|
||||
capture is refused by authentication.
|
||||
- Every advertised input schema closes with `additionalProperties: false`,
|
||||
so an unknown argument is refused as invalid arguments instead of silently
|
||||
degrading to an omitted one. The agentenv framework's card consumers
|
||||
(`verify_env_card.py`, the gateway's `tool.inputSchema` pass-through and
|
||||
the spec-conformance normaliser) read `properties` and `required` only, so
|
||||
the extra key does not disturb them; the guide names it as a key the echo
|
||||
schema does not carry.
|
||||
- The tool description now reads `METHOD template; replays retained records
|
||||
[ordinals]; evidence ev_…`, as the design text specifies.
|
||||
- `serveEnvironment` passes an `onerror` to the shared listener that logs
|
||||
`{ error }` at `error` level and forwards to the caller; the command races
|
||||
the cancellation signal against that failure and, on a listener failure
|
||||
after binding, closes the listener and returns the same typed
|
||||
`resource_constraint` error the bind failure returns (exit `1`) instead of
|
||||
serving nothing. The two constructions share one `transportError` helper.
|
||||
- `tests/boundary/cli/cliStartupImports.test.ts` lists
|
||||
`dist/server/environment/serveEnvironment.js` as `environment-server` in
|
||||
`DEFERRED_IMPORTS`, so the `--help` probe would fail if the serve command
|
||||
ever imported the listener eagerly.
|
||||
|
||||
## Setup
|
||||
|
||||
`npm ci` exited 0 (540 packages added, 541 audited; 4.79 s elapsed, 3.46 s
|
||||
user, 7.34 s sys, 880803840 bytes). After the edits above, `npm run build`
|
||||
exited 0 (three turbo tasks; 4.61 s elapsed, 14.76 s user, 3.23 s sys,
|
||||
3053060096 bytes) and `npm run test:prepare` exited 0 (six tasks; 1.28 s
|
||||
elapsed, 2.94 s user, 0.67 s sys, 424935424 bytes), so every built-group run
|
||||
and built-CLI check below used a `dist/` carrying the revised serve command.
|
||||
|
||||
## Static gates
|
||||
|
||||
Each exit 0 on the final tree: `npm run check:fast` (typecheck and lint,
|
||||
2 tasks; 4.76 s elapsed, 23.36 s user, 2.56 s sys, 4458840064 bytes). `npm run
|
||||
format` (4064 files; 0.93 s elapsed, 4.12 s user, 0.76 s sys, 226344960
|
||||
bytes) then `npm run format:check` (all 4064 files formatted; 0.64 s elapsed,
|
||||
4.19 s user, 0.66 s sys, 219906048 bytes). `npx knip` (no findings; 3.49 s
|
||||
elapsed, 4.92 s user, 0.63 s sys, 871645184 bytes). `npm run check:ci:tasks`
|
||||
(8 tasks successful, 2 served from the turbo cache; `lint:built` 3756 files,
|
||||
0 errors; jscpd 3164 files, 2583 clones, 3.34% duplicated lines under the
|
||||
3.5% threshold; the stray-marker scan; 7.16 s elapsed, 26.46 s user, 5.88 s
|
||||
sys, 2939142144 bytes). `npm run verify:architecture-guards` passed across
|
||||
1523 source files and `npm run verify:test-discovery` discovered all 1592
|
||||
test files exactly once. `npm run docs:check` (six tasks plus the README
|
||||
consistency test, 5 cases; 3.37 s elapsed, 3.49 s user, 0.84 s sys,
|
||||
291618816 bytes). After the last edit of this record, `npm run format`,
|
||||
`npm run format:check` and `npm run docs:check` were rerun (each exit 0) and
|
||||
the same static set was rerun uncached as `npx turbo run typecheck lint:built
|
||||
format:check knip metadata:check jscpd scan:todos --force` (8 tasks
|
||||
successful, 0 cached, 3.34% duplication; 8.40 s elapsed, 48.82 s user, 7.77 s
|
||||
sys, 4638474240 bytes). A case-insensitive grep for the forbidden words and
|
||||
marker comments over all 28 new or modified files found nothing.
|
||||
|
||||
## Test projects
|
||||
|
||||
Source group on the final tree: `/usr/bin/time -l npx vitest run --project
|
||||
services --project adapters` passed 343 files, 5196 tests with 8 skipped, in
|
||||
99.55 s elapsed, 93.60 s user, 28.88 s sys, 1781055488 bytes. The increment's
|
||||
files are `environmentSource.test.ts` (4 tests: pair authentication and
|
||||
exchange indexing, parent mismatch as `invalid_request` with path
|
||||
`["evidence","evidence_id"]`, other view kind, non-capture evidence and
|
||||
non-Evidence JSON, an empty manifest indexing nothing with a tampered capture
|
||||
refused by authentication), `environmentTools.test.ts` (5 tests: ordered
|
||||
mapping and the `; evidence` description, required path parameters with rule,
|
||||
observed values and `additionalProperties: false`, optional query strings
|
||||
without a body for reads, a body object for writes, the card's key order),
|
||||
`environmentReplay.test.ts` (7 tests, unchanged in this pass) and
|
||||
`serveEnvironment.test.ts` (5 adapters tests: the card text byte-equal to the
|
||||
echo card shape with the fixture tools, `/agentenv` answering `-32601` with
|
||||
`-32700` for a malformed body, a route-level body of
|
||||
`DEFAULT_MAX_REQUEST_BODY_SIZE` plus envelope answered `413` `-32000` with
|
||||
`id: null`, a full listener driven by the SDK streamable HTTP client with
|
||||
`_meta` equal to `TARGET_CONTENT_RESULT_META`, exact and inexact replays, an
|
||||
argument-less call and an unknown argument both refused with `isError`, and
|
||||
the `renamed` override with an oversized body answered `413` through
|
||||
`node:http` and a released port).
|
||||
|
||||
Focused built group after `test:prepare`: `npx vitest run --project
|
||||
cli-boundary --project mcp-boundary --project process-boundary --project
|
||||
boundary` over `environmentServeCli`, `environmentServeMcp`,
|
||||
`environmentServeShutdown`, `mcpHttpShutdown`, `streamableHttpMcp`,
|
||||
`cliToolCoverage`, `cliStartupImports` (now carrying the `environment-server`
|
||||
deferred import), `dispatcher`, `productCatalog`, `documentationFacts` and
|
||||
`ciTestProjects` passed 11 files, 33 tests (16.38 s elapsed, 25.77 s user,
|
||||
5.94 s sys, 941948928 bytes).
|
||||
|
||||
Full built-group projects ran one after another on the final `dist/` from a
|
||||
background shell while the built-CLI exchange below ran in the foreground.
|
||||
`/usr/bin/time -l npx vitest run --project mcp-boundary` passed 156 files
|
||||
with 2 skipped, 914 tests with 7 skipped, in 166.23 s elapsed, 319.51 s user,
|
||||
46.18 s sys, 4452171776 bytes. `/usr/bin/time -l npx vitest run --project
|
||||
cli-boundary` exited 1: 109 files passed and 3 failed, 663 tests passed,
|
||||
2 skipped and 7 failed, in 432.45 s elapsed, 725.28 s user, 213.32 s sys,
|
||||
641597440 bytes. No failing file belongs to this increment.
|
||||
`javascriptApplicationJsonOutput.test.ts` (4 cases) and
|
||||
`javascriptApplicationProgress.test.ts` (2 cases) failed with the worker's
|
||||
result-capacity constraint ("The parent's result capacity was exhausted
|
||||
after retaining static facts for main.js"); rerun together on the idle host
|
||||
after the chain they passed 2 files, 17 tests (17.05 s elapsed, 26.40 s user,
|
||||
8.47 s sys, 282394624 bytes), the load classification the previous pass
|
||||
recorded. `closedStdout.test.ts` "stops quietly when the reader closes during
|
||||
streamed JSON" exits `1` instead of `141` with 1009 bytes received, alone as
|
||||
well as in the project, the environmental failure the previous pass
|
||||
reproduced on a clean clone (macOS, Node 22.23.1, npm 12.0.2).
|
||||
`commandFinalizationCancellation.test.ts`, which the previous pass saw fail
|
||||
on worker order, passed in this run.
|
||||
|
||||
`/usr/bin/time -l npx vitest run --project process-boundary` exited 1: 56
|
||||
files passed, 2 skipped and 3 failed, 463 tests passed, 19 skipped and 5
|
||||
failed, in 431.71 s elapsed, 148.34 s user, 114.81 s sys, 710000640 bytes.
|
||||
`environmentServeShutdown.test.ts` and `mcpHttpShutdown.test.ts` passed inside
|
||||
it. `jadxIntegration.test.ts` failed three cases at the 30 s test and 10 s
|
||||
hook timeouts ("retires an idle engine", "completes large class inventories",
|
||||
"close cancels active and queued work"), the lane the previous pass recorded
|
||||
as moving between runs on this host. `releaseArtifactCanary.test.ts` fails
|
||||
parsing `npm pack --json` ("expected tuple, received object"), the npm 12.0.2
|
||||
shape the previous pass reproduced on a clean clone.
|
||||
`processCaptureLifecycle.test.ts` "keeps the process deadline bounded when the
|
||||
wall clock moves backward" failed with `expected false to be true` on
|
||||
`result.ok`; rerun alone on the idle host it failed the same way with 23
|
||||
passed (38.12 s elapsed, 9.84 s user, 18.07 s sys, 323076096 bytes). The file
|
||||
and `src/process/` are untouched by this increment and the case mocks
|
||||
`Date.now` around a real process capture with a 1.5 s fallback abort; it is
|
||||
recorded as a host-dependent failure of that lane, not classified further
|
||||
here.
|
||||
|
||||
## Built-CLI exchange on the prototype capture
|
||||
|
||||
`node scripts/rea.mjs inspect-web-network-capture grafana-like.har har
|
||||
--format json` wrote capture Evidence `ev_4ee4759b…` with 28 records;
|
||||
`inspect-analysis-view` with the inline source and all three template rules
|
||||
wrote manifest Evidence `ev_6553a8e5…` naming that parent, `grafana-example`,
|
||||
19 tools. `environment-serve manifest.json --evidence capture.json --port 0
|
||||
--json < /dev/null` announced one stderr line
|
||||
`{"transport":"streamable-http","url":"http://127.0.0.1:62305/mcp","msg":"MCP transport listening"}`
|
||||
and nothing on stdout until shutdown.
|
||||
|
||||
`GET /.well-known/agent-env.json` returned a card whose top-level keys,
|
||||
`capabilities` keys and tool keys equal the echo card's in order; its
|
||||
`inputSchema` keys are `type`, `properties`, `required` and
|
||||
`additionalProperties`, the echo card's three plus the closing key. Fixed
|
||||
fields: `protocolVersion` `1.0`, `url` `/agentenv`, `preferredTransport`
|
||||
`JSONRPC`, `additionalInterfaces` `[{url:/mcp,transport:mcp}]`, `extensions`
|
||||
null, `operations` `[]`, `children_environments` null, 19 tools, with
|
||||
`get_users_by_id` described as `GET /api/users/{numeric}; replays retained
|
||||
records [5, 6]; evidence ev_4ee4759b…`.
|
||||
|
||||
The Python `mcp` 1.30.0 client (httpx 0.28.1) initialised against
|
||||
`grafana-example 6.6.0` (protocol 2025-11-25), listed 19 tools, called
|
||||
`get_users_by_id {"id":"2"}` and read `body.login == "probe"`,
|
||||
`replay.exact_match == True`, `_meta.provenance.untrusted_content == True`
|
||||
with `source == "analysis_target"`; `{"id":"999"}` gave `exact_match ==
|
||||
False`; `get_folders_by_uid {"uid":"general"}` gave `recorded_path ==
|
||||
"/api/folders/general"`; `get_search {"query":"probe","type":"dash-db"}`
|
||||
returned a list body whose first title is `probe dashboard` with
|
||||
`recorded_query == "?query=probe&type=dash-db"`; `get_users_by_id
|
||||
{"id":"2","unknown":"x"}` returned `isError == True` with no structured
|
||||
content.
|
||||
|
||||
`POST /agentenv {"jsonrpc":"2.0","id":1,"method":"data/reset"}` returned
|
||||
`{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"method not found: data/reset"}}`.
|
||||
A 60 MB non-JSON POST with a `Content-Length` header was answered `413`
|
||||
`{"jsonrpc":"2.0","error":{"code":-32000,"message":"Request body must not exceed 4194304 bytes"},"id":null}`
|
||||
after curl had sent 768332 bytes, in 1.4 ms, with the server's resident set
|
||||
269024 KiB before and 269040 KiB after; the same file sent chunked without a
|
||||
length was answered `413` after 5237311 bytes in 9.9 ms, resident set 267952
|
||||
KiB before and 274208 KiB after, so the read stops at the bound on both
|
||||
paths. A malformed body within the bound (`{`) was answered `400`
|
||||
`{"jsonrpc":"2.0","error":{"code":-32700,"message":"parse error"},"id":null}`.
|
||||
|
||||
SIGTERM ended the process with status 143 after one stdout document
|
||||
`{"status":"stopped","url":"http://127.0.0.1:62305/mcp","environment":"grafana-example","tools":19,"evidence_id":"ev_4ee4759b…","manifest_evidence_id":"ev_6553a8e5…"}`.
|
||||
A restart with `ENVIRONMENT_NAME=renamed` served a card named `renamed`,
|
||||
reported `"environment":"renamed"` in its stopped document and also exited 143. `ENVIRONMENT_NAME=` (set, blank) exited 1 before binding with
|
||||
`{"code":"configuration_invalid","details":{"settings":[{"setting":"ENVIRONMENT_NAME",…}]}}`.
|
||||
The manifest served against the capture of one selected record (`--record
|
||||
0`, `ev_3249d233…`) was refused with exit 1 and
|
||||
`{"code":"invalid_request","details":{"operation":"environment-serve","issues":[{"path":["evidence","evidence_id"],"reason":"invalid_value","expected":"ev_4ee4759b…",…}]}}`
|
||||
whose message names both identifiers; omitting `--evidence` exited 1 with
|
||||
Incur's `VALIDATION_ERROR` naming `evidence`. No `rea.mjs` process or
|
||||
listener remained after these runs.
|
||||
|
||||
## Independent review
|
||||
|
||||
The adversarial review of the first pass is the independent review of this
|
||||
increment; its findings and their repairs are listed above. One further
|
||||
observation surfaced while repairing them: the byte-identical card assertion
|
||||
in `serveEnvironment.test.ts` compares the `inputSchema` object exactly, so
|
||||
closing the schema required extending that expectation as well as the
|
||||
services tests, which is the intended coupling between the card and the
|
||||
advertised MCP schema.
|
||||
|
||||
## Scope and measurement limits
|
||||
|
||||
The echo-card comparison is exact on keys, nesting and tool entries; the
|
||||
property names inside `inputSchema.properties` necessarily differ from the
|
||||
echo tool's single `text` property, and `inputSchema` carries one key the echo
|
||||
schema does not. `exact_match` is defined over supplied values only; omitted
|
||||
parameters cannot mismatch, and a call without arguments is exact on the
|
||||
tool's first record. Request bodies on write tools are accepted and ignored.
|
||||
The post-bind listener `error` path is wired and type-checked but not
|
||||
provoked by a test, since the shared listener does not expose its
|
||||
`node:http` server. Non-loopback binds and the relaxed `Host` check were not
|
||||
exercised; the Python exchange, curl checks and every test used `127.0.0.1`.
|
||||
Replay selection and body parsing were measured on the 28-record fixture
|
||||
only; no large-capture timing was taken. Resident-set figures are `ps` samples
|
||||
of the serving process before and after one request each. Token usage and
|
||||
full process-family peak are unmeasured; the `/usr/bin/time -l` figures
|
||||
account for each command and the children it waited for. Windows was not
|
||||
run.
|
||||
@@ -0,0 +1,18 @@
|
||||
# Plan
|
||||
|
||||
1. Add the `evidence-file` source to `analysisViewSourceSchema`
|
||||
(`src/domain/analysisView/analysisView.ts`).
|
||||
2. Add `inspectAnalysisViewWithFiles` / `inspectAnalysisViewValidatedWithFiles`
|
||||
to `AnalysisViewService`: read with `readJsonFile`, authenticate with
|
||||
`parseEvidence`, then project through the retained path with a lookup that
|
||||
returns the file's Evidence. The synchronous path refuses an unread file
|
||||
source.
|
||||
3. Route the CLI command (with `source.path` resolved against the working
|
||||
directory) and the MCP tool through the file-aware entry point.
|
||||
4. Add `sibling-pair-variance` to `TEMPLATE_RULES`, with its merge pass before
|
||||
`sibling-variance`, its parameter names and its segment-limit limitation.
|
||||
5. Document both in the tool contract, `docs/cli.md` and
|
||||
`docs/web-network-captures.md`.
|
||||
6. Tests: service (file equals inline; a capture refused inline is accepted
|
||||
from a file; refusals), CLI and MCP boundaries, and the pair rule's merges
|
||||
and non-merges.
|
||||
@@ -0,0 +1,56 @@
|
||||
# Rich captures for the environment manifest
|
||||
|
||||
Issue: https://github.com/morluto/rea/issues/4933
|
||||
|
||||
## Outcome
|
||||
|
||||
A capture large enough to hold a rich application state can be projected into
|
||||
an environment manifest whose tool list stays within what model providers
|
||||
accept. Two changes:
|
||||
|
||||
- `inspect_analysis_view` accepts a third source,
|
||||
`{"kind": "evidence-file", "path": "<absolute path>"}`. REA reads one
|
||||
completed Evidence JSON file, authenticates it, and projects it like retained
|
||||
Evidence, so its strings are bounded by the file reader instead of being
|
||||
charged as inline input.
|
||||
- The environment-manifest view gains an opt-in template rule,
|
||||
`sibling-pair-variance`, that templates two adjacent literal segments after a
|
||||
plural literal, such as `/repos/{owner}/{repo}/…`.
|
||||
|
||||
## Requirements
|
||||
|
||||
- An `evidence-file` path must be absolute and free of NUL bytes; the CLI
|
||||
resolves a relative path against the working directory before validation,
|
||||
and MCP clients pass an absolute path.
|
||||
- The file is read through the shared JSON file reader (regular file, heap and
|
||||
string-length guards) and parsed as complete Evidence. A missing or unreadable
|
||||
file is an `EvidenceFileError`; JSON that is not complete Evidence is invalid
|
||||
input reported under `source.path`.
|
||||
- Over MCP the read follows the request's cancellation and the server's
|
||||
resource scope, so a failed descriptor close stays owned for retry; a request
|
||||
cancelled before authentication, before projection or during projection
|
||||
returns typed cancellation and records nothing. Before authentication, which
|
||||
copies and hashes the parsed Evidence, REA checks that live heap headroom holds
|
||||
those copies and otherwise returns `resource_constraint`.
|
||||
- A file source projects exactly what the same Evidence projects inline or
|
||||
retained, with the same parent Evidence ID, view parameters and result.
|
||||
- The synchronous projection API refuses an unread `evidence-file` source with
|
||||
a typed capability error; the CLI and MCP adapters read files first.
|
||||
- `sibling-pair-variance` applies only when listed, before `sibling-variance`, so
|
||||
a single-position merge cannot template one half of a pair first. It
|
||||
compares groups equal in method, origin and segment count except at two
|
||||
adjacent literal positions at index 2 or later that follow a plural literal
|
||||
and precede another literal (a trailing pair, such as
|
||||
`/users/alice/followers`, names a sub-resource rather than a namespace and
|
||||
name), and merges a bucket only when it holds at least three distinct pairs and both
|
||||
positions vary. Stop words and `v1`-style versions never qualify, and routes
|
||||
of more than 32 segments are not compared, with a limitation saying so.
|
||||
- Its parameters are named from the plural literal, singular plus `_namespace`
|
||||
and then `_name`, and labelled `sibling-pair-variance`.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- No change to the 96 MiB projection budget or to inline admission.
|
||||
- No change to `sibling-variance`; pairs are a separate rule so every inferred
|
||||
parameter keeps naming the rule that produced it.
|
||||
- No inference of semantic names (`owner`, `repo`) beyond the plural literal.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Record the outcome, requirements and non-goals.
|
||||
- [x] Add the `evidence-file` source and the file-aware service entry point.
|
||||
- [x] Route the CLI command and the MCP tool through it.
|
||||
- [x] Add the `sibling-pair-variance` rule, its names and its limitation.
|
||||
- [x] Document both; extend the tool contract text.
|
||||
- [x] Add service, CLI, MCP and rule tests.
|
||||
- [x] Run static, formatting, duplication, documentation and test gates.
|
||||
- [x] Rebuild a rich capture with both changes and record the results.
|
||||
@@ -0,0 +1,104 @@
|
||||
# Verification
|
||||
|
||||
Verified commit: a64367a9 on main 2dd5effe.
|
||||
|
||||
## Gates
|
||||
|
||||
Run on macOS 26.5.1 with Node 22.23.1 on that tree: `npm run typecheck`, `lint`
|
||||
(oxlint plus `verify:module-boundaries`, 2172 files), `format:check`, `knip`,
|
||||
`metadata:check`, `docs:check`, `jscpd` (3.43% duplicated lines), `scan:todos`
|
||||
and `mcp-catalog:check` all exit 0. oxlint on the 12 changed TypeScript files
|
||||
reports no finding that main does not already report for the same files.
|
||||
|
||||
`npm run test:prepare`, then `npm run test:changed` (the tests related to the
|
||||
changed files): 84 files, 1531 passed, 3 skipped, 0 failed. They include:
|
||||
|
||||
- the service: an Evidence file projects exactly what the same Evidence projects
|
||||
inline; a capture whose response text exceeds the inline budget is refused
|
||||
inline with `resource_constraint` and projected from its file; a relative
|
||||
path, a missing file and JSON that is not Evidence are refused; the
|
||||
synchronous API refuses an unread file source;
|
||||
- the compiled CLI: a request naming an Evidence file projects the same
|
||||
manifest as the same Evidence inline;
|
||||
- MCP: inline, retained and file sources give identical manifests, outputs
|
||||
validate against the advertised schema, and a relative path is refused;
|
||||
- the file source module: the size estimate, the authentication headroom
|
||||
refusal, a cancelled request refused before reading, and a read within the
|
||||
caller's resource scope; the service returns typed cancellation for a
|
||||
cancelled file request; a request aborted while it runs, even after its read, returns typed
|
||||
cancellation because the service yields before each check;
|
||||
- the pair rule: three owner/repo pairs merge for collection and numeric detail
|
||||
routes, while two pairs, or three pairs sharing one namespace, stay literal;
|
||||
with `sibling-variance` also selected the pair still merges, because pairs
|
||||
are merged first. Each pair parameter keeps its role when
|
||||
`sibling-variance` later templates the segment before the pair.
|
||||
|
||||
## A rich capture
|
||||
|
||||
A Gitea 28.1.0 instance seeded through its API with an invented world (12
|
||||
people, 4 organisations, 16 public repositories, 320 issues with about 600
|
||||
comments, labels, milestones, commits, pull requests and releases) was read by
|
||||
one `capture-browser-scenario` run of 1,217 API reads: 1,218 transactions in
|
||||
24 s, 10.7 MB of Evidence, nothing dropped. Scenario parents are not on main,
|
||||
so this run used this commit merged onto the scenario-capture change
|
||||
(morluto/rea#5182) and packed for the host.
|
||||
|
||||
| | Inline source, previous rules | Evidence-file source with `sibling-pair-variance` |
|
||||
| --------------------- | ------------------------------------- | ------------------------------------------------- |
|
||||
| Manifest | `resource_constraint` (96 MiB budget) | built in 4.1 s, 459 MB maximum RSS |
|
||||
| Tools | 446 (with the budget raised by hand) | 63 |
|
||||
| Recorded exchanges | 1,218 | 1,218 |
|
||||
| Tools with one record | 77% | 49% |
|
||||
|
||||
The pair rule templated 25 route families as `{repo_namespace}/{repo_name}`.
|
||||
A model provider that accepts at most 128 tools refused every request for the
|
||||
446-tool manifest with HTTP 400.
|
||||
|
||||
The 63-tool environment was then exported, and in the AgentEnv runner its
|
||||
smoke task passed and twelve generated retrieval tasks scored 12/12 with
|
||||
`openai/gpt-oss-120b` and 12/12 with `zai-org/GLM-5.3-Flash`, with no provider
|
||||
errors. The 446-tool manifest had scored 0/12 and 8/12 on its own generated
|
||||
tasks, the 0 being the provider's tool limit rather than the model.
|
||||
|
||||
Review then found that the rule as run here also merged one id's
|
||||
sub-resources (`/users/alice/followers`, `/users/bob/following` and
|
||||
`/users/carol/starred` became one tool), and a pair now qualifies only when a
|
||||
literal follows it. The Gitea capture above predates that guard and has not
|
||||
been projected with it: whether any of the 63 tools merged sub-resources this
|
||||
way, and the tool count with bare `/repos/{owner}/{repo}` routes kept one per
|
||||
pair, are not established here.
|
||||
|
||||
## Linux follow-up (2026-10-20)
|
||||
|
||||
Based on `dd2bf3565`, including main `5fa4defcb`, with Node 24.18.0 and npm
|
||||
11.16.0. The file reader now owns authentication; invalid Evidence returns the
|
||||
selected file path and file-relative `content_issues`. Service and advertised
|
||||
MCP error-envelope regressions cover this boundary.
|
||||
|
||||
- Domain analysis views, application analysis views and error projection:
|
||||
16 files, 298 tests passed.
|
||||
- Compiled analysis-view CLI/MCP and environment serve/export boundaries:
|
||||
5 files, 48 tests passed.
|
||||
- Serial Turbo run: typecheck, lint, format, knip, package metadata and required
|
||||
runtime/contract artifacts: 11 tasks passed. Documentation/catalog validation:
|
||||
8 tasks passed. Architecture guards passed across 1,595 source files.
|
||||
- Real Gitea 28.2.0 official Linux binary (published compressed SHA-256 checked),
|
||||
isolated SQLite database, two users and three public repositories with large
|
||||
issue bodies. Actual Chromium navigation captured nine API transactions,
|
||||
45 events, no drops, 12,584,312 bytes of Evidence. Inline manifest projection
|
||||
was refused with `resource_constraint`; file CLI and default stdio MCP
|
||||
projections agreed on five tools with all template rules selected. The
|
||||
three trailing user sub-resources stayed separate. Malformed file diagnostics
|
||||
named the file's JSON root. HTTP SDK replay verified six complete small
|
||||
responses and twelve exact 256 KiB prefixes of large responses, each marked
|
||||
truncated. Owned Gitea, browser and replay-server processes were stopped.
|
||||
|
||||
One CPU and one test worker were used. Sampled sum of descendant RSS every
|
||||
50 ms (shared pages can count twice): focused tests 15.834 s / 15.327 CPU s /
|
||||
764.69 MiB peak; boundaries 194.916 s / 190.121 CPU s / 1,795.15 MiB;
|
||||
static/build 266.311 s / 259.658 CPU s / 4,677.44 MiB; real Gitea workflow
|
||||
31.631 s / 24.999 CPU s / 2,146.29 MiB. These are individual runs, not a
|
||||
cold/warm comparison; model token usage was not measured. The first external
|
||||
probe stopped at the expected CLI refusal because its driver rejected exit 1;
|
||||
the driver was corrected and the complete workflow passed. No new macOS,
|
||||
Windows, model-task scoring or AgentEnv runner verification is claimed here.
|
||||
@@ -0,0 +1,20 @@
|
||||
# Implementation plan
|
||||
|
||||
1. Add `src/domain/analysisView/scenarioCaptureRecords.ts`: index scenario
|
||||
events by transaction, project each request event as a HAR-shaped
|
||||
`WebNetworkCaptureRecord`, and admit a scenario parent for the manifest
|
||||
view (clone budget, schema, network-content selection).
|
||||
2. Route `projectEnvironmentManifestView` through one capture-parent gate that
|
||||
selects the HAR or scenario admission by parent operation, and widen the
|
||||
environment-manifest result's `parent_operation`.
|
||||
3. Branch `parseCaptureEvidence` in `environmentSource.ts` on the Evidence
|
||||
operation, carry the capture kind and digest on the source, and word the
|
||||
bundle README and CLI help for both kinds.
|
||||
4. Add a schema-valid scenario capture fixture and domain and services tests:
|
||||
the record projection, the manifest from a scenario parent, the refusal,
|
||||
replay of UTF-8 and binary bodies, and the bundle README.
|
||||
5. Document scenario parents in `docs/agent-environments.md` and
|
||||
`docs/web-network-captures.md`, extend the tool contract text, and record
|
||||
the spec.
|
||||
|
||||
No new dependency is introduced and no command is added.
|
||||
@@ -0,0 +1,65 @@
|
||||
# Build environments from browser scenario captures
|
||||
|
||||
Issue: https://github.com/morluto/rea/issues/4933
|
||||
|
||||
## Outcome
|
||||
|
||||
The `environment-manifest` view, `environment-serve` and `environment-export`
|
||||
accept `capture_browser_scenario` Evidence as the manifest's parent, in
|
||||
addition to `inspect_web_network_capture` HAR Evidence. A session REA records
|
||||
itself, in a browser it launches, with credential headers and declared secrets
|
||||
redacted, becomes an agent environment without an external capture script or
|
||||
HAR scrubbing step. The replay, the tool surface and the bundle layout do not
|
||||
change.
|
||||
|
||||
## Requirements
|
||||
|
||||
- A scenario parent is admitted only when its scenario selected
|
||||
`capture.network.response_body` or `header_values`, because tools are chosen
|
||||
by response media type; otherwise the view refuses with
|
||||
`unsupported_target`.
|
||||
- Each network transaction becomes one record in request order; its ordinal
|
||||
is the zero-based position of its `request` event, and its location is the
|
||||
JSON pointer of that event in the capture. The record is shaped like a HAR
|
||||
entry: request method and URL, the request body as `postData` when it was
|
||||
captured and decodes as UTF-8, the response status, and the response media
|
||||
type with its retained bytes as base64 content and a retained binary field.
|
||||
- A transaction whose URL the scenario redacted keeps a null URL, so no path
|
||||
template is formed from a redaction marker. A transaction that does not
|
||||
resolve to exactly one request event, at most one response event and at most
|
||||
one content event per phase keeps no request line. Both are counted as
|
||||
unrepresented records, as HAR records without a usable request line are.
|
||||
- The response media type is the captured body's `media_type`, or the
|
||||
`content-type` header value when only headers were captured.
|
||||
- The existing projection representation budget reserves, before schema body
|
||||
validation or request-body decoding and for retained and file parents alike,
|
||||
the scratch of validating the largest body (two bytes per base64 character)
|
||||
and the decoded text of every request body; response bodies stay borrowed,
|
||||
as HAR response text does, so a large capture read from its file is not
|
||||
charged per character for them. Over-budget captures return
|
||||
`resource_constraint` before those allocations. Replay projection omits the
|
||||
request text used only for manifest body-shape inference; retained response
|
||||
bytes and their integrity checks are unchanged.
|
||||
- The manifest result names `parent_operation: "capture_browser_scenario"`,
|
||||
carries an empty `artifact` (`path: ""`, `sha256: null`) and states, as
|
||||
limitations, the ordinal rule, any dropped capture events, how many
|
||||
transactions with a usable request line retained no response body (tools
|
||||
citing them replay only the recorded status and content type, either of
|
||||
which may be absent) and how many retained response bodies carry redaction
|
||||
markers. A body that redaction emptied, because a declared secret could not
|
||||
be cut out, is treated as not retained rather than replayed as an empty
|
||||
reply.
|
||||
- `environment-serve` and `environment-export` authenticate either parent
|
||||
kind through the same loader, project scenario transactions through the same
|
||||
adapter, and keep the cross-check that the manifest's parent is the supplied
|
||||
Evidence. The bundle README names the capture kind and, for a scenario, the
|
||||
SHA-256 of the scenario definition in place of a capture artifact digest.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- No new browser action: scenarios still cover what the UI requests plus GET
|
||||
reads through `goto`. A fetch or script action would be a new effect and
|
||||
needs its own scope decision.
|
||||
- No change to what the scenario redacts. Values the application returns in
|
||||
response bodies are retained, as they are in HAR captures.
|
||||
- The api-surface view keeps HAR parents only.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Record the scenario-parent contract, requirements and non-goals.
|
||||
- [x] Project scenario transactions as HAR-shaped records and admit scenario parents.
|
||||
- [x] Route the manifest view through one capture-parent gate and widen its result schema.
|
||||
- [x] Load scenario Evidence in serve and export; name the capture kind in the README and help.
|
||||
- [x] Add the scenario capture fixture and domain and services tests.
|
||||
- [x] Document scenario parents and extend the tool contract text.
|
||||
- [x] Run static, formatting, duplication, documentation and test gates.
|
||||
- [x] Build environments from real browser scenario captures and record the results.
|
||||
@@ -0,0 +1,144 @@
|
||||
# Verification
|
||||
|
||||
Verified commit: 3c2a8db2, this increment on top of the environment-export increment (481c9883).
|
||||
|
||||
## Gates
|
||||
|
||||
Run on macOS 26.5.1 with Node 22.23.1 on that tree:
|
||||
|
||||
- `npx tsc --noEmit`: no errors.
|
||||
- `oxfmt --check`: all files formatted. `oxlint` on the changed files: no new findings.
|
||||
`npm run verify:module-boundaries`: 2133 files checked.
|
||||
- `npm run jscpd`: 3.35% duplicated lines, unchanged. `npm run scan:todos`: none.
|
||||
`npm run mcp-catalog:check`: passed. `npx knip`: one unresolved import that predates
|
||||
this change (`#javascript-analysis-worker`).
|
||||
- `npm run test:prepare`, then vitest over `src/domain/analysisView`, `src/application`,
|
||||
`src/contracts`, `src/server/environment`, `tests/conformance` and the analysis-view,
|
||||
environment-serve, environment-export and browser-scenario boundary tests: 1946 tests in
|
||||
157 files, 1941 passed, 5 skipped, 0 failed.
|
||||
|
||||
## Real browser captures
|
||||
|
||||
`npm pack` of this commit (`rea-agents-6.7.0.tgz`) was installed on Ubuntu 24.04 amd64 hosts
|
||||
(8 vCPU, 32 GB) with Chromium 155.0.8059.79 at `/snap/bin/chromium`, Node 24, Docker and
|
||||
`agentenv-framework` 0.9.1334. Every capture below is `rea capture-browser-scenario` output;
|
||||
no HAR was recorded, scoped or edited.
|
||||
|
||||
### Fixture
|
||||
|
||||
A loopback fixture served a page that lists photos from `/api/photos`, three PNG thumbnails, and
|
||||
a form that POSTs JSON to `/api/albums`. The scenario waited for the list, opened
|
||||
`/api/photos/1..3` with `goto`, filled and submitted the form.
|
||||
|
||||
- Capture: 14 transactions, 70 events, 0 dropped, authority `controlled-replay`.
|
||||
- `environment-manifest`: `parent_operation` `capture_browser_scenario`, origin
|
||||
`http://127.0.0.1:3199`, confidence `inferred`. Tools `get_photos` (records 1, 9),
|
||||
`get_photos_by_id` (`{numeric}` id, records 5, 6, 7) and `post_albums` (record 13, body shape
|
||||
`title: string`). The page and the three PNGs were excluded as `ui-page-or-asset`.
|
||||
- `environment-serve`: `get_photos_by_id {id: "3"}` returned the recorded JSON with
|
||||
`record_ordinal` 7 and `exact_match` true.
|
||||
- The POST's response body was missing: the scenario ended at the action that sent it, the
|
||||
transaction was still unfinished, and the observer does not read bodies of unfinished
|
||||
responses. The manifest stated "1 of 14 scenario transactions retained no response body" and
|
||||
`post_albums` replayed status 201 with no body. Ending the scenario with a 1.5 s
|
||||
`wait_for_timeout` finished all 14 transactions and retained the body.
|
||||
- `environment-export` of that capture wrote a README naming the browser scenario capture and the
|
||||
scenario definition's SHA-256; `agent-env run <bundle> --task smoke` passed in 30.4 s.
|
||||
|
||||
### Five applications
|
||||
|
||||
Each environment ran manifest, export, the bundle's smoke task and 6 to 12 generated retrieval
|
||||
tasks, with one run per task for two models through the Hugging Face router.
|
||||
|
||||
| Application | Capture | Actions | Transactions | Tools (rules / none) | Smoke | Retrieval tasks | gpt-oss-120b | GLM-5.3-Flash |
|
||||
| ---------------------------- | ------------------------------------------------------------------ | ------- | ------------ | -------------------- | ------ | --------------- | ------------ | ------------- |
|
||||
| PhotoPrism 261007 | self-hosted, no login (public mode) | 55 | 277 | 13 / 28 | passed | 10 | 10/10 | 10/10 |
|
||||
| Lychee 6.10.4 | self-hosted, session cookie seeded from a declared secret | 152 | 134 | 15 / 27 | passed | 12 | 11/12 | 12/12 |
|
||||
| Pinry 2.1.13 | self-hosted, session and CSRF cookies seeded from declared secrets | 148 | 568 | 12 / 12 | passed | 6 | 6/6 | 6/6 |
|
||||
| Art Institute of Chicago API | public, `goto` per URL | 52 | 26 | 11 / 12 | passed | 12 | 11/12 | 12/12 |
|
||||
| Library of Congress JSON API | public, `goto` per URL | 55 | 29 | 11 / 11 | passed | 12 | 11/12 | 12/12 |
|
||||
|
||||
Every scenario step completed and no capture dropped events. Each self-hosted capture includes
|
||||
one create, one edit and one delete made through the application's own forms; all three became
|
||||
tools with their recorded statuses, including a 204 DELETE without a content type. Declared
|
||||
secrets did not appear in any capture; where the application echoed a declared value in a
|
||||
response body (PhotoPrism's session reply, Pinry's owner token) the capture held the redaction
|
||||
marker instead.
|
||||
|
||||
Two other targets were tried and replaced. The Met Collection API projected 3 tools: its data
|
||||
paths are `/departments`, `/objects/{id}` and a search, and numeric object ids merge into one
|
||||
tool. The NASA Image and Video Library projected 16 tools, but its Collection+JSON bodies nest
|
||||
record fields deeper than the task generator reads, so no tasks could be derived.
|
||||
|
||||
## Observed limits
|
||||
|
||||
These were observed during the runs and are outside this increment:
|
||||
|
||||
- A scenario's last action can lose its response body unless the scenario ends with a wait
|
||||
(above).
|
||||
- Large asset bodies count against the analysis-view budget. One Pinry capture at
|
||||
`max_network_body_bytes` 262144 held 12.5 MB of Evidence, mostly CSS and JavaScript reloaded
|
||||
on 33 page loads, and the manifest view returned `resource_constraint`. A 20,000-byte body
|
||||
limit kept it within budget. Scenarios have no per-origin or per-media-type body selection.
|
||||
- Image responses outside an API path are excluded as UI assets, so their bytes are replayable
|
||||
only when an API path serves them (PhotoPrism's `/api/v1/t/...` tiles became tools).
|
||||
- With the default `max_total_event_allocation_bytes`, one PhotoPrism capture dropped its last
|
||||
68 transactions, including all three writes, while the command exited 0; 64 MiB dropped none.
|
||||
- Actions have no default deadline: two runs waited on a locator for 277 s and 7.5 min until
|
||||
cancelled. Every scenario above sets `timeout_ms` on its actions.
|
||||
- A redirect is its own transaction; Django's trailing-slash 301 produced a second tool
|
||||
(`get_pins_by_id` and `get_pins_by_id_2`) for one resource.
|
||||
|
||||
## Linux followup verification (2026-10-19)
|
||||
|
||||
The original PR was merged with current main `7a0c47bfa`, after #4955, #4975
|
||||
and #4999 merged. The export filesystem ownership, mode, tarball, cleanup and
|
||||
caller-path fixes from main are retained. Default MCP remains stdio.
|
||||
|
||||
The retained-parent projection previously decoded every request body before
|
||||
per-record budget checks. It now reserves copied/transformed text through the
|
||||
shared admission owner before schema validation or decoding, including retained
|
||||
parents. Replay projection omits request text used only for body-shape inference;
|
||||
response bytes still undergo the producer schema's integrity validation. The
|
||||
intermediate projected-array copy and unused capture wrapper were removed.
|
||||
Windows test inventories normalize filesystem separators to the portable bundle
|
||||
path representation. CI selects the agentenv lane for the new projection and its
|
||||
shared admission dependencies.
|
||||
|
||||
Executed on Linux x64, Node 24.18.0 / npm 11.16.0, Chrome for Testing
|
||||
156.0.8078.4:
|
||||
|
||||
- `turbo run typecheck lint:built format:check docs:check:built knip artifacts:skills artifacts:mcp-catalog --concurrency=1 --summarize`: eleven tasks passed, one cached, 210.023 s elapsed / 209.860 s CPU / 4562.07 MiB peak process-family RSS. This first complete gate preceded the final lint cleanup and CI-selection additions; later checks are reported on the PR.
|
||||
- Initial source verification passed 111 tests in 13 files, including HAR/manifest regressions, scenario loading/replay/export and new capacity/replay-mode tests. CI-selection verification passed 235 cases.
|
||||
- Real public CLI capture of the source-owned interactive browser network site: ten transactions, fifty events, two tools, no dropped events. Its POST response replayed twice through the official HTTP SDK with exact body/status/Evidence ordinal and untrusted-content provenance. Declared secrets and transport credentials were excluded; the ordinary selected `password` field value remained in retained body bytes.
|
||||
- Real public CLI capture of three live GitHub API responses in Chromium, without a HAR: three transactions, fifteen events, three tools, no dropped events. Every response replayed twice through the HTTP SDK with exact body/status/Evidence/provenance. Explicit navigation waits let response-body capture settle. Complete two-scenario capture/projection/serve task: **22.527 s / 16.678 s CPU / 1397.93 MiB** peak process-family RSS, including owned browser processes. Both listeners exited 143 and newly owned browser profiles were gone.
|
||||
- The same two actual captures produced identical manifest results through CLI and official SDK **stdio** MCP, with HTTP environment variables present: **2.795 s / 2.778 s CPU / 476.67 MiB**. This verifies adapter parity and default transport independently of explicit environment HTTP.
|
||||
|
||||
The first local probe mistakenly searched base64 body content for plaintext; its
|
||||
assertion was corrected to inspect decoded retained bytes. A later SDK probe
|
||||
initially imported stdio transport from the SDK root instead of its documented
|
||||
subpath; the corrected probe passed. An early live navigation also reported one
|
||||
`body-read-failed` response, which the projection preserved as unavailable. No
|
||||
browser-provider behavior was changed; the successful run used explicit waits.
|
||||
These earlier attempts are not counted as passing runs.
|
||||
|
||||
A matched retained-scenario projection comparison bundles the original helper
|
||||
from `3aae3579e` and the followup helper, supplies the same 72,771-byte capture
|
||||
with three JSON writes, and completes one cold plus 100 warm full manifest
|
||||
projections. Both outputs have the same SHA-256
|
||||
`990ac431670374ff68c77a20d77985f626e4681bb0e2f491e5af18f45ce79991`.
|
||||
Before/after process totals: **0.970/0.876 s elapsed, 0.934/0.865 s CPU,
|
||||
184.89/184.39 MiB** RSS. First task: **17.378/13.073 ms**; 100 warm tasks:
|
||||
**261.419/236.131 ms**. Single samples do not establish a speed or memory
|
||||
improvement. Bundle preparation is excluded; process totals include fixture
|
||||
startup. CLI, provider and transport costs are excluded from this comparison.
|
||||
|
||||
Heavy work is serialized on one CPU with one test worker. RSS sums sampled
|
||||
process descendants every 50 ms and can double-count shared pages. Agent token
|
||||
usage is unmeasured. These followups do not rerun the author's five application
|
||||
reconstruction/model tasks, nor establish Windows or macOS behavior. Historical
|
||||
response replay does not reconstruct application state.
|
||||
|
||||
A retained-parent counterexample used a 128 KiB projection budget and one 16 KiB JSON request: the original helper admitted it and decoded once; the repaired helper returned `AnalysisResourceConstraintError` with zero UTF-8 decodes. The test intentionally uses a small budget without inducing an OOM. Final targeted verification passed 242 tests (including 235 CI-selection cases): 41.042 s / 40.666 s CPU / 644.37 MiB peak process-family RSS.
|
||||
|
||||
The broader d2416ca54 regression run passed 657 tests in 47 files plus ten product-catalog cases (295.763 s / 293.370 s CPU / 1921.69 MiB family RSS; catalog checked separately). The author then pushed 655e62334 with redaction/body-budget documentation and withheld-empty-body behavior. That commit was retained; the followup counts bodiless/redacted observations against represented transactions, preserving the complete request ordinals. The related source run passed 145 tests in 14 files (9.793 s / 9.618 s CPU / 688.66 MiB). Docker execution was held at this author-update checkpoint, so no earlier bundle is attributed to the new source. Final committed checks and actual scenario-container/framework results are reported on the PR.
|
||||
@@ -0,0 +1,28 @@
|
||||
# Implementation plan
|
||||
|
||||
1. Move response-body decoding from the environment source into
|
||||
`recordedResponseBody.ts` with the caller's byte limit, and exact JSON
|
||||
parsing into `exactJsonNumber.ts`, so replay and state extraction share one
|
||||
owner each.
|
||||
2. Add `environmentStateRules.ts` as pure helpers: write classification, API
|
||||
bases and roots, entity location, identity names, JSON pointers of depth one,
|
||||
value bytes and the entity and list renderers.
|
||||
3. Add `EnvironmentStateExtraction.ts` for planning item and list tools,
|
||||
observing entities and assembling the result, `environmentStateMerge.ts` for
|
||||
merging observations, and `environmentStateGate.ts` for reproducing each
|
||||
plan's recordings; every body is parsed through a budget callback and
|
||||
released before the next.
|
||||
4. Add `EnvironmentStateAccumulator.ts`, extending the manifest accumulator so
|
||||
grouping is shared, retaining record lines and references to the admitted
|
||||
records, and charging bodies and kept values.
|
||||
5. Add `environmentStateView.ts` with the request schema (sharing the
|
||||
manifest's `template_rules` schema), the result fields and the projector;
|
||||
share capture admission and record grouping with the manifest view; register
|
||||
the kind and pass accumulators to `projectAnalysisView` as one object;
|
||||
always seal the result inferred.
|
||||
6. Cover domain, service, MCP, CLI, catalog and acceptance behavior; document
|
||||
the view in the capture guide, the agent environments guide, the contracts
|
||||
reference and the authored skill.
|
||||
|
||||
No new tool, provider, side effect or network activity is introduced. The state
|
||||
is a projection of already retained Evidence; serving it is deferred.
|
||||
@@ -0,0 +1,74 @@
|
||||
# Environment state view from captures
|
||||
|
||||
Issue: https://github.com/morluto/rea/issues/4933
|
||||
|
||||
## Outcome
|
||||
|
||||
`inspect_analysis_view` accepts `view: {"kind": "environment-state"}` for the
|
||||
parents the environment manifest accepts and extracts the initial state the
|
||||
capture's reads observed: entity types with an identity field, entities with
|
||||
their recorded values, documents for written paths whose reads are not
|
||||
entities, and a render plan per read tool that reproduces the tool's latest
|
||||
initial recordings from the state, or a recorded view with the reason it
|
||||
cannot. This increment adds the view kind only; serving from the state, loading
|
||||
another state through the AgentEnv data plane and modelling write effects are
|
||||
later increments.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Accept `inspect_web_network_capture` HAR parents and
|
||||
`capture_browser_scenario` parents that selected response bodies, with the
|
||||
manifest's admission: the same format, subject and sensitive-value refusals.
|
||||
- Group records exactly as the manifest does for the same `template_rules`, so
|
||||
tool names match the manifest the state accompanies.
|
||||
- Order primary-origin records by recorded start time, or by record order when
|
||||
any start time is missing, and say which. Bound each type's state by the first
|
||||
write under its root (API base, version segments, one segment); a query
|
||||
`POST` is not a write, a read overlapping the write is left out, and a write
|
||||
whose URL was redacted bounds every type, with a limitation counting it.
|
||||
- Prove an item type's identity field from the requested path value its
|
||||
responses repeat; otherwise infer it from a fixed list of identity names that
|
||||
every listed item carries distinctly, which needs at least one listed item.
|
||||
Never treat an envelope that is itself an entity as a list.
|
||||
- Merge observations into one entity per collection and identity: item views'
|
||||
latest values win over list views', differing views keep their own values,
|
||||
and a value that changed between repeats of one request line with no recorded
|
||||
write is volatile and keeps the latest; a change across a write under another
|
||||
root, including between two tools of the type, leaves every tool of the type
|
||||
recorded. Compare list membership, including empty lists, and field presence before selecting each request line's latest recording; a change across a write also leaves the type recorded. Different query strings remain distinct; omitted fields do not erase their earlier observations on other request lines.
|
||||
- A list has `all` membership only when no item-total envelope member (`total`,
|
||||
`count` and their compounds, not page counts) differs from the number of
|
||||
entities it lists and no `has_more`, `next` or next-page token is set.
|
||||
- Keep an item or list plan only when it reproduces each request line's latest
|
||||
initial recording from the state, comparing JSON values without regard to
|
||||
member order; otherwise record the tool with a reason. Parse bodies only when
|
||||
JSON-like, UTF-8, at most 256 KiB, exactly representable and free of
|
||||
`__proto__` members.
|
||||
- Mark the Evidence inferred; state unknowns for authorization, response
|
||||
semantics and write effects.
|
||||
- Charge the manifest's admission and grouping, every record line, every body
|
||||
as temporary bytes before decoding and parsing, and every kept value, within
|
||||
the existing 96 MiB budget.
|
||||
- Read response bodies through one domain owner shared with replay, and parse
|
||||
exact JSON through the owner of the exact-number policy.
|
||||
|
||||
## Acceptance and limits
|
||||
|
||||
The Grafana-shaped HAR fixture with distinct start times drives domain,
|
||||
service, MCP, CLI and acceptance coverage: folders merge their detail and list
|
||||
views under a proved `uid`, the dashboard entity is read from its `/dashboard`
|
||||
wrapper, and the annotation list after the annotation write has no initial
|
||||
recording. Synthetic captures cover write bounds, volatile fields, complete
|
||||
lists with counters, each recorded reason and documents; the scenario fixture
|
||||
covers record order and the response-body requirement. The view performs no
|
||||
network activity and serves nothing. Values copied outside entity objects,
|
||||
derived values and cross-type effects are not tied to the state.
|
||||
|
||||
## Deferred issue criteria
|
||||
|
||||
Serving tool calls from the state, `data/reset`, `data/add` and `data/get` on
|
||||
the environment data plane, exporting the state with a bundle, and write
|
||||
effects are later increments of #4933. So are documents for item-shaped
|
||||
written paths (one per identity rather than per template) and for paths that
|
||||
are only deleted; until then their reads stay recorded. This PR must reference rather than
|
||||
close the issue.
|
||||
@@ -0,0 +1,12 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Record the view contract, requirements and deferred increments.
|
||||
- [x] Move body decoding and exact JSON parsing to their domain owners without
|
||||
changing replay.
|
||||
- [x] Implement the extraction rules, merge, gate and accumulator.
|
||||
- [x] Register the `environment-state` kind, result schema, service wiring and
|
||||
confidence rule.
|
||||
- [x] Add the timed Grafana-shaped fixture and synthetic captures.
|
||||
- [x] Add domain, service, MCP, CLI, catalog and acceptance coverage.
|
||||
- [x] Document the view in the guides, contracts reference and skill.
|
||||
- [x] Run static, formatting, duplication, documentation and test gates.
|
||||
@@ -0,0 +1,156 @@
|
||||
# Verification
|
||||
|
||||
Verified base: bf1eeee9 (fetched main).
|
||||
|
||||
Executed on the implementation worktree after `npm ci` and `npm run
|
||||
build:cached`; every check below ran as written in this pass, after the last
|
||||
source change.
|
||||
|
||||
- `npm run check:fast`: typecheck and lint passed.
|
||||
- `npm run format:check`: passed.
|
||||
- `npx knip`: passed (exit 0).
|
||||
- `npm run jscpd`: 3.40% duplicated lines over 3331 files, under the 3.5%
|
||||
gate; no clone involves a file this change adds.
|
||||
- `npm run scan:todos`: passed.
|
||||
- `npm run docs:check`: the built documentation checks and the README
|
||||
consistency conformance test (5 tests) passed.
|
||||
- `npx vitest run --project domain --project services`: 338 files, 5830 tests
|
||||
passed, 5 pre-existing skips.
|
||||
- `npm run test:focused --` the analysis-view MCP and CLI boundaries, the
|
||||
environment serve and export CLI boundaries, the environment serve MCP
|
||||
boundary, the compact input-schema MCP budget, the analysis-detail and
|
||||
advertised-input-example contracts and the qlog acceptance test: 9 files, 68
|
||||
tests passed, 1 pre-existing skip.
|
||||
|
||||
Real captures (outside the test suite): the built view ran through the
|
||||
application service on 44 recorded captures (39 HAR, 5 browser scenario) with
|
||||
all three template rules, in 7 to 46 ms each. 42 produced a state: 568 types,
|
||||
2,213 entities, 2 documents, and 710 of 1,258 read tools state-backed (211
|
||||
item, 497 list, 2 document views). The other read tools are recorded views:
|
||||
306 not entities (singletons, configuration and computed views), 129 without
|
||||
an identity field, 32 with unusable bodies (most are Nomad bodies with
|
||||
integers beyond exact native conversion), 31 lists and 7 items that do not
|
||||
reproduce, 26 without an initial recording, 12 query `POST`s and 5 documents
|
||||
read under several queries. The two browser-scenario captures that did not
|
||||
produce a state are refused inline with `resource_constraint`, as the
|
||||
environment manifest view refuses them on unchanged main.
|
||||
|
||||
Not covered here: serving tool calls from the state, the AgentEnv data plane
|
||||
and write effects, which are later increments.
|
||||
|
||||
## Linux follow-up (2026-10-20)
|
||||
|
||||
Node 24.18.0 / npm 11.16.0, with the author's `47f8a27a9` main integration
|
||||
preserved and main `d72021b0d` integrated. Eight regression cases failed across
|
||||
two pre-fix runs: membership additions/removals across a write, field presence,
|
||||
sparse field observations, and compact compound totals. The fixes compare
|
||||
membership before selecting latest recordings, retain each field's observation
|
||||
across sparse reads, and recognize `totalcount`/`itemcount`. API-base grouping
|
||||
appends to its owned arrays; identity intersection uses a set and skips the
|
||||
first response's self-intersection.
|
||||
|
||||
Executed checks:
|
||||
|
||||
- Relevant domain, application and contract suites: 39 files, 493 passed,
|
||||
3 existing skips. Includes empty/stable lists, query variants, field additions
|
||||
and removals, sparse reads, identity proof and deferred document paths.
|
||||
- Broader compiled CLI/MCP/environment, compact schema and qlog acceptance:
|
||||
7 files, 65 passed, 1 existing skip. After the final state fixes, both selected
|
||||
compiled state CLI/MCP cases passed again (36 other cases deselected).
|
||||
- Final serial static/build/artifact gate: 11 tasks passed. Typecheck, lint,
|
||||
format, knip, package metadata and runtime/catalog generation all ran.
|
||||
- Final documentation/catalog/format validation: 9 tasks passed; architecture
|
||||
guards passed across 1,602 source files; README consistency: 5 tests passed.
|
||||
|
||||
Real Gitea 28.2.0 used an isolated SQLite database. An owned Node fetch HAR
|
||||
recorder captured actual API exchanges, omitting transport credentials:
|
||||
|
||||
- Three exchanges: empty repository search, creation through `/user/repos`,
|
||||
then the same search. On exactly the same authenticated Evidence, pre-fix
|
||||
`29e745923` published one created repository as initial state; the final
|
||||
source publishes none and keeps the search recorded with `fields-differ`.
|
||||
- Eleven exchanges: initial issue list/details and topics, PATCH, PUT and
|
||||
DELETE, then subsequent reads. The state keeps the two original issues
|
||||
before ordinal 4 and the original topics document at ordinal 3. File CLI and
|
||||
default stdio MCP agree; six HTTP SDK replays reproduce original responses.
|
||||
- Actual Chromium `capture_browser_scenario`: two transactions, eight events,
|
||||
zero dropped. The state uses record order, extracts one issue, and keeps list
|
||||
and item plans; file CLI and default stdio MCP agree. The earlier 12.6 MB
|
||||
real browser capture also projects, keeping oversized bodies recorded with
|
||||
`body-unusable` rather than parsing them into state. Owned servers and browser
|
||||
processes were stopped.
|
||||
|
||||
### Resource and performance measurements
|
||||
|
||||
One CPU and one worker. Process-family RSS sums were sampled every 50 ms and
|
||||
may count shared pages twice. Final source suites: 39.669 s wall / 39.486 CPU s /
|
||||
898.42 MiB peak; broader boundaries: 270.171 s / 267.136 CPU s / 2,130.78 MiB;
|
||||
final static/build: 199.683 s / 199.523 CPU s / 4,689.07 MiB; final live HAR
|
||||
workflow: 22.836 s / 19.880 CPU s / 1,072.43 MiB; actual browser-state workflow:
|
||||
12.734 s / 10.941 CPU s / 1,549.80 MiB. The qlog acceptance lane intentionally
|
||||
exhausts an isolated decoder heap and verifies that MCP remains usable; no
|
||||
workspace OOM occurred.
|
||||
|
||||
The first follow-up benchmark used pre-fix `29e745923` and `6ad63bcc2`, the same
|
||||
completed Evidence files, Node and CPU affinity. One fresh process per revision,
|
||||
with a first call and two warm repeats; filesystem caches were not cleared.
|
||||
Complete normalized output digests matched for both synthetic projection tasks.
|
||||
|
||||
| Work | Before: first / warm (ms) | After: first / warm (ms) |
|
||||
| ----------------------------------------------- | ------------------------- | ------------------------ |
|
||||
| Group 8,000 templates (helper only) | 320.08 / 81.01–81.46 | 11.89 / 3.88–4.33 |
|
||||
| File-to-state projection, 2,000 collections | 1,321.57 / 732.00–939.57 | 1,268.76 / 781.34–811.05 |
|
||||
| File-to-state projection, one 15,001-field item | 657.05 / 494.82–501.19 | 377.78 / 347.62–380.87 |
|
||||
|
||||
Combined benchmark processes, including imports, three helper calls, six
|
||||
projections and the live-capture comparison: before 6.536 s wall / 6.496 CPU s /
|
||||
370.82 MiB peak; after 5.077 s / 5.056 CPU s / 386.53 MiB. Grouping and wide
|
||||
identity inference improved; the 2,000-collection projection has overlapping
|
||||
warm timings, and peak memory did not decrease. These few runs do not establish
|
||||
a general speedup. Model token usage was not measured; no model-task scoring
|
||||
was run. Initial external probe attempts had incorrect CLI positional arguments
|
||||
and assumed coverage counted records rather than grouped tools; those driver
|
||||
errors were corrected before the successful complete runs.
|
||||
|
||||
No new macOS/Windows runtime, state serving, AgentEnv data-plane or write-effects
|
||||
claim is made. A type first observed only after an unrelated-root write may
|
||||
still lack the earlier observation needed to detect its effect. Item-shaped
|
||||
and delete-only document paths remain recorded, as specified.
|
||||
|
||||
## Post-merge write-cutoff follow-up
|
||||
|
||||
Base `3fca2e0ca` includes #5319 and the validated immutable-capture reuse from
|
||||
#5349. The remaining review finding was repeated write-root parsing for each
|
||||
read type and again during output assembly. One admitted prefix/wildcard index
|
||||
now owns parsed write roots, with per-projection positive/negative cutoff
|
||||
caching. Backed type membership is collected once, replacing another plan scan.
|
||||
The old linear matcher is removed. Prefix, parameter, redacted-root and timeline
|
||||
semantics are preserved; no cross-request cache or new authority is introduced.
|
||||
|
||||
- Source checks: 41 files, 508 passed, 3 existing skips, including independent
|
||||
matching-oracle and 12,000-segment iterative-lookup checks.
|
||||
- Serial static/build/artifact gate: 11 tasks passed, 197.356 s wall /
|
||||
197.195 CPU s / 4,744.92 MiB peak process-family RSS.
|
||||
- Both selected compiled state CLI/MCP cases passed after rebuilding.
|
||||
- Live Gitea HAR workflows and fresh actual Chromium capture passed again:
|
||||
initial values, recorded fallbacks, CLI/default stdio parity and six exact
|
||||
HTTP replays. HAR workflow: 22.834 s wall / 19.623 CPU s / 1,067.36 MiB;
|
||||
browser workflow: 22.858 s / 20.912 CPU s / 1,546.59 MiB. Owned processes stopped.
|
||||
|
||||
The same completed Evidence with 1,000 collection reads and 1,000 unrelated
|
||||
writes was projected before and after on one CPU. Two larger full-HAR inputs
|
||||
(3,000/4,000 total records) were refused by the existing 96 MiB admission budget;
|
||||
the limit was retained. One fresh process per revision, first call plus one
|
||||
warm repeat, filesystem caches not cleared:
|
||||
|
||||
| Complete file-to-state projection | Main before | Follow-up |
|
||||
| -------------------------------------------------------- | ------------------- | ------------------- |
|
||||
| First call wall / CPU (ms) | 1,727.89 / 1,727.36 | 1,244.82 / 1,244.05 |
|
||||
| Warm call wall / CPU (ms) | 1,300.84 / 1,300.42 | 624.01 / 623.89 |
|
||||
| Process wall / CPU (s), including imports and both calls | 4.690 / 4.363 | 3.370 / 3.062 |
|
||||
| Sampled process-family peak RSS (MiB) | 365.84 | 360.03 |
|
||||
|
||||
Parent Evidence IDs and complete normalized output digests were identical.
|
||||
RSS sampling is every 50 ms, includes descendants and can double-count shared
|
||||
pages; these few runs do not establish a general memory reduction. Model token
|
||||
usage remains unmeasured. No workspace OOM occurred.
|
||||
+65
-3
@@ -1177,6 +1177,16 @@ identifiable; unknown or shared script changes select everything. Android
|
||||
build-script edits retain the Linux suite and workflow validation; real Android
|
||||
verification stays manual. Renames account for both paths.
|
||||
|
||||
The `agentenv` scope owns the environment application and server modules, the
|
||||
`environment-serve` and `environment-export` command registration, the shared
|
||||
streamable HTTP listener, `scripts/verify-agentenv-environment.mjs` and its
|
||||
workflow `real-agentenv.yml`. The lane exports a bundle from the source-owned
|
||||
Grafana-shaped capture through the built CLI, packs the checkout into the
|
||||
bundle's image and runs `agent-env run` on the hosted runner's Docker with the
|
||||
pinned `agentenv-framework` release; it passes when the smoke task passes and
|
||||
no container remains. It claims nothing about any hosted platform or another
|
||||
framework version.
|
||||
|
||||
This narrower selection trades PR-time coverage for runner capacity. A change to
|
||||
unowned shared runtime code that fails only on arm64, macOS, Windows, the Apple
|
||||
artifact lane, or a provider lane that does not own the file is not caught on
|
||||
@@ -1325,9 +1335,9 @@ Portable owner-based test selection remains observational. Its reports compare
|
||||
proposed omissions with the full run; incomplete or skipped observations do not
|
||||
establish that fewer tests would preserve regression detection.
|
||||
|
||||
The eight automatic browser, binary-layout, EVM, JavaScript-recovery,
|
||||
recorded-crash, and web provider lanes download `rea-build-cache` from their own
|
||||
run after the build and static jobs succeed. Their existing build commands still
|
||||
The nine automatic browser, binary-layout, EVM, JavaScript-recovery,
|
||||
recorded-crash, web and environment-bundle provider lanes download
|
||||
`rea-build-cache` from their own run after the build and static jobs succeed. Their existing build commands still
|
||||
validate task hashes and rebuild mismatched inputs; provider observations always
|
||||
run. Provider-workflow-only edits also admit the build without enabling source
|
||||
test shards. Direct provider dispatches retain the cross-run restore-only cache.
|
||||
@@ -1554,6 +1564,58 @@ This is fixture evidence, not a sandbox claim. Inputs remain unchanged and the
|
||||
sentinel must stay alive; owned cleanup and empty verifier descendants are required. Pass an
|
||||
installed package entrypoint as the script's first argument for package coverage.
|
||||
|
||||
### Exported environment bundles
|
||||
|
||||
`npm run verify:agentenv:environment` is the `real-agentenv` lane. It requires
|
||||
the host's Docker CLI named by absolute `REA_DOCKER_COMMAND`, Python 3.12 with
|
||||
`agentenv-framework==0.9.1334` installed in a virtual environment whose
|
||||
`agent-env` binary is named by absolute `REA_AGENT_ENV_COMMAND`, and outbound
|
||||
network for `pip` on the runner and for `npm` inside the image build. Both
|
||||
variables are required; neither upstream executable is resolved from `PATH`,
|
||||
and the verifier installs nothing. Packing uses the shared npm helper, honoring
|
||||
`npm_execpath` from the caller's selected npm. Direct Node invocations without
|
||||
that value use the helper's platform fallback, so configure the pinned toolchain
|
||||
on `PATH` for those runs. It checks the supplied runner's `--version` against the pin before
|
||||
using Docker and retains the observed version in both passing and failing receipts.
|
||||
Its disposable CI runner creates the virtual environment in runner
|
||||
storage, exports both paths and uses the runner's own Docker; no developer host
|
||||
configuration changes. `agent-env run` attempts share one 22-minute budget
|
||||
inside the 30-minute job (the first attempt at most 15 minutes), so a hung or
|
||||
retried run ends with the verifier's failed receipt rather than the runner's
|
||||
kill. The framework builds its gateway and service-db
|
||||
images from `public.ecr.aws` on every fresh runner, and hosted runners share
|
||||
that registry's anonymous pull quota, so the lane first pulls the base images
|
||||
the pinned framework's Dockerfiles name from `mirror.gcr.io`, which serves the
|
||||
same Docker official images under the same digests, and names them as those
|
||||
Dockerfiles do (on Docker's containerd image store, so digest-pinned names
|
||||
resolve), as the framework's own CI does. The quota handling below covers
|
||||
anything still pulled from that registry. The receipt's `agent_env_run` records the
|
||||
last run whether it passed or failed, with `public_ecr_quota` set to
|
||||
`data_limit` or `rate` when that registry's `429 Too Many Requests` ended it.
|
||||
The data-transfer cap (`toomanyrequests: Data limit exceeded`) is counted per
|
||||
source IP, so it fails the lane at once: a pause on the same runner cannot lift
|
||||
it. When a rate refusal ends the run and at least five minutes of the budget
|
||||
would remain after the pause, the verifier waits 90 seconds and runs once more,
|
||||
recording `attempts: 2` and `retried_for` in the receipt. Any other failure
|
||||
ends the lane at once.
|
||||
|
||||
The verifier writes the Grafana-shaped HAR the view tests own, inspects it and
|
||||
projects the `environment-manifest` view with all three template rules through
|
||||
the built CLI, packs the checkout with `npm pack`, exports the bundle with
|
||||
`--package-tarball` so the image installs this checkout rather than the
|
||||
registry package, and runs `agent-env run` on the bundle. It checks that the
|
||||
export reports the planned file set with the inputs copied byte-exact, that
|
||||
the runner exits `0` with `tasks/smoke.json` reported as passed and the
|
||||
verifier's description naming the tool count, and that no container exists
|
||||
after the run that did not exist before; the container inventory runs even when
|
||||
the run fails or times out, so a failed receipt still lists what it left. The one exception is the framework's
|
||||
own image-store helper, `agentenv-registry` (or `agentenv-registry-<port>`),
|
||||
a `registry:2` container the local provider starts on first use and keeps
|
||||
running across runs; a first run on a fresh host creates it, so the receipt
|
||||
lists it under `image_store_helpers` instead of counting it as a leak. Images
|
||||
built by the run remain and are counted in the receipt. Pass an installed
|
||||
package entrypoint as the script's first argument for package coverage.
|
||||
|
||||
## Agent evaluation and conformance records
|
||||
|
||||
`npm run verify:agent` evaluates investigation scenarios through a real local
|
||||
|
||||
@@ -17,7 +17,7 @@ harm_rank,location,trigger,current_copy,user_risk,proposed_replacement,implement
|
||||
13,src/domain/errors.ts:EvidenceIntegrityError,Evidence validation fails,Raw constructor message,May expose validation internals and lacks recovery,"Evidence is invalid or has changed. Recreate or re-import it, then try again.",implemented,focused assertions passed; full `npm run check` passed
|
||||
14,src/domain/errors.ts:UnknownRegistryError,Evidence-state mutation fails,"Residual unknown registry mutation failed: {reason}",Exposes internal subsystem and state token,"Missing IDs identify the session and retry; concurrent revision conflicts refresh current state.",implemented,focused assertions passed; full `npm run check` passed
|
||||
15,src/domain/errors.ts:ProcessCaptureError,Process capture fails,Raw constructor message,May expose process and cleanup internals,"Process capture could not complete. Check command access and limits, then try again.",implemented,focused assertions passed; full `npm run check` passed
|
||||
16,src/domain/errors.ts:AnalysisTimeoutError,Analysis deadline expires,"Analysis operation timed out after {timeoutMs}ms: {operation}",Exposes internal deadline and operation; weak recovery,"Analysis took too long. Try a smaller request, then run `rea doctor` if it continues.",implemented,focused assertions passed; full `npm run check` passed
|
||||
16,src/domain/errors.ts:AnalysisTimeoutError,Analysis deadline expires,"REA's {operation} operation reached its {timeoutMs}ms server/provider deadline; the MCP client may have ended the call earlier",Explains the two deadline owners and avoids blind retry,"Inspect session/provider state first. If the MCP client ended the call, raise its tool deadline above {timeoutMs}ms; otherwise retry with a smaller request and do not repeat an unchanged call.",implemented,verified by projection tests and real MCP/Codex CLI timeout-recovery probes
|
||||
17,src/domain/errors.ts:HopperTimeoutError,Provider deadline expires,"Hopper did not respond within {timeoutMs}ms",Exposes provider and internal deadline,"Analysis took too long. Try a smaller request, then run `rea doctor` if it continues.",implemented,focused assertions passed; full `npm run check` passed
|
||||
18,src/domain/errors.ts:ProviderSelectionError,Deep-provider selection fails,"Analysis provider selection failed ({reason}) for {requestedProviderId}; candidates: {candidateIds}",Retains non-secret local selection context that needs a safe structured projection,Reason-specific guidance with stable candidate IDs and actionable rejection diagnostics,implemented,focused registry/CLI/MCP assertions passed; full `npm run check` passed
|
||||
20,src/domain/errors.ts:AnalysisInputError,Parsed analysis input invalid,"Invalid analysis input for {operation}",Exposes operation identifier and lacks correction,"Analysis input is invalid. Check the arguments and try again.",implemented,focused assertions passed; full `npm run check` passed
|
||||
@@ -35,7 +35,7 @@ harm_rank,location,trigger,current_copy,user_risk,proposed_replacement,implement
|
||||
24.93,src/server/registerFunctionComparisonTool.ts:probe rationale,Function comparison is incomplete,Capture complete dossiers with equal providers and analysis limits.,Exposes provider architecture and gives ambiguous equality guidance,"Capture complete dossiers for both functions under the same target context and analysis limits.",implemented,verified by focused function-comparison MCP test
|
||||
24.95,incur:CLI argument validation,Caller omits or mistypes a command argument,VALIDATION_ERROR with raw Zod Details and fieldErrors including code/path/expected/received,Exposes validator representation and buries the useful correction,"REA could not read the command arguments. Check the named argument and its expected type, then try again.",implemented,verified by sanitizer unit tests and real dispatcher missing-argument test
|
||||
25,src/application/ReferenceSourceImport.ts:cancelled,Reference-source import is cancelled,Reference source import cancelled,No recovery timing guidance,"Reference source import was cancelled. Start it again when ready.",implemented,focused assertions passed
|
||||
26,src/application/ReferenceSourceImportPolicy.ts:invalid-limits,Configured import limits are not positive integers,Import limits must be positive integers,Fixed importer ceilings prevented full source imports,Removed fixed byte, entry, depth, and path limits.,removed,verified by greater-than-10000 entry and greater-than-16-MiB regressions
|
||||
26,src/application/ReferenceSourceImportPolicy.ts:invalid-limits,Configured import limits are not positive integers,Import limits must be positive integers,Fixed importer ceilings prevented full source imports,"Removed fixed byte, entry, depth, and path limits.",removed,verified by greater-than-10000 entry and greater-than-16-MiB regressions
|
||||
27,src/application/ReferenceSourceImportPolicy.ts:invalid-root,Source path cannot resolve or is not a directory,Reference source root could not be resolved / Reference source root is not a directory,Multiple low-level copies and no complete path check,"Reference source directory could not be opened. Check that the path exists, is readable, and points to a directory.",implemented,focused assertions passed
|
||||
28,src/application/ReferenceSourceImportPolicy.ts:policy,No approved root exists or requested root is outside policy,No approved reference source roots / Reference source root is outside approved roots,Approval friction duplicates direct caller path selection,Removed the configured root allowlist and permission gate; caller-selected directories are validated and traversed directly.,removed,verified by source search and focused import tests
|
||||
29,src/application/ReferenceSourceImport.ts:io,Reader fails during traversal,Raw reader error message,May expose filesystem details and inconsistent recovery,"Reference source files could not be read. Check directory permissions and try again.",implemented,focused assertions passed
|
||||
@@ -72,7 +72,7 @@ harm_rank,location,trigger,current_copy,user_risk,proposed_replacement,implement
|
||||
59,src/application/Setup.ts:unsupported macOS,macOS version is missing or older than 12,Upgrade to macOS 12 or newer.,Low risk; exact support floor,Keep current copy,unchanged,verified by setup host tests
|
||||
60,src/application/Setup.ts:unsupported Linux,Linux distribution is outside supported set,"REA supports Hopper on Ubuntu 24.04+, Fedora 41+, Nobara 44+, 64-bit Arch Linux, and CachyOS.",Low risk; exact supported alternatives,Keep current copy,unchanged,verified by setup unit test
|
||||
61,src/application/process/ProcessCli.ts:readJson read,Process scenario or evidence file cannot be read,Raw filesystem exception through CLI framework,Can expose local paths/codes and no recovery,"Process input file could not be read. Check that the path exists and is readable.",implemented,verified by focused CLI test
|
||||
62,src/application/process/ProcessCli.ts:readJson size,Process scenario or evidence file exceeds 64 MiB,Process JSON input exceeds byte limit,Does not state limit or recovery,"Process input file is too large. Reduce it below 64 MiB, then try again.",implemented,verified by focused sparse-file CLI test
|
||||
62,src/application/JsonInputFile.ts:memory admission,Projected process JSON value exceeds available heap headroom,Insufficient heap headroom to materialize the complete JSON value,Heap admission depends on live runtime capacity rather than a fixed 64 MiB file limit,"Provide a smaller valid JSON value. For Evidence workflows, re-analyze a smaller selection of the original target and use its complete Evidence; splitting JSON text or trimming Evidence fields does not produce valid workflow input.",implemented,Source verified against shared JSON reader and recovery constant; former fixed-size refusal is superseded
|
||||
63,src/application/process/ProcessCli.ts:readJson parse,Process scenario or evidence file is malformed JSON,Raw JSON parser exception through CLI framework,Can expose parser implementation and lacks recovery,"Process input file is not valid JSON. Repair the file, then try again.",implemented,verified by focused CLI test
|
||||
64,src/application/process/ProcessCli.ts:capture scenario parse,Scenario fails Process Capture schema,Raw Zod validation exception through CLI framework,Exposes schema internals and field paths without stable recovery,"Process scenario is invalid. Check its required fields and limits, then try again.",implemented,verified by focused CLI test
|
||||
66,src/application/process/ProcessCli.ts:wrong evidence,Comparison receives valid Evidence from another workflow,Expected REA process capture Evidence,Uses internal schema name and does not explain recovery,"Capture evidence is not from the current process-capture workflow. Create new capture evidence, then try again.",implemented,verified by focused CLI test
|
||||
@@ -114,5 +114,5 @@ Then rerun this installer.",implemented,verified by shell branch inspection
|
||||
88,install.sh:binary absent,npm exits successfully without executable,the rea command was not installed at local path,Leaks local path and no recovery,"npm completed without installing the rea command. Check the npm global bin directory and PATH, then retry.",implemented,verified by exact absent-installed-command test
|
||||
89,install.sh:version read,Installed rea executable cannot report its version,could not read the installed REA version.,No recovery instruction,"The installed REA version could not be read. Reinstall the requested version, then retry.",implemented,verified by exact unreadable-installed-version test
|
||||
90,install.sh:version mismatch,Installed rea reports a different version,installed version ACTUAL does not match EXPECTED.,Low risk; gives exact integrity mismatch,Keep current copy,unchanged,verified by exact installer test
|
||||
90.5,@modelcontextprotocol/server:tool input validation,Caller sends arguments that violate a declared tool schema,"Input validation error: Invalid arguments for tool TOOL: FIELD: Invalid input: expected TYPE, received TYPE",Low risk; public tool and field names plus exact type correction,Keep SDK-generated copy,unchanged,verified by real in-memory MCP client/server call with wrong argument type
|
||||
90.5,@modelcontextprotocol/server:tool input validation,Caller sends arguments that violate a declared tool schema,"Canonical invalid_request with the SDK diagnostic plus recovery.kind=inspect_input_contract and a bounded instruction to read the advertised schema or call tools/list",Keeps field/type correction while telling the agent how to recover; diagnostics over 4 KiB stay on the existing retained-error path,Normalize ordinary validation failures and retain oversized diagnostics,verified by real in-memory and stdio MCP client/server calls with wrong argument types
|
||||
90.6,incur:unknown CLI command,Caller enters an unknown REA command,"'COMMAND' is not a command for 'rea'. Suggested command: rea --help",Low risk; identifies input and exact discovery action,Keep framework-generated copy,unchanged,verified by real packaged dispatcher invocation
|
||||
|
+219
-31
@@ -122,29 +122,96 @@ XML node tree; optional `_w.txt` and `_g.txt` files retain opaque WebSocket and
|
||||
gRPC message bytes. Missing members remain null, distinct from empty bodies.
|
||||
See Telerik's [SAZ format reference](https://www.telerik.com/fiddler/fiddler-everywhere/documentation/knowledge-base/fiddler-archives).
|
||||
|
||||
The existing ZIP reader verifies read members; expanded archive contents are
|
||||
limited to 32 MiB in addition to the input/output budgets below. Exceeding this
|
||||
expanded-byte budget returns `invalid_request` / `invalid_input` through both
|
||||
CLI and MCP, with an `out_of_range` issue at
|
||||
`capture_path` and `maximum_expanded_capture_bytes`. Selecting records does not
|
||||
bypass the complete-archive admission check or return partial Evidence. Encrypted
|
||||
archives and unrecognized raw session members are unsupported. SAZ requires an
|
||||
enforced decoder memory bound: Node's fixed heap, or the Windows Job Object
|
||||
commit limit. Bun on hosts without that Job Object limit returns capability
|
||||
unavailable before starting the SAZ decoder. Non-session
|
||||
members are inventoried without decoding their content. Original files remain
|
||||
unchanged. Linux verification includes independently checked public recorded
|
||||
Mattermost captures from [FOREST](https://github.com/dfrc-korea/FOREST/tree/405c9154658a128ea203a6c10040cec55111ed03/api-results/dataset)
|
||||
and the Fiddler author's [encoding corpus](https://github.com/ericlaw1979/Clearinet/blob/b6faf78cde1e72042980364f180ceb34b77d1434/Test/data/Encodings.saz),
|
||||
plus synthetic malformed/limit fixtures. Their exporting product versions are
|
||||
not authenticated. An interactive export from a current Fiddler Everywhere
|
||||
installation and its live MCP server remain unverified. Support here is the
|
||||
bounded documented raw HTTP/XML SAZ layout, not every product/version profile.
|
||||
## Protobuf and gRPC bodies
|
||||
|
||||
For visual inspection, use Everywhere's **Import Snapshot**, then **Inspectors**
|
||||
and **Overview**. HAR remains an alternative: select sessions, choose
|
||||
**Export > HTTPArchive**, and confirm `log.version` is `"1.2"`. Preserve the
|
||||
original SAZ when exporting. See the [import/export instructions](https://www.telerik.com/fiddler/fiddler-everywhere/documentation/collaboration/exporting-and-importing).
|
||||
Selecting `record_ordinals` (CLI `--record`) also decodes retained bodies with
|
||||
recognized protobuf or gRPC content types. Whole-capture inspection does not
|
||||
perform this extra work. The original binary sidecar, SHA-256 and producer
|
||||
ordinal remain the source; decoding adds flat body, frame and field facts.
|
||||
|
||||
Native gRPC admits only data-frame flags 0/1; gRPC-web additionally admits its
|
||||
terminal trailer frame. `grpc-web-text` supports independently padded base64
|
||||
chunks. Its offsets refer to **base64-decoded body bytes**; other offsets refer
|
||||
to retained body bytes. Every field has a body-scoped `field_id` derived from
|
||||
its byte offset, a `frame_index`, and a `parent_field_id` for schema-driven
|
||||
nested messages or groups. Repeated field numbers remain separate occurrences.
|
||||
|
||||
Without a schema, varints and fixed-width values retain exact unsigned decimal
|
||||
representations. Length-delimited UTF-8, nested-message and packed readings
|
||||
are candidates; REA does not choose one or invent field names or signedness.
|
||||
Nested candidate ranges remain accessible without recursively expanding them.
|
||||
Malformed messages retain the parsed prefix and an exact error offset.
|
||||
Compressed frames and encoded HTTP bodies remain unknown; they are not
|
||||
silently treated as empty messages.
|
||||
|
||||
For schema interpretation, supply a local compiled `FileDescriptorSet` and its
|
||||
fully qualified message type. All imports must be included. REA never fetches
|
||||
schemas or executes the selected application. Descriptor identity and parser
|
||||
version remain in the body facts. Scalars, repeated numeric packing and nested
|
||||
messages are interpreted alongside the unchanged wire facts; disagreements
|
||||
remain explicit. Map semantics, extensions and required-field validation are
|
||||
unsupported. A supplied schema does not establish the sender's actual schema.
|
||||
An invalid nested interpretation records `schema_status: "invalid-message"`
|
||||
and `schema_error` locally; later outer fields remain available. Valid string
|
||||
contents, including a leading U+FEFF, are preserved.
|
||||
|
||||
```bash
|
||||
# Compile your own schema with your existing protoc, including local imports:
|
||||
protoc --include_imports --descriptor_set_out=/evidence/api.pb api.proto
|
||||
rea inspect-web-network-capture /evidence/session.har har --record 3 \
|
||||
--protobuf-schema /evidence/api.pb --protobuf-message example.Response \
|
||||
--detail summary --evidence_output /evidence/session-evidence.json --json
|
||||
```
|
||||
|
||||
MCP accepts the same choices as `protobuf: {schema: {descriptor_set_path:
|
||||
"/evidence/api.pb", message_type: "example.Response"}}`, with explicit
|
||||
`record_ordinals`. Optional `body_pointers` batch-select exact binary pointers;
|
||||
`framing` can explicitly select `raw`, `grpc`, `grpc-web` or `grpc-web-text`
|
||||
when MIME metadata is unavailable. CLI exposes `--protobuf-body-pointer` and
|
||||
`--protobuf-framing`. Filters never invent bytes excluded by source redaction. Decoding an extra
|
||||
base64 layer also checks explicit exclusions; newly recognized trailer
|
||||
credentials exclude the entire affected binary sidecar and its reported
|
||||
encoding, preserving a redaction coordinate instead of a credential digest.
|
||||
Credential inspection covers every selected body before descriptor reads or
|
||||
field expansion. Later capacity or schema failures can therefore retain the
|
||||
sanitized source even with explicit sensitivity options. If that source pass
|
||||
is interrupted, unchecked bodies are not published as partial Evidence.
|
||||
Framing overrides apply only to format-owned body coordinates and preserve
|
||||
exclusions for the transport declared in MIME metadata.
|
||||
Unknown or explicitly excluded HTTP coding metadata is not treated as absent:
|
||||
SAZ/native bodies remain opaque until identity coding is established. Raw
|
||||
header values and retained body bytes remain available in source Evidence.
|
||||
Skipping wire analysis still runs the independent credential check. If HTTP
|
||||
coding metadata is unavailable and that check cannot complete, the operation
|
||||
returns unavailable without publishing unchecked capture bodies as Evidence.
|
||||
|
||||
Use `detail: "summary"`, then `inspect_analysis_view` with its retained parent:
|
||||
|
||||
```json
|
||||
{"kind":"protobuf","collection":"bodies","offset":0,"limit":16}
|
||||
{"kind":"protobuf","collection":"frames","record_ordinals":[3],"offset":0,"limit":16}
|
||||
{"kind":"protobuf","collection":"fields","record_ordinals":[3],"field_numbers":[1,4],"fields":["number","schema_name","schema_value"],"offset":0,"limit":32}
|
||||
```
|
||||
|
||||
Filters intersect; an empty filter selects nothing. Field pages also accept
|
||||
`field_ids`, `frame_indices` and `schema_names`. Default field pages omit `text`,
|
||||
`schema_value` and enum alias inventories; select these columns when needed. Identity, source offset
|
||||
basis and body status survive column selection. Complete Evidence retains
|
||||
all decoded values, and ordinary capture previews expose counts. Page exhaustion
|
||||
covers retained facts only; a partial source does not establish absence.
|
||||
|
||||
Authenticated immutable parents reuse validation and the most recent field
|
||||
filter per capture. These caches live only with retained Evidence, hold references
|
||||
to existing facts and never reopen either file. Cached selector text is bounded
|
||||
by retained field cardinality; larger selections use an uncached scan. Mutable
|
||||
input is revalidated.
|
||||
The existing 96 MiB report budget includes the original decoder reply plus
|
||||
conservative charges for new facts, strings and descriptor expansion. The explicit
|
||||
`protobuf_coverage` reports selected and inspected record counts, preserving
|
||||
partial decoding through composed views. Capacity failure retains the source
|
||||
observation as partial Evidence; cancellation yields
|
||||
between bounded work batches. Real elapsed time and process-family memory
|
||||
still depend on the selected capture and schema.
|
||||
|
||||
## Fiddler Everywhere MCP and public skills
|
||||
|
||||
@@ -300,7 +367,15 @@ exclusions and deduplicated limitations. It scans path spans without splitting
|
||||
literal paths into arrays. Before schema parsing, it reserves the capture clone
|
||||
and validation scratch by walking the parent without cloning it. Inline requests
|
||||
borrow the Evidence value through CLI/MCP envelope validation; the application
|
||||
admits the entire portable envelope before authenticated parsing or hashing.
|
||||
admits the entire portable envelope before authenticated parsing or hashing. An
|
||||
`evidence-file` source is read and authenticated first and then admitted like
|
||||
retained Evidence, so its strings are not charged as inline envelope text: a
|
||||
capture too large to project inline can be projected from its file. Before
|
||||
authentication REA checks that live heap headroom holds its copies of the parsed
|
||||
Evidence, and over MCP the read follows the request's cancellation.
|
||||
An invalid Evidence file returns `EvidenceFileError` with the selected `path`
|
||||
and `content_issues` relative to the file's JSON root; those locations do not
|
||||
refer to the request's `source.path` string.
|
||||
The envelope clone, hash scratch and capture clone stay charged during grouping.
|
||||
Temporary URL/path/MIME/query work and retained state must
|
||||
fit together within a 96 MiB projection representation budget. A whole group or
|
||||
@@ -334,6 +409,13 @@ whose content types are JSON-like. Every other group is listed under
|
||||
`excluded` with reason `other-origin`, `ui-page-or-asset` or `non-json`, and
|
||||
retained records without a usable request line are counted in a limitation.
|
||||
|
||||
The parent may instead be `capture_browser_scenario` Evidence whose scenario
|
||||
selected response bodies or header values: each network transaction is then
|
||||
one record, in request order, and the result names
|
||||
`parent_operation: "capture_browser_scenario"` with an empty `artifact`, since
|
||||
a scenario records no file. See
|
||||
[agent environments](agent-environments.md#from-a-browser-scenario).
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_analysis_view",
|
||||
@@ -367,11 +449,12 @@ template and `params` carry the four format rules with names `id`, `uuid`,
|
||||
only when listed, each at most once (a repeated rule is an `invalid_request`),
|
||||
in this order:
|
||||
|
||||
| Rule | Applies to | Parameter |
|
||||
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
|
||||
| `after-uid-literal` | The literal segment right after a literal `uid`, unless it is a stop word | `uid` |
|
||||
| `opaque-token` | A lowercase alphanumeric segment of twelve or more characters mixing letters and digits that is not a `v1`-style version | `uid` |
|
||||
| `sibling-variance` | Groups equal in method, origin and segment count that differ in one literal at index 2 or later; the siblings whose literal is identifier-like (a digit or `:`, or three or more hyphenated names under a plural literal) merge, never a stop word, and the other literals stay their own tools | singular previous literal plus `_id`, else `id` |
|
||||
| Rule | Applies to | Parameter |
|
||||
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------- |
|
||||
| `after-uid-literal` | The literal segment right after a literal `uid`, unless it is a stop word | `uid` |
|
||||
| `opaque-token` | A lowercase alphanumeric segment of twelve or more characters mixing letters and digits that is not a `v1`-style version | `uid` |
|
||||
| `sibling-pair-variance` | Groups equal in method, origin and segment count that differ in two adjacent literals right after a plural literal at index 2 or later and followed by a literal, with at least three distinct pairs and both positions varying (`/repos/{owner}/{repo}/issues`); stop words and `v1`-style versions never qualify | singular plural literal plus `_namespace`, then `_name` |
|
||||
| `sibling-variance` | Groups equal in method, origin and segment count that differ in one literal at index 2 or later; the siblings whose literal is identifier-like (a digit or `:`, or three or more hyphenated names under a plural literal) merge, never a stop word, and the other literals stay their own tools | singular previous literal plus `_id`, else `id` |
|
||||
|
||||
With `sibling-variance` selected, literal siblings within its comparison scope
|
||||
fold into existing opt-in parameters as observed values unless they are stop words.
|
||||
@@ -388,7 +471,7 @@ cut to their first label, `_by_<params>` appended when parameters exist,
|
||||
sanitized to `[a-z0-9_]`, at most 60 characters and made unique with numeric
|
||||
suffixes: `get_dashboards_by_uid`, `get_plugins_settings_by_plugin_id`.
|
||||
|
||||
`sibling-variance` compares routes of at most 32 segments; longer routes keep
|
||||
`sibling-variance` and `sibling-pair-variance` compare routes of at most 32 segments; longer routes keep
|
||||
their grouped templates and the manifest says so in a limitation. `body_shape`
|
||||
is derived for `POST`, `PUT` and `PATCH` from the tool's earliest record when it
|
||||
carries an `application/json` or `application/*+json` request body: top-level
|
||||
@@ -400,14 +483,119 @@ whenever it is nonzero the Evidence is inferred, otherwise derived. The view
|
||||
performs no network activity and replays nothing. Native mitmproxy, SAZ and
|
||||
qlog captures, and parents inspected with `sensitive_values`, return the same
|
||||
typed unsupported errors as the api-surface view, including the canonical
|
||||
token `sensitive_values_unretained`.
|
||||
token `sensitive_values_unretained`. A `capture_browser_scenario` parent is not
|
||||
refused for declaring secrets: the capture redacted them as it recorded, a
|
||||
transaction whose URL was redacted is left unrepresented, and a response body
|
||||
keeps the capture's redaction markers, which a manifest limitation counts.
|
||||
|
||||
The manifest accumulator shares the api-surface admission model: the capture
|
||||
clone, the inline envelope, record pointers, groups, every route part, observed
|
||||
clone, the inline envelope (for an inline source), record pointers, groups, every route part, observed
|
||||
values, query names and body-shape keys are admitted before allocation, and each
|
||||
recorded body is charged as temporary bytes before it is parsed. Retained state and
|
||||
temporary work must fit together within the 96 MiB projection representation
|
||||
budget; exceeding it returns `resource_constraint` without a partial manifest.
|
||||
Name a large capture with `{"kind": "evidence-file", "path": "/abs/capture.json"}`
|
||||
rather than inline: response bodies the manifest never reads are then charged
|
||||
once per value in the capture clone, not per character. A
|
||||
`capture_browser_scenario` parent is also charged, before validation, for
|
||||
checking its largest body and for the decoded text of its request bodies.
|
||||
|
||||
## Environment state
|
||||
|
||||
Pass the same parent to `inspect_analysis_view` with
|
||||
`view: {"kind":"environment-state"}` to extract the initial state the
|
||||
capture's reads observed, and a render plan per read tool that reproduces those
|
||||
reads from it. Where the environment manifest names an application's tools, the
|
||||
state holds its data: entities by type and identity, plus documents for written
|
||||
paths whose reads are not entities. Use the `template_rules` of the manifest the
|
||||
state accompanies so tool names match; the view groups records the same way.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "inspect_analysis_view",
|
||||
"arguments": {
|
||||
"source": { "kind": "retained-evidence", "evidence_id": "ev_..." },
|
||||
"view": {
|
||||
"kind": "environment-state",
|
||||
"template_rules": [
|
||||
"after-uid-literal",
|
||||
"opaque-token",
|
||||
"sibling-variance"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Records on the primary origin are ordered by recorded start time, or by record
|
||||
order when any start time is missing (`timeline`), as for a
|
||||
`capture_browser_scenario` parent. A `POST`, `PUT`, `PATCH` or `DELETE` with a
|
||||
2xx or 3xx status, or no status, may change state, unless it is a `POST` whose
|
||||
path has a segment such as `search`, `query`, `list`, `find`, `fuzzy`,
|
||||
`count` or `stats`, which queries state instead. A type's state comes from its
|
||||
successful reads that finished before the first such write under the same root:
|
||||
the path's API base (the literal segments every tool sharing its first segment
|
||||
has), any `v1`-style version segments, then one more segment. A read that
|
||||
overlaps that write in time is left out. A write whose URL a scenario redacted
|
||||
has an unknown target, so it ends the state of every type, and a limitation
|
||||
counts such writes. Values from a `capture_browser_scenario` parent keep the
|
||||
capture's redaction markers in place of declared secrets; the state is not
|
||||
refused for them, as the manifest is not.
|
||||
|
||||
Each `GET` tool whose template ends in a parameter is an item tool, and its
|
||||
type's `collection_template` is the template without that parameter; any other
|
||||
`GET` tool is a list tool of its own template. An item tool proves its type's
|
||||
`id_field` when every initial response carries the requested path value in the
|
||||
same field, at the root or in one wrapper member (`id_basis: "path-parameter"`).
|
||||
Otherwise the field is the first of `id`, `uid`, `uuid`, `_id`, `ID`, `UID`,
|
||||
`Id`, `guid`, `key`, `slug` and `name` that every listed item carries as a
|
||||
distinct string or number (`id_basis: "field-name"`). A list is the response
|
||||
array, or the one array of objects in an envelope that is not itself an entity.
|
||||
|
||||
`entities` merge every initial observation of one identity within one
|
||||
collection path: an item view's latest values win over a list view's, a view
|
||||
whose latest values differ keeps them in `view_fields`, and a field whose value
|
||||
changed between one tool's observations with no recorded write between is named
|
||||
in the type's `volatile_fields`, keeping the latest value. A value that changed
|
||||
across a write under another root may be that write's effect, so every tool of
|
||||
its type is a recorded view with reason `fields-differ`; so is a value one tool
|
||||
read before a write and another tool read after it. Repeated reads of one full request line also keep the type recorded when list membership changes across a write, including transitions to or from an empty list, or when entity fields are added or removed. Different query strings remain distinct request lines. Sparse responses do not erase the last observation of a field supplied by another request line. Entities are ordered
|
||||
by type, collection and first sighting; that order is a list's state order.
|
||||
Every entity cites its `record_ordinals`.
|
||||
|
||||
`views` lists one plan per read tool in tool order:
|
||||
|
||||
| Shape | Plan |
|
||||
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `item` | The entity object at `pointer` (the root or one member), the ordered `fields` the view shows and the `required_fields` every recording had, and `envelope_ordinal`: the recording whose response, with the entity replaced, every item response shares |
|
||||
| `list` | The array at `pointer`, its `fields`, `membership` (`all`: every entity of the collection in state order, when each recording listed exactly that, had no query, no item-total member (`total`, `count`, `totalCount`, `item_count`, `totalcount`, `itemcount`, but not `total_pages` or `per_page`; a number or a string of digits) differing from the number listed, and no `has_more`, `next` or next-page token member set; `recorded`: the entities each recording listed) and `counters` that equal the number listed |
|
||||
| `document` | The path's latest initial value, held in `documents` |
|
||||
| `recorded` | A `reason` the tool replays its recordings instead |
|
||||
|
||||
An item or list plan is kept only when it reproduces the latest initial
|
||||
recording of each request line from the state, comparing JSON values without
|
||||
regard to member order; a volatile field compares its recorded value. Each
|
||||
plan lists the `checked_record_ordinals`. Otherwise the tool is a recorded view
|
||||
with reason `envelope-varies`, `fields-differ` or `list-differs`; the other
|
||||
reasons are `query-body` (a query `POST`), `no-init-recording`,
|
||||
`body-unusable`, `not-entity`, `no-identity` and `query-varies` (a document
|
||||
read under several queries). Documents are held only for list-shaped paths
|
||||
that a `POST`, `PUT` or `PATCH` tool writes: a non-entity read of an item path
|
||||
such as `/api/files/{id}`, or of a path that is only deleted, stays recorded
|
||||
with `no-identity` or `not-entity`. A body is usable when its content type is
|
||||
JSON-like, it is UTF-8 text of at most 256 KiB, every number survives native
|
||||
conversion exactly, and no member is named `__proto__`.
|
||||
|
||||
Values in the state are recorded data, so the view refuses the same parents as
|
||||
the manifest, including those inspected with `sensitive_values`, and a
|
||||
`capture_browser_scenario` parent must have selected response bodies. Types and
|
||||
identity are inferences, so the Evidence is always inferred. `unknowns` states
|
||||
that the state establishes no authorization model, no way the application
|
||||
computes responses or derived values, and no effects of writes. The state
|
||||
accumulator charges the manifest's admission and grouping, every record line,
|
||||
each body as temporary bytes before it is decoded and parsed, and every value
|
||||
it keeps, within the same 96 MiB budget. The view performs no network activity
|
||||
and serves nothing.
|
||||
|
||||
## Resource and lifecycle boundaries
|
||||
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# Analysis efficiency measurements
|
||||
|
||||
The [native inventory view checkpoint](native-inventory-views.md) compares
|
||||
complete delivery with summaries and selected rows on real `true` and jq
|
||||
inventories, including oversized-response recovery, portable old Evidence,
|
||||
CLI/MCP parity, tokenizer estimates and independent-engine variation.
|
||||
|
||||
The [retained-capture validation checkpoint](retained-capture-validation.md)
|
||||
reconciles the earlier audit queue and measures immutable historical-capture
|
||||
validation reuse on public compact and large qlogs, including first-view costs,
|
||||
small-input limits and unchanged Evidence delivery.
|
||||
|
||||
The [Ghidra Evidence projection checkpoint](ghidra-evidence-projection.md)
|
||||
measures removal of redundant raw copies on real `true` and jq workflows,
|
||||
with complete provenance, unchanged response budgets, native CLI/MCP checks
|
||||
and explicit cold-analysis variance.
|
||||
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,719 @@
|
||||
{
|
||||
"issue": 5337,
|
||||
"initial_base_revision": "4e716b06078d725a53234503cbefbb340731e809",
|
||||
"integration_base_revision": "3fca2e0ca6e54ba700c286d9a85a9600dee6a19d",
|
||||
"environment": {
|
||||
"platform": "Linux x86_64",
|
||||
"node": "24.18.0",
|
||||
"npm": "11.16.0",
|
||||
"mcp_sdk": "2.3.1",
|
||||
"ghidra": "12.1.4",
|
||||
"java": "Eclipse Adoptium 21.0.12.1+1-LTS",
|
||||
"configured_java_heap": "768M",
|
||||
"configured_active_processors": 1,
|
||||
"query_cpu_quota_percent": 100,
|
||||
"query_cpu_affinity": [15],
|
||||
"query_hard_memory_bytes": 4294967296,
|
||||
"query_early_stop_bytes": 3221225472,
|
||||
"query_task_limit": 192,
|
||||
"swap_bytes": 0,
|
||||
"os_caches": "warm and unflushed; fresh servers, homes and Java analysis",
|
||||
"initial_startup_and_rpc_deadline_ms": 300000,
|
||||
"integrated_startup_and_rpc_deadline_ms": 600000,
|
||||
"product_default_deadlines_changed": false
|
||||
},
|
||||
"method": {
|
||||
"task": "Full observed procedure inventory, overview/load image and first internal non-thunk function facts; repeat inventory/pseudocode, export Evidence and close.",
|
||||
"calls": 15,
|
||||
"catalog_discoveries": 1,
|
||||
"fact_comparison_excludes_only": ["evidence_id", "raw_result"],
|
||||
"bytes": "Node JSON.stringify UTF-8 of complete SDK catalog, tool name/argument objects and reply envelopes; initialization, wire framing and model rendering unmeasured.",
|
||||
"actual_model_usage": null,
|
||||
"tokenizer_estimates": null,
|
||||
"memory": "Kernel cgroup family peak including file cache at payload exit; post-GC retained heap unmeasured.",
|
||||
"counter_scope": "Verified payload exit while the observer remains alive; final whole-scope counters after removal unavailable.",
|
||||
"latency_claim": "No whole-task latency, CPU or peak-memory improvement established.",
|
||||
"candidate_control_overhead": "Baseline fact-file parsing/comparison is included on the candidate side; its isolated cost is unmeasured."
|
||||
},
|
||||
"runtime_tables": {
|
||||
"baseline": {
|
||||
"files": 1841,
|
||||
"sha256_sorted_path_tab_digest_lines": "549bb479ed41e9c55505c169e726552b632f0e5341e63194380b8d0ded344658",
|
||||
"revision": "4e716b06078d725a53234503cbefbb340731e809"
|
||||
},
|
||||
"candidate": {
|
||||
"files": 1841,
|
||||
"sha256_sorted_path_tab_digest_lines": "0ebceab15311cc5ccdcfb92a0891022fc37736f2e7d01e2c1d22e88920df1ca8",
|
||||
"base_revision": "4e716b06078d725a53234503cbefbb340731e809",
|
||||
"source_diff_sha256": "7c460367e77daf2fad507800f1346dcd9e74553999fb049232b33b27684c8f49"
|
||||
},
|
||||
"integrated-baseline": {
|
||||
"files": 1847,
|
||||
"sha256_sorted_path_tab_digest_lines": "0d1672378dd3989663f7ef9ead3a91f56283dd06868f4574cae4d062dc83b041",
|
||||
"base_revision": "3fca2e0ca6e54ba700c286d9a85a9600dee6a19d"
|
||||
},
|
||||
"integrated-candidate": {
|
||||
"files": 1847,
|
||||
"sha256_sorted_path_tab_digest_lines": "d4348d25b4b18e371ac63375accd8f14e72197588934f66b8ab2bb7049ddb7d3",
|
||||
"base_revision": "3fca2e0ca6e54ba700c286d9a85a9600dee6a19d",
|
||||
"source_diff_sha256": "7c460367e77daf2fad507800f1346dcd9e74553999fb049232b33b27684c8f49",
|
||||
"driver_sha256": "b0463f8da110dcbae45769045a690dc5accf74791fa53f5086a60e8383f074de"
|
||||
}
|
||||
},
|
||||
"driver_versions": {
|
||||
"earlier": "b5bf4b378102de16f871c5f13e7302cbdcecbe0a14cc90b30ab80031920aaa06",
|
||||
"integrated": "b0463f8da110dcbae45769045a690dc5accf74791fa53f5086a60e8383f074de"
|
||||
},
|
||||
"completed_tasks": [
|
||||
{
|
||||
"name": "baseline-true-4",
|
||||
"expected_projection": "duplicate",
|
||||
"artifact_sha256": "c79bf44242829108e323378531f4ac839513ca1fba45efd6583643526e1e9fd2",
|
||||
"artifact_bytes": 35664,
|
||||
"procedure_count": 231,
|
||||
"selected_procedure": "0x102000",
|
||||
"facts_sha256": "5b826454c00709fb26777e3b6663aa69ce879f51597c54d6ec3021fc1984eb74",
|
||||
"call_count": 15,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1132.755321,
|
||||
"bytes": 3255374,
|
||||
"sha256": "8a646b95cc5607ee57e5a3c0ef480b77e4cec7e5168498e72f511a8af40daf61"
|
||||
},
|
||||
"rpc_argument_bytes": 908,
|
||||
"rpc_reply_bytes": 473344,
|
||||
"catalog_arguments_replies_bytes": 3729626,
|
||||
"normalized_value_bytes_once_per_selected_reply": 94357,
|
||||
"raw_value_bytes_once_per_selected_reply": 90839,
|
||||
"bundle_bytes": 177922,
|
||||
"phases": {
|
||||
"open_binary": {
|
||||
"elapsed_ms": 913.6441110000001,
|
||||
"reply_bytes": 499
|
||||
},
|
||||
"list_documents": {
|
||||
"elapsed_ms": 27770.679519,
|
||||
"reply_bytes": 6029
|
||||
},
|
||||
"list_procedures": {
|
||||
"elapsed_ms": 156.53601400000116,
|
||||
"reply_bytes": 125181
|
||||
},
|
||||
"binary_overview": {
|
||||
"elapsed_ms": 101.32018699999753,
|
||||
"reply_bytes": 17387
|
||||
},
|
||||
"inspect_native_load_image": {
|
||||
"elapsed_ms": 265.31070799999725,
|
||||
"reply_bytes": 101123
|
||||
},
|
||||
"procedure_info": {
|
||||
"elapsed_ms": 80.39036699999997,
|
||||
"reply_bytes": 7643
|
||||
},
|
||||
"procedure_pseudo_code": {
|
||||
"elapsed_ms": 81.57453700000042,
|
||||
"reply_bytes": 6549
|
||||
},
|
||||
"procedure_assembly": {
|
||||
"elapsed_ms": 53.14257799999905,
|
||||
"reply_bytes": 6715
|
||||
},
|
||||
"procedure_callers": {
|
||||
"elapsed_ms": 22.954539999998815,
|
||||
"reply_bytes": 6709
|
||||
},
|
||||
"procedure_callees": {
|
||||
"elapsed_ms": 8.837930000001506,
|
||||
"reply_bytes": 6753
|
||||
},
|
||||
"analyze_function": {
|
||||
"elapsed_ms": 294.8276279999991,
|
||||
"reply_bytes": 56545
|
||||
},
|
||||
"warm-inventory": {
|
||||
"elapsed_ms": 49.338327999998,
|
||||
"reply_bytes": 125181
|
||||
},
|
||||
"warm-pseudocode": {
|
||||
"elapsed_ms": 16.670878999997512,
|
||||
"reply_bytes": 6549
|
||||
},
|
||||
"export_evidence_bundle": {
|
||||
"elapsed_ms": 51.058947999998054,
|
||||
"reply_bytes": 312
|
||||
},
|
||||
"close_binary": {
|
||||
"elapsed_ms": 494.6449689999936,
|
||||
"reply_bytes": 169
|
||||
}
|
||||
},
|
||||
"scope_seconds": 34.369,
|
||||
"payload_cpu_seconds": 33.150037,
|
||||
"payload_family_peak_bytes": 993923072,
|
||||
"payload_cleanup_verified": true,
|
||||
"zero_limit_events_at_payload_exit": true,
|
||||
"final_whole_scope_counters_verified": false
|
||||
},
|
||||
{
|
||||
"name": "candidate-true-1",
|
||||
"expected_projection": "compact",
|
||||
"artifact_sha256": "c79bf44242829108e323378531f4ac839513ca1fba45efd6583643526e1e9fd2",
|
||||
"artifact_bytes": 35664,
|
||||
"procedure_count": 231,
|
||||
"selected_procedure": "0x102000",
|
||||
"facts_sha256": "5b826454c00709fb26777e3b6663aa69ce879f51597c54d6ec3021fc1984eb74",
|
||||
"call_count": 15,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1208.093488,
|
||||
"bytes": 3255374,
|
||||
"sha256": "8a646b95cc5607ee57e5a3c0ef480b77e4cec7e5168498e72f511a8af40daf61"
|
||||
},
|
||||
"rpc_argument_bytes": 909,
|
||||
"rpc_reply_bytes": 326642,
|
||||
"catalog_arguments_replies_bytes": 3582925,
|
||||
"normalized_value_bytes_once_per_selected_reply": 94357,
|
||||
"raw_value_bytes_once_per_selected_reply": 22114,
|
||||
"bundle_bytes": 137135,
|
||||
"phases": {
|
||||
"open_binary": {
|
||||
"elapsed_ms": 941.0834749999995,
|
||||
"reply_bytes": 499
|
||||
},
|
||||
"list_documents": {
|
||||
"elapsed_ms": 27897.447016,
|
||||
"reply_bytes": 5981
|
||||
},
|
||||
"list_procedures": {
|
||||
"elapsed_ms": 136.00869700000112,
|
||||
"reply_bytes": 65589
|
||||
},
|
||||
"binary_overview": {
|
||||
"elapsed_ms": 124.65224699999817,
|
||||
"reply_bytes": 17387
|
||||
},
|
||||
"inspect_native_load_image": {
|
||||
"elapsed_ms": 249.8859930000035,
|
||||
"reply_bytes": 101123
|
||||
},
|
||||
"procedure_info": {
|
||||
"elapsed_ms": 95.36772800000472,
|
||||
"reply_bytes": 6671
|
||||
},
|
||||
"procedure_pseudo_code": {
|
||||
"elapsed_ms": 84.8552570000029,
|
||||
"reply_bytes": 6355
|
||||
},
|
||||
"procedure_assembly": {
|
||||
"elapsed_ms": 97.34392800000205,
|
||||
"reply_bytes": 6305
|
||||
},
|
||||
"procedure_callers": {
|
||||
"elapsed_ms": 8.018889999999374,
|
||||
"reply_bytes": 6177
|
||||
},
|
||||
"procedure_callees": {
|
||||
"elapsed_ms": 6.999988999996276,
|
||||
"reply_bytes": 6199
|
||||
},
|
||||
"analyze_function": {
|
||||
"elapsed_ms": 271.9528430000064,
|
||||
"reply_bytes": 31929
|
||||
},
|
||||
"warm-inventory": {
|
||||
"elapsed_ms": 30.13376900000003,
|
||||
"reply_bytes": 65589
|
||||
},
|
||||
"warm-pseudocode": {
|
||||
"elapsed_ms": 7.71154899999965,
|
||||
"reply_bytes": 6355
|
||||
},
|
||||
"export_evidence_bundle": {
|
||||
"elapsed_ms": 71.93626799999765,
|
||||
"reply_bytes": 314
|
||||
},
|
||||
"close_binary": {
|
||||
"elapsed_ms": 620.6287440000015,
|
||||
"reply_bytes": 169
|
||||
}
|
||||
},
|
||||
"scope_seconds": 34.901,
|
||||
"payload_cpu_seconds": 33.834228,
|
||||
"payload_family_peak_bytes": 1025228800,
|
||||
"payload_cleanup_verified": true,
|
||||
"zero_limit_events_at_payload_exit": true,
|
||||
"final_whole_scope_counters_verified": false
|
||||
},
|
||||
{
|
||||
"name": "baseline-jq-1",
|
||||
"expected_projection": "duplicate",
|
||||
"artifact_sha256": "b1c22172dd303f3be49e935aa56aa48a8b7a46e0bc838b4997d3bb451495870f",
|
||||
"artifact_bytes": 2267912,
|
||||
"procedure_count": 2276,
|
||||
"selected_procedure": "0x401000",
|
||||
"facts_sha256": "f7b95167c93f2b9129b5a4332590ef6bbb9d7b646d554ba4c396db1055ef0d24",
|
||||
"call_count": 15,
|
||||
"catalog": {
|
||||
"elapsed_ms": 2761.8931860000002,
|
||||
"bytes": 3255374,
|
||||
"sha256": "8a646b95cc5607ee57e5a3c0ef480b77e4cec7e5168498e72f511a8af40daf61"
|
||||
},
|
||||
"rpc_argument_bytes": 965,
|
||||
"rpc_reply_bytes": 2372592,
|
||||
"catalog_arguments_replies_bytes": 5628931,
|
||||
"normalized_value_bytes_once_per_selected_reply": 536023,
|
||||
"raw_value_bytes_once_per_selected_reply": 533635,
|
||||
"bundle_bytes": 609212,
|
||||
"phases": {
|
||||
"open_binary": {
|
||||
"elapsed_ms": 1856.9621389999993,
|
||||
"reply_bytes": 617
|
||||
},
|
||||
"list_documents": {
|
||||
"elapsed_ms": 290349.418768,
|
||||
"reply_bytes": 6167
|
||||
},
|
||||
"list_procedures": {
|
||||
"elapsed_ms": 624.2473109999555,
|
||||
"reply_bytes": 1092959
|
||||
},
|
||||
"binary_overview": {
|
||||
"elapsed_ms": 214.38793299999088,
|
||||
"reply_bytes": 15727
|
||||
},
|
||||
"inspect_native_load_image": {
|
||||
"elapsed_ms": 432.1906869999948,
|
||||
"reply_bytes": 75633
|
||||
},
|
||||
"procedure_info": {
|
||||
"elapsed_ms": 106.62601700000232,
|
||||
"reply_bytes": 7813
|
||||
},
|
||||
"procedure_pseudo_code": {
|
||||
"elapsed_ms": 165.65909500000998,
|
||||
"reply_bytes": 6869
|
||||
},
|
||||
"procedure_assembly": {
|
||||
"elapsed_ms": 98.534897000005,
|
||||
"reply_bytes": 6855
|
||||
},
|
||||
"procedure_callers": {
|
||||
"elapsed_ms": 23.43981900002109,
|
||||
"reply_bytes": 6891
|
||||
},
|
||||
"procedure_callees": {
|
||||
"elapsed_ms": 9.403080000018235,
|
||||
"reply_bytes": 7479
|
||||
},
|
||||
"analyze_function": {
|
||||
"elapsed_ms": 327.3197410000139,
|
||||
"reply_bytes": 45277
|
||||
},
|
||||
"warm-inventory": {
|
||||
"elapsed_ms": 396.67419799999334,
|
||||
"reply_bytes": 1092959
|
||||
},
|
||||
"warm-pseudocode": {
|
||||
"elapsed_ms": 6.517379999975674,
|
||||
"reply_bytes": 6869
|
||||
},
|
||||
"export_evidence_bundle": {
|
||||
"elapsed_ms": 56.13109899999108,
|
||||
"reply_bytes": 308
|
||||
},
|
||||
"close_binary": {
|
||||
"elapsed_ms": 550.6349530000007,
|
||||
"reply_bytes": 169
|
||||
}
|
||||
},
|
||||
"scope_seconds": 304.543,
|
||||
"payload_cpu_seconds": 294.743485,
|
||||
"payload_family_peak_bytes": 1159921664,
|
||||
"payload_cleanup_verified": true,
|
||||
"zero_limit_events_at_payload_exit": true,
|
||||
"final_whole_scope_counters_verified": false
|
||||
},
|
||||
{
|
||||
"name": "integrated-baseline-jq",
|
||||
"expected_projection": "duplicate",
|
||||
"artifact_sha256": "b1c22172dd303f3be49e935aa56aa48a8b7a46e0bc838b4997d3bb451495870f",
|
||||
"artifact_bytes": 2267912,
|
||||
"procedure_count": 2279,
|
||||
"selected_procedure": "0x401000",
|
||||
"facts_sha256": "87722aab25011f1b047eb2bf8302cdbc5a4256c1b7f17d7bd68404a7d191e070",
|
||||
"call_count": 15,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1411.786849,
|
||||
"bytes": 3263492,
|
||||
"sha256": "c754f774156866d70be754e050335d0e4d3a7a6cbb156746cf1f7b5ffa42b26b"
|
||||
},
|
||||
"rpc_argument_bytes": 974,
|
||||
"rpc_reply_bytes": 2375466,
|
||||
"catalog_arguments_replies_bytes": 5639932,
|
||||
"normalized_value_bytes_once_per_selected_reply": 536689,
|
||||
"raw_value_bytes_once_per_selected_reply": 534301,
|
||||
"bundle_bytes": 609878,
|
||||
"phases": {
|
||||
"open_binary": {
|
||||
"elapsed_ms": 983.5489280000002,
|
||||
"reply_bytes": 617
|
||||
},
|
||||
"list_documents": {
|
||||
"elapsed_ms": 270159.690556,
|
||||
"reply_bytes": 6167
|
||||
},
|
||||
"list_procedures": {
|
||||
"elapsed_ms": 717.9032449999941,
|
||||
"reply_bytes": 1094387
|
||||
},
|
||||
"binary_overview": {
|
||||
"elapsed_ms": 203.3595150000183,
|
||||
"reply_bytes": 15727
|
||||
},
|
||||
"inspect_native_load_image": {
|
||||
"elapsed_ms": 362.37314199999673,
|
||||
"reply_bytes": 75633
|
||||
},
|
||||
"procedure_info": {
|
||||
"elapsed_ms": 92.72515800001565,
|
||||
"reply_bytes": 7813
|
||||
},
|
||||
"procedure_pseudo_code": {
|
||||
"elapsed_ms": 85.43632900004741,
|
||||
"reply_bytes": 6869
|
||||
},
|
||||
"procedure_assembly": {
|
||||
"elapsed_ms": 78.87645799998427,
|
||||
"reply_bytes": 6855
|
||||
},
|
||||
"procedure_callers": {
|
||||
"elapsed_ms": 32.66119900002377,
|
||||
"reply_bytes": 6891
|
||||
},
|
||||
"procedure_callees": {
|
||||
"elapsed_ms": 8.895469999988563,
|
||||
"reply_bytes": 7479
|
||||
},
|
||||
"analyze_function": {
|
||||
"elapsed_ms": 343.2701520000119,
|
||||
"reply_bytes": 45277
|
||||
},
|
||||
"warm-inventory": {
|
||||
"elapsed_ms": 338.9290720000281,
|
||||
"reply_bytes": 1094387
|
||||
},
|
||||
"warm-pseudocode": {
|
||||
"elapsed_ms": 9.391289999999572,
|
||||
"reply_bytes": 6869
|
||||
},
|
||||
"export_evidence_bundle": {
|
||||
"elapsed_ms": 82.86341799999354,
|
||||
"reply_bytes": 326
|
||||
},
|
||||
"close_binary": {
|
||||
"elapsed_ms": 679.7842850000015,
|
||||
"reply_bytes": 169
|
||||
}
|
||||
},
|
||||
"scope_seconds": 279.699,
|
||||
"payload_cpu_seconds": 277.696079,
|
||||
"payload_family_peak_bytes": 1111494656,
|
||||
"payload_cleanup_verified": true,
|
||||
"zero_limit_events_at_payload_exit": true,
|
||||
"final_whole_scope_counters_verified": false
|
||||
},
|
||||
{
|
||||
"name": "integrated-candidate-jq",
|
||||
"expected_projection": "compact",
|
||||
"artifact_sha256": "b1c22172dd303f3be49e935aa56aa48a8b7a46e0bc838b4997d3bb451495870f",
|
||||
"artifact_bytes": 2267912,
|
||||
"procedure_count": 2279,
|
||||
"selected_procedure": "0x401000",
|
||||
"facts_sha256": "87722aab25011f1b047eb2bf8302cdbc5a4256c1b7f17d7bd68404a7d191e070",
|
||||
"call_count": 15,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1213.2912340000003,
|
||||
"bytes": 3263492,
|
||||
"sha256": "c754f774156866d70be754e050335d0e4d3a7a6cbb156746cf1f7b5ffa42b26b"
|
||||
},
|
||||
"rpc_argument_bytes": 975,
|
||||
"rpc_reply_bytes": 1264884,
|
||||
"catalog_arguments_replies_bytes": 4529351,
|
||||
"normalized_value_bytes_once_per_selected_reply": 536689,
|
||||
"raw_value_bytes_once_per_selected_reply": 16143,
|
||||
"bundle_bytes": 345626,
|
||||
"phases": {
|
||||
"open_binary": {
|
||||
"elapsed_ms": 927.8219500000005,
|
||||
"reply_bytes": 617
|
||||
},
|
||||
"list_documents": {
|
||||
"elapsed_ms": 391135.781249,
|
||||
"reply_bytes": 6119
|
||||
},
|
||||
"list_procedures": {
|
||||
"elapsed_ms": 646.0353969999705,
|
||||
"reply_bytes": 550261
|
||||
},
|
||||
"binary_overview": {
|
||||
"elapsed_ms": 198.11136500001885,
|
||||
"reply_bytes": 15727
|
||||
},
|
||||
"inspect_native_load_image": {
|
||||
"elapsed_ms": 456.49483100004727,
|
||||
"reply_bytes": 75633
|
||||
},
|
||||
"procedure_info": {
|
||||
"elapsed_ms": 99.12928700004704,
|
||||
"reply_bytes": 6825
|
||||
},
|
||||
"procedure_pseudo_code": {
|
||||
"elapsed_ms": 114.82446700002765,
|
||||
"reply_bytes": 6584
|
||||
},
|
||||
"procedure_assembly": {
|
||||
"elapsed_ms": 80.73368800000753,
|
||||
"reply_bytes": 6444
|
||||
},
|
||||
"procedure_callers": {
|
||||
"elapsed_ms": 10.588890000013635,
|
||||
"reply_bytes": 6337
|
||||
},
|
||||
"procedure_callees": {
|
||||
"elapsed_ms": 19.5354899999802,
|
||||
"reply_bytes": 6631
|
||||
},
|
||||
"analyze_function": {
|
||||
"elapsed_ms": 352.812751999998,
|
||||
"reply_bytes": 26364
|
||||
},
|
||||
"warm-inventory": {
|
||||
"elapsed_ms": 197.59054599999217,
|
||||
"reply_bytes": 550261
|
||||
},
|
||||
"warm-pseudocode": {
|
||||
"elapsed_ms": 8.122649999975692,
|
||||
"reply_bytes": 6584
|
||||
},
|
||||
"export_evidence_bundle": {
|
||||
"elapsed_ms": 61.80356899998151,
|
||||
"reply_bytes": 328
|
||||
},
|
||||
"close_binary": {
|
||||
"elapsed_ms": 614.0456879999838,
|
||||
"reply_bytes": 169
|
||||
}
|
||||
},
|
||||
"scope_seconds": 399.097,
|
||||
"payload_cpu_seconds": 352.659548,
|
||||
"payload_family_peak_bytes": 1088741376,
|
||||
"payload_cleanup_verified": true,
|
||||
"zero_limit_events_at_payload_exit": true,
|
||||
"final_whole_scope_counters_verified": false
|
||||
}
|
||||
],
|
||||
"incomplete_attempts": [
|
||||
{
|
||||
"name": "baseline-true-1",
|
||||
"reason": "Harness selected subject.sha256 instead of subject.digest.sha256.",
|
||||
"reported_status": "failed",
|
||||
"payload_exit_code": 1,
|
||||
"scope_seconds": 41.401,
|
||||
"payload_cpu_seconds": 39.027824,
|
||||
"payload_family_peak_bytes": 1132675072,
|
||||
"observed_procedures": null,
|
||||
"payload_cleanup_verified": true,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"is_completed_performance_sample": false
|
||||
},
|
||||
{
|
||||
"name": "baseline-true-2",
|
||||
"reason": "Harness assumed the composed overview provider was Ghidra rather than rea-workflow.",
|
||||
"reported_status": "failed",
|
||||
"payload_exit_code": 1,
|
||||
"scope_seconds": 37.538,
|
||||
"payload_cpu_seconds": 36.599406,
|
||||
"payload_family_peak_bytes": 1001476096,
|
||||
"observed_procedures": 231,
|
||||
"payload_cleanup_verified": true,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"is_completed_performance_sample": false
|
||||
},
|
||||
{
|
||||
"name": "baseline-true-3",
|
||||
"reason": "Facts and cleanup completed, but the harness incorrectly read a status property from the boolean finalizer and exited 1.",
|
||||
"reported_status": "passed",
|
||||
"payload_exit_code": 1,
|
||||
"scope_seconds": 35.796,
|
||||
"payload_cpu_seconds": 34.736678,
|
||||
"payload_family_peak_bytes": 1001455616,
|
||||
"observed_procedures": 231,
|
||||
"payload_cleanup_verified": true,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"is_completed_performance_sample": false
|
||||
},
|
||||
{
|
||||
"name": "candidate-jq-1",
|
||||
"reason": "SDK five-minute first-query deadline expired while awaiting list_documents; cleanup completed.",
|
||||
"reported_status": "failed",
|
||||
"payload_exit_code": 1,
|
||||
"scope_seconds": 316.605,
|
||||
"payload_cpu_seconds": 293.28205,
|
||||
"payload_family_peak_bytes": 1065631744,
|
||||
"observed_procedures": null,
|
||||
"payload_cleanup_verified": true,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"is_completed_performance_sample": false
|
||||
},
|
||||
{
|
||||
"name": "integrated-baseline-jq-control",
|
||||
"reason": "Unmodified-main repeat differs by three observed procedure rows; strict comparison rejected it before export. Cause unestablished.",
|
||||
"reported_status": "failed",
|
||||
"payload_exit_code": 1,
|
||||
"scope_seconds": 276.557,
|
||||
"payload_cpu_seconds": 274.502165,
|
||||
"payload_family_peak_bytes": 1200726016,
|
||||
"observed_procedures": 2276,
|
||||
"payload_cleanup_verified": true,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"is_completed_performance_sample": false
|
||||
}
|
||||
],
|
||||
"capacity_control": {
|
||||
"baseline_revision": "3fca2e0ca6e54ba700c286d9a85a9600dee6a19d",
|
||||
"records": 30000,
|
||||
"baseline": "regression fails with resource_constraint for list_procedures at the unchanged 10485760-byte stdio budget",
|
||||
"candidate": "all 30000 records delivered exactly; raw_result null",
|
||||
"real_provider": false,
|
||||
"production_boundary": "GhidraProvider -> BinarySession -> public MCP"
|
||||
},
|
||||
"executed_validation": [
|
||||
{
|
||||
"check": "npm run check",
|
||||
"base": "4e716b060",
|
||||
"result": "6 tasks and architecture guards passed"
|
||||
},
|
||||
{
|
||||
"check": "npm run check",
|
||||
"base": "3fca2e0ca",
|
||||
"result": "6 tasks and architecture guards passed; before adding the capacity regression"
|
||||
},
|
||||
{
|
||||
"check": "Focused Ghidra inventory/function/provider and four MCP files",
|
||||
"base": "4e716b060",
|
||||
"result": "246 assertions passed across 7 files"
|
||||
},
|
||||
{
|
||||
"check": "Capacity regression alone",
|
||||
"base": "3fca2e0ca",
|
||||
"result": "candidate passes; unchanged production baseline fails with resource_constraint; six other tests filtered out"
|
||||
},
|
||||
{
|
||||
"check": "Existing real Ghidra large-result helper through a private stdio harness",
|
||||
"result": "passed",
|
||||
"successful_calls": 15,
|
||||
"expected_rejections": 4,
|
||||
"runtime_empty_after_provider_close": true,
|
||||
"scope": "oversized lookup/read/annotation/dossier retention and recovery, complete CLI readback, unchanged source bytes"
|
||||
},
|
||||
{
|
||||
"check": "node scripts/verify-real-ghidra-address-context.mjs",
|
||||
"result": "passed with real Ghidra/CLI/MCP and verified empty descendant set",
|
||||
"scope": "PIE load identity, file-offset decompilation/roles, address round trips, failure guidance/recovery, unchanged source and complete cleanup",
|
||||
"fixture_sha256": "093f8ba3aef80f3d7c0e8f22557e9a8af29505e286835578282b8e32ff804a02"
|
||||
},
|
||||
{
|
||||
"check": "Final focused Ghidra source and MCP regression selection",
|
||||
"base": "3fca2e0ca",
|
||||
"result": "247 assertions across 7 files passed"
|
||||
},
|
||||
{
|
||||
"check": "npm run docs:check",
|
||||
"result": "generated metadata/documentation checks and 5 README conformance assertions passed"
|
||||
}
|
||||
],
|
||||
"unmodified_main_control": {
|
||||
"same_target_digest_and_analysis_profile": true,
|
||||
"expected_procedures": 2279,
|
||||
"observed_procedures": 2276,
|
||||
"missing_relative_to_first_run": ["0x4f9930", "0x5260b0", "0x529640"],
|
||||
"cause": "unestablished; no engine/default-analysis repair included",
|
||||
"interpretation": "The matched pair is an observation, not a guarantee of cross-session procedure discovery or a latency distribution."
|
||||
},
|
||||
"limitations": [
|
||||
"No actual model-token or tokenizer measurement.",
|
||||
"Single completed pairs, not latency distributions.",
|
||||
"Unmodified-main procedure-inventory repeat failed the strict oracle; retained as an incomplete control.",
|
||||
"First three successful query receipts used the earlier driver; archived warm results were rechecked when explicit repeat assertions were added.",
|
||||
"Real Windows/macOS Ghidra, other engine versions and real NativeAOT overlays remain unqualified.",
|
||||
"Initial cached baseline build had an unverified payload-exit cleanup observation; a later systemd inspection found its scope inactive. It is not a query measurement."
|
||||
],
|
||||
"resource_stopped_validation": [
|
||||
{
|
||||
"check": "Final focused regression selection, first attempt",
|
||||
"abort": "host_available_memory_early_stop",
|
||||
"peak_family_bytes_sampled": 837332992,
|
||||
"outcome": "guard-stopped without final test results; not counted as passing",
|
||||
"retry_budget": "4 GiB hard/3 GiB early, based on the earlier focused run peak of about 1.3 GiB"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
# Ghidra Evidence projection
|
||||
|
||||
Continues [#5337](https://github.com/morluto/rea/issues/5337). The bridge
|
||||
adapter supplied a complete `rawResult` for every successful operation, even
|
||||
when strict parsing preserved the same value in `normalized_result`. Function
|
||||
observation envelopes repeated their value and already-projected qualifications.
|
||||
The generic Evidence contract already permits `raw_result: null` in this case.
|
||||
|
||||
## Owning change
|
||||
|
||||
The Ghidra inventory and function parsers now declare whether they retain an
|
||||
independent upstream representation. Execution consumes those parsed facts and
|
||||
named metadata, replacing the positional helper argument that required every
|
||||
caller to supply another complete report. CLI and MCP use the same projection.
|
||||
|
||||
Ordinary inventories and function observations omit the redundant copy.
|
||||
Limitations, relationship coverage and locations remain canonical Evidence
|
||||
fields. The following representations remain available:
|
||||
|
||||
- The overview's upstream segment permissions, sections and provenance.
|
||||
- Load-image observations before local attestation and enrichment.
|
||||
- File-offset selection roles, which generic locations alone do not express.
|
||||
- The database type reply before adding a locally derived NativeAOT overlay.
|
||||
|
||||
No tool, schema, query default, provider protocol, cache, target execution or
|
||||
annotation authority is added. Existing source Evidence is not rewritten.
|
||||
New records have the identities appropriate to their new representation;
|
||||
artifact and procedure identities remain unchanged. Consumers should read
|
||||
function results from `normalized_result`, rather than `raw_result.value`.
|
||||
|
||||
## Real inputs and completed question
|
||||
|
||||
Both ELF x86-64 targets are read without executing them:
|
||||
|
||||
| Target | Bytes | SHA-256 | Provenance |
|
||||
| ---------------- | --------: | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `true` | 35,664 | `c79bf44242829108e323378531f4ac839513ca1fba45efd6583643526e1e9fd2` | Installed Debian coreutils 9.1-1; package metadata is a local observation. |
|
||||
| `jq-linux-amd64` | 2,267,912 | `b1c22172dd303f3be49e935aa56aa48a8b7a46e0bc838b4997d3bb451495870f` | [Official jq 1.8.2 asset](https://github.com/jqlang/jq/releases/tag/jq-1.8.2); matches both the corpus and the release API's asset digest/size. |
|
||||
|
||||
Each fresh production stdio MCP session discovers the complete catalog, opens
|
||||
the target, reads documents and the complete observed procedure inventory,
|
||||
selects the first internal non-thunk procedure, reads overview/load image and
|
||||
six function operations, repeats inventory/pseudocode, exports Evidence and
|
||||
closes. This is one catalog discovery and 15 RPCs, including cleanup.
|
||||
It qualifies this inventory/function-read question, rather than application
|
||||
feature reconstruction or complete native-code discovery.
|
||||
|
||||
The live named SDK contracts validate replies. Each selected record binds to
|
||||
the original target digest and Ghidra profile. The candidate's complete selected
|
||||
fact vector equals the baseline after excluding only `evidence_id` and
|
||||
`raw_result`: subject, parameters, normalized facts, provider/profile, authority,
|
||||
limitations, relationships, locations and Evidence links remain equal. Exported
|
||||
records match returned Evidence, repeated facts agree, and target hashes remain
|
||||
unchanged. This is representation equivalence, not an independent qualification
|
||||
of every Ghidra interpretation.
|
||||
|
||||
## Measurements and limits
|
||||
|
||||
Linux x86-64, Node 24.18.0/npm 11.16.0, MCP SDK 2.3.1, Ghidra 12.1.4 and
|
||||
Eclipse Adoptium 21.0.12.1+1-LTS. Sessions and Java analysis are fresh; OS caches
|
||||
are warm and unflushed. Configured Java heap is 768 MiB with one active processor.
|
||||
The whole process family uses one logical CPU, 4 GiB hard/3 GiB early-stop memory,
|
||||
no swap and 192 tasks. No completed query task has an OOM/limit event or cleanup
|
||||
failure at verified payload exit. Final whole-scope counters after scope removal
|
||||
remain unavailable. Post-GC retained heap is unmeasured.
|
||||
|
||||
The small pair uses main `4e716b06078d725a53234503cbefbb340731e809` and its
|
||||
candidate. Main advanced during the experiment; the large pair uses
|
||||
`3fca2e0ca6e54ba700c286d9a85a9600dee6a19d` on both sides. Existing timing
|
||||
samples keep their original runtime fingerprints. The [curated receipt](ghidra-evidence-projection.json)
|
||||
retains individual observations and incomplete attempts; raw data stays outside Git.
|
||||
|
||||
| Recording / metric | Before | After |
|
||||
| ----------------------------------------------- | ---------: | ---------: |
|
||||
| `true`: observed procedures | 231 | 231 |
|
||||
| `true`: all RPC result bytes | 473,344 | 326,642 |
|
||||
| `true`: catalog + request arguments + results | 3,729,626 | 3,582,925 |
|
||||
| `true`: complete scope wall | 34.369 s | 34.901 s |
|
||||
| `true`: payload-family CPU | 33.150 s | 33.834 s |
|
||||
| `true`: kernel family peak including file cache | 947.9 MiB | 977.7 MiB |
|
||||
| `jq`: observed procedures | 2,279 | 2,279 |
|
||||
| `jq`: all RPC result bytes | 2,375,466 | 1,264,884 |
|
||||
| `jq`: catalog + request arguments + results | 5,639,932 | 4,529,351 |
|
||||
| `jq`: complete scope wall | 279.699 s | 399.097 s |
|
||||
| `jq`: payload-family CPU | 277.696 s | 352.660 s |
|
||||
| `jq`: kernel family peak including file cache | 1060.0 MiB | 1038.3 MiB |
|
||||
|
||||
RPC result bytes include the SDK's text and structured representations. Request
|
||||
objects and the complete catalog are included in the combined byte count;
|
||||
initialization, wire framing and model rendering are unmeasured. Actual model
|
||||
tokens and tokenizer estimates are unmeasured. The observed byte reduction
|
||||
does not establish a token saving or better agent investigation outcome.
|
||||
|
||||
The large result envelopes shrink by 46.8%; combined serialization shrinks by
|
||||
19.7%, with unchanged catalog and call count. Small combined serialization
|
||||
shrinks by 3.9%. These are single pairs, not distributions. No whole-task latency,
|
||||
CPU or peak-memory improvement is established. The large candidate's first
|
||||
`list_documents` costs 391.136 s versus 270.160 s before; this phase includes
|
||||
startup, analysis wait and client work, not isolated engine time. Changed
|
||||
projection code executes after the bridge returns its observation, but the
|
||||
cause of the preparation-time variation is unestablished. One warm inventory
|
||||
read costs 338.93 → 197.59 ms; this is not a general latency qualification.
|
||||
|
||||
A subsequent unchanged-main repeat with the same target/profile returned 2,276
|
||||
procedures instead of 2,279, omitting `0x4f9930`, `0x5260b0` and `0x529640`
|
||||
relative to its earlier inventory. The strict oracle rejected that control
|
||||
before export. Its cause is unestablished; it is retained as an incomplete
|
||||
attempt, not added to performance averages or treated as a projection defect.
|
||||
|
||||
## Verification scope
|
||||
|
||||
The production-boundary capacity regression supplies 30,000 valid simulated
|
||||
Ghidra procedure rows through `GhidraProvider`, `BinarySession` and MCP. On
|
||||
unchanged production main it fails with `resource_constraint` at the existing
|
||||
10 MiB response budget. The candidate delivers every row exactly, without a
|
||||
budget increase or truncation. This qualifies response admission, not a real
|
||||
30,000-function Ghidra analysis or a universal maximum procedure count.
|
||||
|
||||
Source/parser tests cover strict rejection, sidecars, distinct file-offset
|
||||
mapping roles, overview details, NativeAOT enrichment and unchanged observations.
|
||||
Executed checks and real-provider verification are recorded in the receipt.
|
||||
NativeAOT overlay tests use producer fixtures; real Windows/macOS and other
|
||||
engine versions remain unqualified here.
|
||||
|
||||
The existing real large-result helper passed through a private stdio harness:
|
||||
oversized lookup/read/annotation/dossier replies retained complete Evidence,
|
||||
subsequent queries recovered, complete CLI readback matched the original bytes
|
||||
with `raw_result: null`, and generated target bytes remained unchanged.
|
||||
The public `verify-real-ghidra-address-context.mjs` also passed with real
|
||||
Ghidra/CLI/MCP, including file-offset selection, address round trips, recovery,
|
||||
unchanged source and verified cleanup. These capacity/address fixtures use a
|
||||
separate 6 GiB family cap; they are correctness checks, not latency pairs.
|
||||
|
||||
Three early `true` harness attempts used an incorrect subject-digest selector,
|
||||
assumed the composed overview's provider was Ghidra, or misread the existing
|
||||
finalizer's boolean result. The last completed its facts but exited nonzero;
|
||||
none is a completed performance sample. A first `jq` candidate hit the SDK's
|
||||
five-minute deadline while awaiting readiness, then cleaned up successfully.
|
||||
The final driver's startup/RPC deadlines are ten minutes on both sides; product
|
||||
defaults are unchanged. Earlier successful warm facts were rechecked from their
|
||||
archived production replies when explicit warm-result assertions were added.
|
||||
|
||||
## Reproduce
|
||||
|
||||
Build and retain separate baseline/candidate runtimes. Use the same absolute
|
||||
target path and caller-supplied Ghidra/JDK for both:
|
||||
|
||||
```sh
|
||||
python3 experiments/analysis-efficiency/resource-run.py --observe-payload-exit -- \
|
||||
node experiments/analysis-efficiency/ghidra-evidence-projection.mjs \
|
||||
--runtime /absolute/baseline --target /absolute/application \
|
||||
--ghidra /absolute/ghidra --java /absolute/jdk \
|
||||
--expect duplicate --output /absolute/new-before
|
||||
|
||||
python3 experiments/analysis-efficiency/resource-run.py --observe-payload-exit -- \
|
||||
node experiments/analysis-efficiency/ghidra-evidence-projection.mjs \
|
||||
--runtime /absolute/candidate --target /absolute/application \
|
||||
--ghidra /absolute/ghidra --java /absolute/jdk \
|
||||
--expect compact --control /absolute/new-before/facts.json \
|
||||
--output /absolute/new-after
|
||||
```
|
||||
|
||||
The candidate's independent baseline comparison is included in harness costs;
|
||||
its isolated verification overhead is unmeasured. Static checks use a separate
|
||||
8 GiB memory cap. All heavy operations run serially.
|
||||
@@ -0,0 +1,216 @@
|
||||
/** Real Ghidra inventory/function Evidence; raw inputs and receipts stay outside Git. */
|
||||
import assert from "node:assert/strict";
|
||||
import { createHash } from "node:crypto";
|
||||
import { createWriteStream } from "node:fs";
|
||||
import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { finished } from "node:stream/promises";
|
||||
import { parseArgs } from "node:util";
|
||||
import { Client } from "@modelcontextprotocol/client";
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
||||
import { createRetainedQueryCaller } from "./retained-query-calls.mjs";
|
||||
import { finalizeRetainedQueryReceipt } from "./retained-query-finalization.mjs";
|
||||
|
||||
const { values } = parseArgs({
|
||||
options: Object.fromEntries(
|
||||
["runtime", "target", "output", "ghidra", "java", "expect", "control"].map(
|
||||
(name) => [name, { type: "string" }],
|
||||
),
|
||||
),
|
||||
});
|
||||
for (const name of ["runtime", "target", "output", "ghidra", "java"])
|
||||
assert.ok(values[name] && values[name] === resolve(values[name]), name);
|
||||
assert.ok(["duplicate", "compact"].includes(values.expect));
|
||||
const hash = (value) => createHash("sha256").update(value).digest("hex");
|
||||
const output = values.output;
|
||||
await mkdir(output, { mode: 0o700 });
|
||||
const bytes = await readFile(values.target);
|
||||
const home = await mkdtemp(join(tmpdir(), "rea-ghidra-evidence-"));
|
||||
const client = new Client({ name: "ghidra-evidence-projection", version: "1" });
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [join(values.runtime, "scripts/rea.mjs"), "mcp"],
|
||||
env: {
|
||||
HOME: home,
|
||||
PATH: process.env.PATH,
|
||||
NODE_OPTIONS: "--max-old-space-size=2048",
|
||||
GHIDRA_INSTALL_DIR: values.ghidra,
|
||||
JAVA_HOME: values.java,
|
||||
GHIDRA_HEADLESS_MAXMEM: "768M",
|
||||
JAVA_TOOL_OPTIONS: "-XX:ActiveProcessorCount=1",
|
||||
REA_GHIDRA_STARTUP_TIMEOUT_MS: "600000",
|
||||
REA_LOG_LEVEL: "silent",
|
||||
},
|
||||
stderr: "pipe",
|
||||
});
|
||||
const serverLog = createWriteStream(join(output, "server.log"));
|
||||
const serverLogFinished = finished(serverLog);
|
||||
serverLogFinished.catch(() => {});
|
||||
transport.stderr?.pipe(serverLog);
|
||||
const report = {
|
||||
artifact: { path: values.target, bytes: bytes.length, sha256: hash(bytes) },
|
||||
expected_projection: values.expect,
|
||||
calls: [],
|
||||
};
|
||||
const transcript = [];
|
||||
const evidence = [];
|
||||
const call = createRetainedQueryCaller(client, (receipt, event) => {
|
||||
report.calls.push(receipt);
|
||||
transcript.push(event);
|
||||
});
|
||||
const result = async (name, input, phase = name) => {
|
||||
const reply = await call(name, input, phase);
|
||||
assert.notEqual(reply.isError, true, JSON.stringify(reply));
|
||||
assert.ok(reply.structuredContent);
|
||||
return reply.structuredContent;
|
||||
};
|
||||
const inspect = async (name, input, phase = name) => {
|
||||
const value = await result(name, input, phase);
|
||||
assert.equal(value.subject.digest.sha256, report.artifact.sha256);
|
||||
assert.equal(
|
||||
value.provider.id,
|
||||
name === "binary_overview" ? "rea-workflow" : "ghidra",
|
||||
);
|
||||
assert.equal(
|
||||
value.provider.version,
|
||||
name === "binary_overview" ? "1" : "12.1.4",
|
||||
);
|
||||
assert.ok(value.analysis_profile);
|
||||
evidence.push({ phase, value });
|
||||
return value;
|
||||
};
|
||||
const started = performance.now();
|
||||
let opened = false;
|
||||
try {
|
||||
await client.connect(transport);
|
||||
const discovery = performance.now();
|
||||
const catalog = await client.listTools();
|
||||
assert.equal(catalog.nextCursor, undefined);
|
||||
report.catalog = {
|
||||
elapsed_ms: performance.now() - discovery,
|
||||
bytes: Buffer.byteLength(JSON.stringify(catalog)),
|
||||
sha256: hash(JSON.stringify(catalog)),
|
||||
};
|
||||
await writeFile(join(output, "catalog.json"), JSON.stringify(catalog));
|
||||
const definitions = new Map(catalog.tools.map((tool) => [tool.name, tool]));
|
||||
const original = client.callTool.bind(client);
|
||||
client.callTool = (input, options) =>
|
||||
original(input, {
|
||||
...options,
|
||||
timeout: 600000,
|
||||
toolDefinition: definitions.get(input.name),
|
||||
});
|
||||
await result("open_binary", { path: values.target, provider_id: "ghidra" });
|
||||
opened = true;
|
||||
const documents = await inspect("list_documents", {});
|
||||
assert.equal(documents.normalized_result.length, 1);
|
||||
const procedures = await inspect("list_procedures", {});
|
||||
assert.ok(procedures.normalized_result.length > 0);
|
||||
const selected = procedures.normalized_result.find(
|
||||
(row) => row.procedure.external === false && row.procedure.thunk === false,
|
||||
);
|
||||
assert.ok(selected);
|
||||
report.procedure = selected.address;
|
||||
report.procedure_count = procedures.normalized_result.length;
|
||||
await inspect("binary_overview", {});
|
||||
await inspect("inspect_native_load_image", {});
|
||||
for (const name of [
|
||||
"procedure_info",
|
||||
"procedure_pseudo_code",
|
||||
"procedure_assembly",
|
||||
"procedure_callers",
|
||||
"procedure_callees",
|
||||
"analyze_function",
|
||||
])
|
||||
await inspect(name, { procedure: selected.address });
|
||||
const warmInventory = await inspect("list_procedures", {}, "warm-inventory");
|
||||
assert.deepEqual(
|
||||
warmInventory.normalized_result,
|
||||
procedures.normalized_result,
|
||||
);
|
||||
const warmPseudocode = await inspect(
|
||||
"procedure_pseudo_code",
|
||||
{ procedure: selected.address },
|
||||
"warm-pseudocode",
|
||||
);
|
||||
assert.deepEqual(
|
||||
warmPseudocode.normalized_result,
|
||||
evidence.find(({ phase }) => phase === "procedure_pseudo_code").value
|
||||
.normalized_result,
|
||||
);
|
||||
for (const { phase, value } of evidence) {
|
||||
if (phase === "binary_overview") {
|
||||
assert.equal(value.raw_result, null);
|
||||
continue;
|
||||
}
|
||||
const distinct = phase === "inspect_native_load_image";
|
||||
if (values.expect === "compact" && !distinct)
|
||||
assert.equal(value.raw_result, null, phase);
|
||||
else assert.notEqual(value.raw_result, null, phase);
|
||||
if (values.expect === "duplicate" && !distinct) {
|
||||
const raw = value.raw_result;
|
||||
const wrapped =
|
||||
raw !== null &&
|
||||
!Array.isArray(raw) &&
|
||||
Object.hasOwn(raw, "value") &&
|
||||
Object.hasOwn(raw, "limitations");
|
||||
assert.deepEqual(
|
||||
wrapped ? raw.value : raw,
|
||||
value.normalized_result,
|
||||
phase,
|
||||
);
|
||||
}
|
||||
}
|
||||
const facts = evidence.map(({ phase, value }) => {
|
||||
const { evidence_id: _id, raw_result: _raw, ...facts } = value;
|
||||
return { phase, facts };
|
||||
});
|
||||
report.facts_sha256 = hash(JSON.stringify(facts));
|
||||
await writeFile(join(output, "facts.json"), JSON.stringify(facts));
|
||||
if (values.control)
|
||||
assert.deepEqual(facts, JSON.parse(await readFile(values.control, "utf8")));
|
||||
const path = join(output, "evidence.json");
|
||||
await result("export_evidence_bundle", { path });
|
||||
const bundle = JSON.parse(await readFile(path, "utf8"));
|
||||
for (const { value } of evidence)
|
||||
assert.deepEqual(
|
||||
bundle.records.find((record) => record.evidence_id === value.evidence_id),
|
||||
value,
|
||||
);
|
||||
assert.equal(hash(await readFile(values.target)), report.artifact.sha256);
|
||||
report.status = "passed";
|
||||
} catch (cause) {
|
||||
report.status = "failed";
|
||||
report.failure = String(cause?.stack ?? cause);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
if (opened) {
|
||||
try {
|
||||
await result("close_binary", {});
|
||||
} catch (cause) {
|
||||
report.status = "failed";
|
||||
report.close_failure = String(cause?.stack ?? cause);
|
||||
}
|
||||
}
|
||||
report.elapsed_before_cleanup_ms = performance.now() - started;
|
||||
try {
|
||||
await writeFile(
|
||||
join(output, "transcript.json"),
|
||||
JSON.stringify(transcript),
|
||||
);
|
||||
} catch (cause) {
|
||||
report.status = "failed";
|
||||
report.transcript_failure = String(cause);
|
||||
}
|
||||
const finalized = await finalizeRetainedQueryReceipt({
|
||||
report,
|
||||
client,
|
||||
transport,
|
||||
account: home,
|
||||
serverLog,
|
||||
serverLogFinished,
|
||||
output,
|
||||
});
|
||||
if (report.status !== "passed" || !finalized) process.exitCode = 1;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
"""Summarize recorded Codex discovery tasks; tiktoken==0.12.0 is a proxy only."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import tiktoken
|
||||
|
||||
|
||||
def compact(value):
|
||||
return json.dumps(value, ensure_ascii=False, separators=(",", ":"), allow_nan=False)
|
||||
|
||||
|
||||
def inspect(path, encoding):
|
||||
report = json.loads((path / "report.json").read_text())
|
||||
catalog = json.loads((path / "catalog.json").read_text())
|
||||
transcript = json.loads((path / "transcript.json").read_text())
|
||||
log = path.with_suffix(".log")
|
||||
scope = next(json.loads(line) for line in reversed(log.read_text().splitlines())
|
||||
if line.startswith('{"resource_scope":'))
|
||||
observed = scope.get("payload_resource_observation") or {}
|
||||
completed = (report["status"] == "passed" and not report["cleanup_failures"]
|
||||
and report["appserver_exit_code"] == 0 and scope["abort"] is None
|
||||
and scope.get("payload_resource_observation_verified") is True
|
||||
and observed.get("payload_cleanup_verified") is True
|
||||
and observed.get("payload_exit_code") == 0)
|
||||
tools = list(catalog["data"][0]["tools"].values())
|
||||
messages = [event["message"] for event in transcript]
|
||||
ids = {m["id"] for m in messages if m.get("method") == "mcpServer/tool/call"}
|
||||
replies = [m["result"] for m in messages if m.get("id") in ids and "result" in m]
|
||||
arguments = [m["params"] for m in messages if m.get("method") == "mcpServer/tool/call"]
|
||||
count = lambda text: len(encoding.encode(text, disallowed_special=()))
|
||||
input_catalog = count(compact([{k: t[k] for k in ["name", "description", "inputSchema"]} for t in tools]))
|
||||
argument_tokens = sum(count(compact(a)) for a in arguments)
|
||||
text_tokens = sum(count(c["text"]) for r in replies for c in r["content"] if c["type"] == "text")
|
||||
return {
|
||||
"recording": path.name, "completed_with_verified_cleanup": completed,
|
||||
"input_profile": report["input_profile"], "output_profile": report["output_profile"],
|
||||
"catalog": report["catalog"], "calls": report["calls"],
|
||||
# Codex returns tools as a map; JSON member order varies by process.
|
||||
"catalog_semantic_sha256": hashlib.sha256(json.dumps(
|
||||
catalog, ensure_ascii=False, sort_keys=True, separators=(",", ":"),
|
||||
allow_nan=False).encode()).hexdigest(),
|
||||
"digests": [r["structuredContent"]["result"]["server_identity"]["catalog"]["digests"] for r in replies],
|
||||
"token_proxy": {"input_facing_catalog": input_catalog, "arguments": argument_tokens,
|
||||
"text_replies": text_tokens, "combined": input_catalog + argument_tokens + text_tokens,
|
||||
"complete_discovered_catalog": count(compact(catalog))},
|
||||
"wall_seconds": scope["elapsed_seconds"],
|
||||
"cpu_seconds": observed["cpu_stat"]["usage_usec"] / 1e6,
|
||||
"kernel_family_peak_bytes_including_file_cache": observed.get("memory_peak_bytes"),
|
||||
"observed_resource_events": observed.get("resource_events"),
|
||||
"final_whole_scope_events": scope.get("resource_events_final"),
|
||||
"sha256": {p.name: hashlib.sha256(p.read_bytes()).hexdigest()
|
||||
for p in [path / "report.json", path / "catalog.json", path / "transcript.json", log]},
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument("cases", type=Path, nargs="+")
|
||||
args = parser.parse_args()
|
||||
encoding = tiktoken.get_encoding("cl100k_base")
|
||||
records = [inspect(path, encoding) for path in args.cases]
|
||||
result = {"tiktoken": tiktoken.__version__, "encoding": encoding.name,
|
||||
"actual_model_usage": None,
|
||||
"unmeasured": ["actual host model schema injection and rendering", "model input/output/cached tokens", "model reasoning and application investigation"],
|
||||
"recordings": records}
|
||||
args.output.write_text(json.dumps(result, indent=2) + "\n")
|
||||
assert all(r["completed_with_verified_cleanup"] for r in records)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,25 @@
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { resolve } from "node:path";
|
||||
const [root, input, output] = process.argv.slice(2);
|
||||
const start = performance.now();
|
||||
const { createCatalogIdentity } = await import(
|
||||
pathToFileURL(resolve(root, "dist/catalogIdentity.js"))
|
||||
);
|
||||
const imported = performance.now();
|
||||
const cpu = process.cpuUsage();
|
||||
const identity = createCatalogIdentity(
|
||||
input === "compact" ? { budgetBytes: 13 * 1024 } : undefined,
|
||||
output,
|
||||
);
|
||||
const digests = identity.digests;
|
||||
const end = performance.now();
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
input,
|
||||
output,
|
||||
import_ms: imported - start,
|
||||
digest_ms: end - imported,
|
||||
digest_cpu_us: process.cpuUsage(cpu),
|
||||
digests,
|
||||
}),
|
||||
);
|
||||
@@ -0,0 +1,808 @@
|
||||
{
|
||||
"issue": 723,
|
||||
"source_base": "d532b18123916a9e88f98c7880ba4f984e23d715",
|
||||
"runtimes": {
|
||||
"baseline-runtime-d532b1812": {
|
||||
"source_revision": "d532b18123916a9e88f98c7880ba4f984e23d715",
|
||||
"archive_sha256": "79832e2571112400892ec1aae429b030de38c8c672a8acfa6680a2568362fed3",
|
||||
"manifest_sha256": "89cbc1688e4702a47b9a5cc66ac38d18de46b74eb550e44227f0ec2119c5e7af"
|
||||
},
|
||||
"candidate-runtime-1": {
|
||||
"source_base": "d532b18123916a9e88f98c7880ba4f984e23d715",
|
||||
"patch_sha256": "cfb0eee1ee7f3ceecff7cb77ac63e569dfcfc020e4252cb11869644628f4f743",
|
||||
"archive_sha256": "7e4275b1db784545335a4eab373a3db11b1eb5cc6bac7611521a698b0cffce56",
|
||||
"manifest_sha256": "356b3baa153650b330cea6bc7e28163565f3201b8d80aa2d6bb69f66faa8705a"
|
||||
}
|
||||
},
|
||||
"node": "24.18.0",
|
||||
"npm": "11.16.0",
|
||||
"mcp_sdk": "2.3.1",
|
||||
"client": "Codex app-server 0.163.0",
|
||||
"negotiated_protocol": "2025-06-18",
|
||||
"model_turns_started": 0,
|
||||
"actual_model_usage": null,
|
||||
"task": "Complete 209-tool discovery, then verify no target open, no cleanup pending, no bound provider, package version and tool count twice.",
|
||||
"run_order": [
|
||||
"baseline-d532-1",
|
||||
"candidate-1-1 (reversed profiles)",
|
||||
"baseline-d532-2 (reversed profiles)",
|
||||
"candidate-1-2"
|
||||
],
|
||||
"resource_bounds": {
|
||||
"cpu_affinity": [15],
|
||||
"cpu_quota_percent": 100,
|
||||
"hard_memory_gib": 4,
|
||||
"early_stop_gib": 3,
|
||||
"node_heap_gib": 2,
|
||||
"swap": 0,
|
||||
"task_ceiling": 192,
|
||||
"accounting": "Kernel cgroup process-family peak including file cache, Codex, controller and server; CPU counters at payload exit with only observer alive. Final whole-scope counters unavailable.",
|
||||
"oom_events": 0,
|
||||
"verified_payload_cleanup_recordings": 16
|
||||
},
|
||||
"tiktoken": "0.12.0",
|
||||
"encoding": "cl100k_base",
|
||||
"token_proxy_limit": "Input-facing catalog + RPC params + text result serialization; no model turn/usage, schema injection, prompts or reasoning measured. Identity paths/timestamps cause small reply token differences.",
|
||||
"scripts_sha256": {
|
||||
"codex-host-probe.py": "d0da9b39e0343b5e84f05328f56df3e82b712fd3fb32ee375cf3735a448fb80e",
|
||||
"digest-probe.mjs": "e2f8950014bb40806aef147196ca5d432c6400941d3901e1c0a0e78ce5266bcf",
|
||||
"build-digest-probe.mjs": "46060b75c014ca39f9efe317a0445a0220ea7166a08dbdacf04706670e958e6d",
|
||||
"run-cohort.py": "1cc5db864b4fe1b2d657ddfe72d468d9e72bddf5118b0bf10c9fce344ec45e9c"
|
||||
},
|
||||
"profile_digest_oracle": {
|
||||
"full": {
|
||||
"full": {
|
||||
"tools_sha256": "c3e7950408c9005ae3ae0633966609bf1c0f72a36392bc0e0c0fdfe9ea584ae6",
|
||||
"prompts_sha256": "7152916ab48bd2a9677a5ad9cdbde0a51dc53c113934952ad096e93f5f18aa7e",
|
||||
"combined_sha256": "e0b2715af8312fb565f14d75296adc947b94252e53643f08d831ca8fc8aee941"
|
||||
},
|
||||
"off": {
|
||||
"tools_sha256": "5ad649af53472a1d789b26997cb26a4e8d5d1f2a97c587ad1a6b16030ef5d764",
|
||||
"prompts_sha256": "7152916ab48bd2a9677a5ad9cdbde0a51dc53c113934952ad096e93f5f18aa7e",
|
||||
"combined_sha256": "cd84cff526daa360e30e299002e7d14427fde9c2b00f59f622fccf89ff2535c7"
|
||||
}
|
||||
},
|
||||
"compact": {
|
||||
"full": {
|
||||
"tools_sha256": "774c1b7c2c2f57beea9bed62171f046da0f17f2b3bbd7900a90855ecdd26ba79",
|
||||
"prompts_sha256": "7152916ab48bd2a9677a5ad9cdbde0a51dc53c113934952ad096e93f5f18aa7e",
|
||||
"combined_sha256": "2cbc9ae351fe4f1204a4c2b5e2e92fff8a2ecd87bc14c7ef068d6691c17dff15"
|
||||
},
|
||||
"off": {
|
||||
"tools_sha256": "efe7a17bad5617171162414df88529ff54807b870b5d510e2838ce18e2ba8bd8",
|
||||
"prompts_sha256": "7152916ab48bd2a9677a5ad9cdbde0a51dc53c113934952ad096e93f5f18aa7e",
|
||||
"combined_sha256": "07d379697201b70d45579d0811e137a0df088be88498fc9fae6806adba26c976"
|
||||
}
|
||||
}
|
||||
},
|
||||
"recordings": [
|
||||
{
|
||||
"recording": "baseline-d532-1-full-full",
|
||||
"verified_complete": true,
|
||||
"profiles": ["full", "full"],
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 3322571,
|
||||
"catalog_semantic_sha256": "799cc997a9573b5c3dfce3ac22e1fb5eb1637e81b489392f37cc81767b334359",
|
||||
"discovery_ms": 5074.2612921167165,
|
||||
"first_status_ms": 1699.8079400509596,
|
||||
"warm_status_ms": 981.8687399383634,
|
||||
"wall_seconds": 8.919,
|
||||
"cpu_seconds": 8.480822,
|
||||
"kernel_family_peak_bytes": 802246656,
|
||||
"token_proxy": {
|
||||
"input_facing_catalog": 187005,
|
||||
"arguments": 86,
|
||||
"text_replies": 22702,
|
||||
"combined": 209793,
|
||||
"complete_discovered_catalog": 845872
|
||||
},
|
||||
"raw_sha256": {
|
||||
"report.json": "f36f889ab241f704977241443ad55176de6607805f6c82013a287e968dbb428d",
|
||||
"catalog.json": "ad5dcfb828d4524f97b494884deed42389ab3e29e2f7839a12b5e95bd1c50ff1",
|
||||
"transcript.json": "7d019f379100f45ff1c3f55e66d79e233f7b736886be1823da11e4b843d5fdc1",
|
||||
"baseline-d532-1-full-full.log": "3709d55fe5412f49ee880bfee4be818711c977b026ad3133a3f8abb91111f9a7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recording": "baseline-d532-2-full-full",
|
||||
"verified_complete": true,
|
||||
"profiles": ["full", "full"],
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 3322571,
|
||||
"catalog_semantic_sha256": "799cc997a9573b5c3dfce3ac22e1fb5eb1637e81b489392f37cc81767b334359",
|
||||
"discovery_ms": 5847.158181015402,
|
||||
"first_status_ms": 1123.013640055433,
|
||||
"warm_status_ms": 911.4040599670261,
|
||||
"wall_seconds": 8.905,
|
||||
"cpu_seconds": 8.452489,
|
||||
"kernel_family_peak_bytes": 803254272,
|
||||
"token_proxy": {
|
||||
"input_facing_catalog": 187004,
|
||||
"arguments": 86,
|
||||
"text_replies": 22702,
|
||||
"combined": 209792,
|
||||
"complete_discovered_catalog": 845872
|
||||
},
|
||||
"raw_sha256": {
|
||||
"report.json": "b85792959c6838010ebd622d1cf213d2eb1cd9562da14fc49a2555bda03a0eee",
|
||||
"catalog.json": "f588658a13bc1b559008508954f2ce7f34815b326686b4c167c0c27b521cad1b",
|
||||
"transcript.json": "5326f37ffdeae98ad4679cf53f462a41fddd71eb9891040753e716489dcc1b68",
|
||||
"baseline-d532-2-full-full.log": "147000d226f6cfb3450af51ade419b20c6b0b7d7409c22e876c98830762b39eb"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recording": "baseline-d532-1-compact-full",
|
||||
"verified_complete": true,
|
||||
"profiles": ["compact", "full"],
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 2723927,
|
||||
"catalog_semantic_sha256": "d18f02920b84d39b90dc488db89a8f403f6ec35139a39a3329375a18078af1ea",
|
||||
"discovery_ms": 11450.75011299923,
|
||||
"first_status_ms": 4571.937991073355,
|
||||
"warm_status_ms": 1836.9912200141698,
|
||||
"wall_seconds": 18.99,
|
||||
"cpu_seconds": 16.522676,
|
||||
"kernel_family_peak_bytes": 808927232,
|
||||
"token_proxy": {
|
||||
"input_facing_catalog": 43507,
|
||||
"arguments": 82,
|
||||
"text_replies": 22714,
|
||||
"combined": 66303,
|
||||
"complete_discovered_catalog": 702182
|
||||
},
|
||||
"raw_sha256": {
|
||||
"report.json": "b27141e7af05b3dd43930c0df30f4872709d8db72cc953478150084e99e032f4",
|
||||
"catalog.json": "8ff7b639d3409b52761d483283c674e3670e42169ddfda0c1e2a87a207c4dcbd",
|
||||
"transcript.json": "1f91daa9703495ca6519459124e9564f0c96f5542b61563bf10029a1f6337926",
|
||||
"baseline-d532-1-compact-full.log": "fb187f2b9255e4fc1ecd2c15f9df3c7f1efc76aaba528589f5eed81e7e80e835"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recording": "baseline-d532-2-compact-full",
|
||||
"verified_complete": true,
|
||||
"profiles": ["compact", "full"],
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 2723927,
|
||||
"catalog_semantic_sha256": "d18f02920b84d39b90dc488db89a8f403f6ec35139a39a3329375a18078af1ea",
|
||||
"discovery_ms": 5926.411310909316,
|
||||
"first_status_ms": 2899.308810941875,
|
||||
"warm_status_ms": 1365.2960499748588,
|
||||
"wall_seconds": 11.148,
|
||||
"cpu_seconds": 10.613801,
|
||||
"kernel_family_peak_bytes": 816377856,
|
||||
"token_proxy": {
|
||||
"input_facing_catalog": 43507,
|
||||
"arguments": 82,
|
||||
"text_replies": 22714,
|
||||
"combined": 66303,
|
||||
"complete_discovered_catalog": 702182
|
||||
},
|
||||
"raw_sha256": {
|
||||
"report.json": "df856c2a486c513923b3c67db30bc60ca28ba3b1d0e3418d28d9d6429eca9366",
|
||||
"catalog.json": "407ff33fd8e57f2842b76ce190c2089930add6c9a4f618cc90320045f6a67ed2",
|
||||
"transcript.json": "d11dfb4c28c5355e733943e3504d3b4e11f056f0f7625e835e5cdc916a451716",
|
||||
"baseline-d532-2-compact-full.log": "0485a806e815ecb91862d37cb9200b7cb0877a3d8c69882e8299100d13769a57"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recording": "baseline-d532-1-full-off",
|
||||
"verified_complete": true,
|
||||
"profiles": ["full", "off"],
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 832191,
|
||||
"catalog_semantic_sha256": "e537c521b5dd0ac369dee706815579d9d7803497a8fc7f7d683fec267042b81c",
|
||||
"discovery_ms": 11546.154452953488,
|
||||
"first_status_ms": 4629.906311165541,
|
||||
"warm_status_ms": 2057.3992100544274,
|
||||
"wall_seconds": 19.588,
|
||||
"cpu_seconds": 16.963204,
|
||||
"kernel_family_peak_bytes": 709857280,
|
||||
"token_proxy": {
|
||||
"input_facing_catalog": 187005,
|
||||
"arguments": 86,
|
||||
"text_replies": 22710,
|
||||
"combined": 209801,
|
||||
"complete_discovered_catalog": 195042
|
||||
},
|
||||
"raw_sha256": {
|
||||
"report.json": "4963559eafd97d6aa72e2265651d9f6d3d0cd3a04965677d6fdedfe0378e7c11",
|
||||
"catalog.json": "aef1822a088446790e14373a6b9b1039cda640598d19620fdd776f6d7c49d890",
|
||||
"transcript.json": "203f7f00eca7617cdaa8c7fd45fdde3d4502cd29cf36ee0c724181ad3178410b",
|
||||
"baseline-d532-1-full-off.log": "84e5a67f6f1e97fb3c429d96d7b3de33e08eddc56b21b453fa2b3abf19dbd15b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recording": "baseline-d532-2-full-off",
|
||||
"verified_complete": true,
|
||||
"profiles": ["full", "off"],
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 832191,
|
||||
"catalog_semantic_sha256": "e537c521b5dd0ac369dee706815579d9d7803497a8fc7f7d683fec267042b81c",
|
||||
"discovery_ms": 5198.2202511280775,
|
||||
"first_status_ms": 2726.173529867083,
|
||||
"warm_status_ms": 1434.7798710223287,
|
||||
"wall_seconds": 10.325,
|
||||
"cpu_seconds": 9.75455,
|
||||
"kernel_family_peak_bytes": 703328256,
|
||||
"token_proxy": {
|
||||
"input_facing_catalog": 187005,
|
||||
"arguments": 86,
|
||||
"text_replies": 22710,
|
||||
"combined": 209801,
|
||||
"complete_discovered_catalog": 195042
|
||||
},
|
||||
"raw_sha256": {
|
||||
"report.json": "e603556898f606d6804c73a227ef6d809f09f516316255b316e5afb473170ff9",
|
||||
"catalog.json": "8200cccc7a0d7bdcf288cab0225269acadbd532aed69da7d7e8a3b4cf34b9c16",
|
||||
"transcript.json": "4d2a862a24282cac2581a36ca46fd99474835cba247203556c04015ea02096fe",
|
||||
"baseline-d532-2-full-off.log": "90b720806f3f5756408501a1f9d5dd724108647706e38aca10cbc0a35e56a37a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recording": "baseline-d532-1-compact-off",
|
||||
"verified_complete": true,
|
||||
"profiles": ["compact", "off"],
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 233547,
|
||||
"catalog_semantic_sha256": "0b81bdd81251ddd527a9c9ecbb5e6048e9c8a3be1f6195465a803d206f03fe73",
|
||||
"discovery_ms": 12976.338863139972,
|
||||
"first_status_ms": 6152.441991027445,
|
||||
"warm_status_ms": 3121.8184099998325,
|
||||
"wall_seconds": 23.47,
|
||||
"cpu_seconds": 19.234341,
|
||||
"kernel_family_peak_bytes": 665423872,
|
||||
"token_proxy": {
|
||||
"input_facing_catalog": 43507,
|
||||
"arguments": 82,
|
||||
"text_replies": 22690,
|
||||
"combined": 66279,
|
||||
"complete_discovered_catalog": 51352
|
||||
},
|
||||
"raw_sha256": {
|
||||
"report.json": "c3c21635cf212eba3902ad64f956244ddfe122425fa3b39ec280b5296ebe23c6",
|
||||
"catalog.json": "e5a4444ea91913e68705a6bd6afee2b8ea0a43b2f91f8628ada597e57afab5db",
|
||||
"transcript.json": "58df156c85a8b1ec325945db08e4e84e8174dfd739f48e4d83b2a3537f7a52b9",
|
||||
"baseline-d532-1-compact-off.log": "8d5b2ddcf5b31ad4635269837e217dd2242a91d82ea961d01e2ab3a2076b9e35"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recording": "baseline-d532-2-compact-off",
|
||||
"verified_complete": true,
|
||||
"profiles": ["compact", "off"],
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 233547,
|
||||
"catalog_semantic_sha256": "0b81bdd81251ddd527a9c9ecbb5e6048e9c8a3be1f6195465a803d206f03fe73",
|
||||
"discovery_ms": 5907.094460912049,
|
||||
"first_status_ms": 2939.780990127474,
|
||||
"warm_status_ms": 1040.7937010750175,
|
||||
"wall_seconds": 10.742,
|
||||
"cpu_seconds": 10.176561,
|
||||
"kernel_family_peak_bytes": 667668480,
|
||||
"token_proxy": {
|
||||
"input_facing_catalog": 43507,
|
||||
"arguments": 82,
|
||||
"text_replies": 22690,
|
||||
"combined": 66279,
|
||||
"complete_discovered_catalog": 51352
|
||||
},
|
||||
"raw_sha256": {
|
||||
"report.json": "14aed217fcad50915b211e4ee9894ebf9cb4abd8d4c4a4e007cc6fe2b397d44a",
|
||||
"catalog.json": "e55a5b2cba59b09ac4f63831c2af6682d162bf5b05259b3c0f9db8eb2bc67e44",
|
||||
"transcript.json": "e9a9ac8b657da35501e577bf1d1e18c89923c5bcdbb1307c8254d512bbdc7b53",
|
||||
"baseline-d532-2-compact-off.log": "e7c0c68bd1088f11107a6713df1efb74f01ff3479d1913977fe22935814c912c"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recording": "candidate-1-1-full-full",
|
||||
"verified_complete": true,
|
||||
"profiles": ["full", "full"],
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 3322571,
|
||||
"catalog_semantic_sha256": "799cc997a9573b5c3dfce3ac22e1fb5eb1637e81b489392f37cc81767b334359",
|
||||
"discovery_ms": 7035.046560922638,
|
||||
"first_status_ms": 1280.1523089874536,
|
||||
"warm_status_ms": 962.2103709261864,
|
||||
"wall_seconds": 10.309,
|
||||
"cpu_seconds": 9.82015,
|
||||
"kernel_family_peak_bytes": 806117376,
|
||||
"token_proxy": {
|
||||
"input_facing_catalog": 187004,
|
||||
"arguments": 84,
|
||||
"text_replies": 22690,
|
||||
"combined": 209778,
|
||||
"complete_discovered_catalog": 845872
|
||||
},
|
||||
"raw_sha256": {
|
||||
"report.json": "4e6d4cb20d4b435b47164535930c60902eed44c4efec0665d9eeb2bd0635fd39",
|
||||
"catalog.json": "f0700961a2a999671107dc5e5274098a5d97047e834654c7c3c7ee0c420a91b5",
|
||||
"transcript.json": "4d0b64606c913325ce83bd797b2286d0e9758f09c526dacfce422756cf5212d8",
|
||||
"candidate-1-1-full-full.log": "9d37d44776cfd9f41811e0a608c79ed63257383218e01f602317c2dc5a9de10f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recording": "candidate-1-2-full-full",
|
||||
"verified_complete": true,
|
||||
"profiles": ["full", "full"],
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 3322571,
|
||||
"catalog_semantic_sha256": "799cc997a9573b5c3dfce3ac22e1fb5eb1637e81b489392f37cc81767b334359",
|
||||
"discovery_ms": 5431.887832004577,
|
||||
"first_status_ms": 1290.1408409234136,
|
||||
"warm_status_ms": 1058.6841800250113,
|
||||
"wall_seconds": 8.819,
|
||||
"cpu_seconds": 8.06667,
|
||||
"kernel_family_peak_bytes": 801071104,
|
||||
"token_proxy": {
|
||||
"input_facing_catalog": 187004,
|
||||
"arguments": 78,
|
||||
"text_replies": 22690,
|
||||
"combined": 209772,
|
||||
"complete_discovered_catalog": 845872
|
||||
},
|
||||
"raw_sha256": {
|
||||
"report.json": "e2853484e579376a8edb168f84ad2add2ffdd30ce78e123b2dfcdd17d524344e",
|
||||
"catalog.json": "5c918daeb3f8b10625927a87f3f95367a2a2823cc7c098aa8ad3d8d5b47aa6ad",
|
||||
"transcript.json": "1e22387d227134741eac842b8000c5bb73a42e2368c3c53e21bc1d45c17ed819",
|
||||
"candidate-1-2-full-full.log": "877a3bdfe67875bb5ab1981850da2fe519428708184eef55a28c5fa18bb727ea"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recording": "candidate-1-1-compact-full",
|
||||
"verified_complete": true,
|
||||
"profiles": ["compact", "full"],
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 2723927,
|
||||
"catalog_semantic_sha256": "d18f02920b84d39b90dc488db89a8f403f6ec35139a39a3329375a18078af1ea",
|
||||
"discovery_ms": 5128.74938105233,
|
||||
"first_status_ms": 1351.6553710214794,
|
||||
"warm_status_ms": 1321.080510970205,
|
||||
"wall_seconds": 8.815,
|
||||
"cpu_seconds": 8.478922,
|
||||
"kernel_family_peak_bytes": 781271040,
|
||||
"token_proxy": {
|
||||
"input_facing_catalog": 43507,
|
||||
"arguments": 80,
|
||||
"text_replies": 22702,
|
||||
"combined": 66289,
|
||||
"complete_discovered_catalog": 702182
|
||||
},
|
||||
"raw_sha256": {
|
||||
"report.json": "ee38fcca794a959271af78a6c65377f411fdbe3b7b97068835b36586ec804ddf",
|
||||
"catalog.json": "db8f1cbaa97c09672537d87ac48457bdb599ae439bca2d8a9216a032edbbdd88",
|
||||
"transcript.json": "22d7a8b00909ad82e40ca7beb21d793bff61dacd88ba0d345ef82eeacf177516",
|
||||
"candidate-1-1-compact-full.log": "ae17a479c9f094e522e15b58617302bf098464a4f55cea21cc913d18ba02afd2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recording": "candidate-1-2-compact-full",
|
||||
"verified_complete": true,
|
||||
"profiles": ["compact", "full"],
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 2723927,
|
||||
"catalog_semantic_sha256": "d18f02920b84d39b90dc488db89a8f403f6ec35139a39a3329375a18078af1ea",
|
||||
"discovery_ms": 5686.0627620480955,
|
||||
"first_status_ms": 1266.2633110303432,
|
||||
"warm_status_ms": 1025.3590901847929,
|
||||
"wall_seconds": 8.893,
|
||||
"cpu_seconds": 8.191988,
|
||||
"kernel_family_peak_bytes": 780988416,
|
||||
"token_proxy": {
|
||||
"input_facing_catalog": 43507,
|
||||
"arguments": 82,
|
||||
"text_replies": 22702,
|
||||
"combined": 66291,
|
||||
"complete_discovered_catalog": 702182
|
||||
},
|
||||
"raw_sha256": {
|
||||
"report.json": "1ae8930195ef5d5b6d5118ffaad3d0d2bedaaf114b3887a28357504be023821f",
|
||||
"catalog.json": "7e65fcac2fe7257a2f1c058f2b9ad1200772072fd7cabd4ea315bf4c48307303",
|
||||
"transcript.json": "5a0220b5fe36ae31af91f9eb7f8006290fcf790c774096b838f7287c472e94c1",
|
||||
"candidate-1-2-compact-full.log": "2e4baa5d7e627be27f87739d03f2a4e762ae1675a63e14cc7b5adf883c24192c"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recording": "candidate-1-1-full-off",
|
||||
"verified_complete": true,
|
||||
"profiles": ["full", "off"],
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 832191,
|
||||
"catalog_semantic_sha256": "e537c521b5dd0ac369dee706815579d9d7803497a8fc7f7d683fec267042b81c",
|
||||
"discovery_ms": 5637.611771002412,
|
||||
"first_status_ms": 1178.2668600790203,
|
||||
"warm_status_ms": 1036.6536700166762,
|
||||
"wall_seconds": 8.791,
|
||||
"cpu_seconds": 8.201682,
|
||||
"kernel_family_peak_bytes": 656801792,
|
||||
"token_proxy": {
|
||||
"input_facing_catalog": 187005,
|
||||
"arguments": 82,
|
||||
"text_replies": 22698,
|
||||
"combined": 209785,
|
||||
"complete_discovered_catalog": 195042
|
||||
},
|
||||
"raw_sha256": {
|
||||
"report.json": "cd706d0ab9a3dd9662ec73ecc45deefdbd40a4a42cd9ee2735be0f8798cc1933",
|
||||
"catalog.json": "9d1fd380d490edf4b6403e3daed015b72bc654b96dfb06dbe7d776e6a6146d76",
|
||||
"transcript.json": "0e3f0c66a2b638729d341af8d8a379576d267b53c1dcea5563e1afa4f9245cc8",
|
||||
"candidate-1-1-full-off.log": "1baf3976c762923680cd17cbef36778b3982e90005eb34bb8ec3ce8030ebd3fb"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recording": "candidate-1-2-full-off",
|
||||
"verified_complete": true,
|
||||
"profiles": ["full", "off"],
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 832191,
|
||||
"catalog_semantic_sha256": "e537c521b5dd0ac369dee706815579d9d7803497a8fc7f7d683fec267042b81c",
|
||||
"discovery_ms": 5316.555531928316,
|
||||
"first_status_ms": 1383.681601146236,
|
||||
"warm_status_ms": 1108.902801061049,
|
||||
"wall_seconds": 8.698,
|
||||
"cpu_seconds": 8.19166,
|
||||
"kernel_family_peak_bytes": 662822912,
|
||||
"token_proxy": {
|
||||
"input_facing_catalog": 187004,
|
||||
"arguments": 82,
|
||||
"text_replies": 22698,
|
||||
"combined": 209784,
|
||||
"complete_discovered_catalog": 195042
|
||||
},
|
||||
"raw_sha256": {
|
||||
"report.json": "89c5f579cb8f8a1860b3f605042ffc51db4aaae69f7a22213256f93e42b91672",
|
||||
"catalog.json": "0301c40f667a17a9238ad8d9abdbb8e8c7b07a7d0dcc6ae062f5885e0b19dfc7",
|
||||
"transcript.json": "7e884d99d3335707f9108a63528fd2830b1c21e7888cfc320852d75924d833cb",
|
||||
"candidate-1-2-full-off.log": "ba34788aa92aa60e79e832659c48f304396d1df41f493a509ff474a152fa7c6c"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recording": "candidate-1-1-compact-off",
|
||||
"verified_complete": true,
|
||||
"profiles": ["compact", "off"],
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 233547,
|
||||
"catalog_semantic_sha256": "0b81bdd81251ddd527a9c9ecbb5e6048e9c8a3be1f6195465a803d206f03fe73",
|
||||
"discovery_ms": 4922.329220920801,
|
||||
"first_status_ms": 813.2044598460197,
|
||||
"warm_status_ms": 1415.0590698700398,
|
||||
"wall_seconds": 7.982,
|
||||
"cpu_seconds": 7.401054,
|
||||
"kernel_family_peak_bytes": 629260288,
|
||||
"token_proxy": {
|
||||
"input_facing_catalog": 43507,
|
||||
"arguments": 80,
|
||||
"text_replies": 22678,
|
||||
"combined": 66265,
|
||||
"complete_discovered_catalog": 51352
|
||||
},
|
||||
"raw_sha256": {
|
||||
"report.json": "89ae06a9c0e8f4c9b85aa711fc6f71204798c990e582f5702713f78612c4fce2",
|
||||
"catalog.json": "d6a231b2babd7885d58f9712881f96fa9dc2b763cc7efd802bc5676dd71fe28a",
|
||||
"transcript.json": "65fbdb48a9bc2aa65a815ea405d1210e298f908ddbab5e60f815c4e01a0f0e98",
|
||||
"candidate-1-1-compact-off.log": "76501971a3698b13435b96647b59ee092689397012d453e62c821e4ecf40e988"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recording": "candidate-1-2-compact-off",
|
||||
"verified_complete": true,
|
||||
"profiles": ["compact", "off"],
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 233547,
|
||||
"catalog_semantic_sha256": "0b81bdd81251ddd527a9c9ecbb5e6048e9c8a3be1f6195465a803d206f03fe73",
|
||||
"discovery_ms": 4839.713891968131,
|
||||
"first_status_ms": 1174.165149917826,
|
||||
"warm_status_ms": 1409.5584598835558,
|
||||
"wall_seconds": 8.289,
|
||||
"cpu_seconds": 7.764389,
|
||||
"kernel_family_peak_bytes": 627363840,
|
||||
"token_proxy": {
|
||||
"input_facing_catalog": 43507,
|
||||
"arguments": 80,
|
||||
"text_replies": 22678,
|
||||
"combined": 66265,
|
||||
"complete_discovered_catalog": 51352
|
||||
},
|
||||
"raw_sha256": {
|
||||
"report.json": "a4ef1220b73c39421ccfc22c2102c613fd0a33900cbc8683dad1dc3bc034cf5a",
|
||||
"catalog.json": "39ae1d7385fe8684f037e354ec2ab1126243fd31667be1b1cc559a9d1e40149c",
|
||||
"transcript.json": "d60eae5fdbfb4095f040d54103a6e9b1d2ff8b8f64126976d14b0a3e024969f7",
|
||||
"candidate-1-2-compact-off.log": "0e195ca040565e29e1f6d25b087a706d0d500ff1752a24bff54ad30704bd6d6b"
|
||||
}
|
||||
}
|
||||
],
|
||||
"digest_phases": [
|
||||
{
|
||||
"recording": "baseline-d532-1-compact-full-digest",
|
||||
"input": "compact",
|
||||
"output": "full",
|
||||
"import_ms": 3827.9035209999997,
|
||||
"digest_ms": 2916.01332,
|
||||
"digest_cpu_us": {
|
||||
"user": 2587474,
|
||||
"system": 151462
|
||||
},
|
||||
"raw_sha256": "28a8e0b5c847978be9d71e4d5ed6953758e266e697b820f45f1d2141861c95e3"
|
||||
},
|
||||
{
|
||||
"recording": "baseline-d532-1-compact-off-digest",
|
||||
"input": "compact",
|
||||
"output": "off",
|
||||
"import_ms": 3873.5359620000004,
|
||||
"digest_ms": 3790.342601,
|
||||
"digest_cpu_us": {
|
||||
"user": 3121749,
|
||||
"system": 212457
|
||||
},
|
||||
"raw_sha256": "b88dd91f159a1763d547f1e26637afaf3a03b28faad3295c2deff8c90e7d7707"
|
||||
},
|
||||
{
|
||||
"recording": "baseline-d532-1-full-full-digest",
|
||||
"input": "full",
|
||||
"output": "full",
|
||||
"import_ms": 1766.4657209999998,
|
||||
"digest_ms": 0.06419000000005326,
|
||||
"digest_cpu_us": {
|
||||
"user": 65,
|
||||
"system": 12
|
||||
},
|
||||
"raw_sha256": "0f18c0d63def7df73b6183240c49011aca9cd85899a221fdb01908da1d79120f"
|
||||
},
|
||||
{
|
||||
"recording": "baseline-d532-1-full-off-digest",
|
||||
"input": "full",
|
||||
"output": "off",
|
||||
"import_ms": 5377.792571,
|
||||
"digest_ms": 3209.784441,
|
||||
"digest_cpu_us": {
|
||||
"user": 2585090,
|
||||
"system": 252879
|
||||
},
|
||||
"raw_sha256": "2de37e6aac00f37003f619c4b4a5a747637e0b188e9892d5a37a091ae568400a"
|
||||
},
|
||||
{
|
||||
"recording": "baseline-d532-2-compact-full-digest",
|
||||
"input": "compact",
|
||||
"output": "full",
|
||||
"import_ms": 1807.87952,
|
||||
"digest_ms": 1512.0799399999999,
|
||||
"digest_cpu_us": {
|
||||
"user": 1421207,
|
||||
"system": 88507
|
||||
},
|
||||
"raw_sha256": "a330124a6cec474e5337aa5a40c33d31fa0bdce304554c1c68f79954bf5cdd33"
|
||||
},
|
||||
{
|
||||
"recording": "baseline-d532-2-compact-off-digest",
|
||||
"input": "compact",
|
||||
"output": "off",
|
||||
"import_ms": 1854.8867300000002,
|
||||
"digest_ms": 1402.711411,
|
||||
"digest_cpu_us": {
|
||||
"user": 1284446,
|
||||
"system": 116933
|
||||
},
|
||||
"raw_sha256": "02f19af3b9836796d85f81effd5e86bab0aa4978eeae0c59b8c35e57d03dd17b"
|
||||
},
|
||||
{
|
||||
"recording": "baseline-d532-2-full-full-digest",
|
||||
"input": "full",
|
||||
"output": "full",
|
||||
"import_ms": 1572.84796,
|
||||
"digest_ms": 0.0849799999998595,
|
||||
"digest_cpu_us": {
|
||||
"user": 85,
|
||||
"system": 15
|
||||
},
|
||||
"raw_sha256": "a9fca1656b8763f5ef305548eceb7bbd27cdeb426c6d6934a891c82823a54f31"
|
||||
},
|
||||
{
|
||||
"recording": "baseline-d532-2-full-off-digest",
|
||||
"input": "full",
|
||||
"output": "off",
|
||||
"import_ms": 1814.550491,
|
||||
"digest_ms": 1280.248369,
|
||||
"digest_cpu_us": {
|
||||
"user": 1166910,
|
||||
"system": 108321
|
||||
},
|
||||
"raw_sha256": "350a276f6f408de517db2b19af7cd7490c243b71338854d4acb54418f1a00b64"
|
||||
},
|
||||
{
|
||||
"recording": "candidate-1-1-compact-full-digest",
|
||||
"input": "compact",
|
||||
"output": "full",
|
||||
"import_ms": 1760.5044,
|
||||
"digest_ms": 0.08051999999997861,
|
||||
"digest_cpu_us": {
|
||||
"user": 76,
|
||||
"system": 14
|
||||
},
|
||||
"raw_sha256": "3ebc2dac6cff195b54918e5afaedc0b73fe9f1a63ef12ebb253d9a1aca683c8c"
|
||||
},
|
||||
{
|
||||
"recording": "candidate-1-1-compact-off-digest",
|
||||
"input": "compact",
|
||||
"output": "off",
|
||||
"import_ms": 1922.20413,
|
||||
"digest_ms": 0.07567999999992026,
|
||||
"digest_cpu_us": {
|
||||
"user": 75,
|
||||
"system": 11
|
||||
},
|
||||
"raw_sha256": "d5916e1e5760f9c7ce5a42ee8b7635477464a5a7cb7d379309666357b6fbe35f"
|
||||
},
|
||||
{
|
||||
"recording": "candidate-1-1-full-full-digest",
|
||||
"input": "full",
|
||||
"output": "full",
|
||||
"import_ms": 1777.54332,
|
||||
"digest_ms": 0.08724099999994905,
|
||||
"digest_cpu_us": {
|
||||
"user": 93,
|
||||
"system": 13
|
||||
},
|
||||
"raw_sha256": "ed68a8c592c9556b7a279a90b7324d5549f9dc197fe593d27c55c5de24827481"
|
||||
},
|
||||
{
|
||||
"recording": "candidate-1-1-full-off-digest",
|
||||
"input": "full",
|
||||
"output": "off",
|
||||
"import_ms": 2062.896171,
|
||||
"digest_ms": 0.06988900000033027,
|
||||
"digest_cpu_us": {
|
||||
"user": 69,
|
||||
"system": 12
|
||||
},
|
||||
"raw_sha256": "166dcdaa064c334dcc882429c2434c0353b0f6d2ccdea986138a2149ccccf10a"
|
||||
},
|
||||
{
|
||||
"recording": "candidate-1-2-compact-full-digest",
|
||||
"input": "compact",
|
||||
"output": "full",
|
||||
"import_ms": 1813.35421,
|
||||
"digest_ms": 0.06941099999994549,
|
||||
"digest_cpu_us": {
|
||||
"user": 73,
|
||||
"system": 10
|
||||
},
|
||||
"raw_sha256": "86497ee4962fd03fcad4f2bcc31e95f5f57d9c4270cc0578d4b43a076ec01b53"
|
||||
},
|
||||
{
|
||||
"recording": "candidate-1-2-compact-off-digest",
|
||||
"input": "compact",
|
||||
"output": "off",
|
||||
"import_ms": 2024.0317209999998,
|
||||
"digest_ms": 0.08607000000029075,
|
||||
"digest_cpu_us": {
|
||||
"user": 89,
|
||||
"system": 13
|
||||
},
|
||||
"raw_sha256": "82b47ed9bc203f1417f5f76de808d493c171f95598934fc9ebbd6f63be43b938"
|
||||
},
|
||||
{
|
||||
"recording": "candidate-1-2-full-full-digest",
|
||||
"input": "full",
|
||||
"output": "full",
|
||||
"import_ms": 1917.6121309999999,
|
||||
"digest_ms": 0.07732000000009975,
|
||||
"digest_cpu_us": {
|
||||
"user": 77,
|
||||
"system": 11
|
||||
},
|
||||
"raw_sha256": "410395499363c55e53fe4d8488b5e83c54170786fe1814389fd3bf4ec317c6ac"
|
||||
},
|
||||
{
|
||||
"recording": "candidate-1-2-full-off-digest",
|
||||
"input": "full",
|
||||
"output": "off",
|
||||
"import_ms": 1715.96008,
|
||||
"digest_ms": 0.08907999999996719,
|
||||
"digest_cpu_us": {
|
||||
"user": 104,
|
||||
"system": 15
|
||||
},
|
||||
"raw_sha256": "f6ea0fffc2974e02bdab1348736533c8dbd677c2311e339a945d760bd9337f9a"
|
||||
}
|
||||
],
|
||||
"build_digest_phase": [
|
||||
{
|
||||
"phase": "before",
|
||||
"operation": "createCatalogDigests",
|
||||
"import_ms": 2103.978981,
|
||||
"compute_ms": 1640.0212299999998,
|
||||
"compute_cpu_us": {
|
||||
"user": 1487510,
|
||||
"system": 148234
|
||||
},
|
||||
"raw_sha256": "3a4bfe5e4297e95cf0ab3b832bb34c81edeca02e7f884db7dd5a6944c2ee1674"
|
||||
},
|
||||
{
|
||||
"phase": "after",
|
||||
"operation": "createCatalogDigestProfiles",
|
||||
"import_ms": 1752.475121,
|
||||
"compute_ms": 2379.7143199999996,
|
||||
"compute_cpu_us": {
|
||||
"user": 2232836,
|
||||
"system": 140061
|
||||
},
|
||||
"raw_sha256": "b25120373c7433457e30ff00925ca2771c0d61ba96828fe89e63be651b4f6e4a"
|
||||
}
|
||||
],
|
||||
"failed_attempts": [
|
||||
{
|
||||
"recording": "codex-host-full-1",
|
||||
"outcome": "Harness expected counts.tools rather than canonical counts.mcp_tools; corrected oracle, not a completed task.",
|
||||
"raw_sha256": {
|
||||
"report.json": "ac3d531a9dad31e31cb9a5957ed1f5e6415534fda1e52c64bd93a07a36ef7d0f",
|
||||
"codex-host-full-1.log": "7c5f13e2976efef59306b9a2fabd2e2031277d3c5aed2b74f4ff2ad80c25a61c"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recording": "codex-host-matrix-compact-off-1",
|
||||
"outcome": "Functional checks passed but MCP child remained at payload exit. Excluded from qualified cohort. Controller now waits for allocated scope to drain without targeting a PID.",
|
||||
"raw_sha256": {
|
||||
"report.json": "1cbc9df5aff972538629ed695271f0c3a66fcfe546208b392d09b3b7cf72f97c",
|
||||
"codex-host-matrix-compact-off-1.log": "997e13a9ea7b3c5247d5037279fbef1d0dfd238eed0781748cc2ddf3afc706e7"
|
||||
}
|
||||
}
|
||||
],
|
||||
"unverified": [
|
||||
"Model selection/injection and actual model tokens",
|
||||
"Provider analysis, target execution and end-to-end reverse-engineering application quality",
|
||||
"Source-mode speedup: absent build records still use dynamic fallback",
|
||||
"Other MCP clients/OS compatibility not established by this Codex experiment"
|
||||
],
|
||||
"validation": [
|
||||
{
|
||||
"check": "check-profiles",
|
||||
"payload_exit_code": 1,
|
||||
"owned_cleanup_verified": true,
|
||||
"raw_sha256": "04dd1ad8142e61e9c2960ec55a3a7f0421f4c22f5cb5b9702761a0d7f29d3cc4",
|
||||
"source_head": "pre-rebase candidate",
|
||||
"outcome": "Unused build-only export identified; existing output-policy coverage extended to the profile builder."
|
||||
},
|
||||
{
|
||||
"check": "build-latest",
|
||||
"payload_exit_code": 0,
|
||||
"owned_cleanup_verified": true,
|
||||
"raw_sha256": "f430e38ecb205aa7bde17b48ff7ad97805b18511dbbfee8bdbab605f4f93b86e",
|
||||
"source_head": "7ad06628df042a7c8034155a98fc51c35fa0704c",
|
||||
"outcome": "passed"
|
||||
},
|
||||
{
|
||||
"check": "tests-latest",
|
||||
"payload_exit_code": 0,
|
||||
"owned_cleanup_verified": true,
|
||||
"raw_sha256": "58b06f5be6ae5c0cb871cba6788b3f84581289a47ffe26aaad8897b606c94e2d",
|
||||
"source_head": "7ad06628df042a7c8034155a98fc51c35fa0704c",
|
||||
"outcome": "passed"
|
||||
},
|
||||
{
|
||||
"check": "check-latest",
|
||||
"payload_exit_code": 0,
|
||||
"owned_cleanup_verified": true,
|
||||
"raw_sha256": "2373fec55a74515f6e3036d46becd4ed76149d9a70731a64c5dddacaeeb920cc",
|
||||
"source_head": "7ad06628df042a7c8034155a98fc51c35fa0704c",
|
||||
"outcome": "passed"
|
||||
},
|
||||
{
|
||||
"check": "docs-latest",
|
||||
"payload_exit_code": 0,
|
||||
"owned_cleanup_verified": true,
|
||||
"raw_sha256": "23ef004935364b7a1ad2634610b67c1d739bde4f4c455552a473a05b346920ee",
|
||||
"source_head": "7ad06628df042a7c8034155a98fc51c35fa0704c",
|
||||
"outcome": "passed"
|
||||
}
|
||||
],
|
||||
"post_rebase_qualification": {
|
||||
"head": "7ad06628df042a7c8034155a98fc51c35fa0704c",
|
||||
"base": "e84468c0b",
|
||||
"client": "Codex app-server 0.163.0",
|
||||
"profiles": ["compact", "off"],
|
||||
"outcome": "Complete discovery and first/warm status facts pass; allocated scope drains; no model turns. Qualification only; not relabeled as the pinned before/after measurements.",
|
||||
"raw_sha256": {
|
||||
"final-latest-compact-off/report.json": "ba6f549fe087f55dc48011daf92bd18f4bfc2a32e6185081ab2b7436bf39b1b8",
|
||||
"final-latest-compact-off/transcript.json": "461a25723c2a4bb4c18675ffa613e98a8592a4e933054fa8643f1da73fc4686f",
|
||||
"final-latest-compact-off.log": "4efc56e69b8f0d1111345bce10d0d0792b510e18901f52d10e538374257e1d61"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
# MCP schema profile identity startup
|
||||
|
||||
Continues [#723](https://github.com/morluto/rea/issues/723). The default catalog
|
||||
identity was already recorded at build time (#4041). Compact input and omitted
|
||||
output advertisement profiles still converted and hashed the complete catalog
|
||||
on demand. This change records the four built-in combinations at the same
|
||||
owning build step. Canonical tool facts and output-schema conversion are shared
|
||||
while building those records. Only immutable hash triples reach the runtime:
|
||||
the generated module grows from 411 to 1,320 bytes.
|
||||
|
||||
Exact full/compact and full/off selections choose their matching record. Source
|
||||
execution and custom compact budgets retain lazy dynamic calculation, including
|
||||
snapshotting the caller's budget. The build consumes the compiled contracts and
|
||||
pinned converter before bundling; rebuilding invalidates the records. No live
|
||||
provider state is cached. Output-off identities still cover canonical output
|
||||
contracts, as before. All four digest triples match the pre-change runtime.
|
||||
|
||||
## Completed task and oracle
|
||||
|
||||
A fresh **actual Codex app-server 0.163.0** discovers every tool, then reports
|
||||
whether a target is open, cleanup is pending, a provider is bound, and whether
|
||||
the package version and catalog count match, twice in the same session.
|
||||
The oracle is the selected package metadata, the pinned 209-tool contract,
|
||||
no-target state, text/structured-result agreement and original pre-change
|
||||
identities. Canonicalized actual-client catalogs agree before/after within
|
||||
each profile; JSON object order in Codex's tool map varies by process.
|
||||
|
||||
The [driver](mcp-profile-identity.py) uses the installed client's version-specific
|
||||
`initialize`, `thread/start`, `mcpServerStatus/list` and `mcpServer/tool/call`
|
||||
protocol ([official app-server documentation](https://learn.chatgpt.com/docs/app-server)).
|
||||
It uses a fresh temporary account/configuration and starts **no model turns**.
|
||||
This establishes actual-client schema discovery and direct tool-call
|
||||
compatibility, not model analysis quality or actual model token savings.
|
||||
Existing compact/off choices remain explicit; neither becomes a new default.
|
||||
|
||||
Baseline: main `d532b18123916a9e88f98c7880ba4f984e23d715`. Both packaged runtimes
|
||||
are frozen before measurement; their source patch, package/manifests and raw
|
||||
transcripts/resource observations are retained outside Git, pinned by the
|
||||
[receipt](mcp-profile-identity.json). Later validation does not relabel timings.
|
||||
Node 24.18.0/npm 11.16.0, MCP SDK 2.3.1, negotiated protocol `2025-06-18`, Linux
|
||||
x86-64. Two repetitions alternate baseline/candidate cohorts and reverse profile
|
||||
order. Every recording starts a fresh server and includes discovery, first and
|
||||
warm status calls, verification and cleanup. OS caches are unflushed.
|
||||
|
||||
The existing guard admits one CPU, a 4 GiB hard/3 GiB early memory boundary,
|
||||
2 GiB Node heaps, no swap and 192 tasks. Kernel family peaks include file cache,
|
||||
Codex, REA and controller; they are not provider heap measurements. All 16
|
||||
qualified recordings exit successfully with only the observer remaining, no
|
||||
resource stop and zero observed OOM events. Final whole-scope counters are
|
||||
unavailable. The host's CPU is shared with unrelated work; ranges retain the
|
||||
observed variation.
|
||||
|
||||
## Measurements
|
||||
|
||||
Ranges show both repetitions. Times include the whole task and shutdown.
|
||||
|
||||
| Input / output profile | Wall seconds before → after | CPU seconds before → after | Family peak MiB before → after |
|
||||
| ---------------------- | --------------------------- | -------------------------- | ------------------------------ |
|
||||
| full / full | 8.91–8.92 → 8.82–10.31 | 8.45–8.48 → 8.07–9.82 | 765–766 → 764–769 |
|
||||
| compact / full | 11.15–18.99 → 8.82–8.89 | 10.61–16.52 → 8.19–8.48 | 772–779 → 745–745 |
|
||||
| full / off | 10.33–19.59 → 8.70–8.79 | 9.76–16.96 → 8.19–8.20 | 671–677 → 626–632 |
|
||||
| compact / off | 10.74–23.47 → 7.98–8.29 | 10.18–19.23 → 7.40–7.76 | 635–637 → 598–600 |
|
||||
|
||||
First/warm call times remain separately recorded in the receipt. Across the
|
||||
three non-default profiles the first call ranges from 2.73–6.15 s before to
|
||||
0.81–1.38 s after; warm calls range from 1.04–3.12 s before to 1.03–1.42 s after.
|
||||
The default profile has no established gain; one candidate repetition is slower.
|
||||
|
||||
Fresh-process [identity probes](mcp-profile-digest.mjs) isolate the lazy read
|
||||
from module loading. The non-default digest computation takes 1.28–3.79 s
|
||||
before and under 0.2 ms after. Building all four identities shares schema
|
||||
conversion: isolated compute CPU increases from 1.636 s (one identity) to
|
||||
2.373 s (four). Full uncached builds were 42.59 → 81.09 s wall, 41.59 → 68.27 s
|
||||
CPU, 3318.5 → 3216.4 MiB peak. These single builds include TypeScript, bundling
|
||||
and concurrent host contention, so their difference is not attributed solely
|
||||
to digest generation. The isolated phase is one observation per version.
|
||||
|
||||
There is **no token reduction from this change**. Each profile's complete
|
||||
catalog size and serialization-token estimate are unchanged. Full/full remains
|
||||
3,322,571 bytes as exposed by this client; compact/off remains 233,547 bytes
|
||||
through existing configuration. The input-facing catalog plus both argument
|
||||
and text replies is about 209.8k proxy tokens with full input and 66.3k with
|
||||
compact input, before and after. Small differences come from fresh timestamps
|
||||
and runtime paths. The [cost script](mcp-profile-costs.py) uses
|
||||
`tiktoken==0.12.0`/`cl100k_base`; these estimates are not actual model usage or a
|
||||
claim about what the host injects. Output schemas, hidden prompts and model
|
||||
reasoning must not be inferred from that projection.
|
||||
|
||||
## Validation and remaining scope
|
||||
|
||||
Executed: four files / 46 tests pass, including real production stdio doctor
|
||||
for all four profiles, source catalog/schema consistency, output-off identity
|
||||
coverage, malformed replies, startup cancellation/cleanup, generated immutable
|
||||
record reuse and caller-selected custom budgets. The pre-change runtime is
|
||||
also an independent oracle for all four complete hash triples.
|
||||
|
||||
After rebasing onto main `e84468c0b`, the four files / 46 tests pass again,
|
||||
as do `npm run check` (types, lint, format, knip, metadata and architecture)
|
||||
and `npm run docs:check` (including five README consistency tests). Actual
|
||||
Codex compact/off discovery and both status calls pass again with verified
|
||||
cleanup. This final qualification does not replace the pinned timing cohort.
|
||||
An initial static check identified the build-only export as untracked; the
|
||||
existing output-policy regression now directly exercises its four profiles.
|
||||
|
||||
Earlier exploratory attempts remain excluded: one controller expected the
|
||||
wrong count key; one functional compact/off run left a child alive at payload
|
||||
exit. The corrected controller waits for its allocated scope to drain without
|
||||
killing individual PIDs; all later qualified runs verify that drain. Neither
|
||||
failed attempt establishes a production regression.
|
||||
|
||||
SDK 2.3.1 already supports a named `toolDefinition`, used by REA's doctor to
|
||||
avoid preparing every output validator for its one probe. Generic external SDK
|
||||
callers still incur their own catalog preparation. Input presentation and
|
||||
output refs already have owning reuse; these were not duplicate fixes.
|
||||
Target/provider investigations, other clients/platforms and actual model
|
||||
analysis cost remain unverified by this no-target startup task.
|
||||
|
||||
To reproduce with the pinned client and a built runtime, run under the existing
|
||||
resource guard:
|
||||
|
||||
```sh
|
||||
python3 experiments/analysis-efficiency/resource-run.py --observe-payload-exit -- \
|
||||
python3 experiments/analysis-efficiency/mcp-profile-identity.py \
|
||||
--output /tmp/rea-profile-case --runtime /absolute/built/rea \
|
||||
--node /absolute/node24/bin/node --codex /absolute/codex-0.163.0 \
|
||||
--input-profile compact --output-profile off
|
||||
```
|
||||
|
||||
Retain the resource output as `/tmp/rea-profile-case.log` for the cost script.
|
||||
The driver is pinned to Codex 0.163.0's experimental protocol and the 209-tool
|
||||
catalog in this receipt; update its oracle deliberately for another revision.
|
||||
@@ -0,0 +1,199 @@
|
||||
"""Exercise real Codex MCP discovery/calls without starting a model turn."""
|
||||
import argparse
|
||||
import asyncio
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import time
|
||||
import traceback
|
||||
|
||||
|
||||
async def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument("--runtime", type=Path, required=True)
|
||||
parser.add_argument("--codex", type=Path, required=True)
|
||||
parser.add_argument("--node", type=Path, required=True)
|
||||
parser.add_argument("--input-profile", choices=["full", "compact"], default="full")
|
||||
parser.add_argument("--output-profile", choices=["full", "off"], default="full")
|
||||
args = parser.parse_args()
|
||||
args.output.mkdir(mode=0o700)
|
||||
audit_home = args.output / "account"
|
||||
codex_state = audit_home / ".codex"
|
||||
mcp_home = audit_home / "mcp"
|
||||
workspace = audit_home / "workspace"
|
||||
for directory in [codex_state, mcp_home, workspace]:
|
||||
directory.mkdir(parents=True)
|
||||
task_path = f"{args.node.parent}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
mcp_env = {"HOME": str(mcp_home), "PATH": task_path,
|
||||
"NODE_OPTIONS": "--max-old-space-size=2048", "UV_THREADPOOL_SIZE": "1",
|
||||
"REA_LOG_LEVEL": "silent", "REA_MCP_INPUT_SCHEMA_PROFILE": args.input_profile,
|
||||
"REA_MCP_OUTPUT_SCHEMA_PROFILE": args.output_profile}
|
||||
config = '[analytics]\nenabled = false\n[mcp_servers.rea_audit]\n'
|
||||
config += f'command = {json.dumps(str(args.node))}\n'
|
||||
config += f'args = {json.dumps([str(args.runtime / "scripts/rea.mjs"), "mcp"])}\n'
|
||||
config += 'startup_timeout_sec = 60\ntool_timeout_sec = 60\n'
|
||||
config += '[mcp_servers.rea_audit.env]\n'
|
||||
config += ''.join(f'{key} = {json.dumps(value)}\n' for key, value in mcp_env.items())
|
||||
(codex_state / "config.toml").write_text(config)
|
||||
(args.output / "declared-config.toml").write_text(config)
|
||||
environment = {"HOME": str(audit_home), "CODEX_HOME": str(codex_state), "PATH": task_path,
|
||||
"NODE_OPTIONS": "--max-old-space-size=2048", "UV_THREADPOOL_SIZE": "1"}
|
||||
report = {"question": "Discover the complete canonical tool catalog and report no-target session/cleanup state, selected provider, package version and catalog tool count twice.",
|
||||
"client": "Codex app-server 0.163.0", "model_turns_started": 0,
|
||||
"actual_model_usage": None, "input_profile": args.input_profile,
|
||||
"output_profile": args.output_profile, "calls": [], "observations": [],
|
||||
"unexpected_server_requests": [], "cleanup_failures": []}
|
||||
transcript = []
|
||||
pending = {}
|
||||
next_id = 0
|
||||
stderr = (args.output / "server.log").open("wb")
|
||||
process = await asyncio.create_subprocess_exec(str(args.codex), "app-server", "--stdio",
|
||||
stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE, stderr=stderr,
|
||||
cwd=workspace, env=environment, limit=16 * 1024 * 1024)
|
||||
|
||||
async def send(message):
|
||||
encoded = (json.dumps(message, separators=(",", ":")) + "\n").encode()
|
||||
transcript.append({"direction": "client", "bytes": len(encoded), "message": message})
|
||||
process.stdin.write(encoded)
|
||||
await process.stdin.drain()
|
||||
|
||||
async def read_messages():
|
||||
try:
|
||||
while line := await process.stdout.readline():
|
||||
value = json.loads(line)
|
||||
transcript.append({"direction": "server", "bytes": len(line), "message": value})
|
||||
if "id" in value and "method" not in value:
|
||||
future = pending.pop(value["id"], None)
|
||||
if future and not future.done():
|
||||
future.set_result(value)
|
||||
elif "id" in value:
|
||||
report["unexpected_server_requests"].append(value["method"])
|
||||
await send({"id": value["id"], "error": {"code": -32601, "message": "Unexpected request in read-only discovery probe"}})
|
||||
raise RuntimeError("app-server stdout closed")
|
||||
except Exception as error:
|
||||
for future in pending.values():
|
||||
if not future.done():
|
||||
future.set_exception(error)
|
||||
|
||||
reader = asyncio.create_task(read_messages())
|
||||
|
||||
async def request(method, params):
|
||||
nonlocal next_id
|
||||
assert method in ["initialize", "thread/start", "mcpServerStatus/list", "mcpServer/tool/call"]
|
||||
next_id += 1
|
||||
message = {"id": next_id, "method": method, "params": params}
|
||||
future = asyncio.get_running_loop().create_future()
|
||||
pending[next_id] = future
|
||||
start = time.monotonic()
|
||||
await send(message)
|
||||
reply = await asyncio.wait_for(future, 90)
|
||||
report["calls"].append({"method": method, "elapsed_ms": (time.monotonic() - start) * 1000,
|
||||
"error": reply.get("error")})
|
||||
if "error" in reply:
|
||||
raise RuntimeError(json.dumps(reply["error"])[:4000])
|
||||
return reply["result"]
|
||||
|
||||
try:
|
||||
await request("initialize", {"clientInfo": {"name": "rea_host_probe", "title": "REA host compatibility probe", "version": "1"},
|
||||
"capabilities": {"experimentalApi": True, "explicitGatewayOauth": True}})
|
||||
await send({"method": "initialized"})
|
||||
created = await request("thread/start", {"cwd": str(workspace), "ephemeral": True,
|
||||
"approvalPolicy": "never", "sandbox": "read-only"})
|
||||
thread_id = created["thread"]["id"]
|
||||
assert not created["thread"].get("turns", [])
|
||||
inventory = await request("mcpServerStatus/list", {"threadId": thread_id,
|
||||
"serverName": "rea_audit", "detail": "toolsAndAuthOnly", "limit": 10})
|
||||
assert inventory.get("nextCursor") is None
|
||||
assert len(inventory["data"]) == 1
|
||||
server = inventory["data"][0]
|
||||
assert server["name"] == "rea_audit" and server.get("toolsError") is None
|
||||
tools = list(server["tools"].values())
|
||||
names = [tool["name"] for tool in tools]
|
||||
assert len(names) == len(set(names)) == 209
|
||||
assert "binary_session" in names
|
||||
output_schemas = sum(tool.get("outputSchema") is not None for tool in tools)
|
||||
assert output_schemas == (209 if args.output_profile == "full" else 0)
|
||||
raw = json.dumps(inventory, separators=(",", ":"), ensure_ascii=False).encode()
|
||||
(args.output / "catalog.json").write_bytes(raw)
|
||||
report["catalog"] = {"tools": len(tools), "output_schemas": output_schemas,
|
||||
"bytes": len(raw), "sha256": hashlib.sha256(raw).hexdigest(),
|
||||
"server_info": server.get("serverInfo"), "runtime_status": server.get("runtimeStatus")}
|
||||
version = json.loads((args.runtime / "package.json").read_text())["version"]
|
||||
for phase in ["first", "warm"]:
|
||||
reply = await request("mcpServer/tool/call", {"threadId": thread_id,
|
||||
"server": "rea_audit", "tool": "binary_session", "arguments": {}})
|
||||
assert reply.get("isError") is not True
|
||||
structured = reply["structuredContent"]
|
||||
texts = [c["text"] for c in reply["content"] if c.get("type") == "text"]
|
||||
assert len(texts) == 1 and json.loads(texts[0]) == structured
|
||||
facts = structured["result"]
|
||||
assert facts["open"] is False and facts["cleanup_pending"] is False
|
||||
assert facts["analysis_provider_binding"] is None
|
||||
assert facts["server_identity"]["package"]["version"] == version
|
||||
assert facts["server_identity"]["catalog"]["counts"]["mcp_tools"] == 209
|
||||
report["observations"].append({"phase": phase, "open": facts["open"],
|
||||
"cleanup_pending": facts["cleanup_pending"], "selected_provider": facts["analysis_provider_binding"],
|
||||
"package_version": version, "catalog_tools": 209,
|
||||
"negotiated_protocol": facts["server_identity"]["negotiated_protocol_version"],
|
||||
"structured_bytes": len(json.dumps(structured, separators=(",", ":"), ensure_ascii=False).encode())})
|
||||
selected_facts = lambda observation: {key: value for key, value in observation.items()
|
||||
if key not in ["phase", "structured_bytes"]}
|
||||
assert selected_facts(report["observations"][0]) == selected_facts(report["observations"][1])
|
||||
assert not report["unexpected_server_requests"]
|
||||
report["status"] = "passed"
|
||||
except Exception:
|
||||
report["status"] = "failed"
|
||||
report["failure"] = traceback.format_exc()[-6000:]
|
||||
finally:
|
||||
process.stdin.close()
|
||||
try:
|
||||
await asyncio.wait_for(process.wait(), 15)
|
||||
except asyncio.TimeoutError:
|
||||
report["cleanup_failures"].append("app-server did not exit after stdin close; terminated owned process")
|
||||
process.terminate()
|
||||
try:
|
||||
await asyncio.wait_for(process.wait(), 5)
|
||||
except asyncio.TimeoutError:
|
||||
process.kill()
|
||||
await process.wait()
|
||||
await reader
|
||||
# app-server can acknowledge exit before its MCP child finishes EOF
|
||||
# cleanup. Wait on the allocated resource scope; never target a PID.
|
||||
try:
|
||||
groups = [line[3:] for line in Path("/proc/self/cgroup").read_text().splitlines()
|
||||
if line.startswith("0::")]
|
||||
assert len(groups) == 1
|
||||
group = Path("/sys/fs/cgroup") / groups[0].lstrip("/")
|
||||
assert re.fullmatch(r"rea-efficiency-[0-9a-f]{32}\.scope", group.name)
|
||||
observer = os.getppid()
|
||||
observer_args = Path(f"/proc/{observer}/cmdline").read_bytes().split(b"\0")
|
||||
assert any(Path(os.fsdecode(value)).name == "resource_payload.py" for value in observer_args if value)
|
||||
expected = {os.getpid(), observer}
|
||||
until = time.monotonic() + 10
|
||||
while True:
|
||||
actors = {int(value) for value in (group / "cgroup.procs").read_text().splitlines()}
|
||||
assert expected <= actors
|
||||
remaining = sorted(actors - expected)
|
||||
if not remaining or time.monotonic() >= until:
|
||||
break
|
||||
await asyncio.sleep(0.05)
|
||||
report["owned_scope_drain"] = {"remaining_processes": remaining}
|
||||
if remaining:
|
||||
report["cleanup_failures"].append("owned MCP child did not settle after app-server exit")
|
||||
except (OSError, ValueError, AssertionError) as error:
|
||||
report["owned_scope_drain"] = {"status": "unavailable", "reason": type(error).__name__}
|
||||
report["cleanup_failures"].append("allocated resource-scope cleanup could not be verified")
|
||||
stderr.close()
|
||||
report["appserver_exit_code"] = process.returncode
|
||||
if process.returncode != 0:
|
||||
report["cleanup_failures"].append(f"app-server exit code: {process.returncode}")
|
||||
(args.output / "transcript.json").write_text(json.dumps(transcript) + "\n")
|
||||
(args.output / "report.json").write_text(json.dumps(report, indent=2) + "\n")
|
||||
print(json.dumps({key: report.get(key) for key in ["status", "failure", "catalog", "observations", "cleanup_failures", "appserver_exit_code"]}))
|
||||
return 0 if report["status"] == "passed" and not report["cleanup_failures"] else 1
|
||||
|
||||
|
||||
raise SystemExit(asyncio.run(main()))
|
||||
@@ -0,0 +1,115 @@
|
||||
"""Count recorded native-inventory delivery, including errors and file recovery.
|
||||
|
||||
Requires tiktoken==0.12.0 in the experiment environment. These are serialization
|
||||
proxies, not model usage or a claim about a host's schema injection/rendering.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
import tiktoken
|
||||
|
||||
|
||||
def compact(value):
|
||||
return json.dumps(value, ensure_ascii=False, separators=(",", ":"), allow_nan=False)
|
||||
|
||||
|
||||
def inspect_case(path, encoding):
|
||||
report = json.loads((path / "report.json").read_text())
|
||||
transcript = json.loads((path / "transcript.json").read_text())
|
||||
catalog_text = (path / "catalog.json").read_text()
|
||||
catalog = json.loads(catalog_text)
|
||||
scope = next(
|
||||
json.loads(line)
|
||||
for line in reversed(path.with_suffix(".log").read_text().splitlines())
|
||||
if line.startswith('{"resource_scope":')
|
||||
)
|
||||
observation = scope.get("payload_resource_observation") or {}
|
||||
count = lambda value: len(encoding.encode(value, disallowed_special=()))
|
||||
input_projection = [
|
||||
{key: tool[key] for key in ["name", "description", "inputSchema"]}
|
||||
for tool in catalog["tools"]
|
||||
]
|
||||
rpc = [event for event in transcript if "reply" in event or "failure" in event]
|
||||
local = [event for event in transcript if event["name"] == "local-bundle-projection"]
|
||||
arguments = sum(count(compact({"name": event["name"], "arguments": event["input"]})) for event in rpc)
|
||||
text = sum(
|
||||
count(item["text"])
|
||||
for event in rpc
|
||||
for item in event.get("reply", {}).get("content", [])
|
||||
if item["type"] == "text"
|
||||
)
|
||||
full_replies = sum(count(compact(event["reply"])) for event in rpc if "reply" in event)
|
||||
local_output = sum(count(compact(event["projection"])) for event in local)
|
||||
schemas = count(compact(input_projection))
|
||||
completed = (
|
||||
report["status"] == "passed"
|
||||
and len(report["tasks"]) == 2
|
||||
and not report["cleanup_failures"]
|
||||
and not report["receipt_failures"]
|
||||
and scope["abort"] is None
|
||||
and scope.get("payload_resource_observation_verified") is True
|
||||
and observation.get("payload_exit_code") == 0
|
||||
and observation.get("payload_cleanup_verified") is True
|
||||
)
|
||||
return {
|
||||
"recording": path.name,
|
||||
"status": report["status"],
|
||||
"completed_with_verified_cleanup": completed,
|
||||
"mode": report["mode"],
|
||||
"catalog_tools": len(catalog["tools"]),
|
||||
"catalog_bytes": len(catalog_text.encode()),
|
||||
"rpc_calls": len(rpc),
|
||||
"rpc_errors_or_rejections": sum(event.get("reply", {}).get("isError") is True or "failure" in event for event in rpc),
|
||||
"rpc_argument_bytes": sum(len(compact({"name": event["name"], "arguments": event["input"]}).encode()) for event in rpc),
|
||||
"rpc_reply_bytes": sum(call["reply_bytes"] or 0 for call in report["calls"]),
|
||||
"local_recoveries": report.get("local_recoveries", []),
|
||||
"token_estimates": {
|
||||
"catalog_full_json": count(catalog_text),
|
||||
"catalog_input_facing_projection": schemas,
|
||||
"rpc_name_and_argument_json": arguments,
|
||||
"rpc_text_content": text,
|
||||
"rpc_complete_result_json": full_replies,
|
||||
"local_projection_output_json": local_output,
|
||||
"input_facing_catalog_plus_arguments_text_and_local_output": schemas + arguments + text + local_output,
|
||||
},
|
||||
"whole_scope_wall_seconds": scope["elapsed_seconds"],
|
||||
"payload_family_cpu_seconds": observation["cpu_stat"]["usage_usec"] / 1_000_000 if observation.get("cpu_stat", {}).get("usage_usec") is not None else None,
|
||||
"kernel_family_peak_bytes_including_file_cache": observation.get("memory_peak_bytes"),
|
||||
"payload_exit_observation_verified": scope.get("payload_resource_observation_verified"),
|
||||
"observed_resource_events": observation.get("resource_events"),
|
||||
"final_whole_scope_events": scope.get("resource_events_final"),
|
||||
"tasks": [{"phase": task["phase"], "elapsed_ms": task["elapsed_ms"]} for task in report["tasks"]],
|
||||
"facts_sha256": report.get("facts_sha256"),
|
||||
"sources_sha256": report.get("sources_sha256"),
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument("cases", type=Path, nargs="+")
|
||||
args = parser.parse_args()
|
||||
encoding = tiktoken.get_encoding("cl100k_base")
|
||||
result = {
|
||||
"python": sys.version,
|
||||
"tiktoken": tiktoken.__version__,
|
||||
"encoding": encoding.name,
|
||||
"actual_model_usage": None,
|
||||
"unmeasured": [
|
||||
"model-host schema selection, wrapping and rendering",
|
||||
"system prompts, local filesystem tool schema/program generation and follow-up reasoning",
|
||||
"model input/cached/uncached/output usage",
|
||||
"initialization and JSON-RPC wire framing",
|
||||
"isolated correctness-oracle CPU and memory overhead",
|
||||
],
|
||||
"samples": [inspect_case(path, encoding) for path in args.cases],
|
||||
}
|
||||
args.output.write_text(json.dumps(result, indent=2) + "\n")
|
||||
print(json.dumps({"samples": len(result["samples"]), "encoding": encoding.name, "actual_model_usage": None}))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,231 @@
|
||||
/** Verify real pre-summary source Evidence through retained MCP and provider-free CLI views. */
|
||||
import assert from "node:assert/strict";
|
||||
import { execFile } from "node:child_process";
|
||||
import { createHash } from "node:crypto";
|
||||
import { createWriteStream } from "node:fs";
|
||||
import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { finished } from "node:stream/promises";
|
||||
import { parseArgs, promisify } from "node:util";
|
||||
import { Client } from "@modelcontextprotocol/client";
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
||||
import { STDIO_DEFAULT_MAX_BUFFER_SIZE } from "@modelcontextprotocol/server";
|
||||
import { createRetainedQueryCaller } from "./retained-query-calls.mjs";
|
||||
import { finalizeRetainedQueryReceipt } from "./retained-query-finalization.mjs";
|
||||
|
||||
const { values } = parseArgs({
|
||||
options: Object.fromEntries(
|
||||
["runtime", "source", "output"].map((name) => [name, { type: "string" }]),
|
||||
),
|
||||
});
|
||||
for (const name of ["runtime", "source", "output"])
|
||||
assert.ok(values[name] && values[name] === resolve(values[name]), name);
|
||||
const hash = (value) => createHash("sha256").update(value).digest("hex");
|
||||
const originalBytes = await readFile(join(values.source, "evidence.json"));
|
||||
const original = JSON.parse(originalBytes);
|
||||
const sources = JSON.parse(
|
||||
await readFile(join(values.source, "sources.json"), "utf8"),
|
||||
).filter((source) => source.phase === "first");
|
||||
assert.equal(sources.length, 5);
|
||||
await mkdir(values.output, { mode: 0o700 });
|
||||
const home = await mkdtemp(join(tmpdir(), "rea-portable-native-inventory-"));
|
||||
const environment = {
|
||||
HOME: home,
|
||||
PATH: process.env.PATH,
|
||||
NODE_OPTIONS: "--max-old-space-size=2048",
|
||||
REA_LOG_LEVEL: "silent",
|
||||
};
|
||||
const client = new Client({ name: "portable-native-inventory", version: "1" });
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [join(values.runtime, "scripts/rea.mjs"), "mcp"],
|
||||
env: environment,
|
||||
stderr: "pipe",
|
||||
});
|
||||
const serverLog = createWriteStream(join(values.output, "server.log"));
|
||||
const serverLogFinished = finished(serverLog);
|
||||
serverLogFinished.catch(() => {});
|
||||
transport.stderr?.pipe(serverLog);
|
||||
const report = {
|
||||
source_bundle_sha256: hash(originalBytes),
|
||||
calls: [],
|
||||
cli: [],
|
||||
rows: {},
|
||||
};
|
||||
const transcript = [];
|
||||
const call = createRetainedQueryCaller(client, (receipt, event) => {
|
||||
report.calls.push(receipt);
|
||||
transcript.push(event);
|
||||
});
|
||||
const result = async (name, input, phase = name) => {
|
||||
const reply = await call(name, input, phase);
|
||||
assert.notEqual(reply.isError, true, JSON.stringify(reply));
|
||||
assert.ok(reply.structuredContent);
|
||||
return reply.structuredContent;
|
||||
};
|
||||
const started = performance.now();
|
||||
try {
|
||||
await client.connect(transport);
|
||||
const catalog = await client.listTools();
|
||||
assert.equal(catalog.nextCursor, undefined);
|
||||
report.catalog = {
|
||||
tool_count: catalog.tools.length,
|
||||
bytes: Buffer.byteLength(JSON.stringify(catalog)),
|
||||
sha256: hash(JSON.stringify(catalog)),
|
||||
};
|
||||
const definitions = new Map(catalog.tools.map((tool) => [tool.name, tool]));
|
||||
const originalCall = client.callTool.bind(client);
|
||||
client.callTool = (input, options) =>
|
||||
originalCall(input, {
|
||||
...options,
|
||||
toolDefinition: definitions.get(input.name),
|
||||
});
|
||||
await result("import_evidence_bundle", {
|
||||
path: join(values.source, "evidence.json"),
|
||||
});
|
||||
for (const { operation, record } of sources) {
|
||||
assert.deepEqual(
|
||||
original.records.find(
|
||||
(source) => source.evidence_id === record.evidence_id,
|
||||
),
|
||||
record,
|
||||
);
|
||||
const sourcePath = join(values.output, `${operation}.json`);
|
||||
const sourceBytes = Buffer.from(JSON.stringify(record));
|
||||
await writeFile(sourcePath, sourceBytes);
|
||||
const source = {
|
||||
kind: "retained-evidence",
|
||||
evidence_id: record.evidence_id,
|
||||
};
|
||||
const pageView = {
|
||||
kind: "native-inventory",
|
||||
offset: 0,
|
||||
limit: 4,
|
||||
fields: ["value"],
|
||||
};
|
||||
const expected = await result(
|
||||
"inspect_analysis_view",
|
||||
{ source, view: pageView },
|
||||
`${operation}:selected`,
|
||||
);
|
||||
for (const view of [{ kind: "summary" }, pageView]) {
|
||||
const inputPath = join(
|
||||
values.output,
|
||||
`${operation}-${view.kind}.input.json`,
|
||||
);
|
||||
await writeFile(
|
||||
inputPath,
|
||||
JSON.stringify({
|
||||
source: { kind: "evidence-file", path: sourcePath },
|
||||
view,
|
||||
}),
|
||||
);
|
||||
const cliStarted = performance.now();
|
||||
const { stdout, stderr } = await promisify(execFile)(
|
||||
process.execPath,
|
||||
[
|
||||
join(values.runtime, "scripts/rea.mjs"),
|
||||
"inspect-analysis-view",
|
||||
inputPath,
|
||||
"--json",
|
||||
],
|
||||
{
|
||||
env: environment,
|
||||
encoding: "utf8",
|
||||
timeout: 60000,
|
||||
maxBuffer: STDIO_DEFAULT_MAX_BUFFER_SIZE,
|
||||
},
|
||||
);
|
||||
const cliElapsed = performance.now() - cliStarted;
|
||||
const actual = JSON.parse(stdout);
|
||||
const mcp =
|
||||
view.kind === "summary"
|
||||
? await result(
|
||||
"inspect_analysis_view",
|
||||
{ source, view },
|
||||
`${operation}:summary`,
|
||||
)
|
||||
: expected;
|
||||
assert.deepEqual(actual, mcp);
|
||||
report.cli.push({
|
||||
operation,
|
||||
view: view.kind,
|
||||
elapsed_ms: cliElapsed,
|
||||
stdout_bytes: Buffer.byteLength(stdout),
|
||||
stderr_bytes: Buffer.byteLength(stderr),
|
||||
evidence_id: actual.evidence_id,
|
||||
});
|
||||
}
|
||||
const all = [];
|
||||
let offset = 0;
|
||||
do {
|
||||
const evidence = await result(
|
||||
"inspect_analysis_view",
|
||||
{ source, view: { kind: "native-inventory", offset, limit: 128 } },
|
||||
`${operation}:page:${offset}`,
|
||||
);
|
||||
const page = evidence.normalized_result;
|
||||
assert.equal(page.kind, "native-inventory");
|
||||
const expectedRows = Math.min(
|
||||
128,
|
||||
record.normalized_result.length - offset,
|
||||
);
|
||||
assert.equal(page.rows.length, expectedRows);
|
||||
assert.equal(page.coverage.total, record.normalized_result.length);
|
||||
for (const [index, row] of page.rows.entries())
|
||||
assert.equal(row.source_index, offset + index);
|
||||
all.push(...page.rows.map((row) => row.record));
|
||||
assert.equal(
|
||||
page.coverage.next_offset,
|
||||
offset + expectedRows >= record.normalized_result.length
|
||||
? null
|
||||
: offset + expectedRows,
|
||||
);
|
||||
offset = page.coverage.next_offset;
|
||||
} while (offset !== null);
|
||||
assert.deepEqual(all, record.normalized_result);
|
||||
report.rows[operation] = all.length;
|
||||
assert.equal(hash(await readFile(sourcePath)), hash(sourceBytes));
|
||||
}
|
||||
const exportPath = join(values.output, "exported.json");
|
||||
await result("export_evidence_bundle", { path: exportPath });
|
||||
const exported = JSON.parse(await readFile(exportPath, "utf8"));
|
||||
for (const { record } of sources)
|
||||
assert.deepEqual(
|
||||
exported.records.find(
|
||||
(source) => source.evidence_id === record.evidence_id,
|
||||
),
|
||||
record,
|
||||
);
|
||||
assert.equal(
|
||||
hash(await readFile(join(values.source, "evidence.json"))),
|
||||
report.source_bundle_sha256,
|
||||
);
|
||||
report.status = "passed";
|
||||
} catch (cause) {
|
||||
report.status = "failed";
|
||||
report.failure = String(cause?.stack ?? cause);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
report.elapsed_before_cleanup_ms = performance.now() - started;
|
||||
try {
|
||||
await writeFile(
|
||||
join(values.output, "transcript.json"),
|
||||
JSON.stringify(transcript),
|
||||
);
|
||||
} catch (cause) {
|
||||
report.status = "failed";
|
||||
report.transcript_failure = String(cause);
|
||||
}
|
||||
const finalized = await finalizeRetainedQueryReceipt({
|
||||
report,
|
||||
client,
|
||||
transport,
|
||||
account: home,
|
||||
serverLog,
|
||||
serverLogFinished,
|
||||
output: values.output,
|
||||
});
|
||||
if (report.status !== "passed" || !finalized) process.exitCode = 1;
|
||||
}
|
||||
@@ -0,0 +1,647 @@
|
||||
{
|
||||
"question": "Counts and first four address/value pairs for list_procedures, list_names, list_strings and literal main search_procedures/search_strings; first and warm repetition.",
|
||||
"baseline_revision": "58d6043303e722115f1da18b9505c24abeeab423",
|
||||
"runtime_pins": {
|
||||
"baseline_manifest_sha256": "dc5e30fa1c75f999a8597608c5f0a3d8e1e16ccc14eb39d3884b5362076985dd",
|
||||
"candidate_manifest_sha256": "14b4af9117051b722eb528dfdf79668fa188f874ce69853bd1ed609eb704a88d",
|
||||
"candidate_tracked_patch_sha256": "e988c4f5142363dc2cbf6ab98e11d1ea1e4acccc66ce941d15eb51a945b4fb3a",
|
||||
"candidate_untracked_sources": {
|
||||
"experiments/analysis-efficiency/native-inventory-portable.mjs": "262cd4a076ba13936ff744c844892ec108b015f3adc908703120fddd251fe149",
|
||||
"experiments/analysis-efficiency/native-inventory-views.mjs": "c5de78d1998e254a269e3d1694c96d7034162aac41965a7b842235ae09ac9db8",
|
||||
"src/domain/analysisView/nativeInventoryView.test.ts": "b0b3a3d14a0d8be2a42bb9ae9e0e5a32eef94bf09ffcce01f89554300eeb97f2",
|
||||
"src/domain/analysisView/nativeInventoryView.ts": "e1e06ead227c30c7eaa8231b48c934b5f0be57876847b022e75f6a89eadaddbd",
|
||||
"src/domain/native/nativeInventory.ts": "431e6041cd2dec688ca541dbe343cea2228c9d616af1332338b9a36c73a32320",
|
||||
"tests/boundary/mcp/nativeInventoryViewsMcp.test.ts": "f6991ba8e6d97922c430512d5bfec16b392e22a9eacab21a5853ea93f6d3a73d"
|
||||
},
|
||||
"note": "Frozen candidate before measurement; later documentation, bounded harness diagnostics and rebases are not relabeled as measured source revisions."
|
||||
},
|
||||
"environment": {
|
||||
"platform": "Linux-6.1.0-53-cloud-amd64-x86_64-with-glibc2.36",
|
||||
"node": "24.18.0",
|
||||
"npm": "11.16.0",
|
||||
"mcp_sdk": "2.3.1",
|
||||
"ghidra": "12.1.4",
|
||||
"java": "Eclipse Adoptium 21.0.12.1+1-LTS",
|
||||
"java_heap": "768M",
|
||||
"node_heaps_mib": 2048,
|
||||
"active_java_processors": 1,
|
||||
"cpu_affinity": [15],
|
||||
"cpu_quota_percent": 100,
|
||||
"family_memory_hard_bytes": 4294967296,
|
||||
"family_memory_early_stop_bytes": 3221225472,
|
||||
"swap_bytes": 0,
|
||||
"task_limit": 192,
|
||||
"cache_state": "Fresh sessions and Java analysis, then warm repeated calls; OS caches unflushed.",
|
||||
"accounting": "cgroup v2 family counters at verified payload exit; kernel peak includes file cache and controller work; final whole-scope counters unavailable."
|
||||
},
|
||||
"artifacts": [
|
||||
{
|
||||
"path": "true",
|
||||
"bytes": 35664,
|
||||
"sha256": "c79bf44242829108e323378531f4ac839513ca1fba45efd6583643526e1e9fd2"
|
||||
},
|
||||
{
|
||||
"path": "jq-linux-amd64",
|
||||
"bytes": 2267912,
|
||||
"sha256": "b1c22172dd303f3be49e935aa56aa48a8b7a46e0bc838b4997d3bb451495870f"
|
||||
}
|
||||
],
|
||||
"driver_versions": [
|
||||
{
|
||||
"sha256": "c71957b673824a692efeaebb165a305799b6d7424a2dbb96d706a6f48e283aae",
|
||||
"used_by": ["baseline-true-1", "baseline-jq-1"],
|
||||
"path": "experiments/analysis-efficiency/native-inventory-views.mjs"
|
||||
},
|
||||
{
|
||||
"sha256": "c5de78d1998e254a269e3d1694c96d7034162aac41965a7b842235ae09ac9db8",
|
||||
"used_by": ["candidate-true-1", "baseline-jq-2", "candidate-jq-1"]
|
||||
},
|
||||
{
|
||||
"sha256": "0ce2e2a2c02e366f5005f9b1e4563f7cd1a5a378d8461de6750154dccf41995b",
|
||||
"runs": ["candidate-jq-2"],
|
||||
"change": "Bounded full-source comparison diagnostic, same strict oracle; candidate-jq-2 has no cross-session full-source control. Compare task facts separately and preserve Ghidra primary alias variance.",
|
||||
"candidate-jq-1": "Incomplete: scope_memory_early_stop while rendering full source mismatch; facts and source records retained; no completed report or cleanup proof."
|
||||
}
|
||||
],
|
||||
"token_estimates": {
|
||||
"python": "3.11.2 (main, Oct 7 2026, 12:35:07) [GCC 12.2.0]",
|
||||
"tiktoken": "0.12.0",
|
||||
"encoding": "cl100k_base",
|
||||
"actual_model_usage": null,
|
||||
"unmeasured": [
|
||||
"model-host schema selection, wrapping and rendering",
|
||||
"system prompts, local filesystem tool schema/program generation and follow-up reasoning",
|
||||
"model input/cached/uncached/output usage",
|
||||
"initialization and JSON-RPC wire framing",
|
||||
"isolated correctness-oracle CPU and memory overhead"
|
||||
],
|
||||
"samples": [
|
||||
{
|
||||
"recording": "baseline-true-1",
|
||||
"status": "passed",
|
||||
"completed_with_verified_cleanup": true,
|
||||
"mode": "complete",
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 3263492,
|
||||
"rpc_calls": 13,
|
||||
"rpc_errors_or_rejections": 0,
|
||||
"rpc_argument_bytes": 854,
|
||||
"rpc_reply_bytes": 921748,
|
||||
"local_recoveries": [],
|
||||
"token_estimates": {
|
||||
"catalog_full_json": 831584,
|
||||
"catalog_input_facing_projection": 185367,
|
||||
"rpc_name_and_argument_json": 214,
|
||||
"rpc_text_content": 120915,
|
||||
"rpc_complete_result_json": 247753,
|
||||
"local_projection_output_json": 0,
|
||||
"input_facing_catalog_plus_arguments_text_and_local_output": 306496
|
||||
},
|
||||
"whole_scope_wall_seconds": 53.662,
|
||||
"payload_family_cpu_seconds": 44.201557,
|
||||
"kernel_family_peak_bytes_including_file_cache": 1157513216,
|
||||
"payload_exit_observation_verified": true,
|
||||
"observed_resource_events": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_events": null,
|
||||
"tasks": [
|
||||
{
|
||||
"phase": "first",
|
||||
"elapsed_ms": 35533.921138000005
|
||||
},
|
||||
{
|
||||
"phase": "warm",
|
||||
"elapsed_ms": 236.46414299999742
|
||||
}
|
||||
],
|
||||
"facts_sha256": "fb6cfbd6e3b9cdff8f649ffc41de54d7de38a003d3ec4f20f7cdad227e38afca",
|
||||
"sources_sha256": "c87d8c5b12b5244e3777f61a396e2a19b333ce9a9c1cb246db4ab54fb075b13b"
|
||||
},
|
||||
{
|
||||
"recording": "candidate-true-1",
|
||||
"status": "passed",
|
||||
"completed_with_verified_cleanup": true,
|
||||
"mode": "summary",
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 3293844,
|
||||
"rpc_calls": 23,
|
||||
"rpc_errors_or_rejections": 0,
|
||||
"rpc_argument_bytes": 3469,
|
||||
"rpc_reply_bytes": 171130,
|
||||
"local_recoveries": [],
|
||||
"token_estimates": {
|
||||
"catalog_full_json": 838818,
|
||||
"catalog_input_facing_projection": 186009,
|
||||
"rpc_name_and_argument_json": 1086,
|
||||
"rpc_text_content": 20907,
|
||||
"rpc_complete_result_json": 43673,
|
||||
"local_projection_output_json": 0,
|
||||
"input_facing_catalog_plus_arguments_text_and_local_output": 208002
|
||||
},
|
||||
"whole_scope_wall_seconds": 36.777,
|
||||
"payload_family_cpu_seconds": 35.482574,
|
||||
"kernel_family_peak_bytes_including_file_cache": 1244180480,
|
||||
"payload_exit_observation_verified": true,
|
||||
"observed_resource_events": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_events": null,
|
||||
"tasks": [
|
||||
{
|
||||
"phase": "first",
|
||||
"elapsed_ms": 30791.001210000002
|
||||
},
|
||||
{
|
||||
"phase": "warm",
|
||||
"elapsed_ms": 223.86245000000054
|
||||
}
|
||||
],
|
||||
"facts_sha256": "fb6cfbd6e3b9cdff8f649ffc41de54d7de38a003d3ec4f20f7cdad227e38afca",
|
||||
"sources_sha256": "c87d8c5b12b5244e3777f61a396e2a19b333ce9a9c1cb246db4ab54fb075b13b"
|
||||
},
|
||||
{
|
||||
"recording": "baseline-jq-2",
|
||||
"status": "passed",
|
||||
"completed_with_verified_cleanup": true,
|
||||
"mode": "complete",
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 3263492,
|
||||
"rpc_calls": 15,
|
||||
"rpc_errors_or_rejections": 2,
|
||||
"rpc_argument_bytes": 1139,
|
||||
"rpc_reply_bytes": 3032150,
|
||||
"local_recoveries": [
|
||||
{
|
||||
"phase": "first:list_names",
|
||||
"status": "passed",
|
||||
"bundle_bytes_read": 7318483,
|
||||
"projection_bytes": 1186,
|
||||
"elapsed_ms": 164.94348000001628
|
||||
},
|
||||
{
|
||||
"phase": "warm:list_names",
|
||||
"status": "passed",
|
||||
"bundle_bytes_read": 7769768,
|
||||
"projection_bytes": 1186,
|
||||
"elapsed_ms": 116.13296299998183
|
||||
}
|
||||
],
|
||||
"token_estimates": {
|
||||
"catalog_full_json": 831584,
|
||||
"catalog_input_facing_projection": 185367,
|
||||
"rpc_name_and_argument_json": 307,
|
||||
"rpc_text_content": 403794,
|
||||
"rpc_complete_result_json": 820583,
|
||||
"local_projection_output_json": 564,
|
||||
"input_facing_catalog_plus_arguments_text_and_local_output": 590032
|
||||
},
|
||||
"whole_scope_wall_seconds": 355.586,
|
||||
"payload_family_cpu_seconds": 318.976126,
|
||||
"kernel_family_peak_bytes_including_file_cache": 1571012608,
|
||||
"payload_exit_observation_verified": true,
|
||||
"observed_resource_events": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_events": null,
|
||||
"tasks": [
|
||||
{
|
||||
"phase": "first",
|
||||
"elapsed_ms": 334659.460051
|
||||
},
|
||||
{
|
||||
"phase": "warm",
|
||||
"elapsed_ms": 2520.551972999994
|
||||
}
|
||||
],
|
||||
"facts_sha256": "34cf397309b0db4132f1fcfbcaf92daab75b8b823e4c328ac1592f46dff38094",
|
||||
"sources_sha256": "e8f4f98c0cc9a141f07db695fcc5b2b3f22ae3697bb09d0d8fd03d6a47762260"
|
||||
},
|
||||
{
|
||||
"recording": "candidate-jq-2",
|
||||
"status": "passed",
|
||||
"completed_with_verified_cleanup": true,
|
||||
"mode": "summary",
|
||||
"catalog_tools": 209,
|
||||
"catalog_bytes": 3293844,
|
||||
"rpc_calls": 21,
|
||||
"rpc_errors_or_rejections": 0,
|
||||
"rpc_argument_bytes": 3040,
|
||||
"rpc_reply_bytes": 172180,
|
||||
"local_recoveries": [],
|
||||
"token_estimates": {
|
||||
"catalog_full_json": 838818,
|
||||
"catalog_input_facing_projection": 186009,
|
||||
"rpc_name_and_argument_json": 921,
|
||||
"rpc_text_content": 21372,
|
||||
"rpc_complete_result_json": 44505,
|
||||
"local_projection_output_json": 0,
|
||||
"input_facing_catalog_plus_arguments_text_and_local_output": 208302
|
||||
},
|
||||
"whole_scope_wall_seconds": 333.104,
|
||||
"payload_family_cpu_seconds": 311.318996,
|
||||
"kernel_family_peak_bytes_including_file_cache": 1774460928,
|
||||
"payload_exit_observation_verified": true,
|
||||
"observed_resource_events": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_events": null,
|
||||
"tasks": [
|
||||
{
|
||||
"phase": "first",
|
||||
"elapsed_ms": 318797.998245
|
||||
},
|
||||
{
|
||||
"phase": "warm",
|
||||
"elapsed_ms": 6916.484328999999
|
||||
}
|
||||
],
|
||||
"facts_sha256": "34cf397309b0db4132f1fcfbcaf92daab75b8b823e4c328ac1592f46dff38094",
|
||||
"sources_sha256": "237f727384546a5323bf39d08d3fe3061b89cb32da22d062493b008e474ba6a2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"equivalence": {
|
||||
"true_all_original_source_evidence_fields_and_ids_equal": true,
|
||||
"jq_complete_selected_task_facts_equal": true,
|
||||
"jq_all_independent_live_source_evidence_equal": false,
|
||||
"earlier_independent_jq_source_differences": {
|
||||
"attempt": "candidate-jq-1",
|
||||
"completed": false,
|
||||
"reason": "scope_memory_early_stop; no report or payload-exit observation",
|
||||
"task_facts_exact_equal": true,
|
||||
"full_source_equal": false,
|
||||
"different_leaves": {
|
||||
"symbol.primary": 156,
|
||||
"[1, 'record', 'normalized_result', 45988, 'value']": 1,
|
||||
"[1, 'record', 'normalized_result', 46001, 'value']": 1,
|
||||
"evidence_id": 2,
|
||||
"[6, 'record', 'normalized_result', 45988, 'value']": 1,
|
||||
"[6, 'record', 'normalized_result', 46001, 'value']": 1
|
||||
},
|
||||
"examples": [
|
||||
{
|
||||
"path": [
|
||||
1,
|
||||
"record",
|
||||
"normalized_result",
|
||||
30995,
|
||||
"symbol",
|
||||
"primary"
|
||||
],
|
||||
"baseline": false,
|
||||
"candidate": true
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
1,
|
||||
"record",
|
||||
"normalized_result",
|
||||
31013,
|
||||
"symbol",
|
||||
"primary"
|
||||
],
|
||||
"baseline": true,
|
||||
"candidate": false
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
1,
|
||||
"record",
|
||||
"normalized_result",
|
||||
31120,
|
||||
"symbol",
|
||||
"primary"
|
||||
],
|
||||
"baseline": false,
|
||||
"candidate": true
|
||||
},
|
||||
{
|
||||
"path": [
|
||||
1,
|
||||
"record",
|
||||
"normalized_result",
|
||||
31121,
|
||||
"symbol",
|
||||
"primary"
|
||||
],
|
||||
"baseline": true,
|
||||
"candidate": false
|
||||
}
|
||||
],
|
||||
"interpretation": "Independent Ghidra analyses differ in symbol.primary for alias rows and two generated switch-label values (repeated in first/warm records). Root cause is unestablished; no byte-identical full-source or completed-performance claim."
|
||||
},
|
||||
"portable_checks": [
|
||||
{
|
||||
"recording": "portable-true-1",
|
||||
"status": "passed",
|
||||
"rows_reconstructed": {
|
||||
"list_procedures": 231,
|
||||
"list_names": 1027,
|
||||
"list_strings": 143,
|
||||
"search_procedures": 8,
|
||||
"search_strings": 2
|
||||
},
|
||||
"actual_cli_mcp_equal_evidence_comparisons": 10,
|
||||
"rpc_calls_including_import_and_export": 27,
|
||||
"source_bundle_sha256": "d9f574ff9deb84cef58a714e563590c70184c15ba01c174f47c027b50b1d6bfa",
|
||||
"whole_scope_wall_seconds": 25.194,
|
||||
"family_cpu_seconds": 25.015127,
|
||||
"kernel_family_peak_bytes_including_file_cache": 1025908736,
|
||||
"resource_events": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"payload_cleanup_verified": true,
|
||||
"final_whole_scope_events": null
|
||||
},
|
||||
{
|
||||
"recording": "portable-jq-1",
|
||||
"status": "passed",
|
||||
"rows_reconstructed": {
|
||||
"list_procedures": 2279,
|
||||
"list_names": 49684,
|
||||
"list_strings": 2915,
|
||||
"search_procedures": 0,
|
||||
"search_strings": 24
|
||||
},
|
||||
"actual_cli_mcp_equal_evidence_comparisons": 10,
|
||||
"rpc_calls_including_import_and_export": 444,
|
||||
"source_bundle_sha256": "49ba421c1d7c2bbee5d6d4ae7acec437b076f760097eeae1aae8633d45f8e51c",
|
||||
"whole_scope_wall_seconds": 77.537,
|
||||
"family_cpu_seconds": 63.38141,
|
||||
"kernel_family_peak_bytes_including_file_cache": 2034434048,
|
||||
"resource_events": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"payload_cleanup_verified": true,
|
||||
"final_whole_scope_events": null
|
||||
}
|
||||
],
|
||||
"limits": "This qualifies preservation/projection of recorded facts, not every Ghidra interpretation or complete program discovery."
|
||||
},
|
||||
"incomplete_attempts": [
|
||||
{
|
||||
"recording": "baseline-jq-1",
|
||||
"completed": false,
|
||||
"reason": "Default list_names exceeds the existing MCP response budget; first driver lacks export recovery.",
|
||||
"abort": null,
|
||||
"whole_scope_wall_seconds": 283.113,
|
||||
"peak_sampled_bytes": 1308225536,
|
||||
"last_observed_events": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"payload_resource_observation": {
|
||||
"phase": "payload-exited-observer-alive",
|
||||
"ownership_token": "rea-efficiency-2ad56ea678404ef49da951c8f1761822.scope",
|
||||
"payload_exit_code": 1,
|
||||
"observer_pid": 3613358,
|
||||
"remaining_processes": [3613358],
|
||||
"payload_cleanup_verified": true,
|
||||
"cpu_stat": {
|
||||
"usage_usec": 281189241,
|
||||
"user_usec": 274593631,
|
||||
"system_usec": 6595610,
|
||||
"nr_periods": 2829,
|
||||
"nr_throttled": 585,
|
||||
"throttled_usec": 218972,
|
||||
"nr_bursts": 0,
|
||||
"burst_usec": 0
|
||||
},
|
||||
"memory_peak_bytes": 1311993856,
|
||||
"control_group": "user.slice/user-1004.slice/user@1004.service/app.slice/rea-efficiency-2ad56ea678404ef49da951c8f1761822.scope",
|
||||
"resource_events": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"final_whole_scope_events": null
|
||||
},
|
||||
{
|
||||
"recording": "candidate-jq-1",
|
||||
"completed": false,
|
||||
"reason": "Independent full-source observations differ; guard stopped during comparison. Assertion diagnostic expansion is source-derived, not a recovered final stack trace.",
|
||||
"abort": "scope_memory_early_stop",
|
||||
"whole_scope_wall_seconds": 269.574,
|
||||
"peak_sampled_bytes": 3222269952,
|
||||
"last_observed_events": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 26,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"payload_resource_observation": null,
|
||||
"final_whole_scope_events": null
|
||||
}
|
||||
],
|
||||
"validation": {
|
||||
"focused_files": 24,
|
||||
"focused_tests": 305,
|
||||
"focused_command": "npm run test:focused -- src/domain/analysisView/EnvironmentManifestAccumulator.test.ts src/domain/analysisView/WebNetworkApiSurfaceAccumulator.test.ts src/domain/analysisView/analysisView.test.ts src/domain/analysisView/artifactInventoryView.test.ts src/domain/analysisView/environmentManifestRules.test.ts src/domain/analysisView/environmentManifestView.test.ts src/domain/analysisView/environmentStateView.test.ts src/domain/analysisView/environmentStateWriteCutoffs.test.ts src/domain/analysisView/il2cppMetadataView.test.ts src/domain/analysisView/interfaceBuilderView.test.ts src/domain/analysisView/machoView.test.ts src/domain/analysisView/nativeInventoryView.test.ts src/domain/analysisView/pcapCaptureView.test.ts src/domain/analysisView/scenarioCaptureRecords.test.ts src/domain/analysisView/webNetworkApiSurfaceView.test.ts src/domain/analysisView/webNetworkCaptureView.test.ts tests/boundary/mcp/nativeInventoryViewsMcp.test.ts tests/boundary/mcp/analysisViewMcp.test.ts tests/boundary/mcp/officialArguments.test.ts tests/boundary/mcp/officialTools.test.ts tests/boundary/mcp/largeToolResult.test.ts tests/boundary/mcp/ghidraEvidenceMcp.test.ts tests/boundary/mcp/nativeDispatchAvailability.test.ts tests/boundary/mcp/promptCompletion.test.ts",
|
||||
"static_command": "npm run check",
|
||||
"static_result": "passed, six tasks plus module/architecture guards",
|
||||
"docs_result": "npm run docs:check passed",
|
||||
"intermediate_failures": "Type errors and SDK fixture/error-envelope harness errors were corrected before passing checks. Two host-memory guard stops were retained; neither is a successful static check.",
|
||||
"provider_scope": "Real Linux x86-64 Ghidra true/jq metadata; other engines/platforms and complete application reconstruction unqualified.",
|
||||
"post_rebase": {
|
||||
"base_revision": "0134732fc",
|
||||
"implementation_revision": "8d1ddce86",
|
||||
"locked_dependencies": "npm ci passed for updated package-lock.json; frozen measurement runtimes retain their original physical dependency tree",
|
||||
"focused_files": 28,
|
||||
"focused_tests": 325,
|
||||
"focused_command": "npm run test:focused -- src/domain/analysisView/EnvironmentManifestAccumulator.test.ts src/domain/analysisView/WebNetworkApiSurfaceAccumulator.test.ts src/domain/analysisView/analysisView.test.ts src/domain/analysisView/artifactInventoryView.test.ts src/domain/analysisView/environmentManifestRules.test.ts src/domain/analysisView/environmentManifestView.test.ts src/domain/analysisView/environmentStateView.test.ts src/domain/analysisView/environmentStateWriteCutoffs.test.ts src/domain/analysisView/il2cppMetadataView.test.ts src/domain/analysisView/interfaceBuilderView.test.ts src/domain/analysisView/machoView.test.ts src/domain/analysisView/nativeInventoryView.test.ts src/domain/analysisView/pcapCaptureView.test.ts src/domain/analysisView/scenarioCaptureRecords.test.ts src/domain/analysisView/webNetworkApiSurfaceView.test.ts src/domain/analysisView/webNetworkCaptureView.test.ts tests/boundary/mcp/nativeInventoryViewsMcp.test.ts tests/boundary/mcp/analysisViewMcp.test.ts tests/boundary/mcp/officialArguments.test.ts tests/boundary/mcp/officialTools.test.ts tests/boundary/mcp/largeToolResult.test.ts tests/boundary/mcp/ghidraEvidenceMcp.test.ts tests/boundary/mcp/nativeDispatchAvailability.test.ts tests/boundary/mcp/promptCompletion.test.ts src/domain/analysisView/protobufCaptureView.test.ts src/browser/history/protobuf/ProtobufCapture.test.ts src/browser/history/protobuf/ProtobufWire.test.ts tests/acceptance/tools/protobufCapture.test.ts",
|
||||
"static_check": "npm run check passed after final field-selection normalization",
|
||||
"docs_check": "npm run docs:check passed, including five README consistency tests",
|
||||
"final_change": "Normalize duplicate field selection once, preserving original view request; focused two-file/nine-test rerun passed",
|
||||
"final_projector_sha256": "8795c18fe0a7c97b8a841aa3cc1fc6efe08fa1bf9711c21b0183b2cdd1f1b180",
|
||||
"verification_runtime_manifest_sha256": "f6a30e4a56fa1f95926e2259fbefe837ed375fb1c608e2298fb88440e1898f2f",
|
||||
"portable_real_source_checks": [
|
||||
{
|
||||
"recording": "portable-true-final-1",
|
||||
"status": "passed",
|
||||
"rows_reconstructed": {
|
||||
"list_procedures": 231,
|
||||
"list_names": 1027,
|
||||
"list_strings": 143,
|
||||
"search_procedures": 8,
|
||||
"search_strings": 2
|
||||
},
|
||||
"cli_mcp_equal_comparisons": 10,
|
||||
"rpc_calls": 27,
|
||||
"catalog": {
|
||||
"tool_count": 209,
|
||||
"bytes": 3317414,
|
||||
"sha256": "4c920219321a08102a29673a1cdd48f74d9990e766a313313903525a9f9efa87"
|
||||
},
|
||||
"whole_scope_wall_seconds": 23.489,
|
||||
"family_cpu_seconds": 23.283072,
|
||||
"kernel_family_peak_bytes": 1057517568,
|
||||
"payload_cleanup_verified": true,
|
||||
"resource_events": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"recording": "portable-jq-final-1",
|
||||
"status": "passed",
|
||||
"rows_reconstructed": {
|
||||
"list_procedures": 2279,
|
||||
"list_names": 49684,
|
||||
"list_strings": 2915,
|
||||
"search_procedures": 0,
|
||||
"search_strings": 24
|
||||
},
|
||||
"cli_mcp_equal_comparisons": 10,
|
||||
"rpc_calls": 444,
|
||||
"catalog": {
|
||||
"tool_count": 209,
|
||||
"bytes": 3317414,
|
||||
"sha256": "4c920219321a08102a29673a1cdd48f74d9990e766a313313903525a9f9efa87"
|
||||
},
|
||||
"whole_scope_wall_seconds": 34.99,
|
||||
"family_cpu_seconds": 34.589707,
|
||||
"kernel_family_peak_bytes": 2153742336,
|
||||
"payload_cleanup_verified": true,
|
||||
"resource_events": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"performance_limit": "Fixed runtime timing/token pairs remain pinned to the pre-rebase 58d-based runtime. Final source compatibility and full-row preservation were rerun; final main performance pairs and actual model usage were not rerun.",
|
||||
"guard_stopped_static_retry": {
|
||||
"recording": "full-check-rebase-1",
|
||||
"abort": "host_available_memory_early_stop",
|
||||
"completed": false,
|
||||
"own_oom_events": 0
|
||||
},
|
||||
"final_static_and_docs_family": {
|
||||
"wall_seconds": 93.069,
|
||||
"cpu_seconds": 92.615364,
|
||||
"kernel_peak_bytes": 5417869312,
|
||||
"payload_cleanup_verified": true
|
||||
}
|
||||
},
|
||||
"protocol_rebase": {
|
||||
"base_revision": "4eaf3a117",
|
||||
"implementation_revision": "7fbe79ef8",
|
||||
"change": "Preserve latest main oversized-error protocol framing and both documentation insertions; native projector unchanged.",
|
||||
"focused_command": "npm run test:focused -- src/server/EvidenceMcpServer.test.ts tests/boundary/mcp/nativeInventoryViewsMcp.test.ts tests/boundary/mcp/largeToolResult.test.ts",
|
||||
"focused_files": 3,
|
||||
"focused_tests": 11,
|
||||
"result": "passed",
|
||||
"real_old_source_cli_mcp_verifier": "true all rows and ten actual CLI/MCP comparisons passed again; no provider launched",
|
||||
"catalog": {
|
||||
"tool_count": 209,
|
||||
"bytes": 3317414,
|
||||
"sha256": "4c920219321a08102a29673a1cdd48f74d9990e766a313313903525a9f9efa87"
|
||||
},
|
||||
"docs_check": "npm run docs:check passed again",
|
||||
"static_check_scope": "Earlier full static checks remain pinned above; final pre-push/CI checks track the latest branch head."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
# Native inventory summaries and retained rows
|
||||
|
||||
Continues [#4249](https://github.com/morluto/rea/issues/4249). The analyst
|
||||
question is to report observed row counts and the first four address/value
|
||||
pairs from procedures, names, strings, and literal `main` procedure/string
|
||||
searches. Complete delivery reads every row even when only those facts are
|
||||
needed. A real jq name inventory exceeds the existing 10 MiB MCP result budget.
|
||||
|
||||
## Change and preserved semantics
|
||||
|
||||
The five existing tools accept opt-in `detail: "summary"`. Their original
|
||||
complete Evidence is retained before a summary is returned. Delivery detail
|
||||
does not enter provider parameters or the original Evidence identity. Default
|
||||
complete requests keep their existing response. Oversized complete inventory
|
||||
replies gain an actionable retained-page continuation after recording succeeds.
|
||||
|
||||
`inspect_analysis_view` projects `native-inventory` rows with offsets, limits
|
||||
and selected fields. Parent Evidence ID and original `source_index` identify
|
||||
duplicate-address alternatives; original order is preserved. Address is always
|
||||
retained, and selecting a string's value also retains its string/decoding
|
||||
qualifications. Counts describe retained provider observations. Exhausting a
|
||||
page does not establish complete code/string discovery. Unknown source artifact
|
||||
identity stays unknown rather than acquiring an invented path.
|
||||
|
||||
CLI and MCP use the shared application/domain projector. Its source-format
|
||||
validation is reused only after successful representation-preserving validation
|
||||
of an authenticated immutable JSON snapshot. Two weak sets distinguish names
|
||||
and the stricter strings format. They retain no second parsed inventory or live
|
||||
provider state; entries disappear with their source arrays. Mutable, foreign,
|
||||
malformed or transformed values do not acquire this proof. A parent change
|
||||
rebinds provenance rather than reusing an earlier view envelope.
|
||||
|
||||
This reduces delivery after the provider has produced its full inventory. It
|
||||
does not reduce Ghidra discovery, eliminate the retained full inventory, or
|
||||
establish a universal CPU/memory improvement. The catalog becomes larger and
|
||||
summary-plus-page requires more RPCs than a small complete reply.
|
||||
|
||||
## Real inputs, controls and oracle
|
||||
|
||||
Read-only Linux x86-64 targets, without target execution:
|
||||
|
||||
| Target | Bytes | SHA-256 | Provenance |
|
||||
| ------ | --------: | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
|
||||
| `true` | 35,664 | `c79bf44242829108e323378531f4ac839513ca1fba45efd6583643526e1e9fd2` | Installed Debian coreutils 9.1-1 |
|
||||
| jq | 2,267,912 | `b1c22172dd303f3be49e935aa56aa48a8b7a46e0bc838b4997d3bb451495870f` | [Official jq 1.8.2 asset](https://github.com/jqlang/jq/releases/tag/jq-1.8.2), release API size/digest matched |
|
||||
|
||||
Baseline runtime is main `58d6043303e722115f1da18b9505c24abeeab423`.
|
||||
The candidate runtime is frozen from that base plus this implementation before
|
||||
measurement. Runtime file manifests, the exact patch, driver revisions, original
|
||||
Evidence, transcripts, errors and resource observations are retained outside
|
||||
Git. The curated [receipt](native-inventory-views.json) pins the recordings.
|
||||
Later rebases do not relabel these timings as measurements of a different base.
|
||||
|
||||
Node 24.18.0/npm 11.16.0, MCP SDK 2.3.1, Ghidra 12.1.4 and Eclipse Adoptium
|
||||
21.0.12.1+1-LTS. Each pair uses a fresh MCP server and Ghidra analysis, followed
|
||||
by a warm repetition in that session. OS caches are unflushed. Java heap is
|
||||
768 MiB and one active processor; Node heaps are 2 GiB. The family guard uses
|
||||
one logical CPU, 4 GiB hard/3 GiB early-stop memory, no swap, and 192 tasks.
|
||||
Heavy jobs are serial. Kernel peaks include file cache, controller verification
|
||||
and receipt work, rather than isolated provider heap. Payload-exit observations
|
||||
verify owned process cleanup; final whole-scope counters remain unavailable.
|
||||
|
||||
The live named SDK contracts validate replies. Both modes discover the full
|
||||
catalog, open the same target, complete the question twice, export original
|
||||
Evidence and close. Summary mode requests four rows only for nonempty results.
|
||||
Complete mode includes acknowledged export recovery and local JSON projection
|
||||
when `list_names` exceeds transport capacity. Its errors, export calls, bytes
|
||||
read and local projection output remain in costs. Model generation of that
|
||||
local program and filesystem-tool schemas are unmeasured.
|
||||
|
||||
The small pair preserves every field of every original source Evidence,
|
||||
including IDs, and both phases' complete selected fact vectors. The large
|
||||
completed pair compares the requested facts exactly; it must not claim identical
|
||||
independent Ghidra analyses. An earlier candidate attempt found alias-primary
|
||||
and two generated switch-label differences outside the requested selection.
|
||||
Their underlying cause is unestablished. To qualify projection independently
|
||||
of that variation, the portable verifier imports the actual baseline Evidence,
|
||||
reads every canonical row through MCP, and compares it with the recorded source.
|
||||
It also compares actual CLI file-source views with retained MCP views, without
|
||||
Ghidra/JDK configuration, and reexports original source records unchanged.
|
||||
|
||||
## Measurements and limits
|
||||
|
||||
Each recording contains discovery, both first/warm questions, original Evidence
|
||||
export and cleanup. The token proxy below adds input-facing catalog JSON,
|
||||
name/argument JSON, text replies and local recovery output; it does not count
|
||||
both text and structured copies as if every host supplied both to a model.
|
||||
|
||||
| Recording / metric | Before | After |
|
||||
| ---------------------------------------- | ---------: | ---------: |
|
||||
| `true`: RPC calls | 13 | 23 |
|
||||
| `true`: RPC result bytes | 921,748 | 171,130 |
|
||||
| `true`: combined token proxy | 306,496 | 208,002 |
|
||||
| `true`: first question | 35.534 s | 30.791 s |
|
||||
| `true`: warm question | 0.236 s | 0.224 s |
|
||||
| `true`: whole scope wall | 53.662 s | 36.777 s |
|
||||
| `true`: family CPU | 44.202 s | 35.483 s |
|
||||
| `true`: kernel family peak | 1103.9 MiB | 1186.5 MiB |
|
||||
| jq: RPC calls, including export recovery | 15 | 21 |
|
||||
| jq: RPC result bytes | 3,032,150 | 172,180 |
|
||||
| jq: combined token proxy | 590,032 | 208,302 |
|
||||
| jq: first question | 334.659 s | 318.798 s |
|
||||
| jq: warm question | 2.521 s | 6.916 s |
|
||||
| jq: whole scope wall | 355.586 s | 333.104 s |
|
||||
| jq: family CPU | 318.976 s | 311.319 s |
|
||||
| jq: kernel family peak | 1498.2 MiB | 1692.3 MiB |
|
||||
|
||||
The catalog remains complete at 209 tools: 3,263,492 → 3,293,844 bytes.
|
||||
Its input-facing projection grows by 642 proxy tokens, from 185,367 to 186,009.
|
||||
Including that increase and every call, the combined serialization proxy falls
|
||||
32.1% for `true` and 64.7% for jq. Actual model usage is unmeasured.
|
||||
|
||||
Preserve the negative results: the large warm question is slower and both
|
||||
family memory peaks increase. The first native view also pays source-format
|
||||
validation and SDK schema setup; these phases are not isolated by this driver.
|
||||
Fresh analysis dominates total wall time and varies between sessions. These
|
||||
observations do not establish a latency, CPU or memory improvement. The owning
|
||||
benefit is selected delivery with complete source access and actionable
|
||||
recovery, rather than eliminating full provider inventory production.
|
||||
|
||||
Individual completed samples, tokenizer estimates and portable checks are in
|
||||
the receipt. A `cl100k_base` estimate over recorded serialization is not actual
|
||||
model usage. Counts include discovery, call arguments, text replies, errors,
|
||||
continuations and local recovery output; SDK result JSON is also reported
|
||||
separately. Actual client schema selection/rendering, wire framing, model
|
||||
reasoning and isolated correctness-oracle overhead are unmeasured. Single pairs
|
||||
are observations rather than latency distributions or evidence of improved
|
||||
model investigation outcomes.
|
||||
|
||||
Incomplete attempts remain separate. The first baseline jq driver stopped at
|
||||
the real oversized reply because it lacked export recovery. The first candidate
|
||||
jq attempt produced its selected facts and source export, then hit the 3 GiB
|
||||
early-stop threshold while comparing differing full inventories; no final report
|
||||
or payload-exit cleanup proof was produced. Expanding the enormous assertion
|
||||
diagnostic is a source-derived explanation, not an observed final stack trace.
|
||||
The harness now emits a bounded mismatch with source digests while retaining
|
||||
strict equality and both full files. The subsequent recording explicitly does
|
||||
not request cross-session full-source equality.
|
||||
|
||||
Two static-check attempts were stopped by the host-memory guard; later checks
|
||||
passed after host memory recovered. No guard-stopped attempt is a successful
|
||||
performance sample. Engine-version/platform equivalence, other providers,
|
||||
complete application reconstruction and post-GC retained heap are unqualified.
|
||||
|
||||
Executed verification: 24 focused files/305 tests passed, including advertised
|
||||
SDK-converted JSON Schema validation, all five tools, nonretaining refusal,
|
||||
oversized recovery, same-address alternatives, decoding qualifiers, immutable
|
||||
validation ownership and unknown-artifact sources. Full `npm run check` passed,
|
||||
including module/architecture guards; `npm run docs:check` also passed. The portable runs passed with 27/444 MCP
|
||||
calls, ten actual CLI comparisons each, every row reconstructed, original
|
||||
records preserved and payload cleanup verified. Their scope times are
|
||||
25.194/77.537 s and kernel family peaks are 978.4/1940.2 MiB; these additional
|
||||
correctness runs are not part of the selected-question timing pairs.
|
||||
|
||||
After rebasing onto main `0134732fc` (including Protobuf/gRPC views), 28 focused
|
||||
files/325 tests passed. Final field-selection normalization removes repeated
|
||||
per-row scans of duplicate requested fields while retaining the exact request;
|
||||
the affected two files/nine tests passed again. Final `npm run check` and
|
||||
`npm run docs:check` passed. Both real old-source portable checks passed again
|
||||
against the final build, including every canonical row and all 20 CLI/MCP
|
||||
comparisons. The observed catalog is then 209 tools/3,317,414 bytes. Those final
|
||||
source and dependency pins are separate from the earlier frozen timing/token
|
||||
pairs: final-main performance pairs and actual model usage were not rerun.
|
||||
|
||||
## Reproduce
|
||||
|
||||
Build separate baseline/candidate runtimes, then run each mode with the same
|
||||
absolute artifact, Ghidra and JDK paths:
|
||||
|
||||
```sh
|
||||
python3 experiments/analysis-efficiency/resource-run.py --observe-payload-exit -- \
|
||||
node experiments/analysis-efficiency/native-inventory-views.mjs \
|
||||
--runtime /absolute/baseline --target /absolute/application \
|
||||
--ghidra /absolute/ghidra --java /absolute/jdk \
|
||||
--mode complete --output /absolute/before
|
||||
|
||||
python3 experiments/analysis-efficiency/resource-run.py --observe-payload-exit -- \
|
||||
node experiments/analysis-efficiency/native-inventory-views.mjs \
|
||||
--runtime /absolute/candidate --target /absolute/application \
|
||||
--ghidra /absolute/ghidra --java /absolute/jdk \
|
||||
--mode summary --output /absolute/after
|
||||
|
||||
python3 experiments/analysis-efficiency/resource-run.py --observe-payload-exit -- \
|
||||
node experiments/analysis-efficiency/native-inventory-portable.mjs \
|
||||
--runtime /absolute/candidate --source /absolute/before \
|
||||
--output /absolute/portable
|
||||
```
|
||||
|
||||
`--control /absolute/before` additionally requires every source Evidence field
|
||||
to match across the two live analyses. Preserve failure if the engine produces
|
||||
different observations. `--verify-pages` performs extra live full-page checks;
|
||||
keep those correctness runs distinct from selected-question timing pairs.
|
||||
With private `tiktoken==0.12.0`, `native-inventory-costs.py --output FILE CASE...`
|
||||
counts completed recordings and their sibling `.log` resource observations.
|
||||
|
||||
The subsequent main `4eaf3a117` oversized-error protocol-framing fix was also
|
||||
merged into the branch. Its producing-server, native inventory and large-result
|
||||
checks passed (three files/11 tests), preserving both protocol revisions. The
|
||||
real old `true` source verifier and `npm run docs:check` passed again. The native
|
||||
projector and earlier performance pins are unchanged; latest-head pre-push/CI
|
||||
checks provide the final branch status.
|
||||
@@ -0,0 +1,374 @@
|
||||
/** Compare complete delivery with selected native inventory views on real Ghidra. */
|
||||
import assert from "node:assert/strict";
|
||||
import { createHash } from "node:crypto";
|
||||
import { createWriteStream } from "node:fs";
|
||||
import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { finished } from "node:stream/promises";
|
||||
import { isDeepStrictEqual, parseArgs } from "node:util";
|
||||
import { Client } from "@modelcontextprotocol/client";
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
||||
import { createRetainedQueryCaller } from "./retained-query-calls.mjs";
|
||||
import { finalizeRetainedQueryReceipt } from "./retained-query-finalization.mjs";
|
||||
|
||||
const { values } = parseArgs({
|
||||
options: {
|
||||
...Object.fromEntries(
|
||||
["runtime", "target", "output", "ghidra", "java", "mode", "control"].map(
|
||||
(name) => [name, { type: "string" }],
|
||||
),
|
||||
),
|
||||
"verify-pages": { type: "boolean", default: false },
|
||||
},
|
||||
});
|
||||
for (const name of ["runtime", "target", "output", "ghidra", "java"])
|
||||
assert.ok(values[name] && values[name] === resolve(values[name]), name);
|
||||
assert.ok(["complete", "summary"].includes(values.mode));
|
||||
assert.ok(!values["verify-pages"] || values.mode === "summary");
|
||||
const hash = (value) => createHash("sha256").update(value).digest("hex");
|
||||
const output = values.output;
|
||||
await mkdir(output, { mode: 0o700 });
|
||||
const targetBytes = await readFile(values.target);
|
||||
const home = await mkdtemp(join(tmpdir(), "rea-native-inventory-"));
|
||||
const client = new Client({ name: "native-inventory-views", version: "1" });
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [join(values.runtime, "scripts/rea.mjs"), "mcp"],
|
||||
env: {
|
||||
HOME: home,
|
||||
PATH: process.env.PATH,
|
||||
NODE_OPTIONS: "--max-old-space-size=2048",
|
||||
GHIDRA_INSTALL_DIR: values.ghidra,
|
||||
JAVA_HOME: values.java,
|
||||
GHIDRA_HEADLESS_MAXMEM: "768M",
|
||||
JAVA_TOOL_OPTIONS: "-XX:ActiveProcessorCount=1",
|
||||
REA_GHIDRA_STARTUP_TIMEOUT_MS: "600000",
|
||||
REA_LOG_LEVEL: "silent",
|
||||
},
|
||||
stderr: "pipe",
|
||||
});
|
||||
const serverLog = createWriteStream(join(output, "server.log"));
|
||||
const serverLogFinished = finished(serverLog);
|
||||
serverLogFinished.catch(() => {});
|
||||
transport.stderr?.pipe(serverLog);
|
||||
const report = {
|
||||
artifact: {
|
||||
path: values.target,
|
||||
bytes: targetBytes.length,
|
||||
sha256: hash(targetBytes),
|
||||
},
|
||||
mode: values.mode,
|
||||
verify_pages: values["verify-pages"],
|
||||
question:
|
||||
"Report retained row counts and the first four address/value pairs for procedures, names, strings, and literal main procedure/string matches.",
|
||||
calls: [],
|
||||
tasks: [],
|
||||
local_recoveries: [],
|
||||
};
|
||||
const transcript = [];
|
||||
const call = createRetainedQueryCaller(client, (receipt, event) => {
|
||||
report.calls.push(receipt);
|
||||
transcript.push(event);
|
||||
});
|
||||
const result = async (name, input, phase) => {
|
||||
const reply = await call(name, input, phase ?? name);
|
||||
assert.notEqual(reply.isError, true, JSON.stringify(reply));
|
||||
assert.ok(reply.structuredContent);
|
||||
return reply.structuredContent;
|
||||
};
|
||||
const operations = [
|
||||
"list_procedures",
|
||||
"list_names",
|
||||
"list_strings",
|
||||
"search_procedures",
|
||||
"search_strings",
|
||||
];
|
||||
const selectedRows = (rows) =>
|
||||
rows.slice(0, 4).map(({ address, value }) => ({ address, value }));
|
||||
const sourceReferences = [];
|
||||
const started = performance.now();
|
||||
let opened = false;
|
||||
try {
|
||||
await client.connect(transport);
|
||||
const discovery = performance.now();
|
||||
const catalog = await client.listTools();
|
||||
assert.equal(catalog.nextCursor, undefined);
|
||||
report.catalog = {
|
||||
elapsed_ms: performance.now() - discovery,
|
||||
tool_count: catalog.tools.length,
|
||||
bytes: Buffer.byteLength(JSON.stringify(catalog)),
|
||||
sha256: hash(JSON.stringify(catalog)),
|
||||
};
|
||||
await writeFile(join(output, "catalog.json"), JSON.stringify(catalog));
|
||||
const definitions = new Map(catalog.tools.map((tool) => [tool.name, tool]));
|
||||
const original = client.callTool.bind(client);
|
||||
client.callTool = (input, options) =>
|
||||
original(input, {
|
||||
...options,
|
||||
timeout: 600000,
|
||||
toolDefinition: definitions.get(input.name),
|
||||
});
|
||||
await result("open_binary", { path: values.target, provider_id: "ghidra" });
|
||||
opened = true;
|
||||
for (const phase of ["first", "warm"]) {
|
||||
const taskStarted = performance.now();
|
||||
const facts = [];
|
||||
for (const operation of operations) {
|
||||
const parameters = operation.startsWith("search_")
|
||||
? { pattern: "main", mode: "literal", case_sensitive: false }
|
||||
: {};
|
||||
const input =
|
||||
values.mode === "summary"
|
||||
? { ...parameters, detail: "summary" }
|
||||
: parameters;
|
||||
const reply = await call(operation, input, `${phase}:${operation}`);
|
||||
let evidence;
|
||||
if (reply.isError) {
|
||||
const text = reply.content.find((item) => item.type === "text");
|
||||
assert.ok(text);
|
||||
const error = JSON.parse(text.text).error;
|
||||
assert.equal(values.mode, "complete");
|
||||
assert.equal(error.code, "resource_constraint");
|
||||
const reference = error.details?.reported_limits?.evidence_reference;
|
||||
assert.equal(error.details?.reported_limits?.boundary, "mcp-response");
|
||||
assert.equal(reference?.kind, "retained-evidence");
|
||||
const recovery = { phase: `${phase}:${operation}`, status: "started" };
|
||||
report.local_recoveries.push(recovery);
|
||||
const path = join(
|
||||
output,
|
||||
`recovery-${report.local_recoveries.length}.json`,
|
||||
);
|
||||
await result(
|
||||
"export_evidence_bundle",
|
||||
{ path },
|
||||
`${phase}:${operation}:recovery-export`,
|
||||
);
|
||||
const recoveryStarted = performance.now();
|
||||
try {
|
||||
const bytes = await readFile(path);
|
||||
const bundle = JSON.parse(bytes);
|
||||
evidence = bundle.records.find(
|
||||
(record) => record.evidence_id === reference.evidence_id,
|
||||
);
|
||||
assert.ok(evidence);
|
||||
assert.equal(evidence.operation, operation);
|
||||
assert.ok(Array.isArray(evidence.normalized_result));
|
||||
// This is local JSON field selection after the documented export recovery,
|
||||
// not another provider query or an MCP view available in the baseline.
|
||||
const projection = {
|
||||
evidence_id: evidence.evidence_id,
|
||||
operation,
|
||||
row_count: evidence.normalized_result.length,
|
||||
rows: selectedRows(evidence.normalized_result),
|
||||
provider: evidence.provider,
|
||||
parameters: evidence.parameters,
|
||||
limitations: evidence.limitations,
|
||||
};
|
||||
recovery.bundle_bytes_read = bytes.length;
|
||||
recovery.projection_bytes = Buffer.byteLength(
|
||||
JSON.stringify(projection),
|
||||
);
|
||||
recovery.status = "passed";
|
||||
transcript.push({
|
||||
name: "local-bundle-projection",
|
||||
phase: recovery.phase,
|
||||
input: {
|
||||
path,
|
||||
evidence_id: reference.evidence_id,
|
||||
offset: 0,
|
||||
limit: 4,
|
||||
fields: ["address", "value"],
|
||||
},
|
||||
projection,
|
||||
});
|
||||
} finally {
|
||||
recovery.elapsed_ms = performance.now() - recoveryStarted;
|
||||
}
|
||||
} else evidence = reply.structuredContent;
|
||||
assert.ok(evidence);
|
||||
assert.equal(evidence.subject.digest.sha256, report.artifact.sha256);
|
||||
let count, rows, provider, query, parent;
|
||||
if (values.mode === "complete") {
|
||||
assert.equal(evidence.operation, operation);
|
||||
assert.equal(evidence.provider.id, "ghidra");
|
||||
assert.ok(Array.isArray(evidence.normalized_result));
|
||||
count = evidence.normalized_result.length;
|
||||
rows = selectedRows(evidence.normalized_result);
|
||||
provider = evidence.provider;
|
||||
query = evidence.parameters;
|
||||
parent = evidence.evidence_id;
|
||||
} else {
|
||||
assert.equal(evidence.operation, "inspect_analysis_view");
|
||||
const summary = evidence.normalized_result;
|
||||
assert.equal(summary.kind, "summary");
|
||||
assert.equal(summary.parent_operation, operation);
|
||||
assert.equal(summary.summary.discovery_coverage, "unknown");
|
||||
assert.equal(summary.source_provider.id, "ghidra");
|
||||
count = summary.summary.row_count;
|
||||
provider = summary.source_provider;
|
||||
query = summary.source_parameters;
|
||||
parent = summary.parent_evidence_id;
|
||||
assert.deepEqual(evidence.evidence_links, [parent]);
|
||||
rows = [];
|
||||
if (count > 0) {
|
||||
const pageEvidence = await result(
|
||||
"inspect_analysis_view",
|
||||
{
|
||||
source: { kind: "retained-evidence", evidence_id: parent },
|
||||
view: {
|
||||
kind: "native-inventory",
|
||||
offset: 0,
|
||||
limit: 4,
|
||||
fields: ["value"],
|
||||
},
|
||||
},
|
||||
`${phase}:${operation}:page`,
|
||||
);
|
||||
const page = pageEvidence.normalized_result;
|
||||
assert.equal(page.kind, "native-inventory");
|
||||
assert.equal(page.parent_evidence_id, parent);
|
||||
assert.equal(page.coverage.total, count);
|
||||
assert.equal(page.rows.length, Math.min(4, count));
|
||||
for (const [index, row] of page.rows.entries())
|
||||
assert.equal(row.source_index, index);
|
||||
rows = page.rows.map(({ record: { address, value } }) => ({
|
||||
address,
|
||||
value,
|
||||
}));
|
||||
}
|
||||
}
|
||||
assert.ok(Number.isSafeInteger(count) && count >= 0);
|
||||
if (operation.startsWith("list_")) assert.ok(count > 0, operation);
|
||||
assert.equal(provider.version, "12.1.4");
|
||||
assert.equal(Object.hasOwn(query, "detail"), false);
|
||||
assert.equal(rows.length, Math.min(4, count));
|
||||
facts.push({
|
||||
operation,
|
||||
row_count: count,
|
||||
rows,
|
||||
provider,
|
||||
parameters: query,
|
||||
limitations: [...new Set(evidence.limitations)].sort(),
|
||||
});
|
||||
sourceReferences.push({ phase, operation, parent });
|
||||
}
|
||||
report.tasks.push({
|
||||
phase,
|
||||
elapsed_ms: performance.now() - taskStarted,
|
||||
facts,
|
||||
});
|
||||
}
|
||||
assert.deepEqual(report.tasks[0].facts, report.tasks[1].facts);
|
||||
const taskFacts = report.tasks.map(({ phase, facts }) => ({ phase, facts }));
|
||||
await writeFile(join(output, "facts.json"), JSON.stringify(taskFacts));
|
||||
report.facts_sha256 = hash(JSON.stringify(taskFacts));
|
||||
const bundlePath = join(output, "evidence.json");
|
||||
await result("export_evidence_bundle", { path: bundlePath });
|
||||
const bundle = JSON.parse(await readFile(bundlePath, "utf8"));
|
||||
const sources = sourceReferences.map(({ phase, operation, parent }) => {
|
||||
const record = bundle.records.find(
|
||||
(record) => record.evidence_id === parent,
|
||||
);
|
||||
assert.ok(record);
|
||||
assert.equal(record.operation, operation);
|
||||
assert.equal(record.subject.digest.sha256, report.artifact.sha256);
|
||||
const task = report.tasks
|
||||
.find((task) => task.phase === phase)
|
||||
.facts.find((fact) => fact.operation === operation);
|
||||
assert.equal(record.normalized_result.length, task.row_count);
|
||||
assert.deepEqual(selectedRows(record.normalized_result), task.rows);
|
||||
assert.deepEqual(record.parameters, task.parameters);
|
||||
assert.deepEqual(record.provider, task.provider);
|
||||
return { phase, operation, record };
|
||||
});
|
||||
await writeFile(join(output, "sources.json"), JSON.stringify(sources));
|
||||
report.sources_sha256 = hash(JSON.stringify(sources));
|
||||
if (values.control) {
|
||||
assert.deepEqual(
|
||||
taskFacts,
|
||||
JSON.parse(await readFile(join(values.control, "facts.json"), "utf8")),
|
||||
);
|
||||
const control = JSON.parse(
|
||||
await readFile(join(values.control, "sources.json"), "utf8"),
|
||||
);
|
||||
// Assertion formatting of a large mismatch can itself exhaust the experiment
|
||||
// scope. Retain both files and report a bounded failure without weakening it.
|
||||
assert.ok(
|
||||
isDeepStrictEqual(sources, control),
|
||||
`Full source Evidence differs from control; inspect retained sources.json files (${report.sources_sha256} vs ${hash(JSON.stringify(control))}).`,
|
||||
);
|
||||
}
|
||||
if (values["verify-pages"]) {
|
||||
for (const { operation, parent } of sourceReferences.filter(
|
||||
(source) => source.phase === "first",
|
||||
)) {
|
||||
const source = sources.find(
|
||||
(source) => source.phase === "first" && source.operation === operation,
|
||||
).record;
|
||||
const all = [];
|
||||
let offset = 0;
|
||||
do {
|
||||
const evidence = await result(
|
||||
"inspect_analysis_view",
|
||||
{
|
||||
source: { kind: "retained-evidence", evidence_id: parent },
|
||||
view: { kind: "native-inventory", offset, limit: 128 },
|
||||
},
|
||||
`verification:${operation}:${offset}`,
|
||||
);
|
||||
const page = evidence.normalized_result;
|
||||
assert.equal(page.kind, "native-inventory");
|
||||
assert.equal(
|
||||
page.rows.length,
|
||||
Math.min(128, source.normalized_result.length - offset),
|
||||
);
|
||||
for (const [index, row] of page.rows.entries())
|
||||
assert.equal(row.source_index, offset + index);
|
||||
all.push(...page.rows.map((row) => row.record));
|
||||
assert.equal(
|
||||
page.coverage.next_offset,
|
||||
offset + page.rows.length >= source.normalized_result.length
|
||||
? null
|
||||
: offset + page.rows.length,
|
||||
);
|
||||
offset = page.coverage.next_offset;
|
||||
} while (offset !== null);
|
||||
assert.deepEqual(all, source.normalized_result);
|
||||
}
|
||||
}
|
||||
assert.equal(hash(await readFile(values.target)), report.artifact.sha256);
|
||||
report.status = "passed";
|
||||
} catch (cause) {
|
||||
report.status = "failed";
|
||||
report.failure = String(cause?.stack ?? cause);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
if (opened)
|
||||
try {
|
||||
await result("close_binary", {});
|
||||
} catch (cause) {
|
||||
report.status = "failed";
|
||||
report.close_failure = String(cause?.stack ?? cause);
|
||||
}
|
||||
report.elapsed_before_cleanup_ms = performance.now() - started;
|
||||
try {
|
||||
await writeFile(
|
||||
join(output, "transcript.json"),
|
||||
JSON.stringify(transcript),
|
||||
);
|
||||
} catch (cause) {
|
||||
report.status = "failed";
|
||||
report.transcript_failure = String(cause);
|
||||
}
|
||||
const finalized = await finalizeRetainedQueryReceipt({
|
||||
report,
|
||||
client,
|
||||
transport,
|
||||
account: home,
|
||||
serverLog,
|
||||
serverLogFinished,
|
||||
output,
|
||||
});
|
||||
if (report.status !== "passed" || !finalized) process.exitCode = 1;
|
||||
}
|
||||
@@ -0,0 +1,894 @@
|
||||
{
|
||||
"baseline_revision": "5fa4defcbc0e55ef1ec84c888b918abe945303fe",
|
||||
"baseline_runtime_sha256": "8068d5a85e07880998f232ed9f02ba8f5a4bd914164e4097e00104213c05a1c3",
|
||||
"candidate_measured_runtime_sha256": "3de94a0ca596c96c48e9f5f52cd7c064f5bfccaece38166550d7fec884ab2f04",
|
||||
"cohorts": [
|
||||
{
|
||||
"name": "baseline-compact-import-1",
|
||||
"capture_sha256": "2dbe00348c2f6fcd81f829564718e276052d4aaea77f2a6745cbd147869aec6f",
|
||||
"events": 132,
|
||||
"workflow_ms": 5249.381685,
|
||||
"scope_seconds": 5.818,
|
||||
"payload_cpu_seconds": 5.6285,
|
||||
"payload_family_peak_bytes": 692121600,
|
||||
"first_view_ms": 1354.982591,
|
||||
"warm_14_queries_ms": 174.149147000001,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1247.9706180000003,
|
||||
"sha256": "655cb1871fc82a011ea15352bfd1856e28d6d0171269951d3f3a1e1bd694e74f",
|
||||
"bytes": 3254305
|
||||
},
|
||||
"source_id": "ev_df45171a0cf0d3c691124f0cc44a8d6b38e0e8c9d3d298ea611ffdac084c304e",
|
||||
"answers_sha256": "641f85866e87c2768b4cf56911640e4f9e3f3340ac44cd7a468c5329fdd9448c",
|
||||
"original_record_sha256": "6c9f269acba239804406d278cfc3cce1fd7d0880b312c785df3a791a2c157a4d",
|
||||
"call_count": 17,
|
||||
"rpc_reply_bytes": 161234,
|
||||
"query_reply_bytes": 160659,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false,
|
||||
"abort": null
|
||||
},
|
||||
{
|
||||
"name": "baseline-compact-import-2",
|
||||
"capture_sha256": "2dbe00348c2f6fcd81f829564718e276052d4aaea77f2a6745cbd147869aec6f",
|
||||
"events": 132,
|
||||
"workflow_ms": 5668.385286000001,
|
||||
"scope_seconds": 6.178,
|
||||
"payload_cpu_seconds": 5.960291,
|
||||
"payload_family_peak_bytes": 664920064,
|
||||
"first_view_ms": 1400.6959980000001,
|
||||
"warm_14_queries_ms": 183.19255900000007,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1496.2133520000002,
|
||||
"sha256": "655cb1871fc82a011ea15352bfd1856e28d6d0171269951d3f3a1e1bd694e74f",
|
||||
"bytes": 3254305
|
||||
},
|
||||
"source_id": "ev_df45171a0cf0d3c691124f0cc44a8d6b38e0e8c9d3d298ea611ffdac084c304e",
|
||||
"answers_sha256": "641f85866e87c2768b4cf56911640e4f9e3f3340ac44cd7a468c5329fdd9448c",
|
||||
"original_record_sha256": "6c9f269acba239804406d278cfc3cce1fd7d0880b312c785df3a791a2c157a4d",
|
||||
"call_count": 17,
|
||||
"rpc_reply_bytes": 161234,
|
||||
"query_reply_bytes": 160659,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false,
|
||||
"abort": null
|
||||
},
|
||||
{
|
||||
"name": "baseline-compact-import-3",
|
||||
"capture_sha256": "2dbe00348c2f6fcd81f829564718e276052d4aaea77f2a6745cbd147869aec6f",
|
||||
"events": 132,
|
||||
"workflow_ms": 6465.653956,
|
||||
"scope_seconds": 6.988,
|
||||
"payload_cpu_seconds": 6.788176,
|
||||
"payload_family_peak_bytes": 690696192,
|
||||
"first_view_ms": 1959.8266800000001,
|
||||
"warm_14_queries_ms": 271.20134300000063,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1479.3080370000002,
|
||||
"sha256": "655cb1871fc82a011ea15352bfd1856e28d6d0171269951d3f3a1e1bd694e74f",
|
||||
"bytes": 3254305
|
||||
},
|
||||
"source_id": "ev_df45171a0cf0d3c691124f0cc44a8d6b38e0e8c9d3d298ea611ffdac084c304e",
|
||||
"answers_sha256": "641f85866e87c2768b4cf56911640e4f9e3f3340ac44cd7a468c5329fdd9448c",
|
||||
"original_record_sha256": "6c9f269acba239804406d278cfc3cce1fd7d0880b312c785df3a791a2c157a4d",
|
||||
"call_count": 17,
|
||||
"rpc_reply_bytes": 161234,
|
||||
"query_reply_bytes": 160659,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false,
|
||||
"abort": null
|
||||
},
|
||||
{
|
||||
"name": "baseline-large-import-1",
|
||||
"capture_sha256": "4854882fdeec3d66701779cfde1c614a09e3d5e3b90afdea3fc3d2566a0e6e3f",
|
||||
"events": 30753,
|
||||
"workflow_ms": 19253.191338,
|
||||
"scope_seconds": 19.884,
|
||||
"payload_cpu_seconds": 19.622432,
|
||||
"payload_family_peak_bytes": 1791569920,
|
||||
"first_view_ms": 3034.7453300000016,
|
||||
"warm_14_queries_ms": 6399.140819999997,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1299.400326,
|
||||
"sha256": "655cb1871fc82a011ea15352bfd1856e28d6d0171269951d3f3a1e1bd694e74f",
|
||||
"bytes": 3254305
|
||||
},
|
||||
"source_id": "ev_57091094e6142a58af3b1716e8735a963d451a81eb79f214679542ca65e12022",
|
||||
"answers_sha256": "263db399005814e40a7e962f5a46d3eff95e4372cd541fe3241d050be41d3d60",
|
||||
"original_record_sha256": "46a8a61e38a78cf2000c5cda35695bef7c8198776be6d8a3ae54853a5f003a8a",
|
||||
"call_count": 17,
|
||||
"rpc_reply_bytes": 161114,
|
||||
"query_reply_bytes": 160539,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false,
|
||||
"abort": null
|
||||
},
|
||||
{
|
||||
"name": "baseline-large-import-2",
|
||||
"capture_sha256": "4854882fdeec3d66701779cfde1c614a09e3d5e3b90afdea3fc3d2566a0e6e3f",
|
||||
"events": 30753,
|
||||
"workflow_ms": 19464.788056999998,
|
||||
"scope_seconds": 20.089,
|
||||
"payload_cpu_seconds": 19.791543,
|
||||
"payload_family_peak_bytes": 1783373824,
|
||||
"first_view_ms": 2902.7044369999985,
|
||||
"warm_14_queries_ms": 6482.219604,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1361.999307,
|
||||
"sha256": "655cb1871fc82a011ea15352bfd1856e28d6d0171269951d3f3a1e1bd694e74f",
|
||||
"bytes": 3254305
|
||||
},
|
||||
"source_id": "ev_57091094e6142a58af3b1716e8735a963d451a81eb79f214679542ca65e12022",
|
||||
"answers_sha256": "263db399005814e40a7e962f5a46d3eff95e4372cd541fe3241d050be41d3d60",
|
||||
"original_record_sha256": "46a8a61e38a78cf2000c5cda35695bef7c8198776be6d8a3ae54853a5f003a8a",
|
||||
"call_count": 17,
|
||||
"rpc_reply_bytes": 161114,
|
||||
"query_reply_bytes": 160539,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false,
|
||||
"abort": null
|
||||
},
|
||||
{
|
||||
"name": "baseline-large-import-3",
|
||||
"capture_sha256": "4854882fdeec3d66701779cfde1c614a09e3d5e3b90afdea3fc3d2566a0e6e3f",
|
||||
"events": 30753,
|
||||
"workflow_ms": 19570.145603,
|
||||
"scope_seconds": 20.181,
|
||||
"payload_cpu_seconds": 19.910178,
|
||||
"payload_family_peak_bytes": 1784766464,
|
||||
"first_view_ms": 2891.966555000001,
|
||||
"warm_14_queries_ms": 6393.723106999998,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1375.4531000000002,
|
||||
"sha256": "655cb1871fc82a011ea15352bfd1856e28d6d0171269951d3f3a1e1bd694e74f",
|
||||
"bytes": 3254305
|
||||
},
|
||||
"source_id": "ev_57091094e6142a58af3b1716e8735a963d451a81eb79f214679542ca65e12022",
|
||||
"answers_sha256": "263db399005814e40a7e962f5a46d3eff95e4372cd541fe3241d050be41d3d60",
|
||||
"original_record_sha256": "46a8a61e38a78cf2000c5cda35695bef7c8198776be6d8a3ae54853a5f003a8a",
|
||||
"call_count": 17,
|
||||
"rpc_reply_bytes": 161114,
|
||||
"query_reply_bytes": 160539,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false,
|
||||
"abort": null
|
||||
},
|
||||
{
|
||||
"name": "candidate-compact-import-1",
|
||||
"capture_sha256": "2dbe00348c2f6fcd81f829564718e276052d4aaea77f2a6745cbd147869aec6f",
|
||||
"events": 132,
|
||||
"workflow_ms": 5479.5708540000005,
|
||||
"scope_seconds": 6.08,
|
||||
"payload_cpu_seconds": 5.85,
|
||||
"payload_family_peak_bytes": 693051392,
|
||||
"first_view_ms": 1621.3823879999995,
|
||||
"warm_14_queries_ms": 106.31982299999981,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1213.6661829999998,
|
||||
"sha256": "655cb1871fc82a011ea15352bfd1856e28d6d0171269951d3f3a1e1bd694e74f",
|
||||
"bytes": 3254305
|
||||
},
|
||||
"source_id": "ev_df45171a0cf0d3c691124f0cc44a8d6b38e0e8c9d3d298ea611ffdac084c304e",
|
||||
"answers_sha256": "641f85866e87c2768b4cf56911640e4f9e3f3340ac44cd7a468c5329fdd9448c",
|
||||
"original_record_sha256": "6c9f269acba239804406d278cfc3cce1fd7d0880b312c785df3a791a2c157a4d",
|
||||
"call_count": 17,
|
||||
"rpc_reply_bytes": 161236,
|
||||
"query_reply_bytes": 160659,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false,
|
||||
"abort": null
|
||||
},
|
||||
{
|
||||
"name": "candidate-compact-import-2",
|
||||
"capture_sha256": "2dbe00348c2f6fcd81f829564718e276052d4aaea77f2a6745cbd147869aec6f",
|
||||
"events": 132,
|
||||
"workflow_ms": 5673.527286,
|
||||
"scope_seconds": 6.272,
|
||||
"payload_cpu_seconds": 6.059065,
|
||||
"payload_family_peak_bytes": 694616064,
|
||||
"first_view_ms": 1462.453802,
|
||||
"warm_14_queries_ms": 108.05108499999824,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1309.1064109999998,
|
||||
"sha256": "655cb1871fc82a011ea15352bfd1856e28d6d0171269951d3f3a1e1bd694e74f",
|
||||
"bytes": 3254305
|
||||
},
|
||||
"source_id": "ev_df45171a0cf0d3c691124f0cc44a8d6b38e0e8c9d3d298ea611ffdac084c304e",
|
||||
"answers_sha256": "641f85866e87c2768b4cf56911640e4f9e3f3340ac44cd7a468c5329fdd9448c",
|
||||
"original_record_sha256": "6c9f269acba239804406d278cfc3cce1fd7d0880b312c785df3a791a2c157a4d",
|
||||
"call_count": 17,
|
||||
"rpc_reply_bytes": 161236,
|
||||
"query_reply_bytes": 160659,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false,
|
||||
"abort": null
|
||||
},
|
||||
{
|
||||
"name": "candidate-compact-import-3",
|
||||
"capture_sha256": "2dbe00348c2f6fcd81f829564718e276052d4aaea77f2a6745cbd147869aec6f",
|
||||
"events": 132,
|
||||
"workflow_ms": 5393.434348000001,
|
||||
"scope_seconds": 5.976,
|
||||
"payload_cpu_seconds": 5.690881,
|
||||
"payload_family_peak_bytes": 681385984,
|
||||
"first_view_ms": 1448.6816159999994,
|
||||
"warm_14_queries_ms": 131.2696420000002,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1323.0185740000002,
|
||||
"sha256": "655cb1871fc82a011ea15352bfd1856e28d6d0171269951d3f3a1e1bd694e74f",
|
||||
"bytes": 3254305
|
||||
},
|
||||
"source_id": "ev_df45171a0cf0d3c691124f0cc44a8d6b38e0e8c9d3d298ea611ffdac084c304e",
|
||||
"answers_sha256": "641f85866e87c2768b4cf56911640e4f9e3f3340ac44cd7a468c5329fdd9448c",
|
||||
"original_record_sha256": "6c9f269acba239804406d278cfc3cce1fd7d0880b312c785df3a791a2c157a4d",
|
||||
"call_count": 17,
|
||||
"rpc_reply_bytes": 161236,
|
||||
"query_reply_bytes": 160659,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false,
|
||||
"abort": null
|
||||
},
|
||||
{
|
||||
"name": "candidate-large-import-1",
|
||||
"capture_sha256": "4854882fdeec3d66701779cfde1c614a09e3d5e3b90afdea3fc3d2566a0e6e3f",
|
||||
"events": 30753,
|
||||
"workflow_ms": 14052.698892,
|
||||
"scope_seconds": 14.742,
|
||||
"payload_cpu_seconds": 14.496087,
|
||||
"payload_family_peak_bytes": 1173385216,
|
||||
"first_view_ms": 4077.395645999999,
|
||||
"warm_14_queries_ms": 113.30538300000626,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1469.9327679999997,
|
||||
"sha256": "655cb1871fc82a011ea15352bfd1856e28d6d0171269951d3f3a1e1bd694e74f",
|
||||
"bytes": 3254305
|
||||
},
|
||||
"source_id": "ev_57091094e6142a58af3b1716e8735a963d451a81eb79f214679542ca65e12022",
|
||||
"answers_sha256": "263db399005814e40a7e962f5a46d3eff95e4372cd541fe3241d050be41d3d60",
|
||||
"original_record_sha256": "46a8a61e38a78cf2000c5cda35695bef7c8198776be6d8a3ae54853a5f003a8a",
|
||||
"call_count": 17,
|
||||
"rpc_reply_bytes": 161116,
|
||||
"query_reply_bytes": 160539,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false,
|
||||
"abort": null
|
||||
},
|
||||
{
|
||||
"name": "candidate-large-import-2",
|
||||
"capture_sha256": "4854882fdeec3d66701779cfde1c614a09e3d5e3b90afdea3fc3d2566a0e6e3f",
|
||||
"events": 30753,
|
||||
"workflow_ms": 13581.232348000001,
|
||||
"scope_seconds": 14.135,
|
||||
"payload_cpu_seconds": 13.936527,
|
||||
"payload_family_peak_bytes": 1177927680,
|
||||
"first_view_ms": 3845.8420860000006,
|
||||
"warm_14_queries_ms": 114.86873200000264,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1438.3021010000002,
|
||||
"sha256": "655cb1871fc82a011ea15352bfd1856e28d6d0171269951d3f3a1e1bd694e74f",
|
||||
"bytes": 3254305
|
||||
},
|
||||
"source_id": "ev_57091094e6142a58af3b1716e8735a963d451a81eb79f214679542ca65e12022",
|
||||
"answers_sha256": "263db399005814e40a7e962f5a46d3eff95e4372cd541fe3241d050be41d3d60",
|
||||
"original_record_sha256": "46a8a61e38a78cf2000c5cda35695bef7c8198776be6d8a3ae54853a5f003a8a",
|
||||
"call_count": 17,
|
||||
"rpc_reply_bytes": 161116,
|
||||
"query_reply_bytes": 160539,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false,
|
||||
"abort": null
|
||||
},
|
||||
{
|
||||
"name": "candidate-large-import-3",
|
||||
"capture_sha256": "4854882fdeec3d66701779cfde1c614a09e3d5e3b90afdea3fc3d2566a0e6e3f",
|
||||
"events": 30753,
|
||||
"workflow_ms": 14680.945012,
|
||||
"scope_seconds": 15.248,
|
||||
"payload_cpu_seconds": 15.020055,
|
||||
"payload_family_peak_bytes": 1201561600,
|
||||
"first_view_ms": 4190.895545000001,
|
||||
"warm_14_queries_ms": 110.60082600000169,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1362.8905800000002,
|
||||
"sha256": "655cb1871fc82a011ea15352bfd1856e28d6d0171269951d3f3a1e1bd694e74f",
|
||||
"bytes": 3254305
|
||||
},
|
||||
"source_id": "ev_57091094e6142a58af3b1716e8735a963d451a81eb79f214679542ca65e12022",
|
||||
"answers_sha256": "263db399005814e40a7e962f5a46d3eff95e4372cd541fe3241d050be41d3d60",
|
||||
"original_record_sha256": "46a8a61e38a78cf2000c5cda35695bef7c8198776be6d8a3ae54853a5f003a8a",
|
||||
"call_count": 17,
|
||||
"rpc_reply_bytes": 161116,
|
||||
"query_reply_bytes": 160539,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false,
|
||||
"abort": null
|
||||
}
|
||||
],
|
||||
"actual_model_token_usage": null,
|
||||
"tokenizer_estimate": null,
|
||||
"token_claim": "Unmeasured actual model usage; identical catalog and selected view replies, no token reduction claimed.",
|
||||
"captures": [
|
||||
{
|
||||
"id": "compact",
|
||||
"url": "https://quic.aiortc.org/logs/dadb9dba427e797c.qlog",
|
||||
"bytes": 29034,
|
||||
"events": 132,
|
||||
"sha256": "2dbe00348c2f6fcd81f829564718e276052d4aaea77f2a6745cbd147869aec6f"
|
||||
},
|
||||
{
|
||||
"id": "large",
|
||||
"url": "https://quic.aiortc.org/logs/6e037439a5f79d47.qlog",
|
||||
"bytes": 5922676,
|
||||
"events": 30753,
|
||||
"sha256": "4854882fdeec3d66701779cfde1c614a09e3d5e3b90afdea3fc3d2566a0e6e3f"
|
||||
}
|
||||
],
|
||||
"host": {
|
||||
"platform": "Linux x86_64",
|
||||
"kernel": "6.1.0-53-cloud-amd64",
|
||||
"node": "24.18.0",
|
||||
"npm": "11.16.0",
|
||||
"sdk": "2.3.1",
|
||||
"cpu_affinity": [15],
|
||||
"query_memory_hard_bytes": 4294967296,
|
||||
"query_memory_early_stop_bytes": 3221225472,
|
||||
"swap_bytes": 0,
|
||||
"os_cache": "warm, not flushed"
|
||||
},
|
||||
"incomplete_attempts": [
|
||||
{
|
||||
"phase": "initial baseline producer harness, each recording",
|
||||
"reason": "Oracle initially expected the raw QUIC reset credential; actual correctly redacted transport credential. Fixed oracle checks the one exact permitted redaction. Original exported Evidence retained and used unchanged for all imported cohorts.",
|
||||
"included_in_performance_cohorts": false
|
||||
},
|
||||
{
|
||||
"phase": "first candidate build",
|
||||
"reason": "3 GiB early memory stop; payload did not finish; no OOM or hard-limit events. Serial build subsequently passed with 6 GiB hard/5 GiB early limits.",
|
||||
"included_in_performance_cohorts": false
|
||||
},
|
||||
{
|
||||
"phase": "first full static check",
|
||||
"reason": "Typecheck reached the 5 GiB early stop under a 6 GiB hard limit. No OOM or hard-limit events; task incomplete. Subsequent static checks use 8 GiB hard/7 GiB early, one CPU and at least 16 GiB available host memory.",
|
||||
"included_in_performance_cohorts": false
|
||||
},
|
||||
{
|
||||
"phase": "intermediate full static check",
|
||||
"reason": "The driver was being revised and had one formatting discrepancy. Formatter corrected it; the subsequent complete npm run check passed.",
|
||||
"included_in_performance_cohorts": false
|
||||
},
|
||||
{
|
||||
"phase": "first latest-main pre-push check",
|
||||
"reason": "Host available memory temporarily fell below the 8 GiB early-stop floor. Guard stopped the typecheck and push did not complete; no OOM/limit events. Host availability recovered before subsequent validation.",
|
||||
"included_in_performance_cohorts": false
|
||||
}
|
||||
],
|
||||
"harness_finalization_note": "The final driver reuses the existing tested RPC finalizer and streams stderr instead of buffering it; the 12 timing cohorts used the earlier finalization path. RPC arguments and compared runtime implementations are unchanged. Final-driver replays are separate verification, not relabeled timing samples.",
|
||||
"executed_checks": {
|
||||
"source_tests": {
|
||||
"files": 10,
|
||||
"passed": 189
|
||||
},
|
||||
"compiled_tests": {
|
||||
"files": 4,
|
||||
"passed": 43,
|
||||
"skipped": 1,
|
||||
"skip_reason": "The qlog guard-unavailable refusal case runs only on hosts without decoder memory containment; this Linux host has it. The actual decoder heap-exhaustion case passed."
|
||||
},
|
||||
"rpc_finalization_regressions": {
|
||||
"passed": 10
|
||||
},
|
||||
"static_check": "npm run check passed, including typecheck, lint/module boundaries, formatting, knip, metadata and architecture guards",
|
||||
"test_discovery": "1711 test files discovered exactly once"
|
||||
},
|
||||
"final_driver_verification": [
|
||||
{
|
||||
"sample": "compact",
|
||||
"producer_status": "passed",
|
||||
"selected_view_evidence_semantically_equal_to_imported": true,
|
||||
"cli_evidence_equal_to_mcp": true,
|
||||
"source_id": "ev_df45171a0cf0d3c691124f0cc44a8d6b38e0e8c9d3d298ea611ffdac084c304e",
|
||||
"original_record_sha256": "6c9f269acba239804406d278cfc3cce1fd7d0880b312c785df3a791a2c157a4d",
|
||||
"cleanup_failures": [],
|
||||
"receipt_failures": []
|
||||
},
|
||||
{
|
||||
"sample": "large",
|
||||
"producer_status": "passed",
|
||||
"selected_view_evidence_semantically_equal_to_imported": true,
|
||||
"cli_evidence_equal_to_mcp": true,
|
||||
"source_id": "ev_57091094e6142a58af3b1716e8735a963d451a81eb79f214679542ca65e12022",
|
||||
"original_record_sha256": "46a8a61e38a78cf2000c5cda35695bef7c8198776be6d8a3ae54853a5f003a8a",
|
||||
"cleanup_failures": [],
|
||||
"receipt_failures": []
|
||||
}
|
||||
],
|
||||
"latest_main_integration": {
|
||||
"base_revision": "d72021b0dabedcc6fbc7bc6e0286fd359c604271",
|
||||
"candidate_source_revision": "cd1c3758022e42b83bf7954aa3dc663148651b8b",
|
||||
"cohorts": [
|
||||
{
|
||||
"name": "integrated-baseline-compact",
|
||||
"scope_seconds": 6.574,
|
||||
"payload_cpu_seconds": 6.370822,
|
||||
"payload_family_peak_bytes": 695435264,
|
||||
"first_view_ms": 1340.7030219999997,
|
||||
"warm_14_queries_ms": 178.7094820000002,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1916.610111,
|
||||
"sha256": "8a646b95cc5607ee57e5a3c0ef480b77e4cec7e5168498e72f511a8af40daf61",
|
||||
"bytes": 3255374
|
||||
},
|
||||
"answers_sha256": "641f85866e87c2768b4cf56911640e4f9e3f3340ac44cd7a468c5329fdd9448c",
|
||||
"original_record_sha256": "6c9f269acba239804406d278cfc3cce1fd7d0880b312c785df3a791a2c157a4d",
|
||||
"payload_cleanup_verified": true,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false
|
||||
},
|
||||
{
|
||||
"name": "integrated-baseline-large",
|
||||
"scope_seconds": 19.466,
|
||||
"payload_cpu_seconds": 19.132667,
|
||||
"payload_family_peak_bytes": 1795719168,
|
||||
"first_view_ms": 2920.709574999999,
|
||||
"warm_14_queries_ms": 6315.634482000003,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1277.3594239999998,
|
||||
"sha256": "8a646b95cc5607ee57e5a3c0ef480b77e4cec7e5168498e72f511a8af40daf61",
|
||||
"bytes": 3255374
|
||||
},
|
||||
"answers_sha256": "263db399005814e40a7e962f5a46d3eff95e4372cd541fe3241d050be41d3d60",
|
||||
"original_record_sha256": "46a8a61e38a78cf2000c5cda35695bef7c8198776be6d8a3ae54853a5f003a8a",
|
||||
"payload_cleanup_verified": true,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false
|
||||
},
|
||||
{
|
||||
"name": "integrated-candidate-compact",
|
||||
"scope_seconds": 5.664,
|
||||
"payload_cpu_seconds": 5.444678,
|
||||
"payload_family_peak_bytes": 695730176,
|
||||
"first_view_ms": 1347.5685030000004,
|
||||
"warm_14_queries_ms": 108.42142599999988,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1173.273788,
|
||||
"sha256": "8a646b95cc5607ee57e5a3c0ef480b77e4cec7e5168498e72f511a8af40daf61",
|
||||
"bytes": 3255374
|
||||
},
|
||||
"answers_sha256": "641f85866e87c2768b4cf56911640e4f9e3f3340ac44cd7a468c5329fdd9448c",
|
||||
"original_record_sha256": "6c9f269acba239804406d278cfc3cce1fd7d0880b312c785df3a791a2c157a4d",
|
||||
"payload_cleanup_verified": true,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false
|
||||
},
|
||||
{
|
||||
"name": "integrated-candidate-large",
|
||||
"scope_seconds": 13.719,
|
||||
"payload_cpu_seconds": 13.524082,
|
||||
"payload_family_peak_bytes": 1177186304,
|
||||
"first_view_ms": 3807.763484,
|
||||
"warm_14_queries_ms": 105.97323599999982,
|
||||
"catalog": {
|
||||
"elapsed_ms": 1297.2051929999998,
|
||||
"sha256": "8a646b95cc5607ee57e5a3c0ef480b77e4cec7e5168498e72f511a8af40daf61",
|
||||
"bytes": 3255374
|
||||
},
|
||||
"answers_sha256": "263db399005814e40a7e962f5a46d3eff95e4372cd541fe3241d050be41d3d60",
|
||||
"original_record_sha256": "46a8a61e38a78cf2000c5cda35695bef7c8198776be6d8a3ae54853a5f003a8a",
|
||||
"payload_cleanup_verified": true,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false
|
||||
}
|
||||
],
|
||||
"scope": "One integration pair per recording; separate from the original three-run distributions. Current baseline and candidate include Evidence-file source support. Both real CLI inline projections preserve original MCP Evidence.",
|
||||
"baseline_runtime_sha256": "e0e0bb47340bd6a676ef6e939e49f36bd64f4263353afb40cc75bafa79f3d8ba",
|
||||
"candidate_runtime_sha256": "8d6accf22f658b8815a8d52a85e0e5f910394456bd898dc3440f57e278f14b2f",
|
||||
"source_combination_control": {
|
||||
"candidate_combined": {
|
||||
"passed": 251,
|
||||
"failed": 1,
|
||||
"files": 14
|
||||
},
|
||||
"unmodified_main_default_order": {
|
||||
"passed": 243,
|
||||
"failed": 0,
|
||||
"files": 13
|
||||
},
|
||||
"unmodified_main_environment_first": {
|
||||
"passed": 62,
|
||||
"failed": 1,
|
||||
"files": 2
|
||||
},
|
||||
"failure": "File authentication cancellation test returned EvidenceFileError instead of AnalysisCancelledError. Both unmodified main and candidate reproduce it when environmentReplay loads the service before the spy-owning test file.",
|
||||
"control": "Temporary BaseSequencer override sorted environmentReplay before AnalysisViewService; production and test files on main stayed unchanged. A first attempt to pass a sequencer module string directly was rejected during runner setup and executed no tests.",
|
||||
"scope": "Existing order-sensitive module spy, not established production cancellation failure. No unrelated repair included.",
|
||||
"separate_current_runs": {
|
||||
"service_file_passed": 48,
|
||||
"other_source_files_passed": 204,
|
||||
"other_source_files": 13,
|
||||
"note": "Two separate processes avoid the existing shared-module spy order dependency. The mixed-run failure and unmodified-main control remain recorded."
|
||||
}
|
||||
}
|
||||
},
|
||||
"final_scope_note": "Reuse is connected only to retained capture and HAR grouping views. Final call-site review restored ordinary canonical schema admission for one-shot producer/environment-file reads, avoiding extra proof work without a measured repeated-read benefit. Earlier frozen timing runtimes retain their original identities; final focused replay is qualified separately.",
|
||||
"final_narrow_scope_verification": {
|
||||
"source": "Retained views/grouping only; producer/environment schema call sites restored. All active query outputs and original records match baseline.",
|
||||
"source_checks": "252 assertions passed in two invocations; mixed-run cancellation-spy failure retained with unmodified-main reproduction.",
|
||||
"additional_mcp_assertions_passed": 20,
|
||||
"static_check": "Full npm run check passed on final narrowed code.",
|
||||
"cli": "Both public-recording CLI Evidence projections exactly match MCP.",
|
||||
"runs": [
|
||||
{
|
||||
"name": "final-narrow-compact",
|
||||
"scope_seconds": 11.687,
|
||||
"payload_cpu_seconds": 10.892926,
|
||||
"payload_family_peak_bytes": 694239232,
|
||||
"first_view_ms": 2877.247516999999,
|
||||
"warm_14_queries_ms": 232.99256799999966,
|
||||
"catalog_elapsed_ms": 2558.570081,
|
||||
"answers_sha256": "641f85866e87c2768b4cf56911640e4f9e3f3340ac44cd7a468c5329fdd9448c",
|
||||
"original_record_sha256": "6c9f269acba239804406d278cfc3cce1fd7d0880b312c785df3a791a2c157a4d",
|
||||
"payload_cleanup_verified": true,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false
|
||||
},
|
||||
{
|
||||
"name": "final-narrow-large",
|
||||
"scope_seconds": 31.854,
|
||||
"payload_cpu_seconds": 28.414057,
|
||||
"payload_family_peak_bytes": 1236717568,
|
||||
"first_view_ms": 10157.196946,
|
||||
"warm_14_queries_ms": 234.07879700000558,
|
||||
"catalog_elapsed_ms": 2642.0467449999996,
|
||||
"answers_sha256": "263db399005814e40a7e962f5a46d3eff95e4372cd541fe3241d050be41d3d60",
|
||||
"original_record_sha256": "46a8a61e38a78cf2000c5cda35695bef7c8198776be6d8a3ae54853a5f003a8a",
|
||||
"payload_cleanup_verified": true,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false
|
||||
},
|
||||
{
|
||||
"name": "final-control-baseline",
|
||||
"scope_seconds": 18.87,
|
||||
"payload_cpu_seconds": 18.518563,
|
||||
"payload_family_peak_bytes": 1805721600,
|
||||
"first_view_ms": 2797.2868439999993,
|
||||
"warm_14_queries_ms": 5997.660557999998,
|
||||
"catalog_elapsed_ms": 1202.264228,
|
||||
"answers_sha256": "263db399005814e40a7e962f5a46d3eff95e4372cd541fe3241d050be41d3d60",
|
||||
"original_record_sha256": "46a8a61e38a78cf2000c5cda35695bef7c8198776be6d8a3ae54853a5f003a8a",
|
||||
"payload_cleanup_verified": true,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false
|
||||
},
|
||||
{
|
||||
"name": "final-control-candidate",
|
||||
"scope_seconds": 14.242,
|
||||
"payload_cpu_seconds": 14.047643,
|
||||
"payload_family_peak_bytes": 1202728960,
|
||||
"first_view_ms": 4040.8927329999988,
|
||||
"warm_14_queries_ms": 107.68954900000426,
|
||||
"catalog_elapsed_ms": 1348.444556,
|
||||
"answers_sha256": "263db399005814e40a7e962f5a46d3eff95e4372cd541fe3241d050be41d3d60",
|
||||
"original_record_sha256": "46a8a61e38a78cf2000c5cda35695bef7c8198776be6d8a3ae54853a5f003a8a",
|
||||
"payload_cleanup_verified": true,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false
|
||||
}
|
||||
],
|
||||
"qualification": "One final adjacent baseline/candidate pair, not a new distribution. The earlier slower unpaired compact/large runs are retained; their broad phase slowdown is not causally attributed."
|
||||
},
|
||||
"review_oracle_correction": {
|
||||
"root_cause": "Returned-row iteration alone did not reject an empty or short page with unchanged coverage metadata.",
|
||||
"fix": "Require exactly two selected records before checking row facts; production code unchanged.",
|
||||
"archived_transcript_recheck": {
|
||||
"oracle": "Exact page-validation block from the revised driver, evaluated against archived production replies",
|
||||
"completed_transcripts": 22,
|
||||
"pages": 198,
|
||||
"records": 396,
|
||||
"all_passed": true,
|
||||
"negative_controls": [
|
||||
{
|
||||
"returned_records": 0,
|
||||
"coverage_unchanged": true,
|
||||
"rejected": true
|
||||
},
|
||||
{
|
||||
"returned_records": 1,
|
||||
"coverage_unchanged": true,
|
||||
"rejected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"real_mcp_replays": [
|
||||
{
|
||||
"name": "review-compact",
|
||||
"capture_sha256": "2dbe00348c2f6fcd81f829564718e276052d4aaea77f2a6745cbd147869aec6f",
|
||||
"answers_sha256": "641f85866e87c2768b4cf56911640e4f9e3f3340ac44cd7a468c5329fdd9448c",
|
||||
"call_count": 17,
|
||||
"scope_seconds": 5.37,
|
||||
"payload_cpu_seconds": 5.194994,
|
||||
"payload_family_peak_bytes": 679886848,
|
||||
"payload_cleanup_verified": true,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false,
|
||||
"abort": null
|
||||
},
|
||||
{
|
||||
"name": "review-large",
|
||||
"capture_sha256": "4854882fdeec3d66701779cfde1c614a09e3d5e3b90afdea3fc3d2566a0e6e3f",
|
||||
"answers_sha256": "263db399005814e40a7e962f5a46d3eff95e4372cd541fe3241d050be41d3d60",
|
||||
"call_count": 17,
|
||||
"scope_seconds": 14.242,
|
||||
"payload_cpu_seconds": 13.966121,
|
||||
"payload_family_peak_bytes": 1196273664,
|
||||
"payload_cleanup_verified": true,
|
||||
"resource_events_at_payload_exit": {
|
||||
"memory.events": {
|
||||
"low": 0,
|
||||
"high": 0,
|
||||
"max": 0,
|
||||
"oom": 0,
|
||||
"oom_kill": 0,
|
||||
"oom_group_kill": 0
|
||||
},
|
||||
"pids.events": {
|
||||
"max": 0
|
||||
}
|
||||
},
|
||||
"final_whole_scope_counters_verified": false,
|
||||
"abort": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
# Retained historical capture validation
|
||||
|
||||
On main `5fa4defcbc0e55ef1ec84c888b918abe945303fe` (2026-10-20), repeated
|
||||
historical capture pages still validated and copied the complete capture.
|
||||
The immutable-parent work in [#4962](https://github.com/morluto/rea/pull/4962)
|
||||
covered native layouts and JavaScript, rather than historical captures.
|
||||
This continuation of [#3133](https://github.com/morluto/rea/issues/3133)
|
||||
removes that repeated work without changing its query contract.
|
||||
|
||||
## Reconciled audit scope
|
||||
|
||||
The earlier directory list is a starting point, not a list of unfinished fixes.
|
||||
Current history and the existing evidence support these narrower conclusions:
|
||||
|
||||
| Scope | Existing work and remaining limit |
|
||||
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Shared Evidence and queries | [#3134](https://github.com/morluto/rea/issues/3134) and [#4810](https://github.com/morluto/rea/issues/4810) are closed. Authentic mixed histories and immutable native/JavaScript validation reuse have dedicated [measurements](immutable-parent-validation.md). This capture owner still repeated complete validation. |
|
||||
| Apktool | [#4777](https://github.com/morluto/rea/issues/4777) is closed. Shared decoding and optional primary-string omission have [three-APK evidence](apktool-string-tables.md). Do not implement another decode cache from the old issue list. |
|
||||
| Hopper | [#5017](https://github.com/morluto/rea/pull/5017) already reuses decoded string records. Its [checkpoint](hopper-string-records.md) preserves faster warm queries and the negative whole-task result. |
|
||||
| JEB | The [Linux CE audit](jeb-linux-native.md) verifies selected real CLI/MCP reads. It does not justify another wrapper cache; discovery and other platforms remain separate coverage gaps. |
|
||||
| IDA | Existing [native Windows verification](ida-native-windows.md) qualifies selected correctness/authority paths. It does not establish a comparable full performance audit. |
|
||||
| JavaScript/browser/Electron | Prior experiments and retained pages already exist. [#4415](https://github.com/morluto/rea/issues/4415) remains exploratory; no safe general Joplin preparation speedup is established. |
|
||||
| Other static formats | Managed, Flutter, SQLite, WASM, EVM, PCAP, signals and SAZ already have checkpoints in this directory. Extend a specific measured question rather than create duplicate selectors or caches. |
|
||||
| Other providers, languages and infrastructure | Rizin/Cutter/GDB/objdump, Go/Rust, ADB/Frida/inspector and startup/resource owners still need bounded real-workflow questions. Historical local improvements do not prove complete coverage or establish another bottleneck. |
|
||||
|
||||
This reconciles the cited issues and checked-in evidence; it is not a new
|
||||
performance qualification of all `src/` directories or provider capabilities.
|
||||
|
||||
## Owning change and lifetime
|
||||
|
||||
`safeParseOwnedWebNetworkCapture` belongs to the capture format owner. A
|
||||
successful complete schema parse may authenticate the exact source only if
|
||||
`canBorrowParsedJsonSnapshot` verifies an existing realm-private sealing proof,
|
||||
unchanged values, schema normalization, descriptors and alias topology.
|
||||
The existing immutable JSON primitives supply that proof; an external freeze
|
||||
or caller flag cannot supply it.
|
||||
|
||||
One weak-set entry records the successful interpretation, without retaining
|
||||
another report, index or source reference. Its lifetime follows the source's
|
||||
reachability. Exact-object identity, not a path, digest string or live-state
|
||||
assumption, keys reuse. Mutable, merely frozen, malformed and normalized inputs
|
||||
keep the ordinary validation path. Failures are not cached. Validation is
|
||||
synchronous and publishes the proof only after complete success.
|
||||
|
||||
Capture pages and HAR grouping use this same owner. Producer admission and
|
||||
one-shot environment-file loading retain ordinary owner-schema validation,
|
||||
avoiding an extra reuse-proof traversal where no repeated result is available.
|
||||
Each consumer still checks its own subject,
|
||||
operation, format, coverage, declarations and resource budget. In particular,
|
||||
validating a report does not authenticate a different parent's subject.
|
||||
No provider rerun, new tool, schema, default, target execution or persisted cache
|
||||
is introduced. Existing conservative grouping admission budgets remain intact.
|
||||
|
||||
## Real recordings and completed task
|
||||
|
||||
Two unchanged public recordings from the aioquic server's
|
||||
[log listing](https://quic.aiortc.org/logs) were downloaded on 2026-10-20:
|
||||
|
||||
| Recording | Bytes | Events | SHA-256 |
|
||||
| ------------------------------------------------------------- | --------: | -----: | ------------------------------------------------------------------ |
|
||||
| [compact](https://quic.aiortc.org/logs/dadb9dba427e797c.qlog) | 29,034 | 132 | `2dbe00348c2f6fcd81f829564718e276052d4aaea77f2a6745cbd147869aec6f` |
|
||||
| [large](https://quic.aiortc.org/logs/6e037439a5f79d47.qlog) | 5,922,676 | 30,753 | `4854882fdeec3d66701779cfde1c614a09e3d5e3b90afdea3fc3d2566a0e6e3f` |
|
||||
|
||||
Both report JSON qlog 0.3 with one server trace. The producer version and
|
||||
deployment authenticity remain unknown. No recorded destination was contacted.
|
||||
|
||||
Production stdio MCP created authentic complete Evidence, exported it, and
|
||||
fresh servers imported that same unchanged bundle for each comparison. Each
|
||||
task discovers the complete live catalog, imports, performs three traversals
|
||||
of summary/three two-record complete pages/trace facts, exports again and closes
|
||||
the transport. This is 17 RPCs and one catalog discovery, with 14 warm selected
|
||||
queries after the first view. The question is the first six source events and
|
||||
trace event total, revisited within one retained investigation.
|
||||
|
||||
An independent JSON read checks event counts, original event objects, ordinals
|
||||
and trace/event pointers. It explicitly expects only the QUIC reset-token
|
||||
transport-credential redaction in those selected events and checks its sidecar.
|
||||
All 15 selected-view Evidence records, parent identities and original-record
|
||||
round trips agree exactly between implementations. This does not independently
|
||||
qualify every transport fact or prove network completeness.
|
||||
|
||||
## Measurements
|
||||
|
||||
Three fresh-server tasks per implementation per recording ran serially,
|
||||
baseline/candidate first and candidate/baseline in each later cohort.
|
||||
The [curated receipt](retained-capture-validation.json) preserves every run,
|
||||
runtime fingerprints, source and answer identities, resource observations and
|
||||
incomplete attempts. Raw captures, bundles, replies and logs stay outside Git.
|
||||
|
||||
Linux x86-64, Node 24.18.0/npm 11.16.0, MCP SDK 2.3.1. Each client uses the
|
||||
exact named live tool definition for output validation. First-view time includes
|
||||
client/schema initialization as well as capture validation; it is not isolated
|
||||
parser time. OS file caches were warm and unflushed. No cold-disk run occurred.
|
||||
|
||||
Medians across three completed tasks:
|
||||
|
||||
| Recording / metric | Before | After |
|
||||
| ------------------------------------------------- | ---------: | ---------: |
|
||||
| Large: first selected view | 2.903 s | 4.077 s |
|
||||
| Large: 14 warm queries | 6.399 s | 0.113 s |
|
||||
| Large: complete owned scope wall | 20.089 s | 14.742 s |
|
||||
| Large: complete payload-family CPU | 19.792 s | 14.496 s |
|
||||
| Large: kernel family peak, including file cache | 1702.1 MiB | 1123.4 MiB |
|
||||
| Compact: first selected view | 1.401 s | 1.462 s |
|
||||
| Compact: 14 warm queries | 0.183 s | 0.108 s |
|
||||
| Compact: complete owned scope wall | 6.178 s | 6.080 s |
|
||||
| Compact: complete payload-family CPU | 5.960 s | 5.850 s |
|
||||
| Compact: kernel family peak, including file cache | 658.7 MiB | 660.9 MiB |
|
||||
|
||||
Large warm totals range 6.394–6.482 s before and 0.111–0.115 s after.
|
||||
The extra first-parse structural comparison costs time; repeated views recover
|
||||
that cost in this large-recording task. Small-task wall times overlap, so no
|
||||
small-input whole-task benefit or universal capture speedup is established.
|
||||
The weak-set bookkeeping's exact heap size and post-GC retained heap were not
|
||||
measured; the implementation retains no second capture representation.
|
||||
|
||||
The catalog is unchanged at 3,254,305 serialized bytes. The 15 query replies are
|
||||
unchanged at 160,659 bytes for compact and 160,539 for large, including the SDK
|
||||
result envelopes. Actual model token usage and tokenizer estimates are
|
||||
unmeasured; no token reduction or model-quality improvement is claimed.
|
||||
|
||||
Query scopes use one logical CPU, 4 GiB hard/3 GiB early memory limits, no swap
|
||||
and 192 tasks. All 12 completed observations verify payload exit, cleanup and
|
||||
zero OOM/limit events. Final counters after the transient scope disappears
|
||||
remain unknown; payload-exit counters are recorded while its observer is alive.
|
||||
An initial build stopped at 3 GiB before completion, without OOM. The subsequent
|
||||
serial build used 6 GiB hard/5 GiB early limits and peaked at 3264.9 MiB.
|
||||
Two initial producer harnesses stopped on the oracle's incorrect expectation
|
||||
that a reset credential would remain raw. Their original exports remain intact;
|
||||
they are not completed performance tasks or product failures.
|
||||
|
||||
## Reproduction and validation
|
||||
|
||||
Build each runtime with the pinned toolchain and preserve a separate baseline.
|
||||
Keep each capture's absolute selected path identical across compared runtimes:
|
||||
|
||||
```sh
|
||||
python3 experiments/analysis-efficiency/resource-run.py --observe-payload-exit -- \
|
||||
node experiments/analysis-efficiency/retained-capture-validation.mjs \
|
||||
--runtime /absolute/baseline --capture /absolute/input.qlog \
|
||||
--output /absolute/new-producer-output
|
||||
|
||||
python3 experiments/analysis-efficiency/resource-run.py --observe-payload-exit -- \
|
||||
node experiments/analysis-efficiency/retained-capture-validation.mjs \
|
||||
--runtime /absolute/candidate --capture /absolute/input.qlog \
|
||||
--bundle /absolute/new-producer-output/original.json \
|
||||
--output /absolute/new-import-output
|
||||
```
|
||||
|
||||
Executed source verification passed 189 assertions across ten files, covering
|
||||
capture schemas/views/services, subject rechecks, forged freezes, normalization,
|
||||
mutation, invalid ordinals, HAR budgets, environment loading and replay.
|
||||
Four compiled CLI/MCP/decoder/acceptance files passed 43 assertions. One
|
||||
guard-unavailable refusal case was skipped because this Linux host has decoder
|
||||
memory containment; the actual decoder heap-exhaustion and recovery case passed.
|
||||
The ten existing RPC/receipt/finalization regressions passed. `npm run check`
|
||||
passed, and test discovery found all 1711 test files exactly once.
|
||||
|
||||
Final-driver replays completed both public producer tasks with no cleanup or
|
||||
receipt errors. Their selected Evidence matches imported-source queries, and
|
||||
both real CLI projections exactly match the MCP Evidence. The final driver
|
||||
uses the existing tested finalizer and streams stderr; these are separate
|
||||
verification runs, not relabeled samples from the earlier timing cohorts.
|
||||
One intermediate check encountered an unformatted driver revision; formatting
|
||||
was corrected before the successful full check. The first full typecheck also
|
||||
hit the 5 GiB early stop; its successful run used 8 GiB hard/7 GiB early limits
|
||||
and peaked at 5392.8 MiB without OOM. Queries remained within the smaller limit.
|
||||
|
||||
Timing claims cover these qlog tasks only. Current HAR/SAZ/mitmproxy performance,
|
||||
other hosts, real model token costs and post-GC retained heap remain unmeasured.
|
||||
|
||||
## Latest-main integration
|
||||
|
||||
Main advanced to `d72021b0dabedcc6fbc7bc6e0286fd359c604271` before publication,
|
||||
adding Evidence-file sources and template path pairs. The change rebased without
|
||||
conflicts. The original timing distributions above retain their original base.
|
||||
|
||||
One additional pair per recording compares this current main with the rebased
|
||||
candidate, using the final driver and original authentic bundles. Both preserve
|
||||
all selected Evidence and original records. For large, 14 warm queries cost
|
||||
6.316 → 0.106 s; complete scope wall is 19.466 → 13.719 s; payload-family CPU
|
||||
19.133 → 13.524 s; family peak 1712.5 → 1122.7 MiB. Compact scope wall is
|
||||
6.574 → 5.664 s in this single pair, without establishing a small-task trend.
|
||||
Both real CLI projections again exactly match the original MCP Evidence.
|
||||
Individual counters are in the receipt's `latest_main_integration` section.
|
||||
|
||||
Evidence-file reads still authenticate a fresh object on each read. This
|
||||
optimization is not a file-path cache and does not reuse an earlier file's
|
||||
validation after its bytes change.
|
||||
|
||||
The first integrated 14-file source run passed 251/252 assertions and failed
|
||||
the newly merged file-authentication cancellation injection in
|
||||
`AnalysisViewService.test.ts`. An unmodified-main 13-file control passed
|
||||
243/243 with a different service-file order. A controlled unmodified-main run
|
||||
placing `environmentReplay.test.ts` before `AnalysisViewService.test.ts`
|
||||
reproduced exactly that cancellation failure (62 passes, one failure).
|
||||
The service project shares modules between files; the source test installs a
|
||||
module spy after another file may already have loaded the service. This is
|
||||
source-derived reasoning for the order sensitivity, rather than a reproduced
|
||||
production cancellation defect. No unrelated test or production repair is
|
||||
included, and the combined failure remains part of the verification record.
|
||||
|
||||
The service file passed all 48 assertions in its own process, and the other
|
||||
13 selected source files passed 204 assertions. The final narrowed implementation
|
||||
passed the full `npm run check`, plus 20 additional compiled MCP assertions,
|
||||
including identical retained/inline/Evidence-file manifest projection.
|
||||
|
||||
After limiting reuse to retained/grouping queries, both public imports and real
|
||||
CLI projections passed again. This first unpaired replay was slower across
|
||||
catalog discovery, import and first-view work: large scope wall was 31.854 s,
|
||||
CPU 28.414 s, with 0.234 s for 14 warm queries. Compact scope wall was 11.687 s.
|
||||
The cause of this broad slowdown is unestablished; these runs are not discarded.
|
||||
|
||||
A subsequent adjacent baseline/candidate large-recording control used the final
|
||||
code and same inputs. Scope wall was 18.870 → 14.242 s; CPU 18.519 → 14.048 s;
|
||||
kernel family peak 1722.1 → 1147.0 MiB; 14 warm queries 5.998 → 0.108 s.
|
||||
First view was slower at 2.797 → 4.041 s. All original and selected Evidence
|
||||
agree. This final single pair requalifies the narrowed code; it is not a new
|
||||
latency distribution or an explanation of the slower unpaired replay.
|
||||
|
||||
## Review-driven oracle correction
|
||||
|
||||
Review found that the original page oracle checked only returned rows: a short
|
||||
page could pass with unchanged coverage metadata. The driver now also requires
|
||||
both selected records before checking their facts. This fixes the oracle at its
|
||||
owner, without changing production code or introducing a second validator.
|
||||
|
||||
The revised driver's exact page-validation block rechecked all 22 completed
|
||||
production transcripts: 198 pages and 396 records passed. Controls truncating
|
||||
an authentic response to zero or one record, with coverage unchanged, both
|
||||
failed the new assertion. These are archived-response controls, not additional
|
||||
real-provider qualification. Earlier timings remain unchanged observations.
|
||||
|
||||
Both real MCP import/query/export tasks were then repeated with the revised
|
||||
driver. Compact scope wall/CPU/peak were 5.370 s / 5.195 s / 648.4 MiB; large
|
||||
were 14.242 s / 13.966 s / 1140.9 MiB. Both preserve the prior answer digests,
|
||||
verify cleanup, and record zero OOM or limit events at payload exit. These
|
||||
candidate-only correctness replays do not create another before/after pair.
|
||||
@@ -0,0 +1,251 @@
|
||||
/** Public stdio MCP capture paging; keep original recordings and transcripts outside Git. */
|
||||
import assert from "node:assert/strict";
|
||||
import { createHash } from "node:crypto";
|
||||
import { mkdir, readFile, writeFile, mkdtemp } from "node:fs/promises";
|
||||
import { createWriteStream } from "node:fs";
|
||||
import { finished } from "node:stream/promises";
|
||||
import { join, resolve } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { parseArgs } from "node:util";
|
||||
import { Client } from "@modelcontextprotocol/client";
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
||||
import { createRetainedQueryCaller } from "./retained-query-calls.mjs";
|
||||
import { finalizeRetainedQueryReceipt } from "./retained-query-finalization.mjs";
|
||||
|
||||
const { values } = parseArgs({
|
||||
options: Object.fromEntries(
|
||||
["runtime", "capture", "output", "bundle"].map((name) => [
|
||||
name,
|
||||
{ type: "string" },
|
||||
]),
|
||||
),
|
||||
});
|
||||
for (const name of ["runtime", "capture", "output"])
|
||||
assert.ok(
|
||||
values[name] && values[name] === resolve(values[name]),
|
||||
`${name} must be absolute`,
|
||||
);
|
||||
const hash = (value) => createHash("sha256").update(value).digest("hex");
|
||||
const output = values.output;
|
||||
await mkdir(output, { mode: 0o700 });
|
||||
const recording = await readFile(values.capture);
|
||||
const raw = JSON.parse(recording);
|
||||
const events = raw.traces.flatMap((trace, t) =>
|
||||
trace.events.map((reported, e) => ({
|
||||
reported,
|
||||
pointer: `/traces/${t}/events/${e}`,
|
||||
})),
|
||||
);
|
||||
assert.ok(events.length >= 6);
|
||||
const home = await mkdtemp(join(tmpdir(), "rea-capture-validation-"));
|
||||
const client = new Client({
|
||||
name: "retained-capture-validation",
|
||||
version: "1",
|
||||
});
|
||||
const transport = new StdioClientTransport({
|
||||
command: process.execPath,
|
||||
args: [join(values.runtime, "scripts/rea.mjs"), "mcp"],
|
||||
env: {
|
||||
HOME: home,
|
||||
PATH: process.env.PATH,
|
||||
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 report = {
|
||||
capture_sha256: hash(recording),
|
||||
capture_bytes: recording.length,
|
||||
events: events.length,
|
||||
mode: values.bundle ? "imported" : "producer",
|
||||
calls: [],
|
||||
};
|
||||
const transcript = [];
|
||||
const call = createRetainedQueryCaller(client, (receipt, event) => {
|
||||
report.calls.push(receipt);
|
||||
transcript.push(event);
|
||||
});
|
||||
const result = async (name, args, phase) => {
|
||||
const response = await call(name, args, phase);
|
||||
assert.notEqual(response.isError, true, JSON.stringify(response));
|
||||
assert.ok(response.structuredContent);
|
||||
return response.structuredContent;
|
||||
};
|
||||
const started = performance.now();
|
||||
try {
|
||||
await client.connect(transport);
|
||||
const discovery = performance.now();
|
||||
const catalog = await client.listTools();
|
||||
assert.equal(
|
||||
catalog.nextCursor,
|
||||
undefined,
|
||||
"This driver requires a complete catalog page",
|
||||
);
|
||||
report.catalog = {
|
||||
elapsed_ms: performance.now() - discovery,
|
||||
sha256: hash(JSON.stringify(catalog)),
|
||||
bytes: Buffer.byteLength(JSON.stringify(catalog)),
|
||||
};
|
||||
await writeFile(join(output, "catalog.json"), JSON.stringify(catalog));
|
||||
// Compile the exact live named contract for each call, avoiding client-wide compilation.
|
||||
const definitions = new Map(catalog.tools.map((tool) => [tool.name, tool]));
|
||||
const originalCallTool = client.callTool.bind(client);
|
||||
client.callTool = (input, options) =>
|
||||
originalCallTool(input, {
|
||||
...options,
|
||||
toolDefinition: definitions.get(input.name),
|
||||
});
|
||||
let original;
|
||||
if (values.bundle) {
|
||||
const bundle = JSON.parse(await readFile(values.bundle, "utf8"));
|
||||
original = bundle.records.find(
|
||||
(record) => record.operation === "inspect_web_network_capture",
|
||||
);
|
||||
assert.ok(original);
|
||||
await result("import_evidence_bundle", { path: values.bundle }, "import");
|
||||
} else {
|
||||
const evidence = await result(
|
||||
"inspect_web_network_capture",
|
||||
{
|
||||
capture_path: values.capture,
|
||||
format: "qlog",
|
||||
detail: "summary",
|
||||
},
|
||||
"produce-summary",
|
||||
);
|
||||
report.summary_id = evidence.evidence_id;
|
||||
const path = join(output, "original.json");
|
||||
await result("export_evidence_bundle", { path }, "export-original");
|
||||
const bundle = JSON.parse(await readFile(path, "utf8"));
|
||||
original = bundle.records.find(
|
||||
(record) => record.operation === "inspect_web_network_capture",
|
||||
);
|
||||
assert.ok(original);
|
||||
}
|
||||
assert.equal(
|
||||
original.normalized_result.artifact.sha256,
|
||||
report.capture_sha256,
|
||||
);
|
||||
assert.equal(original.normalized_result.total_records, events.length);
|
||||
const source = {
|
||||
kind: "retained-evidence",
|
||||
evidence_id: original.evidence_id,
|
||||
};
|
||||
report.source_id = original.evidence_id;
|
||||
const answers = [];
|
||||
for (let pass = 0; pass < 3; pass += 1) {
|
||||
const summary = await result(
|
||||
"inspect_analysis_view",
|
||||
{ source, view: { kind: "summary" } },
|
||||
`summary-${pass}`,
|
||||
);
|
||||
assert.equal(
|
||||
summary.normalized_result.summary.total_records,
|
||||
events.length,
|
||||
);
|
||||
answers.push(summary);
|
||||
for (const offset of [0, 2, 4]) {
|
||||
const evidence = await result(
|
||||
"inspect_analysis_view",
|
||||
{
|
||||
source,
|
||||
view: { kind: "capture", detail: "complete", offset, limit: 2 },
|
||||
},
|
||||
`page-${pass}-${offset}`,
|
||||
);
|
||||
const view = evidence.normalized_result;
|
||||
assert.equal(view.parent_evidence_id, original.evidence_id);
|
||||
assert.equal(view.coverage.total, events.length);
|
||||
assert.equal(view.coverage.next_offset, offset + 2);
|
||||
assert.equal(
|
||||
view.items.length,
|
||||
2,
|
||||
"Every selected record must be returned",
|
||||
);
|
||||
for (const [i, row] of view.items.entries()) {
|
||||
assert.equal(row.ordinal, offset + i);
|
||||
assert.equal(row.location.pointer, events[offset + i].pointer);
|
||||
const expected = structuredClone(events[offset + i].reported);
|
||||
// QUIC reset tokens are transport authentication material, not reported facts.
|
||||
const resetToken = Object.hasOwn(
|
||||
expected.data ?? {},
|
||||
"stateless_reset_token",
|
||||
);
|
||||
if (resetToken) expected.data.stateless_reset_token = null;
|
||||
assert.deepEqual(
|
||||
row.redactions,
|
||||
resetToken
|
||||
? [
|
||||
{
|
||||
pointer: "/data/stateless_reset_token",
|
||||
reason: "transport-credential",
|
||||
},
|
||||
]
|
||||
: [],
|
||||
);
|
||||
assert.deepEqual(row.reported, expected);
|
||||
}
|
||||
answers.push(evidence);
|
||||
}
|
||||
const traces = await result(
|
||||
"inspect_analysis_view",
|
||||
{
|
||||
source,
|
||||
view: { kind: "qlog", facet: "traces", offset: 0, limit: 50 },
|
||||
},
|
||||
`traces-${pass}`,
|
||||
);
|
||||
assert.equal(traces.normalized_result.page.items.length, raw.traces.length);
|
||||
for (const [i, row] of traces.normalized_result.page.items.entries()) {
|
||||
assert.equal(row.trace_index, i);
|
||||
assert.equal(row.event_count, raw.traces[i].events.length);
|
||||
}
|
||||
answers.push(traces);
|
||||
}
|
||||
const roundtrip = join(output, "roundtrip.json");
|
||||
await result(
|
||||
"export_evidence_bundle",
|
||||
{ path: roundtrip },
|
||||
"export-roundtrip",
|
||||
);
|
||||
const exported = JSON.parse(await readFile(roundtrip, "utf8"));
|
||||
assert.deepEqual(
|
||||
exported.records.find(
|
||||
(record) => record.evidence_id === original.evidence_id,
|
||||
),
|
||||
original,
|
||||
);
|
||||
assert.equal(hash(await readFile(values.capture)), report.capture_sha256);
|
||||
report.answers_sha256 = hash(JSON.stringify(answers));
|
||||
report.original_record_sha256 = hash(JSON.stringify(original));
|
||||
report.status = "passed";
|
||||
} catch (cause) {
|
||||
report.status = "failed";
|
||||
report.failure = String(cause?.stack ?? cause);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
report.elapsed_before_cleanup_ms = performance.now() - started;
|
||||
try {
|
||||
await writeFile(
|
||||
join(output, "transcript.json"),
|
||||
JSON.stringify(transcript),
|
||||
);
|
||||
} catch (cause) {
|
||||
report.status = "failed";
|
||||
report.transcript_failure = String(cause);
|
||||
}
|
||||
const finalized = await finalizeRetainedQueryReceipt({
|
||||
report,
|
||||
client,
|
||||
transport,
|
||||
account: home,
|
||||
serverLog,
|
||||
serverLogFinished,
|
||||
output,
|
||||
});
|
||||
if (report.status !== "passed" || !finalized) process.exitCode = 1;
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
"scripts/verify/fridaBindingsCompatibility.ts",
|
||||
"scripts/verify/termux/browser.mjs",
|
||||
"scripts/verify-real-ghidra-noreturn.mjs",
|
||||
"scripts/verify-real-ghidra-decompiler-failure.mjs",
|
||||
"tests/processOwnershipRunner.ts"
|
||||
],
|
||||
"project": ["src/**/*.ts", "tests/**/*.ts", "scripts/**/*.mjs"],
|
||||
|
||||
@@ -8,6 +8,7 @@ upstream texts; they do not change [REA's MIT license](../LICENSE).
|
||||
| ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `npm-dependencies.txt` | Every package path in the root lockfile, with its exact version, declared license, scope, download URL, and integrity. Includes development and optional platform entries. |
|
||||
| `npm-runtime-notices.txt` | Original license, copyright, and NOTICE texts collected from integrity-verified locked runtime npm archives across all platforms; also the license of Rolldown's generated runtime helpers. The archive manifest's name and version are checked. |
|
||||
| `protobuf-es-v2.16.0-LICENSE.txt` | Original Apache license omitted from the protobuf-es npm archive, preserved from its v2.16.0 source commit. Its embedded Google BSD notice is retained in `npm-runtime-notices.txt`. |
|
||||
| `nativeaot-parser-MIT.txt` | Original Washi notice for derived NativeAOT parser code; source revision is recorded in the parser and the root notices. |
|
||||
| `dotnet-runtime-MIT.txt` | Original .NET Foundation MIT text from the NativeAOT reference revision `a3b2d40328be0be3f8dd43f0e5cc925b8827b044`. |
|
||||
| `ghidra-12.1.4-{LICENSE,NOTICE,DISCLAIMER}.txt` | Unchanged top-level documents from `Ghidra_12.1.4_build`. Ghidra is not bundled; these reference copies do not replace its component and GPL support-program notices. The US-government-specific disclaimer applies to Ghidra, not to REA. |
|
||||
|
||||
@@ -2,7 +2,7 @@ REA npm dependency inventory
|
||||
Prepared from the root package-lock.json; refresh this inventory when locked dependencies change.
|
||||
License identifiers are upstream lockfile declarations, not a legal assessment.
|
||||
Runtime includes transitive and optional platform packages; development includes build, test and website tooling.
|
||||
Locked package entries: 750; runtime: 178.
|
||||
Locked package entries: 751; runtime: 179.
|
||||
Full runtime copyright/license/NOTICE texts: npm-runtime-notices.txt.
|
||||
|
||||
@algolia/abtesting@1.25.0 | development | MIT
|
||||
@@ -335,6 +335,11 @@ obug@3.0.0 | development | MIT
|
||||
Package: https://www.npmjs.com/package/@bcoe/v8-coverage/v/1.0.2
|
||||
Archive: https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz
|
||||
Integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==
|
||||
@bufbuild/protobuf@2.16.0 | runtime | (Apache-2.0 AND BSD-3-Clause)
|
||||
Lockfile path: node_modules/@bufbuild/protobuf
|
||||
Package: https://www.npmjs.com/package/@bufbuild/protobuf/v/2.16.0
|
||||
Archive: https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.16.0.tgz
|
||||
Integrity: sha512-FWa0sPlqYGJgpTs6OxBRcL/AW4JT0OIO+W1ajerluoTVh8CV0B7XM1qsNCRgnkSCkRCgOICKHRQlY17jrsm0+Q==
|
||||
@cfworker/json-schema@4.1.1 | runtime | MIT
|
||||
Lockfile path: node_modules/@cfworker/json-schema
|
||||
Package: https://www.npmjs.com/package/@cfworker/json-schema/v/4.1.1
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
REA third-party npm runtime copyright, license and NOTICE texts
|
||||
Collected from integrity-verified locked npm archives. Release-specific supplements and upstream notice gaps are identified in their entries.
|
||||
External engines are separately obtained; see THIRD_PARTY_NOTICES.md.
|
||||
Runtime dependency fingerprint (SHA-256): 131100b2e1e6ad5e55b10a0baf4be2d7a0838fd724fb307b9bafd73446b0d3ec
|
||||
Runtime dependency fingerprint (SHA-256): 4125777159e02cb749e17c959e77f59528cfffe3f7a5220b8a21ce3c49ad3061
|
||||
|
||||
===== @babel/helper-string-parser@8.0.6 =====
|
||||
Declared license: MIT
|
||||
@@ -126,6 +126,254 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
===== @bufbuild/protobuf@2.16.0 =====
|
||||
Declared license: (Apache-2.0 AND BSD-3-Clause)
|
||||
Archive: https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.16.0.tgz
|
||||
Integrity: sha512-FWa0sPlqYGJgpTs6OxBRcL/AW4JT0OIO+W1ajerluoTVh8CV0B7XM1qsNCRgnkSCkRCgOICKHRQlY17jrsm0+Q==
|
||||
Scope: npm runtime dependency, including optional platform packages.
|
||||
|
||||
The locked archive omits standalone license files. The Apache license is preserved from its v2.16.0 upstream commit; the BSD notice is embedded in the integrity-verified archive.
|
||||
|
||||
--- licenses/protobuf-es-v2.16.0-LICENSE.txt (see upstream-sources.json) ---
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2021-2026 Buf Technologies, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
--- package/dist/esm/wire/varint.js (unchanged leading BSD notice) ---
|
||||
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// Code generated by the Protocol Buffer compiler is owned by the owner
|
||||
// of the input file used when generating it. This code is not
|
||||
// standalone and requires a support library to be linked with it. This
|
||||
// support library is itself covered by the above license.
|
||||
|
||||
|
||||
===== @cfworker/json-schema@4.1.1 =====
|
||||
Declared license: MIT
|
||||
Archive: https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2021-2026 Buf Technologies, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -68,5 +68,10 @@
|
||||
"file": "licenses/LGPL-2.0.txt",
|
||||
"source": "https://www.gnu.org/licenses/old-licenses/lgpl-2.0.txt",
|
||||
"sha256": "cc535c21133c895b56b374c8a1dc1eb948d99003ed2b47372069456b62f42b24"
|
||||
},
|
||||
{
|
||||
"file": "licenses/protobuf-es-v2.16.0-LICENSE.txt",
|
||||
"source": "https://raw.githubusercontent.com/bufbuild/protobuf-es/b3b232f7f466eda27c66e8f63c82f930578b6cc6/LICENSE",
|
||||
"sha256": "c04b4216f1cd4c5a4f7fb2f2a1b0ae70d847e9e0cac7c9dee9bf8cc03177c449"
|
||||
}
|
||||
]
|
||||
Generated
+7
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@babel/parser": "8.0.4",
|
||||
"@babel/types": "8.0.4",
|
||||
"@bufbuild/protobuf": "2.16.0",
|
||||
"@clack/prompts": "1.7.0",
|
||||
"@electron/asar": "4.2.0",
|
||||
"@jridgewell/trace-mapping": "0.3.31",
|
||||
@@ -1090,6 +1091,12 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@bufbuild/protobuf": {
|
||||
"version": "2.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.16.0.tgz",
|
||||
"integrity": "sha512-FWa0sPlqYGJgpTs6OxBRcL/AW4JT0OIO+W1ajerluoTVh8CV0B7XM1qsNCRgnkSCkRCgOICKHRQlY17jrsm0+Q==",
|
||||
"license": "(Apache-2.0 AND BSD-3-Clause)"
|
||||
},
|
||||
"node_modules/@cfworker/json-schema": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz",
|
||||
|
||||
+4
-1
@@ -139,7 +139,7 @@
|
||||
"test": "npm run test:with-build",
|
||||
"test:with-build": "npm run test:prepare && vitest run",
|
||||
"test:run": "vitest run",
|
||||
"test:python": "python3 -m unittest bridge/native/test_rea_lldb_tracer.py bridge/mitmproxy/test_capture.py -v && python3 -m unittest discover -s experiments/analysis-efficiency -p 'test_*.py' -v",
|
||||
"test:python": "python3 -m unittest bridge/native/test_rea_lldb_tracer.py bridge/pwndbg/test_core_context.py bridge/mitmproxy/test_capture.py -v && python3 -m unittest discover -s experiments/analysis-efficiency -p 'test_*.py' -v",
|
||||
"test:local": "node scripts/run-development-tests.mjs local",
|
||||
"test:fast": "node scripts/ci/test-group.mjs source",
|
||||
"test:built": "node scripts/ci/test-group.mjs built",
|
||||
@@ -287,9 +287,11 @@
|
||||
"verify:browser:source-maps": "npm run build:cached && node scripts/verify-browser-source-maps.mjs",
|
||||
"verify:browser:runtime": "npm run build:cached && node scripts/verify-browser-runtime.mjs",
|
||||
"verify:web:network-captures": "npm run build:cached && node scripts/verify-web-network-captures.mjs",
|
||||
"verify:agentenv:environment": "npm run build:cached && node scripts/verify-agentenv-environment.mjs",
|
||||
"verify:web:http3": "npm run build:cached && node scripts/verify-http3-network-captures.mjs",
|
||||
"verify:binary:layout": "npm run build:cached && node scripts/verify-binary-layout.mjs",
|
||||
"verify:go:binary": "npm run build:cached && node scripts/verify-go-binary.mjs",
|
||||
"verify:il2cpp:metadata": "npm run build:cached && node scripts/verify-il2cpp-metadata.mjs",
|
||||
"verify:wasm:package": "npm run build:cached && node scripts/verify-packaged-wasm-artifact.mjs",
|
||||
"verify:wasm-interface-comparison": "npm run build:cached && node scripts/verify-wasm-interface-comparison.mjs",
|
||||
"verify:wasm:artifact": "npm run build:cached && node scripts/verify-wasm-artifact.mjs",
|
||||
@@ -321,6 +323,7 @@
|
||||
"dependencies": {
|
||||
"@babel/parser": "8.0.4",
|
||||
"@babel/types": "8.0.4",
|
||||
"@bufbuild/protobuf": "2.16.0",
|
||||
"@clack/prompts": "1.7.0",
|
||||
"@electron/asar": "4.2.0",
|
||||
"@jridgewell/trace-mapping": "0.3.31",
|
||||
|
||||
@@ -11,6 +11,14 @@ export interface ReadinessResult {
|
||||
readonly description: string;
|
||||
}
|
||||
export interface ReadinessGitHub {
|
||||
graphql(
|
||||
query: string,
|
||||
variables: {
|
||||
readonly owner: string;
|
||||
readonly name: string;
|
||||
readonly after: string | null;
|
||||
},
|
||||
): Promise<unknown>;
|
||||
readonly rest: {
|
||||
readonly pulls: {
|
||||
get(input: {
|
||||
|
||||
@@ -2,6 +2,57 @@ import { readFile } from "node:fs/promises";
|
||||
|
||||
const result = (state, description) => ({ state, description });
|
||||
|
||||
const OPEN_HEADS_QUERY = `query ReadinessOpenHeads($owner: String!, $name: String!, $after: String) {
|
||||
repository(owner: $owner, name: $name) {
|
||||
pullRequests(first: 100, after: $after, states: OPEN, orderBy: {field: CREATED_AT, direction: ASC}) {
|
||||
nodes { number headRefOid }
|
||||
pageInfo { hasNextPage endCursor }
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
/** An unindexed commit needs complete live head coverage, not PR bodies. */
|
||||
async function openHeadsMatching(github, repository, sha) {
|
||||
const matches = [];
|
||||
const numbers = new Set();
|
||||
const cursors = new Set();
|
||||
let after = null;
|
||||
do {
|
||||
const response = await github.graphql(OPEN_HEADS_QUERY, {
|
||||
owner: repository.owner,
|
||||
name: repository.repo,
|
||||
after,
|
||||
});
|
||||
const page = response?.repository?.pullRequests;
|
||||
if (
|
||||
!Array.isArray(page?.nodes) ||
|
||||
typeof page.pageInfo?.hasNextPage !== "boolean"
|
||||
)
|
||||
throw new Error("GitHub returned incomplete open-PR head metadata.");
|
||||
for (const node of page.nodes) {
|
||||
if (
|
||||
!Number.isSafeInteger(node?.number) ||
|
||||
node.number <= 0 ||
|
||||
typeof node.headRefOid !== "string" ||
|
||||
!/^(?:[a-f\d]{40}|[a-f\d]{64})$/u.test(node.headRefOid) ||
|
||||
numbers.has(node.number)
|
||||
)
|
||||
throw new Error(
|
||||
"GitHub returned invalid or repeated open-PR head metadata.",
|
||||
);
|
||||
numbers.add(node.number);
|
||||
if (node.headRefOid === sha)
|
||||
matches.push({ number: node.number, state: "open", head: { sha } });
|
||||
}
|
||||
if (!page.pageInfo.hasNextPage) return matches;
|
||||
const next = page.pageInfo.endCursor;
|
||||
if (typeof next !== "string" || next.length === 0 || cursors.has(next))
|
||||
throw new Error("GitHub open-PR head pagination did not advance.");
|
||||
cursors.add(next);
|
||||
after = next;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
function readinessLines(markdown) {
|
||||
const lines = markdown.split(/\r?\n/u);
|
||||
const visible = [];
|
||||
@@ -125,18 +176,17 @@ export async function runReadinessCheck({ github, context, core }) {
|
||||
);
|
||||
let verdict = evaluateReadiness(pull, template);
|
||||
if (verdict.state === "success") {
|
||||
const associated = await github.paginate(
|
||||
let associated = await github.paginate(
|
||||
github.rest.repos.listPullRequestsAssociatedWithCommit,
|
||||
{ ...repository, commit_sha: pull.head.sha, per_page: 100 },
|
||||
);
|
||||
if (
|
||||
!associated.some(
|
||||
(other) =>
|
||||
other.number === number &&
|
||||
other.state === "open" &&
|
||||
other.head.sha === pull.head.sha,
|
||||
)
|
||||
)
|
||||
const ownsHead = (other) =>
|
||||
other.number === number &&
|
||||
other.state === "open" &&
|
||||
other.head.sha === pull.head.sha;
|
||||
if (!associated.some(ownsHead))
|
||||
associated = await openHeadsMatching(github, repository, pull.head.sha);
|
||||
if (!associated.some(ownsHead))
|
||||
verdict = result(
|
||||
"pending",
|
||||
"GitHub has not confirmed this PR's head association; rerun PR readiness.",
|
||||
|
||||
Loaded 100 of 790 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user